--- source: https://habr.com/ru/company/nixys/blog/661443/ --- > [!seealso] Все части > * [Часть 1 - База](Часть%201%20-%20База.md) > * [Часть 2 - docker-compose](Часть%202%20-%20docker-compose.md) ![[190226ba0ccc3ee44cce82b0a7deeabc.jpg]] Мы продолжаем [цикл обучающих статей](https://habr.com/ru/company/nixys/blog/645451/) для начинающих системных администраторов. В серии "Настройка #LEMP сервера с помощью #docker для простых проектов" мы разберем docker и #docker-compose, рассмотрим, как поднять стек #LAMP+ #nginx с помощью docker, а также расскажем вам, в чем преимущество контейнеризации и виртуализации. Несмотря на то, что тема уже достаточно подробно отражена в сети, мы решили подробно описать общие стандарты администрирования с нуля, поскольку регулярно получаем большое количество базовых вопросов от людей, так или иначе связанных с нашей сферой. Цель серии статей - не показать, как развернуть идеальное окружение, а лишь указать на нюансы в работе и защитить начинающих специалистов от базовых ошибок при настройке. Серия статей будет полезна начинающим системным администраторам и инженерам, поэтому если вы опытный админ, то можете смело пропускать данный материал. ## 1. Сравнение виртуализации и контейнеризации ### Что такое #виртуализация? **Виртуализация** - это набор инструментов, который позволяет нам разбить физический сервер на 2, 3 и более сервера. С разными операционными системами, сервисами, зависимостями, библиотеками, модулями и т.д. Пример: У нас имеется сервер 48 CPU 128 RAM. С использованием виртуализации можно разбить физический сервер на два отдельных от друг-друга сервера. ![[ec8a5c4127e431c8f145515a68bf80bc.png]] ### Для чего и когда она необходима? Чаще всего это используется для разделения DEV- и PROD-окружения, так как проводить тестирование в DEV-окружении будет более безопасно на отдельном сервере. Также это подойдет, когда необходимо развернуть сервисы, которые используют разные зависимости и библиотеки. Пример: PROD окружение работает на php7.2+apache, а для тестов необходима площадка на php8.1+apache. В этом случае #apache2 как интерпретатор php не может работать с разными версиями #php, поэтому нам необходим отдельный сервер. ### Какие минусы виртуализации? 1. Ограничение в ресурсах. Инструмент виртуализации не сможет добавлять виртуальные машины свыше наших физический ядер CPU и RAM. Пример: Были созданы 2 сервера вместо одного с 20 CPU и 60 RAM. Соответственно, если необходим еще один виртуальный сервер, инструмент виртуализации сможет добавить сервер, состоящий только из 8 CPU и 8 RAM. Если в конфигурацию добавить параметры, которые будут превышать доступные ресурсы, инструмент виртуализации будет выдавать ошибку. Пример: Вместо 8 доступных CPU будут указаны 9. 1. Резервирование ядер под виртуальную машину. Будет создана ВМ с 20 СPU, соответственно данные CPU на хост машине будут использоваться только ВМ. Ресурсы не смогут быть задействованы на физическом сервере. 2. Отказоустойчивость. Если что-то случиться с физическим сервером, то это повлияет и на наши ВМ. Пример: Упал наш сервер -> Упали наши виртуальные машины. Итог: Если у нас имеются свободные ресурсы нашего физического сервера и нам необходимо новое окружение для тестов. Виртуализация нам в помощь. ### Что такое #контейнеризация? **Контейнеризация** - это улучшенное видение виртуализации. Контейнеризация тоже позволяет создавать отдельные виртуальные машины, только с одним важным отличием: контейнеризация использует ядро операционной системы, установленной на физическом сервере. В контейнере будет запущен только тот сервис, который необходим, и ничего лишнего. Пример: В контейнере будет исключен запуск других сервисов (например GUI), что обеспечивает минимальное потребление ресурсов. ![[f4f98ddc6eeacc501cf5de4d858f4a0b.png]] Огромными плюсом контейнеризации также выступает быстрое развертывание контейнера, так как поднимается только базовая ОС без зависимостей и ненужных пакетов. В контейнере будет развертываться только минимальный набор библиотек, которые необходимы для запуска сервиса. ## 2. #Масштабирование и его виды Представим, что у нас есть проект, который на текущий момент уже использует 90-100% ресурсов. Код на этом проекте был оптимизирован на все 100%, а значит: - В базе данных не имеется зависших процессов. - В #backend нет зависших скриптов, и отсутствуют долго выполняющие команды. - #Frontend был оптимизирован. Ресурсы, которые используются редко, были закешированны. ### Итак, оптимизировать больше нечего. Как поступить? Необходимо масштабирование сервера. Масштабирование бывает двух типов: 1. Горизонтальное 2. Вертикальное ### Вертикальное масштабирование Вертикальное масштабирование - это когда увеличиваются ресурсы сервера, добавляем RAM, увеличиваем CPU и размер дисков. Пример: ![[e39384d5e84a9068013306b2d19c8b88.png]] ### Когда использовать Вертикальное масштабирование? - Когда заканчивается дисковое пространство. Необходимо докупить дополнительные диски. - Когда сервисы долго отрабатывают операции ввода и вывода. Необходимо менять медленный диск на быстрый. - Когда слишком часто приходит OOM Killer из-за нехватки оперативной памяти. Добавляем оперативной памяти на сервер. ### Горизонтальное масштабирование Горизонтальное масштабирование - это когда вместо увеличения ресурсов сервера сервисы переносятся на другие машины и дублируется для отказоустойчивости. Для данных целей лучше использовать разные дата-центры. Пример: - Статические файлы можно вынести на отдельный сервер, либо использовать хранилище S3. - Базу данных можно разделить, после чего настроить репликацию. Один сервер будет работать только на чтение, другой только на запись. - Поиск по статическим данным нужно отправить на отдельный сервер с быстрыми дисками. ![[e93bf88c7d6bd24c039e9080ec8622e3.png]] ### 3. Как связаны контейнеризация и масштабирование? При поднятии контейнера пишутся параметры, которые нам необходимы в контейнере. #yml -файл со всеми зависимостями, модулями. Стоит единожды настроить и написать контейнер, и он будет работать на любом сервере одинаково, с теми самими условиями, которые были заданы при написание yml-файла. То есть, написав сервис либо сервисы всего один раз, можно будет дублировать его (их) на сколько угодно серверов, и на всех серверах сервис будет работать одинаково. Пример: Представим, что наш проект вырос, и ресурсов уже не хватает. Было принято решение арендовать новый сервер. Для нового сервера будет достаточно дублировать yml-файл, в котором будут подняты все те же самые сервисы, которые были развернуты на старом сервере. И при этом развертывание контейнеров экономит драгоценное время, которого так часто не хватает. Ведь куда приятнее развернуть новый сервер всего за пару минут, чем развертывание сервера пару недель с нуля. Поэтому контейнеризация так необходима в вертикальном масштабировании. Для знакомства с контейнеризацией используем сервис контейнеризации docker. Для того, чтобы понять все тонкости работы docker, для начала необходимо понять, как именно работает docker, после чего уже писать полноценный yml-файл, который позволит запускать все сервисы одной командой. ## 4. Docker. БАЗА. ![[8aaa7a14a88df488518b21df858c0597.png]] Все настройки будут проводится на новом сервере. Поэтому для начала рекомендуем сделать базовые настройки, о которых мы писали в предыдущих статьях (если у вас новый сервер!): [Статья по базовой настройке](https://habr.com/ru/company/nixys/blog/645451/). ### Устанавливаем docker Подготовка системы: обновляем индексы пакетов и устанавливаем нужные пакеты для использования репозиториев через https. ```shell apt-get update apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release ``` Добавьте официальный GPG-ключ Docker: ```shell curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg ``` Устанавливаем движок Docker. Обновляем индексы пакетов и устанавливаем последнюю версию Docker Engine и containerd: ```shell apt-get update apt-get install docker-ce docker-ce-cli containerd.io ``` Вся информация об установке была взята из официального мануала docker. Для установки на другие ОС и для более углубленного изучения можете ознакомится на основном сайте [по ссылке](https://docs.docker.com/). ### Начинаем работать с контейнерами Для начала нам нужно выбрать образ для нашего контейнера. Как это сделать? Идем на сайт со всеми образами контейнеров: [hub.docker.com](https://hub.docker.com/) Пример: Нам необходим nginx. Ищем на сайте образ nginx. Заходим на официальный сайт и скачиваем самый последний образ, после чего создаем образ нашего nginx.В данном случае это будет выглядеть так: ```shell docker pull nginx ``` ``` #:~# docker pull nginx Using default tag: latest latest: Pulling from library/nginx 5eb5b503b376: Pull complete 1ae07ab881bd: Pull complete 78091884b7be: Pull complete 091c283c6a66: Pull complete 55de5851019b: Pull complete b559bad762be: Pull complete Digest: sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767 Status: Downloaded newer image for nginx:latest docker.io/library/nginx:latest ``` Образ скачан и собран на нашем сервере. Для проверки образов, которые уже установлены и имеются на нашем сервере, используется команда: ```shell docker images ``` ``` #:/# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c316d5a335a5 2 weeks ago 142MB ``` Анализируем полученную информацию: - REPOSITORY - репозиторий, откуда загружен и собран наш образ. В данном случае официальный образ взятый с [hub.docker.com/_/nginx](https://hub.docker.com/_/nginx). - TAG - версия нашего nginx. У нас самая последняя версия образа. - IMAGE ID - ID нашего образа. - CREATED - дата, когда был собран данный образ и выложен в репозиторий. - SIZE - размер образа. Как видим выше, имеется образ nginx самой последней стабильной версии, выложенный разработчиками nginx 14 дней назад . Он имеет id c316d5a335a5 и занимает 142 MB нашего дискового пространства. Теперь нам необходимо запустить наш образ. Для этого нам нужна будет команда: ```shell docker run -d "название нашего образа" ``` В нашем случае получится команда: ```shell docker run -d nginx ``` Запускаем: ``` #:/# docker run nginx d9eceddb3c2b25f6863949b776cdda280f132dc0664a4b89c4fcbe9c563436e ``` Для проверки запущенных контейнеров используется команда: ```shell docker ps ``` Проверяем наш контейнер: ``` \#docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8d9eceddb3c2 nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp fervent_allen ``` Контейнер запущен и работает. Анализируем полученную информацию: - `CONTAINER ID` - ID нашего контейнера. - `IMAGE` - название нашего изображения. - `COMMAND` - это инструкция которая выполняется при запуске контейнера Docker. В данную инструкцию мы можем поместить все те команды, которые необходимы запускать каждый раз при перезагрузке контейнера. Чтоб не делать данные действия вручную. - `CREATED` - дата, когда мы собрали наш контейнер - `STATUS` - текущий статус контейнера. - `PORTS` - порты которые открыты в контейнере и на которые он принимает соединение. - `NAMES` - название нашего контейнера. Получаем такую информацию: Имеем контейнер с id `a026e61cf29b`, собранный из образа под название nginx, выполняющий команды указанные в `docker-entrypoint.sh`. Собранный около минуты назад и со статусом up (поднят и работает уже около минуты). Работающий порт `80` внутри контейнера и название контейнера `fervent_allen`. Мы получили контейнер (мини виртуальную машину) на нашем сервере. ### Что мы можем с ним сделать? Пример: Пустить трафик на него, и nginx этот трафик обработает. Также есть возможность зайти в наш контейнер. С помощью команды: ```shell docker exec -it <> bash ``` ``` \#docker exec -it 8d9eceddb3c2 bash docker@/# ``` Это необходимо тогда, когда нам необходимо динамически изменить какие-либо параметры без перезагрузки всего контейнера. Пример: Мы можем перечитать конфигурацию nginx в контейнере. Без перезагрузки всего контейнера. Давайте поднимем еще один контейнер, только прокинем `80` порт на host сервер (наш физический сервер), назовем его `test_nginx` и возьмем самую последнюю стабильную версию nginx. - Для название контейнера используем ключ `--name`. - Для проброса порта используем ключ `-p`. Получаем такую команду: ```shell docker run --name test_nginx -p 80:80 -d nginx:latest ``` Мы не будем использовать команду `docker pull`, потому что данная команда не обязательна. Все образы будут скачаны автоматически при запуске `docker run`. Запускаем: ``` #:/# docker run --name test_nginx -p 80:80 -d nginx:latest 8d14004eb99c374f8540d3494d4c31fad794a61b878a73bb516d9e794b842164 ``` Проверяем: ``` #:/# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8d14004eb99c nginx:latest "/docker-entrypoint.…" 48 seconds ago Up 47 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp test_nginxx 8d9eceddb3c2 nginx "/docker-entrypoint.…" 25 minutes ago Up 25 minutes 80/tcp fervent_allen ``` Как видим выше, мы имеем nginx c id номером `8d14004eb99c` последней версии, работающий на `80` порту хост сервера, собранный и поднятый ~48 секунд назад с названием `test_nginx`. Теперь все соединения, которые будут поступать на `localhost:80` на хосте, будут автоматически попадать в контейнер `test_nginx`. Давайте проведем последний эксперимент: 1. Прокинем логи контейнера на хост сервер. Для этого используется ключ `-v`. 2. Также изменим порт с `80` на `443` и название на `test_nginx1`. Получается команда: ```shell docker run -d -p 443:80 -v/var/log/nginx:/var/log/nginx/ --name test_nginx2 nginx:latesttest ``` Запускаем: ``` #:/# docker run -d -p 443:80 -v/var/log/nginx:/var/log/nginx/ --name test_nginx2 nginx:latest 4879ed36031d2b3b7fe461ca19a2784641483cb2ec727c3ed410eddc9a79bd2f ``` Проверяем: ``` #:/# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4879ed36031d nginx:latest "/docker-entrypoint.…" 44 seconds ago Up 43 seconds 0.0.0.0:443->80/tcp, :::443->80/tcp test_nginxx2 8d14004eb99c nginx:latest "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp test_nginx 8d9eceddb3c2 nginx "/docker-entrypoint.…" 28 minutes ago Up 28 minutes 80/tcp fervent_allen ``` Итак, у нас имеется контейнер `test_nginx1`, который слушает `443` порт на хост сервере. Имеет общую директорию с хост сервером `/var/log/nginx`. Любой файл, который будет добавлен внутри контейнера автоматически появится в `/var/log/nginx` на хосте. Это работает и наоборот. Любой файл, который вы добавите в `/var/log/nginx` на хосте, будет и в контейнере. Директория будет общей для хоста и контейнера. Что мы имеем в итоге? У нас есть три разных контейнера. Два из которых слушают порты на хосте и один, который работает только если поступит запрос. ### Как нам сделать запрос в #контейнер? Для этого необходимо знать ip адрес контейнера. Узнать его мы можем с помощью команды `inspect`. При запросе команды будет выдана вся информация о контейнере, но нас интересует только ip адрес. Поэтому используем данную команду: ```shell docker inspect | grep IPAddress ``` ``` docker inspect test_nginx | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "", "IPAddress": "172.16.2.4", ``` Как видим выше, ip адрес нашего контейнера `172.16.2.4`. Он присваивается автоматически при создании контейнера. Он динамический, поэтому, когда будет пересобираться контейнер, ip адрес может изменится. А если необходим статический адрес? Можно присвоить ip адрес контейнеру. Для начала создаем нашу сеть docker. Рассмотрим команду `network`. Чаще всего в работе вам понадобится 3 команды:​​ 1. `create` - создание сети. `docker network create` 2. `ls` - просмотреть все сети docker. `docker network ls` 3. `rm` - удалить сеть docker. `docker network rm` Сначала создаем сеть. Она состоит из диапазона подсети и названия сети: ```shell docker network create --subnet=172.16.0.0/24 test ``` Мы имеем #подсеть docker в диапазоне `172.16.0.0/24` и с названием `test`. Проверяем: ``` #:/# docker network ls NETWORK ID NAME DRIVER SCOPE 58fcfe449655 test bridge local ``` Как видим выше, у нас здесь имеется: - `NETWORK ID` - id сети. - `NAME` - название сети. - `DRIVER` - драйвер сети (по умолчанию `bridge`). - `SCOPE` - где работает данная сеть. В данном случае она локальная. Итак, останавливаем второй созданный нами контейнер под кодовым названием `test_nginx`. Для остановки используется команда `stop` и название контейнера: ```shell docker stop test_nginx test_nginx ``` ``` #:/# docker stop test_nginx test_nginx test_nginx ``` Удаляем контейнер. Для данного действия нам поможет команда `rm`. ``` docker rm test_nginx #:/# docker rm test_nginx test_nginx ``` Проверяем, отсутствует ли контейнер: ``` #:/# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4879ed36031d nginx:latest "/docker-entrypoint.…" 12 minutes ago Up 12 minutes 0.0.0.0:443->80/tcp, :::443->80/tcp test_nginx2 8d9eceddb3c2 nginx "/docker-entrypoint.…" 40 minutes ago Up 40 minutes 80/tcp fervent_allen ``` Добавляем присваивание ip адреса. Делается это с помощью 2 ключей. - Ключ `--net` - название сети. - Ключ `--ip` - ip адрес который мы хотим присвоить из диапазона 172.16.0.0/24 нашей сети. Получаем команду вида: ```shell docker run -d -p 80:80 --net test --ip 172.16.0.2 --name test_nginx nginx:latest ``` Внимание! Не присваивайте ip адрес с единицей в конце, т.к по умолчанию это ip адрес хоста внутри докера. В нашем примере это `172.16.0.1`. То есть, если вам необходимо из контейнера обратиться к хосту, в коде необходимо будет указать `172.16.0.1` вместо `localhost`. Выполняем: ``` ## docker run -d -p 80:80 --net test --ip 172.16.0.2 --name test_nginx nginx:latest b254a1d0a969543466ff77241e8add857054266997674bd6685ff22a589a34a8 ``` Проверяем: ``` #:/# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b254a1d0a969 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp test_nginx 4879ed36031d nginx:latest "/docker-entrypoint.…" 16 minutes ago Up 16 minutes 0.0.0.0:443->80/tcp, :::443->80/tcp test_nginx2 8d9eceddb3c2 nginx "/docker-entrypoint.…" 43 minutes ago Up 43 minutes 80/tcp fervent_allen ``` Также проверяем соединение с помощью telnet, например на `80` порт в контейнере: ``` #:/# telnet 172.16.0.2 80 Trying 172.16.0.2... Connected to 172.16.0.2. Escape character is '^]'. ``` P.S.: [telnet](https://ru.wikipedia.org/wiki/Telnet) держит соединение открытым. Для того чтоб закрыть соединение с вашей стороны. Используйте комбинацию `ctrl`+`c` либо напишете в консоли `quit`. ``` #:/# telnet 172.16.0.2 80 Trying 172.16.0.2... Connected to 172.16.0.2. Escape character is '^]'. quit ``` ``` #:/# telnet 172.16.0.2 80 Trying 172.16.0.2... Connected to 172.16.0.2. Escape character is '^]'. ^C ``` Мы видим, что соединение проходит. Теперь мы умеем присваивать имя контейнеру, ip адрес, умеем пробрасывать директории и порты. ### Настройка nginx и apache2 для работы в контейнере Что нам понадобится для полноценной настройки? 1. Конфигурационные файлы. 2. Название контейнера. 3. Образ контейнера. 4. Директории, которые необходимо пробросить в контейнер. 5. Директории площадок (для обработки статики nginx) и кода (apache2). 6. Директория log файлов для удобства просмотра и анализа. 7. Порты. 8. Сеть. Cоздадим конфигурацию apache2 и nginx для нашего проекта `DOMAIN_NAME`. Конфигурацию можно сохранять куда угодно. Мы привыкли, что все конфигурации docker хранятся по пути `/var/apps`, поэтому создаем директорию и идем туда. ```shell mkdir /var/apps сd /var/apps mkdir apache2 nginx ``` Создаем конфигурацию: ```shell touch /var/apps/apache2/DOMAIN_NAME.conf /var/apps/nginx/DOMAIN_NAME.conf ``` **Важное уточнение:** перед настройкой у вас уже должна быть настроена директория проекта по пути `/var/www/DOMAIN_NAME/`, создан пользователь `DOMAIN_NAME` и предоставлены права на данную директорию созданному пользователю. Данные работы описаны в наших предыдущих статьях. Запускаем apache2. Открываем `/var/apps/apache2/DOMAIN_NAME.conf`: ```shell mcedit /var/apps/apache2/DOMAIN_NAME.conf ``` Вставляем в него: ``` ServerAdmin webmaster@DOMAIN_NAME DocumentRoot /var/www/DOMAIN_NAME/data ServerName DOMAIN_NAME.com ServerAlias www.DOMAIN_NAME.com php_admin_value session.save_path "/var/www/DOMAIN_NAME.com/sess" php_admin_value upload_tmp_dir "/var/www/DOMAIN_NAME.com/upload" php_admin_value open_basedir "/var/www/DOMAIN_NAME.com:." CustomLog /var/www/DOMAIN_NAME/log/apache2/access.log combined ErrorLog /var/www/DOMAIN_NAME/log/apache2/error.log LogLevel error AllowOverride All Options FollowSymLinks Order allow,deny Allow from all ``` - Название контейнера - `apache2`. - Образ - `php:8.0-apache`. - Директории - прокидываем площадку по пути `/var/www/DOMAIN_NAME`. Прокидываем log файлы по пути `/var/log/apache2`. Прокидываем конфигурацию по пути `/var/apps/apache2/DOMAIN_NAME.conf`. - Порты - открывать порты для `apache2` нам не нужно. - Сеть - т.к сеть создана, присвоим ip адрес `172.16.0.3`. Получаем команду: ```shell docker run -d --net test --ip 172.16.0.3 --name apache2 -v/var/www/DOMAIN_NAME.com:/var/www/DOMAIN_NAME.com -v/var/log/apache2:/var/log/apache2 -v/var/apps/apache2:/etc/apache2/sites-enabled php:8.0-apache ``` Выполняем: ``` #:/# docker run -d --net test --ip 172.16.0.3 --name apache2 -v/var/www/DOMAIN_NAME.com:/var/www/DOMAIN_NAME.com -v/var/log/apache2:/var/log/apache2 -v/var/apps/apache2:/etc/apache2/sites-enabled php:8.0-apache Unable to find image 'php:8.0-apache' locally 8.0-apache: Pulling from library/php 5eb5b503b376: Already exists 8b1ad84cf101: Pull complete 38c937dadeb7: Pull complete 6a2f1dc96e59: Pull complete f8c3f82c39d4: Pull complete 90fc6462bd8e: Pull complete c670d99116c9: Pull complete 268554d6fe96: Pull complete 6c29fa0d4492: Pull complete 73e23c50a259: Pull complete 81ac13c96fc2: Pull complete b60a3e623949: Pull complete dac5dd67fd59: Pull complete Digest: sha256:2a251962959a4027456d62a2f02d716b14cd6befc2c16bfdf585e581fe1d6075 Status: Downloaded newer image for php:8.0-apache e8503234b2458320b38fef304a6040ea455bce45f24fc49a90ec46652c32b45 ``` Проверяем: ``` #:/var/apps/apache2# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 51eb2aea4093 php:8.0-apache "docker-php-entrypoi…" About a minute ago Up 12 seconds 80/tcp apache2 ``` Контейнер запущен и работает. Запускаем nginx: Открываем конфигурационный файл по пути `/var/apps/DOMAIN_NAME.com.conf`: ```shell mcedit /var/apps/DOMAIN_NAME.com.conf ``` Вставляем: ``` server { listen 80; server_name DOMAIN_NAME.com www.DOMAIN_NAME.com; access_log /var/www/DOMAIN_NAME/log/nginx/access.log; error_log /var/www/DOMAIN_NAME/log/nginx/error.log; location ~ /\.(svn|git|hg) { deny all; } location ~* ^.+\.(css|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf)$ { root /var/www/DOMAIN_NAME/data; expires max; access_log off; } location / { proxy_pass http://172.16.0.3; # ip адрес контейнера apache2 proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 10m; client_body_buffer_size 1280k; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; } } ``` - Название контейнера - `nginx` - Образ - `nginx:latest` - Директории - прокидываем площадку по пути `/var/www/DOMAIN_NAME.com`. Прокидываем log файлы по пути `/var/log/nginx`. Прокидываем конфигурацию по пути `/var/apps/nginx/`. - Порты - открываем `80` на хосте. - Сеть - т.к. сеть создана, присвоим ip адрес `172.16.0.4`. Получается команда: ```shell docker run -d --net test --ip 172.16.0.4 --name nginx -p 80:80 -v/var/www/DOMAIN_NAME.com:/var/www/DOMAIN_NAME.com -v/var/log/nginx:/var/log/nginx -v/var/apps/nginx:/etc/nginx/conf.d nginx:latest ``` Проверяем: ``` #:/var/apps/nginx# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ea9a37a74237 nginx:latest "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx 51eb2aea4093 php:8.0-apache "docker-php-entrypoi…" 18 minutes ago Up 17 minutes 80/tcp apache2 ``` **Мы получили два контейнера nginx и apache2, nginx слушает все соединения на `80` порту хоста и проксирует на apache2.** Проверим. Нужно добавить в файл hosts на своем ПК. (Если не знаешь, как это сделать, в интернете полно информации по этому поводу). `127.0.0.1 DOMAIN_NAME.com` Далее добавить `index.php` на хосте по пути `/var/www/DOMAIN_NAME.com/data`. ```shell touch /var/www/DOMAIN_NAME/data/index.php ``` Добавить `HELLO WORLD` в файл: ```shell echo HELLO WORLD > /var/www/DOMAIN_NAME.com/data/index.php ``` Выполним запрос с помощью curl: ``` curl DOMAIN_NAME.com HELLO WORLD ``` ``` curl -LI DOMAIN_NAME.com HTTP/1.1 200 OK Server: nginx/1.21.6 Date: Thu, 10 Feb 2022 20:44:46 GMT Content-Type:text/html; charset=UTF-8 Connection: keep-alive X-Powered-By: PHP/8.0.15 ``` Все работает, поздравляю!!! Итак, в данной статье мы познакомились с контейнеризацией, ознакомились с базовыми ключами и командами. Научились искать образы на официальном сайте. Научились присваивать контейнеру ip адрес. Научились пробрасывать директории. Это вся та база, которая необходима нам в дальнейшем. Как совет - попрактикуйтесь в развертывании контейнеров. Поиграйте с образами. Настройте их работу. Так как чем больше практики, тем лучше. Теперь мы имеем два контейнера на хосте, которые взаимодействуют между собой и работают без ошибок. Осталось только залить файлы площадки, установить mysql на хосте и начать работать. Но данный способ больше подходит для тестов. Мы можем каждый раз писать большие команды и запускать их. А если нам необходимо управлять контейнерами? Если необходимо автоматизировать развертывание контейнеров? На помощь приходит инструмент для управления несколькими контейнерами под названием docker-compose. В следующей статье мы будем рассматривать именно его. Спасибо за уделенное время. Надеемся, статья была для вас познавательна. Если остались вопросы можете спрашивать в комментариях, мы с радостью на них ответим. Всем удачного администрирования, и пусть ваш PROD никогда не падает! P.S.: больше полезного материала в нашем телеграм-канале [DevOps FM](https://t.me/devops_fm).