Собираем минимальный Docker образ
Docker — это инструмент для запуска приложений в изолированных контейнерах. Он делает разработку и развертывание проще, быстрее и безопаснее.
Нужен он для: изоляции приложений и зависимостей, переносимости (софтина работает одинаково на любой системе) и DevOps-процессов.
Плюсы докера: простота развёртывания, экономия ресурсов по сравнению с виртуальными машинами, стандартизация окружения.
Минусы докера: не подходит для задач, требующих полной виртуализации, не оптимален для GUI-приложений, требует грамотной настройки безопасности.
Минимальный пример запуска первого Docker-контейнера с Python (FastAPI):
1. Для развертывания для начала создадим новый проект и установим зависимости. Если не имели раньше дела спитухоном питоном и докером, также скачиваем все необходимое:
После установки создаем рабочую папку:
Активируем виртуальное окружение, чтобы изолировать зависимости проекта и с помощью команды pip freeze автоматически сформировать requirements.txt, не заполняя его вручную:
Cкачиваем необходимые для создания API зависимости с помощью пакетного менеджера pip и сохраняем их в requirements.txt:
2. Создадим в каталоге проекта файл "main.py" с простым API:
3. Ну и наконец добавим в локальный каталог сам Dockerfile - это инструкция по созданию docker контейнера. Cейчас приведу общий пример, в следующих постах подробнее разберем его структуру:
4. На этом все! Мы уже можем собрать свой Docker-образ и запустить его, после чего приложение будет доступно по адресу ниже:
Также можно посмотреть наличие нашего docker контейнера в списке запущенных контейнеров с помощью команды docker ps:
LinuxCamp | #devops #docker #bymaga
Docker — это инструмент для запуска приложений в изолированных контейнерах. Он делает разработку и развертывание проще, быстрее и безопаснее.
Нужен он для: изоляции приложений и зависимостей, переносимости (софтина работает одинаково на любой системе) и DevOps-процессов.
Плюсы докера: простота развёртывания, экономия ресурсов по сравнению с виртуальными машинами, стандартизация окружения.
Минусы докера: не подходит для задач, требующих полной виртуализации, не оптимален для GUI-приложений, требует грамотной настройки безопасности.
Минимальный пример запуска первого Docker-контейнера с Python (FastAPI):
1. Для развертывания для начала создадим новый проект и установим зависимости. Если не имели раньше дела с
$ sudo apt install python3 python3-pip -y
$ sudo apt install docker.io -y
После установки создаем рабочую папку:
$ mkdir fastapi_backend && cd fastapi_backend
Активируем виртуальное окружение, чтобы изолировать зависимости проекта и с помощью команды pip freeze автоматически сформировать requirements.txt, не заполняя его вручную:
$ python3 -m venv venv
$ source venv/bin/activate
Cкачиваем необходимые для создания API зависимости с помощью пакетного менеджера pip и сохраняем их в requirements.txt:
$ pip install fastapi uvicorn
$ pip freeze > requirements.txt
2. Создадим в каталоге проекта файл "main.py" с простым API:
$ touch main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "LinuxCamp"}
3. Ну и наконец добавим в локальный каталог сам Dockerfile - это инструкция по созданию docker контейнера. Cейчас приведу общий пример, в следующих постах подробнее разберем его структуру:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "127.0.0.1", "--port", "8000"]
4. На этом все! Мы уже можем собрать свой Docker-образ и запустить его, после чего приложение будет доступно по адресу ниже:
$ sudo docker build -t myapp .
$ sudo docker run -d -p 8000:8000 myapp
$ curl -L http://127.0.0.1:8000
$ {"Hello":"LinuxCamp"}
http://127.0.0.1:8000
Также можно посмотреть наличие нашего docker контейнера в списке запущенных контейнеров с помощью команды docker ps:
$ docker ps
CONTAINER ID IMAGE COMMAND
640d6f4d1acb fastapi_backend "uvicorn main:app --…"
LinuxCamp | #devops #docker #bymaga
👍38🔥30❤🔥7🤔5❤3⚡1
Из чего состоит Dockerfile
Dockerfile - это набор команд, по которым docker собирает образ контейнера. Для начала нужно немного разъяснить, как это работает под капотом.
Docker-образ - это как слоеный пирог. Почти каждая инструкция в Dockerfile создает новый слой поверх предыдущих.
В одном из предыдущих постов, где мы собирали минимальный образ докера, фигурировал вот такой Dockerfile:
Его и разберем построчно:
1) FROM - команда, которая создает базовый образ. В нашем случае это python:3.11-slim. Но что это и откуда берется? - из Docker Hub!
То есть мы буквально скачиваем зависимость с официального репозитория образов. В хабе имеются образы на многие технологии, языки, базы данных и фреймворки, так что найти нужную базу под проект не будет проблемой.
python:3.11-slim — это официальный образ Python версии 3.11 в минималистичной вариации "slim";
2) WORKDIR - создает рабочую директорию проекта, в которой все команды будут выполняться. Также как и большинство инструкций, создает новый слой;
3) COPY - копирует файлы с хоста в контейнер. Можно заметить, что данная команда выполняется дважды в файле.
Сначала копируется только файл requirements.txt для того, чтобы разделить установку зависимостей и основной код. Это делается ради кэширования слоёв: файл зависимостей меняется редко, и при пересборке будет использоваться уже готовый слой;
4) RUN - выполняет команду внутри контейнера на этапе сборки и создаёт новый слой. В данном случае ставит зависимости из requirements.txt;
5) Ещё один "COPY . ." — теперь закидываем остальной код проекта в контейнер.
6) Последняя инструкция CMD - не создает новый слой, там указаны команды которые выполняются при запуске контейнера. В нашем Dockerfile выполняются команды запуска fastapi-приложения:
— "uvicorn" - сервер, который запускает fastapi-приложение
— "main:app" - main — это имя файла python, а app — это объект fastapi внутри этого файла.
— "--host", "127.0.0.1" - указывает, что сервер будет слушать на локальном интерфейсе.
— "--port", "8000" - указывает порт, на котором работает приложение.
LinuxCamp | #devops #docker #bymaga
Dockerfile - это набор команд, по которым docker собирает образ контейнера. Для начала нужно немного разъяснить, как это работает под капотом.
Docker-образ - это как слоеный пирог. Почти каждая инструкция в Dockerfile создает новый слой поверх предыдущих.
В одном из предыдущих постов, где мы собирали минимальный образ докера, фигурировал вот такой Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "127.0.0.1", "--port", "8000"]
Его и разберем построчно:
1) FROM - команда, которая создает базовый образ. В нашем случае это python:3.11-slim. Но что это и откуда берется? - из Docker Hub!
То есть мы буквально скачиваем зависимость с официального репозитория образов. В хабе имеются образы на многие технологии, языки, базы данных и фреймворки, так что найти нужную базу под проект не будет проблемой.
python:3.11-slim — это официальный образ Python версии 3.11 в минималистичной вариации "slim";
2) WORKDIR - создает рабочую директорию проекта, в которой все команды будут выполняться. Также как и большинство инструкций, создает новый слой;
3) COPY - копирует файлы с хоста в контейнер. Можно заметить, что данная команда выполняется дважды в файле.
Сначала копируется только файл requirements.txt для того, чтобы разделить установку зависимостей и основной код. Это делается ради кэширования слоёв: файл зависимостей меняется редко, и при пересборке будет использоваться уже готовый слой;
4) RUN - выполняет команду внутри контейнера на этапе сборки и создаёт новый слой. В данном случае ставит зависимости из requirements.txt;
5) Ещё один "COPY . ." — теперь закидываем остальной код проекта в контейнер.
6) Последняя инструкция CMD - не создает новый слой, там указаны команды которые выполняются при запуске контейнера. В нашем Dockerfile выполняются команды запуска fastapi-приложения:
— "uvicorn" - сервер, который запускает fastapi-приложение
— "main:app" - main — это имя файла python, а app — это объект fastapi внутри этого файла.
— "--host", "127.0.0.1" - указывает, что сервер будет слушать на локальном интерфейсе.
— "--port", "8000" - указывает порт, на котором работает приложение.
LinuxCamp | #devops #docker #bymaga
👍36🔥8❤7✍4🙈1
docker init: как использовать на Windows
Команда "docker init" создает стартовые файлы, связанные с Docker, для вашего проекта == автоматически генерирует их для запуска приложения под ваш стек. Docker init сейчас доступен только на Docker Desktop на Windows и MacOS, в большинстве ОС на linux данной команды нет:
Устанавливаем Docker Desktop на устройство если не установлен. Если не имели раньше дела спитухоном питоном, также скачиваем его. Как все готово, создаем рабочий каталог:
Создаем и активируем виртуальное окружение чтобы изолировать зависимости проекта и с помощью команды pip freeze автоматически сформировать requirements.txt, не заполняя его вручную:
Cкачиваем необходимые для создания API зависимости с помощью пакетного менеджера pip и сохраняем их в requirements.txt:
Создадим в каталоге проекта файл "main.py" с простым API:
Теперь наконец можем запустить магию:
Нас встречает приветственное сообщение, которое просит нас выбрать язык проекта (python), его версию (3.12.7), порт на котором будет слушать приложение (8000). Далее нужно вписать команду для запуска. Он увидел что я работаю с fastapi и предлагает:
И тут видим, что автоматически были созданы перечисленные в начале файлы! Далее можем абсолютно спокойно поднять контейнер командой:
Дожидаемся пока сбилдится наше приложение и проверяем все ли ок. Переходим в браузер и видим заветное:
LinuxCamp | #devops #docker #bymaga #win
Команда "docker init" создает стартовые файлы, связанные с Docker, для вашего проекта == автоматически генерирует их для запуска приложения под ваш стек. Docker init сейчас доступен только на Docker Desktop на Windows и MacOS, в большинстве ОС на linux данной команды нет:
- .dockerignore
- Dockerfile
- compose.yaml
- README.Docker.md
Устанавливаем Docker Desktop на устройство если не установлен. Если не имели раньше дела с
mkdir my-fastapi-project
cd my-fastapi-project
Создаем и активируем виртуальное окружение чтобы изолировать зависимости проекта и с помощью команды pip freeze автоматически сформировать requirements.txt, не заполняя его вручную:
python3 -m venv venv
source venv/bin/activate
Cкачиваем необходимые для создания API зависимости с помощью пакетного менеджера pip и сохраняем их в requirements.txt:
pip install fastapi uvicorn
pip freeze > requirements.txt
Создадим в каталоге проекта файл "main.py" с простым API:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"Hello": "Docker Init"}
Теперь наконец можем запустить магию:
docker init
Нас встречает приветственное сообщение, которое просит нас выбрать язык проекта (python), его версию (3.12.7), порт на котором будет слушать приложение (8000). Далее нужно вписать команду для запуска. Он увидел что я работаю с fastapi и предлагает:
uvicorn 'main:app' --host=0.0.0.0 --port=8000
И тут видим, что автоматически были созданы перечисленные в начале файлы! Далее можем абсолютно спокойно поднять контейнер командой:
docker compose up --build
Дожидаемся пока сбилдится наше приложение и проверяем все ли ок. Переходим в браузер и видим заветное:
http://127.0.0.1:8000
{"Hello":"Docker Init"}
LinuxCamp | #devops #docker #bymaga #win
👍25🔥7❤🔥2❤1
От cron‑а к DAG‑ам: зачем нужен оркестратор и почему именно Airflow
Представьте простую бытовую задачу: каждую ночь нужно
1) выгрузить базу;
2) превратить её в отчёт;
3) отправить результат в S3;
Пока шагов мало, их легко раскидать по crontab. Но рано или поздно что‑нибудь пойдет медленнее обычного, соседний скрипт стартует раньше, отчёт выедет пустым — и вы начинаете расставлять костыли: sleep, «if‑else», ручные письма об ошибках. Так появляется «снежный ком» расписаний, который трудно контролировать.
Что делает Airflow по‑другому
Airflow описывает тот же процесс в виде DAG — направленного ациклического графа. Каждая задача — узел, каждая зависимость — стрелка.
Расписание теперь одно на весь процесс, а порядок шагов Airflow вычисляет сам:
Что меняется на практике:
- Автоматические повторы — достаточно указать retries, и упавшая задача перезапустится без ваших правок в коде скрипта.
- Чёткая картинка процессов — в веб‑интерфейсе видно, что именно сейчас выполняется, что уже зелёное, а что покраснело.
- Уведомления «из коробки» — функция on_failure_callback отправит сообщение в Telegram/Slack, как только что‑то рухнет.
- Sensors и ожидание событий — вместо бесконечных while sleep 30 можно сказать: «ждать, пока файл появится в папке», и Airflow займётся этим сам.
В одном из следующих постов мы поднимем Airflow одной командой через Docker‑Compose и соберём первый «hello world» DAG.
Реальный сценарий задержки
Допустим, дамп БД обычно занимает пять минут. В одну из ночей, из‑за нагрузки, он растягивается до получаса. Если процесс управляется cron’ом, то в 02:05 по расписанию стартует следующий скрипт report.sh, тот читает ещё незаконченный дамп и отправляет пустой отчёт. Ошибку вы заметите только утром.
С Airflow всё иначе: пока задача dump_db не финишировала, зависимый make_report даже не начнётся. Когда дамп завершится успешно, DAG продолжит движение; если же он дважды упадёт подряд, Airflow пометит цепочку как failed и тут же пришлёт вам Telegram‑уведомление со ссылкой на лог.
Cron остаётся отличным будильником для одиночных задач, но когда шагов несколько и они зависят друг от друга, удобнее отдать управление оркестратору. Airflow даёт одну точку правды (DAG), автоматические ретраи, встроенное журналирование и мгновенные алёрты — и всё это пишется на чистом Python, без костылей.
LinuxCamp | Chat | #devops #bymaga
Представьте простую бытовую задачу: каждую ночь нужно
1) выгрузить базу;
2) превратить её в отчёт;
3) отправить результат в S3;
Пока шагов мало, их легко раскидать по crontab. Но рано или поздно что‑нибудь пойдет медленнее обычного, соседний скрипт стартует раньше, отчёт выедет пустым — и вы начинаете расставлять костыли: sleep, «if‑else», ручные письма об ошибках. Так появляется «снежный ком» расписаний, который трудно контролировать.
Что делает Airflow по‑другому
Airflow описывает тот же процесс в виде DAG — направленного ациклического графа. Каждая задача — узел, каждая зависимость — стрелка.
Расписание теперь одно на весь процесс, а порядок шагов Airflow вычисляет сам:
from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime, timedelta
with DAG(
dag_id="nightly_pipeline",
# стартуем раз в сутки
schedule_interval="0 2 * * *",
start_date=datetime(2025, 4, 1),
# не догоняем пропущенные дни
catchup=False
) as dag:
dump = BashOperator(
task_id="dump_db",
bash_command="/scripts/dump.sh",
retries=2,
retry_delay=timedelta(minutes=10),
# алёрт в чат
on_failure_callback="notify_telegram"
)
transform = BashOperator(
task_id="make_report",
bash_command="/scripts/report.sh"
)
upload = BashOperator(
task_id="upload_s3",
bash_command="/scripts/upload.sh"
)
dump >> transform >> upload
Что меняется на практике:
- Автоматические повторы — достаточно указать retries, и упавшая задача перезапустится без ваших правок в коде скрипта.
- Чёткая картинка процессов — в веб‑интерфейсе видно, что именно сейчас выполняется, что уже зелёное, а что покраснело.
- Уведомления «из коробки» — функция on_failure_callback отправит сообщение в Telegram/Slack, как только что‑то рухнет.
- Sensors и ожидание событий — вместо бесконечных while sleep 30 можно сказать: «ждать, пока файл появится в папке», и Airflow займётся этим сам.
В одном из следующих постов мы поднимем Airflow одной командой через Docker‑Compose и соберём первый «hello world» DAG.
Реальный сценарий задержки
Допустим, дамп БД обычно занимает пять минут. В одну из ночей, из‑за нагрузки, он растягивается до получаса. Если процесс управляется cron’ом, то в 02:05 по расписанию стартует следующий скрипт report.sh, тот читает ещё незаконченный дамп и отправляет пустой отчёт. Ошибку вы заметите только утром.
С Airflow всё иначе: пока задача dump_db не финишировала, зависимый make_report даже не начнётся. Когда дамп завершится успешно, DAG продолжит движение; если же он дважды упадёт подряд, Airflow пометит цепочку как failed и тут же пришлёт вам Telegram‑уведомление со ссылкой на лог.
Cron остаётся отличным будильником для одиночных задач, но когда шагов несколько и они зависят друг от друга, удобнее отдать управление оркестратору. Airflow даёт одну точку правды (DAG), автоматические ретраи, встроенное журналирование и мгновенные алёрты — и всё это пишется на чистом Python, без костылей.
LinuxCamp | Chat | #devops #bymaga
👍52🔥11🤔4❤🔥2❤1💯1🗿1
Шпаргалка по ufw для базовой настройки фаервола
Сетевой безопасностью часто пренебрегают при создании своих проектов на своем сервере. Но даже базовая настройка фаервола уже существенно снижает риски.
ufw (Uncomplicated Firewall) - простой способ настроить фаервол на Linux. Работает как обёртка над iptables, но не требует глубоких знаний сетевого стека.
Зачем вообще нужен фаервол?
Когда ты поднимаешь сервер (особенно публичный), он сразу становится видимым всему интернету. И даже если ты не настраиваешь ничего «опасного», какие-то сервисы по умолчанию могут слушать порты.
ufw помогает ограничить доступ: только те порты, которые ты явно разрешил - будут открыты. Если ты только поднял VPS и хочешь защитить сервер - это именно то, что нужно. Если по какой-то причине у тебя ufw не установлен:
Перед включением стоит задать основные правила, иначе можно случайно отрезать себя от SSH-доступа (особенно на VPS). Начнем с самого важного:
Теперь можно активировать фаервол:
Просмотр статуса и активных правил:
Отключение при необходимости:
Базовые правила, которые стоить задать
Одно правило по разрешению ssh-доступа мы уже добавили, также будет правильно разрешить веб-траффик (http/https):
Полезно запретить весь входящий трафик по умолчанию и разрешить только нужное. Также оставить открытым все на выход:
Полезные команды:
Удалить правило:
Разрешить доступ по конкретному IP:
Это может понадобиться, например, чтобы открыть порты для веб-траффика только Cloudflare или ServicePipe и закрыть для всего остального:
Сбросить все правила и начать с нуля:
После настройки рекомендую протестировать сервер из другого устройства или использовать онлайн-сервисы вроде portquiz или nmap для сканирования портов. Также полезно мониторить логи:
Если ufw работает - ты увидишь записи о заблокированных соединениях, особенно на нестандартные порты. Даже минимальные настройки ufw — это уже огромный шаг вперёд в безопасности. Он не требует глубокого понимания iptables и легко вписывается в ежедневный рабочий процесс. Один раз настроил — и забыл.
LinuxCamp | #utils #devops
Сетевой безопасностью часто пренебрегают при создании своих проектов на своем сервере. Но даже базовая настройка фаервола уже существенно снижает риски.
ufw (Uncomplicated Firewall) - простой способ настроить фаервол на Linux. Работает как обёртка над iptables, но не требует глубоких знаний сетевого стека.
Зачем вообще нужен фаервол?
Когда ты поднимаешь сервер (особенно публичный), он сразу становится видимым всему интернету. И даже если ты не настраиваешь ничего «опасного», какие-то сервисы по умолчанию могут слушать порты.
ufw помогает ограничить доступ: только те порты, которые ты явно разрешил - будут открыты. Если ты только поднял VPS и хочешь защитить сервер - это именно то, что нужно. Если по какой-то причине у тебя ufw не установлен:
$ sudo apt install ufw
Перед включением стоит задать основные правила, иначе можно случайно отрезать себя от SSH-доступа (особенно на VPS). Начнем с самого важного:
$ sudo ufw allow ssh
Теперь можно активировать фаервол:
$ sudo ufw enable
Просмотр статуса и активных правил:
$ sudo ufw status verbose
Отключение при необходимости:
$ sudo ufw disable
Базовые правила, которые стоить задать
Одно правило по разрешению ssh-доступа мы уже добавили, также будет правильно разрешить веб-траффик (http/https):
$ sudo ufw allow 80,443/tcp
Полезно запретить весь входящий трафик по умолчанию и разрешить только нужное. Также оставить открытым все на выход:
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
Полезные команды:
Удалить правило:
$ sudo ufw delete allow 80/tcp
Разрешить доступ по конкретному IP:
$ sudo ufw allow from 192.168.1.10
Это может понадобиться, например, чтобы открыть порты для веб-траффика только Cloudflare или ServicePipe и закрыть для всего остального:
$ ufw default deny incoming
$ ufw default allow outgoing
# пердположим что 192.168.1.10 - IP нашего ServicePipe
$ sudo ufw allow from 192.168.1.10 to any port 22
Сбросить все правила и начать с нуля:
$ sudo ufw reset
После настройки рекомендую протестировать сервер из другого устройства или использовать онлайн-сервисы вроде portquiz или nmap для сканирования портов. Также полезно мониторить логи:
$ sudo less /var/log/ufw.log
Если ufw работает - ты увидишь записи о заблокированных соединениях, особенно на нестандартные порты. Даже минимальные настройки ufw — это уже огромный шаг вперёд в безопасности. Он не требует глубокого понимания iptables и легко вписывается в ежедневный рабочий процесс. Один раз настроил — и забыл.
LinuxCamp | #utils #devops
👍28🔥12❤8❤🔥1
Базовая настройка Nginx и подключение домена: как я это сделал
Недавно настраивал свой домен и хочу поделиться этим мини-опытом — без лишней воды, чтобы ты тоже мог быстро всё поднять у себя.
Шаг 1: Покупка домена
Я купил домен на reg.ru - обычная регистрация, ничего необычного, правда с верификацией по паспорту. После покупки нужно было настроить DNS-записи, чтобы домен знал, на какой сервер ему "смотреть".
Шаг 2: Настройка DNS
В панели управления reg.ru я прописал A-записи для поддоменов. Примерно через 5–10 минут домен уже начал резолвиться (хотя может и сильно дольше):
Шаг 3: Установка Nginx
На сервере я поставил Nginx:
Шаг 4: Bash-скрипт для настройки проксирования
Чтобы не ковыряться руками в /etc/nginx/sites-available, я написал простой скрипт, который:
1) Создаёт конфиг для твоего домена
2) Проксирует запросы на нужный порт
3) Автоматически подключает SSL через certbot (сертификат безопасности, то самое "s" в https)
Запрашиваем данные, вводим домен и порт приложения, на который будет проксироваться nginx:
Задаем стандартные директории для конфигов и логов Nginx:
Создаём папку для логов:
Генерируем конфиг:
Включаем сайт
Проверка и перезапуск:
Получаем SSL-сертификаты:
Финальная проверка и reload
SSL-сертификаты от Let's Encrypt. Certbot сам всё сделал: прописал listen 443 ssl и нужные директивы, плюс настроил редирект с HTTP на HTTPS. Если хочешь, можешь вручную добавить в конфиг ssl_protocols, ssl_ciphers, HSTS и другие фишки безопасности, но базовая настройка уже работает.
В итоге после запуска скрипта твой сайт по HTTPS работает на нужном порту, все логи идут в /var/log/nginx/имя_домена/, и весь трафик проксируется на нужное приложение. Готовый скрипт:
LinuxCamp | #devops #nginx #bymaga
Недавно настраивал свой домен и хочу поделиться этим мини-опытом — без лишней воды, чтобы ты тоже мог быстро всё поднять у себя.
Шаг 1: Покупка домена
Я купил домен на reg.ru - обычная регистрация, ничего необычного, правда с верификацией по паспорту. После покупки нужно было настроить DNS-записи, чтобы домен знал, на какой сервер ему "смотреть".
Шаг 2: Настройка DNS
В панели управления reg.ru я прописал A-записи для поддоменов. Примерно через 5–10 минут домен уже начал резолвиться (хотя может и сильно дольше):
@ → IP моего сервера
www → тот же IP
Шаг 3: Установка Nginx
На сервере я поставил Nginx:
$ sudo apt install nginx -y
Шаг 4: Bash-скрипт для настройки проксирования
Чтобы не ковыряться руками в /etc/nginx/sites-available, я написал простой скрипт, который:
1) Создаёт конфиг для твоего домена
2) Проксирует запросы на нужный порт
3) Автоматически подключает SSL через certbot (сертификат безопасности, то самое "s" в https)
Запрашиваем данные, вводим домен и порт приложения, на который будет проксироваться nginx:
#!/bin/bash
read -p "Домен: (например, example.ru)" DOMAIN
read -p "Порт (например, 9003): " PORT
Задаем стандартные директории для конфигов и логов Nginx:
SITES_AVAILABLE="/etc/nginx/sites-available"
SITES_ENABLED="/etc/nginx/sites-enabled"
CONF_FILE="$SITES_AVAILABLE/$DOMAIN"
LOGS_DIR="/var/log/nginx/$DOMAIN"
Создаём папку для логов:
$ sudo mkdir -p "$LOGS_DIR"
Генерируем конфиг:
sudo tee "$CONF_FILE" > /dev/null <<EOF
server {
listen 80;
server_name $DOMAIN;
location / {
proxy_pass http://127.0.0.1:$PORT;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
error_log $LOGS_DIR/error.log;
access_log $LOGS_DIR/access.log;
}
EOF
Включаем сайт
$ sudo ln -s "$CONF_FILE" "$SITES_ENABLED/$DOMAIN" 2>/dev/null
Проверка и перезапуск:
if sudo nginx -t; then
sudo systemctl reload nginx
else
exit 1
fi
Получаем SSL-сертификаты:
$ sudo certbot --nginx -d "$DOMAIN"
Финальная проверка и reload
if sudo nginx -t; then
sudo systemctl reload nginx
else
exit 1
fi
SSL-сертификаты от Let's Encrypt. Certbot сам всё сделал: прописал listen 443 ssl и нужные директивы, плюс настроил редирект с HTTP на HTTPS. Если хочешь, можешь вручную добавить в конфиг ssl_protocols, ssl_ciphers, HSTS и другие фишки безопасности, но базовая настройка уже работает.
В итоге после запуска скрипта твой сайт по HTTPS работает на нужном порту, все логи идут в /var/log/nginx/имя_домена/, и весь трафик проксируется на нужное приложение. Готовый скрипт:
#!/bin/bash
read -p "Домен: (например, example.ru)" DOMAIN
read -p "Порт (например, 9003): " PORT
SITES_AVAILABLE="/etc/nginx/sites-available"
SITES_ENABLED="/etc/nginx/sites-enabled"
CONF_FILE="$SITES_AVAILABLE/$DOMAIN"
LOGS_DIR="/var/log/nginx/$DOMAIN"
sudo mkdir -p "$LOGS_DIR"
sudo tee "$CONF_FILE" > /dev/null <<EOF
server {
listen 80;
server_name $DOMAIN;
location / {
proxy_pass http://127.0.0.1:$PORT;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
error_log $LOGS_DIR/error.log;
access_log $LOGS_DIR/access.log;
}
EOF
sudo ln -s "$CONF_FILE" "$SITES_ENABLED/$DOMAIN" 2>/dev/null
if sudo nginx -t; then
sudo systemctl reload nginx
else
exit 1
fi
sudo certbot --nginx -d "$DOMAIN"
if sudo nginx -t; then
sudo systemctl reload nginx
else
exit 1
fi
echo "Готово."
LinuxCamp | #devops #nginx #bymaga
👍38🔥13❤6🥱4❤🔥3
Как очистить сервер от мусора
Со временем сервер обрастает мусором: логи, временные файлы, старые кэши. Разберем команды по удалению всего этого добра. Бонусом в конце будет готовый файл для запуска скрипта с логированием, который будет каждый день удалять все ненужное 🙂
Команда для удаления .log-файлов старше 7 дней:
find - команда поиска файлов
/var/log - папка, где хранятся логи
"-type f" - ищем только файлы
-name "*.log" - по шаблону *.log
"-mtime +7" - старше 7 дней
-delete - сразу удалять
Очистить systemd-журналы:
Эта команда удаляет внутренние журналы системы, которым больше 7 дней. Такие журналы хранят события: старты, ошибки, перезагрузки, службы — всё, что происходило в системе. Обычно занимают много места, особенно если сервер работает давно.
Очистить временные файлы:
Удаляет всё из временных директорий. Будь осторожен, если на сервере кто-то работает прямо сейчас.
Очистить кэш и мусор после установки пакетов:
"apt clean" - очищает кэш установленных .deb файлов
"apt autoremove" - удаляет больше не нужные зависимости
"-y" - выполнять без лишних вопросов
Ниже прикладываю готовый файл. Его нужно будет сделать исполняемым и можно добавить в crontab с запуском в каждое воскресенье в 0:00. Все результаты будут сохраняться в файл "/var/log/server-cleanup.log":
LinuxCamp | #devops #utils
Со временем сервер обрастает мусором: логи, временные файлы, старые кэши. Разберем команды по удалению всего этого добра. Бонусом в конце будет готовый файл для запуска скрипта с логированием, который будет каждый день удалять все ненужное 🙂
Команда для удаления .log-файлов старше 7 дней:
$ find /var/log -type f -name "*.log" -mtime +7 -delete
find - команда поиска файлов
/var/log - папка, где хранятся логи
"-type f" - ищем только файлы
-name "*.log" - по шаблону *.log
"-mtime +7" - старше 7 дней
-delete - сразу удалять
Очистить systemd-журналы:
$ journalctl --vacuum-time=7d
Эта команда удаляет внутренние журналы системы, которым больше 7 дней. Такие журналы хранят события: старты, ошибки, перезагрузки, службы — всё, что происходило в системе. Обычно занимают много места, особенно если сервер работает давно.
Очистить временные файлы:
$ rm -rf /tmp/* /var/tmp/*
Удаляет всё из временных директорий. Будь осторожен, если на сервере кто-то работает прямо сейчас.
Очистить кэш и мусор после установки пакетов:
$ apt clean && apt autoremove -y
"apt clean" - очищает кэш установленных .deb файлов
"apt autoremove" - удаляет больше не нужные зависимости
"-y" - выполнять без лишних вопросов
Ниже прикладываю готовый файл. Его нужно будет сделать исполняемым и можно добавить в crontab с запуском в каждое воскресенье в 0:00. Все результаты будут сохраняться в файл "/var/log/server-cleanup.log":
#!/bin/bash
LOG_FILE="/var/log/server-cleanup.log"
echo "[$(date)] Очистка начата" >> "$LOG_FILE"
find /var/log -type f -name "*.log" -mtime +7 -delete >> "$LOG_FILE" 2>&1
journalctl --vacuum-time=7d >> "$LOG_FILE" 2>&1
rm -rf /tmp/* /var/tmp/* >> "$LOG_FILE" 2>&1
apt clean && apt autoremove -y >> "$LOG_FILE" 2>&1
echo "[$(date)] Очистка завершена" >> "$LOG_FILE"
$ sudo chmod +x /usr/local/bin/clean-server.sh
$ sudo crontab -e
0 0 * * 0 /usr/local/bin/clean-server.sh
LinuxCamp | #devops #utils
👍29🔥10❤7🙈1
Caddy - замена Nginx с авто-TLS и минимальной настройкой
Ты наверняка используешь Nginx как прокси-сервер или для выдачи статики. Но есть более свежий и удивительно простой веб-сервер - Caddy. Он сам получит HTTPS, сам перезапустится при изменении конфига и вообще просит к себе минимум внимания. Разбираемся, чем он крут.
Caddy — это современный веб-сервер, написанный на Go. Его главная фишка — всё работает из коробки:
1) Автоматический HTTPS от Let's Encrypt
2) Перезапуск при изменении конфига
3) Встроенный reverse proxy
4) Простая конфигурация (Caddyfile)
Установка:
Минимальный пример Caddyfile:
- Заменяешь "example.com" на свой домен
- localhost:3000 (твой сервис);
Запуск сервера
Caddy работает как systemd-сервис. Чтобы запустить или перезапустить:
После этого caddy сам проверит домен, получит HTTPS-сертификат от Let's Encrypt, настроит прокси и всё заработает)
Почему это удобно
- Не нужно возиться с SSL — HTTPS работает из коробки
- Конфигурация проще, чем у Nginx
- Отлично подходит для pet-проектов, демо, VPS-серверов
Когда лучше остаться с Nginx
1) Если у тебя уже сложная конфигурация с множеством rewrite, map, ssl_params
2) Если нужна кастомная сборка модулей
3) Если используешь stream (TCP/UDP proxy)
LinuxCamp | #utils #devops
Ты наверняка используешь Nginx как прокси-сервер или для выдачи статики. Но есть более свежий и удивительно простой веб-сервер - Caddy. Он сам получит HTTPS, сам перезапустится при изменении конфига и вообще просит к себе минимум внимания. Разбираемся, чем он крут.
Caddy — это современный веб-сервер, написанный на Go. Его главная фишка — всё работает из коробки:
1) Автоматический HTTPS от Let's Encrypt
2) Перезапуск при изменении конфига
3) Встроенный reverse proxy
4) Простая конфигурация (Caddyfile)
Установка:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Минимальный пример Caddyfile:
example.com {
reverse_proxy localhost:3000
}
- Заменяешь "example.com" на свой домен
- localhost:3000 (твой сервис);
Запуск сервера
Caddy работает как systemd-сервис. Чтобы запустить или перезапустить:
sudo systemctl restart caddy
После этого caddy сам проверит домен, получит HTTPS-сертификат от Let's Encrypt, настроит прокси и всё заработает)
Почему это удобно
- Не нужно возиться с SSL — HTTPS работает из коробки
- Конфигурация проще, чем у Nginx
- Отлично подходит для pet-проектов, демо, VPS-серверов
Когда лучше остаться с Nginx
1) Если у тебя уже сложная конфигурация с множеством rewrite, map, ssl_params
2) Если нужна кастомная сборка модулей
3) Если используешь stream (TCP/UDP proxy)
LinuxCamp | #utils #devops
❤31👍14🔥12🤔2🌚1🤝1
Что такое logrotate и как он спасает ваши диски
Системные логи могут незаметно занять весь диск, особенно если ты держишь nginx, docker, или пишешь логи вручную. Если ты ещё не настроил logrotate — однажды тебя очень удивит "df -h". Разберёмся, что это и как работает.
Что делает logrotate
logrotate — это системная утилита, которая:
- архивирует старые логи (обычно в .gz)
- удаляет логи старше N дней/версий
- переименовывает файлы (access.log → access.log.1, и т.д.)
- может перезапускать сервис после ротации (чтобы лог снова появился)
Где живёт конфиг
Ubuntu и Debian по умолчанию кладут правила в "/etc/logrotate.d/*" - здесь обычно уже лежат готовые правила для nginx, apt, docker и т.д. Но конфиги — это ещё не запуск.
Даже если правила лежат в logrotate.d, они не будут применяться, если logrotate либо не установлен, либо не запускается ни через cron, ни через systemd. Можно проверить работает ли logrotate у тебя уже. Посмотри, запланирован ли таймер:
Как всё включить
Установи, если не установлен:
У тебя появится файл "/etc/logrotate.conf". Также появится автоматически сервис и таймер ротации логов. Можно запустить systemd‑таймер, если не запустился автоматически:
Как добавить своё правило
Если у тебя, например, есть лог "/var/log/myapp.log". Создай конфиг, после чего файл будет ротироваться вместе с остальными:
Что с Docker?
Docker пишет логи в "/var/lib/docker/containers/.../*.log". Они не попадают под системный logrotate. Чтобы ограничить размер:
Затем:
Что с journalctl?
Если сервисы логируют в systemd‑журнал, ротация настраивается в "/etc/systemd/journald.conf".
Пример параметров:
SystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=7day
Вывод
1) Конфиги в logrotate.d — это ещё не автомат
2) Сначала проверь, может уже всё работает
3) Самый надёжный способ — logrotate.timer
4) Docker и journalctl — отдельные истории
LinuxCamp | #utils #devops #bymaga
Системные логи могут незаметно занять весь диск, особенно если ты держишь nginx, docker, или пишешь логи вручную. Если ты ещё не настроил logrotate — однажды тебя очень удивит "df -h". Разберёмся, что это и как работает.
Что делает logrotate
logrotate — это системная утилита, которая:
- архивирует старые логи (обычно в .gz)
- удаляет логи старше N дней/версий
- переименовывает файлы (access.log → access.log.1, и т.д.)
- может перезапускать сервис после ротации (чтобы лог снова появился)
Где живёт конфиг
Ubuntu и Debian по умолчанию кладут правила в "/etc/logrotate.d/*" - здесь обычно уже лежат готовые правила для nginx, apt, docker и т.д. Но конфиги — это ещё не запуск.
Даже если правила лежат в logrotate.d, они не будут применяться, если logrotate либо не установлен, либо не запускается ни через cron, ни через systemd. Можно проверить работает ли logrotate у тебя уже. Посмотри, запланирован ли таймер:
systemctl list-timers | grep logrotate
Как всё включить
Установи, если не установлен:
sudo apt install logrotate
У тебя появится файл "/etc/logrotate.conf". Также появится автоматически сервис и таймер ротации логов. Можно запустить systemd‑таймер, если не запустился автоматически:
sudo systemctl enable --now logrotate.timer
Как добавить своё правило
Если у тебя, например, есть лог "/var/log/myapp.log". Создай конфиг, после чего файл будет ротироваться вместе с остальными:
/var/log/myapp.log {
daily # ротация каждый день
rotate 7 # хранить 7 архивов
compress # архивировать .gz
missingok # не ругаться, если файла нет
notifempty # не трогать, если пустой
copytruncate # обрезать файл, не перезапуская процесс
}
sudo nano /etc/logrotate.d/myapp
Что с Docker?
Docker пишет логи в "/var/lib/docker/containers/.../*.log". Они не попадают под системный logrotate. Чтобы ограничить размер:
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Затем:
sudo systemctl restart docker
Что с journalctl?
Если сервисы логируют в systemd‑журнал, ротация настраивается в "/etc/systemd/journald.conf".
Пример параметров:
SystemMaxUse=500M
SystemMaxFileSize=100M
MaxRetentionSec=7day
Вывод
1) Конфиги в logrotate.d — это ещё не автомат
2) Сначала проверь, может уже всё работает
3) Самый надёжный способ — logrotate.timer
4) Docker и journalctl — отдельные истории
LinuxCamp | #utils #devops #bymaga
🔥25👍13❤9
Оптимизация Dockerfile и образов
Общие принципы
- Каждая инструкция Dockerfile создаёт слой - по возможности объединяй команды RUN, чтобы минимизировать количество слоёв.
- Не используй latest: фиксируй версии образов и зависимостей для стабильности и воспроизводимости.
- Всегда указывай версии зависимостей:
Правильная структура Dockerfile
CMD vs ENTRYPOINT
CMD задаёт команду по умолчанию, которую можно переопределить при запуске контейнера. ENTRYPOINT фиксирует поведение и используется, когда контейнер должен всегда выполнять определённую задачу (например, CLI-инструмент). Обычно их комбинируют: ENTRYPOINT задаёт неизменяемую часть, CMD — параметры по умолчанию.
- CMD — удобно для простого запуска,
- ENTRYPOINT — для CLI-обёрток и скриптов, где логика жёстко зафиксирована.
Советы по чистке
- Используй "--no-cache-dir" при установке pip-зависимостей.
- Удаляй .cache, временные и сборочные файлы (pycache, .pytest_cache и т.д.). А лучше использовать ".dockerignore":
Оптимизация кэширования
Сначала копируй наименее изменяемые файлы (например, requirements.txt) — это позволяет использовать кэш при сборке.
Многоступенчатая сборка (multi-stage build)
Когда мы собираем Docker-образ, нам часто нужны временные инструменты — компиляторы, менеджеры зависимостей и прочее. Но в финальном образе они уже не нужны и только раздувают размер.
Multi-stage build позволяет:
1) сначала собрать проект во временном образе (build stage),
2) а затем в финальный образ перенести только то, что нужно для запуска (runtime stage),
3) не таща за собой мусор и сборочные инструменты.
Пример для python:
Пример для Go:
LinuxCamp | #utils #devops
Общие принципы
- Каждая инструкция Dockerfile создаёт слой - по возможности объединяй команды RUN, чтобы минимизировать количество слоёв.
- Не используй latest: фиксируй версии образов и зависимостей для стабильности и воспроизводимости.
- Всегда указывай версии зависимостей:
flask==3.0.0
psycopg2==2.9.9
PyJWT==2.6.0
Правильная структура Dockerfile
FROM python:3.10
WORKDIR /app
# копируем только requirements для кэширования установки
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
rm -rf /root/.cache
# копируем остальной код
COPY . .
CMD ["python", "main.py"]
CMD vs ENTRYPOINT
CMD задаёт команду по умолчанию, которую можно переопределить при запуске контейнера. ENTRYPOINT фиксирует поведение и используется, когда контейнер должен всегда выполнять определённую задачу (например, CLI-инструмент). Обычно их комбинируют: ENTRYPOINT задаёт неизменяемую часть, CMD — параметры по умолчанию.
- CMD — удобно для простого запуска,
- ENTRYPOINT — для CLI-обёрток и скриптов, где логика жёстко зафиксирована.
Советы по чистке
- Используй "--no-cache-dir" при установке pip-зависимостей.
- Удаляй .cache, временные и сборочные файлы (pycache, .pytest_cache и т.д.). А лучше использовать ".dockerignore":
__pycache__/
*.pyc
.git
.venv
.pytest_cache
Оптимизация кэширования
Сначала копируй наименее изменяемые файлы (например, requirements.txt) — это позволяет использовать кэш при сборке.
Многоступенчатая сборка (multi-stage build)
Когда мы собираем Docker-образ, нам часто нужны временные инструменты — компиляторы, менеджеры зависимостей и прочее. Но в финальном образе они уже не нужны и только раздувают размер.
Multi-stage build позволяет:
1) сначала собрать проект во временном образе (build stage),
2) а затем в финальный образ перенести только то, что нужно для запуска (runtime stage),
3) не таща за собой мусор и сборочные инструменты.
Пример для python:
FROM python:3.10 as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY . .
CMD ["python", "main.py"]
Пример для Go:
FROM golang:1.21 as build
COPY . .
RUN go build ./src/main.go
FROM alpine:latest
COPY --from=build /go/main .
CMD ["./main"]
LinuxCamp | #utils #devops
🔥19👍8❤6
Что такое docker network и зачем он нужен?
Docker-сеть (docker network) - это способ связать контейнеры между собой и с внешним миром. Благодаря ей приложения в разных контейнерах могут видеть друг друга, обмениваться данными и быть изолированными от посторонних.
Типы сетей:
1) bridge — дефолтная сеть для контейнеров. Работает как приватная подсеть на хосте. Контейнеры внутри могут общаться друг с другом по имени, но не видят контейнеры из других сетей.
2) host — контейнер использует сетевой стек хоста напрямую. Без изоляции, но с меньшими накладными расходами.
3) none — без сети. Полная изоляция.
4) overlay — для связи контейнеров между хостами (в Swarm-кластере).
5) macvlan — контейнеру назначается собственный MAC-адрес. Выглядит как отдельное устройство в сети.
Зачем нужна Docker-сеть:
Можно обращаться между сервисами внутри сети по имени контейнера, без знания IP-адреса. Это работает благодаря встроенному DNS-серверу Docker.
Например, если у тебя есть два сервиса - frontend и api, то внутри одного контейнера ты можешь сделать запрос:
Docker сам разрешит имя api в IP-адрес другого контейнера в той же сети. Это особенно удобно при использовании docker-compose, где имена сервисов автоматически становятся доступными как DNS-имена.
Создание сети:
Создать сеть можно например командой:
Также можно указать сеть в одном docker-compose.yml:
В этом случае будет создана сеть и туда войдут контейнеры с апи и бд.
Подключение к уже существующей сети:
"external: true" говорит docker-compose, что сеть уже создана заранее (вручную или другим compose-файлом) и её не нужно пересоздавать.
Добавление и удаление вручную:
Контейнер продолжит работу, просто будет подключён или отключён от указанной сети.
Docker-сети позволяют организовать понятную, безопасную и изолированную инфраструктуру между сервисами. Даже при локальной разработке это помогает избавиться от хаоса с IP-адресами и ручными настройками.
LinuxCamp | #docker #devops #bymaga
Docker-сеть (docker network) - это способ связать контейнеры между собой и с внешним миром. Благодаря ей приложения в разных контейнерах могут видеть друг друга, обмениваться данными и быть изолированными от посторонних.
Типы сетей:
1) bridge — дефолтная сеть для контейнеров. Работает как приватная подсеть на хосте. Контейнеры внутри могут общаться друг с другом по имени, но не видят контейнеры из других сетей.
2) host — контейнер использует сетевой стек хоста напрямую. Без изоляции, но с меньшими накладными расходами.
3) none — без сети. Полная изоляция.
4) overlay — для связи контейнеров между хостами (в Swarm-кластере).
5) macvlan — контейнеру назначается собственный MAC-адрес. Выглядит как отдельное устройство в сети.
Зачем нужна Docker-сеть:
Можно обращаться между сервисами внутри сети по имени контейнера, без знания IP-адреса. Это работает благодаря встроенному DNS-серверу Docker.
Например, если у тебя есть два сервиса - frontend и api, то внутри одного контейнера ты можешь сделать запрос:
http://api:5000
Docker сам разрешит имя api в IP-адрес другого контейнера в той же сети. Это особенно удобно при использовании docker-compose, где имена сервисов автоматически становятся доступными как DNS-имена.
Создание сети:
Создать сеть можно например командой:
docker network create mynetwork
Также можно указать сеть в одном docker-compose.yml:
services:
api:
image: my-api
networks:
- mynetwork
db:
image: postgres:17
networks:
- mynetwork
networks:
mynetwork:
В этом случае будет создана сеть и туда войдут контейнеры с апи и бд.
Подключение к уже существующей сети:
services:
frontend:
image: myfront
networks:
- mynetwork
networks:
mynetwork:
external: true
"external: true" говорит docker-compose, что сеть уже создана заранее (вручную или другим compose-файлом) и её не нужно пересоздавать.
Добавление и удаление вручную:
docker network connect mynetwork frontend
docker network disconnect mynetwork frontend
Контейнер продолжит работу, просто будет подключён или отключён от указанной сети.
Docker-сети позволяют организовать понятную, безопасную и изолированную инфраструктуру между сервисами. Даже при локальной разработке это помогает избавиться от хаоса с IP-адресами и ручными настройками.
LinuxCamp | #docker #devops #bymaga
🔥20👍11❤4❤🔥4
Масштабируем сервисы через docker compose --scale
Хочешь, чтобы твой API обрабатывал больше запросов? Или нужно поднять несколько воркеров параллельно? Всё это можно сделать одной командой:
Как это работает
Команда "--scale" запускает несколько контейнеров одного сервиса по конфигурации "docker-compose.yml":
Создаст 3 независимых контейнера api, которые используют один и тот же образ, переменные, порты и т.д.
Варианты использования
Пример docker-compose.yml:
Важно: если nginx поднят в докере и в одной сети с апи, то порт не должен быть жёстко задан как "8000:8000", иначе только один контейнер сможет его занять. Используй:
А в nginx конфиге:
Обычно внутри Docker все контейнеры сервиса находятся под одним именем (например, api), и Docker DNS сам решает, кому из них отдать запрос (round-robin). Если же nginx вне докер сети, то в docker-compose.yml нужно прописать все порты:
И затем настроить nginx конфиг так:
Теперь Nginx равномерно распределяет входящие запросы между 3 контейнерами, которые слушают на разных портах.
Ограничения и подводные камни
1) Порты: нужно либо жестко прописывать порты для каждого инстанса, либо не прописывать порты жестко вообще при масштабировании;
2) Состояние контейнеров не сохраняется — это одинаковые инстансы, а не кластеры;
3) Не путай с кластеризацией — это просто локальное масштабирование;
Про полноценную кластеризацию через Docker Swarm будет в одном из следующих постов :)
LinuxCamp | #devops #docker
Хочешь, чтобы твой API обрабатывал больше запросов? Или нужно поднять несколько воркеров параллельно? Всё это можно сделать одной командой:
docker compose up --scale <сервис>=<кол-во>
Как это работает
Команда "--scale" запускает несколько контейнеров одного сервиса по конфигурации "docker-compose.yml":
docker compose up --scale api=3
Создаст 3 независимых контейнера api, которые используют один и тот же образ, переменные, порты и т.д.
Варианты использования
Пример docker-compose.yml:
version: "3.8"
services:
api:
image: myapp:latest
build: .
ports:
- "8000"
Важно: если nginx поднят в докере и в одной сети с апи, то порт не должен быть жёстко задан как "8000:8000", иначе только один контейнер сможет его занять. Используй:
ports:
- "8000"
А в nginx конфиге:
upstream backend {
server api:8000;
server api:8001;
server api:8002;
}
Обычно внутри Docker все контейнеры сервиса находятся под одним именем (например, api), и Docker DNS сам решает, кому из них отдать запрос (round-robin). Если же nginx вне докер сети, то в docker-compose.yml нужно прописать все порты:
ports:
- "8001:8000"
- "8002:8000"
- "8003:8000"
И затем настроить nginx конфиг так:
upstream backend {
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Теперь Nginx равномерно распределяет входящие запросы между 3 контейнерами, которые слушают на разных портах.
Ограничения и подводные камни
1) Порты: нужно либо жестко прописывать порты для каждого инстанса, либо не прописывать порты жестко вообще при масштабировании;
2) Состояние контейнеров не сохраняется — это одинаковые инстансы, а не кластеры;
3) Не путай с кластеризацией — это просто локальное масштабирование;
Про полноценную кластеризацию через Docker Swarm будет в одном из следующих постов :)
LinuxCamp | #devops #docker
👍23🔥11❤6🦄3
Что такое Docker Swarm и как с ним работать
Когда ты используешь только "docker compose" и поднимаешь по 1 сервису в контейнере в какой-то момент тебе может этого стать недостаточно. Может понадобиться масштабировать сервис чтобы обрабатывать больше запросов, включить отказоустойчивость, развернуть сервис на нескольких серверах.
Тогда можно использовать очень простой но достаточно мощный Docker Swarm - встроенный в Docker механизм кластеризации и управления сервисами. Ничего доустанавливать не нужно!
Что такое Docker Swarm?
Это режим работы Docker, в котором:
1) несколько машин объединяются в кластер (Swarm)
2) сервисы запускаются как реплики на этих машинах
3) нагрузка распределяется автоматически
4) контейнеры перезапускаются при сбоях
Быстрый запуск:
Инициализируй кластер на главной машине
После этого она становится менеджером. Если ты работаешь на одной машине — этого уже достаточно. Создай docker-compose.yml:
Swarm понимает почти тот же формат compose, но использует ключ "deploy:" для настройки. В обычном docker compose блок deploy игнорируется. Запусти сервис как "стек" (stack):
Теперь Docker Swarm создаст сервис с именем mystack_web и запустит 3 реплики nginx.
А если нужно несколько машин?
На других серверах запусти:
Токен можно взять с главной машины через:
Теперь у тебя кластер с множеством нод, и Swarm будет распределять нагрузку автоматически.
Вывод:
Docker Swarm — это простой и мощный способ масштабирования контейнеров и управления кластерами. Он отлично подходит, когда нужно что-то чуть более надежное, чем просто docker-compose, но не хочется разбираться с Kubernetes.
LinuxCamp | #devops #docker #bymaga
Когда ты используешь только "docker compose" и поднимаешь по 1 сервису в контейнере в какой-то момент тебе может этого стать недостаточно. Может понадобиться масштабировать сервис чтобы обрабатывать больше запросов, включить отказоустойчивость, развернуть сервис на нескольких серверах.
Тогда можно использовать очень простой но достаточно мощный Docker Swarm - встроенный в Docker механизм кластеризации и управления сервисами. Ничего доустанавливать не нужно!
Что такое Docker Swarm?
Это режим работы Docker, в котором:
1) несколько машин объединяются в кластер (Swarm)
2) сервисы запускаются как реплики на этих машинах
3) нагрузка распределяется автоматически
4) контейнеры перезапускаются при сбоях
Быстрый запуск:
Инициализируй кластер на главной машине
docker swarm init
После этого она становится менеджером. Если ты работаешь на одной машине — этого уже достаточно. Создай docker-compose.yml:
services:
api:
image: myapi
ports:
- "8000:8000"
deploy:
# запустит 3 экземпляра nginx
replicas: 3
restart_policy:
# перезапускает при сбоях
condition: on-failure
Swarm понимает почти тот же формат compose, но использует ключ "deploy:" для настройки. В обычном docker compose блок deploy игнорируется. Запусти сервис как "стек" (stack):
docker stack deploy -c docker-compose.yml mystack
Теперь Docker Swarm создаст сервис с именем mystack_web и запустит 3 реплики nginx.
А если нужно несколько машин?
На других серверах запусти:
docker swarm join --token <токен> <IP-менеджера>:2377
Токен можно взять с главной машины через:
docker swarm join-token worker
Теперь у тебя кластер с множеством нод, и Swarm будет распределять нагрузку автоматически.
Вывод:
Docker Swarm — это простой и мощный способ масштабирования контейнеров и управления кластерами. Он отлично подходит, когда нужно что-то чуть более надежное, чем просто docker-compose, но не хочется разбираться с Kubernetes.
LinuxCamp | #devops #docker #bymaga
👍25🔥8❤6❤🔥1
Закрываем Docker-порты правильно
Даже если ты настроил ufw и открыл порты только на 22, 433 и 80, у тебя все равно сервисы docker могут торчать наружу. Это происходит потому что docker напрямую управляет iptables/nftables, вставляя свои правила до UFW, поэтому опубликованные порты могут быть доступны, даже если UFW их не показывает.
Почему это небезопасно:
Если ты запускаешь контейнер так:
Или в docker-compose у тебя:
То Docker сам открывает порт всему интернету (0.0.0.0), даже если в ufw status его нет. Это значит, что любой человек из сети может достучаться до твоего контейнера и ufw это не остановит - Docker обходит его фильтры.
Как правильно защитить контейнер:
Лучше всего конечно вообще не открывать порт наружу, если в этом нет нужды. Безопасный вариант (локальный доступ):
либо в docker-compose.yml:
Так сервис будет доступен только внутри сервера. Подходит для проксирования через nginx, который проксирует наружу, скрытых API и внутренних утилит, ssh-туннелей или VPN.
Вариант с открытым портом контейнера:
Если все-таки нужно по какой-то причине поднять контейнер на 0.0.0.0, то лучше закрыть эти порты с помощью правил. Docker поддерживает специальную цепочку DOCKER-USER, где ты можешь сам прописать фильтры:
Важно: правила читаются сверху вниз, поэтому разрешающие вставляем перед правилом DROP.
Проверка:
Если вывод пуст - порт больше не слушает «снаружи».
Вывод:
Docker по умолчанию открывает всё наружу, даже если UFW говорит, что порты закрыты. Если не нужен внешний доступ, не публикуй порты или публикуй на 127.0.0.1. Если доступ нужен - используй DOCKER-USER, чтобы жёстко ограничить доступ к нужным портам.
LinuxCamp | #devops #docker #bymaga
Даже если ты настроил ufw и открыл порты только на 22, 433 и 80, у тебя все равно сервисы docker могут торчать наружу. Это происходит потому что docker напрямую управляет iptables/nftables, вставляя свои правила до UFW, поэтому опубликованные порты могут быть доступны, даже если UFW их не показывает.
Почему это небезопасно:
Если ты запускаешь контейнер так:
docker run -p 8080:80 …
Или в docker-compose у тебя:
ports:
- "8080:80"
То Docker сам открывает порт всему интернету (0.0.0.0), даже если в ufw status его нет. Это значит, что любой человек из сети может достучаться до твоего контейнера и ufw это не остановит - Docker обходит его фильтры.
Как правильно защитить контейнер:
Лучше всего конечно вообще не открывать порт наружу, если в этом нет нужды. Безопасный вариант (локальный доступ):
docker run -p 127.0.0.1:8080:80 …
либо в docker-compose.yml:
ports:
- "127.0.0.1:8080:80"
Так сервис будет доступен только внутри сервера. Подходит для проксирования через nginx, который проксирует наружу, скрытых API и внутренних утилит, ssh-туннелей или VPN.
Вариант с открытым портом контейнера:
Если все-таки нужно по какой-то причине поднять контейнер на 0.0.0.0, то лучше закрыть эти порты с помощью правил. Docker поддерживает специальную цепочку DOCKER-USER, где ты можешь сам прописать фильтры:
# Разрешаем уже установленные соединения
sudo iptables -I DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Разрешаем только нужный порт (например, 8080)
sudo iptables -I DOCKER-USER -p tcp --dport 8080 -j ACCEPT
# Всё остальное режем
sudo iptables -A DOCKER-USER -j DROP
Важно: правила читаются сверху вниз, поэтому разрешающие вставляем перед правилом DROP.
Проверка:
ss -lntp | grep 8080 # Слушает ли порт
Если вывод пуст - порт больше не слушает «снаружи».
Вывод:
Docker по умолчанию открывает всё наружу, даже если UFW говорит, что порты закрыты. Если не нужен внешний доступ, не публикуй порты или публикуй на 127.0.0.1. Если доступ нужен - используй DOCKER-USER, чтобы жёстко ограничить доступ к нужным портам.
LinuxCamp | #devops #docker #bymaga
👍36🔥12❤9👌7✍1
Минимальная настройка мониторинга контейнеров в терминале
Иногда нужно быстро оценить, что происходит с контейнерами: какие из них под нагрузкой, сколько памяти занято, нет ли узких мест по I/O.
Утилита ctop показывает это в реальном времени и позволяет сразу управлять контейнерами без переключения на другие команды.
Установка:
Свежая версия ctop работает и с cgroup v2, поэтому ставим бинарник напрямую из репозитория проекта.
Использование:
Чтобы просмотреть ресурсы всех контейнеров на локальном хосте, достаточно выполнить одну команду:
В интерфейсе можно переключаться между контейнерами стрелками, останавливать или перезапускать их, а также открывать логи прямо оттуда. Если нужно подключиться к удалённому Docker, можно указать адрес перед запуском:
Для тех, кто не хочет ставить бинарник на хост, есть запуск из контейнера:
Чем отличается от docker stats:
docker stats тоже показывает ресурсы, но только в виде списка с метриками. В ctop всё представлено в виде интерактивной таблицы с сортировкой, поиском, фильтрацией и возможностью управлять контейнерами прямо из интерфейса.
Это удобнее, когда нужно не просто увидеть цифры, а быстро действовать, не выходя из одной программы.
Вывод:
С помощью ctop можно в пару команд получить наглядную картину по всем контейнерам, переключаться между хостами и при необходимости управлять ими прямо из терминала. Такой способ полезен как при мониторинге и отладке, так и при повседневной работе с Docker.
LinuxCamp | #utils #devops #docker #bymaga
Иногда нужно быстро оценить, что происходит с контейнерами: какие из них под нагрузкой, сколько памяти занято, нет ли узких мест по I/O.
Утилита ctop показывает это в реальном времени и позволяет сразу управлять контейнерами без переключения на другие команды.
Установка:
Свежая версия ctop работает и с cgroup v2, поэтому ставим бинарник напрямую из репозитория проекта.
sudo install -m 0755 -d /usr/local/bin
curl -L https://github.com/bcicen/ctop/releases/download/v0.7.7/ctop-0.7.7-linux-amd64 -o /tmp/ctop
sudo install -m 0755 /tmp/ctop /usr/local/bin/ctop
ctop -v
Использование:
Чтобы просмотреть ресурсы всех контейнеров на локальном хосте, достаточно выполнить одну команду:
ctop
В интерфейсе можно переключаться между контейнерами стрелками, останавливать или перезапускать их, а также открывать логи прямо оттуда. Если нужно подключиться к удалённому Docker, можно указать адрес перед запуском:
DOCKER_HOST=tcp://remote-host:2375 ctop
Для тех, кто не хочет ставить бинарник на хост, есть запуск из контейнера:
docker run --rm -ti \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
quay.io/vektorlab/ctop:latest
Чем отличается от docker stats:
docker stats тоже показывает ресурсы, но только в виде списка с метриками. В ctop всё представлено в виде интерактивной таблицы с сортировкой, поиском, фильтрацией и возможностью управлять контейнерами прямо из интерфейса.
Это удобнее, когда нужно не просто увидеть цифры, а быстро действовать, не выходя из одной программы.
Вывод:
С помощью ctop можно в пару команд получить наглядную картину по всем контейнерам, переключаться между хостами и при необходимости управлять ими прямо из терминала. Такой способ полезен как при мониторинге и отладке, так и при повседневной работе с Docker.
LinuxCamp | #utils #devops #docker #bymaga
🔥23👍15❤4❤🔥4🥴1
Docker и Podman: в чём разница?
Оба инструмента нужны, чтобы запускать контейнеры в Linux. Контейнер - это процесс с отдельными namespace и cgroup, которому «кажется», что у него есть свой собственный хост, сеть и файловая система. Но у Docker и Podman архитектура работы разная.
Как устроен Docker:
Docker устроен по модели «демон + клиент». На машине работает сервис dockerd с root-правами, который управляет всеми контейнерами. Когда вы пишете команду docker run nginx, CLI отправляет её демону, дальше запускается цепочка:
Контейнерные процессы видны в ps, но родитель у них containerd-shim.
Как устроен Podman:
Podman демона не имеет. Команда podman run nginx сразу создаёт отдельный процесс-контейнер, за которым следит маленький монитор conmon:
В дереве процессов видно сам контейнер, а не один общий демон.
Rootless режим:
С rootless режимом тоже разница. Docker может работать без root через rootlesskit и slirp4netns, но этот режим появился позже и имеет ограничения (например, не все сетевые возможности доступны). Podman изначально задумывался как rootless-friendly:
Сеть:
Сеть у Docker в rootful-режиме - это мост docker0 и правила iptables:
В rootless - slirp4netns. У Podman начиная с версии 4.x сеть делает связка netavark + aardvark-dns, в rootless тоже используется slirp4netns или pasta.
Оркестрация:
Для Docker есть официальный docker compose. У Podman - podman-compose (комьюнити) и поддержка pods:
Ещё Podman умеет работать с Kubernetes-манифестами: podman play kube и podman generate kube.
Вывод:
Оба запускают одни и те же OCI-образы, но Docker работает через демон и привычен для большинства DevOps-пайплайнов, а Podman - без демона, с изначальной поддержкой rootless и pods, ближе к модели Kubernetes.
LinuxCamp | #docker #devops
Оба инструмента нужны, чтобы запускать контейнеры в Linux. Контейнер - это процесс с отдельными namespace и cgroup, которому «кажется», что у него есть свой собственный хост, сеть и файловая система. Но у Docker и Podman архитектура работы разная.
Как устроен Docker:
Docker устроен по модели «демон + клиент». На машине работает сервис dockerd с root-правами, который управляет всеми контейнерами. Когда вы пишете команду docker run nginx, CLI отправляет её демону, дальше запускается цепочка:
dockerd → containerd → containerd-shim → runc → PID 1 контейнера
Контейнерные процессы видны в ps, но родитель у них containerd-shim.
Как устроен Podman:
Podman демона не имеет. Команда podman run nginx сразу создаёт отдельный процесс-контейнер, за которым следит маленький монитор conmon:
podman → conmon → runc/crun → PID 1 контейнера
В дереве процессов видно сам контейнер, а не один общий демон.
Rootless режим:
С rootless режимом тоже разница. Docker может работать без root через rootlesskit и slirp4netns, но этот режим появился позже и имеет ограничения (например, не все сетевые возможности доступны). Podman изначально задумывался как rootless-friendly:
podman run --userns=keep-id -d alpine sleep 1000
Сеть:
Сеть у Docker в rootful-режиме - это мост docker0 и правила iptables:
ip link show docker0
В rootless - slirp4netns. У Podman начиная с версии 4.x сеть делает связка netavark + aardvark-dns, в rootless тоже используется slirp4netns или pasta.
Оркестрация:
Для Docker есть официальный docker compose. У Podman - podman-compose (комьюнити) и поддержка pods:
podman pod create --name mypod
podman run --pod mypod -d nginx
podman run --pod mypod -d redis
Ещё Podman умеет работать с Kubernetes-манифестами: podman play kube и podman generate kube.
Вывод:
Оба запускают одни и те же OCI-образы, но Docker работает через демон и привычен для большинства DevOps-пайплайнов, а Podman - без демона, с изначальной поддержкой rootless и pods, ближе к модели Kubernetes.
LinuxCamp | #docker #devops
👍27❤9🔥7❤🔥2✍2
Что такое Docker context
Docker context определяет, куда отправляются команды docker и docker compose. Контекст может быть локальным или удалённым. Удалённый контекст использует подключение по SSH.
Создание и переключение контекстов
Создать удалённый контекст:
Посмотреть доступные контексты:
Переключиться на нужный:
Где выполняются команды
После переключения любая команда:
выполняется на удалённом сервере, но файлы из текущей локальной директории не копируются на сервер.
Docker Engine на сервере получает только API-команды, но не получает ваши файлы.
Ограничения при работе с compose
Если в compose-файле есть:
удалённый Docker Engine не сможет найти эти пути, потому что это ваш локальный каталог, который не существует на сервере. Чтобы запуск через context работал, нужно использовать образы:
или заранее скопировать проект на сервер и запускать compose через SSH:
Важное про docker compose up --build
Команда, выполненная локально:
не передаёт ваш проект на сервер и не пересобирает его там. Для удалённого Docker Engine ваш каталог недоступен.
Вывод
Docker context только указывает, какой Docker Engine обслуживает ваши команды. Он не синхронизирует директории, не копирует файлы, не выбирает проект по имени папки на вашей машине.
LinuxCamp | #docker #devops
Docker context определяет, куда отправляются команды docker и docker compose. Контекст может быть локальным или удалённым. Удалённый контекст использует подключение по SSH.
Создание и переключение контекстов
Создать удалённый контекст:
docker context create prod --docker "host=ssh://user@server"
Посмотреть доступные контексты:
docker context ls
Переключиться на нужный:
docker context use prod
Где выполняются команды
После переключения любая команда:
docker ps
docker compose up -d
docker compose down
выполняется на удалённом сервере, но файлы из текущей локальной директории не копируются на сервер.
Docker Engine на сервере получает только API-команды, но не получает ваши файлы.
Ограничения при работе с compose
Если в compose-файле есть:
build: .
volumes:
- .:/app
удалённый Docker Engine не сможет найти эти пути, потому что это ваш локальный каталог, который не существует на сервере. Чтобы запуск через context работал, нужно использовать образы:
image: myapp:latest
или заранее скопировать проект на сервер и запускать compose через SSH:
ssh user@server
cd /project
docker compose up -d
Важное про docker compose up --build
Команда, выполненная локально:
docker compose up --build
не передаёт ваш проект на сервер и не пересобирает его там. Для удалённого Docker Engine ваш каталог недоступен.
Вывод
Docker context только указывает, какой Docker Engine обслуживает ваши команды. Он не синхронизирует директории, не копирует файлы, не выбирает проект по имени папки на вашей машине.
LinuxCamp | #docker #devops
👍17❤11🔥3❤🔥1