如何在您的Terraform项目中部署多個環境而無需複制代碼

作者選擇了自由和開源基金作為 Write for DOnations計劃的一部分接受捐贈。

介紹

Terraform提供了隨著您的項目 grows in size and complexity而越來越有用的進階功能。透過結構化您的代碼以最小化重複並引入有助於更容易測試和部署的工具幫助的工作流程,可以缓解維護多個環境的複雜基礎設施定義的成本。

Terraform將一個狀態與後端相關聯,這決定了狀態 stored and retrieved的位置和方式。每個狀態只有一個後端並與基礎設施配置绑定。像locals3這樣的某些後端可能會包含多個狀態。在這種情況下,將狀態和基礎設施與後端配對是在描述一個工作區。工作區允許您部署相同基礎設施配置的多个獨立實例,而無需將它們存儲在不同的後端中。

在本教程中,您將首先使用不同的工作區部署多個基礎設施實例。然後,您將部署一個有狀態資源,在這個教程中,它將是一個DigitalOcean卷宗。最後,您將參考來自<Terraform Registry的預製模塊,您可以使用它們來補充自己的。

先決條件

要完成本教程,您需要:

  • 一個DigitalOcean個人訪問令牌,您可以在DigitalOcean控制面板中創建。您可以在DigitalOcean產品文件中找到說明,如何創建個人訪問令牌

  • Terraform安裝在您的本地機器上,並且有一個使用DO提供者的項目設置。完成第1步第2步如何使用Terraform with DigitalOcean教程,並確保將專案文件夾命名為terraform-advanced,而不是loadbalance。在第2步中,不要包含pvt_key變量和SSH密鑰資源。

注意: 本教程已經特別測試過Terraform 1.0.2

使用工作區部署多個基礎建設實例

當您想要部署或測試您主要基礎設施的修改版本,而不需要創建分開的項目並重新設定驗證匙時,多個工作區非常有用。一旦您在分開的状态下開發和測試了一個功能,您可以將新代碼整合到主工作區中,並可能刪除額外的状态。當您init一個Terraform項目時,無論後端是什麼,Terraform會創建一個稱為default的工作區。它總是存在,您無法刪除它。

然而,多個工作區並不是創建多個環境(如灰度部署和生產環境)的適合解決方案。因此,工作區只追蹤状态,不存放代碼或其修改。

由於工作區並不追蹤實際的代碼,您應該在版本控制(VCS)層面上將多個工作區之間的代碼隔離,將它們配對到它們的基础設施變體。您如何實現這點取決於VCS工具本身;例如,在Git分支會是一個適合的抽象化。為了更容易管理多個環境的代碼,您可以將它們分成可重用模塊,這樣您就可以避免為每個環境重複类似的代碼。

工作區中部署資源

現在,您將創建一個項目,該項目部署一個Droplet,您將從多個工作區應用它。

您將Droplet定義存儲在一個名為droplets.tf的文件中。

假設您位于terraform-advanced目錄中,通過運行以下命令創建並打開它以進行編輯:

  1. nano droplets.tf

添加以下行:

resource "digitalocean_droplet" "web" {
  image  = "ubuntu-18-04-x64"
  name   = "web-${terraform.workspace}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

這個定義將在fra1區域中創建一個運行Ubuntu 18.04、具有1個CPU核心和1 GB RAM的Droplet。其名稱將包含當前工作區部署的名稱。完成後,保存並關閉文件。

將Terraform專案應用於執行其動作:

  1. terraform apply -var "do_token=${DO_PAT}"

輸出將類似於此:

Output
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.web將被創建 + resource "digitalocean_droplet" "web" { + backups = false + created_at = (known after apply) + disk = (known after apply) + id = (known after apply) + image = "ubuntu-18-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + ipv6_address_private = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "web-default" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "fra1" + resize_disk = true + size = "s-1vcpu-1gb" + status = (known after apply) + urn = (known after apply) + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. ...

當提示時,請輸入yes以在default工作區部署Droplet。

Droplet的名稱將是web-default,因為您開始的工作區稱為default。您可以列印工作區以確認它是不是唯一可用的:

  1. terraform workspace list

輸出將類似於此:

Output
* default

星號(*)意味著您目前選擇了該工作區。

通過運行workspace new創建並切換到稱為testing的新工作區,該工作區用於部署不同的Droplet:

  1. terraform workspace new testing

輸出將類似於此:

Output
Created and switched to workspace "testing"! You're now on a new, empty workspace. Workspaces isolate their state, so if you run "terraform plan" Terraform will not see any existing state for this configuration.

您通過運行再次計劃Droplet的部署:

  1. terraform plan -var "do_token=${DO_PAT}"

輸出將與前一次運行類似:

Output
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.web將被創建 + resource "digitalocean_droplet" "web" { + backups = false + created_at = (known after apply) + disk = (known after apply) + id = (known after apply) + image = "ubuntu-18-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + ipv6_address_private = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "web-testing" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "fra1" + resize_disk = true + size = "s-1vcpu-1gb" + status = (known after apply) + urn = (known after apply) + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. ...

請注意,Terraform计划部署一個稱為web-testing的Droplet,其名稱為web-default不同。這是因為defaulttesting工作區有分開的狀態,並且彼此不知道对方的資源——即使它們來自於同一份程式碼。

要以確認您正在testing工作區,請使用workspace show列印您目前所在的工作區:

  1. terraform workspace show

輸出將是當前工作区的名稱:

Output
testing

要刪除一個工作區,您首先需要摧毀其所有部署的資源。然後,如果它還活躍著,您需要使用 `workspace select` 切換到另一個工作區。由於這裡的 `testing` 工作區是空的,您可以直接切換到 `default`:`

  1. terraform workspace select default

您將收到的Terraform確認切換的輸出:`

Output
Switched to workspace "default".

您可以通過運行 `workspace delete` 來刪除它:`

  1. terraform workspace delete testing

Terraform然後將执行刪除:`

Output
Deleted workspace "testing"!

您可以在 `default` 工作區中通過運行:`

  1. terraform destroy -var "do_token=${DO_PAT}"

當提示時輸入 `yes` 以完成過程。`

在這個部分,您已经在多個Terraform工作區中工作了。在下一部分,您將部署一個有状态的資源。`

部署有状态資源

無状态資源不存储數據,因此可以快速創建和替換,因為它們并非唯一。有状态資源另一方面,包含可以重建或不易重建的唯一或非簡單重建的數據;因此,它們需要持久的數據存儲。`

由於您可能會摧毀這些資源,或者多個資源需要它們的數據,最好將其存儲在單独的實體中,例如 `DigitalOcean卷`。

卷提供額外的存儲空間。它們可以附加到Droplets(服務器),但與其分開。在這一步中,您將定義卷並將其連接到droplets.tf中的Droplet。

打開它以進行編輯:

  1. nano droplets.tf

添加以下行:

resource "digitalocean_droplet" "web" {
  image  = "ubuntu-18-04-x64"
  name   = "web-${terraform.workspace}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

resource "digitalocean_volume" "volume" {
  region                  = "fra1"
  name                    = "new-volume"
  size                    = 10
  initial_filesystem_type = "ext4"
  description             = "New Volume for Droplet"
}

resource "digitalocean_volume_attachment" "volume_attachment" {
  droplet_id = digitalocean_droplet.web.id
  volume_id  = digitalocean_volume.volume.id
}

在這裡,您將定義兩個新資源,即卷本身和卷附加。該卷將為10GB,格式為ext4,名為new-volume,並位於與Droplet相同的區域。由於卷和Droplet是不同的實體,您需要定義一個卷附加對象來連接它們。 volume_attachment採用Droplet和卷ID,並指導DigitalOcean雲將卷作為磁碟設備提供給Droplet。

完成後,保存並關閉文件。

通過運行以下命令規劃此配置:

  1. terraform plan -var "do_token=${DO_PAT}"

Terraform將計劃的操作將如下:

Output
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.web 將被創建 + resource "digitalocean_droplet" "web" { + backups = false + created_at = (known after apply) + disk = (known after apply) + id = (known after apply) + image = "ubuntu-18-04-x64" + ipv4_address = (known after apply) + ipv4_address_private = (known after apply) + ipv6 = false + ipv6_address = (known after apply) + ipv6_address_private = (known after apply) + locked = (known after apply) + memory = (known after apply) + monitoring = false + name = "web-default" + price_hourly = (known after apply) + price_monthly = (known after apply) + private_networking = (known after apply) + region = "fra1" + resize_disk = true + size = "s-1vcpu-1gb" + status = (known after apply) + urn = (known after apply) + vcpus = (known after apply) + volume_ids = (known after apply) + vpc_uuid = (known after apply) } # digitalocean_volume.volume 將被創建 + resource "digitalocean_volume" "volume" { + description = "New Volume for Droplet" + droplet_ids = (known after apply) + filesystem_label = (known after apply) + filesystem_type = (known after apply) + id = (known after apply) + initial_filesystem_type = "ext4" + name = "new-volume" + region = "fra1" + size = 10 + urn = (known after apply) } # digitalocean_volume_attachment.volume_attachment 將被創建 + resource "digitalocean_volume_attachment" "volume_attachment" { + droplet_id = (known after apply) + id = (known after apply) + volume_id = (known after apply) } Plan: 3 to add, 0 to change, 0 to destroy. ...

輸出詳情顯示Terraform將創建一個Droplet、一個卷以及一個卷附加,將卷連接到Droplet。

您現在已定義並連接了一個卷(狀態資源)到一個Droplet。在下一節中,您將查看公共的、預製的Terraform模塊,您可以在項目中合並這些模塊。

參考預製模塊

除了創建您自己的自定義模塊外,您還可以使用其他開發者提供的預製模塊和提供商,這些模塊和提供商可在Terraform Registry上公開獲取。

模塊部分中,您可以搜索可用的模塊數據庫,並根據提供商進行排序,以找到具有您所需功能的模塊。找到後,您可以閱讀其描述,其中列出了模塊提供的輸入和輸出,以及其外部模塊和提供商依賴。

現在,您將把DigitalOcean SSH密鑰模塊添加到您的項目中。您將把代碼與現有的定義分開,存儲在一個名為ssh-key.tf的文件中。通過運行以下命令來創建並打開它以進行編輯:

  1. nano ssh-key.tf

添加以下行:

module "ssh-key" {
  source         = "clouddrove/ssh-key/digitalocean"
  key_path       = "~/.ssh/id_rsa.pub"
  key_name       = "new-ssh-key"
  enable_ssh_key = true
}

此代碼定義了從登錄庫中clouddrove/droplet/digitalocean模塊的實例,並設定了其提供的一些參數。它應該從~/.ssh/id_rsa.pub讀取公開SSH密鑰,將其添加到您的帳戶中。

完成後,請儲存並關閉文件。

在您對此代碼執行plan之前,您必須運行以下命令以下載引用的模塊:

  1. terraform init

您將收到與以下類似的輸出:

Output
Initializing modules... Downloading clouddrove/ssh-key/digitalocean 0.13.0 for ssh-key... - ssh-key in .terraform/modules/ssh-key Initializing the backend... Initializing provider plugins... - Reusing previous version of digitalocean/digitalocean from the dependency lock file - Using previously-installed digitalocean/digitalocean v2.10.1 Terraform has been successfully initialized! ...

現在,您可以為這些變更計劃代碼:

  1. terraform plan -var "do_token=${DO_PAT}"

您將收到與此類似的輸出:

Output
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: ... # module.ssh-key.digitalocean_ssh_key.default[0] 將被創建 + resource "digitalocean_ssh_key" "default" { + fingerprint = (known after apply) + id = (known after apply) + name = "devops" + public_key = "ssh-rsa ... demo@clouddrove" } Plan: 4 to add, 0 to change, 0 to destroy. ...

輸出顯示您將創建SSH密鑰資源,這意味著您已從代碼中下載並調用了模塊。

結論

較大的項目可以利用Terraform提供的一些進階功能來幫助降低複雜性並使維護更容易。工作區域允許您在觸碰穩定的主要部署的情況下測試代碼的新增項目。您還可以將工作區域與版本控制系統結合使用以跟蹤代碼變更。使用預製模塊也可以縮短開發時間,但如果模塊未來變得過時,可能會帶來額外的開支或時間成本。

本教程是如何使用Terraform管理基礎設施系列的一部分。該系列涵蓋了多個Terraform主題,從第一次安裝Terraform到管理等複雜項目。

Source:
https://www.digitalocean.com/community/tutorials/how-to-deploy-multiple-environments-with-workspaces-in-your-terraform-project