{"id":432,"date":"2025-03-25T12:37:39","date_gmt":"2025-03-25T12:37:39","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=432"},"modified":"2025-03-25T12:37:39","modified_gmt":"2025-03-25T12:37:39","slug":"helm-best-practices-essential-tips-to-know","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=432","title":{"rendered":"Helm Best Practices: Essential Tips To Know"},"content":{"rendered":"<p>When it comes to Kubernetes configuration management, Helm is one of the key tools used by many organizations.<\/p>\n<p>This blog covers the following simple but essential pieces of Helm Best Practices you should know when using them in your organization&#8217;s projects.<\/p>\n<ul>\n<li>Subcharts<\/li>\n<li>Identifying images used<\/li>\n<li>Converting Public images to private images<\/li>\n<li>Converting helm to manifests<\/li>\n<li>generating values.yaml from defaults.<\/li>\n<\/ul>\n<h2 id=\"using-community-charts\">Using Community Charts<\/h2>\n<p>There is no need to author each and every Helm chart from scratch because we have a lot of official and community Helm charts available that follow all the best practices.<\/p>\n<p>Typically, everyone uses these community charts as a reference or modifies them directly to suit their project requirements while adhering to security guidelines. These customized charts are then hosted in an internal Helm registry (e.g., JFrog, S3, etc.).<\/p>\n<p>However, without understanding the community Helm chart, if you try to install and configure it, you will likely end up troubleshooting extensively without a clear idea of what\u2019s happening.<\/p>\n<p>I have seen this in my projects and in community forums where people raise issues due to a lack of understanding.<\/p>\n<p>For the sake of explanation, we will use the&nbsp;<strong>Prometheus Helm Chart<\/strong>&nbsp;as an example.<\/p>\n<p><!--kg-card-begin: html--><br \/>\n <iframe loading=\"lazy\" src=\"https:\/\/embeds.beehiiv.com\/2a495ef4-3de7-4600-8a0d-de5dc968b372\" data-test-id=\"beehiiv-embed\" width=\"100%\" height=\"320\" frameborder=\"0\" scrolling=\"no\" style=\"border-radius: 4px; border: 2px solid #e5e7eb; margin: 0; background-color: transparent;\"><\/iframe><br \/>\n<!--kg-card-end: html--><\/p>\n<h2 id=\"understanding-subcharts\">Understanding SubCharts<\/h2>\n<p>Let\u2019s say you install the Prometheus Helm chart. It will install the following components:<\/p>\n<ul>\n<li>Prometheus Server<\/li>\n<li>Alertmanager<\/li>\n<li>Kube State Metrics<\/li>\n<li>Node Exporter<\/li>\n<li>Pushgateway<\/li>\n<\/ul>\n<p>However, the chart includes templates only for the Prometheus Server.<\/p>\n<p>For all other components, such as Alertmanager, Kube State Metrics, Node Exporter, and Pushgateway, they are installed from dependency charts (subcharts).<\/p>\n<p>If you check the&nbsp;<code>Chart.yaml<\/code>&nbsp;file, you will find the added chart dependencies listed there.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-341.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"745\" height=\"603\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-341.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-341.png 745w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>This means that when using the chart in internal project networks, you need to download the subcharts used as dependencies (e.g., Alertmanager, Kube State Metrics, Node Exporter, etc.) from their respective repositories.<\/p>\n<p>After downloading and reviewing these dependency charts (e.g., Alertmanager, Kube State Metrics), move them into the&nbsp;<code>charts\/<\/code>&nbsp;directory of your main chart.<\/p>\n<p>Next, modify the main&nbsp;<code>Chart.yaml<\/code>&nbsp;file to remove external dependencies and reference only the local subcharts.<\/p>\n<h2 id=\"public-registry-images\">Public Registry Images<\/h2>\n<p>All community Helm charts use public registry images by default.<\/p>\n<p>However, organizations that&nbsp;<strong>follow strict security compliance<\/strong>&nbsp;will never allow the use of public images.<\/p>\n<p>Instead, you will need to build your own images using the&nbsp;<strong>organization\u2019s approved base images<\/strong>. This is because even Docker images must go through patching lifecycles to comply with security standards.<\/p>\n<p>Some organizations may allow the use of official base images, but these too must be patched regularly, typically within 1 to 3 months, depending on the patching lifecycle.<\/p>\n<h2 id=\"identifying-images-used\">Identifying Images Used<\/h2>\n<p>If you take the example of the Prometheus Helm chart, you might assume it only installs the Prometheus components.<\/p>\n<p>However, if you inspect the chart and its subcharts carefully, and list the images used, you will find additional images, such as the&nbsp;<strong>Config Reloader<\/strong>&nbsp;image, among others.<\/p>\n<p>For example,<\/p>\n<pre><code>$ helm list-images prometheus-community\/prometheus\n\nquay.io\/prometheus-operator\/prometheus-config-reloader:v0.73.2\nquay.io\/prometheus\/alertmanager:v0.27.0\nquay.io\/prometheus\/node-exporter:v1.8.0\nquay.io\/prometheus\/prometheus:v2.52.0\nquay.io\/prometheus\/pushgateway:v1.8.0\nregistry.k8s.io\/kube-state-metrics\/kube-state-metrics:v2.12.0<\/code><\/pre>\n<p>If you are running the deployment from a corporate network, you might not have access to public images due to&nbsp;<strong>network restrictions or security policies.<\/strong><\/p>\n<p>To deploy the chart in such environments, you should first push these required images to your organization&#8217;s private container registry.<\/p>\n<p>If security guidelines do not allow you to push community images directly to private registries, you will need to&nbsp;<strong>build your own images<\/strong>&nbsp;using the referenced Dockerfiles provided by the community charts.<\/p>\n<h2 id=\"convert-chart-to-yaml-manifest\">Convert Chart to YAML Manifest<\/h2>\n<p>If you want to understand everything about a Helm chart, such as the objects it creates, the default values, and their configurations, you can convert the chart into YAML manifests.<\/p>\n<p>For example, to view all the YAML manifests of the Prometheus chart, you can convert the chart into plain YAML files using the following command:<\/p>\n<pre><code>$ helm template prometheus-community prometheus-community\/prometheus --output-dir prometheus-manifests<\/code><\/pre>\n<p>Here is the tree structure of the manifest, which will give you a clear idea of all the objects used.<\/p>\n<pre><code>\u279c  prometheus-manifests tree\n.\n\u2514\u2500\u2500 prometheus\n    \u251c\u2500\u2500 charts\n    \u2502   \u251c\u2500\u2500 alertmanager\n    \u2502   \u2502   \u2514\u2500\u2500 templates\n    \u2502   \u2502       \u251c\u2500\u2500 configmap.yaml\n    \u2502   \u2502       \u251c\u2500\u2500 serviceaccount.yaml\n    \u2502   \u2502       \u251c\u2500\u2500 services.yaml\n    \u2502   \u2502       \u2514\u2500\u2500 statefulset.yaml\n    \u2502   \u251c\u2500\u2500 kube-state-metrics\n    \u2502   \u2502   \u2514\u2500\u2500 templates\n    \u2502   \u2502       \u251c\u2500\u2500 clusterrolebinding.yaml\n    \u2502   \u2502       \u251c\u2500\u2500 deployment.yaml\n    \u2502   \u2502       \u251c\u2500\u2500 role.yaml\n    \u2502   \u2502       \u251c\u2500\u2500 service.yaml\n    \u2502   \u2502       \u2514\u2500\u2500 serviceaccount.yaml\n    \u2502   \u251c\u2500\u2500 prometheus-node-exporter\n    \u2502   \u2502   \u2514\u2500\u2500 templates\n    \u2502   \u2502       \u251c\u2500\u2500 daemonset.yaml\n    \u2502   \u2502       \u251c\u2500\u2500 service.yaml\n    \u2502   \u2502       \u2514\u2500\u2500 serviceaccount.yaml\n    \u2502   \u2514\u2500\u2500 prometheus-pushgateway\n    \u2502       \u2514\u2500\u2500 templates\n    \u2502           \u251c\u2500\u2500 deployment.yaml\n    \u2502           \u251c\u2500\u2500 service.yaml\n    \u2502           \u2514\u2500\u2500 serviceaccount.yaml\n    \u2514\u2500\u2500 templates\n        \u251c\u2500\u2500 clusterrole.yaml\n        \u251c\u2500\u2500 clusterrolebinding.yaml\n        \u251c\u2500\u2500 cm.yaml\n        \u251c\u2500\u2500 deploy.yaml\n        \u251c\u2500\u2500 pvc.yaml\n        \u251c\u2500\u2500 service.yaml\n        \u2514\u2500\u2500 serviceaccount.yaml<\/code><\/pre>\n<h2 id=\"multi-environment-management\">Multi-Environment Management<\/h2>\n<p>When deploying a Helm chart, it is important to understand the default values defined in the&nbsp;<code>values.yaml<\/code>&nbsp;file.<\/p>\n<p>If you are using a community chart for your project, you should customize the&nbsp;<code>values.yaml<\/code>&nbsp;file to meet your specific environment requirements.<\/p>\n<p>For example,<\/p>\n<p>You can extract all the default values from the chart into a&nbsp;<code>values.yaml<\/code>&nbsp;file using the following command:<\/p>\n<pre><code>helm show values prometheus-community\/prometheus &gt; values.yaml<\/code><\/pre>\n<p>Typically projects require different configurations for development, staging, and production environments.<\/p>\n<p>By creating separate&nbsp;<code>values.yaml<\/code>&nbsp;files for each environment, you can customize settings to suit specific requirements.<\/p>\n<p>For example,<\/p>\n<ul>\n<li><code>dev.yaml<\/code>: Use small replicas and no persistence for cost-saving.<\/li>\n<li><code>prod.yaml<\/code>: Use high resource limits and enable persistence for reliability.<\/li>\n<\/ul>\n<p>Structure your values files like this.<\/p>\n<pre><code>values\/\n\u251c\u2500\u2500 base.yaml           # Common across all environments\n\u251c\u2500\u2500 dev.yaml           # Dev specific\n\u251c\u2500\u2500 staging.yaml       # Staging specific  \n\u251c\u2500\u2500 prod.yaml          # Production specific\n\u2514\u2500\u2500 prod-secrets.yaml  # Encrypted with sops or sealed-secrets<\/code><\/pre>\n<h2 id=\"chartyaml-best-practices\">Chart.yaml Best Practices<\/h2>\n<p>Use semantic versioning (e.g., 1.0.0) for both the chart (version) and the application (appVersion).<\/p>\n<pre><code>version: 1.2.3       # Chart version\nappVersion: 4.5.6    # Application version<\/code><\/pre>\n<p>Also, include meaningful description, keywords, and maintainers fields to make your chart discoverable and maintainable.<\/p>\n<h2 id=\"testing-and-validation\">Testing and Validation<\/h2>\n<p>Run&nbsp;<code>helm lint<\/code>&nbsp;to catch errors and enforce conventions before publishing.<\/p>\n<pre><code>helm lint .\/my-chart<\/code><\/pre>\n<p>This command checks for:<\/p>\n<ul>\n<li>Proper YAML formatting<\/li>\n<li>Required fields in Chart.yaml<\/li>\n<li>Common template syntax errors<\/li>\n<li>Best practice violations<\/li>\n<\/ul>\n<p>Before deploying to any environment, use the dry-run feature to verify what Helm will actually generate:<\/p>\n<pre><code>helm install my-release .\/my-chart --dry-run --debug<\/code><\/pre>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>These are just basic tips to help you save time and follow good practices when working with community Helm charts.<\/p>\n<p>There are many more best practices to consider, but those are beyond the scope of this blog.<\/p>\n<p>Now, over to you!<\/p>\n<p>If you have any doubts about this blog, drop them in the comments!<\/p>\n<p>Want to Stay Ahead in DevOps &amp; Cloud? Join the Free Newsletter Below.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/helm-best-practices-essential-tips-to-know\/\" target=\"_blank\" rel=\"noopener noreferrer\">Helm Best Practices: Essential Tips To Know \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/helm-best-practices-essential-tips-to-know\/<\/p>\n","protected":false},"author":1,"featured_media":433,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-432","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/432","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=432"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/432\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/433"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=432"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=432"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}