Docker+Nginx+GitLab+Gunicorn部署方案

Docker+Nginx+GitLab+Gunicorn部署方案

一、技术描述

通过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的基本使用

gunicorn(“绿色独角兽”)是一个python Wsgi http server,只支持在Unix系统上运行,来源于Ruby的unicorn项目。Gunicorn使用prefork master-worker模型(在gunicorn中,master被称为arbiter),能够与各种wsgi web框架协作。

2.1 安装依赖包

 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.

详细参考

2.2 Django项目的启动

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

2.3 Flask项目的启动

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()对象的变量名

三、项目中编写Dockerfile

3.1 clone项目

git clone http://114.116.238.223:10080/liu/elm.git

3.2 项目的Dockerfile

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

3.3 编写shell启动脚本

#!/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错误。

3.4 构建docker镜像

在Dockerfile所在的目录下

docker build -t elm:1.0 .

3.5 启动docker镜像

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

3.6 打包镜像

docker save elm:1.0 -o ~/elm.tar

3.7 分发镜像

将当前服务器下的elm.tar的镜像压缩文件上传到其它服务器

scp ~/elm.tar root@yyserver2:~/

3.8 导入镜像

将上传的镜像压缩文件导入到当前docker镜像中

docker load < ~/elm.tar

查看镜像是否导入成功

docker images

四、Nginx的配置说明

4.1 基本配置

一般的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;
    ######################
}

4.2 项目配置

在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;
     }
}

4.3 自定义请求头

请求头默认情况下,带"", 所有自定义的请求头获取需要使用"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;
}

4.4 跨域请求头

在前后端分离的开发中使用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 中展示。

4.5 Cookie问题

location /proxy_path {
  proxy_pass http://127.0.0.1:8080/project;
  proxy_set_header  Cookie $http_cookie;
}

五、OpenResty的应用

5.1 安装

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

5.2 通用配置

配置文件: /usr/local/openresty/nginx/conf/nginx.conf

Nginx主要通过nginx.conf文件进行配置使用。在nginx.conf文件中主要分为:

  • 全局块:一些全局的属性,在运行时与具体业务功能(比如http服务或者email服务代理)无关的一些参数,比如工作进程数,运行的身份等

  • event块:参考事件模型,单个进程最大连接数等

  • http块:设定http服务器

    • server块:配置虚拟主机
    • location块:配置请求路由及页面的处理情况等
#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("<p>Hello, World!</p>")
            ';
        }

    }
    #负载均衡配置
    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;

    }

}

5.3 负载均衡策略

​ 负载均衡也是Nginx常用的一个功能,负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

​ Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略

RR (轮询策略)

​ 按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。

权重weight

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。

upstream test{
     server localhost:8080 weight=9;
     server localhost:8081 weight=1;
}

此时8080和8081分别占90%和10%。

ip_hash

​ 上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

upstream test {
    ip_hash;
    server localhost:8080;
    server localhost:8081;
}

fair(第三方)

按后端服务器的响应时间来分配请求,响应时间短的优先分配。

upstream backend { 
    fair; 
    server localhost:8080;
    server localhost:8081;
}

url_hash(第三方)

按访问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入口

5.4 静态资源服务器

​ Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作(CDN),这就是网站静态化处理的核心思路。

# 静态文件,nginx自己处理
location  ~ ^/(images|javascript|js|css|flash|media|static)/ {
   root   /home/apple/artproject/art;
   # 过期1天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
   expires  1d;
}

5.5 URL路由规则

语法规则:

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作为方向代理服务器存在。

5.6 x项目配置

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私有仓库

6.1 下载仓库镜像

docker pull registry

6.2 启动本地仓库

docker run -d --name registry_server -p 5000:5000  registry

6.3 配置本地仓库

配置文件的位置: /etc/docker/daemon.json

{
  "registry-mirrors": ["https://y4tay211.mirror.aliyuncs.com"],
  "insecure-registries": [ "119.3.182.128:5000"] 
}

重新加载配置

systemctl daemon-reload
systemctl restart docker

6.4 向本地仓库推送镜像

将已有的镜像重新按地地仓库的格式打标签 tag

docker tag ubuntu-dev 119.3.182.128:5000/ubuntu-dev

推送

docker push 119.3.182.128:5000/ubuntu-dev

6.5 从私有仓库拉镜像

当前系统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)或直接回贴,我会第一时间回复。

发布了6 篇原创文章 · 获赞 7 · 访问量 2425

猜你喜欢

转载自blog.csdn.net/ahhqdyh/article/details/104832778