Helm’s helm uninstall command is surprisingly destructive, deleting all Kubernetes resources associated with a release, even if those resources are still actively used by other applications.
Let’s see this in action. Imagine you have a deployment my-app-deployment and a service my-app-service managed by Helm.
# Example Helm Chart Snippet
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
labels:
app: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:latest
---
apiVersion: v1
kind: Service
metadata:
name: my-app-service
labels:
app: my-app
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
If you install this chart and then run helm uninstall my-release, both my-app-deployment and my-app-service will be gone. Now, what if another application, say another-app, also relies on my-app-service to communicate?
# Example Deployment for 'another-app'
apiVersion: apps/v1
kind: Deployment
metadata:
name: another-app-deployment
labels:
app: another-app
spec:
replicas: 1
selector:
matchLabels:
app: another-app
template:
metadata:
labels:
app: another-app
spec:
containers:
- name: another-app
image: ubuntu:latest
command: ["sleep", "3600"]
env:
- name: MY_APP_SERVICE_HOST
value: "my-app-service.default.svc.cluster.local" # Assumes default namespace
When helm uninstall my-release is executed, another-app-deployment will start failing because its my-app-service dependency has been deleted. This is a common scenario where shared infrastructure components, like databases, message queues, or utility services, are managed by a Helm release that is later uninstalled.
The core problem is that Helm, by default, views all resources it manages as belonging exclusively to that release. When the release is removed, Helm cleans up everything it thinks it "owns." Kubernetes itself doesn’t inherently track shared resource dependencies across different controllers or applications in a way that prevents deletion during a Helm uninstall.
To prevent this, you need to decouple the lifecycle of certain resources from the Helm release. The most straightforward method is to manually create these shared resources outside of your Helm chart.
Here’s how you’d do it:
-
Identify Shared Resources: Before packaging your Helm chart, determine which Kubernetes resources are intended to be long-lived or shared. This might include
Serviceobjects for inter-application communication,PersistentVolumeClaims that should survive application updates, orSecrets andConfigMaps used by multiple applications. -
Create Resources Manually: Instead of defining these resources within your Helm chart’s
templates/directory, create them as standalone YAML files and apply them directly to your cluster usingkubectl apply -f <your-resource.yaml>.For instance, to create the
my-app-serviceindependently:# my-app-service.yaml apiVersion: v1 kind: Service metadata: name: my-app-service labels: app: my-app managed-by: manual # Optional: for identification spec: selector: app: my-app ports: - protocol: TCP port: 80 targetPort: 80Apply it:
kubectl apply -f my-app-service.yaml -
Modify Helm Chart: In your Helm chart, remove the YAML definitions for these manually created resources. Ensure they are no longer present in the
templates/directory. -
Configure Applications to Use Manual Resources:
- For Deployments/Pods: If your Helm chart’s deployment needs to connect to a manually created service, you can either hardcode the service name (e.g.,
my-app-service.default.svc.cluster.local) in your application’s configuration or use Helm’svalues.yamlto pass in the service name. - For other Helm Releases: If another Helm release needs to depend on this manually managed resource, it should be installed after the manual resource is created. The dependent Helm chart can then reference the resource by its name.
- For Deployments/Pods: If your Helm chart’s deployment needs to connect to a manually created service, you can either hardcode the service name (e.g.,
-
Helm Release Lifecycle: When you
helm uninstallthe release that used to manage the shared resource, Helm will only delete the resources it still defines in its state. The manually createdmy-app-servicewill remain untouched, and other applications depending on it will continue to function.
Another advanced technique involves using Helm’s hooks and dependencies, but this can become complex. A simpler approach for truly shared infrastructure is to manage it outside of Helm entirely.
A more granular control mechanism is to leverage Kubernetes’ OwnerReferences. While Helm typically sets itself as the owner of resources, you can manipulate this. However, directly altering OwnerReferences from within Helm charts is discouraged and error-prone. A better approach is to use a "controller" that creates resources and sets appropriate OwnerReferences, allowing the resource to be garbage collected only when the controller itself is deleted. Helm can then manage the lifecycle of this controller. This is often seen in more sophisticated operators.
A common pitfall is forgetting to remove the resource definition from the Helm chart after you’ve created it manually. If the resource definition remains in the chart, Helm might try to manage it again during upgrades or re-installs, leading to unexpected behavior or conflicts.
The next logical step is to consider how to manage the lifecycle of the application that uses the shared resource, especially when that application is also managed by Helm.