How to Design an End-to-End Ansible Automation Lab with Playbooks, Inventories, Roles, Vault, Dynamic Inventory, and Custom Modules
重點摘要
In this tutorial, we build a complete Ansible lab that runs end-to-end in Google Colab or any Linux environment. We start by installing ansible-core, setting up a local workspace, creating an Ansible configuration file, and defining both static and dynamic inventories. We then explore key Ansible concepts, including group variables, host variables, variable precedence, ad hoc commands, playbooks, loops, conditionals, registered outputs, facts, templates, custom filters, custom modules, roles, handlers, tags, dry runs, idempotency, and Ansible Vault. Since every host runs locally, we practice these concepts safely without needing SSH keys, remote servers, or cloud infrastructure. Copy CodeCopiedUse a different Browserimport os, sys, subprocess, textwrap, stat BASE = "/content/ansible_lab" i
In this tutorial, we build a complete Ansible lab that runs end-to-end in Google Colab or any Linux environment. We start by installing ansible-core, setting up a local workspace, creating an Ansible configuration file, and defining both static and dynamic inventories. We then explore key Ansible concepts, including group variables, host variables, variable precedence, ad hoc commands, playbooks, loops, conditionals, registered outputs, facts, templates, custom filters, custom modules, roles, handlers, tags, dry runs, idempotency, and Ansible Vault. Since every host runs locally, we practice these concepts safely without needing SSH keys, remote servers, or cloud infrastructure. Copy CodeCopiedUse a different Browserimport os, sys, subprocess, textwrap, stat BASE = "/content/ansible_lab" if os.path.isdir("/content") else os.path.expanduser("~/ansible_lab") os.makedirs(BASE, exist_ok=True) ENV = os.environ.copy() ENV["ANSIBLE_CONFIG"] = os.path.join(BASE, "ansible.cfg") ENV["ANSIBLE_FORCE_COLOR"] = "1" ENV["PY_COLORS"] = "0" def banner(title): print("\n" + "=" * 78 + f"\n {title}\n" + "=" * 78) def write(relpath, content): """Write a dedented file under BASE, creating parent dirs.""" path = os.path.join(BASE, relpath) os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "w") as f: f.write(textwrap.dedent(content).lstrip("\n")) return path def sh(cmd, title=None): """Run a shell command from BASE, stream stdout, never raise.""" if title: banner(title) print(f"$ {cmd}\n") p = subprocess.run(cmd, shell=True, cwd=BASE, env=ENV, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) print(p.stdout) return p.returncode banner("STEP 1 — Installing ansible-core") subprocess.run([sys.executable, "-m", "pip", "install", "-q", "ansible-core"], check=True) sh("ansible --version") write("ansible.cfg", """ [defaults] inventory = ./inventory.ini roles_path = ./roles library = ./library filter_plugins = ./filter_plugins vault_password_file = ./vault_pass.txt host_key_checking = False retry_files_enabled = False interpreter_python = auto_silent callback_result_format = yaml deprecation_warnings = False localhost_warning = False nocows = 1 [privilege_escalation] become = False """) write("inventory.ini", """ [webservers] web1 ansible_connection=local web2 ansible_connection=local [dbservers] db1 ansible_connection=local [datacenter:children] webservers dbservers """) We start by preparing the Ansible workspace, setting environment variables, and defining helper functions that make the tutorial easier to run. We install ansible-core, verify the installation, and create the main Ansible configuration file. We also define a static inventory with local web and database host groups so that we can practice Ansible concepts without using remote servers. Copy CodeCopiedUse a different Browserwrite("group_vars/all.yml", """ --- app_name: "Colab Demo App" app_version: "2.0.1" admin_email: "[email protected]" packages: - nginx - git - htop feature_flags: enable_cache: true enable_metrics: false """) write("host_vars/web1.yml", """ --- server_id: 101 max_connections: 512 """) write("filter_plugins/custom_filters.py", ''' import re def to_slug(value): return re.sub(r"[^a-z0-9]+", "-", str(value).lower()).strip("-") def human_bytes(value): n = float(value) for unit in ["B", "KB", "MB", "GB", "TB"]: if n < 1024: return f"{n:.1f}{unit}" n /= 1024 return f"{n:.1f}PB" class FilterModule(object): def filters(self): return {"to_slug": to_slug, "human_bytes": human_bytes} ''') write("library/system_report.py", ''' #!/usr/bin/python from ansible.module_utils.basic import AnsibleModule import platform, os def main(): module = AnsibleModule( argument_spec=dict( label=dict(type="str", required=True), threshold=dict(type="int", required=False, default=80), ), supports_check_mode=True, ) report = { "label": module.params["label"], "system": platform.system(), "release": platform.release(), "python": platform.python_version(), "cpu_count": os.cpu_count(), "threshold": module.params["threshold"], } module.exit_json(changed=False, report=report, message="Report generated for %s" % module.params["label"]) if __name__ == "__main__": main() ''') We define shared group variables and host-specific variables to show how Ansible manages configuration data and applies variable precedence. We then create a custom Jinja2 filter plugin that converts text into slugs and formats byte values into readable units. We also built a custom Python-based Ansible module that generates a simple system report for each host. Copy CodeCopiedUse a different Browserwrite("roles/webserver/defaults/main.yml", """ --- listen_port: 8080 """) write("roles/webserver/vars/main.yml", """ --- doc_root: "/tmp/www" """) write("roles/webserver/tasks/main.yml", """ --- - name: Ensure docroot exists ansible.builtin.file: path: "{{ doc_root }}" state: directory mode: "0755" - name: Deploy index.html from a Jinja2 template ansible.builtin.template: src: index.html.j2 dest: "{{ doc_root }}/index.html" notify: Restart web service - name: Run handlers immediately (instead of end of play) ansible.builtin.meta: flush_handlers """) write("roles/webserver/handlers/main.yml", """ --- - name: Restart web service ansible.builtin.debug: msg: "(simulated) restarting web service on port {{ listen_port }}" """) write("roles/webserver/templates/index.html.j2", """ <!DOCTYPE html> <html> <head><title>{{ app_name }}</title></head> <body> <h1>{{ app_name }} v{{ app_version }}</h1> <p>Served on port {{ listen_port }} from {{ doc_root }}</p> <p>Host: {{ inventory_hostname }}</p> </body> </html> """) write("templates/report.txt.j2", """ Deployment Report ================= App: {{ app_name }} ({{ app_version }}) Host: {{ inventory_hostname }} Generated: {{ ansible_date_time.iso8601 | default('n/a') }} Slug: {{ app_name | to_slug }} Packages: {% for p in packages %} - {{ p }} {% endfor %} Cache enabled: {{ feature_flags.enable_cache }} Metrics enabled: {{ feature_flags.enable_metrics }} """) dyn = write("dynamic_inventory.py", ''' #!/usr/bin/env python3 import json, sys INV = { "webservers": {"hosts": ["web1", "web2"], "vars": {"role": "frontend"}}, "dbservers": {"hosts": ["db1"], "vars": {"role": "backend"}}, "_meta": { "hostvars": { "web1": {"ansible_connection": "local", "tier": "gold"}, "web2": {"ansible_connection": "local", "tier": "silver"}, "db1": {"ansible_connection": "local", "tier": "gold"}, } }, } if "--host" in sys.argv: print(json.dumps({})) else: print(json.dumps(INV, indent=2)) ''') os.chmod(dyn, os.stat(dyn).st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) We create a complete web server role with defaults, variables, tasks, handlers, and templates to demonstrate how to build reusable Ansible automation. We use Jinja2 templates to generate an HTML page and a deployment report from Ansible variables. We also add a dynamic inventory script that returns host and group information in JSON format. Copy CodeCopiedUse a different Browserwrite("playbook.yml", """ --- - name: Advanced concepts demo hosts: webservers gather_facts: true vars: deploy_user: colab tasks: - name: Merged variables (group_vars + host_vars precedence) ansible.builtin.debug: msg: "App={{ app_name }} v{{ app_version }} | server_id={{ server_id | default('n/a') }}" - name: CUSTOM filter -> to_slug ansible.builtin.debug: msg: "slug => {{ app_name | to_slug }}" - name: CUSTOM filter -> human_bytes ansible.builtin.debug: msg: "size => {{ 1536000 | human_bytes }}" - name: LOOP with an index variable ansible.builtin.debug: msg: "package #{{ idx + 1 }} = {{ item }}" loop: "{{ packages }}" loop_control: index_var: idx - name: CONDITIONAL (when) — only if caching is enabled ansible.builtin.debug: msg: "cache is ON" when: feature_flags.enable_cache | bool - name: Run a command and REGISTER its output ansible.builtin.command: date +%Y-%m-%d register: date_out changed_when: false - name: SET a derived fact from the registered value ansible.builtin.set_fact: deploy_stam
Related
相關文章
Liquid AI Introduces LFM2.5-Embedding-350M and LFM2.5-ColBERT-350M: Dense Bi-Encoder and Late-Interaction Models for Fast Multilingual Search Across 11 Languages
This week, Liquid AI released two new retrieval models. They are LFM2.5-ColBERT-350M and LFM2.5-Embedding-350M. Both hold 350M parameters. Both are the first bidirectional members of the LFM family. They build on LFM2.5-350M-Base, released in March. The pair targets fast multilingual and cross-lingual search across 11 languages. Their footprint is small enough to run almost anywhere. Both are available now on Hugging Face under the LFM Open License v1.0. LFM2.5 Retrievers The two models share one backbone but represent text differently. LFM2.5-Embedding-350M is a dense bi-encoder. It turns each document into a single vector. Pick it when you want the fastest search and the smallest, cheapest index. LFM2.5-ColBERT-350M is a late-interaction model. It converts each token into a vector rather
Perplexity Launches Brain, a Self-Improving Memory System That Builds a Context Graph of an Agent’s Work and Learns Overnight
Most AI memory remembers the user. It stores your preferences, your tastes, and your role. Perplexity is taking a different path. Today, Perplexity launched Brain, a self-improving memory system for its agent product, Computer. Brain does not focus on remembering you. It remembers what the agent did. That reframes what memory in AI is for. What is Perplexity‘s Brain Brain is a self-improving memory system. It builds a context graph of the work Computer performs. At set intervals, such as overnight, Brain reviews that graph. It then teaches itself how to do the work better. The idea is straightforward. The more work you do, the more efficient Brain makes your Computer. Brain is rolling out today to Perplexity Max and Enterprise Max subscribers in Research Preview. Two Axes of AI Memory Perp

智譜新高,MiniMax承壓,“大模型雙雄”命運殊途
這篇消息聚焦「智譜新高,MiniMax承壓,“大模型雙雄”命運殊途」。原始導語提到:大模型在被市場重新定價 從 AI 情報角度來看,這類內容值得關注其背後的技術進展、產品落地、產業競爭與後續市場影響。

華為昇騰 0 Day 支持智譜 GLM-5.2 模型,提供全面推理優化
華為昇騰 AI 宣佈在智譜開源 GLM-5.2 大模型當天即完成深度推理優化。通過 MOE 大融合算子、通信計算融合、高併發調度等七項關鍵技術,顯著提升編程和長程任務的處理效率,現已支持 A3 系列產品部署。#AI 大模型# #國產算力#
企業AI轉型再添利器:青雲科技算力雲接入 MiniMax-M3 模型
企業AI落地面臨高效低成本難題。青雲科技旗下基石智算平臺接入國產開源大模型MiniMax-M3,提供新算力支持。MiniMax-M3以卓越上下文處理能力等三大核心技術見長,依託自研架構,助企業便捷部署AI業務。
阿里開源統一科學大模型 LOGOS,僅用五十六分之一參數超越微軟
阿里 ATH-Token Foundry 聯閤中國人民大學高瓴人工智能學院開源科學基礎模型 LOGOS。該模型採用統一科學語法與純序列建模範式,在六大科學任務上匹配或超越傳統專用方法。其中 LOGOS-1B 僅 1B 參數,即展現出極高效率,性能超越參數量達 8×7B 的微軟模型。