tech-tips/Программное обеспечение/LEMP-стек/Настройка LEMP сервера с помощью docker для простых проектов/Часть 1 - База.md

672 lines
40 KiB
Markdown
Raw Permalink Normal View History

---
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 <<id контейнера или его имя>> 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 <ID либо Имя контейнера> | 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
```
Вставляем в него:
```
<VirtualHost *:80>
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
<Directory "/var/www/DOMAIN_NAME/data">
AllowOverride All
Options FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
```
- Название контейнера - `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).