-
Notifications
You must be signed in to change notification settings - Fork 387
Infrastructure: Add Kubernetes-native LocalCI execution
#13375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
krusche
wants to merge
20
commits into
develop
Choose a base branch
from
feature/kubernetes-localci
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
fe65028
Development: Add Kubernetes-native LocalCI execution
krusche 8ac1f3e
implement small improvements
krusche cab3f2a
Merge branch 'develop' into feature/kubernetes-localci
krusche aec98e5
Merge branch 'develop' into feature/kubernetes-localci
krusche 9a05f8c
Merge remote-tracking branch 'origin/develop' into feature/kubernetes…
krusche c65e68f
Infrastructure: Address Kubernetes LocalCI review feedback
krusche 689af11
Infrastructure: Publish the requeued build job atomically with its state
krusche a93fab7
Infrastructure: Cover the Hazelcast wire compatibility of the agent d…
krusche 6f5b9b8
Infrastructure: Cap requeues from the pause grace period too
krusche 1191314
Merge remote-tracking branch 'origin/develop' into feature/kubernetes…
krusche bf12c09
Merge remote-tracking branch 'origin/develop' into HEAD
krusche 542f889
Merge remote-tracking branch 'origin/develop' into HEAD
krusche b439915
Infrastructure: Address the review findings on the Kubernetes runner
krusche bd3fc74
Merge develop into feature/kubernetes-localci
krusche 7598b44
Merge remote-tracking branch 'origin/develop' into remerge-13375
krusche 6670618
Merge remote-tracking branch 'origin/develop' into feature/kubernetes…
krusche cbe9e2b
Merge remote-tracking branch 'origin/develop' into feature/kubernetes…
krusche 97d8a6e
Infrastructure: Collect Kubernetes build results from the build direc…
krusche a7b6044
Infrastructure: Guard the Kubernetes teardown by kubectl context
krusche d383197
Merge branch 'develop' into feature/kubernetes-localci
krusche File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| FROM alpine:3.22 | ||
|
|
||
| # ip6tables is a separate package on Alpine; without it the network isolation init container | ||
| # cannot apply any IPv6 rules and builds requested with network mode "none" stay reachable over IPv6. | ||
| RUN apk add --no-cache bash coreutils ip6tables iptables tar | ||
|
|
||
| # No USER directive on purpose: the Kubernetes Job spec sets the effective user per container. | ||
| # The helper and the isolation container both run as UID 0 because they share the workspace volume | ||
| # with a builder container that usually runs as root, and the isolation container needs NET_ADMIN. | ||
| WORKDIR /var/tmp | ||
|
|
||
| CMD ["sh", "-c", "while true; do sleep 3600; done"] | ||
|
Check warning on line 12 in docker/localci-kubernetes-helper/Dockerfile
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Patterns to ignore when building packages. | ||
| *.tmpl | ||
| .DS_Store | ||
| .git/ | ||
| .gitignore | ||
| *.swp | ||
| *.bak | ||
| *.orig | ||
| *~ | ||
| .vscode/ | ||
| .idea/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # Cluster setup and validation | ||
|
|
||
| ## Docker Desktop | ||
|
|
||
| The local acceptance target is Docker Desktop's managed Kubernetes cluster, not a separately installed `kind` binary. | ||
|
|
||
| 1. Open Docker Desktop settings. | ||
| 2. Enable the containerd image store. | ||
| 3. Under Kubernetes, choose the `kind` provisioner and configure three nodes. | ||
| 4. Apply/recreate the cluster and wait until it is running. | ||
| 5. Select the `docker-desktop` context: | ||
|
|
||
| ```bash | ||
| kubectl config use-context docker-desktop | ||
| kubectl get nodes | ||
| ``` | ||
|
|
||
| The expected topology is one control-plane and two schedulable workers. The helper script refuses a one-node or `kubeadm` Docker Desktop cluster because that | ||
| would not exercise multi-node placement. | ||
|
|
||
| Run all local steps: | ||
|
|
||
| ```bash | ||
| ./run-localci-kubernetes.sh all | ||
| ``` | ||
|
|
||
| Individual commands are available: | ||
|
|
||
| ```bash | ||
| ./run-localci-kubernetes.sh build | ||
| ./run-localci-kubernetes.sh up | ||
| ./run-localci-kubernetes.sh status | ||
| ./run-localci-kubernetes.sh test | ||
| ./run-localci-kubernetes.sh logs | ||
| ./run-localci-kubernetes.sh down | ||
| ``` | ||
|
|
||
| `up --skip-build` reuses existing images. `test --filter <playwright-filter>` forwards an optional Playwright filter. `all --keep` leaves the installation and | ||
| port-forward running after validation. `down` removes the release, both namespaces, and the Artemis-specific worker labels while retaining the local images. | ||
|
|
||
| ## Scheduling labels | ||
|
|
||
| The local script labels one worker for both core pods and both workers for controllers/workloads: | ||
|
|
||
| ```text | ||
| artemis.cit.tum.de/core=true | ||
| artemis.cit.tum.de/build-worker=true | ||
| ``` | ||
|
|
||
| The core pin is only for Docker Desktop's `ReadWriteOnce` volume. Do not copy it into a real cluster; provide `ReadWriteMany` storage instead. | ||
|
|
||
| ## Verification | ||
|
|
||
| Useful checks after installation: | ||
|
|
||
| ```bash | ||
| kubectl -n artemis get pods -o wide | ||
| kubectl -n artemis-builds get jobs,pods -w | ||
| kubectl auth can-i create jobs.batch \ | ||
| --as system:serviceaccount:artemis:artemis-localci-controller \ | ||
| -n artemis-builds | ||
| kubectl auth can-i list secrets \ | ||
| --as system:serviceaccount:artemis-builds:artemis-localci-workload \ | ||
| -n artemis-builds | ||
| ``` | ||
|
|
||
| The first authorization check should return `yes`; the workload check should return `no`. | ||
|
|
||
| The chart exposes `artemis-http` as a ClusterIP. For local access: | ||
|
|
||
| ```bash | ||
| kubectl -n artemis port-forward service/artemis-http 8080:8080 | ||
| ``` | ||
|
|
||
| ## Real clusters | ||
|
|
||
| - Use an RWX storage class for `sharedStorage`. | ||
| - Use a managed PostgreSQL/broker if availability is required. | ||
| - Configure a public `artemis.config.serverUrl` that is also resolvable from build-agent pods. | ||
| - Publish both the Artemis image and trusted helper image to a registry and configure pull secrets. | ||
| - Keep the controller and workload service accounts separate. | ||
| - Add network policy, admission controls, quotas, runtime isolation, TLS, and production secret management according to local requirements. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| apiVersion: v2 | ||
| name: artemis | ||
| description: >- | ||
| Multi-node Artemis deployment with LocalCI core nodes and Kubernetes build agents. | ||
| type: application | ||
|
|
||
| # Chart version. Bump on every change to the chart or its templates (SemVer). | ||
| version: 0.2.0 | ||
|
|
||
| # The Artemis application version this chart defaults to. Overridable via `image.tag`. | ||
| # The application image must contain the LocalCI Kubernetes runner. | ||
| appVersion: "9.9" | ||
|
|
||
| home: https://docs.artemis.cit.tum.de | ||
| sources: | ||
| - https://github.com/ls1intum/Artemis | ||
|
|
||
| maintainers: | ||
| - name: Artemis Team | ||
| url: https://github.com/ls1intum/Artemis | ||
|
|
||
| icon: https://raw.githubusercontent.com/ls1intum/Artemis/develop/src/main/resources/public/images/logo.png | ||
|
|
||
| keywords: | ||
| - artemis | ||
| - e-learning | ||
| - postgres | ||
| - localci | ||
| - kubernetes | ||
| - gateway-api |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # Artemis multi-node LocalCI chart | ||
|
|
||
| This MVP chart deploys a complete Artemis LocalCI installation in Kubernetes: | ||
|
|
||
| - one Artemis leader core with the `scheduling` profile; | ||
| - zero or more member cores without `scheduling`; | ||
| - PostgreSQL, JHipster Registry/Eureka, and ActiveMQ STOMP relay; | ||
| - standalone LocalCI build-agent controllers; and | ||
| - Kubernetes Jobs created directly for individual builds. | ||
|
|
||
| Docker execution remains the default outside the `k8s` profile. The controllers in this chart use | ||
| `prod,buildagent,k8s` and do not need a Docker socket. | ||
|
|
||
| ## Execution flow | ||
|
|
||
| 1. Core nodes place jobs in the existing distributed priority queue and calculate estimated start/completion times. | ||
| 2. A controller claims work according to its configured concurrency. | ||
| 3. The controller clones LocalVC repositories with the existing build-agent account. | ||
| 4. The controller creates a Job in `buildAgents.namespace` with the exercise image and a trusted helper sidecar. | ||
| 5. Repositories and the script are transferred through the Kubernetes exec API into an `emptyDir` workspace. | ||
| 6. Build logs are streamed into the existing LocalCI log map. The helper returns the same tar result format used by Docker execution. | ||
| 7. Existing XML/SARIF parsing, result queueing, cancellation, pause/resume, retry counts, statistics, and time estimates continue unchanged. | ||
|
|
||
| The workload service account has no API token and no permissions. The controller receives only the namespaced Job/Pod/log/exec permissions in | ||
| `templates/build-agents/serviceaccounts-rbac.yaml`. | ||
|
|
||
| ## Storage and multi-core requirements | ||
|
|
||
| Every core must mount the same `/opt/artemis/data` filesystem. It contains LocalVC repositories, uploads, exports, and build logs. A real multi-node | ||
| cluster therefore needs `ReadWriteMany` storage. Docker Desktop has only local `ReadWriteOnce` storage, so | ||
| `values-docker-desktop.yaml` pins both core pods to one worker as an explicit local-only compromise. | ||
|
|
||
| Core nodes discover one another through Eureka and use ActiveMQ for cross-core WebSocket delivery. Exactly one core has `scheduling`. | ||
|
|
||
| ## Install | ||
|
|
||
| Create a values file with real secrets and a resolvable server URL, then run: | ||
|
|
||
| ```bash | ||
| helm upgrade --install artemis ./helm/artemis \ | ||
| --namespace artemis --create-namespace \ | ||
| --values my-values.yaml \ | ||
| --wait --timeout 20m | ||
| ``` | ||
|
|
||
| Required secret values are: | ||
|
|
||
| - `artemis.config.admin.password` | ||
| - `artemis.config.jwtBase64Secret` | ||
| - `artemis.config.versionControl.buildAgentGitPassword` | ||
| - `postgresql.auth.password` | ||
| - `registry.password` | ||
| - `broker.auth.password` | ||
|
|
||
| Set `gateway.enabled=false` when using only `kubectl port-forward`. For a plain-HTTP local port-forward, also set `artemis.config.secureCookies=false`; the Docker Desktop values file already does this. Otherwise install the Gateway API CRDs/controller and configure the gateway values. | ||
|
|
||
| ## Docker Desktop acceptance cluster | ||
|
|
||
| Use Docker Desktop 4.51 or newer, switch its managed Kubernetes provisioner to `kind`, select three nodes, and use the containerd image store. Then run: | ||
|
|
||
| ```bash | ||
| ./run-localci-kubernetes.sh all | ||
| ``` | ||
|
|
||
| The script validates the context and topology, labels workers, builds/imports the two local images, installs this chart with | ||
| `values-docker-desktop.yaml`, and verifies that both cores register, the build-agent controllers occupy distinct workers, and native build Jobs run on both workers. See | ||
| `CLUSTER-SETUP.md` for commands and diagnostics. | ||
|
|
||
| ## Supported exercise flags | ||
|
|
||
| The MVP supports environment variables (`KEY=value`), CPU, memory, and `network=none`. Named Docker networks and custom memory swap are rejected with an | ||
| explicit build error. `network=none` is implemented by a short trusted init container with `NET_ADMIN`; the exercise and helper containers do not receive | ||
| that capability. | ||
|
|
||
| This MVP does not include runtime sandboxes such as gVisor/Kata, production secret management, autoscaling, highly available data services, object storage, | ||
| or named network profiles. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # Cluster-setup manifests | ||
|
|
||
| Ready-to-apply manifests for the cluster-level prerequisites described in | ||
| [`../CLUSTER-SETUP.md`](../CLUSTER-SETUP.md). **Edit the `FIXME:` placeholders** (IP ranges, email, namespace, release | ||
| name) before applying. | ||
|
|
||
| ``` | ||
| cluster-setup/ | ||
| ├── gatewayclass.yaml # plain Envoy Gateway GatewayClass | ||
| ├── clusterissuer-letsencrypt.yaml # optional: cert-manager Let's Encrypt issuer | ||
| └── metallb-dualstack/ # dual-stack Envoy LB pinned to a MetalLB pool | ||
| ├── envoyproxy.yaml # references an EXISTING MetalLB pool by name | ||
| └── gatewayclass.yaml # GatewayClass wired to the EnvoyProxy | ||
| ``` | ||
|
|
||
| > These manifests reference an **existing** MetalLB `IPAddressPool` (by name, in `envoyproxy.yaml`); they do not create | ||
| > one. The pool must contain both an IPv4 and an IPv6 range for dual-stack to work. | ||
|
|
||
| ## What to apply | ||
|
|
||
| CRDs and controllers are installed with `helm`/`kubectl` per `../CLUSTER-SETUP.md` (Gateway API experimental CRDs, | ||
| Envoy Gateway, cert-manager, an RWX StorageClass). Once those are in place, apply the manifests here. | ||
|
|
||
| Pick **one** GatewayClass: | ||
|
|
||
| **A. Plain (no MetalLB customization):** | ||
|
|
||
| ```bash | ||
| kubectl apply -f cluster-setup/gatewayclass.yaml | ||
| ``` | ||
|
|
||
| **B. MetalLB pool + dual-stack** (applies the EnvoyProxy and a GatewayClass that references it; set the pool name in | ||
| `envoyproxy.yaml` first): | ||
|
|
||
| ```bash | ||
| kubectl apply -f cluster-setup/metallb-dualstack/ | ||
| ``` | ||
|
|
||
| > Requires Envoy Gateway's own CRDs (the `EnvoyProxy` kind). If you installed the controller with `--skip-crds`, apply | ||
| > them first - see `../CLUSTER-SETUP.md` section 2 - otherwise this fails with | ||
| > `no matches for kind "EnvoyProxy"`. | ||
|
|
||
| Optional TLS (after cert-manager is installed): | ||
|
|
||
| ```bash | ||
| kubectl apply -f cluster-setup/clusterissuer-letsencrypt.yaml | ||
| ``` | ||
|
|
||
| All variants create a GatewayClass named `envoy`, so set `gateway.className=envoy` in your chart values. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Optional: cert-manager ClusterIssuer that solves ACME HTTP-01 challenges | ||
| # through the Gateway API, so the chart's HTTPS listener gets an automatic | ||
| # Let's Encrypt certificate. Requires cert-manager to be installed. | ||
| # Set gateway.tls.certManagerClusterIssuer=letsencrypt-prod in your chart values. | ||
| apiVersion: cert-manager.io/v1 | ||
| kind: ClusterIssuer | ||
| metadata: | ||
| name: letsencrypt-prod | ||
| spec: | ||
| acme: | ||
| server: https://acme-v02.api.letsencrypt.org/directory | ||
| email: admin@example.com # FIXME: your contact email | ||
| privateKeySecretRef: | ||
| name: letsencrypt-prod | ||
| solvers: | ||
| - http01: | ||
| gatewayHTTPRoute: | ||
| parentRefs: | ||
| - name: <gateway-name> # FIXME: the rendered Gateway resource name | ||
| namespace: <ns> # FIXME: the release namespace | ||
| kind: Gateway |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Plain Envoy Gateway GatewayClass (no MetalLB / dual-stack customization). | ||
| # Apply this OR the metallb-dualstack/ variant, not both (same name "envoy"). | ||
| # Set gateway.className=envoy in your chart values. | ||
| apiVersion: gateway.networking.k8s.io/v1 | ||
| kind: GatewayClass | ||
| metadata: | ||
| name: envoy | ||
| spec: | ||
| controllerName: gateway.envoyproxy.io/gatewayclass-controller |
29 changes: 29 additions & 0 deletions
29
helm/artemis/cluster-setup/metallb-dualstack/envoyproxy.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Customizes the LoadBalancer service Envoy Gateway generates: pins it to a | ||
| # specific MetalLB pool and requests dual-stack (IPv4 + IPv6). Referenced by the | ||
| # GatewayClass in this folder via parametersRef. | ||
| apiVersion: gateway.envoyproxy.io/v1alpha1 | ||
| kind: EnvoyProxy | ||
| metadata: | ||
| name: artemis-envoy-proxy | ||
| namespace: envoy-gateway-system # must live in the Envoy Gateway namespace | ||
| spec: | ||
| provider: | ||
| type: Kubernetes | ||
| kubernetes: | ||
| envoyService: | ||
| annotations: | ||
| # Pin the LB address to one of your EXISTING MetalLB pools (must contain | ||
| # both an IPv4 and an IPv6 range for dual-stack). This chart does NOT | ||
| # create the pool. | ||
| metallb.universe.tf/address-pool: lb2 # FIXME | ||
| # Optional: request specific IPs from that pool (comma-separated, one per family). | ||
| # metallb.universe.tf/loadBalancerIPs: 192.0.2.240,2001:db8:42::1 | ||
| patch: | ||
| # No dedicated field for ipFamilies, so patch the generated Service. | ||
| type: StrategicMerge | ||
| value: | ||
| spec: | ||
| ipFamilyPolicy: RequireDualStack # use PreferDualStack to fall back gracefully | ||
| ipFamilies: | ||
| - IPv4 | ||
| - IPv6 |
14 changes: 14 additions & 0 deletions
14
helm/artemis/cluster-setup/metallb-dualstack/gatewayclass.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Envoy Gateway GatewayClass that references the EnvoyProxy in this folder, | ||
| # applying the MetalLB pool + dual-stack customization to every Gateway of this | ||
| # class. Set gateway.className=envoy in your chart values. | ||
| apiVersion: gateway.networking.k8s.io/v1 | ||
| kind: GatewayClass | ||
| metadata: | ||
| name: envoy | ||
| spec: | ||
| controllerName: gateway.envoyproxy.io/gatewayclass-controller | ||
| parametersRef: | ||
| group: gateway.envoyproxy.io | ||
| kind: EnvoyProxy | ||
| name: artemis-envoy-proxy | ||
| namespace: envoy-gateway-system |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.