Skip to content

Commit

Permalink
Merge pull request #1 from jahpola/working-kube
Browse files Browse the repository at this point in the history
Lots of changes
  • Loading branch information
jahpola authored Dec 4, 2024
2 parents dddb2e7 + 788659f commit 10615a1
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 42 deletions.
211 changes: 201 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,205 @@
# spring-portti
# Spring Cloud Gateway Application with Kubernetes Integration

Spring Cloud Gateway example using kubernetes discovery via service labels
This project is a Spring Cloud Gateway application named "Portti" designed for seamless integration with Kubernetes environments. It provides a robust API gateway solution with service discovery, load balancing, and observability features.

## installation
1. uses gradlew
2. install some local kubernetes
3. run skaffold dev
Portti is built using Spring Boot and Spring Cloud, leveraging the power of Spring Cloud Gateway for routing and Spring Cloud Kubernetes for native Kubernetes integration. The application is containerized and can be easily deployed to Kubernetes clusters using provided manifests and Skaffold configurations.

## TODO
Key features of this project include:
- Dynamic service discovery in Kubernetes environments
- Configurable routing and load balancing
- Integration with OpenTelemetry for distributed tracing
- Health probes for Kubernetes readiness and liveness checks
- Graceful shutdown support
- Prometheus metrics exposure
- Customizable deployment options for local development and production environments

### Neat commands
show gateway routes:
```curl localhost:9000/actuator/gateway/routes```
The project is structured to support different deployment scenarios, including local development using Rancher Desktop and deployment to AWS-based Kubernetes clusters. It includes Kustomize configurations for managing environment-specific settings and an AWS Application Load Balancer (ALB) Ingress setup for production deployments.

## Repository Structure

```
.
├── build.gradle # Gradle build configuration
├── gradlew.bat # Gradle wrapper for Windows
├── manifests/ # Kubernetes manifests
│ ├── base/ # Base Kubernetes configurations
│ └── overlays/ # Environment-specific overlays
│ ├── dev/ # Development environment config
│ └── local/ # Local development config
├── skaffold.yaml # Skaffold configuration for CI/CD
├── src/
│ ├── main/
│ │ ├── java/ # Java source code
│ │ └── resources/ # Application properties and configs
│ └── test/ # Test source code
└── README.md # This file
```

## Usage Instructions

### Prerequisites

- Java Development Kit (JDK) 21
- Gradle 8.x (included via wrapper)
- Docker
- Kubernetes cluster (local or remote)
- Skaffold

### Installation

1. Clone the repository:
```
git clone <repository-url>
cd spring-portti
```

2. Build the project:
```
./gradlew build
```

3. Build and push the Docker image (if not using Skaffold):
```
./gradlew jib
```

### Running the Application

#### Local Development

For local development using Rancher Desktop:

```
skaffold dev --profile=local
```

This command will build the application, deploy it to your local Kubernetes cluster, and set up port forwarding.

#### Deployment to Development Environment

To deploy to a development Kubernetes cluster:

1. Ensure your kubectl context is set to the correct cluster.
2. Apply the Kubernetes manifests:
```
kubectl apply -k manifests/overlays/dev
```

### Configuration

The application can be configured using the following files:

- `src/main/resources/application.yaml`: Main application configuration
- `src/main/resources/application-kubernetes.yaml`: Kubernetes-specific configuration

Key configuration options:

- `spring.application.name`: Set to "portti"
- `management.endpoints.web.exposure.include`: Exposes all actuator endpoints
- `management.tracing.sampling.probability`: Set to 1.0 for full tracing
- `management.otlp.tracing.endpoint`: Configure the OpenTelemetry endpoint

### Testing

Run the tests using:

```
./gradlew test
```

### Troubleshooting

1. If the application fails to start in Kubernetes:
- Check the pod logs: `kubectl logs -l app=portti`
- Verify the readiness and liveness probe endpoints are accessible

2. For networking issues:
- Ensure the Kubernetes service is correctly configured
- Check the Ingress resource configuration in `manifests/overlays/dev/ingress.yaml`

3. To enable debug logging:
- Uncomment the logging configuration in `src/main/resources/application.yaml`
- Redeploy the application

## Data Flow

The request data flow through the Portti application is as follows:

1. External request reaches the Kubernetes Ingress (ALB in AWS environments)
2. Request is forwarded to the Portti service
3. Spring Cloud Gateway receives the request
4. Gateway routes the request based on configured rules
5. If enabled, service discovery locates the target service
6. Request is forwarded to the appropriate backend service
7. Response follows the reverse path back to the client

```
[Client] <-> [Ingress] <-> [Portti Service] <-> [Spring Cloud Gateway] <-> [Backend Services]
```

Note: The gateway integrates with Kubernetes service discovery to dynamically route requests to available services.

## Deployment

### Prerequisites

- Access to a Kubernetes cluster
- `kubectl` configured to access the cluster
- Docker registry access (e.g., AWS ECR)

### Deployment Steps

1. Build and push the Docker image:
```
./gradlew jib
```

2. Update the image reference in `manifests/base/deployment.yaml` if necessary.

3. Apply the Kubernetes manifests:
```
kubectl apply -k manifests/overlays/dev
```

4. Verify the deployment:
```
kubectl get pods -l app=portti
```

### Environment Configurations

- Local: Uses `manifests/overlays/local` with Ingress configured for local access
- Development: Uses `manifests/overlays/dev` with ALB Ingress for AWS environments

## Infrastructure

The application uses the following key infrastructure components:

- Kubernetes Deployment:
- Type: `apps/v1/Deployment`
- Name: `portti`
- Runs the Portti application container
- Configures readiness and liveness probes

- Kubernetes Service:
- Type: `v1/Service`
- Name: `portti`
- Exposes the Portti application on port 8080

- Kubernetes Ingress (Development):
- Type: `networking.k8s.io/v1/Ingress`
- Name: `portti`
- Configures ALB for internet-facing access
- Restricts inbound traffic to specific CIDR

- Kubernetes ConfigMap:
- Type: `v1/ConfigMap`
- Name: `portti`
- Stores configuration data for the application

- Kubernetes Role and RoleBinding:
- Grants necessary permissions for the Portti service account

- Kubernetes ServiceAccount:
- Name: `portti`
- Used by the Portti deployment for Kubernetes API access
29 changes: 20 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
plugins {
id 'org.springframework.boot' version '3.3.3'
id 'io.spring.dependency-management' version '1.1.4'
id 'com.google.cloud.tools.jib' version '3.4.3'
id 'org.springframework.boot' version '3.4.0'
id 'io.spring.dependency-management' version '1.1.6'
//id 'com.google.cloud.tools.jib' version '3.4.2'
id "org.sonarqube" version '5.1.0.4882'
//id 'org.cyclonedx.bom' version '1.10.0'
id 'java'
}

group = 'org.kerminator'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '21'
targetCompatibility = '21'
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}


configurations {
developmentOnly
runtimeClasspath {
Expand All @@ -34,7 +37,7 @@ repositories {
maven { url 'https://repo.spring.io/milestone' }
}

jib {
/* jib {
from {
platforms {
platform {
Expand All @@ -54,19 +57,27 @@ jib {
helper = "ecr-login"
}
}
} */

tasks.named("bootBuildImage") {
imageName = "public.ecr.aws/kerminator/${project.name}"
//environment["BP_JVM_JLINK_ENABLED"] = "true"
environment["BP_JVM_VERSION"] = "21"
//environment["BP_JVM_CDS_ENABLED"] = "true"
//environment["BP_NATIVE_IMAGE"] = "true"
}

ext {
set('springCloudVersion', "2023.0.3")
set('springCloudVersion', "2024.0.0-RC1")
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-fabric8-all'
implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation "io.micrometer:micrometer-tracing-bridge-otel"
implementation "io.opentelemetry:opentelemetry-exporter-otlp"
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.parallel=true
org.gradle.caching=true
#org.gradle.configuration-cache=true
org.gradle.configuration-cache=true
org.gradle.configuration-cache.parallel=true
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
6 changes: 4 additions & 2 deletions manifests/base/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ spec:
serviceAccountName: portti
containers:
- name: portti
image: public.ecr.aws/kerminator/spring-portti:latest
image: public.ecr.aws/kerminator/portti:latest
imagePullPolicy: Never
ports:
- containerPort: 8080
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
Expand Down
18 changes: 18 additions & 0 deletions manifests/overlays/local/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: portti
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: portti.192.168.205.2.sslip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: portti
port:
number: 8080
4 changes: 3 additions & 1 deletion manifests/overlays/local/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
resources:
- ../../base
- ../../base
- ingress.yaml

17 changes: 6 additions & 11 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
pluginManagement {
repositories {
maven { url 'https://repo.spring.io/milestone' }
gradlePluginPortal()
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'org.springframework.boot') {
useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}")
}
}
}
repositories {
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
gradlePluginPortal()
}
}

rootProject.name = 'portti'
6 changes: 5 additions & 1 deletion src/main/resources/application-kubernetes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ spring:
kubernetes:
reload:
enabled: true
discovery:
all-namespaces: false
service-labels:
route: "true"

#management:
#otlp:
# tracing:
# endpoint: http://otel-collector.otel-demo.svc.cluster.local:4318/v1/traces
# endpoint: http://otel-collector.otel-demo.svc.cluster.local:4318/v1/traces
Loading

0 comments on commit 10615a1

Please sign in to comment.