Press "Enter" to skip to content

terraform流水线应用

内容目录

前情

之前讲了terraform的安装和使用,里面操作都是直接cli执行指令,如果集成到其他平台显然不是太合适,
最好还是能有api的能力提供出去,这里就推荐使用流水线技术了,本文将以jenkins为例串联terraform的执行流程。

安装jenkins

推荐使用docker-compose方式安装
docker-compose.yml

version: '3.8'
services:
  jenkins:
    container_name: jenkins_2.474
    image: jenkins/jenkins:2.474
    ports:
      - "8080:8080" # Jenkins Web 界面端口
      - "50000:50000" # Jenkins Agent 通信端口
    volumes:
      - jenkins_home:/var/jenkins_home # Jenkins Home 目录持久化
      # === 重新添加:映射宿主机的 Docker Socket ===
      - /var/run/docker.sock:/var/run/docker.sock # 允许 Jenkins 容器内执行 Docker 命令
      - /usr/local/bin/terraform:/usr/local/bin/terraform # 映射主机上的 Terraform 可执行文件
      - /root/.terraformrc:/var/jenkins_home/.terraformrc # 映射主机上的 Terraform 配置文件到 Jenkins Home 目录
      # 映射主机上存放 Terraform 代码的目录到 Jenkins 工作区的一个子目录
      - /root/chenjiecode/terraform/learn-terraform-docker-container:/var/jenkins_home/workspace/terraform-docker/learn-terraform-docker-container_mounted

    environment:
      - JAVA_OPTS=-Xmx1024m # 示例:设置JVM内存限制

    # === 添加:将 Jenkins 容器用户添加到宿主机的 Docker 组 ===
    group_add:
      - "996" # <--- IMPORTANT: 将此处的 999 替换为您在第一步中查到的宿主机 docker 组的实际 GID

    restart: unless-stopped # 容器退出时自动重启

volumes:
  jenkins_home: # 定义用于持久化 Jenkins Home 的命名卷

启动

docker-compose up -d

terraform 配置

main.tf

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0.1"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = var.container_name

  ports {
    internal = 80
    external = 8088
  }
}

variables.tf

variable "container_name" {
  description = "Value of the name for the Docker container"
  type        = string
  default     = "ExampleNginxContainer"
}

output "container_id" {
  description = "ID of the Docker container"
  value       = docker_container.nginx.id
}

output "image_id" {
  description = "ID of the Docker image"
  value       = docker_image.nginx.id
}

terraform.tfvars

container_name = "fileTypeName"

创建流水线

选择类型“流水线”,名字“terraform-docker”

编写流水线

pipeline {
    agent any

    stages {
        stage('Initialize and Apply Terraform') {
            steps {
                script {
                    // 进入到映射的 Terraform 配置文件目录
                    dir('learn-terraform-docker-container_mounted') {
                        echo "Current directory: ${pwd()}"
                        sh 'terraform init -input=false'
                        sh 'terraform plan -out="tfplan"'
                        sh 'terraform apply -auto-approve "tfplan"'
                    }
                }
            }
        }
    }
}

然后执行流水线可以顺序创建成功,下面是输出日志

Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/terraform-docker
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Initialize and Apply Terraform)
[Pipeline] script
[Pipeline] {
[Pipeline] dir
Running in /var/jenkins_home/workspace/terraform-docker/learn-terraform-docker-container_mounted
[Pipeline] {
[Pipeline] pwd
[Pipeline] echo
Current directory: /var/jenkins_home/workspace/terraform-docker/learn-terraform-docker-container_mounted
[Pipeline] sh
+ terraform init -input=false
[0m[1mInitializing the backend...[0m
[0m[1mInitializing provider plugins...[0m
- Reusing previous version of kreuzwerker/docker from the dependency lock file
- Using previously-installed kreuzwerker/docker v3.0.2

[0m[1m[32mTerraform has been successfully initialized![0m[32m[0m
[0m[32m
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.[0m
[Pipeline] sh
+ terraform plan -out=tfplan
[0m[1mdocker_image.nginx: Refreshing state... [id=sha256:9592f5595f2b12c2ede5d2ce9ec936b33fc328225a00b3901b96019e3dd83528nginx][0m
[0m[1mdocker_container.nginx: Refreshing state... [id=d92f366b4431f9a628c657f388112f87187cc4a409b3bb63006cba7a387ef192][0m

[1m[36mNote:[0m[1m Objects have changed outside of Terraform
[0m
Terraform detected the following changes made outside of Terraform since the
last "terraform apply" which may have affected this plan:

[1m  # docker_container.nginx[0m has been deleted
[0m  [31m-[0m[0m resource "docker_container" "nginx" {
      [31m-[0m[0m id                                          = "d92f366b4431f9a628c657f388112f87187cc4a409b3bb63006cba7a387ef192" [90m-> null[0m[0m
        name                                        = "fileTypeName"
        [90m# (16 unchanged attributes hidden)[0m[0m

        [90m# (1 unchanged block hidden)[0m[0m
    }

Unless you have made equivalent changes to your configuration, or ignored the
relevant attributes using ignore_changes, the following plan may include
actions to undo or respond to these changes.
[90m
─────────────────────────────────────────────────────────────────────────────[0m

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  [32m+[0m create[0m

Terraform will perform the following actions:

[1m  # docker_container.nginx[0m will be created
[0m  [32m+[0m[0m resource "docker_container" "nginx" {
      [32m+[0m[0m attach                                      = false
      [32m+[0m[0m bridge                                      = (known after apply)
      [32m+[0m[0m command                                     = (known after apply)
      [32m+[0m[0m container_logs                              = (known after apply)
      [32m+[0m[0m container_read_refresh_timeout_milliseconds = 15000
      [32m+[0m[0m entrypoint                                  = (known after apply)
      [32m+[0m[0m env                                         = (known after apply)
      [32m+[0m[0m exit_code                                   = (known after apply)
      [32m+[0m[0m hostname                                    = (known after apply)
      [32m+[0m[0m id                                          = (known after apply)
      [32m+[0m[0m image                                       = "sha256:9592f5595f2b12c2ede5d2ce9ec936b33fc328225a00b3901b96019e3dd83528"
      [32m+[0m[0m init                                        = (known after apply)
      [32m+[0m[0m ipc_mode                                    = (known after apply)
      [32m+[0m[0m log_driver                                  = (known after apply)
      [32m+[0m[0m logs                                        = false
      [32m+[0m[0m must_run                                    = true
      [32m+[0m[0m name                                        = "fileTypeName"
      [32m+[0m[0m network_data                                = (known after apply)
      [32m+[0m[0m read_only                                   = false
      [32m+[0m[0m remove_volumes                              = true
      [32m+[0m[0m restart                                     = "no"
      [32m+[0m[0m rm                                          = false
      [32m+[0m[0m runtime                                     = (known after apply)
      [32m+[0m[0m security_opts                               = (known after apply)
      [32m+[0m[0m shm_size                                    = (known after apply)
      [32m+[0m[0m start                                       = true
      [32m+[0m[0m stdin_open                                  = false
      [32m+[0m[0m stop_signal                                 = (known after apply)
      [32m+[0m[0m stop_timeout                                = (known after apply)
      [32m+[0m[0m tty                                         = false
      [32m+[0m[0m wait                                        = false
      [32m+[0m[0m wait_timeout                                = 60

      [32m+[0m[0m healthcheck (known after apply)

      [32m+[0m[0m labels (known after apply)

      [32m+[0m[0m ports {
          [32m+[0m[0m external = 8088
          [32m+[0m[0m internal = 80
          [32m+[0m[0m ip       = "0.0.0.0"
          [32m+[0m[0m protocol = "tcp"
        }
    }

[1mPlan:[0m 1 to add, 0 to change, 0 to destroy.
[0m
Changes to Outputs:
  [32m+[0m[0m container_id = (known after apply)
[90m
─────────────────────────────────────────────────────────────────────────────[0m

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"
[Pipeline] sh
+ terraform apply -auto-approve tfplan
[0m[1mdocker_container.nginx: Creating...[0m[0m
[0m[1mdocker_container.nginx: Creation complete after 1s [id=78e8cabbea2a38fb021c85404806ff713c810f482f575259e797fd2de0423997][0m
[0m[1m[32m
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
[0m[0m[1m[32m
Outputs:

[0mcontainer_id = "78e8cabbea2a38fb021c85404806ff713c810f482f575259e797fd2de0423997"
image_id = "sha256:9592f5595f2b12c2ede5d2ce9ec936b33fc328225a00b3901b96019e3dd83528nginx"
[Pipeline] }
[Pipeline] // dir
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

修改流水线

上面流水线的参数用的文件方式进行传递

现在我们讲改为使用命令行输入方式(其优先级高于文本方式)

pipeline {
    agent any

    parameters {
        string(name: 'container_name', defaultValue: '', description: '容器名')
    }

    stages {
        stage('Initialize and Apply Terraform') {
            steps {
                script {
                    dir('/var/jenkins_home/workspace/terraform-docker/learn-terraform-docker-container_mounted') {
                        echo "Current directory: ${pwd()}"
                        echo "Jenkins parameter 'container_name' value: ${params.container_name}" // 打印参数值以确认

                        sh 'terraform init -input=false'

                        // === 关键修改:通过 -var 命令行参数传递变量 ===
                        // 注意:如果您的 container_name 包含空格或特殊字符,最好用双引号包裹变量值
                        sh "terraform plan -out=\"tfplan\" -var=\"container_name=${params.container_name}\""

                        // 执行 apply 时不需要再次传递变量,因为 plan 文件 ('tfplan')
                        // 已经包含了在 plan 阶段确定的变量值。
                        sh 'terraform apply -auto-approve "tfplan"'
                    }
                }
            }
        }
    }
}

按要求输入container_name变量完成构建

也可以使用http方式提供构建能力,

需要

  1. 设置用户token http://ip:8080/user/admin/security
  2. 设置流水线任务开启远程调用并设置token

最后请求形如:

curl -i -X POST \
     -u admin:adminToken \
     -d "token=jobToken" \
     -d "container_name=my-dynamic-container" \
     "http://ip:8080/job/terraform-docker/buildWithParameters"
发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注