springboot+vue持续集成
Jenkins持续集成
项目部署参考资料:见参考资料详情
项目实战成长:见百度网盘jenkins
视频资料地址参考:https://www.bilibili.com/video/BV1kJ411p7mV?spm_id_from=333.999.0.0
整体流程:
- 先下载tomcat,到http://tomcat.apache.org/index.html
- 解压tomcat,并重命名
Linux环境安装
jdk安装
1. jdk下载地址 https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
- 解压设置环境变量
mkidr -p /usr/local/java # 创建文件夹
tar -zxvf jdk-8u171-linux-x64.tar.gz # 解压jdk
-
配置环境变量
vi /etc/profile
# 在unset i 在后面插入 for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do if [ -r "$i" ]; then if [ "${-#*i}" != "$-" ]; then . "$i" else . "$i" >/dev/null fi fi done export JAVA_HOME=/usr/local/java/jdk1.8.0_171 export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar export PATH=$PATH:$JAVA_HOME/bin unset i unset -f pathmunge
-
验证是否安装成功
source /etc/profile java -version
tomcat和jenkins安装
- 先下载tomcat,到http://tomcat.apache.org/index.html
-
解压tomcat,并重命名
mkidr -p /usr/local/tomcat_jenkins # 创建文件夹 tar -zxvf apache-tomcat-8.5.16.tar.gz # 解压tomcat mv mv apache-tomcat-8.5.16 tomcat # 重命名
-
将jenkins的war包放到webapps里面去
-
启动tomcat,访问地址:http://192.168.1.217:8080/jenkins/http://192.168.1.217:8080/jenkins/
/usr/local/tomcat_jenkins/apache-tomcat-8.5.47/bin/startup.sh # 启动tomcat
docker 安装
1. 查看当前系统内核是否高于3.0,验证你的centos是否支持Docker
uname -r
2. 使用 root 权限登录 Centos。确保 yum 包更新到最新
sudo yum update # 更新yum源
3. 卸载旧版本(如果安装过旧版本的话)
sudo yum remove docker docker-common docker-selinux docker-engine
4. 安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
5.设置yum源,任意选其一即可,国外下载稍慢
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 官方yum源,国外
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 阿里云
sudo yum-config-manager--add-repohttps://mirrors.tuna.tsinghua.edu.cn \
/docker-ce/linux/centos/docker-ce.repo # 清华大学源
6.可以查看所有仓库中所有docker版本,并选择特定版本安装
yum list docker-ce --showduplicates | sort -r
7.安装docker
sudo yum install <FQPN> # 例如:sudo yum install docker-ce-18.06.1.ce
8.启动并加入开机启动
sudo systemctl start docker
sudo systemctl enable docker
9.验证是否安装成功
docker version
docker-compose安装
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
maven安装
在Jenkins集成服务器上,我们需要安装Maven来编译和打包项目
1.创建maven文件夹
mkdir /usr/local/maven
2.解压maven
tar -zxvf /usr/local/maven/apache-maven-3.6.2-bin.tar.gz
3.配置环境变量
vi /etc/profile
# 在unset i 在后面插入
export MAVEN_HOME=/usr/local/maven/apache-maven-3.6.2
export PATH=$PATH:$JAVA_HOME/bin/:$MAVEN_HOME/bin
4.验证是否安装成功
mvn -v
nodejs 安装
1.创建文件夹,将安装包放入目录下
mkdir /usr/local/nodejs
2. 依次执行步骤,解压
xz -d /usr/local/nodejs/node-v14.17.4-linux-x64.tar.xz
tar -xf /usr/local/nodejs/node-v14.17.4-linux-x64.tar.xz
3.软连接类似于windows的快捷方式,必须全路径,否则可能关联错误
ln -s /usr/local/nodejs/node-v14.17.4-linux-x64/bin/node /usr/bin/node
ln -s /usr/local/nodejs/node-v14.17.4-linux-x64/bin/npm /usr/bin/npm
4.验证版本
node -v
5.使用淘宝的npm镜像cnpm,因为国外的服务器会掉线
npm install -g cnpm --registry=https://registry.npm.taobao.org
ln -s /usr/local/nodejs/node-v14.17.4-linux-x64/bin/cnpm /usr/bin/cnpm
git安装
#安装
yum install -y git
#查看版本
git version
portainer
安装
# 搜索相关portainer镜像
docker pull portainer
# 下载选定的最新版本
docker pull portainer/portainer
# 运行镜像
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --restart=always --name prtainer portainer/portainer
配置Docker主机,允许远程连接
-
修改Docker主机daemon.json
[root@localhost ~]# vi /etc/docker/daemon.json { "hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"] }
-
重启docker引擎,使配置生效。
[root@localhost ~]# systemctl daemon-reload [root@localhost ~]# systemctl restart docker.service
-
登录到portainer,添加endpoints节点
配置成功,就可以看到新加的docker节点了
Jenkins插件安装和环境配置
常用插件
redentials Binding
Credentials Binding凭证管理,凭据主要是用来存储密文保护的数据库密码,gitlab密码信息,docker私有仓库等密码,以便Jenkins方便与这些第三方的用户进行交互
- 在插件管理中安装好redentials Binding
- 所有的凭据都在这里管理,新增
3.添加凭据
-
添加方式:
这里主要介绍一下公钥私钥的方式做凭据,以gitlab为例:
# 1.使用root用户生成公钥和私钥
ssh-keygen -t rsa
# 2.生成的公钥私钥保存在/root/.ssh/目录下
id_rsa:私钥文件
id_rsa.pub:公钥文件
# 3.将生成的公钥放在gitlab中
- .使用root用户生成公钥和私钥
ssh-keygen -t rsa
-
生成的公钥私钥保存在/root/.ssh/目录下
id_rsa:私钥文件
id_rsa.pub:公钥文件 -
将生成的公钥放在gitlab中
以root账户登录->点击头像->Settings->SSH Keys
复制刚才id_rsa.pub文件的内容到这里,点击"Add Key"
4.在Jenkins中添加凭证,配置私钥
在Jenkins添加一个新的凭证,类型为"SSH Username with private key",把刚才生成私有文件内容复
制过来
GIT
GIT:为了让Jenkins支持从Gitlab拉取源码,需要安装Git插件以及在CentOS7上安装Git工具,安装方式同上
Git Parameter
Git Parameter:从gitlab或者github中动态检索项目的分支信息,在jenkins job参数化过程中提供选择分支项,方便用户在执行构建时执行选择的分支
pipeline
pipeline:用户构建复杂的流水线项目
Publish Over SSH
Publish Over SSH:该插件主要是通过SSH连接其他Linux机器,远程传输文件及执行Shell命令
- 安装ssh 插件
- 配置shh
Passphrase:密码(key的密码,如果你设置了)
Path to key:key文件(私钥)的路径
Key:将私钥复制到这个框中
Disable exec:禁止运行命令
私有配置:
SSH Server Name:标识的名字(随便你取什么)
Hostname:需要连接ssh的主机名或ip地址(建议ip)
Username:用户名
Remote Directory:远程目录
Use password authentication, or use a different key:可以替换公共配置(选中展开的就是公共配置的东西,这样做扩展性很好)
私有配置的高级:
Port:端口(默认22)
Timeout (ms):超时时间(毫秒)默认即可
Disable exec:禁止运行命令
Test Configuration:测试连接
Jenkins环境配置
全局工具配置
- 配置jdk
-
配置maven
-
配置nodejs
下载node插件:
-
配置nodejs
常见问题汇总
脚本执行错误---/bin/sh^M:损坏的解释器 没有那个文件或目录
在执行脚本时,弹出错误提示
原因:这是由于脚本文件在保存时使用了DOS文件格式造成的,可以用vim打开文件,然后执行下列操作
端口访问不通
一般情况下端口访问不通,很多情况下是防火墙没有开放指定端口
1、开启防火墙
systemctl start firewalld
2、开放指定端口
firewall-cmd --zone=public --add-port=1935/tcp --permanent
命令含义:
--zone #作用域
--add-port=1935/tcp #添加端口,格式为:端口/通讯协议
--permanent #永久生效,没有此参数重启后失效
3、重启防火墙
firewall-cmd --reload
4、查看所有开放的端口
firewall-cmd --permanent --zone=public --list-ports
插件安装失败
1.如图下图或者在下载插件过程中,安装一直失败
解决方式:更改升级站点
修改升级站点:更改升级站点下的url为https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json,点击提交,再点击立即获取(重要)
git构建变量不生效,导致拉拉取代码失败
现象:
原因:原因是在checkout代码的时候根本没有解析变量
解决方式:取消"清量级检出"勾选
pipeline语法规范错误
单引号踩坑:ssh execommand :必须使用双引号,否则报错
构建报错
项目实战
springboot后台项目
- 新建一个item,选择pipeline方式
- 参数化构建的项目,部署端口,拉去分支
- 选择pipeline脚本构建
- 编写pipeline脚本
def git_auth = "2cf3403b-af33-48f7-b20c-b86e29757662"
def git_url = "git@gitlab.octvision.com:kexing/weifuwu.git"
def tag = "latest"
/*
def harbor_url = "192.168.64.53:85"
def harbor_project_name = "buyu"
def username = "eric"
def password = "Eric123456"
*/
def imageName = "${project_name}:${tag}"
node {
stage('pull code') {
checkout([$class: 'GitSCM', branches: [[name: "*/${branch_or_tag}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
}
// 检查代码
/*
stage('check code') {
def scannerHome = tool 'sonarqube-scanner'
withSonarQubeEnv('SonarQube') {
sh """
${scannerHome}/bin/sonar-scanner \
-Dsonar.host.url=http://192.168.64.70:9000 \
-Dsonar.login=admin \
-Dsonar.password=admin \
-Dsonar.sources=/root/.jenkins/workspace/weifuwu_server/${project_name}/src \
-Dsonar.projectBaseDir=/root/.jenkins/workspace/weifuwu_server/${project_name}/src \
-Dsonar.projectKey=myfirstproject \
-Dsonar.projectName=myfirstproject \
-Dsonar.projectVersion=1.0.0-alpha \
-Dsonar.language=java \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.java.binaries=/root/.jenkins/workspace/weifuwu_server/${project_name}/target \
-X
"""
}
}
*/
stage('编译构建') {
sh "mvn clean package"
}
// sshPublisher(publishers: [sshPublisherDesc(configName: 'web', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "chmod 777 /home/tomcat.sh&&/home/tomcat.sh ${project_name} ${tag} ${port}&&ps -ef | grep java", execTimeout: 300000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/usr/tomcatserver', remoteDirectorySDF: false, removePrefix: '${project_name}/target/', sourceFiles: '${project_name}/target/*.war')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)])
stage('部署') {
sshPublisher(publishers: [sshPublisherDesc(configName: '192.168.1.74', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "sudo chmod 777 /data/buyu/autodeploy/${project_name}.sh&&/data/buyu/autodeploy/${project_name}.sh ${project_name} ${tag} ${port}&& ps -ef | grep java", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: ' /data/buyu/origin/${project_name}/', remoteDirectorySDF: false, removePrefix: '${project_name}/target/', sourceFiles: '${project_name}/target/*.war')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)]) }
}
- 编写远程部署机器上的脚本,octv-promotion.sh
#! /bin/sh
#接收外部参数
project_name=$1
if [ -f "/data/buyu/produce/${project_name}/tomcat/webapps/${project_name}.war" ]
then
# /data/buyu/remote/${project_name}
# 移除容器所对应的文件
rm -f /data/buyu/produce/${project_name}/tomcat/webapps/${project_name}.war
# 移除自动发布目录的相关文件
rm -f /data/buyu/autodeploy/${project_name}.war
# 将项目的war包拷贝到相应的docker容器映射目录中去
cp -r /data/buyu/origin/${project_name}/${project_name}.war /data/buyu/produce/${project_name}/tomcat/webapps/
# 拷贝项目发布需要的东西到自动发布文件夹
cp -r /data/buyu/origin/${project_name}/${project_name}.war /data/buyu/autodeploy/
cd /data/buyu/autodeploy
docker-compose restart ${project_name}
echo "容器重启成功"
else
echo "初次运行容器${project_name}"
mkdir -p /data/buyu/produce/${project_name}/tomcat/webapps/
cp -r /data/buyu/origin/${project_name}/${project_name}.war /data/buyu/produce/${project_name}/tomcat/webapps/
cp -r /data/buyu/origin/${project_name}/${project_name}.war /data/buyu/autodeploy/
cd /data/buyu/autodeploy
docker-compose up -d ${project_name}
echo "容器发布成功 "
fi
echo "构建结束"
exit 0
-
编写dockerfile镜像,octv-promotion_dockerfile
FROM tomcat:8.5 #维护者 ARG WAR_FILE ARG PORT #将webapp下的全部删除 RUN rm -rf /usr/local/tomcat/webapps/* #将target下的xx.war拷贝到/usr/local/tomcat/webapps/下 COPY octv-promotion.war /usr/local/tomcat/webapps/ RUN chmod 777 /usr/local/tomcat/bin/catalina.sh #端口 # EXPOSE ${PORT} #设置启动命令 ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]
-
编写docker-compose文件
version: "2.2" services: buyu_front2: restart: always build: context: . dockerfile: Dockerfile ports: - 85:80 volumes: - "/usr/nginx/html:/usr/share/nginx/html" - "/usr/nginx/conf/nginx.conf:/etc/nginx/nginx.conf" - "/usr/nginx/conf.d:/etc/nginx/conf.d" - "/usr/nginx/logs:/var/log/nginx" container_name: nginxpwd privileged: true networks: hx_net: ipv4_address: 172.26.0.3 octv-promotion: restart: always build: context: . dockerfile: octv-promotion_dockerfile ports: - 8081:8080 volumes: - "/data/buyu/produce/octv-promotion/tomcat/webapps:/usr/local/tomcat/webapps" - "/data/buyu/produce/octv-promotion/tomcat/logs:/usr/local/tomcat/logs" container_name: octv-promotion privileged: true depends_on: - redis networks: hx_net: ipv4_address: 172.26.0.4 redis: restart: always image: redis ports: - 6380:6379 container_name: redis privileged: true networks: hx_net: ipv4_address: 172.26.0.5 networks: hx_net: driver: bridge ipam: config: - subnet: 172.26.0.0/16
部署服务器结构:
vue项目
-
1.2.3步骤同上,这里就不重复写了
-
编写pipeline脚本
def git_auth = "f13bcbc1-b90f-4e41-a1d9-76d73c22a2ac" def git_url = "http://gitlab.octvision.com/kexing/weifuwu.git" def tag = "latest" /* def harbor_url = "192.168.64.53:85" def harbor_project_name = "buyu_front" def username = "eric" def password = "Eric123456" */ def imageName = "${project_name}:${tag}" node { stage("pull code") { checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'f13bcbc1-b90f-4e41-a1d9-76d73c22a2ac', url: 'http://gitlab.octvision.com/kexing/buyu_front.git']]]) } stage("打包") { nodejs("node-V14.17.4"){ sh """ cnpm install cnpm run build:stage """ } } stage("部署网站") { sshPublisher(publishers: [sshPublisherDesc(configName: 'web', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "chmod 777 /home/nginx.sh&&/home/nginx.sh ${project_name} ${tag}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/usr/home', remoteDirectorySDF: false, removePrefix: 'dist/staging/', sourceFiles: 'dist/staging/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)]) } }
-
编写远程nginx.sh脚本
#! /bin/sh # 接受外部参数 project_name=$1 if [ -f "/usr/nginx/html/index.html" ] then rm -rf /usr/nginx/html/* rm -rf /home/autodeploy/front/* cp -r /usr/home/* /usr/nginx/html cp -r /usr/home/* /home/autodeploy/front/ cd /home/autodeploy docker-compose restart ${project_name} echo "容器重启成功" else echo "初次运行容器${project_name}" mkdir -p /usr/nginx/html cp -r /usr/home/* /usr/nginx/html cp -r /usr/home/* /home/autodeploy/front/ cd /home/autodeploy docker-compose up -d ${project_name} echo "容器发布成功 " fi echo "构建结束"
-
编写dockerfile镜像,
FROM nginx
COPY front/* /usr/share/nginx/html/
RUN chmod -R 777 /usr/share/nginx/html/
EXPOSE 80
ENTRYPOINT ["nginx"]
CMD ["-g","daemon off;"]
-
nginx配置文件
default.conf配置文件如下
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
nginx.conf
user root;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
user root;worker_processes auto; error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid; events { worker_connections 1024;} http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf;}
注意:配置文件时docker挂载到容器里面相应目录的,用默认的配置文件经常会出现nginx权限错误
项目总结
问题一:环境问题
1.此次自动化构建过程中涉及到众多服务器,每个服务器有很多配置,一个出现了问题,找问题比较麻烦,百度问题答案层出不穷,较为恶心。(特别指出jenkins配置较多)
2.在初始阶段,一直出现redis数据库连接不上,没有找出问题所在之处,只好在本地安装了redis
问题二:对于项目前后端服务架构不熟
1. 对后台所写代码springboot技术框架不了解,如更改一些配置,服务与子工程的关系,打包方式等等
2. 对前后台的服务部署架构不熟
问题三:脚本不熟
1. 此次涉及到的脚本有shell脚本,pipeline脚本,DockerFile脚本,由于对这些脚本基本零经验,都是从小白学起,碰到很多脚本语法报错,格式规范等等问题,都是采用最原始的编辑器notepad++编写,很难看出格式问题(如有的变量引用必须要用双引号)
- 有些脚本在服务器直接执行一点问题都没有,集成在jenkins上,就需要特殊的脚本规范(shell 脚本exit 0结尾)
问题四:docker
1. docker容器之间的网络通信问题,不能通过本机的ip地址+端口映射进行访问
①通过docker容器内部ip+端口访问
②在运行容器是加上--link参数,直接可以已别名进行访问
③创建自己的bridge网络,需要通信的容器之前都连接上新建的bridge网络
- Nginx通过dockerfile拷贝的静态文件,运行docker run -v时,容器运行起来时,访问nginx时,
没有显示页面,通过查看容器静态文件所在的目录,结果目录没有显示任何静态文件.
总结:目前springboot,vue项目自动化构建部署的基本流程均已打通,由于涉及东西较多,仍有很多东西需要增加,优化,后续将持续学习完善。