项目概述 本项目旨在通过 GitLab + Jenkins + Ansible
实现 Hexo 博客的一键自动化部署,并使用 Nginx + Keepalived
实现多节点高可用架构,提升部署效率和服务稳定性。
架构设计图 flowchart TB
Developer[开发者] -->|Git Push| GitLab[GitLab]
GitLab -->|Webhook| Jenkins[Jenkins]
Jenkins -->|调用 Ansible| Cluster[Web Server Cluster<br>Nginx + Hexo]
Cluster --> Master[Master]
Cluster --> Backup[Backup]
Master -->|Keepalived| VIP[VIP]
Backup -->|Keepalived| VIP
%% 样式定义
classDef default fill:#f9f,stroke:#333,stroke-width:2px;
classDef server fill:#9cf,stroke:#333,stroke-width:2px;
classDef cluster fill:#fcf,stroke:#333,stroke-width:2px;
%% 应用样式
class GitLab,Jenkins,Master,Backup server;
class Cluster cluster;
环境准备 主机规划
主机名
IP
环境
角色
Gitlab
192.168.100.116
Ubuntu22.04 LTS / 4h4g
GitLab 18.0
Jenkins
192.168.100.114
Ubuntu22.04LTS / 2h2g
Jenkins 2.504.2 + Ansible
web01
192.168.100.110
Centos7.9 / 1h1g
Nginx + Keepalived(主)
web02
192.168.100.112
Centos7.9 /1h1g
Nginx + Keepalived(备)
VIP
192.168.100.120
-
虚拟IP地址
目录规划
服务
目录
说明
Hexo
/www/blog/
静态网站根目录
Nginx
/etc/nginx/
Nginx安装目录
Keepalived
/etc/keepalived
Keepalived安装目录
Ansible
/etc/ansible/
Ansible工作目录
部署步骤 搭建 GitLab 仓库 信任 GitLab 的 GPG 公钥
1 curl -fsSL https://packages.gitlab.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/gitlab_gitlab-ce-archive-keyring.gpg
配置/etc/apt/sources.list.d/gitlab-ce.list
1 2 echo 'deb [signed-by=/usr/share/keyrings/gitlab_gitlab-ce-archive-keyring.gpg] https://mirrors.cernet.edu.cn/gitlab-ce/ubuntu jammy main ' | sudo tee /etc/apt/sources.list.d/gitlab-ce.list
更新源并安装 gitlab-ce
1 2 sudo apt-get updatesudo apt-get install gitlab-ce
镜像站参考配置:https://help.mirrors.cernet.edu.cn/gitlab-ce/
编辑配置文件/etc/gitlab/gitlab.rb
,默认80端口未被占用可不修改,修改external_url,nginx[‘listen_port’]
加载配置,同时防火墙放行端口
1 2 sudo gitlab-ctl reconfiguresudo gitlab-ctl restart
查看初始密码,默认用户名为root,密码将在24h后失效
1 cat /etc/gitlab/initial_root_password
搭建 Jenkins + Ansible 环境 安装jdk环境
这里以openjdk为例,实际最好编译安装java环境
1 2 sudo apt updatesudo apt install fontconfig openjdk-17-jre
检查安装情况
添加jenkins官方仓库到包管理器
1 curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
安装jenkins
1 2 sudo apt updatesudo apt install jenkins
查看安装情况
1 sudo systemctl status jenkins
配置开机自启动,并放行响应端口
1 2 sudo systemctl start jenkinssudo systemctl enable jenkins
初次访问的时候会有个解锁密码
1 sudo cat /var/lib/jenkins/secrets/initialAdminPassword
安装Ansible
1 sudo apt install ansible
1 2 yum install -y epel-release yum install -y ansible
配置 Anisble 配置SSH免密连接 创建密钥对
分发公钥
1 2 ssh-copy-id sky@192.168.100.110 ssh-copy-id sky@192.168.100.112
测试免密连接是否配置成功
目录结构设计 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 ansible/ ├── deploy_hexo.yml ├── deploy_ha.yml ├── jenkins-playbook.yml ├── Jenkinsfile ├── inventory/ │ └── hosts ├── group_vars/ │ └── web.yml └── roles/ ├── nginx/ │ ├── tasks/ │ │ └── main.yml │ ├── handlers/ │ │ └── main.yml │ ├── templates/ │ │ ├── nginx.conf.j2 │ │ └── default.conf.j2 │ └── files/ │ └── index.html └── keepalived/ ├── tasks/ │ └── main.yml ├── handlers/ │ └── main.yml └── templates/ ├── keepalived.conf.j2 └── check_nginx.sh.j2
配置主机清单 在inventory/hosts
文件中配置Web服务器组:
1 2 3 4 5 6 7 [web ] web01 ansible_host=192.168.100.110 web02 ansible_host=192.168.100.112 [all:vars ] ansible_user=root ansible_connection=ssh
配置Nginx角色 Nginx角色通过Ansible自动化完成安装、配置和服务启动。主要任务包括:
添加Nginx官方仓库
安装Nginx
配置Nginx服务
启动并启用Nginx服务
配置防火墙规则
编辑roles/nginx/handlers/main.yml
1 2 3 4 5 6 7 8 9 10 --- - name: restart nginx systemd: name: nginx state: restarted - name: reload firewalld systemd: name: firewalld state: reloaded
编辑roles/nginx/tasks/main.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 --- - name: 添加Nginx仓库 yum_repository: name: nginx description: nginx repo baseurl: http://nginx.org/packages/centos/7/$basearch/ gpgcheck: no enabled: yes when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7' - name: 安装Nginx yum: name: nginx state: present when: ansible_distribution == 'CentOS' - name: 确保Nginx配置目录存在 file: path: /etc/nginx/conf.d state: directory mode: '0755' - name: 复制Nginx配置文件 template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: '0644' notify: restart nginx - name: 复制默认站点配置 template: src: default.conf.j2 dest: /etc/nginx/conf.d/default.conf mode: '0644' notify: restart nginx - name: 启用Nginx服务 systemd: name: nginx enabled: yes state: started - name: 开放HTTP防火墙端口 firewalld: port: 80 /tcp permanent: yes state: enabled notify: reload firewalld when: ansible_distribution == 'CentOS'
配置roles/nginx/templates/default.conf.j2
1 2 3 4 5 6 7 8 9 10 server { listen 80 ; server_name localhost; error_log /var/log/nginx/blog_error.log notice; access_log /var/log/nginx/blog_access.log main; location / { root /www/blog; index index.html index.htm; } }
配置roles/nginx/templates/nginx.conf.j2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; 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; keepalive_timeout 65 ; include /etc/nginx/conf.d/*.conf; }
配置Keepalived角色 Keepalived角色实现Web服务器的高可用配置,主要任务包括:
安装Keepalived
配置Keepalived服务
配置Nginx服务监控脚本
配置虚拟IP
启动并启用Keepalived服务
编辑roles/keepalived/handlers/main.yml
1 2 3 4 5 --- - name: restart keepalived systemd: name: keepalived state: restarted
编辑roles/keepalived/tasks/main.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 --- - name: 安装Keepalived yum: name: keepalived state: present when: ansible_distribution == 'CentOS' - name: 创建检查脚本目录 file: path: /etc/keepalived/scripts state: directory mode: '0755' - name: 复制Nginx检查脚本 template: src: check_nginx.sh.j2 dest: /etc/keepalived/scripts/check_nginx.sh mode: '0755' - name: 配置Keepalived template: src: keepalived.conf.j2 dest: /etc/keepalived/keepalived.conf mode: '0644' notify: restart keepalived - name: 启用Keepalived服务 systemd: name: keepalived enabled: yes state: started
编辑roles/keepalived/templates/check_nginx.sh.j2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /usr/bin/systemctl status nginx | grep -q 'active (running)' if [ $? -ne 0 ]; then /usr/bin/systemctl restart nginx sleep 2 /usr/bin/systemctl status nginx | grep -q 'active (running)' if [ $? -ne 0 ]; then /usr/bin/systemctl stop keepalived fi fi
编辑roles/keepalived/templates/keepalived.conf.j2
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 global_defs { router_id {{ inventory_hostname }} script_user root enable_script_security } vrrp_script check_nginx { script "/etc/keepalived/scripts/check_nginx.sh" interval 3 weight -2 fall 2 rise 1 } vrrp_instance VI_1 { {% if inventory_hostname == 'web01' % } state MASTER priority 100 {% else % } state BACKUP priority 90 {% endif % } interface eth0 virtual_router_id 51 advert_int 1 authentication { auth_type PASS auth_pass hexo_blog } virtual_ipaddress { 192.168 .100 .120 /24 } track_script { check_nginx } }
编写部署Hexo博客 playbook 编辑deploy_hexo.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 --- - name: 部署Hexo博客 hosts: web become: yes vars: hexo_blog_path: /www/blog roles: - nginx tasks: - name: 创建Hexo博客目录 file: path: "{{ hexo_blog_path }} " state: directory mode: '0755' - name: 清空原有博客内容 shell: "rm -rf {{ hexo_blog_path }} /*" args: warn: false - name: 复制Hexo博客内容 copy: src: "{{ hexo_blog_content_dir }} /" dest: "{{ hexo_blog_path }} " mode: '0644' directory_mode: '0755' when: hexo_blog_content_dir is defined
编写部署Keepalived高可用 playbook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 --- - name: 配置高可用Web服务器 hosts: web become: yes roles: - keepalived tasks: - name: 确保防火墙允许Keepalived VRRP协议 firewalld: rich_rule: 'rule protocol value="vrrp" accept' permanent: yes state: enabled notify: reload firewalld when: ansible_distribution == 'CentOS' handlers: - name: reload firewalld systemd: name: firewalld state: reloaded
编写Jenkins playbook 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 --- - name: Jenkins调用的Hexo部署任务 hosts: web become: yes vars: hexo_blog_path: /www/blog hexo_blog_content_dir: "{{ jenkins_workspace }} /public" roles: - nginx tasks: - name: 创建Hexo博客目录 file: path: "{{ hexo_blog_path }} " state: directory mode: '0755' - name: 清空原有博客内容 shell: "rm -rf {{ hexo_blog_path }} /*" args: warn: false - name: 复制Hexo博客内容 copy: src: "{{ hexo_blog_content_dir }} /" dest: "{{ hexo_blog_path }} " mode: '0644' directory_mode: '0755' when: hexo_blog_content_dir is defined
配置Gitlab 创建项目
推送本地项目 创建SSH密钥对
复制id_rsa.pub文件的内容,配置Gitlab SSH密钥
初始化项目并推送到Gitlab仓库
1 2 3 4 5 6 git init git add . git switch --create main git commit -m "Initial commit" git remote add origin http://192.168.100.116:1080/root/hexo-blog.git git push -u origin main
配置Jenkins公钥
配置Webhooks 在Jenkins查看触发器配置,复制url,生成token
配置url,token,勾选推送事件,合并请求事件
关闭SSL认证(本地测试)
勾选允许本地网络请求
配置Jenkins 配置用户凭证 配置Jenkins的私钥
配置Gitlab凭证
配置成果如下:
配置 NodeJS 安装完相应的插件,配置NodeJS,别名取成node
新建流水线项目
配置触发器 勾选图示选项
配置流水线
编辑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 pipeline { agent any tools { nodejs "node" } stages { stage('Checkout') { steps { checkout scm } } stage('Install Dependencies') { steps { sh 'npm install' sh 'npm install hexo-cli -g' } } stage('Build') { steps { sh 'hexo clean && hexo generate' } } stage('Deploy') { steps { script { def workspace = env.WORKSPACE sh "ansible-playbook -i /etc/ansible/inventory/hosts /etc/ansible/jenkins-playbook.yml -e 'jenkins_workspace=${workspace}'" sh "ansible-playbook -i /etc/ansible/inventory/hosts /etc/ansible/deploy_ha.yml" } } } } post { success { echo '部署成功!' } failure { echo '部署失败,请检查日志!' } } }
部署 & 测试 推送事件测试
状态码返回200
Jenkins出现新的构建
部署成功
访问网站测试,状态码均200,正常