Gatsbyjs 是一个静态网站生成器,用它搭建的网站由一个个静态文件组成,可以直接部署在 github pages 等静态网站托管服务,这样是免费的,但出于 SEO,访问速度,自由度等方面的考虑,我还是部署到自己的服务器吧,虽然麻烦了一点,而且我刚好有一个腾讯云服务器,新用户购买也不贵,我买了三年,不想放着浪费了。
因为一个服务器可以运行 N 多个程序,每个程序所依赖的环境可能都不一样,这些环境可能互相影响呢,而且万一哪天我不爽要重装系统,那我不仅要备份程序代码,还要记住我之前折腾过的所有配置/安装过的所有依赖等等,重装系统之后要从头搭建,很烦。
所以我打算把我的 gatsby 网站 docker 化,docker 化后的网站可以比喻为一个压缩包,把这个压缩包 push 到 docker hub,重装系统后把这个压缩包 pull 下来就能马上运行,无需从头配置。如果哪天我把腾讯云换成其他云,同样只要 pull 下来就能使用,非常方便。
gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog
会创建一个 blog 文件夹,里面就是一个完整 gatsby 网站的代码;Gatsbyjs 官方提供了一个 gatsby docker image,其 github 主页有使用说明,不过 docker 新手可能看不懂,所以才有了这篇文章。
在你的本地电脑,通过命令行工具进入 blog 文件夹,npm i
之后运行 gatsby build
,得到 /public 文件夹,这个文件夹就是网站的静态文件。
接着新建一个命名为 Dockerfile 的文件,在这个文件添加以下代码:
FROM gatsbyjs/gatsby:onbuild
gatsby 官方 docker image 有两个 tag,分别是 onbuild 和 latest,onbuild 取自 ONBUILD 这个 dockerfile 命令,两个 tag 的区别就是 onbuild 多了 ONBUILD ADD public/ /pub
这行代码,所以你也可以把上述代码改成:
FROM gatsbyjs/gatsby:latest
ADD public/ /pub
接着根据这个 Dockerfile 创建 image,在命令行工具执行:
docker build -t kejiweixun.com:latest .
-t 表示 tag,即标签,我打了 latest 这个标签,注意最后的 .
不能省略,表示基于 Dockerfile 所在的目录创建 image,kejiweixun.com
是所创建的 image 的名称,你可以自定义。
接着运行这个 image:
docker run --rm -p 80:80 kejiweixun.com
image 运行之后就创建了一个 container,--rm 的作用是当这个 container 被停止后,自动清除 container,如果没有 --rm,container 被停止后会被保留。-p 的作用是把电脑的端口和 container 的端口进行映射,第一个 80 是电脑的端口,这意味着你可以在浏览器通过 localhost:80 访问这个 container,第二个端口之所以也是 80,是因为官方的 image 暴露的就是 80 端口,所以你通过本地电脑的 80 端口访问 container,其实就是访问 container 暴露的 80 端口。
最后打开浏览器,前往 localhost 这个网址就可以访问网站,这时命令行工具会实时显示 log,如果你不希望显示这些 log,可以添加 -d 选项:
docker run --rm -d -p 80:80 kejiweixun.com
gatsby 官方的 docker image 其实封装了一个叫 alpine 的 linux 系统,alpine 体积大概只有 5MB,比 Ubuntu 等 linux 系统小得多,所以通过上述方法生成的 image 体积只有 15MB。官方 image 还在这个 alpine 系统中安装了 nginx 服务器,网站的静态文件就是通过 nginx 提供给访客的。
nginx 功能强大,我们可以通过更改 nginx 的配置文件改变 nginx 的功能,gatsby 的官方 image 提供了更改配置文件的方法,方法一是在定义 Dockerfile 的时候附加配置文件,方法二是给 docker run
添加 -e 选项。例如:
docker run --rm -d -e CACHE_IGNORE=html -p 80:80 kejiweixun.com
文档显示 -e CACHE_IGNORE=html
这个选项表示 nginx 在提供 html 文件时,把 header 的 Cache-Control 值设置为 no-store
,当然这是本来就是默认配置,所以和前面的 docker run 命令没有区别。但 gatsby 官方文档建议 page-data.json
,sw.js
文件也不应该被缓存,为了不缓存 json 文件,所以把刚才的命令改为:
docker run --rm -d -e CACHE_IGNORE=html|json -p 80:80 kejiweixun.com
但为了不缓存 sw.js 文件,恐怕要通过前面提到的第一种方法实现,即在定义 Dockerfile 的时候附加配置文件,具体怎么配置,可参考 nginx-boot.sh 这个文件,以及官方说明。
如果你要更改的 nginx 配置有很多,那 docker run 这条命令可能很长,例如:
docker run --rm -d -e CACHE_IGNORE=html -e CACHE_IGNORE=html -e CACHE_IGNORE=html -e CACHE_IGNORE=html -e CACHE_IGNORE=html -e CACHE_IGNORE=html -e CACHE_IGNORE=html -e CACHE_IGNORE=html -p 80:80 kejiweixun.com
每次运行 image 都要输入这么长的命令,是不是很烦? 有两种解决方法,第一种是把 -e 改为 --env-file,把所有环境变量选项写在一个文件中执行,另一种方法是通过 docker-compose up
运行 image,而不是通过 docker run
运行。如果只是为了解决命令行太长的问题,那这两个方法都差不多,但 docker-compose 还有很多其他作用,是一个非常值得学习的知识点,所以这里说说怎么用 docker-compose。
首先要安装 docker-compose,方法请查阅官方文档,不过如果你的 mac 或 win 电脑是通过 Docker Desktop 安装 docker 的话,那不需要再单独安装 docker-compose,因为 Docker Desktop 自带了 docker-compose,如果你的服务器是 linix 系统,那通常要单独安装 docker-compose。
安装完 docker-compose 之后,在 blog 文件夹创建一个名为 docker-compose.yml
的文件,并在里面添加如下代码:
version: "3.7"
services:
kejiweixun.com:
image: kejiweixun.com
environment:
- CACHE_IGNORE=html|json
ports:
- "80:80"
和 docker run --rm -d -e CACHE_IGNORE=html|json -p 80:80 kejiweixun.com
比一比,简直一模一样。区别是 services 下多了一行 kejiweixun.com,这一行是 image 运行后 container 的名称。把一长串的命令记在一个文件中,以后再使用就简单很多了。不过 docker-compose 其实并不十分适合于 production 阶段,更适合用于 development 阶段,生产阶段可以用 docker swarm。
接着执行:
docker-compose up -d
这样 image 就运行起来了,不需要指定 docker-compose.yml 这个文件,它会自动找到同目录下的文件。要想停止这个 container 只要执行:
docker-compose down
同样不用指定文件,它会自动停止 container,并自动清除这个 container,作为对比 docker run
还要加 --rm 才能自动清除。
docker-compose 另一个显著优点是更新 container 更方便。例如你更新了网站代码,通过 gatsby build
生成了新的静态文件,然后通过 docker build
创建了新的 image,如果你是通过 docker run --rm
运行 image 的话,你需要先通过 docker container stop
停止这个 container,这段时间用户无法访问你的网站,最后通过 docker run
运行新的 image。
但如果你用 docker-compose,你不需要先停止原来的 container,准备好新的 image 之后,直接通过 docker-compose up -d kejiweixun.com
就可以让 container 基于新的 image 运行,注意 kejiweixun.com
指的是 container 的名称,即 yml 文件中 service 下那一项定义的名称,不是 image 的名称,虽然这里 image 的名称也是 kejiweixun.com。
docker-compose 还可以同时 up 多个 image,同时 down 多个 image,很好用。
通常以上操作都在本地电脑进行,确认没有问题之后,把你创建的 image 上传到 docker hub,不过要先把本地的 image 关联到 docker hub 的 image:
docker tag kejiweixun.com:latest kejiweixun/kejiweixun.com:latest
kejiweixun/kejiweixun.com:latest
前面的 kejiweixun
是你的 docker hub 用户名,后面的 kejiweixun.com
是位于 docker hub 的一个叫 kejiweixun.com 的仓库,你可以自定义,不一定要和本地的 image 名称一致,但我为了方便写成一样了。
然后通过下面的命令把 image 上传到 docker hub,如果这是你第一次 push,可能会提示你先登录,按要求操作即可。
docker push kejiweixun/kejiweixun.com
接着登录你的云服务器,按照前文所说的方法在服务器端安装 docker 和 docker-compose,最后在服务器执行:
docker run --rm -d -e CACHE_IGNORE=html|json -p 80:80 kejiweixun/kejiweixun.com
如果服务器本地没有 kejiweixun/kejiweixun.com 这个 image,它会自动从 docker hub 寻找并自动下载下来。
当然也可以用 docker-compose,但你需要先把刚才创建的 docker-compose.yml 文件上传到服务器,或者直接在服务器创建 docker-compose.yml 文件,然后:
docker-compose up -d
这时在浏览器输入你的服务器 ip 地址,应该就可以访问你的网站了,如果你的服务器已经绑定了域名,那应该也可以通过域名访问。
通过 gatsbyjs 官方提供的 docker image 可以方便地把自己搭建的 gatsby 网站通过 docker 的方式部署到自己的服务器中,例如腾讯云等等。部署完成之后,你可能还要添加 ssl,让网站可以通过 https 访问,但这不是这篇文章讨论的范围啦。