Семен Тюреньков
Старший разработчик ГК Юзтех
К вам в отдел выходит новый коллега-разработчик и, прежде чем брать первые задачи в одном из проектов, первым делом ему нужно запустить его у себя локально.
Если это Senior Full Stack разработчик с опытом администрирования Linux, то установка и настройка конфигов Nginx, PHP-fpm, MariaDB для него не будут проблемой (а может и с Docker даже знаком?).
Разработчик Middle уровня (особенно без опыта с backend) возможно пользуется одним из готовых решений под Windows/MacOS.
Junior верстальщик, в свою очередь, раньше не запускал приложение работающее на PHP на своем компьютере вообще, и вот-вот попробует в первый раз.
Было такое? У меня было. Случалось даже поздним вечером помогать новичку с установкой или решением проблемы, возникшей в ходе установки.
А потом, еще через некоторое время, из-за разных конфигов или окружения возникали и новые проблемы из разряда “на моем компьютере же все работает”, которые в том числе могут появиться из-за разных настроек готовых сборок.

Я начал регулярно сталкиваться с этими проблемами, когда стал ответственным за релизы и работу команды. Это было сравнительно недавно.Docker к этому времени уже был широко известен и даже использовался другими отделами в компании, в том числе отделом DevOps на production, но для разработки в образе с прода не хватало компонентов.
Чтобы решить проблему с локальным окружением, которое у всех в команде должно быть максимально похожим на Production, и удобным для разработки, я начал сам погружаться в документацию и примеры по Docker. В этой статье я поделюсь с вами инструкцией как сделать аналогичный образ для нужд своей команды.

Установка
Хотя в результате вы получите практически одинаковые локальные окружения, установка на Windows, Linux и Mac несколько отличается.
Я расскажу о двух способах установки — для Windows и Mac.
Некоторые шаги одинаковы, а об индивидуальных отличиях я скажу по ходу. Начнем с простого:
Docker Desktop
Для начала нужно поставить Docker Desktop, чтобы заниматься менеджментом образов и контейнеров через визуальный интерфейс — видеть скачанные образы, удалять неиспользуемые, запускать или останавливать контейнеры, создавать тома для данных и заходить в командный интерфейс каждого контейнера для выполнения команд, дебага или проверки гипотезы до занесения ее в конфиг.
Немного теории в контексте веб-разработки своими словами (для более официального описания посмотрите документацию):
-
Образ — это конфигурация системы с программами-компонентами и их зависимостями. Образы могут быть как отдельные для каждого компонента (Nginx, PHP-fpm, MariaDB, Redis, Postfix и т.п.), так можно создать и единый образ сразу со всеми компонентами и самим веб-сайтом внутри.
-
Том — это постоянное хранилище данных, используемое контейнерами. Зачем это нужно? Все данные появляющиеся в контейнере удаляются при перезапуске, кроме тех, что заложены в образ. Чтобы сохранить какие-то данные, папки их содержащие нужно подключить как тома.
-
Контейнер — это изолированная система, созданная из образа и, возможно, с подключенными к ней томами, в котором выполняются нужные вам программы.
Дополнительная информация для Windows: для выполнения шагов в этой статье, ваш компьютер должен поддерживать WSL2.
При установке Docker Desktop спросит хотите ли вы использовать WSL2 и вам следует согласиться. К тому же это рекомендуется и самим Docker.
Если вы пропустили этот шаг и Docker у вас давно, попробуйте включите поддержку WSL2 через настройки.

Хранение данных и проекта
Далее вам нужно подготовить, где вы будете хранить данные — папки с проектами, базы данных, nginx конфиги сайтов и SSL сертификаты.
На Windows можно настроить хранение проектов в файловой системе Windows, но обращения к файловой системе от веб-сервера будут очень медленные, поэтому в этой статье мы делаем через WSL2.
Лично мне сложно работать с проектом, когда страницы грузятся долго. Лучшим решением будет хранить файлы в Linux подсистеме, поэтому для пользователей Windows рекомендую поставить еще одну подсистему через WSL2, например Debian или Ubuntu и хранить проекты там.
Создайте или определите 4 папки в проекте, которые вы будете использовать в .env при установке Docker окружения:
Я рекомендую все хранить внутри той же директории, что и проект:
SITE_DOMAIN=”localhost” SITE_PATH=”$PWD” SSL_PATH=”$PWD/ssl” DB_PATH=”$PWD/db” NGINX_CONF_PATH=”$PWD/docker/nginx/site-conf”
Установочный скрипт
Создайте файл install.sh, и все, с кем вы поделитесь проектом, смогут запустить его, чтобы автоматически скачать базовые образы, сбилдить проектные образы и создать тома из папок указанных в .env
#!/bin/bash # скрипт прочитает .env файл и возьмет переменные if [ -f .env ]; then export $(cat .env | xargs) fi # небольшая функция которая будет проверять существует ли том, и если нет, то создаст # его из указанных в .env путей. Если захотите переопределить тома, их надо будет # удалить в интерфейсе Docker Desktop или через командную строку create_volume() { local volume_name=$1 local volume_path=$2 echo "Creating volume $volume_name..." \ && docker volume create --driver local --opt type=none --opt device=$volume_path --opt o=bind $volume_name } # и, наконец, основное тело скрипта, создает папки из указанных путей, на случай если они # еще не существуют. Затем входит в подпапки nginx и php-fpm для сборки образа (об # этом расскажу еще чуть позже). И создает тома используя указанную выше функцию. mkdir -p $SSL_PATH \ && mkdir -p $SITE_PATH \ && mkdir -p $DB_PATH \ && mkdir -p $NGINX_CONF_PATH \ && openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout $SSL_PATH/nginx.key -out $SSL_PATH/nginx.crt \ && sed -i ‘’ "s|server_name localhost;|server_name ${SITE_DOMAIN};|g" "${NGINX_CONF_PATH}/website.conf" \ && cd docker/nginx \ && sh builddocker.sh \ && cd ../php-fpm \ && sh builddocker.sh \ && create_volume ssl $SSL_PATH \ && create_volume site $SITE_PATH \ && create_volume db $DB_PATH \ && create_volume nginx-conf $NGINX_CONF_PATH
Docker Compose
Создайте файл docker-compose.yml, а также добавьте в .env еще одну переменную —
MYSQL_ROOT_PASSWORD=password
Содержимое docker-compose файла ниже, о синтаксисе можете прочитать в документации, а я поясню, что именно будет выполняться.
-
На основе указанных образов запустяться контейнеры
-
В контейнеры пробросятся созданные нами ранее тома
-
В контейнерах откроются порты
-
Создается внутренняя сеть, которая упростит взаимодействие контейнеров друг с другом.
version: '3.1' services: db: image: mariadb:11.2.2 container_name: db restart: always volumes: - db:/var/lib/mysql ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} networks: - localhost_net php-fpm-custom: image: php-fpm-custom:latest container_name: php-fpm-custom restart: always volumes: - www:/var/www ports: - 9000:9000 networks: - localhost_net redis: image: redis:7.2.4 container_name: redis restart: always ports: - 6379:6379 networks: - localhost_net expose: - '6379' nginx-custom: depends_on: - db - php-fpm-custom image: nginx-custom:latest container_name: nginx-custom restart: always volumes: - ssl:/etc/nginx/ssl - www:/var/www - nginx-conf:/etc/nginx/sites-enabled ports: - 80:80 - 443:443 networks: localhost_net: aliases: - ${SITE_DOMAIN} node: image: node:21.5 container_name: node restart: always networks: - localhost_net command: "tail -f /dev/null" phpmyadmin: depends_on: - db image: phpmyadmin:5.2.1 container_name: phpmyadmin restart: always ports: - 8081:80 environment: - PMA_ARBITRARY=1 networks: - localhost_net networks: localhost_net: volumes: ssl: external: true nginx-conf: external: true www: external: true db: external: true
В результате у нас будут контейнеры с Nginx, PHP-fpm, MariaDB, Redis, Phpmyadmin и NodeJS
Небазовые образы
Если по каким-то причинам базового образа недостаточно, вы можете создать папки в проекте с Dockerfile для вашего индивидуального образа. В моем случае мне потребовалось изменить некоторые настройки в Nginx и PHP-fpm образах, поэтому создайте папки nginx и php-fpm соответственно.
В каждой папке создайте Dockerfile и builddocker.sh скрипт.
Nginx Dockerfile
# Используем этот базовый образ FROM nginx:1.25.3 # Установим дополнительные пакеты, которые оказались нужны в этом контейнере RUN apt-get update \ && apt-get -y install lsb-release libssl-dev ca-certificates curl openssl # Если хотите пробросить кастомный основной конфиг nginx, то это можно сделать здесь COPY nginx.conf /etc/nginx/nginx.conf # Откроем порты EXPOSE 80 443 # Запустим Nginx как foreground процесс, чтобы контейнер не выключался CMD ["nginx", "-g", "daemon off;"]
Nginx builddocker.sh
Этот скрипт будет создавать образ с тегом :latest в вашей локальной системе, а также удалять старый образ, если вдруг вы обновите конфигурацию, который потом будет использовать docker compose.
#!/bin/bash docker build -t nginx-custom --label nginx-custom . \ && docker image prune --force --filter='label=nginx-custom'
Основной конфиг nginx.conf
В моем случае я только увеличил client_max_body_size, возможно еще gzip и tcp директивы, не помню точно стандартный конфиг, но настройка конфига nginx под себя — это тема отдельных статей.
user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nodelay on; tcp_nopush on; keepalive_timeout 35; gzip on; client_max_body_size 20M; include /etc/nginx/conf.d/*.conf; }
PHP-fpm Dockerfile
# В моем случае я использую 8.1 т.к. WordPress ещё слабо поддерживает версии выше, но вы можете указать нужную вам версию PHP. FROM php:8.1-fpm # добавим в PATH композер, который мы установим далее. ENV PATH="/root/.composer/vendor/bin:${PATH}" # А вот здесь устанавливаем довольно много всего - рекомендуемые php-extensions для WordPress, Composer, WP-CLI, RUN apt-get update \ && apt-get install -y libzip-dev unzip libpq-dev libmagickwand-dev libpng-dev libjpeg-dev libfreetype6-dev less \ && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-install zip pdo_pgsql pgsql bcmath opcache \ && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ && chmod +x /usr/local/bin/composer \ && curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ && chmod +x wp-cli.phar \ && mv wp-cli.phar /usr/local/bin/wp \ && pecl install imagick && docker-php-ext-enable imagick \ && docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) gd # Если хотите пробросить кастомный php.ini, то это можно раскомментировать # COPY php.ini /usr/local/etc/php/php.ini # Expose port 9000 for PHP-FPM EXPOSE 9000 # Start PHP-FPM CMD ["php-fpm"]
Кастомизированный php.ini
Не буду публиковать весь огромный файл конфига, но стандартно я в нем меняю следующие настройки:
upload_max_filesize = 16M
post_max_size = 16M
Следует помнить, что за основу нужно брать стандартный конфиг от выбранной вами версии PHP, найти его можно например запустив контейнер сначала без пробрасывания собственного php.ini.
Nginx конфиги для проектов
В указанной в .env папке для nginx конфигов сайтов создайте файл website.conf
Мы указали выше, что все созданные конфиги будут добавляться, как том в контейнер с Nginx, и при запуске он их учтет.
В моем случае, конфиг для сайта на WordPress вот такой:
server { listen 80; server_name localhost; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name localhost; gzip on; gzip_comp_level 5; gzip_min_length 256; gzip_proxied any; gzip_vary on; ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; root /var/www/site; index index.php; location / { proxy_request_buffering off; try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { include fastcgi_params; fastcgi_pass php-fpm-custom:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; } }
Важно! Выбранные вами домены надо также указать в hosts родительской системы или прописать в DNS, если разворачиваете на сервере!
Например:
127.0.0.1 mysite.test
Запуск окружения
Если вы еще этого не сделали, то выполните
sh install.sh в вашем терминале.
Затем вы можете запустить окружение командой
docker-compose up -d
И остановить командой
docker-compose down
Алиасы
Так как все привычные вам инструменты разработки тоже оказались в контейнере, то это скажется на процессе разработки.
Привычные вам короткие команды потребуется запускать через docker exec или docker run, поэтому предлагаю использовать алиасы. Да и линтеры не будут выполняться если в родительской системе их нет и не созданы алиасы на контейнер.
Добавьте следующее содержимое в файл ~/.bashrc (если используете bash в качестве терминала):
alias composer='docker exec -it -w /var/www/site php-fpm-custom composer' alias phpcs='docker run --rm -it -v $(pwd):/app -w /app php-fpm-custom vendor/bin/phpcs' alias phpcbf='docker run --rm -it -v $(pwd):/app -w /app php-fpm-custom vendor/bin/phpcbf' alias wp='docker exec -it -w /var/www/site php-fpm-custom wp' alias node='docker run --rm -it -v $(pwd):/app -w /app node:21.5 node' alias npm='docker run --rm -it -v $(pwd):/app -w /app node:21.5 npm' alias npx='docker run --rm -it -v $(pwd):/app -w /app node:21.5 npx' alias yarn='docker run --rm -it -v $(pwd):/app -w /app node:21.5 yarn'
Алиас позволит вам привычно писать команду, но выполняться будет другая, указанная вами. То есть написав npm run build, вы фактически выполните команду docker run --rm -it -v $(pwd):/app -w /app node:21.5 npm run build
Если у вас в родительской системе уже стоят свои версии ПО и вы хотите оставить их доступными, вы можете изменить алиасы, добавив суффиксы в команды: -docker
Таким образом, вы сможете выполнять npm или npm-docker в зависимости от того, хотите ли вы использовать родительские программы или те, что находятся в контейнере.
IDE
Я использую VScode, поэтому могу рассказать только про эту IDE.
Если вы все же решите использовать только алиасы, а на родительской системе программ не будет, вы можете столкнуться с тем, что придется дополнительно настроить IDE или же заменить некоторые extensions на аналоги, которые будут использовать программы в контейнере.
В моем случае это коснулось линтеров PHP и JS.
В рекомендациях своего проекта я указал следующие:
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint",
"bmewburn.vscode-intelephense-client",
"mtbdata.vscode-phpsab-docker"
Также при использовании подсистемы для хранения файлов необходимо будет установить WSL extension и запускать VScode в режиме WSL.

Заключение
Надеюсь, эта информация окажется вам полезна, а о причинах переноса локального окружения для разработки в Docker, я рассказал в начале статьи.
Возможно, помимо использования разработчиками, вы также сможете внедрить процессы тестирования веток QA командой на их локальных машинах.
Также эти образы можно использовать в пайплайнах GitLab для автоматических линтов и тестов MR.
ссылка на оригинал статьи https://habr.com/ru/articles/808025/
Добавить комментарий