现在利用已有的k8s环境部署一套CI/CD环境

DevOps

理解CI/CD,持续集成和持续交付的基本过程包括如下几个步骤:

  1. 软件更新或者迭代——Gitlab
  2. 新版软件打包成镜像——Jenkins
  3. 新的镜像在k8s中集成——Registry

当代码提交到gitlab之后,会立马触发jenkins将新代码编译成镜像,然后再在kmaster上部署新的镜像。

cicd

镜像仓库

Registry部署

# 部署docker并修改docker启动参数
[root@Gitlab ~]# vim /usr/lib/systemd/system/docker.service
# 新增--insecure-registry=192.168.10.9:5000 -H tcp://0.0.0.0:2376 
ExecStart=/usr/bin/dockerd --insecure-registry=192.168.10.9:5000 -H tcp://0.0.0.0:2376 -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
# 重新加载docker服务
[root@Gitlab ~]# systemctl daemon-reload 
[root@Gitlab ~]# systemctl restart docker
[root@Gitlab ~]# docker pull registry
Using default tag: latest
latest: Pulling from library/registry
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest
[root@Gitlab ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
Status: Image is up to date for nginx:latest
docker.io/library/nginx:latest
# 创建镜像存储空间
[root@Gitlab ~]# mkdir /data/registry
[root@Gitlab ~]# pvcreate /dev/sdb
  Physical volume "/dev/sdb" successfully created.
[root@Gitlab ~]# vgcreate vg_data /dev/sdb
  Volume group "vg_data" successfully created
[root@Gitlab ~]# lvcreate -n lv_data vg_data -l 100%free
  Logical volume "lv_data" created.
[root@Gitlab ~]# mkfs.xfs /dev/mapper/vg_data-lv_data 
meta-data=/dev/mapper/vg_data-lv_data isize=512    agcount=4, agsize=655104 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0, sparse=0
data     =                       bsize=4096   blocks=2620416, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
# 新增mount挂点
[root@Gitlab ~]# vim /etc/fstab 
/dev/mapper/vg_data-lv_data	/data	xfs	defaults	0 0
[root@Gitlab ~]# mount -a
# 建立registry容器,映射端口5000
[root@Gitlab ~]# docker run -d --name registry -p 5000:5000 --restart=always -v /data/registry:/var/lib/registry registry
d6af24382a2e05583c50faf566f9411474666f81d923102e9ac38d8b38cb30e
[root@Gitlab ~]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:5000            0.0.0.0:*               LISTEN      2197/docker-proxy   
tcp6       0      0 :::5000                 :::*                    LISTEN      2203/docker-proxy   
# 同时在kubernetes群集上完成docker配置文件的修改,即将10.9主机设置为镜像下载来源和dockerca认证源。

Harbor部署

# 部署2core4GB主机
# 创建/data目录,并映射独立20GB磁盘空间
[root@harbor harbor]# df -Th
Filesystem                  Type      Size  Used Avail Use% Mounted on
/dev/mapper/centos-root     xfs        17G  8.4G  8.7G  50% /
/dev/mapper/vg_data-lv_data xfs        20G   33M   20G   1% /data
# 部署Harbor
[root@harbor ~]# tar zxvf harbor-offline-installer-v2.7.3.tgz 
harbor/harbor.v2.7.3.tar.gz
harbor/prepare
harbor/LICENSE
harbor/install.sh
harbor/common.sh
harbor/harbor.yml.tmpl
[root@Harbor ~]# mv harbor /opt/
[root@Harbor ~]# cd /opt/harbor
[root@harbor harbor]# docker load -i harbor.v2.7.3.tar.gz 
[root@harbor harbor]# docker images
REPOSITORY                      TAG       IMAGE ID       CREATED         SIZE
goharbor/harbor-exporter        v2.7.3    44f17702b0d6   3 weeks ago     96.9MB
goharbor/chartmuseum-photon     v2.7.3    e21f928bea75   3 weeks ago     229MB
goharbor/redis-photon           v2.7.3    68ef52d98298   3 weeks ago     120MB
goharbor/trivy-adapter-photon   v2.7.3    aabf279df9cc   3 weeks ago     463MB
goharbor/notary-server-photon   v2.7.3    992cbac9892b   3 weeks ago     113MB
goharbor/notary-signer-photon   v2.7.3    e384f965170c   3 weeks ago     110MB
goharbor/harbor-registryctl     v2.7.3    0adcdbbc67c8   3 weeks ago     140MB
goharbor/registry-photon        v2.7.3    91fa7c3c922c   3 weeks ago     78.7MB
goharbor/nginx-photon           v2.7.3    a780e583d37f   3 weeks ago     116MB
goharbor/harbor-log             v2.7.3    48a9ddf4a380   3 weeks ago     128MB
goharbor/harbor-jobservice      v2.7.3    265eda6d72aa   3 weeks ago     260MB
goharbor/harbor-core            v2.7.3    1a415c050c9c   3 weeks ago     222MB
goharbor/harbor-portal          v2.7.3    9a0f808a9eed   3 weeks ago     125MB
goharbor/harbor-db              v2.7.3    731c8c0fe6ca   3 weeks ago     174MB
goharbor/prepare                v2.7.3    36fd5b190502   3 weeks ago     168MB
[root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
# 注释掉443端口和修改主机名
[root@harbor harbor]# vim harbor.yml
# 环境准备
[root@harbor harbor]# ./prepare 
prepare base dir is set to /opt/harbor
WARNING:root:WARNING: HTTP protocol is insecure. Harbor will deprecate http protocol in the future. Please make sure to upgrade to https
Generated configuration file: /config/portal/nginx.conf
Generated configuration file: /config/log/logrotate.conf
Generated configuration file: /config/log/rsyslog_docker.conf
Generated configuration file: /config/nginx/nginx.conf
Generated configuration file: /config/core/env
Generated configuration file: /config/core/app.conf
Generated configuration file: /config/registry/config.yml
Generated configuration file: /config/registryctl/env
Generated configuration file: /config/registryctl/config.yml
Generated configuration file: /config/db/env
Generated configuration file: /config/jobservice/env
Generated configuration file: /config/jobservice/config.yml
Generated and saved secret to file: /data/secret/keys/secretkey
Successfully called func: create_root_cert
Generated configuration file: /compose_location/docker-compose.yml
Clean up the input dir
# 部署Harbor
[root@harbor harbor]# ./install.sh 
[Step 0]: checking if docker is installed ...
Note: docker version: 24.0.6
[Step 1]: checking docker-compose is installed ...
Note: Docker Compose version v2.21.0
[Step 2]: loading Harbor images ...
[Step 5]: starting Harbor ...
[+] Running 10/10
 ✔ Network harbor_harbor        Created                                                                             
 ✔ Container harbor-log         Started                                                                             
 ✔ Container registry           Started                                                                             
 ✔ Container registryctl        Started                                                                             
 ✔ Container harbor-portal      Started                                                                             
 ✔ Container redis              Started                                               
 ✔ Container harbor-db          Started                                                                             
 ✔ Container harbor-core        Started                                                   
 ✔ Container nginx              Started                                                                             
 ✔ Container harbor-jobservice  Started                                                                             
✔ ----Harbor has been installed and started successfully.----
# 修改docker的daemon配置文件
[root@harbor harbor]# vim /etc/docker/daemon.json 
{
  "registry-mirrors": ["https://37y8py0j.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "insecure-registries":["192.168.10.8"]
}
[root@harbor harbor]# systemctl daemon-reload 
[root@harbor harbor]# systemctl restart docker
# 拉起Harbor服务
[root@harbor harbor]# docker-compose up -d
Recreating harbor-log ... done
Recreating harbor-portal ... 
Recreating registryctl ... 
Recreating redis ... 
Recreating registry ... 
Recreating registry ... done
Recreating harbor-core ... done
Recreating harbor-jobservice ... 
Recreating nginx ... done
# 设置Harbor服务的systemd启动脚本
[root@harbor harbor]# cat /etc/systemd/system/harbor.service 
[Unit]
Description=Harbor Image Service
After=docker.service systemd-networkd.service systemd-resolved.service
Requires=docker.service
Documentation=http://github.com/vmware/harbor

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c "docker-compose -f /opt/harbor/docker-compose.yml up "
ExecStart=/bin/bash -c "docker-compose -f /opt/harbor/docker-compose.yml stop"

[Install]
WantedBy=multi-user.target
[root@harbor harbor]# systemctl daemon-reload 
# 设置Harbor的自启动
[root@harbor harbor]# systemctl enable --now harbor.service 
Created symlink from /etc/systemd/system/multi-user.target.wants/harbor.service to /etc/systemd/system/harbor.service.
# 配置/etc/hosts的解析
[root@harbor harbor]# cat /etc/hosts
192.168.10.8	reg.sujx.net
# 登陆Harbor
[root@harbor harbor]# docker login 192.168.10.8
Username: sujx
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
# 推送镜像
[root@harbor harbor]# docker tag nginx:latest 192.168.10.8/library/nignx:v1.24
[root@harbor harbor]# docker push 192.168.10.8/library/nignx:v1.24
The push refers to repository [192.168.10.8/library/nignx]
d874fd2bc83b: Pushed 
32ce5f6a5106: Pushed 
f1db227348d0: Pushed 
b8d6e692a25e: Pushed 
e379e8aedd4d: Pushed 
2edcec3590a4: Pushed 
v1.24: digest: sha256:ee89b00528ff4f02f2405e4ee221743ebc3f8e8dd0bfd5c4c20a2fa2aaa7ede3 size: 1570
  • 登陆Harbor
    login
  • 创建用户
    user
  • 查看上传镜像
    image

代码管理

部署Gitlab

# 单独部署gitlab需要4G内存,整合部署需要8G内存
# 获取gitlab-ce
[root@Gitlab ~]# docker pull gitlab/gitlab-ce
# 创建数据目录
[root@Gitlab ~]# mkdir -pv /data/gitlab/{etc,log,data}
[root@Gitlab ~]# tree /data/gitlab/
/data/gitlab/
├── data
├── etc
└── log
3 directories, 0 files
# 建立容器
[root@Gitlab ~]# docker run -dit --name=gitlab --restart=always -p 443:443 -p 80:80 -p 2022:22 -v /data/gitlab/etc:/etc/gitlab -v /data/gitlab/log:/var/log/gitlab -v /data/gitlab/data:/var/opt/gitlab --privileged=true gitlab/gitlab-ce
3507cf28a460c8448efa77f784f02ab7585fbf71f01e628fe11d4988625bbaf1

# 修改容器时区
[root@Gitlab ~]# docker exec -it gitlab sh -c "ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime"
# 修改gitlab配置文件
# 设定gitlab使用https访问
# 设定gitlab的时区为上海
[root@Gitlab ~]# docker stop gitlab
[root@Gitlab ~]# vim /data/gitlab/etc/gitlab.rb 
# 配置访问地址
external_url 'https://git.sujx.net'
# 内部ssh地址
gitlab_rails['gitlab_ssh_host'] = 'git.sujx.net'
# 配置时区
gitlab_rails['time_zone'] = 'Asia/Shanghai'
# 配置ssh端口号,因为宿主机还要使用22端口,所以使用2022端口
gitlab_rails['gitlab_shell_ssh_port'] = 2022
gitlab_rails['gitlab_shell_git_timeout'] = 800
# 配置Nginx开启https服务
nginx['enable'] = true 
nginx['client_max_body_size'] = '250m'
nginx['redirect_http_to_https'] = true 
nginx['redirect_http_to_https_port'] = 80 
# 放置SSl证书,这个路径是Docker内部看到的路径
nginx['ssl_certificate'] = "/etc/gitlab/ssl/git.sujx.net.pem"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/git.sujx.net.key"

#将对应的证书放入外部被映射到docker的路径下
[root@gitlab ~]# mkdir /data/gitlab/etc/ssl
[root@gitlab ~]# ls /data/gitlab/etc/ssl
git.sujx.net.key  git.sujx.net.pem

# 重启gitlab容器
[root@Gitlab ~]# docker start gitlab
# 查看gitlab的root初始密码
[root@Gitlab ~]# docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
Password: KPcQ2ei7K4cTfQFsJAE5kU05+j5dBi7TcTV1elGWMLE=

登陆Gitlab

gitlab

  • gitlab首页
    gitlab
  • 项目新建
    gitlab
  • 新建P1项目
    gitlab
  • 克隆项目
    gitlab

部署测试

[root@Gitlab ~]# yum install git
Loaded plugins: fastestmirror, versionlock
Loading mirror speeds from cached hostfile
Package git-1.8.3.1-25.el7_9.x86_64 already installed and latest version
Nothing to do
[root@Gitlab ~]# git clone http://192.168.10.9/root/p1.git
Cloning into 'p1'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
[root@Gitlab ~]# cd p1/
[root@Gitlab p1]# git config --global user.name "sujx"
[root@Gitlab p1]# git config --global user.email sujx@live.cn
[root@Gitlab p1]# git config --global push.default simple
[root@Gitlab p1]# echo 1111 > index.html
[root@Gitlab p1]# git add .
[root@Gitlab p1]# git commit -m 111
[main 45a4e3b] 111
 1 file changed, 1 insertion(+)
 create mode 100644 index.html
[root@Gitlab p1]# git push
Username for 'http://192.168.10.9': root
Password for 'http://root@192.168.10.9': 
Counting objects: 4, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 266 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To http://192.168.10.9/root/p1.git
   1c92ec5..45a4e3b  main -> main
  • 验证结果
    gitlab

部署管理

部署Jenkins

# 下载jenkins镜像
[root@Jenkins ~]# docker pull jenkins/jenkins
Using default tag: latest
latest: Pulling from jenkins/jenkins
Digest: sha256:c3fa8e7f70d1e873ea6aa87040c557aa53e6707eb1d5ecace7f6884a87588ac8
Status: Image is up to date for jenkins/jenkins:latest
docker.io/jenkins/jenkins:latest
# 创建数据目录,并赋权 uid和gid为1000
[root@Jenkins ~]# mkdir /data/jenkins ; chown 1000:1000 /data/jenkins
# 创建容器
[root@Jenkins ~]# docker run -dit -p 8080:8080 -p 50000:50000 --name jenkins --privileged=true --restart=always -v /data/jenkins:/var/jenkins_home jenkins/jenkins
061b8fc6a51351451cb4b764a52fcf496d75ce877a816b822e7fa40c7bd8438a
# 查看Jenkins密码
[root@Jenkins ~]# docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
3517870dedb14ab99614ad7150dba69e
# 配置容器时区为上海
[root@Jenkins ~]# docker exec -it -u root jenkins sh -c "ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime"
# 停止Jenkins
[root@Jenkins ~]# docker stop jenkins
jenkins
# 修改jenkins的仓库地址,避免安装时的离线实例提示
[root@Jenkins ~]# sed -i "s@https://updates.jenkins.io/@https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/@" /data/jenkins/hudson.model.UpdateCenter.xml
[root@Jenkins ~]# sed -i "s@https://www.google.com@https://www.baidu.com/@" /data/jenkins/updates/default.json
# 升级Jenkins
[root@Jenkins ~]# wget https://updates.jenkins.io/download/war/2.427/jenkins.war
[root@Jenkins ~]# docker cp jenkins.war jenkins:/usr/share/jenkins/

初始化环境

  • 初始登陆界面(可忽略插件安装失败)
    jenkins
    jenkins
    jenkins
    jenkins
    jenkins
    jenkins

  • 升级和安装插件(安装Docker、docker-build-step、Generic Webhook Trigger)
    jenkins
    jenkins

  • 重启Jenkins(使用URL/restart重启,使用URL/stop停止)
    jenkins
    jenkins

  • 配置docker,新增cloud

    [root@devops ~]# vim /usr/lib/systemd/system/docker.service 
    # 将docker的监听方式由unix socket修改为TCP
    # 修改配置文件
    ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
    修改为:
    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H fd:// --containerd=/run/containerd/containerd.sock

    jenkins
    jenkins
    jenkins

与Gitlab联动

  1. Jenkins上安装generic-webhook-trigger、Docker、docker-build-step 插件
    jenkins
  2. 在Jenkins上创建账号的Token
    jenkins
  3. 使用root账号登陆gitlab进行配置
    gitlab

实际案例

游戏代码

俄罗斯方块这是一个经典的小游戏,现在我们使用这个小游戏来演示CI/CD的实现过程。
tetris

# 获取俄罗斯方块Javascript版源码
[sujx@Dev ~]$ git clone https://github.com/LeeYiyuan/tetrisai.git
Cloning into 'tetrisai'...
remote: Enumerating objects: 280, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 280 (delta 0), reused 2 (delta 0), pack-reused 272
Receiving objects: 100% (280/280), 54.71 KiB | 40.00 KiB/s, done.
Resolving deltas: 100% (139/139), done.
# 删除git信息
[sujx@Dev ~]$ rm  -rf tetrisai/.git/
[sujx@Dev ~]$ cd tetrisai/
[sujx@Dev tetrisai]$ tar zcvf ~/build/tetris.tar.gz ./
# 创建Docker镜像构建目录
[sujx@Dev tetrisai]$ mkdir ~/build
[sujx@Dev tetrisai]$ cd ~/build
# 编辑Dockerfile创建spaceinvaders:v1镜像
[sujx@Dev build]$ cat > Dockerfile <<EOF
FROM nginx:latest
MAINTAINER sujx@live.cn
ADD tetris.tar.gz /usr/share/nginx/html
EOF
# 构建镜像
[sujx@Dev build]$ docker build -t tetris:v1 .
[+] Building 0.1s (7/7) FINISHED                                                                                   
……
 => => naming to docker.io/library/tetris:v1 
# 运行测试容器镜像OK
[sujx@Dev build]$ docker run -itd --name tetris -p 80:80 tetris:v1
f6cc0a5f33caa89402e95a9d51c0b417208c7f73965b703113f8db020718ea39
# 重新打标签,准备推送到内网镜像库
[sujx@Dev ~]$ docker tag tetris:v1 harbor.sujx.net/sujx/tetris:v1
[sujx@Dev ~]$ docker login harbor.sujx.net
Username: sujx
Password: 
Login Succeeded
# 推送成功
[sujx@Dev ~]$ docker push harbor.sujx.net/sujx/tetris:v1
The push refers to repository [harbor.sujx.net/sujx/tetris]
ebe1bde802c1: Pushed 
d874fd2bc83b: Pushed 
32ce5f6a5106: Pushed 
f1db227348d0: Pushed 
b8d6e692a25e: Pushed 
e379e8aedd4d: Pushed 
2edcec3590a4: Pushed 
v1: digest: sha256:5607cb3d8d89803f0c1cbab72d74168c801bc202d5a545b0420f0afbf9739512 size: 1778

上传代码

在前面搭建的gitlab上面,注册sujx的账号,并新建tetris项目。

tetris

# 创建用户使用的公钥,并将内容粘贴到gitlab-ce中
[sujx@Dev ~]$ ssh-keygen -t rsa -b 2048 -C "sujx@live.cn"
# 测试免密登陆,使用-p参数指定gitlab-ce的2022端口
[sujx@Dev ~]$ ssh -T git@git.sujx.net -p 2022
Welcome to GitLab, @sujx!
# 创建gitlab用户信息
[sujx@Dev ~]$ git config --global user.name "sujx"
[sujx@Dev ~]$ git config --global user.email sujx@live.cn
[sujx@Dev ~]$ git config --global push.default simple
# 免密克隆项目,这里使用ssh路径,而非前述的https
[sujx@Dev ~]$ git clone ssh://git@git.sujx.net:2022/sujx/tetris.git
Cloning into 'tetris'...
# 将从github上下载的代码复制到tetris目录中
[sujx@Dev ~]$ cd ~/tetris
[sujx@Dev tetris]$ cp ~/tetrisai ./
[sujx@Dev tetris]$ git add .
[sujx@Dev tetris]$ git commit -m 'init game code'
[main 41d1eef] init game code
 13 files changed, 1329 insertions(+), 2 deletions(-)
 create mode 100644 License.md
 create mode 100644 index.html
 create mode 100644 js/ai.js
 create mode 100644 js/game_manager.js
 create mode 100644 js/grid.js
 create mode 100644 js/piece.js
 create mode 100644 js/polyfill.js
 create mode 100644 js/random_piece_generator.js
 create mode 100644 js/stopwatch.js
 create mode 100644 js/timer.js
 create mode 100644 js/tuner.js
 create mode 100644 style/main.css
# 实现免密推送
[sujx@Dev tetris]$ git push
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 441 bytes | 441.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
To ssh://git.sujx.net:2022/sujx/tetris.git
   41d1eef..92aa7b5  main -> main

自动化构建

  1. 使用Token连接Jenkins和Gitlab
    token

  2. 连接测试

    token

  3. 设置测试job,开启webhook trigger

    webhook

  4. 设置任务

    webhook

  5. 测试自动执行

    # 在开发机上新增文件a
    [sujx@Dev tetris]$ touch a
    [sujx@Dev tetris]$ git add .
    [sujx@Dev tetris]$ git commit -m "add a"
    [main cbfba5b] add a
     1 file changed, 0 insertions(+), 0 deletions(-)
     create mode 100644 a
    [sujx@Dev tetris]$ git push
    Enumerating objects: 4, done.
    Counting objects: 100% (4/4), done.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (3/3), 244 bytes | 244.00 KiB/s, done.
    Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
    To ssh://git.sujx.net:2022/sujx/tetris.git
       1775d7f..cbfba5b  main -> main
    # 在Jenkins主机上查看结果
    [root@Jenkins ~]# docker exec -it jenkins cat /tmp/a
    HelloWorld!

    build

  6. 配置Jenkins任务,添加执行shell和docker构建项

    cd /var/jenkins_home/tetris
    git clone https://git.sujx.net/sujx/tetris.git
    cd tetris
    tar zcf ../tetris.tar.gz ./

    jenkins

  7. 执行任务
    jenkins

  8. 查看镜像库,除了前述上传的v1版本外,新增了7版本
    harbor

  9. 发布到Kubernets

    再次到Jenkins中新增构建步骤,添加执行shell

    export KUBECONFIG=/kc1
    /kubectl set image deployment/web1 tetris="harbor.sujx.net/sujx/tetris:${BUILD_NUMBER}" -n nscicd