LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

Docker Overview

Docker and the container ecosystem

  • Open source projects: There are several open source projects started by Docker, which are now maintained by a large community of developers.
    • Moby Project is the upstream project upon which the Docker Engine is based. It provides all of the components needed to assemble a fully functional container system.
    • Runc is a command-line interface for creating and configuring containers, and has been built to the OCI specification.
    • Containerd is an easily embeddable container runtime. It is also a core component of the Moby Project.
    • LibNetwork is a Go library that provides networking for containers.
    • Notary is a client and server that aims to provide a trust system for signed container images.
    • HyperKit is a toolkit that allows you to embed hypervisor capabilities into your own applications, presently it only supports the macOS and the Hypervisor.framework.
    • VPNKit provides VPN functionality to HyperKit.
    • DataKit allows you to orchestrate application data using a Git-like workflow.
    • SwarmKit is a toolkit that allows you to build distributed systems using the same raft consensus algorithm as Docker Swarm.
    • LinuxKit is a framework that allows you to build and compile a small portable Linux operating system for running containers.
    • InfraKit is a collection of tools that you can use to define infrastructure to run your LinuxKit generated distributions on.
  • Docker CE and Docker EE: This is the core collection of free-to-use and commercially supported Docker tools built on top of the open source components.
    • Docker Compose: A tool that allows you to define and share multi-container definitions.
    • Docker Machine: A tool to launch Docker hosts on multiple platforms.
    • Docker Hub: A repository for your Docker images.
    • Docker Store: A storefront for official Docker images and plugins as well as licensed products.
    • Docker Swarm: A multi-host-aware orchestration tool.
    • Docker for Mac
    • Docker for Windows
    • Docker for Amazon Web Services
    • Docker for Azure
  • Docker, Inc.: This is the company founded to support and develop the core Docker tools. Docker, Inc. are offer consultative services to companies who wish take their existing applications and containerize them as part of Docker’s Modernize Traditional Apps (MTA) program.

Building Container Images

Introducing the Dockerfile

A Dockerfile is simply a plain text file that contains a set of user-defined instructions. When the Dockerfile is called by the docker image build command.

FROM alpine:latest
LABEL maintainer="Russ McKendrick <[email protected]>"
LABEL description="This example Dockerfile installs NGINX."
RUN apk add --update nginx && \
    rm -rf /var/cache/apk/* && \
    mkdir -p /tmp/nginx/

COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/default.conf /etc/nginx/conf.d/default.conf
ADD files/html.tar.gz /usr/share/nginx/

EXPOSE 80/tcp

ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

Reviewing the Dockerfile in depth

FROM

The FROM instruction tells Docker which base you would like to use for your image.

LABEL

The LABEL instruction can be used to add extra information to the image.

However, using too many labels can cause the image to become inefficient as well, so I would recommend using the label schema detailed at http://label-schema.org/. You can view the containers’ labels with the following Docker inspect command:

docker image inspect -f {{.Config.Labels}} <IMAGE_ID>

RUN

The RUN instruction is where we interact with our image to install software and run scripts, commands, and other tasks.

However, much like adding multiple labels, this is considered to be considered inefficient as it can add to the overall size of the image, which for the most part we should try to avoid.

COPY and ADD

COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/default.conf /etc/nginx/conf.d/default.conf

We are copying two files from the files folder on the host we are building our image on.

ADD files/html.tar.gz /usr/share/nginx/

ADD automatically uploads, uncompresses, and puts the resulting folders and files at the path we tell it to.

EXPOSE

The EXPOSE instruction lets Docker know that when the image is executed, the port and protocol defined will be exposed at runtime. This instruction does not map the port to the host machine, but instead, opens the port to allow access to the service on the container network.

ENTRYPOINT and CMD

If you think of some of the CLI commands you might use, you have to specify more than just the CLI command. You might have to add extra parameters that you want the command to interpret. This would be the use case for using ENTRYPOINT only.

ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

As the CMD is executed, giving us the equivalent of running the following command:

nginx -g daemon off;

Another example of how ENTRYPOINT can be used is the following:

docker container run --name nginx-version dockerfile-example -v

This would be the equivalent of running the following command on our host:

nginx -v

Other Dockerfile instructions

User

The USER instruction lets you specify the username to be used when a command is run. The USER instruction can be used on the RUN instruction, the CMD instruction, or the ENTRYPOINT instruction in the Dockerfile. Also, the user defined in the USER instruction has to exist, or your image will fail to build. Using the USER instruction can also introduce permission issues, not only on the container itself, but also if you mount volumes.

WORKDIR

The WORKDIR instruction sets the working directory for the same set of instructions that the USER instruction can use.

ONBUILD

The ONBUILD instruction lets you stash a set of commands to be used when the image is used in future, as a base image for another container image.
The ONBUILD instruction can be used in conjunction with the ADD and RUN instructions.

ENV

The ENV instruction sets environment variables within the image both when it is built and when it is executed. These variables can be overridden when you launch your image.

Building container images

You can use the other options to limit how much CPU and memory the build process will use. In some cases, you may not want the build command to take as much CPU or memory as it can have. The process may run a little slower, but if you are running it on your local machine or a production server and it’s a long build process, you may want to set a limit. There are also options that affect the networking configuration of the container launched to build our image.

Typically, you don’t use the --file or -f switch, as you run the docker build command from the same folder that the Dockerfile is in. Keeping the Dockerfile in separate folders helps sort the files and keeps the naming convention of the files the same.

It also worth mentioning that, while you are able to pass additional environment variables as arguments at build time, they are used at build time and your container image does not inherit them. This is useful for passing information such as proxy settings, which may only be applicable to your initial build/test environment.

The .dockerignore file is used to exclude those files or folders we don’t want to be included in the docker build as, by default, all files in the same folder as the Dockerfile will be uploaded.

Using a Dockerfile to build a container image

docker image build --file /path/to/your/dockerfile --tag local:dockerfile-example .

Typically, the --file switch isn’t used.

The most important thing to remember is the dot (or period) at the very end. This is to tell the docker image build command to build in the current folder.

Using an existing container

However, it is not recommended or considered to be good practice.

docker image pull alpine:latest
docker container run -it --name alpine-test alpine /bin/sh

To save our container as an image, you need to do something similar to the following:

docker container commit alpine-test local:broken-container

To save the image file, simply run the following command:

docker image save -o broken-container.tar local:broken-container

In the early days of Docker, distributing images that had been prepared in this way was common practice.

Building a container image from scratch

Docker has done some of the hard work for us already, and created an empty TAR file on the Docker Hub named scratch; you can use it in the FROM section of your Dockerfile. You can base your entire Docker build on this, and then add parts as needed.

FROM scratch
ADD files/alpine-minirootfs-3.8.0-x86_64.tar.gz /
CMD ["/bin/sh"]

Using environmental variables

ENV <key> <value>
ENV username admin

Alternatively, you can always use an equals sign between the two:

ENV <key>=<value>
ENV username=admin

With the second ENV example, you can set multiple environmental variables on the same line, as shown here:

ENV username=admin database=wordpress tableprefix=wp

Using multi-stage builds

FROM golang:latest as builder
WORKDIR /go-http-hello-world/
RUN go get -d -v golang.org/x/net/html 
ADD https://raw.githubusercontent.com/geetarista/go-http-hello-world/master/hello_world/hello_world.go ./hello_world.go
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM scratch 
COPY --from=builder /go-http-hello-world/app .
CMD ["./app"]

Storing and Distributing Images

Docker Registry

Deploying your own registry

Docker Registry is distributed as an image from Docker Hub, which makes deploying it as easy as running the following commands:

docker image pull registry:2
docker container run -d -p 5000:5000 --name registry registry:2

Now that we have a copy of the Alpine Linux image, we need to push it to our local Docker Registry, which is available at localhost:5000. To do this, we need to tag the Alpine Linux image with the URL of our local Docker Registry, along with a different image name:

docker image tag alpine localhost:5000/localalpine
docker image push localhost:5000/localalpine

Docker Registry currently supports the following storage options:

  • Filesystem: All images are stored on the filesystem at the path you define. The default is /var/lib/registry.
  • Azure: This uses Microsoft Azure Blob Storage.
  • GCS: This uses Google Cloud storage.
  • S3: This uses Amazon Simple Storage Service (Amazon S3).
  • Swift: This uses OpenStack Swift.

Docker Trusted Registry

One of the components that ships with the commercial Docker Enterprise Edition (Docker EE) is Docker Trusted Registry (DTR). Think of it as a version of Docker Hub that you can host in your own infrastructure. DTR adds the following features on top of the ones provided by the free Docker Hub and Docker Registry:

  • Integration into your authentication services, such as Active Directory or LDAP
  • Deployment on your own infrastructure (or cloud) behind your firewall
  • Image signing to ensure your images are trusted
  • Built-in security scanning
  • Access to prioritized support directly from Docker

Microbadger

Microbadger is a great tool when you are looking at shipping your containers or images around. It will take into account everything that is going on in every single layer of a particular Docker image and give you the output of how much weight it has in terms of actual size or the amount of disk space it will take up.
Another great feature is that Microbadger gives you the option of embedding basic statistics about your images in your Git repository or Docker Hub.

Managing Containers

Docker container commands

Interacting with your containers

attach

The first way of interacting with your running container is to attach to the running process.

docker container attach nginx-test

Pressing Ctrl + C will terminate the process and return your Terminal to normal.

docker container attach --sig-proxy=false nginx-test

Pressing Ctrl + C will detach us from the nginx process, but this time, rather than terminating the nginx process, it will just return us to our Terminal, leaving the container in a detached state.

exec

docker container exec nginx-test cat /etc/debian_version

This will spawn a second process. The second process will then terminate, leaving our container as it was before the exec command was executed.

docker container exec -i -t nginx-test /bin/bash

The -i flag is shorthand for --interactive, which instructs Docker to keep stdin open so that we can send commands to the process. The -t flag is short for --tty and allocates a pseudo-TTY to the session.

Logs and process information

logs

docker container logs --since 2018-08-25T18:00 nginx-test

The logs command shows the timestamps of stdout as recorded by Docker, and not the time within the container.

docker container logs --since 2018-08-25T18:00 -t nginx-test

The -t flag is short for --timestamp; this option prepends the time the output was captured by Docker.

top

The top command is quite a simple one; it lists the processes running within the container you specify.

stats

The stats command provides real-time information on either the specified container or, if you don’t pass a NAME or ID container, on all running containers.

Resource limits

By default, when launched, a container will be allowed to consume all the available resources on the host machine if it requires it.
Typically, we would have set the limits when we launched our container using the run command:

docker container run -d --name nginx-test --cpu-shares 512 --memory 128M -p 8080:80 nginx

However, we didn’t launch our nginx-test container with any resource limits, meaning that we need to update our already running container:

docker container update --cpu-shares 512 --memory 128M --memory-swap 256M nginx-test

Container states and miscellaneous commands

Pause and unpause

docker container pause nginx1

Running docker container ls will show that the container has a status of Up, but it also says Paused.

Note that we didn’t have to use the -a flag to see information about the container as the process has not been terminated; instead, it has been suspended using the cgroups freezer. With the cgroups freezer, the process is unaware it has been suspended, meaning that it can be resumed.

Miscellaneous commands

The create command is pretty similar to the run command, except that it does not start the container, but instead prepares and configures one.

The port command displays the port along with any port mappings for the container.
The diff command prints a list of all of the files that have been added (A) or changed (C) since the container was started.

Docker networking and volumes

Docker networking

docker image pull redis:alpine
docker image pull russmckendrick/moby-counter
docker network create moby-counter

docker container run -d --name redis --network moby-counter redis:alpine
docker container run -d --name moby-counter --network moby-counter -p 8080:80 russmckendrick/moby-counter

docker container exec moby-counter ping -c 3 redis
docker container exec moby-counter cat /etc/hosts
docker container exec moby-counter cat /etc/resolv.conf
# nameserver 127.0.0.11

docker container exec moby-counter nslookup redis 127.0.0.11

docker network create moby-counter2
docker run -itd --name moby-counter2 --network moby-counter2 -p 9090:80 russmckendrick/moby-counter
docker container run -d --name redis2 --network moby-counter2 --network-alias redis redis:alpine

Docker volumes

docker volume create redis_data
docker volume inspect redis_data
docker container run -d --name redis -v redis_data:/data --network moby-counter redis:alpine
docker volume ls

docker container stop redis
docker volume prune

Docker Compose

Our first Docker Compose application

Docker Compose uses a YAML file, typically named dockercompose.yml, to define what your multi-container application should look like.

version: "3"

services:
    redis:
        image: redis:alpine
         volumes:
            - redis_data:/data
        restart: always
    mobycounter:
        depends_on:
            - redis
        image: russmckendrick/moby-counter
        ports:
            - "8080:80"
        restart: always
        
volumes:
    redis_data:

To launch our application, we simply change to the folder that contains your docker-compose.yml file and run the following:

docker-compose up

You may have also spotted the Docker Compose namespace in our multi-container application has prefixed everything with mobycounter. It took this name from the folder our Docker Compose file was being stored in.

Docker Compose commands

when we used Ctrl + C to return to our Terminal, the Docker Compose containers were stopped.

Up and PS

docker-compose up -d

This will start your application back up, this time in detached mode.

Once control of your Terminal is returned, you should be able to check that the containers are running using the following command:

docker-compose ps

When running these commands, Docker Compose will only be aware of the containers defined in the service section of your docker-compose.yml file; all other containers will be ignored as they don’t belong to our service stack.

Config

docker-compose config

If there are no issues, it will print a rendered copy of your Docker Compose YAML file to screen; this is how Docker Compose will interpret your file. If you don’t want to see this output and just want to check for errors, then you can run the following command:

docker-compose config -q

Pull, build, and create

The docker-compose build command can also be used to trigger a build if there are updates to any of the Dockerfiles used to originally build your images.

Scale

The scale command will take the service you pass to the command and scale it to the number you define.

docker-compose scale worker=3
# or
docker-compose up -d --scale vote=3

Kill, rm, and down

If you want to remove everything, you can do so by running the following:

docker-compose down --rmi all --volumes

This will remove all of the containers, networks, volumes, and images (both pulled and built) when you ran the docker-compose up command; this includes images that may be in use outside of your Docker Compose application. There will, however, be an error if the images are in use, and they will not be removed.

Docker App

Docker App is a self-contained binary that helps you to create an application bundle that can be shared via Docker Hub or a Docker Enterprise Registry.

There is a slight change to the docker-compose.yml file we will be using. The version needs to be updated to 3.6 rather than just 3.

docker-app init --single-file mobycounter

This command takes our docker-compose.yml file and embeds it in a .dockerapp file.

version: latest
name: mobycounter
description: An example Docker App file which packages up the Moby Counter application
namespace: masteringdockerthirdedition
maintainers:
  - name: Russ McKendrick
    email: [email protected]
    
---
version: "3.6"

services:
    redis:
        image: redis:alpine
        volumes:
            - redis_data:/data
        restart: always
    mobycounter:
        depends_on:
            - redis
        image: russmckendrick/moby-counter
        ports:
            - "${port}:80"
        restart: always
        
volumes:
    redis_data:
    
---

{ "port":"8080" }

As you can see, it split into three sections; the first contains metadata about the application.

The second section contains our Docker Compose file. You may notice that a few of the options have been replaced with variables. The default value for the port variable is defined in the final section.

Once the .dockerapp file is complete, you can run the following command to save the Docker App as an image:

docker-app save

You can view just the Docker Apps you have active on your host by running this:

docker-app ls
docker-app inspect masteringdockerthirdedition/mobycounter.dockerapp:latest

Now that we have our finished application, we need to push it:

docker-app push
docker image rm masteringdockerthirdedition/mobycounter.dockerapp:latest
docker-app render masteringdockerthirdedition/mobycounter:latest --set port="9090" | docker-compose -f - up
docker-app render masteringdockerthirdedition/mobycounter:latest --set port="9090" | docker-compose -f - ps

Windows Containers

An introduction to Windows containers

The work by Microsoft on the Windows kernel has introduced the same process isolation as found on Linux. Also, like Linux containers, this isolation extends to a sandboxed filesystem and even a Windows registry.

There is also another layer of isolation provided by Windows Containers. Hyper-V isolation runs the container processes within a minimal hypervisor when the container is started. This further isolates the container processes from the host machine. However, there is a cost of a small amount of additional resources needed for each container running with Hyper-V isolation, while these containers will also have an increased start time as the hypervisor needs to be launched before the container can be started.

You can’t manage Hyper-V isolated containers using the standard Hyper-V management tools. You have to use Docker.

Docker Machine

An introduction to Docker Machine

Docker Machine’s biggest strength is that it provides a consistent interface to several public cloud providers. The following locally-hosted hypervisors are supported, such as Oracle VirtualBox and VMware Workstation or Fusion.

Deploying local Docker hosts with Docker Machine

docker-machine create --driver virtualbox docker-local
docker-machine env docker-local

This command returns the following:

export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/russ/.docker/machine/machines/docker-local"
export DOCKER_MACHINE_NAME="docker-local"
# Run this command to configure your shell:
# eval $(docker-machine env docker-local)

Docker Swarm

Introducing Docker Swarm

There are two very different versions of Docker Swarm. There was a standalone version of Docker Swarm; this was supported up until Docker 1.12 and is no longer being actively developed.

Docker version 1.12 introduced Docker Swarm mode. This introduced all of the functionality that was available in the standalone Docker Swarm into the core Docker engine, along with a significant number of additional features.

A Swarm is a collection of hosts, all running Docker, which have been set up to interact with each other in a clustered configuration.

Roles within a Docker Swarm cluster

Swarm manager

The Swarm manager is a host that is the central management point for all Swarm hosts. Swarm manager is where you issue all your commands to control those nodes. You can switch between the nodes, join nodes, remove nodes, and manipulate those hosts.

Each cluster can run several Swarm managers. Swarm managers use the Raft Consensus Algorithm to maintain a consistent state across all of the manager nodes.

Swarm worker

The Swarm workers are those that run the Docker containers.

Creating and managing a Swarm

Adding a Swarm manager to the cluster

docker $(docker-machine config swarm-manager) swarm init \
    --advertise-addr $(docker-machine ip swarm-manager):2377 \
    --listen-addr $(docker-machine ip swarm-manager):2377

Joining Swarm workers to the cluster

docker $(docker-machine config swarm-worker01) swarm join \
     --token $SWARM_TOKEN \
     $(docker-machine ip swarm-manager):2377

Listing nodes

docker node ls

Managing a cluster

Finding information on the cluster

docker node inspect swarm-manager --pretty

Promoting a worker node

You can promote a worker node to a manager node.

docker node promote swarm-worker01

Demoting a manager node

docker node demote swarm-manager

Draining a node

docker node update --availability drain swarm-manager

This will stop any new tasks, such as new containers launching or being executed against the node we are draining. Once new tasks have been blocked, all running tasks will be migrated from the node we are draining to nodes with an ACTIVE status.

To add the node back into the cluster, simply change the AVAILABILITY to active by running this:

docker node update --availability active swarm-manager

Docker Swarm services and stacks

Services

The service command is a way of launching containers that take advantage of the Swarm cluster.

docker service create \
    --name cluster \
    --constraint "node.role == worker" \
    -p:80:80/tcp \
    russmckendrick/cluster

First of all, we can list the services again by running this command:

docker service ls

As you can see, it is a replicated service and 1/1 containers are active.

We haven’t had to care about which of our two worker nodes the service is currently running on.

Run the following commands to scale and check our service:

docker service scale cluster=6

Stacks

Docker has created functionality that allows you to define your services in Docker Compose files.

version: "3"
services:
    cluster:
        image: russmckendrick/cluster
        ports:
            - "80:80"
        deploy:
            replicas: 6
            restart_policy:
                condition: on-failure
            placement:
                constraints:
                    - node.role == worker

The stack can be made up of multiple services, each defined under the services section of the Docker Compose file.

In addition to the normal Docker Compose commands, you can add a deploy section; this is where you define everything relating to the Swarm element of your stack.

docker stack deploy --compose-file=docker-compose.yml cluster
docker stack ls
docker stack services cluster
docker stack ps cluster
docker stack rm cluster

Load balancing, overlays, and scheduling

Ingress load balancing

Docker Swarm has an ingress load balancer built in, making it easy to distribute traffic to our public facing containers.

This means that our application can be scaled up or down, fail, or be updated, all without the need to have the external load balancer reconfigured.

Network overlays

Docker Swarm’s network overlay layer extends the network you launch your containers in across multiple hosts, meaning that each service or stack can be launched in its own isolated network.

Each overlay network has its own inbuilt DNS service, which means that every container launched within the network is able to resolve the hostname of another container within the same network to its currently assigned IP address.

Scheduling

At the time of writing, there is only a single scheduling strategy available within Docker Swarm, called Spread. What this strategy does is to schedule tasks to be run against the least loaded node that meets any of the constraints you defined when launching the service or stack.