docker-compose.yamlのentrypoint/commandフィールドで環境変数を使いたい

Bash,Docker,YAML

はじめに

docker-compose.yaml 内で環境変数を記述する際には、その展開時期に注意しましょう。
entrypointcommand フィールドに環境変数を記述する場合には、environment フィールドでの環境変数定義を参照できないため、別途工夫が必要です。
また、docker-compose up 実行時に、環境変数が「置換」される挙動については、使い方次第では不便な場合もあるため、注意が必要です。

問題点について、具体的なコードをあげて説明した後、対処法を紹介します。

検証環境

$ uname -moi
arm64 unknown Darwin

$ docker-compose --version
Docker Compose version v2.15.1

docker-compose.yaml に環境変数 $VAR を記述した時の挙動

docker-compose.yaml には $VAR あるいは ${VAR} の形式で環境変数を記述できます。

環境変数の展開の挙動を確認しておきます。

以下のような docker-compose.yaml ファイルを用意します。

version: "3"
services:
  app:
    image: bash
    environment:
      "Z": genzouw
    entrypoint: echo "$X",'$Y',"$Z"

YAML ファイルを配置しているディレクトリで、以下のコマンドを実行することで Docker Compose 内に記述されているイメージを使ってコンテナが立ち上がります。

$ docker-compose up

ここで、環境変数 XY を渡して起動してみます。
Zdocker-compose.yaml 内の environment フィールドで定義したものを参照してみます。

$ X=hello Y=world docker-compose up
WARN[0000] The "Z" variable is not set. Defaulting to a blank string.
[+] Running 1/0
 ⠿ Container work-app-1  Created                                                                                                                                                               0.0s
Attaching to work-app-1
work-app-1  | hello,world,

コマンド実行直後のメッセージに Z 変数についてのメッセージが表示されています。
echo "$X",'$Y',"$Z" の処理を実行した結果、Z だけが出力されていないことからもわかりますが、環境変数は docker-compose up 実行タイミングで YAML ファイル内の各フィールド値の $AVR の記述が「置換」されているようです。
environment フィールドの値は、 YAML ファイルの「置換」のタイミングでは利用できません。

今回の YAML は起動直後に以下の内容と同じに扱われているということです。

version: "3"
services:
  app:
    image: bash
    environment:
      "Z": genzouw
    entrypoint: echo "hello",'world',""

environment フィールドに設定した環境変数を entrypoint/command で参照する方法

それでは environment フィールドで設定した環境変数を entrypointcommand で参照するにはどうしたら良いでしょう?

環境変数を YAML ファイル解釈のタイミングではなく、 entrypoint 実行タイミングで展開させればよいことになります。

以下のように記述することでこれを実現できます。

version: "3"
services:
  app:
    image: bash
    environment:
      "Z": genzouw
    entrypoint: sh -c 'echo $X,$Y,$$Z'
$ X=hello Y=world docker-compose up
[+] Running 1/0
 ⠿ Container work-app-1                                                                                                                               Recreated                                0.0s
 ⠋ app The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested                                          0.0s
Attaching to work-app-1
work-app-1  | hello,world,genzouw

無事、実行結果として hello.world,genzouw が出力されました。

$$Z についてですが、 docker-compose.yaml 内で $$VAR のように $ を二重に記述すると、環境変数の展開は行われず、 $VAR という文字列リテラルとして扱われます。
シェルの \$VAR のように解釈されたと思っていただければよいでしょう。

その後、 entrypoint に記述されたコマンドが実行されます。

$X$Y は YAML ファイル読み込み直後にすでに環境変数が展開されているので、 以下のコマンドが実行されたのと同義です。

sh -c 'echo hello,world,$Z'

結果として sh 実行時に $Z が無事展開されます。

entrypoint / command でプログラム、スクリプトを呼び出し、中で環境変数を参照する場合の注意

以下のような内容の main.sh を用意し、 entrypointcommand の中で main.sh を呼び出す場合には、今度は docker-compose コマンド実行時に指定した環境変数を利用できません。

#!/usr/bin/env bash

echo "$X"
echo "$Y"
echo "$Z"

docker-compose.yaml は以下のとおりです。

version: "3"
services:
  app:
    image: bash
    environment:
      "Z": genzouw
    volumes:
      - ./main.sh:/main.sh
    entrypoint: /main.sh

実行結果は以下のようになり、 $Z しか出力されません。

$ X=hello Y=world docker-compose up
work-app-1  |
work-app-1  |
work-app-1  | genzouw

この場合の対処は 2 つです。

1 つは docker-compose.yamlenvironment フィールドに、コマンド実行時に設定された環境変数を引き継ぐ方法。

version: "3"
services:
  app:
    image: bash
    environment:
      "X": $X
      "Y": $Y
      "Z": genzouw
    volumes:
      - ./main.sh:/main.sh
    entrypoint: /main.sh

実行します。

$ X=hello Y=world docker-compose up
[+] Running 1/0
 ⠿ Container work-app-1                                                                                                                               Recreated                                0.1s
 ⠋ app The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested                                          0.0s
Attaching to work-app-1
work-app-1  | hello
work-app-1  | world
work-app-1  | genzouw

もう1つは docker-compose up ではなく docker-compose run を使い、 -e オプションで環境変数を指定する方法。

version: "3"
services:
  app:
    image: bash
    environment:
      "Z": genzouw
    volumes:
      - ./main.sh:/main.sh
    entrypoint: /main.sh

実行します。

$ docker-compose run -e X=hello -e Y=world app
hello
world
genzouw

ひとこと

docker-compose up 実行時に $VAR が置換される挙動は、個人的には少し気持ちが悪いです。

Bash,Docker,YAML