Search code examples
kuberneteskubernetes-helm

How does helm chart deals with multiple values.yaml files when deploying?


Context:

I'm trying to implement a helm chart to deploy a python application. This python application has multiple web-servers on the same docker image. So with the same image I can run the web-sever A,B,C, etc. The only thing that changes from one another is the path inside the image. So for web-server A would be like /path/to/web_server/A.py, and B /path/to/web_server/B.py, and so on. My idea was to create this single docker image and use multiples values.yaml to deploy each one of the web-servers into Kubernetes using Helm Charts.

The problem:

I'm using helm CLI to create a deployment and service to those apps:

helm install app -f ./path/to/webserver_A.yaml webserverA

When it is deployed it is getting the status of CrashLoopBackOff, when I run kubectl describe pod to check the details of the application I'm getting an error of: no such file or directory: unknown.

What I tried:

I tested the docker image using docker run my_image /opt/my_app/path/to/webserverA.py --host 0.0.0.0 --port 8001 and it was running fine the webserver. In this image I define the workdir as /opt/my_app and the entrypoint as "python". So the image is running as expected. On Kubernetes I tried to run the image in interactive mode using: kubectl run test --rm -i -tty --image my_image:1.0 -- /bin/bash to inspect what could be wrong. Using the interactive mode I was able to run python /opt/my_app/path/to/webserverA.py and it worked as expected, the same behavior of using a docker image.

My Helm Chart code:

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-webserver
  labels:
    app: webserver
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      labels:
        app: webserver
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          command: {{ .Values.pod.cmd }}
          args: {{ .Values.pod.args }}
          ports:
            - name: http
              containerPort: {{ .Values.pod.port }}
              protocol: TCP

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-service
spec:
  selector:
    app.kubernetes.io/instance: {{ .Release.Name }}
  type: {{ .Values.service.type }}
  ports:
    - protocol: {{ .Values.service.protocol | default "TCP" }}
      port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.targetPort }}

webserverA.yaml (this is the "values.yaml that I'm using on helm CLI")

replicaCount: 1

image:
  repository: webserverImage
  tag: "1.0"
  pullPolicy: Never

service:
  name: webserver-service
  type: ClusterIP
  port: 8001
  targetPort: 8001

pod:
  port: 8001 
  cmd: ["python","/opt/my_app/path/to/webserverA.py", "--host", "0.0.0.0", "--port","8001"]

env:
  name: dev

What I found until now:

When I just write the cmd directly on deployment.yaml the webserver starts as expected, but when I get the cmd from the custom values.yaml it indeed get the parameters but it does not run.

So this works:

deployment.yaml

command: ["python","/opt/my_app/path/to/webserverA.py", "--host", "0.0.0.0", "--port","8001"]

This does not work:

deployment.yaml

command: {{ .Values.pod.cmd }}


Solution

  • You need to look at the generated manifest to see the problem. If you run helm template . from your chart directory, you will see:

    .
    .
    .
            - name: webserver
              image: "webserverImage:1.0"
              imagePullPolicy: Never
              command: [python /opt/my_app/path/to/webserverA.py --host 0.0.0.0 --port 8001]
              args: 
              ports:
                - name: http
                  containerPort: 8001
                  protocol: TCP
    

    Note that the value of command is invalid' it's no longer a list with multiple values; it's a list with the single value python /opt/my_app/path/to/webserverA.py --host 0.0.0.0 --port 8001.

    You can fix that by using the toJson filter in your template:

    .
    .
    .
          containers:
            - name: {{ .Chart.Name }}
              image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
              imagePullPolicy: {{ .Values.image.pullPolicy }}
              command: {{ .Values.pod.cmd | toJson }}
    

    Which gets you:

    .
    .
    .
          containers:
            - name: webserver
              image: "webserverImage:1.0"
              imagePullPolicy: Never
              command: ["python","/opt/my_app/path/to/webserverA.py","--host","0.0.0.0","--port","8001"]
    

    Which is what you want.