用 Ansible 來實作 K3s 叢集搭建自動化

程式技術
sharkHead
用 Ansible 來實作 K3s 叢集搭建自動化

之前寫過一篇文章介紹如何在雲端上面搭建 k3s 叢集:

分享如何在雲端服務上架個 K3s 來學 K8s

雖然過程不難,但要人工操作顯然不符合工程師的作風 🤪。因此我開始嘗試使用 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_clusterk3s_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 叢集的步驟如下:

  1. k3s_server 主機上安裝 k3s server,並產生 token。
  2. 使用 k3s server 的 token,在 k3s_agent_1k3s_agent_2 上面安裝 k3s agent。
  3. 修改 proxy 的設定,轉發外部 kubectl 的請求到 k3s server。

當你想執行的任務需要在多台主機上依照順序執行時,Ansible Playbook 是非常好用的工具。

設定主機變數

在開始撰寫 Playbook 的任務之前,先來設定待會會用到的變數,變數會存放在 host_vars 資料夾底下,可以看到有兩個檔案 proxy.yamlk3s_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,不用嗎?

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言