🐳

Docker in Docker(DinD)ホストコンテナでsshdも動かす

2023/04/17に公開

経緯

Dockerコンテナを使ったバックエンド開発で、下記のようなことをしたい場面がありました。

サーバ構成

  • ファイルコンテンツを配信するAPIコンテナを立てている
  • APIコンテナはコンテナホストの特定のディレクトリをmountしており、そのディレクトリに置かれたファイルを取得してAPI配信している
  • バッチサーバからコンテナホストの上記のディレクトリに定期的に新しいファイルを配置している

このような仕組みのシステムは全然珍しくはないと思いますし、それぞれの要素の実装もバックエンド開発の場においてはよくある内容だと思います。
このような仕組みのシステムを作ることはそんなに難しくはない一方で、この仕組みの動作確認やテストをやろうと思うと少し悩むかもしれません。

今回は、このような仕組みのシステムの動作確認やテストを実現するために、Dockerとsshdが稼働するコンテナを作成してみました。

TL;DR

一連の動作確認ができるコンテナ環境下記リンクにて公開しました
https://github.com/Tarrown/dind-with-sshd

作るもの

今回は下記の図の構成に従ってコンテナを作成します。
containers

  • Host container
    • Dockerホストを模したコンテナ。Batchコンテナとの通信のためにsshdも導入する
  • Guest container
    • Host container内で動くコンテナ
    • 動作確認用に簡単なhttpサーバを動かす
    • httpサーバで出力するhtmlファイルは、Host containerにマウントしたディレクトリから取得する
    • Host containerとポートバインドしたポートをHost container側でLocalhost(手元のマシン)とポートバインドするすることで、Localhostにアクセスして擬似的に外界とGuest container間の疎通確認を可能にする
  • Batch container
    • Host containerとssh通信(鍵認証)するためのコンテナ
    • Guest containerがマウントしているHost containerのディレクトリにhtmlファイルを新たに配置する
    • sshログインができればファイル操作は問題ないはずなので、今回の動作確認ではhtmlファイルは手動で再配置する

実際に自分が開発しているシステムではhtmlファイルを配信するようなことは行なっていませんが、webサーバの方が外界からの動作確認が簡単なのでhtmlファイルを配置することにしました。

成果物(再掲)

上記の仕様に従って作ったものがこちらになります。
https://github.com/Tarrown/dind-with-sshd

動作環境
OS: macOS13.3
CPU: Intel Core i5
Docker desktop: 4.18.0

以下、このプロジェクトを使って説明します

使い方

コンテナ起動+α

$ /bin/bash up.sh

このスクリプトで上で説明したコンテナ構成図の環境は全て整います。

動作確認

まず、Guest containerにLocalhost経由でアクセスしてみます。
今回は、簡単なWebサーバを立てているので、ブラウザからlocalhost:1080にアクセスして確認します。
before
"Hello"と黒文字で表示されていることが確認できます。

次に、Batch containerに入って、Host containerにsshログインを行います。

$ docker exec -it batch-container bash // ホストマシンで実行
testuser@6d1562ca2f37:/$ ssh -i ~/.ssh/id_rsa host-container // sshログイン
The authenticity of host 'host-container (192.168.0.2)' can't be established.
ED25519 key fingerprint is SHA256:6h++WV3vmBoDNE6mwMLmGr3q9NaqsUNxem2Sc10BUZ0.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'host-container' (ED25519) to the list of known hosts.
Welcome to Alpine!

The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <https://wiki.alpinelinux.org/>.

You can setup the system with the command: setup-alpine

You may change this message by editing /etc/motd.

d921f8fbb878:~$ // Host containerへログイン成功

ログインできました。

次に、Guest containerがマウントしているディレクトリに移動してhtmlファイルの中身を書き換えてみます。

d921f8fbb878:~$ cd /work/guest/mnt/
d921f8fbb878:/work/guest/mnt$ cat << EOF > index.html
> <html>
> <font color="orange">Hello world</font>
> </html>
> EOF

最後に、改めてブラウザでlocalhost:1080にアクセスして出力を確認してみます。
after

メッセージが"Hello world"となって、色もオレンジ色に変わっていることが確認できます。

解説

今回のメインであるHost containerのDockerfileについて解説します。

https://github.com/Tarrown/dind-with-sshd/blob/main/host/Dockerfile_host

Docker in Docker(DinD)

Dockerコンテナの中でDockerを動かすことをDinDというらしいです。Docker Official Imageとして提供されています。
https://hub.docker.com/_/docker
ベースとなるイメージがAlpine Lunuxで統一されているようなので、厳密にコンテナホストの振る舞いをDinDを使ってエミュレートしたいと思った時に困るかもしれません。
ちなみに余談ですが、DinDコンテナを立てる時は特権を付与する必要があります。

sshd in Alpine Linux

こちらの記事を参考にしました
https://dev.to/yakovlev_alexey/running-ssh-in-an-alpine-docker-container-3lop
sshdを使うにあたって、プロセスを管理するためにopenrcを使っています。

ハマったポイント1

今回、Batch containerがHost containerにsshログインするときに使うユーザをコンテナ立ち上げ時に作成しています。
https://github.com/Tarrown/dind-with-sshd/blob/main/host/Dockerfile_host#L10-L11
sshログインには公開鍵を使った認証を採用しているので、パスワードの設定は不要だと思っていたのですがログインに失敗しました。パスワードを設定するか、sshd_configのPermitEmptyPasswordsを許可するかのどちらかの対応が必要みたいなので、今回は前者を選びました。

ハマったポイント2

ここが一番ハマりました。
https://github.com/Tarrown/dind-with-sshd/blob/main/host/Dockerfile_host#L19-L19

CMDの部分に単純にrc-service sshd startと書けばコンテナ起動時にsshdが起動してくれると思っていたのですが、このやり方だと起動に失敗してコンテナが立ち上がりません。
なぜなのかはイマイチわかっていませんが、rc-statusを一度実行した後にrc-service sshd restart(startではなくrestart)を実行した場合に限ってはsshdの起動(実際には再起動)に成功します。

コンテナ起動時にsshdとdockerを同時に起動させる

最後に、コンテナ起動時にsshdとdockerをそれぞれ起動させるようにCMDを修正しました。
https://github.com/Tarrown/dind-with-sshd/blob/main/host/Dockerfile_host#L19-L19
この/usr/local/bin/dockerd-entrypoint.shは、ベースとなっているDinDのイメージからコンテナを作ると配置されるスクリプトになっており、このスクリプトを実行することでdockerdが起動します。なので、sshdを起動した後にこのスクリプトを実行するようにしています。

さいごに

既存のイメージにsshdを入れれば動くものだと軽く見ていましたが、思ったより時間がかかりました。

Discussion