В выпуске Stackato 3.6 мы представили возможность развертывания образов Docker в Stackato . Это обеспечивает альтернативу обычному методу развертывания приложений, используемому Cloud Foundry и Heroku, который основан на buildpacks .
То, что мы не рассмотрели здесь ранее, это то, как создавать «Docker-ized» приложения, которые будут работать в Stackato и использовать преимущества предоставляемых сервисов. Мы рассмотрим это здесь, взяв один из наших любимых примеров приложений Stackato и превратив его в образ Docker.
Начиная с нового «стека»
Одна из замечательных особенностей Docker заключается в том, что вы можете запускать разные версии Linux, не беспокоясь об ОС хост-системы. Stackato основан на Ubuntu 12.04 LTS, как и базовый образ stackato / stack-alsek, используемый для поэтапных приложений, но ничто не мешает нам запускать образы Docker на основе другого дистрибутива.
Я немного экспериментировал с образами Debian, CentOS и Alpine Linux, но для этого мы начнем с базового образа opensuse .
С помощью Docker вы можете разделить конфигурацию системы на отдельные уровни Docker, разбив шаги конфигурации на отдельные директивы RUN в одном Dockerfile , или создав отдельные образы и связав их вместе с директивой FROM в верхней части каждого отдельного Dockerfile. Мы сделаем немного того и другого с этим примером.
Обновление и установка пакетов ОС
Я немного сторонник поддержания своей операционной системы в актуальном состоянии, поэтому первое, что я делаю в своем Dockerfile, это запускаю обновление с помощью диспетчера пакетов ОС. На openSUSE это делается с помощью zypper
. После некоторых экспериментов ( -i
для этого docker run
крайне важна опция ) я создал Dockerfile, который выглядит следующим образом:
FROM opensuse:13.2
MAINTAINER Troy Topnik <[email protected]>
RUN \
zypper -n up \
&& zypper -n install python python-pip ca-certificates-mozilla \
vim git curl wget
Базовый образ openSUSE намеренно минимален (больше, чем большинство, но это не обязательно плохо). Как видите, я взял на себя смелость установки:
- Python: довольно важно для веб-приложения Python
- python-pip: для установки моих зависимостей Python
- CA-Certificates-Mozilla: необходим для pip для использования SSL
- vim: потому что во время экспериментов нам понадобится редактор
- git: для клонирования репозиториев на лету или установки pip из исходников git
- curl: для общего тестирования HTTP
- wget: для разной загрузки потенциально полезных вещей
После установки этих инструментов у меня есть большинство вещей, которые могут понадобиться для написания сценария установки приложения Python или для интерактивного тестирования в локально работающем контейнере Docker. Это также дает мне некоторые инструменты, которые могут понадобиться для устранения неполадок в приложении, работающем в Stackato (через stackato ssh
сеанс).
Обратите внимание, что я связал пару команд вместе в операторе RUN. Каждый раз, когда я использую команду RUN в Dockerfile, я получаю другой слой изображения, и в текущих версиях Docker существует жесткое ограничение в 127 слоев (ограничение AUFS по умолчанию). Объединение связанных команд уменьшает количество слоев, которые мы создаем, и помогает гарантировать, что мы не достигнем предела.
Если у нас много свободного места, может быть полезно иметь шаги обновления и установки пакета в отдельных командах RUN, чтобы воспользоваться возможностью Docker использовать кэшированные слои при перестроении, что может значительно ускорить сборку. Определенно есть несколько способов сделать это, но рекомендации по лучшему способу размещения слоев Docker широко освещены в других местах .
Давайте создадим то, что у нас есть, и можем приступить к следующему Dockerfile, который упаковывает наше приложение.
Создание, тестирование и передача изображений Docker
Я решил назвать свой новый слой изображений «base + 1» «troytop / opensuse-python». Я построил это, выполнив эту команду в пустой директории, содержащей мой Dockerfile:
$ sudo docker build --no-cache -t troytop/opensuse-python .
Я использовал --no-cache
в этом случае, потому что я хочу быть уверенным, что zypper update
каждый раз, когда я собираюсь, запускается, а не полагаться на слой кэшированного изображения из предыдущих сборок. Когда сборка завершается, я обычно осматриваю интерактивную оболочку, чтобы убедиться, что все работает так, как я ожидаю:
$ sudo docker run -i -t troytop/opensuse-python /bin/sh
sh-4.2# pip -V
pip 1.5 from /usr/lib/python2.7/site-packages (python 2.7)
sh-4.2# exit
Если я собираюсь поделиться этим с кем-то еще, я должен отправить его в Docker Hub или на другой сервер реестра:
$ sudo docker push troytop/opensuse-python
Следующий уровень: бутылка-валюта-сусе
Приложение Stackato-Apps / bottle-currency — одно из моих любимых демонстрационных приложений для Stackato. Билли Тат уже сделал версию Docker для этого на основе ubuntu: 12.04, поэтому я могу адаптировать созданный им Dockerfile для работы с моим образом opensuse-python.
Вот разбивка:
FROM troytop/opensuse-python
MAINTAINER Troy Topnik <[email protected]>
Ничего удивительного здесь. Использование другого контейнера в качестве отправной точки и определение себя в качестве сопровождающего.
RUN useradd -d /home/www -m -s /bin/bash www
USER www
WORKDIR /home/www
Здесь я создаю нового пользователя с именем «www» и устанавливаю активного пользователя и рабочий каталог. Если нам не нужно * запускать наше приложение как root, мы не должны. Многие веб-приложения Docker не беспокоятся об этом шаге (глядя на вас, Билли!), И в контексте Docker это может быть немного параноидально, но я думаю, что мы должны применять те же самые лучшие методы здесь, как и при запуске сервер на виртуальной машине или голый металл.
COPY . .
Мой Dockerfile будет существовать в базовом каталоге источников бутылочной валюты, поэтому эта команда рекурсивно скопирует содержимое текущего каталога в определенный выше WORKDIR.
RUN pip install --user -r requirements.txt
Это устанавливает модули, необходимые для приложения. В поэтапном приложении эта часть будет обрабатываться сборочным пакетом.
EXPOSE 8080
Наше приложение по умолчанию работает на порте 8080, если оно не видит переменную среды $ PORT, поэтому мы выставляем этот порт в контейнере с помощью этой команды, чтобы он направлялся на внешний порт на хосте DEA. В свою очередь, маршрутизатор Stackato сможет маршрутизировать приложение, перенаправляя внешние запросы на порт hostname: port.
При запуске поэтапного приложения в Stackato код приложения или Procfile должен будет ссылаться на переменную $ PORT, которую Stackato предоставляет в контейнере. С приложениями Docker нам просто нужно убедиться, что открыт только один порт.
CMD python wsgi.py
Наконец, мы указываем команду, которая запускает веб-процесс. В мире buildpack это будет указано в Procfile.
Когда готов Dockerfile, мы повторяем те же шаги, что и с образом opensuse-python, чтобы построить, протестировать и отправить изображение. Этап тестирования немного отличается, так как мы, вероятно, хотим посмотреть на это с помощью браузера. Для этого мы перенаправляем порт 8080 контейнера на порт 8000 на хосте:
$ sudo docker run -p 8000:8080 -t troytop/bottle-currency-suse
Предполагая, что мы делаем все это на локальном хосте, при подключении к http: // localhost: 8000 с помощью браузера будет отображаться работающее веб-приложение.
Это пока не сработает. Если вы попробуете это сами, вы увидите «Произошла ошибка при обращении к серверу», и приложение конвертации фактически не будет работать. Это потому, что приложение ищет сервис данных Redis. Есть несколько способов связать Docker-контейнеры с другими Docker-контейнерами, предоставляющими базы данных, но мы можем справиться с этим, когда он будет развернут в Stackato. А пока мы добавим наш новый образ в Docker Hub:
$ sudo docker push troytop/bottle-currency-suse
Прикрепление к сервисам
Приложение «бутылка-валюта» ищет службу данных Redis, предоставляемую переменной среды REDIS_URL. Stackato внедряет переменные среды (VCAP_SERVICES, _URL и т. Д.) В контейнеры Docker для предоставления информации о подключении для любых служб, которые были связаны с приложением. Это 12-факторный подход , и поэтапные приложения всегда работали в Cloud Foundry.
Итак, чтобы развернуть наше приложение в Stackato:
$ stackato push -n --docker-image troytop/bottle-currency-suse --mem 128 --as docker-currency
…
Это выбирает образ докера из концентратора и развертывает его (пока без базы данных). Далее мы создаем новый сервис данных Redis и привязываем его к приложению:
$ stackato create-service --plan free redis currency-data docker-currency
…
Этот шаг воссоздает контейнер с необходимой переменной REDIS_URL в нем.
Используя свой собственный код, вы можете найти несколько способов для анализа этих переменных среды и подключения к службам данных, но если вы пытаетесь развернуть то, что кто-то * еще * уже упаковал как образ Docker, вам, возможно, придется найдите творческие способы переформатировать учетные данные, предоставленные Stackato, в переменные, которые понимает приложение. Способ сделать это — добавить еще один слой поверх стороннего изображения, который анализирует переменные среды, предоставленные Stackato, переформатирует их так, как приложение поймет, и повторяет строку CMD / ENTRYPOINT из исходного изображения.
Но что я знаю …
Возможно, я сделал все это неправильно. Чем больше я читаю об оптимизации образов Docker, управлении изменениями в слоях изображений и написании хороших файлов Docker; тем больше я понимаю, что мне есть чему поучиться. Лучшие практики для упаковки приложений для контейнеров все еще появляются, но я уже вижу огромные преимущества декларативного, линейного, разрозненного подхода к упаковке и конфигурации, предоставляемого Docker.
Запуск образов Docker в контексте Platform-as-a-Service — совершенно новая территория, но она готова к исследованию.