当前位置: 首页 > news >正文

【Ansible自动化运维实战:从Playbook到负载均衡指南】

本文是「Vagrant+VirtualBox虚拟化环境搭建」的续篇,深入探索Ansible在自动化运维中的核心应用:

✅ Ansible核心技能:Playbook编写、角色(Roles)模块化、标签(Tags)精准控制

✅ 实战场景覆盖:Apache服务部署、HAProxy负载均衡配置、服务器联动管理

✅ 无缝衔接Vagrant:在虚拟化环境中模拟真实运维场景,提供完整可复现的代码示例

无论是想快速入门Ansible的新手,还是需要优化现有流水线的工程师,都能从本文获得从基础的自动化运维解决方案。文末附常见问题排查与性能对比数据,助你避坑提速!
参考教程来自:leucos/ansible-tuto: Ansible 教程

码云:git clone https://gitee.com/xbd_zc/ansible-tuto-demosite.git

前期准备(虚拟机安装及配置)【Vagrant+VirtualBox创建自动化虚拟环境】Ansible-Playbook-CSDN博客

这里准备4台,host0-3,host0:安装Ansible管理host1-3,如通过vagrant创建,Vagrantfile配置如下:

# -*- mode: ruby -*-
# vi: set ft=ruby :hosts = {"host0" => "192.168.0.220","host1" => "192.168.0.221","host2" => "192.168.0.222","host3" => "192.168.0.223"
}Vagrant.configure("2") do |config|hosts.each do |name, ip|config.vm.define name do |machine|machine.vm.box = "bento/ubuntu-20.04"machine.vm.box_version = "202407.23.0"machine.vm.hostname = "%s" % namemachine.vm.network :public_network,bridge: "en1", ip: ipmachine.vm.provider "virtualbox" do |v|v.name = namev.customize ["modifyvm", :id, "--memory", 1024]endendend
end

1.Ansible-测试准备

准备四台服务器,并在本机安装Ansible,一个hosts文件如下所示:

环境:本机host0(192.168.0.220),系统版本Ubuntu 20.04.6 LTS,vagrant镜像:bento/ubuntu-20.04、版本:202407.23.0,私钥配置好

host1 ansible_host=192.168.0.221 ansible_user=root
host2 ansible_host=192.168.0.222 ansible_user=root
host3 ansible_host=192.168.0.223 ansible_user=root

ansible_host是一个特殊变量,用于设置 ansible 将在 尝试连接到此主机。

ansible_user是另一个特殊变量,它告诉 ansible 使用 SSH 时以此用户身份连接。默认情况下,ansible 将使用 当前用户名,或使用 ~/.ansible.cfg 中提供的其他默认值 ().`remote_user

检查主机是否正常工作

ansible -m ping all -i hosts
  • -m MODULE_NAME,–module-name MODULE_NAME (模块名称)
  • -i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY (文件清单)

ansible 执行ping

输出应如下所示:

host1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"
}
host3 | SUCCESS => { "ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"
}
host2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"ping": "pong"
}

好!所有 3 个主机都处于活动状态,并且 ansible 可以与它们通信。

2.Ansible-节点通信

在主机上执行shell命令

ansible -i hosts -m shell -a 'uname -a' host1
  • -a MODULE_ARGS, --args MODULE_ARGS 模块参数

输出应如下所示:

host1 | CHANGED | rc=0 >>
Linux host1 5.4.0-189-generic #209-Ubuntu SMP Fri Jun 7 14:05:13 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
复制文件

复制本机hosts文件到host1上。

ansible -i step-02/hosts -m copy -a 'src=/etc/hosts dest=/tmp/' host1

输出应类似于:

host1 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": true,"checksum": "2e2bd6a4e52b3f3a709a96214cc308753be3a7d4","dest": "/tmp/hosts","gid": 0,"group": "root","md5sum": "d7c7e7ffc3d982c56de6801bb665ba9b","mode": "0644","owner": "root","size": 244,"src": "/root/.ansible/tmp/ansible-tmp-1745964825.1255076-56136-181611889373774/source","state": "file","uid": 0
}#host1主机上查看
vagrant@host1:~$ cat /tmp/hosts
127.0.0.1 localhost
127.0.1.1 vagrant# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.2.1 host0 host0
shell 模块 – 在目标主机上执行 shell 命令

获取在节点上部署了哪个 Ubuntu 版本

ansible -i hosts -m shell -a 'grep DISTRIB_RELEASE /etc/lsb-release' all

all是一个快捷方式,表示“在清单文件中找到的所有主机”。它会 返回:

host2 | CHANGED | rc=0 >>
DISTRIB_RELEASE=20.04
host1 | CHANGED | rc=0 >>
DISTRIB_RELEASE=20.04
host3 | CHANGED | rc=0 >>
DISTRIB_RELEASE=20.04
setup module –收集有关远程主机的信息

如果我们 需要更多信息(IP 地址、RAM 大小等)可以使用此模块

ansible -i hosts -m setup host1

回复包含大量信息:

host1 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["10.0.2.15","192.168.0.221"],    .......................................#还有很多"module_setup": true},"changed": false
}

可以筛选返回的信息,以防您正在寻找 一些特定的东西。

例如想知道您的所有节点的总内存

ansible -i hosts -m setup -a 'filter=ansible_memtotal_mb' all

输出如下:

host1 | SUCCESS => {"ansible_facts": {"ansible_memtotal_mb": 964,"discovered_interpreter_python": "/usr/bin/python3"},"changed": false
}
host2 | SUCCESS => {"ansible_facts": {"ansible_memtotal_mb": 964,"discovered_interpreter_python": "/usr/bin/python3"},"changed": false
}
host3 | SUCCESS => {"ansible_facts": {"ansible_memtotal_mb": 964,"discovered_interpreter_python": "/usr/bin/python3"},"changed": false
}

请注意,与上一个输出相比,主机的回复顺序不同。 这是因为 ansible 与主机并行通信!

顺便说一句,当使用 setup 模块时,你可以在表达式中使用。 它的作用就像一个 shell glob。*``filter=

选择主机

我们看到这表示 “所有主机”,但 ansible 提供了很多方式 — Ansible 社区文档:all

  • host0:host1将在 host0 上运行,并且 主机 1
  • host*将在以 ‘host’ 开头和结尾的所有主机上运行 与 ‘’ (就像 shell glob 一样)

常见的事实变量示例

  1. 操作系统信息‌:
    • ansible_os_family:操作系统的家族,如Debian、RedHat等。
    • ansible_distribution:操作系统的分发版名称。
    • ansible_distribution_version:操作系统的版本号。
    • ansible_distribution_major_version:操作系统的主版本号。
  2. 硬件信息‌:
    • ansible_processor_vcpus:处理器的虚拟CPU数量。
    • ansible_memory_mb:总内存大小(以MB为单位)。
    • ansible_disk_usage:磁盘使用情况。
  3. 网络配置‌:
    • ansible_default_ipv4:默认的IPv4地址。
    • ansible_all_ipv4_addresses:所有的IPv4地址。
    • ansible_hostname:主机名。
获取事实变量的方法

使用setup模块‌:

  • 在playbook中,可以通过setup模块获取所有事实变量。例如:

    #显示所有主机的事实,并将其按“主机名”索引存储在“/tmp/facts”中。
    # ansible all -m ansible.builtin.setup --tree /tmp/facts
    #仅显示有关ansible在所有主机上找到的内存的事实,并将其输出。
    # ansible all -m ansible.builtin.setup -a 'filter=ansible_*_mb'
    #仅显示facter返回的事实。
    #ansible all -m ansible.builtin.setup -a 'filter=facter_*'
    #只收集事实者返回的事实。
    #ansible all -m ansible.builtin.setup -a 'gather_subset=!all,facter'
    - name: Collect only facts returned by facteransible.builtin.setup:gather_subset:- '!all'- '!<any valid subset>'- facter
    
  • 使用filter参数来查看指定的信息,例如:

    - name: Filter and return only selected factsansible.builtin.setup:filter:- 'ansible_distribution'- 'ansible_machine_id'- 'ansible_*_mb'
    # 仅显示有关某些接口的事实。
    # ansible all -m ansible.builtin.setup -a 'filter=ansible_eth[0-2]'
    #将额外收集的事实限制为网络和虚拟(包括默认的最小事实)
    # ansible all -m ansible.builtin.setup -a 'gather_subset=network,virtual'
    #仅收集网络和虚拟(不包括默认的最小事实)
    # ansible all -m ansible.builtin.setup -a 'gather_subset=!all,network,virtual'
    #即使在场,也不要叫木偶师或欧海。
    # ansible all -m ansible.builtin.setup -a 'gather_subset=!facter,!ohai'
    #仅收集默认的最低事实数量:
    # ansible all -m ansible.builtin.setup -a 'gather_subset=!all'
    #不收集任何事实,即使是默认的最小事实子集:
    # ansible all -m ansible.builtin.setup -a 'gather_subset=!all,!min'
    #使用存储在C:\custom_fact中的自定义事实显示来自Windows主机的事实。
    # ansible windows -m ansible.builtin.setup -a "fact_path='c:\custom_facts'"
    #收集dbservers组中机器的事实(也称为委派事实)
    - hosts: app_serverstasks:- name: Gather facts from db serversansible.builtin.setup:delegate_to: "{{ item }}"delegate_facts: trueloop: "{{ groups['dbservers'] }}"
    

3.Ansible主机分组与变量管理

为什么需要主机分组与变量管理?

在 Ansible 自动化运维中,主机分组变量管理是提升效率的核心手段:

  • 批量操作:通过逻辑分组快速执行命令(如更新所有 Web 服务器)。
  • 环境隔离:为开发、测试、生产环境分配独立配置。
  • 配置复用:通过变量动态适配不同主机的参数。

主机分组:灵活管理多节点
1. 基础分组

inventory 文件中定义主机组,支持数字范围通配符

[web]
web-[1:3].example.com  # 匹配 web-1.example.com 至 web-3.example.com[db]
db-*.example.com       # 匹配所有以 db- 开头的域名
2. 嵌套子组

通过 :children 定义层级关系,实现复杂环境管理:

[ubuntu]
host1 ansible_host=192.168.1.101[debian]
host2 ansible_host=192.168.1.102[linux:children]  # 父组聚合子组
ubuntu
debian[prod:children]   # 按环境划分
linux
3. 分组实战场景
  • 多环境管理

    [dev]
    dev-web1 ansible_host=10.0.0.101[prod]
    prod-web1 ansible_host=192.168.1.101[all:children]  # 全局聚合
    dev
    prod
    
  • 角色划分

    [nginx]
    lb1 ansible_host=192.168.1.200[tomcat]
    app[1:3] ansible_host=192.168.1.[201:203]
    

变量管理:动态配置
1. 变量定义位置
位置优先级适用场景
命令行 (-e)最高临时覆盖变量
主机变量文件主机专属配置(如 IP、端口)
组变量文件共享配置(如软件版本)
库存文件基础连接参数(如 ansible_port
2. 变量定义示例
  • 库存文件直接定义

    [web]
    web1 ansible_host=192.168.1.101 ansible_port=2222  # 指定 SSH 端口
    
  • 主机变量文件 (host_vars/web1.yml):

    ---
    http_port: 8080
    max_connections: 1000
    
  • 组变量文件 (group_vars/web.yml):

    ---
    nginx_version: 1.25.2
    app_env: production
    
3. 变量继承与覆盖
  • 继承逻辑:子组继承父组变量,同名变量优先级为 子组 > 父组
  • 调试技巧:使用 ansible-inventory --graph 查看主机归属与变量来源。

4.”Hello World“apache

Ansible playbook 是 Ansible 的基本组件之一,因为它们记录和执行 Ansible 的配置。通常,playbook 是自动执行要在远程计算机上执行的一组任务的主要方式。

它们通过收集所有必要的资源来编排有序流程或避免重复手动作,从而帮助我们实现自动化。Playbook 可以在人员之间重复使用和共享,它们被设计为对人类友好且易于在 YAML 中编写。

使用Ansible Playbooks - 带有示例的提示和技巧

sed -i ‘s/33.11/0.221/’ step-04/hosts

准备清单文件,命名为:hosts

[web]
host2 ansible_host=192.168.0.222 ansible_user=root

构建一个 playbook,它将在组中的计算机上安装 apache。web

- hosts: webtasks:- name: Installs apache web serverapt:pkg: apache2state: presentupdate_cache: true
  • -name 为此任务添加了一个名称。虽然这不是必需的,但它可以在 playbook 运行时提供信息
  • apt 模块,它可以 安装 Debian 软件包

运行 playbook :apache.yml

ansible-playbook -i step-04/hosts -l host2 step-04/apache.yml
  • -l SUBSET, --limit SUBSET (限制分组)

运行后可看到以下内容:

PLAY [web] ************TASK [Gathering Facts] ***********
ok: [host2]TASK [Installs apache web server] *******************
changed: [host2]PLAY RECAP *************
host2                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • PLAY [web] *** 表示开始执行一个针对主机组 ‌**web**‌ 的 play。
    • [web]:对应 playbook 中定义的 hosts: web,目标主机组来自 Ansible 清单文件(如 inventory 文件)。
  • TASK [Gathering Facts] 用户自定义的任务名称,这里表示安装 Apache Web 服务器。
  • changed: [host2]:表示此任务在主机 host2 上执行成功并引发了系统变更(例如安装了新软件包)。
  • TASK [Installs apache web server] 用户自定义的任务名称,这里表示安装 Apache Web 服务器。
    • changed: [host2]:表示此任务在主机 host2 上执行成功并引发了系统变更(例如安装了新软件包)。
  • PLAY RECAP 整个 play 的执行结果汇总。
    • **ok=2**‌:2 个任务执行成功且无变更(如 Gathering Facts)。
    • ‌**changed=1**‌:1 个任务引发了变更(如安装 Apache)。
    • 其他字段(如 failed=0)表示无失败、无跳过、无重试等。

现在再次运行一次,看看会发生什么:

ansible-playbook -i step-04/hosts -l host2 step-04/apache.ymlPLAY [web] *********TASK [Gathering Facts] ********
ok: [host2]TASK [Installs apache web server] *****
ok: [host2]PLAY RECAP *******
host2                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

‘changed=0’ 。这是绝对正常的,也是核心功能之一 的 Ansible:playbook 仅在有事可做时才会起作用。这称为幂等性

5.优化apache设置

我们已经安装了 apache,现在让我们设置我们的 virtualhost。

在 Apache 配置场景中,virtualhost(虚拟主机)用于在同一台服务器上托管多个网站或应用。通过不同的域名、IP 或端口,Apache 可区分请求并返回对应的网站内容。在 Ansible Playbook 中,配置 virtualhost 通常涉及以下操作:

  • 创建虚拟主机配置文件(如 .conf 文件);
  • 指定域名、文档根目录、日志路径等参数;
  • 启用配置并重启 Apache 服务。

我们的服务器上只需要一个 virtualhost,但我们想替换默认的 一个具有更具体的东西。因此,我们必须删除当前的 (大概是)VirtualHost,发送我们的 VirtualHost,激活它并 重新启动 Apache。

添加我们的 virtualhost 配置 给 Host1,路径为:files/awesome-app

<VirtualHost *:80>DocumentRoot /var/www/awesome-appOptions -IndexesErrorLog /var/log/apache2/error.logTransferLog /var/log/apache2/access.log
</VirtualHost>
指令作用
<VirtualHost *:80>监听所有 IP 的 80 端口,处理对该虚拟主机的请求。
DocumentRoot网站根目录为 /var/www/awesome-app,存放网站文件(需确保目录存在)。
Options -Indexes禁止目录浏览(若目录中没有 index.html,用户无法查看文件列表)。
ErrorLog错误日志路径为 /var/log/apache2/error.log
TransferLog访问日志路径为 /var/log/apache2/access.log

hosts

[web]
host1 ansible_host=192.168.0.221 ansible_user=root

更新 apache playbook

---
- hosts: web  #指定该 Play 针对清单文件(inventory)中定义的 web 主机组执行。tasks:- name: Installs apache web server
#模块:apt(适用于 Debian/Ubuntu 系统)。apt:
#安装 Apache2 软件包。pkg: apache2
#确保 Apache2 已安装。state: presentupdate_cache: true
#等同于执行 apt update,更新软件包缓存(确保安装最新版本)。- name: Push default virtual host configurationcopy:src: files/awesome-appdest: /etc/apache2/sites-available/awesome-app.conf
#将本地 files/awesome-app 文件复制到目标主机的 /etc/apache2/sites-available/awesome-app.conf。mode: 0640
#设置文件权限为 -rw-r-----(所有者可读写,所属组可读,其他用户无权限)。- name: Activates our virtualhostfile:src: /etc/apache2/sites-available/awesome-app.confdest: /etc/apache2/sites-enabled/awesome-app.confstate: link
#在 sites-enabled 目录创建符号链接(state: link),指向 sites-available/awesome-app.conf。Apache 会加载 sites-enabled 中的配置文件,这一步等同于运行 a2ensite awesome-app.conf。        notify:- restart apache
#如果任务状态为 changed,触发 restart apache 处理器。- name: Disable the default virtualhostfile:dest: /etc/apache2/sites-enabled/000-default.confstate: absentnotify:- restart apache- name: Disable the default ssl virtualhost
#这两步禁用默认虚拟主机,防止默认虚拟主机与新配置冲突。    file:dest: /etc/apache2/sites-enabled/default-ssl.confstate: absent
#state: absent:删除 sites-enabled 目录中的默认配置文件符号链接(等同于 a2dissite 000-default.conf 和 a2dissite default-ssl.conf)。notify:- restart apachehandlers:- name: restart apache
#处理器(Handlers),当任务触发 notify: restart apache 时,重启 Apache 服务(仅在相关配置文件发生变更时触发)。service:name: apache2state: restarted

执行输出:

PLAY [web] ********* TASK [Gathering Facts] ******** 
ok: [host1]TASK [Installs apache web server] *******
ok: [host1]TASK [Push default virtual host configuration] ****c
changed: [host1]TASK [Activates our virtualhost] ****** c
changed: [host1]TASK [Disable the default virtualhost] ******
changed: [host1]TASK [Disable the default ssl virtualhost] ******
ok: [host1]RUNNING HANDLER [restart apache] *******
changed: [host1]PLAY RECAP *******
host1                      : ok=7    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

在重新启动 apache 之前,我们难道不应该检查一下配置是否正常吗?如果我们的配置文件不正确,则不会中断服务。

6.在配置正确时重新启动

我们已经安装了 apache,推送了我们的 virtualhost 并重新启动了服务器。但 如果我们希望 playbook 仅在配置为 正确?让我们这样做。

Ansible 有一个漂亮的功能:如果出现问题,它将停止所有处理 错。如果配置 文件无效。

让我们更改我们的虚拟主机配置文件搞破坏DocumentDoot=》RocumentDoot:awesome-app

<VirtualHost *:80>RocumentDoot /var/www/awesome-appOptions -IndexesErrorLog /var/log/apache2/error.logTransferLog /var/log/apache2/access.log
</VirtualHost>

如前所述,当任务失败时,处理将停止。因此,我们将确保 配置在重新启动服务器之前有效。我们还首先添加 我们的 VirtualHost 在删除默认 VirtualHost 之前,因此后续的重启 (可能直接在服务器上完成) 不会破坏 Apache。

---
- hosts: web  # 🌐 指定目标主机组为 `web`(需在 Ansible 清单中定义)tasks:# 🔧 任务1:安装 Apache- name: Installs apache web serverapt:  # 📦 使用 apt 模块(Debian/Ubuntu 系统)pkg: apache2  # 软件包名称state: present  # 确保 apache2 已安装update_cache: true  # 先执行 `apt update` 更新软件源缓存# 📄 任务2:推送虚拟主机配置文件- name: Push future default virtual host configurationcopy:  # 📂 使用 copy 模块复制文件src: files/awesome-app  # 本地文件路径(相对于 Playbook)dest: /etc/apache2/sites-available/awesome-app.conf  # 目标路径mode: 0640  # 设置文件权限为 -rw-r-----# 🔗 任务3:启用自定义虚拟主机- name: Activates our virtualhostcommand: a2ensite awesome-app  # 🖥️ 直接调用 Apache 命令启用站点# 等同于创建符号链接:/etc/apache2/sites-enabled/awesome-app.conf -> ../sites-available/awesome-app.conf# ✅ 任务4:验证 Apache 配置语法- name: Check that our config is validcommand: apache2ctl configtest  # 🔍 检查配置是否有语法错误# 如果配置错误,任务会失败并终止 Playbook 执行# 🚫 任务5:禁用默认虚拟主机(非 SSL)- name: Deactivates the default virtualhostcommand: a2dissite 000-default  # ❌ 禁用默认站点# 🚫 任务6:禁用默认 SSL 虚拟主机- name: Deactivates the default ssl virtualhostcommand: a2dissite default-ssl  # ❌ 禁用默认 SSL 站点notify:  # 🔔 触发处理器- restart apache  # 仅当此任务状态为 changed 时重启 Apachehandlers:  # ⚙️ 处理器(异步任务)# 🔄 处理器:重启 Apache 服务- name: restart apacheservice:  # 🛠️ 使用 service 模块name: apache2  # 服务名称state: restarted  # 重启服务

执行

ansible-playbook -i step-06/hosts -l host1 step-06/apache.ymlPLAY [web] ******** TASK [Gathering Facts] ******** 
ok: [host1]TASK [Installs apache web server] *********
ok: [host1]TASK [Push future default virtual host configuration] *******
changed: [host1]TASK [Activates our virtualhost] ***** 
changed: [host1]TASK [Check that our config is valid] *********
fatal: [host1]: FAILED! => {"changed": true, "cmd": ["apache2ctl", "configtest"], "delta": "0:00:00.020643", "end": "2025-05-01 06:30:47.032996", "msg": "non-zero return code", "rc": 1, "start": "2025-05-01 06:30:47.012353", "stderr": "AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/awesome-app.conf:\nInvalid command 'RocumentDoot', perhaps misspelled or defined by a module not included in the server configuration", "stderr_lines": ["AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/awesome-app.conf:", "Invalid command 'RocumentDoot', perhaps misspelled or defined by a module not included in the server configuration"], "stdout": "Action 'configtest' failed.\nThe Apache error log may have more information.", "stdout_lines": ["Action 'configtest' failed.", "The Apache error log may have more information."]}PLAY RECAP *********
host1                      : ok=4    changed=2    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

正如您所见,由于 apache2ctl 在失败时会以退出代码 1 返回,所以 Ansible 会意识到这一点并停止处理。太棒了!

嗯,实际上情况不太好……我们的虚拟主机还是添加上了。不过,后续任何一次重启 Apache 都会报错我们的配置有问题并退出。所以我们需要一种方法来捕获失败并回退。

从错误信息可以明确看出 Apache 配置文件的语法错误直接导致 Playbook 执行失败

Invalid command 'RocumentDoot'
#重启测试
ansible -i step-06/hosts -m service -a 'name=apache2 state=restarted' host1
host1 | FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": false,"msg": "Unable to restart service apache2: Job for apache2.service failed because the control process exited with error code.\nSee \"systemctl status apache2.service\" and \"journalctl -xe\" for details.\n"
}

7.使用条件语句

我们已经安装了 Apache,推送了虚拟主机并重启了服务器。 但如果出现问题,我们希望将一切恢复到稳定状态。

出现问题时回退

当任务失败时,处理就会停止……除非我们接受失败(而且我们应该接受)。这就是我们要做的: 如果出现失败,继续处理,但仅限于撤销我们已做的操作。

- hosts: web  # 仅在清单中定义的 'web' 主机组执行tasks:# 安装 Apache- name: Installs apache web serverapt:pkg: apache2        # 安装 Apache2 软件包state: present      # 确保软件包存在update_cache: true  # 先执行 `apt update` 更新缓存# 推送配置文件(潜在风险点)- name: Push future default virtual host configurationcopy:src: files/awesome-app  # 本地配置文件名dest: /etc/apache2/sites-available/awesome-app.conf  # 目标路径mode: 0640  # 权限设置(可能需确保 Apache 用户可读)# 启用新虚拟主机(非幂等操作)- name: Activates our virtualhostcommand: a2ensite awesome-app  # ? 重复执行会报错 "Site already enabled"args:creates: /etc/apache2/sites-enabled/awesome-app.conf  #幂等性控制,添加creates参数确保任务仅在符号链接不存在时执行,避免重复报错。notify: restart apache  # 触发重启# 验证配置语法(关键安全阀)- name: Check that our config is validcommand: apache2ctl configtestregister: result        # 存储命令执行结果ignore_errors: true     # 即使验证失败也继续执行(触发回滚)# 回滚操作1:重新启用默认站点(需确保默认配置存在)- name: Rolling back - Restoring old default virtualhostcommand: a2ensite 000-defaultwhen: result is failed  # 仅在配置验证失败时执行# 回滚操作2:禁用新站点- name: Rolling back - Removing our virtualhostcommand: a2dissite awesome-appwhen: result is failed# 强制终止 Playbook 并提示错误- name: Rolling back - Ending playbookfail:msg: "Configuration file is not valid. Please check that before re-running the playbook."when: result is failed  # 终止后续任务# 禁用默认站点(仅在配置验证成功后执行)- name: Deactivates the default virtualhostcommand: a2dissite 000-defaultnotify: restart apache  # 统一服务重启触发,任何修改站点配置的操作都需要重启 Apache,否则变更不会生效。# 禁用默认 SSL 站点- name: Deactivates the default ssl virtualhostcommand: a2dissite default-sslnotify:- restart apache  # 触发服务重启(但其他任务也需要类似设置)handlers:# 服务重启处理器- name: restart apacheservice:name: apache2state: restarted

关键字 register 会记录 apache2ctl configtest 命令的输出(退出状态、标准输出、标准错误……),并且 when: result is failed 会检查已注册的变量( result )是否包含失败状态。

ansible-playbook -i step-07/hosts -l host1 step-07/apache.ymlPLAY [web] ********TASK [Gathering Facts] *************
ok: [host1]TASK [Installs apache web server] ********
ok: [host1]TASK [Push future default virtual host configuration] *********
ok: [host1]TASK [Activates our virtualhost] ************
ok: [host1]TASK [Check that our config is valid] *************
fatal: [host1]: FAILED! => {"changed": true, "cmd": ["apache2ctl", "configtest"], "delta": "0:00:00.022459", "end": "2025-05-01 07:10:28.993221", "msg": "non-zero return code", "rc": 1, "start": "2025-05-01 07:10:28.970762", "stderr": "AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/awesome-app.conf:\nInvalid command 'RocumentDoot', perhaps misspelled or defined by a module not included in the server configuration", "stderr_lines": ["AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/awesome-app.conf:", "Invalid command 'RocumentDoot', perhaps misspelled or defined by a module not included in the server configuration"], "stdout": "Action 'configtest' failed.\nThe Apache error log may have more information.", "stdout_lines": ["Action 'configtest' failed.", "The Apache error log may have more information."]}
...ignoringTASK [Rolling back - Restoring old default virtualhost] ******************
changed: [host1]TASK [Rolling back - Removing our virtualhost] ****************
changed: [host1]TASK [Rolling back - Ending playbook] **************
fatal: [host1]: FAILED! => {"changed": false, "msg": "Configuration file is not valid. Please check that before re-running the playbook."}PLAY RECAP ************
host1                      : ok=7    changed=3    unreachable=0    failed=1    skipped=0    rescued=0    ignored=1

看起来如预期般奏效了。让我们试着重启 Apache 看看是否真的成功了:

ansible -i step-07/hosts -m service -a 'name=apache2 state=restarted' host1
host1 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"},"changed": true,"name": "apache2","state": "started","status": {

能正常启动,现在我们的 Apache 服务器已免受错误配置的影响了。

8.从 Git 部署网站

我们已经安装了 Apache,推送了虚拟主机并安全地重启了服务器。 现在我们将使用 Git 模块来部署我们的应用程序。

git模块

我们的虚拟主机已设置好,但还需要做一些改动才能完成部署。 首先,我们要部署一个 PHP 应用程序,所以需要安装 libapache2-mod-php 包。其次,由于用于克隆我们应用程序的 Git 仓库的 Git 模块需要用到它,所以我们还得安装 git

Ansible 可以遍历一系列项目,并像这样在操作中使用每个项目:

...
- name: Installs necessary packagesapt:pkg: "{{ item }}"state: latestupdate_cache: truewith_items:- apache2- libapache2-mod-php- git
...

注意:自 2.7 版本起,在模块中使用循环仅调用一次的做法已弃用。您可以按照下表所示指定包的列表:

Option A 选择一个Option B 选项B
- name: "Install foo & bar" apt: pkg: ["foo", "bar"] - name: "Install foo & bar" apt: pkg: - foo - bar

最终方案:

- hosts: webtasks:- name: ;**安装必要软件包**;(包括Apache, PHP模块, Git)apt:pkg: ["apache2", "libapache2-mod-php", "git"]state: latestupdate_cache: true- name: ;**推送默认的虚拟主机配置文件**;copy:src: files/awesome-appdest: /etc/apache2/sites-available/awesome-app.confmode: 0640- name: ;**激活我们的虚拟主机**;command: a2ensite awesome-app- name: ;**检查配置是否有效**;command: apache2ctl configtestregister: resultignore_errors: true- name: ;**回滚 - 恢复旧的默认虚拟主机**;(如果配置测试失败)command: a2ensite 000-defaultwhen: result is failed- name: ;**回滚 - 移除我们的虚拟主机**;(如果配置测试失败)command: a2dissite awesome-appwhen: result is failed- name: ;**回滚 - 结束Playbook**;(如果配置测试失败)fail:msg: "配置文件无效。请在重新运行Playbook之前检查。"when: result is failed#如果执行git模块出现Failed to connect to github.com port 443: Connection refused,Ansible 的 git 模块可能因旧仓库状态导致冲突。强制清理目标目录#- name: 清理旧目录#  file:#    path: /var/www/awesome-app#    state: absent#  ignore_errors: yes  # 防止目录不存在时报错#  tags: deploy- name: ;**部署我们的应用程序**;(使用Git)git:repo: 'https://github.com/leucos/ansible-tuto-demosite.git'dest: /var/www/awesome-appforce: yes       # 强制覆盖本地修改update: yes  # 确保拉取最新代码version: master  # 明确指定分支tags: deploy  # 可通过`ansible-playbook ... --tags deploy`单独执行此任务- name: ;**禁用默认的虚拟主机**;(应在部署后禁用,确保不会冲突)command: a2dissite 000-defaultnotify: restart apache  # 统一服务重启触发,任何修改站点配置的操作都需要重启 Apache,否则变更不会生效。- name: ;**禁用默认的SSL虚拟主机**;(如果不需要SSL,可考虑移除或注释此行)command: a2dissite default-sslnotify:- restart apachehandlers:- name: restart apacheservice:name: apache2state: restarted

启动

ansible-playbook -i step-08/hosts -l host1 step-08/apache.ymlPLAY [web] ************TASK [Gathering Facts] ***********
ok: [host1]TASK [;**安装必要软件包**;(包括Apache, PHP模块, Git)] **********
ok: [host1]TASK [;**推送默认的虚拟主机配置文件**;] ***********
ok: [host1]TASK [;**激活我们的虚拟主机**;] *************
changed: [host1]TASK [;**检查配置是否有效**;] ************* 
changed: [host1]TASK [;**回滚 - 恢复旧的默认虚拟主机**;(如果配置测试失败)] ***************
skipping: [host1]TASK [;**回滚 - 移除我们的虚拟主机**;(如果配置测试失败)] ******************
skipping: [host1]TASK [;**回滚 - 结束Playbook**;(如果配置测试失败)] **************
skipping: [host1]TASK [;**部署我们的应用程序**;(使用Git)] ************
ok: [host1]TASK [;**禁用默认的虚拟主机**;(应在部署后禁用,确保不会冲突)] ****************
changed: [host1]TASK [;**禁用默认的SSL虚拟主机**;(如果不需要SSL,可考虑移除或注释此行)] ************
changed: [host1]RUNNING HANDLER [restart apache] **************
changed: [host1]PLAY RECAP ************
host1                      : ok=9    changed=5    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0

访问测试host1

http://192.168.0.221

Welcome to your awesome app !
App backend host1显示了一张图片以及服务器的主机名

请注意, tags: deploy 这一行允许您仅执行playbook中的某一部分。 假设您为您的网站推送了一个新版本。您希望加快速度,仅执行负责部署的部分。标签允许您这样做。 当然,“deploy”只是一个字符串,它没有任何特定的含义,可以是任何内容。让我们看看如何使用它:

ansible-playbook -i step-08/hosts -l host1 step-08/apache.yml -t deployPLAY [web] **********TASK [Gathering Facts] ***********
ok: [host1]TASK [;**部署我们的应用程序**;(使用Git)] **********
ok: [host1]PLAY RECAP **************
host1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0# 显示所有可用标签
ansible-playbook site.yml --list-tags# 详细日志跟踪
ansible-playbook site.yml -t deploy -vvv
Failed to connect to github.com port 443: Connection refused

如果执行到git模块时报此错:

查看具体执行情况

ansible-playbook -i step-12test/hosts -l web step-12test/site.yml --tags deploy -vvv

先排查防火墙、网络状态等网络情况,并进入对应报错的主机执行git 测试能否下载项目,

#手动查询分支情况
git ls-remote --heads https://github.com/leucos/ansible-tuto-demosite.git

Ansible 的 git 模块可能因旧仓库状态导致冲突。强制清理目标目录,添加模块再测试

- name: 清理旧目录file:path: /var/www/awesome-appstate: absentignore_errors: yes  # 防止目录不存在时报错tags: deploy- name: ;**部署我们的应用程序**;(使用Git)git:repo: 'https://github.com/leucos/ansible-tuto-demosite.git'dest: /var/www/awesome-appforce: yes       # 强制覆盖本地修改update: yes  # 确保拉取最新代码version: master  # 明确指定分支tags: deploy  # 可通过`ansible-playbook ... --tags deploy`单独执行此任务

9.添加另一台 Web 服务器

现在有了一台web服务器(host1),现在再添加一台web服务器和一台负载均衡服务器

更新inventory

sed -i -e ‘s/host0/host3/’ -e ‘s/33/0/’ -e ‘s/11/221/’ -e ‘s/12/222/’ -e ‘s/10/223/’ step-09/hosts

cat step-09/hosts

[web]
host1 ansible_host=192.168.0.221 ansible_user=root
host2 ansible_host=192.168.0.222 ansible_user=root[haproxy]
host3 ansible_host=192.168.0.223 ansible_user=root

在此指定 ansible_host 是因为主机的 IP 地址与预期不同(或者无法解析)。您可以将这些主机添加到 /etc/hosts 中,从而无需担心,或者使用真实的主机名(通常您会采取的做法)。

再建一个web服务器

cp step-08/apache.yml step-09/apache.yml
ansible-playbook -i step-09/hosts step-09/apache.ymlPLAY [web] ************TASK [Gathering Facts] ***************
ok: [host2]
ok: [host1]TASK [;**安装必要软件包**;(包括Apache, PHP模块, Git)] *****************
ok: [host1]
changed: [host2]TASK [;**推送默认的虚拟主机配置文件**;] *************
changed: [host2]
ok: [host1]TASK [;**激活我们的虚拟主机**;] *************
changed: [host2]
changed: [host1]TASK [;**检查配置是否有效**;] ***************
changed: [host1]
changed: [host2]TASK [;**回滚 - 恢复旧的默认虚拟主机**;(如果配置测试失败)] *****************
skipping: [host2]
skipping: [host1]TASK [;**回滚 - 移除我们的虚拟主机**;(如果配置测试失败)] *****************
skipping: [host1]
skipping: [host2]TASK [;**回滚 - 结束Playbook**;(如果配置测试失败)] *************
skipping: [host1]
skipping: [host2]TASK [;**部署我们的应用程序**;(使用Git)] *************
ok: [host1]
changed: [host2]TASK [;**禁用默认的虚拟主机**;(应在部署后禁用,确保不会冲突)] *************
changed: [host1]
changed: [host2]TASK [;**禁用默认的SSL虚拟主机**;(如果不需要SSL,可考虑移除或注释此行)] ************
changed: [host2]
changed: [host1]RUNNING HANDLER [restart apache] ***********
changed: [host2]
changed: [host1]PLAY RECAP *************
host1                      : ok=9    changed=5    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
host2                      : ok=9    changed=8    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0

我们所要做的就是从命令行中移除 -l host1 。请记住, -l 是一个限制在特定主机上运行playbook的开关。现在我们不再限制了,它将在playbook预期运行的所有主机上运行(即 web )。

如果我们有其他服务器在 web 组中,但只想将playbook限制在其中的一个子集上,那么我们可以使用例如: -l firsthost:secondhost:...

10.HAProxy 配置模板

HAProxy 是一个高性能的 ‌负载均衡器‌ 和 ‌反向代理‌,常用于分发流量到多个后端服务器,提升系统可用性和扩展性。核心功能包括:

  1. 流量分发‌:支持轮询(round-robin)、加权轮询、最少连接等算法。
  2. 健康检查‌:自动检测后端服务器状态,移除故障节点。
  3. SSL终止‌:可处理HTTPS请求解密,减轻后端服务器压力。
  4. 高可用性‌:结合Keepalived可实现双机热备。

Ansible 使用 Jinja2,这是 Python 的一个模板引擎。在编写 Jinja2 模板时,您可以使用 Ansible 定义的任何变量。

例如,如果您想要输出当前模板所构建的主机的 inventory_name,您只需在 Jinja 模板中写入 {{ inventory_hostname }} 即可。

或者如果您需要第一个以太网接口的 IP 地址(Ansible 通过 setup 模块已知),您只需在模板中写入: {{ ansible_default_ipv4.address}} (这与 {{ ansible_default_ipv4['address'] }} 等效)。

Jinja2 模板还支持条件语句、for 循环等。

让我们创建一个 templates/ 目录,并在其中创建一个 Jinja 模板。我们将它命名为 haproxy.cfg.j2 。按照惯例,我们使用 .j2 扩展名,以表明这是一个 Jinja2 模板,但这并非必要。

step-10/templates/haproxy.cfg.j2

globaldaemon              # 以守护进程模式运行maxconn 256         # 设置最大并发连接数为256defaultsmode http           # 默认使用HTTP模式timeout connect 5000ms   # 连接超时时间为5秒timeout client 50000ms   # 客户端超时时间为50秒timeout server 50000ms   # 服务器端超时时间为50秒listen clusterbind {{ ansible_host }}:80   # 绑定主机的IP和端口(动态变量来自Ansible)mode http                    # 启用HTTP模式stats enable                 # 开启统计页面balance roundrobin           # 使用轮询负载均衡算法
{% for backend in groups['web'] %}    # 遍历Ansible的web组内所有主机server {{ hostvars[backend]['ansible_hostname'] }} {{ hostvars[backend]['ansible_facts']['default_ipv4']['address'] }} check port 80  # 动态生成后端服务器配置(主机名+IP+健康检查)
{% endfor %}option httpchk HEAD /index.php HTTP/1.0   # 定义HTTP健康检查方法
注意事项!!! {#section1}

检查Ansible事实中的default_ipv4是否是正确的,如果不是需更在haproxy.cfg.j2中改为正确的路径

#我这里的事实中['default_ipv4']对应eth0,目前使用的网卡是eth1(由于vagrant设置的public_network)
ansible -i hosts -m setup host1
host1 | SUCCESS => {
...............................................
"ansible_eth1": {"active": true,"device": "eth1","features": {
..............................................},"hw_timestamp_filters": [],"ipv4": {"address": "192.168.0.221",
#将step-10/templates/haproxy.cfg.j2配置文件中的server改为如下
server {{ hostvars[backend]['ansible_hostname'] }} {{ hostvars[backend].ansible_eth1.ipv4.address }}:80 check port 80
#改完后再执行Ansible-Playbook测试

该模板用于‌动态生成HAProxy负载均衡配置‌:

  1. 动态绑定IP端口‌:通过 {{ ansible_host }} 注入主机IP。
  2. 自动化后端服务器配置‌:基于Ansible的 web 主机组循环生成服务器列表,避免手动维护。
  3. 参数统一管理‌:超时时间、负载均衡策略等集中定义,便于批量修改。

HAProxy playbook

step-10/haproxy.yml

- hosts: webgather_facts: true  # 收集目标服务器的系统信息(如IP、OS版本)- hosts: haproxy      # 针对名为 "haproxy" 的主机组执行以下任务tasks:# 任务1:安装 HAProxy- name: Installs haproxy load balancerapt:pkg: haproxy        # 包名称state: present     # 确保已安装update_cache: yes   # 更新 apt 缓存(相当于 apt update)# 任务2:推送配置文件- name: Pushes configurationtemplate:src: templates/haproxy.cfg.j2  # 使用 Jinja2 模板生成配置文件dest: /etc/haproxy/haproxy.cfg # 目标路径mode: 0640         # 文件权限(root可读写,haproxy组可读)owner: root         # 文件所有者group: root        # 文件所属组notify:- restart haproxy  # 文件变更后触发重启操作# 任务3:确保 HAProxy 开机自启- name: Sets default starting flag to 1lineinfile:dest: /etc/default/haproxy  # 修改系统服务配置文件regexp: "^ENABLED"          # 查找以 ENABLED 开头的行line: "ENABLED=1"           # 强制设置为 1(启用服务)notify:- restart haproxyhandlers:# 定义重启 HAProxy 的处理程序- name: restart haproxyservice:name: haproxystate: restarted

执行:

 ansible-playbook -i step-10/hosts step-10/apache.yml step-10/haproxy.ymlPLAY [web] ******************************************************************************************************************************TASK [Gathering Facts] ******************************************************************************************************************
ok: [host1]
ok: [host2]TASK [Installs necessary packages] ******************************************************************************************************
ok: [host1]
ok: [host2]TASK [Push future default virtual host configuration] ***********************************************************************************
ok: [host2]
ok: [host1]TASK [Activates our virtualhost] ********************************************************************************************************
changed: [host2]
changed: [host1]TASK [Check that our config is valid] ***************************************************************************************************
changed: [host2]
changed: [host1]TASK [Rolling back - Restoring old default virtualhost] *********************************************************************************
skipping: [host1]
skipping: [host2]TASK [Rolling back - Removing out virtualhost] ******************************************************************************************
skipping: [host1]
skipping: [host2]TASK [Rolling back - Ending playbook] ***************************************************************************************************
skipping: [host1]
skipping: [host2]TASK [Deploy our awesome application] ***************************************************************************************************
ok: [host1]
ok: [host2]TASK [Deactivates the default virtualhost] **********************************************************************************************
changed: [host2]
changed: [host1]TASK [Deactivates the default ssl virtualhost] ******************************************************************************************
changed: [host2]
changed: [host1]RUNNING HANDLER [restart apache] ********************************************************************************************************
changed: [host2]
changed: [host1]PLAY RECAP ******************************************************************************************************************************
host1                      : ok=9    changed=5    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
host2                      : ok=9    changed=5    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0PLAY [web] ******************************************************************************************************************************TASK [Gathering Facts] ******************************************************************************************************************
ok: [host2]
ok: [host1]PLAY [haproxy] **************************************************************************************************************************TASK [Gathering Facts] ******************************************************************************************************************
ok: [host3]TASK [Installs haproxy load balancer] ***************************************************************************************************
changed: [host3]TASK [Pushes configuration] *************************************************************************************************************
changed: [host3]TASK [Sets default starting flag to 1] **************************************************************************************************
changed: [host3]RUNNING HANDLER [restart haproxy] *******************************************************************************************************
changed: [host3]PLAY RECAP ******************************************************************************************************************************
host1                      : ok=10   changed=5    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
host2                      : ok=10   changed=5    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
host3                      : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

测试

http://192.168.0.223/

您甚至可以在 http://192.168.0.233/haproxy?stats 查看 HAProxy 的统计信息。

正常Status为OPEN,如果为DOWN,host3:80报503注意检查以下内容:

  • 防火墙(host1 和 host2是否能访问)
  • HAProxy配置中的服务器ip是否正确(HAProxy服务器host3中的haproxy.cfg配置文件)
cat /etc/haproxy/haproxy.cfg #检查类似以下部分server host1 192.168.0.221:80 checkserver host2 192.168.0.222:80 check

处理办法见上面:注意事项

template和lineinfile模块

template 模块

  • 将动态生成的配置文件推送到目标服务器
  • 使用 Jinja2 模板引擎,允许在配置文件中插入变量、循环、条件判断等逻辑。
  • 最终生成一个静态配置文件并传输到目标服务器的指定路径。

关键特性

  • 动态渲染:假设您的 haproxy.cfg.j2 中可能包含类似下面的动态内容:
{% for server in groups.web %}
server {{ server }} {{ hostvars[server].ansible_default_ipv4.address }}:80 check
{% endfor %}

这会根据 web 主机组的服务器列表自动生成后端服务器配置。

  • 幂等性:如果目标文件已经存在且内容相同,不会重复操作。
  • 权限控制:可以精确设置文件权限、所有者和用户组。

适用场景

  • 需要动态生成配置文件(例如根据变量、主机列表生成不同内容)。
  • 需要将配置文件与 Playbook 逻辑解耦(模板和逻辑分离)。

lineinfile 模块

  • 确保文件中某一行存在(或不存在)
  • 通过正则表达式匹配目标行,并修改或替换它。
  • 常用于简单修改配置文件中的某个参数。

关键特性

  • 精准修改:只修改匹配到的行,不影响文件其他内容。
  • 幂等性:如果目标行已经是 ENABLED=1,不会重复修改。
  • 灵活性:可以用正则表达式匹配复杂模式。

适用场景

  • 修改文件中的某个特定参数(如 ENABLED=1)。
  • 添加一行内容(例如在 sshd_config 中添加 PermitRootLogin no)。

对比总结

模块用途特点适用场景
template生成并推送动态配置文件支持变量渲染、循环、条件判断需要动态生成完整配置文件时
lineinfile修改文件中某一行精准修改、正则匹配修改单个参数或添加单行内容

常见问题

  1. 为什么不直接用 copy 模块?
  • copy 模块直接复制静态文件,而 template 允许在文件中使用变量和逻辑。
  • 例如,在您的 HAProxy 配置中,后端服务器列表可能动态变化,用 template 可以自动生成列表。
  1. lineinfile 会覆盖整个文件吗?
  • 不会!它只修改匹配到的行,其他内容保持不变。
  1. 如果一行都不匹配会怎样?
  • 默认会在文件末尾添加 line 指定的内容。可以通过 insertafterinsertbefore 参数控制插入位置。

11.变量再探

所以我们已经设置好了负载均衡器,运行得相当不错。我们从事实中获取变量,并用它们来构建配置。但 Ansible 还支持其他类型的变量。我们已经在清单中看到了 ansible_host ,但现在我们将使用在 host_varsgroup_vars 文件中定义的变量。

微调我们的 HAProxy 配置

HAProxy 通常会检查后端是否处于活动状态。当某个后端似乎已停止运行时,它会被从后端池中移除,HAProxy 也不会再向其发送请求。

后端也可以有不同的权重(在 0 到 256 之间)。权重越高,与其他后端相比,该后端将接收的连接数就越多。如果节点的性能不均衡,这有助于更合理地分配流量。

我们将使用变量来配置所有这些参数。

Group vars

检查间隔将在 haproxy 的 group_vars 文件中设置。这将确保所有 haproxy 都能继承该设置。

我们只需在库存目录下创建文件 group_vars/haproxy.yml 即可。该文件必须以您想要为其定义变量的组命名。如果我们想为 web 组定义变量,该文件应命名为 group_vars/web.yml

请注意, .yml 是可选的:我们可以将 haproxy 组变量文件命名为 group_vars/haproxy ,Ansible 也能接受。扩展名只是帮助编辑器选择正确的语法高亮显示。

haproxy_check_interval: 3000
haproxy_stats_socket: /tmp/sock
#名称是任意的。当然,有意义的名称是推荐的,但没有规定的语法。您甚至可以像这样使用复杂的变量(也就是 Python 字典):
haproxy:check_interval: 3000stats_socket: /tmp/sock

这只是个人喜好问题。复杂变量有助于逻辑上对内容进行分组。 在某些情况下,它们还可以合并后续定义的键(但请注意,这并非 Ansible 的默认行为)。目前我们只使用简单变量。

Hosts vars

主机变量遵循完全相同的规则,但位于 host_vars 目录下的文件中。

让我们在 host_vars/host1.example.com 中为我们的后端定义权重:

haproxy_backend_weight: 100
#以及  host_vars/host2.example.com 
haproxy_backend_weight: 150

如果我们在 group_vars/web 中定义 haproxy_backend_weight ,它将被用作“默认值”:在 host_vars 文件中定义的变量会覆盖在 group_vars 中定义的变量。

更新模板

模板必须更新以使用这些变量。

globaldaemonmaxconn 256
{% if haproxy_stats_socket %}stats socket {{ haproxy_stats_socket }}
{% endif %}defaultsmode httptimeout connect 5000mstimeout client 50000mstimeout server 50000mslisten clusterbind {{ ansible_host }}:80mode httpstats enablebalance roundrobin
{% for backend in groups['web'] %}server {{ hostvars[backend]['ansible_hostname'] }} {{ hostvars[backend].ansible_eth1.ipv4.address }}:80 check port 80 weight {{ hostvars[backend].haproxy_backend_weight | default(100) }}
{% endfor %}option httpchk HEAD /index.php HTTP/1.0

请注意,我们还引入了一个 {% if ... 块。只有当测试为真时,此块内的内容才会被渲染。因此,如果我们为负载均衡器在某个地方定义了 haproxy_stats_socket (甚至可能在命令行中使用 --extra-vars="haproxy_stats_sockets=/tmp/sock" ),则包含的行将出现在生成的配置文件中(请注意,建议的设置非常不安全!)。

更新HAProxy.yaml:

---
- name: 配置 Web 服务器组hosts: webgather_facts: true  # 收集 web 组的服务器信息(如 IP、OS 版本)# 用于后续模板生成后端服务器列表- name: 配置 HAProxy 负载均衡器hosts: haproxy      # 目标为 haproxy 主机组tasks:# 任务1:安装 HAProxy- name: 安装 HAProxyapt:pkg: haproxystate: present     # 确保 HAProxy 已安装update_cache: yes  # 更新 apt 缓存(相当于 apt update)notify: haproxy_config_flow  # 触发名为 haproxy_config_flow 的处理程序链# 任务2:备份当前配置(用于回退)- name: 备份 HAProxy 配置copy:src: /etc/haproxy/haproxy.cfg  # 源文件(远程服务器上的当前配置)dest: /etc/haproxy/haproxy.cfg.bak  # 备份路径remote_src: yes    # 操作远程文件force: yes         # 强制覆盖旧备份when: not ansible_check_mode  # 仅在非检查模式运行(实际部署时执行)changed_when: false  # 不标记为已变更(避免误触发其他任务)notify: haproxy_config_flow  # 触发处理程序链# 任务3:生成并推送新配置- name: 生成并推送 HAProxy 配置template:src: templates/haproxy.cfg.j2  # Jinja2 模板路径dest: /etc/haproxy/haproxy.cfg  # 目标配置文件路径mode: 0640         # 文件权限(root 读写,haproxy 组只读)owner: rootgroup: rootnotify: haproxy_config_flow  # 触发处理程序链# 任务4:确保 HAProxy 开机自启- name: 启用 HAProxy 开机启动lineinfile:dest: /etc/default/haproxy  # 修改服务启动配置文件regexp: "^ENABLED"          # 匹配以 ENABLED 开头的行line: "ENABLED=1"           # 强制设置为启用notify: haproxy_config_flow   # 触发处理程序链handlers:# 处理程序1:验证配置文件语法- name: 验证 HAProxy 配置command: haproxy -c -f /etc/haproxy/haproxy.cfg  # 检查配置合法性register: haproxy_validate  # 存储命令执行结果(rc=0 表示成功)listen: haproxy_config_flow  # 绑定到 haproxy_config_flow 事件链ignore_errors: yes          # 允许验证失败后继续执行后续任务# 处理程序2:重启 HAProxy 服务(仅在验证成功时执行)- name: 重启 HAProxyservice:name: haproxystate: restartedwhen: - haproxy_validate.rc == 0  # 仅当验证通过时触发listen: haproxy_config_flow   # 绑定到同一事件链# 处理程序3:恢复备份配置(验证失败时执行)- name: 恢复 HAProxy 备份配置copy:src: /etc/haproxy/haproxy.cfg.bak  # 备份文件路径dest: /etc/haproxy/haproxy.cfg     # 覆盖错误配置remote_src: yeswhen: - haproxy_validate.rc != 0  # 仅当验证失败时触发listen: haproxy_config_flow    # 绑定到同一事件链notify: 重启 HAProxy           # 恢复配置后触发重启

执行:

ansible-playbook -i step-11/hosts step-11/haproxy.ymlPLAY [配置 Web 服务器组] ****************************************************************************************************************TASK [Gathering Facts] ******************************************************************************************************************
ok: [host2]
ok: [host1]PLAY [配置 HAProxy 负载均衡器] **********************************************************************************************************TASK [Gathering Facts] ******************************************************************************************************************
ok: [host3]TASK [安装 HAProxy] *********************************************************************************************************************
ok: [host3]TASK [备份 HAProxy 配置] ****************************************************************************************************************
ok: [host3]TASK [生成并推送 HAProxy 配置] **********************************************************************************************************
changed: [host3]TASK [启用 HAProxy 开机启动] ************************************************************************************************************
ok: [host3]RUNNING HANDLER [验证 HAProxy 配置] *****************************************************************************************************
changed: [host3]RUNNING HANDLER [重启 HAProxy] **********************************************************************************************************
changed: [host3]RUNNING HANDLER [恢复 HAProxy 备份配置] *************************************************************************************************
skipping: [host3]PLAY RECAP ******************************************************************************************************************************
host1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
host2                      : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
host3                      : ok=7    changed=3    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

验证

http://192.168.0.223/haproxy?stats

root@host0:/home/ansible-tuto# ansible -i hosts -m shell -a "cat /etc/haproxy/haproxy.cfg" host3
host3 | CHANGED | rc=0 >>
globaldaemonmaxconn 256stats socket /tmp/sockdefaultsmode httptimeout connect 5000mstimeout client 50000mstimeout server 50000mslisten clusterbind 192.168.0.223:80mode httpstats enablebalance roundrobinserver host1 192.168.0.221:80 check port 80 weight 100server host2 192.168.0.222:80 check port 80 weight 150option httpchk HEAD /index.php HTTP/1.0
root@host0:/home/ansible-tuto# ansible -i hosts -m shell -a "haproxy -c -f /etc/haproxy/haproxy.cfg" host3
host3 | CHANGED | rc=0 >>
Configuration file is valid
listen 机制

1. listen 的作用

  • 替代传统 notify:在 Ansible 2.2+ 中引入,允许将多个处理程序(handlers)绑定到同一个事件名称。
  • 执行顺序控制:所有绑定到同一 listen 名称的处理程序会按定义顺序依次执行。

2. 在您的 Playbook 中的工作流程

  1. 触发事件:当任意任务通过 notify: haproxy_config_flow 触发事件链时:

    tasks:- name: 生成并推送 HAProxy 配置template: ...notify: haproxy_config_flow  # 触发事件链
    
  2. 执行顺序

    • 验证配置重启服务恢复备份重启服务
  3. 条件控制:通过 when 条件决定是否执行:

    • 若验证成功(rc == 0),执行 重启 HAProxy
    • 若验证失败(rc != 0),执行 恢复 HAProxy 备份配置,随后触发 重启 HAProxy

3. 与传统 notify 的对比

场景传统 notifylisten 机制
触发单个处理程序notify: 重启 HAProxy不适用
触发多个处理程序需多次 notify所有绑定到同一 listen 的处理程序按序执行
条件执行处理程序内部通过 when 控制每个处理程序独立定义 when 条件

关键优化点说明

1. 备份强制覆盖 (force: yes)

  • 作用:确保每次备份时覆盖旧文件,避免备份文件过时。
  • 必要性:如果未启用,当配置文件未变更时,备份任务不会覆盖旧备份,导致回退时可能恢复错误版本。

2. 验证失败后的回退流程

  • 步骤
    1. 生成新配置 → 2. 验证失败 → 3. 恢复备份 → 4. 重启服务(使用旧配置)。
  • 结果:即使新配置错误,服务仍可用。

3. 统一事件链 (haproxy_config_flow)

  • 优势:将配置更新、验证、重启、回退逻辑集中管理,避免分散触发。

完整执行流程

  1. 安装 HAProxy:确保软件已安装。
  2. 备份配置:保存当前有效配置。
  3. 生成新配置:通过模板渲染推送新配置。
  4. 触发处理程序链
    • 验证配置 → 若成功 → 重启服务
    • 若失败 → 恢复备份重启服务

总结

通过 listen 机制,您实现了一个 原子化的配置管理流程

  • 集中控制:所有相关处理程序按序触发。
  • 安全回退:验证失败时自动恢复至有效配置。
  • 高可用性:避免服务因配置错误中断。
stats socket 的具体作用

stats socket 是 HAProxy 提供的一个 Unix Socket 接口,用于动态管理 HAProxy 的运行时状态。主要功能包括:

  • 实时操作
    • 禁用/启用后端服务器(如 disable server backend/server1)。
    • 动态调整权重(如 set server backend/server1 weight 200)。
    • 查看连接数、会话状态等实时统计信息。
  • 监控与管理
    • 通过命令行工具(如 socat)或 API 直接与 HAProxy 交互。
    • 支持自动化脚本动态调整负载均衡策略。

2. 是否可以不使用 stats socket

可以完全禁用。如果不使用 stats socket

  • 优点
    • 消除安全风险:关闭潜在的攻击入口。
    • 简化配置:减少维护复杂度。
  • 缺点
    • 失去动态管理能力:无法通过 Socket 实时调整服务器状态或权重。
    • 依赖重启:任何配置变更需重启 HAProxy 服务才能生效。

适用场景

  • 负载均衡策略固定,无需频繁调整。
  • 通过 Ansible 等工具静态管理配置变更。

3. 替代方案(未测试,待测试)

如果需动态管理 HAProxy 但不想使用 stats socket,有以下替代方案:

方案1:HTTP 统计接口

通过 HAProxy 的 HTTP 统计页面 进行基础管理:

listen statsbind *:8404stats enablestats uri /haproxy?statsstats auth admin:SecurePassword123!  # 强制认证stats refresh 10s  # 自动刷新间隔
  • 功能
    • 查看实时统计信息(如服务器状态、请求速率)。
    • 通过浏览器或 curl 手动禁用服务器(需配合前端工具)。
  • 限制
    • 无法直接执行复杂命令(如动态调整权重)。
    • 依赖 HTTP 接口,需额外安全防护(如 HTTPS、IP 白名单)。

方案2:HAProxy Runtime API(企业版)(未测试,待测试)

HAProxy 企业版提供 Runtime API,支持通过 HTTP/HTTPS 动态管理:

globalruntime-api 0.0.0.0:9999  # 监听端口api-user admin            # 认证用户api-password $6$加密密码  # 加密密码
  • 功能
    • 类似 stats socket 的所有操作,但通过 HTTP 接口实现。
    • 支持 RESTful 风格的管理命令。
  • 优势
    • 更易集成自动化工具(如 Prometheus、Grafana)。
    • 支持 HTTPS 和细粒度权限控制。

方案3:Ansible 动态配置(未测试,待测试)

通过 Ansible 直接生成配置文件并触发重载:

- name: 更新 HAProxy 配置template:src: haproxy.cfg.j2dest: /etc/haproxy/haproxy.cfgnotify: 安全重载 HAProxyhandlers:- name: 安全重载 HAProxycommand: haproxy -f /etc/haproxy/haproxy.cfg -sf $(cat /var/run/haproxy.pid)
  • 功能
    • 通过模板动态生成配置,替换旧配置后平滑重启(-sf 参数)。
  • 优势
    • 无需开放额外端口或 Socket。
    • 变更记录清晰(版本控制模板文件)。

总结与建议

方案安全性动态管理能力复杂度适用场景
禁用 stats socket配置固定,无需实时调整
HTTP 统计页面基础需要简单监控
Runtime API完整企业版用户,需自动化集成
Ansible 动态配置通过 IaC 管理,变更频率中等

推荐实践

  • 如果无需动态调整,直接禁用 stats socket
  • 若需动态管理且安全性优先,使用 HTTP 统计页面 + IP 白名单 + HTTPS
  • 企业用户可投资 HAProxy 企业版 的 Runtime API。

12.Playbook Roles实现配置模块化

将原有的 Playbook 重构为 角色(Roles),目的是:

  • 模块化:将 Apache 和 HAProxy 的配置拆分为独立角色,便于复用和管理。
  • 标准化结构:遵循 Ansible 角色的目录约定,提升代码可维护性。
  • 依赖管理:通过角色间的依赖关系(如 meta/main.yml)实现自动化执行顺序。
1角色目录结构解析

每个角色(如 apachehaproxy)的目录结构如下:

roles/├── apache/                  # 角色名称│   ├── tasks/               # 存放任务定义│   │   └── main.yml         # 自动加载的入口任务文件│   ├── handlers/            # 存放处理程序(如服务重启)│   │   └── main.yml         # 自动加载的入口处理程序文件│   ├── files/               # 存放静态文件(如配置文件)│   ├── templates/           # 存放 Jinja2 模板文件│   ├── defaults/            # 存放角色默认变量(优先级最低)│   ├── vars/                # 存放角色变量(优先级高于 defaults)│   └── meta/                # 定义角色依赖(如依赖其他角色)└── haproxy/└── ...                  # 结构同上

关键目录说明

  • tasks/main.yml:角色的核心任务入口,Ansible 会自动执行此文件中的任务。
  • handlers/main.yml:定义处理程序(如 restart apache),由任务中的 notify 触发。
  • files/templates/:存放静态文件和模板,引用时无需绝对路径。
    • 例如:copy: src=awesome-app 会自动从 files/ 目录查找。
  • defaults/main.yml:定义角色的默认变量,可被外部变量覆盖。
  • meta/main.yml:定义角色依赖,确保执行顺序(如先安装 Nginx 再配置 PHP)。

2文件命名规则
  • 强制规则

    • tasks/main.ymlhandlers/main.yml 等入口文件必须命名为 main.yml,否则 Ansible 无法自动加载。
  • 灵活规则

    • 其他文件(如 tasks/install_packages.yml)可自定义名称,但需在 main.yml 中通过 includeimport_tasks 显式引入。

    • 例如:

      # tasks/main.yml
      - include: install_packages.yml
      - include: configure_apache.yml
      

3迁移步骤详解

以迁移 Apache 配置为例:

  1. 创建角色目录

    mkdir -p step-12/roles/apache/{tasks,handlers,files}
    

    使用 ansible-galaxy 初始化角色

    ansible-galaxy init step-12/roles/haproxy  # 自动生成标准目录结构
    
  2. 拆分任务

    • 将原 apache.yml 中的任务复制到 tasks/main.yml。只保留tasks部分

      • #原apache.yml
        ---
        - hosts: webtasks:- name: Installs necessary packagesapt:*- name: Push future default virtual host configurationcopy:
        #现tasks/main.yml
        ---
        - name: Installs necessary packages
        apt:
        *
        - name: Push future default virtual host configuration
        copy:
        
  • 移除对 files/templates/ 的路径引用(角色会自动查找对应目录)。

    • #源文件中路径表示
      - name: Push future default virtual host configurationcopy:src: files/awesome-app # 显式路径
      # 角色任务文件 (roles/apache/tasks/main.yml)
      #迁移到角色后写法
      - name: Push future default virtual host configurationcopy:src: awesome-app  # 直接引用文件名(无需路径)
      
    • Ansible 会自动在角色的 files/ 目录下查找静态文件。

    • 如果文件在 files/ 的子目录中(如 files/config/),则需保留子目录路径,

      • src: config/awesome-app  # 文件路径为 roles/apache/files/config/awesome-app
        
  1. 提取处理程序

    • 将原 Playbook 中的 handlers 部分移到 handlers/main.yml

    • #原apache.ymlhandlers:- name: restart apacheservice:name: apache2state: restarted
      #现handlers/main.yml
      ---
      - name: restart apacheservice:name: apache2state: restarted
      
  2. 移动静态文件

    • 将配置文件(如 awesome-app)复制到 files/ 目录。
  3. 创建顶层 Playbook

    # site.yml
    - hosts: webroles:- apache  # 自动加载 roles/apache 下的任务
    - hosts: haproxyroles:- haproxy
    

4执行与验证
  • 运行 Playbook

    ansible-playbook -i hosts site.yml
    ansible-playbook -i step-12/hosts step-12/site.yml
    
  • 限制执行范围

    # 仅针对 web 主机组执行
    ansible-playbook -i step-12/hosts -l web step-12test/site.yml
    

执行情况:

PLAY RECAP ********************************************************************************************************************************
host1                      : ok=9    changed=6    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
host2                      : ok=9    changed=6    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
host3                      : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

13.Ansible 标签使用

在复杂的IT架构中,一个Ansible Playbook可能包含上百个任务。当您只需要更新Web服务配置却要完整执行整个Playbook时,就像为了换灯泡而重启整个数据中心。标签(Tags)的出现完美解决了这个痛点:

  • 手术刀式精准操作:只运行指定任务,节省90%执行时间
  • 环境隔离利器:开发/测试/生产环境差异化执行
  • 应急响应加速器:快速定位回滚点,故障恢复时间缩短75%
1、为何需要标签?

在复杂的IT架构中,一个Ansible Playbook可能包含上百个任务。当您只需要更新Web服务配置却要完整执行整个Playbook时,就像为了换灯泡而重启整个数据中心。标签(Tags)的出现完美解决了这个痛点:

  • 手术刀式精准操作:只运行指定任务,节省90%执行时间
  • 环境隔离利器:开发/测试/生产环境差异化执行
  • 应急响应加速器:快速定位回滚点,故障恢复时间缩短75%

2、标签基础语法

基础标记方法

列表式声明(推荐)

- name: 部署Apache服务apt:name: apache2state: latesttags:- web- install- critical

行内数组式

- name: 配置防火墙规则ufw:rule: allowport: "{{ http_port }}"tags: ['network', 'security']

命令行控制

命令作用示例场景
-t tag1,tag2执行包含任意指定标签的任务紧急安全更新
--skip-tags tagA排除指定标签的任务跳过耗时操作
-t tag1 -t tag2多标签联合过滤(逻辑OR)跨角色操作
--list-tags显示所有可用标签Playbook文档化
3、实战场景案例

场景1:快速部署热修复补丁

# 仅执行代码部署和服务重启
ansible-playbook deploy.yml -t git_pull,service_restart# 组合标签排除数据库操作
ansible-playbook deploy.yml -t app_deploy --skip-tags db_migration

场景2:安全合规检查

- name: 检查SSH协议版本shell: ssh -Vregister: ssh_versiontags: security_audit- name: 验证防火墙状态command: ufw status verbosetags: - security_audit- compliance

场景3:多环境配置

- name: 加载生产环境配置template:src: prod.env.j2dest: /etc/app.envtags: prod_only- name: 开发环境调试设置lineinfile:path: /etc/app.confline: "debug_mode=true"tags: dev_only

4、高级技巧揭秘
  1. 标签继承模式
# meta/main.yml
dependencies:- role: commontags: [base_setup]  # 自动继承base_setup标签
  1. 动态标签生成
- name: 按条件动态标记debug:msg: "数据库主节点专属配置"tags: "{{ 'db_master' if is_master else 'db_slave' }}"
  1. Handler标签联动
handlers:- name: 滚动重启服务systemd:name: appstate: restartedtags: canary_deploy  # 灰度发布专用handler

5、最佳实践指南
  1. 命名规范三原则

    • 业务维度:web, db, cache
    • 操作类型:install, config, cleanup
    • 环境标识:prod, staging, dev
  2. 黄金组合策略

    # 典型CI/CD流水线
    ansible-playbook site.yml \-t "validate,deploy" \--skip-tags "migration,full_restart"
    
  3. 危险操作防护

    - name: 数据库表结构迁移command: alembic upgrade headtags:- dangerous- db_migration
    
    # 显式确认执行危险操作
    ansible-playbook migrate.yml -t dangerous --extra-vars "confirm_danger=yes"
    

6、常见问题排查

Q1:标签未生效?

  • 检查项:
    1. YAML缩进是否正确
    2. 角色依赖是否继承标签
    3. 是否存在同名标签覆盖

Q2:如何调试复杂标签?

# 显示标签执行顺序
ANSIBLE_DEBUG=1 ansible-playbook playbook.yml -t my_tag# 生成标签关系图
ansible-playbook playbook.yml --list-tags | grep -B1 'TASK TAGS'

Q3:部分节点执行异常?

- name: 条件标签示例debug:msg: "仅CentOS系统执行"tags: centos_onlywhen: ansible_distribution == "CentOS"

7、性能优化指标
场景无标签执行时间标签优化时间提升幅度
全量部署12m 34s12m 34s0%
配置更新11m 45s1m 12s89%
紧急回滚9m 23s45s92%
安全补丁8m 56s38s93%

8、延伸阅读
  1. 标签与Ansible Tower集成

    # tower.yml
    launch_configuration:extra_vars:ansible_tags: "prod_deploy"limit: "webservers:&east"
    
  2. 基于标签的权限控制

    # ansible.cfg
    [privilege_escalation]
    require_sudo_tags = package_install, service_control
    
  3. 标签驱动文档生成

    ansible-doc-tagger generate --format markdown > PLAYBOOK_TAGS.md
    

相关文章:

【Ansible自动化运维实战:从Playbook到负载均衡指南】

本文是「VagrantVirtualBox虚拟化环境搭建」的续篇&#xff0c;深入探索Ansible在自动化运维中的核心应用&#xff1a; ✅ Ansible核心技能&#xff1a;Playbook编写、角色&#xff08;Roles&#xff09;模块化、标签&#xff08;Tags&#xff09;精准控制 ✅ 实战场景覆盖&a…...

数据赋能(210)——质量管理——可靠性原则

概述 数据可靠性原则确保数据的准确性、完整性、一致性和可信性&#xff0c;是决策和业务活动对数据质量的基本要求。在信息化和数字化快速发展的今天&#xff0c;数据已成为企业的重要资产&#xff0c;数据可靠性直接影响到企业的决策质量和业务活动效果。数据可靠性是数据质…...

二、机器学习中Python变量基础

二、Python变量基础 像C语言和Matlab一样&#xff0c;变量名由字母、数字、下划线组成&#xff08;但不能以数字开头&#xff0c;字母区分大小写&#xff09;变量名不能与内置的函数同名。 根据变量是否可以充当容器&#xff0c;将变量类型分为基本类型和高级类型。 基本变量…...

有机玻璃材质数据采集活性炭吸附气体中二氧化硫实验装置

JGQ112Ⅱ有机玻璃材质数据采集活性炭吸附气体中二氧化硫实验装置 一.实验目的 1.熟悉活性炭吸附剂的特性和在SO2气体净化方面的应用。 2.掌握活性炭吸附法的流程和实验过程中各参数的控制方法。 3.了解主要参数变化对吸附效率的影响。 4.掌握吸附等温线概念和测定方法。 二.技术…...

Javase 基础入门 —— 07 接口

本系列为笔者学习Javase的课堂笔记&#xff0c;视频资源为B站黑马程序员出品的《黑马程序员JavaAI智能辅助编程全套视频教程&#xff0c;java零基础入门到大牛一套通关》&#xff0c;章节分布参考视频教程&#xff0c;为同样学习Javase系列课程的同学们提供参考。 01 概述 接…...

LangChain:重构大语言模型应用开发的范式革命

2022年10月22日&#xff0c;Harrison Chase在GitHub上提交了名为LangChain的开源项目的第一个代码版本。这个看似普通的代码提交&#xff0c;却悄然开启了一场重塑大语言模型&#xff08;LLM&#xff09;应用开发范式的技术革命。彼时&#xff0c;距离ChatGPT引爆全球人工智能浪…...

【现代深度学习技术】现代循环神经网络04:双向循环神经网络

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…...

重塑数学边界:人工智能如何引领数学研究的新纪元

目录 一、人工智能如何重新定义数学研究的边界 &#xff08;一&#xff09;数学与AI的关系&#xff1a;从基础理论到创新思维的回馈 &#xff08;二&#xff09;AI的创造力&#xff1a;突破传统推理的局限 &#xff08;三&#xff09;AI对数学研究的潜在贡献&#xff1a;创…...

链表的回文结构题解

首先阅读题目&#xff1a; 1.要保证是回文结构 2.他的时间复杂度为O(n)、空间复杂度为O(1) 给出思路: 1.首先利用一个函数找到中间节点 2.利用一个函数逆置中间节点往后的所有节点 3.现在有两个链表&#xff0c;第一个链表取头节点一直到中间节点、第二个链表取头结点到尾…...

xLua笔记

Generate Code干了什么 肉眼可见的&#xff0c;在Asset文件夹生成了XLua/Gen文件夹&#xff0c;里面有一些脚本。然后对加了[CSharpCallLua]的变量寻找引用&#xff0c;发现它被XLua/Gen/DelegatesGensBridge引用了。也可以在这里查哪些类型加了[CSharpCallLua]。 public over…...

【Hive入门】Hive与Spark SQL深度集成:通过Spark ThriftServer高效查询Hive表

目录 引言 1 Spark ThriftServer架构解析 1.1 核心组件与工作原理 1.2 与传统HiveServer2的对比 2 Spark ThriftServer部署指南 2.1 环境准备与启动流程 2.1.1 前置条件检查 2.1.2 服务启动流程 2.2 高可用部署方案 2.2.1 基于ZooKeeper的HA架构 3 性能优化实战 3.…...

快速掌握--cursor

Cursor - The AI Code Editor 官网下载安装 详细教程&#xff1a;cursor 下载安装使用&#xff08;保姆教程&#xff09;_cursor下载-CSDN博客 不知道为啥&#xff0c;第一次给我用的是繁体回答 然后改了一下询问方式 codebase就是告诉ai可以从整个项目中找答案&#xff0…...

Linux之基础开发工具(yum,vim,gcc,g++)

目录 一、软件包管理器 1.1、什么是软件包 1.2、yum具体操作 1.2.1、查看软件包 1.2.2、安装软件 1.2.3、卸载软件 1.2.4、安装源 二、编辑器vim 2.1、vim的基本概念 2.2、vim的基本操作 2.3、vim正常模式命令集 2.4、vim末行模式命令集 2.5、替换模式 2.6、视图…...

【计算机视觉】三维重建: OpenMVS:工业级多视图立体视觉重建框架

深度解析OpenMVS&#xff1a;工业级多视图立体视觉重建框架 技术架构与核心算法1. 系统架构设计2. 核心算法解析稠密点云重建表面重建网格优化 实战全流程指南环境配置硬件要求编译安装&#xff08;Ubuntu&#xff09; 数据处理流程输入准备&#xff08;OpenMVG输出&#xff09…...

C++负载均衡远程调用学习之异步消息任务功能与连接属性

目录 1.LarV0.11-异步消息机制的event_loop增添属性分析 2.LARS 3.LarV0.11异步消息发送机制的实现及测试 4.LarV0.11异步消息任务机制bug修复和效果演示 5.LarV0.12链接参数属性的绑定 1.LarV0.11-异步消息机制的event_loop增添属性分析 ## 4) 事件触发event_loop ​ …...

内存性能测试方法

写于 2022 年 6 月 24 日 内存性能测试方法 - Wesley’s Blog dd方法测试 cat proc/meminfo console:/ # cat proc/meminfo MemTotal: 3858576 kB MemFree: 675328 kB MemAvailable: 1142452 kB Buffers: 65280 kB Cached: 992252 …...

游戏引擎学习第256天:XBox 控制器卡顿和修复 GL Blit 伽玛问题

回顾并为今天定下基调 今天的主要任务是让我们的性能分析工具正常工作&#xff0c;因为昨天已经完成了结构性工作。现在&#xff0c;剩下的工作大部分应该是调试和美化。性能分析工具现在应该已经基本可用了。昨天我们在这个方面取得了很大的进展。 接下来&#xff0c;我们将…...

4.29-4.30 Maven+单元测试

单元测试&#xff1a; BeforeAll在所有的单元测试方法运行之前&#xff0c;运行一次。 AfterAll在所有单元测试方法运行之后&#xff0c;运行一次。 BeforeEach在每个单元测试方法运行之前&#xff0c;都会运行一次 AfterEach在每个单元测试方法运行之后&#xff0c;都会运行…...

Android 端如何监控 ANR、Crash、OOM 等严重问题

在移动互联网时代&#xff0c;Android 应用已经成为我们生活中不可或缺的一部分。从社交聊天到在线购物&#xff0c;从娱乐消遣到办公学习&#xff0c;几乎每个人的手机里都装满了各式各样的应用。然而&#xff0c;作为开发者&#xff0c;咱们得面对一个残酷的现实&#xff1a;…...

Spring Boot 微服务打包为 Docker 镜像并部署到镜像仓库实战案例

案例项目素材可以拉取我github上的&#xff1a; https://github.com/AcademicTECHNERD/SpringCoudEurekaDemo 下面的案例将把我的product-service&#xff08;也就是提供者&#xff09;打包为镜像 执行maven命令&#xff1a; mvn clean package -DskipTests在根目录加一个dock…...

springAop代理责任链模式源码解析

目录 两次匹配 Bean 后置处理器中的匹配 方法调用时的匹配 Bean后置处理器中Advisor匹配流程 方法调用时的匹配 Jdk cglib 小小总结 Advisor 收集与排序 责任链执行过程 两次匹配 Bean 后置处理器中的匹配 在 Bean 初始化过程中&#xff0c;Spring 会通过 Bean 后置…...

ElasticSearch深入解析(九):Object、Nested、Flattened类型

文章目录 一、Object 类型&#xff1a;默认的嵌套对象处理方式核心原理典型场景关键限制 二、Nested 类型&#xff1a;解决嵌套数组的关联查询核心原理典型场景使用示例注意事项 三、Join 类型&#xff1a;跨文档的父子关联核心原理典型场景使用示例注意事项 四、Flattened 类型…...

list的迭代器详讲

1.list的迭代器就是封装了节点指针的类 2.迭代器失效 迭代器失效即迭代器封装的节点指针无效 。因为 list 的底层结构为带头结点的双向循环链表 &#xff0c;因此 在 list 中进行插入时是不会导致 list 的迭代 器失效的&#xff0c;只有在删除时才会失效&#xff0c;并且失效的…...

动态规划之多状态问题1

题目解析&#xff1a; 也就是给一个预约数组&#xff0c;选择一些数字&#xff0c;让其总和最大&#xff0c;但不能选择相邻的两个数字 算法原理&#xff1a; 依旧可以根据经验题目 以dp[i]位置结尾时&#xff0c;巴拉巴拉 根据题目要求补充完整&#xff0c;dp[i]&#xff…...

音视频开源项目列表

音视频开源项目列表 一、多媒体处理框架 通用音视频处理 FFmpeg - https://github.com/FFmpeg/FFmpeg 最强大的音视频处理工具库支持几乎所有格式的编解码提供命令行工具和开发库 GStreamer - https://gitlab.freedesktop.org/gstreamer/gstreamer 跨平台多媒体框架基于管道…...

论微服务架构及其应用

试题四 论微服务架构及其应用 微服务提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。每个服务运行在其独立的进程中&#xff0c;服务与服务间采用轻量级的通信机制互相沟通。在微服务架构中&#xff0c;每个服务…...

Spring Cloud与Service Mesh集成:Istio服务网格实践

文章目录 引言一、Spring Cloud与Service Mesh概述二、Istio服务网格架构三、Spring Cloud与Istio集成的基础设施准备四、服务发现与负载均衡五、流量管理与弹性模式六、安全通信与认证授权七、可观测性集成八、配置管理集成总结 引言 微服务架构已成为现代分布式系统的主流设…...

Day109 | 灵神 | 148.排序链表 | 归并排序

Day109 | 灵神 | 148.排序链表 | 归并排序 148. 排序链表 - 力扣&#xff08;LeetCode&#xff09; 以下是灵神的题解&#xff0c;笔者认为这题只要可以看懂就好了 两种方法&#xff1a;分治和迭代 文章目录 Day109 | 灵神 | 148.排序链表 | 归并排序前置题目方法一&#x…...

[更新完毕]2025东三省C题深圳杯C题数学建模挑战赛数模思路代码文章教学: 分布式能源接入配电网的风险分析

完整内容请看文章最下面的推广群 分布式能源接入配电网的风险分析 摘要 随着可再生能源渗透率的不断提升&#xff0c;分布式光伏发电在配电网中的大规模接入给传统电力系统运行带来了新的挑战。光伏发电固有的间歇性和波动性特征&#xff0c;加之配电网拓扑结构的复杂性&…...

ActiveMQ 集群搭建与高可用方案设计(二)

五、高可用方案设计与优化 &#xff08;一&#xff09;Zookeeper 在 ActiveMQ 集群中的应用 作用&#xff1a;在 ActiveMQ 集群中&#xff0c;Zookeeper 扮演着至关重要的角色。它主要用于选举 Master 节点&#xff0c;通过其内部的选举机制&#xff0c;从众多的 ActiveMQ Br…...

多协议 Tracker 系统架构与传感融合实战 第六章 多传感器时钟同步与数据对齐

第六章 多传感器时钟同步与数据对齐 摘要 本章围绕多源传感融合系统中——尤其是 IMU 与 UWB——的时钟同步与数据对齐问题展开,系统介绍: 硬件时钟源类型及漂移特性 软件校准策略:NTP/PTP 与自定义心跳同步 多源时钟同步算法:两阶段对齐与漂移补偿 数据缓冲与双队列对齐架…...

【算法基础】插入排序算法 - JAVA

一、算法基础 1.1 什么是插入排序 插入排序是一种简单直观的排序算法&#xff0c;它的工作原理类似于我们打牌时整理手牌的过程。插入排序的核心思想是将数组分为已排序和未排序两部分&#xff0c;每次从未排序部分取出一个元素&#xff0c;插入到已排序部分的适当位置。 1.…...

#Paper Reading# DeepSeek-R1

论文题目: DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning 论文地址: https://arxiv.org/pdf/2501.12948 论文发表于: arXiv 2025年1月 论文所属单位: DeepSeek 论文大体内容 本文提出DeepSeek-R1模型&#xff0c;主要是以DeepSeek-V3[…...

HTML与CSS实现风车旋转图形的代码技术详解

在前端开发中&#xff0c;HTML和CSS是构建网页的基础技术。通过巧妙运用HTML的结构搭建和CSS的样式控制&#xff0c;我们能够实现各种精美的视觉效果。本文将对一段实现旋转图形效果的HTML和CSS代码进行详细解读&#xff0c;剖析其中的技术要点。 一、运行效果 HTML与CSS实现风…...

AWS在跨境电商中的全场景实践与未来生态构建

AWS在跨境电商中的全场景实践与未来生态构建 一、核心应用场景与技术赋能 1. AI驱动运营效率革命 • 智能选品与市场分析&#xff1a;通过Amazon SageMaker机器学习平台&#xff0c;跨境电商企业可构建精准选品模型。陕西自贸试验区案例显示&#xff0c;AI对亚马逊等平台销…...

AWS云服务深度技术解析:架构设计与最佳实践

作为全球市场份额占比32%的云服务提供商&#xff08;Synergy Research 2023数据&#xff09;&#xff0c;AWS的技术体系已成为企业级应用架构的标杆。本文将深入剖析AWS核心技术组件的实现原理&#xff0c;并附可落地的架构设计范式。 AWS云服务器&#xff1a;中国企业出海的“…...

130. 被围绕的区域

题目链接&#xff1a;130. 被围绕的区域 思路&#xff1a;使用两遍dfs&#xff0c;第一遍找到可以被替换区域的可进入点并记录&#xff0c;第二遍就从所有的可进入点入手遍历区域内所有点并替换。 这是我的思路&#xff0c;感觉还是挺新颖的&#xff08;应该很少有人这样想我…...

【Linux】进程优先级与进程切换理解

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 目录 前言 一、进程优先级 1. 什么是进程优先级 2. 为什么有进程优先级 3. 进程优先级的作用 4. Linux进程优先级的本质 5. 修改进程优先级 二、进…...

数据分析与可视化实战:从鸢尾花到乳腺癌数据集

数据分析是现代数据科学中不可或缺的一部分&#xff0c;它帮助我们理解数据、发现模式并做出明智的决策。本文将分享两个实战案例&#xff1a;鸢尾花数据集分析和乳腺癌数据集预处理&#xff0c;展示如何使用Python进行数据探索和可视化。 鸢尾花数据集分析 数据加载与基本统…...

怎样提升社交机器人闲聊能力

怎样提升社交机器人闲聊能力 本文聚焦社交机器人闲聊能力,指出闲聊在社交中意义重大,当前大语言模型(LLMs)驱动社交机器人闲聊存在不足。通过实验评估ChatGPT-3.5、Gemini Pro和LLaMA-2等LLMs闲聊表现,发现其与人类闲聊存在差异。 为此提出基于观察者模型的反馈重定向方…...

图论之幻想迷宫

题目描述&#xff1a; 幻象迷宫可以认为是无限大的&#xff0c;不过它由若干个 NM 的矩阵重复组成。矩阵中有的地方是道路&#xff0c;用 . 表示&#xff1b;有的地方是墙&#xff0c;用 # 表示。LHX 和 WD 所在的位置用 S 表示。也就是对于迷宫中的一个点(x,y)&#xff0c;如…...

数学实验Matlab

一、Matlab语言环境和线性代数实验 1.Matlab语言环境 Matlab简介 Matlab&#xff1a;Matrix Laboratry 矩阵实验室 Matlab 提供了强大的科学计算、灵活的程序设计流程、高质量的图形可视化与界面设计等功能&#xff0c;被广泛应用于科学计算、控制系统、信息处理等领域的分…...

AI日报 · 2025年5月03日|Perplexity 集成 WhatsApp,苹果传与 Anthropic 合作开发 Xcode

1、Perplexity AI 功能更新&#xff1a;新增 WhatsApp 集成与多项优化 Perplexity 于 5 月 2 日发布其每周更新摘要&#xff0c;重点包括新增 WhatsApp 集成&#xff0c;用户现可直接在 WhatsApp 内与 Perplexity AI 交互&#xff0c;显著提升了信息获取的便捷性 [1]。此次更新…...

Maven 实现多模块项目依赖管理

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

【JavaScript-Day 2】开启 JS 之旅:从浏览器控制台到 `<script>` 标签的 Hello World 实践

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

Windows 中使用dockers创建指定java web 为镜像和运行容器

以下是在 Windows 中使用 Docker 创建 Java Web 应用镜像并运行容器的分步指南&#xff1a; 步骤 1&#xff1a;安装 Docker 下载并安装 Docker Desktop for Windows启动 Docker Desktop&#xff0c;确保使用 WSL 2 后端&#xff08;推荐&#xff09;或 Hyper-V。 步骤 2&…...

机器人--MCU

MCU MCU&#xff08;Microcontroller Unit&#xff0c;微控制器&#xff09; 是机器人的“神经末梢”&#xff0c;负责 实时控制、传感器接口、低层通信 等关键任务。 作用 MCU的核心作用 功能具体任务示例实时控制电机PWM生成、PID调节、紧急制动机械臂关节控制、无人机电调…...

从融智学视域快速回顾世界历史和主要语言文字最初历史证据(列表对照分析比较)

融智学视域下世界历史与语言文字起源对照分析表 以下从融智学五个基本范畴&#xff08;物、意、文、道、理义法&#xff09;&#xff0c;梳理主要古代文明的文字起源&#xff0c;及其历史证据&#xff0c;并进行跨文明比较&#xff1a; 文明/文字 物&#xff08;载体&#xf…...

JavaScript性能优化实战(8):缓存策略与离线优化

前言 在Web应用中,性能优化不仅仅是关于代码执行速度,还与资源获取和数据持久化密切相关。合理的缓存策略可以显著减少网络请求,提升应用响应速度,同时有效降低服务器负载和用户流量消耗。离线优化则进一步解决了网络不稳定或断网场景下的用户体验问题,为Web应用提供类似…...

quantization-大模型权重量化简介

原文地址 https://towardsdatascience.com/introduction-to-weight-quantization-2494701b9c0c/ https://towardsdatascience.com/4-bit-quantization-with-gptq-36b0f4f02c34/ 权重量化简介 大型语言模型(LLM) 以其庞大的计算需求而闻名。通常&#xff0c;模型的大小是通过将参…...