第一章 初识Docker
1.1 符合标准1.0
待补充的部分
[root@master ~]# yum install wget net-tools vim yum-utils -y
yum --config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
1.2 安装docker
删除系统可能存在的docker
[root@master ~]# yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
[root@docker ~]# yum search docker-ce #可省略
[root@docker ~]# yum install docker-ce-19.03.6 docker-ce-cli-19.03.6 containerd.io
[root@docker ~]# service docker start
[root@docker ~]# chkconfig docker on #开机启动
查看docker版本
[root@template ~]# docker version
Client: Docker Engine - Community
Version: 19.03.6
API version: 1.40
Go version: go1.12.16
Git commit: 369ce74a3c
Built: Thu Feb 13 01:29:29 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.6
API version: 1.40 (minimum version 1.12)
Go version: go1.12.16
Git commit: 369ce74a3c
Built: Thu Feb 13 01:28:07 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.22
GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca
runc:
Version: 1.1.8
GitCommit: v1.1.8-0-g82f18fe
docker-init:
Version: 0.18.0
GitCommit: fec3683
查看docker的基本信息
[root@template ~]# docker info
Client:
Debug Mode: false
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 19.03.6
Storage Driver: overlay2 #一定得是overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs #一定得是cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive #不活跃状态
Runtimes: runc #运行时 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 8165feabfdfe38c65b599c4993d227328c231fca
runc version: v1.1.8-0-g82f18fe
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-1160.95.1.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 7.638GiB
Name: template
ID: KSTG:266G:AKUX:XYZH:CEWZ:WEAB:WNPY:74NB:EPLA:RJ53:UJXD:3GK2
Docker Root Dir: /var/lib/docker #容器存放的地方
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
1.3 修改Docker存储目录
[root@template ~]# vim /usr/lib/systemd/system/docker.service
[Service]
ExecStart=/usr/bin/dockerd --graph=/data/docker -H fd:// --containerd=/run/containerd/containerd.sock
[root@template ~]# systemctl daemon-reload
[root@template ~]# systemctl restart docker
添加镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://th8d5hdw.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
启动第一个容器
[root@template ~]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
1.4 Docker C/S 模式
Docker客户端和服务端是使用Socket方式连接的,主要有以下几种方式:
1)本地的Socket文件unix:///var/run/docker/sock (默认)
2)tcp://host:port
3)fd://socketfd
[root@template ~]# vim /usr/lib/systemd/system/docker.service
[Service]
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock -H fd:// --containerd=/run/containerd/containerd.sock
[root@template ~]# systemctl daemon-reload
[root@template ~]# systemctl restart docker
[root@template ~]# netstat -anltup | grep 2375
tcp6 0 0 :::2375 :::* LISTEN 25417/dockerd
第二章 Docker镜像
2.1 容器运行基础
镜像是Docker运行的基础,就好比计算机硬件需要安装操作系统一样。使用Docker Image命令可以看到当前系统中存在的镜像。当运行容器时,使用的镜像如果在本地系统中不存在,Docker就会自动从Docker镜像仓库或者配置的私有仓库中下载;默认是Docker Hub公共镜像源下载。
上节在Docker C/S架构逻辑图中提到了私有仓库。这节主要讲镜像的基本操作。

2.2 Docker加载镜像流程
1)检查本地是否有与启动镜像相匹配的镜像。
2)查询与镜像地址中是否有启动的镜像。
3)如果在镜像地址中没有完整的地址,则从默认的Docker Hub(http://registry.hub.docker.com/)下载。
Docker加载镜像的两种方式:
公共仓库与私有仓库

2.3 Docker镜像基本操作
搜索Docker Hub有哪些镜像
docker search [镜像名字]
[root@template ~]# docker search nginx
从Docker Hub下载镜像
下载镜像(没有指定版本,则会下载最新版的latest)
docker pull nginx:latest = docker pull nginx
[root@template ~]# docker pull nginx:latest
从第三方Docker镜像仓库或者私有仓库下载镜像方法
[root@template ~]# docker pull abc.cn/nginx:latest
镜像加速器
在下载Docker Hub镜像非常慢的情况下,可以使用镜像加速功能。配置加速可以用官网提供的,也可以使用私有仓库。
[root@template ~]# sudo mkdir -p /etc/docker
[root@template ~]# sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://th8d5hdw.mirror.aliyuncs.com"]
}
EOF
[root@template ~]# sudo systemctl daemon-reload
[root@template ~]# sudo systemctl restart docker
基于容器创建镜像系统(容器快照功能)
#类似虚拟机的克隆功能
#在运行当中的容器基础上创建一个新的镜像(相当于VM的完整的快照)或者是容器备份
[root@template ~]# docker ps -a # -a 显示所有容器
[root@template ~]# docker run -d -P nginx
0bbba19e95365ffb549e03a41d6dd76cd43f77e64c04494874e40c58b96dc13a #返回容器ID
[root@template ~]# docker ps

基于这个已经创建好了的容器再去创建一个容器
[root@template ~]# docker container commit 0bbba19e9536 nginx-test:1.0
sha256:80af4ca141d13047189ce21f4f84cced54a0c546e3f81cbe8d902d7f615d0cd3
#0bbba19e9536 容器运行的ID
查看镜像
[root@template ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-test 1.0 80af4ca141d1 2 minutes ago 141MB
nginx latest 605c77e624dd 20 months ago 141MB
hello-world latest feb5d9fea6a5 23 months ago 13.3kB
⚠️不能删除原来的镜像(父镜像)
[root@template ~]# docker rmi nginx
Error response from daemon: conflict: unable to remove repository reference "nginx" (must force) - container 0bbba19e9536 is using its referenced image 605c77e624dd
删除镜像(子镜像)
[root@template ~]# docker rmi nginx-test:1.0
Untagged: nginx-test:1.0
Deleted: sha256:80af4ca141d13047189ce21f4f84cced54a0c546e3f81cbe8d902d7f615d0cd3
Deleted: sha256:ef0d56dc3a57737b77013bc40756eee7ab41883671764296329da17bdc35ac3e
如何正确删除镜像?
方式一:
1、先删除容器
[root@template ~]# docker rm 0bbba19e9536 #0bbba19e9536 是容器的ID
0bbba19e9536
2、查询该容器是否还在运行
[root@template ~]# docker ps -a
3、删除镜像
[root@template ~]# docker rmi nginx
Untagged: nginx:latest
Untagged: nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Deleted: sha256:605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85
Deleted: sha256:b625d8e29573fa369e799ca7c5df8b7a902126d2b7cbeb390af59e4b9e1210c5
Deleted: sha256:7850d382fb05e393e211067c5ca0aada2111fcbe550a90fed04d1c634bd31a14
Deleted: sha256:02b80ac2055edd757a996c3d554e6a8906fd3521e14d1227440afd5163a5f1c4
Deleted: sha256:b92aa5824592ecb46e6d169f8e694a99150ccef01a2aabea7b9c02356cdabe7c
Deleted: sha256:780238f18c540007376dd5e904f583896a69fe620876cabc06977a3af4ba4fb5
Deleted: sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f
方式二:强制删除
[root@template ~]# docker rmi -f nginx
Untagged: nginx:latest
Untagged: nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
❓小问题:强制删除后,正在运行的容器和镜像快照怎么样了?
答案:依旧存在
[root@template ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-test 1.0 76fb45b4e3f6 4 minutes ago 141MB
[root@template ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cfa8d04941c1 605c77e624dd "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 0.0.0.0:32769->80/tcp nervous_dhawan
使用克隆的镜像创建一个容器
[root@template ~]# docker run -d -p 8090:80 nginx-test:1.0
18903b78e259f1c9b842229c343ff22e70c31353477cdd372191b8ab50a68ef8
导出镜像
导出镜像为tar包
[root@template ~]# docker image save nginx:latest > nginx.tar.gz
[root@template ~]# ll
总用量 142712
-rw-r--r-- 1 root root 145905152 9月 6 03:32 nginx.tar.gz
导入镜像
直接使用tar包导入镜像,注意如果原来导出的源镜像还是存在于系统中,那么新导入的镜像在系统中将不可见。
[root@template ~]# docker image load -i nginx.tar.gz
Loaded image: nginx:latest
#强制删除原来的镜像
[root@template ~]# docker rmi nginx:latest --force
Untagged: nginx:latest
Untagged: nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
#再次执行导入
[root@template ~]# docker image load -i nginx.tar.gz
Loaded image: nginx:latest
镜像tag标签
#语法:docker image tag 源_Image[:Tag] 目标_Image[:Tag]
#类似Linux硬链接,在推送镜像到私有仓库时,需要添加tag。
[root@template ~]# docker image tag nginx:latest repo.zxm.com/nginx:v1.0
[root@template ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
repo.zxm.com/nginx v1.0 605c77e624dd 20 months ago 141MB
#推送镜像到镜像仓库
[root@template ~]# docker push repo.zxm.com/nginx:v1.0
#使用新的tag标签启动容器
[root@template ~]# docker run -d -p 9091:80 repo.zxm.com/nginx:v1.0
ff7ffb5c14044fd5a70d988bed6f646d2cb7cfeb96b4ee5333be29b30fe83700
构建Java基础镜像(JDK)
之前使用的为Docker自带镜像仓库中的镜像,现在我们自己来构建私有镜像
❓在Java镜像中为什么要构建alpine-glib?
#Java Dockerfile
[root@template ~]# mkdir -p /root/jdk/
[root@template ~]# vim Dockerfile
FROM frolvlad/alpine-glibc #模版镜像
MAINTAINER Tony #创建人
RUN echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.4/main/" > /etc/apk/repositories #修改源
RUN apk add --no-cache bash #安装bash
ADD jre1.8.0_211.tar.gz /usr/java/jdk/ #添加文件
ENV JAVA_HOME /usr/java/jdk/jre1.8.0_211 #设置环境变量
ENV PATH ${PATH}:${JAVA_HOME}/bin
RUN chmod +x /usr/java/jdk/jre1.8.0_211/bin/java
WORKDIR /opt #工作目录
构建镜像
#需要把jre_1.8.0_211.tar.gz放到与Dockerfile同级目录
[root@template jdk]# docker pull frolvlad/alpine-glibc
[root@template jdk]# docker build -t jre8:1.0 .
[root@template jdk]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jre8 1.0 7917424f51a1 7 seconds ago 137MB
查看容器构建的历史
#missing 为当前镜像的源镜像的构建历史
#
[root@template jdk]# docker history jre8:1.0
查看镜像的详情
[root@template jdk]# docker image inspect jre8:1.0
2.4 Docker镜像与Overlay2关系
Docker 需要OverlayFS文件系统存储
1)Docker中的镜像采用分层构建设计,每层都可以称为“layer”,镜像文件默认存在目录:/var/lib/docker/overlay2
2)Docker支持的文件存储如下:AUFS、OverlatFS、VFS、Brtfs等。
3)通过Docker info命令查看当前的存储驱动
[root@template jdk]# docker info | grep -E "Storage Driver|Server Version"
Server Version: 19.03.6
Storage Driver: overlay2
3.1)修改Docker的存储驱动为Overlay2
[root@template jdk]# vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://th8d5hdw.mirror.aliyuncs.com"],
"log-driver": "json-file",
"log-opts":{
"max-size":"100m"
},
"storage-driver": "overlay2"
}
4)通常Ubuntu类的系统默认采用的是AUFS,CentOS7.1+系列采用的是OverlayFS。
第三章 Docker镜像存储机制
本章节是对上章节Docker镜像原理理解的巩固;从Linux系统运行基础到OverlayFS存储机制去了解与分析;在底层,镜像是怎么实现存储到;并且会详细说明存储文件的作用。
3.1 Linux系统运行基础
Linux系统正常运行,通常需要两个文件系统:
3.1.1 boot file system(bootfs)
- 包含Boot Loader与kernel文件,用户不能修改这些文件。并且在系统启动过程完成之后,整个系统的内核都会被家在进内存。此时bootfs会被卸载,从而释放出所占的系统内存。
- 在容器中可以运行不同版本的Linux,说明对于同样内核版本的不同的Linux发行版的bootfs都是一致的,否则会无法启动。因此可以推断,Docker运行时需要内核支持的。
- Linux系统中典型的bootfs目录:(核心)/boot/vmlinuz、(核心解压缩所需 RAM Dis k)/boot/initramfs
3.1.2 root file system(rootfs)
- 不同的Linux发行版本,bootfs相同,rootfs不同(二进制文件)
- 每个容器都有自己的rootfs,它来自不同的Linux发行版本的基础镜像,包括Ubuntu、Debian、和SUSE等。
- 使用不同的rootfs就决定了,在构建镜像的过程中,可以使用哪些系统的命令。
- 典型的rootfs目录:/dev、/proc、/etc、/lib、/usr
3.2 OverlayFS层次
OverlayFS结构分为了三个层:LowerDir、Upperdir、MergeDir
-
LowerDir(只读)
只读的image layer,其实就是roots,在使用Dockerfile构建镜像的时候,Image Layer可以分很多层,所以对应的lowerdir会很多(源镜像)。
-
Upperdir(读写)
Upperdir则是在lowerdir之上的一层,为读写层。容器在启动的时候会创建,所有对容器的修改,都是在这层。比如容器启动写入的日志文件,或者是应用程序写入的临时文件。
-
MergeDir(展示)
mergeDir目录是容器的挂载点,在用户视角能够看到的所有文件,都是从这层展示的。
LowerDir、Upperdir、MergeDir关系图

3.3 查看镜像存储目录结构
获取镜像存储路径
通过镜像信息获取到物理存储位置
[root@template jdk]# docker image inspect jre8:1.0
3.4 查看容器的存储目录结构
启动容器
前台启动,直接进入到容器
[root@template jdk]# docker run -it jre8:1.0
查看容器挂载信息
容器启动以后,挂载merged、lowerdir、upperdir以及workdir目录
lowerdir是只读的image layer,其实就是roots
#获取容器ID
[root@template ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
27d755b69076 jre8:1.0 "bash" About a minute ago Up About a minute pensive_ritchie
容器存储目录信息
注意在所有的启动容器中会自动添加init目录,此目录是存放系统的hostname与域名解析文件。
[root@template ~]# docker inspect 27d755b69076
[
{
"Id": "27d755b6907645bccf2528d88bcfa652e4a08e6fb4a87c8a8f8a1e7566726706",
"Created": "2023-09-08T06:24:04.376632294Z",
"Path": "bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 2507,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-09-08T06:24:04.704758518Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:7917424f51a13ad1c5ca650347d76b0cf151eee71a26b21626f7265a8a73198c",
"ResolvConfPath": "/data/docker/containers/27d755b6907645bccf2528d88bcfa652e4a08e6fb4a87c8a8f8a1e7566726706/resolv.conf",
"HostnamePath": "/data/docker/containers/27d755b6907645bccf2528d88bcfa652e4a08e6fb4a87c8a8f8a1e7566726706/hostname",
"HostsPath": "/data/docker/containers/27d755b6907645bccf2528d88bcfa652e4a08e6fb4a87c8a8f8a1e7566726706/hosts",
"LogPath": "/data/docker/containers/27d755b6907645bccf2528d88bcfa652e4a08e6fb4a87c8a8f8a1e7566726706/27d755b6907645bccf2528d88bcfa652e4a08e6fb4a87c8a8f8a1e7566726706-json.log",
"Name": "/pensive_ritchie",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {
"max-size": "100m"
}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/data/docker/overlay2/19a77cb63dcb95ab5c942d83e280d4c7e34d5e0effa341d9f2a09805117bceda-init/diff:/data/docker/overlay2/ca16304c1570bb49cb16b14c85fbdf1500e4bf0b403083bebfd62e76d35f4a17/diff:/data/docker/overlay2/ce674c6b9a1d62652171ebfe592237e249ee16fc10132605d42249c1a78fbd7f/diff:/data/docker/overlay2/e62c418de52bc27b3835abdb387c6569d9e8af45e85798ea28a31213d9a5d395/diff:/data/docker/overlay2/4ef4d01f24a7e01d38f3131efcb38899c224a29478873b88b54d5ab2acfe22b2/diff:/data/docker/overlay2/b33b4c6939ff8bd2454b32e4eaa497977454c9da56073783a76603d6c90333a8/diff:/data/docker/overlay2/08894f5a26ecfca0c0aad5bbc86cf0f946ca8eaa551a744618e7a61a19d54f98/diff",
"MergedDir": "/data/docker/overlay2/19a77cb63dcb95ab5c942d83e280d4c7e34d5e0effa341d9f2a09805117bceda/merged",
"UpperDir": "/data/docker/overlay2/19a77cb63dcb95ab5c942d83e280d4c7e34d5e0effa341d9f2a09805117bceda/diff",
"WorkDir": "/data/docker/overlay2/19a77cb63dcb95ab5c942d83e280d4c7e34d5e0effa341d9f2a09805117bceda/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "27d755b69076",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/java/jdk/jre1.8.0_211/bin",
"LANG=C.UTF-8",
"JAVA_HOME=/usr/java/jdk/jre1.8.0_211"
],
"Cmd": [
"bash"
],
"Image": "jre8:1.0",
"Volumes": null,
"WorkingDir": "/opt",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "6bc9eef7140a0695367e70b72fb3ed0a5a3c6b75fc29c58226fb41c8b38de6b8",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/6bc9eef7140a",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "8364e08bf711c06e0f2b405c8fdb1b8f6fef37baca92ec1a38e915d613decf3e",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "92619e79ee875cb1d8bcd2ab45903d1a6dc04428041888ecf06450720e09d454",
"EndpointID": "8364e08bf711c06e0f2b405c8fdb1b8f6fef37baca92ec1a38e915d613decf3e",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
3.5 容器文件存储
查看init层地址指向
容器在启动的过程中,Lower会自动挂载init的一些文件
init层主要内容是什么?
init层是一个uuid+-init结尾表示,放在只读层和读写层之间,作用只是存放在/etc/hosts、/etc/resolv.conf等文件
为什么需要init层
- 容器在启动以后,默认情况下lower层是不能修改内容的,但是用户有需求需要修改主机名与域名地址,那么就需要添加init层的文件(hostname、resolv.conf),用于解决此类问题。
- 修改的内容只对当前的容器生效,而在docker commit提交为镜像时候,并不会将init层提交。
- init文件存放的目录为/var/lib/docker/overlay2/<init_id>/diff
查看init层文件
hostname与resolve.conf全部为空文件,在系统启动以后由系统写入。
总结
- 镜像所挂载的目录层为 Lower 层,然后通过 Merged 展示所有的文件目录与文件。用户写入的所有文件都是在 UpperDir 目录,并且会在 UpperDir 建立于 Merged 层展示的文件目录结构,所以用户就可以看到写入的文件。并且底层的镜像是不能被修改(如果挂载目录为 UpperDir,则可以修改源镜像)。
- 在下次重新启动己经停止的容器的时候,如果容器的 ID 没有发生改变,那么所写入的文件是存在物理系统中的;反之就会是一个新的容器,之前手工创建的文件是不存在的。
- 基于容器创建的镜像,就相当于容器的快照,可以删除原来的容器,但是不能删除原来的镜像
第四章 容器基本操作
本章节主要讲解 Docker 容器的基本操作指令,学会查看指令的帮助;并且掌握容器常用的操作命令,对之后容器的操作会有很大的帮助。
4.1 查看 Docker 命令行帮助
查看帮助的方法
Docker 操作命令分为:管理命令与直接命令参数
- 管理命令为区分每个项目的命令,比如说镜像操作,就是以docker image 开头
- 直接命令参数就是在docker 命令之后直接的命令,比如说删除镜像 docker rmi管理命令相对于直接命令参数,更加严谨。
[root@template ~]# docker --help
4.2 常用基本操作列表
可以通过以下命令来启动一个Docker容器:
`docker run -d -p host_port:container_port image_name`
其中,“-d”选项使容器在后台运行,“-p”用于映射主机端口到容器端口,“image_name”是你要运行的Docker镜像的名称。
例如,如果你有一个名为“myapp”的镜像并想在主机的8080端口和容器的80端口之间建立映射,你可以使用以下命令:
`docker run -d -p 8080:80 myapp`
如果你想给Docker容器设置一个名称,可以通过添加“--name”选项来实现:
`docker run -d -p 8080:80 --name my_container myapp`
[root@template ~]# docker --help
Usage: docker [OPTIONS] COMMAND
Options:
--config string Location of client config files (default "/root/.docker")
这是一个 Docker 命令选项,用于指定 Docker daemon 的配置文件路径。默认情况下,Docker daemon 的配置文件位于 `/etc/docker/daemon.json`。
| 命令行 | 解释 |
|---|---|
查看容器日志
启动容器,-P 生成随机映射端口
[root@template ~]# docker run --name t1 -P -d nginx
查看日志
[root@template ~]# docker logs -f t1
?
docker run和docker container run实际上是相同的命令,前者是 Docker 早期版本的命令使用方式,后者是在 Docker 1.13 版本后添加的新式命令使用方式。在 Docker 1.13 版本以后,Docker 引入了一个新的命令结构,从而更好地组织其日益增长的功能集。新的命令如
docker container run,docker image build等,更具描述性,容易理解,对于新手来说学习成本较低。然而,为了保持向后兼容性,Docker 仍然保留了旧的命令方式如
docker run,docker build等,它们和新的命令方式在功能上是完全一样的,只是命令的表述形式不同。
创建容器
[root@template ~]# docker container run --help
Usage: docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]
-i 交互模式
-t 终端
-it 分配一个交互式终端
-p 端口映射
-v 源地址(宿主机):目标地址(容器)(本地的宿主机的目录挂载到容器目录)
-h 指定容器主机名
--name 指定容器的名字
--restart=always 每次重启服务,容器跟着重启
创建容器,并且挂载本地目录到容器目录
把宿主机的/test目录挂载到容器的/mnt目录(新开终端)
[root@template ~]# mkdir /test
[root@template ~]# docker run -it --name test1 -h test -v /test:/mnt centos /bin/bash
宿主机在test中创建一个文件
[root@template test]# touch a.file
容器中查看
[root@test /]# ls /mnt/
a.file
容器启动、停止
[root@template ~]# docker container start --help
Usage: docker container start [OPTIONS] CONTAINER [CONTAINER...]
列出所有容器,包括运行和停止的容器,获取到容器ID
[root@template ~]# docker ps -a
根据容器ID启动容器
[root@template ~]# docker start cfa8d04941c1
根据容器ID停止容器
[root@template ~]# docker stop cfa8d04941c1
4.3 常用容器操作命令详解
4.4 容器资源限制
docker 对系统资源的限制(CPU、内存、磁盘)实际使用的为系统的cgroup功能
查看容器资源情况
[root@template ~]# docker stats cfa8d04941c1
4.4.1 #CPU资源限制
根据CPU核心去绑定
容器可占用的CPU编号,0-3表示占用四个核,表示占用两个核
单个核心虚拟机无法做这个实验
[root@template ~]# docker run --name nginx-cpu-t1 --cpuset-cpus=0-1 -d -P nginx
第五章 Docker基本网络管理
5.1 网络模式基本介绍
Docker单机网络模式分为以下几种:
- Bridge Network 启动容器时使用 --net=bridge参数指定,默认设置
- Host Network 启动容器时使用 --net=host参数指定
- None Network 启动容器时使用 --net=none参数指定
- ContainerNetwork 启动容器时使用 --net=container:NAME_or_ID参数指定
Docker 支持网络驱动列表
https://docs.docker.com/network/
5.2 Docker网络模式详解
如果启动容器的时候使用host 模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP 和端口。但是容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
❓小问题:用主机网络的时候,一个宿主机,相同的容器,只能启动一个?

5.2.1 案例:容器网络host模式
注意如果是host模式,命令行参数不能带-p/-P 主机端口:容器端口
WARNING: Published ports are discarded when using host network mode
[root@template ~]# docker pull gliderlabs/alpine
[root@template ~]# docker run --name host_demo -it --network host gliderlabs/alpine /bin/sh
5.2.2 案例:容器网络host模式
运行nginx服务,如果运行多次,由于使用主机模式,会造成端口冲突,所以只会存在一个容器运行。
[root@template ~]# netstat -anltup | grep 80
[root@template ~]# docker run --name host_demo_nginxq --network host -d nginx
[root@template ~]# netstat -anltup | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 19770/nginx: master
tcp6 0 0 :::80 :::* LISTEN 19770/nginx: master
5.2.3 案例:容器网络桥接模式
此模式可以添加映射参数:-p 2000:22
[root@template ~]# docker run --name host_demo_bridge -it --network bridge gliderlabs/alpine /bin/sh
5.2.3 案例:容器网络Container模式
Container模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡、配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过localhost网卡设备通信。

[root@template ~]# docker run --name host_demo_container -it --network container:host_demo_bridge1 gliderlabs/alpine /bin/sh
5.2.4 案例:none模式
使用none 模式,Docker 容器拥有自己的Network Namespace,但是,并不为Docker 容器进行任何网络配置。也就是说,这个Docker 容器没有网卡、IP、路由等信息。需要我们自己为Docker 容器添加网卡、配置IP 等。
[root@template ~]# docker run --name host_demo_none -it --network none gliderlabs/alpine /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
5.3 容器网络操作
5.3.1 容器网络配置查看
语法:docker container inspect 容器ID
系统默认分配的IP 地址段为172.17.0.0/16,没有安装bash,无法进入到容器查,或者能进到容器,但是没有ip命令
[root@template ~]# docker container inspect 366c97e6596d
5.3.2 容器端口映射
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。(当使用 -P(大写) 标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。当使用 -p(小写)标记时,Docker 指定一个宿主机端口映射到到内如容器开放的网络端口。
使用docker ps 可以看到,本地主机的端口被映射到了容器端口。
在一个指定端口上只可以绑定一个容器。
指定映射(docker 会自动添加一条iptables规则来实现端口映射)
-p hostPort:containerPort #映射主机端口8080到容器的端口80
[root@template ~]# docker run --name nginx-demo -p 8080:80 -d nginx
297d85329f56e62f14c849d8e87361b7b2441539b0ff8323d688c885ea8a0273
[root@template ~]# iptables -L -n -t nat |grep 8080
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.4:80
-p ip:hostPort:containerPort #映射主机的指定IP与指定端口8180到容器的端口80
[root@template ~]# docker run --name nginx-demo-8180-1 -p 10.10.1.110:8180:80 -d nginx
06ec689a4c9e088e39bd62d9c31238522831a59d8bdb49313cfc851154f65960
[root@template ~]# iptables -L -n -t nat |grep 8180
DNAT tcp -- 0.0.0.0/0 10.10.1.110 tcp dpt:8180 to:172.17.0.7:80
-p ip::containerPort(随机端口)
[root@template ~]# docker run --name nginx-1 -p 10.10.1.110::80 -d nginx
-p 81:80 -p 443:443 可以指定多个-p
[root@template ~]# docker run --name nginx-demo-123 -p 81:80 -p 32:22 -d nginx
-P宿主机随机端口映射
[root@template ~]# docker run --name nginx-port -P -d nginx
查看容器的端口映射
[root@template ~]# docker port b6fd7a9ba730
80/tcp -> 0.0.0.0:32772
第六章 数据卷管理
本章节主要介绍 Docker 数据卷的使用场景;复制宿主机文件到容器中,怎样创建数据卷,以及数据卷的权限管理。
6.1 数据卷特点
数据卷是一个可供一个或多个容器使用的本地文件目录,主要特性如下:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立即生效
- 对数据卷的更新,不会影响镜像
- 数据卷默认会一直存在,即使容器被删除
提示:数据卷的使用,类似于 Linux 下对目录进行 mount。
6.2 使用场景
-
程序目录-
-
程序日志-
-
集群数据目录(中间件/数据库)

6.3 数据卷操作
从容器内拷贝文件到主机上
6.3.1 语法
docker cp [OPTIONS] CONTINER:SRC_PATH DEST_PATH
将容器的/test.txt拷贝到主机的/tmp目录中
[root@template ~]# docker run -it --name c1 -h c1-test centos /bin/bash
[root@c1-test /]# echo "docker" > /test.txt
[root@c1-test /]# exit
[root@template ~]# docker cp c1:/test.txt /tmp
6.3.2 #推送文件至容器/tmp目录下,通过容器ID
[root@template ~]# echo 'ceshi1' >ceshi.txt
复制本机的ceshi.txt文件到c1容器
[root@template ~]# docker cp ./ceshi.txt c1:/tmp
⚠️注意:复制到容器中的文件与挂载本机的目录到容器不同,如果是挂载模式,在本机目录写入文件那么也会同步到容器,如果是复制文件,则内容是不会同步的。
6.3.3 检查
进入容器
[root@template ~]# docker attach c1
或
[root@template ~]# docker container exec -it c1 /bin/bash
6.4 创建数据卷
使用docker run 命令中使用-v标识来给容器内添加一个数据卷,也可以在一次docker run命令中,多次使用-v标识挂载多个数据卷。
数据卷使用场景
6.4.1 创建一个容器并挂载本地目录
[root@template ~]# echo "926" >/mnt/file.txt
将本地目录的/mnt挂载在容器的/mnt中
[root@template ~]# docker run -it -v /mnt:/mnt centos /bin/bash
在宿主机/mnt创建的目录和文件,都会在容器挂载的目录实时看到
6.4.2 多个目录的绑定
[root@template ~]# docker run -it -v /mnt:/mnt -v /opt:/opt centos /bin/bash
6.5 数据卷权限设置
docker 默认情况下是对数据卷有读写权限,但是通过这样的方式让数据卷只读
[root@template ~]# docker run -it -v /mnt:/mnt:ro -v /opt:/opt:rw centos /bin/bash
6.6 第三方插件
Docker有许多插件和工具可以扩展其功能。
第七章 Dockerfile
本章主要讲解Dockerfile的语法结构,并且会详细讲解在Dockerfile中常用指令的使用方法。
7.1 为什么要用Dockerfile
Docker运行的基础是需要镜像,那么构建镜像的基础是Dockerfile也就是说如果需要运行自己的应用程序,那么需要编写Dockerfile来构建镜像。
7.2 Dockerfile结构
Dockerfile是用来构建Docker镜像的语法文件,是由一系列命令和参数构成Docker所特有的脚本命令。
Dockerfile结构如下:

#模版镜像
FROM centos
#镜像创建者
MAINTAINER Lucas
# 建立一个目录
RUN mkdir /usr/local/java
# copy gz 压缩文件到容器目录
ADD jdk-8u141-linux-x64.tar.gz /usr/local/java/jdk
# 建立软连接
RUN ln -s /usr/local/java/jdk1.8.0_141 /usr/local/java/jdk
# 设置环境变量
ENV JAVA_HOME /usr/local/java/jdk
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH ${JAVA_HOME}/bin:$PATH
#定义容器端口
#EXPOSE 8080
#启动命令
CMD ["java","-version"]
7.3 Dockerfile 命令详解
FROM
构建的镜像设置基础镜像
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
FROM指令初始化新的构建阶段,并为后续指令设置基础镜像,e.g: FROM openjdk:8-jdk-alpine。因此,Dockerfile文件必须以FROM指令开头。 而指定的镜像可以使任何有效的镜像(推荐使用公共仓库的镜像,因为拉取更为容易)。
- 在Dockerfile文件中
ARG是唯一一个可以用于FROM之前的指令。
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
- 一个Dockerfile文件可以有多个FROM指令,来创建多个镜像或者使用其中一个阶段作为另一个阶段的依赖项。以最后一个镜像的ID为输出值。e.g:
FROM golang:1.10.3
COPY server.go /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server
ENTRYPOINT ["/build/server"]
- 我们可以通过在
FROM指令中添加AS 名称来指定一个名称给后续阶段的FROM和COPY --from=<name|index>使用。 - tag和digest是可选的,如果不提供则使用latest。
RUN
在镜像的构建过程中执行特定的命令,并生成一个中间镜像。比如安装一些软件、配置一些基础环境,可使用\来换行。
RUN <command> (shell格式)
RUN ["executable", "param1", "param2"] (exec格式)
要注意的是,executable是命令,后面的param是参数
采用exec格式指令将会被解析成json格式所以不能使用单引号,并且使用反斜杠也是必须要转移的,这在windows上尤为重要。
dockerfile 每执行一个run 会临时创建一个容器,每次从创建都会重新挂载这三个配置文件。所以有对于此三个配置文件有依赖操作的命令需要处于同一个RUN(也可以在容器启动以后,执行脚本修改)。
CMD
指定容器运行时的默认参数。
CMD ["executable","param1","param2"](exec格式,首选)
CMD ["param1","param2"](给ENTRYPOINT提供默认参数)
CMD command param1 param2(shell格式)
注意:
RUN是在构建的时候执行,并生成一个新的镜像,CMD在构建时不进行任何操作,在容器运行的时候执行。
如果CMD用于为ENTRYPOINT指令提供缺省参数,那么CMD和ENTRYPOINT指令都应该使用JSON数组格式。
LABEL
给构建的镜像打标签。
LABEL <key>=<value> <key>=<value> <key>=<value> ...
标签是键值对格式,要在标签中包含空格则需转义或用引号"括起来。e.g:
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."
一个镜像可以有多个标签,如果基础镜像也有标签则继承,名字相同的话则会覆盖。如果使用多个标签,建议合并成一个标签指令,如果使用多个标签指令, 则每个标签指令都会生成一个图层,这会导致镜像生成效率低下。举个栗子:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
也可以写成:
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
MAINTAINER(已弃用)
设置作者信息。
MAINTAINER <name>
LABEL比MAINTAINER更灵活,推荐使用LABEL,弃用MAINTAINER。
LABEL maintainer="makise_kurisuu@outlook.jp"
EXPOSE
为构建的镜像设置监听端口。
EXPOSE <port> [<port>/<protocol>...]
EXPOSE指令让docker容器在运行的时候监听指定的端口,可以指定端口是upd还是tcp协议,如果没有指定则默认tcp协议。
EXPOSE指令并不会发布端口,如果发布端口,则需要在docker run时使用-p来发布和映射一个或多个容器端口、或者使用-P来发布所有公开的端口 并将它们映射到高阶端口。
ENV
在构建的镜像中设置环境变量,在后续的Dockerfile指令中可以直接使用,也可以固化在镜像里,在容器运行时仍然有效。 ENV指令有两种格式:
- ENV :把第一个空格之后的所有值都当做的值,无法在一行内设定多个环境变量。
- ENV = ...:可以设置多个环境变量,如果中存在空格,需要转义或用引号
"括起来。
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
或者
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
推荐使用第二种,同样的这样能减少图层提高效率。
注意:可以在容器运行时指定环境变量,替换镜像中的已有变量,
docker run --env <key>=<value>。
使用ENV可能会对后续的Dockerfile指令造成影响,如果只需要对一条指令设置环境变量,可以使用这种方式:RUN <key>=<value> <command>
ADD
构建镜像时,复制上下文中的文件到镜像内。
ADD <src>... <dest>
ADD ["<src>",... "<dest>"](路径包含空格的必须使用这种格式)
<src>可以是文件、目录,也可以是文件URL。可以使用模糊匹配(wildcards,类似shell的匹配),可以指定多个<src>,必须是在上下文目录和子目录中, 无法添加../a.txt这样的文件。如果<src>是个目录,则复制的是目录下的所有内容,但不包括该目录。如果<src>是个可被docker识别的压缩包, docker会以tar -x的方式解压后将内容复制到<desct>。<dest>可以是绝对路径,也可以是相对WORKDIR目录的相对路径。 所有文件的UID和GID都是0。
如果docker发现文件内容被改变,则接下来的指令都不会再使用缓存。
关于复制文件时需要处理的/,基本跟正常的copy一致。
COPY
将主机的文件复制到镜像内,如果目的位置不存在,Docker会自动创建所有需要的目录结构,但是它只是单纯的复制,并不会去做文件提取和解压工作。
COPY <src>... <dest>
COPY ["<src>",... "<dest>"](路径包含空格的必须使用这种格式)
注意:需要复制的目录一定要放在Dockerfile文件的同级目录下。
因为构建环境将会上传到Docker守护进程,而复制是在Docker守护进程中进行的。任何位于构建环境之外的东西都是不可用的。 COPY指令的目的的位置则必须是容器内部的一个绝对路径。
COPY指令
ENTRYPOINT
指定镜像的执行程序。ENTRYPOINT指令有两种格式:
ENTRYPOINT ["executable", "param1", "param2"] (执行格式,首选)
ENTRYPOINT command param1 param2 (shell格式)
详细参考:ENTRYPOINT指令
CMD和ENTRYPOINT至少得使用一个。ENTRYPOINT应该被当做docker的可执行程序,CMD应该被当做ENTRYPOINT的默认参数。 docker run <image> <arg1> <arg2> ...会把之后的参数传递给ENTRYPOINT,覆盖CMD指定的参数。可以用docker run --entrypoint 来重置默认的ENTRYPOINT。
VOLUME
指定镜像内的目录为数据卷。
VOLUME ["/data"]
在容器运行的时候,docker会把镜像中的数据卷的内容复制到容器的数据卷中去。 如果在接下来的Dockerfile指令中,修改了数据卷中的内容,则修改无效。
USER
为接下来的Dockerfile指令指定用户。受影响的指令有:RUN、CMD、ENTRYPOINT。
USER <user>[:<group>] or
USER <UID>[:<GID>]
注意:当用户没有主要组时,镜像(或下一条指令)将与该root组一起运行。
WORKDIR
为接下来的Dockerfile指令指定当前工作目录,可多次使用,如果使用的是相对路径,则相对的是上一个工作目录,类似shell中的cd命令。
WORKDIR /path/to/workdir
受影响的指令有:RUN、CMD、ENTRYPOINT、COPY和ADD。 可以在Dockerfile中多次使用WORKDIR指令。如果提供了相对路径,则它将相对于上一个WORKDIR指令的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
此Dockerfile中pwd指令将输出 /a/b/c.。
WORKDIR指令可以解析之前使用ENV设置的环境变量,只能使用Dockerfile中显式设置的环境变量。例如:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
此Dockerfile中pwd指令将输出 /path/$DIRNAME。
ARG
指定用户在docker build --build-arg <varname>=<value>时可以使用的参数。
ARG <name>[=<default value>]
如果用户指定了未在Dockerfile中定义的构建参数,则构建会输出警告。
[Warning] One or more build-args [foo] were not consumed.
构建参数在定义的时候生效而不是在使用的时候。如下面第三行开始的user才是用户构建参数传递过来的user:
FROM busybox
# 此处获取不到用户传来的user
USER ${user:-some_user}
ARG user
USER $user
您可以使用ARG或ENV指令指定RUN指令可用的变量。使用ENV指令定义的环境变量始终覆盖同名的ARG指令。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
# 始终是v1.0.0
RUN echo $CONT_IMG_VER
正确的用法:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
要在多个阶段中使用arg,每个阶段都必须包含该ARG指令。
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS
此外docker还内置了一批构建参数,可以不用在Dockerfile中声明:HTTP_PROXY、http_proxy、HTTPS_PROXY、https_proxy、FTP_PROXY、 ftp_proxy、NO_PROXY、no_proxy
注意:在使用构建参数(而不是在构建参数定义的时候)的指令中,如果构建参数的值发生了变化,会导致该指令发生变化,会重新寻找缓存。
ONBUILD
向镜像中添加一个触发器,当以该镜像为基础镜像再次构建新的镜像时,会触发执行其中的指令。
ONBUILD [INSTRUCTION]
比如我们生成的镜像是用来部署Python代码的,但是因为有多个项目可能会复用该镜像。你可以这样使用:
[...]
# 在下一次以此镜像为base image的构建中,执行ADD . /app/src,将项目代目添加到新镜像中去
ONBUILD ADD . /app/src
# 并且build Python代码
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
不允许
ONBUILD使用链接指令ONBUILD ONBUILD。
ONBUILD只会继承给子节点的镜像,不会再继承给孙子节点。
ONBUILD不会触发FROM、MAINTAINER指令。
STOPSIGNAL
触发系统信号。
STOPSIGNAL signal
STOPSIGNAL指令设置将发送到容器以退出的系统调用信号。此信号可以是与内核的SysCall表中的位置匹配的有效无符号数字(例如9),也可以是格式为SIGNAME的信号名称(例如SIGKILL)。
HEALTHCHECK
增加自定义的心跳检测功能,多次使用只有最后一次有效。格式:
HEALTHCHECK [OPTIONS] CMD command (通过在容器内运行命令来检查容器运行状况)
HEALTHCHECK NONE (禁用从基础映像继承的任何运行状况检查)
可选的OPTION:
--interval=DURATION(检测间隔,默认值:30s)--timeout=DURATION(命令超时时间,默认值:30s)--start-period=DURATION(初始化后开始检查时间,默认值:0s)--retries=N(连续N次失败后标记为不健康,默认值:3次)
Start Period为需要时间引导的容器提供初始化时间。在此期间的探测失败将不计入最大重试次数。但是,如果运行状况检查在开始期间成功,则容器将被视为已启动,并且所有连续失败将计入最大重试次数。
<command>可以是shell脚本,也可以是exec格式的json数组。 docker以<command>的退出状态码来区分容器是否健康,这一点同shell一致:
- 0:命令返回成功,容器健康
- 1:命令返回失败,容器不健康
- 2:保留状态码,不要使用 举个栗子:每5分钟检测本地网页是否可访问,超时设为3秒:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
可以使用docker inspect命令查看健康状态。
当容器的运行状况发生更改时,将生成带有新状态的health_status事件。
SHELL
更改后续的Dockerfile指令中所使用的shell。默认的shell是["bin/sh", "-c"]。可多次使用,每次都只改变后续指令。
SHELL ["executable", "parameters"]