服务器开机记录1: Caddy & ghost in Docker

我劝自己不要太浮躁廖。

为后面装威土锐豪华套装作准备,挑了个Caddy来作Web Server,因为它有Caddyfile,看上去对我这种零基础的来讲最简单😇。看到Caddy也有Docker镜像,正好久仰Docker大名,这次就尝试了一下。

实际上用Docker来部署Caddy还有ghost都没什么难的,但是零基础想一口气吃成个胖子还是很愚蠢的……起码看了两行介绍都不知道Docker容器的通信机制就自以为是地把给书扔了准备上手这样的行为属实弱智了。

Docker

Install Docker Engine on CentOS
Instructions for installing Docker Engine on CentOS
Docker的安装过程基本按照官方文档来就好了

在Docker中启动的容器都会获得一个自己的IP地址,而不是占用本地的端口来通信。并且它们都会被分配到一个子网中,通过虚拟网桥才能与宿主网络进行通信。
没有理解这一点的话,后面的步骤都难以进行。我就是因为自己太菜了,甚至未曾接触过这种概念,所以花了很多时间才把Caddy反代的服务配置好。

首先Docker每次启动一个容器,都会给他分配一个子网下的IP,但这个IP并不是固定的,而是按照启动顺序自动分配的。如果说你想要容器之间能够互相通信(譬如说像我这样,用Caddy反向代理另一个在Docker容器里的网站),那我们最好是通过--ip参数在启动容器时分配给它一个固定IP,这样就避免了可能的IP地址变动问题。不过Docker默认的子网只支持自动分配,所以我们要先自己创建一个子网。

docker network create --subnet 172.64.0.0/16 \
-o "com.docker.network.bridge.name"="dockerHub" \
dockerHub

ifconfig命令我们可以看到,除了默认的docker0之外,又多了我们新添加的子网的网桥dockerHub。它的IP地址被分配为172.64.0.1,之后和宿主网络通信的时候会用到它。
注意默认的docker0拥有子网172.17.0.0,所以创建子网的时候不要取这个区间的。

Caddy

准备完成,然后就到安装Caddy的时候了。这里介绍的是Caddy v1而非最新的v2。因为我就是为了偷懒才选了Caddy,而v2不是很成熟,不像v1一样有很多作业抄。

先把Caddy v1的镜像拖下来:

docker pull abiosoft/caddy

(注意如果用docker pull caddy的话会下载最新的v2)

在启动Caddy之前,我们先来写它的配置文件Caddyfile:

nano Caddyfile

当然,由于它的服务对象还没搭起来,所以暂时还没有太多东西要填:

your.domain{
	tls your@e.mail
}

tls后面填写注册你域名的邮箱,Caddy会帮你自动申请和续期Let's Encrypt的SSL证书;如果你有现成的证书,这里也可以写成tls /path/yourCrt.pem /path/yourKey.pem;或者也可以填tls internal启用自签证书。

接着启动一个Caddy容器:

docker run -d \
--name=yourCaddy --restart=always \
--ip=172.64.0.2 --net=dockerHub \
-v $(pwd)/Caddyfile:/etc/Caddyfile \
-v $HOME/.caddy:/root/.caddy \
-e ACME_AGREE=true \
-p 80:80 -p 443:443 \
abiosoft/caddy

这样这个Caddy的容器就被分配到了我们创建的子网下的172.64.0.2这个IP并启动了。由于我要让Caddy管理我的HTTP/HTTPS流量,这里用-p参数将本机的80和443端口映射到此Caddy容器的80和443端口上。

注意一下-v $HOME/.caddy:/root/.caddy这行对于希望Caddy自动续签证书的用户很重要,如果不把Docker容器外的地址挂载进容器里的/root/.caddy,你重启容器时它就会重新申请证书。但是Let's Encrypt的证书申请冷却时间长达720hr,所以最好还是避免它发生。

好吧,其实是我先试了Caddy v2,但是因为我对Docker的理解有问题,所以一直没成功过。后来想控制一下变量就换了现成作业很多的v1,但理所当然地还是没做出来。最后我才意识到自己一开始完全就没理解Docker容器的通信机制,就在v1的基础上改好了,目前还没有用回v2去体验一下效果。看官方的说明文档好像是说新的reverse_proxy取代了proxy并且不用额外加一行transparent了。

ghost

ghost就是我现在正在使用的Blog服务,它也有提供封装好的Docker镜像。

从Docker下载ghost的镜像

docker pull ghost

接着启动一个ghost容器:

docker run -d \
--name=yourGhost --restart=always\
--ip=172.64.0.3 --net=dockerHub \
-v $(pwd)/ghost/content:/var/lib/ghost/content \
-e url=https://your.domain \
ghost

和上面类似,这个名为yourGhost的ghost容器被分配到了172.64.0.3。由于接下来要用Caddy反向代理它,所以我们不需要做端口映射来进行直接访问。

……不过我们还是要知道一下它在监听哪个端口:

docker ps #加入-a参数可以查看已经停止的容器

现在我们能看到有yourCaddy和yourGhost这两个容器正在运行中,而其中的Caddy监听全局的80,443端口及其本地的2015端口;而ghost只监听其本地的2368端口。

现在我们来更新Caddyfile:

your.domain{
	tls your@e.mail
	proxy / 172.64.0.3:2368{
		transparent
	}
}

完了我们重启一下Caddy:

docker restart yourCaddy

重启完访问我们的域名就能马上看到我们的小可爱ghost成功上线惹,尽管……

ghost in Docker 之 覆 灭
A Farewell Song for ghost0, Forever.
这里的ghost很快就结束了它短暂的一生

前面Caddyfile的逆向代理地址我一开始填了localhost:2368,因为网上的很多作业都是填的localhost,而ghost是在监听2368端口的。但正如前面讲过的,这是大错特错了。因为对yourCaddy这个容器来讲,它本身就是一个host,localhost指向的正是它自己,那当然是不可能找到另一个host,也就是yourGhost上的任何端口了。实际上你完全可以让这两个容器监听相同的本地端口,因为它们根本不在一个host上,自然也不会冲突。因此,我们要填的地址是之前yourGhost被分配到的地址172.64.0.3,而不是localhost

网上的作业之所以都写localhost,是因为它们都被直接放在服务器里,只是在监听不同的端口。而Docker却是把Caddy和ghost放在两个不同容器里,将其作为两个独立的虚拟host来看待。

梳理完一遍再回来看,其实这个过程是非常简单的,但是由于本人基础知识了解甚少又没有静下心来学习,就只能后面花大量时间来还债了。不过这个自己探索和实践的过程还是相当有趣的,大概就像三岁小孩搭积木的那种成就感吧XD