英文:
how to build docker images with terraform providers preinstalled
问题
我正在尝试构建一个包含所有必要插件/提供程序的Docker镜像,以便在运行自动化的terraform validate
时,它不需要下载冗余数据。
然而,我意识到这会带来维护问题,因为有人可能会更新插件版本,而Docker镜像中将不包含它。
问题
- 如何预先下载所有提供程序和插件
- 告诉CLI使用这些预先下载的插件
- 同时告诉它,如果在本地找不到所需的内容,那么可以去网络上获取
以下是相关文件:
.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
- How can I pre-download all providers and plugins
- Tell the CLI use those predownloaded plugins AND
- 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
- 插件暂存:
最简单的方式是在 CLI 中使用 插件缓存目录设置 来完成。这取代了 init
命令中旧的 -plugin-dir=PATH
参数的使用方式。你也可以在根模块配置中的每个 terraform
块中设置 文件系统镜像,但对于你的用例来说可能会很繁琐。在你的情况下,你已经在 .terraformrc
中进行了配置,但 filesystem_mirror
的 path
与 plugin_cache_dir
冲突。你需要解决这个冲突,或者可能完全移除镜像块。
- 使用已暂存的插件:
由于该设置被捕获在 Dockerfile
中的 CLI 配置文件中,这将自动在将来的命令中使用。
- 如有必要,下载额外的插件:
这是 init
命令的默认行为,因此不需要你采取进一步的操作。
附注:
jenkins
用户通常是 /sbin/nologin
作为 shell,/var/lib/jenkins
作为主目录。如果这个 Docker 镜像的目的是用于 Jenkins 构建代理,则你可能希望 jenkins
用户配置更符合标准。
英文:
- 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.
- Use staged plugins:
Since the setting is captured in the CLI configuration file within the Dockerfile
, this would be automatically used in future commands.
- 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
- 配置 Terraform 插件缓存目录
- 创建一个只包含
required_providers
块的单个 TF 文件的目录 - 从那里运行
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:
- Configure the terraform plugin cache directory
- Create directory with a single TF file containing
required_providers
block - 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'
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论