模板机拷贝 自己电脑上, 之前的IP尾号为20已经当作了模板机, 拥有Java和docker、docker-compose的环境了。
我们拷贝个尾号21的机器(如何拷贝看这里 ), 利用docker安装Gitlab、Jenkins、Harbor, 虚拟机资源分配多点, 这里分了2核8G。
Gialab安装 1 docker pull gitlab/gitlab-ce:15.0.5-ce.0
1 2 [root@localhost gitlab]# pwd /usr/local/gitlab
创建docker-compose.yml
文件
这个👇配置自己试了不行, 使用ssh连接clone代码就会出现ssh: connect to host 192.168.163.21 port 2224: Connection refused
. 自己进入gitlab容器, 也发现容器内ssh端口依旧是22
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:80' - '2224:22' volumes: - './config:/etc/gitlab' - './logs:/var/log/gitlab' - './data:/var/opt/gitlab' privileged: true
所以改成👇这个配置, 把容器内ssh端口改成22, gitlab_shell_ssh_port
依旧不变, 这个关系到gitlab前台显示的ssh路径
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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/
首次启动会因为数据卷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并保存
测试拉取代码并打包推送 准备一个java项目 宿主机生成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代码就是最新的啦
配置jenkins任务 这里暂时纯手工
的方式触发任务构建。
新建任务 配置源代码拉取地址 配置拉取代码后的打包过程 配置推送服务器的信息 构建后推送配置 Jenkins需要将Git服务器上存放的源码存储到Jenkins服务所在磁盘的本地。
下面是git服务器仓库参数配置。(这一部分配置完后, 执行任务, 就能在jenkins的workplace目录下
看到拉取的代码了)
配置maven打包。(这一步过后, 执行任务, 能)
即配置Publish Over SSH插件参数, 这里需要提前下载Publish Over SSH
插件, 不然系统配置里没有该项配置
。
运行jar的目标服务器这里选择当前虚拟机, 即 192.168.163.21
, 之后配置端口映射的时候, 注意下别出现端口冲突就行。
在系统配置
里, 配置Publish Over SSH
这是在任务配置里面配置的。
到这里, 预期效果是, jenkins能拉取代码并使用maven打包, 最后推送到目标服务器上。
准备docker相关文件 这一步是是将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
同目录下新增docker-compose.yml配置文件
1 2 3 4 5 6 7 8 9 10 11 12 version: '1.0' services: cicd: build: context: ./ dockerfile: Dockerfile image: cicd container_name: cicd ports: - 8081 :8080
这时候打包结果成了下图这样。在此基础上, 我们还需要将docker目录下的两个文件一并发送到目标服务器。
因为jenkins服务器不做构建镜像操作, 所以这里选择把docker配置文件publish到目标服务器。
这时候目标服务器推送目录下的目录结构如下图
这时候如果需要目标服务器做构建镜像并运行的操作, 我们需要jenkins在构建后发送多余的脚本命令。
1 2 3 4 5 cd /usr/local/publish-java-test/docker mv ../target/*.jar ./# 停掉并删除之前的容器 docker-compose down docker-compose up -d --build
测试结果 重新构建, 除了拉镜像特别慢还出现了超时, 其他过程都没问题了
目前流程总结
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即可。
简单测试 maven方式 现在sonarqube服务已经可用, 我们在本地环境的maven的setting.xml配置文件中, 添加上如下内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 <profile > <id > sonar</id > <activation > <activeByDefault > true</activeByDefault > </activation > <properties > <sonar.login > admin</sonar.login > <sonar.password > sonar</sonar.password > <sonar.host.url > http://192.168.163.21:9000</sonar.host.url > </properties > </profile >
然后打开cmd打开项目文件夹, 执行mvn sonar:sonar 命令,
1 2 [ERROR] Failed to execute goal org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184:sonar (default-cli) on project test: Execution default-cli of goal org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184:sonar failed: An API incompatibility was encountered while executing org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184:sonar: java.lang.UnsupportedClassVersionError: org/sonar/batch/bootstrapper/EnvironmentInformation has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
降低sonarqube版本到8.x, 别用9.0+
1 [ERROR] Failed to execute goal org.sonarsource.scanner.maven:sonar-maven-plugin:3.9.1.2184:sonar (default-cli) on project test: Your project contains .java files, please provide compiled classes with sonar.java.binaries property, or exclude them from the analysis with sonar.exclusions property. -> [Help 1]
需要在执行mvn sonar:sonar
前先执行mvn package
测试结果如下
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
修改配置为如下内容, 这里没指定用户名、密码, 所以需借用配置token来登录。
1 2 3 4 5 sonar.host.url =http://192.168.163.21:9000 sonar.sourceEncoding =UTF-8
为sonarqube的当前用户
下面以sonar-scanner命令方式检测代码质量
1 2 3 4 5 6 7 8 [root@localhost bin]# pwd# 这是sonar-scanner的bin目录 /usr/local/jenkins/data/sonar-scanner/bin# 这是jenkins数据卷中Java项目代码路径 [root@localhost bin]# cd /usr/local/jenkins/data/workspace/manual-ci# 下面执行代码检测, 命令敲错了也会有提示的~ [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/
成功了
1 2 3 4 5 6 INFO: -------------------------- INFO: EXECUTION SUCCESS INFO: -------------------------- INFO: Total time: 5.653s INFO: Final Memory: 9M/30M INFO: --------------------------
整合sonarqube到jenkins jenkins安装sonarqube插件
接下来在系统配置
中, 配置SonarQube servers
接下来, 在全局配置
中, 配置SonarQube Scanner
接下来, 在任务
中, 构建后操作
配置如下内容
点击立即构建
, 查看控制台, 出现如下错误。
1 Caused by: java.io.FileNotFoundException: /var/jenkins_home/workspace/manual-ci/.scannerwork/.sonar_lock (Permission denied)
这是因为我们之前手动测试过一次, 生成了这个目录, 导致没有权限, 删除该目录即可。
1 2 3 4 5 [root@localhost manual-ci]# ls -a . .. docker .git .gitignore pom.xml README.md .scannerwork src target [root@localhost manual-ci]# pwd /usr/local/jenkins/data/workspace/manual-ci [root@localhost manual-ci]# rm -rf .scannerwork/
接下来就构建成功了。
目前流程总结
harbor镜像仓库 安装Harbor 现有问题: 如果多个机器都需要jar镜像, 那么这个推送jar并构建镜像的操作就会进行多次, 而这完全是重复的操作。
进行优化:
jenkins容器调用宿主机docker进行打包镜像并推送
发送命令给目标服务器执行拉取镜像的命令
我这里加个虚拟机(192.168.163.28), 专门部署harbor.
浏览器访问harborIP:80
即可
用户名: admin
默认密码: Harbor12345
新建个用户
新建项目(私有)
项目授权给用户使用
之后的操作就用cicd
这个运维账号进行。
整合进Jenkins 尝试命令行推送镜像到harbor仓库
1
名称要求:harborIP地址:80/项目名/镜像名:版本
192.168.163.28:80/cicd/cicd:v1.0.0(端口很重要!!!
)
改insecure-registries
1 2 3 4 { "registry-mirrors" : [ "https://yz5wi4lf.mirror.aliyuncs.com" ] , "insecure-registries" : [ "http://192.168.163.28:80" ] }
刷新配置
1 2 systemctl daemon-reload 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(==重启后好像就不生效了==)
root用户下root组
并让其他用户拥有该文件的读写权限
CentOS在/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
…
1 2 3 4 5 [root@localhost jenkins]# docker exec -it jenkins bash jenkins@780bd8e7b017:/$ docker version Client: Docker Engine - Community Version: 20.10.17 ...
1.去除原来的构建后操作(maven编译打包后推送到目标服务器的/usr/local/publish-java-test, 再构建镜像并运行为容器)
原来的docker-compose.yml就可以不要了
2.新增构建步骤(用于jenkins容器调用宿主机Docker利用项目中的Dockerfile构建镜像)
1 2 3 4 5 mv target/*.jar docker/ docker build -t cicd:v3.0.0 docker/ docker login -u cicd -p Harbor12345 192.168.163.28:80 docker tag cicd:v3.0.0 192.168.163.28:80/cicd/cicd:v3.0.0 docker push 192.168.163.28:80/cicd/cicd:v3.0.0
3.通知目标服务器, 拉取harbor中的镜像
拉取哪个镜像
判断当前服务器是否正在运行该容器, 需要停止并删除
已经存在该镜像, 需要删除
拉取Harbor上的镜像
将镜像运行成容器
准备个脚本文件 cicd-deploy.sh
, 将其扔到目标服务器环境变量路径下, 如/usr/bin
, 设置可执行权限
1 chmod a+x cicd-deploy.sh
harbor地址/项目名/镜像名:镜像版本
端口号
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 harbor_url=$1 harbor_project_name=$2 project_name=$3 tag=$4 export_port=$5 container_port=$6 imageName=$harbor_url/$harbor_project_name/$project_name:$tag containerId=`docker ps -a | grep ${project_name} | awk '{print $1}'` if [ "$containerId" != "" ] ; then docker stop $containerId docker rm $containerId echo "Delete Container Success" fi imageId=`docker images | grep ${project_name} | awk '{print $3}'` if [ "$imageId" != "" ] ; then docker rmi -f $imageId echo "Delete Image Success" fi docker login -u DevOps -p P@ssw0rd $harbor_url docker pull $imageName docker run -d -p $export_port:$container_port --name $project_name $imageName echo "Start Container Success" echo $project_name
添加构建后操作, 让目标服务器执行脚本文件
1 cicd-deploy.sh 192.168.163.28:80 cicd cicd v3.0.0 8090 8080
执行构建即可!
Jenkins流水线优化
另外也提供了基于git仓库中的Jenkinsfile的方式.
当然也能进行参数化构建, 在脚本中使用${}进行引用即可。
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 pipeline { agent any environment { harborUsername = 'cicd' harborPassword = 'Harbor12345' harborAddress = '192.168.163.28:80' repository = 'cicd' containerPort = 8080 exportPort = 8091 } 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('通过Publish Over SSH通知目标服务器' ) { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'gitlab-jenkins-harbor-21' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: "cicd-deploy.sh $harborAddress $repository $JOB_NAME latest $exportPort $containerPort" , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: '' , sourceFiles: '' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )]) } } } }
注意想让execCommand中$引用的变量能生效, 需用双引号
整合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图形化工具 安装文档
常用命令 namespace 1 2 3 kubectl get ns kubectl create ns test kubectl delete ns test
1 2 3 4 apiVersion: v1 kind: Namespace metadata: name: test
1 kubectl apply -f namespace-test.yml
pod 1 2 3 4 5 6 7 8 9 10 11 12 13 kubectl get pods kubectl get pods -A kubectl get pods -n test kubectl run nginx --image=nginx:latest -n test# 查看某一个pod的详细信息 kubectl describe pod nginx kubectl delete pod nginx -n test# 查看pod日志 kubectl logs -f nginx -n test# 进入容器 kubectl exec -it nginx -n test -- bash
一个pod运行多个容器
1 2 3 4 5 6 7 8 9 apiVersion: v1 kind: Pod metadata: name: nginx-yml namespace: test spec: containers: - image: nginx:latest name: nginx-yml
1 2 kubectl apply -f pod-nginx.yml kubectl describe pod nginx-yml -n test
1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Pod metadata: name: nginx-tomcat-yml namespace: test spec: containers: - image: nginx:latest name: nginx - image: tomcat:9.0-jre8 name: tomcat
deployment 1 2 3 4 5 kubectl get deployment kubectl get deployment -n test kubectl delete deployment deploy-nginx -n test kubectl create deployment deploy-nginx --image=nginx:latest -n test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80
1 kubectl apply -f deployment-nginx.yml -n test
service 集群内暴露(type=ClusterIP)
1 kubectl expose deployment nginx-deployment --port=8888 --target-port=80 -n test
1 2 3 [root@k8smaster i-ymls]# kubectl get service -n test NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-deployment ClusterIP 10.96.199.68 <none> 8888/TCP 6s
可以改nginx的index.html内容, 使得接下来每次访问nginx能判断出是否进行了负载均衡。
1 2 3 4 5 6 7 # 在宿主机进行的测试 [root@k8smaster i-ymls]# curl 10.96.199.68:8888 2222 [root@k8smaster i-ymls]# curl 10.96.199.68:8888 3333 [root@k8smaster i-ymls]# curl 10.96.199.68:8888 2222
1 2 3 # 使用域名的方式访问(容器内部才能使用) root@nginx-deployment-585449566-2rlbw:/# curl nginx-deployment.test:8888 3333
1 kubectl delete service nginx-deployment -n test
暴露到集群外: 修改暴露的type
1 2 3 4 5 [root@k8smaster i-ymls]# kubectl expose deployment nginx-deployment --port=8888 --target-port=80 -n test --type=NodePort service/nginx-deployment exposed [root@k8smaster i-ymls]# kubectl get service -n test NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-deployment NodePort 10.96.31.5 <none> 8888:32580/TCP 8s
现在的访问方式
CLUSTER-IP:8888: 集群内访问
k8smasterIP(或者k8sworkerIP):32580: 外部访问
service-name:namespace:8888: 容器内访问
完成一个yml部署service、deployment
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 apiVersion: apps/v1 kind: Deployment metadata: namespace: test name: nginx-deployment labels: app: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: namespace: test name: nginx-deployment labels: app: nginx-deployment spec: selector: app: nginx ports: - port: 8888
Ingress操作 本质上就是nginx
k8s更推荐这种方式, 而非service(毕竟service需要ip地址、端口, 虽然也能固定)
安装成功
停掉之前的service
1 kubectl delete -f deployment-nginx.yml
修改配置, 添加Ingress配置
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 apiVersion: apps/v1 kind: Deployment metadata: namespace: test name: nginx-deployment labels: app: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: namespace: test name: nginx-deployment labels: app: nginx-deployment spec: selector: app: nginx ports: - port: 8888 targetPort: 80 type: NodePort --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: namespace: test name: nginx-ingress spec: ingressClassName: ingress rules: - host: lsycai.nginx.top http: paths: - path: / pathType: Prefix backend: service: name: nginx-deployment port: number: 8888
1 kubectl apply -f deployment-nginx.yml
本地hosts文件(C:\Windows\System32\drivers\etc
)配一个dns
1 192.168.163.21 lsycai.nginx.top
碰到的问题, 是因为windows开了代理
!!!
最终部署 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 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能登陆! 这样才能使得配置的私服镜像能被拉取下来
我们还需去/etc/docker/daemon.json
中的”insecure-registries”中去增加harbor仓库地址(同之前在命令行手动docker login的配置)
1 2 3 4 5 6 7 { "registry-mirrors": ["https://xxxxxx.mirror.aliyuncs.com"], "insecure-registries": ["192.168.163.28:80"] } # 增加下面这串 "insecure-registries": ["192.168.163.28:80"]
重启docker
1 systemctl restart docker
等待k8s能访问即可。
Jenkins, 系统管理/系统配置, 新增一个目标服务器(k8s)
修改git代码中的Jenkinsfile(只有最后一步需要更新): 将pipeline.yml推送到目标服务器指定文件夹
1 2 3 4 5 6 7 8 stages { 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 )]) } } }
Jenkins, 任务配置中, 构建
, 执行shell
命令修改为如下代码
1 2 3 4 5 mv target/*.jar docker/ docker build -t pipeline:latest docker/ docker login -u cicd -p Harbor12345 192.168.163.28:80 docker tag pipeline:latest 192.168.163.28:80/cicd/pipeline:latest docker push 192.168.163.28:80/cicd/pipeline:latest
最后需要让目标服务器(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
让目标服务器(k8smaster执行kubectl apply -f pipeline.yml
)
1 ssh root@192.168.163.29 kubectl apply -f pipeline.yml
将其生成流水线脚本, 追加一步groovy代码
1 2 3 4 5 stage('jenkins容器ssh调用k8smaster执行kubectl命令部署' ) { steps { sh 'ssh root@192.168.163.29 kubectl apply -f pipeline.yml' } }
最终的Jenkinsfile如下
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 pipeline { // 指定任务在Jenkins哪个节点执行 agent any // 声明全局变量 environment { harborUsername = 'cicd' harborPassword = 'Harbor12345' harborAddress = '192.168.163.28:80' repository = 'cicd' // 参数化构建也可, 为了方便就在这里定义了 containerPort = 8080 exportPort = 8091 } 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' } } } }
自动化CI
推送代码后, 如何让Jenkins自动执行任务
安装Gitlab插件
暂时做如下图的配置
点击添加会报错
解决方案
重新添加Webhooks即可。
测试出发推送事件, 又会报错
Jenkins, 全局配置中找到Gitlab
这下就能正常触发Jenkins的任务了。
解决kubectl apply
在文件未变化时的不会更新pod的问题
Jenkinsfile的kubectl apply -f /usr/local/jenkins-push-k8s/pipeline.yml
之后, 新增代码
1 2 # 在其之后新增一段为如下代码, 指定名为pipeline的deployment进行滚动部署 kubectl rollout restart deployment pipeline -n test
最终的groovy代码
1 2 3 4 5 6 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''' } }
问题总结
hosts 翻墙
Secrets在yml的配置
Webhooks Gitlab插件