home / uni / docker-on-mac MacでDocker

 Docker Desktopを使うのが楽なのだが、仕事でAmazon Linuxのイメージでsystemdを動かしていたときに、systemdがv247以上じゃないと動かないというアップグレードに見舞われ、しばらくは4.2.0で頑張っていたが、Amazon Linuxのイメージが更新される兆しがないので、それ以来コマンドライン版を使っている。 動かなくなった時はほんとあせった。 現状のDocker Desktopがどうなっているかは使っていないので全くわからない。

 MacのDockerはQEMU上のLinux仮想マシンで動くようになっていて、QEMUの設定が面倒らしいのだが、Lima(Linux Machines)というのを使うとその辺りをやってくれる。 さらにこの仮想マシン上でDockerを使う設定が必要だが、これはColima(container runtimes on macOS (and Linux) with minimal setup)というのが全部やってくれる。 ・・・なんとなくLinux on MacとContainer on Limaだと思ってたんだが。

Homebrewを入れる

 もし入れてなければ。 Homebrewに書いてある指示に従う。 インストールの最後で.bash_profile(zsh派ではなくbash派なので)に書く内容を吐き出すので書き込んでおく。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> .bash_profile

終わったら.bash_profileの内容を反映させる。 sourceしてもよいが、個人的にはシェルを再起動する派。

Dockerを入れる
brew install colima docker

QEMU・LimaはColimaの依存で自動的に入る。

VM起動
colima start

 これでコマンドラインでDockerが使えるようになっている。

Dockerのちょっとした設定

 コンテナからのデタッチがデフォルトで Ctrl+PCtrl+Q というシーケンスになっているが、 Ctrl+P がBashのヒストリを遡るショートカットと当たっていて、 Ctrl+P を2回押さないと履歴が出ない(そしてふたつ遡るので Ctrl+N で戻すことになる)ので、 ~/.docker/config.json に次の1行を書き加えてある。

    "detachKeys": "ctrl-\\,ctrl-q",

これでデタッチキーは Ctrl+\Ctrl+Q になる。

home / uni / docker-on-mac クロスプラットフォームエミュレーション

 x86でARM、ARMでx86のバイナリを動かす。 カイシャの機械がM1 Macになったのだが、以前作成したDockerfileをdocker buildしたら、MySQL 5.7のARMのイメージがないらしく、起動できなかった。仕方なくx86エミュレーションで起動することに。 Google CloudのCloud Runなんかも現時点ではx86_64のイメージを要求されるので、この方法でイメージの動作確認ができる。

 基本的にはこれだけ。

colima start -a x86_64 x86_64
最初のx86_64-aオプションのパラメータで、起動するQEMUのアーキテクチャを指定している。 後ろのx86_64はプロファイル名(設定名)で、指定しなければdefaultが使われるが-aを指定する時はプロファイル名も指定しないとだいたい痛い目に遭う。 以後、Dockerのコマンドはx86_64側で動くようになる。

 止める場合は

colima stop x86_64

 プロファイルは起動時のオプションを覚えているので、次回以降は環境名だけ指定すればよい。

colima start x86_64

というか、アーキテクチャの指定は最初にプロファイルを作った時のみ正常に動く模様。 アーキテクチャ指定を忘れてしまった場合は、一度colima delete x86_64のようにして、そのプロファイルを消して作り直せばよい。 このコマンドは仮想ディスクなんかも全部消す。 既存の環境に間違ってアーキテクチャを指定した場合は悲惨な結末が待っているので注意すること。

 x86のVMが起動したところでcolima listすると、ARMとx86が両方動いていることがわかる。

PROFILE    STATUS     ARCH       CPUS    MEMORY    DISK     RUNTIME    ADDRESS
default    Running    aarch64    2       2GiB      60GiB    docker     
x86_64     Running    x86_64     2       2GiB      60GiB    docker     

DockerはこのVM上で動くので、仮想ディスクなども全部別である。 当然コンテナもイメージもボリュームも全部別である。 共有は別途考える必要がある。

 Dockerがどちらを使っているかは、Docker側のコマンドで分かる。

$ docker context ls
NAME              DESCRIPTION                               DOCKER ENDPOINT                                      ERROR
colima            colima                                    unix:///Users/ec2-user/.colima/default/docker.sock   
colima-x86_64 *   colima [profile=x86_64]                   unix:///Users/ec2-user/.colima/x86_64/docker.sock    
default           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                          

*が付いているのが今使っている環境。 Colimaのデフォルトのプロファイルで起動したVMはcolimaになっている。 切り替えるにはdocker context use colimaのようにすればよい。 特に、どちらか一方のVMを colima stop で止めた時は、どのコンテキストに切り替わったか確認する必要があるだろう(そして、だいたいDocker側の default に切り替わっていて動かないわけだ)。

home / uni / docker-on-mac SSHエージェントフォワーディング

 docker build時などにSSH公開鍵で認証しているGitのリポジトリからデータを引っ張ってきたいことが割と頻繁に発生する。 そんな時のお話。

既存の知識でがんばる

 docker build時でなければ(つまりdocker runで動かしたコンテナの中ならば)、SSHデーモンを起動してしまい、エージェントフォワード有効で接続して使う、という手がある。 これならば既存の知識だけでエージェントフォワーディングが扱える。

 とりあえず公開鍵を設定しないといけない。 ホストで適当に鍵を作る。

cd ~/.ssh
ssh-keygen -f docker

たとえばUbuntuコンテナを起動して:

docker run -it --rm -p 2222:22 ubuntu
コンテナ内で:
apt update
apt install -y --no-install-recommends openssh-server
ssh host.docker.inetrnal

--no-install-recommends を付けないと、systemdから何から大量にインストールされてしまう。 逆に、Ubuntu限定かもしれないが、動作確認できる状態でsystemdを入れたいなら( --no-install-recommends を付けずに)openssh-serverをインストールするのが一番楽かもしれない。 bashの代わりに /sbin/init を起動した状態が、冒頭の「systemdを動かして」いる状態になる。

 最後のSSHコマンドはホームに.sshディレクトリを作っている。 接続先はSSHで接続できるホストならばどこでもよく、認証は失敗してよい。 host.docker.internalは起動したホストを示すホスト名で、Dockerが自動的に設定している。 Macならばシステム設定・共有・リモートログインを有効にしていればSSHデーモンが起動している。 続いて鍵をコピーする。

cd ~/.ssh
cat >> authorized_keys
さっきホストで作ったubuntu.pubを貼り付ける。 コンテナでSSHデーモンを起動:
/usr/sbin/sshd -De

おっと、Missing privilege separation directory: /run/sshdと言われる。 コンテナにはマニュアルがインストールされていないので、他のOpenSSHが入ったシステム、具体的にはMacのホスト側で適当にマニュアルを参照すると「The directory should not contain any files and must be owned by root and not group or world-writable」と書いてあるので、この通り作ってあげる。

mkdir -p -m 700 /run/sshd
/usr/sbin/sshd -De

ちなみに-Dはデーモン化しない、-eはログを標準エラー出力に出す。 Cygwinでsshサーバーで使ったのと同じ。 UNIXとその仲間たちはこういう知識が使い回せる。それに対してほんとWinなんちゃらっていうシステムは・・・。

 ホスト側から接続:

ssh -p 2222 -i docker root@localhost

コンテナを作り直すとコンテナのSSHホスト鍵が変わってしまうので、一度 ~/.ssh/known_hosts から削除するか、ホスト鍵のキーをバックアップしておいて、コンテナを新しく作った時に送り込んでやればよい。 ホスト鍵はUbuntuなら /etc/ssh にある。

 MacではデフォルトでSSHエージェントが起動しているので ssh-add すれば -i は不要になる。 また、 -A でエージェントフォワーディングが有効になるので、ホスト側の適当な鍵を ssh-add しておけばその鍵をコンテナ内でも使えるようになる。

ssh-add docker
ssh-add key-for-host-exampe-com
ssh -p 2222 -A root@localhost
ssh-add -l
ssh user@host.example.com

home / uni / docker-on-mac バインドマウントを使う

 実はQEMUのVMにはSSHでアクセスできる。

colima ssh

プロファイルを指定しないとdefaultが使われる。 指定したい場合は -p を使う。

colima ssh -p x86_64 uname -m
x86_64

ということは、ホストからQEMUのVMにエージェントフォワードありで接続して、VM内のソケットをコンテナに教えてやればよい。 デフォルトではVMのエージェントフォワードが有効になっていないので、Colima起動時に --ssh-agent を指定するrestart では指定できないので、VMが起動している場合は一度止める。

colima stop
colima start --ssh-agent
colima ssh printenv SSH_AUTH_SOCK
/tmp/ssh-XXXXIdPBJp/agent.3555

オプションは一度設定すれば覚えている( ~/.colima/default 以下などに設定が書き込まれる)ので、次回以降は指定しなくてよい。

 で、バインドマウントの方だが、DockerはホストではなくVMのディスクをマウントしている。 つまり、 -v--mount で指定したディレクトリは、VMのディレクトリとして解釈される。 ではなぜホストであるMacのホームディレクトリがマウントできるかというと、Colimaがホストの /Users をVMの /Users に見えるようにマウントしているからである。

colima ssh mount | grep /Users
:/Users/ec2-user on /Users/ec2-user type fuse.sshfs (rw,nosuid,nodev,relatime,user_id=501,group_id=1000,allow_other)

AWS EC2上の mac2 ホストなんでユーザー名が ec2-user になってっけど。 こうなっているので、 docker run -v /Users/ec2-user/docker:/app とすれば、VMの /Users/ec2-user/docker をマウントする=ホストの /Users/ec2-user/docker がマウントされるので、ホストのデータがコンテナからも見える。 逆にいうと、 /Users の下にないホストディレクトリはマウントできない。 ホストとVMのルートディレクトリに適当にフォルダを作って、Dockerでバインドマウントしてフォルダがどう見えるか確認してみるとよい。

 結局、VMの SSH_AUTH_SOCK に設定されているパスを、ホストのコマンドラインに直接指定してバインドマウントすればOKで、マウントしたパスをコンテナの SSH_AUTH_SOCK 環境変数に設定してやればよい。

docker run -it --rm -v $(colima ssh printenv SSH_AUTH_SOCK):/tmp/ssh-auth.sock -e SSH_AUTH_SOCK=/tmp/ssh-auth.sock ubuntu

うまくいかないときは、 colima ssh の中で ssh-agent -l してみるとよい。これで見えていなければVMがホストのソケットを見失っているので、 colima restart でVMを再起動すればよい。

 なお、VM内には /run/host-services というディレクトリがあって、その中に ssh-auth.sock というシンボリックリンクがあり、これがエージェントフォワードのソケットを示している。 一見、 /run/host-services をバインドマウントすれば万事解決のように見えるが、シンボリックリンクなのでリンクが指してる先も同じ名前でマウントしないと動かない。 結局、ソケットのバインドマウントも作らなければならず、二度手間になる。 いまのところ、VMの SSH_AUTH_SOCK にはソケットの実体の方が設定されているので、現状では colima ssh printenv SSH_AUTH_SOCK を使った方が確実である。

  /run/host-services の流儀に合わせたいのなら、 colima ssh printenv SSH_AUTH_SOCK の結果を /run/host-services/ssh-auth.sock にバインドマウントすればよい。 ただ、 SSH_AUTH_SOCK 環境変数にも同じパスを設定する必要があり、コマンドラインが長くなってしまうので、ここでは /tmp/ssh-auth.sock にマウントしている。

home / uni / docker-on-mac docker buildで使う


Copyright (C) 2023 akamoz.jp

$Id: mac-docker.htm,v 1.4 2023/10/24 14:50:23 you Exp $