概要:本篇介绍使用宝塔Linux面板操作云服务器,搭建Jenkins和docker用于部署项目+持续集成。介绍如何通过docker部署spring boot项目和前端vue项目。
环境准备
准备云服务器
我使用的是阿里的云服务器,配置上个人的话使用两核 2G/40G 3M带宽足够了,如果想提高Jenkins持续集成的速度的话,建议上4G。
博主在购买服务器的时候使用的是centos7,如果选错系统也没关系,可以从阿里的管理平台上更换
- 更换系统
点击你的实例
在一览页面找到系统信息点击更换
服务器准备好之后就可以开始操作了,这里我选择使用宝塔Linux面板来操作我的服务器(选择自己习惯的即可)
- centos安装宝塔的脚本
yum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh ed8484bec
其他系统也可自行进入宝塔官网寻找脚本
下载完宝塔之后,使用它提供的账号密码登录,注意账号密码不要丢失了
记得在你的安全组里开放端口给自己电脑的ip(注意!你要是把电脑带到家/公司你的ip会变,记得再加一遍)
运行Jenkins
- 下载Jenkins
注意!推荐不要使用docker运行Jenkins,因为后面Jenkins要用docker命令来部署项目,我之前用docker版的Jenkins搞半天跑不起来项目,贼麻烦,干脆用运行它的war包的方式来运行Jenkins。版本选择2.361.4以后的!!!
链接:https://pan.baidu.com/s/106QmmARIKqDai_R98z2ByA
提取码:bja4
官网其他版本 War Jenkins Packages
版本选择2.361.4以后是因为它的插件不支持低版本的Jenkins,后面下插件很麻烦。
- 下载jdk11(或更高版本)
**为什么不是jdk8?**因为Jenkins能支持jdk8的版本它的插件不支持,不过后续也需要下载jdk8来运行部署的项目。
wget https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_linux-x64_bin.tar.gz
记得自己选择一个目录解压,方便找文件
tar -xzvf jdk-11.0.14_linux-x64_bin.tar.gz
配置环境变量
vi /etc/profile
export JAVA_HOME=/你的目录/jdk-11
export CLASSPATH=$JAVA_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile
进入war包对应的目录运行脚本把Jenkins跑起来
nohup java -jar jenkins.war --ajp13Port=-1 --httpPort=10000 --prefix=/jenkins >> ./jenkins.log 2>&1
检查一下运行状态(跑起来后关闭终端也没事)
netstat -tln | grep 10000
记得在安全组开放10000端口给自己
访问 http://你的服务器IP:10000/jenkins
无法访问?
大概率是centos的防火墙阻止了访问
- 查看10000端口是否开放了访问
firewall-cmd --query-port=10000/tcp
如果是no,那么就开放访问或者直接关闭防火墙
- 开放访问
firewall-cmd --add-port=10000/tcp --permanent
firewall-cmd --reload
- 或者直接永久关闭防火墙
systemctl disable firewalld
再次访问,看到以下页面即代表成功
找不到密码没关系,在jenkins.war同一个目录下有jenkins.log文件,查看该文件即可拿到密码
然后继续->安装推荐的插件->创建管理员账户就完成啦!
准备项目运行的环境
- 下载docker
由于我是用宝塔linux面板,它里面就有按钮,点击即可下载,这里我就跳过了
- 下载jdk8,maven,node.js(记得自己找个目录下)
jdk8(不用配环境变量)
链接:https://pan.baidu.com/s/13a3xw4qx476AyBEAO9_UvQ
提取码:gc5e
maven直接用命令下就行了
wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz
tar -xvf apache-maven-3.8.6-bin.tar.gz
配置maven的环境变量
vi /etc/profile
export MAVEN_HOME=/你的目录/apache-maven-3.8.6
export PATH=$PATH:$MAVEN_HOME/bin
source /etc/profile
node.js (16.20.2)
链接:https://pan.baidu.com/s/14vBK4hNjZGvfB5P-x53sIw
提取码:5c5e
推荐使用软连接的方式配置环境变量
ln -s /你的目录/nodejs/bin/node /usr/local/bin/
ln -s /你的目录/nodejs/bin/npm /usr/local/bin/
由于我前端使用cnpm命令,所以我还要下载cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
配置cnpm的软连接
ln -s /你的目录/nodejs/bin/cnpm /usr/local/bin/
检查一下命令是不是生效了
node -v
npm -v
cnpm -v
docker version
java --version
确保你的命令可以使用,因为在部署项目时需要执行shell脚本
为什么java --version 是jdk11
因为已经配了jdk11,jdk8只是用来跑后端项目的,在Jenkins里面配置一下jdk8的目录就行。
在Jenkins->系统管理-> 全局工具配置找到jdk
把刚刚下载的jdk8的路径填上
搭建CI/CD环境
搭建后端
我的项目使用到了mysql 8,我打算使用docker来运行mysql,方便管理
- 运行mysql容器
先创建bridge!!这一步很重要,因为不同的容器通信使用bridge会很方便
docker network create -d bridge mybridge
拉取mysql的镜像,并运行容器(密码是root,也可自行配置MYSQL_ROOT_PASSWORD=?)
docker pull mysql:latest
docker run -it --network mybridge -d -p 3306:3306 --privileged=true -v /home/mysql/conf:/etc/mysql/conf.d -v/home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql mysql
记得去阿里的服务器管理页面在安全组开放3306端口,如果你的centos系统没关闭防火墙,则要配置一下3306端口允许访问
firewall-cmd --query-port=3306/tcp
firewall-cmd --add-port=3306/tcp --permanent
firewall-cmd --reload
如果你还使用了其他技术,拉取对应的docker镜像,然后运行容器即可,记得加上**–network mybridge**
- 创建新的任务
- 然后直接到build step这一步
选择执行shell
mvn clean package -DskipTests
cp 项目打包.jar docker
cd docker
docker ps -a | grep 名字 | awk '{print $1}' | xargs -i docker stop {} | xargs -i docker rm {}
docker images | grep 名字 | awk '{print $3}' | xargs -i docker rmi {}
docker build -f Dockerfile -t "名字:prod" . --no-cache
docker run -it --network mybridge -d -p 9090:9090 --name 名字 名字:prod
这里的脚本意思是,先打包,然后拷贝jar包到项目根目录下的docker目录,进入docker目录,先删除(之前的)容器和镜像,再通过docker目录下的Dockerfile构建镜像,然后运行镜像。记得加**–network mybridge**(之前创建的bridge)
这里我的项目里面的Dockerfile长这样
# jre
FROM openjdk:8-jre-alpine
# author
MAINTAINER author
# 接受传参
ENV JAVA_OPIS ""
# 添加jar包到容器中(注意!请先执行shell拷贝jar包到docker目录下)
ADD 项目打包.jar /home/app.jar
EXPOSE 9090
CMD ["sh", "-c", "echo \"****** 运行命令: java -jar ${JAVA_OPIS} /home/app.jar --spring.profiles.active=prod\" & java -jar ${JAVA_OPIS} /home/app.jar --spring.profiles.active=prod"]
因为使用了bridge,所以你需要修改一下yml配置文件(将host改成docker容器名)
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://mysql的docker容器名:3306/test?useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
其他配置同理
Q&A
- 我的后端项目成功跑起来了,怎么接口无法访问?答:记得在安全组开放你配置的端口,还有配置centos防火墙
- 为什么我后端的项目访问不了mysql的docker容器?答:两个容器在运行时推荐使用自己创建的bridge的方式通信,通过加–network mybridge的方式
- 为什么我在运行jar包时–spring.profiles.active=prod失效?答:检查一下你的启动类有没有加上args
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);// args没加的话参数就传不进去
}
}
- 怎么查看日志?答:
docker ps -a # 查看所有正在运行的容器
docker logs -f --since "2024-01-02" 29ac5d06777a # 查看指定容器id指定日期的日志
搭建前端
我使用的是Jenkinsfile+Dockerfile的方式配置,在项目根目录先创建Docker文件夹
- 在Jenkins新建任务
Dockerfile
# 拉取nginx基础镜像
FROM nginx:1.21.1
# 维护者信息
MAINTAINER author
# 将dist文件中的内容复制到 `/usr/share/nginx/html/` 这个目录下面
COPY dist/ /usr/share/nginx/html/
# 用本地配置文件来替换nginx镜像里的默认配置
COPY nginx/nginx.conf /etc/nginx/nginx.conf
# 对外暴漏的端口号
# [注:EXPOSE指令只是声明容器运行时提供的服务端口,给读者看有哪些端口,在运行时只会开启程序自身的端口!!]
EXPOSE 5173
# 启动nginx容器
CMD ["nginx", "-g", "daemon off;"]
.dockerignore
node_modules
在项目根目录创建Jenkinsfile
pipeline {
agent any
environment {
APP_NAME = '项目名'
APP_IMAGE = '镜像名'
APP_PORT = 5173
}
stages {
stage('vue环境准备') {
steps {
sh """
# 拷贝dist到Docker目录下
cp -r dist Docker/
"""
}
}
stage('构建Docker镜像') {
steps {
sh """
# 删除旧容器
docker ps -a | grep ${APP_NAME} | awk '{print \$1}' | xargs -i docker stop {} | xargs -i docker rm {}
# 删除旧镜像
docker images | grep ${APP_NAME} | awk '{print \$3}' | xargs -i docker rmi {}
# 进入Docker目录
cd Docker
# 构建镜像
docker build -f Dockerfile -t ${APP_IMAGE} . --no-cache
"""
}
}
stage('运行容器') {
steps {
sh """
docker run --network mybridge -d -p ${APP_PORT}:5173 --restart=always --name ${APP_NAME} ${APP_IMAGE}
"""
}
}
}
}
nginx.conf的内容(经供参考)
user nginx;
worker_processes 1;
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;
server {
listen 5173; # 你的前端项目端口
server_name localhost; # 服务器地址或绑定域名,可以写localhost
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
# =========================================================
# ================== ↓↓↓↓↓↓ start ↓↓↓↓↓↓ ==================
# =========================================================
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# 此处的 @router 实际上是引用下面的转发,否则在 Vue 路由刷新时可能会抛出 404
try_files $uri $uri/ @router;
}
# 由于路由的资源不一定是真实的路径,无法找到具体文件
# 所以需要将请求重写到 index.html 中,然后交给真正的 Vue 路由处理请求资源
location @router {
rewrite ^.*$ /index.html last;
}
# 配置代理服务器,访问前缀有/api/的接口讲会去除前缀api交给代理服务器进行处理
location ^~/api/ {
proxy_pass http://后端镜像名:端口/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Connection "";
client_max_body_size 100m;
proxy_connect_timeout 90;
proxy_read_timeout 90;
proxy_send_timeout 90;
}
# =========================================================
# ================== ↑↑↑↑↑↑ end ↑↑↑↑↑↑ ==================
# =========================================================
#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;
}
}
}
Q&A
-
为什么Jenkinsfile里面是直接拷贝dist文件夹而不是使用npm build命令?答:因为我的两核2G服务器扛不住这个命令,构建会报错javascript heap out of memory,所以我只能在自己的电脑上build然后git推送上去
-
为什么项目跑起来了但是页面无法访问?答:在安全组开放端口,在centos配置防火墙。
-
502错误?答:可能是nginx.conf文件有问题
-
访问不到后端的接口?答:如果是通过docker的bridge的话,你需要在nginx.conf的location里面配置proxy_pass,主机名是后端docker容器名字,记得检查你的docker容器运行命令有没有加–network mybridge。
其他搭建方式(仅供参考,我还没实践以下方式)
- 后端-通过docker+shell直接运行jar包的方式
# 解决jenkins默认自动终止shell产生的进程问题
BUILD_ID=dontKillMe
# 打包
mvn clean package -DskipTests
cd target
# 先停掉旧服务
ps -ef | grep demo-0.0.1-SNAPSHOT.jar | grep -v grep | awk '{print "kill -15 "$2}' | sh
# 再运行新服务
nohup java -jar demo-0.0.1-SNAPSHOT.jar > app.log 2>&1
- 前端-docker+执行shell的方式(不通过Jenkinsfile的方式)
cnpm i
cnpm run build:prod
cp -r dist/ Docker/
cd Docker
docker ps -a | grep APP_NAME | awk '{print $1}' | xargs -i docker stop {} | xargs -i docker rm {}
docker images | grep APP_NAME | awk '{print \$3}' | xargs -i docker rmi {}
docker build -f Dockerfile -t APP_NAME . --no-cache
docker run -d -p 81:80 --restart=always --name APP_NAME APP_IMAGE