MyTetra Share
Делитесь знаниями!
Виртуализация процесса разработки, часть 1: Docker
Время создания: 02.02.2018 13:50
Автор: Андрей Двояк
Текстовые метки: docker development tutorial
Раздел: Docker
Запись: Velonski/mytetra-database/master/base/15175614486e3p3bwlnu/text.html на raw.githubusercontent.com

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