カテゴリー
docker-composeのprofilesタグを設定して特定のサービスだけを選択的に実行する
※ 当ページには【広告/PR】を含む場合があります。
2023/02/19

最近知りましたが、ちょっと前に
Docker-composeのプロファイル機能を利用することで、単一のdocker-compose.ymlで、様々なサービスが細かく場合分けできるようになります。
この記事では、以下のポイントについて取り扱っていきます。
1. Docker-composeでのprofilesタグの使い方
2. runサブコマンドの--service-potsオプションによるポート有効化
3. runサブコマンドで立ち上げたコンテナを止めるときのSIGINTエラー対策
4. Docker-composeのcommandで使うシェル形式とexec形式の違い
Docker-compseのプロファイル機能の使い方
まずはocker-composeのプロファイルの使いどころから解説していきます。
profiles登場以前のサービス切り分け
プロファイル機能登場以前の古いDocker-composeでは、設定YAMLファイルに記述したサービスは基本的にすべて起動してしまう仕様でした。
このため、区分けしたい実行環境ごとに複数の設定ファイルを分けて、
-f
例えば以下のような雰囲気です。
$ docker-compose -f A.yml run app hoge
$ docker-compose -f B.yml run server piyo
$ docker-compose -f A.yml -f B.yml -f C.yml run fuga
問題として、どのyamlファイルがどの実行環境に対応するのか、利用者が適切に選択する必要があります。
docker-compose.development.bootstrap.yml
また複数の設定ファイルのインジェクションオプション(
-f A.yml -f B.yml -f C.yml ...
そもそも、
profilesタグを使ってサービスを細かく切り替える
では本題の
version: "3.9"
services:
hoge:
image: hoge-image
piyo:
image: piyo-image
fuga:
image: hoge-image
command: echo hoge
depends_on:
- piyo
という3つのサービス(
hoge
piyo
fuga
この場合、例えば
docker-compose run fuga
fugaにプロファイル指定するとfuga(と依存性のあるpiyoも)だけ立ち上げることが可能になります。
version: "3.9"
services:
hoge:
image: hoge-image
ports:
- "3000:3000"
tty: true
piyo:
image: piyo-image
ports:
- "3001:3001"
fuga:
image: hoge-image
command: echo hoge
ports:
- "3002:3002"
depends_on:
- piyo
profiles:
- fuga-only
#profiles: ["fuga-only"] でもOK
としておいて、
$ docker-compose --profile fuga-only run fuga
#👇もしくはプロファイルとサービスの一意に決まる場合、--profile省略可
$ docker-compose run fuga
とすることで起動するコンテナが自由に選択できるようになります。
逆に、profilesが割り当てられたサービスはプロファイル指定の手動コマンドでしか立ち上がらないことになります。
#👇これはhogeとpiyoのみ起動
$ docker-compose up -d
つまり、
プロファイル機能のもっと詳しい使い方は、以下の公式ドキュメントを参考にしてください。
docker-compose runでportsを使うときは--service-portsオプションを忘れずに
例えば、先程のdocker-compose.ymlの例でいうと、
docker-compose run
$ docker-compose run fuga http-server
Server is now starting on http://localhost:3002 ...
一見、良い感じに見えますが、
http://localhost:3002
コンテナの稼働状況をチェックしてみると、
$ docker ps
CONTAINER ID IMAGE COMMAND PORTS NAMES
755ce5d439ca node:alpine3.17 "http-server" fuga_run_a48da6a76102
#...
ポートが開いていないことが分かります。
これはdocker-composeの仕様で、
runサブコマンドからポートを有効化したい場合には、
$ docker-compose run --service-ports fuga http-server
Server is now starting on http://localhost:3002 ...
これでポートが有効になります。
$ docker ps
CONTAINER ID IMAGE COMMAND PORTS NAMES
cdd707747dd5 node:alpine3.17 "http-server" 0.0.0.0:3002->3002/tcp, :::3002->3002/tcp, fuga_run_a48da6a76102
#...
よもやま話〜docker-compose.ymlのversionの意味
先程の
docker-compose.yml
3.9
というか、以下のサイトで詳しく書かれているように、
3.9
では、なんのためにversionが存在しているかというと、
つまり、docker-compose.ymlを実装する場合、「あの機能はこのバージョンに対応してるのかな?」などと正確なスキーマを選択するのにヤキモキするべきではなく、Composeファイルを設計した時点での最新のスキーマを選択すべき、というのが正しい設計指針です。
ということで、versionは後々の利用者が見たときに、「あー、あのときのバージョンが最新のときに作られたYAMLファイルか、理解した」くらいに使われるスキーマなのにご留意ください。
なお、
version: "3"
3.*
^3.0
3 = 3.0
ついつい面倒で
version: "3"
コンテナの終了時に出てしまうSIGINTエラーに対応する
こちらはプロファイル機能の話題と直接は関係ないのですが、
docker-compose run
SIGINTエラー
例えば何らかのフロントエンド開発環境で、Dockerコンテナ内で、ビルドしてサーバー起動をしたいというケースを考えてみます。
version: '3.9'
services:
app: &app
image: node:alpine3.17
container_name: node-dev
user: "node:node"
environment:
NODE_ENV: development
volumes:
- ./:/usr/src/app
working_dir: "/usr/src/app"
ports:
- "3000:3000"
tty: true
cmd:
<<: *app
container_name: build-and-serve
command: >
bash -c '
cd myapp &&
yarn build &&
echo "Server is getting Started!" &&
yarn serve
'
stop_signal: SIGINT
profiles:
- mytool
で、これで
cmd
$ docker-compose run --service-ports cmd
#...ビルドとサーバー起動が行われる
で、これで開発作業も終わって、要らなくなったコンテナを停めようとすると(些細な)問題が起こります。
error Command failed with signal "SIGINT".
実際にやってもらえばわかりますが、深刻ではないものの、ちょっとしたエラーが発生しています。
docker-composeのcommandとendpointの使い分け
しばしばDockerユーザーを悩ませるのが、
SIGINTエラーを理解するためには、このCMDとENTRYPOINTの使い方の理解が関係してきますので、すこし話を整理しておきましょう。
docker-composeにおいては、
どちらもコマンドを実行することに違いはないのですが、使い方には注意が必要になります。
また、コンテナがプロセスを開始する際に、
「シェル形式」とはその名の通り、shやbashなどのシェルを呼び出してから、そこでコマンドを呼び出して処理プロセスを開始する方式のことを指します。
反対に「exec形式」はシェルを呼び出しを行わず、新しい子プロセスとして処理を開始する方式です。
つまり、「シェル形式」は通常のシェルによる処理を行いたい場合に利用し、「exec形式」はシェルを使わない処理で利用します。
これを踏まえた上で、Dockerの「CMD」と「ENTRYPOINT」を見ていきましょう。
まず
CMD
1. exec形式(推奨):
CMD ["実行バイナリ", "パラメータ1", "パラメータ2", "パラメータ3", ...]
2. シェル形式:
CMD <コマンド>
3. ENDPOINTのパラメータ形式:
CMD ["パラメータ1", "パラメータ2"]
(※ENTRYPOINTでコマンド指定が必要)
他方、
ENTRYPOINT
1. exec形式(推奨):
ENTRYPOINT ["実行可能なコマンド", "パラメータ1", "パラメータ2", "パラメータ3", ...]
2. シェル形式:
ENTRYPOINT コマンド パラメータ1 パラメータ2 ...
と、ここまでは良いでしょう。
もう一つ、Docker関連の記事の中のComposeファイルで割と見かけるcommandタグへのテクニックについても触れておきましょう。
docker-compose.ymlに複数行のシェルスクリプトをcommandタグに埋め込むイディオムとして良く紹介されるテクニックがCMDのシェル形式と
bash -c
#...
command: >
bash -c '
cd myapp &&
yarn build &&
echo "Server is getting Started!" &&
yarn serve
'
#...
で、CMDのシェル形式を使っているからシェル(bash)で起動出来ているだろうと、一見正しく見えます。
bashのcオプションは
bash -c 'hoge.sh'
つまり、一度別プロセスで走ったhoge.shの中身はシェルが評価してくれないので、
シェバンを正しく指定してシェルのプロセスを生存させる
ではSIGINTエラーの問題を改めて考えていきましょう。
通常起動したサービスを停止させる場合、キーボードから
Ctrlキー+Cキー
その際に、正常にプロセスが終わらなった場合、
error Command failed with signal "SIGINT".
というエラーメッセージが出ているわけです。
ここでの
「SIGINT」
SIGINTエラーとは、Ctrl+Cキー割り込みでプロセスが上手く停まらなかった、というになります。
エラーが出てしまう場合、サーバー起動時に
ps a
$ ps a
PID TTY STAT TIME COMMAND
13724 pts/1 Sl+ 0:00 docker-compose run --rm --service-ports cmd
13851 pts/0 Ssl+ 0:00 node /usr/share/node_modules/yarn/bin/yarn.js serve
14052 pts/0 Sl+ 0:01 /usr/bin/node /usr/src/app/******/node_modules/.bin/vite pr
14063 pts/0 Sl+ 0:00 /usr/src/app/******/node_modules/@esbuild/linux-x64/bin/esb
すると
bash
当然、Cntl + Cで終了シグナルを受け取るはずのシェルが既に終了しているならば、
SIGINT
なんでシェル形式でコンテナをプロセスを走らせているにも関わらず、シェルが途中でプロセスの面倒を放棄してそのままいなくなってしまったかというと、
もうおわかりかも知れませんが、先程説明していた
bash -c
#...
command: >
bash -c '
cd myapp &&
yarn build &&
echo "Server is getting Started!" &&
yarn serve
'
#...
が分かりにくい原因になっていたのです。
ここはちゃんとシェバン付きのスクリプトファイルとして、全てのプロセスをシェルが面倒を見てくれるように書き直しましょう。
#!/bin/bash
cd myapp
yarn build
echo "Server is getting Started!"
yarn serve
とファイルを作っておき、ファイルにパーミッションを与えておきます。
$ chmod +x run.sh
で、docker-compose.ymlを修正します。
#...
command: bash -c './run.sh'
#👇シェル形式・exec形式どっちでもOK
command: ['./run.sh']
ではサービスを実行してみます。
$ ps a
PID TTY STAT TIME COMMAND
12710 pts/1 Sl+ 0:00 docker-compose run --rm --service-ports cmd
(☆)12835 pts/0 Ss+ 0:00 /bin/bash ./run.sh
13011 pts/0 Sl+ 0:00 node /usr/share/node_modules/yarn/bin/yarn.js serve
13032 pts/0 Sl+ 0:01 /usr/bin/node /usr/src/app/******/node_modules/.bin/vite pr
13048 pts/0 Sl+ 0:00 /usr/src/app/******/node_modules/@esbuild/linux-x64/bin/esb
この場合、☆で示した
bash run.sh
commandタグに
bash -c
まとめ
以上、まとめますと、この記事では以下のポイントについて解説してきました。
1. Docker-composeでのprofilesタグの使い方
2. runサブコマンドの--service-potsオプションによるポート有効化
3. runサブコマンドで立ち上げたコンテナを止めるときのSIGINTエラー対策
4. Docker-composeのcommandで使うシェル形式とexec形式の違い
Dockerユーザーならdocker-composeも必須ツールとなっているので、今後もより使いやすい進化を期待したいものです。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー