分类
OpenResty

我的第一段 OpenResty 代码

前端同学需要一个上传文件的后端做一些调试,我刚好有,就给她用了。然后遇到跨域问题,一般来说我会在 PHP 里解决,不过这次我想挑战一下 nginx,于是找到这个答案。照做之后,OPTIONS 请求按照预期返回了正确的头,但是 POST 不行。

于是我在群里请教同事,结果被老板批评:

  1. 在 nginx 里,“IF is evil”,因为要在一些特定条件下,它的行为会跟预期完全相反;有时候甚至会引起 segfault
  2. 我厂是 OpenResty,没理由不用 OpenResty

行吧,本来就是为了跨出舒适区,不妨走远一点,OpenResty 就 OpenResty。

0. 用 OpenResty 替换 Nginx

我的系统是 Ubuntu 18.04,所以先按照 官方文档 安装最新版本的 OpenResty。

安装完成之后,可以通过 service --status-all 查看服务列表,不出意外的话,能看到 openresty 服务。

停掉 nginx,然后启动 openresty:

service nginx stop
service openresty start

此时 openresty 的配置位于 /usr/local/openresty/nginx/conf/nginx.conf,默认的 web 服务地址是 /usr/local/openresty/nginx/html/,所以我们需要修改一下,以便继续使用 /var/www/html,也就是之前 nginx 的网页文件。

1. 继续使用 nginx 的配置

修改配置的方式很简单,OpenResty 本来也兼容 nginx 的配置。对于我而言,最简单的做法是干掉 server 的部分,然后加载 nginx 的网站配置,也就是这两句:

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

此处会有个小问题:PHP 网站的配置里需要加载 snippets/astcgi-php.conf,默认使用的是相对路径,需要改成绝对路径 /etc/nginx/snippets/fastcgi-php.conf

至于其它配置,比如 gzipssl 等,酌情拷过来即可。

2. 增加配置以自动输出请求头

最后就是写 lua,这部分难度不大,基本就是普通的 lua 和 nginx lua API。写好的配置是这样的:

location / {
  access_by_lua_block {
    if ngx.req.get_method() == 'POST' then
      ngx.header['Access-Control-Allow-Origin'] = '*';
      ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS';
      ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
      ngx.header['Access-Control-Allow-Expose-Headers'] = 'Content-Length,Content-Range';
 
    elseif ngx.req.get_method() == 'OPTIONS' then
      ngx.header['Access-Control-Allow-Origin'] = '*';
      ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS';
      ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
      ngx.header['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS';
      ngx.header['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
      ngx.header['Access-Control-Max-Age'] = 1728000;
      ngx.header['Content-Type'] = 'text/plain; charset=utf-8';
      return 204;
    end
  }
  # First attempt to serve request as file, then
  # as directory, then fall back to displaying a 404.
  try_files $uri $uri/ /index.php$args;
}

首先,使用 access_by_lua_block 添加 lua 代码块。这里注意,一定要用 access_by_lua_block,不能用 content_by_lua_block,因为我们后面还要用 proxy_pass 把请求反向代理给 php-fpm 处理,而 content_by_lua_block 就直接返回了。

然后,因为要添加 headers,所以要在能输出其它内容前先输出头。所以这个代码块要放在最前面,前面不能有其它输出。

3. 总结

需求不大,代码也比较简单,不过还是花了大约一天的时间调试。将来可能会写更多的 OpenResty lua,到时候再慢慢分享咯。

另外我也搞不清楚为啥 nginx 这么流行的一个软件竟然有 if 这种问题。有知道的同学请指教。

分类
ghost

Ubuntu 配置 Nginx + Ghost

按照惯例,买好机器,登录进去。建议是境外服务器,可以省掉备案环节。但是不要干坏事哦。

1. 更新系统

apt-get update
apt-get upgrade

2. 创建 ssh-key

ssh-keygen -t rsa -b 4096 -C "my-email@meathill.com"

3. 添加 authorized_keys

使用 ssh key 登录可以大大提升服务器的安全性。

首先,将你的电脑上的公 key 添加到服务器 ~/.ssh/authorized_keys。接着编辑服务器上的 /etc/ssh/sshd_config,禁用密码登录。

ChallengeResponseAuthentication no
PasswordAuthentication no

最后重启 ssh 服务:service ssh restart

4. 安装 Node.js

按照 NodeSource Node.js Binary Distributions 的指引,安装对应版本的 Node.js—— Ghost 要求 LTS,所以目前只能用 v12。

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs

5. 安装 MySQL/MariaDB

Ghost 默认使用 SQLite 作为数据库,不过生产环境中 SQLite 性能不够,因此建议直接安装并使用成熟可靠的数据库软件。这里建议使用 MariaDB,开源免费。不同版本的 MariaDB 源可以在 MariaDB Repositories 找到,我一般使用清华的源:

sudo apt-get install software-properties-common
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] https://mirrors.ustc.edu.cn/mariadb/repo/10.5/ubuntu focal main'

# 添加完仓库和 key 之后,就可以安装了
sudo apt update
sudo apt install mariadb-server

MariaDB 的安装过程没有配置密码的环节,可以使用 重置 MariaDB root 密码 一文中介绍的方法先重置 root 密码,然后创建需要的用户和数据库。

create user 'ghost'@'localhost' identified by 'ghost';
create database `ghost`;
grant all on `ghost`.* to 'ghost'@'localhost';

6. 安装 ghost-cli

ghost-cli 是 ghost 提供的命令行管理工具,可以大大减少我们管理 Ghost 实例的时间。

# 安装
npm i ghost-cli -g

# 创建一个目录安装 ghost
cd /var/www
mkdir ghost
cd ghost

Ghost 要求我们不能用 root 用户维护实例,所以这时可能需要创建一个新用户,并赋予其 sudo 权限(方便操作目录权限之类的),我习惯命名为 ghost-admin:

adduser ghost-admin
usermod -aG sudo ghost-admin

接着,切换到 ghost-admin,安装 ghost

su ghost-admin
cd /var/www
# 改变权限
sudo chown -R ghost-admin:ghost-admin ghost
cd /var/www/ghost
ghost install

安装完成之后,执行 ghost ls,可以看到正在运行的 Ghost 实例,就是一切正常了:

+ sudo systemctl is-active ghost_fav-meathill-com
┌──────────────────┬────────────────┬─────────┬──────────────────────┬─────────────────────────┬──────┬─────────────────┐
│ Name             │ Location       │ Version │ Status               │ URL                     │ Port │ Process Manager │
├──────────────────┼────────────────┼─────────┼──────────────────────┼─────────────────────────┼──────┼─────────────────┤
│ my-ghost │ /var/www/ghost │ 3.31.5  │ running (production) │ http://my-ghost.com │ 2368 │ systemd         │
└──────────────────┴────────────────┴─────────┴──────────────────────┴─────────────────────────┴──────┴─────────────────┘

接下来,编辑 config.production.json,把数据库配置和域名配置都写进去,就基本可用了。

7. 安装并配置 Nginx

首先,配置源。目前 Ubuntu 20.04,代号 focal,命令如下:

deb https://nginx.org/packages/ubuntu/ focal nginx
deb-src https://nginx.org/packages/ubuntu/ focal nginx
sudo apt update
sudo apt install nginx

增加配置文件,并进行反向代理,即把外来的访问反向代理给 Ghost 服务:


cp /etc/nginx/site-available/default /etc/nginx/site-available/ghost.conf
ln -s /etc/nginx/site-available/ghost.conf /etc/nginx/site-enabled/ghost.conf

配置文件大体如下:

server {
    listen 80;
    listen [::]:80;

    server_name my-ghost.com;
    root /var/www/ghost/system/nginx-root; # Used for acme.sh SSL verification (https://acme.sh)

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;

    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}

最后重启 nginx 服务或者重新加载配置即可:

# 一般建议先检查一下配置有没有问题
nginx -t
service nginx restart

总结

这次主要更新了使用 ghost-cli 安装和配置的内容,服务器也改用 MariaDB,新的技术使得安装配置都更简单了。

分类
服务器端

配置 nginx 支持目录别名

我有一个小项目,我们假定它叫 up,是用 PHP 写的私人图床,部署在服务器上,域名是 up.meathill.com。因为是 PHP,不需要编译,直接 `git clone` 仓库然后配置 nginx 指过去就好。

后来想做一个在线预览的功能,是个单页应用,也放在这个仓库里,目录是 `up/fe`,项目用 Vue + Webpack 来做,源码放在 `up/fe/src`,生成的文件放在 `up/fe/dist`。

这样一来,我就需要在原本的 nginx 里配置一个虚拟目录 `/admin`,指向生成的文件。预览的文件路径就是 `/admin/${file}`,所以我还要把未命中的文件重定向到 `index.html

经过反复摸索,最终的配置如下:

{
        location / {
                # 未直接命中的请求都交给根目录里的 index.php 处理
                try_files $uri $uri/ /index.php$args;
        }

        # 默认的 nginx 1.14 + PHP 7.1 + php-fpm 配置
        location ~ .php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
        }

        # /admin 的访问指向编译后的前端资源
        location /admin {
                alias /mnt/www/uploader/fe/dist;
        }

        # 直接访问的地址解析,正则其实不必要
        location ~ "/admin/(?<page>d{8}-d{6})$" {
                alias /mnt/www/uploader/fe/dist/index.html;
                # 这里的定义很重要,默认是 application/octet-stream 会启动下载
                default_type text/html;
        }
        
        # 把不希望看到的请求屏蔽掉
        location /fe {
                return 404;
        }
}
分类
wp

开发 WordPress 的 nginx 配置

以前比较习惯放在 /etc/nginx/ 里,用 service nginx start 启动(对应 MacOS + brew 就是 /usr/local/etc/nginx/brew services start nginx)。来到我厂后,开始频繁接触 Nginx 和 OpenResty,现在觉得单独放在目录下面比较方便管理,所以改了一个出来。

配合 Makefile,方便日后使用:

  1. 启动服务 make run
  2. 关闭服务 make stop
  3. 重新加载 make reload
  4. 使用 php-fpm
  5. 代理上传资源(/wp-contents/uploads/)到真实服务器
  6. log 在本地
nginx = nginx

.PHONY: run
run:
    mkdir -p logs
    $(nginx) -p $$PWD -c nginx.conf

.PHONY: reload
reload: logs/nginx.pid all
    $(nginx) -p $$PWD -c conf/nginx.conf -t
    kill -HUP `cat $<`

.PHONY: reload
stop: logs/nginx.pid
    $(nginx) -p $$PWD -c conf/nginx.conf -t
    kill -QUIT `cat $<`
error_log logs/error.log error;
pid logs/nginx.pid;

events {
    accept_mutex off;
}

http {

    server {
        listen 9010;
        listen [::]:9010;

        include /usr/local/etc/nginx/mime.types;

        root /Users/meathill/Sites/wp-dev;

        index index.php index.html index.htm index.nginx-debian.html;

        server_name wp-dev.com;

        location / {
            try_files $uri $uri/ /index.php?$args;
        }

        location /wp-content/uploads/ {
            proxy_pass https://blog.meathill.com/wp-content/uploads/;
            proxy_set_header Host blog.meathill.com;
            proxy_ssl_name "blog.meathill.com";
            proxy_ssl_server_name on;
        }

        location ~ \.php$ {
            include /usr/local/etc/nginx/fastcgi.conf;

            fastcgi_pass 127.0.0.1:9999;
        }
    }
}

代码放在 Github wp-dev-env

分类
技术

诡异的 Mac + PHP + Nginx 问题

问题描述

这次是微信公众号开发,本身就有很多问题,也严重加剧了我调试的难度。正在做一个 WordPress 插件,功能是将公众号文章和 WordPress 互相同步。之前一切正常,突然有用户反馈抓不到公众号上的文章了。

顺便说下,插件地址:https://github.com/meathill/wp-plugin-weixin 目前支支持手动部署,完成二维码之后开始做发布版。

分类
服务器端

上 HTTPS

为啥

  1. 更安全。现在经常在公共场合工作,如果登录博客走 http,太不安全。
  2. 更稳定。国内运营商劫持简直丧心病狂,只能 https 解燃眉之急。
  3. 更有逼格。好歹是个工作十几年的老油条,自己博客连个 https 都没,浏览器一直报告“不安全”,看着太低端。

怎么上

简直容易到死。我之所以选在这个时间上 https,就是想等工具链成熟。现在何止成熟,简直傻瓜。

  1. 免费证书有好几家,我选择 Let’s Encrypt,无他,他们家名字好记。
  2. 进入官网,选择 “Get Started” 进入下一页
  3. 简单阅读一下,他们建议使用命令行工具 Certbot
  4. 听人劝吃饱饭,那就用,进入官网,选择 Software -> Nginx,System -> Ubuntu 17.04,自动跳转到使用步骤。
  5. 然后安装 Certbot
  6. certbot --nginx(其实不带参数也行,它会自动检查),启动配置向导,跟着一步一步来。它会自动配置证书、自动修改 nginx、还可以更新证书,基本上一路回车就行。
  7. 修改 /etc/crontab,配置自动更新。按理说证书有效期为90天,差不多3个月更新就行。然而 crontab 只能按月配置,这样算起来一年有好几天会出问题,所以我干脆配置奇数月的第一天更新。

问题

  1. 七牛云需要独立的证书,申请中。
  2. 友言也挂了,看来要用回 WordPress 自己的评论,或者干脆 Disqus。
分类
服务器端

Ubuntu 16.04 搭建 LNMP 开发环境

前天帮人配了台机器,未来可能还要帮人配。在学会用 Docker 之前,先写一篇记录下怎么搭建环境吧。

这篇收费!¥4.99,请阅后自觉打钱。