Skip to content
mrVanich
Назад до нотаток

Docker на Windows 11 за допомогою підсистеми Linux в Windows (WSL): тестове вебсередовище з Nginx, PHP і Python

Коли потрібно швидко перевірити PHP, Python або Nginx, не хочеться щоразу встановлювати їх у Windows, редагувати системні змінні й потім згадувати, що саме було змінено. Docker дає просте тестове середовище: усі компоненти описані файлами проєкту, запускаються однією командою і видаляються без сліду в системі.

У цьому мануалі зберемо локальний вебстенд для Windows 11 на диску D:. Зовні буде доступний лише Nginx на http://localhost:8080; Nginx передаватиме PHP-файли у PHP-FPM, а PHP-сторінка звертатиметься до Python/Flask по внутрішній мережі Docker. Це хороший мінімальний фундамент для тестової веброзробки.

Що буде в результаті

  • Nginx приймає запити браузера на порту 8080;
  • PHP-FPM виконує index.php;
  • Python + Flask відповідає на внутрішній URL http://python:8000/health;
  • код і конфігурація лежать у D:\web-lab;
  • стенд запускається через docker compose up --build -d і вимикається через docker compose down.

1. Підготовка Windows 11

Для Linux-контейнерів у Windows 11 рекомендую Docker Desktop з WSL 2 backend. Відкрийте PowerShell від імені адміністратора та, якщо WSL ще не встановлений, виконайте:

wsl --install
wsl --update
wsl --version

Після перезавантаження встановіть Docker Desktop for Windows. Під час першого запуску залиште увімкненим WSL 2 backend і дочекайтеся статусу Engine running. Compose уже входить до Docker Desktop, окремо його ставити не потрібно.

Перевірка в PowerShell:

docker version
docker compose version

2. Структура проєкту на диску D:

Створіть каталоги:

New-Item -ItemType Directory -Force -Path "D:\web-lab", "D:\web-lab\nginx", "D:\web-lab\php\public", "D:\web-lab\python" | Out-Null

У результаті має бути така структура:

D:\web-lab
├── docker-compose.yml
├── nginx
│   └── default.conf
├── php
│   ├── Dockerfile
│   └── public
│       └── index.php
└── python
    ├── Dockerfile
    ├── app.py
    └── requirements.txt

3. Docker Compose: три сервіси

Створіть D:\web-lab\docker-compose.yml. Compose автоматично створює приватну мережу: контейнер PHP бачить Python за ім’ям сервісу python, але порт Flask не відкритий у Windows.

services:
  nginx:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./php/public:/var/www/html:ro
    depends_on:
      - php

  php:
    build:
      context: ./php
    volumes:
      - ./php/public:/var/www/html:ro

  python:
    build:
      context: ./python
    volumes:
      - ./python:/app
    command: python app.py

4. Конфігурація Nginx

Створіть D:\web-lab\nginx\default.conf:

server {
    listen 80;
    server_name _;

    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass php:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    }

    location ~ /\. {
        deny all;
    }
}

Ключовий рядок тут – fastcgi_pass php:9000. Ім’я php не є IP-адресою: це назва сервісу з docker-compose.yml, яку Docker сам знаходить у своїй мережі.

5. PHP-FPM

Створіть D:\web-lab\php\Dockerfile:

FROM php:8.3-fpm-alpine

WORKDIR /var/www/html

Створіть D:\web-lab\php\public\index.php. Ця сторінка виконується PHP-FPM і робить внутрішній HTTP-запит до Flask.

<?php
declare(strict_types=1);

$pythonUrl = 'http://python:8000/health';
$response = @file_get_contents($pythonUrl);
$payload = $response !== false ? json_decode($response, true) : null;
$pythonIsReady = is_array($payload) && ($payload['status'] ?? '') === 'ok';

$message = $pythonIsReady
    ? $payload['message']
    : 'Python-сервіс поки недоступний. Перевірте docker compose logs python.';
?>
<!doctype html>
<html lang="uk">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Docker web lab</title>
    <style>
        body { font-family: system-ui, sans-serif; max-width: 760px; margin: 64px auto; padding: 0 22px; }
        .status { padding: 20px; border-radius: 10px; background: #f4f4f4; }
        .ok { color: #087443; }
        .error { color: #aa2635; }
        code { background: #eee; padding: 2px 6px; border-radius: 4px; }
    </style>
</head>
<body>
    <h1>Docker test web server</h1>
    <p>Nginx передав цей запит у PHP-FPM. Далі PHP звернувся до Python Flask по внутрішній Docker-мережі.</p>
    <div class="status">
        <h2 class="<?= $pythonIsReady ? 'ok' : 'error' ?>">
            <?= $pythonIsReady ? 'Python працює' : 'Python недоступний' ?>
        </h2>
        <p><?= htmlspecialchars((string) $message, ENT_QUOTES, 'UTF-8') ?></p>
        <p>Внутрішній URL: <code><?= htmlspecialchars($pythonUrl, ENT_QUOTES, 'UTF-8') ?></code></p>
    </div>
</body>
</html>

6. Python і Flask

Створіть D:\web-lab\python\requirements.txt:

Flask==3.1.1

Створіть D:\web-lab\python\Dockerfile:

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

І нарешті D:\web-lab\python\app.py:

from flask import Flask, jsonify

app = Flask(__name__)


@app.get("/health")
def health():
    return jsonify(
        status="ok",
        service="python-flask",
        message="Python Flask відповідає коректно."
    )


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

7. Збірка та запуск

У PowerShell перейдіть у корінь проєкту та запустіть стек:

Set-Location D:\web-lab
docker compose up --build -d
docker compose ps

Відкрийте http://localhost:8080. Правильний результат – сторінка з повідомленням «Python працює» та текстом Python Flask відповідає коректно.

Те саме можна перевірити без браузера:

Invoke-WebRequest http://localhost:8080 | Select-Object -ExpandProperty Content

8. Діагностика та зупинка

  • Переглянути всі логи: docker compose logs -f
  • Логи лише Python: docker compose logs -f python
  • Перезібрати після зміни Dockerfile або requirements.txt: docker compose up --build -d
  • Зупинити й видалити контейнери: docker compose down
  • Видалити також дані named volumes, якщо вони з’являться пізніше: docker compose down -v

PHP- і Python-код змонтовані з диска D:. Це означає, що зміну index.php видно після оновлення браузера, а зміну app.py у цьому простому варіанті зручно застосувати перезапуском: docker compose restart python.

Що далі

Це середовище свідомо просте й призначене для локального тестування. Наступні логічні кроки: додати базу даних MariaDB або PostgreSQL, окремий .env для налаштувань, Xdebug для PHP, автоматичні тести та Git. Але навіть у такому вигляді проєкт уже демонструє головну ідею Docker: вебстенд можна відтворити на іншому комп’ютері з тих самих файлів, без ручного налаштування Nginx, PHP і Python у Windows.

Офіційна документація