feat: configure dedicated server proxy
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
e787359c54
commit
a86a0cb539
8 changed files with 91 additions and 91 deletions
16
.github/workflows/deploy-rancher.yml
vendored
16
.github/workflows/deploy-rancher.yml
vendored
|
|
@ -43,9 +43,7 @@ jobs:
|
||||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
JWT_SECRET: ${{ secrets.GO_GUESS_JWT_SECRET }}
|
JWT_SECRET: ${{ secrets.GO_GUESS_JWT_SECRET }}
|
||||||
GO_GUESS_HOST: ${{ vars.GO_GUESS_HOST }}
|
|
||||||
GO_GUESS_FRONTEND_URL: ${{ vars.GO_GUESS_FRONTEND_URL }}
|
GO_GUESS_FRONTEND_URL: ${{ vars.GO_GUESS_FRONTEND_URL }}
|
||||||
GO_GUESS_TLS_SECRET: ${{ vars.GO_GUESS_TLS_SECRET }}
|
|
||||||
RANCHER_NAMESPACE: ${{ vars.RANCHER_NAMESPACE }}
|
RANCHER_NAMESPACE: ${{ vars.RANCHER_NAMESPACE }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -82,10 +80,6 @@ jobs:
|
||||||
echo "GO_GUESS_JWT_SECRET must contain at least 32 characters" >&2
|
echo "GO_GUESS_JWT_SECRET must contain at least 32 characters" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$GO_GUESS_HOST" ]; then
|
|
||||||
echo "GO_GUESS_HOST is required for production" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
namespace="${RANCHER_NAMESPACE:-go-guess-$TARGET_ENVIRONMENT}"
|
namespace="${RANCHER_NAMESPACE:-go-guess-$TARGET_ENVIRONMENT}"
|
||||||
if [ -n "$PUBLISHED_SHA" ]; then
|
if [ -n "$PUBLISHED_SHA" ]; then
|
||||||
image_tag="$PUBLISHED_SHA"
|
image_tag="$PUBLISHED_SHA"
|
||||||
|
|
@ -94,7 +88,7 @@ jobs:
|
||||||
fi
|
fi
|
||||||
frontend_url="$GO_GUESS_FRONTEND_URL"
|
frontend_url="$GO_GUESS_FRONTEND_URL"
|
||||||
if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$frontend_url" ]; then
|
if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$frontend_url" ]; then
|
||||||
frontend_url="https://$GO_GUESS_HOST"
|
frontend_url="https://goguess.urpi.be"
|
||||||
fi
|
fi
|
||||||
{
|
{
|
||||||
echo "DEPLOY_NAMESPACE=$namespace"
|
echo "DEPLOY_NAMESPACE=$namespace"
|
||||||
|
|
@ -130,14 +124,6 @@ jobs:
|
||||||
if [ -n "$RESOLVED_FRONTEND_URL" ]; then
|
if [ -n "$RESOLVED_FRONTEND_URL" ]; then
|
||||||
set -- "$@" --set-string "app.frontendURL=$RESOLVED_FRONTEND_URL"
|
set -- "$@" --set-string "app.frontendURL=$RESOLVED_FRONTEND_URL"
|
||||||
fi
|
fi
|
||||||
if [ "$TARGET_ENVIRONMENT" = "production" ]; then
|
|
||||||
set -- "$@" --set-string "ingress.hosts[0].host=$GO_GUESS_HOST"
|
|
||||||
if [ -n "$GO_GUESS_TLS_SECRET" ]; then
|
|
||||||
set -- "$@" \
|
|
||||||
--set-string "ingress.tls[0].secretName=$GO_GUESS_TLS_SECRET" \
|
|
||||||
--set-string "ingress.tls[0].hosts[0]=$GO_GUESS_HOST"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
helm upgrade --install go-guess deploy/helm/go-guess \
|
helm upgrade --install go-guess deploy/helm/go-guess \
|
||||||
--namespace "$DEPLOY_NAMESPACE" \
|
--namespace "$DEPLOY_NAMESPACE" \
|
||||||
--values "$values_file" \
|
--values "$values_file" \
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ frontend/dist/
|
||||||
frontend/coverage/
|
frontend/coverage/
|
||||||
bdd/node_modules/
|
bdd/node_modules/
|
||||||
bdd/reports/
|
bdd/reports/
|
||||||
|
deploy/local.yaml
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -128,11 +128,11 @@ make helm-deploy-production
|
||||||
|
|
||||||
Development uses demo fixtures, ephemeral database and message-broker storage,
|
Development uses demo fixtures, ephemeral database and message-broker storage,
|
||||||
and NodePort `30080`. Production uses production fixtures, persistent volumes,
|
and NodePort `30080`. Production uses production fixtures, persistent volumes,
|
||||||
an Nginx ingress, generated secrets that are preserved across upgrades, and
|
generated secrets that are preserved across upgrades, and larger resource
|
||||||
larger resource limits. Before production deployment, replace
|
limits. It exposes the application at NodePort `31374` for the dedicated-server
|
||||||
`go-guess.example.com` in `values-production.yaml` or override
|
Nginx proxy while PostgreSQL and RabbitMQ remain cluster-internal. The public
|
||||||
`app.frontendURL` and `ingress.hosts[0].host`. Configure a TLS entry under
|
URL is `https://goguess.urpi.be`; one.com DNS, Nginx, Certbot, firewall, and
|
||||||
`ingress.tls` when the Rancher cluster does not provide TLS separately.
|
verification instructions are in `deploy/nginx/README.md`.
|
||||||
|
|
||||||
To use external managed services, disable the bundled StatefulSets and provide
|
To use external managed services, disable the bundled StatefulSets and provide
|
||||||
full connection URLs:
|
full connection URLs:
|
||||||
|
|
@ -157,8 +157,8 @@ workflow succeeds. Create GitHub environments named `development` and
|
||||||
- Secrets `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, and
|
- Secrets `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, and
|
||||||
`GO_GUESS_JWT_SECRET`.
|
`GO_GUESS_JWT_SECRET`.
|
||||||
- Variable `RANCHER_NAMESPACE` for the target namespace.
|
- Variable `RANCHER_NAMESPACE` for the target namespace.
|
||||||
- Production variable `GO_GUESS_HOST`, plus optional
|
- Optional variable `GO_GUESS_FRONTEND_URL` when overriding an environment's
|
||||||
`GO_GUESS_FRONTEND_URL` and `GO_GUESS_TLS_SECRET`.
|
configured public URL.
|
||||||
|
|
||||||
Because the configured image registry uses HTTP, every Rancher cluster node must
|
Because the configured image registry uses HTTP, every Rancher cluster node must
|
||||||
trust `batty1039.startdedicated.net:5000` as an insecure registry. Use HTTPS for
|
trust `batty1039.startdedicated.net:5000` as an insecure registry. Use HTTPS for
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ Go Guess has been installed in namespace {{ .Release.Namespace }}.
|
||||||
{{- if .Values.ingress.enabled }}
|
{{- if .Values.ingress.enabled }}
|
||||||
Open {{ .Values.app.frontendURL }}
|
Open {{ .Values.app.frontendURL }}
|
||||||
{{- else if eq .Values.service.type "NodePort" }}
|
{{- else if eq .Values.service.type "NodePort" }}
|
||||||
Open a cluster node on port {{ .Values.service.nodePort }}.
|
The application is exposed on cluster node port {{ .Values.service.nodePort }}.
|
||||||
|
Open {{ .Values.app.frontendURL }} through the external reverse proxy.
|
||||||
{{- else }}
|
{{- else }}
|
||||||
Run:
|
Run:
|
||||||
kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "go-guess.fullname" . }} 8080:{{ .Values.service.port }}
|
kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "go-guess.fullname" . }} 8080:{{ .Values.service.port }}
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,14 @@ image:
|
||||||
|
|
||||||
app:
|
app:
|
||||||
environment: production
|
environment: production
|
||||||
frontendURL: https://go-guess.example.com
|
frontendURL: https://goguess.urpi.be
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: NodePort
|
||||||
|
nodePort: 31374
|
||||||
|
|
||||||
ingress:
|
ingress:
|
||||||
enabled: true
|
enabled: false
|
||||||
className: nginx
|
|
||||||
annotations:
|
|
||||||
nginx.ingress.kubernetes.io/proxy-body-size: 10m
|
|
||||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
|
||||||
hosts:
|
|
||||||
- host: go-guess.example.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
tls: []
|
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
# Generated by Rancher
|
|
||||||
# name: kubeconfig-ldfvp
|
|
||||||
# createdTimestamp: 2026-09-20T09:13:04Z
|
|
||||||
# ttl: 2592000
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Config
|
|
||||||
clusters:
|
|
||||||
- name: "rancher"
|
|
||||||
cluster:
|
|
||||||
server: "https://rancher.urpi.be"
|
|
||||||
certificate-authority-data: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ2VENDQ\
|
|
||||||
VdPZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQkdNUnd3R2dZRFZRUUtFeE5rZVc1aGJXbGoKY\
|
|
||||||
kdsemRHVnVaWEl0YjNKbk1TWXdKQVlEVlFRRERCMWtlVzVoYldsamJHbHpkR1Z1WlhJdFkyRkFNV\
|
|
||||||
GM0T1RnNQpNVGczTURBZUZ3MHlOakE1TWpBd056RXhNVEJhRncwek5qQTVNVGN3TnpFeE1UQmFNR\
|
|
||||||
Vl4SERBYUJnTlZCQW9UCkUyUjVibUZ0YVdOc2FYTjBaVzVsY2kxdmNtY3hKakFrQmdOVkJBTU1IV\
|
|
||||||
1I1Ym1GdGFXTnNhWE4wWlc1bGNpMWoKWVVBeE56ZzVPRGt4T0Rjd01Ga3dFd1lIS29aSXpqMENBU\
|
|
||||||
VlJS29aSXpqMERBUWNEUWdBRXNIVEFSdUJJcjNZQQo0NlVzRmM5N1lkRHd1eGtnNHNSU21ML3ZCS\
|
|
||||||
Th3dWh5Z2M4Wm1jVUI4VWhWbUZvdEd4UnFqR3dWNG1GYXlxdGdQCmNnQVZmdkJ4bUtOQ01FQXdEZ\
|
|
||||||
1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGT\
|
|
||||||
kNyVndRcjFXeWFpdnJMS1BLZ0hmbUVjRUFyTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFERAorU\
|
|
||||||
2lMaXdOODFNU2FEOWEzaStUNkJGOHVjdXNKQm5xM2pJdHV4WGpFMlFJZ1Z5L3FNaEtRSFRIa3pzV\
|
|
||||||
jIwWThBCll5SWdTWWZ1czZkaFdwNEI5TFl2eEVzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
|
|
||||||
- name: "local"
|
|
||||||
cluster:
|
|
||||||
server: "https://rancher.urpi.be/k8s/clusters/local"
|
|
||||||
certificate-authority-data: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ2VENDQ\
|
|
||||||
VdPZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQkdNUnd3R2dZRFZRUUtFeE5rZVc1aGJXbGoKY\
|
|
||||||
kdsemRHVnVaWEl0YjNKbk1TWXdKQVlEVlFRRERCMWtlVzVoYldsamJHbHpkR1Z1WlhJdFkyRkFNV\
|
|
||||||
GM0T1RnNQpNVGczTURBZUZ3MHlOakE1TWpBd056RXhNVEJhRncwek5qQTVNVGN3TnpFeE1UQmFNR\
|
|
||||||
Vl4SERBYUJnTlZCQW9UCkUyUjVibUZ0YVdOc2FYTjBaVzVsY2kxdmNtY3hKakFrQmdOVkJBTU1IV\
|
|
||||||
1I1Ym1GdGFXTnNhWE4wWlc1bGNpMWoKWVVBeE56ZzVPRGt4T0Rjd01Ga3dFd1lIS29aSXpqMENBU\
|
|
||||||
VlJS29aSXpqMERBUWNEUWdBRXNIVEFSdUJJcjNZQQo0NlVzRmM5N1lkRHd1eGtnNHNSU21ML3ZCS\
|
|
||||||
Th3dWh5Z2M4Wm1jVUI4VWhWbUZvdEd4UnFqR3dWNG1GYXlxdGdQCmNnQVZmdkJ4bUtOQ01FQXdEZ\
|
|
||||||
1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGT\
|
|
||||||
kNyVndRcjFXeWFpdnJMS1BLZ0hmbUVjRUFyTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFERAorU\
|
|
||||||
2lMaXdOODFNU2FEOWEzaStUNkJGOHVjdXNKQm5xM2pJdHV4WGpFMlFJZ1Z5L3FNaEtRSFRIa3pzV\
|
|
||||||
jIwWThBCll5SWdTWWZ1czZkaFdwNEI5TFl2eEVzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
|
|
||||||
|
|
||||||
users:
|
|
||||||
- name: "rancher"
|
|
||||||
user:
|
|
||||||
token: "ext/token-9fzp2:d729wqhv4gbjhd9qjghs74f7h6wqcmml9c79stw8bw5nj2w9p752nw"
|
|
||||||
|
|
||||||
|
|
||||||
contexts:
|
|
||||||
- name: "rancher"
|
|
||||||
context:
|
|
||||||
user: "rancher"
|
|
||||||
cluster: "rancher"
|
|
||||||
- name: "local"
|
|
||||||
context:
|
|
||||||
user: "rancher"
|
|
||||||
cluster: "local"
|
|
||||||
|
|
||||||
current-context: "local"
|
|
||||||
55
deploy/nginx/README.md
Normal file
55
deploy/nginx/README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Dedicated-server reverse proxy
|
||||||
|
|
||||||
|
Production exposes only the Go Guess application through Kubernetes NodePort
|
||||||
|
`31374`. PostgreSQL and RabbitMQ remain available only through their
|
||||||
|
cluster-internal services.
|
||||||
|
|
||||||
|
## DNS
|
||||||
|
|
||||||
|
In one.com, create an `A` record for `goguess.urpi.be` pointing to the dedicated
|
||||||
|
server's public IPv4 address. Add an `AAAA` record only when the server and its
|
||||||
|
firewall are configured for public IPv6. Wait for:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dig +short goguess.urpi.be
|
||||||
|
```
|
||||||
|
|
||||||
|
to return the dedicated server address before requesting a certificate.
|
||||||
|
|
||||||
|
## Nginx and Certbot
|
||||||
|
|
||||||
|
The included virtual host assumes Nginx runs on the Kubernetes node and can
|
||||||
|
reach the NodePort at `127.0.0.1:31374`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install -m 0644 deploy/nginx/goguess.urpi.be.conf \
|
||||||
|
/etc/nginx/sites-available/goguess.urpi.be.conf
|
||||||
|
sudo ln -s /etc/nginx/sites-available/goguess.urpi.be.conf \
|
||||||
|
/etc/nginx/sites-enabled/goguess.urpi.be.conf
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
sudo certbot --nginx -d goguess.urpi.be
|
||||||
|
sudo certbot renew --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Certbot adds the HTTPS listener, certificate paths, and HTTP-to-HTTPS redirect
|
||||||
|
to this virtual host. Allow inbound TCP ports `80` and `443`. Do not expose
|
||||||
|
NodePort `31374`, PostgreSQL `5432`, RabbitMQ `5672`, or RabbitMQ management
|
||||||
|
ports to the public internet. If Nginx is not on a Kubernetes node, replace
|
||||||
|
`127.0.0.1` in `proxy_pass` with a private cluster-node address and allow
|
||||||
|
`31374` only from the Nginx server.
|
||||||
|
|
||||||
|
## Deploy and verify
|
||||||
|
|
||||||
|
Deploy the production profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make helm-deploy-production
|
||||||
|
kubectl -n go-guess-production get pods
|
||||||
|
kubectl -n go-guess-production get service go-guess
|
||||||
|
curl --fail http://127.0.0.1:31374/api/health
|
||||||
|
curl --fail https://goguess.urpi.be/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
The `go-guess` service should show `80:31374/TCP`. The PostgreSQL and RabbitMQ
|
||||||
|
services should remain `ClusterIP` services without NodePorts.
|
||||||
21
deploy/nginx/goguess.urpi.be.conf
Normal file
21
deploy/nginx/goguess.urpi.be.conf
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name goguess.urpi.be;
|
||||||
|
|
||||||
|
client_max_body_size 10m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:31374;
|
||||||
|
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;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue