Keeping the Wheels Turning: How to Keep Docker Containers Running Post-Service Startup

Keeping the Wheels Turning: How to Keep Docker Containers Running Post-Service Startup

containers

Introduction

Docker has revolutionized the way we think about deploying applications. However, a common challenge developers face is ensuring that a Docker container continues to run after its services have been started. This blog post explores the mechanisms and strategies for keeping Docker containers running, ensuring that your services remain available and resilient.

Understanding Docker Containers

Docker containers are designed to run a specific task or process. When that process terminates, so does the container. This behavior is by design, with the idea being that containers should remain lightweight and disposable. However, in practical scenarios, we often need the container to keep running even after the initial process has finished.

The PID 1 Problem

The primary reason containers exit after starting a service is the PID 1 problem. In Unix-like systems, the process with Process ID 1 is responsible for initializing the system and then reaping zombie processes. If your service doesn’t handle SIGTERM (a signal to terminate the process), Docker will not be able to shut down cleanly, causing issues.

Solutions to Keep Docker Containers Running

1. Using the docker run Command

The most straightforward method to keep a container running is to start the container with a process that doesn't exit. For instance:

docker run -d my-container tail -f /dev/null

Here, tail -f /dev/null is a non-terminating command that keeps the container alive by 'following' an empty output.

2. Init Systems

Using an init system like tini or systemd can handle the reaping of zombie processes and forwarding signals appropriately.

docker run -d --init my-container

The --init flag attaches an init system to your container, which will handle PID 1 duties.

3. Service Supervisors

Supervisors like supervisord are designed for running multiple services within a single container. They can keep your services running and restart them if they fail.

FROM python:3.8-slim
RUN pip install supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["/usr/local/bin/supervisord"]

In the Dockerfile above, supervisord is used to manage the application process.

4. Entrypoint Scripts

Entrypoint scripts are used to perform setup tasks and to execute the main process. They are commonly used in Docker containers to ensure that the main process stays in the foreground.

FROM ubuntu
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

Your entrypoint.sh script should execute the service in a way that keeps it in the foreground.

5. Using the sleep Command

This is more of a hack and not recommended for production, but you can keep a container alive by running a sleep command in a loop.

docker run -d my-container sh -c "while true; do sleep 10; done"

This command will sleep for 10 seconds in an infinite loop.

Best Practices for Long-Running Containers

Keep Containers Immutable

Ensure your containers are immutable – they should not change state once started. Configurations should be passed in via environment variables or configuration files mounted as volumes.

Logging and Monitoring

Always log to stdout and stderr. Docker captures these streams and makes it easier to monitor the logs using Docker’s logging drivers.

Health Checks

Implement health checks in your Dockerfile using the HEALTHCHECK instruction. This allows Docker to know if your service is still running as expected.

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

Graceful Shutdown

Handle the SIGTERM signal to ensure that your services can shut down gracefully when Docker sends this signal.

Readiness and Liveness Probes

If you're running on Kubernetes, make use of readiness and liveness probes to manage the lifecycle of your containers better.

Conclusion

Keeping a Docker container running after starting services is crucial for service availability and resiliency. Whether you’re using an init system, service supervisor, entrypoint scripts, or handling the process lifecycle correctly, it's vital to ensure that your container’s main process does not exit unexpectedly. By following these strategies and best practices, you can keep your Docker containers spinning smoothly, ensuring that your applications remain responsive and robust.