A Practical Guide to Deploying Microservices on Kubernetes
This guide covers deploying microservices on Kubernetes, highlighting containerization, scaling, monitoring, and security.
Join the DZone community and get the full member experience.
Join For FreeSo, you're thinking about using Kubernetes to run microservices? You're not the only one. As of 2024, more than 60% of businesses use Kubernetes, and by 2027, that percentage is predicted to climb to more than 90%. That's pretty crazy, huh?
But here's the deal. Microservices provide us a lot of freedom and the opportunity to grow, but they can be hard to manage. Do you know what it means? There are dozens or perhaps hundreds of services floating around, and it can be hard to keep track of everything. That's where Kubernetes comes in. It's like having a really clever helper who takes care of all the scalability, deployment, and management for you.
This post will show you how to actually install microservices on Kubernetes. No fluff, just useful advice from the real world. Are you ready? Let's go.
Understanding the Fundamentals
Before we start deploying things, let's talk about how Kubernetes really organizes your microservices. Kubernetes uses "pods" as the smallest parts to build things. Normally, each pod has one or more containers in it. For microservices, you'll normally run each service in its own pod. It's pretty easy, right?
But this is where it gets fascinating. Kubernetes doesn't want you to manage pods directly. You utilize higher-level tools like Deployments and StatefulSets instead.
Why?
A Deployment makes sure that your microservice always has the proper number of copies running, manages updates without a hitch, and can even go back to a previous version if something goes wrong. Services (that's what they're called) let your microservices talk to each other via the network.
You may trust me when I say that knowing these essentials will save you a lot of grief later on.
Containerizing Your Microservices
Alright, first things first. You need to get your microservices into containers. Each microservice needs its own Dockerfile that packages everything it needs to run. Here's what a typical Node.js microservice looks like:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
See that? We're using Alpine Linux as the base image. Why? Because it's super lightweight. The smaller your images, the faster your deployments. And honestly, who wants to wait around for huge images to download, right?
One more thing. Never use the "latest" tag for your images. I know it's tempting, but use semantic versions instead, like "1.2.0." This way, you know exactly what you're deploying, and rolling back becomes way easier if something breaks.
Creating Kubernetes Manifests
Now we're getting to the meat of it. Kubernetes uses YAML files (manifests) to define what you want to run. For a typical microservice, you'll need at least two things: a Deployment and a Service. Check this out:
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
labels:
app: payment-service
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
spec:
containers:
- name: payment-service
image: myregistry/payment-service:1.2.0
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Look at all that goodness! Those resource requests and limits? They prevent one microservice from hogging all your cluster's resources. And those health checks? Kubernetes uses them to know when your service is actually ready and healthy. Super important stuff.
Notice how we're pulling the database URL from a secret? Never hardcode sensitive data in your manifests. Just don't do it.
Service Discovery and Communication
So, your microservices need to talk to each other. How does that work? Well, here's the cool part: Kubernetes has built-in service discovery through DNS. When you create a Service, Kubernetes automatically creates DNS entries for it.
Let's say you have a service called "payment-service" in the "production" namespace. Other services can reach it at payment-service.production.svc.cluster.local. Pretty neat, huh?
Here's what a Service manifest looks like:
apiVersion: v1
kind: Service
metadata:
name: payment-service
spec:
selector:
app: payment-service
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
For internal communication between microservices, use ClusterIP services. This keeps all the traffic inside your cluster and gives you automatic load balancing across all your Pod replicas. Can you imagine doing all that manually? No thanks!
Configuration Management
Here's something that trips up a lot of people: managing configuration across multiple microservices. You've got non-sensitive config like feature flags, and sensitive stuff like database passwords. Kubernetes handles both.
For regular config, use ConfigMaps:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
log_level: "info"
max_connections: "100"
feature_flags: |
{
"new_checkout": true,
"beta_features": false
}
For sensitive data, use Secrets (and please, please encrypt them at rest). The key thing is keeping configuration separate from your code. This lets you deploy the same container to different environments without rebuilding it. Makes sense, right?
Scaling Strategies
Okay, this is where Kubernetes really shines. Companies using container orchestration see a 20–30% improvement in their deployment speed. That's a huge boost!
Kubernetes can automatically scale your microservices based on CPU usage, memory, or even custom metrics. It's called horizontal pod autoscaling, and it looks like this:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: payment-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: payment-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Set your minimum replicas high enough to handle sudden traffic spikes. You don't want to be caught off guard when everyone decides to use your app at the same time, you know?
Observability and Monitoring
You really need high observability in production. If you don't have it, you're flying blind. Use structured logging with correlation IDs so you can follow a single request as it moves between several microservices.
Always log to standard output, as this is a pro tip. You don't have to worry about collecting and combining logs; Kubernetes will do it for you. Don't try to handle log files inside containers; that's just asking for trouble.
Use a tool like Prometheus to track metrics. Set up an endpoint in your service that works with Prometheus and keep track of things like request latency, error rates, and any other business metrics that are important to you. Jaeger and other tools can help you with distributed tracing. They help you find bottlenecks when requests go via more than one service.
Handling State and Persistence
Now, ideally, your microservices should be stateless. But let's be real — sometimes you need to store data. For services like databases that need stable identities and persistent storage, use StatefulSets instead of Deployments.
You'll need to request storage through PersistentVolumeClaims:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: fast-ssd
But honestly? For most microservices, try to keep them stateless. Store session data in external systems like Redis. This makes scaling and recovery from failures so much easier.
Deployment Strategies
There are many ways to provide updates to your microservices. Rolling updates slowly replace old Pods with new ones, which is great because there is no downtime. Blue-green deployments keep two full environments and switch traffic after testing the new version. Canary deployments deliver a tiny amount of traffic to the new version first. If everything goes well, they will slowly send more traffic to it.
No matter what plan you pick, make sure you get the right health exams. You don't want Kubernetes to deliver traffic to pods that aren't ready yet. Been there, not fun.
Security Best Practices
Let's talk about safety for a moment. Two-thirds of companies say they are delaying or slowing down the deployment of applications because they are worried about Kubernetes security. A lot of businesses are anxious about security, and they should be!
NetworkPolicies let you decide which pods can talk to each other. This is like a firewall for your microservices. When you can, run containers as non-root users and make the root filesystem.
Conclusion
Look, deploying microservices on Kubernetes is a journey. It takes time to understand all the pieces and how they fit together. But once you get the hang of it? The benefits are huge. Automatic scaling, self-healing, rolling updates, it's all pretty amazing.
The CNCF annual survey shows that Kubernetes adoption rates have increased to 96% among respondents, telling you something: this isn't a fad. Kubernetes has become the standard way to run containerized applications, especially microservices.
Published at DZone with permission of Puneet Pahuja. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments