Primeros pasos con helm


Introduccion

En este tutorial vamos a ver como usar Helm, el cluster de despliegue sera minikube si preferis kind or cualquier otro cluster deberia funcionar de la misma manera:


Creando el chart:

helm create hello-world

Siempre hay que tener en cuenta las restricciones de DNS a la hora de elegir nombres, ya que esto puede traer problemas despues.


Inspeccionemos el contenido, como podemos ver son recursos de kubernetes similar a los templates de go:

$ cd hello-world

charts       <--- Dependencies, charts that your chart depends on.
Chart.yaml   <--- Metadata mostly, defines the version of your chart, etc.
templates    <--- Here is where the magic happens.
values.yaml  <--- Default values file (this is used to replace in the templates at runtime)

Nota: este link explica lo basico de manejo de dependencias, tu chart puede tener varias dependencias, solo tienes que listarlas como dependencias.


El archivo values.yaml por defecto se ve asi:

replicaCount: 1

image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent

nameOverride: ""
fullnameOverride: ""

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  path: /
  hosts:
    - chart-example.local
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
nodeSelector: {}
tolerations: []
affinity: {}

El proximo paso es revisar la carpeta templates:

deployment.yaml  <--- Recurso deployment estandar de kubernetes con variables de go templates.
_helpers.tpl     <--- En este archivo se definen funciones y variables comunes.
ingress.yaml     <--- Recurso ingress.
NOTES.txt        <--- Este archivo se usa para mostrar las notas una vez que termino el despliegue del chart.
service.yaml     <--- Recurso service de kubernetes o balanceador de carga virtual interno.

Go templates basico, si necesitas refrescar go templates, siempre se puede ir a la documentacion.


Revisemos el archivo deployment:

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: {{ include "hello-world.fullname" . }}
  labels:
    app.kubernetes.io/name: {{ include "hello-world.name" . }}
    helm.sh/chart: {{ include "hello-world.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "hello-world.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "hello-world.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
{{ toYaml .Values.resources | indent 12 }}
    {{- with .Values.nodeSelector }}
      nodeSelector:
{{ toYaml . | indent 8 }}
    {{- end }}
    {{- with .Values.affinity }}
      affinity:
{{ toYaml . | indent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
{{ toYaml . | indent 8 }}
    {{- end }}

La mayoria de los campos se reemplazan con lo que pasemos via values.yaml esto se pasa via .Values a menos que uses un helper o variable extra.


Sigamos con el archivo service:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "hello-world.fullname" . }}
  labels:
    app.kubernetes.io/name: {{ include "hello-world.name" . }}
    helm.sh/chart: {{ include "hello-world.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: {{ include "hello-world.name" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}

Luego el archivo ingress:

{{- if .Values.ingress.enabled -}}
{{- $fullName := include "hello-world.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ $fullName }}
  labels:
    app.kubernetes.io/name: {{ include "hello-world.name" . }}
    helm.sh/chart: {{ include "hello-world.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
  annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
  tls:
  {{- range .Values.ingress.tls }}
    - hosts:
      {{- range .hosts }}
        - {{ . | quote }}
      {{- end }}
      secretName: {{ .secretName }}
  {{- end }}
{{- end }}
  rules:
  {{- range .Values.ingress.hosts }}
    - host: {{ . | quote }}
      http:
        paths:
          - path: {{ $ingressPath }}
            backend:
              serviceName: {{ $fullName }}
              servicePort: http
  {{- end }}
{{- end }}

Este es probablemente el archivo mas interesante por que hace uso de variables locales, tambien itera sobre hosts.


Desplegando nuestro chart:

$ helm install --name my-nginx -f values.yaml .
NAME:   my-nginx
LAST DEPLOYED: Sun Dec 23 00:30:11 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Service
NAME                  AGE
my-nginx-hello-world  0s

==> v1beta2/Deployment
my-nginx-hello-world  0s

==> v1/Pod(related)

NAME                                   READY  STATUS   RESTARTS  AGE
my-nginx-hello-world-6f948db8d5-s76zl  0/1    Pending  0         0s

NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=hello-world,app.kubernetes.io/instance=my-nginx" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:80

Nuestro despliegue parece exitoso, ya que vemos el pod con estado Pending inmediatamente.


Revisando el servicio:

$ kubectl get services
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes             ClusterIP   10.96.0.1       <none>        443/TCP   1h
my-nginx-hello-world   ClusterIP   10.111.222.70   <none>        80/TCP    5m

Podemos probar desde otro pod de manera interactiva si todo funciona correctamente:

$ kubectl run -i --tty alpine --image=alpine -- sh
If you don't see a command prompt, try pressing enter.

/ # apk add curl
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20171114-r3)
(2/5) Installing nghttp2-libs (1.32.0-r0)
(3/5) Installing libssh2 (1.8.0-r3)
(4/5) Installing libcurl (7.61.1-r1)
(5/5) Installing curl (7.61.1-r1)
Executing busybox-1.28.4-r2.trigger
Executing ca-certificates-20171114-r3.trigger
OK: 6 MiB in 18 packages

/ # curl -v my-nginx-hello-world
* Rebuilt URL to: my-nginx-hello-world/
*   Trying 10.111.222.70...
* TCP_NODELAY set
* Connected to my-nginx-hello-world (10.111.222.70) port 80 (#0)
> GET / HTTP/1.1
> Host: my-nginx-hello-world
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.14.2
< Date: Sun, 23 Dec 2018 03:45:31 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 04 Dec 2018 14:44:49 GMT
< Connection: keep-alive
< ETag: "5c0692e1-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host my-nginx-hello-world left intact

y voila, como podemos ver nuestro chart de nginx funciona perfectamente.


Podemos verificar el estado via helm de esta manera:

$ helm ls
NAME            REVISION        UPDATED                         STATUS          CHART                   APP VERSION     NAMESPACE
my-nginx        1               Sun Dec 23 00:30:11 2018        DEPLOYED        hello-world-0.1.0       1.0             default

Actualizemos nuestro despliegue cambiando el tag en el archivo values.yaml de stable a mainline y tambien actualizemos la metadata en Chart.yaml para mantener la version de nuestro chart en buenas condiciones.

 $ helm upgrade my-nginx . -f values.yaml
Release "my-nginx" has been upgraded. Happy Helming!
LAST DEPLOYED: Sun Dec 23 00:55:22 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Pod(related)
NAME                                   READY  STATUS             RESTARTS  AGE
my-nginx-hello-world-6f948db8d5-s76zl  1/1    Running            0         25m
my-nginx-hello-world-c5cdcc95c-shgc6   0/1    ContainerCreating  0         0s

==> v1/Service

NAME                  AGE
my-nginx-hello-world  25m

==> v1beta2/Deployment
my-nginx-hello-world  25m


NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=hello-world,app.kubernetes.io/instance=my-nginx" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:80

Parece que todo salio bien, como podemos ver cambio la revision y la version del chart

$ helm ls
NAME            REVISION        UPDATED                         STATUS          CHART                   APP VERSION     NAMESPACE
my-nginx        2               Sun Dec 23 00:55:22 2018        DEPLOYED        hello-world-0.1.1       1.0             default

Ahora verifiquemos que la version de nginx es la que nosotros especificamos:

$ kubectl exec my-nginx-hello-world-c5cdcc95c-shgc6 -- /usr/sbin/nginx -v
nginx version: nginx/1.15.7

al momento de escribir esto la version mainline es 1.15.7, podemos hacer “rollback” si algo no salio bien con helm de esta manera:

$ helm rollback my-nginx 1
Rollback was a success! Happy Helming!

Hay varias formas de hacer rollback, en este caso para nuestro chart my-nginx especificamos la revision a la que queremos hacer “rollback” y voila.


Revisemos la version de nginx de nuevo:

$ kubectl exec my-nginx-hello-world-6f948db8d5-bsml2 -- /usr/sbin/nginx -v
nginx version: nginx/1.14.2

Siempre es una buena idea limpiar todo cuando terminamos de probar algo:

$ helm del --purge my-nginx
release "my-nginx" deleted

Si necesitas depurar o ver los templates a medida que vas trabajando en ellos podes hacer lo siguiente:

$ helm template . -n name -f values.yaml
# Source: hello-world/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: name-hello-world

Y eso es todo por hoy, ante cualquier duda deja un comentario o revisa la documentacion y el comando helm help.


DRY: Don’t Repeat Yourself

Lo bueno de usar templates es que podemos ahorrar mucho tiempo y a la vez mantener todo ordenado y sin mucha repeticion.

Upcoming topics

Algunos articulos que te pueden interesar:


Errata

Si ves algun error por favor enviame un mensaje para poder corregirlo.



No tienes cuenta? Regístrate aqui

Ya registrado? Iniciar sesión a tu cuenta ahora.

Iniciar session con GitHub
Iniciar sesion con Google
  • Comentarios

    Online: 0

Por favor inicie sesión para poder escribir comentarios.

by Gabriel Garrido