拖稿拖很久的 Packer 簡介文(主要講的是如何建 AWS EC2 AMI)

這篇是很久以前答應 hSATAC 大人說要寫的,由於一直有更有趣的事情可以做所以就拖稿拖到現在,主要講的是如何使用 Packer 來快快樂樂建 AWS EC2 AMI,將這件事情給它 infrastructure as code 這樣。

範例檔案放在 [GitHub] 這裡,這篇舉的例子,是建出一個把 Drupal 8 裝好的 AMI,先看 packer.json:

{
    "variables": {
        "drupal_version": "8.2.5"
    },
    "builders": [
        {
            "type": "amazon-ebs",
            "name": "drupal-8-ami-us-east-1",
            "ami_description": "My Drupal 8 barebone AMI ({{user `drupal_version`}}) (us-east-1)",
            "region": "us-east-1",
            "source_ami": "ami-e13739f6",
            "instance_type": "t2.micro",
            "ssh_username": "ubuntu",
            "ami_name": "drupal-8-ami-{{timestamp}}",
            "tags": {
                "Name": "drupal-8-ami-{{timestamp}}",
                "Distribution": "Ubuntu Server 16.04 LTS (HVM), SSD Volume Type",
                "Drupal": "({{user `drupal_version`}})"
            },
            "ami_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "launch_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "vpc_id": "vpc-cc9aeba9",
            "subnet_id": "subnet-940f76ae",
            "associate_public_ip_address": true,
            "security_group_ids": [
                "sg-15a50671"
            ],
            "communicator": "ssh",
            "ssh_pty": true
        },
        {
            "type": "amazon-ebs",
            "name": "drupal-8-ami-us-east-2",
            "ami_description": "My Drupal 8 barebone AMI ({{user `drupal_version`}}) (us-east-2)",
            "region": "us-east-2",
            "source_ami": "ami-d1cb91b4",
            "instance_type": "t2.micro",
            "ssh_username": "ubuntu",
            "ami_name": "drupal-8-ami-{{timestamp}}",
            "tags": {
                "Name": "drupal-8-ami-{{timestamp}}",
                "Distribution": "Ubuntu Server 16.04 LTS (HVM), SSD Volume Type",
                "Drupal": "({{user `drupal_version`}})"
            },
            "ami_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "launch_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "vpc_id": "sg-789c2d11",
            "subnet_id": "subnet-60e84409",
            "associate_public_ip_address": true,
            "security_group_ids": [
                "sg-789c2d11"
            ],
            "communicator": "ssh",
            "ssh_pty": true
        }
    ],
    "provisioners": [
        {
            "type": "file",
            "source": "./nginx/default",
            "destination": "/tmp/nginx-default"
        },
        {
            "type": "shell",
            "inline": "wget https://ftp.drupal.org/files/projects/drupal-{{user `drupal_version`}}.tar.gz -O /tmp/drupal.tar.gz"
        },
        {
            "type": "shell",
            "script": "./provision.sh"
        }
    ]
}

這份 Packer template 裡頭很多地方都可以望文生義,我就不一一逐條解說了。只講一些概念上的、可能會遇到坑的地方。

首先,一份 Packer template 分為好幾個部份,我這個例子裡,就分成了 variables, builders 以及 provisioners 三塊。

其中,在 variables 裡我們可以宣告接下來會用到的變數,在我這個例子裡,我把想裝的 Drupal 版本寫在這裡,之後很多地方都可以用 {{user `drupal_version`}} 來引用。

然後看到 builders 這塊,我分別想在 AWS 兩個美東 region 建立需要的 AMI。這裡有個小 tricky 的地方,就是如果我們不特別寫 name 這個屬性的話,Packer 是不會允許我們建立多個同一 type 的 images 的(這裡就是指 amazon-ebs),如果我們需要在複數個 AWS region 建立 AMI,就要特別寫明 name

還有要注意的一個概念是,Packer template 裡面寫的各種屬性,是在「建立 image 當下」使用的,AMI 建完後,要怎麼使用是你家的事,Packer 並不負責這塊。所以呢,為了 Packer 方便作業(在這裡是透過 SSH 連進一台為了做 AMI 而臨時開出的 EC2 instance),像是 vpc_id, subnet_id, associate_public_ip_address, security_group_ids 這些設定,是 Packer 呼叫 AWS API 開一台 EC2 instance 時使用的,特別是 security_group_ids,注意這些設定要能夠保證 Packer 可以 SSH 登進去機器,不然就等著 timeout 執行失敗。

provisioners 這裡我們可以用很多方式來對 AMI 做 provisioning,Packer 官方提供了很多常見的 provisioners,除了基本的丟 file、跑 shell script 以外,還有一些很潮很夯或不潮不夯但有用的自動組態工具像是 Ansible, Chef, Puppet。搭配這些自動組態工具就有更多花樣可以玩,可能也比 shell script 更好處理一些更需要彈性的東西。

最後提一個小技巧,就是如果我們跑 packer build packer.json 時遇到問題,那麼用 packer build -debug packer.json 進除錯模式,Packer 會把它為了這次 building 動態產生的 SSH key 寫在檔案系統裡,讓您可以手動 SSH 進去這台機器,方便診斷問題所在。

該如何行銷 Rust 的筆戰

先從 [Rust is more than safety] 這篇開始,作者認為到目前為止,Rust 在行銷上並沒有打到「我能為用戶解決什麼問題?」的痛點,光只在語言設計的層面上兜圈子,特別是「安全」這點,然後有了這些回應:

  • [graydon2 | Rust is mostly safety]: 作者認為以一個系統語言來說,Rust 不但追求「安全」還特別致力於滿足 concurrent 下的「安全」,就是 Rust 的最大賣點。
  • [Safety is Rust’s Fireflower]: 作者認為 Rust 主打的「安全」,就是讓涉足系統程式設計的用戶,終於有一個在這方面做得比過去同質產品更好的選擇可用,如同瑪莉歐裡的金花一樣。
  • [Fire Mario, not Fire Flowers]: 原作者被各方吐嘈後寫的回應。引用前面瑪莉歐的譬喻,認為 Rust 的行銷應在是聚焦在「吃了 Rust 這朵『安全』金花,會讓你成為火球瑪莉歐」,而不光是「Rust 是一朵有『安全』功效的金花」。
  • [Fire Flowers and Marios: Marketing Rust – Medium]: 各打五十大板的吐嘈。我覺得這篇前面有點像來亂的,但是後面寫得還頗有道理,對於技術的行銷來說很中肯。
  • [Rust is Software’s Salvation – Redox – Your Next(Gen) OS]: Redox 這個採用 Rust 寫的 OS 專案也來參戰,作者寫出從早期因為 Rust 的幾項特性 (safety, concurrency, performance, documentation) 而開始接觸這語言,算是忠實愛用者,但是覺得 Rust 當前的問題之一就是「太少東西是以 Rust 寫成的」,從這點引申論述 C 語言為何至今還是受到相對多數的用戶採用、C 語言的問題、軟體的品質問題、Rust 或許就是這個問題的救贖。

2016 年回顧

2016 年回顧:

  1. 硬著頭皮、厚著臉皮,報名碼天狗週刊的策展人,鞭策自己看 Erlang, Elixir 的東西。在這種大家都還沒凝聚出死板的操作定義、最佳實踐、把生態圈風氣變得人云亦云、按表操課、造神拜神的時候,很多東西可以隨心所欲地摸索,我覺得就是最好的時候。
  2. 悟出「購物車」這種系統,其實可以採取簿記原理設計。
  3. 在工作中採用 Scrumban 方法,以及實踐心目中的「真.DevOps」工作文化,覺得學到很多、成長很多。也比較快樂。
  4. 「用戶想的與你不一樣」,其實沒有倒站,卻在用戶的感受下(所謂的 UX)認定我們就是倒站, 雖然覺得冤枉,但是習得這樣的教訓並改進相關的 UI 後,覺得如果自己是用戶,這樣的改良用起來也會比較爽。也是一次寶貴的經驗。
  5. 從「月薪嬌妻」中警惕自己對於「責任」與「義務」的區別,不能因為因循苟且而麻木、失去判斷力。追求婚姻的品質,這是根本。

買了幾片 Arduino Leonardo

想做兩個應用:

  • 鄰居的二手菸偵測器
  • 依空間亮度、時段的芳香噴霧機啟動開關

之前其實我對 Arduino 抱著一種鄙夷的態度,認為男子漢既然要做這種單晶片應用,就該直接去用單晶片,這樣才硬派。

但是直到最近想做上列的兩個應用,光是製作一塊電路板,就讓平時已經忙得不可開交的我卻步。而 Arduino 這種把介面(無論是指軟體或硬體)、控制語言的基礎先弄得具體而微的快速原型製作工具,其實幫人省下了不少時間。

而且做出來的東西,就算不是量產等級的,但也不只是不中用的「玩具」,而是確確實實能派上用場的成品。

就是這樣,我對 Arduino 的觀感開始轉為覺得它可親可愛。

雜論 SNS 與入口網站的資訊架構設計 (2)

一開始接著要寫這第二篇的時候,我的腦袋又陷入了之前寫學位論文時的窠臼,或者說是心魔,心想要有一套論述、理論,來支持我最後想帶出來的結論,於是愈寫愈覺得處處碰壁,深怕哪邊沒防禦好、又要被吐嘈。

唉,網路何時變得這麼可怕?(掩面嚇哭)

不過這系列廢文就只是「雜論」罷了,我真的給自己太多壓力了。且其實資訊架構有其適合的人事時地物(不過也存在著因循苟且、習慣成自然的那種「沒有架構的架構」啊…),並沒有絕對的是非好壞。所以我只要照我的腦子想到哪、就記到哪、歸納到哪,這樣就好了吧?

巴哈洽特

因為 Chat 被起了個渾名叫「洽特」,所以我就自己把巴哈姆特 BBS 的 Chat 看板戲稱為「巴哈洽特」。

在很久很久以前,巴哈姆特 BBS 還算百花齊放的時候,Chat 只是一個「不分類雜談區」,但是現在 Chat 可能是巴哈姆特 BBS 之所以還維持資訊流量的、一枝獨秀的看板。在我忘記鳥窩 BBS 密碼、且鳥窩關站後,Chat 就是我溫度最接近的同溫層了。

為什麼要特別講這個呢?是因為我覺得巴哈姆特的「主題串接模式」很難用,又或者說目前這些還活著的 Telnet BBS 站的同質功能都很難用,在訊息量大的 Chat 尤其常讓我覺得心煩意亂,追某主題的新文章,得一直在「一般文章列表」、「主題串接模式」間切換。

但是這當中的含金量、哏量又每每讓我覺得心滿意足。

Twitter 在訊息列表整個攤平 (flatten) 這點上有些相似。Twitter 把資訊單位化簡到「某個人,在某個時間,講了什麼話」的「訊息」,然後在「時間軸」上鋪排開來,這樣的資訊架構。要追個前後文,來來回回點個好幾層是常有的事。

不太一樣的地方是,Twitter 通常是我先看到我 follow 的帳號用戶發的推文,或這些帳號轉他人的推文,但是 BBS 比較沒有這樣的 filter bubble 效應。

現在很多 Web 式的論壇系統,也稱自己為 BBS,但在討論串 (threaded) 呈現這方面,做得更直觀、好用些,甚至還有 semi-threaded 與 full-threaded 的結構。

我當然是希望我的「這個 BBS」能夠把討論串的呈現弄得更好爬文。而我設想的結構甚至不是樹狀,而是圖 (Graph)。

雜論 SNS 與入口網站的資訊架構設計 (1)

寫這篇文的時候,我正在設計一套 BBS。我希望這套 BBS 除了可以找回過去「只要有心(加上做了一些功課),人人都可以架一個 BBS 站」的樂趣,以及期許這樣的樂趣會喚醒人們對 SNS(社群網絡服務)其實可以不必那麼中心化 (centralized) 的認知,還有一點點練功的成份在。

呃,好吧,我承認,也許不只是一點點而已,我希望透過從做中學,瞭解一個基於 Web 技術的 SNS 該如何設計、實作以及營運管理。

因為這樣的動機,促使我整理對於「這個 BBS」應該如何鑑往知來的想法。所以本文會以時間為主軸,在這條線上看 SNS 與入口網站的資訊架構設計。

1995: BBS 以及絕大部分很靜態的 Web

1995 年,在電腦史上,或者不要那麼浮誇,至少在我的個人電腦利用史上,我覺得是很重要的一年。這年,當然地,Microsoft 的 Windows 95 在 PC 市場上取得了成功,積極意義上就是說,PC 市場上有了一個普及的 32-bit 作業系統,這使得 PC 用戶的電腦終於有一個能夠充分發揮硬體性能、且不會因為單一程式影響就動輒當機死透的多工、圖形化、多媒體使用介面。

為什麼這個需要在開頭大書特書?因為有了這麼一台具備圖形、多媒體處理能力(且足夠穩定了)的設備,可說是後來我們之所以可以體驗 Web 的一種基礎建設。

(這裡不免要再大膽不怕羞地提一次,當時我是 Windows 95 競爭對手、IBM OS/2 Warp 3.0 的狂熱愛用者,以現在的流行語來說,就是個腦殘粉。)

約莫同時,Internet 開始普及到民間,在這之前,數據機 (Modem) 的功能多是玩家用在架設與連接 Dial-up BBS,這種 BBS 跟現在大家用的 Telnet 式 BBS 不太一樣,信件、討論文章為了要節省撥接的電話費,所以通常是用「快信軟體」下載到自己電腦上後再閱讀,同時將您的回應上傳,形成一種「信包交換網路」。

而因為「時間就是金錢(電話費)」、「空間也是金錢(那時主流規格硬碟容量約莫是 540MB~1GB 之間)」的緣故,Dial-up BBS 很注重「言之有物」,不歡迎「灌水」、不需要的「引言」需要刪除。這樣的文化,其實也影響了早期的 Telnet BBS。

回到正題。也就是說,在 1995 年,如果您用約二~四千元台幣買了一台當時主流規格 14.4K 或 28.8K Bps 的數據機,再跟 ISP 業者租用他們的上網服務,或是極其有幸,透過學校電腦教室、宿舍提供的網路連線,就可以「上網」。

為什麼要提這件事呢?這件事的隱含意義是:無論用數據機、網路卡還是什麼我這個鄉下人沒見過的黑科技,在當時終於可以讓一台 PC(當時咸認最具代表性的「資訊設備」)不再只是一座孤島,讓用戶有了線上連結、在上頭可以資訊交流、發展社交,如此的可能性。

而當用戶連上網,或撥進 BBS 後,呈現在其面前的,是由「信區」、「新聞群組」、「版面(看板)」為單位,組織而成的一種資訊架構,透過這樣分門別類、按圖索驥的方式,用戶被動去適應這樣的主題分類,或是積極一點去申請一個主題區,但無論如何,用戶是圍繞在「主題→主題分類」這樣的框架下進行資訊交流。

同時,在 Web 這邊,我們回顧以前的蕃薯藤或是 Yahoo! 的分類目錄設計,一樣也是讓服務提供者決定了主題、分類體系,來引導我們尋得或整理相關的資訊。

那麼,如果您的興趣是所謂的「次文化」、「惡趣味」、不見得在這個分類法下找的到一個歸屬的呢?又或者,您根本不想「被分類」,上網只是想要有個地方裸奔、自言自語呢?BBS 裡可能有些站台可以申請「個人版面」或您乾脆架個「個人站」,Web 也可以有業者提供的免費空間,您就自己架個獨特的網站吧!

大致上說來,當時的網路,是很強調「主題分類」這個框架的。我覺得箇中原因之一是,當時還缺乏一個夠強大、很會爬的搜尋引擎,好讓用戶跳脫被設定的分類體系、自行決定「要找什麼」;另外一個原因,就是適應人類其實很喜歡翻目錄、型錄的天性。

解決在 Vim NERDTree window 裡誤切換 buffer 的問題

這篇是承自之前提過的問題,今天終於花了一點時間,看了一下 NERDTree 的 source code 找出解法。

在自己的 .vimrc 裡新增這麼一段自訂 function:

function! LeaveNERDTree()
  if winnr() == g:NERDTree.GetWinNum()
    wincmd p
  endif
endfunction

大概的邏輯是這樣:

  1. 如果目前所在視窗的號碼等於 NERDTree 的視窗號碼
  2. 不管怎樣,跳開就對了,cursor 就是不要在 NERDTree 裡面

然後就可以套用到自己切換 buffer 的快速鍵上:

nnoremap <silent> <F3> :call LeaveNERDTree()<CR>:bprev<CR>
nnoremap <silent> <F4> :call LeaveNERDTree()<CR>:bnext<CR>

短短幾行,解決了長期以來的、常常為了自己操作錯誤而惹得很毛的問題,怨自己當初為什麼沒仔細去看 source code?

Game & Watch EG-26: EGG

昨晚,幫老婆找出童年時玩過的掌上型電玩「接雞蛋」,前因是我跟強者我同事 hSATAC 借 PS Vita 玩,被她看到又再問了一遍:「有『接雞蛋』嗎?」這是她不曉得第幾次提到「接雞蛋」了。心想平常不碰電玩、最多只玩手機休閒遊戲的她,看到別人在玩掌上型電玩就會聯想到「接雞蛋」,就像提到電視遊樂器就有很多人聯想到「超級馬力歐」那般懷念的經典?

用 “Game and Watch Egg” 去搜尋,因為這類液晶電玩,任天堂的 Game & Watch 當然是最知名系列產品,其次應該是 CASIO。

先問是不是就是影片中的這款:

老婆猛點頭說對,看來沒有比我也很懷念的 Game & Watch MW-56: MARIO BROS. 難找。

於是再往 Google Play 上找看看有沒有 clone 這款的遊戲,果然有,而且還不少人做!而 MW-56 這款搬貨工馬力歐兄弟卻沒人 clone,我難過。

看了一下維基百科才明白,原因是 EGG 這款當年在蘇聯有仿製品,所以可能是「國際」(←又是大叔的雙關語冷笑點)知名度最高的一款 Game & Watch,也解釋了為何 Google Play 裡找到的仿製遊戲面板是俄文。

頭一次見到老婆玩遊戲玩得這麼開心,比上次「打飛機」還開心。其實不管是「接雞蛋」還是「搬貨工」遊戲規則都很簡單,變化只在速度與偶爾的例外攪局,就能帶給人樂趣,在這個凡是遊戲都要 3A 級才上得了市面、看得上眼的時代,其實回頭想想,我們在追求無止盡的聲光效果進化時,是不是先問問「我這樣玩得開心嗎」?

之前在巴哈姆特裡不時有針對「任飯」「你們說任天堂作品的『遊戲性』較高?什麼叫『遊戲性』?你們自己都說不上來!」這樣的攻擊。我現在想想,「遊戲性」其實就是一種 UX(用戶體驗),或許 UI 有 guideline 可以遵循,但是光跟著 guideline 做,不代表你就能營造出讓人覺得 “Awesome!”, “Incredible”, “Wonderful” 的 UX。

嘗試做一個 envchain 的仿品 (6)

最近這幾天,下班後沒什麼動力,而且胃病又找上門,更是折磨人,還是沒有什麼明顯的進度。看了一下 dbus-rs 給的範例裡滿滿的 unwrap(),很好奇這方法是何作用,便搜尋了一下:

To “unwrap” something in Rust is to say, “Give me the result of the computation, and if there was an error, panic and stop the program.”

原來是一個錯誤處理用的便捷(且看來有點暴力?)方法。

嘗試做一個 envchain 的仿品 (5)

今天沒有什麼特別的進度,就是慢慢地把 Ruby PoC code 往 Rust 改寫而已。因為想要採行 TDD,所以按照  convention 為專案加上 tests/ 目錄,測試程式就可以放在裡面。過程當中因為同檔名程式的關係在鬼打牆,還以為怎麼程式就是無法跑出預期的結果。

同時把 rustfmt 架起來,仿照 gofmt 自動在我用 VIm 儲存 Rust 程式時自動排版程式碼。