Kustomize/Helm

Kind

Для работы Ingress Controller в кластере kind необходимо, чтобы кластер был предварительно подготовлен как это было сделано в одной из предыдущих практик.

Установка

Утилита kustomize существует в двух вариантах - как отдельная утилита, которую можно скачать со страницы с релизами, и как встроенная в утилиту kubectl. Для практических занятий воспользуемся встроенным функционалом.

Утилиту helm необходимо скачать со страницы с релизами и разместить в каталоге из переменной PATH, вариант для Linux:

curl -Ls https://get.helm.sh/helm-v3.12.0-linux-amd64.tar.gz | tar xvz linux-amd64/helm
chmod +x linux-amd64/helm
sudo mv linux-amd64/helm /usr/local/bin/helm

Kustomize

Опробуем работу kustomize в варианте, когда у нас есть базовая конфигурация и дополнительные модификации для различных сред. В качестве основного приложения для деплоя возьмем образ nginx и в зависимости от среды развертывания модифицируем хостнейм для Ingress и сообщение выдаваемое при запросе.

Создадим директории и неймспейсы для деплоя:

$ mkdir base dev prod
$ kubectl create ns development
$ kubectl create ns production

Base

Подготовим базовую конфигурацию из ресурсов Deployment, Service и Ingress, а также файл kustomization в котором перечислены данные ресурсы:

cat <<EOF>> base/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
EOF

cat <<EOF>> base/service.yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: ClusterIP
EOF

cat <<EOF>> base/ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
  - host: localhost
    http:
      paths:
      - backend:
          service:
            name: nginx
            port:
              number: 80
        path: /
        pathType: Prefix
EOF

cat <<EOF>> base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
- ingress.yaml
EOF

Development

Создадим kustomize конфигурацию для develop среды:

cat <<EOF>> dev/kustomization.yaml
resources:
- ../base
namespace: development
commonLabels:
  environment: development
configMapGenerator:
- name: index
  literals:
  - |
    index.html=develop server

  options:
    disableNameSuffixHash: true
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
        - name: nginx
          volumeMounts:
          - name: index
            mountPath: /usr/share/nginx/html
        volumes:
        - name: index
          configMap:
            name: index
patchesJson6902:
- target:
    group: networking.k8s.io
    version: v1
    kind: Ingress
    name: nginx
  patch: |-
    - op: replace
      path: /spec/rules/0/host
      value: dev.127.0.0.1.nip.io
    - op: add
      path: /spec/ingressClassName
      value: nginx
EOF

Здесь мы модифицируем лейблы и неймспейс, генерируем ConfigMap из одной строки, а также делаем патчи:

  • для Deployment, добавляя volume с ConfigMap

  • для Ingress, указывая поля host и ingressClassName

Production

Аналогично создадим kustomize конфигурацию для production среды:

cat <<EOF>> prod/kustomization.yaml
resources:
- ../base
namespace: production
commonLabels:
  environment: production
configMapGenerator:
- name: index
  literals:
  - |
    index.html=PRODUCTION SERVER!!!

  options:
    disableNameSuffixHash: true
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
        - name: nginx
          volumeMounts:
          - name: index
            mountPath: /usr/share/nginx/html
        volumes:
        - name: index
          configMap:
            name: index
patchesJson6902:
- target:
    group: networking.k8s.io
    version: v1
    kind: Ingress
    name: nginx
  patch: |-
    - op: replace
      path: /spec/rules/0/host
      value: prod.127.0.0.1.nip.io
    - op: add
      path: /spec/ingressClassName
      value: nginx
EOF

Здесь мы меняем те же параметры, что и для develop среды.

Deploy

Получается следующая структура каталогов:

.
├── base
│   ├── deployment.yaml
│   ├── ingress.yaml
│   ├── kustomization.yaml
│   └── service.yaml
├── dev
│   └── kustomization.yaml
└── prod
    └── kustomization.yaml

Используя kubectl apply -k применим обе конфигурации:

$ kubectl apply -k dev/
configmap/index created
service/nginx created
deployment.apps/nginx created
ingress.networking.k8s.io/nginx created
$ kubectl apply -k prod/
configmap/index created
service/nginx created
deployment.apps/nginx created
ingress.networking.k8s.io/nginx created

После деплоя можно наблюдать созданные ресурсы в каждом неймспейсе:

$ kubectl get deploy,po,svc,ingress -n development
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           24m
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-6b9b58c86f-5888p   1/1     Running   0          16m
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/nginx   ClusterIP   10.96.242.213   <none>        80/TCP    24m
NAME                              CLASS   HOSTS                     ADDRESS     PORTS   AGE
ingress.networking.k8s.io/nginx   nginx   dev.127.0.0.1.nip.io   localhost   80      24m

$ kubectl get deploy,po,svc,ingress -n production
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           8m20s
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-79ffd89469-mzhtf   1/1     Running   0          8m20s
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/nginx   ClusterIP   10.96.189.189   <none>        80/TCP    8m20s
NAME                              CLASS   HOSTS                      ADDRESS     PORTS   AGE
ingress.networking.k8s.io/nginx   nginx   prod.127.0.0.1.nip.io   localhost   80      8m20s

Также видно, что в зависимости от среды приложение ведет себя по разному:

$ curl dev.127.0.0.1.nip.io
develop server
$ curl prod.127.0.0.1.nip.io
PRODUCTION SERVER!!!

После можно удалить созданные неймспейсы вместе с ресурсами:

$ kubectl delete ns development production

Helm

Повторим такой же сценарий, но уже с использованием утилиты helm.

Chart

Создадим новый чарт с именем nginx и удалим генерируемые шаблоны по-умолчанию, так как они нам не понадобятся:

$ helm create nginx
Creating nginx
$ rm -r nginx/templates/*

Templates

Создадим шаблоны ресурсов Deployment, Service, Ingress и ConfigMap взяв за основу базовые ресурсы из примера с kustomize и параметризовав те части, которые будут зависеть от среды развертывания:

cat <<EOF>> nginx/templates/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: {{ .Values.image }}
        name: nginx
        volumeMounts: {{- toYaml .Values.volumeMounts | nindent 8 }}
      volumes: {{- toYaml .Values.volumes | nindent 6 }}
EOF
cat <<EOF>> nginx/templates/service.yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: ClusterIP
EOF
cat <<EOF>> nginx/templates/ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
spec:
  ingressClassName: {{ .Values.ingress.className }}
  rules:
  - host: {{ .Values.ingress.host }}
    http:
      paths:
      - backend:
          service:
            name: nginx
            port:
              number: 80
        path: /
        pathType: Prefix
EOF
cat <<EOF>> nginx/templates/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: index
data:
  index.html: |
    {{ .Values.indexData }}
EOF

Для шаблонизации используется шаблонизатор языка go, подробнее о работе с шаблонами в helm можно почитать тут.

Values

Также создадим файл values.yaml, который будет содержать параметры вставляемые в шаблон по-умолчанию и отдельно файлы с параметрами для каждой среды:

cat <<EOF>> nginx/values.yaml
image: nginx

volumeMounts:
- name: index
  mountPath: /usr/share/nginx/html
volumes:
- name: index
  configMap:
    name: index

indexData: "index string"

ingress:
  host: localhost
  className: nginx
EOF
cat <<EOF>> nginx/values-dev.yaml
indexData: "develop server"

ingress:
  host: dev.127.0.0.1.nip.io
EOF
cat <<EOF>> nginx/values-prod.yaml
indexData: "PRODUCTION SERVER!!!"

ingress:
  host: prod.127.0.0.1.nip.io
EOF

Deploy

В итоге у нас получится следующая структура каталогов:

nginx/
├── Chart.yaml
├── charts
├── templates
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── ingress.yaml
│   └── service.yaml
├── values-dev.yaml
├── values-prod.yaml
└── values.yaml

Установим наш чарт с разными параметрами в отдельные неймспейсы:

$ helm install nginx ./nginx/ -f nginx/values-dev.yaml --namespace development --create-namespace
NAME: nginx
LAST DEPLOYED: Tue May 23 00:01:23 2023
NAMESPACE: development
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ helm install nginx ./nginx/ -f nginx/values-prod.yaml --namespace production --create-namespace
NAME: nginx
LAST DEPLOYED: Tue May 23 00:03:24 2023
NAMESPACE: production
STATUS: deployed
REVISION: 1
TEST SUITE: None

После установки также можно наблюдать созданные ресурсы с разными параметрами:

$ kubectl get deploy,po,svc,ingress -n development
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           37s
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-796d76f6f9-dn6vf   1/1     Running   0          37s
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/nginx   ClusterIP   10.96.147.211   <none>        80/TCP    38s
NAME                              CLASS   HOSTS                  ADDRESS     PORTS   AGE
ingress.networking.k8s.io/nginx   nginx   dev.127.0.0.1.nip.io   localhost   80      37s
$ kubectl get deploy,po,svc,ingress -n production
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           35s
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-796d76f6f9-vbmd9   1/1     Running   0          35s
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/nginx   ClusterIP   10.96.229.104   <none>        80/TCP    35s
NAME                              CLASS   HOSTS                   ADDRESS     PORTS   AGE
ingress.networking.k8s.io/nginx   nginx   prod.127.0.0.1.nip.io   localhost   80      36s

$ curl dev.127.0.0.1.nip.io
develop server
$ curl prod.127.0.0.1.nip.io
PRODUCTION SERVER!!!