mirror of
https://github.com/OpenStaticFish/MicroServices.git
synced 2026-10-13 06:35:12 +00:00
feat: own production deployment manifests
This commit is contained in:
@@ -4,9 +4,11 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- deploy/prod/**
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
env:
|
||||
@@ -46,3 +48,33 @@ jobs:
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }}
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:main
|
||||
|
||||
update-deploy:
|
||||
name: Update production image tags
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update deploy/prod image tags
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sed -i -E "/name: ghcr\.io\/openstaticfish\/microservices\/site-analyzer/{n;s/newTag: .*/newTag: ${GITHUB_SHA}/;}" deploy/prod/kustomization.yaml
|
||||
sed -i -E "/name: ghcr\.io\/openstaticfish\/microservices\/scraper/{n;s/newTag: .*/newTag: ${GITHUB_SHA}/;}" deploy/prod/kustomization.yaml
|
||||
|
||||
- name: Commit production image tag update
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if git diff --quiet -- deploy/prod/kustomization.yaml; then
|
||||
echo "deploy/prod image tags already match ${GITHUB_SHA}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email github-actions[bot]@users.noreply.github.com
|
||||
git add deploy/prod/kustomization.yaml
|
||||
git commit -m "chore: update production image tags [skip ci]"
|
||||
git push
|
||||
|
||||
@@ -281,11 +281,11 @@ doppler run -- your-command
|
||||
|
||||
Tilt uses Doppler to create the local Kubernetes `webshare-api` secret for the scraper service. Do not commit local `.env` files; `.env` and `.env.*` are ignored by Git.
|
||||
|
||||
## Production Images
|
||||
## Production Deployment
|
||||
|
||||
Tilt is local-development only in this repo. It continues to build local images named `site-analyzer` and `scraper` for the Kind/Tilt workflow.
|
||||
|
||||
Production deployment is owned by the HetznerTerra GitOps repo. This repo publishes container images only; it is not the source of truth for production Kubernetes deployment manifests.
|
||||
Production Kubernetes manifests live in `deploy/prod` in this repo. HetznerTerra owns the cluster and subscribes Flux to this repository; adding or changing MicroServices workloads should happen here, not in the infrastructure repo.
|
||||
|
||||
On pushes to `main`, GitHub Actions builds and pushes these GHCR images:
|
||||
|
||||
@@ -294,7 +294,9 @@ On pushes to `main`, GitHub Actions builds and pushes these GHCR images:
|
||||
- `ghcr.io/openstaticfish/microservices/scraper:main`
|
||||
- `ghcr.io/openstaticfish/microservices/scraper:<git-sha>`
|
||||
|
||||
HetznerTerra should reference immutable `<git-sha>` tags for production deployments. Runtime secrets, including the scraper Webshare configuration, must be supplied by the deployment environment and are not baked into images.
|
||||
After pushing images, CI updates `deploy/prod/kustomization.yaml` to the immutable `<git-sha>` tag and commits that change back to `main`. Flux then applies the production manifests from this repo.
|
||||
|
||||
Runtime secrets, including the scraper Webshare configuration, stay in Doppler project `openstaticfish-microservices` config `dev`. They are exposed to the cluster through External Secrets and are not baked into images or stored in HetznerTerra.
|
||||
|
||||
## Layout
|
||||
|
||||
@@ -303,6 +305,11 @@ HetznerTerra should reference immutable `<git-sha>` tags for production deployme
|
||||
├── flake.nix
|
||||
├── doppler.yaml
|
||||
├── Tiltfile
|
||||
├── deploy/
|
||||
│ └── prod/
|
||||
│ ├── kustomization.yaml
|
||||
│ ├── scraper-deployment.yaml
|
||||
│ └── site-analyzer-deployment.yaml
|
||||
├── k8s/
|
||||
│ ├── scraper.yaml
|
||||
│ └── site-analyzer.yaml
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: microservices
|
||||
namespace: microservices
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`apps.silverside-gopher.ts.net`) && PathPrefix(`/site-analyzer`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: microservices-strip-prefix
|
||||
services:
|
||||
- name: site-analyzer
|
||||
port: 8090
|
||||
- match: Host(`apps.silverside-gopher.ts.net`) && PathPrefix(`/scraper`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: microservices-strip-prefix
|
||||
services:
|
||||
- name: scraper
|
||||
port: 8080
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- webshare-api-externalsecret.yaml
|
||||
- site-analyzer-deployment.yaml
|
||||
- site-analyzer-service.yaml
|
||||
- scraper-deployment.yaml
|
||||
- scraper-service.yaml
|
||||
- traefik-middleware-strip-prefix.yaml
|
||||
- ingressroute-microservices.yaml
|
||||
images:
|
||||
- name: ghcr.io/openstaticfish/microservices/site-analyzer
|
||||
newTag: 4c6b1dc752feab5ef4c52f40d5c97a85ec59bcba
|
||||
- name: ghcr.io/openstaticfish/microservices/scraper
|
||||
newTag: 4c6b1dc752feab5ef4c52f40d5c97a85ec59bcba
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: microservices
|
||||
@@ -0,0 +1,64 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: scraper
|
||||
namespace: microservices
|
||||
labels:
|
||||
app: scraper
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: scraper
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: scraper
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: ghcr-pull-secret
|
||||
containers:
|
||||
- name: scraper
|
||||
image: ghcr.io/openstaticfish/microservices/scraper:4c6b1dc752feab5ef4c52f40d5c97a85ec59bcba
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: PORT
|
||||
value: "8080"
|
||||
- name: WEBSHARE_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: webshare-api
|
||||
key: api-key
|
||||
optional: true
|
||||
- name: WEBSHARE_PROXY_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: webshare-api
|
||||
key: proxy-username
|
||||
optional: true
|
||||
- name: WEBSHARE_PROXY_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: webshare-api
|
||||
key: proxy-password
|
||||
optional: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: scraper
|
||||
namespace: microservices
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: scraper
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
@@ -0,0 +1,80 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: site-analyzer
|
||||
namespace: microservices
|
||||
labels:
|
||||
app: site-analyzer
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: site-analyzer
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: site-analyzer
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: ghcr-pull-secret
|
||||
terminationGracePeriodSeconds: 30
|
||||
containers:
|
||||
- name: site-analyzer
|
||||
image: ghcr.io/openstaticfish/microservices/site-analyzer:4c6b1dc752feab5ef4c52f40d5c97a85ec59bcba
|
||||
ports:
|
||||
- containerPort: 8090
|
||||
env:
|
||||
- name: PORT
|
||||
value: "8090"
|
||||
- name: MAX_CONCURRENT_ANALYSES
|
||||
value: "20"
|
||||
- name: ANALYSIS_TIMEOUT
|
||||
value: 15s
|
||||
- name: FETCH_TIMEOUT
|
||||
value: 10s
|
||||
- name: MAX_REQUEST_BYTES
|
||||
value: "4096"
|
||||
- name: MAX_RESPONSE_BYTES
|
||||
value: "2097152"
|
||||
- name: READ_HEADER_TIMEOUT
|
||||
value: 2s
|
||||
- name: READ_TIMEOUT
|
||||
value: 5s
|
||||
- name: WRITE_TIMEOUT
|
||||
value: 20s
|
||||
- name: IDLE_TIMEOUT
|
||||
value: 60s
|
||||
- name: SHUTDOWN_TIMEOUT
|
||||
value: 25s
|
||||
- name: MAX_IDLE_CONNS
|
||||
value: "200"
|
||||
- name: MAX_IDLE_CONNS_PER_HOST
|
||||
value: "20"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8090
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 1
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8090
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 3
|
||||
timeoutSeconds: 1
|
||||
failureThreshold: 2
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: site-analyzer
|
||||
namespace: microservices
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: site-analyzer
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 8090
|
||||
targetPort: 8090
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: microservices-strip-prefix
|
||||
namespace: microservices
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /site-analyzer
|
||||
- /scraper
|
||||
@@ -0,0 +1,29 @@
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: webshare-api
|
||||
namespace: microservices
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: doppler-openstaticfish-microservices
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: webshare-api
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: Opaque
|
||||
data:
|
||||
api-key: "{{ .webshareApiKey }}"
|
||||
proxy-username: "{{ .webshareProxyUsername }}"
|
||||
proxy-password: "{{ .webshareProxyPassword }}"
|
||||
data:
|
||||
- secretKey: webshareApiKey
|
||||
remoteRef:
|
||||
key: WEBSHARE_API_KEY
|
||||
- secretKey: webshareProxyUsername
|
||||
remoteRef:
|
||||
key: WEBSHARE_PROXY_USERNAME
|
||||
- secretKey: webshareProxyPassword
|
||||
remoteRef:
|
||||
key: WEBSHARE_PROXY_PASSWORD
|
||||
Reference in New Issue
Block a user