Terraform 在連網環境執行 init 時會自動從 HashiCorp 的 registry 下載 provider plugin 和 module。斷網環境沒有這個路徑——provider、module、state backend 全部要用離線替代。IaC 的核心價值(宣告式描述 + state 追蹤 + plan 預覽)不因斷網而改變,改變的只是依賴的取得方式和 state 的存放位置。

Provider 離線管理

Provider Mirror

Terraform 的 providers mirror 指令在有網路的環境把指定 provider 的二進位檔下載到本地目錄,產出符合 filesystem mirror 結構的檔案:

 1# 在有網路的工作站執行
 2mkdir -p /path/to/mirror
 3terraform providers mirror -platform=linux_amd64 /path/to/mirror
 4
 5# mirror 目錄結構
 6# /path/to/mirror/
 7# └── registry.terraform.io/
 8#     └── hashicorp/
 9#         └── aws/
10#             └── 5.50.0/
11#                 └── terraform-provider-aws_5.50.0_linux_amd64.zip

把整個 mirror 目錄搬進隔離網路後,在 Terraform 設定裡指定 filesystem mirror:

 1# ~/.terraformrc 或 terraform.rc(Windows)
 2provider_installation {
 3  filesystem_mirror {
 4    path    = "/opt/terraform/providers"
 5    include = ["registry.terraform.io/*/*"]
 6  }
 7  direct {
 8    exclude = ["registry.terraform.io/*/*"]
 9  }
10}

direct 區塊的 exclude 確保 Terraform 不會嘗試連網下載——如果 mirror 裡沒有某個 provider,init 會直接報錯而非 hang 在網路連線。

Plugin Cache

替代 mirror 的另一個做法是 plugin cache directory。在有網路的環境跑過 init 後,.terraform/providers/ 裡會有已下載的 plugin。把這整個目錄搬進隔離網路,用 TF_PLUGIN_CACHE_DIR 環境變數指向它:

1export TF_PLUGIN_CACHE_DIR="/opt/terraform/plugin-cache"
2terraform init

mirror 跟 plugin cache 的差別:mirror 是正式的離線分發機制(有版本結構、支援多平台)、plugin cache 是快取機制(省重複下載、但目錄結構跟 mirror 不同)。長期運作用 mirror,臨時驗證用 cache。

Provider 版本鎖定

斷網環境的 provider 版本管理比連網更嚴格——升級一個 provider 代表要重新搬運整個 provider binary。在 versions.tf 裡鎖定精確版本(= 5.50.0 而非 ~> 5.50),避免 init 期待一個 mirror 裡沒有的版本:

1terraform {
2  required_providers {
3    aws = {
4      source  = "hashicorp/aws"
5      version = "= 5.50.0"
6    }
7  }
8}

Module 離線來源

連網環境的 module source 常指向 Terraform Registry 或 GitHub:source = "terraform-aws-modules/vpc/aws"。斷網環境要改成本地路徑或內部 git server。

本地路徑

最簡單——module 放在同一個 repo 或共用檔案系統的目錄裡:

1module "network" {
2  source = "../../modules/network"
3}

內部 Git Server

如果有架 Gitea 或 GitLab CE(見斷網通用原則),module 可以指向內部的 git repo:

1module "network" {
2  source = "git::http://gitea.internal/infra/modules.git//network?ref=v1.2.0"
3}

ref=v1.2.0 鎖定版本。內部 git server 的 module repo 用 git bundle 從外部搬運更新。

State Backend:沒有 S3 時的替代

連網環境的 state 通常放 S3 + DynamoDB lock。斷網環境如果沒有 AWS(地端機房或隔離網路),state backend 的替代選項:

Backend適用情境Lock 機制
本地檔案 + 共用磁碟小團隊、單人操作無(靠紀律避免並行 apply)
Consul內網有 Consul cluster內建 lock
PostgreSQL內網有 PostgreSQL內建 lock
GitLab managed state內網有 GitLab CE內建 lock
HTTP backend自建簡易 API自建 lock

最常見的組合是 PostgreSQL backend——多數環境已經有 PostgreSQL,不需要額外裝服務:

1terraform {
2  backend "pg" {
3    conn_str = "postgres://terraform:password@db.internal/terraform_state?sslmode=disable"
4  }
5}

PostgreSQL backend 的 lock 機制用 PostgreSQL 的 advisory lock,多人同時 apply 時第二個人會被擋住。

state 的備份紀律不變——定期 terraform state pull > backup.json,backup 存在版本控制或另一台機器上。

Plan / Apply 流程

斷網不影響 plan 和 apply 的執行——它們操作的是本地 provider 和目標基礎設施(地端伺服器、內部雲、VMware vSphere 等)。影響的是 provider 初始化和 module 取得,這些在前面幾節已處理。

沒有雲端 API 的情境

如果基礎設施不是雲端(地端 VMware、OpenStack、裸機),Terraform 有對應的 provider:

  • VMware vSphere:hashicorp/vsphere
  • OpenStack:terraform-provider-openstack/openstack
  • Proxmox:telmate/proxmox(社群維護)
  • 裸機管理:用 null_resource + local-exec 呼叫 Ansible 或 shell script

provider 的離線管理方式相同——mirror 或 plugin cache。

Plan 輸出的離線 Review

沒有 GitHub PR 的環境,plan 輸出用檔案分享 review:

1# 產出 plan 並存成可讀格式
2terraform plan -out=plan.tfplan
3terraform show plan.tfplan > plan-review-$(date +%Y%m%d).txt
4
5# 把 review 檔放到內部共用位置供 reviewer 閱讀
6cp plan-review-*.txt /shared/reviews/

reviewer 讀完後以 email、內部 chat、或直接在 review 檔旁邊放一個 approved-by-alice-20260626.txt 標記核准。不優雅但可追溯。

內網 CI/CD

斷網環境的 CI/CD 用自架的 CI server:

工具特性適用規模
GitLab CE + Runner完整的 git + CI + review,功能最豐富中大團隊
Gitea + Drone / Woodpecker輕量 git + 輕量 CI小團隊
Jenkins老牌 CI、plugin 生態豐富任何規模(但維護成本高)

CI server 本身也需要離線安裝——GitLab CE 有 offline 安裝指南(.deb / .rpm 包)、Gitea 是單一二進位。CI runner 執行 Terraform 時使用內部的 provider mirror 和 module source。

CI workflow 的離線版本跟連網版本結構相同(init → fmt → validate → plan → review → apply),差別在 init 用 -plugin-dir 而非連網下載。

時程參考:內網 CI server 的初次建置(含 git server + CI runner + Terraform 離線環境)約需 3-5 天。之後的維護主要是 provider 版本更新的搬運(每次 1-2 小時)。

跨分類引用