Kubernetes Deployment Guidelines

The following guide will provision the Operational dashboard component with 3 nodes.

Diagram

Create Namespace

kubectl create namespace ipf

Create Operational Dashboard Manifests

Create operational-dashboard.yaml manifests using the following contents:

OperationalDashboard Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
    name: ipf-operational-dashboard
    labels:
        app: operational-dashboard
        product: ipfv2
spec:
    replicas: 3
    selector:
        matchLabels:
            app: operational-dashboard
            product: ipfv2
    template:
        metadata:
            labels:
                app: operational-dashboard
                product: ipfv2
        spec:
            containers:
                - name: operational-dashboard
                  image: CONTAINER_REGISTRY/ipf-operational-dashboard-service:VERSION
                  imagePullPolicy: Always
                  ports:
                    - containerPort: 8080
                      name: server-port
                    - containerPort: 55001
                      name: akka-artery
                  env:
                    - name: IPF_PODNAME
                      valueFrom:
                        fieldRef:
                          fieldPath: metadata.name
                    - name: POD_IP
                      valueFrom:
                        fieldRef:
                          fieldPath: status.podIP
                    - name: POD_NAMESPACE
                      valueFrom:
                        fieldRef:
                          fieldPath: metadata.namespace
                    - name: "AKKA_CLUSTER_BOOTSTRAP_SERVICE_NAME"
                      valueFrom:
                        fieldRef:
                          apiVersion: v1
                          fieldPath: metadata.labels['app']
                    - name: IPF_JAVA_ARGS
                      value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=60 -XX:InitialRAMPercentage=60 -XX:-PreferContainerQuotaForCPUCount"
                  resources:
                    limits:
                      memory: 3.5Gi
                    requests:
                      memory: 2Gi
                      cpu: 500M
                  livenessProbe:
                    httpGet:
                      path: /actuator/health
                      port: server-port
                      scheme: HTTP
                    initialDelaySeconds: 60
                    periodSeconds: 5
                    timeoutSeconds: 1
                    failureThreshold: 3
                    successThreshold: 1
                  readinessProbe:
                    httpGet:
                      path: /actuator/health
                      port: server-port
                      scheme: HTTP
                    initialDelaySeconds: 60
                    periodSeconds: 5
                    timeoutSeconds: 1
                    failureThreshold: 3
                    successThreshold: 1
                  startupProbe:
                    httpGet:
                      path: /actuator/health
                      port: server-port
                      scheme: HTTP
                    periodSeconds: 10
                    failureThreshold: 30
                  volumeMounts:
                    - name: configuration-dashboard
                      mountPath: /operational-dashboard-service/conf
            volumes:
              - name: configuration-dashboard
                projected:
                  defaultMode: 420
                  sources:
                    - secret:
                      name: operational-dashboard
                      items:
                        - key: users.conf
                          mode: 420
                          path: users.conf
                    - configMap:
                      name: operational-dashboard-cm
                      items:
                        - key: logback.xml
                          mode: 420
                          path: logback.xml
                        - key: application.conf
                          mode: 420
                          path: application.conf
                    - configMap:
                        name: operational-dashboard-cm-summary-layout
                        items:
                          - key: summary-layout.conf
                            mode: 420
                            path: summary-layout.conf
                    - configMap:
                        name: ipf-operational-dashboard-cm-reason-codes
                        items:
                          - key: reason-codes.conf
                            mode: 420
                            path: reason-codes.conf
OperationalDashboard Configmap
apiVersion: v1
kind: ConfigMap
metadata:
    name: ipf-operational-dashboard-cm
data:
    application.conf: |
        spring.data.mongodb.uri = "${ipf.mongodb.url}"
        
        ipf {
          business-operations = {
            auth = {
                jwt {
                  secret = ""
                  roles-claim = "roles"
                }
        
                cors {
                  allowed-origin-patterns = [ "*" ]
                }
        
                saml2 {
                  enabled = true
                  verification-certificate = "classpath:idp.crt"
                  registration-id = "sample-client"
                  single-sign-on-service-location = "https://simplesaml.${environment_name}.ipfdev.co.uk/simplesaml/saml2/idp/SSOService.php"
                  single-log-out-service-location = "https://simplesaml.${environment_name}.example.org/simplesaml/saml2/idp/SingleLogoutService.php"
                  identity-provider-entity-id = "https://simplesaml.${environment_name}.example.org/simplesaml/saml2/idp/metadata.php"
                  service-provider-entity-id = "sample-client"
                  want-authn-requests-signed = false
                  uid-attribute = "uid"
                  roles-attribute = "roles"
                  roles-separator = ","
                  return-url = "/"
                }
        
                oauth2 {
                  enabled = true
                  registrationId = "keycloak"
                  clientId = "login-app"
                  clientSecret = "802e7940-648b-4925-8079-24fa6dc47afe"
                  scopes = "openid, roles"
                  authorizationUri = "https://keycloak.${environment_name}.example.org/realms/demo/protocol/openid-connect/auth"
                  tokenUri = "https://keycloak.${environment_name}.example.org/realms/demo/protocol/openid-connect/token"
                  jwkSetUri = "https://keycloak.${environment_name}.example.org/realms/demo/protocol/openid-connect/certs"
                  returnUrl = "/"
                  rolesFromAttributes = true
                  rolesAttribute = "roles"
                  username = "preferred_username"
                }
            }
            audit = {
                enabled = true
            }
            cluster-management = {
              systems = [
                {
                  name = "Payments Service"
                  base-urls = [
                    "http://payment-service:8558"
                  ],
                  akka-management = true,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                },
                {
                  name = "Notification Service"
                  base-urls = [
                    "http://notification-service:8558"
                  ],
                  akka-management = true,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                },
                {
                  name = "ODS Ingestion"
                  base-urls = [
                    "http://ods-ingestion:8558"
                  ],
                  akka-management = true,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                },
                {
                  name = "ODS Inquiry"
                  base-urls = [
                    "http://ods-inquiry:8080"
                  ],
                  akka-management = false,
                  actuator = {
                    protocol = "http",
                    port = "8080"
                  }
                }
              ]
            }
            metrics = {
              http = {
                client = {
                  host = "grafana"
                  port = "3000"
                  endpoint-url = "/api/health"
                }
              }
              metric-url = "http://grafana:3000",
              local-metric-url = "https://grafana.${environment_name}.example.org",
              call-timeout  = 30s,
              dashboards = [
                {
                  title: "Business Metrics",
                  name: "transactions",
                  id: "0000000001",
                  panels: [
                    {id: "1"},
                    {id: "2"},
                    {id: "15"},
                    {id: "16&var-lookback_period=1y"}
                  ],
                  columns: "2"
                },
                {
                  title: "Debtor Credit Transfer Metrics",
                  name: "transactions",
                  id: "0000000001",
                  panels: [
                    {id: "4&var-behaviour=DebtorCreditTransferBehaviour&var-latency_type=FULL_FLOW", colspan: "1"},
                    {id: "4&var-behaviour=DebtorCreditTransferBehaviour&var-latency_type=CSM_STATES_ONLY", colspan: "1"},
                    {id: "4&var-behaviour=DebtorCreditTransferBehaviour&var-latency_type=NO_CSM_STATES", colspan: "1"},
                    {id: "8&var-behaviour=DebtorCreditTransferBehaviour", colspan: "2"}
                  ],
                  columns: "3"
                },
                {
                  title: "Creditor Credit Transfer Metrics",
                  name: "transactions",
                  id: "0000000001",
                  panels: [
                    {id: "4&var-behaviour=CreditorCreditTransferBehaviour&var-latency_type=FULL_FLOW", colspan: "1"},
                    {id: "4&var-behaviour=CreditorCreditTransferBehaviour&var-latency_type=CSM_STATES_ONLY", colspan: "1"},
                    {id: "4&var-behaviour=CreditorCreditTransferBehaviour&var-latency_type=NO_CSM_STATES", colspan: "1"},
                    {id: "8&var-behaviour=CreditorCreditTransferBehaviour", colspan: "2"}
                  ],
                  columns: "3"
                },
                {
                  title: "Connector Metrics",
                  name: "ipf-connectors",
                  id: "0000000002",
                  panels: [
                    {id: "1"},
                    {id: "2"},
                    {id: "4", colspan: "2"},
                    {id: "5", colspan: "2"}
                  ],
                  columns: "2"
                }
              ]
            }
        
            payment-search = {
                ods = {
                  security = {
                    enabled = false
                    grant_type = "password"
                    client_id = "login-app"
                    client_secret = "802e7940-648b-4925-8079-24fa6dc47afe"
                    username = "test"
                    password = "p4ssw0rd"
                    jwt-certificate = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhXrGmY331co1PX/tDGdMpChoaVfokUMxdrrRul4lLIGSOAEBRegLdmmmY7FgCSTtIhmkkwZWu3gaZLs5+oyld9ncXSL4OpQQvoCOd84RvWiHLhxPBynuYmypTQUP2kLeM3ntCsXI13SwN59tE/y4H/GVGTBDrfN2ELaS43OeZRpQuW1XqLWuyNGDCtC4V7cd+gld5uDBa93PUB40ypWqnYQVrC+PRiiiXcF6uyEgkOgMinR8LerKFi/iFpuMytJa9zW0d/O5aOceulWDjUJeqf+6EbonWjfJv5GMSKdsCjQ6rnq/a1gaxSyYeCctmqtUpu0Ogjjjwfwf3qkj6fWUawIDAQAB"
                  }
                }
                ods-inquiry-url = "http://ods-inquiry:8080"
                payment-summaries.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/views/summaries/payments"
                payment-details.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/views/details"
                system-events.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/catalogue/process-objects/system-events"
                message-logs.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/catalogue/process-objects/message-logs"
                process-flow-events.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/catalogue/process-objects/process-flow-events"
                payment-objects.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/all/payment-objects"
                custom-objects.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/all/custom-objects"
                auth-server.http.client.endpoint-url = ${ipf.business-operations.payment-search.ods-inquiry-url}"/realms/demo/protocol/openid-connect/token"
            }
          }
        }

    logback.xml: |
        <?xml version="1.0" encoding="UTF-8"?>
        <configuration>
          <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <target>System.out</target>
            <encoder>
              <pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg%n</pattern>
            </encoder>
          </appender>
        
          <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
            <queueSize>8192</queueSize>
            <neverBlock>true</neverBlock>
            <appender-ref ref="CONSOLE" />
          </appender>
        
          <logger name="com.iconsolutions" level="ERROR"/>
        
          <root level="INFO">
            <appender-ref ref="ASYNC"/>
          </root>
        
        </configuration>
OperationalDashboard Service
apiVersion: v1
kind: Service
metadata:
    name: operational-dashboard
    labels:
        app: operational-dashboard
        product: ipfv2
spec:
    selector:
        app: operational-dashboard
        product: ipfv2
    ports:
        - name: server-port
          protocol: TCP
          port: 8080
          targetPort: 8080
OperationalDashboard Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
    name: operational-dashboard-ingress
    labels:
        app: operational-dashboard
        product: ipfv2
    annotations:
        kubernetes.io/ingress.class: nginx
        cert-manager.io/cluster-issuer: letsencrypt
        nginx.ingress.kubernetes.io/whitelist-source-range: "0.0.0.0/0"
spec:
    rules:
        - host: ipf-gui.example.org
          http:
              paths:
                  - pathType: Prefix
                    path: /
                    backend:
                        service:
                            name: operational-dashboard
                            port:
                                number: 8080