Running Apollo Router in K8s

Hi, I’ve spent the last 2 days trying to run Apollo Router in a K8s cluster with no luck. I would love some help.

For some bizarre reason, it seems to work when exposing the service as a LoadBalancer, but does not work when using a NodePort service and connecting it to Ingress.

Any guidance on best practices for running with Ingress would be awesome, thanks!

I finally figured out what the issue was.

I’m using GKE and the built-in load balancer for Ingress. That was a mistake. I should’ve just used Nginx ingress.

Anyway, the solution was to use a custom health check. For some reason, the default health check for the service to be labeled HEALTHY by the ingress was failing.

I followed this guide in the GCP docs. Here are the resulting K8s manifest files and router config yaml.

  1. Apollo Router config - Exposing the GraphQL endpoint on / on port 4002 and exposing the health check endpoint /health on the same port, 4002. For Docker and K8s envs, need to use 0.0.0.0.
# router configuration configmap
apiVersion: v1
kind: ConfigMap
metadata:
  name: apollo-router-configuration-configmap
  labels:
    service: apollo-router
data:
  router.yaml: |
    supergraph:
      listen: 0.0.0.0:4002
      introspection: true
      path: /
    health-check:
      listen: 0.0.0.0:4002
      enabled: true
    headers:
      all:
        request:
          - propagate:
              matching: .*

  1. Deployment. Expose port 4002; make sure to specify the health check. Load router config and supergraph file from a volume mount that loads a config map.
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    service: apollo-router
  name: apollo-router
spec:
  replicas: 1
  selector:
    matchLabels:
      service: apollo-router
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        service: apollo-router
    spec:
      containers:
        - name: apollo-router
          image: "ghcr.io/apollographql/router:v1.0.0"
          imagePullPolicy: IfNotPresent
          args:
            - -c
            - /graphql/router.yaml
            - -s
            - /graphql/supergraph.graphql
          ports:
            - containerPort: 4002
          livenessProbe:
            httpGet:
              path: "/health"
              port: 4002
          readinessProbe:
            httpGet:
              path: "/health"
              port: 4002
          volumeMounts:
            - name: router-configuration
              mountPath: /graphql/router.yaml
              subPath: router.yaml
              readOnly: true
            - name: router-supergraph
              mountPath: /graphql/supergraph.graphql
              subPath: supergraph.graphql
              readOnly: true
      volumes:
        - name: router-configuration
          configMap:
            name: apollo-router-configuration-configmap
        - name: router-supergraph
          configMap:
            name: apollo-router-supergraph-configmap

  1. Create a Back-end config resource. This is specific for the GCE Ingress!
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: apollo-router-backendconfig
spec:
  healthCheck:
    type: HTTP
    requestPath: /health
    port: 4002

  1. Expose the deployment with a service. Specify which back-end config to use with an annotation.
cloud.google.com/backend-config: '{"ports": {
  "4002":"apollo-router-backendconfig"
}}'

Here’s the whole service file.

apiVersion: "v1"
kind: "Service"
metadata:
  name: "apollo-router-service"
  labels:
    service: "apollo-router"
  annotations:
    cloud.google.com/backend-config: '{"ports": {
    "4002":"apollo-router-backendconfig"
    }}'
spec:
  ports:
  - protocol: "TCP"
    port: 4002
    targetPort: 4002
  selector:
    service: "apollo-router"
  type: "NodePort"

  1. And, finally the GCE Ingress.
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.class: gce
    kubernetes.io/ingress.allow-http: "true"
    kubernetes.io/ingress.global-static-ip-name: REDACTED
spec:
  tls:
    - secretName: REDACTED
      hosts:
        - REDACTED
  defaultBackend:
    service:
      name: REDACTED
      port:
        number: REDACTED
  rules:
    - host: REDACTED
      http:
        paths:
        - path: /*
          pathType: ImplementationSpecific
          backend:
            service:
              name: apollo-router-service
              port:
                number: 4002

That’s it! Hope this helps the community when running the Apollo Router in Kubernetes envs. :grin:

1 Like

Thanks for sharing your solution and the details!

The Apollo Solutions team recently published a tutorial for deploying a managed federation system to GKE using Apollo Router. GitHub - apollosolutions/build-a-supergraph: Tutorial series for building an Apollo federated supergraph using Google Kubernetes Engine

I’ll definitely compare your solutions to what we have!

1 Like