前些日子的 Hexo 自動(dòng)化構(gòu)建折騰起來真心有趣,但是速度好慢窒所,幾十篇文章從構(gòu)建到壓縮處理需要等待至少好幾秒的時(shí)間鲸拥,構(gòu)建時(shí)內(nèi)存占用也很高,雖然只是一時(shí)但就是讓人興致大減掉分。
所以肯定得換構(gòu)建引擎俭缓,然后就是 Hugo 了,Go 語言編寫酥郭,速度沒得說华坦,問題是擴(kuò)展功能不多,所以今天可是折騰了一天不从,笑惜姐。
實(shí)現(xiàn)效果:
- 本地保存即時(shí)全自動(dòng)發(fā)布。
自動(dòng)化流程:
- 本地編輯器保存,webdav (或者其他你喜歡的方式)服務(wù)自動(dòng)上傳服務(wù)器歹袁。
- 服務(wù)器監(jiān)測(cè)到有文件上傳坷衍,啟動(dòng)圖片壓縮程序,自動(dòng)壓縮圖片条舔。
- 圖片壓縮之后枫耳,啟動(dòng) Hugo 構(gòu)建頁面。
- Hugo 構(gòu)建結(jié)束孟抗,啟動(dòng) Gulp 壓縮網(wǎng)頁資源文件迁杨。
- 構(gòu)建任務(wù)結(jié)束,推送到所有服務(wù)器夸浅。
- 最后由各個(gè)節(jié)點(diǎn)的 Nginx 服務(wù)器向外展示網(wǎng)站仑最。
為什么沒有部署到 Github?也沒有上傳圖片到 CDN帆喇?
- 因?yàn)?Github 不支持綁定域名的站點(diǎn)使用 HTTPS 訪問;
- 我服務(wù)器多亿胸,自動(dòng)同步坯钦,普通 CDN 太慢。
為了簡(jiǎn)化初次部署的繁瑣侈玄,以上操作只需要一行命令即可啟動(dòng)婉刀。在此之前還是先解釋一下整個(gè)流程的原理。
一共兩個(gè)鏡像:
REPOSITORY SIZE
zuolan/hugo 19.3 MB
zuolan/hugo:minify 79.73 MB
1. 自動(dòng)上傳服務(wù)器
這一步通過 Nextcloud 實(shí)現(xiàn)序仙,我日常就是用這種私有云盤突颊,所以這一步我跳過,如果你需要實(shí)現(xiàn)自動(dòng)上傳潘悼,可以考慮其他方法律秃。比如 scp、rsync 之類的治唤,Windows 下自動(dòng)同步的工具更是數(shù)不盡啦棒动。
2. 主鏡像:zuolan/hugo
整合功能包括:
2.1. 服務(wù)器監(jiān)測(cè)到有文件上傳
這一步的工具就是 inotifywait 了,已經(jīng)整合到容器中宾添,當(dāng)數(shù)據(jù)卷有變動(dòng)就會(huì)觸發(fā)一系列動(dòng)作船惨。
2.2. 圖片壓縮程序
就是一個(gè)腳本,自動(dòng)壓縮圖片缕陕,使用的是 TinyPNG 的服務(wù)粱锐,雖然 Linux 有不少圖片處理程序,但是壓縮算法不好扛邑,這里使用的在線壓縮服務(wù)非常不錯(cuò)怜浅,這個(gè)功能也已經(jīng)整合到容器中。
因?yàn)槭窃?sh 中執(zhí)行的腳本鹿榜,平時(shí)在 bash 和 zsh 養(yǎng)成的習(xí)慣寫法在 sh 中不一定有效海雪,所以暫時(shí)先把這部分的腳本寫死放進(jìn)了容器中锦爵,等明天完善了再把這部分代碼丟上來。
2.3. Hugo 構(gòu)建頁面
這個(gè)作為整個(gè)流程最關(guān)鍵的一步奥裸,近百篇文章耗時(shí)也只有幾十毫秒险掀。
構(gòu)建一般不會(huì)出錯(cuò),但是如何觸發(fā)下一步壓縮網(wǎng)頁文件就是讓我折騰了一天的關(guān)鍵點(diǎn)湾宙。
劃重點(diǎn)時(shí)間
如何觸發(fā)下一步壓縮構(gòu)建好的網(wǎng)頁文件呢樟氢?
首先想到的就是使用前端打包工具執(zhí)行,但是一個(gè) Node.js 環(huán)境就會(huì)使我的主鏡像體積翻了一番侠鳄,這點(diǎn)不能忍埠啃,我要努力把主鏡像壓縮到僅有 6 MB 的存儲(chǔ)體積。
所以把 Hugo 和 Gulp 放一塊是不可能的伟恶,畢竟不是每個(gè)人需要壓縮功能碴开。
所以就想到了在容器中運(yùn)行容器,但是需要在容器中裝 Docker 環(huán)境博秫,鏡像體積必然破百 MB潦牛,而且每次容器重啟還得重新拉壓縮鏡像,太二挡育,所以不予考慮巴碗。
那,就使用 docker-compose 吧即寒,通過掛載 docker.sock 與宿主機(jī)通信橡淆,避免了上面的問題,試了一下母赵,只有 22 MB逸爵,還能接受。
但是額外加一個(gè) docker-compose 還是讓我不舒服市咽,我要做到盡可能小的體積痊银,所以,我選擇 curl施绎。
直接對(duì)著 unix socket 來操作溯革。
2.3.1. 在容器中啟動(dòng)容器
API 文檔地址:https://docs.docker.com/engine/api/v1.26/
API 返回的是 json 格式,本來用 jq 可以搞定的谷醉,迅速取到相應(yīng)的值致稀,但是不想裝額外的軟件包,所以強(qiáng)行使用 cut 把結(jié)果剪切出來了俱尼,好粗暴抖单,笑。
ID=$(curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" -d '{"Image": "zuolan/hugo:minify", "Volumes": {"/hugo/public":"/work/html"}}' -X POST http:/v1.26/containers/create | cut -d: -f2 | cut -d, -f1 | cut -d\" -f2)
curl --unix-socket /var/run/docker.sock -X POST http:/v1.26/containers/$ID/start
curl --unix-socket /var/run/docker.sock -X POST http:/v1.26/containers/$ID/wait
curl --unix-socket /var/run/docker.sock "http:/v1.26/containers/$ID/logs?stdout=1"
curl --unix-socket /var/run/docker.sock -X DELETE http:/v1.26/containers/$ID
2.3.2. 主鏡像源代碼
run.sh
#!/bin/sh
SEPARATOR="================================================================"
echo "正在執(zhí)行初次構(gòu)建:"
echo $SEPARATOR
echo "正在構(gòu)建頁面:"
hugo
echo "正在壓縮網(wǎng)頁資源:"
ID=$(curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" -d '{"Image": "zuolan/hugo:minify", "Volumes": {"/hugo/public":"/work/html"}}' -X POST http:/v1.26/containers/create | cut -d: -f2 | cut -d, -f1 | cut -d\" -f2)
curl --unix-socket /var/run/docker.sock -X POST http:/v1.26/containers/$ID/start
curl --unix-socket /var/run/docker.sock -X POST http:/v1.26/containers/$ID/wait
curl --unix-socket /var/run/docker.sock "http:/v1.26/containers/$ID/logs?stdout=1"
curl --unix-socket /var/run/docker.sock -X DELETE http:/v1.26/containers/$ID
echo "頁面已經(jīng)發(fā)布,容器進(jìn)入監(jiān)視狀態(tài)矛绘。"
VOLUMES="/hugo"
INOTIFY_EVENTS="create,delete,modify,move"
INOTIFY_OPTONS="--monitor --exclude=public"
inotifywait -rqe ${INOTIFY_EVENTS} ${INOTIFY_OPTONS} ${VOLUMES} | \
while read -r notifies;
do
echo $SEPARATOR
echo "文件有變動(dòng):"
echo "$notifies"
echo "正在重新構(gòu)建頁面:"
hugo
ID=$(curl --unix-socket /var/run/docker.sock -H "Content-Type: application/json" -d '{"Image": "zuolan/hugo:minify", "Volumes": {"/hugo/public":"/work/html"}}' -X POST http:/v1.26/containers/create | cut -d: -f2 | cut -d, -f1 | cut -d\" -f2)
curl --unix-socket /var/run/docker.sock -X POST http:/v1.26/containers/$ID/start
curl --unix-socket /var/run/docker.sock -X POST http:/v1.26/containers/$ID/wait
curl --unix-socket /var/run/docker.sock "http:/v1.26/containers/$ID/logs?stdout=1"
curl --unix-socket /var/run/docker.sock -X DELETE http:/v1.26/containers/$ID
echo "新的頁面構(gòu)建完成耍休。"
echo $SEPARATOR
done
Dockerfile
FROM alpine
WORKDIR /hugo
ENV GIT_USER=izuolan GIT_EMAIL=i@zuolan.me
COPY run.sh /run.sh
RUN apk add --no-cache inotify-tools hugo curl && \
chmod a+x /run.sh
VOLUME ["/hugo"]
CMD ["/run.sh"]
3. 壓縮網(wǎng)頁文件
因?yàn)?Hugo 是使用 Go 語言寫的,在網(wǎng)頁處理上缺少相應(yīng)的功能货矮,所以為了壓縮網(wǎng)頁羊精,這里使用 Gulp 來自動(dòng)化壓縮網(wǎng)頁。
比較遺憾的是我不知道 Gulp 有哪些可以實(shí)現(xiàn) replace (替換 link)效果的插件(類似的插件)囚玫,所以沒能實(shí)現(xiàn)把外聯(lián)式資源喧锦、圖片嵌入網(wǎng)頁的功能。哪位大佬知道請(qǐng)告知~注意是不能修改 HTML 的情況下實(shí)現(xiàn)抓督。如果修改了主題文件就不能適用于他人了燃少。
3.1. Gulp 壓縮網(wǎng)頁資源文件
某一次的自動(dòng)壓縮過程:
$ docker-compose logs -f
Attaching to hugo, minify, blog
hugo | 正在執(zhí)行初次構(gòu)建:
hugo | ================================================================
hugo | 正在構(gòu)建并部署頁面:
hugo | Started building sites ...
hugo | Built site for language en:
hugo | 0 draft content
hugo | 0 future content
hugo | 0 expired content
hugo | 6 regular pages created
hugo | 16 other pages created
hugo | 0 non-page files copied
hugo | 14 paginator pages created
hugo | 7 tags created
hugo | 5 categories created
hugo | total in 584 ms
hugo | 頁面已經(jīng)發(fā)布,容器進(jìn)入監(jiān)視狀態(tài)铃在。
minify | 正在執(zhí)行資源文件壓縮:
minify | ================================================================
minify |
minify | > @ build /work
minify | > gulp build
minify |
minify | [12:50:26] Requiring external module babel-register
minify | [12:50:28] Using gulpfile /work/gulpfile.babel.js
minify | [12:50:28] Starting 'build'...
minify | [12:50:28] Starting 'minify-html'...
minify | [12:50:29] Finished 'minify-html' after 1.43 s
minify | [12:50:29] Starting 'minify-js'...
minify | [12:50:32] Finished 'minify-js' after 2.33 s
minify | [12:50:32] Finished 'build' after 3.77 s
minify | 頁面已經(jīng)壓縮阵具,容器進(jìn)入監(jiān)視狀態(tài)。
3.1.1. 壓縮鏡像源代碼
Dockerfile
FROM mhart/alpine-node
WORKDIR /work
COPY . /work
RUN mkdir /work/html && \
npm install
VOLUME ["/work/html"]
CMD ["npm", "run", "build"]
gulpfile.babel.js
import gulp from 'gulp'
import htmlmin from 'gulp-htmlmin'
import uglify from 'gulp-uglify'
import runSequence from 'run-sequence'
gulp.task('minify-html', () => {
return gulp.src('html/**/*.html')
.pipe(htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true,
removeComments: true,
useShortDoctype: true,
}))
.pipe(gulp.dest('./html'))
})
gulp.task('minify-js', () => {
return gulp.src('./html/**/*.js')
.pipe(uglify())
.pipe(gulp.dest('./html'));
});
gulp.task('build', (callback) => {
runSequence('minify-html','minify-js', callback)
})
package.json
{
"private": true,
"scripts": {
"build": "gulp build"
},
"devDependencies": {
"babel-preset-es2015": "^6.5.0",
"babel-register": "^6.5.2",
"gulp": "^3.9.1",
"gulp-cli": "^1.2.1",
"gulp-htmlmin": "^1.3.0",
"gulp-uglify": "^2.0.0",
"run-sequence": "^1.1.5"
},
"babel": {
"presets": [
"es2015"
]
}
}
3.2. 推送到所有服務(wù)器
這個(gè)就各顯神通了定铜,文件都有怔昨,自己看著辦。
不知不覺寫到 23 點(diǎn) 49 分宿稀,怎么使用就留到明天吧。