iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
DevOps

不爆肝學習 Ansible 的短暫30天系列 第 15

# Day15 - 善用 Roles 積木化 Playbook

  • 分享至 

  • xImage
  •  

今日目標

  • 了解 Roles 是什麼,可以拿來做什麼
  • 掌握 Roles 的最佳實務與變數優先序建議

為什麼需要使用 Roles?

相信各位在寫程式都知道我們應該要把重複使用的程式碼抽成 function 讓其他地方可以共用,那 Ansible 的世界裡也是一樣,我們需要透過 Roles 來定義每個不同的小任務。

其實大家可以換個角度想,當我們把任務拆的越小像是一塊塊積木,那是不是我們在將其組合起來是非常方便且更具彈性。

常見的標準 Roles 結構

以下結構是筆者多方吸收消化後得出的結論,供大家參考。

roles/
  deploy_fastapi/
    defaults/      # 使用者可覆蓋的預設變數
      main.yml
    handlers/      # 服務重載/重啟事件
      main.yml
    tasks/         # task 進入點
      main.yml
    templates/     # 放置 Jinja2 templates
      fastapi.service.j2
      nginx_fastapi.conf.j2
    meta/          # metadata (optional)
      main.yml

💡Tips:筆者實務上常用 main.yaml 或者 main.yml 為檔案名稱。

舉個例子

  • 首先我們會有一個主要的 Playbook,來引用自己寫好的 roles 來使用。
---
# fastapi-site.yaml
- name: Use fastapi_app role
  hosts: all
  become: yes
  roles:
    - role: fastapi_app
      vars:
        app_port: 8000
        enable_nginx: true

💡Tips:筆者實務上常用 xxxx-site.yaml,會用 site 來表明這個 Playbook。

  • 我們可以在 defaults 裡面定義這個 role 的相關變數,之後再 task 裡面可以拿來使用。
---
# roles/deploy_fastapi/defaults/main.yaml
app_user: fastapi
app_dir: /opt/fastapi-app
app_port: 8000
enable_nginx: true
  • 定義我們的 handlers,這邊主要是用於重啟 nginx service。
---
# roles/deploy_fastapi/handlers/main.yaml
- name: Reload systemd
  systemd:
    daemon_reload: yes

- name: Reload nginx
  service:
    name: nginx
    state: reloaded
  • 這個就是我們整個 role 最重要的核心了,我們會將 role 要執行的任務定義在這,主要在做的事情這裡就不贅述了,昨天都有提到過了。
---
# roles/deploy_fastapi/tasks/main.yml
- name: Install base packages
  package:
    name: "{{ (ansible_facts.os_family == 'RedHat') | ternary(['python3','python3-virtualenv','python3-pip'], ['python3','python3-venv','python3-pip']) }}"
    state: present

- name: Ensure app user and directory
  block:
    - user:
        name: "{{ app_user }}"
    - file:
        path: "{{ app_dir }}"
        state: directory
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0755'

- name: Create virtualenv
  command: python3 -m venv {{ app_dir }}/venv
  args:
    creates: "{{ app_dir }}/venv/bin/python"

- name: Install FastAPI and Uvicorn
  pip:
    name:
      - fastapi
      - "uvicorn[standard]"
    virtualenv: "{{ app_dir }}/venv"

- name: Create sample FastAPI app
  copy:
    dest: "{{ app_dir }}/main.py"
    owner: "{{ app_user }}"
    group: "{{ app_user }}"
    mode: '0644'
    content: |
      from fastapi import FastAPI
      app = FastAPI()
      @app.get("/")
      def read_root():
          return {"hello": "world"}

- name: Install nginx (optional)
  package:
    name: nginx
    state: present
  when: enable_nginx

- name: Deploy systemd service
  template:
    src: fastapi.service.j2
    dest: /etc/systemd/system/fastapi.service
  notify: Reload systemd

- name: Deploy nginx reverse proxy (optional)
  template:
    src: nginx_fastapi.conf.j2
    dest: /etc/nginx/conf.d/fastapi.conf
    mode: '0644'
    validate: 'nginx -t -c %s'
  when: enable_nginx
  notify: Reload nginx

- name: Enable and start services
  service:
    name: "{{ 'nginx' if enable_nginx else 'fastapi' }}"
    state: started
    enabled: yes

- name: Ensure FastAPI service is running
  service:
    name: fastapi
    state: started
    enabled: yes
  • 接著就是把需要用到的 jinja2 templates 定義出來。
# roles/fastapi_app/templates/fastapi.service.j2
# Managed by Ansible – DO NOT EDIT
[Unit]
Description=FastAPI app
After=network.target

[Service]
User={{ app_user }}
WorkingDirectory={{ app_dir }}
ExecStart={{ app_dir }}/venv/bin/uvicorn main:app --host 127.0.0.1 --port {{ app_port }}
Restart=always
Environment=PATH={{ app_dir }}/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

[Install]
WantedBy=multi-user.target
# roles/fastapi_app/templates/nginx_fastapi.conf.j2
# Managed by Ansible – DO NOT EDIT
server {
  listen 80;
  server_name {{ inventory_hostname | default('_') }};
  location / {
    proxy_pass http://127.0.0.1:{{ app_port }};
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Roles 的變數與優先序建議

筆直建議使用 defaults/main.yml 來定義這個 role 的變數,相信大部分的情境都是夠用的,除非今天各位的變數是 global,那就直接放在 group_vars 裡面即可,這樣其他 role 或者 Playbook 比較容易使用。

作業練習時間

  • 練習 1:今天就來練習怎麼設計自己的 roles 吧,然後把它運行起來看看效果。

明日預告

明天來看看比較進階一點的 Inventory 管理技巧吧!


上一篇
Day14 - ansible.cfg 介紹與 FastAPI 自動化部署實戰
下一篇
Day16 - 今天來點比較進階的 Inventory
系列文
不爆肝學習 Ansible 的短暫30天21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言