【问题标题】:How to set dynamic values with Kubernetes yaml file如何使用 Kubernetes yaml 文件设置动态值
【发布时间】: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 文件。有没有办法设置环境变量的键?

【问题讨论】:

标签: templates kubernetes yaml config key-value


【解决方案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 Awardsed 可以自己完美地读取文件,所有剩余的参数都被解析为输入文件路径。
    • 非常感谢,@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 -
    

    【讨论】:

    【解决方案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。

            一个例子:

            1. 在您的机器上安装 helm,以便存在 helm 命令

            2. https://artifacthub.io/packages/helm/pauls-helm-charts/helloworld - 安装按钮

            3. helm repo add pauls-helm-charts http://tech.paulcz.net/charts

            4. helm pull pauls-helm-charts/helloworld --version 2.0.0

            5. tar -zxvf helloworld-2.0.0.tgz &amp;&amp; cd helloworld

            6. 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

                      【讨论】:

                        猜你喜欢
                        • 2020-02-18
                        • 2019-10-26
                        • 2016-02-26
                        • 2018-12-18
                        • 2020-01-13
                        • 2016-08-21
                        • 1970-01-01
                        相关资源
                        最近更新 更多