CLOVER🍀

That was when it all began.

Docker Composeで、構成ファイルを複数使って定義内容を上書きする

これは、なにをしたくて書いたもの?

Docker Composeで、構成ファイルを複数使って上書きができることはなんとなく知っていたのですが、ちゃんと情報を追ったことが
なかったので、ちょっと見てみることにしました。

Docker Composeでの構成の上書き

ドキュメントとしては、以下が該当します。

Share Compose configurations between files and projects | Docker Documentation

複数のComposeのファイルを使うことで、アプリケーションを様々な環境やワークロードに合わせてカスタマイズできるという仕組みです。

Using multiple Compose files lets you to customize a Compose application for different environments or different workflows.

Share Compose configurations between files and projects / Multiple Compose files

デフォルトでは、docker-compose.ymlとオプションのdocker-compose.override.ymlが読み込まれると書かれています。

By default, Compose reads two files, a docker-compose.yml and an optional docker-compose.override.yml file.

docker-compose.ymlの方が、基本構成ですね。docker-compose.override.ymlで、基本構成を上書きすることになります。

サービスが両方のファイルで定義されている場合は、マージが行われるようです。

If a service is defined in both files, Compose merges the configurations using the rules described in Adding and overriding configuration.

Share Compose configurations between files and projects / Multiple Compose files / Understanding multiple Compose files

マージのルールは、こちらに書かれています。

Share Compose configurations between files and projects / Adding and overriding configuration

複数の値を取る設定については、両方のファイルの内容を足し合わせたものになります。
environtmentとlabelは名前を考慮したマージが行われるようです。

単一の値の場合は、上書き用のファイルの内容が採用されます。

あと、今回の内容とは関係ないですが、ある構成ファイルの内容を拡張(というか共通化)もできるようです。

Share Compose configurations between files and projects / Extending services

今回は、上書きについて試してみます。

環境

今回の環境は、こちら。

Docker Engine。

$ docker version
Client: Docker Engine - Community
 Version:           20.10.23
 API version:       1.41
 Go version:        go1.18.10
 Git commit:        7155243
 Built:             Thu Jan 19 17:45:08 2023
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.23
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.10
  Git commit:       6051f14
  Built:            Thu Jan 19 17:42:57 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.15
  GitCommit:        5b842e528e99d4d4c1686467debf2bd4b88ecd86
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker Compose。

$ docker compose version
Docker Compose version v2.15.1

基本の構成ファイル

今回は、こちらのファイルを基本構成にします。

compose.yaml

services:
  proxy:
    image: nginx:1.23.3
    volumes:
      - ./default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - "80:80"
    deploy:
      resources:
        limits:
          cpus: '1'
        reservations:
          cpus: '1'

  webapp:
    image: quay.io/wildfly/wildfly:27.0.0.Final-jdk17
    deploy:
      resources:
        limits:
          cpus: '1'
        reservations:
          cpus: '1'

ファイル名については、compose.yamlとするのが推奨のようなので、こちらに合わせます。

Compose specification / Compose file

WildFlyの前に、nginxをリバースプロキシとして置く構成にします。

default.conf

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        proxy_pass http://webapp:8080/;
    }
}

こちらで、動作確認しておきます。

$ docker compose up

ローカルにバインドされているポートの確認。

$ docker compose ps
NAME                              IMAGE                                        COMMAND                  SERVICE             CREATED             STATUS              PORTS
configuration-override-proxy-1    nginx:1.23.3                                 "/docker-entrypoint.…"   proxy               5 seconds ago       Up 3 seconds        0.0.0.0:80->80/tcp, :::80->80/tcp
configuration-override-webapp-1   quay.io/wildfly/wildfly:27.0.0.Final-jdk17   "/opt/jboss/wildfly/…"   webapp              5 seconds ago       Up 3 seconds        8080/tcp

アクセス確認。

$ curl localhost
<!DOCTYPE html>

<html>
<head>
    <!-- proper charset -->
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />

    <title>Welcome to WildFly</title>
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
    <link rel="StyleSheet" href="wildfly.css" type="text/css">
</head>

<body>
<div class="wrapper">
    <div class="content">
        <div class="logo">
                <img src="wildfly_logo.png" alt="WildFly" border="0" />
        </div>
        <h1>Welcome to WildFly</h1>

        <h3>Your WildFly instance is running.</h3>

        <p><a href="https://docs.wildfly.org">Documentation</a> | <a href="https://github.com/wildfly/quickstart">Quickstarts</a> | <a href="/console">Administration
            Console</a> </p>

        <p><a href="https://wildfly.org">WildFly Project</a> |
            <a href="https://community.jboss.org/en/wildfly">User Forum</a> |
            <a href="https://issues.jboss.org/browse/WFLY">Report an issue</a></p>
        <p class="logos"><a href="https://www.jboss.org"><img src="jbosscommunity_logo_hori_white.png" alt="JBoss and JBoss Community" width=
                "195" height="37" border="0"></a></p>

        <p class="note">To replace this page simply deploy your own war with / as its context path.<br />
            To disable it, remove the "welcome-content" handler for location / in the undertow subsystem.</p>
    </div>
</div>
</body>
</html>

cpusに設定した値も確認しておきます。

$ docker container inspect configuration-override-proxy-1 | grep NanoCpus
            "NanoCpus": 1000000000,


$ docker container inspect configuration-override-webapp-1 | grep NanoCpus
            "NanoCpus": 1000000000,

このコンテナ群は、1度停止・削除しておきます。

上書きするファイルを用意する

上書きするファイルは、こちらを用意。
ドキュメントには、ファイル名がcompose.override.yamlでも読まれるとは書かれていませんが、いけるのでは?と思って試してみます。

compose.override.yaml

services:
  proxy:
    ports:
      - "10080:80"

  webapp:
    deploy:
      resources:
        limits:
          cpus: '2'
        reservations:
          cpus: '2'

nginxの方はローカルにバインドするポートを追加し、WildFlyの方はCPU割当を増やしています。

確認。docker compose upの時に、特に引数は指定しません。

$ docker compose up

nginxのコンテナに対して、ローカルにバインドされているポートが増えています。

$ docker compose ps
NAME                              IMAGE                                        COMMAND                  SERVICE             CREATED             STATUS              PORTS
configuration-override-proxy-1    nginx:1.23.3                                 "/docker-entrypoint.…"   proxy               46 seconds ago      Up 44 seconds       0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:10080->80/tcp, :::10080->80/tcp
configuration-override-webapp-1   quay.io/wildfly/wildfly:27.0.0.Final-jdk17   "/opt/jboss/wildfly/…"   webapp              46 seconds ago      Up 44 seconds       8080/tcp

CPU割当は、WildFlyのコンテナのみ増えていますね。

$ docker container inspect configuration-override-proxy-1 | grep NanoCpus
            "NanoCpus": 1000000000,


$ docker container inspect configuration-override-webapp-1 | grep NanoCpus
            "NanoCpus": 2000000000,

というわけで、compose.override.yamlファイルで構成を上書きできていることが確認できました。
この時、compose.override.yamlファイルについてはなにも指定していないことがポイントです。

ちなみに、-fオプションでファイルを明示的に指定すると上書き用のファイルは読まれなくなります。

$ docker compose -f compose.yaml up

確認。

$ docker compose ps
NAME                              IMAGE                                        COMMAND                  SERVICE             CREATED              STATUS              PORTS
configuration-override-proxy-1    nginx:1.23.3                                 "/docker-entrypoint.…"   proxy               About a minute ago   Up About a minute   0.0.0.0:80->80/tcp, :::80->80/tcp
configuration-override-webapp-1   quay.io/wildfly/wildfly:27.0.0.Final-jdk17   "/opt/jboss/wildfly/…"   webapp              About a minute ago   Up About a minute   8080/tcp


$ docker container inspect configuration-override-proxy-1 | grep NanoCpus
            "NanoCpus": 1000000000,


$ docker container inspect configuration-override-webapp-1 | grep NanoCpus
            "NanoCpus": 1000000000,

この場合は、それぞれのファイルを-fオプションで明示的に指定する必要があります。

$ docker compose -f compose.yaml -f compose.override.yaml up

これを利用することで、目的に応じて上書きするファイルを指定することが可能になります。

$ docker compose -f compose.yaml -f compose.prod.yaml up

また、オプションで指定するファイルの順番に上書きされていくようで、例えば以下のようにファイルの順番を逆にすると

$ docker compose -f compose.override.yaml -f compose.yaml up

マージされる値については結果は変わりませんが、単一の値については意味が変わってしまいます。

$ docker compose ps
NAME                              IMAGE                                        COMMAND                  SERVICE             CREATED             STATUS              PORTS
configuration-override-proxy-1    nginx:1.23.3                                 "/docker-entrypoint.…"   proxy               3 seconds ago       Up 1 second         0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:10080->80/tcp, :::10080->80/tcp
configuration-override-webapp-1   quay.io/wildfly/wildfly:27.0.0.Final-jdk17   "/opt/jboss/wildfly/…"   webapp              3 seconds ago       Up 1 second         8080/tcp


$ docker container inspect configuration-override-proxy-1 | grep NanoCpus
            "NanoCpus": 1000000000,


$ docker container inspect configuration-override-webapp-1 | grep NanoCpus
            "NanoCpus": 1000000000,

使い方はわかりましたね。

デフォルトの上書き用ファイル名のパターンは?

ちょっと気になったので調べてみました。

GitHubリポジトリを見てみたのですが、今ひとつわからなかったので

GitHub - docker/compose: Define and run multi-container applications with Docker

straceで見てみることにしました。

$ strace -f docker compose up 2>&1

以下のようなファイルを対象とするようです。

[pid 39678] newfstatat(AT_FDCWD, "/path/to/compose.yaml", {st_mode=S_IFREG|0664, st_size=121, ...}, 0) = 0
[pid 39678] newfstatat(AT_FDCWD, "/path/to/compose.yml", 0xc00039b898, 0) = -1 ENOENT (そのようなファイルやディレクトリ
はありません)
[pid 39678] newfstatat(AT_FDCWD, "/path/to/docker-compose.yml", 0xc00039b968, 0) = -1 ENOENT (そのようなファイルやディレクトリはありません)
[pid 39678] newfstatat(AT_FDCWD, "/path/to/docker-compose.yaml", 0xc00039ba38, 0) = -1 ENOENT (そのようなファイルやディ
レクトリはありません)
[pid 39678] newfstatat(AT_FDCWD, "/path/to/compose.override.yml", 0xc00039bb08, 0) = -1 ENOENT (そのようなファイルやディレクトリはありません)
[pid 39678] newfstatat(AT_FDCWD, "/path/to/compose.override.yaml", {st_mode=S_IFREG|0664, st_size=46, ...}, 0) = 0
[pid 39678] newfstatat(AT_FDCWD, "/path/to/docker-compose.override.yml", 0xc00039bca8, 0) = -1 ENOENT (そのようなファイ
ルやディレクトリはありません)
[pid 39678] newfstatat(AT_FDCWD, "/path/to/docker-compose.override.yaml", 0xc00039bd78, 0) = -1 ENOENT (そのようなファイ
ルやディレクトリはありません)

compose.override.yml、compose.override.yaml、docker-compose.override.yml、docker-compose.override.yamlの4つですね。

まとめ

Docker Composeで、構成ファイルを複数使って定義内容を上書きしていみました。

なんとなくそんな機能があることは知っていたのですが、今回ちゃんとドキュメントと動きを確認して、どのような機能かハッキリしたので
良かったかなとは思います。

とはいえ、あんまり濫用する機能でもないかなとも思うので、利用はほどほどにといったところかなという印象を持ちました。