环境准备
下载安装vm
下载centos7镜像
虚拟机中没有IP解决方案
- vi /etc/sysconfig/network-scripts/ifcfg-ens33
- onboot设置为yes
- systemctl restart network 重启网络
- 关闭防火墙命令
systemctl status firewalld.service
systemctl stop firewalld.service
gitlab+jenkis+tomcat集成springboot项目
安装gitlab (192.168.174.128)
- 安装相关依赖
yum -y install policycoreutils openssh-server openssh-ckuebts postfix
- 启动ssh服务&设为开机启动
systemctl enable sshd && sudo systemctl start sshd
- 设置postfix开机自启,并启动,postfix支持gitlab发信功能
systemctl enable postfix && systemctl start postfix
- 开放ssh以及http服务,然后重新加载防火墙列表 (防火墙关闭则忽略操作)
firewall-cmd --add-service=ssh --permanent
firewall-cmd --add-service=http --permanent
firewall-cmd --reload
- 下载gitlab包,并且安装
- 安装
rpm -ivh gitlab-ce-12.4.2-ce.0.el7.x86_64.rpm
- 如果报了这个错,运行以下命令
- yum install policycoreutils-python
- 然后重新执行解压命令
- 修改gitlab外部访问端口和地址
vi /etc/gitlab/gitlab.rb
- wq保存
- 重载配置及启动gitlab (需要等好几分钟)
-
gitlab-ctl reconfigure
-
gitlab-ctl restart
-
- 把端口添加到防火墙
- firewall-cmd --zone=public --add-port=82/tcp --permanent
- firewall-cmd --reload
操作gitlab
- 访问 http://192.168.174.128:82/
- 在登陆界面更改根密码,第一次登陆需要更改
- 更改完成后使用root + 密码登陆访问
- 创建组、用户、项目
- New Project
- admin area中创建新用户
- 创建完成后点击Edit设置密码
- 进入groups
- 选择成员
- 选择刚才创建好的成员,并且赋予权限
- Guest:可以创建issue、发表评论,不能读写版本库
- Reporter:可以克隆代码,不能提交,QA、PM可以赋予这个权限
- Developer:可以克隆代码、开发、提交、push,普通开发可以赋予这个权限
- Maintainer:可以创建项目、添加tag、保护分支、添加项目成员、编辑项目,核心开发可以赋予这个
权限 - Owner:可以设置项目访问权限 - Visibility Level、删除项目、迁移项目、管理组成员,开发组组
长可以赋予这个权限
- 使用张三的账号登录
- 首次登陆需要重置密码
- 首次登陆需要重置密码
创建一个项目并上传源码
- 新建一个项目并且添加servlet-api依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
- VCS中选择Enable Version Control Integration
- 选择git
- 右键项目,选择git,+add
- add后再次点击commit directory提交
- 此时已经本地提交
- 选择git中的manager Remotes或者remotes
- 登陆
- git push
- 刷新项目(代码提交成功)
jenkins安装和持续集成环境配置(192.168.174.129)
- 安装jdk依赖
yum install java-1.8.0-openjdk* -y
下载jenkins 或者使用 rpm 安装包
- 官网 https://www.jenkins.io/download/
- 清华镜像站 https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/
- 解压
rpm -ivh jenkins-2.346.1-1.1.noarch.rpm
- 修改jenkins配置
vi /etc/sysconfig/jenkins
- 启动jenkins
systemctl start jenkins
-
把端口添加到防火墙
- firewall-cmd --zone=public --add-port=8888/tcp --permanent
- firewall-cmd --reload
-
也可以直接关掉防火墙
-
访问 http://192.168.174.129:8888/ 发现配置的8888没有生效
-
vi /usr/lib/systemd/system/jenkins.service
- 把里面的端口和用户名和组改掉,不然后面有坑。。
- 把里面的端口和用户名和组改掉,不然后面有坑。。
-
systemctl restart jenkins
- 如果报错Restarting jenkins (via systemctl): Warning: jenkins.service changed on disk. Run ‘systemctl daemon-reload’ to reload units.
- 那就先输入 systemctl daemon-reload
-
-
cat /var/lib/jenkins/secrets/initialAdminPassword 获取密码
-
跳过插件安装
-
自定义插件安装(选择插件来安装)
-
选择无
-
-
安装并创建第一个用户
-
安装插件
-
进入Manage Jenkins -> Pulgin Manager
- 需先中Available等待加载完成
- 在服务器查看jenkins插件下载地址
- cd /var/lib/jenkins/updates
- ll
- 把插件下载地址替换成国内的
sed -i 's/http:\/\/updates.jenkinsci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json && sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json
- 在Advanced中把插件下载地址替换成国内的
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
- restart重启jenkins
- 下载中文语言包
- 在插件管理处勾选中插件,选择下载完重启
- 在插件管理处勾选中插件,选择下载完重启
- 下载完成后重启
- 已经替换为了中文,使用账号登陆(zhangsan)
jenkins权限管理
-
安装插件 Role-based Authorization Strategy
-
安装完成
-
点击主菜单,进入全局安全配置
-
选择新下载的策略并保存
- role-based strategy
- role-based strategy
-
进入角色管理
-
进入Manage Roles
- 在Global roles中 新建一个baseRole
- 勾选read权限
-
item roles中新建项目权限
- 勾选所有权限
- 规则中配置访问 xxx.* 来配置可访问url
- role1 -> test.*
- role2 -> web.*
-
保存
-
user manager中 新建两个用户
-
使用新用户登陆做测试
- 没有权限访问任何内容
- 没有权限访问任何内容
-
使用管理员账户登录,分配角色
-
使用管理员账号分配权限
- test1分配role1
- test2分配role2
- test1和test2使用baseRole权限
-
新建两个项目
- web01
- test01
-
管理员账号可以看到两个项目
-test1 可以看到web01
-test2 可以看到test01
jenkins凭证管理
- 安装插件
- 在Manager中多出个凭证管理
- 进入全局,查看(添加凭据)
- 一共有五种
- Username with password:用户名和密码登陆凭证 (常用)
- SSH Username with private key: 使用SSH用户和密钥 (ssh免密登陆)(常用)
- Secret file:需要保密的文本文件,使用时Jenkins会将文件复制到一个临时目录中,再将文件路径
设置到一个变量中,等构建结束后,所复制的Secret file就会被删除。 - Secret text:需要保存的一个加密的文本串,如钉钉机器人或Github的api token
- Certificate:通过上传证书文件的方式
以gitlab为例进行测试
- 安装git插件
- 创建一个空项目
- 发现已经可以选择git
- 在jenkins服务器中安装git
yum install git -y
-
用户密码的方式
- 新建一个凭证,选择账号密码方式
- 使用(gitlab)张三的账号密码,填写描述
-
选择刚才创建的test-git项目
- 进入配置
- 选择git方式,填写gitlab中的http地址
- 选择刚才创建好的凭证
- 保存
- 点击build now
- 点击#1进入,查看控制台输出,已经完成了first commit代码拉取
- 进入build in workspace目录,发现代码已经存在
- 进入配置
-
ssh私钥凭证形式
- 免密登陆示意图
- 在jenkins服务器存放私钥
- 在gitlab服务器存放公钥
- 使用root用户生成公钥和秘钥
- ssh-keygen -t rsa
- 使用root用户生成公钥和秘钥(不输入任何东西,全部回车跳过)
- cd /root/.ssh 查看公钥(.pub)和私钥
- 使用root账号登陆gitlab
- 点击头像处settings 找到 ssh keys
- 把公钥的内容粘贴到右侧文本处
- 复制完成点击add key
- 免密登陆示意图
-
配置好公钥后先在服务器测试一下拉取代码
- jenkins配置私钥
- 添加一个凭证,选择SSH的形式
- 添加描述
- 把私钥从服务器复制到Enter directly中
- jenkins配置私钥
-
添加一个新项目
-
拷贝ssh连接
-
选择刚才创建好的用户
-
build now
jenkins安装maven+jdk
- maven下载地址 https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/
- 上传到根目录 解压
- tar -xzf apache-maven-3.6.3-bin.tar.gz
- mkdir /opt/maven
- mv apache-maven-3.6.3/* /opt/maven
- vi /etc/profile 修改环境变量
- 文末追加
- tar -xzf apache-maven-3.6.3-bin.tar.gz
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.342.b07-1.el7_9.x86_64
export MAVEN_HOME=/opt/maven
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
- 执行 source /etc/profile 命令
- 执行mvn -v ,查看版本号
-
进入jenkins Manage Jenkins -> Global Tool Configuration
- 配置jdk
- 配置maven
-
jdk
-
maven
-
进入Configure System
- 全局变量添加以下三个键值对
JAVA_HOME
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.342.b07-1.el7_9.x86_64
M2_HOME
/opt/maven
PATH+EXTRA
$M2_HOME/bin
-
更改maven settings配置
- 新建一个仓库地址 mkdir -p /opt/repo
- 编辑 maven的settings.xml文件
-
更改仓库地址
-
在镜像处追加阿里云地址
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
-
测试构建maven
- 进入之前创建好的test-git项目
- 在 构建部分增加以下shell指令
-
如果遇到HTTP ERROR 403 No valid crumb is included in the request
- 在 安全配置中勾选此项
- 在 安全配置中勾选此项
-
如果遇到以下报错信息
-
改变 目录 /opt/repo 的用户组 为 jenkins 即可。
- chown -R jenkins:jenkins /opt/repo
-
第一次会从仓库下载很多东西
-
保存后重新build now
-
在代码目录发现已经成功打包
tomcat服务器搭建 (192.168.174.130)
- 安装jdk
yum install java-1.8.0-openjdk* -y
- tomcat安装 tomcat官网
- 下载,上传,解压tomcat
- 下载,上传,解压tomcat
- tar -xzf apache-tomcat-8.5.47.tar.gz
- mkdir -p /opt/tomcat 创建目录
- mv /root/apache-tomcat-8.5.47/* /opt/tomcat 移动到创建好的目录
- /opt/tomcat/bin/startup.sh 启动tomcat
- 访问 http://192.168.174.130:8080/
- 访问不到,把端口添加到防火墙
- firewall-cmd --zone=public --add-port=82/tcp --permanent
- firewall-cmd --reload
- 配置tomcat权限 (jenkins使用)
- 进入tomcat 的conf目录
- vi tomcat-users.xml 并添加以下内容
- 进入tomcat 的conf目录
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager-script"/>
<role rolename="manager-gui"/>
<role rolename="manager-status"/>
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<user username="tomcat" password="tomcat" roles="manager-gui,manager-script,tomcat,admin-gui,admin-script"/>
-
进入 /opt/tomcat/webapps/manager/META-INF/ 修改context.xml
- 注释掉此行
- 注释掉此行
-
重启tomcat
- 点击 manager webapp
jenkins构建项目部署到tomcat
- 常见的构建方式有以下三种
- 自由风格 (FeeStyle Project)
- maven项目 (Maven Project)
- 流水线项目 (Pipeline Project) -> 灵活度高
自由风格构建流程
- 拉取代码->编译->打包->部署
自由风格项目实战
- 创建一个freestyle项目
- 选择连接方式
- 构建项目
- 编译打包
echo "开始编译和打包"
mvn clean package
echo "编译和打包完成"
-
在项目中添加一个shell脚本
-
构建build now ,构建完成后项目下会产生target目录,下面有war包
-
安装远程部署插件 Deploy to container
-
回到项目添加一个构建后操作 Depoly war/ear to a container
-
war路径写一个通配符 target/*.war 扫描所有target下的war包
-
选择tomcat8.x容器
-
新增tomcat角色权限
-
选择刚才创建好的tomcat账号,填写tomcat地址 http://192.168.174.130:8080
-
tomcat管理界面刷新查看 http://192.168.174.130:8080/manager/html
持续集成 -
代码修改
-
本地提交代码
-
push到远程仓库
-
重新构建
- 刷新服务器,看到已经变更了
Maven风格项目实战
- 安装插件 Maven Integration
- 新建一个maven项目
- 拉取源码
- maven构建与自由风格的区别在于会执行一个pom.xml文件
- 如果有子项目,在root pom 处前加上子项目目录
- 在Goals and options 直接写要执行的mvn指令,不需要指出mvn
- 配置tomcat
- 新增一行代码,提交(与之前的项目区分一下)
- 提交完成build now
Pipeline(流水线)构建项目
- 支持两种语法
- Declarative(声明式)
- Scripted (脚本式)
- 创建方式
- jenkins 的Web UI 界面
- jenkinsfile脚本文件(代码)
- 安装插件
- 新建流水线项目
-
流水线脚本选择
-
hello word
- 基于声明式的模板
- pipeline开头标记
- jenkins推荐使用
- stages:代表整个流水线的所有执行阶段。通常stages只有1个,里面包含多个stage
- stage:代表流水线中的某个阶段,可能出现n个。一般分为拉取代码,编译构建,部署等阶段。
- steps:代表一个阶段内需要执行的逻辑。steps里面是shell脚本,git拉取代码,ssh远程发布等任意内
容
-
Scripted Pipeline
- node开头标记
- 脚本式
- Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行
环境,后续讲到Jenkins的Master-Slave架构的时候用到。 - Stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如:
Build、Test、Deploy,Stage 是一个逻辑分组的概念。 - Step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,
由各类 Jenkins 插件提供,比如命令:sh ‘make’,就相当于我们平时 shell 终端中执行 make 命令
一样。
- Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行
脚本体验测试
- 声明式
pipeline {
agent any
stages {
stage('pull code') {
steps {
echo 'pull code'
}
}
stage('build project') {
steps {
echo 'build project'
}
}
stage('publish project') {
steps {
echo 'publish project'
}
}
}
}
- Scripted
node {
def mvnHome
stage('pull code') {
echo 'pull code'
}
stage('Build') {
echo 'pull Build'
}
stage('Results') {
echo 'pull Results'
}
}
拉取代码+编译打包
- 进入配置,找到流水线语法
拉取代码
- 在片段生成器中,选择check out from version control
- 生成流水线脚本
- credentialsId 对应ssh连接凭证
- credentialsId 对应ssh连接凭证
- 把流水线脚本复制到配置
- build now 构建
- 查看代码,拉取成功
编译打包
- 脚本生成器选择 sh:shell script
- 输入 mvn clean package 生成脚本
- 输入 mvn clean package 生成脚本
- 把脚本拷贝到第二个stage
- 构建完成,并且生成了war包
发布到远程服务器
- 脚本生成器选择 deploy:Deploy war/ear to a container
- 脚本复制到第三个stage
- 执行成功
jenkinsfile脚本
-
好处
- 利于版本控制
- 不会因jenkins服务器崩溃丢失
- 。。。
-
代码中新建Jenkinsfile文件
- 文件中把上面的脚本复制到文件内,提交到gitlab仓库
- 文件中把上面的脚本复制到文件内,提交到gitlab仓库
- 流水线中选择 from SCM
- 配置获取地址,以及文件名(默认存放在项目根目录)
- build now构建测试
构建触发器
- jenkins内置的4中构建触发器
- 触发远程构建
- 其他工程构建后触发
- 定时构建
- 轮询SCM
- 触发远程构建
- 在设置中选择触发远程构建,配置token令牌(生产环境使用加密字符串,测试使用6666)
- 当前构建为第9次
- 打开浏览器输入 http://192.168.174.129:8888//job/web_demo_pipeline/build?token=6666
- 可以看到已经开始了第10次构建
- 构建成功
- 其它工程构建后触发
- 建立一个新的自由风格工程(任意类型都可以)
- 里面只执行输出一句话
- 刚才的项目中取消 触发远程构建,选择build after other projects are built
- 输入前一步创建好的工程名
- 输入前一步创建好的工程名
- 构建前置工程
- 刚才配置好的项目开始构建第11次
- 构建成功
定时构建字符串
- 从左往右分别分:分 时 日 月 周
- H代表形参 是可以变化的,H可以替换为0,代表准点
每30分钟构建一次:
H/30 * * * *
执行时间为 00:00 00:30 。。。
每2个小时构建一次:
H H/2 * * *
每天的8点,12点,22点,一天构建3次: (多个时间点中间用逗号隔开)
0 8,12,22 * * *
每天中午12点定时构建一次
H 12 * * *
每天下午18点定时构建一次
H 18 * * *
在每个小时的前半个小时内的每10分钟
H(0-29)/10 * * * *
每两小时一次,每个工作日上午9点到下午5点
(也许是上午10:38,下午12:38,下午2:38,下午4:38)
H H(9-16)/2 * * 1-5
-
定时构建
-
设置中选择 Build periodically
-
等待2分钟左右,就可以看到开始第12次构建
-
再等两分钟,可以看到第13次构建
-
轮询SCM构建
-
定时扫描本地代码仓库的代码是否有变更,如果有就触发构建项目
-
jenkins会定时扫描本地整个项目的代码,增大系统开销,不建议使用
- 等待两分钟,没有触发构建
- 代码做一些改动提交,则会触发构建
- 没做测试,感兴趣的可以自己试试
git hook自动触发构建 (着重推荐使用方案)
- 安装插件
- GitLab
- Gitlab Hook ()
- 配置中选择 Build when a change is pushed to GitLab. GitLab webhook URL: http://192.168.174.129:8888/project/web_demo_pipeline
- 使用root账号登陆gitlab
- 找到settings中的network
- 勾选Allow requests to the local network from web hooks and services保存
- 打开项目,找到settings中的Intergrations
- 把上一步产生的地址填写到URL,Add webhook
- test 测试推送
- 返回403信息
- 在jenkins的configure system中,找到gitlab模块,把Enable authentication for ‘/project’ end-point的勾选取消
- 再次测试
- jenkins触发了构建事件
- 代码中修改片段测试
- push完成后开始构建
- 构建完成,成功部署
jenkins参数化构建
- 在配置中,把现有构建触发器取消
- 在This project is parameterized选择String Parameter
- 在This project is parameterized选择String Parameter
- 名称输入branch,默认值master
- 保存后菜单栏多出一行build with parameters
- 在代码中把branches中的值改为 */${branch}
- 在测试jsp中修改文字
- 代码push到仓库(master分支)
- 在代码中选择git - > Branches
- 创建v1分支
- 修改jsp代码
- push代码到v1分支
- 在gitlab上查看
- 构建master分支
- 构建完成后
- 构建v1分支
- 构建完成后
jenkins配置邮箱服务器发送构建结果
-
下载插件 Email Extension Template
- 默认版本的插件会导致发送邮件失败,从下放连接找到2.7.1版本进行下载
- https://plugins.jenkins.io/email-ext/#releases
-
在插件管理,高级中找到Deploy Plugin,选择下载好的hpi文件,Deploy
-
使用2.7.1版本
-
进入QQ邮箱客户端
- 在设置,账户下开启POP3/IMAP/SMTP服务
- 需要验证手机号,并返回给一个字符串授权码
- wystxlmanijrbeda
- 进入Configure System->Extended E-mail Notification
- smtp服务器
- smtp端口 465
- 勾选使用ssl
- 勾选Use SMTP Authentication
- 输入邮箱和令牌
- 默认点子邮件后缀
- 默认内容类型选择html
- 默认收件人
- jenkins location中把管理员邮件地址改成收件地址
- jenkins默认邮件通知
- smtp服务器
- 默认后缀
- 使用SMTP认证并且输入邮箱和刚才的那一串令牌
- 使用SSL协议
- 端口改成465
- 回复邮箱同收件邮箱
- 测试邮件发送
- 定制邮件发送通用模板
- 把邮件html房子根目录与src平级,否则按下面的示例会导致找不到html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0"
style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>(本邮件是程序自动下发的,请勿回复!)</td>
</tr>
<tr>
<td><h2>
<font color="#0000FF">构建结果 - ${BUILD_STATUS}</font>
</h2></td>
</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>项目名称 : ${PROJECT_NAME}</li>
<li>构建编号 : 第${BUILD_NUMBER}次构建</li>
<li>触发原因: ${CAUSE}</li>
<li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
<li>构建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
<li>项目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
</ul>
</td>
</tr>
<tr>
<td><b><font color="#0B610B">Changes Since Last
Successful Build:</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>历史变更记录 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li>
</ul> ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n:<br />%c<br />",showPaths=true,changesFormat="<pre>[%a]<br />%m</pre>",pathFormat=" %p"}
</td>
</tr>
<tr>
<td><b>Failed Test Results</b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><pre
style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">$FAILED_TESTS</pre>
<br /></td>
</tr>
<tr>
<td><b><font color="#0B610B">构建日志 (最后 100行):</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td><textarea cols="80" rows="30" readonly="readonly"
style="font-family: Courier New">${BUILD_LOG, maxLines=100}</textarea>
</td>
</tr>
</table>
</body>
</html>
-
模板中的参数为jenkins固定值
- 具体其它参数可以从安全配置中找到content token reference点击小问号展开查看
- 具体其它参数可以从安全配置中找到content token reference点击小问号展开查看
-
在jenkinsfile中配置邮件发送
- 流水线语法中找到 Directive Generator,选择post
- 流水线语法中找到 Directive Generator,选择post
-
根据提示可以生成对应脚本
- 片段生成器可以选择emailext
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: 'f9831a99-2028-4c36-b10a-d9a36d687f42', url: 'git@192.168.174.128:first_group/web_demo.git']]])
}
}
stage('build project') {
steps{
sh 'mvn clean package'
}
}
stage('publish project') {
steps{
deploy adapters: [tomcat8(credentialsId: '726b8934-8c30-4b05-941a-2d7cdeff151b', path: '', url: 'http://192.168.174.130:8080')], contextPath: null, war: 'target/*.war'
}
}
}
post {
always {
emailext body: '${FILE,path="email.html"}',
subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} -${BUILD_STATUS}!',
to: '843635953@qq.com'
}
}
}
- 把代码提交到gitlab
- 构建代码提交的分支
- 构建成功通知
代码审查 SonarQube
- 先安装mysql5.7
-
wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
-
yum -y install mysql57-community-release-el7-10.noarch.rpm
-
yum -y install mysql-community-server
-
- 如果提示软件包失败。。。公钥尚未安装
- 先执行 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022
- 再次执行 yum -y install mysql-community-server
- systemctl start mysqld
- grep “password” /var/log/mysqld.log
- 得到初始秘钥 d&PfEfytd1uB
- 得到初始秘钥 d&PfEfytd1uB
- mysql -uroot -p
- 使用上一步的密码登陆
- 直接改密码会报错
- set global validate_password_policy=LOW;
- set global validate_password_length=6;
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
grant all privileges on *.* to 'root'@'192.168.174.129' identified by '123456' with grant option;
- flush privileges;
- exit
- 防火墙添加3306
- firewall-cmd --zone=public --add-port=3306/tcp --permanent
- firewall-cmd --reload
- mysql -uroot -p 123456
- use mysql
- select host, user from user;
update user set host='%' where user='root';
或者update user set host='%' where user='localhost'; //看查出来的名字什么
- flush privileges;
- 这个错误不用理会
- 安装sonarqube
- 官网 https://www.sonarqube.org/downloads/
- 先创建一个sonar的数据库
- 先创建一个sonar的数据库
- 压缩包下载好上传至虚拟机
- unzip sonarqube-6.7.4.zip
- mkdir /opt/sonar
- mv sonarqube-6.7.4/* /opt/sonar
- 创建普通用户sonar用于启动,用root会报错
- useradd sonar
- chown -R sonar. /opt/sonar 更改sonar目录及文件权限
- vi /opt/sonar/conf/sonar.properties
sonar.jdbc.username=root
sonar.jdbc.password=123456
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?
useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=
maxPerformance&useSSL=false
- sonar 监听数据库9000端口,如果9000被占用,需要改掉
- cd /opt
- su sonar /opt/sonar/bin/linux-x86-64/sonar.sh start
su sonar /opt/sonar/bin/linux-x86-64/sonar.sh start 启动
su sonar /opt/sonar/bin/linux-x86-64/sonar.sh status 查看状态
su sonar /opt/sonar/bin/linux-x86-64/sonar.sh stop 停止
tail -f /opt/sonar/logs/sonar.log 查看日志
- 查看日志,出现Process[ce] is up 就是成功了
- 防火墙放开9000端口
- firewall-cmd --zone=public --add-port=9000/tcp --permanent
- firewall-cmd --reload
- http://192.168.174.129:9000/
- 默认账号密码都是admin,点击登录
- 输入一个名字生成秘钥
- 994b30f143cfa4ed135dd905c40ec166feef7917
jenkins+SonarQube代码审查
- 安装SonarQube Scanner插件
- 添加全局配置
- Configure System->SonarQube servers配置
- Global Tool Configuration 配置SonarQube Scanner
- 非流水线项目 (测试)
- 进入web_demo_freestyle构建部分
- 新增Execute SonarQube Scanner构建步骤
- Task to run (scan)
- 选择jdk8
- 填写以下内容
# must be unique in a given SonarQube instance
sonar.projectKey=web_demo_freestyle
# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
sonar.projectName=web_demo_freestyle
sonar.projectVersion=1.0
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
# This property is optional if sonar.modules is set.
sonar.sources=.
sonar.exclusions=**/test/**,**/target/**
sonar.java.source=1.8
sonar.java.target=1.8
# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
- build now 构建
- 构建完成查看
- 构建完成查看
- 成功捕获错误
- 流水线项目 (测试)
- 代码中根目录新增配置文件sonar-project.properties
# must be unique in a given SonarQube instance
sonar.projectKey=web_demo_pipline
# this is the name and version displayed in the SonarQube UI. Was mandatoryprior to SonarQube 6.1.
sonar.projectName=web_demo_pipline
sonar.projectVersion=1.0
# Path is relative to the sonar-project.properties file. Replace "\" by "/" onWindows.
# This property is optional if sonar.modules is set.
sonar.sources=.
sonar.exclusions=**/test/**,**/target/**
sonar.java.source=1.8
sonar.java.target=1.8
# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
- jenkinsfile中添加
- sq-scanner 来自于全局工具配置SonarQube Scanner
- sonarqube-scanner 来自于系统配置 SonarQube servers
stage('code check') {
steps{
script {
scannerHome = tool 'sq-scanner'
}
withSonarQubeEnv('sonarqube-scanner') {
sh "${scannerHome}/bin/sonar-scanner"
}
}
}
- 提交代码
- web_demo_pipeline 构建
springcloud+jenkins+docker集成
docker安装 (以下服务器都需要装)
- jenkins服务器
- docker + harbor镜像仓库
- 生产服务器(n台)
- 使用虚拟机的话,可以用一台新的下载docker,然后镜像2-3台作为生产服务器
安装流程
- 卸载旧版本(没有安装可忽略)
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
- 安装必要的软件包
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
- 设置下载的镜像源(三选一)
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum-config-manager \
--add-repo \
https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
- 查看列表
yum list docker-ce --showduplicates | sort -r
- 安装docker
sudo yum install docker-ce-18.06.1.ce
- 查看版本
docker -v
- 启动Docker
启动
sudo systemctl start docker
设置开机启动
sudo systemctl enable docker
- 添加阿里云镜像下载地址 镜像加速地址获取
- 勿使用下文
vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://3e=x2n.mirror.aliyuncs.com"]
}
sudo systemctl daemon-reload
sudo systemctl restart docker
docker info 查看
- 下载openjdk
docker pull openjdk:8-jdk-alpine
- 安装docker-compose
curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
- docker-compose -version
安装docker私有仓库harbor(192.168.174.131)
- 地址 https://github.com/goharbor/harbor/releases
- 下载安装包
- 上传解压
- 创建文件夹
- 移动到指定目录
- 修改配置
tar -xzf harbor-offline-installer-v1.9.2.tgz
mkdir /opt/harbor
mv harbor/* /opt/harbor
cd /opt/harbor
vi harbor.yml
修改hostname和port
hostname: 192.168.174.131
port: 85
- 执行准备命令 ./prepare
- 执行 ./install.sh
- 访问 http://192.168.174.131:85/
- 默认账户:admin
- 默认密码:Harbor12345
创建项目
创建用户
-
zhangsan Zhangsan123456
-
在tensquera项目中的成员添加用户
-
使用zhangsan用户登陆查看
-
把Harbor地址加入到jenkins的Docker信任列表 (用于上传镜像)
- 在jenkins服务器中vi /etc/docker/daemon.json
- 添加一行"insecure-registries": [“192.168.174.131:85”]
{
"registry-mirrors": ["https://3e=x2n.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.174.131:85"]
}
- 重启jenkins的docker
- systemctl restart docker
- 使用同样的方法在harbor中添加jenkins的Docker信任列表 (用于下载镜像)
- 在harbor服务器中vi /etc/docker/daemon.json
- 添加一行"insecure-registries": [“192.168.174.129:85”]
{
"registry-mirrors": ["https://3e=x2n.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.174.131:85"]
}
- 重启harbor的docker
- systemctl restart docker
SSH远程调用安装
- Publish Over SSH
-
在jenkins服务器执行 ssh-copy-id pro1服务器IP
- ssh-copy-id 192.168.174.132
- ssh-copy-id 192.168.174.132
-
Configure System 目录下找到 jenkins ssh key
- 把authorized_keys秘钥输入到密码栏
- path to key 为jenkins 的id_rsa目录
- 添加一个ssh server
-
ip为pro
-
name 随便写
-
username 为 root
-
路径 /
-
测试连接
-
主节点
-
-
从节点
jenkins添加harbar凭证
代码上传到仓库
- gitlab新建前端和后台项目
- tensquareAdmin (前端代码)
- tensquare_parent (微服务代码)
- 上传
后台部署 (192.168.174.132 + 192.168.174.133)
192.168.174.132为pro1,192.168.174.133为pro2
- 新建流水线项目
- 配置git,使用jenkinsfile
- 安装多选插件 Extended Choice Parameter
- 配置中添加参数
-
进入构建部分,发现已经可以多选,并且有具体描述
-
jenkinsfile
//git凭证ID
def git_auth = "f9831a99-2028-4c36-b10a-d9a36d687f42"
//git的url地址
def git_url = "git@192.168.174.128:root/tensquare_parent.git"
//镜像的版本号
def tag = "latest"
//Harbor的url地址
def harbor_url = "192.168.174.131:85"
//镜像库项目名称
def harbor_project = "tensquare"
//Harbor的登录凭证ID
def harbor_auth = "ff0da5ec-efae-4fd5-bad3-d968e9432055"
node {
//获取当前选择的项目名称
def selectedProjectNames = "${project_name}".split(",")
//获取当前选择的服务器名称
def selectedServers = "${publish_server}".split(",")
stage('拉取代码') {
checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
}
stage('代码审查') {
for(int i=0;i<selectedProjectNames.length;i++){
//tensquare_eureka_server@10086
def projectInfo = selectedProjectNames[i];
//当前遍历的项目名称
def currentProjectName = "${projectInfo}".split("@")[0]
//当前遍历的项目端口
def currentProjectPort = "${projectInfo}".split("@")[1]
//定义当前Jenkins的SonarQubeScanner工具
def scannerHome = tool 'sq-scanner'
//引用当前JenkinsSonarQube环境
withSonarQubeEnv('sonarqube-scanner') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
stage('编译,安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译,打包微服务工程,上传镜像') {
for(int i=0;i<selectedProjectNames.length;i++){
//tensquare_eureka_server@10086
def projectInfo = selectedProjectNames[i];
//当前遍历的项目名称
def currentProjectName = "${projectInfo}".split("@")[0]
//当前遍历的项目端口
def currentProjectPort = "${projectInfo}".split("@")[1]
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
//定义镜像名称
def imageName = "${currentProjectName}:${tag}"
//对镜像打上标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
//把镜像推送到Harbor
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
//登录到Harbor
sh "docker login -u ${username} -p ${password} ${harbor_url}"
//镜像上传
sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
sh "echo 镜像上传成功"
}
//遍历所有服务器,分别部署
for(int j=0;j<selectedServers.length;j++){
//获取当前遍历的服务器名称
def currentServerName = selectedServers[j]
//加上的参数格式:--spring.profiles.active=eureka-server1/eureka-server2
def activeProfile = "--spring.profiles.active="
//根据不同的服务名称来读取不同的Eureka配置信息
if(currentServerName=="master_server"){
activeProfile = activeProfile+"eureka-server1"
}else if(currentServerName=="slave_server"){
activeProfile = activeProfile+"eureka-server2"
}
//部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServerName}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project $currentProjectName $tag $currentProjectPort $activeProfile", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
- dockerfile模板
#FROM java:8
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 10086
ENTRYPOINT ["java","-jar","/app.jar"]
- jenkins_shell脚本 deployCluster.sh
#! /bin/sh
#接收外部参数
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5
profile=$6
imageName=$harbor_url/$harbor_project_name/$project_name:$tag
echo "$imageName"
#查询容器是否存在,存在则删除
containerId=`docker ps -a | grep -w ${project_name}:${tag} | awk '{print $1}'`
if [ "$containerId" != "" ] ; then
#停掉容器
docker stop $containerId
#删除容器
docker rm $containerId
echo "成功删除容器"
fi
#查询镜像是否存在,存在则删除
imageId=`docker images | grep -w $project_name | awk '{print $3}'`
if [ "$imageId" != "" ] ; then
#删除镜像
docker rmi -f $imageId
echo "成功删除镜像"
fi
# 登录Harbor
docker login -u zhangsan -p Zhangsan123456 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName $profile
echo "容器启动成功"
- 把脚本放在pro服务器中,在opt目录下新建jenkins_shell,并授权
- chmod +x deployCluster.sh
- pro服务器添加harbor镜像信任(用于下载镜像)
- systemctl daemon-reload
- systemctl restart docker
{
"registry-mirrors": ["https://3eygzx2n.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.174.131:85"]
}
- build 测试
- 代码检测执行成功
-
maven构建成功
-
镜像上传成功
-
镜像下载成功
-
postman测试接口
前端web网站部署 (192.168.174.132)
pro1安装nginx
- yum install epel-release
- yum -y install nginx
- 修改nginx的端口,默认80,改为9090:
- vi /etc/nginx/nginx.conf
server {
listen 9090;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
-
还需要关闭selinux,将SELINUX=disabled
- 永久关闭 vi /etc/selinux/config 编辑文件
- SELINUX=disabled
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
- nginx命令
systemctl enable nginx 设置开机启动
systemctl start nginx 启动
systemctl stop nginx 停止
systemctl restart nginx 重启
- 启动Nginx
- 防火墙放开9090端口
- firewall-cmd --zone=public --add-port=9090/tcp --permanent
- firewall-cmd --reload
- 如果下图报错
- setenforce 0 临时关闭selinux防火墙,然后再启动
http://192.168.174.132:9090/
- setenforce 0 临时关闭selinux防火墙,然后再启动
- 安装NodeJS插件
- 配置环境 Global Tool Configuration
- 新建一个web_vue_pipeline 流水线项目
- 添加参数 This project is parameterized
- 流水线添加脚本
- git_auth 为gitlab的ssh凭证
- url 为代码地址
//gitlab的凭证
def git_auth = "f9831a99-2028-4c36-b10a-d9a36d687f42"
node {
stage('拉取代码') {
checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']],doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],userRemoteConfigs: [[credentialsId: "${git_auth}", url:'git@192.168.174.128:root/tensquareadmin.git']]])
}
stage('打包,部署网站') {
//使用NodeJS的npm进行打包
nodejs('nodejs12'){
sh '''
npm install
npm run build
'''
} /
sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server',transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '',execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes:false, patternSeparator: '[, ]+', remoteDirectory: '/usr/share/nginx/html',remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')],usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
- build master
- 构建完成 http://192.168.174.132:9090/#/login
- admin + 123456登陆
nginx+zuul高可用网关部署 (192.168.174.133)
- 在pro2安装nginx,配置以下信息
upstream zuulServer{
server 192.168.174.132:10002 weight=1;
server 192.168.174.133:10002 weight=1;
}
server {
listen 85;
listen [::]:85;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location / {
proxy_pass http://zuulServer/;
}
}
-
systemctl restart nginx
-
修改前端网关,重新部署
-
把端口添加到防火墙
- firewall-cmd --zone=public --add-port=85/tcp --permanent
- firewall-cmd --reload
基于k8s构建jenkins持续集成
192.168.174.128(gitlab)
192.168.174.129(k8s-master)
192.168.174.131(harbor)
192.168.174.132(k8s-node1)
192.168.174.133(k8s-node2)
Jenkins的Master-Slave测试
- Configure Global Security
- manage nodes新建节点
- 在132的root目录新建jenkins目录
- 进入salve1,点击下载agent.jar
- 下载好放入从节点根目录
- 执行命令 java -jar agent.jar -jnlpUrl http://192.168.174.129:8888/computer/slave1/jenkins-agent.jnlp -secret 605ecabbbb1caadf7acfd01e9d06bf628acb5b3d9d292dfd3dc4778243490b9a -workDir “/root/jenkins”
- 需要关闭129的防火墙或者把8888端口放开
- 需要关闭129的防火墙或者把8888端口放开
- 新建一个自由风格项目
- 构建
- 如果构建有问题,可以参考上面文档安装git,创建slave-ssh账户进行连接
- 如果构建有问题,可以参考上面文档安装git,创建slave-ssh账户进行连接
- 再尝试一个流水线项目
- 以下内容使用了新的slave-ssh
node('slave1') {
stage('check out') {
checkout([$class: 'GitSCM', branches: [[name: '*/master']],doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],userRemoteConfigs: [[credentialsId: ' 7ac724bf-f529-4ef8-879a-999748e10efd', url:'git@192.168.174.128:root/tensquare_parent.git']]])
}
}
k8s实现master-slave分布式构建方案
- 传统Jenkins的Master-Slave方案的缺陷
- Master节点发生单点故障时,整个流程都不可用了
- 每个 Slave节点的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲
- 资源分配不均衡,有的 Slave节点要运行的job出现排队等待,而有的Slave节点处于空闲状态资源浪费,每台 Slave节点可能是实体机或者VM,当Slave节点处于空闲状态时,也不会完全释放
掉资源
- 以上种种问题,我们可以引入Kubernates来解决!
- Kubernates简介
- Kubernetes(简称,K8S)是Google开源的容器集群管理系统,在Docker技术的基础上,为容器化的
应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的
便捷性。 其主要功能如下:- 使用Docker对应用程序包装(package)、实例化(instantiate)、运行(run)。以集群的方式运行、管理跨机器的容器。以集群的方式运行、管理跨机器的容器。解决Docker跨机器容器之间的通讯问题。解决Docker跨机器容器之间的通讯问题。
- Kubernetes的自我修复机制使得容器集群总是运行在用户期望的状态。
- Kubernetes(简称,K8S)是Google开源的容器集群管理系统,在Docker技术的基础上,为容器化的
- Kubernates+Docker+Jenkins持续集成架构图
- 大致工作流程
- 手动/自动构建
- Jenkins 调度 K8S API
- 动态生成 Jenkins Slave pod
- Slave pod
- 拉取 Git 代码/编译/打包镜像
- 推送到镜像仓库 Harbor
- Slave 工作完成,Pod 自动销毁
- 部署到测试或生产 Kubernetes平台。(完全自动化,无需人工干预)
- 大致工作流程
-
Kubernates+Docker+Jenkins持续集成方案好处
- 服务高可用:当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。
- 动态伸缩,合理使用资源:每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。
- 扩展性好:当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个Kubernetes Node 到集群中,从而实现扩展
-
Kubernetes的架构
- API Server:用于暴露Kubernetes API,任何资源的请求的调用操作都是通过kube-apiserver提供的接口进行的。
- Etcd:是Kubernetes提供默认的存储系统,保存所有集群数据,使用时需要为etcd数据提供备份计划。
- Controller-Manager:作为集群内部的管理控制中心,负责集群内的Node、Pod副本、服务端点(Endpoint)、命名空间(Namespace)、服务账号(ServiceAccount)、资源定额(ResourceQuota)的管理,当某个Node意外宕机时,Controller Manager会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。
- Scheduler:监视新创建没有分配到Node的Pod,为Pod选择一个Node。
- Kubelet:负责维护容器的生命周期,同时负责Volume和网络的管理
- Kube proxy:是Kubernetes的核心组件,部署在每个Node节点上,它是实现Kubernetes Service的通信与负载均衡机制的重要组件。
k8s-master(192.168.174.129) 环境
- 安装kube-apiserver、kube-controller-manager、kubescheduler、docker、etcd、calico,NFS
- 修改三台机器的hostname和hosts文件
hostnamectl set-hostname k8s-master
hostnamectl set-hostname k8s-node1
hostnamectl set-hostname k8s-node2
cat >>/etc/hosts<<EOF
192.168.174.129 k8s-master
192.168.174.132 k8s-node1
192.168.174.133 k8s-node2
EOF
- 三台机器执行不同命令
-
三台机器执行相同命令
-
systemctl status firewalld (确保三台机器防火墙关闭)
-
vi /etc/sysconfig/selinux
- 改为SELINUX=disabled
-
执行 setenforce 0,否则重启才能生效
-
设置允许路由转发,不对bridge的数据进行处理 (三台都做)
- 创建文件 vi /etc/sysctl.d/k8s.conf
- 内容如下:
- net.bridge.bridge-nf-call-ip6tables = 1
- net.bridge.bridge-nf-call-iptables = 1
- net.ipv4.ip_forward = 1
- vm.swappiness = 0
- 执行文件 sysctl -p /etc/sysctl.d/k8s.conf
- kube-proxy开启ipvs的前置条件 (三台都执行)
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -enf_conntrack_ipv4
- 所有节点关闭swap (三台都做,如果无法执行临时关闭命令,调大内存)
swapoff -a 临时关闭
vi /etc/fstab 永久关闭
注释掉以下字段
/dev/mapper/cl-swap swap swap defaults 0 0
- 安装kubelet、kubeadm、kubectl (三台都装)
- yum clean all
- 设置yum安装源
- yum clean all
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
- yum install -y kubelet kubeadm kubectl (这个安装的是最新版,可能会有意想不到的错误,建议使用下行)
- yum install -y kubelet-1.17.0-0 kubeadm-1.17.0-0 kubectl-1.17.0-0
- kubelet设置开机启动(注意:先不启动,现在启动的话会报错)
- systemctl enable kubelet
- 查看版本 kubelet --version
master节点完成
- 运行初始化命令
kubeadm init --kubernetes-version=1.17.0 \
--apiserver-advertise-address=192.168.174.129 \
--image-repository registry.aliyuncs.com/google_containers \
--service-cidr=10.1.0.0/16 \
--pod-network-cidr=10.244.0.0/16
-
报错 [ERROR NumCPU]: the number of available CPUs 1 is less than the required 2
- 虚拟机CPU设置为2
-
报错detected “cgroupfs” as the Docker cgroup driver. The recommended driver is “systemd”. Please follow the guide at https://kubernetes.io/docs/setup/cri/
- vi /etc/docker/daemon.json
- 加入 “exec-opts”:[“native.cgroupdriver=systemd”]
- 重启docker
- vi /etc/docker/daemon.json
-
如果没运行成功,报错 swap,可以尝试以下命令
- swapoff -a && sed -i ‘/ swap / s/^(.*)$/#\1/g’ /etc/fstab
- swapoff -a && sed -i ‘/ swap / s/^(.*)$/#\1/g’ /etc/fstab
-
节点安装命令保存下来
kubeadm join 192.168.174.129:6443 --token rqydp7.yd7zw5xylk67y9jv \
--discovery-token-ca-cert-hash sha256:df255c8d19eb9eea59201de81d65752d830608079849c04c8c2c2bf7efc8b2e0
- 启动k8s
- systemctl restart kubelet
- systemctl status kubelet
- 逐行执行命令
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
- 安装Calico
mkdir k8s
cd k8s
wget https://docs.projectcalico.org/v3.10/manifests/calico.yaml --no-check-certificate
sed -i 's/192.168.0.0/10.244.0.0/g' calico.yaml
kubectl apply -f calico.yaml
- 等待几分钟,查看所有Pod的状态,确保所有Pod都是Running状态
- kubectl get pod --all-namespaces -o wide
- kubectl get pod --all-namespaces -o wide
- 开启
- systemctl start kubelet
- systemctl status kubelet
k8s-node1(192.168.174.132) ,k8s-node2(192.168.174.133) 加入主节点
- 在子节点上执行start命令开启k8s
- 执行上面的节点命令,把node1加入主节点
- kubectl get nodes
- node1为Ready状态
- node1为Ready状态
k8s常用命令
kubectl get nodes 查看所有主从节点的状态
kubectl get ns 获取所有namespace资源
kubectl get pods -n {$nameSpace} 获取指定namespace的pod
kubectl describe pod的名称 -n {$nameSpace} 查看某个pod的执行过程
kubectl logs --tail=1000 pod的名称 | less 查看日志
kubectl create -f xxx.yml 通过配置文件创建一个集群资源对象
kubectl delete -f xxx.yml 通过配置文件删除一个集群资源对象
kubectl delete pod名称 -n {$nameSpace} 通过pod删除集群资源
kubectl get service -n {$nameSpace} 查看pod的service情况
安装和配置NFS
-
yum install -y nfs-utils (三台都要做)
-
mkdir -p /opt/nfs/jenkins
-
vi /etc/exports 编写NFS的共享配置
- 内容如下:
- /opt/nfs/jenkins *(rw,no_root_squash) *代表对所有IP都开放此目录,rw是读写
- 内容如下:
-
systemctl enable nfs 设置开机启动
-
systemctl start nfs 启动
-
在子节点查看
- showmount -e 192.168.174.129
- showmount -e 192.168.174.129
-
主节点上传配置
-
cd nfs-client
- kubectl create -f .
- kubectl create -f .
-
创建kube-ops的namespace,因为我们把Jenkins-Master的pod放到kube-ops下
-
cd jenkins-master
- kubectl create namespace kube-ops
- 构建Jenkins-Master的pod资源 kubectl create -f .
-
查看pod是否创建成功 kubectl get pods -n kube-ops -o wide
-
查看信息,并访问,查看Pod运行在那个Node上 kubectl describe pods -n kube-ops
-
查看分配的端口 kubectl get service -n kube-ops
- 分配到了31162 (随机端口)
- 访问 http://192.168.174.132:31162
-
安装同上
-
输入秘钥,不安装插件,创建lisi + 123456
-
替换源
-
安装插件
- Chinese
- Git
- Pipeline
- Extended Choice Parameter
- Kubernetes
-
系统管理->系统配置->云->新建云->Kubernetes
- kubernetes地址采用了kube的服务器发现:https://kubernetes.default.svc.cluster.local
- namespace填kube-ops,然后点击Test Connection,如果出现 Connection test successful 的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信
- Jenkins URL 地址:http://jenkins.kube-ops.svc.cluster.local:8080
jenkins-slave自定义镜像
-
准备好的文件上传到服务器
-
构建镜像
- docker build -t jenkins-slave-maven:latest .
- docker build -t jenkins-slave-maven:latest .
-
镜像拷贝到harbor仓库
- docker tag jenkins/jnlp-slave:latest 192.168.174.131:85/library/jenkins/jnlp-slave:latest
- docker login -u admin -p Harbor12345 192.168.131.85
- docker push 192.168.174.131:85/library/jenkins/jnlp-slave:latest
132的jenkins创建流水线项目(测试)
- 创建一个用户名的凭证
def git_address = "http://192.168.174.128:82/root/tensquare_parent.git"
def git_auth = "0e53c6eb-dffc-423c-b430-8d4e48953aa6"
//创建一个Pod的模板,label为jenkins-slave
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "192.168.174.131:85/library/jenkins/jnlp-slave:latest"
)
]
)
{
//引用jenkins-slave的pod模块来构建Jenkins-Slave的pod
node("jenkins-slave"){
// 第一步
stage('拉取代码'){
checkout([$class: 'GitSCM', branches: [[name: 'master']],userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
}
}
- 构建
- 可以在节点列表动态创建了节点
- 可以在节点列表动态创建了节点
- 构建成功,节点被回收
Jenkins+k8s+docker完成微服务持续集成
- 编辑129机器的文件共享
- vi /etc/exports
- /opt/nfs/maven *(wr,no_root_squash)
- systemctl restart nfs
- vi /etc/exports
- 131的jenkins添加凭证
- 新建流水线项目
def git_address = "http://192.168.174.128:82/root/tensquare_parent.git"
def git_auth = "0e53c6eb-dffc-423c-b430-8d4e48953aa6"
//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "192.168.174.131:85"
//Harbor的项目名称
def harbor_project_name = "tensquare"
//Harbor的凭证
def harbor_auth = "11c4a594-30f7-4f4c-af60-f7e715d1aea1"
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "192.168.174.131:85/library/jenkins/jnlp-slave:latest"),
containerTemplate(
name: 'docker',
image: "docker:stable",
ttyEnabled: true,
command: 'cat'),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath:'/var/run/docker.sock'),
nfsVolume(mountPath: '/usr/local/apache-maven/repo', serverAddress:'192.168.174.129' , serverPath: '/opt/nfs/maven'),
],)
{
node("jenkins-slave"){
// 第一步
stage('拉取代码'){
checkout([$class: 'GitSCM', branches: [[name: '${branch}']],
userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
// 第二步
stage('代码编译'){
//编译并安装公共工程
sh "mvn -f tensquare_common clean install"
}
// 第三步
stage('构建镜像'){
//把选择的项目信息转为数组
def selectedProjects = "${project_name}".split(',')
for(int i=0;i<selectedProjects.size();i++){
//取出每个项目的名称和端口
def currentProject = selectedProjects[i];
//项目名称
def currentProjectName = currentProject.split('@')[0]
//项目启动端口
def currentProjectPort = currentProject.split('@')[1]
//定义镜像名称注意:在构建过程会发现无法创建仓库目录,是因为NFS共享目录权限不足,需更改权限还有Docker命令执行权限问题需要手动上传父工程依赖到NFS的Maven共享仓库目录中微服务部署到K8S
def imageName = "${currentProjectName}:${tag}"
//编译,构建本地镜像
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
container('docker') {
//给镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"
//登录Harbor,并上传镜像
withCredentials([usernamePassword(credentialsId:"${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')])
}
{
//登录
sh "docker login -u ${username} -p ${password} ${harbor_url}"
//上传镜像
sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}"
}
//删除本地镜像
sh "docker rmi -f ${imageName}"
sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
}
}
}
}
-
build 测试
-
安装持续集成插件Kubernetes Continuous Deploy
- 目前官方插件下载地址没有这个插件了,所以从网上找了个添加进去
-
配置全局管理凭证k8s-auth
- 进入主节点 cd /root/.kube/ ,cat config
- 进入主节点 cd /root/.kube/ ,cat config
-
复制内容,添加到Enter directly
- 点击确定后得到一个全局ID
- 点击确定后得到一个全局ID
-
在每个微服务编写deploy.yml
- 以eureka为例
---
apiVersion: v1
kind: Service
metadata:
name: eureka
labels:
app: eureka
spec:
type: NodePort
ports:
- port: 10086
name: eureka
targetPort: 10086
selector:
app: eureka
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: eureka
spec:
serviceName: "eureka"
replicas: 2
selector:
matchLabels:
app: eureka
template:
metadata:
labels:
app: eureka
spec:
imagePullSecrets:
- name: $SECRET_NAME
containers:
- name: eureka
image: $IMAGE_NAME
ports:
- containerPort: 10086
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: EUREKA_SERVER
value: "http://eureka-0.eureka:10086/eureka/,http://eureka-1.eureka:10086/eureka/"
- name: EUREKA_INSTANCE_HOSTNAME
value: ${MY_POD_NAME}.eureka
podManagementPolicy: "Parallel"
- 修改每个服务的yml文件
- 以eureka为例
server:
port: ${PORT:10086}
spring:
application:
name: eureka
eureka:
server:
# 续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms)
eviction-interval-timer-in-ms: 5000
enable-self-preservation: false
use-read-only-response-cache: false
client:
# eureka client间隔多久去拉取服务注册信息 默认30s
registry-fetch-interval-seconds: 5
serviceUrl:
defaultZone: ${EUREKA_SERVER:http://127.0.0.1:${server.port}/eureka/}
instance:
# 心跳间隔时间,即发送一次心跳之后,多久在发起下一次(缺省为30s)
lease-renewal-interval-in-seconds: 5
# 在收到一次心跳之后,等待下一次心跳的空档时间,大于心跳间隔即可,即服务续约到期时间(缺省为90s)
lease-expiration-duration-in-seconds: 10
instance-id: ${EUREKA_INSTANCE_HOSTNAME:${spring.application.name}}:${server.port}@${random.long(1000000,9999999)}
hostname: ${EUREKA_INSTANCE_HOSTNAME:${spring.application.name}}
-
修改后的代码提交到gitlab
-
在129服务器先登录 docker login -u admin -p Harbor12345 192.168.174.131:85
-
生成docker凭证,用于k8s到docker私服拉取镜像
kubectl create secret docker-registry registry-auth-secret --docker-server=192.168.174.131:85 --docker-username=admin --docker-password=Harbor12345 --docker-email=843635953@qq.com
-
查看秘钥 kubectl get secret
-
修改流水线脚本
def git_address = "http://192.168.174.128:82/root/tensquare_parent.git"
def git_auth = "0e53c6eb-dffc-423c-b430-8d4e48953aa6"
//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "192.168.174.131:85"
//Harbor的项目名称
def harbor_project_name = "tensquare"
//Harbor的凭证
def harbor_auth = "11c4a594-30f7-4f4c-af60-f7e715d1aea1"
//k8s凭证
def k8s_auth = "d60faedf-0e56-425f-8c22-6253697381fa"
//定义k8s连接harbor的凭证
def secret_name = "registry-auth-secret"
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
containerTemplate(
name: 'jnlp',
image: "192.168.174.131:85/library/jenkins/jnlp-slave:latest"),
containerTemplate(
name: 'docker',
image: "docker:stable",
ttyEnabled: true,
command: 'cat'),
],
volumes: [
hostPathVolume(mountPath: '/var/run/docker.sock', hostPath:'/var/run/docker.sock'),
nfsVolume(mountPath: '/usr/local/apache-maven/repo', serverAddress:'192.168.174.129' , serverPath: '/opt/nfs/maven'),
],)
{
node("jenkins-slave"){
// 第一步
stage('拉取代码'){
checkout([$class: 'GitSCM', branches: [[name: '${branch}']],
userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
}
// 第二步
stage('代码编译'){
//编译并安装公共工程
sh "mvn -f tensquare_common clean install"
}
// 第三步
stage('构建镜像'){
//把选择的项目信息转为数组
def selectedProjects = "${project_name}".split(',')
for(int i=0;i<selectedProjects.size();i++){
//取出每个项目的名称和端口
def currentProject = selectedProjects[i];
//项目名称
def currentProjectName = currentProject.split('@')[0]
//项目启动端口
def currentProjectPort = currentProject.split('@')[1]
//定义镜像名称注意:在构建过程会发现无法创建仓库目录,是因为NFS共享目录权限不足,需更改权限还有Docker命令执行权限问题需要手动上传父工程依赖到NFS的Maven共享仓库目录中微服务部署到K8S
def imageName = "${currentProjectName}:${tag}"
//编译,构建本地镜像
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
container('docker') {
//给镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"
//登录Harbor,并上传镜像
withCredentials([usernamePassword(credentialsId:"${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')])
}
{
//登录
sh "docker login -u ${username} -p ${password} ${harbor_url}"
//上传镜像
sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}"
}
//删除本地镜像
sh "docker rmi -f ${imageName}"
sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
}
def deploy_image_name = "${harbor_url}/${harbor_project_name}/${imageName}"
//部署到K8S
sh """
sed -i 's#\$IMAGE_NAME#${deploy_image_name}#'${currentProjectName}/deploy.yml
sed -i 's#\$SECRET_NAME#${secret_name}#'${currentProjectName}/deploy.yml
"""
kubernetesDeploy configs: "${currentProjectName}/deploy.yml",kubeconfigId: "${k8s_auth}"
}
}
}
- build测试 构建注册中心
- 通过 kubectl get pods 查看副本创建情况
- 通过 kubectl get service 查看随机端口
- 访问eureka http://192.168.174.132:32476/ 或者 http://192.168.174.133:32476/
- 测试zuul
---
apiVersion: v1
kind: Service
metadata:
name: zuul
labels:
app: zuul
spec:
type: NodePort
ports:
- port: 10020
name: zuul
targetPort: 10020
selector:
app: zuul
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zuul
spec:
serviceName: "zuul"
replicas: 2
selector:
matchLabels:
app: zuul
template:
metadata:
labels:
app: zuul
spec:
imagePullSecrets:
- name: $SECRET_NAME
containers:
- name: zuul
image: $IMAGE_NAME
ports:
- containerPort: 10020
podManagementPolicy: "Parallel"
server:
port: 10020 # 端口
# 基本服务信息
spring:
application:
name: tensquare-zuul # 服务ID
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://eureka-0.eureka:10086/eureka/,http://eureka-1.eureka:10086/eureka/ # Eureka访问地址
instance:
prefer-ip-address: true
# 修改ribbon的超时时间
ribbon:
ConnectTimeout: 1500 # 连接超时时间,默认500ms
ReadTimeout: 3000 # 请求超时时间,默认1000ms
# 修改hystrix的熔断超时时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 2000 # 熔断超时时长,默认1000ms
# 网关路由配置
zuul:
routes:
admin:
path: /admin/**
serviceId: tensquare-admin-service
gathering:
path: /gathering/**
serviceId: tensquare-gathering
# jwt参数
jwt:
config:
key: tokenstr
ttl: 1800000