Docker学习
一款产品:开发-上线 两套环境!应用环境,应用配置!
传统的开发模式是:开发人员开发应用程序,运维来部署环境。而此种模式会带来一种问题,就是在开发人员的电脑中程序可以运行起来,而在其它地方程序无法运行。环境冲突的问题导致程序无法正常执行
而Docker的解决方法是:
- Docker的思想模式是来自于集装箱
- 在多个应用(端口冲突)–原来都是交叉的
- 隔离:Docker核心思想!打包装箱!每个箱子都是互相隔离的
- Docker通过隔离机制,可以将服务器利用到机制
本质:所有的技术都是因为出现了一些问题,我们需要去解决,才去学习
2010年,几个搞IT的年轻人,就在美国成立了一家公司dotcloud
做一些pass的云计算服务!LXC有关的容器技术!
他们将自己的技术(容器化技术)命名及时Docker!
Docker在给刚刚诞生的时候,没有引起行业的注意!dotCloud,就无法活不下去!
2013年,将Docker开源!
Docker越来越多的人发现了Docker的优点,Docker每个月都会更新一个版本
2014年4月9日,Docker1.0发布
Docker为什么这么火?十分的轻巧!
在容器技术出来之前,我们都是使用的虚拟机技术!
虚拟机:在window中装一个Vmware,通过这个软件我们可以虚拟出来一个或者多台电脑!但是比较笨重!
虚拟机也是属于虚拟化技术,Docker容器技术,也是一种虚拟化技术!
vm, Linux centos原生镜像(一个电脑) 隔离,需要开启多个虚拟机! 几个G 几分钟
docker,隔离, 镜像(最核心的环境 4m + jdk + mysql)十分的轻巧, 运行镜像就可以了!小巧!几个M KB 秒级启动!
到现在,所有的开发人员都必须要回Docker
聊聊Docker
Docker是基于GO语言开发的!开源项目!
官网:https://www.docker.com/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yu7Kymwd-1623469059437)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/image-20210514095514860.png)]
仓库地址:https://hub.docker.com/
虚拟机技术
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6bcuOFtX-1623469059440)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210514114532.png)]
虚拟机技术缺点:
容器化技术
容器化技术不是模拟的一个玩这个的操作系统
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LGbZz7ZL-1623469059443)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210514115007.png)]
比较Docker和虚拟机技术的不同
DevOps(开发、运维)
应用更快捷的交付和部署
传统:一堆的帮助文档, 安装程序
Docker:打包镜像发布测试,一键运行
更便捷的升级和扩缩容
使用了Docker之后,我们部署应用就和搭积木一样!
项目打包为一个镜像,扩展服务器A!服务器B!
更简单的系统运维
在容器化之后,我们的开发和测试的环境都是高度一致
更高效的计算资源利用
Docker是内核级别的虚拟化,可以在一个物理机上可以运行很多的容器实例!服务器的性能可以被压缩到极致。
镜像(image)
docker净吸纳过就好比是一个模板,可以通过这个模板来创建容器服务,Tomcat镜像===> run ===> Tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)
容器(container)
Docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。
启动,停止,删除,基本命令!
目前就可以把这个容器理解为就是一个简易的Linux系统
仓库(repository)
仓库就是存放镜像的地方!
仓库分为公有仓库和私有仓库
Docker Hub(默认都是国外的)
阿里云、华为云都有容器服务器(配置镜像加速)
环境准备
# 系统内核是3.10以上的
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# uname -r
3.10.0-1160.24.1.el7.x86_64
#
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# 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"
安装
帮助文档
yum -y install gcc
yum -y install gcc-c++
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
yum install -y yum-utils
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 makecache fast
yum install -y docker-ce docker-ce-cli containerd.io
systemctl start docker
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker version
Client: Docker Engine - Community
Version: 20.10.6
API version: 1.41
Go version: go1.13.15
Git commit: 370c289
Built: Fri Apr 9 22:45:33 2021
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.6
API version: 1.41 (minimum version 1.12)
Go version: go1.13.15
Git commit: 8728dd2
Built: Fri Apr 9 22:43:57 2021
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.4.4
GitCommit: 05f951a3781f4f2c1911b05e61c160e9c30eaa8e
runc:
Version: 1.0.0-rc93
GitCommit: 12644e614e25b05da6fd08a38ffa0cfe1903fdec
docker-init:
Version: 0.19.0
GitCommit: de40ad0
docker run hello-world
docker images
# 1. 卸载依赖
yum remove docker-ce docker-ce-cli containerd.io
# 2. 删除资源
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
容器镜像服务
(阿里云的内网使用)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OUdGWvPM-1623469059448)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/未命名文件.jpg)]
Docker是怎么工作的?
Docker是一个Client - Server 结构的系统, Docker的守护进程运行在主机上。通过Socket从客户端访问!
DockerServer接收到Docker-Client 的指令,就会执行这个命令!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7prKYQUK-1623469059450)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/未命名文件 (1)].jpg)
Docker为什么要比VM快
所以说,新建一个容器的时候,Docker不需要像虚拟机重新加载一个操作系统内核,避免引导。虚拟机是加载Guest OS,分钟级别启动的,而Docker是利用宿主机的操作系统的,省略了这个复杂的过程,是秒级启动的。
docker version # 显示docker的版本信息
docker info # 显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help #万能命令
帮助文档的地址:https://docs.docker.com/reference/
docker images 查看所有本地的主机上的镜像
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d1165f221234 2 months ago 13.3kB
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的ID
CREATED 镜像的创建时间
SIZE 镜像的大小
-a, --all 列出所有镜像
-q, --quiet 只显示镜像的ID
docker search 搜索命令
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10873 [OK]
mariadb MariaDB Server is a high performing open sou… 4099 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 806 [OK]
percona Percona Server is a fork of the MySQL relati… 536 [OK]
docker pull 下载镜像
# 下载镜像 dockerpull 镜像名[:tag]
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker pull mysql
Using default tag: latest # 如果不写 tag , 默认就是latest
latest: Pulling from library/mysql
69692152171a: Pull complete # 分层下载,docker image的核心 联合文件系统
1651b0be3df3: Pull complete
951da7386bc8: Pull complete
0f86c95aa242: Pull complete
37ba2d8bd4fe: Pull complete
6d278bb05e94: Pull complete
497efbd93a3e: Pull complete
f7fddf10c2c2: Pull complete
16415d159dfb: Pull complete
0e530ffc6b73: Pull complete
b0a4a1a77178: Pull complete
cd90f92aa9ef: Pull complete
Digest: sha256:d50098d7fcb25b1fcb24e2d3247cae3fc55815d64fec640dc395840f8fa80969
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真是地址
docker pull mysql
docker pull docker.io/library/mysql:latest
# 以上的两条命令是一样的
# 指定版本的镜像
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
69692152171a: Already exists
1651b0be3df3: Already exists
951da7386bc8: Already exists
0f86c95aa242: Already exists
37ba2d8bd4fe: Already exists
6d278bb05e94: Already exists
497efbd93a3e: Already exists
a023ae82eef5: Pull complete
e76c35f20ee7: Pull complete
e887524d2ef9: Pull complete
ccb65627e1c3: Pull complete
Digest: sha256:a682e3c78fc5bd941e9db080b4796c75f69a28a8cad65677c23f7a9f18ba21fa
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
docker rmi 删除镜像
# 删除指定的镜像
docker rmi -f 镜像ID
# 删除多个的镜像
docker rmi -f 镜像ID 镜像ID 镜像ID
# 删除全部的镜像
docker rmi -f $(docker images -aq)
说明:我们欧了镜像才可以创建容器,Linux,下载一个centos镜像来测试
docker pull centos
新建容器并启动
docker run [可选参数] image
# 参数说明
--name="Name" 容器名字 Tomcat01 Tomcat02 用来区分容器
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-P 指定容器的端口 -P 8080:8080
-P ip:主机端口:容器端口
-P 主机端口:容器端口
-P 容器端口
容器端口
-p 随机指定端口
# 启动并进入容器
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -it centos /bin/bash
[root@a05ab38c8636 /]# ls # 查看容器内的centos, 基本版本,有很多的命令并不完善
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
# 启动NGINX实例
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -d -p 3344:80 --name nginx02 nginx
bc6810eee3480b464733575829a48b4d9a7e573697dc25f74934db0598ed989b
# 从容器中退出
[root@a05ab38c8636 /]# exit
exit
列出所有运行中的容器
# docker ps 命令
-a # 累出当前正在运行的容器+历史运行的容器
-n=? # 最近创建的容器
-q # 只显示容器的编号
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a05ab38c8636 centos "/bin/bash" 3 minutes ago Exited (0) 51 seconds ago hopeful_darwin
3059f7efd070 hello-world "/hello" 2 hours ago Exited (0) 2 hours ago vigilant_rubin
[root@iZwz94tkn42l6pe1uu1mqiZ ~]#
退出容器
exit # 直接容器停止并退出
Ctrl + p + q # 容器不停止退出
删除容器
docker rm 容器ID # 删除指定的容器, 不能删除正在运行的容器,如果要强制删除, rm -f
docker rm -r $(docker ps -aq) # 删除全部的容器
docker ps -a -q|xargs docker rm # 删除所有的容器
启动和停止容器的操作
docker start 容器ID # 启动容器
docker restart 容器ID # 重启容器
docker stop 容器ID # 停止当前正在运行容器
docker kill 容器ID # 轻质停止当前容器
后台启动容器
# 命令 docker run -d 精明名
docker run -d centos
常见的坑,docker容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
NGINX,容器启动后,发现自己没有提供服务,就会立刻停止
查看日志
docker logs -f -t --tail 容器
# 显示日志
-tf #显示日志
--tial number # 要显示日志的条数
docker logs -tf --tail 10 300e315adb2f
查看容器中的进程信息 PS
# docker top 容器ID
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker top 3565dac1ec32
UID PID PPID C STIME TTY
root 13848 13828 0 15:59 ?
root 13921 13848 0 15:59 ?
查看镜像的元数据
# docker inspect 容器ID
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker inspect 3565dac1ec32
[
{
"Id": "3565dac1ec32c34dbcdc651c538238e36a52e53fef729da7876aca893c5ae8c7",
"Created": "2021-05-14T07:59:30.176021788Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true;do echo aa; sleep 1; done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 13848,
"ExitCode": 0,
"Error": "",
"StartedAt": "2021-05-14T07:59:30.553009964Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55",
"ResolvConfPath": "/var/lib/docker/containers/3565dac1ec32c34dbcdc651c538238e36a52e53fef729da7876aca893c5ae8c7/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/3565dac1ec32c34dbcdc651c538238e36a52e53fef729da7876aca893c5ae8c7/hostname",
"HostsPath": "/var/lib/docker/containers/3565dac1ec32c34dbcdc651c538238e36a52e53fef729da7876aca893c5ae8c7/hosts",
"LogPath": "/var/lib/docker/containers/3565dac1ec32c34dbcdc651c538238e36a52e53fef729da7876aca893c5ae8c7/3565dac1ec32c34dbcdc651c538238e36a52e53fef729da7876aca893c5ae8c7-json.log",
"Name": "/peaceful_einstein",
"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,
"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",
"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/da103020cf947821a9b717c70b556c0b2658ddc3e14af9585242965f1038c7b6-init/diff:/var/lib/docker/overlay2/7ae992f7b556a66ecbc14cc40e0df81da30ac28feffa45999a4e389d0e68bfed/diff",
"MergedDir": "/var/lib/docker/overlay2/da103020cf947821a9b717c70b556c0b2658ddc3e14af9585242965f1038c7b6/merged",
"UpperDir": "/var/lib/docker/overlay2/da103020cf947821a9b717c70b556c0b2658ddc3e14af9585242965f1038c7b6/diff",
"WorkDir": "/var/lib/docker/overlay2/da103020cf947821a9b717c70b556c0b2658ddc3e14af9585242965f1038c7b6/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "3565dac1ec32",
"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 aa; sleep 1; done"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20201204",
"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": "dca9b4cec16049ee9eea3ef3f69832c54a4bbe5e07bc92d1716f07924b748dbd",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
},
"SandboxKey": "/var/run/docker/netns/dca9b4cec160",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "9b128f7836259194f4ef26a30d52141755432956388c6b8f69916a593f6bc808",
"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": "83e597ad26efff30e939cf0d4300ea418aae59880a638c6153cec986c5d663dc",
"EndpointID": "9b128f7836259194f4ef26a30d52141755432956388c6b8f69916a593f6bc808",
"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
}
}
}
}
]
进入当前正在运行的容器
# 我们通常容器都是使用的后台方式运行的,需要进入容器,修改一些配置
docker exec -it 容器ID /bin/bash
docker attach 容器ID
# docker exec # 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker attach # 进入容器正在执行的终端,不会启动新的进程
从容器内拷贝文件到主机上
# docker cp 容器ID:容器文件的地址 宿主机的地址
docker cp 4d9d244f926f:/home/aa.java /home
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
什么portainer
Docker图形化界面管理工具!提供一个后台面板供我们操作
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
访问测试:外网:8088
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件
所有的应用,直接打包Docker镜像,就可以直接跑起来
如何得到镜像
UnionFS(联合文件系统)
我们下载的时候看到的一层层的就是这个
UnionFS(联合文件系统):Union 文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改,作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承(没有父镜像),可以制作各种具体的应用镜像
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
Docker的镜像实际上是由一层一层的文件系统组成,这种层级的文件系统UnionFS
bootfs(boot file system)主要包括bootloader和kernel,bootloader主要就是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs,这一层与我们典型的Linux/Unix系统是一样的,包括boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件,rootfs就是各种不同的操作系统的发行版,比如Ubuntu,centos等等
平时我们安装进虚拟机的centos系统都有几个GB,为什么docker才200MB
对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令、工具和程序库就可以了,因为底层直接用host和kernel,自己值需要童工rootfs就可以了,由此可见对于不同Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs
虚拟机是分钟级别,容器时秒级
分层的镜像
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
69692152171a: Already exists
a4a46f2fd7e0: Pull complete
bcdf6fddc3bd: Pull complete
b7e9b50900cc: Pull complete
5f3030c50d85: Pull complete
63dae8e0776c: Pull complete
Digest: sha256:365eddf64356169aa0cbfbeaf928eb80762de3cc364402e7653532bcec912973
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
为什么Docker要采用这种分层的结构
最大的好处,莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以别共享
查看镜像分层的方式可以通过 docker image inspect
命令
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker image inspect redis
[
{
"Id": "sha256:bc8d70f9ef6cae366eb6423fcb4699597c5a6b99126f3ced35b6b9d6d134375b",
"RepoTags": [
"redis:latest"
],
"RepoDigests": [
"redis@sha256:365eddf64356169aa0cbfbeaf928eb80762de3cc364402e7653532bcec912973"
],
"Parent": "",
"Comment": "",
"Created": "2021-05-12T19:07:40.989268815Z",
"Container": "7f80d5e2c33e07cfd43daf9e5b15825833a69ff09cf999c562b325c5f2b1a177",
"ContainerConfig": {
"Hostname": "7f80d5e2c33e",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"6379/tcp": {
}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"REDIS_VERSION=6.2.3",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.3.tar.gz",
"REDIS_DOWNLOAD_SHA=98ed7d532b5e9671f5df0825bb71f0f37483a16546364049384c63db8764512b"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"redis-server\"]"
],
"Image": "sha256:c55155952d103fdae7acc2a5d613050c83fed8710409072bf456ed45ac1f9ee5",
"Volumes": {
"/data": {
}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
}
},
"DockerVersion": "19.03.12",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"6379/tcp": {
}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"REDIS_VERSION=6.2.3",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.3.tar.gz",
"REDIS_DOWNLOAD_SHA=98ed7d532b5e9671f5df0825bb71f0f37483a16546364049384c63db8764512b"
],
"Cmd": [
"redis-server"
],
"Image": "sha256:c55155952d103fdae7acc2a5d613050c83fed8710409072bf456ed45ac1f9ee5",
"Volumes": {
"/data": {
}
},
"WorkingDir": "/data",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 105384748,
"VirtualSize": 105384748,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/a3d262fe90e54560c0b13f2cb515d554c056dcf660af5ffa87802e91b9a73eb8/diff:/var/lib/docker/overlay2/ac6227ff51b65b18a0cac529ad1318d43d01fc39d14f5b45b34f4326e961cc87/diff:/var/lib/docker/overlay2/fae797250cd99c97520e32ffd7e8c08c9092a60f1b3a5c5dd308b64b4aa33c4f/diff:/var/lib/docker/overlay2/987c2f138efbd62bb4e7998112459177a0840c003fa8d208ad3bb2e2c94f0f04/diff:/var/lib/docker/overlay2/11a41b7bf24ea9dbd4b6a309aae1c5fdc6c484de958fa076a48b8e585caa83de/diff",
"MergedDir": "/var/lib/docker/overlay2/f881db1dd4a4f465829fbaa43a77f4f078e22e1d16bebed707490cd80d0c7650/merged",
"UpperDir": "/var/lib/docker/overlay2/f881db1dd4a4f465829fbaa43a77f4f078e22e1d16bebed707490cd80d0c7650/diff",
"WorkDir": "/var/lib/docker/overlay2/f881db1dd4a4f465829fbaa43a77f4f078e22e1d16bebed707490cd80d0c7650/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:02c055ef67f5904019f43a41ea5f099996d8e7633749b6e606c400526b2c4b33",
"sha256:ec5652c3523d96657d66169c0eb71b572ff065711c705a15ec02f60a21c212c3",
"sha256:76d3e24d63f60e6a73af70be15959eb4021dd7a5a09da6925037d3b4a1673fca",
"sha256:f281464c05be6822b95403339e2b7744fd1664e88ee9d118c7e89436ab094d58",
"sha256:7fde79e38c038ef46904196710861e2c556363d723b8f8bf9b00369944d36aec",
"sha256:6d4185a1708b677241d83683283c76703e788e41a2341b7c1bf4dbf12aebab45"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
理解:
所有的Docker镜像都起始于一个基础镜像层,当进行或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单地例子,假如基于Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包。
该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自镜像层的6个文件
上图中的镜像层跟之前的略有区别,主要目的是便于展示文件
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层的文件7是文件5的一个更新版本。
这种情况下,上层镜像中的文件覆盖了底层镜像层中的文件。这样就是的文件的更新版本作为一个新镜像层添加到镜像当中
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为同一的文件系统
Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点
Docker在windows上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1].
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图
特点
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们说的容器层,容器之下的都叫镜像层!
docker commit 提交容器称为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器ID 目标镜像名:[tag]
docker的理念问题
将应用和环境打包成一个镜像!
数据?如果数据都在容器中,那么我们容器删除,数据就是丢失!需求:数据可以持久化
对于MySQL来说,容器删了,等于删库,需求:MySQL数据可以存储在本地!
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术,说白了,就是目录的挂载,将我们容器内的目录,挂载到Linux上面!
总结一句话,就是为了容器的持久化和同步操作!容器间也是可以数据共享的
方式一:直接使用命令来挂载 -v
docker run -it -v 主机目录:容器内目录
# 挂载NGINX的静态HTML文件
[root@iZwz94tkn42l6pe1uu1mqiZ home]# docker run -d --name nginx -p 3344:80 -v /home/:/usr/share/nginx/html nginx
7c49e254e17291c5c364af967329677b933015bace6911a8e048e355de8534b2
好处:我们以后修改只需要在本地修改即可,容器内会自动同步!
思考:MySQL的数据持久化的问题
# 获取镜像
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# 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@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -d -p 3310: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
匿名挂载
-v 容器内路径!
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -d -p --name nginx01 -v /etc/nginx nginx
# 查看说是有的volume的情况
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker volume ls
DRIVER VOLUME NAME
local 50e376e28038ef4d45c9c7b61be2fa85984577973cc150dc84ad1d9cae07425d
这种就是匿名挂载,我们在 -v 只写了容器内的路径,没有写容器外的路径!
具名挂载
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx nginx
64dc774d7bb835447e0911327f54fc3c1761a4075dd5749d9b6f31592e02486c
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker volume ls
DRIVER VOLUME NAME
local juming-nginx
# 通过 -v 卷名:容器内路径
# 查看一下这个卷
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker volume inspect juming-nginx
[
{
"CreatedAt": "2021-05-15T09:41:07+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/xxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用的
具名挂载
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机的路径:容器内路径 # 指定路径股灾
拓展:
# 通过 -v 容器内领: ro | rw 改变读写权限
ro readonly # 只读
rw readwrite # 可读可写
# 一旦设定了容器的权限,容器对我们挂载出来的内容就有限定了
docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx:rw nginx
# ro 只要看到了 ro 就说明了这个路径只能通过宿主机来操作,容器内都是无法操作
Dockerfile就是用来构建docker镜像的构建文件!命令脚本
通过这个脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,每一个命令都是一层!
# 创建一个dockerfile文件,名字可以随机,建议Dockerfile
# 文件中的内容 指令(大写)参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "-------end-------"
CMD /bin/bash
# 这里的每个命令,就是镜像的一层
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker build -f dockerfile1 -t aa/centos:1.0 .
Sending build context to Docker daemon 57.34kB
Step 1/4 : FROM centos
latest: Pulling from library/centos
7a0437f04f83: Pull complete
Digest: sha256:5528e8b1b1719d34604c87e11dcd1c0a20bedf46e83b5632cdeac91b8c04efc1
Status: Downloaded newer image for centos:latest
---> 300e315adb2f
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in e729d405d7e1
Removing intermediate container e729d405d7e1
---> 9cb7e78cee19
Step 3/4 : CMD echo "-------end-------"
---> Running in a92b012924fc
Removing intermediate container a92b012924fc
---> df21671f4490
Step 4/4 : CMD /bin/bash
---> Running in 070dcd4c32ce
Removing intermediate container 070dcd4c32ce
---> 9a0c5e69eca5
Successfully built 9a0c5e69eca5
Successfully tagged aa/centos:1.0
启动自己的容器
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -it 9a0c5e69eca5 /bin/bash
[root@9b26d4a1598d /]# ls -l
total 56
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 360 May 15 02:16 dev
drwxr-xr-x 1 root root 4096 May 15 02:16 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 4096 Dec 4 17:37 lost+found
drwxr-xr-x 2 root root 4096 Nov 3 2020 media
drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt
drwxr-xr-x 2 root root 4096 Nov 3 2020 opt
dr-xr-xr-x 93 root root 0 May 15 02:16 proc
dr-xr-x--- 2 root root 4096 Dec 4 17:37 root
drwxr-xr-x 11 root root 4096 Dec 4 17:37 run
lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Nov 3 2020 srv
dr-xr-xr-x 13 root root 0 May 15 02:16 sys
drwxrwxrwt 7 root root 4096 Dec 4 17:37 tmp
drwxr-xr-x 12 root root 4096 Dec 4 17:37 usr
drwxr-xr-x 20 root root 4096 Dec 4 17:37 var
drwxr-xr-x 2 root root 4096 May 15 02:16 volume01
drwxr-xr-x 2 root root 4096 May 15 02:16 volume02
drwxr-xr-x 2 root root 4096 May 15 02:16 volume01
drwxr-xr-x 2 root root 4096 May 15 02:16 volume02
这个卷和外部一定有一个同步的目录
FROM centos
VOLUME ["volume01","volume02"] # 匿名挂载
CMD echo "-------end-------"
CMD /bin/bash
查看一下卷挂载的路径
docker inspect 容器ID
"Mounts": [
{
"Type": "volume",
"Name": "b002d80bb17e6729578528ae73d124664d45f3619e1ae3baa2aef0b1eae28ab7",
"Source": "/var/lib/docker/volumes/b002d80bb17e6729578528ae73d124664d45f3619e1ae3baa2aef0b1eae28ab7/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "7a6846ab080bf2e6b0ffb9117a2b50819dc76b396f66843a48f3efc0a31d1106",
"Source": "/var/lib/docker/volumes/7a6846ab080bf2e6b0ffb9117a2b50819dc76b396f66843a48f3efc0a31d1106/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHyQXZr5-1623469059452)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/1621049134.jpg)]
# 启动3个容器,通过我们刚才自己写的镜像启动
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -it --name docker01 9a0c5e69eca5
[root@39b85ea978ac /]# ls
bin etc lib lost+found mnt proc run srv tmp var volume02
dev home lib64 media opt root sbin sys usr volume01
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -it --volumes-from docker01 --name docker02 9a0c5e69eca5
[root@39b85ea978ac /]# ls
bin etc lib lost+found mnt proc run srv tmp var volume02
dev home lib64 media opt root sbin sys usr volume01
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -it --volumes-from docker01 --name docker03 9a0c5e69eca5
[root@39b85ea978ac /]# ls
bin etc lib lost+found mnt proc run srv tmp var volume02
dev home lib64 media opt root sbin sys usr volume01
# 测试:可以大会上哪壶docker01 ,查看一下docke02 和docker03是否还可以访问这个文件
# 测试依旧可以访问
多个MySQL同步数据
docker run -d -p 3310: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
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --volumes-from mysql01 --name mysql01 mysql:5.7
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --volumes-from mysql01 --name mysql01 mysql:5.7
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止
但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的
dockerfile是用来构建docker镜像的文件!命令参数脚本!
构建步骤:
基础知识
每个保留关键字(指令)都是必须是大写字母
执行从上到下顺序执行
#
表示注释
每一个指令都会创建提交一个新的镜像层,并提交!
dockerfile是面向开发的,我们以后要发布项目的,做镜像,就需要编写dockerfile文件,这个文件十分简单!
Docker镜像逐渐成为企业的交付标准,必须要掌握!
步骤:开发,部署,运维 --------> 缺一不可
DockerFile:构建文件,定义了一切的步骤,相当于源代码
DockerImages:通过DockerFile 构建生成的镜像,最终发布和运行的产品
Docker容器:容器就是镜像运行起来提供服务器
指令 | 注释 |
---|---|
FROM | 基础镜像,一切从这里开始构建 |
MAINTAINER | 镜像是谁写的,姓名+邮箱 |
RUN | 镜像构建的时候需要运行的命令 |
ADD | 步骤,Tomcat镜像,这个Tomcat压缩包!添加内容 |
WORKDIR | 镜像的工作目录 |
VOLUME | 挂载的目录 |
EXPOSE | 暴露端口配置 |
CMD | 制定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代 |
ENTRYPOINT | 指定这个容器启动的时候呀运行的命令,可以追加命令 |
ONBUILD | 当构建一个被继承 DockerFile 这个时候就会运行 ONBUILD 的指令,触发指令 |
COPY | 类似 ADD 将我们文件拷贝到镜像中 |
ENV | 构建的时候设置环境变量 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a96oOqhv-1623469059455)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515121616.png)]
列出本地进行的变更历史
# docker history 镜像ID
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker history 9a0c5e69eca5
IMAGE CREATED CREATED BY SIZE COMMENT
9a0c5e69eca5 3 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
df21671f4490 3 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
9cb7e78cee19 3 hours ago /bin/sh -c #(nop) VOLUME [volume01 volume02] 0B
300e315adb2f 5 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 5 months ago /bin/sh -c #(nop) ADD file:bd7a2aed6ede423b7… 209MB
CMD和ENTRYPOINT的区别
CMD # 制定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候呀运行的命令,可以追加命令
CMD
# 编写 dockerfile 文件
FROM centos
CMD ["ls", "-a"]
# 构建镜像
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker build -f cmd-test -t cmdtest .
# run 运行, 发现我们的ls -a 生效了
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run df0caba05a52
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
# 追加一个命令 -l 期待的是 ls -al
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run df0caba05a52 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec: "-l": executable file not found in $PATH: unknown.
# CMD 的情况下, -l 替换了 ["ls", "-a"]的命令, -l 不是一个命令所以报错
ENTRYPOINT
# 编写 dockerfile 文件
FROM centos
ENTRYPOINT ["ls", "-a"]
# 构建镜像
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker build -f dockerfile-cmd-entorypoint -t entrypoint-text .
# run 运行, 发现我们的ls -a 生效了
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run 5aa6363fa5f1
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
# 我们追加一个命令是直接在我们的 ENTRYPOINT 命令的后面
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run 5aa6363fa5f1 -l
total 56
drwxr-xr-x 1 root root 4096 May 15 05:47 .
drwxr-xr-x 1 root root 4096 May 15 05:47 ..
-rwxr-xr-x 1 root root 0 May 15 05:47 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 May 15 05:47 dev
drwxr-xr-x 1 root root 4096 May 15 05:47 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
DockerFile中很多的命令都十分的相似,我们需要了解它们的区别,我们最好的学习就是对比他们然后测试效果!
准备镜像文件 Tomcat 压缩包,JDK压缩包
[root@iZwz94tkn42l6pe1uu1mqiZ sort]# ls -al
total 200732
drwxr-xr-x 2 root root 4096 May 15 14:16 .
dr-xr-x---. 7 root root 4096 May 15 14:07 ..
-rw-r--r-- 1 root root 11496577 May 9 02:06 apache-tomcat-9.0.46.tar.gz
-rw-r--r-- 1 root root 194042837 May 15 14:16 jdk-8u202-linux-x64.tar.gz
编写dockerfile文件,官方命名 Dockerfile
,build 会自动寻找这个文件,就不需要 -f 指定了
FROM centos
MAINTAINER 稻草人<3020139699@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u202-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.46.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_202
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.46
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.46
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib;$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.46/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.46/bin/logs/catalina.out
构建镜像
docker build -t diytomcat .
我们以后开发的步骤,需要掌握Dockerfile的编写!我们之后的一切都是使用Docker镜像来发布运行!
DockerHub
地址: https://hub.docker.com/ 需要有自己的账号
在我们服务器上提交自己的镜像
[root@iZwz94tkn42l6pe1uu1mqiZ sort]# 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
登录完毕后就可以提交镜像了,就是一步 docker push
# docker push 作者名/镜像名:tag标签
# 示例
docker push daocaoren/diytomcat:1.0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKAzf1dg-1623469059456)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515163733.png)]
三个网络代表三个不同的环境
# docker 是如何处理容器网络访问的?
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -d -P --name tomcat01 tomcat
# 查看容器的内部网络地址 ip addr
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# 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
70: eth0@if71: <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@iZwz94tkn42l6pe1uu1mqiZ ~]# 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.090 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.061 ms
# Linux是可以ping通 docker 容器内部
原理
我们每启动一个 docke 容器,docker 就会给 docker 容器分配一个IP,我们只要安装了docker,就会有一个网卡 docker0
桥接模式,使用的技术是 evth-pair
技术
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zecVOVgk-1623469059457)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515165024.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Uzpqnoh-1623469059458)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515165217.png)]
我们发现这个容器带来网络,都是一对对的
evth-pair
就是一对的虚拟设备接口,它们都是成对出现的,一端连着协议,一端彼此相连正因为有这个特性,
evth-pair
充当一个桥梁,链接各种虚拟网络设备的OpenStac, Docker容器之间的连接, OVS的连接, 都是使用
evth-pair
技术
测试不同的容器之间是否可以ping通
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker exec -it tomcat01 ping 172.17.0.3
# 结论,容器和容器之间是可以互相ping通的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rQsZQKCV-1623469059460)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/1621049135.jpg)]
结论:
所有的不同容器都是公用的一个路由器,docker0
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用IP
小结:
Docker 使用的是Linux的桥接, 宿主机中是一个Docker容器的网桥 docker0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FuokTyJv-1623469059461)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/1621049136.jpg)]
Docker中的所有的网络接口都是虚拟的,虚拟的抓发效率较高(内网传递文件)
只要容器被删除了, 那么与之相对应的网络就没了
思考一个场景,我们编写了一个微服务,database url=ip,项目不重启,数据库IP换掉了,我们希望可以处理这个问题,可以使用名字来进行访问容器?
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker exec -it tomcat02 ping comcat01
ping: comcat01: Name or service not known
# 如何解决这个问题
# 通过--link 就可以解决这个问题
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
d64b68844a1da24ef8c3195dd3e6c06a0a2e183b4b5a3e68c02ad0eaa3ea5452
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# 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.122 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.096 ms
--link
其实就是在host文件中添加了一个地址的解析不建议使用
而默认的docker0 不支持容器名连接访问
查看所有的docker网络
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
83e597ad26ef bridge bridge local
c126af0d2f76 host host local
83f0315e65a6 none null local
网络模式
网络名词 | 说明 |
---|---|
bridge | 桥接 docker(默认,自己创建的也使用bridge模式) |
none | 不配置网络 |
host | 和宿主机共享网络 |
container | 容器内网络连通(用得少,局限很大) |
测试
# 我们直接启动的命令 --net bridge 在这个就是我们的docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat
# docker0特点,默认域名不能访问 --link可以打通链接
我们可以自定义一个网络
# --driver 桥接
# --subnet 子网地址
# --gateway 网关
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
d4c462502c5eb5b9dac22f19e323920ac979f2acc773bb094afc311786bc69d9
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
83e597ad26ef bridge bridge local
c126af0d2f76 host host local
d4c462502c5e mynet bridge local
83f0315e65a6 none null local
我们自己的网络
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "d4c462502c5eb5b9dac22f19e323920ac979f2acc773bb094afc311786bc69d9",
"Created": "2021-05-15T17:54:19.860356594+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": {
},
"Options": {
},
"Labels": {
}
}
]
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "d4c462502c5eb5b9dac22f19e323920ac979f2acc773bb094afc311786bc69d9",
"Created": "2021-05-15T17:54:19.860356594+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": {
"1f8a1a02d2fdec9aef59ae85fa4a9fa8b87ce53967be2be108c9b40a991088c2": {
"Name": "tomcat01",
"EndpointID": "eb29f2cd24ec1df8f35edf049a60316da7bbed9c2b1c54b63b20b74befeb0eac",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"ec2aeb7840d203896373189036a909e424ff1d4a3d4069a37afb6042a9d3abc2": {
"Name": "tomcat02",
"EndpointID": "bb2164e0e5dcaa171e7ebecf4df69d2fabdf17997629e785d53c6e290a1d2aae",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {
},
"Labels": {
}
}
]
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker exec -it tomcat01 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.124 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.077 ms
^C
--- 192.168.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.077/0.100/0.124/0.025 ms
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker exec -it tomcat01 ping tomcat02
PING tomcat02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.082 ms
64 bytes from tomcat02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.096 ms
^C
--- tomcat02 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.058/0.078/0.096/0.018 ms
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvJGGetr-1623469059463)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515180852.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4pzdMAr-1623469059465)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515181001.png)]
测试打通 Tomcat01 到mynet
# 将一个 tomcat01 加入到 mynet 的网络中
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network connect mynet tomcat01
# 检查 mynet 的网络, 发现将 tomcat01 的容器加入直接放到了这个网络中了
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "d4c462502c5eb5b9dac22f19e323920ac979f2acc773bb094afc311786bc69d9",
"Created": "2021-05-15T17:54:19.860356594+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": {
"650e741fac935b4163eb8a005c326855a881c377d87679fa453e74139c399d0d": {
"Name": "tomcat-net-01",
"EndpointID": "3821c1b908f2029b648bc3c78d660201245bf28bbdc6dda3ea86a1f4dc9296ef",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"d41c1e63421c3df4e983b5817109c0c4e8775ddf7505b381c9866b85fb01e645": {
"Name": "tomcat01",
"EndpointID": "405c3c313f44c0d7258bb1f757bcc9453fcb3a319d01d2e42bd1530bacbe947e",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {
},
"Labels": {
}
}
]
# 测试是否可以ping通
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker exec -it tomcat-net-01 ping tomcat01
PING tomcat01 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat01.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from tomcat01.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.093 ms
# 创建网络
[root@iZwz94tkn42l6pe1uu1mqiZ ~]# docker network create redis --driver bridge --subnet 172.38.0.0/16
# 通过shell脚本创建六个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-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 通过shell脚本启动容器
for port in $(seq 1 6); \
do \
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} -v /mydata/redis/node-${port}/data:/data -v /mydata/redsi/node-${port}/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.38.0.1${port} redis redis-server /etc/redis/redis.conf
done
Docker
DockerFile build run 手动操作,单个容器
微服务。100个微服务!依赖关系。
Docker Compose 来轻松高效的管理容器。定义运行多个容器
三个步骤
Using Compose is basically a three-step process:
Dockerfile
so it can be reproduced anywhere.
docker-compose.yml
so they can be run together in an isolated environment.
docker compose up
and the Docker compose command starts and runs your entire app. You can alternatively run docker-compose up
using the docker-compose binary.
作用:批量容器编排
Compose 是 Docker 官方的开源项目,需要安装
Dockerfile
让程序咋任何地方运行。web服务,Redis、MySQL、NGINX等等多个容器
Compose
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {
}
Compose:重要的概念
下载
curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
授权
sudo chmod +x /usr/local/bin/docker-compose
体验
官方示例:https://docs.docker.com/compose/gettingstarted/
step 1
mkdir composetest
cd composetest
创建app.py文件
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
创建requirements.txt文件
flask
redis
step2:创建Dockerfile文件
# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["python", "app.py"]
step3:创建docker-compose.yml文件
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
step4:构建应用
docker-compose up
流程:
Docker-compose.yaml
Creating composetest_web_1 … done
Creating composetest_redis_1 … done
默认的服务名 文件名 _ 服务名 _ num
未来可能有多个服务器,集群
网络规则
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dwkLuHTH-1623469059466)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515224839.png)]
例如:10个服务 => 项目(项目中的内容都在同个网络下。可以进行域名访问)
例如: mysql:3306
10个容器实例都是MySQL
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHFYe1EA-1623469059468)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515225226.png)]
如果在同一个网络下,我们可以直接通过域名访问
停止: docker-compose down
Docker小结
yaml规则
docker-compose.yaml 是核心
官网地址:https://docs.docker.com/compose/compose-file/compose-file-v3/
version: "" # 版本
services: # 服务
服务1:web
服务2:Redis
项目依赖的启动顺序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S05cwzs8-1623469059469)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210515231207.png)]
项目要重新部署打包
docker-compose up --build #重新部署打包
4台阿里云服务器,建议使用按量付费,1核2G的配置
Docker Engine 1.12 introduces swarm mode that enables you to create a cluster of one or more Docker Engines called a swarm. A swarm consists of one or more nodes: physical or virtual machines running Docker Engine 1.12 or later in swarm mode.
There are two types of nodes: managers and workers.
If you haven’t already, read through the swarm mode overview and key concepts.
docker swarm --help
[root@iZ2vc1rk4i9i8i1z7q92wxZ ~]# docker swarm --help
Usage: docker swarm COMMAND
Manage Swarm
Commands:
ca Display and rotate the root CA
init Initialize a swarm
join Join a swarm as a node and/or manager
join-token Manage join tokens
leave Leave the swarm
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm
Run 'docker swarm COMMAND --help' for more information on a command.
docker swarm init
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJJ3Fy4Q-1623469059472)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516093222.png)]
docker swarm join 加入一个节点
# 获取令牌
docker swarm join-token manager # 角色1
docker swarm join-token worker # 角色2
# 通过令牌生成一个worker 节点
# docker swarm join-token worker
[root@iZ2vc1rk4i9i8i1z7q92wzZ ~]# docker swarm join --token SWMTKN-1-3xy7x0ygn751grv4ierl11c3ktq1v6y41744vaukkco88x608r-15fufl7g1kjuyhgk5f8h7pngt 172.22.9.93:2377
This node joined a swarm as a worker.
[root@iZ2vc1rk4i9i8i1z7q92wxZ ~]# docker swarm join --token SWMTKN-1-3xy7x0ygn751grv4ierl11c3ktq1v6y41744vaukkco88x608r-9eqp8d46za5gx07bnri6aci0g 172.22.9.93:2377
This node joined a swarm as a manager.
集群节点要保证manager节点为单数,至少是3个
保证大多数节点存活,才可以使用,高可用!
当前共有两个manager节点,关闭了一台服务器后,直接报错
[root@iZ2vc1rk4i9i8i1z7q92wxZ ~]# docker node ls
Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
重启manager节点服务器后,集群有重新选举了一个新的leader
重选前的leader
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-atzkn3Do-1623469059473)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516102003.png)]
重选后的leader
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tFwiWhAr-1623469059475)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516101818.png)]
移除一个worker节点后,集群服务继续存活
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOySPeXQ-1623469059476)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516102221.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YiJwz5pL-1623469059477)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516102305.png)]
我们可以将此移除的worker节点加入到manager节点,此时我们已经有了3台manager管理节点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zL0shQ5R-1623469059479)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516102512.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lZOLNaEf-1623469059480)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516102546.png)]
弹性、扩缩容、进群
以后告别docker run
docker-compose up 启动一个项目,单机
集群:swarm docker service
容器 => 服务
容器 => 服务 => 副本
容器 => 服务 => 副本(同时开启10个Redis容器)
体验
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XR9Ea7NB-1623469059481)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516103705.png)]
灰度发布 = 金丝雀发布
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FPi4sxJX-1623469059483)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516103938.png)]
docker run # 容器启动, 不具有扩缩容器
docker service # 服务,具有扩缩容器,滚动更新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2fJv7ZVV-1623469059485)(https://raw.githubusercontent.com/zhusli2013you/image/master/data/20210516104645.png)]
swarm
集群的管理和编排,docker可以初始化一个swarm集群,其他节点可以加入,(管理、工作者)
Node
就是一个docker节点。多个节点就组成了一个网络集群(管理、工作者)
Service
任务,可以在管理节点或者工作节点来运行,核心
Task
容器内的命令
CI\CD Jenkins
文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout
文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件
文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"
文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules
文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure
文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板
文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server
文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d
文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c
文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...
文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy
文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos