ぺのめも

Web開発を勉強中。学んだことや思ったことなど

個人開発のプロジェクトにtagprを導入した

はじめに

個人でRuby on Railsのサービス開発を始めており、CIの構築の中でtagprを導入しました。導入にあたって設定したことを記録しておきます。
tagprのおかげでCIが快適になりいい感じです。ありがとうございます!

tagprリポジトリgithub.com

tagprの作者の方のリリースブログ: songmu.jp

やりたいこと

今回のプロジェクトでは下記のようなブランチ運用にしたく、やり方を検討していたところ tagpr を見つけました。

  • mainブランチと、各featureブランチのみで運用
    • featureブランチはmainへのマージ後に削除
  • mainブランチにfeatureブランチがマージされたら、ステージング環境へデプロイ
  • mainブランチにタグを打ったら、本番環境へデプロイ
    • どのコミットまでをリリースに含めるかを決めたら、タグを打つまでは追加でマージしないようにする

自分がこのような運用にしたかった背景・CI設定の全体については、こちらの記事に記載しています。

peno022.hatenablog.com

導入結果

PRの確認を経てmainにfeatureがマージされると、

  • リリースブランチの作成
  • CHANGELOGの更新
  • PRの作成

を自動で実行してくれます。 PRをマージすると、mainに該当バージョンのタグが打たれ、デプロイできる状態になります。
OSSのリリース作業の属人性を下げることを主な動機として開発されたアクションとのことですが、個人開発であっても快適です。

ワークフローファイル(完成版)

リポジトリに記載のサンプルコードを参考に、tagpr 実行のワークフローファイルを作成しました。
最終的なコードは下記になります。

# .github/workflows/tagpr.yml

name: tagpr
on:
  push:
    branches:
      - main

jobs:
  tagpr:
    runs-on: ubuntu-latest
    outputs:
      tagpr-tag: ${{ steps.run-tagpr.outputs.tag }}
    env:
      GITHUB_TOKEN: ${{ secrets.GH_PAT }}
    steps:
      - name: Check out source code
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.GH_PAT }}

      - id: run-tagpr
        name: Run tagpr
        uses: Songmu/tagpr@v1

  release:
    needs: tagpr
    if: needs.tagpr.outputs.tagpr-tag != ''
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.GH_PAT }}
    steps:
      - name: Check out source code
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.GH_PAT }}
      - name: Trigger release action
        uses: ./.github/actions/release

調べた箇所

リポジトリの許可Actionに登録する

リポジトリSettings > Actions > Generalの Actions permissions の設定を修正しました。
自分で追加したactionsか、GitHub認証済みのactionsのみ利用できる設定にしていたため、許可するactionsにtagprを追加しています。

後続のワークフローをトリガーできない問題

tagprのサンプルコードだとsecrets.GITHUB_TOKENを使っているのですが、このトークンを利用した場合、後続のワークフローのトリガーができません。(pushしたらCIのワークフローを起動する、等)

自分の場合は、リリース用PRについても「CIが通ったことを確認してマージ」としたかったので、GITHUB_TOKENを使うのをやめて、personal access tokenを利用することにしました(secrets.GH_PAT)。
PATを使うと、後続アクションをトリガーできるようになります。(ただし、PATには有効期限を適切に設定・更新する必要があったり、流出に注意が必要であったりと、GITHUB_TOKENと違って自分で管理する手間がかかるという面もあります)

personal access token を発行する

personal access tokenは、
Settings > Developper settings > Personal access tokens > Fine-grained tokens
から作成することができます。

Fine-grained tokens について少し調べると、2022年10月に発表された比較的新しい機能でした。
従来のトークンは発行者がアクセスできる全リポジトリに対して使えていましたが、Fine-grained tokens は、対象とする組織・リポジトリを選択することができるという点で、より「きめ細かい」トークンであるということのようです。

github.blog

docs.github.com

トークンの権限設定をする

トークンを作成したら、Permissions の項目を設定します。
tagprの実行には、下記の権限設定が必要でした。

  • Actions
    • Read and write
  • Contents
    • Read and write
  • Metadata
    • Read-only
      • mandatoryと表示され、Actions に Read and write 権限を付与すると自動的にこちらも設定されます
  • Pull requests
    • Read and write

最初にActionsの Read and write 権限だけを設定して実行したら、下記ログのように権限エラーが出たので、必要な権限を追加していった形です。

POST https://api.github.com/repos/peno022/kpi-tree-generator/releases/generate-notes  403 Resource not accessible by personal access token [] 


どのAPIにどの権限が必要かは、こちらのドキュメントに一覧でまとまっているため、ここを見ながら権限設定をしました。

docs.github.com

トークンをリポジトリに登録してワークフローで利用する

リポジトリSettings > Security > Secrets and variables > Actionsから、作成したトークンを登録します。
項目名に任意の名前(今回はGH_PAT)をつけ、Valueには生成したトークンの文字列をペーストして、登録完了です。


リリースのジョブをactionsへの切り出す

「タグが打たれたら本番環境にデプロイする」という部分について、デプロイの実行を./.github/actions/releaseに切り出しています。

      - name: Trigger release action
        uses: ./.github/actions/release

製作者の方のリリースブログに記載があった、 tagprのワークフローが途中で意図せず失敗してしまった場合に、リリースフローをリカバリするために手動でタグを打つワークフローを別途用意しやすくなる という利点のため、このようにしました。

actionsの構文について補足

workflow間に共通するようなジョブを切り出す際には、workflowではなくactionsとして作る必要があり、構文のルールがworkflowとは少し違っています。 下記のような条件があるので、注意が必要でした。

  • .github/actions/[任意のアクション名]/action.ymlというパス・ファイル名にする
  • workflowの場合は必須である、トリガーイベント、ランナーの指定は不要
  • using: "composite"の記述が必須
  • shell の指定が必須

参考: zenn.dev zenn.dev

公式ドキュメント:

docs.github.com

docs.github.com

GitHub アプリのレート制限エラー

403 You have exceeded a secondary rate limit. Please wait a few minutes before you try again. 

頻繁にリクエストを送りすぎると、一時的な制限がかかって出るエラーのようです。動作確認のために繰り返し実行していたタイミングで、一度だけ出ました。
メッセージに記載があるように、数分待ってからジョブの Rerun を行ったら解消しました。

docs.github.com

actions/setup-nodeでNode.jsがキャッシュされていない

TL;DR

自分が利用しているNode.jsのバージョンが、actions/node-versionsでまだ対応されていたかったことによるようです。
18系だとキャッシュされました。

状況

個人開発しているプロジェクトのCIを設定するにあたり、Node.jsのインストールが必要だったので、setup-nodeのアクションを利用しました。

# 自分が作成したワークフローファイルより抜粋

  name: Install Node.js
  uses: actions/setup-node@v3
    with:
      node-version: '19.4'

github.com

ただ、setup-nodeのREADMEにはOptionally downloading and caching distribution of the requested Node.js version, and adding it to the PATHという記載があるものの、 実行のたびにNode.jsのキャッシュが見つからずにダウンロードしていたので、なぜキャッシュが使えていないのだろう?を調査しました。

// 実行ログ

Run actions/setup-node@v3
/usr/bin/docker exec  f7656153548b0c0c871c87f547a809d02cea3ae16dbd0ba4734738022cf761da sh -c "cat /etc/*release | grep ^ID"
##[debug]ID=debian
##[debug]Running JavaScript Action with default external tool: node16
##[debug]isExplicit: 
##[debug]explicit? false
##[debug]isExplicit: 14.21.2
##[debug]explicit? true
##[debug]isExplicit: 16.19.0
##[debug]explicit? true
##[debug]isExplicit: 18.13.0
##[debug]explicit? true
##[debug]evaluating 3 versions
##[debug]match not found
Attempting to download 19.4...
##[debug]No manifest cached
##[debug]Getting manifest from actions/node-versions@main
##[debug]set auth
##[debug]check 18.13.0 satisfies 19.4
##[debug]check 18.12.1 satisfies 19.4
##[debug]check 18.12.0 satisfies 19.4

// 中略

##[debug]check 8.1.0 satisfies 19.4
##[debug]check 8.0.0 satisfies 19.4
##[debug]check 6.17.1 satisfies 19.4
Not found in manifest. Falling back to download directly from Node
##[debug]evaluating 555 versions
##[debug]matched: v19.4.0
Acquiring 19.4.0 - x64 from https://nodejs.org/dist/v19.4.0/node-v19.4.0-linux-x64.tar.gz
##[debug]Downloading https://nodejs.org/dist/v19.4.0/node-v19.4.0-linux-x64.tar.gz

// 以下略

結論

自分が利用しているNode.jsのバージョンが19.4だったことに起因していました。
取得元となっているactions/node-versions側で、まだ対応されていないバージョンのようです。

調査内容

ログに

##[debug]Getting manifest from actions/node-versions@main

が出ていたのでmanifestの取得箇所を確認してみると、actions/node-versionsから取得していそうです。(該当箇所

  private getManifest(): Promise<tc.IToolRelease[]> {
    core.debug('Getting manifest from actions/node-versions@main');
    return tc.getManifestFromRepo(
      'actions',
      'node-versions',
      this.nodeInfo.auth,
      'main'
    );
  }

そこでactions/node-versionsを見に行くと、18.13.0が最新で、バージョン19にはまだ対応していませんでした。

github.com

actions/node-versions リリース一覧画面
actions/node-versions リリース一覧画面

自分が利用している19.4はactions/node-versionsから取得できないことにより、都度https://nodejs.org/dist/v19.4.0/node-v19.4.0-linux-x64.tar.gzからダウンロードする形になっていたようでした。

バージョン18を指定して実行してみると、キャッシュが取得され、実行時間も短くなりました。

// 実行ログ

Run actions/setup-node@v3
/usr/bin/docker exec  c32933f9e63a5f41642ba7d0ce44bc6929286d7f1bbddf57c64c26b562e4ef3f sh -c "cat /etc/*release | grep ^ID"
##[debug]ID=debian
##[debug]Running JavaScript Action with default external tool: node16
##[debug]isExplicit: 
##[debug]explicit? false
##[debug]isExplicit: 14.21.2
##[debug]explicit? true
##[debug]isExplicit: 16.19.0
##[debug]explicit? true
##[debug]isExplicit: 18.13.0
##[debug]explicit? true
##[debug]evaluating 3 versions
##[debug]matched: 18.13.0
##[debug]checking cache: /__t/node/18.13.0/x64
##[debug]Found tool in cache node 18.13.0 x64
Found in cache @ /__t/node/18.13.0/x64
::group::Environment details
Environment details
##[add-matcher]/__w/_actions/actions/setup-node/v3/.github/tsc.json
##[debug]Added matchers: 'tsc'. Problem matchers scan action output for known warning or error strings and report these inline.
##[add-matcher]/__w/_actions/actions/setup-node/v3/.github/eslint-stylish.json
##[debug]Added matchers: 'eslint-stylish'. Problem matchers scan action output for known warning or error strings and report these inline.
##[add-matcher]/__w/_actions/actions/setup-node/v3/.github/eslint-compact.json
##[debug]Added matchers: 'eslint-compact'. Problem matchers scan action output for known warning or error strings and report these inline.
##[debug]Node Action run completed with exit code 0
##[debug]Set output node-version = v18.13.0
##[debug]Finishing: Install Node.js

はてなブログのMarkdown編集モードでMermaidの図を書く設定

Mermaid

Mermaidとは、テキストをレンダリングしてグラフやチャートを描けるJavaScriptのライブラリです。
はてなブログで記事を書く際にも利用したかったのですが、デフォルトでは利用できず追加の設定が必要だったので、設定方法をまとめておきます。

mermaid.js.org

設定方法

1)Mermaidのスクリプトをロード・埋め込む

  1. はてなブログの「設定」画面を開き、詳細設定をクリック
  2. <head>要素にメタデータを追加」の項目に、下記のスクリプトを追記
<script type="module">
import mermaid from 'https://unpkg.com/mermaid@9/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>

2)CSS設定を追加

上記により```mermaidの記法が使えるようになりますが、このままだとはてなブログCSSがきいてコードブロックの背景が黒くなってしまい、mermaidの図がかなり見づらくなります。
それを避けるため、pre.code.mermaidのときはスタイルが当たらないように修正します。

手順:

  1. はてなブログの「デザイン」画面を開き、「カスタマイズ」タブをクリック
  2. 「デザインCSS」をクリックしてテキストボックスを開き、下記のCSSを追記
/*mermaid用*/
.entry-content pre.code.mermaid {
  background: none;
  border: none;
  padding: 0;
}
.entry-content pre.code.mermaid[data-lang="mermaid"]:before {
    content: none;
}

3)Mermaidを書く

ここまでの設定を終えると、Markdownモードの記事編集画面で、```mermaidの記法が使えるようになります。

 ```mermaid
   sequenceDiagram
       Alice->>John: Hello John, how are you?
       John-->>Alice: Great!
       Alice-)John: See you later!
 ```
sequenceDiagram
    Alice->>John: Hello John, how are you?
    John-->>Alice: Great!
    Alice-)John: See you later!

色んなチャートが簡単に書けて便利

フローチャート、シーケンス図、クラス図、ER図など、色々な図を簡単に書くことができます。
元々はGitのブランチ運用の図をサクッと書けないかな〜と思って探していたのですが、それもバッチリありました。

 ```mermaid
 ---
 title: Example Git diagram
 ---
 gitGraph
    commit
    commit
    branch develop
    checkout develop
    commit
    commit
    checkout main
    merge develop
    commit
    commit
 ```
---
title: Example Git diagram
---
gitGraph
   commit
   commit
   branch develop
   checkout develop
   commit
   commit
   checkout main
   merge develop
   commit
   commit

参考記事

個人開発用のGitHubリポジトリで設定した項目たち

はじめに

前提条件

今回は以下のようなリポジトリに対して、設定を行いました。

記載を割愛した項目は、デフォルトのまま変更しなかったもの、かつ変更しないことについて特に悩まなかったものです。

設定箇所

リポジトリの「Settings」画面内の項目を見ていきます。

Repository Settings
Repository Settings
GitHubリポジトリの設定画面
設定画面

General

Features

デフォルトのまま。

  • Sponsorships
    • スポンサーボタンを表示する設定。ひとまず不要そう。
  • Discussions
    • issueとは別で議論するためのスペースを設定できる。issueで十分そうなので設定せず。

Featuresの設定項目
Featuresの設定項目

Pull Requests

マージ方法の設定

squash and mergeのみを許可するように変更しました。
自分が現在rebase方式しか経験がなく、squash and mergeの方式も試したかったためです。

Pull Requestsのマージ方法の設定
Pull Requestsのマージ方法の設定

この設定をすると、PRをマージする時に「Squash and merge」しか選べないようになります。

Squash and merge しか選択できない
Squash and merge しか選択できない

ベースブランチ更新時にPRブランチの更新をサジェスト

Always suggest updating pull request branches を有効化しました。
PRを作成してからベースブランチに新たに変更が入った場合、画面上に「ブランチを更新する」ボタンが表示されるようになります。
基本的にベースブランチの最新を取り込んでCIのテストをパスしてからマージする形にしたいので、簡単にベースブランチの更新を取り込めるように設定しました。

PRマージ後にブランチを自動で削除

Automatically delete head branches を有効化しました。
マージ後に画面上のボタンを押してブランチを削除する必要がなくなります。大体の場合有効にしてよさそう。

Access

Collaborators

Interaction limits

議論が白熱している時などに、一時的に外部ユーザーとリポジトリとのやりとりを制限できる機能。
今回対象とした個人開発用リポジトリで議論が白熱することは当面なさそうなので、何も変更せず。

Code review limits

Limit to users explicitly granted read or higher access を有効化しました。
見知らぬユーザーがPRをApproveできないようにしたく、設定しておきました。
コメントは誰でもすることができる状態のままになります。

Code and automation

Branches

このリポジトリでは、GitHub flowをベースとしたブランチ運用を想定しています。

---
title: Git branch 運用イメージ
---
gitGraph
   commit
   branch feature1
   branch feature2
   checkout feature1
   commit
   commit
   checkout main
   merge feature1
   checkout feature2
   commit
   checkout main
   merge feature2
   branch feature3
   checkout feature3
   commit
   checkout main
   merge feature3
   commit tag: "v1.0.0"

上図のような形式で、

  • mainブランチと、各featureブランチのみで運用
    • featureブランチはmainへのマージ後に削除
  • mainブランチにタグを打ったら、本番環境へデプロイ

という運用ができるように、mainブランチに対して以下の保護設定をしています。

Branch protection rule

Require a pull request before merging を有効化しました。
mainへのマージは、基本的にPR上でレビューしてからにしたいためです。
ただ今回は自分ひとりで開発・確認・マージするシーンが多くなるはずので、approveは必須にしませんでした。

Require status checks to pass before merging を有効化しました。
指定したステータスチェックが通らないとマージできないようにすることができます。

Actions - General

Allow peno022(自分), and select non-peno022, actions and reusable workflows を選択しました。
品質の分からないworkflowを使ってしまうのを防ぐため、自分で作ったものか公式で認証済みのworkflowのみを許可したかったからです。

Security

Code security and analysis

Dependabot

Dependabot alerts と Dependabot security updates を有効化しました。
脆弱性の問題がなければ必ずしも常に最新バージョンにする必要はなさそうに思い、Dependabot version updatesは無効化のままとしています。

Dependabotは、依存しているライブラリのセキュリティ脆弱性を通知してくれ、最新バージョンにアップグレードするためのPull Requestの自動作成まで行ってくれるものです。

Dependabot
Dependabot

Code scanning

CodeQL analysis をAdvancedで有効化しました。

Defaultを選択するとGitHub側で自動的に CodeQL analysis が実行されます。
一方で、Advancedにすると、自分でワークフローを組む必要があります。
ただ、GitHubがデフォルト設定のymlファイルを作成してくれるので、ワークフローの設定自体はすぐにできました。
今回はワークフローの実行タイミングを自分でコントロールできたほうが都合がよいと考え、Advanced設定を選択しています。

CodeQL analysis
CodeQL analysis

Secret scanning

Receive alerts on GitHub for detected secrets, keys, or other tokens.を有効化しました。
publicリポジトリに秘密情報がアップロードされてしまった場合に、GitHubから通知が来るようになります。

Secrets and variables

Actions secretsに下記を設定しました。両方ともvalueにはtrueを設定します。
この設定により、デバッグログが有効になります。

  • ACTIONS_RUNNER_DEBUG
  • ACTIONS_STEP_DEBUG

docs.github.com

Integrations

特に変更せず、デフォルトのまま。
Email notificationsはデフォルトでActiveになっているため、メール通知は受け取れる状態になっています。

Dependabotのアラートが上がったけど自動PRが作成されない

状況

タイトルのとおりです。
自分は、Ruby on Railsで個人開発しているサービスのリポジトリにDependabotの以下の設定を入れています。
下記の機能を有効にしている状態です。

  • 依存ライブラリの脆弱性アラートを受信
  • アラート解決のために、Dependabotが自動的でPRを作成する

Repository Settings -&gt; Code security and analysis -&gt; Dependabot
Repository Settings -> Code security and analysis -> Dependabot

そのため、Dependabotのセキュリティアラートが出ると自動でPRが作成されるはずと思ったのですが、アラートが上がってきたのにPRが作成されていなかったので、あれ?と思いました。

結論

簡単なことだったんですが、まだ脆弱性が修正されたバージョンがリリースされていなかったことが理由でした。
Dependabotは、修正バージョンがリリース済みの場合に、そのバージョンに上げるようなPRを自動で作成してくれます。

アラートの内容。railsのバージョン7.0.4以下が対象になっています。

Dependabotのアラート
Dependabotのアラート

railsの最新バージョンはまだ7.0.4なので、バージョンを上げることによる自動修正はできない状態。

修正バージョンはまだリリースされていない
修正バージョンはまだリリースされていない

Dockerとは?開発環境導入までに必要だった最低限の基本知識

はじめに

個人でRuby on RailsWebサービスを開発しているのですが、Docker+VSCode Remote Containerでの開発環境を初めて作るにあたって、コンテナやDockerについて何も分からないところから始めたので、その時に調べた基本知識をまとめておきます。

コンテナとは、Dockerとは

コンテナとは

1つの共有されたホストOS上で動く、独立したアプリケーション実行環境のこと。 仮想マシンと比較されることがあるが、下記のように比較できる。

  • 仮想マシン
    • ハードウェアとOSの間にハイパーバイザという管理層をはさみ、ハイパーバイザが提供する仮想的なハードウェア上で複数のOSを同時に動かす
  • コンテナ
    • ホストOSのカーネル上で、そのOSカーネルの提供する環境隔離機能を使って、独立した実行環境を作る技術
    • コンテナ自体にはOSは含まれない

Docker

コンテナにまつわるワークフロー全体を管理できるアプリケーション。
Dockerで管理するコンテナ=「Dockerコンテナ」。

コンテナのワークフロー(Build, Ship, Run)

「Build, Ship, Run」は、コンテナのワークフローを示す、Docker社提唱のスローガン的な言葉。
ワークフローには、「Dockerイメージ」「Dockerレジストリ」「Dockerコンテナ」の3つの概念が登場します。

  • Build
    • コンテナ作成に必要なファイル群などの材料から、Dockerイメージを作成
  • Ship
  • Run
    • Dockerイメージから、Dockerコンテナを実行
Dockerイメージ
  • Dockerコンテナとして実行するためのアプリケーションやそれが依存するファイル群、コンテナ環境自体の設定などを定めたもの。
  • コンテキストとDockerfileをビルドすることで、コンテナイメージを作成する
    • コンテキストとは、アプリケーションの実行に必要なファイル群のこと。
    • Dockerfileとは、Dockerイメージの作成手順を記載したファイルのこと。
Dokerレジストリ
  • Dockerイメージが格納されている場所。
  • 代表的なのはDocker Hub Container Image LibraryGitHubのような感じと理解。
  • Dockerレジストリで配布されている基本のDockerイメージをベースにして、必要なものを追加する形で自分のDockerイメージを作る形になる。
Dockerコンテナ
  • 実際にアプリケーションを実行できる環境。
  • Dockerイメージを実行することで、Dockerコンテナを立ち上げる。

コンテナのレイヤー構造

コンテナは、「変更差分の集まり」になっている。各変更差分を「レイヤ」と呼び、レイヤを重ね合わせる形で構成される。

Dockerfileの書き方

DockerfileにはDockerイメージの作成手順を記載する。
手順1つ1つがレイヤとして重ねられていくイメージ。
下記のDockerファイルであれば、3つの手順ごとにレイヤが重ねられていく。

  • FROM mcr.microsoft.com/devcontainers/ruby:0-3.1
    • rubyのコンテナイメージを取得する(この中も実際はレイヤの重ね合わせになっている)
  • RUN gem install rails webdrivers
    • railsをインストールする
  • ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"

Dockerfile(VSCode devcontainerでRuby on Railsの開発用に作成したもの):

FROM mcr.microsoft.com/devcontainers/ruby:0-3.1

# Install Rails
RUN gem install rails webdrivers

# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
# The value is a comma-separated list of allowed domains 
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"

よく使いそうなDockerコマンド

  • docker build -t [イメージ名] [Dockerfileが置いてあるパス]
    • Dockerイメージを作成する
    • -tオプションを付けると名前を指定できる
  • docker run -it [コンテナ名] /bin/bash
    • Dockerコンテナを起動する
    • -it以降を付けることで、コンテナ内のシェルをインタラクティブに操作できる
  • docker image ls
    • Dockerイメージの一覧を表示
  • docker container ls
    • Dockerコンテナの一覧を表示
  • docker ps
    • 起動中のコンテナのステータスを表示
  • docker container prune
    • 停止中のコンテナをまとめて削除できる
  • docker image prune
    • タグがないイメージをまとめて削除できる
    • 例えばDockerfileを更新して再度ビルドすると、更新前の古いイメージIDに紐づくコンテナはタグが削除されてになる

Dockerfile A をビルドしてイメージを作成
Dockerfile A をビルドしてイメージを作成

Dockerfile Aを更新した A&#x27; をビルドしてイメージを作成
Dockerfile Aを更新した A' をビルドしてイメージを作成すると、古いほうはnoneになる

参考資料

書籍

イラストでわかる DockerとKubernetes技術評論社

かなり分かりやすくてよかったです。初学者向け。1-2時間でサクッと読めます。
分かりやすさ重視なので、ある程度知っている人にとっては内容が薄そう。

気に入ったところ

  • イラストでの図解が豊富&フルカラーなのでとっつきやすい
  • サンプルでこまめに挙動を確認し、実行したコマンドと表示結果にきちんと補足がある
  • サクッと読める

記事

基本事項について
開発環境構築について

最初にいきなりこれらの記事を読んだのですが、分かるような分からないような?だったので、上述の書籍を参照してコンテナの基本概念を理解してから、記事に戻った感じです。 qiita.com

qiita.com

VSCodeのRemote ContainerでRubyの開発環境を構築する

はじめに

Ruby on RailsWebサービスを個人開発中です。
Docker+VSCode Remote Container開発環境を作るにあたり、初めてで色々調べながら進めたので、記録しておきます。
やってみて分かったのですが、開発環境はDockerコンテナを使いつつ、コーディングはローカルでやるのと全く変わらない感じでVSCode上でできるので、かなり快適です。

そもそもDockerとは?

Docker自体に初めて触れる方の場合は、こちらの記事をさらっと読んでから戻ってくると、この後の内容を読み進めやすいと思います。

peno022.hatenablog.com

手順

VSCodeでコンテナを起動・接続する

プロジェクトディレクトリ自体は、既に作成済みの状態から始めます。
VSCode公式のドキュメントの手順に沿って進めていきました。
code.visualstudio.com

1)Docker Desktopのインストール

  • Windows: Docker Desktop 2.0+ on Windows 10 Pro/Enterprise. Windows 10 Home (2004+) requires Docker Desktop 2.3+ and the WSL 2 back-end. (Docker Toolbox is not supported. Windows container images are not supported.)
  • macOS: Docker Desktop 2.0+.
  • Linux: Docker CE/EE 18.06+ and Docker Compose 1.21+. (The Ubuntu snap package is not supported.)
  • Remote hosts: 1 GB RAM is required, but at least 2 GB RAM and a 2-core CPU is recommended.

私の場合はMacを利用しているので、Mac版のDocker Desktopを利用しています。
Docker Desktopのインストール自体は過去に行っていたので(ほとんど使わないままでしたが)、今回はアプリを最新バージョンにアップデートすることだけ実施しました。

2)VSCodeを起動し、対象ディレクトリとコンテナの設定方法を選択する

VSCodeを起動し、開発対象のプロジェクトのフォルダを開きます。
VSCodeのコマンドパレットを開き(Macであれば command + Shift + p)「Dev Containers: Open Folder in Container...」を選択します。
ディレクトリ選択画面が開くので、コンテナ上で開発したいプロジェクトのディレクトリを選択すればOKです。
ディレクトリを選択すると、コンテナ設定の方法を選択する画面になります。

  • From a predefined container configuration definition...
    →既存のテンプレートから選択する形式です。
  • From 'Dockerfile'
    →プロジェクトディレクトリに既にDockerfileもしくはdocker-compose.ymlを作成済みであれば、それを元にコンテナを実行します。

コンテナの設定方法を選択する
コンテナの設定方法を選択する

今回はまだDockerfileを何も作っていなかったので、「From a predefined container configuration definition...」のほうを選択しました。

3)ベースとなるテンプレートを選択する

ベースとなるテンプレートを選択する画面になります。
今回は「Ruby on Rails & Postgres」を選択しました。
ちなみに「i」のアイコンをクリックすると、そのテンプレートのリポジトリがブラウザで開きます。

ベースにするDockerイメージを選択する
ベースにするDockerイメージを選択する

次に、Rubyバージョンを選択します。
今回は「3.1」を選択しました。

Rubyのバージョンを選択する
Rubyのバージョンを選択する

さらに追加でインストールしたいものがあれば、選択します。
今回は何も選択せず、「OK」だけ押下。

追加でインストールしたいものを選択
追加でインストールしたいものを選択

4)プロジェクトがコンテナ上で開く

ここまでの設定を終えると、コンテナの構築が始まり、VSCodeが再読み込みされました。簡単!

VSCodeがコンテナを起動・読み込み
VSCodeがコンテナを起動・読み込み

ローカルでプロジェクトを開いていた時のVSCodeの見え方と違うのは、

  • ウィンドウの左下にDev Containerの表示が出ていること
  • ターミナルでコンテナ内のシェルが動くようになっていること

です。

ウィンドウの左下にDev Containerの表示が出ている
ウィンドウの左下にDev Containerの表示が出ている

この時点で、.devcontainerディレクトリ配下にこれらのファイルが自動作成されています。

  • create-db-user.sql
  • devcontainer.json
  • docker-compose.yml
  • Dockerfile

作成されたDockerfileの内容:

FROM mcr.microsoft.com/devcontainers/ruby:0-3.1

# Install Rails
RUN gem install rails webdrivers

# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
# The value is a comma-separated list of allowed domains 
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment this line to install additional gems.
# RUN gem install <your-gem-names-here>

# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

Dockerfileを編集する(必要に応じて)

土台のDockerイメージを変更

Rubyのバージョンが細かく指定できなさそうだったのと、既にnvmインストール済みだったりしてコンテナの中身が分かりづらかったので、土台のDockerイメージをmcr.microsoft.com/devcontainers/ruby:0-3.1ではなく、Docker Hubのofficial imageのrubyに変更しました。

# FROM mcr.microsoft.com/devcontainers/ruby:0-3.1 を書き換え
FROM ruby:3.1.3

hub.docker.com

nvm、nodeをインストール

nvmの公式で記載されているインストール方法はbashで実行するのが前提です。

# 公式に記載のインストールコマンド
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash


しかし、今回利用するDockerイメージのデフォルトのshellはshだったので、bashを実行するために

SHELL ["/bin/bash", “-c”]

をDockerfileに追記したうえでbashコマンドを書く必要がありました。
一時的にbashを使う場合はRUN ["/bin/bash", "-c", "echo hello”]とも書けるみたいですが、基本bashでよいので、こちらにしておきました。
参考: qiita.com
更新後のDockerfile:

FROM ruby:3.1.3
SHELL ["/bin/bash", "-c"]

# Install Rails
RUN gem install rails webdrivers

# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
# The value is a comma-separated list of allowed domains
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment this line to install additional gems.
# RUN gem install <your-gem-names-here>

RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash \
  && source ~/.bashrc \
  && nvm install 19.4 \
  && nvm use 19.4

再ビルド

Dockerfileを更新したら、VSCodeのコマンドパレットから「Rebuild Container」を実行するだけで、再ビルド・再読み込みされます。簡単で助かりますね。

デバッグする時

ただ、VSCodeの「Rebuild Container」でビルドすると、エラーが出た時にログが見られず原因がよく分かりませんでした。
なので、下記のような手順を取るとやりやすかったです。
(前節に書いた、今回の状況だとデフォルトshellがbashではないということを知らずビルドに失敗したりしました)

  1. いったんローカルの適当な場所にDockerfileをコピー
  2. docker build -t tmp-image .のコマンドを実行してビルド(※)
  3. エラーが出たらログを見て適宜Dockerfileを修正
  4. ビルドに成功することを確認してから、そのDockerfileの内容を.devcontainer配下の本体のDockerfileに反映し、以降はVSCodeの「Rebuild Container」を利用

※Dockerビルドコマンドの補足

  • -tはタグ名をつけるオプション
  • tmp-imageの部分は任意の名前でOK
  • .の部分はコピーしたDockerfileが置いてあるディレクトリへの相対パス