Kubernetes Manifests
These raw manifests mirror the Helm chart in infra/helm/helm.md. Keep them around for direct cluster debugging, quick manifest inspection, and one-off application of the workloads.
Default Workflow
Use the infra/k8s directory as the working directory. First create your real
secrets.yml from the tracked template (it is gitignored — never commit it),
create both namespaces (one-time, if they don't already exist — see "Cluster Notes"
below), replace the <APP_NAMESPACE> placeholder in configmaps/grafana-lgtm-config.yml's
embedded prometheus.yaml with your actual app namespace, then apply everything recursively:
cd infra/k8s
cp secrets.yml.example secrets.yml # then fill in real values (see the comments inside)
# One-time, only if these don't already exist. Use `create`, not `apply` — on a Rancher-managed
# cluster like this course's, `apply`'s GET-before-create check needs permission this identity
# doesn't have for a namespace name it's never been granted access to (confirmed live). These
# manifests also carry the Rancher project-association label/annotation a bare `kubectl create
# namespace` lacks — see namespaces/deployment-namespace.yml for why that matters.
kubectl get namespace deployment >/dev/null 2>&1 || kubectl create -f namespaces/deployment-namespace.yml
kubectl get namespace monitoring-rolling-restarts >/dev/null 2>&1 || kubectl create -f namespaces/monitoring-namespace.yml
# -n deployment: resources without an explicit metadata.namespace (the app ones) land in the app
# namespace regardless of your current kubectl context; grafana-lgtm's manifests set their own
# metadata.namespace: monitoring-rolling-restarts, which wins over -n regardless. Explicit file
# list (not -R -f .) deliberately excludes namespaces/ — those were just bootstrapped above with
# `create`, and re-applying them here would hit the same apply-permission issue that motivated
# using `create` in the first place.
kubectl apply -n deployment -R \
-f secrets.yml -f deployments -f services -f configmaps -f rbac -f ingress.yml
This is the default path for the raw manifests in this repository because it keeps the deployment, service, and ingress files together and lets kubectl recurse through the directory layout.
To remove the same resources again, use the same recursive pattern:
kubectl delete -R -f .
What is here
- deployments/ contains one deployment manifest per workload, plus
mongodb-deployment.yml(the shared MongoDB Deployment and its PersistentVolumeClaim) andgrafana-lgtm-deployment.yml(the monitoring stack). - services/ contains one service manifest per workload, including
mongodb-service.ymlandgrafana-lgtm-service.yml. - configmaps/ contains
grafana-lgtm-config.yml, the Prometheus/dashboard/alerting config for the monitoring stack (hand-maintained — see the comment at the top of that file for what it must stay in sync with). - rbac/ contains
grafana-lgtm-rbac.yml— a ServiceAccount + namespaced Role/RoleBinding grantinggrafana-lgtm's Prometheusget/list/watchonpodsonly, needed for per-pod metrics scraping. - secrets.yml.example is the tracked template for the
mongodb-credentials,mongodb-user-credentials,jwt-keys, andservice-credentialsSecrets consumed by MongoDB and the Spring services. Copy it tosecrets.yml(gitignored) and fill in real values — the file's comments explain each field. Use a sealed-secret or external secret store for any non-local deployment. - namespaces/ contains
deployment-namespace.ymlandmonitoring-namespace.yml— one-time bootstrap manifests for a fully-wiped cluster, carrying the Rancher project-association label/annotation a barekubectl create namespacelacks (see the comment at the top ofdeployment-namespace.ymlfor the full explanation and how it was verified). - ingress.yml defines the external routing rules.
Stack Setup Notes
These are the setup details that matter for this stack and were present in the original guide.
Set the current kubectl context namespace when you want commands to target the same namespace repeatedly:
kubectl config set-context --current --namespace=<namespace-name>
If you have multiple kubeconfig files, merge them before working with the cluster:
KUBECONFIG=/home/<usr>/.kube/config:/home/<usr>/.kube/<second_config> kubectl config view --merge --flatten > /home/<usr>/.kube/merged-config
Use absolute paths when merging configs. Confirm the active configuration with:
kubectl config view
Push images to the registry before applying the manifests or Helm chart. The repository uses the GitHub Actions workflow in upload_images.yml for that step.
When to use the raw manifests
Use the raw manifests when you want to:
- apply a single workload without templating,
- compare the rendered Helm output against the original Kubernetes YAML,
- debug a cluster issue quickly without involving chart rendering,
- validate a service or ingress change in isolation.
Use the Helm chart when you want reusable installs, upgrades, shared configuration, and the chart-local Makefile targets.
Cluster Notes
If you need to adjust your current kubectl context, do that outside the manifests themselves. The repository does not assume a namespace in these files — except grafana-lgtm, which now runs in its own dedicated namespace: its ServiceAccount, Deployment, Service, PVC, and ConfigMaps hardcode namespace: monitoring-rolling-restarts (that name must already exist — kubectl create -f namespaces/monitoring-namespace.yml, not a bare kubectl create namespace — before applying; see the "Default Workflow" section above). This is deliberate: it isolates the monitoring stack's own ResourceQuota from the app workloads', and it's a fixed project-level name rather than a per-user choice, unlike the app namespace.
One consequence: grafana-lgtm-rbac.yml's Role/RoleBinding have no namespace field (they rely on whatever -n <app-namespace> you apply the directory with, same as everything else) — a RoleBinding must live in the same namespace as the pods it grants access to, which is the app namespace, not monitoring-rolling-restarts where the ServiceAccount itself lives. The RoleBinding's subject names that namespace explicitly instead.
Another: configmaps/grafana-lgtm-config.yml's embedded prometheus.yaml has a literal <APP_NAMESPACE> placeholder in its kubernetes_sd_configs — replace it with your actual app namespace before applying. The Helm chart doesn't have this problem (that file is rendered with tpl, so {{ .Release.Namespace }} resolves automatically), but raw manifests have no templating step.
Usage Guide
Apply only the deployments:
kubectl apply -f deployments
Apply only the services:
kubectl apply -f services
Apply only the ingress:
kubectl apply -f ingress.yml
Inspect the current resources:
kubectl get all
kubectl get ingress
Resource Map
web-clientlistens on port3000.api-gatewaylistens on port8080.user-servicelistens on port8081.content-servicelistens on port8082.gen-ailistens on port8000.mongodblistens on port27017(ClusterIP only — not exposed via ingress). user-service uses databaseusers, content-service uses databasecontent, both via the*-credentialsSecrets.- The ingress uses path-based routing on a single host (
rolling-restarts.stud.k8s.aet.cit.tum.de):/api/and/actuator/health→ API gateway,/ai/→ GenAI service,/→ web client. Only/actuator/healthis publicly routed —/actuator/prometheusis reachable internally only (Prometheus scrapes pods directly, not through the ingress). - The ingress secret is
rolling-restarts-tls.
Relationship To Helm
The Helm chart in infra/helm/helm.md uses the same service names, ports, hostnames, and ingress secret. If the raw manifests and chart diverge, update this file and the chart together.