DevOps & Cloud

Microservice Architecture with Kubernetes: Pod, Service & Deployment

15 min readMarch 17, 2026
Kubernetes rehberiKubernetes tutorialMikroservisMicroservicesKubernetes podKubernetes deploymentKubernetes serviceHelm chartContainer orchestrationKubernetes .NET

Kubernetes (K8s) is an open-source platform that has become the de facto standard for container orchestration. It automates deployment, scaling, and management of applications in microservice architectures. In this article, I will examine the core components of Kubernetes, microservice deployment strategies, and package management with Helm in detail.

Since I transitioned to Kubernetes in my projects, the reliability of production environments has significantly improved thanks to zero-downtime deployments, automatic scaling, and self-healing capabilities. A properly configured K8s cluster greatly reduces operational overhead.

Kubernetes Core Components

Pod: The Smallest Deployment Unit

A Pod is the smallest and most basic unit in Kubernetes. It contains one or more containers, shared storage, and network resources.

yaml
# pod.yaml - Basic pod definition
apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: myapi
    version: v1
    environment: production
spec:
  containers:
    - name: api
      image: ghcr.io/myorg/myapi:1.0.0
      ports:
        - containerPort: 8080
          protocol: TCP
      env:
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
        - name: ConnectionStrings__DefaultConnection
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: connection-string
      resources:
        requests:
          cpu: "250m"
          memory: "256Mi"
        limits:
          cpu: "500m"
          memory: "512Mi"
      livenessProbe:
        httpGet:
          path: /health/live
          port: 8080
        initialDelaySeconds: 15
        periodSeconds: 20
      readinessProbe:
        httpGet:
          path: /health/ready
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 10
      startupProbe:
        httpGet:
          path: /health/startup
          port: 8080
        failureThreshold: 30
        periodSeconds: 10
  restartPolicy: Always

Deployment: Declarative Update Management

A Deployment defines the desired state of Pods and manages update strategies. It automatically creates ReplicaSets and provides zero-downtime deployment through rolling updates.

yaml
# deployment.yaml - Production-ready deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapi-deployment
  labels:
    app: myapi
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapi
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: myapi
        version: v1
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - myapi
                topologyKey: kubernetes.io/hostname
      containers:
        - name: api
          image: ghcr.io/myorg/myapi:1.0.0
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: myapi-config
            - secretRef:
                name: myapi-secrets
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          livenessProbe:
            httpGet:
              path: /health/live
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
---
# service.yaml - ClusterIP and LoadBalancer
apiVersion: v1
kind: Service
metadata:
  name: myapi-service
spec:
  selector:
    app: myapi
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  type: ClusterIP
---
# Ingress - External access
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapi-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/rate-limit-window: "1m"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls-cert
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapi-service
                port:
                  number: 80

ConfigMap and Secret Management

Separating application configuration and sensitive data from the container image is one of Kubernetes' core principles. ConfigMap is used for environment variables and configuration files, while Secrets are used for sensitive data like passwords and API keys:

yaml
# configmap.yaml - Application configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapi-config
  namespace: production
data:
  ASPNETCORE_ENVIRONMENT: "Production"
  Logging__LogLevel__Default: "Information"
  AllowedOrigins: "https://example.com,https://www.example.com"
  Cache__DefaultExpirationMinutes: "30"
  FeatureFlags__NewDashboard: "true"
---
# secret.yaml - Sensitive data (base64 encoded)
apiVersion: v1
kind: Secret
metadata:
  name: myapi-secrets
  namespace: production
type: Opaque
data:
  ConnectionStrings__DefaultConnection: SG9zdD1wb3N0Z3Jlczt...
  Redis__ConnectionString: cmVkaXM6NjM3OQ==
  JWT__SecretKey: c3VwZXItc2VjcmV0LWtleQ==
---
# External Secrets with vault integration (recommended)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: myapi-external-secrets
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: myapi-secrets
  data:
    - secretKey: ConnectionStrings__DefaultConnection
      remoteRef:
        key: myapi/production
        property: db-connection-string

For ConfigMap and Secret changes to take effect in pods, the deployment needs to be restarted. You can automate this using tools like Reloader or Helm hooks.

PersistentVolumeClaim for Data Persistence

For stateful applications (databases, file storage), you can request persistent storage using PersistentVolumeClaim:

yaml
# StatefulSet with persistent storage for database
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: production
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: "myapp"
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: password
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              cpu: "500m"
              memory: "1Gi"
            limits:
              cpu: "1"
              memory: "2Gi"
  volumeClaimTemplates:
    - metadata:
        name: postgres-storage
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: gp3
        resources:
          requests:
            storage: 50Gi

StatefulSet is a more appropriate choice than Deployment for stateful applications like databases. It preserves data integrity by assigning each pod a stable identity and persistent storage.

Namespace Management and Isolation

Namespaces are used to logically separate cluster resources. They provide isolation for different environments (dev, staging, production) and different teams:

bash
# Create namespaces
kubectl create namespace production
kubectl create namespace staging
kubectl create namespace development

# Namespace-level resource quotas
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "8"
    requests.memory: "16Gi"
    limits.cpu: "16"
    limits.memory: "32Gi"
    pods: "50"
    services: "20"
EOF

# Namespace-level network policy
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: production
EOF

With ResourceQuota, you can limit the resources each namespace can use. With NetworkPolicy, you can control traffic between namespaces to enforce security boundaries.

Horizontal Pod Autoscaler

HPA is used to automatically respond to traffic increases:

yaml
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapi-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapi-deployment
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
        - type: Percent
          value: 100
          periodSeconds: 30

Monitoring with Prometheus

In Kubernetes environments, Prometheus is the standard solution for metric collection and monitoring. With ServiceMonitor, you can automatically discover application metrics:

yaml
# servicemonitor.yaml - Application monitoring with Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: myapi-monitor
  namespace: production
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: myapi
  endpoints:
    - port: http
      path: /metrics
      interval: 30s
      scrapeTimeout: 10s
---
# PrometheusRule for alert definitions
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: myapi-alerts
  namespace: production
spec:
  groups:
    - name: myapi.rules
      rules:
        - alert: HighErrorRate
          expr: |
            rate(http_requests_total{status=~"5..", app="myapi"}[5m])
            / rate(http_requests_total{app="myapi"}[5m]) > 0.05
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "High error rate detected"
            description: "Error rate above 5% in the last 5 minutes"
        - alert: HighLatency
          expr: |
            histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{app="myapi"}[5m])) > 1
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "High response time detected"

The Prometheus + Grafana combination allows you to visualize metrics such as CPU, memory, HTTP request counts, error rates, and response times, and define alert rules for proactive incident management.

Package Management with Helm

Helm Chart Structure

Helm is a package manager used for packaging and distributing Kubernetes applications.

yaml
# Chart.yaml
apiVersion: v2
name: myapi
description: My API Helm Chart
version: 1.0.0
appVersion: "1.0.0"

# values.yaml - Default values
replicaCount: 3

image:
  repository: ghcr.io/myorg/myapi
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  host: api.example.com
  tls: true

resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    cpu: 500m
    memory: 512Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilization: 70

Deployment with Helm:

bash
# Install chart
helm install myapi ./charts/myapi -f values-production.yaml -n production

# Upgrade
helm upgrade myapi ./charts/myapi -f values-production.yaml -n production

# Rollback
helm rollback myapi 1 -n production

# Status check
helm status myapi -n production

Microservice Communication Patterns

For inter-service communication in Kubernetes environments:

  • ClusterIP Service: Synchronous HTTP/gRPC communication within the cluster
  • Headless Service: DNS-based solution for service discovery
  • Service Mesh (Istio/Linkerd): mTLS, traffic management, observability
  • Message Queue: Asynchronous communication with RabbitMQ or Kafka

Practical Tips and Summary

Keep these points in mind when managing microservices with Kubernetes:

  1. Resource limits: Always define CPU and memory limits for every container
  2. Health probes: Properly configure liveness, readiness, and startup probes
  3. Pod anti-affinity: Distribute Pods across different nodes for high availability
  4. Namespace isolation: Separate environments (dev, staging, prod) with namespaces
  5. GitOps: Apply declarative deployment management with ArgoCD or Flux

When properly configured, Kubernetes largely solves the scaling, reliability, and management challenges of microservice architectures. Starting small and scaling as needed is the healthiest approach.

Related Articles

Have a Flutter Project?

I build high-performance Flutter applications for iOS, Android, and web.

Get in Touch