由於 Ingress 目前還沒有支援直接 rewrite to HTTPS 的功能,目前只能在 App 層透過 Rails 在 production.rb 裡面設定 config.force_ssl = true 來讓瀏覽器轉向 HTTPS。然而將設定放上後,service 將會正常個一陣子,過不久後變成這個畫面。

然而 kubectl describe pod/POD_NAME 跟 kubectl logs POD_NAME 都沒有異狀,甚至 kubectl exec -it POD_NAME bash 去裡面 curl http://127.0.0.1:3000/ 也都是沒問題的,到底是什麼原因造成的呢?
到 GKE 的 dashboard 看,發現服務裡面的 ingress 項目中 ingress.kubernetes.io/backends 的 annotation 顯示為「UNHEALTHY」。查了一下發現原來 Ingress 在建立時若 deployment 沒有自己的 livenessProbe 的話,就會預設自己建立一個用 HTTP GET / 來判斷服務是否健康的服務,這個自動被建立的項目可以從「Google Computer Engine」裡面的「健康狀態
檢查 」看到,這個自動建立的項目有個問題是,當 GET / 時只要 HTTP 回傳狀態不是 200,都一律視為「UNHEALTHY」,而 Rails 的 force_ssl 選項會讓 HTTP 的連線都一律被回傳 HTTP 302(redirect to HTTPS://xxx),這時 Load Balancer 就會以為這個 Web app 有問題,而不把流量導到那邊去,所有節點都回傳 UNHEALTHY 時,這個網址也就爆了。
當然直接去修改健康狀態檢查也可以,不過能的話當然還是希望在 kubectl 佈署時就能搞定這一切對吧?
加上 livenessProbe 等敘述
在佈署時將 livenessProbe 與 readinessProbe 可以讓 Load Balancer 知道這個 pod 是否已經就緒,跟運行狀態是否良好。
apiVersion: apps/v1
kind: Deployment
metadata:
name: someapp
spec:
selector:
matchLabels:
app: someapp
template:
metadata:
labels:
app: someapp
spec:
containers:
- name: someapp
image: someapp/someapp:latest
imagePullPolicy: Always
ports:
- containerPort: 3000
livenessProbe:
httpGet:
path: /healthcheck
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthcheck
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
Rails 方面的 ssl_options 設定
另外這個 /healthcheck 也必須在 Rails config 裡面做些設定,讓這個網址在被 query 時不會被 redirect to HTTPS(此方法可能只適用 Rails >=5.2 的版本):
config.force_ssl = true # 或者較有彈性的 ENV[“FORCE_SSL”].present?
config.ssl_options = {
redirect: {
exclude: -> request { request.path =~ /healthcheck/ }
}
}
至於這個 /healthcheck 就要另外到 config/route.rb 裡面去另外開一個 route 給他了。懶得開的話,也可以使用某個 asset(例如 /404.html),並在 deploy 時的 env 記得加上 RAILS_SERVE_STATIC_FILES=true
deploy apply 了這個加了 livenessProbe 的設定後,記得重新建立一次 Ingress,如此一來新的 Ingress 才會自動使用這個健康狀態檢查的參數,而重新建立 Ingress 後可能會需要 10~20 分鐘左右的佈署時間,(尤其如果有使用 cert-manager 的話)。
以上是純 Rails 與 force_ssl 的會遇到的一些小麻煩,本來只是想偷懶不多用一層 nginx 來當 Rails proxy,結果反而好像弄得更麻煩了啊(菸)。