在現代軟體開發流程中,持續整合與持續交付(CI/CD)至關重要。Jenkins 作為常用的自動化伺服器,其高用性佈署更是不可或缺。本文將著重於 Packer 和 Terraform 的運用,實作多雲環境下 Jenkins 叢集的建置與管理,特別針對 Azure 平台進行實務操作說明,包含映像建立、網路設定、安全存取等導向,以確保 Jenkins 服務的穩定性和可靠性。此方法能有效降低佈署成本、提升自動化程度,並確保 CI/CD 流程的順暢執行。
在多雲供應商上佈署高用性 Jenkins
使用 Packer 建立 Azure 映像
在 Azure 上建立 Jenkins worker 和 master 映像,需要使用 Packer。以下是一個範例的 template.json 檔案,用於建立 Jenkins worker 映像:
{
"variables": {
"subscription_id": "{{user `subscription_id`}}",
"client_id": "{{user `client_id`}}",
"client_secret": "{{user `client_secret`}}",
"tenant_id": "{{user `tenant_id`}}",
"resource_group": "{{user `resource_group`}}",
"location": "{{user `location`}}"
},
"builders": [
{
"type": "azure-arm",
"subscription_id": "{{user `subscription_id`}}",
"client_id": "{{user `client_id`}}",
"client_secret": "{{user `client_secret`}}",
"tenant_id": "{{user `tenant_id`}}",
"managed_image_resource_group_name": "{{user `resource_group`}}",
"managed_image_name": "jenkins-worker",
"os_type": "Linux",
"image_publisher": "OpenLogic",
"image_offer": "CentOS",
"image_sku": "8.0",
"location": "{{user `location`}}",
"vm_size": "Standard_B1s"
}
],
"provisioners": [
{
"type": "shell",
"script": "./setup.sh",
"execute_command": "sudo -E -S sh '{{ .Path }}'"
}
]
}
內容解密:
- 使用
azure-armbuilder 型別來建立 Azure 資源。 - 需要提供 Azure 的訂閱 ID、客戶端 ID、客戶端密碼和租戶 ID。
- 使用 CentOS 8.0 作為基礎映像,並安裝必要的依賴項。
- 使用
setup.sh指令碼來安裝 Jenkins worker 所需的軟體。
建立 Jenkins Master 映像
建立 Jenkins master 映像的流程與建立 worker 映像類別似,只是需要修改一些引數。以下是一個範例的 template.json 檔案,用於建立 Jenkins master 映像:
{
"variables": {
...
},
"builders": [
{
"type": "azure-arm",
"subscription_id": "{{user `subscription_id`}}",
"client_id": "{{user `client_id`}}",
"client_secret": "{{user `client_secret`}}",
"tenant_id": "{{user `tenant_id`}}",
"managed_image_resource_group_name": "{{user `resource_group`}}",
"managed_image_name": "jenkins-master-v22041",
"os_type": "Linux",
"image_publisher": "OpenLogic",
"image_offer": "CentOS",
"image_sku": "8.0",
"location": "{{user `location`}}",
"vm_size": "Standard_B1ms"
}
],
"provisioners": [
...
]
}
內容解密:
- 建立 Jenkins master 映像的流程與建立 worker 映像類別似。
- 需要修改
managed_image_name和vm_size等引數。
使用 Terraform 佈署虛擬網路
在佈署 Jenkins 叢集之前,需要建立一個虛擬網路。以下是一個範例的 virtual_network.tf 檔案,用於建立虛擬網路:
data "azurerm_resource_group" "management" {
name = var.resource_group
}
resource "azurerm_virtual_network" "management" {
name = "management"
location = var.location
resource_group_name = data.azurerm_resource_group.management.name
address_space = [var.base_cidr_block]
dns_servers = ["10.0.0.4", "10.0.0.5"]
dynamic "subnet" {
for_each = [for s in var.subnets: {
name = s.name
prefix = cidrsubnet(var.base_cidr_block, 8, s.number)
}]
content {
name = subnet.value.name
address_prefix = subnet.value.prefix
}
}
subnet {
name = "AzureBastionSubnet"
address_prefix = cidrsubnet(var.base_cidr_block, 11, 224)
}
tags = {
environment = "management"
}
}
圖表翻譯:
此圖表呈現了虛擬網路的架構,包括公有和私有子網路,以及一個專用的 AzureBastionSubnet 子網路。
此虛擬網路的建立是為了確保 Jenkins 叢集的安全性和可管理性。
Terraform變數定義
Terraform 使用變數來引數化和自定義佈署。以下是一些常用的變數:
| 名稱 | 型別 | 值 | 描述 |
|---|---|---|---|
| subscription_id | String | None | 要使用的訂閱 ID。也可以從 ARM_SUBSCRIPTION_ID 環境變數中取得。 |
| client_id | String | None | 要使用的客戶端 ID。也可以從 ARM_CLIENT_ID 環境變數中取得。 |
| client_secret | String | None | 要使用的客戶端密碼。也可以從 ARM_CLIENT_SECRET 環境變數中取得。 |
| tenant_id | String | None | 要使用的租戶/目錄 ID。也可以從 ARM_TENANT_ID 環境變數中取得。 |
| resource_group | String | None | 要在其中建立虛擬網路的資源群組名稱。 |
| location | String | None | 要建立虛擬網路的位置/區域。更改此值會強制建立新的資源。 |
| base_cidr_block | String | 10.0.0.0/16 | 用於虛擬網路的位址空間(CIDR區塊)。 |
| subnets | Map | None | 要在虛擬網路中建立的子網路列表。 |
圖表翻譯:
此表格呈現了 Terraform 中定義的變數及其描述,有助於瞭解每個變數的作用和用途。
綜上所述,本文介紹瞭如何使用 Packer 和 Terraform 在 Azure 上佈署高用性的 Jenkins 叢集。首先,使用 Packer 建立了 Jenkins worker 和 master 的映像;然後,使用 Terraform 建立了一個虛擬網路,以確保 Jenkins 叢集的安全性和可管理性。
在 Azure 上佈署高用性 Jenkins 叢集
使用 Azure Bastion 服務實作安全存取
為了安全地存取私有 Jenkins 機器,需要佈署一個閘道或代理伺服器,也就是所謂的跳板機(jump box)或堡壘主機(bastion host)。Azure 提供了一個稱為 Azure Bastion 的受控服務,可以提供遠端桌面協定(RDP)和 SSH 存取任何虛擬機器,而無需管理一個強化的堡壘例項並應用安全補丁(無維運開銷)。
建立 Azure Bastion 服務
首先,建立一個名為 bastion.tf 的檔案,內容如下:
resource "azurerm_public_ip" "bastion_public_ip" {
name = "bastion-public-ip"
location = var.location
resource_group_name = data.azurerm_resource_group.management.name
allocation_method = "Static"
sku = "Standard"
}
data "azurerm_subnet" "bastion_subnet" {
name = "AzureBastionSubnet"
virtual_network_name = azurerm_virtual_network.management.name
resource_group_name = data.azurerm_resource_group.management.name
depends_on = [azurerm_virtual_network.management]
}
resource "azurerm_bastion_host" "bastion" {
name = "bastion"
location = var.location
resource_group_name = data.azurerm_resource_group.management.name
depends_on = [azurerm_virtual_network.management]
ip_configuration {
name = "bastion-configuration"
subnet_id = data.azurerm_subnet.bastion_subnet.id
public_ip_address_id = azurerm_public_ip.bastion_public_ip.id
}
}
output "bastion" {
value = azurerm_public_ip.bastion_public_ip.ip_address
}
程式碼說明
- 建立公用 IP 位址:
azurerm_public_ip資源用於請求一個靜態的公用 IP 位址。 - 參考子網路:
data "azurerm_subnet"用於參考將要建立堡壘主機的子網路。 - 建立 Azure Bastion 主機:
azurerm_bastion_host資源用於建立 Azure Bastion 主機,並將其與公用 IP 位址和子網路關聯。 - 輸出 Bastion IP 位址:使用
output將 Bastion 主機的公用 IP 位址輸出。
佈署 Jenkins 主虛擬機器
在 VPN 佈署完成後,可以開始佈署 Jenkins 叢集。目標架構如圖所示。
建立 Jenkins 主虛擬機器
定義 jenkins_master.tf 檔案中的資源如下:
data "azurerm_image" "jenkins_master_image" {
name = var.jenkins_master_image
resource_group_name = data.azurerm_resource_group.management.name
}
resource "azurerm_virtual_machine" "jenkins_master" {
name = "jenkins-master"
resource_group_name = data.azurerm_resource_group.management.name
location = var.location
vm_size = var.jenkins_vm_size
network_interface_ids = [
azurerm_network_interface.jenkins_network_interface.id,
]
os_profile {
computer_name = var.config["os_name"]
admin_username = var.config["vm_username"]
}
os_profile_linux_config {
disable_password_authentication = true
ssh_keys {
path = "/home/${var.config["vm_username"]}/.ssh/authorized_keys"
key_data = file(var.public_ssh_key)
}
}
storage_os_disk {
name = "main"
caching = "ReadWrite"
managed_disk_type = "Standard_LRS"
create_option = "FromImage"
disk_size_gb = "30"
}
storage_image_reference {
id = data.azurerm_image.jenkins_master_image.id
}
delete_os_disk_on_termination = true
}
網路介面組態
data "azurerm_subnet" "private_subnet" {
name = var.subnets[2].name
virtual_network_name = azurerm_virtual_network.management.name
resource_group_name = data.azurerm_resource_group.management.name
depends_on = [azurerm_virtual_network.management]
}
resource "azurerm_network_interface" "jenkins_network_interface" {
name = "jenkins_network_interface"
location = var.location
resource_group_name = data.azurerm_resource_group.management.name
depends_on = [azurerm_virtual_network.management]
ip_configuration {
name = "internal"
subnet_id = data.azurerm_subnet.private_subnet.id
private_ip_address_allocation = "Dynamic"
}
}
程式碼說明
- 參考 Jenkins 主映像:
data "azurerm_image"用於參考先前使用 Packer 建立的 Jenkins 主映像。 - 建立虛擬機器:
azurerm_virtual_machine資源用於建立 Jenkins 主虛擬機器。 - 網路介面組態:
azurerm_network_interface資源用於將 Jenkins 主虛擬機器連線到私有網路子網路。 - SSH 金鑰組態:在
os_profile_linux_config中停用密碼驗證並啟用 SSH 金鑰驗證。