Dockerで新たにWebアプリケーションをデプロイするときに、コンテナ間でファイルを共有するにはどうすればよいでしょうか。 正解はvolumeを使うのが一般的な解答ではあるのですが、必ずしもそれが正しいとは限らないこともあります。 gRPCを使うときにprotobuf形式のファイルを相互のコンテナ間で共有したいと考えました。 Gitリポジトリからクローンして、そのフォルダのなかでdocker-compose buildを実行して、docker-compose upをするだけなら以下のように書けば問題ありません:

version: '3.8'
services:
  foo:
    volumes:
      - ./hello.proto:/foo/hello.proto
  bar:
    volumes:
      - ./hello.proto:/bar/hello.proto

しかしGitリポジトリからクローンするのであればDockerでなければならない理由はない気がします。 というのも、この構成だと水平スケーリングができません。 今の所私の環境はオンプレミスなので水平スケーリングをできる状況にはないことも事実ではありますが、やはりDockerを使う以上はDockerレジストリからコンテナをpullしてきて、そのコンテナを実行したいと思っています。 そうすると、上記のdocker-compose.ymlは使えません。なぜならhello.protoというファイルは存在しえないからです。 もちろんAnsibleを利用して予めprotoファイルをサーバーに配置して、絶対パスで指定する方法も取れなくはありません。 ですが、この方法もやはり毎回Ansibleが必須になってしまうので残念ながら没にせざるをえません。 そんな状況で最初のvolumeに頼る方法が必ずしもうまくいくとは限らないわけ。 あらかじめdockerでデプロイする前にvolumeを作成しておいて、externalを指定して各コンテナで共有するというのもよいアイディアではあるのですが、protobufは永続性のあるデータではなくてむしろ毎回のビルドごとに確実に更新してほしいファイルです。 そこで、最終的に思いついたアイディアとしてはDockerfileのステージを利用する方法を思いつきました。

FROM registry.example.com/user/foo AS foo
FROM node:latest
# ...
COPY --from=app /app/protos/hello.proto /bar

このように書くとhello.protofooにあるprotobufをbarでも共有できることに気づきました。 しかももとのイメージにも影響はなくて単純にファイルをCOPYするだけだからレイヤーこそ増えてしまうものの無駄なものではありません。 言葉で説明するのはなかなか難しいですが、この方法はなかなかよい解決策だと思いました。 もちろんコンテナのビルドはなるべく他に影響を与えないことが好ましいとは思いますが、同じプロジェクト間で使いまわすなら問題はないかと思っています。 実際にデプロイしてみたところ、現時点では意図したとおりに動いてくれています。 Dockerにも限りませんが、何かしらこういった抜け道や、その方法を実現するのにただしそうな方法が見つかるのは気持ちが良いものです。