Continuous integration 使用Terraform时的最佳实践

Continuous integration 使用Terraform时的最佳实践,continuous-integration,terraform,Continuous Integration,Terraform,我正在将我们的基础设施转换为地形。 实际管理地形文件和状态的最佳实践是什么? 我意识到它是作为代码的基础设施,我将把.tf文件提交到git中,但我是否也提交了tfstate?它应该驻留在S3这样的地方吗?我希望最终让CI来管理所有这些,但这远远不够,需要我找出文件的移动部分 我真的只是想看看那里的人们是如何在生产中实际使用这种类型的东西的我还处于将现有AWS基础设施迁移到Terraform的状态,因此我将在开发过程中更新答案 我一直严重依赖于官方的地形和多次尝试和错误来充实我一直不确定的领域 .

我正在将我们的基础设施转换为地形。 实际管理地形文件和状态的最佳实践是什么? 我意识到它是作为代码的基础设施,我将把.tf文件提交到git中,但我是否也提交了tfstate?它应该驻留在S3这样的地方吗?我希望最终让CI来管理所有这些,但这远远不够,需要我找出文件的移动部分


我真的只是想看看那里的人们是如何在生产中实际使用这种类型的东西的

我还处于将现有AWS基础设施迁移到Terraform的状态,因此我将在开发过程中更新答案

我一直严重依赖于官方的地形和多次尝试和错误来充实我一直不确定的领域

.tfstate
文件

Terraform config可用于在不同的基础设施上配置多个箱子,每个箱子可能具有不同的状态。因为它也可以由多人运行,所以这个状态应该位于一个集中的位置(如S3),而不是git

这可以通过观察地形得到证实

开发者控制

我们的目标是为开发人员提供对基础设施的更多控制,同时维护完整审计(git日志)和健全检查更改(pull请求)的能力。考虑到这一点,我的目标是实现新的基础架构工作流:

  • 常见AMI的基础,包括可重用的模块,如傀儡,
  • DevOps使用Terraform提供的核心基础设施
  • 开发人员根据需要更改Git中的Terraform配置(实例数量;新VPC;添加区域/可用区域等)
  • 推送Git配置,并提交拉取请求,以供DevOps团队的一名成员进行健全性检查
  • 如果获得批准,将webhook调用到CI以构建和部署(此时不确定如何划分多个环境)
  • 编辑1-更新当前状态

    自从开始回答这个问题以来,我已经写了很多TF代码,并且对我们的现状感到更舒服。我们一路上遇到了bug和限制,但我承认这是使用新的、快速变化的软件的一个特点

    布局

    我们有一个复杂的AWS基础设施,有多个VPC,每个VPC都有多个子网。轻松管理的关键是定义一个灵活的分类法,它包含区域、环境、服务和所有者,我们可以使用它来组织我们的基础设施代码(terraform和puppet)

    模块

    下一步是创建一个git存储库来存储我们的terraform模块。模块的顶级目录结构如下所示:

    stage
      └ main.tf
      └ vars.tf
      └ outputs.tf
    prod
      └ main.tf
      └ vars.tf
      └ outputs.tf
    global
      └ main.tf
      └ vars.tf
      └ outputs.tf
    
    data "aws_region" "current" {}
    
    resource "aws_s3_bucket" "bucket" {
      bucket = "my-tf-test-bucket-${data.aws_region.current.name}-${terraform.workspace}"
    }
    
    tree-l1。
    
    结果:

    ├── README.md
    ├── aws-asg
    ├── aws-ec2
    ├── aws-elb
    ├── aws-rds
    ├── aws-sg
    ├── aws-vpc
    └── templates
    
    每一个都设置了一些合理的默认值,但将它们作为变量公开,这些变量可以被我们的“胶水”覆盖

    胶水

    我们还有第二个存储库,其中包含
    胶水
    ,它利用了上述模块。其布局符合我们的分类文件:

    .
    ├── README.md
    ├── clientA
    │   ├── eu-west-1
    │   │   └── dev
    │   └── us-east-1
    │       └── dev
    ├── clientB
    │   ├── eu-west-1
    │   │   ├── dev
    │   │   ├── ec2-keys.tf
    │   │   ├── prod
    │   │   └── terraform.tfstate
    │   ├── iam.tf
    │   ├── terraform.tfstate
    │   └── terraform.tfstate.backup
    └── clientC
        ├── eu-west-1
        │   ├── aws.tf
        │   ├── dev
        │   ├── iam-roles.tf
        │   ├── ec2-keys.tf
        │   ├── prod
        │   ├── stg
        │   └── terraform.tfstate
        └── iam.tf
    
    在客户端级别,我们有AWS特定于帐户的
    .tf
    文件,用于提供全局资源(如IAM角色);接下来是区域级,使用EC2 SSH公钥;最后,在我们的环境中(
    dev
    stg
    prod
    等)存储了我们的VPC设置、实例创建和对等连接等

    旁注:正如你所看到的,我违背了我自己的建议,将
    terraform.tfstate
    保留在git中。这是一个临时措施,直到我转到S3,但适合我,因为我目前是唯一的开发人员

    下一步

    这仍然是一个手动过程,在Jenkins中还没有,但我们正在移植一个相当大、复杂的基础架构,到目前为止还不错。就像我说的,很少有虫子,但进展顺利

    编辑2-更改

    我写下这个最初的答案已经快一年了,地球和我自己的状态都发生了显著的变化。我现在在一个新的位置上使用Terraform来管理Azure集群,Terraform现在是
    v0.10.7

    状态

    人们一再告诉我,州政府不应该使用Git——他们是正确的。我们将此作为一个依靠开发人员沟通和纪律的两人团队的临时措施。有了一个更大的分布式团队,我们现在可以充分利用DynamoDB提供的S3中的远程状态。理想情况下,这将迁移到Concur,现在它是v1.0,以切断跨云提供商

    模块

    之前我们创建并使用了内部模块。这种情况仍然存在,但随着信息技术的出现和发展,我们尝试将其作为至少一个基础

    文件结构

    新职位的分类更加简单,只有两个infx环境——
    dev
    prod
    。每个模块都有自己的变量和输出,重用上面创建的模块。提供程序还帮助在环境之间共享已创建资源的输出。我们的场景是将不同Azure资源组中的子域连接到一个全局管理的TLD

    ├── main.tf
    ├── dev
    │   ├── main.tf
    │   ├── output.tf
    │   └── variables.tf
    └── prod
        ├── main.tf
        ├── output.tf
        └── variables.tf
    
    规划

    同样,由于分布式团队的额外挑战,我们现在总是保存
    terraform plan
    命令的输出。我们可以检查并知道将运行什么,而不会在
    计划
    应用
    阶段之间发生某些更改(尽管锁定有助于此)。请记住删除此计划文件,因为它可能包含纯文本“机密”变量


    总的来说,我们对Terraform非常满意,并通过添加新功能继续学习和改进。

    以前的
    远程配置
    允许这样做,但现在已被“”取代,因此Terraform remote不再可用

    terraform远程配置-
    
    ⁃   Modules
    ⁃   Environment management 
    ⁃   Separation of duties
    
    .
    ├── 1_tf-backend #remote AWS S3 + Dynamo Lock tfstate 
    │   ├── main.tf
    │   ├── ...
    ├── 2_secrets
    │   ├── main.tf
    │   ├── ...
    ├── 3_identities
    │   ├── account.tf
    │   ├── roles.tf
    │   ├── group.tf
    │   ├── users.tf
    │   ├── ...
    ├── 4_security
    │   ├── awscloudtrail.tf
    │   ├── awsconfig.tf
    │   ├── awsinspector.tf
    │   ├── awsguarduty.tf
    │   ├── awswaf.tf
    │   └── ...
    ├── 5_network
    │   ├── account.tf
    │   ├── dns_remote_zone_auth.tf
    │   ├── dns.tf
    │   ├── network.tf
    │   ├── network_vpc_peering_dev.tf
    │   ├── ...
    ├── 6_notifications
    │   ├── ...
    ├── 7_containers
    │   ├── account.tf
    │   ├── container_registry.tf
    │   ├── ...
    ├── config
    │   ├── backend.config
    │   └── main.config
    └── readme.md
    
    ├── main.tf
    ├── dev
    │   ├── main.tf
    │   ├── output.tf
    │   └── variables.tf
    └── prod
    ├── main.tf
    ├── output.tf
    └── variables.tf
    
    ├── deployment
    │ ├── 01-network.tf
    │ ├── 02-ecs_cluster.tf
    │ ├── 03-ecs_service.tf
    │ ├── 04-eks_infra.tf
    │ ├── 05-db_infra.tf
    │ ├── 06-codebuild-k8s.tf
    │ ├── 07-aws-secret.tf
    │ ├── backend.tf
    │ ├── provider.tf
    │ └── variables.tf
    ├── env
    │ ├── dev
    │ │ ├── dev.backend.tfvar
    │ │ └── dev.variables.tfvar
    │ └── prod
    │ ├── prod.backend.tfvar
    │ └── prod.variables.tfvar
    ├── modules
    │ └── aws
    │ ├── compute
    │ │ ├── alb_loadbalancer
    │ │ ├── alb_target_grp
    │ │ ├── ecs_cluster
    │ │ ├── ecs_service
    │ │ └── launch_configuration
    │ ├── database
    │ │ ├── db_main
    │ │ ├── db_option_group
    │ │ ├── db_parameter_group
    │ │ └── db_subnet_group
    │ ├── developertools
    │ ├── network
    │ │ ├── internet_gateway
    │ │ ├── nat_gateway
    │ │ ├── route_table
    │ │ ├── security_group
    │ │ ├── subnet
    │ │ ├── vpc
    │ └── security
    │ ├── iam_role
    │ └── secret-manager
    └── templates
    
      region = "ap-southeast-2"
      bucket = "dev-samplebackendterraform"
      key = "dev/state.tfstate"
      dynamo_db_lock = "dev-terraform-state-lock"
    
    environment                     =   "dev"
    vpc_name                        =   "demo"
    vpc_cidr_block                  =   "10.20.0.0/19"
    private_subnet_1a_cidr_block    =   "10.20.0.0/21"
    private_subnet_1b_cidr_block    =   "10.20.8.0/21"
    public_subnet_1a_cidr_block     =   "10.20.16.0/21"
    public_subnet_1b_cidr_block     =   "10.20.24.0/21"
    
    variable vpc_create {
       default = "true"
    }
    
    module "vpc" {
      source = "../modules/aws/network/vpc"
      enable = "${var.vpc_create}"
      vpc_cidr_block = "${var.vpc_cidr_block}"
      name = "${var.vpc_name}"
     }
    
     resource "aws_vpc" "vpc" {
        count                = "${var.enable == "true" ? 1 : 0}"
        cidr_block           = "${var.vpc_cidr_block}"
        enable_dns_support   = "true"
       enable_dns_hostnames = "true"
    }
    
      terraform init -var-file=dev.variables.tfvar -backend-config=dev.backend.tfvar ../../deployment/
    
      terraform apply -var-file=dev.variables.tfvar ../../deployment
    
    terraform workspace new dev
    
    resource "aws_s3_bucket" "bucket" {
      bucket = "my-tf-test-bucket-${terraform.workspace}"
    }
    
    data "aws_region" "current" {}
    
    resource "aws_s3_bucket" "bucket" {
      bucket = "my-tf-test-bucket-${data.aws_region.current.name}-${terraform.workspace}"
    }
    
    TERRAFORM_IMAGE=hashicorp/terraform:0.11.7
    TERRAFORM_CMD="docker run -ti --rm -w /app -v ${HOME}/.aws:/root/.aws -v ${HOME}/.ssh:/root/.ssh -v `pwd`:/app $TERRAFORM_IMAGE"