Мы продолжаем цикл обучающих статей для начинающих системных администраторов. В серии "Настройка LEMP сервера с помощью docker для простых проектов" мы разберем docker и docker-compose, рассмотрим, как поднять стек LAMP+Nginx с помощью docker, а также расскажем вам, в чем преимущество контейнеризации и виртуализации.

Несмотря на то, что тема уже достаточно подробно отражена в сети, мы решили подробно описать общие стандарты администрирования с нуля, поскольку регулярно получаем большое количество базовых вопросов от людей, так или иначе связанных с нашей сферой. Цель серии статей - не показать, как развернуть идеальное окружение, а лишь указать на нюансы в работе и защитить начинающих специалистов от базовых ошибок при настройке.

Серия статей будет полезна начинающим системным администраторам и инженерам, поэтому если вы опытный админ, то можете смело пропускать данный материал.

Итак, в этой статье мы разберем:

  1. Сравнение виртуализации и контейнеризации.

    • Что такое виртуализация?

    • Для чего и когда она необходима?

    • Какие минусы виртуализации?

    • Что такое контейнеризация?

  2. Масштабирование и его виды.

    • Вертикальное масштабирование.

    • Когда использовать Вертикальное масштабирование?

    • Горизонтальное масштабирование.

  3. Как связаны контейнеризация и масштабирование.

  4. Docker. БАЗА.

    • Устанавливаем docker.

    • Начинаем работать с контейнерами.

    • Что мы можем сделать с контейнером?

    • Как нам сделать запрос в контейнер?

    • Настройка nginx и apache2 для работы в контейнере.

1. Сравнение виртуализации и контейнеризации.

Что такое виртуализация?

Виртуализация - это набор инструментов, который позволяет нам разбить физический сервер на 2, 3 и более сервера. С разными операционными системами, сервисами, зависимостями, библиотеками, модулями и т.д.

Пример: У нас имеется сервер 48 CPU 128 RAM. С использованием виртуализации можно разбить физический сервер на два отдельных от друг-друга сервера.

Для чего и когда она необходима?

Чаще всего это используется для разделения 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), что обеспечивает минимальное потребление ресурсов. 

Огромными плюсом контейнеризации также выступает быстрое развертывание контейнера, так как поднимается только базовая ОС без зависимостей и ненужных пакетов. В контейнере будет развертываться только минимальный набор библиотек, которые необходимы для запуска сервиса.

2. Масштабирование и его виды.

Представим, что у нас есть проект, который на текущий момент уже использует 90-100% ресурсов. Код на этом проекте был оптимизирован на все 100%, а значит:

  • В базе данных не имеется зависших процессов.

  • В backend нет зависших скриптов, и отсутствуют долго выполняющие команды.

  • Frontend был оптимизирован. Ресурсы, которые используются редко, были закешированны.

Итак, оптимизировать больше нечего. Как поступить?

Необходимо масштабирование сервера. Масштабирование бывает двух типов: 

  1. Горизонтальное

  2. Вертикальное.

Вертикальное масштабирование.

Вертикальное масштабирование - это когда увеличиваются ресурсы сервера, добавляем RAM, увеличиваем CPU и размер дисков.

Пример:

Когда использовать Вертикальное масштабирование?

  • Когда заканчивается дисковое пространство. Необходимо докупить дополнительные диски.

  • Когда сервисы долго отрабатывают операции ввода и вывода. Необходимо менять медленный диск на быстрый.

  • Когда слишком часто приходит OOM Killer из-за нехватки оперативной памяти. Добавляем оперативной памяти на сервер.

Горизонтальное масштабирование.

Горизонтальное масштабирование - это когда вместо увеличения ресурсов сервера сервисы переносятся на другие машины и дублируется для отказоустойчивости. Для данных целей лучше использовать разные дата-центры.

Пример:

  • Статические файлы можно вынести на отдельный сервер, либо использовать хранилище S3.

  • Базу данных можно разделить, после чего настроить репликацию. Один сервер будет работать только на чтение, другой только на запись.

  • Поиск по статическим данным нужно отправить на отдельный сервер с быстрыми дисками.

3. Как связаны контейнеризация и масштабирование?

При поднятии контейнера пишутся параметры, которые нам необходимы в контейнере. yml-файл со всеми зависимостями, модулями.

Стоит единожды настроить и написать контейнер, и он будет работать на любом сервере одинаково, с теми самими условиями, которые были заданы при написание yml-файла. То есть, написав сервис либо сервисы всего один раз, можно будет дублировать его (их) на сколько угодно серверов, и на всех серверах сервис будет работать одинаково.

Пример: Представим, что наш проект вырос, и ресурсов уже не хватает. Было принято решение арендовать новый сервер. Для нового сервера будет достаточно дублировать yml-файл, в котором будут подняты все те же самые сервисы, которые были развернуты на старом сервере. И при этом развертывание контейнеров экономит драгоценное время, которого так часто не хватает. Ведь куда приятнее развернуть новый сервер всего за пару минут, чем развертывание сервера пару недель с нуля. Поэтому контейнеризация так необходима в вертикальном масштабировании. 

Для знакомства с контейнеризацией используем сервис контейнеризации docker. 

Для того, чтобы понять все тонкости работы docker, для начала необходимо понять, как именно работает docker, после чего уже писать полноценный yml-файл, который позволит запускать все сервисы одной командой.

4. Docker. БАЗА.

Все настройки будут проводится на новом сервере. Поэтому для начала рекомендуем сделать базовые настройки, о которых мы писали в предыдущих статьях (если у вас новый сервер!): Статья по базовой настройке.

Устанавливаем docker:

Подготовка системы: обновляем индексы пакетов и устанавливаем нужные пакеты для использования репозиториев через HTTPS.

apt-get update apt-get install \
     ca-certificates \
     curl \
     gnupg \
     lsb-release

Добавьте официальный GPG-ключ Docker:

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Устанавливаем движок Docker.

Обновляем индексы пакетов и устанавливаем последнюю версию Docker Engine и containerd:

apt-get update 
apt-get install docker-ce docker-ce-cli containerd.io

Вся информация об установке была взята из официального мануала docker. Для установки на другие ОС и для более углубленного изучения можете ознакомится на основном сайте по ссылке.

Начинаем работать с контейнерами.

Для начала нам нужно выбрать образ для нашего контейнера. Как это сделать? Идем на сайт со всеми образами контейнеров: hub.docker.com

Пример: Нам необходим nginx. Ищем на сайте образ nginx. Заходим на официальный сайт и скачиваем самый последний образ, после чего создаем образ нашего nginx.В данном случае это будет выглядеть так:

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

Образ скачан и собран на нашем сервере. Для проверки образов, которые уже установлены и имеются на нашем сервере, используется команда:

docker images
#:/# docker images 
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE 
nginx        latest    c316d5a335a5   2 weeks ago   142MB

Анализируем полученную информацию:

  • REPOSITORY - репозиторий, откуда загружен и собран наш образ. В данном случае официальный образ взятый с hub.docker.com/_/nginx.

  • TAG - версия нашего nginx. У нас самая последняя версия образа.

  • IMAGE ID - ID нашего образа.

  • CREATED - дата, когда был собран данный образ и выложен в репозиторий.

  • SIZE - размер образа.

Как видим выше, имеется образ nginx самой последней стабильной версии, выложенный разработчиками nginx 14 дней назад . Он имеет id c316d5a335a5  и занимает 142 MB нашего дискового пространства.

Теперь нам необходимо запустить наш образ. Для этого нам нужно будет команда: 

docker run -d "название нашего образа"

В нашем случае получится команда:

docker run -d nginx

Запускаем:

#:/# docker run nginx 
d9eceddb3c2b25f6863949b776cdda280f132dc0664a4b89c4fcbe9c563436e

Для проверки запущенных контейнеров используется команда:

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 этот трафик обработает. Также есть возможность зайти в наш контейнер. С помощью команды:

docker exec -it <<id контейнера или его имя>> bash 
#docker exec -it 8d9eceddb3c2 bash
docker@/#

Это необходимо тогда, когда нам необходимо динамически изменить какие-либо параметры без перезагрузки всего контейнера. 

Пример: Мы можем перечитать конфигурацию nginx в контейнере. Без перезагрузки всего контейнера. 

Давайте поднимем еще один контейнер, только прокинем 80 порт на host сервер (наш физический сервер), назовем его test_nginx и возьмем самую последнюю стабильную версию nginx. 

  • Для название контейнера используем ключ --name.

  • Для проброса порта используем ключ -p.

Получаем такую команду:

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.

Получается команда:

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 адрес. Поэтому используем данную команду:

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

Сначала создаем сеть. Она состоит из диапазона подсети и названия сети:

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 и название контейнера:

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 нашей сети.

Получаем команду вида:

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 держит соединение открытым. Для того чтоб закрыть соединение с вашей стороны. Используйте комбинацию 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, поэтому создаем директорию и идем туда.

mkdir /var/apps
сd /var/apps
mkdir apache2 nginx

Создаем конфигурацию:

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:

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.

Получаем команду:

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:

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.

Получается команда:

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.

touch /var/www/DOMAIN_NAME/data/index.php

Добавить HELLO WORLD в файл:

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.

Комментарии (7)


  1. oslikia
    20.04.2022 12:57
    +1

    Исправьте описку "Промеряем" -> "Проверяем " и "Проверяем, отсутствует ли образ: " на "Проверяем, отсутствует ли контейнер: ".

    Т.к. это для начинающих админов, то можно добавить про "Escape character is '^]'. ", что это не просто так, а для выхода в консоль надо нажать <CTRL>+<]> и набрать quit.


    1. thisprogame Автор
      20.04.2022 13:40
      +1

      Исправил и добавил информацию по telnet. Спасибо!


  1. ximik13
    20.04.2022 13:02

    Мне кажется или вы вертикальное с горизонтальным перепутали?


    1. thisprogame Автор
      20.04.2022 13:20
      +2

      Добрый день. Да, действительно перепутал. Спасибо, исправил!


  1. Sonzanie
    20.04.2022 15:47
    +3

    Хотел было поныть, что какая-то очень нубская статья для хабра, но потом вчитался и понял, что не прав. Потому что по крайней мере объяснение различий виртуализации и контейнеризации у вас самое понятное без стопки водки из тех, что я встречал.


  1. zhulan0v
    21.04.2022 07:34
    +2

    Спасибо за статью, добавила ясности!

    Почитаю от вас на эту тему ещё с удовольствием ????


  1. Sarvarbek_M
    21.04.2022 09:57
    +1

    Спасибо за информацию !

    Очень полезная информация было для меня