Docker

概述

什么是Docker

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。

Docker为什么会出现

假定您在开发一个电商项目,您使用的是一台笔记本电脑,而且您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。此外,您的企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列的支持文件。您希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销。请问?

您要如何确保应用能够在这些环境中运行和通过质量检测?并且在部署过程中不出现令人头疼的版本、配置问题,也无需重新编写代码和进行故障修复?
image-20230405185604813

答案就是使用容器。Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案—-系统平滑移植,容器虚拟化技术

举个例子:

在以前,开发人员只需要提交源代码(jar包等等)直接发给运维人员即可。然后,运维开始完成部署。会有三种情况让运维人员头疼。

第一种情况,部署环境问题。

运维在部署的时候会有很多环境问题:mysql版本、redis补丁、java版本(开发的时候是java8,生产的时候是java7,可能一些java8的新特性,根本无法使用)

第二种情况 ,安装过于繁琐。

运维因为环境问题导致各种软件版本不一样,这时候运维人员就会要求开发人员写一张list清单(环境配置表)。开发是什么环境,我运维就怎么配。那么问题来了:在多台集群的情况下。这时候运维就疯了,或许我的mysql是1主1从,装2台。redis是3主3从,装6台。运维稍微因为版本和环境配置,装错一步,可能系统就跑不起来了,大家只能互相扯皮,运维是非常非常痛苦的。

第三种情况,弹性发布太慢。

假设服务器不够用了,运维需要扩容、缩容,符合弹性的发布。redis是六台3主3从。现在环境并发量大了,需要马上扩容,变成4主4从,瞬间要求你起来两个实例,如果按照以前的安装步骤,你把运维逼疯,也不可能秒级别的把服务跑起来。

image-20230405185909006

环境配置相当麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样复制过来,开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。

image-20230405185945874

假设你在北京某小区,5号楼5单元朝北的屋子501室,找到工作后要搬家到上海,绝对不可能是同样的小区,同样的楼层,同样的户型。这时候你就要打包、解压、货拉拉搬家公司,搬过去,重新收拾,非常麻烦,而且还可能适应不了新环境。

用了docker以后,陆地航空母舰,类似于推土机直拔下去,连根基都搬起来,搬到另外一个地方,也就是直接搬楼,就没有上面述说的问题了。

也就是说,docker实现了从搬家到搬楼。
image-20230405190217525

之前在服务器配置一个应用的运行环境,要安装各种软件,就拿某电商项目的环境来说,Java/ RabbitMQ/ MySQL/ JDBC等驱动包等。安装和配置这些东西有多麻烦就一一举例了,它还不能跨平台使用。假如我们在windows上安装的这些环境,到了Linux又得重新装,况且就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。

传统上认为,软甲编码开发/测试结束后,所产生的成果即是程序或是能够编译执行的二进制字节码等(java为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让运维团队得以部署应用程式。开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。Docker的出现使得Docker得以打破过去[程序及应用]的观念。通过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由上而下打包,达到应用程式跨平台的无缝运作。

Docker的安装

docker的基本构成

image-20230405190537527

Docker由三部分构成

docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像===>run ==> tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。

(有点像Java 中的 类和对象的关系)

容器(Containers):Docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。启动,停止,删除,基本命令,目前就可以把这个容器理解为就是一个简易的linux系统

仓库(Registry):仓库就是存放镜像的地方!仓库分为公有仓库和私有仓库!Docker Hub (默认是国外的)阿里云….都有容器服务器(配置镜像加速!)

Docker安装

首先卸载之前所安装的docker

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

安装docker所需的安装包

1
yum intsall -y yum-utils

设置镜像仓库(阿里云)

1
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装Docker

docker-ce 是社区版,docker-ee 企业版,正常推荐都是社区版

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

启动docker

1
systemctl start docker

查看docker版本号

1
systemctl enable docker

设置docker开机启动

1
systemctl enable docker

helloWorld

image-20230405193634592

查看hello-world镜像

1
docker images

image-20230405194014644

卸载依赖

1
yum remove docker-ce docker-ce-cli containerd.io

删除资源

1
rm -rf /var/lib/docker  # /var/lib/docker  docker的默认工作路径

docker是如何工作的

Docker是一个Client - Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问你DockerServer接收到Docker-Client的指令,就会执行这个命令!

img

Docker 的常用命令

帮助命令

1
2
3
docker version   #显示docker 的版本信息
docker info #显示docker的系统信息
docker 命令 --hlep #帮助命令

镜像命令

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

1
2
3
4
5
6
docker images 查看所有本地的主机上的镜像

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

image-20230405201336610

REPOSITORY 镜像仓库

TAG 镜像标签

IMAGE ID 镜像ID

CREATED 镜像创建时间

SIZE 镜像的大小

docker search 搜索镜像

1
2
3
4
5
6
docker search    搜索镜像


#可选项

-f , --filter 过滤不匹配某个条件的结果

image-20230405202519749

1
docker search mysql -f starts=3000   #过滤掉stars少于3000的结果

image-20230405203024683

docker pull 下载镜像

1
2
3
docker pull  镜像名 
docker pull mysql #默认下载最新版
docker pull mysql:版本 #指定版本下载

image-20230405203902854

本次 docker pull mysql

等价于

docker pull docker.io/library/mysql:latest

指定版本下载时,需要进入docker hub 搜索所要拉取的镜像,然后查看其版本号,不能随便你填写版本号

image-20230405204652116

docker rmi 镜像名 (ID) 删除镜像

1
docker rmi mysql  删除Mysql镜像

我们可以结合查询出的ID来进行删除

1
docker rmi -f  $(docker images 镜像名 -aq)         #-f 强制

删除docker镜像中所有该镜像名的镜像(通过ID),类似于SQL语句 select * from (select * from table where age=18) where id=1

容器命令

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

1
docker pull centos

docker pull 镜像名(创建容器并启动)

1
2
3
4
5
6
7
8
9
10
docker run [可选参数] images

#参数说明
--name="Name" 容器名称tomcat1 tomcat2 用来区分容器
-d 以后台方式是运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器端口, -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口

image-20230405215533012

image-20230405215830610

我们可以发现,我们进入了centos的镜像中后,我们的主机名变成了容器ID

image-20230405215601465

我们在容器的交互中,可以使用CentOs中的一些基本命令

docker ps (列出所有正在运行的容器)

1
2
3
4
5
docker ps 
#可选项
-a 列出历史运行的容器
-n=? 显示最近创建的容器 (n为显示几个)
-q 只显示容器的编号

image-20230405220106374

退出容器

1
2
exit   #容器退出并停止
Ctrl + p + Q #容器退出但不停止

删除容器 (docker rm)

1
docker rm 容器id  #不能删除正在运行的容器,如果要删除则需要 -f

启动容器和停止容器

1
2
3
docker start 容器id
docker restart 容器id
docker stop 容器id

常用的其他命令

后台启动容器

1
2
3
4
  docker run -d 镜像名
#间题docker ps,发现centos停止了
#常见的坑: docker容器使用后台运行,就必须要有要一个前台进程,docker发现没有应用,就会自动停止
# nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了

查看日志

docker logs 容器id

1
2
3
4
5
docker logs 容器id
-t 日志时间
-f 按顺序输出
-n (n为1,2,3,4……正整数) 表示显示多少条日志 默认为全部输出
--tail 显示多少行

image-20230405224456268

获取容器/镜像的元数据

docker inspect name

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
[root@zf ~]# docker inspect 28d5c397bc7a
[
{
"Id": "28d5c397bc7a04d1714ab29c732d437bf5f724e5c13f13eccc735d46f63bbd3c",
"Created": "2023-04-05T14:05:28.549337831Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 25927,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-04-05T14:34:18.593114606Z",
"FinishedAt": "2023-04-05T14:27:23.27214544Z"
},
"Image": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6",
"ResolvConfPath": "/var/lib/docker/containers/28d5c397bc7a04d1714ab29c732d437bf5f724e5c13f13eccc735d46f63bbd3c/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/28d5c397bc7a04d1714ab29c732d437bf5f724e5c13f13eccc735d46f63bbd3c/hostname",
"HostsPath": "/var/lib/docker/containers/28d5c397bc7a04d1714ab29c732d437bf5f724e5c13f13eccc735d46f63bbd3c/hosts",
"LogPath": "/var/lib/docker/containers/28d5c397bc7a04d1714ab29c732d437bf5f724e5c13f13eccc735d46f63bbd3c/28d5c397bc7a04d1714ab29c732d437bf5f724e5c13f13eccc735d46f63bbd3c-json.log",
"Name": "/flamboyant_brown",
"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,
"ConsoleSize": [
28,
96
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"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",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": [],
"BlkioDeviceWriteBps": [],
"BlkioDeviceReadIOps": [],
"BlkioDeviceWriteIOps": [],
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"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/18dc63033c647828a8eefe6182a62e2bda3007b7b98bc89da143f1eb44a18e51-init/diff:/var/lib/docker/overlay2/73c431d34821ae24e87e28743131aec77315b42e3ebe01348ee0e04d42d35b79/diff",
"MergedDir": "/var/lib/docker/overlay2/18dc63033c647828a8eefe6182a62e2bda3007b7b98bc89da143f1eb44a18e51/merged",
"UpperDir": "/var/lib/docker/overlay2/18dc63033c647828a8eefe6182a62e2bda3007b7b98bc89da143f1eb44a18e51/diff",
"WorkDir": "/var/lib/docker/overlay2/18dc63033c647828a8eefe6182a62e2bda3007b7b98bc89da143f1eb44a18e51/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "28d5c397bc7a",
"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": "20210915",
"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": "657e17eed5f6d372e6bc2ffa1d434b7917f272b0a8730a03b8e85abc4d3ce253",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/657e17eed5f6",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "868c72fa757c50501eccc38e346ac5be7bd03ed3abdbeb816e61ffb0a6c0daa7",
"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": "a572afb31a92cc9289bd647bfa9fa3c86c078ebcb8d69778a7aac550893e76f5",
"EndpointID": "868c72fa757c50501eccc38e346ac5be7bd03ed3abdbeb816e61ffb0a6c0daa7",
"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
docker attach 容器id
docker exec -it 容器id /bin/bash

区别
#当多个窗口同是attach到同一个容器的时候,所有窗口都会同步显示;当某个窗口因命令阻塞时,其他窗口也无法执行操作。可以使用 docker exec -it 容器id /bin/bash 进入容器并开启一个新的bash终端。 退出容器终端时,不会导致容器的停止。使用 docker attach 容器id 进入正在执行容器,不会启动新的终端, 退出容器时,会导致容器的停止。

将文件容器拷贝到主机中

1
docker cp 容器id:复制文件的绝对路径 主机路径

image-20230406083140961

练习

docker 安装 nginx

首先搜索nginx

1
docker search nginx

下载nginx镜像

1
docker pull nginx

创建容器并在后台运行设置其端口号和宿主机端口号为 3344:80

1
docker run -d --name nginx01 -p 3344:80 nginx

查看nginx是否正常运行

在宿主机中直接curl 访问3344端口

1
curl localhost:3344

image-20230406084540618

进入容器交互 ,查看nginx配置文件

image-20230406085020057

可视化

  • portainer(先用这个)

    1
    2
    docker run -d -p 8088:9000 \
    --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privilaged-true portainer/portainer

从外部访问portainer

image-20230406093012246

image-20230406093028681

Docker镜像讲解

什么是镜像

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来!如何得到镜像︰

  • 朋友拷贝给你

  • 自己制作一个镜像DockerFile

  • 从远程仓库下载

镜像加载原理

UnionFS(联合文件系统)

UnionFS ( 联合文件系统):Union文件系统( UnionFS )是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootis(boot flesystem)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
roots (root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc, /bin, /etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如uuntu , Centos等等。

image-20230406094122283

可是为什么平时我们的CentOS都是好几个G但是,Docker这里才200MB?

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

Commit镜像

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

image-20230406101411062

容器数据卷

什么是容器数据卷

​ docker理念回顾:将应用和环境打包成一个镜像!但是如果数据在容器中,一旦容器被删除,那么数据就会消失

所以我们就有了需求:数据持久化

容器之间可以有一个数据共享的技术!Docker容器中产生的数据同步到本地!这就是卷技术!目录的挂载,将我们容器内的目录挂载到Linux上面

image-20230406103413573

使用数据卷

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

    1
    docker run -it -v 主机目录:容器内的目录 -p 宿主机端口:容器端口

    当我们查看元数据时

    image-20230406154016803

image-20230406154039046

当我们在宿主机目录中创建文件并添加内容时,容器目录中我们可以看到该内容

image-20230406154445464

类似于动态绑定,宿主机目录中的内容和容器目录内容相同

具名挂载与匿名挂载

当我们进行容器运行挂载时我们可以指定挂载时,数据卷的名称

1
2
3
4
5
6
#匿名挂载
docker run -v 容器目录 镜像名
#具名挂载
docker run -v 数据卷名:容器目录 镜像名
#指定路径名挂载
docker run -v /宿主机目录:容器目录 镜像名

我们将nginx配置文件相关目录进行挂载

利用对数据卷操作命令

1
2
3
docker volume 
#可选项
ls 打印所有数据卷名称

打印出所有数据卷的名称

image-20230406165303846

查看数据卷在宿主机中的名称(具名挂载)

image-20230406164722489

倘若我们不指定在宿主机中的挂载位置,docker就会将数据卷挂载到宿主机的 /var/lib/docker/(具名挂载名称或匿名挂载名称)/_data 目录中

查看(匿名挂载)数据卷在宿主机中的名称(匿名挂载的名称是一串不规律的字符串)

image-20230406165100392

我们可以看到 docker下一级目录的名称与具名挂载或匿名挂载的数据卷的名称相同

设置对数据卷的读写权限

1
2
3
4
#ro read only 只读
#rw readwrite 可读可写
docker run -v 数据卷名:容器目录:ro 镜像名
docker run -v 数据卷名:容器目录:rw 镜像名

练习

docker安装并配置mysql

首先安装拉取mysql8.0镜像

1
docker pull mysql:8.0

创建容器并实现挂载 并且 要提前设置ROOT用户的密码 将容器3306端口映射为宿主机端口3406

1
docker run -d -p 3406:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql01 mysql

我们发现可以实现多挂载,将宿主机目录挂载到容器中

image-20230406160954531

当我们将容器删除,我们发现数据卷依然存在,实现了容器的持久化

image-20230406161200982

数据卷容器

多个mysql同步数据

image-20230406220143255

dockerFile

dockerfile是用来构建dokcer镜像的文件!命令参数脚本,是由一条条构建镜像所需的指令和参数构成的脚本

dockerFile有什么作用

某种镜像的增强,给我个list做个清单;后续,我需要加入任何功能,
直接在list单子里面写好,相当于多次提交
‘Dockerfile,本镜像需要一次性添加
vim/ifconfig/tomcat8/java8…..

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

DockerFile的构建过程

基础知识

1、每个保留关键字(指令)都是必须是大写字母

2、执行从上到下顺序执行
3、#表示注释
4、每一个指令都会创建提交一个新的镜像层,并提交!

image-20230406221926098

DockerFile的指令

以前的话就是我们使用别人的,现在我们知道了这些指令了,我们来自己配置一些镜像

docker基础知识

  • 每条保留字指令都必须为大写字母且后面要跟随至少一个参数

  • 指令按照从上到下,顺序执行

  • #表示注释

  • 每条指令都会创建一个新的镜像层并对镜像进行提交

docker执行过程

( 1) docker从基础镜像运行一个容器

(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层

( 4) docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成

从应用软件的角度来看,

Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,*Dockerfile是软件的原材料
Docker镜像是软件的交付品
*Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,哈力充当Dopker体系的基石。

image-20230407170824382

保留字

保留字就是其关键字,结合上图去理解每个关键字所执行的时刻

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM #基础镜像,一切从这里开始,制定一个已经存在的镜像作为模板,第一条必须是from
MAINTAINER #镜像是谁写的,镜像维护者的姓名和邮箱地址
RUN #镜像构建(build)的时候需要运行的命令
ADD # 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar解压包
WORKDIR #镜像的工作目录 ,指定在创建容器后,终端默认登录进来的工作目录,一个落脚点
VOLUME #挂载的目录
EXPOSE #暴露端口
CMD #指定容器要启动的时候执行的命令 ,只有最后一个会生效,CMD会被docker run之后的参数替代
ENTRYPOINT #指定这个容器启动的时候要启动的命令,可以追加命令
ONBUILD #当构建一个被继承DockerFile 这个时候就会运行
COPY # 类似于ADD ,将我们的文件拷贝到镜像中,将从构建上下文目录中<源路径>的文件/目录复制到新一层的镜像内的<目标路径>
ENV #构建的时候设置环境变量
VOLUME #容器数据卷,用于数据保存和持久化工作

CMD会被docker run命令替代类似于

我们在DockerFile中设置了命令

1
CMD["catalina.sh","run"]

当我们在运行镜像添加命令 例如 docker run tomcat /bin/bash

这时候,DockerFile中的命令变成了

1
CMD["/bin/bash","run"]

这样启动tomcat容器,tomcat服务并未启动

ENTRYPOINT可以和CMD一起用,一般是变参才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。
当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合就会变成

1
<ENTRYPOINT> <CMD>

image-20230407180309174

实战测试

构建ctenos

DockerHub中99%镜像都是从这个基础镜像过来的 FROM scratch ,然后配置需要的软件和配置来进行构建

image-20230407001519383

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM centos:7   #基于centos7进行构建
MAINTAINER zhangfeng<zzzzzf011129@163.com> #构建者与构建者的名称

ENV MYPATH /usr/local #配置环境变量 MYPATH

WORKDIR $MYPATH #当跳转到控制台后的路径

RUN yum -y install vim #镜像构建时所要执行的命令
RUN yum -y install net-tools
EXPOSE 80 #暴露的端口号

CMD eho $MYPATH #容器构建所要执行的命令

CMD eho "---END---"

CMD /bin/bash

构建镜像所要执行的命令

1
docker build -f dockerFile名称 -t 目标镜像名:版本号  .

image-20230407092742429

创建容器并运行后

直接跳转到 /usr/local目录下

image-20230407092819762

dockerhub

将本地镜像推送dockerhub

首先要登录dockerhub,注册账号(记住用户名)

将本地仓库中所要推送的镜像打上标签

1
docker tag 本地镜像名:版本号 dockerhub用户名/镜像名:版本号

这里注意,没有必要把dockerhub用户名/镜像名:版本号这里的镜像名和版本号要填成和本地镜像名和版本号一致,此处是对外展示的,只是打上了一个其他名字和版本号的标签。

在这里我们可以使用命令看一下

1
docker images

image-20230407155043302

我们生成了一个带有标签名的镜像,而且版本号也是我们设置的版本号

将本地镜像推送到dockerhub中

1
docker push 标签镜像名:版本号

image-20230407155157777

推送成功,我们 可以去dockerhub中查看

image-20230407155236765

创建本地私有仓库并将本地镜像存入其中

(49条消息) docker搭建私有仓库_docker配置私有仓库_谬也的博客-CSDN博客

Docker 微服务实战

docker网络

清空所有环境

1
docker system prune -a  

我们可以通过在虚拟机中的命令查看虚拟网卡

image-20230407200628516

其中 ens33网卡为本机的虚拟网卡,其中的ip为虚拟机外可以访问的IP地址 lo为回环地址

image-20230407200735739

上图中的virbr0和virbr0-nic

virbr0 是 KVM 默认创建的一个 Bridge,其作用是为连接其上的虚机网卡提供 NAT 访问外网的功能。

virbr0 默认分配了一个IP 192.168.122.1,并为连接其上的其他虚拟网卡提供 DHCP 服务。

当开启docker服务后我们会发现有个叫docker0的网卡

image-20230407201649779

Docker服务默认会创建一个docker0网桥〈其上有一个docker)内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker默认指定了docker接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

基本命令

1
2
3
4
5
6
7
docker network 
#可选项 ls 列出所有网络
# connect 让一个容器连接到一个网络中
# disconnect 断开一个容器和一个网络的连接
# inspect 展示出一个或多个网络的详细信息
# prune 移除所有未被使用的网络
# rm 移除一个或多个网络

image-20230407201110469

docker network网络模式

image-20230407202036541

在容器创建时,可以通过命令设定使用什么网络连接模式 ,默认是bridge模式

1
docker run --network bridge/host/none/(contain:公用网络的容器名)

Bridge模式

  • 1、Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-lP直接通信。
  • 2、docker run的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是dockerO。在宿主机ifconfig,就可以看到docker0和自己createi的network(后面讲)eth0, eth1, eth2……表网卡一,网卡二,网卡三…… lo代表127.0.0.1,即localhost. inet addr用来表示网卡的IP地址
  • 3、网桥dockerO创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
    • 3.1整个宿主机的网桥模式都是dockerO,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair) ;
    • 3.2每个容器实例内部也有一块网卡,每个接口叫eth0;
    • 3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
  • 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。

image-20230407202848949

Container模式

新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

image-20230407210710919

注意,在进行Container模式连接时,不能在容器端口映射时,选择同一端口例如:

1
2
docker run -p 8081:8080 --network bridge --name tomcat1 tomcat
docker run -p 8082:8080 --network container:tomcat1 --name tomcat2 tomcat

此时就会报错因为二者不仅使用同一IP,而且还是用同一映射端口

image-20230407211724251

那我们就使用轻便的alpine

首先创建容器alpine1

image-20230407213044815

再创建容器alpine2

image-20230407213110233

我们可以发现二者的eth0网卡信息一致符合上图

这时我们不仅好奇,如果alpine1突然挂掉,那么alpine2会代替alpine1吗,或者说,alpine2的ip依旧不变

我们测试一下

image-20230407213405956

image-20230407213353813

此时我们发现,alpine2的eth0网卡没了。

所以contain连接,倘若实际带有网卡的容器宕机,那么通过contain连接的容器网卡也会一并消失

自定义网络

我们的容器在进行网络交互时,是否可以根据ip进行相互通信呢

1
2
docker run -it --name alpine1 alpine /bin/sh
docker run -it --name alpine2 alpine /bin/sh

alpine1 ip为:172.17.0.3

alpine2 ip为:172.17.0.4

image-20230407215004747

测试说明可以进行网络通信,如果使用容器名进行通信呢

image-20230407215059835

并不可以,但是我们使用DHCP协议,动态的生成ip,倘若服务重启后容器各自的ip都不同了,那么就会造成通信异常,所以我们要解决这种问题

所以要使用自定义网络

我们自定义了一个网络连接模式aa_network

image-20230407215435082

然后以aa_network作为网络连接模式创建了两个容器(alpine1 alpine2)

image-20230407215743038

image-20230407215819894

可以使用ip进行网络通信

image-20230407215915748

更可以使用容器名进行通信!!!!!!!!!!!!!!

image-20230407220042312

哇哦哇哦,至于内部什么原理,俺也不知道

docker Compose 容器编排

听某位大佬说(田某某),这块儿很难嘿嘿,背着他偷偷学

Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。

Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成一个应用。你需要定义一个YAML格式的配置文作docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器

安装

1
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

赋予权限

1
sudo chmod +x /usr/local/bin/docker-compose

查看版本

image-20230408162340131

安装成功

compose的核心概念

一个文件 ,两个要素

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
# Compose 版本 Version 2支持更多的指令。Version 1将来会被弃用。
version: "3"

# 定义服务
services:

# 为project定义服务
redis:
# 服务的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取镜像
image: redis:4.0
# 配置端口 - "宿主机端口:容器暴露端口"
ports:
- 6379:6379
# 配置容器连接的网络,引用顶级 networks 下的条目(就是最下面配置的networks(一级目录))
networks:
network_name:
# 为单redis创建别名, REDIS_URL标记为redis服务的地址. (不配置aliases也可以, 这样就通过定义的服务名: redis链接)
aliases:
- REDIS_URL
# 挂载
volumes:
- "/docker/redis/conf/redis.conf:/etc/redis/redis.conf"
- "/docker/redis/data:/data"
# 容器总是重新启动
restart: always
# 相当于执行一些命令
command:
redis-server /etc/redis/redis.conf --appendonly yes
# 指定一个自定义容器名称,而不是生成的默认名称。
container_name: redis
# 使用该参数,container内的root拥有真正的root权限。
privileged: true

db:
image: mysql:5.7
ports:
- 3306:3306
# 添加环境变量
environment:
MYSQL_ROOT_PASSWORD: "123456"
volumes:
- "/docker/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf"
- "/docker/mysql/logs:/var/log/mysql"
- "/docker/mysql/data:/var/lib/mysql"
- "/docker/mysql/sql/init.sql:/docker-entrypoint-initdb.d/init.sql"
- "/etc/localtime:/etc/localtime"
networks:
network_name:
aliases:
- MYSQL_URL
restart: always
command: --init-file /docker-entrypoint-initdb.d/init.sql
container_name: mysql
privileged: true

project-name:
# 服务的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取镜像
image: project-name:1.0.0
# 构建镜像
build:
# 指定项目的地址
context: /root/docker_mysql_redis
# 指定Dockerfile
dockerfile: Dockerfile
ports:
- 8080:8080
# 从文件添加环境变量
env_file:
- /root/environment.env
networks:
network_name:
aliases:
- PROJECT_URL
privileged: true
restart: always
container_name: test-name

# ........可以继续添加


networks:
# bridge:默认,需要单独配置ports映射主机port和服务的port,并且开启了容器间通信
network_name:
driver: bridge


常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker-compose -h                           # 查看帮助
docker-compose up # 启动所有docker-compose服务
docker-compose up -d # 启动所有docker-compose服务并后台运行
docker-compose down # 停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id # 进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
docker-compose ps # 展示当前docker-compose编排过的运行的所有容器
docker-compose top # 展示当前docker-compose编排过的容器进程

docker-compose logs yml里面的服务id # 查看容器输出日志
dokcer-compose config # 检查配置
dokcer-compose config -q # 检查配置,有问题才有输出
docker-compose restart # 重启服务
docker-compose start # 启动服务
docker-compose stop # 停止服务x`x