---
title: "如何在幾分鐘內容器化 Rails 專案"
date: 2021-12-19T00:00:00+08:00
publishDate: 2021-12-19T14:57:30+08:00
lastmod: 2022-01-17T14:20:52+08:00
tags: ["Rails","容器","Docker","雲端","CNCF","DevOps"]
toc: true
permalink: "https://blog.aotoki.me/posts/2021/12/19/containerize-ruby-on-rails-in-a-few-minutes/"
language: "zh-tw"
---


Ruby on Rails 至今為止一直都是快速開發網站的首選框架之一，雖然我們可以利用 Rails 快速的製作網站，然而在部署上依舊還是要 DevOps 透過手動的方式部署伺服器、更新才能夠進行測試或者發布。

那麼，我們是否有更好的方式來解決這樣的問題呢？

<!--more-->

## Boxing Gem

為了解決這樣的問題，我製作了 [Boxing](https://github.com/elct9620/boxing) 這一個 Ruby Gem 來產生 Dockerfile 透過自動偵測 Ruby 專案的方式，我們可以省下撰寫 Dockerfile 的時間。

這是從 [Bundler Audit](https://github.com/rubysec/bundler-audit) 所獲得的靈感，這個 Gem 會自動的偵測 Gemfile 的配置選擇適合的 Linux 套件配置進去，因此不需要擔心相容性問題。

> 目前還是很初期的階段套件的關聯資訊仍不夠豐富，還需要大家協助回報來改善。

簡單來說，我們只需要在 Gemfile 加入以下設定：

```ruby
group :development do
	gem 'boxing'
end
```

執行 `bundle install` 安裝後，就可以利用 `bundle exec boxing generate` 指令產生 Dockerfile。

這個 Dockerfile 是我在 [Ruby on Rails 容器化最佳指南](https://blog.aotoki.me/posts/2021/10/09/the-best-practice-of-containerize-ruby-on-rails-part-1/)會介紹的技巧，是我在業界工作這五年多不斷累積的經驗所彙整出來的最佳實踐。

目前，在我任職的公司[五倍紅寶石軟體開發](https://5xruby.com)所有的新專案大多會搭配這套設置，並且整合對應的 CI/CD 配置讓每一次程式碼的整合都可以自動部署並且讓開發團隊可以直接測試。

## Openbox Gem

如果你已經經常使用 Docker 或者 Docker Compose 來部署專案，可能還會遇到幾個問題。

其中一個就是 Docker Compose 的 `depends` 並不太表伺服器已經準備好，即使在比較新的版本中已經有 Health Check 的配置，依舊還是有機會發生 Ruby on Rails 已經啟動後，依舊出現無法連上資料庫的情況。

網路上有不少做法是在容器中安裝 PostgreSQL / MySQL 的客戶端指令，並且在 Entrypoint 的腳本中事先呼叫確認連線，再繼續執行 Rails Server 的指令，類似像這樣。

```bash
while ! mysqladmin ping -h"$DB_HOST" --silent; do
    sleep 1
done

rails server
```

實際上，這樣的做法會增加 Docker Image 的大小，同時安裝了一些不必要的檔案，在 Production-ready 的前提下，我們應該盡可能的減少這類大小。這樣在部署的速度可以加速，也對 Kubernetes 這類容器調度工具在不同結點間遷移能夠更快的完成，以及減少網路的壓力。

基於這樣的理由，我使用 Ruby 製作了 [Openbox](https://github.com/elct9620/openbox) 來以 Ruby Gem 為基礎的連線檢查腳本。假設我們使用 `pg` 這個 Gem 當作我們專案的 Adapter 來連接資料庫，以 Openbox 作為 Entrypoint 的時候就會自動的參考 `DATABASE_URL` 環境變數連接資料庫，這樣就可以確保連線正常以及安裝最少的相依套件。

除此之外 Openbox 還具備了其他機制，像是我們可以將 `docker run --rm -it myapp rails console` 簡寫成 `docker run --rm -it myapp console` 這樣的語法，以及限制直接使用 Shell 進入容器，來確保一定程度的安全性。

## GitLab CI Template

在前面的工具都是關於專案以及容器話本身的設定，除了大量的減少「人工」之外，我們在打包跟部署依舊還是需要手動處理，此時就必須出動 CI/CD 工具來輔助。

然而，這些設定檔的撰寫依舊是一件費工的事情，即使 GitLab 有提供了 AutoDevOps 的選項，卻依舊需要花費許多時間打包，以及很難的以最佳化的 Dockerfile 設定來運作。

因此取而代之的方案就是使用自己客製化的 GitLab CI 設定檔，並且將報告（Report Artifacts）設定正確，讓原本 GitLab 支援解析的格式可以正確被設定。

因此 [elct9620/ruby-gitlab-ci](https://github.com/elct9620/ruby-gitlab-ci) 這個樣板專案就被製作出來，用於減少設定檔的撰寫。

現在，透過上述三個工具的整合，大多數情況下我們可以在幾分鐘內（安裝 Gem、使用指令自動生成）完成一個 Ruby 或者 Rails 專案的容器化。

## 快，還要更快{#fast-and-faster}

我常常對同事說，當好一個工程師需要的是夠懶惰。我們的人生中有太多事情需要去嘗試、體驗，為什麼要把時間浪費在這些事情上呢？

科技發展是要讓人們生活變得更加便利，那麼軟體工程師的工作不就是去將那些「浪費時間可重複的事情」減少嗎？

經過幾年的累積，這些處理大多已經變成我在每個專案上重複發生的情況，因此將他自動化，然後爭取更多的時間去做更多有意義的事情，同時也希望能夠幫助大家解決在類似問題上的困擾。

這個主題我想有影片可能會更好理解，如果希望有影片版本的話可以[匿名投票](https://doodle.com/poll/287xb52qz6xqg6gi?utm_source=poll&utm_medium=link)讓我統計有興趣的人，如果人數夠多的話我會再找時間製作影片版本。

