在容器中執行多個程序

容器的主要執行程序是 `Dockerfile` 末尾的 `ENTRYPOINT` 和/或 `CMD`。最佳實踐是透過為每個容器使用一個服務來分離關注點。該服務可能會分叉成多個程序(例如,Apache Web 伺服器會啟動多個工作程序)。擁有多個程序是可以的,但為了從 Docker 中獲得最大收益,請避免讓一個容器負責整個應用程式的多個方面。您可以使用使用者定義的網路和共享捲來連線多個容器。

容器的主程序負責管理其啟動的所有程序。在某些情況下,主程序設計不佳,在容器退出時無法優雅地處理“回收”(停止)子程序。如果您的程序屬於此類別,您可以在執行容器時使用 `--init` 選項。`--init` 標誌會在容器中插入一個微小的初始化程序作為主程序,並在容器退出時處理所有程序的回收。以這種方式處理此類程序優於使用像 `sysvinit` 或 `systemd` 這樣的成熟初始化程序來處理容器內的程序生命週期。

如果您需要在容器內執行多個服務,可以通過幾種不同的方式實現。

使用包裝指令碼

將所有命令放入一個包裝指令碼中,並附上測試和除錯資訊。將此包裝指令碼作為您的 `CMD` 執行。以下是一個簡單的示例。首先,是包裝指令碼:

#!/bin/bash

# Start the first process
./my_first_process &

# Start the second process
./my_second_process &

# Wait for any process to exit
wait -n

# Exit with status of process that exited first
exit $?

接下來,是 Dockerfile:

# syntax=docker/dockerfile:1
FROM ubuntu:latest
COPY my_first_process my_first_process
COPY my_second_process my_second_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.sh

使用 Bash 作業控制

如果您有一個需要首先啟動並保持執行的主程序,但您臨時需要執行一些其他程序(也許是為了與主程序互動),那麼您可以使用 bash 的作業控制。首先,是包裝指令碼:

#!/bin/bash

# turn on bash's job control
set -m

# Start the primary process and put it in the background
./my_main_process &

# Start the helper process
./my_helper_process

# the my_helper_process might need to know how to wait on the
# primary process to start before it does its work and returns


# now we bring the primary process back into the foreground
# and leave it there
fg %1
# syntax=docker/dockerfile:1
FROM ubuntu:latest
COPY my_main_process my_main_process
COPY my_helper_process my_helper_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.sh

使用程序管理器

使用像 `supervisord` 這樣的程序管理器。這比其他選項更復雜,因為它要求您將 `supervisord` 及其配置打包到您的映象中(或將您的映象基於一個包含 `supervisord` 的映象),以及它所管理的不同應用程式。然後您啟動 `supervisord`,它會為您管理您的程序。

以下 Dockerfile 示例展示了這種方法。該示例假設這些檔案存在於構建上下文的根目錄中:

  • supervisord.conf
  • my_first_process
  • my_second_process
# syntax=docker/dockerfile:1
FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY my_first_process my_first_process
COPY my_second_process my_second_process
CMD ["/usr/bin/supervisord"]

如果您想確保兩個程序都將其 `stdout` 和 `stderr` 輸出到容器日誌,您可以在 `supervisord.conf` 檔案中新增以下內容:

[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0

[program:app]
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true