Первое знакомство с Docker Compose

Это руководство предназначено для ознакомления с ключевыми понятиями Docker Compose при создании простого веб-приложения Python. Приложение использует фреймворк Flask и поддерживает счётчик посещений в Redis.

Представленные здесь концепции должны быть понятны даже тем, кто не знаком с Python.

Пререквизиты

На вашем компьютере должны быть установлены Docker Engine и Docker Compose. Вы также можете:

Вам не нужно устанавливать Python или Redis, т. к. оба они предоставляются образами Docker.

Шаг 1. Определение зависимости приложения

  1. Создадим каталог для проекта:

$ mkdir composetest

$ cd composetest
  1. Создадим файл с именем app.py в каталоге вашего проекта и вставим в него следующий код:

import time

import redis from flask import Flask

app = Flask(\ **name**)

cache = redis.Redis(host='redis', port=6379)

def get\_hit\_count():
  retries = 5 while True: try: return
  cache.incr('hits') except redis.exceptions.ConnectionError as exc: if
  retries == 0: raise exc retries -= 1 time.sleep(0.5)

@app.route('/')
def hello():
  count = get\_hit\_count()
  return 'Hello World! I have been seen {} times.'.format(count)

В этом примере redis — это имя хоста контейнера Redis в сети приложения. Мы используем порт по умолчанию для Redis, 6379.

Примечание

Обработка временных ошибок

Обратите внимание на то, как написана функция get_hit_count. Данный базовый цикл повтора позволяет нам несколько раз выполнить запрос, если служба Redis недоступна. Это полезно при запуске, когда приложение подключается к сети, но также делает приложение более устойчивым, если службу Redis необходимо перезапустить в любое время в течение жизненного цикла приложения. В кластере это также помогает справляться с мгновенными разрывами соединения между узлами.

  1. Создадим ещё один файл с именем requirements.txt в каталоге вашего проекта и вставляет в него следующий код:

text
flask
redis

Шаг 2: Создадим Dockerfile

Dockerfile используется для создания образа Docker. Образ содержит все зависимости, необходимые приложению Python, включая сам Python.

В каталоге проекта создадим файл с именем Dockerfile и вставляет в него следующий код:

# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]

Это сообщает Docker:

  • Создадим образ, начиная с образа Python 3.7.

  • Установим рабочий каталог на /code.

  • Установим переменные среды, используемые командой flask.

  • Установим gcc и другие зависимости

  • Скопируем requirements.txt и устанвим зависимости Python.

  • Добавим к образу метаданные, чтобы описать, что контейнер прослушивает порт 5000.

  • Скопируем текущий каталог . в проекте в рабочий каталог . в образе.

  • Установим для контейнера команду по умолчанию flask run.

    Важно

    Убеждаемся, что Dockerfile не имеет расширения файла, такого как .txt. Некоторые редакторы могут добавлять это расширение файла автоматически, что приводит к ошибке при запуске приложения.

Дополнительные сведения о том, как писать файлы Dockerfile, см. в документах Руководство пользователя Docker и Справочник по Dockerfile.

Шаг3. Определим сервисы в файле Compose.

Создадим файл с именем docker-compose.yml в каталоге вашего проекта и вставим следующее:

version: "{{ site.compose_file_v3 }}"
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"

Данный файл Compose определяет две службы: web и redis.

Служба web использует образ, созданный из Dockerfile в текущем каталоге. Затем он привязывает контейнер и хост-компьютер к открытому порту 8000. В этом примере службы используется порт по умолчанию для веб-сервера Flask, 5000.

Служба redis использует общедоступный образ Redis, извлеченный из реестра Docker Hub.

Шаг 4. Создадим и запустим приложение с помощью Compose

  1. Из каталога проекта выполним приложение, запустив docker compose up.

$ docker compose up

Creating network "composetest\_default" with the default driver Creating
composetest\_web\_1 ... Creating composetest\_redis\_1 ... Creating
composetest\_web\_1 Creating composetest\_redis\_1 ... done Attaching to
composetest\_web\_1, composetest\_redis\_1 web\_1 \| \* Running on
http://0.0.0.0:5000/ (Press CTRL+C to quit) redis\_1 \| 1:C 17 Aug
22:11:10.480 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo redis\_1 \|
1:C 17 Aug 22:11:10.480 # Redis version=4.0.1, bits=64, commit=00000000,
modified=0, pid=1, just started redis\_1 \| 1:C 17 Aug 22:11:10.480 #
Warning: no config file specified, using the default config. In order to
specify a config file use redis-server /path/to/redis.conf web\_1 \| \*
Restarting with stat redis\_1 \| 1:M 17 Aug 22:11:10.483 \* Running
mode=standalone, port=6379. redis\_1 \| 1:M 17 Aug 22:11:10.483 #
WARNING: The TCP backlog setting of 511 cannot be enforced because
/proc/sys/net/core/somaxconn is set to the lower value of 128. web\_1 \|
\* Debugger is active! redis\_1 \| 1:M 17 Aug 22:11:10.483 # Server
initialized redis\_1 \| 1:M 17 Aug 22:11:10.483 # WARNING you have
Transparent Huge Pages (THP) support enabled in your kernel. This will
create latency and memory usage issues with Redis. To fix this issue run
the command 'echo never > /sys/kernel/mm/transparent\_hugepage/enabled'
as root, and add it to your /etc/rc.local in order to retain the setting
after a reboot. Redis must be restarted after THP is disabled. web\_1 \|
\* Debugger PIN: 330-787-903 redis\_1 \| 1:M 17 Aug 22:11:10.483 \*
Ready to accept connections

Compose извлекает образ Redis, создает образ для вашего кода и запускает определённые вами службы. В этом случае код статически копируется в образ во время сборки.

  1. Войдите http://localhost:8000/ в браузере, чтобы увидеть работающее приложение.

    Если это не помогло, вы также можете пытается зайти http://127.0.0.1:8000.

    В браузере должно появиться сообщение:

    Hello World! I have been seen 1 times.
    
    hello world in browser
  2. Обновление страницы.

    Число должно увеличиваться.

    Hello World! I have been seen 2 times.
    
    hello world in browser
  3. Переключитесь в другое окно терминала и наберите docker image ls, чтобы просмотреть локальные образы.

Список образов на этом этапе должен возвращать redis и web.

$ docker image ls


REPOSITORY TAG IMAGE ID CREATED SIZE composetest\_web latest
e2c21aa48cc1 4 minutes ago 93.8MB python 3.4-alpine 84e6077c7ab6 7 days
ago 82.5MB redis alpine 9d8fa9aa0e5b 3 weeks ago 27.5MB

Вы можете проверять образа с docker inspect <tag or id>.

  1. Остановите приложение, либо запустив docker compose down из каталога проекта во втором терминале, либо нажав CTRL+C в исходном терминале, где вы запустили приложение.

Шаг 5. Изменение файла Compose, чтобы добавить монтирование

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

version: "{{ site.compose_file_v3 }}"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_DEBUG: True
  redis:
    image: "redis:alpine"

Новый ключ volumes монтирует каталог проекта (текущий каталог) на хосте в /code внутри контейнера, позволяя изменять код на лету без необходимости перестраивать образ. Ключ environment задаёт переменную среды FLASK_DEBUG, которая указывает flask run запускать в режиме разработки и перезагружает код при изменении. Данный режим следует использовать только в разработке.

Шаг 6. Пересоборка и запуск приложения с помощью Compose

В каталоге проекта наберите docker compose up, чтобы создать приложение с обновленным файлом Compose, и запустите его.

$ docker compose up

Creating network "composetest_default" with the default driver
Creating composetest_web_1 ...
Creating composetest_redis_1 ...
Creating composetest_web_1
Creating composetest_redis_1 ... done
Attaching to composetest_web_1, composetest_redis_1
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
...

Снова проверьте сообщение Hello World в веб-браузере и обновите страницу, чтобы увидеть приращение счетчика.

Общие папки, тома и связывающие монтирования

  • Если ваш проект находится за пределами каталога Users (cd ~), вам необходимо предоставить общий доступ к диску или расположению файла Dockerfile и тома, которые вы используете. Если вы получаете ошибки во время выполнения, указывающие на то, что файл приложения не найден, монтирование тома отклонено или служба не может запуститься, пытается включить общий доступ к файлам или дискам. Для подключения тома требуются общие диски для проектов, которые находятся за пределами C:\Users (Windows) или /Users (Mac), и требуется для любого проекта в Docker Desktop для Windows, который использует Контейнеры Linux. Дополнительные сведения см. в разделе Обмен файлами в Docker для Mac и в общих примерах использования Управление данными в контейнерах.

  • Если вы используете Oracle VirtualBox в более старой ОС Windows, вы можете столкнуться с проблемой с общими папками, как приведено в этом Тикет о неисправности VB. Более новые системы Windows соответствуют требованиям Docker Desktop для Windows и не нуждаются в VirtualBox.

Шаг 7: Обновление приложения

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

Измените приветствие в app.py и сохраните его. Например, измените сообщение Hello World! на Hello from Docker!:

return 'Hello from Docker! I have been seen {} times.\n'.format(count)

Обновите приложение в браузере. Приветствие должно быть обновлено, а счётчик по-прежнему должен увеличиваться.

hello world in browser

Шаг 8. Поэкспериментируйте с некоторыми другими командами

Если вы хотите запускать свои службы в фоновом режиме, вы можете передать флаг -d (для «отключенного» режима) в docker compose up и использовать docker compose ps, чтобы увидеть, что в данный момент работает:

$ docker compose up -d

Starting composetest_redis_1...
Starting composetest_web_1...

$ docker compose ps

       Name                      Command               State           Ports
-------------------------------------------------------------------------------------
composetest_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp
composetest_web_1     flask run                        Up      0.0.0.0:8000->5000/tcp

Команда docker compose run позволяет запускать одноразовые команды для ваших служб. Например, чтобы узнать, какие переменные среды доступны службе web:

$ docker compose run web env

См. docker compose --help, чтобы увидеть другие доступные команды.

Если вы запустили Compose с кодом docker compose up -d, остановите свои службы, как только закончите с ними:

$ docker compose stop

Вы можете снести все, полностью удалив контейнеры, с помощью команды down. Передать --volumes, чтобы также удалит том данных, используемый контейнером Redis:

$ docker compose down --volumes

Куда двигаться дальше?