Привет, друзья!


Продолжаю делиться с вами заметками о Docker.


Заметки состоят из 3 частей: первые две теоретические, третья практическая.


Если быть более конкретным:


  • первая часть посвящена самому Docker, Docker CLI и Dockerfile;
  • вторая часть полностью о Docker Compose;
  • в третьей части мы разработаем и "контейнеризуем" приложение, состоящее из клиента, сервера и базы данных, развернем его (задеплоим) и настроим CI/CD.

Это часть номер два.


Вот часть номер раз.


Пришел к выводу, что в первой части был излишне многословен, в этой части буду более лаконичным.


Хочу немного дополнить первую часть, а именно: показать парочку примеров Dockerfile для Node.js-приложений.


Пример с официального сайта Node.js


FROM node:16

# создание директории приложения
WORKDIR /usr/src/app

# установка зависимостей
# символ астериск ("*") используется для того чтобы по возможности
# скопировать оба файла: `package.json` и `package-lock.json`
COPY package*.json ./

RUN npm install
# для создания сборки для продакшн
# RUN npm ci --only=production

# копируем исходный код
COPY . .

EXPOSE 4000
CMD [ "node", "server.js" ]

Инструкции COPY package*.json ./ и COPY . . выполняются по отдельности в целях извлечения максимальной выгоды из кеширования слоев. Зависимости проекта меняются на так часто, как файлы, не имеет смысла устанавливать их при каждой сборке образа.


Пример из статьи "10 лучших практик по контейнеризации Node.js-приложений с помощью Docker"


Пример является сокращенным и для продакшна.


FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a
RUN apk add dumb-init
ENV NODE_ENV production
WORKDIR /usr/src/app
COPY --chown=node:node . .
RUN npm ci --only=production
USER node
CMD ['dumb-init', 'node', 'server.js']


Docker Compose


Compose — это инструмент для определения и запуска Docker-приложений, состоящих из нескольких контейнеров. Для настройки сервисов приложения используется файл docker-compose.yml.


Процесс использования Compose, как правило, состоит из 3 этапов:


  • определение среды приложения с помощью Dockerfile;
  • определение сервисов, из которых состоит приложение, в docker-compose.yml (для совместного запуска сервисов в изолированной среде);
  • выполнение команды docker compose up для запуска приложения.

Пример файла docker-compose.yml:


version: "3.9"  # опционально
services:
 web:
   build: .
   ports:
     - "5000:5000"
   volumes:
     - .:/code
     - logvolume:/var/log
   links:
     - redis
 redis:
   image: redis
volumes:
 logvolume: {}

Compose позволяет делать следующее:


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

Compose предоставляет следующие возможности:


  • создание нескольких изолированных сред на одном хосте;
  • сохранение данных томов при создании контейнеров;
  • повторное создание только модифицированных контейнеров;
  • передача переменных среды окружения и возможность создания разных сред (для разработки, продакшна и т.д.).

Начало работы с Docker Compose.


docker compose


Команда docker compose является альтернативой docker-compose CLI и используется для управления Compose.


# сигнатура
docker-compose [-f <arg>...] [--profile <name>...] [options] [COMMAND] [ARGS...]

# основные флаги
-f - путь к docker-compose.yml
-p - название проекта
--project-path - альтернативная рабочая директория (по умолчанию рабочей является директория, содержащая docker-compose.yml)

# основные команды
up - создание и запуск сервисов
down - остановка и удаление контейнеров, сетей, образов и томов
start - запуск сервисов
stop - остановка сервисов
restart - перезапуск сервисов
create - создание сервисов
rm - удаление остановленных контейнеров
run - выполнение одноразовой команды
exec - выполнение команды в запущенном контейнере

Полный список флагов и команд.


docker-compose.yml


Файл Compose — это файл в формате YAML, определяющий сервисы, сети и тома. Дефолтным путем этого файла является ./docker-compose.yml.


Определение сервиса включает в себя установку настроек, которые применяются к каждому контейнеру, запущенному для этого сервиса. Это похоже на передачу аргументов при выполнении команды docker run. Определения сети и тома аналогично выполнению команд docker network create и docker volume create.


Настройки, определенные в Dockerfile, такие как CMD, EXPOSE, VOLUME и ENV не нуждаются в дублировании в docker-compose.yml.


Рассмотрим основные настройки сервисов.


build


Настройки, применяемые во время сборки.


build может определяться в виде строки — пути к контексту сборки:


version: "3.9"
services:
 webapp:
   build: ./dir

Или в виде объекта, где context — путь к контексту, dockerfile — используемый Dockerfile и args — аргументы:


version: "3.9"
services:
 webapp:
   build:
     context: ./dir
     dockerfile: Dockerfile-alternate
     args:
       buildno: 1

В случае с args аргументы должны быть определены в Dockerfile:


# syntax=docker/dockerfile:1

ARG buildno
ARG gitcommithash

RUN echo "Номер сборки: $buildno"
RUN echo "Основано на коммите: $gitcommithash"

build:
 context: .
 args:
   buildno: 1
   gitcommithash: cdc3b19
   # or
   - buildno=1
   - gitcommithash=cdc3b19

network


Сеть, к которой подключается контейнер во время сборки (для использования при выполнении команды RUN):


build:
 context: .
 network: host
# or
build:
 context: .
 network: custom_network_1

command


Перезапись дефолтной команды:


command: bundle exec thin -p 3000

depends_on


Зависимость между сервисами. Это означает следующее:


  • docker compose up запускает сервисы в определенном порядке. В примере ниже db и redis запускаются перед web;
  • docker compose up SERVICE автоматически включает зависимости SERVICE. В примере docker compose up web также создает и запускает db и redis;
  • docker compose stop останавливает сервисы в определенном порядке. В примере web останавливается перед db и redis.

version: "3.9"
services:
 web:
   build: .
   depends_on:
     - db
     - redis
   redis:
     image: redis
   db:
     image: postgres

restart_policy


Политика перезапуска — определяет, как и когда контейнер должен перезапускаться:


  • condition: условие перезапуска — none, on-failure или any (значение по умолчанию);
  • delay: время между попытками (по умолчанию равняется 5s);
  • max_attempts: количество попыток (по умолчанию — бесконечное);
  • window: время принятия решения об успехе перезапуска (по умолчанию — немедленно).

version: "3.9"
services:
 redis:
   image: redis:alpine
   deploy:
     restart_policy:
       condition: on-failure
       delay: 5s
       max_attempts: 3
       window: 120s

entrypoint


Перезапись дефолтной точки входа:


entrypoint: /code/entrypoint.sh

env_file


Извлечение переменных среды окружения из файла. Может быть единичным значением или списком.


Если файл Compose определен с помощью docker compose -f FILE, пути в env_file будут относительными директории, в которой находится этот файл.


Переменные, определенные в разделе environment, перезаписывают эти значения.


env_file: .env
# or
env_file:
 - ./common.env
 - ./apps/web.env
 - /opt/runtime_opts.env

expose


Выставление портов без их публикации на хосте — порты будут доступны только связанным (linked) сервисам. Могут определяться только внутренние порты:


expose:
 - "3000"
 - "8000"

external_links


Подключение к контейнеру, запущенному за пределами docker-compose.yml или даже за пределами Compose. Особенно полезно для контейнеров, предоставляющих общие или распределенные сервисы:


external_links:
 - redis_1
 - project_db_1:mysql
 - project_db_1:postgresql

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


image


Образ для контейнера. Может быть репозиторием/тегом или частичным идентификатором (partial identifier):


image: redis
image: node:16
image: example-registry.com:4000/postgresql

links


Подключение контейнера к другому сервису. Подключаемый сервис определяется с помощью названия сервиса и синонима ссылки (link alias) ("SERVICE:ALIAS") или только названия:


web:
 links:
   - "db"
   - "db:database"
   - "redis"

network_mode


Сетевой режим:


network_mode: "bridge"
network_mode: "host"
network_mode: "none"

networks


Сети для подключения:


services:
 some-service:
   networks:
    - some-network
    - other-network

ports


Выставление портов.


Короткий синтаксис позволяет делать следующее:


  • определять оба порта (HOST:CONTAINER);
  • определять только порт контейнера (для хоста выбирается эфемерный порт);
  • определять IP-адрес хоста для привязки (bind) и оба порта (значением по умолчанию является 0.0.0.0, что означает все интерфейсы) (IPADDR:HOSTPORT:CONTAINERPORT).

ports:
 - "3000"
 - "8000:8000"
 - "9090-9091:8080-8081"
 - "127.0.0.1:8001:8001"
 - "127.0.0.1::5000"
 - "6060:6060/udp"

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


  • target: порт контейнера;
  • published: порт хоста (доступный публично);
  • protocol: протокол порта (tcp или udp);
  • mode: host | ingress.

restart


Определение политики перезапуска. Значением по умолчанию является no, что означает отключение автоматического перезапуска. always означает перезапуск в любом случае. on-failure означает перезапуск только в случае аварийной остановки контейнера. unless-stopped означает перезапуск контейнера во всех случаев, кроме преднамеренной остановки:


restart: "no"
restart: always
restart: on-failure
restart: unless-stopped

volumes


Монтирование путей хоста (host paths) или именованных томов (named volumes), определенных в виде дополнительных настроек сервиса.


Пути хоста могут монтироваться как часть определения сервиса. Их не нужно указывать в ключе volume на верхнем уровне.


Однако, если необходимо, чтобы тома использовались несколькими сервисами, они должны быть перечислены в таком volume.


В следующем примере именованный том mydata используется сервисом web, для отдельного сервиса определяется bind mount (первый путь в volumes сервиса db). db также использует именованный том dbdata (второй путь), но определяет его с помощью устаревшего строкового формата для монтирования именованных томов. Именованные тома указываются в ключе volume верхнего уровня:


version: "3.9"
services:
 web:
   image: nginx:alpine
   volumes:
     - type: volume
       source: mydata
       target: /data
       volume:
         nocopy: true
     - type: bind
       source: ./static
       target: /opt/app/static

 db:
   image: postgres:latest
   volumes:
     - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
     - "dbdata:/var/lib/postgresql/data"

volumes:
 mydata:
 dbdata:

Короткий синтаксис


В этом случае используется формат [SOURCE:]TARGET[:MODE], где SOURCE — это путь хоста или именованный том, TARGET — это путь монтирования тома в контейнере и MODEro для доступа только для чтения и rw для доступа для чтения и записи (дефолтное значение).


Для монтирования могут использоваться относительные пути (путь вычисляется, начиная с директории с файлом Compose). Относительные пути должны начинаться с . или ...


volumes:
 # определяем только путь и делегируем создание тома движку
 - /var/lib/mysql

 # определяем связывание (mapping) абсолютных путей
 - /opt/data:/var/lib/mysql

 # путь хоста относительно директории с файлом `Compose`
 - ./cache:/tmp/cache

 # путь относительно пользователя
 - ~/configs:/etc/configs/:ro

 # именованный том
 - datavolume:/var/lib/mysql

Длинный синтаксис


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


  • type: тип монтирования — volume, bind, tmpfs или npipe;
  • source: источник монтирования, путь хоста для bind mount или название тома, определенное в верхнеуровневом volumes;
  • target: путь монтирования тома в контейнере;
  • read_only: индикатор доступности тома только для чтения;
  • bind: дополнительные настройки связывания:
    • propagation: режим распространения, используемый для связывания;
  • volume: дополнительные настройки тома:
    • nocopy: индикатор запрета копирования данных тома.

version: "3.9"
services:
 web:
   image: nginx:alpine
   ports:
     - "80:80"
   volumes:
     - type: volume
       source: mydata
       target: /data
       volume:
         nocopy: true
     - type: bind
       source: ./static
       target: /opt/app/static

networks:
 webnet:

volumes:
 mydata:

Другие настройки, соответствующие настройкам команды docker run


user: postgres
working_dir: /code

domainname: foo.com
hostname: foo
ipc: host
mac_address: 02:42:ac:11:65:43

privileged: true

read_only: true
shm_size: 64M
stdin_open: true
tty: true

Примеры определения продолжительности


2.5s
10s
1m30s
2h32m
5h34m56s

Примеры определения байтовых значений


2b
1024kb
2048k
300m
1gb

Замена переменных


Настройки могут содержать переменные среды окружения. Compose использует значения переменных из терминала при выполнении команды docker compose. Например, предположим, что терминал содержит POSTGRES_VERSION=9.3 и применяется такая настройка:


db:
 image: "postgres:${POSTGRES_VERSION}"

При выполнении docker compose значение переменной POSTGRES_VERSION в настройках заменяется на 9.3 и мы получаем postgres:9.3.


Если значение переменной не установлено, переменная в настройках заменяется пустой строкой и мы получаем postgres:.


Дефолтные значения переменных могут быть установлены в файле .env, который должен находиться в той же директории, что и файл Compose. Значения переменных в терминале перезаписывают значения, определенные в .env.


Поддерживается 2 варианта синтаксиса: $VAR и ${VAR}. Второй вариант предоставляет такие дополнительные возможности, как:


  • определение значений по умолчанию:
    • ${VAR:-default}: оценивается как default, когда VAR не установлена или является пустой;
    • ${VAR-default}: оценивается как default только когда VAR не установлена;
  • определение обязательных значений:
    • ${VAR:?error}: ошибка возникает, если VAR не установлена или является пустой;
    • ${VAR?error}: ошибка возникает, только если VAR не установлена.

Расширенные возможности интерполяции переменных типа ${VAR/foo/bar} в настоящее время не поддерживаются.


Спецификация файла Compose версии 3.


Это конец второй части.


Благодарю за внимание и happy coding!




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