如何使用 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 - 您有envsubsetdeployment 的示例,以便我们查看变量声明和锚点的语法吗? 您只需将 $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 &amp;&amp; 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

在尝试了sedenvsubst 之后,我发现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

Kubernetes - YAML文件解读

如何滚动重启pod而不改变kubernetes(k8s)中的部署yaml?

kubernetes(K8S)集群yaml常见用法

kubernetes:如何编写 pod yaml 文件以将卷从主机映射到容器

验证Kubernetes YAML的最佳实践和策略