Непрерывная интеграция (CI, Continuous Integration) – это практика разработки ПО, которая подразумевает частое слияние кода, тестирование каждого коммита и создание общего репозитория путём автоматизированной сборки.
CI позволяет ускорить процесс разработки и сводит к минимуму вероятность возникновения критических ошибок на стадии производства. Однако среду непрерывной разработки не так просто настроить. Автоматизированная сборка запускается в другой среде, потому установка рабочих зависимостей и настройка внешних сервисов может выполняться нестандартным способом.
Docker – это платформа контейнеризации, которая устраняет проблемы стандартизации среды. Она позволяет стандартизировать развёртывание приложений.
Читайте также: Экосистема Docker: основы контейнеризации
С помощью Docker разработчики могут симулировать среду разработки на локальной машине, запустив компоненты приложения в локальных контейнерах. Управление этими контейнерами можно автоматизировать с помощью Docker Compose.
Данное руководство поможет автоматизировать рабочие процессы непрерывной интеграции с Docker Compose. Вы узнаете, как создать простое приложение Python «Hello world» и тестовый сценарий Bash в Docker. Для запуска приложения Python требуется два контейнера: первый для самого приложения, второй – для зависимости, хранилища Redis.
Тестовый сценарий будет помещён в отдельный контейнер, а среда тестирования перемещена в файл docker-compose.test.yml.
Примечание: Вы можете использовать этот подход для создания идентичной среды тестирования своего собственного приложения и его зависимостей.
В результате у вас получится автоматизированная среда тестирования для непрерывной интеграции.
Требования
- Сервер Ubuntu 16.04.
- Пользователь с доступом к sudo (инструкции можно найти здесь).
- Предустановленная платформа Docker (для этого выполните разделы 1 и 2 данного руководства).
- Инструмент Docker Compose (читайте руководство по установке).
1: Создание приложения Python
Для начала нужно создать простое приложение Python, которое будет использоваться в качестве примера.
Создайте каталог для приложения:
cd ~
mkdir hello_world
cd hello_world
Отредактируйте файл file app.py:
nano 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 = "<h3>Hello World!</h3>" \
"<b>Visits:</b> {visits}" \
"<br/>"
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
Вставьте в него:
Flask
Redis
Сохраните и закройте файл. Теперь зависимости приложения определены.
2: Контейнеризация приложения
Docker использует специальный файл под названием Dockerfile для определения всех процессов, необходимых для сборки образа приложения. Откройте этот файл в редакторе:
nano 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 нужно собрать из официального образа python:2.7.
- WORKDIR /app: определяет рабочий каталог внутри образа (/app).
- ADD requirements.txt /app/requirements.txt: добавляет в образ файл requirements.txt.
- RUN pip install -r requirements.txt: устанавливает зависимости pip.
- ADD app.py /app/app.py: добавляет в образ исходный код приложения.
- EXPOSE 80: задаёт порт приложения (80, стандартный порт).
- CMD [“python”, “app.py”]: команда, которая запускает приложение.
Сохраните и закройте файл. Этот Dockerfile содержит всю информацию, необходимую для сборки главного компонента приложения.
Зависимости
Дальнейшая настройка немного сложнее. Данное приложение зависит от Redis. Установка такой зависимости в среде Linux, как правило, не выполняется каждый раз одинаково. В таком случае на помощь приходит Docker Compose.
Создайте docker-compose.yml и откройте его в редакторе:
nano docker-compose.yml
Вставьте в файл:
web:
build: .
dockerfile: Dockerfile
links:
- redis
ports:
- "80:80"
redis:
image: redis
Данный файл определяет, как запускается приложение Hello World в локальных контейнерах. В данном файле указаны два контейнера: web и redis.
- web использует текущий каталог для контекста build и собирает приложение Python с помощью Dockerfile. Это локальный образ Docker для данного приложения Python. Он определяет ссылку на контейнер redis, чтобы получить доступ к его IP. Кроме того, он открывает общий доступ к порту 80 с помощью внешнего IP сервера Ubuntu.
- redis выполняется из стандартного публичного образа Docker под названием redis.
Сохраните и закройте файл.
3: Развёртывание приложения
Теперь можно попробовать развернуть приложение и сделать его доступным в сети. Процесс развёртывания можно выполнить в среде разработки, отладки или производства.
Файлы 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
Команда должна вернуть:
<h3>Hello World!</h3><b>Visits:</b> 2<br/>
Число посещений увеличивается на единицу каждый раз, когда вы запускаете вышеприведённую команду. Вы также можете получить доступ к приложению «Hello World» в браузере, посетив внешний IP-адрес сервера Ubuntu.
Кастомизация развёртывания под другое приложение
Чтобы настроить развёртывание собственного приложения, вам нужно выполнить его контейнеризацию и запустить все зависимости в отдельных контейнерах. Затем можно определить отношения между контейнерами с помощью Docker Compose, как было показано ранее.
Читайте также:
- Установка и использование Docker Compose
- Установка WordPress и PhpMyAdmin с помощью Docker Compose в Ubuntu 14.04
4: Создание тестового сценария
Теперь нужно создать сценарий для приложения Python. Это будет простой сценарий, который проверяет вывод приложения. Это отличный пример сценария для тестирования приложения, который вы можете использовать в среде непрерывной интеграции.
Отредактируйте файл:
nano test.sh
Вставьте в него:
sleep 5
if curl web | grep -q '<b>Visits:</b> '; then
echo "Tests passed!"
exit 0
else
echo "Tests failed!"
exit 1
fi
Файл test.sh проверяет связуемость приложения. Сценарий использует cURL для извлечения количества посещений и сообщает о результатах проверки.
5: Настройка среды тестирования
Чтобы протестировать приложение, нужно развернуть специальную среду. Она должна быть идентична реальной среде приложения (раздел 3).
Выполните контейнеризацию сценария тестирования с помощью Dockerfile.
nano 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 для запуска сценария в Bash.
Теперь нужно связать контейнер сценария с приложением Hello World с помощью Docker Compose.
nano 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, чтобы сценарий test.sh мог получить доступ к IP-адресу контейнера приложения.
Кастомизация под другое приложение
Во время настройки важно помнить о том, что docker-compose.test.yml может включать десятки внешних сервисов и множество тестовых контейнеров. Docker может запускать все эти зависимости на одном хосте, поскольку все контейнеры совместно используют одну ОС.
Если вы хотите выполнить несколько тестов приложения, вы можете создать дополнительные файлы Dockerfile (подобные Dockerfile.test).
Также под контейнером sut в файле docker-compose.test.yml вы можете добавить другие контейнеры, ссылаясь на Dockerfile.
6: Тестирование приложения
Теперь среда тестирования настроена. Чтобы выполнить автоматическое тестирование приложения с помощью 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
Creating 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, если тестирование прошло успешно. Если вы получили другой вывод, приложение не прошло тест.
Готово! Теперь вы умеете настраивать среду тестирования, эмулирующую производство, и запускать автоматическое тестирование приложения.
Заключение
Инструменты Docker и Docker Compose выполняют автоматическую сборку приложений (Dockerfile) путём развёртывания локальной среды (docker-compose.yml), создания тестового образа (Dockerfile.test) и дальнейшего тестирования приложения (docker-compose.test.yml).
Преимущества файла docker-compose.test.yml:
- Автоматизация: выполнение файла docker-compose.test.yml не зависит от тестируемого приложения.
- Легковесность: файл позволяет разворачивать огромное количество внешних сервисов в рамках одного хоста и эмулировать сложную среду для тестирования приложения.
- Независимость: вы можете проводить тесты в любой инфраструктуре и операционной системе, которая поддерживает Docker.
Попробуйте протестировать свое собственное приложение, выполнить контейнеризацию сценариев тестирования и создать новый файл docker-compose.test.yml.