Docker

Docker 概述

Docker 是基于 Go 语言开发的开源项目!

官网:https://www.docker.com/

文档地址:https://docs.docker.com/

仓库地址:https://hub.docker.com/

Docker 能干嘛

之前的虚拟机技术

image-20200611160918604

虚拟机技术缺点

  1. 资源占用十分多
  2. 冗余步骤多
  3. 启动很慢

容器化技术

容器化技术不是模拟的一个完整的操作系统

image-20200611161800008

比较 Docker 和 虚拟机技术的不同:

  • 传统虚拟机,虚拟出一套硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
  • 容器内的应用直接运行在宿主机的内核,容器没有自己的内核,也没有虚拟硬件,所以比较轻便
  • 每个容器间互相隔离,每个容器内都有一个属于自己的文件系统,互不影响

DevOps (开发、运维)

应用更快速的交付和部署

Docker:打包镜像发布测试、一键运行

更便捷的升级扩缩容

项目打包为一个镜像

更简单的系统运维

在容器化之后,开发、测试环境高度一致

更高效的计算资源利用

Docker 是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例,服务器的性能可以被压榨到极致

Docker 安装

Docker 的基本组成

img

镜像(image):

docker 镜像就好比是一个模板,可以通过这个模板来创建容器服务,可以创建多个容器

容器(container):

docker 利用容器技术,独立运行一个或一组应用,通过镜像来创建的。

启动、停止、删除、基本命令

可以理解为一个简易的 linux 系统

仓库(repository):

存放镜像的地方,分为私有仓库和公有仓库,Docker Hub (默认是国外的)、阿里云镜像服务

配置阿里云镜像加速

安装 Docker

环境查看

1
2
3
# 系统内核是 3.10 以上的
[root@localhost ~]# uname -r
3.10.0-1127.el7.x86_64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 系统版本
[root@localhost ~]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 1. 卸载旧的版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

# 2. 需要的安装包
yum install -y yum-utils

# 3. 设置镜像的仓库
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo # 默认是国外的

yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 阿里云

# 更新 yum 软件包索引
yum makecache fast

# 4. 安装 docker docker-ce 社区版 ee 企业版
yum install docker-ce docker-ce-cli containerd.io

# 5. 启动 docker
systemctl start docker

# 6. 使用 docker version 测试是否安装成功
[root@localhost ~]# docker version
Client: Docker Engine - Community
Version: 19.03.11
API version: 1.40
Go version: go1.13.10
Git commit: 42e35e61f3
Built: Mon Jun 1 09:13:48 2020
OS/Arch: linux/amd64
Experimental: false

Server: Docker Engine - Community
Engine:
Version: 19.03.11
API version: 1.40 (minimum version 1.12)
Go version: go1.13.10
Git commit: 42e35e61f3
Built: Mon Jun 1 09:12:26 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683

# 7. hello-world
docker run hello-world

image-20200611170709771

1
2
3
4
# 8. 查看下载的 hello-world 镜像
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB

卸载 docker

1
2
3
4
5
6
7
# 1. 卸载依赖
yum remove docker-ce docker-ce-cli containerd.io

# 2. 删除 docker
rm -rf /var/lib/docker

# /var/lib/docker docker 的默认工作路径

阿里云镜像加速

  1. 登录阿里账号

    image-20200611175008291

  2. 找到加速器地址

    image-20200611175054681

  3. 配置使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    sudo mkdir -p /etc/docker

    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
    "registry-mirrors": ["https://6s9ckomb.mirror.aliyuncs.com"]
    }
    EOF

    sudo systemctl daemon-reload

    sudo systemctl restart docker

回顾 Hello-World 流程

image-20200611170709771

image-20200611182624316

底层原理

Docker 是怎么工作的

Docker 是一个 Client + Server 结构的系统,Docker 的守护进程运行在主机上,通过 Socket 从客户端访问

Docker Server 接收到 Docker Client 的指令,就会执行这个命令!

image-20200611183327981

Docker 为什么比 VM 快?

  1. Docker 有比虚拟机更少的抽象层
  2. Docker 利用的是宿主机的内核,VM 需要的是 Guest OS

img

所以说,新建一个容器的时候,docker 不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载 Guest OS,分钟级别的,而 Docker 是利用宿主机的操作系统,省略了复杂的过程,秒级别。

Docker 的常用命令

帮助命令

1
2
3
docker version			 # 显示 docker 的版本信息
docker info # 显示 docker 的系统信息,包括镜像和容器的数量
docker 命令 --help # 帮助命令

帮助文档地址:https://docs.docker.com/reference/

镜像命令

docker images 查看本地的主机上的镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB

# 解释
REPOSITORY # 镜像的仓库源
TAG # 镜像的标签
IMAGE ID # 镜像的 id
CREATED # 镜像的创建时间
SIZE # 镜像的大小

# 可选项
-a, --all # 列出所有镜像
-q, --quiet # 只显示镜像的id

docker search 搜索镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9616 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3494 [OK]

# 可选项
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print search using a Go template
--limit int Max number of search results (default 25)
--no-trunc Don't truncate output

--filter=STARS=3000 搜索 STARS 大于 3000
[root@localhost ~]# docker search mysql --filter=STARS=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9616 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3494 [OK]
[root@localhost ~]# docker search mysql --filter=STARS=5000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9616 [OK]

docker pull 下载镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 下载镜像 docker pull 镜像名[:tag]
[root@localhost ~]# docker pull mysql
Using default tag: latest # 如果不指定tag,默认选择 latest
latest: Pulling from library/mysql
8559a31e96f4: Pull complete # 分层下载,联合文件系统 image 的核心
d51ce1c2e575: Pull complete
c2344adc4858: Pull complete
fcf3ceff18fc: Pull complete
16da0c38dc5b: Pull complete
b905d1797e97: Pull complete
4b50d1c6b05c: Pull complete
c75914a65ca2: Pull complete
1ae8042bdd09: Pull complete
453ac13c00a3: Pull complete
9e680cd72f08: Pull complete
a6b5dc864b6c: Pull complete
Digest: sha256:8b7b328a7ff6de46ef96bcf83af048cb00a1c86282bfca0cb119c84568b4caf6 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址

# 等价于
docker pull mysql
docker pull docker.io/library/mysql:latest

# 指定版本下载
[root@localhost ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
8559a31e96f4: Already exists
d51ce1c2e575: Already exists
c2344adc4858: Already exists
fcf3ceff18fc: Already exists
16da0c38dc5b: Already exists
b905d1797e97: Already exists
4b50d1c6b05c: Already exists
d85174a87144: Pull complete
a4ad33703fa8: Pull complete
f7a5433ce20d: Pull complete
3dcd2a278b4a: Pull complete
Digest: sha256:32f9d9a069f7a735e28fd44ea944d53c61f990ba71460c5c183e610854ca4854
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 9cfcce23593a 2 days ago 448MB
mysql latest be0dbf01a0f3 2 days ago 541MB
hello-world latest bf756fb1ae65 5 months ago 13.3kB

docker rmi 删除镜像

1
2
3
[root@localhost ~]# docker rmi -f 镜像id		# 删除指定的镜像
[root@localhost ~]# docker rmi -f 镜像id 镜像id 镜像id 镜像id # 删除多个镜像
[root@localhost ~]# docker rmi -f $(docker images -aq) # 删除全部的镜像

容器命令

说明:有了镜像才可以创建容器

1
docker pull centos

新建容器并启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
docker run [可选参数] iamge

# 参数说明
--name="NAME" # 容器名称 nginx01 nginx02 用来区分容器
-d # 后台方式运行
-it # 使用交互方式运行,进入容器查看内容
-p # 指定容器的端口
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
-P # 随机指定端口

# 测试一下
[root@localhost ~]# docker run -it centos /bin/bash
[root@78a594c12edb /]# ls # 查看容器内的centos,基础版本,很多命令不完善
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var

# 从容器中退出
[root@78a594c12edb /]# exit
exit

列出所有运行的容器

1
2
3
4
5
6
7
8
9
# docker ps
# 列出当前再运行的容器
-a # 列出当前再运行的容器 + 历史运行过的容器
-n=? # 显示最近创建的容器
-q # 之前是容器的id
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
78a594c12edb centos "/bin/bash" About a minute ago Exited (0) 10 seconds ago competent_cray
3d46b25aa340 bf756fb1ae65 "/hello" 4 hours ago Exited (0) 4 hours ago busy_gates

退出容器

1
2
exit			# 停止并退出
ctrl + p + q # 不停止退出

删除容器

1
2
3
docker rm 容器id					   # 删除指定的容器,不能删除正在运行的容器,如果要强制删除 rm -f 
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -a -q | xargs docker rm # 删除所有容器

启动和停止容器的操作

1
2
3
4
docker start 容器id		# 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止容器
docker kill 容器id # 强制停止容器

其他常用命令

后台启动容器

1
2
3
4
5
6
7
8
# 命令 docker run -d 镜像名
[root@localhost ~]# docker run -d centos
ae6f9985c61069663a958dece7992ea1807da60b56dbecc7067132dc5fb22322

# 问题:docker ps 发现 centos 停止了

# docker 容器使用后台运行,就必须要有一个前台进程,docker 发现容器没有应用。就会自动停止
# nginx 容器启动后,发现没有提供服务,就会立刻停止

查看日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker logs -tf --tail 参数 容器

# 编写一段shell脚本
[root@localhost ~]# docker run -d centos /bin/sh -c "while true;do echo hello;sleep 1;done"
5bb213af616ac776171c849c3dcd8795aec0f1debad6e2782c7e00e7318f0bab

[root@localhost ~]# docker ps
CONTAINER ID IMAGE
5bb213af616a centos

# 显示日志
-tf # 显示日志
--tail number # 显示日志条数
[root@localhost ~]# docker logs -tf --tail 10 5bb213af616a
2020-06-11T13:40:37.546256449Z hello
2020-06-11T13:40:38.552587889Z hello
2020-06-11T13:40:39.565318505Z hello

查看容器中进程信息

1
2
3
4
# docker top 容器id
[root@localhost ~]# docker top 6eaf349e79be
UID PID PPID C STIME TTY TIME CMD
root 24053 24036 0 13:44 ? 00:00:00 /bin/sh -c while true;do echo hello;sleep 1;done

查看镜像的元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
[root@localhost ~]# docker inspect 6eaf349e79be
[
{
"Id": "6eaf349e79be874c28457b4515de3a7900c768683222c1702eb51915c6029ca7",
"Created": "2020-06-11T13:44:41.532686534Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true;do echo hello;sleep 1;done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 24053,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-06-11T13:44:43.395002077Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:470671670cac686c7cf0081e0b37da2e9f4f768ddc5f6a26102ccd1c6954c1ee",
"ResolvConfPath": "/var/lib/docker/containers/6eaf349e79be874c28457b4515de3a7900c768683222c1702eb51915c6029ca7/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/6eaf349e79be874c28457b4515de3a7900c768683222c1702eb51915c6029ca7/hostname",
"HostsPath": "/var/lib/docker/containers/6eaf349e79be874c28457b4515de3a7900c768683222c1702eb51915c6029ca7/hosts",
"LogPath": "/var/lib/docker/containers/6eaf349e79be874c28457b4515de3a7900c768683222c1702eb51915c6029ca7/6eaf349e79be874c28457b4515de3a7900c768683222c1702eb51915c6029ca7-json.log",
"Name": "/stoic_pike",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"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": "/var/lib/docker/overlay2/719a6ab22bfd9bf97499e90c7f6de38ff742997356a92f2a6cd3b37a3052d4bb-init/diff:/var/lib/docker/overlay2/25a668062a606e9441316f1c798e041af80565a8b1f1904b5d9490663704aea8/diff",
"MergedDir": "/var/lib/docker/overlay2/719a6ab22bfd9bf97499e90c7f6de38ff742997356a92f2a6cd3b37a3052d4bb/merged",
"UpperDir": "/var/lib/docker/overlay2/719a6ab22bfd9bf97499e90c7f6de38ff742997356a92f2a6cd3b37a3052d4bb/diff",
"WorkDir": "/var/lib/docker/overlay2/719a6ab22bfd9bf97499e90c7f6de38ff742997356a92f2a6cd3b37a3052d4bb/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "6eaf349e79be",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"while true;do echo hello;sleep 1;done"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200114",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-01-14 00:00:00-08:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "517185b13e8a9f81fabfba7bdc5eb50ed91bf01ac182edaef3e0f0a315f7936e",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/517185b13e8a",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "fc71eae4417b53a1898c3bc3090e7babd680621730433693bcfee86cca8ad227",
"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": "5a19adc4237c49e7efce044d00bc8326f8f433473e57a6b179c638fa9b5a14ad",
"EndpointID": "fc71eae4417b53a1898c3bc3090e7babd680621730433693bcfee86cca8ad227",
"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
}
}
}
}
]
[root@localhost ~]#

进入当前运行的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
# 容器通常都是使用后台运行的
# docker exec -it 容器id /bin/bash

[root@localhost ~]# docker exec -it 6eaf349e79be /bin/bash
[root@6eaf349e79be /]#

# 方式二
# docker attach 容器id
[root@localhost ~]# docker attach 6eaf349e79b
正在运行的代码……

# docker exec 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker attact 进入容器正在运行执行的终端,不会启动新的进程

从容器内拷贝文件到主机上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3b50985e31d6 centos "/bin/bash" 49 seconds ago Up 3 seconds compassionate_khayyam]

# 进入容器内部
[root@localhost ~]# docker attach 3b50985e31d6
[root@3b50985e31d6 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@3b50985e31d6 /]# cd /home/

# 在容器内新建文件
[root@3b50985e31d6 home]# touch test.php
[root@3b50985e31d6 home]# ls
test.php
[root@3b50985e31d6 home]# exit
exit
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3b50985e31d6 centos "/bin/bash" About a minute ago Exited (0) 9 seconds ago compassionate_khayyam

# 将文件拷贝到主机中
[root@localhost ~]# docker cp 3b50985e31d6:/home/test.php /home
[root@localhost ~]# ls /home/
test.php vagrant

小结

https://philipzheng.gitbooks.io/docker_practice/content/appendix_command/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
attach		Attach to a running container 			# 当前 shell 下 attach 连接指定运行镜像
build Build an image from a Dockerfile # 通过 Dockerfile 定制镜像
commit Create a new image from a container changes # 提交当前容器为新的镜像
cp Copy file/folders from the containers filesystem to the host path # 从容器中拷贝指定文件或者目录到宿主机中
create Create a new container # 创建一个新的容器,同 run ,但不启动容器
diff Inspect changes on a container's filesystem # 查看 docker 容器变化
events Get real time events from the server # 从 docker 服务获取容器实时事件
exec Run a command in an existing container # 在已存在的容器上运行命令
export Stream the contents of a container as a tar archive # 导出容器的内容流作为一个 tar 归档文件[对应 import]
history Show the history of an images # 显示一个镜像形成历史
images List images # 列出系统当前镜像
import Create a new filesystem image from the contents of a tarball # 从 tar 包中的内容创建一个新的文件系统镜像 [对应 export]
info Display ysytem-wide information # 显示系统相关信息
inspect Return low-level information on a container # 查看容器详细信息
kill Kill a running container # kill 指定的 docker 容器
load Load an image from a tar archive # 从一个 tar 包中加载一个镜像[对应 save]
login Register or Login to the docker registry server # 注册或登录一个 docker 源服务器
logout Logout from a Docker registry server # 从当前 Docker registry 退出
logs Fetch the logs of a container # 输出当前容器日志信息
port Lookup the public0facing port which is NAT-ed to PRIVATE_PORT # 查看映射端口对应的容器内部源端口
pause Pause all processes with in a container # 暂停容器
ps List containers # 列出容器列表
pull Pull an image or a respository from the docker registry server # 从 docker 镜像服务器拉取指定镜像或者库镜像
push Push an image or a respository to the docker registry server # 推送指定镜像或库镜像至 docer 源服务器
restart Restart a running container # 重启运行的容器
rm Remove one or more containers # 移除一个或者多个容器
rmi Remove one or more images # 移除一个或者多个镜像[无容器使用该镜像才可删除,否则需要删除相关容器才可继续或 -f 强制删除]
run Run a command in a new container # 创建一个新的容器并运行一个命令
save Save an image to a tar archive # 保存一个镜像为一个 tar 包 [对应 load]
search Search fro an image on the Docker Hub # 在 docker hub 中搜索镜像
start Start a stopped container # 启动容器
stop Stop a running container # 停止容器
tag Tag an image into a repository # 给源中容器打标签
top Lookup the running processes of a container #查看容器中运行的进程信息
unpause Unpause a paused container # 取消暂停容器
version Show the docjer version information # 查看 docker 版本信息
wait Block until a container stops, the print its exit code # 截取容器停止时的退出状态值

Docker 镜像讲解

镜像是什么

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时所需的库、环境变量和配置文件。

Docker 镜像加载原理

UnionFS(联合文件系统)

UnionFS(联合文件系统): Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。UnionFS 是 Docker 镜像的基础,镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

Docker 镜像加载原理

Docker 的镜像实际上有一层又一层的文件系统组成,这种层级的文件系统叫做 UnionFS。

bootfs(boot file system)主要包含 bootloader 和 kernel,bootloader 主要是引导加载 kernel,Linux 刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs。这一层与我们典型的 Linux / Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就存在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。

rootfs (root file system),在 bootfs 之上。包含的就是典型 Linux 系统中的 /dev ,/proc,/bin ,/etx 等标准的目录和文件。rootfs 就是各种不同的操作系统发行版。比如 Ubuntu,Centos 等等。

img

平时安装虚拟机的 Centos 都是好几个G,为什么 docker 里才200M?

1
2
3
[root@localhost www]# docker images centos
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 470671670cac 4 months ago 237MB

对于一个精简的OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host(宿主机)的 kernel,自己只需要提供 rootfs 就行了,由此可见对于不同的 Linux 发行版,bootfs 基本是一致的,rootfs 会有差别,因此不同的发行版可以公用 bootfs。

分层理解

分层的镜像

下载一个镜像,观察下载的日志输出,可以看到是一层一层的在下载

image-20200615183703327

思考:为什么 Docker 镜像采用这种分层的结构呢?

资源共享!比如有多个镜像都是从相同的 base 镜像构建而来,那么宿主机只需要再磁盘上保留一份 base 镜像,同时内存中也只需要加载一份 base 镜像,这样就可以为所有容器服务了,而且镜像的每一层都可以被共享。

查看镜像的分层方式可以通过 docker image inspect 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost ~]# docker inspect redis
[
{
# ........
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74",
"sha256:e6b49c7dcaac7a2ec2acc379da5f5b1bcc6a5d3badd72814fe945296216557bd",
"sha256:cdaf0fb0082b74223a224c39c2d2ea886c32f53b7e1d5b872d5354aae0df56b8",
"sha256:72d3a7e6fe022824ee2f852ca132030e22c644fbaf8287f4ea8044268abe40b7",
"sha256:67c707dbd847d8310d3b988c3e3d9d9eb53387ede0de472e36a15abbcb6c719c",
"sha256:7b9c5be81844318508f57a5b0574822dabaaed3dc25ee35d960feec3a9e941c4"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]

理解:

所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。

举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python 包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。

该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

基于Ubuntu Linux 16.04创建镜像

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含 3 个文件,而镜像包含了来自两个镜像层的 6 个文件。

添加额外的镜像层后的镜像

上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。

下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有 6 个文件,这是因为最上层中的文件 7 是文件 5 的一个更新版本。

三层镜像

这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。

Docker 在 Windows 上仅支持 windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW[1]。

下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。

从系统角度看三层镜像

特点

Dokcer 镜像都是只读的,当容器启动时,一个新的可写层会被加载到镜像的顶部!

新加的一层就是容器层,容器层之下的叫做镜像层

image-20200616120416199

Commit 镜像

1
2
docker commit 提交容器成为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名[:tag]

测试:

1
2
3
4
5
6
7
# 1. 启动一个默认 tomcat

# 2. 发现默认的 tomcat 镜像 webapps 目录下没有文件

# 3. 自己拷贝基本的文件进去

# 4. 将自己操作过的容器通过 commit 提交为一个新的镜像

image-20200616141100722

如果想保存当前容器的状态,就可以通过 commit 来提交,保存成为一个镜像。类似于虚拟机的快照,可以通过镜像的 tag 来标注信息

容器数据卷

什么是容器数据卷

数据保存再容器中,如果容器删除,数据就会丢失,需求:数据可以持久化

容器之间可以有一个数据共享的技术,Docker 容器中产生的数据,同步到本地(目录的挂载,将容器的目录,挂载到 Linux 上)

image-20200616142447190

使用数据卷

方式一:直接使用命令来挂载 -v

1
2
3
4
5
6
7
8
9
10
11
12
docker run -d -it -v 主机目录:容器内目录

# 测试
[root@localhost home]# pwd
/home
[root@localhost home]# ls
ceshi test.php vagrant
[root@localhost home]# docker run -it -v /home/ceshi:/home centos /bin/bash
[root@4811e7677070 /]#

# 启动以后,通过 docker inspect 容器id 来查看容器信息
[root@localhost ~]# docker inspect 4811e7677070

image-20200616143940721‘’

测试文件同步

image-20200616144823744

安装 MySQL

1
2
3
4
5
6
7
8
9
10
11
12
# 获取镜像
[root@localhost ~]# docker pull mysql

# 运行容器,进行数据卷挂载,-e MYSQL_ROOT_PASSWORD=root 指定 root 密码
# -d 后台运行
# -p 端口映射
# -v 数据卷挂载
# -e 环境变量
# --name 容器名字

[root@localhost ~]# docker run -d -p 3306:3306 -v /var/web/mysql/conf.d:/etc/mysql/conf.d -v /var/web/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql01 mysql

具名和匿名挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 匿名挂载
[root@localhost ~]# docker run -d -P -v /etc/nginx --name nginx02 nginx

# 查看所有卷的情况
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local 325659ae6a85f8c019bf06e32fed1495a94ae25f221a15b119a5581fc4daff7d

# 这样的就是匿名挂载,我们在 -v 只写了容器内的路径,没有写容器外的路径

# 具名挂载
[root@localhost ~]# docker run -d -P -v juming-nginx:/etc/nginx --name nginx03 nginx
e05bdf32964dc47cd1b53a8b9529ce54cece56765abf01f5037109354bbd60ae
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local 325659ae6a85f8c019bf06e32fed1495a94ae25f221a15b119a5581fc4daff7d
local juming-nginx

# 查看具名挂载的卷
[root@localhost ~]# docker volume inspect juming-nginx
[
{
"CreatedAt": "2020-06-16T18:25:37Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]

所有的 docker 容器内的卷,没有指定目录的情况下都在 /var/lib/docker/volumes/xxx/_data

使用具名挂载可以方便的找到卷,大多数情况下都是使用 具名挂载

1
2
3
4
# 总结
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径:容器内路径 # 指定路径挂载

拓展:

1
2
3
4
5
6
7
8
9
# 通过 -v 容器内路径:ro rw 改变读写权限
ro readonly # 只读
rw readwrite # 可读可写

# 一旦设置了容器权限,容器对我们挂载出来的目录就有了权限的限定
[root@localhost ~]# docker run -d -P -v juming-nginx:/etc/nginx:ro --name nginx03 nginx
[root@localhost ~]# docker run -d -P -v juming-nginx:/etc/nginx:rw --name nginx03 nginx

# ro 只能通过宿主机来操作,容器内部无法操作

初始 DockerFile

DockerFile 就是用来构建 docker 镜像的构建文件,是一个脚本文件

通过这个脚本可以生成镜像,镜像是一层一层的,脚本中的每一个命令,都是一层

1
2
3
4
5
6
7
8
9
10
11
# 创建一个 dockerfile 文件,名字随机,建议 Dockfile
# 文件中的指令必须大写

FROM centos

VOLUME ["volume01", "volume02"]

CMD echo "---end---"
CMD /bin/bash

# 每个命令都是镜像的一层

image-20200617171647225

1
# 启动容器

image-20200617173419039

这个挂载的目录就是数据卷目录,在外部一定有一个同步的挂载目录

image-20200617173903579

查看卷信息

image-20200617173921831

测试一下文件是否同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 容器内
[root@localhost docker-test-volume]# docker run -it fe0ae7af1da9 /bin/bash
[root@83599054db40 /]# ls -l
total 0
lrwxrwxrwx. 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x. 5 root root 360 Jun 16 21:52 dev
drwxr-xr-x. 1 root root 66 Jun 16 21:52 etc
drwxr-xr-x. 2 root root 6 May 11 2019 home
lrwxrwxrwx. 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------. 2 root root 6 Jan 13 21:48 lost+found
drwxr-xr-x. 2 root root 6 May 11 2019 media
drwxr-xr-x. 2 root root 6 May 11 2019 mnt
drwxr-xr-x. 2 root root 6 May 11 2019 opt
dr-xr-xr-x. 114 root root 0 Jun 16 21:52 proc
dr-xr-x---. 2 root root 162 Jan 13 21:49 root
drwxr-xr-x. 11 root root 163 Jan 13 21:49 run
lrwxrwxrwx. 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 May 11 2019 srv
dr-xr-xr-x. 13 root root 0 Jun 16 02:47 sys
drwxrwxrwt. 7 root root 145 Jan 13 21:49 tmp
drwxr-xr-x. 12 root root 144 Jan 13 21:49 usr
drwxr-xr-x. 20 root root 262 Jan 13 21:49 var
drwxr-xr-x. 2 root root 6 Jun 16 21:52 volume01
drwxr-xr-x. 2 root root 6 Jun 16 21:52 volume02
[root@83599054db40 /]# cd volume01
[root@83599054db40 volume01]# touch container.txt
[root@83599054db40 volume01]# ls
container.txt
[root@83599054db40 volume01]#

# 宿主机
[root@localhost ~]# cd /var/lib/docker/volumes/067f640cdfc81c1e3ce11bff8c24388db73e367561377f5d73ab6a45463717e5/_data
[root@localhost _data]# ls
container.txt
[root@localhost _data]#

数据卷容器

image-20200617174749579

1
# 启动三个容器

image-20200617175144877

image-20200617181705013

image-20200617182108856

1
# 测试,删除 docker01 后,docker02、docker03 依旧可以访问文件,数据卷容器是拷贝的概念

结论:

容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止

一旦持久化到到了本地,那么即使容器删除,本地的数据也是不会消失的

DockerFile

DockerFile 介绍

dockfile 是用来构建镜像的脚本文件

构建步骤

  1. 编写一个 dockerfile 文件
  2. docker build 构建一个镜像
  3. docker run 运行镜像
  4. docker push 发布镜像(DockerHub、阿里云仓库)

image-20200617220456412

看一下官方 CentOS 是怎么做的

image-20200617220834719

很多官方的镜像都是基础包,很多功能没有,只有最底层的命令,我要需要自己搭建个性化镜像

DockerFile 构建过程

基础知识:

  1. 每个保留关键字(指令)都必须大写
  2. 执行从上到下顺序执行
  3. # 表示注释
  4. 每一个指令都会创建提交一个新的镜像层

img

Dockerfile 是面向开发的,以后我们要发布项目,做镜像,就要编写 Dockerfile

步骤:

DockerFile:构建文件,定义了一切的步骤,是源代码

DockerImage:通过 DockFile 构建生成的镜像,最终运行发布的产品

Docker容器:镜像运行起来以后提供服务

DockerFIle 指令说明

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM					# 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的,姓名 + 邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 要增加的步骤:nginx、tomcat 压缩包
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露的端口配置
CMD # 指定容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承的镜像,就会运行 ONBUILD 触发指令
COPY # 将文件拷贝到镜像中
ENV # 构建的时候设置环境变量

img

实战测试

DockerHub 中 99% 的镜像都是从 FROM scratch 基础包开始的

image-20200617223515144

创建一个自己的 CentOS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. 编写一个自己的 DockerFile
[root@localhost dockerfile]# cat dockerfile
FROM centos

MAINTAINER jiawang<zhangjiawang@126.com>

ENV MYPATH /usr/local

WORKDIR $MYPATH

RUN yum -y install vim

RUN yum -y install net-tools

EXPOSE 80

CMD echo $MYPATH

CMD echo "------end------"

CMD /bin/bash

# 2. ton过这个文件构建镜像
# 命令 docker build -f DockerFile文件 -t 镜像名[:tag]

[root@localhost dockerfile]# docker build -f dockerfile -t mycentos:1.0 .

Successfully built 1a79d7cec780
Successfully tagged mycentos:1.0

对比之前的 CentOS

image-20200617225319137

自己编写 DockerFile 构建的 CentOS

image-20200617225357125

可以通过 docker history 来查看镜像构建历史

image-20200617225656518

CMD 和 ENTRYPOINT 的区别

1
2
CMD						# 指定容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定容器启动的时候要运行的命令,可以追加命令

测试 CMD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 编写 dockerfile
[root@localhost dockerfile]# vi dockerfile-cmd
[root@localhost dockerfile]# cat dockerfile-cmd
FROM centos
CMD ["ls", "-a"]

# 构建镜像
[root@localhost dockerfile]# docker build -f dockerfile-cmd -t cmdtest .
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM centos
---> 470671670cac
Step 2/2 : CMD ["ls", "-a"]
---> Running in 7b5b77f02801
Removing intermediate container 7b5b77f02801
---> 3ce704558500
Successfully built 3ce704558500
Successfully tagged cmdtest:latest

# 运行镜像
[root@localhost dockerfile]# docker run 3ce704558500
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

# 想追加一个命令 -l ls -a -l
[root@localhost dockerfile]# docker run 3ce704558500 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.

# CMD 的情况下,-l 替换了 cmd ["ls", "-a"] -l 不是命令,所以报错

测试 ENTRYPOINT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 编写 dockerfile
[root@localhost dockerfile]# vi dockerfile-entryporint
[root@localhost dockerfile]# cat dockerfile-entryporint
FROM centos
ENTRYPOINT ["ls", "-a"]

# 构建镜像
[root@localhost dockerfile]# docker build -f dockerfile-entryporint -t entrypoint-test .
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM centos
---> 470671670cac
Step 2/2 : ENTRYPOINT ["ls", "-a"]
---> Running in d53dc3860faf
Removing intermediate container d53dc3860faf
---> 40d90fb68bc1
Successfully built 40d90fb68bc1
Successfully tagged entrypoint-test:latest

# 运行镜像
[root@localhost dockerfile]# docker run 40d90fb68bc1
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

# # 追加一个命令 -l 这里是直接追加在
[root@localhost dockerfile]# docker run 40d90fb68bc1 -l
total 0
drwxr-xr-x. 1 root root 6 Jun 17 01:10 .
drwxr-xr-x. 1 root root 6 Jun 17 01:10 ..
-rwxr-xr-x. 1 root root 0 Jun 17 01:10 .dockerenv
lrwxrwxrwx. 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x. 5 root root 340 Jun 17 01:10 dev
drwxr-xr-x. 1 root root 66 Jun 17 01:10 etc
drwxr-xr-x. 2 root root 6 May 11 2019 home
lrwxrwxrwx. 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------. 2 root root 6 Jan 13 21:48 lost+found
drwxr-xr-x. 2 root root 6 May 11 2019 media
drwxr-xr-x. 2 root root 6 May 11 2019 mnt
drwxr-xr-x. 2 root root 6 May 11 2019 opt
dr-xr-xr-x. 109 root root 0 Jun 17 01:10 proc
dr-xr-x---. 2 root root 162 Jan 13 21:49 root
drwxr-xr-x. 11 root root 163 Jan 13 21:49 run
lrwxrwxrwx. 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 May 11 2019 srv
dr-xr-xr-x. 13 root root 0 Jun 16 02:47 sys
drwxrwxrwt. 7 root root 145 Jan 13 21:49 tmp
drwxr-xr-x. 12 root root 144 Jan 13 21:49 usr
drwxr-xr-x. 20 root root 262 Jan 13 21:49 var

# 这里是直接追加在 ENTRYPOINT ["ls", "-a"] 命令后,ls -a -l 执行成功

dockerfile 文件建议使用官方命名:Dockerfile,这样在 build 的时候就不用 -f 参数了

发布自己的镜像

DockerHub

1
2
3
4
5
6
7
8
9
10
11
[root@localhost dockerfile]# docker login --help

Usage: docker login [OPTIONS] [SERVER]

Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.

Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
  • 登录 DockerHub 账号,并提交镜像 docker push
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@localhost dockerfile]# docker login -u zhangjiawang
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

[root@localhost dockerfile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.0 1a79d7cec780 56 minutes ago 317MB
redis latest 235592615444 6 days ago 104MB
nginx latest 2622e6cca7eb 7 days ago 132MB
mysql latest be0dbf01a0f3 7 days ago 541MB
centos latest 470671670cac 5 months ago 237MB
php 7.0-fpm 29e9a8718c7b 17 months ago 357MB


[root@localhost dockerfile]# docker push jiawang/mycentos:1.0
The push refers to repository [docker.io/jiawang/mycentos]
An image does not exist locally with the tag: jiawang/mycentos
[root@localhost dockerfile]# docker push mycentos:1.0
The push refers to repository [docker.io/library/mycentos]
f1cbd2486cfa: Preparing
c78665c57f59: Preparing
0683de282177: Preparing
denied: requested access to the resource is denied

# 提交被拒绝的解决办法
[root@localhost dockerfile]# docker tag 1a79d7cec780 zhangjiawang/mycentos:1.0
[root@localhost dockerfile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.0 1a79d7cec780 2 hours ago 317MB
zhangjiawang/mycentos 1.0 1a79d7cec780 2 hours ago 317MB
redis latest 235592615444 6 days ago 104MB
nginx latest 2622e6cca7eb 7 days ago 132MB
mysql latest be0dbf01a0f3 7 days ago 541MB
centos latest 470671670cac 5 months ago 237MB
php 7.0-fpm 29e9a8718c7b 17 months ago 357MB

# 增加自己 DockerHub 账号/镜像名[:tag] 后,再次提交
[root@localhost dockerfile]# docker push zhangjiawang/mycentos:1.0
The push refers to repository [docker.io/zhangjiawang/mycentos]
f1cbd2486cfa: Pushed
c78665c57f59: Pushed
0683de282177: Pushed
1.0: digest: sha256:77b380fce2147e1f19d1d265d937e19e2fccefb012c5ba8398d50d75ab502b3a size: 953

可以在自己的 DockerHub 仓库看到刚刚提交的镜像

image-20200618100421706

阿里云镜像服务器

  • 登录阿里云账号
  • 创建命名空间

image-20200618100919677

  • 创建镜像仓库

image-20200618101019584

  • 查看镜像仓库信息

image-20200618101149854

  • 参考官方操作指南,即可成功推送到阿里云镜像服务

小结

img

Docker 网络

理解 Docker0

docker 是如何处理容器网络访问的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@localhost ~]# docker run -d -P --name tomcat01 tomcat

# 查看容器的内部网络地址
[root@localhost ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
93: eth0@if94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

# eth0@if94 就是 docker 分配的

# 试一下再主机是否 ping 通容器内部
[root@localhost ~]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.087 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.065 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.246 ms
^C
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.065/0.132/0.246/0.081 ms

原理

1、我们每启动一个 docker 容器,docker 就会给容器分配一个ip,只要安装了 docker,就会有一个 docker0 网卡(桥接模式,使用的是 veth-pair 技术)

查看主机上的 ip addr

image-20200618113943738

2、再启动一个容器

1
2
3
4
5
6
7
8
9
10
11
[root@localhost ~]# docker run -d -P --name tomcat02 tomcat
54855663ad52514a9ad9e7f590a9962e340963d66dbfb90288d8f9540dbe5b44
[root@localhost ~]# docker exec -it tomcat02 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
95: eth0@if96: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever

再次查看主机上的 ip addr

image-20200618114227407

1
2
3
# 发现容器和主机上的网卡是一对一对相对应的
# veth-pair 就是一对虚拟设备接口,成对出现,一端连着协议,一端彼此相连
# 正因为这个特性,veth-pair 充当一个桥梁,链接各种虚拟网络设备

3、测试 tomcat01 和 tomcat02 是否 ping 通

1
2
3
4
5
6
7
8
9
[root@localhost ~]# docker exec -it tomcat02 ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.245 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.651 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.075 ms
^C
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 0.075/0.323/0.651/0.242 ms

以上的网络模型图

image-20200618115941525

结论: tomcat01 和 tomcat02 公用一个路由器(docker0)

所有容器不指定网络的情况下,都是 docker0 路由的,会给每个容器分配一个可以 IP

Docker 使用的是 Linux 的桥接,宿主机是一个 docker 容器的网桥

image-20200618120709828

Docker 中的所有网络接口都是虚拟的,虚拟转发效率高

只要容器删除,对应的一对网卡就会自动删除

容器与容器之间使用 name 可以 ping 通吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f644d2c6d657 tomcat "catalina.sh run" 14 seconds ago Up 9 seconds 0.0.0.0:32773->8080/tcp tomcat02
46468ed62331 tomcat "catalina.sh run" 3 hours ago Up 3 hours 0.0.0.0:32771->8080/tcp tomcat01

# 无法 ping 通
[root@localhost ~]# docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known

# 启动 tomcat03 使用 --link 链接 tomcat02
[root@localhost ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
accf9de93780b13d88ab78ce41c58728bcabfe5c3e3dded2293251286992c26d

# 可以 ping 通
[root@localhost ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.240 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.657 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.086 ms
^C
--- tomcat02 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 8ms
rtt min/avg/max/mdev = 0.086/0.327/0.657/0.242 ms

# 反向无法 ping 通
[root@localhost ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known

其本质就是在 tomcat03 的 /etc/hosts 文件增加了 tomcat02 的绑定

自定义网络

查看所有的 docker 网络

1
2
3
4
5
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
d6717b713333 bridge bridge local
d194cceb15fd host host local
29deb151b0e7 none null local

网络模式:

brige:桥接 docker默认

None:不配置网络

Host:和宿主机共享网络

Container:容器网络联通(不常用)

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# 我们启动容器时,其实会有一段默认参数 --net bridge 这个就是 docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat

# docker0 特点:默认的、域名不能访问, --link 可以打通

# 自定义一个网络
# --driver bridge 桥接
# -subnet 192.168.0.0/16 子网地址 192.168.0.2 - 192.168.255.255
# --gateway 192.168.0.1 网关

[root@localhost ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
abcfddab01f114ad25497940a1249cdc3f3dcac245a4b4089f3fc571ad753932
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
d6717b713333 bridge bridge local
d194cceb15fd host host local
abcfddab01f1 mynet bridge local
29deb151b0e7 none null local

# 查看自己创建的自定义网络
[root@localhost ~]# docker inspect abcfddab01f1
[
{
"Name": "mynet",
"Id": "abcfddab01f114ad25497940a1249cdc3f3dcac245a4b4089f3fc571ad753932",
"Created": "2020-06-17T07:43:33.09350487Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

[root@localhost ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
c1ec595f70cbf33e66ac63e8fe2774fa716c175444b66848d4a3a95eb2d8a6df
[root@localhost ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat
7fdd5b5b1d5a4c392dbc97193972d3d55a7cb672d5b7baf1a7fa39cd0013cc0c
[root@localhost ~]# docker inspect abcfddab01f1
[
{
"Name": "mynet",
"Id": "abcfddab01f114ad25497940a1249cdc3f3dcac245a4b4089f3fc571ad753932",
"Created": "2020-06-17T07:43:33.09350487Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"7fdd5b5b1d5a4c392dbc97193972d3d55a7cb672d5b7baf1a7fa39cd0013cc0c": {
"Name": "tomcat-net-02",
"EndpointID": "6d1fb68a72be6b3c5926427ba7cc2a206b544d3d54f3891e93dd2c562e3705c7",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"c1ec595f70cbf33e66ac63e8fe2774fa716c175444b66848d4a3a95eb2d8a6df": {
"Name": "tomcat-net-01",
"EndpointID": "5391dfd1a1b16b6bcc5777435b021066dc23ea99b35a95b83c47d2f87c59c522",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

# 再次测试 ping 链接
[root@localhost ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.835 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.215 ms
^C
--- 192.168.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.215/0.525/0.835/0.310 ms

# 没有使用--link 的情况下,也可以通过 name 来 ping 通了
[root@localhost ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.073 ms
^C
--- tomcat-net-02 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 0.062/0.067/0.073/0.009 ms

自定义网络 docker 已经维护好了对应关系

好处:

redis / mysql - 不同的集群使用不同的网络,保证集群是安全和健康的

网络连通

按照图片搭建容器

image-20200618153306931

查看容器:

image-20200618153420796

1
2
# 这个时候使用 tomcat01 是无法联通 tomcat-net-01 的
# 使用 docker network 命令进行联通

image-20200618152708158

image-20200618152757697

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 测试 打通 tomcat01 - mynet

# 联通之后将 tomcat01 添加到了 mynet 网络下
[root@localhost ~]# docker network connect mynet tomcat01
[root@localhost ~]# docker inspect mynet
[
{
"Name": "mynet",
"Id": "abcfddab01f114ad25497940a1249cdc3f3dcac245a4b4089f3fc571ad753932",
"Created": "2020-06-17T07:43:33.09350487Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"7fdd5b5b1d5a4c392dbc97193972d3d55a7cb672d5b7baf1a7fa39cd0013cc0c": {
"Name": "tomcat-net-02",
"EndpointID": "6d1fb68a72be6b3c5926427ba7cc2a206b544d3d54f3891e93dd2c562e3705c7",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"c1ec595f70cbf33e66ac63e8fe2774fa716c175444b66848d4a3a95eb2d8a6df": {
"Name": "tomcat-net-01",
"EndpointID": "5391dfd1a1b16b6bcc5777435b021066dc23ea99b35a95b83c47d2f87c59c522",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"c71620f5b41a88a78c110df8ef1e8fd12ccd1139e54e5ada151a1bf61fbafb84": {
"Name": "tomcat01",
"EndpointID": "940cb07f278fa561865fea08fee69b3d9faeddb731e963acd1706a798bdd7e85",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

# 使用 tomcat01 ping tomcat-net-01 ok!
[root@localhost ~]# docker exec -it tomcat01 ping tomcat-net-01
PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.093 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.188 ms
^C
--- tomcat-net-01 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.093/0.140/0.188/0.048 ms

# tomcat02 依旧不通
[root@localhost ~]# docker exec -it tomcat02 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known

该笔记整理与 B 站 up 主:狂神说Java

完整视频地址: https://www.bilibili.com/video/BV1S54y1R7SB

感谢狂神!