Вступление
Continuous integration (CI) относится к практике, когда разработчики кодируютintegrate как можно чаще, и каждая фиксация проверяется до и после объединения в общий репозиторийautomated build.
CI ускоряет процесс разработки и сводит к минимуму риск возникновения критических проблем в производственной среде, но его нетривиально настроить; Автоматические сборки выполняются в другой среде, где установкаruntime dependencies и конфигурацияexternal services могут отличаться от вашей локальной среды и среды разработки.
Docker - это платформа для контейнеризации, цель которой - упростить проблемы стандартизации среды, чтобы можно было стандартизировать развертывание приложений (find out more about Docker). Для разработчиков Docker позволяет моделировать производственные среды на локальных машинах, запуская компоненты приложения в локальных контейнерах. Эти контейнеры легко автоматизировать с помощьюDocker Compose, независимо от приложения и базовой ОС.
В этом руководстве используется Docker Compose для демонстрации автоматизации рабочих процессов CI.
Мы создадим приложение Python Dockerized «Hello world» и тестовый скрипт Bash. Для запуска приложения Python потребуется два контейнера: один для самого приложения и контейнер Redis для хранилища, которое требуется как зависимость для приложения.
Затем тестовый сценарий будет Dockerized в своем собственном контейнере, а вся тестовая среда будет перемещена в файлdocker-compose.test.yml, чтобы мы могли убедиться, что мы запускаем каждое выполнение теста в новой и однородной среде приложения.
Этот подход показывает, как вы можете создавать идентичную, свежую среду тестирования для своего приложения, включая его зависимости, каждый раз, когда вы тестируете его.
Таким образом, мы автоматизируем рабочие процессы CI независимо от тестируемого приложения и базовой инфраструктуры.
Предпосылки
Прежде чем начать, вам понадобится:
-
Сервер Ubuntu 16.04 сnon-root user with sudo privileges. Initial Server Setup with Ubuntu 16.04 объясняет, как это настроить.
-
Docker, установленный после шагов 1 и 2How To Install and Use Docker on Ubuntu 16.04.
-
Docker Compose, установленный после шага 1 изHow to Install Docker Compose on Ubuntu 16.04
[[step-1 -—- create-the-quot-hello-world-quot-python-application]] == Шаг 1. Создайте приложение Python «Hello World»
На этом шаге мы создадим простое приложение Python в качестве примера типа приложения, которое вы можете протестировать с помощью этой настройки.
Создайте новый каталог для нашего приложения, выполнив:
cd ~
mkdir hello_world
cd hello_world
Отредактируйте новый файлapp.py
с помощьюnano:
nano app.py
Добавьте следующий контент:
app.py
from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host="redis")
@app.route("/")
def hello():
visits = redis.incr('counter')
html = "Hello World!
" \
"Visits: {visits}" \
"
"
return html.format(visits=visits)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
Когда вы закончите, сохраните и выйдите из файла.
app.py
- это веб-приложение на основеFlask, которое подключается к службе данных Redis. Строкаvisits = redis.incr('counter')
увеличивает количество посещений и сохраняет это значение в Redis. Наконец, в HTML возвращается сообщениеHello World
с количеством посещений.
В нашем приложении есть две зависимости,Flask
иRedis
, которые вы можете увидеть в первых двух строках. Эти зависимости должны быть определены до того, как мы сможем выполнить приложение.
Откройте новый файл:
nano requirements.txt
Добавьте содержимое:
requirements.txt
Flask
Redis
Когда вы закончите, сохраните и выйдите из файла. Теперь, когда мы определили наши требования, которые мы изложим позже вdocker-compose.yml
, мы готовы к следующему шагу.
[[step-2 -—- dockerize-the-quot-hello-world-quot-application]] == Шаг 2 - Dockerize приложение «Hello World»
Docker использует файл с именемDockerfile
, чтобы указать необходимые шаги для создания образа Docker для данного приложения. Редактировать новый файл:
nano Dockerfile
Добавьте следующее содержание:
Dockerfile
FROM python:2.7
WORKDIR /app
ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
ADD app.py /app/app.py
EXPOSE 80
CMD ["python", "app.py"]
Давайте проанализируем значение каждой строки:
-
FROM python:2.7
: указывает, что наш образ приложения «Hello World» создан из официального образа Dockerpython:2.7
-
WORKDIR /app
: устанавливает рабочий каталог внутри образа Docker на/app
-
ADD requirements.txt /app/requirements.txt
: добавляет файлrequirements.txt
в наш образ Docker -
RUN pip install -r requirements.txt
: устанавливает зависимости приложенияpip
-
ADD app.py /app/app.py
: добавляет исходный код нашего приложения в образ Docker -
EXPOSE 80
: указывает, что наше приложение доступно через порт 80 (стандартный общедоступный веб-порт) -
CMD ["python", "app.py"]
: команда, запускающая наше приложение
Сохраните и выйдите из файла. Этот файлDockerfile
содержит всю информацию, необходимую для создания основного компонента нашего приложения «Hello World».
Зависимость
Теперь перейдем к более сложной части примера. Наше приложение требует Redis в качестве внешнего сервиса. Это тип зависимости, который может быть сложно каждый раз устанавливать одинаковым образом в традиционной среде Linux, но с помощью Docker Compose мы можем настраивать его повторяемым образом каждый раз.
Давайте создадим файлdocker-compose.yml
, чтобы начать использовать Docker Compose.
Редактировать новый файл:
nano docker-compose.yml
Добавьте следующее содержание:
docker-compose.yml
web:
build: .
dockerfile: Dockerfile
links:
- redis
ports:
- "80:80"
redis:
image: redis
Этот файл Docker Compose показывает, как локально раскрутить приложение «Hello World» в два контейнера Docker.
Он определяет два контейнера:web
иredis
.
-
web
использует текущий каталог для контекстаbuild
и строит наше приложение Python из только что созданного файлаDockerfile
. Это локальное изображение Docker, которое мы создали только для нашего приложения Python. Он определяет ссылку на контейнерredis
, чтобы иметь доступ к IP-адресу контейнераredis
. Это также делает порт 80 общедоступным из Интернета, используя общедоступный IP-адрес вашего сервера Ubuntu. -
redis
выполняется из стандартного общедоступного образа Docker с именемredis
.
Когда вы закончите, сохраните и выйдите из файла.
[[шаг-3 -—- deploy-the-quot-hello-world-quot-application]] == Шаг 3. Разверните приложение «Hello World»
На этом этапе мы развернем приложение, и к концу оно будет доступно через Интернет. В целях вашего рабочего процесса развертывания вы можете рассматривать это как среду разработки, промежуточную или производственную среду, поскольку вы можете развертывать приложение одинаково много раз.
Файлыdocker-compose.yml
иDockerfile
позволяют автоматизировать развертывание локальных сред, выполнив:
docker-compose -f ~/hello_world/docker-compose.yml build
docker-compose -f ~/hello_world/docker-compose.yml up -d
Первая строка создает образ нашего локального приложения из файлаDockerfile
. Вторая строка запускает контейнерыweb
иredis
в режиме демона (-d
), как указано в файлеdocker-compose.yml
.
Проверьте, что контейнеры приложения были созданы, выполнив:
docker ps
Это должно показать два запущенных контейнера с именамиhelloworld_web_1
иhelloworld_redis_1
.
Давайте проверим, что приложение запущено. Мы можем получить IP-адрес контейнераhelloworld_web_1
, выполнив:
WEB_APP_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' helloworld_web_1)
echo $WEB_APP_IP
Убедитесь, что веб-приложение возвращает правильное сообщение:
curl http://${WEB_APP_IP}:80
Это должно вернуть что-то вроде:
Выход
Hello World!
Visits: 2
Количество посещений увеличивается каждый раз, когда вы достигаете этой конечной точки. Вы также можете получить доступ к приложению «Hello World» из браузера, посетив общедоступный IP-адрес вашего сервера Ubuntu.
Как настроить для вашего собственного приложения
Ключом к настройке вашего собственного приложения является помещение вашего приложения в собственный контейнер Docker и запуск каждой зависимости из своего собственного контейнера. Затем вы можете определить отношения между контейнерами с помощью Docker Compose, как показано в примере. Docker Compose более подробно рассматривается в этомDocker Compose article.
Другой пример того, как запустить приложение в нескольких контейнерах, можно найти в этой статье о запускеWordPress and phpMyAdmin with Docker Compose.
[[step-4 -—- create-the-test-script]] == Шаг 4 - Создайте тестовый сценарий
Теперь мы создадим тестовый скрипт для вашего приложения Python. Это будет простой скрипт, который проверяет HTTP-вывод приложения. Сценарий является примером типа теста, который вы, возможно, захотите выполнить в рамках процесса развертывания непрерывной интеграции.
Редактировать новый файл:
nano test.sh
Добавьте следующее содержание:
test.sh
sleep 5
if curl web | grep -q 'Visits: '; then
echo "Tests passed!"
exit 0
else
echo "Tests failed!"
exit 1
fi
test.sh
проверяет базовое подключение к Интернету нашего приложения «Hello World». Он использует cURL для получения количества посещений и отчетов о том, был ли тест пройден или нет.
[[step-5 -—- create-the-testing-environment]] == Шаг 5. Создание среды тестирования.
Чтобы протестировать наше приложение, нам нужно развернуть среду тестирования. И мы хотим убедиться, что она идентична реальной среде приложения, которую мы создали вStep 3.
Во-первых, нам нужно Dockerize наш скрипт тестирования, создав новый файл Dockerfile. Редактировать новый файл:
nano Dockerfile.test
Добавьте следующее содержание:
Dockerfile.test
FROM ubuntu:xenial
RUN apt-get update && apt-get install -yq curl && apt-get clean
WORKDIR /app
ADD test.sh /app/test.sh
CMD ["bash", "test.sh"]
Dockerfile.test
расширяет официальный образubuntu:xenial
для установки зависимостиcurl
, добавляетtests.sh
в файловую систему образа и указывает командуCMD
, которая выполняет тестовый сценарий с Баш.
Как только наши тесты Dockerized, они могут выполняться реплицируемым и независимым способом.
Следующий шаг - связать наш тестовый контейнер с нашим приложением «Hello World». Здесь Docker Compose снова приходит на помощь. Редактировать новый файл:
nano docker-compose.test.yml
Добавьте следующее содержание:
docker-compose.test.yml
sut:
build: .
dockerfile: Dockerfile.test
links:
- web
web:
build: .
dockerfile: Dockerfile
links:
- redis
redis:
image: redis
Вторая половина файла Docker Compose развертывает основное приложениеweb
и его зависимостьredis
так же, как и предыдущий файлdocker-compose.yml
. Это часть файла, определяющая контейнерыweb
иredis
. Единственное отличие состоит в том, что контейнерweb
больше не предоставляет порт 80, поэтому приложение не будет доступно через общедоступный Интернет во время тестов. Итак, вы можете видеть, что мы создаем приложение и его зависимости точно так же, как в реальном развертывании.
Файлdocker-compose.test.yml
также определяет контейнерsut
(названный в честьsystem under tests), который отвечает за выполнение наших интеграционных тестов. Контейнерsut
определяет текущий каталог как наш каталогbuild
и указывает наш файлDockerfile.test
. Он связывается с контейнеромweb
, поэтому IP-адрес контейнера приложения доступен нашему сценариюtest.sh
.
Как настроить для вашего собственного приложения
Обратите внимание, чтоdocker-compose.test.yml
может включать в себя десятки внешних служб и несколько тестовых контейнеров. Docker сможет запустить все эти зависимости на одном хосте, потому что каждый контейнер использует общую ОС.
Если у вас есть дополнительные тесты для запуска вашего приложения, вы можете создать для них дополнительные файлы Docker, аналогичные файлуDockerfile.test
, показанному выше.
Затем вы можете добавить дополнительные контейнеры под контейнеромsut
в файлеdocker-compose.test.yml
, ссылаясь на дополнительные файлы Docker.
[[step-6 -—- test-the-quot-hello-world-quot-application]] == Шаг 6. Протестируйте приложение «Hello World»
Наконец, распространяя идеи Docker от локальных сред к средам тестирования, у нас есть автоматизированный способ тестирования нашего приложения с использованием Docker путем выполнения:
docker-compose -f ~/hello_world/docker-compose.test.yml -p ci build
Эта команда создает локальные образы, необходимыеdocker-compose.test.yml
. Обратите внимание, что мы используем-f
для указания наdocker-compose.test.yml
и-p
для указания имени конкретного проекта.
Теперь раскрутите свою свежую среду тестирования, выполнив:
docker-compose -f ~/hello_world/docker-compose.test.yml -p ci up -d
OutputCreating ci_redis_1
Creating ci_web_1
Creating ci_sut_1
Проверьте вывод контейнераsut
, выполнив:
docker logs -f ci_sut_1
Выход
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 42 100 42 0 0 3902 0 --:--:-- --:--:-- --:--:-- 4200
Tests passed!
И, наконец, проверьте код выхода контейнераsut
, чтобы проверить, прошли ли ваши тесты:
docker wait ci_sut_1
Выход
0
После выполнения этой команды значение$?
будет0
, если тесты пройдены. В противном случае наши тесты приложений не пройдены.
Обратите внимание, что другие инструменты CI могут клонировать наш репозиторий кода и выполнить эти несколько команд, чтобы проверить, проходят ли тесты последние биты вашего приложения, не беспокоясь о зависимостях времени выполнения или конфигурациях внешних служб.
Это оно! Мы успешно провели наш тест в новой среде, идентичной нашей производственной среде.
Заключение
Благодаря Docker и Docker Compose мы смогли автоматизировать сборку приложения (Dockerfile
), развертывание локальной среды (docker-compose.yml
), создание тестового образа (Dockerfile.test
) и выполнение (интеграционные) тесты (docker-compose.test.yml
) для любого приложения.
В частности, преимущества использования файлаdocker-compose.test.yml
для тестирования заключаются в том, что процесс тестирования:
-
Automatable: способ, которым инструмент выполняет
docker-compose.test.yml
, не зависит от тестируемого приложения -
Light-weight: сотни внешних сервисов могут быть развернуты на одном хосте, имитируя сложные (интеграционные) тестовые среды
-
Agnostic: избегайте привязки к поставщику CI, и ваши тесты могут работать в любой инфраструктуре и на любой ОС, которая поддерживает Docker.
-
Immutable: тесты, проходящие на вашем локальном компьютере, пройдут через ваш инструмент CI
В этом руководстве показан пример тестирования простого приложения «Hello World».
Теперь пришло время использовать ваши собственные файлы приложения, Dockerize ваши собственные тестовые сценарии приложений и создать свои собственныеdocker-compose.test.yml
для тестирования вашего приложения в свежей и неизменной среде.