672 lines
40 KiB
Markdown
672 lines
40 KiB
Markdown
---
|
||
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).
|