基础设施自动化流水线

更新时间:

方案概述

本文提供一种基础架构代码配置CI/CD的解决方案,使用Terraform作为IaC语言,并整合JenkinsGit实现Terraform代码的持续集成和版本控制。结合阿里云OSSOTS产品,对Terraform的状态文件(state文件)进行远端存储和加锁,实现多人协作。同时,集成第三方开源插件Terraform-compliance,实现对Terraform代码的合规审计操作,减小创建不安全资源带来的安全风险。

方案优势

基础设施代码化

基于IaC思想,将基础设施代码使用Terraform规范化,流水线运行使用版本化管理的Terraform代码,代码可扩展并减轻运维管理负担。

自动化集成及合规检测

代码提交后可自动运行流水线,流水线集成合规规则进行检测,保障Terraform合规。

客户场景

自动化管理基础设施配置变更

场景描述

基础设施即代码,所有对基础设施的改动都可通过代码实现。代码每次变更都会触发pipeline的部署。这既减小了手动部署的工作,也确保在不同运行环境下基础架构保证统一。

适用客户

希望使用Terraform自动化管理基础设施的企业客户。

方案架构

本方案主要目的是指导企业客户搭建一套管理Terraform代码变更的流水线。使用Git仓库管理Terraform代码,使用Jenkins进行CI/CD集成,使用Terraform-compliance插件进行合规审计,使用OSS + OTS作为Remote Backend实现多人协作及State文件安全。

产品费用及名词

产品费用

产品名称

产品说明

产品费用

资源目录RD

资源目录RD(Resource Directory)是阿里云面向企业客户提供的一套多级账号和资源关系管理服务。

免费,详情参见产品定价

访问控制RAM

访问控制使您能够安全地集中管理对阿里云服务和资源的访问。您可以使用RAM创建和管理用户和组,并使用各种权限来运行或访问他们对云资源的访问。

免费。

容器镜像服务ACR

容器镜像服务ACR提供安全地镜像托管能力,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。

企业版收费,详情见产品计费

云服务器ECS

云服务器ECS(Elastic Compute Service)是阿里云提供的性能卓越、稳定可靠、弹性扩展的IaaS(Infrastructure as a Service)级别云计算服务

收费,详情参见产品计费

对象存储OSS

对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存储服务。

收费,详情参见产品计费

表格存储OTS

表格存储(Tablestore)是阿里云自研的多模型结构化数据存储,提供海量结构化数据存储以及快速的查询和分析服务。

收费,详情参见产品定价

Jenkins

Jenkins是应用广泛的开源自动化CI/CD产品。

免费。

Docker

Docker容器化技术,用于支持创建和使用容器,实现对容器的高效创建、部署及复制,并能将其从一个环境顺利迁移至另一个环境,从而有助于针对云优化应用。

高级版收费,详情参见Pricing

名词解释

名称

说明

RAM用户

RAM用户是RAM的一种实体身份类型,有确定的身份ID和身份凭证,它通常与某个确定的人或应用程序一一对应。因安全原因不推荐直接使用云账号的AK/SK。因此在账号下创建一个RAM用户,授予RAM用户最小权限,使用该RAM用户的AK/SK。

State文件

Terraform中用于描述当前资源配置的文件,详情参见State。本文使用OSS + OTS作为State文件的远端存储,详情参见OSS Backend

安全性

Jenkins安全性

  • Jenkins需要进行安全加固,安全性详情参考官方文档Securing Jenkins

  • 当使用Jenkins并拥有大量用户及并发任务等情况时参考官方文档Scaling Jenkins

Terraform State文件安全

使用OSS作为Backend保存Terraform State文件,使用OTS支持State文件加锁及一致性检查,详情参见OSS Backend

Docker安全性

Docker安全性详情参见官方文档Security

AccessKey使用

资源操作的云账号下RAM用户AK至少需要拥有AliyunOSSFullAccessAliyunOTSFullAccess权限,用于读写账号下的OSSOTS实例,并根据实际情况添加要操作的云资源的权限。

注意事项

ECS使用限制

ECS存在一些产品功能和服务性能等限制,详情参见使用限制

OSS使用限制

OSS使用限制及性能指标详情参见使用限制

OTS使用限制

OTS使用限制详情参见使用限制

实施步骤

实施准备

  • 账号下已有RAM用户并已创建AccessKey,该RAM用户至少需要拥有AliyunOSSFullAccessAliyunOTSFullAccess,用于读写账号下的OSSOTS实例。

  • 账号已开通服务并创建一台ECS,ECS上已安装JenkinsDocker

  • 已开通OSSOTS服务,OSS服务用于存储Terraformstate文件,OTS服务用于对state文件加锁,保证多人协作。

  • 通过ACR能够拉取到Docker镜像。本文中使用的Docker镜像已打包存储在阿里云镜像服务ACR之中,可通过 registry.cn-hangzhou.aliyuncs.com/terraform/terraform-docker-hub:v1 路径拉取,该镜像的Dockerfile如下所示,用户可根据自身需要修改并重新打包使用。

    FROM alpine:3.14
    
    ENV TERRAFORM_PLUGIN_DIR=/srv/terraform-plugins
    ENV TF_PLUGIN_CACHE_DIR=${TERRAFORM_PLUGIN_DIR}
    ENV TERRAFORM_VERSION=1.0.2
    ENV TERRAFORM_PROVIDER_ALICLOUD_VERSION=1.127.0
    ENV TERRAFORM_PROVIDER_LOCAL_VERSION=2.1.0
    ENV TERRAFORM_PROVIDER_EXTERNAL_VERSION=2.1.0
    ENV PATH="/opt/venv/bin:$PATH"
    
    RUN apk add libxml2-dev libxslt-dev python3-dev gcc build-base wget unzip git bash \
        && python3 -m venv /opt/venv \
        && pip install terraform-compliance \
        && pip install checkov \
    
        && wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \
        && unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/local/bin/ \
    
        && mkdir -p ${TERRAFORM_PLUGIN_DIR}/registry.terraform.io/aliyun/alicloud/${TERRAFORM_PROVIDER_ALICLOUD_VERSION}/linux_amd64 \
        && wget https://releases.hashicorp.com/terraform-provider-alicloud/${TERRAFORM_PROVIDER_ALICLOUD_VERSION}/terraform-provider-alicloud_${TERRAFORM_PROVIDER_ALICLOUD_VERSION}_linux_amd64.zip \
        && unzip terraform-provider-alicloud_${TERRAFORM_PROVIDER_ALICLOUD_VERSION}_linux_amd64.zip -d ${TERRAFORM_PLUGIN_DIR}/registry.terraform.io/aliyun/alicloud/${TERRAFORM_PROVIDER_ALICLOUD_VERSION}/linux_amd64 \
    
        && mkdir -p ${TERRAFORM_PLUGIN_DIR}/registry.terraform.io/hashicorp/local/${TERRAFORM_PROVIDER_LOCAL_VERSION}/linux_amd64 \
        && wget https://releases.hashicorp.com/terraform-provider-local/${TERRAFORM_PROVIDER_LOCAL_VERSION}/terraform-provider-local_${TERRAFORM_PROVIDER_LOCAL_VERSION}_linux_amd64.zip \
        && unzip terraform-provider-local_${TERRAFORM_PROVIDER_LOCAL_VERSION}_linux_amd64.zip -d ${TERRAFORM_PLUGIN_DIR}/registry.terraform.io/hashicorp/local/${TERRAFORM_PROVIDER_LOCAL_VERSION}/linux_amd64 \
    
        && mkdir -p ${TERRAFORM_PLUGIN_DIR}/registry.terraform.io/hashicorp/external/${TERRAFORM_PROVIDER_EXTERNAL_VERSION}/linux_amd64 \
        && wget https://releases.hashicorp.com/terraform-provider-external/${TERRAFORM_PROVIDER_EXTERNAL_VERSION}/terraform-provider-external_${TERRAFORM_PROVIDER_EXTERNAL_VERSION}_linux_amd64.zip \
        && unzip terraform-provider-external_${TERRAFORM_PROVIDER_EXTERNAL_VERSION}_linux_amd64.zip -d ${TERRAFORM_PLUGIN_DIR}/registry.terraform.io/hashicorp/external/${TERRAFORM_PROVIDER_EXTERNAL_VERSION}/linux_amd64 \
        
        && rm -rf *.zip
  • Jenkins上已安装如下插件:

  • Git:让GitJenkins结合

  • Pipeline:Jenkins核心功能,Jenkins根据Jenkinsfile自动执行编译打包等操作

  • Docker:让JenkinsDocker结合

  • Docker Pipeline:在Pipeline中使用Docker容器

  • Blue Ocean:提供更加友好的Jenkins UI界面,代码更好的用户体验

  • 已拥有Git仓库,并在Jenkins中添加git仓库的访问凭证。

操作步骤

配置Terraform远端存储

  1. 登录阿里云OSS控制台,在对应的地域下创建Bucket。建议开启版本控制功能,便于state文件的恢复。记录下Bucket的名称和endpoint。

  2. 登录阿里云OTS控制台,在对应地域下创建表格存储实例,可根据自身需要选择预留模式或者按量模式。

  3. 创建完成后进入OTS实例,在实例中创建数据表。注意:数据表中必须含有一个名为LockID,字符串类型的主键。

编写Terraform代码

  1. 编写Terraform backend文件,将上述记录的参数填写到backend.tf文件中。其中:

    • access_keysecret_key为存储OSS的账号子用户AK;

    • key为将要保存的state文件名称,对于不同state配置而言,key值必须不同,否则会出现配置覆盖的情况;

    • tablestore_endpointtablestore_table为步骤3中创建的数据表实例的入口。

    • 详细参数说明可参考文档

      terraform {
        backend "oss" {
          bucket = "bucket-with-terraform-state"
          prefix = "path/mystate"
          key = "terraform-state.tfstate"
          region = "cn-hangzhou"
          tablestore_endpoint = "https://xxxx.cn-hangzhou.ots.aliyuncs.com"
          tablestore_table = "statelock"
          access_key = "****************"
          secret_key = "**********************"
          endpoint = "oss-cn-hangzhou.aliyuncs.com"
        }
      }
  1. 根据业务需求,编写用户自定义的Terraform代码,并上传到前序工作中的Git仓库。本文以创建RAM角色,并为角色赋权为例。其他资源配置详情参见Alibaba Cloud Provider

    provider "alicloud" {
      access_key = "LTA****************"
      secret_key = "tQW**********************"
      region = "cn-hangzhou"
    }
    
    resource "alicloud_ram_role" "ram_role" {
      name = "tf-pipeline-test"
      description = "test terraform pipeline"
      document    = <<EOF
      {
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Principal": {
              "RAM": [
                "acs:ram::195**********:root"
              ]
            }
          }
        ],
        "Version": "1"
      }
      EOF
      force = true
    }
    
    resource "alicloud_ram_role_policy_attachment" "ram_role_attach_policies" {
      policy_name = "AliyunLogFullAccess"
      policy_type = "System"
      role_name   = alicloud_ram_role.ram_role.name
    }
    

配置合规审计规则

  1. 我们选择 terraform-compliance 作为合规审计插件,该插件已经安装在docker镜像中,之后将使用docker直接运行pipeline,因此无需提前安装。若用户希望自定义安装,可参考安装文档

  2. terraform-compliance使用Behavior-Driven Development<BDD>编写合规规则。BDD提供了很好的自我描述能力以及很强的理解性,能够让非技术人员很轻松地编写规则和阅读测试结果。例如,我们编写如下规则:“不允许给任何RAM角色赋予AdministratorAccess”。

    Feature: Test terraform-compliance
    
    Scenario: Ensure that ram_role_policy could not be AdministratorAccess
        Given I have alicloud_ram_role_policy_attachment defined
        Then it must contain policy_name
        And its value must not be AdministratorAccess
    • Feature:功能,下面可以由多个Scenario

    • Scenario:场景,下面可以由多个Steps

    • Steps:步骤使用Given、When、Then、But、And这些关键词,真正进行合规配置的字段。

    • 详细语法可参考BDD文档

  1. 规则编写完成后,可以将规则上传到Git仓库。建议Terraform代码和合规规则使用不同的Git仓库存储,分别由开发人员和审计人员管理。

配置Pipeline

  1. 登录Jenkins页面,点击“New Item”,创建流水线项目

  2. 点击上方“Pipeline”,编写Jenkinsfile流水线脚本。用户可根据自身需要选择直接在Jenkins上填写或者在项目中使用Jenkinsfile。流水线脚本如下所示:

    pipeline {
        agent {
            docker {
                image 'registry.cn-hangzhou.aliyuncs.com/terraform/terraform-docker-hub:v1'
            }
        }
        stages {
            stage('Git Checkout'){
                steps{
                    git branch: 'main', credentialsId: 'xxx-github', url: 'git@github.com:xxx/Terraform2.git'
                    dir('features') {
                        git credentialsId: 'xxx-github', url: 'git@github.com:xxx/features.git'
                    }
                }
            }
            stage('Terraform Init') {
                steps{
                    sh 'terraform init -plugin-dir=/srv/terraform-plugins'
                }
            }
            stage('Terraform validate') {
                steps{
                    sh 'terraform validate'
                }
            }
            stage('Terraform Plan') {
                steps{
                    sh 'terraform plan -out tf.out'
                }
            }
            stage('合规审查') {
                steps{
                    sh 'terraform-compliance -f features -p tf.out'
                }
            }
            stage('Terraform Apply') {
                steps{
                    sh 'terraform apply --auto-approve'
                }
            }
        }
        post {
            always {
                deleteDir()
            }
        }
    }
    • docker image使用的是阿里云容器镜像服务,镜像中已安装Terraform,aliyun Provider,terraform-compliance等插件,可直接用于运行Terraform代码。

    • 第一步:分别从Git仓库分别拉取terraform代码和合规规则,并将合规规则存放在 ./features 文件夹中。可以使用Pipeline Syntax工具生成Git语句。

    • 第二步:初始化Terraform环境,为加快初始化速度,建议将常用的插件提前下载在指定路径,插件下载详情参见下载地址。 

      注意:出于安全网络方面的考虑,本文中使用 -plugin-dir 参数指定了插件安装的地址(docker镜像中插件安装在 /srv/terraform-plugins 文件夹下),如果所需插件在该文件夹下不存在,会报“插件未找到”的错误。若允许开发人员执行任意插件且网络畅通,可以使用缓存来加速Terraform init,具体可参考文档
    • 第三步:检查Terraform语法是否正确

    • 第四步:使用Terraform plan命令,检查state文件和当前Terraform代码的配置差异,并将更改信息写入tf.out文件。

    • 第五步:使用Terraform-compliance插件进行合规审查, -f 参数指定合规审查规则的路径, -p 参数指定plan后的.out文件路径。

    • 当上述流程全部成功后,执行Terraform代码。执行完成后,最后在ECS上将该Pipeline的工作空间删除。

运行pipeline

  1. JenkIns左侧边栏点击“Open Blue Ocean”,进入Blue Ocean页面。点击“运行”按钮,运行该pipeline。运行过程中可能出现jenkins权限不足的问题,注意为jenkins用户所在的用户组授予访问docker的权限。

  2. 运行完成后,查看每一个节点的运行日志。其中“合规审查”中可以看见合规审查的结果,由于我们给RAM用户授权的并非“AdministratorAccess”,因此合规审查通过。

  3. 当我们将RAM用户的权限修改为“AdministratorAccess”时,重新将Terraform代码pushGit仓库,再次执行该流水线,会发现合规审查无法通过。

  4. 至此,我们完成一个简易的Terraform流水线,用户可根据自身需求,修改Terraform代码、合规规则以及流水线的步骤。

发送邮件

Jenkins邮件配置
  1. 点击左侧菜单栏“Manage Jenkins”-“Configure System”。填写“Jenkins Location”下的“System Admin e-mail address”。

  2. 填写“E-mail Notification”下的参数,该参数与所使用的邮箱有关。以QQ邮箱为例,配置如下图所示:

  3. 我们可以勾选“Test configuration by sending test e-mail”选项,测试能否成功发送邮件。

Pipeline新增邮件发送步骤
  1. Pipelinepost中添加以下代码,表示当Pipeline执行成功后发送邮件。

    post {
            success {
                echo 'successful,and send mail'
                mail body: 'current job ok', from: 'xxxx@qq.com', subject: 'account baseline success', to: 'xxx@gmail.com'
            }
        }
  2. 执行该流水线,成功后查看目标邮箱是否收到邮件。

故障排除

Jenkins

Jenkins相关故障排除请参见官网

Terraform

Terraform相关问题参见官方文档

相关内容