docker
DOCKER FUNDAMENTALS
Docker is a platform for developing shipping and running application using container virtualization technology.
Docker platform consist of
Docker Engine
Docker Hub
Docker Machine
Docker Swarm
Docker Compose
Kitematic
Introduction of Containers:
Consider container as a box, app+dependencies which runs on container engine that uses the OS-kernal.
Container based virtualisation uses the kernel on the host's operating system to run multiple guest instances.
Each guest instance is called container
Each container consists of
a. Root filesystem
b. Processes
c. Memory
d. Devices
e. Network ports
Outside it looks like VM but it is not VM.
Containers Vs VM
1. Containers are lightweight
2. No need to install guest OS
3. Less CPU, RAM storage space required
4. More containers per machine than VMs
5. Greater Portability
Docker has become the de-facto standard for containers. Docker is not the only solution for containers. LXC has existed for many years. Linux systemd has systemd-nspawn, which also is a solution to run containers.
Docker Concepts and Terminologies
DOCKER ENGINE:
Docker engine is a program that enables containers to be built, shipped and run
Docker engine uses Linux kernel namespaces and control groups.
Namespaces gives us isolated workspace
We go pid for process isolation, the net namespace to managing network stack, ipc namespace, mnt namespace for managing mount points and UTS namespace give us isolated work space which we call Container.
To Install Docker
wget -qo- https://get.docker.com/ | sh
The URL is going to return a shell script, where we pass the output to 'sh'. Potentially running all commands to install docker.
Provide sudo permission for the user so we can run docker commands without sudo.
sudo usermod -aG docker <username>
Docker Client and Demon:
- Client Server Architecture
- Client takes input and sends them back to the demon
(ex when we say docker run, run is the client which sends input to the demon)
- Daemon builds, runs and distributes container.
- Client and Demon can run on same or different host.
- CLI client and GUI(Kinematic - has a graphical interface)
docker version - gives you the version of docker
DOCKER CONTAINER AND IMAGES:
Images:
- Read only template used to create containers
- Built by you or other docker users
( there are official images that we can use, within the image there are set of instructions for creating the containers such as what programme to install, what folders and files to create, and what file volume to mount etc. )
- Stored in docker hub or local Registry
Container:
- Isolated application platform
- Contains everything needed to run your application
- Based on one or more images.
We create containers from images and run the container. We have everything to run the images - example all necessary libraries and binaries.
Registry and Repository
Registry is where we store our images. We can run our own registry or use docker hub registry which is called docker hub. Inside the registry, images are stored in repositories.
DOCKER ORCHESTRATION
Orchestration is about automated arrangements co-ordination, and management of complex computer systems, middleware and services.
Three tools for Orchestrating distributed application with Docker
1. Docker Machine: Tool that provision Docker hosts and install the Docker engine on them.
2. Docker Swarm: Tool that clusters many Engines and schedules containers
3. Docker Compose: Tool to create and manage multi-container applications.
BENEFITS OF DOCKER
Separation of concerns
Developers focus on building their apps.
System admins focus on deployments
Fast development cycles
Application portability
Build in one environment, ship to another
Scalability
Easily spin more containers if needed.
Runs more apps on one host machine.
Image Tags:
Images are specified in repository:tag
The same image might have multiple tags
The default tag is latest(ie. is no tag is specified, the image will be tagged as latest)
CREATING A CONTAINER
Use docker run command
Syntax
sudo docker run [options] [image] [command] [args]
Image is specified with repository:tag (example: docker run ubuntu:14.04 echo "Hello World!"
Running a Container:
docker run ubuntu:14.04 echo "hello world"
will print 'hello world'
Unable to find image 'ubuntu:14.04' locally 14.04: Pulling from library/ubuntu 96c6a1f3c3b0: Pull complete ed40d4bcb313: Pull complete b171f9dbc13b: Pull complete ccfc4df4fbba: Pull complete Digest: sha256:9274d908eb6d9a3784e93290fcc49f3c5618db9e1b0174ee27f9fc75aa3c0fb0 Status: Downloaded newer image for ubuntu:14.04 hello world
First time when we run the command since the ubuntu images is not found locally the ubuntu image was downloaded from the docker hub.
docker run ubuntu:14.04 ps ex
PID TTY STAT TIME COMMAND
1 ? Rs 0:00 ps ax
Second time it will be very fast as the images is present locally.
Container with Terminal
Use -i and -t flag with docker run
The -i flag tells docker to connect to STDIN on the container
The -t flag tells docker to get a pseudo terminal
Note: You need to run a terminal process as your command (eg /bin/bash)
Example: docker run -i -t ubuntu:latest /bin/bash
Container Process:
- A container only run as long as the process from your specified docker run command is running.
- Your command process is always 1 inside the container.
To test this, run
docker run ubuntu -it bash
now by pressing Ctrl P + Q to keep the process running and to exit from the container.
If you look at the ps command the pid of the bash will be having a separate pid and the parent id will be docker pid.
Container ID:
Containers can be specified by their ID or name. There are LongID and Short ID.
Short ID can be obtained by docker ps command and Long id can be obtained by inspecting the container.
Find Container:
docker ps to list all running containers
docker ps -a list all the containers.
Run in Detached mode:
Also know as run in the background or as a deemon
Use -d flag.
To observe log output use
docker logs [container id] command. ( -f will tail the log)
Example: docker run -d ubuntu:40.04 ping 127.0.0.1 -c50
To inspect docker log <containerid>
Port Mapping:
User -P command to map a port
Example: docker run -d -P tomcat:7 (no command means default command will be used)
When you do docker ps command it will show which port tomcat is mapped to (like 0.0.0.0.45838-->8080/tcp). First port is the host port where you can access tomcat from the browser using port 45838 and 8080 is the container port.
IMAGE LAYER
Images are comprised of multiple layers
A layer is also just another image
Every image contains a base image
Docker uses a copy on write system
Layers are read-only
Containers Writable Layer
Docker provides a top level writable layer for containers.
Parent images are read-only
All changes are made in the writable layer.
If any changes in the apache image(in the below image). Docker uses copy of the read-only image to the writable layer. The original read-only version is still present, but hidden. And changes happen on the writable layer. The copy of the write system is what it makes it so fast to spin up containers.
Docker Commit
doctor commit command saves changes in container as new image
Syntax
docker commit [options] [container ID] [repository:tag]
Repository name should be username/application
Can reference the container with container name instead of ID.
Example: docker commit 984d29e88 user/myapp:1.0
Here 1.0 is the tag.
Docker FILE
DOCFILE IS A CONFIGURATION FILE THAT CONTAINS INSTRUCTIONS FOR BUILDING DOCKER IMAGE
Provides a more effective way to build images compared to using docker commit
Easily fits into your continuous integration and deployment process
DocFile Instructions:
- Instructions specify what to do when building images
- FROM instruction specifies what the base image should be.
- RUN instruction specifies a command to execute
Example:
FROM ubuntu:14.04
RUN apt-get install vim
Each RUN instruction creates a commit on top of writable layer, to avoid multiple commits, the RUN commands can be combined together
Example:
RUN apt-get update && apt-get install -y \
curl \
vim \
openjdk-7-jdk
DOCKER BUILD
Syntax
docker build [options] [path]
Common option to tag the build
docker build -t [repository:tag] [path]path is the build context. Say you want to add source code, the path of the source code should be specified.
docker build -t sbbabu/project:1.2 .
CMD INSTRUCTIONS
CMD defines a default instruction to execute when container is created
CMD reforms no action during the image build
Shell format and EXEC format
Can only be specified once in the Dockerfile
CAN BE OVERRIDDEN AT RUNTIME
Shell Format
CMD ping 127.0.0.1 -c 30
EXEC Format
CMD ["ping","127.0.0.1", "-c", "30"]
Example:
Dockerfile - below will update and install curl and vim in RUN command and CMD command will ping for 30times
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y curl
\ vim
CMD ["ping","127.0.0.1", "-c", "30"]
Build the file using the below command
build -t bsb/dkfile:1.1 .
Run the image - This will print the ping out for 30times.
docker run bsb/dkfile:1.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.039 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.053 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.053 ms
We can override the CMD command by passing the command while running, in which case it will override the CMD command specified in the Dockerfile
Ex: docker run bsb/dkfile:1.1 echo "hello world"
ENTRY POINT INSTRUCTIONS
Defines the command that will run when a container is executed.
Runtime arguments and CMD instruction are passed as parameters to ENTRYPOINT instruction.
EXEC form preferred as shell form cannot accept arguments at runtime.
Container essentially runs as and executable.
Note: We cannot override instruction during runtime unlike CMD instruction.
Example: We have added ENTRYPOINT instruction and set to run 'ping'. During run time we will need to provide the arguments for ping
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y curl \ vim
ENTRYPOINT "ping"
After building Run by executing below command.
docker run bsb/dkfile:1.1 127.0.0.1 -c 5
Note that we are not specifying the ping command but the arguments passed are sent to the ENTRYPOINT as parameters.
MANAGING IMAGES AND CONTAINERS
Find the containers first with docker ps command and note the ID and name of the container.
Start container using
docker start <containerid> - This could be useful when the container is running in detached mode.
Stop Container
doctor stop <containerid> - To stop the container
GETTING TERMINAL ACCESS
Say our server is running tomcat or other web server to get the terminal access use the EXEC command. This starts another process within our container.
Execute with /bin/bash to get the bash shell
docker exec -it [container id] /bin/bash
If you exit the terminal it will not exit the container. As the bash is not the container id '1'.
DELETING CONTAINER
To delete a container use
docker rm <container id> or name
Note that only stopped containers can be deleted.
Remove container with filter
docker rm $(docker ps -a -q -f status=exited)
DELETING LOCAL IMAGES
Use docker rmi [imageid] or
docker rmi [repo:id]
If an image is tagged multiple times, remove each tag.
TAGGING IMAGES
Used to rename the local image repository before pushing to Docker hub. Docker hub require tag names to be same as the docker hub tag.
Syntax
docker tag [image id] [repo:tag] or
docker tag [local repo:id] [Docker Hub repo:tag]
Example:
docker tag 51316840fa8c sbbabu/dkfile:1.2 or
docker bsb/dkfile:1.2 sbbabu/dkfile:1.2
Push to Docker Hub
docker push sbbabu/dkfile:1.1
VOLUMES
A volume is a designated directory in a container, which is designed to persist data, independent of the container's lifecycle.
Volume changes are excluded when updating the image
i.e when a mount a file system to a container at a certain folder we add files and commit the folder as new image. The files added to the image will not be included.
Persist when a container is deleted
Can be mapped to a host folder
Can be shared between containers.
REX-Ray is an open source, storage management solution designed to support container runtimes - check this link
http://rexray.readthedocs.io/en/stable/
MOUNT A VOLUME
Volumes are mounted when creating or executing a container
Can be mapped to host directory.
Volume paths must be absolute.
Execute a new container and mount the folder /myvolume into its filesystem
docker run -d -P -v /myvolume ubuntu14.04
Execute a new container and map the /data/src folder from the host into /test/src folder in the container
docker run -it -v /data/src:/test/src ubuntu14.04
Use -u command to set the user
ie. docker run -it -v /data/src:/test/src -u 1000:1000 ubuntu14.04
where 1000:1000 are ids of user/group respectively.
VOLUMES IN DOCKErFILE
VOLUME instruction creates a mount point
Can specify argument JSON array or string
Cannot map volume to host directory
Volumes are initiated when the container is executed.
Example:
String example
VOLUME /myvol
String example with multiple volume
VOLUME /www/site1/ /www/site2/
JSON example
VOLUME ["myvol1","myvol2"]
USES OF VOLUMES
De-couple the data that is stored from the container which created the data. Say your containers runs an app which creates many log files, if you store them in a volume you can access them even when the container is shutdown.
Good for sharing data between containers
Can setup a data containers which has a volume you mount to other containers
Mounting folders from the host is good for testing but generally not recommended in production environment.
Example
Create a mount point for ubuntu image
docker run -d -it -P -v /www/site1 ubuntu14.04
Remove unwanted volumes
docker volume rm $(docker volume ls -qf dangling=true)
CONTAINER NETWORKING
MAPPING PORT:
Ports can be manually mapped or auto mapped
Using -P will assign a port automatically. Host port number used go from 49153 to 65535. Only works for ports defined in EXPOSE instruction
Using -p , we can specify which port to map.
docker run -d -p 8080:80 nginx
EXPOSE INSTRUCTION
Configure which ports a container will listen on at runtime. Ports still need to be mapped when container is executed.
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y nginx
EXPOSE 80 443
CMD["nginx","-g","daemon off;"]
LINKING CONTAINERS
Linking container is a communication method between containers which allow them to securely transfer data from one to another.
Recipient containers have access to data on source containers
Links are established based on container names.
Creating Link
Create source container first
Create the recipient container and use the --link option
Syntax
Create source container
docker run -d --name database posture
Create recipient container
docker run -d -P --link database:db nginix ( here 'db' is the alias provided for the link)
docker run -d --name dbms postgres (run posters and assign name dbms)
docker run -it --name website --link dbms:db ubuntu:14.04 bash (link the dbms to the website container)
In the dbms container the host file will have an entry
cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4 db ba815a7a88ea dbms
172.17.0.2 1a3c31715b70
This can be verified by using the inspect command
docker inspect dbms |grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.4",
DOCKER OPERATIONS
Trouble Shooting Containers:
docker logs <containername or id>
To tail logs
docker logs -f <containername or id>
To Run Docker with non-root user
#usermod -aG docker $USER
MONITORING DOCKER
cADVISOR (from google)
cAdvisor (Container Advisor) provides container users an understanding of the resource usage and performance characteristics of their running containers. It is a running daemon that collects, aggregates, processes, and exports information about running containers. Specifically, for each container it keeps resource isolation parameters, historical resource usage, histograms of complete historical resource usage and network statistics. This data is exported by container and machine-wide.
To run cAdvisor
sudo docker run \ --volume=/:/rootfs:ro \ --volume=/var/run:/var/run:rw \ --volume=/sys:/sys:ro \ --volume=/var/lib/docker/:/var/lib/docker:ro \ --publish=8080:8080 \ --detach=true \ --name=cadvisor \ google/cadvisor:latest
WORKING WITH REDIS IMAGE
Download and start redis. This will expose port 6379
$ docker run --name some-redis -d redis
Start redis with persistent storage
$ docker run --name some-redis -d redis redis-server --appendonly yes
Connect to redis from an application
$ docker run --name some-app --link some-redis:redis -d application-that-uses-redis
Using redis.conf in docker run
$ docker run -v /myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf --name myredis redis redis-server /usr/local/etc/redis/redis.conf
Where /myredis/conf/ is a local directory containing your redis.conf file. Using this method means that there is no need to have Dockerfiel for reais container.
Alternatively if you want to create your own Dockerfile, use the below command that adds a redis.conf from the context into /data
FROM redis COPY redis.conf /usr/local/etc/redis/redis.conf CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
UNDERSTANDING HOW CONTAINERS WORK
When a process is started the kernel makes lots of information available to it.
Containers are simply process that are given their own isolated view of the information via namespaces.
There are 6 namespaces
Mount namespace
UTS namespace - Unix Timesharing Namespace
IPC namespace - Inter Process Communication
NET namespace
PID namespace
USER namespace
USHARE - Is a tool which runs program with some namespaces unshared from parent.
When a process with pid 1 is killed all other process running in the container will be killed.
For example, you run a container with bash process and 2 sleep processes to test that the process is running. When you kill the bash process which is pid 1, the sleep processes will be deleted as well.
Normally the kernel shuts down the process when you have do Ctrl+c, but that is not true.
Say you run a container with
docker run -it -rm --name sleepy debian sleep 10000
when you try to kill the process using CTRL+C, the process will not stop and you will need to stop the process. You should use the docker rm -f sleepy, to kill the process.
UTS NAMESPACE - Unix Timesharing Namespace
Each container has its own hostname. How this is done in docker can be explained using the unshare tool.
testuser$ sudo unshare -u bash
$ hostname newhostname
After the commands if you switch to other tab, you can find that the namespace is not changed.
Network Namespace: Allocates another copy the entire networking stack, with its own routes, firewall routes and network devices.
To test when we run
sudo unshare -n bash
root@user:/home/user# ip a
The result will only be the <loopback> interfaces.
But in docker there will be one more interface if you run the same command inside docker container, which is set by default, where you can find 'ethic' a virtual interface which allows to communicate with the containers.
User Namespace: Provides mapping from uid and gid outside the namespace to a different set inside namespace.
Docker specifically will map the uid of the specific user to be root inside container.
Say you are running the container using a non root user
$docker -it --rm -v "$(pwd)":/cwd/ debian bash
root@289388b32:/# cd /cwd
root@289388b32:/# touch test.txt
touch: cannot touch 'test.txt': Permission denied
Here though you are the root use inside container you will not be allowed to create a file inside the shared volume. This is because the user is not having proper permissions to create folders.
Caveat: When you enable username spaces or modify which uid maps to root. You have to remake or redownload all the images.
Mount Name Space: Gives processes belonging to the namespace a separate list of mount space, where it will not share with other namespaces.
CGROUP and Pivot Root
cgroup - which control access to memory, cpu and devicesand pivot root to control access to file systems.
If a process request more than the allocated memory resources it throws out of memory error.
$docker run -it --rm -m 100m debian
root@b3298f923:/# free -m
does not show that your memory is limited. To find that you have to cat the cgroup file associated with that.
root@b3298f923: cat /sys/fs/cgroup/memory/memory.limit_in_bytes
104857600
root@b3298f923: echo "$((104857600 /1024 /1024 ))"
100
Now we can see that it is limited to 100mb.
cpu c group can cfs(completely fair schedules) - how much CPU power is it can use.
Run this in container
$docker run -it --rm --cpuset-cpus 0 debian nproc
Tell docker to access the 0th cpu.
Give more
$docker run -it --rm --cpuset-cpus 0 debian proc
1
$docker run -it --rm --cpuset-cpus 0-2,5 debian nproc
4
Given a period and a quota containers get roughly the quota dived to the period percentage relative to a single CPU core. Thus to get half a core to worth of CPU time, we can use values of for the periods 2000 and 1000.
$docker run -d --cpu-period=2000 --cpu-quota=1000 --name cpu-hog debian:jessie bash -c 'for((i=1; i< $(nproc); i++)); do bash -c "while true; do true; do true; done" & true; done; while true; do true; done'
$docker exec cpu-hog ps aux
You can see that the cpu usage is around 50.
Device cgroup
Containers does not have access to all the devices
$docker run -rm -v /dev:/hostdev debian head --bytes=100 /hostdev/sda1 |hexdump -C
--error operation not permitted
We can add cgroup to give access to that device
$docker run -rm -device /dev/sda1 debian head --bytes=100 /dev/sda1 |hexdump -C
Not you should see the fir 100 bytes of the device printed.
Pivot root -
$ mkdir rootfs
$ sudo mount -t tempfs tempos rootfs
$ mkdir rootfs/old
$ cd rootfs
Use busy boximage to get static busy box so that when we have new root we can run something
$ docker run --rm busybox cat /bin/busybox > busybox
$ chmod +x busybox
Start new shell in a new namespace (part of requirement of pivot root)
$sudo unshare --fork --mount --mount-proc --pid ./busybox.sh
/home/username/rootfs# pivot_root .old
/# ./busybox ls
busybox old
Old root can be found in old directory
IMAGE - Remove <none> images.
You can find many <none> images when you type docker images -a, this is because of the intermediate images which docker doesn't clear automatically. This can be removed by using the below command.
docker rmi $(docker images -f "dangling=true" -q)
More details about the none images can be found at http://www.projectatomic.io/blog/2015/07/what-are-docker-none-none-images/
SET TIMEZONE
In docker run set environment variable -e TZ=Asia/Singapore
COPY FILES FROM CONTAINER
example:
docker cp <containerid>:<filepathofcontainer> <hostpath>
docker cp 53ea67f622f2:/usr/local/tomcat/conf/catalina.properties .
DOCKER SWARM
Create a swarm master using docker machine
docker-machine create -d virtualbox --swarm --swarm-master --swarm-discovery="zk://192.168.99.100:2181/swarm" --engine-opt="cluster-store=zk://192.168.99.100:2181/overlay" --engine-opt="cluster-advertise=eth1:2376" mymaster
Create a swarm node
docker-machine create -d virtualbox --swarm --swarm-discovery="zk://192.168.99.100:2181/swarm" --engine-opt="cluster-store=zk://192.168.99.100:2181/overlay" --engine-opt="cluster-advertise=eth1:2376" mynode1
Switch to swarm master and create overlay network
$ eval $(docker-machine env mymaster)
docker network create --driver overlay myoverlay-network
Deploy application on different host using Constraints
$ docker run --name myapp --net=myoverlay-network --env="constraint:mynode1" sbbabu/myapp
same way you can deploy you backend app into another host. And they should be able to communicate with each through over-lay network.
Docker can be run inside docker
There are couple of things needed the image should have docker in it and we should mount /var/run/docker.sock. If docker is not installed we can mount the local docker as shown below. (mapping docker binary many not work on some machines)
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock -v /usr/local/bin/docker:/usr/bin/docker busybox:latest
MACVLAN
Joining Existing network with MACVLAN
Docker allow to connect to existing networks use MACVLAN. MACVLAN assigns each container its own IP. This would require PROMISCUOUS MOD E (But AWS and most cloud provides don't allow this)
Joining Existing network with IPVLAN
Crate a docker network with driver ipvalan, and this will connect to the existing network and the containers will be accessible though the underlay network.
Similar to Linux but doesn't give containers their own MAC addresses. Containers cannot ping their host. Special consideration required when working with DHCP.
docker network create -d ipvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.254 \
--ip-range=192.168.1.0/28 \
-o ipvlan_mode=l2 \ #(this is default layer2 - no need to specify)
-o parent=eth0 myipvnetwork
DOCKER SERVICE DISCOVERY
Every container gets a small DNS resolver which
- listens at 127.0.0.11:53
- Forwards request to DNS server on Docker host. If the docker dos cannot resolve, it will try to resolve with public dns.
VIP - every service gets a VIP and it routes to the tasks accordingly.
Ingress network is on every node and is used as routing mesh on every node. Ingress is a container (internal) used by docker.
HTTP Routing Mesh (HRM)
Builds on top of port-based /L4 routing mesh
- Allows multiple services on the same port
- Operates at the application layer (L7)
Requires Docker Datacenter ( only for docker EE)
LINKS
Create test instance to play around with docker, this has ssh access for limited hours.
http://play-with-docker.com/
To increase the size of base docker
https://community.hortonworks.com/content/kbentry/65901/how-to-increase-the-size-of-the-base-docker-for-ma.html
Check Docker Configuration
https://github.com/moby/moby/blob/master/contrib/check-config.sh
Create a Small JDK image
https://developer.atlassian.com/blog/2015/08/minimal-java-docker-containers/
FROM alpine:3.2
RUN apk --update add openjdk7-jre
CMD ["/usr/bin/java", "-version"]
--------------- NON DOCKER LINKS -----------
HTML slide show
https://remarkjs.com