[Book] GitOps Cookbook: 04. Helm


Репо проекта:
https://github.com/gitops-cookbook/helm-charts/tree/master/pacman


5.1 Creating a Helm Project


Делаю:
2025.11.27


Задача:
Вы хотите создать простой Helm проект


$ cd ~/tmp
$ git clone git@github.com:gitops-cookbook/helm-charts.git
$ cd helm-charts/pacman
$ helm template .
$ helm template --set replicaCount=3 .


$ helm install pacman .


$ kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
pacman-86454cc887-jdn62   1/1     Running   0          46s


$ helm history pacman


$ helm uninstall pacman


5.2 Reusing Statements Between Templates


Делаю:
2025.11.29


Задача:
Вы хотите спользовать шаблоны и указывать в них значения


$ cat > templates/_helpers.tpl << EOFapp.kubernetes.io/name: 
app.kubernetes.io/version: 
EOF


Если в коде yaml прописать



nindent - отсутп


$ helm template .


Появится содержимаое.


5.3 Updating a Container Image in Helm


Делаю:
2025.11.29


Задача:
Вы хотите обновить container image в deployment файле используя Helm и проапгрейдить запущенный instance


$ git checkout .
$ rm templates/_helpers.tpl


$ helm install pacman .
NAME: pacman
LAST DEPLOYED: Sat Nov 29 01:36:29 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ helm history pacman
REVISION	UPDATED                 	STATUS  	CHART       	APP VERSION	DESCRIPTION
1       	Sat Nov 29 01:36:29 2025	deployed	pacman-0.1.0	1.0.0      	Install complete


$ vi values.yaml

Прописываем container tag to 1.1.0


$ vi Chart.yaml

Прописываем appVersion: “1.1.0”


$ helm upgrade pacman .


$ helm history pacman
REVISION	UPDATED                 	STATUS    	CHART       	APP VERSION	DESCRIPTION
1       	Sat Nov 29 01:36:29 2025	superseded	pacman-0.1.0	1.0.0      	Install complete
2       	Sat Nov 29 01:39:43 2025	deployed  	pacman-0.1.0	1.1.0      	Upgrade complete


$ helm rollback pacman 1


$ helm history pacman
REVISION	UPDATED                 	STATUS    	CHART       	APP VERSION	DESCRIPTION
1       	Sat Nov 29 01:36:29 2025	superseded	pacman-0.1.0	1.0.0      	Install complete
2       	Sat Nov 29 01:39:43 2025	superseded	pacman-0.1.0	1.1.0      	Upgrade complete
3       	Sat Nov 29 01:40:33 2025	deployed  	pacman-0.1.0	1.0.0      	Rollback to 1


$ cat > newvalues.yaml << EOF
image:
  tag: "1.2.0"
EOF


$ helm template pacman -f newvalues.yaml .


Tag прописался из файла newvalues.yaml.


$ helm uninstall pacman


5.4 Packaging and Distributing a Helm Chart


Делаю в бесплатном облаке google-cloud-shell:
2025.11.30


Задача:
Вы хотите создать свой Helm Chart и распространять его между пользователями


$ helm package .

// Сгенерировать файл index.yaml
$ helm repo index .


С gpg подписью


# Создать основную папку
mkdir -p ~/.gnupg
chmod 700 ~/.gnupg

# Создать необходимые подкаталоги
mkdir -p ~/.gnupg/private-keys-v1.d
mkdir -p ~/.gnupg/crls.d


# Убедиться что владелец правильный
chown -R $USER:$USER ~/.gnupg


$ gpg --batch --generate-key << EOF
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: marley
Name-Email: marley@internet.ru
Expire-Date: 0
%commit
EOF


$ gpg --list-secret-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
/home/marley/.gnupg/pubring.kbx
-------------------------------
sec   rsa4096 2025-11-30 [SCEA]
      5546C6173CCC9C5B5E1CD4FE0547DC35D64C40F1
uid           [ultimate] marley <marley@internet.ru>
ssb   rsa4096 2025-11-30 [SEA]


$ gpg --export-secret-keys > ~/.gnupg/secring.gpg
$ chmod 600 ~/.gnupg/secring.gpg


$ helm package --sign --key 'marley@internet.ru' \
--keyring /home/${USER}/.gnupg/secring.gpg .


$ cat pasha256:1.0.tgz.prov | grep sha256:
  pacman-0.1.0.tgz: sha256:6f8820f8109e62c28f3dec5ef8605529e452edec2e02d67add646054238bfb6b


$ helm verify pacman-0.1.0.tgz --keyring /home/${USER}/.gnupg/secring.gpg
Signed by: marley <marley@internet.ru>
Using Key With Fingerprint: 5546C6173CCC9C5B5E1CD4FE0547DC35D64C40F1
Chart Hash Verified: sha256:6f8820f8109e62c28f3dec5ef8605529e452edec2e02d67add646054238bfb6b


[OK!] 5.5 Deploying a Chart from a Repository


Делаю в бесплатном облаке google-cloud-shell:
2025.11.30


Задача:
Вы хотите задеплоить Helm Chart хранящийся в репо


// На дату 2025.11.30 не получается добавить, т.к. забанены всей страной
// Удалось выполнить команды в бесплатном облаке google-cloud-shell
$ helm repo add bitnami https://charts.bitnami.com/bitnami


$ helm repo update
$ helm repo list
$ helm search repo postgresql


$ helm install my-db \
  bitnami/postgresql \
  --namespace postgres \
  --create-namespace \
  --set auth.username=user1,auth.password=postgres1,auth.database=postgresdb1,primary.persistence.enabled=false


$ kubectl get pods -n postgres
NAME                 READY   STATUS    RESTARTS   AGE
my-db-postgresql-0   1/1     Running   0          23s


// To get the password for "user1" run:
$ {
  export POSTGRES_PASSWORD=$(kubectl get secret --namespace postgres my-db-postgresql -o jsonpath="{.data.password}" | base64 -d)
  echo ${POSTGRES_PASSWORD}
}


// Подключиться к базе postgresql с помощью клиента psql
// OK!
$ kubectl run my-db-postgresql-client --rm --tty -i --restart='Never' --namespace postgres --image docker.io/bitnami/postgresql:latest --env="PGPASSWORD=$POSTGRES_PASSWORD" \
      --command -- psql --host my-db-postgresql -U user1 -d postgresdb1 -p 5432


// To connect to your database from outside the cluster
// OK!
$ kubectl port-forward --namespace postgres svc/my-db-postgresql 5432:5432 &
    PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U user1 -d postgresdb1 -p 5432


$ kubectl get statefulset -n postgres
NAME               READY   AGE
my-db-postgresql   1/1     2m54s


$ helm show values bitnami/postgresql


[OK!] 5.6 Deploying a Chart with a Dependency


Делаю в бесплатном облаке google-cloud-shell:
2025.11.30


Задача:
Вы хотите задеплоить Helm Chart, с зависимостями от другого Helm Chart


https://github.com/bitnami/charts/tree/main/bitnami/postgresql/#installing-the-chart


$ cd ~/tmp
$ mkdir -p music/templates
$ cd music


$ cat > templates/deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: 
  labels:
    app.kubernetes.io/name: 
    app.kubernetes.io/version: 
spec:
  replicas: 
  selector:
    matchLabels:
      app.kubernetes.io/name: 
  template:
    metadata:
      labels:
        app.kubernetes.io/name: 
    spec:
      containers:
          - image: ":"
            imagePullPolicy: 
            securityContext:
            name: 
            ports:
              - containerPort: 
                name: http
                protocol: TCP
            env:
              - name: QUARKUS_DATASOURCE_JDBC_URL
                value: 
              - name: QUARKUS_DATASOURCE_USERNAME
                value: 
              - name: QUARKUS_DATASOURCE_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: 
                    key: 
EOF


$ cat > templates/service.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: 
  name: 
spec:
  ports:
    - name: http
      port: 
      targetPort: 
  selector:
    app.kubernetes.io/name: 
EOF


$ cat > Chart.yaml << EOF
apiVersion: v2
name: music
description: A Helm chart for Music service

type: application
version: 0.1.0
appVersion: "1.0.0"

dependencies:
  - name: postgresql
    repository: "https://charts.bitnami.com/bitnami"
    version: 18.1.13
EOF


$ cat > values.yaml << EOF
image:
  repository: quay.io/gitops-cookbook/music
  tag: "1.0.0"
  pullPolicy: Always
  containerPort: 8080

replicaCount: 1

postgresql:
  server: jdbc:postgresql://music-db-postgresql:5432/postgresdb1
  postgresqlUsername: user1
  secretName: music-db-postgresql
  secretKey: password
EOF


$ helm dependency update


$ tree
.
├── Chart.lock
├── Chart.yaml
├── charts
│   └── postgresql-18.1.13.tgz
├── templates
│   ├── deployment.yaml
│   └── service.yaml
└── values.yaml


$ helm install music-db \
  --namespace music \
  --create-namespace \
  --set global.postgresql.auth.username=user1,global.postgresql.auth.password=postgres1,global.postgresql.auth.database=postgresdb1,primary.persistence.enabled=false .


$ kubectl get pods -n music
NAME                      READY   STATUS      RESTARTS       AGE
music-6d957c46bf-5w2g8    1/1     Running     2 (4m3s ago)   4m12s
music-db-postgresql-0     1/1     Running     0              4m12s


// Можно пропустить
// GET ADMIN_POSTGRES_PASSWORD
$ {
  export ADMIN_POSTGRES_PASSWORD=$(kubectl get -n music secret music-db-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d)
  echo ${POSTGRES_PASSWORD}
}


// Можно пропустить
// To connect to your database as admin
$ kubectl run my-db-postgresql-client --rm --tty -i --restart='Never' --namespace music --image docker.io/bitnami/postgresql:latest --env="PGPASSWORD=$ADMIN_POSTGRES_PASSWORD" \
      --command -- psql --host music-db-postgresql -U postgres -d postgres -p 5432


// Можно пропустить
// GET USER POSTGRES_PASSWORD
$ {
  export USER_POSTGRES_PASSWORD=$(kubectl get -n music secret music-db-postgresql -o jsonpath="{.data.password}" | base64 -d)
  echo ${POSTGRES_PASSWORD}
}


// Можно пропустить
// To connect to your database as user1
$ kubectl run my-db-postgresql-client --rm --tty -i --restart='Never' --namespace music --image docker.io/bitnami/postgresql:latest --env="PGPASSWORD=$USER_POSTGRES_PASSWORD" \
      --command -- psql --host music-db-postgresql -U user1 -d postgresdb1 -p 5432


// Можно пропустить
// To connect to your database from outside the cluster
$ kubectl port-forward --namespace music svc/music-db-postgresql 5432:5432 &
    PGPASSWORD="$USER_POSTGRES_PASSWORD" psql --host 127.0.0.1 -U user1 -d postgresdb1 -p 5432


// Можно пропустить
$ kubectl get services -n music
NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
music                    ClusterIP   10.109.139.154   <none>        8080/TCP   83s
music-db-postgresql      ClusterIP   10.106.198.189   <none>        5432/TCP   83s
music-db-postgresql-hl   ClusterIP   None             <none>        5432/TCP   83s


$ kubectl port-forward -n music service/music 8080:8080


$ curl localhost:8080/song | jq
[
  {
    "id": 1,
    "artist": "DT",
    "name": "Quiero Munchies"
  },
  {
    "id": 2,
    "artist": "Lin-Manuel Miranda",
    "name": "We Don't Talk About Bruno"
  },
  {
    "id": 3,
    "artist": "Imagination",
    "name": "Just An Illusion"
  },
  {
    "id": 4,
    "artist": "Txarango",
    "name": "Tanca Els Ulls"
  },
  {
    "id": 5,
    "artist": "Halsey",
    "name": "Could Have Been Me"
  }
]


[OK!] 5.7 Triggering a Rolling Update Automatically


Делаю в бесплатном облаке google-cloud-shell:
2025.11.30


Задача:
Вы хотите обновить deployment при изменении ConfigMap


$ cd ~/tmp
$ mkdir -p greetings/templates
$ cd greetings


$ cat > templates/deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: 
  labels:
    app.kubernetes.io/name: 
    app.kubernetes.io/version: 
spec:
  replicas: 
  selector:
    matchLabels:
      app.kubernetes.io/name: 
  template:
    metadata:
      labels:
        app.kubernetes.io/name: 
    spec:
      containers:
          - image: ":"
            imagePullPolicy: 
            securityContext:
            name: 
            ports:
              - containerPort: 
                name: http
                protocol: TCP
            env:
              - name: GREETING
                valueFrom:
                  configMapKeyRef:
                    name: 
                    key: greeting
EOF


$ cat > templates/service.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: 
  name: 
spec:
  ports:
    - name: http
      port: 
      targetPort: 
  selector:
    app.kubernetes.io/name: 
EOF


$ cat > templates/configmap.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: greeting-config
data:
  greeting: Aloha
EOF


$ cat > Chart.yaml << EOF
apiVersion: v2
name: greetings
description: A Helm chart for Greetings service

type: application
version: 0.1.0
appVersion: "1.0.0"
EOF


$ cat > values.yaml << EOF
image:
  repository: quay.io/gitops-cookbook/greetings
  tag: "1.0.0"
  pullPolicy: Always
  containerPort: 8080

replicaCount: 1

configmap:
  name: greeting-config
EOF


$ tree .
.
├── Chart.yaml
├── templates
│   ├── configmap.yaml
│   ├── deployment.yaml
│   └── service.yaml
└── values.yaml


$ helm install greetings .


$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
greetings-6df4d99d46-vzrj9   1/1     Running   0          29s


$ kubectl port-forward service/greetings 8080:8080


$ curl localhost:8080

returns

Aloha Ada


Update the ConfigMap


$ cat > templates/configmap.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: greeting-config
data:
  greeting: Hola
EOF


$ helm upgrade greetings .


$ kubectl port-forward service/greetings 8080:8080


$ curl localhost:8080

returns

Aloha Alexandra⏎


There are no changes in the Deployment object, there is no restart of the pod.


$ cat > templates/deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: 
  labels:
    app.kubernetes.io/name: 
    app.kubernetes.io/version: 
spec:
  replicas: 
  selector:
    matchLabels:
      app.kubernetes.io/name: 
  template:
    metadata:
      labels:
        app.kubernetes.io/name: 
      annotations:
        checksum/config: 
    spec:
      containers:
          - image: ":"
            imagePullPolicy: 
            securityContext:
            name: 
            ports:
              - containerPort: 
                name: http
                protocol: TCP
            env:
              - name: GREETING
                valueFrom:
                  configMapKeyRef:
                    name: 
                    key: greeting
EOF


$ cat > templates/configmap.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: greeting-config
data:
  greeting: Namaste
EOF


$ helm upgrade greetings .


$ kubectl port-forward service/greetings 8080:8080


$ curl localhost:8080


returns

Namaste Ada⏎


$ kubectl describe pod greetings-bd8c9c4df-59xrj


***
Annotations:      checksum/config:
***


$ cat > templates/configmap.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: greeting-config
data:
  greeting: Привет!
EOF


$ helm upgrade greetings .


$ kubectl port-forward service/greetings 8080:8080


// Нужно подождать перестартовки пода
$ curl localhost:8080


Привет! Ada⏎