How to Run OpenIG in Kubernetes

The Ingress example uses the networking.k8s.io/v1 API. Enable an Ingress controller in your cluster (for Minikube, minikube addons enable ingress) before testing external access. See Kubernetes Ingress for controller and API requirements.

Preface

This article describes how to deploy OpenIG on Kubernetes. We will use OpenIG Docker image from Docker Hub Registry https://hub.docker.com/r/openidentityplatform/openig/

In this tutorial, we will use Minikube as a local Kubernetes cluster. kubectl should also be installed

Start Minikube with at least 4 cores, and 8 GB of memory to avoid insufficient resources errors.

minikube start --cpus=4 --memory=8g

Create OpenIG Service

The following service will be used for DNS lookups between OpenIG Pods and clients.

Create openig-service.yaml file with the following contents:

openig-service.yaml:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: openig
  name: openig
spec:
  clusterIP: None
  ports:
  - port: 8080
  selector:
    app: openig

Then create service in Kubernetes using kubectl:

kubectl apply -f openig-service.yaml

Make sure the service has been created:

kubectl get svc openig

NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
openig-service   ClusterIP   None         <none>        8080/TCP   7s

Create OpenIG ConfigMaps

Create Main ConfigMap

Create config folder and create config files there admin.json and config.json with the following contents

admin.json:

{
  "prefix" : "openig",
  "mode": "PRODUCTION"
}

config.json:

{
  "heap": [
  ],
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [

      ],
      "handler": {
        "type": "Router",
        "name": "_router",
        "capture": "all"
      }
    }
  }
}

Create ConfigMap for this files:

kubectl create configmap openig-config --from-file=./config/

Then make sure ConfigMap has been created

kubectl describe configmaps openig-config
Name:         openig-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
admin.json:
----
{
  "prefix" : "openig",
  "mode": "PRODUCTION"
}
config.json:
----
{
  "heap": [
  ],
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [

      ],
      "handler": {
        "type": "Router",
        "name": "_router",
        "capture": "all"
      }
    }
  }
}
Events:  <none>

Create ConfigMaps for OpenIG Routes

Create routes directory in config folder and add two route files: 10-api.json - main route and 99-default.json - default route.

10-api.json:

{
  "name": "${matches(request.uri.path, '^/')}",
  "condition": "${matches(request.uri.path, '^/')}",
  "monitor": true,
  "timer": true,
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "SwitchFilter",
          "config": {
            "onRequest": [
              {
                "condition": "${request.method != 'POST' and request.method != 'GET'}",
                "handler": {
                  "type": "StaticResponseHandler",
                  "config": {
                    "status": 405,
                    "reason": "Method not allowed",
                    "headers": {
                      "Content-Type": [
                        "application/json"
                      ]
                    },
                    "entity": "{ \"error\": \"Method not allowed\"}"
                  }
                }
              }
            ]
          }
        }
      ],

      "handler": "EndpointHandler"
    }
  },
  "heap": [
    {
      "name": "EndpointHandler",
      "type": "DispatchHandler",
      "config": {
        "bindings": [
          {
            "handler": "ClientHandler",
            "capture": "all",
            "baseURI": "https://xkcd.com"
          }
        ]
      }
    }
  ]
}

99-default.json:

{
  "name": "99-default",
  "handler": {
    "type": "StaticResponseHandler",
    "config": {
      "status": 404,
      "reason": "Not Found",
      "headers": {
        "Content-Type": [ "application/json" ]
      },
      "entity": "{ \"error\": \"Something went wrong, please contact your system administrator.\"}"
    }
  },
  "audit": "/404"
}

Create routes ConfigMap

kubectl create configmap openig-config-routes --from-file=./config/routes

Make sure ConfigMap has been created:

kubectl describe configmaps openig-config-routes

Create OpenIG Deployment

Create OpenIG deployment file

openig-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: openig-deployment
spec:
  selector:
    matchLabels:
      app: openig
  replicas: 1
  template:
    metadata:
      labels:
        app: openig
    spec:
      containers:
      - name: openig
        image: openidentityplatform/openig
        ports:
        - containerPort: 8080
        env:
        - name: CATALINA_OPTS
          value: -Dopenig.base=/usr/local/openig-config
        volumeMounts:
        - mountPath: /usr/local/openig-config/config
          readOnly: true
          name: config-volume
        - mountPath: /usr/local/openig-config/config/routes
          readOnly: true
          name: config-routes-volume
      volumes:
      - name: config-volume
        configMap:
          name: openig-config
      - name: config-routes-volume
        configMap:
          name: openig-config-routes
kubectl apply -f openig-deployment.yaml

Check if OpenIG works

Forward OpenIG service port to the local machine

kubectl port-forward service/openig 8080

Call target service:

curl -v -X GET -H 'Host: xkcd.com' -H 'Accept: application/json' 'http://localhost:8080/info.0.json'
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /info.0.json HTTP/1.1
> Host: xkcd.com
> User-Agent: curl/7.54.0
> Accept: application/json
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Age: 134
< Cache-Control: max-age=300
< Date: Tue, 14 Jan 2020 07:58:31 GMT
< ETag: "5e1ce20d-157"
< Expires: Mon, 13 Jan 2020 21:38:45 GMT
< Last-Modified: Mon, 13 Jan 2020 21:33:01 GMT
< Server: nginx
< Vary: Accept-Encoding
< Via: 1.1 varnish
< X-Cache: HIT
< X-Cache-Hits: 7
< X-Served-By: cache-ams21025-AMS
< X-Timer: S1578988712.741555,VS0,VE0
< Content-Type: application/json
< Content-Length: 343
<
* Connection #0 to host localhost left intact
{"month": "1", "num": 2254, "link": "", "year": "2020", "news": "", "safe_title": "JPEG2000", "transcript": "", "alt": "I was actually a little relieved when I learned that JPEG2000 was used in the DCI digital cinema standard. I was feeling so bad for it!", "img": "https://imgs.xkcd.com/comics/jpeg2000.png", "title": "JPEG2000", "day": "13"}

Expose OpenIG Service via Ingress

You can also expose OpenIG via Ingress

Create openig-ingress.yaml file:

openig-ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: openig-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: openig
              port:
                number: 8080

Create Ingress for OpenIG

kubectl apply -f openig-ingress.yaml

Test Access

Get your minikube IP

echo $(minikube ip)
192.168.64.15

Make request to exposed OpenIG:

$ curl -X GET -H 'Host: xkcd.com' -H 'Accept: application/json' 'http://192.168.64.15/info.0.json'
{"month": "1", "num": 2254, "link": "", "year": "2020", "news": "", "safe_title": "JPEG2000", "transcript": "", "alt": "I was actually a little relieved when I learned that JPEG2000 was used in the DCI digital cinema standard. I was feeling so bad for it!", "img": "https://imgs.xkcd.com/comics/jpeg2000.png", "title": "JPEG2000", "day": "13"}