如何使用 Kubernetes yaml 文件设置动态值
Posted
技术标签:
【中文标题】如何使用 Kubernetes yaml 文件设置动态值【英文标题】:How to set dynamic values with Kubernetes yaml file 【发布时间】:2018-06-26 00:53:46 【问题描述】:例如一个部署yaml文件:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: Here want to read value from config file outside
Kubernetes 有一个 ConfigMap
功能,但这也是将键/值写入 yaml 文件。有没有办法设置环境变量的键?
【问题讨论】:
另见: ***.com/questions/41620674/use-placeholders-in-yaml 【参考方案1】:部署时也可以使用envsubst
。
例如
cat $app/deployment.yaml | envsubst | kubectl apply ...
它将用它们的值替换文件中的所有变量。 在部署到多个环境时,我们在 CI 上成功地使用了这种方法,还将 CI_TAG 等注入到部署中。
【讨论】:
没有猫也可以:envsubst < deployment.yaml | kubectl apply -f -
您有envsubset
和deployment
的示例,以便我们查看变量声明和锚点的语法吗?
您只需将 $YOUR_ENV_NAME 放入文件中即可。
我写了一个快速的帖子来补充这一点。谢谢。雅 - 在线我看到各种语法 $FOO $FOO FOO 等..所以不清楚。 envsubst
【参考方案2】:
您不能自动执行此操作,您需要使用外部脚本来“编译”您的模板,或者按照@Jakub 的建议使用 helm。
您可能想要使用自定义 bash 脚本,可能与您的 CI 管道集成。
给定一个名为 deploy.yml.template
的模板 yml 文件与您提供的非常相似,您可以使用以下内容:
#!/bin/bash
# sample value for your variables
MYVARVALUE="nginx:latest"
# read the yml template from a file and substitute the string
# MYVARNAME with the value of the MYVARVALUE variable
template=`cat "deploy.yml.template" | sed "s/MYVARNAME/$MYVARVALUE/g"`
# apply the yml with the substituted value
echo "$template" | kubectl apply -f -
【讨论】:
感谢您的明确答复!我已阅读helm
文件。这是一个很棒的工具。您的脚本是使用 CI 部署应用程序的正确方法。
这是我为 Kubernetes 作业清单模板化的解决方案。谢谢!【参考方案3】:
我认为不可能通过 Kubernetes 中的变量或 Config Map 设置图像。但是您可以使用例如Helm 来使您的部署更加灵活和可配置。
【讨论】:
【参考方案4】:一行:
cat app-deployment.yaml | sed "s/BITBUCKET_COMMIT/$BITBUCKET_COMMIT/g" | kubectl apply -f -
在 yaml 中:
...
containers:
- name: ulisses
image: niceuser/niceimage:BITBUCKET_COMMIT
...
【讨论】:
或使用默认值 -cat app-deployment.yaml | sed "s/BITBUCKET_COMMIT/$BITBUCKET_COMMIT:=1/g" | kubectl apply -f -
你们都值得Useless Use of Cat Award! sed
可以自己完美地读取文件,所有剩余的参数都被解析为输入文件路径。
非常感谢,@CelinHC 这正是我想要的。
BITBUCKET_COMMIT 是环境变量吗?实际设置的值在哪里以便可以替换?【参考方案5】:
使用ytt
,这种事情非常容易:
deployment.yml
#@ load("@ytt:data", "data")
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: #@ data.values.image
values.yml
#@data/values
image: nginx@sha256:fe2fa7bb1ceb86c6d9c935bc25c3dd8cbd64f2e95ed5b894f93ae7ffbd1e92bb
那么……
$ ytt -f deployment.yml -f values.yml | kubectl apply -f -
或者更好的是,使用ytt
的表弟kapp
获得高度控制的部署体验:
$ ytt -f deployment.yml -f values.yml | kapp deploy -a guestbook -f -
【讨论】:
是 Linux 上的ytt
标准还是我们需要在部署之前安装的东西?
你安装它:carvel.dev/ytt/docs/latest/install【参考方案6】:
我创建了一个名为kubectl_create
的脚本并使用它来运行创建命令。它将替换模板中在环境变量中引用的任何值。
#!/bin/bash
set -e
eval "cat <<EOF
$(<$1)
EOF
" | kubectl create -f -
例如,如果模板文件有:
apiVersion: v1
kind: Service
metadata:
name: nginx-external
labels:
app: nginx
spec:
loadBalancerIP: $PUBLIC_IP
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
selector:
app: nginx
运行kubectl_create nginx-service.yaml
,然后在运行实际的 kubectl create 命令之前替换环境变量 PUBLIC_IP。
【讨论】:
为什么不 out="$(cat $1)" 和 kubectl apply -f out ...也许我错过了什么【参考方案7】:我的做法:
tools/jinja2-cli.py
:
#!/usr/bin/env python3
import os
import sys
from jinja2 import Environment, FileSystemLoader
sys.stdout.write(Environment(loader=FileSystemLoader('templates/')).from_string(sys.stdin.read()).render(env=os.environ) + "\n")
制定规则:
_GENFILES = $(basename $(TEMPLATES))
GENFILES = $(_GENFILES:templates/%=%)
$(GENFILES): %: templates/%.j2 $(MKFILES) tools/jinja2-cli.py .env
env $$(cat .env | xargs) tools/jinja2-cli.py < $< > $@ || (rm -f $@; false)
在.j2
模板文件中,您可以使用任何 jinja 语法结构,例如env.GUEST
将被GUEST
的值替换为.env
中定义的值
所以你的templates/deploy.yaml.j2
看起来像:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: env.GUEST
另一种方法(仅使用 bash
内置函数和 xargs
)可能是
env $(cat .env | xargs) cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: $GUEST
EOF
【讨论】:
【参考方案8】:我一直在用kubetpl
它具有三种不同的模板风格,并支持 ConfigMap/Secret 冻结。
【讨论】:
【参考方案9】:yaml 不会从另一个 yaml 文件中读取值。作为替代方法,您可以试试这个。
kind: Pod
metadata:
creationTimestamp: null
annotations:
namespace: &namespaceId dev
imageId: &imgageId nginx
podName: &podName nginx-pod
containerName: &containerName nginx-container
name: *podName
namespace: *namespaceId
spec:
containers:
- image: *imgageId
name: *containerName
resources:
dnsPolicy: ClusterFirst
restartPolicy: Always
status:
【讨论】:
这比硬编码这些值有什么好处?如果它们在同一个文件中...【参考方案10】:我创建了一个名为 kubectl_apply 的脚本。它从 .env 加载变量,替换 yml 中的 $CUSTOMVAR 并将其传递给 kubectl 命令
#!/bin/bash
set -a
source .env
set +a
eval "cat <<EOF
$(<$1)
EOF
" | kubectl apply -f -
【讨论】:
【参考方案11】:我已经发布了一个命令行工具ysed,它可以帮助你完成这个任务,以防你打算编写脚本。
【讨论】:
【参考方案12】:我认为现在应该使用标准 - Helm 而不是自定义脚本来解决这个问题。您无需部署即可在机器上生成 Kubernetes yaml。
一个例子:
在您的机器上安装 helm,以便存在 helm
命令
https://artifacthub.io/packages/helm/pauls-helm-charts/helloworld - 安装按钮
helm repo add pauls-helm-charts http://tech.paulcz.net/charts
helm pull pauls-helm-charts/helloworld --version 2.0.0
tar -zxvf helloworld-2.0.0.tgz && cd helloworld
helm template -f values.yaml --output-dir helloworld . --namespace my-namespace --name-template=my-name
所以它从values.yaml
创建了这些文件:
wrote helloworld/helloworld/templates/serviceaccount.yaml
wrote helloworld/helloworld/templates/service.yaml
wrote helloworld/helloworld/templates/deployment.yaml
在values.yaml
中,您可以更改预定义的repository
(或者100% 可以在Kubernetes yamls 中随意重复任何值):
image:
repository: paulczar/spring-helloworld
现在,如果您要部署,请确保 kubectl
正常工作,然后使用 kubectl apply -f serviceaccount.yaml
等应用这些生成的文件。
【讨论】:
【参考方案13】:Helm 正是为这样的事情而设计的。它处理一组复杂的资源部署作为一个组等。
但如果我们仍在寻找一些简单的替代方案,那么使用 ant 怎么样?
如果您想在构建过程或测试过程中修改文件,那么您也可以使用 ant 任务。
使用 ant 您可以将所有环境值加载为属性,或者您可以简单地加载属性文件,例如:
<property environment="env" />
<property file="build.properties" />
然后你可以有一个目标,将模板文件转换成你想要的 yaml 文件。
<target name="generate_from_template">
<!-- Copy task to replaces values and create new file -->
<copy todir="$dest.dir" verbose="true" overwrite="true" failonerror="true">
<!-- List of files to be processed -->
<fileset file="$source.dir/xyz.template.yml" />
<!-- Mapper to transform filename. Removes '.template' from the file
name when copying the file to output directory -->
<mapper type="regexp" from="(.*).template(.*)" to="\1\2" />
<!-- Filter chain that replaces the template values with actual values
fetched from properties file -->
<filterchain>
<expandproperties />
</filterchain>
</copy>
</target>
当然,您可以使用fileset
而不是file
,以防您想动态更改多个文件(嵌套或其他)的值
您的模板文件xyz.template.yml
应如下所示:
apiVersion: v1
kind: Service
metadata:
name: $XYZ_RES_NAME-ser
labels:
app: $XYZ_RES_NAME
version: v1
spec:
type: NodePort
ports:
- port: $env.XYZ_RES_PORT
protocol: TCP
selector:
app: $XYZ_RES_NAME
version: v1
env.
属性从环境变量加载,其他从属性文件加载
希望它有所帮助:)
【讨论】:
【参考方案14】:如果您只想在部署运行时更改镜像或标签,您可以在部署中设置特定容器的镜像:
kubectl apply -f k8s
kubectl set image deployments/worker-deployment worker=IMAGE:TAG
【讨论】:
【参考方案15】:在 jitsi 项目中,tpl
== frep
命令用于替换值,是envsubst
的扩展
https://github.com/jitsi/docker-jitsi-meet/issues/65
我继续使用像 sed 和朋友这样的旧 shell 工具,但是当处理的价值超过少数时,这样的代码很快就会变得不可读。
【讨论】:
【参考方案16】:创建一个名为 kubectl_advance
的文件,如下所示,并像 kubectl 命令一样享受调用它的乐趣。
例如
EXPORT MY_VAL="my-v1"
kubectl_advance -c -f sample.yaml # -c option is to call create command
kubectl_advance -r -f sample2.yaml # -r option is to call replace command
假设 yaml 文件的值类似于 $MY_VAL 将被环境变量替换。
#!/usr/bin/env bash
helpFunction()
echo "Supported option is [-f] for file"
exit 1
while getopts "f:cr" opt
do
case "$opt" in
f ) yamlFile="$OPTARG" ;;
c ) COMMAND_IS_CREATE="true" ;;
r ) COMMAND_IS_REPLACE="true" ;;
? ) helpFunction ;; # Print helpFunction in case parameter is non-existent
esac
done
echo 'yaml file is : '$yamlFile
YAML_CONTENT=`eval "cat <<EOF
$(<$yamlFile)
EOF
"`
echo 'Final File Content is :=>'
echo '------------------'
echo "$YAML_CONTENT"
if [[ "$COMMAND_IS_CREATE" == "true" ]]; then
COMMAND="create"
fi
if [[ "$COMMAND_IS_REPLACE" == "true" ]]; then
COMMAND="replace"
fi
echo "$YAML_CONTENT" | kubectl $COMMAND -f -
【讨论】:
【参考方案17】:使用 YAML 处理器,例如 yq
在尝试了sed
和envsubst
之后,我发现yq 是更改YAML 配置标签的好方法。在大多数情况下,您希望基于 GIT_COMMIT_HASH
变量(主要在您的 GitHub Actions、GitLab、BitBucket 等中提供)更改 CI/CD 管道中的 spec.template.spec.containers[0].image
。所以在installing yq 之后,例如通过自制软件:
brew install yq
定义变量并让 yq 进行替换:
# define variables
GIT_COMMIT_HASH=f01ffa895db8b7e25d5410ce4d33493fd8db9d8e8b089aaa265020be8099ff38
IMAGE_NAME=ghcr.io/yourrepo/guestbook:$GIT_COMMIT_HASH
# replace image tag
yq e ".spec.template.spec.containers[0].image = \"$IMAGE_NAME\"" -i deployment.yaml
现在你的deployment.yaml
得到了新的图像版本,然后看起来像这样:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
containers:
- image: ghcr.io/yourrepo/guestbook:f01ffa895db8b7e25d5410ce4d33493fd8db9d8e8b089aaa265020be8099ff38
name: guestbook
仅供参考:您的 deployment.yaml
不是真正有效的 Kubernetes 配置 - template.spec.container
不应位于 metadata
标签下 - 而且拼写为 containers
。
【讨论】:
以上是关于如何使用 Kubernetes yaml 文件设置动态值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 kubernetes 中使用 yaml 文件删除和重新创建 pod
如何滚动重启pod而不改变kubernetes(k8s)中的部署yaml?