เฟส 1 — สร้าง
เฟส 1 ปรับใช้ Primary DNS Zone แบบ authoritative บน F5 Distributed Cloud ด้วย Terraform โดยมีสถานะระยะไกลสำรองใน Azure Blob Storage หน้านี้แสดงไฟล์ทั้งหมดในแผน อธิบายตัวแปรและความลับที่จำเป็น รวมถึงครอบคลุมทั้งการรันในเครื่องและไปป์ไลน์ GitHub Actions
สิ่งที่คุณปรับใช้
หัวข้อที่มีชื่อว่า “สิ่งที่คุณปรับใช้”xcsh_dns_zoneสำหรับโดเมนที่มอบหมายให้คุณ ในเนมสเปซsystemพร้อมกลุ่มdemo-recordsของ A records (www,app,api)- สถานะ Terraform ที่จัดเก็บในคอนเทนเนอร์ Azure Blob Storage เพื่อให้ใช้สถานะเดียวกันระหว่างเครื่องของคุณและ CI
แผนอยู่ภายใต้ terraform/: โมดูลรูทขนาดเล็กที่เชื่อมอินพุตเข้ากับโมดูล dns-zone
versions.tf
หัวข้อที่มีชื่อว่า “versions.tf”กำหนดเวอร์ชัน Terraform และผู้ให้บริการ ข้อจำกัดของผู้ให้บริการคือ >= 3.62.0 — รีลีสที่ทรัพยากร DNS เฉพาะระบบกำหนดเนมสเปซโดยอัตโนมัติ
terraform { required_version = ">= 1.5"
required_providers { xcsh = { source = "f5-sales-demo/xcsh" # >= 3.62.0: the namespace attribute for system-only DNS resources defaults # to "system" (spec-driven), so it can be omitted. Locally the provider is # consumed via dev_overrides, which ignores this constraint. version = ">= 3.62.0" } }}providers.tf
หัวข้อที่มีชื่อว่า “providers.tf”ผู้ให้บริการไม่รับอาร์กิวเมนต์ในโค้ด — ยืนยันตัวตนจากสภาพแวดล้อม ดังนั้นจึงไม่มีความลับที่ถูก commit
# The xcsh provider authenticates from the environment — no secrets in code.# Export one of the following credential sets before running Terraform:## Token auth: XCSH_API_URL + XCSH_API_TOKEN# P12 auth: XCSH_API_URL + XCSH_P12_FILE + XCSH_P12_PASSWORD# PEM auth: XCSH_API_URL + XCSH_CERT + XCSH_KEY## See terraform/README.md for local dev setup (dev_overrides + env).provider "xcsh" {}backend.tf
หัวข้อที่มีชื่อว่า “backend.tf”แบ็กเอนด์ azurerm แบบ partial: ไม่มีค่าเฉพาะสภาพแวดล้อมที่ hardcode ไว้ พิกัดจะถูกส่งมาเมื่อถึงเวลา init (จากไฟล์ในเครื่อง หรือจากตัวแปร GitHub Actions ใน CI)
terraform { # Azure Blob Storage remote state, configured as a PARTIAL backend: # no environment-specific values are hardcoded here. Supply them at init time. # # CI: terraform init -backend-config="resource_group_name=$RG" ... # (values from GitHub Actions repository variables) # Local: terraform init -backend-config=backend.hcl (copy backend.hcl.example; gitignored) # # Auth is the storage account access key via the ARM_ACCESS_KEY environment # variable (never committed). Keyless auth (use_oidc / use_azuread_auth) is not # used: our Contributor-only RBAC cannot assign the "Storage Blob Data # Contributor" role those methods require. backend "azurerm" {}}variables.tf
หัวข้อที่มีชื่อว่า “variables.tf”domain เป็นค่าที่จำเป็น (ส่งมาขณะรันไทม์ ไม่เคย hardcode); a_records แมปชื่อเรคคอร์ดแต่ละรายการกับที่อยู่ IPv4 ไม่มีตัวแปร namespace — ผู้ให้บริการกำหนดออบเจกต์ DNS ให้กับเนมสเปซ system จากข้อจำกัดของ API spec ดังนั้นจึงไม่มีการกำหนดค่าที่นี่
variable "domain" { description = "DNS zone FQDN (required; supplied via TF_VAR_domain / a GitHub variable / tfvars)." type = string}
variable "labels" { description = "Labels applied to managed DNS objects." type = map(string) default = { managed_by = "terraform" use_case = "dns" }}
variable "a_records" { description = "A records: record name (\"\" = apex) to list of IPv4 addresses." type = map(list(string)) default = { www = ["203.0.113.10"] app = ["203.0.113.20"] api = ["203.0.113.30"] }}main.tf และโมดูล dns-zone
หัวข้อที่มีชื่อว่า “main.tf และโมดูล dns-zone”โมดูลรูทเชื่อมอินพุตเข้ากับ ./modules/dns-zone:
module "dns_zone" { source = "./modules/dns-zone"
domain = var.domain labels = var.labels a_records = var.a_records}โมดูลสร้างโซน (เนมสเปซถูกละเว้น — ผู้ให้บริการกำหนดค่าเริ่มต้นเป็น system) บล็อก dynamic "rr_set" แปลง map a_records เป็นชุดเรคคอร์ดหนึ่งรายการต่อรายการ:
resource "xcsh_dns_zone" "this" { name = var.domain labels = var.labels
primary { default_soa_parameters {}
rr_set_group { metadata { name = "demo-records" }
dynamic "rr_set" { for_each = var.a_records content { ttl = var.record_ttl a_record { name = rr_set.key values = rr_set.value } } } } }}outputs.tf ของรูทส่งออกชื่อโซนและตัวระบุ F5 XC จากโมดูลอีกครั้ง
ตัวแปรและความลับที่จำเป็น
หัวข้อที่มีชื่อว่า “ตัวแปรและความลับที่จำเป็น”ไม่มีอะไรเฉพาะสภาพแวดล้อมที่ฝังอยู่ในไฟล์ .tf ทุกอย่างส่งมาขณะรันไทม์ — จาก GitHub Actions ตัวแปร และ ความลับ ใน CI หรือจากไฟล์ในเครื่องและตัวแปรสภาพแวดล้อม
| ค่า | วัตถุประสงค์ | แหล่งที่มา CI | แหล่งที่มาในเครื่อง |
|---|---|---|---|
resource_group_name, storage_account_name, container_name, key | พิกัดแบ็กเอนด์ azurerm | Repository ตัวแปร TFSTATE_RESOURCE_GROUP, TFSTATE_STORAGE_ACCOUNT, TFSTATE_CONTAINER, TFSTATE_KEY (ส่งผ่าน -backend-config) | backend.hcl (คัดลอก backend.hcl.example; gitignored) |
domain | อินพุต Terraform | Repository ตัวแปร DNS_DOMAIN (เป็น TF_VAR_domain) | terraform.tfvars (คัดลอกตัวอย่าง) หรือ TF_VAR_domain |
ARM_ACCESS_KEY | การยืนยันตัวตนแบ็กเอนด์ azurerm (คีย์บัญชีพื้นที่จัดเก็บ) | Repository ความลับ | export ARM_ACCESS_KEY=... |
XCSH_API_URL, XCSH_API_TOKEN | การยืนยันตัวตนผู้ให้บริการ xcsh | Repository ความลับ | export XCSH_API_URL=... XCSH_API_TOKEN=... |
ไฟล์ตัวอย่างสองไฟล์ที่มาพร้อมกับ repo:
resource_group_name = "f5-sales-demo-tfstate"storage_account_name = "f5salesdemotfstate"container_name = "tfstate"key = "dns.tfstate"domain = "f5-sales-demo.com"
labels = { managed_by = "terraform" use_case = "dns"}
a_records = { www = ["203.0.113.10"] app = ["203.0.113.20"] api = ["203.0.113.30"]}Bootstrap แบ็กเอนด์สถานะ Azure
หัวข้อที่มีชื่อว่า “Bootstrap แบ็กเอนด์สถานะ Azure”ที่จัดเก็บแบ็กเอนด์ไม่สามารถเก็บ bootstrap ของตัวเองได้ ดังนั้นให้สร้างครั้งเดียวนอกกระบวนการด้วย session Azure CLI ที่ยืนยันตัวตนแล้ว repo มาพร้อมกับ scripts/bootstrap-azure-state.sh:
az group create --name f5-sales-demo-tfstate --location eastus2 \ --tags managed_by=terraform use_case=dns purpose=tfstate
az storage account create --name f5salesdemotfstate --resource-group f5-sales-demo-tfstate \ --location eastus2 --sku Standard_LRS --kind StorageV2 \ --min-tls-version TLS1_2 --allow-blob-public-access false
# State safety: keep prior versions and allow recovery of deleted state blobs.az storage account blob-service-properties update \ --account-name f5salesdemotfstate --resource-group f5-sales-demo-tfstate \ --enable-versioning true \ --enable-delete-retention true --delete-retention-days 7 \ --enable-container-delete-retention true --container-delete-retention-days 7
KEY="$(az storage account keys list \ --account-name f5salesdemotfstate --resource-group f5-sales-demo-tfstate \ --query '[0].value' -o tsv)"
az storage container create --name tfstate \ --account-name f5salesdemotfstate --auth-mode key --account-key "$KEY"จากนั้น export คีย์สำหรับการรันในเครื่อง และตั้งค่าคีย์นั้น (พร้อมข้อมูลรับรองผู้ให้บริการ) เป็น GitHub secrets:
export ARM_ACCESS_KEY="$KEY"gh secret set ARM_ACCESS_KEY -R f5-sales-demo/dnsgh secret set XCSH_API_URL -R f5-sales-demo/dnsgh secret set XCSH_API_TOKEN -R f5-sales-demo/dnsปรับใช้ในเครื่อง
หัวข้อที่มีชื่อว่า “ปรับใช้ในเครื่อง”-
กำหนดค่าอินพุต คัดลอกตัวอย่างและกรอกค่าของคุณ:
Terminal window cp terraform/backend.hcl.example terraform/backend.hclcp terraform/terraform.tfvars.example terraform/terraform.tfvars -
Export ข้อมูลรับรอง:
Terminal window export XCSH_API_URL="https://<tenant>.console.ves.volterra.io"export XCSH_API_TOKEN="<api-token>"export ARM_ACCESS_KEY="<storage-account-key>" -
เริ่มต้น แบ็กเอนด์ด้วยการกำหนดค่าบางส่วน:
Terminal window cd terraformterraform init -backend-config=backend.hcl -
ตรวจสอบการจัดรูปแบบและตรวจสอบความถูกต้อง:
Terminal window terraform fmt -check -recursiveterraform validate -
วางแผนและปรับใช้:
Terminal window terraform planterraform apply
การ apply ที่สำเร็จจะสร้างโซนและเขียนสถานะไปยังคอนเทนเนอร์ Azure ดำเนินการต่อไปยัง เฟส 2 — ตรวจสอบ เพื่อยืนยันว่ามีการ resolve
การ Integration อย่างต่อเนื่อง
หัวข้อที่มีชื่อว่า “การ Integration อย่างต่อเนื่อง”workflow .github/workflows/terraform.yml รัน plan ทุกครั้งที่มี pull request และ apply
เมื่อ merge ไปยัง main โดยอ่านพิกัดแบ็กเอนด์และอินพุตจากตัวแปร repository และข้อมูลรับรองจาก secrets โดย inject แต่ละตัวเข้าไปในขั้นตอนที่ต้องการเท่านั้น และทำให้การรันเป็นแบบ serialize บน concurrency group เพื่อให้ apply สองรายการไม่แข่งขันกันบน state blob ที่ใช้ร่วมกัน
name: Terraform
on: pull_request: branches: [main] paths: ['terraform/**', '.github/workflows/terraform.yml'] push: branches: [main] paths: ['terraform/**']
permissions: contents: read
concurrency: group: terraform-state cancel-in-progress: false
env: TF_IN_AUTOMATION: 'true'
jobs: terraform: name: ${{ github.event_name == 'push' && 'apply' || 'plan' }} runs-on: ubuntu-latest defaults: run: working-directory: terraform steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Setup Terraform uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
- name: Init env: ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }} RESOURCE_GROUP: ${{ vars.TFSTATE_RESOURCE_GROUP }} STORAGE_ACCOUNT: ${{ vars.TFSTATE_STORAGE_ACCOUNT }} CONTAINER: ${{ vars.TFSTATE_CONTAINER }} STATE_KEY: ${{ vars.TFSTATE_KEY }} run: | terraform init -input=false \ -backend-config="resource_group_name=${RESOURCE_GROUP}" \ -backend-config="storage_account_name=${STORAGE_ACCOUNT}" \ -backend-config="container_name=${CONTAINER}" \ -backend-config="key=${STATE_KEY}"
- name: Format check run: terraform fmt -check -recursive
- name: Validate run: terraform validate -no-color
- name: Plan if: github.event_name == 'pull_request' env: ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }} XCSH_API_URL: ${{ secrets.XCSH_API_URL }} XCSH_API_TOKEN: ${{ secrets.XCSH_API_TOKEN }} TF_VAR_domain: ${{ vars.DNS_DOMAIN }} run: terraform plan -input=false -no-color
- name: Apply if: github.event_name == 'push' env: ARM_ACCESS_KEY: ${{ secrets.ARM_ACCESS_KEY }} XCSH_API_URL: ${{ secrets.XCSH_API_URL }} XCSH_API_TOKEN: ${{ secrets.XCSH_API_TOKEN }} TF_VAR_domain: ${{ vars.DNS_DOMAIN }} run: terraform apply -input=false -auto-approve -no-coloractions ถูกกำหนดให้ใช้ commit SHA (พร้อมคอมเมนต์เวอร์ชัน) และ secrets ถูก scope ไปยังขั้นตอน init, plan และ apply แทนที่จะเป็นทั้ง job