在 coding.net 上部署 Jekyll 博客
自从 coding 推出 PaaS 演示平台以及开放自定义域名之后,很多人开始尝试在 coding 上部署自己的博客,其中就有 jekyll,coding 上就有官方推荐的 jekyll-demo。但是因为这个 Demo 的 README 文档中只是简单介绍配置步骤而已,没有详细介绍原理以及灵活配置的地方,我在参照着迁移 jekyll 博客的过程中也遇到一些问题。现在写下文章,希望能够把原理理清楚。
声明:这篇文章主要是对原来的 Demo 的几个主要思路做一个补充说明,而并非 coding 演示平台使用操作的详细教程,所以在有些细节上不一定覆盖到,建议最终的部署代码需要以官方推荐的 repo 里的代码为主。
基本原理
因为 Coding 提供的演示平台是通用的 PaaS 平台,并非类似 Github 或者 Gitcafe 的 Pages 服务,所以 jekyll 部署到演示平台需要解决三个问题:
1. 运行问题,blog 需要以常规 Web 程序的方式运行;
2. 启动脚本,部署完成后自动启动服务器;
3. 自动更新,blog 内容更新 push 后能够自动生成新的页面。
第一个问题我们可以通过 rack-jekyll 解决;第二个问题通过 Coding 约定的 Procfile
文件解决;第三个问题我们通过 Coding 的 Webhook 结合脚本解决。
1. 将 Jekyll 博客变为一个在线运行的 Rack 程序
Jekyll 原本是一个用于生成静态博客站点的框架,但是为了能够在 coding 演示平台上直接运行 Jekyll 博客,我们需要一个能够在 Unicorn 服务器上运行 Jekyll 的方法。通过原来 coding 提供的 Demo,找到了一个叫 rack-jekyll 的工具。
rack-jekyll 主要的功能如其介绍:
Transform your Jekyll app into Rack application!
就是将 Jekyll 作为 Rack 程序运行。
首先,为了能够使用 rack-jekyll 以及 unicorn,我们在 Gemfile
文件(如果没有则直接新建即可)中加入:
gem "rack-jekyll"
gem "unicorn"
这两行,然后执行 bundle install
这样,我们的项目中就成功引入 rack-jekyll
以及 unicorn
了。
其次,因为 unicorn 默认会从项目根目录下的 config.ru
文件启动,再结合 rack-jekyll 的使用说明 ,我们在 jekyll 项目根目录下要创建一个包含以下内容的文件,并且名字就是 config.ru
:
# config.ru
require "rack/jekyll"
run Rack::Jekyll.new
到此,可以在命令行中 cd 到当前项目根目录,执行 jekyll build
生成站点,然后再执行 unicorn
从默认配置启动服务器,成功启动后,在浏览器中访问“ http://127.0.0.1:8080 ”就可以看到博客了。
2. 添加用于 Coding 演示平台的启动脚本
上面第一步只是解决了 Jekyll 能够以 Rack 方式运行的问题而已,但是为了部署到 coding 后,项目能够正常启动,我们还需要加入启动命令。
按照 coding 在关于 Ruby 部分的演示平台文档 中的介绍得知,coding 会查找项目根目录下的 Procfile
文件,并将里边的内容作为启动命令,当此文件不存在时,则将默认使用一下启动命令:
web: bundle exec rackup config.ru -p $PORT
按照默认启动命令的格式,我们也可以写出以下 Procfile
文件,用于部署后从 unicorn 启动项目:
web: bundle exec unicorn -p $PORT -c ./unicorn.rb
完成前面两步之后,将代码 push 到 coding 上,再从演示平台一键部署的话,就应该可以成功启动 unicorn 服务器,并且能够访问你的 jekyll 博客了。但是,如果有了新文章呢?怎么自动在站点改动后重新生成站点?
3. 使用 Webhook 在 push 后自动重新生成站点内容
coding 为用户提供了 webhook 功能,方便用户在 push 代码改动后自动 POST 请求你指定的 Web URL,你可以利用这个 URL 在程序后台完成程序的自动部署等操作。更多的介绍跟使用方法请参考 “WebHook 的内容是什么?” 以及 “WebHook 是什么?我该如何使用?”。
为了增加新的入口以接收 coding 的 Webhook 通知,我们可以在 config.ru
中添加新的路由,并且添加响应的处理脚本,这部分的内容我先直接拷贝官方推荐的 jekyll demo 的代码 后再做必要的解读:
# config.ru
require "bundler/setup"
Bundler.require(:default)
WEBHOOK_TOKEN = ENV['WEBHOOK_TOKEN']
app = Proc.new do |env|
request = Rack::Request.new(env)
response = Rack::Response.new
path_info = request.path_info
if request.content_type =~ /application\/json/
params = JSON.parse(request.body.read)
else
params = request.params
end
if request.post? && params['token'] == WEBHOOK_TOKEN
repo_url = params['repository']['url'] rescue nil
if repo_url
archive_url = "#{repo_url}/archive/master"
puts "--> updating to #{params['ref']}.."
puts `jekyll build`
`rm -rf $HOME/_posts; curl -s -L -o $TMPDIR/archive.zip #{archive_url}; unzip -qo -d $HOME $TMPDIR/archive.zip; cd $HOME; jekyll build`
puts "--> done."
else
STDERR.puts "--> error: no url field found in params: #{params}"
end
['200', { 'Conetent-Type' => 'application/json;charset=utf-8' }, ['ok']]
else
['403', { 'Conetent-Type' => 'application/json;charset=utf-8' }, [{ error: 'webhook token mismatch!' }.to_json]]
end
end
jekyll = Rack::Jekyll.new(auto: true)
run Rack::URLMap.new('/' => jekyll, '/_' => app)
首先,程序在启动时,指定了两个路由入口分别指向不同的后台程序,其中 '/'
路径指向了我们的 jekyll
程序,这个跟原来的配置目的一致;而 '/_'
路径指向了 app
这个程序。
所以,当有外部向服务器发送了一个指向 “/” 路径(比如“ http://test.codingapp.com/ ”)的请求时,服务器在内部启动了 app
的脚本。(注意,如果你希望使用别的路径名来配置 webhook 的入口,只要将下划线改成你需要的路径即可,比如: “http://test.codingapp.com/deploy”)。
app
脚本首先通过请求的 Content-Type
头信息判断请求格式,并据此从请求中提取请求参数赋给 params
变量;接着脚本验证请求的合法性,要求请求必须是 POST 方式,并且参数中的 token
参数的值必须与我们在 coding 后台中配置的 token 一致。
最后,在确认请求的合法性后,脚本先清空了当前部署的项目,然后下载解压指定分支的最新代码,并且进入项目根目录($HOME
环境变量)重新执行了 jekyll build
命令以重新生成静态站点,见代码:
`rm -rf $HOME/_posts; curl -s -L -o $TMPDIR/archive.zip #{archive_url}; unzip -qo -d $HOME $TMPDIR/archive.zip; cd $HOME; jekyll build`
其中值得一提的是,archive_url
是在前面代码中拼接而来的链接:
archive_url = "#{repo_url}/archive/master"
请注意其中硬编码的部分 "archive/master"
,其中的 master
指定了是 master
分支上的代码压缩包的路径,所以假如你需要从 master 分支外的分支部署代码,请务必记得将 master
改为对应的分支名,比如我的部署分支是 coding-pages
,那我这里的代码就应该改为:
archive_url = "#{repo_url}/archive/coding-pages"
完成 webhook 处理脚本后,需要重新 push 代码并且重新在演示平台部署一次,以使 config.ru
文件里的代码生效。至于如何配置 webhook ,直接参照 coding 的官方文档即可。
总结
以上的三点主要是对在 coding 上部署 jekyll 博客的关键思路的说明,通过这三点,相信你再去看原来的 README 的时候,应该就能很快理解为什么需要配置 WEBHOOK_TOKEN
环境变量以及为什么要配置 webhook 的 URL 为类似 “http://host/_” 这么奇怪的链接了吧?除此之外,你也可以根据你的需要将脚本中的代码分支从 master
改为你所需要的目标分支了。
其实用 unicorn 运行 jekyll 项目的原理还是非常简单的,知道了这些之后,将你的已有 jekyll 项目直接迁移到 coding 甚至是其他 PaaS 平台上就不是件麻烦的事了。
其他联想
- Octopress 博客是在 jekyll 的基础上封装而来的更高级也更方便的静态站点框架,所以按照上面的原理,将已有的 octopress 项目部署到 coding 平台上,应该也不是件难事。
- Octopress 本身支持另外一种部署方式,就是本地生成静态站点之后,直接执行
rake deploy
将生成后的静态站点 push 到指定的远程 repo 或者指定的分支上,从这个角度考虑,其实也可以为 jekyll 实现类似的脚本,结合 coding 演示平台的 静态站点部署 ,就可以直接部署 jekyll 博客了,这种方式就省去了 unicorn 服务器等的配置了,也不需要再使用 webhook 重新生成站点了,而且纯静态站点的方案的最大优点就是,特别节约内存。这种方案只是构想,但是值得一试。如果哪位朋友尝试成功了,请记得在评论里回复一下。