標準入出力で OpenSSH の -L や -R のようなポートフォワードを行うツールを作成しました。
ローカルへのインストールは go get github.com/ngyuki/somux
で、Docker イメージは docker pull ngyuki/somux
で取得できます。
使い方
主な用途は 「ローカルのポートをリモートの Docker ホスト上のコンテナのポートへ転送、およびその逆」 です。
# リモートの Docker ホストを指定 export DOCKER_HOST=example.com # nginx を実行 docker run --name=nginx --rm -d nginx:alpine # ローカルとコンテナの双方で somux を実行してポートフォワードを実行 # - ローカルの 8080 ポート -> コンテナの 80 ポート # - コンテナの 9000 ポート -> ローカルの 9000 ポート somux -v \ -L 8080:nginx:80 \ -R 9000:localhost:9000 \ docker run --name=somux --rm -i --link=nginx ngyuki/somux -v & # ローカルの 8080 からリモートのコンテナのポートに転送される curl http://localhost:8080/ # ローカルで 9000 ポートでリッスンしてみる nc -lk 9000 & # コンテナの 9000 ポートからローカルのポートに転送される echo hello | docker exec -i somux nc localhost 9000
なぜこんなものが必要か
普段 Docker Desktop で開発を行っているのですが、WSL1 + Docker Desktop(Hyper-V) という構成だったため(※1)、コンテナにホストのディレクトリをマウントしても実際には CIFS となり(※2)、I/O性能が非常に悪く、また、inotify などのネイティブのディレクトリ変更監視が効かないという問題がありました。
- ※1 NTFS 上のディレクトリをワークスペースにしたかったため
- WSL2 の 9p よりは WSL1 の DrvFS の方が良かった
- ※2 最近はgRPC ベースの FUSE に変わっているのでだいぶ改善されていると思います
ので、Docker Desktop はやめてリモートの Docker ホストに unison でディレクトリを同期して開発をすることにしました。
リモートの Docker ホストを使う場合、コンテナでポートを expose するだけでは localhost ではアクセスできないし、コンテナからローカルの Windows のポートに接続しようとしても host.docker.internal
は利用できず Windows 機のIPアドレスをベタに指定する必要があり、素の Docker Desktop と比べてかなり体験が損なわれます。
そこで、下記の記事でもチラッと書いていたように、ポートの転送は sshd のコンテナを追加して OpenSSH のポートフォワードを利用していました。
次のような感じです。
# docker-compose.yml version: "3.7" services: app: # ...snip... sshd: image: ngyuki/insecure-sshd networks: default: aliases: - host.docker.internal
# Makefile all: make -j up fwd up: docker-compose up fwd: while ! docker-compose exec -T sshd nc -zv localhost 22; do sleep 1; done ssh root@localhost -C -N -g \ -o ProxyCommand="docker-compose exec -T sshd nc -v localhost 22" \ -o ExitOnForwardFailure=yes \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -L 8080:app:80 -R 9000:localhost:9000
これでローカルからリモートへは Docker でさえ接続できれば、あとは docker exec
の標準入出力上で SSH を通して双方向にポートフォワードができます。
これでも十分でしたが「これだけのために sshd は過剰では」という気もしたので、もっとシンプルに docker exec
の標準入出力を使ってポートフォワードができるツールがありそう・・と思って探したのですが、ぱっと見つからなかったので作りました。
somux を使えば次のようにできます。
# docker-compose.yml version: "3.7" services: app: # ...snip... somux: image: ngyuki/somux init: true networks: default: aliases: - host.docker.internal command: [tail, -f, /dev/null]
# Makefile all: make -j up fwd up: docker-compose up fwd: while ! docker-compose exec -T somux true; do sleep 1; done somux -L 8080:app:80 -R 9000:localhost:9000 \ docker-compose exec -T somux somux
さいごに
諸般の事情で PC が新しくなり、OS が Windows 10 Pro → Windows 10 Home になったことで Hyper-V が使えなくなったので、普段使いのディストリ(fedora)と Docker Desktop をともに WSL2 に変更しました。
WSL2 の場合、NTFS 上のディレクトリをワークスペースにすると WSL2 からは 9p でのアクセスになってしまい、WSL1 の DrvFs と比べて性能の劣化が激しすぎて使い物にならないのと、inotify も効かなくなってしまうので、WSL2 の ext4 領域をワークスペースをすることにしました。
すると、以前の構成と比べて Docker Desktop でもかなり快適になりました。WSL2 の fedora と Docker Desktop では別のディストリなわけなので ext4 領域はマウントはできないか(WSL1 の VolFS は Docker Desktop でマウントできないですよね)、なにかしらネットワークファイルシステムになると思っていたのですが、そんなこともなく、WSL2 の fedora の ext4 上の領域が Docker Desktop でもそのまま ext4 として見えています。なので I/O も早いし、inotify などの変更監視も問題ありません。
ので、別にこんなことしなくても普通に Docker Desktop で良いのでは・・・という気もしています。なにか別の用途で使えればいいんですけど、うーん、思いつかない。