Python Flask SQLAlchemy 容器无法连接到 MySQL 容器
Posted
技术标签:
【中文标题】Python Flask SQLAlchemy 容器无法连接到 MySQL 容器【英文标题】:Python Flask SQLAlchemy container unable to connect to MySQL container 【发布时间】:2022-01-22 09:02:08 【问题描述】:我无法从 Flask 应用程序连接到我的 mysql 数据库。
我正在从this tutorial 学习使用 Python Flask 构建 Web 应用程序,但尝试修改其中的一些元素以试验 Docker。即使不使用 docker-compose,我也无法从 Web 应用程序连接到数据库。
我先给出应用日志中的错误回溯(flask_test容器):
[2021-12-20 18:13:18 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2021-12-20 18:13:18 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2021-12-20 18:13:18 +0000] [1] [INFO] Using worker: sync
[2021-12-20 18:13:18 +0000] [8] [INFO] Booting worker with pid: 8
[2021-12-20 18:13:19,105] INFO in __init__: Microblog startup
[2021-12-20 18:14:19,239] ERROR in app: Exception on /auth/register [POST]
Traceback (most recent call last):
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connection.py", line 174, in _new_conn
conn = connection.create_connection(
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/util/connection.py", line 96, in create_connection
raise err
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/util/connection.py", line 86, in create_connection
sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/flasktest/venv/lib/python3.10/site-packages/elasticsearch/connection/http_urllib3.py", line 255, in perform_request
response = self.pool.urlopen(
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 755, in urlopen
retries = retries.increment(
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/util/retry.py", line 507, in increment
raise six.reraise(type(error), error, _stacktrace)
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/packages/six.py", line 770, in reraise
raise value
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 699, in urlopen
httplib_response = self._make_request(
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 394, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connection.py", line 239, in request
super(HTTPConnection, self).request(method, url, body=body, headers=headers)
File "/usr/local/lib/python3.10/http/client.py", line 1282, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/local/lib/python3.10/http/client.py", line 1328, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/local/lib/python3.10/http/client.py", line 1277, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/local/lib/python3.10/http/client.py", line 1037, in _send_output
self.send(msg)
File "/usr/local/lib/python3.10/http/client.py", line 975, in send
self.connect()
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connection.py", line 205, in connect
conn = self._new_conn()
File "/home/flasktest/venv/lib/python3.10/site-packages/urllib3/connection.py", line 186, in _new_conn
raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7f73470be7d0>: Failed to establish a new connection: [Errno 111] Connection refused
这是 MySQL 容器 (mysql_test) 的日志:
2021-12-20T18:13:14.094155Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2021-12-20T18:13:14.098891Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2021-12-20T18:13:14.110089Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2021-12-20T18:13:14.110149Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.27' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
mbind: Operation not permitted
mbind: Operation not permitted
2021-12-20 18:13:10+00:00 [Note] [Entrypoint]: Creating database test_db
2021-12-20 18:13:10+00:00 [Note] [Entrypoint]: Creating user test_user
2021-12-20 18:13:10+00:00 [Note] [Entrypoint]: Giving user test_user access to schema test_db
2021-12-20 18:13:10+00:00 [Note] [Entrypoint]: Stopping temporary server
2021-12-20 18:13:13+00:00 [Note] [Entrypoint]: Temporary server stopped
2021-12-20 18:13:13+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
这里是Python应用的起点(microblog.py):
from app_pkg import create_app, cli
app = create_app()
cli.register(app)
这是模型类:
class User(UserMixin, SearchableMixin, db.Model):
__searchable__ = ['username']
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
这是我的 compose.yaml:
version: '3'
services:
python_app:
container_name: flask_test
build: .
env_file: .env
ports:
- 8000:5000
links:
- mysqldb:dbserver
depends_on:
mysqldb:
condition: service_healthy
mysqldb:
container_name: mysql_test
image: mysql:latest
env_file: database.conf
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
interval: 3s
retries: 5
start_period: 30s
volumes:
db-data:
这是我的 Dockerfile:
FROM python:slim
RUN useradd flasktest
WORKDIR /home/flasktest
COPY requirements.txt requirements.txt
RUN python -m venv venv
RUN venv/bin/pip install -r requirements.txt
RUN venv/bin/pip install gunicorn pymysql cryptography
COPY app_pkg app_pkg
COPY migrations migrations
COPY microblog.py config.py boot.sh ./
RUN chmod +x boot.sh
ENV FLASK_APP microblog.py
RUN chown -R flasktest:flasktest ./
USER flasktest
EXPOSE 5000
ENTRYPOINT ["./boot.sh"]
最后,这是 boot.sh:
#!/bin/bash
source venv/bin/activate
while true; do
flask db upgrade
if [[ "$?" == "0" ]]; then
break
fi
echo Upgrade command failed, retrying in 5 secs...
sleep 5
done
exec gunicorn -b :5000 --access-logfile - --error-logfile - microblog:app
这些是在应用程序中使用的一些必要的环境变量:
DATABASE_URL=mysql+pymysql://test_user:abc123@dbserver/test_db
MYSQL_ROOT_PASSWORD=root123
MYSQL_DATABASE=test_db
MYSQL_USER=test_user
MYSQL_PASSWORD=abc123
对不起,如果问题变得太长。我想在问题中提供尽可能多的细节。让我知道是否需要任何其他详细信息。过去一周我一直在尝试调试此问题,但无法找到将应用程序与 sql server 连接的方法。
如果我应该尝试任何特定方法来尝试调试此问题,请告诉我。
编辑:
create_app
函数:
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_mail import Mail
from flask_bootstrap import Bootstrap
from flask_moment import Moment
db = SQLAlchemy()
migrate = Migrate()
login = LoginManager()
login.login_view = 'auth.login'
login.login_message = _l('Please log in to access this page.')
mail = Mail()
bootstrap = Bootstrap()
moment = Moment()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
moment.init_app(app)
from app_pkg.errors import bp as errors_bp
app.register_blueprint(errors_bp)
from app_pkg.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix='/auth')
from app_pkg.main import bp as main_bp
app.register_blueprint(main_bp)
我尝试将 DATABASE_URL
从 mysql+pymysql://test_user:abc123@dbserver/test_db
更改为 mysql+pymysql://test_user:abc123@mysqldb/test_db
,但问题仍然存在。
我还尝试将 'ports: -3306:3306' 添加到 compose.yaml 并将 DATABASE_URL 更改为 0.0.0.0
作为主机,但这会导致此错误:
[+] Running 3/4
- Network flask_tutorial_default Created 0.7s
- Volume "flask_tutorial_db-data" Created 0.0s
- Container mysql_test Starting 2.4s
- Container flask_test Created 0.2s
Error response from daemon: Ports are not available: listen tcp 0.0.0.0:3306: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
【问题讨论】:
【参考方案1】:您当前正在使用 docker-compose dns 功能,您可以使用容器名称作为在 docker-compose 中运行的服务的域名,这很整洁 - 除非您重命名容器;)您是否从 dbserver 重命名了 mysqldb?
如果您想继续使用此功能,请将环境变量修改为:(将 dbserver 更改为 mysqldb)
DATABASE_URL=mysql+pymysql://test_user:abc123@mysqldb/test_db
...
如果您改为使用更明确的方法:
在您的 docker-compose 中,您需要通过
将端口 3306 绑定到您的主机网络version: '3'
services:
python_app:
container_name: flask_test
build: .
env_file: .env
ports:
- 8000:5000
links:
- mysqldb:dbserver
depends_on:
mysqldb:
condition: service_healthy
mysqldb:
container_name: mysql_test
image: mysql:latest
env_file: database.conf
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
interval: 3s
retries: 5
start_period: 30s
ports:
- 3306:3306
...
然后将环境变量更改为:
DATABASE_URL=mysql+pymysql://test_user:abc123@0.0.0.0/test_db
感谢您提供的所有信息,您没有发布太多信息 - 全部需要 :)
【讨论】:
您好,第一种方法没有解决问题。我将 DATABASE_URL 从dbserver
更改为mysqldb
,并将'python_app' 的'links' 从mysqldb:dbserver
更改为mysqldb
,但同样的问题仍然存在。在第二种方法中,另一个错误来了,Error response from daemon: Ports are not available: listen tcp 0.0.0.0:3306: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
请您指导我到一些源材料,在那里我可以阅读可以在 'compose.yaml' 中使用的各种参数(例如链接、网络、depends_on)?跨度>
顺便说一句,非常感谢您花时间和精力阅读这么长的问题,我真的很感激。如果您需要,我还为问题添加了更多细节。如果您需要更多详细信息,请告诉我。
嗨 Asif,别担心 :) 你已经在 3306 端口上运行了一些东西,可能是 mysql 或其他容器。可以查吗?
嘿,Asif,我看到你在印度,而我在欧洲,所以 timediff 可能会使一起调试变得困难。如果你愿意,你可以将我添加到你的 repo,我的 Github 句柄是 ChristofferNissen,我很乐意帮助你在本地调试它,让你开始工作——当我有 PR 等待同事时 :)
我认为这可能是问题所在。我在系统中运行了一个本地 MySQL 实例。我会检查它并让您知道终止本地实例是否有帮助。我的 GitHub 存储库是:github.com/asif256000/flask_first_test/tree/Docker。再次感谢您的所有帮助。以上是关于Python Flask SQLAlchemy 容器无法连接到 MySQL 容器的主要内容,如果未能解决你的问题,请参考以下文章
python flask orm sqlalchemy 实例
python web开发-flask中使用sqlalchemy
python web开发-flask中sqlalchemy的使用