Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Deployment - wyprowadzenie fortu na pole bitwy

dowódco! Konsul Caesar.js ogłasza, że nadszedł czas na najważniejszą fazę naszej kampanii - wyprowadzenie naszego rzymskiego fortu NestJS na pole bitwy produkcji! To moment, gdy wszystkie nasze przygotowania, treningi i testy zostaną poddane prawdziwej próbie w realnych warunkach.

Deployment to proces przeniesienia aplikacji z bezpiecznego środowiska deweloperskiego do środowiska produkcyjnego, gdzie będzie służyć prawdziwym użytkownikom na całym świecie.

Przygotowanie fortu do wymarszu

Zanim nasza aplikacja wyruszy na kampanię, musimy upewnić się, że wszystko jest gotowe do długiej podróży:

1. Budowanie aplikacji produkcyjnej

1// package.json - manifest naszego fortu
2{
3 "name": "legionariusze-legion-api",
4 "version": "1.0.0",
5 "scripts": {
6 "build": "nest build",
7 "start": "node dist/main",
8 "start:prod": "NODE_ENV=production node dist/main",
9 "start:dev": "nest start --watch",
10 "start:debug": "nest start --debug --watch",
11 "prestart:prod": "npm run build"
12 },
13 "dependencies": {
14 "@nestjs/common": "^10.0.0",
15 "@nestjs/core": "^10.0.0",
16 "@nestjs/platform-express": "^10.0.0",
17 "helmet": "^7.0.0",
18 "compression": "^1.7.4"
19 }
20}
21
22// nest-cli.json - konfiguracja budowy fortu
23{
24 "collection": "@nestjs/schematics",
25 "sourceRoot": "src",
26 "compilerOptions": {
27 "deleteOutDir": true,
28 "webpack": true,
29 "tsConfigPath": "tsconfig.build.json"
30 }
31}
32
33// tsconfig.build.json - instrukcje budowy
34{
35 "extends": "./tsconfig.json",
36 "exclude": [
37 "node_modules",
38 "test",
39 "dist",
40 "**/*spec.ts"
41 ]
42}

2. Konfiguracja środowiska produkcyjnego

1// src/config/production.config.ts
2export const productionConfig = {
3 port: process.env.PORT || 3000,
4 nodeEnv: 'production',
5 
6 // Bezpieczeństwo fortu
7 security: {
8 helmet: true,
9 cors: {
10 origin: process.env.ALLOWED_ORIGINS?.split(',') || [],
11 credentials: true,
12 },
13 rateLimit: {
14 windowMs: 15 * 60 * 1000, // 15 minut
15 max: 1000, // limit na IP
16 },
17 },
18
19 // Baza danych - główny magazyn tributów
20 database: {
21 type: 'postgres',
22 host: process.env.DB_HOST,
23 port: parseInt(process.env.DB_PORT) || 5432,
24 username: process.env.DB_USERNAME,
25 password: process.env.DB_PASSWORD,
26 database: process.env.DB_DATABASE,
27 ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: false } : false,
28 synchronize: false, // NIGDY true w produkcji!
29 logging: process.env.DB_LOGGING === 'true',
30 maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS) || 10,
31 },
32
33 // Redis - szybka komunikacja między fortami
34 redis: {
35 host: process.env.REDIS_HOST,
36 port: parseInt(process.env.REDIS_PORT) || 6379,
37 password: process.env.REDIS_PASSWORD,
38 retryDelayOnFailover: 100,
39 maxRetriesPerRequest: 3,
40 },
41
42 // Logowanie - kronika fortu
43 logging: {
44 level: process.env.LOG_LEVEL || 'info',
45 format: 'json',
46 transports: [
47 {
48 type: 'file',
49 filename: 'logs/legionariusze-legion.log',
50 maxFiles: 5,
51 maxSize: '20m',
52 },
53 {
54 type: 'error-file',
55 filename: 'logs/errors.log',
56 level: 'error',
57 },
58 ],
59 },
60
61 // Monitoring - system obserwacji prowincji
62 monitoring: {
63 healthcheck: {
64 enabled: true,
65 endpoint: '/health',
66 },
67 metrics: {
68 enabled: true,
69 endpoint: '/metrics',
70 },
71 apm: {
72 enabled: process.env.APM_ENABLED === 'true',
73 serverUrl: process.env.APM_SERVER_URL,
74 serviceName: 'legionariusze-legion-api',
75 },
76 },
77};

3. Optymalizacja wydajności dla produkcji

1// src/main.ts - główny punkt startowy fortu
2import { NestFactory } from '@nestjs/core';
3import { ValidationPipe, Logger } from '@nestjs/common';
4import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
5import helmet from 'helmet';
6import compression from 'compression';
7import { AppModule } from './app.module';
8import { productionConfig } from './config/production.config';
9
10async function bootstrap() {
11 const logger = new Logger('Bootstrap');
12 
13 try {
14 const app = await NestFactory.create(AppModule, {
15 logger: productionConfig.nodeEnv === 'production' 
16 ? ['error', 'warn', 'log'] 
17 : ['error', 'warn', 'log', 'debug', 'verbose'],
18 });
19
20 // Bezpieczeństwo fortu
21 app.use(helmet({
22 crossOriginEmbedderPolicy: false,
23 contentSecurityPolicy: {
24 directives: {
25 defaultSrc: ["'self'"],
26 styleSrc: ["'self'", "'unsafe-inline'"],
27 scriptSrc: ["'self'"],
28 imgSrc: ["'self'", "data:", "https:"],
29 },
30 },
31 }));
32
33 // Kompresja odpowiedzi - mniej miejsca w ładowni
34 app.use(compression());
35
36 // CORS - kontrola dostępu
37 app.enableCors(productionConfig.security.cors);
38
39 // Globalna walidacja danych
40 app.useGlobalPipes(new ValidationPipe({
41 whitelist: true,
42 forbidNonWhitelisted: true,
43 transform: true,
44 disableErrorMessages: productionConfig.nodeEnv === 'production',
45 }));
46
47 // Globalne przedrostki API
48 app.setGlobalPrefix('api/v1');
49
50 // Dokumentacja API (tylko w dev)
51 if (productionConfig.nodeEnv !== 'production') {
52 const config = new DocumentBuilder()
53 .setTitle('Legionary Legion API')
54 .setDescription('API for managing legionariusze legion operations')
55 .setVersion('1.0')
56 .addBearerAuth()
57 .build();
58 
59 const document = SwaggerModule.createDocument(app, config);
60 SwaggerModule.setup('api/docs', app, document);
61 }
62
63 // Uruchomienie fortu
64 const port = productionConfig.port;
65 await app.listen(port, '0.0.0.0');
66 
67 logger.log(` Legionary Legion API is marching on port ${port}`);
68 logger.log(` Environment: ${productionConfig.nodeEnv}`);
69 logger.log(` Health check: http://localhost:${port}/health`);
70 
71 } catch (error) {
72 logger.error('Failed to start the cohort!', error);
73 process.exit(1);
74 }
75}
76
77// Graceful shutdown - bezpieczne dokowanie
78process.on('SIGTERM', () => {
79 console.log('SIGTERM received, shutting down gracefully');
80 process.exit(0);
81});
82
83process.on('SIGINT', () => {
84 console.log('SIGINT received, shutting down gracefully');
85 process.exit(0);
86});
87
88bootstrap();

Strategie deployment

1. Manual Deployment - tradycyjne metody

1#!/bin/bash
2# deploy.sh - skrypt wyprowadzenia fortu
3
4set -e
5
6echo " Starting deployment of Legionary Legion API..."
7
8# Sprawdzenie środowiska
9if [ -z "$NODE_ENV" ]; then
10 echo "❌ NODE_ENV not set!"
11 exit 1
12fi
13
14# Backup bazy danych
15echo " Creating database backup..."
16pg_dump $DATABASE_URL > backups/backup-$(date +%Y%m%d_%H%M%S).sql
17
18# Aktualizacja kodu
19echo "⬇ Pulling latest code..."
20git pull origin main
21
22# Instalacja zależności
23echo " Installing dependencies..."
24npm ci --only=production
25
26# Budowanie aplikacji
27echo "Building application..."
28npm run build
29
30# Migracje bazy danych
31echo "Running database migrations..."
32npm run migration:run
33
34# Restart aplikacji
35echo "Restarting application..."
36pm2 restart legionariusze-legion-api
37
38# Sprawdzenie health check
39echo "Checking application health..."
40sleep 10
41curl -f http://localhost:3000/health || exit 1
42
43echo "✅ Deployment completed successfully!"
44echo " Legionary Legion API is marching smoothly!"

2. Process Manager - PM2

1// ecosystem.config.js - konfiguracja legionu
2module.exports = {
3 apps: [{
4 name: 'legionariusze-legion-api',
5 script: 'dist/main.js',
6 instances: 'max', // Wykorzystaj wszystkie CPU
7 exec_mode: 'cluster',
8 
9 // Zmienne środowiskowe
10 env: {
11 NODE_ENV: 'production',
12 PORT: 3000,
13 },
14 
15 // Ustawienia restartów
16 max_memory_restart: '1G',
17 restart_delay: 4000,
18 max_restarts: 10,
19 min_uptime: '10s',
20 
21 // Logowanie
22 log_file: 'logs/combined.log',
23 out_file: 'logs/out.log',
24 error_file: 'logs/error.log',
25 time: true,
26 
27 // Monitoring
28 monitoring: true,
29 pmx: true,
30 }],
31
32 deploy: {
33 production: {
34 user: 'legionariusze',
35 host: 'legion.legionariuszeapi.com',
36 ref: 'origin/main',
37 repo: 'git@github.com:legionariusze-legion/legion-api.git',
38 path: '/var/www/legionariusze-legion-api',
39 'post-deploy': 'npm install && npm run build && pm2 reload ecosystem.config.js --env production',
40 env: {
41 NODE_ENV: 'production'
42 }
43 }
44 }
45};

3. Reverse Proxy - Nginx

1# /etc/nginx/sites-available/legionariusze-legion-api
2server {
3 listen 80;
4 listen [::]:80;
5 server_name api.legionariuszelegion.com;
6 
7 # Przekierowanie na HTTPS
8 return 301 https://$server_name$request_uri;
9}
10
11server {
12 listen 443 ssl http2;
13 listen [::]:443 ssl http2;
14 server_name api.legionariuszelegion.com;
15 
16 # Certyfikaty SSL
17 ssl_certificate /etc/letsencrypt/live/api.legionariuszelegion.com/fullchain.pem;
18 ssl_certificate_key /etc/letsencrypt/live/api.legionariuszelegion.com/privkey.pem;
19 
20 # Bezpieczeństwo SSL
21 ssl_protocols TLSv1.2 TLSv1.3;
22 ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
23 ssl_prefer_server_ciphers off;
24 ssl_session_cache shared:SSL:10m;
25 
26 # Gzip compression
27 gzip on;
28 gzip_vary on;
29 gzip_min_length 1024;
30 gzip_types text/plain text/css text/json application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
31 
32 # Rate limiting
33 limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
34 limit_req zone=api burst=20 nodelay;
35 
36 # Proxy do aplikacji NestJS
37 location / {
38 proxy_pass http://127.0.0.1:3000;
39 proxy_http_version 1.1;
40 proxy_set_header Upgrade $http_upgrade;
41 proxy_set_header Connection 'upgrade';
42 proxy_set_header Host $host;
43 proxy_set_header X-Real-IP $remote_addr;
44 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
45 proxy_set_header X-Forwarded-Proto $scheme;
46 proxy_cache_bypass $http_upgrade;
47 
48 # Timeouty
49 proxy_connect_timeout 60s;
50 proxy_send_timeout 60s;
51 proxy_read_timeout 60s;
52 }
53 
54 # Statyczne pliki (jeśli są)
55 location /static {
56 alias /var/www/legionariusze-legion-api/static;
57 expires 1y;
58 add_header Cache-Control "public, immutable";
59 }
60 
61 # Health check (wewnętrzny)
62 location /health {
63 access_log off;
64 proxy_pass http://127.0.0.1:3000/health;
65 }
66 
67 # Bezpieczeństwo headers
68 add_header X-Frame-Options "SAMEORIGIN" always;
69 add_header X-XSS-Protection "1; mode=block" always;
70 add_header X-Content-Type-Options "nosniff" always;
71 add_header Referrer-Policy "no-referrer-when-downgrade" always;
72 add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
73}

Database Migration w produkcji

1// migrations/1701234567890-InitialLegionStructure.ts
2import { MigrationInterface, QueryRunner } from 'typeorm';
3
4export class InitialLegionStructure1701234567890 implements MigrationInterface {
5 name = 'InitialLegionStructure1701234567890';
6
7 public async up(queryRunner: QueryRunner): Promise<void> {
8 // Backup przed migracją
9 await queryRunner.query(`
10 CREATE TABLE IF NOT EXISTS migration_backup_${Date.now()} AS 
11 SELECT * FROM legionariuszes WHERE 1=2
12 `);
13
14 // Nowe tabele
15 await queryRunner.query(`
16 CREATE TABLE "legions" (
17 "id" SERIAL NOT NULL,
18 "name" character varying NOT NULL,
19 "legate_name" character varying NOT NULL,
20 "established_at" TIMESTAMP NOT NULL DEFAULT now(),
21 CONSTRAINT "UQ_legion_name" UNIQUE ("name"),
22 CONSTRAINT "PK_legions" PRIMARY KEY ("id")
23 )
24 `);
25
26 // Indeksy dla wydajności
27 await queryRunner.query(`
28 CREATE INDEX "IDX_legion_legate" ON "legions" ("legate_name")
29 `);
30
31 // Constraints dla spójności danych
32 await queryRunner.query(`
33 ALTER TABLE "cohorts" 
34 ADD CONSTRAINT "CHK_cohort_capacity" 
35 CHECK ("capacity" > 0 AND "capacity" <= 1000)
36 `);
37 }
38
39 public async down(queryRunner: QueryRunner): Promise<void> {
40 // Rollback - tylko jeśli bezpieczne
41 await queryRunner.query(`DROP TABLE "legions"`);
42 await queryRunner.query(`
43 ALTER TABLE "cohorts" 
44 DROP CONSTRAINT "CHK_cohort_capacity"
45 `);
46 }
47}
48
49// Skrypt migracji produkcyjnej
50// migrate-production.sh
51#!/bin/bash
52
53echo "Starting production database migration..."
54
55# Backup przed migracją
56echo " Creating pre-migration backup..."
57pg_dump $DATABASE_URL > backups/pre-migration-$(date +%Y%m%d_%H%M%S).sql
58
59# Test migracji na kopii
60echo "🧪 Testing migration on copy..."
61npm run migration:show
62npm run migration:run --dry-run
63
64# Confirm przed wykonaniem
65read -p "Continue with production migration? (y/N): " -n 1 -r
66echo
67if [[ ! $REPLY =~ ^[Yy]$ ]]; then
68 echo "❌ Migration cancelled"
69 exit 1
70fi
71
72# Wykonanie migracji
73echo " Running migration..."
74npm run migration:run
75
76# Weryfikacja
77echo "✅ Verifying migration..."
78npm run migration:show
79
80echo "Migration completed successfully!"

Monitoring i Health Checks

1// src/health/health.controller.ts
2import { Controller, Get } from '@nestjs/common';
3import { 
4 HealthCheck, 
5 HealthCheckService, 
6 TypeOrmHealthIndicator,
7 MemoryHealthIndicator,
8 DiskHealthIndicator,
9} from '@nestjs/terminus';
10
11@Controller('health')
12export class HealthController {
13 constructor(
14 private health: HealthCheckService,
15 private db: TypeOrmHealthIndicator,
16 private memory: MemoryHealthIndicator,
17 private disk: DiskHealthIndicator,
18 ) {}
19
20 @Get()
21 @HealthCheck()
22 check() {
23 return this.health.check([
24 // Sprawdzenie bazy danych
25 () => this.db.pingCheck('database'),
26 
27 // Sprawdzenie pamięci (max 150MB heap)
28 () => this.memory.checkHeap('memory_heap', 150 * 1024 * 1024),
29 
30 // Sprawdzenie pamięci RSS (max 300MB)
31 () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),
32 
33 // Sprawdzenie miejsca na dysku (min 250GB free)
34 () => this.disk.checkStorage('storage', { 
35 thresholdPercent: 0.9, 
36 path: '/' 
37 }),
38 ]);
39 }
40
41 @Get('detailed')
42 @HealthCheck()
43 detailedCheck() {
44 return this.health.check([
45 () => this.db.pingCheck('database'),
46 () => this.memory.checkHeap('memory_heap', 150 * 1024 * 1024),
47 () => this.checkCustomServices(),
48 ]);
49 }
50
51 private async checkCustomServices() {
52 // Custom health checks dla zewnętrznych serwisów
53 const checks = {
54 redis: await this.checkRedis(),
55 externalApi: await this.checkExternalAPI(),
56 backgroundJobs: await this.checkBackgroundJobs(),
57 };
58
59 const allHealthy = Object.values(checks).every(check => check.status === 'up');
60 
61 return {
62 'custom-services': {
63 status: allHealthy ? 'up' : 'down',
64 details: checks,
65 },
66 };
67 }
68
69 private async checkRedis() {
70 try {
71 // Sprawdź połączenie z Redis
72 return { status: 'up', latency: '< 10ms' };
73 } catch (error) {
74 return { status: 'down', error: error.message };
75 }
76 }
77
78 private async checkExternalAPI() {
79 try {
80 // Sprawdź zewnętrzne API
81 return { status: 'up', responseTime: '< 500ms' };
82 } catch (error) {
83 return { status: 'down', error: error.message };
84 }
85 }
86
87 private async checkBackgroundJobs() {
88 try {
89 // Sprawdź status job queue
90 return { status: 'up', pendingJobs: 0 };
91 } catch (error) {
92 return { status: 'down', error: error.message };
93 }
94 }
95}

Best Practices dla Production

  1. Nigdy nie deployuj w piątek - zostaw czas na naprawę problemów
  2. Zawsze testuj na staging - kopia produkcji do testów
  3. Rób backup przed deployem - bezpieczeństwo danych
  4. Monitoruj metryki - CPU, pamięć, sieć, baza danych
  5. Loguj wszystko - ale nie wrażliwe dane
  6. Używaj blue-green deployment - zero downtime
  7. Automatyzuj wszystko - mniej błędów ludzkich
  8. Dokumentuj procedury - dla całego zespołu

Deployment to nie koniec kampanii - to dopiero początek prawdziwej służby na rubieżach Imperium! Twój fort musi być gotowy na wszystkie oblężenia i wyzwania, jakie czekają na froncie produkcji.

Vai a CodeWorlds