用 Ansible 來實作 K3s 叢集搭建自動化
之前寫過一篇文章介紹如何在雲端上面搭建 k3s 叢集:
雖然過程不難,但要人工操作顯然不符合工程師的作風 🤪。因此我開始嘗試使用 Ansible 將整個叢集搭建流程自動化。
本篇文章簡單分享我如何使用 Ansible 當一個懶人。
設定 Ansible 與機器的連線
Ansible 主要是透過 SSH 的方式來操作機器,所以可以使用 Inventory 搭配 SSH config 的方式來設定 Ansible 與機器的連線。
先來設定 SSH Config,編輯 ~/.ssh/config
,設定各個主機的連線資訊。
Host proxy
User ubuntu
Hostname 10.0.0.10
Port 22
IdentityFile ~/.ssh/id_ed25519.pub
Host k3s_server
User ubuntu
Hostname 10.0.1.10
Port 22
ForwardAgent Yes
ProxyJump proxy
Host k3s_agent_1
User ubuntu
Hostname 10.0.1.11
Port 22
ForwardAgent Yes
ProxyJump proxy
Host k3s_agent_2
User ubuntu
Hostname 10.0.1.12
Port 22
ForwardAgent Yes
ProxyJump proxy
接下來建立一個資料夾 install-k3s-cluster
,並在底下建立以下這些檔案。
├── ansible.cfg
├── files
│ └── nginx
│ └── kubectl.conf
├── host_vars
│ ├── k3s_server.yaml
│ └── proxy.yaml
├── install-k3s.yaml
├── install-proxy.yaml
└── inventory.yaml
inventory.yaml
用來設定主機的連線資訊,Ansible 可以透過這個檔案與目標主機進行連線。
k3s_cluster:
hosts:
k3s_server:
k3s_agent_1:
k3s_agent_2:
k3s_agents:
hosts:
k3s_agent_1:
k3s_agent_2:
ungrouped:
hosts:
proxy:
k3s_cluster
與 k3s_agents
是主機群組的名稱,而 hosts
底下就是各個主機的名稱。這些名稱可以由使用者自行定義。
ungrouped
則是 Ansible 預設就有的群組名稱,代表沒有加入任何使用者自定義群組的主機。
這裡注意主機名稱是對應到 ~/.ssh/config
裡面的 Host
名稱,這樣 Ansible 才能夠透過 inventory,在 SSH config 中找到正確的主機連線資訊。
主機名稱的命名方式建議使用蛇形命名,例如 hello_world。
連線設定好之後,可以使用 ansible 的 ping 模組來測試與主機的連線。
ansible proxy -i inventory.yaml -m ping
可以在 ansible.cfg
中先設定好 inventory 檔案的位置。
[defaults]
inventory = ./inventory.yaml
這樣執行指令時,就可以省去 -i inventory.yaml
。
ansible proxy -m ping
使用 Ansible Playbook 建立 K3s 叢集
在之前文章中,搭建一個 k3s 叢集的步驟如下:
- 在
k3s_server
主機上安裝 k3s server,並產生 token。 - 使用 k3s server 的 token,在
k3s_agent_1
與k3s_agent_2
上面安裝 k3s agent。 - 修改 proxy 的設定,轉發外部 kubectl 的請求到 k3s server。
當你想執行的任務需要在多台主機上依照順序執行時,Ansible Playbook 是非常好用的工具。
設定主機變數
在開始撰寫 Playbook 的任務之前,先來設定待會會用到的變數,變數會存放在 host_vars
資料夾底下,可以看到有兩個檔案 proxy.yaml
與 k3s_server.yaml
。
proxy.yaml
用來存放與 proxy 主機相關的變數,這裡我放了 proxy 的 public IP。
public_ip: "123.123.123.123"
同理,k3s_server.yaml 會存放 k3s server 主機相關的變數,這裡我放了 k3s server 的 private IP。
private_ip: "10.0.1.10"
AWS EC2 的 IP 相關資訊,你也可以透過 Metadata 來取得。你可以在 Ansible 中寫一個任務從 Metadata 取得 IP 並註冊變數,以便後續任務使用,這樣就不需要額外設定變數。
安裝 K3s Server
接下來開始撰寫第一個任務:安裝 k3s server。編輯檔案 install-k3s.yaml
。
---
# 任務說明,根據 ansible lint,開頭必須是大寫
- name: Install k3s server in Ubuntu
# 設定要在哪個主機上執行任務
hosts: k3s_server
tasks:
- name: Populate service facts
# 使用 ansible 內建的模組取得主機上所有執行中的服務
# ansible 本身有提供非常多的模組可以使用
ansible.builtin.service_facts:
- name: Install k3s server
# 使用 when 來做條件判斷
# 當主機上沒有 k3s.service 運行時,才會執行任務
when: "'k3s.service' not in ansible_facts.services"
# block 可以將多個任務包裝起來
# 根據上面 when 的判斷結果,會決定要不要執行 block 底下的多個任務
block:
- name: Download k3s install script
# 使用模組下載 k3s 的安裝檔案,並設定檔案權限
ansible.builtin.get_url:
# 模組可以設定參數,例如下方的參數分別是
# url:下載檔案的連結
# dest:檔案儲存的路徑
# mode:檔案權限
url: https://get.k3s.io
dest: /tmp/k3s_install.sh
mode: "0755"
- name: Execute the k3s install script
# 使用模組執行 k3s 的安裝檔案
ansible.builtin.command: sh /tmp/k3s_install.sh
# 設定環境變數,這裡我們使用了剛剛設定的 proxy public IP 變數
environment:
INSTALL_K3S_EXEC: "server --tls-san {{ hostvars['proxy']['public_ip'] }}"
# 因為執行指令會讓 ansible 無法確認主機狀態是否有變更
# 這裡設定 true 是告訴 ansible,這個任務必定會改變主機狀態
changed_when: true
- name: Check k3s server status
ansible.builtin.systemd_service:
name: k3s.service
state: started
enabled: true
- name: Get k3s server token
become: true
ansible.builtin.command: cat /var/lib/rancher/k3s/server/node-token
# cat 指令並不會改變主機的狀態,因此這裡設定 false
changed_when: false
# 這裡將輸出結果註冊為新的變數,讓後續的任務可以使用
register: k3s_server_token
- name: Copy k3s config file to local
become: true
ansible.builtin.fetch:
src: /etc/rancher/k3s/k3s.yaml
dest: ~/.kube/config
flat: true
在本機上設定 K3s Config
修改剛剛從 k3s server 複製到本地的 k3s config,讓本機上的 kubectl 可以對 proxy 發送請求:
- name: Change ~/.kube/config permissions and update ip
# localhost 為 ansible 預設的主機名稱,代表本地主機
hosts: localhost
tasks:
- name: Change file permissions
ansible.builtin.file:
path: ~/.kube/config
mode: "0600"
# 修改 config 中要遠端連線的 IP
# 這裡會使用正則找出要替換的 IP 字串,並更換成 proxy 的 public IP
- name: Update ip in config
ansible.builtin.replace:
path: ~/.kube/config
regexp: 'server: https://127\.0\.0\.1:6443'
replace: "server: https://{{ hostvars['proxy']['public_ip'] }}:6443"
安裝 K3s Agent
接下來撰寫安裝 k3s agent 1 & 2 的任務,這邊只要更換 hosts 的值即可,其他內容都是一樣的:
- name: Install first k3s agent in Ubuntu
# hosts 這裡使用群組名稱 k3s_agents,這樣任務會在群組中的所有主機上執行
hosts: k3s_agents
tasks:
- name: Populate service facts
ansible.builtin.service_facts:
- name: Install first k3s agent
when: "'k3s-agent.service' not in ansible_facts.services and
hostvars['k3s_server']['k3s_server_token'].rc == 0"
block:
- name: Download k3s install script
ansible.builtin.get_url:
url: https://get.k3s.io
dest: /tmp/k3s_install.sh
mode: "0755"
- name: Execute k3s script to install agent
ansible.builtin.command: sh /tmp/k3s_install.sh
environment:
K3S_URL: "https://{{ hostvars['k3s_server']['private_ip'] }}:6443"
# 使用剛剛註冊的變數 k3s_server_token
K3S_TOKEN: "{{ hostvars['k3s_server']['k3s_server_token'].stdout }}"
register: install_k3s_agent
changed_when: true
- name: Check k3s agent server status
ansible.builtin.systemd_service:
name: k3s-agent.service
state: started
enabled: true
執行 Playbook 指令,搭建 K3s 叢集
playbook.yaml
寫好之後,執行 Ansible Playbook 指令:
ansible-playbook install-k3s.yaml
執行完畢之後記得先修改 proxy 的設定,轉發 kubectl 指令的請求到 k3s server。
# kubectl.conf
stream {
server {
listen 6443;
proxy_pass 10.0.1.10:6443;
}
}
當然這個步驟我也是使用 Ansible 來幫我達成。
# install-proxy.yaml
---
- name: Insall nginx and setting up reverse proxy
hosts: proxy
tasks:
- name: Install nginx
become: true
ansible.builtin.apt:
name: nginx
state: present
- name: Copy stream config to proxy server
become: true
ansible.builtin.copy:
src: files/nginx/kubectl.conf
dest: /etc/nginx/kubectl.conf
owner: root
group: root
mode: "0644"
- name: Include stream config in nginx config
become: true
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
line: "include /etc/nginx/kubectl.conf;"
insertafter: '^include /etc/nginx/modules-enabled/\*\.conf;'
- name: Restart nginx
become: true
ansible.builtin.systemd_service:
name: nginx.service
state: restarted
enabled: true
完成後可以嘗試在本機使用 kubectl 指令,檢查能否順利操作 k3s cluster。
原本需要人工操作的 k3s 叢集搭建,現在只要兩行指令即可搞定。
好 Ansible,不用嗎?