Опишу свои страдания на тему.
В итоге я победил этот маразм, который есть результат многих совпадений, приведших к потере 2 дней в поисках решений.
Дисклеймер. Избитая задача деплоя Flask проекта на боевой сервер. Так как я ленивый и сижу на винде, uswgi и gunicorn были отметены, ибо протестировать их в винде нельзя. Я нашел waitress, протестировал его закатал проект в docker.
Но вот незадача в проекте есть return redurect(utl_for(somefunc)). И эта фигня не работала. Сервер возвращал что-то вроде hostname%##hostname/url. Как оказалось это известная проблема ReverseProxy во Flask. Ее решение вроде как представлено в документации Flask.
http://flask.pocoo.org/snippets/35/. Разбираться в этой хурме незахотелось, да и вообще на мой взгляд неправильно кодом проекта чинить баг на стыке веб-сервера (nginx) и сервера приложений (waitress). При том, что debug сервер flask app.run прекрасно работал с проксей.
Вот конфиг nginx рабочий с app.run (с учетом, что flaskapp, nginx, postgress в контейнерах).
flaskapp.conf
server { listen 80; server_name localhost;
location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off;
proxy_pass http://flaskapp:5090;
} }
dockerfile
FROM python:3.6.5-alpine MAINTAINER Brendel Vadim brendel.vadim@gmail.com
RUN echo "http://dl-cdn.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
RUN apk --update add --no-cache
lapack-dev
gcc
freetype-dev
RUN apk add --no-cache --virtual .build-deps
gfortran
musl-dev
g++
RUN ln -s /usr/include/locale.h /usr/include/xlocale.h
COPY . /app WORKDIR /app RUN file="$(ls -1 /app)" && echo $file RUN apk --no-cache add build-base RUN apk --no-cache add postgresql-dev RUN pip3 install -r requirements.txt
RUN apk del .build-deps
EXPOSE 5090 ENTRYPOINT ["python"] CMD ["upwbp.py"]
docker-compose.yml
version: '3' services: flaskapp: image: me/flaskapp build: context: flaskapp volumes:
networks:
depends_on:
restart: always nginx: image: nginx:alpine ports:
volumes:
depends_on:
networks:
restart: always db: image: postgres:10.5-alpine ports:
volumes:
env_file:
networks:
restart: always networks: web_nw: driver: bridge db_nw: driver: bridge volumes: dbdata:
Окей выкидываем waitress, накатываем uwsgi, все делаем по феньшую.
Нашел вот такой мануал. https://medium.com/@greut/minimal-python-deployment-on-docker-with-uwsgi-bc5aa89b3d35.
FROM alpine:3.7
EXPOSE 3031 VOLUME /usr/src/app/public WORKDIR /usr/src/app
RUN apk add --no-cache
uwsgi-python3
python3
COPY . . RUN rm -rf public/*
RUN pip3 install --no-cache-dir -r requirements.txt
CMD [ "uwsgi", "--socket", "0.0.0.0:3031",
"--uid", "uwsgi",
"--plugins", "python3",
"--protocol", "uwsgi",
"--wsgi", "main:application" ]
Вроде все понятно. Правлю dockerfile, docker-compose. conf. Не взлетает хурма. Так как в чем затык непонятно, для разделения стыка uwsgi, nginx принято решение запустить uwsgi в режиме http. Тогда отключив nginx можно поглядеть, работает корректно ли uwsgi.
Для тестирования я собрал простенький проект-каркас, который грузит немного модулей, создает app=Flask и возвращает Helloworld. И началась боль.
FROM python:3.6.6-alpine MAINTAINER Brendel Vadim brendel.vadim@gmail.com
RUN echo "http://dl-cdn.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
RUN apk --update add --no-cache
lapack-dev
gcc
freetype-dev
RUN apk add --no-cache --virtual .build-deps
gfortran
musl-dev
g++
RUN ln -s /usr/include/locale.h /usr/include/xlocale.h
VOLUME /usr/src/app/public
WORKDIR /usr/src/app
COPY . .
RUN rm -rf public/*
RUN file="$(ls -1 /usr/src/app)" && echo $file
#RUN apk --no-cache add build-base
RUN apk add build-base
#postgresql-dev
curl
linux-headers
pcre-dev
python3-dev
RUN curl -O http://projects.unbit.it/downloads/uwsgi-lts.tar.gz
tar -xvzf uwsgi-2.0.12.tar.gz
cd u
#RUN apk add --no-cache \
RUN pip3 install uWSGI RUN pip3 install -r requirements.txt
RUN apk del .build-deps
EXPOSE 5090 ENTRYPOINT ["tail", "-f", "/dev/null"]
Контейнер с flaskapp постоянно перегружается, если в конфиг uwsgi добавить http. С горем пополам выяснилось, что http-socket вроде как должен работать из коробки, а вот http надо ставить плагин. В дистрибутива debian вроде как есть uwsgi-plugins-all. А вот в индексе пакетов alpine есть либо uwsgi-python3, либо uwsgi-http. Бинарника с двумя одновременно плагинами нет. Более того если поставить uwsgi-python3 и использовать socket вместо http, контейнер все равно валится. Причина — валится uwsgi, он выдает ошибку (еще логирование настроить надо!) — не может импортировать модули Python (flask, flask-restful). Вот это вообще беда. Быстра пересборка всего:
docker-compose up -d --force-recreate --build.
Выяснилось следующее, что при установке apk add uwsgi-python3. Бинарник ставится в /usr/bin/uwsgi, а если использовать pip install uwsgi (как рекомендует официальный сайт) то в /usr/local/bin/uwsgi. Где-то здесь порылась собака. uwsgi не видит основной python системы и поэтому не грузит модули, которые 100% были установлены из requirements.txt во время создания докера. Если зайти в docker
docker exec -ti flask-example_flaskapp_1 sh
И вручную сделать python, import flask все работает. Скорее всего проблема в том что пример dockerfile с uwsgi-python3 начинается от чистого alpine и ставит python3 после установки uwsgi (скорее всего модули тянут одинаковую версию python из alpine index). А у меня dockerfile начинается от python:3.6.6-alpine. Но даже если согласовать версии python, запустить uwsgi с htpp не получится, т.к. нет в alpine index.
Окей, будем добавлять модули в uwsgi вручную.
Я нашел ЕДИНСТВЕННЫЙ внятный материал как ставить uswgi из исходников вот тут. Спасибо огромное человеку. Из документации с сайта uswgi нифига не понятно (хотя это скорее я тупой).
How to build uwsgi from source and run as a service As you might have already noticed, I always deploy python applications using uwsgi and nginx. But I didn't write yet how to install uwsgi. P...
CODEINPYTHON.BLOGSPOT.COM
Делаем как написано в инструкции. копируем скомплированные модули. запускаем ./uwsgi /usr/src/app/uwsgi_init.ini
uwsgi_init.ini
[uwsgi] plugins = python http = 0.0.0.0:5090 plugins-dir = /usr/lib/uwsgi/ ;logto = file:/uwsgi.log module = application:app daemonize = /tmp/uwsgi_daemonize.log
Делаем curl localhost:5090. И вуаля! Все рабоет. Было трудно... Но теперь все-равно надо вернуть socket и прикрутить nginx. И еще написать ручную установку python plugina в докерфайле. Либо же согласовать python:alpine и apk add uwsgi-python3.
Итак docker побежден, все что нужно запущено и работает.
Теперь на будущее.
Надо взять се свои сайты и распихать так:
nginx — 1 контейнер, читает конфигурацию и volume, логи пишет в другой volume.
Каждое приложение — в своем докере со своим uwsgi. PHP/python подключен как модуль (uwsgi лучше собирать из исходников в каждом контейнере). uwsgi в каждым контейнере пишет в unix socket в volume. Эти же volume монтируются в nginx container, откуда он их читает.
Use Unix sockets with Docker WWW.JUJENS.EU
Также каждое приложение монтирует свою статику в volume, эти же volume монтируются в nginx. Nginx настраивается на отдачу статики.
Mysql в отдельном докере. База в volume. Все контейнеры приложений пишут в нее в свою базу.
В основной системе скрипт, который запускается по Crony, делает дамп всего пакует и кладет резервную копию в YandexDisk с учетом ротации. Как-то так.