Documentation for a newer release is available. View Latest

Deployment

Building as a Container

The following Dockerfile can be used as a template for integration with your own underlying base container.

FROM adoptopenjdk/openjdk11

RUN mkdir -p /tips-csm/conf /tips-csm/lib

COPY tips-csm-application-kafka-1.2.107-runnable.jar /tips-csm/lib/
COPY cinnamon-agent-2.17.3.jar /tips-csm/lib/

WORKDIR /tips-csm
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http://localhost:8080/actuator/health/ | grep UP || exit 1

ENTRYPOINT java \
  -javaagent:/tips-csm/lib/cinnamon-agent-2.17.3.jar \
  -cp "/tips-csm/lib/tips-csm-application-kafka-1.2.107-runnable.jar:/tips-csm/conf" \
  ${IPF_JAVA_ARGS} \
  -Dma.glasnost.orika.writeClassFiles=false \
  -Dma.glasnost.orika.writeSourceFiles=false \
  -Dconfig.override_with_env_vars=true \
  -Dloader.main=com.iconsolutions.ipf.payments.csm.tips.application.TipsApplicationKafkaApplication  \
  org.springframework.boot.loader.PropertiesLauncher

Docker-Compose

Docker-compose.yaml
# This contains only infrastructure and payment, query + CSM adapters.
services:

  tips-csm-application-kafka:
    image: tips-csm
    container_name: tips-csm-application-kafka
    ports:
      - "8080:8080"
      - "9001:9001"
#    environment:
#      - IPF_JAVA_ARGS=-Dnoargs=set
    volumes:
      - ./tips-csm.conf:/tips-csm/conf/application.conf:ro
    depends_on:
      - kafka

  zookeeper:
    image: zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"

  kafka:
    image: registry.ipf.iconsolutions.com/kafka-icon:2.13-2.7.1
    container_name: kafka
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
      - "29092:29092"
    environment:
      - KAFKA_BROKER_ID=0
      - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
      - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
      - KAFKA_LOG_RETENTION_MINUTES=10
      - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1
      - KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS=1
      - KAFKA_LISTENERS=PLAINTEXT://:29092,PLAINTEXT_HOST://:9092
      - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      - KAFKA_INTER_BROKER_LISTENER_NAME=PLAINTEXT

  kafdrop:
    image: obsidiandynamics/kafdrop:latest
    container_name: kafdrop
    ports:
      - "9000:9000"
    environment:
      - KAFKA_BROKERCONNECT=kafka:29092
    depends_on:
      - kafka

  ipf-mongo:
    image: registry.ipf.iconsolutions.com/ipf-docker-mongodb:latest
    container_name: ipf-mongo
    ports:
      - "27017:27017"
    healthcheck:
      test: echo 'db.runCommand("ping").ok' | mongo localhost:27017/test --quiet

Kubernetes and OpenShift

The TIPS CSM Service operates as an un-clustered, stateless application and so can be easily deployed using the following manifests

Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tips-csm-service
  namespace: default
  labels:
    app: tips-csm-service
    product: ipfv2
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tips-csm-service
      product: ipfv2
  template:
    metadata:
      labels:
        app: tips-csm-service
        product: ipfv2
    spec:
      imagePullPolicy: Always
      containers:
        - name: tips-csm-service
          image: tips-csm-service-kafka:latest
          ports:
            - name: actuator
              containerPort: 8080
            - name: akka-metrics
              containerPort: 9001
          livenessProbe:
            httpGet:
              path: /actuator/health
              port: actuator
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
            successThreshold: 1
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: actuator
              scheme: HTTP
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
            successThreshold: 1
          startupProbe:
            httpGet:
              path: /actuator/health
              port: actuator
              scheme: HTTP
            periodSeconds: 10
            failureThreshold: 30
          env:
            - name: "POD_NAME"
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: "POD_IP"
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: "KUBERNETES_NAMESPACE"
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: "AKKA_CLUSTER_BOOTSTRAP_SERVICE_NAME"
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.labels['app']
            - name: "KAFKA_KEYSTORE_PASS"
              valueFrom:
                secretKeyRef:
                  name: kafka-client-keystore
                  key: kafka-client-keystore-pass
            - name: "KAFKA_TRUSTSTORE_PASS"
              valueFrom:
                secretKeyRef:
                  name: kafka-client-keystore
                  key: kafka-client-truststore-pass
            - name: "POD_NAME"
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: "POD_IP"
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: "KUBERNETES_NAMESPACE"
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          resources:
            limits:
              memory: 1Gi
            requests:
              memory: 1Gi
              cpu: 1
          volumeMounts:
            - mountPath: /tips-csm/conf
              name: config-volume
            - name: kafka-client-keystore
              mountPath: /tmp/keys/kafka-client-keystore.p12
              subPath: kafka-client-keystore.p12
            - name: kafka-client-keystore
              mountPath: /tmp/keys/kafka-client-truststore.p12
              subPath: kafka-client-truststore.p12
      volumes:
        - name: config-volume
          configMap:
            name: tips-csm-service-cm
        - name: kafka-client-keystore
          secret:
            secretName: kafka-client-keystore
Service Manifest
apiVersion: v1
kind: Service
metadata:
  name: tips-csm-service
  namespace: default
  labels:
    app: tips-csm-service
    product: ipfv2
spec:
  selector:
    app: tips-csm-service
    product: ipfv2
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      name: actuator
    - protocol: TCP
      port: 9001
      targetPort: 9001
      name: akka-metrics
ConfigMap Manifest
apiVersion: v1
kind: ConfigMap
metadata:
  name: tips-csm-service-cm
  namespace: default
  labels:
    app: tips-csm-service
    product: ipfv2
data:
  application.conf: |
    ipf.mongodb.url = ""
    spring.data.mongodb.uri = ${ipf.mongodb.url}

    common-kafka-client-settings {
      bootstrap.servers = "${ipf.kafka.bootstrap.servers}"
      security.protocol = ${ipf.kafka.security.protocol}
      ssl {
        protocol = SSL

        keystore {
          location = /tmp/keys/kafka-client-keystore.p12
          password = ${KAFKA_KEYSTORE_PASS}
        }

        truststore {
          location = /tmp/keys/kafka-client-truststore.p12
          password = ${KAFKA_TRUSTSTORE_PASS}
        }
      }
    }

    akka {
      kafka {
        producer {
          kafka-clients = ${common-kafka-client-settings}
        }

        consumer {
          kafka-clients = ${common-kafka-client-settings}
          kafka-clients {
            auto.offset.reset = earliest
          }
        }
      }
      loglevel = "INFO"
    }

    akka {
      remote.artery {
        enabled = on
        transport = tcp
        canonical.hostname = ${POD_IP}
      }

      discovery {
        kubernetes-api {
          pod-label-selector = "app=%s"
        }
      }

      management {
        # available from Akka management >= 1.0.0
        health-checks {
          readiness-path = "health/ready"
          liveness-path = "health/alive"
        }
        http.hostname = ${POD_IP}
        cluster.bootstrap {
          contact-point-discovery {
            discovery-method = kubernetes-api
            service-name = ${AKKA_CLUSTER_BOOTSTRAP_SERVICE_NAME}
            required-contact-point-nr = 2
            required-contact-point-nr = ${?REQUIRED_CONTACT_POINT_NR}
          }
        }
      }
    }

    management.endpoints.web.exposure.include = "*"

  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 MDC: {%mdc}%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="akka.cluster" level="INFO"/>

        <root level="INFO">
            <appender-ref ref="ASYNC"/>
        </root>

    </configuration>