如何使用预安装 Terraform 提供程序构建 Docker 镜像。

huangapple go评论60阅读模式
英文:

how to build docker images with terraform providers preinstalled

问题

我正在尝试构建一个包含所有必要插件/提供程序的Docker镜像,以便在运行自动化的terraform validate时,它不需要下载冗余数据。

然而,我意识到这会带来维护问题,因为有人可能会更新插件版本,而Docker镜像中将不包含它。

问题

  1. 如何预先下载所有提供程序和插件
  2. 告诉CLI使用这些预先下载的插件
  3. 同时告诉它,如果在本地找不到所需的内容,那么可以去网络上获取

以下是相关文件:

.terraformrc

plugin_cache_dir   = "$HOME/.terraform.d/plugin-cache"
disable_checkpoint = true
provider_installation {
  filesystem_mirror {
    path    = "$HOME/.terraform/providers"
  }
  direct {
  }
}

tflint(与此问题无关,但出现在下面的Dockerfile中)

plugin "aws" {
  enabled = true
  version = "0.21.1"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}
plugin "azurerm" {
  enabled = true
  version = "0.20.0"
  source  = "github.com/terraform-linters/tflint-ruleset-azurerm"
}

Dockerfile

FROM ghcr.io/terraform-linters/tflint-bundle AS  base
LABEL name=tflint
RUN adduser -h /home/jenkins -s /bin/sh -u 1000 -D jenkins

RUN apk fix && apk --no-cache --update add git terraform openssh
ADD .terraformrc /home/jenkins/.terraformrc
RUN mkdir -p  /home/jenkins/.terraform.d/plugin-cache/registry.terraform.io

ADD .tflint.hcl /home/jenkins/.tflint.hcl
WORKDIR /home/jenkins
RUN tflint --init

FROM base AS build

ARG SSH_PRIVATE_KEY

RUN mkdir /root/.ssh && \
    echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_ed25519 &&  \
    chmod 400 /root/.ssh/id_ed25519 &&  \
    touch /root/.ssh/known_hosts &&  \
    ssh-keyscan mygitrepo >> /root/.ssh/known_hosts


RUN git clone git@mygitrepo:wrai/tools/g.git

RUN git clone git@mygitrepo:myproject/a.git && \
    git clone git@mygitrepo:myproject/b.git && \
    git clone git@mygitrepo:myproject/c.git && \
    git clone git@mygitrepo:myproject/d.git && \
    git clone git@mygitrepo:myproject/e.git && \
    git clone git@mygitrepo:myproject/f.git

RUN ls -1d */ | xargs -I {} find {} -name '*.tf' | xargs -n 1 dirname | sort -u |  \
    xargs -I {} -n 1 -P 20 terraform -chdir={} providers mirror /home/jenkins/.terraform.d

RUN chown -R jenkins:jenkins /home/jenkins
USER jenkins

FROM base AS a
COPY --from=build /home/jenkins/a/ /home/jenkins/a
RUN cd /home/jenkins/a && terraform init

FROM base AS b
COPY --from=build /home/jenkins/b/ /home/jenkins/b
RUN cd /home/jenkins/b && terraform init

FROM base AS c
COPY --from=build /home/jenkins/c/ /home/jenkins/c
RUN cd /home/jenkins/c && terraform init

FROM base AS azure_infrastructure
COPY --from=build /home/jenkins/d/ /home/jenkins/d
RUN cd /home/jenkins/d && terraform init

FROM base AS aws_infrastructure
COPY --from=build /home/jenkins/e/ /home/jenkins/e
RUN cd /home/jenkins/e && terraform init
英文:

I am trying to build a docker image that contains all of the necessary plugins/providers that several source repos need, so that when an automated terraform validate runs, it doesn't have to download gigs of redundant data.

However, I recognize that this provides for a maintenance problem in that someone may update a plugin version, and that would needed to be downloaded, since the docker image would not contain it.

The question

  1. How can I pre-download all providers and plugins
  2. Tell the CLI use those predownloaded plugins AND
  3. also tell it that, if it doesn't find what it needs locally, then it can go to the network

Below are the relevant file:
.terraformrc

plugin_cache_dir   = "$HOME/.terraform.d/plugin-cache"
disable_checkpoint = true
provider_installation {
  filesystem_mirror {
    path    = "$HOME/.terraform/providers"
  }
  direct {
  }
}

tflint (not relevant to this question, but it shows up in the below Dockerfile)

plugin "aws" {
  enabled = true
  version = "0.21.1"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}
plugin "azurerm" {
  enabled = true
  version = "0.20.0"
  source  = "github.com/terraform-linters/tflint-ruleset-azurerm"
}

Dockerfile

FROM ghcr.io/terraform-linters/tflint-bundle AS  base
LABEL name=tflint
RUN adduser -h /home/jenkins -s /bin/sh -u 1000 -D jenkins

RUN apk fix && apk --no-cache --update add git terraform openssh
ADD .terraformrc /home/jenkins/.terraformrc
RUN mkdir -p  /home/jenkins/.terraform.d/plugin-cache/registry.terraform.io

ADD .tflint.hcl /home/jenkins/.tflint.hcl
WORKDIR /home/jenkins
RUN tflint --init

FROM base AS build

ARG SSH_PRIVATE_KEY

RUN mkdir /root/.ssh && \
    echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_ed25519 &&  \
    chmod 400 /root/.ssh/id_ed25519 &&  \
    touch /root/.ssh/known_hosts &&  \
    ssh-keyscan mygitrepo >> /root/.ssh/known_hosts


RUN git clone git@mygitrepo:wrai/tools/g.git

RUN git clone git@mygitrepo:myproject/a.git && \
    git clone git@mygitrepo:myproject/b.git && \
    git clone git@mygitrepo:myproject/c.git && \
    git clone git@mygitrepo:myproject/d.git && \
    git clone git@mygitrepo:myproject/e.git && \
    git clone git@mygitrepo:myproject/f.git

RUN ls -1d */ | xargs -I {} find {} -name '*.tf' | xargs -n 1 dirname | sort -u |  \
    xargs -I {} -n 1 -P 20 terraform -chdir={} providers mirror /home/jenkins/.terraform.d

RUN chown -R jenkins:jenkins /home/jenkins
USER jenkins

FROM base AS a
COPY --from=build /home/jenkins/a/ /home/jenkins/a
RUN cd /home/jenkins/a && terraform init

FROM base AS b
COPY --from=build /home/jenkins/b/ /home/jenkins/b
RUN cd /home/jenkins/b && terraform init

FROM base AS c
COPY --from=build /home/jenkins/c/ /home/jenkins/c
RUN cd /home/jenkins/c && terraform init

FROM base AS azure_infrastructure
COPY --from=build /home/jenkins/d/ /home/jenkins/d
RUN cd /home/jenkins/d && terraform init

FROM base AS aws_infrastructure
COPY --from=build /home/jenkins/e/ /home/jenkins/e
RUN cd /home/jenkins/e && terraform init

答案1

得分: 2

  1. 插件暂存:

最简单的方式是在 CLI 中使用 插件缓存目录设置 来完成。这取代了 init 命令中旧的 -plugin-dir=PATH 参数的使用方式。你也可以在根模块配置中的每个 terraform 块中设置 文件系统镜像,但对于你的用例来说可能会很繁琐。在你的情况下,你已经在 .terraformrc 中进行了配置,但 filesystem_mirrorpathplugin_cache_dir 冲突。你需要解决这个冲突,或者可能完全移除镜像块。

  1. 使用已暂存的插件:

由于该设置被捕获在 Dockerfile 中的 CLI 配置文件中,这将自动在将来的命令中使用。

  1. 如有必要,下载额外的插件:

这是 init 命令的默认行为,因此不需要你采取进一步的操作。

附注:

jenkins 用户通常是 /sbin/nologin 作为 shell,/var/lib/jenkins 作为主目录。如果这个 Docker 镜像的目的是用于 Jenkins 构建代理,则你可能希望 jenkins 用户配置更符合标准。

英文:
  1. Staging plugins:

This is most easily accomplished with the plugin cache dir setting in the CLI. This supersedes the old usage with the -plugin-dir=PATH argument for the init command. You could also set a filesystem mirror in each terraform block within the root module config, but this would be cumbersome for your use case. In your situation, you are already configuring this in your .terraformrc, but the filesystem_mirror path conflicts with the plugin_cache_dir. You would want to resolve that conflict, or perhaps remove the mirror block entirely.

  1. Use staged plugins:

Since the setting is captured in the CLI configuration file within the Dockerfile, this would be automatically used in future commands.

  1. Download additional plugins if necessary:

This is default behavior of the init command, and therefore requires no further actions on your part.

Side note:

The jenkins user typically is /sbin/nologin for shell and /var/lib/jenkins for home directory. If the purpose of this Docker image is for a Jenkins build agent, then you may want the jenkins user configuration to be more aligned with the standard.

答案2

得分: 2

  1. 配置 Terraform 插件缓存目录
  2. 创建一个只包含 required_providers 块的单个 TF 文件的目录
  3. 从那里运行 terraform init

我在尝试弄清楚同样的问题时遇到了这个问题。

我首先尝试利用暗示的文件系统镜像,在一个只包含一个 Terraform 文件并包含 required_providers 块的目录中运行 terraform providers mirror /usr/local/share/terraform/plugins。只要您只使用你镜像的提供者版本,这个方法就可以很好地工作。
然而,如果要使用与你镜像的版本不同的提供者版本是不可能的,因为:

> Terraform 将扫描所有文件系统镜像目录,以查看哪些提供者被放置在那里,并自动排除所有那些提供者从暗示的直接块中。

我发现使用插件缓存目录是一个更好的解决方案。编辑: 您可以通过将 TF_PLUGIN_CACHE_DIR 设置为某个目录,然后在一个只声明 required_providers 的目录中运行 terraform init 来预取插件。

之前过度设计的东西如下:
terraform providers mirror 下载的提供者是打包布局

> 打包布局: HOSTNAME/NAMESPACE/TYPE/terraform-provider-TYPE_VERSION_TARGET.zip 是从提供者的原始注册表获取的分发 zip 文件。

而 Terraform 预期插件缓存目录使用未打包的布局:

> 未打包的布局: HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET 是一个包含提供者分发 zip 文件提取结果的目录。

因此,我使用 find 和 parallel 将打包布局转换为未打包布局:

find path/to/plugin-dir -name index.json -exec rm {} +`  
find path/to/plugin-dir -name '*.json' | parallel --will-cite 'mkdir -p {//}/{/.}/linux_amd64; unzip {//}/*.zip -d {//}/{/.}/linux_amd64; rm {}; rm {//}/*.zip'
英文:

TL;DR:

  1. Configure the terraform plugin cache directory
  2. Create directory with a single TF file containing required_providers block
  3. Run terraform init from there

...

I've stumbled over this question as I tried to figure out the same thing.

I first tried leveraging an implied filesystem_mirror by running terraform providers mirror /usr/local/share/terraform/plugins in a directory containing only one terraform file containing the required_providers block. This works fine as long as you only use the versions of the providers you mirrored.
However, it's not possible to use a different version of a provider than the one you mirrored, because:

> Terraform will scan all of the filesystem mirror directories to see which providers are placed there and automatically exclude all of those providers from the implied direct block.

I've found it to be a better solution to use a plugin cache directory instead. EDIT: You can prefetch the plugins by setting TF_PLUGIN_CACHE_DIR to some directory and then running terraform init in a directory that only declares the required_providers.

Previously overengineered stuff below:
The only hurdle left was that terraform providers mirror downloads the providers in the packed layout:

> Packed layout: HOSTNAME/NAMESPACE/TYPE/terraform-provider-TYPE_VERSION_TARGET.zip is the distribution zip file obtained from the provider's origin registry.

while Terraform expects the plugin cache directory to use the unpacked layout:

> Unpacked layout: HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET is a directory containing the result of extracting the provider's distribution zip file.

So I converted the packed layout to the unpacked layout with the help of find and parallel:

find path/to/plugin-dir -name index.json -exec rm {} +`  
find path/to/plugin-dir -name '*.json' | parallel --will-cite 'mkdir -p {//}/{/.}/linux_amd64; unzip {//}/*.zip -d {//}/{/.}/linux_amd64; rm {}; rm {//}/*.zip'

huangapple
  • 本文由 发表于 2023年2月8日 13:00:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75381534.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定