This gives a some quick examples of a service running in 3 copies exposed on http and https behind a load balancer, with a setup that allow zero downtime deployments and a multi environment deployments with environment specifics in separate files

Client (HTTP/HTTPS)-> Load balancer (HTTP)-> 3 x Service

Overview of files

  • my-api.deployment.yaml: Defines what pods(containers) to run, environments variables to set, liveliness, readiness, startup checks and
  • my-api.service.yaml: Creates a common reference for all pods that can be used by the ingress to create the load balancers

Environment specific files:

my-api.deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: my-api
        type: backend
    spec:
      serviceAccountName: my-api
      containers:
        - name: my-api
          image: URL_TO_CONTAINER_IMAGE
          imagePullPolicy: Always # Make sure we are not using an old cached version on the node
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            runAsNonRoot: true
            runAsUser: 65534
            runAsGroup: 65534
            capabilities:
              add: []
          envFrom:
            - configMapRef:
                name: common
            - configMapRef:
                name: my-api
            - secretRef:
                name: my-api
          ports:
            - name: listen-port
              containerPort: 3000
          startupProbe:
            failureThreshold: 60
            httpGet:
              path: /_startup
              port: listen-port
              scheme: HTTP
            periodSeconds: 1
            successThreshold: 1
            timeoutSeconds: 10
          livenessProbe:
            httpGet:
              path: /_status
              port: listen-port
            failureThreshold: 3
            initialDelaySeconds: 30
            periodSeconds: 5
            successThreshold: 1
            timeoutSeconds: 6
          readinessProbe:
            httpGet:
              path: /_readiness
              port: listen-port
            failureThreshold: 3
            initialDelaySeconds: 10
            periodSeconds: 5
            successThreshold: 1
            timeoutSeconds: 6
          volumeMounts:
            - name: my-api
              mountPath: /keys
              readOnly: true
          resources:
            requests:
              cpu: 1000m
              memory: 4000M
      volumes:
        - name: my-api
          secret:
            secretName: my-api-files

my-api.service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-api
  labels:
    app: my-api
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 3000
  type: ClusterIP
  selector:
    app: my-api

my-api.ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-api
spec:
  tls:
  - hosts:
    - my-api.domain.tld
  rules:
  - host: my-api.domain.tld
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: my-api
            port:
              name: http

common.configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: common
data:
  LOG_LEVEL: DEBUG
  ENVIRONMENT: production
  NODE_ENV: production

my-api.configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: common
data:
  DATABASE_URL: pg://username:${password}@1.2.3.4/some-database # Password is replaced with DATABASE_PASSWORD when reading the environment variable

my-api.secretsmap.yaml

apiVersion: v1
kind: Secret
metadata:
  name: my-api
data:
  DATABASE_PASSWORD: cGFzc3dvcmQ= # Base64 encoded passwords, echo -n 'password' | base64

my-api-files.secretsmap.yaml

apiVersion: v1
kind: Secret
metadata:
  name: my-api-files
data:
  private-key.pem: cGFzc3dvcmQ= # Base64 encoded binary file, base64 --input private-key.pem