Docker 常用命令
本文参考自:
RUNOOB.COM Docker 教程
《第一本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 images
或 docker 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 ...
更新镜像
更新一个镜像文件的步骤:
- 用镜像运行一个交互式的容器
[wztshine@localhost ~]$ docker run -it ubuntu:15.10 /bin/bash
- 更新并退出: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
- 通过命令 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>
来删除一个网络
在网络中连接容器
建立两个容器test1
和test2
,并给它们指定网络在一个网络里
[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 update
和 apt install iputils-ping
,就好了。
连接已经运行的容器
向网络中添加一个已经运行中的容器:
$ docker network connect <network_name> <container>
从网络删除容器
$ docker network disconnect <network_name> <container>
Link 互联
容器的互联(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 tag
将 ubuntu: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
在容器中自动运行 BuildKitkubernetes
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/v7
和 linux/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命令,如果有多个,也只有最后一个生效。这个命令有两个作用:
-
镜像运行时,默认的主进程启动命令,和RUN命令的区别是:
-
RUN 会在创建镜像时运行命令
-
镜像运行时,默认的容器主进程的启动命令
-
-
来为
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 的参数
- 不传参运行
$ docker run nginx:test # 这里没有写命令
上面的命令会默认运行:
$ nginx -c /etc/nginx/nginx.conf
- 传参运行
$ 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
来帮助判断,其 Dockerfile
的 HEALTHCHECK
可以这么写:
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_search
配置 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
集群可以有多个管理节点,但只有一个管理节点可以成为 leader
,leader
通过 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