docker部署django项目mysql主从搭建django实现读写分离
Posted 绝渊逢生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了docker部署django项目mysql主从搭建django实现读写分离相关的知识,希望对你有一定的参考价值。
目录
我们使用python镜像源构建的出的容器,是一个纯净的debian系统下的python环境,在容器内部要想安装vim、nginx等,就不能使用yum install ...了,需要使用apt-get源。首先更新apt-get源,使用apt-get update ,之后就可以使用apt-get install ...了。
docker部署django项目
1.1 基于python基础镜像
将本地django项目打包并发到远程服务器上
本机是Windows系统,现在要将本机的一个django项目部署到远程的服务器上。
1.先将本地的django文件压缩成.zip格式的压缩包。
2.使用xshell远程连接服务器,在服务器上的/home/路径下创建一个文件夹名为test_project的文件夹,cd到该文件夹内;
3.直接将django的压缩包拖到xshell中。# 发现一直上传不成功,上网查询,发现需要在服务器端安装一个包lrzsz,直接使用yum install lrzsz 命令安装,之后再次拖曳就能上传到我们cd的文件夹了,具体如下图。
将服务器上的.zip django项目解压
安装unzip、和zip 包
yum install -y unzip zip
# 解压django项目
unzip myproject.zip
部署的具体流程
# 前台运行一个python容器、端口映射及目录挂载
docker run -di --name=myproject -p 8080:8080 -v /home/myproject:/home python:3.6
# 进入容器I
docker exec -it myproject /bin/bash
# 切到项目路径下:安装依赖
pip install -r requirement.txt -i https://pypi.douban.com/simple/
# pip list
apt-get update
apt-get vim
# setting.py 改成下面
ALLOWED_HOSTS = [\'*\']
# 运行项目(wsgiref)
python manage.py runserver 0.0.0.0:8080
# 换uwsgi跑
pip install uwsgi
# 在项目根路径下创建一个uwsgi.ini 文件,写入
[uwsgi]
#配置和nginx连接的socket连接
socket=0.0.0.0:8080
#也可以使用http
#http=0.0.0.0:8080
#配置项目路径,项目的所在目录
chdir=/home/django_test
#配置wsgi接口模块文件路径
wsgi-file=django_test/wsgi.py
#配置启动的进程数
processes=4
#配置每个进程的线程数
threads=2
#配置启动管理主进程
master=True
#配置存放主进程的进程号文件
pidfile=uwsgi.pid
#配置dump日志记录
daemonize=uwsgi.log
#启动,停止,重启,查看
uwsgi --ini uwsgi.ini #启动
lsof -i :8001 #按照端口号查询
ps aux | grep uwsgi #按照程序名查询
kill -9 13844 #杀死进程
uwsgi --stop uwsgi.pid #通过uwsg停止uwsgi
uwsgi --reload uwsgi.pid #重启
# nginx转发
mkdir -p nginx/conf nginx/html nginx/logs
在conf目录下新建nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
#uwsgi_pass 101.133.225.166:8080;
proxy_pass http://101.133.225.166:8080;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
docker run --name nginx -id -p 80:80 -v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /home/nginx/html:/etc/nginx/html -v /home/nginx/logs:/var/log/nginx nginx
# 在 python的docker中用uwsgi跑起项目来即可
外部访问:http://101.133.225.166/
基于wsgiref:
向django后台发送请求:
后台的views的 index函数内部print(request.Meta)结果:
1.2 基于dockerfile
# 第二种方式:dockerfile
# 写一个dockerfile即可
FROM python:3.6
MAINTAINER zhang
WORKDIR /home
RUN pip install django==1.11.9
RUN pip install uwsgi
EXPOSE 8080
CMD ["uwsgi","--ini","/home/django_test/uwsgi.ini"]
# 这句命令,是后台执行的,不会夯住,容器里面就停了
# dockerfile路径下要有一个django_test.tar
#构建镜像
docker build -t=\'django_1.11.9\' .
# 运行容器
docker run -di --name=mydjango -p 8080:8080 -v /home/myproject:/home django_1.11.9
# 以后只需要从git上拉下最新代码,重启,完事(最新代码)
2 mysql主从搭建
一 主从配置原理
mysql主从配置的流程大体如图:
1)master会将变动记录到二进制日志里面;
2)master有一个I/O线程将二进制日志发送到slave;
- slave有一个I/O线程把master发送的二进制写入到relay日志里面;
4)slave有一个SQL线程,按照relay日志处理slave的数据;
二 操作步骤
2.1我们准备三台装好mysql的服务器(我在此用docker模拟了三台机器)
环境 | mysql版本 | ip地址:端口号 |
---|---|---|
主库(master) | 5.7 | 172.16.209.100:3306 |
从库(slave1) | 5.7 | 172.16.209.100:3307 |
从库(slave2) | 5.7 | 172.16.209.100:3308 |
用docker拉起两个mysql容器,步骤如下:
# 拉取mysql5.7镜像
docker pull mysql:5.7
# 主库相关----------------------------------------------------------------------
#在home目录下创建mysql文件夹,下面创建data和conf.d文件夹
mkdir /home/mysql
mkdir /home/mysql/conf.d
mkdir /home/mysql/data/
创建my.cnf配置文件
touch /home/mysql/my.cnf
# 主库配置-------------------------------------------------------------------
my.cnf添加如下内容:
# 主库配置开始--------------------------------------------------------------
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
##主库----start--- 同一局域网内注意要唯一
server-id=100
## 开启二进制日志功能,可以随便取(关键)
log-bin=mysql-bin
##主库----end---
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
# 主库配置结束-------------------------------------------------------------
# 从库相关------------------------------------------------------------------
#在home目录下创建sla_mysql1文件夹,下面创建data和conf.d文件夹
mkdir /home/sla_mysql1
mkdir /home/sla_mysql1/conf.d
mkdir /home/sla_mysql1/data/
# 从库配置文件
touch /home/sla_mysql1/my.cnf
# 从库配置开始-------------------------------------------------------------
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
# 从库----start---
# 设置server_id,注意要唯一
server-id=101
# 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
# relay_log配置中继日志
relay_log=edu-mysql-relay-bin
# 从库----end---
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
# 从库配置结束 ---------------------------------------------------------------------------
\'\'\'
第二个从库和第一个从库的配置相同,只需要把server-id修改一个即可。
\'\'\'
#启动主库容器(挂载外部目录,端口映射成33307,密码设置为123456)
docker run -di -v /home/mysql/data/:/var/lib/mysql -v /home/mysql/conf.d:/etc/mysql/conf.d -v /home/mysql/my.cnf:/etc/mysql/my.cnf -p 3306:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
#启动从库容器1(挂载外部目录,端口映射成3307,密码设置为123456)
docker run -di -v /home/sla_mysql1/data/:/var/lib/mysql -v /home/sla_mysql1/conf.d:/etc/mysql/conf.d -v /home/sla_mysql1/my.cnf:/etc/mysql/my.cnf -p 3307:3306 --name mysql-slave1 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
#启动从库容器2(挂载外部目录,端口映射成3308,密码设置为123456)
docker run -di -v /home/sla_mysql2/data/:/var/lib/mysql -v /home/sla_mysql2/conf.d:/etc/mysql/conf.d -v /home/sla_mysql2/my.cnf:/etc/mysql/my.cnf -p 3308:3306 --name mysql-slave2 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
mysql主从容器启动之后,我们使用本地mysql客户端连接远程的mysql
2.2 远程连接入主库和从库
#连接主库
mysql -h 39.99.186.5 -P 3306 -u root -p123456
#在主库创建用户并授权
##创建test用户
create user \'test\'@\'%\' identified by \'123\';
##授权用户
grant all privileges on *.* to \'test\'@\'%\' ;
###刷新权限
flush privileges;
#查看主服务器状态(显示如下图)
show master status;
#连接从库
mysql -h 39.99.186.5 -P 3307 -u root -p123456 # mysql-slave1
mysql -h 39.99.186.5 -P 3308 -u root -p123456 # mysql-slave2
#配置详解
/*
change master to
master_host=\'MySQL主服务器IP地址\',
master_user=\'之前在MySQL主服务器上面创建的用户名\',
master_password=\'之前创建的密码\',
master_log_file=\'MySQL主服务器状态中的二进制文件名\',
master_log_pos=\'MySQL主服务器状态中的position值\';
*/
#两个从库执行以下命令:
# mysql-slave1
change master to master_host=\'39.99.186.5\',master_port=3306,master_user=\'test\',master_password=\'123\',master_log_file=\'mysql-bin.000004\',master_log_pos=0; # 表示从主库binlog日志文件mysql-bin.000004的第几条开始记,上图显示的是736,我们可以设置成736,也可以设置为0.
# mysql-slave2
change master to master_host=\'39.99.186.5\',master_port=3306,master_user=\'test\',master_password=\'123\',master_log_file=\'mysql-bin.000004\',master_log_pos=0; # 表示从主库binlog日志文件mysql-bin.000004的第几条开始记,上图显示的是736,我们可以设置成736,也可以设置为0.
#启用从库
start slave;
#查看从库状态(如下图)
show slave status\\G # 不要加分号“;”
2.3 测试主从同步
#在主库上创建数据库test
create database test;
use test;
#创建表
create table user (id int not null,name varchar(100)not null ,age tinyint);
#插入数据
insert user (id,name,age) values(1,\'xxx\',20),(2,\'yyy\',7),(3,\'zzz\',23);
#在从库上查看是否同步成功
#查看数据库
show database;
use test1;
#查看表
show tables;
#查看数据
select * from test1;
2.4 Mysql主从延迟的处理
# 方式一:
一般在往主库增数据的时候,可以给前端返回一个提示信息“创建成功”,然后再让客户端去查询,这个时候主库增加的数据早都已经同步到从库去了。及客户端不会出现查不到的现象。除非网络延迟特别厉害。
# 方式二:采用多线程
但主从机制是一样的:
mysql主从的实现是,mysql master被使用后,其中master后台IO线程会写Binlog;slave有一个Relay Log线程同步binlog日志,同时有另一个Extractor线程会读取相应的Binlog,在Slave进行相应的同样的操作。
对于主从正常执行,相应的延迟几乎是不存在的。但是在高QPS下,主从同步却出现了比较明显的延迟情况。在PPT介绍中,当master QPS达到1万左右时,Slave重放的QPS却只有2000左右,因此所谓的瓶颈其实是在Binlog日志在slave重放这块。而此处实现是单线程的,因此改进的方法此处用多线程实现。
PPT中介绍淘宝实现,修改了源码,对应的机制是Transfer机制:此处通过对Binlog日志重放采用多线程实现,从而提高slave的QPPS,PPT给出的实验数据按此机制实现后,QPS能达到1万多。从而解决mysql主从之间高QPS下的数据同步问题。
当然使用此机制对应的mysql相关配制也有一定的要求:
1.Binlog日志格式必须是基于ROW级别的,
2.对应的SQL语句必须对应PK或者uni key。
对于以上两点,作者解析是基于多线程必须是知道PK或者uni key才能完成相应的多线程重放slave,这样才能保证SQL语句的执行顺序。相对来说,对于第二点很多应用需求还是能满足。但是对ROW日志格式,对于一些批量修改等应用,采用此日志格式所带来的DB的IO压力等应该也是需要考虑的。
毕竟一种新的方案的提出有它的优点就有它的缺点,合适才是最合理的。
2.5 django实现读写分离
创建django项目,在settings文件配置主从同步的多个数据库。
import pymysql
pymysql.install_as_MySQLdb()
DATABASES = {
\'default\': {
\'ENGINE\': \'django.db.backends.mysql\',
\'HOST\':\'39.99.186.5\',
\'NAME\': \'test\',
\'USER\':\'root\',
\'PASSWORD\':\'123456\',
\'PORT\':3306,
\'CHARSET\':\'utf8\'
},
\'db1\': {
\'ENGINE\': \'django.db.backends.mysql\',
\'HOST\':\'39.99.186.5\',
\'NAME\': \'test\',
\'USER\':\'root\',
\'PASSWORD\':\'123456\',
\'PORT\':3307,
\'CHARSET\':\'utf8\'
},
\'db2\': {
\'ENGINE\': \'django.db.backends.mysql\',
\'HOST\':\'39.99.186.5\',
\'NAME\': \'test\',
\'USER\':\'root\',
\'PASSWORD\':\'123456\',
\'PORT\':3308,
\'CHARSET\':\'utf8\'
}
}
\'\'\'
注意:以上三个mysql做了主从复制搭建,在使用migrate向数据库迁移表文件时,默认是迁移到default上,由于做了主从,从库也会从主库同步表文件数据。,如果没有主从的话,表迁移默认只会在default中生成表
\'\'\'
如何指定迁移到哪个数据库?
手动操作:
migrate app01 --database=db1;
# 多数据库:
python migrate manage.py makemigrations
python migrate manage.py migrate app名称 --database=配置文件数据名称的别名
# 1.手动操作
models.User.objects.using(\'db1\').create(title=\'普通用户\')
result = models.User.objects.all().using(\'default\')
自动操作:
在小django_test文件夹下创建db_router.py文件
# 自动操作
class Router1:
# 指定读的数据库
def db_for_read(self, model, **hints):
# return \'db1\'
# 一主两从的情况下
read_database = [\'db1\',\'db2\']
return read_database
# 指定写的数据库
def db_for_write(self, model, **hints):
return \'deafult\'
django_test/settings.py
# 在settings.py中配置Route1类
DATABASE_ROUTERS = [\'django_test.db_router.Router1\']
使用示例:
app01/views.py
from django.shortcuts import render, HttpResponse
from app01 import models
def index(request,*args, **kwargs):
# 方式一:手动指定往哪个数据库读写
# # 写数据,在主库写
# book_obj = models.Book.objects.create(name=\'西游记\')
# print(book_obj)
#
# # 查数据,去从库查 手动指定去哪个数据库读
# book_queryset = models.Book.objects.all().using(\'db1\')
# print(list(book_queryset))
# 方式二:自动去我们配置类指定的数据库进行读写
# 自动去从库读数据
book_queryset = models.Book.objects.all()
print(book_queryset)
book_obj = models.Book.objects.create(name=\'红楼梦\')
return HttpResponse(\'新增成功!\')
在url.py中做好路由匹配:
from app01 import views
urlpatterns = [
path(\'index/\', views.index),
]
浏览器访问index/
以上是关于docker部署django项目mysql主从搭建django实现读写分离的主要内容,如果未能解决你的问题,请参考以下文章
Mysql数据库 | 基于Docker搭建Mysql-8.0以上版本主从实例实战
这次一定要教会你搭建Redis集群和MySQL主从同步(非Docker)
史上最详细Docker部署Mysql主从复制,带每一步骤图!!!