概述
基础概念
-
docker是一种容器化技术,通过Namespaces技术来实现资源隔离。Linux Namespaces主要提供了6种资源的隔离机制(其他还有Cgroup等)
Mount: 隔离文件系统挂载点UTS: 隔离主机名和域名信息IPC: 隔离进程间通信PID: 隔离进程的IDNetwork: 隔离网络资源User: 隔离用户和用户组的ID
sudo /proc/pid/ns # 可查进程的namespaces情况 cgroup -> cgroup:[4026531835] ipc -> ipc:[4026531839] mnt -> mnt:[4026531840] net -> net:[4026531992] pid -> pid:[4026531836] pid_for_children -> pid:[4026531836] time -> time:[4026531834] time_for_children -> time:[4026531834] user -> user:[4026531837] uts -> uts:[4026531838] # 可以看到每个资源都对应的有一个id,这个id就是对应的namespace,一般的docker容器进程只有time,time_for_children,user资源是不隔离的在clone()进程时,可以指定要隔离的内容,隔离哪个,在哪个维度你就只能看到相关命名空间里的内容,比如隔离pid,在pid维度,你无法看到其他命名空间的pid,但是其他没有隔离的资源依然共享,比如time…
相同命名空间则资源共享,docker容器的隔离就是这么实现的。
例子:
run 一个 mysql
5105fee6f9f5 mysql:8.0 "docker-entrypoint.s…" 4 hours ago Up 4 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp condescending_khorana通过
pstree或者docker top containerid查看得知,run命令之后,容器在宿主机上主要有2个进程,bash和mysqld(run的时候有bash进程,如果是start container 则只有一个mysqld进程)systemd+ 29352 29332 0 00:22 ? 00:00:02 mysqld root 29681 29332 0 00:28 pts/0 00:00:00 /bin/bash查看29352的namespaces
lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 cgroup -> 'cgroup:[4026531835]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 ipc -> 'ipc:[4026532606]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 mnt -> 'mnt:[4026532604]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:56 net -> 'net:[4026532609]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 pid -> 'pid:[4026532607]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 pid_for_children -> 'pid:[4026532607]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 time -> 'time:[4026531834]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 time_for_children -> 'time:[4026531834]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 user -> 'user:[4026531837]' lrwxrwxrwx. 1 systemd-coredump input 0 6月 25 20:57 uts -> 'uts:[4026532605]'查看29681的namespaces,发现和mysqld相同
同时mysqld下面开启的其他子进程,都拥有和mysqld同样的namespaces
通过查看
cat /proc/29352/status | grep NSpid可以看到mysqld进程在容器内是1号进程。NSpid: 29352 1正常系统的1号进程肯定是系统进程,但是容器内的1号进程就是一个普通进程,由Entrypoint指定。
所以,run一个容器,实际就是开了一个新的命名空间,然后将相关进程加入这个命名空间。从宿主机的视角,这些进程都是运行在宿主机上的,只是命名空间不同,从命名空间里的视角来看,内部只有内部的视角,看不到宿主机,以为自己是个独立的系统,所以还能产生pid=1的进程
另:容器的真实载体是一个叫containerd-shim的进程,这个进程是宿主机的进程,没有开启命名空间(或者说和宿主机一个命名空间),他会产生容器进程,每启动一个容器都会产生一个containerd-shim进程。
-
docker使用文件分层技术,一个完整的镜像由多个层组成,每一层可以复用。
文件分层的好处是,如果多个镜像都使用到了一个文件层,那么这个文件层只会下载一个,被共享了。
-
docker daemon
docker相对于podman有2个特点,root运行,必须要有daemon程序。
docker的daemon程序为整个核心,所有服务由其提供
命令行工具实际是调用docker Remote API和deamon交互,最终实现操作。
为什么是 Remote API,这不是本地调用吗?
实际上docker在运行时采用C/S模型,daemon为服务端,docker CLI相当于客户端,daemon通过Unix socket监听请求,一切功能的实现实际在服务端完成(所以docker 很容易就能实现远程调用,daemon可以部署在其他主机上)。比如通过dockfile构建的镜像的时候,build的最后一个参数为上下文路径,build命令会将上下文路径里的所有内容全部传输给daemon,以完成构建。
image分层
分层原理
每一个image实际由多层组成。
以一个image的构建为例:
FROM ubuntu:18.04
COPY . /app由2个命令组成,每个命令就是一层layer
构建过程:
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM ubuntu:18.04
---> 7d0d8fa37224
Step 2/2 : COPY . /app
---> ae9e62f2be44
Successfully built ae9e62f2be44
Successfully tagged acme/my-base-image:1.0查看history:
IMAGE CREATED CREATED BY SIZE COMMENT
ae9e62f2be44 34 minutes ago /bin/sh -c #(nop) COPY dir:d526a01dff55d760c… 161B
7d0d8fa37224 13 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 13 days ago /bin/sh -c #(nop) ADD file:900f735ff138e5137… 63.1MB <missing>表示这部分是其他系统构建的,你不具有回滚操作能力。FROM unbuntu:18.04这是一个基础镜像,他的构建由发布者完成。所以本次构建实际从CMD ["bash"]开始,可以理解为这是一个FROM启动命令。
然后直接COPY复制文件。
**综上:**这个镜像将由2个layer组成。
以上述镜像为基础再构建一个镜像:
# 上述镜像acme/my-base-image:1.0
FROM acme/my-base-image:1.0
CMD /app/hello.sh构建过程:
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM acme/my-base-image:1.0
---> ae9e62f2be44
Step 2/2 : CMD /app/hello.sh
---> Running in bd5c62ac9b5a
Removing intermediate container bd5c62ac9b5a
---> f4f02e2c32f0
Successfully built f4f02e2c32f0
Successfully tagged acme/my-final-image:1.0从dockfile中可以看出,还是2步,构建过程也是2步
查看history:
IMAGE CREATED CREATED BY SIZE COMMENT
f4f02e2c32f0 28 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/app… 0B
ae9e62f2be44 29 minutes ago /bin/sh -c #(nop) COPY dir:d526a01dff55d760c… 161B
7d0d8fa37224 13 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 13 days ago /bin/sh -c #(nop) ADD file:900f735ff138e5137… 63.1MB 由于FROM的镜像由本机构建,所以这里能看到基础镜像的构建过程
FROM对应了基础镜像的2个layer + <missing>
然后新增了一个layer
**综上:**此镜像由2步构建,分为3层,和基础镜像共用了2层,最终文件系统里会新增一层数据。
Layer存储在/var/lib/docker/<storage driver>中
storage driver目前推荐使用的是overlay2。
docker info可以查看现在使用的storage driver。
image大小
可以通过docker ps -s查看容器镜像的大小
- size: 可写层的大小
- virtual: image + 可写成的大小
所以image的大小 = virtual - size
如果一个image构建了n个容器,那么总磁盘占用量的大小应是:
n * size + (virtual -size)
因为每个容器都有独立的容器层(可写层),但是image层是共用的。
注意:以上占用量并不包括volumn和一些配置文件,这些内容是在host上的,不计算进容器内。

COPY-ON-WRITE
镜像是由多个分层文件组成,在文件层最上面加一个可写层,镜像就变成了容器。
镜像是只读的,因为可写层,容器是可修改的。
如果要修改“镜像中”的文件,怎么实现?
通过copy-on-write来完成:
当容器需要读取文件的时候
从最上层镜像开始查找,往下找,找到文件后读取并放入内存,若已经在内存中了,直接使用。(即,同一台机器上运行的docker容器共享运行时相同的文件)。
当容器需要添加文件的时候
直接在最上面的容器层可写层添加文件,不会影响镜像层。
当容器需要修改文件的时候
从上往下层寻找文件,找到后,复制到容器可写层,然后,对容器来说,可以看到的是容器层的这个文件,看不到镜像层里的文件。容器在容器层修改这个文件。
当容器需要删除文件的时候
从上往下层寻找文件,找到后在容器中记录删除。即,并不会真正的删除文件,而是软删除。这将导致镜像体积只会增加,不会减少。
由以上可知:
只有当文件需要编辑的时候,才会读入到可写层,所以可写层的内容实际非常少。
理解:当容器中的进程要读取文件时,可以看作是一个俯视的视角,同一个东西只能看到最上面的,一个文件修改后,他会存在于位于最上层的可写层中了,进程只能看到最上面那一个,所以修改就生效了,这也是为什么容器层要放在最上面的缘故。
安装
-
卸载旧版本
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 device-mapper-persistent-data lvm2 # device-mapper-persistent-data lvm2 用于持久化存储的,是docker使用逻辑卷技术的必要工具 # 实际现在已推荐使用overlay2,这是默认设置,不需要安装额外的软件了 -
安装docker-ce等内容
yum install --allowerasing docker-ce docker-ce-cli containerd.io # 由于centos8内置podman,和docker冲突,需要添加--allowerasing 选项 -
配置docker仓库,使用阿里云的源
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo配置专属镜像加速
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://ivzo68z7.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload # 重载配置文件 sudo systemctl restart docker # 重启 -
查看
docker --version systemctl start docker # 启动
卸载
sudo yum remove docker-ce docker-ce-cli containerd.iosudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerdrootless docker vs root docker
rootless下docker daemon以普通用户运行,杜绝了绝大部分问题,但是可能会遇到2类问题:
- 容器无法启动,有些容器需要特殊的权限
- 多网卡情况,可能导致无法从所有网卡访问到
Note
获取不到真实ip,这个可以配置来解决
如果不是安全性要求太高,建议暂时(docker v29.3.0)还是以root模式安装,但是最好开启userns-remap,这样容器内的root用户会映射到host的普通用户,避免过高的权限,同时应该配置一些安全选项,比如cap_drop,以及资源限制。
Tip
关于映射规则 可查看/etc/subuid和/etc/subgid, 一般使用userns-remap: default后,在这2个文件会有个dockremap: 100000:65536的规则,意思是可以映射从100000到165536的uid。 映射规则为: container uid + 100000 → host uid 假设容器内的用户id为0,101,经过映射后,则为100000和100101
root模式下的userns-remap和rootless都利用了用户态空间的原理,但是实现不同,userns-remap只是一个简单的特性,且只在root模式下有。
Caution
在有些tun模式代理下,userns-remap可能造成容器间的访问问题。
Caution
tun模式下也会造成外部访问容器的问题,需要排除掉docker相关的网卡。
权限问题
不管是root还是rootless,都要注意文件权限,特别是挂载已有目录时,要明白对应的host用户是哪个才能chown,否则很可能看到各种无权限的错误。
docker proxy
使用proxy pull image,由于docker是cs结构,cli本质上只是一个客户端,操作都是由docker daemon完成,所以需要daemon本身能够识别proxy。
有2种模式:
-
daemon.json
OS and configuration File location Linux, regular setup /etc/docker/daemon.jsonLinux, rootless mode ~/.config/docker/daemon.jsonWindows C:\ProgramData\docker\config\daemon.json{ "proxies": { "http-proxy": "http://proxy.example.com:3128", "https-proxy": "https://proxy.example.com:3129", "no-proxy": "*.test.example.com,.example.org,127.0.0.0/8" } }# restart service sudo systemctl restart docker -
environment variables
rootless mode: ~/.config/systemd/
/docker.service.d/ In addition,
systemctlmust be executed withoutsudoand with the--userflag.regular: /etc/systemd/system/docker.service.d
# create config file sudo mkdir -p /etc/systemd/system/docker.service.d vim /etc/systemd/system/docker.service.d/http-proxy.conf # add below lines [Service] Environment="HTTP_PROXY=http://proxy.example.com:3128" Environment="HTTPS_PROXY=https://proxy.example.com:3129" Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp" # reload sudo systemctl daemon-reload sudo systemctl restart docker # verify sudo systemctl show --property=Environment docker
docker cli
busybox
为了方便测试,使用这个工具箱镜像,封装了几百个linux常用命令,很好用。
docker run -it busybox --name test1veth-pair
当我们使用bridge默认模式,使用busybox开启2个容器
此时主机上新增了3个设备
#ip link
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:33:8a:9b:c7 brd ff:ff:ff:ff:ff:ff
15: veth4c0cf57@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether b2:3b:30:86:24:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 1
17: vethb2ff952@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether a6:43:72:73:2f:39 brd ff:ff:ff:ff:ff:ff link-netnsid 2其中:
-
docker0,为docker daemon创建的一个虚拟网桥,可以简单理解为交换机,这个交换机用于各个容器的通信。正常交换机是没有IP的,现实中使用交换机是将一根网线插上面,而这里是虚拟的交换机,主机怎么识别呢,答案是分配一个IP,这个IP就是主机连接虚拟交换机的IP,也是网关地址。(完全可以把这个IP当作主机在这个网段的IP)
-
veth4c0cf57@if14和vethb2ff952@if16是虚拟网卡,进入busybox 容器,能看到2个容器的网卡
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff 16: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff观察网卡前面的数字以及@if后面的数字,14 — 15,16 —17,这4个网卡实际是2对。一个在主机上,一个在容器内部。这就是veth-pair技术,总是成对出现,veth-pair就是一个桥梁、一根网线,连接2端,可以**穿透network namespace**,让2端通信,一端发出的数据包可以直接到达另一端。
再观察主机上veth4c0cf57@if14和vethb2ff952@if16虚拟网卡:
# nmcli -p d show GENERAL.DEVICE: docker0 GENERAL.TYPE: bridge GENERAL.HWADDR: 02:42:33:8A:9B:C7 GENERAL.MTU: 1500 GENERAL.STATE: 100(连接(外部)) GENERAL.CONNECTION: docker0 GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/5 IP4.ADDRESS[1]: 172.17.0.1/16 IP4.GATEWAY: -- IP4.ROUTE[1]: dst = 172.17.0.0/16, nh = 0.0.0.0, mt = 0 IP6.ADDRESS[1]: fe80::42:33ff:fe8a:9bc7/64 IP6.GATEWAY: -- IP6.ROUTE[1]: dst = fe80::/64, nh = ::, mt = 256 IP6.ROUTE[2]: dst = ff00::/8, nh = ::, mt = 256, table=255 GENERAL.DEVICE: veth4c0cf57 GENERAL.TYPE: ethernet GENERAL.HWADDR: B2:3B:30:86:24:A7 GENERAL.MTU: 1500 GENERAL.STATE: 10(未托管) GENERAL.CONNECTION: -- GENERAL.CON-PATH: -- WIRED-PROPERTIES.CARRIER: 开 GENERAL.DEVICE: vethb2ff952 GENERAL.TYPE: ethernet GENERAL.HWADDR: A6:43:72:73:2F:39 GENERAL.MTU: 1500 GENERAL.STATE: 10(未托管) GENERAL.CONNECTION: -- GENERAL.CON-PATH: -- WIRED-PROPERTIES.CARRIER: 开他们是没有ip的,为什么?查看状态也是未连接的!
# nmcli -p d status DEVICE TYPE STATE CONNECTION ens33 ethernet 已连接 ens33 docker0 bridge 连接(外部) docker0 veth4c0cf57 ethernet 未托管 -- vethb2ff952 ethernet 未托管 -- lo loopback 未托管 --实际上,此时这2个虚拟网卡被添加到了docker0虚拟交换机上,变成了虚拟交换机的端口,端口是没有IP的。相当于网线一头插主机,一头插交换机,插主机上的网卡有IP地址,插交换机上的很显然没必要有。
# 安装bridge-utils,需要epel源 yum install -y bridge-utils # brctl show docker0 bridge name bridge id STP enabled interfaces docker0 8000.0242338a9bc7 no veth4c0cf57 vethb2ff952 # 可知这2个网卡都添加到了docker0上brctl addif docker0 vetchxxx # 可以将设备添加到交换机上至此:
容器/主机之间的网络通信完全连通。容器怎么连外网呢?通过NAT转发,容器 ⇒ 交换机 ⇒ 网关⇒ NAT转发。
实际交换机和网关就是docker0。
查看HOST中docker0的NAT规则
#iptables -t nat -S | grep docker0 -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE可知:docker0如果收到来自172.17.0.0/16网段的请求,会交给MASQUERADE处理,而MASQUERADE处理方式是将包的源地址替换成HOST的地址再发出去,做了一次网络地址转换(NAT)。
举例:
# 容器ping外网 [wgx@localhost ~]$ sudo docker exec -it test1 ping www.baidu.com PING www.baidu.com (14.215.177.39): 56 data bytes 64 bytes from 14.215.177.39: seq=0 ttl=127 time=23.789 ms 64 bytes from 14.215.177.39: seq=1 ttl=127 time=22.918 ms # 监听主机虚拟网桥 [root@localhost ~]# tcpdump -i br-702eb58eabcf -n icmp dropped privs to tcpdump tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on br-702eb58eabcf, link-type EN10MB (Ethernet), capture size 262144 bytes 09:17:04.601609 IP 172.18.1.1 > 14.215.177.39: ICMP echo request, id 15, seq 0, length 64 09:17:04.625245 IP 14.215.177.39 > 172.18.1.1: ICMP echo reply, id 15, seq 0, length 64 09:17:05.602734 IP 172.18.1.1 > 14.215.177.39: ICMP echo request, id 15, seq 1, length 64 09:17:05.625344 IP 14.215.177.39 > 172.18.1.1: ICMP echo reply, id 15, seq 1, length 64 # 监听主机网卡 [root@localhost ~]# tcpdump -i ens33 -n icmp dropped privs to tcpdump tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes 09:17:55.745305 IP 192.168.227.100 > 14.215.177.39: ICMP echo request, id 15, seq 51, length 64 09:17:55.768962 IP 14.215.177.39 > 192.168.227.100: ICMP echo reply, id 15, seq 51, length 64 09:17:56.748906 IP 192.168.227.100 > 14.215.177.39: ICMP echo request, id 15, seq 52, length 64 09:17:56.772987 IP 14.215.177.39 > 192.168.227.100: ICMP echo reply, id 15, seq 52, length 64可以看到 172.18.1.1容器IP地址,在ping外网时,被NAT转换成了192.168.227.100,也就是HOST的IP地址。
正常都是容器端口映射到主机端口,然后外网访问主机端口,完成对容器的访问。
如果想外网直接访问容器也可以做到(非host模式):
- 创建虚拟网桥,分配IP地址,IP地址为主机所在网络
- 创建veth-pair,一个连接上述网桥,一个分配给容器
- 给容器内的veth分配IP地址,此时和网桥同一网段,实际就是和主机同一网段了,外网能直接访问主机,就能直接访问容器,因为同一网段了
实际就是类似vmware桥接模式(将一个物理网卡开启混合模式,能监听多个IP),虚拟机和主机在一个网段,处于同一层。
网络总结:
docker0或者说bridge网络模式下在host上创建的虚拟设备,这个设备被host识别(也就是适配了,在相关维度,宿主机访问其他设备的代表就是这个设备)。模型可以想象成下面,网卡-网桥-网关就是docker0。
host适配了这个设备,设别了这个网卡,拥有了地址。
容器连接上了网桥,此时容器,host通过网桥完成互相通信。
如果要访问外网,会经过网关,网关进行NAT转换,转换为Host的可用IP地址,然后根据这个IP地址转发到对应的网卡上,从而完成外网的访问。
graph LR 1[host]--适配-->2[网卡] 2---3[网桥] 3---4[网关] 5[容器]--连接-->3
引申内容,vmware的网络模式。
vmware有4种网络模式:
-
none,没有网络,相当于一台没有网卡的设备,即不能访问主机,也不能访问外网。
-
bridge,将host的物理网卡设置成混合模式,能监听多个ip,像网桥一样接入多个设备,此时虚拟机和host在同一层。
-
host-only,虚拟机只能访问host,无法访问外网
-
nat模式
这种模式整体上就是创建一个子网,然后通过nat转发访问外网。
vmware会创建一个虚拟交换机,虚拟DHCP服务器,虚拟网关(NAT服务器),并在host上创建一个虚拟网卡vmnet8。
graph TB 1[虚拟交换机] 2[虚拟DHCP服务器] -->1 3["虚拟网关(NAT)"] ---1 h["host vmnet8"] -->1 V1[VM1] -->1 V2[VM2] -->1 Vn[VM..] -->1 p["host 物理网卡"] ---3-
DHCP: 自动分配虚拟机IP,可以关掉
-
虚拟机之间可以通过交换机通信
-
由于vmnet8的存在,host和虚拟机之间可以通过虚拟交换机通信
-
虚拟机如果要访问外网,可以通过网关进行NAT转换,转换为host的ip,进而通过物理网卡访问外网
虚拟机和vmnet8的网关地址就是虚拟网关的地址。但是host是ping不通这个网关地址的,尽管他们在同一个网段,原因不明(可能是如果host能到达这个网关,那host访问外网是不是也要nat转发一次,然后又转到自身的物理网卡上,画蛇添足?)。
从上图也能看到,虚拟机访问外网是通过网关完成,并没有经过vmnet8,所以就算禁用了vmnet8也不影响虚拟机访问外网,只是不能访问host。
实际上nat模式和host-only模式唯一的区别就是,host-only没有虚拟网关这个设备(host-only下在host上创建的虚拟网卡一般命名为vmnet1),所以无法访问外网。
-
docker run
基本命令
$ docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]选项
-
Detached : -d,背景运行,类似 command &
-
Foreground: 默认行为,前端运行,就是将容器的stdin,stdout,stderr附加到控制台,也就是终端上
-a=[] : Attach to `STDIN`, `STDOUT` and/or `STDERR` # -a=[stdout,stderr]默认行为 -t : Allocate a pseudo-tty # 分配一个伪tty --sig-proxy=true: Proxy all received signals to the process (non-TTY mode only) -i : Keep STDIN open even if not attached # 保持输入流打开,即使没有连接到一般
-it组合使用,才能激活终端、输入流如果要在
-d模式下运行,并且确保命令执行后不会退出容器docker run -d --rm --name test busybox /bin/sh # 执行了sh后没有新的任务,容器自动退出了 docker run -dit --rm --name test busybox /bin/sh # 执行了sh后,由于分配了终端,也没有关闭stdin,容器还在等待输入,此时并不会关闭容器 -
容器命名: 容器在运行时如果没有通过
--name指定一个名称,docker daemon会生成一个UUID,并随即生成一个namedocker run -it --name test busybox /bin/sh -
指定镜像
docker run ubuntu:14.04 # 指定版本,不指定,默认最新版本如果要更精确的指定,可以指定摘要
$ docker run alpine@sha256:9cacb71397b640eca97488cf08582ae4e4068513101088e9f96c9814bfda95e0 date -
设置PID namespace
--pid="" : Set the PID (Process) Namespace mode for the container, 'container:<name|id>': joins another container's PID namespace # 指定其他容器的 'host': use the host's PID namespace inside the container # 和主机共享 -
network: 可以使用的网络模式有:
-
none: 不允许容器有任何网络连接,别人也无法连接过来
-
bridge: 桥接模式,默认模式
-
host: 使用宿主机的网络
相当于不开启network namespace(共用主机网卡)
-
container: 和其他容器共享网络
和其他容器用一个network namespace(共用其他容器网卡)
-
自定义网络: 可以使用docker network driver自建网络,并将容器加入其中
$ docker network create -d bridge my-net $ docker run --network=my-net -itd --name=container3 busybox
还可以指定dns,默认和主机的dns相同;指定IP;指定host;指定mac地址。
--dns=[] : Set custom dns servers for the container --network="bridge" : Connect a container to a network 'bridge': create a network stack on the default Docker bridge 'none': no networking 'container:<name|id>': reuse another container's network stack 'host': use the Docker host network stack '<network-name>|<network-id>': connect to a user-defined network --network-alias=[] : Add network-scoped alias for the container --add-host="" : Add a line to /etc/hosts (host:IP) --mac-address="" : Sets the container's Ethernet device's MAC address --ip="" : Sets the container's Ethernet device's IPv4 address -
-
重启策略:
docker run --restart=on-failure:10 redis # on-failure策略,最多10次Policy Result no 不自动重启 on-failure[:max-retries] 除非正常退出,也就是exit(0),会一直被daemon重启,可以设置最大重启次数,否则将一直重启 always 一直重启,不管退出状态是什么 unless-stopped 一直重启,除非容器正常stop 关于退出状态:
0表示正常退出
非0表示异常,比如:
[root@any ~]# docker exec -it 66c2a929553d /bin/bash;echo $? OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown 126 # 退出number 126,没有/bin/bash这个目录(busybox的shell是sh) -
退出容器时删除
正常退出容器,容器内包括匿名卷都会保留,如果仅仅是临时测试,退出容器后想清除相关数据
--rm # 退出容器后会自动删除容器 -
容器资源约束
很多内容,可以约束内存,比如最小内存多少,最大内存多少,swap多少等等,默认是不约束,要多少给多少
还可以约束cpu,内核内存,硬盘约束等等
比如:
$ docker run -it -m 300M ubuntu:14.04 /bin/bash # 内存300M,Swap300M,一共600M限制 -
特权容器
正常容器无法访问任何设备,但是可以给容器授权,让容器和在宿主机上运行的进程权限几乎一样
Option Description --cap-addAdd Linux capabilities --cap-dropDrop Linux capabilities --privilegedGive extended privileges to this container --device=[]Allows you to run devices inside the container without the —privileged flag. privileged相当于容器中的普通用户提升为真正的root用户可以通过—device限定访问哪些设备,但是遗憾的是通过—device指定后,依然访问不了,需要进一步授权
-
通过
privileged提升为root权限docker run -it --rm --name test3 --device=/dev/sda5 --privileged busybox /bin/sh # 现在可以进行mount了,但是--privileged权限太大,很危险 -
其他方式
--cap-add,添加其他权限docker run -it --rm --name test3 --device=/dev/sda5 --cap-add=SYS_ADMIN busybox /bin/sh # 添加SYS_ADMIN权限,可以进行挂载/dev/sda5 -
如果仅仅是要或的设备的read,write,mknod权限
docker run -it --rm --name test3 --device=/dev/sda5:rwm busybox /bin/sh # 默认是可以rwm的,可以手动控制
-
-
日志驱动
默认使用json-file,还有一些其他指定。
-
Dockfile
通过dockfile启动的容器,可以在run的时候指定参数覆盖dockerfile中的参数
但是
FROM,MAINTAINER,RUN, 和ADD的值是不能覆盖的。下面的属性可在run的时候覆盖:
-
CMD
# 可以使用COMMAND替换dockfile中的CMD指定的指令 docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...] -
ENTRYPOINT
ENTRYPOINT是容器启动时默认执行的,可以看作init(1),1号进程。
实际上ENTRYPOINT和CMD很像,如果没有指定ENTRYPOINT会去执行CMD作为init,如果2者同时存在,CMD会组合进ENTRYPOINT的指令中,当作选项或参数
docker run -it --entrypoint /bin/bash example/redis -c ls -l # COMMAND作为了entrypoint执行文件,也就是/bin/bash的参数甚至可以直接重置掉ENTRYPOINT
docker run --entrypoint="" busybox /bin/sh -
EXPOSE
暴露端口
--expose=[]: 暴露容器一个、一组或一段范围端口 -P : 把暴露出去的端口映射到Host的随机端口上 -p=[] : 精确映射一个端口或一段范围端口,format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort;-p 1234-1256:1234-1256/tcp --link="" : Add link to another container (<name or id>:alias or <name or id>)关于
--linkdocker run -it --rm --expose 10080 --name test3 busybox /bin/sh # test3容器 docker run -it --rm --link test3:busytest3 --name test4 busybox /bin/sh # test4容器test4 link test3并给被连接对象test3指定别名busytest3
此时:
-
查看test4中的/etc/hosts
172.17.0.4 busytest3 1791fb88ae3a test3 # 此时test4拥有test3的容器name,容器id,以及别名设置的host -
查看env
BUSYTEST3_NAME=/test4/busytest3 # 获得Alias_NAME的环境变量 -
test3暴露端口10080,test4环境变量中同样有记录
BUSYTEST3_PORT_10080_TCP_ADDR=172.17.0.4 BUSYTEST3_PORT_10080_TCP_PORT=10080 BUSYTEST3_PORT_10080_TCP_PROTO=tcp BUSYTEST3_PORT=tcp://172.17.0.4:10080 BUSYTEST3_PORT_10080_TCP=tcp://172.17.0.4:10080
当使用
--link时,连接容器和被连接容器之间通过私有网络接口连通了,可以通过域名直接访问,如果暴露了端口,同样可以访问这些端口(暴露端口仅仅是声明本机暴露了相关端口,如果能访问本机,就能访问这个端口,但是并没有和host端口进行映射,也就是访问host时转发不到容器中)。 -
-
ENV
Linux docker默认会生成一些环境变量:
Variable Value HOMESet based on the value of USERHOSTNAMEThe hostname associated with the container PATHIncludes popular directories, such as /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binTERMxtermif the container is allocated a pseudo-TTYexport TODAY=WEN docker run -it --rm --name test5 -e TODAY -e "VAR=123" busybox /bin/sh # 指定环境变量,可使用当前的环境变量,或者完全自定义 -
HEALTHCHECK
健康检查,可以在运行容器时,使用健康检查,指定检查命令,检查间隔,失败次数等,开启了健康检查的容器,在
docker ps也会有相关状态。初始状态:
starting健康:
healthy不健康:
unhealthy--health-cmd 检查命令,容器内执行的命令 --health-interval 检查间隔 --health-retries 尝试次数 --health-timeout 超时时间 --health-start-period 开始检查的等待时间 --no-healthcheck 不开启健康检查 -
TMPFS
tmpfs是一个在内存中的虚拟文件系统,一般默认都支持,可以通过
df -h查看挂载临时文件系统命令: mount -t tmpfs -o size=20m tmpfs /tmp
通过—tmpfs可以挂载容器内目录到tmpfs
docker run -it --rm --tmpfs /tmp:size=20m --name test6 busybox /bin/sh # 将一个20M的tmpfs挂载到/tmp目录上了 -
VOLUME
容器持久化存储技术:
容器中目录和host目录的关联:官方称这种映射关联为挂载,实际也可以理解为挂载,把host目录、文件、tmpfs作为一个文件系统挂载到容器中的目录上。
主要有3种方式,指定容器卷挂载到容器内目录,指定host文件系统(文件或目录)挂载进容器内目录,挂载tmpfs,容器卷是可被docker直接管理的,但是host文件系统是被host管理。

从图上可以很直观的3种方式的区别,
bind对应host 文件系统的内容(理论上docker daemon host上的所有文件、目录都可以挂载进容器),volume对应docker管理区域(/var/lib/docker/volumes),tmpfs mount将tmpfs(内存中)挂载到容器中目录。实际可以看出bind mount已经超出了docker的管理范围,可能对其他非相关文件,进程等产生影响,所以非必要建议不要使用。
-
volume方式
容器卷是独立管理,也就是可以提前创建,同时容器的存在与否并不影响容器卷的状态(匿名卷在docker rm -v container模式下会随着容器的删除而删除)。一个volume可以被挂载到多个容器上。
docker volumn create my_vol # 创建一个具名容器卷 docker volumn ls # 查看 docker volumn inspect my_vol docker volumn rm my_vol # 删除容器卷可以在run的时候创建,此时可以是具名的或匿名的,匿名的由docker daemon指定一个唯一的名字。
docker run -d --name test -v my_vol:/tmp:ro busybox /bin/sh # volumn_name:dest:option ,dest必须是容器中目录,option可以设置只读ro,读写rw,这个权限是针对卷的。如果省略volumn_name会创建一个匿名卷。my_vol并不需要提前创建,docker daemon可以自动以这个名字创建具名卷。 -
bind mount方式
mount的方式,重点在type:
- volumn: 此时和容器卷的表现一致
- bind: bind模式,可以挂载host上任意文件和目录
- tmpfs: 挂载tmpfs文件系统
docker run -d --name test --mount type=[volumn|bind|tmpfs] , source=/...,target=/...,readonly,volume-opt=xxx # type默认是volumn # source/src,如果type是volumn,那这里可以指定目录,或容器卷 # target/dst,容器内的目录 # readonly只读模式 # volume-opt 可以指定很多选项,可以指定多个 # 以上选项都用逗号隔开,无顺序要求 -
tmpfs挂载
可以使用
--tmpfs,也可以通过上面的--mount type=tmpfs来指定docker run -it --mount type=tmpfs,target=/tmp,tmpfs-size=20m,tmpfs-mode=1770 busybox /bin/sh docerk run -it --tmpfs:size=20m /tmp: -
--volumes-from: 把其他容器挂载的内容引用过来,相当于其他容器挂载的目录和容器卷,原样挂载进当前容器docker run -it --rm --name test3 -v /tmp/mydata busybox /bin/sh docker run -it --rm --name test4 --volumes-from test3 busybox /bin/sh # 此时test4,出现/tmp/mydata,并被挂载了相同的容器卷可以很方便的进行备份
docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata # 比如dbstore中的dbdata,备份进/backup,相当于备份进了当前目录
bind propagation:
这是一个嵌套挂载的数据传播属性,比如/mnt 挂载到/tmp上,然后/foo挂载到/tmp/a上,默认/tmp/a下面的内容也是会传播到/mnt里的。
这是一个高级选项,一般很少需要去修改。
关于容器无法删除错误:
提示容器无法删除,因为Device or resource busy,这时候可以通过
lsof找到使用这个资源的进程==注意:==
- 如果挂载的volume为空目录,则容器中的对应目录里的内容会copy进volume。(-v 模式下可以设置option,nocopy阻止容器内的内容在挂载时自动复制进volume中。bind模式下不支持修改)
- 如果挂载的文件系统或volume为非空目录,且容器中对应的目录不为空,那么和正常的挂载一样,容器中目录下的内容会被隐藏(不是删除),取消挂载后就能看到。
-
-
USER
通过
-u选项可以覆盖dockerfile中指定的USER,指定的用户如果是数值,会当作uid,且这个uid不需要再容器中提前存在,如果指定的是name,那必须在容器中存在。这个用户会去执行第一个process。[ user | user:group | uid | uid:gid | user:gid | uid:group ] # 可以同时指定group -
WORKDIR
工作目录,实际就是进去后所在目录
-w可以进行覆盖。
-
docker attach
获取一个容器的stdin,stdout,stderr,实际就是进入这个容器
docker attach test1docker build
根据dockerfile构建image
虽然可以使用远程地址,比如Git构建,但是一般都是使用本地文件构建。
docker build [options] [path] <context> # 最后的context是构建的上下文,默认会去这个context下找Dockerfile文件,path是相当于上下文的路径docker build . # 当前目录是context,去当前目录下找Dockerfile# 指定当前目录下的dockerfiles文件夹下的内容,必须要用-f指定
docker build -f dockerfiles/Dockerfile.base .
docker build -f dockerfiles/Dockerfile.prov .
docker build -f dockerfiles/Dockerfile.dev .docker build -t myapp:1.0 . # -t 指定tag如果一个Dockerfile有多个阶段,比如多个FROM,可以通过--target指定以哪一个作为final阶段
FROM debian AS build-env
...
FROM alpine AS production-env
...docker build -t mybuildimage --target build-env .成功构建后删除中间容器
docker build --rm .
docker builder prune # 删除所有的中间缓存docker commit
将一个容器生成镜像
docker commit [options] CONTAINER IMAGENAME:TAG提交期间容器是暂停的,可以通过-p false不暂停
-m: 添加说明
-a: 添加作者
-c: 改变配置,比如当前容器的镜像指定的CMD,EXPOSE等
docker container
相关命令同docker command
docker cp
docker cp [options] SRC_PATH CONTAINER:DEST_PATH # 将宿主机中的文件复制进容器中 -a 选项可以复制UID/GID等信息
docker cp [options] CONTAINER:SRC_PATH DEST_PATH # 将容器中的文件复制进宿主机docker diff
docker diff CONTAINER # 容器修改了哪些内容
# A 表示add
# D 表示delete
# C 表示changedocker create
和docker run的选项参数相同,docker run实际是先create再run
docker create ...
docker start containerstart容器会去执行 docker ps 中显示的command,也就是docker create/run指定的entrypoint cmd,或者是dockfile中指定的ENTRYPOINT CMD
docker exec
在主机中执行容器命令
docker exec CONTAINER COMMAND # command必须是可执行的可以-e设置环境变量,-w设置WORKDIR
docker exec -it -e VAR=1 -w /tmp test1 /bin/sh # 开启一个新的bash,可以看到pid不是1,此时exit并不会kill容器docker export
将容器的文件系统导出
docker export -o test1.tar test1dokcer history
查看image的history
docker history [options] IMAGEdocker history --no-trunc f4f02e2c32f0 # 完成的输出,不进行截断
IMAGE CREATED CREATED BY SIZE COMMENT
sha256:f4f02e2c32f07d015faee4464cc1ba5543c15e6d7d3df71dfb2e59d05f12c730 6 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/app/hello.sh"] 0B
sha256:ae9e62f2be44feba8a4232d4072ae40c276907cf8b6bcf34673eff977cb37851 6 hours ago /bin/sh -c #(nop) COPY dir:d526a01dff55d760ca6fbc6c95bb81b42cbaffe3bfe06bc24513a4a98eae1f64 in /app 161B
sha256:7d0d8fa372249c6a1de9868ad62af9a14aaae2a2b17da867d8fad099a637fd0f 13 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> docker history -q f4f02e2c32f0 # 只显示ID
docker history -H f4f02e2c32f0 # 更易读的方式显示docker history --format "{{.ID}}:{{.CreatedSince}}:{{.Size}}" f4f02e2c32f0 # 格式化输出可以使用的格式:
| Placeholder | Description |
|---|---|
.ID | Image ID |
.CreatedSince | Elapsed time since the image was created if --human=true, otherwise timestamp of when image was created |
.CreatedAt | Timestamp of when image was created |
.CreatedBy | Command that was used to create the image |
.Size | Image disk size |
.Comment | Comment for image |
docker image
- docker image prune -a: 移除所有未被使用的image
- docker image ls: 列出所有image
- docker image rm: 删除image
docker info
docker info # 展示当前docker实例的整体信息docker inspect
以json格式展示docker objects的详细信息
可以给容器、镜像、Volume、Network等使用
docker inspect test1 # 查看这个容器
docker inspect my-net # 查看network
docker inspect busybox # 查看imagedocker kill
docker kill -s 1 test1 # 发送Sign指令,常用的有 1,9,15docker load
docker load < busybox.tar.gz # 实际就是加载本地image包,让其被docker纳入管理
docker load -i busybox.tar.gz # 效果同上docker logs
docker logs test1 # 查看某个容器的日志docker network
-
create
docker network create [OPTIONS] NETWORK指定驱动,如果是单主机使用
bridge,如果是多主机且容器在一个子网里,那必须使用overlaydocker network create -d bridge my-net # 注意my-net的命名,首先必须是唯一的,第二这个名字并不是host中虚拟网桥的名字,会自动生成一个,一般是br-xxxxx其他常用选项
docker network create \ -d bridge \ --subnet 172.18.0.0/16 \ # 子网范围 --ip-range 172.18.5.0/25 \ # 自动分配ip的范围,/25,表示后面的主机号只有7位了,能分配0-128。 --gateway 172.18.5.1 \ # 网关地址,也就是虚拟网桥的地址,如果不指定,会自动生成 br0 # docker run --network br0 --ip 这里指定的IP只要是subnet中的IP即可,并不一定要在ip-range中,但是建议符合ip-range -
connect
docker network connect [OPTIONS] NETWORK CONTAINER容器在启动时可以通过
--network my-net指定子网,对于一个启动了的容器,可以通过connect连接docker network connect \ --ip 172.18.5.2 # 指定ip --alias tt # 在这个网络中给容器分配一个别名 br0 # 指定network test3 # 容器name/id -
disconnect
docker network disconnect br0 test3 # -f可以强制断开,注意容器必须是运行中的 -
ls
docker network ls # 展示所有子网 -
prune rm
docker network prune # 删除所有未被使用的 docker network rm my-net # 删除指定的network
docker start stop restart pause unpause
docker COMMAND CONTAINER # 开启、停止、重启、暂停、取消暂停容器,可以一次操作多个容器docker port
docker port CONTAINER # 展示容器expose的端口以及映射关系docker ps
docker ps \ # 展示容器
-a # 展示所有状态的
--no-trunc # 不进行截断
-s # 展示size
--filter # 过滤docker pull
docker pull mysql:5.7.0 # pull imagedocker rename
docker rename test1 test-one # 重命名容器docker rm
docker rm test1 # 删除容器
docker rm -v test1 # 删除容器的同时删除匿名卷
docker rm --link /webapp/redis # 删除基于docker0的--link,这里没有删除容器docker rmi
docker rmi IMAGE # 删除镜像docker save
docker save busybox > busybox.tar # 将镜像打包,默认是打包到stdout
docker save busybox | gzip > busybox.tar.gz # 打包压缩docker search
docker search nginx # 从docker hub中搜索镜像docker stats
docker stats \ # 动态的展示容器资源使用情况
--no-stream \ # 只展示一次
test1结果:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
6a7399899179 test1 0.00% 1.086MiB / 1.748GiB 0.06% 12.1kB / 8.33kB 0B / 0B 2
# CPU % 占主机CPU资源比例
# MEM % 占主机内存
# MEM USAGE / LIMIT 容器允许使用的内存,和已经使用的内存
# NET I/O 网络IO
# BLOCK I/O 硬盘IO
# PIDS 容器有多少进程docker system
-
docker system df: 查看docker磁盘使用情况
-
docker system prune: 删除所有未被使用的镜像、容器、卷、网络等docker对象。
\ -a # 移除所有 --volumes # 移除卷
docker top
docker top test1 # 展示这个容器的进程,注意这里展示的进程是映射到主机的进程,毕竟容器的进程也是跑在主机上,所以容器内的进程在主机上肯定有对应的进程docker update
可以修改资源限制,比如内存限制
docker update -m 500M test1可以修改restart策略
docker update --restart on:failure:3 test1dockerfile
FROM
FROM一般是第一条指令,用于指定基础image。
ARG是唯一一个可以放在FROM之前的指令,可以指定变量,然后FROM可以使用,但是FROM之后的指令无法使用,可以在FROM后面定制ARG。
ARG VERSION=latest
FROM busybox:$VERSION
RUN
ENTRYPOINT和CMD是构建后在容器中运行的指令
而RUN是构建的时候运行的指令,RUN运行的结果可以在后面的指令中应用
RUN有2种模式:
-
RUN <command>: 命令默认使用/bin/sh这个shell。
RUN echo "hello" # /bin/sh -c echo "hello" RUN /bin/bash -c echo "hello" # /bin/sh -c /bin/bash -c echo "hello" -
RUN [“executable”, “param1”, “param2”]
RUN ["/bin/bash","-c","echo hello"] # 更灵活一点,字符串拼接难度降低。并且可以不在shell中执行
CMD
有3种模式
-
CMD command param1 param2: shell模式,同样的默认使用/bin/sh -c 去执行。(如果ENTRYPOINT存在,那么会以/bin/sh -c param1 param2的形式添加在ENTRYPOINT后面)
CMD echo "hello" # /bin/sh -c echo "hello" -
CMD [“executable”,“param1”,“param2”]: 类似上面的RUN exec form,可以直接执行指定executable,而不需要在shell中执行。
CMD ["/usr/bin/wc","--help"] -
CMD [“param1”,“param2”]: 作为ENTRYPOINT的参数
注意:RUN可以有很多个,但是CMD只能有一个,如果有多个只有最后一个生效。
不在shell中执行,在容器中很重要,因为如果在shell中执行,那么shell就是pid=1进程了。很多指令都可以不用在shell中执行。
LABLE
以key=value的格式设置标签,可以设置多个,空格用反斜杠转义,或者使用双引号包裹
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
# 以上也可以放在一行中EXPOSE
暴露端口,注意这里是暴露,并不是publish,只是expose给同一network的其他应用。默认是TCP,可以指定
EXPOSE 80/tcp # 真正的publish需要在run的时候设置,这里实际就是告诉使用镜像的人,你可以映射这个端口ENV
设置环境变量,这里设置的变量可以在dockerfile后面的指令中使用,并且会在容器中存在
ENV TODYA=wen # 同样的空格需要用双引号或者反斜杠转义
# 可以多个ENV,也可以放在一行如果仅仅只是想在build的时候有效,可以使用ARG
ADD
将基于context中的文件或目录添加进容器
src文件基于context相对定位
dest可以基于WORKDIR相对定位,也可以是用绝对定位
ADD 可以添加tar包,会自动解压
src可以有多个,此时dest必须以/结尾
如果dest不存在会自动创建。(mkdir -p)
COPY
对比ADD,src不能是URL和tar,其他基本相似。
能用COPY的地方就不要用ADD
ENTRYPOINT
和CMD一样,有shellform 和 execform2种格式
shellform会在/bin/sh中执行,并且CMD设置的内容和run命令中指定的参数全部失效。同时由于是在sh这个shell中执行,那么进程必然是这个shell的子进程,意味者容器的pid=1进程不是期望的进程,而是shell,期望的进程就无法接收SIGNAL,比如docker stop CONTAINER时,期望的进程无法收到信号(都是发给1号进程的),可能造成困惑和问题。
而execform可以避免这些问题,可以追加CMD和run中的参数,同时直接执行executable,让其成为1号进程。
FROM ubuntu
ENTRYPOINT ["top", "-b"] # 直接执行top命令,查看得知top此时就是pid=1
CMD ["-c"]多个ENTRYPOINT只有最后一个生效。
VOLUME
VOLUME ["/data"]
VOLUME /data /tmpDockerfile无法指定命名卷,这很正常,如果image中指定了命名卷,意味着所有基于这个image的容器都有相同的卷,数据就乱了。
WORKDIR
可以指定多次,如果后面指定的是相对路径,那么就是相对之前的路径
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd # 此时在/a/b/cWORKDIR影响很大,ENTRYPOINT,CMD,RUN在该目录下运行,COPY,AND如果是相对路径,则是这个目录的相对路径。
ARG
ARG user=w # 指定默认值
USER $user # 使用
# ARG只在被定义后才能使用,且只在定义所在的stage生效docker build --build-arg user=www . # 构建的时候指定ARG总是会被同名的ENV覆盖
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER # 不管build --build-arg指定了没有,这里的结果必然是v1.0.0ONBUILD
把一个image作为一个基础image时,这个基础image中定义的ONBUILD会在以这个镜像作为基础镜像构建镜像时调用,调用时机在FROM后面。
# base image
FROM centos
ONBUILD ADD . /tmp
ONBUILD RUN ["/bin/bash","-c","echo hello"]
ENTRYPOINT ["/bin/bash"]# extend image
FROM base-img
ENTRYPOINT ["/bin/bash"]# build extend image
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM base-img
# Executing 2 build triggers 2个触发器被触发了
---> Running in f106ded8eca0
hello # 输出了结果,进入容器可以看到ADD命令的结果
Removing intermediate container f106ded8eca0
---> cf0db7d2d1f5
Step 2/2 : ENTRYPOINT ["/bin/bash"]
---> Running in da69b6f32841
Removing intermediate container da69b6f32841
---> 226b35122d8c
Successfully built 226b35122d8c
Successfully tagged img-extend:latestSHELL
Dockerfile中的指令如果使用的shell form格式,默认调用的就是["/bin/sh","-c"]这个shell,可以通过SHELL重新指定。
SHELL可以出现多次,每一次都会覆盖之前指定的,并影响后面的所有命令。
Compose
安装
直接下载二进制文件
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 速度较慢可以 -x socks5://代理地址修改权限
sudo chmod a+x /usr/local/bin/docker-compose查看版本
docker-compose --version卸载
直接删除下载的那个文件即可
sudo rm /usr/local/bin/docker-composecompose cli
类似docker cli管理镜像、容器、网络、dockerfile等,compose cli也可以基于docker-compose.yml对整个应用进行管理
命令格式:
docker-compose [options] [COMMAND] # 基本格式options
-f
默认会去当前文件夹下寻找docker-compose.yml文件,可以通过-f指定,指定后出现的相对路径都是以这个指定的文件路径为参照。
docker-compose -f ~/docker-compose.yml # 指定yml文件
docker-compose -f ~/docker-compose.yml pull db # 根据这个yml的配置,获取db服务的镜像-p
默认会以当前目录名为project-name(在生成比如network,容器名称时会自动添加project-name前缀)
可以-p指定一个名称
—profile
给service添加profiles标记后,可以通过命令行工具让这些标记enabled,从而启用对应的服务
docker-compose --profile debug --profile dev up环境变量
实际上即可以通过-f,-p等设定内容,也可以使用内置的环境变量,来设置相关内容。可以理解为按以下顺序配置:
- 根据option进行设置
- 根据环境变量进行设置
- 使用默认值
COMPOSE_PROJECT_NAME:
# pwd /root/myapp
export COMPOSE_PROJECT_NAME=Mall;docker-compose up # 此时会以Mall而不是myapp作为项目名称COMPOSE_FILE:
同样的,可以指定yml文件的位置,可以指定多个用:隔开即可
export COMPOSE_FILE=/root/myapp/docker-compose.dev.yml:/path1/path2/docker-compose.yml还有其他变量可以设置。
COMMAND
bash中基于docker-compose的命令补全功能较弱,可以添加额外的补全配置。
sudo curl \
-L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/docker-compose \
-o /usr/local/etc/bash_completion.d/docker-compose source ~/.bashrc # 重新加载配置文件,或者关掉终端,然后重新连接另:
命令一般有2种模式
- 针对service,这和docker command基本一样
- 针对整个应用(命令不体现任何service),相当于针对整个yml配置文件管理的内容
build
docker-compose build [SERVICE...] # build image。如果某个service的dockerfile改变了,可以通过build指令重新build,否则会使用之前build的imageconfig
用于查看yml文件,这里会替换各种变量,生成最终需要的内容
docker-compose configdown
up用于构建,down就是删除,停止容器并删除相关内容
docker-compose down # 默认删除创建的容器,网络。external的容器卷、网络不会被删除
docker-compose down --rmi {all|local} -v # all删除所有image,local删除没有image属性的image,-v删除具名卷,service里的匿名卷exec
docker-compose exec SERVICE /bin/sh # 和docker exec相同images
docker-compose images # 基于这个yml所构建的image lskill
docker-compose kill -s 1|9|15 [SERVICE...] # 默认是9,直接杀掉logs
docker-compose logs --tail 10 [SERVICE...] # 查看日志pause,restart,start,stop,unpause
docker-compose start [SERVICE...] # 类似docker cli里的指令ps
docker-compose ps # 展示这个应用所有启动中的容器情况
# -a 查看所有
# --services 只展示servicepull
docker-compose pull [SERVICE...] # 获取某个服务的image,如果是dockerfile形式,使用build命令
# --includes-deps 包括依赖一起pullrm
docker-compose rm [SERVICE...] # 删除停止的容器
# -f 强制删除
# -s 删除之前先停止
# -v 同时删除匿名卷run
单独运行一个service,注意这里会基于yml的配置运行一个新的容器。run可以使用的选项和docker run类似。
docker-compose run [options] SERVICE [COMMAND] [ARGS...]options基本和docker run 相同,比如
-
-d: 后台运行
-
—name: 指定容器名称
-
—entrypoint: 覆盖yml、Dockerfile或者image中的entrypoint
-
-e: 环境变量
-
—rm: 一次性
-
-p: 端口映射。
注意:run命令不会发布service中指定的端口,以免端口冲突,如果要使用service中的端口定义,添加
--service-ports选项。并且这个选项和-p手动添加的端口映射不能共存。 -
-v: 容器卷
-
-w: 指定workdir
-
—no-deps: 默认会去检查depends_on的服务是否开启,没有开启则启动,然后再run这个容器,可以使用这个选项关掉依赖检查,不去管依赖服务是否启动。
top
docker-compose top [SERVICE...] # 类似docker top CONTAINERup
这是一个复合指令,类似docker run,整合了build/pull,create,start,attach等过程,相当于直接通过yml开启容器。
Usage: up [options] [--scale SERVICE=NUM...] [SERVICE...]
Options:
-d, --detach Detached mode: Run containers in the background,
print new container names. Incompatible with
--abort-on-container-exit.
--no-color Produce monochrome output.
--quiet-pull Pull without printing progress information
--no-deps Don't start linked services.
--force-recreate Recreate containers even if their configuration
and image haven't changed.
--always-recreate-deps Recreate dependent containers.
Incompatible with --no-recreate.
--no-recreate If containers already exist, don't recreate
them. Incompatible with --force-recreate and
--renew-anon-volumes.
--no-build Don't build an image, even if it's missing.
--no-start Don't start the services after creating them.
--build Build images before starting containers.
--abort-on-container-exit Stops all containers if any container was
stopped. Incompatible with --detach.
--attach-dependencies Attach to dependent containers.
-t, --timeout TIMEOUT Use this timeout in seconds for container
shutdown when attached or when containers are
already running. (default: 10)
-V, --renew-anon-volumes Recreate anonymous volumes instead of retrieving
data from the previous containers.默认recreate依然会使用原来的匿名卷。
--remove-orphans Remove containers for services not defined
in the Compose file.
--exit-code-from SERVICE Return the exit code of the selected service
container. Implies --abort-on-container-exit.
--scale SERVICE=NUM Scale SERVICE to NUM instances. Overrides the
`scale` setting in the Compose file if present.
开启多个实例compose file
这里以v3版本为例。使用yaml格式配置,默认读取./docker-compose.yml(yaml也可)。
compose file文件结构
compose可以将多个服务以yaml的格式进行配置,类似Dockerfile,这也是一个配置文件
# docker-compose.yml
version: "3.9"
services:
redis:
image: redis:alpine
ports:
- "6379"
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
db:
image: postgres:9.4
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
placement:
max_replicas_per_node: 1
constraints:
- "node.role==manager"
vote:
image: dockersamples/examplevotingapp_vote:before
ports:
- "5000:80"
networks:
- frontend
depends_on:
- redis
deploy:
replicas: 2
update_config:
parallelism: 2
restart_policy:
condition: on-failure
result:
image: dockersamples/examplevotingapp_result:before
ports:
- "5001:80"
networks:
- backend
depends_on:
- db
deploy:
replicas: 1
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
worker:
image: dockersamples/examplevotingapp_worker
networks:
- frontend
- backend
deploy:
mode: replicated
replicas: 1
labels: [APP=VOTING]
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
placement:
constraints:
- "node.role==manager"
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints:
- "node.role==manager"
networks:
frontend:
backend:
volumes:
db-data:环境变量
compose配置文件更大,需要使用变量的机会更多,可以设置ENV,然后直接使用
services:
web:
image:"webapp:${TAG}" # TAG首先会去当前shell下找这个环境变量,如果没有则去找指定的env文件env文件:
- 可以在项目目录下指定
.env文件,会自动读取这里的变量 - 可以使用
--env-file指定配置文件路径
注意:以上都是给compose设置变量,用于compose构建阶段,最终是通过compose构建image,通过image启动容器。
那怎么在compose中给容器设置env呢?就像在Dockerfile中设置ENV和docker run中指定-e一样。
web:
environment: # 直接指定环境变量
- DEBUG=1 # 给容器设置环境变量,如果不指定=1,会去当前shell中找
env_file: # 通过额外的文件指定环境变量
- ./web_var.env # 直接指定./web_var.env这个路径文件,把里面的内容都当作环境变量还可以docker-compose run -e的时候指定,类似docker run -e
优先级:
这么多地方可以指定容器的环境变量,那读取顺序呢?
-
compose file中设置的
-
shell 环境变量
-
env文件
-
Dockerfile中设置的
给服务设置启动条件
通过给服务设置profiles数组,数组中的一个生效则服务会启动。
不指定profiles的服务默认是可以被直接启动的。
version: "3.9"
services:
frontend:
image: frontend
profiles: ["frontend"]
phpmyadmin:
image: phpmyadmin
depends_on:
- db
profiles:
- debug
backend:
image: backend
db:
image: mysql-
通过启动命令指定生效的profile
docker-compose --profile frontend up # 此时frontend,backend,db生效 docker-compose --profile debug up # 此时phpmyadmin,backend,db生效 -
启动时明确启动服务,该服务的profiles自动生效
docker-compose up phpmyadmin # 此时debug自动生效了注意:
此时只会自动生效目标服务的
profiles,目标所依赖的服务的profiles不会自动生效比如:
version: "3.9" services: phpmyadmin: image: phpmyadmin depends_on: - db profiles: - debug db: image: mysql profiles: - dev此时:
docker-compose up phpmyadmin # 此时会启动失败,因为默认只enable了debug,而phpmyadmin的依赖服务db的profile,dev并没有生效,导致db启动失败有2种解决办法:
-
给db再添加debug的profile
db: image: mysql profiles: - dev - debug -
启动时通过
--profile dev让dev生效
-
多配置文件
一个应用根据配置文件,表现不同的模式,这是很常见的需求,比如dev,prod,test等情况。
虽然通过改变shell的环境变量,或者.env的值,能给compose file设置一定的变化,但是只通过变量无法完成更复杂的差异化配置。
此时可以使用多配置文件,并结合merge策略,完成不同模式的构建。
默认情况下,可以有2个配置文件:
- docker-compose.yml
- docker-compose.override.yml
docker-compose up会直接合并docker-compose.yml,docker-compose.override.yml,然后使用合并后的配置文件。
可以自定义多个配置文件,然后通过-f指定,同时-f实际可以指定多个文件,然后根据指定的顺序进行合并,特别注意路径问题,后面-f指定的文件,都是以第一个-f指定的文件作为相对路径。合并模式是后面的覆盖前面的配置,最终形成一个配置文件。
类似Dockerfile的-f,默认docker-compose up会去当前路径下找docker-compose.yml文件,也可以根据-f指定yml文件
比如:
# docker-compose.yml
web:
image: example/my_web_app:latest
depends_on:
- db
- cache
db:
image: postgres:latest
cache:
image: redis:latest# docker-compose.override.yml
web:
build: .
volumes:
- '.:/code'
ports:
- 8883:80
environment:
DEBUG: 'true'
db:
command: '-d'
ports:
- 5432:5432
cache:
ports:
- 6379:6379# docker-compose.prod.yml
web:
ports:
- 80:80
environment:
PRODUCTION: 'true'
cache:
environment:
TTL: '500'docker-compose up
# docker-compose.override.yml 覆盖 docker-compose.ymldocker-compose -f docker-compose.yml -f docker-compose.prod.yml up
# docker-compose.prod.yml 覆盖 docker-compose.yml合并策略:
-
如果是单个值得情况,比如image,存在则替换,不存在则直接使用
-
如果是多个值得情况,基本都是以数组的形式存在的,比如expose,会取并集。
-
如果是多个值,以key=value对的情况出现的,取并集,以key作为判断依据。
注意:volumes的key是
:后面的路径,也就是容器中的路径。
compose file V2版本(compose file的版本,不是docker compose的版本)中,引入了一个新的配置选项extends
和-f的合并不同,这里可以直接继承某个文件的某个服务的所有配置内容过来
# common-services.yml
services:
webapp:
build: .
ports:
- "8000:8000"
volumes:
- "/data"# docker-compose.yml
services:
web:
extends:
file: common-services.yml
service: webapp你可以直接使用这些继承过来的配置内容,还可以重新定义,或者添加新的配置项。
网络
通过docker compose构建的service,默认会加入一个以项目命名的bridge网络中,项目名称默认是docker-compose配置文件所在目录名,可以通过—project-name指定,或者设置COMPOSE_PROJECT_NAME环境变量。
比如项目名称为:myapp,这个默认构建的bridge网络为myapp_default。
同时构建的容器在这个网络中拥有对应的名称(hostname),即服务名,比如web,db。
...
"Networks": {
"myapp_default": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"5113b5fb6ba5",
"web"
],
"NetworkID": "e3097d50fa66ebde46804bd5655bb2b49131c3ccc1b8e2ad75e291981f044eb7",
"EndpointID": "ebe5b1e9ee1bbba37bbd0db445006819851c650fed424db2ff586abc29f51e74",
"Gateway": "172.19.0.1",
"IPAddress": "172.19.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:13:00:02",
"DriverOpts": null
}
}
}
...可以直接用这个名称来访问对应的服务,比如 web:80。
使用名称有个好处,如果compose file修改了,重新up时会删除原来的容器,并生成新的容器,同时可能指定了新的IP,但是只要service名称不变,以名称作为连接的地方就都不需要改变。
**注意:**只能在这个网络内部使用这个名称,宿主机访问,只能通过对应的容器ip以及映射的端口访问。
可以自定义网络,可以定义顶级networks,也可以定义service级networks
可以不创建默认的网络,而是加入一个已存在的网络
services:
# ...
service-name:
# 如果对于指定外部网络的需要指定IP地址的,可以如下设置
networks:
default:
ipv4_address: xxxxx
networks:
default: # 将下面的网络当作默认网络
external: true # 允许加入已存在的网络
name: my-pre-existing-network # 指定这个网络服务启动顺序
虽然有depends_on,会在依赖是running状态时,再启动服务,但是这并不能完全保证依赖已经启动,可以借用第三方脚本
wait-for-it.sh,这个脚本会监听一个host:port,一当监听有返回时,会执行--后面的指令
version: "2"
services:
web:
build: .
ports:
- "80:8000"
depends_on:
- "db"
command: ["./wait-for-it.sh", "db:5432", "--", "python", "app.py"]
db:
image: postgres等待db:5432这个端口能进行响应了,会执行python app.py这个指令,也就是启动web服务了。
compose file 配置
build
调用dockerfile进行build image
2种用法:
-
直接一个目录作为context,会自动取寻找这个context下的Dockerfile
version: "3.9" services: webapp: build: ./dir -
以object的形式配置更详细的内容
verson: "3.9" services: webapp: build: context: ./dir # 指定上下文路径 dockerfile: dockerfiles/Dockerfile.base # 指定Dockerfile路径 args: # 配置Dockerfile中的ARG参数 - arg1=1 # 不指定值会去找compose下的环境变量 - arg2=2 image: webapp:tag # 构建的镜像名称,替换默认的image名称
cache_from
构建镜像时使用相关镜像的缓存
build:
context: .
cache_from:
- alpine:latest
- corp/web_app:3.14network
配置网络,默认compose会创造一个project-name_default的bridge网络,并将所有服务加入其中。
这里可以配置使用host,none,自定义网络
build:
context: .
network: hosttarget
cap_add,cap_drop
容器的权限修改,比如设备的访问权限,这在docker cli的run命令中提到过
cap_add:
- ALL
cap_drop:
- NET_ADMIN
- SYS_ADMINcommand
容器启动后执行的命令,类似Dockerfile中的CMD,也有2种写法,建议使用execform格式
command: ["top","-d","5"]container_name
默认会以project-name_service-name_sequence这样的格式生成容器名称
depends_on
通过依赖关系,构建启动,停止顺序
version: "3.9"
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres启动时,先启动redis和db,停止时同样
而且单独启动某个服务,也会顺带启动depends_on下的服务。
docker-compose up SERVICE # 会自动启动SERVICE下的depends_ondns
自定义DNS
dns: 8.8.8.8dns:
- 8.8.8.8
- 9.9.9.9entrypoint
覆写默认的entrypoint
entrypoint: ["top","-b","-n","10"]env_file
给service设置环境变量。默认是以项目目录为相对路径,如果通过-f指定了compose file,则是以这个compose file作为相对路径。
env_file:
- ./common.env
- ./apps/web.env
- /opt/runtime_opts.env # 下面的会覆盖上面的同名变量environment
environment:
- RACK_ENV=development
- SHOW=true # 如果是对象形式,true,false等需要被'true'包裹,以免被解析
- SESSION_SECRET # 不指定值,则取当前compose下的环境变量expose
暴露端口,并没有映射,只能被相关的服务访问,也就是同一网段的服务访问
expose:
- "3000"
- "8000"extra_hosts
添加额外的域名映射,类似docker run --add-hosts
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"对应的服务启动的容器/etc/hosts中会添加对应的记录
healthcheck
健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40stest如果是以execform格式时,第一个元素必须是NONE,CMD,CMD-SHELL
- NONE表示不进行healthcheck,就算image/Dockerfile设置了healthcheck,这里也会取消
- CMD表示直接允许,而不是在一个shell中允许
- CMD-SHELL,在shell,默认/bin/sh中允许,test如果是shellform格式,默认就是在/bin/sh中允许
image
指定镜像
image: ubuntu:18.04logging
日志选项
version: "3.9"
services:
some-service:
image: some-service
logging:
driver: "json-file" # 日志驱动,类似run --log-driver。只有json-file,syslog,none三个选项
options: # 选项,类似run --log-opt
max-size: "200k" # 单个日志文件的大小
max-file: "10" # 日志文件的数量,如果超过了就删除最旧的那个network_mode
和docker run --network一样。针对单个服务,比如有2个服务,一个使用了host,一个默认的,那么依然会创建一个以这个项目命名的bridge网络,然后默认的那个服务加入这个桥接网络,host那个使用主机的network namespace;但是如果2个都是host,是不会创建桥接网络的。
version: "3.9"
services:
web:
image: nginx
network_mode: host # 会和主机使用一个network namespace,能看到主机的所有网络设备
db:
image: mysql # 此时nginx处于主机网络中,依然可以通过这个yml生成的虚拟网桥连接网桥中的其他服务可以设置bridge,none,host,其他服务的network,其他容器的network
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"network
配置容器连接的网络,这些网络需要提前在顶级选项下配置
services:
some-service:
networks:
- some-network
- other-network
networks:
some-network:
other-network:修改默认网络配置
services:
# ...
networks:
default:
external: true
name: my-pre-existing-network # 此时默认网络会去外部找这个网络,比如docker network create my-pre-exist...services:
# ...
networks:
default:
driver: custom-driver-1 # 给默认网络指定自定义driver指定外部网络
version: "3.9"
networks:
outside:
external: true # 不会创建一个project-name_outside网络,而是去外部找outside这个已经存在的网络version: "3.9"
networks:
outside:
external: true
name: actual-name-of-network # 去外部找actual-name-of-network这个网络,并且引用名称为outsidealias
在不同的网络给当前服务指定别名,同一个网络下可以用这个alias连接到这个服务
services:
some-service:
networks:
- some-network
aliases:
- alias1
- alias2
- other-network
aliases:
- alias1
- alias4
network:
- some-network
- other-networkalias是给自己取别名,通过这个名字可以连接到这个服务,—add-hosts是添加host,比如 172.19.0.2 service1
ipv4
可以给服务配置静态ip地址,前提是顶级networks下需要配置ipam,并指定subnet
version: "3.9"
services:
app:
image: nginx:alpine
networks:
app_net:
ipv4_address: 172.16.238.10
ipv6_address: 2001:3984:3989::10
networks:
app_net:
ipam:
driver: default
config:
- subnet: "172.16.238.0/24"
- subnet: "2001:3984:3989::/64"pid
pid: "host" # 和主机共享pid维度的命名空间ports
暴露并映射端口
ports:
- "3000" # host指定随机端口映射
- "3000-3005" # host指定随机端口段映射
- "8000:8000" # host8000:container8000
- "9090-9091:8080-8081" # host端口段:container端口段
- "49100:22" # 映射不同的端口
- "127.0.0.1:8001:8001" # 指定网络接口映射,默认是0.0.0.0,也就是所有网络接口
- "127.0.0.1:5000-5010:5000-5010" # 指定网络接口映射
- "127.0.0.1::5000" # 指定网络接口,随机端口映射
- "6060:6060/udp" # upd协议映射
- "12400-12500:1240" # 一个端口映射到host的端口段,注意不能反过来还有一种object的映射方式
ports:
- target: 80 # 容器端口
published: 8080 # 主机端口
protocol: tcp # 协议
mode: host # 模式,只有host和ingress2种模式,ingress用于docker swarmprofiles
控制service是否启动,不声明这个属性,表示默认启动
profiles:
- debug
- testrestart
重启策略,和docker run,dockerfile中相同设置
restart: "no" # 默认值,不重启
restart: always # 一直重启
restart: on-failure # exit code不为0时,会重启
restart: unless-stopped # 一直重启,除非已经stoppedsecrets
存储机密信息
version: "3.9"
services:
redis:
image: redis:latest
deploy:
replicas: 1
secrets:
- my_secret # 引用secret,同时将对应的文件挂载进容器的/run/secrets/my_secret
- my_other_secret
secrets: # 被使用的机密文件,必须先在顶级结构中定义
my_secret: # 机密的名称
file: ./my_secret.txt # 具体内容
my_other_secret:
external: true # 表示是外部定义的secret,比如docker secret create my_other_secret - (从stdin生成secret)还有一种object写法,可以进行更细粒度的控制
version: "3.9"
services:
redis:
image: redis:latest
deploy:
replicas: 1
secrets:
- source: my_secret # 表示引用的是哪个secret
target: redis_secret # 表示挂载进容器/run/secrets/redis_secret这个目录,默认是source的名称
uid: '103' # uid,默认0
gid: '103' # gid, 默认0
mode: 0440 # 权限,默认0444,具有读权限
secrets:
my_secret:
file: ./my_secret.txt
my_other_secret:
external: truestop_grace_period
在发送stop_signal指定的停止信号并没有被响应时,开始尝试stop的时间,时间超过还没有停止则发送SIGKILL信号杀死进程,默认10s
stop_signal
stop信号,默认SIGNTERM(15),可以自行指定。
tmpfs
挂载临时文件系统
- type: tmpfs # 挂载临时文件系统
target: /app # 容器内的目录
tmpfs: # 配置相关选项
size: 1000volumes
容器卷挂载
可以使用short语法或long语法格式,都是围绕容器卷,匿名卷,host path,tmpfs几种情况展开
具名卷必须先在顶级结构volumes下声明,才能在service上使用。(具名卷一般用于多个服务共享一个容器卷)
version: "3.9"
services:
web:
image: nginx:alpine
volumes:
- type: volume # long语法格式,指定类型,volume,type,tmpfs
source: mydata # 如果是volume,此时source可以是具名卷名称,如果省略就是匿名卷
target: /data # 容器内目录,也就是挂载点
volume: # 配置volume属性
nocopy: true # 不进行自动复制,挂载点目录里的内容不自动复制进容器卷中
read_only: true # 设置容器卷权限为只读,默认rw
- type: bind # 类型为bind
source: ./static # source可以是相对路径或绝对路径,相对路径以compose file路径作为参照
target: /opt/app/static # 容器内目录
- type: tmpfs # tmpfs类型
target: /run # 此时source不再需要
tmpfs: # 配置tmpfs属性
size: 200 # 默认单位bytes,可以指定string格式,200b,200kb,200mb,200gb
db:
image: postgres:latest
volumes: # short语法格式
- "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock" # 实际就是bind host上的path
- "dbdata:/var/lib/postgresql/data" # 挂载具名卷
- "~/configs:/etc/configs/:ro" # 最后可以设置mode,默认rw
- "/var/lib/postgres" # 挂载匿名卷
volumes: # 声明具名容器卷,可多服务共享
mydata:
dbdata:
otherdata:
external: true # 引用已经存在的具名卷,比如docker volume create otherdata
name: volume-name # 还可以以name的形式指定外部已经创建好的容器卷名称,这样引用的时候可以指定不同的名称domainname, hostname, ipc, mac_address, privileged, read_only, shm_size, stdin_open, tty, user, working_dir
这些单属性和docker run中的表现一致
比如hostname
service:
web:
hostname: my-web #此时这个服务实际上有3个Alias,containerId,web,my-web,通过这些名称都可以直接访问(同一network)