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:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- deploy/prod/**
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -46,3 +48,33 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }}
|
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:${{ github.sha }}
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ matrix.service }}:main
|
${{ 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.
|
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.
|
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:
|
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:main`
|
||||||
- `ghcr.io/openstaticfish/microservices/scraper:<git-sha>`
|
- `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
|
## Layout
|
||||||
|
|
||||||
@@ -303,6 +305,11 @@ HetznerTerra should reference immutable `<git-sha>` tags for production deployme
|
|||||||
├── flake.nix
|
├── flake.nix
|
||||||
├── doppler.yaml
|
├── doppler.yaml
|
||||||
├── Tiltfile
|
├── Tiltfile
|
||||||
|
├── deploy/
|
||||||
|
│ └── prod/
|
||||||
|
│ ├── kustomization.yaml
|
||||||
|
│ ├── scraper-deployment.yaml
|
||||||
|
│ └── site-analyzer-deployment.yaml
|
||||||
├── k8s/
|
├── k8s/
|
||||||
│ ├── scraper.yaml
|
│ ├── scraper.yaml
|
||||||
│ └── site-analyzer.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