16

This is a follow up question to my post Parameterized Kubernetes manifest files?.

I understand more about Helm now, after lots of reading, including the Learn Helm book (which I recommend). I still have this understanding that each microservice will have its OWN Helm chart? This defeats the purpose of what I wanted, where I just have ONE helm chart, and every time I want to use it, I just update the Chart.yml and values.yml files for each microservice, everything else is the same. For example, the microservice name, versions, and repo will change of course per microservice. Seems like I'm looking for a "template" for Chart.yaml and values.yaml. Is this possible to do? I don't know if it's recommended or not (I may not care).

I come from a CloudFoormation background, where I just have ONE parameterized CloudFormation template, and I just pass it parameters pertinent to each microservice.

UPDATE: I'm aware of Helm library charts, and I don't think they can provide the functionality I want?

Chris F
  • 421
  • 1
  • 3
  • 14
  • What problem are you trying to solve? Are there too many lines of yaml? You can do as do describe, but it is not a good practice. – Jonas Feb 17 '21 at 20:18
  • Hi again @Jonas. ALL my microservices (will) use the SAME set of charts. They will literally only differ in name, version, ingress path. If I had one Helm chart per microservice, which I think is the recommended way to do it, I will just literally copy/paste the chart files from one microservice to the other, all templates files will be the SAME, and I'll make changes in Chart.yaml (name, version, etc.) and values.yaml (image, ingress, etc.). So if I update a templates file in one, I'll have to do it in all. What's the point of doing all this? – Chris F Feb 17 '21 at 20:46
  • It sounds like all applications is managed centrally in your org? Usually, different teams manage their own apps. – Jonas Feb 17 '21 at 21:09
  • Yeah, I manage ALL of them, or at least their deployments. LOL. – Chris F Feb 17 '21 at 21:10

3 Answers3

15

Subcharts are the way to go.

I've done a very similar thing as follows:

  • Create one or more base charts which cover microservices that have similar configuration (e.g. one for backend microservices, another for frontend servers).
  • Create an overall chart for your application. This may be a bare bones chart without any resource configurations, but just a Chart.yaml and values.yaml. Or, you may also include some resources specific to your application overall and not a particular microservice (e.g. networkpolicy or ingress).

You could put everything in one git repo e.g.:

my-deployment-repo/
|- base-microservice/
   |- templates/
      |- deployment.yml
      |- service.yml
   |- Chart.yaml
   |- values.yaml
|- base-ui/
   |- templates/
      |- deployment.yml
      |- service.yml
   |- Chart.yaml
   |- values.yaml
|- myapp/
   |- Chart.yaml
   |- values.yaml

Then, your overall application chart (here myapp) can include the base charts multiple times as a dependency in its Chart.yaml. If you are putting all these charts in one git repo, you can use relative paths to point to them. E.g.

# Chart.yaml
dependencies:
  - alias: my-microservice-1
    name: base-microservice
    version: "0.1.0"
    repository: file://../base-microservice
  - alias: my-microservice-2
    name: base-microservice
    version: "0.1.0"
    repository: file://../base-microservice
  - alias: my-ui-1
    name: base-ui
    version: "0.1.0"
    repository: file://../base-ui
  - alias: my-ui-2
    name: base-ui
    version: "0.1.0"
    repository: file://../base-ui

The key thing here is alias. This allows charts to be depended on multiple times. It sets the name of the chart to the alias, so within the subcharts you can use {{.Chart.Name}}.

Finally, in myapp's values.yaml, you can pass different values to the subcharts under their alias key, e.g. if you have used {{.Values.image}} in base-microservice, you can do the following in myapp's values.yaml:

# values.yaml
my-microservice-1:
  image: foo

my-microservice-2: image: bar

When it comes to deploying myapp, run the following commands from within the myapp directory:

helm dependency update
helm install myapp .

You must always run helm dependency update before installing/upgrading a chart if any of the subcharts have changed.

I recommend git ignoring .tgz files, so you don't end up with .tgz files created by helm dependency update getting committed to your repo.

emorris
  • 266
  • 2
  • 2
  • I love this approach but sadly there are Helm bugs that prevent it from working right. You cannot reference the aliased names in the parent chart in various contexts. (https://github.com/helm/helm/issues/7729, https://github.com/helm/helm/issues/2993, https://github.com/helm/helm/issues/7093, https://github.com/helm/helm/issues/3457) – TrogDor Oct 14 '22 at 17:56
  • I'm FINALLY getting back into this project (LOL) so I'll try this approach. Thanks! – Chris F Jan 24 '23 at 17:39
  • So in the myapp/templates/deployment.yaml for example, how would you reference the image.name for each alias for my-microservice-1? Will I just do a range here, and it'll install ALL the my-microservice-*? – Chris F Jan 24 '23 at 18:23
  • Ignore my last comment. I should mess with the base-microservice templates. Should I even have myapp/templates/ ? – Chris F Jan 24 '23 at 18:49
  • So with my idea, you wouldn't have a templates folder in myapp (unless you wanted some resource for the overall app and not for every microservice). The Chart.yaml pulls in the base charts multiple times as dependencies and values.yaml configures them. – emorris Jan 25 '23 at 22:36
  • Re-reading your question again, you may not even need this. If you just need to use a chart multiple times but for differing microservices (and don't want to deploy them all at once), then just create one chart and do exactly that. Install it multiple times with different names and values files, nothing to stop charts being installed multiple times. – emorris Jan 25 '23 at 22:41
  • Yes, I may want to deploy multiple microservices all at once, or at least in the same deployment cycle, and deploy them concurrently. – Chris F Feb 01 '23 at 20:57
  • One of the nicest thing about this is the separation of concerns it allows. Individual service pipelines no longer need to know about the release environments; they only have to run their particular repo's operations (lint, build, test...) and deploy a minimalist chart to an OCI repo for the "umbrella" chart / pipeline to pick up later on. This is where you want environments and "top level" resources (secret manager, networking rules, reverse proxy, 3rd party subcharts...) to be defined. Required env config is then passed to subcharts as values. Efficient, clean and much easier to maintain. – Crono Aug 11 '23 at 20:23
  • Can anyone please share a git repo link if manage to get it working? Thanks – Muhammad Hannan Jan 08 '24 at 15:00
7

Generally, a Helm chart would cover all your microservices - ideally, you should be able to deploy whole application in one chart. It may get a little messy if you include external 3rd party dependencies, where you would need to handle dependent charts, but I assume this is out of scope of your question.

More so, if your microservices are very similar you could write for loops, such as:

{{- range $index, $service := .Values.myservices}}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-{{ $service.name }}
# --- etc ---
{{- end }}

Now, where to put your chart - in most cases a preferred way is to have a separate repo for all Ops CD files, including Helm chart - so this is where Helm chart would live. This is not to be mixed with CI files, such as Dockerfile - those should live alongside microservice repositories themselves.

taleodor
  • 867
  • 6
  • 10
  • Y, i'm leaning toward having my Helm charts in our separate Ops Git repo (we do this for ECS now). I'll explore this idea, thanks! – Chris F Feb 17 '21 at 21:36
  • All micro services in one chart? So you just have 1 release for all your services? I don't think that's great. Your helm rollback could affect everything, even only if one thing is broken. I know it sounds like it should be fine, but I have seen case where, for example, the CRD API changes behind the scenes, so the rollback fails, and you need to update the CRD API in your yaml in order to proceed. Having everything in 1 release can lead to a mess – TigerBear Jun 21 '23 at 09:08
  • Yes, single release for all services. In a distributed system you trade liveness for safety and you can never get 100% liveness guarantee - so no matter what you do you will find an example where liveness fails. Now, with a single release you always move from one well tested state to another - which is a strict requirement in many cases. Finally, for mission critical applications, test your rollback plan before the deployment - this will help avoid issues like the one you mentioned. – taleodor Jun 21 '23 at 09:31
2

I believe what you're asking is: "Is there any way to create just one helm chart that can be used for all microservices in my application?". If so, then you can just use the values.yaml file to store all the values for your templates. This is not considered good practice, considering your template file needs to hold the information for each of your microservice deployments (and thus will become really difficult to manage), but it is possible.

One example: say you have two microservices, and you need one Helm chart that will create the template for both microservices. Generally, you would create separate templates for each service under the templates folder, and deploy each Helm chart for each service individually, but instead you could create multiple deployments in one template yaml file, like

# For service 1
apiVersion: apps/v1
type: Deployment
etc., etc... (stick in all values.yaml file values here for service 1)
---
# For service 2
apiVersion: apps/v1
type: Deployment
etc., etc... (stick in all values.yaml file values here for service 2)

In your values.yaml file, you would then just place in the values for each of your services, like

# Service 1 Keys/Values
foo: value
# Service 2 Keys/Values
bar: otherValue

So to answer your question, you can package all your services into one individual Helm chart using the above method, and Kubernetes will run each service as their own ReplicaSet as expected. However, when you have many services to manage, it can be tricky to manage the template YAML files and the values.yaml files when you put in values for all your services in one file, and so it's most likely not a good practice to do this.

This is just my understanding of Helm so far, as I'm still learning Helm myself. As such, I'm not 100% sure if this can be done, so you might want to double check with another person that this answer is actually correct.

EDIT: To summarize: like I mentioned above, in theory, it is possible to use one Helm chart per service. But in practice, it will be extremely messy later on to manage the values.yaml and the templates in the templates folder. So the answer would be, "Yes in theory, but not recommended at all".

  • yes, this is exactly what I want. But what will be the directory structure to accomplish this? Each microservice is in it's own Git repo. So where will be the "generic" Helm charts be? Where will the microservice-specific values.yaml files be? How do I consolidate the two directory files? Remember, I want to be able to do a helm install ... from some diretory that has the complete chart. – Chris F Feb 17 '21 at 20:52
  • @ChrisF You would only need to put the regular Helm directory structure (Chart.yaml, values.yaml, and the templates folder) in one service. At that point, you would need to build your Docker images/push them to the registry first. Then, because K8s references only images stored in the registry, when you install your Helm chart using the files in just one microservice, K8s can pull the container image for all of your services and run it as expected. So when you run helm install from one service, it will work, as Kubernetes doesn't reference the directory, it references the container image. – Rohit Karthik Feb 17 '21 at 21:07
  • I think this is a bad way to do it. – Chris F Feb 17 '21 at 21:10
  • 1
    Yes, like I said earlier this isn't a good way to go about it all, it makes more sense to just package each service using Helm separately, as this method is extremely messy. But, this shows it's possible. This is not the best way to do it though. – Rohit Karthik Feb 17 '21 at 21:11
  • My answer highlighted the way to do it, but if you want a much cleaner way to manage each service, you would need to create individual Chart.yaml, values.yaml, and a templates folder for each app. While it might be some additional work in the beginning (have to replicate the same thing across many services), it will be a huge timesaver later on. – Rohit Karthik Feb 17 '21 at 21:15
  • 1
    Then the correct answer is "No, you need a separate Helm chart per microservice." – Chris F Feb 17 '21 at 21:16
  • @ChrisF Yes, you're right, unless you want to deal with a messy Helm structure later on. I'll update that in my answer. – Rohit Karthik Feb 17 '21 at 21:17