Despliegue
Ejecutándose como una aplicación independiente
IPF Operational Dashboard puede ser creado con 2 maven perfiles:
-
contenedor- Genera docker contenedores
-
runnable- Genera un archivo jar ejecutable para funcionar como una aplicación independiente.
En este caso, estamos interesados en el archivo jar ejecutable.
Al construir IPF Operational Dashboard utilizando el arquetipo, tanto el container como el runnable pueden ser generados en el pom.xml.
Si desea generar el runnable, deberá invocar el perfil runnable de la siguiente manera:
mvn clean install -P runnable
Todos los archivos JAR ejecutables de IPF se producen como Spring Boot aplicaciones. Para ejecutar un IPF Operational Dashboard, usted debe ejecutar lo siguiente:
java -cp ipf-operational-dashboard-docker/target/com.iconsolutions.payments_ipf-operational-dashboard-docker_<version>-runnable.jar:config -Dconfig.override_with_env_vars=true -Dloader.main=com.iconsolutions.ipf.gui. BusinessOperationsMain org.springframework.boot.loader.launch. PropertiesLauncher
[IMPORTANTE]
====
Reemplace el `<version>` placeholder en el comando con su IPF específico Operational Dashboard construir la versión antes de la ejecución.
Por ejemplo, si su versión de compilación es 8. 7. 1, el archivo JAR sería:
ipf-operational-dashboard-docker_8. 7. 1-runnable.jar
Si utiliza Windows, necesitamos reemplazar el primer ":" con un ";", a saber: "com.iconsolutions.payments_ipf-operational-dashboard-docker_<version>-runnable.jar;config"
Si usted está sobrescribiendo alguna de la configuración predeterminada para las aplicaciones, entonces, en este ejemplo, el archivo de configuración para la aplicación,application.conf, viviría en la carpeta de configuración como se define en :config. Luego aprovechamos el Spring Boot_Properties Launcher_ para cargar las propiedades en la aplicación.
=== Información adicional
Para empaquetar el javacript requerido para los módulos IPF que ha elegido servir para su IPF Operational Dashboard Necesitará estas dos secciones de código en el pom de su aplicación web:
<build>
<resources>
<resource>
<directory>${project.build.directory}/static</directory>
<targetPath>static</targetPath>
</resource>
</resources>
</build>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.12.0</version>
<configuration>
<nodeVersion>v20.11.1</nodeVersion>
<npmVersion>10.2.4</npmVersion>
<installDirectory>${project.build.directory}</installDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
</execution>
<execution>
<id>Build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Deberá coincidir esto en su pom de servicio con el requerido java dependencias, ops-gui-service-ng-starter, su aplicación web, commons-codec y los módulos IPF que usted eligió:
<dependencies>
<dependency>
<groupId>com.iconsolutions.ipf.gui</groupId>
<artifactId>ops-gui-service-ng-starter</artifactId>
</dependency>
<dependency>
<groupId>com.iconsolutions.payments</groupId>
<artifactId>iconsolutions</artifactId>
</dependency>
<dependency>
<groupId>com.iconsolutions.ipf.gui</groupId>
<artifactId>ops-gui-service-ng-cluster</artifactId>
</dependency>
<dependency>
<groupId>com.iconsolutions.ipf.gui</groupId>
<artifactId>ops-gui-service-ng-htm</artifactId>
</dependency>
<dependency>
<groupId>com.iconsolutions.ipf.gui</groupId>
<artifactId>ops-gui-service-ng-metrics</artifactId>
</dependency>
<dependency>
<groupId>com.iconsolutions.ipf.gui</groupId>
<artifactId>ops-gui-service-ng-ods</artifactId>
</dependency>
<dependency>
<groupId>com.iconsolutions.ipf.gui</groupId>
<artifactId>ops-gui-service-ng-processing-settings</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</dependencies>
Para crear el archivo jar ejecutable requerido.
== Construcción como Contenedor
Todas las aplicaciones que IPF produce se envían a ipf-lanzamientos en Nexus. Los archivos jar ejecutables estarán disponibles como un artefacto con un sufijo de runnable en el repositorio de Nexus para cada versión de la aplicación que necesite. Por ejemplo, si desea obtener la IPF Operational Dashboard el archivo jar ejecutable para la versión 8. 7. 1 lo encontrará en este enlace:
https://nexus.ipf.iconsolutions.com/repository/ipf-releases/com/iconsolutions/payments/ipf-operational-dashboard-docker/8. 7. 1/ipf-operational-dashboard-docker-8. 7. 1-runnable.jar
El siguiente Dockerfile puede ser utilizado como una plantilla para la integración con su propio contenedor base subyacente.
FROM registry.ipf.iconsolutions.com/ubi8-minimal-openjdk-17:latest
USER root
RUN mkdir -p /ipf-operational-dashboard-service/conf /ipf-operational-dashboard-service/lib
COPY ipf-operational-dashboard-docker-<ipf-dashboard-version>-runnable.jar /ipf-operational-dashboard-service/lib/
COPY cinnamon-agent-<cinnamon-version>.jar /ipf-operational-dashboard-service/lib/
WORKDIR /ipf-operational-dashboard-service
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http://localhost:8080/actuator/health/ | grep UP || exit 1
ENTRYPOINT java \
-javaagent:/ipf-operational-dashboard-service/lib/cinnamon-agent-<cinnamon-version>.jar \
-cp "/ipf-operational-dashboard-service/lib/ipf-operational-dashboard-docker-<ipf-dashboard-version>-runnable.jar:/ipf-operational-dashboard-service/conf" \
$IPF_OPS_GUI_SERVICE_JAVA_ARGS \
-Dconfig.override_with_env_vars=true \
-Dloader.main=com.iconsolutions.ipf.gui.BusinessOperationsMain \
org.springframework.boot.loader.launch.PropertiesLauncher
Al construir el Dockerfile, asegúrese de:
-
Establezca el
<cinnamon-version>para hacer coincidir la versión de cinnamon-agent compatible con su versión de IPF-
Esta versión se puede encontrar en el
${cinnamon.version}propiedad dentro del ipf-bom
-
-
Reemplace
<ipf-dashboard-version>con su IPF objetivo Operational Dashboard versión
Ambos valores deben ser especificados con precisión para garantizar un despliegue y compatibilidad adecuados.
== Kubernetes y OpenShift
El IPF Operational Dashboard opera como una aplicación sin estado y no agrupada, por lo que puede ser fácilmente desplegada utilizando los siguientes manifiestos.
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
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
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>