Adding other services to a Docker container
Tags: docker, linux, containerapps, sysadmins
Published: 2022-11-20 13:32:21

If you are about to dockerize a production-ready infrastructure in order to save server-related costs, you may find a problem with certain images that are enough complex to be a pain to override but does not fulfill your requeriments completly.

In that case the common roadmap is to extend that image until you are comfortable with it. But, what if we want or need to add some other services? Simply overriding the `ENTRYPOINT` layer is often both a risk and a pain too, and you would have to be aware of the changes in the original image. For that we can look after the existence of `supervisord`.

# Learning by example

An example of this is the official *[Redmine](https://github.com/docker-library/redmine/blob/cae991b96a33dbf0771dc73c5e145c9badc4c6c8/5.0/Dockerfile)* image. This image serves by default the *Redmine* app in the port `3000` using `HTTP`. But we want to use `HTTPS` because we want to use it in a production environment! Then we need a `HTTP` server acting as a proxy where we configure `HTTPS`.

Well, that's not an issue, we just extend the image in a `Dockerfile` like the following:

```
FROM redmine:5

RUN apt-get update && \
    apt-get install -y \
    nginx \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY conf/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
EXPOSE 443
```
Being `conf/default.conf` the *Nginx* config file, something like the following:
```
server {
    listen       80;
    listen      [::]:80;
    server_name  redmine.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen      443 ssl;
    listen      [::]:443 ssl;
    server_name redmine.example.com;

    ssl_certificate     /path/to/certificate.crt;
    ssl_certificate_key /path/to/certificate.key;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }

}
```
Ok, but that's not launching *Nginx* at all yet. That's why we need `supervisord`. If we inspect the *Redmine* [`Dockerfile`](https://github.com/docker-library/redmine/blob/cae991b96a33dbf0771dc73c5e145c9badc4c6c8/5.0/Dockerfile), we can see the following lines:
```
WORKDIR /usr/src/redmine
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["rails", "server", "-b", "0.0.0.0"]
```
Thats all the information we need to configure `supervisord` to launch *Redmine*. But first, we need to install it. For that we edit our `Dockerfile` so it finally remains like the following:
```
FROM redmine:5

RUN apt-get update && \
    apt-get install -y \
    supervisor \
    nginx \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY conf/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY conf/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
EXPOSE 443

ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
```
We have added the `supervisor` package to the `apt-get install` command, and the copy of the configuration file of it. As we want `supervisord` to be our new entrypoint we added the last line too, passing the configuration file to the command.

Then the `conf/supervisor.conf` file will have to be something like this:
```
[supervisord]
nodaemon=true
user=root

[program:nginx]
user=root
command=nginx

[program:redmine]
user=redmine
directory=/usr/src/redmine
command=/docker-entrypoint.sh rails server -b 127.0.0.1
```
The first section is the configuration of the supervisor daemon itself. We simply set the user to be root and indicate that it have to run in foreground with `nodaemon=true`.

With the second section we just start *Nginx* using the default configuration file (the one we copied in the `Dockerfile`).

With the last section we demand `supervisord` to start *Redmine*. First we change to the dir `/usr/src/redmine`, as the *Redmine* [`Dockerfile`](https://github.com/docker-library/redmine/blob/cae991b96a33dbf0771dc73c5e145c9badc4c6c8/5.0/Dockerfile) states with the `WORKDIR` layer. And we start the application linking the `ENTRYPOINT` and the `CMD` layers of the original [`Dockerfile`](https://github.com/docker-library/redmine/blob/cae991b96a33dbf0771dc73c5e145c9badc4c6c8/5.0/Dockerfile). We also change the `IP` *Redmine* will listen on to `127.0.0.1` in order to avoid direct connections from outside the container.

# Conclusion

We have tweaked a Docker image to fit the need of other services inside the same container without changing a word of the original image.

You can see more about `supervisor` in the [Debian man page](https://manpages.debian.org/testing/supervisor/supervisor.1.en.html).