Ruby: 進階用法

深入了解 Ruby 部署

👋 歡迎來到 Stackhero 文檔!

Stackhero 提供一個即用型的 Ruby cloud 解決方案,帶來多項好處,包括:

  • 只需一個簡單的 git push,即可在幾秒鐘內部署您的應用程式。
  • 使用您自己的域名,並享受 HTTPS 證書的自動配置以增強安全性。
  • 享受自動備份一鍵更新,以及簡單、透明且可預測的定價,讓您安心無憂。
  • 憑藉專用的私人 VM,獲得最佳的性能和強大的安全性

節省時間簡化您的生活:只需 5 分鐘即可嘗試 Stackhero 的 Ruby cloud hosting 解決方案!

到目前為止,我們已經透過推送 main 分支來部署我們的 Ruby 應用程式,使用以下指令:

git push stackhero main

如果您希望部署其他分支,可以使用此指令。將 <BRANCH> 替換為您想部署的分支名稱:

git push stackhero <BRANCH>:main

例如,要部署名為 production 的分支,請執行:

git push stackhero production:main

在某些情況下,您可能希望部署標籤而不是分支。要這樣做,請執行以下指令。將 <TAG> 替換為您想部署的標籤:

git push stackhero '<TAG>^{}:main'

例如,要部署標籤 v1.0.0,請執行:

git push stackhero 'v1.0.0^{}:main'

^{} 語法用於引用標籤指向的提交。

除了分支或標籤,您還可以部署特定的提交。將以下指令中的 <COMMIT_HASH> 替換為所需提交的哈希:

git push -f stackhero <COMMIT_HASH>:main

例如,要部署哈希為 abcde 的提交,請執行:

git push -f stackhero abcde:main

如果您的生產部署未如預期運行,您可以透過部署較舊的提交來回滾。首先,使用以下指令查看您的提交歷史:

git log

此指令會顯示您儲存庫中每個提交的日期、提交哈希和描述。例如,您可能會看到如下輸出:

commit cccc8b3ebdccb9abc1926ef49ee589dae5c5fe06 (HEAD -> main, stackhero/main)
Author: Developer
Date:   Fri Apr 28 09:36:18 +0000

    Break the code

commit bbbb622301772072c3d82f3cc0d91e29e6e84901
Author: Developer
Date:   Wed Apr 26 12:49:28 +0000

    Update the code

commit aaaa1d8b06535b413e0df8298ccf52339dfef3ff
Author: Developer
Date:   Wed Apr 26 12:44:50 +0000

    Improve the code

如果帶有訊息 "Break the code" 的提交(哈希 cccc...)正在生產中運行,而您決定回滾到先前的提交 "Update the code"(哈希 bbbb...),請執行:

git push -f stackhero bbbb622301772072c3d82f3cc0d91e29e6e84901:main

為了避免部署有缺陷的代碼並提高生產的穩定性,強烈建議設置一個 "staging" 環境。

"staging" 環境位於 "development" 環境和 "production" 環境之間,提供與生產環境幾乎相同的複製品。這允許您在部署到生產環境之前測試代碼並確保其質量。

使用 staging 環境,您可以對代碼的功能和性能更有信心,確保更可靠和穩健的生產部署。

這種類型的環境將在文檔中稍後討論。

staging 環境是與您的 developmentproduction 環境一起使用的最佳實踐。它複製您的生產環境,以便您可以在更新和更改上線之前進行測試。

staging 環境必須緊密反映生產環境。

但是,請確保 staging 環境使用生產資料庫的克隆,而不是實際的生產資料庫。

如果您的 Ruby 服務與資料庫或其他服務相關聯,請在新的 <Project> - Staging 堆疊中重新創建它們。

要在 Stackhero 上設置 staging 環境,請按照以下步驟操作:

  1. 在 Stackhero 儀表板上,將現有堆疊從 <Project> 重命名為 <Project> - Production。例如,如果您的專案名為 Chat Bot,請將堆疊重命名為 Chat Bot - Production
  2. 創建一個名為 <Project> - Staging 的新堆疊。使用前面的例子,這將是 Chat Bot - Staging
  3. 在 staging 堆疊中啟動一個 Ruby 服務。
  4. 獲取 git remote 指令的值,並按照 Deploying to staging environment 部分中的說明進行操作。

按照這些步驟,您將獲得一個正確配置的 staging 環境,以便在更新到達生產環境之前進行測試和驗證。

強烈建議管理獨立的環境,如 stagingproduction。如 Setting up a staging environment 中所述,您可以使用不同的 Git remotes 部署到每個環境。

首先,重命名當前的遠端儲存庫。例如,使用以下指令將遠端 "stackhero" 重命名為 "stackhero-production":

git remote rename stackhero stackhero-production

接下來,為 staging 環境創建一個新的 Ruby 服務。使用提供的 "git remote add" 指令並按如下方式修改(將 <XXXXXX> 替換為您的服務域名):

  • 原始指令:

    git remote add stackhero ssh://stackhero@<XXXXXX>.stackhero-network.com:222/project.git
    
  • 修改後的指令:

    git remote add stackhero-staging ssh://stackhero@<XXXXXX>.stackhero-network.com:222/project.git
    

您現在可以使用以下指令部署到 staging:

git push stackhero-staging main

或使用以下指令部署到生產:

git push stackhero-production main

為了進一步簡化部署過程,考慮使用 改進版 Makefile

使用這個改進的 Makefile,可以輕鬆地使用 make deploy-productionmake deploy-staging 進行生產或 staging 部署。

以下是一個改進的 Makefile,支持多個常見任務的規則:

  • make dev(或簡單地 make):在開發模式下啟動應用程式。
  • make deploy:將應用程式部署到名為 stackhero 的遠端(理想情況下當您只有一個 Stackhero 實例時)。
  • make deploy-production:將應用程式部署到名為 stackhero-production 的遠端。
  • make deploy-staging:將應用程式部署到名為 stackhero-staging 的遠端。

Makefile 設計用於處理代碼已經部署的情況,避免 "Everything up-to-date" 錯誤。

將以下內容複製並粘貼到您的新 Makefile 中:

# 默認執行的規則,當不帶參數調用 "make" 時執行
.DEFAULT_GOAL := dev


# Stackhero for Ruby 將在您的實例上執行 "run" 規則。
# 這是在生產和 staging 平台上運行的指令。
run:
  rake assets:precompile
  rake db:migrate RAILS_ENV=production
  RAILS_ENV=production bundle exec puma -C config/puma.rb


# 在開發環境中運行的指令
dev:
  RAILS_ENV=development rails server -b 0.0.0.0


# "deploy" 規則部署到 "stackhero" 實例。
# 適合當您只有一個實例時。
deploy:
  @$(MAKE) -s deploy-script DEPLOY_REMOTE=stackhero DEPLOY_BRANCH=main


# "deploy-*" 規則部署到 "stackhero-*" 實例。
# 例如,運行 "make deploy-production" 部署到 "stackhero-production",
# 或 "make deploy-staging" 部署到 "stackhero-staging"。
deploy-%:
  @$(MAKE) -s deploy-script DEPLOY_REMOTE=stackhero-$* DEPLOY_BRANCH=main


# 內部部署規則。請勿修改。
deploy-script:
  @echo "Deploying branch \"${DEPLOY_BRANCH}\" to \"${DEPLOY_REMOTE}\"..."
  @echo

  @if [ -n "$$(git status --porcelain)" ]; then \
    echo "無法部署,因為有未提交的更改:"; \
    echo "\e[0m"; \
    git status -s; \
    echo ""; \
    echo "\e[0;31m"; \
    echo "您可以使用此指令提交更改:"; \
    echo "git add -A . && git commit -m \"Your message\""; \
    echo "\e[0m"; \
    exit 1; \
  fi

  @git push --dry-run ${DEPLOY_REMOTE} ${DEPLOY_BRANCH} 2>&1 | grep -q -F "Everything up-to-date"; \
  EXIT_CODE=$$?; \
  if [ $$EXIT_CODE -eq 0 ]; then \
    echo -n "沒有新內容可部署... 強制部署(這將創建一個新提交)? (y/N) "; \
    read answer && \
    case $$answer in \
      y|Y|yes|YES) \
      git commit --allow-empty -m "Force update for deploy purpose to \"${DEPLOY_REMOTE}\"" ; \
      ;; \
      *) \
      echo "沒有可部署的內容!"; \
      exit 1; \
      ;; \
    esac \
  fi

  git push ${DEPLOY_REMOTE} ${DEPLOY_BRANCH}

在某些時候,您需要管理如資料庫和第三方服務的令牌或密碼等機密。安全地存儲這些機密至關重要。避免將機密直接嵌入到您的儲存庫或代碼中,因為這會帶來嚴重的安全風險。

環境變數提供兩個重要的好處:

  1. 您的機密不會存儲在您的 Git 儲存庫中,從而降低了如果有人獲得您的源代碼的風險。
  2. 您可以為不同的環境使用不同的憑證。例如,在生產中連接到您的生產資料庫,而在開發中使用開發資料庫。

對於開發,請在專案的根目錄中創建 .env 文件。此文件將被 Git 排除,因此永遠不會被提交。使用 dotenv gem 自動加載 .env 文件。

首先,將 dotenv-rails gem 添加到您的 Gemfile:

# Gemfile
gem 'dotenv-rails', groups: [:development, :test]

然後安裝 gem:

bundle install

接下來,在專案的根目錄中創建 .env 文件並添加您的變數:

RAILS_ENV="development"
DATABASE_PASSWORD="secretPassword"
THIRD_API_PRIVATE_KEY="secretKey"
# ...

最後,確保 .env 文件被 Git 忽略:

echo '.env*' >> .gitignore

對於 staging 和生產,.env 文件不安全也不實用,因為它不能存儲在 Git 儲存庫中。相反,Stackhero 提供了一個安全的解決方案,直接在您的 Ruby 服務配置中管理環境變數。

您可以通過選擇您的 Ruby 服務並點擊 "Configure" 按鈕,在 Stackhero 儀表板中設置這些變數。

在 Ruby 中,您可以輕鬆地使用 ENV 訪問環境變數。例如,要檢索 DATABASE_PASSWORD,請使用:

ENV['DATABASE_PASSWORD'] # => 'secretPassword'

以下是如何使用環境變數連接到 RabbitMQ 服務器的示例:

require 'bunny'

class RabbitMQClient
  def initialize
    @connection = Bunny.new(hostname: ENV['RABBITMQ_HOST'],
                            username: ENV['RABBITMQ_USERNAME'],
                            password: ENV['RABBITMQ_PASSWORD'])
    @connection.start
  end

  def publish(queue_name, message)
    channel = @connection.create_channel
    queue = channel.queue(queue_name)
    channel.default_exchange.publish(message, routing_key: queue.name)
  end

  def close
    @connection.close
  end
end

在開發平台上,您的 .env 文件可能包括:

RABBITMQ_HOST='127.0.0.1'
RABBITMQ_USERNAME='developmentUser'
RABBITMQ_PASSWORD='developmentPassword'

對於生產和 staging,請在 Stackhero 儀表板的 Ruby 服務配置中定義您的環境變數,如下所示:

RABBITMQ_HOST='<XXXXXX>.stackhero-network.com'
RABBITMQ_USERNAME='production'
RABBITMQ_PASSWORD='secretProductionPassword'

Ruby 應用程式通常在端口 80(HTTP)和 443(HTTPS)上使用 HTTP 協議。如果您的應用程式需要額外的端口或不同的協議(TCP 或 UDP),請通過 Stackhero 儀表板在您的 Ruby 服務中配置 "Ports Redirections" 設置。

您需要指定入口端口(公開開放)、目標端口(在您的 Ruby 服務中開放)和協議(TCP 或 UDP)。

對於存儲用戶照片或文件等,強烈建議使用物件存儲解決方案。物件存儲允許您在多個服務和實例之間共享文件,並將存儲層與代碼分離。這被認為是最佳實踐。

我們推薦 MinIO 作為一個簡單、快速且強大的解決方案,兼容 Amazon S3 協議。

如果您選擇本地文件存儲,您可以使用 Ruby 實例提供的持久存儲。此本地存儲位於目錄 /persistent/storage/ 下。

然而,本地文件存儲通常不被推薦,因為它可能不是長期擴展性和可靠性的最佳實踐。

警告:切勿將數據存儲在 /persistent/storage/ 文件夾之外!

將數據存儲在持久存儲文件夾之外的任何位置可能會導致數據丟失,當您的實例重新啟動、更新或推送新代碼時。

如果您使用 macOS,您可能會發現每次推送代碼時輸入 SSH 私鑰密碼很不方便。雖然安全性至關重要,但您可以通過將密碼安全地存儲在 Apple 的 Keychain 中來提高便利性。

可能會想要從 SSH 私鑰中刪除密碼,但這不建議這樣做。

相反,使用以下指令將您的密鑰密碼存儲在 Keychain 中,對於名為 id_ed25519 的密鑰:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

執行此指令後,您不應再被提示輸入密鑰密碼。如果您使用 RSA 密鑰,請將 id_ed25519 替換為 id_rsa,如下所示:

ssh-add --apple-use-keychain ~/.ssh/id_rsa