Contents
Online Playground
* Play with Docker: https://labs.play-with-docker.com/
Overview
* Docker leverages Linux OS features (x64/3.10):
– process isolation
– filesystem isolation
* Docker editions:
– Docker EE (Enterprise Edition)
– Docker CE (Community Edition)
* Default port: 3275
* Socket file: /var/run/docker.sock
Docker Advantages
* No OS virtualization for Docker means:
– Smaller disk footprint
– Faster setup/startup/stop time
– Can be pulled from repository and pushed to targets
– Cost savings
* Supports:
– Microservices
Windows Docker Containers
* Different from Linux based containers
* Supported in:
– all versions of Windows Server 2016 (Full, Core, Nano)
– Windows 10 Enterprise/Professional edtions
– Azure
* Containers can be deployed/managed from any Docker client, e.g.
– command line
– Powershell
* Supports:
– Docker image format/Docker API
* Two types:
– Windows Server container (on Windows Server)s
|- shared OS kernel
|- pros: performance/efficiency
|- cons: weak isolation/security
– Hyper-V container (on Windows Nano Server)
|- dedicated OS kernel/memory
|- pros: strong isolation/security
Install Docker Engine CE
* Auto install script:
sudo curl -sSL https://get.docker.io/ # -s: silent, -S: show errors, -L: location sudo wget -qO- https://get.docker.io/ # -q: quiet, O-: output to stdout
Ubuntu Manual Install
# Update repo sudo apt-get update # Add Docker repo to apt sources sudo sh -c "echo deb https://apt.dockerproject.org/repo \ ubuntu-xenial main > /etc/apt/sources.list.d/docker.list" # Add GPG key sudo apt-key adv --keyserver \ hkp://p80.pool.sks-keyservers.net:80 --recv-keys \ 58118E89F3A912897C070ADBF76221572C52609D # Resync repo sudo apt-get update # Install Docker and start Docker service sudo apt-get install -y docker-engine # Verify install sudo docker version sudo docker info
Download and Install Docker Image
# Download and install hello-world image sudo docker pull hello-world # Show/verify install/images sudo docker images # Run hello-world image docker run hello-world # Download/install/Run ubuntu in interactive mode docker run -it ubuntu bash
Docker Lifecycle Commands
# List docker processes docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 16d1621a6ceb ubuntu "bash" 3 minutes ago Exited (0) 6 seconds ago happy_tesla # Start by ID sudo docker start 16d1621a6ceb # Start by Name sudo docker start happy_tesla # Attach sudo docker attach id/name # Detach: CtrlP + CtrlQ # Start but remove when state is exited sudo docker run -it --rm ubuntu:16.04 /bin/bash # Restart sudo docker restart id/name # Pause sudo docker pause id/name # UnPause sudo docker unpause id/name # Remove docker image sudo docker rm id/name # Remove all stoped images sudo docker rm $(sudo docker ps -aq -f status=exited) sudo docker container prune
Troubleshooting
# Check status sudo service docker status # Restart Docker sudo service docker restart # Extract Docker log journalctl -u docker # List Docker processes sudo docker ps sudo docker ps -a
Manage Docker Containers
Terms
Docker Image
* Docker image is:
– a collection of all the files that make up an executable software app (compare to dll and jar files for software but at app level)
– readonly template (compare to Java class)
* Includes:
– libraries
– binaries
– other dependencies
* Capabilities can be added as layers to existing image
Docker Container
* Running instance of a Docker image (compare to Java class instance)
– A writeable layer is added on top of the image to maintain the app state
Docker Registry
* Images can be pushed to or pulled from registry
* Can be public or private
* Example registries:
– Docker Hub: index.docker.io
– Quay
– Google Container Registry
– AWS Container Registry
* Search for an image
sudo docker search mysql | head -10
* Pull an image
# Pull busybox image from Docker Hub sudo docker pull busybox # Pull version 1.2 of busybox image from Docker Hub sudo docker pull busybox:1.2 # Pull version 1.2 of busybox image from Docker Hub under developer named dockerguy sudo docker pull dockerguy/busybox:1.2 # Pull version 1.2 of busybox image from my local registry sudo docker pull registry.mylocal.com/busybox:1.2 # Examing pulled image sudo docker images # You can diff images sudo docker diff id/name # Commit image sudo docker commit id mydocker/ubuntu_wget # Don't use
* Run image in interactive mode
-a=[] : Attach to `STDIN`, `STDOUT` and/or `STDERR` -t : Allocate a pseudo-tty --sig-proxy=true: Proxy all received signals to the process (non-TTY mode only) -i : Keep STDIN open even if not attached
– Example:
# Run Ubuntu image in interactive mode sudo docker run -it ubunbu:16.04 # -i: interactive, -t: terminal emulation # Detach from interactive session with CtrlP + CtrlQ # Attach back into interactive session sudo docker attach id/name # Terminate Docker container with exit command
* Run image in detached mode
sudo docker run -d ubuntu
Building Images using Dockerfile
Example
* Create a file named: Dockerfile
FROM busybox:latest CMD echo Hello World!!
* Build using Dockerfile file in current directory with name busybox2
sudo docker build -t mybusybox .
Dockfile Instructions
* Specify escaple character, default is \
# escape='
* FROM
FROM <image>[:<tag>|@<digest>] # Examples FROM centos FROM ubuntu:16.04 FROM ubuntu@sha256:xxxxxx
* MAINTAINER
MAINTAINER <author's detail> # Example MAINTAINER Jimmy LI <jimmy@yahoo.com>
* COPY
COPY <src> ... <dst> # Examples COPY html /var/www/html
* ADD
– handles tar and remote files in addition to copy
ADD <src> ... <dst> e.g. ADD web-page-config.tar /
# ENV: sets env var ENV DEBUG_LVL 3 ENV APACHE_LOG_DIR /var/log/apache # ARG: defines build arguments ARG usr ARG uid=1000 #Usage: docker build --build-arg usr=app --build-arg uid=100 . ARG BUILD_VERSION #Usage: LABEL com.example.app.build_version=${BUILD_VERSION} # USER: setup startup user ID or username #USER <UID>|<UName> USER 73 # WORKDIR: setup working dir WORKDIR /var/log # VOLUME VOLUME ["<mountpoint>"] VOLUME <mountpoint> # EXPOSE: opens up a container network port # Defaults to TCP port EXPOSE 7373/udp 8080 # LABEL LABEL org.label-schema.schema-version="1.0" org.label-schema.version="2.0" org.label-schema.description="Learning Docker Example" # RUN: executed during build process RUN apt-get update && \ apt-get install -y apache2 && \ apt-get clean # CMD: executed when container is launched CMD ["echo", "Dockerfile CMD demo"] # ENTRYPOINT: # - executed when container is launched # - container is terminated when command stops ENTRYPOINT ["echo", "Dockerfile ENTRYPOINT demo"] # HEALTHCHECK # - runs health check command # HEALTHCHECK [<options>] CMD <command> HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1 HEALTHCHECK NONE # ONBUILD # - triggered at next build #ONBUILD <INSTRUCTION> ONBUILD ADD config /etc/appconfig # STOPSIGNAL #STOPSIGNAL <signal> # SHELL # - overrides the default shell # SHELL ["<shell>", "<arg-1>", ..., "<arg-n>"]
* .dockerignore file
# Ignore .git directory and all files ending with .tmp .git *.tmp
* Check image history
sudo docker history apache2
* Best practices for writing a Dockerfile:
https://docs.docker.com/articles/dockerfile_best-practices/.
Publishing Images
Docker Hub
* Can be used for:
– public
– private
* Features:
– image repo
– user auth
– manage org and groups
– auto image build
– integrate with GibHub and Bitbucket
* URL: https://hub.docker.com
– Docker ID: jxxliextpie1
* Automate build process for images
* Local repo
– see ref: https://github.com/docker/docker-registry
* Supports REST API
Private Docker Infrastructure
Docker Registry
* Listening on: TCP 5000
* Storage path: /var/lib/registry
* Install and start registry
# Start sudo docker run -d -p 5000:5000 \ --restart=always --name registry registry:2 # Check sudo docker ps -a
* Get and tag an image
# Get image sudo docker pull hello-world # Tag image sudo docker tag hello-world localhost:5000/hello-world
* Push image
sudo docker push localhost:5000/hello-world
* Pull image
sudo docker pull localhost:5000/hello-world
* Stop and delete registry
sudo docker stop registry && sudo docker rm -v registry
* Restart registry
sudo docker run -d -p 5000:5000 --restart=always --name registry -v `pwd`/data:/var/lib/registry registry:2
Sharing Data with Container
* Data persistence approaches:
– use volumes created using Docker’s volume management
– mount host directory inside container
– use data-only container
Data Volume
* Mount data volume manually
docker run -v /datavol -it ubuntu:16.04
* Or create Dockerfile and specify a data volume:
FROM ubuntu:16.04 VOLUME /datavol
* Build image
docker build -t datavol1 .
* Check
docker inspect datavol1
* Test run
docker run --rm -it datavol1 # List datavol1 ls -ld /datavol drwxr-xr-x 2 root root 6 Apr 10 18:32 /datavol # Check mount mount|grep datavol /dev/sdb on /datavol type xfs (rw,relatime,attr2,inode64,prjquota) # Check host dir docker inspect -f '{{json .Mounts}}' 52c437babf62 {"Type":"volume","Name":"15a7fecfbca850863cbfa61e679d9b33bf765c1087ddf621115ee82da4ab545a","Source":"/var/lib/docker/volumes/15a7fecfbca850863cbfa61e679d9b33bf765c1087ddf621115ee82da4ab545a/_data","Destination":"/datavol","Driver":"local","Mode":"","RW":true,"Propagation":""}]
* Remove host directory for associtated volume when shutting down container
docker rm -v 52c437babf62 # Force removal of volume when removing container docker rm -fv 52c437babf62
Volume Management
* Volume plugins:
https://docs.docker.com/engine/extend/legacy_plugins/#volume-plugins
* docker volume
# Create a volume named datavol2 docker volume create --name datavol2 # List volumes docker volume list # Inspect volumes docker volume inspect datavol2 [ { "CreatedAt": "2018-04-10T19:05:30Z", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/datavol2/_data", "Name": "datavol2", "Options": {}, "Scope": "local" } ] # Remove volume docker volume rm datavol2
Sharing Host Data
* docker run -v options:
-v <container mount path> -v <host path>:<container mount path> -v <host path>:<container mount path>:<read write mode> -v <volume name>:<container mount path> -v <volume name>:<container mount path>:<read write mode>
* Example, mount host /datavol as /datavol inside container:
docker run -v /datavol:/datavol -it ubuntu:16.04
Sharing Data Between Containers
* Use run –volume-from option:
–volumes-from=””: Mount all volumes from the given container(s)
Data Only Container
* Launch a busybox image as a data only container
– container in stopped state
docker run --name datavol -v /DataMount busybox:latest /bin/true
* Launch an ubuntu image and mount all volumes from datavol container
docker run -it --volumes-from datavol ubuntu:latest /bin/bash
Pit Falls
* Directory leaks
– always inspect images for data volumes
– always use docker rm -v option to remove any data volume created for the container
– keep a log of data volumes to keep
* Don’t use data volume as a storage during the build process
Container Orchestration
Internal DNS
* Container DNS: 127.0.0.11
* Create a network bridge
docker network create mybridge
docker network inspect mybridge
* Launch a container using mybridge
docker container run -itd –net mybridge –name testdns ubuntu
– Check network setup
docker container inspect –format ‘{{.NetworkSettings.Networks.mybridge.IPAddress}}’ testdns
docker container exec testdns cat /etc/resolv.conf
* Ping from another container
docker container run –rm –net mybridge busybox ping -c 2 testdns
Linking Containers
Container Orchestration with docker-compose
* docker-compose file: docker-compose.yml
– see Compose file version 3 reference
version: "<version>" services: <service>: <key>: <value> <key>: - <value> - <value> networks: <network>: <key>: <value> volumes: <volume>: <key>: <value>
* docker-compose command
– see here
docker-compose [<options>] <command> [<args>...]
Example
Files
FROM node:latest RUN npm install redis ADD example.js /myapp/example.js
version: "3.1" services: web: build: . command: node /myapp/example.js depends_on: - redis ports: - 8080:80 redis: image: redis:latest
// A Simple Request/Response web application // Load all required libraries var http = require('http'); var url = require('url'); var redis = require('redis'); // Connect to redis server running // createClient API is called with // -- 6379, a well-known port to which the // redis server listens to // -- redis, is the name of the service (container) // that runs redis server var client = redis.createClient(6379, 'redis'); // Set the key value pair in the redis server // Here all the keys proceeds with "/", because // URL parser always have "/" as its first character client.set("/", "Welcome to Docker-Compose helper\nEnter the docker-compose command in the URL for help\n", redis.print); client.set("/build", "Build or rebuild services", redis.print); client.set("/kill", "Kill containers", redis.print); var server = http.createServer(function (request, response) { var href = url.parse(request.url, true).href; response.writeHead(200, {"Content-Type": "text/plain"}); // Pull the response (value) string using the URL client.get(href, function (err, reply) { if ( reply == null ) response.write("Command: " + href.slice(1) + " not supported\n"); else response.write(reply + "\n"); response.end(); }); }); console.log("Listening on port 80"); server.listen(80);
Commands
cd exmaple
docker-compose build
docker-compose pull
docker-compose up
References
* Learning Docker – Second Edition by Jeeva S. Chelladhurai; Pethuru Raj; Vinod Singh
* Docker run reference
* Play with Docker