メインコンテンツへスキップ

docker-compose.yaml の書き方が間違っていた話

·406 文字·2 分·
技術記事 自宅サーバー Docker
著者
on-keyday
隠者

TL;DR
#

docker buildx bake を使っているときに docker-compose.yaml でおんなじサービスの複製を作るときは build で同じ Dockerfile を指定してはいけない

内容
#

いつものように自宅サーバーを再起動しようとしたら

INFO: failed to resume the status from path .buildx-cache/ingest/39e3ffd26acfcd29cffe52b4e0f3ed668c06daf8a81552cb090ef701d17fa756: failed reading status of resume write: stat .buildx-cache/ingest/39e3ffd26acfcd29cffe52b4e0f3ed668c06daf8a81552cb090ef701d17fa756/data: no such file or directory: not found. will recreate them
(中略)
ERROR: (*service).Write failed: rpc error: code = Canceled desc = context canceled
ERROR: target server1: failed to solve: error writing layer blob: rpc error: code = Unknown desc = rename tmp file: rename .buildx-cache/ingest/39e3ffd26acfcd29cffe52b4e0f3ed668c06daf8a81552cb090ef701d17fa756/startedat.tmp .buildx-cache/ingest/39e3ffd26acfcd29cffe52b4e0f3ed668c06daf8a81552cb090ef701d17fa756/startedat: no such file or directory: unknown

といったようなエラーで落ちてしまった。

一体何なんだと思って調べたら

#151 [server3] exporting cache to client directory
#151 writing layer sha256:1f3e46996e2966e4faa5846e56e76e3748b7315e2ded61476c24403d592134f0 done
#151 writing layer sha256:51463de4e5797bcc36a1a19b8f6309c079b129cc677aeb5655c1669e75c39318
#151 writing layer sha256:51463de4e5797bcc36a1a19b8f6309c079b129cc677aeb5655c1669e75c39318 1.1s done
#151 writing layer sha256:95101ac40e2ff0ac79fc615bcf401be1514c8f9eb0157bf7a9bcdd3bdb70f6a8
#151 preparing build cache for export 1.2s done
#151 writing layer sha256:95101ac40e2ff0ac79fc615bcf401be1514c8f9eb0157bf7a9bcdd3bdb70f6a8 done
#151 ERROR: error writing layer blob: rpc error: code = Unknown desc = rename tmp file: rename .buildx-cache/ingest/fbcff690bc06459030780f486d15a593ecd86fb27dac52f951f6268f4ad9b832/startedat.tmp .buildx-cache/ingest/fbcff690bc06459030780f486d15a593ecd86fb27dac52f951f6268f4ad9b832/startedat: no such file or directory: unknown

#153 [server1] exporting cache to client directory
#153 preparing build cache for export 1.2s done
#153 writing layer sha256:1f3e46996e2966e4faa5846e56e76e3748b7315e2ded61476c24403d592134f0 done
#153 writing layer sha256:51463de4e5797bcc36a1a19b8f6309c079b129cc677aeb5655c1669e75c39318 1.1s done
#153 writing layer sha256:95101ac40e2ff0ac79fc615bcf401be1514c8f9eb0157bf7a9bcdd3bdb70f6a8 done
#153 ERROR: error writing layer blob: rpc error: code = Unknown desc = rename tmp file: rename .buildx-cache/ingest/fbcff690bc06459030780f486d15a593ecd86fb27dac52f951f6268f4ad9b832/startedat.tmp .buildx-cache/ingest/fbcff690bc06459030780f486d15a593ecd86fb27dac52f951f6268f4ad9b832/startedat: no such file or directory: unknown

#154 [server2] exporting cache to client directory
#154 preparing build cache for export 1.3s done
#154 writing layer sha256:1f3e46996e2966e4faa5846e56e76e3748b7315e2ded61476c24403d592134f0 done
#154 writing layer sha256:51463de4e5797bcc36a1a19b8f6309c079b129cc677aeb5655c1669e75c39318 1.1s done
#154 writing layer sha256:95101ac40e2ff0ac79fc615bcf401be1514c8f9eb0157bf7a9bcdd3bdb70f6a8 0.0s done
#154 writing layer sha256:b1f77e77296a02790b70f8ed8cfd6f310025ee050000230d1ddd51a0c9d5b2c2 0.0s done
#154 writing layer sha256:f08ee5bac4d0300b89d29658a05711bdc4ddebd37821ae36f6b2662aee51998d done
#154 writing layer sha256:f57fe6a295e506ba6fb8aea87751853a2e0b1593d74cac42ca7516871179837e 0.0s done
#154 writing config sha256:8237ad0dcb6ae6a79225202401f7b89cf9e7a1eeeb94248481c656eec97c7b98 done
#154 writing cache manifest sha256:5df95bcd761912ba0665b2b56adc7aa1e54b09d21409c486929ae790852ca776 done
#154 DONE 1.3s

どうも複数のコンテナのビルドで同じハッシュ値のイメージレイヤーを書き込もうとしてエラーになっているようだ。 最後のだけは成功していることからおそらく最後のやつが始めた rename 処理は正常に走ったが並列で動いていた他のやつではエラーになってしまっているようだ。

ということで、docker-compose.yaml を見直してみると、

services:
  server1:
    container_name: ${HOMESERVER_BUILD_PREFIX}server1
    hostname: server1
    build:
      context: .
      tags:
        - ${HOMESERVER_BUILD_PREFIX}server1:latest
      target: final
      dockerfile: ./docker/server.Dockerfile
    # 略

  server2:
    container_name: ${HOMESERVER_BUILD_PREFIX}server2
    hostname: server2
    build:
      context: .
      tags:
        - ${HOMESERVER_BUILD_PREFIX}server2:latest
      target: final
      dockerfile: ./docker/server.Dockerfile
    # 略

  server3:
    container_name: ${HOMESERVER_BUILD_PREFIX}server3
    hostname: server3
    build:
      context: .
      tags:
        - ${HOMESERVER_BUILD_PREFIX}server3:latest
      target: final
      dockerfile: ./docker/server.Dockerfile
    # 略

…どう見ても

    build:
      context: .
      tags:
        - ${HOMESERVER_BUILD_PREFIX}serverN:latest
      target: final
      dockerfile: ./docker/server.Dockerfile

という記述が重複している。これで並列でビルドが走りったとして結果としてできるものは全くおなじになり当然 Hash 値も同じになり その 同じファイルに並列でアクセスしようとすることで競合が起こり、結果として先程のエラーになっていたと推測される。

おそらく設計的にハッシュ値で一意になるはずだということでロックが取られていないのが原因であろうがまあ単純に効率が悪いのでこんなことをするやつがいるとは思っていなかったのだろう。

解決策を o3-mini に絞らせてみたところいかのように profiles 属性を使う形がよいと提案された。

server-base:
    image: ${HOMESERVER_BUILD_PREFIX}server-base:latest
    build:
      context: .
      dockerfile: ./docker/server.Dockerfile
      target: final
      tags:
        - ${HOMESERVER_BUILD_PREFIX}server-base:latest
    profiles:
      - build-only

  server1:
    container_name: ${HOMESERVER_BUILD_PREFIX}server1
    hostname: server1
    image: ${HOMESERVER_BUILD_PREFIX}server-base:latest
    # 略

  server2:
    container_name: ${HOMESERVER_BUILD_PREFIX}server2
    hostname: server2
    image: ${HOMESERVER_BUILD_PREFIX}server-base:latest
    # 略

  server3:
    container_name: ${HOMESERVER_BUILD_PREFIX}server3
    hostname: server3
    image: ${HOMESERVER_BUILD_PREFIX}server-base:latest
    # 略

なお、profiles というのは docker compose のサービスを分割するための機能でありこれを利用することで server-base が起動しないでビルドでのみ使われるようにしている。 https://docs.docker.jp/compose/profiles.html

これにより無事、サーバーは起動できたのであった。

感想
#

今まで動いていたのは偶然だったということです。並列ビルドの思わぬ落とし穴にハマってしまった…