Diploma in DevOps
Containerization with Docker
•
Diploma
Container Lifecycle and Docker CLI
Introduction
Docker containers provide isolated, reproducible environments for running applications and their dependencies. A container is not a lightweight virtual machine; it is a process, or group of processes, isolated by the operating system through mechanisms such as namespaces and control groups. The Docker CLI supplies the practical interface through which developers and operators create, start, inspect, operate, monitor, stop, restart, and remove containers.
Understanding the container lifecycle is essential in DevOps because an application can be correctly packaged yet still fail through an incorrect command, missing environment variable, unavailable port, insufficient memory, or unsuitable restart policy. Lifecycle knowledge also supports reliable deployment, incident response, security, and automation.
Learning Objectives
By the end of this chapter, you should be able to:
distinguish images, containers, processes, and lifecycle states;
create and run containers using the Docker CLI;
publish ports and supply environment variables safely;
inspect container configuration, processes, logs, and resource use;
execute commands inside running containers;
stop, restart, pause, kill, and remove containers appropriately;
apply CPU, memory, and restart controls;
troubleshoot common lifecycle and networking failures; and
select professional practices for reliable and secure container operations.
1. Images, Containers, and the Docker Engine
A Docker image is an immutable, layered package containing a filesystem and metadata. It normally includes an application, runtime libraries, configuration defaults, and an instruction identifying the process to start. A container is a runtime instance of an image. Multiple containers may be created from the same image, with each container having its own writable layer, process, network identity, and lifecycle state.
The Docker Engine consists conceptually of a client, a daemon, and an execution environment. The Docker CLI is the client. It sends requests to the Docker daemon, which manages images, containers, networks, and volumes. The daemon asks a container runtime to create the isolated process.
A useful distinction is:
Object
Main purpose
Image
Immutable application template
Container
Runtime instance of an image
Volume
Persistent data managed independently of a container
Network
Communication mechanism between containers and external clients
Registry
Repository for storing and distributing images
The command docker run is a high-level operation. It can create a container from an image and start it in one command. In contrast, docker create creates the container but does not start its process, while docker start starts an existing stopped container.
2. Lifecycle States and Transitions
A container normally moves through the following states:
Created: configuration exists, but the main process has not started.
Running: the configured main process is executing.
Paused: processes are temporarily frozen.
Exited: the main process has terminated, either normally or because of an error.
Restarting: Docker is attempting to start the container again under a restart policy.
Dead: Docker could not completely remove or manage the container.
The most important rule is that a container’s lifetime is tied to its main process, also called PID 1 within the container. If that process exits, the container normally becomes exited. A container does not remain alive merely because its image exists.
Docker container lifecycle state diagram
Figure: Docker container lifecycle state diagram
Figure: Principal Docker container lifecycle states and transitions.
The command docker ps lists running containers. docker ps -a lists running and stopped containers. A container’s status should be interpreted together with its exit code and logs. For example, exit code 0 commonly indicates normal termination, whereas a non-zero code indicates an error; however, the meaning depends on the application.
3. Creating and Running Containers
The general form of the most frequently used command is:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
For example:
docker run --name web-test -d -p 8080:80 nginx:latest
This command:
downloads nginx:latest if it is not available locally;
creates a container named web-test;
maps host TCP port 8080 to container TCP port 80;
starts the container in detached mode; and
returns the container identifier.
Useful options include:
Option
Purpose
Example
--name
Assign a readable name
--name api
-d
Run in background
-d
-it
Interactive terminal
-it
--rm
Remove container after exit
--rm
-p
Publish a port
-p 8080:80
-P
Publish exposed ports automatically
-P
--env or -e
Set an environment variable
-e MODE=prod
--env-file
Read variables from a file
--env-file .env
--network
Attach to a network
--network app-net
--volume or -v
Mount storage
-v data:/var/lib/app
--restart
Define restart behavior
--restart unless-stopped
--memory
Set memory limit
--memory 512m
--cpus
Set CPU quota
--cpus 1.5
Foreground and interactive execution
A foreground container attaches its standard input, output, and error streams to the terminal. This is useful for observing a short-lived command:
docker run --rm alpine:3.20 echo "container completed"
An interactive shell can be started with:
docker run --rm -it alpine:3.20 sh
The -i option keeps standard input open, and -t allocates a pseudo-terminal. They are commonly combined as -it; neither option makes a service persistent by itself.
Create, then start
Separating creation from starting is useful when configuration must be reviewed:
This separation is also valuable in automation because it makes configuration and execution distinct operations.
4. Ports and Container Networking
A process inside a container may listen on a container port, but that port is not automatically reachable from the host. Port publishing creates a host-to-container forwarding rule:
docker run -d --name web -p 127.0.0.1:8080:80 nginx
The syntax is generally [HOST_IP:]HOST_PORT:CONTAINER_PORT[/PROTOCOL]. Thus, the example binds host loopback address 127.0.0.1, host port 8080, and container TCP port 80. Binding to 127.0.0.1 prevents direct access through external host interfaces. Binding to 0.0.0.0 exposes the port on all host interfaces and should be used only when required and protected by suitable network controls.
Common port failures include:
the application listens on 127.0.0.1 inside the container rather than 0.0.0.0;
the host port is already occupied;
the wrong container port was published;
a firewall or cloud security group blocks traffic; or
the service is not ready even though the container is running.
Useful commands include:
docker port web
docker inspect web
docker network ls
docker network inspect bridge
Containers on the same user-defined network can normally reach each other by container name. This is preferable to hard-coding changing IP addresses. For example:
docker network create app-net
docker run -d --name redis --network app-net redis:7
docker run -d --name api --network app-net -e CACHE_HOST=redis my-api:1.0
5. Environment Variables and Configuration
Environment variables provide runtime configuration without rebuilding an image:
docker run -d --name api \
-e APP_MODE=production \
-e DATABASE_HOST=db \
-e DATABASE_PORT=5432 \
my-api:1.0
A file may be used for several variables:
docker run --env-file production.env my-api:1.0
Variables should be treated as configuration inputs, not as a complete secrets-management system. Environment variables can be exposed through inspection, process diagnostics, logs, crash reports, or orchestration interfaces. Passwords, API tokens, and private keys should preferably be supplied through a dedicated secret-management mechanism, with restricted file permissions and rotation procedures.
The image may define defaults using ENV, but a value supplied at runtime generally overrides that default. Configuration precedence should be documented because application defaults, image settings, command-line values, mounted files, and orchestration settings can interact.
6. Inspecting and Monitoring Containers
Inspection should proceed from general status to specific evidence.
docker ps -a
docker inspect api
docker logs api
docker logs --tail 100 --timestamps api
docker top api
docker stats api
docker inspect returns structured JSON containing identifiers, state, mounts, network settings, environment variables, image information, and configuration. Important fields include State.Status, State.ExitCode, State.Error, State.StartedAt, and State.FinishedAt.
To extract a particular field:
docker inspect --format '{{.State.Status}}' api
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' api
docker logs displays data written to the container’s standard output and standard error. It does not automatically display messages written to arbitrary files inside the container. Production applications should normally log structured, useful events to standard streams so that Docker or a logging platform can collect them.
docker stats reports live resource usage. It is an operational view, not a replacement for application monitoring. A container may use little CPU while returning errors, and a healthy resource graph does not prove that a service is available.
7. Executing Commands in a Running Container
The docker exec command starts an additional process in an existing running container:
docker exec -it api sh
docker exec api printenv APP_MODE
docker exec api ls -la /app
exec is useful for diagnosis, checking configuration, and controlled administrative tasks. It does not change the image, and changes made to the writable layer are normally lost when the container is deleted. It is therefore not a substitute for changing the Dockerfile or deployment configuration.
A common mistake is attempting to execute a shell that is not present. Minimal images may contain sh but not bash; some contain no shell at all. In such cases, use an available diagnostic executable, a temporary debugging image, or application-specific health and diagnostic endpoints.
8. Stopping, Restarting, Pausing, and Removing
The principal lifecycle commands are:
docker stop api
docker start api
docker restart api
docker pause api
docker unpause api
docker kill api
docker rm api
docker stop asks the main process to terminate gracefully and waits for a configurable period before forcing termination. docker kill sends a signal immediately by default and is appropriate when a process is unresponsive or urgent termination is required. Graceful shutdown is preferable because applications may need to flush data, close connections, or release locks.
A stopped container can be started again with its original configuration. docker restart stops and starts it. docker pause freezes processes without terminating them; it is not a normal application shutdown mechanism.
A running container cannot normally be removed:
docker rm api
docker rm -f api
The -f option forcibly stops and removes the container and should be used deliberately. Removing a container does not normally remove named volumes, images, or other containers. The --rm option is convenient for temporary jobs because Docker removes the container automatically after it exits.
9. Resource Limits and Restart Policies
Without controls, a container can consume excessive host resources. Resource limits create operational boundaries:
Here, memory is limited to 512 MiB and CPU capacity is limited approximately to one CPU. Actual scheduling depends on host capacity and competing workloads. Memory limits can cause an out-of-memory termination; CPU limits generally throttle rather than terminate a process.
Resource controls should be selected from measured workload behavior. A limit that is too low causes instability; one that is too high weakens isolation. Monitor working-set usage, throttling, restart frequency, latency, and application-specific indicators.
on-failure is appropriate for retrying failed jobs, while always and unless-stopped are commonly used for long-running services. Restart policies do not repair an application defect; they may create a crash loop. A health check and alerting are therefore important.
10. Troubleshooting Method
A disciplined procedure prevents random command execution.
Step 1: Identify the actual object
docker ps -a --no-trunc
Confirm the name, image, status, and exit code. Names may be confused with image tags.
Step 2: Read logs and metadata
docker logs --tail 200 api
docker inspect api
Look for missing configuration, binding errors, permission failures, migrations, and dependency failures.
Step 3: Verify the process and network
docker top api
docker port api
docker inspect --format '{{json .NetworkSettings.Networks}}' api
Check whether the process is listening on the intended interface and whether the published port matches the service port.
Step 4: Test from the correct location
A service may be reachable from another container but not from the host, or reachable locally but blocked externally. Test each path separately. Use a temporary diagnostic container on the same network when appropriate.
Step 5: Check resources and dependencies
docker stats api
docker events --since 10m
Inspect memory pressure, CPU throttling, restarts, and dependency availability.
Step 6: Reproduce with a controlled command
Run the image interactively or override its command when safe:
docker run --rm -it --entrypoint sh my-api:1.0
Do not modify production containers casually. Record changes and reproduce the fix in source configuration, the Dockerfile, or the deployment definition.
Symptom
Likely causes
First checks
Container exits immediately
Main process completed, bad command, missing config
docker logs, exit code, image command
Port unavailable
Wrong mapping, host conflict, service not listening
docker port, logs, listener configuration
Cannot connect to another container
Different network, wrong name, dependency not ready
docker network inspect, DNS/name, readiness
Repeated restarts
Crash, failed health logic, restart policy
docker inspect, logs, events
Permission denied
User identity, ownership, read-only mount
docker exec, mount options, file ownership
Out-of-memory termination
Limit too low or leak
docker stats, host memory, exit state
11. Applied Case: A Failing API Container
The following is an illustrative case. A small development team deploys an API using:
docker run -d --name orders-api -p 8080:8080 orders-api:2.1
Users report that the service is unavailable. The team first runs docker ps, discovers that the container is not listed, and then runs docker ps -a. The status is Exited (1).
Analysis
The team examines the logs:
docker logs orders-api
The application reports that DATABASE_HOST is missing. This establishes that the container process started but terminated during configuration validation. The team then inspects the image and confirms that the API expects a database hostname and credentials at runtime.
A second test supplies configuration and connects both services to a user-defined network:
The team checks logs, port publication, and resource use. They also discover that the API starts before the database is ready. They add application retry logic and a health/readiness design rather than relying solely on container start order.
Outcome and learning points
The immediate failure was caused by missing configuration, while the second potential failure involved readiness rather than networking. The case demonstrates that “container running” does not mean “application ready.” It also demonstrates the importance of container names on user-defined networks, localhost-only port binding during development, explicit configuration, and evidence-based troubleshooting.
12. Security, Governance, and Professional Practice
Operational convenience must not override security. Apply least privilege by running as a non-root user when possible, limiting capabilities, avoiding privileged mode, and mounting only necessary paths. Do not place secrets in images, Dockerfiles, shell history, public command output, or source repositories.
Use trusted, versioned image tags or digests for reproducibility. The tag latest can point to different image content over time, so it is unsuitable as a strong production version identifier. Scan images for vulnerabilities, remove unnecessary packages, patch base images, and maintain an inventory of deployed versions.
Container isolation is not an absolute security boundary. The Docker daemon and host kernel are highly privileged components. Access to the Docker socket can effectively provide host-level control, so socket exposure must be tightly controlled. Logging should avoid sensitive values, and retained logs should follow organizational privacy and retention policies.
13. Contemporary Practice
The Docker CLI is widely used for local development and single-host operations. Larger environments commonly use Compose, Kubernetes, or another orchestrator. These systems add declarative configuration, service discovery, health management, rolling updates, and secret integration, but they do not eliminate the need to understand individual container lifecycle behavior.
Modern build and deployment pipelines commonly combine image scanning, software bill-of-materials generation, provenance or signing, automated tests, and controlled promotion between environments. AI-assisted tools can help summarize logs or suggest commands, but operators must verify proposed actions, particularly commands that remove containers, expose ports, alter permissions, or reveal sensitive data.
Best Practices Checklist
Use explicit image versions and document the expected architecture.
Give important containers meaningful names.
Keep application state in volumes or external services, not ephemeral writable layers.
Publish only required ports and bind development services narrowly.
Use user-defined networks and service names for inter-container communication.
Supply configuration through controlled mechanisms and protect secrets.
Emit useful logs to standard output and error.
Define resource limits and observe actual usage.
Use graceful shutdown and an appropriate restart policy.
Add health and readiness checks for services with dependencies.
Treat exec changes as temporary and reproduce permanent changes declaratively.
Remove unused containers and images through controlled housekeeping.
Record operational actions in production environments.
Chapter Summary
Docker containers are runtime instances of immutable images, and their behavior is governed by the lifecycle of the main process. The Docker CLI supports creation, execution, inspection, diagnosis, stopping, restarting, pausing, and removal. Port publishing controls host access, while user-defined networks support name-based communication between services. Environment variables provide runtime configuration but require careful handling when they contain secrets. Inspection, logs, process views, events, and resource statistics provide complementary evidence during troubleshooting. Memory and CPU limits improve isolation, while restart policies must be combined with monitoring and application health logic. Professional container operations require reproducibility, least privilege, controlled networking, secure configuration, explicit versions, and declarative correction of problems.
Key Terms
Container: An isolated runtime instance of an image.
Image: Immutable, layered package used to create containers.
Docker daemon: Background service that manages Docker objects and operations.
Docker CLI: Command-line client used to interact with Docker.
PID 1: The main process whose termination normally ends a container.
Created: Lifecycle state in which configuration exists but execution has not started.
Running: State in which the main container process is executing.
Exited: State in which the main process has terminated.
Port publishing: Mapping of a host port to a container port.
Environment variable: Runtime key-value configuration supplied to a process.
User-defined network: Docker network providing controlled container communication and name resolution.
Volume: Persistent storage managed independently of a container’s writable layer.
Restart policy: Rule controlling whether Docker attempts to start a stopped container.
Resource limit: Boundary placed on CPU, memory, or another host resource.
Health check: Test used to assess whether an application is functioning properly.
docker exec: Command that starts an additional process in a running container.
Graceful shutdown: Controlled termination allowing cleanup and data flushing.
Exit code: Numeric result returned by a terminated process.
Image tag: Human-readable image version label, such as 2.1.
Least privilege: Security principle of granting only the permissions required.
Review Questions
Short Answer Questions
What is the relationship between a Docker image and a container?
Why does a container normally stop when its main process exits?
What is the difference between docker stop and docker kill?
What information can docker inspect provide?
Why are user-defined networks useful for multi-container applications?
Long Answer Questions
Explain the major Docker container lifecycle states and the commands that cause transitions between them.
Describe how host ports, container ports, and application bind addresses interact.
Explain how environment variables, image defaults, and secret-management practices should be combined.
Discuss a systematic method for diagnosing a container that repeatedly exits.
Explain the operational benefits and risks of CPU limits, memory limits, and restart policies.
Analytical / Application Questions
An API container is running, but requests to the host port fail. Develop a diagnostic sequence that distinguishes a port conflict, incorrect mapping, application binding error, firewall issue, and application readiness issue.
Design a Docker command set for an API and database that uses a user-defined network, controlled port exposure, runtime configuration, resource limits, and a suitable restart policy. Justify each decision.
A service restarts continuously after deployment and consumes increasing memory before termination. Analyze the evidence you would collect and propose corrective actions at the application, container, and operational levels.
Multiple Choice Questions with Answers
Question 1
Which condition normally causes a Docker container to enter the exited state?
A. The image is present on the host
B. The container’s main process terminates
C. A host port is published
D. A user-defined network is created
Correct Answer: B. The container’s main process terminates
Question 2
Which command lists both running and stopped containers?
A. docker images
B. docker ps -a
C. docker network ls
D. docker top
Correct Answer: B. docker ps -a
Question 3
What does -p 8080:80 normally configure?
A. Container port 8080 mapped to host port 80
B. Host port 8080 mapped to container port 80
C. Two container ports connected internally
D. A memory limit of 8080 MiB
Correct Answer: B. Host port 8080 mapped to container port 80
Question 4
Which command starts an additional process inside a running container?
A. docker exec
B. docker create
C. docker rm
D. docker tag
Correct Answer: A. docker exec
Question 5
Which option supplies a runtime environment variable?
A. --volume
B. --network
C. --env
D. --memory
Correct Answer: C. --env
Question 6
What is the main purpose of docker logs?
A. To display image layers
B. To show data written to the container’s standard output and error
C. To remove old log files from the host
D. To list available networks
Correct Answer: B. To show data written to the container’s standard output and error
Question 7
Which command requests graceful termination before force may be required?
A. docker pause
B. docker stop
C. docker kill
D. docker inspect
Correct Answer: B. docker stop
Question 8
What is a principal risk of using the latest image tag in production?
A. It prevents all port publishing
B. It always disables networking
C. It may refer to different image content over time
D. It limits memory to zero
Correct Answer: C. It may refer to different image content over time
Question 9
Which option limits a container’s CPU capacity approximately to one CPU?
A. --cpus=1.0
B. --ports=1.0
C. --memory=1.0
D. --processes=1.0
Correct Answer: A. --cpus=1.0
Question 10
Why is a user-defined Docker network generally preferred for communicating services?
A. It automatically stores application data permanently
B. It provides controlled communication and name-based service discovery
C. It removes the need for application readiness checks
D. It converts containers into virtual machines
Correct Answer: B. It provides controlled communication and name-based service discovery