Deployments and Services

Deployment for order-service

Deployment

k8s/order-service/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: shopease
  labels:
    app: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1            # at most 1 extra Pod during an update
      maxUnavailable: 0      # never go below 3 ready Pods
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        - name: order-service
          image: ghcr.io/shopease/order-service:1.4.2
          ports:
            - name: http
              containerPort: 8082
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
          envFrom:
            - configMapRef:
                name: order-service-config
            - secretRef:
                name: order-service-secrets
          resources:
            requests:
              cpu: 250m          # used by the scheduler to place the Pod
              memory: 512Mi
            limits:
              memory: 768Mi      # killed (OOMKilled) above this
          readinessProbe:
            httpGet: { path: /actuator/health/readiness, port: http }
            periodSeconds: 5
          livenessProbe:
            httpGet: { path: /actuator/health/liveness, port: http }
            periodSeconds: 10
          startupProbe:
            httpGet: { path: /actuator/health/liveness, port: http }
            failureThreshold: 30
            periodSeconds: 2     # allow up to 60 s for Spring to start
      terminationGracePeriodSeconds: 45

Service: A Stable Name

Service

Pods come and go and change IPs. A Service gives them one name. Other services simply call http://order-service (or order-service.shopease.svc.cluster.local):

k8s/order-service/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: shopease
spec:
  type: ClusterIP            # internal only
  selector:
    app: order-service       # sends traffic to Pods with this label
  ports:
    - name: http
      port: 80               # port other services use
      targetPort: http       # containerPort 8082

Example output

$ kubectl apply -f k8s/order-service/
deployment.apps/order-service created
service/order-service created

$ kubectl get deploy,svc -n shopease -l app=order-service
NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/order-service   3/3     3            3           52s

NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/order-service   ClusterIP   10.96.142.17   <none>        80/TCP    52s

Scaling and Autoscaling

HorizontalPodAutoscaler

k8s/order-service/hpa.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service
  namespace: shopease
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 3
  maxReplicas: 12
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70     # % of the CPU request

Example output

$ kubectl scale deploy/order-service --replicas=5 -n shopease     # manual
deployment.apps/order-service scaled

$ kubectl get hpa -n shopease
NAME            REFERENCE                  TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
order-service   Deployment/order-service   84%/70%   3         12        7          4d

Tip: Autoscaling only works for stateless services: keep sessions in Redis and data in the database, never in the Pod's memory or disk.