Try to connect up nginx in prod server
Some checks failed
Podman Rootless Demo / test-backend (push) Has been skipped
Podman Rootless Demo / test-frontend (push) Has been skipped
Podman Rootless Demo / build-backend (push) Has been skipped
Podman Rootless Demo / build-frontend (push) Has been skipped
Podman Rootless Demo / deploy-prod (push) Failing after 20s

This commit is contained in:
continuist 2025-09-20 20:31:24 -04:00
parent 9a927bcb8b
commit 80f8f75208
3 changed files with 58 additions and 140 deletions

View file

@ -245,6 +245,21 @@ jobs:
podman --remote pull "$REGISTRY_HOST/$APP_NAME/sharenet-backend-api-postgres:$IMAGE_TAG" podman --remote pull "$REGISTRY_HOST/$APP_NAME/sharenet-backend-api-postgres:$IMAGE_TAG"
podman --remote pull "$REGISTRY_HOST/$APP_NAME/sharenet-frontend:$IMAGE_TAG" podman --remote pull "$REGISTRY_HOST/$APP_NAME/sharenet-frontend:$IMAGE_TAG"
- name: Prepare in-pod nginx config on host
run: |
set -euo pipefail
# create dir on host (via user namespace)
podman --remote unshare mkdir -p /opt/sharenet/nginx /opt/sharenet/volumes/nginx-cache
# render temp config (inside the job container)
apk add --no-cache gettext >/dev/null
envsubst < nginx/nginx.conf > /tmp/nginx.conf
# write it onto the host
podman --remote unshare sh -c 'cat > /opt/sharenet/nginx/nginx.conf' < /tmp/nginx.conf
# reasonable perms for rootless mount
podman --remote unshare chown -R 1001:1001 /opt/sharenet
podman --remote unshare chmod 0755 /opt/sharenet /opt/sharenet/nginx /opt/sharenet/volumes /opt/sharenet/volumes/nginx-cache
podman --remote unshare chmod 0644 /opt/sharenet/nginx/nginx.conf || true
- name: Install envsubst (Alpine) - name: Install envsubst (Alpine)
run: apk add --no-cache gettext run: apk add --no-cache gettext

View file

@ -150,27 +150,19 @@ spec:
capabilities: capabilities:
drop: ["ALL"] drop: ["ALL"]
ports: ports:
- containerPort: 80 - containerPort: 8080 # inside pod
hostPort: 8080 hostIP: 127.0.0.1 # only loopback on host
protocol: TCP hostPort: 18080 # high port exposed to host
- containerPort: 443 - containerPort: 8090 # health inside pod (not exposed)
hostPort: 8443
protocol: TCP
volumeMounts: volumeMounts:
- { name: nginx-run, mountPath: /var/run, readOnly: false } - { name: nginx-run, mountPath: /var/run, readOnly: false }
- { name: nginx-conf, mountPath: /etc/nginx/nginx.conf, readOnly: true, subPath: nginx.conf } - { name: nginx-cache, mountPath: /var/cache/nginx, readOnly: false }
- { name: nginx-cache, mountPath: /var/cache/nginx, readOnly: false } - { name: nginx-conf, mountPath: /etc/nginx/nginx.conf, readOnly: true, subPath: nginx.conf }
- { name: letsencrypt, mountPath: /etc/letsencrypt, readOnly: true }
# Health check # Health check
livenessProbe: livenessProbe:
httpGet: httpGet: { path: /healthz, port: 8090, scheme: HTTP }
path: /healthz
port: 8090
scheme: HTTP
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 30 periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
# Resource limits # Resource limits
resources: resources:
requests: requests:
@ -193,15 +185,7 @@ spec:
emptyDir: { medium: Memory } emptyDir: { medium: Memory }
- name: nginx-run - name: nginx-run
emptyDir: {} emptyDir: {}
- name: nginx-conf
hostPath:
path: /opt/sharenet/nginx
type: Directory
- name: nginx-cache - name: nginx-cache
hostPath: hostPath: { path: /opt/sharenet/volumes/nginx-cache, type: DirectoryOrCreate }
path: /opt/sharenet/volumes/nginx-cache - name: nginx-conf
type: DirectoryOrCreate hostPath: { path: /opt/sharenet/nginx, type: Directory }
- name: letsencrypt
hostPath:
path: /etc/letsencrypt
type: Directory

View file

@ -1,118 +1,37 @@
events { user nginx;
worker_connections 1024; worker_processes auto;
} pid /var/run/nginx.pid;
events { worker_connections 1024; }
http { http {
upstream frontend { sendfile on;
server frontend:3000; tcp_nopush on;
tcp_nodelay on;
# health
server {
listen 8090;
location = /healthz { return 200 "ok\n"; add_header Content-Type text/plain; }
}
# public HTTP entrypoint (host will terminate TLS and proxy here)
server {
listen 8080;
# frontend default
location / {
proxy_pass http://127.0.0.1:${PROD_FRONTEND_PORT};
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
} }
upstream backend { # backend API
server backend:3001; location /api/ {
} proxy_pass http://127.0.0.1:${PROD_BACKEND_PORT}/;
proxy_set_header Host $host;
# Rate limiting proxy_set_header X-Forwarded-For $remote_addr;
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; proxy_set_header X-Forwarded-Proto $scheme;
limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
server {
listen 80;
server_name _;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name _;
# SSL configuration
# These paths match Let's Encrypt certificate files copied in the CI/CD setup guide
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Frontend routes
location / {
limit_req zone=frontend burst=20 nodelay;
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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 $scheme;
proxy_cache_bypass $http_upgrade;
}
# API routes
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://backend/;
proxy_http_version 1.1;
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 $scheme;
# CORS headers
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always;
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
}
# Health check endpoint
location /health {
access_log off;
proxy_pass http://backend/health;
proxy_http_version 1.1;
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 $scheme;
add_header Content-Type application/json;
}
} }
}
} }