蒼時弦也
蒼時弦也
資深軟體工程師
發表於
這篇文章是 Rails 部署實踐 系列的一部分。

目前大多數的 Rails 專案都還是前端、後端一起開發的,因此我們還需要讓製作出來的容器鏡像能夠將網站運行所需的圖檔、JavaScript 等等製作出來加入到容器鏡像中。

目前我們還沒有搭配持續交付(Continuous Delivery)相關的設計,因此我們先以直接在容器鏡像製作階段製作素材(Assets)的方式。

利用多階段建置實現

目前 Rails 專案大多是以 Node.js 為基礎來編譯這些素材,然而我們的 Ruby 基礎鏡像並不會內建 Node.js,因此可以利用多階段部署來解決這個問題。

 1# ...
 2FROM node:16.14-alpine as node
 3FROM ruby:3-alpine as assets
 4
 5COPY --from=node /usr/local/bin/node /usr/local/bin/node
 6COPY --from=gem /usr/local/bundle/config /usr/local/bundle/config
 7COPY --from=gem /usr/local/bundle /usr/local/bundle
 8COPY --from=gem /src/vendor/bundle /src/vendor/bundle
 9
10RUN mkdir -p /src
11
12COPY . /src
13WORKDIR /src
14
15ENV RAILS_ENV production
16
17RUN bundle exec rake assets:precompile
18
19FROM ruby:3-alpine
20
21# ...
22
23COPY --from=assets /${APP_ROOT}/public /${APP_ROOT}/public
24
25# ...

我可以直接下載 Node.js 的鏡像不做任何事情,因為 Node.js 的主程式只需要 /usr/local/bin/node 這單一一個檔案就能夠運行,因此我們可以直接將他複製到我們的容器鏡像中,讓我們可以呼叫 node 命令。

接下來將 Ruby Gem 也複製到這個建置階段,這樣也就不需要額外的安裝 Ruby Gem 的處理,剩下的就是將環境變數設定為 RAILS_ENV=production 來以正式環境的模式進行預先編譯。

當整個程序完成後,我們就可以將 /src/public 目錄下的 packs 以及 assets 兩個目錄複製到最終的鏡像中,來讓我們的 Rails 專案可以使用。

Rails Credentials

然而,在靜態素材預先編譯時,可能會遇到一些狀況。像是我們有一些密鑰會保存在 Rails 6 加入的 Credentials(憑證)機制中,在編譯過程中會需要使用。

此時我們就會需要提供 Master Key(主鑰匙)到建置的環境裡面,因此可以透過 ARG 命令來允許傳入額外的設定。

1FROM ruby:3-alpine as assets
2
3ARG RAILS_MASTER_KEY
4
5# ...
6ENV RAILS_ENV production
7ENV RAILS_MASTER_KEY $RAILS_MASTER_KEY
8
9RUN bundle exec rake assets:precompile

因為 Rails 是基於環境變數偵測的,因此在設定 ARG 命令後我們還需要利用 ENV 命令轉換為環境變數使用。

透過多階段建置,我們還額外獲得了 RAILS_MASTER_KEY 這類敏感資訊只會在建置階段中被保存,而不會被正式部署的鏡像紀錄,也提高了安全性。然而在建置的快取中仍會因為我們的 ENV 命令而被暫存到,因此在使用上還是需要多加注意。

如果你遇到了需要 Redis 或者資料庫的狀況,我會推薦你重新審視為什麼編譯靜態素材會需要連線 Redis 和資料庫,在大多數狀況下都是不太合理的情況。這有可能是專案的連線方式有問題,或者對靜態素材的定義實際上不太「靜態」

提供靜態素材

雖然我們已經具備了 public/packs 以及 public/assets 這兩個目錄,然而在 Rails 中正式環境(Production Mode,生產力模式)並不會處理這類靜態素材,因為這類檔案更適合由 Nginx 這類伺服器處理,吞吐量(Throughput)也會是讓 Rails 來處理高上數倍。

在這樣的情況,我們更適合搭配持續交付的工具來處理,如果是測試環境或者小型專案,我們可以透過設定來告訴 Rails 直接處理,而不是透過第三方的靜態檔案伺服器。

 1FROM ruby:3-alpine as assets
 2
 3# ...
 4
 5FROM ruby:3-alpine
 6
 7# ...
 8
 9ENV RAILS_ENV=production
10ENV RAILS_SERVE_STATIC_FILES=yes
11
12# ...

自從 Ruby on Rails 5 開始,就陸陸續續加入許多 RAILS_ 類型的環境變數,讓我們可以很輕鬆的針對這類常見的環境差異來進行設定。

在使用 config/environments/production.rb 這個檔案的狀況下,可以利用 RAILS_SERVE_STATIC_FILES=yes 的設定讓 Rails 來處理靜態素材,這樣我們建置出來的容器鏡像就能處理 JavaScript 這類檔案。


如果想在第一時間收到更新,歡迎訂閱弦而時習之在這系列文章更新時收到通知,如果有希望了解的知識,可以利用Rails 部署實踐回饋表單告訴我。