尚硅谷云原生学习笔记(1-75集)
尚硅谷云原生学习笔记
笔记列表:
目录
- 1、什么是云计算
- 2、云计算平台的分类理解
- 3、云平台的优缺点
- 4、服务的架构变革
- 5、技术的变革
- 6、提问解答
- 7、完整云原生平台的基础技术量
- 8、应用的12要素
- 9、云原生的其他概念
- 10、云原生的官方指导学习路线
- 11、云原生课程的疑问
- 12、Docker的底层容器隔离机制
- 13、Docker安装完成
- 14、Docker镜像加速配置
- 15、Docker镜像的一些概念
- 16、Docker镜像的一些其他操作
- 17、Docker其他的一些命令
- 18、Docker的进入容器细节
- 19、Docker cp命令
- 20、Docker镜像的推送等操作
- 21、Docker镜像的转移操作(导入、导出)
- 22、Docker容器长久运行之道
- 23、Docker如何构建镜像
- 24、如何下载做实验
- 25、镜像如何在底层存储
- 26、容器与镜像的写时复制技术
- 27、Docker的overlayFS存储驱动原理
- 28、Docker数据卷挂载的三种方式
- 29、Docker的-v挂载测试
- 30、Docker的卷管理
- 31、Docker的可视化界面
- 32、最基本Dockerfile构建镜像
- 33、Dockerfile怎么写—1
- 34、ARG—指令
- 35、ENV指令
- 36、ENV的持久化问题
- 37、ADD与COPY指令
- 38、COPY的文件可以改变目录
- 39、WORKDIR的应用
- 40、USER配合COPY进行权限的控制
- 41、VOLUME的坑
- 42、EXPOSE暴露端口
- 43、CMD、ENTRYPOINT容器启动指令
- 44、多阶段构建
- 45、开通云服务器
- 46、Dockerfile的springboot应用写法
- 47、Docker运行期间的一些问题延伸
- 48、桥接网络的原理
- 49、--link来连接容器
- 50、docker自定义网络原理
- 51、docker-compose简单安装
- 52、compose以及集群模式
- 53、青云需要创建vpc
- 54、子网掩码
- 55、青云开通服务器集群
- 56、CICD
- 57、CICD的指导实现
- 58、jenkins简介与安装
- 59、jenkins安装完成
- 60、再绑一个公网IP
- 61、创建git项目和gitee建立连接
- 62、jenkins文件的结构
- 63、jenkins步骤生成器与环境变量
- 64、jenkins其他简单的设置
- 65、jenkins环境检查
- 66、gitee远程触发jenkins自动构建
- 67、Jenkins插件安装
- 68、使用基础网络
- 69、jenkins插件安装
- 70、自定义maven代理,使用自定义配置文件
- 71、docker maven完全加速起来
- 72、简单Jenkins流水线完成
- 73、发送邮件通知
- 74、CICD还能做什么?
- 75、其他问题
1、什么是云计算
1.1、互联网时代的历程
1.2、云计算到底是什么
2、云计算平台的分类理解
2.1、云计算历程
- CNCF官网:https://www.cncf.io
2.2、云计算服务名称
- IaaS:比如华为服务器等
- PaaS:比如阿里云等
- SaaS:比如微盟、金蝶等
- CaaS:比如QQ安装包
3、云平台的优缺点
4、服务的架构变革
4.1、体系变革
4.2、架构变革
4.2.1、单体架构
4.2.2、集群架构
4.2.3、分布式架构
4.2.4、微服务架构
4.2.5、网格化架构
5、技术的变革
5.1、云上挑战
5.2、技术变革
- CNCF云原生蓝图:https://landscape.cncf.io
6、提问解答
无
7、完整云原生平台的基础技术量
7.1、云原生的生态系统
7.2、完整云原生平台基础研究量
8、应用的12要素
9、云原生的其他概念
9.1、常用术语
文档链接: https://kdocs.cn/l/cshYnro61lmQ
官方链接: http://www.cloudfoundry.cn/cloud-native-glossary/
文档截图:
9.2、云原生官方定义
链接: https://github.com/cncf/toc/blob/main/DEFINITION.md
中文版本截图:
10、云原生的官方指导学习路线
官方链接(貌似不能访问):
https://raw.githubusercontent.com/cncf/trailmap/master/CNCF_TrailMap_latest.png
11、云原生课程的疑问
无
12、Docker的底层容器隔离机制
12.1、Docker架构
12.1.1、整体框架
- Client:客户端;操作docker服务器的客户端(命令行或者界面)
- Docker_Host:Docker主机;安装Docker服务的主机
- Docker daemon:后台进程;运行在Docker服务器的后台进程
- Images:镜像;Image是只读模板,其中包含创建Docker容器的说明,其中容器是由Image运行而来,Image固定不变。
- Containers:容器;在Docker服务器中的容器(一个容器一般是一个应用实例,容器间互相隔离),一个镜像可以生成多个容器
- Registries:镜像仓库,也就是存储Docker Image的地方,官方远程仓库地址:https://download.docker.com/linux/centos/docker-ce.repo,当然也可以使用阿里云镜像仓库:https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
12.1.2、虚拟机、Docker、容器的关系
- Infrastructure:基础设施;比如华为服务器
- Host Operating System:操作系统;比如基于Linux操作系统的CentOS
- Docker:docker环境
- App X:容器
12.2、Docker隔离原理
12.2.1、资源隔离(namespace 6项隔离)
12.2.2、资源限制(cgroups资源限制)
cgroup资源控制系统,每种子系统独立地控制一种资源。功能如下:
13、Docker安装完成
14、Docker镜像加速配置
14.1、在线安装
14.1.1、docker官网
注意: 安装过程中需要在linux上登录root用户,否则部分命令将执行受阻
14.1.2、找到在CentOS中安装docker的文档
点击Developers》Docs,如下:
点击Download And Install,如下:
点击Docker fro Linux,如下:
点击CentOS,如下:
14.1.3、判断安装docker的CentOS环境是否符合要求
判断来源依然是官网,截图如下,如果你不知道你的CentOS版本,你可以在终端中使用cat /etc/redhat-release
命令查看当前CentOS版本
14.1.4、卸载旧版本
如果你之前已经安装过docker,然后本次安装的是新的docker版本,那需要先卸载旧版本,然后在安装新版本,卸载命令如下。如果你根本没有安装过docker,请忽略这一步。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
上述命令来源于官网,截图如下:
14.1.5、安装gcc相关工具
14.1.5.1、判断CentOS是否能上外网
使用下列命令即可判断,如下:
ping www.baidu.com
如果无法连接外网,那就需要配置一下,博客如下:CentOS配置静态IP
14.1.5.2、安装gcc编译器
执行以下命令安装gcc编译器:
yum -y install gcc
注意:如果你不确定是否已经安装了,你也可以执行这条命令,多次执行该命令不会多次下载
14.1.5.3、安装gcc-c++编译器
执行以下命令安装gcc-c++编译器:
yum -y install gcc-c++
注意:如果你不确定是否已经安装了,你也可以执行这条命令,多次执行该命令不会多次下载
14.1.6、选择合适的安装方式
选择大多数人使用的安装方式就可以了,点击下图中的蓝色链接即可:
14.1.7、安装yum-utils安装包
安装yum-utils安装包,命令如下:
yum install -y yum-utils
14.1.8、安装阿里云镜像仓库
不能使用官方推荐的 https://download.docker.com/linux/centos/docker-ce.repo
,因为这是国外的镜像仓库,在国内使用容易产生TCP连接问题和超时问题
建议使用阿里云或者网易云的docker镜像仓库,我们下面以阿里云docker镜像仓库为例来进行安装配置,以下配置脚本来源于阿里云官网的文章Docker CE 镜像中,如下:
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
14.1.9、安装docker引擎
(1)安装最新版本docker,需要执行以下命令:
sudo yum -y install docker-ce docker-ce-cli containerd.io
(2)如果想安装其他版本docker,可以先使用以下命令查询所有docker版本列表
yum list docker-ce --showduplicates | sort -r
上述命令查询结果如下,其中红框中就是dockerVersion:
如果选好了docker版本,可以将下面命令中的dockerVersion替换掉,之后执行命令安装docker即可:
sudo yum install -y docker-ce-dockerVersion.x86_64 docker-ce-cli-
dockerVersion.x86_64 containerd.io
例如我选择的dockerVersion是3:19.03.9-3.el7
,那么命令就是:
sudo yum install -y docker-ce-3:19.03.9-3.el7.x86_64 docker-ce-cli-3:19.03.9-3.el7.x86_64 containerd.io
14.1.10、配置镜像加速器
14.1.10.1、配置阿里云镜像加速器(不太推荐,原因是部分镜像无法下载)
首先进入阿里云官网,并登录账号,搜索容器镜像服务,选择立即开通,如下:
找到需要执行的脚本命令,如下:
一定要在linux中执行上述截图中框起来的脚本命令才能完成加速功能
14.1.10.2、配置网易镜像加速器(推荐)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
14.1.11、启动docker
执行以下命令启动docker,如下:
systemctl start docker
注意: 启动完成不会有任何消息提示,但是可以通过ps -ef | grep docker
来验证是否docker是否启动成功
14.1.12、设置docker开机自启
执行以下命令:
systemctl enable docker
14.2、离线安装(说明:没有实现,先记录一下,后续可能会实现)
14.2.1、docker官网
注意: 安装过程中需要在linux上登录root用户,否则部分命令将执行受阻
14.2.2、下载docker离线安装包
点击Developers》Docs,如下:
点击Download And Install,如下:
点击Docker fro Linux,如下:
点击Binaries下面的Install daemon and client binaries on Linux
,如下:
tgz压缩包下载路径在以下位置:
之后点击x86_64/
,如下:
之后就可以选择适合自己的docker版本下载,下载速度很快,所以就不给安装包了,如下:
TODO 安装方法后续验证通过在补充
15、Docker镜像的一些概念
- 查看所有镜像:
docker images
或者docker image ls
- 拉取最新镜像:
docker pull 镜像名称
,相当于docker pull 镜像名称:latest
- 拉取特定版本镜像:
docker pull 镜像名称:镜像版本
- 镜像选择:尽量选择带
alpline
、slim
的镜像。原因是:镜像是linux操作系统+软件自身构成的,其中带alpline
、slim
的镜像使用的linux操作系统很小,所以最终镜像本身也不大,另外常规版Linux+软件自身构成的镜像还是很大的
所有Docker命令手册:
https://docs.docker.com/engine/reference/commandline/docker
16、Docker镜像的一些其他操作
- 删除单个镜像:
docker rmi -f 镜像名称:标签
;添加-f的作用是删除已经运行过的容器所属镜像,或者正在运行的容器所属镜像 - 删除所有镜像:
docker rmi -f $(docker images -aq)
,其中-a是列出所有镜像,而-q是只列出镜像id - 删除游离镜像:
docker image prune
,然后在控制台输入y
就可以了 - 镜像改名:
docker tag 原镜像名称:原标签 新镜像名称:新标签
,改名之后通过docker images
可以看到原来镜像和新镜像,并且镜像id相同
17、Docker其他的一些命令
- 查看运行中Up状态的容器:
docker ps
,当容器状态是Up(运行中)才能被看到:Up(运行中);如果启动命令太长会被省略,想看查看完整启动命令需要添加--no-trunc
指令,例如:docker ps --no-trunc
- 查看所有状态的容器:
docker ps -a
,当容器状态是以下几种中任意一种都能被看到:Created(新建)、Up(运行中)、Pause(暂停)、Exited(退出) - 删除所有容器:
docker rm -f $(docker ps -aq)
,其中-a是展示所有状态的容器 - 容器的几种状态:Created(新建)、Up(运行中)、Pause(暂停)、Exited(退出)
- 新建容器:
docker create --name=myredis -p 6379:6379 redis:latest
,可以通过docker ps -a
看到该容器,并且容器状态是Created
- 启动容器:
docker start 容器id/容器名称
,可以把Created(新建)、Exited(退出)状态的容器启动起来,执行完成后可以通过docker ps
或者docker ps -a
看到该容器,并且容器状态是Up
- 暂停容器:
docker pause 容器id/容器名称
,执行完成后可以通过docker ps -a
看到该容器,并且容器状态是Pause
- 暂停恢复容器:
docker unpause 容器id/容器名称
,执行完成后可以通过docker ps
或者docker ps -a
看到该容器,并且容器状态是Up
- 优雅停止容器:
docker stop 容器id/容器名称
,执行完成后可以通过docker ps -a
看到该容器,并且容器状态是Exited
,该操作运行正在运行中的程序处理完所有事情在停止 - 强制停止容器:
docker kill 容器id/容器名称
,执行完成后可以通过docker ps -a
看到该容器,并且容器状态是Exited
,该操作将会使程序马上停止 - 直接以后台方式启动容器:
docker run -d --name=myredis -p 6379:6379 redis:latest
,可以通过docker ps
看到该容器,并且容器状态是Up
,-d代表后台启动,这行语句相当于docker create --name=myredis -p 6379:6379 redis:latest
+docker start myredis
- 以交互模式启动容器:
docker run -it --name=myredis -p 6379:6379 redis:latest
,容器启动之后我们会直接进入容器内部 - 查看容器日志:
docker logs -f 容器id/容器名称
,添加-f用于动态跟踪查看容器日志
18、Docker的进入容器细节
- 进入容器:
docker exec -it -u 0:0 --privileged 容器id/容器名称 /bin/bash
,-i
是以交互模式进入容器,-t
是新打开一个终端,-u 0:0
是以root
用户身份进入容器,--privileged
是以特权方式进入容器,这样用户进入容器之后就能拥有极大的执行权力,最后的/bin/bash
代表进入容器bash控制台,部分容器进入控制台使用sh
命令或者/bin/sh
命令 - 查看容器详情:
docker inspect 容器id/容器名称
,对应复杂写法:docker container inspect 容器id/容器名称
- 查看镜像详情:
docker inspect 镜像名称:镜像标签
,对应复杂写法:docker image inspect 容器id/容器名称
19、Docker cp命令
简单使用:
# 从容器中复制内容到外部
docker cp 容器:容器内部内容 外部存储位置
例如:docker cp 4e7c32cf23cd:/etc/nginx/nginx.conf nginx.conf,用于将nginx.conf
# 将外部内容复制到容器中
docker cp 外部内容 容器:容器内部存储位置
例如:docker cp index.html 4e7c32cf23cd:/usr/share/nginx/html,用于将index.html复制到容器的/usr/share/nginx/html中,并且会替换html下面的index.html
详细用法:
# 从容器中复制内容到外部
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
# 将外部内容复制到容器中
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
当SRC_PATH
和DEST_PATH
身份不同时,最终效果也不同,具体使用注意点如下:
20、Docker镜像的推送等操作
- 检查容器中文件系统结构的改变:
docker diff 容器id/容器名称
,结果中以不同字母开头就代表不同含义,其中:【A:添加文件或目录;C:文件或者目录更改;D:文件或者目录删除】 - 把容器提交成新镜像:
docker commit -a 明快de玄米61 -m "first commmit" 容器id/容器名称 myredis:v1
,其中-a
后面写的是作者,-m
后面写的是提交说明,由于中间有空格,所以用双引号包裹起来,myredis
是新镜像名称,而v1
是新镜像版本号 - 游离镜像产生途径:两次docker commit相同镜像名称和版本,将会使前一次的镜像成为游离镜像
- docker仓库中镜像命名规则:一般是
仓库地址/命名空间/镜像仓库名称:镜像标签
,比如docker hub中的nginx官方镜像就是docker.io/library/nginx:latest
,官方镜像在拉取的时候默认可以省略docker.io/library/
,所以拉取的时候直接使用docker pull nginx:latest
。而docker hub
中的非官方镜像在拉取的时候只能省略仓库地址,而不能省略命名空间;如果是阿里云镜像仓库,那仓库地址也不能省略,比如:docker pull registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/quay.io_prometheus_prometheus:[镜像版本号]
- 登录docker仓库:
docker login -u 用户名 docker仓库地址(如果推送docker hub就不用写docker仓库地址)
,-u就是–username,也就是用户名。比如登录dockerhub的命令是:docker login -u mingkuaidexuanmi61
,而登录阿里云的命令是:docker login --username=明快de玄米61 registry.cn-hangzhou.aliyuncs.com
,登录成功会提示:Login Succeeded
- 退出docker仓库:
docker logout
- 镜像推送到镜像仓库步骤
- 登录镜像仓库:
docker login -u 用户名 docker仓库地址(如果推送docker hub就不用写docker仓库地址)
,比如:1、docker login -u mingkuaidexuanmi61
(登录docker hub);2、docker login --username=明快de玄米61 registry.cn-hangzhou.aliyuncs.com
(登录阿里云镜像仓库) - 镜像改名:
docker tag 镜像id 仓库地址/命名空间/镜像仓库名称:镜像标签(如果推送到docker hub 就不用写“仓库地址/”)
,比如:1、docker tag 69a160e0a651 mingkuaidexuanmi61/myredis:v1
(未来推送到docker hub);2、docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/quay.io_prometheus_prometheus:[镜像版本号]
(未来推送到阿里云镜像仓库) - 推送到镜像仓库:
docker push 仓库地址/命名空间/镜像仓库名称:镜像标签(如果推送到docker hub 就不用写“仓库地址/”)
,比如:1、docker push mingkuaidexuanmi61/myredis:v1
(推送到docker hub);2、docker push registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/quay.io_prometheus_prometheus:[镜像版本号]
(推送到阿里云镜像仓库)
对于上述三个步骤,我们把推送镜像到阿里镜像仓库的步骤截取一下吧,如下:
- 登录镜像仓库:
21、Docker镜像的转移操作(导入、导出)
- 单个容器导出、导入(注意:不要使用):不要使用的原因是导出的镜像在导入之后必须使用之前容器的启动命令启动,非常麻烦,这里把命令也说明一下,如下:
docker export -o tar包名称 容器id
、docker import tar包名称 镜像名称:镜像标签
;如果必须要将现有容器变成镜像导出,大家可以使用docker commit
先制作镜像,然后使用docker save -o tar包名称 镜像名称:镜像标签
导出镜像 - 多个镜像导出成tar包(注意:可以使用):
docker save -o tar包名称 单个或者多个镜像信息(多个镜像中间用空格分隔)
,例如:docker save -o myredis.tar myredis:v1
- 导出包含多个镜像的tar包(注意:可以使用):
docker load -i tar包名称
,例如:docker load -i myredis.tar
22、Docker容器长久运行之道
容器启动之后肯定有需要一直做的事情,否则不可能长久运行,比如busybox
镜像就没有这种事情,那就不能长久运行。如果我们想让busybox容器长久运行,那就需要指明一个命令让它能长久运行,比如:docker run -d busybox ping www.baidu.com
23、Docker如何构建镜像
创建一个ping百度的镜像,其中Dockerfile如下:
FROM alpine
CMD ping www.baidu.com
构建镜像命令如下:
# -t后面指定镜像名称:标签,-f后面指定Dockerfile文件的全路径,.代表工作目录
docker build -t attackbaidu:v1 -f Dockerfile .
24、如何下载做实验
说明: 每次登陆docker实验室,都会获得4小时的使用时长,最主要的功能是可以下载国外的镜像,有些时候下载国外镜像非常慢,可以先使用该网站下载下来,然后上传到阿里云镜像仓库中,之后我们下载下来,然后通过docker tag
改名之后就可以正常使用了
25、镜像如何在底层存储
26、容器与镜像的写时复制技术
通过docker image inspect 镜像id
可以查看容器细节,比如nginx镜像底层存储信息如下:
- LowerDir:底层目录
- MergedDir:合并目录
- UpperDir:上层目录
- WorkDir:工作目录
LowerDir:
/var/lib/docker/overlay2/03ed09a2686181fef7a4418b94eafb135695a4053a4ecf910556f54fcbe931b5/diff
目录文件列表:docker-entrypoint.d
/var/lib/docker/overlay2/aad5146a099afeed2f1facca6e0673f0535eb58aaf609fbd187961aaf1d29967/diff
目录文件列表:docker-entrypoint.d
/var/lib/docker/overlay2/36b7df9b17ff9dc6197461d5da662e1fe546554fbd4adcba91f5dae6eaedf564/diff
目录文件列表:docker-entrypoint.sh
/var/lib/docker/overlay2/6ea0055863bb9a7870cc5677087fb8b57a669989ae506e5bfc07af6b833bf214/diff
目录文件列表:docker-entrypoint.d etc lib tmp usr var
/var/lib/docker/overlay2/81a82a1e3f38865dec609b878d7f1d9c8a667144d95407fab90173563a70ff1b/diff
目录文件列表:bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
这相当于是一种叠罗汉的过程,最底层就是linux系统,然后往上一层层记录不相同的内容就构成了底层目录
上面简单介绍了一下镜像的结构,其实我们对容器执行docker inspect 容器id
也可以看到容器也是这些层,如果我们不修改容器内容,那么容器的层就会指向镜像的层,如果我们对容器内容进行修改,根据写时复制思想,就会把原始的文件复制到容器对应层,然后进行修改,但是无论怎么修改,都不会影响到镜像
27、Docker的overlayFS存储驱动原理
简单来说镜像层的lowerdir底层目录来存储基础内容,如果我们需要修改文件,那就在把文件复制到容器层的upperdir上层目录中进行修改,原始层和修改层组合到一起就是合并层,而容器层真正使用的是merged合并层目录
详细了解请看:https://docs.docker.com/storage/storagedriver/overlayfs-driver/
28、Docker数据卷挂载的三种方式
29、Docker的-v挂载测试
30、Docker的卷管理
说明:
讲解Volumes
和Bind mounts
之前,我们先来说明一下两者的区别,如果-v后面,并且:之前的目录以/开头,那就是Bind mounts
(比如:-v /data/nginx_html:/usr/share/nginx/html
),否则就是Volumes
(比如:-v /usr/share/nginx/html
或者-v nginx_html:/usr/share/nginx/html
)
使用Bind mounts
需要提前把目录内容准备好,因为容器中的对应内容将被替换成挂载指定的目录里面的,如果挂载指定的目录里面是空的,那容器中的对应内容就会被删除,所以谨慎使用这种方式;
但是这种方式也不是一无是处,比如我们想让容器中的编码方式以及时间和本地虚拟机中的一致,那就可以使用这种挂载方式,让容器共享本地虚拟机中的文件
使用Volumes
不需要提前准备内容,因为卷对应目录中的内容和容器中对应目录的内容完全一样
30.1、Volumes(解释:开发者自己创建文件夹,手动挂载)
匿名卷:
# 挂载目录为空,甚至连:都不用写,虽然没有指定卷名称,但是docker会为它自动生成一个随机卷名称
docker run -d -P -v /usr/share/nginx/html --name=mynginx nginx
具名卷(不需要提前创建卷名,创建容器的时候会自动创建):
# 名字是卷的名称,都是自己取的
docker run -d -P -v nginx_html:/usr/share/nginx/html --name=mynginx nginx
查看卷:
卷的几种操作:
# 创建卷
docker volume create 卷名称
# 查看卷详情,尤其是卷对应的真实物理地址
docker volume inspect 一个或者多个卷名称
# 查看所有卷
docker volume ls
# 清除没有被使用的卷
docker volume prune
# 删除卷
docker volume rm 一个或者多个卷名称
查看容器所用的卷一般使用两种方法
方法1:通过查看容器详情查找
具体命令是docker inspect 容器id
,例如查看nginx容器的信息如下:
匿名卷物理位置截图:
具名卷物理位置截图:
方法2:通过卷详情查找
如果你知道卷名称,可以通过卷详情查找,当然如果不知道卷名称,也可以通过docker volume ls
先查找对应卷名称,然后继续说通过卷详情查找物理存储路径,比如我设置的卷名称是nginx_html,那么命令就是:docker volume inspect nginx_html
30.2、Bind mounts(解释:docker自己创建文件夹,自动挂载)
# 不需要提前创建/data/nginx_html,创建容器的时候会自动创建多级目录
# 但是存在的一个问题是:如果nginx_html是空的/没创建,那么将导致/usr/share/nginx/html也是空的,这是空挂载,多多注意
docker run -d -P -v /data/nginx_html:/usr/share/nginx/html --name=mynginx1 nginx
30.3、tmpfs mounts(解释:可以把数据挂载到内存中,不用这个)
31、Docker的可视化界面
31.1、容器开机自启动
我们使用--restart=always
参数可以完成功能,这其实是设置容器存在时的重启策略,我们设置的是任何情况都重启,那开机的时候容器存在但是没有启动,自然就会触发重启策略;
- 如果容器还没有创建,那就可以通过
docker run --restart=always
方式设置启动策略 - 如果容器已经创建,那就可以通过
docker update --restart=always
来设置重启策略
31.2、安装docker可视化界面—portainer
docker主节点可以安装可视化界面,单个节点那自身就是主节点了,安装指令如下:
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v \
/var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data \
portainer/portainer-ce
安装完成后直接访问http://主机:9000
端口就可以了,访问成功之后需要先设置密码,然后就能看到对应界面了
对于集群情况,还有其他节点,那它们需要安装agent端,之后我们就可以在主节点的页面看到集群中所有节点信息了,具体指令如下:
docker run -d -p 9001:9001 --name portainer_agent --restart=always -v \
/var/run/docker.sock:/var/run/docker.sock -v \
/var/lib/docker/volumes:/var/lib/docker/volumes portainer/agent
32、最基本Dockerfile构建镜像
Dockerfile:
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo 111
# 容器运行期间执行的命令,可以使用sh文件,或者直接在CMD后面写也行
CMD sleep 10;echo success
构建镜像:
# -t:后面写的是新镜像名称和标签,-f后面指定Dockfile文件位置,.代表Dockfile中命令执行的工作空间
docker build -t myalpine:v1 -f Dockerfile .
打印的构建过程:
查看构建完成的镜像:
采用交互模式运行构建完成的镜像:
docker run -it 0ca5b66e5a2e
然后10s
之后控制台输出success
退出
33、Dockerfile怎么写—1
33.1、FROM
挑选说明:
FROM 指定基础镜像,最好挑一些apline,slim之类的基础小镜像
如何确定我需要什么要的基础镜像?
- Java应用是jdk/jre基础镜像(例如:SpringBoot应用,打jar包),或者是Tomcat基础镜像(例如:SpringWeb应用,打war包)
- JS模块化应用一般用nodejs基础镜像
- 其他各种语言用自己的服务器或者基础环境镜像,例如python、golang、java、php等
33.2、LABEL
作用:
用来标明镜像的说明信息,比如镜像的制作人、制作时间等
写法举例:
# 写法1(一行写完):
LABEL multi.label1="value1" multi.label2="value2" other="value3"
# 写法2(多行写,用\换行):
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
33.3、ARG的生命周期
总结:镜像构建时期存活,但是容器运行时期失效
解释:构建镜像的时候有效,可以用在RUN指令(镜像构建期间执行)中,但是不能用在CMD
和ENTRYPOINT
(容器运行期间执行)中
33.4、ENV的生命周期
总结:镜像构建和容器运行时期都存活
解释:可以用在Dockerfile
的任何指令中,都是生效的
33.5、RUN的执行时期
总结:镜像构建时期执行
解释:构建镜像的时候执行,也就是根据Dockerfile创建镜像的整个过程中会执行
33.6、CMD的执行时期
总结:容器运行时期执行
解释:容器启动完成的运行期间执行,也就是在容器启动完成之后运行的时候执行
33.7、RUN的两种书写形式
33.7.1、shell形式(可以取出变量的值)
写法: RUN 命令
举例: RUN echo $name;echo $name2
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
ARG name="hello world"
ARG name2="明快de玄米61"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $name;echo $name2
解释: 这种写法等于:/bin/sh -c "echo $name;echo $name2"
,由于我本次测试使用的是alpine
镜像,所以是/bin/sh -c
,如果使用其他镜像,相当于是/bin/bash -c
,视具体镜像而定
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
ARG name="hello world"
ARG name2="明快de玄米61"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN /bin/sh -c "echo $name;echo $name2"
说明: 这种写法可以取出ARG
、ENV
定义的变量值
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
ARG name="hello world"
ENV address="中国"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $name;echo $address
33.7.2、exec形式(可以执行shell命令,但是无法取出变量的值)
写法: RUN ["命令"……]
举例: RUN ["echo", "$name"]
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
ARG name="hello world"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN ["echo", "$name"]
执行结果:无法取出变量name的值,构建过程中只能打印出$name
,可以使用docker build -t myalpine:v1 --no-cache -f Dockerfile .
去尝试
33.7.3、exec形式转变成shell形式(可以取出变量的值)
写法: RUN ["/bin/sh", "-c", "具体指令"]
举例: RUN ["/bin/sh", "-c", "echo $name"]
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
ARG name="hello world"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN ["/bin/sh", "-c", "echo $name"]
执行结果:可以取出name变量的值
解释: 这种写法等于:/bin/sh -c "echo $name"
,由于我本次测试使用的是alpine
镜像,所以是/bin/sh -c
,如果使用其他镜像,相当于是/bin/bash -c
,视具体镜像而定
Dockerfile全部指令说明:https://docs.docker.com/engine/reference/builder/
34、ARG—指令
34.1、生效时间
镜像构建时期存活,但是容器运行时期失效;所以可以在RUN
指令中可以使用,但是在CMD
和ENTRYPOINT
指令中不能使用
34.2、是否可以在定义之前使用
不能
34.3、能否并排定义
不能
34.4、如何在镜像构建命令中修改参数值
可以通过–build-arg参数修改ARG定义的参数值,例如我通过ARG定义了两个参数的值,分别是name="明快de玄米61"
、address="中国"
,如果我想把它们修改成其他值,执行的构建指令是:
docker build --build-arg name=mingming --build-arg address="my china" -t myalpine:v1 --no-cache -f D1 .
Dockerfile如下:
# 基础镜像
FROM alpine
# 打标签,也就是描述信息
LABEL maintainer="明快de玄米61" date="2022/12/17" \
weather=sunny
ARG name="明快de玄米61"
ARG address="中国"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $name;echo $address
构建结果如下:
34.5、可以在FROM之前使用ARG定义参数吗
可以
Dockerfile:
# 可以在任意位置定义,并且在镜像构建期间生效的命令中使用
ARG version=3.17.0
# 基础镜像
FROM alpine:$version
构建结果如下:
35、ENV指令
35.1、生效时间
构建期和运行期间都是生效的
Dockerfile:
# 基础镜像
FROM alpine
ENV desc="知识分享者"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $desc
# 容器运行期间执行的命令,可以使用sh文件,或者直接在CMD后面写也行
CMD echo $desc
构建期间执行结果:
运行期间执行结果:
运行命令:docker run -it myalpine:v1
35.2、能否在镜像构建命令中修改参数值
不能
35.3、能否在容器运行命令中修改参数值
可以
Dockerfile:
# 基础镜像
FROM alpine
ENV desc="知识分享者"
ENV age=100
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $desc;echo $age
# 容器运行期间执行的命令,可以使用sh文件,或者直接在CMD后面写也行
CMD echo $desc;echo $age
容器运行期间执行结果:
命令:docker run -it -e desc=博主 -e age=120 myalpine:v1
35.4、ENV能否引用ARG
能
Dockerfile:
# 基础镜像
FROM alpine
ARG desc1="知识分享者1"
ENV desc2=$desc1
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $desc1;echo $desc2
构建过程如下:
35.5、能否并排定义
能
Dockerfile:
# 基础镜像
FROM alpine
ENV desc1="知识分享者1" desc2="知识分享者2"
# 容器构建过程中运行的指令,默认使用镜像的root用户执行命令
RUN echo $desc1;echo $desc2
构建期间执行结果:
36、ENV的持久化问题
36.1、ENV的值到底存在哪里了
就那上述1中的镜像为例,通过docker inspect 镜像id
命令查看镜像信息,可以看到desc2的信息如下,说明只要镜像一旦形成,ENV定义的环境变量的值就已经固化了,不过后期可以在docker run
的时候通过-e
参数修改
37、ADD与COPY指令
说明: ADD与COPY指令复制的内容都来自于Linux虚拟机中,而不是镜像中,不过最终是要复制到镜像中的
37.1、ADD能复制普通内容、解压压缩包、下载url链接中的文件
- 链接:下载链接中的文件,然后复制到目录中,不会解压
- 压缩包:解压压缩包到目录中,会解压(说明:jar包不属于压缩包)
- 普通目录/文件:直接复制到目录中即可,不会解压
Dockerfile:
# 基础镜像
FROM alpine
# 自动下载
ADD https://download.docker.com/mac/static/stable/x86_64/docker-20.10.14.tgz /dest/
RUN cd /dest && ls -l
# 自动解压压缩包
ADD docker-20.10.14.tgz /app/
RUN cd /app && ls -l
# 复制普通内容到镜像中
ADD *.txt /redis/
RUN cd /redis && ls -l
结果:
拓展:
如果想复制某目录下面的全部内容,就可以通过/
来表示,如下:
Dockerfile:
# 基础镜像
FROM alpine
# 赋值当前路径下的所有内容到redis下面
ADD ./ /redis/
# 复制duty目录下的内容到/webapp下,假设duty目录下是index.html、WEB-INF等,最终效果是/webapp下面是index.html、WEB-INF等,这种写法不会将duty整体复制到/webapp下
ADD duty /webapp/
# 复制duty目录到/webapp下,最终效果是/webapp下面是duty目录,这种写法可以将duty整体复制到/webapp下
ADD duty /webapp/duty
RUN cd /redis && ls -l
37.2、COPY是直接复制
- 直接复制没什么好说的,不会下载链接中的目录,也不会解压压缩包
Dockerfile:
# 基础镜像
FROM alpine
COPY ./ /redis/
RUN cd /redis && ls -l
复制目录:
# 复制duty目录下的内容到/webapp下,假设duty目录下是index.html、WEB-INF等,最终效果是/webapp下面是index.html、WEB-INF等,这种写法不会将duty整体复制到/webapp下
COPY duty /webapp/
# 复制duty目录到/webapp下,最终效果是/webapp下面是duty目录,这种写法可以将duty整体复制到/webapp下
COPY duty /webapp/duty
构建过程截图:
37.3、多次RUN的指令的上下文关系
每一次RUN指令的执行都是以WORKDIR
为上下文,如果想让执行能以我们cd进入的目录为上下文,那就需要把它写在一行中,并且用&&分隔,比如:RUN cd /redis && ls -l
38、COPY的文件可以改变目录
无,老师没说清,感觉作用也不是很大
39、WORKDIR的应用
- 指定Dockerfile中语句的工作空间
- 当我们首次进入容器时,会直接进入workdir指定的目录下
指定工作空间:
Dockerfile:
# 基础镜像
FROM alpine
# alpine镜像的默认workdir是/,此时workdir是/
RUN pwd
# 设置workdir是/app,此时workdir是/app
WORKDIR /app
RUN pwd
# 在/app的基础上再次设置workdir为webabb/WEB-INF,此时workdir是/app/webabb/WEB-INF
WORKDIR webapp/WEB-INF
RUN pwd
构建过程截图:
进入容器的目录是workdir指定的目录:
我们进入nginx容器中最常改的东西是html文件,所以我们把workdir设置成/usr/share/nginx/html
,Dockerfile如下:
# 基础镜像
FROM nginx
# 设置workdir
WORKDIR /usr/share/nginx/html
构建命令是:
docker build -t mynginx:v1 --no-cache -f D2 .
运行命令是:
docker run -dP mynginx:v1
进入容器内部,命令是:
docker exec -it f37721b0288 /bin/bash
可以看到默认空间正是我们设置的workdir,如下:
40、USER配合COPY进行权限的控制
对38、COPY的文件可以改变目录的补充,但是作用不大,就不总结了
41、VOLUME的坑
- 相当于
docker run
中-v
指令中匿名卷Volumes
的作用 - 尽量写在最后,因为VOLUME之后对挂载目录中内容的修改操作都会被丢弃
- 只要被挂载出去的目录,后续
docker commit
提交镜像的时候这些挂载出去的目录都会被丢弃,包括VOLUMN
和docker run -v
VOLUMN
指令会延续下去,即使docker commit
提交镜像的时候VOLUMN
指令也会起作用
41.1、相当于docker run
中-v
指令中匿名卷Volumes
的作用
我们来制作一个nginx镜像,并且把/usr/share/nginx/html
和/etc/nginx
目录挂载出去
Dockerfile:
# 基础镜像
FROM nginx
# 将html目录和配置文件挂载出来
VOLUME [ "/usr/share/nginx/html", "/etc/nginx" ]
构建命令:
docker build -t mynginx:v1 --no-cache -f D1 .
运行命令:
docker run -dP --name=mynginx1 mynginx:v1
查看挂载情况:
# 这一串字符串是容器id
docker inspect b3780984ae99
如下图:
进入htm挂载目录:
cd /var/lib/docker/volumes/3b79fbfb8e1cf3fd5d0da72f956b51f3db3189b73ffebbbc550bdbea34d6a476/_data
如下图:
41.2、尽量写在最后,因为VOLUME之后对挂载目录中内容的修改操作都会被丢弃
Dockerfile:
# 基础镜像
FROM alpine
VOLUME [ "/data" ]
RUN echo "111" > /data/a.txt
RUN cd /data && ls -l
构建过程截图:
41.3、只要被挂载出去的目录,后续docker commit
提交镜像的时候这些挂载出去的目录都会被丢弃,包括VOLUMN
和docker run -v
我们以1中制作的nginx镜像为例,我们把index.html中的内容修改成111,然后通过docker diff 容器id
却看不到index.html的改变,其实从这个情况我们已经看出来了,即使通过docker commit
提交镜像也不会把index.html
中是111
的情况记录进去
我们把修改之后的nginx容器制作成镜像,命令是:
# b3780984ae99是nginx容器id
docker commit b3780984ae99 mynginx:v2
然后把容器运行起来,命令是:
docker run -dP mynginx:v2
之后通过docker inspect命令查看index.html所在位置,命令是:
docker insepct f08516
结果是:
进入该位置之后,查看index.html
,结果如下:
41.4、VOLUMN
指令会延续下去,即使docker commit
提交镜像的时候VOLUMN
指令也会起作用
上述操作3中已经运行了提交的镜像,我们通过docker inspect指令看到了挂载出来的/usr/share/nginx/html
和/etc/nginx
,说明延续成功了
42、EXPOSE暴露端口
- EXPOSE的主要是给程序员看的
- 在运行docker run指令,然后设置-P参数的时候,docker会为容器自动暴露EXPOSE指定的端口
- EXPOSE指令可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。
下面举个例子,以busybox镜像为基础镜像,然后把80和90端口暴露出来,虽然这两个端口没啥用处,但是依然可以暴露出来看下嘛
Dockerfile:
# 基础镜像
FROM alpine
EXPOSE 80
EXPOSE 90
CMD ping www.baidu.com
构建语句:
docker build -t myalpine:v1 --no-cache -f D1 .
运行语句:
docker run -dP myalpine:v1
查看容器语句:
docker ps
查看结果如下
43、CMD、ENTRYPOINT容器启动指令
-
CMD和ENTRYPOINT角色划分:ENTRYPOINT是真正的大门,而CMD是为ENTRYPOINT提供参数的
-
指令重复哪个会生效:多个CMD指令只有最后一个会生效、多个ENTRYPOINT指令只有最后一个会生效,所以只写一个就行了
-
CMD和ENTRYPOINT也有两种写法,分别是shell形式和exec形式,这两种的区别和写法与RUN中说明的一致,依然是exec形式无法取出变量值,但是可以通过
/bin/sh -c
的形式让exec形式变成shell形式 -
如何覆盖CMD的参数:
docker run
指令中最后一个位置是写命令的,这个位置写的东西会覆盖CMD中的全部内容,比如下面的例子中默认是ping 5次百度
的,但是我们在docker run
指令的最后写了6 atugigu.com
,所以最后的效果变成了ping 6次尚硅谷
,这也说明了docker run
指令的最后一个位置可以覆盖CMD
中的全部指令
-
CMD和ENTRYPOINT同时写,具体执行标准如下:
举个CMD和ENTRYPOINT同时写的例子吧,该例子的作用是ping baidu.com
Dockerfile:
# 基础镜像
FROM alpine
CMD ["www.baidu.com"]
ENTRYPOINT ["ping"]
构建指令:
docker build -t myalpine:v1 --no-cache -f D1 .
运行指令:
docker run -P -it myalpine:v
44、多阶段构建
多阶段构建的目的是使用前一阶段的成果,我们举个例子吧,我们写一个普通的springboot项目,然后第一阶段使用maven进行构建,第二阶段生成可以运行的镜像,具体Dockerfile如下:
# 第一阶段:生成jar包,给第一阶段通过AS取名buildapp,方便第二阶段复制app.jar
FROM maven:3.5.0-jdk-8-alpine AS buildapp
WORKDIR /app
COPY pom.xml .
COPY src .
RUN mvn clean package -Dmaven.test.skip=true
RUN cp target/*.jar app.jar
# 第二阶段:生成可以运行的镜像
FROM openjdk:8-jre-alpine
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
# 从第一阶段复制app.jar
COPY --from=buildapp /app/app.jar app.jar
# 定义参数
ENV JAVA_OPTS=""
ENV PARAMS=""
# 启动命令
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]
45、开通云服务器
我使用本地虚拟机,所以就不总结这一集了
46、Dockerfile的springboot应用写法
46.1、自己制作的镜像如何瘦身
- 选择最小可用的基础镜像,比如带有alpine、slim这种的
- 合并RUN环节的所有指令,少生成一些层,毕竟每一个指令就是一层,多个RUN指令合并的时候用&&连接,比如RUN echo 111 && echo 222
- 使用
.dockerignore
文件,排除上下文中不需要参与构建的资源,类似于.gitignore
- 使用多阶段构建
- 合理使用构建缓存加速构建,比如构建的时候不加–no-cache
47、Docker运行期间的一些问题延伸
不同的容器运行使用不同的虚拟机
48、桥接网络的原理
48.1、docker系统净化
命令:
docker system prune
作用:
- 移除所有停止的容器
- 所有没有被使用的网络
- 所有游离的镜像
- 所有的游离构建缓存
48.2、docker容器、宿主机网络关系
如果我们不开启任何容器,我们在虚拟机上通过ip addr
命令可以看到至少3中网络,分别是lo
、ens33
、docker0
,其中lo
是本地网络,ens33
是和外界通信的网络,而docker0
是我们docker网关
,如下图:
我们通过docker run -d -p 30000:9090 --name=myalpine alpine ping www.baidu.com
开启一个alpine容器,添加ping百度命令的原因是避免alpine容器停止,总要找个事情给它做嘛,然后在虚拟机上继续执行ip addr
,我们会看到会多出一个网络信息,比如我的是31: veth38dede4@if30
,如下:
我们使用命令docker exec -it 083654229bcc /bin/sh
进入启动的alpine容器内部,输入ip addr命令,我们也会看到一个独特的网络信息,比如我的是30: eth0@if31
,如下:
是不是已经感受到了一一对应的关系,没错它们就是一一对应完成容器和虚拟机的通信
我启动alpine容器的命令是:docker run -d -p 30000:9090 --name=myalpine alpine ping www.baidu.com
,那我们访问外界的30000
端口其实就是在访问alpine
容器的9090
端口,其实这种端口映射关系虚拟机都帮我们记着呢,另外大家先看下上面在alpine容器内部的ip addr命令截图,里面记载了ip信息是172.17.0.2/16
,现在我们通过exit
命令退出容器,并且回到虚拟机中,执行一下iptables -nL
命令,执行结果如下:
你看看ip
和port
都和我们容器的信息对应起来的,所以当我们访问30000
端口的时候,虚拟机就知道我们找的是alpine
容器的9000
端口
我们在开一个alpine
容器,命令是:docker run -d -p 31000:9090 --name=myalpine1 alpine ping www.baidu.com
,使用ip addr可以看到多处的网络信息是33: vethfd0079b@if32
,然后进入容器内部通过ip addr看到的网络信息是32: eth0@if33
,对应的ip
信息是172.17.0.3/16
,然后在虚拟机中通过iptables -nL
可以看到多了一行记录,它是172.17.0.3 tcp dpt:9090
如果我们在第二个alpine
(ip:172.17.0.3)容器中ping第一个alpine(ip:172.17.0.2)容器的ip,发现是可以ping通的,反之亦然,所以说明两个容器的网络是连通的,其实两个容器中间的传输介质就是docker0网关
,另外我们在alpine容器中ping www.baidu.com
,发现也是通的,其实我们也是通过docker0网关,然后到ens33
,最终猜到公网的,所以我们以两个alpine容器、docker0网关、ens33为例来说明一下网络交互情况,如下图:
49、–link来连接容器
49.1、容器网络互通在解释
上一个总结中基本说清了容器之间、容器和公网之间网络互通的原因,但是docker0网关那块没说太明白,我们再来聊一下,其实我们默认启动的容器都是在一个网络下面,默认网络叫做网桥bridge
,查看网络的命令是:docker network ls
,结果如下:
我们通过docker network inspect f9a627c0fa09
查看网桥bridge
的详细信息,里面可以看到网关和子网信息、网桥中的容器信息,如下:
因此我们那些默认容器都在同一个子网下面,当然可以通过一个网关进行网络互通了
49.2、–link的弊端
如果通过IP连接,其实是不稳定的,因为docker容器可能会出现问题挂掉,当再起一个同样名称容器的时候,容器原有ip可能已经变化了,如果频繁的去更改其他容器连接该容器的ip,说实话是一个很烦人的事情,所以最好用域名来代替ip,这时候–link属性就出现了,我们先把–link的使用方法说一下吧
我们先启动一个redis容器,命令是docker run -dP --name=redisserver redis
,可以看出来redis容器的名称是redisserver
,再来启动一个tomcat容器,并且让tomcat容器和redis容器联系在一起,命令是docker run -dP --name=tomcat --link=redisserver tomcat:jre8-alpine
,其中--link
后面写的是redis容器的名称,所以我们进入tomcat容器内部,应该是可以通过ping redisserver
来连接redis容器的,我们来尝试一下
首先通过命令docker exec -it bf6f17bf556d /bin/bash
进入tomcat
容器,然后执行ping redisserver
,发现是可以ping
通的,其实原因是我们已经把redisserver
对应的redis容器ip信息已经记录到了hosts
文件中,我们可以执行cat /etc/hosts
命令查看hosts
文件内容,如下:
这种做法存在两个弊端,如下:
redis
容器重新创建之后,ip可能改变,那么ping redisserver
就无法连通了- 这种做法是单向的,也就是只有
tomcat
容器能ping
通redis
,但是redis
无法ping
通tomcat
50、docker自定义网络原理
50.1、常用网络模式
我们想完成的效果其实就是在一个容器里面可以通过类似域名的形式ping
通另外一个容器,其中一方容器删除在重启都不应该影响到另外一个容器的连接,上次提到了--link
,发现这种把域名和ip
记录在hosts
文件中的方式是不符合要求的,而自定义网络方式可以满足这一需求
我们先来说一下几种常见的网络模式:
项目 | Value | Value |
---|---|---|
bridge模式 | –net=bridge | 默认值,在Docker网桥docker0上为容器创建新的网络栈 |
none模式 | –net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
container模式 | –network=container:容器id,当前容器和另外一个容器共享网络。kubernetes中的pod就是多个容器共享一个Network namespace。 | |
host模式 | –net=host | 容器和宿主机共享Network namespace |
用户自定义模式 | –net=自定义网络名称 | 在创建容器的时候还可以将网络指定成用户自己定义的网络 |
默认情况下,我们使用的就是bridge
桥接网络模式,然后简单介绍一下其他几个网络的缺点
- none模式:一般不使用
- container模式:和其他容器共享网络,也就是当前容器的
ip
和其他容器ip
都是一致的,其他网络信息也都是共享的,比如我们可以让alipine
在启动的时候共享redis
容器的网络;操作是先启动一个redis
容器,命令是:docker run -dP --name=redisserver redis
,然后容器一个alpine
容器来共享redis容器的网络,命令是:docker run -it --name=myalpine --network=container:a06b4114eef6 alpine
,我们在alipine
容器中输入ip addr
,然后在redis
容器中输入ip addr
,发现两者都是一样的,这种很少使用 - host模式:和虚拟机用同一个网络,那我们容器每开一个端口,那么虚拟机也需要开启对应端口,这种一般也不用
- 用户自定义模式:用户自定义的网络,其中子网范围、网关都是我们自定义的,最大的好处是同处一个自定义网络的容器可以使用
ping 容器名
ping通,即使某容器删除之后又创建,只要容器名不变,那么其他容器依然可以连接上它,这个使用频率较高
50.2、自定义网络
- 自定义网络:命令是
docker network create --driver 网络驱动(默认是:bridge) --subnet 子网范围 --gateway 网关 网络名称
,例如:docker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.1 mynet
- 查看所有网络:
docker network ls
,其他自定义网络命令可以使用docker network --help
查看 - 启动容器指定自定义网络:命令例如
docker run -d --network=mynet --name=myalpine alpine ping www.baidu.com
,使用--network
属性来指定自定义网络名称,其中mynet
就是我的自定义网络名称 - 查看自定义网络详情:命令是
docker inspect 网络id
,也可以写成docker network inspect 网络id
,比如:docker inspect 91733d1c9e17
,效果如下
- 将其他容器加入自定义网络:
docker network connect 自定义网络名称 容器名称
,比如我们将redis容器加入mynet网络,命令是docker network connect mynet redisserver
- 测试处于同一自定义网络的容器之间互通:启动alpine容器命令
docker run -d --network=mynet --name=myalpine alpine ping www.baidu.com
,启动redis容器命令docker run -dP --network=mynet --name=redisserver redis
,由于redis容器没法ping,所以我们只测试alpine容器ping通redis容器,我们通过命令docker exec -it 5be99b974da0 /bin/sh
进入alpine容器,然后输入ping redisserver
回车,发现能ping通
51、docker-compose简单安装
52、compose以及集群模式
TODO docker官网无法访问,明天在总结这两节内容
53、青云需要创建vpc
我不用青云,我用自己本地虚拟机,不总结这块
54、子网掩码
XXX.XXX.XXX.XXX/数字
,比如172.16.0.0/16
,其中每一个XXX的范围都是0~255,其中一个XXX代表8为二进制数,然后斜杠后面的16代表前16位不能变,也就是172.16不能变,而后面的16位可以改变,所以斜杠后面的数字越大,那么子网范围就越小
55、青云开通服务器集群
我不用青云,我用自己本地虚拟机,不总结这块
56、CICD
56.1、DevOps概念
DevOps是Development和Operations的组合词,DevOps重视开发人员和运维人员的交互,然后通过自动化流程让软件构建、测试、发布更加便捷、频繁、可靠。
56.2、CICD概念
CICD简单来说就是持续集成、持续部署
57、CICD的指导实现
57.1、内循环与外循环
57.2、实践流程
57.3、CICD蓝图
58、jenkins简介与安装
59、jenkins安装完成
59.1、官方网址
59.2、安装jenkins
安装docker环境:
如果你的虚拟机没有安装docker环境,可以参考上面的13、Docker安装完成
来安装docker环境
下载jenkins镜像:
https://blog.csdn.net/qq_42449963/article/details/127470086
docker命令:
docker run \
-u root \
-d \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins-data:/var/jenkins_home \
-v /etc/localtime:/etc/localtime:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
--restart=always \
jenkinsci/blueocean
命令解释:
- -u root:以root用户身份运行镜像
- -v:以具名卷方式挂载,详细了解请看上文
29、Docker的-v挂载测试
- -v jenkins-data:/var/jenkins_home:/var/jenkins_home目录中包含了jenkins的所有配置信息,所以需要挂载出来
- -v /etc/localtime:/etc/localtime:ro:外国用的都是UTC时间,所以需要用我们的本地时间代替容器中的UTC时间,最后的
ro
代表read only
,这样可以保证jenkins容器内时间运行正常;如果我们写Dockerfile制作镜像,那就需要使用把这些情况写在Dockerfile中 - -v /var/run/docker.sock:/var/run/docker.sock:
/var/run/docker.sock
表示Docker守护程序,用于监听基于Unix的套接字。 该映射允许 jenkinsci/blueocean 容器与Docker守护进程通信, 如果 jenkinsci/blueocean 容器需要实例化其他Docker容器,则该守护进程是必需的 - –restart=always:不论发生什么原因,始终尝试重启,其实一般情况下作用就是虚拟机启动的时候就需要自动启动该容器
- jenkinsci/blueocean:相比于传统的jenkins,多了一个
blueocean
,这种展示效更加好看
59.3、首次登录、插件安装
访问地址:
http://ip:port/8080/
获取管理员密码:
先通过docker ps
找到启动好的jenkins容器,然后通过docker logs -f 容器id
查看容器启动日志,操作如下:
回车之后结果如下:
插件安装:
将该字符串复制之后输入登录页面中点击继续
按钮即可,然后就会让我们安装插件,我们选择安装推荐的插件
即可,如下:
如果有安装失败的,尽量重试几次,如果还是无法下载,那就点击继续按钮吧,如果有些插件你是真的需要,可以在jenkins的插件管理里面在下载
创建第一个管理员用户:
我喜欢的用户名和密码一般都是root/123456
,设置完成之后点击保存并完成
即可,如下:
实例化配置:
一般使用默认值的就可以,其实就是访问地址,然后点击保存并完成
即可
点击按钮重启jenkins:
即使重启完成,但是页面依然是加载中的状态,我们可以刷新浏览器页面,然后就可以看到登录页面了
59.4、正常登陆
输入用户名和密码之后点击登录按钮就可以登录了,比如我的用户名和密码就是root/123456
60、再绑一个公网IP
这节不总结
61、创建git项目和gitee建立连接
61.1、创建springboot项目
首先创建project:
然后创建springboot
项目,如下:
创建过程中一般选择添加spring web、lombok等依赖,并且spring父工程版本尽量选2版本的,不要选3的,免得出错,然后随意写一个Controller,比如:
@RestController
public class TestController {
@GetMapping("/devops")
public String devops() {
return "devops";
}
}
另外可以根据自己的需求在application.properties/yml
中设置一下server.port
,也就是项目访问端口号,如下:
server.port=30001
61.2、推送项目到gitee
创建本地仓库:
在本地项目中创建git仓库,可以通过IDEA
中VCS
下面的Create Git Repository……
来创建,但是我这版的IDEA中没有该选项,所以只能通过下面的Terminal终端来使用git init
来创建本地仓库了
在gitee中创建空仓库:
仓库创建完成是这个样子的,如下:
上图中就是git远程仓库地址,我们下面会用到的
将项目推送到gitee仓库:
对IDEA中的项目执行git add
、git commit
操作,如下:
设置远程仓库地址,这就是我们上面复制的git
远程仓库地址,如下:
点击Push按钮
推送本地仓库数据到远程仓库即可,如下:
62、jenkins文件的结构
62.1、新建流水线任务
新建任务:
给流水线起一个名字:
将gitee仓库设置成公开的,然后复制仓库地址:
填写初始化信息:
看下分支名称,以及gitee项目中的jenkinsfile文件名称和下图中的是否一致,都没问题的话点击保存按钮即可,如下:
62.2、编写Jenkinsfile文件
在项目根路径下创建Jenkinsfile
文件(对应新建流水线任务时的Jenkinsfile文件名称)
Jenkinsfile
文件内容如下:
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、编译
// 注意:单引号:一般包括常量字符串,无法识别变量;双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来
stage('编译') {
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
}
}
// 2、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 3、打包
stage('打包') {
steps {
echo '打包……'
}
}
// 4、部署
stage('部署') {
steps {
echo '部署……'
}
}
}
}
我们把Jenkinsfile
推送到gitee
仓库中
在jenkins中找到上一步初始化的流水线任务,可以立即构建,可以通过Blue Ocean
去运行,结果如下:
63、jenkins步骤生成器与环境变量
63.1、流水线语法
进入你的流水线任务,然后点击流水线语法,如下:
里面有片段生成器和过程声明器:
63.2、测试过程声明器的使用
使用过程声明器生成脚本,如下:
然后把脚本复制到上述java-devops-demo
项目的Jenkinsfile
文件中,如下:
变量写法需要注意:
- 单引号:一般包括常量字符串,无法包裹变量
- 双引号:可以用来包括常量字符串和变量,其中变量以
$
开头,或者用${}
包裹起来,那么就可以被识别
然后我们将项目改变提交到Gitee中,然后在Jenkins中重新构建任务,输出结果如下:
64、jenkins其他简单的设置
64.1、在Jenkinsfile中写linux命令
点击流水线任务里面的流水线语法,如下:
在页面左侧选中片段生成器,然后选中sh
生成方式,如下:
然后在文本框中输入shell脚本,之后点击生成流水线语法按钮,如下:
然后把shell脚本放在Jenkinsfile中,如下:
把Jenkinsfile传到Gitee中,然后在Jenkins中进行构建即可,效果如下图:
根据上步运行结果,我们还可以把printenv
中的属性值打印出来,Jenkinsfile中需要添加如下内容:
最终在Jenkins中运行效果如下:
64.2、Jenkins中printenv命令打印的环境变量
常用环境变量:
- WORKSPACE:工作空间,比如:
/var/jenkins_home/workspace/java-devops-demo
,其中每一个流水线任务都有一个工作空间 - WORKSPACE_TMP:临时目录,比如:
/var/jenkins_home/workspace/java-devops-demo@tmp
,其中每一个流水线任务都有一个临时目录,存储流水线构建过程中的一些临时文件 - JOB_URL:任务构建访问地址,比如:
http://192.168.139.131:8080/job/java-devops-demo/
,前面的ip和port可以通过Jenkins中系统管理》系统配置》Jenkins Location》Jenkins URL
去设置 - BUILD_NUMBER:已经构建的次数,即当前构建编号,比如:
15
全部环境变量举例:
JENKINS_HOME=/var/jenkins_home
GIT_PREVIOUS_SUCCESSFUL_COMMIT=ac6eb8d76c8ae1b50cb5225c62470311338e5be6
JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental
CI=true
RUN_CHANGES_DISPLAY_URL=http://192.168.139.131:8080/job/java-devops-demo/15/display/redirect?page=changes
HOSTNAME=82a6b03774d6
LD_LIBRARY_PATH=/opt/java/openjdk/lib/server:/opt/java/openjdk/lib:/opt/java/openjdk/../lib
NODE_LABELS=built-in
HUDSON_URL=http://192.168.139.131:8080/
GIT_COMMIT=7b1264e417081269ea49ebd96d89513d891f247e
SHLVL=2
HOME=/root
BUILD_URL=http://192.168.139.131:8080/job/java-devops-demo/15/
HUDSON_COOKIE=06f60d19-0622-4c0d-b584-cb0dd06d35e0
JENKINS_SERVER_COOKIE=durable-08b2d13e1a43b2acce2dbe978130dabdc466cce60036ff8972bdd7a1aa0f16b0
JENKINS_UC=https://updates.jenkins.io
WORKSPACE=/var/jenkins_home/workspace/java-devops-demo
REF=/usr/share/jenkins/ref
NODE_NAME=built-in
RUN_ARTIFACTS_DISPLAY_URL=http://192.168.139.131:8080/job/java-devops-demo/15/display/redirect?page=artifacts
STAGE_NAME=编译
EXECUTOR_NUMBER=1
GIT_BRANCH=origin/master
JENKINS_VERSION=2.346.3
RUN_TESTS_DISPLAY_URL=http://192.168.139.131:8080/job/java-devops-demo/15/display/redirect?page=tests
JENKINS_INCREMENTALS_REPO_MIRROR=https://repo.jenkins-ci.org/incrementals
BUILD_DISPLAY_NAME=#15
HUDSON_HOME=/var/jenkins_home
JOB_BASE_NAME=java-devops-demo
PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
BUILD_ID=15
BUILD_TAG=jenkins-java-devops-demo-15
LANG=C.UTF-8
JENKINS_URL=http://192.168.139.131:8080/
JOB_URL=http://192.168.139.131:8080/job/java-devops-demo/
GIT_URL=https://toscode.gitee.com/mkdxm61/java-devops-demo.git
BUILD_NUMBER=15
JENKINS_NODE_COOKIE=4e20f893-eac7-443c-b282-b45b55a36952
RUN_DISPLAY_URL=http://192.168.139.131:8080/job/java-devops-demo/15/display/redirect
JENKINS_SLAVE_AGENT_PORT=50000
HUDSON_SERVER_COOKIE=df1a2dec263d5c01
JOB_DISPLAY_URL=http://192.168.139.131:8080/job/java-devops-demo/display/redirect
JOB_NAME=java-devops-demo
COPY_REFERENCE_FILE_LOG=/var/jenkins_home/copy_reference_file.log
JAVA_HOME=/opt/java/openjdk
PWD=/var/jenkins_home/workspace/java-devops-demo
GIT_PREVIOUS_COMMIT=ac6eb8d76c8ae1b50cb5225c62470311338e5be6
WORKSPACE_TMP=/var/jenkins_home/workspace/java-devops-demo@tmp
65、jenkins环境检查
我们目前从gitee中拿到了代码,但是我们把代码从gitee中拉取下来(需要git环境),然后把代码打成jar包(需要maven环境),并且未来要根据Dockerfile文件生成镜像(需要docker环境),所以需要检查一下相关环境,比如我们就检查一下java、git、docker、maven环境吧,在Jenkinsfile文件中添加脚本如下:
最终结果如下:
所以可以看出maven环境是缺乏的
66、gitee远程触发jenkins自动构建
66.1、生成用户token
选择系统管理:
点击安全》管理用户:
然后点击设置》添加新Token》生成按钮
,如下:
复制token之后,点击应用和保存按钮,如下:
66.2、获取远程构建链接
进入项目之中,点击配置按钮,如下:
设置一个合适的令牌名称,如下:
回到刚才项目中的配置页面的身份验证令牌下面,复制远程触发链接,如下:
其中JENKINS_URL
对应Jenkins中系统管理》系统配置》 Jenkins Location》Jenkins URL的值,一般都是Jenkins的ip和端口,如下:
然后webhook
回调地址拼接方式是:
http://用户名:用户token@JENKINS_URL的IP:JENKINS_URL的Port/job/java-devops-demo/build?token=身份验证令牌
结合上一步生成的token(即:11631e11e42b9cbf6859819c41c057270c)、token对应用户名称(即:root)、本次身份验证令牌(即:mingkuaidexuanmi61)、本次远程回调链接(即:JENKINS_URL/job/java-devops-demo/build?token=TOKEN_NAME),可知本次webhook
回调地址是:
http://root:11631e11e42b9cbf6859819c41c057270c@192.168.139.131:8080/job/java-devops-demo/build?token=mingkuaidexuanmi61
66.3、设置gitee中的webhook
添加webhook,如下:
将上面的webhook
回调地址填入gitee中,如下:
后续我们就可以实现推送代码到gitee,然后gitee直接回调jenkins了,这样就不用我们去jenkins上手动构建了
67、Jenkins插件安装
67.1、每个阶段使用一个代理
对应链接:https://www.jenkins.io/zh/doc/book/pipeline/docker/
67.2、总流程中写agent none和agent any的区别
- agent none:每一个stage都需要写agent才行
- agent any:不需要每一个stage都需要写agent
67.3、更改jenkins插件镜像源
虽然不更改也行,但是更改成国内jenkins插件镜像源之后下载插件速度更快一点
点击系统管理按钮,如下:
点击插件管理按钮,如下:
点击高级按钮,如下:
然后把页面滑到最下方,使用https://jenkins-zh.gitee.io/update-center-mirror/tsinghua/update-center.json
或者 http://mirror.xmission.com/jenkins/updates/current/update-center.json
替换掉升级站点》URL下文本框中的内容,点击保存按钮,如下:
68、使用基础网络
青云配置,我不需要配置
69、jenkins插件安装
推荐安装插件列表:
70、自定义maven代理,使用自定义配置文件
我们需要使用maven来为项目打包,那就需要一个maven依赖,我们可以使用不同阶段使用不同容器的方式来完成效果,在项目Jenkinsfile
文件中添加内容如下:
我们在运行jenkins容器的时候,使用Docker镜像代理的方式来为Jenkins容器在Linux虚拟机上启动了一个maven容器,不过maven容器依然可以使用jenkins容器的目录,所以我们把settings.xml
放在jenkins的/var/jenkins_home/
目录中,这样可以在移动jenkins的时候,直接将maven配置文件也移动了,并且我们已经把该目录挂载出来了,所以我们可以直接将整理好的settings.xml
文件放在上述目录中,另外在settings.xml中需要指定仓库地址,我们也把仓库建立在jenkins的/var/jenkins_home/
目录中,这样就可以循环利用仓库内容,其中settings.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>/var/jenkins_home/data/maven_repository</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
<mirror>
<id>central</id>
<name>central</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
<profiles>
<!-- 自己配置,目的是避免使用错误的jre版本 -->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
</settings>
画图分析上述内容如下所示:
我们来看一下执行效果吧,如下:
上述说明了我们使用放在jenkins
的/var/jenkins_home/
目录中的settings.xml
和maven_repository
。对于settings.xml
来说,便于迁移jenkins的时候同步迁移maven配置文件。对于maven仓库来说,已经下载过的jar包就不需要在下载了,可以减少项目构建所花的时间。
以上是一种做法,另外一种做法是执行linux中的挂载位置,这种做法在迁移jenkins的时候容易忽略settings.xml
,不建议使用,有兴趣的朋友可以去看容器的缓存数据:
71、docker maven完全加速起来
改变maven本地仓库位置的目的:
在上一集视频中我们提到可以把maven容器的settings.xml
和maven本地仓库都放在jenkins
的/var/jenkins_home/
目录中,这样可以在移植jenkins的时候不用在单独操心maven的settings.xml
配置文件,另外还可以让每次mvn clean package
命令执行的时候可以利用上一次命令执行下载的jar包
对于maven容器的settings.xml
,我这边没有什么异议,不过maven本地仓库我这边还是有异议的,上次我们把maven本地仓库也放在了jenkins
的/var/jenkins_home/
目录中,这样造成一个问题是我们在迁移jenkins目录时还需要将maven本地仓库先删除,这样不太好
我们本次的做法是将maven本地仓库从jenkins
的/var/jenkins_home/
目录中抽取出来,但是依然起到maven命令执行加速的作用(本次mvn clean package
的命令执行可以利用到上次命令执行产生的maven本地仓库jar包)
改变maven本地仓库位置的方案:
改变settings.xml
中的仓库位置,如下:
在IDEA的项目中Jenkinsfile
文件中添加maven的挂载信息,如下:
将Jenkinsfile
提交到Gitee中,然后在Jenkins中构建即可,发现mvn clean package
的执行时间也不长
72、简单Jenkins流水线完成
创建Dockerfile:
# 基础镜像
FROM openjdk:8-jre-alpine
# 修改时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
# 复制打成的jar包
COPY /target/*.jar app.jar
# 定义参数
ENV JAVA_OPTS=""
ENV PARAMS=""
# 暴露端口,和springboot程序中的端口对应
EXPOSE 30001
# 启动命令
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]
修改Jenkinsfile文件:
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
environment {
WS = "${WORKSPACE}"
}
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、环境检查
// 注意:单引号:一般包括常量字符串,无法识别变量;
// 双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来,就可以被识别
stage('环境检查') {
steps {
echo '环境检查……'
sh 'java -version'
sh 'git --version'
sh 'docker version'
}
}
// 2、编译
stage('编译') {
agent {
docker {
image 'maven:3-alpine'
args '-v /data/maven/repository:/root/maven/repository'
}
}
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
sh 'pwd & ls -alh'
sh 'mvn --version'
echo "当前工作目录:${WORKSPACE}"
echo "常态工作目录:${WS}"
/**
* 需要做:
* 每一个stage阶段都是从“常态工作目录”开始,但是部分阶段会进入临时目录,比如本次就是这种情况
* 我们需要在打包阶段也需要获取到target中的jar包,但是目前mvn clean package命令在
* 临时目录中执行,当我们进入“打包”阶段的时候不会发现打成的jar包,所以我们需要让mvn clean package
* 命令在“常态工作目录”中执行,然后进入“打包”阶段也能看到生成的jar包
*
* 怎么做:
* 我们先进入“常态工作目录”,然后执行“mvn clean package”命令,我们不能在该阶段中通过
* ${WORKSPACE}获取工作目录,毕竟当前工作目录是临时目录,所以我们在上面“定义环境信息”处已经
* 记录了“常态工作目录”的路径,所以我们在这里使用就ok了
* 由于每一行指令都是基于“当前工作目录”,所以必须将“cd ${WS}”和后面的指令写在一行,否则
* 执行位置就不正确了
*/
sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/config/maven/settings.xml" -Dmaven.test.skip=true && pwd && ls -alh'
}
}
// 3、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 4、打包
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
sh 'docker rmi -f java-devops-demo'
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
}
// 5、部署
stage('部署') {
steps {
echo '部署……'
sh 'docker rm -f java-devops-demo-item'
sh 'docker run -d -P --name=java-devops-demo-item java-devops-demo'
sh 'docker ps | grep java-devops-demo-item'
}
}
}
}
- 修改环境信息:添加
WS = "${WORKSPACE}"
,用以存储常态工作空间,让mvn clean package
可以在常态工作空间执行 - 修改编译阶段:主要做的是切换
mvn clean package
的执行位置为常态工作空间 - 添加打包阶段:其中执行
docker rmi -f java-devops-demo
的作用是删除以往的镜像,避免出现虚悬镜像 - 添加部署阶段:其中执行
docker rm -f java-devops-demo-item
的作用是删除正在运行的容器,然后docker run
命令的作用是按照后台方式运行容器,并且以随机端口形式暴露Dockerfile
中的端口
73、发送邮件通知
73.1、后置阶段的使用
查找位置:
用在stage中:
说明:如果本阶段成功,那就打印成功,否则就打印失败,当然也可以在成功或者失败里面做其他事情
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
sh 'docker rmi -f java-devops-demo'
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
// 后置执行
post {
// 成功
success {
echo '成功'
}
// 失败
failure {
echo '失败'
}
}
}
效果截图:
用在pipeline中:
说明:如果所有阶段都成功,那就在最后一个阶段的最后打印成功,有任何一个阶段失败,那就会打印失败,当然也可以在成功或者失败里面做其他事情
pipeline {
……………………
// 后置执行
post {
// 成功
success {
echo '成功'
}
// 失败
failure {
echo '失败'
}
}
}
73.2、完成邮件发送功能
73.2.1、获取邮箱授权码
我的邮箱是QQ邮箱,所以肯定去QQ邮箱官网查找授权码了,具体方式是登录QQ邮箱,然后按照如下方式获取授权码,比如我的就是:bxyxzwlsizpaecah
73.2.1、配置系统管理员邮件地址
进入Jenkins
,然后点击系统管理》系统配置
,然后找到系统管理员邮件地址
,配置一个邮件地址,如下:
73.2.3、配置 Extended E-mail Notification
进入Jenkins
,然后点击系统管理》系统配置
,然后找到 Extended E-mail Notification
,配置如下内容
Credentials
信息的填写需要点击高级按钮才会出现奥
我们先说一下上面SMTP server
和SMTP Port
的内容是从哪里来的,它们来自于QQ邮箱官网,具体位置如下:
然后找到如下位置:
我们说一下上面的添加按钮吧,它可以用来添加邮箱用户和授权码信息,点击添加按钮之后填写如下内容:
添加完成后就可以在Credentials
下面去选中刚才添加的邮箱连接信息了,现在我们继续往下说
配置发送内容为html格式,如下:
73.2.4、配置邮件通知
进入Jenkins
,然后点击系统管理》系统配置
,然后找到邮件通知
,配置如下内容
配置完成之后可以测试一下连通情况,如下:
没有问题记得应用和保存
73.2.5、获取邮件Html模板
我是直接使用https://blog.csdn.net/shenshenruoxi/article/details/106222851里面的模板,具体内容是:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${PROJECT_NAME}-第${BUILD_NUMBER}次构建日志</title>
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0"
style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>(本邮件是程序自动下发的,请勿回复!)<br/></td>
</tr>
<tr>
<td><h2>
<font color="#0000FF">构建结果 - ${BUILD_STATUS}</font>
</h2></td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>项目名称 : ${PROJECT_NAME}</li>
<li>构建编号 : 第${BUILD_NUMBER}次构建</li>
<li>触发原因: ${CAUSE}</li>
<li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
<li>构建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>工作目录 : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
</ul>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td><b><font color="#0B610B">构建情况总览:</font></b>${TEST_COUNTS,var="fail"}<br/>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><pre cols="160" rows="80"
style="font-family: Courier New">${BUILD_LOG,maxLines=250}</pre>
</td>
</tr>
</table>
</body>
</html>
73.2.6、获取Jenkinsfile中需要填写的内容
在Jenkins中进入对应的流水线任务,点击流水线语法按钮,执行如下操作:
然后将内容复制到IDEA中Jenkinsfile中,大家只用看邮件通知
即可,如下:
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
environment {
WS = "${WORKSPACE}"
}
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、环境检查
// 注意:单引号:一般包括常量字符串,无法识别变量;
// 双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来,就可以被识别
stage('环境检查') {
steps {
echo '环境检查……'
sh 'java -version'
sh 'git --version'
sh 'docker version'
}
}
// 2、编译
stage('编译') {
agent {
docker {
image 'maven:3-alpine'
args '-v /data/maven/repository:/root/maven/repository'
}
}
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
sh 'pwd & ls -alh'
sh 'mvn --version'
echo "当前工作目录:${WORKSPACE}"
echo "常态工作目录:${WS}"
/**
* 需要做:
* 每一个stage阶段都是从“常态工作目录”开始,但是部分阶段会进入临时目录,比如本次就是这种情况
* 我们需要在打包阶段也需要获取到target中的jar包,但是目前mvn clean package命令在
* 临时目录中执行,当我们进入“打包”阶段的时候不会发现打成的jar包,所以我们需要让mvn clean package
* 命令在“常态工作目录”中执行,然后进入“打包”阶段也能看到生成的jar包
*
* 怎么做:
* 我们先进入“常态工作目录”,然后执行“mvn clean package”命令,我们不能在该阶段中通过
* ${WORKSPACE}获取工作目录,毕竟当前工作目录是临时目录,所以我们在上面“定义环境信息”处已经
* 记录了“常态工作目录”的路径,所以我们在这里使用就ok了
* 由于每一行指令都是基于“当前工作目录”,所以必须将“cd ${WS}”和后面的指令写在一行,否则
* 执行位置就不正确了
*/
sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/config/maven/settings.xml" -Dmaven.test.skip=true && pwd && ls -alh'
}
}
// 3、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 4、打包
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
sh 'docker rmi -f java-devops-demo'
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
// 后置执行
post {
// 成功
success {
// One or more steps need to be included within each condition's block.
echo '成功'
}
// 失败
failure {
// One or more steps need to be included within each condition's block.
echo '失败'
}
}
}
// 5、部署
stage('部署') {
steps {
echo '部署……'
sh 'docker rm -f java-devops-demo-item'
sh 'docker run -d -P --name=java-devops-demo-item java-devops-demo'
sh 'docker ps | grep java-devops-demo-item'
}
}
// 6、邮件通知
stage('邮件通知') {
steps {
echo '邮件通知……'
emailext body: '''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${PROJECT_NAME}-第${BUILD_NUMBER}次构建日志</title>
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0"
style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>(本邮件是程序自动下发的,请勿回复!)<br/></td>
</tr>
<tr>
<td><h2>
<font color="#0000FF">构建结果 - ${BUILD_STATUS}</font>
</h2></td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>项目名称 : ${PROJECT_NAME}</li>
<li>构建编号 : 第${BUILD_NUMBER}次构建</li>
<li>触发原因: ${CAUSE}</li>
<li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
<li>构建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>工作目录 : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
</ul>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td><b><font color="#0B610B">构建情况总览:</font></b>${TEST_COUNTS,var="fail"}<br/>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><pre cols="160" rows="80"
style="font-family: Courier New">${BUILD_LOG,maxLines=250}</pre>
</td>
</tr>
</table>
</body>
</html>
''', subject: '${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志', to: '2746711685@qq.com'
}
}
}
}
最终执行结果是正确的,这是Jenkins的执行结果
这是接收到的邮件内容
73.3、如何在Jenkinsfile中把消息通过短信形式发送到手机,或者发送到微信、钉钉、飞书等
各大平台都支持curl方式的内容发送,所以我们可以通过以下方式把信息发给其他用户
sh "curl XXX"
74、CICD还能做什么?
74.1、推送镜像到阿里云镜像仓库
74.1.1、推送目的
如果是单个虚拟机上装docker、k8s、jenkins还好,如果是多个机器呢,那我们未来使用k8s的yaml文件进行构建的时候可能本地没有最新镜像,那我们就需要从镜像仓库中下载,并且我们构建的镜像总要有一个位置去存储它,不可能让他一直在jenkins所在虚拟机中,这也不利于版本维护,目前我们测试推送镜像到阿里云镜像仓库,后续在k8s中我们将通过Jenkinsfile文件把生成的镜像推送到harbor仓库中,然后在k8s通过yaml部署项目的时候就可以从harbor中拉取最新镜像
74.1.2、新建阿里云账号信息
首先需要登录阿里云,然后点击控制台,如下:
然后找到容器镜像服务
,如下:
然后点击实例列表中的个人版,没开通的记得开通下,如下:
依次点击仓库管理》镜像仓库》创建镜像仓库
,如下:
在弹窗中填写如下信息:
然后创建本地仓库即可,如下:
创建完成后就可以进入镜像仓库的基本信息页面,如下:
上图中那几个将镜像推送到仓库的步骤等会我们往阿里云镜像仓库推送镜像的时候会用到
74.1.3、设置阿里云账号信息
在阿里云的容器镜像服务中,点击实例列表中的个人版,之后设置一个阿里云镜像仓库连接密码
74.1.4、在jenkins中配置阿里云镜像仓库的用户名和密码连接信息
首先进入jenkins中,然后先进入用户名和密码添加页面,如下:
按照以下步骤进入用户名和密码设置页面,如下:
然后填写阿里云镜像仓库的连接信息,如下:
74.1.5、更改Jenkinsfile
74.1.5.1、使用阿里云镜像仓库的第1种连接方式
-
environment
中可以看到ALIYUN_DOCKER_REPO = credentials('aliyun-docker-repo')
首先aliyun-docker-repo
就是我们上面设置的阿里云镜像仓库连接信息的ID,就在上面那张图片里面,我就不粘贴图片了
而ALIYUN_DOCKER_REPO = credentials('aliyun-docker-repo')
这种写法来自于https://www.jenkins.io/zh/doc/book/pipeline/syntax/#environment,另外这种写法将会产生两个额外变量,也就是ALIYUN_DOCKER_REPO_USR
(用户名)ALIYUN_DOCKER_REPO_PSW
(密码),其实也就是在变量的基础上添加了_USR
和_PSW
,这是Jenkins规定的方式,截图如下:
-
stage('推送镜像')
中用到的命令来自于阿里云,除了登录命令需要设置用户名和密码,这和阿里云的设置不一样,其他都是一样的
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
environment {
WS = "${WORKSPACE}"
// 阿里云连接信息
ALIYUN_DOCKER_REPO = credentials('aliyun-docker-repo')
}
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、环境检查
// 注意:单引号:一般包括常量字符串,无法识别变量;
// 双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来,就可以被识别
stage('环境检查') {
steps {
echo '环境检查……'
sh 'java -version'
sh 'git --version'
sh 'docker version'
}
}
// 2、编译
stage('编译') {
agent {
docker {
image 'maven:3-alpine'
args '-v /data/maven/repository:/root/maven/repository'
}
}
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
sh 'pwd & ls -alh'
sh 'mvn --version'
echo "当前工作目录:${WORKSPACE}"
echo "常态工作目录:${WS}"
/**
* 需要做:
* 每一个stage阶段都是从“常态工作目录”开始,但是部分阶段会进入临时目录,比如本次就是这种情况
* 我们需要在打包阶段也需要获取到target中的jar包,但是目前mvn clean package命令在
* 临时目录中执行,当我们进入“打包”阶段的时候不会发现打成的jar包,所以我们需要让mvn clean package
* 命令在“常态工作目录”中执行,然后进入“打包”阶段也能看到生成的jar包
*
* 怎么做:
* 我们先进入“常态工作目录”,然后执行“mvn clean package”命令,我们不能在该阶段中通过
* ${WORKSPACE}获取工作目录,毕竟当前工作目录是临时目录,所以我们在上面“定义环境信息”处已经
* 记录了“常态工作目录”的路径,所以我们在这里使用就ok了
* 由于每一行指令都是基于“当前工作目录”,所以必须将“cd ${WS}”和后面的指令写在一行,否则
* 执行位置就不正确了
*/
sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/config/maven/settings.xml" -Dmaven.test.skip=true && pwd && ls -alh'
}
}
// 3、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 4、打包
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
// 删除最新镜像
sh 'docker rmi -f java-devops-demo'
// 删除该版本镜像,避免产生虚悬镜像
sh 'docker rmi -f java-devops-demo:v1.0'
// 删除改名的镜像,避免产生虚悬镜像
sh 'docker rmi -f registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0'
// 构建镜像
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
}
// 5、推送镜像
stage('推送镜像') {
steps {
echo '推送镜像……'
sh "docker login -u ${ALIYUN_DOCKER_REPO_USR} -p ${ALIYUN_DOCKER_REPO_PSW} registry.cn-hangzhou.aliyuncs.com"
sh "docker tag java-devops-demo registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0"
sh "docker push registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0"
}
}
}
}
74.1.5.2、使用阿里云镜像仓库的第2种连接方式
在推送镜像
中,大家可以看到如下结构:
withCredentials([usernamePassword(credentialsId: 'aliyun-docker-repo', passwordVariable: 'aliyun_docker_pwd', usernameVariable: 'aliyun_docker_usr')]) {
XXX
}
这种结构来自于Jenkins流水线语法,我们可以进入单个项目,然后点击左侧的流水线语法,之后选择如下选项:
之后我们在绑定下面点击新增按钮,选择最后一个选项,用来分别生成用户名称和用户密码,然后我们给用户名和用户密码取个名字,分别是aliyun_docker_usr
和aliyun_docker_pwd
可以看上面的代码,其中aliyun-docker-repo
是阿里云镜像仓库连接信息的ID,这个上面是说过的,然后aliyun_docker_usr
和aliyun_docker_pwd
就是我们为阿里云镜像仓库连接信息取的名字
只要把代码写在withCredentials
方法里面,就可以把aliyun_docker_usr
和aliyun_docker_pwd
当做变量来使用
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
environment {
WS = "${WORKSPACE}"
}
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、环境检查
// 注意:单引号:一般包括常量字符串,无法识别变量;
// 双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来,就可以被识别
stage('环境检查') {
steps {
echo '环境检查……'
sh 'java -version'
sh 'git --version'
sh 'docker version'
}
}
// 2、编译
stage('编译') {
agent {
docker {
image 'maven:3-alpine'
args '-v /data/maven/repository:/root/maven/repository'
}
}
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
sh 'pwd & ls -alh'
sh 'mvn --version'
echo "当前工作目录:${WORKSPACE}"
echo "常态工作目录:${WS}"
/**
* 需要做:
* 每一个stage阶段都是从“常态工作目录”开始,但是部分阶段会进入临时目录,比如本次就是这种情况
* 我们需要在打包阶段也需要获取到target中的jar包,但是目前mvn clean package命令在
* 临时目录中执行,当我们进入“打包”阶段的时候不会发现打成的jar包,所以我们需要让mvn clean package
* 命令在“常态工作目录”中执行,然后进入“打包”阶段也能看到生成的jar包
*
* 怎么做:
* 我们先进入“常态工作目录”,然后执行“mvn clean package”命令,我们不能在该阶段中通过
* ${WORKSPACE}获取工作目录,毕竟当前工作目录是临时目录,所以我们在上面“定义环境信息”处已经
* 记录了“常态工作目录”的路径,所以我们在这里使用就ok了
* 由于每一行指令都是基于“当前工作目录”,所以必须将“cd ${WS}”和后面的指令写在一行,否则
* 执行位置就不正确了
*/
sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/config/maven/settings.xml" -Dmaven.test.skip=true && pwd && ls -alh'
}
}
// 3、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 4、打包
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
// 删除最新镜像
sh 'docker rmi -f java-devops-demo'
// 删除该版本镜像,避免产生虚悬镜像
sh 'docker rmi -f java-devops-demo:v1.0'
// 删除改名的镜像,避免产生虚悬镜像
sh 'docker rmi -f registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0'
// 构建镜像
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
}
// 5、推送镜像
stage('推送镜像') {
steps {
echo '推送镜像……'
withCredentials([usernamePassword(credentialsId: 'aliyun-docker-repo', passwordVariable: 'aliyun_docker_pwd', usernameVariable: 'aliyun_docker_usr')]) {
sh "docker login -u ${aliyun_docker_usr} -p ${aliyun_docker_pwd} registry.cn-hangzhou.aliyuncs.com"
sh "docker tag java-devops-demo registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0"
sh "docker push registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0"
}
}
}
}
}
74.2、input语法—手动确认
74.2.1、input语法说明
首先我们先说一下input手动确认的意义,比如现在有一些参数无法确定,需要在运行的时候执行,那就需要用到input手动确定了
然后我们来看一下官方的input语法地址https://www.jenkins.io/zh/doc/book/pipeline/syntax/#input,相关的解释也是有说明的
接下来我们来说明一下input语法的生成情况,首先我们在jenkins中找到对应项目,然后进入项目,之后打开项目左侧的流水线语法,如下:
然后点击左侧的Declarative Directive Generator
,之后在右侧点击input:input
,如下:
我们在消息中输入交互式输入的标题,输入完成后点击下面的高级…
按钮,如下:
然后起一个确认按钮标题
的名称,如下:
点击新增
按钮,我们先来说一个文本参数
的写法,如下:
下面的镜像版本IMAGE_VERSION
是给我们脚本中用的,默认版本是不填的时候用的,提示信息是给用户看的,如下:
然后我们再来说一个可以选择框的情况,点击新增
按钮,点击选项参数
,如下:
然后填写相关内容,如下:
74.2.2、Jenkinsfile脚本
大家直接去看里面的input语法就好
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
environment {
WS = "${WORKSPACE}"
}
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、环境检查
// 注意:单引号:一般包括常量字符串,无法识别变量;
// 双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来,就可以被识别
stage('环境检查') {
steps {
echo '环境检查……'
sh 'java -version'
sh 'git --version'
sh 'docker version'
}
}
// 2、编译
stage('编译') {
agent {
docker {
image 'maven:3-alpine'
args '-v /data/maven/repository:/root/maven/repository'
}
}
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
sh 'pwd & ls -alh'
sh 'mvn --version'
echo "当前工作目录:${WORKSPACE}"
echo "常态工作目录:${WS}"
/**
* 需要做:
* 每一个stage阶段都是从“常态工作目录”开始,但是部分阶段会进入临时目录,比如本次就是这种情况
* 我们需要在打包阶段也需要获取到target中的jar包,但是目前mvn clean package命令在
* 临时目录中执行,当我们进入“打包”阶段的时候不会发现打成的jar包,所以我们需要让mvn clean package
* 命令在“常态工作目录”中执行,然后进入“打包”阶段也能看到生成的jar包
*
* 怎么做:
* 我们先进入“常态工作目录”,然后执行“mvn clean package”命令,我们不能在该阶段中通过
* ${WORKSPACE}获取工作目录,毕竟当前工作目录是临时目录,所以我们在上面“定义环境信息”处已经
* 记录了“常态工作目录”的路径,所以我们在这里使用就ok了
* 由于每一行指令都是基于“当前工作目录”,所以必须将“cd ${WS}”和后面的指令写在一行,否则
* 执行位置就不正确了
*/
sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/config/maven/settings.xml" -Dmaven.test.skip=true && pwd && ls -alh'
}
}
// 3、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 4、打包
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
// 删除最新镜像
sh 'docker rmi -f java-devops-demo'
// 删除该版本镜像,避免产生虚悬镜像
sh 'docker rmi -f java-devops-demo:v1.0'
// 删除改名的镜像,避免产生虚悬镜像
sh 'docker rmi -f registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0'
// 构建镜像
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
}
// 5、推送镜像
stage('推送镜像') {
input {
message '需要推送镜像到阿里云仓库吗?'
ok '需要'
parameters {
text defaultValue: 'v1.0', description: '生产环境需要部署的版本', name: 'IMAGE_VERSION'
choice choices: ['华东区', '华南区', '华中区', '华北区'], description: '部署大区', name: 'DEPLOY_WHERE'
}
}
steps {
echo '推送镜像……'
echo "注意:服务将被部署到“${DEPLOY_WHERE}”"
withCredentials([usernamePassword(credentialsId: 'aliyun-docker-repo', passwordVariable: 'aliyun_docker_pwd', usernameVariable: 'aliyun_docker_usr')]) {
sh "docker login -u ${aliyun_docker_usr} -p ${aliyun_docker_pwd} registry.cn-hangzhou.aliyuncs.com"
sh "docker tag java-devops-demo registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:${IMAGE_VERSION}"
sh "docker push registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:${IMAGE_VERSION}"
}
}
}
}
}
然后我们来看下jenkins流水线的运行截图,如下:
74.3、script语法
下面的Jenkinsfile中只是简单说了script语法,里面介绍了if else if else
的用法,其实script语法可以帮助我们做的事情非常多,比如多分支流水线构建等,如下:
// 写流水线的脚本(常用:声明式、脚本式)
pipeline {
// 全部CICD流程都需要在这里定义
// 在任何可用的代理上,执行流水线或它的任何阶段。
agent any
// 定义环境信息
environment {
WS = "${WORKSPACE}"
}
// 定义流水线加工流程,定义流水线所有阶段
stages {
// 定义阶段
// 1、环境检查
// 注意:单引号:一般包括常量字符串,无法识别变量;
// 双引号:可以用来包括常量字符串和变量,其中变量以$开头,或者用${}包裹起来,就可以被识别
stage('环境检查') {
steps {
echo '环境检查……'
sh 'java -version'
sh 'git --version'
sh 'docker version'
}
}
// 2、编译
stage('编译') {
agent {
docker {
image 'maven:3-alpine'
args '-v /data/maven/repository:/root/maven/repository'
}
}
// 定义步骤
steps {
// 要做的所有事情
echo '编译……'
sh 'pwd & ls -alh'
sh 'mvn --version'
echo "当前工作目录:${WORKSPACE}"
echo "常态工作目录:${WS}"
/**
* 需要做:
* 每一个stage阶段都是从“常态工作目录”开始,但是部分阶段会进入临时目录,比如本次就是这种情况
* 我们需要在打包阶段也需要获取到target中的jar包,但是目前mvn clean package命令在
* 临时目录中执行,当我们进入“打包”阶段的时候不会发现打成的jar包,所以我们需要让mvn clean package
* 命令在“常态工作目录”中执行,然后进入“打包”阶段也能看到生成的jar包
*
* 怎么做:
* 我们先进入“常态工作目录”,然后执行“mvn clean package”命令,我们不能在该阶段中通过
* ${WORKSPACE}获取工作目录,毕竟当前工作目录是临时目录,所以我们在上面“定义环境信息”处已经
* 记录了“常态工作目录”的路径,所以我们在这里使用就ok了
* 由于每一行指令都是基于“当前工作目录”,所以必须将“cd ${WS}”和后面的指令写在一行,否则
* 执行位置就不正确了
*/
sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/config/maven/settings.xml" -Dmaven.test.skip=true && pwd && ls -alh'
}
}
// 3、测试
stage('测试') {
steps {
echo '测试……'
}
}
// 4、打包
stage('打包') {
steps {
echo '打包……'
sh 'pwd & ls -alh'
// 删除最新镜像
sh 'docker rmi -f java-devops-demo'
// 删除该版本镜像,避免产生虚悬镜像
sh 'docker rmi -f java-devops-demo:v1.0'
// 删除改名的镜像,避免产生虚悬镜像
sh 'docker rmi -f registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:v1.0'
// 构建镜像
sh 'docker build -t java-devops-demo -f Dockerfile .'
}
}
// 5、推送镜像
stage('推送镜像') {
input {
message '需要推送镜像到阿里云仓库吗?'
ok '需要'
parameters {
text defaultValue: 'v1.0', description: '生产环境需要部署的版本', name: 'IMAGE_VERSION'
choice choices: ['华东区', '华南区', '华中区', '华北区'], description: '部署大区', name: 'DEPLOY_WHERE'
}
}
steps {
echo '推送镜像……'
script {
// groovy语法
def where = "${DEPLOY_WHERE}"
if (where == "华东区") {
echo "注意:服务将被部署到“${DEPLOY_WHERE}”"
} else if(where == "华南区") {
echo "注意:服务将被部署到“${DEPLOY_WHERE}”"
} else if(where == "华中区") {
echo "注意:服务将被部署到“${DEPLOY_WHERE}”"
} else if(where == "华北区") {
echo "注意:服务将被部署到“${DEPLOY_WHERE}”"
withCredentials([usernamePassword(credentialsId: 'aliyun-docker-repo', passwordVariable: 'aliyun_docker_pwd', usernameVariable: 'aliyun_docker_usr')]) {
sh "docker login -u ${aliyun_docker_usr} -p ${aliyun_docker_pwd} registry.cn-hangzhou.aliyuncs.com"
sh "docker tag java-devops-demo registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:${IMAGE_VERSION}"
sh "docker push registry.cn-hangzhou.aliyuncs.com/mingkuaidexuanmi61/java-devops-demo:${IMAGE_VERSION}"
}
} else {
sh "注意:没有合适的部署地点"
}
}
}
}
}
}
具体效果如下:
75、其他问题
没啥需要总结的
更多推荐
所有评论(0)