iT邦幫忙

2023 iThome 鐵人賽

DAY 4
0

原簡體中文教程連結: Introduction.《Terraform入門教程》


1.2.1. Terraform 初步體驗

1.2.1.1 安裝

首先我們先安裝 Terraform。對於 Ubuntu 用戶:

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository -y "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install -y terraform

對於 CentOS 用戶:

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform

對於 Mac 用戶:

brew tap hashicorp/tap
brew install hashicorp/tap/terraform

對於 Windows 用戶,官方推薦的套件管理工具是 Choco,可以去 https://chocolatey.org/ 下載安裝好 chocolatey 後,以管理員身份啟動 Powershell,然後:

choco install terraform

如果只想純手動安裝,那麼可以前往 Terraform 官網下載對應作業系統的可執行文件(Terraform 是用 go 編寫的,只有一個可執行文件),解壓縮到指定的位置後,配置一下環境變數的 PATH,使其包含 Terraform 所在的目錄即可。

1.2.1.2. 驗證

terraform version
Terraform v0.13.5

terraform -help
Usage: terraform [-version] [-help] <command> [args]

The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.
##...

1.2.1.3. 一個簡單的例子

讓我們以 UCloud 為例寫一個簡單的範例,執行這個範例會產生些許費用,如果讀者不想付費,那麼只需要閱讀流程即可。另外介紹 Terraform 入門的文章很多,大多以 AWS 為例,阿里雲與騰訊雲也有相關入門介紹文章,不想註冊 UCloud 帳號的讀者也可以自行查閱其他公有雲相關的入門文章(譯者備註:不過後面的文章大部分以 AWS 為範例),本系列教程的重點將是後續的部分,所以無論用什麼公有雲,讀者只需要對 Terraform 有一個最簡單的體驗就可以。

首先,你需要前往 ucloud.cn 註冊一個 UCloud 帳號,然後登錄控制台,取得 PublicKey 和 PrivateKey,再訪問 https://accountv2.ucloud.cn/auth_manage/project,取得預設項目 id。

然後我們建立一個乾淨的空文件夾,在裡面新增一個 main.tf 文件(tf 就是 Terraform,Terraform 程式大部分是 .tf 文件,語法是 HCL,當然目前也支持 JSON 格式的 Terraform 程式,但我們暫時只以 tf 為例):

terraform {
  required_version = "~>0.13.5"
  required_providers {
    ucloud = {
      source  = "ucloud/ucloud"
      version = "~>1.22.0"
    }
  }
}

provider "ucloud" {
  public_key  = "JInqRnkSY8eAmxKFRxW9kVANYThg1pcvjD2Aw5f5p"
  private_key = "IlJn6GlmanYI1iDVEtrPyt5R9noAGz41B8q5TML7abqD8e4YjVdylwaKWdY61J5TcA"
  project_id  = "org-tgqbvi"
  region      = "cn-bj2"
}

data "ucloud_security_groups" "default" {
  type = "recommend_web"
}

data "ucloud_images" "default" {
  availability_zone = "cn-bj2-04"
  name_regex        = "^CentOS 6.5 64"
  image_type        = "base"
}

resource "ucloud_instance" "web" {
  availability_zone = "cn-bj2-04"
  image_id          = data.ucloud_images.default.images[0].id
  instance_type     = "n-basic-2"
  root_password     = "supersecret1234"
  name              = "tf-example-instance"
  tag               = "tf-example"
  boot_disk_type    = "cloud_ssd"

  security_group = data.ucloud_security_groups.default.security_groups[0].id

  delete_disks_with_instance = true

  user_data = <<EOF
#!/bin/bash
yum install -y nginx
service nginx start
EOF
}

resource "ucloud_eip" "web-eip" {
  internet_type = "bgp"
  charge_mode   = "bandwidth"
  charge_type   = "dynamic"
  name          = "web-eip"
}

resource "ucloud_eip_association" "web-eip-association" {
  eip_id      = ucloud_eip.web-eip.id
  resource_id = ucloud_instance.web.id
}

output "eip" {
  value = ucloud_eip.web-eip.public_ip
}

這裡要注意修改程式中的這一段:

provider "ucloud" {
  public_key  = "JInqRnkSY8eAmxKFRxW9kVANYThg1pcvjD2Aw5f5p"
  private_key = "IlJn6GlmanYI1iDVEtrPyt5R9noAGz41B8q5TML7abqD8e4YjVdylwaKWdY61J5TcA"
  project_id  = "org-tgqbvi"
  region      = "cn-bj2"
}

這裡的 public_keyprivate_key 以及 project_id 要替換成讀者自己剛才取得到的訪問密鑰以及項目 id。程式碼裡的 key 和 project id 已經被我刪除了。必須特別指出的是,這種將機密訊息硬編碼(Hard-Code)在程式碼中的做法是非常錯誤的,僅在此處方便展示適用,切勿將含有自己機密訊息的程式提交到版控工具裡,練習後注意重置自己的 Key。

這段程式比較簡單,頭部的 terraform 這一段宣告了這段程式碼所需要的 Terraform 版本以及 UCloud 套件版本,後面的 provider 段則是給出了呼叫 UCloud API 所需要的 key 和項目 id 等訊息。

真正定義雲端基礎設施的程式碼就是後面的部分,分為三部分,data、resource 和 output。

data 代表利用 UCloud 套件定義的 data 模型對 UCloud 進行查詢,例如我們在程式碼中利用 data 查詢 cn-bj2-04 機房 UCloud 官方提供的 CentOS 6.5 x64 主機鏡像的 id,以及官方提供的默認 Web 伺服器適用的安全組(可以理解成防火牆)的 id,這樣我們就不需要人工在介面上去查詢相關 id,再硬編碼到程式中。

resource 代表我們需要在雲端建立的資源,在例子裡我們創建了三個資源,分別是主機、彈性公網 ip,以及主機和公網 ip 的綁定。

我們在定義主機時給定了主機的規格、硬碟類型等關鍵訊息,並且通過定義了第一次開機時 user_data 所要執行的初始化腳本,在腳本中我們在這台 CentOS 服務器上安裝了 nginx 服務並啟動之。

最後,我們宣告了一個 output,名字是 eip,它的值就是我們創建的彈性公網 ip 的值。

運行這段程式碼很簡單,讓我們在程式碼所在的路徑下進入命令行,執行:

$ terraform init

這時 Terraform 會進行初始化操作,通過官方套件庫下載對應作業系統的 UCloud 套件。如果一切都正常,讀者應該會看到:

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

然後我們可以預覽一下程式碼即將產生的變更:

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.ucloud_images.default: Refreshing state...
data.ucloud_security_groups.default: Refreshing state...

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # ucloud_eip.web-eip will be created
  + resource "ucloud_eip" "web-eip" {
      + bandwidth     = (known after apply)
      + charge_mode   = "bandwidth"
      + charge_type   = "dynamic"
      + create_time   = (known after apply)
      + expire_time   = (known after apply)
      + id            = (known after apply)
      + internet_type = "bgp"
      + ip_set        = (known after apply)
      + name          = "web-eip"
      + public_ip     = (known after apply)
      + remark        = (known after apply)
      + resource      = (known after apply)
      + status        = (known after apply)
      + tag           = "Default"
    }

  # ucloud_eip_association.web-eip-association will be created
  + resource "ucloud_eip_association" "web-eip-association" {
      + eip_id        = (known after apply)
      + id            = (known after apply)
      + resource_id   = (known after apply)
      + resource_type = (known after apply)
    }

  # ucloud_instance.web will be created
  + resource "ucloud_instance" "web" {
      + auto_renew                 = (known after apply)
      + availability_zone          = "cn-bj2-04"
      + boot_disk_size             = (known after apply)
      + boot_disk_type             = "cloud_ssd"
      + charge_type                = (known after apply)
      + cpu                        = (known after apply)
      + cpu_platform               = (known after apply)
      + create_time                = (known after apply)
      + data_disk_size             = (known after apply)
      + data_disk_type             = (known after apply)
      + delete_disks_with_instance = true
      + disk_set                   = (known after apply)
      + expire_time                = (known after apply)
      + id                         = (known after apply)
      + image_id                   = "uimage-awndwi"
      + instance_type              = "n-basic-2"
      + ip_set                     = (known after apply)
      + isolation_group            = (known after apply)
      + memory                     = (known after apply)
      + name                       = "tf-example-instance"
      + private_ip                 = (known after apply)
      + remark                     = (known after apply)
      + root_password              = (sensitive value)
      + security_group             = "firewall-jofwjzmw"
      + status                     = (known after apply)
      + subnet_id                  = (known after apply)
      + tag                        = "tf-example"
      + user_data                  = <<~EOT
            #!/bin/bash
            yum install -y nginx
            service nginx start
        EOT
      + vpc_id                     = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + eip = (known after apply)

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

這段輸出告訴我們,程式即將創建 3 個新資源,修改 0 個資源,刪除 0 個資源。資源的屬性少部分是我們在程式中直接給出的,或是通過 data 查詢的,所以在 plan 命令的結果中可以看到它們的值;更多的屬性只有在資源真正被建立以後我們才能看到,所以會顯示 “(known after apply)”。

然後我們執行一下:

$ terraform apply
data.ucloud_images.default: Refreshing state...
data.ucloud_security_groups.default: Refreshing state...

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # ucloud_eip.web-eip will be created
  + resource "ucloud_eip" "web-eip" {
      + bandwidth     = (known after apply)
      + charge_mode   = "bandwidth"
      + charge_type   = "dynamic"
      + create_time   = (known after apply)
      + expire_time   = (known after apply)
      + id            = (known after apply)
      + internet_type = "bgp"
      + ip_set        = (known after apply)
      + name          = "web-eip"
      + public_ip     = (known after apply)
      + remark        = (known after apply)
      + resource      = (known after apply)
      + status        = (known after apply)
      + tag           = "Default"
    }

  # ucloud_eip_association.web-eip-association will be created
  + resource "ucloud_eip_association" "web-eip-association" {
      + eip_id        = (known after apply)
      + id            = (known after apply)
      + resource_id   = (known after apply)
      + resource_type = (known after apply)
    }

  # ucloud_instance.web will be created
  + resource "ucloud_instance" "web" {
      + auto_renew                 = (known after apply)
      + availability_zone          = "cn-bj2-04"
      + boot_disk_size             = (known after apply)
      + boot_disk_type             = "cloud_ssd"
      + charge_type                = (known after apply)
      + cpu                        = (known after apply)
      + cpu_platform               = (known after apply)
      + create_time                = (known after apply)
      + data_disk_size             = (known after apply)
      + data_disk_type             = (known after apply)
      + delete_disks_with_instance = true
      + disk_set                   = (known after apply)
      + expire_time                = (known after apply)
      + id                         = (known after apply)
      + image_id                   = "uimage-awndwi"
      + instance_type              = "n-basic-2"
      + ip_set                     = (known after apply)
      + isolation_group            = (known after apply)
      + memory                     = (known after apply)
      + name                       = "tf-example-instance"
      + private_ip                 = (known after apply)
      + remark                     = (known after apply)
      + root_password              = (sensitive value)
      + security_group             = "firewall-jofwjzmw"
      + status                     = (known after apply)
      + subnet_id                  = (known after apply)
      + tag                        = "tf-example"
      + user_data                  = <<~EOT
            #!/bin/bash
            yum install -y nginx
            service nginx start
        EOT
      + vpc_id                     = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + eip = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

當我們運行 terraform apply 時,Terraform 會首先重新計算一下變更計劃,並且像剛才執行 plan 命令那樣把變更計劃印出來給我們,要求我們人工確認。讓我們輸入 yes,然後按下 Enter:

ucloud_eip.web-eip: Creating...
ucloud_instance.web: Creating...
ucloud_eip.web-eip: Creation complete after 3s [id=eip-pyjwpcgd]
ucloud_instance.web: Still creating... [10s elapsed]
ucloud_instance.web: Still creating... [20s elapsed]
ucloud_instance.web: Still creating... [30s elapsed]
ucloud_instance.web: Creation complete after 39s [id=uhost-e4heibq3]
ucloud_eip_association.web-eip-association: Creating...
ucloud_eip_association.web-eip-association: Creation complete after 5s [id=eip-pyjwpcgd:uhost-e4heibq3]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

eip = 106.75.32.183

可以看到,Terraform 成功地建立了我們定義的資源,並且把我們定義的輸出給顯示了出來。如果我們在瀏覽器裡訪問我們輸出的彈性 ip 地址,我們就可以看到一個 nginx 頁面:
nginx輸出,我們的虛擬機正在工作
圖1.2/1 - nginx輸出,我們的虛擬機正在工作

1.2.1.4. 清理

完成這個體驗後,不要忘記清理我們的雲端資源。我們可以通過呼叫 destroy 命令來輕鬆完成清理:

$ terraform destroy
data.ucloud_images.default: Refreshing state... [id=1609882940]
data.ucloud_security_groups.default: Refreshing state... [id=2820377529]
ucloud_eip.web-eip: Refreshing state... [id=eip-pyjwpcgd]
ucloud_instance.web: Refreshing state... [id=uhost-e4heibq3]
ucloud_eip_association.web-eip-association: Refreshing state... [id=eip-pyjwpcgd:uhost-e4heibq3]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # ucloud_eip.web-eip will be destroyed
  - resource "ucloud_eip" "web-eip" {
      - bandwidth     = 1 -> null
      - charge_mode   = "bandwidth" -> null
      - charge_type   = "dynamic" -> null
      - create_time   = "2020-11-15T16:31:42+08:00" -> null
      - expire_time   = "2020-11-15T17:31:42+08:00" -> null
      - id            = "eip-pyjwpcgd" -> null
      - internet_type = "bgp" -> null
      - ip_set        = [
          - {
              - internet_type = "BGP"
              - ip            = "106.75.32.183"
            },
        ] -> null
      - name          = "web-eip" -> null
      - public_ip     = "106.75.32.183" -> null
      - resource      = {
          - "id"   = "uhost-e4heibq3"
          - "type" = "instance"
        } -> null
      - status        = "used" -> null
      - tag           = "Default" -> null
    }

  # ucloud_eip_association.web-eip-association will be destroyed
  - resource "ucloud_eip_association" "web-eip-association" {
      - eip_id        = "eip-pyjwpcgd" -> null
      - id            = "eip-pyjwpcgd:uhost-e4heibq3" -> null
      - resource_id   = "uhost-e4heibq3" -> null
      - resource_type = "instance" -> null
    }

  # ucloud_instance.web will be destroyed
  - resource "ucloud_instance" "web" {
      - auto_renew                 = true -> null
      - availability_zone          = "cn-bj2-04" -> null
      - boot_disk_size             = 20 -> null
      - boot_disk_type             = "cloud_ssd" -> null
      - charge_type                = "month" -> null
      - cpu                        = 2 -> null
      - cpu_platform               = "Intel/Broadwell" -> null
      - create_time                = "2020-11-15T16:31:46+08:00" -> null
      - delete_disks_with_instance = true -> null
      - disk_set                   = [
          - {
              - id      = "bsi-wnj4eh2x"
              - is_boot = true
              - size    = 20
              - type    = "cloud_ssd"
            },
        ] -> null
      - expire_time                = "2020-12-15T16:31:48+08:00" -> null
      - id                         = "uhost-e4heibq3" -> null
      - image_id                   = "uimage-awndwi" -> null
      - instance_type              = "n-basic-2" -> null
      - ip_set                     = [
          - {
              - internet_type = "Private"
              - ip            = "10.9.20.202"
            },
          - {
              - internet_type = "BGP"
              - ip            = "106.75.32.183"
            },
        ] -> null
      - memory                     = 4 -> null
      - name                       = "tf-example-instance" -> null
      - private_ip                 = "10.9.20.202" -> null
      - root_password              = (sensitive value)
      - security_group             = "firewall-jofwjzmw" -> null
      - status                     = "Running" -> null
      - subnet_id                  = "subnet-0azpshdq" -> null
      - tag                        = "tf-example" -> null
      - user_data                  = <<~EOT
            #!/bin/bash
            yum install -y nginx
            service nginx start
        EOT -> null
      - vpc_id                     = "uvnet-olpsy01g" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Changes to Outputs:
  - eip = "106.75.32.183" -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

可以看到,Terraform 列出了它即將清理的資源訊息,並且要求我們人工確認同意繼續執行清理操作。我們輸入 yes,然後按下 Enter:

ucloud_eip_association.web-eip-association: Destroying... [id=eip-pyjwpcgd:uhost-e4heibq3]
ucloud_eip_association.web-eip-association: Destruction complete after 2s
ucloud_eip.web-eip: Destroying... [id=eip-pyjwpcgd]
ucloud_instance.web: Destroying... [id=uhost-e4heibq3]
ucloud_eip.web-eip: Destruction complete after 0s
ucloud_instance.web: Destruction complete after 6s

Destroy complete! Resources: 3 destroyed.

很快的,剛才建立的資源就全部被刪除了。

Terraform 與以往諸如 Ansible 等配置管理工具比較大的不同在於,它是根據程式碼計算出的目標狀態與當前狀態的差異來計算變更計劃的,有興趣的讀者可以在執行 terraform apply 以後,直接再執行一次 terraform apply,看看會發生什麼,就能明白他們之間的差異。

實際上這段程式碼在 apply 以後,直接再次 apply,得到的計劃會是什麼也不做,因為當前雲端的資源狀態已經完全符合程式碼所描述的期望狀態了,所以 Terraform 什麼也不會做。好了,這就是我們對 Terraform 的一個初步體驗。


原簡體中文教程連結: Introduction.《Terraform入門教程》


上一篇
Day3-【學習】Terraform 備考方式
下一篇
Day5-【入門教程】Terraform基礎概念—Provider
系列文
Terraform 繁體中文25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言