Docker 常用命令

本文参考自:

RUNOOB.COM Docker 教程

《Dockers - 从入门到实践》

《第一本Docker书》

《Docker 技术入门与实战》

Centos 安装

安装

一键安装

安装命令如下:

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

也可以使用国内 daocloud 一键安装命令:

curl -sSL https://get.daocloud.io/docker | sh

手动安装

卸载旧版

$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

设置仓库:

$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

你也可以设置国内的源:

阿里云:http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

清华:https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo

安装最新版本

$ sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

安装特定版本

先查看有哪些版本:

$ yum list docker-ce --showduplicates | sort -r
docker-ce.x86_64  3:18.09.1-3.el7                     docker-ce-stable
docker-ce.x86_64  3:18.09.0-3.el7                     docker-ce-stable

第二列中,:- 之间的字符串是版本号,上面的例子中,18.09.1 就是版本号。

安装特定版本:

sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io docker-compose-plugin

注意:将 <VERSION_STRING> 替换成版本号,譬如: 18.09.1

启动 Docker

$ sudo systemctl start docker

通过运行 hello-world 映像来验证是否正确安装了 Docker Engine-Community (如果安装成功,它会自动下载执行这个镜像)

$ sudo docker run hello-world

卸载 docker

删除安装包:

sudo yum remove docker-ce docker-ce-cli containerd.io docker-compose-plugin

删除镜像、容器、配置文件等内容:

 sudo rm -rf /var/lib/docker
 sudo rm -rf /var/lib/containerd

概念解释

镜像(Image)

Docker 镜像是由文件系统叠加而成。最底端是一个引导文件系统

在Docker里,root文件系统永远是只读状态,并且Docker利用联合加载(union mount)技术会在root文件系统层上加载更多的只读文件系统。联合加载指的是一次同时加载多个文件系统,但是在外面看起来只能看到一个文件系统。联合加载会将各层文件系统叠加到 一起,这样最终的文件系统会包含所有底层的文件和目录。

Docker将这样的文件系统称为镜像。一个镜像可以放到另一个镜像的顶部。位于下面的镜像称为父镜像(parent image),可以依次类推, 直到镜像栈的最底部,最底部的镜像称为基础镜像(base image)。最 后,当从一个镜像启动容器时,Docker会在该镜像的最顶层自动加载一个读写文件系统。我们在Docker中运行的程序就是在这个读写层中执行的。

当用户需要修改某个文件时,会从相应的某一层只读镜像中将其复制到读写层中,然后进行修改。

容器(container)

当创建一个新容器时,Docker会构建出一个镜像栈,并在栈的最顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。(类似于编程语言中的类文件运行,产生一个实例)

仓库(repository)

镜像存放的仓库,可以用来获取镜像

一个 Docker Registry 中可以包含多个 仓库Repository);每个仓库可以包含多个 标签Tag);每个标签对应一个镜像。

我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

有时仓库会是这种类型:xxxx/ubuntu, 其中 xxxx/ 通常代表某个用户的用户名,表明了这是某个用户自己创建的仓库,而非 Docker 维护的顶层仓库。

核心实现

Docker目前采用了标准的C/S架构,客户端和服务端既可以运行在一个机器上,也可运行在不同机器上通过 socket 或者 RESTful API 来进行通信。

启动 docker 服务后,Docker 守护进程会在宿主主机后台运行,作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。

Docker服务端默认监听本地的 unix:///var/run/docker.sock 套接字,只允许本地的root用户或docker用户组成员访问,我们可以修改它的监听方式:

$ docker daemon -H 0.0.0.0:1234

让服务端监听本地的TCP连接1234端口

我们通常使用的命令,都是针对客户端的,譬如: docker run ... , docker ps 。当我们运行docker 命令时,这些命令会通过套接字或我们上面指定的监听地址来向服务端发送请求,等待服务端返回。

镜像管理

拉取镜像

docker pull 可以拉取镜像到本机

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

[Docker Registry 地址[:端口号]/] 是可选项,用来设置仓库地址,默认是从 Docker Hub 上下载

譬如:

$ docker pull ubuntu:18.04

会从 docker.io 上拉取 ubuntu:18.04

查找镜像

docker search <镜像名> 可以从 Docker Hub 上查找镜像

[wztshine@localhost ~]$ docker search httpd
NAME                                    DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
httpd                                   The Apache HTTP Server Project                  3124                [OK]                
centos/httpd-24-centos7                 Platform for running Apache httpd 2.4 or bui…   36                                      
centos/httpd                                                                            30                                      [OK]
arm32v7/httpd                           The Apache HTTP Server Project                  9                                       
salim1983hoop/httpd24                   Dockerfile running apache config                2                                       [OK]

NAME: 镜像仓库名称

DESCRIPTION: 镜像的描述

OFFICIAL: 是否 docker 官方发布

stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。

AUTOMATED: 是由Docker Hub的自动构建流程创建的

列出本地镜像

docker imagesdocker image ls ,这两个命令是一样的。

[wztshine@localhost ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              f6                  3e225315cacf        47 hours ago        119MB
ubuntu              15.10               9b9cb95443b5        4 years ago         137MB
training/webapp     latest              6fae60ef3446        5 years ago         349MB
  • REPOSITORY:表示镜像的仓库源
  • TAG:镜像的标签,也就是版本
  • IMAGE ID:镜像ID
  • CREATED:镜像创建时间
  • SIZE:镜像大小

列出特定镜像

$ docker image ls ubuntu               # 列出 ubuntu 
$ docker image ls ubuntu:18.04         # 列出特定的 TAG
$ docker image ls -f since=mongo:3.2   # mongo:3.2 以后的镜像
$ docker image ls -f before=mongo:3.2  # mongo:3.2 之前的镜像
$ docker image ls -f label=com.example.version=0.1  # 特定 label 的镜像

输出格式

$ docker image ls -q    # 仅显示 id
5f515359c7f8
05a60462f8ba
fe9198c04d62

$ docker image ls --format "{{.ID}}: {{.Repository}}"  # 显示 id 和 仓库
5f515359c7f8: redis
05a60462f8ba: nginx
fe9198c04d62: mongo

镜像占用空间

$ docker system df

虚悬镜像

有些镜像名字是 none ,是因为当出现新版本后,镜像原本的文件名被新下载的版本占用了,之前的老版本就没了名字了。

$ docker image ls -f dangling=true
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              00285df0df87        5 days ago          342 MB

可以通过如下命令删除这些镜像

$ docker image prune

中间层镜像

我们指导镜像是一层一层的,有些镜像使用了同一个底层的镜像作为依赖,因此这些底层依赖的镜像就是中间层镜像。 docker image ls 默认显示的是顶层镜像。

$ docker image ls -a  # 可以显示所有的镜像,包含中间层镜像

删除镜像

docker rmi <名字或id> 或者 docker image rm <id/名字>

[wztshine@localhost ~]$ docker rmi 3e2253
Untagged: ubuntu:f6
Deleted: sha256:3e225315cacfc54f483cc732176aaf29ae460168220784ee7c630912b972ffd9
Deleted: sha256:2cd15aaedf66e78afca6f1e79923e64c1a6c7bf4eaf85523dd1d63f509d6d5e5

也可以直接 docker rmi ubuntu:f6 来删除

删除一个仓库的所有 redis 镜像:

$ docker image rmi $(docker image ls -q redis)

说明 rmi 支持删除多个镜像列表: docker rmi id1 id2 id3 ...

更新镜像

更新一个镜像文件的步骤:

  1. 用镜像运行一个交互式的容器
[wztshine@localhost ~]$ docker run -it ubuntu:15.10 /bin/bash
  1. 更新并退出:apt-get update
root@e6537aea240a:/# apt-get update
Ign http://archive.ubuntu.com wily InRelease
Ign http://archive.ubuntu.com wily-updates InRelease
Ign http://archive.ubuntu.com wily-security InRelease
......
root@e6537aea240a:/# exit
  1. 通过命令 docker commit 来提交容器副本
  • -m:描述;
  • -a:作者;
  • 36537aea:容器id;
  • ubuntu:v2.0:版本
[wztshine@localhost ~]$ docker commit -m="has update" -a="wztshine" e6537aea ubuntu:v2.0
sha256:40f24a5226d780c80e9a8a6427c522f8ce25b7ee54d66eaeb582c2c38b7c80ca

查看一下:

[wztshine@localhost ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              v2.0                40f24a5226d7        6 seconds ago       137MB

构建镜像

从零构建一个镜像:

在一个空白文件夹中,建立一个 Dockerfile 文件

Dockerfile 文件包含了一组指令来说明如何构建镜像,内容如下:

FROM    centos:6.7
MAINTAINER      Test "Test@sl.com"
RUN     /bin/echo 'root:123456' |chpasswd
RUN     useradd test
RUN     /bin/echo 'test:123456' |chpasswd
RUN     /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
EXPOSE  22
CMD     /bin/bash

每一条指令前缀都是大写字母。

  • FROM:继承自那个镜像,也就是说在哪个镜像的基础上构建
  • MAINTAINER:指定作者
  • RUN:执行命令,并将命令结果打包进镜像。(也就是对镜像进行预先修改,这里进行的是修改密码,添加用户:test等操作),每个run命令,都会创建一个镜像层
  • EXPOSE:暴露的端口22,允许外部连接这个端口。
  • CMD:由此镜像创建的容器启动后,自动执行的主进程命令。多个CMD命令,只有最后一个CMD生效

镜像在构建过程中,每一个命令都会单独创建一层镜像,最终所有镜像层叠加在一起,变成了最终镜像。

创建镜像

在 Dockerfile 文件所在目录下,执行如下命令:

[wztshine@localhost ~]$ docker build -t centos:6.7 .
Sending build context to Docker daemon    238MB
Step 1/8 : FROM    centos:6.7
6.7: Pulling from library/centos
cbddbc0189a0: Pull complete 
Digest: sha256:4c952fc7d30ed134109c769387313ab864711d1bd8b4660017f9d27243622df1
Status: Downloaded newer image for centos:6.7
 ---> 9f1de3c6ad53
Step 2/8 : MAINTAINER      Test "Test@sl.com"
 ---> Running in 66f3d9dcaf5d
Removing intermediate container 66f3d9dcaf5d
 ---> 300267754ddd
Step 3/8 : RUN     /bin/echo 'root:123456' |chpasswd
 ---> Running in 27b24b53f996
Removing intermediate container 27b24b53f996
 ---> 9e4ad9273e5e
Step 4/8 : RUN     useradd test
 ---> Running in b0b830439dc4
Removing intermediate container b0b830439dc4
 ---> 27af3cc1c8ba
Step 5/8 : RUN     /bin/echo 'test:123456' |chpasswd
 ---> Running in 3edb1be95fd6
Removing intermediate container 3edb1be95fd6
 ---> c59f2ded7165
Step 6/8 : RUN     /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
 ---> Running in 88f535a9109b
Removing intermediate container 88f535a9109b
 ---> c46e8516fbf8
Step 7/8 : EXPOSE  22
 ---> Running in d84ab8e305e9
Removing intermediate container d84ab8e305e9
 ---> fce8cb8ef4dc
Step 8/8 : CMD     /bin/bash
 ---> Running in 9973f4a4dccd
Removing intermediate container 9973f4a4dccd
 ---> d9f083b70267
Successfully built d9f083b70267
Successfully tagged centos:6.7

-t:指定构建的镜像名,想要指定仓库可以写成:仓库名/镜像名:标签

查看是否成功:

[wztshine@localhost ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              6.7                 d9f083b70267        43 seconds ago      191MB

创建个容器试试:

[wztshine@localhost ~]$ docker run -it centos:6.7 
[root@32c3f948f232 /]# id test
uid=500(test) gid=500(test) groups=500(test)

上下文

在构建镜像时, docker build -t centos:6.7 . 中的 . 代表了构建的上下文。Docker会在构建镜像时将构建上下文和该上下文中的文件和目录上传到Docker守护进程。这样Docker守护进程就能直接访问用户想在镜像中存储的任何代码、文件或者其他数据。

譬如 Dockerfile 中这么写:

COPY ./package.json /app/

这句话不是代表拷贝当前目录下的 ./package.json 文件到容器的 /app/ ,而是拷贝 上下文 目录下的 package.json 。也就是说,Dockerfile 中的相对路径,是相对于 上下文 而言的。

创建标签

docker tag <镜像id> <镜像名>:<tag名>

给 id 为 d9f083 的镜像加个tag:self

[wztshine@localhost ~]$ docker tag d9f083 centos:self
[wztshine@localhost ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              6.7                 d9f083b70267        12 minutes ago      191MB
centos              self                d9f083b70267        12 minutes ago      191MB

多阶段构建

假设我们想写一个 go 应用,想将它打包成镜像,我们要先开发编译 go 应用(第一个阶段),然后将编译好的 go 应用构建一个镜像(第二阶段)。之所以分成两个阶段,是因为第一个阶段开发和编译应用,会生成一些不需要的文件,如果直接在此基础上构建,会造成镜像无意义的过大。

本小节来自:https://zhuanlan.zhihu.com/p/403381021

一、不使用多阶段构建

不使用多阶段构建时,我们通常会创建两dockerfile文件,一个先用来开发和编译应用,另一个用于构建精简的生产镜像。这样能比较大限度的减小生产镜像的大小。

譬如先创建一个用来编译 go 语言应用的镜像:

FROM golang:1.16
WORKDIR /go/src
COPY app.go ./
# go编译
RUN go build  -o myapp app.go

构建镜像:

[root@localhost dockerfiles]# docker build -t builder_app:v1 .

通过镜像生成容器:

# docker create --name builder builder_app:v1

create 会创建一个容器,但是并不会运行这个容器

--name 为容器命名,否则会默认生成一个容器名字。

将容器中我们编译好的 go 应用,复制到主机:

# docker cp builder:/go/src/myapp ./

cp 命令可以将容器的内容复制到主机本地

至此,我们已经获取到编译好的 go 应用, 然后我们可以制作一个新的镜像:

FROM scratch
WORKDIR /server
COPY myapp ./
CMD ["./myapp"]

构建镜像:

#docker build  --no-cache -t server_app:v1 .
二、多阶段构建

在一个 Dockerfile 中可以写多个 FROM 命令,每个 FROM 可以使用不同的镜像,这样每个 FROM 都会开启一个新的阶段。在多阶段构建中,我们可以将资源从一个阶段复制到另一个阶段,在最终镜像中只保留我们所需要的内容。

还是上面的例子,可以写成这样:

# 阶段1
FROM golang:1.16
WORKDIR /go/src
COPY app.go ./
RUN go build app.go -o myapp

# 阶段2
FROM scratch
WORKDIR /server
COPY --from=0 /go/src/myapp ./  # from=0 代表了从上面第一阶段生成的镜像中复制文件
CMD ["./myapp"]

--from 不仅可以从前面的阶段获取文件,还可以从网络或者本地镜像中获取: COPY --from httpd:latest /usr/local/apache2/conf/httpd.conf ./httpd.conf

构建镜像:

# docker build --no-cache  -t server_app:v2 .

在编写 Dockerfile 时,还可以给阶段起别名

# 阶段1命名为 builder
FROM golang:1.16 as builder  # as builder 起别名
WORKDIR /go/src
COPY app.go ./
RUN go build app.go -o myapp

# 阶段2
FROM scratch
WORKDIR /server
COPY --from=builder /go/src/myapp ./   # from=builder 
CMD ["./myapp"]

只构建某个阶段:

譬如上面的例子中有两个阶段,我们可以只构建第一阶段的 builder 阶段。

# docker build --target builder -t builder_app:v2 .

将上一阶段作为基础镜像:

# 阶段1命名为 builder
FROM golang:1.16 as builder
WORKDIR /go/src
COPY app.go ./
RUN go build app.go -o myapp

# 阶段2
FROM builder as builder_ex
ADD dest.tar ./
...

多种架构的镜像

每种镜像,都有自己的系统架构,例如 Linux x86_64 的系统中只能使用 Linux x86_64 的镜像。

而用户在获取镜像时,是如何自动获取自己相应系统架构的镜像的呢?

这是因为每个镜像仓库,都有一个自己的 manifest list,你可以这样查看:

$ docker manifest inspect golang:alpine

查看 golang:alpine 这个仓库的说明文件

创建

我们在自己构建一个镜像时,可以自己创建一个 manifest 列表:

$ docker manifest create username/test \
      username/x8664-test \
      username/arm64v8-test

username/test 创建两个架构内容:username/x8664-test , username/arm64v8-test

设置内容

设置manifest 内容:

# $ docker manifest annotate [OPTIONS] MANIFEST_LIST MANIFEST
$ docker manifest annotate username/test \
      username/x8664-test \
      --os linux --arch x86_64

$ docker manifest annotate username/test \
      username/arm64v8-test \
      --os linux --arch arm64 --variant v8
查看内容
$ docker manifest inspect username/test
推送列表
$ docker manifest push username/test

其他制作镜像方式

导入
$ docker import \
    http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz \
    openvz/ubuntu:16.04

从网络位置创建一个镜像到本地:openvz/ubuntu:16.04

查看镜像历史
$ docker history openvz/ubuntu:16.04
保存镜像

使用 docker save 命令可以将镜像保存为归档文件。

$ docker save alpine -o filename
$ file filename
filename: POSIX tar archive

容器命令

启动docker服务

systemctl start docker

docker 安装成功后,我们需要启动 docker 守护进程。守护进程作为服务端监听 /var/run/docker.sock 这个套接字文件,来获取来自客户端的 Docker 请求。

查看docker命令

查看所有命令

docker

查看某个详细命令

docker <command> --help

e.g.

docker run --help
docker logs --help

启动容器

docker run 用来启动容器

[wztshine@localhost ~]$ docker run ubuntu:15.10 /bin/echo 'hh'
hh

docker run <镜像> <命令>

  • docker : docker 命令的二进制文件
  • run : 执行一个镜像
  • ubuntu:15.10 , 指定运行的镜像名,如果本地没有这个镜像,会自动去仓库中下载
  • /bin/echo 'hh' : 这是在镜像中要执行的命令

上面的容器执行完命令后,输出了结果:hh

--name <container_name> 可以手动指定容器名字,而不是自动生成容器名字。

[wztshine@localhost ~]$ docker run --name test ubuntu:15.10 /bin/echo 'hh'

交互模式

-it 选项可以用来开启一个交互式的docker

[wztshine@localhost ~]$ docker run -it ubuntu:15.10 /bin/bash
root@912f52b64384:/# ls               <--- 此时已经进入交互模式,执行ls命令
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@912f52b64384:/# exit             <--- 退出此交互模式
exit
  • -i:代表交互模式,即保持容器的 STDIN 处于打开状态
  • -t:打开一个伪终端,将终端绑定到标准输入

使用了上面两个参数,这样我们就能开启一个终端,并与之交互。

后台模式

-d 选项使用后台模式运行容器。使用后台模式不会进入容器,只会返回一个容器 id

[wztshine@localhost ~]$ docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
ded25db4a4d155e39ccfcf8acd766a2e741359bb64f0a775b3487f469663ed69    <--- 打印的容器id

bash 的 -c 选项,是从字符串读取命令,这是 shell 的知识,而不是 docker 的知识。

后台模式执行容器,会返回一个容器 id

启动停止的容器

docker start 用来启动停止的容器

[wztshine@localhost ~]$ docker ps -a     <--- 列出所有的容器
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                       PORTS               NAMES
ded25db4a4d1        ubuntu:15.10        "/bin/sh -c 'while t…"   12 minutes ago      Exited (137) 4 minutes ago                       agitated_wright
912f52b64384        ubuntu:15.10        "/bin/bash"              17 minutes ago      Exited (0) 17 minutes ago                        objective_brattain


[wztshine@localhost ~]$ docker start ded25db   <--- 启动某个容器id
ded25db


[wztshine@localhost ~]$ docker ps              <--- 列出活动的容器
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
ded25db4a4d1        ubuntu:15.10        "/bin/sh -c 'while t…"   12 minutes ago      Up 6 seconds                            agitated_wright

重启容器

$ docker restart <容器 ID>

容器自动重启

我们还可以在容器运行时,指定让它遇到错误自动重启:

$ docker run --restart=always --name daemon_dave -d ubuntu /
bin/sh -c "while true; do echo hello world; sleep 1; done"

--restart 指定容器自动重启的模式:

  • always:无论容器退出码是什么,都会自动重启容器
  • on-failure:容器退出码非0时,才自动重启(还可以写成 on-failure:5 来设置最多重启5次)

查看容器内部进程

docker top <容器id>

[wztshine@localhost ~]$ docker top dea5b
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                3701                3685                0                   21:30               ?                   00:00:00            python app.py

容器状态

docker stats <容器> <容器> ... 可以查看一组容器的状态:cpu,内存,网络,IO等信息

$ docker stats dea5b

深入容器状态

docker inspect 命令会对容器进行详细的检查,然后返回其配置信 息,包括名称、命令、网络配置以及很多有用的数据

$ sudo docker inspect daemon_dave
[{
"ID": "
c2c4e57c12c4c142271c031333823af95d64b20b5d607970c334784430bcbd0f
",
"Created": "2014-05-10T11:49:01.902029966Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true; do echo hello world; sleep 1; done"
],
"Config": {
"Hostname": "c2c4e57c12c4",

这个命令还支持显示特殊格式:

$ sudo docker inspect --format '{{.Name}} {{.State.Running}}' \
daemon_dave bob_the_container
/daemon_dave false
/bob_the_container false

上面的例子显示出两个容器的名字和运行状态

列出容器

列出所有容器

docker ps -a 可以列出所有容器(不管是否活动)

[wztshine@localhost ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
ded25db4a4d1        ubuntu:15.10        "/bin/sh -c 'while t…"   About a minute ago   Up About a minute                       agitated_wright

显示的信息依次是:

容器id;镜像;执行的命令;创建时间;容器状态;端口和连接类型(tcp/udp);自动分配的容器名字;

其中容器状态有如下几种:

  • created(已创建)
  • restarting(重启中)
  • running 或 Up(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)

列出活动容器

仅显示活动中的容器

docker ps

列出最后一个创建的容器

docker ps -l

查看容器内的日志

docker logs 可以用来查看容器内部的标准输出

[wztshine@localhost ~]$ docker logs agitated_wright   <--- 可以使用容器名字,也可以使用容器id
hello world
hello world
hello world
hello world
hello world
。。。。。

agitaged_wright 是通过 docker ps 显示的容器名字

docker logs -f 可以持续查看输出,类似于 tail -f

docker logs --tail 10 可以获取最后10行

docker logs -f -t 可以附带时间戳信息

停止容器

docker stop 用来停止一个容器

[wztshine@localhost ~]$ docker stop agitated_wright  # 可以写容器名或者容器id
agitated_wright

[wztshine@localhost ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

进入容器

[wztshine@localhost ~]$ docker run -itd ubuntu:15.10 /bin/bash
29f69fe50b7fd6eeee2ac30039e738737317930a63173e5bd355d4beb8e96d3b

在使用 -d 参数时,容器启动后会进入后台模式,并直接返回一个容器id。此时想要进入容器,可以通过以下指令进入:

  • docker attach

这个命令进入容器后,当退出容器时,会停止这个容器。

[wztshine@localhost ~]$ docker attach 29f6     <--- 进入容器
root@29f69fe50b7f:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@29f69fe50b7f:/# exit                      《--- 退出
exit
[wztshine@localhost ~]$ docker ps              《--- 再看一下,容器没了
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
  • docker exec

推荐大家使用 docker exec 命令,因为这个命令退出容器终端时,不会导致容器的停止。

这个命令用来在容器中启动新的进程。

[wztshine@localhost ~]$ docker ps    <--- 查看有无活动容器
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
29f69fe50b7f        ubuntu:15.10        "/bin/bash"         About a minute ago   Up About a minute                       optimistic_nightingale

[wztshine@localhost ~]$ docker exec -it 29f6 /bin/bash   <--- 在容器中以交互模式执行 /bin/bash
root@29f69fe50b7f:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@29f69fe50b7f:/# exit                               <--- 退出容器
exit

exec -it 会以前台交互模式执行命令

exec -d 会以后台模式在容器中执行命令

导出/导入容器

docker export 导出成快照

[wztshine@localhost ~]$ docker ps -a      《--- 查看所有的容器
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
29f69fe50b7f        ubuntu:15.10        "/bin/bash"              6 minutes ago       Exited (0) 2 minutes ago                       optimistic_nightingale


[wztshine@localhost ~]$ docker export 29f6 > 29f6.tar  《--- 将id为 29f6 的容器导出成tar


[wztshine@localhost ~]$ ls
29f6.tar  dfg  公共  模板  视频  图片  文档  下载  音乐  桌面

导入成镜像

ubuntu:[wztshine@localhost ~]$ cat 29f6.tar | docker import - ubuntu:f6   <--- 从tar导入成镜像 ubuntu:f6,冒号后面代表版本号
sha256:3e225315cacfc54f483cc732176aaf29ae460168220784ee7c630912b972ffd9


[wztshine@localhost ~]$ docker image        <---可以看出镜像库多了一个f6版本的镜像
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              f6                  3e225315cacf        6 seconds ago       119MB
ubuntu              15.10               9b9cb95443b5        4 years ago         137MB

docker import 29f6.tar ubuntu:v4 这种方式也可以导入。

docker images 也可以列出所有镜像

删除容器

docker rm 或者 docker container rm 用来删除容器

docker容器建立后,不主动删除,是一直存在的,如果要强制删除运行的容器,可以使用 -f 参数:docker rm -f <id>

[wztshine@localhost ~]$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
0478af7610bd        ubuntu:15.10        "/bin/bash"              25 hours ago        Exited (0) 25 hours ago                       magical_faraday
035d693a9276        ubuntu:15.10        "/bin/echo 'Hello wo…"   25 hours ago        Exited (0) 25 hours ago                       keen_hodgkin


[wztshine@localhost ~]$ docker rm 0478    《--- 删掉id为0478的容器
0478

清除所有已终止的容器

$ docker container prune

删除所有容器

$ docker rm `sudo docker ps -a -q`

-q 用来只显示容器的 id

使用网络

外部访问

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个主机端口到内部容器开放的网络端口。

$ docker run -d -P nginx:alpine

$ docker container ls -l
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
fae320d08268        nginx:alpine        "/docker-entrypoint.…"   24 seconds ago      Up 20 seconds       0.0.0.0:32768->80/tcp   bold_mcnulty

使用 docker container ls 可以看到,本地主机的 32768 被随机映射到了容器的 80 端口。此时访问本机的 32768 端口即可访问容器内 NGINX 默认页面。

端口映射

映射端口

-p <本机端口>:<容器端口> 可以用来映射某个端口到主机。

$ docker run -d \
    -p 80:80 \
    -p 443:443 \
    nginx:alpine

将容器的 80 端口映射到主机的 80 端口, 将 443 端口同样映射到主机 443

映射网络地址

$ docker run -d -p 127.0.0.1:80:80 nginx:alpine

映射任意地址

$ docker run -d -p 127.0.0.1::80 nginx:alpine

将容器的 80 端口随机映射到本地的任意端口

还可以使用 udp 标记来指定 udp 端口:

$ docker run -d -p 127.0.0.1:80:80/udp nginx:alpine

随机映射

-P 参数,可以将内容的 Dockerfile 中通过 EXPOSE 暴露出的端口,随机映射到本机的端口上。

$ docker run -P nginx:alpine

查看映射

$ docker port <容器id> 80

查看某个容器的 80 端口映射到哪个本机地址

web 服务

拉取镜像

docker pull training/webapp
docker run -d -P training/webapp python app.py

查看容器:

[wztshine@localhost ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                     NAMES
dea5b5682a85        training/webapp     "python app.py"     55 seconds ago      Up 52 seconds       0.0.0.0:32768->5000/tcp   determined_banzai

发现 PORTS 字段的内容:0.0.0.0:32768->5000/tcp

说明 Docker 开放了 5000 端口( Python Flask 的默认端口)映射到主机端口 32768 上。

查看本机ip

[wztshine@localhost ~]$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
		......

可以看到本机 IP 是 172.17.0.1 ,访问 ip:port,也就是 http://172.17.0.1:32768/,就可以看到:Hello World!

访问 Flask 网站

$ curl 172.17.0.1:32768
Hello World!

容器互联

网络连接

容器互联互通

docker 有一个连接系统允许将多个容器连接在一起,共享连接信息。

docker 连接会创建一个父子关系,其中父容器可以看到子容器的信息。

建立网络

docker network create -d <类型> <网络名字>

[root@bogon ~]# docker network create -d bridge test-net
c6cdcc9908765692564dd1a01ef3db86a2a016af3e9e628827345eea2d7f113d
  • -d:指定网络类型,有 bridge、overlay。

查看网络信息

docker network ls 命令列出当前系统中的所有网络

docker network inspect <network_name> 可以用来查看网络信息

删除网络

docker network rm <network> 来删除一个网络

在网络中连接容器

建立两个容器test1test2,并给它们指定网络在一个网络里

[root@bogon ~]# docker run -itd --name test1 --network test-net ubuntu:15.10 /bin/bash
2ef95f89cf6e4b401ee5045029a8c2f9ca39eaee683e8521bbb19519dd3803bd


[root@bogon ~]# docker run -itd --name test2 --network test-net ubuntu:15.10 /bin/bash
f4929e2fd89009161fe60591fc5b871880ede081523e003945d5d801983ea566

--network 指定在哪个网络

当我们在一个网络中启动一个容器时,docker 会自动查找这个网络下的其他容器,并将这些容器的地址写入到当前启动的容器的 /etc/hosts 文件中。

进入其中一个容器,并ping另一个容器名,能ping通。

[root@bogon ~]# docker exec -it test1 /bin/bash
root@2ef95f89cf6e:/# ping test2
PING test2 (172.18.0.3) 56(84) bytes of data.
64 bytes from test2.test-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.118 ms
64 bytes from test2.test-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.098 ms
64 bytes from test2.test-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.093 ms
^C
--- test2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.093/0.103/0.118/0.010 ms

p.s.:如果上面 ping test2 时,提示没有 ping 这个命令

可以执行apt-get update,然后执行apt install iputils-ping,如果apt-get update也执行不成功

root@2ef95f89cf6e:/# apt-get update
Ign http://archive.ubuntu.com wily InRelease         
Ign http://archive.ubuntu.com wily-updates InRelease 
Ign http://archive.ubuntu.com wily-security InRelease
Ign http://archive.ubuntu.com wily Release.gpg
Ign http://archive.ubuntu.com wily-updates Release.gpg
Ign http://archive.ubuntu.com wily-security Release.gpg
Ign http://archive.ubuntu.com wily Release
Ign http://archive.ubuntu.com wily-updates Release
Ign http://archive.ubuntu.com wily-security Release
30% [Waiting for headers]
Err http://archive.ubuntu.com wily/main Sources
  404  Not Found [IP: 91.189.88.142 80]
Err http://archive.ubuntu.com wily/restricted Sources
  404  Not Found [IP: 91.189.88.142 80]

说明你的更新源是国外的源,需要指定国内镜像源:

root@1b4671904bfa:/#  mv /etc/apt/sources.list /etc/apt/sources.list.bak
root@1b4671904bfa:/#  echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list
root@1b4671904bfa:/#  echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
root@1b4671904bfa:/#  echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list
root@1b4671904bfa:/#  echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list

再重新 apt-get updateapt install iputils-ping,就好了。

连接已经运行的容器

向网络中添加一个已经运行中的容器:

$ docker network connect <network_name> <container>

从网络删除容器

$ docker network disconnect <network_name> <container>

容器的互联(linking)是一种让多个容器中应用进行快速交互的方式,它会在源和接收容器之间创建连接关系

--link参数的格式为--link name:alias,其中name是要连接的容器名称,alias是这个连接的别名。

--link参数的格式为:

--link name:alias,其中 name 是要连接的容器名称,alias 是这个连接的别名。

譬如:

$ docker run -d --name db training/postgres

$ docker run -d -P --name web --link db:db training/webapp python app.py

Docker相当于在两个互联的容器之间创建了一个虚机通道,而且不用映射它们的端口到宿主主机上

在上面的例子中,web 容器的 /etc/hosts 文件中会写入一条 db 容器的记录,因此 web 容器可以访问 db 容器

配置DNS

我们可以在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS:

{
  "dns" : [
    "114.114.114.114",
    "8.8.8.8"
  ]
}

设置后,启动容器的 DNS 会自动配置为 114.114.114.114 和 8.8.8.8。

配置完,需要重启 docker 才能生效: systemctl restart docker

查看容器的 DNS 是否生效可以使用以下命令,它会输出容器的 DNS 信息:

--rm:退出时直接删掉容器

[root@bogon ~]# docker run -it --rm ubuntu:15.10 cat etc/resolv.conf
search Home
nameserver 114.114.114.114
nameserver 8.8.8.8

给某个容器设置DNS

[root@bogon ~]# docker run -it --rm -h host_test --dns=114.114.114.114 ubuntu:15.10


root@host_test:/# cat /etc/hostname
host_test


root@host_test:/# 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.2	host_test


root@host_test:/# cat /etc/resolv.conf
search Home
nameserver 114.114.114.114
  • --rm : 退出容器时自动删除容器
  • -h HOSTNAME 或者 --hostname=HOSTNAME: 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。
  • --dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
  • --dns-search=DOMAIN: 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。

Docker 仓库管理

仓库(Repository)是集中存放镜像的地方。以下介绍一下 Docker Hub。当然不止 docker hub,只是远程的服务商不一样,操作都是一样的。

Docker Hub

目前 Docker 官方维护了一个公共仓库 Docker Hub

大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

注册

https://hub.docker.com 免费注册一个 Docker 账号。

登录和退出

登录需要输入用户名和密码,登录成功后,我们就可以从 docker hub 上拉取自己账号下的全部镜像。

$ docker login

退出

$ docker logout

拉取镜像

你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。

以 ubuntu 为关键词进行搜索:

$ docker search ubuntu

使用 docker pull 将官方 ubuntu 镜像下载到本地:

$ docker pull ubuntu 

推送镜像

用户登录后,可以通过 docker push 命令将自己的镜像推送到 Docker Hub。

以下命令中的 username 请替换为你的 Docker 账号用户名。

$ docker tag ubuntu:18.04 username/ubuntu:18.04
$ docker image ls

REPOSITORY      TAG        IMAGE ID            CREATED           ...  
ubuntu          18.04      275d79972a86        6 days ago        ...  
username/ubuntu 18.04      275d79972a86        6 days ago        ...  
$ docker push username/ubuntu:18.04
$ docker search username/ubuntu

NAME             DESCRIPTION       STARS         OFFICIAL    AUTOMATED
username/ubuntu

私有仓库

安装运行 docker-registry

$ docker run -d -p 5000:5000 --restart=always --name registry registry

默认情况下,仓库会被创建在容器的 /var/lib/registry 目录下。你可以通过 -v 参数来将镜像文件存放在本地的指定路径,例如下面的例子将上传的镜像放到本地的 /opt/data/registry 目录。

$ docker run -d \
    -p 5000:5000 \
    -v /opt/data/registry:/var/lib/registry \
    registry

创建好私有仓库之后,就可以使用 docker tag 来标记一个镜像,然后推送它到仓库。例如私有仓库地址为 127.0.0.1:5000

先在本机查看已有的镜像:

$ docker image ls
REPOSITORY                        TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu                            latest              ba5877dc9bec        6 weeks ago         192.7 MB

添加标记

使用 docker tagubuntu:latest 这个镜像标记为 127.0.0.1:5000/ubuntu:latest

格式为 docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

$ docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
$ docker image ls
REPOSITORY                        TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu                            latest              ba5877dc9bec        6 weeks ago         192.7 MB
127.0.0.1:5000/ubuntu:latest      latest              ba5877dc9bec        6 weeks ago         192.7 MB

上传镜像

使用 docker push 上传标记的镜像:

$ docker push 127.0.0.1:5000/ubuntu:latest
The push refers to repository [127.0.0.1:5000/ubuntu]
373a30c24545: Pushed
a9148f5200b0: Pushed
cdd3de0940ab: Pushed
fc56279bbb33: Pushed
b38367233d37: Pushed
2aebd096e0e2: Pushed
latest: digest: sha256:fe4277621f10b5026266932ddf760f5a756d2facd505a94d2da12f4f52f71f5a size: 1568

curl 查看仓库中的镜像

$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ubuntu"]}

这里可以看到 {"repositories":["ubuntu"]},表明镜像已经被成功上传了。

先删除已有镜像,再尝试从私有仓库中下载这个镜像。

$ docker image rm 127.0.0.1:5000/ubuntu:latest

$ docker pull 127.0.0.1:5000/ubuntu:latest
Pulling repository 127.0.0.1:5000/ubuntu:latest
ba5877dc9bec: Download complete
511136ea3c5a: Download complete
9bad880da3d2: Download complete
25f11f5fb0cb: Download complete
ebc34468f71d: Download complete
2318d26665ef: Download complete

$ docker image ls
REPOSITORY                         TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
127.0.0.1:5000/ubuntu:latest       latest              ba5877dc9bec        6 weeks ago         192.7 MB

配置非 https 仓库地址

如果你不想使用 127.0.0.1:5000 作为仓库地址,比如想让本网段的其他主机也能把镜像推送到私有仓库。你就得把例如 192.168.199.100:5000 这样的内网地址作为私有仓库地址,这时你会发现无法成功推送镜像。

这是因为 Docker 默认不允许非 HTTPS 方式推送镜像。我们可以通过 Docker 的配置选项来取消这个限制,或者查看下一节配置能够通过 HTTPS 访问的私有仓库。

Ubuntu 16.04+, Debian 8+, centos 7

对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下 json 格式的内容(如果文件不存在请新建该文件)

{
  "registry-mirror": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ],
  "insecure-registries": [
    "192.168.199.100:5000"
  ]
}

其他系统

对于 Docker Desktop for Windows 、 Docker Desktop for Mac 在设置中的 Docker Engine 中进行编辑 ,增加和上边一样的字符串即可。

数据管理

数据卷 是一个可供一个或多个容器使用的特殊目录。我们知道容器通常是不持久的,可能会挂掉或者被删掉,因此如何保存容器内应用产生的数据是一个问题,譬如一个数据库容器,将容器中的数据库文件保存到主机上,这是很重要的(假如保存到容器中,那么万一容器被删了,数据就丢失了)

数据卷,就是做这个用的。我们将主机上的一个目录挂载到容器某个目录上,容器中向这个挂载点写入内容时,这些内容会被复制到主机相应的目录上,从而可以将数据保存到主机上。

数据卷

创建数据卷

$ docker volume create my-vol

centos7 默认挂载点是:/var/lib/docker/volumes/my-vol/_data

列出数据卷

列出所有的数据卷信息

$ docker volume ls

查看数据卷信息

$ docker volume inspect my-vol
[
	{
		"Driver": "local",
		"Labels": {},
		"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
		"Name": "my-vol",
		"Options": {},
		"Scope": "local"
	}
]

能看到这个卷的默认挂载点:"/var/lib/docker/volumes/my-vol/_data"

容器挂载数据卷

docker run 一个容器时,可以使用 --mount 或者 -v 来直接挂载 数据卷 或一个目录。并且可以一次挂载多个数据卷。

语法:

# 指定将 my-vol 这个数据卷,挂载到容器 /usr/share/nginx/html 上
-v my-vol:/usr/share/nginx/html

# 会自动创建一个数据卷,挂载到容器 /usr/share/nginx/html 上
-v /usr/share/nginx/html

# 将主机的 /var/lib/data 目录,挂载到容器 /usr/share/nginx/html 上
-v /var/lib/data:/usr/share/nginx/html

手动指定将数据卷 my-vol 挂载到 /usr/share/nginx/html:

$ docker run -d -P \
	--name web \
	# -v my-vol:/usr/share/nginx/html \
	--mount source=my-vol,target=/usr/share/nginx/html \
	nginx:alpine

上面的例子中,--name web 是创建了一个名为 web 的容器,并挂载一个 数据卷 到容器的 /usr/share/nginx/html 目录。当容器向 /usr/share/nginx/html 写入内容,实际是向我们的 my-vol 写入。

注释掉的 # -v my-vol:/usr/share/nginx/html \ 其实和 --mount ... 是等效的两种写法。

我们还可以直接指定挂载到哪儿:

$ docker run -d -P \
	--name web \
	# 自动创建一个数据卷,并挂载到容器的:/usr/share/nginx/html 上
	-v /usr/share/nginx/html \
	nginx:alpine

数据卷的默认权限是读写(rw),用户也可以通过 ro 指定为只读:

docker run -d -P --name web -v /var/lib/data:/opt/webapp:ro

/var/lib/data:/opt/webapp:ro

查看容器中的数据卷信息

$ docker inspect web  # web 是容器名

数据卷信息在输出结果的 Mounts 字段下。

删除数据卷

$ docker volume rm my-vol

正常删除容器时,数据卷不受影响。如果想要删除容器的同时删除数据卷,可以使用 docker rm -v 命令

清理无主的数据卷,可以:

$ docker volume prune

数据卷容器

介绍

数据卷容器,就是把一个数据卷挂载到一个容器中,然后其他容器可以从这个容器中来获取这个数据卷,从而实现多个容器的数据互通。

首先,创建一个数据卷容器:

$ docker run -d -v /dbdata --name dbdata ubuntu

然后,可以在其他容器中使用 --volumes-from 来挂载 dbdata 容器中的数据卷

$ docker run -d --volumes-from dbdata --name db1 ubuntu
$ docker run -d --volumes-from dbdata --name db2 ubuntu

这样,这三个容器之间的数据是互通的。

可以多次使用--volumes-from参数来从多个容器挂载多个数据卷

使用 --volumes-from 参数所挂载数据卷的容器自身并不需要保持在运行状态。

备份容器中的数据卷

$ docker run --volumes-from dbdata -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdata

docker run --volumes-from dbdata 使用 dbdata 容器的数据卷配置来配置当前容器

-v $(pwd):/backup 将当前路径挂载到当前容器的 /backup 目录上

--name worker ubuntu 使用 ubuntu 镜像来创建一个名为 worker 的容器

tar cvf /backup/backup.tar /dbdata 执行归档命令。

恢复容器的数据卷

$ docker run --volumes-from dbdata -v $(pwd):/backup busybox tar xvf
/backup/backup.tar

Buildx

Buildx 是一个 docker CLI 插件,它使用 buildkit 拓展了构建容器的能力。buildkit 详情见 官方文档.

如果 Docker 的版本 >= 19.03 buildx 是内置的,不用额外安装。

查看docker版本:docker version

查看是否有buildx:docker buildx, 如果有正常显示输出,则说明没问题

手动安装 buildx:

mkdir -pv ~/.docker/cli-plugins/
wget -O ~/.docker/cli-plugins/docker-buildx \
    https://github.com/docker/buildx/releases/download/v0.5.1/buildx-v0.5.1.linux-amd64
chmod a+x ~/.docker/cli-plugins/docker-buildx 

安装和使用buildx:https://github.com/docker/buildx/#installing

将 buildx 作为默认构建器

我们通常使用 docker build 来构建项目,这是我们默认的构建器(builder),使用 docker buildx install 命令来给 docker buildx build 起别名,以后使用 docker build 就相当于执行 docker buildx build

移除我们起的别名(即恢复默认的builder),运行 docker buildx uninstall 命令

BuildKit

BuildKit 支持一些扩展的构建语法。

RUN --mount=...

RUN --mount 允许你创建可以被构建进程访问的挂载点。它可以绑定别的构建过程中的文件,而不需要事先复制它们,又或者可以访问 ssh-agent sockets、创建缓存等来尽速你的构建过程。

RUN --mount=type=bind (默认挂载类型)

这个选项可以在上下文或一个镜像中,绑定只读文件夹到构建的容器中。

Option Description
target (required) 挂载在哪个位置
source 源路径(这个路径来自 from 参数,默认是 from 的root路径)
from 参数可以是构建阶段,又或者一个镜像。它用来指定 source 的来源,默认是构建时的 上下文路径
rw,readwrite 允许在挂载点中写入

from 用来指定镜像,source 指定这个镜像中的某个路径,target 指定将 source 挂载的目标地址。

RUN --mount=type=cache

这种类型允许缓存文件夹以供后续使用。

Option Description
id 可选的 ID 来区分不同的缓存. 默认值是 target.
target (required) 挂载在哪个位置
ro,readonly 设置只读
sharing 可选值是 shared, private, or locked. 默认是 shared. shared 类型可以被多个构建进程同时使用. private 如果存在多个构建进程,则每个进程都会创建一个自己的挂载点. locked 会上锁,当一个构建完成后,才能被另一个构建使用。
from Build stage to use as a base of the cache mount. Defaults to empty directory.
source Subpath in the from to mount. Defaults to the root of the from.
mode File mode for new cache directory in octal. Default 0755.
uid User ID for new cache directory. Default 0.
gid Group ID for new cache directory. Default 0.

譬如缓存 Go packages:

# syntax = docker/dockerfile:1.3
FROM golang
...
RUN --mount=type=cache,target=/root/.cache/go-build go build ...

你可以直接使用 docker buildx build 命令构建镜像。

构建多种系统架构

每种系统架构,通常只能构建自己系统架构的容器,譬如 x86 系统构建的容器不能支持 arm64

使用 Buildx 来构建多架构镜像是非常便捷的方法

首先,要先创建一个新的构建器实例:

$ docker buildx create --name mybuilder --driver docker-container

四种不同的 driver:

  • docker 在 Docker 守护进程中集成了 BuildKit 二进制库
  • docker-container 在容器中自动运行 BuildKit
  • kubernetes to spin up pods with defined BuildKit container image to build your images.
  • remote to connect to manually provisioned and managed buildkitd instances.

对于 Linux 系统,需要先使用 QEMU 模拟才能构建其他系统架构,(如果是 Docker 桌面版或者windows版,不需要执行这个命令),并且系统 kernel >= 4.8 而且 binfmt-support >= 2.1.7:

$ docker run --privileged --rm tonistiigi/binfmt --install all

构建镜像

新建 Dockerfile 文件:

FROM --platform=$TARGETPLATFORM alpine

RUN uname -a > /os.txt

CMD cat /os.txt

使用 $ docker buildx build 命令构建镜像:

$ docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t myusername/hello . --push


# 查看镜像信息
$ docker buildx imagetools inspect myusername/hello

--platform 用来指定生成的镜像的构架

注意将 myusername 替换为自己的 Docker Hub 用户名。

--push 参数表示将构建好的镜像推送到 Docker 仓库。

在不同架构运行该镜像,可以得到该架构的信息:

# arm
$ docker run -it --rm myusername/hello
Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 armv7l Linux

# arm64
$ docker run -it --rm myusername/hello
Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 aarch64 Linux

# amd64
$ docker run -it --rm myusername/hello
Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 Linux

架构相关变量

Dockerfile 中支持如下架构相关的变量

TARGETPLATFORM

构建镜像的目标平台,例如 linux/amd64, linux/arm/v7, windows/amd64

TARGETOS

TARGETPLATFORM 的 OS 类型,例如 linux, windows

TARGETARCH

TARGETPLATFORM 的架构类型,例如 amd64, arm

TARGETVARIANT

TARGETPLATFORM 的变种,该变量可能为空,例如 v7

BUILDPLATFORM

构建镜像主机平台,例如 linux/amd64

BUILDOS

BUILDPLATFORM 的 OS 类型,例如 linux

BUILDARCH

BUILDPLATFORM 的架构类型,例如 amd64

BUILDVARIANT

BUILDPLATFORM 的变种,该变量可能为空,例如 v7

使用举例

例如我们要构建支持 linux/arm/v7linux/amd64 两种架构的镜像。假设已经生成了两个平台对应的二进制文件:

  • bin/dist-linux-arm

  • bin/dist-linux-amd64

那么 Dockerfile 可以这样书写:

FROM scratch

# 使用变量必须申明
ARG TARGETOS
ARG TARGETARCH

COPY bin/dist-${TARGETOS}-${TARGETARCH} /dist

ENTRYPOINT ["dist"]

Dockerfile 命令

简介

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。通过这个文件,可以直接构建一个自己的镜像。

FROM nginx
RUN yum install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
COPY hom* /mydir/

CMD     /bin/bash

WARNING: 每个命令都会在 docker 上新建一层,过多无意义的层会导致镜像变大,因此可以将多个命令合并到一起

  • FROM:基于某个镜像来定制后续的镜像,相当于继承自这个镜像
  • RUN:用来执行命令,有两种格式
    • RUN 命令:RUN echo "Hello"
    • RUN [可执行文件,参数1,参数2,...]:RUN ["echo", "Hello"]

构建

# docker build -t nginx:v3 .

build: 用来构建镜像

-t:指定生成的镜像名和TAG

.:执行构建时的上下文路径

其他方式:

# docker build -t nginx:v4 -f ./Dockerfile.txt
  • -f 选项可以指定 Dockerfile,并且 Dockerfile 的文件名不是固定的。

指令

FROM

指定基础镜像,即在这个镜像的基础上,构建我们新的镜像。

FROM Alpine

Alpine 是一个很精简的基础镜像,文件非常小,常用来作为基础镜像。Alpine 有自己的包安装工具 apk

MAINTAINER

给镜像指定作者和邮箱

MAINTAINER James Turnbull "james@example.com" 

COPY

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

语法:

COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
  • [--chown=<user>:<group>] 可选参数。当文件复制到容器中后,改变文件的用户和属组
  • <源路径1> : 主机中的文件路径, 可以是多个,也可以带通配符:COPY hom* /mydir/ 。如果源路径是文件夹,则会将此文件夹内的内容复制到目标路径。
  • <目标路径>:容器中的路径(如果路径不在,会自动创建,可以是容器内的绝对路径,也可以是工作目录的相对路径:工作目录可以用 WORKDIR 指令来指定)

ADD

copy 类似,使用方式也类似。这个命令会将文件添加到容器中,不同之处是这个命令会自动解压 tar, gz, xz 等类型的文件到目标路径。ADD 命令也可以将一个网络 url 下载下来放到容器中。

CMD

一个 Dockerfile 中只能有一个 CMD命令,如果有多个,也只有最后一个生效。这个命令有两个作用:

  1. 镜像运行时,默认的主进程启动命令,和RUN命令的区别是:

    • RUN 会在创建镜像时运行命令

    • 镜像运行时,默认的容器主进程的启动命令

  2. 来为 ENTRYPOINT 指令接受参数

当我们以 docker run -it <container> <command> 这种方式启动容器时,<command> 会覆盖掉默认的 CMD 命令。换句话说,CMD 命令就是我们默认启动容器时的命令,默认是/bin/bash

Dockfile 有如下格式:

CMD <shell 命令>   # shell 格式
CMD ["<可执行文件或命令>","<param1>","<param2>",...]   # exec 格式, 注意是双引号
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而是将这些命令行参数当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

假设我们使用如下 Dockerfile 文件创建了一个镜像:nginx:test

FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 默认要传递给 ENTRYPOINT 的参数 
  1. 不传参运行
$ docker run  nginx:test  # 这里没有写命令

上面的命令会默认运行:$ nginx -c /etc/nginx/nginx.conf

  1. 传参运行
$ docker run  nginx:test -c /etc/nginx/new.conf

上面的命令会运行:nginx -c /etc/nginx/new.conf

ENV

设置容器的环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量(在构建完成的镜像中,这个环境变量也依然存在)

格式:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:

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"

也可以使用docker run命令行的 -e 标志来传递环境变量。这些变量 将只会在运行时有效:

$ sudo docker run -ti -e "WEB_PORT=8080" ubuntu env
HOME=/
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=792b171c5e9f
TERM=xterm
WEB_PORT=8080

ARG

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建完成后的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖 Dockerfile 中的参数。

格式:

ARG <参数名>[=<默认值>]

示例:

ARG DOCKER_USERNAME=library

FROM ${DOCKER_USERNAME}/alpine

# 在FROM 之后使用变量,必须在每个阶段分别指定
ARG DOCKER_USERNAME=library

RUN set -x ; echo ${DOCKER_USERNAME}

FROM ${DOCKER_USERNAME}/alpine

# 在FROM 之后使用变量,必须在每个阶段分别指定
ARG DOCKER_USERNAME=library

RUN set -x ; echo ${DOCKER_USERNAME}

ARG 指定的参数,必须分阶段指定,因为它在下一个阶段会失效。

VOLUME

定义数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷(譬如你的容器有一个数据库,你不会想要将数据库文件直接保存到容器中,因为容器不应该进行数据的改写,就像你运行一个python文件时,不应该改写这个python文件本身,因此我们可以将数据库文件保存到一个挂载点)

卷可以在多个容器之间共享。

格式:

# 可以写成多个路径的列表,或者单独的路径。
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

譬如如下 Dockerfile 配置:

VOLUME /data

这里的 /data 目录就会在容器运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行容器时可以覆盖这个挂载设置。比如:

$ docker run -d -v mydata:/data xxxx

在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

EXPOSE

仅仅只是声明端口,不会真的打开这个端口(要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问)

作用:

  • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式:

EXPOSE <端口1> [<端口2>...]

WORKDIR

WORKDIR指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录,为 Dockerfile 中后续的一系列指令设置工作目录(包括 CMD 和 ENTRYPOINT)

格式:

WORKDIR <工作目录路径>

Dockerfile 错误示范:

RUN cd /app
RUN echo "hello" > world.txt

这个例子中,world.txt 不会创建到 app/ 目录下,这是因为每一个 RUN 命令都是单独的一层镜像。所以你在第一层镜像中执行 cd /app 和第二层镜像没有任何关系。

如果你要变更工作目录,可以这样:

WORKDIR /app

RUN echo "hello" > world.txt

也可以使用相对路径:

WORKDIR /a
WORKDIR b
WORKDIR c

RUN pwd

RUN pwd 的工作目录为 /a/b/c

在运行镜像时,可以通过 -w 覆盖工作目录:

$ sudo docker run -ti -w /var/log ubuntu pwd
/var/log

USER

用来指定运行程序的用户身份。

格式:

USER <user>:[group]

user 和 group 可以是名字,也可以是 uid 和 gid

USER 可以改变后续 RUN, CMD, ENTRYPOINT 这类命令运行时的身份。当然,指定用户时需要事先创建好用户:

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]

HEALTHCHECK

对容器进行健康检查。

格式:

  • HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
  • HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK 支持下列选项:

  • --interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
  • --timeout=<时长>:健康检查命令运行超时时间,如果超时,本次健康检查就被视为失败,默认 30 秒;
  • --retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。

CMD, ENTRYPOINT 一样,HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

HEALTHCHECK [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样,分为 shell 格式,和 exec 格式。命令的返回值决定了该次健康检查的成功与否:0:成功;1:失败;2:保留,不要使用这个值。

实例:

假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否在正常工作,我们可以用 curl 来帮助判断,其 DockerfileHEALTHCHECK 可以这么写:

FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -fs http://localhost/ || exit 1

这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。

ONBUILD

ONBUILD 的作用很简单:在构建当前镜像时这个命令没有任何作用,但是当别的人用当前这个镜像作为基础镜像时,他们构建他们的镜像时,会自动执行 ONBUILD 定义的命令。也就说是,ONBUILD 这个命令其实是为别人服务的,而不是为自己服务。

譬如,现有一个基础镜像:my-node

FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]

这是一个 npm 的镜像,每个依赖它的子镜像,都会执行 npm install , COPY ./package.json /app 等操作,所以我们事先写好了 ONBUILD ,以后所有依赖了这个基础镜像的镜像在构建时,都会自动运行这三句 ONBUILD 指定的命令。(注意只有子镜像会执行这几句话,孙子级别的镜像不会执行)

别的镜像使用这个基础镜像:

FROM my-node

LABEL

LABEL 指令用来给镜像以键值对的形式添加一些元数据(metadata)。

LABEL <key>=<value> <key>=<value> <key>=<value> ...

譬如, 我们还可以用一些标签来申明镜像的作者、文档地址等:

LABEL org.opencontainers.image.authors="yeasy"

LABEL org.opencontainers.image.documentation="https://yeasy.gitbooks.io"

SHELL

格式:SHELL ["executable", "parameters"]

SHELL 指令可以指定 RUN, ENTRYPOINT, CMD 指令所使用的 shell,Linux 中默认为 ["/bin/sh", "-c"]

譬如:

SHELL ["/bin/sh", "-cex"]

RUN lll ; ls

STOPSIGNAL

STOPSIGNAL指令用来设置停止容器时发送什么系统调用信号给容 器。这个信号必须是内核系统调用表中合法的数,如9,或者SIGNAME格式中的信号名称,如SIGKILL。

Docker Compose

Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。

Compose 定位是 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)」

Compose 中有两个重要的概念:

  • 服务 (service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

Compose 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。

compose 命令

选项

  • -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定
  • -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
  • --verbose 输出更多调试信息。
  • -v, --version 打印版本并退出。

build

格式为 docker compose build [options] [SERVICE...]

构建(重新构建)项目中的服务容器。

可以随时在项目目录下运行 docker compose build 来重新构建服务。服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,服务名会是 web_db

选项包括:

  • --force-rm 删除构建过程中的临时容器。
  • --no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。
  • --pull 始终尝试通过 pull 来获取更新版本的镜像。

config

验证当前目录下 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因。

down

此命令将会停止 up 命令所启动的容器,并移除网络

exec

进入指定的容器。

help

获得一个命令的帮助。

images

列出 Compose 文件中包含的镜像。

kill

格式为 docker compose kill [options] [SERVICE...]

通过发送 SIGKILL 信号来强制停止服务容器。

支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号。

$ docker compose kill -s SIGINT

logs

格式为 docker compose logs [options] [SERVICE...]

查看服务容器的输出。默认情况下,docker compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。

该命令在调试问题的时候十分有用。

pause

格式为 docker compose pause [SERVICE...]

暂停一个服务容器。

port

格式为 docker compose port [options] SERVICE PRIVATE_PORT

打印某个容器端口所映射的公共端口。

选项:

  • --protocol=proto 指定端口协议,tcp(默认值)或者 udp。
  • --index=index 如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。

ps

格式为 docker compose ps [options] [SERVICE...]

列出项目中目前的所有容器。

选项:

  • -q 只打印容器的 ID 信息。

pull

格式为 docker compose pull [options] [SERVICE...]

拉取服务依赖的镜像。

选项:

  • --ignore-pull-failures 忽略拉取镜像过程中的错误。

push

推送服务依赖的镜像到 Docker 镜像仓库。

restart

格式为 docker compose restart [options] [SERVICE...]

重启项目中的服务。

选项:

  • -t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)。

rm

格式为 docker compose rm [options] [SERVICE...]

删除所有(停止状态的)服务容器。推荐先执行 docker compose stop 命令来停止容器。

选项:

  • -f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。
  • -v 删除容器所挂载的数据卷。

run

格式为 docker compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]

在指定服务上执行一个命令。

例如:

$ docker compose run ubuntu ping docker.com

将会启动一个 ubuntu 服务容器,并执行 ping docker.com 命令。默认情况下,如果存在关联,则所有关联的服务将会自动被启动,除非这些服务已经在运行中。

如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如:

$ docker compose run --no-deps web python manage.py shell

其他选项:

  • -d 后台运行容器。

  • --name NAME 为容器指定一个名字。

  • --entrypoint CMD 覆盖默认的容器启动指令。

  • -e KEY=VAL 设置环境变量值,可多次使用选项来设置多个环境变量。

  • -u, --user="" 指定运行容器的用户名或者 uid。

  • --no-deps 不自动启动关联的服务容器。

  • --rm 运行命令后自动删除容器,d 模式下将忽略。

  • -p, --publish=[] 映射容器端口到本地主机。

  • --service-ports 配置服务端口并映射到本地主机。

  • -T 不分配伪 tty,意味着依赖 tty 的指令将无法运行。

scale

格式为 docker compose scale [options] [SERVICE=NUM...]

设置指定服务运行的容器个数。

通过 service=num 的参数来设置数量。例如:

$ docker compose scale web=3 db=2

将启动 3 个容器运行 web 服务,2 个容器运行 db 服务。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

start

格式为 docker compose start [SERVICE...]

启动已经存在的服务容器。

stop

格式为 docker compose stop [options] [SERVICE...]

停止已经处于运行状态的容器,但不删除它。通过 docker compose start 可以再次启动这些容器。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

top

查看各个服务容器内运行的进程。

unpause

格式为 docker compose unpause [SERVICE...]

恢复处于暂停状态中的服务。

up

格式为 docker compose up [options] [SERVICE...]

该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

链接的服务都将会被自动启动,除非已经处于运行状态。

可以说,大部分时候都可以直接通过该命令来启动一个项目。

默认情况,docker compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。当通过 Ctrl-C 停止命令时,所有容器将会停止。

如果使用 docker compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

默认情况,如果服务容器已经存在,docker compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker compose up --no-deps -d <SERVICE_NAME> 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

选项:

  • -d 在后台运行服务容器。
  • --no-color 不使用颜色来区分不同的服务的控制台输出。
  • --no-deps 不启动服务所链接的容器。
  • --force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用。
  • --no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用。
  • --no-build 不自动构建缺失的服务镜像。
  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

version

格式为 docker compose version

打印版本信息。

场景一

最常见的项目是 web 网站,该项目应该包含 web 应用和缓存。

下面我们用 Python 来建立一个能够记录页面访问次数的 web 网站。

web 应用

新建文件夹,在该目录中编写 app.py 文件

from flask import Flask
from redis import Redis

app = Flask(__name__)
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
    count = redis.incr('hits')
    return 'Hello World! 该页面已被访问 {} 次。\n'.format(count)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

Dockerfile

编写 Dockerfile 文件,内容为

FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install redis flask
CMD ["python", "app.py"]

docker-compose.yml

编写 docker-compose.yml 文件,这个是 Compose 使用的主模板文件。

version: '3'
services:

  web:
    build: .
    ports:
     - "5000:5000"

  redis:
    image: "redis:alpine"

指定了两个服务:web 和 redis

运行 compose 项目

$ docker compose up

Compose 模板文件

模板文件是使用 Compose 的核心。默认的模板文件名称为 docker-compose.yml,格式为 YAML 格式。

version: "3"

services:
  webapp:
    image: examples/web
    ports:
      - "80:80"
    volumes:
      - "/data"

build

指定 Dockerfile 所在文件夹的路径(可以是绝对路径,或者相对 docker-compose.yml 文件的路径)

version: '3'
services:
  webapp:
    build: ./dir

其他指令:

version: '3'
services:

  webapp:
    build:
      context: ./dir
      dockerfile: Dockerfile-alternate
      args:
        buildno: 1

context 指定 Dockerfile 文件夹位置

dockerfile 指定 Dockerfile 文件名

args 指定构建镜像时的变量

使用 cache_from 指定构建镜像的缓存

build:
  context: .
  cache_from:
    - alpine:latest
    - corp/web_app:3.14

cap_add, cap_drop

指定容器的内核能力(capacity)分配。cap_add 添加能力,cap_drop 删除能力

例如:

# 添加所有能力
cap_add:
  - ALL
  
  
# 删除网络能力
cap_drop:
  - NET_ADMIN

command

覆盖容器启动后默认执行的命令。

command: echo "hello world"

cgroup_parent

指定父 cgroup 组,意味着将继承该组的资源限制。例如,创建了一个 cgroup 组名称为 cgroups_1 :

cgroup_parent: cgroups_1

container_name

指定容器名称。默认将会使用 项目名称_服务名称_序号 这样的格式。

container_name: docker-web-container

注意: 指定容器名称后,该服务将无法进行扩展(scale),因为 Docker 不允许多个容器有相同名

devices

指定设备映射关系。

devices:
  - "/dev/ttyUSB1:/dev/ttyUSB0"

depends_on

解决容器的依赖、启动先后的问题。以下例子中会先启动 redis db 再启动 web

version: '3'

services:
  web:
    build: .
    depends_on:
      - db
      - redis

  redis:
    image: redis

  db:
    image: postgres

注意:web 服务不会等待 redis db 「完全启动」之后才启动。

dns

自定义 DNS 服务器。可以是一个值,也可以是一个列表。

dns: 8.8.8.8

dns:
  - 8.8.8.8
  - 114.114.114.114

配置 DNS 搜索域。可以是一个值,也可以是一个列表。

dns_search: example.com

dns_search:
  - domain1.example.com
  - domain2.example.com

tmpfs

挂载一个 tmpfs 文件系统到容器。

tmpfs: /run
tmpfs:
  - /run
  - /tmp

env_file

从文件中获取环境变量,可以为单独的文件路径或列表。

如果通过 docker-compose -f FILE 方式来指定 Compose 模板文件,则 env_file 中变量的路径会基于模板文件路径。如果有变量名称与 environment 指令冲突,则按照惯例,以后者为准。

env_file: .env

env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

environment

设置环境变量。你可以使用数组或字典两种格式。

只给定名称的变量会自动获取运行 Compose 主机上对应变量的值,可以用来防止泄露不必要的数据。

environment:
  RACK_ENV: development
  SESSION_SECRET:

environment:
  - RACK_ENV=development
  - SESSION_SECRET

expose

暴露端口,但不映射到宿主机,只被连接的服务访问。仅可以指定内部端口为参数

expose:
 - "3000"
 - "8000"

extra_hosts

类似 Docker 中的 --add-host 参数,指定额外的 host 名称映射信息。

extra_hosts:
 - "googledns:8.8.8.8"
 - "dockerhub:52.1.157.61"

会在启动后的服务容器中 /etc/hosts 文件中添加如下两条条目。

8.8.8.8 googledns
52.1.157.61 dockerhub

healthcheck

通过命令检查容器是否健康运行

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"]
  interval: 1m30s
  timeout: 10s
  retries: 3

image

指定为镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。

image: ubuntu
image: orchardup/postgresql
image: a4bc65fd

labels

为容器添加 Docker 元数据(metadata)信息。例如可以为容器添加辅助说明信息。

labels:
  com.startupteam.description: "webapp for a startup team"
  com.startupteam.department: "devops department"
  com.startupteam.release: "rc3 for v1.0"

logging

配置日志选项。

logging:
  driver: syslog
  options:
    syslog-address: "tcp://192.168.0.42:123"

目前支持三种格式:

  • driver: "json-file"
  • driver: "syslog"
  • driver: "none"

options 配置日志驱动的相关参数:

options:
  max-size: "200k"
  max-file: "10"

network_mode

设置网络模式。使用和 docker run--network 参数一样的值。

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"

networks

配置容器连接的网络。

version: "3"
services:

  some-service:
    networks:
     - some-network
     - other-network

networks:
  some-network:
  other-network:

pid

跟主机系统共享进程命名空间。打开该选项的容器之间,以及容器和宿主机系统之间可以通过进程 ID 来相互访问和操作。

pid: "host"

ports

暴露端口信息。

使用宿主端口:容器端口 (HOST:CONTAINER) 格式,或者仅仅指定容器的端口(宿主将会随机选择端口)都可以

ports:
 - "3000"
 - "8000:8000"
 - "49100:22"
 - "127.0.0.1:8001:8001"

secrets

存储敏感数据,例如 mysql 服务密码。

version: "3.1"
services:

mysql:
  image: mysql
  environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
  secrets:
    - db_root_password
    - my_other_secret

secrets:
  my_secret:
    file: ./my_secret.txt
  my_other_secret:
    external: true

security_opt

指定容器模板标签(label)机制的默认属性(用户、角色、类型、级别等)。例如配置标签的用户名和角色名。

security_opt:
    - label:user:USER
    - label:role:ROLE

stop_signal

设置另一个信号来停止容器。在默认情况下使用的是 SIGTERM 停止容器。

stop_signal: SIGUSR1

sysctls

配置容器内核参数。

sysctls:
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls:
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

ulimits

指定容器的 ulimits 限制值。

例如,指定最大进程数为 65535,指定文件句柄数为 20000(软限制,应用可以随时修改,不能超过硬限制) 和 40000(系统硬限制,只能 root 用户提高)。

  ulimits:
    nproc: 65535
    nofile:
      soft: 20000
      hard: 40000

volumes

数据卷所挂载路径设置。可以设置为宿主机路径(HOST:CONTAINER)或者数据卷名称(VOLUME:CONTAINER),并且可以设置访问模式 (HOST:CONTAINER:ro)。该指令中路径支持相对路径。

volumes:
 - /var/lib/mysql
 - cache/:/tmp/cache
 - ~/configs:/etc/configs/:ro

如果路径为数据卷名称,必须在文件中配置数据卷。

version: "3"

services:
  my_src:
    image: mysql:8.0
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:  

其他指令:

指定服务容器启动后执行的入口文件。

entrypoint: /code/entrypoint.sh

指定容器中运行应用的用户名。

user: nginx

指定容器中工作目录。

working_dir: /code

指定容器中搜索域名、主机名、mac 地址等。

domainname: your_website.com
hostname: test
mac_address: 08-00-27-00-0C-0A

允许容器中运行一些特权命令。

privileged: true

指定容器退出后的重启策略为始终重启。该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped

restart: always

以只读模式挂载容器的 root 文件系统,意味着不能对容器内容进行修改。

read_only: true

打开标准输入,可以接受外部输入。

stdin_open: true

模拟一个伪终端。

tty: true

读取变量

Compose 模板文件支持动态读取主机的系统环境变量和当前目录下的 .env 文件中的变量。

例如,下面的 Compose 文件将从运行它的环境中读取变量 ${MONGO_VERSION} 的值,并写入执行的指令中。

version: "3"
services:

db:
  image: "mongo:${MONGO_VERSION}"

如果执行 MONGO_VERSION=3.2 docker-compose up 则会启动一个 mongo:3.2 镜像的容器;如果执行 MONGO_VERSION=2.8 docker-compose up 则会启动一个 mongo:2.8 镜像的容器。

若当前目录存在 .env 文件,执行 docker-compose 命令时将从该文件中读取变量。

在当前目录新建 .env 文件并写入以下内容。

# 支持 # 号注释
MONGO_VERSION=3.6

执行 docker-compose up 则会启动一个 mongo:3.6 镜像的容器。

Docker Swarm

Swarm 是使用 SwarmKit 构建的 Docker 引擎内置(原生)的集群管理和编排工具。

节点

运行 Docker 的主机可以主动初始化一个 Swarm 集群或者加入一个已存在的 Swarm 集群,这样这个运行 Docker 的主机就成为一个 Swarm 集群的节点 (node) 。

节点分为管理 (manager) 节点和工作 (worker) 节点。

管理节点用于 Swarm 集群的管理,docker swarm 命令基本只能在管理节点执行(节点退出集群命令 docker swarm leave 可以在工作节点执行)。一个 Swarm 集群可以有多个管理节点,但只有一个管理节点可以成为 leaderleader 通过 raft 协议实现。

工作节点是任务执行节点,管理节点将服务 (service) 下发至工作节点执行。管理节点默认也作为工作节点。你也可以通过配置让服务只运行在管理节点。

服务和任务

任务 (Task)是 Swarm 中的最小的调度单位,目前来说就是一个单一的容器。

服务 (Services) 是指一组任务的集合,服务定义了任务的属性。服务有两种模式:

  • replicated services 按照一定规则在各个工作节点上运行指定个数的任务。
  • global services 每个工作节点上运行一个任务

集群

创建集群,执行 docker swarm init 的节点,会自动成为管理节点

$ docker swarm init --advertise-addr 192.168.99.100
Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
    192.168.99.100:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

如果你的 Docker 主机有多个网卡,拥有多个 IP,必须使用 --advertise-addr 指定 IP。

增加工作节点

在另一台机器上,创建工作节点并加入到集群中:

$ docker swarm join \
    --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
    192.168.99.100:2377

This node joined a swarm as a worker.

查看集群

在管理节点使用 docker node ls 查看集群。

$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
03g1y59jwfg7cf99w4lt0f662    worker2   Ready   Active
9j68exjopxe7wfl6yuxml7a7j    worker1   Ready   Active
dxn1zf6l61qsb1josjja83ngz *  manager   Ready   Active        Leader

部署服务

我们使用 docker service 命令来管理 Swarm 集群中的服务,该命令只能在管理节点运行。

新建服务

现在我们在上一节创建的 Swarm 集群中运行一个名为 nginx 服务。

$ docker service create --replicas 3 -p 80:80 --name nginx nginx:1.13.7-alpine

--replicas 用来指定创建几个 task

查看服务

使用 docker service ls 来查看当前 Swarm 集群运行的服务。

$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                 PORTS
kc57xffvhul5        nginx               replicated          3/3                 nginx:1.13.7-alpine   *:80->80/tcp

使用 docker service ps 来查看某个服务的详情。

$ docker service ps nginx
ID                  NAME                IMAGE                 NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
pjfzd39buzlt        nginx.1             nginx:1.13.7-alpine   swarm2              Running             Running about a minute ago
hy9eeivdxlaa        nginx.2             nginx:1.13.7-alpine   swarm1              Running             Running about a minute ago
36wmpiv7gmfo        nginx.3             nginx:1.13.7-alpine   swarm3              Running             Running about a minute ago

使用 docker service logs 来查看某个服务的日志。

$ docker service logs nginx
nginx.3.36wmpiv7gmfo@swarm3    | 10.255.0.4 - - [25/Nov/2017:02:10:30 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.3.36wmpiv7gmfo@swarm3    | 10.255.0.4 - - [25/Nov/2017:02:10:30 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.3.36wmpiv7gmfo@swarm3    | 2017/11/25 02:10:30 [error] 5#5: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.255.0.4, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.99.102"
nginx.1.pjfzd39buzlt@swarm2    | 10.255.0.2 - - [25/Nov/2017:02:10:26 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.1.pjfzd39buzlt@swarm2    | 10.255.0.2 - - [25/Nov/2017:02:10:27 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.1.pjfzd39buzlt@swarm2    | 2017/11/25 02:10:27 [error] 5#5: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.255.0.2, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.99.101"

服务伸缩

我们可以使用 docker service scale 对一个服务运行的容器数量进行伸缩。

当业务处于高峰期时,我们需要扩展服务运行的容器数量。

$ docker service scale nginx=9

删除服务

使用 docker service rm 来从 Swarm 集群移除某个服务。

$ docker service rm nginx
posted @ 2021-03-25 22:41  wztshine  阅读(164)  评论(0编辑  收藏  举报