diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 738ac1f..39a769d 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -40,7 +40,7 @@ jobs: needs: test if: github.event_name == 'push' permissions: - contents: read + contents: write packages: write steps: @@ -86,7 +86,15 @@ jobs: KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} with: args: |- - # Update both deployments with new image tags - sed -i 's|image: .*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:1.0.${{ github.run_number }}|' k8s/manifest.yaml + # Render template → manifest with the actual image tag + sed 's|__IMAGE_TAG__|1.0.${{ github.run_number }}|g' k8s/manifest.template.yaml > k8s/manifest.yaml # Apply Kubernetes manifests kubectl --insecure-skip-tls-verify apply -n ${{ env.K8S_NAMESPACE }} -f k8s/manifest.yaml + + - name: Commit updated manifest + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add k8s/manifest.yaml + git commit -m "chore: update manifest image to 1.0.${{ github.run_number }} [skip ci]" + git push diff --git a/k8s/manifest.template.yaml b/k8s/manifest.template.yaml new file mode 100644 index 0000000..e842553 --- /dev/null +++ b/k8s/manifest.template.yaml @@ -0,0 +1,320 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: feiniu-images-nfs-apps +spec: + capacity: + storage: 100Gi + accessModes: + - ReadWriteMany + nfs: + server: 192.168.1.5 + path: "/fs/1000/nfs/data/images" + +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: "feiniu-images-nfs-apps" +spec: + storageClassName: "" + volumeName: feiniu-images-nfs-apps + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + +--- + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: uploads-nfs-apps +spec: + capacity: + storage: 1000Gi + accessModes: + - ReadWriteMany + nfs: + server: 192.168.1.5 + path: "/fs/1000/nfs/uploads" + +--- + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: "uploads-nfs-apps" +spec: + storageClassName: "" + volumeName: uploads-nfs-apps + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1000Gi + +# --- +# apiVersion: v1 +# kind: PersistentVolumeClaim +# metadata: +# name: links-pvc +# spec: +# accessModes: +# - ReadWriteOnce +# storageClassName: nfs-client +# resources: +# requests: +# storage: 2Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: links + labels: + tags.datadoghq.com/env: "prod" + tags.datadoghq.com/service: "links" +spec: + selector: + matchLabels: + app: links + replicas: 1 + template: + metadata: + labels: + app: links + tags.datadoghq.com/env: "prod" + tags.datadoghq.com/service: "links" + spec: + serviceAccountName: links + volumes: + - name: downloads + persistentVolumeClaim: + claimName: feiniu-images-nfs-apps + - name: data + persistentVolumeClaim: + claimName: links-local-pvc + - name: uploads + persistentVolumeClaim: + claimName: uploads-nfs-apps + - name: cache + emptyDir: {} + initContainers: + - name: links-init + image: "ghcr.io/wahyd4/links:__IMAGE_TAG__" + command: ["sh", "-c", "uv run manage.py migrate && uv run manage.py rebuild_search_index"] + volumeMounts: + - name: data + mountPath: /app/data + containers: + - name: links + image: "ghcr.io/wahyd4/links:__IMAGE_TAG__" + securityContext: + runAsUser: 1000 + imagePullPolicy: Always + volumeMounts: + - name: data + mountPath: /app/data + - name: cache + mountPath: /app/.cache + - name: downloads + mountPath: /images + - name: uploads + mountPath: /uploads + env: + - name : DEBUG + value: "false" + - name: FILE_UPLOADS_FOLDER + value: "/uploads" + - name: R2_CUSTOM_DOMAIN + value: home-links-prod.junv.cc + - name: DB_HOST + value: new-postgres-postgresql.db.svc.cluster.local + - name: DB_NAME + value: badges + - name: DB_USERNAME + value: postgres + # - name: DB_PASSWORD + # valueFrom: + # { secretKeyRef: { name: database-credentials, key: password } } + - name: CSRF_TRUSTED_ORIGINS + value: "https://to.junv.cc,https://go.junv.cc,http://go" + - name: UV_CACHE_DIR + value: "/app/.cache/uv" + - name: R2_BUCKET_NAME + value: "home-links-prod" + - name: R2_ENDPOINT_URL + value: https://d39b5aca439164602c01f7af2a58d4bf.r2.cloudflarestorage.com + - name: IMAGES_FOLDER + value: "/images" + - name: R2_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: r2-credentials + key: access_key + - name: R2_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: r2-credentials + key: key_id + - name: REDIS_URL + value: "redis://redis.db.svc.cluster.local:6379/0" + - name: CRAWL4AI_API_URL + value: "http://crawl4ai.ai.svc.cluster.local:80" + - name: CRAWL4AI_ENABLED + value: "true" + - name: QDRANT_SYNC_ENABLED + value: "true" + - name: QDRANT_HOST + value: "192.168.1.2" + - name: OLLAMA_URL + value: "http://ollama-service.ollama.svc.cluster.local:11434" + ports: + - containerPort: 8000 + name: links-port + protocol: TCP + resources: + requests: + cpu: 200m + memory: 400Mi + limits: + cpu: 1200m + memory: 2Gi + imagePullSecrets: + - name: github-image-pull-secret +--- +apiVersion: v1 +kind: Service +metadata: + name: links + labels: + app: links +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: links-port + protocol: TCP + name: web + selector: + app: links + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: links-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + kubernetes.io/tls-acme: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/proxy-body-size: 1024m + nginx.ingress.kubernetes.io/auth-url: "https://pass.junv.cc/oauth2/auth" + nginx.ingress.kubernetes.io/auth-signin: "https://pass.junv.cc/oauth2/start?rd=https://$host$escaped_request_uri" + # Skip authentication for /public, but looks like doesn't work + # nginx.ingress.kubernetes.io/auth-snippet: | + # if ($request_uri ~ "/public") { + # return 200; + # } +spec: + ingressClassName: nginx + tls: + - hosts: + - to.junv.cc + - go.junv.cc + secretName: links-tls + rules: + - host: go.junv.cc + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: links + port: + number: 80 + - host: to.junv.cc + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: links + port: + number: 80 +--- + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: go-links-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + kubernetes.io/tls-acme: "false" + nginx.ingress.kubernetes.io/proxy-body-size: 1024m +spec: + ingressClassName: nginx + rules: + - host: go + http: + paths: + - backend: + service: + name: links + port: + number: 80 + path: / + pathType: Prefix + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: public-links-ingress + annotations: + kubernetes.io/ingress.class: "nginx" + kubernetes.io/tls-acme: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/proxy-body-size: 100m +spec: + tls: + - hosts: + - xgo.junv.cc + secretName: public-links-tls + rules: + - host: xgo.junv.cc + http: + paths: + - path: /public + pathType: Prefix + backend: + service: + name: links + port: + number: 80 + - path: /static + pathType: Prefix + backend: + service: + name: links + port: + number: 80 + - path: /favicon.ico + pathType: Prefix + backend: + service: + name: links + port: + number: 80 + +--- +# ServiceAccount for the links pod +apiVersion: v1 +kind: ServiceAccount +metadata: + name: links + namespace: apps