Deploy in OpenShift

A prerequisite to deal with OpenShift 3.11 is to have the relevant version of oc tool installed. Below are described the steps to create an OpenShift project and populate it with Optimization Server.

Step 1: create the OpenShift project and needed pull secrets

First, create an OpenShift project, either using the web UI, or with the following command:

oc new-project <my_project_name>

Then a pull secret has to be created and added to service accounts to be able to access the docker registry the images will be pulled from:

oc create secret docker-registry nexus-docker-puller --docker-server=https://dbos-registry.decisionbrain.cloud  <set your credentials here>
oc secrets add serviceaccount/default secrets/nexus-docker-puller --for=pull
oc secrets add serviceaccount/builder secrets/nexus-docker-puller

This secret needs to correspond to valid credentials on an accessible docker registry.

Finally, a secret has to be created to hold MongoDB credentials. An example is given below:

apiVersion: v1
kind: Template
metadata:
  name: 1-openshift-template-secrets.yml
objects:
- apiVersion: v1
  kind: Secret
  metadata:
    name: mongo
  type: Opaque
  data:
    database-admin-user: YWRtaW4= # admin
    database-admin-password: SWRCeVdNVHZtR0NHbndBcw== # IdByWMTvmGCGnwAs
    database-name: b3B0aW1zZXJ2ZXItZGI= # optimserver-db
    database-user: b3B0aW1zZXJ2ZXI= # optimserver
    database-password: b3B0aW1zZXJ2ZXI= # optimserver
# TODO: have all the secrets in there
#  - apiVersion: v1
#    kind: Secret
#    metadata:
#      name: nexus-docker-puller
#    type: kubernetes.io/dockerconfigjson
#    data:

Credentials provided in this secret have to be aligned with credentials given to the master application.

Step 2: import the OpenShift template file for image streams

apiVersion: v1
kind: Template
metadata:
  name: 2-openshift-template-images.yml
parameters:
- name: PROJECT_NAME
  displayName: Namespace of the current project
  required: true
  value: optim-server-integration
- name: DOCKER_TAG
  required: true
  value: snapshot
- name: CPLEX_STUDIO_WORKER_DOCKER_TAG
  required: true
  value: snapshot
- name: DOCKER_REGISTRY
  required: true
  value: docker-registry.decisionbrain.loc
objects:
### infra ###
# mongo
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: mongo
  spec:
    lookupPolicy:
      local: false
    tags:
      - annotations: null
        from:
          kind: DockerImage
          name: ${DOCKER_REGISTRY}/mongo-non-root:${DOCKER_TAG}
        importPolicy:
          insecure: true
          scheduled: true
        name: ${PROJECT_NAME}
        referencePolicy:
          type: Local
# rabbitmq
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: rabbitmq
  spec:
    lookupPolicy:
      local: false
    tags:
    - annotations: null
      from:
        kind: DockerImage
        name: ${DOCKER_REGISTRY}/rabbitmq-stomp:${DOCKER_TAG}
      importPolicy:
        insecure: true
        scheduled: true
      name: ${PROJECT_NAME}
      referencePolicy:
        type: Local
# keycloak
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: keycloak
  spec:
    lookupPolicy:
      local: false
    tags:
    - annotations: null
      from:
        kind: DockerImage
        name: ${DOCKER_REGISTRY}/dbos-keycloak:${DOCKER_TAG}
      importPolicy:
        insecure: true
        scheduled: true
      name: ${PROJECT_NAME}
      referencePolicy:
        type: Local
# no need for postgres image stream, because we use public docker hub image
### appli ###
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: dbos-master
  spec:
    lookupPolicy:
      local: false
    tags:
      - annotations: null
        from:
          kind: DockerImage
          name: ${DOCKER_REGISTRY}/dbos-master:${DOCKER_TAG}
        importPolicy:
          insecure: true
          scheduled: true
        name: ${PROJECT_NAME}
        referencePolicy:
          type: Local
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: dbos-web-console
  spec:
    lookupPolicy:
      local: false
    tags:
      - annotations: null
        from:
          kind: DockerImage
          name: ${DOCKER_REGISTRY}/dbos-web-ui-dashboard:${DOCKER_TAG}
        importPolicy:
          insecure: true
          scheduled: true
        name: ${PROJECT_NAME}
        referencePolicy:
          type: Local
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: dbos-documentation
  spec:
    lookupPolicy:
      local: false
    tags:
      - annotations: null
        from:
          kind: DockerImage
          name: ${DOCKER_REGISTRY}/dbos-documentation:${DOCKER_TAG}
        importPolicy:
          insecure: true
          scheduled: true
        name: ${PROJECT_NAME}
        referencePolicy:
          type: Local
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: cplex-cpo-worker
  spec:
    lookupPolicy:
      local: false
    tags:
      - annotations: null
        from:
          kind: DockerImage
          name: ${DOCKER_REGISTRY}/cplex-cpo-worker:${CPLEX_STUDIO_WORKER_DOCKER_TAG}
        importPolicy:
          insecure: true
          scheduled: true
        name: ${PROJECT_NAME}
        referencePolicy:
          type: Local
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: opl-worker
  spec:
    lookupPolicy:
      local: false
    tags:
      - annotations: null
        from:
          kind: DockerImage
          name: ${DOCKER_REGISTRY}/opl-worker:${CPLEX_STUDIO_WORKER_DOCKER_TAG}
        importPolicy:
          insecure: true
          scheduled: true
        name: ${PROJECT_NAME}
        referencePolicy:
          type: Local


Step 3: import the OpenShift template file for services

apiVersion: v1
kind: Template
metadata:
  name: 3-openshift-template-services.yml
parameters:
- name: PROJECT_NAME
  displayName: Namespace of the current project
  required: true
  value: optim-server-integration
objects:
# infra
- apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: infra
    name: mongo
  spec:
    ports:
      - name: mongo
        port: 27017
        protocol: TCP
        targetPort: 27017
    selector:
      deploymentconfig: mongo
- apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: infra
    name: rabbitmq
  spec:
    ports:
    - name: 4369-tcp
      port: 4369
      protocol: TCP
      targetPort: 4369
    - name: 5671-tcp
      port: 5671
      protocol: TCP
      targetPort: 5671
    - name: 5672-tcp
      port: 5672
      protocol: TCP
      targetPort: 5672
    - name: 15671-tcp
      port: 15671
      protocol: TCP
      targetPort: 15671
    - name: 15672-tcp
      port: 15672
      protocol: TCP
      targetPort: 15672
    - name: 25672-tcp
      port: 25672
      protocol: TCP
      targetPort: 25672
    - name: 61613-tcp
      port: 61613
      protocol: TCP
      targetPort: 61613
    selector:
      deploymentconfig: rabbitmq
- apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: infra
    name: keycloak
  spec:
    ports:
      - name: 8080-tcp
        port: 8080
        protocol: TCP
        targetPort: 8080
    selector:
      deploymentconfig: keycloak
# appli
- apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: dbos
    name: dbos-master
  spec:
    ports:
      - name: 8080-tcp
        port: 8080
        protocol: TCP
        targetPort: 8080
    selector:
      app: dbos
      deploymentconfig: dbos-master
- apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: dbos
    name: dbos-web-console
  spec:
    ports:
      - name: 80-tcp
        port: 80
        protocol: TCP
        targetPort: 80
      - name: 8080-tcp
        port: 8080
        protocol: TCP
        targetPort: 8080
    selector:
      app: dbos
      deploymentconfig: dbos-web-console
- apiVersion: v1
  kind: Service
  metadata:
    labels:
      app: dbos
    name: dbos-documentation
  spec:
    ports:
      - name: 80-tcp
        port: 80
        protocol: TCP
        targetPort: 80
      - name: 8080-tcp
        port: 8080
        protocol: TCP
        targetPort: 8080
    selector:
      app: dbos
      deploymentconfig: dbos-documentation
### claims ###
- kind: PersistentVolumeClaim
  apiVersion: v1
  metadata:
    name: postgres
  spec:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 1Gi
- kind: PersistentVolumeClaim
  apiVersion: v1
  metadata:
    name: mongo
  spec:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 2Gi

Step 4: import the OpenShift template file for deployment configurations

apiVersion: v1
kind: Template
metadata:
  name: 4-openshift-template-deploy.yml
parameters:
- name: PROJECT_NAME
  displayName: Namespace of the current project
  required: true
  value: optim-server-integration
objects:
### infra ###
# mongo
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    annotations:
      template.alpha.openshift.io/wait-for-ready: "true"
    name: mongo
  spec:
    replicas: 1
    selector:
      name: mongo
    strategy:
      type: Recreate
    template:
      metadata:
        labels:
          name: mongo
      spec:
        containers:
          - env:
              - name: MONGODB_USER
                valueFrom:
                  secretKeyRef:
                    key: database-user
                    name: mongo
              - name: MONGODB_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: database-password
                    name: mongo
              - name: MONGODB_ADMIN_USER
                valueFrom:
                  secretKeyRef:
                    key: database-admin-user
                    name: mongo
              - name: MONGO_INITDB_ROOT_USERNAME
                valueFrom:
                  secretKeyRef:
                    key: database-admin-user
                    name: mongo
              - name: MONGODB_ADMIN_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: database-admin-password
                    name: mongo
              - name: MONGO_INITDB_ROOT_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: database-admin-password
                    name: mongo
              - name: MONGODB_DATABASE
                valueFrom:
                  secretKeyRef:
                    key: database-name
                    name: mongo
            image: docker-registry.default.svc:5000/${PROJECT_NAME}/mongo
            imagePullPolicy: Always
            livenessProbe:
              failureThreshold: 3
              initialDelaySeconds: 30
              periodSeconds: 10
              successThreshold: 1
              tcpSocket:
                port: 27017
              timeoutSeconds: 1
            name: mongodb
            ports:
              - containerPort: 27017
                protocol: TCP
            readinessProbe:
              tcpSocket:
                port: 27017
                initialDelaySeconds: 15
                timeoutSeconds: 1
            volumeMounts:
              - mountPath: /data/db
                name: mongo-data
        volumes:
          - name: mongo-data
            persistentVolumeClaim:
              claimName: mongo
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - mongodb
          from:
            kind: ImageStreamTag
            name: mongo:${PROJECT_NAME}
            namespace: ${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
# rabbitmq
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: infra
    name: rabbitmq
  spec:
    replicas: 1
    selector:
      app: infra
      deploymentconfig: rabbitmq
    template:
      metadata:
        labels:
          app: infra
          deploymentconfig: rabbitmq
      spec:
        containers:
          - image: docker-registry.default.svc:5000/${PROJECT_NAME}/rabbitmq
            imagePullPolicy: Always
            name: rabbitmq
            ports:
              - containerPort: 4369
                protocol: TCP
              - containerPort: 5671
                protocol: TCP
              - containerPort: 5672
                protocol: TCP
              - containerPort: 15671
                protocol: TCP
              - containerPort: 15672
                protocol: TCP
              - containerPort: 25672
                protocol: TCP
              - containerPort: 61613
                protocol: TCP
            volumeMounts:
              - mountPath: /var/lib/rabbitmq
                name: rabbitmq-1
        volumes:
          - emptyDir: {}
            name: rabbitmq-1
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - rabbitmq
          from:
            kind: ImageStreamTag
            name: rabbitmq:${PROJECT_NAME}
            namespace: ${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
# keycloak / postgres
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: infra
    name: keycloak
  spec:
    replicas: 1
    template:
      metadata:
        labels:
          app: infra
          deploymentconfig: keycloak
      spec:
        containers:
          - name: postgresql
            image: docker.io/centos/postgresql-96-centos7
            imagePullPolicy: IfNotPresent
            env:
              - name: POSTGRES_USER
                value: keycloak
              - name: POSTGRES_PASSWORD
                value: keycloak
              - name: POSTGRESQL_DATABASE
                value: keycloak
              - name: POSTGRESQL_USER
                value: keycloak
              - name: POSTGRESQL_PASSWORD
                value: keycloak
            ports:
              - containerPort: 5432
                protocol: TCP
            volumeMounts:
              - mountPath: "/var/lib/postgresql/data"
                name: keycloak-postgres-volume
          - name: keycloak
            image: docker-registry.default.svc:5000/${PROJECT_NAME}/keycloak:${PROJECT_NAME}
            imagePullPolicy: Always
            env:
              - name: KEYCLOAK_PASSWORD
                value: admin
              - name: KEYCLOAK_USER
                value: admin
              - name: KEYCLOAK_URL
                value: https://keycloak-${PROJECT_NAME}.okd.decisionbrain.io/auth
              - name: DB_VENDOR
                value: postgres
              - name: PROXY_ADDRESS_FORWARDING
                value: 'true'
              - name: DB_ADDR
                value: localhost
              - name: DB_PASSWORD
                value: keycloak
              - name: KEYCLOAK_IMPORT
                value: /tmp/realm.json
              - name: CLIENT_URL
                value: "https://web-console-${PROJECT_NAME}.okd.decisionbrain.io"
            ports:
              - containerPort: 8080
                protocol: TCP
        volumes:
            - name: keycloak-postgres-volume
              persistentVolumeClaim:
                claimName: postgres
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - keycloak
          from:
            kind: ImageStreamTag
            name: keycloak:${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
### appli ###
# master
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: dbos
    name: dbos-master
  spec:
    replicas: 1
    selector:
      app: dbos
      deploymentconfig: dbos-master
    template:
      metadata:
        labels:
          app: dbos
          deploymentconfig: dbos-master
      spec:
        containers:
          - env:
              - name: LOGGING_FILE
                value: /tmp/dbos-master
              - name: SPRING_PROFILES_ACTIVE
                value: integration
              - name: JAVA_OPTS
                value: -Djava.security.egd=file:/dev/./urandom -Xmx512m  -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
              - name: SPRING_RABBITMQ_USERNAME
                value: guest
              - name: SPRING_RABBITMQ_PASSWORD
                value: guest
              - name: SPRING_DATA_MONGODB_USERNAME
                valueFrom:
                  secretKeyRef:
                    key: database-user
                    name: mongo
              - name: SPRING_DATA_MONGODB_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: database-password
                    name: mongo
              - name: SPRING_DATA_MONGODB_DATABASE
                valueFrom:
                  secretKeyRef:
                    name: mongo
                    key: database-name
              - name: SPRING_DATA_MONGODB_ADMIN_USER
                valueFrom:
                  secretKeyRef:
                    key: database-admin-user
                    name: mongo
              - name: SPRING_DATA_MONGODB_ADMIN_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: database-admin-password
                    name: mongo
              - name: KEYCLOAK_AUTHSERVERURL
                value: https://keycloak-${PROJECT_NAME}.okd.decisionbrain.io/auth
            image: docker-registry.default.svc:5000/${PROJECT_NAME}/dbos-master
            imagePullPolicy: Always
            name: dbos-master
            ports:
              - containerPort: 8080
                protocol: TCP
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - dbos-master
          from:
            kind: ImageStreamTag
            name: dbos-master:${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: dbos
    name: dbos-web-console
  spec:
    replicas: 1
    selector:
      app: dbos
      deploymentconfig: dbos-web-console
    template:
      metadata:
        labels:
          app: dbos
          deploymentconfig: dbos-web-console
      spec:
        containers:
          - env:
              - name: LOGGING_FILE
                value: /tmp/dbos-console
              - name: OPTIMSERVER_MASTER_URL
                value: '''http://dbos-master:8080/'''
              - name: KEYCLOAK_URL
                value: https://keycloak-${PROJECT_NAME}.okd.decisionbrain.io/
              - name: OPTIMSERVER_MASTER_DOC_URL
                value: '''http://dbos-documentation:8080/'''
            image: docker-registry.default.svc:5000/${PROJECT_NAME}/dbos-web-console
            imagePullPolicy: Always
            name: dbos-web-console
            ports:
              - containerPort: 80
                protocol: TCP
              - containerPort: 8080
                protocol: TCP
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - dbos-web-console
          from:
            kind: ImageStreamTag
            name: dbos-web-console:${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: dbos
    name: dbos-documentation
  spec:
    replicas: 1
    template:
      metadata:
        labels:
          app: dbos
          deploymentconfig: dbos-documentation
      spec:
        containers:
          - image: docker-registry.default.svc:5000/${PROJECT_NAME}/dbos-documentation
            imagePullPolicy: Always
            name: dbos-documentation
            ports:
              - containerPort: 80
                protocol: TCP
              - containerPort: 8080
                protocol: TCP
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - dbos-documentation
          from:
            kind: ImageStreamTag
            name: dbos-documentation:${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: dbos-workers
    name: cplex-cpo-worker
  spec:
    replicas: 1
    selector:
      app: dbos-workers
      deploymentconfig: cplex-cpo-worker
    template:
      metadata:
        labels:
          app: dbos-workers
          deploymentconfig: cplex-cpo-worker
      spec:
        containers:
          - env:
              - name: JAVA_OPTS
                value: -Xmx4000m -XX:+CrashOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/cplex-cpo-worker-heap-dump.hprof
            image: docker-registry.default.svc:5000/${PROJECT_NAME}/cplex-cpo-worker
            imagePullPolicy: Always
            name: cplex-cpo-worker
            resources:
              limits:
                cpu: 2
                memory: 8G
              requests:
                cpu: 200m
                memory: 500M
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - cplex-cpo-worker
          from:
            kind: ImageStreamTag
            name: cplex-cpo-worker:${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: dbos-workers
    name: opl-worker
  spec:
    replicas: 1
    selector:
      app: dbos-workers
      deploymentconfig: opl-worker
    template:
      metadata:
        labels:
          app: dbos-workers
          deploymentconfig: opl-worker
      spec:
        containers:
          - env:
              - name: JAVA_OPTS
                value: -Xmx4000m -XX:+CrashOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/opl-worker-heap-dump.hprof
            image: docker-registry.default.svc:5000/${PROJECT_NAME}/opl-worker
            imagePullPolicy: Always
            name: opl-worker
            resources:
              limits:
                cpu: 2
                memory: 8G
              requests:
                cpu: 200m
                memory: 500M
    triggers:
      - imageChangeParams:
          automatic: true
          containerNames:
            - opl-worker
          from:
            kind: ImageStreamTag
            name: opl-worker:${PROJECT_NAME}
        type: ImageChange
      - type: ConfigChange
### Routes ###
- apiVersion: v1
  kind: Route
  metadata:
    annotations:
      haproxy.router.openshift.io/timeout: 24h
    name: dbos-master
  spec:
    port:
      targetPort: 8080-tcp
    tls:
      insecureEdgeTerminationPolicy: Redirect
      termination: edge
    to:
      kind: Service
      name: dbos-master
      weight: 100
    wildcardPolicy: None
- apiVersion: v1
  kind: Route
  metadata:
    annotations:
      haproxy.router.openshift.io/timeout: 24h
    labels:
      app: dbos
    name: dbos-web-console
  spec:
    host: web-console-${PROJECT_NAME}.okd.decisionbrain.io
    port:
      targetPort: 8080-tcp
    tls:
      insecureEdgeTerminationPolicy: Redirect
      termination: edge
    to:
      kind: Service
      name: dbos-web-console
      weight: 100
    wildcardPolicy: None
- apiVersion: v1
  kind: Route
  metadata:
    name: dbos-documentation
  spec:
    host: documentation-${PROJECT_NAME}.okd.decisionbrain.io
    port:
      targetPort: 8080-tcp
    tls:
      insecureEdgeTerminationPolicy: Redirect
      termination: edge
    to:
      kind: Service
      name: dbos-documentation
      weight: 100
    wildcardPolicy: None
- apiVersion: v1
  kind: Route
  metadata:
    labels:
      app: infra
    name: keycloak
  spec:
    host: keycloak-${PROJECT_NAME}.okd.decisionbrain.io
    port:
      targetPort: 8080-tcp
    tls:
      insecureEdgeTerminationPolicy: Redirect
      termination: edge
    to:
      kind: Service
      name: keycloak
      weight: 100
    wildcardPolicy: None