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 {
|
||||
provider = "sqlite"
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
@ -55,4 +55,4 @@ model Recipe {
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
}
|
||||
|
||||
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