跳转至

DevOps概述

一. DevOps介绍

1. 背景

软件开发最开始是由两个团队组成

  • 开发计划由开发团队从头开始设计和整体系统的构建。需要系统不停的迭代更新
  • 运维团队将开发团队的Code进行测试后部署上线,希望系统稳定安全的运行

在这过程中开发团队制定好计划并完成coding后,需要提供给运维团队。同样,运维团队将也会向开发团队反馈需要修复的bug以及一些需要返工的任务。如果这时候开发团队等待运维团队的反馈就会延长时间,导致推迟了整个软件开发的周期

为了保证更敏捷是的开发,就需要把开发和运维整合到同一个团队,这就是DevOps

DevOps,字面意思就是 Develoment&Operations 的缩写,也就是开发&运维。

核心就在于简化开发和运维团队之间的流程,使得整体的软件开发过程更快速

2. 整体的软件开发流程包括

  • PLAN: 开发团队根据客户的需求制定开发计划
  • CODE: 根据PLAN开始编码过程,需要将不同版本的代码存储在同一个库中
  • BUILD: 编码完成后,需要将代码构建并运行
  • TEST: 成功构建项目后,需要测试代码是否存在BUG或其他错误
  • DEPLOY: 代码警告手动测试和自动化测试后,认定代码以及准备好部署并且交给运维团队
  • OPERATE: 运维团队将代码部署到生产环境中
  • MONITOR: 项目部署上线以后,需要持续监控的产品
  • INTEGRATE: 最好将监控阶段收到的反馈发送回PLAN阶段,整体反复的留着就是DevOps的核心。即持续集成,持续部署。

3. DevOps的最终定义

DevOps强调的是高校组织团队之间如何通过自动化工具协作和沟通来完成软件的声明周期管理,从而更快更频繁的交付更稳定的软件

二. GitOps平台部署计划

主机名 IP地址 安装服务名 对应阶段
1 192.168.100.99 git,docker,gitlab CODE
2 192.168.100.100 docker,maven,jenkins BUILD/INTEGRATE
3 192.168.100.101 docker,sonarqube OPERATE

三. Code阶段工具

在code阶段,我们需要将不同版本的代码存储到一个仓库中,场景的版本控制工具就是SVN或者Git,这里我们采用Git作为版本控制工具,GitLab作为远程仓库。

一般开发人员和jenkins需要使用git作为版本控制工具,开发人员使用git作为客户端工具进行拉取码,jenkins可以通过安装git插件来实现从远处仓库拉取代码

我们在1上部署Code阶段工具

1. Git安装

直接使用命令 dnf -y install git 安装git

[root@K8s1 ~]# git --version
git version 2.47.1

2. GitLab安装

单独准备服务器,采用docker容器部署GitLab

前提是先部署好docker环境和docker compose

1) 安装docker

这里我们可以参考 docker安装文档就不多说了。同时其他几台服务器也都需要安装docker,我们这里就一起安装了

[root@my-project dockerinstall]# ansible -m command -a "docker --version" "K8s*"
3 | CHANGED | rc=0 >>
Docker version 29.7.2, build a7dcaa6
2 | CHANGED | rc=0 >>
Docker version 29.7.2, build a7dcaa6
1 | CHANGED | rc=0 >>
Docker version 29.7.2, build a7dcaa6

2) 安装GitLab

a. 如果我们没有gitlab的镜像可以手动拉取

# 查看gitlab镜像
docker search gitlab-ce
# 拉取gitlab镜像
docker pull gitlab/gitlab-ce

或者将已有的镜像导入

docker load -i /root/gitlab.tar

b. 创建目录

mkdir -p /usr/local/docker/gitlab-docker
cd /usr/local/docker/gitlab-docker

c. 准备docker-compose.yaml文件

在/usr/local/docker/gitlab-docker下创建docker-compose.yaml文件

version: '3.1'
services:
  gitlab:
    image: 'gitlab/gitlab-ce:latest'
    container_name: gitlab
    restart: always
    environment:
      GITLAB_OMNIBUS_CONFIG: |
      external_url 'http://192.168.100.99:8929'
      gitlab _rails['gigtlab_shell_ssh_port'] = 2224
    ports:
    - '8929:8929'
    - '2224:2224'
    volumes:
    - './config:/etc/gitlab'
    - './logs:/var/log/gitlab'
    - './data:/var/opt/gitlab'

这其中要挂载的目录中/etc/gitalbyongyucunchugitlab的配置文件,/var/log/gitlab用于存储日志,/var/opt/gitlab用于存储应用数据

# 启动容器
[root@K8s1 gitlab-docker]# docker compose up -d
WARN[0000] /usr/local/docker/gitlab-docker/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion 
[+] up 2/2
 ✔ Network gitlab-docker_default Created                                                                                    0.0s
 ✔ Container gitlab              Started   
# 查看gitlab
[root@K8s1 gitlab-docker]# docker compose logs -f
...

然后我们就可以访问gitlab了

3) 配置gitlab

a. 浏览器输入http://192.168.100.99:8929访问GitLab首页

alt text

b. 在终端中查看root用户初始密码

[root@K8s1 gitlab-docker]# docker exec -it gitlab cat /etc/gitlab/initial_root_password
...
Password: FRhgz3OTAy75mDKN+akgryhrJ25I/cpo5nqXOX4kiiw=

c. 首次以root身份登录进行配置

alt text

d. 然后修改登陆密码

alt textalt text

都搞定后gitlab就可以像Gitee,GitHub一样使用了

四. build阶段工具

再单独准备一台服务器K8s2。我们此处使用Maven作为项目的编译工具,并安装Jenkins

具体的maven安装流程可以参考这里我们可以参考 Docker镜像仓库就不多说了。

a. 我本地有就直接解压了

ansible -m unarchive -a 'src=/root/DevOps/2026_08_24_GitOps/jdk-8u333-linux-x64.tar.gz dest=/usr/local/ remote_src=no' "2"
ansible -m unarchive -a 'src=/root/DevOps/2026_08_24_GitOps/apache-maven-3.8.5.tgz dest=/usr/local/ remote_src=no' "2"

因为不需要在宿主机做java的编译操作,这里就不再宿主机做jdk环境变量设置了,jenkins是需要jdk和maven环境。我们把jenkins放在容器里面运行,到时候直接通过数据卷映射到容器就可以了

b. 设置maven

vi /usr/local/apache-maven-3.8.5/conf/settings.xml 

settings.xml 是 Maven 的配置文件,这里主要改动两处:

改动一:添加阿里云镜像(<mirrors> 标签内)

Maven 默认从国外官网下载 jar 包很慢,加一个阿里云镜像来加速下载。

<mirror>
  <id>aliyunmaven</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云公共仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>
字段 含义
id 镜像的唯一标识(自定义)
mirrorOf * 表示所有仓库的下载请求都走这个镜像
name 镜像显示名称
url 阿里云仓库地址

改动二:设置 JDK 1.8 编译版本(<profiles> 标签内)

保证不管宿主机装了几个 JDK,编译时统一用 JDK 1.8。

<profile>
  <id>jdk18</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>

再在 <activeProfiles> 标签中启用(使上面的配置默认生效):

<activeProfiles>
  <activeProfile>jdk18</activeProfile>
</activeProfiles>

⚠️ 注意:<mirrors> 里原本有一个 maven-default-http-blocker 镜像,作用是屏蔽不安全的 HTTP 仓库,这个保留不用动,只新增 aliyunmaven 即可。<profiles> 注释里的示例内容也不用动。

至此,JDK和maven就都就绪了。后面Jenkins容器会用到,我们直接把它挂载到Jenkins容器数据卷即可。

五. Operate阶段工具

部署过程会采用docker进行部署,暂时只安装docker即可,后续会用到K8s。我们之前已经在所有主机上都一起部署了docker,这里就先跳过

六. Integrate工具

1. Jenkins介绍

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具

Jenkins应用广泛,大多数互联网公司都会采用Jenkins配合GitLab、Docker、K8s作为实现DevOps的核心工具。

Jenkins的强大在于其插件,这些插件保证了自动化CI/CD过程中的繁杂功能

Jenkins最主要的工作就是将GitLab上可以构建的工程代码拉取并进行构建,再根据流程可以选择发布到测试环境或者是生产环境

一般是GitLab上进行的代码进行大量测试后确定发行版本,再发布到生产环境。

CI/CD可以理解为:

  • CI过程即使通过Jenkins将代码拉取、构建、制作镜像交给测试人员测试
    • 持续集成: 让软件代码可以持续集成到主干上,并自动构建测试
  • CD过程即是通过Jenkins将打好标签的发行版本代码拉取、构建、制作镜像交给运维人员部署
    • 持续交付: 让经过持续集成的代码可以进行手动部署
    • 持续部署: 让可以持续交付的代码随时随地的自动化部署

2. Jenkins安装

1) 拉取镜像

此处我们在2上直接拉取Jenkins的镜像使用命令docker pull jenkins/jenkins

或者如果本地有镜像直接导入

[root@K8s2 ~]# docker load -i /root/jenkins-2.572.tgz 
Loaded image: jenkins/jenkins:latest

2) 创建目录,并编写docker-compose.yaml文件

mkdir -p /usr/local/docker/jenkins_docker
vi /usr/local/docker/jenkins_docker/docker-compose.yaml
version: "3.1"
services:
  jenkins:
    image: jenkins/jenkins
    container_name: jenkins
    ports:
    - 8080:8080
    - 50000:50000
    volumes:
    - ./data/:/var/jenkins_home/

3) 启动容器

docker compose -f /usr/local/docker/jenkins_docker/docker-compose.yaml up -d

注意: 首次启动我们可能会因为数据卷data目录没有权限二导致启动失败,这时候我们就需要设置data目录写入权限

# 赋权
chmod -R a+w /usr/local/docker/jenkins_docker/data
# 重启
docker compose -f /usr/local/docker/jenkins_docker/docker-compose.yaml restart

4) 配置加速

由于Jenkins需要下载大量内容,但是默认下载地址的下载速度较慢,这里可以设置为国内镜像站。

步骤一:修改更新源配置文件,如下:

进入 Jenkins 容器数据卷中的 hudson.model.UpdateCenter.xml 文件:

vi /usr/local/docker/jenkins_docker/data/hudson.model.UpdateCenter.xml

原来的默认地址(国外,下载慢):

<?xml version='1.1' encoding='UTF-8'?>
<sites>
  <site>
    <id>default</id>
    <url>https://updates.jenkins.io/update-center.json</url>
  </site>
</sites>

将其中的 <url> 改为官方可用的更新源地址(国内镜像站不提供 update-center.json 索引文件,实测会返回 404):

<?xml version='1.1' encoding='UTF-8'?>
<sites>
  <site>
    <id>default</id>
    <url>https://updates.jenkins.io/update-center.json</url>
  </site>
</sites>

步骤二:重启 Jenkins 使配置生效,如下:

docker compose -f /usr/local/docker/jenkins_docker/docker-compose.yaml restart

然后浏览器访问 Jenkins,就能正常下载插件了。

⚠️ 重要提示(实测验证):

  • 国内镜像站(清华 mirrors.tuna.tsinghua.edu.cn、华为云 mirrors.huaweicloud.com)只提供插件二进制文件(plugins/、war/ 等目录),不提供 updates/update-center.json 这个更新索引文件,访问会返回 404。

  • 配置成清华源 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json 会导致 Jenkins 连不上更新中心,页面上显示"离线"。

  • update-center.json 这个索引文件只有官方源 https://updates.jenkins.io/update-center.json 提供,它是通的(会 301 跳转到最新路径,属正常)。

3. 浏览器中配置Jenkins

浏览器中输入http://192.168.100.100:8080进入Jenkins界面进行配置

alt text

这时候就要回到终端,查看密码

[root@K8s2 jenkins_docker]# docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
ef6c4e0a965b45d4a952e5d727b95777

重启返回浏览器,将得到的密码输入到对应位置

alt text

alt text

我这里本地有插件就先跳过了,一会初始化完成后再一次性导入插件。

然后创建用户如图

alt textalt text

4. 下载插件

a. 就直接从本地导入了

# 解压到挂载目录
cd /usr/local/docker/jenkins_docker/data/
tar zxf /root/jenkins-2.572-plugin.tar.gz

b. 查看确认插件内容出来了

[root@K8s2 data]# ls /usr/local/docker/jenkins_docker/data/plugins  
ant                                     github-api.jpi                localization-support              resource-disposer.jpi
antisamy-markup-formatter               github-branch-source          localization-support.jpi          scm-api
antisamy-markup-formatter.jpi           github-branch-source.jpi      localization-zh-cn                scm-api.jpi
ant.jpi                                 github.jpi                    localization-zh-cn.jpi            script-security
apache-httpcomponents-client-4-api      git.jpi                       mailer                            script-security.jpi
apache-httpcomponents-client-4-api.jpi  git-parameter                 mailer.jpi                        snakeyaml-api
asm-api                                 git-parameter.jpi             matrix-auth                       snakeyaml-api.jpi
asm-api.jpi                             gradle                        matrix-auth.jpi                   snakeyaml-engine-api
bootstrap5-api                          gradle.jpi                    matrix-project                    snakeyaml-engine-api.jpi
bootstrap5-api.jpi                      gson-api                      matrix-project.jpi                ssh-credentials
bouncycastle-api                        gson-api.jpi                  mina-sshd-api-common              ssh-credentials.jpi
bouncycastle-api.jpi                    instance-identity             mina-sshd-api-common.jpi          ssh-slaves
branch-api                              instance-identity.jpi         mina-sshd-api-core                ssh-slaves.jpi
branch-api.jpi                          ionicons-api                  mina-sshd-api-core.jpi            structs
build-timeout                           ionicons-api.jpi              okhttp-api                        structs.jpi
build-timeout.jpi                       jackson2-api                  okhttp-api.jpi                    theme-manager
caffeine-api                            jackson2-api.jpi              pipeline-build-step               theme-manager.jpi
caffeine-api.jpi                        jackson3-api                  pipeline-build-step.jpi           timestamper
checks-api                              jackson3-api.jpi              pipeline-github-lib               timestamper.jpi
checks-api.jpi                          jackson-annotations2-api      pipeline-github-lib.jpi           token-macro
cloudbees-folder                        jackson-annotations2-api.jpi  pipeline-graph-view               token-macro.jpi
cloudbees-folder.jpi                    jakarta-activation-api        pipeline-graph-view.jpi           trilead-api
commons-lang3-api                       jakarta-activation-api.jpi    pipeline-groovy-lib               trilead-api.jpi
commons-lang3-api.jpi                   jakarta-mail-api              pipeline-groovy-lib.jpi           variant
commons-text-api                        jakarta-mail-api.jpi          pipeline-input-step               variant.jpi
commons-text-api.jpi                    jakarta-xml-bind-api          pipeline-input-step.jpi           woodstox-core-api
credentials                             jakarta-xml-bind-api.jpi      pipeline-milestone-step           woodstox-core-api.jpi
credentials-binding                     javax-activation-api          pipeline-milestone-step.jpi       workflow-aggregator
credentials-binding.jpi                 javax-activation-api.jpi      pipeline-model-api                workflow-aggregator.jpi
credentials.jpi                         jaxb                          pipeline-model-api.jpi            workflow-api
dark-theme                              jaxb.jpi                      pipeline-model-definition         workflow-api.jpi
dark-theme.jpi                          jjwt-api                      pipeline-model-definition.jpi     workflow-basic-steps
display-url-api                         jjwt-api.jpi                  pipeline-model-extensions         workflow-basic-steps.jpi
display-url-api.jpi                     joda-time-api                 pipeline-model-extensions.jpi     workflow-cps
durable-task                            joda-time-api.jpi             pipeline-stage-step               workflow-cps.jpi
durable-task.jpi                        jquery3-api                   pipeline-stage-step.jpi           workflow-durable-task-step
echarts-api                             jquery3-api.jpi               pipeline-stage-tags-metadata      workflow-durable-task-step.jpi
echarts-api.jpi                         jsch                          pipeline-stage-tags-metadata.jpi  workflow-job
eddsa-api                               jsch.jpi                      plain-credentials                 workflow-job.jpi
eddsa-api.jpi                           json-api                      plain-credentials.jpi             workflow-multibranch
email-ext                               json-api.jpi                  plugin-util-api                   workflow-multibranch.jpi
email-ext.jpi                           json-path-api                 plugin-util-api.jpi               workflow-scm-step
font-awesome-api                        json-path-api.jpi             prism-api                         workflow-scm-step.jpi
font-awesome-api.jpi                    jsoup                         prism-api.jpi                     workflow-step-api
git                                     jsoup.jpi                     publish-over                      workflow-step-api.jpi
git-client                              junit                         publish-over.jpi                  workflow-support
git-client.jpi                          junit.jpi                     publish-over-ssh                  workflow-support.jpi
github                                  ldap                          publish-over-ssh.jpi              ws-cleanup
github-api                              ldap.jpi                      resource-disposer                 ws-cleanup.jpi

c. 导入后重启Jenkins

docker compose -f /usr/local/docker/jenkins_docker/docker-compose.yaml restart

5. Jenkins入门配置

由于Jenkins需要从Git拉取代码(需要安装git parameter插件)、需要本地构建(需要maaven、jdk,可以将之前部署在宿主机的maven、jdk放到jenkins容器的数据卷中)、甚至需要直接发布自定义镜像到Docker仓库(需要安装publish over ssh插件),所以jenkins需要做一些配置

准备好GitLab仓库的项目,并且通过Kenkins配置项实现当前项目DevOps的基本流程

1) 构建Maven工程发布到GitLab

a. 先创建仓库

alt textalt textalt text

b. 登录

git config --global user.name admin
git config --global user.email admin@123.com

c. 初始化仓库,后将提前准备好的代码提交

cd /root/mytest-hello程序代码/mytest-hello/
git init
git add .
git commit -m "初始提交 mytest-hello"

d. 关联远程仓库,后推送

⚠️ 注意(重要):GitLab 创建项目时,即使没勾选 README,也会默认生成一个 main 分支(初始提交)。而本地 git init 生成的是 master 分支。两者历史不相关,直接推 main 会报 fetch first 或 --force 被拒(main 是 GitLab 的保护分支,禁止强推)。

因此正确的做法是:先 pull 合并两条历史,再推送。

第一步:关联远程仓库,并设置好 pull 合并策略

git remote add origin http://192.168.100.99:8929/root/test.git
git config --global pull.rebase false   # 让 git pull 默认用 merge 合并

第二步:拉取远程 main,允许两条不相关历史合并

git pull origin main --allow-unrelated-histories
# 如果提示"需要指定如何调和偏离的分支",是 pull.rebase 未设置为 false,执行上面那条配置后重试
# 输出类似: Merge made by the 'ort' strategy.  README.md | 93 +++ 表示合并成功

第三步:推送到远程 main

git push -u origin main

成功输出类似:

[root@K8s1 mytest-hello]# git remote add origin http://192.168.100.99:8929/root/test.git
[root@K8s1 mytest-hello]# git push -u origin main
Username for 'http://192.168.100.99:8929': root
Password for 'http://root@192.168.100.99:8929': 
枚举对象中: 20, 完成.
对象计数中: 100% (20/20), 完成.
使用 2 个线程进行压缩
压缩对象中: 100% (14/14), 完成.
写入对象中: 100% (19/19), 3.02 KiB | 619.00 KiB/s, 完成.
总共 19(差异 1),复用 0(差异 0),包复用 0(来自  0 个包)
To http://192.168.100.99:8929/root/test.git
   e57ff74..99507cb  main -> main
分支 'main' 设置为跟踪 'origin/main'。

e. 到gitlab上查看验证

alt text

## 💡 避坑复盘(重要经验)

本次推送踩到的坑,核心原因和解决办法如下:

现象 根本原因 正确做法
代码在 Branches 里能看到,但主页看不到 GitLab 主页显示默认分支 main(空),你的代码在 master 统一分支名,让代码推到默认分支
git push --force 被拒 GitLab 把默认分支设为保护分支,禁止强推覆盖 不要用 --force,改用合并历史的方式
删了 master 还是推不上 删 master 与 main 是否被保护无关 处理 main 本体,不是删别的分支
最后还是推上去了 git pull --allow-unrelated-histories 把两条历史合并成一个提价,push 变成正常快进,保护分支放行 远程已有初始提交时,先 pull 合并再 push

一句话总结:远程 main 是保护分支,禁强推;解决"历史不相关"问题用 pull --allow-unrelated-histories 合并,而不是用 --force 对抗保护机制。

至此,一个maven工程就成功推送到gitlab上了

2) Jenkins新建任务

点击左侧导航

alt textalt text alt textalt textalt text

3) Jenkins立即构建

alt textalt textalt text

可以看到源码已经拉渠到了Jenkins本地,可以根据第三行日志信息,查看Jenkins本地拉渠道的源码

4) 查看Jenkins容器的源码

[root@K8s2 ~]# docker exec -it jenkins ls /var/jenkins_home/workspace/test
README.md  pom.xml  src

5) 配置Maven构建代码

代码拉取到Jenkins本地后,需要在Jenkins中对代码进行构建这里maven的环境。而Maven需要java的环境。所以接下来需要在Jenkins中安装JDK和maven,并且配置到Jenkins服务

# 将宿主机的JDK、MAVEN目录移动到Jenkins容器的数据卷中
cd /usr/local/docker/jenkins_docker/data/
mv /usr/local/jdk1.8.0_333/ ./
mv /usr/local/apache-maven-3.8.5/ ./

然后进入jenkins容器查看

[root@K8s2 data]# docker exec -it jenkins ls /var/jenkins_home/
apache-maven-3.8.5              identity.key.enc                                nodeMonitors.xml          userContent
config.xml                      jdk1.8.0_333                                    plugins                   users
copy_reference_file.log         jenkins.install.InstallUtil.lastExecVersion     queue.xml                 war
credentials.xml                 jenkins.install.UpgradeWizard.state             queue.xml.bak             workspace
fingerprints                    jenkins.model.JenkinsLocationConfiguration.xml  secret.key
hudson.model.UpdateCenter.xml   jenkins.telemetry.Correlator.xml                secret.key.not-so-secret
hudson.plugins.git.GitTool.xml  jobs                                            secrets

可以看到jdk和maven已经移动到Jenkins的容器卷了。

6) Jenkins配置JDK&Maven并保存

alt textalt textalt text

7) 配置Jenkins任务的构建代码

alt textalt textalt textalt text

重新测试构建并查看

alt textalt text

可以看到已经构建成功了

8) 配置Publish发布&远程操作

jar包构建好以后就可以根据情况发布到测试或生产环境,这里需要用到之前下载好的插件Publish Over SSH。

a. 配置Publish Over SSH连接 测试、生产环境

系统管理 ---> 系统配置 ---> Publish over SSH ---> SSH Servers。点击新增。

alt text

我们使用192.168.100.101作为测试生产环境的服务器,所以hostname填写其的IP地址,username写用户比如管理员root,remote directory填写将构建好的jar包部署到对应主机的哪个目录下,如/usr/local/test确保指定目录存在。最后点击保存。

如果目标机器不存在该目录,可以先使用命令mkidr /usr/local/test创建

b. 配置好任务构建后操作,发布jar包到目录服务

alt textalt textalt text

c. 重新构建任务,然后去目标服务器查看

alt text

回到K8s3上,查看文件

[root@K8s3 ~]# cd /usr/local/test
[root@K8s3 test]# tree target/
target/
└── mytest-1.0-SNAPSHOT.jar

0 directories, 1 file

9) 更新java项目的内容

vi /root/mytest-hello程序代码/mytest-hello/src/main/java/com/bdqn/controller/HelloController.java
vi /root/mytest-hello程序代码/mytest-hello/docker/docker-compose.yaml
package com.bdqn.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Administrator
 * @version 1.0
 * @description TODO
 * @date 2023/11/17 11:27
 */
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello(){
        return "hello jenkins --v2.0.0"; 
/* 把hello jenkins改成hello jenkins --v2.0.0 */
    }
version: "3.1"
services:
  demo:
    build:
      context: ./
      dockerfile: Dockerfile
    image: demo:v2.0.0
    container_name: demo
    ports:
    - 8888:8080
~                  
# 然后上传
git add .
git commit -m "版本更新至v2.0.0"
git push -u origin main