Docker入门

Docker 为什么会出现

​ Docker 出现的原因是为了解决软件开发和部署的复杂性问题。Docker 可以在一个轻量级的容器中运行应用程序,这样就可以确保应用程序在不同环境中的一致性。这样就可以简化部署过程,并减少在不同环境中出现的问题。此外,Docker 还可以提高应用程序的可移植性和可重复性,并且可以在整个开发和部署流程中提高效率。

Docker的历史

Docker是一个开源项目,最初由Dotcloud公司开发。Dotcloud是一家云计算公司,专门提供云端基础设施服务。

在2010年,Dotcloud公司的一组工程师开始研究如何更好地管理和部署应用程序。他们发现,当时的应用程序部署方式存在诸多问题,如:

  • 不同的环境导致应用程序在不同机器上表现不同
  • 应用程序依赖的库和环境配置难以维护
  • 部署和维护应用程序需要大量人力和时间

为了解决这些问题,Dotcloud公司开发了一种新的应用程序部署方式,即容器化。

在容器化中,应用程序和它所依赖的环境都被打包在一个容器中。这样,应用程序就可以在任何机器上运行,而且不会受到环境的影响。

Dotcloud公司在2013年将这种技术开源,并将其命名为Docker。

随着Docker的普及,容器化成为了当前应用程序部署和管理的主流方式。Docker社区也不断壮大,目前已经有超过35000个项目在Docker Hub上发布。

Docker最新超详细版教程通俗易懂

image-20230127211322366

虚拟化技术和容器化技术对比

虚拟化技术的缺点

  • 资源占用十分多
  • 冗余步骤多
  • 启动很慢

容器化技术

  • 比较Docker和虚拟化技术的不同
    • 传统虚拟机, 虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
    • 容器内的应用直接运行在宿主机的内部,容器是没有自己的内核的,也没有虚拟硬件,所以轻便
    • 每个容器间是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响
  • 应用更快速的交互和部署
    • 传统:一堆帮助文档,安装程序
    • Docker: 打包镜像发布测试,一键运行
  • 更便捷的升级和扩缩容
  • 更简的系统运维
  • 更高效的计算资源利用

DevOps

DevOps是一种软件开发方法论,旨在缩短软件交付周期并提高质量。它通过提高开发人员和运维人员之间的合作来实现这一目标。DevOps的特点包括:

  • 自动化和流程化:使用工具和技术自动化软件开发和部署流程,减少人工干预
  • 持续交付:通过迭代开发和部署,不断地将新功能添加到生产环境中
  • 持续集成:通过自动化测试和验证来确保软件质量,并确保每次更改都能被快速集成到主干中
  • 关注效能: 通过监控和分析来提高软件的可用性和可靠性
  • 团队合作:通过增加开发人员和运维人员之间的沟通和协作来提高效率和质量

名词解释

  • 镜像(image)
    • Docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像=>run=> tomcat01容器, 通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)
  • 容器(container)
    • Docker利用容器技术,独立运行一个或者一组应用, 通过镜像来创建的
    • 启动,停止,删除,基本命令!
      就目前可以把这个容器理解为一个建议的linux系统
  • 仓库(repository)
    • 存放镜像的地方
    • Docker Hub(默认是国外的)
    • 阿里云都有容器服务(配置镜像加速!)

阿里云镜像加速

  1. 登录阿里云服务器,找到容器镜像服务
  2. 设置Registry登录密码
  3. 找到镜像加速器
  4. 配置使用
1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://pi9dpp60.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

安装docker

其他系统详细可以参考官方文档,这里以centos为例

卸载旧版本

较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关的依赖项。

1
2
3
4
5
6
7
8
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装 Docker Engine-Community

使用 Docker 仓库进行安装

在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库。之后,您可以从仓库安装和更新 Docker。

设置仓库:

安装所需的软件包。yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2。

1
2
3
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

使用以下命令来设置稳定的仓库。

使用官方源地址(比较慢)
1
2
3
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
阿里云
1
2
3
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
清华大学源
1
2
3
sudo yum-config-manager \
--add-repo \
https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
安装 Docker Engine-Community

安装最新版本的 Docker Engine-Community 和 containerd,或者转到下一步安装特定版本:

1
sudo yum install docker-ce docker-ce-cli containerd.io

Docker 安装完默认未启动。并且已经创建好 docker 用户组,但该用户组下没有用户。

要安装特定版本的 Docker Engine-Community,请在存储库中列出可用版本,然后选择并安装:

1、列出并排序您存储库中可用的版本。此示例按版本号(从高到低)对结果进行排序。

1
yum list docker-ce --showduplicates | sort -r

2、通过其完整的软件包名称安装特定版本,该软件包名称是软件包名称(docker-ce)加上版本字符串(第二列),从第一个冒号(:)一直到第一个连字符,并用连字符(-)分隔。例如:docker-ce-18.09.1。

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

启动 Docker

1
sudo systemctl start docker

通过运行 hello-world 映像来验证是否正确安装了 Docker Engine-Community 。

1
sudo docker run hello-world

底层原理

  • HelloWorld镜像

image-20230127214419439

image-20230127214506402
  • 底层原理

    Docker Engine是一个客户端-服务器应用程序,具有以下主要组件:

    • 一个服务器,它是一种长期运行的程序,称为守护进程(dockerd命令)
    • 一个REST API,它指定程序可以用来与守护进程对话并指示它做什么的接口。

Docker是一个Client Server结构的系统,Docker守护进程运行在主机上,然后通过Socket连接从客户 端访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器,是一个运行时环境就是我们所说的集装箱。

image-20230127214543713
  • 为什么Docker比Vm快
    • docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。**
    • docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个 容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引寻、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载GuestOS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返个过程,因此新建一个docker容器只需要几秒钟。
image-20230127214609071

Docker基本命令

Docker的常用命令

帮助命令

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

镜像命令

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

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

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

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

docker search 查找镜像

1
2
3
4
5
6
7
8
9
10
11
12
NAME                              DESCRIPTION                                     STARS               OFFICIAL         AUTOMATED
mysql MySQL is a widely used, open-source relation… 9822 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3586 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 719 [OK]

# 可选项
--filter=STARS=3000 # 搜素出来的镜像就是STARS大于3000的

[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker search mysql --filter=STARS=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 9822 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3586 [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
48
# 下载镜像,docker pull 镜像名[:tag]
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker pull mysql
Using default tag: latest # 如果不写tag,默认就是latest
latest: Pulling from library/mysql
bf5952930446: Pull complete # 分层下载,dockerimages的核心,联合文件系统
8254623a9871: Pull complete
938e3e06dac4: Pull complete
ea28ebf28884: Pull complete
f3cef38785c2: Pull complete
894f9792565a: Pull complete
1d8a57523420: Pull complete
6c676912929f: Pull complete
ff39fdb566b4: Pull complete
fff872988aba: Pull complete
4d34e365ae68: Pull complete
7886ee20621e: Pull complete
Digest: sha256:c358e72e100ab493a0304bda35e6f239db2ec8c9bb836d8a427ac34307d074ed # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址

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

# 指定版本下载
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
bf5952930446: Already exists
8254623a9871: Already exists
938e3e06dac4: Already exists
ea28ebf28884: Already exists
f3cef38785c2: Already exists
894f9792565a: Already exists
1d8a57523420: Already exists
5f09bf1d31c1: Pull complete
1b6ff254abe7: Pull complete
74310a0bf42d: Pull complete
d398726627fd: Pull complete
Digest: sha256:da58f943b94721d46e87d5de208dc07302a8b13e638cd1d24285d222376d6d84
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

# 查看本地镜像
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 718a6da099d8 6 days ago 448MB
mysql latest 0d64f46acfd1 6 days ago 544MB
hello-world latest bf756fb1ae65 7 months ago 13.3kB

docker rmi 删除镜像

1
2
3
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker rmi -f IMAGE ID                        # 删除指定镜像
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker rmi -f IMAGE ID1 IMAGE ID2 IMAGE ID3 # 删除多个镜像
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker rmi -f $(docker images -aq) # 删除所有镜像

容器命令

说明: 我们有了镜像才可创建容器,linux,下载一个centos镜像来测试学习

1
docker pull 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
docker run [可选参数] image

# 参数说明
--name=“Name” 容器名字 tomcat01 tomcat02 用来区分容器
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-p 随机指定端口


# 测试,启动并进入容器
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -it centos /bin/bash
[root@74e82b7980e7 /]# ls # 查看容器内的centos,基础版本,很多命令是不完善的
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr

# 从容器中退回主机
[root@77969f5dcbf9 /]# exit
exit
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# ls
bin dev fanfan lib lost+found mnt proc run srv tmp var
boot etc home lib64 media opt root sbin sys usr

列出所有的运行的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# docker ps 命令
# 列出当前正在运行的容器
-a # 列出正在运行的容器包括历史容器
-n=? # 显示最近创建的容器
-q # 只显示当前容器的编号

[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77969f5dcbf9 centos "/bin/bash" 5 minutes ago Exited (0) 5 minutes ago xenodochial_bose
74e82b7980e7 centos "/bin/bash" 16 minutes ago Exited (0) 6 minutes ago silly_cori
a57250395804 bf756fb1ae65 "/hello" 7 hours ago Exited (0) 7 hours ago elated_nash
392d674f4f18 bf756fb1ae65 "/hello" 8 hours ago Exited (0) 8 hours ago distracted_mcnulty
571d1bc0e8e8 bf756fb1ae65 "/hello" 23 hours ago Exited (0) 23 hours ago magical_burnell

[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker ps -qa
77969f5dcbf9
74e82b7980e7
a57250395804
392d674f4f18
571d1bc0e8e8

退出容器

1
2
exit            # 直接退出容器并关闭
Ctrl + P + Q # 容器不关闭退出

删除容器

1
2
3
docker rm -f 容器id                       # 删除指定容器
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -a -q|xargs docker rm -f # 删除所有的容器

启动和停止容器的操作

1
2
3
4
docker start 容器id           # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 强制停止当前的容器

常用的其他命令

后台启动容器

1
2
3
4
5
6
7
# 命令 docker run -d 镜像名
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker run -d centos

# 问题 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 number 容器id

[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker logs -tf --tail 1 8d1621e09bff
2020-08-11T10:53:15.987702897Z [root@8d1621e09bff /]# exit # 日志输出

# 自己编写一段shell脚本
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker run -d centos /bin/sh -c "while true;do echo liaojie1314;sleep 1;done"
a0d580a21251da97bc050763cf2d5692a455c228fa2a711c3609872008e654c2

[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0d580a21251 centos "/bin/sh -c 'while t…" 3 seconds ago Up 1 second lucid_black

# 显示日志
-tf # 显示日志
--tail number # 显示日志条数
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker logs -tf --tail 10 a0d580a21251

查看容器中进程信息ps

1
2
3
4
# 命令 docker top 容器id
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker top df358bc06b17
UID PID PPID C STIME TTY
root 28498 28482 0 19:38 ?

查看镜像的元数据

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
# 命令
docker inspect 容器id

[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker inspect df358bc06b17
[
{
"Id": "df358bc06b17ef44f215d35d9f46336b28981853069a3739edfc6bd400f99bf3",
"Created": "2020-08-11T11:38:34.935048603Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 28498,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-08-11T11:38:35.216616071Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:0d120b6ccaa8c5e149176798b3501d4dd1885f961922497cd0abef155c869566",
"ResolvConfPath": "/var/lib/docker/containers/df358bc06b17ef44f215d35d9f46336b28981853069a3739edfc6bd400f99bf3/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/df358bc06b17ef44f215d35d9f46336b28981853069a3739edfc6bd400f99bf3/hostname",
"HostsPath": "/var/lib/docker/containers/df358bc06b17ef44f215d35d9f46336b28981853069a3739edfc6bd400f99bf3/hosts",
"LogPath": "/var/lib/docker/containers/df358bc06b17ef44f215d35d9f46336b28981853069a3739edfc6bd400f99bf3/df358bc06b17ef44f215d35d9f46336b28981853069a3739edfc6bd400f99bf3-json.log",
"Name": "/hungry_heisenberg",
"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/5af8a2aadbdba9e1e066331ff4bce56398617710a22ef906f9ce4d58bde2d360-init/diff:/var/lib/docker/overlay2/62926d498bd9d1a6684bb2f9920fb77a2f88896098e66ef93c4b74fcb19f29b6/diff",
"MergedDir": "/var/lib/docker/overlay2/5af8a2aadbdba9e1e066331ff4bce56398617710a22ef906f9ce4d58bde2d360/merged",
"UpperDir": "/var/lib/docker/overlay2/5af8a2aadbdba9e1e066331ff4bce56398617710a22ef906f9ce4d58bde2d360/diff",
"WorkDir": "/var/lib/docker/overlay2/5af8a2aadbdba9e1e066331ff4bce56398617710a22ef906f9ce4d58bde2d360/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "df358bc06b17",
"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"
],
"Cmd": [
"/bin/bash"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200809",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "4822f9ac2058e8415ebefbfa73f05424fe20cc8280a5720ad3708fa6e80cdb08",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/4822f9ac2058",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "5fd269c0a28227241e40cd30658e3ffe8ad6cc3e6514917c867d89d36a31d605",
"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": "30d6017888627cb565618b1639fecf8fc97e1ae4df5a9fd5ddb046d8fb02b565",
"EndpointID": "5fd269c0a28227241e40cd30658e3ffe8ad6cc3e6514917c867d89d36a31d605",
"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
}
}
}
}
]

进入当前正在运行的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 我们通常容器使用后台方式运行的, 需要进入容器,修改一些配置

# 命令
docker exec -it 容器id /bin/bash

# 测试
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker exec -it df358bc06b17 /bin/bash
[root@df358bc06b17 /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@df358bc06b17 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Aug11 pts/0 00:00:00 /bin/bash
root 29 0 0 01:06 pts/1 00:00:00 /bin/bash
root 43 29 0 01:06 pts/1 00:00:00 ps -ef

# 方式二
docker attach 容器id

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

从容器中拷贝文件到主机

1
2
3
docker cp 容器id:容器内路径    目的地主机路径

[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker cp 7af535f807e0:/home/Test.java /home

查看服务器上Docker镜像 latest具体版本

1
docker image inspect (docker image名称):latest|grep -i version

打包加载镜像

1
2
3
4
5
6
7
8
# 打包镜像
docker save 镜像id > myImage.tar
# 或者
docker save 镜像id -o myImage.tar
# 加载镜像
docker load < myImage.tar
# 或者
docker load -i myImage.tar

Docker部署软件实战

Docker安装Nginx

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
# 1. 搜索镜像 search 建议去docker hub搜索,可以看到帮助文档
# 2. 下载镜像 pull
# 3. 运行测试
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 0d120b6ccaa8 32 hours ago 215MB
nginx latest 08393e824c32 7 days ago 132MB

# -d 后台运行
# -name 给容器命名
# -p 宿主机端口:容器内部端口
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d --name nginx01 -p 3344:80 nginx # 后台方式启动启动镜像
fe9dc33a83294b1b240b1ebb0db9cb16bda880737db2c8a5c0a512fc819850e0
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe9dc33a8329 nginx "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 0.0.0.0:3344->80/tcp nginx01
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# curl localhost:3344 # 本地访问测试

# 进入容器
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker exec -it nginx01 /bin/bash
root@fe9dc33a8329:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@fe9dc33a8329:/# cd /etc/nginx/
root@fe9dc33a8329:/etc/nginx# ls
conf.d koi-utf mime.types nginx.conf uwsgi_params
fastcgi_params koi-win modules scgi_params win-utf

端口暴露概念

image-20230127215644450

Docker安装Tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 官方的使用
docker run -it --rm tomcat:9.0

# 我们之前的启动都是后台的,停止了容器之后, 容器还是可以查到,docker run -it --rm 一般用来测试,用完就删

# 下载再启动
docker pull tomcat

# 启动运行
docker run -d -p 3344:8080 --name tomcat01 tomcat

# 测试访问没有问题

# 进入容器
docker exec -it tomcat01 /bin/bash

# 发现问题:1.linux命令少了, 2. webapps下内容为空,阿里云净吸纳过默认是最小的镜像,所有不必要的都剔除了,保证最小可运行环境即可

Docker部署es + kibana

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
# es 暴露的端口很多
# es 十分的耗内存
# es 的数据一般需要放置到安全目录! 挂载
# --net somenetwork 网络配置

# 启动elasticsearch
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2

[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
a920894a940b354d3c867079efada13d96cf9138712c76c8dea58fabd9c7e96f

# 启动了linux就卡主了,docker stats 查看cpu状态

# 测试一下es成功了
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# curl localhost:9200
{
"name" : "a920894a940b",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "bxE1TJMEThKgwmk7Aa3fHQ",
"version" : {
"number" : "7.6.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date" : "2020-03-26T06:34:37.794943Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}


# 增加内存限制,修改配置文件 -e 环境配置修改
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2

可视化

  • portainer
1
2
3
4
5
6
7
8
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer

# 测试
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# curl localhost:8088
<!DOCTYPE html
><html lang="en" ng-app="portainer">

# 外网访问 http://ip:8088

image-20230127215923865

  • Rancher(CI/CD再用)

Docker原理

特点:

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

这一层就是我们通常说的容器层, 容器之下的都叫做镜像层

image-20230127220034488

commit镜像

1
2
3
4
5
6
docker commit 提交容器成为一个新的版本

# 命令和git 原理类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]

docker commit -a="liaojie1314" -m="add webapps app" d798a5946c1f tomcat007:1.0

容器数据卷

docker的理解回顾

从前面的学习中我们不难发现:如果我们将数据都存在容器中,那么当我们把容器删除后,数据就会永远丢失!那么我们数据可以持久化

比如MySQL,我们把容器删了,就相当于删库跑路了!

这时我们就想把数据存储到本地,在容器删除后,本地依然保留着数据。这就是卷技术,目录的挂载就是将我们容器内的目录挂载到linux目录上面!

**总结: **容器的持久化和同步操作!容器间数据也是可以共享的!

使用数据卷

直接使用命令来挂载 -v

1
2
3
docker run -it -v 主机目录:容器目录

[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -it -v /home/ceshi:/home centos /bin/bash
image-20230127220759858

测试文件的同步(在主机上改动,观察容器变化)

image-20230127220851065

再来测试(测试通过)

  1. 停止容器
  2. 主机上修改文件
  3. 启动容器
  4. 容器内的数据依旧是同步的!

实战:安装MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 获取镜像
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker pull mysql:5.7

# 运行容器, 需要做数据挂载! # 安装启动mysql,需要配置密码(注意)
# 官方测试, docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

# 启动我们的
-d # 后台运行
-p # 端口隐射
-v # 卷挂载
-e # 环境配置
--name # 容器的名字
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d -p 3344:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
9552bf4eb2b69a2ccd344b5ba5965da4d97b19f2e1a78626ac1f2f8d276fc2ba

# 启动成功之后,我们在本地使用navicat链接测试一下
# navicat链接到服务器的3344 --- 3344 和 容器的3306映射,这个时候我们就可以连接上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
32
33
34
35
36
37
38
39
40
41
# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx # -P 随机指定端口

# 查看所有volume的情况
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker volume ls
DRIVER VOLUME NAME
local 561b81a03506f31d45ada3f9fb7bd8d7c9b5e0f826c877221a17e45d4c80e096
local 36083fb6ca083005094cbd49572a0bffeec6daadfbc5ce772909bb00be760882

# 这里发现,这种情况就是匿名挂载,我们在-v 后面只写了容器内的路径,没有写容器外的路径!

# 具名挂载
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
26da1ec7d4994c76e80134d24d82403a254a4e1d84ec65d5f286000105c3da17
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
26da1ec7d499 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:32769->80/tcp nginx02
486de1da03cb nginx "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 0.0.0.0:32768->80/tcp nginx01
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker volume ls
DRIVER VOLUME NAME
local 561b81a03506f31d45ada3f9fb7bd8d7c9b5e0f826c877221a17e45d4c80e096
local 36083fb6ca083005094cbd49572a0bffeec6daadfbc5ce772909bb00be760882
local juming-nginx

# 通过-v 卷名:容器内的路径
# 查看一下这个卷
# docker volume inspect juming-nginx

[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker volume inspect juming-nginx
[
{
"CreatedAt": "2020-08-12T18:15:21+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]

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

我们通过具名挂载可以方便的找到我们的一个卷,大多数情况下使用的是具名挂载

1
2
3
4
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载!
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /主机路径:容器内路径 # 指定路径挂载

拓展

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

docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx

# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内容无法操作

Dockerfile

初始Dockerfile

Dockerfile 是一种用来创建 Docker 镜像的配置文件。它包含了创建镜像所需要的命令和说明。Dockerfile 可以让用户在已有镜像的基础上构建新镜像,并且能够保证新镜像中所包含的文件和设置都是可预测的。

1
2
3
4
5
6
7
8
9
10
11
# 创建一个dockerfile文件, 名字可以随机
# 文件的内容 指定(大写) 参数

FROM centos

VOLUME ["volume01", "volume02"]

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

# 这里的每一个命令都是镜像的一层!
1
2
docker build -f 指定dockerfile名,如果只有一个Dockerfile文件可以不指定(注意名字要为Dockerfile) -t 自定义的镜像名:版本号 .
#注意最后的.一定要搞忘了

最后的 . 一定不要忘了

数据卷容器

多个mysql同步数据

image-20230127221939537
1
2
3
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7

结论

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

但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的!

Dockerfile

dockerfile是用来构建docker镜像的文件,命令参数脚本。

构建步骤

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

Dockerfile的构建过程

基础知识:

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

dockerfile是面向开发的, 我们以后要发布项目, 做镜像, 就需要编写dockefile文件, 这个文件十分简单!

Docker镜像逐渐成为企业的交互标准,必须要掌握!

步骤:开发,部署, 运维… 缺一不可!

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

DockerImages: 通过DockerFile构建生成的镜像, 最终发布和运行的产品!

Docker容器:容器就是镜像运行起来提供服务器

DockerFile指令说明

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

创建一个自己的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
# 1. 编写Dockerfile的文件
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# cat mydockerfile-centos
FROM centos
MAINTAINER liaojie1314<1517438366@qq.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. 通过这个文件构建镜像
# 命令 docker build -f dockerfile文件路径 -t 镜像名:[tag] .

[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker build -f mydockerfile-centos -t mycentos:0.1 .

Successfully built d2d9f0ea8cb2
Successfully tagged mycentos:0.1

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
# 1. 编写dockerfile文件
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# vim dockerfile-cmd-test
FROM centos
CMD ["ls", "-a"]

# 2. 构建镜像
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest .

# 3. run运行, 发现我们的ls -a 命令生效
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker run ebe6a52bb125
.
..
.dockerenv
bin
dev
etc
home
lib
lib64

# 想追加一个命令 -l 变成 ls -al
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker run ebe6a52bb125 -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.
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker run ebe6a52bb125 ls -l

# 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
# 1. 编写dockerfile文件
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# vim dockerfile-entrypoint-test
FROM centos
ENTRYPOINT ["ls", "-a"]

# 2. 构建文件
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker build -f dockerfile-entrypoint-test -t entrypoint-test .

# 3. run运行 发现我们的ls -a 命令同样生效
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker run entrypoint-test
.
..
.dockerenv
bin
dev
etc
home
lib

# 4. 我们的追加命令, 是直接拼接到ENTRYPOINT命令的后面的!
[root@iZ2zeg4ytp0whqtmxbsqiiZ dockerfile]# docker run entrypoint-test -l
total 56
drwxr-xr-x 1 root root 4096 Aug 13 07:52 .
drwxr-xr-x 1 root root 4096 Aug 13 07:52 ..
-rwxr-xr-x 1 root root 0 Aug 13 07:52 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Aug 13 07:52 dev
drwxr-xr-x 1 root root 4096 Aug 13 07:52 etc
drwxr-xr-x 2 root root 4096 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 4096 Aug 9 21:40 lost+found

Dockerfile制作tomcat镜像

  1. 准备镜像文件 tomcat压缩包jdk-8u73-linux-x64.tar.gz,jdk的压缩包apache-tomcat-9.0.37.tar.gz

  2. 编写Dockerfile文件,官方命名Dockerfile, build会自动寻找这个文件,就不需要-f指定了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    [root@iZ2zeg4ytp0whqtmxbsqiiZ tomcat]# cat Dockerfile 
    FROM centos
    MAINTAINER liaojie1314<1517438366@qq.com>

    COPY readme.txt /usr/local/readme.txt

    ADD jdk-8u73-linux-x64.tar.gz /usr/local/
    ADD apache-tomcat-9.0.37.tar.gz /usr/local/

    RUN yum -y install vim

    ENV MYPATH /usr/local
    WORKDIR $MYPATH

    ENV JAVA_HOME /usr/local/jdk1.8.0_73
    ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
    ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.37
    ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.37
    ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

    EXPOSE 8080

    CMD /usr/local/apache-tomcat-9.0.37/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.37/bin/logs/catalina.out
  3. 构建镜像

    1
    docker build -t diytomcat .
  4. 启动镜像

    1
    docker run -d -p 3344:8080 --name liaojie1314tomcat1 -v /home/liaojie1314/build/tomcat/test:/usr/local/apache-tomcat-9.0.37/webapps/test -v /home/liaojie1314/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.37/logs diytomcat
  5. 访问测试

  6. 发布项目(由于做了卷挂载, 我们直接在本地编写项目就可以发布了)

在本地编写web.xml和index.jsp进行测试

image-20230127223727182
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

</web-app>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello. liaojie1314</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("-----my test web logs------");
%>
</body>
</html>

发布自己的镜像到Docker Hub

  1. 地址 注册自己的账号
  2. 点击Repositories=>Create创建仓库
image-20230127224342231
  1. 确定这个账号可以登录

​ 通过docker login -u 账号在本地登录测试

  1. 在我们的服务器上提交自己的镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# push到我们的服务器上
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker push diytomcat
The push refers to repository [docker.io/library/diytomcat]
2eaca873a720: Preparing
1b38cc4085a8: Preparing
088ebb58d264: Preparing
c06785a2723d: Preparing
291f6e44771a: Preparing
denied: requested access to the resource is denied # 拒绝

# push镜像的问题?
The push refers to repository [docker.io/liaojie1314/diytomcat]
An image does not exist locally with the tag: liaojie1314/diytomcat

# 解决,增加一个tag
docker tag diytomcat liaojie1314/tomcat:1.0

发布到阿里云镜像服务上

  1. 登录阿里云

  2. 找到容器镜像服务

  3. 创建命名空间

  4. 创建容器镜像

  5. 点击仓库名称,参考官方文档即可

image-20230127225018125

Docker网络

链接Docker0

测试

image-20230127225224495

三个网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 问题: docker是如何处理容器网络访问的?

# [root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -d -P --name tomcat01 tomcat

# 查看容器内部的网络地址 ip addr
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker exec -it tomcat01 ip addr, 发现容器启动的时候得到一个eth0@if115 ip地址,docker分配的!
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
114: eth0@if115: <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

# 思考: linux 能不能ping通容器?
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# 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.077 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.069 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.075 ms

# linux 可以 ping 通docker容器内部

原理

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

再次测试 ip addr

image-20230127225334190
  1. 再启动一个容器测试, 发现又多了一对网卡
image-20230127225402053
1
2
3
4
# 我们发现这个容器带来网卡,都是一对对的
# veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相连
# 正因为有这个特性,veth-pair充当一个桥梁, 连接各种虚拟网络设备
# OpenStac, Docker容器之间的链接,OVS的链接, 都是使用veth-pair技术
  1. 我们测试一下tomcat01和tomcat02之间是否可以ping通
image-20230127225448619

结论:容器与容器之间是可以相互ping通的

绘制一个网络模型图

image-20230127225526499

结论:tomcat01和tomcat02是共用的一个路由器,docker0

所有容器不指定网络的情况下,都是docker0路由的,doucker会给我们的容器分配一个默认的可用IP

小结

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

Docker中的所有的网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件!)

只要容器删除,对应的网桥一对就没有了!

image-20230127225734131

思考一个场景,我们编写了一个微服务,database url =ip; 项目不重启,数据ip换掉了,我们希望可以处理这个问题,可以按名字来进行访问容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known

# 如何可以解决呢?
# 通过--link既可以解决网络连通问题
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
3a2bcaba804c5980d94d168457c436fbd139820be2ee77246888f1744e6bb473
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3a2bcaba804c tomcat "catalina.sh run" 4 seconds ago Up 3 seconds 0.0.0.0:32772->8080/tcp tomcat03
f22ed47ed1be tomcat "catalina.sh run" 57 minutes ago Up 57 minutes 0.0.0.0:32771->8080/tcp tomcat02
9d97f93401a0 tomcat "catalina.sh run" About an hour ago Up About an hour 0.0.0.0:32770->8080/tcp tomcat01
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# 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.129 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.100 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.110 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=4 ttl=64 time=0.107 ms

# 反向可以ping通吗?
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known

探究:inspect

image-20230127225838682

其实这个tomcat03就是在本地配置了tomcat02的配置?

1
2
3
4
5
6
7
8
9
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 f22ed47ed1be
172.17.0.4 3a2bcaba804c

本质探究:–link 就是我们在hosts配置中增加了一个172.17.0.3 tomcat02 f22ed47ed1be

我们现在使用Docker已经不建议使用–link了!

自定义网络!不使用Docker0!

Docker0的问题:它不支持容器名链接访问!

自定义网络

查看所有的docker网络

image-202301272300764

网络模式

bridge: 桥接模式,桥接 docker 默认,自己创建的也是用brdge模式

none: 不配置网络

host: 和宿主机共享网络

container:容器网络连通!(用的少, 局限很大)

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 我们直接启动的命令默认有一个 --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 可以支持255*255个网络 192.168.0.2 ~ 192.168.255.254
# --gateway 192.168.0.1
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
26a5afdf4805d7ee0a660b82244929a4226470d99a179355558dca35a2b983ec
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
30d601788862 bridge bridge local
226019b14d91 host host local
26a5afdf4805 mynet bridge local
7496c014f74b none null local

我们自己创建的网络就ok了

image-20230127233339168

在自己创建的网络里面启动两个容器

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
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
0e85ebe6279fd23379d39b27b5f47c1e18f23ba7838637802973bf6449e22f5c
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat
c6e462809ccdcebb51a4078b1ac8fdec33f1112e9e416406b606d0c9fb6f21b5
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "26a5afdf4805d7ee0a660b82244929a4226470d99a179355558dca35a2b983ec",
"Created": "2020-08-14T11:12:40.553433163+08:00",
"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": {
"0e85ebe6279fd23379d39b27b5f47c1e18f23ba7838637802973bf6449e22f5c": {
"Name": "tomcat-net-01",
"EndpointID": "576ce5c0f5860a5aab5e487a805da9d72f41a409c460f983c0bd341dd75d83ac",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"c6e462809ccdcebb51a4078b1ac8fdec33f1112e9e416406b606d0c9fb6f21b5": {
"Name": "tomcat-net-02",
"EndpointID": "81ecbc4fe26e49855fe374f2d7c00d517b11107cc91a174d383ff6be37d25a30",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

# 再次拼连接
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# 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.113 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.093 ms
^C
--- 192.168.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.093/0.103/0.113/0.010 ms
# 现在不使用 --link也可以ping名字了!
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# 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.068 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.096 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.094 ms

我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络

好处:

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

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

网络连通

image-20230127233456365

测试打通tomcat01 和mynet

image-20230127233520066
1
2
3
4
5
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker network connect  mynet tomcat01

# 连通之后就是讲tomcat01 放到了mynet网路下
# 一个容器两个ip地址:
# 阿里云服务器,公网ip,私网ip
image-20230127233556382 image-20230127233611573
1
2
3
4
5
6
7
8
9
10
11
12
# 连通ok
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# 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.100 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.085 ms
^C
--- tomcat-net-01 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.085/0.092/0.100/0.012 ms
# 依旧无法连通,没有connect
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker exec -it tomcat02 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known

结论:假设要跨网络 操作别人,就要使用docker network connect连通

实战:部署redis

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
# 创建网卡
docker network create redis --subnet 172.38.0.0/16

# 通过脚本创建六个redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 创建结点1
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

#创建结点2
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \
-v /mydata/redis/node-2/data:/data \
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#创建结点3
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
-v /mydata/redis/node-3/data:/data \
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#创建结点4
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \
-v /mydata/redis/node-4/data:/data \
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#创建结点5
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \
-v /mydata/redis/node-5/data:/data \
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#创建结点6
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

# 创建集群
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker exec -it redis-1 /bin/sh
/data # ls
appendonly.aof nodes.conf
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 541b7d237b641ac2ffc94d17c6ab96b18b26a638 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: a89c1f1245b264e4a402a3cf99766bcb6138dbca 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: 259e804d6df74e67a72e4206d7db691a300c775e 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: 9b19170eea3ea1b92c58ad18c0b5522633a9e271 172.38.0.14:6379
replicates 259e804d6df74e67a72e4206d7db691a300c775e
S: 061a9d38f22910aaf0ba1dbd21bf1d8f57bcb7d5 172.38.0.15:6379
replicates 541b7d237b641ac2ffc94d17c6ab96b18b26a638
S: 7a16b9bbb0615ec95fc978fa62fc054df60536f0 172.38.0.16:6379
replicates a89c1f1245b264e4a402a3cf99766bcb6138dbca
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 541b7d237b641ac2ffc94d17c6ab96b18b26a638 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: a89c1f1245b264e4a402a3cf99766bcb6138dbca 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 7a16b9bbb0615ec95fc978fa62fc054df60536f0 172.38.0.16:6379
slots: (0 slots) slave
replicates a89c1f1245b264e4a402a3cf99766bcb6138dbca
S: 061a9d38f22910aaf0ba1dbd21bf1d8f57bcb7d5 172.38.0.15:6379
slots: (0 slots) slave
replicates 541b7d237b641ac2ffc94d17c6ab96b18b26a638
M: 259e804d6df74e67a72e4206d7db691a300c775e 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 9b19170eea3ea1b92c58ad18c0b5522633a9e271 172.38.0.14:6379
slots: (0 slots) slave
replicates 259e804d6df74e67a72e4206d7db691a300c775e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

docker搭建redis集群完成!

Docker Compose

简介

Docker

Dockerfile build run 手动操作,单个容器!

微服务,100个微服务,依赖关系。

Docker Compose 来轻松高效的管理容器,定义运行多个容器。

官方介绍

  1. 定义运行多个容器
  2. YAML file配置文件
  3. single command。命令有哪些?

​ Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.

  1. 所有的环境都可以使用compose。

​ Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in Common Use Cases.

三步骤:

Using Compose is basically a three-step process:

  1. Define your app’s environment with a Dockerfile so it can be reproduced anywhere.

    1. Dockerfile保证我们的项目再任何地方可以运行
  2. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.

    1. services 什么是服务。
  3. Run docker-compose up and Compose starts and runs your entire app.

    1. 启动项目

Compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: '2.0'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}

Compose:重要概念

  • 服务services, 容器、应用(web、redis、mysql…)
  • 项目project。 一组关联的容器

安装

  1. 下载
1
2
3
4
5
6
# 官网提供 (没有下载成功)
curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 国内地址
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

当然你也可以去github上下载下来放到/usr/local/bin/docker-compose文件中

  1. 授权
1
chmod +x /usr/local/bin/docker-compose

官方教程

1
sudo apt-get install docker-compose

流程:

  1. 创建网络
  2. 执行Docker-compose.yaml
  3. 启动服务

yaml规则

docker-compose.yaml 核心!

官方文档

开源项目:wordpress博客

https://docs.docker.com/compose/wordpress/

下载程序、安装数据库、配置…

compose应用 => 一键启动

  1. 下载项目(docker-compse.yaml)
  2. 如果需要文件。Dockerfile
  3. 文件准备齐全,一键启动项目即可

Docker Swarm

Swarm集群搭建

工作机制

image-20230127234620173
1
2
3
4
5
6
7
8
9
docker swarm init --help

ip addr # 获取自己的ip(用内网的不要流量)

[root@iZ2ze58v8acnlxsnjoulk5Z ~]# docker swarm init --advertise-addr 172.16.250.97
Swarm initialized: current node (otdyxbk2ffbogdqq1kigysj1d) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-3vovnwb5pkkno2i3u2a42yrxc1dk51zxvto5hrm4asgn37syfn-0xkrprkuyyhrx7cidg381pdir 172.16.250.97:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

初始化结点docker swarm init

docker swarm join 加入一个结点!

1
2
3
# 获取令牌
docker swarm join-token manager
docker swarm join-token worker
1
2
[root@iZ2ze58v8acnlxsnjoulk6Z ~]# docker swarm join --token SWMTKN-1-3vovnwb5pkkno2i3u2a42yrxc1dk51zxvto5hrm4asgn37syfn-0xkrprkuyyhrx7cidg381pdir 172.16.250.97:2377
This node joined a swarm as a worker.

把后面的结点都搭建进去

image-20230127234757483

Raft协议

双主双从:假设一个结点挂了!其他结点是否可以用!

Raft协议:保证大多数结点存活才可以使用,只要>1, 集群至少大于3台!

实验:

  1. 将docker1机器停止。宕机!双主,另外一个结点也不能使用了!

  2. 可以将其他结点离开docker swarm leave

  3. worker就是工作的,管理结点操作! 3台结点设置为了管理结点。

  4. Docker swarm集群增加节点

十分简单:集群,可用! 3个主节点。 > 1台管理结点存活!

Raft协议:保证大多数结点存活,才可以使用,高可用!

体会

弹性、扩缩容!集群!

以后告别 docker run!

docker-compose up!启动一个项目。单机!

集群: swarm docker-service

k8s service

容器 => 服务!

容器 => 服务! => 副本!

redis => 10个副本!(同时开启10个redis容器)

体验:创建服务、动态扩容服务、动态更新服务

  • 灰度发布(金丝雀发布)
1
2
docker run 容器启动! 不具有扩缩容器
docker service 服务! 具有扩缩容器,滚动更新!

查看服务

image-20230127235205938

动态扩缩容

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@iZ2ze58v8acnlxsnjoulk5Z ~]# docker service update --replicas 3 my-nginx
1/3: running [==================================================>]
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged


[root@iZ2ze58v8acnlxsnjoulk5Z ~]# docker service scale my-nginx=5
my-nginx scaled to 5
overall progress: 3 out of 5 tasks
overall progress: 3 out of 5 tasks
overall progress: 3 out of 5 tasks
overall progress: 5 out of 5 tasks
1/5: running [==================================================>]
2/5: running [==================================================>]
3/5: running [==================================================>]
4/5: running [==================================================>]
5/5: running [==================================================>]
verify: Service converged


[root@iZ2ze58v8acnlxsnjoulk5Z ~]# docker service scale my-nginx=1
my-nginx scaled to 1
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged

移出!docker service rm

docker swarm其实并不难

只要会搭建集群、会启动服务、动态管理容器就可以了!

概念的总结

swarm

集群的管理和编号,docker可以初始化一个swarm集群,其他结点可以加入。(管理,工作者)

Node

就是一个docker结点,多个结点就组成了一个网络集群(管理、工作者)

Service

任务,可以在管理结点或者工作结点来运行。核心,用户访问。

Task

容器内的命令、细节任务!

其他命令学习方式

  • Docker Stack
1
2
3
4
5
6
7
docker-compose 单机部署项目
docker stack 集群部署

# 单机
docker-compose up -d wordpress.yaml
# 集群
docker stack deploy wordpress.yaml
  • Docker Secret
1
2
3
4
5
6
7
8
9
10
11
12
13
安全!配置密码!证书!

[root@iZ2ze58v8acnlxsnjoulk5Z ~]# docker secret --help

Usage: docker secret COMMAND

Manage Docker secrets

Commands:
create Create a secret from a file or STDIN as content
inspect Display detailed information on one or more secrets
ls List secrets
rm Remove one or more secrets
  • Docker Config
1
2
3
4
5
6
7
8
9
10
11
12
配置!
[root@iZ2ze58v8acnlxsnjoulk5Z ~]# docker config --help

Usage: docker config COMMAND

Manage Docker configs

Commands:
create Create a config from a file or STDIN
inspect Display detailed information on one or more configs
ls List configs
rm Remove one or more configs

docker镜像仓库

镜像仓库简介

镜像仓库作用:存储镜像,可以通过镜像仓库实现镜像的共享

镜像仓库的使用流程:

image-20230424164001006

镜像仓库分类:

1、公有镜像仓库:可以理解成云仓库、公有仓库,是由某个组织、公司维护的仓库,对互联网开放,所有人共享。如官方的仓库docker hub。

2、私有镜像仓库:不对外开放。一般情况下只在公司内网中使用,用于加快镜像拉取速度、维护公司资产。

私有镜像仓库种类:

1、Registry是Docker自带的镜像仓库,部署运行非常简洁,非常适合在测试时使用。

2、Harbor

Harbor简介

Harbor是VMware公司开源的企业级DockerRegistry项目,其目标是帮助用户迅速搭建一个企业级的Dockerregistry服务。它以Docker公司开源的registry为基础,提供了管理UI,基于角色的访问控制(Role Based Access Control),AD/LDAP集成、以及审计日志(Auditlogging) 等企业用户需求的功能,同时还原生支持中文。

下载

Harbor被部署为多个Docker容器,因此可以部署在任何支持Docker的Linux服务器上,且需要Docker和Docker Compose才能安装。

下载地址:https://github.com/goharbor/harbor/releases

image-20230424165240085

这里我们下载在线安装包:

1
wget https://github.com/goharbor/harbor/releases/download/v2.8.0/harbor-online-installer-v2.8.0.tgz

安装

具体步骤如下所示:

1、解压并修改配置文件

1
2
3
tar -zxvf harbor-online-installer-v2.8.0.tgz     # 解压tgz包
cp harbor.yml.tmpl harbor.yml # 创建一个配置文件
vim harbor.yml # 修改配置文件

配置的文件的修改如下所示:

image-20230725123018878

2、安装Harbor

1
2
3
# 1、 进入到Harbor的解压目录
# 2、执行安装脚本
sh install.sh

image-20230725114808111

注意:如果安装的时候报错了,可以给docker配置多个镜像地址:

1
2
3
4
5
6
7
// 编辑文件
vim /etc/docker/daemon.json

// 文件内容
{
"registry-mirrors": ["https://registry.docker-cn.com","http://hub-mirror.c.163.com","http://f1361db2.m.daocloud.io","https://mirror.ccs.tencentyun.com","https://phtv51hj.mirror.aliyuncs.com"]
}

3、启动关闭命令

1
2
docker compose -f docker-compose.yml up -d            启动 Harbor
docker compose -f docker-compose.yml stop 关闭 Harbor

4、访问Harbor

image-20230725114929808

  • 用户名/密码:admin/Harbor12345

image-20230725115003967

5、docker添加安全访问权限

1
2
3
4
5
6
7
8
9
10
# 编辑/etc/docker/daemon.json文件
vim /etc/docker/daemon.json

# 添加安全访问权限
{
"insecure-registries":["http://你的电脑ip"]
}

# 重启Docker
systemctl restart docker

docker部署

基于docker环境及Harbor部署springboot3项目

settings.xml

在maven的settings.xml文件中配置harbor服务的账号信息:

1
2
3
4
5
6
7
8
<server>
<id>harbor</id>
<username>admin</username>
<password>Harbor12345</password>
<configuration>
<email>123456@aliyun.com</email>
</configuration>
</server>

引入依赖插件

pom.xml文件添加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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<properties>
<docker.repostory>你的电脑ip</docker.repostory>
<docker.registry.name>模块名</docker.registry.name>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.2.2</version>

<!--将插件绑定在某个phase执行-->
<executions>
<execution>
<id>build-image</id>
<!--将插件绑定在package这个phase(阶段)上。也就是说,用户只需执行mvn package,就会自动执行mvn docker:build-->
<phase>package</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>

<configuration>

<serverId>harbor</serverId>
<registryUrl>http://${docker.repostory}</registryUrl>
<dockerHost>http://${docker.repostory}:2375</dockerHost> <!-- 配置docker主机地址 -->

<!--指定生成的镜像名-->
<imageName>${docker.repostory}/${docker.registry.name}/${project.artifactId}:${project.version}</imageName>

<!-- 指定 dockerfile 路径-->
<dockerDirectory>${project.basedir}</dockerDirectory>

<!-- 是否跳过docker构建 -->
<skipDockerBuild>false</skipDockerBuild>

</configuration>

</plugin>
</plugins>
</build>

插件原理:就是在docker主机上构建docker对应的镜像,然后将构建的镜像推送到harbor远程仓库中。

docker服务端开启远程访问

1
2
3
4
5
6
7
8
#修改该文件
vim /lib/systemd/system/docker.service

#找到ExecStart行,修改成如下内容
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H fd:// --containerd=/run/containerd/containerd.sock

systemctl daemon-reload #重启守护进程
systemctl restart docker #重启docker

注意:关闭防火墙systemctl stop firewalld.service

编写dockerfile文件

  • 使用dockerfile来构建一个包含Jdk17的centos7镜像

分析:

1、基础的镜像的应该选择centos:7

2、在自己所构建的镜像中需要包含Jdk17,就需要把Jdk17添加到centos:7的基础镜像中

3、为了方便的去使用自己构建的镜像中的Jdk17,就需要去配置环境变量

4、因为Jdk17仅仅是一个开发工具,并不是一个服务进程,因此在启动容器的时候可以不指定任何的执行命令

实现步骤:

1、将Jdk17的安装包上传到linux服务器的指定目录下

2、在Jdk17所在的目录下创建一个dockerfile文件

3、使用docker build命令构建镜像

4、使用docker images查看镜像构建情况

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1、创建目录
mkdir –p /usr/local/dockerfilejdk17
cd /usr/local/dockerfilejdk17
  
# 2、下载jdk-17_linux-x64_bin.tar.gz并上传到服务器(虚拟机)中的/usr/local/dockerfilejdk17目录
# 3、在/usr/local/dockerfilejdk17目录下创建dockerfile文件,文件内容如下:
vim dockerfile

FROM centos:7
MAINTAINER yuanyuan
RUN mkdir -p /usr/local/java
ADD jdk-17_linux-x64_bin.tar.gz /usr/local/java/
ENV JAVA_HOME=/usr/local/java/jdk-17.0.7
ENV PATH=$PATH:$JAVA_HOME/bin

# 4、执行命令构建镜像;不要忘了后面的那个 .
docker build -t centos7-jdk17 .

# 5、查看镜像是否建立完成
docker images
  • Dockerfile文件的位置必须是和pom.xml处于同一个目录
1
2
3
4
5
6
7
FROM centos7-jdk17
MAINTAINER yuanyuuan
EXPOSE 项目运行端口
ADD target/xxx-1.0-SNAPSHOT.jar /xxx-1.0-SNAPSHOT.jar

WORKDIR /
ENTRYPOINT ["java" , "-jar" , "xxx-1.0-SNAPSHOT.jar"]

执行maven的打包命令

  • 在父工程目录下面执行命令
1
2
mvn clean package -DskipTests								# 打包跳过测试
mvn clean package -DskipTests -DskipdockerBuild # 打包跳过测试的同时提高构建

Harbor查看镜像:

image-20230711220733821

注意:需要先在Harbor上将项目(模块名)创建出来

拉取镜像部署

部署

使用docker compose部署

xxx.yml文件的内容如下所示:

1
2
3
4
5
6
services:
service-user:
container_name: xxx
image: 你的电脑ip/模块名/xxx:1.0-SNAPSHOT
ports:
- "8512:8512"

**注意:**拉取失败,请在docker中添加安全访问权限

1
2
3
4
5
6
7
8
9
10
# 编辑/etc/docker/daemon.json文件
vim /etc/docker/daemon.json

# 添加安全访问权限
{
"insecure-registries":["http://你的电脑ip"]
}

# 重启Docker
systemctl restart docker

docker compose相关命令复习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动容器(如果不存在容器就创建、存在则修改)
docker compose -f docker-compose.yml up -d

# 删除所有容器
docker compose -f docker-compose.yml down

# 停止所有容器
docker compose -f docker-compose.yml stop

# 启动所有容器
docker compose -f docker-compose.yml start

# 重启所有容器
docker compose -f docker-compose.yml restart

启动

docker compose相关命令:

1
2
# 启动容器(如果不存在容器就创建、存在则修改)
docker compose -f xxx.yml up -d

nvidia-docker

什么是nvidia-docker

nvidia-docker 是一个用于将 NVIDIA GPU 支持集成到 Docker 容器中的工具和运行时。它允许容器直接访问和利用宿主机上的 NVIDIA GPU,这对于运行 GPU 加速的应用程序,如深度学习和数据科学工作负载非常有用。

nvidia-docker 的早期版本中,它是作为一个独立的命令行工具提供的,使得用户能够以类似于常规 docker 命令的方式运行和管理 Docker 容器,但额外地提供了对 GPU 的支持。然而,从 Docker 19.03 版本开始,Docker 引入了原生的 GPU 支持,这使得 nvidia-docker 的功能被集成到标准的 Docker 命令中。

为什么使用nvidia-docker

使用 nvidia-docker 或 Docker 的 --gpus 标志,你可以在隔离的容器环境中运行 GPU 加速的应用程序,而无需担心依赖项和配置问题。这对于确保环境的一致性和可复现性非常有用,特别是在深度学习和机器学习的研究和开发中。

安装nvidia-container-toolkit

nvidia-docker和nvidia-docker2已经被英伟达迭代了,使用的话会出现别的问题,可以认为nvidia-container-toolkit是nvidia-docker和nvidia-docker2的替代品,它可以让您在Docker容器中使用NVIDIA GPU。

nvidia-container-toolkit是一组软件包,它们包括一个容器运行时库和一些工具,可以自动配置容器来利用NVIDIA GPU。它支持不同的容器引擎,如Docker、containerd、LXC、Podman等。

这里按照官方推荐使用nvidia-container-toolkit会更好,此处给出安装成功的方法:

1
2
3
4
5
6
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker

也可以按照官方提供的安装方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#1.配置生产存储库:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
#(可选)配置存储库以使用实验包:
sed -i -e '/experimental/ s/^#//g' /etc/apt/sources.list.d/nvidia-container-toolkit.list

#2.从存储库更新包列表:
sudo apt-get update

#3.安装 NVIDIA Container Toolkit 软件包:
sudo apt-get install -y nvidia-container-toolkit

#配置 Docker
#使用以下命令配置容器运行时nvidia-ctk:
sudo nvidia-ctk runtime configure --runtime=docker
#该nvidia-ctk命令修改/etc/docker/daemon.json主机上的文件。该文件已更新,以便 Docker 可以使用 NVIDIA 容器运行时。

#重新启动 Docker 守护进程:
sudo systemctl restart docker

其他安装工具安装配置请参考官网

测试一下:

1
2
3
4
sudo docker run --rm --gpus all nvidia/cuda:${根据网站查询得到} nvidia-smi

# 例如
sudo docker run --rm --gpus all nvidia/cuda:11.7.1-base-ubuntu20.04 nvidia-smi

supported-tags中找到与自己系统对应的cuda版本,并将${根据网站查询得到}改成对应的版本

image-20240128154121020

出现如下输出即为成功

image-20240128152417452

docker安装软件实战

安装RabbitMQ

1
docker run -e RABBITMQ_DEFAULT_USER=yuanyuan -e RABBITMQ_DEFAULT_PASS=1024 -v rabbitmq3.8-plugins:/plugins --name rabbitmq3.8  --hostname rabbitmq3.8 -p 15672:15672 -p 5672:5672 -d rabbitmq:3.8-management

其中

  • rabbitmq3_8-plugins:/plugins: 创建存放位置 会自动创建mq-plugins数据卷
  • 15672: RabbitMQ提供的管理控制台的端口
  • 5672: RabbitMQ的消息发送处理接口

安装RocketMQ

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
docker pull apache/rocketmq:5.3.0 # 拉取镜像
# 创建容器共享网络
# RocketMQ 中有多个服务,需要创建多个容器,创建 docker 网络便于容器间相互通信。
docker network create rocketmq
# 启动NameServer
docker run -d --name rmqnamesrv -p 9876:9876 --network rocketmq apache/rocketmq:5.3.0 sh mqnamesrv
# 验证 NameServer 是否启动成功
docker logs -f rmqnamesrv # 我们可以看到 'The Name Server boot success..', 表示NameServer 已成功启动。
# 启动 Broker+Proxy
# 配置 Broker 的IP地址
echo "brokerIP1=127.0.0.1" > broker.conf

# 启动 Broker 和 Proxy
docker run -d \
--name rmqbroker \
--network rocketmq \
-p 10912:10912 -p 10911:10911 -p 10909:10909 \
-p 8080:8080 -p 8081:8081 \
-e "NAMESRV_ADDR=rmqnamesrv:9876" \
-v ./broker.conf:/home/rocketmq/rocketmq-5.3.0/conf/broker.conf \
apache/rocketmq:5.3.0 sh mqbroker --enable-proxy \
-c /home/rocketmq/rocketmq-5.3.0/conf/broker.conf

# 验证 Broker 是否启动成功
docker exec -it rmqbroker bash -c "tail -n 10 /home/rocketmq/logs/rocketmqlogs/proxy.log"

# 进入broker容器,通过mqadmin创建 Topic
docker exec -it rmqbroker bash
sh mqadmin updatetopic -t TestTopic -c DefaultCluster