Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Git
.git
.gitignore

# IDE
.idea
*.iml
.vscode

# Build output
target/

# Kubernetes manifests (not needed in image)
k8s/

# Documentation
*.md
docs/

# CI/CD
.github/

# Local configuration
*.local
.env
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,11 @@ target/
hs_err_pid*
replay_pid*

.idea/
target/

k8s/base/secret.yaml

# Kubernetes secrets and config (env files with real credentials)
k8s/overlays/*/secrets-*.env
k8s/overlays/*/config.env
!k8s/overlays/*/secrets-*.env.example
!k8s/overlays/*/config.env.example
65 changes: 65 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Build stage
FROM maven:3.8-eclipse-temurin-8 AS build
WORKDIR /app

# Build arguments for Maven filtering (with defaults for Docker build)
ARG SERVER_IP=localhost
ARG POSTGRES_PORT=5432
ARG ADMIN_USER=admin
ARG ADMIN_PASSWORD=admin
ARG DDL_BEHAVIOUR=update
ARG FTP_PROXY_HOST=
ARG FTP_PROXY_PORT=

# Set as environment variables for Maven
ENV SERVER_IP=${SERVER_IP} \
POSTGRES_PORT=${POSTGRES_PORT} \
ADMIN_USER=${ADMIN_USER} \
ADMIN_PASSWORD=${ADMIN_PASSWORD} \
DDL_BEHAVIOUR=${DDL_BEHAVIOUR} \
FTP_PROXY_HOST=${FTP_PROXY_HOST} \
FTP_PROXY_PORT=${FTP_PROXY_PORT}

# Copy pom.xml first to leverage Docker cache for dependencies
COPY pom.xml .
RUN mvn dependency:go-offline -B

# Copy source code and build
COPY src ./src
RUN mvn clean package -DskipTests -Pseqcol -Dmaven.gitcommitid.skip=true

# Runtime stage
FROM eclipse-temurin:8-jre
WORKDIR /app

# Install curl for health checks
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*

# Create non-root user for security
RUN groupadd -r seqcol && useradd -r -g seqcol seqcol

# Create tmp directory for file downloads
RUN mkdir -p /tmp && chown seqcol:seqcol /tmp

# Copy the WAR file from build stage
COPY --from=build /app/target/*.war app.war

# Copy service-info.json
COPY --from=build /app/src/main/resources/static/service-info.json /app/service-info.json

# Set ownership
RUN chown -R seqcol:seqcol /app

USER seqcol

EXPOSE 8081

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8081/eva/webservices/seqcol/health || exit 1

# JVM options for containerized environments
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC"

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.war"]
46 changes: 46 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
services:
eva-seqcol:
build:
context: .
dockerfile: Dockerfile
ports:
- "8081:8081"
environment:
# Spring Boot runtime overrides (property names with dots become underscores)
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/seqcol_db
- SPRING_DATASOURCE_USERNAME=seqcol_user
- SPRING_DATASOURCE_PASSWORD=seqcol_password
- SPRING_JPA_PROPERTIES_HIBERNATE_DEFAULT_SCHEMA=seqcol
- CONTROLLER_AUTH_ADMIN_USERNAME=admin
- CONTROLLER_AUTH_ADMIN_PASSWORD=admin123
- SPRING_JPA_HIBERNATE_DDL_AUTO=update
- SERVICE_INFO_FILE_PATH=/app/service-info.json
- JAVA_OPTS=-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8081/eva/webservices/seqcol/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s

postgres:
image: postgres:14
ports:
- "5432:5432"
environment:
- POSTGRES_USER=seqcol_user
- POSTGRES_PASSWORD=seqcol_password
- POSTGRES_DB=seqcol_db
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U seqcol_user -d seqcol_db"]
interval: 10s
timeout: 5s
retries: 5

volumes:
postgres_data:
23 changes: 23 additions & 0 deletions k8s/base/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: eva-seqcol-config
labels:
app: eva-seqcol
data:
# Database connection URL - override per environment
SPRING_DATASOURCE_URL: "jdbc:postgresql://localhost:5432/seqcol_db"

# Database DDL behaviour - use 'validate' in production
# Options: validate, update, create, create-drop, none
DDL_BEHAVIOUR: "validate"

# Default schema used to store seqcol tables
SPRING_JPA_PROPERTIES_HIBERNATE_DEFAULT_SCHEMA: "seqcol"

# FTP proxy settings (set to actual values if behind proxy)
FTP_PROXY_HOST: "null"
FTP_PROXY_PORT: "0"

# Enable scaffold processing
SCAFFOLDS_ENABLED: "true"
130 changes: 130 additions & 0 deletions k8s/base/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: eva-seqcol
labels:
app: eva-seqcol
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: eva-seqcol
template:
metadata:
labels:
app: eva-seqcol
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000

containers:
- name: eva-seqcol
image: ebivariation/eva-seqcol:v0.0.1
imagePullPolicy: Always
ports:
- containerPort: 8081
protocol: TCP

env:
# Database configuration - Spring Boot runtime overrides
- name: SPRING_DATASOURCE_URL
valueFrom:
configMapKeyRef:
name: eva-seqcol-config
key: SPRING_DATASOURCE_URL
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: eva-seqcol-db-credentials
key: username
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: eva-seqcol-db-credentials
key: password

# Admin credentials - Spring Boot runtime overrides
- name: CONTROLLER_AUTH_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: eva-seqcol-admin-credentials
key: username
- name: CONTROLLER_AUTH_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: eva-seqcol-admin-credentials
key: password

# Application configuration (from configmap)
- name: SPRING_JPA_HIBERNATE_DDL_AUTO
valueFrom:
configMapKeyRef:
name: eva-seqcol-config
key: DDL_BEHAVIOUR

# JVM tuning for containers
- name: JAVA_OPTS
value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC"

# Database schema
- name: SPRING_JPA_PROPERTIES_HIBERNATE_DEFAULT_SCHEMA
valueFrom:
configMapKeyRef:
name: eva-seqcol-config
key: SPRING_JPA_PROPERTIES_HIBERNATE_DEFAULT_SCHEMA

# Service info file path
- name: SERVICE_INFO_FILE_PATH
value: "/app/service-info.json"

resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"

livenessProbe:
httpGet:
path: /eva/webservices/seqcol/health
port: 8081
initialDelaySeconds: 90
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3

readinessProbe:
httpGet:
path: /eva/webservices/seqcol/health
port: 8081
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3

volumeMounts:
- name: tmp-volume
mountPath: /tmp

volumes:
- name: tmp-volume
emptyDir: {}

# Spread pods across nodes for high availability
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: eva-seqcol
topologyKey: kubernetes.io/hostname
32 changes: 32 additions & 0 deletions k8s/base/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: eva-seqcol
labels:
app: eva-seqcol
annotations:
# Adjust these annotations based on your ingress controller
kubernetes.io/ingress.class: nginx
# TLS with cert-manager (optional)
# cert-manager.io/cluster-issuer: letsencrypt-prod
# Nginx-specific settings
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
# Uncomment for TLS
# tls:
# - hosts:
# - seqcol.example.com
# secretName: eva-seqcol-tls
rules:
- host: seqcol.example.com # Replace with your actual hostname
http:
paths:
- path: /eva/webservices/seqcol
pathType: Prefix
backend:
service:
name: eva-seqcol
port:
number: 8081
12 changes: 12 additions & 0 deletions k8s/base/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

commonLabels:
app.kubernetes.io/name: eva-seqcol
app.kubernetes.io/component: api

resources:
- deployment.yaml
- service.yaml
- configmap.yaml
- ingress.yaml
43 changes: 43 additions & 0 deletions k8s/base/secret.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# IMPORTANT: This is a template file. Do NOT commit actual credentials to version control.
#
# For production, use one of these approaches:
# 1. External Secrets Operator with AWS Secrets Manager, HashiCorp Vault, etc.
# 2. Sealed Secrets (Bitnami)
# 3. kubectl create secret (manual)
#
# Example manual creation:
# kubectl create secret generic eva-seqcol-db-credentials \
# --from-literal=host=your-db-host.example.com \
# --from-literal=port=5432 \
# --from-literal=username=seqcol_user \
# --from-literal=password=your-secure-password
#
# kubectl create secret generic eva-seqcol-admin-credentials \
# --from-literal=username=admin \
# --from-literal=password=your-admin-password

---
apiVersion: v1
kind: Secret
metadata:
name: eva-seqcol-db-credentials
labels:
app: eva-seqcol
type: Opaque
stringData:
host: "your-postgresql-host.example.com"
port: "5432"
username: "seqcol_user"
password: "REPLACE_WITH_ACTUAL_PASSWORD"

---
apiVersion: v1
kind: Secret
metadata:
name: eva-seqcol-admin-credentials
labels:
app: eva-seqcol
type: Opaque
stringData:
username: "admin"
password: "REPLACE_WITH_ACTUAL_PASSWORD"
Loading