零、为什么要折腾

在此之前的半年里我的网站、V2ray、Ghost博客都是直接部署在机器上的,部署起来很麻烦,部署后软件升级也很麻烦(尤其是Ghost)。

Docker是一个很有趣的东西,简单点说我希望用Docker将以上的部件解耦后部署,哪个部件出问题就替换哪个部件。

推荐一些工具

  1. 本地SSH客户端

    SSH乃远程登录操作VPS的必备工具,因此各平台的SSH工具都很多,例如Windows平台的Xshell,安卓平台我经常使用的JuiceSSH,iOS平台我经常使用的webSSH。如果大家有更好的软件请推荐给我一下哦~

    其实我在完成这篇文章所记录的工作时并没有使用上述任何一个软件,我用的是win10的Windows Subsystem for Linux (WSL),也就是在Windows上运行的Linux子系统(因此有人戏称Windows10是最好的Linux发行版2333)。我的WSL安装了Ubuntu18.04,于是可以使用Ubuntu自带的ssh命令完成操作。

    WSL自带的shell很难用,建议使用第三方shell,推荐一款叫做Cmder的开源软件,至于WSL和Cmder怎么配置,这里就不展开了,请询问谷歌娘吧。

  2. 远程文件传输工具WinSCP

    支持多种远程文件传输协议,可以很轻松地把网站的静态文件从本地拖到VPS上。

一、准备VPS

我目前选择的是Vultr家的VPS,为了求稳而选择欧洲的节点,因为不玩游戏所以延迟大一点也没关系(平均延迟为2000~3000ms)。

1. 购买VPS

因为不是重度用户,所以最低配置就可以了。我选的是1 vCore CPU,512MB RAM,20GB SSD,500GB bandwidth。

选择操作系统:Ubuntu 18.04 LTS Server x64

2. 配置防火墙

在VPS的设置页面配置防火墙,对外仅开放以下三个周知端口:

传输层协议 端口号 备注
TCP 22 SSL
TCP 80 HTTP
TCP 443 HTTPS

注:开放80端口仅仅用于设置301跳转,目的是强制将http流量转到https上。

3. 购买域名

我已购买本网站的域名bufbrane.com,将DNS解析至VPS的IP地址上。暂时没有启用IPv6的计划。

4. 添加一个用户到root组

直接用root用户操作Linux具有一定的危险性,因此需要一个具有root权限的普通用户。例如我要添加一个叫bufbrane的用户(以下操作均以root用户执行):

adduser bufbrane

然后修改/etc/sudoers文件,找到如下一行内容:

root ALL=(ALL)	ALL

在其下面添加一行,照抄上面的格式,把root换成自己的用户账户,如下:

root ALL=(ALL)	ALL
bufbrane ALL=(ALL)	ALL

修改完毕后注销,重新登录用户账户,即可使用sudo命令进行提权。

5. 安装Docker

因为VPS在国外,因此Docker安装速度很快。如果在国内可以用一些Docker镜像来加速。

# 安装docker
wget -qO- https://get.docker.com/ | sh
# 将docker加入root组
sudo usermod -aG docker bufbrane

二、用Docker部署各项服务

我只买了一台VPS,也没有大规模部署的需求,因此我希望将Nginx、V2ray、Ghost解耦成三个模块,作为三个相互独立的容器运行,升级时仅升级各自单独的部分。因此我放弃了用Dockerfile定制镜像的方式,而是直接拉取各自的官方镜像配置使用。

我的想法是用Nginx做反向代理,所有访问请求通过HTTPS的443端口进入然后由Nginx分流到网站、V2ray容器和Ghost容器,免去了为网站、V2ray和Ghost分别配置SSL的麻烦。

后续打算手动编译Nginx以开启对TLS 1.3的支持。

1. 在Docker中部署Nginx

Nginx是一款优秀的web服务器,因为其在高并发条件下的性能优异而经常用做反向代理、负载均衡器。

(1) 拉取Nginx镜像

拉取Nginx官方镜像:

docker pull nginx

(2) 建立存储目录

在适当路径下建立如下目录结构:

nginx
 ├─cert(存放证书和私钥)
 | ├─域名A
 | | ├─域名A.cer
 | | └─域名A.key
 | └─域名B
 |   ├─域名B.cer
 |   └─域名B.key
 ├─conf(存放各网站配置文件)
 | ├─网站A.conf
 | └─网站B.conf
 ├─logs(存放日志文件)
 ├─www
 | ├─网站A根目录
 | └─网站B根目录
 └─nginx.conf(nginx主配置文件)

可以使用如下命令:

cd ~ # 我以/home/bufbrane目录为当前目录,请根据实际情况修改
rm -rf nginx
mkdir nginx && cd nginx
mkdir cert conf logs www
cd ..

注意:下文使用shell内部变量$PWD指代当前目录。

(3)用SSL保护自己的网站

申请免费SSL证书可以使用GitHub上的这个项目:acme.sh。将申请到的证书和私钥放在$PWD/cert目录下。

可以使用standalone server方式来获取证书。此方式原理为:acme.sh在本地监听80(或443)端口,CA通过DNS解析访问服务器的80(或443)端口验证域名与服务器的关联。此方法需要临时关闭Nginx。

注:上述方式存在漏洞,当DNS管理的密码被盗,别有用心之人篡改DNS记录后也可以用上述方式获得证书,之后再搞中间人攻击就非常简单了。

(4) 配置Nginx

Nginx配置反向代理,其中V2ray使用1080端口,Ghost使用2368端口。Nginx的配置文件写法可以参考这篇文章:Nginx配置文件nginx.conf中文详解

如果手头没有Nginx的默认配置文件,可以这样获取:

# 创建一个临时容器(获取配置文件)
docker run -d --name temp-nginx nginx
# 将临时容器中的配置文件复制出来
docker cp temp-nginx:/etc/nginx/nginx.conf $PWD/nginx/nginx.conf
# 停止临时容器
docker container stop temp-nginx
# 删除临时容器(必须先停止再删除)
docker container rm temp-nginx

nginx.conf放在$PWD/nginx目录下,将每个网站的配置文件*.conf放在$PWD/nginx/conf目录下。

(5) 上传网站

Windows平台可以使用WinSCP这个工具。将网站的根目录放在$PWD/www目录下。

(6) 部署容器

正式部署Nginx容器,绑定主机的80和443端口监听,并将本地的文件目录挂载到容器上。

docker run --name nginx --net=host \
-p 80:80 -p 443:443 \
-v $PWD/nginx/conf:/etc/nginx/conf.d \
-v $PWD/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v $PWD/nginx/www:/var/www:ro \
-v $PWD/nginx/logs:/var/log/nginx \
-v $PWD/nginx/cert:/etc/nginx/cert \
-d nginx
# 命令解释:
# docker run	创建docker容器并运行
# -p port:port	端口映射,本地端口:容器端口
# --name 		设置容器名称
# -v path:path	将本地目录挂载到容器指定目录
# -d nginx		在后台运行nginx镜像
# --net=host	使用主机的网络命名空间

(7) 管理容器的一些命令

# 查看容器运行情况
docker ps
# 查看容器列表
docker container ls
# 停止容器(此处nginx为容器名而非镜像名,下同)
docker container stop nginx
# 移除容器(必须先停止再移除)
docker container rm nginx

2. 在Docker中部署V2ray

V2ray是一个科学上网软件,功能强大但配置复杂,对新手很不友好。这里我并不打算用各种一键配置脚本来安装V2ray,而是自行定制我所需的功能。

(1) 获取V2ray镜像

v2ray官方提供了Docker镜像v2ray/official,获取镜像:

docker pull v2ray/official

但是该镜像自2019年2月之后就不再更新(截至2019年4月),建议使用官方Dockerfile自行构建最新版镜像.

(2) 建立存储目录

在适当路径下建立如下目录结构:

v2ray
 ├─logs(存放日志文件)
 ├─Dockerfile(官方Dockerfile)
 └─config.json(v2ray配置文件)

可以使用如下命令:

cd ~ # 我以/home/bufbrane目录为当前目录,请根据实际情况修改
rm -rf v2ray
mkdir v2ray && mkdir v2ray/logs

(3) 配置V2ray

V2ray的配置可以参考官方网站或者白话文教程
(以上两个网站需科学上网,请自行解决先有鸡还是先有蛋的问题。)

将配置文件config.json放入$PWD/v2ray目录下。

(4) 部署容器

docker run --name v2ray -p 1080:1080 --net=host \
-v $PWD/v2ray/config.json:/etc/v2ray/config.json \
-v $PWD/v2ray/logs:/var/log/v2ray \
-d v2ray/official

如果是自行构建镜像,将官方Dockerfile放入$PWD/v2ray目录下,然后构建镜像,再部署容器:

# 构建镜像
docker build -t v2ray/bufbrane:latest .
# 部署容器(注意最后一行的镜像名,应当是自行构建的镜像名)
docker run --name v2ray -p 1080:1080 --net=host \
-v $PWD/v2ray/config.json:/etc/v2ray/config.json \
-v $PWD/v2ray/logs:/var/log/v2ray \
-d v2ray/bufbrane

3. 在Docker中部署个人博客Ghost

参考了这篇文章:Run your blog with Ghost, Docker and LetsEncrypt

(1) 拉取Ghost镜像

docker pull ghost

(2) 建立存储目录

在适当路径下建立如下目录结构:

ghost
 └─content(存放Ghost的网站内容)

可以使用如下命令:

cd ~ # 我以/home/bufbrane目录为当前目录,请根据实际情况修改
rm -rf ghost
mkdir ghost && mkdir ghost/content

(3)部署容器

Ghost默认端口为2368,需要将容器的2368端口绑定到主机的127.0.0.1:2368端口以接收Nginx传来的HTTP流量。

docker run --name ghost -p 127.0.0.1:2368:2368 \
-e url=https://bufbrane.com \
-v $PWD/ghost/content:/var/lib/ghost/content \
--restart=always \
-d ghost

(4) 配置Ghost

Ghost通过网页端进行配置。首先打开Ghost的主页地址,例如本博客的地址为https://bufbrane.com。确认博客可以正常打开之后,在路径后边添加/ghost/,即https://bufbrane.com/ghost/,注册用户账号并进入Ghost的后台管理页面。

至此所有工作完成。