• 續談 OpsWorks + Docker 管理架構

    Cookbooks 的規劃

    對應 OpsWorks 的任務分支,分為以下幾個 cookbooks:

    • common
    • setup
    • configure
    • deploy
    • shutdown
    • service-pack

    不要有類似 OpsWorks 的 nginxrails 這類獨樹一格的「專用」、「特用」cookbook,依以上基於動詞的 cookbook 來做事。

    比如如果遇到要設定 nginx 某個獨特的項目,就是 setup::nginx_do_somethingconfigure::nginx_do_something,而不要自己搞一個 nginx::do_something

    這樣做的好處就是管理起來簡單,一眼望過去知道這個 recipe「要做什麼事、對象是誰」,寫 recipe 時有明確分類可以遵循。

    其中有幾點要注意:

    • common cookbook 是用在「支援、配合任一其他類 cookbook」的地方,而不是「萬用類」。開發初期如果不曉得某任務應該歸在哪類,可「暫時」放在這裡,一旦確定放在某特定類比較適當後,則應立即 refactoring 改放到該特定類。
    • service-pack cookbook 適合用在「單次任務」如元件安全更新、強制不使用 cache 重建 Docker images 的地方。
    • 因為 Docker 的特性,基本上不需要 undeploy 類的任務,退版其實就是切回到某個舊版本的 Docker image 開 container 出來而已。所以不規劃 undeploy cookbook。

    Docker 的部署設計

    理想上,應該是把 docker image 從 registry pull 回來後立即 docker run 就能動起來。

    實務上,我們有些地方還是需要仰賴 stack, layers, nodes 的設定資訊,所以還是免不了要使用 Chef 的 template 命令幫我們生成設定檔與 Dockerfile,最後再疊加上一層,生成新的 image 後予以部署。

    部署時特別要注意,像是 Rails 應用會產生 logs、Unicorn 也會產生 logs,這些 logs 是營運績效、問題診斷的重要資訊 ,我會盡可能在 container 內把這些 logs 放在同一個目錄下,再用 volume 對應到 host 某一目錄底下保存。這樣可以直接從 host 取得 logs,不用進到 container 內,方便很多,也利於 logrotate, fluentd 之類的工具操作這些 logs。

    目前想到的是這些,以後若有想到再寫…。

  • NHK World Premium 從台灣大寬頻類比頻道中消失

    今天凌晨,位在 CH95 的 NHK World Premium 突然斷訊,有幾秒的時間被調到 CH94,旋又消失,接著 CH95 換載 MTV 綜合台。

    求證有裝數位有線電視機上盒的岳家,NHK World Premium 還在數位頻道裡。

    親眼見證這段歷程,很有感觸,我在當下還抱著希望,以為像之前一樣只是被賣藥台排擠而換了頻道。重新掃描頻道掃了兩、三次,再按著遙控器一台接一台尋找,覺得這很像是國中時新買的自行車失竊,我茫然地在附近兜圈那樣。

    我知道類比頻道頻率範圍有限、數位頻道(理論上)無限的道理,但是今天台灣大寬頻可以這樣獨斷決定一個類比頻道的去留,我相信同樣的事情,很有可能也會在數位頻道裡發生。

    於是我(終於)決定去裝衛星碟。

  • 真假 DevOps

    今天談到一個話題:「不熟 OP 的 developer 如果缺少 OP 相關知識,那怎麼做好 DevOps?」

    我覺得這是個假議題。

    今天就是因為 developer 都認為 OP 會負責 deploy 到好、負責出來坦,所以開發時毫不考慮 OP 負荷,logs 也不看不管,這才是真正導致 DevOps 做不起來的原因,也就是我一直批評的「假 DevOps 之名,行傳統 OP 之實」。

    而 developer 真的需要很強的 OP/sysadmin 知識嗎?不用,真的不用,developer 缺的是很強的 DRY 意識。

    如果能時時設想「我做的這個東西,能不能在 10 分鐘內整套自動 setup run 起來?」那 developer 自然會去想「程式能不能通過測試?」、「設定、部署能不能簡單輕鬆?」這才是以 developer 這一側要實行 DevOps 該有的思維。

  • 我親愛的網站案件客戶,您好

    我自大學開始學習如何製作「帶有語意的網頁」、「樣式與本體分開處理的網頁」,早在 SEO(搜尋引擎最佳化)這個詞出現之前,便已瞭解「一個讓搜尋引擎覺得『好』的網頁,其結構應該是如何如何」的眉眉角角。

    從碩士班開始,又學到 IA(資訊架構學),深知內容如何妥善編排,導覽動線該如何設計。

    製作「帶有語意的網頁」、「樣式與本體分開處理的網頁」是讓機器(搜尋引擎、瀏覽器)高興。

    妥善編排內容,設計良好的導覽動線,則是讓高興。

    能夠兼顧「機器」與「人」的 Web 設計師在這市場上真的不多,許多從業者照本宣科跟著「專家」做 SEO,還不如我們這類圖資系出身的、一直被以為只能在圖書館櫃台處理借還書的人,動手調整一下網站架構,結果立即大不同。

    您的網站,因為我的勸阻,沒花錢去買當初您不知聽了哪位「專家」說的關鍵字廣告,但是在搜尋引擎排名還是名列前茅,實不相瞞,就是我提供給您如上述的專業服務,正如貴寶號也提供專業服務給客戶那般。

    您在意「效果」、「設計」、「美學」,是的,這些的確很重要,但並非一個網站的全部。當我向您表達我擅長的 Semantic Web, Information Architecture 如何幫助貴寶號的時候,希望請您不要認為那些不重要,而不當一回事,執意要做您認為「視覺效果好才是硬道理、壓倒一切」的網站,因為若忽略這些要素,您的網站只能吸睛,但不會好用,搜尋引擎也不愛來爬資料。

    無論之後我們還是不是可以繼續合作,我衷心感謝您看完我這篇苦口婆心、肺腑之言,謝謝!

  • 架設 Docker Registry v2

    我的架設方式與官方文件不太一樣,所以踩了不少雷。

    官方文件傾向把 nginx 與 docker-registry 都用 Docker container 的方式連接 (link) 部署,而我的方式與層次是:

    1. AWS ELB
    2. Nginx on EC2 Host OS
    3. Dockerized  docker-registry (registry:2)

    因為我想要閃掉 SSL/TLS 的設定,還有惱人的 private key passphrase,所以讓現成已經掛好了憑證的 ELB 充當 SSL/TLS termination proxy。在 ELB 後面的 nginx 與 docker-registry 就不用煩惱憑證的設定問題。

    Nginx 主要是負責提供 HTTP basic access authentication,在 docker-registry 前提供一層帳密安全關卡,由於我個人實在是很討厭使用 link,所以把它放在 host 裡。設定檔大致如下:

    server {
      listen 8080;
      server_name docker.registry.in.my.com;
      client_max_body_size 0;
      chunked_transfer_encoding on;
    
      access_log    /var/log/nginx/docker-registry-access.log;
      error_log     /var/log/nginx/docker-registry-error.log;
    
      location / {
        # Do not allow connections from docker 1.5 and earlier
        # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
        if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*\$" ) {
          return 404;
        }
    
        # To add basic authentication to v2 use auth_basic setting plus add_header
        auth_basic "registry.localhost";
        auth_basic_user_file /etc/nginx/conf.d/registry.password;
        add_header Docker-Distribution-Api-Version  registry/2.0    always;
    
        proxy_pass                          http://127.0.0.1:5000;
        proxy_set_header  Host              $http_host;
        proxy_set_header  X-Real-IP         $remote_addr;
        proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header  X-Forwarded-Proto https;
        proxy_set_header  X-Original-URI    $request_uri;
        proxy_set_header  Docker-Distribution-Api-Version   registry/2.0;
        proxy_read_timeout                  900;
      }
    }

    過程中我遇到的幾個大地雷有:

    • Ubuntu 14.04 內的 nginx 版本太舊,不支援 add_header 的 always 指令,導致不會送出該識別 header,所以 docker login 會判斷這個 docker-registry 為 Invalid registry endpoint,解決方法就是要從 nginx 官網裝新版(至少 1.7.5 以上,這顆地雷是從 [Running Secured Docker Registry 2.0 – Container Solutions] 這篇看到提示才解掉的)。
    • 由於 nginx <-> docker-registry 之間通訊是走 http://,所以在 push image 的時候,一開始從 remote 下 PUT 指令還是 https://,但是接著 docker-registry 會根據 X-Forwarded-Proto 的值送出 Location: http://xxx HTTP header 請 client 轉址,導致接下來的 PUT 操作失敗,解決方法就是要把 X-Forwarded-Proto 強制指定為 https

    幾個經驗:

    • 善用 curl -v 給的資訊來判斷,不要瞎猜。
    • V2 目前還是很多坑,使得你不得不去看 source code,但是還好小的我還學過一點 Go
  • OpsWorks + Docker 管理架構

    今(昨?)天在公司跟同仁分享的經驗,簡單筆記一下:

    • Elastic Beanstalk 自訂的彈性與便利性,都比不上 OpsWorks。
    • 公司在 Web 的主要使用語言是 Ruby:
      • OpsWorks = AWS + Chef
      • Chef 是用 Ruby 來管理 infrastructure
      • 所以相對來說,入門門檻相對較低
      • 理想上,應該可以跟 Vagrant 用同一套 Chef Cookbook(s),達成跨方案快速部署開發與營運環境
    • OpsWorks 每個 Stack 內只允許有一個 Rails App layer
      • 如果覺得在 custom layer 照抄 AWS 的 Rails App layer 套用的 recipes,就可以解決這個限制,你就會踩雷踩到想哭。
      • 如果為了要使用內建的 Rails App layer 而拆分到不同 stacks,又顯得太蠢,且無法佔到「放在同一個 stack 時可共用很多管理資料」的便宜。
    • 目前(我)已知最好的方法,就是架 Docker
      • 利用 Docker 的隔間特性,以 KTV 包廂為例,理想上 (container) A 房間開趴,不會影響到 (container) B 房間,更不會影響到大廳 (host)。
      • 所以我們甚至可以一台 EC2 instance 裡部署 A 應用,如果資源有餘裕,就可以堆 B 應用、C 應用、D 應用…而不互相打架,且不會被 OpsWorks 一堆未知的地雷炸到。
      • 實務上我會這樣堆疊:debian:stable → rvm-ruby-base → rails-base → app-base → app。
      • rvm-ruby-base 就是先在裡頭裝好 RVM 以及 Ruby。
      • rails-base 是預先裝 Rails 會用到的 Debian packages 如 NodeJS (XD)
      • app-base 預先 git clone 把 application 的 code 拉下來,先跑 bundle install,這樣預先裝好一堆落落長的 gems 後,往後疊 app 上去就不用把時間耗在重新安裝。
      • app 則是做 git pull,把異動拉下來後認 branch/tag 部署。這個放在 deploy 類的 recipes 底下,上面那些則是擺在 setup 類底下。
    • 好處:
      • 我們基於 Rails 的產品很多,用 AWS 也用很大,所以每個產品理想上都可以用同一套 cookbook(s) 做部署基礎,只需在 deploy 這邊自訂個別需求就好。
      • 就算不用 Rails,還是可以在 rvm-ruby-base 上疊其他的 Ruby 應用。
      • 好,就算不用 Ruby,你還是可以在 debian:stable 上堆其他應用。
      • 因為用了 OpsWorks & Chef,我們可以用 template 動態產生 Dockerfile 以及設定檔,可以當成「超彈性隨你玩加強版 ECS (EC2 Container Service)」來用。
    • 先決條件:
      • 規劃良好的 VPC subnets, security groups
      • 看 AWS 官方的 cookbooks 怎麼寫,模仿,並瞭解 node 物件有哪些東西可以方便我們部署
      • 考量日後發展,彈性劃分 Docker 堆疊