用 Gatsby 重构自己的博客之后,他的众多优点比如可以用 react 组件化开发,可以享受 ssr、单页应用也能 seo 等等,感觉一直很爽。 但随着 Gatsby 和它的相关插件同步升级,下载、构建失败的事情经常爆出,每一个都是时间杀手,解决一次可能要花掉一个晚上。主要是跟图片相关的几个插件 gatsby-transformer-sharp ,需要同构c构建的模块,国内网络经常下载失败。
gatsby-plugin-sharp,依赖pngquant-bin ,这个包下载也会经常出问题 pngquant-bin@5.0.1 postinstall: node lib/install.js
· Issue #97 · imagemin/pngquant-bin · GitHub、
pngquant-bin@5.0.2 postinstall · Issue #104 · imagemin/pngquant-bin · GitHub
gatsby-image,依赖 mozjpg ,mozjpeg 的编译需要 libtool automake autoconf nasm。
最近出现的的构建图片出错
construct-error.js
- child “error” fails because “error” must be an object · Issue #15242 · gatsbyjs/gatsby · GitHub
而这个 issue 依然没有 close: gatsby-plugin-sharp: WorkerError @2.6.27 · Issue #26723 · gatsbyjs/gatsby · GitHub ,试了各种 hack 方法没法解决,我只好将版本回滚并锁定。
问题是如果还有其他服务也做了依赖升级导致依赖冲突怎么办?如果以后我重装系统、更换云服务器,岂不是还要重走一遍下载安装构建的坑?万一倒时候又有新坑怎么办? 因此我开始着手把 Gatsby 项目 docker 化,做成镜像传到 docker hub。即便以后换个运行环境也可以快速使用,不用再被时间杀手折磨。
gatsby 的 docker 镜像有两个 tag 版本:
latest
- 会起一个nginx serve 静态文件,需要手动把 public 目录拷过去onbuild
- 构建并生成一个新的docker镜像这里直接用 onbuild
版本的镜像,新建 dockerfile
和 .dockerignore
在 dockerfile
中添加:
在 .dockerignore
中添加:
这里没有参考 gatsby 镜像的文档添加 public/
,因为接下来会将 gatsby 网站项目打包构建出静态资源,/public
目录即为其静态资源目录:
接下来创建镜像:
这里用 -t
打上了 latest
标签, albertaz.com
是我的镜像名。由于我在 dockerfile
同级目录创建镜像,所以最后的 .
不能省略。
整个镜像制作时间大概会持续两道三分钟,时间很快,主要原因在于 gatsby 自身的 docker 镜像使用了一个轻型的 linux 系统发行版 alpine ,整个体积极小只有 4.4 M,而我生成的网站项目镜像也只有 13 M。
接下来运行着这个镜像试试看:
镜像运行会创建一个 container,--rm
表示在 container 停止后自动清除, -p 80:80
表示把本地机器的 80 端口映射到 container 的 80 端口(也是 container 的默认端口),所以接下来可以通过浏览器打开 localhost 访问网站了。
前面提到 gatsby 镜像会在运行时起一个 nginx serve 静态文件,其实是因为镜像里也安装了一个 nginx。镜像文档里还提到了通过两种方式修改 nginx 配置: 一种是修改 Dockerfile, 使用自定义的 nginx 配置文件:
另一种是通过 docker run
命令 的 --env-file
传参:
但如果我需要加多个参数,配置更多端口、网络、启动策略等等,这个命令会越写越长,显然用一个脚本文件写这些指令会更好。不过当我在 mac 上装 docker desktop 的时候,它自带了 docker-compose,docker-compose 的意义是让我们可以编排多个 容器、不用写复杂的 shell 脚本。现在尽管只有一个容器,但依然可以借助 docker-compose,通过 yaml 配置文件的方式来运行,这个方式显然更有拓展性。
在项目根目录下创建一个 docker-compose.yml
文件:
可以看到写法更有结构性、更易读。随后就可以通过 docker-compose 运行镜像:
docker-compose 在停止运行时会自动清除 container,不用像 docker run 那样加 —rm 参数。
本地自测没问题之后,开始把创建好的镜像上传到 docker hub,当然前提是需要注册一下账号,拿到一个 docker hub 的用户名。
上传前先把本地镜像通过 docker tag
关联到 docker hub
albertaz/albertaz.com:latest
中第一个 albertaz 就是 docker hub 的用户名。
随后上传:
上传完成后,回到服务器,装好 docker 和 docker-compose。因为我依然想用 docker-compose 运行镜像,所以需要再把 docker-compose.yml
文件也上传到服务器,这个文件之前可不会被 push 到 docker hub。随后启动镜像:
服务器会从 docker hub 拉取我们配置好的网站镜像,不用再跑一遍安装构建流程了。
另外如果我网站更新了,需要更新镜像文件,利用 docker-compose 做法很简单,直接在启动命令后加上 container 名即可:
注意更新的时候原来的 container 大概率是在运行的,docker-compose 可以让 container 基于新的镜像运行,无需停止原来正在运行的 container。但如果直接通过 docker 原生命令运行,就需要先 docker container stop
停止 container,重新 docker run
,所以网站更新时会有一段无法访问的时间。这点也是选择用 docker-compose 的重要原因之一。