【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
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/78884.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/78884.shtml
英文地址,請注明出處:http://en.pswp.cn/web/78884.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于STM32、HAL庫的STC31-C-R3氣體傳感器驅動程序設計

一、簡介: STC31-C-R3是Sensirion公司推出的一款基于CMOSens技術的CO2傳感器,具有以下特點: 測量范圍:0-100%體積濃度 I2C數字接口 低功耗設計 高精度和長期穩定性 小尺寸封裝(5mm x 5mm) 二、硬件接口: STC31-C-R3 STM32L4xx ---------------------------- VDD (P…

Nginx篇之限制公網IP訪問特定接口url實操

一、nginx配置限制IP訪問 要在 Nginx 配置中添加 IP 限制&#xff0c;阻止來自指定公網 IP 地址段的訪問&#xff0c;并且只對特定路徑進行限制&#xff0c;可以在 location 配置中使用 deny 和 allow 指令來控制訪問。 二、案例 1. 需求 對來自特定公網的地址段&#xff0…

算法研習:無重復字符的最長子串問題剖析

算法研習:無重復字符的最長子串問題剖析 一、引言 在算法的廣袤天地中,字符串相關問題一直是備受關注的焦點。“無重復字符的最長子串”這一問題,不僅在面試中頻繁出現,更是對算法思維和編程技巧的一次深度考驗。它要求我們從給定字符串中找出不含有重復字符的最長子串的長…

Spring Cloud Gateway路由+斷言+過濾

目錄 介紹核心功能三大核心Route以服務名動態獲取URLPredicate常用斷言Path Route PredicateAfter Route PredicateBefore Route PredicateBetween Route PredicateCookie Route PredicateHeader Route PredicateHost Route PredicateQuery Route PredicateRemoteAddr Route Pr…

springboot集成langchain4j記憶對話

流式輸出 LLM 一次生成一個標記&#xff08;token&#xff09;&#xff0c;因此許多 LLM 提供商提供了一種方式&#xff0c;可以逐個標記地流式傳輸響應&#xff0c;而不是等待整個文本生成完畢。 這顯著改善了用戶體驗&#xff0c;因為用戶不需要等待未知的時間&#xff0c;幾…

【SpringCloud GateWay】Connection prematurely closed BEFORE response 報錯分析與解決方案

一、背景 今天業務方調用我們的網關服務報錯: Connection prematurely closed BEFORE response二、原因分析 三、解決方案 第一步: 增加 SCG 服務的JVM啟動參數,調整連接獲取策略。 將連接池獲取策略由默認的 FIFO&#xff08;先進先出&#xff09;變更為 LIFO&#xff08…

使用ZYNQ芯片和LVGL框架實現用戶高刷新UI設計系列教程(第十一講)

這一期講解lvgl中下拉框的基礎使用&#xff0c;下拉列表允許用戶從選項列表中選擇一個值&#xff0c;下拉列表的選項表默認是關閉的&#xff0c;其中的選項可以是單個值或預定義文本。 當單擊下拉列表后&#xff0c;其將創建一個列表&#xff0c;用戶可以從中選擇一個選項。 當…

【神經網絡與深度學習】VAE 在解碼前進行重參數化

在 VAE 中&#xff0c;解碼之前進行重參數化主要有以下幾個重要原因&#xff1a; 可微分性 在深度學習里&#xff0c;模型是通過反向傳播算法來學習的&#xff0c;而這需要計算梯度。若直接從潛在變量的分布 (q_{\theta}(z|x))&#xff08;由編碼器輸出的均值 (\mu) 和方差 (…

BBDM學習筆記

1. configs 1.1 LBBDM: Latent BBDM [readme]

mysql主從復制搭建,并基于?Keepalived + VIP實現高可用

以下是基于 ?Keepalived VIP? 實現 MySQL 主從復制高可用的詳細步驟&#xff0c;涵蓋主從復制搭建與故障自動切換&#xff1a; 一、MySQL 主從復制搭建&#xff08;基礎步驟回顧&#xff09; 1. ?主庫&#xff08;Master&#xff09;配置? 修改配置文件? /etc/my.cnf&…

CD36.【C++ Dev】STL庫的string的使用 (下)

目錄 1.reserve函數(不是reverse) 代碼示例 2.resize 代碼示例 3.reserve和resize的區別 4.shrink_to_fit 代碼示例 5.與C語言的配合的接口函數: c_str 代碼示例 6.rfind 知識回顧:find函數 rfind 代碼示例 練習題: 字符串最后一個單詞的長度 代碼 提交結果 ?…

STM32的網絡天氣時鐘項目

一、項目概述與硬件架構 1.1 核心功能 本智能天氣時鐘系統集成了實時天氣獲取、網絡時間同步、環境監測和低功耗管理四大核心功能&#xff1a; 網絡數據獲取&#xff1a; 通過ESP8266 WiFi模塊連接心知天氣API&#xff08;每小時更新&#xff09;獲取北京標準時間服務器的時…

FPGA DDR4多通道管理控制器設計

DDR4控制器一般采用自帶的MIG控制器&#xff0c;用戶控制主要是基于MIG IP核進行設計 實際工程項目中可能只掛載了一組DDR&#xff0c;但是用戶數據可能有很多種&#xff0c;用戶通過給每種數據劃分特定地址進行存儲&#xff0c;如何實現靈活管理成為設計的關鍵 為了方便后端數…

低代碼 x AI,解鎖數智化應用的創新引擎

AI 智能體開發指南 隨著全球信息化浪潮的持續推進&#xff0c;數字化、智能化轉型已成為企業發展的必經之路。在這個變革的時代&#xff0c;企業面臨著前所未有的挑戰與機遇。一方面&#xff0c;市場環境瞬息萬變&#xff0c;企業需要快速響應并調整業務模式&#xff1b;另一方…

【Spring Boot 注解】@Configuration與@AutoConfiguration

文章目錄 Configuration與AutoConfiguration一、Configuration二、AutoConfiguration Configuration與AutoConfiguration 一、Configuration 這是最常用的 Spring 注解之一&#xff0c;表示當前類是一個 配置類&#xff0c;可以定義 Bean 方法&#xff0c;等效于傳統的 XML 配…

arXiv論文 MALOnt: An Ontology for Malware Threat Intelligence

文章講惡意軟件威脅情報本體。 作者信息 作者是老美的&#xff0c;單位是倫斯勒理工學院&#xff0c;文章是2020年的預印本&#xff0c;不知道后來發表在哪里&#xff08;沒搜到&#xff0c;或許作者懶得投稿&#xff0c;也可能是改了標題&#xff09;。 中心思想 介紹開源…

【存儲管理—動態不等長存儲資源分配算法】

文章目錄 一、實驗目的二、實驗內容與設計思想實驗內容設計思路 三、實驗代碼實現四、總結 一、實驗目的 理解動態異長存儲分區資源管理&#xff0c;掌握所需數據結構和管理程序&#xff0c;了解各種存儲分配算法的優點和缺點。 二、實驗內容與設計思想 實驗內容 1.分析uni…

快速上手 Docker:從入門到安裝的簡易指南(Mac、Windows、Ubuntu)

PS&#xff1a;筆者在五一剛回來一直搞Docker部署AI項目&#xff0c;發現從開發環境遷移到生成環境時&#xff0c;Docker非常好用。但真的有一定上手難度&#xff0c;推薦讀者多自己嘗試踩踩坑。 本篇幅有限&#xff0c;使用與修改另起篇幅。 一、Docker是什么 #1. Docker是什…

LabVIEW高沖擊加速度校準系統

在國防科技領域&#xff0c;高 g 值加速度傳感器廣泛應用于先進兵器研制&#xff0c;如深侵徹系統、精確打擊彈藥及鉆地彈藥等。其性能指標直接影響研究結果的準確性與可靠性&#xff0c;因此對該傳感器進行定期校準意義重大。高沖擊加速度校準系統具備多方面功能&#xff0c;適…

FPGA 純邏輯NVME raid0 IP核

系統采用XCZU19EG搭載4個三星990 PRO SSD 單盤讀寫不低于3GB/s 4盤總帶寬不低于12GB/s