Helm is a package manager for Kubernetes, and installing Linkerd, a popular service mesh, is a prime example of its power.
Let’s see Linkerd in action. Imagine we have two simple microservices, webapp and api, running in Kubernetes.
# webapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: nginxdemos/hello:plain-1.0
ports:
- containerPort: 80
---
# api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: nginxdemos/hello:plain-1.0
ports:
- containerPort: 80
Without Linkerd, webapp would directly call api using its Kubernetes ClusterIP. If api were to fail, webapp would likely see connection refused errors.
Now, let’s install Linkerd using Helm. First, add the Linkerd Helm repository:
helm repo add linkerd https://helm.linkerd.io/stable
helm repo update
Then, install Linkerd into the linkerd namespace. We’ll enable the ingress-nginx controller for external access and set a replica count of 2 for the control plane:
helm install linkerd2 linkerd/linkerd2 --namespace linkerd --create-namespace \
--set controller.replicaCount=2 \
--set proxyInject.enabled=true \
--set ingressController.enabled=true \
--set ingressController.replicaCount=1
After installation, Linkerd automatically injects a linkerd-proxy sidecar container into your application pods. This proxy intercepts all inbound and outbound traffic for your application containers.
Let’s apply the Linkerd installation and then update our webapp deployment to enable automatic proxy injection. The linkerd.io/inject: enabled annotation is key here.
# webapp-deployment-with-injection.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
annotations:
linkerd.io/inject: enabled # This line enables automatic proxy injection
spec:
containers:
- name: webapp
image: nginxdemos/hello:plain-1.0
ports:
- containerPort: 80
Apply this updated deployment:
kubectl apply -f webapp-deployment-with-injection.yaml
Now, when you inspect the webapp pod, you’ll see two containers: webapp and linkerd-proxy.
kubectl get pod <webapp-pod-name> -o jsonpath='{.spec.containers[*].name}'
The linkerd-proxy is what enables Linkerd’s features. It’s a high-performance Envoy proxy configured by the Linkerd control plane. When webapp tries to communicate with api, the request first goes to the linkerd-proxy in the webapp pod. This proxy then forwards the request to the linkerd-proxy in the api pod, which finally passes it to the api container.
The Linkerd control plane continuously watches your Kubernetes cluster. It configures the proxies with routing rules, TLS certificates, and metrics collection endpoints. This means Linkerd can provide features like mTLS (mutual TLS) encryption, retries, timeouts, and detailed observability metrics without any code changes to your applications.
For example, to see the Linkerd dashboard, run:
linkerd dashboard
This command will open a web interface where you can see traffic flowing between your services, observe latency, success rates, and more. You can also check the status of your Linkerd installation:
linkerd check
The mental model is that Linkerd creates a "service mesh" around your applications. Each application pod gets a proxy sidecar. These proxies talk to each other and to the Linkerd control plane. The control plane acts as the brain, orchestrating the proxies and providing the service mesh’s intelligence. You interact with the control plane via the linkerd CLI or the dashboard.
A key configuration lever is the proxyInject.enabled flag during Helm installation. When true, Linkerd automatically adds the linkerd-proxy sidecar to pods based on the linkerd.io/inject: enabled annotation. If you want to manually inject the proxy for specific deployments, you can set this to false and use linkerd inject -f deployment.yaml | kubectl apply -f -.
The next step is usually to configure advanced routing rules or implement traffic splitting for canary deployments.