Docker Debugging
Introduction
Docker has revolutionized how we build, ship, and run applications by providing a consistent environment across different stages of development. However, when things go wrong in containerized applications, debugging can be challenging due to the isolated nature of containers. This guide will walk you through effective strategies and tools for debugging Docker containers, helping you quickly identify and resolve issues to maintain optimal performance.
Understanding Docker Container Lifecycle
Before diving into debugging techniques, it's important to understand how Docker containers operate:
Debugging can occur at various stages, but most commonly when a container is running or when it unexpectedly stops.
Essential Docker Debugging Commands
Inspecting Container Logs
The first step in debugging any Docker container is examining its logs:
# Basic log viewing
docker logs container_name_or_id
# Follow logs in real-time
docker logs -f container_name_or_id
# Show timestamps
docker logs --timestamps container_name_or_id
# Show only the last 100 lines
docker logs --tail 100 container_name_or_id
Example output:
2023-05-15T14:32:18.215Z INFO Server started on port 3000
2023-05-15T14:32:45.123Z ERROR Database connection failed: timeout
2023-05-15T14:32:50.876Z INFO Retrying database connection...
2023-05-15T14:33:01.345Z INFO Database connected successfully
Inspecting Container Details
To get detailed information about a container:
docker inspect container_name_or_id
This command returns a JSON object with comprehensive information about the container, including:
- Network settings
- Mount points
- Environment variables
- Resource constraints
- Current state
For a more focused approach, use the format option to extract specific information:
# Get IP address
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id
# Get environment variables
docker inspect --format='{{.Config.Env}}' container_name_or_id
Interactive Debugging with Shell Access
Sometimes you need to explore from inside the container:
# Access a running container with bash (if available)
docker exec -it container_name_or_id bash
# If bash is not available, try sh
docker exec -it container_name_or_id sh
Once inside, you can:
- Check running processes with
ps aux
- Examine file contents
- Check network connectivity
- Verify environment variables with
env
Advanced Debugging Techniques
Debugging Container Startup Issues
If your container fails to start, these approaches can help:
- Start with interactive mode:
docker run -it --entrypoint sh your_image
This bypasses the default entry point and gives you a shell to investigate.
- Check for dependency issues:
# Create a Dockerfile for debugging
FROM your_problematic_image
CMD ["sleep", "3600"]
Build and run this image, then exec into it to investigate without it immediately exiting.
Monitoring Resource Usage
Performance issues often stem from resource constraints:
# View real-time container stats
docker stats container_name_or_id
# Get more detailed process information
docker top container_name_or_id
Example output from docker stats
:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c8f53ba47acc web-server 0.15% 45.57MiB / 7.772GiB 0.57% 648B / 648B 0B / 0B 4
Debugging Network Issues
Container network problems are common debugging scenarios:
# Check what ports are exposed
docker port container_name_or_id
# Install network tools inside container
docker exec -it container_name_or_id sh -c "apt-get update && apt-get install -y iputils-ping net-tools"
# Test connectivity from inside
docker exec -it container_name_or_id ping google.com
Inspecting Docker Events
The events stream can reveal important system-wide information:
docker events
For targeted debugging, filter events by container:
docker events --filter container=container_name_or_id
Debugging Multi-Container Applications
Docker Compose applications require additional debugging strategies:
Viewing Logs from All Services
docker-compose logs
# Follow logs from specific service
docker-compose logs -f service_name
Selective Service Startup
# Start only specific services
docker-compose up database api
# Rebuild a specific service
docker-compose up --build service_name
Performance Debugging
Identifying CPU and Memory Issues
# Create a process list showing resource usage
docker exec container_name_or_id ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu
# Check memory allocation details
docker exec container_name_or_id cat /proc/meminfo
Identifying I/O Bottlenecks
For applications experiencing slowdowns:
# Install iostat (if not available)
docker exec -it container_name_or_id sh -c "apt-get update && apt-get install -y sysstat"
# Monitor I/O activity
docker exec container_name_or_id iostat -x 1 5
Practical Debugging Walkthrough
Let's walk through a common debugging scenario:
Scenario: Node.js Application with High Memory Usage
- Identify the issue:
docker stats app_container
You notice memory usage climbing steadily.
- Check application logs:
docker logs app_container
No obvious errors, but response times are increasing.
- Inspect running processes:
docker exec app_container ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem
Output shows Node.js process consuming large amounts of memory.
- Generate heap dump (Node.js specific):
docker exec app_container node --inspect=0.0.0.0:9229
Then connect with Chrome DevTools to examine memory usage.
- Fix and verify:
After fixing a memory leak in the code:
# Rebuild and restart the container
docker-compose up -d --build app_service
# Verify the fix
docker stats app_container
Using Docker Debugging Tools
Docker Desktop Extension
If you're using Docker Desktop, the built-in dashboard provides:
- Container logs
- Shell access
- Port information
- Resource usage graphs
Debug with Visual Studio Code
VS Code's Docker extension enables:
- Attaching debuggers to containers
- Setting breakpoints in containerized code
- Viewing logs directly in the editor
Add this to your .vscode/launch.json
for Node.js debugging:
{
"type": "node",
"request": "attach",
"name": "Docker: Attach to Node",
"port": 9229,
"address": "localhost",
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app",
"protocol": "inspector"
}
Best Practices for Docker Debugging
-
Use proper logging:
- Implement structured logging (JSON format)
- Include contextual information (request IDs, timestamps)
- Set appropriate log levels
-
Build debugging-friendly images:
- Include troubleshooting tools in development images
- Consider multi-stage builds with debuggable development stages
-
Implement health checks:
dockerfileHEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/ || exit 1
-
Set resource limits:
bashdocker run --memory=512m --cpus=0.5 your_image
This makes resource issues more apparent before production.
Summary
Debugging Docker containers requires understanding both container-specific concepts and traditional debugging techniques. By mastering the tools and approaches covered in this guide, you'll be able to:
- Quickly identify the source of problems in containerized applications
- Resolve issues before they impact production environments
- Improve the overall performance and reliability of your Docker deployments
Remember that effective debugging is a proactive process. Implementing proper monitoring, logging, and health checks can prevent many issues before they require intensive debugging.
Additional Resources
- Documentation: Docker troubleshooting guide
- Tool: ctop - Top-like interface for container metrics
- Tool: dive - Explore Docker image layers
Exercises
- Debug a container that starts and immediately exits. Identify the cause and fix it.
- Set up health checks for a web application container and observe how Docker handles unhealthy containers.
- Create a multi-container application with Docker Compose and practice debugging communication issues between services.
- Implement a monitoring solution that alerts you when a container's memory usage exceeds 80%.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)