The following is a short tutorial to use Docker, aimed at only the essentials.
Terms
- Docker: the daemon
- Container: an virtualization environment that has its own disk and process
- Layers: docker image are created as union file system of layers, which the layer on top add/delete some files of the layer beneath
- Volume: writable persistent storage for containers
- States: a container can be in any of the following states: created, running, paused, restarting, dead, stopped
Workflow
Pull an image from docker hub and start a container
docker run -d --name container_name repo:tag
Go into a container and launch a shell
docker exec -it container_name /bin/bash
Run a command in a container
docker exec container_name command arg1 arg2
Clean up (stop=SIGTERM, kill=SIGKILL)
docker stop container_name # SIGTERM container
docker kill container_name # SIGKILL container
docker rm container_name # remove from docker memory
Advanced use of docker run
:
docker run --rm \ # remove container when exits
--name pgdocker \ # container name
-e POSTGRES_PASSWORD=letmein \ # env var for the container
-c 512 \ # limit to 50% CPU (1024=100%)
-m 500M \ # limit to 500M memory
-d \ # detach
-p 2345:5432 \ # map container port 5432 as host port 2345
-v /opt/pgdata:/var/lib/postgresql/data # volume binding
Inspection
Show running containers (-a
to show stopped ones as well)
docker ps -a
docker container ls -a
Show processes running in a container
docker top container_name
Continuously show resources in a container (CPU, RAM, I/O, network)
docker stats container_name
Show exposed ports from a container
docker port container_name
Show logs from the container
docker logs container_name
docker logs -f container_name # continuosly while it is running
docker logs -t container_name # prepend timestamp
Show docker daemon system information:
docker info
Show downloaded images
docker images
docker image ls
Show layers of an image
docker history repo:tag
docker image history repo:tag
Show image detail in JSON
docker image inspect repo:tag
Inspect a running container
docker constainer inspect container_name
docker inspect container_name
Images
Download an image only
docker pull repo:tag
docker image pull repo:tag
Dump an image into tarball
docker image save repo:tag filename.tar
docker save --output filename.tar repo:tag
Save a container as new image
docker commit container_name new_repo:new_tag
Create an image from Dockerfile
or a tarball (path can be URL)
docker build -t repo:tag path_to_Dockerfile
docker import path_to_tarball.tar
Load an image file from tarball
docker load path_to_image.tar
Tag an image
docker tag image_hash repo:tag
Remove image
docker image rm repo:tag
docker rmi repo:tag
Remove dangling images
docker rmi -f $(docker images -q -f dangling=true)
Remove all images
docker rmi $(docker images -aq)
Networking
Docker networking is created using libnetwork
, providing independent network
stacks for each containers
Native docker network drivers:
- bridge: created on host using Linux bridge, for containers to talk to each other
- default device
docker0
, can be found inifconfig
in host. With default address assigned (by docker IPAM) to 172.17.0.0/16
- default device
- host: container use host’s network namespace
- none: network inside container but no connection to outside word (completely isolated)
- overlay: create a network for multi-hosts, using Linux bridge and VXLAN. Containers communicate over network infrastructure
- macvlan: using MACVLAN bridge in Linux, joining the same L2 network as host
Create networks
docker network create --driver bridge net_name
docker network create --driver macvlan \
--subnet 192.168.8.0/24 --gateway 192.168.8.1 \
-o parent=enp6s0 vlan_net_name
Show networks
docker network ls
Create a container using a network (afterwards, a veth device is created at host)
docker run -d --net net_name repo:tag
- at docker start up,
bridge
,host
, andnone
networks are created; by default containers are connected to thebridge
network
Create a container (e.g., on macvlan) with a specific IP address
docker run -d --network vlan_net_name --ip=192.168.8.123 repo:tag
Create container with direct connection to another container (i.e., entry added to /etc/hosts
in container)
docker run -d --name redis redis:3.2.0
docker run -d -p 8888:8000 --link redis --name myapp repo:tag
Connect and disconnect an existing container:
docker network connect net_name container_name
docker network disconnect net_name container_name
Volume
Create and remove volume
docker volume create --name=vol_name
docker volume rm vol_name
- volume is created under
/var/lib/docker/volumes/<vol_name>
- files are in
/var/lib/docker/volumes/<vol_name>/_data
, can be modified directly from host
Mount a volume at container creation (create if not exist)
docker run -v vol_name:/mount_point repo:tag
docker run --mount source=vol_name,target=/mount_point repo:tag
Mount a host directory to container
docker run -v /hostpath:/mountpoint repo:tag
docker run --mount type=bind,source=/host_path,target=/mountpoint repo:tag
Check volume mounts in a container
docker inspect -f '{{ .Mounts }}' container_name
Copy files from host to container and vice versa
docker cp hostpath container_name:path
docker cp container_name:path hostpath
Create tarball from a container
docker export -o /hostpath/tarball.tar container_name
Create tarball from volume and save to local, and then restore
docker run --rm --volumes-from vol_name -v $(pwd):/backup repo:tag tar cvf /backup/backup.tar /vol_name
docker run --rm --volumes-from vol_name -v $(pwd):/backup repo:tag tar xvf /backup/backup.tar
docker run --rm --volumes-from vol_name -v $(pwd):/backup repo:tag bash -c "cd /vol_name && tar xvf /backup/backup.tar --strip 1"
- the
--volumes-from
volume will be mounted at root with the same name - the tarball created will contain the full path, hence we can extract with
tar xvf
; otherwise we cancd
to the target directory and extract with option--strip 1
Remove all volumes
docker volume prune
Remove dangling volumes
docker volume rm $(docker volume ls -q -f dangling=true
docker volume ls -q -f dangling=true | xargs -r docker volume rm
Storage
Docker daemon storage drivers:
- overlay2 (default, requires ext4 or xfs)
- aufs (requires ext4 or xfs)
- devicemapper (using direct-lvm)
- btrfs, zfs
- vfs (generic for all file systems, slowest and no copy-on-write)
May need kernel modules to support, e.g., modprobe overlay
Storage driver can be verified with docker info
; change at /etc/docker/daemon.json
.
If using overlay2
, the image layers are stored in /var/lib/docker/overlay2
(and the l
subdir contains symlinks to layers)
Corner cases
Create a container and run iteractively a different command
docker run -it repo:tag command arg1 arg2
-i
is for interactive and-t
is to get a TTY- to detach from the container, hit Ctrl-P Ctrl-Q
- to attach from the container again, do
docker attach container_name
Pause and resume a container
docker container pause container_name
docker container unpause container_name
Rename a container
docker rename container_name new_name
Create a container but not run
docker container create container_name repo:tag
Stop (SIGTERM) a container and restart it:
docker stop container_name # SIGTERM a container
docker start container_name # start a stopped container
docker restart container_name # stop, wait, and start a container
- use case for restart: editing a config file in container and need to apply the change
Block until a container stopped
docker wait container_name
Kill all running containers (docker ps -q
to show only the container names)
docker rm $(docker ps -a -q)
Remove all containers
docker container prune
Remove everything
docker system prune -f --all
Dockerfile
Dockerfile is to create an image using docker build .
, filename is case sensitive. Example:
FROM python:3.5
RUN pip install Flask==0.11.1 redis==2.10.5
RUN useradd -ms /bin/bash admin
USER admin
COPY app /app
WORKDIR /app
CMD ["python", "app.py"]
Build from docker:
docker build -t repo:tag [-f path/to/Dockerfile] directory
where the directory
is the build context, where the commands in Dockerfile is run
Dockerfile instructions:
FROM
: based on which imageLABEL
: just some key-value pair as metadata to the image (optional)ENV
: environment variables set when the container launchedRUN
: commands to run to build the image, must not interactive; each RUN creates a layer- to avoid creating too many layers, chain the commands with
&&
and add line continuation with\\
- to avoid creating too many layers, chain the commands with
ADD
orCOPY
: copy file from host to the image’s file system; copy from URL also allowed- with
ADD
, a local tarball will be extracted to the image destination, butCOPY
will copy as-is - when copied a directory, files specified in
BUILDDIR/.dockerignore
are skipped
- with
EXPOSE
: which ports the container will listen at runtime; for information onlyWORKDIR
: set the working dir for any RUN, CMD, ENTRYPOINT, COPY, ADD to followCMD
: commands to run when launching the containerENTRYPOINT
: similar to CMD; but not overridable- e.g.,
docker run <image> <command>
will run the image but override the command at CMD. But if the command is specified asENTRYPOINT
instead ofCMD
, you rundocker run <image> <args>
will have<args>
appended to the entrypoint command - if both
CMD
andENTRYPOINT
are given, the default command to run will beENTRYPOINT
withCMD
appended; which only theCMD
part can be overridden- e.g.,
ENTRYPOINT "sleep"
andCMD 5
- e.g.,
- both
CMD
andENTRYPOINT
can be in shell form, i.e.,CMD command arg1 arg2
or in exec form, i.e.,CMD ["commnad", "arg1", "arg2"]
- e.g.,
USER
: set the UID (and optionally GID) to be used for any RUN, CMD, ENTRYPOINT to followVOLUME
: creates a mount point with the specified name, new volume will be created at docker run
Multistage build example:
# build environment
FROM node:13.12.0-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json ./
COPY package-lock.json ./
RUN npm ci --silent
RUN npm install react-scripts@3.4.1 -g --silent
COPY . ./
RUN npm run build
# production environment
FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
The as build
in the first FROM
instruction created the container for build
environment which files are created from it. The second FROM
instruction
creates a deployment environment, which the COPY
instruction will refer to
the file created from the build environment.