From 9f0c613768da787c314da3fc703b51d7cbbdc52f Mon Sep 17 00:00:00 2001 From: Albert Buluma Date: Tue, 11 Aug 2026 16:06:22 +0300 Subject: [PATCH] add app init files --- apps/base/01-cals-service.yaml | 17 ++++ apps/base/02-cals-deployment.yaml | 60 ++++++++++++++ apps/base/03-db.yaml | 88 +++++++++++++++++++++ apps/base/04-backend.yaml | 59 ++++++++++++++ apps/base/05-frontend.yaml | 45 +++++++++++ apps/base/06-gateway.yaml | 48 +++++++++++ apps/base/07-databasus.yaml | 48 +++++++++++ apps/base/08-ingress.yaml | 32 ++++++++ apps/base/09-pvcs.yaml | 52 ++++++++++++ apps/base/10-configmap.yaml | 31 ++++++++ apps/base/11-nginx.yaml | 36 +++++++++ apps/base/kustomization.yaml | 15 ++++ apps/base/namespace.yaml | 4 + apps/overlays/hexm-0001/kustomization.yaml | 18 +++++ apps/overlays/hexm-0001/secrets.yaml | 92 ++++++++++++++++++++++ clusters/hexm-0001/apps.yaml | 27 +++++++ 16 files changed, 672 insertions(+) create mode 100644 apps/base/01-cals-service.yaml create mode 100644 apps/base/02-cals-deployment.yaml create mode 100644 apps/base/03-db.yaml create mode 100644 apps/base/04-backend.yaml create mode 100644 apps/base/05-frontend.yaml create mode 100644 apps/base/06-gateway.yaml create mode 100644 apps/base/07-databasus.yaml create mode 100644 apps/base/08-ingress.yaml create mode 100644 apps/base/09-pvcs.yaml create mode 100644 apps/base/10-configmap.yaml create mode 100644 apps/base/11-nginx.yaml create mode 100644 apps/base/kustomization.yaml create mode 100644 apps/base/namespace.yaml create mode 100644 apps/overlays/hexm-0001/kustomization.yaml create mode 100644 apps/overlays/hexm-0001/secrets.yaml create mode 100644 clusters/hexm-0001/apps.yaml diff --git a/apps/base/01-cals-service.yaml b/apps/base/01-cals-service.yaml new file mode 100644 index 0000000..4e999c3 --- /dev/null +++ b/apps/base/01-cals-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: cals + namespace: epcare +spec: + selector: + app: cals + ports: + - name: syslog-tcp + protocol: TCP + port: 601 + targetPort: 601 + # ClusterIP (default) — no fixed IP needed anymore. + # Each namespace (epcare-ep01, epcare-ep02...) gets its own cals Service. + # Other pods in the same namespace reach it via: cals:601 + # Cross-namespace: cals.epcare-ep01.svc.cluster.local:601 (not needed here) diff --git a/apps/base/02-cals-deployment.yaml b/apps/base/02-cals-deployment.yaml new file mode 100644 index 0000000..20056b5 --- /dev/null +++ b/apps/base/02-cals-deployment.yaml @@ -0,0 +1,60 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cals + namespace: epcare +spec: + replicas: 1 + selector: + matchLabels: + app: cals + template: + metadata: + labels: + app: cals + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME # Patched per overlay (e.g. ep01) + imagePullSecrets: + - name: gitlab-registry + containers: + - name: cals + image: registry.gitlab.com/signalytic/client-external/client-apps-log-sync/cals/arm64:1.3 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 601 + protocol: TCP + envFrom: + - secretRef: + name: cals-env + - secretRef: + name: global-env + volumeMounts: + - name: cals-logs + mountPath: /var/log/syslog-ng + - name: ssh-key + mountPath: /root/.ssh/id_ed25519 + subPath: id_ed25519 + readOnly: true + readinessProbe: + tcpSocket: + port: 601 + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + livenessProbe: + tcpSocket: + port: 601 + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + volumes: + - name: cals-logs + persistentVolumeClaim: + claimName: cals-logs-pvc + - name: ssh-key + secret: + secretName: cals-ssh-key + defaultMode: 0400 diff --git a/apps/base/03-db.yaml b/apps/base/03-db.yaml new file mode 100644 index 0000000..ff953cf --- /dev/null +++ b/apps/base/03-db.yaml @@ -0,0 +1,88 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: db + namespace: epcare +spec: + selector: + app: db + ports: + - name: mysql + protocol: TCP + port: 3306 + targetPort: 3306 + clusterIP: None # Headless — stable DNS for OpenMRS without load balancing +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: db + namespace: epcare +spec: + replicas: 1 + selector: + matchLabels: + app: db + template: + metadata: + labels: + app: db + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME + # Fix: chown /var/lib/mysql to mysql user (999) at pod start + securityContext: + fsGroup: 999 + initContainers: + - name: fix-permissions + image: busybox + command: ["sh", "-c", "chown -R 999:999 /var/lib/mysql"] + volumeMounts: + - name: db-data + mountPath: /var/lib/mysql + imagePullSecrets: + - name: gitlab-registry + containers: + - name: db + image: registry.gitlab.com/signalytic/client-external/epcare/mysql/arm64:1.0 + imagePullPolicy: IfNotPresent + args: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_general_ci + envFrom: + - secretRef: + name: db-env + ports: + - containerPort: 3306 + readinessProbe: + exec: + command: + - sh + - -c + - 'mysqladmin ping -h localhost -u${MYSQL_USER} -p${MYSQL_PASSWORD}' + initialDelaySeconds: 30 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 20 + volumeMounts: + - name: db-data + mountPath: /var/lib/mysql + - name: mysql-config + mountPath: /etc/mysql/conf.d/mysql.cnf + subPath: mysql.cnf + readOnly: true + - name: db-dumps + mountPath: /docker-entrypoint-initdb.d + readOnly: true + volumes: + - name: db-data + persistentVolumeClaim: + claimName: db-data-pvc + - name: mysql-config + configMap: + name: mysql-config + - name: db-dumps + hostPath: + path: /opt/epcare/resources/db/dumps + type: DirectoryOrCreate diff --git a/apps/base/04-backend.yaml b/apps/base/04-backend.yaml new file mode 100644 index 0000000..e43d2cd --- /dev/null +++ b/apps/base/04-backend.yaml @@ -0,0 +1,59 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: backend + namespace: epcare +spec: + selector: + app: backend + ports: + - name: http + protocol: TCP + port: 8080 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend + namespace: epcare +spec: + replicas: 1 + selector: + matchLabels: + app: backend + template: + metadata: + labels: + app: backend + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME + imagePullSecrets: + - name: gitlab-registry + initContainers: + - name: wait-for-db + image: busybox:1.36 + command: ['sh', '-c', 'until nc -z db 3306; do echo "waiting for db..."; sleep 3; done'] + containers: + - name: backend + image: registry.gitlab.com/signalytic/client-external/epcare/backend/arm64:1.1 + imagePullPolicy: IfNotPresent + envFrom: + - secretRef: + name: backend-env + volumeMounts: + - name: openmrs-data + mountPath: /openmrs/data + - name: openmrs-config + mountPath: /openmrs/distribution/openmrs_config + readOnly: true + volumes: + - name: openmrs-data + persistentVolumeClaim: + claimName: openmrs-data-pvc + - name: openmrs-config + hostPath: + path: /opt/epcare/resources/backend/configuration + type: DirectoryOrCreate diff --git a/apps/base/05-frontend.yaml b/apps/base/05-frontend.yaml new file mode 100644 index 0000000..71b8c07 --- /dev/null +++ b/apps/base/05-frontend.yaml @@ -0,0 +1,45 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend + namespace: epcare +spec: + selector: + app: frontend + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend + namespace: epcare +spec: + replicas: 1 + selector: + matchLabels: + app: frontend + template: + metadata: + labels: + app: frontend + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME + imagePullSecrets: + - name: gitlab-registry + initContainers: + - name: wait-for-backend + image: busybox:1.36 + command: ['sh', '-c', 'until nc -z backend 8080; do echo "waiting for backend..."; sleep 10; done'] + containers: + - name: frontend + image: registry.gitlab.com/signalytic/client-external/epcare/frontend/arm64:1.1 + imagePullPolicy: IfNotPresent + envFrom: + - secretRef: + name: frontend-env diff --git a/apps/base/06-gateway.yaml b/apps/base/06-gateway.yaml new file mode 100644 index 0000000..1e8b73e --- /dev/null +++ b/apps/base/06-gateway.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: gateway + namespace: epcare +spec: + selector: + app: gateway + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gateway + namespace: epcare +spec: + replicas: 1 + selector: + matchLabels: + app: gateway + template: + metadata: + labels: + app: gateway + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME + imagePullSecrets: + - name: gitlab-registry + initContainers: + - name: wait-for-frontend + image: busybox:1.36 + command: ['sh', '-c', 'until nc -z frontend 80; do echo "waiting for frontend..."; sleep 3; done'] + - name: wait-for-backend + image: busybox:1.36 + command: ['sh', '-c', 'until nc -z backend 8080; do echo "waiting for backend..."; sleep 3; done'] + containers: + - name: gateway + image: registry.gitlab.com/signalytic/client-external/epcare/gateway/arm64:1.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 diff --git a/apps/base/07-databasus.yaml b/apps/base/07-databasus.yaml new file mode 100644 index 0000000..79ddac9 --- /dev/null +++ b/apps/base/07-databasus.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: databasus + namespace: epcare +spec: + selector: + app: databasus + ports: + - name: http + protocol: TCP + port: 4005 + targetPort: 4005 + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: databasus + namespace: epcare +spec: + replicas: 1 + selector: + matchLabels: + app: databasus + template: + metadata: + labels: + app: databasus + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME + imagePullSecrets: + - name: gitlab-registry + containers: + - name: databasus + image: registry.gitlab.com/signalytic/client-external/epcare/databasus/arm64:1.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 4005 + volumeMounts: + - name: databasus-data + mountPath: /databasus-data + volumes: + - name: databasus-data + persistentVolumeClaim: + claimName: databasus-data-pvc diff --git a/apps/base/08-ingress.yaml b/apps/base/08-ingress.yaml new file mode 100644 index 0000000..30e3680 --- /dev/null +++ b/apps/base/08-ingress.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: gateway-ingress + namespace: epcare +spec: + entryPoints: + - websecure + routes: + - match: Host(`epcare.signalytic.lan`) + kind: Rule + services: + - name: gateway + port: 80 + tls: {} +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: databasus-ingress + namespace: epcare +spec: + entryPoints: + - websecure + routes: + - match: Host(`epcare-databasus.signalytic.lan`) + kind: Rule + services: + - name: databasus + port: 4005 + tls: {} diff --git a/apps/base/09-pvcs.yaml b/apps/base/09-pvcs.yaml new file mode 100644 index 0000000..96afc2f --- /dev/null +++ b/apps/base/09-pvcs.yaml @@ -0,0 +1,52 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: openmrs-data-pvc + namespace: epcare +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: local-path # k3s built-in provisioner +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: db-data-pvc + namespace: epcare +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: local-path +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: databasus-data-pvc + namespace: epcare +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + storageClassName: local-path +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: cals-logs-pvc + namespace: epcare +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi + storageClassName: local-path diff --git a/apps/base/10-configmap.yaml b/apps/base/10-configmap.yaml new file mode 100644 index 0000000..152a3fc --- /dev/null +++ b/apps/base/10-configmap.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql-config + namespace: epcare +data: + mysql.cnf: | + [mysqld] + # optimise for large db-imports + innodb_redo_log_capacity = 2147483648 + + # Buffer pool - bump this if the host has more RAM available + innodb_buffer_pool_size = 512M + + # Log buffer - good + innodb_log_buffer_size = 256M + + # Flush behavior - 2 is good for import speed, consider switching + # back to 1 in production for ACID compliance + innodb_flush_log_at_trx_commit = 2 + + # WARNING: O_DIRECT can fail on overlay2 (common Docker storage driver) + # Safer default for containers: + innodb_flush_method = fsync + + # Recommended additions for large imports: + innodb_write_io_threads = 8 + innodb_read_io_threads = 8 + bulk_insert_buffer_size = 256M + + sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" diff --git a/apps/base/11-nginx.yaml b/apps/base/11-nginx.yaml new file mode 100644 index 0000000..856e5e9 --- /dev/null +++ b/apps/base/11-nginx.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-demo + namespace: epcare +spec: + replicas: 4 + selector: + matchLabels: + app: nginx-demo + template: + metadata: + labels: + app: nginx-demo + spec: + nodeSelector: + kubernetes.io/hostname: NODE_HOSTNAME + containers: + - name: nginx + image: nginx:stable-alpine + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-demo + namespace: epcare +spec: + selector: + app: nginx-demo + ports: + - port: 80 + targetPort: 80 + type: LoadBalancer \ No newline at end of file diff --git a/apps/base/kustomization.yaml b/apps/base/kustomization.yaml new file mode 100644 index 0000000..d7b3cb2 --- /dev/null +++ b/apps/base/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespace.yaml + - 01-cals-service.yaml + - 02-cals-deployment.yaml + - 03-db.yaml + - 04-backend.yaml + - 05-frontend.yaml + - 06-gateway.yaml + - 07-databasus.yaml + - 08-ingress.yaml + - 09-pvcs.yaml + - 10-configmap.yaml + - 11-nginx.yaml diff --git a/apps/base/namespace.yaml b/apps/base/namespace.yaml new file mode 100644 index 0000000..6e95ef3 --- /dev/null +++ b/apps/base/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: epcare diff --git a/apps/overlays/hexm-0001/kustomization.yaml b/apps/overlays/hexm-0001/kustomization.yaml new file mode 100644 index 0000000..ebbf806 --- /dev/null +++ b/apps/overlays/hexm-0001/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: epcare + +resources: + - ../../base + - secrets.yaml + +patches: + # Pin all Deployments to the single node of this cluster. + # Value must match: kubectl get nodes --show-labels | grep kubernetes.io/hostname + - patch: |- + - op: replace + path: /spec/template/spec/nodeSelector/kubernetes.io~1hostname + value: hexm-0001 + target: + kind: Deployment diff --git a/apps/overlays/hexm-0001/secrets.yaml b/apps/overlays/hexm-0001/secrets.yaml new file mode 100644 index 0000000..f83add7 --- /dev/null +++ b/apps/overlays/hexm-0001/secrets.yaml @@ -0,0 +1,92 @@ +# ============================================================ +# Node-specific secrets for hexm-0001 +# +# These MUST be encrypted with SOPS before committing to Git. +# Encrypt: sops --encrypt --in-place secrets.yaml +# Decrypt: sops --decrypt secrets.yaml | kubectl apply -f - +# +# Alternatively, create imperatively and skip committing this file: +# kubectl create secret generic global-env \ +# --from-literal=SIGNALYTIC_NODE_ID=HEXM0001 \ +# --from-literal=SIGNALYTIC_FACILITY_NAME="Facility Name Here" \ +# -n epcare +# ============================================================ +--- +apiVersion: v1 +kind: Secret +metadata: + name: global-env + namespace: epcare +type: Opaque +stringData: + SIGNALYTIC_NODE_ID: "HEXM0001" + SIGNALYTIC_FACILITY_NAME: "HOPE CLINIC LUKULI" +--- +apiVersion: v1 +kind: Secret +metadata: + name: cals-env + namespace: epcare +type: Opaque +stringData: + CALS_REMOTE_HOST: "REPLACE_WITH_REMOTE_HOST" + CALS_REMOTE_PATH: /home/cals/logs/HEXM0001 + CALS_REMOTE_USER: cals + CALS_SSH_KEY_PATH: /root/.ssh/id_ed25519 + CALS_TZ: UTC + CALS_SCHEDULE: "*/2 * * * *" + CALS_OPTIONS: -avz --delete + CALS_RETENTION_DAYS: "30" +--- +apiVersion: v1 +kind: Secret +metadata: + name: cals-ssh-key + namespace: epcare +type: Opaque +stringData: + id_ed25519: | + -----BEGIN OPENSSH PRIVATE KEY----- + REPLACE_WITH_ACTUAL_PRIVATE_KEY_FOR_HEXM0001 + -----END OPENSSH PRIVATE KEY----- +--- +apiVersion: v1 +kind: Secret +metadata: + name: db-env + namespace: epcare +type: Opaque +stringData: + OMRS_DB_USER: "openmrs" + OMRS_DB_PASSWORD: "password" + MYSQL_DATABASE: openmrs + MYSQL_USER: openmrs + MYSQL_PASSWORD: "password" + MYSQL_ROOT_PASSWORD: "password" +--- +apiVersion: v1 +kind: Secret +metadata: + name: backend-env + namespace: epcare +type: Opaque +stringData: + OMRS_CONFIG_MODULE_WEB_ADMIN: "true" + OMRS_CONFIG_AUTO_UPDATE_DATABASE: "true" + OMRS_CONFIG_CREATE_TABLES: "false" + OMRS_CONFIG_CONNECTION_SERVER: db + OMRS_CONFIG_CONNECTION_DATABASE: openmrs + OMRS_CONFIG_CONNECTION_USERNAME: openmrs + OMRS_CONFIG_CONNECTION_PASSWORD: "openmrs" +--- +apiVersion: v1 +kind: Secret +metadata: + name: frontend-env + namespace: epcare +type: Opaque +stringData: + SPA_PATH: /openmrs/spa + API_URL: /openmrs + SPA_CONFIG_URLS: /openmrs/spa/config-core_demo.json + SPA_DEFAULT_LOCALE: "" diff --git a/clusters/hexm-0001/apps.yaml b/clusters/hexm-0001/apps.yaml new file mode 100644 index 0000000..69ce17a --- /dev/null +++ b/clusters/hexm-0001/apps.yaml @@ -0,0 +1,27 @@ +# Flux CD Kustomization for hexm-0001 +# Flux reads this file after bootstrap and reconciles the epcare app overlay. +# +# Bootstrap this cluster (run once per cluster): +# flux bootstrap gitlab \ +# --owner= \ +# --repository=epcare-k8s-files \ +# --branch=main \ +# --path=clusters/hexm-0001 \ +# --token-auth +# +# After bootstrap, Flux will automatically sync any Kustomization resources +# found under clusters/hexm-0001/ — including this file. +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: epcare + namespace: flux-system +spec: + interval: 10m + path: ./apps/overlays/hexm-0001 + prune: true + sourceRef: + kind: GitRepository + name: flux-system # Created automatically by flux bootstrap + wait: true + timeout: 5m