Pullup – 在 Kubernetes 上部署與測試 Pull Request

本篇接續上一篇文章,是我在 Dcard 開發的第二個 Kubernetes 工具。

開發流程

圖片來源

目前敝社的開發流程基於 GitHub flow,大致上如上圖所示。要開發新的功能時,會從主要分支 master 開出新的功能分支 feature,功能開發完成後會提出 pull request,審核通過後就會合併進 master,並部署到 staging 伺服器上。

通常在審核 pull request 時,我們僅會檢視程式碼和附帶的測試,如果一切順利而且 CI 測試通過的話,就會直接合併進 master。然而在某些情況下,特別是以前端極少測試的狀況來說,只看程式碼有時無法看出問題所在,必須實際執行才能確保程式能夠正常運作,有時也可能需要 PM 確認程式符合 spec,或是讓設計師確認程式符合設計稿。

對於工程師來說,要把程式執行起來並不困難,但對於其他人來說,光是設定環境可能就是件讓人頭痛的事了。我們常用的方法是直接讓他們透過區網或 ngrok 連到工程師的電腦上,有時甚至為了上 staging 伺服器就直接合併進 master 了。讓還沒審核通過的程式碼進入 master 不僅可能造成 staging 伺服器的異常,也有可能會影響其他工程師的開發進度。

Read More

在 Monorepo 裡用 TypeScript

最近在開發公司內部使用的工具時,心血來潮想用 Lerna 來管理 monorepo,但是又想用 TypeScript,結果碰到了一些編譯上的問題,例如套件之間互相依賴時,TypeScript 不知道依賴關係而無法了解編譯順序,導致整個 monorepo 無法編譯成功。

之後發現了 Quramy/lerna-yarn-workspaces-example 這個範例,裡頭用了 Yarn、Lerna 和 TypeScript,在這之中 Yarn 並不是必須的可以忽視,重點是在 TypeScript 3.0 推出的 Project References,這個功能讓 TypeScript 能夠知道各個模組之間的依賴關係,因此自動解決了編譯順序的問題。

要使用 Project References 的話必須在 tsconfig.json 加上下列選項。

1
2
3
4
5
6
7
8
{
"compilerOptions": {
"composite": true,
"declaration": true,
"rootDir": "src",
"outDir": "dist"
}
}
  • composite - 讓 TypeScript 能夠快速找到被引用專案的位置。
  • declaration - 編譯定義檔 (.d.ts)。
  • rootDir - 設定專案的根目錄,預設是 tsconfig.json 的所屬資料夾。
  • outDir - 編譯的輸出路徑。

並在要引用的地方加上 references

1
2
3
4
5
{
"references": [
{ "path": "../x-core" }
]
}

然後在執行 tsc 時加上 -b 選項以及要編譯的專案路徑,就能順利編譯。

1
tsc -b packages/x-core packages/x-cli

因為專案比較多,所以我另外寫了一個 script 自動找出所有需要編譯的專案路徑。

build.jsSource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"use strict";

const spawn = require("cross-spawn");
const globby = require("globby");
const { dirname } = require("path");

const TSC = "tsc";

const pkgs = globby.sync("packages/*/tsconfig.json").map(dirname);
const args = ["-b", ...pkgs, ...process.argv.slice(2)];

console.log(TSC, ...args);

spawn.sync(TSC, args, {
stdio: "inherit"
});

執行 tsc 時加上 --watch 就能夠監看檔案變化並自動重新編譯。

1
tsc -b packages/x-core packages/x-cli --watch

執行 tsc 時加上 --clean 則是能夠自動根據 outDir 設定清除編譯後的檔案。

1
tsc -b packages/x-core packages/x-cli --clean

可以在 tommy351/kosko 看到實際運用的範例。

用 Elixir 和 hackney 做 proxy

前幾個月改版時,我決定用 Elixir 來實作 OAuth server + Proxy,這是一門結合了 Erlang VM 和 Ruby 語法的程式語言,可以很容易運用 Erlang 的特性做出低延遲、高並發且高容錯度的系統,又不用學習 Erlang 比較特殊的 Prolog 式語法(但是你可能還是多少要懂 Erlang 語法,因為很多時候你會直接運用 Erlang library)。

Erlang 的這些強大特性拿來做 OAuth server + Proxy 似乎有些大材小用,不過因為我爽,所以就決定用 Elixir 來寫了。

Read More

使用 Ansible 管理 Google Compute Engine

最近忙著佈署新的測試伺服器,而 Google Cloud Platform 剛好有提供 $300 兩個月的免費試用,且在台灣又有設點,所以我就決定拿 Google Compute Engine 來建置測試伺服器了。

Dynamic inventory

在開始之前,先稍微解釋一下何謂 Ansible 的 inventory,inventory 即代表伺服器,在 Ansible 中,可把伺服器列在 inventory file 中,藉此來分類伺服器,例如:

1
2
3
4
5
6
[webservers]
1.2.3.4 ansible_ssh_user=john
5.6.7.8 ansible_ssh_user=john

[dbservers]
9.10.11.12 ansible_ssh_user=mary

然而伺服器一多,管理 inventory file 就顯得有些麻煩,這時可以利用 dynamic inventory,只要把 inventory file 指定為可執行檔,Ansible 就能從執行檔的輸出中取得 inventory 資料。

Ansible 官方提供了各種主流主機商的 dynamic inventory,可以直接取用,而 GCE 當然也沒有缺席:http://docs.ansible.com/ansible/guide_gce.html

Read More