こんにちは、id:tsub511 です。
TELNET プロトコルには全く馴染みがないのですが、今回たまたま使う機会があり、かつ調べても割と見つけられない情報だったので記事を書いてみました。
curl で TELNET プロトコルを使う
curl は HTTP/HTTPS 以外のプロトコルも使うことができます。
curl のドキュメントを確認すると、サポートしているプロコトルは DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET and TFTP
のようです。
$ man curl # in macOS curl(1) Curl Manual curl(1) NAME curl - transfer a URL SYNOPSIS curl [options / URLs] DESCRIPTION curl is a tool to transfer data from or to a server, using one of the supported protocols (DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET and TFTP). The command is designed to work without user interaction. curl offers a busload of useful tricks like proxy support, user authentication, FTP upload, HTTP post, SSL connections, cookies, file transfer resume, Metalink, and more. As you will see below, the number of features will make your head spin! curl is powered by libcurl for all transfer-related features. See libcurl(3) for details. ...
例えば以下のように指定することで TELNET プロトコルで対象のサーバーに接続することが可能です。
$ curl telnet://localhost:3306
ユースケース
docker-compose ではコンテナ間の依存関係を depends_on
で定義できますが、コンテナ内でサーバーなどが立ち上がるまでは待ってくれません。
そこで、以下のドキュメントに書いてあるような方法でコンテナ間で依存しているサーバーに対するヘルスチェックを行うことで解決できます。
実際には以下のスクリプトで MySQL サーバーの起動を待ってから別のコンテナを実行するような仕組みにしていました。
#!/bin/bash host="$1" shift cmd="$@" until mysql -h "$host" -u root -e 'show databases' > /dev/null 2>&1; do >&2 echo "MySQL is unavailable - sleeping" sleep 1 done >&2 echo "MySQL is up - executing" exec $cmd
ただ、この方法だと mysql コマンドがコンテナ内にインストールされている必要があります。
本番環境でのコンテナの実行を考慮すると、mysql のクライアントはインストールする必要がなかったので mysql コマンド以外の方法で MySQL サーバーの起動を確認する必要がありました。
そこで、ベースイメージの都合でたまたま curl がインストールされていたので curl の TELNET プロトコルを使うことにしました。
変更後のスクリプトが以下になります。
#!/bin/bash host="$1" shift cmd="$@" until echo 'quit' | curl telnet://$host:3306 > /dev/null 2>&1; do >&2 echo "MySQL is unavailable - sleeping" sleep 1 done >&2 echo "MySQL is up - executing" exec $cmd
変更したのは 7 行目のみで、mysql コマンドを curl に置き換えています。
こうすることで mysql のクライアントをインストールせずに MySQL サーバーが起動するのを待ってから別のコンテナを実行することができるようになりました。
解説
例えば以下のように実行すると、TELNET プロトコルを使って疎通確認ができます。
$ docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=1 mysql $ curl -s -o /dev/null telnet://localhost:3306
ただしこのままだと Ctrl+C などでコネクションを切るまで curl が実行されたままになります。
TELNET プロトコルは対話型であるため、コネクションを張りっぱなしになるという認識です。
Ctrl+C が必要ということは、上述したスクリプトでは使えません。
ではどうすれば疎通確認後に自動でコネクションを切れるでしょうか。
実は以下のように quit
を curl に標準入力で渡すことで解決できます。
$ docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=1 mysql $ echo 'quit' | curl -s -o /dev/null telnet://localhost:3306
quit
とは何かというと、telnet コマンドでは quit
というコマンドを指定することで telnet の接続を切ることができます。
$ man telnet # in macOS with brew install telnet TELNET(1) BSD General Commands Manual TELNET(1) NAME telnet -- user interface to the TELNET protocol SYNOPSIS telnet [-468EFKLNacdfruxy] [-S tos] [-X authtype] [-e escapechar] [-k realm] [-l user] [-n tracefile] [-s src_addr] [host [port]] DESCRIPTION The telnet command is used to communicate with another host using the TELNET protocol. If telnet is invoked without the host argument, it enters command mode, indicated by its prompt (``telnet>''). In this mode, it accepts and executes the commands listed below. If it is invoked with arguments, it performs an open command with those arguments. Options: ... quit Close any open TELNET session and exit telnet. An end of file (in command mode) will also close a session and exit.
そして quit
を使った telnet コマンドを自動的に終了するための方法が以下の記事で紹介されていました。
上記の記事を参考に、curl でも同様の方法を試してみたら動いた、ということになります。
ただし、curl に標準入力を渡すことで TELNET プロトコルにコマンドを渡すことができる、という挙動自体は curl のドキュメントを確認しても見つけられませんでした。
公式の情報で裏が取れない限りは当記事の事例のように開発環境でのみ使った方が良いかもしれません。