Docker

Table of Contents

1. Docker 简介

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后进行发布。

参考:
Introduction to Docker: http://view.dckr.info/DockerIntro.pdf
Get started with Docker: https://docs.docker.com/get-started/
Engine (docker) CLI reference: https://docs.docker.com/engine/reference/commandline/docker/

1.1. Docker VS VM

Docker(准确地说是 Docker Container)和 VM 的区别如图 1 所示。

docker_vs_vm.jpg

Figure 1: Docker Container VS VM

1.2. Docker architecture

Docker 采用 C/S (client-server)架构。 The Docker client (command 'docker') talks to the Docker daemon, which does the heavy lifting of building, running, and distributing your Docker containers. The Docker client and daemon can run on the same system, or you can connect a Docker client to a remote Docker daemon. The Docker client and daemon communicate using a REST API, over UNIX sockets or a network interface.

docker_architecture.gif

Figure 2: Docker Architecture

参考:https://docs.docker.com/engine/docker-overview/

1.2.1. Docker 基本概念(Image 和 Container)

An image is a read-only template with instructions for creating a Docker container. Often, an image is based on another image, with some additional customization. For example, you may build an image which is based on the ubuntu image, but installs the Apache web server and your application, as well as the configuration details needed to make your application run.

A container is a runnable instance of an image. You can create, run, stop, move, or delete a container using the Docker API or CLI. You can connect a container to one or more networks, attach storage to it, or even create a new image based on its current state.

总结:可把 docker 的概念和面向对象概念类比, Image 相当于“类”,而 Container 相当于“对象实例”。

1.3. 安装和启动

各个系统上安装 docker 的过程参见文档:https://docs.docker.com/install/

1.3.1. 以非 root 用户运行

docker daemon 总是以 root 用户运行。

把当前用户加入到 docker 组中,可以实现非 root 用户运行 docker client。如:

$ sudo groupadd docker             # 创建docker组
$ sudo usermod -aG docker $USER    # 把当前用户加入到docker组中

参考:https://docs.docker.com/engine/installation/linux/linux-postinstall/#manage-docker-as-a-non-root-user

1.3.2. 检测是否安装成功

使用命令 docker version 可以检测 docker 是否安装成功。如:

$ docker version
Client:
 Version:      17.03.1-ce
 API version:  1.27
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Tue Mar 28 00:40:02 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      17.03.1-ce
 API version:  1.27 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Fri Mar 24 00:00:50 2017
 OS/Arch:      linux/amd64
 Experimental: true

说明:如果 docker daemon 没有启动,请先启动它,否则上面命令会出错。在 Linux 中,可以用 dockerd 启动 docker daemon。

1.3.3. systemd

用 systemd 管理 docker daemon 可以参考:https://docs.docker.com/engine/admin/systemd/

1.4. bash 中 docker 命令的自动补全

利用 Bash completion,可以实现 docker 命令的自动补全。

Mac 中,docker 命令的自动补全的配置文件在目录/Applications/Docker.app/Contents/Resources/etc/中。

$ ls /Applications/Docker.app/Contents/Resources/etc/
docker-compose.bash-completion docker-machine.bash-completion docker.bash-completion
docker-compose.zsh-completion  docker-machine.zsh-completion  docker.zsh-completion

参考:
Get Bash completion on Docker Mac: http://blog.alexellis.io/docker-mac-bash-completion/

1.5. 查看帮助文档

使用 docker --help 可以查看 docker 帮助文档。
对于具体的命令可以使用 docker COMMAND --help 来查看 COMMAND 的相关文档。如:

$ docker ps --help

Usage:	docker ps [OPTIONS]

List containers

Options:
  -a, --all             Show all containers (default shows just running)
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print containers using a Go template
      --help            Print usage
  -n, --last int        Show n last created containers (includes all states) (default -1)
  -l, --latest          Show the latest created container (includes all states)
      --no-trunc        Don't truncate output
  -q, --quiet           Only display numeric IDs
  -s, --size            Display total file sizes

2. Docker 基本使用

2.1. 第一个 docker 命令(docker run)

安装好 docker 后,可以尝试命令 docker run -i -t ubuntu /bin/bash 作为第一次尝试,如:

$ docker run -i -t ubuntu /bin/bash      # 也可以简写为: docker run -i -t ubuntu
root@203471cd80b7:/#

命令 docker run [OPTIONS] IMAGE [COMMAND] [ARG...]] 表示在一个新的 Container 中运行命令。

运行前面命令时,会执行下面几个步骤:
(1) 首先检测本地有没有名为 ubuntu 的 Image,如果没有则从 Registry 中下载(相当于执行 docker pull ubuntu )。
(2) 然后创建一个新的 Container,相当于执行 docker create
(3) Docker allocates a read-write filesystem to the container, as its final layer.
(4) Docker creates a network interface to connect the container to the default network, since you did not specify any networking options.
(5) Docker starts the container and executes /bin/bash. Because the container is run interactively (due to -i flag) and attached to your terminal (due to -t flag), you can provide input using your keyboard and output is logged to your terminal.
(6) When you type exit to terminate the /bin/bash command, the container stops.

说明:如果你是第一次运行上面命令,得到的输出可能为:

$ docker run -i -t ubuntu /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
c62795f78da9: Pull complete
d4fceeeb758e: Pull complete
5c9125a401ae: Pull complete
0062f774e994: Pull complete
6b33fd031fac: Pull complete
Digest: sha256:c2bbf50d276508d73dd865cda7b4ee9b5243f2648647d21e3a471dd3cc4209a0
Status: Downloaded newer image for ubuntu:latest
root@910b355177d5:/#

进入到 Container 的交互终端后,你可以在 Container 中输入 shell 命令了。不过,这个 ubuntu 仅包含一些必要的组件,很多命令(如 vi 等)没有默认提供(你可以通过 apt-get 安装)。如:

$ docker run -i -t ubuntu /bin/bash
root@203471cd80b7:/# ps
  PID TTY          TIME CMD
    1 ?        00:00:00 bash
   10 ?        00:00:00 ps
root@203471cd80b7:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr
root@203471cd80b7:/# vi
bash: vi: command not found

参考:
https://docs.docker.com/engine/docker-overview/
https://docs.docker.com/engine/reference/commandline/run/

2.1.1. 自定义容器名字

使用 --name 选项可以为容器自定义命名,如:

$ docker run --name myname -it ubuntu /bin/bash              # 命名容器为myname

容器名称是唯一的。如果已经命名了一个叫 name1 的容器,当你要再次使用 name1 这个名称时,需要先用 docker rm 来删除之前创建的同名容器。不过,在执行 docker run 的时候如果添加 --rm 标记,则容器在终止后会立刻删除,这可避免名字冲突。

$ docker run --name myname --rm -it ubuntu /bin/bash         # --rm 容器终止后会立刻删除掉容器

2.2. 基本 docker 命令

2.2.1. 查看本地的 Image (docker images)

使用命令 docker images 可以列出本地系统中存在的 Images。如:

$ docker images
REPOSITORY                     TAG                  IMAGE ID            CREATED             SIZE
ubuntu                         latest               6a2f32de169d        4 days ago          117 MB
2.2.1.1. 查找远程 Registry 中的 Images (docker search)

使用命令 docker search 可以查找远程 Registry 中的 Images。如:

$ docker search nginx
NAME                                     DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
nginx                                    Official build of Nginx.                        5788      [OK]
jwilder/nginx-proxy                      Automated Nginx reverse proxy for docker c...   998                  [OK]
richarvey/nginx-php-fpm                  Container running Nginx + PHP-FPM capable ...   367                  [OK]
jrcs/letsencrypt-nginx-proxy-companion   LetsEncrypt container to use with nginx as...   165                  [OK]
webdevops/php-nginx                      Nginx with PHP-FPM                              77                   [OK]
million12/nginx-php                      Nginx + PHP-FPM 5.5, 5.6, 7.0 (NG), CentOS...   77                   [OK]
h3nrik/nginx-ldap                        NGINX web server with LDAP/AD, SSL and pro...   38                   [OK]
bitnami/nginx                            Bitnami nginx Docker Image                      26                   [OK]
evild/alpine-nginx                       Minimalistic Docker image with Nginx            15                   [OK]
webdevops/nginx                          Nginx container                                 7                    [OK]
1science/nginx                           Nginx Docker images that include Consul Te...   4                    [OK]
blacklabelops/nginx                      Dockerized Nginx Reverse Proxy Server.          4                    [OK]
frekele/nginx                            docker run --rm --name nginx -p 80:80 -p 4...   3                    [OK]
ixbox/nginx                              Nginx on Alpine Linux.                          3                    [OK]
dock0/nginx                              Arch container running nginx                    2                    [OK]
servivum/nginx                           Nginx Docker Image with Useful Tools            2                    [OK]
drupaldocker/nginx                       NGINX for Drupal                                2                    [OK]
xataz/nginx                              Light nginx image                               2                    [OK]
tozd/nginx                               Dockerized nginx.                               1                    [OK]
tianon/nginx                             DEPRECATED; use nginx:*                         1                    [OK]
xutongle/nginx                           nginx http                                      1                    [OK]
unblibraries/nginx                       Baseline non-PHP nginx container                0                    [OK]
c4tech/nginx                             Several nginx images for web applications.      0                    [OK]
watsco/nginx                             nginx:1.11-alpine                               0                    [OK]
funkygibbon/nginx                        nginx + openssl automated build, customisa...   0                    [OK]

2.2.2. 查看系统中的 Container (docker ps)

使用命令 docker ps 可以列出系统中正在运行的 Containers。如:

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
aef40053a33e        ubuntu              "/bin/bash"         40 seconds ago      Up 38 seconds                           festive_newton

命令 docker ps -q ,仅显示 Containers 的 ID,其它字段不显示。如:

$ docker ps -q
aef40053a33e

命令 docker ps -l ,仅显示上一个启动的 Containers。如:

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
aef40053a33e        ubuntu              "/bin/bash"         3 minutes ago       Up 3 minutes                            festive_newton

命令 docker ps -a ,可以把已经处于 stopped 状态(当 Container 的最后一个程序退出后,就处于 stopped 状态了)的 Containers 也显示出来。

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
aef40053a33e        ubuntu              "/bin/bash"         14 hours ago        Exited (0) 14 hours ago                         festive_newton
203471cd80b7        ubuntu              "/bin/bash"         17 hours ago        Exited (0) 16 hours ago                         condescending_swartz
910b355177d5        ubuntu              "/bin/bash"         18 hours ago        Exited (0) 17 hours ago                         clever_montalcini
a87a3eb6b0d0        ubuntu              "/bin/bash"         18 hours ago        Exited (127) 18 hours ago                       hopeful_lovelace
a6e2bc3ec12d        ubuntu              "/bin/bash"         18 hours ago        Exited (0) 18 hours ago                         quizzical_dubinsky
48c93b69807a        ubuntu              "/bin/bash"         22 hours ago        Exited (0) 22 hours ago                         ecstatic_gates
5f43a09eb4a5        ubuntu              "/bin/bash"         22 hours ago        Exited (0) 22 hours ago                         nifty_albattani

2.2.3. 重新启动 Stopped containers (docker start)

在 Container 的最后一个交互终端中 exit 后,Container 会进入 stopped 状态。

$ docker run -i -t ubuntu /bin/bash
root@c3a1518ef18f:/# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 02:21 ?        00:00:00 /bin/bash
root        67     1  0 03:20 ?        00:00:00 ps -ef
root@c3a1518ef18f:/# exit       # 在最后一个shell中执行exit后,container会进入stopped状态
exit
$ docker ps -al
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
c3a1518ef18f        ubuntu              "/bin/bash"         About an hour ago   Exited (0) 1 minutes ago                       wizardly_curran

使用命令 docker start 可以启动一个 stopped 状态的 Container。如:

$ docker start c3a1518ef18f
c3a1518ef18f
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
c3a1518ef18f        ubuntu              "/bin/bash"         About an hour ago   Up 9 seconds                            wizardly_curran

接着,可以使用命令 docker attach 进入到其交互终端。如:

$ docker attach c3a1518ef18f
root@c3a1518ef18f:/#

如果想启动 Stopped containers 的同时进入到其交互终端,可以指定 --attach 选项(或者 -a )。如:

$ docker start --attach c3a1518ef18f
root@c3a1518ef18f:/#

2.2.4. 在 Container 和 host 机器之间复制文件 (docker cp)

可以使用 docker cp 命令在 Container 和 host 机器之间复制文件。如:

$ docker cp c3a1518ef18f:/tmp/file1 .          # 把container中的文件/tmp/file1复制到host机器的当前目录中
$ docker cp file2 c3a1518ef18f:/tmp/           # 把host机器的当前目录中的file2复制到container中的/tmp/目录中

2.3. 从外部访问容器内网络应用(-P, -p)

启动容器时,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用的。

要让外部访问容器内的网络应用,需要通过 -P 进行自动端口配置。如:

$ docker run -d -P nginx
$ docker ps                  # 输出中有端口的映射关系
CONTAINER ID IMAGE ... PORTS ...
e40ffb406c9e nginx ... 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp ...

也可以通过 -p port-on-host:port-on-container 参数来手动指定端口映射。

$ docker run -d -p 8000:80 nginx

3. Background Containers

可以在执行 docker run 时指定 --detach 选项(可简写为 -d ),把 Container 设置在后台运行。如:

$ docker run -d -i -t ubuntu /bin/bash
c3a1518ef18f2d7e82de6f10a9261d74f5220de758f84148968547c70e54b131
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
c3a1518ef18f        ubuntu              "/bin/bash"         7 minutes ago       Up 7 minutes                            wizardly_curran

3.1. 把后台 Container 放到前台 (docker attach)

使用命令 docker attach 可以把后台 Container 放到前台执行。如:

$ docker attach c3a1518ef18f
root@c3a1518ef18f:/#

注:有时 Container 中正在运行的程序并不是一个交互式的 shell,这时你把它放到前台执行也得不到一个交互式 shell。通过下面命令可以开户一个新的交互式 shell:

$ docker exec -it [container-id] bash

参考:https://stackoverflow.com/questions/20932357/docker-enter-running-container-with-new-tty

3.2. 把 Container 放到后台 (^P^Q)

除了在启动时直接把 Container 放到后台外,我们也可以在 Container 的交互终端上输入快捷键^P^Q (即 Ctrl+p, Ctrl+q),把前台的 Container 放入到后台中。

通过执行 docker run 时指定 --detach-keys 选项,可以定制这个快捷键。如下面命令可以定制把 Container 放到后台的快捷键为“Ctrl+x, x”:

$ docker run -d -i -t --detach-keys ctrl-x,x ubuntu /bin/bash

注意:如果你在启动 Container 时没有指定 -i -t ,那么你无法通过快捷键^P^Q 把 Container 放到后台外,这时你杀掉 docker client 进程可以把对应的 Container 放到后台。

3.3. 在 Container 中执行命令 (docker exec)

当 Container 在后台执行时,如果想在 Container 中执行命令,我们可以先用 docker attach 把它放到前台,这时可以提交命令,执行完后,可以用快捷键^P^Q 把 Container 再次放入后台。

我们还可以用命令 docker exec 直接向后台 Container 中提交命令执行。注:也可以向前台 Container 提交命令执行,在 host 机器中打开另外一个终端,执行 docker exec 即可。

$ docker exec -i -t c3a1518ef18f touch /tmp/file1
$ docker exec -i -t c3a1518ef18f ls /tmp/file1
/tmp/file1

3.4. 停止 Container (docker kill/stop)

There are two ways we can terminate our detached container:
(1) Killing it using the docker kill command.
(2) Stopping it using the docker stop command.
The first one stops the container immediately, by using the KILL signal (the KILL signal cannot be intercepted).
The second one is more graceful. It sends a TERM signal, and after 10 seconds, if the container has not stopped, it sends KILL.

4. Building Images Interactively

下面将介绍如何建立一个新的 Image。

4.1. 从 Container 中建立一个新的 Image (docker commit/diff)

首先,我们启动一个全新的 Container,并在 Container 中增加一个文件。如:

$ docker run -i -t ubuntu /bin/bash
root@89342bc98066:/# echo "test" > /tmp/file1
root@89342bc98066:/# exit
exit

通过命令 docker diff 可以显示出对 Container 的所有改变。如:

$ docker diff 89342bc98066          #查看Container 89342bc98066的改变
C /root
A /root/.bash_history
C /tmp
A /tmp/file1

The docker commit command will create a new layer with those changes, and a new image using this new layer.

$ docker commit 89342bc98066                      #这里指定Container ID
sha256:d5c209fda61e4bd20f460c3b76ae1430b0ba361db7ccfe6d22f09c2ffa7ea511    #返回的是新Image的ID

通过 docker images ,可以看到刚刚新建立的 Image。如:

$ docker images
REPOSITORY                     TAG                  IMAGE ID            CREATED             SIZE
<none>                         <none>               d5c209fda61e        2 seconds ago       117 MB
ubuntu                         latest               6a2f32de169d        5 days ago          117 MB

现在,我们可以基于这个新的 Image 来启动 Container。如:

$ docker run -i -t d5c209fda61e /bin/bash
root@46cd0d36b923:/# cat /tmp/file1
test

4.1.1. Tagging images

用 ID 来引用新创建的 Image 不是很方便,我们可以给 Image 指定一个 tag。这可以通过命令 docker tag 来实现。如:

$ docker images     # 下面是运行 docker tag 前的输出
REPOSITORY                     TAG                  IMAGE ID            CREATED             SIZE
<none>                         <none>               d5c209fda61e        2 seconds ago       117 MB
ubuntu                         latest               6a2f32de169d        5 days ago          117 MB
$ docker tag d5c209fda61e mytestimage   # 运行 docker tag,为ID为d5c209fda61e的Image指定tag名mytestimage(相当于mytestimage:latest)
$ docker images     # 下面是运行 docker tag 后的输出
REPOSITORY                     TAG                  IMAGE ID            CREATED             SIZE
mytestimage                    latest               d5c209fda61e        28 minutes ago      117 MB
ubuntu                         latest               6a2f32de169d        5 days ago          117 MB

使用 docker run 时,指定 Images 的 tag 名字可以方便地(比指定 Images 的 ID 要更方便)新建 Container。如:

$ docker run -i -t mytestimage /bin/bash
root@1a972f6a8af6:/#

当然,你可以在用 docker images 命令新建 Image 时就指定 tag 名字。

5. Building Images With A Dockerfile

为了更好地把创建 Images 的过程自动化,我们建立 Image 的步骤放入 Dockerfile 中,然后用命令 docker build 来自动生成 Images。

参考:
Dockerfile reference: https://docs.docker.com/engine/reference/builder/
Best practices for writing Dockerfiles: https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/
Dockerfile 最佳实践:http://blog.csdn.net/candcplusplus/article/details/53366024

5.1. 一个简单的 Dockerfile

下面是利用 Dockerfile 创建 Image 的简单实例。

首先,新建一个目录(如 myimage),在这个目录中创建好 Dockerfile

$ mkdir myimage
$ cd myimage
$ vim Dockerfile

Dokerfile 的内容如下:

# A simple Dockerfile
FROM ubuntu
RUN echo "test" > /tmp/file1

docker build 命令即可建立一个新 Image。如:

$ docker build -t mytestimage2 .       # 同时指定的新Image的tag
Sending build context to Docker daemon 2.048 kB
Step 1/2 : FROM ubuntu
 ---> 6a2f32de169d
Step 2/2 : RUN echo "test" > /tmp/file1
 ---> Running in 12b18a5d8df4
 ---> 7cda0e79c53e
Removing intermediate container 12b18a5d8df4
Successfully built 7cda0e79c53e

运行成功结束后,用 docker images 命令可以看到刚刚建立的 Image。如:

$ docker images
REPOSITORY                     TAG                  IMAGE ID            CREATED             SIZE
mytestimage2                   latest               7cda0e79c53e        7 minutes ago       117 MB
mytestimage                    latest               d5c209fda61e        About an hour ago   117 MB
ubuntu                         latest               6a2f32de169d        5 days ago          117 MB

5.2. 基本格式

Dockerfile 不区分大小写,但还是约定指令使用大写。
# 开头的是注释,行内的 # 都被当做参数(不是 Dockerfile 注释)。

5.3. 常用 Instruction

5.3.1. FROM

FROM 指令用于指定当前构造 image 的 base image,它要位于 Dockerfile 中除空白和注释外的第一行。如:

FROM ubuntu
FROM ubuntu:12.04
FROM training/sinatra
FROM localhost:5000/funtoo

5.3.2. RUN

RUN 用于在 image 构建过程中执行特定的命令,并生成一个(一层)中间镜像。

RUN 有两种格式:

RUN <command>
RUN ["executable", "param1", "param2"]

第一种称为 shell 格式(Linux 中默认使用 sh -c 来执行命令),第二种称为 exec 格式。

shell 格式的例子:

RUN apt-get update

由于 shell 格式默认使用 sh 执行命令,所以上面命令相当于:

RUN ["sh", "-c", "apt-get update"]

exec 格式的例子:

RUN ["apt-get", "update"]

说明 1:后面介绍的 CMD 和 ENTRYPOINT 也有这两种格式。

说明 2:exec 格式中不会启动 shell。

# 下面命令是exec格式,不会启动shell,所以没有shell变量替换,会直接输出字符串$HOME。
RUN [ "echo", "$HOME" ]

下面两种方式可以显示展开后的$HOME:

RUN echo "$HOME"
RUN [ "sh", "-c", "echo $HOME" ]

说明 1:exec 格式后面接的是字符串数组,它采用的 JSON 格式。也就是说命令 RUN ["apt-get", "update"] 中, ["apt-get", "update"] 部分应该是一个合法的 JSON 格式。违反 JSON 格式的常见错误有:

# 下面写法是错误的,因为RUN(exec格式)后的不是合法JSON格式(JSON中要求字符串用双引号,而不能是单引号)
RUN ['apt-get', 'update']
# 下面写法是错误的,字符\在JSON中有特殊含义,应该转义,正确写法为:RUN ["c:\\windows\\system32\\tasklist.exe"]
RUN ["c:\windows\system32\tasklist.exe"]
5.3.2.1. 最佳实践(Cache Busting)

永远将 RUN apt-get update 和 apt-get install 组合成一条 RUN 声明 ,例如:

RUN apt-get update && apt-get install -y \
        package-bar \
        package-baz \
        package-foo

设你有一个 Dockerfile 文件:

# 这是不推荐的写法
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl

构建镜像后,所有的层都在 Docker 的缓存中。假设你后来又修改了其中的 apt-get install,添加了一个包,变为了:

# 这是不推荐的写法
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx

Docker 重新构建时,发现修改后的 RUN apt-get update 指令和之前的完全一样。所以,apt-get update 不会执行,而是使用之前的缓存镜像。因为 apt-get update 没有运行,后面的 apt-get install 可能安装的是过时的 curl 和 nginx 版本。 下面是推荐的写法:

# 这是推荐的写法
RUN apt-get update && apt-get install -y \
    curl \
    nginx

由于一个 RUN 命令会生成一个(一层)中间镜像,所以为了减少 image 大小,不推荐使用两个 RUN 命令:

# 这是不推荐的写法,没必要增加一个(一层)中间镜像,应该合并为一个RUN
RUN apt-get update && apt-get install -y curl
RUN apt-get update && apt-get install -y nginx

5.3.3. CMD

CMD 指令用于指定 docker run 时默认执行的命令。 如果一个 Dockerfile 文件中有多个 CMD,则只有最后一个 CMD 指令会生效。
CMD 仅仅是提供一个默认命令,如果执行 docker run 时指定了程序(如 docker run -i -t ubuntu /bin/bash 指定了执行程序 /bin/bash ),则会执行命令行中指定的程序,CMD 中指定的命令不会被执行。

下面的例子中将使用 figlet (一个把显示字符为大号的特殊形式的小程序)来测试 CMD 和 ENTRYPOINT 的使用。下面是 figlet 的简单例子:

$ figlet -f script hello
 _          _   _
| |        | | | |
| |     _  | | | |  __
|/ \   |/  |/  |/  /  \_
|   |_/|__/|__/|__/\__/

假设你有下面 Dockerfile:

FROM ubuntu
RUN apt-get update
RUN ["apt-get", "install", "figlet"]
CMD figlet -f script hello

根据这个 Dockerfile 构建一个 image,如:

$ docker build -t myfiglet .
...
Successfully built 042dff3b4a8d

运行这个 image,如:

$ docker run myfiglet             # docker run时没有在命令行指定程序,默认会执行CMD中指定的命令(即 figlet -f script hello )。
 _          _   _
| |        | | | |
| |     _  | | | |  __
|/ \   |/  |/  |/  /  \_
|   |_/|__/|__/|__/\__/

不过,一旦我们在执行 docker run 在命令行指定了某个程序(下面例子中这个程序是 bash),则 CMD 中指定的默认命令不会被执行。如:

$ docker run -it myfiglet bash    # 会执行bash,而不是CMD指定的默认命令。
root@7ac86a641116:/#

注:当指定了 ENTRYPOINT 后,CMD 的含义会发生改变,后方将说明。

5.3.4. ENTRYPOINT

ENTRYPOINT 的最佳用处是设置镜像的主命令,允许将镜像当成命令一样来运行。

我们接着上面的例子来介绍 ENTRYPOINT。比如我们想要像下面这样使用 image(这使得镜像变和像命令一样使用):

$ docker run myfiglet salut      # “命令”是在Dockerfile中配置的,而“命令的参数”是在命令行中指定的。相当于执行figlet -f script salut
           _       _
 ___  __ _| |_   _| |_
/ __|/ _` | | | | | __|
\__ \ (_| | | |_| | |_
|___/\__,_|_|\__,_|\__|

在 Dockerfile 中用 ENTRYPOINT 即可实现这个效果。如:

FROM ubuntu
RUN apt-get update
RUN ["apt-get", "install", "figlet"]

# 下面这行指定了“部分的”的默认执行命令,docker run时指定的命令会作为参数追加到ENTRYPOINT所指定的命令上。
ENTRYPOINT ["figlet", "-f", "script"]

假设编译出来的 image 名为 myfiglet。运行 docker run myfiglet salut 相当于在 image 中运行 figlet -f script salut

注:和 CMD 类似,如果指定多个 ENTRYPOINT,则只有最后一个生效。

5.3.4.1. CMD 和 ENTRYPOINT 一起使用

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令 ,换句话说实际执行时,将变为:

<ENTRYPOINT> "<CMD>"

比如,有下面 Dockerfile:

FROM ubuntu
RUN apt-get update
RUN ["apt-get", "install", "figlet"]

ENTRYPOINT ["figlet", "-f", "script"]
CMD ["hello world"]

假设编译出来的 image 名为 myfiglet。运行 docker run myfiglet 相当于在 image 中运行 figlet -f script "hello world"

5.3.4.2. 覆盖 ENTRYPOINT 设置

执行 docker run 时指定 --entrypoint 参数可以覆盖 Dockerfile 中 ENTRYPOINT 中的设置。如:

$ docker run -it --entrypoint bash myfiglet    # 会执行bash,而不会执行figlet。--entrypoint会覆盖Dockerfile中ENTRYPOINT中的设置
root@6027e44e2955:/#

上面命令中“--entrypoint bash”必须在 images 名字“myfiglet”的前面。下面是错误的用法:

$ docker run -it myfiglet --entrypoint bash    # 这是错误的用法!--entrypoint bash不会生效。

5.3.5. COPY 和 ADD

COPY 指令可以复制文件到 image 中,复制过程中源文件的各种元数据(比如读、写、执行权限、文件变更时间等)都会保留。

COPY 指令有两种格式:

COPY <src>... <dest>
COPY ["<src>",... "<dest>"]

如果路径名中包含空格,则必须使用第二种格式。

COPY 的源文件是相对于 docker build 的上下文(比如执行 docker build . 时,编译上下文就是当前目录;执行 docker build dir1 时,编译上下文就是目录 dir1),比如:

COPY /bin/ /tmp/
# 上面命令不是复制本地系统的 /bin目录,而是复制 docker build 所指定目录的 bin 子目录,和下面一样
COPY bin/ /tmp/

“<src>”可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

“<dest>”可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录(或子目录)不存在会在复制文件前先行创建它们。

ADD 和 COPY 类似,只不过是增加一些高级功能。比如复制本地文件中自动解压:

# 下面指令会使foo.tar.gz压缩文件解压到容器的/tmp目录
ADD /foo.tar.gz /tmp/
# 下面指令并不会解压bar.tar.gz,因为它不是本地文件
ADD http://foo.com/bar.tar.gz /tmp/

Docker 官方的最佳实践文档中提到, 尽量地使用 COPY,而尽量不使用 ADD ,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。

5.3.6. ENV

ENV 的作用是设置环境变量,其后的其它指令(如 RUN 等),还是运行时的应用程序,都可以直接使用用 ENV 指令定义的环境变量。

如官方 node 镜像的 Dockerfile 中,就有类似这样的代码:

ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
  && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
  && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
  && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
  && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
  && ln -s /usr/local/bin/node /usr/local/bin/nodejs

注:用 docker run-e 选项也可以设置环境变量,如:

$ docker run -e 'MyEnv=MyValue' ubuntu bash '-c' 'echo $MyEnv'
MyValue

5.3.7. ARG(使--build-arg 中定义的参数在 RUN 指令中可用)

假设执行 docker build 时指定了 --build-arg ,如:

$ docker build --build-arg myarg=myvalue .

在 Dockerfile 中,可以使用 ARG 指令使--build-arg 中定义的参数在 RUN 指令中可用。

ARG myarg
RUN echo $myarg   # 输出 myvalue

注:ARG 和 ENV 类似,它们设置的变量在 RUN 命令中都能使用。和 ENV 不同的是, ARG 所设置的变量,在以后容器运行时就不存在了,只能在构造过程中使用。

5.3.7.1. Predefined ARGs

下面这些变量是内置的 ARG,用户不用再显式地指定了:

HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy

5.3.8. EXPOSE

EXPOSE 指令可以声明运行时容器提供服务端口,比如提供 Web 服务的镜像将使用 EXPOSE 80,而提供 MongoDB 服务的镜像使用 EXPOSE 27017。注:它仅仅是声明,不会主动开启端口。这样,当使用 docker run -P 时,通过 EXPOSE 指定的端口会映射为容器外可访问。

EXPOSE 8080
EXPOSE 80 443
EXPOSE 53/tcp 53/udp

注:就算没有通过 EXPOSE 声明端口,使用 docker run -p <port> 也可以映射这个端口为外部可访问。

5.4. Multi-stage Build

从 Docker 17.05 起,我们可以在一个容器中编译,再复制编译结果到另外一个容器中,这个功能称为“Multi-stage Build”。编译产生的中间文件等仅在编译容器中。

下面是一个例子,在容器 maven:3.6-jdk-11 中编译一个 maven 工程,再把编译得到的 war 包复制到容器 tomcat:9.0 中:

FROM maven:3.6-jdk-11
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package

FROM tomcat:9.0
COPY --from=0 /usr/src/app/target/your-pkg.war /usr/local/tomcat/webapps/

上面命令中 COPY --from=0 表示从第 1 个(编号从数字 0 开始)容器中复制。

当然,你可以为编译容器指定一个名字,如起名为 builder 或者其它( FROM 指令中加个 AS <NAME> 参数即可):

FROM maven:3.6-jdk-11 AS builder
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package

FROM tomcat:9.0
COPY --from=builder /usr/src/app/target/your-pkg.war /usr/local/tomcat/webapps/

参考:https://docs.docker.com/develop/develop-images/multistage-build/

6. Docker CLI

6.1. docker logs

docker logs 可以获取 Container 的日志。你可以通过它查看 Container 默认启动命令(可由 Dockerfile 中 CMD 或 ENTRYPOINT 指定)的输出。

6.2. docker inspect

docker inspect 命令显示 docker 容器或者 image 的相关信息。它返回是的 JSON 格式的信息。

比如,我们想要查看 docker 中 container 的 IP:

$ docker inspect  a30fb0bc1985 |grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",

使用 grep 在 JSON 数据中查找信息不够精确,我们可以通过 --format 参数指定“Go 模版”来精确地找到 container 的 IP,如:

$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'  a30fb0bc1985

6.3. docker top

docker top 命令可以查看 Container 中运行的进程。有些 Container 没有包含 top 命令(甚至 ps 命令也没有),这里 docker top 就很有用了。

6.4. docker export/import(导出/导入 Container 快照)

使用 docker export 命令,可以导出 Container 快照到 tar 文件。如:

$ docker ps -al
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
1a972f6a8af6        mytestimage         "/bin/bash"         48 minutes ago      Exited (127) 27 minutes ago                       eager_wescoff
$ docker export 1a972f6a8af6 > mytestimage.tar

使用 docker import 命令,可以把 Container 快照文件导入为 Image。如:

$ docker import mytestimage.tar test/ubuntu:v1.0
sha256:a795cfb6e4a781c2a6a56fcb7c67fb885a6a447cd1c4e8bfe231786f2c967e02
$ docker images
REPOSITORY                     TAG                  IMAGE ID            CREATED                  SIZE
test/ubuntu                    v1.0                 a795cfb6e4a7        Less than a second ago   97.3 MB
mytestimage2                   latest               7cda0e79c53e        23 minutes ago           117 MB
mytestimage                    latest               d5c209fda61e        About an hour ago        117 MB
ubuntu                         latest               6a2f32de169d        5 days ago               117 MB

6.5. docker save/load(保存和加载 Image)

用导出(docker expor)后再导入(docker import)的方式会丢失所有的历史;而保存(docker save)后再加载(docker load)的镜像不会丢失历史和层(layer)。这意味着使用导出后再导入的方式,你将无法回滚到之前的层(layer),而使用保存后再加载的方式持久化整个镜像,就可以做到层回滚。

6.6. docker system

6.6.1. docker system df(查看空间使用)

使用 docker system df 命令可查询镜像(Images)、容器(Containers)和本地卷(Local Volumes)的空间占用情况。如:

$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              5                   2                   16.43 MB            11.63 MB (70%)
Containers          2                   0                   212 B               212 B (100%)
Local Volumes       2                   1                   36 B                0 B (0%)

命令 docker system df 后增加参数 -v 可显示更多的信息。如:

$ docker system df -v
Images space usage:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE                SHARED SIZE         UNIQUE SIZE         CONTAINERS
my-curl             latest              b2789dd875bf        6 minutes ago       11 MB               11 MB               5 B                 0
my-jq               latest              ae67841be6d0        6 minutes ago       9.623 MB            8.991 MB            632.1 kB            0
<none>              <none>              a0971c4015c1        6 minutes ago       11 MB               11 MB               0 B                 0
alpine              latest              4e38e38c8ce0        9 weeks ago         4.799 MB            0 B                 4.799 MB            1
alpine              3.3                 47cf20d8c26c        9 weeks ago         4.797 MB            4.797 MB            0 B                 1

Containers space usage:

CONTAINER ID        IMAGE               COMMAND             LOCAL VOLUMES       SIZE                CREATED             STATUS                      NAMES
4a7f7eebae0f        alpine:latest       "sh"                1                   0 B                 16 minutes ago      Exited (0) 5 minutes ago    hopeful_yalow
f98f9c2aa1ea        alpine:3.3          "sh"                1                   212 B               16 minutes ago      Exited (0) 48 seconds ago   anon-vol

Local Volumes space usage:

NAME                                                               LINKS               SIZE
07c7bdf3e34ab76d921894c2b834f073721fccfbbcba792aa7648e3a7a664c2e   2                   36 B
my-named-vol                                                       0                   0 B

6.6.2. docker system prune(清理无用空间)

使用 docker system prune 命令可以清理 docker 占用的无用空间。默认地,它会删除:所有已停止的容器、所有未被任何容器所使用的卷、所有未被任何容器所使用的网络、所有悬空的镜像(没有任何 Tag 的镜像)。如:

$ docker system prune
WARNING! This will remove:
	- all stopped containers
	- all volumes not used by at least one container
	- all networks not used by at least one container
	- all dangling images
Are you sure you want to continue? [y/N] y
Deleted Containers:
......

6.7. docker history(查看 Docker Image 构建过程及每一层大小)

使用 docker history 可查看 Docker Image 的构建过程及每一层大小,如:

docker history redis:alpine
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
500703a12fa4   10 months ago   /bin/sh -c #(nop)  CMD ["redis-server"]         0B
<missing>      10 months ago   /bin/sh -c #(nop)  EXPOSE 6379                  0B
<missing>      10 months ago   /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>      10 months ago   /bin/sh -c #(nop) COPY file:c48b97ea65422782…   377B
<missing>      10 months ago   /bin/sh -c #(nop) WORKDIR /data                 0B
<missing>      10 months ago   /bin/sh -c #(nop)  VOLUME [/data]               0B
<missing>      10 months ago   /bin/sh -c mkdir /data && chown redis:redis …   0B
<missing>      10 months ago   /bin/sh -c set -eux;   apk add --no-cache --…   25.4MB
<missing>      10 months ago   /bin/sh -c #(nop)  ENV REDIS_DOWNLOAD_SHA=ba…   0B
<missing>      10 months ago   /bin/sh -c #(nop)  ENV REDIS_DOWNLOAD_URL=ht…   0B
<missing>      10 months ago   /bin/sh -c #(nop)  ENV REDIS_VERSION=6.2.4      0B
<missing>      10 months ago   /bin/sh -c apk add --no-cache   'su-exec>=0.…   1.29MB
<missing>      10 months ago   /bin/sh -c addgroup -S -g 1000 redis && addu…   4.7kB
<missing>      10 months ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      10 months ago   /bin/sh -c #(nop) ADD file:f278386b0cef68136…   5.6MB

7. Docker Compose 简介

Compose 是一个“定义和运行多个 Docker 容器的应用”的工具。 Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。

在工作中,经常需要多个容器相互配合来完成某项任务。例如,一个 Web 项目,除了 Web 服务容器本身,往往还有后端的数据库服务容器、负载均衡容器等等。Compose 可以满足了这样的需求,它允许用户通过一个 docker-compose.yml 模板文件来定义一组相互关联的应用容器(称为一个 project)。

参考:
Overview of Docker Compose: https://docs.docker.com/compose/overview/

7.1. docker-compose.yml 配置文件

7.1.1. 入门例子

关于 docker-compose 的入门例子可以参考:Get started with Docker Compose

7.1.2. 默认创建网络

默认地,使用 docker-compose up 时,会根据“project name”来创建一个网络。“project name”默认为 docker-compose.yml 文件所在文件夹的名字,不过“project name”还可以通过 docker-compose 的命令行参数 -p 或者 COMPOSE_PROJECT_NAME 环境变量来指定。

比如,在目录 myapp 中,有下面 docker-compose.yml 文件:

version: '3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

运行 docker-compose up 命令时,会发生下面事情:

  1. A network called myapp_default is created.
  2. A container is created using db’s configuration. It joins the network myapp_default under the name db.
  3. A container is created using wordpress’s configuration. It joins the network myapp_default under the name wordpress.

这样,网络 myapp_default 中有两个运行容器,在 db 对应的容器中,可以通过名字 wordpress 访问另外一个容器,反之亦然。

注:如果运行 docker-compose -p xxx up ,则创建的网络名字会为 xxx_default,而不是 myapp_default。

参考:
https://docs.docker.com/compose/networking/

7.1.3. 加入到存在的网络(而不是创建网络)

如果不想加入到默认网络,也可以定制 docker-compose.yml 文件中 service 对应 container 所加入的网络。

下面的例子中,容器 web 和 db 都会加入到网络 your_network_name(这个网络必须事先存在)中。

version: "3"

networks:
  net1:
    external:
      name: your_network_name

services:
  web:
    build: .
    ports:
      - "8000:8000"
    networks:
       - net1
  db:
    image: postgres
    ports:
      - "8001:5432"
    networks:
       - net1

上面 docker-compose.yaml 文件中的 your_network_name 必须是存在的网络名字(通过 docker network ls 可以检查一下是否存在),可以通过 docker network create your_network_name 来创建。

参考:https://docs.docker.com/compose/networking/#using-a-pre-existing-network

7.2. docker-compose 常用命令

Docker Compose 常用命令如表 1 所示。

Table 1: Docker Compose 常用命令
命令 说明
build Build or rebuild services
bundle Generate a Docker bundle from the Compose file
config Validate and view the Compose file
create Create services
down Stop and remove containers, networks, images, and volumes
events Receive real time events from containers
exec Execute a command in a running container
help Get help on a command
images List images
kill Kill containers
logs View output from containers
pause Pause services
port Print the public port for a port binding
ps List containers
pull Pull service images
push Push service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
top Display the running processes
unpause Unpause services
up Create and start containers
version Show the Docker-Compose version information

需要说明的是 docker-compose restart 并不会更新环境变量,如果涉及到环境变量的修改,请先执行 docker-compose stop 再执行 docker-compose up ,参考:https://docs.docker.com/compose/reference/restart/

7.3. 使用 journald 日志引擎

默认,docker 使用 json-file 日志引擎,但它有一个不足:如果 docker-compose 中 service 更新并重启,旧 service 产生的日志使用命令 docker-compose logs service-name 无法再查看到了(由于使用 json-file 日志引擎时,日志保存于以容器 ID 命名的某个子目录中,而更新 service 更新并重启后,容器 ID 会变化,这样旧容器 ID 的日志就无法查看到了)。

如果把日志引擎改为 journald ,使用 journalctl CONTAINER_NAME=your_container 命令可以查看旧 service 所产生的日志。

要设置日志引擎为 journald,可以在 docker 配置文件“/etc/docker/daemon.json”中增加下面设置:

{
    "log-driver": "journald"
}

默认配置下,journald 最大限制为所在文件系统容量的十分之一。当然,也可以在配置文件“/etc/systemd/journald.conf”中通过配置 SystemMaxUse 精确地控制日志文件的大小。

要查看容器日志,可以执行:

$ docker-compose logs web                 # 方式一,指定的是service-name,查不到旧日志
$ docker logs myapp_web_1                 # 方式二,指定的是容器名字,查不到旧日志
$ journalctl CONTAINER_NAME=myapp_web_1   # 方式三,指定的是容器名字,可以查到旧日志

默认,journald 的日志会转发到 syslog 中。如果容器产生的日志很多,会导致 syslog 变得很大。可以配置为禁止转发,在文件“/etc/systemd/journald.conf”中增加下面设置即可:

ForwardToSyslog=no
ForwardToWall=no

重启 journald 即可生效 systemctl restart systemd-journald.service

参考:https://docs.docker.com/config/containers/logging/journald/

7.4. 限制 container 产生 log 文件的大小

使用 json-file 日志引擎时,通过参数“max-size”和“max-file”可以配置 docker-compose.yml 文件中 service 对应 container 所产生的 log 文件的大小和个数,如:

  redis:
    image: "redis:alpine"
    ports:
      - "6379:6379"
    logging:
      driver: "json-file"
      options:
        max-file: "3"
        max-size: "50m"

参考:https://docs.docker.com/compose/compose-file/#logging

7.5. docker compose vs docker-compose

新版的 Docker CLI 中集成了 Compose,这样可以直接以子命令的形式使用 Compose 了,如 docker compose up -d

参考:https://docs.docker.com/compose/migrate/#docker-compose-vs-docker-compose

8. Tips

8.1. 配置文件 daemon.json

Docker daemon 的默认配置文件为“/etc/docker/daemon.json”,不存在的话可以创建该文件。

比如,设置 "data-root" 可以修改 docker 的保存数据的目录:

{
    "data-root": "/path/to/data-root"
}

又如,要增加私有仓库("192.168.0.100:8090")可以添加下面设置:

{
    "insecure-registries": ["192.168.0.100:8090"]
}

它的详细配置可参见:https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file

8.2. 设置 Container 和 host 机器之间的共享目录

可以使用 run 命令的 -v /outside/dir:/container/dir (注:两个目录都必须是绝对路径)参数来设置 Container 和 host 机器之间的共享目录。如:

$ docker run -v /Users/cig01/test/:/test -it ubuntu /bin/bash

8.3. stop 所有 container

$ docker stop $(docker ps -a -q)

8.4. 删除所有 images

$ docker rmi -f $(docker images -a -q)

8.5. 删除正在运行 container 的 log

如果使用默认的 json-file 作为日志引擎,执行下面命令可以删除正在运行 container 的 log:

$ sudo truncate -s 0 $(docker inspect --format='{{.LogPath}}' <container_name_or_id>)

上面命令执行完后,使用 docker logs <container_name_or_id> 可验证 log 确实被清空了。

8.6. 通过 image 名字找到所有运行的 container

$ docker ps -q  --filter ancestor=<image-name>

8.7. 限制 container 使用 CPU 资源

比如,限制 container 只能使用 0.75 个 CPU 资源(可能很慢):

$ docker run -it --cpus="0.75" ubuntu /bin/bash

参考:https://docs.docker.com/engine/admin/resource_constraints/

8.8. Docker 私有仓库

推荐使用 Harbor 搭建 Docker 私有仓库,它是由 VMware 公司开源的企业级的 Docker Registry 管理项目。安装步骤可参考:https://github.com/goharbor/harbor/blob/master/docs/installation_guide.md

在 Harbor 控制台中注册用户后,可以使用下面命令登录到私有仓库:

$ docker login <host:port>    # 会提示输入用户名和密码

注:docker 默认使用 https,由于自己搭建的私有仓库往往没有启用 https,所以登录前需要在文件/etc/docker/daemon.json 中把私有仓库地址配置为 insecure-registries:

{ "insecure-registries":["192.168.1.123:8090"] }

把镜像 push 到私有仓库中:

$ docker tag <img_name>:<tag> <host:port>/<project>/<repo>:<tag>
$ docker push <host:port>/<project>/<repo>:<tag>

从私有仓库中 pull 镜像:

$ docker pull <host:port>/<project>/<repo>:<tag>

Author: cig01

Created: <2017-04-01 Sat>

Last updated: <2019-11-20 Wed>

Creator: Emacs 27.1 (Org mode 9.4)