使用预安装的 Terraform 插件,而不是使用 terraform init 下载它们

Posted

技术标签:

【中文标题】使用预安装的 Terraform 插件,而不是使用 terraform init 下载它们【英文标题】:Use pre-installed Terraform plugins instead of downloading them with terraform init 【发布时间】:2018-11-29 08:40:22 【问题描述】:

在运行 terraform init 时使用 Terraform 0.11.3 我们收到以下错误:

正在初始化提供程序插件... - 检查https://releases.hashicorp.com上的可用提供程序插件...

安装提供程序“模板”时出错:获取 https://releases.hashicorp.com/terraform-provider-template/:读取tcp 172.25.77.25:53742->151.101.13.183:443:读取:对等方重置连接。

Terraform 自动分析配置和状态 下载使用的提供程序的插件。然而,当试图 下载此插件时发生意外错误。

这可能是由于某种原因 Terraform 无法到达 插件库。如果可以访问,则存储库可能无法访问 被防火墙阻止。

如果在您的系统中无法或不希望自动安装 环境,您也可以手动安装插件 下载合适的分发包并放置插件的 以下目录中的可执行文件: terraform.d/plugins/linux_amd64

我意识到这是因为 https://releases.hashicorp.com 域的连接问题。由于一些明显的原因,我们将不得不调整这个连接问题,因为在控制服务器和 Hashicorp 的服务器之间存在一些 SSL 和防火墙问题。

有什么方法可以绕过这个问题,从 Hashicorp 的服务器下载插件并将它们复制到控制服务器上?或者任何其他替代方法来避免尝试从 Hashicorp 的服务器下载内容?

【问题讨论】:

访问此链接以获取 Windows 10、企业防火墙:***.com/a/67178151/12981044 请查看我的新答案***.com/a/70779192/869951,这是推荐的做法(甚至 terraform-b​​undle,用作接受答案的基础,也表示要按照我在答案中描述的方式进行操作)。 【参考方案1】:

您可以通过将插件放在与terraform 二进制文件相同的目录中或设置-plugin-dir flag 来使用预安装的插件。

还可以使用terraform-bundle tool 自动构建您需要的每个提供程序的捆绑包。

我在 Docker 容器中的 CI 管道中运行 Terraform,所以有一个看起来像这样的 Dockerfile:

FROM golang:alpine AS terraform-bundler-build

RUN apk --no-cache add git unzip && \
    go get -d -v github.com/hashicorp/terraform && \
    go install ./src/github.com/hashicorp/terraform/tools/terraform-bundle

COPY terraform-bundle.hcl .

RUN terraform-bundle package terraform-bundle.hcl && \
    mkdir -p terraform-bundle && \
    unzip -d terraform-bundle terraform_*.zip

####################

FROM python:alpine

RUN apk add --no-cache git make && \
    pip install awscli

COPY --from=terraform-bundler-build /go/terraform-bundle/* /usr/local/bin/

请注意,完成的容器映像还添加了 gitmake 和 AWS CLI,因为我还需要在使用此容器的 CI 作业中使用这些工具。

terraform-bundle.hcl 然后看起来像这样(取自terraform-bundle README):

terraform 
  # Version of Terraform to include in the bundle. An exact version number
  # is required.
  version = "0.10.0"


# Define which provider plugins are to be included
providers 
  # Include the newest "aws" provider version in the 1.0 series.
  aws = ["~> 1.0"]

  # Include both the newest 1.0 and 2.0 versions of the "google" provider.
  # Each item in these lists allows a distinct version to be added. If the
  # two expressions match different versions then _both_ are included in
  # the bundle archive.
  google = ["~> 1.0", "~> 2.0"]

  # Include a custom plugin to the bundle. Will search for the plugin in the 
  # plugins directory, and package it with the bundle archive. Plugin must have
  # a name of the form: terraform-provider-*, and must be build with the operating
  # system and architecture that terraform enterprise is running, e.g. linux and amd64
  customplugin = ["0.1"]

【讨论】:

好吧,比如releases.hashicorp.com/terraform-provider-template有多个模板,我应该从上面的url下载所有的插件吗?还是更高版本可以?如果我一个一个下载,我觉得太重了。 你应该使用你需要的版本。如果由于某种原因您不需要固定到特定版本,则应仅使用最新版本。我建议在 Terraform 代码中定义您需要的提供程序版本(请参阅terraform.io/docs/configuration/…)并使用悲观版本约束运算符(~>)将其限制为单个主要版本,并避免 API 中断更改被自动拉入/使用. 当他们推出 0.12 时,这在 master 上被打破了。您需要固定到 0.11.9,直到 0.12 完全发布。请参阅github.com/hashicorp/terraform/issues/19173 了解更多信息 详细文档可在 Terraform 的工作原理:发现中找到。见terraform.io/docs/extend/how-terraform-works.html#discovery 嘿@ydaetskcoR,我添加了新答案,因为我们从 0.13.2 开始具有网络镜像功能。我会要求你审查一次:)【参考方案2】:

.terraformrc中配置plugin_cache_dir

plugin_cache_dir   = "$HOME/.terraform.d/plugin-cache"

然后将预安装的提供程序移动到 plugin_cache_dir,

terraform 将不再下载提供程序

顺便说一句,使用 ~/.terraform.d/plugin 目录不起作用

/.terraform.d/plugin/linux_amd64$ terraform -v
Terraform v0.12.15

【讨论】:

你能分享完整的 dockerfile。我遇到了 Terraform 0.13.7 的问题【参考方案3】:

Terraform 0.13.2 版本开始,可以通过网络镜像协议从本地 webserver/http 服务器下载插件。

更多详情,请查看this link

它期望在 $HOME 路径中有一个 .terraformrc 文件,指向插件的提供者路径,如下所示。如果文件在不同的目录中,您可以提供TERRAFORM_CONFIG env var 的路径。

provider_installation 
  network_mirror 
    url    = "https://terraform-plugins.example.net/providers/"
  

然后,您在自定义 tf 中定义提供程序,如下所示。

providers.tf::

terraform 
  required_providers 
    azurerm = 
      source  = "registry.terraform.io/example/azurerm"
    
    openstack = 
      source  = "registry.terraform.io/example/openstack"
    
    null = 
      source  = "registry.terraform.io/example/null"
    
    random = 
      source  = "registry.terraform.io/example/random"
    
    local = 
      source  = "registry.terraform.io/example/local"
    
  

但是,您必须上传 .zip 格式的插件文件以及 index.json<version>.json 文件,以便 terraform 发现要下载的插件版本。

包含插件版本的示例 index.json::


  "versions": 
    "2.3.0": 
  

同样,2.3.0.json 包含插件文件的哈希值。在这种情况下,它是<version>.json


  "archives": 
    "linux_amd64": 
      "hashes": [
        "h1:nFL6uiwsQFLiP8QCr35sPfWe9LpXI3/c7gP9tYnih+k="
      ],
      "url": "terraform-provider-random_2.3.0_linux_amd64.zip"
    
  

如何获取index.json<version>.json 文件的详细信息?

通过在包含 tf 文件的目录上运行 terraform providers。注意,运行此命令的机器需要连接到公共 terraform 注册表。 Terraform 将下载这些文件的信息。如果您有不同的 terraform 配置文件,那么自动化这些步骤是有意义的,否则您可以手动执行 :)

terraform init 之后,terraform 从上面的 Web 服务器而不是 terraform 注册表下载插件。确保您不要plugin-dir 参数与 terraform init 一起使用,因为它会覆盖您所做的所有更改。

【讨论】:

这是正确的方向,但有点误导,至少在 tf 1.0 中,您不需要创建您提到的所有这些文件。【参考方案4】:

为@ydaetskcoR 的解决方案更新了Dockerfile,因为目前terraform-bundle 不适用于0.12.x(问题已在0.12.2 修复,但出现在0.12.18)

FROM hashicorp/terraform:0.12.18 as terraform-provider

COPY provider.tf .

RUN terraform init && \
    mv .terraform/plugins/linux_amd64/terraform-provider* /bin/ 

FROM hashicorp/terraform:0.12.18
# Install terraform pre-installed plugins
COPY --from=terraform-provider /bin/terraform-provider* /bin/

这里是provider.tf的内容

provider "template"  version = "~>2.1.2" 
provider "aws"  version = "~>2.15.0" 
...

【讨论】:

很好的解决方案,但 mv 步骤正在下降。我正在使用 terraform 0.13.7 如果可能的话你能分享更新的 dockerfile【参考方案5】:

这花了我一段时间,遇到了同样的问题。我最终不得不从源代码下载并使用它吐出的图像。它很讨厌,但它可以满足我与 Google 提供商合作的需要。

FROM golang:alpine AS terraform-bundler-build

ENV TERRAFORM_VERSION=0.12.20
ENV GOOGLE_PROVIDER=3.5.0

RUN apk add --update --no-cache git make tree bash curl
ENV GOPATH=/go
RUN mkdir -p $GOPATH/src/github.com/terraform-providers

RUN cd $GOPATH/src/github.com/terraform-providers && curl -sLO https://github.com/terraform-providers/terraform-provider-google-beta/archive/v$GOOGLE_PROVIDER.tar.gz
RUN cd $GOPATH/src/github.com/terraform-providers && tar xvzf v$GOOGLE_PROVIDER.tar.gz && mv terraform-provider-google-beta-$GOOGLE_PROVIDER terraform-provider-google-beta
RUN cd $GOPATH/src/github.com/terraform-providers/terraform-provider-google-beta && pwd &&  make build

RUN cd $GOPATH/src/github.com/terraform-providers && curl -sLO https://github.com/terraform-providers/terraform-provider-google/archive/v$GOOGLE_PROVIDER.tar.gz
RUN cd $GOPATH/src/github.com/terraform-providers && tar xvzf v$GOOGLE_PROVIDER.tar.gz && mv terraform-provider-google-$GOOGLE_PROVIDER terraform-provider-google
RUN cd $GOPATH/src/github.com/terraform-providers/terraform-provider-google && pwd &&  make build

RUN mkdir -p $GOPATH/src/github.com/hashicorp
RUN cd $GOPATH/src/github.com/hashicorp && curl -sLO https://github.com/hashicorp/terraform/archive/v$TERRAFORM_VERSION.tar.gz
RUN cd $GOPATH/src/github.com/hashicorp && tar xvzf v$TERRAFORM_VERSION.tar.gz && mv terraform-$TERRAFORM_VERSION terraform
RUN cd $GOPATH/src/github.com/hashicorp/terraform && go install ./tools/terraform-bundle
ENV TF_DEV=false
ENV TF_RELEASE=true
COPY my-build.sh $GOPATH/src/github.com/hashicorp/terraform/scripts/
RUN cd $GOPATH/src/github.com/hashicorp/terraform && /bin/bash scripts/my-build.sh
ENV HOME=/root
COPY terraformrc $HOME/.terraformrc
RUN mkdir -p $HOME/.terraform.d/plugin-cache

########################################
FROM alpine:3
ENV HOME=/root

RUN ["/bin/sh", "-c", "apk add --update --no-cache bash ca-certificates curl git jq openssh"]

RUN ["bin/sh", "-c", "mkdir -p /src"]
COPY --from=terraform-bundler-build /go/bin/terraform* /bin/
RUN mkdir -p /root/.terraform.d/plugins/linux_amd64
COPY --from=terraform-bundler-build /root/.terraform.d/ $HOME/.terraform.d/
RUN cp /bin/terraform-provider-google $HOME/.terraform.d/plugin-cache/linux_amd64
RUN cp /bin/terraform-provider-google-beta $HOME/.terraform.d/plugin-cache/linux_amd64

COPY terraformrc $HOME/.terraformrc

COPY provider.tf $HOME/
COPY backend.tf $HOME/

# For Testing (This should be echoed or taken care of in the CI pipeline)
#COPY google.json $HOME/.google.json
WORKDIR $HOME
ENTRYPOINT ["/bin/bash"]

.terraformrc:

plugin_cache_dir   = "$HOME/.terraform.d/plugins/linux_amd64"
disable_checkpoint = true

provider.tf

# Define which provider plugins are to be included
provider "google" 
  credentials = ".google.json"


provider "google-beta" 
  credentials = ".google.json"

my-build.sh

#!/usr/bin/env bash
#
# This script builds the application from source for multiple platforms.

# Get the parent directory of where this script is.
SOURCE="$BASH_SOURCE[0]"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"

# Change into that directory
cd "$DIR"
echo "DIR=$DIR"

# Get the git commit
GIT_COMMIT=$(git rev-parse HEAD)
GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)

# Determine the arch/os combos we're building for
XC_ARCH=$XC_ARCH:-"amd64 arm"
XC_OS=$XC_OS:-linux
XC_EXCLUDE_OSARCH="!darwin/arm !darwin/386"

mkdir -p bin/

# If its dev mode, only build for ourself
if [[ -n "$TF_DEV" ]]; then
    XC_OS=$(go env GOOS)
    XC_ARCH=$(go env GOARCH)

    # Allow LD_FLAGS to be appended during development compilations
    LD_FLAGS="-X main.GitCommit=$GIT_COMMIT$GIT_DIRTY $LD_FLAGS"
fi

if ! which gox > /dev/null; then
    echo "==> Installing gox..."
    go get -u github.com/mitchellh/gox
fi

# Instruct gox to build statically linked binaries
export CGO_ENABLED=0

# In release mode we don't want debug information in the binary
if [[ -n "$TF_RELEASE" ]]; then
    LD_FLAGS="-s -w"
fi

# Ensure all remote modules are downloaded and cached before build so that
# the concurrent builds launched by gox won't race to redundantly download them.
go mod download

# Build!
echo "==> Building..."
gox \
    -os="$XC_OS" \
    -arch="$XC_ARCH" \
    -osarch="$XC_EXCLUDE_OSARCH" \
    -ldflags "$LD_FLAGS" \
    -output "pkg/.OS_.Arch/$PWD##*/" \
    .


## Move all the compiled things to the $GOPATH/bin
GOPATH=$GOPATH:-$(go env GOPATH)
case $(uname) in
    CYGWIN*)
        GOPATH="$(cygpath $GOPATH)"
        ;;
esac
OLDIFS=$IFS
IFS=: MAIN_GOPATH=($GOPATH)
IFS=$OLDIFS
#
# Create GOPATH/bin if it's doesn't exists
if [ ! -d $MAIN_GOPATH/bin ]; then
    echo "==> Creating GOPATH/bin directory..."
    mkdir -p $MAIN_GOPATH/bin
fi

# Copy our OS/Arch to the bin/ directory
DEV_PLATFORM="./pkg/$(go env GOOS)_$(go env GOARCH)"
if [[ -d "$DEV_PLATFORM" ]]; then
    for F in $(find $DEV_PLATFORM -mindepth 1 -maxdepth 1 -type f); do
        cp $F bin/
        cp $F $MAIN_GOPATH/bin/
        ls -alrt $MAIN_GOPATH/bin/
        echo "MAIN_GOPATH=$MAIN_GOPATH"
    done
fi

bucket.tf

terraform 
  backend "gcs" 
    bucket = "my-terraform-bucket"
    prefix = "terraform/state"
    credentials = ".google.json"
  
  required_version = "v0.12.20"

【讨论】:

【参考方案6】:

您可以通过设置“plugins-dir”标志将插件二进制文件放在可以使用 Terraform 二进制文件的同一目录中来使用预安装的插件。

默认情况下,所有插件都下载在 .terraform 文件夹中。例如,Null 资源插件将在以下位置可用

.terraform\providers\registry.terraform.io\hashicorp\null\3.0.0.\windows_amd64.

在 Terraform 目录中创建类似“terraform-plugins”的新文件夹,并将包括上述示例中提到的 registry.terraform.io 文件夹在内的所有内容复制到创建的文件夹中。

现在运行带有 plugins-dir 标志的 terraform init 命令

terraform init -plugin-dir="/terraform-plugins"

用 plugin-dir 标志指定完整的目录路径

【讨论】:

【参考方案7】:

自 terraform 0.14 以来处理此问题的正确方法,正如在当前接受的答案中提到的 terraform-b​​undle 页面上所讨论的那样,是使用 terraform providers mirror,如 https://www.terraform.io/cli/commands/providers/mirror 中所述。此命令创建所有必要的索引文件等,因此该文件夹可用于插件。例如:

$ cd your-tf-root-module
$ terraform providers mirror path/to/tf-plugins
...
$ terraform init --plugin-dir path/to/tf-plugins
...

您可以 cd 到每个根模块(即具有 terraform 状态的模块)并运行镜像命令;那里可能安装了多个版本的插件,这没关系。当您运行 terraform init 命令时,它将获取正确的命令。与没有 --plugin-dir 参数相同。

所以唯一的区别是不使用互联网来获取插件,terraform init 从插件文件夹中获取它们。

这对于创建缓存也非常有用,然后可以在 ci/cd 中由 terraform 使用。例如,在 circleci 中,您将有一项手动工作,该工作调用镜像并执行保存缓存;并且您的自动 terraform 初始化作业将恢复缓存,并使用 --plugin-dir arg;那么自动 terraform apply 作业将照常运行。

【讨论】:

以上是关于使用预安装的 Terraform 插件,而不是使用 terraform init 下载它们的主要内容,如果未能解决你的问题,请参考以下文章

Terraform:如何安装多个版本的提供程序插件? [复制]

Terraform 使用本地提供程序/插件

使用 Helm 而不是 Terraform 的困惑

在使用 github 操作进行构建期间执行诗歌安装时,使用预编译的 numpy 包而不是构建它

Terraform GCP 启动脚本本地文件而不是内联文件

Play商店中的Android应用状态显示预注册而不是安装[关闭]