Terraform整合到GitLab+Jenkins工具链

整体工作流程设计

复制代码
GitLab(代码仓库) → Jenkins(CI/CD流水线) → Terraform(基础设施管理) → 云平台
  • 开发者在GitLab提交Terraform代码
  • GitLab触发Jenkins流水线
  • Jenkins执行Terraform计划、验证和部署
  • 最终在云平台创建/更新基础设施

阶段1:环境准备

1.1 安装必要工具

在Jenkins服务器上安装所需软件:

复制代码
# 安装Terraform(以Linux为例)
sudo apt update && sudo apt install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install -y terraform

# 安装Git(用于拉取代码)
sudo apt install -y git

# 安装云平台CLI(以GCP为例)
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt update && sudo apt install -y google-cloud-sdk

1.2 配置权限与凭证

1.2.1 GitLab凭证

  • 登录GitLab,进入个人设置 → Access Tokens
  • 创建令牌,权限勾选 read_repository(用于Jenkins拉取代码)
  • 保存令牌(如 gitlab-tf-token)

1.2.2 云平台凭证

以GCP为例:

  • 登录GCP控制台 → IAM与管理 → 服务账号
  • 创建服务账号(如 terraform-sa),授予权限:Editor(或最小权限集)
  • 为服务账号创建密钥(JSON格式),保存为 gcp-credentials.json

1.2.3 Jenkins凭证管理

  • 登录Jenkins → Manage Jenkins → Manage Credentials
  • 点击 (global) → Add Credentials,添加以下凭证:
    1. GitLab令牌:类型选择 Username with password,用户名任意,密码填GitLab令牌,ID设为 gitlab-credentials
    2. 云平台密钥:类型选择 Secret file,上传 gcp-credentials.json,ID设为 gcp-credentials

1.3 配置Terraform远程后端

使用GCS存储Terraform状态文件(已创建存储桶如 tf-state-bucket):

复制代码
# main.tf 中配置后端
terraform {
  backend "gcs" {
    bucket = "tf-state-bucket"  # 替换为你的存储桶名
    # prefix通过Jenkins动态传入,不在这里硬编码
  }
}

阶段2:GitLab配置

2.1 创建Terraform代码仓库

在GitLab中创建仓库(如 terraform-infra),并按以下结构提交代码:

复制代码
terraform-infra/
├── main.tf              # 主配置文件
├── variables.tf         # 变量定义
├── outputs.tf           # 输出定义

示例 main.tf 内容(创建GCP实例):

复制代码
# 此代码与 Terraform 4.25.0 及向后兼容 4.25.0 的版本兼容。# 如需了解如何验证此 Terraform 代码,请参阅 https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/google-cloud-platform-build#format-and-validate-the-configuration
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "6.8.0"
    }
  }
}
provider "google" {
  project = var.project
  credentials = file("/root/.gcp-credentials.json")
}

resource "google_compute_instance" "instance" {
  name = var.instance_name
  machine_type = var.machine_type
  tags = var.network_tags
  zone = var.zone
  boot_disk {
    auto_delete = true
    device_name = "var.instance_name"

    initialize_params {
      image = "projects/centos-cloud/global/images/centos-stream-9-v20250812"
      size  = var.disk_size
      type  = var.disk_type
    }

    mode = "READ_WRITE"
  }

  deletion_protection = true
  can_ip_forward      = false
  enable_display      = false

  labels = {
    terraform             = "001"
    year                  = "2025"
  }


  metadata = {
    enable-osconfig = "TRUE"
  }


  network_interface {
    access_config {
      network_tier = "PREMIUM"
    }

    queue_count = 0
    stack_type  = "IPV4_ONLY"
    subnetwork  = "projects/${var.project}/regions/${element(split("-", var.zone), 0)}-${element(split("-", var.zone), 1)}/subnetworks/default"
  }

  scheduling {
    automatic_restart   = var.automatic_restart
    on_host_maintenance = "MIGRATE"
    preemptible         = false
    provisioning_model  = "STANDARD"
  }

  service_account {
    email  = "your@gserviceaccount.com"
    scopes = ["https://www.googleapis.com/auth/devstorage.read_write", "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/monitoring.write", "https://www.googleapis.com/auth/service.management.readonly", "https://www.googleapis.com/auth/servicecontrol", "https://www.googleapis.com/auth/trace.append"]
  }

  shielded_instance_config {
    enable_integrity_monitoring = true
    enable_secure_boot          = false
    enable_vtpm                 = true
  }

}

module "ops_agent_policy" {
  source        = "github.com/terraform-google-modules/terraform-google-cloud-operations/modules/ops-agent-policy"
  project       = var.project
  zone          = var.zone
  assignment_id = "goog-ops-agent-${var.instance_name}-${var.zone}"
  agents_rule = {
    package_state = "installed"
    version       = "latest"
  }
  instance_filter = {
    all = false
    inclusion_labels = [{
      labels = {
        goog-ops-agent-policy = "v2-x86-template-1-4-0"
      }
    }]
  }
}
terraform {
  backend "gcs" {
    bucket = "tf-state-bucket"
    //prefix = "terraform/state/${var.project}/${var.zone}/vm/${var.instance_name}"
  }
}

示例 variables.tf 内容(创建GCP实例):

复制代码
variable "project" {
  default = "your_project_id"
}
variable "instance_name" {
  default = "terrafrom-test-jenkins001"
  description = "Name of the instance"
  type        = string
  validation {
    # 验证名称是否符合正则规则
    condition     = can(regex("^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$", var.instance_name))
    error_message = "不合法的 instance 名字, 必须匹配规则: ^[a-z]([-a-z0-9]{0,61}[a-z0-9])?."
  }
}

variable "machine_type" {
  default = "e2-medium"
}

variable "network_tags" {
  type = list(string)
  default = ["base", "office"]
}

variable "disk_size" {
  description = "Number of disk size."
  type        = number
  default = 60
  validation {
   condition     = var.disk_size >= 50
   error_message = "磁盘大小 ${var.disk_size} GB 不符合要求!磁盘大小必须大于或等于 50 GB。"
 }
}

variable "automatic_restart" {
  description = "Number of disk size."
  type        = bool
  default = true
}

variable "disk_type" {
  default = "pd-balanced"
}
variable "metadata" {
  description = "A map of metadata key-value pairs to assign to the instance"
  type        = map(string)
  default     = {}  # 默认为空映射
}

variable "zone" {
  default = "asia-east2-c"
}

示例 outputs.tf 内容(创建GCP实例):

复制代码
output "instance_name" {
  value = google_compute_instance.instance.name
  description = "The name of the instance"
}


output "机器类型" {
  value = google_compute_instance.instance.machine_type
  description = "The machine type of the instance"
}

output "公网IP" {
  value = google_compute_instance.instance.network_interface.0.access_config.0.nat_ip
  description = "The external IP address of the instance"
}
output "内网IP" {
  value = google_compute_instance.instance.network_interface.0.network_ip
  description = "The internal IP address of the instance"
}
output "网络标记" {
  value = google_compute_instance.instance.tags
  description = "The firewalld tags of the instance "
}

output "磁盘信息" {
  value = "磁盘大小: ${google_compute_instance.instance.boot_disk.0.initialize_params.0.size}, 磁盘类型: ${google_compute_instance.instance.boot_disk.0.initialize_params.0.type}"
  description = "The boot disk info of the instance "
}

2.2 配置GitLab WebHook

让GitLab代码推送触发Jenkins流水线:

  • 在GitLab仓库 → Settings → Integrations
  • URL填写:http://:8080/gitlab-webhook/(需安装Jenkins的GitLab插件)
  • 勾选触发事件:Push events 和 Merge request events
  • 点击 Add webhook 并测试连接

阶段3:Jenkins配置

3.1 安装必要插件

  • 进入Jenkins → Manage Jenkins → Plugins
  • 安装以下插件:
    • GitLab Plugin(GitLab集成)
    • Pipeline(流水线支持)
    • Credentials Binding(凭证绑定)

3.2 创建Jenkins流水线项目

  1. 新建项目 → 选择 Pipeline,名称设为 terraform-pipeline
  2. 在 Pipeline 配置中:
    • 定义方式选择 Pipeline script from SCM
    • SCM选择 Git,Repository URL填写GitLab仓库地址(如 https://gitlab.com/your-org/terraform-infra.git)
    • Credentials选择 gitlab-credentials(之前创建的)
    • 脚本路径填写 Jenkinsfile(仓库中流水线文件的路径)

3.3 编写Jenkinsfile

在GitLab仓库中添加 Jenkinsfile,定义完整流水线:

复制代码
pipeline {
    agent {
        node {
            label 'tes-agent'
        }
    }
    environment {
        GOOGLE_APPLICATION_CREDENTIALS = "/root/.gcp-credentials.json"
    }
    parameters {
        string(name: 'instance_name', defaultValue: '----------', description: '请输入实例名字 示例:terraform-instance-211')
        choice(name: 'zone', choices: ['asia-east1-b', 'asia-east2-a', 'asia-southeast1-b'], 
            description: '实例区域 台湾:asia-east1-b 新加坡:asia-southeast1-b 香港:asia-east2-a')
        string(name: 'machine_type', defaultValue: 'e2-custom-2-4096', description: '机器类型 示例:e2-custom-2-4096')
        string(name: 'disk_size', defaultValue: '50', description: '磁盘大小 示例:50')
        choice(name: 'disk_type', choices: ['pd-balanced', 'pd-ssd'], 
            description: '磁盘类型 pd-balanced:平衡性永久磁盘 pd-ssd:SSD永久性磁盘')
        text(name: 'network_tags', defaultValue: '["base", "office"]', 
            description: '防火墙网络标记,每行一个标签')
        booleanParam(name: 'AUTO_APPROVE', defaultValue: false, description: '是否自动批准部署')
    }
  • options {

    timeout(time: 30, unit: 'MINUTES')

    复制代码
         disableConcurrentBuilds()
     }
     
     stages {
         stage('Pre-Build') {
             steps {
                 // 构建开始前删除工作区
                 deleteDir()
                 echo 'Workspace deleted successfully'
             }
         }
         stage('拉取运维代码') {
             steps {
                 checkout([
                     $class: 'GitSCM',
                     branches: [[name: 'terraform-test']],  // 分支配置
                     doGenerateSubmoduleConfigurations: false,
                     extensions: [
                         cleanAfterCheckout()  // 配置 Clean after checkout
                     ],
                     submoduleCfg: [],
                     userRemoteConfigs: [[
                         url: 'git@<gitlab-ip>.com:ops/devops.git',
                         credentialsId: 'terraform-test-gitlab'
                     ]]
                 ])
             }
         }
         
         stage('Terraform初始化') {
             steps {
                 script {
                     echo "初始化Terraform并配置远程后端..."
                     sh '''
                     cd ./terraform/gcp/vm/
                     terraform init -backend-config="prefix=terraform/state/instance/${instance_name}"
                     '''
                 }
             }
         }
         
         stage('代码检查') {
             steps {
                 script {
                     echo "检查Terraform代码格式和语法..."
                     sh '''
                         # 格式检查(不符合则失败)
                         cd ./terraform/gcp/vm/
                         terraform fmt
                         if ! terraform fmt -check; then
                             echo "代码格式不符合规范,请运行terraform fmt修复"
                             exit 1
                         fi
                         
                         # 语法验证
                         terraform validate
                     '''
                 }
             }
         }
    
         stage('生成执行计划') {
             steps {
                 script {
                     echo "生成Terraform执行计划..."
                     sh '''
                         cd ./terraform/gcp/vm/
                         terraform plan \
                           -var "instance_name=${instance_name}" \
                           -var "zone=${zone}" \
                           -var "machine_type=${machine_type}" \
                           -var "disk_size=${disk_size}" \
                           -var "disk_type=${disk_type}" \
                           -var "network_tags=${network_tags}" \
                           -out=./tfplan_${zone}_${instance_name}
                         '''
                 }
             }
         }
                  stage('Approval for Deployment') {
             steps {
                 script {
                     def approvalNote = timeout(time: 5, unit: 'MINUTES') {
                         input(
                             id: 'deployApproval',
                             message: '是否批准部署到生产环境?',
                             ok: '批准',
                             parameters: [
                                 string(
                                     name: 'APPROVER_NOTE',
                                     defaultValue: '无特殊说明',
                                     description: '请输入审批备注(可选)'
                                 )
                             ]
                         )
                     }
                     echo "审批通过!审批人备注:${approvalNote}"
                 }
             }
          }
         stage('执行部署') {
             steps {
                 script {
                     echo "执行Terraform部署..."
                     sh '''
                     cd ./terraform/gcp/vm/
                     terraform apply "tfplan_${zone}_${instance_name}"
                     '''
                 }
             }
         }
     }
     
     post {
         always {
             echo 'Pipeline 完成'
         }
         success {
             echo "成功在${zone}区域创建实例${instance_name}"
             // 可以添加通知逻辑
         }
         failure {
             echo "在${zone}区域创建实例${instance_name}失败"
             // 可以添加失败通知逻辑
         }
     }

    }

阶段4:测试与验证

  1. 触发流水线:
    • 方式1:向GitLab仓库推送代码,通过WebHook自动触发
    • 方式2:在Jenkins手动点击 Build with Parameters,选择环境后执行
  2. 验证结果:
    • 查看Jenkins控制台输出,确认各阶段执行成功

    • 登录云平台控制台,检查资源是否按预期创建

    • 查看GCS存储桶,确认状态文件已按环境路径(如 terraform/state/dev/)存储

关键最佳实践

  1. 安全控制:
    • 敏感凭证通过Jenkins安全存储,不暴露在代码中
    • 生产环境禁用 AUTO_APPROVE,必须人工确认
  2. 质量保障:加入代码格式化检查、语法验证和安全扫描
  3. 可追溯性:状态文件版本控制(GCS自动支持)+ 流水线日志归档
相关推荐
码畜也有梦想5 小时前
Maven中optional的作用
java·jenkins·maven
走上未曾设想的道路1 天前
中标麒麟7.4为gitlab添加Registry
gitlab
韩zj1 天前
docker部署Gitlab社区版,步骤以及外网访问出现502的解决方式
docker·容器·gitlab
走上未曾设想的道路1 天前
gitlab流水线与k8s集群的联通
kubernetes·gitlab
走上未曾设想的道路1 天前
中标麒麟7.4部署gitlab-runner
gitlab
橙*^O^*安2 天前
Kubernetes集群部署Jenkins指南
云原生·容器·kubernetes·jenkins·devops
刘一说2 天前
Elasticsearch启动失败?5步修复权限问题
大数据·elasticsearch·jenkins
pwj去战斗吧2 天前
k8s+jenkins+harbor构建Devops平台
kubernetes·jenkins·devops
刘一说2 天前
Elasticsearch安装启动常见问题全解析
大数据·elasticsearch·jenkins