教學課程 - 如何使用 cloud-init 在首次開機時於 Azure 中自訂 Linux 虛擬機器

適用於:✔️ Linux VM ✔️ 彈性擴展集

在先前的教學課程中,您學到了如何以 SSH 連線到虛擬機器 (VM) 並手動安裝 NGINX。 若要以快速且一致的方式建立 VM,您通常需要借助某種形式的自動化。 在初次開機時自訂 VM 的常見方法是使用 cloud-init (英文)。 在本教學課程中,您將了解如何:

  • 建立 Cloud-init 組態檔
  • 建立 VM 來使用 Cloud-init 檔案
  • 在建立 VM 之後,檢視執行中的 Node.js 應用程式
  • 使用 Key Vault 安全地儲存憑證
  • 使用 Cloud-init 來安全地自動部署 NGINX

如果您選擇在本機安裝和使用 CLI,本教學課程會要求您執行 Azure CLI 2.0.30 版或更新版本。 執行 az --version 以尋找版本。 如果您需要安裝或升級,請參閱安裝 Azure CLI

Cloud-Init 概觀

Cloud-init (英文) 是在 Linux VM 初次開機時,廣泛用來自訂它們的方法。 您可以使用 cloud-init 來安裝封裝和寫入檔案,或者設定使用者和安全性。 當 cloud-init 在初次開機程序期間執行時,不需要使用任何額外的步驟或必要的代理程式來套用您的組態。

Cloud-init 也適用於散發套件。 例如,您不使用 apt-get installyum install 來安裝套件。 您可以改為定義要安裝的套件清單。 Cloud-init 會針對您選取的散發套件自動使用原生的套件管理工具。

我們正在與合作夥伴合作,以期在他們提供給 Azure 的映像中包含和使用 cloud-init。 如需每個發佈的 cloud-init 支援詳細資訊,請參閱 Azure中 VM 的 cloud-init 支援

建立 Cloud-init 組態檔

若要查看作用中的 cloud-init,請建立 VM 來安裝 NGINX 並執行簡單 'Hello World' Node.js 應用程式。 下列 cloud-init 組態會安裝必要的封裝、建立 Node.js 應用程式,然後初始化並啟動應用程式。

在 Bash 提示字元或 Cloud Shell 中,建立名為 cloud-init.txt 的檔案,並貼上下列組態。 例如,輸入 sensible-editor cloud-init.txt 可建立檔案,並查看可用的編輯器清單。 請確定已正確複製整個 cloud-init 檔案,特別是第一行:

#cloud-config
package_upgrade: true
packages:
  - nginx
  - nodejs
  - npm
write_files:
  - owner: www-data:www-data
    path: /etc/nginx/sites-available/default
    defer: true
    content: |
      server {
        listen 80;
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection keep-alive;
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
  - owner: azureuser:azureuser
    path: /home/azureuser/myapp/index.js
    defer: true
    content: |
      var express = require('express')
      var app = express()
      var os = require('os');
      app.get('/', function (req, res) {
        res.send('Hello World from host ' + os.hostname() + '!')
      })
      app.listen(3000, function () {
        console.log('Hello world app listening on port 3000!')
      })
runcmd:
  - service nginx restart
  - cd "/home/azureuser/myapp"
  - npm init
  - npm install express -y
  - nodejs index.js

如需 Cloud-init 組態選項的詳細資訊,請參閱 Cloud-init 組態範例 \(英文\)。

建立虛擬機器

建立 VM 之前,請先使用 az group create 來建立資源群組。 下列範例會在 eastus 位置建立名為 myResourceGroupAutomate 的資源群組:

az group create --name myResourceGroupAutomate --location eastus

現在,使用 az vm create 建立 VM。 使用 --custom-data 參數以傳入 cloud-init 組態檔。 如果您將檔案儲存於目前工作目錄之外的位置,請提供 cloud-init.txt 組態的完整路徑。 下列範例會建立名為 myVM 的 VM。

az vm create \
    --resource-group myResourceGroupAutomate \
    --name myAutomatedVM \
    --image Ubuntu2204 \
    --admin-username azureuser \
    --generate-ssh-keys \
    --custom-data cloud-init.txt

系統需要花幾分鐘的時間來建立 VM、安裝封裝和啟動應用程式。 在 Azure CLI 將您返回提示字元之後,背景工作會繼續執行。 可能需要再等候幾分鐘,才能存取應用程式。 建立 VM 之後,請注意 Azure CLI 所顯示的 publicIpAddress。 此位址是用來透過 Web 瀏覽器存取 Node.js 應用程式。

若要讓 Web 流量到達您的 VM,請使用 az vm open-port 從網際網路開啟通訊埠 80:

az vm open-port --port 80 --resource-group myResourceGroupAutomate --name myAutomatedVM

測試 Web 應用程式

現在,您可以開啟 Web 瀏覽器,並在網址列輸入 http://<publicIpAddress>。 提供您自己從 VM 建立程序中取得的公用 IP 位址。 您的 Node.js 應用程式即會顯示,如下列範例所示:

View running NGINX site

從 Key Vault 插入憑證

您可以選擇性閱讀這一節,其中示範如何安全地將憑證儲存在 Azure Key Vault 中,並在 VM 部署期間插入它們。 不需使用包含憑證的自訂映像內建中,此程序可確保會在初次開機時將最新憑證插入至 VM。 過程中,憑證絕對不會離開 Azure 平台,或在指令碼、命令列記錄或範本中公開。

Azure Key Vault 會保護密碼編譯金鑰和祕密,像是憑證或密碼。 Key Vault 有助於簡化金鑰管理程序,並可讓您控管存取和加密資料的金鑰。 此案例會引進一些 Key Vault 概念來建立和使用憑證,但並不是如何使用 Key Vault 的詳盡概觀。

下列步驟將示範如何:

  • 建立 Azure Key Vault
  • 產生或上傳憑證至 Key Vault
  • 從憑證建立祕密以插入至 VM
  • 建立 VM 並插入憑證

建立 Azure Key Vault

首先,使用 az keyvault create 建立 Key Vault,並加以啟用以供您在部署 VM 時使用。 每個 Key Vault 需要唯一的名稱,而且應該全部小寫。 使用您自己唯一的 Key Vault 名稱來取代下列範例中的 mykeyvault

keyvault_name=mykeyvault
az keyvault create \
    --resource-group myResourceGroupAutomate \
    --name $keyvault_name \
    --enabled-for-deployment

產生憑證並儲存於 Key Vault

若要在生產環境中使用,您應該使用 az keyvault certificate import 來匯入由受信任的提供者所簽署的有效憑證。 在本教學課程中,下列範例示範如何透過使用預設憑證原則的 az keyvault certificate create 來產生自我簽署憑證:

az keyvault certificate create \
    --vault-name $keyvault_name \
    --name mycert \
    --policy "$(az keyvault certificate get-default-policy --output json)"

準備要與 VM 搭配使用的憑證

若要在 VM 建立程序期間使用憑證,使用 az keyvault secret list-versions 來取得憑證的識別碼。 VM 需要特定格式的憑證,才能在開機時將其插入,因此請將憑證轉換為 az vm secret format。 下列範例會將這些命令的輸出指派給變數,以方便在後續步驟中使用:

secret=$(az keyvault secret list-versions \
          --vault-name $keyvault_name \
          --name mycert \
          --query "[?attributes.enabled].id" --output tsv)
vm_secret=$(az vm secret format --secret "$secret" --output json)

建立 Cloud-init 組態來保護 NGINX

當您建立 VM 時,憑證和金鑰會儲存在受保護的 /var/lib/waagent/ 目錄中。 若要將憑證自動新增至 VM 並設定 NGINX,您可以從上一個範例中使用更新的 cloud-init 組態。

建立名為 cloud-init-secured.txt 的檔案,並貼上下列組態。 如果您使用 Cloud Shell,請在該處而非在本機電腦上建立 cloud-init 組態檔。 例如,輸入 sensible-editor cloud-init-secured.txt 可建立檔案,並查看可用的編輯器清單。 請確定已正確複製整個 cloud-init 檔案,特別是第一行:

#cloud-config
package_upgrade: true
packages:
  - nginx
  - nodejs
  - npm
write_files:
  - owner: www-data:www-data
    path: /etc/nginx/sites-available/default
    defer: true
    content: |
      server {
        listen 80;
        listen 443 ssl;
        ssl_certificate /etc/nginx/ssl/mycert.cert;
        ssl_certificate_key /etc/nginx/ssl/mycert.prv;
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection keep-alive;
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
  - owner: azureuser:azureuser
    path: /home/azureuser/myapp/index.js
    defer: true
    content: |
      var express = require('express')
      var app = express()
      var os = require('os');
      app.get('/', function (req, res) {
        res.send('Hello World from host ' + os.hostname() + '!')
      })
      app.listen(3000, function () {
        console.log('Hello world app listening on port 3000!')
      })
runcmd:
  - secretsname=$(find /var/lib/waagent/ -name "*.prv" | cut -c -57)
  - mkdir /etc/nginx/ssl
  - cp $secretsname.crt /etc/nginx/ssl/mycert.cert
  - cp $secretsname.prv /etc/nginx/ssl/mycert.prv
  - service nginx restart
  - cd "/home/azureuser/myapp"
  - npm init
  - npm install express -y
  - nodejs index.js

建立安全的 VM

現在,使用 az vm create 建立 VM。 使用 --secrets 參數,從 Key Vault 插入憑證資料。 如同先前範例,您也要使用 --custom-data 參數來傳入 cloud-init 組態:

az vm create \
    --resource-group myResourceGroupAutomate \
    --name myVMWithCerts \
    --image Ubuntu2204 \
    --admin-username azureuser \
    --generate-ssh-keys \
    --custom-data cloud-init-secured.txt \
    --secrets "$vm_secret"

系統需要花幾分鐘的時間來建立 VM、安裝封裝和啟動應用程式。 在 Azure CLI 將您返回提示字元之後,背景工作會繼續執行。 可能需要再等候幾分鐘,才能存取應用程式。 建立 VM 之後,請注意 Azure CLI 所顯示的 publicIpAddress。 此位址是用來透過 Web 瀏覽器存取 Node.js 應用程式。

若要讓 Web 流量安全到達您的 VM,請使用 az vm open-port 從網際網路開啟通訊埠 443:

az vm open-port \
    --resource-group myResourceGroupAutomate \
    --name myVMWithCerts \
    --port 443

測試安全的 Web 應用程式

現在,您可以開啟 Web 瀏覽器,並在網址列輸入 https://<publicIpAddress>。 提供您自己的公用 IP 位址,其已顯示在先前 VM 建立程序中的輸出中。 如果您使用自我簽署憑證,請接受安全性警告:

Accept web browser security warning

接著會顯示受保護的 NGINX 網站和 Node.js 應用程式,如下列範例所示:

View running secure NGINX site

下一步

在本教學課程中,您已在初次開機時使用 Cloud-init 來設定 VM。 您已了解如何︰

  • 建立 Cloud-init 組態檔
  • 建立 VM 來使用 Cloud-init 檔案
  • 在建立 VM 之後,檢視執行中的 Node.js 應用程式
  • 使用 Key Vault 安全地儲存憑證
  • 使用 Cloud-init 來安全地自動部署 NGINX

前進至下一個教學課程,以了解如何建立自訂的 VM 映像。