在去年五月份的时候,新浪免费图床做了防盗链机制,羊毛就不能褥了。个人博客不挂图片忍忍也就过去,但在某些时候还是不便。后来我采用 docker 镜像 + 开源图床系统 lychee-docker 的方式,在自己的阿里服务器上搭建了一个简易图床。时移近一年,使用起来没有什么不对劲的地方。但当时我知道的东西还是太少,做了许多不规范的操作,现在想弥补。
我基于前文(当新浪防盗链时,我在想些什么?)在虚拟机里还原了过往操作,并将在此基础上弥补之前的不足。其用意是:倘若你不幸参看我写的教程部署了图床系统,那么我带你入坑的,我负责把你带出来。
关于谷歌对 HTTP 协议越来越不友好的新闻屡见不鲜,而作为增加了加密层的 HTTPS 协议已经出来多年。不论是跟风还是为保数据安全,将 HTTP 更换至 HTTPS 都显得必要。
如果你的网站还不是 HTTPS,浏览器市场的霸主 chrome 就会负责任的告诉游客:当前您访问的网站不安全。
所以第一步要做的就是,把 lychee-docker 原有的 HTTP 修改为 HTTPS。这其实很简单,因为图床系统使用 nginx 做代理,我们只需要修改 nginx 配置即可。
在动手之前,我们还需要准备 HTTPS 证书。譬如我的服务器与域名都是阿里那儿买的,于是证书索性也从阿里购买(SSL证书)。对于个人站点来说,所谓“购买”就是申请,不用花钱。当然,你也可以去其他机构获取证书,我既不曾尝试,便不再妄言,心中有疑惑可以向搜索引擎倾述。
我为方便本地演示,需要做一个伪证书,否则不能开启 HTTPS。
$ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-cert.key -out localhost-cert.pem
执行上述命令之后,在当前目录下就会生成 localhost-cert.key, localhost-cert.pem 两个文件。现在将其拷贝进图床所在的 docker 容器中。
$ docker cp localhost-cert.pem [container_id]:/etc/nginx
$ docker cp localhost-cert.key [container_id]:/etc/nginx
接着进入容器,修改 nginx 的配置。使其支持 HTTPS。
$ docker exec -it [container_id] bash # 进入 docker
(docker) $ vi /etc/nginx/sites-enabled/lychee
替换为以下内容。
server {
listen 443 ssl;
server_name localhost;
ssl_certificate localhost-cert.pem; # 注意 1
ssl_certificate_key localhost-cert.key; # 注意 2
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root /var/www/lychee;
index index.php;
client_max_body_size 20G;
access_log /var/log/nginx/lychee.access.log;
error_log /var/log/nginx/lychee.error.log;
location / {
index index.php index.html index.htm;
}
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
expires max;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
include php.conf;
}
server {
listen 80;
server_name 192.168.111.136:5121; # 注意 3
rewrite ^(.*) https://$server_name$1 permanent;
}
你可以仔细对比上述内容和原来 /etc/nginx/sites-enabled/lychee 文件中的内容。其实就是把 server 中的 80 修改为 443 ssl
,同时添加一系列 ssl 的设置。如果你有自己的 HTTPS 证书,你需要替换掉 localhost-cert.pem 和
localhost-cert.key,一定要确保文件路径正确。
最后在末尾添加监听 80 端口的 server,这样做是为了同时支持对 80 端口的访问,但并不能让用户真去访问 80 端口。rewrite ^(.*) https://$server_name$1 permanent
表示所有访问 80 端口的请求,都会自动跳转到 HTTPS 监听的端口。在 80 sever 中,需要留意 server_name 对应的值,这里写入的是容器外的 ip:port。域名也行,比方说我的域名是 youguanxinqing.xyz,所以我图床中 80 端口对应的 server_name 是:
server_name youguanxinqing.com:5121
在前文中我们用 5120 映射容器 80 端口,而 5121 映射容器中的 443 端口。
最后重启 nginx。
(docker) $ supervisorctl
到了这里,别以为就大功告成。用 docker ps
命令可以看到,容器与宿主机之间仅有一个端口映射:5120:80。我们还需要添加 5121:443,肯定不能去重新生成一个容器,所以涉及到修改 docker 配置。需要先把宿主机中的 docker 主进程停下来。
$ systemctl stop docker
$ cd /var/lib/docker/containers/[container_id]*
$ vim hostconfig.json
*
。在 hostconfig.json 文件中找到 PortBindings:"PortBindings":{"80/tcp":[{"HostIp":"","HostPort":"5120"}]}
。我们根据 80 端口的映射格式,添加一个 443 端口(其实就是把内容复制一遍,修改一下端口值就好)。为方便你观看,我将关键内容缩进之后呈现如下。
"PortBindings":{
"80/tcp":[{"HostIp":"","HostPort":"5120"}],
"443/tcp":[{"HostIp":"","HostPort":"5121"}]
}
保存退出,同级目录下打开 config.v2.json,找到"ExposedPorts":{"80/tcp":{}}
。以同样的方式添加一个 443。
"ExposedPorts":{
"80/tcp":{},
"443/tcp":{}
}
启动 docker,启动容器:
$ systemctl start docker
$ docker start [container_id]
如果操作无误,在执行 docker ps 命令后,你应该可以看到输出信息中有这样一段内容。有,则说明成功:
0.0.0.0:5120->80/tcp, 0.0.0.0:5121->443/tcp
现在访问图床就应该能看到协议是 HTTPS 了,即使你访问是 http://$your_host:5120 也会跳转到 https://$your_host:5121。
由于我使用的是本地测试环境,证书是未经认证的伪证书。所以访问的时候前面还是有“不安全”三个字。实际场景应该呈如下这般:
这是一个可选项,但我建议你选择。
照理说更换为 HTTPS 已经很 OK 了,安全性也有了,“小锁”也是贼好看。但 HTTPS 其实只是 HTTP + SSL,在 HTTP 外面套了一层 SSL 而已,使用的还是 HTTP1.1 协议——这玩意儿很落伍了。如果你不怕麻烦,为什么不来试试 HTTP2 呢?相较于 HTTP1.1 来说,HTTP2 允许信道复用,分帧传输,头信息压缩,从传输效率上可以嘲讽 1.1 小弟弟。
要想容器中的 nginx 支持 HTTP2 需要源码编译一个版本高一点的 nginx。lychee-docker 中的 nginx 为 1.4.6,而如今最新稳定版本是 1.16.1。这是下载地址,毕竟是外网,如果你访问速度太慢可以从我的网盘下载(链接放在文末)。如果你太懒,不想编译也是可以的,我已将编译好的可执行文件放在网盘中。
注意了,nginx 有个蛋疼的地方,如果想无缝替换 nginx 可执行文件,就必须使用相同的参数,所以需要进入容器获取 nginx 配置时的参数。命令也很简单。
(docker) $ nginx -V
“configure arguments: ” 后面就是我们要用到的配置参数,需要记录下来。回到宿主机,开始我们的源码编译之旅:
$ tar xf nginx-1.16.1.tar.gz
$ cd nginx-1.16.1
$ ./configure ... # ... 表示之前记录的那串参数
回车之后大概率你会遇到这样一个错误:./configure: error: invalid option "--with-http_spdy_module"
,这是因为新版的 nginx 不支持该选项,需要将此参数替换为 --with-file-aio --with-http_v2_module
。
继续回车你还会遇到错误,类似于这样的一系列错误 ./configure: error: the HTTP XSLT module requires the libxml2/libxslt
。这是因为你环境上缺少编译时用到的依赖。我暂无法告诉你需要安装哪些依赖,但你可以参考 Linux Nginx安装以及可能出现错误 这篇文章,几乎涵盖了所有需要安装的依赖。
当 configure 成功后你能看到输出信息如下。
开始编译 nginx。编译成功后可执行文件在 objs 目录下,你可以 ll
进行查看。
$ make
$ ll objs/nginx
我们需要先去备份容器中的 nginx。
(docker) $ mv /usr/sbin/nginx /usr/sbin/nginx.old
回到宿主机将刚刚编译好的 nginx 拷贝到容器中:
$ docker cp objs/nginx [container_id]:/usr/sbin/
进入容器重启 nginx。重启命令在上面贴过图,这里就不重复了。很遗憾,nginx 进程会起不来。这是因为 nginx 链接的动态库在容器中没找到。我整理了 supervisor 错误日志中的输出信息,一共需要六个动态库。
/usr/sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libssl.so.10: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libcrypto.so.10: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libgd.so.2: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libjpeg.so.62: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libpng15.so.15: cannot open shared object file: No such file or directory
这里我说明一下,我的宿主机环境是 centos7,而 docker 容器中是 ubuntu 14,我直接将这些需要的动态库拷贝到容器的 /usr/lib 目下,发现是可行的。由于还涉及到动态库软链接,避免累述,我把这些动态库整理之后放在网盘里,请自行下载。将动态库拷贝到容器的 /usr/lib 目录下,修改 /etc/nginx/sites-enabled/lychee 文件,进入 supervisorctl 启动 nginx。
$ docker cp [动态库] [container_id]:/usr/lib
(docker) $ vi /etc/nginx/sites-enabled/lychee
# listen 443 ssl;
# 修改为:
# listen 443 ssl http2;
倘若一切成功,访问页面能看到请求的协议是 h2,也就是 http2 的缩写。
在升级 nginx 过程中,我遇到的所有问题已经在前面一一说明了。但不同的人操作会遇到不同的问题,这在部署环境时屡见不鲜。一旦遇到上述之外的问题,请查看容器中 nginx 和 supervisor 的日志信息,根据错误内容到搜索引擎中寻找答案。
链接:https://pan.baidu.com/s/1rjW5ysRD_3EQinvkIFl7ng 提取码:yvt7