Firebase functions 裡用 Admin SDK 時,怎麼去把設定跟程式切開?

一般來說,照文件來做,會要放兩個設定,第一個是 Service account credential,第二個是 Firebase config。

但其實在部署 functions 上去以後,這些設定都已經在執行環境裡了,所以不需要特別去放這些設定。而且,把這些設定放到程式裡,那程式會上到 git repository 啊,這樣設定就都曝光了。那不想把這些設定放到程式裡,本地端又要開發時該怎麼辦呢?我是找到這篇:Firebase: Separating configuration from code in Admin SDK

第一個,Service account credential 在下載以後,假設放到 /credentials/your_service_account.json,那麼在執行前,把這檔案路徑指定到 GOOGLE_APPLICATION_CREDENTIALS 這環境變數就可以,例如:

export GOOGLE_APPLICATION_CREDENTIALS=/credentials/your_servcice_account.json

第二個是 Firebase config,從 firebase console 取得以後,假設放到 /config/your_project_config.json ,裡面內容大致是

{
apiKey: "your_api_key",
authDomain: "your_project.firebaseapp.com",
databaseURL: "https://your_project.firebaseio.com",
projectId: "your_project-abcdef",
storageBucket: "your_project.appspot.com",
messagingSenderId: "00000000",
appId: "1:00000000:web:000000000",
measurementId: "G-11111111"
};

接著一樣去設定環境變數 FIREBASE_CONFIG

export FIREBASE_CONFIG=/config/your_project_config.json

然後就可以使用 firebase serve 去模擬啦。

筆記:Lessons learnt (the hard way) using Firebase RealTime Database

主要是看這篇 Lessons learnt (the hard way) using Firebase RealTime Database 所摘要下來的重點。

TL;DR:作者用了 realtime database,然後意外收到 1000 EUR 的帳單。

作者做的是交通運輸類的 app ,realtime database 存的是使用者的最愛站牌、路線等。主要用了 Firebase 的這兩項功能:

  • Firebase authentication
  • Realtime database

使用人數約 400K+ ,主要就是 updated / authenticated 等等的。這樣帳單約 1000 EUR
他們檢討以後,發現有幾個關鍵點:

  1. keepSync ,這個不要設成 true,firebase SDK的行為沒有預期中聰明,他會在每次使用者開啟app時就下載一次!
    1. database.getReference(getUserFavoritesPath(getCurrentUid())).keepSynced(true)
  2. key 的名稱不要太長,只要這個弄短,無形中可以省掉非常多。

接著針對這兩個點,做了處理

  1. 最佳化存在資料庫的資料,改用 GSON ,以及縮短 key 的長度。
  2. 關掉 keepSync
  3. 在 app 端實作了 memory cache ,以避免無謂的去 firebase 撈資料。

作者學到的事情:

  1. 人數很多時,realtime database 可能會是很花錢的項目,特別是結構跟讀取資料邏輯沒有最佳化的時候。
  2. 要將存在雲端上的資料最小化,使用較短長度的 key ,可以幫你省掉不少錢。
  3. Firebase persistence 啟用時,firebase 只會在沒網路的時候使用本地的 cache,其他的狀況他不管資料有沒有改都會存取雲端。
  4. 不要用 keepSync(true) 
  5. 實作本地端的 cache ,以避免重複跟 firebase 索取資料。

用 go 的 docker image 來建置 go 專案

指令蠻簡單的,而且可以指定用 go 的哪個版本來進行建置。

# 先切到你的專案目錄下
cd "$WORKSPACE"

# 執行 docker run
docker run --rm -e GOBIN=/usr/src/myapp -v "$PWD":/usr/src/myapp -w /usr/src/myapp golang:1.6 bash -c make
  • –rm 表示執行完就把 container 刪掉
  • -e GOBIN=/usr/src/myapp 是設定環境變數,GOBIN 這環境變數是指編譯好的執行檔所放的位置
  • -v “$PWD”:/usr/src/myapp 表示把當前的目錄掛載到 container 裡的 /usr/src/app
  • -w /usr/src/myapp 表示把工作目錄指定到 /usr/src/myapp
  • golang:1.6 這是表示要使用 golang 1.6 這版,其他版本可以參考 dockerhub
  • bash -c make 是指用 bash 來執行 make,要用 bash 的原因是大部份 Makefile 都是假設用 bash,用其他 shell 會很容易踩到雷。

dockerhub 上有更多說明。

查程式連網速度

之前家裡 wordpress 速度慢慢的,看了 PHP slow log ,發現是使用 libcurl 去外面抓資料時慢。後來用 SSH 連上去在命令列下使用 curl 試試,第一次會慢慢的,之後就比較正常了。

那,為什麼 curl 會慢?上網找了,有在 StackOverflow 上找到一個方法可以顯示 curl 在各階段處理時所花費的時間。先在 $HOME 下新增一個 .curlrc ,然後把下面內容貼進去

-w "dnslookup: %{time_namelookup} | connect: %{time_connect} | appconnect: %{time_appconnect} | pretransfer: %{time_pretransfer} | starttransfer: %{time_starttransfer} | total: %{time_total} | size: %{size_download}\n"

再去執行 curl ,輸出結果後面就會出現 curl 所花費的時間

dnslookup: 0.253 | connect: 0.264 | appconnect: 0.773 | pretransfer: 0.773 | starttransfer: 0.809 | total: 0.809 | size: 6665

那個時候,我連網速度慢的主因是 dnslookup 慢,所以就根據這點,去做調整。至於這個調整,就又是另外一個故事了。

如何寫 Git commit message

主要是看 How to Write a Git Commit Message 這篇文章的紀錄,這篇文章是 2014 年的文,我可能有看過,印象中之前有看過介紹原則跟工具的文章。之前的印象,第一行要明確、簡單,不要太長。有細項,從第三行寫起,用 markdown 語法。

這篇一開始先講為什麼,作者不囉唆,直接要你看自己的 git log –oneline 輸出,看自己覺得 OK 不 OK。所以可讀性很重要,可以很快的看懂,那麼就可以不用費心再去看裏面改了什麼。

主要有7個原則:

  1. Separate subject from body with a blank line :第一行寫主旨,第三行開始寫內容。
  2. Limit the subject line to 50 characters :主旨不要超過 50 個字元
  3. Capitalize the subject line :主旨的第一個字元大寫
  4. Do not end the subject line with a period :主旨不要有句點
  5. Use the imperative mood in the subject line :主旨要用祈使句,就動詞+名詞
  6. Wrap the body at 72 characters :內容的每行不要超過 72 的字元
  7. Use the body to explain what and why vs. how :內容要儘可能描述做了什麼、為什麼這樣做跟如何做。

Ansible – 版本判斷條件句

執行之前寫好的 Ansible 腳本,卻有錯誤,奇怪,明明之前是好的啊?看了錯誤訊息,發現是找不到 /proc/sys/net/ipv4/tcp_tw_recycle 這個檔案的關係。怎麼會沒有這個檔案呢?使用 SSH 連上主機去看,還真的是沒有,上網找了之後,發現是 Linux Kernel 在 4.12 以後移除掉這檔案了(連結)。

好吧,因為腳本還有機會在較低版本的 Linux kernel 使用,必須得使用條件式來處理。在 Ansible 腳本裡,可以用 when 來做條件判斷,但是要怎麼取得 Linux kernel 版本以及做條件判斷呢?在 StackOverflow 上看到這篇:How to compare kernel (or other) version numbers in Ansible

從這篇得知:

  1. 可以使用 ansible_kernel 就可以取得 Linux kernel 版本
  2. 可以使用 version_compare 來比較版本

所以下面的腳本就可以在 Linux kernel 是 4.12 以後的版本印出 “foo”

tasks:
  - name: Display "foo" if kernel > 4.12
    debug:
      msg: "foo"
      when: ansible_kernel | version_compare('4.12', '>')

太好了。可是再仔細看,執行時有 deprecated 的警告耶,說在之後會把 version_compare 拿掉。上 Ansible 網站看了 Ansible 的文件之後,做了調整,新的語法要使用 “is” 來判斷,然後用 version() 來取代 version_compare。

tasks:
  - name: Display "foo" if kernel > 4.12
    debug:
      msg: "foo"
      when: ansible_kernel is version('4.12', '>')

好,收工。

變數的命名

看這篇 The art of naming variables 的紀錄
變數命名的藝術,好吧,對非英語系國家來說,的確是有些難度。作者整理了一些慣例讓大家參考:

  1. Arrays :名詞字尾加上 s ,當複數。
  2. Boolean:變數用 is/has/can 開頭,例如 isOpen/hasWrite/canOpen,函數就用 check 開頭,例如 checkHasPermission()。
  3. Numbers:試著使用 min/max/total 來描述,例如 minBugs / maxPugs / totalFruits
  4. Functions:要有動詞跟名詞,例如 getUser() / calculateTotal() ;轉換的話,可以用 to,例如:toUppser() 。

我本來以為會有講 dict / list 的,但沒有,嗯,順手找了一下

  • List:可以考慮後綴就用 List,例如 namesList ,也有人把 list 擺在前面,例如 listOfNames
  • dict:有找,但沒有特定的慣例,參考 List 的命名作法應該是可以。然後有人這樣用 email_to_contact / ip_to_hostname

寫到這邊想起 Clean code 這本書有一章就是在講這個,直接看別人的讀書筆記/心得吧:

Cronitor

網址:https://cronitor.io

今天在 hacker news 上看到 crontab.guru,網址名稱挺不錯的,詳細介紹 crontab 怎麼用。正在想說怎麼會有人這麼佛心的建這網站時,就看到建立網站的是 cronitor.io。去看了 cronitor.io 以後,發現這個服務很有趣,是監控主機上的 cron job 用的。

一般 cron 的工作,只能透過 SMTP 發送結果,或者是自己去寫 wrapper ,把執行結果送到 slack, telegram 等服務。cronitor 就等同於是 wrapper 的角色,幫你執行指令,再把結果送到 cronitor 網站上,然後你可以在網站設定要通知誰。在網站上也可以去看 cron job 的執行歷程或者是知道到底有沒有被執行。

cronitor 這個工具不管是用網站介面或是 CLI,都需要先行安裝。安裝很簡單,就一個執行檔,下載以後,複製到 /usr/local/bin 就行了。(安裝說明,同時頁面也會提供你的 api-key)

tar xf cronitor-stable-linux-amd64.tgz
sudo cp cronitor /usr/local/bin
sudo cronitor configure --api-key <your-api-key>

你可以直接用網站介面,只是用網站介面,要先在介面上建立 cron job monitor ,然後再自己去改自家的 crontab,稍嫌麻煩。

用 CLI 比較方便,基本上用 sudo cronitor discover 就跑完了。sudo cronitor discover 會掃描你的 crontab,然後逐一詢問你 job 的名稱,然後再去網站上建立對應的 cron job monitor。這邊要注意的是,sudo cronitor discover 會自動幫你改 crontab ,也就是 root 的 crontab、/etc/cron.d 下的檔案都會幫你改掉。如果你不想這樣的話,得之後手動還原。目前網站雖然可以建立多個 cron job monitor,但目前只有一個 monitor 能運作,要用更多,得升級。收費的資訊在這裡:https://cronitor.io/subscribe

Bulma

介紹與教學

跟 Bootstrap 的差異與比較:https://bulma.io/alternative-to-bootstrap/

Django 生態圈已經有人寫 app 了 – django-bulma,支援 template 以及 form ,包的挺完整的。

AWS SES SMTP

前兩周碰到 GMAIL 很 GY 的問題,想說要不要去申請 MailGun ,後來想說,要另外申請服務挺麻煩(畢竟不是自家用),AWS 應該有這類服務吧。去 Google 以後,才發現自己之前有用過,只是全都忘光光了,這次要紀錄下來。這次的紀錄重點:

  1. 簡介
  2. 需要什麼?
  3. 如何移出 Sandbox?

AWS SES 是 AWS 的大規模電子郵件解決方案,可以用來寄發大量的電子郵件。他除了有提供 SMTP 設定之外,也可以直接使用程式來寄送。用 SMTP 設定方便的地方在於各個語言都有 SMTP 的函式庫可以用,所以設定填一填就可以寄了,不用額外再加函式庫。

要申請這個服務,要預先準備好網域,因為他是以網域為基礎。首先要新增網域,在進行新增網域的步驟時,步驟的說明會詳列出你該怎麼做來進行後續的驗證。這部份蠻簡單的,就只是去新增一個網域記錄,然後把 AWS 要求你填的資訊放進去就可以了。

在新增網域並進行驗證以後,就可以使用 SES SMTP 或是用程式去寄了。只是這時候是在 Sandbox 裡,有限制在的,不能寄太多信,寄件者跟收件者也都有限制。AWS Management console 也會提醒你,現在還在 Sandbox,如果需要提升額度或是可以寄送到外面,必須要提出申請 (有連結可以按),這邊就是照網頁說明去提出申請即可。提出申請時,AWS 會要求你回答幾個問題。這幾個問題主要是要確保 AWS 自身的立場,也就是當有人回報信件被濫用或退信時(抱怨),該怎麼去處理。

AWS 建議的作法就是要求你使用 AWS SNS ,當有退信或是抱怨時,讓 AWS 可以透過 AWS SNS 去通知你。AWS SNS 可以用郵件通知或是用 HTTP/HTTPS 去通知你的網站進行自動處理等等的。我後來是簡單的設定 AWS SNS,用郵件來通知我,等以後再來改成讓網站自動處理。

參考資料(來自AWS 文件):