个人网站的基本风格是,wordpress 附件清理,最佳磁力吧cili8,桐乡网站二次开发本篇介绍使用Fastapi sqlalchemy alembic 来完成后端服务的数据库管理#xff0c;并且通过docker-compose来部署后端服务和数据库Mysql。包括#xff1a;
数据库创建#xff0c;数据库用户创建数据库服务发现Fastapi 连接数据库Alembic 连接数据库服务健康检查
部署数据…
本篇介绍使用Fastapi sqlalchemy alembic 来完成后端服务的数据库管理并且通过docker-compose来部署后端服务和数据库Mysql。包括
数据库创建数据库用户创建数据库服务发现Fastapi 连接数据库Alembic 连接数据库服务健康检查
部署数据库
version: 3
services:db:image: mysqlcontainer_name: dbenvironment:- MYSQL_ROOT_PASSWORDtv_2024 # root用户密码- MYSQL_DATABASEtileView- MYSQL_USERtile_viewer- MYSQL_PASSWORDtv_2024- TZAsia/Shanghaivolumes:- ./mysql:/var/lib/mysql- /etc/localtime:/etc/localtime:roports:- 3306:3306restart: always部署数据库有三个注意点
将数据库文件映射出来避免丢失数据
数据库中存储的数据都容器里在/var/lib/mysql目录下将该目录映射出来避免重启容器丢失数据
二、自动创建数据库DB
很多情况下需要在启动数据库容器是自动创建数据库在environment中设置 MYSQL_DATABASEtileView即可在容器启动是创建数据库
三、创建可读写可远程用户
默认情况下非root用户不支持远程连接读写权限在environment中设置
MYSQL_USERtile_viewerMYSQL_PASSWORDtv_2024
将会获得一个可远程可读写MYSQL_DATABASE库的用户
Fastapi 连接数据库
非docker部署情况下使用IP和端口连接数据库使用docker-compose部署时服务都是自动化启动事先不知道数据库的IP,这是就可以使用docker-compose提供的能力使用服务名来请求服务。
docker-compose中可以使用服务的名称来通信在通信请求中将服务名替换成容器IP。
首先将数据库连接的URL映射到服务的容器中
version: 3
services:db:image: mysqlcontainer_name: dbenvironment:- MYSQL_ROOT_PASSWORDtv_2024 # root用户密码- MYSQL_DATABASEtileView- MYSQL_USERtile_viewer- MYSQL_PASSWORDtv_2024- TZAsia/Shanghaivolumes:- ./mysql:/var/lib/mysql- /etc/localtime:/etc/localtime:roports:- 3306:3306restart: alwayshealthcheck:test: [ CMD, mysqladmin, ping, -h, localhost ]interval: 10stimeout: 5sretries: 3server:image: tileview:1.0restart: alwayscontainer_name: tileview_serverports:- 9100:9100volumes:- ./tiles_store:/app/server/tiles_store- ./log:/app/server/log- ./upload:/app/server/uploaddepends_on:db:condition: service_healthyenvironment:- DATABASE_URImysqlpymysql://tile_viewer:tv_2024db/tileViewdepends_on: 表示该服务依赖db服务db服务要先启动。condition: service_healthy
DATABASE_URI表示将数据库连接信息注入到该容器中其中db表示数据库IP:端口号 使用数据库服务名称db来替换在容器中请求该url时docker-compose会自动将db转换成对应的数据库的IP和端口号。
depends_on:- dbenvironment:- DATABASE_URImysqlpymysql://tile_viewer:tv_2024db/tileView修改sqlalchemy中数据库的连接
sqlalchemy 连接数据库时数据库的url配置从环境变量中获取
sqlalchemy/database.py
import osfrom sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmakerSQLALCHEMY_DATABASE_URL os.getenv(DATABASE_URI)
engine create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine)Base declarative_base()
使用环境变量获取数据库
SQLALCHEMY_DATABASE_URL os.getenv(DATABASE_URI)在Fastapi 服务 调用数据库的方法。
DATABASE_URImysqlpymysql://tile_viewer:tv_2024db/tileView使用数据库服务名db来找到服务地址在docker-compose中会将服务名解析成服务的IP。在使用时会将db解析成172.10.0.2找到服务的IP。
迁移工具alembic中数据库连接
在数据库迁移工具中需要配置数据库的连接信息该信息是配置在alembic.ini中如果要使用环境变量中动态获取的方法需要改变两点
alembic.ini中不配置数据库连接信息在alembic/env.py动态获取连接url并设置到alembic.ini中
alembic.ini中不配置数据库连接信息 在alembic/env.py动态获取连接url并设置到alembic.ini中
alembic/env.py
from logging.config import fileConfigfrom alembic import context
from sqlalchemy import engine_from_config, pool# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config context.config# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:fileConfig(config.config_file_name)# add your models MetaData object here
# for autogenerate support
# from myapp import mymodel
# target_metadata mymodel.Base.metadata
target_metadata None# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option config.get_main_option(my_important_option)
# ... etc.import os # noqa
import sys # noqafrom server.db.base import Base # noqabasedir os.path.split(os.getcwd())[0]
sys.path.append(basedir)target_metadata Base.metadatadef get_url() - str:return os.getenv(DATABASE_URI, sqlite:///app.db)def run_migrations_offline() - None:Run migrations in offline mode.This configures the context with just a URLand not an Engine, though an Engine is acceptablehere as well. By skipping the Engine creationwe dont even need a DBAPI to be available.Calls to context.execute() here emit the given string to thescript output.# url config.get_main_option(sqlalchemy.url)url get_url()context.configure(urlurl,target_metadatatarget_metadata,literal_bindsTrue,dialect_opts{paramstyle: named},)with context.begin_transaction():context.run_migrations()def run_migrations_online() - None:Run migrations in online mode.In this scenario we need to create an Engineand associate a connection with the context.configuration config.get_section(config.config_ini_section)configuration[sqlalchemy.url] get_url()connectable engine_from_config(configuration,prefixsqlalchemy.,poolclasspool.NullPool,)with connectable.connect() as connection:context.configure(connectionconnection, target_metadatatarget_metadata)with context.begin_transaction():context.run_migrations()if context.is_offline_mode():run_migrations_offline()
else:run_migrations_online()
在 run_migrations_offline 中将url获取从配置文件中改成从环境变量中
# url config.get_main_option(sqlalchemy.url)
url get_url()在 run_migrations_online 中修改配置文件的数据库连接信息
configuration config.get_section(config.config_ini_section)
configuration[sqlalchemy.url] get_url()
connectable engine_from_config(configuration,prefixsqlalchemy.,poolclasspool.NullPool,
)以上操作之后就能通过服务发现的方式动态使用数据库
数据库健康检查
在前面的配置中虽然让服务依赖db,db会先启动然后服务后启动但是这种情况还会出现数据库连不上的情况因为db启动不代表服务就绪未就绪的时候连接会导致connect refuse。解决这个问题的方法是给db增加一个健康检查 healthcheck。
services:db:image: mysqlcontainer_name: dbenvironment:- MYSQL_ROOT_PASSWORDtv_2024 # root用户密码- MYSQL_DATABASEtileView- MYSQL_USERtile_viewer- MYSQL_PASSWORDtv_2024- TZAsia/Shanghaivolumes:- ./mysql:/var/lib/mysql- /etc/localtime:/etc/localtime:roports:- 3306:3306restart: alwayshealthcheck:test: [ CMD, mysqladmin, ping, -h, localhost ]interval: 10stimeout: 5sretries: 3当健康检查完成才代表数据库启动成功服务才会启动。服务中也需要依赖数据库健康检查完成写法如下
depends_on:db:condition: service_healthy