思考 Terraform – 考慮提仓库结构和利用范围

首先

开始学习 Terraform 后,虽然创建一个简单的 VPC 相对容易,但是一旦考虑到实际运维方面的 terraform.tfstate 管理方式或者高效的目录结构时,就会陷入停滞。

我已经完成了初学阶段,现在正在阅读《Pragmatic Terraform on AWS》这本书,重新学习有关 Terraform 的最佳实践。为了整理我所获得的知识,我打算写一篇文章。

写东西

    • Terraform の使い所

 

    Terraform で実装したリポジトリの例とサンプルコード(一部)

不写书

    Terraform の使い方・インストール方法

Terraform 使用指南

只需使用Terraform构建云环境非常简单,但是考虑到“在构建的环境上运行应用程序的生命周期”和“从创建AWS账户到第一次使用Terraform在目标环境中执行”等因素,我们会有很多事情需要考虑。

实际的构建顺序

根据本次文章以”不用Terraform构建全部”为前提,考虑到”已获得AWS账号并完成基本设置”,总结出以下3个步骤需求:从”部署应用程序”开始。

    1. 设置账户基本配置和tfstate存储区

 

    1. 使用Terraform构建基础设施

 

    构建Terraform不支持的基础设施(图形界面、命令行界面)

其中,仅有选项2通过执行Terraform来构建环境的阶段。

image.png

1. 設定帳戶基本設定並確保tfstate存儲空間

我想获取一个AWS账户,然后根据以下网站进行设置。

    AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ – Qiita

完成此设定后,我们将进行以下配置以使用Terraform进行工作。

    1. 创建任务用户

 

    1. 发放access_key, secret_key

 

    创建后端用的S3存储桶

此外,根据情况,在运行 Terraform 之前可能还需要做以下工作。

    • Elastic IP の発行(外部IF として固定が必要なものなど)

 

    • ドメインの取得 と Route 53 の設定

 

    ACM を使用した SSL証明書の登録

参考:HashiCorp推出的Terraform多账户AWS架构。

理想情况下,由Terraform使用的基础设施应该存在于Terraform管理的基础设施之外。

用Terraform构建基础设施。

关于详细内容,请参阅《整理 Module 的规范》一节以后的部分进行了解。

3. 进行了Terraform不支持的基础设施建设(图形用户界面,命令行界面)

我们将在由Terraform构建的系统基础设施上进行应用程序的部署,以及部署依赖于该基础设施的资源。

    • Elastic Beanstalk アプリケーション

 

    • AWS Lambda アプリケーション

 

    API Gateway アプリケーション

然而,关于这第3点,我内心也感到很苦恼,对于如何分担角色还感到迷茫。

整理Module的使用规范

首先,在使用Terraform构建基础架构时,我们需要澄清一下无法回避的模块。

标准模块结构

在Terraform的官方网站上,介绍了两种标准模块结构,称为Standard Module Structure(标准模块结构)。
参考:Terraform by HashiCorp – 创建模块

    • minimal recommended module

 

    complete example of a module

構造的规则 de

规则构造中包含了以下内容。

Root module : リポジトリのルートディレクトリに「Terraform files」を配置しなければならない。これがモジュールの primary entrypoint となる。

README : Root module と ネストされた moduleは README を配置した方が良い。input や output はツールで自動生成できるため、記載する必要はない。

LICENSE : Public にモジュール公開するなら、あった方が良い。

main.tf, variables.tf, outputs.tf : 最小モジュールとして推奨されるファイル名。空でもあったほうが良い。

Variables and outputs should have descriptions. : 全ての variable と output に1~2行の説明を記載しましょう。

Nested modules. : module は modules/配下に配置しましょう。module は可能な限り複雑さを排除した振る舞いを記載し、README に用法を記載しましょう。Root module が Nested modules を呼ぶ場合、個別のリポジトリにせずに全て1つのリポジトリで管理するようにしましょう。

Examples. : module の使用方法を記載した example は examples/ 配下に配置しましょう。example には README を配置し、用法のゴールを記載しましょう。

一个最低推荐的模块

$ tree minimal-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf

一个完整的模块的例子

$ tree complete-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
├── ...
├── modules/
│   ├── nestedA/
│   │   ├── README.md
│   │   ├── variables.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   ├── nestedB/
│   ├── .../
├── examples/
│   ├── exampleA/
│   │   ├── main.tf
│   ├── exampleB/
│   ├── .../

考虑地形形状库的结构

我们将讨论在使用Terraform构建系统基础架构时实施的仓库结构。

基本概念

考虑时的基本理念如下。

    1. 组件拆分

 

    1. 环境分割

 

    1. 本地模块

 

    后端在AWS S3上管理

1. 组件拆分

在《Pragmatic Terraform on AWS》这本书中,有一节叫做「17.4 组件分割」。

这里有以下这样的描述。

环境是一个明确的界限。对于每个环境,只有一个tfstate文件的想法是直接的。然而,这种想法有一些缺点。如果将一个环境的所有资源都在一个tfstate文件中管理,一个错误就会对整体产生影响。此外,执行Terraform也需要时间。因此,让我们将环境分解为多个组件。

另外,作为具体示例,以下是一些指导准则所提供的。

    • 安定度が高いコンポーネントとそれ以外の分離

 

    • ステートフルなリソース(ストレージやデータストア)の隔離

 

    • (エンドユーザへの)影響範囲が異なるものの分割

 

    • 組織のライフサイクルに関わるリソースの分離

 

    関心事の分離

根据这些记录,我们可以发现在一个 tfstate 文件中保存具有不同角色的资源,例如“VPC”、“RDS”、“EC2”和“IAM”,是不可取的。

因此,我决定参考文章「如何在Terraform中进行组件拆分 – Qiita」来进行组件的分割。

2. 环境划分

在这里所说的环境是指Terraform所构建的服务在”生产”、”验证”或”开发”用途上的使用方式。
对于这个环境的定义有两种方法。

    • ディレクトリ分割型

 

    workspace型

在互联网上,有很多关于使用“目录分割型”来定义环境的例子。文章《Terraform运营最佳实践2019〜摒弃workspace等种种〜 – 长生村本乡工程师博客》中提到了由于“workspace型”存在的缺点,而回归到了“目录分割型”。

另外,在書籍《Pragmatic Terraform on AWS》中提到,在2018年9月的HashiCorp Japan Meetup活动中,使用workspace类型的用户只占少数,大多数用户选择进行目录分割。

在另一方面上,工作区类型的应用实例确实存在,由于对那些实例更具吸引力,因此我们决定采用工作区类型。

3. 本地模块

根据「HashiCorp 的 Terraform 模块资源」,module块作为源可以指定以下方法。

    • Local paths

 

    • Terraform Registry

 

    • GitHub

 

    • Bitbucket

 

    • Generic Git, Mercurial repositories

 

    • HTTP URLs

 

    • S3 buckets

 

    GCS buckets

这次我们将会使用的是”本地路径”。

4. 后端在 AWS S3 上进行管理。

这是一个理所当然的选择,我们采用将 AWS S3 指定为后端,并管理 terraform.tfstate 的方式。

项目结构

基于标准模块结构,并考虑了上述基本概念,设计的目录结构如下。

$ tree sample-project/
.
├── README.md
├── LICENSE
├── .secret
├── .gitignore
├── ...
├── bin             <--- リポジトリの初期化 
│   ├── init_components.sh
│   ├── init_s3.sh
│   ├── init_s3.bat
│   ├── config/
├── environments/   <--- 環境ごとの変数を定義
│   ├── common
│   │   ├── terraform.tfvars
│   ├── product
│   │   ├── terraform.tfvars
│   ├── staging
│   ├── develop
│   ├── default
├── modules/   <--- Local Module
│   ├── nestedA/
│   │   ├── README.md
│   │   ├── variables.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   ├── nestedB/
│   ├── .../
├── components/   <--- コンポーネント分割した設定群
│   ├── network/
│   │   ├── README.md
│   │   ├── main.tf        <--- 基本となる処理を記載
│   │   ├── variables.tf   <--- variables を記載
│   │   ├── outputs.tf     <--- output ブロックを記載
│   │   ├── backend.tf     <--- リモートステートを記載(terraformブロック、dataブロック)
│   │   ├── provider.tf    <--- provider を記載
│   │   ├── .terraform
│   ├── firewall/
│   ├── iam/
│   ├── s3/
│   ├── datastore/
│   ├── application/
│   ├── operation/
│   ├── .../

在执行 Terraform 时,实际上是在 components 文件夹下的任意组件目录中进行操作。

粗略的结构图如下所示。

image.png

示例代码

以防火墙组件为例,以下是组件内代码的示例。

后端

以下是定义远程状态信息的 components/firewall/backend.tf 文件的示例:

terraform {
  required_version = "0.12.3"
  backend "s3" {
    region  = "ap-northeast-1"
    encrypt = true

    bucket = "<unique-bucket-name>"
    key    = "firewall/terraform.tfstate"

    profile = "profile-name"
  }
}

data "terraform_remote_state" "network" {
  backend = "s3"

  config = {
    bucket = "<unique-bucket-name>"
    key    = "env:/${terraform.workspace}/network/terraform.tfstate"
    region = "ap-northeast-1"

    profile = "profile-name"
  }
}

如果指定terraform模块中的backend块,远程状态将在以下路径下创建。

唯一的桶名称/env:/${terraform.workspace}/防火墙/terraform.tfstate

变量

定义组件/防火墙/variables.tf 中的变量信息的示例如下:

variable "common" {
  type = map(string)

  default = {
    "default.region"  = "ap-northeast-1"
    "default.project" = "project-name"
  }
}

主要的

定义基本处理的组件是components/firewall/main.tf文件的示例如下。

module "firewall" {
  source = "../../modules/nestedB"

  common = var.common
  vpc    = data.terraform_remote_state.network.outputs.vpc
}

供应商

下面是定义provider的组件文件”components/firewall/provider.tf”的示例。

variable "aws_access_key" {
}

variable "aws_secret_key" {
}

provider "aws" {
  version    = "= 2.18.0"
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = "ap-northeast-1"
}

terraform.tfvars的意思是Terraform变量文件。

根据环境来定义变量文件 environments/common/terraform.tfvars 的示例如下。

region = "ap-northeast-1"
cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ]
amis = {
  "ap-northeast-1a" = "ami-abc123"
  "ap-northeast-1c" = "ami-def456"
}

工作空间

在每个组件目录下创建以下四个工作空间。

terraform workspace list
* default
  develop
  product
  staging

AWS CLI配置文件

设置用于访问在后端指定的 AWS S3 的配置文件信息。

[profile-name]
aws_access_key_id = AKIAXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

使用AWS S3存储桶来创建后端脚本。

使用AWS CLI创建S3 Bucket。

@echo off

set profile_name=%1
set bucket_name=%2

aws s3 mb s3://%bucket_name% ^
  --profile %profile_name%

aws s3api put-bucket-versioning ^
  --profile %profile_name% ^
  --bucket %bucket_name% ^
  --versioning-configuration Status=Enabled

aws s3api put-bucket-encryption ^
  --profile %profile_name% ^
  --bucket  %bucket_name%  ^
  --server-side-encryption-configuration file://config/config-public-access-block.json

aws s3api put-public-access-block ^
  --profile %profile_name% ^
  --bucket %bucket_name% ^
  --public-access-block-configuration file://config/config-public-access-block.json
{
    "Rules": [{
        "ApplyServerSideEncryptionByDefault": {
            "SSEAlgorithm": "AES256"
        }
    }]
}
{
    "BlockPublicAcls": true,
    "IgnorePublicAcls": true,
    "BlockPublicPolicy": true,
    "RestrictPublicBuckets": true
}

进行

在执行terraform命令时,请按以下方式进行操作。
请注意选择正确的命令实施文件夹。

// 環境変数の設定を確認
export TF_VAR_aws_access_key="AKIAXXXXXXXXXXXXXXXXXX"
export TF_VAR_aws_secret_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
echo $TF_VAR_aws_access_key
echo $TF_VAR_aws_secret_key

// workspace の選択
terraform workspace select [ default / product / staging / develop ]

// 初期化
terraform init

// Dry run
terraform plan \
-var-file="../../environments/common/terraform.tfvars" \
-var-file="../../environments/$(terraform workspace show)/terraform.tfvars"
// 環境変数の設定を確認
$env:TF_VAR_aws_access_key="AKIAXXXXXXXXXXXXXXXXXX"
$env:TF_VAR_aws_secret_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
$env:TF_VAR_public_key_path=".\.secret\public"
$env:TF_VAR_aws_access_key;
$env:TF_VAR_aws_secret_key;
$env:TF_VAR_public_key_path;
Get-ChildItem env:

// workspace の選択
terraform workspace select [ default / product / staging / develop ]

// 初期化
terraform init

// Dry run
terraform plan `
-var-file="../../environments/common/terraform.tfvars" `
-var-file="../../environments/$(terraform workspace show)/terraform.tfvars"

总结

以下是这篇文章的总结。

我取得了成功。

    1. 通过组件分割,将要在tfstate文件中管理的资源进行分割

 

    1. 使用工作区将环境(生产、验证、开发)进行分割

 

    1. 使用本地模块定义资源

 

    使用AWS S3来管理和版本控制terraform.tfstate文件

问题或挑战

    1. 在一个仓库中管理多个组件,并且每次运行”terraform init”时,提供程序工具会被安装到执行目录中。

 

    1. 不太了解模块仓库。

 

    整体上都是摸索着进行的。

解决方案 ‘àn)

    1. 或许最好为每个组件设计独立的存储库。

 

    1. 唯有实际使用才能得知。

 

    (让人犹豫不决)

最后

在写文章的过程中,我升级了Terraform的版本,从v0.11.x升级到了v0.12.x,结果我之前写的代码完全无法运行,我感到非常着急。我尝试执行了terraform 0.12upgrade命令,但似乎并不是百分百有效。

确实,由于主要版本仍然是0系列,所以会有破坏性的变更。

terraform的核心功能非常出色,而且很容易理解,但是当您实际动手操作时,需要掌握HCL语法和各个构建目标服务的规范,您可能会感到在克服初期难关时困难重重。
另外,由于工具本身仍处于发展阶段,不断出现新的语法和规范,您需要了解并跟进这些信息,这可能会增加一些额外的工作。

然而,由于Terraform带来的好处远大于其成本,我希望能够更多地了解Terraform。

请提供中文指南。

在撰写这篇文章时所参考的信息。

目录结构

    • Terraform Best Practices in 2017 – Qiita

 

    • Terraformにおけるディレクトリ構造のベストプラクティス – Developers.IO

 

    • shogomuranushi/oreno-terraform – GitHub

 

    • DevOpsを支える今話題のHashiCorpツール群についてに登壇してきました – てっくぼっと!

 

    • Terraform workspaceを利用する。環境ごとのリソース名の分岐など – Goldstine研究所

 

    TerraformのModuleソースとしてTerraform Enterprise’s Private Module Registryを利用する – GMOメディア エンジニアブログ

Terraform 的分割单位

    • terraformはどの単位で分割すべきか – Qiita

 

    • Terraformと変数(variable)の話 – CUPSULE CLOUD

 

    • Feature: Conditionally load tfvars/tf file based on Workspace #15966 – GitHub

 

    Variable Definitions (.tfvars) Files – Input Variables – Configuration Language – Terraform by HashiCorp

国家的管理

    Backend の S3 や DynamoDB 自体を terraform で管理するセットアップ方法 – Qiita

样本

    • cloudposse/terraform-aws-elastic-beanstalk-application – GitHub

 

    terraform-aws-modules/terraform-aws-vpc – GitHub

Terraform 版本 v0.12.x

    terraform v0.12 アップデート terraform 0.12upgrade,terraform 0.12checklistサブコマンド実行結果と、ファイルの変更例 – Qiita

使用AWS S3作为terraform.tfstate的存储。

    • 独り Terraform 研究所 (1) 〜 Backend についてドキュメントを読んだり, チュートリアルしたり 〜 – ようへいの日々精進XP

 

    S3の暗号化についてまとめてみた(2018年6月版) – 本日も乙
bannerAds