Приведенная выше структура помещает всю информацию о контейнере в папку deployment
Весь код и ресурсы Python попадают в папку src в виде псевдомодуля
Подумайте о своем коде данных, живущем в src и определяющем конечные точки в файле wsgi.py
Установите фласк, если нету в ос:
yum -y install python-flask
Проверяем приложение в режиме отладки:
python ./src/wsgi.py * Running on http://0.0.0.0:8080/ * Restarting with reloader
http://10.106.65.42:8080/ Simple response from Flask running inside uWSGI, which is running within nGINX, which is hosted inside a Docker image!
Для этого поста мы хотим запустить наше приложение Flask внутри NGINX и uWSGI на OpenShift
Необходимо сначала настроить uWSGI, указать, где он может найти приложение Flask
Это делается в файле конфигурации ./deployment/uwsgi.ini
Основными вариантами, на которые следует обратить внимание, являются module и callable
Эти два параметра указывают службе uWSGI, где искать код Python для выполнения
В нашем случае мы просим его искать в папке /opt/repo/src файл wsgi.py и переменную app в этом файле
(которая является нашей главной вызываемой переменной Flask)
Явно указываем, где находится файл сокета этой службы, который нужно будет согласовать с конфигурацией NGINX
Конфигурация NGINX
Затем нам нужно сообщить NGINX, где он может найти файл сокета uWSGI
Когда новые запросы поступают на прослушиваемый порт, он знает, как соответствующим образом направить запросы
Это делается в файле ./deployment/nginx.conf
Определяем, где служба NGINX должна пытаться создать какие-либо журналы, временные файлы, какие порты прослушивать
Поскольку OpenShift будет запускать этот образ как произвольный пользователь, мы не можем использовать любые номера портов ниже 1024
(т.е. привилегированные порты, такие как стандартный HTTP 80 или HTTPS 443)
Поэтому назначим службе прослушивать порт 8080
Затем в рамках маршрутизации местоположения мы говорим NGINX направлять все запросы к сокету uWSGI и приложению Python (то есть @app)
За исключением любых статических файлов, которые должны идти непосредственно в эту папку на диске
Супервизор Конфиг
Последнее, что нам нужно настроить, - это способ заставить все это работать в одном образе
Лучшие практики Docker настоятельно рекомендуют одно приложение для каждого образа
В нашем случае нам нужен uWSGI для запуска нашего кода Python и NGINX для маршрутизации в uWSGI
Поэтому мы будем использовать хитрость запуска Supervisord для обработки нескольких одновременных сервисов
Это делается в файле ./deployment/supervisord.conf
Указываем, какие команды выполнять для каждой службы
Расположение файлов конфигурации, которые мы подробно описали ранее
Вы можете запустить supervisord или systemd от root и переключить пользователя для выполнения определенных служб
Однако, с произвольным идентификатором пользователя в OpenShift мы должны разрешить запуск этих служб как любой пользователь в группе root
Вот почему мы не указываем какие-либо пользовательские параметры в файле конфигурации и направляем журналы в /dev/stdout
(что позволит им отображаться в файлах журнала Docker во время работы образа)
Docker ENTRYPOINT
Когда файлы конфигурации настроены, нам просто нужно сказать Docker, что выполнять при запуске образа
Использование произвольного идентификатора пользователя в некоторых приложениях Python вносит корректировки в эти планы
Хорошей новостью является то, что есть простой обходной путь
Каждый раз, когда запускается наш образ Docker, нам просто нужно добавить проверку
Убедиться, что произвольный пользователь имеет запись в файле /etc/passwd
Это делается в файле ./deployment/docker-entrypoint.sh
Скрипт docker-entrypoint:
cat ./deployment/docker-entrypoint.sh #!/bin/bash set -e
# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html # if the running user is an Arbitrary User ID if ! whoami &> /dev/null; then # make sure we have read/write access to /etc/passwd if [ -w /etc/passwd ]; then # write a line in /etc/passwd for the Arbitrary User ID in the 'root' group echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:${HOME}:/sbin/nologin" >> /etc/passwd fi fi
# https://success.docker.com/article/use-a-script-to-initialize-stateful-container-data if [ "$1" = 'supervisord' ]; then exec /usr/bin/supervisord fi
exec "$@"
Для того, чтобы это работало правильно, нужно настроить образ Docker, разрешить всем пользователям доступ на запись в файл /etc/passwd
Как только это будет сделано, мы указываем второе условие, которое нужно отследить
Когда выполнять приложение supervisord в качестве произвольного идентификатора пользователя и направить все журналы в /dev/stdout
(supervisord в свою очередь, выполнит uWSGI и NGINX)
Dockerfile
Последний шаг в развертывании - сказать Docker, как создать и настроить образ с помощью файлов конфигурации, которые указали выше
Приятно то, что как только мы сделаем это один раз, сможем повторно использовать структуру/образ для будущих итераций нескольких проектов
Это делается в файле ./deployment/Dockerfile
cat ./deployment/Dockerfile # Use the standard Nginx image from Docker Hub FROM nginx
ENV HOME=/opt/repo
# install python, uwsgi, and supervisord RUN apt-get update && apt-get install -y supervisor uwsgi python python-pip procps vim && \ /usr/bin/pip install uwsgi==2.0.17 flask==1.0.2
# Source code file COPY ./src ${HOME}/src
# Copy the configuration file from the current directory and paste # it inside the container to use it as Nginx's default config. COPY ./deployment/nginx.conf /etc/nginx/nginx.conf
# Copy the base uWSGI ini file to enable default dynamic uwsgi process number COPY ./deployment/uwsgi.ini /etc/uwsgi/apps-available/uwsgi.ini RUN ln -s /etc/uwsgi/apps-available/uwsgi.ini /etc/uwsgi/apps-enabled/uwsgi.ini
COPY ./deployment/supervisord.conf /etc/supervisor/conf.d/supervisord.conf RUN touch /var/log/supervisor/supervisord.log
# enter WORKDIR ${HOME} ENTRYPOINT ["docker-entrypoint.sh"] CMD ["supervisord"]cat: file: No such file or directory
Этот пост не предназначен для просмотра каждой строки Dockerfile.
Просто знайте, что мы начинаем с официальной сборки NGINX (на которой уже установлен NGINX)
Добавляем несколько пакетов Python через PIP
Явно устанавливаем разрешения для всех папок, к которым NGINX, uWSGI и Supervisord будут касаться во время выполнения
Поэтому произвольный идентификатор пользователя в root группе имеет необходимые разрешения
Наконец, просим образ взглянуть на файл docker-entrypoint.sh, чтоб запускать supervisord для каждого запуска образа по умолчанию
Построение Docker image
Чтобы собрать все вышеупомянутые строительные блоки вместе, нам просто нужно выполнить сборку образа Docker
Выше примере нам нужно выполнить сборку из корневого каталога, чтобы у контекста сборки был доступ к папкам ./deployment и ./src
Тестирование образа Docker как произвольного идентификатора пользователя
Одно дело успешно создать свой образ Docker, другое - заставить работать в OpenShift, работающим под произвольным идентификатором пользователя
Хорошей новостью является то, что мы можем проверить это на нашей локальной машине с флагом пользователя -u
docker run -p 8080:8080 -u 777 prototype:latest
Выше мы выбрали произвольный идентификатор пользователя 777, не имеет значения, какой номер используется.
Вы должны иметь возможность изменить это значение на любое числовое значение
Ваш образ все равно должно работать правильно
Кроме того, мы перенаправляем порт 8080 на нашем локальном компьютере в службу NGINX внутри контейнера
Эт означает, что мы должны иметь возможность открыть веб-браузер на следующих конечных точках: http://localhost:8080/static/index.html : загрузить статическую HTML-страницу из NGINX http://localhost:8080/ : загрузить универсальную домашнюю конечную точку из Flask http://localhost:8080/echo_request : Эхо заголовок запроса из Flask обратно к вызывающей
Устранение неполадок с образами Docker
Если вышеперечисленное не работает, нужно отладить, чтоб выяснить, какие разрешения нужны приложению, и соответствующим образом изменить файл Docker
Чтобы отладить образ, вы можете перезаписать команду entrypoint командой:
docker run -it -p 8080:8080 -u 0 prototype:latest /bin/bash
Которая перенаправит вас в интерактивную командную строку bash от имени пользователя root, чтоб могли копаться для устранения неполадок внутри образа
Вам также может потребоваться изменить идентификатор пользователя, используемый в аргументе -u
Используйте CTRL + D или выход для завершения образа
Чтобы проверить выполнение supervisord, в командной строке bash вы можете выполнить следующее, чтоб просмотреть журналы, чтоб определить, в чем проблема
supervisorctl [start|stop|restart] nginx # NGINX service
supervisorctl [start|stop|restart] uwsgi # uWSGI service
Иногда ваше состояние сборки становится грязным с кучей образов, которые вам больше не нужны и занимают место на диске Чтобы убрать это, вы можете запустить:
docker system prune
Развертывание в репозиторие контейнеров
После того, как мы несколько раз изменили произвольный идентификатор пользователя и убедились в выполнении нашего прототипа с одним образом
Пришло время отправить его в репозиторий контейнеров для развертывания
Существует несколько способов создания и развертывания образов в OpenShift
Мы не будем вдаваться в конвейеры развертывания в этом посте
Все, что нам нужно сделать, это отправить наш образ в репозиторий контейнеров (например, Docker Hub), затем дать OpenShift команду извлечь и развернуть образ
Сначала нам нужно пройти аутентификацию в нашем репозиторие контейнеров
Где yourhubusername - это ваше имя пользователя в репозиторие контейнеров youremail@company.com - ваш адрес электронной почты, указанный в репозиторие контейнеров
Затем build/tag/push наше образ в этот контейнерный репозиторий
Где yourhubusername - это ваше имя пользователя в репозиторие контейнеров, в котором вы аутентифицировались
Теперь образ должен быть в вашем репозиторие контейнеров
Если вы используете общедоступный Docker Hub, можете перейти к своим репозиториям по этому URL (после аутентификации) и увидеть свое новый образ: https://hub.docker.com/
Развертывание в OpenShift
Openshift - платформа для разработки и публикации ПО, базируется на платформе оркестрации контейнеров Kubernetes
Теперь давайте скажем OpenShift выполнить развертывание из этого образа
Для этого мы собираемся использовать инструмент OpenShifts CLI
Для начала нам нужно пройти аутентификацию на нашем сервере OpenShift
Просто замените URL своим сервером OpenShift, а <MY_TOKEN> - своим токеном OpenShift
По сути, просто укажите на образ в репозиторие контейнеров, который мы только что создали
oc new-app yourhubusername/prototype
Затем нам нужно указать OpenShift добавить маршрут, чтобы наш веб-трафик мог доходить до нашего образа
Ниже мы говорим, чтобы он направлял трафик, предназначенный для prototype.example.com, для использования службы prototype через порт TCP 8080
Это эффективно говорит OpenShift, как направлять трафик к нашему образу NGINX, который мы только что создали
oc create route edge --service=prototype --hostname=prototype.example.com --port=8080-tcp
Теперь вы должны быть в состоянии перейти к комбинации hostname:port, чтоб увидеть ваше приложение, работающее в OpenShift, от произвольного пользователя
Время магической итерации
Теперь, когда мы прошли через всю эту конфигурацию и сборку, мы можем быстро перебрать прототип
Поскольку наш Dockerfile просто скопировал наш код Python из папки ./src, чтобы обновить образ, мы должны убедиться, что новый код находится внутри папки ./src
Проведем локальное тестирование с помощью нашей команды отладки Flask:
python ./src/wsgi.py
После того, как мы довольны новой функциональностью, мы можем создать и отправить изображение с помощью простой команды ./deploy.sh:
bash ./deploy.sh yourhubusername prototype latest
После завершения команды ваш URL в OpenShift выполнит непрерывное обновление службы и представит ваши новые функциональные возможности в прототипе всего за 5 минут