Docker Hands On Experience

You might also like

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 32

docker tutorial ninad

What is docker
--------------

Build - image
Ship - registry
Run - container

Docker tasks for Hands-on

Manual installation of docker


==============================

AWS image id for CentOS7 in N.Virginia


-----------------------------------------
CentOS Linux 7 x86_64 HVM EBS 1704_01-b7ee8a69-ee97-4a49-9e68-afaee216db2e-ami-
d52f5bc3.4 (ami-46c1b650)

Installing docker community edition on CentOS


---------------------------------------------
https://store.docker.com/editions/community/docker-ce-server-centos?tab=description

Prerequisites:
Docker CE is supported on CentOS 7.3 64-bit.

Installation steps:
1. Set up the repository. Set up the Docker CE repository on CentOS:

sudo yum install -y yum-utils


sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum makecache fast

2. Get Docker CE - Install the latest version of Docker CE on CentOS:

sudo yum -y install docker-ce

3. Start Docker:

sudo systemctl start docker

4. Test your Docker CE installation

sudo docker run hello-world

5. Give privileges to login user to run docker without sudo


By default, running the docker command requires root privileges � that is, you have
to prefix the command with sudo. It can also be run by a user in the docker group
sudo usermod -aG docker your-user-name

6. Logout and login again for this to take effect

$ docker ps -a

Steps only (Script)


-------------------
#!/bin/bash

sudo yum install -y yum-utils


sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum makecache fast
sudo yum -y install docker-ce
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $(whoami)

Check docker version


--------------------
docker version

Commands to manage docker images [download, save, upload, label]


================================================================

Search image
------------
Search images on repository [Docker Hub by default]

docker search http

List image
-----------
List images in local repository on your host

docker images

Download image
--------------
You can also download image with a specific tag [usually tag refers to a version].
If no tag is specified, image with tage 'latest' is downloaded
e.g.
docker pull ubuntu
is same as
docker pull ubuntu:latest
docker pull ubuntu:14.04
docker pull ubuntu:16.04

It is not mandatory to download image before launching a container. If you launch a


container for which image is not available in the local repository, then docker
downloads it automatically as part of launching the instance

Few things to know before you launch a container


------------------------------------------------
docker logs <container-name or id> - to see the logs [like console output]
docker inspect <container-name or id> - to see the configuration information

If you have python installed you can pretty print the output
docker inspect --format='' container2 | python -m json.tool

You can also filter out any specific configuration information you are looking for
docker inspect --format='{{json .NetworkSettings.Networks}}' container_name
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
container_name

List containers
---------------
List running containers
docker ps

List all containers, even those which have exited


docker ps -a

Launch your first container and access application running on it


----------------------------------------------------------------

Few important things about containers


1. Container is launched in order to run a command (could be a simple command, an
application or a service)
2. This process that your container runs gets a PID of 1 [Top most PID] inside a
container
3. Your container only runs as long as the command you specify for docker run
continues to run. If the process exits (i.e. the process with PID 1 completes or
terminates due to some reason), the container also exits.

Command to launch a container


-----------------------------
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

If image is available in local repository [as a result of any previous pull /


download] then it will be used, else the image will be downloaded from docker
registry

docker run -t => assign pseudo tty


-i => interactive [connect to stdin on container]
-d => detached mode

You can use multiple of the above options same time e.g.
docker run -it <image-name> - If you want to launch container in interactive mode
(and connect to STDIN) with TTY assigned [e.g. You need to assign TTY if you are
running say bash shell on the container]
docker run -d <image-name> - If you want to run the container in detached mode
[in background]

#Launch container as foreground undetachable process [this is the default mode]


docker run httpd
docker inspect <container-id> | grep IPAddress
curl container_ip

Press Ctrl+C for the httpd container to


docker ps -a

#Launch a container in foreground mode, but this time in interactive mode and with
a tty assigned
docker run -it ubuntu
docker run -it httpd

To exit, you can type the key combination amongst Ctrl+C , Ctrl+D or type exit - It
depends on which image you are using
When you exit from the container, the container also exits/stops
If you want to detach instead of exiting from the container, then type the key
combination of Ctrl+P+Q

If you want to connect back to the container, you have two options
1. docker attach <container-name>
Using this method, for some containers like those launched from httpd image mainly
receive a signal which exits the container, however it works fine for containers
launched from say ubuntu, centos image

2. docker exec -it <container-name> bash


This will run bash command inside the container and because of -it you will be
interactively connected to the container
Even after exiting from a container in such a way does not cause the container to
stop/exit, because the process running as PID 1 is still running [which was httpd
or /bin/bash for httpd or ubuntu respectively, the bash we ran was additional
process we ran inside the container and it did not have PID 1]

Now connect to the container again and kill the process with PID 1
docker exec -it <container-name> bash
ps -eaf
kill 1

You will get disconnected from the container. Also you will see that the container
has Exited
docker ps -a

#Launch container as daemon (background process and not connecting to container)


docker run -d httpd

#Check container's IP address and access web page using that locally from the host
docker inspect <container-id> | grep IPAddress
curl <container-ip>

You can execute a command inside a running docker container using docker exec e.g.
docker exec <container-name> pwd
docker exec -it <container-name> top
docker exec -it <container-name> "/bin/ps" "-eaf"

#Run workload and stop the container after finishing the job
docker run ubuntu echo "My first container run"
docker ps -a

docker run ubuntu /bin/bash -c 'for i in 1 2 3 4; do echo Running for $i times;


sleep 1; done'
docker ps -a

So far we have accessed the container from within the Docker Host. If you want to
access the container from outside the Docker Host, you need to map Host ports to
container ports

-P => map a random high-numbered port on host to the exposed port on container
-p => map a specific port on host to a particular port on container

#Launch container with automatic port mapping to host


docker run -d -P httpd

#Check port on host mapped to container's port and access web page from outside
[web browser] using host's port
docker ps -a
From web browser do http://host-ip:port

#Launch container with specific port mapping


docker run -d -p 8080:80 -d httpd
curl http://host-ip:8080

So far the container we launched was getting a random name. If we want to give a
specific name we can use --name
#Launch container with specific name
docker run -d -p 8081:80 --name myhttp1 -d httpd
curl http://host-ip:8081
Run a different command on the container than the one built-in to the image
---------------------------------------------------------------------------
#Notes: Typically, when an image is created, it is created to run some command
(e.g. httpd image runs httpd-foreground)
If we use docker run without mentioning any command to be run on launch - the
command defined at the time of image creation is run.
However if you want, you can specify a different command to be run at the time of
container launch. If done so, this will be the command that will run in the
container instead of the one defined in the image

You can inspect the image to check what command has been defined to run at
container launch by default
docker inspect <image-name>

docker run --name vm1 -it httpd bash


To detach from the container type the key combination of Ctrl+P+Q

Stop/Start container
----------------------
docker stop container_name
docker start container_name

Delete container
----------------
To delete a container you need to first stop the container
docker stop container_name
docker rm container_name

or you can force remove


docker rm -f container_name

Download image file to local


----------------------------
docker save image_name > local_file_name
docker save alpine > alpine.tar

docker save -o image.tar image


docker save -o apline.tar alpine

Upload image from local file


------------------------------
docker load < ./local_file_name

Remove the previously downloaded image and then load it from the saved file in
above step
docker rmi alpine
docker load < ./apline.tar

Create custom image from container


----------------------------------
1. Create docker container from some base image
docker run -it --name ubuntu bash
2. Update the image with customisation you need
apt-get update -y
apt-get install -y apache2

3. Create image from the container


docker stop vm1
docker commit vm1 myhttp:v0.1
docker images

Tag image
---------
docker images
docker tag myhttp:v0.1 myhttp:latest
docker images

Delete image
------------
docker images
docker rmi image_id
or
docker rmi repository_name:tag_name

Create custom image using Dockerfile


------------------------------------

FROM = Mandatory as first instruction in a Docker file. Instructs Docker to pull


the base image from which you are building the new image. Use a tag to specify the
exact image from which you are building
MAINTAINER = Author of the build image
RUN = This instruction can be used on multiple lines and runs any commands after
Docker image has been created.
CMD = Run any command when Docker image is started. Use only one CMD instruction in
a Dockerfile.

ENTRYPOINT = Same as CMD but used as the main command for the image.
EXPOSE = Instructs the container to listen on network ports when running. The
container ports are not reachable from the host by default.
ENV = Set container environment variables.
ADD = Copy resources (files, directories or files from URLs).
COPY = Copy resources (files, directories).

ADD allows <src> to be an URL


If the <src> parameter of ADD is an archive in a recognised compression format, it
will be unpacked

Each RUN will execute the command on the top writable layer and perform a commit to
image. Thus the layers keep increasing as you execute multiple RUN commands.
You can aggregate multiple RUN instructions using "&&", which will execute all run
commands on the same layer and only after all executions it will perform a commit
to image

Check history of commands run to create image. Also shows size of each layer
docker history <image-name|image-id>
docker history --no-trunc <image-name|image-id>

Sample image creation 1


------------------------
#Using CMD

mkdir demoapp1
cd demoapp1

#Create file named Dockerfile with following contents


FROM centos
MAINTAINER Ninad Date
CMD ["ping" , "-c" , "5" , "127.0.0.1"]

-----------------------------------------------------------------------------------
-----------------------
sample code for creating image using docker file
#Pull base image
FROM ubuntu
#Install Apache
RUN apt-get update -y && apt-get install apache2 apache2-utils -y#Define
default port
EXPOSE 80
ENTRYPOINT [ "/usr/sbin/apa-che2ctl" ]#Define default command
CMD [ "-D", "FOREGROUND" ]
-----------------------------------------------------------------------------------
----------------------------

#Create docker image from Dockerfile in current location [hence the .]


docker build -t demoapp1:v1.0 .

#Run container
docker run demoapp1:v1.0

#Run container to execute a different command


docker run demoapp1:v1.0 echo "I can run a different command as well"

#Delete container after command execution


You will notice that previous runs of docker container have left behind the
container. If we do not want the container to exist after execution, we can delete
it on execution completion
Check previous containers
docker ps -a

Delete those to cleanup


docker rm <container-id> <container-id> ...

Launch a container with --rm which will delete the container when it exits
docker run --rm demoapp1:v1.0
Sample image creation 2
------------------------
#Using ENTRYPOINT

mkdir demoapp2
cd demoapp2

#Create file named Dockerfile with following contents


FROM centos
MAINTAINER Ninad Date
ENTRYPOINT ["ping"]

#Create docker image from Dockerfile in current location [hence the .]


docker build -t demoapp2:v1.0 .

#Run container
docker run --rm demoapp2:v1.0

#Run container to execute a different command


docker run --rm demoapp2:v1.0 echo "I cant run any other command"

Sample image creation 3


------------------------
#Use ENTRYPOINT and CMD

mkdir demoapp3
cd demoapp3

#Create file named Dockerfile with following contents


FROM centos
MAINTAINER Ninad Date
ENTRYPOINT ["ping"]
CMD ["-c" , "5" , "127.0.0.1"]

#Create docker image from Dockerfile in current location [hence the .]


docker build -t demoapp3:v1.0 .

#Run container
docker run --rm demoapp3:v1.0

#Run container to execute a different command


docker run --rm demoapp3:v1.0 -c 5 google.com

Sample image creation 4


------------------------
#Create a web server application

mkdir demoapp4
cd demoapp4
echo '<html><body><h1>Welcome to my first demo app on docker at ACTCoE Training!!!
</h1></body></html>' > index.html

#Create file named Dockerfile with following contents


FROM centos
MAINTAINER Ninad Date
ENV PATH /usr/sbin:$PATH
RUN yum -y update \
&& yum install -y httpd
COPY index.html /var/www/html/index.html
EXPOSE 80
CMD ["httpd" , "-D" , "FOREGROUND"]

#Create docker image from Dockerfile in current location [hence the .]


docker build -t demoapp4:v1.0 .

docker run -d -P demoapp4:v1.0

Sample image creation 5


------------------------
#Create web server using ngnix

FROM ubuntu:14.04
RUN apt-get update -y \
&& apt-get install -y nginx
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

Push your image to Docker Hub


------------------------------
Create a login on https://hub.docker.com/

Tag your image to refer to your repository name on Docker Hub


e.g.
docker image tag demoapp3:v1.0 nindate/nin-contactsapp:demoapp3-v1.0

Login to Docker Hub repository


docker login

Push your image


docker push nindate/nin-contactsapp:demoapp3-v1.0

Goto Docker Hub dashboard and check your repository

Volumes
-------
## Data Volumes
#Create container to have a blank volume created and made available inside the
container
docker run -d -P --name myhttp1 -v /var/www/html httpd

Check volume used by the container


docker inspect myhttp1

Check volumes and see the volume used by container appears in the list of volumes
docker volume ls

Check the directory /var/www/html, create an index.html file

Data is written directly to the volumes and does not follow / use the overlay
filesystem.
Contents inside the volume are not saved into the container or if you create image
from it
docker stop myhttp1
docker commit myhttp1 testimg:v0.1

docker run -ti --name testmyhttp1 testimg:v0.1 bash


Check contents of /var/www/html

Delete the containers


docker stop myhttp1 testmyhttp1
docker rm myhttp1 testmyhttp1

Volume will still exists even after container is deleted


docker volume ls

If you attach that same volume to a new container, we can see that the data in the
volume is intact (persists)
docker run -ti --name newmyhttp1 -v volume-name-from-list:/var/www/html httpd bash
Check contents of /var/www/html

#Create container and map local host directory as volume available inside the
container
mkdir -p /var/docker-volumes/vol1
docker run -d -P -v /var/docker-volumes/vol1:/vol1 httpd

#Create a volume and define this precreated volume to be used inside container
docker volume ls
docker volume create --name myvol1
docker run -d -P --name myhttp2 -v myvol1:/var/www/html httpd

#Mount a host file as a data volume


docker run --rm -it -v ~/.bash_history:/root/.bash_history ubuntu /bin/bash

## Data Volume container


#Create container to have a blank volume created and made available inside the
container
docker run -d -P --name myhttp3 -v /var/www/html httpd
#Login to container and create an index.html file in the volume
docker exec -it myhttp3 bash
echo "Welcome to Docker training" > /var/www/html/index.html

#Create another container to use volume from existing container volume


docker run -d -P --volumes-from myhttp3 --name myhttp4 httpd

#Check if index.html file exists on the volume mounted from existing container
ls /var/www/html

docker networks
----------------

Default networks:
default bridge
none
host

#Check networks using


docker ls

user defined networks - types:


bridge - containers connecting to same bridge and requiring to talk to each other,
need to be on same host
overlay - for multiple hosts
MACVLAN

#Create user defined bridge network


docker network ls
docker network create --driver=bridge --subnet 172.25.0.0/26 mynet1
docker network ls
docker network inspect mynet1

#Launch a container on user defined network


docker run --network=mynet1 -d -P --name mynewhttp1 httpd

Link containers
----------------
https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/
#updating-the-etchosts-file

--link

limited to same docker host

Docker also exposes each Docker originated environment variable from the source
container as an environment variable in the target. For each variable Docker
creates an <alias>_ENV_<name> variable in the target container. The variable�s
value is set to the value Docker used when it started the source container.

These include variables from:


- the ENV commands in the source container�s Dockerfile
- the -e, --env and --env-file options on the docker run command when the source
container is started

all environment variables originating from Docker within a container are made
available to any container that links to it. This could have serious security
implications if sensitive data is stored in them.

In addition to the environment variables, Docker adds a host entry for the source
container to the /etc/hosts

Note: You can link multiple recipient containers to a single source. For example,
you could have multiple (differently named) web containers attached to your db
container.

If you restart the source container, the linked containers /etc/hosts files will be
automatically updated with the source container�s new IP address, allowing linked
communication to continue.

https://docs.docker.com/engine/userguide/networking/work-with-networks/#network-
alias-scoping-example

Run the following commands to run one database and one web server container. Using
--link we can link the database container into the web server container.
docker run -d --name mydb1 -e MYSQL_DATABASE=testdb -e MYSQL_USER=dbadmin -e
MYSQL_PASSWORD=dbpassword -e MYSQL_ROOT_PASSWORD=rootpass mysql
docker run -d --name myhttp1 --link mydb1:dbsrv httpd

docker exec -it myhttp1 bash


Try to reach the database server with its name or alias
ping dbsrv
ping mydb1

Check the environment variables


env

Now you can connect from the web server to database server using name of database
server
apt-get update -y
apt-get install -y mysql-client

mysql -h dbsrv -u $DBSRV_ENV_MYSQL_USER -p$DBSRV_ENV_MYSQL_PASSWORD

ENVIRONMENT VARIABLE
---------------------
--env, -e Set environment variables
--env-file Read in a file of environment variables

docker run -it -e "colour=purple" ubuntu /bin/bash


echo $colour

LIMIT CONTAINER RESOURCES


--------------------------

By default, a container has no resource constraints and can use as much of a given
resource as the host�s kernel scheduler will allow. Docker provides ways to control
how much memory, CPU, or block IO a container can use, setting runtime
configuration flags of the docker run command.

-m or --memory=<value> b,k,m,g
--cpus=<value>
--device-read-bps
--device-read-iops
--device-write-bps
--device-write-iops

docker run -it --cpus=1 -m 300M ubuntu:14.04 /bin/bash

Check the limits and usage of the containers using


docker stats

Additional options for docker run command


-----------------------------------------

Use --entrypoint to override the default ENTRYPOINT set in image

docker run -it --entrypoint="" mysql bash

Use --expose=[] to expose a port or a range of ports inside the container, in


additional to those exposed by the `EXPOSE` instruction when image was built using
Dockerfile

docker run -d --expose=22 httpd


docker ps

Docker machine
--------------

https://github.com/docker/machine/releases/

curl -L https://github.com/docker/machine/releases/download/v0.9.0-rc2/docker-
machine-`uname -s`-`uname -m` >/tmp/docker-machine &&
chmod +x /tmp/docker-machine &&
sudo cp /tmp/docker-machine /usr/local/bin/docker-machine

Docker machine install


-----------------------
curl -L https://github.com/docker/machine/releases/download/v0.10.0/docker-machine-
`uname -s`-`uname -m` >/tmp/docker-machine && chmod +x /tmp/docker-machine

sudo cp /tmp/docker-machine /usr/local/bin/docker-machine

docker-machine version
docker-machine version 0.10.0, build 76ed2a6

Create docker host on AWS


-------------------------

$ docker-machine create --driver amazonec2 --amazonec2-access-key


AKIAI2C5OP6LO4ZI4D6Q --amazonec2-secret-key
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 --amazonec2-root-size 8 aws-sandbox

Creating CA: /home/ninaddatecloud/.docker/machine/certs/ca.pem


Creating client certificate: /home/ninaddatecloud/.docker/machine/certs/cert.pem
Running pre-create checks...
Creating machine...
(aws-sandbox) Launching instance...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with ubuntu(systemd)...
Installing Docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this
virtual machine, run: docker-machine env aws-sandbox

$ docker-machine env aws-sandbox


export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://54.152.113.174:2376"
export DOCKER_CERT_PATH="/home/ninaddatecloud/.docker/machine/machines/aws-sandbox"
export DOCKER_MACHINE_NAME="aws-sandbox"
# Run this command to configure your shell:
# eval $(docker-machine env aws-sandbox)

$ eval $(docker-machine env aws-sandbox)

Launch a container
-------------------
$ docker run -d -p 8000:80 --name webserver kitematic/hello-world-nginx
Unable to find image 'kitematic/hello-world-nginx:latest' locally
latest: Pulling from kitematic/hello-world-nginx
77c6c00e8b61: Pull complete
9b55a9cb10b3: Pull complete
e6cdd97ba74d: Pull complete
7fecf1e9de6b: Pull complete
6b75f22d7bea: Pull complete
e8e00fb8479f: Pull complete
69fad424364c: Pull complete
b3ba6e76b671: Pull complete
a956773dd508: Pull complete
26d2b0603932: Pull complete
3cdbb221209e: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:ec0ca6dcb034916784c988b4f2432716e2e92b995ac606e080c7a54b52b87066
Status: Downloaded newer image for kitematic/hello-world-nginx:latest
6e35823e4d5e4e03a9d607a409671f3cc0536570b7d9af63b2cd9aedd6f1d805

Launch containers to deploy "Contacts" application


---------------------------------------------------
$ docker run -d --name mydb2 -e MYSQL_DATABASE=test2 -e MYSQL_USER=user1 -e
MYSQL_PASSWORD=password -e MYSQL_ROOT_PASSWORD=rootpass nindate/nin-contactsapp:db
Unable to find image 'nindate/nin-contactsapp:db' locally
db: Pulling from nindate/nin-contactsapp
cd0a524342ef: Pull complete
d9c95f06c17e: Pull complete
46b2d578f59a: Pull complete
10fbc2bcc6e9: Pull complete
91b1a29c3956: Pull complete
5bf9316bd602: Pull complete
69bd23f08b55: Pull complete
4fb778132e94: Pull complete
6913628d7744: Pull complete
a477f36dc2e0: Pull complete
c954124ae935: Pull complete
f43e9337ddc4: Pull complete
caf47b0634fa: Pull complete
0987495f986b: Pull complete
Digest: sha256:32772067f030c7f715af1c07799f4acd7ed383ec0feace1ca6533fb1264b2ba4
Status: Downloaded newer image for nindate/nin-contactsapp:db
311758b4aa188c7ac136cb1e30753766075433d2e1ac5142397ca46af03cda4c

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
311758b4aa18 nindate/nin-contactsapp:db "/entrypoint.sh mysql" 7
seconds ago Up 6 seconds 3306/tcp mydb2
6e35823e4d5e kitematic/hello-world-nginx "sh /start.sh" 11
minutes ago Up 11 minutes 0.0.0.0:8000->80/tcp webserver

$ docker run -d --name myhttp2 --volumes-from mydb2 -P nindate/nin-contactsapp:web


Unable to find image 'nindate/nin-contactsapp:web' locally
web: Pulling from nindate/nin-contactsapp
5d890c53be21: Pull complete
f775b856e199: Pull complete
552c4f407d99: Pull complete
fda304b96f8a: Pull complete
2b033adb904a: Pull complete
0b7e160b6fa2: Pull complete
e000ec1f6ff8: Pull complete
a6f356092598: Pull complete
5e250a8a403a: Pull complete
29ce67ed723f: Pull complete
Digest: sha256:10841fc81ffd407f1845e895ba84d7d66c15510c7586af2fb6647aecc7b356dc
Status: Downloaded newer image for nindate/nin-contactsapp:web
7c5b667a4bce075fbdecd8845a16f0229aa564d85b1230685a94f20e07fa4779

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
7c5b667a4bce nindate/nin-contactsapp:web "/root/entrypoint.sh" 6
seconds ago Up 5 seconds 0.0.0.0:32768->80/tcp myhttp2
311758b4aa18 nindate/nin-contactsapp:db "/entrypoint.sh mysql" 49
seconds ago Up 48 seconds 3306/tcp mydb2
6e35823e4d5e kitematic/hello-world-nginx "sh /start.sh" 12
minutes ago Up 12 minutes 0.0.0.0:8000->80/tcp webserver

Access the application


http://host-name:32768/contactsmenu.php

Stop the docker host (Stop the AWS instance running as docker host)
--------------------------------------------------------------------
$ docker-machine stop aws-sandbox
Stopping "aws-sandbox"...
Machine "aws-sandbox" was stopped.

Remove the docker host (Terminate the AWS instance running as docker host)
--------------------------------------------------------------------------
$ docker-machine rm aws-sandbox
About to remove aws-sandbox
WARNING: This action will delete both local reference and remote instance.
Are you sure? (y/n): y
Successfully removed aws-sandbox

Docker compose
---------------
Instead of running docker run commands - you can define all the details into a yaml
file and use docker compose which can read from this yaml file and create the
containers, with defined params, volumes, links etc

sudo su
yum install -y python-pip
pip install --upgrade pip
pip install docker-compose

Running a sample application using docker-compose


--------------------------------------------------
Create a directory and in that create a file docker-compose.yml
version: '2'
services:
mydb2:
image: "mysql"
environment:
- MYSQL_DATABASE=test2
- MYSQL_USER=user1
- MYSQL_PASSWORD=password
- MYSQL_ROOT_PASSWORD=rootpass
myhttp2:
image: "httpd"
links:
- mydb2:dbsrv
ports:
- "8081:80"

docker-compose up -d
docker ps -a
docker exec -it <container-name-of-web> bash
ping dbsrv

docker-compose down

$ cat docker-compose.yml
version: '2'
services:
mydb2:
image: "nindate/nin-contactsapp:db"
environment:
- MYSQL_DATABASE=test2
- MYSQL_USER=user1
- MYSQL_PASSWORD=password
- MYSQL_ROOT_PASSWORD=rootpass
volumes:
- mydb2-data-vol:/var/lib/mysql
- mydb2-info:/info
myhttp2:
image: "nindate/nin-contactsapp:web"
volumes:
- mydb2-info:/info
links:
- mydb2:dbsrv
ports:
- "8081:80"
volumes:
mydb2-data-vol:
mydb2-info:

docker-compose up -d
docker-compose down

OPTIONAL Hands-on tasks for Swarm


---------------------------------
Docker swarm
-------------

Setup 3 nodes with docker-ce [on CentOS7 for this demo]


-------------------------------------------------------
#!/bin/bash

sudo yum install -y yum-utils

sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

sudo yum makecache fast

sudo yum -y install docker-ce

sudo systemctl start docker

sudo usermod -aG docker $(whoami)

sudo usermod -aG docker ninaddatecloud

Firewall ports required for swarm


----------------------------------
TCP port 2377 for cluster management communications
TCP and UDP port 7946 for communication among nodes
UDP port 4789 for overlay network traffic

Routing mesh:
Port 7946 TCP/UDP for container network discovery.
Port 4789 UDP for the container ingress network.

Create swarm and setup one node as swarm manager


-------------------------------------------------
docker swarm init --advertise-addr <ip-of-manager-node>

Save the output you get like below

warm initialized: current node (fqt4ok51tufnqdcnt8chotd6z) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join \


--token SWMTKN-1-1tyx8tkxhgcd91egc1uh5xheys0v0odfceivdppd4qufxhvruv-
6lcg96zpg825pdd2q91k3swtl \
10.128.0.4:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow
the instructions.
Run docker info
+++++++++++++++
[ninaddatecloud@test-docker1 ~]$ docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 17.03.1-ce
Storage Driver: overlay
Backing Filesystem: xfs
Supports d_type: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Swarm: active
NodeID: fqt4ok51tufnqdcnt8chotd6z
Is Manager: true
ClusterID: 2hjgkcniqujrhw8jl5q4hr7i4
Managers: 1
Nodes: 1
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1
Election Tick: 3
Dispatcher:
Heartbeat Period: 5 seconds
CA Configuration:
Expiry Duration: 3 months
Node Address: 10.128.0.4
Manager Addresses:
10.128.0.4:2377
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 4ab9917febca54791c5f071a9d1f404867857fcc
runc version: 54296cf40ad8143b62dbcaa1d90e520a2136ddfe
init version: 949e6fa
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-514.16.1.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 3.613 GiB
Name: test-docker1
ID: B4LW:NG45:PED5:QBVU:7VS6:3B54:PACC:CSXJ:7LFI:PPPV:ORNI:P77O
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false

Run docker node ls


+++++++++++++++++++
[ninaddatecloud@test-docker1 ~]$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
fqt4ok51tufnqdcnt8chotd6z * test-docker1 Ready Active Leader

Add other nodes to swarm


-------------------------

You can add more nodes to the swarm. You can add a node as a worker node or as a
manager node.

On the 1st manager node run below command to get the token to be used when joined
additional node as manager node
docker swarm join-token manager

On the 1st manager node run below command to get the token to be used when joined
additional node as worker node
docker swarm join-token worker

Run the output of above command on additional nodes to be added to swarm cluster
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
docker swarm join \
--token SWMTKN-1-1tyx8tkxhgcd91egc1uh5xheys0v0odfceivdppd4qufxhvruv-
6lcg96zpg825pdd2q91k3swtl \
10.128.0.4:2377

swarm node: Docker Hosts in swarm mode


- manager
- worker

service: application you launch in swarm which launches and maintains the necessary
number of containers (these are called tasks), and manages load balancing and
connectivity to the containers (tasks)
- tasks: individual containers

service
- global
- replicated
stack: you can launch a set of containers

you can have HA by having multiple manager nodes. These manager nodes should be in
odd numbers 3, 5, 7, ...

Run docker service


-------------------
We will create custom image and will use it for our example

Create custom image for this example


+++++++++++++++++++++++++++++++++++++
mkdir test-http
cd test-http

[ninaddatecloud@test-docker1 test-http]$ cat Dockerfile


FROM httpd
MAINTAINER NINAD DATE
COPY entrypoint.sh /usr/local/apache2/scripts/entrypoint.sh

EXPOSE 80

CMD ["/usr/local/apache2/scripts/entrypoint.sh"]

[ninaddatecloud@test-docker1 test-http]$ cat entrypoint.sh


#!/bin/bash

echo "Welcome to your web page served from container $(hostname)" >
/usr/local/apache2/htdocs/index.html
echo "This is version 1.0 of the application"

/usr/local/apache2/bin/apachectl -D FOREGROUND

chmod u+x entrypoint.sh

docker build -t nindate/nin-contactsapp:test-http-v1.0 .

docker push nindate/nin-contactsapp:test-http-v1.0

Swarm mode has two types of services, replicated and global. For replicated
services, you specify the number of replica tasks for the swarm manager to schedule
onto available nodes. For global services, the scheduler places one task on each
available node.

You control the type of service using the --mode flag. If you don�t specify a mode,
the service defaults to replicated. For replicated services, you specify the number
of replica tasks you want to start using the --replicas flag.

$ docker service create \


--name my_web \
--replicas 3 \
nginx

$ docker service create \


--name myservice \
--mode global \
alpine top

Create global service


+++++++++++++++++++++

docker service create \


--name myservice \
--mode global \
httpd

docker service create --mode global --name my-global-http -p 8001:80 nindate/nin-


contactsapp:test-http-v1.0

docker service inspect --pretty my-global-http

docker service ls

docker service ps my-global-http

Create replicated service


++++++++++++++++++++++++++

docker service create --replicas 2 --name my-test-http -p 8000:80 nindate/nin-


contactsapp:test-http-v1.0

docker service inspect --pretty my-test-http

docker service ls
ID NAME MODE REPLICAS IMAGE
bkkygd2ngq2f my-test-http replicated 2/2 nindate/nin-contactsapp:test-http

docker service ps my-test-http


ID NAME IMAGE NODE
DESIRED STATE CURRENT STATE ERROR PORTS
9xtt6d10jp1y my-test-http.1 nindate/nin-contactsapp:test-http test-docker1
Running Running 6 minutes ago
imvog2s0v5wj my-test-http.2 nindate/nin-contactsapp:test-http test-docker3
Running Running 6 minutes ago

Scale the application


---------------------

From another terminal run, check IP address for eth0 and run below to continuously
check website
while true; do curl 10.128.0.6:8000; sleep 1; done
docker service scale my-test-http=5
docker service ps my-test-http

Rolling update
--------------

Delete the previous service


docker service rm my-test-http

docker service create \


-p 8000:80 \
--replicas 3 \
--name my-test-http \
--update-delay 10s \
nindate/nin-contactsapp:test-http-v1.0

Build image for version 2 of the application


++++++++++++++++++++++++++++++++++++++++++++
cat entrypoint.sh
#!/bin/bash

echo "Welcome to your web page served from container $(hostname)" >
/usr/local/apache2/htdocs/index.html
echo "This is version 2.0 of the application"

/usr/local/apache2/bin/apachectl -D FOREGROUND

docker build -t nindate/nin-contactsapp:test-http-v2.0 .

docker push nindate/nin-contactsapp:test-http-v2.0

docker service inspect --pretty my-test-http

docker service ps my-test-http

docker service update --image nindate/nin-contactsapp:test-http-v2.0 my-test-http

docker service inspect --pretty my-test-http

docker service ps my-test-http

Rollback
--------

docker service ps my-test-http

docker service update --rollback my-test-http

docker service ps my-test-http

Swarm secrets
--------------
You can use secrets to manage any sensitive data which a container needs at runtime
but you don�t want to store in the image or in source control, such as:

Usernames and passwords


TLS certificates and keys
SSH keys
Other important data such as the name of a database or internal server
Generic strings or binary content (up to 500 kb in size)

Docker secrets are only available to swarm services, not to standalone containers.
To use this feature, consider adapting your container to run as a service with a
scale of 1

Another use case for using secrets is to provide a layer of abstraction between the
container and a set of credentials. Consider a scenario where you have separate
development, test, and production environments for your application. Each of these
environments can have different credentials, stored in the development, test, and
production swarms with the same secret name. Your containers only need to know the
name of the secret in order to function in all three environments.

When you add a secret to the swarm, Docker sends the secret to the swarm manager
over a mutual TLS connection. The secret is stored in the Raft log, which is
encrypted. The entire Raft log is replicated across the other managers, ensuring
the same high availability guarantees for secrets as for the rest of the swarm
management data.

echo "myrootpass"| docker secret create mysql_root_password -


echo "mysqlpass" | docker secret create mysql_password -

docker network create -d overlay mysql_private

docker service create \


--name mysql \
--replicas 1 \
--network mysql_private \
--mount type=volume,source=mydata,destination=/var/lib/mysql \
--secret source=mysql_root_password,target=mysql_root_password \
--secret source=mysql_password,target=mysql_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
-e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \
-e MYSQL_USER="wordpress" \
-e MYSQL_DATABASE="wordpress" \
mysql:latest

docker service update --secret-rm=mysql_root_password mysql


docker service update --secret-rm=mysql_password mysql

docker service ps mysql


docker exec -it <container-id> "ls" "/run"

Drain a node
-----------

docker node ls
docker service ps my-test-http

docker node update --availability drain test-docker2

docker node ls
docker service ps my-test-http

Bring back node to active status


--------------------------------

docker node ls
docker service ps my-test-http

docker node update --availability active test-docker2

docker node ls
docker service ps my-test-http

Docker swarm stacks


--------------------
When running Docker Engine in swarm mode, you can use docker stack deploy to deploy
a complete application stack to the swarm. The deploy command accepts a stack
description in the form of a Compose file.
The docker stack deploy command supports any Compose file of version "3.0" or
above.

Set up a Docker registry


+++++++++++++++++++++++
Because a swarm consists of multiple Docker Engines, a registry is required to
distribute images to all of them. You can use the Docker Hub or maintain your own.
Here�s how to create a throwaway registry, which you can discard afterward.

Start the registry as a service on your swarm:


$ docker service create --name registry --publish 5000:5000 registry:2

Check its status with docker service ls:


$ docker service ls

Once it reads 1/1 under REPLICAS, it�s running. If it reads 0/1, it�s probably
still pulling the image.

Check that it�s working with curl:


$ curl http://127.0.0.1:5000/v2/
{}

Create the example application


++++++++++++++++++++++++++++++
The app used in this guide is based on the hit counter app in the Get started with
Docker Compose guide. It consists of a Python app which maintains a counter in a
Redis instance and increments the counter whenever you visit it.

Create a directory for the project:


$ mkdir stackdemo
$ cd stackdemo

Create a file called app.py in the project directory and paste this in:
from flask import Flask
from redis import Redis

app = Flask(__name__)
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
count = redis.incr('hits')
return 'Hello World! I have been seen {} times.\n'.format(count)

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

Create a file called requirements.txt and paste these two lines in:
flask
redis

Create a file called Dockerfile and paste this in:


FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

Create a file called docker-compose.yml and paste this in:


version: '3'

services:
web:
image: 127.0.0.1:5000/stackdemo
build: .
ports:
- "8000:8000"
redis:
image: redis:alpine

Note that the image for the web app is built using the Dockerfile defined above.
It�s also tagged with 127.0.0.1:5000 - the address of the registry created earlier.
This will be important when distributing the app to the swarm.

Test the app with Compose


+++++++++++++++++++++++++
Start the app with docker-compose up. This builds the web app image, pull the Redis
image if you don�t already have it, and create two containers.

You will see a warning about the Engine being in swarm mode. This is because
Compose doesn�t take advantage of swarm mode, and deploys everything to a single
node. You can safely ignore this.

$ docker-compose up -d
WARNING: The Docker Engine you're using is running in swarm mode.

Compose does not use swarm mode to deploy services to multiple nodes in
a swarm. All containers will be scheduled on the current node.

To deploy your application across the swarm, use `docker stack deploy`.

Creating network "stackdemo_default" with the default driver


Building web
...(build output)...
Creating stackdemo_redis_1
Creating stackdemo_web_1

Check that the app is running with docker-compose ps:


$ docker-compose ps

Name Command State Ports


-----------------------------------------------------------------------------------
stackdemo_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
stackdemo_web_1 python app.py Up 0.0.0.0:8000->8000/tcp

You can test the app with curl:


$ curl http://127.0.0.1:8000
Hello World! I have been seen 1 times.

$ curl http://127.0.0.1:8000
Hello World! I have been seen 2 times.

$ curl http://127.0.0.1:8000
Hello World! I have been seen 3 times.

Bring the app down:


$ docker-compose down --volumes

Stopping stackdemo_web_1 ... done


Stopping stackdemo_redis_1 ... done
Removing stackdemo_web_1 ... done
Removing stackdemo_redis_1 ... done
Removing network stackdemo_default

Push the generated image to the registry


++++++++++++++++++++++++++++++++++++++++

To distribute the web app�s image across the swarm, it needs to be pushed to the
registry you set up earlier. With Compose, this is very simple:
$ docker-compose push
The stack is now ready to be deployed.

Deploy the stack to the swarm


++++++++++++++++++++++++++++++
Create the stack with docker stack deploy:
$ docker stack deploy --compose-file docker-compose.yml stackdemo

The last argument is a name for the stack. Each network, volume and service name is
prefixed with the stack name.

Check that it�s running with docker stack services stackdemo:


$ docker stack services stackdemo

Once it�s running, you should see 1/1 under REPLICAS for both services. This might
take some time if you have a multi-node swarm, as images need to be pulled.

Check where (on which nodes) the containers are launched


docker stack ps stackdemo

Check using curl


$ curl http://127.0.0.1:8000
Hello World! I have been seen 1 times.

$ curl http://127.0.0.1:8000
Hello World! I have been seen 2 times.

$ curl http://127.0.0.1:8000
Hello World! I have been seen 3 times.

Thanks to Docker�s built-in routing mesh, you can access any node in the swarm on
port 8000 and get routed to the app:
$ curl http://address-of-other-node:8000
Hello World! I have been seen 4 times.

Bring the stack down with docker stack rm:


$ docker stack rm stackdemo

Bring the registry down with docker service rm:


$ docker service rm registry

Docker swarm nodes HA


----------------------
From the swarm manager node check the nodes joined into the swarm
docker node ls

Promote additional nodes as manager nodes so that we can test the HA


docker node promote <node-id-node-2>
docker node promote <node-id-node-3>
docker node ls

Create a service to test HA of services also along with swarm manager nodes' HA
docker service create --name myhttp2 --publish 8081:80 --replicas 3 httpd

docker service ls
docker service ps myhttp2

Now stop the 1st node which currently is the docker swarm manager in Leader mode
Observe for a while, when the 1st node becomes unreachable, another manager node is
elected as a Leader
docker node ls
docker service ps myhttp2

Once the 1st node is marked a Down, you will observe that another container is
launched in place of the container that was running on the 1st node which went down

#################
Advanced tasks

Create custom image with multiple commands using Dockerfile and supervisord
---------------------------------------------------------------------------

mkdir myapp
cd myapp

#Create file name setup.sh with following contents


#!/bin/bash
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
apt-get update -y
apt-get install -y apache2 openssh-server supervisor expect
mkdir -p /var/lock/apache2 /var/run/apache2 /var/run/sshd /var/log/supervisor
sed -i "s/^PasswordAuthentication/#PasswordAuthentication/" /etc/ssh/sshd_config
echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
service ssh restart
useradd -d /home/myuser -m -s "/bin/bash" myuser
expect -c "
set timeout 10
spawn passwd myuser
expect \"Enter new UNIX password: \"
send \"password\r\"
expect \"Retype new UNIX password: \"
send \"password\r\"
expect eof
"
mkdir -p /home/myuser
chown myuser:myuser /home/myuser
echo "myuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/myuser
apt-get remove -y expect
echo 'debconf debconf/frontend select Dialog' | debconf-set-selections
echo "Welcome to myapp" > /var/www/html/index.html

#Make setup.sh executable


chmod u+x setup.sh

#Create file named supervisord.conf with following contents


[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -
DFOREGROUND"

#Create file named Dockerfile with following contents


FROM ubuntu
MAINTAINER Ninad Date
COPY setup.sh /root/setup.sh
RUN /root/setup.sh
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 22 80
CMD ["/usr/bin/supervisord"]

#Create docker image from Dockerfile in current location [hence the .]


docker build -t myapp:v1.0 .

### Some testing for fun


docker run --name vm1 -d ubuntu /bin/bash -c 'while true; do echo -n "testing at ";
date; sleep 5; done'

docker run --name vm1 -d alpine /bin/sh -c 'while true; do echo -n "testing at ";
date; sleep 5; done'

Run following command every 5 secs and you will see that the given commands are
running inside the container
docker logs vm1
### Check this and find out a working method for health check
FROM ubuntu
RUN apt-get update -y \
&& apt-get install -y apache2 curl
HEALTHCHECK --interval=10s --timeout=2s --retries=2 CMD curl localhost/index.html
|| exit 1
CMD ["/usr/sbin/apache2ctl" "-D FOREGROUND"]

You might also like