I have a flask application that I want to host on my VPS. I already have a domain name that is pointed to the VPS's IP.
My project structure is like this:
+-nginx
| \nginx.conf
+-main.py
+-models.py
+-wsgi.py
+-requirements.txt
+-Dockerfile
+-docker-compose.yml
nginx\nginx.conf:
server {
listen 80;
server_name mydomain.com;
location / {
proxy_set_header Host $http_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_pass http://0.0.0.0:5000;
}
location /socket.io {
include proxy_params;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://0.0.0.0:5000/socket.io;
}
}
Dockerfile:
FROM python:3.8.3
WORKDIR /
COPY . /
RUN pip install --no-cache-dir -r requirements.txt
docker-compose.yml:
version: '3.8'
services:
web:
build: .
command: gunicorn --bind 0.0.0.0:5000 -k gevent -w 1 wsgi:socket_
ports:
- 5000:5000
environment:
- FLASK_APP=main.py
env_file:
- .env
depends_on:
- postgres
postgres:
image: postgres
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
- POSTGRES_DB=mydb
volumes:
- flask_polls_data:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
environment:
- PGADMIN_DEFAULT_EMAIL=mymaiil
- PGADMIN_DEFAULT_PASSWORD=mypass
- PGADMIN_LISTEN_PORT=80
ports:
- 15432:80
volumes:
- pgadmin:/var/lib/pgadmin
depends_on:
- postgres
nginx:
image: nginx:1.19-alpine
ports:
- 1337:80
depends_on:
- web
volumes:
flask_polls_data:
postgres:
pgadmin:
wsgi.py:
from main import app, socket_
if __name__ == "__main__":
socket_.run(app, debug=False)
The URI for the database is in .env:
URI=postgresql://myuser:mypassword@postgres:5432/mydb
I set the app config for URI like:
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("URI", None)
I get this error when I run docker-compose up --build
web_1 | Application object must be callable.
models.py:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, unique=True, nullable=False)
def __init__(self, name):
self.name = name
def __repr__(self):
return f"<User name: {self.name}>"
And this is main.py:
import os
from flask import Flask, render_template
from flask_socketio import SocketIO
from flask_migrate import Migrate
from sqlalchemy.orm.session import Session
from dotenv import load_dotenv
from models import *
load_dotenv()
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app)
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("URI", None)
db.init_app(app)
migrate = Migrate(app, db)
with app.app_context():
db.create_all()
sess: Session = db.session # type: ignore
@app.route("/test", methods=["GET"])
def test():
return {
"hello": "world"
}
if __name__ == '__main__':
socket_.run(app)
This is what I see in the console:
Attaching to test_postgres_1, test_pgadmin_1, test_web_1, test_nginx_1
nginx_1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
nginx_1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
nginx_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
postgres_1 |
postgres_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres_1 |
postgres_1 | 2022-09-10 09:06:25.043 UTC [1] LOG: starting PostgreSQL 14.5 (Debian 14.5-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
postgres_1 | 2022-09-10 09:06:25.045 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
postgres_1 | 2022-09-10 09:06:25.045 UTC [1] LOG: listening on IPv6 address "::", port 5432
postgres_1 | 2022-09-10 09:06:25.049 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_1 | 2022-09-10 09:06:25.058 UTC [24] LOG: database system was shut down at 2022-09-10 09:05:55 UTC
postgres_1 | 2022-09-10 09:06:25.073 UTC [1] LOG: database system is ready to accept connections
nginx_1 | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
nginx_1 | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
nginx_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
nginx_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
nginx_1 | /docker-entrypoint.sh: Configuration complete; ready for start up
web_1 | [2022-09-10 09:06:27 +0000] [1] [INFO] Starting gunicorn 20.1.0
web_1 | [2022-09-10 09:06:27 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
web_1 | [2022-09-10 09:06:27 +0000] [1] [INFO] Using worker: gevent
web_1 | [2022-09-10 09:06:27 +0000] [7] [INFO] Booting worker with pid: 7
web_1 | /usr/local/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:872: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
web_1 | warnings.warn(FSADeprecationWarning(
web_1 | Application object must be callable.
web_1 | [2022-09-10 09:06:30 +0000] [7] [INFO] Worker exiting (pid: 7)
web_1 | [2022-09-10 09:06:30 +0000] [1] [INFO] Shutting down: Master
web_1 | [2022-09-10 09:06:30 +0000] [1] [INFO] Reason: App failed to load.
test_web_1 exited with code 4
If I go to to VPS IP I get the default nginx page where it tells me further configuration is required. If I go to domain I get 502 Bad Gateway.