Привет, меня зовут Андрей Двояк. Я специалист по комплексной разработке веб-приложений в украинском стартапе Preply.com, это платформа для поиска репетиторов. За последний год наша команда выросла, и для облегчения процесса адаптации новых разработчиков мы решили организовать и стандартизировать наш процесс разработки.
Мы посвятили много времени поиску лучших методик и обсуждению с другими командами, стремясь выяснить, какой путь наиболее эффективен для организации процесса разработки и распределения доступа, особенно когда ваш продукт разделен на несколько микросервисов. В итоге мы остановились на двух основных технологиях: Vagrantи Docker. Вы можете прочитать обо всех аспектах и особенностях у самих создателей этих сервисов на StackOverflow.
В этом руководстве я покажу вам, как «докеризировать» ваше приложение, чтобы вы таким образом могли удобно и просто распространить и развернуть его на любой машине, поддерживающей Docker.
Прежде, чем начать
В этом руководстве мы будем работать с простым приложением Django с базой PostgerSQLи Redisв качестве брокера для выполнения задач Celery. Также мы используем Supervisorдля запуска нашего сервера Gunicorn. Мы будем использовать технологию Docker Compose, чтобы организовать работу нашего мультиконтейнерного приложения. Обратите внимание, что Compose 1.5.1 требует Docker 1.8.0 или более поздние версии.
Это поможет нам запустить приложение Django, PostgreSQL и Redis Server и Celery Worker в отдельных контейнерах и связать их между собой. Чтобы реализовать это все, нам нужно всего лишь создать несколько файлов в корневом каталоге вашего проекта Django рядом с файлом manage.py):
- Dockerfile — для создания финального образа и загрузки его в DockerHub;
- redeploy.sh — для развертывание в обеих средах DEV и PROD;
- docker-compose.yml — организовать работу нескольких контейнеров;
- Vagrantfile — предоставить виртуальную машину для среды разработки.
Мы используем переменную RUN_ENV для определения текущей среды. Вы набираете на клавиатуре export RUN_ENV=PROD на рабочем сервере и export RUN_ENV=DEV на виртуальной машине Vagrant.
Docker
Как вы, возможно, уже знаете, о Docker нужно знать две вещи: образы и контейнеры. Мы создадим образ, основанный на Ubuntu с установленными Python, PIP и другими инструментами, необходимыми для запуска вашего приложения Django. В этом образе будет предустановлено всё, что необходимо. Этот образ направим в публичное хранилище в DockerHub. Имейте ввиду, что этот образ не содержит ни одного файла вашего проекта.
Мы договорились хранить наш образ на DockerHub всегда обновленным. С этого образа мы загрузим контейнер, пробрасывая специфические порты и монтируя ваш локальный каталог с проектом к какой-то папке внутри контейнера. Это означает, что файлы вашего проекта будут доступны внутри контейнера. Никакой необходимости копировать файлы! Это очень удобно для процесса разработки, потому что вы можете вносить изменения в ваши файлы, и они сразу же будут изменены в работающем контейнере Docker, но это неприемлемо в реальной рабочей среде.
Как только вы запустили контейнер, мы запустим Supervisor с вашим сервером Gunicorn. Как только вы захотели сделать повторное развертывание, мы извлечем новые данные из GitHub, остановим и удалим существующий контейнер и запустим совершенно новый контейнер. Настоящее волшебство!
Установка Docker и Docker-Compose
Прежде чем начать, нам нужно установить Docker и Docker-Compose на наш локальный компьютер или сервер. Ниже указан скрипт, который это делает на Ubuntu 14.04:
sudo -i echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections curl -sSL https://get.docker.com/ | sh curl -L https://github.com/docker/compose/releases/download/1.5.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose usermod -aG docker ubuntu sudo reboot
Тут ubuntu — ваш текущий пользователь.
Если ваша текущая среда разработки не Ubuntu 14.04 — тогда вам будет лучше использовать Vagrant для создания этой среды. Подробнее расскажу в следующей статье.
Образ Docker
Во-первых, для подготовки проекта к развертыванию докером нам нужно создать образ при помощи только Python, PIP и нескольких зависимостей, необходимых для запуска Django.
Давайте создадим новый докер файл под названием Dockerfile в корневом каталоге проекта. Это будет выглядеть так:
FROM ubuntu:14.04 MAINTAINER Andrii Dvoiak RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections RUN apt-get update RUN apt-get install -y python-pip python-dev python-lxml libxml2-dev libxslt1-dev libxslt-dev libpq-dev zlib1g-dev && apt-get build-dep -y python-lxml && apt-get clean # Specify your own RUN commands here (e.g. RUN apt-get install -y nano) ADD requirements.txt requirements.txt RUN pip install -r requirements.txt WORKDIR /project EXPOSE 80
Вы можете указать ваши собственные команды RUN, например, установить другие необходимые инструменты.
Затем вам нужно создать образ из этого, используя команду:
docker build -t username/image
Тут username — ваше имя пользователя Dockerhub и image — название вашего нового образа для данного проекта.
Когда вам успешно удалось создать основной образ, лучше будет вам загрузить в облако DockerHub. Это можно сделать командой docker push username/image. И не переживайте, в этом образе нет никакой информации из вашего проекта (кроме файла requiremets.txt). Если вы используете приватное хранилище DockerHub, удостоверьтесь в исполнении docker loginперед загрузкой/выгрузкой образов.
Организация работы контейнеров
Итак, на данном этапе мы можем запустить наш контейнер с приложениям Django, но нам также нужно запустить некоторые другие контейнеры c Redis, базой данных PostgreSQL и Celery Worker. Чтобы упростить этот процесс, мы воспользуемся технологией Docker Compose, которая позволяет нам создать простой файл YML с инструкциями о том, какие контейнеры запускать и как линковать их между собой.
Давайте создадим этот волшебный файл и назовем его по умолчанию docker-compose.yml:
django: image: username/image:latest command: python manage.py supervisor environment: RUN_ENV: "$RUN_ENV" ports: - "80:8001" volumes: - .:/project links: - redis - postgres celery_worker: image: username/image:latest command: python manage.py celery worker -l info links: - postgres - redis postgres: image: postgres:9.1 volumes: - local_postgres:/var/lib/postgresql/data ports: - "5432:5432" environment: POSTGRES_PASSWORD: "$POSTGRES_PASSWORD" POSTGRES_USER: "$POSTGRES_USER" redis: image: redis:latest command: redis-server --appendonly yes
Как вы видите, мы запустим четыре проекта под названиями django, celery_worker, postgresи redis. Эти названия важны для нас.
Итак, во-первых, наш файл загрузит образ Redis из dockerhub и запустит из него контейнер. Во-вторых, он загрузит образ Postgres и запустит контейнер с закрепленными данными из радела local_postgres. О создании локальной базы данных расскажу подробней в следющей статье.
Затем, этот файл запустит контейнер с нашим приложением Django, направит
Вы можете направить любое количество портов, если необходимо — для этого просто добавьте новые записи в соответствующий раздел. Также вы можете пролинковать любое количество контейнеров, например, контейнер с вашей базой данных. Используйте тег :latestдля автоматической проверки обновленного образа на DockerHub.
Если вы назвали проект Redis Server именем redisв файле YML — вам нужно указать redisвместо localhostв вашем settings.pyчтобы позволить вашему приложению Django подсоединиться к redis:
REDIS_HOST = "redis" BROKER_URL = "redis"
То же с базой данных Postgres — используйте postgresвместо localhost:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'database_name', 'USER': os.getenv('DATABASE_USER', ''), 'PASSWORD': os.getenv('DATABASE_PASSWORD', ''), 'HOST': 'postgres', 'PORT': '5432', } }
Скрипт для повторного развертывания
Давайте создадим скрипт для повторного развертывания в один клик (давайте назовем его redeploy.sh):
#!/bin/sh if [ -z "$RUN_ENV" ]; then echo 'Please set up RUN_ENV variable' exit 1 fi if [ "$RUN_ENV" = "PROD" ]; then git pull fi docker-compose stop docker-compose rm -f docker-compose up -d
Давайте проверим, что он делает:
— Он проверяет, установлена ли переменная RUN_ENV и совершает выход, если нет
Если RUN_ENV установлена на PROD, он сделает команду ’git pull’ чтобы получить новую версию вашего проекта;
— Он остановит все проекты, указанные в файле docker-compose.yml;
— Он удалит все существующие контейнеры;
— Он запустит новые контейнеры.
Итак, это выглядит достаточно просто: чтобы сделать повторное развертывание, вам нужно всего лишь запустить ./redeploy.sh. Не забудьте дать права на исполнение (chmod +x redeploy.sh) этому скрипту и всем другим скриптам, описанным в инструкции.
Чтобы сделать быстрое повторное развертывание, используйте эту команду:
docker-compose up --no-deps -d django
Развертывание внутри контейнера
Нам нужно всего лишь запустить супервайзера для того, чтобы собственно запустить наш сервис внутри контейнера: python manage.py supervisor. Если вы не используете супервайзер, вы можете просто запустить ваш сервер вместо супервайзера.
Если же вы пользуетесь супервайзером, давайте посмотрим на файл ’supervisord.conf’:
[supervisord] environment=C_FORCE_ROOT="1" [program:__defaults__] redirect_stderr=true startsecs=10 autorestart=true [program:gunicorn_server] command=gunicorn -w 4 -b 0.0.0.0:8001 YourApp.wsgi:application directory={{ PROJECT_DIR }} stdout_logfile={{ PROJECT_DIR }}/gunicorn.log
Итак, супервайзер запустит ваш сервер Gunicorn с 4 работающими процессами и связанным портом 8001.
Процесс разработки
Чтобы зайти на ваш локальный сервер, пройдите по 127.0.0.1:8000.
Для повторного развертывания локальных изменений сделайте:
sh redeploy.sh
Чтобы просмотреть все логи в вашем проекте, выполните:
docker-compose logs
Чтобы быстро реализовать повторное развертывание изменений, используйте:
docker-compose restart django
Чтобы подсоединиться к Django — просто подсоединяйтесь (если ваш работающий контейнер назван CONTAINER):
docker exec -it CONTAINER python manage.py shell
Чтобы создать изначального суперпользователя:
from django.contrib.auth.models import User; User.objects.create_superuser(’admin’, ’admin@example.com’, ’admin’)
Выполнить миграцию:
docker exec -it CONTAINER python manage.py schemamigration blabla —auto
Или вы можете подсоединиться к bash внутри контейнера:
docker exec -it CONTAINER /bin/bash
Вы можете сохранить вашу локальную базу данных в файл .json (вы можете указать таблицу для сохранения):
docker exec -it CONTAINER python manage.py dumpdata > testdb.json
Или вы можете загрузить данные в вашу базу данных из файла:
docker exec -it CONTAINER python manage.py loaddata testdb.json
Используйте эту команду для мониторинга статуса ваших работающих контейнеров:
docker stats $(docker ps -q)
Используйте эту команду для удаления всех остановленных контейнеров:
docker rm -v `docker ps -a -q -f status=exited`
Вы можете играться с вашими контейнерами как вам захочется. Здесь — полезный Docker cheat sheet.
GIT
Когда сервер работает, он будет создавать дополнительные файлы, такие как .log, .pidи так далее. Вам не нужно их включать в репозиторий. Не забудьте создать файл .gitignore:
.idea db.sqlide3 *.pyc *.ini *.log *.pid /static .vagrant/
Статические файлы
У вас могут обнаружиться некоторые проблемы в работе со статическими файлами на рабочем сервере при использовании только gunicorn, поэтому не забудьте создать пустую папку /static/в вашем корневом каталоге проекта с файлом __init__.pyдля обслуживания из него статики.
По этой схеме ваш файл settings.py должен включать:
STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static')
И для обслуживания статики с помощью gunicorn на рабочем сервере добавьте это в конец файла urls.py:
SERVER_ENVIRONMENT = os.getenv('RUN_ENV', '') if SERVER_ENVIRONMENT == 'PROD': urlpatterns += patterns('', (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), )
Или же вы можете пользоваться другими сервисами для обслуживания статических файлов, например, использовать сервер nGinx.
P.S.В следующей статье я расскажу, как запустить Docker практически где угодно при помощи Vagrant. Также рассмотрим, как создавать среду разработки на виртуальной машине, которую вы можете легко передать вашим сотрудникам, не беспокоясь о том, какие операционные системы установлены у них локально.