Docker for the Absolute Beginner - Hands On - DevOps
Docker Overview
Why do you need docker
- Compatibility / Dependency
- Long setup time
- Different Dev/test/prod environment
What can it do?
- Containerize application
- Run each service with its own dependencies in separate containers.
What are containers?
Containers are completely isolated environments, as in they can have their own processes or services their own network interfaces their own mounts just like virtual machines except they all share the same OS kernel.
Sharing the kernel
The main purpose of Docker is to package and containerized applications and to ship them and to run them anywhere any times as many times as you want
Containers vs virtual machines
- High Utilization on virtual machines
- Consume more space in virtual machines
- docker has less isolation as more resources are shared between the containers like the kernel.
Container vs image
- an image is a package or a template just like a VM template that you might have worked with in the virtualization world, it is used to create more containers.
- Containers are running instances of images that are isolated and have their own environments and set of processes.
Getting started
Docker Editions
- Community Edition - set of free docker product
- Enterprise edition - certified and supported container platform that comes with enterprise add-ons
Docker on windows
- Docker toolbox - contains a set of tools like oracle virtual box, docker engine, docker machine, docker compose, kitematic GUI.
- Docker desktop for windows - uses hyper-v
https://docs.docker.com/desktop/windows/install/
docker run nginx
docker run hello-world
docker run -it ubuntu bash
-it is short for --interactive + --tty. When you docker run with this command it takes you straight inside the container.
Basic Container Commands
// list containers
docker ps
docker ps -a // show current and previous running docker
// stop container
docker stop "name" / "container id"
// remove container
docker rm silly_sammet
// list images
docker images
// remove images
docker rmi nginx
// just download the image without running the image
docker pull nginx
//example
docker run --init kodekloud/simple-webapp // use --init so we can use ctrl-c
docker run -d kodekloud/simple-webapp // run in background mode
docker attach 05eb79b3dfe4a2b7a
Docker Commands
docker pull centos
docker run -it centos bash # goes to bash
docker run -d centos sleep 20 # run and shutdown after 20 second
docker stop {name_container} / {container_id} #force kill
docker rm {container_id} / {name_container}
docker images # list all images
docker rmi {name} # remove docker images
docker exec {container_id} cat /etc/*release*
go to https://hub.docker.com/search?q=&source=verified&type=image to check all images available
RUN - STDIN
Let's now look at inputs. I have a simple prompt application that when Run asks for my name. And on entering my name prints a welcome message. If I were to tokenize this application and run it as a Docker container like this, it wouldn't waitfor the prompt. It just prints whatever the application is supposed to print on standard out. That is because by default the Docker container does not listen to a standard input (non interactive).
but if you put -i (interative) before docker run it will wait for the input but still it will not prompt until we use -t (terminal)
RUN - port mapping
docker run kodekloud/webapp
# running on http://0.0.0.0:5000 -> it can be access from the docker but not outside docker
docker run -p 80:5000 kodekloud/webapp
# port 80 of localhost to port 5000
RUN - Volume mapping
#non persistent data
docker run mysql
docker stop mysql
docker rm mysql
# persistent data
docker run -v /opt/datadir:/var/lib/mysql
Inspect Container
docker inspect {container_name}
Container logs
docker logs {container_name}
Docker Commands Advanced Features
docker run ubuntu cat /etc/issue # check version ubuntu 22.04
docker run ubuntu:18.04 cat /etc/issue # using different version
docker run ubuntu sleep 15 # stay alive for 15 minutes
docker inspect {container_id}
Jenkins
docker run -p 8081:8080 -p 50000:50000 jenkins/jenkins # i use 8081 because mine conflicted
Demo Docker Image
docker run -it ubuntu bash
apt-get update
sudo apt install python3-pip
pip install flask
nano /opt/app.py
---
import os
from flask import Flask
app = Flask(__name__)
@app.route("/")
def main():
return "Welcome!"
@app.route('/how are you')
def hello():
return 'I am good, how about you?'
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
---
FLASK_APP=/opt/app.py flask run --host=0.0.0.0
docker build . -t simpleapp
docker tag simpleapp {docker_username}/simpleapp
docker push {docker_username}/simpleapp
Demo Environment Variables
docker run -e APP_COLOR=blue -p 38282:8080 --name blue-app kodekloud/simple-webapp
docker run -e MYSQL_ROOT_PASSWORD=db_pass123 --name mysql-db mysql
Command Vs Entrypoint
CMD ["nginx"] # this is a command from DockerFile
ENTRYPOINT ["/entropoint.sh"] # this is an entrypoint
Docker Compose
docker run mmumshad/simple-webapp
docker run mongodb
docker run redis:alpine
docker run ansible
#docker-compose.yml
services:
web:
image: "mmumshad/simple-webapp"
database:
image: "mongodb"
messaging:
image: "redis:alpine"
orchestration:
image: "ansible"
docker-compose up
Demo voting application
In this demo we are going to learn on putting it together but 1 by 1
git clone https://github.com/dockersamples/example-voting-app.git
#build voting app
cd vote
docker build . -t voting-app
#end build
docker run -p 8001:80 voting-app # if run well good now attach with redis
docker run -d --name=redis redis
docker run --name db -e POSTGRES_PASSWORD=postgres -d postgres:9.4
docker run -p 8001:80 -d --link redis:redis voting-app
docker run --link redis:redis --link db:db worker-app
#build result app on mac m1
cd result
docker build --platform linux/amd64 . -t result-app
docker run --platform linux/amd64 -p 5001:80 --link=db:db result-app
#end build
Docker compose
Docker compose for mac is different, use docker compose up
when linux uses docker-composer up
version: '2'
services:
redis:
image: redis
db:
image: postgres:9.4
vote:
image: voting-app
ports:
- 8001:80
worker:
image: worker-app
links:
- db
- redis
result:
image: result-app
ports:
- 5001:80
Docker Quiz
First create a redis database container called redis, image redis:alpine.
-> docker run --name redis -d redis:alpine
Next, create a simple container called clickcounter with the image kodekloud/click-counter, link it to the redis container that we created in the previous task and then expose it on the host port 8085
The clickcounter app run on port 5000.
-> docker run -p 8085:5000 --link redis:redis --name clickcounter -d kodekloud/click-counter
Create a docker-compose.yml file under the directory /root/clickcounter. Once done, run docker-compose up.
The compose file should have the exact specification as follows -
redis service specification - Image name should be redis:alpine.
clickcounter service specification - Image name should be kodekloud/click-counter, app is run on port 5000 and expose it on the host port 8085 in the compose file.
->
services:
redis:
image: redis:alpine
clickcounter:
image: kodekloud/click-counter
ports:
- 8085:5000
version: '3.0'
Docker Registry
Deploy Private registry
# tag image
docker image tag my-image localhost:5000/my-image
# push image
docker push localhost:5000/my-image
# pull image
docker pull localhost:5000/my-image
docker pull 192.168.56.100:5000/my-image
Docker quiz
Let practice deploying a registry server on our own.
Run a registry server with name equals to my-registry using registry:2 image with host port set to 5000, and restart policy set to always.
-> docker run -d -p 5000:5000 --restart=always --name my-registry registry:2
Now its time to push some images to our registry server. Let's push two images for now .i.e. nginx:latest and httpd:latest.
Note: Don't forget to pull them first.
To check the list of images pushed , use curl -X GET localhost:5000/v2/_catalog
->
Run: docker pull nginx:latest then docker image tag nginx:latest localhost:5000/nginx:latest and finally push it using docker push localhost:5000/nginx:latest.
We will use the same steps for the second image docker pull httpd:latest and then docker image tag httpd:latest localhost:5000/httpd:latest and finally push it using docker push localhost:5000/httpd:latest
docker image prune -a
-> remove all images without at least one container associated to them
Docker Engine
Cgroups
docker run --cpu=.5 ubuntu # limit 50% of cpu
docker run --memory=100m # limit 100mb for memory
docker run --platform linux/amd64 -it --rm -d -p 8888:8080 tomcat:8.0
docker exec 5b20fb4b1820 ps -eaf # to see the processes
Docker Storage
There are 2 types of mounts, volume mounting, and bind mounting. Volume mounting will mount from the volume directory and the bind mounting will mount from anywhere on the docker host.
Storage drivers
- AUFS
- ZFS
- BTRFS
- Device Mapper
- Overlay
- Overlay2
Docker storage demo
docker images
docker history {container_id} # to see how it builds
docker system df # to see how much it takes on the system
docker system df -v # to see how many shared usage for each builds
Docker quiz
Run a mysql container named mysql-db using the mysql image. Set database password to db_pass123
Note: Remember to run it in the detached mode.
-> docker run -d --name mysql-db -e MYSQL_ROOT_PASSWORD=db_pass123 mysql
Run a mysql container again, but this time map a volume to the container so that the data stored by the container is stored at /opt/data on the host.
Use the same name : mysql-db and same password: db_pass123 as before. Mysql stores data at /var/lib/mysql inside the container.
-> docker run -v /opt/data:/var/lib/mysql -d --name mysql-db -e MYSQL_ROOT_PASSWORD=db_pass123 mysql
Docker Networking
Bridge -> docker run ubuntu
None -> docker run ubuntu --network=none
Host -> docker run ubuntu --network=host
docker inspect {container_name}
Docker quiz
Explore the current setup and identify the number of networks that exist on this system.
-> docker network ls
-> docker inspect {container_name}
What is the subnet configured on bridge network?
-> docker network inspect bridge
Run a container named alpine-2 using the alpine image and attach it to the none network.
->docker run --name alpine-2 --network=none alpine
Create a new network named wp-mysql-network using the bridge driver. Allocate subnet 182.18.0.1/24. Configure Gateway 182.18.0.1
-> docker network create --driver bridge --subnet 182.18.0.1/24 --gateway 182.18.0.1 wp-mysql-network
-> docker network inspect wp-mysql-network
Deploy a mysql database using the mysql:5.6 image and name it mysql-db. Attach it to the newly created network wp-mysql-network
Set the database password to use db_pass123. The environment variable to set is MYSQL_ROOT_PASSWORD.
-> docker run -d -e MYSQL_ROOT_PASSWORD=db_pass123 --name mysql-db --network wp-mysql-network mysql:5.6
Deploy a web application named webapp using the kodekloud/simple-webapp-mysql image. Expose the port to 38080 on the host.
The application makes use of two environment variable:
1: DB_Host with the value mysql-db.
2: DB_Password with the value db_pass123.
Make sure to attach it to the newly created network called wp-mysql-network.
Also make sure to link the MySQL and the webapp container.
-> docker run --network=wp-mysql-network -e DB_Host=mysql-db -e DB_Password=db_pass123 -p 38080:8080 --name webapp --link mysql-db:mysql-db -d kodekloud/simple-webapp-mysql
Container Orchestration
docker service create --replicas=100 nodejs
docker swarm , kubernetes, mesos