Behebung von sofortigen Docker-Containerabbruchen

DockerDockerBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Introduction

In this hands-on lab, you will learn how to identify and resolve a common Docker issue: containers that exit immediately after starting. This problem often confuses beginners and can occur for various reasons ranging from configuration errors to application issues.

By completing this lab, you will understand Docker container lifecycle, learn to diagnose immediate container exits, master debugging techniques, and implement best practices to ensure your containers run reliably. These skills are essential for anyone working with Docker in development or production environments.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL docker(("Docker")) -.-> docker/ContainerOperationsGroup(["Container Operations"]) docker(("Docker")) -.-> docker/DockerfileGroup(["Dockerfile"]) docker/ContainerOperationsGroup -.-> docker/run("Run a Container") docker/ContainerOperationsGroup -.-> docker/ps("List Running Containers") docker/ContainerOperationsGroup -.-> docker/rm("Remove Container") docker/ContainerOperationsGroup -.-> docker/exec("Execute Command in Container") docker/ContainerOperationsGroup -.-> docker/logs("View Container Logs") docker/ContainerOperationsGroup -.-> docker/inspect("Inspect Container") docker/ContainerOperationsGroup -.-> docker/create("Create Container") docker/DockerfileGroup -.-> docker/build("Build Image from Dockerfile") subgraph Lab Skills docker/run -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/ps -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/rm -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/exec -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/logs -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/inspect -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/create -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} docker/build -.-> lab-391856{{"Behebung von sofortigen Docker-Containerabbruchen"}} end

Understanding Docker Container Basics

Let's begin by exploring Docker container fundamentals and familiarizing ourselves with the basic commands used to manage containers.

What is a Docker Container?

A Docker container is a lightweight, standalone, and executable software package that includes everything needed to run an application:

  • Code
  • Runtime
  • System tools
  • Libraries
  • Settings

Containers run in isolation from the host system and from other containers, providing consistency across different environments.

Exploring Docker on Your System

First, let's verify Docker is correctly installed and running on your system:

docker --version

You should see output similar to:

Docker version 20.10.21, build baeda1f

Next, let's check if any containers are currently running:

docker ps

This command lists running containers. Since we haven't started any yet, you should see only the column headers:

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

To view all containers, including stopped ones:

docker ps -a

Understanding Container Lifecycle

The Docker container lifecycle consists of several states:

  1. Created: A container is created but not yet started
  2. Running: The container is running its defined processes
  3. Paused: Container processes are temporarily suspended
  4. Stopped: The container has exited or been stopped
  5. Deleted: The container has been removed from the system

Let's run a simple container and observe its lifecycle:

docker run hello-world

You should see output similar to:

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.
...

Notice that this container ran and exited immediately after displaying the message. This is actually normal behavior for the hello-world container, as it's designed to simply display a message and exit.

Check for the container in the list of all containers:

docker ps -a

You should see your hello-world container in the list with an "Exited" status:

CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
a1b2c3d4e5f6   hello-world   "/hello"   30 seconds ago   Exited (0) 30 seconds ago             peaceful_hopper

The exit code (0) indicates the container exited successfully without errors.

Key Docker Commands for Container Management

Here are some essential Docker commands you will use throughout this lab:

  • docker run [OPTIONS] IMAGE [COMMAND]: Create and start a container
  • docker ps: List running containers
  • docker ps -a: List all containers (including stopped ones)
  • docker logs [CONTAINER_ID]: View container logs
  • docker inspect [CONTAINER_ID]: Get detailed container information
  • docker exec -it [CONTAINER_ID] [COMMAND]: Run a command in a running container
  • docker stop [CONTAINER_ID]: Stop a running container
  • docker rm [CONTAINER_ID]: Remove a container

Now that you understand the basics of Docker containers and their lifecycle, we'll move on to troubleshooting containers that exit unexpectedly.

Identifying Container Exit Issues

In this step, we'll create a Docker container that exits immediately and learn how to diagnose the problem.

Running a Container That Exits Immediately

Let's first try running an Ubuntu container:

docker run ubuntu

You'll notice something interesting - the command completes instantly and returns to your prompt. Where's our Ubuntu container? Let's check:

docker ps

No containers are running. Now check all containers, including stopped ones:

docker ps -a

You'll see output similar to:

CONTAINER ID   IMAGE         COMMAND       CREATED          STATUS                      PORTS     NAMES
f7d9e7f6543d   ubuntu        "/bin/bash"   10 seconds ago   Exited (0) 10 seconds ago             focused_galileo
a1b2c3d4e5f6   hello-world   "/hello"      10 minutes ago   Exited (0) 10 minutes ago             peaceful_hopper

The Ubuntu container started and then exited immediately with a status code of 0, indicating it exited without errors. This is expected behavior but may be confusing for beginners.

Understanding Why Containers Exit

Containers are designed to run a specific command or process. When that process completes or exits, the container stops. This is a fundamental principle of Docker's design.

The Ubuntu container exited immediately because:

  1. The default command for Ubuntu image is /bin/bash
  2. When run without the -it flags (interactive, terminal), there's no input to the bash shell
  3. With no input and no specific command to execute, bash exits immediately
  4. When the main process (bash) exits, the container stops

Viewing Container Logs and Information

Let's examine the logs of our exited Ubuntu container to understand what happened. First, find the container ID from your docker ps -a output, then:

docker logs CONTAINER_ID

Replace CONTAINER_ID with your actual container ID. You'll likely see no output because the container didn't produce any logs before exiting.

For more detailed information about the container:

docker inspect CONTAINER_ID

This will display a large JSON object with container configuration and state information.

Let's focus on the exit code:

docker inspect CONTAINER_ID --format='{{.State.ExitCode}}'

You should see:

0

This confirms the container exited normally, not due to an error.

Keeping a Container Running

To keep the Ubuntu container running, we need to either:

  1. Provide an interactive session, or
  2. Override the default command with a long-running process

Let's try the interactive approach:

docker run -it ubuntu

Now you're inside the container with a bash prompt:

root@3a4b5c6d7e8f:/#

The container remains running as long as this bash session is active. Type exit or press Ctrl+D to exit the container.

exit

Alternatively, we can keep a container running by providing a command that doesn't complete immediately:

docker run -d ubuntu sleep 300

This runs the Ubuntu container and executes the sleep 300 command, which will keep the container running for 300 seconds (5 minutes).

Check that the container is running:

docker ps

You should see your container in the running state:

CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
9a8b7c6d5e4f   ubuntu    "sleep 300"   10 seconds ago   Up 10 seconds             hopeful_hopper

When diagnosing containers that exit immediately, remember these key points:

  1. Containers exit when their main process completes
  2. Check logs and exit codes to understand why they stopped
  3. If a container should keep running, ensure its main process doesn't exit

Troubleshooting Common Container Exit Issues

Now that we understand why containers exit immediately, let's explore common causes of unexpected container exits and how to troubleshoot them.

Creating a Container with a Problem

Let's create a simple situation where a container exits unexpectedly. First, create a directory for our test files:

mkdir -p ~/project/docker-exit-test
cd ~/project/docker-exit-test

Now, create a simple Python script with an error:

nano app.py

Add the following code:

import os

## Attempt to read a required environment variable
database_url = os.environ['DATABASE_URL']

print(f"Connecting to database: {database_url}")
print("Application running...")

## Rest of the application code would go here

Save and exit the file (press Ctrl+O, Enter, then Ctrl+X).

Now, create a Dockerfile to build an image with this application:

nano Dockerfile

Add the following content:

FROM python:3.9-slim

WORKDIR /app

COPY app.py .

CMD ["python", "app.py"]

Save and exit the file.

Build the Docker image:

docker build -t exit-test-app .

You should see output indicating that the image was built successfully:

Successfully built a1b2c3d4e5f6
Successfully tagged exit-test-app:latest

Now, run the container:

docker run exit-test-app

You should see the container exit immediately with an error:

Traceback (most recent call last):
  File "/app/app.py", line 4, in <module>
    database_url = os.environ['DATABASE_URL']
  File "/usr/local/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'DATABASE_URL'

The container exited because our Python script expected an environment variable that wasn't provided.

Diagnosing the Issue

When a container exits unexpectedly, follow these troubleshooting steps:

  1. Check the exit code to determine if it's an error:
docker ps -a

Look for your container and note the exit code. It should be non-zero, indicating an error:

CONTAINER ID   IMAGE           COMMAND            CREATED          STATUS                     PORTS     NAMES
b1c2d3e4f5g6   exit-test-app   "python app.py"   20 seconds ago   Exited (1) 19 seconds ago           vigilant_galileo
  1. Examine the container logs:
docker logs $(docker ps -a -q --filter ancestor=exit-test-app --latest)

This retrieves the logs from the most recent container created from our image.

The error message clearly shows the problem: the Python script is trying to access an environment variable named DATABASE_URL that doesn't exist.

Fixing the Issue

Now let's fix the issue by providing the missing environment variable:

docker run -e DATABASE_URL=postgresql://user:password@db:5432/mydatabase exit-test-app

You should now see the container run successfully:

Connecting to database: postgresql://user:password@db:5432/mydatabase
Application running...

The container still exits, but this time it's because our script reaches the end and terminates normally. If we wanted the container to keep running, we would need to modify our script to include an infinite loop or a long-running process.

Common Causes of Container Exits

Here are several common reasons why containers might exit unexpectedly:

  1. Missing environment variables: As we just demonstrated
  2. Absent dependencies: Missing libraries or system packages
  3. Connection failures: Unable to reach a database or other service
  4. Permission issues: Insufficient permissions to access files or resources
  5. Resource limitations: Container runs out of memory or CPU
  6. Application crashes: Bugs in the application code

Troubleshooting Techniques

For each of these issues, here are effective troubleshooting approaches:

  1. Review logs: Always check container logs first with docker logs
  2. Override entrypoint: Use docker run --entrypoint /bin/sh -it my-image to enter the container and investigate
  3. Add debugging statements: Modify your application to add more logging
  4. Check resource usage: Use docker stats to monitor container resource usage
  5. Inspect environment: Run docker exec -it CONTAINER_ID env to verify environment variables

Let's try the entrypoint overriding technique with our image:

docker run --entrypoint /bin/sh -it exit-test-app

Now you're inside the container with a shell. You can explore the environment:

ls -la
cat app.py
echo $DATABASE_URL

You'll see that DATABASE_URL is not set. Exit the container when you're done:

exit

By understanding why containers exit and applying these troubleshooting techniques, you can quickly diagnose and resolve most container exit issues.

Implementing Best Practices for Container Reliability

Now that we understand how to troubleshoot container exits, let's implement best practices to make our containers more reliable and robust. We'll enhance our example application with proper error handling, health checks, and logging.

1. Improving Error Handling

Let's modify our Python application to handle missing environment variables gracefully:

cd ~/project/docker-exit-test
nano app.py

Update the content to:

import os
import time
import sys

## Get environment variables with defaults
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')

print(f"Connecting to database: {database_url}")

## Simulate a long-running process
try:
    print("Application running... Press Ctrl+C to exit")
    counter = 0
    while True:
        counter += 1
        print(f"Application heartbeat: {counter}")
        time.sleep(10)
except KeyboardInterrupt:
    print("Application shutting down gracefully...")
    sys.exit(0)

Save and exit the file.

This improved version:

  • Uses os.environ.get() with a default value instead of raising an exception
  • Implements a long-running loop to keep the container alive
  • Handles graceful shutdown when terminated

Let's rebuild the image:

docker build -t exit-test-app:v2 .

And run the improved container:

docker run -d --name improved-app exit-test-app:v2

Check that the container is running:

docker ps

You should see your container running:

CONTAINER ID   IMAGE              COMMAND            CREATED          STATUS          PORTS     NAMES
c1d2e3f4g5h6   exit-test-app:v2   "python app.py"   10 seconds ago   Up 10 seconds             improved-app

View the logs to confirm it's working:

docker logs improved-app

You should see output like:

Connecting to database: sqlite:///default.db
Application running... Press Ctrl+C to exit
Application heartbeat: 1
Application heartbeat: 2

2. Implementing a Health Check in Dockerfile

Health checks allow Docker to monitor the health of your container. Let's update our Dockerfile to include a health check:

nano Dockerfile

Update it to:

FROM python:3.9-slim

WORKDIR /app

## Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

COPY app.py .

## Add a health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

## Expose port for the health check endpoint
EXPOSE 8080

CMD ["python", "app.py"]

Now we need to add a health check endpoint to our application:

nano app.py

Replace the content with:

import os
import time
import sys
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler

## Get environment variables with defaults
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')

## Simple HTTP server for health checks
class HealthRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/health':
            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write(b'OK')
        else:
            self.send_response(404)
            self.end_headers()

def run_health_server():
    server = HTTPServer(('0.0.0.0', 8080), HealthRequestHandler)
    print("Starting health check server on port 8080")
    server.serve_forever()

## Start health check server in a separate thread
health_thread = threading.Thread(target=run_health_server, daemon=True)
health_thread.start()

print(f"Connecting to database: {database_url}")

## Main application loop
try:
    print("Application running... Press Ctrl+C to exit")
    counter = 0
    while True:
        counter += 1
        print(f"Application heartbeat: {counter}")
        time.sleep(10)
except KeyboardInterrupt:
    print("Application shutting down gracefully...")
    sys.exit(0)

Save and exit the file.

Rebuild the image with our health check:

docker build -t exit-test-app:v3 .

Run the container with the new version:

docker run -d --name healthcheck-app -p 8080:8080 exit-test-app:v3

After about 30 seconds, check the health status:

docker inspect --format='{{.State.Health.Status}}' healthcheck-app

You should see:

healthy

You can also test the health endpoint directly:

curl http://localhost:8080/health

This should return OK.

3. Using Docker Restart Policies

Docker provides restart policies to automatically restart containers when they exit or encounter errors:

docker run -d --restart=on-failure:5 --name restart-app exit-test-app:v3

This policy will restart the container up to 5 times if it exits with a non-zero code.

Available restart policies:

  • no: Never restart (default)
  • always: Always restart regardless of exit status
  • unless-stopped: Always restart unless manually stopped
  • on-failure[:max-retries]: Restart only on non-zero exit

4. Setting Resource Limits

To prevent container crashes due to resource exhaustion, set appropriate resource limits:

docker run -d --name resource-limited-app \
  --memory=256m \
  --cpus=0.5 \
  exit-test-app:v3

This limits the container to 256MB of memory and half of a CPU core.

5. Proper Logging Configuration

For better debugging, configure your application to output structured logs:

docker run -d --name logging-app \
  --log-driver=json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  exit-test-app:v3

This configures the container to use the JSON log driver with log rotation (maximum 3 files of 10MB each).

Summary of Best Practices

By implementing these best practices, you've significantly improved the reliability of your Docker containers:

  1. Graceful error handling with default values
  2. Container health checks for monitoring
  3. Appropriate restart policies for automatic recovery
  4. Resource limits to prevent resource exhaustion
  5. Proper logging configuration for easier troubleshooting

These techniques will help you create resilient containers that can recover from failures and provide better observability when issues occur.

Summary

In this lab, you've learned essential skills for troubleshooting and resolving Docker container exit issues:

  • Understanding the Docker container lifecycle and why containers exit when their main process completes
  • Identifying common causes of immediate container exits through logs and exit codes
  • Implementing robust error handling in containerized applications
  • Adding container health checks to monitor application status
  • Configuring restart policies for automatic recovery from failures
  • Setting appropriate resource limits to prevent container crashes
  • Implementing proper logging strategies for easier debugging

These skills form the foundation of reliable Docker container deployments. As you continue working with Docker in real-world scenarios, you'll find these troubleshooting techniques invaluable for maintaining stable and resilient containerized applications.

Remember that the most common causes of immediate container exits are:

  1. The main process completing its task (by design)
  2. Missing environment variables or configuration
  3. Application errors or exceptions
  4. Resource constraints or connectivity issues

By applying the diagnostic techniques and best practices covered in this lab, you can quickly identify and resolve these issues, ensuring your Docker containers run reliably in any environment.