diff --git a/Makefile b/Makefile index c763290..e4ee272 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ HELM_RELEASE ?= go-guess HELM_NAMESPACE ?= go-guess KUBECONFIG ?= $(HOME)/.config/kubectl/rancher.urpi.local.yaml -.PHONY: help dev db-up db-down migrate migrate-down seed backend frontend frontend-install test test-backend test-frontend test-integration test-bdd test-bdd-down test-env test-env-down lint build helm-lint helm-deploy-development helm-deploy-production +.PHONY: help dev db-up db-down migrate migrate-down seed backend frontend frontend-install test test-backend test-frontend test-integration test-bdd test-bdd-down test-env test-env-down lint build rancher-storage helm-lint helm-deploy-development helm-deploy-production help: @echo "make dev builds the demo docker image" @@ -32,6 +32,7 @@ help: @echo "make test-env-down stops the test image" @echo "make lint runs lint on the frontend" @echo "make build builds the entire project" + @echo "make rancher-storage installs the persistent local-path StorageClass" @echo "make helm-lint lints development and production Helm configurations" @echo "make helm-deploy-development deploys the development Helm release" @echo "make helm-deploy-production deploys the production Helm release" @@ -98,6 +99,9 @@ build: cd backend && go build ./... cd frontend && npm run build +rancher-storage: + KUBECONFIG="$(KUBECONFIG)" kubectl apply -f deploy/rancher/local-path.yaml + helm-lint: KUBECONFIG="$(KUBECONFIG)" $(HELM) lint $(HELM_CHART) -f $(HELM_CHART)/values-development.yaml KUBECONFIG="$(KUBECONFIG)" $(HELM) lint $(HELM_CHART) -f $(HELM_CHART)/values-production.yaml diff --git a/README.md b/README.md index 45ef4cc..bfbaf48 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,11 @@ make helm-deploy-development make helm-deploy-production ``` +For the repository's single-node Docker Rancher setup, run +`make rancher-storage` once before the first production deployment. This +installs the default persistent StorageClass documented in +`rancher-install.txt`. + Development uses demo fixtures, ephemeral database and message-broker storage, and NodePort `30080`. Production uses production fixtures, persistent volumes, generated secrets that are preserved across upgrades, and larger resource diff --git a/deploy/rancher/local-path.yaml b/deploy/rancher/local-path.yaml new file mode 100644 index 0000000..6cf033d --- /dev/null +++ b/deploy/rancher/local-path.yaml @@ -0,0 +1,188 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: local-path-storage + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: local-path-provisioner-service-account + namespace: local-path-storage + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: local-path-provisioner-role + namespace: local-path-storage +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: local-path-provisioner-role +rules: + - apiGroups: [""] + resources: ["nodes", "persistentvolumeclaims", "configmaps", "pods", "pods/log"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] + - apiGroups: [""] + resources: ["events"] + verbs: ["create", "patch"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "list", "watch"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: local-path-provisioner-bind + namespace: local-path-storage +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: local-path-provisioner-role +subjects: + - kind: ServiceAccount + name: local-path-provisioner-service-account + namespace: local-path-storage + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: local-path-provisioner-bind +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: local-path-provisioner-role +subjects: + - kind: ServiceAccount + name: local-path-provisioner-service-account + namespace: local-path-storage + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: local-path-provisioner + namespace: local-path-storage +spec: + replicas: 1 + selector: + matchLabels: + app: local-path-provisioner + template: + metadata: + labels: + app: local-path-provisioner + spec: + serviceAccountName: local-path-provisioner-service-account + containers: + - name: local-path-provisioner + image: docker.io/rancher/local-path-provisioner:v0.0.37 + imagePullPolicy: IfNotPresent + command: + - local-path-provisioner + - --debug + - start + - --config + - /etc/config/config.json + ports: + - name: health + containerPort: 8080 + protocol: TCP + startupProbe: + httpGet: + path: /health + port: health + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 12 + livenessProbe: + httpGet: + path: /health + port: health + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: health + initialDelaySeconds: 5 + periodSeconds: 5 + volumeMounts: + - name: config-volume + mountPath: /etc/config/ + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_MOUNT_PATH + value: /etc/config/ + - name: HEALTH_PORT + value: "8080" + volumes: + - name: config-volume + configMap: + name: local-path-config + +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: local-path + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: rancher.io/local-path +volumeBindingMode: WaitForFirstConsumer +reclaimPolicy: Retain + +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: local-path-config + namespace: local-path-storage +data: + config.json: |- + { + "nodePathMap":[ + { + "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES", + "paths":["/var/lib/rancher/k3s/storage"] + } + ] + } + setup: |- + #!/bin/sh + set -eu + mkdir -m 0777 -p "$VOL_DIR" + teardown: |- + #!/bin/sh + set -eu + rm -rf "$VOL_DIR" + helperPod.yaml: |- + apiVersion: v1 + kind: Pod + metadata: + name: helper-pod + spec: + priorityClassName: system-node-critical + tolerations: + - key: node.kubernetes.io/disk-pressure + operator: Exists + effect: NoSchedule + containers: + - name: helper-pod + image: docker.io/library/busybox + imagePullPolicy: IfNotPresent diff --git a/rancher-install.txt b/rancher-install.txt index 2183c2a..4d7b15c 100644 --- a/rancher-install.txt +++ b/rancher-install.txt @@ -110,6 +110,19 @@ After deploying Go Guess, verify the application NodePort: curl --fail http://127.0.0.1:30080/api/health ``` +Production PostgreSQL and RabbitMQ require persistent volumes. Install the +single-node local-path provisioner before the production Helm release: + +```bash +make rancher-storage +make helm-deploy-production +``` + +The provisioner stores data under `/var/lib/rancher/k3s/storage`, which is +inside the persistent `/opt/rancher` host mount. Its StorageClass uses a +`Retain` reclaim policy so deleting a claim does not automatically delete the +underlying production data. + Retrieve the one-time bootstrap password locally, open `https://127.0.0.1:8443`, and immediately choose a permanent administrator password: