feat: implement Kubernetes deployment infrastructure, migrate database to PostgreSQL, and add CI/CD pipeline
Some checks failed
Build & Deploy to K3s / build-and-deploy (push) Failing after 24s
Some checks failed
Build & Deploy to K3s / build-and-deploy (push) Failing after 24s
This commit is contained in:
parent
3bff3c8600
commit
21c92abc9c
102
.gitea/workflows/deploy.yml
Normal file
102
.gitea/workflows/deploy.yml
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
name: Build & Deploy to K3s
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: git.arthurbarre.fr
|
||||||
|
BACKEND_IMAGE: git.arthurbarre.fr/ordinarthur/freedge-backend
|
||||||
|
FRONTEND_IMAGE: git.arthurbarre.fr/ordinarthur/freedge-frontend
|
||||||
|
REGISTRY_USER: ordinarthur
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Login to Gitea Container Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
|
||||||
|
docker login ${{ env.REGISTRY }} -u ${{ env.REGISTRY_USER }} --password-stdin
|
||||||
|
|
||||||
|
- name: Build backend image
|
||||||
|
run: |
|
||||||
|
docker build \
|
||||||
|
-t ${{ env.BACKEND_IMAGE }}:${{ github.sha }} \
|
||||||
|
-t ${{ env.BACKEND_IMAGE }}:latest \
|
||||||
|
./backend
|
||||||
|
|
||||||
|
- name: Build frontend image
|
||||||
|
run: |
|
||||||
|
docker build \
|
||||||
|
--build-arg VITE_API_BASE_URL=https://freedge.app/api \
|
||||||
|
--build-arg VITE_GOOGLE_CLIENT_ID=173866668387-i18igc0e1avqtsaqq6nig898bv6pvuk6.apps.googleusercontent.com \
|
||||||
|
-t ${{ env.FRONTEND_IMAGE }}:${{ github.sha }} \
|
||||||
|
-t ${{ env.FRONTEND_IMAGE }}:latest \
|
||||||
|
./frontend
|
||||||
|
|
||||||
|
- name: Push backend image
|
||||||
|
run: |
|
||||||
|
docker push ${{ env.BACKEND_IMAGE }}:${{ github.sha }}
|
||||||
|
docker push ${{ env.BACKEND_IMAGE }}:latest
|
||||||
|
|
||||||
|
- name: Push frontend image
|
||||||
|
run: |
|
||||||
|
docker push ${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
|
||||||
|
docker push ${{ env.FRONTEND_IMAGE }}:latest
|
||||||
|
|
||||||
|
- name: Install kubectl
|
||||||
|
run: |
|
||||||
|
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||||
|
chmod +x kubectl
|
||||||
|
mv kubectl /usr/local/bin/kubectl
|
||||||
|
|
||||||
|
- name: Configure kubeconfig
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.kube
|
||||||
|
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
|
||||||
|
|
||||||
|
- name: Apply namespace and shared resources
|
||||||
|
run: |
|
||||||
|
kubectl apply -f k8s/namespace.yml
|
||||||
|
kubectl apply -f k8s/configmap.yml
|
||||||
|
kubectl apply -f k8s/pvc.yml
|
||||||
|
kubectl apply -f k8s/service.yml
|
||||||
|
|
||||||
|
- name: Create image pull secret
|
||||||
|
run: |
|
||||||
|
kubectl -n freedge create secret docker-registry gitea-registry-secret \
|
||||||
|
--docker-server=${{ env.REGISTRY }} \
|
||||||
|
--docker-username=${{ env.REGISTRY_USER }} \
|
||||||
|
--docker-password="${{ secrets.REGISTRY_PASSWORD }}" \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
- name: Create app secrets
|
||||||
|
run: |
|
||||||
|
kubectl -n freedge create secret generic freedge-secrets \
|
||||||
|
--from-literal=DATABASE_URL="${{ secrets.DATABASE_URL }}" \
|
||||||
|
--from-literal=JWT_SECRET="${{ secrets.JWT_SECRET }}" \
|
||||||
|
--from-literal=OPENAI_API_KEY="${{ secrets.OPENAI_API_KEY }}" \
|
||||||
|
--from-literal=STRIPE_SECRET_KEY="${{ secrets.STRIPE_SECRET_KEY }}" \
|
||||||
|
--from-literal=STRIPE_WEBHOOK_SECRET="${{ secrets.STRIPE_WEBHOOK_SECRET }}" \
|
||||||
|
--from-literal=STRIPE_PRICE_ID_ESSENTIAL="${{ secrets.STRIPE_PRICE_ID_ESSENTIAL }}" \
|
||||||
|
--from-literal=STRIPE_PRICE_ID_PREMIUM="${{ secrets.STRIPE_PRICE_ID_PREMIUM }}" \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
- name: Deploy workloads
|
||||||
|
run: |
|
||||||
|
kubectl apply -f k8s/deployment.yml
|
||||||
|
kubectl -n freedge set image deployment/freedge-backend \
|
||||||
|
freedge-backend=${{ env.BACKEND_IMAGE }}:${{ github.sha }}
|
||||||
|
kubectl -n freedge set image deployment/freedge-frontend \
|
||||||
|
freedge-frontend=${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
|
||||||
|
kubectl -n freedge rollout status deployment/freedge-backend --timeout=180s
|
||||||
|
kubectl -n freedge rollout status deployment/freedge-frontend --timeout=180s
|
||||||
|
kubectl -n freedge rollout status deployment/freedge-proxy --timeout=180s
|
||||||
|
|
||||||
|
- name: Cleanup old images
|
||||||
|
run: |
|
||||||
|
docker image prune -f
|
||||||
27
backend/Dockerfile
Normal file
27
backend/Dockerfile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
FROM node:20-slim AS build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npx prisma generate
|
||||||
|
RUN npm run build
|
||||||
|
RUN npm prune --omit=dev
|
||||||
|
|
||||||
|
FROM node:20-slim
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=3000
|
||||||
|
|
||||||
|
COPY --from=build /app/package*.json ./
|
||||||
|
COPY --from=build /app/node_modules ./node_modules
|
||||||
|
COPY --from=build /app/prisma ./prisma
|
||||||
|
COPY --from=build /app/dist ./dist
|
||||||
|
COPY --from=build /app/uploads ./uploads
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["node", "dist/server.js"]
|
||||||
@ -3,7 +3,7 @@ generator client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "sqlite"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
k8s/configmap.yml
Normal file
42
k8s/configmap.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: freedge-config
|
||||||
|
namespace: freedge
|
||||||
|
data:
|
||||||
|
NODE_ENV: "production"
|
||||||
|
PORT: "3000"
|
||||||
|
LOG_LEVEL: "info"
|
||||||
|
CORS_ORIGINS: "https://freedge.app"
|
||||||
|
FRONTEND_URL: "https://freedge.app"
|
||||||
|
PUBLIC_BASE_URL: "https://freedge.app/api"
|
||||||
|
OPENAI_TEXT_MODEL: "gpt-4o-mini"
|
||||||
|
OPENAI_TRANSCRIBE_MODEL: "gpt-4o-mini-transcribe"
|
||||||
|
ENABLE_IMAGE_GENERATION: "true"
|
||||||
|
OPENAI_IMAGE_MODEL: "gpt-image-1"
|
||||||
|
OPENAI_IMAGE_QUALITY: "medium"
|
||||||
|
OPENAI_IMAGE_SIZE: "1024x1024"
|
||||||
|
OPENAI_MAX_RETRIES: "3"
|
||||||
|
OPENAI_TIMEOUT_MS: "60000"
|
||||||
|
proxy.conf: |
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 20M;
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
rewrite ^/api/(.*) /$1 break;
|
||||||
|
proxy_pass http://freedge-backend:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://freedge-frontend:80;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
122
k8s/deployment.yml
Normal file
122
k8s/deployment.yml
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: freedge-backend
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: freedge-backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: freedge-backend
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: gitea-registry-secret
|
||||||
|
initContainers:
|
||||||
|
- name: prisma-db-push
|
||||||
|
image: git.arthurbarre.fr/ordinarthur/freedge-backend:latest
|
||||||
|
command: ["sh", "-c", "npx prisma db push --skip-generate"]
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: freedge-config
|
||||||
|
- secretRef:
|
||||||
|
name: freedge-secrets
|
||||||
|
volumeMounts:
|
||||||
|
- name: uploads
|
||||||
|
mountPath: /app/uploads
|
||||||
|
containers:
|
||||||
|
- name: freedge-backend
|
||||||
|
image: git.arthurbarre.fr/ordinarthur/freedge-backend:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: freedge-config
|
||||||
|
- secretRef:
|
||||||
|
name: freedge-secrets
|
||||||
|
volumeMounts:
|
||||||
|
- name: uploads
|
||||||
|
mountPath: /app/uploads
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 3000
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 30
|
||||||
|
volumes:
|
||||||
|
- name: uploads
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: freedge-uploads
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: freedge-frontend
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: freedge-frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: freedge-frontend
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: gitea-registry-secret
|
||||||
|
containers:
|
||||||
|
- name: freedge-frontend
|
||||||
|
image: git.arthurbarre.fr/ordinarthur/freedge-frontend:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: freedge-proxy
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: freedge-proxy
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: freedge-proxy
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
volumeMounts:
|
||||||
|
- name: proxy-config
|
||||||
|
mountPath: /etc/nginx/conf.d/default.conf
|
||||||
|
subPath: proxy.conf
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
volumes:
|
||||||
|
- name: proxy-config
|
||||||
|
configMap:
|
||||||
|
name: freedge-config
|
||||||
4
k8s/namespace.yml
Normal file
4
k8s/namespace.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: freedge
|
||||||
12
k8s/pvc.yml
Normal file
12
k8s/pvc.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: freedge-uploads
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: local-path
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
40
k8s/service.yml
Normal file
40
k8s/service.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: freedge-backend
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: freedge-backend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: freedge-frontend
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: freedge-frontend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: freedge-proxy
|
||||||
|
namespace: freedge
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app: freedge-proxy
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
nodePort: 30082
|
||||||
Loading…
x
Reference in New Issue
Block a user