Linux中國

使用 Cloud-init 將節點添加到你的私有雲中

Cloud-init 是一種廣泛使用的行業標準方法,用於初始化雲實例。雲提供商使用 Cloud-init 來定製實例的網路配置、實例信息,甚至用戶提供的配置指令。它也是一個可以在你的「家庭私有雲」中使用的很好的工具,可以為你的家庭實驗室的虛擬機和物理機的初始設置和配置添加一點自動化 —— 並了解更多關於大型雲提供商是如何工作的信息。關於更多的細節和背景,請看我之前的文章《在你的樹莓派家庭實驗室中使用 Cloud-init》。

![A screen showing the boot process for a Linux server running Cloud-init ](/data/attachment/album/202007/12/101007plwlzp6268oww8zw.jpg "A screen showing the boot process for a Linux server running Cloud-init ")

運行 Cloud-init 的 Linux 伺服器的啟動過程(Chris Collins,CC BY-SA 4.0

誠然,Cloud-init 對於為許多不同客戶配置機器的雲提供商來說,比對於由單個系統管理員運行的家庭實驗室更有用,而且 Cloud-init 解決的許多問題對於家庭實驗室來說可能有點多餘。然而,設置它並了解它的工作原理是了解更多關於這種雲技術的好方法,更不用說它是首次啟動時配置設備的好方法。

本教程使用 Cloud-init 的 NoCloud 數據源,它允許 Cloud-init 在傳統的雲提供商環境之外使用。本文將向你展示如何在客戶端設備上安裝 Cloud-init,並設置一個運行 Web 服務的容器來響應客戶端的請求。你還將學習如何審查客戶端從 Web 服務中請求的內容,並修改 Web 服務的容器,以提供基本的、靜態的 Cloud-init 服務。

在現有系統上設置 Cloud-init

Cloud-init 可能在新系統首次啟動時最有用,它可以查詢配置數據,並根據指令對系統進行定製。它可以包含在樹莓派和單板計算機的磁碟鏡像中,也可以添加到用於 配給 provision 虛擬機的鏡像中。對於測試用途來說,無論是在現有系統上安裝並運行 Cloud-init,還是安裝一個新系統,然後設置 Cloud-init,都是很容易的。

作為大多數雲提供商使用的主要服務,大多數 Linux 發行版都支持 Cloud-init。在這個例子中,我將使用 Fedora 31 Server 來安裝樹莓派,但在 Raspbian、Ubuntu、CentOS 和大多數其他發行版上也可以用同樣的方式來完成。

安裝並啟用雲計算初始服務

在你想作為 Cloud-init 客戶端的系統上,安裝 Cloud-init 包。如果你使用的是 Fedora:

# Install the cloud-init package
dnf install -y cloud-init

Cloud-init 實際上是四個不同的服務(至少在 systemd 下是這樣),這些服務負責檢索配置數據,並在啟動過程的不同階段進行配置更改,這使得可以做的事情更加靈活。雖然你不太可能直接與這些服務進行太多交互,但在你需要排除一些故障時,知道它們是什麼還是很有用的。它們是:

  • cloud-init-local.service
  • cloud-init.service
  • cloud-config.service
  • cloud-final.service

啟用所有四個服務:

# Enable the four cloud-init services
systemctl enable cloud-init-local.service
systemctl enable cloud-init.service
systemctl enable cloud-config.service
systemctl enable cloud-final.service

配置數據源以查詢

啟用服務後,請配置數據源,客戶端將從該數據源查詢配置數據。有許多數據源類型,而且大多數都是為特定的雲提供商配置的。對於你的家庭實驗室,請使用 NoCloud 數據源,(如上所述)它是為在沒有雲提供商的情況下使用 Cloud-init 而設計的。

NoCloud 允許以多種方式包含配置信息:以內核參數中的鍵/值對,用於在啟動時掛載的 CD(或虛擬機中的虛擬 CD);包含在文件系統中的文件中;或者像本例中一樣,通過 HTTP 從指定的 URL(「NoCloud Net」 選項)獲取配置信息。

數據源配置可以通過內核參數提供,也可以在 Cloud-init 配置文件 /etc/cloud/cloud.cfg 中進行設置。該配置文件對於使用自定義磁碟鏡像設置 Cloud-init 或在現有主機上進行測試非常方便。

Cloud-init 還會合併在 /etc/cloud/cloud.cfg.d/ 中找到的任何 *.cfg 文件中的配置數據,因此為了保持整潔,請在 /etc/cloud/cloud.cfg.d/10_datasource.cfg 中配置數據源。Cloud-init 可以通過使用以下語法從 seedfrom 鍵指向的 HTTP 數據源中讀取數據。

seedfrom: http://ip_address:port/

IP 地址和埠是你將在本文後面創建的 Web 服務。我使用了我的筆記本電腦的 IP 和 8080 埠。這也可以是 DNS 名稱。

創建 /etc/cloud/cloud.cfg.d/10_datasource.cfg 文件:

# Add the datasource:
# /etc/cloud/cloud.cfg.d/10_datasource.cfg

# NOTE THE TRAILING SLASH HERE!
datasource:
  NoCloud:
    seedfrom: http://ip_address:port/

客戶端設置就是這樣。現在,重新啟動客戶端後,它將嘗試從你在 seedfrom 鍵中輸入的 URL 檢索配置數據,並進行必要的任何配置更改。

下一步是設置一個 Web 伺服器來偵聽客戶端請求,以便你確定需要提供的服務。

設置網路伺服器以審查客戶請求

你可以使用 Podman 或其他容器編排工具(如 Docker 或 Kubernetes)快速創建和運行 Web 伺服器。這個例子使用的是 Podman,但同樣的命令也適用於 Docker。

要開始,請使用 fedora:31 容器鏡像並創建一個容器文件(對於 Docker 來說,這會是一個 Dockerfile)來安裝和配置 Nginx。從該容器文件中,你可以構建一個自定義鏡像,並在你希望提供 Cloud-init 服務的主機上運行它。

創建一個包含以下內容的容器文件:

FROM fedora:31

ENV NGINX_CONF_DIR "/etc/nginx/default.d"
ENV NGINX_LOG_DIR "/var/log/nginx"
ENV NGINX_CONF "/etc/nginx/nginx.conf"
ENV WWW_DIR "/usr/share/nginx/html"

# Install Nginx and clear the yum cache
RUN dnf install -y nginx 
      && dnf clean all 
      && rm -rf /var/cache/yum

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout ${NGINX_LOG_DIR}/access.log 
    && ln -sf /dev/stderr ${NGINX_LOG_DIR}/error.log

# Listen on port 8080, so root privileges are not required for podman
RUN sed -i -E 's/(listen)([[:space:]]*)([::]:)?80;$/1238080 default_server;/' $NGINX_CONF
EXPOSE 8080

# Allow Nginx PID to be managed by non-root user
RUN sed -i '/user nginx;/d' $NGINX_CONF
RUN sed -i 's/pid /run/nginx.pid;/pid /tmp/nginx.pid;/' $NGINX_CONF

# Run as an unprivileged user
USER 1001

CMD ["nginx", "-g", "daemon off;"]

註:本例中使用的容器文件和其他文件可以在本項目的 GitHub 倉庫中找到。

上面容器文件中最重要的部分是改變日誌存儲方式的部分(寫到 STDOUT 而不是文件),這樣你就可以在容器日誌中看到進入該伺服器的請求。其他的一些改變使你可以在沒有 root 許可權的情況下使用 Podman 運行容器,也可以在沒有 root 許可權的情況下運行容器中的進程。

在 Web 伺服器上的第一個測試並不提供任何 Cloud-init 數據;只是用它來查看 Cloud-init 客戶端的請求。

創建容器文件後,使用 Podman 構建並運行 Web 伺服器鏡像:

# Build the container image
$ podman build -f Containerfile -t cloud-init:01 .

# Create a container from the new image, and run it
# It will listen on port 8080
$ podman run --rm -p 8080:8080 -it cloud-init:01

這會運行一個容器,讓你的終端連接到一個偽 TTY。一開始看起來什麼都沒有發生,但是對主機 8080 埠的請求會被路由到容器內的 Nginx 伺服器,並且在終端窗口中會出現一條日誌信息。這一點可以用主機上的 curl 命令進行測試。

# Use curl to send an HTTP request to the Nginx container
$ curl http://localhost:8080

運行該 curl 命令後,你應該會在終端窗口中看到類似這樣的日誌信息:

127.0.0.1 - - [09/May/2020:19:25:10 +0000] "GET / HTTP/1.1" 200 5564 "-" "curl/7.66.0" "-"

現在,有趣的部分來了:重啟 Cloud-init 客戶端,並觀察 Nginx 日誌,看看當客戶端啟動時, Cloud-init 向 Web 伺服器發出了什麼請求。

當客戶端完成其啟動過程時,你應該會看到類似的日誌消息。

2020/05/09 22:44:28 [error] 2#0: *4 open() "/usr/share/nginx/html/meta-data" failed (2: No such file or directory), client: 127.0.0.1, server: _, request: "GET /meta-data HTTP/1.1", host: "instance-data:8080"
127.0.0.1 - - [09/May/2020:22:44:28 +0000] "GET /meta-data HTTP/1.1" 404 3650 "-" "Cloud-Init/17.1" "-"

註:使用 Ctrl+C 停止正在運行的容器。

你可以看到請求是針對 /meta-data 路徑的,即 http://ip_address_of_the_webserver:8080/meta-data。這只是一個 GET 請求 —— Cloud-init 並沒有向 Web 伺服器發送任何數據。它只是盲目地從數據源 URL 中請求文件,所以要由數據源來識別主機的要求。這個簡單的例子只是向任何客戶端發送通用數據,但一個更大的家庭實驗室應該需要更複雜的服務。

在這裡,Cloud-init 請求的是實例元數據信息。這個文件可以包含很多關於實例本身的信息,例如實例 ID、分配實例的主機名、雲 ID,甚至網路信息。

創建一個包含實例 ID 和主機名的基本元數據文件,並嘗試將其提供給 Cloud-init 客戶端。

首先,創建一個可複製到容器鏡像中的 meta-data 文件。

instance-id: iid-local01
local-hostname: raspberry
hostname: raspberry

實例 ID(instance-id)可以是任何東西。但是,如果你在 Cloud-init 運行後更改實例 ID,並且文件被送達客戶端,就會觸發 Cloud-init 再次運行。你可以使用這種機制來更新實例配置,但你應該意識到它是這種工作方式。

local-hostnamehostname 鍵正如其名,它們會在 Cloud-init 運行時為客戶端設置主機名信息。

在容器文件中添加以下行以將 meta-data 文件複製到新鏡像中。

# Copy the meta-data file into the image for Nginx to serve it
COPY meta-data ${WWW_DIR}/meta-data

現在,用元數據文件重建鏡像(使用一個新的標籤以方便故障排除),並用 Podman 創建並運行一個新的容器。

# Build a new image named cloud-init:02
podman build -f Containerfile -t cloud-init:02 .

# Run a new container with this new meta-data file
podman run --rm -p 8080:8080 -it cloud-init:02

新容器運行後,重啟 Cloud-init 客戶端,再次觀察 Nginx 日誌。

127.0.0.1 - - [09/May/2020:22:54:32 +0000] "GET /meta-data HTTP/1.1" 200 63 "-" "Cloud-Init/17.1" "-"
2020/05/09 22:54:32 [error] 2#0: *2 open() "/usr/share/nginx/html/user-data" failed (2: No such file or directory), client: 127.0.0.1, server: _, request: "GET /user-data HTTP/1.1", host: "instance-data:8080"
127.0.0.1 - - [09/May/2020:22:54:32 +0000] "GET /user-data HTTP/1.1" 404 3650 "-" "Cloud-Init/17.1" "-"

你看,這次 /meta-data 路徑被提供給了客戶端。成功了!

然而,客戶端接著在 /user-data 路徑上尋找第二個文件。該文件包含實例所有者提供的配置數據,而不是來自雲提供商的數據。對於一個家庭實驗室來說,這兩個都是你自己提供的。

你可以使用許多 user-data 模塊來配置你的實例。對於這個例子,只需使用 write_files 模塊在客戶端創建一些測試文件,並驗證 Cloud-init 是否工作。

創建一個包含以下內容的用戶數據文件:

#cloud-config

# Create two files with example content using the write_files module
write_files:
 - content: |
    "Does cloud-init work?"
   owner: root:root
   permissions: '0644'
   path: /srv/foo
 - content: |
   "IT SURE DOES!"
   owner: root:root
   permissions: '0644'
   path: /srv/bar

除了使用 Cloud-init 提供的 user-data 模塊製作 YAML 文件外,你還可以將其製作成一個可執行腳本供 Cloud-init 運行。

創建 user-data 文件後,在容器文件中添加以下行,以便在重建映像時將其複製到鏡像中:

# Copy the user-data file into the container image
COPY user-data ${WWW_DIR}/user-data

重建鏡像,並創建和運行一個新的容器,這次使用用戶數據信息:

# Build a new image named cloud-init:03
podman build -f Containerfile -t cloud-init:03 .

# Run a new container with this new user-data file
podman run --rm -p 8080:8080 -it cloud-init:03

現在,重啟 Cloud-init 客戶端,觀察 Web 伺服器上的 Nginx 日誌:

127.0.0.1 - - [09/May/2020:23:01:51 +0000] "GET /meta-data HTTP/1.1" 200 63 "-" "Cloud-Init/17.1" "-"
127.0.0.1 - - [09/May/2020:23:01:51 +0000] "GET /user-data HTTP/1.1" 200 298 "-" "Cloud-Init/17.1" "-

成功了!這一次,元數據和用戶數據文件都被送到了 Cloud-init 客戶端。

驗證 Cloud-init 已運行

從上面的日誌中,你知道 Cloud-init 在客戶端主機上運行並請求元數據和用戶數據文件,但它用它們做了什麼?你可以在 write_files 部分驗證 Cloud-init 是否寫入了你在用戶數據文件中添加的文件。

在 Cloud-init 客戶端上,檢查 /srv/foo/srv/bar 文件的內容:

# cd /srv/ && ls
bar foo
# cat foo
"Does cloud-init work?"
# cat bar
"IT SURE DOES!"

成功了!文件已經寫好了,並且有你期望的內容。

如上所述,還有很多其他模塊可以用來配置主機。例如,用戶數據文件可以配置成用 apt 添加包、複製 SSH 的 authorized_keys、創建用戶和組、配置和運行配置管理工具等等。我在家裡的私有雲中使用它來複制我的 authorized_keys、創建一個本地用戶和組,並設置 sudo 許可權。

你接下來可以做什麼

Cloud-init 在家庭實驗室中很有用,尤其是專註於雲技術的實驗室。本文所演示的簡單服務對於家庭實驗室來說可能並不是超級有用,但現在你已經知道 Cloud-init 是如何工作的了,你可以繼續創建一個動態服務,可以用自定義數據配置每台主機,讓家裡的私有雲更類似於主流雲提供商提供的服務。

在數據源稍顯複雜的情況下,將新的物理(或虛擬)機器添加到家中的私有雲中,可以像插入它們並打開它們一樣簡單。

via: https://opensource.com/article/20/5/create-simple-cloud-init-service-your-homelab

作者:Chris Collins 選題:lujun9972 譯者:wxy 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國