Skip to content
Notifications
Clear all

Check out what I made: A Helm chart linter that hooks into your PR pipeline

1 Posts
1 Users
0 Reactions
5 Views
(@infra_architect_42)
Reputable Member
Joined: 1 month ago
Posts: 127
Topic starter   [#11259]

Having observed the proliferation of Helm charts across our multi-cloud estate—often hastily assembled by development teams with varying degrees of Kubernetes acumen—I found myself repeatedly auditing the same classes of configuration errors. These weren't merely stylistic concerns; they manifested as tangible security vulnerabilities, cost overruns from misconfigured resources, and deployment failures during environment promotion. While existing linters like `helm lint` and `kubeconform` provide a baseline, they lack the depth to enforce architectural guardrails specific to our operational model across AWS EKS, GCP GKE, and Azure AKS.

Consequently, I developed an internal tool, provisionally named `helm-arch-lint`. It is designed as a CI/CD plugin, specifically to gate pull requests targeting Helm chart repositories. Its core philosophy is to move beyond generic YAML validation and into the realm of context-aware policy enforcement. It evaluates charts not in isolation, but against a matrix of predefined corporate standards and cloud-provider specifics.

The tool is structured around a series of pluggable check modules. For example, a network module validates that Services of type `LoadBalancer` include appropriate annotations for internal provisioning in the target cloud, while a resource module ensures memory limits are always set and that `emptyDir` volumes are not mounted as `memory` medium in production-grade charts. A critical security module scans for overly permissive Pod Security Contexts and the use of `latest` tags.

Here is a condensed example of the configuration YAML that drives the rule sets, which we commit alongside our charts:

```yaml
# .helm-arch-lint/config.yaml
rules:
- id: "aws-lb-internal-only"
scope: "Service.spec.type=LoadBalancer"
condition: |
metadata.annotations['service.beta.kubernetes.io/aws-load-balancer-scheme'] != 'internal'
severity: "high"
message: "LoadBalancer Services must be explicitly defined as internal in non-public VPCs."

- id: "resource-requests-required"
scope: "PodSpec"
condition: |
containers.any(resources.requests.cpu == null or resources.requests.memory == null)
severity: "medium"
message: "All containers must have CPU and memory requests defined for proper scheduling."

- id: "no-latest-tags"
scope: "Container"
condition: |
image.endsWith(':latest')
severity: "high"
message: "Image tags must be immutable (e.g., use a SHA or versioned tag)."
```

Integration is achieved via a simple script invoked in the CI pipeline. The tool outputs a structured report (JSON, JUnit) for pipeline consumption and a human-readable summary. A typical GitLab CI job configuration looks like this:

```yaml
lint-helm:
stage: test
image: our-registry/helm-arch-lint:latest
script:
- helm-arch-lint --config ./.helm-arch-lint/ --chart-path ./charts/myapp/ --output-format junit --report-file report.xml
artifacts:
reports:
junit: report.xml
```

The initial rollout across three business units has surfaced fascinating, if predictable, patterns: a significant number of charts omitted `podDisruptionBudget` definitions, many used `hostPath` volumes without lifecycled node affinity, and several attempted to use cloud-specific storage class annotations on the wrong provider's cluster. The tool has successfully blocked over a dozen PRs containing critical misconfigurations that would have passed conventional linting.

I am considering open-sourcing the core engine, but the value is inherently in the rule sets, which are organization-specific. I'm interested in the community's perspective on a few points:
- What are the most egregious Helm chart anti-patterns you consistently encounter in production environments?
- How do you currently enforce cross-provider configuration standards (e.g., ensuring a GCP Cloud SQL proxy sidecar is not deployed on an AWS cluster)?
- Is there merit in a tool that deeply understands the semantic layer of Helm (templating logic, `values.yaml` schema) versus operating on the rendered manifests?


Boring is beautiful


   
Quote