diff --git a/.dockerignore b/.dockerignore index feab7a9..9b0d74d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,12 +1,12 @@ -# Keep the musl release binary, ignore the rest +# Keep musl release binaries, ignore build artifacts server/target/debug server/target/release -server/target/x86_64-unknown-linux-musl/debug -server/target/x86_64-unknown-linux-musl/release/build -server/target/x86_64-unknown-linux-musl/release/deps -server/target/x86_64-unknown-linux-musl/release/.fingerprint -server/target/x86_64-unknown-linux-musl/release/incremental -server/target/x86_64-unknown-linux-musl/release/examples +server/target/*/debug +server/target/*/release/build +server/target/*/release/deps +server/target/*/release/.fingerprint +server/target/*/release/incremental +server/target/*/release/examples web/node_modules web/dist *.db diff --git a/Dockerfile b/Dockerfile index afb33d1..dc8403d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,8 @@ FROM alpine:3.21 RUN apk add --no-cache git ca-certificates WORKDIR /app -COPY server/target/x86_64-unknown-linux-musl/release/repo-vis-server ./ +ARG MUSL_TARGET=x86_64-unknown-linux-musl +COPY server/target/${MUSL_TARGET}/release/repo-vis-server ./ COPY --from=frontend /build/dist ./web/dist/ ENV PORT=8080 diff --git a/Makefile b/Makefile index 1d0bd99..124a57a 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,29 @@ -.PHONY: build-server build-web build deploy clean +.PHONY: build-server build-web build deploy deploy-oci clean -MUSL_TARGET := x86_64-unknown-linux-musl +MUSL_TARGET_X86 := x86_64-unknown-linux-musl +MUSL_TARGET_ARM := aarch64-unknown-linux-musl CONTAINER := repo-vis IMAGE := repo-vis:latest PORT := 9120 +OCI_HOST := oci.euphon.net -build-server: - cd server && cargo build --release --target $(MUSL_TARGET) +build-server-x86: + cd server && cargo build --release --target $(MUSL_TARGET_X86) + +build-server-arm: + cd server && cargo build --release --target $(MUSL_TARGET_ARM) build-web: cd web && npm run build -build: build-server build-web +build: build-server-x86 build-web +build-arm: build-server-arm build-web +# --- Local Docker deploy (x86_64) --- deploy: build -docker stop $(CONTAINER) 2>/dev/null -docker rm $(CONTAINER) 2>/dev/null - docker build -t $(IMAGE) . + docker build --build-arg MUSL_TARGET=$(MUSL_TARGET_X86) -t $(IMAGE) . docker run -d \ --name $(CONTAINER) \ -p $(PORT):8080 \ @@ -25,6 +32,37 @@ deploy: build $(IMAGE) @echo "repo-vis running at http://localhost:$(PORT)" +# --- OCI K8s deploy (aarch64, native build on OCI) --- +OCI_TMP = /tmp/repo-vis-deploy +deploy-oci: build-arm + @echo "==> Uploading to OCI..." + ssh $(OCI_HOST) "rm -rf $(OCI_TMP) && mkdir -p $(OCI_TMP)" + scp server/target/$(MUSL_TARGET_ARM)/release/repo-vis-server $(OCI_HOST):$(OCI_TMP)/ + cd web && tar czf /tmp/_rv_dist.tar.gz dist && scp /tmp/_rv_dist.tar.gz $(OCI_HOST):$(OCI_TMP)/ + @echo "==> Building image on OCI..." + ssh $(OCI_HOST) 'cd $(OCI_TMP) && tar xzf _rv_dist.tar.gz && cat > Dockerfile < Applying K8s manifests..." + ssh $(OCI_HOST) "kubectl apply -f -" < k8s/namespace.yaml + ssh $(OCI_HOST) "kubectl apply -f -" < k8s/pvc.yaml + ssh $(OCI_HOST) "kubectl apply -f -" < k8s/deployment.yaml + ssh $(OCI_HOST) "kubectl apply -f -" < k8s/service.yaml + ssh $(OCI_HOST) "kubectl apply -f -" < k8s/ingress.yaml + ssh $(OCI_HOST) "kubectl -n repo-vis rollout restart deployment/repo-vis" + @echo "==> Deployed to https://repo-vis.oci.euphon.net" + clean: cd server && cargo clean rm -rf web/dist diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..3156c64 --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: repo-vis + namespace: repo-vis +spec: + replicas: 1 + selector: + matchLabels: + app: repo-vis + template: + metadata: + labels: + app: repo-vis + spec: + containers: + - name: repo-vis + image: repo-vis:latest + imagePullPolicy: Never + ports: + - containerPort: 8080 + env: + - name: PORT + value: "8080" + - name: FRONTEND_DIR + value: "./web/dist" + - name: DATA_DIR + value: "/data" + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: repo-vis-data diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml new file mode 100644 index 0000000..713a684 --- /dev/null +++ b/k8s/ingress.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: repo-vis + namespace: repo-vis + annotations: + traefik.ingress.kubernetes.io/router.tls.certresolver: le +spec: + ingressClassName: traefik + rules: + - host: repo-vis.oci.euphon.net + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: repo-vis + port: + number: 80 + tls: + - hosts: + - repo-vis.oci.euphon.net diff --git a/k8s/namespace.yaml b/k8s/namespace.yaml new file mode 100644 index 0000000..9c270c1 --- /dev/null +++ b/k8s/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: repo-vis diff --git a/k8s/pvc.yaml b/k8s/pvc.yaml new file mode 100644 index 0000000..a36bf88 --- /dev/null +++ b/k8s/pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: repo-vis-data + namespace: repo-vis +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi diff --git a/k8s/service.yaml b/k8s/service.yaml new file mode 100644 index 0000000..e8ce8a7 --- /dev/null +++ b/k8s/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: repo-vis + namespace: repo-vis +spec: + selector: + app: repo-vis + ports: + - port: 80 + targetPort: 8080 diff --git a/server/.cargo/config.toml b/server/.cargo/config.toml new file mode 100644 index 0000000..7d3d57a --- /dev/null +++ b/server/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-gnu-gcc"