如何把 gatsbyjs 网站通过 docker 部署到云服务器

作者: 科技微讯

日期:

为什么要通过 Docker 部署

Gatsbyjs 是一个静态网站生成器, 用它搭建的网站由一个个静态文件组成, 可以直接部署在 github pages 等静态网站托管服务, 这样是免费的, 但出于 SEO, 访问速度, 自由度等方面的考虑, 我还是部署到自己的服务器吧, 虽然麻烦了一点, 而且我刚好有一个腾讯云服务器, 新用户购买也不贵, 我买了三年, 不想放着浪费了.

因为一个服务器可以运行 N 多个程序, 每个程序所依赖的环境可能都不一样, 这些环境可能互相影响呢, 而且万一哪天我不爽要重装系统, 那我不仅要备份程序代码, 还要记住我之前折腾过的所有配置/安装过的所有依赖等等, 重装系统之后要从头搭建, 很烦.

所以我打算把我的 gatsby 网站 docker 化, docker 化后的网站可以比喻为一个压缩包, 把这个压缩包 push 到 docker hub, 重装系统后把这个压缩包 pull 下来就能马上运行, 无需从头配置. 如果哪天我把腾讯云换成其他云, 同样只要 pull 下来就能使用, 非常方便.

前提

  • 在你的本地电脑和云服务器安装 docker, 不同系统有不同的安装方法, 可参考 docker 官方的安装说明;
  • 了解基本的 docker 概念, 基本的命令行, 可参考 docker 的 quick start;
  • 创建一个 gatsbyjs 网站, 如果你还没有, 可以用 gatsbyjs 的各种 starter, 例如执行 gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog 会创建一个 blog 文件夹, 里面就是一个完整 gatsby 网站的代码;

第一步: 定义 dockerfile 并运行 image

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

第二步: 对 image 进行配置

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 这个文件, 以及官方说明.

第三步: 使用 docker-compose 运行 image

如果你要更改的 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 访问, 但这不是这篇文章讨论的范围啦.