feat: add module3-6
This commit is contained in:
parent
6d6273a33b
commit
a4b002dc61
95 changed files with 1848 additions and 1 deletions
|
|
@ -1 +1,3 @@
|
|||
# STACKIT University Course
|
||||
|
||||
This repository accompanies the course `Deploying an Application with STACKIT Kubernetes Engine`.
|
||||
|
|
|
|||
1
module3/.gitignore
vendored
Normal file
1
module3/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
jwt.key
|
||||
91
module3/README.md
Normal file
91
module3/README.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Scrumlr K8s Deployment
|
||||
|
||||
## Generate JWT Key
|
||||
|
||||
```bash
|
||||
openssl ecparam -genkey -name secp521r1 -noout -out $(git rev-parse --show-toplevel)/module3/k8s/kustomize/scrumlr/jwt.key
|
||||
```
|
||||
|
||||
## Differences in deployment between local minikube cluster and SKE cluster
|
||||
|
||||
* Skip PostgreSQL Deployment for SKE deployment, since we use external PostgreSQL
|
||||
* Set the right `SCRUMLR_SERVER_DATABASE_URL` value in the scrumlr-backend secret
|
||||
* Minikube: Ingress without Host and external-dns/ cert-manager annotations
|
||||
* SKE: Ingress with Host and external-dns/ cert-manager annotations
|
||||
|
||||
## Deploy Scrumlr into local cluster using minikube
|
||||
|
||||
Local Deployment that can run on a lokal K8s cluster like a [minikube](https://minikube.sigs.k8s.io/docs/) cluster.
|
||||
As we don't have Postgres as an external service here, we also install Postgres directly in our local cluster.
|
||||
|
||||
```sh
|
||||
cd $(git rev-parse --show-toplevel)/module3/k8s/
|
||||
|
||||
# Create Cluster with kind and start cloud-provider-kind for Ingress
|
||||
minikube start
|
||||
minikube tunnel
|
||||
|
||||
# Install ingress-nginx and nats with helm
|
||||
|
||||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
|
||||
helm repo add nats https://nats-io.github.io/k8s/helm/charts/
|
||||
helm repo update
|
||||
helm upgrade \
|
||||
ingress-nginx ingress-nginx/ingress-nginx \
|
||||
--install \
|
||||
--namespace ingress-nginx \
|
||||
--create-namespace \
|
||||
--version 4.12.1 \
|
||||
--values helm/ingress-nginx/values.yaml
|
||||
helm upgrade \
|
||||
--install \
|
||||
nats nats/nats \
|
||||
--namespace nats \
|
||||
--create-namespace \
|
||||
--version 1.3.3 \
|
||||
--values helm/nats/values.yaml
|
||||
|
||||
|
||||
# Install kustomize applications
|
||||
|
||||
kubectl apply -k kustomize/postgres
|
||||
kubectl apply -k kustomize/scrumlr
|
||||
|
||||
# Use external IP of your Ingress Controller in your Browser to open Scrumlr
|
||||
kubectl get services --namespace ingress-nginx ingress-nginx-controller --output jsonpath='{.status.loadBalancer.ingress[0].ip}'
|
||||
|
||||
# Destroy Cluster
|
||||
minikube stop
|
||||
minikube delete
|
||||
```
|
||||
|
||||
```shell
|
||||
# Optional: Connect to your PostgreSQL instance with psql (psql must be installed)
|
||||
kubectl port-forward -n postgres services/postgres-scrumlr
|
||||
psql -h localhost -U scrumlr -d scrumlr
|
||||
# List all tables
|
||||
scrumlr-# \dt
|
||||
List of relations
|
||||
Schema | Name | Type | Owner
|
||||
--------+------------------------+-------+---------
|
||||
public | apple_users | table | scrumlr
|
||||
public | azure_ad_users | table | scrumlr
|
||||
public | board_session_requests | table | scrumlr
|
||||
public | board_sessions | table | scrumlr
|
||||
public | board_templates | table | scrumlr
|
||||
public | boards | table | scrumlr
|
||||
public | column_templates | table | scrumlr
|
||||
public | columns | table | scrumlr
|
||||
public | deleted_boards | table | scrumlr
|
||||
public | github_users | table | scrumlr
|
||||
public | google_users | table | scrumlr
|
||||
public | microsoft_users | table | scrumlr
|
||||
public | notes | table | scrumlr
|
||||
public | oidc_users | table | scrumlr
|
||||
public | reactions | table | scrumlr
|
||||
public | schema_migrations | table | scrumlr
|
||||
public | users | table | scrumlr
|
||||
public | votes | table | scrumlr
|
||||
public | votings | table | scrumlr
|
||||
(19 rows)
|
||||
```
|
||||
15
module3/k8s/helm/ingress-nginx/README.md
Normal file
15
module3/k8s/helm/ingress-nginx/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# ingress-nginx
|
||||
|
||||
Docs: https://kubernetes.github.io/ingress-nginx/
|
||||
Github Repo: https://github.com/kubernetes/ingress-nginx
|
||||
|
||||
```sh
|
||||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx --force-update
|
||||
helm upgrade \
|
||||
ingress-nginx ingress-nginx/ingress-nginx \
|
||||
--install \
|
||||
--namespace ingress-nginx \
|
||||
--create-namespace \
|
||||
--version 4.12.1 \
|
||||
--values values.yaml
|
||||
```
|
||||
1
module3/k8s/helm/ingress-nginx/values.yaml
Normal file
1
module3/k8s/helm/ingress-nginx/values.yaml
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Note: This file is intentionally empty and is more of a placeholder to ensure consistency.
|
||||
15
module3/k8s/helm/nats/README.md
Normal file
15
module3/k8s/helm/nats/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# NATS
|
||||
|
||||
Docs: https://docs.nats.io/
|
||||
Github Repo: https://github.com/nats-io/k8s
|
||||
|
||||
```sh
|
||||
helm repo add nats https://nats-io.github.io/k8s/helm/charts/ --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
nats nats/nats \
|
||||
--namespace nats \
|
||||
--create-namespace \
|
||||
--version 1.3.3 \
|
||||
--values values.yaml
|
||||
```
|
||||
4
module3/k8s/helm/nats/values.yaml
Normal file
4
module3/k8s/helm/nats/values.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
config:
|
||||
cluster:
|
||||
enabled: true
|
||||
replicas: 3
|
||||
8
module3/k8s/kustomize/postgres/kustomization.yaml
Normal file
8
module3/k8s/kustomize/postgres/kustomization.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace-postgres.yaml
|
||||
- statefulset-postgres-scrumlr.yaml
|
||||
- service-postgres-scrumlr.yaml
|
||||
- persistentvolumeclaim-postgres-scrumlr.yaml
|
||||
- secret-postgres-scrumlr.yaml
|
||||
4
module3/k8s/kustomize/postgres/namespace-postgres.yaml
Normal file
4
module3/k8s/kustomize/postgres/namespace-postgres.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: postgres
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgres-scrumlr
|
||||
namespace: postgres
|
||||
labels:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 8Gi
|
||||
14
module3/k8s/kustomize/postgres/secret-postgres-scrumlr.yaml
Normal file
14
module3/k8s/kustomize/postgres/secret-postgres-scrumlr.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: postgres-scrumlr
|
||||
namespace: postgres
|
||||
labels:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
type: Opaque
|
||||
data:
|
||||
# echo -n 'super-secret-password' | base64
|
||||
POSTGRES_USER: c2NydW1scg== # base64 encoded value of 'scrumlr'
|
||||
POSTGRES_PASSWORD: c3VwZXItc2VjcmV0LXBhc3N3b3Jk # base64 encoded value of 'super-secret-password'
|
||||
18
module3/k8s/kustomize/postgres/service-postgres-scrumlr.yaml
Normal file
18
module3/k8s/kustomize/postgres/service-postgres-scrumlr.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres-scrumlr
|
||||
namespace: postgres
|
||||
labels:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
selector:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: postgres-scrumlr
|
||||
namespace: postgres
|
||||
labels:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
serviceName: "postgres-scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "postgres"
|
||||
app.kubernetes.io/component: "database"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:17.4
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "20m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-scrumlr
|
||||
key: POSTGRES_USER
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-scrumlr
|
||||
key: POSTGRES_PASSWORD
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
volumeMounts:
|
||||
- name: postgres
|
||||
mountPath: /var/lib/postgresql/data
|
||||
subPath: postgres
|
||||
volumes:
|
||||
- name: postgres
|
||||
persistentVolumeClaim:
|
||||
claimName: postgres-scrumlr
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
data:
|
||||
SCRUMLR_SERVER_PORT: "8080"
|
||||
SCRUMLR_BASE_PATH: "/api"
|
||||
SCRUMLR_SERVER_NATS_URL: "nats.nats.svc.cluster.local:4222"
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: ghcr.io/inovex/scrumlr.io/scrumlr-server:3.10.3
|
||||
args:
|
||||
- "/app/main"
|
||||
- "-disable-check-origin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "200Mi"
|
||||
limits:
|
||||
memory: "200Mi"
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
failureThreshold: 30
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: scrumlr-backend
|
||||
- secretRef:
|
||||
name: scrumlr-backend
|
||||
env:
|
||||
- name: SCRUMLR_PRIVATE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: scrumlr-ecdsa-key
|
||||
key: jwt.key
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
8
module3/k8s/kustomize/scrumlr/backend/kustomization.yaml
Normal file
8
module3/k8s/kustomize/scrumlr/backend/kustomization.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- configmap-scrumlr-backend.yaml
|
||||
- secret-scrumlr-backend.yaml
|
||||
- deployment-scrumlr-backend.yaml
|
||||
- service-scrumlr-backend.yaml
|
||||
- poddisruptionbudget-backend.yaml
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: backend
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
type: Opaque
|
||||
data:
|
||||
SCRUMLR_SERVER_DATABASE_URL: "cG9zdGdyZXM6Ly9zY3J1bWxyOnN1cGVyLXNlY3JldC1wYXNzd29yZEBwb3N0Z3Jlcy1zY3J1bWxyLnBvc3RncmVzLnN2Yy5jbHVzdGVyLmxvY2FsOjU0MzIvc2NydW1scj9zc2xtb2RlPWRpc2FibGU=" # echo -n 'postgres://scrumlr:super-secret-password@postgres-scrumlr.postgres.svc.cluster.local:5432/scrumlr?sslmode=disable' | base64
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
data:
|
||||
SCRUMLR_SERVER_URL: "/api"
|
||||
SCRUMLR_SERVER_PORT: "8080"
|
||||
SCRUMLR_SHOW_LEGAL_DOCUMENTS: "true"
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: ghcr.io/inovex/scrumlr.io/scrumlr-frontend:3.10.3
|
||||
resources:
|
||||
requests:
|
||||
cpu: "25m"
|
||||
memory: "100Mi"
|
||||
limits:
|
||||
memory: "100Mi"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: scrumlr-frontend
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- configmap-scrumlr-frontend.yaml
|
||||
- deployment-scrumlr-frontend.yaml
|
||||
- service-scrumlr-frontend.yaml
|
||||
- poddisruptionbudget-frontend.yaml
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: frontend
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
31
module3/k8s/kustomize/scrumlr/ingress-scrumlr.yaml
Normal file
31
module3/k8s/kustomize/scrumlr/ingress-scrumlr.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: scrumlr
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/limit-connections: "100"
|
||||
# Websocket optimization https://kubernetes.github.io/ingress-nginx/user-guide/miscellaneous/#websockets
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "7200"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "7200"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: scrumlr-backend
|
||||
port:
|
||||
number: 8080
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: scrumlr-frontend
|
||||
port:
|
||||
number: 80
|
||||
14
module3/k8s/kustomize/scrumlr/kustomization.yaml
Normal file
14
module3/k8s/kustomize/scrumlr/kustomization.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: scrumlr
|
||||
resources:
|
||||
- namespace-scrumlr.yaml
|
||||
- backend/
|
||||
- frontend/
|
||||
- ingress-scrumlr.yaml
|
||||
|
||||
# Create key: openssl ecparam -genkey -name secp521r1 -noout -out jwt.key
|
||||
secretGenerator:
|
||||
- name: scrumlr-ecdsa-key
|
||||
files:
|
||||
- jwt.key
|
||||
4
module3/k8s/kustomize/scrumlr/namespace-scrumlr.yaml
Normal file
4
module3/k8s/kustomize/scrumlr/namespace-scrumlr.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: scrumlr
|
||||
13
module4/.gitignore
vendored
Normal file
13
module4/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Secrets
|
||||
sa_key.json
|
||||
config.s3.tfbackend
|
||||
|
||||
# Var file
|
||||
terraform.tfvars
|
||||
25
module4/tf/.terraform.lock.hcl
Normal file
25
module4/tf/.terraform.lock.hcl
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/stackitcloud/stackit" {
|
||||
version = "0.50.0"
|
||||
constraints = "0.50.0"
|
||||
hashes = [
|
||||
"h1:uU8/DLvW8tEty0PI2sUMem43IDNSrncHuLaXaEYdGFk=",
|
||||
"zh:0dde99e7b343fa01f8eefc378171fb8621bedb20f59157d6cc8e3d46c738105f",
|
||||
"zh:219d678bc471b3f5030724dcdde6be3f3fa63e911b7c7a0f446b0a0b4e5f48e7",
|
||||
"zh:4cd09155a09e320b0b68db4ba2971564f3d147c19ad991d6b7e731e26034d91c",
|
||||
"zh:507c1e24432f0d455ac8b628c37ee20db62e89a6e85508568c2820ba52404786",
|
||||
"zh:5aa10bfc4baad277a2bc746c83fca19911bb95a5e8821dd46f333bc621cbb453",
|
||||
"zh:67b55ad1135ca12997b0928cb67973b11ea196299e0cf66e3e0145faec762644",
|
||||
"zh:6d1d108edcd6794a8839d849e6ea48699875e22afeea7edd38bee3dd56dea7e8",
|
||||
"zh:7473c28b3781c0d00294d985bd067e753a419ca8e379f91a8f6f2ce4663566ee",
|
||||
"zh:8d234b24734f950f986322a5f084ca23bfd9b3d9fb7742b54404171cfcabc99e",
|
||||
"zh:af0804ea918648600cc6300dffce8a7b9115d30dc88db10f962b8e596d1465e1",
|
||||
"zh:b557940dc6387dc4cce8b100981ccaadac6bc4e6b50c566baf148d67939f8f2e",
|
||||
"zh:d477f77ce6f807d60069c1efcfa20607088ae7ab91d22805331a7634d84c2d1c",
|
||||
"zh:d95086e2338ceed511e798a2acc6d5cefdfff1a14f7b47b2d29b4ebc36b77a3a",
|
||||
"zh:ea0d8d5c9cf7d5871a54dd4786c378dfd9d10416f3c4d0ea4776465e8c562e10",
|
||||
"zh:f96af7b89dc99745f6a22c0ca2aedb18e10273251b8cbac9e2b1011c68c3c3f9",
|
||||
]
|
||||
}
|
||||
37
module4/tf/README.md
Normal file
37
module4/tf/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Terraform
|
||||
|
||||
## Create service account
|
||||
|
||||
* Install CLI tool: https://github.com/stackitcloud/stackit-cli/blob/main/INSTALLATION.md
|
||||
* `stackit auth login`
|
||||
* `stackit project list`
|
||||
* `stackit config set --project-id PROJECTx-IDyy-zzzz-aaaa-DUMMYbbbbbbb`
|
||||
* `stackit service-account create --name terraform`
|
||||
* `stackit service-account list`
|
||||
|
||||
* `stackit service-account key create --email terraform-vsPzcS7@sa.stackit.cloud > sa_key.json`
|
||||
* `stackit service-account key list --email terraform-vsPzcS7@sa.stackit.cloud`
|
||||
|
||||
* `stackit project member add terraform-vsPzcS7@sa.stackit.cloud --role editor`
|
||||
|
||||
## S3 Backend for tfstate
|
||||
|
||||
`stackit object-storage enable`
|
||||
|
||||
Note: The name must be globally unique. Use only lowercase letters, numbers or hyphens. The name should be at least 3 and at most 63 characters long.
|
||||
|
||||
Lets use something kinda random, to have a higher chance of catching an free bucket name:
|
||||
|
||||
* `stackit object-storage bucket create tfstate-bucket-g5el`
|
||||
* `stackit object-storage credentials-group create --name terraform-state`
|
||||
* `stackit object-storage credentials create --credentials-group-id CREDGROU-Pxxx-IDzz-aaaa-DUMMYbbbbbbb`
|
||||
|
||||
* `terraform init --backend-config=./config.s3.tfbackend`
|
||||
|
||||
Now we can start using Terraform.
|
||||
|
||||
## Connect to created SKE cluster
|
||||
|
||||
* `stackit ske cluster list`
|
||||
* `stackit ske kubeconfig create scrumlr --login`
|
||||
* `--login` ensures that authentication is performed with the stackit CLI and very short-lived credentials are used in the background, without this flag the credentials are static and and usually have a longer lifetime. If the credentials are obtained without the `--login` flag, they must be renewed manually.
|
||||
7
module4/tf/config.s3.tfbackend.example
Normal file
7
module4/tf/config.s3.tfbackend.example
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Replace keys and bucket name here and rename this file to config.s3.tfbackend
|
||||
secret_key = "xxx"
|
||||
access_key = "yyy"
|
||||
bucket = "tfstate-bucket-SUFFIX"
|
||||
|
||||
# The key can be left as it is, but can also be customized as desired (before the terraform init)
|
||||
key = "scrumlr.tfstate"
|
||||
5
module4/tf/dns.tf
Normal file
5
module4/tf/dns.tf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
resource "stackit_dns_zone" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
dns_name = var.dns_name
|
||||
name = "Scrumlr Zone"
|
||||
}
|
||||
36
module4/tf/main.tf
Normal file
36
module4/tf/main.tf
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
stackit = {
|
||||
source = "stackitcloud/stackit"
|
||||
version = "0.50.0"
|
||||
}
|
||||
}
|
||||
backend "s3" {
|
||||
|
||||
# Secrets and config outsourced to config.s3.tfbackend file, which is included in .gitignore
|
||||
# See also: https://developer.hashicorp.com/terraform/language/backend#partial-configuration
|
||||
# terraform init --backend-config=./config.s3.tfbackend
|
||||
#bucket = "tfstate-bucket-SUFFIX"
|
||||
#key = "scrumlr.tfstate"
|
||||
#secret_key = "SECRETKEY"
|
||||
#access_key = "ACCESSKEY"
|
||||
|
||||
endpoints = {
|
||||
s3 = "https://object.storage.eu01.onstackit.cloud"
|
||||
}
|
||||
region = "eu01"
|
||||
|
||||
# Also use remote locking
|
||||
use_lockfile = true
|
||||
|
||||
# AWS specific checks must be skipped as they do not work on STACKIT.
|
||||
skip_credentials_validation = true
|
||||
skip_region_validation = true
|
||||
skip_s3_checksum = true
|
||||
skip_requesting_account_id = true
|
||||
}
|
||||
}
|
||||
provider "stackit" {
|
||||
default_region = "eu01"
|
||||
service_account_key_path = "sa_key.json"
|
||||
}
|
||||
42
module4/tf/postgres.tf
Normal file
42
module4/tf/postgres.tf
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
resource "stackit_postgresflex_instance" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
name = "scrumlr"
|
||||
acl = stackit_ske_cluster.scrumlr.egress_address_ranges
|
||||
backup_schedule = "00 00 * * *"
|
||||
flavor = {
|
||||
cpu = 2
|
||||
ram = 4
|
||||
}
|
||||
replicas = 3
|
||||
storage = {
|
||||
class = "premium-perf6-stackit"
|
||||
size = 5
|
||||
}
|
||||
version = 17
|
||||
}
|
||||
|
||||
resource "stackit_postgresflex_user" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
instance_id = stackit_postgresflex_instance.scrumlr.instance_id
|
||||
username = "scrumlr"
|
||||
roles = ["login", "createdb"]
|
||||
}
|
||||
|
||||
resource "stackit_postgresflex_database" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
instance_id = stackit_postgresflex_instance.scrumlr.instance_id
|
||||
owner = stackit_postgresflex_user.scrumlr.username
|
||||
name = "scrumlr"
|
||||
}
|
||||
|
||||
output "postgres_dsn" {
|
||||
value = format(
|
||||
"postgres://%s:%s@%s:%d/%s",
|
||||
stackit_postgresflex_user.scrumlr.username,
|
||||
stackit_postgresflex_user.scrumlr.password,
|
||||
stackit_postgresflex_user.scrumlr.host,
|
||||
stackit_postgresflex_user.scrumlr.port,
|
||||
stackit_postgresflex_database.scrumlr.name
|
||||
)
|
||||
sensitive = true
|
||||
}
|
||||
28
module4/tf/ske.tf
Normal file
28
module4/tf/ske.tf
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
resource "stackit_ske_cluster" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
name = "scrumlr"
|
||||
kubernetes_version_min = "1.31.7"
|
||||
node_pools = [
|
||||
{
|
||||
name = "scrumlrpool"
|
||||
machine_type = "c1.3"
|
||||
os_name = "flatcar"
|
||||
minimum = "3"
|
||||
maximum = "3"
|
||||
availability_zones = ["eu01-1", "eu01-2", "eu01-3"]
|
||||
volume_type = "storage_premium_perf2"
|
||||
}
|
||||
]
|
||||
maintenance = {
|
||||
enable_kubernetes_version_updates = true
|
||||
enable_machine_image_version_updates = true
|
||||
start = "01:00:00Z"
|
||||
end = "02:00:00Z"
|
||||
}
|
||||
extensions = {
|
||||
dns = {
|
||||
enabled = true
|
||||
zones = [stackit_dns_zone.scrumlr.dns_name]
|
||||
}
|
||||
}
|
||||
}
|
||||
2
module4/tf/terraform.tfvars.example
Normal file
2
module4/tf/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
project_id = "PROJECTx-IDyy-zzzz-aaaa-DUMMYbbbbbbb" # CHANGE-ME
|
||||
dns_name = "CHANGE-ME.stackit.gg" # CHANGE-ME
|
||||
9
module4/tf/variables.tf
Normal file
9
module4/tf/variables.tf
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
variable "project_id" {
|
||||
description = "The STACKIT project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "dns_name" {
|
||||
description = "DNS name for generated Zone"
|
||||
type = string
|
||||
}
|
||||
4
module5/.gitignore
vendored
Normal file
4
module5/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
jwt.key
|
||||
|
||||
secret-scrumlr-backend.yaml
|
||||
ingress-scrumlr.yaml
|
||||
21
module5/k8s/helm/cert-manager/README.md
Normal file
21
module5/k8s/helm/cert-manager/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# cert-manager
|
||||
|
||||
Docs: https://cert-manager.io/docs/
|
||||
Github Repo: https://github.com/cert-manager/cert-manager
|
||||
|
||||
```sh
|
||||
helm repo add jetstack https://charts.jetstack.io --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
cert-manager jetstack/cert-manager \
|
||||
--namespace cert-manager \
|
||||
--create-namespace \
|
||||
--version v1.17.2 \
|
||||
--values values.yaml
|
||||
```
|
||||
|
||||
Install [ClusterIssuer](https://cert-manager.io/docs/concepts/issuer/) for [Let's Encrypt](https://letsencrypt.org/)
|
||||
|
||||
```sh
|
||||
kubectl apply -f clusterissuer-letsencrypt-production.yaml
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-production
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-production
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
ingressClassName: nginx
|
||||
2
module5/k8s/helm/cert-manager/values.yaml
Normal file
2
module5/k8s/helm/cert-manager/values.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
crds:
|
||||
enabled: true
|
||||
15
module5/k8s/helm/ingress-nginx/README.md
Normal file
15
module5/k8s/helm/ingress-nginx/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# ingress-nginx
|
||||
|
||||
Docs: https://kubernetes.github.io/ingress-nginx/
|
||||
Github Repo: https://github.com/kubernetes/ingress-nginx
|
||||
|
||||
```sh
|
||||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx --force-update
|
||||
helm upgrade \
|
||||
ingress-nginx ingress-nginx/ingress-nginx \
|
||||
--install \
|
||||
--namespace ingress-nginx \
|
||||
--create-namespace \
|
||||
--version 4.12.1 \
|
||||
--values values.yaml
|
||||
```
|
||||
1
module5/k8s/helm/ingress-nginx/values.yaml
Normal file
1
module5/k8s/helm/ingress-nginx/values.yaml
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Note: This file is intentionally empty and is more of a placeholder to ensure consistency.
|
||||
15
module5/k8s/helm/nats/README.md
Normal file
15
module5/k8s/helm/nats/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# NATS
|
||||
|
||||
Docs: https://docs.nats.io/
|
||||
Github Repo: https://github.com/nats-io/k8s
|
||||
|
||||
```sh
|
||||
helm repo add nats https://nats-io.github.io/k8s/helm/charts/ --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
nats nats/nats \
|
||||
--namespace nats \
|
||||
--create-namespace \
|
||||
--version 1.3.3 \
|
||||
--values values.yaml
|
||||
```
|
||||
10
module5/k8s/helm/nats/values.yaml
Normal file
10
module5/k8s/helm/nats/values.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
config:
|
||||
cluster:
|
||||
enabled: true
|
||||
replicas: 3
|
||||
|
||||
podTemplate:
|
||||
topologySpreadConstraints:
|
||||
kubernetes.io/hostname:
|
||||
maxSkew: 1
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
data:
|
||||
SCRUMLR_SERVER_PORT: "8080"
|
||||
SCRUMLR_BASE_PATH: "/api"
|
||||
SCRUMLR_SERVER_NATS_URL: "nats.nats.svc.cluster.local:4222"
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
containers:
|
||||
- name: backend
|
||||
image: ghcr.io/inovex/scrumlr.io/scrumlr-server:3.10.3
|
||||
args:
|
||||
- "/app/main"
|
||||
- "-disable-check-origin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "200Mi"
|
||||
limits:
|
||||
memory: "200Mi"
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
failureThreshold: 30
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: scrumlr-backend
|
||||
- secretRef:
|
||||
name: scrumlr-backend
|
||||
env:
|
||||
- name: SCRUMLR_PRIVATE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: scrumlr-ecdsa-key
|
||||
key: jwt.key
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
8
module5/k8s/kustomize/scrumlr/backend/kustomization.yaml
Normal file
8
module5/k8s/kustomize/scrumlr/backend/kustomization.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- configmap-scrumlr-backend.yaml
|
||||
- secret-scrumlr-backend.yaml
|
||||
- deployment-scrumlr-backend.yaml
|
||||
- service-scrumlr-backend.yaml
|
||||
- poddisruptionbudget-backend.yaml
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: backend
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
type: Opaque
|
||||
data:
|
||||
# echo -n 'postgres://user:PASSWORD@INSTANCE-ID.postgresql.eu01.onstackit.cloud:5432/scrumlr' | base64
|
||||
SCRUMLR_SERVER_DATABASE_URL: "CHANGE-ME"
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
data:
|
||||
SCRUMLR_SERVER_URL: "/api"
|
||||
SCRUMLR_SERVER_PORT: "8080"
|
||||
SCRUMLR_SHOW_LEGAL_DOCUMENTS: "true"
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
containers:
|
||||
- name: frontend
|
||||
image: ghcr.io/inovex/scrumlr.io/scrumlr-frontend:3.10.3
|
||||
resources:
|
||||
requests:
|
||||
cpu: "25m"
|
||||
memory: "100Mi"
|
||||
limits:
|
||||
memory: "100Mi"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: scrumlr-frontend
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- configmap-scrumlr-frontend.yaml
|
||||
- deployment-scrumlr-frontend.yaml
|
||||
- service-scrumlr-frontend.yaml
|
||||
- poddisruptionbudget-frontend.yaml
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: frontend
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
37
module5/k8s/kustomize/scrumlr/ingress-scrumlr.yaml.example
Normal file
37
module5/k8s/kustomize/scrumlr/ingress-scrumlr.yaml.example
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: scrumlr
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/limit-connections: "100"
|
||||
# Websocket optimization https://kubernetes.github.io/ingress-nginx/user-guide/miscellaneous/#websockets
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "7200"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "7200"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-production"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- CHANGE-ME.domain.tld
|
||||
secretName: scrumlr-tls
|
||||
rules:
|
||||
- host: CHANGE-ME.domain.tld
|
||||
http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: scrumlr-backend
|
||||
port:
|
||||
number: 8080
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: scrumlr-frontend
|
||||
port:
|
||||
number: 80
|
||||
14
module5/k8s/kustomize/scrumlr/kustomization.yaml
Normal file
14
module5/k8s/kustomize/scrumlr/kustomization.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: scrumlr
|
||||
resources:
|
||||
- namespace-scrumlr.yaml
|
||||
- backend/
|
||||
- frontend/
|
||||
- ingress-scrumlr.yaml
|
||||
|
||||
# Create key: openssl ecparam -genkey -name secp521r1 -noout -out jwt.key
|
||||
secretGenerator:
|
||||
- name: scrumlr-ecdsa-key
|
||||
files:
|
||||
- jwt.key
|
||||
4
module5/k8s/kustomize/scrumlr/namespace-scrumlr.yaml
Normal file
4
module5/k8s/kustomize/scrumlr/namespace-scrumlr.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: scrumlr
|
||||
20
module6/.gitignore
vendored
Normal file
20
module6/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Secrets
|
||||
sa_key.json
|
||||
config.s3.tfbackend
|
||||
|
||||
# Var file
|
||||
terraform.tfvars
|
||||
|
||||
jwt.key
|
||||
|
||||
ingress-scrumlr.yaml
|
||||
secret-scrumlr-backend.env
|
||||
|
||||
k8s/helm/alloy/values.yaml
|
||||
16
module6/k8s/helm/alloy/README.md
Normal file
16
module6/k8s/helm/alloy/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Grafana Alloy
|
||||
|
||||
Docs: https://grafana.com/docs/alloy/latest/
|
||||
Github Repo: https://github.com/grafana/alloy
|
||||
|
||||
```shell
|
||||
cp values.yaml.example values.yaml
|
||||
helm repo add grafana https://grafana.github.io/helm-charts --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
alloy grafana/alloy \
|
||||
--namespace o11y \
|
||||
--create-namespace \
|
||||
--version 1.0.2 \
|
||||
--values values.yaml
|
||||
```
|
||||
154
module6/k8s/helm/alloy/values.yaml.example
Normal file
154
module6/k8s/helm/alloy/values.yaml.example
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
alloy:
|
||||
configMap:
|
||||
content: |-
|
||||
// Write your Alloy config here:
|
||||
logging {
|
||||
level = "info"
|
||||
format = "logfmt"
|
||||
}
|
||||
|
||||
prometheus.remote_write "default" {
|
||||
endpoint {
|
||||
url = <terraform output o11y_metrics_push_url>
|
||||
basic_auth {
|
||||
username = <terraform output o11y_cluster_user>
|
||||
password = <terraform output o11y_cluster_password>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loki.write "default" {
|
||||
endpoint {
|
||||
url = <terraform output o11y_logs_push_url>
|
||||
basic_auth {
|
||||
username = <terraform output o11y_cluster_user>
|
||||
password = <terraform output o11y_cluster_password>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prometheus.operator.podmonitors "services" {
|
||||
forward_to = [prometheus.remote_write.default.receiver]
|
||||
}
|
||||
|
||||
prometheus.operator.servicemonitors "services" {
|
||||
forward_to = [prometheus.remote_write.default.receiver]
|
||||
}
|
||||
|
||||
// discovery.kubernetes allows you to find scrape targets from Kubernetes resources.
|
||||
// It watches cluster state and ensures targets are continually synced with what is currently running in your cluster.
|
||||
discovery.kubernetes "pod" {
|
||||
role = "pod"
|
||||
}
|
||||
|
||||
// discovery.relabel rewrites the label set of the input targets by applying one or more relabeling rules.
|
||||
// If no rules are defined, then the input targets are exported as-is.
|
||||
discovery.relabel "pod_logs" {
|
||||
targets = discovery.kubernetes.pod.targets
|
||||
|
||||
// Label creation - "namespace" field from "__meta_kubernetes_namespace"
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_namespace"]
|
||||
action = "replace"
|
||||
target_label = "namespace"
|
||||
}
|
||||
|
||||
// Label creation - "pod" field from "__meta_kubernetes_pod_name"
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_name"]
|
||||
action = "replace"
|
||||
target_label = "pod"
|
||||
}
|
||||
|
||||
// Label creation - "container" field from "__meta_kubernetes_pod_container_name"
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_container_name"]
|
||||
action = "replace"
|
||||
target_label = "container"
|
||||
}
|
||||
|
||||
// Label creation - "app" field from "__meta_kubernetes_pod_label_app_kubernetes_io_name"
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"]
|
||||
action = "replace"
|
||||
target_label = "app"
|
||||
}
|
||||
|
||||
// Label creation - "job" field from "__meta_kubernetes_namespace" and "__meta_kubernetes_pod_container_name"
|
||||
// Concatenate values __meta_kubernetes_namespace/__meta_kubernetes_pod_container_name
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_namespace", "__meta_kubernetes_pod_container_name"]
|
||||
action = "replace"
|
||||
target_label = "job"
|
||||
separator = "/"
|
||||
replacement = "$1"
|
||||
}
|
||||
|
||||
// Label creation - "container" field from "__meta_kubernetes_pod_uid" and "__meta_kubernetes_pod_container_name"
|
||||
// Concatenate values __meta_kubernetes_pod_uid/__meta_kubernetes_pod_container_name.log
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_uid", "__meta_kubernetes_pod_container_name"]
|
||||
action = "replace"
|
||||
target_label = "__path__"
|
||||
separator = "/"
|
||||
replacement = "/var/log/pods/*$1/*.log"
|
||||
}
|
||||
|
||||
// Label creation - "container_runtime" field from "__meta_kubernetes_pod_container_id"
|
||||
rule {
|
||||
source_labels = ["__meta_kubernetes_pod_container_id"]
|
||||
action = "replace"
|
||||
target_label = "container_runtime"
|
||||
regex = "^(\\S+):\\/\\/.+$"
|
||||
replacement = "$1"
|
||||
}
|
||||
}
|
||||
|
||||
// loki.source.kubernetes tails logs from Kubernetes containers using the Kubernetes API.
|
||||
loki.source.kubernetes "pod_logs" {
|
||||
targets = discovery.relabel.pod_logs.output
|
||||
forward_to = [loki.process.pod_logs.receiver]
|
||||
}
|
||||
|
||||
// loki.process receives log entries from other Loki components, applies one or more processing stages,
|
||||
// and forwards the results to the list of receivers in the component's arguments.
|
||||
loki.process "pod_logs" {
|
||||
stage.static_labels {
|
||||
values = {
|
||||
cluster = "scrumlr",
|
||||
}
|
||||
}
|
||||
|
||||
forward_to = [loki.write.default.receiver]
|
||||
}
|
||||
|
||||
// loki.source.kubernetes_events tails events from the Kubernetes API and converts them
|
||||
// into log lines to forward to other Loki components.
|
||||
loki.source.kubernetes_events "cluster_events" {
|
||||
job_name = "integrations/kubernetes/eventhandler"
|
||||
log_format = "logfmt"
|
||||
forward_to = [
|
||||
loki.process.cluster_events.receiver,
|
||||
]
|
||||
}
|
||||
|
||||
// loki.process receives log entries from other loki components, applies one or more processing stages,
|
||||
// and forwards the results to the list of receivers in the component's arguments.
|
||||
loki.process "cluster_events" {
|
||||
forward_to = [loki.write.default.receiver]
|
||||
|
||||
stage.static_labels {
|
||||
values = {
|
||||
cluster = "scrumlr",
|
||||
}
|
||||
}
|
||||
|
||||
stage.labels {
|
||||
values = {
|
||||
kubernetes_cluster_events = "job",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
21
module6/k8s/helm/cert-manager/README.md
Normal file
21
module6/k8s/helm/cert-manager/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# cert-manager
|
||||
|
||||
Docs: https://cert-manager.io/docs/
|
||||
Github Repo: https://github.com/cert-manager/cert-manager
|
||||
|
||||
```sh
|
||||
helm repo add jetstack https://charts.jetstack.io --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
cert-manager jetstack/cert-manager \
|
||||
--namespace cert-manager \
|
||||
--create-namespace \
|
||||
--version v1.17.2 \
|
||||
--values values.yaml
|
||||
```
|
||||
|
||||
Install [ClusterIssuer](https://cert-manager.io/docs/concepts/issuer/) for [Let's Encrypt](https://letsencrypt.org/)
|
||||
|
||||
```sh
|
||||
kubectl apply -f clusterissuer-letsencrypt-production.yaml
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-production
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-production
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
ingressClassName: nginx
|
||||
2
module6/k8s/helm/cert-manager/values.yaml
Normal file
2
module6/k8s/helm/cert-manager/values.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
crds:
|
||||
enabled: true
|
||||
15
module6/k8s/helm/ingress-nginx/README.md
Normal file
15
module6/k8s/helm/ingress-nginx/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# ingress-nginx
|
||||
|
||||
Docs: https://kubernetes.github.io/ingress-nginx/
|
||||
Github Repo: https://github.com/kubernetes/ingress-nginx
|
||||
|
||||
```sh
|
||||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx --force-update
|
||||
helm upgrade \
|
||||
ingress-nginx ingress-nginx/ingress-nginx \
|
||||
--install \
|
||||
--namespace ingress-nginx \
|
||||
--create-namespace \
|
||||
--version 4.12.1 \
|
||||
--values values.yaml
|
||||
```
|
||||
5
module6/k8s/helm/ingress-nginx/values.yaml
Normal file
5
module6/k8s/helm/ingress-nginx/values.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
controller:
|
||||
metrics:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
15
module6/k8s/helm/nats/README.md
Normal file
15
module6/k8s/helm/nats/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# NATS
|
||||
|
||||
Docs: https://docs.nats.io/
|
||||
Github Repo: https://github.com/nats-io/k8s
|
||||
|
||||
```sh
|
||||
helm repo add nats https://nats-io.github.io/k8s/helm/charts/ --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
nats nats/nats \
|
||||
--namespace nats \
|
||||
--create-namespace \
|
||||
--version 1.3.3 \
|
||||
--values values.yaml
|
||||
```
|
||||
15
module6/k8s/helm/nats/values.yaml
Normal file
15
module6/k8s/helm/nats/values.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
config:
|
||||
cluster:
|
||||
enabled: true
|
||||
replicas: 3
|
||||
|
||||
podTemplate:
|
||||
topologySpreadConstraints:
|
||||
kubernetes.io/hostname:
|
||||
maxSkew: 1
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
|
||||
promExporter:
|
||||
enabled: true
|
||||
podMonitor:
|
||||
enabled: true
|
||||
15
module6/k8s/helm/prometheus-operator-crds/README.md
Normal file
15
module6/k8s/helm/prometheus-operator-crds/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Prometheus Operator CRDs
|
||||
|
||||
Docs: https://prometheus-operator.dev/docs/getting-started/introduction/
|
||||
Github Repo: https://github.com/prometheus-community/helm-charts/tree/main/charts/prometheus-operator-crds
|
||||
|
||||
```sh
|
||||
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts --force-update
|
||||
helm upgrade \
|
||||
--install \
|
||||
prometheus-operator-crds prometheus-community/prometheus-operator-crds \
|
||||
--namespace o11y \
|
||||
--create-namespace \
|
||||
--version 19.1.0 \
|
||||
--values values.yaml
|
||||
```
|
||||
31
module6/k8s/helm/prometheus-operator-crds/values.yaml
Normal file
31
module6/k8s/helm/prometheus-operator-crds/values.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Only enable Service & Pod Monitor
|
||||
crds:
|
||||
servicemonitors:
|
||||
enabled: true
|
||||
|
||||
podmonitors:
|
||||
enabled: true
|
||||
|
||||
alertmanagerconfigs:
|
||||
enabled: false
|
||||
|
||||
alertmanagers:
|
||||
enabled: false
|
||||
|
||||
probes:
|
||||
enabled: false
|
||||
|
||||
prometheusagents:
|
||||
enabled: false
|
||||
|
||||
prometheuses:
|
||||
enabled: false
|
||||
|
||||
prometheusrules:
|
||||
enabled: false
|
||||
|
||||
scrapeconfigs:
|
||||
enabled: false
|
||||
|
||||
thanosrulers:
|
||||
enabled: false
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
SCRUMLR_SERVER_PORT=8080
|
||||
SCRUMLR_BASE_PATH=/api
|
||||
SCRUMLR_SERVER_NATS_URL=nats.nats.svc.cluster.local:4222
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
containers:
|
||||
- name: backend
|
||||
image: ghcr.io/inovex/scrumlr.io/scrumlr-server:3.10.3
|
||||
args:
|
||||
- "/app/main"
|
||||
- "-disable-check-origin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "200Mi"
|
||||
limits:
|
||||
memory: "200Mi"
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
failureThreshold: 30
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 8080
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: scrumlr-backend
|
||||
- secretRef:
|
||||
name: scrumlr-backend
|
||||
env:
|
||||
- name: SCRUMLR_PRIVATE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: scrumlr-ecdsa-key
|
||||
key: jwt.key
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
24
module6/k8s/kustomize/scrumlr/backend/kustomization.yaml
Normal file
24
module6/k8s/kustomize/scrumlr/backend/kustomization.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment-scrumlr-backend.yaml
|
||||
- service-scrumlr-backend.yaml
|
||||
- poddisruptionbudget-backend.yaml
|
||||
configMapGenerator:
|
||||
- name: scrumlr-backend
|
||||
options:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
envs:
|
||||
- configmap-scrumlr-backend.env
|
||||
secretGenerator:
|
||||
- name: scrumlr-backend
|
||||
options:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
envs:
|
||||
- secret-scrumlr-backend.env
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: backend
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1 @@
|
|||
SCRUMLR_SERVER_DATABASE_URL=postgres://user:PASSWORD@INSTANCE-ID.postgresql.eu01.onstackit.cloud:5432/scrumlr
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scrumlr-backend
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "backend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
SCRUMLR_SERVER_URL=/api
|
||||
SCRUMLR_SERVER_PORT=8080
|
||||
SCRUMLR_SHOW_LEGAL_DOCUMENTS=true
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
containers:
|
||||
- name: frontend
|
||||
image: ghcr.io/inovex/scrumlr.io/scrumlr-frontend:3.10.3
|
||||
resources:
|
||||
requests:
|
||||
cpu: "25m"
|
||||
memory: "100Mi"
|
||||
limits:
|
||||
memory: "100Mi"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: scrumlr-frontend
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
15
module6/k8s/kustomize/scrumlr/frontend/kustomization.yaml
Normal file
15
module6/k8s/kustomize/scrumlr/frontend/kustomization.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment-scrumlr-frontend.yaml
|
||||
- service-scrumlr-frontend.yaml
|
||||
- poddisruptionbudget-frontend.yaml
|
||||
configMapGenerator:
|
||||
- name: scrumlr-frontend
|
||||
options:
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
envs:
|
||||
- configmap-scrumlr-frontend.env
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: frontend
|
||||
spec:
|
||||
minAvailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scrumlr-frontend
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/component: "frontend"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
37
module6/k8s/kustomize/scrumlr/ingress-scrumlr.yaml.example
Normal file
37
module6/k8s/kustomize/scrumlr/ingress-scrumlr.yaml.example
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: scrumlr
|
||||
labels:
|
||||
app.kubernetes.io/name: "scrumlr"
|
||||
app.kubernetes.io/part-of: "scrumlr"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/limit-connections: "100"
|
||||
# Websocket optimization https://kubernetes.github.io/ingress-nginx/user-guide/miscellaneous/#websockets
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "7200"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "7200"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-production"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- CHANGE-ME.domain.tld
|
||||
secretName: scrumlr-tls
|
||||
rules:
|
||||
- host: CHANGE-ME.domain.tld
|
||||
http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: scrumlr-backend
|
||||
port:
|
||||
number: 8080
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: scrumlr-frontend
|
||||
port:
|
||||
number: 80
|
||||
14
module6/k8s/kustomize/scrumlr/kustomization.yaml
Normal file
14
module6/k8s/kustomize/scrumlr/kustomization.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: scrumlr
|
||||
resources:
|
||||
- namespace-scrumlr.yaml
|
||||
- backend/
|
||||
- frontend/
|
||||
- ingress-scrumlr.yaml
|
||||
|
||||
# Create key: openssl ecparam -genkey -name secp521r1 -noout -out jwt.key
|
||||
secretGenerator:
|
||||
- name: scrumlr-ecdsa-key
|
||||
files:
|
||||
- jwt.key
|
||||
4
module6/k8s/kustomize/scrumlr/namespace-scrumlr.yaml
Normal file
4
module6/k8s/kustomize/scrumlr/namespace-scrumlr.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: scrumlr
|
||||
25
module6/tf/.terraform.lock.hcl
Normal file
25
module6/tf/.terraform.lock.hcl
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/stackitcloud/stackit" {
|
||||
version = "0.50.0"
|
||||
constraints = "0.50.0"
|
||||
hashes = [
|
||||
"h1:uU8/DLvW8tEty0PI2sUMem43IDNSrncHuLaXaEYdGFk=",
|
||||
"zh:0dde99e7b343fa01f8eefc378171fb8621bedb20f59157d6cc8e3d46c738105f",
|
||||
"zh:219d678bc471b3f5030724dcdde6be3f3fa63e911b7c7a0f446b0a0b4e5f48e7",
|
||||
"zh:4cd09155a09e320b0b68db4ba2971564f3d147c19ad991d6b7e731e26034d91c",
|
||||
"zh:507c1e24432f0d455ac8b628c37ee20db62e89a6e85508568c2820ba52404786",
|
||||
"zh:5aa10bfc4baad277a2bc746c83fca19911bb95a5e8821dd46f333bc621cbb453",
|
||||
"zh:67b55ad1135ca12997b0928cb67973b11ea196299e0cf66e3e0145faec762644",
|
||||
"zh:6d1d108edcd6794a8839d849e6ea48699875e22afeea7edd38bee3dd56dea7e8",
|
||||
"zh:7473c28b3781c0d00294d985bd067e753a419ca8e379f91a8f6f2ce4663566ee",
|
||||
"zh:8d234b24734f950f986322a5f084ca23bfd9b3d9fb7742b54404171cfcabc99e",
|
||||
"zh:af0804ea918648600cc6300dffce8a7b9115d30dc88db10f962b8e596d1465e1",
|
||||
"zh:b557940dc6387dc4cce8b100981ccaadac6bc4e6b50c566baf148d67939f8f2e",
|
||||
"zh:d477f77ce6f807d60069c1efcfa20607088ae7ab91d22805331a7634d84c2d1c",
|
||||
"zh:d95086e2338ceed511e798a2acc6d5cefdfff1a14f7b47b2d29b4ebc36b77a3a",
|
||||
"zh:ea0d8d5c9cf7d5871a54dd4786c378dfd9d10416f3c4d0ea4776465e8c562e10",
|
||||
"zh:f96af7b89dc99745f6a22c0ca2aedb18e10273251b8cbac9e2b1011c68c3c3f9",
|
||||
]
|
||||
}
|
||||
37
module6/tf/README.md
Normal file
37
module6/tf/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Terraform
|
||||
|
||||
## Create service account
|
||||
|
||||
* Install CLI tool: https://github.com/stackitcloud/stackit-cli/blob/main/INSTALLATION.md
|
||||
* `stackit auth login`
|
||||
* `stackit project list`
|
||||
* `stackit config set --project-id PROJECTx-IDyy-zzzz-aaaa-DUMMYbbbbbbb`
|
||||
* `stackit service-account create --name terraform`
|
||||
* `stackit service-account list`
|
||||
|
||||
* `stackit service-account key create --email terraform-vsPzcS7@sa.stackit.cloud > sa_key.json`
|
||||
* `stackit service-account key list --email terraform-vsPzcS7@sa.stackit.cloud`
|
||||
|
||||
* `stackit project member add terraform-vsPzcS7@sa.stackit.cloud --role editor`
|
||||
|
||||
## S3 Backend for tfstate
|
||||
|
||||
`stackit object-storage enable`
|
||||
|
||||
Note: The name must be globally unique. Use only lowercase letters, numbers or hyphens. The name should be at least 3 and at most 63 characters long.
|
||||
|
||||
Lets use something kinda random, to have a higher chance of catching an free bucket name:
|
||||
|
||||
* `stackit object-storage bucket create tfstate-bucket-g5el`
|
||||
* `stackit object-storage credentials-group create --name terraform-state`
|
||||
* `stackit object-storage credentials create --credentials-group-id CREDGROU-Pxxx-IDzz-aaaa-DUMMYbbbbbbb`
|
||||
|
||||
* `terraform init --backend-config=./config.s3.tfbackend`
|
||||
|
||||
Now we can start using Terraform.
|
||||
|
||||
## Connect to created SKE cluster
|
||||
|
||||
* `stackit ske cluster list`
|
||||
* `stackit ske kubeconfig create scrumlr --login`
|
||||
* `--login` ensures that authentication is performed with the stackit CLI and very short-lived credentials are used in the background, without this flag the credentials are static and and usually have a longer lifetime. If the credentials are obtained without the `--login` flag, they must be renewed manually.
|
||||
7
module6/tf/config.s3.tfbackend.example
Normal file
7
module6/tf/config.s3.tfbackend.example
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Replace keys and bucket name here and rename this file to config.s3.tfbackend
|
||||
secret_key = "xxx"
|
||||
access_key = "yyy"
|
||||
bucket = "tfstate-bucket-SUFFIX"
|
||||
|
||||
# The key can be left as it is, but can also be customized as desired (before the terraform init)
|
||||
key = "scrumlr.tfstate"
|
||||
5
module6/tf/dns.tf
Normal file
5
module6/tf/dns.tf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
resource "stackit_dns_zone" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
dns_name = var.dns_name
|
||||
name = "Scrumlr Zone"
|
||||
}
|
||||
36
module6/tf/main.tf
Normal file
36
module6/tf/main.tf
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
terraform {
|
||||
required_providers {
|
||||
stackit = {
|
||||
source = "stackitcloud/stackit"
|
||||
version = "0.50.0"
|
||||
}
|
||||
}
|
||||
backend "s3" {
|
||||
|
||||
# Secrets and config outsourced to config.s3.tfbackend file, which is included in .gitignore
|
||||
# See also: https://developer.hashicorp.com/terraform/language/backend#partial-configuration
|
||||
# terraform init --backend-config=./config.s3.tfbackend
|
||||
#bucket = "tfstate-bucket-SUFFIX"
|
||||
#key = "scrumlr.tfstate"
|
||||
#secret_key = "SECRETKEY"
|
||||
#access_key = "ACCESSKEY"
|
||||
|
||||
endpoints = {
|
||||
s3 = "https://object.storage.eu01.onstackit.cloud"
|
||||
}
|
||||
region = "eu01"
|
||||
|
||||
# Also use remote locking
|
||||
use_lockfile = true
|
||||
|
||||
# AWS specific checks must be skipped as they do not work on STACKIT.
|
||||
skip_credentials_validation = true
|
||||
skip_region_validation = true
|
||||
skip_s3_checksum = true
|
||||
skip_requesting_account_id = true
|
||||
}
|
||||
}
|
||||
provider "stackit" {
|
||||
default_region = "eu01"
|
||||
service_account_key_path = "sa_key.json"
|
||||
}
|
||||
32
module6/tf/o11y.tf
Normal file
32
module6/tf/o11y.tf
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
resource "stackit_observability_instance" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
name = "scrumlr"
|
||||
# Observability-Starter-EU01 may be enough for a short trial, but the metric samples (per minute) limit is quickly reached with our example.
|
||||
plan_name = "Observability-Basic-EU01"
|
||||
}
|
||||
|
||||
resource "stackit_observability_credential" "cluster" {
|
||||
project_id = var.project_id
|
||||
instance_id = stackit_observability_instance.scrumlr.instance_id
|
||||
}
|
||||
|
||||
output "o11y_grafana_url" {
|
||||
value = stackit_observability_instance.scrumlr.grafana_url
|
||||
}
|
||||
|
||||
output "o11y_logs_push_url" {
|
||||
value = stackit_observability_instance.scrumlr.logs_push_url
|
||||
}
|
||||
|
||||
output "o11y_metrics_push_url" {
|
||||
value = stackit_observability_instance.scrumlr.metrics_push_url
|
||||
}
|
||||
|
||||
output "o11y_cluster_user" {
|
||||
value = stackit_observability_credential.cluster.username
|
||||
}
|
||||
|
||||
output "o11y_cluster_password" {
|
||||
value = stackit_observability_credential.cluster.password
|
||||
sensitive = true
|
||||
}
|
||||
42
module6/tf/postgres.tf
Normal file
42
module6/tf/postgres.tf
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
resource "stackit_postgresflex_instance" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
name = "scrumlr"
|
||||
acl = stackit_ske_cluster.scrumlr.egress_address_ranges
|
||||
backup_schedule = "00 00 * * *"
|
||||
flavor = {
|
||||
cpu = 2
|
||||
ram = 4
|
||||
}
|
||||
replicas = 3
|
||||
storage = {
|
||||
class = "premium-perf6-stackit"
|
||||
size = 5
|
||||
}
|
||||
version = 17
|
||||
}
|
||||
|
||||
resource "stackit_postgresflex_user" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
instance_id = stackit_postgresflex_instance.scrumlr.instance_id
|
||||
username = "scrumlr"
|
||||
roles = ["login", "createdb"]
|
||||
}
|
||||
|
||||
resource "stackit_postgresflex_database" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
instance_id = stackit_postgresflex_instance.scrumlr.instance_id
|
||||
owner = stackit_postgresflex_user.scrumlr.username
|
||||
name = "scrumlr"
|
||||
}
|
||||
|
||||
output "postgres_dsn" {
|
||||
value = format(
|
||||
"postgres://%s:%s@%s:%d/%s",
|
||||
stackit_postgresflex_user.scrumlr.username,
|
||||
stackit_postgresflex_user.scrumlr.password,
|
||||
stackit_postgresflex_user.scrumlr.host,
|
||||
stackit_postgresflex_user.scrumlr.port,
|
||||
stackit_postgresflex_database.scrumlr.name
|
||||
)
|
||||
sensitive = true
|
||||
}
|
||||
32
module6/tf/ske.tf
Normal file
32
module6/tf/ske.tf
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
resource "stackit_ske_cluster" "scrumlr" {
|
||||
project_id = var.project_id
|
||||
name = "scrumlr"
|
||||
kubernetes_version_min = "1.31.7"
|
||||
node_pools = [
|
||||
{
|
||||
name = "scrumlrpool"
|
||||
machine_type = "c1.3"
|
||||
os_name = "flatcar"
|
||||
minimum = "3"
|
||||
maximum = "3"
|
||||
availability_zones = ["eu01-1", "eu01-2", "eu01-3"]
|
||||
volume_type = "storage_premium_perf2"
|
||||
}
|
||||
]
|
||||
maintenance = {
|
||||
enable_kubernetes_version_updates = true
|
||||
enable_machine_image_version_updates = true
|
||||
start = "01:00:00Z"
|
||||
end = "02:00:00Z"
|
||||
}
|
||||
extensions = {
|
||||
dns = {
|
||||
enabled = true
|
||||
zones = [stackit_dns_zone.scrumlr.dns_name]
|
||||
}
|
||||
argus = {
|
||||
argus_instance_id = stackit_observability_instance.scrumlr.instance_id
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
2
module6/tf/terraform.tfvars.example
Normal file
2
module6/tf/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
project_id = "PROJECTx-IDyy-zzzz-aaaa-DUMMYbbbbbbb" # CHANGE-ME
|
||||
dns_name = "CHANGE-ME.stackit.gg" # CHANGE-ME
|
||||
9
module6/tf/variables.tf
Normal file
9
module6/tf/variables.tf
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
variable "project_id" {
|
||||
description = "The STACKIT project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "dns_name" {
|
||||
description = "DNS name for generated Zone"
|
||||
type = string
|
||||
}
|
||||
Loading…
Reference in a new issue