[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⏎