机器准备 均关闭了防火墙
Gitlab、Jenkins、sonarqube
均使用docker容器安装
192.168.163.21: 该机器上有docker、docker-compose环境
2核8G
harbor镜像仓库
docker容器安装
192.168.163.28: 该机器上有docker、docker-compose环境
2核4G
k8smaster
参考kuboard官方文档安装
192.168.163.29
2核4G
k8sworker
参考kuboard官方文档安装
192.168.163.30
2核4G
Gialab安装 1 docker pull gitlab/gitlab-ce:15.0.5-ce.0
1 2 [root@localhost gitlab]# pwd /usr/local/gitlab
创建docker-compose.yml
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 version: '1.0' services: gitlab: image: 'gitlab/gitlab-ce:15.0.5-ce.0' container_name: gitlab restart: always environment: GITLAB_OMNIBUS_CONFIG: | external_url 'http://192.168.163.21:8929' gitlab_rails['gitlab_shell_ssh_port'] = 2224 ports: - '8929:8929' - '2224:22' volumes: - './config:/etc/gitlab' - './logs:/var/log/gitlab' - './data:/var/opt/gitlab' privileged: true
启动容器
稍等一会, 访问http://192.168.163.21:8929/
就能看到Gitlab登陆页面了。接下来就去config/initial_root_password 文件看root用户初始密码(容器内或者挂载的目录下看都可)
第一次进去了, 就把密码修改了。
顺便把ssh公钥给配置上, 方便之后直接上传代码, 这个ssh公钥的内容就是宿主机用户目录/.ssh
目录下id_rsa.pub
的文件内容, 如果不存在百度生成下这个文件即可。
Jenkins安装 宿主机环境准备 宿主机提前准备好JDK和maven, 之后Jenkins安装好了, 直接通过数据卷挪进去。
解压压缩包, 并把jdk和maven的文件夹名称分别修改为java、maven ,并配置Maven的settings.xml
1 2 3 4 5 6 <mirror > <id > nexus-tencentyun</id > <mirrorOf > *</mirrorOf > <name > Nexus tencentyun</name > <url > http://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url > </mirror >
1 2 3 4 5 6 7 8 9 10 11 12 <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 >
Jenkins安装 1 2 [root@localhost gitlab]# pwd /usr/local/jenkins
在jenkins目录下, 准备docker-compose.yml文件
注意下载的jenkins镜像, 镜像名是jenkins/jenkins, 而不是jenkins.
挂载后三个数据卷, 是为后续jenkins容器内使用宿主机docker命令做准备.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 version: "1.0" services: jenkins: image: jenkins/jenkins:lts container_name: jenkins privileged: true environment: JENKINS_UC: "https://mirrors.cloud.tencent.com/jenkins/" JENKINS_UC_DOWNLOAD: "https://mirrors.cloud.tencent.com/jenkins/" ports: - 8080 :8080 - 50000 :50000 volumes: - ./data/:/var/jenkins_home/ - /usr/bin/docker:/usr/bin/docker - /var/run/docker.sock:/var/run/docker.sock - /etc/docker/daemon.json:/etc/docker/daemon.json
首次启动会因为数据卷data目录没有权限导致启动失败,设置data目录写权限 。或者事先创建data目录, 先修改data目录写权限。
重新启动Jenkins容器, 等待一会, 访问8080端口进登录首页。
查看初始化的登录密码, 在secrets/initialAdminPassword
文件中
下载默认插件即可, 如果失败了重新下载或者直接下一步, 进去后在插件管理处下载所需插件也可。
紧接着创建管理员用户和配置访问地址
发现好像是缺失部分插件。
ssh-credentials
: 看下图描述, 应该它对应的插件就是SSH Agent .
之前初始化过程中下载失败的两个Pipeline插件
Pipeline
Pipeline: Declarative
在插件商店重新下载后, 重启jenkins容器, 刷新网页即可
1 docker-compose restart jenkins
配置JDK和Maven 首先把宿主机的java和maven环境直接拷贝到数据卷内, 从而让jenkins容器能进行打包及java相关操作。
1 2 3 4 5 6 # 宿主机内操作 [root@localhost jenkins]# pwd /usr/local/jenkins# 将java目录一并复制到数据卷 data目录下(这里我为了宿主机也继续拥有java环境, 所以使用拷贝) # [root@localhost jenkins] [root@localhost jenkins]# cp -r /usr/local/maven ./data
Jenkins配置JDK&Maven并保存
配置SSH Server Jenkins, 系统管理/系统配置, 新增一个目标服务器(k8s)
准备Gitlab代码 SpringBoot应用 宿主机生成ssh公钥, 放到gitlab中。在宿主机创建一个简洁的SpringBoot项目, 只存在一个测试接口, 确保项目能打包并且使用java -jar xx.jar 运行起来。
pom.xml application.yml controller.TestController 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.7.2</version > <relativePath /> </parent > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > <build > <finalName > cicd</finalName > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build >
1 2 3 4 5 6 7 8 9 @RestController public class TestController { @GetMapping("test") public String test () { return "test - 01" ; } }
建立一个gitlab群组(比如说叫 backend), 然后在这个群组下创建一个gitlab仓库。
接下来进行分支配置, 模拟下公司环境。
基于main(默认分支)新建dev分支
将dev分支设置为保护分支, 禁止远程推送
将main、dev设置为保护分支, 并禁止远程推送
接下来将我们的项目推上去, 这里将仓库名(cicd)和项目打包名(cicd.jar)保持一致, 方便后续操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # 注意之前的端口映射, 如果ssh没配置好, 这里clone 会出现 port 2224: Connection refused git clone ssh://git@192.168.163.21:2224/backend/cicd.git git remote add origin ssh://git@192.168.163.21:2224/backend/cicd.git git add . git commit -m "init test maven project"# 因为远程dev是保护分支, 我们得切到自己分支, 并push自己分支 git checkout -b yincaiTA git push --set-upstream origin yincaiTA git init# 注意ssh用的是 2224 端口噢!!!! git remote add origin ssh://git@192.168.163.21:2224/backend/cicd.git# 拉取远程dev分支代码 git fetch origin dev git checkout -b dev origin/dev# 提交个人的测试项目 git add . git commit -m "init test maven project"# 从最新的dev上切出个人分支, 因为我们远程dev是保护分支, 无法直接push git checkout -b yincaiTA# 指定远程分支推送 git push --set-upstream origin yincaiTA
之后在远程(gitlab)中进行请求合并分支, 如果有分支建议在本地合并了再push。
接下来的开发流程就是: 本地dev分支pull代码, 切换到yincaiTA(本地个人开发分支), merge dev并解决冲突, 然后push yincaiTA分支, 最后在远程发起和并请求
下面能看到dev代码就是最新的啦
准备Dockerfile 这一步是是将jar构建成镜像, 并运行。
项目目录下新增一个docker目录, 配置Dockerfile
1 2 3 4 5 FROM daocloud.io/library/java:8 u40-jdkCOPY cicd.jar /usr/local/ WORKDIR /usr/local CMD java -jar cicd.jar
Jenkins集成Sonar Qube 安装Sonar Qube Sonar Qube是一个开源的代码分析平台,支持Java、Python、PHP、JavaScript、CSS等25种以上的语言,可以检测出重复代码
、代码漏洞
、代码规范
和安全性漏洞
的问题。
Sonar Qube可以与多种软件整合进行代码扫描,比如Maven、Gradle、Git、Jenkins等,并且会将代码检测结果推送回Sonar Qube
并且在系统提供的UI界面上显示出来。
Sonar Qube在7.9版本中已经放弃了对MySQL的支持,并且建议在商业环境中采用PostgreSQL,那么安装Sonar Qube时需要依赖PostgreSQL。
首先拉取需要的镜像。
1 2 3 docker pull postgres:12.11# sonarqube别用9+版本, 不然之后代码质量检测会有编译jar和运行环境的JDK版本不兼容的问题 docker pull sonarqube:8.9.6-community
准备一个docker-compose.yml的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 version: "1.0" services: db: image: postgres:12.11 container_name: db ports: - 5432 :5432 networks: - sonarnet environment: POSTGRES_USER: sonar POSTGRES_PASSWORD: sonar sonarqube: image: sonarqube:8.9.6-community container_name: sonarqube depends_on: - db ports: - "9000:9000" networks: - sonarnet environment: SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar SONAR_JDBC_USERNAME: sonar SONAR_JDBC_PASSWORD: sonar networks: sonarnet: driver: bridge
执行如下命令构建并启动容器
出现这样的错
1 2 3 4 ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch. bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] ERROR: Elasticsearch did not exit normally - check the logs at /opt/sonarqube/logs/sonarqube.log 2022.10.10 07:36:57 INFO es[][o.e.n.Node] stopping ...
需要将宿主机的虚拟内存(vm.max_map_count
)给加到至少262144.
在这个文件末尾追加上如下内容
再更新下内核运行配置参数
1 2 3 sysctl -p# sysctl命令被用于在内核运行时动态地修改内核的运行参数 # 参数p: 从配置文件"/etc/sysctl.conf" 加载内核参数
现在重新构建容器并启动就行了。
然后访问http://192.168.163.21:9000/
即可访问sonarqube了, 默认用户名/密码是admin/admin。(admin/sonar)
安装中文插件, 根据页面提示重启server即可。
挂载sonar-scanner到Jenkins数据卷 使得jenkins容器使用命令调用sonar-scanner, 生成代码质量检测结果.
下载网站: https://docs.sonarqube.org/latest/analysis/scan/sonarscanner/。
这里我选择4.6的linux版进行下载.
下载好后上传到linux上, 解压.
1 2 3 4 5 6 [root@localhost sonar-scanner]# ls sonar-scanner-cli-4.6.0.2311-linux.zip [root@localhost sonar-scanner]# unzip sonar-scanner-cli-4.6.0.2311-linux.zip [root@localhost sonar-scanner]# mv sonar-scanner-4.6.0.2311-linux sonar-scanner# 因为后期jenkins可能会用到sonar-scanner, 这里将sonar-scanner拷贝一份到jenkins数据卷中 [root@localhost sonar-scanner]# cp -r sonar-scanner ../jenkins/data
对sonar-scanner进行配置
1 2 [root@localhost sonar-scanner]# cd /usr/local/jenkins/data/sonar-scanner/conf/ [root@localhost conf]# vim sonar-scanner.properties
修改配置为如下内容
1 2 3 4 5 sonar.host.url =http://192.168.163.21:9000 sonar.sourceEncoding =UTF-8
下面是sonar-scanner检测代码质量的命令, 后面会用到(下图是生成token的方式)
1 2 # 下面执行代码检测, 命令敲错了也会有提示的~ [root@localhost manual-ci]# /usr/local/jenkins/data/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=sonarscanner-test -Dsonar.login=7de3ca0b06045e2131f6cdda033293f904a4f356 -Dsonar.projectKey=sonarscanner-test -Dsonar.java.binaries=./target/
整合sonarqube到jenkins jenkins安装sonarqube插件
接下来在系统配置
中, 配置SonarQube servers
接下来, 在全局配置
中, 配置SonarQube Scanner
harbor镜像仓库 安装Harbor 现有问题: 如果多个机器都需要jar镜像, 那么这个推送jar并构建镜像的操作就会进行多次, 而这完全是重复的操作。
进行优化:
jenkins容器调用宿主机docker进行打包镜像并推送
发送命令给目标服务器执行拉取镜像的命令
我这里加个虚拟机(192.168.163.28), 专门部署harbor.
浏览器访问harborIP:80
即可
用户名: admin
默认密码: Harbor12345
新建个用户
新建项目(私有)
项目授权给用户使用
之后的操作就用cicd
这个运维账号进行。
推送镜像到仓库的预配置 镜像名规范 修改镜像名命令: docker tag imageId 192.168.163.28:80/cicd/cicd:v1.0.0
镜像名称规范:harborIP地址:80/项目名/镜像名:版本
, 符合该规范的镜像才能被推送到指定仓库
仓库白名单配置 ==该配置k8smaster和k8sworker都得做==
修改/var/docker/daemon.json
文件, 增加insecure-registries
配置, 相当于docker白名单
1 2 3 4 { "registry-mirrors" : [ "https://yz5wi4lf.mirror.aliyuncs.com" ] , "insecure-registries" : [ "http://192.168.163.28:80" ] }
重启docker刷新配置
1 systemctl restart docker
测试是否能手动推送
1 2 3 docker login -u cicd -p Harbor12345 192.168.163.28:80 docker push 192.168.163.28:80/cicd/cicd:v1.0.0
手动拉取
1 docker pull 192.168.163.28:80/cicd/cicd:v1.0.0
Jenkins容器内部执行宿主机docker命令 配置Jenkins容器使用宿主机Docker, 调用宿主机docker命令进行推送
执行如下命令
1 2 sudo chown root:root /var/run/docker.sock sudo chmod o+rw /var/run/docker.sock
重启后该文件权限、用户组就变回去了, 所以按照如下操作, 让机器在启动后也执行一次该配置命令
CentOS7.9, 在/etc/rc.d/rc.local
文件中加入配置docker.sock文件的权限, 机器重启后就能保证其权限及所属组正确。
1 2 sudo chown root:root /var/run/docker.sock sudo chmod o+rw /var/run/docker.sock
安装K8s 安装k8s集群 安装k8s集群: 利用Kuboard安装k8s集群 , 照着教程做就好
192.168.163.29(master)
192.168.163.30(worker)
安装完毕
1 2 3 4 [root@localhost local]# kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8smaster Ready master 7m57s v1.19.5 192.168.163.29 <none> CentOS Linux 7 (Core) 3.10.0-1160.71.1.el7.x86_64 docker://19.3.11 k8sworker Ready <none> 50s v1.19.5 192.168.163.30 <none> CentOS Linux 7 (Core) 3.10.0-1160.71.1.el7.x86_64 docker://19.3.11
安装Kuboard图形化工具 安装文档
安装Ingress 使得未来能通过域名访问Service.
本质上就是nginx
k8s更推荐这种方式, 而非Service(毕竟service需要ip地址、端口, 虽然也能固定)
安装成功
最终部署 Jenkins新增流水线应用
另外也提供了基于git仓库中的Jenkinsfile的方式.
接下来在Gitlab项目根路径下添加Jekinsfile
这个文件就好, 内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 pipeline { agent any environment { harborUsername = 'cicd' harborPassword = 'Harbor12345' harborAddress = '192.168.163.28:80' repository = 'cicd' } stages { stage('拉取git仓库代码' ) { steps { checkout([$class: 'GitSCM' , branches: [[name: '*/dev' ]], extensions: [], userRemoteConfigs: [[credentialsId: '7a281c56-17b8-4a0e-95b1-62e0cf7275d6' , url: 'http://192.168.163.21:8929/backend/cicd.git' ]]]) } } stage('通过maven构建项目' ) { steps { sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests' } } stage('通过sonarqube做代码质量检测' ) { steps { sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=8f3eee52a57ec314ee64962e4368b33a6b58cc10' } } stage('通过docker制作自定义镜像' ) { steps { sh '''mv ./target/*.jar ./docker/ docker build -t ${JOB_NAME}:latest ./docker/''' } } stage('将自定义镜像推送到Harbor' ) { steps { sh '''docker login -u ${harborUsername} -p ${harborPassword} ${harborAddress} docker tag ${JOB_NAME}:latest ${harborAddress}/${repository}/${JOB_NAME}:latest docker push ${harborAddress}/${repository}/${JOB_NAME}:latest''' } } stage('将pipeline.yml传输到k8smaster上' ) { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'k8s' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: '' , sourceFiles: 'pipeline.yml' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )]) } } stage('jenkins容器ssh调用k8smaster执行apply pipeline.yml' ) { steps { sh '''ssh root@192.168.163.29 kubectl apply -f /usr/local/jenkins-push-k8s/pipeline.yml ssh root@192.168.163.29 kubectl rollout restart deployment pipeline -n test''' } } } }
注意想让execCommand
中$引用的变量能生效, 需用双引号
下面是groovy代码翻译之前的步骤说明
拉取git仓库代码
通过maven构建项目
1 2 # 去调用mvn, clean、package、跳过测试 /var/jenkins_home/maven/bin/mvn clean package -DskipTests
通过sonarqube做代码质量检测
1 2 3 # 指定 源代码路径、projectname、projectKey、打包后路径、登陆的token # 手动调用质量检测api, 推送给sonarqube服务器 /var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=./target/ -Dsonar.login=8f3eee52a57ec314ee64962e4368b33a6b58cc10
通过docker制作自定义镜像
1 2 3 4 # 移动jar到Dockerfile路径 mv ./target/*.jar ./docker/# 打包成镜像 docker build -t ${JOB_NAME}:latest ./docker/
将自定义镜像推送到Harbor
1 2 3 4 5 6 # 登录 docker login -u ${harborUsername} -p ${harborPassword} ${harborAddress}# 改镜像名为符合要求的名字 docker tag ${JOB_NAME}:latest ${harborAddress}/${repository}/${JOB_NAME}:latest# 推送 docker push ${harborAddress}/${repository}/${JOB_NAME}:latest
将pipeline.yml传输到k8smaster上(需要安装Pulish Over On SSH插件)
jenkins容器ssh调用k8smaster执行相关命令
gitlab项目根路径下新增pipeline.yml
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 apiVersion: apps/v1 kind: Deployment metadata: namespace: test name: pipeline labels: app: pipeline spec: replicas: 2 selector: matchLabels: app: pipeline template: metadata: labels: app: pipeline spec: containers: - name: pipeline image: 192.168 .163 .28 :80/cicd/pipeline:latest imagePullPolicy: Always ports: - containerPort: 8080 imagePullSecrets: - name: harbor --- apiVersion: v1 kind: Service metadata: namespace: test name: pipeline labels: app: pipeline spec: selector: app: nginx ports: - port: 8080 targetPort: 8080 type: NodePort --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: namespace: test name: pipeline spec: ingressClassName: ingress rules: - host: lsycai.nginx.top http: paths: - path: / pathType: Prefix backend: service: name: nginx-deployment port: number: 8080
先去kuboard中配置私服登陆信息, 是的k8smaster、k8sworker能登陆! 这样才能使得配置的私服镜像能被拉取下来
另外我们想让Jenkins容器通知目标服务器执行命令(即k8smaster执行kubectl apply -f pipeline.yml
), 但是使用ssh远程执行如下命令需要输入账号密码
1 ssh root@192.168.163.29 kubectl apply -f /usr/local/jenkins-push-k8s/pipeline.yml
我们采用给Jenkins容器配置免密登录, 让其能直接操作k8smaster.
1 2 3 4 5 6 7 8 9 10 11 12 # 操作Jenkins容器宿主机 [root@localhost local]# docker exec -it jenkins bash jenkins@780bd8e7b017:~$ cd .ssh bash: cd: .ssh: No such file or directory# 如果没有该文件夹进行生成 jenkins@780bd8e7b017:/etc/ssh$ ssh-keygen -t rsa -C "ks1229344939@163.com"# 3次回车, 生成公私钥 jenkins@780bd8e7b017:~$ cd .ssh/ jenkins@780bd8e7b017:~/.ssh$ ls id_rsa id_rsa.pub jenkins@780bd8e7b017:~/.ssh$ cat id_rsa.pub# 手动将公钥内容传递给k8smaster
1 2 3 4 5 6 7 8 9 10 # 操作k8smaster # 没有.ssh目录就生成 [root@k8smaster ~]# ssh-keygen -t rsa -C "ks1229344939@163.com"# 3次回车 [root@k8smaster ~]# cd .ssh/ [root@k8smaster .ssh]# ls id_rsa id_rsa.pub [root@k8smaster .ssh]# touch authorized_keys [root@k8smaster .ssh]# vim authorized_keys# 将jenkins容器内的ssh公钥放进去
1 2 3 # 在k8smaster那边的authorized_keys文件配置好后, 进行测试 # 操作Jnekins容器 jenkins@780bd8e7b017:~/.ssh$ ssh root@192.168.163.29 asda
自动化CI
推送代码后, 如何让Jenkins自动执行任务
Jenkins安装Gitlab插件
暂时做如下图的配置
点击添加会报错
解决方案
重新添加Webhooks即可。
测试出发推送事件, 又会报错
Jenkins, 全局配置中找到Gitlab
这下在gitlab代码存在推送事件就能正常触发Jenkins的任务了。
本地hosts文件(C:\Windows\System32\drivers\etc
)配一个dns, 浏览器就能通过域名:端口访问了.
1 192.168.163.21 lsycai.nginx.top
踩坑 碰到如下问题, 是因为windows开了代理
!!!
总结 还蛮耗资源的