I have the following Dockerfile with a bit of customization for Nginx server. I build the image with docker build -t nginx-custom:1.21.1 .
command finished without errors. But when I want to create a container from this image with docker run -it -d nginx-custom:1.21.1 /bin/bash
command also not appears any errors. But when I enter to container with docker exec
command and check running processes with ps -ef
the result contains only the bash process.
FROM nginx:1.21.1 AS nginx-base
RUN set -x \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y man nano procps ca-certificates wget gnupg gnupg2 iputils-ping net-tools supervisor \
&& apt-get clean autoclean \
&& apt-get autoremove --yes \
&& rm -rf /var/lib/{apt,dpkg,cache,log}/
EXPOSE 80 443
WORKDIR /var/www
COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["service supervisor restart"]
FROM nginx-base AS nginx-config
# Replace the original Nginx config file
COPY config/nginx.conf /etc/nginx/nginx.conf
# Test HTML file
COPY scripts/index.html /var/www/html/index.html
After some failures I create a compose file with the following content and ran the next command: docker-compose up -d --build
then everything is OK. All necessary processes are running.
version: '3.8'
services:
nginx-custom:
tty: true
build:
context: .
dockerfile: Dockerfile
image: nginx-custom:1.21.1
container_name: nginx-custom
networks:
devnet:
ipv4_address: 172.18.0.4
restart: unless-stopped
networks:
devnet:
external: true
Does anyone know why the Supervisor only works this way?
CodePudding user response:
A Docker container only runs a single process. You need some process manager like supervisord if you need to run multiple processes in a single container, but that's usually considered an anti-pattern. The standard Docker Hub images roughly break into three varieties: base OS images (alpine
, ubuntu
); language toolchains and runtimes (python
, node
, golang
); and things that run some actual program (postgresql
, nginx
). In the last case the base image is already configured to run the program as the single main container process.
This means you can reduce the Dockerfile to just
FROM nginx:1.21.1
COPY config/nginx.conf /etc/nginx/nginx.conf
COPY scripts/index.html /var/www/html/index.html
and the base image already has a correct CMD
. You don't need a text editor, man pages, or low-level network debugging tools, since the only thing this container is going to do is serve Web pages.
A Docker container only runs a single process. There are a couple of ways to specify it. The Dockerfile CMD
is a default, but it can be overridden by Compose command:
or by putting a command after the docker run
image name. That means, if you docker run --rm -it your-image /bin/bash
, the interactive shell runs instead of the CMD
in the Dockerfile.
The simplest answer here is to just not worry about running an interactive shell. docker run
the image without a command. docker exec
sparingly; it's a really useful debugging tool, but not generally the primary way you'll interact with your container.
# do not override the command at the end
docker run -d -p 8080:80 nginx-custom:1.21.1
A Docker container only runs a single process. When that process completes, the container exits. This means the main container process needs to be a long-running foreground process; it can't be a script that launches background processes and then completes, or a "service"-type command.
The base nginx
image will say something like
CMD ["nginx", "-g", "daemon off;"]
which launches the Nginx server as a foreground process.
If you really need to run supervisord, then the main CMD
needs to run supervisord
and not a service
command, and it needs a -n
option to run it in the foreground. If you use the JSON-array form of CMD
then you are responsible for breaking the command into words yourself. If there are embedded spaces (as in daemon off;
in my example, or the CMD
in the question) they are treated as a single word with spaces include, like if you quoted it in the shell.
CMD ["supervisord", "-n"]
Generally commands like service
don't work in Docker, for a couple of reasons. Since they do some action and return immediately they're not suitable as the main container command. If you say "restart" as you do in the question, the service won't have previously been running. If the environment depends on some init daemon, that won't be running either, and service
or systemctl
won't be able to talk to it. Don't try to introduce an init system, just run the command directly as the single foreground main container process.