
Lab 000 - Docker Basics¶
- This lab covers the fundamental concepts and commands of Docker.
- You will learn basic Docker operations including running containers, managing images, and creating simple Dockerfiles.
- Each task is designed to be simple and atomic, focusing on one specific Docker command or concept.
- By the end of this lab, you will have hands-on experience with core Docker functionality.
- The lab includes 50 tasks divided into Docker CLI commands, Dockerfile creation, and Docker Compose orchestration.
Tasks Overview¶
Docker CLI Tasks¶
- Task 01: Run hello-world container
- Task 02: List running containers
- Task 03: List all containers
- Task 04: Pull an image
- Task 05: List images
- Task 06: Run container interactively
- Task 07: Run container in background
- Task 08: List running containers again
- Task 09: Stop a container
- Task 10: Remove a container
- Task 11: Inspect a container
- Task 12: View container logs
- Task 13: Execute command in container
- Task 14: Copy file to container
- Task 15: Copy file from container
- Task 16: Run container with port mapping
- Task 17: Run container with volume
- Task 18: Create container without starting
- Task 19: Start created container
- Task 20: Kill a container
Dockerfile Tasks¶
- Task 21: Create basic Dockerfile
- Task 22: Build image from Dockerfile
- Task 23: Run built image
- Task 24: Create Dockerfile with COPY
- Task 25: Create Dockerfile with RUN
- Task 26: Docker commit - create image from container
- Task 27: Advanced docker cp - copy directories
- Task 28: Dockerfile with multiple RUN commands
- Task 29: Dockerfile with WORKDIR
- Task 30: Dockerfile with ENV variables
- Task 31: Dockerfile with EXPOSE
- Task 32: Custom ENTRYPOINT in Dockerfile
- Task 33: Dockerfile with USER (non-root)
- Task 34: Copy multiple files with COPY
- Task 35: Build a simple web server image
Docker Compose Tasks¶
- Task 36: Basic docker-compose.yml structure
- Task 37: Docker Compose with multiple services
- Task 38: Docker Compose with networks
- Task 39: Docker Compose with volumes
- Task 40: Docker Compose with environment variables
- Task 41: Docker Compose with build context
- Task 42: Docker Compose with depends_on
- Task 43: Docker Compose with health checks
- Task 44: Docker Compose scaling services
- Task 45: Docker Compose with logging
- Task 46: Docker Compose with environment files
- Task 47: Docker Compose overrides
- Task 48: Docker Compose with profiles
- Task 49: Docker Compose with secrets
- Task 50: Docker Compose with extensions
Docker CLI Tasks¶
Task 01: Run hello-world container¶
- Run the official hello-world container to verify Docker installation.
- This container will print a message and exit.
Task 02: List running containers¶
- Display all currently running containers.
Task 03: List all containers¶
- Display all containers, including stopped ones.
Task 04: Pull an image¶
- Download the alpine image from Docker Hub without running it.
Task 05: List images¶
- Display all Docker images available locally.
Task 06: Run container interactively¶
- Run an alpine container and open an interactive shell.
Task 07: Run container in background¶
- Run an nginx container in the background.
Task 08: List running containers again¶
- Verify the nginx container is running.
Task 09: Stop a container¶
- Stop the running nginx container.
Task 10: Remove a container¶
- Remove the stopped nginx container.
Task 11: Inspect a container¶
- Run a container and inspect its details.
Task 12: View container logs¶
- View the logs of a running container.
Task 13: Execute command in container¶
- Execute a command inside a running container.
Task 14: Copy file to container¶
- Copy a file from the host to a running container.
Task 15: Copy file from container¶
- Copy a file from a container back to the host.
Task 16: Run container with port mapping¶
- Run nginx with port mapping to access it from the host.
Task 17: Run container with volume¶
- Run a container with a volume mount.
Task 18: Create container without starting¶
- Create a container but don’t start it.
Task 19: Start created container¶
- Start the previously created container.
Task 20: Kill a container¶
- Forcefully kill a running container.
Dockerfile Tasks¶
Task 21: Create basic Dockerfile¶
- Create a simple Dockerfile that uses alpine as base and runs a basic command.
Task 22: Build image from Dockerfile¶
- Build an image from the Dockerfile created in Task 21.
Task 23: Run built image¶
- Run the image built in Task 22.
Task 24: Create Dockerfile with COPY¶
- Create a Dockerfile that copies a file and runs it.
Task 25: Create Dockerfile with RUN¶
- Create a Dockerfile that installs a package and runs a command.
Task 26: Docker commit - create image from container¶
- Modify a running container and create a new image from it using docker commit.
Solution
# Run a container and modify it
docker run -d --name modify-me alpine sleep 100
docker exec modify-me sh -c "echo 'Modified content' > /modified.txt"
# Commit the changes to a new image
docker commit modify-me my-modified-image
# Run a container from the new image to verify
docker run --rm my-modified-image cat /modified.txt
# Clean up
docker stop modify-me
docker rm modify-me
docker rmi my-modified-image
Task 27: Advanced docker cp - copy directories¶
- Copy entire directories between host and container using docker cp.
Solution
# Create a directory with files
mkdir test-dir
echo "file1 content" > test-dir/file1.txt
echo "file2 content" > test-dir/file2.txt
# Run a container
docker run -d --name copy-test alpine sleep 100
# Copy directory to container
docker cp test-dir copy-test:/tmp/
# Copy directory from container to host
docker cp copy-test:/tmp/test-dir copied-dir
# Verify
ls -la copied-dir/
# Clean up
docker stop copy-test
docker rm copy-test
rm -rf test-dir copied-dir
Task 28: Dockerfile with multiple RUN commands¶
- Create a Dockerfile with multiple RUN instructions to install packages and configure the image.
Solution
FROM alpine:latest
# Update package index
RUN apk update
# Install multiple packages
RUN apk add --no-cache \
curl \
wget \
git
# Create a directory
RUN mkdir -p /app
# Set permissions
RUN chmod 755 /app
CMD ["echo", "Multi-RUN Dockerfile completed"]
Build and run:
Task 29: Dockerfile with WORKDIR¶
- Use WORKDIR instruction to set the working directory for subsequent instructions.
Solution
FROM alpine:latest
# Set working directory
WORKDIR /app
# Create files in the working directory
RUN echo "Hello from /app" > hello.txt
# Copy files to working directory
COPY testfile.txt .
# Run commands in working directory
CMD ["cat", "hello.txt"]
Build and run:
Task 30: Dockerfile with ENV variables¶
- Use ENV instruction to set environment variables in the Dockerfile.
Solution
FROM alpine:latest
# Set environment variables
ENV APP_NAME="My Docker App" \
APP_VERSION="1.0.0" \
AUTHOR="Docker User"
# Use environment variables in RUN commands
RUN echo "Building $APP_NAME version $APP_VERSION by $AUTHOR" > /app/info.txt
# Use in CMD
CMD ["sh", "-c", "echo 'App: '$APP_NAME', Version: '$APP_VERSION', Author: '$AUTHOR && cat /app/info.txt"]
Build and run:
Task 31: Dockerfile with EXPOSE¶
- Use EXPOSE instruction to document which ports the container listens on.
Solution
FROM alpine:latest
# Install a simple HTTP server
RUN apk add --no-cache python3
# Create a simple HTML file
RUN echo "<html><body><h1>Hello from Docker!</h1></body></html>" > /index.html
# Expose port 8080
EXPOSE 8080
# Start a simple HTTP server
CMD ["python3", "-m", "http.server", "8080"]
Build and run:
Task 32: Custom ENTRYPOINT in Dockerfile¶
- Use ENTRYPOINT instruction to set the default executable for the container.
Solution
FROM alpine:latest
# Install curl
RUN apk add --no-cache curl
# Set entrypoint to curl
ENTRYPOINT ["curl"]
# Default arguments for curl
CMD ["--version"]
Build and run:
Task 33: Dockerfile with USER (non-root)¶
- Create a Dockerfile that runs as a non-root user for security.
Solution
FROM alpine:latest
# Create a non-root user
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
# Create app directory and set ownership
RUN mkdir /app && chown appuser:appgroup /app
# Switch to non-root user
USER appuser
# Set working directory
WORKDIR /app
# Create a file as non-root user
RUN echo "Running as $(whoami)" > user-info.txt
CMD ["cat", "user-info.txt"]
Build and run:
Task 34: Copy multiple files with COPY¶
- Use COPY instruction to copy multiple files and directories into the container.
Solution
FROM alpine:latest
# Create source files
RUN mkdir /source
RUN echo "config data" > /source/config.txt
RUN echo "script content" > /source/script.sh
# Copy multiple files
COPY /source/* /app/
# Copy entire directory
COPY /source /app/source/
# List copied files
CMD ["ls", "-la", "/app/"]
Build and run:
Task 35: Build a simple web server image¶
- Create a complete Dockerfile for a simple web server that serves static files.
Solution
FROM alpine:latest
# Install nginx
RUN apk add --no-cache nginx
# Create web directory
RUN mkdir -p /var/www/html
# Create a simple HTML page
RUN echo '<html><head><title>Docker Web Server</title></head><body><h1>Welcome to my Docker Web Server!</h1><p>This page is served from a custom Docker image.</p></body></html>' > /var/www/html/index.html
# Create nginx config
RUN echo 'server { listen 80; root /var/www/html; index index.html; location / { try_files $uri $uri/ =404; } }' > /etc/nginx/http.d/default.conf
# Expose port
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]
Build and run:
Docker Compose Tasks¶
Task 36: Basic docker-compose.yml structure¶
- Create a basic docker-compose.yml file with a single service.
Solution
Create docker-compose.yml:
Run and test:
Task 37: Docker Compose with multiple services¶
- Create a docker-compose.yml with multiple services that communicate with each other.
Solution
Create docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
api:
image: alpine:latest
command: ["sh", "-c", "while true; do echo 'API running'; sleep 30; done"]
Run and test:
Task 38: Docker Compose with networks¶
- Configure custom networks in docker-compose.yml for service isolation.
Solution
Create docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
networks:
- frontend
api:
image: alpine:latest
command: ["sh", "-c", "while true; do echo 'API running'; sleep 30; done"]
networks:
- backend
proxy:
image: nginx:latest
ports:
- "8081:80"
networks:
- frontend
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
Run and test:
Task 39: Docker Compose with volumes¶
- Use named volumes and bind mounts in docker-compose.yml.
Solution
Create docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- nginx-logs:/var/log/nginx
data:
image: alpine:latest
volumes:
- data-volume:/data
command: ["sh", "-c", "echo 'Data stored' > /data/file.txt && sleep 3600"]
volumes:
nginx-logs:
data-volume:
Run and test:
Task 40: Docker Compose with environment variables¶
- Configure environment variables in docker-compose.yml.
Solution
Create docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
environment:
- NGINX_PORT=80
- APP_ENV=production
app:
image: alpine:latest
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
command: ["sh", "-c", "echo 'DB: '$DATABASE_URL && echo 'Redis: '$REDIS_URL && sleep 30"]
Run and test:
Task 41: Docker Compose with build context¶
- Build custom images using docker-compose.yml.
Solution
Create Dockerfile:
Create docker-compose.yml:
Run and test:
Task 42: Docker Compose with depends_on¶
- Use depends_on to control service startup order.
Solution
Create docker-compose.yml:
version: '3.8'
services:
db:
image: postgres:13
environment:
- POSTGRES_PASSWORD=mypassword
- POSTGRES_DB=testdb
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
app:
image: alpine:latest
depends_on:
db:
condition: service_healthy
command: ["sh", "-c", "echo 'App started after DB is healthy' && sleep 30"]
Run and test:
Task 43: Docker Compose with health checks¶
- Configure health checks for services in docker-compose.yml.
Solution
Create docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
monitor:
image: alpine:latest
depends_on:
web:
condition: service_healthy
command: ["sh", "-c", "echo 'Web service is healthy!' && sleep 30"]
Run and test:
Task 44: Docker Compose scaling services¶
- Scale services up and down using docker-compose.
Solution
Create docker-compose.yml:
Run and test:
Task 45: Docker Compose with logging¶
- Configure logging options in docker-compose.yml.
Solution
Create docker-compose.yml:
version: '3.8'
services:
app:
image: alpine:latest
command: ["sh", "-c", "for i in $(seq 1 10); do echo 'Log message '$i; sleep 2; done"]
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
web:
image: nginx:latest
ports:
- "8080:80"
logging:
driver: syslog
options:
syslog-address: "tcp://localhost:514"
Run and test:
Task 46: Docker Compose with environment files¶
- Use .env files to manage environment variables.
Solution
Create .env file:
Create docker-compose.yml:
version: '3.8'
services:
app:
image: alpine:latest
environment:
- APP_NAME=${APP_NAME}
- APP_VERSION=${APP_VERSION}
- DATABASE_URL=${DATABASE_URL}
command: ["sh", "-c", "echo 'App: '$APP_NAME' v'$APP_VERSION && echo 'DB: '$DATABASE_URL && sleep 30"]
Run and test:
Task 47: Docker Compose overrides¶
- Use multiple compose files for different environments.
Solution
Create docker-compose.yml (base):
Create docker-compose.override.yml (development):
version: '3.8'
services:
web:
environment:
- ENV=development
volumes:
- ./dev-html:/usr/share/nginx/html
debug:
image: alpine:latest
command: ["sh", "-c", "while true; do echo 'Debug service running'; sleep 30; done"]
Run and test:
Task 48: Docker Compose with profiles¶
- Use profiles to enable/disable services.
Solution
Create docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
db:
image: postgres:13
environment:
- POSTGRES_PASSWORD=mypassword
profiles:
- database
cache:
image: redis:latest
profiles:
- cache
monitoring:
image: alpine:latest
command: ["sh", "-c", "while true; do echo 'Monitoring...'; sleep 30; done"]
profiles:
- monitoring
Run and test:
Task 49: Docker Compose with secrets¶
- Manage sensitive data using Docker secrets.
Solution
Create secret files:
Create docker-compose.yml:
version: '3.8'
services:
db:
image: postgres:13
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
app:
image: alpine:latest
secrets:
- app_secret
command: ["sh", "-c", "cat /run/secrets/app_secret && sleep 30"]
secrets:
db_password:
file: ./db_password.txt
app_secret:
file: ./app_secret.txt
Run and test:
Task 50: Docker Compose with extensions¶
- Use extensions (x-) for reusable configurations.
Solution
Create docker-compose.yml:
version: '3.8'
x-app-defaults: &app-defaults
image: alpine:latest
environment:
- LOG_LEVEL=info
restart: unless-stopped
x-db-defaults: &db-defaults
restart: unless-stopped
environment:
- POSTGRES_USER=app
- POSTGRES_DB=myapp
services:
web:
<<: *app-defaults
ports:
- "8080:80"
command: ["sh", "-c", "echo 'Web service with defaults' && sleep 3600"]
api:
<<: *app-defaults
ports:
- "8081:8081"
environment:
- LOG_LEVEL=debug
command: ["sh", "-c", "echo 'API service with custom log level' && sleep 3600"]
db:
<<: *db-defaults
image: postgres:13
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=mypassword
Run and test:
- After completing all tasks, clean up containers and images.
Clean Up Commands
