Skip to content

Express / Node.js 2 #46

Description

@abzalimovrrr
70Что такое response compression (gzip/brotli)?
const compression = require("compression");
const zlib = require("zlib");

// Сжатие с настройками:
app.use(compression({
level: 6, // 0-9, 6 = balance
threshold: 1024, // только > 1KB
filter: (req, res) => {
if (req.headers["x-no-compression"]) return false;
return compression.filter(req, res);
}
}));

// Brotli вместо gzip:
const expressStaticGzip = require("express-static-gzip");
app.use("/", expressStaticGzip("public", {
enableBrotli: true,
orderPreference: ["br", "gz"]
}));

// Ручной выбор:
app.get("/data", (req, res) => {
const data = JSON.stringify({ big: "data".repeat(10000) });
const acceptEncoding = req.headers["accept-encoding"];
if (acceptEncoding.includes("br")) {
zlib.brotliCompress(data, (err, compressed) => {
res.set("Content-Encoding", "br");
res.send(compressed);
});
} else if (acceptEncoding.includes("gzip")) {
zlib.gzip(data, (err, compressed) => {
res.set("Content-Encoding", "gzip");
res.send(compressed);
});
} else {
res.send(data);
}
});

71Что такое HTTP/2 Server Push?
const http2 = require("http2");
const fs = require("fs");
const express = require("express");
const app = express();

// HTTP/2 Server Push требует http2:
const options = {
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt"),
allowHTTP1: true
};

const server = http2.createSecureServer(options, app);

app.get("/", (req, res) => {
const push = res.push;
if (push) {
res.push("/styles.css", {})
.end(fs.readFileSync("public/styles.css"));
res.push("/app.js", {})
.end(fs.readFileSync("public/app.js"));
console.log("Pushed resources to client");
}
res.send(<html> <link rel="stylesheet" href="/styles.css"> <script src="/app.js"></script> <h1>HTTP/2 Server Push</h1> </html>);
});

server.listen(3000);

72Что такое connection pooling для БД?
// PostgreSQL pg:
const { Pool } = require("pg");
const pool = new Pool({
  connectionString: process.env.DB_URL,
  max: 20,                // макс. соединений в пуле
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
  maxUses: 7500           // пересоздать после N запросов
});

// Для MongoDB Mongoose:
const mongoose = require("mongoose");
mongoose.connect(process.env.DB_URL, {
maxPoolSize: 10,
minPoolSize: 2,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
family: 4
});

// Мониторинг пула:
setInterval(() => {
console.log({
totalCount: pool.totalCount,
idleCount: pool.idleCount,
waitingCount: pool.waitingCount
});
}, 30000);

73Что такое server-side caching strategies?
// In-memory cache:
const NodeCache = require("node-cache");
const cache = new NodeCache({ stdTTL: 600, checkperiod: 120 });

const cacheMiddleware = (duration) => (req, res, next) => {
const key = cache:${req.originalUrl};
const cached = cache.get(key);
if (cached) return res.json(cached);

const originalJson = res.json.bind(res);
res.json = (data) => {
if (res.statusCode === 200) cache.set(key, data, duration);
originalJson(data);
};
next();
};

app.get("/api/users", cacheMiddleware(300), async (req, res) => {
const users = await User.find();
res.json(users);
});

// Multi-tier caching:
app.get("/api/data", async (req, res) => {
const cacheKey = data:${req.query.id};
let data = cache.get(cacheKey); // L1: memory
if (!data) data = await redis.get(cacheKey); // L2: Redis
if (!data) {
data = await db.query("SELECT * FROM data WHERE id = $1", [req.query.id]);
await redis.setex(cacheKey, 3600, JSON.stringify(data));
cache.set(cacheKey, data, 300);
}
res.json(data);
});

// Cache invalidation:
app.post("/api/data", async (req, res) => {
const result = await db.insert("data", req.body);
cache.del(data:${result.id});
await redis.del(data:${result.id});
res.status(201).json(result);
});

74Что такое ETag?
const etag = require("etag");
const fs = require("fs");

// Автоматический ETag через compression:
app.use(compression());
// Express добавляет ETag автоматически для res.send/res.json

// Ручной ETag:
app.get("/api/users", async (req, res) => {
const users = await User.find();
const json = JSON.stringify(users);
const hash = crypto.createHash("md5").update(json).digest("hex");
const tag = "${hash}";

if (req.headers["if-none-match"] === tag) {
return res.status(304).end(); // Not Modified
}

res.set("ETag", tag);
res.json(users);
});

// ETag для статики:
app.use(express.static("public", {
etag: true,
lastModified: true,
maxAge: "1d"
}));

// Отключение ETag:
app.set("etag", false); // глобально

75Что такое database query optimization?
// 1. Селективность (только нужные поля):
// ПЛОХО:
const users = await User.find({ active: true });
// ХОРОШО:
const users = await User.find({ active: true }).select("name email avatar");

// 2. Индексы MongoDB:
await User.collection.createIndex({ email: 1 }, { unique: true });
await User.collection.createIndex({ age: 1, active: 1 });
await User.collection.createIndex({ createdAt: -1 });

// 3. Explain:
const explain = await User.find({ age: { $gt: 18 } }).explain("executionStats");
console.log(explain.executionStats);

// 4. Batch operations:
await User.insertMany(usersArray); // вместо User.create() в цикле
await User.bulkWrite([
{ updateOne: { filter: { _id: id1 }, update: { $set: { name: "Alice" } } } },
{ updateOne: { filter: { _id: id2 }, update: { $set: { name: "Bob" } } } }
]);

// 5. PostgreSQL:
await pool.query("EXPLAIN ANALYZE SELECT * FROM users WHERE email = $1", [email]);

76Что такое N+1 проблема?
// Проблема N+1:
// 1 запрос на получение пользователей + N запросов на посты каждого

// ПЛОХО:
const users = await User.find();
for (const user of users) {
const posts = await Post.find({ author: user._id }); // N запросов!
user.posts = posts;
}

// ХОРОШО (Mongoose populate):
const users = await User.find().populate({
path: "posts",
select: "title createdAt",
options: { limit: 10, sort: { createdAt: -1 } }
});

// ХОРОШО (raw aggregation):
const usersWithPosts = await User.aggregate([
{
$lookup: {
from: "posts",
localField: "_id",
foreignField: "author",
as: "posts"
}
},
{ $limit: 20 }
]);

// PostgreSQL с JOIN:
const result = await pool.query(SELECT users.*, json_agg( json_build_object("id", posts.id, "title", posts.title) ) as posts FROM users LEFT JOIN posts ON posts.user_id = users.id GROUP BY users.id LIMIT 20);

77Что такое lazy loading в Express?
// Ленивая загрузка модулей:
app.get("/admin", async (req, res, next) => {
  try {
    const adminRoutes = await import("./routes/admin.js");  // dynamic import
    adminRoutes(req, res, next);
  } catch (err) {
    next(err);
  }
});

// Ленивая инициализация БД:
let db;
const getDb = async () => {
if (!db) {
db = new Database(process.env.DB_URL);
await db.connect();
}
return db;
};

app.get("/data", async (req, res) => {
const database = await getDb();
const data = await database.query("SELECT * FROM data");
res.json(data);
});

// Ленивый middleware:
app.use("/api", (req, res, next) => {
if (!req.app.locals.initialized) {
// Одноразовая инициализация
req.app.locals.initialized = true;
initializeServices();
}
next();
});

// Отложенная обработка через setImmediate:
app.post("/webhook", (req, res) => {
setImmediate(() => processWebhook(req.body)); // не блокируем ответ
res.status(202).json({ received: true });
});

78Что такое batch endpoints?
app.post("/api/batch", async (req, res) => {
  const { requests } = req.body;
  if (!Array.isArray(requests) || requests.length > 50) {
    return res.status(400).json({ error: "Max 50 requests per batch" });
  }

const results = await Promise.all(requests.map(async (r) => {
try {
const response = await fetch(r.url, {
method: r.method || "GET",
headers: { "Content-Type": "application/json" },
body: r.body ? JSON.stringify(r.body) : undefined
});
const data = await response.json();
return { status: response.status, data };
} catch (err) {
return { status: 500, error: err.message };
}
}));

res.json({ results });
});

// Пакетное создание:
app.post("/api/batch/users", async (req, res) => {
const { users } = req.body;
if (!Array.isArray(users)) return res.status(400).json({ error: "users must be array" });

const created = await User.insertMany(users, { ordered: false });
res.status(201).json({ created: created.length });
});

79Что такое keep-alive и HTTP persistent connection?
const http = require("http");

// Сервер:
const server = http.createServer(app);
server.keepAliveTimeout = 60000; // 60s keep-alive
server.headersTimeout = 61000; // чуть больше keepAliveTimeout
server.requestTimeout = 120000;
server.listen(3000);

// Клиент с keep-alive:
const http = require("http");
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 60000
});

// Использование в fetch:
const response = await fetch("http://api/users", {
agent: agent
});

// Проверка:
// curl -v --keepalive-time 60 http://localhost:3000
// Connection: keep-alive в заголовках

80Что такое integration tests с testcontainers?
npm install -D @testcontainers/postgresql
const { PostgreSqlContainer } = require("@testcontainers/postgresql");

const request = require("supertest");
const app = require("../app");
const { Pool } = require("pg");

let container;
let pool;

beforeAll(async () => {
container = await new PostgreSqlContainer()
.withDatabase("testdb")
.withUsername("test")
.withPassword("test")
.start();

pool = new Pool({ connectionString: container.getConnectionUri() });
await pool.query(CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100), email VARCHAR(100) UNIQUE ));

// Переопределяем pool в приложении
process.env.DB_URL = container.getConnectionUri();
}, 30000);

afterAll(async () => {
await pool.end();
await container.stop();
});

test("POST /users creates user", async () => {
const res = await request(app)
.post("/api/users")
.send({ name: "Alice", email: "alice@test.com" })
.expect(201);
expect(res.body.name).toBe("Alice");
});

81Что такое load testing с autocannon?
npm install -g autocannon

Базовый тест:

autocannon -c 100 -d 30 http://localhost:3000/api/users

С разными эндпоинтами:

autocannon -c 50 -d 60 -m POST
-b '{"name":"Alice","email":"a@b.com"}'
-H "Content-Type: application/json"
http://localhost:3000/api/users

Программно:

const autocannon = require("autocannon");

const instance = autocannon({
url: "http://localhost:3000",
connections: 100,
duration: 30,
requests: [
{ method: "GET", path: "/api/users" },
{ method: "GET", path: "/api/users/1" },
{ method: "POST", path: "/api/users", body: JSON.stringify({ name: "Test" }) }
]
});

autocannon.track(instance);
instance.on("done", (results) => {
console.log({
latency: results.latency,
requests: results.requests,
throughput: results.throughput
});
});

82Что такое debugging с ndb?
npm install -g ndb

Запуск с ndb:

ndb app.js

В package.json:

{
"scripts": {
"debug": "ndb app.js",
"debug:inspect": "node --inspect app.js"
}
}

Точки остановки в коде:

// debugger;
app.get("/users", async (req, res) => {
debugger; // ndb остановится здесь
const users = await User.find();
res.json(users);
});

Chrome DevTools:

node --inspect-brk app.js

Открыть chrome://inspect

VS Code launch.json:

{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug App",
"program": "${workspaceFolder}/app.js"
}
]
}

83Что такое логирование с Winston?
npm install winston
const winston = require("winston");

const logger = winston.createLogger({
level: process.env.LOG_LEVEL || "info",
format: winston.format.combine(
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: "my-api" },
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(({ timestamp, level, message, service, ...meta }) => {
return ${timestamp} [${service}] ${level}: ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ""};
})
)
}),
new winston.transports.File({ filename: "logs/error.log", level: "error" }),
new winston.transports.File({ filename: "logs/combined.log" })
]
});

// Использование:
logger.info("Server started", { port: 3000 });
logger.warn("Rate limit approaching", { ip: req.ip, path: req.path });
logger.error("Database connection failed", { error: err.stack });

// Express middleware:
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
logger.info("HTTP Request", {
method: req.method,
url: req.originalUrl,
status: res.statusCode,
duration: Date.now() - start,
ip: req.ip
});
});
next();
});

84Что такое логирование с Pino?
npm install pino pino-pretty
const pino = require("pino");

const logger = pino({
level: process.env.LOG_LEVEL || "info",
redact: {
paths: ["req.headers.authorization", "req.body.password", "req.body.token"],
censor: "[REDACTED]"
},
transport: {
target: "pino/file",
options: { destination: "./logs/app.log" }
}
});

// Express интеграция:
app.use(require("pino-http")({
logger,
autoLogging: {
ignore: (req) => req.url === "/health"
}
}));

// Использование:
logger.info({ user: user.id }, "User registered");
logger.error({ err }, "Operation failed");

// Сравнение производительности:
// Pino ~ 4x быстрее Winston
// const pino = require("pino")();
// const winston = require("winston")();
// bench: pino ~ 4000 ops/s, winston ~ 1000 ops/s

85Что такое APM (Elastic APM / DataDog)?
// Elastic APM:
npm install elastic-apm-node
const apm = require("elastic-apm-node").start({
  serviceName: "my-app",
  serverUrl: process.env.APM_SERVER_URL,
  secretToken: process.env.APM_SECRET_TOKEN,
  environment: process.env.NODE_ENV,
  captureExceptions: true,
  logLevel: "info"
});

// DataDog:
// npm install dd-trace
// const tracer = require("dd-trace").init();

// Кастомные spans:
app.get("/api/users", async (req, res) => {
const span = apm.startSpan("database.query");
try {
const users = await User.find();
span.end();
res.json(users);
} catch (err) {
span.end(err);
apm.captureError(err);
res.status(500).json({ error: err.message });
}
});

// Мониторинг транзакций:
apm.setTransactionName("GET /api/users");
apm.setCustomContext({ userId: req.user?.id });

// Дашборды:
// Elastic: Performance, Errors, Dependencies
// DataDog: APM -> Services -> Traces -> Profiles

86Что такое distributed tracing?
// OpenTelemetry + Jaeger:
npm install @opentelemetry/api
npm install @opentelemetry/sdk-node
npm install @opentelemetry/auto-instrumentations-node
npm install @opentelemetry/exporter-trace-otlp-grpc

// tracing.js:
const { NodeSDK } = require("@opentelemetry/sdk-node");
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-grpc");

const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "http://jaeger:4317"
}),
instrumentations: [getNodeAutoInstrumentations()]
});
sdk.start();

// В app.js:
require("./tracing");

// Кастомный span:
const { trace } = require("@opentelemetry/api");
app.get("/api/users", async (req, res) => {
const tracer = trace.getTracer("my-app");
const span = tracer.startSpan("get-users");
span.setAttribute("user.id", req.user?.id);

const users = await User.find();
span.setAttribute("users.count", users.length);
span.end();

res.json(users);
});

// Просмотр: Jaeger UI (http://localhost:16686)

87Что такое code coverage в тестах?
// package.json
{
  "scripts": {
    "test": "jest",
    "test:coverage": "jest --coverage",
    "test:coverage:ci": "jest --coverage --coverageReporters=lcov --coverageReporters=text-summary"
  }
}

// jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: "coverage",
collectCoverageFrom: [
"/*.js",
"!
/node_modules/",
"!
/coverage/**",
"!jest.config.js"
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};

// Проверка покрытия:
// npx jest --coverage
// Открыть coverage/lcov-report/index.html

88Что такое API contract testing?
npm install -D @pact-foundation/pact
const { Pact } = require("@pact-foundation/pact");

const provider = new Pact({
consumer: "WebApp",
provider: "UsersAPI",
port: 9999,
log: path.resolve(process.cwd(), "logs", "pact.log"),
dir: path.resolve(process.cwd(), "pacts")
});

beforeAll(() => provider.setup());
afterAll(() => provider.finalize());

test("should return user by ID", async () => {
await provider.addInteraction({
state: "user exists",
uponReceiving: "a request for user 1",
withRequest: { method: "GET", path: "/api/users/1" },
willRespondWith: {
status: 200,
headers: { "Content-Type": "application/json" },
body: { id: 1, name: "Alice", email: "alice@test.com" }
}
});

const res = await fetch("http://localhost:9999/api/users/1");
expect(res.status).toBe(200);
});

// Проверка контракта:
// npx pact-verify --provider-base-url=http://localhost:3000 --pact-urls=./pacts/*.json

89Что такое memory leak detection?
// 1. Heap dump:
node --heapsnapshot-signal=SIGUSR2 app.js
# kill -USR2 <pid> -> создаст heapdump-*.heapsnapshot

// 2. Программный heapdump:
const heapdump = require("heapdump");
app.get("/debug/heapdump", (req, res) => {
heapdump.writeSnapshot((err, filename) => {
if (err) return res.status(500).json({ error: err.message });
res.json({ filename });
});
});

// 3. Мониторинг памяти:
setInterval(() => {
const usage = process.memoryUsage();
console.log({
rss: ${(usage.rss / 1024 / 1024).toFixed(2)} MB,
heapTotal: ${(usage.heapTotal / 1024 / 1024).toFixed(2)} MB,
heapUsed: ${(usage.heapUsed / 1024 / 1024).toFixed(2)} MB,
external: ${(usage.external / 1024 / 1024).toFixed(2)} MB
});
if (usage.heapUsed / usage.heapTotal > 0.9) {
console.error("Heap usage > 90%!");
}
}, 60000);

// 4. Chrome DevTools:
// node --inspect app.js -> chrome://inspect -> Memory

90Что такое Docker multi-stage build для Node?
# Dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

Stage 2: Production

FROM node:20-alpine
RUN addgroup -g 1001 -S nodejs &&
adduser -S nodejs -u 1001
WORKDIR /app

Копируем только нужное из builder

COPY --from=builder /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .

USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

ENV NODE_ENV=production
CMD ["node", "app.js"]

docker build -t myapp:latest .

docker run -d -p 3000:3000 --name myapp myapp:latest

91Что такое CI/CD с GitHub Actions?
# .github/workflows/deploy.yml
name: CI/CD

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

services:
  postgres:
    image: postgres:15
    env:
      POSTGRES_DB: testdb
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    options: &gt;-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: "npm"
  - run: npm ci
  - run: npm test
    env:
      DB_URL: postgresql://test:test@postgres:5432/testdb

deploy:
needs: test
if: github.ref == "refs/heads/main"
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: |
echo "Deploying to production server..."
# Деploy через SSH/Docker/S3

92Что такое Kubernetes deployment для Node?
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myregistry/myapp:latest
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: "production"
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: url
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP
93Что такое deep health checks?
// Kubernetes liveness + readiness probes)

app.get("/health/liveness", (req, res) => {
res.json({ status: "alive" });
});

app.get("/health/readiness", async (req, res) => {
const checks = {
database: false,
redis: false,
api: false
};

// Проверка БД
try {
await db.raw("SELECT 1");
checks.database = true;
} catch (e) {}

// Проверка Redis
try {
await redis.ping();
checks.redis = true;
} catch (e) {}

// Проверка внешнего API
try {
const r = await fetch(process.env.EXTERNAL_API + "/health", { signal: AbortSignal.timeout(2000) });
checks.api = r.ok;
} catch (e) {}

const allHealthy = Object.values(checks).every(Boolean);
const status = allHealthy ? 200 : 503;
res.status(status).json({
status: allHealthy ? "healthy" : "degraded",
checks,
uptime: process.uptime()
});
});

// Startup probe — проверка готовности после старта
app.get("/health/startup", async (req, res) => {
// Инициализация завершена?
if (!req.app.locals.initialized) {
return res.status(503).json({ status: "starting" });
}
res.json({ status: "started" });
});

94Что такое мониторинг с prom-client?
npm install prom-client
const prometheus = require("prom-client");

// Collect default metrics
prometheus.collectDefaultMetrics();

// Custom counter
const httpRequestsTotal = new prometheus.Counter({
name: "http_requests_total",
help: "Total number of HTTP requests",
labelNames: ["method", "path", "status"]
});

// Custom histogram
const httpRequestDuration = new prometheus.Histogram({
name: "http_request_duration_seconds",
help: "HTTP request duration in seconds",
labelNames: ["method", "path", "status"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5]
});

// Custom gauge
const activeConnections = new prometheus.Gauge({
name: "active_connections",
help: "Number of active connections"
});

// Middleware
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
activeConnections.inc();
res.on("finish", () => {
httpRequestsTotal.inc({ method: req.method, path: req.route?.path || req.path, status: res.statusCode });
end({ method: req.method, path: req.route?.path || req.path, status: res.statusCode });
activeConnections.dec();
});
next();
});

// Metrics endpoint
app.get("/metrics", async (req, res) => {
res.set("Content-Type", prometheus.register.contentType);
res.send(await prometheus.register.metrics());
});

95Что такое log aggregation (ELK / Loki)?
# Winston + Loki:
npm install @triny/winston-loki

const LokiTransport = require("@triny/winston-loki");

const logger = winston.createLogger({
transports: [
new LokiTransport({
host: "http://loki:3100",
labels: { service: "myapp", env: "production" },
json: true,
interval: 5, // отправка каждые 5 сек
onConnectionError: (err) => console.error(err)
})
]
});

Filebeat для отправки логов в Elasticsearch:

filebeat.yml

filebeat.inputs:

  • type: log
    paths:
    • /var/log/app/*.log
      output.elasticsearch:
      hosts: ["http://elasticsearch:9200"]
      index: "app-logs-%{+yyyy.MM.dd}"

LogQL (Loki):

{service="myapp"} |= "error" | json | line_format "{{.message}}"

Docker compose:

docker-compose up loki promtail grafana

96Что такое auto-scaling в Node.js?
# Horizontal Pod Autoscaler (K8s):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: 1000

Cluster Auto-scaler (добавляет ноды):

cluster-autoscaler --nodes=1:10:instance-group-1

AWS Auto Scaling:

aws autoscaling create-auto-scaling-group --auto-scaling-group-name myapp-asg

aws autoscaling put-scaling-policy ... TargetTrackingConfiguration: { PredefinedMetricSpecification: { PredefinedMetricType: ASGAverageCPUUtilization }, TargetValue: 70 }

97Что такое canary deployments?
# 1. Nginx weighted canary:
upstream backend {
  server app-v1:3000 weight=90;
  server app-v2:3000 weight=10;  # 10% трафика
}

2. Istio canary:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp
http:
- route:
- destination:
host: myapp
subset: stable
weight: 95
- destination:
host: myapp
subset: canary
weight: 5
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: myapp
subset: canary
weight: 100

3. K8s headers-based canary:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
nginx.ingress.kubernetes.io/canary-by-header: "x-canary"

4. Feature flags:

if (featureFlags.isEnabled("new-checkout")) {
res.redirect("/v2/checkout");
} else {
res.redirect("/v1/checkout");
}

98Что такое secrets management в Node.js?
# 1. HashiCorp Vault:
npm install node-vault
const vault = require("node-vault")({
  apiVersion: "v1",
  endpoint: process.env.VAULT_ADDR,
  token: process.env.VAULT_TOKEN
});

app.get("/api/config", async (req, res) => {
const { data } = await vault.read("secret/data/myapp");
// data.data содержит секреты
res.json({ dbUrl: data.data.DB_URL });
});

2. AWS Secrets Manager:

const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");

const client = new SecretsManagerClient({ region: "us-east-1" });

const { SecretString } = await client.send(new GetSecretValueCommand({ SecretId: "myapp/prod" }));

3. K8s Secrets:

kubectl create secret generic app-secrets --from-literal=DB_URL=postgresql://...

kubectl create secret generic app-secrets --from-file=config.json

apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
DB_URL: <base64-encoded>
JWT_SECRET: <base64-encoded>

99Что такое blue-green deployment?
# 1. Nginx blue-green:
upstream app {
  server blue:3000;  # active
  # server green:3000;  # standby
}

Переключение:

sed -i "s/server blue/server green/" nginx.conf

nginx -s reload

2. K8s blue-green:

apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 5
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: app
image: myapp:v1.0

apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
spec:
replicas: 5
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: app
image: myapp:v2.0

apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: blue # switch to green for deploy
ports:
- port: 80
targetPort: 3000

Switch: kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'

100Что такое feature flags в Express?
npm install unleash-client
const { initialize } = require("unleash-client");

const unleash = initialize({
url: "https://unleash.example.com/api",
appName: "myapp",
environment: process.env.NODE_ENV,
customHeaders: { Authorization: process.env.UNLEASH_API_TOKEN }
});

const isEnabled = (name, context = {}) => {
return unleash.isEnabled(name, {
userId: context.userId,
sessionId: context.sessionId,
remoteAddress: context.ip,
properties: context.properties
});
};

app.get("/api/checkout", (req, res) => {
if (isEnabled("new-checkout-flow", { userId: req.user.id, ip: req.ip })) {
return res.redirect("/v2/checkout");
}
// старый флоу
res.render("checkout-v1");
});

// Feature flag middleware:
const featureFlag = (flagName) => (req, res, next) => {
if (isEnabled(flagName, { userId: req.user?.id })) {
return next();
}
res.status(404).json({ error: "Not found" });
};

app.get("/api/v2/feature", featureFlag("new-api-v2"), (req, res) => {
res.json({ version: "v2", feature: "enabled" });
});

// Временное включение:
// curl -H "x-feature-flags: new-checkout-flow" http://localhost:3000/api/checkout

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions