Как установить динамические значения с помощью файла Kubernetes yaml?

Например, файл развертывания 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}}

E сть ConfigMap особенность с Kubernetes, но это также записывает ключ / значение в файл yaml. Есть ли способ установить ключ для переменных среды?

19 ответов

Решение

Я не думаю, что можно установить изображение через переменную или Config Map в Kubernetes. Но вы можете использовать, например, Helm, чтобы сделать развертывание более гибким и настраиваемым.

Вы также можете использовать envsubst при развертывании.

например

cat $app/deployment.yaml | envsubst | kubectl apply ...

Он заменит все переменные в файле их значениями. Мы успешно используем этот подход в нашем CI при развертывании в нескольких средах, а также для внедрения CI_TAG и т. Д. В развертывания.

Вы не можете сделать это автоматически, вам нужно использовать внешний скрипт для "компиляции" вашего шаблона или использовать helm, как предложено @Jakub.

Возможно, вы захотите использовать собственный скрипт bash, возможно, интегрированный с вашим конвейером CI.

Учитывая шаблон YML-файл называется deploy.yml.template очень похоже на тот, который вы предоставили, вы можете использовать что-то вроде этого:

#!/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 -

Одна линия:

cat app-deployment.yaml | sed "s/{{BITBUCKET_COMMIT}}/$BITBUCKET_COMMIT/g" | kubectl apply -f -

В ямле:

  ...
  containers:
  - name: ulisses
    image: niceuser/niceimage:{{BITBUCKET_COMMIT}}
  ...

Когда переменная, которую вы хотите заменить, представляет собой URL-адрес, содержащий косую черту, подобную этой

      DASHBOARD_HOST=http://abd1c6f-123246.eu-central-1.elb.amazonaws.com    

решение с использованием sed создает bad flag in substitute command: '/'ошибка. Но как сед sкоманда может использовать любой символ в качестве разделителя , мы могли бы оптимизировать решение, используя вместо s/как это:

      sed "s#{{DASHBOARD_HOST}}#$DASHBOARD_HOST#g" app-deployment.yaml | kubectl apply -f -

Мы также можем опустить catкак заявил gertvdijk , поскольку sedумеет читать файлы самостоятельно. Переменная, которую мы хотим заменить внутри, может выглядеть примерно так:

      ...
params:
  - name: DASHBOARD_HOST
    value: {{DASHBOARD_HOST}}
...

Замена нескольких переменных

Используя sed, вы в файле yaml. Предположим, ваш app-deployment.yamlимеет следующее содержание:

      ...
params:
  - name: DASHBOARD_HOST
    value: {{DASHBOARD_HOST}}
  - name: DASHBOARD_PORT
    value: {{DASHBOARD_PORT}}
...

Теперь установите обе переменные внутри вашей оболочки:

      DASHBOARD_HOST=http://abd1c6f-123246.eu-central-1.elb.amazonaws.com
DASHBOARD_PORT=9785

А затем даже можете заменить несколько переменныхсвязать sed s#команды с использованием ;как это:

      sed "s#{{DASHBOARD_HOST}}#$DASHBOARD_HOST#g;s#{{DASHBOARD_PORT}}#$DASHBOARD_PORT#g" app-deployment.yaml | kubectl apply -f -

Это очень легко с 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 -

Я создаю сценарий под названием 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 и тогда переменная окружения PUBLIC_IP будет подставлена ​​перед выполнением фактической команды kubectl create.

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: {}

Я использую kubetpl

Он имеет три различных шаблона и поддерживает заморозку ConfigMap/Secret.

Мой подход:

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/%.in $(MKFILES) tools/jinja2-cli.py .env
        env $$(cat .env | xargs) tools/jinja2-cli.py < $< > $@ || (rm -f $@; false)

Внутри файла вы можете использовать любую синтаксическую конструкцию jinja, например {{env.IMAGE}} будет заменено значением IMAGE определяется в .env

Другой подход может быть

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: ${IMAGE}
EOF

Я думаю, что в настоящее время для решения этой проблемы следует использовать стандарт - Helm вместо пользовательских скриптов. Вам не нужно выполнять развертывание для генерации yamls Kubernets на машине.

Пример:

  1. Установите руль на свою машину, чтобы 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 && cd helloworld

  6. helm template -f values.yaml --output-dir helloworld . --namespace my-namespace --name-template=my-name

Итак, он создал эти файлы из:

      wrote helloworld/helloworld/templates/serviceaccount.yaml
wrote helloworld/helloworld/templates/service.yaml
wrote helloworld/helloworld/templates/deployment.yaml

Внутри values.yaml, вы можете изменить предустановленные repository (или 100% любое значение можно повторять в ямлах Kubernetes, как хотите):

      image:
  repository: paulczar/spring-helloworld

Теперь, если вы хотите развернуть, убедитесь, что kubectl работает и просто примените эти сгенерированные файлы, используя kubectl apply -f serviceaccount.yaml, так далее.

Если вы просто хотите изменить образ или тег во время развертывания, вы можете установить образ определенного контейнера в своем развертывании:

kubectl apply -f k8s
kubectl set image deployments/worker-deployment worker=IMAGE:TAG

Согласно спецификации YAML , сделать это невозможно. но...

Это возможно с помощью оператора разнесения yq.

Пространство имен, развертывание, ClusterIP, вход ... Чисто и СУХО :

      cat <<EOF | yq 'explode(.)' | kubectl apply -f -
kind: Namespace
apiVersion: v1
metadata:
  name: &namespace example-namespace
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: example-deployment
  namespace: *namespace
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: &app-label example-app
    spec:
      containers:
      - name: httpecho
        image: hashicorp/http-echo:0.2.3
        args:
        - "-listen=:5678"
        - "-text=hello world 1"
        ports:
        - containerPort: &app-port 5678
  selector:
    matchLabels:
      app: *app-label
---
kind: Service
apiVersion: v1
metadata:
  name: &app-cluster-ip example-service
  namespace: *namespace
spec:
  type: ClusterIP
  selector:
    app: *app-label
  ports:
  - name: http
    protocol: TCP
    port: *app-port
    targetPort: *app-port
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name: example-ingress
  namespace: *namespace
spec:
  ingressClassName: caddy
  rules:
  - host: example.kubernetes.localhost
    http:
      paths:
      - path: /hello
        pathType: Prefix
        backend:
          service:
            name: *app-cluster-ip
            port:
              number: *app-port
EOF

Я создаю скрипт под названием kubectl_apply. Он загружает переменные из.env, заменяет ${CUSTOMVAR} в yml и передает его команде kubectl

  #!/bin/bash
  set -a
  source .env
  set +a
  eval "cat <<EOF
  $(<$1)
  EOF
  " | kubectl apply -f -

Я опубликовал инструмент командной строки ysed, который помогает именно в этом, на случай, если вы планируете его написать.

создать файл с именем 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 -

В проекте jitsi tpl == frep команда используется для подстановки значений, расширение для envsubst

https://github.com/jitsi/docker-jitsi-meet/issues/65

Я продолжаю использовать старые инструменты оболочки, такие как sed и друзья, но такой код быстро становится нечитаемым, когда с ним нужно иметь дело более чем горстки ценности.

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. свойство загружается из переменных среды и другого из файла свойств

Надеюсь, это помогло:)

Для своих развертываний я обычно использую диаграммы Helm. Это требует, чтобы я периодически обновлял файлы values.yaml.

Для динамического обновления файлов YAML я использовал envsubst, поскольку он прост и не требует сложной настройки. Кроме того, большинство инструментов работают только с действительными манифестами Kubernetes, а не с простыми файлами YAML.

Я создал простой скрипт для обработки модификации YAML, чтобы упростить использование.

https://github.com/alexusarov/vars_replacer

Пример:

      ./vars_replacer.sh -i [input_file] -o [output_file] -p "[key=value] [key=value]"
Другие вопросы по тегам