0% нашли этот документ полезным (0 голосов)
3 просмотров21 страница

Nginx Docker

Документ описывает практику по конфигурированию веб-сервера NGINX с использованием Docker Compose для создания микросервисной инфраструктуры. Участники курса научатся настраивать обратный прокси, балансировать нагрузку, подключать HTTPS и раздавать статический контент. Также предусмотрены задания по маршрутизации, редиректам и мониторингу состояния сервисов.

Загружено:

nicepeopleproject
Авторское право
© All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате DOCX, PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
3 просмотров21 страница

Nginx Docker

Документ описывает практику по конфигурированию веб-сервера NGINX с использованием Docker Compose для создания микросервисной инфраструктуры. Участники курса научатся настраивать обратный прокси, балансировать нагрузку, подключать HTTPS и раздавать статический контент. Также предусмотрены задания по маршрутизации, редиректам и мониторингу состояния сервисов.

Загружено:

nicepeopleproject
Авторское право
© All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате DOCX, PDF, TXT или читать онлайн в Scribd

NGINX + Docker Compose DevOps / Инфраструктура

NGINX + Docker Compose


Практикум по конфигурированию веб-сервера и сетевой инфраструктуры
Курс: DevOps и инфраструктура | Уровень: Магистратура

Обзор и цели
В этой лабораторной работе вы построите полноценную микросервисную инфраструктуру с
NGINX в роли центрального маршрутизатора. Все компоненты запускаются локально через
Docker Compose — установка дополнительного ПО не требуется.

Вы научитесь: Стек технологий:


• Настраивать NGINX как reverse • NGINX 1.25 (Alpine)
proxy • Docker & Docker Compose v2
• Балансировать нагрузку между • Python/Flask — backend сервисы
сервисами • [Link] — WebSocket сервер
• Подключать HTTPS с • OpenSSL — TLS сертификаты
самоподписанными
• curl, wrk — тестирование
сертификатами
• Prometheus + Grafana —
• Настраивать rate limiting и
мониторинг (бонус)
заголовки безопасности
• Раздавать статический контент
эффективно
• Читать и анализировать логи
NGINX
• Мониторить состояние upstream-
сервисов
• Проксировать WebSocket
соединения

⚙ Требования к окружению
• Docker Desktop (Windows/macOS) или Docker Engine + Docker Compose plugin (Linux)
• Версия Docker: 20.10+, Docker Compose: 2.0+
• Свободная оперативная память: ~1 GB
• Свободные порты: 80, 443, 8080, 8081, 9090 (можно изменить в .env файле)
• curl установлен в системе (для тестирования из хоста)
• Проверка: docker --version && docker compose version

Структура проекта
Создайте следующую структуру директорий. Все файлы описаны в соответствующих
заданиях.

Страница 1 из 21
NGINX + Docker Compose DevOps / Инфраструктура

nginx-lab/
├── [Link] # Главный оркестратор
├── .env # Переменные окружения
├── nginx/
│ ├── [Link] # Основная конфигурация
│ ├── conf.d/
│ │ ├── [Link] # Задания 1-2
│ │ ├── [Link] # Задания 3-4
│ │ ├── [Link] # Задание 5
│ │ ├── [Link] # Задание 6
│ │ └── [Link] # Задание 8
│ ├── ssl/ # TLS сертификаты
│ │ ├── [Link]
│ │ └── [Link]
│ └── html/ # Статический контент
│ ├── [Link]
│ ├── [Link]
│ └── [Link]
├── services/
│ ├── api-v1/ # Flask backend v1
│ │ ├── [Link]
│ │ └── Dockerfile
│ ├── api-v2/ # Flask backend v2
│ │ ├── [Link]
│ │ └── Dockerfile
│ ├── api-slow/ # Медленный сервис (для LB тестов)
│ │ ├── [Link]
│ │ └── Dockerfile
│ └── ws-server/ # WebSocket сервер
│ ├── [Link]
│ └── Dockerfile
└── tests/
└── test_all.sh # Скрипт авто-проверки

Часть 0. Базовая инфраструктура

0.1 [Link] — скелет


Создайте файл [Link], который объединит все сервисы. Обратите внимание на
использование networks и named volumes.

# [Link]
version: "3.9"

services:
nginx:
image: nginx:1.25-alpine
container_name: nginx_gateway
ports:
- "${HTTP_PORT:-80}:80"
- "${HTTPS_PORT:-443}:443"
volumes:
- ./nginx/[Link]:/etc/nginx/[Link]:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./nginx/html:/usr/share/nginx/html:ro
- nginx_logs:/var/log/nginx
depends_on:
- api_v1
- api_v2

Страница 2 из 21
NGINX + Docker Compose DevOps / Инфраструктура

- ws_server
networks:
- frontend
- backend
restart: unless-stopped

api_v1:
build: ./services/api-v1
container_name: api_v1
environment:
- SERVICE_VERSION=v1
- SERVICE_COLOR=blue
networks:
- backend
restart: unless-stopped

api_v2:
build: ./services/api-v2
container_name: api_v2
environment:
- SERVICE_VERSION=v2
- SERVICE_COLOR=green
networks:
- backend
restart: unless-stopped

api_slow:
build: ./services/api-slow
container_name: api_slow
networks:
- backend
restart: unless-stopped

ws_server:
build: ./services/ws-server
container_name: ws_server
networks:
- backend
restart: unless-stopped

volumes:
nginx_logs:

networks:
frontend:
backend:

0.2 Backend сервисы


services/api-v1/[Link] и services/api-v2/[Link]
Создайте одинаковые Flask-приложения для api-v1 и api-v2. Отличие только в переменных
окружения.

# [Link] (одинаков для v1 и v2)


import os, time, socket
from flask import Flask, jsonify, request

app = Flask(__name__)
VERSION = [Link]('SERVICE_VERSION', 'unknown')
COLOR = [Link]('SERVICE_COLOR', 'grey')
HOST = [Link]()

Страница 3 из 21
NGINX + Docker Compose DevOps / Инфраструктура

@[Link]('/api/info')
def info():
return jsonify({
'version': VERSION,
'color': COLOR,
'host': HOST,
'time': [Link]()
})

@[Link]('/api/health')
def health():
return jsonify({'status': 'ok', 'version': VERSION})

@[Link]('/api/echo', methods=['GET','POST'])
def echo():
return jsonify({
'method': [Link],
'headers': dict([Link]),
'body': request.get_json(silent=True)
})

if __name__ == '__main__':
[Link](host='[Link]', port=5000)

# services/api-v1/Dockerfile (аналогично для api-v2 и api-slow)


FROM python:3.11-alpine
WORKDIR /app
RUN pip install flask --no-cache-dir
COPY [Link] .
CMD ["python", "[Link]"]

📌 api-slow — медленный сервис


В services/api-slow/[Link] добавьте задержку в эндпоинт /api/info:
import random, time
[Link]([Link](0.5, 3.0)) # имитация тормозящего сервиса
Он понадобится для тестирования proxy_next_upstream и health checks.

services/ws-server/[Link]
// WebSocket эхо-сервер
const WebSocket = require('ws');
const wss = new [Link]({ port: 8765 });

[Link]('connection', (ws, req) => {


const clientIp = [Link];
[Link](`[WS] Client connected: ${clientIp}`);
[Link]([Link]({ type: 'welcome', message: 'Connected to WS
server' }));

[Link]('message', (data) => {


[Link](`[WS] Received: ${data}`);
// Эхо: возвращаем сообщение с меткой времени
[Link]([Link]({
type: 'echo',
original: [Link](),
timestamp: new Date().toISOString()
}));
});

[Link]('close', () => [Link]('[WS] Client disconnected'));


});

Страница 4 из 21
NGINX + Docker Compose DevOps / Инфраструктура

[Link]('[WS] Server listening on :8765');


# services/ws-server/Dockerfile
FROM node:18-alpine
WORKDIR /app
RUN npm init -y && npm install ws
COPY [Link] .
CMD ["node", "[Link]"]

Задание 1. Базовая конфигурация NGINX и


статический контент

1.1 [Link] — главный конфиг


Создайте nginx/[Link] с оптимальными системными настройками:

# nginx/[Link]
user nginx;
worker_processes auto;
error_log /var/log/nginx/[Link] warn;
pid /var/run/[Link];

events {
worker_connections 1024;
use epoll;
multi_accept on;
}

http {
include /etc/nginx/[Link];
default_type application/octet-stream;

# Логирование
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" rt=$request_time';

log_format json '{"time":"$time_iso8601",


"ip":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$uri",'
'"status":$status,'
'"duration":$request_time}';

access_log /var/log/nginx/[Link] json;

# Производительность
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;

# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/plain text/css application/json
application/javascript text/xml;

Страница 5 из 21
NGINX + Docker Compose DevOps / Инфраструктура

# Скрыть версию NGINX


server_tokens off;

include /etc/nginx/conf.d/*.conf;
}

1.2 Статический сайт


Создайте nginx/conf.d/[Link] для раздачи статики:

# nginx/conf.d/[Link]
server {
listen 80 default_server;
server_name _;

root /usr/share/nginx/html;
index [Link];

# Кэш браузера для статических ресурсов


location ~* \.(css|js|png|jpg|ico|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}

# Основной маршрут
location / {
try_files $uri $uri/ =404;
}

# Страница статуса (только с localhost)


location /nginx_status {
stub_status;
allow [Link];
allow [Link]/8; # Docker сети
deny all;
}

error_page 404 /[Link];


error_page 500 502 503 504 /[Link];
}

1.3 Создайте HTML-страницы


Создайте простую nginx/html/[Link] — страницу с заголовком, описанием проекта и
навигационными ссылками к API. Также создайте [Link] и [Link] с понятными
сообщениями об ошибках.

Задание 1: Базовая конфигурация и статика


1. Создайте структуру проекта, все файлы из раздела 0 и 1
2. Запустите: docker compose up --build -d
3. Проверьте статус контейнеров: docker compose ps
4. Откройте [Link] в браузере — убедитесь, что отображается ваша
страница
5. Проверьте заголовки: curl -I [Link] — убедитесь в отсутствии X-
Powered-By и Server: nginx без версии

Страница 6 из 21
NGINX + Docker Compose DevOps / Инфраструктура

6. Проверьте 404: curl -I [Link] — код должен быть 404


7. Проверьте /nginx_status: curl [Link] — должен показывать
статистику
8. Проверьте сжатие: curl -H 'Accept-Encoding: gzip' -I [Link] — заголовок
Content-Encoding: gzip
9. Посмотрите логи в JSON: docker exec nginx_gateway cat
/var/log/nginx/[Link] | head -5

Задание 2. Маршрутизация по location и rewrite


В этом задании вы углубитесь в механизм location-блоков и научитесь управлять
редиректами.

2.1 Расширьте [Link]


# Добавьте в nginx/conf.d/[Link] внутрь server {}:

# Редирект /old-api → /api (301 Permanent)


location /old-api {
return 301 /api$request_uri;
}

# Точное совпадение — быстрее обычного location


location = /[Link] {
log_not_found off;
access_log off;
return 204;
}

# Запрет прямого доступа к .env и скрытым файлам


location ~ /\. {
deny all;
return 404;
}

# Пример rewrite с захватом группы


location /blog {
rewrite ^/blog/(\d+)$ /articles?id=$1 last;
return 404;
}

Задание 2: Маршрутизация и редиректы


10. Примените конфиг без перезапуска: docker exec nginx_gateway nginx -s reload
11. Проверьте редирект: curl -v [Link] — должен быть 301 →
/api/test
12. Проверьте /[Link]: curl -I [Link] — код 204, без тела
ответа
13. Проверьте защиту: curl [Link] — код 404
14. Проверьте rewrite: curl -v [Link] — должен перейти на /articles?
id=42
15. Добавьте свой location-блок с regex и реализуйте любой redirect на ваш выбор

Страница 7 из 21
NGINX + Docker Compose DevOps / Инфраструктура

16. Добавьте X-Request-ID заголовок в ответы: add_header X-Request-ID


$request_id; (встроенная переменная NGINX)

Задание 3. Reverse Proxy и маршрутизация к


сервисам

3.1 [Link] — маршрутизация к бэкендам


Создайте отдельный файл для proxy-настроек. Обратите внимание на передачу заголовков
— это критично для корректной работы backend-сервисов.

# nginx/conf.d/[Link]

# Карта для определения версии API


map $uri $api_version {
~^/api/v1 "v1";
~^/api/v2 "v2";
default "unknown";
}

server {
listen 80;
server_name [Link]; # Отдельный виртуальный хост

# Проксирование /api/v1 → сервис api_v1


location /api/v1 {
proxy_pass [Link]
proxy_http_version 1.1;

# Стандартные proxy-заголовки
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 X-API-Version $api_version;

# Таймауты
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;

# Буферы
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}

# Проксирование /api/v2 → сервис api_v2


location /api/v2 {
proxy_pass [Link]
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_connect_timeout 5s;
proxy_read_timeout 30s;

Страница 8 из 21
NGINX + Docker Compose DevOps / Инфраструктура

# Реврайт: /api без версии → /api/v2 (latest)


location = /api {
return 302 /api/v2/info;
}
}

💡 Почему [Link], а не просто путь?


Использование отдельного server_name (виртуального хоста) — реальная практика. В
продакшене
вы имеете [Link], [Link], [Link] — всё на одном IP,
один NGINX.
Чтобы [Link] заработал локально, добавьте в /etc/hosts (или C:\Windows\
System32\drivers\etc\hosts):
[Link] [Link]

Задание 3: Reverse Proxy


17. Добавьте [Link] [Link] в /etc/hosts
18. Примените конфиг: docker exec nginx_gateway nginx -s reload
19. Запросите v1: curl [Link] — убедитесь в ответе
{"version":"v1",...}
20. Запросите v2: curl [Link] — должен вернуть
{"version":"v2",...}
21. Проверьте заголовки на backend: curl [Link] —
посмотрите Headers в ответе (X-Real-IP, X-Forwarded-For должны быть)
22. Проверьте редирект /api: curl -v [Link] — должен быть 302 →
/api/v2/info
23. Проверьте, что map работает: убедитесь что X-API-Version виден в ответе
/api/v1/echo
24. Напишите curl-команду которая делает POST запрос с JSON-телом к
/api/v2/echo и изучите что возвращает echo-эндпоинт

Задание 4. Load Balancing и отказоустойчивость

4.1 Настройка upstream с несколькими серверами


Добавьте в начало [Link] (до блока server) конфигурацию upstream:

# Добавить в nginx/conf.d/[Link] ПЕРЕД блоком server {}

# Round-robin балансировка между v1 и v2 (для /api/lb)


upstream api_pool {
server api_v1:5000 weight=3; # 75% трафика
server api_v2:5000 weight=1; # 25% трафика

keepalive 16; # пул постоянных соединений


keepalive_requests 100;
keepalive_timeout 30s;
}

Страница 9 из 21
NGINX + Docker Compose DevOps / Инфраструктура

# Least-connections с медленным сервисом


upstream api_fair {
least_conn;
server api_v1:5000;
server api_v2:5000;
server api_slow:5000 max_fails=2 fail_timeout=10s;
}

# IP-hash: sticky sessions


upstream api_sticky {
ip_hash;
server api_v1:5000;
server api_v2:5000;
}

4.2 Добавьте location-блоки для LB


Добавьте внутрь server-блока в [Link]:

# Weighted round-robin
location /api/lb {
proxy_pass [Link]
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;

# Retry на следующий сервер при ошибках


proxy_next_upstream error timeout http_500 http_502 http_503;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;

add_header X-Upstream-Addr $upstream_addr always;


add_header X-Upstream-Status $upstream_status always;
}

# Least-connections (с медленным сервером)


location /api/fair {
proxy_pass [Link]
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_read_timeout 5s; # короткий таймаут
proxy_next_upstream timeout error;
add_header X-Upstream-Addr $upstream_addr always;
}

# IP-hash (sticky)
location /api/sticky {
proxy_pass [Link]
proxy_http_version 1.1;
proxy_set_header Connection "";
add_header X-Upstream-Addr $upstream_addr always;
}

Задание 4: Load Balancing


25. Примените конфиг и сделайте 10 запросов к /api/lb: for i in {1..10}; do curl -s
[Link] | python3 -c "import sys,json; d=[Link]([Link]);
print(d['version'])"; done
26. Убедитесь, что v1 получает примерно 75% запросов (из-за weight=3)
27. Посмотрите upstream в заголовке: curl -I [Link] и найдите
X-Upstream-Addr

Страница 10 из 21
NGINX + Docker Compose DevOps / Инфраструктура

28. Проверьте least_conn: сделайте 5 запросов к /api/fair — часть пройдёт через


api_slow, но NGINX переключится по таймауту
29. Проверьте sticky: сделайте 5 запросов к /api/sticky — X-Upstream-Addr должен
быть одинаковым (IP клиента фиксирует сервер)
30. Остановите один из бэкендов: docker stop api_v1 — убедитесь, что /api/lb
продолжает работать только через v2
31. Верните сервис: docker start api_v1 — убедитесь, что трафик снова
распределяется
32. Напишите вывод: в каких ситуациях вы бы выбрали round-robin, least_conn
или ip_hash?

Задание 5. HTTPS с самоподписанными


сертификатами

5.1 Генерация сертификата


Выполните команду для создания самоподписанного сертификата (действителен 365 дней):

mkdir -p nginx/ssl

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \


-keyout nginx/ssl/[Link] \
-out nginx/ssl/[Link] \
-subj "/C=RU/ST=Moscow/L=Moscow/O=University/CN=localhost"

# Проверка сертификата:
openssl x509 -in nginx/ssl/[Link] -noout -text | grep -E 'Subject|Validity|Not'

5.2 [Link] — HTTPS сервер


# nginx/conf.d/[Link]
server {
listen 443 ssl http2;
server_name localhost;

# Сертификаты
ssl_certificate /etc/nginx/ssl/[Link];
ssl_certificate_key /etc/nginx/ssl/[Link];

# Современные настройки TLS


ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-
GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

# HSTS (осторожно: включайте только для продакшена!)


# add_header Strict-Transport-Security "max-age=31536000" always;

root /usr/share/nginx/html;
index [Link];

location / {

Страница 11 из 21
NGINX + Docker Compose DevOps / Инфраструктура

try_files $uri $uri/ =404;


}

# Проксируем API через HTTPS


location /api {
proxy_pass [Link]
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
}

# Редирект HTTP → HTTPS


server {
listen 80;
server_name localhost;
return 301 [Link]
}

⚠ Конфликт server_name в [Link] и [Link]


В [Link] у вас listen 80 default_server с server_name _;
В [Link] мы добавляем listen 80 server_name localhost.
Если возникает конфликт — замените в [Link]: server_name _; на server_name
[Link];
и уберите default_server, либо оставьте редирект только для [Link].

Задание 5: HTTPS
33. Обновите порты в [Link] (если ещё не указаны): добавьте
443:443
34. Примените: docker compose restart nginx
35. Проверьте HTTPS: curl -k [Link] — флаг -k игнорирует ошибку
самоподписанного сертификата
36. Проверьте редирект: curl -v [Link] — должен быть 301 →
[Link]
37. Проверьте HTTP/2: curl -k --http2 -I [Link] — в заголовках
найдите :version: h2 или HTTP/2
38. Посмотрите информацию о TLS: curl -kv [Link] 2>&1 | grep -E 'TLS|
SSL|cipher'
39. Откройте [Link] в браузере — изучите информацию о сертификате
через значок замка
40. Раскомментируйте HSTS заголовок, проверьте его наличие в ответах.
Объясните риски включения HSTS с самоподписанным сертификатом

Задание 6. Безопасность: Rate Limiting и заголовки

6.1 [Link]
# nginx/conf.d/[Link]

# ── Rate Limit зоны ──────────────────────────────────────────────


# 10 запросов/сек на IP для API
limit_req_zone $binary_remote_addr zone=api_ratelimit:10m rate=10r/s;

Страница 12 из 21
NGINX + Docker Compose DevOps / Инфраструктура

# 5 запросов/мин на IP для auth-эндпоинтов


limit_req_zone $binary_remote_addr zone=auth_ratelimit:5m rate=5r/m;
# Ограничение числа одновременных соединений
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

server {
listen 80;
server_name [Link];

# ── Заголовки безопасности ──────────────────────────────────


add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin"
always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
add_header Content-Security-Policy "default-src 'self'" always;

# ── API с rate limiting ──────────────────────────────────────


location /api {
limit_req zone=api_ratelimit burst=20 nodelay;
limit_req_status 429;
limit_conn conn_limit 10;

proxy_pass [Link]
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

# ── Эмуляция auth endpoint (жёсткий лимит) ──────────────────


location /auth {
limit_req zone=auth_ratelimit burst=3;
limit_req_status 429;
return 200 '{"auth":"mock endpoint"}';
add_header Content-Type "application/json";
}

# ── ACL: только локальные IP к /admin ────────────────────────


location /admin {
allow [Link];
allow [Link]/8;
deny all;
return 200 '{"admin":"access granted"}';
add_header Content-Type "application/json";
}

# ── Basic Auth ───────────────────────────────────────────────


location /private {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/ssl/.htpasswd;
return 200 '{"private":"secret content"}';
add_header Content-Type "application/json";
}
}

6.2 Создание .htpasswd


Файл паролей нужно создать и смонтировать в контейнер. Добавьте в volumes NGINX в
[Link]: - ./nginx/ssl/.htpasswd:/etc/nginx/ssl/.htpasswd:ro

# Вариант 1: Если установлен apache2-utils


htpasswd -c nginx/ssl/.htpasswd student

Страница 13 из 21
NGINX + Docker Compose DevOps / Инфраструктура

# Вариант 2: Через Python (без зависимостей)


python3 -c "
import crypt, getpass
user = 'student'
passwd = 'password123'
h = [Link](passwd, [Link](crypt.METHOD_SHA512))
print(f'{user}:{h}')
" > nginx/ssl/.htpasswd

# Вариант 3: Через Docker (не нужно ничего устанавливать)


docker run --rm httpd:2.4-alpine htpasswd -nb student password123 \
> nginx/ssl/.htpasswd

💡 Про [Link] и Docker-сети


В Docker allow [Link] не пустит хостовую машину — она приходит с IP Docker-сети.
Поэтому в /admin мы добавили allow [Link]/8.
Для тестирования ACL из контейнера: docker exec nginx_gateway curl
[Link]

Задание 6: Безопасность
41. Добавьте [Link] [Link] в /etc/hosts
42. Создайте .htpasswd и пересоберите: docker compose restart nginx
43. Проверьте заголовки безопасности: curl -I [Link] —
должны быть X-Frame-Options, X-Content-Type-Options и другие
44. Проверьте rate limit: for i in {1..15}; do curl -s -o /dev/null -w "%{http_code}\n"
[Link] done — при превышении должен появиться
429
45. Проверьте auth limit: запустите curl 8 раз к /auth — первые 3 проходят
мгновенно, остальные тормозят или возвращают 429
46. Проверьте ACL: curl [Link] с хоста — если получаете
403, попробуйте из контейнера: docker exec nginx_gateway wget -qO-
[Link]
47. Проверьте Basic Auth: curl [Link] — 401; curl -u
student:password123 [Link] — 200
48. Зафиксируйте все заголовки безопасности из ответа NGINX и объясните
назначение каждого

Задание 7. Кэширование на стороне прокси

7.1 Добавьте proxy_cache в [Link]


Добавьте в начало nginx/[Link] (внутрь блока http {}), перед include:

# Кэш для прокси-ответов


proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=api_cache:5m
max_size=100m
inactive=10m
use_temp_path=off;

Страница 14 из 21
NGINX + Docker Compose DevOps / Инфраструктура

Добавьте в [Link] новый location, который кэширует ответы:

# Кэширующий endpoint — только GET, только 200 OK


location /api/cached {
proxy_pass [Link]
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;

proxy_cache api_cache;
proxy_cache_valid 200 30s; # кэш 30 сек
proxy_cache_valid 404 5s;
proxy_cache_methods GET;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;

# Показываем статус кэша в заголовке


add_header X-Cache-Status $upstream_cache_status always;
add_header X-Upstream-Addr $upstream_addr always;

# Не кэшировать если клиент прислал Cache-Control: no-cache


proxy_cache_bypass $http_pragma $http_authorization;
}

Для работы кэша на диске нужно смонтировать том. Добавьте в [Link] в


секции volumes NGINX:

- nginx_cache:/var/cache/nginx

# И в глобальной секции volumes:


volumes:
nginx_logs:
nginx_cache:

Задание 7: Proxy Cache


49. Пересоберите: docker compose restart nginx
50. Первый запрос (MISS): curl -I [Link] — заголовок X-
Cache-Status: MISS
51. Повторный запрос (HIT): curl -I [Link] — X-Cache-
Status: HIT, X-Upstream-Addr пустой
52. Подождите 30 секунд и снова запросите — должен быть MISS (кэш протух)
53. Принудительно обойдите кэш: curl -H 'Pragma: no-cache'
[Link] — X-Cache-Status: BYPASS
54. Убедитесь что POST-запросы не кэшируются: curl -XPOST
[Link] — X-Cache-Status отсутствует
55. Проверьте файлы кэша: docker exec nginx_gateway find /var/cache/nginx -type f
| head -10
56. Объясните: зачем нужен proxy_cache_lock on и что произойдёт без него при
одновременных запросах к MISS-ключу?

Задание 8. WebSocket Proxy

8.1 [Link]
Страница 15 из 21
NGINX + Docker Compose DevOps / Инфраструктура

# nginx/conf.d/[Link]

map $http_upgrade $connection_upgrade {


default upgrade;
'' close;
}

server {
listen 80;
server_name [Link];

location / {
root /usr/share/nginx/html;
index [Link];
}

# WebSocket proxy
location /ws {
proxy_pass [Link]
proxy_http_version 1.1;

# Ключевые заголовки для WebSocket handshake


proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

# Долгое соединение
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;

# Отключить буферизацию для real-time


proxy_buffering off;
}
}

8.2 Тестовый WebSocket клиент


Создайте тестовый скрипт для проверки WebSocket соединения:

# tests/test_ws.py
import asyncio, json, websockets

async def test_websocket():


uri = "[Link]
async with [Link](uri) as ws:
# Получить welcome сообщение
welcome = await [Link]()
print(f"Welcome: {[Link](welcome)}")

# Отправить сообщение
await [Link]('Hello, NGINX!')

# Получить echo
echo = await [Link]()
data = [Link](echo)
print(f"Echo received: {data['original']} at {data['timestamp']}")
assert data['type'] == 'echo'
print('✅ WebSocket proxy works correctly!')

[Link](test_websocket())

Страница 16 из 21
NGINX + Docker Compose DevOps / Инфраструктура

Задание 8: WebSocket Proxy


57. Добавьте [Link] [Link] в /etc/hosts
58. Примените конфиг: docker exec nginx_gateway nginx -s reload
59. Проверьте WebSocket через wscat (если установлен): npx wscat -c
[Link] — введите любой текст, получите echo
60. Альтернатива: pip install websockets && python3 tests/test_ws.py
61. Откройте консоль браузера на [Link] и выполните: const ws = new
WebSocket('[Link] [Link] = e =>
[Link]([Link]([Link])); [Link]('test')
62. Проверьте логи: docker logs ws_server — должны быть записи о подключении
клиента
63. Объясните: почему для WebSocket нужны заголовки Upgrade и Connection?
Что произойдёт если их не передать?

Задание 9. Логирование и анализ

9.1 Кастомный формат логов


Обогатите JSON-формат логов дополнительными метриками. Обновите log_format json в
[Link]:

log_format json_extended escape=json


'{"time":"$time_iso8601",'
'"ip":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$uri",'
'"args":"$args",'
'"status":$status,'
'"bytes":$body_bytes_sent,'
'"duration":$request_time,'
'"upstream":"$upstream_addr",'
'"upstream_time":"$upstream_response_time",'
'"cache":"$upstream_cache_status",'
'"ua":"$http_user_agent",'
'"referer":"$http_referer"}';

access_log /var/log/nginx/[Link] json_extended;


error_log /var/log/nginx/[Link] warn;

9.2 Анализ логов


После нескольких запросов к разным эндпоинтам выполните анализ:

# Войти в контейнер:
docker exec -it nginx_gateway sh

# 10 последних запросов (pretty print):


tail -10 /var/log/nginx/[Link] | python3 -m [Link]

# Только статусы и URI:


cat /var/log/nginx/[Link] | python3 -c "
import sys, json
for line in [Link]:

Страница 17 из 21
NGINX + Docker Compose DevOps / Инфраструктура

d = [Link](line)
print(f\"{d['status']} {d['duration']}s {d['uri']}\")
"

# Топ-5 самых медленных запросов:


cat /var/log/nginx/[Link] | python3 -c "
import sys, json
lines = [[Link](l) for l in [Link]]
top = sorted(lines, key=lambda x: float([Link]('duration',0)), reverse=True)[:5]
for r in top: print(f\"{r['duration']}s {r['method']} {r['uri']} →
{r['status']}\")
"

Задание 9: Логирование
64. Обновите формат логов и перезапустите NGINX
65. Сделайте 20+ разных запросов (разные endpoint'ы, методы, параметры)
66. Выполните анализ: найдите самый медленный запрос
67. Найдите запросы с кэш HIT и MISS
68. Подсчитайте процент 2xx/4xx/5xx ответов в логах
69. Экспортируйте лог: docker cp nginx_gateway:/var/log/nginx/[Link]
./[Link] и изучите файл
70. Напишите однострочник (python/awk/jq), который выводит среднее время
ответа за последние 50 запросов

Бонус. Нагрузочное тестирование и мониторинг

⭐ Бонусные задания — за дополнительные баллы


Бонусные задания выполняются по желанию. Каждое бонусное задание = +1 балл к
итоговой оценке.

Б1 Нагрузочный тест с wrk


# Установка wrk (Linux/macOS) или через Docker:
docker run --rm --network host williamyeh/wrk \
-t4 -c50 -d30s [Link]

# Параметры: 4 потока, 50 соединений, 30 секунд


# Запустите тест ДО и ПОСЛЕ включения кэша
# Сравните Requests/sec и Latency
Проведите тест для трёх сценариев: без кэша, с кэшем, с gzip. Оформите сравнение
результатов в таблицу.

Б2 NGINX Prometheus Exporter


Добавьте в [Link] сбор метрик:

# Добавить в [Link]:
nginx_exporter:
image: nginx/nginx-prometheus-exporter:latest
command:

Страница 18 из 21
NGINX + Docker Compose DevOps / Инфраструктура

- -[Link]-uri=[Link]
ports:
- "9113:9113"
networks:
- backend
depends_on:
- nginx

prometheus:
image: prom/prometheus
volumes:
- ./[Link]:/etc/prometheus/[Link]
ports:
- "9090:9090"
networks:
- backend
# [Link]
global:
scrape_interval: 5s
scrape_configs:
- job_name: nginx
static_configs:
- targets: ['nginx_exporter:9113']
Откройте [Link] → Graph → введите запрос: nginx_http_requests_total и
понаблюдайте метрики в реальном времени.

Б3 Автоматическая проверка
Создайте tests/test_all.sh — скрипт, который автоматически проверяет все задания:

#!/bin/bash
PASS=0; FAIL=0
check() { local desc=$1; local cmd=$2; local expected=$3
result=$(eval $cmd 2>/dev/null)
if echo "$result" | grep -q "$expected"; then
echo "✅ $desc"; PASS=$((PASS+1))
else
echo "❌ $desc (got: $result)"; FAIL=$((FAIL+1))
fi
}

check "Static HTML serves" "curl -so/dev/null -w %{http_code}


[Link] "200"
check "NGINX hides version" "curl -sI [Link] | grep Server"
"nginx$"
check "Gzip enabled" "curl -sI -H 'Accept-Encoding: gzip'
[Link] "gzip"
check "404 custom page" "curl -so/dev/null -w %{http_code}
[Link] "404"
check "API v1 proxy works" "curl -s [Link]
"v1"
check "API v2 proxy works" "curl -s [Link]
"v2"
check "HTTPS redirect" "curl -sI [Link] | grep -i location"
"https"
check "HTTPS works" "curl -ks [Link] | grep -i html"
"html"
check "Rate limit returns 429" "for i in {1..20}; do curl -s -o/dev/null -w '%
{http_code}\n' [Link] done | grep 429" "429"
check "Cache HIT on 2nd request" "curl -s [Link] >
/dev/null && curl -sI [Link] | grep X-Cache" "HIT"

echo ""; echo "Results: $PASS passed, $FAIL failed"

Страница 19 из 21
NGINX + Docker Compose DevOps / Инфраструктура

Чеклист для самопроверки


Перед сдачей убедитесь, что выполнено каждое из следующих требований:

Обязательные пункты
☐ docker compose up --build проходит без ошибок
☐ docker compose ps — все 5 контейнеров в статусе running
☐ curl [Link] возвращает 200 с HTML-страницей
☐ server_tokens off — в заголовке Server нет версии NGINX
☐ Gzip работает — curl -H 'Accept-Encoding: gzip' -I [Link] показывает Content-
Encoding: gzip
☐ /api/v1 и /api/v2 корректно проксируются к разным backend-сервисам
☐ Балансировка работает — 10 запросов к /api/lb возвращают ответы от разных инстансов
☐ Остановка одного backend не ломает балансировщик
☐ HTTPS доступен на 443, самоподписанный сертификат установлен
☐ HTTP перенаправляет на HTTPS (301)
☐ Rate limit срабатывает — 20+ запросов возвращают 429
☐ Заголовки безопасности присутствуют в ответах
☐ Basic Auth работает — без пароля 401, с паролем 200
☐ proxy_cache работает — второй запрос к /api/cached даёт X-Cache-Status: HIT
☐ WebSocket соединение проходит через NGINX proxy
☐ JSON логи содержат поля: time, ip, method, uri, status, duration
☐ nginx -t проходит без ошибок

Дополнительные (бонус)
☐ Нагрузочный тест проведён и результаты задокументированы
☐ Prometheus Exporter запущен, метрики собираются
☐ Автоматический тест test_all.sh проходит все проверки

Требования к отчёту
Отчёт должен содержать следующие разделы:

1. Структура проекта: дерево директорий (команда tree или ls -R)


2. Скриншоты или вывод curl/терминала для каждого задания
3. Ответы на вопросы из заданий (выделены курсивом в тексте)
4. Итоговый вывод test_all.sh (если выполнялся)
5. Выводы: что узнали нового, какие трудности возникли

Страница 20 из 21
NGINX + Docker Compose DevOps / Инфраструктура

📦 Сдача работы
Сдайте архив с именем nginx-lab-<фамилия>.[Link], содержащий:
• Все конфигурационные файлы проекта (без node_modules, __pycache__)
• Файл отчёта [Link] или [Link]
• Файл [Link] (экспортированный из контейнера)
Команда для создания архива: tar --exclude='*.pyc' --exclude='node_modules' -czf nginx-
[Link] nginx-lab/

Частые проблемы и решения

Проблема Решение
nginx -t: unknown Модуль не включён. Проверьте nginx -V 2>&1 | grep
directive modules и наличие нужного модуля
502 Bad Gateway Backend контейнер не запущен. docker compose ps, docker
logs api_v1
listen conflict: already Два server-блока слушают один порт/server_name.
taken Сделайте nginx -T и найдите дубликат
connect() failed (111) NGINX не запущен или PID-файл устарел. docker compose
/var/run/[Link] restart nginx
port is already allocated Порт 80/443 занят другим процессом. lsof -i :80 или
измените порты в .env
permission denied Файл не смонтирован или нет прав. Проверьте docker-
/etc/nginx/ssl/.htpasswd compose volumes и chmod 644
WebSocket: 101 не Нет заголовков Upgrade/Connection. Убедитесь в наличии
возвращается map-блока и proxy_set_header Upgrade
Кэш не работает (всегда proxy_cache_path не указан в http-блоке, или том не
MISS) смонтирован. docker exec nginx cat /etc/nginx/[Link] |
grep cache

Удачи! Если что-то не работает — читайте логи: docker logs nginx_gateway

Страница 21 из 21

Вам также может понравиться