5 Commits

18 changed files with 753 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: application-config
namespace: urlshortener
labels:
app: urlshortener
data:
# Spring Boot Actuator configuration
management.endpoints.web.exposure.include: "health,info,metrics,prometheus"
management.endpoint.health.probes.enabled: "true"
management.health.livenessState.enabled: "true"
management.health.readinessState.enabled: "true"
# Logging configuration
logging.level.root: "INFO"
logging.level.me.lenajenichen: "DEBUG"
logging.pattern.console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
# Application common settings
spring.application.name: "urlshortener"
# Server configuration
server.error.include-message: "always"
server.error.include-binding-errors: "always"
server.error.include-stacktrace: "on_param"
server.error.include-exception: "false"

View File

@@ -0,0 +1,24 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: database-config
namespace: urlshortener
labels:
app: urlshortener
data:
# PostgreSQL connection settings
# These will be used by services that need database access
# Note: Password should be set via secret: urlshortener-secrets (database-password key)
spring.r2dbc.url: "r2dbc:postgresql://postgres:5432/urlshortener"
spring.r2dbc.username: "urlshortener"
# Password is loaded from secret: urlshortener-secrets.database-password
# Connection pool settings
spring.r2dbc.pool.initial-size: "5"
spring.r2dbc.pool.max-size: "20"
spring.r2dbc.pool.max-idle-time: "30m"
# Database migration (if using Flyway or Liquibase)
spring.flyway.enabled: "true"
spring.flyway.baseline-on-migrate: "true"

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: service-config
namespace: urlshortener
labels:
app: urlshortener
data:
# Service discovery - internal service URLs
shortener.service.url: "http://shortener-service:8083"
auth.service.url: "http://auth-service:8080"
analytics.service.url: "http://analytics-service:8080"
redirect.service.url: "http://redirect-service:8080"
# External service URLs (if needed)
shortener.service.external.url: "https://api.urlshortener.example.com/api/shortener"
redirect.service.external.url: "https://short.urlshortener.example.com"

View File

@@ -0,0 +1,79 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: analytics-service
namespace: urlshortener
labels:
app: analytics-service
component: backend
spec:
replicas: 2
selector:
matchLabels:
app: analytics-service
template:
metadata:
labels:
app: analytics-service
component: backend
spec:
containers:
- name: analytics-service
image: analytics-service:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
protocol: TCP
env:
- name: SERVER_PORT
value: "8080"
- name: SPRING_APPLICATION_NAME
value: analytics-service
- name: SPRING_R2DBC_PASSWORD
valueFrom:
secretKeyRef:
name: urlshortener-secrets
key: database-password
envFrom:
- configMapRef:
name: application-config
- configMapRef:
name: database-config
- configMapRef:
name: service-config
- secretRef:
name: urlshortener-secrets
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
restartPolicy: Always

View File

@@ -0,0 +1,79 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
namespace: urlshortener
labels:
app: auth-service
component: backend
spec:
replicas: 2
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
component: backend
spec:
containers:
- name: auth-service
image: auth-service:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
protocol: TCP
env:
- name: SERVER_PORT
value: "8080"
- name: SPRING_APPLICATION_NAME
value: auth-service
- name: SPRING_R2DBC_PASSWORD
valueFrom:
secretKeyRef:
name: urlshortener-secrets
key: database-password
envFrom:
- configMapRef:
name: application-config
- configMapRef:
name: database-config
- configMapRef:
name: service-config
- secretRef:
name: urlshortener-secrets
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
restartPolicy: Always

View File

@@ -0,0 +1,79 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redirect-service
namespace: urlshortener
labels:
app: redirect-service
component: backend
spec:
replicas: 3
selector:
matchLabels:
app: redirect-service
template:
metadata:
labels:
app: redirect-service
component: backend
spec:
containers:
- name: redirect-service
image: redirect-service:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
protocol: TCP
env:
- name: SERVER_PORT
value: "8080"
- name: SPRING_APPLICATION_NAME
value: redirect-service
- name: SPRING_R2DBC_PASSWORD
valueFrom:
secretKeyRef:
name: urlshortener-secrets
key: database-password
envFrom:
- configMapRef:
name: application-config
- configMapRef:
name: database-config
- configMapRef:
name: service-config
- secretRef:
name: urlshortener-secrets
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
restartPolicy: Always

View File

@@ -0,0 +1,79 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: shortener-service
namespace: urlshortener
labels:
app: shortener-service
component: backend
spec:
replicas: 2
selector:
matchLabels:
app: shortener-service
template:
metadata:
labels:
app: shortener-service
component: backend
spec:
containers:
- name: shortener-service
image: shortener-service:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8083
name: http
protocol: TCP
env:
- name: SERVER_PORT
value: "8083"
- name: SPRING_APPLICATION_NAME
value: shortener-service
- name: SPRING_R2DBC_PASSWORD
valueFrom:
secretKeyRef:
name: urlshortener-secrets
key: database-password
envFrom:
- configMapRef:
name: application-config
- configMapRef:
name: database-config
- configMapRef:
name: service-config
- secretRef:
name: urlshortener-secrets
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8083
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8083
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8083
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
restartPolicy: Always

View File

@@ -0,0 +1,54 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: urlshortener-ingress
namespace: urlshortener
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.urlshortener.example.com
- short.urlshortener.example.com
secretName: urlshortener-tls
rules:
# API routes for shortener and auth services
- host: api.urlshortener.example.com
http:
paths:
- path: /api/shortener
pathType: Prefix
backend:
service:
name: shortener-service
port:
number: 8083
- path: /api/auth
pathType: Prefix
backend:
service:
name: auth-service
port:
number: 8080
- path: /api/analytics
pathType: Prefix
backend:
service:
name: analytics-service
port:
number: 8080
# Redirect service - handles short URL redirects
- host: short.urlshortener.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: redirect-service
port:
number: 8080

View File

@@ -0,0 +1,8 @@
apiVersion: v1
kind: Namespace
metadata:
name: urlshortener
labels:
name: urlshortener
environment: production

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
name: urlshortener-secrets
namespace: urlshortener
labels:
app: urlshortener
type: Opaque
stringData:
# Database credentials
# IMPORTANT: Replace these with actual values or use a secret management system
database-password: "CHANGE_ME"
# JWT secrets for authentication
jwt.secret: "CHANGE_ME_JWT_SECRET_KEY_MIN_32_CHARS"
jwt.expiration: "86400000"

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: analytics-service
namespace: urlshortener
labels:
app: analytics-service
component: backend
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: http
selector:
app: analytics-service
sessionAffinity: None

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: auth-service
namespace: urlshortener
labels:
app: auth-service
component: backend
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: http
selector:
app: auth-service
sessionAffinity: None

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: redirect-service
namespace: urlshortener
labels:
app: redirect-service
component: backend
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: http
selector:
app: redirect-service
sessionAffinity: None

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: shortener-service
namespace: urlshortener
labels:
app: shortener-service
component: backend
spec:
type: ClusterIP
ports:
- port: 8083
targetPort: 8083
protocol: TCP
name: http
selector:
app: shortener-service
sessionAffinity: None

View File

@@ -0,0 +1,53 @@
# Build stage
FROM gradle:8.10.1-jdk21 AS build
WORKDIR /app
# Copy Gradle wrapper and build files for dependency caching
COPY gradle/ gradle/
COPY gradlew gradlew.bat ./
COPY build.gradle settings.gradle ./
# Copy source code
COPY services/analytics-service/build.gradle ./services/analytics-service/
COPY services/analytics-service/src ./services/analytics-service/src/
# Build the application
RUN ./gradlew :services:analytics-service:bootJar --no-daemon
# Runtime stage
FROM eclipse-temurin:21-jre-jammy AS runtime
# Install curl for health checks (minimal size impact)
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring
# Set working directory
WORKDIR /app
# Copy the JAR from build stage
COPY --from=build /app/services/analytics-service/build/libs/*.jar app.jar
# Change ownership to non-root user
RUN chown spring:spring app.jar
# Switch to non-root user
USER spring:spring
# Expose application port (default Spring Boot port, can be overridden via env)
EXPOSE 8080
# Health check for Kubernetes (can be overridden by K8s probes)
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1
# Optimize JVM for containers
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+UseStringDeduplication"
# Run the application
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

View File

@@ -0,0 +1,53 @@
# Build stage
FROM gradle:8.10.1-jdk21 AS build
WORKDIR /app
# Copy Gradle wrapper and build files for dependency caching
COPY gradle/ gradle/
COPY gradlew gradlew.bat ./
COPY build.gradle settings.gradle ./
# Copy source code
COPY services/auth-service/build.gradle ./services/auth-service/
COPY services/auth-service/src ./services/auth-service/src/
# Build the application
RUN ./gradlew :services:auth-service:bootJar --no-daemon
# Runtime stage
FROM eclipse-temurin:21-jre-jammy AS runtime
# Install curl for health checks (minimal size impact)
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring
# Set working directory
WORKDIR /app
# Copy the JAR from build stage
COPY --from=build /app/services/auth-service/build/libs/*.jar app.jar
# Change ownership to non-root user
RUN chown spring:spring app.jar
# Switch to non-root user
USER spring:spring
# Expose application port (default Spring Boot port, can be overridden via env)
EXPOSE 8080
# Health check for Kubernetes (can be overridden by K8s probes)
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1
# Optimize JVM for containers
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+UseStringDeduplication"
# Run the application
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

View File

@@ -0,0 +1,53 @@
# Build stage
FROM gradle:8.10.1-jdk21 AS build
WORKDIR /app
# Copy Gradle wrapper and build files for dependency caching
COPY gradle/ gradle/
COPY gradlew gradlew.bat ./
COPY build.gradle settings.gradle ./
# Copy source code
COPY services/redirect-service/build.gradle ./services/redirect-service/
COPY services/redirect-service/src ./services/redirect-service/src/
# Build the application
RUN ./gradlew :services:redirect-service:bootJar --no-daemon
# Runtime stage
FROM eclipse-temurin:21-jre-jammy AS runtime
# Install curl for health checks (minimal size impact)
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring
# Set working directory
WORKDIR /app
# Copy the JAR from build stage
COPY --from=build /app/services/redirect-service/build/libs/*.jar app.jar
# Change ownership to non-root user
RUN chown spring:spring app.jar
# Switch to non-root user
USER spring:spring
# Expose application port (default Spring Boot port, can be overridden via env)
EXPOSE 8080
# Health check for Kubernetes (can be overridden by K8s probes)
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1
# Optimize JVM for containers
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+UseStringDeduplication"
# Run the application
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

View File

@@ -0,0 +1,54 @@
# Build stage
FROM gradle:8.10.1-jdk21 AS build
WORKDIR /app
# Copy Gradle wrapper and build files for dependency caching
COPY gradle/ gradle/
COPY gradlew gradlew.bat ./
COPY build.gradle settings.gradle ./
# Copy source code
COPY services/shortener-service/build.gradle ./services/shortener-service/
COPY services/shortener-service/src ./services/shortener-service/src/
# Build the application
RUN chmod +x gradlew
RUN ./gradlew :services:shortener-service:bootJar --no-daemon
# Runtime stage
FROM eclipse-temurin:21-jre-jammy AS runtime
# Install curl for health checks (minimal size impact)
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring
# Set working directory
WORKDIR /app
# Copy the JAR from build stage
COPY --from=build /app/services/shortener-service/build/libs/*.jar app.jar
# Change ownership to non-root user
RUN chown spring:spring app.jar
# Switch to non-root user
USER spring:spring
# Expose application port
EXPOSE 8080
# Health check for Kubernetes (can be overridden by K8s probes)
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# Optimize JVM for containers
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 -XX:+UseG1GC"
# Run the application
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]