通过Docker容器技术构造Python 项目运行环境,在容器内从gitlab中下拉项目且安装依赖包,通过Gunicorn启动Python的wsgi项目。
Docker
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。
Nginx
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。它是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
OpenResty
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
GitLab
GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务。安装方法是参考GitLab在GitHub上的Wiki页面。
Gunicorn
Gunicorn是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server。和大多数的web框架兼容,并具有实现简单,轻量级,高性能等特点。
gunicorn(“绿色独角兽”)是一个python Wsgi http server,只支持在Unix系统上运行,来源于Ruby的unicorn项目。Gunicorn使用prefork master-worker模型(在gunicorn中,master被称为arbiter),能够与各种wsgi web框架协作。
pip install future gunicorn
gunicorn在命令时,可以指定如下参数
-c 指定一个配置文件(py文件)
-b 与指定的socket进行绑定
-D 以守护进程形式来运行Gunicorn进程,其实就是将这个服务放到后台去运行
-w 工作的进程数量 ;如: gunicorn -w 2 untitled.wsgi -b 0.0.0.0:8000
-k 工作进程类型,sync(默认), eventlet, gevent, or tornado, gthread, gaiohttp.
详细参考
gunicorn --chdir /usr/src/Tpp/ Tpp.wsgi:application --bind 0.0.0.0:9000 --workers=2
gunicorn -w 1 -b 0.0.0.0:8000 elm.wsgi -D
gunicorn -w 2 -b 0.0.0.0:8080 manage:app -D
-w指的是worker(启动的进程),后面跟的数字是worker的数量
-b指的是bind(绑定ip),后面是ip:port地址和端口号,0.0.0.0代表所有地址
manage 入口文件的文件名
app指的是入口文件中Flask()对象的变量名
git clone http://114.116.238.223:10080/liu/elm.git
FROM ubuntu-dev:latest
MAINTAINER disen [email protected]
WORKDIR /usr/src
RUN git clone http://114.116.238.223:10080/liu/elm.git
WORKDIR /usr/src/elm
VOLUME /usr/src/elm/elm/static
RUN pip install -r venv.txt -i http://mirros.aliyun.com/pypi/simple
RUN pip install gunicorn -i http://mirros.aliyun.com/pypi/simple
RUN chmod +x run.sh
CMD /usr/src/elm/run.sh
#!/bin/sh
echo 'starting elm project'
cd /usr/src/elm
git pull
pip install -r venv.txt
cd /usr/src/elm/elm
gunicorn -w 1 -b 0.0.0.0:8000 elm.wsgi
【注】如果是在Window下编写了sh脚本,需要在unix下运行时,需要将文件的格式改为unix:
> vi run.sh
按Shift+: 输入 set ff=unix 回车之后,退出保存即可,否则会出现 Bad intercepter错误。
在Dockerfile所在的目录下
docker build -t elm:1.0 .
docker run -itd --name el_oa_server1 -p 8001:8000 elm:1.0
docker run -itd --name el_oa_server2 -p 8002:8000 elm:1.0
docker run -itd --name el_oa_server3 -p 8003:8000 elm:1.0
docker save elm:1.0 -o ~/elm.tar
将当前服务器下的elm.tar的镜像压缩文件上传到其它服务器
scp ~/elm.tar root@yyserver2:~/
将上传的镜像压缩文件导入到当前docker镜像中
docker load < ~/elm.tar
查看镜像是否导入成功
docker images
一般的nginx.conf文件的内容如下:
# user nobody;
# user apple; # 当前nginx访问静态资源的用户, 避免出现403问题。
worker_processes 2; #cat /proc/cpuinfo 查看cpu cores核数
worker_cpu_affinity 01 10;
# 配置错误的日志信息
error_log logs/error.log notice;
# error_log logs/error.log debug;
# 配置Nginx worker进程最大打开文件数
# 进程连接数量要小于等于系统的最大打开文件数
# ulimit -a|grep "open files" 查看打开文件的最大连接数
worker_rlimit_nofile 65535;
pid logs/nginx.pid;
# 事件配置
events
{
use epoll; # 使用epoll 事件模型(I/O多路复用)
multi_accept on;
accept_mutex_delay 50ms;
worker_connections 20480;
}
http
{
include mime.types; # 定义所有的Content-Type 数据类型
# default_type application/octet-stream;
default_type text/html;
# 配置日志格式
# log_format main '$remote_addr - $remote_user [$time_local] "$request"'
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"'
# '"$upstream_addr" "$upstream_status" "$upstream_response_time" '
# '$request_time -- $http_cookie -- $cookie_pin';
# access_log logs/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 0;
gzip on;
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_types text/plain application/x-javascript text/css text/shtml application/xml;
proxy_intercept_errors on;
charset utf-8;
######################
include conf.d/*.conf;
######################
}
在conf.d目录下的project.conf文件内容如下:
proxy_next_upstream error;
server
{
#nginx提供对外的端口是80
listen 80;
server_name localhost;
client_max_body_size 50M;
#配置项目静态资源目录
location /static {
alias /usr/src/elm/elm/static;
}
location / {
proxy_pass http://127.0.0.1:8000;
# 设置请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.htlml {
root html;
}
}
请求头默认情况下,带"", 所有自定义的请求头获取需要使用"http_"。
nginx默认request的header的那么中包含’_’时,会自动忽略掉。http部分中添加如下配置:
http{
# ...
underscores_in_headers on; # 默认 off
server {
...
}
}
在反向代理请求配置如下:
location /
{
proxy_pass http://127.0.0.1:8000;
# 设置请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 自定义请求头
proxy_set_header Authorization $http_Authroization;
proxy_set_header Token $http_Token;
}
在前后端分离的开发中使用nginx做反向代理去请求服务器地址时,发现自定义的header请求头Authorization信息丢失了。解决办法可以考虑"4.3 自定义请求头", 也可以尝试以下方式:
http{
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Headers Authroization;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header X-Frame-Options SAMEORIGIN;
}
X-Frame-Options HTTP 响应头是用来给浏览器 指示允许一个页面 可否在frame或者iframe中展现HTML的标记。站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。
X-Frame-Options
有三个可能的值:
X-Frame-Options: deny
X-Frame-Options: sameorigin
X-Frame-Options: allow-from https://example.com/
deny
表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
sameorigin
表示该页面可以在相同域名页面的 frame 中展示。
allow-from uri
表示该页面可以在指定来源的 frame 中展示。
location /proxy_path {
proxy_pass http://127.0.0.1:8080/project;
proxy_set_header Cookie $http_cookie;
}
Openresty下载页:
https://openresty.org/cn/download.html
下载版本:wget https://openresty.org/download/openresty-1.11.2.5.tar.gz (Ubuntu 16.x)
较新版本: wget https://openresty.org/download/openresty-1.13.6.2.tar.gz (Ubuntu 17.10+)
以下是在Ubuntu18.04 (bionic) 下安装过程:
如果nginx已安装,则执行以下操作:
sudo systemctl disable nginx
sudo systemctl stop nginx
安装GPG public keys及apt源
apt-get -y install --no-install-recommends wget gnupg ca-certificates
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
apt-get -y install --no-install-recommends software-properties-common
add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"
apt-get update
安装gunicorn
apt install openresty
默认安装在 /usr/local/
查看服务是否启动
service openresty status
ps -ef|grep 80
ps -ef|grep nginx
配置文件: /usr/local/openresty/nginx/conf/nginx.conf
Nginx主要通过nginx.conf文件进行配置使用。在nginx.conf文件中主要分为:
全局块:一些全局的属性,在运行时与具体业务功能(比如http服务或者email服务代理)无关的一些参数,比如工作进程数,运行的身份等
event块:参考事件模型,单个进程最大连接数等
http块:设定http服务器
#nginx进程数,建议设置为等于CPU总核心数。
worker_processes 8;
#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
error_log /usr/local/nginx/logs/error.log info;
#进程pid文件
pid /opt/openresty/nginx/logs/nginx.pid;
#指定进程可以打开的最大描述符:数目
#工作模式与连接数上限
#这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致。
worker_rlimit_nofile 65535;
http{
#虚拟主机的配置
server
{
#监听端口
listen 80;
#域名可以有多个,用空格隔开, cat /etc/hosts
server_name www.jd.com jd.com;
index index.html index.htm index.php;
root /data/www/jd;
#url 请求路由
location /hello {
default_type text/html;
content_by_lua '
ngx.say("Hello, World!
")
';
}
}
#负载均衡配置
upstream piao.jd.com {
#upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weight参数表示权值,权值越高被分配到的几率越大。
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
}
}
负载均衡也是Nginx常用的一个功能,负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略
按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream test{
server localhost:8080 weight=9;
server localhost:8081 weight=1;
}
此时8080和8081分别占90%和10%。
上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream test {
ip_hash;
server localhost:8080;
server localhost:8081;
}
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backend {
fair;
server localhost:8080;
server localhost:8081;
}
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。
upstream backend {
hash $request_uri;
hash_method crc32;
server localhost:8080;
server localhost:8081;
}
处理动态请求转发到某一个服务
location = / {
proxy_pass http://localhost:8080
}
此处的proxy_pass 对应的服务,会导到上述upstream入口
Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作(CDN),这就是网站静态化处理的核心思路。
# 静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root /home/apple/artproject/art;
# 过期1天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
expires 1d;
}
语法规则:
location [=|~|~*|^~] /uri/ {
}
= 开头表示精确匹配
^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。
~ 开头表示区分大小写的正则匹配
~* 开头表示不区分大小写的正则匹配
!和!*分别为区分大小写不匹配及不区分大小写不匹配 的正则
/ 通用匹配,任何请求都会匹配到。
多个location配置的情况下匹配顺序为:
首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。
例子,有如下匹配规则:
location = / {
#规则A
}
location = /login {
#规则B
}
location ^~ /static/ {
#规则C
}
location ~ \.(gif|jpg|png|js|css)$ {
#规则D
}
location ~* \.png$ {
#规则E
}
location !~ \.xhtml$ {
#规则F
}
location !~* \.xhtml$ {
#规则G
}
location / {
#规则H
}
那么产生的效果如下:
访问根目录/, 比如http://localhost/ 将匹配规则A
访问 http://localhost/login 将匹配规则B,http://localhost/register 则匹配规则H
访问 http://localhost/static/a.html 将匹配规则C
访问 http://localhost/a.gif, http://localhost/b.jpg 将匹配规则D和规则E,但是规则D顺序优先,规则E不起作用,而 http://localhost/static/c.png 则优先匹配到规则C
访问 http://localhost/a.PNG 则匹配规则E,而不会匹配规则D,因为规则E不区分大小写。
访问 http://localhost/a.xhtml 不会匹配规则F和规则G,http://localhost/a.XHTML不会匹配规则G,因为不区分大小写。规则F,规则G属于排除法,符合匹配规则但是不会匹配到,所以想想看实际应用中哪里会用到。
访问 http://localhost/category/id/1111 则最终匹配到规则H,因为以上规则都不匹配,这个时候应该是nginx转发请求给后端应用服务器,比如FastCGI(php),tomcat(jsp),nginx作为方向代理服务器存在。
worker_processes 2;
worker_rlimit_nofile 65535;
events {
use epoll;
worker_connections 20480;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
upstream www.elm.com {
server localhost:8001;
server localhost:8002;
server localhost:8003;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://www.elm.com;
}
location ^~ /static {
alias /usr/src/elm/elm/static;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
修改完conf配置文件之后,需要重新装载配置文件。
在/usr/local/openresty/nginx的目录下执行以下命令:
./sbin/nginx -s reload
docker pull registry
docker run -d --name registry_server -p 5000:5000 registry
配置文件的位置: /etc/docker/daemon.json
{
"registry-mirrors": ["https://y4tay211.mirror.aliyuncs.com"],
"insecure-registries": [ "119.3.182.128:5000"]
}
重新加载配置
systemctl daemon-reload
systemctl restart docker
将已有的镜像重新按地地仓库的格式打标签 tag
docker tag ubuntu-dev 119.3.182.128:5000/ubuntu-dev
推送
docker push 119.3.182.128:5000/ubuntu-dev
当前系统OS 需要配置本地仓库信息,参考/etc/docker/daemon.json
{
"registry-mirrors": ["https://y4tay211.mirror.aliyuncs.com"],
"insecure-registries": [ "119.3.182.128:5000"]
}
当前系统的OS重新装载daemon的配置及重启docker服务
systemctl daemon-reload
systecmctl restart docker
下拉镜像
docker pull 119.3.182.128:5000/ubuntu-dev
【提示】如果在配置过程中出现问题,可以加我QQ(610039018)或直接回贴,我会第一时间回复。