Keywords: Docker | CMD directive | multi-service management | supervisor | container orchestration
Abstract: This paper provides an in-depth analysis of the runtime characteristics of Docker CMD directive and its override mechanism in image inheritance. By examining the limitations of the single-process model, it systematically introduces complete solutions for multi-service management using supervisor. The article details the differences between JSON and string formats of CMD, demonstrates supervisor configuration methods with practical Dockerfile examples, and covers key technical aspects including signal handling and process monitoring, offering practical guidance for building production-ready multi-service containers.
Runtime Characteristics of Docker CMD Directive
The Docker CMD directive is essentially runtime configuration information, similar to the EXPOSE directive but different from build-time instructions like RUN and ADD. This design means that CMD can be overridden in subsequent Dockerfile inheritance or container run commands. In Docker's single-process model, only one valid CMD instruction can exist at any time, which explains why in image inheritance, the child image's CMD completely overrides the parent image's CMD settings.
Challenges and Solutions for Multi-Service Management
Since Docker containers follow the design philosophy of a single process principle, directly running multiple services through multiple CMD directives is not feasible. When multiple service processes need to be managed within a single container, using the process management tool supervisor is recommended. This approach not only solves the problem of multi-service coexistence but also provides comprehensive process monitoring and signal handling mechanisms.
Detailed Supervisor Configuration
Supervisor configuration requires creating a main configuration file and sub-configuration files for each service. The main configuration file is typically located at /etc/supervisor/supervisord.conf, with core configuration as follows:
[supervisord]
nodaemon=true
[include]
files = /etc/supervisor/conf.d/*.conf
Where nodaemon=true ensures supervisor runs in the foreground, aligning with Docker container best practices. The include directive is used to load all service configuration files in the subdirectory.
Service Configuration File Examples
For SSH service, create /etc/supervisor/conf.d/sshd.conf:
[program:sshd]
command=/usr/sbin/sshd -D
autostart=true
autorestart=true
stderr_logfile=/var/log/sshd.err.log
stdout_logfile=/var/log/sshd.out.log
For RabbitMQ service, create /etc/supervisor/conf.d/rabbitmq.conf:
[program:rabbitmq]
command=/opt/mq/sbin/rabbitmq-server
autostart=true
autorestart=true
stderr_logfile=/var/log/rabbitmq.err.log
stdout_logfile=/var/log/rabbitmq.out.log
Dockerfile Integration Implementation
Complete Dockerfile implementation requires installing supervisor and configuring relevant files:
FROM centos+ssh
RUN yum install -y supervisor
RUN mkdir -p /etc/supervisor/conf.d
COPY supervisord.conf /etc/supervisor/
COPY conf.d/ /etc/supervisor/conf.d/
EXPOSE 22
EXPOSE 4149
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]
Signal Handling and Error Management
An important advantage of using supervisor is the improvement in signal handling mechanism. When directly using shell to run multiple commands, issues may arise where signals cannot be properly transmitted, causing container stops to require timeout waits. Supervisor, as the PID 1 process, can correctly handle signals like SIGTERM, ensuring graceful container shutdown.
Alternative Approach Comparison
Although shell command combinations like service sshd start && /opt/mq/sbin/rabbitmq-server start can be used to achieve multi-service startup, this method has significant limitations: lack of process monitoring, imperfect error handling, and potential signal transmission abnormalities. In contrast, supervisor provides a complete process management ecosystem, including features like automatic restart, log management, and status monitoring.
Production Environment Recommendations
For complex multi-service scenarios, consider using Docker Compose to split different services into separate containers. This microservices architecture better aligns with Docker's design philosophy, providing improved isolation, scalability, and maintainability. Only in specific requirements should one choose to use supervisor to manage multiple service processes within a single container.