一,引言
我們都知道在執行部署計划之后,當前目錄中就產生了名叫 “terraform.states” 的 Terraform 的狀態文件,該文件中記錄了已部署資源的狀態。默認情況下,在執行部署計划后,Terraform 的狀態文件會存儲在本地,但是這樣往往就造成一些弊端:
(1)不適用團隊之間協助,就好比在數據庫中對同一條數據進行操作時,就會引起異常
(2)狀態文件中包含一些機密信息,會造成一定的機密泄露
(3)如果不慎將本地的狀態文件刪除掉的話,已執行部署計划的資源的管理將很難在通過 Terraform 進行管理
所以,Terraform 是支持在遠端存儲狀態文件,也就是在 Azure Storage Account 中存儲遠端狀態文件,Terraform 狀態的存儲是由一個稱之為Backend的組件決定的,local state使用的是local backend。並且其他所有的Backend在使用之前都需要在模板中顯式定義並通過 terraform init 來實現加載和配置。
--------------------Azure Terraform 系列--------------------
1,Azure Terraform(一)入門簡介
2,Azure Terraform(二)語法詳解
3,Azure Terraform(三)部署 Web 應用程序
4,Azure Terraform(四)狀態文件存儲
5,Azure Terraform(五)利用Azure DevOps 實現自動化部署基礎資源
6,Azure Terraform(六)Common Module
7,Azure Terraform(七)利用Azure DevOps 實現自動化部署基礎資源(補充)
8,Azure Terraform(八)利用Azure DevOps 實現Infra資源和.NET CORE Web 應用程序的持續集成、持續部署
二,正文
1,創建狀態文件存儲賬戶
轉到Azure Portal 上,點擊 “+ Create a resource”,輸入 “Storage account“ 進行搜索,並且點擊 ”create“
輸入以下參數:
Resource group 選擇:”Web_Test_TF_RG“
Storage account name:”cnbateterraformstorage“
Location:”(Asia Pacific) East Asia“
Performance:”Standard“
Account kind (賬戶類型)選擇:”BlobStorage“
Replication (復制)選擇:”Locally-redundant storage(LRS)“ (本地冗余存儲(LRS))
點擊 ”Review + create“ 進行創建預校驗
校驗完成后,點擊 ”Create“ 進行創建操作
稍等片刻,等待創建完成后,點擊 ”go to resource“ 跳轉到資源可以查看創建的資源。
選擇 “Blog service =》Containers”,點擊頁面上的 “+ Container” 添加存儲狀態文件的 Container
Name:"terraform-state"
Public access level:“Private(no anonymous access)”
點擊 “Create” 進行創建。
可以看到剛剛創建容器
復制存儲賬戶的訪問密鑰,稍后會有用
2,創建 Azure Key Vault(密鑰保管庫)
回到 Azure Portal 首頁,點擊 ”+ create a resource“,輸入”Key Vault“ 進行搜索,點擊 ”Create“ 創建
輸入相關參數:
Resource group 選擇:”Web_Test_TF_RG“
Key vault name:”cnbate-terraform-kv“
Region:”East Asia“
Pricing tier:”Standard“
點擊 ”Review + create“ ,創建預校驗。
預校驗完成后,點擊 ”Create“ 進行創建操作
創建完成后,可以點擊 ”Go to resource“ 查看創建好的資源
選擇 “Settings=》Secrets”,點擊 “+ Generate/Import” 創建、或者導入機密信息
Upload options:“Manual”(手動)
Name:“terraform-stste-storage-key”
Value:復制粘貼剛剛的存儲賬戶訪問密鑰
創建成功,並且可以查看到剛剛創建的機密信息
3,配置 Terraform 后端,並且測試遠程 tf 狀態
Terraform 需要配置后端,需要以下參數
(1)storage_account_name :Azure 存儲賬戶名稱
terraform init -backend-config="access_key=$(az keyvault secret show --name terraform-stste-storage-key --vault-name cnbate-terraform-kv --query value -o tsv)"
(2)container_name:容器名稱
(3)key:存儲狀態文件的名稱
(4)access_key:存儲賬戶訪問密鑰
大家需要注意的是,我這里將 “access_key” 也就是存儲賬戶訪問密鑰存放在 Azure Key Vault 中了,想要獲取 “access_key” 就得通過 Azure Key Vault 獲取。
以下是 Terraform 后端配置
terraform { backend "azurerm" { storage_account_name = "cnbateterraformstorage" container_name = "terraform-state" key = "cnbate.terraform.stats" } }
3.1,初始化 Terraform 代碼
既然我們沒有在 Terraform 后端配置代碼塊中添加 “access_key” 的信息,那么我們就得在初始化的時候對 ”access_key“ 信息賦值
terraform init -backend-config="access_key=$(az keyvault secret show --name terraform-stste-storage-key --vault-name cnbate-terraform-kv --query value -o tsv)"
然后,我們可以看到執行初始化命令之后的日志輸出
同時會在存儲賬戶的容器中生成 Blob 塊
並且可以看到當前blob 塊的詳細信息
LEASE STATUS:解鎖狀態
LEASE STATE:可用狀態
3.2,生成執行計划
terraform plan
以下是執行后輸入的日志信息
從中不難看到,在生成執行計划之前,先獲取狀態鎖。(注意,生成執行計划。不會將該執行計划持久化到遠程狀態存儲)
PS D:\Core\Terraform\Azure\terraform_cnbate_traffic manager> terraform plan Acquiring state lock. This may take a few moments... 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.azurerm_resource_group.cnbate_resource_group: 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: # azurerm_app_service.cnbate_app_service01 will be created + resource "azurerm_app_service" "cnbate_app_service01" { + app_service_plan_id = (known after apply) + app_settings = { + "ASPNETCORE_ENVIRONMENT" = "Production" } + client_affinity_enabled = false + client_cert_enabled = false + custom_domain_verification_id = (known after apply) + default_site_hostname = (known after apply) + enabled = true + https_only = false + id = (known after apply) + location = "eastasia" + name = "CnBateBlogWeb01" + outbound_ip_address_list = (known after apply) + outbound_ip_addresses = (known after apply) + possible_outbound_ip_address_list = (known after apply) + possible_outbound_ip_addresses = (known after apply) + resource_group_name = "Web_Test_TF_RG" + site_credential = (known after apply) + auth_settings { + additional_login_params = (known after apply) + allowed_external_redirect_urls = (known after apply) + default_provider = (known after apply) + enabled = (known after apply) + issuer = (known after apply) + runtime_version = (known after apply) + token_refresh_extension_hours = (known after apply) + token_store_enabled = (known after apply) + unauthenticated_client_action = (known after apply) + active_directory { + allowed_audiences = (known after apply) + client_id = (known after apply) + client_secret = (sensitive value) } + facebook { + app_id = (known after apply) + app_secret = (sensitive value) + oauth_scopes = (known after apply) } + google { + client_id = (known after apply) + client_secret = (sensitive value) + oauth_scopes = (known after apply) } + microsoft { + client_id = (known after apply) + client_secret = (sensitive value) + oauth_scopes = (known after apply) } + twitter { + consumer_key = (known after apply) + consumer_secret = (sensitive value) } } + connection_string { + name = (known after apply) + type = (known after apply) + value = (sensitive value) } + identity { + identity_ids = (known after apply) + principal_id = (known after apply) + tenant_id = (known after apply) + type = (known after apply) } + logs { + detailed_error_messages_enabled = (known after apply) + failed_request_tracing_enabled = (known after apply) + application_logs { + file_system_level = (known after apply) + azure_blob_storage { + level = (known after apply) + retention_in_days = (known after apply) + sas_url = (sensitive value) } } + http_logs { + azure_blob_storage { + retention_in_days = (known after apply) + sas_url = (sensitive value) } + file_system { + retention_in_days = (known after apply) + retention_in_mb = (known after apply) } } } + site_config { + always_on = (known after apply) + app_command_line = (known after apply) + auto_swap_slot_name = (known after apply) + default_documents = (known after apply) + dotnet_framework_version = (known after apply) + ftps_state = (known after apply) + health_check_path = (known after apply) + http2_enabled = (known after apply) + ip_restriction = (known after apply) + java_container = (known after apply) + java_container_version = (known after apply) + java_version = (known after apply) + linux_fx_version = (known after apply) + local_mysql_enabled = (known after apply) + managed_pipeline_mode = (known after apply) + min_tls_version = (known after apply) + php_version = (known after apply) + python_version = (known after apply) + remote_debugging_enabled = (known after apply) + remote_debugging_version = (known after apply) + scm_ip_restriction = (known after apply) + scm_type = (known after apply) + scm_use_main_ip_restriction = (known after apply) + use_32_bit_worker_process = (known after apply) + websockets_enabled = (known after apply) + windows_fx_version = (known after apply) + cors { + allowed_origins = (known after apply) + support_credentials = (known after apply) } } + source_control { + branch = (known after apply) + manual_integration = (known after apply) + repo_url = (known after apply) + rollback_enabled = (known after apply) + use_mercurial = (known after apply) } + storage_account { + access_key = (sensitive value) + account_name = (known after apply) + mount_path = (known after apply) + name = (known after apply) + share_name = (known after apply) + type = (known after apply) } } # azurerm_app_service.cnbate_app_service02 will be created + resource "azurerm_app_service" "cnbate_app_service02" { + app_service_plan_id = (known after apply) + app_settings = { + "ASPNETCORE_ENVIRONMENT" = "Production" } + client_affinity_enabled = false + client_cert_enabled = false + custom_domain_verification_id = (known after apply) + default_site_hostname = (known after apply) + enabled = true + https_only = false + id = (known after apply) + location = "southeastasia" + name = "CnBateBlogWeb02" + outbound_ip_address_list = (known after apply) + outbound_ip_addresses = (known after apply) + possible_outbound_ip_address_list = (known after apply) + possible_outbound_ip_addresses = (known after apply) + resource_group_name = "Web_Test_TF_RG" + site_credential = (known after apply) + auth_settings { + additional_login_params = (known after apply) + allowed_external_redirect_urls = (known after apply) + default_provider = (known after apply) + enabled = (known after apply) + issuer = (known after apply) + runtime_version = (known after apply) + token_refresh_extension_hours = (known after apply) + token_store_enabled = (known after apply) + unauthenticated_client_action = (known after apply) + active_directory { + allowed_audiences = (known after apply) + client_id = (known after apply) + client_secret = (sensitive value) } + facebook { + app_id = (known after apply) + app_secret = (sensitive value) + oauth_scopes = (known after apply) } + google { + client_id = (known after apply) + client_secret = (sensitive value) + oauth_scopes = (known after apply) } + microsoft { + client_id = (known after apply) + client_secret = (sensitive value) + oauth_scopes = (known after apply) } + twitter { + consumer_key = (known after apply) + consumer_secret = (sensitive value) } } + connection_string { + name = (known after apply) + type = (known after apply) + value = (sensitive value) } + identity { + identity_ids = (known after apply) + principal_id = (known after apply) + tenant_id = (known after apply) + type = (known after apply) } + logs { + detailed_error_messages_enabled = (known after apply) + failed_request_tracing_enabled = (known after apply) + application_logs { + file_system_level = (known after apply) + azure_blob_storage { + level = (known after apply) + retention_in_days = (known after apply) + sas_url = (sensitive value) } } + http_logs { + azure_blob_storage { + retention_in_days = (known after apply) + sas_url = (sensitive value) } + file_system { + retention_in_days = (known after apply) + retention_in_mb = (known after apply) } } } + site_config { + always_on = (known after apply) + app_command_line = (known after apply) + auto_swap_slot_name = (known after apply) + default_documents = (known after apply) + dotnet_framework_version = (known after apply) + ftps_state = (known after apply) + health_check_path = (known after apply) + http2_enabled = (known after apply) + ip_restriction = (known after apply) + java_container = (known after apply) + java_container_version = (known after apply) + java_version = (known after apply) + linux_fx_version = (known after apply) + local_mysql_enabled = (known after apply) + managed_pipeline_mode = (known after apply) + min_tls_version = (known after apply) + php_version = (known after apply) + python_version = (known after apply) + remote_debugging_enabled = (known after apply) + remote_debugging_version = (known after apply) + scm_ip_restriction = (known after apply) + scm_type = (known after apply) + scm_use_main_ip_restriction = (known after apply) + use_32_bit_worker_process = (known after apply) + websockets_enabled = (known after apply) + windows_fx_version = (known after apply) + cors { + allowed_origins = (known after apply) + support_credentials = (known after apply) } } + source_control { + branch = (known after apply) + manual_integration = (known after apply) + repo_url = (known after apply) + rollback_enabled = (known after apply) + use_mercurial = (known after apply) } + storage_account { + access_key = (sensitive value) + account_name = (known after apply) + mount_path = (known after apply) + name = (known after apply) + share_name = (known after apply) + type = (known after apply) } } # azurerm_app_service_plan.cnbate_app_service_plan01 will be created + resource "azurerm_app_service_plan" "cnbate_app_service_plan01" { + id = (known after apply) + kind = "Windows" + location = "eastasia" + maximum_elastic_worker_count = (known after apply) + maximum_number_of_workers = (known after apply) + name = "cnbate_appserviceplan01" + resource_group_name = "Web_Test_TF_RG" + sku { + capacity = (known after apply) + size = "S1" + tier = "Standard" } } # azurerm_app_service_plan.cnbate_app_service_plan02 will be created + resource "azurerm_app_service_plan" "cnbate_app_service_plan02" { + id = (known after apply) + kind = "Windows" + location = "southeastasia" + maximum_elastic_worker_count = (known after apply) + maximum_number_of_workers = (known after apply) + name = "cnbate_appserviceplan02" + resource_group_name = "Web_Test_TF_RG" + sku { + capacity = (known after apply) + size = "S1" + tier = "Standard" } } # azurerm_traffic_manager_endpoint.cnbate_traffic_manager_endpoint01 will be created + resource "azurerm_traffic_manager_endpoint" "cnbate_traffic_manager_endpoint01" { + endpoint_location = (known after apply) + endpoint_monitor_status = (known after apply) + endpoint_status = (known after apply) + geo_mappings = [ + "CN", ] + id = (known after apply) + name = "cnbateblogweb_webapp01_performance" + priority = (known after apply) + profile_name = "cnbateblogweb" + resource_group_name = "Web_Test_TF_RG" + target = (known after apply) + target_resource_id = (known after apply) + type = "azureEndpoints" + weight = (known after apply) } # azurerm_traffic_manager_endpoint.cnbate_traffic_manager_endpoint02 will be created + resource "azurerm_traffic_manager_endpoint" "cnbate_traffic_manager_endpoint02" { + endpoint_location = (known after apply) + endpoint_monitor_status = (known after apply) + endpoint_status = (known after apply) + geo_mappings = [ + "SG", ] + id = (known after apply) + name = "cnbateblogweb_webapp02_performance" + priority = (known after apply) + profile_name = "cnbateblogweb" + resource_group_name = "Web_Test_TF_RG" + target = (known after apply) + target_resource_id = (known after apply) + type = "azureEndpoints" + weight = (known after apply) } # azurerm_traffic_manager_profile.cnbate_traffic_manager_profile will be created + resource "azurerm_traffic_manager_profile" "cnbate_traffic_manager_profile" { + fqdn = (known after apply) + id = (known after apply) + name = "cnbateblogweb" + profile_status = (known after apply) + resource_group_name = "Web_Test_TF_RG" + tags = { + "Environment" = "Production" } + traffic_routing_method = "Geographic" + dns_config { + relative_name = "cnbateblogweb" + ttl = 60 } + monitor_config { + interval_in_seconds = 30 + path = "/" + port = 80 + protocol = "http" + timeout_in_seconds = 10 + tolerated_number_of_failures = 3 } } Plan: 7 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ 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. PS D:\Core\Terraform\Azure\terraform_cnbate_traffic manager>
3.3,執行部署計划
terraform apply
以下是正在執行部署計划輸入日志
同樣,也是先獲取狀態鎖
PS D:\Core\Terraform\Azure\terraform_cnbate_traffic manager> terraform plan Acquiring state lock. This may take a few moments... 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.azurerm_resource_group.cnbate_resource_group: Refreshing state...
在執行的過程中,我們再次登錄Azure Portal 中,查看存儲賬容器中的 Blob 塊的狀態
LEASE STATUS:已鎖定
LEASE STATE:已租用(其實可以理解為 “不可用”)
等待部署計划執行完畢之后,Blob 塊的狀態又恢復到 “已解鎖,可用”
同時,點擊圖中的 “Edit” 可以看到由 terraform 管理的各自資源狀態信息全部寫到了當前 Blob 塊中
ok,今天的內容就先到此。重要提醒:大家做完測試后,別忘記執行 terraform destroy (銷毀部署計划)
*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。φ(゜▽゜*)♪是✌✌✌✌✌
三,結尾
將狀態文件進行遠端存到Azure Storage Account 中,Blob 中存儲的數據會在保存前進行加密處理,並且一旦配置遠端存儲模式后,狀態文件永遠不會存儲在本地,這樣更加方面團隊之間的協作。並且遠端存儲帶來的好處是實現了與資源定義模板管理的解耦,可以讓 Terraform 狀態脫離本地磁盤而存儲,提升了資源狀態的安全性。
參考資料:Terraform 官方,azurerm 文檔
Terraform_Cnbate_Traffic_Manager github:https://github.com/yunqian44/Terraform_Cnbate_Traffic_Manager
作者:Allen
版權:轉載請在文章明顯位置注明作者及出處。如發現錯誤,歡迎批評指正。