patorashのブログ

方向性はまだない

ActiveJobのコールバックは2系統に分かれている

ActiveJobの処理が時々コケることがあったので調査した。

Active Job の基礎 - Railsガイド を参照したところ、ActiveJobのコールバックは以下のようになる。

  • before_enqueue
  • around_enqueue
  • after_enqueue
  • before_perform
  • around_perform
  • after_perform

問題のあったコード

そして、元々のコードはこういう感じのことをやっていた。TaskLogモデルがあったとする。

  1. キューに入った時点でTaskLogモデルを作成する
  2. TaskJob実行直前に、ジョブIDからタスクログを取得してステータスを処理中に変更
  3. TaskJobが終了したら、ジョブIDからタスクログを取得してステータスを終了に変更
class TaskJob < ApplicationJob
  
  after_enqueue do |job|
    TaskLog.create!(job_id: job.id, status: :queued)
  end

  before_perform do |job|
    task = TaskLog.find_by! job_id: job.id
    task.status_processing!
  end

  after_perform do |job|
    task = TaskLog.find_by! job_id: job.id
    task.status_finished!
  end

  def perform
    # do something...
  end

end

しかし、いざ実行すると、たまに失敗するときがあった。before_performのTaskLog.find_by!で落ちていた。ここで落ちるということは、TaskLogが作られていないということだ。

実験

after_enqueueが終わる前にbefore_performが実行されることがある、ということだと判断したので、実験用のコードを書いた。

class TestJob < ApplicationJob

  after_enqueue do |job|
    puts "enqueued TestJob. sleep 10..."
    sleep 10
    puts "enqueued TestJob. end"
  end

  before_perform do |job|
    puts "before_perform TestJob. sleep 2"
    sleep 2
    puts "before_perform TestJob. end"
  end

  after_perform do |job|
    puts "after_perform TestJob."
  end

  def perform
    puts "perform!!!"
  end
end

これをrails cで実行した。sleepしているので10秒後にenquequd TestJob. endと出ます。

pry(main) > TestJob.perform_later
enqueued TestJob. sleep 10...
enqueued TestJob. end
Enqueued TestJob (Job ID: 0234b71e-aa4a-4644-b86e-dcf67b40d550) to Sidekiq(default)
=> #<TestJob:0x00007ff599493630
 @arguments=[],
 @executions=0,
 @job_id="0234b71e-aa4a-4644-b86e-dcf67b40d550",
 @priority=nil,
 @provider_job_id="3f7bda026dc435a015d6f81c",
 @queue_name="default">

その頃、sidekiqを実行中のターミナルでは…

2020-10-21T01:36:32.006Z 51089 TID-our2bxzcp TestJob JID-f427b7a69e619b9e45c68371 INFO: start
before_perform TestJob. sleep 2
before_perform TestJob. end
perform!!!
after_perform TestJob.
2020-10-21T01:36:34.027Z 51089 TID-our2bxzcp TestJob JID-f427b7a69e619b9e45c68371 INFO: done: 2.021 sec

早々にbefore_performが実行され、performも呼ばれ、after_enqueueが終わる前に処理が終わった。

ということは、ActiveJobのコールバックのenqueue系とperform系は完全に別々の処理と考える必要がある。

直したコード

before_enqueueでTaskLogモデルを作るのをやめればいい。

class TaskJob < ApplicationJob
  
  before_perform do |job|
    TaskLog.create!(job_id: job.id, status: :processing)
  end

  after_perform do |job|
    task = TaskLog.find_by! job_id: job.id
    task.status_finished!
  end

  def perform
    # do something...
  end

end

まとめ

enqueue系のコールバックは、使うにしても「予約しました」とか、そういう通知系をやるくらいに留めておく。perform系の処理で参照したいような前処理をenqueue系でやるべきではない!

Elasticsearchを5.6から7.9にバージョンアップ

過去にElasticsearchのバージョンアップをしたのだけれど、そのバージョンも既にEOLとなり、早く最新のバージョンにしたいと思いつつも、なかなかできずにいた。 ようやくできるタイミングがきたので、取り組んでみた。まだステージング環境では検証が終わっていないけれど、CIが通ったので修正点を一旦まとめる。

やったこと

Elasticsearch 7.9.1のDockerイメージを作成

kuromojiプラグインを入れたDockerイメージをDockerHubに作った。

https://hub.docker.com/repository/docker/patorash/elasticsearch-kuromoji

docker-compose.ymlを修正

Elasticsearchの公式のページにあるdocker-composeの設定を参考にした。

www.elastic.co

しかし、CircleCIでテストをする際にdocker-composeで行っているのだが、その際にElasticsearchのコンテナが起動しなかった。 起動しなかったときのエラーはこれ。

elasticsearch_1 | ERROR: [1] bootstrap checks failed elasticsearch_1 | [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

max_map_countの値が低すぎるから増やせという話だけれど、CircleCIのVMカーネルの値は変更できないということだった。

Running Elasticsearch 5 - Build Environment - CircleCI Discuss

このフォーラムで紹介されていた設定を追加した。environmentのところの"transport.host=localhost""bootstrap.system_call_filter=false"だ。 これで、CircleCIのdocker-composeでElasticsearchが起動するようになった。

version: '3.3'

services:
  elasticsearch:
    image: patorash/elasticsearch-kuromoji:7.9.1
    expose:
      - "9200"
      - "9300"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - cluster.initial_master_nodes=es01
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - "TZ=/usr/share/zoneinfo/Asia/Tokyo"
      - "transport.host=localhost"
      - "bootstrap.system_call_filter=false"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    healthcheck:
      test: ["CMD", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
      interval: 60s
      timeout: 30s
      retries: 3

関連gemの更新

  • elasticsearch-rails
  • elasitcsearch-model

この2つを更新した。

コードの修正

クラウドワークスさんのところの記事を参考にした。

engineer.crowdworks.jp

ここに書かれていた通り、_allが使えなくなったので削除したのと、copy_toを使うようにした。copy_toは6系で追加されたらしい。1つのキーワード検索用のフィールドを作り、そこに対して各フィールドをcopy_toで集める。今までは検索対象のフィールドをいくつも指定していたため、効率が悪かったけれど、これを使うことで1つのフィールドだけを検索対象とできるので効率がよくなるそう。

あとは、ngramの設定を変更した。以前はmin_gramを1に、max_gramを20に指定していたのだが、起動時にエラーとなった。max_ngram_diffを19に設定する必要があった(デフォルトで1のため)。

www.elastic.co

これで大体のテストは通ったのだが、ソートのテストがうまくいかなかった。

ソート順がElasticsearchへの指定順にならない

なぜかソート順がおかしいのでgemのコードを調べたところ、Elasticsearchから取得したデータをActiveRecordにするところでorderが定義されているとElasticsearchのソート条件よりもActiveRecordのソート条件のほうが優先されるように変更されていた…。

github.com

default_scopeでorderを定義していたため、elasticsearch-railsrecordsメソッドを呼んだ時点でもうダメだった。default_scopeでのorderを止めるのは変更点が多すぎて厳しいので、elasticsearch-railsから呼ばれた場合のみ、default_scopeを無効にしたいと考え、caller_locationsを使って対処した。

caller_locationsは処理が呼び出された場所を取得するので、そのパス情報の中にelasticsearch/model/adapters/active_record.rbが含まれていたらorderを無視するようにした。ただ毎回この処理が実行されるわけなので、オーバーヘッドがどれくらいになるのかが気になる…が、今はこれで。これで、Elasticsearchのソート順でデータが取れるようになった。

default_scope do
  paths = caller_locations.map(&:path)
  order(name: :asc) unless paths.any? { |path| path.end_with?('elasticsearch/model/adapters/active_record.rb') }
end

とりあえず、これでCIのテストが通るようになった👍

5にバージョンアップしたときと比べると比較的楽にバージョンアップできそう。

タブレット型Chromebookは実質Androidタブレットだった

先日、ふとChromebookが気になり始めてググっていたら、LenovoChromebookSurfaceみたいにタブレットにもなるタイプのやつを売っているのを見つけました。タブレットにもなるし、普通にキーボードも付いてくるのに4万円ちょいだったので、ボーナスが入ってから特に何も買ってなかったので思い切って買ってみました!(ここまで書いて、ボーナスが入ったから在宅勤務多いのでエアコンを買ったのを思い出した…)

買った機種は、Lenovo IdeaPad Duet Chromebook です。

www.lenovo.com

注文して3日程度で届きました。やはり新しいPCが来るとワクワクします!😁

開封の儀

f:id:patorash:20200926131139j:plain

chromebookの文字があります。

f:id:patorash:20200926131330j:plain

開けたところ。使い方のパターンが印刷されていました。

f:id:patorash:20200926131415j:plain

色はアイスブルー+アイアングレー。でも普段は立てるためにカバーつけることになるので色はあまり意識しなさそう。

f:id:patorash:20200926131628j:plain

立たせたところです。こんな感じか。カッコいい。

f:id:patorash:20200926131817j:plain

付属品は、充電用のUSB Type Cと、USBをイヤフォンジャックに変換するケーブル。インターフェースがUSB Type Cの1つのみ。 これは外部ディスプレイとか繋げないのかな?と思ってググってみたら、繋げられるらしい!アダプター買おうかな!

blog.itokoichi.com

f:id:patorash:20200926131945j:plain

そして、いよいよ起動…。うーん、速い!さすがChromebook

設定していく

ログインはPIN CODEでできるように

ログインには、当たり前だけれどGoogleアカウントが必要なので、長いパスワードとか設定していると入力が大変…。しかし、1度やってしまえば、あとはPIN CODEの設定ができるので、ログインのために長いパスワードを入力しなくてよくなります。昔にChromebook使おうとしたときはこれが辛かったので進歩してる!

Androidアプリが使える

正直、これが購入する判断に至ったものなのですが、めっちゃ便利。タブレットKindle Fire HD 8を使っていたのですが、Kindle以外にも他の会社の電子書籍サービスを使っているので、それが全部1つの端末で読めるようになりました。Kindleebook japanもGoogle Booksも読めるぞー!

また、AuthyやLastPassも入れられるので、いろんなサービスへのログインも簡単。設定もサクサクと終わらせられました。

ディスプレイの解像度を変更

初期表示だと文字がかなり大きいので、解像度を1440x900にしました。それ以上にすると、ちょっと文字が小さすぎて読みづらい…。

レビュー

半日くらいしか使っていませんが、とりあえずレビューします。

軽い

キーボードとかスタンドがあるとまぁまぁな重量がするけれど(それでも1kgは切る)、本体だけにすると450gらしいのでかなり軽いです。読書するには軽いは正義!

動画が綺麗

Chromebookなので、YouTubeは当然として、AndroidアプリでHuluやNetflixも入れられます。私はHuluを契約しているので、Google Play StoreからHuluをインストール。 10インチながら、1920x1200のWUXGAなので、フルハイビジョンで見ることができます。

縦持ちできるのがGood

キーボードを切り離したら縦持ちできるので、大きな本を読むのに便利です。また、横持ちならば漫画を見開きで見ても見やすいサイズなのでいいですね。

動作はキビキビ

CPUは8コアあるからか、まったくモッサリという印象はないです。また、ファンレスなので静か。

キーボードがやや打ちづらい

10インチに合わせるために、ホームポジション付近のよく使うキーに関してはまぁまぁいいサイズになっているのですが、記号系のキーとエンターキーが小さいです。特にエンターキーが細長くて見た目は微妙…。でも押しにくいというほどでもない…。あと、Ctrlキーがあってほしいところに検索キーがあります。WindowsでいうCapslockキーの位置です。ですが、設定からキーバインドは柔軟に変えられるので、検索キーとCtrlキーを入れ替えておきました。

Linux(Beta)が付いてた

WSLみたいな感じなのか、Linuxをインストールすることができます。Androidアプリの開発とかで使えるらしい…。ただ、コピーペーストがうまくいかないみたいで今のところの印象だと使いづらい。調べてきたコマンドがコピペできんの辛い。

メモリが少ない(4GB)ので、開発のメインマシンにはならないけれど、VSCode Onlineとかが出てきているので、今後に期待できるかも。

総評

カスタマイズで遊ぶというよりは、エンタメ系とか、執筆とかに気軽に使える端末としてすごく優秀だと思う。いわゆるノートPC型のChromebookと違ってタブレットとして使えるのがいい!なんかよく巷で「最近Androidタブレットが出ない〜。iPadは出てるのに〜!」というのをtwitterとかで見かけるが、もしかしたら、Androidタブレットは諦めてChromebookに統合していくつもりではなかろうか?Androidタブレットを探している人は、IdeaPad Duetを候補にするのがいいと思う。

なお、私はLenovo Storeで購入したが、AmazonでもAmazon限定モデルで販売していた(後で気づいた…)。違いは、Lenovo Storeだとディスク容量が128GBで、Amazonのモデルだと64GBというところ。ただし、容量が少ない分、定価はAmazonのほうが4千円くらい安い。

ストレージにGoogle Driveを使うのであれば、本体の容量は64GBでも十分かなぁと思えるので、Amazonモデルでもいいと思う。安いし。そして今さらに気づいたのだが、Amazonのモデル、9/30までの期間限定で10%オフクーポン付いてた。4万円くらいなので、3万6千円で買えるらしい!グヌヌ…!この情報を最初に知っていれば!!

CircleCIで使う処理をShellScriptにしていっている

CircleCIで時々思わぬところでライブラリ系のキャッシュがされずにエラーになってしまうことがあった。 処理を速くするために複雑なキャッシュをするようになっていたので、それが原因である…。久々に自分で見てみても複雑だなぁ〜と思う。

patorash.hatenablog.com

もっとシンプルにしたいなぁと思っていたら、たまたま以下の記事を見かけた。

qiita.com

これには感銘を受けた。makeはイマイチよくわかっていないので今のところは採用しないが、.circleci/config.ymlで使っているcommand類はShellScriptにすることが可能だ。ShellScriptにすることで、ローカル環境で試しやすくなる。config.ymlに直書きだと、試しづらい。当たり前だけれどなんだか目から鱗だった。

遅くても必ず完走させるようにしたい

キャッシュの不備が原因でテストがコケるのは本当にダサい…。しかも何が原因でライブラリの更新漏れやキャッシュし忘れが発生するのかを探すのが大変になってくると本末転倒…。

それならばもうシンプルに毎度毎度、ちゃんとライブラリの更新処理とキャッシュ処理をするようにした。これでもう予期しないエラーにはならないはず!😀

ただし、このせいで6分くらい遅くなっている…😫

workspaceを渡すのが遅い

CircleCIでは、workspaceを保存して次のジョブに渡すことができる。これをすると、次のジョブで環境構築する手間が省けるので速…そうなのだが、この受け渡し処理が遅い…。dockerイメージを含めているせいで容量も大きいというのはあるのだが、3分以上かかる。受け取るのも1分近くかかる。

1コンテナで環境構築を行い、テストを流す際は4コンテナにそれを渡して処理させているので、料金的には多少安いはずなのだが、時間的には直列になるので頂けない。

コマンドのShellScript化

これはローカルで実験がしやすくなったのでよかった。

エラーを見逃してしまう

ただ、CircleCIのconfig.ymlではステップ毎に定義していたものを1つのShellScriptにまとめたので、エラーの検知がうまくいっていなかった。エラーが起きているのに次の処理に行ってしまい、最後の処理が成功しているので終了コードが0になり、CircleCI上で成功したことになってしまっていた。

エラー対策

ShellScriptでもtry-catchみたいなことができれば…と思い、調べてみたら、trap関数を使えばできることがわかった。

zuqqhi2.com

手元にあるシェルスクリプトコマンドブックにも書いてあった。とにかく、これを使えばtry-catch-finallyができる。 エラーが起きようが起きまいが、docker-compose downはさせたかったので、finally的にやるようにした。

docker_compose_down() {
  docker-compose down
}

catch_error() {
  printf "エラーが発生しました"
  exit 1
}

trap docker_compose_down EXIT
trap catch_error ERR

上記の定義してあるファイル(common.sh)を、全てのShellScriptで読み込むように修正した。

#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0); pwd)
. $SCRIPT_DIR/common.sh

一旦これで様子見する。

高速化するには?

スタディストさんの開発ブログにCircleCIの高速化に効きそうな記事があったので、これらについて今後試していきたい。

medium.com

medium.com

それにしてもやはりdocker-composeを使ってテストをするのは速くなりにくいようだ…。 machineだと、resource_class: smallが選択不可能なので、smallにして並列数を2倍に増やす作戦も失敗に終わったし、打ち手に限りがある。

1台でキャッシュ用ファイルを作る戦略だったが、そろそろ見直したほうがいいかもなぁ…と考えている。 まぁdocker-composeを使っているせいなので(キャッシュしたいファイルがdocker volumeにあるから引っ張り出すのが遅い)もうちょい、いい方法がないか考える。まぁ速度よりも安定性をまずは重視する。

tmuxに慣れようとしている最中

便利なツールにも慣れていきたいなと思って、最近まで放置していたものにも挑戦していこうと思い、tmuxに再チャレンジしている。再チャレンジというのは、昔に使おうとして面倒になって断念していたからだ。

tmuxはターミナルの多重接続ソフトウェアである。詳しくはWikipediaを。

tmux - Wikipedia

いいところは、接続を終了せずにデタッチという形でセッションを残しておけるところ。普通のターミナルだと、終了したらセッションも終了するので、続きから作業ができないが、tmuxなら会社のPCでセッションをデタッチしておいて、家に帰ってから家のPCでセッションをアタッチして続きを作業する、というような使い方できる(そんな使い方はまだしたことはないが)

あとは、複数ウィンドウを立ち上げたり、ペインを分けて1画面で複数使えるようにしたりできるのが便利。。。なのだが、それはiTermやWindows Terminalでもできたから、そのために慣れきれなくて断念してきていた。

しかし、SSHした先で複数ウィンドウを使ったりペインを分けたりできるのは便利だろうし、なによりデタッチ・アタッチを使いたい機会がいつかくるだろうという想いもあった。

私は習うより慣れろ派ではなくてひとまず知識をザックリ入れてから慣れたい派なので、tmuxの記事を探そうとした。そうしたら、Kindle Unlimitedで読めるtmuxの本があったので、それを読んだ。

ターミナルマルチプレクサ tmux 入門

ターミナルマルチプレクサ tmux 入門

知りたいことも一通り書いてあったし、ザックリと知識が入ってきたことで若干苦手意識が薄れた。まずウィンドウに慣れようと思って、ここ数日はiTermでタブを使わずにtmuxでウィンドウを作って作業していた。 本を読んで、ペインの使い方もなんとなくわかったので、とりあえず素直に分割して使うところまではやれている。幅の調整とかはまだ。 慣れないのが、コピーモード。まだ全然慣れないのだけれど、慣れたら便利そうなので、少しずつやっていく。

本はざっくりと知れるのに便利なのだが、なんだかんだでチートシートも欲しかったのでqiitaにあるものを参照させてもらった。

qiita.com

こちらにあるものから、.tmux.confを作って、マウス操作である程度できるようにした。

そして今はWindowsでブログを書いているのだが、tmuxの操作の検証のためにWindows Terminalを立ち上げているが、やはりCtrlキーはCapsLockキーの位置にあってほしいなと強く感じている…。あまりキーバインドは弄っていないのだが、いじったほうがいいかなぁと感じ始めている。でもどのキーがどれになるかわからなくなるので、キーボードのキーの上に上書きできるシールが欲しい…。

【追記】

Microsoft製のCtrl2CapというCtrlキーとCapsLockキーを入れ替えるソフトの存在を知ったのでインストールしといた。便利。

docs.microsoft.com

でもCtrlキーにCapsLockキーが割り当てられてないような…😅

ansibleでSSH接続時にToo many authentication failuresが出た場合の対処法

小ネタです。表題の通りですけど、エラーが出ました。

ちょうど他のサーバにSSHするときにも同じエラーが出ていました。

kou-i.hatenadiary.org

通常のSSHでは、.ssh/configの中でIdentitiedOnly yesを設定すれば、問題なく接続できるようになりました。

そして、ansible-playbookを実行しようとしたら、同じようなエラーが…。

TASK [Gathering Facts] ***********************************************************************************************************************************************************************************************************************
fatal: [vagrant]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Received disconnect from 127.0.0.1 port 2222:2: Too many authentication failures\r\nDisconnected from 127.0.0.1 port 2222", "
unreachable": true}

PLAY RECAP ***********************************************************************************************************************************************************************************************************************************
vagrant       : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0

インベントリではどうやってIdentitiedOnlyを指定するんだ?エラーメッセージでググったら、即出てきました。

Ansible SSH too many authentication failures · GitHub

ansible_ssh_extra_args="-o IdentitiesOnly=yes"を追加します。

[app]
vagrant ansible_host=127.0.0.1 ansible_port=2222 ansible_user=vagrant ansible_ssh_private_key_file=/Users/*******/sources/github.com/********/ansible-project/.vagrant/machines/default/virtualbox/private_key ansible_ssh_extra_args="-o IdentitiesOnly=yes"

これで問題なくansible-playbookを実行できました👏

ansibleでheroku cli経由でherokuに自動ログインする方法

herokuへのメンテナンスを行うための中継サーバの構成をansibleで作っている最中です。

やりたいことはほぼできたのですが、1つだけ課題が残っていました。それは、ansibleでherokuに自動ログインできないことです。今回、それをなんとか自動ログインさせることに成功したので、メモとして残しておきます。

expectモジュールだとNG

herokuへのログインはheroku cliを使えばCUIでできるので、ansibleのexpectモジュールを使ってログインしようとしたのですが、なぜかうまくいきませんでした。 どうも、herokuのパスワードを入力後の改行コードが何故かパスワードの一部として解釈されてしまい、タイムアウトしてしまいました。

NGだったやつを残しておきます。

---
- name: heroku-cliのインストール
  command: sudo sh -c "export PATH=/usr/local/bin:$PATH && curl https://cli-assets.heroku.com/install.sh | sh"
  args:
    creates: /usr/local/bin/heroku

- name: pexpectのインストール
  pip:
    name: pexpect
    executable: pip3.8

- name: Heroku Loginする
  become_user: "{{ user }}"
  expect:
    command: heroku auth:login --interactive
    responses:
      "Email: ": "{{ heroku_email }}"
      "Password: ": "{{ heroku_token }}"
    echo: yes
  environment:
    PATH: "/usr/local/bin:{{ ansible_env.PATH }}"
  no_log: yes

scriptモジュール経由でexpectを使えばOK

入力の自動化はexpectでできるはずなので、もっと具体的に書き込めばいいかなと思い、expectコマンドをインストールして直接使うようにしてみました。

roleのファイルはこれ。 変数でheroku_emailheroku_tokenを渡しています。(2要素認証にしているためパスワードの代わりにトークンを使用) ローカルにscriptがあればいいので、scriptモジュールで指定。argsのexecutableで実行される命令としてexpectを指定しています(デフォルトだとshになる)

---
- name: heroku-cliのインストール
  command: sudo sh -c "export PATH=/usr/local/bin:$PATH && curl https://cli-assets.heroku.com/install.sh | sh"
  args:
    creates: /usr/local/bin/heroku

- name: yumでexpectのインストール
  yum:
    name: expect
    state: present

- name: ローカルから、expectスクリプトを実行してheroku loginする
  become_user: "{{ user }}"
  script: heroku_login.exp
  args:
    executable: /usr/bin/expect
  environment:
    PATH: "/usr/local/bin:{{ ansible_env.PATH }}"
    HEROKU_EMAIL: "{{ heroku_email }}"
    HEROKU_TOKEN: "{{ heroku_token }}"
  changed_when: false

expectスクリプトである、heroku_login.expは、これ。肝は、sendでパスワード文字列を送った後に入れているsleep 1です。

spawn heroku auth:login --interactive
sleep 1
expect "Email: "
send "$env(HEROKU_EMAIL)"
send "\r"
sleep 1
expect "Password: "
send "$env(HEROKU_TOKEN)"
sleep 1
send "\r"
sleep 2
interact

heroku cliは、入力されたパスワード文字列を****という感じに隠してくれるのですが、表示が****になる前に改行コード\rを送ってしまうと、パスワードの1部と解釈されるみたいでした。

そのため、パスワード入力後に1秒のsleepを差し込んで、****に変換されるのを確認した後、改行コードを送るようにしたところ、ログインできました👍