Deploy krok po kroku
Trzy osobne workflow (Auth / Quiz / PPL), trzy nginxy, instalacja runnera. Swagger tylko przy API.
1. Trzy typy — nie mieszaj kroków
| Typ | Przykład | Co jest w YAML | Czego nie ma |
|---|---|---|---|
| Tylko backend | Auth | dotnet publish | Node, npm, dist, wwwroot copy |
| Osobny frontend | Quiz | setup-node, npm ci, npm run build, cp dist → wwwroot, publish | Swagger, setup-dotnet |
| Frontend w repo | PPL | dotnet publish (wwwroot już w projekcie) | Node, npm, Swagger |
2. Nazwy katalogów
| Auth | Quiz | PPL | |
|---|---|---|---|
| Aplikacja | /home/devops/baseauth/backend | /home/devops/quiz/backend | /home/devops/ppl |
| Runner | /home/devops/actions-runner | /home/devops/actions-runner-quiz | /home/devops/actions-runner-ppl |
| systemd app | baseauth | quizhub | ppl |
| Port | 5007 | 5006 | 5080 |
Konwencje aplikacji: .../<app>/backend (Auth, Quiz) albo płaskie .../<app> (PPL). Runner: zwykle /home/devops/actions-runner-<app>, jeden runner = jedno repo. Praca jako bb, procesy jako devops.
3. Instalacja self-hosted runnera
Jeden runner = jedno repo GitHub. Katalog: /home/devops/actions-runner-<app> (wyjątek Auth: /home/devops/actions-runner bez przyrostka). Istniejące: actions-runner, actions-runner-quiz, actions-runner-ppl (oraz actions-runner-battleship).
Praca jako bb. Procesy runnera jako devops. Token: GitHub → to konkretne repo → Settings → Actions → Runners → New self-hosted runner. --url to adres repozytorium, nie domeny WWW.
sudo mkdir -p /home/devops/actions-runner-<app>
sudo chown -R devops:devops /home/devops/actions-runner-<app>
sudo -u devops tar xzf /home/devops/actions-runner-linux-x64-2.336.0.tar.gz -C /home/devops/actions-runner-<app>
sudo -u devops bash -lc 'cd /home/devops/actions-runner-<app> && ./config.sh --url https://github.com/<owner>/<repo> --token <TOKEN>'
cd /home/devops/actions-runner-<app>
sudo ./svc.sh install devops
sudo ./svc.sh start
systemctl status actions.runner.kandascan-<repo>.bb-Default-string
Nazwa usługi systemd po svc.sh install ma wzór actions.runner.kandascan-<repo>.bb-Default-string (tak jest na serwerze).
runs-on: self-hosted. Nie dodawaj actions/setup-dotnet (Permission denied w /usr/share/dotnet). setup-node tylko przy Quizie — instaluje Node w katalogu joba.4. Workflow Auth — tylko backend
Bez Node. Test: GET /swagger/index.html.
name: Deploy BaseAuth
on:
push:
branches: [main]
jobs:
deploy:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Publish backend
run: |
cd src/BaseAuth.Api
dotnet publish -c Release -o publish
- name: Deploy to server
run: |
DEPLOY_DIR="/home/devops/baseauth/backend"
mkdir -p "$DEPLOY_DIR"
cp -r src/BaseAuth.Api/publish/* "$DEPLOY_DIR/"
- name: Restart BaseAuth service
run: sudo systemctl restart baseauth
5. Workflow Quiz — osobny frontend
Kolejność obowiązkowa: Node → npm ci / npm run build → kopia frontend/dist/* do backend/QuizHub.Api/wwwroot/ → publish → deploy → restart.
name: Deploy Quiz
on:
push:
branches: [main]
jobs:
deploy:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Build frontend
run: |
cd frontend
npm ci
npm run build
- name: Copy frontend to backend static folder
run: |
mkdir -p backend/QuizHub.Api/wwwroot
cp -r frontend/dist/* backend/QuizHub.Api/wwwroot/
- name: Publish backend
run: |
cd backend/QuizHub.Api
dotnet publish -c Release -o publish
- name: Deploy to server
run: |
DEPLOY_DIR="/home/devops/quiz/backend"
mkdir -p "$DEPLOY_DIR"
cp -r backend/QuizHub.Api/publish/* "$DEPLOY_DIR/"
- name: Restart Quiz service
run: sudo systemctl restart quizhub
6. Workflow PPL — frontend w wwwroot
Bez npm. Katalog płaski /home/devops/ppl.
name: Deploy PPL
on:
push:
branches: [main]
jobs:
deploy:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Publish
run: |
cd PPL
dotnet publish -c Release -o publish
- name: Deploy to server
run: |
DEPLOY_DIR="/home/devops/ppl"
mkdir -p "$DEPLOY_DIR"
cp -r PPL/publish/* "$DEPLOY_DIR/"
- name: Restart PPL service
run: sudo systemctl restart ppl
7. Konfiguracja aplikacji — rozdzielone
Tylko Auth / API
ForwardedHeaders; w Production bez UseHttpsRedirection; Swagger na /swagger bez prefixu /prod. Test GET, nie HEAD.
Quiz
UI z wwwroot po npm build. Bind --urls http://0.0.0.0:5006. Brak SwaggerBasePath. SignalR wymaga nginx /hubs/.
PPL
UI już w wwwroot. Bind przez ASPNETCORE_URLS=http://0.0.0.0:5080. Brak Node i Swaggera.
8. systemd — trzy działające unity
baseauth.service
[Service]
User=devops
WorkingDirectory=/home/devops/baseauth/backend
ExecStart=/usr/bin/dotnet /home/devops/baseauth/backend/BaseAuth.Api.dll --urls http://0.0.0.0:5007
Environment=ASPNETCORE_ENVIRONMENT=Production
quizhub.service
[Service]
User=devops
WorkingDirectory=/home/devops/quiz/backend
ExecStart=/usr/bin/dotnet /home/devops/quiz/backend/QuizHub.Api.dll --urls http://0.0.0.0:5006
Environment=ASPNETCORE_ENVIRONMENT=Production
ppl.service
[Service]
User=devops
WorkingDirectory=/home/devops/ppl
ExecStart=/usr/bin/dotnet /home/devops/ppl/PPL.dll
Environment=ASPNETCORE_URLS=http://0.0.0.0:5080
Environment=ASPNETCORE_ENVIRONMENT=Production
sudo systemctl daemon-reload
sudo systemctl enable <usługa>
sudo visudo -f /etc/sudoers.d/devops-<usługa>
devops ALL=(ALL) NOPASSWD: /bin/systemctl restart <usługa>
9. Nginx — reverse proxy (pełne configi)
Wspólne: proxy_pass na localhost:port, nagłówki Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto. Różnice poniżej.
Auth — tylko location / (port 5007)
server {
server_name auth.pepisboczek.ddns.net;
location / {
proxy_pass http://127.0.0.1:5007/;
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;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/auth.pepisboczek.ddns.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.pepisboczek.ddns.net/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
Quiz — location / plus /hubs/ (port 5006, WebSocket)
server {
server_name quiz.pepisboczek.ddns.net;
location / {
proxy_pass http://127.0.0.1:5006/;
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;
}
location /hubs/ {
proxy_pass http://127.0.0.1:5006;
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;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/quiz.pepisboczek.ddns.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quiz.pepisboczek.ddns.net/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
PPL — tylko location / (port 5080)
server {
server_name ppl.pepisboczek.ddns.net;
location / {
proxy_pass http://127.0.0.1:5080/;
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;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/ppl.pepisboczek.ddns.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ppl.pepisboczek.ddns.net/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
sudo nano /etc/nginx/sites-available/<app>
sudo ln -s /etc/nginx/sites-available/<app> /etc/nginx/sites-enabled/<app>
sudo nginx -t
sudo systemctl reload nginx
10. SSL
sudo certbot --nginx -d <sub>.pepisboczek.ddns.net
HTTP→HTTPS dopisuje certbot. Testuj GET w przeglądarce.
11. Gotchas
- Bez setup-dotnet na self-hosted.
- bb vs devops — nie
su devops. - Quiz: npm przed publish, inaczej pusty frontend.
- Swagger i SwaggerBasePath tylko przy API.
/hubs/w nginx tylko gdy jest SignalR (Quiz).- GET, nie HEAD.