1.前期选择

  1. 公司领导要求为公司的项目搭建自动化发布部署环境,由于以前做过这方面的工作,就主动揽下了这个任务。以前一直是使用别人搭建好的服务,总是很好奇这东西到底是怎么实现的。如今有个这样的机会,正好来试试。
  2. 目前市面上实现自动化部署的开源方案一般有两个(Jenkins和GitLab)。从可扩展性而言Jenkins是更好的选择,不过自由也意味着很多东西都需要自己配置。我觉得GitLab自带的CI/CD实现就已经绰绰有余,而且毕竟是GitLab自家的东西,到时候应该能少踩不少坑。
  3. 我们主要目标是自动部署SpringBoot项目到目标环境。

这次实践综合了网上各种文章,也踩了很多坑,单看一篇文章根本解决不了遇到的所有问题。主要也是给自己留个记录,下次再次搭建不用再去翻各种文章即麻烦又容易遗忘。(所以重点还是遇到的各种坑与解决方法记录)

2.环境准备

  1. 什么是CI/CD?

    粘贴下wiki里面的描述

    在​​软件工程​​​中,CI/CD或CICD是​​持续集成​​​(CI) 和(更常见)​​持续交付​​​或(较少见)​​持续部署​​​(CD) 的组合实践。​​[1]​​ CI/CD 通过在应用程序的构建、测试和部署中强制执行自动化来弥合开发和运营活动与团队之间的差距。CI/CD 服务编译开发人员所做的增量代码更改,然后将它们链接并打包成软件可交付成果。​​[2]​​​自动化测试验证软件功能,自动化部署服务将其交付给最终用户。​​[3]​​​目的是增加早期缺陷发现,提高生产力,并提供更快的发布周期。该过程与在部署较新版本之前将一组软件更新集成到一个大批次中的传统方法形成对比。现代​​DevOps​​实践涉及软件应用程序整个开发生命周期的持续开发、持续测试、持续集成、持续部署和持续监控。CI/CD 实践或CI/CD 管道构成了现代 DevOps 操作的支柱。

  2. 我们需要有三台服务器,用以部署不同的服务(当然都部署到一台机器也是可以,不过就无法模拟真实情景了) 如下:

服务器ip 系统 安装服务
10.10.3.176 Centos7 GitLab
10.10.3.177 Centos7 GitLab Runner
124.221.220.227 Centos7 SpringBoot项目

前两个是创建的虚拟机,第三个是买的云服务器

GitLab

私有化仓库,GitLab的服务器还是要性能好一点,不然甚至启动不了,勉强启动也很卡。

GitLab Runner

相当于执行者,没有它所有操作都无法执行。通过注册的方式与GitLab建立连接。

SpringBoot项目

该环境需要提前安装Maven和JDK并配置好环境变量。

我们一个一个环境来

3.环境搭建

1.虚拟机准备

我们在一台电脑上用VMware构建两个虚拟机,为了模拟真实所以让虚拟机都以桥接模式接入网络(修改虚拟机网络连接为桥接模式)。

桥接模式:这种模式让我们的两台虚拟机在网路上被视作两台不同的主机,并配置静态ip用以访问外网

  1. 查看本机ip
C:\Users\13323>ipconfig

Windows IP 配置


以太网适配器 以太网:

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::f8d1:f6c8:bba9:3f75%6
   IPv4 地址 . . . . . . . . . . . . : 10.10.3.175
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : 10.10.3.1
  1. 为虚拟机配置ip
[root@localhost ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33 

TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPADDR=10.10.3.176
NAME=ens33
UUID=16377499-9540-4ac1-875b-279b3855509e
DEVICE=ens33
ONBOOT=yes
NETMASK=255.255.255.0
GATEWAY=10.10.3.1
DNS1=8.8.8.8
  1. 重启网络
[root@localhost ~]# service network restart
Restarting network (via systemctl):                        [  确定  ]
  1. 配置文件中增加网关地址
[root@localhost ~]# vim /etc/sysconfig/network

# Created by anaconda
NETWORKING=yes
HOSTNAME=qinaan
GATEWAGY=10.10.3.1
  1. 测试连接
[root@localhost ~]# ping 10.10.3.177
PING 10.10.3.177 (10.10.3.177) 56(84) bytes of data.
64 bytes from 10.10.3.177: icmp_seq=1 ttl=64 time=0.384 ms
64 bytes from 10.10.3.177: icmp_seq=2 ttl=64 time=0.993 ms
64 bytes from 10.10.3.177: icmp_seq=3 ttl=64 time=0.432 ms
^C
--- 10.10.3.177 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2008ms
rtt min/avg/max/mdev = 0.384/0.603/0.993/0.276 ms
[root@localhost ~]# ping 124.221.220.227
PING 124.221.220.227 (124.221.220.227) 56(84) bytes of data.
64 bytes from 124.221.220.227: icmp_seq=1 ttl=50 time=13.8 ms
64 bytes from 124.221.220.227: icmp_seq=2 ttl=50 time=13.8 ms
64 bytes from 124.221.220.227: icmp_seq=3 ttl=50 time=13.8 ms
^C
--- 124.221.220.227 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2016ms
rtt min/avg/max/mdev = 13.817/13.832/13.855/0.136 ms

可以看到三台服务器之间通信没有问题

2.GitLab安装(默认已安装docker)

  1. docker拉取GitLab镜像
docker pull gitlab/gitlab-ce:latest
  1. 运行
docker run -id -p 3000:80 -p 9922:22 -v /root/gitlab/etc:/etc/gitlab  -v /root/gitlab/log:/var/log/gitlab -v /root/gitlab/opt:/var/opt/gitlab --restart always --privileged=true --name gitlab gitlab/gitlab-ce

3.进入镜像容器内修改

docker exec -it gitlab /bin/bash
# 修改gitlab.rb

vi /etc/gitlab/gitlab.rb

# 加入如下
# gitlab访问地址,可以写域名。如果端口不写的话默认为80端口
external_url 'http://10.10.3.176'
# ssh主机ip
gitlab_rails['gitlab_ssh_host'] = '10.10.3.176'
# ssh连接端口
gitlab_rails['gitlab_shell_ssh_port'] = 9922

# 让配置生效
gitlab-ctl reconfigure

# 修改http和ssh配置
# 这里改成3000主要是因为我们的gitlab是跑在docker中的虽然在容器内端口是80:但外部访问映射的是3000端口
# 这里也不能重启,不然gitlab.rb的配置会映射给gitlab.yml(默认80) 导致我们修改的端口失效
vi /opt/gitlab/embedded/service/gitlab-rails/config/gitlab.yml

  gitlab:
    host: 10.10.3.176
    port: 3000 # 这里改为3000
    https: false
    
# 重启
gitlab-ctl restart
# 退出容器
exit
  1. 丢失密码修改root密码
# 进入容器内部
docker exec -it gitlab /bin/bash

# 进入控制台
gitlab-rails console -e production

# 查询id为1的用户,id为1的用户是超级管理员
user = User.where(id:1).first
# 修改密码为lqz123456
user.password='12345678zl'
# 保存
user.save!
# 退出
exit
  1. 用gitlab里面的模板创建个springboot项目 image.png 可以看到项目中并没有gitlab.yml配置文件,我们这里也用gitlab生成个,用默认的提交就可以 image.png clone下代码然后启动下看看能不能使用 image.png

3.gitlabRunner服务安装

  1. 这一步直接用官方给的操作就可以 image.png image.png 不过用官方的方式下载太慢,可以直接装在docker中
  2. 拉取镜像
docker pull gitlab/gitlab-runner

启动镜像
docker run -d --name gitlab-runner --restart always \
    -v /srv/gitlab-runner/config:/etc/gitlab-runner \
    -v /var/run/docker.sock:/var/run/docker.sock \
    gitlab/gitlab-runner:latest

进入容器,启动gitlab-runner register
docker exec -it gitlab-runner /bin/bash
gitlab-runner register

root@f6d4b53a848a:/# gitlab-runner register
Runtime platform                                    arch=amd64 os=linux pid=45 revision=5316d4ac version=14.6.0
Running in system-mode.                            
//上图复制的地址                                                   
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://10.10.3.176:3000/
//上图复制的token
Enter the registration token:
GR1348941WxvqJa78y_d-1ZXWhsVB
//runner描述
Enter a description for the runner:
[f6d4b53a848a]: learn
//tags为后面指定对应runner设定id
Enter tags for the runner (comma-separated):
zl
Registering runner... succeeded                     runner=GR134894
Enter an executor: ssh, virtualbox, docker-ssh+machine, kubernetes, docker, docker-ssh, shell, custom, parallels, docker+machine:
//选择docker,方便后面的步骤
docker
Enter the default Docker image (for example, ruby:2.6):
//选择maven:3.3.9和jdk8,这里maven版本选择低一点有个小坑就是
(如果采用私人仓库管理私有jar包,网址是http:形式(个人公司一般不会花销配置https:),3.6.3以上版本的maven不改配置会要求https:)
maven:3.3.9-jdk-8
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! 

配置好后去刚才的界面查看是否关联上,如图有对应的runner出现则说明配置成功 image.png

4.服务器环境事先配置

  1. 安装jdk8 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 选择对应版本下载 上传到目标服务器
# 解压
tar -zxvf jdk-8u333-linux-x64 .tar.gz
# 修改
vim /etc/profile
随便位置增加
export JAVA_HOME=/usr/local/java/jdk1.8.0_333
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

# 刷新配置使其生效
source /etc/profile
# 查看
java -version
  1. 修改gitlab.yml配置(我们这里不具体实践讨论gitlab.yml里面用法)
  1. 考虑安全因素配置runner可用全局变量隐藏(配置ssh连接云服务器密码) image.png
  2. 修改gitlab.yml
# 定义一些变量, 下面各阶段会使用24
variables:
  server_ip: 124.221.220.227
  jar_name: demo-0.0.1-SNAPSHOT.jar
  java_path: /usr/local/java/jdk1.8.0_333
  upload_path: /home/zl/gitlab-cicd-learn

# 定义执行的各个阶段及顺序
stages:
  - build
  - upload
  - deploy

# 使用 maven 镜像打包项目
maven-build:
  stage: build
  image: maven:3.6.3-jdk-8
# 选择执行runner
  tags:
    - zl
# 仅仅在master分支上执行
  only:
    - master
  script:
    - mvn package -B -Dmaven.test.skip=true
  cache:
    key: m2-repo
    paths:
      - .m2/repository
  artifacts:
    paths:
      - target/$jar_name
    # 上传gitlab缓存仅仅保留一天
    expire_in: 1 day

upload-jar:
  stage: upload
  image: ictu/sshpass
  tags:
    - zl
  only:
    - master
  script:
    - ls -l target/
    - sshpass -p $tecent_ssh_password scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no target/$jar_name root@$server_ip:$upload_path/$jar_name

# 启动 SpringBoot jar包
deploy-test:
  stage: deploy
  image: ictu/sshpass
  tags:
    - zl
  only:
    - master
  script:
# 事先杀除对应进程
    - sshpass -p $tecent_ssh_password ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@$server_ip << zl
    - cd /home/zl/java/shell
    - ./killnet.shell $jar_name
    - nohup java -jar $upload_path/$jar_name >/dev/null 2>&1 &
    - exit
    - zl

killnet.shell代码

ps -ef | grep $1 | grep -v grep | awk '{print $2}' | xargs kill -9

上传到服务器后为其增加权限
chmod 775 killnet.shell

提交更改,回到gitlab cicd模块 image.png 可以看到正在执行 image.png 等一段时间后发现执行完成 image.png

  1. 访问对应ip端口发现服务已经成功启动 image.png
  1. 实际为真实环境部署遇到的问题
  1. 项目中有很多私有的jar包,我们测试最简单的程序使用的依赖都是公共的自然没有问题。这时候maven就找不到对应的依赖了。解决方案也很简单,配置一个私有化仓库把私有jar包都丢进去。 用Nexus,在自己项目的pom里增加配置.
 <repositories>
     <repository>
         <id>nexus</id>
         <name>nexus</name>
         <url>http://10.10.3.176:8081/nexus/content/groups/public/</url>
         <releases>
             <enabled>true</enabled>
         </releases>
         <snapshots>
             <enabled>true</enabled>
         </snapshots>
     </repository>
 </repositories>
  1. 第一个步骤打包每次都特别慢,因为我们是使用的docker,每次它下载的依赖都会丢失,因为打包结束对应的容器就立马被删除了。所以必须将这些依赖持久化不然每次都这么慢即无法接受也不合理。
# 进入容器
docker exec -it gitlab-runner /bin/bash
# 修改配置
cat /etc/gitlab-runner/config.toml

concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "learn"
  url = "http://10.10.3.176:3000/"
  # 有些特殊情况gitlab默认地址无法访问可以手动增加
  clone_url = "http://10.10.3.176:3000"
  token = "mtM4Y5wVsUfx7hdJSszX"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "maven:3.3.9-jdk-8"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
# 依赖映射,解决重复下载问题
    volumes = ["/cache","/home/gitlab-runner/.m2:/root/.m2"]
    shm_size = 0
  1. 问题与总结
  1. 这次的学习项目结构很简单,真实项目的更加复杂。不过基本的gitlab cicd环境配置也算是这样配置完成了。
  2. 自动化配置的最灵活的点就是这个gitlab.yml 他可以执行shell,只要定义好管道流程。然后为每一个步骤定制化设计对应的脚本,就可以完成各种复杂的功能,所以无论遇到什么样子的项目都可以应对。
  3. 综合多篇文章cv下来感觉就是自己Linux操作太垃圾了,很多基础的操作都得去查询,然后踩的很多坑也是这个原因导致的,所以这个得多敲敲强化。