Day 20-Ansible 自动化运维实战复盘:从环境搭建到高可用集群部署


Ansible自动化运维实操:从环境搭建到高可用集群部署全流程复盘

一、实验环境说明

本次实操共使用2台Rocky Linux和1台Centos 7主机,分别承担控制节点、Web后端、高可用备节点角色,具体IP与作用如下:

主机名IP地址角色与作用
server1192.168.48.161Ansible控制节点、Keepalived主节点、Haproxy主节点,负责下发配置与管控所有主机
server2192.168.48.162Web后端节点,部署Apache服务,作为负载均衡后端池成员
node1192.168.48.171Keepalived备节点、Haproxy备节点,同时用于测试批量用户管理等功能

这次实操从Ansible基础环境搭建开始,一步步完成了Playbook入门、Jinja2模板配置、Keepalived高可用部署,最后实现了Haproxy+Keepalived负载均衡集群的全自动化部署。中间踩了不少权限、YAML缩进、变量引用的坑,把完整流程和排坑思路整理出来,方便后续复盘。

二、环境准备:Ansible管控端与被控端配置

2.1 被控端统一用户与sudo权限配置

Ansible基于SSH协议工作,为了统一管控权限,所有被控节点都需要创建专用的运维用户,并配置sudo免密提权。

首先在server2节点创建devops用户并设置密码:

useradd devops
passwd devops

一开始图省事设了个短密码,直接被系统弹出BAD PASSWORD警告,还踩了一次两次密码输入不一致的坑,最后设置了符合复杂度要求的密码才通过验证。
在这里插入图片描述

用户创建完成后,通过visudo命令配置sudo免密权限,在root配置行下方添加devops用户的权限规则,让该用户可以无需密码执行所有sudo命令:

## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
devops  ALL=(ALL)       NOPASSWD: ALL

在这里插入图片描述

同样的操作在server1控制端也执行一遍,创建devops用户并配置sudo免密,为后续SSH免密和Ansible运行做准备。

2.2 控制端SSH免密配置

Ansible默认通过SSH连接被控端,配置密钥免密登录后无需每次输入密码,是自动化运维的基础。

切换到devops用户,生成4096位的RSA密钥对:

su - devops
ssh-keygen -t rsa -b 4096

生成过程一路回车即可,不设置密钥口令,避免后续使用时重复输入密码。

密钥生成完成后,通过ssh-copy-id命令将公钥推送到被控节点server2:

ssh-copy-id devops@192.168.48.162

首次连接会弹出主机指纹确认提示,输入yes后再输入devops用户的密码,即可完成公钥推送。推送完成后可以直接SSH登录验证,无需输入密码就说明配置成功。
在这里插入图片描述

2.3 Ansible工作目录与基础配置

我没有使用系统默认的/etc/ansible目录,而是在devops用户家目录下创建独立的ansible工作目录,方便文件管理和权限控制。

mkdir -p ~/ansible
cd ~/ansible

在这里插入图片描述

首先编写inventory主机清单文件,定义web主机组和通用连接变量:

[web]
server2 ansible_host=192.168.48.162

[web :vars]
ansible_user=devops
ansible_become=yes
ansible_become_method=sudo

将用户名、sudo提权配置都放在组变量中,后续执行命令时无需重复附加参数。
在这里插入图片描述

接着编写ansible.cfg主配置文件,指定主机清单路径,同时关闭主机密钥检查,避免首次连接弹出确认提示:

[defaults]
inventory=/home/devops/ansible/inventory
host_key_checking = False

在这里插入图片描述

配置完成后,使用ping模块做连通性测试:

ansible web -m ping

返回"ping": "pong"就说明两端通信正常,Ansible基础环境搭建完成。
在这里插入图片描述

三、Playbook入门:Apache服务自动化部署

3.1 基础版:三步完成Apache部署

第一个Playbook实现最基础的Apache部署,包含安装软件、启动服务、写入默认首页三个任务。

创建install-apache.yml文件:

- hosts: web
  become: yes
  tasks:
    - name: Install the Apache
      ansible.builtin.yum:
        name: httpd
        state: present

    - name: Start service httpd, if not started
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: yes

    - name: create index.html
      ansible.builtin.copy:
        content: "www.westos.org\n"
        dest: /var/www/html/index.html

在这里插入图片描述

执行Playbook:

ansible-playbook install-apache.yml

首次执行三个任务均为changed状态,说明软件安装、服务启动、首页写入全部执行成功。Ansible的幂等性在这里体现得很明显,重复执行时已完成的任务会显示ok,不会重复操作。

在这里插入图片描述

3.2 主机分组:多节点批量管理

后续新增node1节点后,我将主机按角色分组管理:web组存放Web节点,db组存放测试节点,再通过:children关键字定义lamp父组,批量操作所有节点时直接调用lamp组即可。

更新inventory文件:

[web]
192.168.48.162

[db]
192.168.48.171

[lamp:children]
web
db

在这里插入图片描述

node1节点需要提前配置好devops用户和sudo免密,操作流程和server2完全一致。
在这里插入图片描述

配置完成后测试lamp组的连通性:

ansible lamp -m ping

两个节点均返回pong,分组配置正常。

在这里插入图片描述

3.3 配置文件修改:lineinfile模块改端口

需要修改Apache配置文件的监听端口时,使用lineinfile模块最合适,功能类似Linux的sed命令,通过正则匹配行并替换内容。

比如将默认80端口修改为8080,在Playbook中新增任务:

    - name: Ensure the default Apache port is 8080
      ansible.builtin.lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^Listen'
        insertafter: '#Listen'
        line: Listen 8080

在这里插入图片描述

执行Playbook后,只有端口修改任务为changed状态,其余已配置完成的任务均为ok状态,不会重复执行。
在这里插入图片描述

3.4 配置变更触发重启:handlers机制

修改端口后需要重启服务才会生效,但如果端口没有发生变更,就没必要重启服务,这种场景正好适用Ansible的handlers机制:只有任务状态为changed时,才会通过notify触发对应的处理器。

将端口改回80,新增notify触发和handlers配置:

- hosts: web
  become: yes
  tasks:
    - name: Install the Apache
      ansible.builtin.yum:
        name: httpd
        state: present

    - name: Start service httpd, if not started
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: yes

    - name: create index.html
      ansible.builtin.copy:
        content: "www.westos.org\n"
        dest: /var/www/html/index.html

    - name: Ensure the default Apache port is 80
      ansible.builtin.lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^Listen'
        insertafter: '#Listen'
        line: Listen 80
      notify: restart service httpd

  handlers:
    - name: restart service httpd
      ansible.builtin.service:
        name: httpd
        state: restarted

在这里插入图片描述

执行后可以看到,端口修改任务变更后,触发了RUNNING HANDLER执行服务重启。如果端口已经是80,任务为ok状态,handler就不会执行,避免不必要的服务中断。

在这里插入图片描述

3.5 变量引用:facts变量与自定义变量

Ansible的facts组件可以自动采集被控端的主机名、IP、系统版本等信息,这些内置变量可以直接在Playbook中调用。比如将首页内容设置为当前主机名,方便区分不同后端节点:

    - name: create index.html
      ansible.builtin.copy:
        content: "{{ ansible_hostname }}\n"
        dest: /var/www/html/index.html

在这里插入图片描述
在这里插入图片描述

除了内置facts变量,也可以在Playbook中自定义变量,比如将端口号定义为变量,后续修改时只需更改变量值即可:

- hosts: lamp
  vars:
    http_port: 80
  tasks:
    ...
    - name: Ensure the default Apache port is {{ http_port }}
      ansible.builtin.lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^Listen'
        insertafter: '#Listen'
        line: Listen {{ http_port }}
      notify: restart service httpd

3.6 自动校验:uri模块做可用性检测

服务部署完成后,可以通过uri模块自动发起HTTP请求,验证服务是否正常返回200状态码,无需手动逐个curl测试。

在Playbook中新增一个针对localhost的play,专门执行检测任务:

- hosts: localhost
  gather_facts: false
  become: false
  tasks:
    - name: Check that you can connect (GET) to a page and it returns a status 200
      ansible.builtin.uri:
        url: http://192.168.48.162
        return_content: true
      register: result

    - name: Print return information from the previous task
      ansible.builtin.debug:
        var: result

在这里插入图片描述

在这里插入图片描述

执行完成后可以看到返回状态码为200,响应内容为server2,说明Apache服务运行正常。
在这里插入图片描述

3.7 任务标签:tags实现精细化执行

当Playbook任务较多时,有时只需要执行其中某几个任务,不需要全量运行。给任务打上tags标签,就可以通过标签指定执行范围。

给每个任务添加对应的标签:

    - name: Install the Apache
      ansible.builtin.yum:
        name: httpd
        state: present
      tags: t1

    - name: Start service httpd, if not started
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: yes
      tags: t2

    - name: create index.html
      ansible.builtin.copy:
        content: "{{ ansible_hostname }}\n"
        dest: /var/www/html/index.html
      tags: t3

在这里插入图片描述

可以先查看Playbook中所有的标签:

ansible-playbook install-apache.yml --list-tags

在这里插入图片描述

指定单个标签执行,比如只执行安装任务:

ansible-playbook install-apache.yml --tags=t1

也可以指定多个标签,用逗号分隔:

ansible-playbook install-apache.yml --tags=t1,t3

在这里插入图片描述

四、进阶实战:Jinja2模板与高可用集群部署

4.1 Keepalived高可用:模板化批量配置

Keepalived主备节点的配置大部分内容一致,只有节点状态、优先级等少数参数不同,非常适合用Jinja2模板+主机变量的方式实现,一份模板适配所有节点。

首先在inventory中新增hacluster组,给每个主机定义独立的状态和优先级变量,组变量定义公共参数:

[hacluster]
192.168.48.161 state=MASTER pri=100
192.168.48.171 state=BACKUP pri=80

[hacluster:vars]
interface=eth0
router_id=61
vip=192.168.48.200/24

在这里插入图片描述

编写keepalived.conf.j2模板文件,用变量替换所有差异化配置项:

global_defs {
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_instance VI_1 {
    state {{ state }}
    interface {{ interface }}
    virtual_router_id {{ router_id }}
    priority {{ pri }}
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        {{ vip }} dev {{ interface }}
    }
}

在这里插入图片描述

编写keepalived.yml Playbook,完成软件安装、配置下发、服务启动全流程,配置变更时自动触发服务重启,最后在本地验证VIP连通性:

- hosts: hacluster
  become: yes
  tasks:
    - name: Install keepalived
      ansible.builtin.yum:
        name: keepalived
        state: present

    - name: Deploy keepalived config template
      ansible.builtin.template:
        src: keepalived.conf.j2
        dest: /etc/keepalived/keepalived.conf
      notify: restart keepalived service

    - name: Start & enable keepalived
      ansible.builtin.service:
        name: keepalived
        state: started
        enabled: yes

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

- hosts: localhost
  gather_facts: false
  become: false
  tasks:
    - name: Test ping virtual VIP
      ansible.builtin.command: ping -c 3 192.168.48.200
      register: ping_res

    - debug:
        var: ping_res.stdout

在这里插入图片描述

执行完成后,在server1上通过ip a查看网卡信息,可以看到虚拟IP 192.168.48.200已经成功绑定在eth0网卡上,本地ping VIP也能正常通。

在这里插入图片描述
在这里插入图片描述

4.2 批量用户管理:loop循环迭代

需要批量创建多个用户时,不需要重复编写user任务,使用loop循环迭代用户列表即可,配合password_hash过滤器可以对密码进行SHA512加密。

编写user.yml

- hosts: db
  become: yes
  tasks:
    - ansible.builtin.user:
        name: "{{ item.user }}"
        password: "{{ item.pass | password_hash('sha512') }}"
        state: present
        create_home: yes
      loop:
        - {user: user1, pass: pass1}
        - {user: user2, pass: pass2}

在这里插入图片描述

执行时会出现一条弃用警告:Python的crypt模块将在后续版本中移除,推荐安装passlib库。测试环境可以忽略该警告,不影响用户创建结果。
在这里插入图片描述

登录被控端查看用户家目录,两个用户均已正常创建。
在这里插入图片描述

4.3 批量分发hosts文件:模板自动生成映射

集群节点数量较多时,手动维护每台机器的/etc/hosts文件非常繁琐。通过Jinja2模板结合Ansible的groups魔法变量,可以自动生成所有节点的主机名映射,批量下发到所有机器。

基础版hosts.j2模板,固定写入所有节点映射:

127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1       localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.48.161 server1
192.168.48.162 server2
192.168.48.171 node1

在这里插入图片描述

编写deploy_hosts.yml,将模板批量分发到所有节点:

- hosts: all
  become: yes
  tasks:
    - ansible.builtin.template:
        src: hosts.j2
        dest: /etc/hosts
        owner: root
        group: root
        mode: '0644'

在这里插入图片描述
在这里插入图片描述

后续优化了模板写法,通过for循环遍历指定主机组,自动生成后端节点映射,新增节点时只需修改inventory,无需改动模板:

127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1       localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.48.161 server1
192.168.48.162 server2
192.168.48.171 node1
{% for ip in groups['backend_web'] %}
{{ ip }} web{{ loop.index }}
{% endfor %}

在这里插入图片描述

4.4 综合实战:Haproxy+Keepalived负载均衡集群

最后做了一个综合实战案例:两台节点部署Haproxy+Keepalived实现负载层高可用,后端对接Apache服务,整套架构全部通过Ansible自动化部署。

先更新inventory最终分组:

[backend_web]
192.168.48.162

[hacluster]
192.168.48.161 state=MASTER pri=100
192.168.48.171 state=BACKUP pri=80

[hacluster:vars]
interface=eth0
router_id=61
vip=192.168.48.200/24
vip_ip=192.168.48.200

[all:children]
hacluster
backend_web

在这里插入图片描述

Haproxy配置模板同样使用for循环遍历backend_web组,自动生成后端服务器列表,扩容时只需在inventory中加IP,无需修改配置模板:

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    {% for srv in groups['backend_web'] %}
    server web{{ loop.index }} {{ srv }}:80 check inter 2000 fall 3 rise 2
    {% endfor %}

listen stats
    bind *:8088
    stats enable
    stats uri /stats
    stats auth admin:123456

在这里插入图片描述

完整的Playbook分为三个逻辑段:先批量更新所有节点的hosts解析,再部署Haproxy+Keepalived高可用负载层,最后部署后端Web服务。执行完成后所有节点状态正常,通过VIP即可访问后端Apache服务,Haproxy状态页也能正常打开。

在这里插入图片描述
在这里插入图片描述


踩坑汇总与实操总结

核心踩坑点汇总

  1. 密码复杂度校验报错:设置用户密码时长度过短、包含用户名都会触发PAM的密码复杂度警告,测试环境可忽略提示重复输入,生产环境建议遵循密码规范。
  2. sudo配置格式错误:visudo中单词拼写错误、缩进异常都会导致sudo命令失效,修改后建议保留一个root终端验证,避免普通用户无法提权。
  3. YAML缩进语法错误:YAML对空格缩进要求极其严格,禁止使用Tab键,层级必须对齐。初期handlers和tasks层级错位,导致反复报语法错误。
  4. handlers触发条件限制:只有任务状态为changed时才会触发notify,若配置已符合预期,任务为ok状态则handler不会执行,修改配置时需留意该特性。
  5. 密码哈希弃用警告password_hash默认依赖的Python crypt模块在高版本Python中已弃用,生产环境建议安装passlib库以保证兼容性。
  6. 模板变量未定义:Jinja2模板中引用的变量必须在inventory或Playbook中提前定义,否则渲染时会直接报变量未定义错误。

实操心得

完整走下来最大的感受是,Ansible的核心价值在于幂等性模板化:同一份脚本重复执行不会产生副作用,配置通过变量+模板解耦后,扩容节点只需要修改主机清单,部署脚本完全不用改动,批量运维的效率提升非常明显。

从单模块Ad-hoc命令,到Playbook任务编排,再到Jinja2模板、分组管理,一步步进阶下来,自动化运维的思路也越来越清晰。后续还可以继续探索Roles角色化管理,把不同服务的部署拆分成独立角色,代码复用性和可维护性会更高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值