diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..135410b --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,58 @@ +# # Build and push distributed-secrets-vault to Docker Hub on version tags or manual dispatch. +# name: Publish Docker image + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + image_tag: + description: "Docker image tag (e.g. latest or v1.0.0)" + required: true + default: "latest" + +env: + DSV_IMAGE_NAME: distributed-secrets-vault + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up JDK 25 + uses: actions/setup-java@v5.2.0 + with: + java-version: "25" + distribution: "temurin" + cache: maven + + - name: Build application JAR + run: | + ./mvnw -B clean package -DskipTests + mkdir -p target/dependency + (cd target/dependency && jar -xf ../*.jar) + + - name: Resolve image tag + id: meta + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "tag=${{ inputs.image_tag }}" >> "$GITHUB_OUTPUT" + else + echo "tag=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + fi + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.DSV_IMAGE_NAME }}:${{ steps.meta.outputs.tag }} diff --git a/.gitignore b/.gitignore index c1c9ad4..7edf54e 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,5 @@ build/ ### Environment Variables ### .env .env.local -.env.*.local \ No newline at end of file +.env.*.local +k8s/image.env \ No newline at end of file diff --git a/README.md b/README.md index 6b97a24..6c94b5e 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,6 @@ The Distributed Secrets Vault is a leaderless cluster system where secrets are s ## Installation and Usage Guide - [Installation Instructions](docker/README.md) +- [Docker Hub image & Kubernetes deploy](docs/docker-hub.md) - [API and Usage Documentation](docs/api.md) - [DSV Client Repository](https://github.com/S26-Distributed-Capstone/DSVClient) diff --git a/docs/docker-hub.md b/docs/docker-hub.md new file mode 100644 index 0000000..67407f7 --- /dev/null +++ b/docs/docker-hub.md @@ -0,0 +1,83 @@ +# Docker Hub image for Kubernetes + +Production and testing manifests pull the DSV backend from Docker Hub. Workers do not need a local `ctr import`. + +## Image + +Published at [noambensim/distributed-secrets-vault](https://hub.docker.com/r/noambensim/distributed-secrets-vault) on Docker Hub: + +```text +docker.io/noambensim/distributed-secrets-vault:latest +``` + +Pull manually: + +```bash +docker pull noambensim/distributed-secrets-vault:latest +``` + +To change the repository, edit: + +- `k8s/image.env` (for build/push scripts) +- `k8s/production/kustomization.yaml` and `k8s/testing/kustomization.yaml` (`images.newName` / `newTag`) + +## One-time Docker Hub setup + +1. Repository: [noambensim/distributed-secrets-vault](https://hub.docker.com/r/noambensim/distributed-secrets-vault). +2. Create an access token: Docker Hub → Account Settings → Security → New Access Token. +3. For GitHub Actions, add repository secrets: + - `DOCKERHUB_USERNAME` + - `DOCKERHUB_TOKEN` (the access token, not your account password) + +## Build and push (local) + +```bash +cp k8s/image.env.example k8s/image.env +# Edit DOCKERHUB_USERNAME if needed + +docker login +chmod +x scripts/docker-build-push.sh +./scripts/docker-build-push.sh +``` + +Or publish via GitHub Actions: **Actions → Publish Docker image → Run workflow**, or push a tag `v1.0.0`. + +## Deploy to the cluster + +```bash +kubectl apply -k k8s/production/ --dry-run=client +kubectl apply -k k8s/production/ +kubectl get pods -n dsv -w +``` + +Each node pulls `docker.io/noambensim/distributed-secrets-vault:latest` when a pod starts (`imagePullPolicy: IfNotPresent`). + +## Private repository + +If the image is private, create a pull secret in the `dsv` namespace: + +```bash +kubectl create secret docker-registry dockerhub-credentials \ + --docker-server=https://index.docker.io/v1/ \ + --docker-username=YOUR_USER \ + --docker-password=YOUR_TOKEN \ + -n dsv +``` + +Add to `k8s/production/app-statefulset.yaml` under `spec.template.spec`: + +```yaml +imagePullSecrets: + - name: dockerhub-credentials +``` + +See `k8s/production/dockerhub-pull-secret.yaml.example`. + +## Pin a release + +Set the same tag in `k8s/image.env` (`DSV_IMAGE_TAG`) and in both kustomization files (`images.newTag`), then push and redeploy: + +```bash +kubectl apply -k k8s/production/ +kubectl rollout restart statefulset/dsv-app -n dsv +``` diff --git a/docs/kubernetes.md b/docs/kubernetes.md index fd7ed61..8d77b00 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -65,8 +65,8 @@ KAFKA_BOOTSTRAP_SERVERS=kafka.dsv.svc.cluster.local:9092 `k8s/testing` is intended for Docker Desktop, Minikube, or K3d. It runs three DSV app replicas without production node-affinity constraints. ```bash -kubectl apply -f k8s/testing/ -kubectl get pods -w +kubectl apply -k k8s/testing/ +kubectl get pods -n dsv -w ``` ## Production Environment @@ -77,9 +77,9 @@ kubectl get pods -w - app pods use pod anti-affinity (one DSV pod per worker) - **10 replicas** with `cluster.totalNodes=10`, `thresholdK=6`, `quorumM=6` -Full steps (scp manifests, import image on all workers, verify): [production-kubernetes-deploy.md](production-kubernetes-deploy.md). +Image is pulled from Docker Hub on each node. Build/push: [docker-hub.md](docker-hub.md). Deploy steps: [production-kubernetes-deploy.md](production-kubernetes-deploy.md). ```bash -kubectl apply -f k8s/production/ --dry-run=client -kubectl apply -f k8s/production/ +kubectl apply -k k8s/production/ --dry-run=client +kubectl apply -k k8s/production/ ``` diff --git a/docs/production-kubernetes-deploy.md b/docs/production-kubernetes-deploy.md index 546ff6d..ed90e61 100644 --- a/docs/production-kubernetes-deploy.md +++ b/docs/production-kubernetes-deploy.md @@ -2,7 +2,9 @@ Guide for deploying Distributed Secrets Vault to a cluster with **3 control-plane nodes** and **10 worker nodes**, using manifests in `k8s/production/`. -All resources run in the **`dsv`** namespace (`namespace.yaml` is applied first). +The DSV app image is pulled from **Docker Hub** on each node (no per-worker `ctr import`). See [docker-hub.md](docker-hub.md) for build, push, and registry setup. + +All resources run in the **`dsv`** namespace. ## What gets deployed @@ -13,166 +15,109 @@ All resources run in the **`dsv`** namespace (`namespace.yaml` is applied first) | PVCs | 11 × RWO | 10 × Redis (5Gi) + 1 × Kafka (10Gi) | | Services + Ingress | Traefik → `dsv-app-service` | HTTP API on port **9080** | +Default image: `docker.io/noambensim/distributed-secrets-vault:latest` ([Docker Hub](https://hub.docker.com/r/noambensim/distributed-secrets-vault), via Kustomize). + Cluster parameters (10 nodes, Shamir **k=6**, write quorum **m=6**) are set via `JAVA_TOOL_OPTIONS` in `app-statefulset.yaml`. ## Prerequisites -- `kubectl` on the machine you deploy from, with a valid kubeconfig for the cluster -- Built image tagged **`dsv-backend:latest`** on **every worker** that can run `dsv-app` (see [Image distribution](#image-distribution)) -- Default **StorageClass** for dynamic PVCs (k3s: `local-path` is typical) -- Traefik ingress controller (bundled with k3s) - -## 1. Build the application image (build machine) - -```bash -cd DistributedSecretsVault -./mvnw clean package -DskipTests -mkdir -p target/dependency && (cd target/dependency && jar -xf ../*.jar) -docker build -t dsv-backend:latest . -docker save dsv-backend:latest | gzip -9 > dsv-backend.tar.gz -``` +- `kubectl` with cluster access +- Image published to Docker Hub (public, or private with `imagePullSecrets`) +- Default **StorageClass** for PVCs (k3s: `local-path`) +- Traefik ingress (bundled with k3s) +- **10 schedulable worker nodes** for the default replica count -## 2. Copy manifests to the remote machine (scp) +## 1. Image on Docker Hub -From your laptop (or CI), upload the production YAML directory: - -```bash -scp -r k8s/production user@REMOTE:/tmp/dsv-k8s/ -``` +The cluster pulls [noambensim/distributed-secrets-vault:latest](https://hub.docker.com/r/noambensim/distributed-secrets-vault). Skip this step if that tag is already published. -Optional: upload the image tarball to a jump host or each worker: +To build and push a new version: ```bash -scp dsv-backend.tar.gz user@REMOTE:/tmp/ +cp k8s/image.env.example k8s/image.env +docker login +./scripts/docker-build-push.sh ``` -You can apply with `kubectl` from any host that has cluster access; the YAML does not need to live on a control-plane node. - -## 3. Load the image on all workers - -DSV pods only schedule on **workers**. Import the image on **each of the 10 workers** (not required on control-plane nodes unless they run workloads). +Or use GitHub Actions (**Publish Docker image**) with `DOCKERHUB_USERNAME=noambensim` and `DOCKERHUB_TOKEN` secrets. -On each worker (after `scp` of the tarball): +## 2. Copy manifests to the remote machine (optional) ```bash -gunzip -c /tmp/dsv-backend.tar.gz | sudo k3s ctr images import - -sudo k3s ctr images ls | grep dsv-backend +scp -r k8s/production user@REMOTE:/tmp/dsv-k8s/ ``` -Loop from your workstation (adjust hosts and SSH user): +Apply with **`-k`** (Kustomize), not plain `-f`, so the Docker Hub image name is resolved: ```bash -for host in worker1 worker2 worker3 worker4 worker5 worker6 worker7 worker8 worker9 worker10; do - scp dsv-backend.tar.gz user@${host}:/tmp/ - ssh user@${host} 'gunzip -c /tmp/dsv-backend.tar.gz | sudo k3s ctr images import -' -done +kubectl apply -k /tmp/dsv-k8s/ ``` -**Alternative:** push to a private registry, set `image:` in `app-statefulset.yaml`, and add `imagePullSecrets` if needed. - -## 4. Validate manifests (before apply) - -On the remote machine with `kubectl`: +## 3. Validate before apply ```bash -kubectl apply -f /tmp/dsv-k8s/ --dry-run=client -kubectl diff -f /tmp/dsv-k8s/ # optional; shows changes if upgrading -``` +kubectl apply -k k8s/production/ --dry-run=client +# or from remote copy: +kubectl apply -k /tmp/dsv-k8s/ --dry-run=client -Check cluster capacity: - -```bash kubectl get nodes -l '!node-role.kubernetes.io/control-plane,!node-role.kubernetes.io/master' -# Expect 10 Ready workers - kubectl get storageclass -# Expect a default StorageClass for PVCs ``` -## 5. Deploy (order matters for first install) +## 4. Deploy ```bash -kubectl apply -f /tmp/dsv-k8s/namespace.yaml -kubectl apply -f /tmp/dsv-k8s/kafka-service.yaml -kubectl apply -f /tmp/dsv-k8s/kafka-statefulset.yaml -kubectl wait -n dsv --for=condition=ready pod/kafka-0 --timeout=300s - -kubectl apply -f /tmp/dsv-k8s/app-service.yaml -kubectl apply -f /tmp/dsv-k8s/app-statefulset.yaml -kubectl apply -f /tmp/dsv-k8s/ingress.yaml +kubectl apply -k k8s/production/ +kubectl get pods -n dsv -w ``` -Or apply everything at once (Kafka may restart apps until it is ready): +Ordered apply (optional): ```bash -kubectl apply -f /tmp/dsv-k8s/ -kubectl get pods -n dsv -w +kubectl apply -k k8s/production/ --server-side=false # or apply resources individually +kubectl wait -n dsv --for=condition=ready pod/kafka-0 --timeout=300s ``` -Expect: +Expect `kafka-0` and `dsv-app-0` … `dsv-app-9` Running. -- `kafka-0` → Running on a worker -- `dsv-app-0` … `dsv-app-9` → Running (each: `dsv-app` + `redis-sidecar`) - -## 6. Verify +## 5. Verify ```bash kubectl get pods -n dsv -o wide kubectl get pvc -n dsv -kubectl get ingress -n dsv dsv-ingress +kubectl get ingress -n dsv ``` -Port-forward (if ingress is not exposed yet): - ```bash kubectl port-forward -n dsv svc/dsv-app-service 9080:9080 -curl -s http://127.0.0.1:9080/actuator/health | jq . -curl -s http://127.0.0.1:9080/api/v1/cluster/status | jq . -curl -s http://127.0.0.1:9080/api/v1/cluster/nodes | jq . -``` - -`cluster/status` should report **10** healthy nodes once ScaleCube has formed the cluster. - -Via Traefik (k3s default): - -```bash -curl -s http:///actuator/health +curl -s http://127.0.0.1:9080/actuator/health +curl -s http://127.0.0.1:9080/api/v1/cluster/status ``` -## Headlamp - -1. Open the cluster in Headlamp and select the **`dsv`** namespace. -2. **Workloads** → StatefulSets: confirm `dsv-app` (10/10) and `kafka` (1/1). -3. **Storage** → PVCs: all Bound. -4. **Network** → Services / Ingress: `dsv-app-service`, `dsv-ingress`. -5. Use **Pod logs** (`dsv-app` container) if a pod is not Ready. -6. **Port forward** `dsv-app-service` port **9080** for API tests without DNS. +Via Traefik: `curl -s http:///actuator/health` ## Tuning | Setting | Location | Notes | |---------|----------|--------| -| Replica count | `app-statefulset.yaml` `replicas` | Match worker count for one pod per node | -| Shamir / quorum | `JAVA_TOOL_OPTIONS` | For `N` nodes: `k = N/2+1`, `m = k` (see demo script) | -| Ingress host/TLS | `ingress.yaml` | Add `host` and `tls` for production DNS | -| Image registry | `app-statefulset.yaml` `image` | Replace `dsv-backend:latest` | -| Storage size | PVC templates | Redis 5Gi, Kafka 10Gi defaults | +| Docker image | `k8s/production/kustomization.yaml` | `images.newName` / `newTag` | +| Replica count | `app-statefulset.yaml` | Match worker count | +| Shamir / quorum | `JAVA_TOOL_OPTIONS` | For `N` nodes: `k = N/2+1`, `m = k` | +| Ingress host/TLS | `ingress.yaml` | Production DNS | +| Private Hub repo | `imagePullSecrets` | See [docker-hub.md](docker-hub.md) | ## Troubleshooting | Symptom | Cause | Fix | |---------|--------|-----| -| `ErrImagePull` / `ImagePullBackOff` | Image missing on that worker | Import `dsv-backend.tar.gz` on that node | -| `dsv-app-*` Pending | Anti-affinity or no workers | Need 10 schedulable workers; check `kubectl describe pod` | -| PVC Pending | No StorageClass | Install/configure provisioner (e.g. `local-path`) | +| `ErrImagePull` / `ImagePullBackOff` | Image missing on Hub, wrong name/tag, or private without secret | `docker pull` from a worker; check kustomization; add pull secret | +| `ImagePullBackOff` 401 | Private repo | `kubectl create secret docker-registry ...` + `imagePullSecrets` | +| `dsv-app-*` Pending | Anti-affinity / insufficient workers | Need 10 workers; `kubectl describe pod` | +| PVC Pending | No StorageClass | Configure `local-path` or other provisioner | | 503 on writes | Cluster not fully up | Wait for 10 pods; check `/api/v1/cluster/nodes` | -| Ingress 404 | Wrong class | `kubectl get ingressclass`; set `ingressClassName: traefik` | ## Teardown ```bash -kubectl delete -f /tmp/dsv-k8s/ -# PVCs are retained by default; delete manually if you need a clean slate: -# kubectl delete pvc -l app=dsv-app -# kubectl delete pvc -l app=kafka +kubectl delete -k k8s/production/ ``` diff --git a/k8s/README.md b/k8s/README.md index 05aee3c..89a014b 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -2,11 +2,15 @@ This directory contains Kubernetes manifests for Distributed Secrets Vault. The app runs as a StatefulSet, each app pod has a Redis sidecar for shard storage, and Kafka runs as a StatefulSet for commit messaging. +**Deploy with Kustomize** (`kubectl apply -k …`) so the DSV image is resolved to Docker Hub. See [docs/docker-hub.md](../docs/docker-hub.md). + ## Structure ```text k8s/ +├── image.env.example # Docker Hub user/tag for build/push scripts ├── production/ +│ ├── kustomization.yaml # → docker.io/noambensim/distributed-secrets-vault:latest │ ├── namespace.yaml │ ├── app-service.yaml │ ├── app-statefulset.yaml @@ -14,12 +18,9 @@ k8s/ │ ├── kafka-service.yaml │ └── kafka-statefulset.yaml ├── testing/ -│ ├── namespace.yaml -│ ├── app-service.yaml -│ ├── app-statefulset.yaml -│ ├── ingress.yaml -│ ├── kafka-service.yaml -│ └── kafka-statefulset.yaml +│ ├── kustomization.yaml +│ ├── patches/ +│ └── … └── README.md ``` @@ -30,40 +31,38 @@ All resources use the **`dsv`** namespace. - `dsv-app` is a StatefulSet. - Redis runs as a sidecar inside every `dsv-app` pod and persists data through a per-pod PVC. - `dsv-app-headless` exposes pod DNS records for ScaleCube peer discovery. -- `dsv-app-service` load-balances HTTP traffic to healthy app pods on port **9080** (avoids common **8080** conflicts). +- `dsv-app-service` load-balances HTTP traffic to healthy app pods on port **9080**. - Kafka is available at `kafka.dsv.svc.cluster.local:9092`. -The production manifests keep the one-app-pod-per-worker-node placement strategy through node affinity and pod anti-affinity. The testing manifests remove those scheduling constraints for Docker Desktop, Minikube, or K3d. - -## Local Testing +Production keeps one app pod per worker node (affinity + anti-affinity). Testing drops those constraints for local clusters. -Build the local image first: +## Publish image (Docker Hub) ```bash -./mvnw clean package -DskipTests -mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar) -docker build -t dsv-backend:latest . +cp k8s/image.env.example k8s/image.env +docker login +./scripts/docker-build-push.sh ``` -Then deploy: +## Testing environment ```bash -kubectl apply -f k8s/testing/ +kubectl apply -k k8s/testing/ kubectl get pods -n dsv -w ``` -The testing app manifest uses `imagePullPolicy: Never`, so the image must exist in the local cluster's Docker image store. +Pulls the same Docker Hub image (`imagePullPolicy: IfNotPresent`). For fully offline local images, build and tag locally as `docker.io/noambensim/distributed-secrets-vault:latest` or edit `k8s/testing/kustomization.yaml`. -## Production +## Production environment -Tuned for **10 worker nodes** (one `dsv-app` pod per worker, Shamir k=6 / quorum m=6). See [docs/production-kubernetes-deploy.md](../docs/production-kubernetes-deploy.md) for scp, image import on all workers, and verification steps. +Tuned for **10 worker nodes** (Shamir k=6 / quorum m=6). See [docs/production-kubernetes-deploy.md](../docs/production-kubernetes-deploy.md). ```bash -kubectl apply -f k8s/production/ --dry-run=client -kubectl apply -f k8s/production/ +kubectl apply -k k8s/production/ --dry-run=client +kubectl apply -k k8s/production/ ``` -Before production use, load `dsv-backend:latest` on every worker (or switch to a registry image) and set ingress host/TLS as needed. +Workers pull [noambensim/distributed-secrets-vault:latest](https://hub.docker.com/r/noambensim/distributed-secrets-vault) automatically. ## App Environment @@ -80,10 +79,6 @@ Before production use, load `dsv-backend:latest` on every worker (or switch to a ## ScaleCube Discovery -ScaleCube startup is DNS-based: - - `SEED_DNS_HOST=dsv-app-headless.dsv.svc.cluster.local` - `SEED_DNS_PORT=4801` - `CLUSTER_PORT=4801` - -The headless service exposes port `4801` so each worker can resolve and join active peer pods. diff --git a/k8s/image.env.example b/k8s/image.env.example new file mode 100644 index 0000000..f1fe910 --- /dev/null +++ b/k8s/image.env.example @@ -0,0 +1,9 @@ +# Copy to k8s/image.env (optional, gitignored) and adjust for your Docker Hub account. +# Used by scripts/docker-build-push.sh. Keep in sync with k8s/production/kustomization.yaml images. + +DOCKERHUB_USERNAME=noambensim +DSV_IMAGE_NAME=distributed-secrets-vault +DSV_IMAGE_TAG=latest + +# Full reference used for docker build/push (no docker.io prefix required for push) +DOCKERHUB_IMAGE=${DOCKERHUB_USERNAME}/${DSV_IMAGE_NAME} diff --git a/k8s/production/app-statefulset.yaml b/k8s/production/app-statefulset.yaml index 9a253bb..3ad2938 100644 --- a/k8s/production/app-statefulset.yaml +++ b/k8s/production/app-statefulset.yaml @@ -36,6 +36,7 @@ spec: topologyKey: kubernetes.io/hostname containers: - name: dsv-app + # Replaced by k8s/production/kustomization.yaml → noambensim/distributed-secrets-vault: image: dsv-backend:latest imagePullPolicy: IfNotPresent ports: diff --git a/k8s/production/dockerhub-pull-secret.yaml.example b/k8s/production/dockerhub-pull-secret.yaml.example new file mode 100644 index 0000000..9d7201c --- /dev/null +++ b/k8s/production/dockerhub-pull-secret.yaml.example @@ -0,0 +1,16 @@ +# Optional: use when the Docker Hub repository is private. +# 1. kubectl create secret docker-registry dockerhub-credentials \ +# --docker-server=https://index.docker.io/v1/ \ +# --docker-username=YOUR_USER \ +# --docker-password=YOUR_TOKEN_OR_PAT \ +# -n dsv +# 2. Uncomment imagePullSecrets in app-statefulset.yaml, or add this patch via kustomize. +apiVersion: v1 +kind: Secret +metadata: + name: dockerhub-credentials + namespace: dsv +type: kubernetes.io/dockerconfigjson +data: + # Replace with: kubectl create secret ... --dry-run=client -o yaml | yq '.data' + .dockerconfigjson: REPLACE_ME diff --git a/k8s/production/kustomization.yaml b/k8s/production/kustomization.yaml new file mode 100644 index 0000000..d982dae --- /dev/null +++ b/k8s/production/kustomization.yaml @@ -0,0 +1,18 @@ +# Apply with: kubectl apply -k k8s/production/ +# Image name/tag defaults match k8s/image.env.example — edit both when changing registry. +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: dsv +resources: + - namespace.yaml + - kafka-service.yaml + - kafka-statefulset.yaml + - app-service.yaml + - app-statefulset.yaml + - ingress.yaml +images: + - name: dsv-backend + newName: docker.io/noambensim/distributed-secrets-vault + newTag: latest +patches: + - path: patches/app-image-pull-policy.yaml diff --git a/k8s/production/patches/app-image-pull-policy.yaml b/k8s/production/patches/app-image-pull-policy.yaml new file mode 100644 index 0000000..e2e125b --- /dev/null +++ b/k8s/production/patches/app-image-pull-policy.yaml @@ -0,0 +1,12 @@ +# Pull from Docker Hub on every pod start so :latest updates are picked up after a push. +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: dsv-app + namespace: dsv +spec: + template: + spec: + containers: + - name: dsv-app + imagePullPolicy: Always diff --git a/k8s/testing/app-statefulset.yaml b/k8s/testing/app-statefulset.yaml index 60cfb2b..0b84983 100644 --- a/k8s/testing/app-statefulset.yaml +++ b/k8s/testing/app-statefulset.yaml @@ -17,6 +17,7 @@ spec: spec: containers: - name: dsv-app + # Replaced by k8s/testing/kustomization.yaml; pull policy patched to IfNotPresent image: dsv-backend:latest imagePullPolicy: Never ports: diff --git a/k8s/testing/kustomization.yaml b/k8s/testing/kustomization.yaml new file mode 100644 index 0000000..4a2f7c5 --- /dev/null +++ b/k8s/testing/kustomization.yaml @@ -0,0 +1,17 @@ +# Apply with: kubectl apply -k k8s/testing/ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: dsv +resources: + - namespace.yaml + - kafka-service.yaml + - kafka-statefulset.yaml + - app-service.yaml + - app-statefulset.yaml + - ingress.yaml +images: + - name: dsv-backend + newName: docker.io/noambensim/distributed-secrets-vault + newTag: latest +patches: + - path: patches/app-image-pull-policy.yaml diff --git a/k8s/testing/patches/app-image-pull-policy.yaml b/k8s/testing/patches/app-image-pull-policy.yaml new file mode 100644 index 0000000..80a5aba --- /dev/null +++ b/k8s/testing/patches/app-image-pull-policy.yaml @@ -0,0 +1,11 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: dsv-app + namespace: dsv +spec: + template: + spec: + containers: + - name: dsv-app + imagePullPolicy: IfNotPresent diff --git a/scripts/docker-build-push.sh b/scripts/docker-build-push.sh new file mode 100755 index 0000000..0384e24 --- /dev/null +++ b/scripts/docker-build-push.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Build the DSV backend image and push to Docker Hub. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "${ROOT}" + +if [[ -f k8s/image.env ]]; then + # shellcheck disable=SC1091 + source k8s/image.env +elif [[ -f k8s/image.env.example ]]; then + # shellcheck disable=SC1091 + source k8s/image.env.example +fi + +: "${DOCKERHUB_USERNAME:?Set DOCKERHUB_USERNAME in k8s/image.env (see k8s/image.env.example)}" +: "${DSV_IMAGE_NAME:=distributed-secrets-vault}" +: "${DSV_IMAGE_TAG:=latest}" +DOCKERHUB_IMAGE="${DOCKERHUB_IMAGE:-${DOCKERHUB_USERNAME}/${DSV_IMAGE_NAME}}" + +echo "Building ${DOCKERHUB_IMAGE}:${DSV_IMAGE_TAG} ..." +./mvnw clean package -DskipTests +mkdir -p target/dependency +( cd target/dependency && jar -xf ../*.jar ) + +docker build -t "${DOCKERHUB_IMAGE}:${DSV_IMAGE_TAG}" . +docker tag "${DOCKERHUB_IMAGE}:${DSV_IMAGE_TAG}" "docker.io/${DOCKERHUB_IMAGE}:${DSV_IMAGE_TAG}" + +echo "Pushing docker.io/${DOCKERHUB_IMAGE}:${DSV_IMAGE_TAG} ..." +docker push "docker.io/${DOCKERHUB_IMAGE}:${DSV_IMAGE_TAG}" + +echo "Done. Deploy with: kubectl apply -k k8s/production/" diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index e475eaa..0fe91fe 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -1,8 +1,10 @@ # ============================================================================ # Production Profile - Distributed Secrets Vault # ============================================================================ -# Cluster size (totalNodes, thresholdK, quorumM) is set via env in k8s/production. -# Defaults below apply when those env vars are not set. +# Cluster size for the default 10-node production StatefulSet (overridden by JAVA_TOOL_OPTIONS in k8s). +cluster.totalNodes=10 +cluster.thresholdK=6 +cluster.quorumM=6 # HTTP API port (override with SERVER_PORT env in Kubernetes) server.port=9080