docker build . - создание образа из текущей директории
docker build -t hello_world . - создание образа из текущей директории(.) с (-t)именем. В текущей директории должен быть Dockerfile
Любая команда в докерфайле создает новый слой имеджа. Поэтому иногда объединяют команды, чтобы уменьшить количество слоев.
Имеет смысл сначала создавать слои, которые редко изменяются при пересборке. Затем создавать слои, которые могут поменяться при пересборке.
Например не стоит копировать все файлы проекта, а потом выполнять установку зависимостей. Т.к. установка зависимостей может быть очень долгой и затратной. И в случае изменения кода будет производиться каждый раз. Лучше сначала скопировать файл с зависимостями и установить их. Только после этого копировать более часто изменяющиеся файлы, например код. Таким образом при изменении только кода и пересборке образа, докер будет начинать не с установки зависимостей, а с того слоя, где произошли изменения - именно с того места, где копировался код (т.е. после установки зависимостей).
| Команда | Описание |
|---|---|
FROM |
задаёт базовый образ |
ARG |
задаёт переменные, используемый при сборке образа |
LABEL |
описывает метаданные, например, автора образа |
ENV |
задаёт переменные среды в образе |
RUN |
выполняет команду, например установку пакетов внутри образа |
COPY |
копирует в контейнер файлы и папки |
ADD |
копирует в контейнер файлы или папки |
WORKDIR |
задаёт рабочую директорию |
EXPOSE |
описывает с какими портами работает контейнер |
VOLUME |
монтирует директории для хранения данных контейнера на хостовой ОС |
ENTRYPOINT |
выполняет команду с аргументами после запуска контейнера. Команда и аргументы не перезаписываются в командной строке запуска контейнера. |
CMD |
выполняет команду с аргументами после запуска контейнера. Команда и аргументы перезаписываются командой в командной строке запуска контейнера. |
FROM python:3.10
RUN mkdir -p /usr/src/app/
WORKDIR /usr/src/app/
COPY . /usr/src/app/
CMD ["python", "app.py"]FROM - базовый образ из которого начинается сборка
RUN - выполнение команды на стороне докера
WORKDIR - установить директорию как рабочий каталог и начать выполнение всех команд оттуда
COPY . /usr/src/app/ - копирование с локальной машины в контейнер
Если в докерфайле содержится несколько записей ENTRYPOINT и CMD:
ENTRYPOINT [ "php-fpm", "-c"]
CMD ["/var/www"]то непосредственно исполняемой частью является первый элемент из ENTRYPOINT``, все остальные элементы будут являться параметрами команды. CMD``может перезаписаться через командную строку запуска контейнера.docker run -d <image_name> /var/www2- в этом случае останется`ENTRYPOINT`со значением`php-fpm -c`и к нему добавиться параметр`/var/www2`, т.о. при запуске контейнера будет выполняться команда `php-fpm -c /var/www2`.
docker run --name hello <imagename> - запуск контейнера c именем(--name) hello на основе образа imagename
Контейнер работает до тех пор пока работает команда, которая выполняется при запуске
docker rm <container_id | container_name -удаление контейнера по id или его имени
docker rm $(docker ps -a -q) - вывести id и передать их команде удаления контейнеров
docker build -t hello-world . - создать докер образ из текущего каталога
docker run --name hello -d hello-world - запуск контейнера с именем(--name) hello из имеджа (hello-world) в фоне (-d)
docker stop <имя или id> - остановить контейнер c заданным именем или id
docker run --rm hello-world - запуск контейнера из имеджа, который удалится как отработает или будет остановлен
FROM python:3.10-alpine
RUN mkdir -p /usr/src/app/
WORKDIR /usr/src/app/
# сначала копируем то, что реже меняется
# т.к. докер создает слои и хэширует их после каждой команды
COPY requirements.txt ./
RUN pip install -r --no-cache-dir requirements.txt
COPY payload.json ./
COPY app.py ./
# декларация о том, что открываем порт
EXPOSE 8080
CMD ["python", "app.py"]docker run --rm -p 8080: 1234
Указание переменной окружения в докерфайле
ENV TZ Europe/MoscowЧерез параметр при запуске контейнера
docker run --rm -d --name web -e TZ=Europe/Moscow web-helloЗапуск с контейнером с заданно1 переменной окружения.
docker-compose up -d -f ./docker-compose.yml - стартовать все сервисы в контейнерах для указанного докер-компоуз файла
docker-compose ps - запущенные контейнеры в рамках компоуза
docker-compose logs service_name - логи сервиса
docker-compose exec service_name bash - выполнить bash внутри сервиса
docker-compose restart service_name - перезапустить контейнер для сервиса
docker-compose stop service_name - остановить контейнер для сервиса
docker-compose start service_name - запустить контейнер для сервиса
docker-compose up --force-recreate --no-deps -d service_name - пересоздать контейнер для сервиса service_name, не трогая все зависимые контейнеры.
1 - монтирование папки 2 - монтирование образа
docker run -v /host/path:/docker/pathКонтейнеризация - это изоляция на уровне процессов. Фактически все процессы контейнеров работают на хостовой машине. Виртуализация:
- максимальная гибкость
- эмуляция различных устройств
- нет ограничений по ос и ядрам
- максимальная изоляция (большая степень изоляции только в отдельном компьютере)
- высокие накладные расходы
- усложнение поиска проблем (иногда одна и та же ос работала по разному. чаще всего была проблема в гипервизоре, но реже от соседних систем)
Контейнеризация
- легкие виртуальный процессы
- изоляция на уровне встроенных механизмов в unix-live системах. cgroups, namespaces, и др Накладные расходы в докере на разных системах. На маке и винде - докер десктом запускает виртуалку (wsl linux). А в линуксе на докере почти нулевые накладные расходы.
- используется только одно ядро ос
- возможны ограничения ресурсов по цпу, рам, сети и т.д. контейнера могут есть все ресурсы хоста
- реализации
- докер
- LXC
- OpenVZ (Virtuozzo)
Преимущеста
- быстрый запуск
- низкие накладные расходы
- возможность изоляции (ниже чем в виртуалках, на уровне процессов)
- контроль окружения (знаем что внутри докер контейнера)
- повторяемость окружения (в контейнере есть все необходимое для старта приложения)
- решаемость проблемы совместимости
- автоматизация (автотесты в докере)
- масштабирование (можно масштабировать горизонтально, запустив много докеров, если написан правильный код, который распараллеливает) кубер - система оркестрации и масштабирования, балансирования
Раньше были монолитные приложения. Сейчас микросервисная архитектура. Система разита на изолированные части. Они могут разрабатываться разными командами на разных языках. Чтобы спокойно деплоить микросервисы растер количество манипуляция для деплоя проекта в продакшен среде.
- докер демон (сервер) docker-d
- клиент (команда докер). команды по работе с образами и контейнерами.
- клиент и сервер взаимодействую по rest api (можно посмотреть)
Чаще всего все микросервисы располагаются в одном кластере и задержки по цепочкам вызовов не оч большие. По типу взаимодействия - http вызовы и очереди. Например в асинхронном варианте, А запрашивает документ у Б. Б отвечает ид документа и начинает работать. Дальше А и Б взаимодействуют через очередь (Kafka, Rabbit) - брокеры сообщений. Либо они могут общаться напрямую через очередь
- образы images - дистрибутивы для контейнеров
- контейнеры containers - рабочие образы
- сети networks - передача данных
- тома volumes - хранение данных
- готовы к использованию
- есть открытый реестр register (есть и закрытые)
- можно собирать новые на основе существующих
- состоит из слоёв
- что такое слои в докере и как они работают
- каждая команда в докерфайле создает определенный слой - несколько команд, например при установке пакетов лучше минимизировать количество строк Чтобы экономить слои - схлопывают команды ран в одну длинную команду. Зачем экономить слои? Сам докер кэширует слои - создает на файловой системе хостовой машины определенные папки с названиями через хэш функцию Если что-то поменялось в слое, то все последующие слои обязаны будут пересобраться в 99.9% случаев
bridge- режим по умолчанию, изоляция NAT- есть дефолтная сеть в рамках которой запускаются все контейнеры
- можно создать свои сети
docker run ubuntuУ каждого контейнера появляется свой ip адрес.
hostотсутствует изоляция, полный доступ к хостуdocker run --network=host ubuntuИспользуется редко, например когда контейнер наружу выставляет много портов. Или когда нужно повысить производительность (нет сетевой абстракции (трансляции сетевых адресов) вряд ли и большой выигрыш) У контейнера нет своего ip адреса в рамках докера.overlay- связь между контейнерами на разных хостах в единую сеть чтобы они имели сетевую связность два компьютера могут быть соединены между собой проводом - это называется сетевой доступностью (можно отправить пинг и есть сеть). Две остовые системы могут быть связаны и чтобы два контейнера на этих системах также могли между собой общаться придется лио плясать с бриджем, либо выбрать host и там и там и контейнеры будут в сетевой доступности. Но host убирает изоляцию, что неправильно. Поэтому придумалиoverlayipvalan- сеть на уровне IP (L2, 802.1q trunk L2, L3)macvlan- создание отдельных интерфейсов с MAC-адресами (bridge, 802.1q trunk bridge) при старте контейнера он на хостовой системе создаёт виртуальное сетевое устройствоnone- отсутствие доступа к сети из контейнераdocker run --network=none ubuntu
-
bind mount- подключение к ФС хоста Опасно. Нарушение изоляции. Любая программа может придти в ФС и попортить что-то. -
volume- том в докер Выделяется некоторое пространство в файловой системе хоста докером. Именуется и туда имеем возможность записывать постоянные данные контейнер. Например при разработке приложения, можно подмонтировать папку src и процесс в докере может отслеживать изменения в этой папке и перезапускать сборку проекта при обнаружении изменений. -
tmpfs mount- монтирование в RAM -- быстро
- данные могут быстро пропасть
- логи хранятся вне контейнера
- локальное хранения (journald, syslog) - или централизованное (gelf, fluentd)
docker logs nginxdocker logs nginx --follow- автообновление логов с просмотром последних событий
docker logs --tail 100 nginx- последние 100 строк в логах
docker logs --until 10m nginx- не более 10 мегабайт логов
docker logs --since 2023-10-20T10:00:00 nginx- логи с определенного момента
docker infoинформация о докереdocker run hello-world- запуск контейнера из образа
docker run --rm -d --network host --name my_nginx nginx- запуск контейнера в фоне(-d) с именем (-name) в режиме автоудаления после выполнения(--rm) также в режиме сети хоста, когда все порты контейнера являются портами хоста
docker run -d --name nginx -p 80:80 -v /var/www/html/:/usr/share/nginx/html nginx- запуск nginx контейнера в режиме сети bridge(по-умолчанию) c с прикреплением к хостовой машине 80 порта
и подключением папки хоста (-v)
/var/ww/html/как папки в докере/usr/share/nginx/html
- запуск nginx контейнера в режиме сети bridge(по-умолчанию) c с прикреплением к хостовой машине 80 порта
и подключением папки хоста (-v)
docker search nginx- поиск всех именджов нгинкс и доступных регистрах
docker ps -a
docker exec -ti nginx bash- выполнить (exec) в интерактивном режиме(-i) команду (bash) - откроется терминал. (провалиться внутрь терминала контейнера). Обычно залезание внутрь контейнера является плохой практикой. Т.к. после пересоздания контейнера все изменения затираются за исключением примонтированных volumes или прикрепленных папок.
docker start|stop|restart nginxзапуск, остановка, перезапуск определенного контейнера команды похожие на systemctldocker inspect nginx- информация по контейнеру - все конфиги
docker top nginx- показать процессы этого контейнера - аналог команды
top
- показать процессы этого контейнера - аналог команды
docker stats nginx|dockers stats- показать статистику в реальном времени - по контейнеру или по всем контейнерам
статистика показывается применительно к контейнеру - например потребление cpu считается в процентах от cpu ядра контейнера, а не cpu ядра хоста -
LIMIT- сколько контейнер может съест хостовой оперативки -
- показать статистику в реальном времени - по контейнеру или по всем контейнерам
статистика показывается применительно к контейнеру - например потребление cpu считается в процентах от cpu ядра контейнера, а не cpu ядра хоста -
docker rm nginxудаление остановленного контейнера nginxdocker rm -f nginx- force remove - удаление даже работающего контейнера.
docker images- список образов на хостеdocker pull nginx:1.20- загрузить на хост из регистра образ nginx версии1.20docker rmi nginx- удалить образnginxdocker build -t webserver .- собрать из докерфайла в текущей директории(.) имедж и назвать его (-t)webserver
При работе с контейнерами флаг -v создается новый волюм со случайным именем.
Если задать имя после флага -v, то подключится уже существующий волюм.
docker volume ls- список томов на хостеdocker volume crate storage1- создание локального томаdocker volume rm storage1- удаление тома
По-умолчанию при создании контейнера для него создается сеть bridge с именем как у контейнера.
docker network ls- список сетей на хостеdocker network create --driver bridge alpine-net- создание новой сети типаbridgedocker network create my-net- создание новой сети (типа bridge - по-умолчанию)docker network rm my-net- удаление существующей сетиdocker run -p 127.0.0.1:8080:80 nginx- запуск контейнера в сети типа bridge и прокинуть порт на локалхост портхоста:портдокераdocker run -p 8080:80 nginx- запуск контейнера с пробрасыванием порта с 0.0.0.0:8080 на 80 порт контейнераdocker network inspect bridge- инспекция настроек сетиdocker run --network <network_name> <image>- запуск контейнера в определённой сети.
При использовании контейнеров с дефолтной сетью нельзя к ним обращаться по имени, а можно только по ip-адресу. Адрес можно узнать с помощью команды docker inspect <container>.
Если использовать пользовательскую сеть, то к контейнерам внутри сети можно будет обращаться по их имени.
docker network create <network_name> - создание пользовательской сети
Задача: развернуть приложение на Go
-
Проинспектировать приложение
-
Держим код в порядке:
- отдельная папка для декерфайла
Зачем нужна отельная папка для докерфайла?
- для разных окружений (тест, дев), может у одного приложения, развёрнутого на разных клиентах могут быть разные докерфайлы
- название Dockerfile
mkdir docker cd docker vim Dockerfile - отдельная папка для декерфайла
Зачем нужна отельная папка для докерфайла?
-
Ищем базовый образ
- Docker Hub
- обращаем внимание на бейджи (офиц. версия)
- смотрим на версии
docker pull golang:1.21- качаем докерфайл
-
Dockerfile
FROM golang:1.21 # копирование из текущей папки (из места сборки) # в терминале в папку по-дефолту внутри контейнера COPY . .
-
Создание образа и его проверка Если докерфайл лежит не там, откуда будет происходить сборка, то необходимо указать путь к докерфайлу и место сборки (последний аргумент). Место сборки будет тем местом, из которого мы копируем файлы.
-t- флаг тега-f- флаг файла..- указатель на директорию, в которой будет происходить сборка (у нас докерфайл лежит)
docker build -t test -f ./docker/Dockerfile .
Запуск контйенера из образа
testи выполнение в немbashв интерактивном режиме(-t)docker run -it test bashБилдим приложение на go внутри контейнера
go build main.go
Запускаем приложение внутри контейнера
./main
-
Дописываем докерфайл полностью https://docs.docker.com/engine/reference/builder/#from
FROM golang:1.21 COPY . . RUN go build ./main.go CMD ["./main"]
CMD- говорит нам что именно будет являться нашим процессом с id=1.docker top- показывает процессы, которые будут запущены внутри контейнера. У каждого процесса внутри контейнера будет свой ид. Если процесс с ид=1 умрёт, свалится, остановится, то остановится и контейнер. Это позволяет определить жизненный цикл контейнера.
-
Пересобираем образ из обновленного докерфайла
docker build -t test -f ./docker/Dockerfile .
Т.к. мы использовали такое же название, то предыдущий имедж с таким названием потерял свой тэг и стал
<none> -
Запускаем контейнер и проверяем
docker run --rm test--rm- после остановки контейнера - удалить его и почистить все его артефакты. Best practice для недолгоиграющих контейнеров. По-умолчанию контейнер запускается в режиме сетиbridgeи хост не знает о том что работает докер контейнер с открытым портом.
docker run --rm -p 8888:80 testВот так привязываем локальный порт 80 к порту докера 8888. Некоторые флаги в докере работают без пробела.
-
Проблема больших образов. Есть супер маленькие образы alpine.
FROM golang:1.21 as build COPY . . RUN CGO_ENABLED=0 go build ./main.go FROM alpine: COPY ./static ./static COPY --from=build /app/main ./ CMD ["./main"]
as build- именование, говорит о том, что этот этап - шаг под именемbuildCGO_ENABLEd=0- установка переменной окружения (специфична для го). Нужна для того, чтобы в образе альпайна хватило модулей для запуска го.Т.о. на первом этапе мф копируем исходники и компилируем. Шаг 2 -
FROM alpineCOPY ./static ./static- копируем каталог хостаCOPY --from=build /go/main .- копируем бинарный файлmainв базовую папку образа alpine/CMD ['./main']- устанавливаем 1й процесс контейнера как >/main -
Получившийся образ можно загрузить в локальный регистр образов.
Hadolint - линтер для докерфайлов.
https://hadolint.github.io/hadolint - онлайн версия. Но лучше запускать его локальную версию - она дает больше подсказок.
docker run --rm -i hadolint/hadolint < docker/DockerfileПравим файл по подсказкам
FROM golang:1.21 as build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build ./main.go
FROM alpine
WORKDIR /app
COPY ./static ./static
COPY --from=build /app/main ./
CMD ["./main"]WORKDIR - указывает рабочую директорию
Удаляем всё лишнее
- удаляем папку копирование докера на первой стадии
- используем
.dockerignore
Не даёт копировать некоторые файлы при использовании команды COPY.
*ocker*
.idea
node_modules/
.env
WORKDIR /app
FROM golang:1.21 as build
COPY ./main.go ./
RUN CGO_ENABLED=0 go build ./main.go
FROM alpine:
WORKDIR /app
COPY ./static ./static
COPY --from build /app/main ./
CMD ["./main"]Иногда можно встретить при сборке образа надпись CACHED - это значит что мы не выполнили реальную операцию из докерфайла, а взяли её результат из предыдущей операции (кэша).
В дистрибутиве альпина нет установленного bash, но есть sh:
docker run --rm -it test /bin/sh - запуск в интерактивном режиме оболочки sh.
docker run --rm -d -p 8888:8888 test - запустить контейнер в фоновом режиме
Докер не может удалить имеджи на основе которых есть контейнеры, т.к. этот контейнер можем запустить.
Можно использовать флаг -f или удалить контейнер.
docker exec -it nginx bash - запустить баш в работающем контейнере
stackoverflow
Докер не может удалить имеджи на основе которых есть контейнеры, т.к. этот контейнер можем запустить.
Можно использовать флаг -f или удалить контейнер.
Например в следующем примере создается на первом этапе сборка из react проекта, потом она копируется вместе с конфигом в образ с nginx:
FROM node:current-alpine3.19 as build
# задание метки созданному образу
LABEL stage=build
RUN mkdir /app/
WORKDIR /app/
COPY . .
RUN npm install && npm run build
FROM nginx:stable-alpine3.17
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN mkdir -p /www/site
COPY --from=build /app/dist/ /www/site/В итоге получается промежуточный образ, который мы можем затем удалить с помощью команды docker image prune:
docker image prune --filter label=stage=buildДокерфайл - для развертывание одного контейнера. Конечно можно в 1 контейнер запихнуть все. Но контейнеры должны быть легковесными, переносимыми. Идеально 1 процесс на контейнер. Чаще всего большое приложение содержит много процессов, которые должны между собой коммуницировать.
Docker-compose - это самое первое средство для оркестрации контейнеров.
Оркестрация - некое описания их взаимодействия, описание их разворачивания.
Docker - управляет одним контейнером, в идеале изолирует один процесс.
docker-compose - управляет набором контейнеров.
Dockerfile - описывает как построить образ. Dockerfile - среда приложения, docker-compose - сервисы приложения.
Отличие docker-compose от swarm и kubernetes. Говоря о docker-compose, мы достаточно часто имеем в виду dev среду и именно это про разработчиков. Чаще всего docker-compose не применяется в продакшене. На это есть некоторое кол-во причин. Docker Swarm и kubernetes более гибкие, более настраиваемые и дают некоторое количество преимуществ, например масштабирование с помощью docker-compose невозможно, а в kubernetes достаточно просто. Kubernetes = еше одно стороннее по, которое создает ещё один слой абстракции над контейнерами- оборачивает их в абстракцию - под. В зависимости от нагрузки может изменять количество подов.
При разработке это все не нужно. И docker-compose часто бывает достаточно. Docker-swarm - официальная утилита от докера. Припозднилась, приехала когда kubernetes уже был де факто стандартом.
https://docs.docker.com/compose/reference/
-
docker-compose up- builds, (re)create, starts and attaches to containers for a service Проходится по файлуdocker-compose.yamlсобирает пересоздает все контейнеры на основе определенных образов стартует их и связывает контейнер с сервисом. Появляется новая абстракция - сервис - составная часть приложения. Сервис это чуть более верхнеуровневая абстракция над контейнером. -
docker compose down- stop containers and remove containers, networks, volumes and images Останавливает и удаляем все контейнеры, сети и volumes, которые были созданы во времяdocker-compose up -
docker-compose start- start services -
docker-compose stop- stop services - очищение памяти, временных файлов, логов, любых артефактов работы контейнера. -
docker-compose pause- pause services - любые артефакты работы контейнера остаются на мсте после unpause -
docker-compose unpause- unpause services -
docker-compose logs -f [service name]- displays log output from service - логи конкретного сервиса -
docker-compose ps- list containers - список контейнеров, из которых могут быть собраны сервисы в рамках изолированного compose. -
docker-compose exec [service name] [command]- выполнить команду в запущенном контейнере - можем провалиться внутрь контейнера по его service_name. Сервис - абстракция над контейнером. -
docker-compose images
https://docs.docker.com/compose/compose-file/compose-file-v3/ Есть несколько версий - 2, 3...
В docker-compose файле мы описываем наше приложение. Приложение состоит из сервисов, может включать volumes и сети. Наше приложение состоит из следующих сервисов:
- web
- redis
# docker-compose.yml
version: "2"
services:
web:
build:
# build from Dockerfile
context: ./Path
dockerfile: Dockerfile
ports:
- "5000:5000"
volumes:
- .:/code
redis:
image: redishttps://docs.docker.com/compose/compose-file/05-services
Сделан на базе контейнера. Мы получаем этот контейнера на шаге build на базе образа из Dockerfile. В контексте указываем путь до докерфайла и название докерфайла Dockerfile.
Указываем порты портхоста:портконтейнера.
volumes - пробрасываем папку с хоста внутрь контейнера.
Более простой сервис. Строится на основе docker image redis.
Есть несколько вариантов билдится внутри сервиса
web:
# build from dockerfile
# из текущей папки
build: .
# пробрасывание аргументов в докерфайл
args: # add build args
APP_HOME: app
# build from custom Dockerfile
# например если есть папка с фронтом, бэком и т.д.
build:
context: ./dir
dockerfile: Dockerfile.dev
# build from image
image: ubuntu
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry:4000/postgresql
image: a4bc65fd # билд по ид - не рекомендуется!!!ports:
- "3000"
- "8000:80" # host:container
# expose ports to linked services (not to host)
expose: ["3000"]ports- проброс портов к хосту (хост:контейнер) если написана одна цифра, то это значит"3000:3000"expose- если не хотим пробрасывать порты наружу в хост, но хотим, чтобы наши контейнеры общались по сети, то используется командаexposeи далее передается список портов.
Можно прокидывать по одной или передать сразу все в одном файле
environment:
RACK_ENV: development
environment:
- RACK_ENV=developmentили передать сразу все через .env файл
env_file: .env
env_file: [.env, .development.env]Очень часто переменные окружения зашивают на момент билда самого контейнера. Что есть не очень хорошо. Т.к. любой человек с доступом к имеджу имеет доступ и к переменным, где может ранить чувствительная информация.
Чаще всего проект разворачивается во вне с помощью автоматизированного процесса, который называется pipeline, ci/cd. В пайплайнах также не должны быть прикопаны енв.
https://docs.docker.com/compose/compose-file/05-services/#volumes
Опция, которая перечисляет используемые докер-компоузом контейнеры. Если волюма не существует, до он буде создан.
volumes:
elasticsearch:
storage:
postgres:По-умолчанию docker-compose созадёт образ в своей системе, отличающийся от списка образов из docker. Можно использовать volume из docker, для этого образ объявляем, как внешний:
volumes:
db-data:
external:
name: actual-name-of-volumeКонтейнер после билда будет запускаться. Соответственно контейнер запускается с конкретной команды. Существует два способа передать команду. Второй способ с квадратными скобками является предпочтительнее.
Разница между CMD и ENTRYPOINT в докерфайле?
- при определенных обстоятельствах никакой
- при других обстоятельствах предпочтительнее ENTRYPOINT
- при других обстоятельствах (совсем редко) предпочтительнее CMD.
command: bundle exec thin -p 3000
command: [bundle, exec, thin, -p, 3000]
# ovverrride the entrypoint
entrypoint: /app/start.sh
entrypoint: [php, -d, vendor/bin/phpunit]
# makes the `db` service available as the hostname database
# (implies depends_on)
links:
- db:database
- redis
# make sure `db` is alive before starting
depends_on:
- db
# make sure `db` is healthy before starting
# and db-init completed without failure
depends_on:
db:
condition: service_healthy
db_init:
condition: service_completed_successfullyЛинки - можно сделать доступным к обращению из других контейнеров с помощью линка.
db - будет сервис, который теперь доступен к обращению из других сервисов как database.
Сервис redis становится доступным для других сервисов на хосте как redis.
depends_on - одна из самых важных вещей в docker-compose файле.
Секция внутри конкретного сервиса, которая рассказывает нам о том, от чего зависит запуск этого описываемого сервиса.
Наш сервис зависит от сервиса db, который должен быть здоров (отвечать, доступен и т.д.).
Также наш описываемый сервис зависит от другого сервиса db_init, который должен завершится успехом.
Тогда при старте приложения, он создает внутри себя dependency_injection контейнер, в котором определенным образом прописывает коннекшн к базе, и если этого коннекшена нет, он упадет с ошибкой. Поэтому надо быть уверенными, что на момент запуска нашего сервиса уже будет развернута база данных. Это позволяет сделать секция depends_on.
extends- один сервис может базироваться ну другом, расширяя его Своеобразное наследование, используется не очень часто, но иногда, когда нужно на нескольких средах сделать немного разные вещи, то используется.volumes- прокидывание хостовых папок и волюмов в докер.restart- перезапуск контейнера при определенных условияхunless_stoppedalways- всегда - при завершении или падении, при перезапуске компоуза.on_failureno- значение по умолчанию
# make one service extend another
extends:
file: common.yml # optionsl
service: webapp
volumes:
- /var/lib/mysql
- ./_data:/var/lib/mysql
# automatically restart container
restart: unless_stopped
# always, on-failure, no (default)Дергается когда прописан depends_on у сервиса со значением service_healthy.
Мы выполняем команду тест, которая может быть успешной. Интервал - раз в 90 сек.
timeout - сколько мы будем ждать выполнения этой команды.
retries - сколько попыток сделать перед тем как сказать, что healthcheck провален.
# declare service healthy when test command succeed
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40sУ postgresql своя система проверки при помощи crud запросов.
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $$POSTGRES_DB -U $$POSTGRES_USER"]
interval: 10s
timeout: 120s
retries: 10Обычно есть одна сетка и все контейнеры, которые поднимаются в раках docker-compose взаимодействуют через эту сеть. Но мы можем задать свою сеть. Например по независящим от разработчика причинам, например эмулировать реальную инфраструктуру.
service1:
network:
- my_network
service2:
network:
- my_networkИспользование сети типа host
build:
context: .
network: hostОтключение доступа к сети
build:
context: .
network: noneОпределенные изменения в базе данных. Иногда запускается сервис для миграций.Например может быть отдельный контейнер с некоторым количеством sql файлов, которые в определённом порядке запускается. При запуске сервиса миграции, этот набор скриптов определённым образом меняет базу. Это надо, например, для изменения базы данных. Механизм миграций нужен для возможности отката т.к. они позволяют восстановить состояние базы данных в любой конкретный момент времени.
Миграции могут быть вынесены в отдельный сервис.
https://github.com/reportportal/reportportal/blob/master/docker-compose.yml
- Облако
- docker-compose
- nginx
- back
- front
- docker-compose
На выходе
Докер компоуз файл в проекте в котором 3 папки: front, back, nginx, database.
Внутри каждой папки должный быть свои докерфайлы.
В докеркомпоузе должны быть описаны 4 сервиса.
При изменениях в исходном коде должны пересобираться внутри приложения. Внутри приложения должен быть watch, чтобы видеть изменения, которые были внесены.
Можно билдить приложение во фронте, собирать и закинуть в nginx.
При этом нельзя будет разрабатывать, т.к. образ будет готовым.
Но т.к. докер-компоуз нужен для разработки, то надо иметь возможность разрабатывать с ним.
Внутри докер компоуза CMD должен быть не стандартным, а npm run dev .... Когда запускается эта команда, она запускается в watch моде, следит за изменениями файлов.
Папка nginx помимо docker-файла должна иметь определённый конфиг, который прячет за собой фронт и бэк.
У бэкэнда должна быть сетевая связность с базой данных.
У фронтэнда не должно быть сетевой связности с базой. Все .env через докер компоуз. Секретный данные не должны быть в докер-компоуз файле.
Яндекс, ВК, АВС Digital Ocean - Azure - не оплатить из россии Amazon - оплачивается зарубежной картой
В основе любого облака заложены определенные принципы, вытекающие из определенных предпосылок.
Облако - метафора, олицетворяющая сложную инфраструктуру. Сетевую, спец. хранилища и т.п. В этом очень сложно разбираться. Раньше в начале 2000х было все намного проще. Сейчас настроить сервер бд и прикладного по и сервис elasticsearch - это 3 большие и три разных задачи в т.ч. и по требуемым навыкам. Компании столкнулись со сложностями. Одним из первых был амазон, - большой сайт на котором было много транзакций. Решение было найдено - было нанято много админов и они столкнулись с той же проблемой - поиск квалифицированных кадров. Поэтому упростили многое, чтобы можно было брать менее квалифицированных специалистов. Так и получили развитие AWS. AWS выглядят ужасно, т.к. это решение от инженеров для инженеров. Но это было настолько удачное решение, спрятавшее огромную сложность за кликами мыши за кнопками. Развернуть машину для базы данных стало очень просто.
Облачные платформы позволяют арендовать ИТ ресурсы:
- серверы
- базы данных
- ип адреса
- сетевую инфраструктуру
- и т.п. (нейросети)
Плюсы:
- Разделение ответственности - пользуемся не беспокоясь за издержки владения
- Масштабируемость - гибкость в реагировании на изменение нагрузки
- Экономия - платим за то, что используем Минусы:
- Сложно уйти с облака - бизнес привыкает с этим услугам и отказаться от них сложно. Попытаться поднять у себя - долго, дорого и не всегда возможно вытащить виртаулки к себе с клауда. Иногда просто нет доступных ресурсов внутри сети.
- Хороший интернет - постоянный коннект. Обычно слабо выражено в современном мире.
- Иногда надежнее иметь у себя полноценную серверную. Иногда правильнее и быстрее решить проблему зайдя у себя в ЦОД. Да это дороже. Банки стараются по минимуму использовать клауд инфраструктуру. Включается также и юридический аспект. В россии банки и крупные компании пилят свои облака, т.к. высококвалифицированных инженеров немного и нужны простые решения.
- Доступность 99.95% - всегда можно попасть в 0.05%, или на убитые диски или на что-то еще. В итоге будет большой инцидент.
IaaS - Infrastructure as a Service
- Идентификация и безопасность
- Виртуальные машины и контейнеры
- Объектное и блочное хранилище
- Сеть и доставка контента - LoadBalancer
PaaS - Platform as a Service Нам продают уже готовую преднастроенную виртуалку со специализированным ПО. Самый простой пример - S3 хранилище или СУБД.
- Управление данными и аналитика Сервис управления базами данных, классические СУБД
- Serverless / Cloud Native
- ML & AI
SaaS - Software as a Service Это приложение, комплексное, которое разворачивается на нескольких серверах. Например это GitLab (который можно купить у GitLab - гит сервер с CI/CD раннерами). Тоже можно сказать про Jira. Раньше её можно было поставить на свои железки, но понемногу они мигрировала в облако.
- Партнерские приложения и сервисы
На данный момент ЯК предоставляет 9 разделов
- Инфраструктура и сеть - инфраструктурные сервисы для обработки данных, безопасного доступа к ним и обработки данных. IaaS
- Платформа данных - управление базами данных и кластерами, масштабируемое хранение данных, сбор и визуализация метрик и данных. PaaS. Модно поднять целый кластер без детальных настроек.
- Контейнерная разработка - управление кластерами Kubernetes и Docker-образами. У многих рос облаков сейчас оч плохо с поддержкой докер контейнеров. Все больше концентрируются на кубере, а не на докер образах.
- Инструменты разработчика - сервисы для оптимизации разработки и тестирования приложения. AWS и Azure имеют свою систему тикетов, аналогичную Jira. У яндекса есть доски для тикетов.
- Безсерверные вычисления - serverless. Сервисы для хранения данных и разработки приложений без создания виртуальных машин. Мы можем написать кусок кода, который надо будет запускать по требованию, например при обращении их некого приложения. Можно вызвать одну функцию, которая вызовет определённые функции, обратиться к другим сервисам в облаке и после того завершит свою работу. Это будет не очень надёжно, зато супер дешево.
- Безопасность - управление ключами шифрования и TLS сертификатами, защита от DDoS атака. Легко упустить в большом приложении. Клауды чаше всего очень хорошо справляются с DDoS.
- Ресурсы и управление - идентификация и контроль доступа к облачным ресурсам, управление ресурсами в каталогах и облаках. Вокруг облачной инфраструктуры часто вырастает своя инфраструктура. в т.ч. связанная и с контролем ролей, доступов. Обычно это некая ролевая модель, лежащая в отдельном сервисе и позволяющая очень гибко настраивать доступ к личному кабинету кампании.
- Машинное обучение - речевые технологии, анализ изображений и машинный перевод.
- Бизнес-инструменты - визуализация и анализ данных, хранение базы знаний, трекеры задач для организации работы команды.
В какой-то момент Amazon запустил свое облачное хранилище данных. Сейчас это используется повсеместно. Если мы в Москве, а пользователь в Хабаровске, то расстояние большое и задержки высокие. Плюс при передачи данных убудет загружен большой участок сети. Надо чтобы физическое расстояние минимально влияло на передачу данных. Было бы здорово, чтобы файл сам реплицировался на много географически распределенных серверов и пользователь будет его скачать с ближайшего сервера. Это первым и сделал Amazon. Это решение потом стало открытым, появились его реализации, в т.ч. OpenStorage в Yandex.
Оба балансируют нагрузку по сети, но первый по транспортному уровню сети, второй по прикладному уровню сети.
- прикладной - Application Load Balancer
- представительский
- сеансовый
- транспортный - Network Load Balancer
- сетевой
- канальный
- физический
Postgresql Mysql Mongo Redis ElasticSearch MessageQueue - очереди для асинхронного взаимодействия.
У яндекса есть 3 цода (конец 2023 года)
- ru-central1-a
- ru-central1-b
- ru-central1-c Каждая зона - отдельный датацентр. ДЦ полностью независимы друг от друга.
Ресурсы в ЯК - это все сущности, которые вы можете создать (виртуальные машины, диски, виртуальные сети). Ресурсы объединены в группы - каталоги. Каталоги объединены в облако. Облако - самая крупная логическая структура в ЯО. Она изолирована - ресурсы из одного облака не могут взаимодействовать с ресурсами в другом облаке (только по сети интернет). Зачем и когда логически делить? Скорее всего у одной кампании есть одно облако. Если компания большая и у неё несколько направлений - туризм и машины - имеет смысл разделить на два облака
Каталог - если внутри туристического направления нашей кампании - есть подразделы бизнеса в одном домене - например, резервирование авиабилетов и продажа туров - подразделы бизнеса в одном домене. В одном каталоге - все связано с конкретным проектом. Все что связано с бронированием билетов.
Сервис Yandex Resource Manager определяет ресурсную модель Yandex Cloud и позволяет структурировать ресурсы с помощью каталогов. Роли бывают двух типов:
- Примитивные роли - содержат разрешения, действующие для всех типов ресурсов Yandex Cloud - роли admin, editor, viewer.
- Сервисные роли - содержат разрешения только для определенного типа ресурсов в указанном сервисе. Например в контейнерах, чтобы получить доступ к образу, который хранится в хранилище образов, нужна определённая роль.
Платёжный аккаунт используется для идентификации пользователя, оплачивающего услуги. Он хранит информацию о плательщике и может быть не связан с конкретным человеком. С точки зрения Resource Manager платежный аккаунт - это ресурс. К нему можно давать доступ обычным аккаунтам, используя роль billing.accounts.member.
Это образ виртуальной машины, оптимизированной для запуска Docker контейнеров. Образ включает в себя Ubuntu LTS, Docker и демона для запуска докер-контейнеров.
Образ интегрирован с платформой ЯК, что позволяет:
- запускать докер контейнеры сразу из консоли управления ЯК или YC-CLI
- обновлять запущенный докер контейнер с минимальным временем простоя. Вручную приложение немного "моргнёт". А здесь определенным образом оптимизировано, чтобы не потерять пакеты во время перезапуска контейнера.
- получить доступ к открытым сетевым портам докер контейнера без дополнительных настроек
Все контейнеры запускаются с использованием сетевого интерфейса хоста. Все порты открытые на докер контейнере будут открыты и на хосте.
Все порты хоста открыты в интернет, что позволяет автоматически получить доступ к портам запущенного докер-контейнера.
Это может создать некоторые дыры в безопасности. Не всегда нужно, чтобы все элементы инфраструктуры были доступны в сети. Т.к. образом это больше про разработку, чем под продакшен.
https://cloud.yandex.ru/docs/cos/tutorials/vm-create
https://www.youtube.com/live/uLp-zgset00
https://github.com/artemonsh/deploy-frontend-backend
code/
code/backend/
app.py
...
.dockerignore
Dockerfile
code/frontend/
...
Dockerfile
.dockerignore
code/nginx/
nginx.conf
...
docker-compose.ymlСначала копируем requirements.txt и устанавливаем зависимости, т.к. докер кэширует слои и дальнейшая пересборка будет происходить быстрее в случае, если изменённые файлы копируются последними.
# backend/Dockerfile
FROM python:3.11-alpine3.20
RUN addgroup --system python
RUN adduser -S -G python python
ENV HOME=/home/python
ENV APPHOME=${HOME}/app
RUN mkdir -p ${APPHOME}
WORKDIR ${APPHOME}
COPY ./requirements.freeze .
RUN python -m pip install --no-cache-dir -r requirements.freeze
COPY . .
RUN chown -R python:python ${HOME}
USER python
CMD [ "uvicorn", "app:app", "--reload" ]Тут мы собираем реакт проект в статику, затем получившиеся файлы копируем в образ с нгинксом и передаем туда конфиг для nginx.
FROM node:alpine3.20 as build
ENV HOME=/home/node/
ENV APPHOME=${HOME}/app
RUN mkdir -p ${APPHOME}
WORKDIR ${APPHOME}
COPY ./package.json ./
RUN npm install
COPY . .
RUN npx vite build
FROM nginx:stable-alpine
COPY --from=build /home/node/app/dist/ /usr/share/nginx/html/
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 3000
CMD [ "nginx", "-g", "daemon off;"]В конфиге для nginx, веб сервер слушает 3000 порт и все входящие запросы на корень сайта перенаправляет в папку /usr/share/nginx/html, куда скопирована получившаяся статика с react проекта.
#frontend/nginx.conf
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.html;
try_files $uri $uri/ /index.html =404;
}
include /etc/nginx/extra-conf/*.conf;
}Создаем докер компоуз из 3 сервисов
- бэк на фастапи fastapi:8000
- фронт на react работающий н nginx:3000
- прокси nginx, который принимает все запросы на
/и/api/и перенаправляет их соответственно на fastapi и react сервисы
version: "3.8"
networks:
web:
services:
nginx:
image: nginx:stable-alpine
ports:
- 80:80
depends_on:
- vite
- fastapi
volumes:
- ./my_nginx.conf:/etc/nginx/nginx.conf
networks:
- web
fastapi:
entrypoint: 'uvicorn app:app --host 0.0.0.0'
build: ./my_back/
networks:
- web
vite:
build:
context: ./my_front/
networks:
- web
depends_on:
- fastapiПодключаем к
# Конфигурация с поддоменом и SSL сертификатами в файле nginx_subdomain.conf
user root;
worker_processes 1;
events {
}
http {
server {
listen 80;
server_name 30.30.20.20; # <-- укажите ip адрес вашего сервера
location / {
proxy_pass http://frontend:3000/;
}
location /api/ {
proxy_pass http://backend:8000/;
}
}
}# django
FROM python:3.12.5-alpine3.19
ENV HOME=/home/django/
ENV APP_PATH=${HOME}/app/
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN mkdir -p ${APP_PATH}
WORKDIR ${APP_PATH}
COPY ./requirements.txt ./
RUN pip3 install --no-cache-dir -U pip && pip install --no-cache-dir -r requirements.txt
COPY . .
RUN python manage.py migrate --noinput
RUN addgroup --system django
RUN adduser -S -G django django
RUN chown -R django:django ${HOME}
RUN chmod 755 -R ${APP_PATH}/media/
USER django# Dockerfile.nginx
FROM python:3.12.5-alpine3.19 as build
ENV HOME=/home/django/
ENV APP_PATH=${HOME}/app/
RUN mkdir -p ${APP_PATH}
WORKDIR ${APP_PATH}
COPY ./requirements.txt ./
RUN pip3 install --no-cache-dir -r requirements.txt
COPY . .
RUN python manage.py collectstatic
# копируем статику с предыдущего шага
FROM nginx:stable-alpine3.20-slim
COPY --from=build /home/django/app/static/ /usr/share/nginx/html/static/
COPY nginx.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]# nginx.conf
upstream backend {
server django:8000;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /usr/share/nginx/html/static/;
}
location /media/ {
alias /usr/share/nginx/html/media/;
}
include /etc/nginx/extra-conf/*.conf;
}# docker-compose.yml
version: "3"
services:
django:
command: sh -c 'python manage.py makemigrations && python manage.py migrate --noinput && python manage.py collectstatic --noinput && gunicorn sofia_site.wsgi:application --bind=0.0.0.0:8000'
build:
context: ./sofia_site
dockerfile: Dockerfile.django
env_file: .env
environment:
- SECRET_KEY=${SECRET_KEY}
restart: unless-stopped
ports:
- "8080:8000"
volumes:
- django_media:/home/django/app/media/:rw
nginx:
build:
context: ./sofia_site
dockerfile: Dockerfile.nginx
depends_on:
- django
restart: unless-stopped
ports:
- 80:80
volumes:
- django_media:/usr/share/nginx/html/media/:ro
volumes:
django_media: