こんにちは、ピリカ開発チームの九鬼です。
Docker Composeで複数コンテナからなるサービスを動かしている最中に、一部コンテナをアップデートしたいことがあります*1。
そこで、本記事ではその方法を紹介します(もし、さらに良い方法がありましたらぜひ伺いたいです)。
前提
例えば下図のサービス構成で、service_1~3を動的に更新することを考えます。
この構成では、apiコンテナがコンテナ外部とのインタフェースになっています。コンテナ更新用のAPIにリクエストがあると、core_controllコンテナを介してservice_*用imageコンテナを都度ビルド、新規コンテナをアタッチする想定です。
<network> api internal |--------------| |---------------| <=> [service_1] [api] <=> [core_controll] <=> [service_2] <=> [service_3]
新たにコンテナをビルドをするにはDockerデーモンにアクセスする必要があるのですが、デフォルト設定ではコンテナ内からアクセスできません。
そこで、DooD(Docker outside of Docker)を使用します*2。DooDの説明にいては割愛します。
環境構築
ファイル構成を準備する
以下の通りファイルを配置します*3。
some_service/ - docker-compose.yml - api/ - Dockerfile - ...(APIサーバ用コード)... - core_controll/ - Dockerfile - docker-compose.yml - ...(core_controll用コード)... service1/ - Dockerfile - ...(service1用コード)... service2/ - Dockerfile - ...(service1用コード)... service3/ - Dockerfile - ...(service1用コード)...
この構成において、some_service直下でdocker-compose up
を実行することによりapiコンテナ、core_controllコンテナを作成するようにしています。
DooDを使えるようにする
some_service/docker-compose.ymlについて、volumesで/var/run/docker.sock:/var/run/docker.sock
と指定します。
この設定がホスト環境のDockerのsockをcore_controllコンテナに共有する設定になります。これにより、コンテナ側からホスト側のDockerを操作できるようになります。
some_service/docker-compose.yml
version: '3.4' services: vision: container_name: "api" build: context: ./api dockerfile: Dockerfile restart: always tty: true ports: - "8000:8000" networks: - api core_controll: container_name: "core_controll" build: context: ./core_controll dockerfile: Dockerfile depends_on: - service1 - service2 - service3 volumes: - /var/run/docker.sock:/var/run/docker.sock networks: - api - internal networks: api: internal:
コンテナ内部でdocker-composeコマンドを使えるようにする
core_controllコンテナ内でdocker-composeコマンドを使えるようインストールに加えます。
(Alpine Linux環境のためapkを使っていますが、ベースOSに応じてyumやapt-get等に読み替えてください)
FROM node:14-alpine RUN apk add --update docker-compose
コンテナ内部でservice_*用コードを取得、ビルドする機構を作る
コード上で、以下2処理を記載します。
- service1~3のコードをsome_service/下にフェッチします(サンプルコードではシェルスクリプトによりコピーしているので、割愛しています)。
- サブプロセスで
docker-compose up -d
を実行します。pythonの場合、subprocess.run
、Node.jsの場合child_process.exec
などが該当します。
service1~3構成用Docker Compose設定を記載する
some_service/core_controll/docker-compose.ymlにservice1~3の構成を記載します。
version: '3.4' services: service1: container_name: "service1" build: context: ./service2 dockerfile: Dockerfile networks: - internal service2: container_name: "service2" build: context: ./service2 dockerfile: Dockerfile networks: - internal service3: container_name: "service3" build: context: ./service3 dockerfile: Dockerfile networks: - internal networks: internal: external: name: some_service_internal
このうち、networksでinternalネットワークと、some_service_internalネットワークを接続しています。このsome_service_internalネットワークは、some_service/docker-compose.ymlで設定したinternalネットワークに対応します。
以上で設定は完了です。
service1~3コンテナを動的に更新する
※ 具体的な実装例、および動作をテストしてみたい場合はサンプルコードをご覧ください。
(事前処理) some_service/下でdocker-compose up -d
を実行し、apiコンテナとcore_controllコンテナを立ち上げておきます。
コンテナ更新用のAPIを呼び出し、core_controll下でdocker-composeコマンドによるビルドを行います。ビルドが成功していれば、service1~3コンテナが自動的にアタッチされます。
これによりサービス全体を動かしつつ、service1~3を動的に更新することができます。
サンプルコード
以下URLを参照ください。
https://github.com/pirika-inc/docker_compose_hot_container_update_sample
以上になります。ご覧いただきありがとうございます!