今天跟大家介紹一個好用的縮圖伺服器 thumbor ,我們已經在線上用了一陣子,還真是方便的服務,我想即使現在用不著,也應當加入你的工具箱裡。
thumbor 是一個 python based 的 server,原理大致上是你有一個圖源的伺服器,例如:
http://example.com/foo/my-image.jpg
只要將圖源的網址經過 thumbor 後,就能得到任意裁切大小的新圖
http://my-thumbor.foo.com/200x100/http://example.com/foo/my-image.jpg
my-thumbor.foo.com
是 thumbor 的網址,後面接上 200x100
的路徑,最後再加上圖源網址,這樣回傳的圖檔就是 200x100 大小的新圖了。
ok,就是這麼簡單的概念,看到這裡你也能想像 thumbor 的最大用途是什麼了:
thumbor 能夠即時的產生任意大小的縮圖,適合不同的使用情境
縮圖 (thumbnail) 這種概念已經很久了,用戶上傳一張照片,讓伺服器裁切成幾份小圖也是工程師常做的任務。幾年前還好,大概只要裁一兩份小圖就夠網頁用了,但現在有各種不同尺吋的手機,還有平板,而桌機也有了 hi-dpi 的需求。瞬時一兩張小圖不夠用了,而且手機還是用 3/4G 網路,裁了太大的圖用戶就是等等等.... 等個沒完沒了,而且也吃掉用戶網路的用量 (現在 4g 沒吃到飽了)。
這些新的需求浮現,所以像 thumbor 這樣專屬處理縮圖的應用就變得很重要了。透過 thumbor,我們可以滿足像是下列的情境:
- 當用戶是用 4" 小手機,就餵給他 100x200 的小圖
- 當用戶是 6" 高檔 iPhone,就給他高解析 200x400
- 當用戶用的是橫著放的平版,就給他 200x100 不同長寬比例的圖
- … 等等。
安裝 thumbor
下面將介紹如何安裝 thumbor 伺服器到虛擬機裡 (vagrant),請預先安裝好 vagrant、virtualbox、以及部署工具 ansible 。詳細的安裝步驟就不提了,前面的連結都有詳細的安裝法。
1. 啟動新的 VM (ubuntu 14.04)
開啟個目錄 thumbor-tutorial
$ mkdir thumbor-tutorial
$ cd thumbor-tutorial
建立 vagrant 的設定檔 Vagrantfile
Vagrant.configure(2) do |config|
config.vm.provider "virtualbox" do |v|
v.memory = 512
end
config.vm.box = "ubuntu-14.04"
config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"
config.vm.define "thumbor" do |v|
v.vm.network "private_network", ip: "192.168.33.99"
end
end
建立好後,啟動 vagrant vm
$ vagrant up
# 等一陣子,沒錯誤的話就能 ssh 進 vm
$ vagrant ssh
vagrant up 的指令按照 Vagrantfile 設定檔的指示,建立一個 512MB ram 的 linux VM,安裝 ubuntu 14.04,它的 ip 是 192.168.33.99
。
2. 設定 ansible 自動化安裝 thumbor
成功後,接下來就是要使用部署工具 ansible
幫我們自動化安裝 thumbor
# 先下載別人寫好的 thumbor 部署設定
$ ansible-galaxy -p roles install savagegus.thumbor
# 成功後會下載到 `roles` 這個目錄裡,會有兩個設定
ansible-galaxy 裡有許多別人貢獻、預先做好的安裝設定,很多常見的應用都已經有人做好了,thumbor 也不例外,這裡我們使用的是 savagegus.thumbor
接下來是指定 ansible 部署 thumbor 到 vagrant 的 VM 裡。
- 先新增
inventory
檔,裡面是放要安裝的主機設定,這裡是指向 剛剛建立的 vagrant vm192.168.33.99
thumbor ansible_ssh_host=192.168.33.99
## 指定所以設定的主機都使用 vagrant 這 user、及其 private ssh key
[all:vars]
ansible_ssh_user=vagrant
ansible_ssh_private_key_file='.vagrant/machines/thumbor/virtualbox/private_key'
注意:此設定檔後半段關於 user/key 的設定只限於 vagrant 使用,正式上線時不需要。
- 再來建立安裝 thumbor 的設定檔
site.yml
(在 ansible 的術語叫 playbook)
- hosts: thumbor
roles:
- role: savagegus.thumbor
sudo: True
thumbor_results_storage: thumbor.result_storages.file_storage
thumbor_bind_address: '0.0.0.0'
安裝設定本身很短,因為大部份都交給剛才下載的 savagegus.thumbor
處理好了。不過,原本的設定只能從 localhost 連進 thumbor 伺服器 (按設定檔它希望你經過 nginx),這裡我們覆蓋掉預設值,讓所有網址都能直接連進 thumbr 伺服器,方便測試。
部署 thumbor
呼~ 要開始正式部署了,一個 ansible 指令搞定:
$ ansible-playbook -i inventory site.yml
如果一切正常無誤的話,你會看到 ansible 一步步下載、安裝、設定 thumbor 所有需要的套作。savagegus.thumbor
這個 role 也包含了安裝 nginx 伺服器,所以你也會看到 nginx 的安裝過程。
thumbor 是個 python 套件,理論上只要
pip install thumbor
就應該能裝完,但是我們要部署的是一個伺服器,伺服器總是要設許多權限、log 檔位置、啟動 script… 等等諸多繁雜的工作,這些工作就是讓 ansible 來做了,有興趣可以看看roles/savagegus.thumbor/tasks/main.yml
就知道部署 thumbor 的所有細節
安裝完後,可以在瀏覽器測試 thumbor 是否正常運作:
# 正常的話會看到 `WORKING` 的結果
http://192.168.33.99:8888/healthcheck
# 正常的話會看到一張 200x200 的圖
http://192.168.33.99:8888/unsafe/http://dummyimage.com/200x200.png
# 注意上述的測試都是跳過 nginx,直接測 thumbor 本身 (在 port 8888)
如果有任何問題的話,可以用 vagrant ssh
連進 VM,查看 /var/log/upstart/thumbor-worker-8888.log
錯誤訊息
使用 thumbor
讓我們來試試幾個 thumbor 的功能:
範例的源圖網址:
https://http.cat/100.jpgcrop 成 200x300 圖片:
http://192.168.33.99:8888/unsafe/200x300/https://http.cat/100.jpg
轉成 png:
http://192.168.33.99:8888/unsafe/filters:format(png)/https://http.cat/100.jpg
圓角 + 灰階:(產生的網址太長,下面多加了斷行)
http://192.168.33.99:8888/unsafe/
filters:round_corner(40,255,255,255):grayscale()/
https://http.cat/100.jpg
thumbor 的基本功能是縮圖與裁切,你也看到了在網址上加上 /200x300/
這種大小就能很簡單做到,但 thumbor 還有強大的濾鏡功能,上面就展示了轉換格式,以及裁圓角與加上灰階等等進階功能,而且能任意組合,你可以在 thumbor 的 wiki 上查到更多的濾鏡說明 。濾鏡的語法就是在路徑上加上 /filters:foo:bar/
而已,非常簡單。
secure thumbor
眼尖的你可能發現了,上面的測試網址都帶有 /unsafe/
的路徑,這在 thumbor 裡是指網址不需經過驗證,能直接轉圖。這在測試環境下很方便,不過正式上線的話,網址沒驗證就很容易被別人惡用 (thumbor 是個任意轉圖、快取的伺服器,沒驗的話別人可以偷接)。
thumbor 提供兩個方式驗證:
- 第一個是限定圖源網址,比方說你只限定上游的圖源必須是你自家的網站,這樣就能保證不能偷接。我們可以修改 thumbor 的設定檔
roles/savagegus.thumbor/templates/thumbor.conf.j2
:
## ...etc
ALLOWED_SOURCES = [
's3.amazonaws.com',
'http.cat'
]
## ...etc
預設值是不限任何網站,而上面的範例我們修改成限定圖源網站必須是 aws s3 與 http.cat。
- 第二個是直接驗證網址本身,我們需要設定一個私有鍵,並且關閉 /unsafe/ 的功能,我們可以在
roles/savagegus.thumbor/templates/thumbor.conf.j2
裡,關閉 unsafe 的功能:
## ...etc
ALLOW_UNSAFE_URL = False
## ...etc
私有鍵的設定檔則放在 roles/savagegus.thumbor/templates/thumbor.key.j2
裡,長度不居。下面的值僅供本文範例使用,請不要直接抄用在你的正式主機上:
Vzt9X7LHbcyGsaAzVLV54rfuEpEQWspbynaRZeq9+WCBfC7iFAiSThYHI79zwKt
建議:你可以用
openssl rand -base64 48
指令產生高強度的任意字串,這裡的 48 是 byte 長度
好了,ansible 設定檔改完之後,就是再部署一次進 vagrant vm:
$ ansible-playbook -i inventory site.yml
一鍵搞定部署!爽!而新的 thumbor.key 則會安裝到 /etc/thumbor.key
路徑
部署完畢之後,剛才上述範例的那些 /unsafe/
網址不能再使用了,用了 thumbor 只會吐 status 400 bad request 給你。你必須使用加上驗證碼的網址,像是剛才的 crop 200x300,就會變成:
http://192.168.33.99:8888/lNmQrS_bz-er8L6nIO1qFPgGOm4=/
200x300/https://http.cat/100.jpg
unsafe 那段需要填入經 HMAC 算過的驗證碼。如此一來,沒有私有鍵的人便無法任意產生合法的 thumbor 網址。
thumbor-url 與 API
前述的驗證碼需要經過一定手續計算而得,為了方便測試,thumbor 有提供 command line 工具 thumbor-url
可以產生驗證網址。因為我們的 thumbor 裝在 vagrant 裡,所以要進入 vagrant vm 內去執行
#進入 vm
$ vagrant ssh
#進入後,執行 thumbor-url 產生網址路徑
$ thumbor-url -l /etc/thumbor.key /200x300/https://http.cat/100.jpg
如果一切正常的話,terminal 會輸出
URL:
/lNmQrS_bz-er8L6nIO1qFPgGOm4=/200x300/https://http.cat/100.jpg
只要將該路徑前面冠上 thumbor 的主機就是個合法的 thumbor 網址。
command line 工具只限測試使用,正式在 app 裡運用時,必須透過 API 的幫忙,不然自己算 HMAC 太累了。thumbor 網站列出了很多平台的函式庫 ,例如 android /java 用的 Pollexor,使用上就非常簡潔:
Thumbor.create("http://192.168.33.99:8888/",
"... thumbor private key...")
.buildImage("https://http.cat/100.jpg")
.crop(10, 10, 90, 90)
.resize(40, 40)
.smart()
.toUrl();
Pollexor 採用 fluent API 設計概念,裁剪及套用濾鏡都相當的直覺好用。
smart thumbor
thumbor 的縮圖最強的地方是它有智慧的功能。一般在裁剪圖時,大小不符合就是剪裁圖的正中央,而 thumbor 內建進階的圖形辨識功能,可以偵測人臉的位置或是影像的核心部位。要開啟這個功能,我們要替 thumbor 安裝 opencv 函式庫
- 在
roles/savagegus.thumbor/tasks/main.yml
加上 opencv 的安裝
- name: install pgmagick deps
...
# 加上 opencv 的安裝
- name: install opencv deps
apt: pkg={{item}}
with_items:
- libcurl4-openssl-dev
- libopencv-dev
- libmagick++-dev
- graphicsmagick
- python-opencv
- name: install pgmagick
....
- 然後,到
roles/savagegus.thumbor/templates/thumbor.conf.j2
裡打開人臉辨識與重點偵測的功能
DETECTORS = [
'thumbor.detectors.face_detector',
'thumbor.detectors.feature_detector'
]
設定好後,一鍵完成部署:
$ ansible-playbook -i inventory site.yml
部署完後,就可以在裁切圖時加上 /smart/
這個功能,正確裁剪到人臉的位置:
- 圖源 https://http.cat/405.jpg (750x600)
- 無 smart,裁切成 300x100 -
http://192.168.33.99:8888/UsXPegr1vNw-sUO5wJKpq9Wy8FM=/
300x100/https://http.cat/405.jpg
- 開啟 smart,同樣裁切成 300x100
http://192.168.33.99:8888/HZBfomhL9aMALrBdHEhM_asJMs8=/
300x100/smart/https://http.cat/405.jpg
上面的簡單測試可以看出 smart 裁圖的中心移到了人臉上。有了智慧裁圖之後,運用就更自由了,例如在手機上,就能裁成直的圖,而在平板上,就能裁成橫的,都不會失去太多圖片的核心部位。
注意,thumbor 產生過的網址會快取在磁碟一份,之後就不會再重新算一次,所以如果想看同網址但不同的結果的話,要清除
/tmp/thumbor/result_storage
路徑
其他
這裡展示的步驟,只是可以安裝到 VM 裡測試而已,雖然 ansible 已經讓部署簡化許多,但要正式上線的還有許多設定需要調整,例如圖片快取的暫存路徑是設在 /tmp/ 下,這一定不夠放,需要額外的安排。另外圖片快取過期的時間你也要考慮一下,thumbor 是可以設定圖片的過期時間 (會反應在 http cache control header 上),過期後再存取一次也會重新產生圖,不過過期的圖片卻不會自動刪除,所以跑久了磁碟很快就佔滿了,這也要自行處理。
本文所展示的 thumbor 功能只是其中一小部份而已,例如 thumbor 本身也可以當圖源網站 (圖上傳到 thumbor 本身,後面可接資料庫),這部份我本身沒有實務經驗,所以這裡我就不提了。更多的資料請見 thumbor github
Real world thumbor
我已經在不同的服務上正式的使用 thumbor。兩個服務用途和目的相差很大。
- 案例一:縮圖給手機 app 用
這個應用是用戶會上傳自己拍的圖,而且一次多張。當圖片是一張、二張、三張時,呈現的編排方式會不同,所以需要直裁切、橫裁切兩種變化,透過 thumbor 之後,就能即時產生不同方向的裁圖,達到最佳顯示效果和下載速度。而且,像是圖片的編排方式其實設計師常常會更動設計的,有了 thumbor 之後便能應付未來臨時變動的設計。這個應用裡,我們也開啟了 smart 功能,因為大部份的用戶圖片都是自拍,而 thumbor 框到臉的機率大概在 8 成上下,還不錯。
這個服務的部署架構是這樣的:
AWS cloud front (CDN)
v
EC2 load balancer
v
3 * thumbor server (ec2)
v
AWS S3 (圖源)
最前面掛了 cloud front CDN,後面則是經過 load balancer,最後才到 thumbor,thumbor 雖然裝了三台,但因為前端有 CDN 擋著,所以流量很低,不需要裝到三台這麼多。只是一般 high availiabilty 的架構都是習慣以 3 台做為基本單位去部署的,所以我們才會運用到三台。
- 案例二:最佳化 png 圖檔
另外一個服務是我個人的 pet project,用戶的使用習慣是上傳遊戲的截圖,所以大約有 40% 的圖片都是 png。png 圖檔超級大啊,截個圖隨便都是超過 500KB,不僅下載慢、頻寬也都吃光光了。
為了應付這個問題,特別去找縮小 png 圖的工具,這個領域裡 pngquant 是最知名的工具了,它的作法是將 24bit png 轉成 8bit png ,然後用特別的演算法和 dither 讓圖片看起來幾乎一模一樣。經過這道手續,原本 600KB 的 png 大小就降成 1/3,200KB。這實在是差很多。
我原本打算是在上傳圖片的過程中就先用 pngquant 處理好再上傳的 (Java app server),可惜 pngquant 並沒有 java 版本,這表示要去寫 JNI 或是呼叫外部 process 去處理了,這寫起來會死人。後來往 thumbor 的方向去找,發現只要裝 thumbor-plugins 就行了,pngquant 的整合早已有人寫好。
於是就改了一下 ansible 設定檔,加進 plugins,然後一鍵搞定部署,線上就用了起來。這個 pet project 的架構是這樣的:
Cloudflare (CDN)
v
nginx
v
thumbor server
v
AWS S3 (圖源)
因為是 pet project,所以用了免費的 CDN cloudflare,上線之後,從 cloudflare 的統計資料來看,每日的流量從 ~270GB 下降到 ~200GB 左右,真是不錯的結果,當然用戶下載圖的時間也整整少了 2/3,很有感。而 pngquant 最佳化的圖雖然有失真,但其實不把兩張圖並在一起看根本就看不出差別,這結果很滿意啊。
結語
thumbor 是個好工具!加進自己的工具箱裡,未來遇到跟圖片有關的問題,自然就多了一個解決的選項。本篇的範例可在 github clone 下來自己玩,而 pngquant 的設定則是在另一個 branch 供各位參考。