Container Lifecycle Operations
Effective container management begins with understanding how to control the full lifecycle—from creation and execution to termination and cleanup. Unlike traditional virtual machines, containers are designed to be ephemeral, but that doesn’t mean their orchestration is trivial. Precise command usage ensures reliability, observability, and resource efficiency.
Core Lifecycle Commands
docker create: Instantiates a container in a stopped state—ideal for pre-configuration (e.g., volume mounts, network settings) before activation.docker start/docker stop: Controls runtime state.stopsendsSIGTERM, waits 10 seconds by default, then escalates toSIGKILLif needed.docker inspect: Returns structured JSON metadata—including IP addresses, port bindings, mounted volumes, and health status.docker exec -it <container> <command>: Launches an interactive process inside a running container—critical for debugging without restarting.docker logs -f <container>: Streams stdout/stderr output, optionally following live updates or filtering by timestamp.docker cp <src> <dst>: Copies files between host and container filesystems—useful for log extraction or configuraton injection.docker update: Dynamically adjusts resource limits (CPU shares, memory caps) on live containers without interruption.
Practical Example: Managing a Flask Service
Let’s deploy a lightweight Flask application while demonstrating key lifecycle patterns:
- Create the app (
app.py):
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def index():
counter_file = "/var/data/visits"
count = 0
if os.path.exists(counter_file):
with open(counter_file, "r") as f:
count = int(f.read().strip() or "0")
with open(counter_file, "w") as f:
f.write(str(count + 1))
return f"Visit #{count + 1}"
if __name__ == "__main__":
os.makedirs("/var/data", exist_ok=True)
app.run(host="0.0.0.0", port=8000)
- Build the image:
FROM python:3.11-slim
WORKDIR /srv
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
VOLUME ["/var/data"]
EXPOSE 8000
CMD ["python", "app.py"]
- Build and launch:
docker build -t flask-counter .
docker run -d \
--name counter-app \
-p 8080:8000 \
-v counter-storage:/var/data \
--restart unless-stopped \
flask-counter
- Observe and manage:
# Stream logs
docker logs -f counter-app
# Enter container interactively
docker exec -it counter-app sh
# Check resource usage
docker stats counter-app
# Gracefully terminate
docker stop counter-app
# Remove stopped instance
docker rm counter-app
# Clean up associated volume
docker volume rm counter-storage
Persistent and Ephemeral Data Strategies
Docker offers three primary mechanisms for data handling—each suited to distinct use cases:
- Volumes: Managed by Docker, stored in
/var/lib/docker/volumes/, ideal for production persistence and cross-container sharing. - Bind mounts: Direct host path mappings—excellent for development, config injection, or sharing host directories like
/etcor source trees. - tmpfs mounts: In-memory storage—perfect for secrets, session caches, or temporary artifacts that must never touch disk.
Volume-Based Persistence Example
Using the same Flask app, persist visit counts across restarts:
# Create named volume
docker volume create counter-storage
# Run with volume mount
docker run -d \
--name counter-app \
-p 8080:8000 \
-v counter-storage:/var/data \
flask-counter
Now, even after docker stop counter-app && docker start counter-app, the counter retains its value.
Secure tmpfs Usage for Sensitive Data
To avoid leaking credentials or tokens to disk:
docker run -d \
--name secure-app \
--tmpfs /run/secrets:rw,size=64k,mode=0700 \
-v ./config.json:/etc/app/config.json:ro \
flask-counter
Any file written to /run/secrets lives only in RAM and vanishes when the container exits.
Container Networking Fundamentals
Docker abstracts networking through drivers, enabling flexible communication models:
| Driver | Use Case | Isolation Level |
|---|---|---|
bridge |
Default single-host inter-container traffic | Full network namespace isolation |
host |
Performence-critical services (e.g., proxies) | No isolation—shares host network stack |
overlay |
Multi-host clusters (Swarm/Kubernetes) | Encrypted VXLAN tunneling across nodes |
none |
High-security air-gapped workloads | No network interfaces attached |
Multi-Service Communication with Custom Networks
Enable DNS-based service discovery between frontend and backend:
# Create isolated network
docker network create --driver bridge app-net
# Launch backend first (resolvable as 'backend')
docker run -d \
--name backend \
--network app-net \
-e FLASK_ENV=production \
python:3.11-slim \
sh -c "pip install flask && python -c \"from flask import Flask; app=Flask(__name__); @app.route('/health')\ndef h(): return 'OK'; app.run(host='0.0.0.0', port=5000)\""
# Launch frontend referencing backend by name
docker run -d \
--name frontend \
--network app-net \
-p 8000:5000 \
python:3.11-slim \
sh -c "pip install flask requests && python -c \"import requests, time; from flask import Flask; app=Flask(__name__); @app.route('/')\ndef index(): return requests.get('http://backend:5000/health').text; app.run(host='0.0.0.0', port=5000)\""
Requests from frontend resolve backend automatically via embedded DNS—no hardcdoed IPs or port mapping required.
Declarative Orchestration with Compose
Replace manual commands with a reproducible docker-compose.yml:
version: '3.9'
services:
web:
build: .
ports: ["8000:8000"]
depends_on: [db]
networks: [app-net]
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: counterdb
POSTGRES_PASSWORD: devpass
volumes: [pgdata:/var/lib/postgresql/data]
networks: [app-net]
volumes:
pgdata:
networks:
app-net:
driver: bridge
Then launch the entire stack with one command:
docker-compose up -d
This approach enforces consistency across environments, embeds networking logic, and simplifies scaling and teardown.