{"id":24305849,"url":"https://github.com/880831ian/ansible","last_synced_at":"2025-06-16T23:02:02.095Z","repository":{"id":168882487,"uuid":"492711670","full_name":"880831ian/Ansible","owner":"880831ian","description":"Ansible 介紹與實作 (Inventory、Playbooks、Module、Template、Handlers)","archived":false,"fork":false,"pushed_at":"2022-05-18T02:47:21.000Z","size":1984,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-08T18:03:25.469Z","etag":null,"topics":["ansible","cicd","devops","docker"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/880831ian.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-16T06:35:10.000Z","updated_at":"2024-11-20T04:21:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"f98bf762-6854-48c5-9cda-ab8b6819e6ec","html_url":"https://github.com/880831ian/Ansible","commit_stats":null,"previous_names":["880831ian/ansible"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/880831ian/Ansible","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/880831ian%2FAnsible","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/880831ian%2FAnsible/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/880831ian%2FAnsible/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/880831ian%2FAnsible/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/880831ian","download_url":"https://codeload.github.com/880831ian/Ansible/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/880831ian%2FAnsible/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260256221,"owners_count":22981804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","cicd","devops","docker"],"created_at":"2025-01-17T02:16:57.070Z","updated_at":"2025-06-16T23:02:02.074Z","avatar_url":"https://github.com/880831ian.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ansible 介紹與實作 (Inventory、Playbooks、Module、Template、Handlers)\n\n本篇文章是接續前面兩篇 [Jenkins 及 Ansible IT 自動化 CI/CD 介紹](https://pin-yi.me/jenkins-ansible/) 跟 [使用 Jenkins 設定 GitHub 觸發程序並通知 Telegram Bot](https://pin-yi.me/jenkins/) 文章，歡迎大家先去觀看前面兩篇文章 🤪\n\n\u003cbr\u003e\n\n## Ansible 是如何運作的？\n\n在 Ansible 世界裡，我們會透過 `Inventory 檔案` 來定義有哪些的 `Managed Node`，並藉由 `SSH` 與 `Python` 來進行溝通。那我們先來看一張圖：\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/run.png)\n\n\u003cbr\u003e\n\n誒 😱 突然多了很多新名詞，沒關係我來一一解釋，首先我們先從 `Managed Node` 是什麼，以及圖片中的 `Control machine` 開始說起吧！\n\n\n### 什麼是控制主機及被控節點？\n\n在 Ansible 裡，我們會把所有機器的角色做以下的區分：\n\n* 控制主機 (Control Machine)：顧名思義，這類主機可以透過運行 Ansible 的劇本 (Playbooks) 對被控節點進行部署。\n* 被控節點 (Managed Node)：也稱為遙控節點 (Remote Node)。相對於控制主機，這類節點就是我們透過 Ansible 進行部署的對象。\n\n所以代表我們在操作這邊就是 Control Machine，要部署的機器就是 Managed Node，透過 SSH 來做連線。但什麽是 `Inventory` 跟 `Playbooks` 呢？\n\n\u003cbr\u003e\n\n### 什麼是 Ansible Inventory\n\n`Inventory` 這個單字本身有**詳細目錄**、**清單**和**列表**的意思。在這裡我們可以把它理解成一份主機列表，可以透過它來定義每個 Managed Node 的代號、IP 位址、連線設定和群組。\n\n```sh\n$ vim hosts\n# ansible_ssh_host：遠端 SSH 主機位址\n# ansible_ssh_port：遠端 SSH Port\n# ansible_ssh_user：遠端 SSH 使用者名稱\n# ansible_ssh_private_key_file：本機 SSH 私鑰檔案路徑\n# ansible_ssh_pass：遠端 SSH 密碼 (建議使用私鑰)\n\n[local]\nserver1 ansible_ssh_host=127.0.0.1  ansible_ssh_port=55000 ansible_ssh_pass=docker\n```\n所以我們可以在這邊輸入很多個主機來做管理，可以把它想成一個設定檔。\n\n\u003cbr\u003e\n\n\n### 什麼是 Ansible Playbooks\n\n再談 Ansible Playbooks 之前，先說明我們要怎麼去操作 Ansible？一般來說，我們可以使用 Ad-Hoc Commands 和 Playbooks 兩種方式來操作 Ansible。\n\n\u003cbr\u003e\n\n#### Ad-Hoc Commands 是什麼？\n\nAd hoc 可以翻譯成**簡短地指令**，也就是我們常用的指令模式，最常見的 `ping`和`echo` 為例。\n\n* `ping`\n\n```sh\n$ ansible all -m ping\n\nserver1 | SUCCESS =\u003e {\n    \"ansible_facts\": {\n        \"discovered_interpreter_python\": \"/usr/bin/python\"\n    },\n    \"changed\": false,\n    \"ping\": \"pong\"\n}\n```\n\n\u003cbr\u003e\n\n* `echo`\n\n```sh\n$ ansible all -m command -a \"echo Hello World\"\n\nserver1 | CHANGED | rc=0 \u003e\u003e\nHello World\n```\n從上面的例子中可以看到 Ad-Hoc Commands 一次只能處理一件事情，這是它與 Playbooks 最大的差異。\n\n\u003cbr\u003e\n\n### Playbooks 是什麼？\n\nPlaybooks 就是字面上的意思為**劇本**，我們可以先透過寫好的**劇本 (Playbooks)** 來讓各個 Managed Node 進行指定的**動作 (Plays)** 和**任務 (Tasks)**。\n\n簡而言之，Playbooks 就是 Ansible 的腳本 (Script)，而且比傳統 Shell Script 還要強大好幾百倍的腳本！此外它是使用 **YAML** 格式，寫 Code 就如同寫文件一樣，簡單易讀。\n\n有關詳細的**動作 (Plays)** 和**任務 (Tasks)**，等我們實際安裝好再來說明 😆\n\n\u003cbr\u003e\n\n## Ansible 安裝與實作\n\n安裝之前先讓大家看一下版本吧！大家要記得檢查自己的版本與教學是否相同，如果不同，記得要先查看官網是否有修改內容。\n\n### 版本\n\n* macOS：11.6\n* Docker：Docker version 20.10.14, build a224086\n* Aansible：ansible [core 2.12.5]\n\n\u003cbr\u003e\n\n### 如何安裝 Ansible 在控制主機\n\n由於 Ansible 是一套開源的軟體，所以在目前大部分主流作業系統上都可以透過對應的套件管理 (package manager) 進行安裝。\n\n本人使用 macOS ，所以這邊僅列出 masOS 安裝方式，其他的可以參考[官方的安裝指南](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-specific-operating-systems)。\n\n\u003cbr\u003e\n\nmacOS 安裝可以使用兩種方式，官方較推薦使用 `pip` 來做安裝：\n\n* [Pip Install Packages (pip 官方較推薦)](https://pip.pypa.io/en/stable/#)\n\n```sh\n$ sudo pip install ansible\n```\n\n\u003cbr\u003e\n\n* [Homebrew (brew)](https://formulae.brew.sh/formula/ansible#default)\n\n```sh\n$ sudo brew install ansible\n```\n\n\u003cbr\u003e \n\n安裝完後，可以使用 `--version` 指令來檢查是否安裝完成：\n\n```\n$ ansible --version\n\nansible [core 2.12.5]\n  config file = None\n  configured module search path = ['/Users/ian_zhuang/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']\n  ansible python module location = /usr/local/Cellar/ansible/5.7.1/libexec/lib/python3.10/site-packages/ansible\n  ansible collection location = /Users/ian_zhuang/.ansible/collections:/usr/share/ansible/collections\n  executable location = /usr/local/bin/ansible\n  python version = 3.10.4 (main, Apr 26 2022, 19:43:24) [Clang 13.0.0 (clang-1300.0.29.30)]\n  jinja version = 3.1.2\n  libyaml = True\n```\n\n\u003cbr\u003e\n\n### 如何安裝 Ansible 在被控節點\n\n不需要！！！ 透過 Ansible 進行管理的被控節點完全不需要安裝 Ansible。我們只需要確保這個節點可以透過 SSH 與控制主機做溝通，並安裝 Python 2.6 以上版本就可以透過控制主機來進行部署及管理了。\n\n\u003cbr\u003e\n\n那我們為了要模擬，所以我們使用 Docker 來模擬 Managed Node，首先老樣子，一樣先寫一個 Dockerfile 來建立我們的映像檔，此映像檔是微調 [chusiang/ansible-managed-node.dockerfile](https://github.com/chusiang/ansible-managed-node.dockerfile/blob/master/ubuntu-14.04/Dockerfile) 的內容，修改 ubuntu 版本以及內容作調整，我會把程式碼放在 [GitHub 連結](https://github.com/880831ian/Ansible) ，以及 [DockerHub 連結](https://hub.docker.com/r/880831ian/ansible-ubuntu-server)，歡迎大家前去下載使用。\n\n\u003cbr\u003e\n\n```dockerfile\nFROM ubuntu:22.10\n\nLABEL maintainer=\"880831ian@gmail.com\"\n\n# Update the index of available packages.\nRUN apt-get update\n\n# Install the requires package.\nRUN apt-get install -y openssh-server sudo curl wget bash-completion openssl \u0026\u0026 apt-get clean\n\n# Setting the sshd.\nRUN mkdir /var/run/sshd\nRUN echo 'root:root' | chpasswd\nRUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config\n\n# SSH login fix. Otherwise user is kicked off after login\nRUN sed 's@session\\s*required\\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd\n\nENV NOTVISIBLE \"in users profile\"\nRUN echo \"export VISIBLE=now\" \u003e\u003e /etc/profile\n\n# Create a new user.\n#\n# - username: docker\n# - password: docker\nRUN useradd --create-home --shell /bin/bash \\\n      --password $(openssl passwd -1 docker) docker\n\n# Add sudo permission.\nRUN echo 'docker ALL=(ALL) NOPASSWD: ALL' \u003e\u003e /etc/sudoers\n\n# Setting ssh public key.\nRUN wget https://raw.githubusercontent.com/chusiang/ansible-jupyter.dockerfile/master/files/ssh/id_rsa.pub \\\n      -O /tmp/authorized_keys \u0026\u0026 \\\n      mkdir /home/docker/.ssh \u0026\u0026 \\\n      mv /tmp/authorized_keys /home/docker/.ssh/ \u0026\u0026 \\\n      chown -R docker:docker /home/docker/.ssh/ \u0026\u0026 \\\n      chmod 644 /home/docker/.ssh/authorized_keys \u0026\u0026 \\\n      chmod 700 /home/docker/.ssh\n\nEXPOSE 22\n\n# Run ssh server daemon.\nCMD [\"/usr/sbin/sshd\", \"-D\"]\n```\n\n\u003cbr\u003e\n\n接下來將它包成 Image 並啟動他：\n\n```sh\n$ docker build -t ansible-ubuntu-server . \u0026\u0026 docker run --name server1 -d -p 8888:22 ansible-ubuntu-server\n\n64c51235e34a7ba42c0c45e690201dd80248c9aac76c3b855c99cf63f7f0af7c\n```\n\n\u003cbr\u003e\n\n可以用 `exec` 進入容器：\n\n```sh\ndocker exec -it server1 /bin/bash\n```\n\n\u003cbr\u003e\n\n### 如何讓 Ansible 操控 Docker 容器？\n\n我們在工作目錄下，新增一個 `ansible.cfg`：\n\n```cfg\n[defaults]\n\ninventory = hosts\nremote_user = docker\nhost_key_checking = False\n```\n\n\u003cbr\u003e\n\n設定 inventory hosts：\n\n```hosts\n[local]\nserver1 ansible_ssh_host=127.0.0.1  ansible_ssh_port=8888 ansible_ssh_pass=docker\n```\n其中 8888 是我們在啟動時所開放的 Port，也可以自行更改。\n* `ansible_ssh_host`：設為本機的 IP。\n* `ansible_ssh_port`：設為 `docker ps `取得的 SSH Port 也就是我們的 8888。\n* `ansible_ssh_pass`：因為我們沒有連線用的金鑰，所以直接使用密碼方式做連結。(建議只用於練習環境使用) \n\n\u003cbr\u003e\n\n### Hello World On Managed Node\n\n當我們都設置完成後，就可以使用 Terminal 用 Docker 建立好的 Ansible 來練習了！\n\n```sh\n$ ansible all -m command -a 'echo Hello World on Docker.'\n\nserver1 | CHANGED | rc=0 \u003e\u003e\nHello World on Docker.\t\n```\n\n\u003cbr\u003e\n\n## 第一個 Playbook\n\n在我們都安裝好後，要來說說我們剛剛有偷偷提到的 Playbooks 的動作 (Plays) 和任務 (Tasks)。在一份 Playbooks 裡面，可以有多個 Play、多個 Task 和多個 Module：\n\n* Play：通常為某個特定的目的，例如：\n\t* `Setup a official website with Drupal` 藉由 Drupal 建置官網\n\t* `Restart the API Service` 重開 API 服務\n* Task：要實行 Play 這個目的所需做的每個步驟，例如：\n\t* `Install the Nginx` 安裝 Nginx\n\t* `Kill the djnago process` 強制停止 django 的行程\n* Module：Ansible 所提供的各種操作方式，例如：\n\t* `apt: name=vim state=present` 使用 apt 套件安裝 vim\n\t* `command: /sbin/shutdown -r now` 使用 shutdown 的指令關機\n\n\u003cbr\u003e\n\n有點聽不懂吧！我來舉個例子，我們最熟悉的 Hello World，先建立一個 `helloworld.yaml` 的檔案：\n\n\n```yaml\n---\n\n- name: say 'hello world'\n  hosts: all\n  tasks:\n\n    - name: echo 'hello world'\n      command: echo 'hello world'\n      register: result\n\n    - name: print stdout\n      debug:\n        msg: \"{{ result.stdout }}\"\n```\n\n可以看到這整個就是 Play，我們想要達到 say 'hello world' 的目的，其中有兩個 name 分別代表兩個 Task，也就是達成 Play 目的所需得步驟。最後 command 與 debug 就是我們的 Module 要怎麼達成這兩個步驟的操作方式。\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/playbooks.gif)\n\n\u003cbr\u003e\n\n我們使用 `ansible-playbook` 執行 Playbook，在這個範例中，我們執行了１個 Play、3 個 Tasks 和 2 個 Modules：\n\n\u003cbr\u003e\n\n```sh\n$ ansible-playbook helloworld.yaml\n```\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/helloworld.png)\n\n\u003cbr\u003e\n\n我們剛剛明明只寫兩個 tasks，為什麼執行就變成三個 tasks？\n\n這是因為 Ansible 預設會使用 `Setup` task 來取得 Managed node 的 facts。關於 facts 的詳細說明，請滑到後面 [取得-managed-node-的-facts](#取得-managed-node-的-facts) 觀看😬\n\n\u003cbr\u003e\n\n那如果沒有 Ansible 時，我們是怎麼操作的？我會附上 Shell Script 的做法，我們來比較看看吧！\n\n\u003cbr\u003e\n\n* **Shell Script** 建立 `helloworld.sh` 檔案\n\n```sh\n#! /bin/bash\necho \"Hello World\"\n```\n\n\u003cbr\u003e\n\n* 執行 `helloworld.sh`\n\n```sh\n./ helloworld.sh\nHello World\n```\n\n\u003cbr\u003e\n\n看起來 Shell Script 已經夠用了，為什麼還要寫 Playbook 呢？這邊整理幾個理由給大家參考：\n1. 用 Ansible 的 Module 可以把很多複雜的指令給標準化，例如不同的 Linux 發行版本在安裝套件時需代各種不同的參數。\n2. 在現有的雲原生 (cloud native) 的架構下，傳統的 Shell Script 已經不敷使用，一般而言 Shell Script 只能對一台機器 (instance) 進行操作。\n\n\u003cbr\u003e\n\n## 常用的 Ansible Module 有哪些？\n\n接下來簡單介紹一下比較常用到的 8 個 Module：\n\n### `ansible.builtin.apt`\n[apt module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html#ansible-collections-ansible-builtin-apt-module) 是給 Debian, Ubuntu 等作業系統使用的套件模組 (Packing Modules)，我們可以透過它管理 apt 套件。類似的有 `apt-get`、`dpkg`等。\n\n1. 更新套件索引(快取)，等同於 `apt-get update` 指令\n\n```yaml\n- name: Update repositories cache\n  ansible.builtin.apt:\n    update_cache: yes\n```\n\n\u003cbr\u003e\n\n2. 安裝 vim 套件\n\n```yaml\n- name: Install the package \"vim\"\n  ansible.builtin.apt:\n    name: vim\n    state: present\n```\n\n\u003cbr\u003e\n\n3. 移除 nano 套件\n\n```yaml\n - name: Remove \"nano\" package\n   ansible.builtin.apt:\n     name: nano\n     state: absent\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.command`\n[command module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html#ansible-collections-ansible-builtin-command-module) 是可以在遠端上執行指令的指令模組，但它不支援變數 (variables) 和 `\u003c`、`\u003e`、`|`、`;`、`\u0026`，若有這類需求要改用 `shell` module。\n\n1. 重新開機\n\n```yaml\n- name: Reboot at now\n  ansible.builtin.command: /sbin/shutdown -r now\n```\n\n\u003cbr\u003e\n\n2. 當某個檔案不存在時才執行指令\n\n```yaml\n- name: create .ssh directory\n  ansible.builtin.command: mkdir .ssh creates=.ssh/\n```\n\n\u003cbr\u003e\n\n3. 先切換目錄再執行指令\n\n```yaml\n- name: cat /etc/passwd\n  ansible.builtin.command: cat passwd\n  args:\n    chdir: /etc\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.copy`\n\n[copy moudule](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html#ansible-collections-ansible-builtin-copy-module) 是從本地複製檔案到遠端的檔案模組，若有使用變數需求，可以改用 `template`。它類似 Linux 指令的 `scp`。\n\n1. 複製 ssh public key 到遠端 (chmod 644 /target/file)\n\n```yaml\n- name: copy ssh public key to remote node\n  ansible.builtin.copy:\n    src: files/id_rsa.pub\n    dest: /home/docker/.ssh/authorized_keys\n    owner: docker\n    group: docker\n    mode: 0644\n```\n\n\u003cbr\u003e\n\n2. 複製 ssh public key 到遠端 (chmod u=rw,g=r,o=r /target/file)\n\n```yaml\n- name: copy ssh public key to remote node\n  ansible.builtin.copy:\n    src: files/id_rsa.pub\n    dest: /home/docker/.ssh/authorized_keys\n    owner: docker\n    group: docker\n    mode: \"u=rw,g=r,o=r\"\n```\n\n\u003cbr\u003e\n\n3. 複製 nginx vhost 設定檔到遠端，並備份原有的檔案\n\n```yaml\n- name: copy nginx vhost and backup the original\n  ansible.builtin.copy:\n    src: files/ironman.conf\n    dest: /etc/nginx/sites-available/default\n    owner: root\n    group: root\n    mode: 0644\n    backup: yes\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.file`\n\n[file module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/file_module.html#ansible-collections-ansible-builtin-file-module) 是在遠端建立和刪除檔案 (file)、目錄 (directory) 和軟連結 (symlinks) 的檔案模組。它類似的 Linux 指令為 `chown`、`mkdir` 和 `touch`。\n\n1. 建立檔案 (touch)，並設定權限為 644\n\n```yaml\n- name: touch a file, and set the permissions\n  ansible.builtin.file:\n    path: /etc/motd\n    state: touch\n    mode: \"u=rw,g=r,o=r\"\n```\n\n\u003cbr\u003e\n\n2. 建立目錄 (mkdir)，並設定檔案擁有者為 docker\n\n```yaml\n- name: create a directory, and set the permissions\n  ansible.builtin.file:\n    path: /home/docker/.ssh/\n    state: directory\n    owner: docker\n    mode: \"700\"\n```\n\n\u003cbr\u003e\n\n3. 建立軟連結 (ln)\n\n```yaml\n- name: create a symlink file\n  ansible.builtin.file:\n    src: /tmp\n    dest: /home/docker/tmp\n    state: link\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.lineinfile`\n\n[lineinfile](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html) module 是個可用正規表示法對檔案進行插入或取代文字的檔案模組。它類似的 Linux 指令是 `sed`。\n\n1. 移除 docker 使用者的 sudo 權限\n\n```yaml\n- name: remove sudo permission of docker\n  ansible.builtin.lineinfile:\n    dest: /etc/sudoers\n    state: absent\n    regexp: '^docker'\n```\n\n\u003cbr\u003e\n\n2. 在 /etc/hosts 檔案裡用 127.0.0.1 localhost 取代開頭為 127.0.0.1 的一行\n\n```yaml\n- name: set localhost as 127.0.0.1\n  ansible.builtin.lineinfile:\n    dest: /etc/hosts\n    regexp: '^127\\.0\\.0\\.1'\n    line: '127.0.0.1 localhost'\n    owner: root\n    group: root\n    mode: 0644\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.service`\n\n[service module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html#ansible-collections-ansible-builtin-service-module) 是用來管理遠端系統服務的系統模組。它類似的 Linux 指令為 `service`。\n\n1. 啟動 Nginx\n\n```yaml\n- name: start nginx service\n  ansible.builtin.service:\n    name: nginx\n    state: started\n```\n\n\u003cbr\u003e\n\n2. 停止 Nginx\n\n```yaml\n- name: stop nginx service\n  ansible.builtin.service:\n    name: nginx\n    state: stopped\n```\n\n\u003cbr\u003e\n\n3. 重開網路服務\n\n```yaml\n- name: restart network service\n  ansible.builtin.service:\n    name: network\n    state: restarted\n    args: eth0\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.shell`\n\n[shell module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html#ansible-collections-ansible-builtin-shell-module) 是可以在遠端用 `/bin/sh` 執行指令的指令模組，支援變數 (variables) 和 `\u003c`、`\u003e`、`|`、`;` 和 `\u0026` 等運算。\n\n1. 藉由 `ls` 和 `wc` 檢查檔案數量\n\n```yaml\n- name: check files number\n  ansible.builtin.shell: ls /home/docker/ | wc -l\n```\n\n\u003cbr\u003e\n\n2. 把所有的 Python 行程給砍掉\n\n```yaml\n- name: kill all python process\n  ansible.builtin.shell: kill -9 $(ps aux | grep python | awk '{ print $2 }')\n```\n\n\u003cbr\u003e\n\n### `ansible.builtin.stat`\n\n[stat module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/stat_module.html#ansible-collections-ansible-builtin-stat-module) 是用來檢查檔案狀態的檔案模組。其類似的 Linux 指令為 `stat`。\n\n1. 檢查檔案是否存在，若不存在則建立他。\n\n```yaml\n- name: check the 'vimrc' target exists\n  ansible.builtin.stat:\n    path: /home/docker/.vimrc\n  register: stat_vimrc\n\n- name: touch vimrc\n  file:\n    path: /home/docker/.vimrc\n    ansible.builtin.state: touch\n          mode: \"u=rw,g=r,o=r\"\n  when: stat_vimrc.stat.exists == false\n```\n\n\u003cbr\u003e\n\n2. 取的某檔案的 md5sum\n\n```yaml\n- name: Use md5sum to calculate checksum\n  ansible.builtin.stat:\n    path: /path/to/something\n    checksum_algorithm: md5sum\n```\n\n\u003cbr\u003e\n\n### 其他\n\n其他還有很多可以使用的 Module ，詳情可以查看 [Ansible.Builtin](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html)。\n\n\u003cbr\u003e\n\n## Ansible 發送通知到 Telegram Bot\n\n剛剛看了很多內建的模組，當然 Ansible 還有很多好玩的模組可以使用，我們就跟 [使用 Jenkins 設定 GitHub 觸發程序並通知 Telegram Bot 文章](https://pin-yi.me/jenkins/) 一樣，將我們取得的內容傳送到 Telegram Bot 吧！那首先我們要先創造一個 Telegram Bot，在 Telegram 找到一個機器人叫 `BotFather` 的官方機器人帳號。並使用指令 `/newbot`，會看到一下畫面：\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/telegram_1.png)\n\n\u003cbr\u003e\n\n他詢問你要幫機器人取叫什麼名稱，可以直接在輸入欄位輸入想要取的名稱，當然不能是別人已經取過的：\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/telegram_2.png)\n\n看到它回覆你 `Done!` 代表成功了，接下來你會拿到一組 API Token，像我的是 `5335968936:AAEDO_Tudhy0t577jtbF9TpgrzqOsL99h9c` (已更換，大家放心 😂 )，接下來開啟瀏覽器輸入以下網址 `https://api.telegram.org/bot{API Token}/getupdates`，其中的 `{API Token}` 請帶入自己的 Token，直到出現 `{\"ok\":true,\"result\":[]}` 代表完成。\n\n\u003cbr\u003e\n\n接下來開啟你自己的 Bot ，打上 `/start` 指令，重新整理剛剛的網頁就可以看到以下這樣的文字：\n\n```\n{\"ok\":true,\"result\":[{\"update_id\":606594112,\"message\":{\"message_id\":1,\"from\":{\"id\":493995679,\"is_bot\":false,\"first_name\":\"\\u54c1\\u6bc5\",\"last_name\":\"Ian\",\"username\":\"pinyichuchu\",\"language_code\":\"zh-hans\"},\"chat\":{\"id\":493995679,\"first_name\":\"\\u54c1\\u6bc5\",\"last_name\":\"Ian\",\"username\":\"pinyichuchu\",\"type\":\"private\"},\"date\":1652695148,\"text\":\"/start\",\"entities\":[{\"offset\":0,\"length\":6,\"type\":\"bot_command\"}]}}\n```\n\n這是你傳訊息給 Bot 它所收到的 API，資料很多沒關係，我們找到 `id`，像我的是 `493995679`，這個就是我跟機器人的聊天室，我們就先回到 Ansible 這邊吧！\n\n\u003cbr\u003e\n\n開啟一個新的檔案叫 `send_notify_tg.yaml`，打以下內容：\n\n```yaml\n---\n- name: Send notify\n  hosts: all\n  tasks:\n    - name: Send notify to Telegram\n      community.general.telegram:\n        token: \"9999999:XXXXXXXXXXXXXXXXXXXXXXX\"\n        api_args:\n          chat_id: 000000\n          parse_mode: \"markdown\"\n          text: \"Your precious application has been deployed: https://example.com\"\n          disable_web_page_preview: True\n          disable_notification: True\n```\n可以看到我們使用的模組不是 Ansible 內建的，而是社群別人寫的，詳細可以參考 [community.general.telegram module – module for sending notifications via telegram](https://docs.ansible.com/ansible/latest/collections/community/general/telegram_module.html#ansible-collections-community-general-telegram-module)：\n\n\u003cbr\u003e\n\n其中 token 就是我們剛剛在 `BotFather` 那邊所拿到的 Token，chat_id 就是我們剛剛在網頁上看到的 id，把資料都輸入進去後，我們可以修改 text 內容，改成 \"Send notify to Telegram 測試傳送通知\"，接著執行 `ansible-ploybook send_notify_tg.yaml` ，看看能不能正常收到通知！ \n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/telegram_3.png)\n\n\u003cbr\u003e\n\n我們可能需要將機器人加入群組內，這時候需要更換一下 chat_id，先將機器人加入群組，再次到剛剛瀏覽器的網頁刷新，查看 chat 後面的 id 帶有 `-`，像是 `-540226836` 這樣，這個就是該群組的 ID，將 send_notify_tg.yaml 的 chat_id 修改成 `-540226836` 在測試看看，他就會在群組中發送通知囉！\n\n```\n{\"update_id\":606594124,\"message\":{\"message_id\":14,\"from\":{\"id\":493995679,\"is_bot\":false,\"first_name\":\"\\u54c1\\u6bc5\",\"last_name\":\"Ian\",\"username\":\"pinyichuchu\",\"language_code\":\"zh-hans\"},\"chat\":{\"id\":-540226836,\"title\":\"\\u54c1\\u6bc5 \u0026 AnsibleSendMessageBot\",\"type\":\"group\",\"all_members_are_administrators\":true},\"date\":1652696181,\"group_chat_created\":true}}\n```\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/telegram_4.png)\n\n\u003cbr\u003e\n\n## 取得 Managed node 的 facts \n\n還記得我們在執行任務 (Tasks) 時，明明只有兩個，但最後結果顯示三個嗎？是因為在使用 Playbooks 時，Ansible 會自動執行 [Setup module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html#ansible-collections-ansible-builtin-setup-module) 以蒐集各個 Managed node 的 **facts**。 這個 facts 就好比是系統變數一樣，從 IP 位址、作業系統、CPU 等資訊應有盡有。\n\n\u003cbr\u003e\n\n### Ad-Hoc Commands\n\n通常我們都會先使用 Ad-Hoc Commands 來呼叫 `setup` 看看有哪些可用的資訊，這對於我們稍後撰寫較為複雜的 Playbooks 會很有幫助。\n\n1. 可以藉由 ``less`` 快速搜尋所有的變數\n\n```sh\n$ ansible all -m setup | less\n\nserver1 | SUCCESS =\u003e {\n    \"ansible_facts\": {\n        \"ansible_apparmor\": {\n            \"status\": \"disabled\"\n        },\n        \"ansible_architecture\": \"x86_64\",\n        \"ansible_bios_date\": \"03/14/2014\",\n        \"ansible_bios_vendor\": \"BHYVE\",\n        \"ansible_bios_version\": \"1.00\",\n        \"ansible_board_asset_tag\": \"NA\",\n        \"ansible_board_name\": \"NA\",\n        \"ansible_board_serial\": \"NA\",\n        \"ansible_board_vendor\": \"NA\",\n        \"ansible_board_version\": \"NA\",\n```\n\n\u003cbr\u003e\n\n2. 搭配 `filter` 將發行版本 (distribution) 資訊給過濾出來\n\n```sh\n$ ansible all -m setup -a \"filter=ansible_distribution*\"\n\nserver1 | SUCCESS =\u003e {\n    \"ansible_facts\": {\n        \"ansible_distribution\": \"Ubuntu\",\n        \"ansible_distribution_file_parsed\": true,\n        \"ansible_distribution_file_path\": \"/etc/os-release\",\n        \"ansible_distribution_file_variety\": \"Debian\",\n        \"ansible_distribution_major_version\": \"22\",\n        \"ansible_distribution_release\": \"kinetic\",\n        \"ansible_distribution_version\": \"22.10\",\n        \"discovered_interpreter_python\": \"/usr/bin/python3\"\n    },\n    \"changed\": false\n}\n```\n\n\u003cbr\u003e\n\n3. 取得套件管理員的種類資訊，例子中取得的值是 **apt**\n\n```sh\n$ ansible all -m setup -a \"filter=ansible_pkg_mgr\"\n\nserver1 | SUCCESS =\u003e {\n    \"ansible_facts\": {\n        \"ansible_pkg_mgr\": \"apt\",\n        \"discovered_interpreter_python\": \"/usr/bin/python3\"\n    },\n    \"changed\": false\n}\n```\n\n\u003cbr\u003e\n\n### 轉寫 Playbooks\n\n我來出個題目，我想要知道 Ansible 所使用的公鑰，並透過 Telegram Bot 發送到群組，要怎麼做呢！？\n\n首先要利用剛剛的 Ad-Hoc Commands filter，找到公鑰，再將公鑰透過 Telegram Bot 傳送，所以我們會有兩個 Tasks，那我們開始實作囉 🤓\n\n1.找到公鑰\n\n```yaml\n---\n- name: Filter rsa_public \u0026 Send notify\n  hosts: all\n  tasks:\n    - name: Filter setup rsa_public key\n      ansible.builtin.setup:\n        filter:\n          - \"ansible_ssh_host_key_rsa_public\"\n      register: result\n```\n可以看到我們將 filter setup 從 Ad-Hoc 轉成 Playbooks，並使用 result 來存在找到的公鑰。\n\n\u003cbr\u003e\n\n2. 發送通知至 Telegram Bot\n\n```yaml\n    - name: Send notify to Telegram\n      community.general.telegram:\n        token: \"5335968936:AAFhxxMRJy-rucGKgSE80Xss7qPq2iOHWlc\"\n        api_args:\n          chat_id: -540226836\n          parse_mode: \"markdown\"\n          text: \"{{ result }}\"\n          disable_web_page_preview: True\n          disable_notification: True\n```\n老樣子，我們就使用上次 `send_notify_tg.yaml` 內的 Send notify to Telegram 任務來傳送通知。\n\n\u003cbr\u003e\n\n執行後，看看群組是否有收到我們找到的 ansible_ssh_host_key_rsa_public 通知。\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/telegram_5.png)\n\n\u003cbr\u003e\n\n## 使用 Ansible 的 Template 系統\n\n[Template module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html#ansible-collections-ansible-builtin-template-module) 是常使用的檔案模組之一，我們在 [常用的 Ansible Module 有哪些？](#常用的-ansible-module-有哪些) 文章中有提到，可以用它和變數 (Variables) 來操作檔案。\n\n我們只需要事先定義變數和模板 (Templates)，即可用它動態產生遠端的 Shell Script、設定檔 (Configure)等。換句話說，我們可以用一份 template 來開發 (Development)、測試 (Test)、正式環境 (Production) 等不同環境設定。\n\n舉例說明：\n\n1. 建立 template 檔案\n\n```j2\n$ vim hello_world.txt.j2\nHello \"{{ dynamic_word }}\"\n```\n* 由於 Ansible 是就由 [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/) 來實作 template 系統，所以需要使用 `*.j2` 的副檔名。\n* 上面的 `\"{{ dynamic_word }}\"\"` 代表我們在 template 裡使用了名為 `dynameic_word` 的變數。\n\n\u003cbr\u003e\n\n2. \t建立 playbook，並加入變數 `vim template_demo.yaml`\n\n```yaml\n---\n- name: Play the template module\n  hosts: localhost\n  vars:\n    dynamic_word: \"World\"\n  tasks:\n    - name: generation the hello_world.txt file\n      ansible.builtin.template:\n        src: hello_world.txt.j2\n        dest: /tmp/hello_world.txt\n\n    - name: show file context\n      command: cat /tmp/hello_world.txt\n      register: result\n\n    - name: print stdout\n      debug:\n        msg: \"{{ result.stdout_lines }}\"\n```\n* 在第 5 行，我們幫 `dynamic_word` 變數設了一個預設值 `World`。\n* 在 8 行的第 1 個 task 裡，我們使用 template module，並指定了檔案的來源 (src) 和目的地 (dest)。\n* 之後的 2 個 task 則是把 template module 產生的檔案給印出來。\n\n\u003cbr\u003e\n\n3. 直接使用 `ansible-playbook template_demo.yaml` 執行 Playbook。\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/template.png)\n\n\u003cbr\u003e\n\n也可以透過 `-e` 參數將 `dynamic_word` 覆寫成 \"ansible\"\n\n\u003cbr\u003e\n\n```sh\n $ ansible-playbook template_demo.yaml -e \"dynamic_word=ansible\"\n```\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/template_1.png)\n\n\u003cbr\u003e\n\n\n### 如何切換不同環境\n\n\n1. 除了我們剛剛用 `vars` 來宣告變以外，還可以使用 `vars_files` 來 include 其他的變數檔：`$ vim template_demo2.yaml`\n\n```yaml\n---\n- name: Play the template module\n  hosts: localhost\n  vars:\n    env: \"development\"\n\n  vars_files:\n    - vars/{{ env }}.yml\n\n  tasks:\n    - name: generation the hello_world.txt file\n      ansible.builtin.template:\n        src: hello_world.txt.j2\n        dest: /tmp/hello_world.txt\n\n    - name: show file context\n      command: cat /tmp/hello_world.txt\n      register: result\n\n    - name: print stdout\n      debug:\n        msg: \"{{ result.stdout_lines }}\"\n```\n可以看到上面例子中第 7 行，就是我們使用 `vars_files` 來 include 其他的變數檔。\n\n\u003cbr\u003e\n\n2. 建立 `vars/development.yaml`、`vars/test.yaml`、`vars/production.yaml` 檔案，接下來將依不同得環境 include 不同的檔案變數檔案 (vars files)，這樣就可以用一份 Playbook 切換環境了！\n\n* Development\n\n```sh\n $ vim vars/development.yaml\n dynamic_word: \"development\"\n```\n\n* Test\n\n```sh\n $ vim vars/test.yaml\n dynamic_word: \"test\"\n```\n\n* Production\n\n```sh\n $ vim vars/production.yaml\n dynamic_word: \"production\"\n```\n\n\u003cbr\u003e\n\n3. 執行 `ansible-playbook template_demo2.yaml -e \"dynamic_word=Test\"`，並有 `-e` 去修改各個環境。\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/template_2.png)\n\n\u003cbr\u003e\n\nTemplate 系統是實務上很常見的手法之一，藉由它我們可以很輕鬆地讓開發、測試、正式環境無縫接軌。但若是在大型的 Playbook 裡切換環境，建議使用較為進階的 `group_vars` 跟 `host_vars`。\n\n\u003cbr\u003e\n\n## 在 Playbooks 使用 Handlers\n\n[Handlers](https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html) 是我們在 Ansible Playbooks 裡很常用來重開系統服務 (Service) 的手法，我們這邊透過安裝 Nginx 來介紹它。\n\n那什麼是 Handlers 呢？Handler 本身是一種非同步的 callback function ; 在這裡則是指關聯於特定 tasks 的事件 (event) 觸發機制。當這些特定的 tasks 狀態為 **被改變 (changed)** 且都已被執行，才會觸發一次的 event。\n\n\u003cbr\u003e\n\n1. 我們建立 setup_nginx.yaml\n\n```yaml\n---\n- name: setup the nginx\n  hosts: all\n  become: true\n  vars:\n    username: \"PinYi\"\n    mail: \"880831ian@gmail.com\"\n    blog: \"https://pin-yi.me\"\n\n  tasks:\n    # 執行 'apt-get update' 指令。\n    - name: update apt repo cache\n      apt: name=nginx update_cache=yes\n\n    # 執行 'apt-get install nginx' 指令。\n    - name: install nginx with apt\n      apt: name=nginx state=present\n\n    # 於網頁根目錄 (DocumentRoot) 編輯 index.html。\n    - name: modify index.html\n      ansible.builtin.template: src=templates/index.html.j2\n        dest=/var/www/html/index.html\n        owner=www-data\n        group=www-data\n        mode=\"644\"\n        backup=yes\n      notify: restart nginx\n\n  # handlers\n  #\n  # * 當確認事件有被觸發才會動作。\n  # * 一個 handler 可被多個 task 通知 (notify)，並於 tasks 跑完才會執行。\n  handlers:\n    # 執行 'sudo service nginx restart' 指令。\n    - name: restart nginx\n      service: name=nginx enabled=yes state=restarted\n\n  # post_tasks:\n  #\n  # 在 tasks 之後執行的 tasks。\n  post_tasks:\n    # 檢查網頁內容。\n    - name: review http state\n      command: \"curl -s http://localhost\"\n      register: web_context\n\n    # 印出檢查結果。\n    - name: print http state\n      debug: msg={{ web_context.stdout_lines }}\n```\n來說明一下上面這個 yaml 檔案：\n\n* 首先我們想要安裝 Nginx，我們給了三個參數，分別是 username、mail、blog，等等會帶入我們的 template。\n* 我們一開始有 3 個 task，分別代表執行更新、安裝、編輯 index.html 檔案。\n* 以及 1 個 handlers 他會等 `modify index.html` 有改變且執行後才會動作。\n* 最後是 post_tasks 他是等 tasks 之後執行的 tasks。\n\n\u003cbr\u003e\n\n2. 接下建立 Nginx html 的 template：`vim templates/index.html.j2`\n\n```yaml\n _____________________________________\n/ This is a ansible-playbook demo for \\\n\\ automate-with-ansible at 2022/05/17./\n -------------------------------------\n        \\   ^__^\n         \\  (oo)\\_______\n            (__)\\       )\\/\\\n                ||----w |\n                ||     ||\n[ {{ username }}@automate-with-ansible ~ ]$\n[ {{ username }}@automate-with-ansible ~ ]$\n[ {{ username }}@automate-with-ansible ~ ]$ cat .profile\n- {{ mail }}\n- {{ blog }}\n```\n\n\u003cbr\u003e\n\n3. 執行 Playbook\n\n可以看到因為我們 `modify index.html` 沒有被改變，notify 沒有通知 handlers，所以他不會執行 handlers 該段程式。(正常來說，修改 html 不需要重啟，此為範例🤣 )\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/handlers_1.png)\n\n\u003cbr\u003e\n\n4. 那我們修改一下 index.html 來測試一下會不會把 index.html 的狀態**被改變**，而讓 handlers 執行呢！我們隨意修改 index.html 內容，修改日期改成 05/17：\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/handlers_2.png)\n\n可以看到我們的 `modify index.html` 被改變了，所以 notify 通知 handlers 執行重新啟動。\n\n\u003cbr\u003e\n\n## 在 Playbooks 使用 loops\n\n在 Shell Script 中，我們會使用 for 和 while 等迴圈 (loop) 來簡化重複的程式碼，而在 Ansible 我們也可以使用 loop 來簡化**重複的任務 (Tasks)**。\n\n### 標準迴圈\n\n首先我們先以簡單的方式重複印出三筆資料。\n\n* Shell Script\n\n1. 建立 for loop 的 Script\n\n```sh\n$ vim bash_loop.sh\n\n#!/bin/bash\nfor x in 0 1 2; do\n        echo Loop $x\ndone\n```\n* 在第 4 行，我們用 `for`，並\b代入 0,1,2 三個值到 `$x` 變數\n* 在第 5 行，則用了 `echo`，印出訊息和 `$x` 變數\n\n\u003cbr\u003e\n\n2. 執行 Script：可以看到底下跑了 3 次的 loop\n\n```sh\n$ chmod a+x bash_loop.sh\n$ ./bash_loop.sh\n\nLoop 0\nLoop 1\nLoop 2\n```\n\n\u003cbr\u003e\n\n* Ansible Playbooks\n\n我們需要透過 `item` 和 `with_items` 來使用 Ansible 的 loop，其 `item` 為預設名。在 Ansible 2.5 中添加了 `loop`，所以我們後續兩者都會提到 (目前兩者都可以使用！)\n\n1. 建立 loop 的 playbook `vim playbook_with_items.yaml`\n\n```yaml\n---\n- name: a basic loop with playbook\n  hosts: localhost\n  tasks:\n    - name: print loop message\n      ansible.builtin.debug:\n        msg: \"Loop {{ item }}\"\n      with_items:\n        - 0\n        - 1\n        - 2\n```\n* 在第 6、7 行裡，我們用 `debug` module 來印出訊息，並定義 `item`\n*  在第 8 ~ 11 行，則用了 `with_item` 將 0,1,2 的值傳入 `item`\n\n\u003cbr\u003e\n\n2. 執行 `ansible-playbook playbook_with_items.yaml` 後會得到：\n\n```yaml\nTASK [print loop message] *************************************************************************************************************\nok: [server1] =\u003e (item=0) =\u003e {\n    \"msg\": \"Loop 0\"\n}\nok: [server1] =\u003e (item=1) =\u003e {\n    \"msg\": \"Loop 1\"\n}\nok: [server1] =\u003e (item=2) =\u003e {\n    \"msg\": \"Loop 2\"\n}\n```\n\n\u003cbr\u003e\n\n另一種 在 Ansible 新增的 `loop`\n\n1. 建立 loop 的 playbook `vim playbook_loop.yaml `\n\n```yaml\n---\n- name: a basic loop with playbook\n  hosts: all\n  tasks:\n    - name: print loop message\n      ansible.builtin.debug:\n        msg: \"{{ item }} {{ my_idx }}\"\n      loop:\n        - Loop\n        - Loop\n        - Loop\n      loop_control:\n        index_var: my_idx\n```\n\n\u003cbr\u003e\n\n2. 執行 `ansible-playbook playbook_loop.yaml` 後會得到：\n\n```yaml\nTASK [print loop message] *************************************************************************************************************\nok: [server1] =\u003e (item=0) =\u003e {\n    \"msg\": \"Loop 0\"\n}\nok: [server1] =\u003e (item=1) =\u003e {\n    \"msg\": \"Loop 1\"\n}\nok: [server1] =\u003e (item=2) =\u003e {\n    \"msg\": \"Loop 2\"\n}\n```\n\n\u003cbr\u003e\n\n會使用 Loop 就可以減少我們在寫重複的程式碼，當然上面的只是簡單的範例，詳細請參考 [Loops - Ansible Documentation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#query-vs-lookup)。\n\n\u003cbr\u003e\n\n##  ansible 安裝時常見問題\n1. server1 | FAILED | rc=-1 \u003e\u003e to use the 'ssh' connection type with passwords or pkcs11_provider, you must install the sshpass program\n\t\nAns1. 會遇到這個問題是因為需要多安裝 sshpass，一般系統安裝 sshpass 很簡單，但在 macOS 上稍微麻煩，詳細可以參考[這篇文章](https://stackoverflow.com/questions/32255660/how-to-install-sshpass-on-mac)。\n\n2. ~paramiko/transport.py:236: CryptographyDeprecationWarning: Blowfish has been deprecated\n\nAns2. 在我安裝過程中，發現上前幾天才出現這個 Bug 詳細情形可以參考 [GitHub issues](https://github.com/paramiko/paramiko/issues/2038)，目前解決辦法有降板或是先將錯誤訊息給註解掉，之後再等新的版本出來再更新，大家可以自行選擇，我這邊是直接把出現問題的 `transport.py` 內容註解掉，大概位於236行，可以看下方圖片。\n\n\u003cbr\u003e\n\n![圖片](https://raw.githubusercontent.com/880831ian/Ansible/master/images/blowfish.png)\n\n\u003cbr\u003e \n\n## 參考資料\n\n[現代 IT 人一定要知道的 Ansible 自動化組態技巧](https://chusiang.gitbooks.io/automate-with-ansible/content/)\n\n[Ansible 安裝](https://tso-liang-wu.gitbook.io/learn-ansible-and-jenkins-in-30-days/ansible/ansible/ansible-installation)\n\n[怎麼用 Docker 練習 Ansible？](https://chusiang.gitbooks.io/automate-with-ansible/content/05.how-to-practive-the-ansible-with-docker.html)\n\n[community.general.telegram module – module for sending notifications via telegram](https://docs.ansible.com/ansible/latest/collections/community/general/telegram_module.html#ansible-collections-community-general-telegram-module)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F880831ian%2Fansible","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F880831ian%2Fansible","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F880831ian%2Fansible/lists"}