使用Docker快速创建.Net Core2.0 Nginx负载均衡节点

本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws

一.Self-Host Kestrel

1、在vs2017中新建dotnet core2.0 webapi项目 ApiService

2.、参照官方文档,https://docs.microsoft.com/en-us/aspnet/core/publishing/linuxproduction?tabs=aspnetcore2x 在Startup中增加

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

配置运行Url, 在Program.cs中

未分类

3、发布项目文件,通过FTP上传到linux服务器。 一个core2.0 webapi新项目发布后只有几百kb

4、切换目录,dotnet ApiService.dll

5、运行成功,开放服务器端口,不过目前是运行于Kestrel 的selfhost 状态。

未分类

二. 需要一个代理

ASP.NET Core 的运行环境由新开发的 Kestrel Server 负责,IIS 退回到 HTTP 的侦听器的角色,微软也特别为了这个需求开发了 IIS Platform Handler,以处理 HTTP 与运行环境之间的信息转发工作,微软官方推荐在Linux服务器上使用Nginx,Haproxy等代理Kestrel Server

理解dotnet core host最重要的一点是,它独立运行。不在IIS中运行,也不需要IIS运行。它拥有独立的自宿主Web Server,在内部使用self-host server处理请求。

然而,你依然可以把IIS放在self-host server前面,作为一个前端代理,因为Kestrel是一个只拥有原始功能的web server,它并没有像iis那样完整的web server 功能,比如Kestrel不支持单个ip上,多个应用绑定80端口,IIS还可以提供静态文件服务,gzip压缩,静态文件缓存等其他高级功能,IIS在处理请求时效率非常好,,所以有必要利用这一点,您可以让iis处理它真正擅长的任务,并将动态任务传递到core应用程序。所以说在windows上,iis依然继续扮演着非常重要的角色。

在传统经典的Asp.Net应用中,所有内容都托管在iis工作进程中(w3wp.exe),这就是我们常说的应用程序池。并且应用由IIS内置托管功能加载实例化,经过工作者进程加载aspnet_isapi.dll,在用aspnet_isapi加载.Net运行时。IIS工作者进程中的应用程序池加载应用程序域。一系列工作结束后,由ISAPIRuntime对象调用PR方法,封装HttpWorkerRequest对象,传递给HttpRuntime 创建HttpApplication实例, 然后一系列HttpApplication初始化和管道事件执行。当然加载运行时,应用程序域等都只是第一个请求到达后做的事儿。

在dotnet core中很不同的是,core不会在iis工作进程中运行,而是在自己的Kestrel组件中。通过一个叫做AspNetCoreModule的原生的IIS module,执行外部的应用。Kestrel是一款针对吞吐量性能做了大量优化的dotnet web server的实现,它将网络请求快速传递给你的应用,但它仅仅是一个原始的web server,没有IIS那样全面的Web管理服务。

未分类

虽然IIS站点依然需要应用程序池,但是应该设置为无托管代码,由于应用程序池只作为转发请求的代理,因此不需要实例化.net 运行时。所以在linux上也一样,我们需要一个self-host的前端代理,在这里参考文档使用nginx。

三.nginx做代理

找到/etc/nginx配置nginx.conf

server {
    listen 80;
    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

我将nginx 的user改为root 5000改成自己的10000

创建service file

nano /etc/systemd/system/apiservice.service

service file的内容,官方示例:

[Unit]
Description=Example .NET Web API Application running on Ubuntu

[Service]
WorkingDirectory=/var/aspnetcore/hellomvc
ExecStart=/usr/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll
Restart=always
RestartSec=10  # Restart service after 10 seconds if dotnet service crashes
SyslogIdentifier=dotnet-example
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production 

[Install]
WantedBy=multi-user.target

修改了 User为root。还修改了工作目录 就是我项目文件ftp上传后的目录,ExecStart的 dotnet这个目录不要修改 dll目录,改成目标要执行的dll的目录

然后enable service

执行 systemctl enable kestrel-hellomvc.service

start并验证service的状态

systemctl start kestrel-hellomvc.service

systemctl status kestrel-hellomvc.service

访问监听中的80端口,证明服务成功。

四.做负载均衡

按照相同的方式 我们再部署一个10001,修改nginx,配置负载均衡。

未分类

访问证明我们配置成功。

未分类

未分类

五.创建Docker Image

官方提供的dotnet core镜像位 microsoft/dotnet。docker基础命令就不提了,刚开始用也是边学边记。下面基于microsoft/dotnet image创建自己的image。以便快速运行多个docker image,配置更多的负载均衡,而无需手动copy到各个服务器上再配置环境,也就是说无论我们创建几十个甚至上百个,有我们自己的docker hub的话,创建起来是很快的,也不会出现在这台服务器上可用,在另一台服务器上搞出什么其他问题。

下面只是一个学习过程中自己的范例,离最佳实践方式还差得很远,希望能对看随笔的朋友有所帮助。

由于还要在每个image的apiService前面 放置nginx,所以 core application在各个容器中都是使用self-host的形式,在Kestrel上运行。在前端通过nginx 对docker暴露出的端口号进行代理。

在发布的网站目录下 创建Dockerfile。

未分类

保存后 执行docker构建 使用当前目录的Dockerfile创建镜像。docker build -t image/apiservice-v3 . 注意结尾有个 . (使用当前目录)

未分类

docker images 查看镜像

我们可以发现 刚创建的docker image 比我们FROM的microsoft/dotnet 大小大一点。

未分类

下面运行下看看 四行命令 运行了四个我们刚创建的image

docker run -d -p :81:20000 image/apiservice-v3

未分类

docker ps -a 查看下运行中的image进程

未分类

下面配置nginx负载均衡然后service nginx reload,实验完成。

未分类

下面使用docker kill对docker container逐一停止,停止后访问,确认负载均衡成功。当四个container都停止后,nginx返回 502 error.

未分类

Nginx配合docker安装nextcloud(超简易)|抛弃owncloud

Nextcloud是owncloud母公司破产前核心人员离职出来创建的,免费版里包含了很多owncloud付费版本的功能,因为核心都是一样的,所以我们当然可以选择功能更多的nextcloud版本了。

我安装的nextcloud网盘:https://nextpan.net

安装docker

在使用本教程前,建议您对docker进行一些基本的了解,知道一些简单的命令,比如:

docker images
docker ps –a
docker start
docker stop
docker rm
docker rmi

不了解docker的可以谷歌一下这些命令快速了解,也可以看官方的docker教程。

docker官方安装教程:https://store.docker.com/search?type=edition&offering=community

我贴一下Ubuntu系统的安装方式,依次输入下列命令:

sudo apt–get –y install
  apt–transport–https
  ca–certificates
  curl
curl –fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add –
sudo add–apt–repository
       “deb [arch=amd64] https://download.docker.com/linux/ubuntu
       $(lsb_release -cs)
       stable”
sudo apt–get update
sudo apt–get –y install docker–ce

安装docker-compose

docker-compose是定义和运用docker的一种工具,使用下列命令安装:

apt install docker–compose

安装完成后,新建docker-compose.yml文件:

vim docker–compose.yml

然后把下列内容复制粘贴,注意这里我选用的是wonderfall的版本,他的版本star数最多,比官方的新,另外注意,修改pwd为自己的密码:

nextcloud:
  image: wonderfall/nextcloud
  links:
    – nextcloud–db:nextcloud–db  
  environment:
    – UID=1000
    – GID=1000
    – UPLOAD_MAX_SIZE=10G
    – APC_SHM_SIZE=128M
    – OPCACHE_MEM_SIZE=128
    – CRON_PERIOD=15m
    – TZ=Aisa/Shanghai
    – ADMIN_USER=admin            
    – ADMIN_PASSWORD=pwd  
    – DOMAIN=localhost
    – DB_TYPE=mysql
    – DB_NAME=nextcloud
    – DB_USER=nextcloud
    – DB_PASSWORD=pwd
    – DB_HOST=nextcloud–db
  volumes:
    – /docker/nextcloud/data:/data
    – /docker/nextcloud/config:/config
    – /docker/nextcloud/apps:/apps2
    – /docker/nextcloud/themes:/nextcloud/themes
  ports:
    – 127.0.0.1:8888:8888/tcp
nextcloud–db:
  image: mariadb:10
  volumes:
    – /docker/nextcloud/db:/var/lib/mysql
  environment:
    – MYSQL_ROOT_PASSWORD=pwd
    – MYSQL_DATABASE=nextcloud
    – MYSQL_USER=nextcloud
    – MYSQL_PASSWORD=pwd

然后在docker—compose.yml所在文件夹里,输入以下命令:

docker–compose up –d

这时候docker版本的nextcloud就已经安装好了,端口是127.0.0.1:8888,我们接下来利用nginx反代这个端口,并且加上https,从而能让外网安全的访问。

安装nginx

apt install nginx

安装letsencrypt

apt install letsencrypt

生成证书

注意替换域名

letsencrypt certonly —webroot –w /var/www/html —domain “abc.example.com”

配置nginx

可以修改默认的,也可以新建一个配置文件,我这里直接修改默认的

vim /etc/nginx/sites–available/default

删掉里面的内容,复制粘贴进这个,注意你的letsencrypt生成的证书路径对不对,一般只需要改一下domain就行了:

server {
  listen 80;
  server_name abc.example.com;
  return 301 https://$host$request_uri;
}
server {

    server_name nextpan.net www.nextpan.net;
    listen 443 ssl http2;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/abc.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/abc.example.com/privkey.pem;
    include /etc/nginx/conf/ssl_params.conf;
    client_max_body_size 10G;
    location / {
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $http_host;
    }
    location = /.htaccess {
        return 404;
    }
}

重启nginx:

service nginx restart

然后输入自己的域名就可以直接登陆了。

如果想更新,只需要删掉nextcloud和mariadb的container,重新docker-compose up -d就可以了。

Linux Awk使用案例总结 nginx日志统计

知识点:

  • 数组

数组是用来存储一系列值的变量,可通过索引来访问数组的值。
Awk中数组称为关联数组,因为它的下标(索引)可以是数字也可以是字符串。
下标通常称为键,数组元素的键和值存储在Awk程序内部的一个表中,该表采用散列算法,因此数组元素是随机排序。

数组格式:array[index]=value

一、Nginx日志分析

日志格式

'$remote_addr - $remote_user [$time_local] "$request" $status $request_body  $body_bytes_sent "$http_referer"  "$http_user_agent" "$http_x_forwarded_for"

日志记录:

183.251.21.109 - - [16/Sep/2017:09:43:36 +0800] "POST /article/getKeywords HTTP/1.1" 200 str=Linux+Awk%E4%BD%BF%E7%94%A8%E6%A1%88%E4%BE%8B%E6%80%BB%E7%BB%93+nginx%E6%97%A5%E5%BF%97%E7%BB%9F%E8%AE%A1  185 "http://xxxx" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36" "-"

1、统计日志中访问最多的10个IP

思路:对第一列进行去重,并输出出现的次数

方法1:

awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log

方法2:

awk '{print $1}' access.log |sort |uniq -c |sort -k1 -nr |head -n10

说明:a[$1]++ 创建数组a,以第一列作为下标,使用运算符++作为数组元素,元素初始值为0。处理一个IP时,下标是IP,元素加1,处理第二个IP时,下标是IP,元素加1,如果这个IP已经存在,则元素再加1,也就是这个IP出现了两次,元素结果是2,以此类推。因此可以实现去重,统计出现次数。

2、统计日志中访问大于100次的IP

方法1:

awk '{a[$1]++}END{for(i in a){if(a[i]>100)print i,a[i]}}' access.log

方法2:

awk '{a[$1]++;if(a[$1]>100){b[$1]++}}END{for(i in b){print i,a[i]}}' access.log

说明:方法1是将结果保存a数组后,输出时判断符合要求的IP。方法2是将结果保存a数组时,并判断符合要求的IP放到b数组,最后打印b数组的IP。

3、统计2016年4月9日一天内访问最多的10个IP

思路:先过滤出这个时间段的日志,然后去重,统计出现次数

方法1:

awk '$4>="[9/Apr/2016:00:00:01" && $4<="[9/Apr/2016:23:59:59" {a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log

方法2:

sed -n '/[9/Apr/2016:00:00:01/,/[9/Apr/2016:23:59:59/p' access.log |sort |uniq -c |sort -k1 -nr |head -n10  #前提开始时间与结束时间日志中必须存在

4、统计当前时间前一分钟的访问数

思路:先获取当前时间前一分钟对应日志格式的时间,再匹配统计

date=$(date -d '-1 minute' +%d/%b/%Y:%H:%M);awk -vdate=$date '$0~date{c++}END{print c}' access.log
date=$(date -d '-1 minute' +%d/%b/%Y:%H:%M);awk -vdate=$date '$4>="["date":00" && $4<="["date":59"{c++}END{print c}' access.log
grep -c $(date -d '-1 minute' +%d/%b/%Y:%H:%M) access.log

说明:date +%d/%b/%Y:%H:%M –> 09/Apr/2016:01:55

5、统计访问最多的前10个页面($request)

awk '{a[$7]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log

6、统计每个URL访问内容的总大小($body_bytes_sent)

awk '{a[$7]++;size[$7]+=$10}END{for(i in a)print a[i],size[i],i}' access.log

7、统计每个IP访问状态码数量($status)

awk '{a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log

8、统计访问状态码为404的IP及出现次数

awk '{if($9~/404/)a[$1" "$9]++}END{for(i in a)print i,a[i]}' access.log

AWK分析nginx访问日志

如题,记录一次 awk 应用。通过分析 nginx 访问日志,得出 pv、uv等数据。

背景

前段时间被要求通过分析 nginx 的访问日志得出项目的 pv、uv 和来源设备等占比。主要是该平台下面有多个 h5 子项目,每个子项目又是一个简单的 SPA 应用,所以就没有考虑到第三方统计平台。不过这个完成了好久,现在才写,主要还是个人懒癌发作。。

AWK

首先介绍下 AWK 吧。

AWK 是一种 linux/unix 下的编程语言,强大的文本语言处理工具。它仅仅需要几行代码就能够完成复杂的文本处理工作。AWK 模式如下:

awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

网上找到一张对应的流程图如下:

未分类

BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。

END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。

pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行 { print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。

基本语法

AWK 的基本语法可参考: http://man.linuxde.net/awk

处理 nginx 日志

默认的 nginx 日志输出少了些我们需要的部分,所以我们需要先处理 nginx.conf 配置,打出我们需要的日志。这里先说下信息的统计思路吧。PV 很简单,每条 html 的访问记录算一条 PV,每个时间段的 PV 通过 nginx 日志里面的访问时间来判断,每天的 PV 通过将 nginx 日志切割,即每天的访问日志放到每天对应的文件里来判断。而 UV 则通过 cookie 来鉴别,即首先通过 javascript 在客户端种一个唯一的 cookie,每次访问记录中没有携带 cookie 的访问算一次 UV,客户端 cookie 的有效期到每天的0点。而来源及访问设备等可以通过浏览器携带的信息来判断。

nginx 配置

我们采取的部分 nginx.conf 配置如下:

http {
    include       mime.types;
    default_type  application/octet-stream;
    // 定义一种 project_log 格式的日志模式
    log_format project_log '$remote_addr - $time_local "$request" $status $sent_http_content_type UV: "$guid" $http_user_agent';
    sendfile        on;
    keepalive_timeout  65;
    client_max_body_size 2m;
    fastcgi_intercept_errors on;
    server {
    listen 443;
    server_name localhost;
    ssl on;
        ssl_certificate /test/server.crt;
    ssl_certificate_key /test/server.key;
    // 通过 cookie 正则匹配,如果有 _qingguoing 形式的 cookie 记录,就打到 log 中,默认是空
    if ($http_cookie ~* "_qingguoing=([A-Z0-9.]*)" ) {
        set $guid $1;
    }
    if ( $request_method !~ GET|POST|HEAD|DELETE|PUT ) {
            return 403;
        }
    location /qingguoing/ {
            index index.html index.htm;
            error_page 404 ./404.html;
            // 日志输出到 logs/qingguoing.log 文件
            access_log logs/qingguoing.log project_log;
            if (!-e $request_filename){
                return 404;
            }
        }
        error_page  404              /404.html;
    }
}

nginx 日志切割脚本

#nginx 日志切割脚本
#!/bin/bash
# 设置日志文件存放目录
logs_path="/var/www/logs/";
#设置pid文件
pid_path="/var/www/logs/nginx.pid";
#重命名日志文件
mv ${logs_path}qingguoing.log ${logs_path}logs_daily/qingguoing_$(date -d "yesterday" +"%Y%m%d").log
#向ngin主进程发信号重新打开日志
kill -USR1 `cat ${pid_path}`

AWK 处理脚本

BEGIN {
}
{
    # 过滤错误请求,只处理 html 200 请求,因为 html 没设缓存
    if ($8 != 200) next;
    if ($9 == "text/html") {
        # 来源匹配
        fromIndex = index($6, "from=");
        endIndex = index($6, "&");
        if (fromIndex != 0 && endIndex == 0) {
            # 增加 from= 的长度
            origin = substr($6, fromIndex + 5);
        } else {
            origin = substr($6, fromIndex + 5, endIndex);
        }
        # pathname 匹配
        pathLength = split($6, path, "/");
        # uv标记
        uv = 0;
        project = path[3];
        # 404.html
        if (pathLength > 4 || project == "404.html") next;
        if (origin != "platform") {
            # 日期 时间
            split($3, datetime, ":");
            if (timeArr[project] && timeArr[project] != datetime[2]) {
                # 新的一条记录,数据存到 mongodb 中并清空
                system("mongo 'yshow' --eval 'var project=""project"", date=""dateArr[project]"", time=""timeArr[project]"", pv=""pvArr[project]"", uv=""uvArr[project]"", pvFromOther=""pvFromOtherArr[project]"", pvFromTimeline=""pvFromTimelineArr[project]"", pvFromGroupmsg=""pvFromGroupmsgArr[project]"", pvFromSinglemsg=""pvFromSinglemsgArr[project]"",  uvFromOther=""uvFromOtherArr[project]"", uvFromTimeline=""uvFromTimelineArr[project]"", uvFromGroupmsg=""uvFromGroupmsgArr[project]"", uvFromSinglemsg=""uvFromSinglemsgArr[project]"", pvIos=""pvIosArr[project]"", uvIos=""uvIosArr[project]"", pvAdr=""pvAdrArr[project]"", uvAdr=""uvAdrArr[project]"", pvDeviceOther=""pvDeviceOtherArr[project]"", uvDeviceOther=""uvDeviceOtherArr[project]""' ./logs_mongo_insert.js");
                delete dateArr[project];
                delete timeArr[project];
                delete pvArr[project];
                delete uvArr[project];
                delete pvFromOtherArr[project];
                delete pvFromTimelineArr[project];
                delete pvFromGroupmsgArr[project];
                delete pvFromSinglemsgArr[project];
                delete uvFromOtherArr[project];
                delete uvFromTimelineArr[project];
                delete uvFromGroupmsgArr[project];
                delete uvFromSinglemsgArr[project];
                delete pvIosArr[project];
                delete uvIosArr[project];
                delete pvAdrArr[project];
                delete uvAdrArr[project];
                delete pvDeviceOtherArr[project];
                delete uvDeviceOtherArr[project];
            }
            # save
            timeArr[project] = datetime[2];
            dateArr[project] = datetime[1];
            pvArr[project]++;
            if ($11 == """") {
                uvArr[project]++;
                uv = 1;
            }
            ios = index($0, "iPhone");
            adr = index($0, "Android");
            # 设备
            if (ios > 0) {
                if (uv) uvIosArr[project]++;
                pvIosArr[project]++;
            } else if (adr > 0) {
                if (uv) uvAdrArr[project]++;
                pvAdrArr[project]++;
            } else {
                if (uv) uvDeviceOtherArr[project]++;
                pvDeviceOtherArr[project]++;
            }
            # 其他
            if (fromIndex == 0 ) {
                if (uv) uvFromOtherArr[project]++;
                pvFromOtherArr[project]++;
            }
            # 微信朋友圈
            if (origin == "timeline") {
                if (uv) uvFromTimelineArr[project]++;
                pvFromTimelineArr[project]++;
            }
            # 微信群
            if (origin == "groupmessage") {
                if (uv) uvFromGroupmsgArr[project]++;
                pvFromGroupmsgArr[project]++;
            }
            # 好友分享
            if (origin == "singlemessage") {
                if (uv) uvFromSinglemsgArr[project]++;
                pvFromSinglemsgArr[project]++;
            }
        }
    };
}
END {
    for (project in pvArr) {
        system("mongo 'yshow' --eval 'var project=""project"", date=""dateArr[project]"", time=""timeArr[project]"", pv=""pvArr[project]"", uv=""uvArr[project]"", pvFromOther=""pvFromOtherArr[project]"", pvFromTimeline=""pvFromTimelineArr[project]"", pvFromGroupmsg=""pvFromGroupmsgArr[project]"", pvFromSinglemsg=""pvFromSinglemsgArr[project]"",  uvFromOther=""uvFromOtherArr[project]"", uvFromTimeline=""uvFromTimelineArr[project]"", uvFromGroupmsg=""uvFromGroupmsgArr[project]"", uvFromSinglemsg=""uvFromSinglemsgArr[project]"", pvIos=""pvIosArr[project]"", uvIos=""uvIosArr[project]"", pvAdr=""pvAdrArr[project]"", uvAdr=""uvAdrArr[project]"", pvDeviceOther=""pvDeviceOtherArr[project]"", uvDeviceOther=""uvDeviceOtherArr[project]""' ./logs_mongo_insert.js");
    }
}

至此基本逻辑已写完。只需要在服务器里设置定时任务就行,例如每天的0点切割nginx日志,0点30开始处理前一天的日志存储统计信息。所以当前该思路唯一也是最大的缺陷就是没办法获取实时访问记录。后续如果有新思路会及时补充。

至此基本逻辑已写完。只需要在服务器里设置定时任务就行,例如每天的0点切割nginx日志,0点30开始处理前一天的日志存储统计信息。所以当前该思路唯一也是最大的缺陷就是没办法获取实时访问记录。

最后

前面也说了,最近才写这篇文章的主要原因是懒癌,导火索是前两天想通过 AWK 处理一个 hosts 文件到 charles 的 DNS 记录里,居然忘了 AWK 该怎么写了,果然好记性不如烂笔头。

本文代码地址:https://github.com/qingguoing/test/tree/master/awk/nginx

hosts 文件转 charles DNS 记录文件:https://github.com/qingguoing/test/tree/master/awk/DNS

Debian/Ubuntu 下 Nginx+Lua 环境搭建

前言

  • 关于lua的特性不再赘述;
  • 以下步骤均使用了apt-get进行操作,免去了诸如ng-lua组件手动加载等繁琐的步骤,妄图使用其他方式安装的请移步官方文档:https://github.com/openresty/lua-nginx-module#installation
  • lua在web上基于lua-nginx-module运作,目前还没有Apache的支持组件,所以妄图使用Apache的可以撤了;

本人环境供参考:

Distributor ID: Debian
Description:    Debian GNU/Linux 8.8 (jessie)
Release:    8.8
Codename:   jessie

注:不确定wheezy版本的Linux下luajit是否可用。

搭建步骤

1、首先确定自己的APT源是否可用,是否足够新.

本人最开始使用了一个较为老旧的dotdeb版本,导致apt-cache查询不到lua-nginx-module,甚至一度尝试手动加载该模块,由此浪费了许多时间;
本人使用的apt source list供参考:

sudo vi /etc/apt/sources.list

>
deb http://mirrors.aliyun.com/dotdeb jessie all
deb http://mirrors.aliyun.com/debian jessie main contrib non-free
deb http://mirrors.aliyun.com/debian jessie-updates main contrib non-free
deb http://mirrors.aliyun.com/debian-security jessie/updates main contrib non-free

需要注意的是:添加源的时候需要注意完整性,本人最开始图省事,仅用了一个all,发现apt并不能找到luajit模块,又浪费了很多时间。

2、没有nginx的先安装nginx;

不再赘述

sudo apt-get install nginx-full

3、安装lua及相关组件

不再赘述

apt-get install lua5.2 lua5.2-doc liblua5.2-dev

4、安装luajit

sudo apt-get install luajit

关于JIT :

通常,程序有两种运行方式:静态编译与动态直译。
静态编译的程序在执行前全部被翻译为机器码,而动态直译执行的则是一句一句边运行边翻译。
即时编译(Just-In-Time Compiler)则混合了这二者,一句一句编译源代码,但是会将翻译过的代码缓存起来以降低性能损耗。

此外,使用luajit可以大大提高lua的运行效率,由此也被官方钦定。

It is highly recommended to use OpenResty releases which integrate Nginx, ngx_lua, LuaJIT 2.1, as well as other powerful companion Nginx modules and Lua libraries.

5、安装nginx的lua模块 lua-nginx-module

sudo apt-get install libnginx-mod-http-lua

需要注意的是:如果前面apt源不够新或不够全,很可能会在安装此模块的时候出现找不到luajit依赖项的情况,此时请寻找新的可靠的源并确保完整性,不要在这里浪费时间。

6、配置nginx

nginx的配置中,核心在于content_by_lua_file。

关于 content_by_lua_file 的官方文档: https://github.com/openresty/lua-nginx-module#content_by_lua_file

本人的配置代码供参考:

server {
            listen 80;
            server_name ebid.xxxx.com;
            root /home/separes/xxx;

            location / {
                    lua_code_cache off;  // 缓存
                    content_by_lua_file /home/separes/xxx/index.o;
            }
}

需要注意的是:这里的nginx缓存是默认开启的,推荐调试及开发环境中手动关闭 lua_code_cache。

7、其它

这里推荐几个组件

// cjson
sudo apt-get install lua-cjson

// lyaml
sudo luarocks install lyaml

// dbi
sudo luarocks install luadbi-mysql MYSQL_INCDIR=/usr/include/mysql
//实测后面必须指定MYSQL路径,指向系统中的mysql根目录,匹配mysql.c文件

需要注意的是,使用 luarocks/apt-get 安装或升级 lua-DBI 的时候,需要注意新版本的DBI并不完全向下兼容,其中dbi参数由全局变量改为了局部变量,如果在以前的代码中使用过,需要重新进行声明,否则该参数会报错。

文档:

Lua官方文档: https://www.lua.org/manual/5.3

lua-nginx-module官方文档: https://github.com/openresty/lua-nginx-module

环境搭建至此为止。

简单的灰度测试,lua或者直接改nginx配置

配置过程:

1、根据cookies特征值实现

在做的过程中把login的操作给做了过滤。登录过程将cookie中设置了一个新的cookie值 GINA_gray_SESSIONID =gray这样在nginx配置中

加入筛选

Root不要配置在最前面而是配置在location中

#过滤login 请求进来会优先判断 请求是不是/ gina-client/login 然后判断是不是 / gina-client/

location /client/login {
     proxy_pass http://client_stable_gray;
}

登陆过程加上cookie

 location /client/{
                set $group "client_stable";

if ( $http_cookie ~* "GINA_gray_SESSIONID = gray" ) {

                set $group "client_stable_gray";

}

               proxy_pass http://$group;
        }

筛选root

location / {

                 set root /data0/project/gina/html/agent;

                 if ( $http_cookie ~*"GINA_gray_SESSIONID = gray" ) {

                      set root /data0/project/gina/html/agent_gray/magneto/dist/agent;

                 }

        }

2、根据cookie ||||| lua 方式

如果单独抽出来写lua脚本

cookies_gray.lua

local uin = ngx.var.cookie_loginuin

--取cookies里的loginuin字段,末尾被2整数的灰度

if uin ~= nil and string.sub(uin,string.len(uin))%2 == 0then

  ngx.exec("@gray_env")

else

  ngx.exec("@product_env")

end

nginx.conf

  ...

  #http

  lua_code_cacheoff; #正式上线记得打开cache

  lua_package_path"/usr/local/openresty/lualib/?.lua;;";

  lua_shared_dictconfig 1m;

  ...

  #server

  location / {

     #access_by_lua_file  conf/lua/ip_gray.lua;

    access_by_lua_file conf/lua/cookies_gray.lua;

  }

  location@gray_env {

      proxy_passhttp://gray_env;

     proxy_set_header Host $http_host;

  }

  location@product_env {

      proxy_passhttp://product_env;

     proxy_set_header Host $http_host;

  }

cal uin = ngx.var.cookie_GINA_gray_SESSIONID

if uin ~= nil and uin == 'gray' then

 ngx.say("Gray Environment World")

 #ngx.exec("@gray_env")

else

 ngx.exec("@product_env")

end

Nginx+uWSGI+Mysql+Django+Virtualenv环境部署

安装软件包

我的Linux是Ubuntu,需要安装Python3,如果你需要安装其他版本替换即可

sudo apt-get install mysql-server python3 python-dev python3-pip virtualenv

配置Virtualenv及Python环境

首先,这个Virtualenv并不是必须的,他的作用就是创建一个“隔离”的Python运行环境。

我想大家都碰到过在一台电脑上同时开发多个应用程序,如果应用A需要Python 2.7,而应用B需要Python 2.6怎么办?有时还会碰到不只是Python版本的不一致,比如Django等软件包版本。

这种情况下,每个应用可能需要各自拥有一套“独立”的Python运行环境。

1、新建独立运行环境,命名为pyblog_env

virtualenv pyblog_env --python=python3.5 --no-site-packages

–python指定Python版本 –no-site-packages不复制系统已安装Python包

2、进入pyblog_env环境

source pyblog_env/bin/activate

这时会发现命令提示符前面多了一个(pyblog_env),变成(pyblog_env)user@hostname:~$ 这种形式,这表示我们已经进入到刚刚创建的虚拟环境。 然后再通过pip安装包,都会安装到这个环境下,不会对系统环境造成影响。 退出虚拟环境命令:deactivate

3、安装项目依赖

pip3 install django PyMySQL mysqlclient Pillow
#如果有需求文件,比如requirements.txt,则执行
pip3 install -r requirements.txt

Django项目配置

1、上传Django项目,我的项目在GitHub。

git clone https://github.com/zytx/pyblog.git

2、配置项目的数据库信息

3、数据迁移

python manage.py makemigrations
python manage.py migrate

4、建立超级用户:

python manage.py createsuperuser

5、收集静态文件:

python manage.py collectstatic

6、测试项目 运行开发服务器看项目能否正常运行

python manage.py runserver 0.0.0.0:8000

访问ip:8000看是否出错

uWSGI 配置

退出刚才进入的虚拟环境,使用命令:deactivate

1、安装uWSGI

pip3 install uwsgi

2、运行测试:

uwsgi --http :8000 --home /home/zytx/pyblog_env/ --chdir /home/zytx/pyblog_env/pyblog -w pyblog.wsgi

–home Virtualenv的虚拟环境目录 –chdir Django项目目录

如果访问IP:8000可以看到项目,就说明Python环境和uWSGI是没有问题的

3、创建配置文件 接下来在项目里新建一个配置文件uwsgi.ini

[uwsgi]
# 项目根目录路径(full path)
chdir           = /home/zytx/pyblog_env/pyblog/
# Django的 wsgi 文件
module          = pyblog.wsgi:application
# virtualenv目录 (full path)
home            = /home/zytx/pyblog_env/

master          = true
# 最大工作进程数(CPU密集型建议设为CPU核心数,IO密集型建议设为CPU核心数的两倍)
processes       = 2
# unix套接字文
socket          = /home/zytx/pyblog_env/pyblog.sock
# socket文件权限
chmod-socket    = 777
# 退出时清空环境
vacuum          = true

4、后台启动uWSGI

uwsgi /home/zytx/pyblog_env/pyblog/uwsgi.ini&

Nginx 配置

1、创建配置文件

sudo vi /etc/nginx/sites-available/pyblog

加入如下内容:

server {
    listen 80;
    server_name www.immrz.com;
    root /home/zytx/pyblog_env/pyblog/;

        location /media/  {
            #如果你的media目录不在root里,可以配置 alias /path/to/your/mysite/media;
        }

        location  /static/ {
            #如果你的static目录不在root里,可以配置 alias /path/to/your/mysite/static;
        }

        location / {
            uwsgi_pass  unix:///home/zytx/pyblog_env/pyblog.sock;
            include uwsgi_params; # the uwsgi_params file you installed
        }
}

软连接到sites-enabled

sudo ln -s /etc/nginx/sites-available/pyblog /etc/nginx/sites
enabled/

2、重新加载Nginx配置

sudo service nginx reload

如果配置无误我们就可以让uWSGI开机运行

#编辑/etc/rc.local,在exit0之前加入
/usr/local/bin/uwsgi --ini /home/zytx/pyblog_env/pyblog/pyblog.uwsgi.ini&

CENTOS 6.x/7.x yum安装配置Nginx

第一种方法:直接通过RPM包安装

1、查看系统是否已安装Nginx,若已安装查看Nginx版本

yum info nginx

rpm -qa|grep nginx

2、选择RPM包进行安装

在 http://nginx.org/packages/centos 网站下查找需要安装的Nginx版本

执行命令

yum install http://nginx.org/packages/centos/6/x86_64/RPMS/nginx-1.8.0-1.el6.ngx.x86_64.rpm

第二种方法:设置Nginx软件源

在/etc/yum.repos.d/目录下创建一个源配置文件nginx.repo:

cd /etc/yum.repos.d/

vim nginx.repo

内容:

[nginx]

name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

保存,则会产生一个/etc/yum.repos.d/nginx.repo文件。

启动Nginx了:

/etc/init.d/nginx start

或者

service nginx start

配置一下Linux防火墙开通80端口。

iptables -I INPUT 5 -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
service iptables save
service iptables restart

Nginx配置文件位置:

/etc/nginx/nginx.conf

使用nginx ngx_http_referer_module模块配置防盗链

nginx referer简介

nginx模块ngx_http_referer_module通常用于阻挡来源非法的域名请求.我们应该牢记,伪装Referer头部是非常简单的事情,所以这个模块只能用于阻止大部分非法请求。我们应该记住,有些合法的请求是不会带referer来源头部的,所以有时候不要拒绝来源头部(referer)为空的请求.

图片防盗链配置示例如下

location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$ {
valid_referers none blocked *.91linux.org 91linux.org server_names ~.google. ~.baidu.;
    if ($invalid_referer) {
    return 403;
    #rewrite ^/ https://www.91linux.org/403.jpg;
    }
}

以上所有来至91linux.org和域名中包含google和baidu的站点都可以访问到当前站点的图片,如果来源域名不在这个列表中,那么$invalid_referer等于1,在if语句中返回一个403给用户,这样用户便会看到一个403的页面,如果使用下面的rewrite,那么盗链的图片都会显示403.jpg。如果用户直接在浏览器输入你的图片地址,那么图片显示正常,因为它符合none这个规则.

示例解析

第一行:

location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$

其中“gif|jpg|jpeg|png|bmp|swf”设置防盗链文件类型,自行修改,每个后缀用“|”符号分开!

第二行:

valid_referers none blocked *.91linux.org 91linux.org server_names ~.google. ~.baidu.;

就是白名单,允许文件链出的域名白名单,自行修改成您的域名! *.91linux.org这个指的是子域名,域名与域名之间使用空格隔开!baidu和google是搜索引擎。

第五行:

rewrite ^/ https://www.91linux.org/403.jpg;

这个图片是盗链返回的图片,也就是替换盗链网站所有盗链的图片。这个图片要放在没有设置防盗链的网站上,因为防盗链的作用,这个图片如果也放在防盗链网站上就会被当作防盗链显示不出来了,盗链者的网站所盗链图片会显示X符号;当然你也可以直接返回403

扩展

这样设置差不多就可以起到防盗链作用了,上面说了,这样并不是彻底地实现真正意义上的防盗链!

我们来看第三行:

valid_referers none blocked *.91linux.org 91linux.org server_names ~.google. ~.baidu.;

valid_referers 里多了“none blocked”

我们把“none blocked”删掉,改成

valid_referers *.91linux.org 91linux.org server_names ~.google. ~.baidu.;
  • none
    “Referer” 来源头部为空的情况

  • blocked
    “Referer”来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以http://或者https://开头.

nginx彻底地实现真正意义上的防盗链完整的代码应该是这样的:

location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$ {
valid_referers *.91linux.org 91linux.org server_names ~.google. ~.baidu.;
    if ($invalid_referer) {
    return 403;
    #rewrite ^/ https://www.91linux.org/403.jpg;
    }
}

这样您在浏览器直接输入图片地址就不会再显示图片出来了,也不可能会再右键另存什么的。

我们应该记住,有些合法的请求是不会带referer来源头部的,所以有时候不要拒绝来源头部(referer)为空的请求.

Ubuntu系统安装Nginx web服务器

在QQ群很多朋友问阿里云服务器怎么安装LNMP环境,怎么把项目放到服务器上面去,在这里,我就从头开始教大家怎么在阿里云服务器安装LNMP环境。

在这之前,我们先要知道什么是LNMP。

  • L: 表示的是Linux系统, 包括Ubuntu、Centos但不限于以上两种的系统版本。
  • N: 表示的是Nginx,这是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。
  • M: 表示的是Mysql,Mysql是一个小型关系型数据库管理系统。
  • P: 表示的是PHP,PHP是一种在服务器端执行的嵌入HTML文档的脚本语言。

首先, 我们使用ssh连接到服务器:

$  ssh [email protected]

接着我们需要更新一下apt管理工具包:

$ apt update

更新完之后,使用包管理工具直接下载Nginx:

$ apt install -y nginx

这个时候可以看一下外面安装的nginx在放在哪里:

$ whereis nginx
nginx: /usr/sbin/nginx /etc/nginx /usr/share/nginx

现在我们先不管配置, 打开浏览器, 输入服务器IP地址访问, 如果出现:

Welcome to nginx!

那就证明已经安装成功了Nginx。

先简单说一下配置:

现在我使用的nginx版本是1.10.3

$ nginx -v
nginx version: nginx/1.10.3 (Ubuntu)

这个版本的配置在/etc/nginx/sites-enabled/default

$ cd /etc/nginx/sites-enabled/
$ ls
default

这个default里面就有我们一会要配置的东西.

还有就是关于Nginx的错误日志的路径/var/log/nginx, 很多时候我们找错误都是从错误日志中找到问题的解决方法,所以这个路径还是要记住的。

$ cd /var/log/nginx
$ ls
access.log  error.log