Ansible之Playbook(六):实例部署实战

OpenEuler系统全栈自动化部署实战

一、前言

在之前的Playbook系列文章中,我们学习了Ansible的基础语法、变量、条件判断、循环和Handlers等核心概念。本文将通过一个综合性的实战案例,演示如何使用Ansible Playbook在OpenEuler系统上一键部署一个完整的企业级服务集群,包括:远程获取SSH密钥认证、部署Nginx Web服务、搭建Keepalived高可用反向代理集群、配置DNS域名解析服务以及DHCP动态IP分配服务。

本文将按照CSDN博客的完整格式,带你从环境规划、目录结构设计到Playbook编写,一步步完成整个自动化部署项目。

二、环境规划

2.1 主机清单(Inventory)

在Ansible控制节点192.168.64.128上,编辑/etc/ansible/hosts文件:

bash 复制代码
[all:vars]
ansible_user=root
ansible_password=openeuler123
# ansible_ssh_private_key_file=/root/.ssh/id_rsa  # 密钥认证后可切换为私钥认证

[webservers]
192.168.64.100
192.168.64.101

[loadbalancers]
192.168.64.200  priority=100  state=MASTER  vip=192.168.64.250
192.168.64.201  priority=90   state=BACKUP  vip=192.168.64.250

[dnsserver]
192.168.64.108

[dhcpserver]
192.168.64.109

2.2 服务器角色规划

IP地址 主机名建议 角色说明
192.168.64.100 web01 Nginx Web服务器(主)
192.168.64.101 web02 Nginx Web服务器(备)
192.168.64.108 dns01 DNS域名解析服务器
192.168.64.109 dhcp01 DHCP动态IP分配服务器
192.168.64.200 lb01 Keepalived + Nginx反向代理(Master)
192.168.64.201 lb02 Keepalived + Nginx反向代理(Backup)
192.168.64.128 ansible Ansible控制节点

2.3 VIP规划

  • VIP地址:192.168.64.250

  • 作用:Keepalived虚拟IP,作为整个集群的统一入口,客户端访问此VIP即可获得Nginx反向代理服务

三、项目目录结构

在Ansible控制节点上创建如下目录结构:

bash 复制代码
mkdir -p /ansible/playbooks/roles/{common,nginx,keepalived,dns,dhcp}/{tasks,handlers,templates,files,vars}
mkdir -p /ansible/playbooks/{group_vars,host_vars}

完整目录结构:

bash 复制代码
/ansible/playbooks/
├── site.yml                      # 主入口Playbook
├── ssh_key_distribution.yml      # SSH密钥分发Playbook
├── inventory                     # 主机清单文件
├── group_vars/
│   └── all.yml                   # 全局变量
├── roles/
│   ├── common/
│   │   └── tasks/
│   │       └── main.yml          # 通用任务(防火墙、SELinux等)
│   ├── nginx/
│   │   ├── tasks/
│   │   │   └── main.yml          # Nginx安装配置任务
│   │   ├── handlers/
│   │   │   └── main.yml          # Nginx服务重启处理
│   │   ├── templates/
│   │   │   ├── nginx.conf.j2     # Nginx主配置模板
│   │   │   └── index.html.j2     # Web页面模板
│   │   └── vars/
│   │       └── main.yml
│   ├── keepalived/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   ├── handlers/
│   │   │   └── main.yml
│   │   └── templates/
│   │       ├── keepalived.conf.j2
│   │       └── nginx-lb.conf.j2  # 反向代理配置模板
│   ├── dns/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   └── templates/
│   │       ├── named.conf.j2
│   │       └── zone.db.j2
│   └── dhcp/
│       ├── tasks/
│       │   └── main.yml
│       └── templates/
│           └── dhcpd.conf.j2
└── files/
    └── id_rsa.pub                # Ansible控制节点的SSH公钥

四、SSH密钥远程分发

在执行所有自动化部署任务之前,首先需要将Ansible控制节点的SSH公钥分发到所有目标主机,实现免密登录。这一步是后续Playbook顺畅执行的基础。

4.1 生成SSH密钥对

在Ansible控制节点上执行:

bash 复制代码
ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa

4.2 编写SSH密钥分发Playbook

创建/ansible/playbooks/ssh_key_distribution.yml

bash 复制代码
---
- name: 分发SSH公钥到所有目标主机
  hosts: all
  gather_facts: no
  vars:
    ansible_user: root
    ansible_password: "openeuler123"  # 首次连接使用的密码
    
  tasks:
    - name: 确保目标主机.ssh目录存在
      ansible.builtin.file:
        path: /root/.ssh
        state: directory
        owner: root
        group: root
        mode: '0700'
      
    - name: 读取控制节点的SSH公钥
      ansible.builtin.slurp:
        src: /root/.ssh/id_rsa.pub
      register: ssh_pub_key
      delegate_to: localhost
      run_once: true
      
    - name: 将公钥添加到目标主机的authorized_keys
      ansible.posix.authorized_key:
        user: root
        state: present
        key: "{{ ssh_pub_key.content | b64decode }}"
        exclusive: no
        manage_dir: yes
      
    - name: 关闭SSH密码认证(可选,提高安全性)
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PasswordAuthentication'
        line: 'PasswordAuthentication no'
        state: present
      notify: restart sshd
      
  handlers:
    - name: restart sshd
      ansible.builtin.service:
        name: sshd
        state: restarted

执行命令

bash 复制代码
ansible-playbook /ansible/playbooks/ssh_key_distribution.yml

注意authorized_key模块位于ansible.posix集合中,如果未安装,先执行:

bash

复制代码
ansible-galaxy collection install ansible.posix

密钥分发成功后,可以将inventory中的密码认证注释掉,改用私钥认证:

bash 复制代码
[all:vars]
ansible_user=root
ansible_ssh_private_key_file=/root/.ssh/id_rsa
# ansible_password=openeuler123  # 已注释

五、全局变量配置

创建/ansible/playbooks/group_vars/all.yml

bash 复制代码
---
# 网络配置
domain_name: "example.local"
dns_server: "192.168.64.108"
dhcp_server: "192.168.64.109"
gateway: "192.168.64.2"
netmask: "255.255.255.0"
network: "192.168.64.0"

# Nginx Web配置
nginx_version: "1.20.1"
web_domain: "www.example.local"
web_root: "/usr/share/nginx/html"

# Keepalived配置
vip_address: "192.168.64.250"
vip_interface: "ens33"           # 根据实际网卡修改
vrid: 51

# DNS配置
dns_zone: "example.local"
dns_network_reverse: "64.168.192"
dns_forwarders:
  - "114.114.114.114"
  - "8.8.8.8"

# DHCP配置
dhcp_subnet: "192.168.64.0"
dhcp_netmask: "255.255.255.0"
dhcp_range_start: "192.168.64.150"
dhcp_range_end: "192.168.64.200"
dhcp_lease_time: 3600
dhcp_max_lease_time: 7200

六、通用角色(common)

创建/ansible/playbooks/roles/common/tasks/main.yml

bash 复制代码
---
- name: 关闭SELinux
  ansible.builtin.selinux:
    state: disabled

- name: 停止并禁用firewalld
  ansible.builtin.service:
    name: firewalld
    state: stopped
    enabled: no
  ignore_errors: yes

- name: 安装基础工具包
  ansible.builtin.dnf:
    name:
      - vim
      - wget
      - curl
      - net-tools
      - lsof
      - tcpdump
      - bash-completion
    state: present

- name: 配置hosts文件
  ansible.builtin.lineinfile:
    path: /etc/hosts
    line: "{{ item }}"
    state: present
  loop:
    - "192.168.64.100 web01 web01.example.local"
    - "192.168.64.101 web02 web02.example.local"
    - "192.168.64.108 dns01 dns01.example.local"
    - "192.168.64.109 dhcp01 dhcp01.example.local"
    - "192.168.64.200 lb01 lb01.example.local"
    - "192.168.64.201 lb02 lb02.example.local"
    - "{{ vip_address }} vip.example.local"

七、Nginx Web服务器角色

7.1 Nginx安装配置任务

创建/ansible/playbooks/roles/nginx/tasks/main.yml

bash 复制代码
---
- name: 安装Nginx依赖包
  ansible.builtin.dnf:
    name:
      - gcc
      - pcre
      - pcre-devel
      - zlib
      - zlib-devel
      - openssl
      - openssl-devel
    state: present

- name: 安装Nginx(使用openEuler官方源)
  ansible.builtin.dnf:
    name: nginx
    state: present

- name: 创建Web根目录
  ansible.builtin.file:
    path: "{{ web_root }}"
    state: directory
    owner: nginx
    group: nginx
    mode: '0755'

- name: 配置Nginx主配置文件
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify: restart nginx

- name: 部署自定义首页
  ansible.builtin.template:
    src: index.html.j2
    dest: "{{ web_root }}/index.html"
    owner: nginx
    group: nginx
    mode: '0644'

- name: 启动Nginx服务并设置开机自启
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: yes

7.2 Nginx配置模板

创建/ansible/playbooks/roles/nginx/templates/nginx.conf.j2

nginx

bash 复制代码
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
}

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;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;
    gzip on;
    gzip_vary on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
    
    server {
        listen 80;
        server_name {{ ansible_default_ipv4.address }} {{ ansible_hostname }} {{ web_domain }};
        root {{ web_root }};
        
        location / {
            index index.html index.htm;
        }
        
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }
}

7.3 自定义首页模板

创建/ansible/playbooks/roles/nginx/templates/index.html.j2

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Ansible自动化部署 - Nginx Web服务器</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
        .container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
        .info { background: #ecf0f1; padding: 15px; border-radius: 5px; margin: 20px 0; }
        .footer { margin-top: 30px; color: #7f8c8d; font-size: 14px; text-align: center; }
    </style>
</head>
<body>
    <div class="container">
        <h1>🚀 Ansible自动化部署成功!</h1>
        <div class="info">
            <p><strong>服务器信息:</strong></p>
            <ul>
                <li>主机名:{{ ansible_hostname }}</li>
                <li>IP地址:{{ ansible_default_ipv4.address }}</li>
                <li>操作系统:{{ ansible_distribution }} {{ ansible_distribution_version }}</li>
                <li>内核版本:{{ ansible_kernel }}</li>
                <li>部署时间:{{ ansible_date_time.iso8601 }}</li>
            </ul>
        </div>
        <p>🎉 恭喜!Nginx Web服务器已通过Ansible Playbook成功部署在OpenEuler系统上。</p>
        <div class="footer">
            <p>Powered by Ansible | OpenEuler | Nginx</p>
        </div>
    </div>
</body>
</html>

7.4 Handlers

创建/ansible/playbooks/roles/nginx/handlers/main.yml

bash 复制代码
---
- name: restart nginx
  ansible.builtin.service:
    name: nginx
    state: restarted

八、Keepalived + Nginx反向代理角色

在两台负载均衡器(192.168.64.200和192.168.64.201)上部署Keepalived实现高可用,同时配置Nginx作为反向代理,将请求转发到后端Web服务器。Keepalived基于VRRP协议实现故障切换与健康检查功能。

8.1 Keepalived安装配置任务

创建/ansible/playbooks/roles/keepalived/tasks/main.yml

bash 复制代码
---
- name: 安装Nginx(反向代理)
  ansible.builtin.dnf:
    name: nginx
    state: present

- name: 安装Keepalived
  ansible.builtin.dnf:
    name: keepalived
    state: present

- name: 启用IP转发
  ansible.builtin.sysctl:
    name: net.ipv4.ip_forward
    value: '1'
    sysctl_set: yes
    state: present
    reload: yes

- name: 配置Nginx反向代理
  ansible.builtin.template:
    src: nginx-lb.conf.j2
    dest: /etc/nginx/conf.d/loadbalancer.conf
    owner: root
    group: root
    mode: '0644'
  notify: reload nginx

- name: 备份原有Keepalived配置
  ansible.builtin.copy:
    src: /etc/keepalived/keepalived.conf
    dest: /etc/keepalived/keepalived.conf.bak
    remote_src: yes
    force: no
  ignore_errors: yes

- name: 配置Keepalived
  ansible.builtin.template:
    src: keepalived.conf.j2
    dest: /etc/keepalived/keepalived.conf
    owner: root
    group: root
    mode: '0644'
  notify: restart keepalived

- name: 启动Nginx服务
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: yes

- name: 启动Keepalived服务
  ansible.builtin.service:
    name: keepalived
    state: started
    enabled: yes

8.2 Nginx反向代理配置模板

创建/ansible/playbooks/roles/keepalived/templates/nginx-lb.conf.j2

bash 复制代码
# Nginx反向代理配置 - 负载均衡器
upstream backend_web {
    # 使用加权轮询算法,将请求分发到后端Web服务器
    server 192.168.64.100:80 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.64.101:80 weight=1 max_fails=3 fail_timeout=30s;
    
    # 开启会话保持
    keepalive 32;
}

upstream backend_api {
    # API反向代理后端服务
    server 192.168.64.100:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.64.101:8080 weight=1 max_fails=3 fail_timeout=30s;
    
    keepalive 32;
}

server {
    listen 80;
    server_name {{ web_domain }} {{ vip_address }};
    
    # 访问日志
    access_log /var/log/nginx/lb_access.log main;
    error_log /var/log/nginx/lb_error.log;
    
    # Web流量转发
    location / {
        proxy_pass http://backend_web;
        proxy_http_version 1.1;
        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_set_header Connection "";
        
        # 超时设置
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
    
    # API流量转发
    location /api/ {
        proxy_pass http://backend_api;
        proxy_http_version 1.1;
        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_set_header Connection "";
        
        # 重写请求路径(可选)
        # rewrite ^/api/(.*)$ /$1 break;
    }
    
    # 健康检查端点
    location /health {
        access_log off;
        return 200 "LB-{{ ansible_hostname }}-OK\n";
        add_header Content-Type text/plain;
    }
    
    # Nginx状态页(仅内网访问)
    location /nginx_status {
        stub_status on;
        access_log off;
        allow 192.168.64.0/24;
        deny all;
    }
}

8.3 Keepalived配置模板

创建/ansible/playbooks/roles/keepalived/templates/keepalived.conf.j2

bash 复制代码
! Configuration File for keepalived

global_defs {
    router_id {{ ansible_hostname }}
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/usr/bin/killall -0 nginx"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state {{ state }}
    interface {{ vip_interface }}
    virtual_router_id {{ vrid }}
    priority {{ priority }}
    advert_int 1
    
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    
    virtual_ipaddress {
        {{ vip_address }}/24 dev {{ vip_interface }}
    }
    
    track_script {
        check_nginx
    }
    
    notify_master "/usr/bin/echo '{{ ansible_hostname }} became MASTER' | /usr/bin/logger -t keepalived"
    notify_backup "/usr/bin/echo '{{ ansible_hostname }} became BACKUP' | /usr/bin/logger -t keepalived"
    notify_fault "/usr/bin/echo '{{ ansible_hostname }} became FAULT' | /usr/bin/logger -t keepalived"
}

8.4 Handlers

创建/ansible/playbooks/roles/keepalived/handlers/main.yml

bash 复制代码
---
- name: reload nginx
  ansible.builtin.service:
    name: nginx
    state: reloaded

- name: restart keepalived
  ansible.builtin.service:
    name: keepalived
    state: restarted

九、DNS域名解析角色

在DNS服务器(192.168.64.108)上部署BIND9,为内网提供域名解析服务。OpenEuler使用DNF包管理器,直接安装BIND即可。

9.1 DNS安装配置任务

创建/ansible/playbooks/roles/dns/tasks/main.yml

bash 复制代码
---
- name: 安装BIND9 DNS服务
  ansible.builtin.dnf:
    name: bind
    state: present

- name: 创建正向解析区域文件目录
  ansible.builtin.file:
    path: /var/named/zones
    state: directory
    owner: named
    group: named
    mode: '0750'

- name: 配置named主配置文件
  ansible.builtin.template:
    src: named.conf.j2
    dest: /etc/named.conf
    owner: root
    group: named
    mode: '0640'
  notify: restart named

- name: 配置正向解析区域文件
  ansible.builtin.template:
    src: zone.db.j2
    dest: "/var/named/zones/{{ dns_zone }}.db"
    owner: named
    group: named
    mode: '0640'
  notify: restart named

- name: 配置反向解析区域文件
  ansible.builtin.template:
    src: reverse.db.j2
    dest: "/var/named/zones/{{ dns_network_reverse }}.db"
    owner: named
    group: named
    mode: '0640'
  notify: restart named

- name: 配置防火墙规则(允许DNS查询)
  ansible.builtin.firewalld:
    service: dns
    permanent: yes
    state: enabled
    immediate: yes
  ignore_errors: yes

- name: 启动named服务并设置开机自启
  ansible.builtin.service:
    name: named
    state: started
    enabled: yes

9.2 named主配置模板

创建/ansible/playbooks/roles/dns/templates/named.conf.j2

bash 复制代码
options {
    listen-on port 53 { any; };
    listen-on-v6 port 53 { ::1; };
    directory   "/var/named";
    dump-file   "/var/named/data/cache_dump.db";
    statistics-file "/var/named/data/named_stats.txt";
    memstatistics-file "/var/named/data/named_mem_stats.txt";
    secroots-file   "/var/named/data/named.secroots";
    recursing-file  "/var/named/data/named.recursing";
    allow-query     { any; };
    
    recursion yes;
    
    forwarders {
        {% for fwd in dns_forwarders %}
        {{ fwd }};
        {% endfor %}
    };
    
    dnssec-validation no;
    
    managed-keys-directory "/var/named/dynamic";
    pid-file "/run/named/named.pid";
    session-keyfile "/run/named/session.key";
};

logging {
    channel default_debug {
        file "data/named.run";
        severity dynamic;
    };
};

zone "." IN {
    type hint;
    file "named.ca";
};

# 正向解析区域
zone "{{ dns_zone }}" IN {
    type master;
    file "zones/{{ dns_zone }}.db";
    allow-update { none; };
};

# 反向解析区域
zone "{{ dns_network_reverse }}.in-addr.arpa" IN {
    type master;
    file "zones/{{ dns_network_reverse }}.db";
    allow-update { none; };
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

9.3 正向解析区域文件模板

创建/ansible/playbooks/roles/dns/templates/zone.db.j2

bash 复制代码
$TTL 86400
@       IN      SOA     dns01.{{ dns_zone }}. admin.{{ dns_zone }}. (
                        {{ ansible_date_time.epoch }} ; Serial
                        3600        ; Refresh
                        1800        ; Retry
                        604800      ; Expire
                        86400 )     ; Minimum TTL

; 名称服务器
@               IN      NS      dns01.{{ dns_zone }}.
dns01           IN      A       {{ dns_server }}

; Web服务器
web01           IN      A       192.168.64.100
web02           IN      A       192.168.64.101
www             IN      CNAME   web01
www2            IN      CNAME   web02

; 负载均衡器
lb01            IN      A       192.168.64.200
lb02            IN      A       192.168.64.201
lb              IN      CNAME   lb01

; 虚拟IP
vip             IN      A       {{ vip_address }}
portal          IN      CNAME   vip

; DHCP服务器
dhcp01          IN      A       {{ dhcp_server }}
dhcp            IN      CNAME   dhcp01

; 其他服务
api             IN      A       {{ vip_address }}

9.4 反向解析区域文件模板

创建/ansible/playbooks/roles/dns/templates/reverse.db.j2

bash 复制代码
$TTL 86400
@       IN      SOA     dns01.{{ dns_zone }}. admin.{{ dns_zone }}. (
                        {{ ansible_date_time.epoch }} ; Serial
                        3600        ; Refresh
                        1800        ; Retry
                        604800      ; Expire
                        86400 )     ; Minimum TTL

; 名称服务器
@               IN      NS      dns01.{{ dns_zone }}.

; PTR记录
100             IN      PTR     web01.{{ dns_zone }}.
101             IN      PTR     web02.{{ dns_zone }}.
108             IN      PTR     dns01.{{ dns_zone }}.
109             IN      PTR     dhcp01.{{ dns_zone }}.
200             IN      PTR     lb01.{{ dns_zone }}.
201             IN      PTR     lb02.{{ dns_zone }}.
250             IN      PTR     vip.{{ dns_zone }}.

9.5 Handlers

创建/ansible/playbooks/roles/dns/handlers/main.yml

bash 复制代码
---
- name: restart named
  ansible.builtin.service:
    name: named
    state: restarted

十、DHCP分配IP角色

在DHCP服务器(192.168.64.109)上部署DHCP服务,为内网客户端动态分配IP地址。DHCP服务器自身必须使用静态IP,否则服务会失效。

10.1 DHCP安装配置任务

创建/ansible/playbooks/roles/dhcp/tasks/main.yml

bash 复制代码
---
- name: 安装DHCP服务
  ansible.builtin.dnf:
    name: dhcp-server
    state: present

- name: 从模板复制DHCP配置文件
  ansible.builtin.template:
    src: dhcpd.conf.j2
    dest: /etc/dhcp/dhcpd.conf
    owner: root
    group: root
    mode: '0644'
  notify: restart dhcpd

- name: 配置DHCP监听网卡
  ansible.builtin.lineinfile:
    path: /etc/sysconfig/dhcpd
    line: 'DHCPDARGS="{{ ansible_default_ipv4.interface }}"'
    regexp: '^DHCPDARGS='
    state: present
  notify: restart dhcpd

- name: 配置防火墙规则(允许DHCP服务)
  ansible.builtin.firewalld:
    service: dhcp
    permanent: yes
    state: enabled
    immediate: yes
  ignore_errors: yes

- name: 启动DHCP服务并设置开机自启
  ansible.builtin.service:
    name: dhcpd
    state: started
    enabled: yes

10.2 DHCP配置模板

创建/ansible/playbooks/roles/dhcp/templates/dhcpd.conf.j2

bash 复制代码
# 全局配置
option domain-name "{{ domain_name }}";
option domain-name-servers {{ dns_server }};
default-lease-time {{ dhcp_lease_time }};
max-lease-time {{ dhcp_max_lease_time }};
authoritative;

# 禁用DNS动态更新
ddns-update-style none;

# 日志配置
log-facility local7;

# 子网配置
subnet {{ dhcp_subnet }} netmask {{ dhcp_netmask }} {
    range {{ dhcp_range_start }} {{ dhcp_range_end }};
    option routers {{ gateway }};
    option subnet-mask {{ dhcp_netmask }};
    option broadcast-address 192.168.64.255;
    option domain-name-servers {{ dns_server }};
    option domain-name "{{ domain_name }}";
}

# 为特定MAC地址分配固定IP
# 例如:为测试客户端分配固定IP
host test-client {
    hardware ethernet 00:0C:29:XX:XX:XX;  # 替换为实际MAC地址
    fixed-address 192.168.64.210;
    option host-name "test-client";
}

10.3 Handlers

创建/ansible/playbooks/roles/dhcp/handlers/main.yml

bash 复制代码
---
- name: restart dhcpd
  ansible.builtin.service:
    name: dhcpd
    state: restarted

十一、主入口Playbook(site.yml)

创建/ansible/playbooks/site.yml,作为整个自动化部署的入口:

bash 复制代码
---
- name: 1. 初始化所有服务器(通用配置)
  hosts: all
  gather_facts: yes
  roles:
    - common

- name: 2. 部署Nginx Web服务器
  hosts: webservers
  gather_facts: yes
  roles:
    - nginx

- name: 3. 部署Keepalived + Nginx反向代理(高可用负载均衡)
  hosts: loadbalancers
  gather_facts: yes
  roles:
    - keepalived

- name: 4. 部署DNS域名解析服务器
  hosts: dnsserver
  gather_facts: yes
  roles:
    - dns

- name: 5. 部署DHCP动态IP分配服务器
  hosts: dhcpserver
  gather_facts: yes
  roles:
    - dhcp

- name: 6. 部署后验证
  hosts: localhost
  gather_facts: no
  tasks:
    - name: 显示部署完成信息
      ansible.builtin.debug:
        msg:
          - "========================================"
          - "🎉 恭喜!全栈自动化部署已完成!"
          - "========================================"
          - "Nginx Web服务器: 192.168.64.100, 192.168.64.101"
          - "Keepalived高可用集群 (VIP: 192.168.64.250): 192.168.64.200(MASTER), 192.168.64.201(BACKUP)"
          - "DNS服务器: 192.168.64.108"
          - "DHCP服务器: 192.168.64.109"
          - "========================================"
          - "验证命令:"
          - "  curl http://192.168.64.250"
          - "  nslookup web01.example.local 192.168.64.108"
          - "  ip addr show | grep 192.168.64.250"
          - "  systemctl status dhcpd"
          - "========================================"

十二、执行Playbook

12.1 语法检查

bash 复制代码
cd /ansible/playbooks
ansible-playbook site.yml --syntax-check

12.2 试运行(Dry Run)

bash 复制代码
ansible-playbook site.yml --check

12.3 正式执行

bash 复制代码
ansible-playbook site.yml

12.4 指定执行部分角色

bash 复制代码
# 仅部署Web服务器
ansible-playbook site.yml --tags nginx

# 仅部署负载均衡器
ansible-playbook site.yml --tags keepalived

十三、部署验证

13.1 验证Nginx Web服务

bash 复制代码
# 访问单台Web服务器
curl http://192.168.64.100
curl http://192.168.64.101

# 查看Nginx服务状态
ansible webservers -m shell -a "systemctl status nginx | head -3"

13.2 验证Keepalived高可用集群

bash 复制代码
# 查看VIP所在节点
ansible loadbalancers -m shell -a "ip addr show | grep {{ vip_address }}"

# 模拟故障切换测试
# 在MASTER节点上停止Keepalived服务
ansible 192.168.64.200 -m service -a "name=keepalived state=stopped"

# 观察VIP是否漂移到BACKUP节点
ansible 192.168.64.201 -m shell -a "ip addr show | grep 192.168.64.250"

# 恢复MASTER节点
ansible 192.168.64.200 -m service -a "name=keepalived state=started"

13.3 验证反向代理

bash 复制代码
# 通过VIP访问(应该轮询分发到两台Web服务器)
for i in {1..10}; do curl -s http://192.168.64.250 | grep "主机名"; done

# 查看负载均衡器状态
curl http://192.168.64.250/nginx_status

13.4 验证DNS解析

bash 复制代码
# 测试正向解析
nslookup web01.example.local 192.168.64.108
nslookup www.example.local 192.168.64.108
nslookup vip.example.local 192.168.64.108

# 测试反向解析
nslookup 192.168.64.100 192.168.64.108

13.5 验证DHCP服务

bash 复制代码
# 查看DHCP服务状态
ansible dhcpserver -m shell -a "systemctl status dhcpd"

# 查看DHCP租约文件
ansible dhcpserver -m shell -a "cat /var/lib/dhcpd/dhcpd.leases"

十四、故障排查与优化建议

14.1 常见问题排查

问题现象 可能原因 解决方法
SSH连接失败 密钥未正确分发 重新执行ssh_key_distribution.yml
Nginx启动失败 端口被占用 `netstat -tlnp
Keepalived VIP不漂移 防火墙阻止VRRP协议 确保firewalld已关闭或放行VRRP(协议号112)
DNS解析失败 named服务未启动 systemctl restart named
DHCP分配失败 网卡配置错误 检查/etc/sysconfig/dhcpd中的网卡名称

14.2 优化建议

  1. 使用Ansible Vault加密敏感信息:将密码等敏感变量放入加密文件中

  2. 开启Pipeline :在ansible.cfg中设置pipelining = True可提升执行效率

  3. 配置Fact缓存 :设置fact_caching = jsonfile加速重复执行

  4. 使用Roles结构:将不同服务拆分为独立Role,便于维护和复用

十五、总结

本文通过一个完整的实战案例,演示了如何使用Ansible Playbook在OpenEuler系统上自动化部署一套企业级服务集群,涵盖:

  • SSH密钥分发 :使用authorized_key模块实现免密登录

  • Nginx Web服务:通过模板部署Web服务器和自定义页面

  • Keepalived高可用集群:基于VRRP协议实现VIP漂移,配合Nginx实现反向代理和API转发

  • DNS域名解析:部署BIND9服务,实现内网正向/反向域名解析

  • DHCP动态IP分配:为内网客户端自动分配IP地址

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展