Archlinux 修復紀錄

Universal Studio Singapore

之前我在推特上有說過我不小心把我放 blog 的主機搞壞,當時就是用 pacman 更新過後,出現一些錯誤,我快速的重跑pacman -Syu然後就開始一直出現錯誤了,當時想說是因為我太久沒更新,然後有相依性錯誤造成系統幾乎爛掉,一度要放棄,不過因為網站相關的 instance 都還跑著,所以我就想說暫時放著,等有空把資料弄出來再重建系統,然後十一月中去了一趟新加坡,這趟行程要邊顧小孩其實很累,然後就在回來當天晚上就收到 Linode 的緊急維護,已經把我的 Linode 主機重開了,網站當然也死了,真的是晴天霹靂,不過實在太累了我也只能先放著不管。

過了幾天終於比較有力氣來看看看問題,我當時的狀況是,無法使用 pacman,然後更進一步發現是 curl 就死掉,curl 死掉會造成很多東西一起掛掉,像是 git、wget 也都掛了,結果我能使用的工具和手段就變的很少,總之先來看看錯誤訊息吧:

/usr/lib/libcurl.so.4: undefined symbol: BrotliDecoderCreateInstance

由此可知基本上問題就是動態連結 Brotli 的 library 時出錯,我還記得我當初裝機器時,Archlinux 還沒有正式 Brotli 的套件,所以我還自己編譯了一版給 nginx 用,而我的 nginx 也是自己編譯的,沒想到不知不覺 Archlinux 已經有正式的 Brotli 套件,而且 curl 還相依於它。

接著我就開始各種嘗試,想辦法重新裝 Brotli 套件,curl 雖然不能動,但是我還可以用 scp 傳檔案上去,不過就算傳上去 pacman 也還是完全無法跑起來,即使我只是想要他安裝本地的檔案,而不是要連網路,然後我也去看了/use/local/裡面 brotli 套件的 header 檔案,查看內容,發現真的沒有BrotliDecoderCreateInstance,不過這個 symbol 在 Brotli 的 repo 內是有的,而且已經存在了有四年之久,所以顯然,我系統內安裝的版本很有問題,雖然確定問題在哪,但是還是一直沒有解決方法,重裝套件需要 pacman,但是 pacman 需要修好 brotli 才能動,陷入死結當中,更糟的是,我在網路上搜尋就是找不到有一樣問題的人。

然後我就開始研究 pacman 掛掉要怎麼辦,找了許久終於找到有一個pacman-static的工具,是預先編譯好,並且是靜態連結的 pacman 執行檔,抓下來後發現真的可以用,真的是感動的痛哭流涕,然後我立馬執行pacman -Syu,一切執行順利,感動QQ,然後我執行了curl想確認有沒有修好,結果我再次看到了那個一樣的,熟悉的錯誤訊息...

這時我百思不得其解,我用 pacman 看安裝的套件版本確實是新的,我去解開套件來看也是新的,但是我去系統的/usr/local/下看裡面的檔案卻是舊的,重新裝了很多次也都是一樣狀況,就這樣鬼打牆很久之後,我突然察覺,/usr/local/下的東西,其實是我們手動編譯安裝的,也是路徑中優先權較高的,然後我在前面有提過,我很久以前有手動編譯安裝 Brotli 套件,終於,一切真相大白,我手動裝的時間點是五年前,所以該版本沒有BrotliDecoderCreateInstance,然後 Archlinux 用的是四年前版本,所以系統中其他需要 Brotli 的東西都會因此而掛掉,解決方法就是把手動裝的全部砍光光就好了。

不過砍掉我手動編譯的 Brotli,也同時造成我的 nginx 再起不能,因為在設定檔內它是需要我手動編譯安裝的那那個套件,解決方法是很簡單,就把需要的 module 路徑改到 pacman 安裝的套件那邊,然後我的 nginx 就可以起來了,不過我的 blog 還是死的,非 blog 的部分倒是活著,我一開始想說是 php-fpm 的問題,看錯誤訊息發現有 permission 問題,就去改 socket file permisson 成 666,然後網站還是起不來,我研究了很久,想要看看 PHP 的錯誤訊息,但是一直看不到東西,也去看 nginx error log,journalctl 也是看沒錯誤,還以為 php-fpm 是死的,還用了

<? echo phpinfo(); ?>

然後開瀏覽器看到原始碼直接回回來,搞了一陣子想起要改用<?php,改下去結果又發現一切正常,最後才發現,問題是因為我在用 pacman 更新整個系統時,把 PHP 7 升級到 8,然後我的程式碼裡面有個地方寫死大版號不對的話會回錯誤訊息,但是我沒有把錯誤訊息寫到 log 中,結果就造成我一直找不到問題點。

PHP 的問題解決後,我的 blog 就回到線上了,不過其實,這時候我的 mariadb 還是死的,透過 journalctl 看 log

sudo journalctl -xeu mariadb.service

有一行寫著:

Plugin 'InnoDB' registration as a STORAGE ENGINE failed.

一開始想說是安裝失敗,後來往上找發現還有另外一行:

InnoDB: Upgrade after a crash is not supported. The redo log was created with MariaDB 10.4.8

意思就是如果你的 DB crash 後,沒有正常關閉的狀態下,去更新 mariadb,就會有這個錯誤,不過一開始我不以為意,因為我認為我只有 upgrade,但是不知道是何時 crash 過,後來回想,應該是 brotli 爛掉時,mariadb 就跟著起不來了,總之,這個問題的解決方法,就是退回舊的 10.4.8 然後重新啟動 DB,所以就研究了一下 Archlinux 怎樣安裝舊版的套件,基本上 pacman 是不能指定版本的,有兩個方法可以裝舊版,一個是透過系統內的 pacman cache,不過我之前在修理的時候已經清掉了,所以就只能從Arch Linux Package Archive那邊下載特定版本的 package tar 檔案,下來用pacman -U安裝,然後因為有相依性問題,所以要把幾個需要的套件都抓下來,一起安裝:

pacman -U mariadb-10.4.8-2-x86_64.pkg.tar.xz \
  mariadb-clients-10.4.8-2-x86_64.pkg.tar.xz \
  mariadb-libs-10.4.8-2-x86_64.pkg.tar.xz

反正如果啟動失敗,也會有訊息提示你要看 log,結果把 mariadb 三個都裝下去後還真的有問題,說找不到 openssl 1.1 的檔案,所以也去抓下來手動安裝:

pacman -S openssl-1.1

至此,總算是修好了,接下來就是有時間要把我的 blog 系統容器化吧,有太多不是很好安裝的東西了。

PS. 這篇發的出去表示真的修好了。


My First Contribution to Nginx

nginx conf

因為工作上的需要,所以其實我還蠻常會編輯 nginx configuration file 的,理所當然的編輯器是用 vim,然後就會對 nginx 設定檔的支援有意見,一般人用的 nginx 設定檔的 vim script 其實是 nginx repository 的 contrib 目錄裡面的那份,這份 vim script 其實本來也是獨立的,不過原作者好像把他捐進去 nginx 裡面,之後就一直都在裡面了,也因此之後更新就很不頻繁。

然後因為檔案都放在 nginx repository 裡面,Vim 要使用其實不太方便,所以 Github 上還看的到不少人單獨抽出來,我一開始也是 forkmosky的來用,後來就直接在自己的 repository上面修改了,改一陣子之後就開始想要推回 upstream,也就是 nginx 的程式庫,然後就開始了這段協工旅程(?)。

要發修改上 upstream,第一步自然是看一下如何貢獻,節錄這邊幾個重點:

  1. nginx-devel這個 mailing list 做討論
  2. 發 patch 前有一些注意事項,不過我改 vim script 比較沒關係
  3. Patch 也是用 email 發到 nginx-devel,有範例
  4. 推薦用patchbomb
  5. 要先簽CLA(不過目前這條已經不見了,改成最後說發 patch 等於同意用他們專案的 LCIENSE)

總之我就照這份,先去訂閱了 nginx-devel 觀察一陣子,然後就直接把我的修改整個丟上去了,一開始是直接用 Gmail 發,把 patch 檔內容直接複製貼過去,產生 patch 檔的方法是:

hg export > something.patch

hg export會直接輸出最後一個 commit 的 patch 內容到 STDOUT,然後就直接用 Gmail 發過去,結果 review 的Maxim Dounin說他沒辦法 apply patch,可能是因為我的 mail client 的關係,建議我用patchbomb發,所以就研究搜尋了一下,發現他是直接發 email 的機制,所以要把帳號密碼都寫到設定內,找了一篇 Gmail 的設定範例,搭配 Google account 的應用程式密碼,設定範例如下:

[extensions]
hgext.patchbomb =

[email]
from=othree <othree@gmail.com>
to=nginx-devel@nginx.org
cc=othree@gmail.com
method=smtp

[smtp]
host=smtp.gmail.com
port=587
username=othree@gmail.com
password=[gmail_password]
tls=True

把這些資訊填入.hg/hgrc這個檔案內,然後就可以用hg email -o --test來測試看看,這個指令會把完整的原始信件內容,包括 header 等都顯示出來(丟到 STDOUT),如果正式要發就把--test拿掉就好了。

確認一切沒問題後,我就改用 patchbomb 發 patch 到 nginx-devel 了,結果還是被拒絕了,問題主要是這個 patch 一次修改太多,理想上應該是不同目的的修改放到不同 patch 內,當然這和我一開始的預想不一樣就是了,我一開始的想法是因為 contrib 這邊的東西,相對於 nginx 本體的原始碼來說比較次要,所以盡量減少 commit 數,其實如果我有先去問過應該是可以少繞這段路,總之,為了一個一個修改送出,我又開了一個 github repository,叫做nginx-contrib-vim-patch,想要慢慢把我的 nginx-contrib-vim 內的更動搬過去,接著開始的,就是漫長的 review 和溝通了。

其實我完全沒想到 Maxim Dounin 會這麼認真的 review,不止會看我這樣改是要達到什麼目的,還有認真測試,結果被抓出一堆問題,雖然都是奇妙的 conf 寫法,合語法,但是應該不會有人這樣寫的 case,這些 case 我也開始慢慢收集到 github 上的nginx-conf-test,方便之後測試用,總之來回許久,終於有一部分比較簡單的東西先進去 nginx repository 了,然後我發現外部貢獻者都會在change log那邊被感謝,我貢獻進去的目前應該都在 1.11.11 那版,其實只有把新的 directive 補上(core modules, 3rd party modules)和幾個 protocol 參數的 highlight,至於其他的修改還進不去,目前看起來會是一場長期抗戰,主要是因為 reviewer 對於期望的目標和我不一樣,目前大概會維持兩個版本吧,一邊弄自己希望的,一邊抽東西送回去 upstream,不得不說主事者控制太緊會讓貢獻者動力被削減不少。

貢獻 nginx 的過程讓我體會到以前的開源協做的模式(應該是吧?),用 mailing list 溝通,發 Patch、code review、做討論,這些點來看,nginx 的流程其實是非常老派,和現在用 Github 做溝通、協做 的流程差很大,門檻也高不少,當然這不一定是壞事,還是要看專案性質,在 Github 這類平台上做這些協做流程的話,門檻降低了,其實可能隨之而來的問題就是太多人進來造成貢獻品質落差很大,反而會吃掉主力人員的時間,剛好今天也看到知乎上有一篇「維護一個大型開源專案是怎樣的體驗?」,裡面就有提到 VSCode 的狀況,變成還需要排人專門處理 issue 和 PR,感覺就很可怕。

順帶一題,nginx 的固定貢獻者當中不少中國人啊。


Docker 雜談

最近使用的一些感想和疑問

  • --name自動會變 network 內的 hostname,蠻方便
  • Image build 失敗,還是會產生一個東西在那邊,要 rm 掉才能重新 build...
  • Service 還不支援 update 修改 network
  • DB container 的資料要開 volume 掛比較保險(免得不小心砍到 container)
  • 在多個 node 上開 replica,掛 volume 的話都是在該 node 上找,目前似乎沒有透過網路共享 volume container 這種事
  • 很多服務的官方 docker 都是用環境變數做設定,不知道是不是本來就有的慣例
  • 一直更新 registry 上的 image 好像會越來越多垃圾,不知道是不是有清理機制
  • 整體而言,我覺得 docker service 介面是個很容易讓 fat finger 造成服務爆炸設計,不過架構規劃的好的話,應該不會太難修復就是

Ubuntu 14.04 與 MovableType 4.x

前一篇文章提到我為 SSL 安全性升級到 Ubuntu 14.04,其實升級之後遇到一些 MovableType 的相容性問題,Ubuntu 在 12.04 的時候還是用 Perl 5.14,不過到 14.04 時,Perl 升級到 5.18 了,順便提一下現在最新的穩定版是 5.20,不過 5.18 和 5.20 是同時都有在維護的。

然後我的 MovableType 是用很久以前的 4.38,用新的 Perl 會跑不起來,不過我也不太想升級,一來是 License 問題,二來是新的 MT 一個很大的架構改變是他變成是多 blog 系統,我也不太喜歡這點。總之剩下的方法就是想辦法修 bug,或是用舊的 Perl 跑,顯然後者容易許多,然後我也找到 gugod 開發的perlbrew這工具,類似於Ruby 的RVM,c9s 也有寫一篇文章介紹

比較有趣的是我用了 perlbrew 安裝好 Perl 5.14 後,用which perl找到 Perl 5.14 執行檔位置然後手動修改 mt.cgi 等檔案,用 mt-check 檢查發現還缺 DBI 的套件,就用升級前就已經裝好的 cpanminus 裝了,不過怎麼裝都顯示正常但是 MT 就是一直抓不到,看了一下 cpanm 檔案原始碼才發現它用的 perl 是:

#!/usr/bin/perl

心想 gugod 怎麼可能不處理這個問題,於是搜尋一下發現可以用perlbrew 安裝 cpanm

perlbrew install-cpanm

看了安裝出來的 cpanm 用的 perl 來源是不一樣的:

#!/usr/bin/env perl

這樣用 cpanm 裝的 Perl 套件總算可以用了,之後還有一個 5.14.2 和 5.14.4 的差異造成的錯誤,就照網路上找到的文章修正了。


[

最近因為shellshock的關係,跑去看了/bin/下面的東西,結果突然發現有個執行檔叫做[

ls /bin/

執行了也完全沒反應,結果和同事討論就上去 stack overflow 來問問看,然後馬上就被回說看看man [。想不到,原來 shell script 裡面的 if else condition 的[ ],其實就是這個執行檔啊,以前一直以為是個語法的 syntax...


mozjpeg 2.1

Mozilla 最近發佈了mozjpeg2.1,同時還有一篇用 mozjpeg 產生高效率的 JPEG教大家使用裡面的工具,所以我之前誤會以為現在只有 c lib 可以用,這篇文章有一些範例指令讓大家可以把cjpeg把圖片重新壓縮,小缺憾是安裝講的比較不清楚,所以我另外測試過,提供一下 Mac OSX 的指令:

cd mozjpeg
autoreconf -fiv
mkdir build && cd build
sh ../configure
make
sudo make install

關鍵的就是BUILDING.txt沒說build目錄是要自己建立的,不過實際測試的效果要等週一才能測試看看,目前只有編譯過確定有指令工具可以用而已。

另外一個比較容易漏掉的是要編譯需要NASM, homebrew 可以直接brew install nasm


OSX 裝 ruby 1.9.3p392 編譯問題

最近因故要在 Mac 上重新建立工作環境,結果在裝 ruby 時遇到 compile error,看起來也不是今天才有的問題,狀況是 clang 4.1 之後才開始有的,而 OSX 10.9 要用的 XCode 5.0.1 已經是 clang 5.0 了,所以不避開這個問題會無法在新的 OSX 上裝 Ruby,包括用其他 Ruby 管理工具也是一樣,像我其實就是用 rvm 要裝,還好有解法:

CFLAGS=-Wno-error=shorten-64-to-32 rvm install ruby-1.9.3-p392

fasd, 命令列加速工具

以前曾經介紹過autojump這個很好用的快速切換目錄的指令,後來大貓跟我說有個叫z的,一樣用途,原理也差不多,不過 z 的位置比較好按些,最近在看Vim Scripts時,意外發現到有個fasd,也是一樣的原理,不過他的功能比較強大,配合一些 alias 就可以做到和z或是v一樣的功能,作者對相關的領域很熟悉,對於常用 shell 的整合很好,像是 zsh 和 bash 的指令補完就都有支援,目前正在改用他,Mac 安裝很方便:

brew install fasd

然後在.bashrc或是.zshrc加上

eval "$(fasd --init auto)"

就可以了,其他環境有包好的就比較少了,可以看看他的 Wiki:Installing via Package Managers,其他的環境我測試過 Ubuntu 編譯安裝都很順利,基本上只是拷拷檔案而已。


Build v8

平常是使用 Google V8 引擎的 command line 來做自己工作機的 js console,最主要的用途是跑 jslint,不過這需要自己來編譯,以前是用 scons 來編譯,剛剛想要編譯新版發現 Google 又換了自動化工具,從scons換成gyp,安裝流程其實比較簡單,先安裝好 svn,然後執行:

make dependencies
make console=readline native

產生的 d8 執行檔會在 out/native/ 下面,官方說明有提到可以用 clang 編譯,不過我嘗試後會有錯誤,還在看要怎樣處理。


automjump

今天發現的有趣的命令列工具autojump,它會記錄你常用的目錄位置,然後之後就可以用簡單的關鍵字跳到路徑符合關鍵字,又最常用的目錄,以我為例,我最近常常會跑到某個位置的 vim-plugins 目錄,然後我又沒有其它目錄名稱和 plugin 相似,那不論我在哪,我都可以執行下面的指令跳到 vim-plugins:

j plugin

我目前使用上是都沒什麼大問題,不過hlb說他的 vim 檔名自動完成會爛掉,可能要在研究看看問題在哪了。


此類別所有文章