MyTetra Share
Делитесь знаниями!
Виртуализация процесса разработки, часть 1: Docker
Время создания: 02.02.2018 13:50
Автор: Андрей Двояк
Текстовые метки: docker development tutorial
Раздел: Docker

Привет, меня зовут Андрей Двояк. Я специалист по комплексной разработке веб-приложений в украинском стартапе 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):

  1. Dockerfile — для создания финального образа и загрузки его в DockerHub;
  2. redeploy.sh — для развертывание в обеих средах DEV и PROD;
  3. docker-compose.yml — организовать работу нескольких контейнеров;
  4. 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

Как вы видите, мы запустим четыре проекта под названиями djangocelery_workerpostgres и redis. Эти названия важны для нас.

Итак, во-первых, наш файл загрузит образ Redis из dockerhub и запустит из него контейнер. Во-вторых, он загрузит образ Postgres и запустит контейнер с закрепленными данными из радела local_postgres. О создании локальной базы данных расскажу подробней в следющей статье.

Затем, этот файл запустит контейнер с нашим приложением Django, направит 8001-й порт изнутри на 80-й снаружи, свяжет ваш текущий каталог прямо с папкой /project внутри контейнера, пролинкует его с контейнерами Redis и Postgres и запустит супервайзера. И последнее, но не менее важное — наш контейнер с celery worker, который также пролинкован с Postgres и Redis.

Вы можете направить любое количество портов, если необходимо — для этого просто добавьте новые записи в соответствующий раздел. Также вы можете пролинковать любое количество контейнеров, например, контейнер с вашей базой данных. Используйте тег :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. Также рассмотрим, как создавать среду разработки на виртуальной машине, которую вы можете легко передать вашим сотрудникам, не беспокоясь о том, какие операционные системы установлены у них локально.

 
MyTetra Share v.0.53
Яндекс индекс цитирования