Lab 1: The “Magical Data” Test (Named Volumes)
- Create Volume:
docker volume create magic-box - Start Container 1:
docker run -d --name writer -v magic-box:/data alpine sh -c "echo 'I was here' > /data/secret.txt && sleep 1000" - Check Data:
docker exec writer cat /data/secret.txt(Output: I was here) - Destroy Container 1:
docker rm -f writer - Start Container 2:
docker run -d --name reader -v magic-box:/app_data alpine sleep 1000(Notice we mounted it to a different path/app_datajust to prove we can!) - Verify:
docker exec reader cat /app_data/secret.txtResult: Output is I was here. The data survived!
Lab 2: The “Live Code” Test (Bind Mounts)
Best for Web Developers
- Create a folder:
mkdir my-site - Create file: Inside
my-site, createindex.htmlwith text<h1>Version 1</h1>. - Run Nginx:
docker run -d -p 8080:80 -v $(pwd)/my-site:/usr/share/nginx/html --name web nginx - Check Browser: Go to
localhost:8080. You see Version 1. - Modify File: Edit
index.htmlon your laptop to say<h1>Version 2 - Live Update!</h1>. Save it. - Refresh Browser: Instantly, you see Version 2. No rebuild required!
Lab 1: The “Disaster Recovery” (Backup & Restore)
Goal: Simulate a database corruption and restore from a backup file.
- Create Data & Populate:
docker volume create precious-datadocker run -d --name production-db -v precious-data:/data alpine sh -c "echo 'Important Financial Records' > /data/money.txt && sleep 1000"
- Perform Backup (The “Sidecar” Method):
- Run a temporary container that sees both the volume AND your current folder.
docker run --rm -v precious-data:/volume-data -v $(pwd):/backup alpine tar cvf /backup/snapshot.tar /volume-data- Check: You should see
snapshot.taron your laptop now.
- Simulate Disaster:
docker rm -f production-dbdocker volume rm precious-data(Data is GONE!)
- Restore:
docker volume create new-datadocker run --rm -v new-data:/volume-data -v $(pwd):/backup alpine tar xvf /backup/snapshot.tar -C /
- Verify:
docker run --rm -v new-data:/data alpine cat /data/money.txt- Result: “Important Financial Records” is back!
5: Extremely Detailed Labs for “DevSecOps Architect Level”
Lab 2: The “Manual Full Stack” (WordPress + MySQL)
Goal: Manually connect a frontend and backend. This is “The Hard Way.”
Create Network: docker network create my-blog-net
Start MySQL (The Backend):
docker run -d \
--name mysql-db \
--network my-blog-net \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=somewordpress \
-e MYSQL_DATABASE=wordpress \
mysql:5.7Start WordPress (The Frontend):
docker run -d \
--name my-blog \
--network my-blog-net \
-p 8080:80 \
-e WORDPRESS_DB_HOST=mysql-db \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=somewordpress \
wordpress- Note: See
-e WORDPRESS_DB_HOST=mysql-db? We are using the container name as the address!
Test: Open http://localhost:8080. You will see the WordPress installation screen.
Cleanup (Painful, isn’t it?): docker rm -f my-blog mysql-db docker network rm my-blog-net docker volume rm mysql-data
Takeaway: Running these huge commands every time is annoying. This is why we use Docker Compose (next topic).
Lab 3: The “Security Lockdown” (Read-Only Containers)
Goal: Prevent a hacked application from modifying configuration files.
- Create a Dummy Config:
echo "admin_access=true" > config.txt - Run in Read-Only Mode:
docker run -d --name secure-app -v $(pwd)/config.txt:/app/config.txt:ro alpine sleep 1000- Note the
:roat the end of the volume path.
- Note the
- Attempt Hack (Try to change config):
docker exec -it secure-app sh -c "echo 'admin_access=FALSE' > /app/config.txt" - Result:
sh: can't create /app/config.txt: Read-only file system- Success: The container prevented the modification.