TenForward

技術ブログ。はてなダイアリーから移転しました

docker-compose.yaml の volumes の指定でハマった話

私はDockerもDocker Composeもcontainerdも素人で、今回の問題がよく知られた問題なのか、そうでないのかもよく知りません。ちなみにDocker Composeは今日まで使ったことがなかったし、Rancher Desktopも今日はじめてインストールしました(^^)。

この辺りのコンテナランタイムの知識もないので、用語の使い方も間違ってたりするかもしれませんがご容赦を。

このエントリは単に「ちょっと時間使って調べた」ので「オレ頑張ったよ」と誰かに言わないと気がすまないし、最近技術ブログ書いてないよなと思ったので書いてみるだけです。

問題

「Sensu Goをコンテナで起動してるけど、コンテナを再起動するたびに初期化されるので、Sensuに詳しいだろうからちょっと見て」という相談を受けたのが始まりです。(ちなみにSensu Go詳しくないです。Sensu Goを使ったシステムをちょっと作ったことがありますが)結局は Sensu の知識は全く不要でした。

Rancher Desktop環境で、Sensu Goの公式ドキュメントにあるDocker Composeを使ってsensu-backend(sensuサーバー)を起動すると、再起動するたびにetcdのデータが初期化されているのか、毎回クライアント側も初期設定する必要があるということでした。

ちなみに yaml はこんな。

version: "3"
services:
  sensu-backend:
    ports:
    - 3000:3000
    - 8080:8080
    - 8081:8081
    volumes:
    - "sensu-backend-data:/var/lib/sensu/sensu-backend/etcd"
    command: "sensu-backend start --state-dir /var/lib/sensu/sensu-backend --log-level debug"
    environment:
    - SENSU_BACKEND_CLUSTER_ADMIN_USERNAME=admin
    - SENSU_BACKEND_CLUSTER_ADMIN_PASSWORD=admin
    image: sensu/sensu:latest

volumes:
  sensu-backend-data:
    driver: local

解決

問題自体はすぐに解決しました。手元に Rancher Desktop がなかったので、Linux 上の Docker 環境で docker-compose.yaml を書いて起動してみました。

しかしどのようにやっても、ちょっとコンテナを強制終了しても、特に再起動後は問題なく使えます。で、このことを相談を受けた人に伝えると、すぐに

  • Linux(のDocker)でやったらたしかに問題は起こらない
  • 試しに Rancher Desktop のコンテナランタイムを "containerd" にしていたのを "dockerd(moby)" に切り替えたら問題が起こらなくなった

というわけで、これで解決です。

問題を再燃させる

でも、気になりますよね、これ!ということであえて Rancher Desktop のコンテナランタイムを "containerd" に切り替えて調査!

ここで引き下がるのはなんか悔しいってことで色々調べてみました。調べたといってもほとんど Docker Compose の YAML の書き方とか、そもそも「ボリュームってなんやねん」とか、皆無だった基礎知識のあたりを調べてた時間がほとんどですが。

まず、最初に気づいたのは起動時に Warning が出ていること。

% nerdctl compose up -d
INFO[0000] Creating network sensu_default
WARN[0000] Ignoring: volume sensu-backend-data: [Driver] <-(コレ)
INFO[0000] Ensuring image sensu/sensu:latest
INFO[0000] Creating container sensu_sensu-backend_1

もしかして、ボリュームがうまくできてないの?とか思ったけど、これは文字通りWarningで関係ないことが後で判明します。多分ですが、

ココ

Unimplemented docker volume create flags: --driver, --opt

と書かれてるので、これが原因でしょう。

色々調べてると、どうやら再起動するたびにボリュームが新たに作られていってることに気づきます。これだったら、毎回新たに etcd のデータが作られるので、毎回初期設定が必要になるのもわかります。こんな感じ。再起動のたびに新たにボリュームが作られています。

% nerdctl volume list               
VOLUME NAME    DIRECTORY
% nerdctl compose up -d
INFO[0000] Creating network sensu_default               
WARN[0000] Ignoring: volume sensu-backend-data: [Driver] 
INFO[0000] Creating volume sensu_sensu-backend-data     
INFO[0000] Ensuring image sensu/sensu:latest            
INFO[0000] Creating container sensu_sensu-backend_1     
% nerdctl volume list  
VOLUME NAME                                                         DIRECTORY
9a3721923ba55334655208752d48846585c9072f11bce8f1e7fa9f2a4a5074a0    /var/lib/nerdctl/dbb19c5e/volumes/default/9a3721923ba55334655208752d48846585c9072f11bce8f1e7fa9f2a4a5074a0/_data
sensu_sensu-backend-data                                            /var/lib/nerdctl/dbb19c5e/volumes/default/sensu_sensu-backend-data/_data
% nerdctl compose down              
INFO[0000] Removing container sensu_sensu-backend_1     
INFO[0000] Removing network sensu_default               
karma@warbird sensu % nerdctl compose up -d
INFO[0000] Creating network sensu_default               
WARN[0000] Ignoring: volume sensu-backend-data: [Driver] 
INFO[0000] Ensuring image sensu/sensu:latest            
INFO[0000] Creating container sensu_sensu-backend_1     
% nerdctl volume list  
VOLUME NAME                                                         DIRECTORY
sensu_sensu-backend-data                                            /var/lib/nerdctl/dbb19c5e/volumes/default/sensu_sensu-backend-data/_data
0b65bbacb252e1ac8953c2b2903aa7ae94b0f213952776ce568d08bdb8b794d2    /var/lib/nerdctl/dbb19c5e/volumes/default/0b65bbacb252e1ac8953c2b2903aa7ae94b0f213952776ce568d08bdb8b794d2/_data
9a3721923ba55334655208752d48846585c9072f11bce8f1e7fa9f2a4a5074a0    /var/lib/nerdctl/dbb19c5e/volumes/default/9a3721923ba55334655208752d48846585c9072f11bce8f1e7fa9f2a4a5074a0/_data

たぶん、ここは sensu-backend-data という名前のボリュームがひとつだけ作られるのが正解っぽいけど、なぜか謎のボリュームが増殖していくという…

これは何が起こっているのかコンテナ内(コンテナのMount Namespace内)を確認しないとダメだね、ってことで、やってみました。

% nerdctl ps          
CONTAINER ID    IMAGE                           COMMAND                   CREATED          STATUS    PORTS                                                                     NAMES
0eeffa3553b5    docker.io/sensu/sensu:latest    "sensu-backend start…"    2 minutes ago    Up        0.0.0.0:3000->3000/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8081->8081/tcp    sensu_sensu-backend_1    
% nerdctl exec -ti sensu_sensu-backend_1 -- /bin/sh
/ # cat /proc/self/mountinfo | grep sensu
146 125 253:1 /var/lib/nerdctl/dbb19c5e/volumes/default/sensu_sensu-backend-data/_data /var/lib/sensu/sensu-backend/etcd rw,relatime master:4 - ext4 /dev/disk/by-label/data-volume rw
147 125 253:1 /var/lib/nerdctl/dbb19c5e/volumes/default/0b65bbacb252e1ac8953c2b2903aa7ae94b0f213952776ce568d08bdb8b794d2/_data /var/lib/sensu rw,relatime master:4 - ext4 /dev/disk/by-label/data-volume rw

これでもうわかりましたね。ちゃんと指定のボリュームは作られているものの、別のマウントもされているので、おそらくはそっち側が上にマウントされてる感じかな?

ちなみに名前付きで作ってるボリュームについては、コンテナ内の別のディレクトリにマウントしてみると空でした。こっちが指定どおり sensu-backend-data という名前で作られてないのも気になりますね。("name" 属性を指定したらちゃんとその名前で作られたけど)

匿名のボリュームは /var/lib/sensu にマウントされていることから、コンテナ内に /var/lib/sensu は存在しなくても作ってくれるものの、さらにその子ディレクトリが /var/lib/sensu/sensu-backend/etcd のように指定されていると、コンテナ上にはそのようなディレクトリがないので、まずは /var/lib/sensu を作りに行って、それに新たにボリュームが作成されてマウントされてしまう感じでしょうか。名前付きのボリュームもマウントはされているので、そちらがどのような状態になっているのかが謎…

存在しないディレクトリにはマウントできないし、でも作成された後であとでマウントされたらそっちが見えるはずだし、でもどうやら匿名で作られた /var/lib/sensu にマウントされている方が上で見えているようだし…

というわけで、docker-compose.yaml には、存在しないディレクトリは /var/lib/sensu のように一段だけ指定して無事解決でした。

version: "3"
services:
  sensu-backend:
    ports:
    - 3000:3000
    - 8080:8080
    - 8081:8081
    volumes:
    - sensu-backend-data:/var/lib/sensu
    command: "sensu-backend start --state-dir /var/lib/sensu/sensu-backend --log-level debug"
    environment:
    - SENSU_BACKEND_CLUSTER_ADMIN_USERNAME=admin
    - SENSU_BACKEND_CLUSTER_ADMIN_PASSWORD=admin
    image: sensu/sensu:latest

volumes:
  sensu-backend-data:

まとめると、

  • containerd をランタイムとして使うと、volumesで指定したパスのうち、コンテナ内に存在するディレクトリ(ここでは /var/lib)の下に存在しないディレクトリ(マウントポイント)は、直下のディレクトリ(ここでは /var/lib/sensu まで)は作成してくれる
  • それ以上深いディレクトリを指定しても作ってくれなくて、とりあえず新しいボリュームを作成し、存在するディレクトリの一層下までのディレクトリまで作成し、そこにマウントしてしまう(ここでは /var/lib/sensu
  • 存在しない深いディレクトリ(ここでは /var/lib/sensu/sensu-backend/etcd)を指定すると、それもマウントはしてくれるようだが、その上にさらに上記のマウントを重ねてしまうようなので見えないので結局は使えない(マウント情報を見ての想像です)
  • Rancher Desktop でランタイムとして "dockerd" 指定すると、最初に示した YAML で問題なく動く
  • ここの volumes の指定をどのように行うのが正解なのかはよく知らない(わからない)<ドキュメントとか読んでません

という感じです。雑なまとめ。

うーん、Mac 上から VM 層が隠蔽されていると、VM 内を覗けないのでトラブルシューティングしにくいなあ…(やる方法あるのかもしれんけどしらん)

ま、これ以上は特に興味がない個別のプロダクトのお話になるのでこの辺りで終わり!