Docker, одно из последних увлечений, - это удивительный и мощный инструмент для упаковки, доставки и запуска приложений. Однако понимание и настройка Docker для вашего конкретного приложения может занять некоторое время. Поскольку в Интернете полно концептуальных руководств, я не буду слишком углубляться в концептуальном плане контейнеров. Вместо этого я объясню, что означает каждая написанная мною строка и как вы можете применить это к своему конкретному приложению и конфигурации.
Почему Докер?
Я работаю в управляемой студентами некоммерческой организации под названием Hack4Impact в UIUC, где мы разрабатываем технические проекты для некоммерческих организаций, чтобы помочь им в выполнении их миссий. Каждый семестр у нас есть несколько проектных групп из 5–7 студентов-разработчиков программного обеспечения с различными уровнями навыков, включая студентов, которые только закончили свой первый курс информатики на уровне колледжа.
Поскольку многие некоммерческие организации часто запрашивают веб-приложения, я создал Flask Boilerplate, чтобы команды могли быстро запустить и запустить свои серверные службы REST API. Общие служебные функции, структура приложения, оболочки базы данных и соединения предоставляются вместе с документацией по настройке, передовыми методами кодирования и шагами по развертыванию Heroku.
Проблемы со средой разработки и зависимостями
Однако, поскольку мы принимаем новых студентов-разработчиков программного обеспечения каждый семестр, команды будут тратить много времени на настройку и устранение неполадок среды. У нас часто было несколько участников, разрабатывающих разные операционные системы, и возникало множество проблем (Windows, я указываю на вас). Хотя многие из этих проблем были тривиальными, например, запуск правильной версии базы данных PostgreSQL с правильным пользователем и паролем, это напрасная трата времени, которую можно было вложить в сам продукт.
В дополнение к этому, я писал документацию только для пользователей MacOS, используя только инструкции bash (у меня Mac), и, по сути, оставил пользователей Windows и Linux для просушки. Я мог бы развернуть несколько виртуальных машин и снова задокументировать настройку для каждой ОС, но зачем мне это делать, если есть Docker?
Войдите в Docker
С помощью Docker все приложение можно изолировать в контейнерах, которые можно переносить с машины на машину. Это обеспечивает согласованные среды и зависимости. Таким образом, вы можете «собрать один раз, запустить где угодно», и разработчики теперь смогут установить только одну вещь - Docker - и выполнить пару команд для запуска приложения. Новички смогут быстро начать развиваться, не беспокоясь об окружающей среде. Некоммерческие организации также смогут быстро вносить изменения в будущем.
Docker также имеет много других преимуществ, таких как его портативность и эффективность использования ресурсов (по сравнению с виртуальными машинами), а также то, что вы можете безболезненно настроить непрерывную интеграцию и быстро развернуть приложение.
Краткий обзор основных компонентов Docker
В Интернете есть много ресурсов, которые объяснят Docker лучше, чем я, поэтому я не буду останавливаться на них слишком подробно. Вот потрясающий пост в блоге о его концепциях и еще один, в частности, о Docker. Однако я рассмотрю некоторые из основных компонентов Docker, которые необходимы для понимания остальной части этого сообщения в блоге.
Образы Docker
Образы Docker - это доступные только для чтения шаблоны, описывающие контейнер Docker. Они включают в себя конкретные инструкции, написанные в файле Dockerfile, который определяет приложение и его зависимости. Думайте о них как о снимке вашего приложения в определенное время. Вы получите изображения, когда docker build
.
Контейнеры Docker
Контейнеры Docker - это экземпляры образов Docker. Они включают операционную систему, код приложения, среду выполнения, системные инструменты, системные библиотеки и так далее. Вы можете соединить несколько контейнеров Docker вместе, например, имея приложение Node.js в одном контейнере, подключенном к контейнеру базы данных Redis. Вы запустите контейнер Docker с docker start
.
Реестры Docker
Реестр Docker - это место для хранения и распространения образов Docker. Мы будем использовать образы Docker в качестве базовых образов из DockerHub, бесплатного реестра, размещенного на самом Docker.
Docker Compose
Docker Compose - это инструмент, который позволяет создавать и запускать сразу несколько образов Docker. Вместо того, чтобы запускать одни и те же несколько команд каждый раз, когда вы хотите запустить свое приложение, вы можете выполнять их все в одной команде - после того, как вы предоставите определенную конфигурацию.
Пример Docker с Flask и Postgres
Имея в виду все компоненты Docker, давайте приступим к настройке среды разработки Docker с приложением Flask, используя Postgres в качестве хранилища данных. В оставшейся части этого сообщения я буду ссылаться на Flask Boilerplate, репозиторий, о котором я упоминал ранее для Hack4Impact.
В этой конфигурации мы будем использовать Docker для создания двух изображений:
app
- приложение Flask, обслуживаемое через порт 5000postgres
- база данных Postgres обслуживается через порт 5432
Когда вы смотрите на верхний каталог, есть три файла, которые определяют эту конфигурацию:
- Dockerfile - скрипт, состоящий из инструкций по настройке
app
контейнеров. Каждая команда автоматическая и выполняется последовательно. Этот файл будет расположен в каталоге, в котором вы запускаете приложение (например,python manage.py runserver
,python app.py
илиnpm start
). В нашем случае он находится в верхнем каталоге (где находитсяmanage.py
). Dockerfile принимает инструкции Docker. - .dockerignore - указывает, какие файлы не включать в контейнер. Это похоже на
.gitignore
, но для контейнеров Docker. Этот файл связан с файлом Dockerfile. - docker-compose.yml - файл конфигурации для Docker Compose. Это позволит нам одновременно создавать
app
иpostgres
изображения, определять тома и указывать, чтоapp
зависит отpostgres
, а также устанавливать необходимые переменные среды.
Примечание. Для двух образов существует только один файл Dockerfile, потому что мы будем брать официальный образ Docker Postgres с DockerHub! Вы можете включить свой собственный образ Postgres, написав для него свой собственный файл Docker, но в этом нет смысла.
Dockerfile
Чтобы еще раз прояснить, этот Dockerfile предназначен для контейнера app
. В качестве обзора, вот весь Dockerfile - он, по сути, получает базовый образ, копирует приложение, устанавливает зависимости и устанавливает конкретную переменную среды.
FROM python:3.6 LABEL maintainer "Timothy Ko <[email protected]>" RUN apt-get update RUN mkdir /app WORKDIR /app COPY . /app RUN pip install --no-cache-dir -r requirements.txt ENV FLASK_ENV="docker" EXPOSE 5000
Поскольку это приложение Flask использует Python 3.6, нам нужна среда, которая его поддерживает и уже установила. К счастью, у DockerHub есть официальный образ, установленный поверх Ubuntu. В одной строке у нас будет базовый образ Ubuntu с Python 3.6, virtualenv и pip. В DockerHub есть множество образов, но если вы хотите начать со свежего образа Ubuntu и построить поверх него, вы можете это сделать.
FROM python:3.6
Затем я отмечаю, что являюсь сопровождающим.
LABEL maintainer "Timothy Ko <[email protected]>"
Пришло время добавить к изображению приложение Flask. Для простоты я решил скопировать приложение в каталог /app
на нашем образе Docker.
RUN mkdir /app COPY . /app WORKDIR /app
WORKDIR
- это, по сути, cd
в bash, а COPY
копирует определенный каталог в указанный каталог в образе. ADD
- это еще одна команда, которая делает то же самое, что и COPY
, но также позволяет добавлять репозиторий из URL-адреса. Таким образом, если вы хотите клонировать свой репозиторий git, а не копировать его из локального репозитория (для промежуточных и производственных целей), вы можете это использовать. COPY
, однако, следует использовать большую часть времени, если у вас нет URL-адреса. Каждый раз, когда вы используете RUN
, COPY
, FROM
или CMD
, вы создаете новый слой в своем образе докера, который влияет на то, как Docker хранит и кэширует образы. Для получения дополнительной информации о передовых методах и уровнях, см. Рекомендации по использованию Dockerfile.
Теперь, когда у нас есть репозиторий, скопированный в образ, мы установим все наши зависимости, которые определены в requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
Но предположим, что у вас есть приложение Node вместо Flask - вместо этого вы должны написать RUN npm install
. Следующим шагом будет указание Flask использовать конфигурации Docker, которые я жестко запрограммировал в config.py
. В этой конфигурации Flask будет подключаться к правильной базе данных, которую мы настроим позже. Поскольку у меня были производственные конфигурации и конфигурации для обычной разработки, я сделал так, чтобы Flask выбирал конфигурацию Docker всякий раз, когда для переменной среды FLASK_ENV
установлено значение docker
. Итак, нам нужно настроить это в нашем app
изображении.
ENV FLASK_ENV="docker"
Затем откройте порт (5000), на котором работает приложение Flask:
EXPOSE 5000
Вот и все! Поэтому независимо от того, на какой ОС вы работаете или насколько плохо вы следуете инструкциям в документации, ваш образ Docker будет таким же, как и у членов вашей команды из-за этого файла Dockerfile.
Каждый раз, когда вы создаете свой образ, будут выполняться следующие команды. Теперь вы можете создать этот образ с помощью sudo docker build -t app .
. Однако, когда вы запустите его с sudo docker run app
для запуска контейнера Docker, приложение столкнется с ошибкой подключения к базе данных. Это потому, что вы еще не подготовили базу данных.
docker-compose.yml
Docker Compose позволит вам сделать это и одновременно создать app
образ. Весь файл выглядит так:
version: '2.1' services: postgres: restart: always image: postgres:10 environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=${POSTGRES_DB} volumes: - ./postgres-data/postgres:/var/lib/postgresql/data ports: - "5432:5432" app: restart: always build: . ports: - 5000:5000 volumes: - .:/app
Для этого конкретного репозитория я решил использовать версию 2.1, так как мне было удобнее с ней, и в ней было еще несколько руководств и учебных пособий - да, это моя единственная причина не использовать версию 3. В версии 2 вы должны указать « услуги »или изображения, которые вы хотите включить. В нашем случае это app
и postgres
(это просто имена, на которые вы можете ссылаться, когда используете команды docker-compose. Вы называете их database
и api
или как угодно, что плавает ваша лодка).
Изображение Postgres
Глядя на службу Postgres, я указываю, что это образ postgres:10
, который является еще одним образом DockerHub. Этот образ представляет собой образ Ubuntu, на котором установлен Postgres, и который автоматически запускает сервер Postgres.
postgres: restart: always image: postgres:10 environment: - POSTGRES_USER=${USER} - POSTGRES_PASSWORD=${PASSWORD} - POSTGRES_DB=${DB} volumes: - ./postgres-data/postgres:/var/lib/postgresql/data ports: - "5432:5432"
Если вам нужна другая версия, просто измените цифру «10» на другую. Чтобы указать, какого пользователя, пароля и базы данных вы хотите использовать в Postgres, вам необходимо заранее определить переменные среды - это реализовано в Dockerfile официального образа Postgres Docker. В этом случае изображение postgres
вставит переменные среды $USER
, $PASSWORD
и $DB
и сделает их POSTGRES_USER
, POSTGRES_PASSWORD
и POSTGRES_DB
переменными среды внутри контейнера postgres. Обратите внимание, что $USER
и другие введенные переменные среды - это переменные среды, указанные на вашем собственном компьютере (в частности, процесс командной строки, который вы используете для запуска команды docker-compose up
. Введя свои учетные данные, это позволяет вам не фиксировать свои учетные данные в общедоступном репозитории. .
Docker-compose также автоматически вводит переменные среды, если у вас есть файл .env
в том же каталоге, что и ваш файл docker-compose.yml
. Вот пример файла .env для этого сценария:
USER=testusr PASSWORD=password DB=testdb
Таким образом, наша база данных PostgreSQL будет называться testdb с пользователем testusr с паролем password.
Наше приложение Flask будет подключаться к этой конкретной базе данных, потому что я записал ее URL-адрес в конфигурациях Docker, о которых упоминал ранее.
Каждый раз, когда контейнер останавливается и удаляется, данные удаляются. Таким образом, вы должны обеспечить постоянное хранилище данных, чтобы никакие данные базы данных не удалялись. Сделать это можно двумя способами:
- Докер-тома
- Монтирование локального каталога
Я решил установить его локально на ./postgres-data/postgres
, но он может быть где угодно. Синтаксис always[HOST]:[CONTAINER]
. Это означает, что любые данные из /var/lib/postgresql/data
фактически хранятся в ./postgres-data
.
volumes: - ./postgres-data/postgres:/var/lib/postgresql/data
Мы будем использовать тот же синтаксис для портов:
ports: - "5432:5432"
изображение приложения
Затем мы определим изображение app
.
app: restart: always build: . ports: - 5000:5000 volumes: - .:/app depends_on: - postgres entrypoint: ["python", "manage.py","runserver"]
Сначала мы определяем его как restart: always
. Это означает, что он будет перезапускаться при выходе из строя. Это особенно полезно, когда мы создаем и запускаем эти контейнеры. app
обычно запускается до postgres
, что означает, что app
попытается подключиться к базе данных и потерпит неудачу, поскольку postgres
еще не работает. Без этого свойства app
просто остановился бы, и на этом все.
Затем мы определяем, что хотим, чтобы эта сборка была файлом Dockerfile, который находится в этом текущем каталоге:
build: .
Этот следующий шаг очень важен для перезапуска сервера Flask всякий раз, когда вы меняете какой-либо код в локальном репозитории. Это очень полезно, так как вам не нужно каждый раз заново строить образ, чтобы увидеть свои изменения. Для этого мы делаем то же самое, что и для postgres
: мы заявляем, что каталог /app
внутри контейнера будет тем, что находится внутри (текущий каталог). Таким образом, любые изменения в вашем локальном репо будут отражены внутри контейнера.
volumes: - .:/app
После этого нам нужно сообщить Docker Compose, что приложение зависит от postgres
container. Обратите внимание: если вы измените имя изображения на другое, например database
, вы должны заменить это postgres
на это имя.
depends_on: - postgres
Наконец, нам нужно предоставить команду, которая вызывается для запуска нашего приложения. В нашем случае это python manage.py runserver
.
entrypoint: ["python", "manage.py","runserver"]
Одно предостережение для Flask заключается в том, что вы должны явно указать, на каком хосте (порту) вы хотите запустить его, и хотите ли вы, чтобы он находился в режиме отладки при запуске. Итак, в manage.py
я делаю это с помощью:
def runserver(): app.run(debug=True, host=’0.0.0.0', port=5000)
Наконец, соберите и запустите приложение Flask и базу данных Postgres, используя командную строку:
docker-compose build docker-compose up -d docker-compose exec app python manage.py recreate_db
Последняя команда по существу создает схему базы данных, определенную моим приложением Flask в Postgres.
Вот и все! Вы должны увидеть, как приложение Flask работает на http: // localhost: 5000!
Команды Docker
Запоминание и поиск команд Docker вначале может быть довольно неприятным, поэтому вот их список! Я также написал кучу часто используемых в моих Документах по Flask Boilerplate, если вы хотите сослаться на это.
Заключение
Docker действительно позволяет командам развиваться намного быстрее благодаря своей переносимости и согласованности сред на разных платформах. Хотя я использовал Docker только для разработки, Docker превосходен, когда вы используете его для непрерывной интеграции / тестирования и развертывания.
Я мог бы добавить еще пару строк и полностью настроить производство с помощью Nginx и Gunicorn. Если бы я хотел использовать Redis для кэширования сеанса или в качестве очереди, я мог бы сделать это очень быстро, и все в моей команде могли бы иметь ту же среду при восстановлении своих образов Docker.
Мало того, я мог бы развернуть 20 экземпляров приложения Flask за секунды, если бы захотел. Спасибо за прочтение! :)
Если у вас есть какие-либо мысли и комментарии, не стесняйтесь оставлять комментарии ниже или напишите мне по адресу [email protected]! Кроме того, не стесняйтесь использовать мой код или поделиться им со своими коллегами!