Ansible 自动化部署全栈项目(Spring Boot + Vue + MySQL + Redis)实战(Rockylinux9.6)

前言

本教程使用 Ansible 实现全栈应用的自动化部署,包括:

  • 后端:Spring Boot(运行在 Tomcat 或 jar 方式)
  • 数据库:MySQL
  • 缓存:Redis
  • 前端:Vue.js(部署在 Nginx)
  • 代理:Nginx(反向代理 Vue 和 Spring Boot)

安装 Ansible

在 Ansible 控制节点(本地机器或 CI/CD 服务器)上安装:(本文以Rockylinux9.6为例)

这里看我的博客文章:Ansible 环境搭建

这里一共两个节点,192.168.92.19:ansible控制节点,192.168.92.20:被控制节点

确保 Ansible 已正确安装,先检查 Ansible 是否安装:

复制代码
rpm -qa | grep ansible
复制代码
[root@ansible ~]# ansible --version
ansible [core 2.14.18]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.21 (main, Feb 10 2025, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-5)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

配置 Ansible Hosts

编辑 /etc/ansible/hosts 添加你的目标服务器:

复制代码
[webservers]
192.168.92.20 

在这里配置完后要用ssh设置密钥,生成密钥,将本地的密钥推送,推送到远程的192.168.92.20机器(被控制端)---这些看我的博客:Ansible 环境搭建

测试连接:

复制代码
[root@ansible ~]# ansible all -m ping
192.168.92.20 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

这说明 Ansible 已经完全配置成功

创建 Ansible Playbook

创建目录结构:

复制代码
# 创建项目根目录并进入
mkdir -p ansible-playbook
cd ansible-playbook

# 为所有角色创建 tasks 目录(每个角色都需要)
mkdir -p roles/{docker_install,system_init,mysql,redis,springboot,nginx,vue}/tasks

# 为需要存放静态文件的角色创建 files 目录
mkdir -p roles/mysql/files
mkdir -p roles/springboot/files
mkdir -p roles/vue/files
mkdir -p roles/docker_install/files    # 新增:用于存放预置的 docker-compose 二进制文件

# 为需要存放配置模板的角色创建 templates 目录
mkdir -p roles/springboot/templates
mkdir -p roles/nginx/templates

# 为 docker_install 角色创建 handlers 目录(存放 handler 定义)
mkdir -p roles/docker_install/handlers

项目的树形包结构

复制代码
[root@ansible ansible-playbook]# tree
.
├── roles
│   ├── docker_install
│   │   ├── handlers
│   │   │   └── main.yml
│   │   └── tasks
│   │       └── main.yml
│   ├── mysql
│   │   ├── files
│   │   │   └── init.sql
│   │   └── tasks
│   │       └── main.yml
│   ├── nginx
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── nginx.conf.j2
│   ├── redis
│   │   └── tasks
│   │       └── main.yml
│   ├── springboot
│   │   ├── files
│   │   │   └── Springbootdemo-0.0.1-SNAPSHOT.jar
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── Dockerfile.j2
│   ├── system_init
│   │   └── tasks
│   │       └── main.yml
│   └── vue
│       ├── files
│       │   └── dist
│       │       ├── assets
│       │       │   ├── 1-DxVeaUtM.jpg
│       │       │   ├── 2-U6Qjex4J.jpg
│       │       │   ├── yun-DW8jof7n.jpg
│       │       │   ├── index-CYqzScuv.js
│       │       │   └── index-Cp89o39-.css
│       │       ├── favicon.ico
│       │       └── index.html
│       └── tasks
│           └── main.yml
└── site.yml

23 directories, 20 files

创建 system_init 角色(基础系统配置)

复制代码
mkdir -p /root/ansible-playbook/roles/system_init/tasks
vi /root/ansible-playbook/roles/system_init/tasks/main.yml

写入以下内容(与你手工部署步骤对应):

复制代码
---
# 关闭防火墙
- name: 停止 firewalld 服务
  systemd:
    name: firewalld
    state: stopped
    enabled: no

# 关闭 SELinux
- name: 禁用 SELinux(临时)
  selinux:
    state: disabled

- name: 永久禁用 SELinux(修改配置文件)
  lineinfile:
    path: /etc/selinux/config
    regexp: '^SELINUX='
    line: 'SELINUX=disabled'
    backup: yes

# 配置 Rocky Linux yum 源为阿里云镜像
- name: 备份原 yum 源并替换为阿里云源
  shell: |
    sed -e 's|^mirrorlist=|#mirrorlist=|g' \
        -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
        -i.bak /etc/yum.repos.d/rocky*.repo
    dnf makecache

# 安装基础工具(可选,便于调试)
- name: 安装基础工具包
  dnf:
    name:
      - vim
      - wget
      - curl
      - net-tools
    state: present

创建 docker_install 角色(安装 Docker 环境)

安装必要的 Ansible 集合

由于使用了 docker_networkdocker_container 等模块,需要安装 community.docker 集合:

复制代码
ansible-galaxy collection install community.docker

预置 Docker Compose 二进制文件,在控制节点(192.168.92.19)上手动下载 Docker Compose(仅一次),运行的时候开了vpn下载 Docker CE 仓库文件报错,不开下载 Docker Compose 二进制文件报错

复制代码
# 如果之前下载过,直接使用已有文件;否则执行:
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64" -o /tmp/docker-compose

# 将文件复制到角色的 files 目录
cp /tmp/docker-compose /root/ansible-playbook/roles/docker_install/files/docker-compose-Linux-x86_64

mkdir -p /root/ansible-playbook/roles/docker_install/tasks
vi /root/ansible-playbook/roles/docker_install/tasks/main.yml

写入内容(对应你手工部署中的 Docker 安装步骤):

复制代码
---
# 移除旧版 Docker
- name: 移除旧版 Docker 组件
  dnf:
    name:
      - docker
      - docker-common
      - docker-selinux
      - docker-engine
    state: absent
  ignore_errors: yes

# 安装依赖
- name: 安装必要依赖包
  dnf:
    name:
      - yum-utils
      - device-mapper-persistent-data
      - lvm2
    state: present

# 添加 Docker CE 仓库(华为云镜像)
- name: 下载 Docker CE 仓库文件
  get_url:
    url: https://mirrors.huaweicloud.com/docker-ce/linux/centos/docker-ce.repo
    dest: /etc/yum.repos.d/docker-ce.repo

- name: 替换仓库地址为华为云
  replace:
    path: /etc/yum.repos.d/docker-ce.repo
    regexp: 'download.docker.com'
    replace: 'mirrors.huaweicloud.com/docker-ce'

# 安装 Docker CE
- name: 安装 Docker CE
  dnf:
    name: docker-ce
    state: present

# 启动并设置开机自启
- name: 启动 Docker 服务并设置开机自启
  systemd:
    name: docker
    state: started
    enabled: yes

# 配置镜像加速器
- name: 创建 Docker 配置目录
  file:
    path: /etc/docker
    state: directory
    mode: '0755'

- name: 配置镜像加速器
  copy:
    dest: /etc/docker/daemon.json
    content: |
      {
        "registry-mirrors": [
          "https://docker.1ms.run",
          "https://docker.m.daocloud.io",
          "https://docker.xuanyuan.me"
        ]
      }
  notify: restart docker 

- name: 安装 pip
  dnf:
    name: python3-pip
    state: present

- name: 安装 Python 依赖(requests 和 docker)
  pip:
    name:
      - requests
      - docker
    state: present

# 安装 Docker Compose
- name: 复制 Docker Compose 二进制文件
  copy:
    src: docker-compose-Linux-x86_64
    dest: /usr/local/bin/docker-compose
    mode: '0755'

# 验证安装
- name: 验证 Docker Compose 安装
  command: docker-compose -v
  register: compose_version
  changed_when: false
  failed_when: false

- name: 输出 Docker Compose 版本
  debug:
    msg: "Docker Compose installed: {{ compose_version.stdout }}"
  when: compose_version.rc == 0 

# 创建自定义网络 app-network(幂等)
- name: 创建 Docker 网络 app-network
  docker_network:
    name: app-network
    driver: bridge
    state: present

在 Ansible 角色中,handlers 必须放在独立的 handlers/main.yml 文件中。

复制代码
[root@ansible ansible-playbook]# mkdir -p /root/ansible-playbook/roles/docker_install/handlers

#vi /root/ansible-playbook/roles/docker_install/handlers/main.yml
---
- name: restart docker
  systemd:
    name: docker
    state: restarted
    daemon_reload: yes

MySQL 自动化部署

MySQL 任务文件(roles/mysql/tasks/main.yml)

复制代码
# 1. 创建宿主机数据目录
- name: 创建 MySQL 数据目录
  file:
    path: /data/mysql/data
    state: directory

- name: 创建 MySQL 初始化脚本目录
  file:
    path: /data/mysql/init
    state: directory

# 2. 将初始化 SQL 复制到宿主机
- name: 复制 init.sql 到宿主机
  copy:
    src: init.sql
    dest: /data/mysql/init/init.sql
    mode: '0644'

# 3. 创建 Docker 网络(网络已在 docker_install 角色中统一创建,此处无需重复)

# 4. 启动 MySQL 容器
- name: 运行 MySQL 容器
  docker_container:
    name: mysql
    image: mysql:8.0
    state: started
    restart_policy: always
    ports:
      - "3306:3306"
    env:
      TZ: "Asia/Shanghai"
      LANG: "en_US.UTF-8"
      MYSQL_ROOT_PASSWORD: "123456"
      MYSQL_DATABASE: "big_event"
    volumes:
      - "/data/mysql/data:/var/lib/mysql"
      - "/data/mysql/init:/docker-entrypoint-initdb.d:ro"
    networks:
      - name: app-network
    command: [
      "--default-authentication-plugin=mysql_native_password",
      "--character-set-server=utf8mb4",
      "--collation-server=utf8mb4_general_ci",
      "--lower-case-table-names=1",
      "--performance-schema=1",
      "--skip-log-bin"
    ]

Redis 自动化部署

Redis 任务文件(roles/redis/tasks/main.yml)

复制代码
# 1. 创建宿主机数据目录
- name: 创建 Redis 数据目录
  file:
    path: /data/redis/data
    state: directory
    mode: '0755'

# 2. 创建 Docker 网络(网络已在 docker_install 角色中统一创建,此处无需重复)

# 3. 启动 Redis 容器
- name: 运行 Redis 容器
  docker_container:
    name: redis
    image: redis:6-alpine
    state: started
    restart_policy: always
    command: redis-server --maxmemory 512mb --appendonly yes
    volumes:
      - "/data/redis/data:/data"
    networks:
      - name: app-network

Spring Boot 后端自动化部署

准备文件

  • Springbootdemo-0.0.1-SNAPSHOT.jar 放到 roles/springboot/files/ 下。
  • Dockerfile 的模板(Jinja2)放到 roles/springboot/templates/Dockerfile.j2,内容就是你手工写的 Dockerfile(可以原样复制,不需要变量也可以直接作为静态文件)。

Spring Boot 任务文件(roles/springboot/tasks/main.yml)

复制代码
# 1. 创建目标目录(存放构建上下文)
- name: 创建 Spring Boot 构建目录
  file:
    path: /data/docker-compose/backend
    state: directory
    mode: '0755'

# 2. 复制 JAR 包
- name: 复制 Spring Boot JAR 包
  copy:
    src: Springbootdemo-0.0.1-SNAPSHOT.jar
    dest: /data/docker-compose/backend/Springbootdemo-0.0.1-SNAPSHOT.jar
    mode: '0644'

# 3. 复制 Dockerfile(如果内容固定,也可以直接用 copy 模块)
- name: 复制 Dockerfile
  copy:
    src: ../templates/Dockerfile.j2
    dest: /data/docker-compose/backend/Dockerfile
    mode: '0644'
  # 或者用 template 模块,如果你需要动态渲染

# 4. 确保自定义网络存在(网络已在 docker_install 角色中统一创建,此处无需重复)

# 5. 构建镜像
- name: 构建 Spring Boot 镜像
  docker_image:
    name: docker-compose_springboot
    build:
      path: /data/docker-compose/backend
      pull: no
    source: build
    state: present

# 6. 运行容器
- name: 运行 Spring Boot 容器
  docker_container:
    name: springboot
    image: docker-compose_springboot
    state: started
    restart_policy: always
    env:
      SPRING_REDIS_HOST: "redis"
      SPRING_REDIS_PORT: "6379"
    networks:
      - name: app-network

Vue/Nginx 前端自动化部署

Vue 角色中的 synchronize 模块

synchronize 依赖 rsync,如果目标主机未安装,任务可能失败。在ansible控制端(192.168.92.19)执行

复制代码
ansible webservers -m dnf -a 'name=rsync state=present'

Vue 任务文件(roles/vue/tasks/main.yml)

Vue 只需要把打包好的 dist 文件夹放置到宿主机上,供 Nginx 容器挂载。

复制代码
# 1. 创建宿主机存放前端静态文件的目录
- name: 创建前端文件目录
  file:
    path: /data/docker-compose/nginx/dist
    state: directory
    mode: '0755'

# 2. 将本地 dist 文件夹(位于 roles/vue/files/dist/)复制到目标主机
- name: 复制前端静态文件到宿主机
  synchronize:
    src: files/dist/
    dest: /data/docker-compose/nginx/dist/
    delete: yes
  # 或者用 copy 模块(如果 dist 不大):
  # copy:
  #   src: files/dist/
  #   dest: /data/docker-compose/nginx/dist/

Nginx 配置与部署(roles/nginx/tasks/main.yml

Nginx 使用容器运行,配置文件通过模板生成,并挂载静态文件目录。

复制代码
# 1. 确保 Nginx 配置文件目录存在(宿主机)
- name: 创建 Nginx 配置目录
  file:
    path: /data/docker-compose/nginx
    state: directory
    mode: '0755'

# 2. 使用模板生成 nginx.conf(支持变量,如服务器 IP)
- name: 生成 nginx.conf 配置文件
  template:
    src: nginx.conf.j2
    dest: /data/docker-compose/nginx/nginx.conf
    mode: '0644'

# 3. 确保自定义网络存在(网络已在 docker_install 角色中统一创建,此处无需重复)

# 4. 运行 Nginx 容器
- name: 启动 Nginx 容器
  docker_container:
    name: nginx
    image: nginx:alpine
    state: started
    restart_policy: unless-stopped
    ports:
      - "80:80"
    volumes:
      - "/data/docker-compose/nginx/nginx.conf:/etc/nginx/nginx.conf:ro"
      - "/data/docker-compose/nginx/dist:/usr/share/nginx/html:ro"
    networks:
      - name: app-network

Nginx 配置模板(roles/nginx/templates/nginx.conf.j2)

模板内容与你的手工配置完全一致,只是将 server_name 改为变量,方便不同环境修改。

复制代码
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    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;

    server {
        listen  80;
        server_name  {{ nginx_server_name | default('192.168.92.20') }};

        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;

        location /api/ {
            add_header 'Access-Control-Allow-Origin' '$http_origin' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH' always;
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;

            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
            }

            proxy_pass http://springboot:8080/;
            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-Forwarded-Proto $scheme;

            proxy_connect_timeout 75s;
            proxy_read_timeout 300s;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

运行 Ansible Playbook

创建主 Playbook site1.yml

site1.yml 中的 roles 列表会严格按照定义的顺序依次执行

ansible-playbook/ 目录下创建 site1.yml 文件:

复制代码
- hosts: webservers
  become: yes
  roles:
    - system_init          # 系统基础配置
    - docker_install       # 安装 Docker 环境(含网络创建)
    - mysql
    - redis
    - springboot
    - vue
    - nginx

cd /root/ansible-playbook
ansible-playbook site1.yml

[root@ansible ansible-playbook]# ansible-playbook site1.yml
[WARNING]: Collection community.docker does not support Ansible version 2.14.18

PLAY [webservers] ***************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************
ok: [192.168.92.20]

TASK [system_init : 停止 firewalld 服务] ****************************************************************************************************
ok: [192.168.92.20]

TASK [system_init : 禁用 SELinux(临时)] ***************************************************************************************************
ok: [192.168.92.20]

TASK [system_init : 永久禁用 SELinux(修改配置文件)] ***************************************************************************************
ok: [192.168.92.20]

TASK [system_init : 备份原 yum 源并替换为阿里云源] ******************************************************************************************
changed: [192.168.92.20]

TASK [system_init : 安装基础工具包] *********************************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 移除旧版 Docker 组件] ************************************************************************************************
changed: [192.168.92.20]

TASK [docker_install : 安装必要依赖包] ******************************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 下载 Docker CE 仓库文件] *********************************************************************************************
changed: [192.168.92.20]

TASK [docker_install : 替换仓库地址为华为云] ************************************************************************************************
changed: [192.168.92.20]

TASK [docker_install : 安装 Docker CE] ******************************************************************************************************
changed: [192.168.92.20]

TASK [docker_install : 启动 Docker 服务并设置开机自启] **************************************************************************************
changed: [192.168.92.20]

TASK [docker_install : 创建 Docker 配置目录] ************************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 配置镜像加速器] ******************************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 安装 pip] ************************************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 安装 Python 依赖(requests 和 docker)] ******************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 复制 Docker Compose 二进制文件] **************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 验证 Docker Compose 安装] ********************************************************************************************
ok: [192.168.92.20]

TASK [docker_install : 输出 Docker Compose 版本] ********************************************************************************************
skipping: [192.168.92.20]

TASK [docker_install : 创建 Docker 网络 app-network] ****************************************************************************************
ok: [192.168.92.20]

TASK [mysql : 创建 MySQL 数据目录] **********************************************************************************************************
ok: [192.168.92.20]

TASK [mysql : 创建 MySQL 初始化脚本目录] ****************************************************************************************************
ok: [192.168.92.20]

TASK [mysql : 复制 init.sql 到宿主机] *******************************************************************************************************
ok: [192.168.92.20]

TASK [mysql : 运行 MySQL 容器] **************************************************************************************************************
ok: [192.168.92.20]

TASK [redis : 创建 Redis 数据目录] **********************************************************************************************************
ok: [192.168.92.20]

TASK [redis : 运行 Redis 容器] **************************************************************************************************************
ok: [192.168.92.20]

TASK [springboot : 创建 Spring Boot 构建目录] ***********************************************************************************************
ok: [192.168.92.20]

TASK [springboot : 复制 Spring Boot JAR 包] *************************************************************************************************
ok: [192.168.92.20]

TASK [springboot : 复制 Dockerfile] *********************************************************************************************************
ok: [192.168.92.20]

TASK [springboot : 构建 Spring Boot 镜像] ***************************************************************************************************
ok: [192.168.92.20]

TASK [springboot : 运行 Spring Boot 容器] ***************************************************************************************************
ok: [192.168.92.20]

TASK [vue : 创建前端文件目录] ***************************************************************************************************************
ok: [192.168.92.20]

TASK [vue : 复制前端静态文件到宿主机] *******************************************************************************************************
changed: [192.168.92.20]

TASK [nginx : 创建 Nginx 配置目录] **********************************************************************************************************
ok: [192.168.92.20]

TASK [nginx : 生成 nginx.conf 配置文件] *****************************************************************************************************
changed: [192.168.92.20]

TASK [nginx : 启动 Nginx 容器] **************************************************************************************************************
changed: [192.168.92.20]

PLAY RECAP **********************************************************************************************************************************
192.168.92.20              : ok=35   changed=9    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

[root@ansible ansible-playbook]#

验证部署

检查各个服务状态

复制代码
[root@ansible ansible-playbook]# ansible webservers -m shell -a 'docker ps'
192.168.92.20 | CHANGED | rc=0 >>
CONTAINER ID   IMAGE                       COMMAND                  CREATED              STATUS              PORTS                               NAMES
e10821b911f3   nginx:alpine                "/docker-entrypoint...."   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp                  nginx
30ad93685e0d   docker-compose_springboot   "java -jar /app.jar"     7 minutes ago        Up 2 minutes        8080/tcp                            springboot
d2b800954467   redis:6-alpine              "docker-entrypoint.s..."   13 minutes ago       Up 2 minutes        6379/tcp                            redis
ed7add482e27   mysql:8.0                   "docker-entrypoint.s..."   13 minutes ago       Up 2 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   mysql

状态没有问题

访问 Web 应用(192.168.92.20:80)

实验完成!

相关推荐
Agent产品评测局2 小时前
企业超自动化落地,如何打通全业务流程的数据孤岛?技术路径全景盘点与选型指南
运维·人工智能·ai·chatgpt·自动化
志栋智能2 小时前
安全自动化不烧钱:低成本实战策略
运维·网络·人工智能·安全·自动化
Darkdreams2 小时前
Java进阶-在Ubuntu上部署SpringBoot应用
java·spring boot·ubuntu
想进大厂的小徐2 小时前
Spring 容器启动与 Bean 创建流程
java·spring boot·spring
梦梦代码精3 小时前
Dify + 扣子 + n8n + BuildingAI:从零搭建写作自动化平台,踩坑与实战全记录
运维·人工智能·架构·gitee·开源·自动化
真上帝的左手3 小时前
12. 消息队列-RabbitMQ-Spring Boot 集成 RabbitMQ
spring boot·rabbitmq·java-rabbitmq
运营小白3 小时前
2026年,我如何用AI自动化构建一个持续增长的博客矩阵
人工智能·经验分享·搜索引擎·自动化·ai自动写作
zhangren024683 小时前
Laravel5.x版本特性全解析
android·vue.js·spring boot·mysql
Meepo_haha3 小时前
ES在SpringBoot集成使用
spring boot·elasticsearch·jenkins