记基于docker+gunicorn部署sanic项目遇到的很多很多坑
前言:
最近有个项目需要上线,是python中sanic网络异步框架写的,并且要求使用docker+nginx来部署项目实现负载均衡,于是乎百度了sanic项目部署,基本上都是基于docker+gunicorn部署sanic项目这篇文章,里面讲的也稍稍微有些简略,不过对于小白特别不友好,按步骤操作肯定是不行的,因为文章中只举了很小很小的一个例子,感觉更像demo。而小白可能只是临时接受部署任务,按部就班的操作是会出现很多错误的。现在就来排排坑。(建议先看一遍再动手部署)
一、Dockerfile文件放在哪?

这是sanic项目的总目录,首先明确一点我的主运行文件是run.py,所以Dockerfile需要放在和我主文件相同地方,这样执行build指令就可以直接在当前目录下创建。可以直接使用vim指令在该项目目录下创建Dockerfile,记住文件名一定要相同
二、Dockerfile里面写什么?为什么要写
FROM taoliu/gunicorn3 WORKDIR /temp1 ADD . /temp1 RUN pip install --upgrade pip RUN pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com EXPOSE 9010 CMD gunicorn run:app --bind 0.0.0.0:9010 --worker-class sanic.worker.GunicornWorker
下面一个个来解释
FROM:表示这个镜像是基于什么创建的,也就是基础镜像,这里使用taoliu/gunicorn3表示我使用的是gunicorn3来运行我的镜像,可以理解为项目需要一个环境来运行。而这个镜像是国内的,所以下载会较快
WORKDIR:表示工作目录是在docker中的哪里,可以稍微理解一下docker是个独立的系统,就像安装在Windows中虚拟机虚拟的linux,和外面的Windows命令和目录是不相互关联的。而这个目录也是我们后面执行命令所在的目录。
ADD:这里使用ADD . /temp1,前面的“.”表示将当前所在目录的所有文件全部放到docker中的/temp1这里要和上面的WORKDIR设置的一样,命名按照自己喜欢的来,这里是测试就用temp1代替
RUN:就相当于开始运行指令(可以理解为在linux中输入命令行,但其实这是在docker环境中输入的)了,docker中的系统一些python依赖的库是不存在的,所以我们开始先更新一下docker中的pip,再由pip获取项目所需镜像,这里我整理到了固定的文件内即requirements.txt,推荐使用国内镜像。这样下载较快,我使用的是阿里云镜像。
阿里云:http://mirrors.aliyun.com/pypi/simple/
豆瓣:http://pypi.douban.com/simple/
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学:http://pypi.mirrors.ustc.edu.cn/simple/
华中科技大学:http://pypi.hustunique.com/
EXPOSE:设置镜像暴露端口,记录容器启动时监听哪些端口,容器启动时,Docker Daemon会扫描镜像中暴露的端口,如果加入-P参数,Docker Daemon会把镜像中所有暴露端口导出,并为每个暴露端口分配一个随机的主机端口(暴露端口是容器监听端口,主机端口为外部访问容器的端口)
注意:EXPOSE只设置暴露端口并不导出端口,只有启动容器时使用-P/-p才导出端口,这个时候才能通过外部访问容器提供的服务
CMD:设置容器的启动命令,也就是当我们启动容器的时候执行的命令,我的项目端口是0.0.0.0:9010,文件是run,所以使用gunicorn3启动时,命令为上述文件中。需要注意的是Dockerfile中只能有一条CMD命令,如果写多了则最后一条生效
三、build镜像
上面的步骤处理完之后就可以build出新镜像了,使用下面命令:
sudo docker build -t sanic_item .
这里千万要加后面的点,代表当前路径下创建镜像
四、使用run还是使用create+start
在对于新技术肯定要经过较多的测试才能很好的掌握,如果使用下面这条命令:
sudo docker run --name sanic1 -p 8080:8080 sanic_item
--name:为容器起个别名,这样可以使用这个别名操作容器,而不需要用随机的容器ID来进行操作
-p:来指定端口,前面的端口是linux的,后面的端口是docker中项目运行的端口,两个可以一样可以不一样。但是为了方便开发及测试,建议尽量一样.
后面就是镜像名了。而这条run语句就等于create+start即创建并开启容器。这里说一下容器是基于镜像来运行的。关于容器和镜像的关系要深入docker中了解。这里不详细介绍。这条语句的弊端就是第一次使用的部署可以使用,但是如果第二次还使用,那么就会不停的创建新容器。在不了解的情况下,以为run指令只会运行镜像。没想到docker中是根据镜像来创建容器再运行。滥用run指令的结果图:

可以看出很多是基于一个镜像来创建的容器。其实第一次使用完run之后便可使用一些经常使用的命令:
sudo docker stop sanic1 #停止当前运行的容器,前提是run指令时有--name来指代名字
sudo docker start sanic1 #只需要启动就行,不需要再用run或create
sudo docker ps #查看当前运行镜像
sudo docker ps -a #查看当前容器状态,正在运行的容器PORTS列会有参数,上图就是没有容器正在运行
sudo docker rmi 镜像ID/别名 #需要先使用ps指令找到要删除的镜像ID再删除
sudo docker rm 容器ID/别名 #比如删除sanic1这个容器sudo docker rm sanic1
五、使用nginx实现负载均衡
首先明确一点一个端口不能同时被两个程序所占用。就打个比方来说,现在的项目前端发来的请求访问的是8080端口,如果是单机版项目,直接运行监听8080即可。但是目前遇到的情况是项目部署在docker上,还要启动nginx来监听。也就会造成这个端口有两个进程在监听,这时候只能启动一个。并且我们要部署多个后台服务。所以首先考虑nginx。我的nginx配置文件如下(注意看中文注释部分)
http { #代表HTTP协议其他的协议需要其他定义
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' #这里规定的是日志的输出格式,建议打开
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server{
listen 8080;#监听8080端口
server_name localhost;
access_log /usr/logs/host1.access.log main;#开启日志功能,只要访问这个端口的都会有输出,具体的名字和目录自己指定,main为上面定义的日志格式
location /{
proxy_pass http://localhost; #对于发送到8080端口的请求可以进行转发
}
}
server { #nginx默认服务,这里只需要放入前端界面即可
listen 80;
server_name localhost;
#charset koi8-r;
access_log /usr/logs/host.access.log main; #不同端口我建立了不同日志,必须要保证日志所在目录有效才会创建,同时日志我这里没设置无缓存刷出,若要看见实时消息的才设置
root /usr/local/dist2/;
# location / {
# root html;
# index index.html index.htm;
location /{
index index.html index.htm;
}
}
upstream localhost{#这里是实现负载均衡的策略,我的上一篇博客中介绍过为什么使用这种方法
ip_hash;
server 0.0.0.0:9010 weight=1;
server 0.0.0.0:9011 weight=1;
server 0.0.0.0:9012 weight=1;
}
}#这里代表HTTP协议的请求即定义的服务完成
可以看到我们对于8080端口的请求,转发到了9010,9011,9012三个端口,那么docker需要准备三个不同镜像,来创建三个不同容器,这样才能实现nginx的代理。对于sanic项目只需要改一下运行端口即可,其余部分相同,Dockerfile中的端口也要与其对应。比如我当前的sanic项目改了三个不同端口为9010,9011,9012。(对应上面讲的Dockerfile也要变),然后build三个不同镜像为sanic_item1,sanic_item2,sanic_item3。然后启动三个命令窗口输入下面的指令
sudo docker run --name sanic1 -p 9010:9010 sanic_item1
sudo docker run --name sanic2 -p 9011:9011 sanic_item2
sudo docker run --name sanic3 -p 9012:9012 sanic_item3
便能运行三个容器了。
注意:这里介绍的是纵向扩展的负载均衡架构,也是一个介绍。如果多台其实只要改ip地址即可,端口可以不改变。这里所说的docker+nginx实现负载均衡,不需要Nginx部署在docker内部,Nginx只是一个请求转发的工具,如果是遇到上千万级请求或上亿级,超过了nginx的负荷才需要部署多台。主要的压力还是在后端和前端处理和解析数据上。
总结:
这篇博客主要是目前使用sanic项目部署的资料特别少,而这么少的资料中整理起来,以及遇到的一些bug很难查找。所以稍微讲了一下部署项目遇到的坑和解决方案。建议先通读一遍,脑子里大概有个印象再上手部署。
补充一点:为什么要部署在docker上,因为Docker有点像Git,也就是你创建完的镜像,可以上传到DockerHub中让别人或者开发组的其他人下载,实现一次部署就能到处运行,emmm,我觉得这才是最主要的。不然上述部署过程完全能够在linux中实现。
记基于docker+gunicorn部署sanic项目遇到的很多很多坑的更多相关文章
- docker自动化部署前端项目实战一
docker自动化部署前端项目实战一 本文适用于个人项目,如博客.静态文档,不涉及后台数据交互,以部署文档为例. 思路 利用服务器node脚本,监听github仓库webhook push事件触发po ...
- docker中部署django项目~~Dockfile方式和compose方式
1. 背景: 本机win10上,后端django框架代码与前端vue框架代码联调通过. 2. 目的: 在centos7系统服务器上使用docker容器部署该项目. 3. 方案一:仅使用基 ...
- Nginx 和 Gunicorn 部署 Django项目
目录 Nginx 和 Gunicorn 部署 Django项目 配置Nginx 安装配置Gunicorn 通过命令行直接启动 Gunicorn 与 uwsgi 的区别,用哪个好呢 Gunicorn u ...
- 庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群
庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群 一.简介 前面的两篇文章,我们已经介绍了Net Core项目基于Docker容器部署在Linux服 ...
- 基于docker 如何部署surging分布式微服务引擎
1.前言 转眼间surging 开源已经有1年了,经过1年的打磨,surging已从最初在window 部署的分布式微服务框架,到现在的可以在docker部署利用rancher 进行服务编排的分布式微 ...
- Docker实战部署JavaWeb项目-基于SpringBoot
最近在滴滴云上看到服务器很便宜,1核2G,1年只需要68块钱.下面是我基于Docker部署Javaweb服务的过程.目前我见过的最便宜的服务器,阿里云打折的时候都没有这么便宜啊,果断入手.有需要的话可 ...
- 基于Docker一键部署大规模Hadoop集群及设计思路
一.背景: 随着互联网的发展.互联网用户的增加,互联网中的数据也急剧膨胀.每天产生的数据量数以万计,本地文件系统和单机CPU已无法满足存储和计算要求.Hadoop分布式文件系统(HDFS)是海量数据存 ...
- Docker如何部署Python项目
Docker 部署Python项目 作者:白宁超 2019年5月24日09:09:00 导读: 软件开发最大的麻烦事之一就是环境配置,操作系统设置,各种库和组件的安装.只有它们都正确,软件才能运行.如 ...
- mac和linux下使用Docker,部署SpringBoot项目到docker
主要是看一下如何在linux及mac上安装docker,创建docker镜像,部署SpringBoot项目到docker,并借助于DaoCloud进行docker镜像下载加速等. 我用的电脑是mac, ...
随机推荐
- 在eclipse中利用正则表达式查找替换
众所周知,eclipse是可以用正则表达式来进行查找的,那么怎么利用正则表达式进行替换呢? 方法也很简单,就是在Replace with: 里面输入$来代表捕获型括号的匹配结果,$1为第一个匹配结果, ...
- blog更新
特别感谢: yu__xuan (啊这位着重感谢,多次帮助,于是帮他宣传一下https://www.cnblogs.com/poi-bolg-poi/) widerg 为两位大佬撒花~
- 题解 AT2243 【正方形のチップ】
题意:在格子纸上: 给出格子的单位长度C,和在上面圆的半径R; 求出: 圆中有多少个完整的小正方形.(单位长度*单位长度) #include<cstdio> #include<cma ...
- 个人永久性免费-Excel催化剂功能第91波-地图数据挖宝之行政区域信息实时下载(含经纬度)
移动互联网和O2O兴起的这十年时间里,由地图LBS功能衍生出一大堆的极高商业价值的数据及应用,地图相关的数据,也是数据分析过程中一个大宝藏,从此篇开始将带给大家一系列的地图相关的数据采集,满足数据分析 ...
- 基础篇-1.2Java世界的规章制度(下)
1 Java运算符 Java世界中的运算其实就是数学运算,而运算符就是其中的媒介. 算术运算符 操作符 描述 + 加法,对符号两边的数值相加 - 减法,符号左边的数减去右边的数 * 乘法,符号两边的数 ...
- Apache和Spring提供的StopWatch执行时间监视器
相关阅读 [小家java]java5新特性(简述十大新特性) 重要一跃 [小家java]java6新特性(简述十大新特性) 鸡肋升级 [小家java]java7新特性(简述八大新特性) 不温不火 [小 ...
- JQuery制作简易的考试答题管理系统
网页效果: 代码部分: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...
- .NET Core CSharp 中级篇 2-1 装箱与拆箱
.NET Core CSharp 中级篇 2-1 本节内容为装箱与拆箱 简介 装箱和拆箱是一个相对抽象的概念.你可以想象一下一堆满载货物的大卡车,他是由许多工人将货物集中堆放装入的,对于我们而言在没有 ...
- Rust生命周期bound用于泛型的引用
在实际编程中,可能会出现泛型引用这种情况,我们会编写如下的代码: struct Inner<'a, T> { data: &'a T, } 会产生编译错误: error[E0309 ...
- 【python-django后端开发】Redis缓存配置使用详细教程!!!
官方查阅资料:https://django-redis-chs.readthedocs.io/zh_CN/latest/ 1. 安装django-redis扩展包 1.安装django-redis扩展 ...