Ansible Galaxy 是社群共享的 Role/Collection 平台,換句話說可以把它想像成類似類似 PyPI、npm、RubyGems,讓我們可以直接重用別人寫好的模組,避免重新造輪子。
舉的例子:
- name: Install nginx
package: name=nginx
- name: Configure nginx
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
roles:
- geerlingguy.nginx
可以發現使用社群好心人士寫好的 Role,可以大幅降低開發成本,提升開發效率。
# 這邊以一個社區很有名的 Role 為例
ansible-galaxy install geerlingguy.nginx # 安裝最新版本
ansible-galaxy install geerlingguy.nginx,3.2.0 # 安裝指定版本
ansible-galaxy install -r requirements.yml -p ./roles # 下載到專案內
ansible-galaxy install git+https://github.com/geerlingguy/ansible-role-nginx.git
ansible-galaxy install file:///path/to/local/role # 從本地安裝
- hosts: web
become: yes
roles:
- geerlingguy.nginx # 直接導入即可
nginx_vhosts:
- listen: "80"
server_name: "example.com"
root: "/var/www/html"
其實這問題不外乎就跟我們在選 opensource 專案時的概念一樣:
roles:
- name: geerlingguy.nginx
version: "3.2.0"
- name: geerlingguy.mysql
version: "5.1.0"
collections:
- name: community.general
version: ">=11.0.0"
- hosts: web
become: yes
roles:
- role: geerlingguy.nginx
vars:
nginx_vhosts:
- listen: "80"
server_name: "example.com"
root: "/var/www/html"
tags: [nginx]
tasks:
- name: Optionally install Docker
include_role:
name: geerlingguy.docker
when: install_docker | default(false)
./roles
,ansible.cfg
設 roles_path=./roles
ansible-lint
、yamllint
、--syntax-check
我們今天換來實戰看看,10 分鐘搞定部署 FastAPI 和 Nginx。
# 先來建置個資料夾
mkdir fastapi-stack && cd fastapi-stack
# 創建一個 requirements.yaml
cat > requirements.yaml << 'EOF'
roles:
- name: geerlingguy.nginx
- name: geerlingguy.pip
EOF
ansible-galaxy install -r requirements.yml -p roles
# inventory.ini
[app_servers]
web01 ansible_host=192.168.1.10
web02 ansible_host=192.168.1.11
[app_servers:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
# group_vars/app_servers.yaml
app_user: fastapi
app_dir: /opt/fastapi-app
app_port: 8000
nginx_remove_default_vhost: true
nginx_vhosts:
- listen: "80"
server_name: "{{ inventory_hostname }}"
root: "/var/www/html"
extra_parameters: |
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# site.yml
- hosts: app_servers
become: yes
pre_tasks:
- 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'
roles:
- role: geerlingguy.pip
vars:
pip_install_packages:
- name: "fastapi"
virtualenv: "{{ app_dir }}/venv"
- name: "uvicorn[standard]"
virtualenv: "{{ app_dir }}/venv"
- role: geerlingguy.nginx
tasks:
- 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: Create systemd service for FastAPI (uvicorn)
copy:
dest: /etc/systemd/system/fastapi.service
mode: '0644'
content: |
[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
- name: Reload systemd and enable service
shell: |
systemctl daemon-reload
systemctl enable --now fastapi.service
args:
warn: false
# 執行
ansible-playbook -i inventory.ini site.yml
ansible-playbook -i inventory.ini site.yml --tags nginx # 單獨部署 Nginx
ansible-playbook -i inventory.ini site.yml --check # dry-run
明天我們換來玩玩 Templates 與 Jinja2。