前言

本文介绍使用docker+nginx+jenkins将hexo博客部署至云服务器。

hexo博客提交云服务器流程:

  1. 本地电脑hexo三连,或者githubaction三连部署到github。
  2. jenkins监测到github项目主页更新,拉取更新后的文件到vps的jenkins工作目录,然后持续集成部署更新文件。Jenkins(概念篇):Jenkins 简介_ron jenkins 什么意思
  3. vps本地脚本把jenkins工作目录更新的文件传输到nginx工作目录。

开始之前确保以下几个条件:

  • 可以部署到githubpage的hexo博客;

  • vps云服务器;

  • vps安装好docker,docker-compose

  • 安装git

1
sudo apt-get install git

docker安装jenkins和nginx

创建目录

1
2
3
4
5
6
7
8
9
cd /root/data/docker_data/jenkins         
mkdir compose
mkdir jenkins_home
mkdir nginx
mkdir nginx/conf
mkdir html
mkdir html/dev
mkdir html/release
mkdir html/pro

创建docker-compose.ymlnginx.conf配置文件

1
2
3
4
5
cd /root/data/docker_data/jenkins/compose
touch docker-compose.yml

cd /root/data/docker_data/jenkins/nginx/conf
touch nginx.conf

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
31
32
33
34
35
36
version: '3'
services: # 集合
docker_jenkins:
user: root # 为了避免一些权限问题 在这我使用了root
restart: always # 重启方式
image: jenkins/jenkins:lts # 指定服务所使用的镜像 在这里我选择了 LTS (长期支持)
container_name: jenkins # 容器名称
ports: # 对外暴露的端口定义,8888可修改
- 8888:8080 # 访问Jenkins服务端口
- 50000:50000
volumes: # 卷挂载路径
- ../jenkins_home/:/var/jenkins_home # 这是我们一开始创建的目录挂载到容器内的jenkins_home目录
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker # 这是为了我们可以在容器内使用docker命令
- /usr/local/bin/docker-compose:/usr/local/bin/docker-compose
docker_nginx_dev: # nginx-dev环境
restart: always
image: nginx
container_name: nginx_dev
ports:
- 8871:8001 # 对外暴露的端口定义,8871可修改
volumes:
- ../nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ../html:/usr/share/nginx/html
- ../nginx/logs:/var/log/nginx

docker_nginx_sit: # nginx-sit环境
restart: always
image: nginx
container_name: nginx_sit
ports:
- 6092:8002 # 对外暴露的端口定义,6092可修改
volumes:
- ../nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ../html:/usr/share/nginx/html
- ../nginx/logs:/var/log/nginx

上面的代码跑了三个容器,jenkins,nginx_dev,nginx_sit

nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 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;

#dev环境
server {
#监听的端口
listen 8001;
server_name localhost;
#设置日志
# access_log logs/dev.access.log main;

#定位到index.html
location / {
#linux下HTML文件夹,就是你的前端项目文件夹
root /usr/share/nginx/html/dev/hexo;
# root /home/html/dev/dist;
#输入网址(server_name:port)后,默认的访问页面
index index.html;
try_files $uri $uri/ /index.html;
}
}

#sit环境
server {
#监听的端口
listen 8002;
server_name localhost;
#设置日志
# access_log logs/sit.access.log main;

#定位到index.html
location / {
#linux下HTML文件夹,就是你的前端项目文件夹
root /usr/share/nginx/html/sit/hexo;
# root /home/html/dev/dist;
#输入网址(server_name:port)后,默认的访问页面
index index.html;
try_files $uri $uri/ /index.html;
}
}


# include /etc/nginx/conf.d/*.conf;


}

运行docker容器

1
2
cd /root/data/docker_data/jenkins/compose
docker-compose up -d

查看容器运行状态

1
docker-compose ps -a

三个容器运行状态

验证nginx

/docker/html/dev/hexo目录下新建index.html,文件内容如下

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>welcome to Nginx</h1>
</body>
</html>

浏览器访问服务器ip:8871,例如192.168.8.5:8871

返回页面

jenkins配置

浏览器输入你的服务器ip:8888

解锁jenkins

图片展示的docker-compose.yml和上面给出的有出入。当前运行给出挂载jenkins_home前有两个点,代表当前目录的上一级目录。所以实际运行的jenkins_home目录在docker-compose.yml的上一级目录。总之到jenkins_home目录下去找密码。

后面的安装和配置建议看从零开始搭建JENKINS+GITHUB持续集成环境,这篇文章看Jenkins的使用验证构建。主要步骤看建议的文章,下面列出有出入的地方

  • 新建任务时,任务名填blog。

自定义任务名字的话,在后续创建脚本的步骤中把监控文件代码中的blog,改成你的任务名称。

1
2
# 监控的文件
JENKINS_FILE="/root/data/docker_data/jenkins/jenkins_home/workspace/blog/hexo.tar"

任务名

  • 启用github插件,github plugin虽然默认安装了,但是并没有启用。需要取消启用github-branch这个插件,再开启github plugin。这两个插件只能启用一个,所以需要取消一个。

github插件

  • 在配置构建步骤时,再添加一个执行shell的步骤

添加执行shell

1
2
rm -rf hexo.tar    
tar -zcvf hexo.tar ./*

脚本的意思是,jenkins从github拉取更新的代码后,先把jenkins容器/var/jenkins_home/workspace/blog/hexo.tar 文件先给删除,然后再打包jenkins容器/var/jenkins_home/workspace/blog目录下的所有文件到hexo.tar文件。把文件打包成hexo.tar方便后续传输到nginx。

注意:构建步骤这里添加的执行shell本质是在jenkins容器的/var/jenkins_home/workspace/blog目录下执行的操作。所以在这里写脚本传输jenkins容器文件到nginx容器是没可能的。下面会给出本地脚本来进行容器间文件的传输。

  • 补几张图

建议文章的图会和安装的有出入,但细心点是可以按他的步骤设置好的。

配置github凭据

验证凭据

构建测试

项目配置

启用脚本

脚本功能:每隔10秒监测jenkins构建的输出目录workspace下hexo.tar是否更新,有更新就会执行先删除目标目录hexo文件夹下的所有文件,然后解压hexo.tar到目标目录,无更新不执行。

1
2
cd /root/data/shell_script
touch monitor_jenkins_to_nginx.sh

复制代码到monitor_jenkins_to_nginx.sh文件,然后执行以下命令

1
./monitor_jenkins_to_nginx.sh

monitor_jenkins_to_nginx.sh脚本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/bin/bash

# 配置参数
readonly JENKINS_FILE="/root/data/docker_data/jenkins/jenkins_home/workspace/blog/hexo.tar"
readonly NGINX_DEST_DIR="/root/data/docker_data/jenkins/html/dev/hexo"
readonly INTERVAL=10
readonly LOG_FILE="/root/data/shell_script/monitor_jenkins_to_nginx/monitor_jenkins_to_nginx.log"
readonly STOP_FILE="/root/data/shell_script/monitor_jenkins_to_nginx/stop_monitor_script"
readonly MAX_LOG_SIZE=$((20 * 1024 * 1024)) # 20 MB
readonly PROTECTED_DIRS=("nav" "qrc" "fyh" "bbd" "finalshell")

# 日志函数
log_message() {
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp $1" >> "$LOG_FILE"
}

# 获取文件的最后修改时间
get_file_mod_time() {
[ -f "$JENKINS_FILE" ] && stat -c %Y "$JENKINS_FILE" || echo 0
}

# 检查并控制日志文件大小
check_log_size() {
if [ -f "$LOG_FILE" ] && [ "$(stat -c%s "$LOG_FILE")" -gt "$MAX_LOG_SIZE" ]; then
log_message "日志文件超过 $MAX_LOG_SIZE 字节,正在删除..."
: > "$LOG_FILE"
log_message "日志文件已被重置。"
fi
}

# 判断文件是否稳定
is_file_stable() {
local initial_size current_size
initial_size=$(stat -c%s "$JENKINS_FILE")
sleep 2
current_size=$(stat -c%s "$JENKINS_FILE")
[ "$initial_size" -eq "$current_size" ]
}

# 处理文件变化
handle_file_change() {
log_message "检测到 $JENKINS_FILE 文件变化,正在处理..."

# 创建目标目录
if ! mkdir -p "$NGINX_DEST_DIR"; then
log_message "创建目标目录 $NGINX_DEST_DIR 失败。"
return 1
fi

# 构建排除目录的 find 命令参数
local exclude_args=()
for dir in "${PROTECTED_DIRS[@]}"; do
exclude_args+=(-not -name "$dir")
done

# 清理目标目录
if find "$NGINX_DEST_DIR" -mindepth 1 -maxdepth 1 "${exclude_args[@]}" -exec rm -rf {} + ; then
log_message "已成功删除 $NGINX_DEST_DIR 目录中的无关文件。"

# 检查文件稳定性并解压
if is_file_stable; then
log_message "文件 $JENKINS_FILE 已经稳定,准备解压..."
if tar -tf "$JENKINS_FILE" > /dev/null 2>&1; then
if tar -xf "$JENKINS_FILE" -C "$NGINX_DEST_DIR"; then
log_message "文件已成功解压到 $NGINX_DEST_DIR"
else
log_message "解压文件失败,请检查路径是否正确。"
fi
else
log_message "文件 $JENKINS_FILE 可能已损坏,无法解压。"
fi
else
log_message "文件 $JENKINS_FILE 仍在变化,稍后再试。"
fi
else
log_message "删除 $NGINX_DEST_DIR 目录中的文件失败,请检查路径。"
fi
}

# 主监控循环
main_loop() {
local last_mod_time current_mod_time
last_mod_time=$(get_file_mod_time)

while true; do
[ -f "$STOP_FILE" ] && {
log_message "检测到停止标志文件,脚本退出。"
rm -f "$STOP_FILE"
exit 0
}

current_mod_time=$(get_file_mod_time)

[ "$current_mod_time" -gt "$last_mod_time" ] && {
handle_file_change
last_mod_time="$current_mod_time"
}

check_log_size
sleep "$INTERVAL"
done
}

# 创建日志目录
mkdir -p "$(dirname "$LOG_FILE")"

# 启动脚本
if [ "$1" != "start" ]; then
echo "正在启动脚本,并在后台运行..."
nohup "$0" start > "$LOG_FILE" 2>&1 &
echo "脚本已在后台启动,日志输出到 $LOG_FILE"
exit 0
fi

main_loop

日志记录

1
2
3
4
2024-11-27 11:09:13 检测到 /root/data/docker_data/jenkins/jenkins_home/workspace/blog/hexo.tar 文件变化,正在处理...
2024-11-27 11:09:13 已成功删除 /root/data/docker_data/jenkins/html/dev/hexo 目录中的无关文件。
2024-11-27 11:09:15 文件 /root/data/docker_data/jenkins/jenkins_home/workspace/blog/hexo.tar 已经稳定,准备解压...
2024-11-27 11:09:15 文件已成功解压到 /root/data/docker_data/jenkins/html/dev/hexo

停止运行脚本

1
2
cd /root/data/shell_script/monitor_jenkins_to_nginx
touch stop_monitor_script

查看脚本是否退出运行

1
ps aux | grep monitor_jenkins_to_nginx.sh | grep -v grep

如果没有输出,则表示该脚本已经退出运行状态

脚本设置开机自启

方法 1: 使用 crontab 设置开机自启

将脚本添加到 crontab 中,以便在系统启动时自动运行。

1.安装 cron

使用 apt 包管理器来安装 cron,通常在 Debian/Ubuntu 系统上可以执行以下命令:

1
2
sudo apt update
sudo apt install cron

2.启动并启用 cron 服务

安装完成后,启动 cron 服务并设置其开机自启:

1
2
sudo systemctl start cron
sudo systemctl enable cron

3.编辑 crontab 文件:

1
crontab -e

4.在 crontab 中添加以下行,设置开机启动脚本:

1
@reboot /bin/bash /root/data/shell_script/monitor_jenkins_to_nginx.sh start

这行命令会在系统启动时自动执行脚本。

5.保存并退出编辑器。

方法 2: 使用 systemd 设置开机自启

  1. 创建一个新的 systemd 服务文件,例如 monitor_jenkins_to_nginx.service

    1
    sudo nano /etc/systemd/system/monitor_jenkins_to_nginx.service
  2. 在服务文件中添加以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [Unit]
    Description=Monitor Jenkins to Nginx Script
    After=network.target

    [Service]
    Type=simple
    ExecStart=/bin/bash /root/data/shell_script/monitor_jenkins_to_nginx.sh start
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target

    这将使脚本在系统启动后运行,并确保网络启动完成后才运行该脚本。

  3. 保存文件并退出编辑器。

  4. 重新加载 systemd 配置:

    1
    sudo systemctl daemon-reload
  5. 启动服务并设置为开机启动:

    1
    sudo systemctl start monitor_jenkins_to_nginx.service
    1
    sudo systemctl enable monitor_jenkins_to_nginx.service

6.查看脚本运行状态

1
sudo systemctl status monitor_jenkins_to_nginx.service

参考资料

从零开始搭建JENKINS+GITHUB持续集成环境

Docker Compose安装部署Jenkins

Jenkins修改显示语言为中文显示(亲测有效)_jenkins 中文-CSDN博客

Jenkins配置任务时无 send files execute commands over SSH

Docker + Jenkins + Nginx实现前端自动化部署