まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:cicd

自分で開発したウェブサービスの公開先として、最近は dokku で作った公開プライベート(=自分専用)クラウド環境が重宝しています。デプロイはソースコードを git clone(pull) して git push するだけ、という非常に簡略化されていることに加え、やはり「自分専用(=使いたいサブドメインを誰かに先に取られている心配がない)」という気持ちの余裕も以外と大きい気がしています。dokku の導入については過去の記事を参照してください:
dokku でプライベート PaaS 環境を構築する(1)
dokku でプライベート PaaS 環境を構築する(2)


今回は dokku のいわゆる CI/CD(Continuous Integration / Continuous Delivery) についてブログエントリを書きます。dokku が非常に便利であるが故に、ソースコードが更新されるたびに手作業で push するのは(大した手間ではないのですが)やはり面倒です。というわけで Git リポジトリへのプッシュをフックとする dokku のデプロイ自動化を実現する方法を調べてみました。特に今回紹介するのは GitHub Actions を使った方法で、ソースコードを GitHub リポジトリで管理している人であればちょっとした設定を追加するだけで CI が実現できます。なお、今回 CI/CD を設定する dokku サーバーは "yellowmix.net" というカスタムドメインを有効に設定してあって、"XXXXX.yellowmix.net" というホスト名でアプリケーションをデプロイできるように設定済みである、という前提で紹介します(手順3の中にこの設定に関わる部分があります)。


以下、その手順紹介です。


【手順1 パスフレーズなしの秘密鍵/公開鍵を作成・登録】
GitHub Actions 内でデプロイを自動実行するため、パスフレーズ無しで作られた秘密鍵が必要です。というわけで、パスフレーズ有りの秘密鍵を使っている場合は作成および登録の再実行が必要になります。

まずは秘密鍵と公開鍵を(再)作成します。dokku サーバーのシェルにログインして、以下のコマンドを実行します:
$ ssh-keygen

この後、秘密鍵を作成するパスを入力するよう促されます。デフォルトでは "~/.ssh/id_rsa" が指定されているはずです。変更する場合は正しいファイルパスを入力、デフォルト設定のままで問題なければそのまま Enter を入力します。

次にファイルのパスフレーズを入力するよう促されますが、今回はパスフレーズのない秘密鍵および公開鍵のペアを作りたいので、空のまま Enter を押して先に進めます。パスフレーズの指定が終わると指定されたパスで秘密鍵および公開鍵が作成されます。特に秘密鍵の内容は手順2でも必要になるので、 cat コマンドで一度中身を確認しておきます("-----BEGIN RSA PRIVATE KEY-----" で始まって "-----END RSA PRIVATE KEY-----" で終わる内容が表示されます):
$ cat ~/.ssh/id_rsa

秘密鍵が作れたら dokku に登録します。上述のセットアップを既に別の(パスフレーズ付きの)秘密鍵で実施済みで、別の秘密鍵が登録済みの場合は、以下のコマンドで一度削除しておきます:
$ sudo dokku ssh-keys:remove admin

改めて(再度)秘密鍵をファイルパスを指定して登録します:
$ cat ~/.ssh/id_rsa | sudo dokku ssh-keys:add admin

これで新しい(パスフレーズのない)秘密鍵が作成され、 dokku に登録されました。続いて GitHub のリポジトリ側で GitHub Actions 側の設定を行います。


【手順2 GitHub の対象アプリケーションに GitHub Actions 用の秘密鍵を設定】
GitHub リポジトリにコミットされたタイミングでデプロイを実行できるよう、対象アプリケーションの GitHub リポジトリに GitHub Actions を設定します。まずは GitHub の対象アプリケーションのページを開き、Settings を選択します(ユーザーの Settings ではなく、アプリケーションの Settings を開きます):
2022060901


CI ワークフローを定義する前に、ワークフロー内で使う秘密鍵の情報を先に登録しておきます。Settings ページの左ペインから Secrets - Actions を選択します:
2022060902


新規に秘密鍵を登録したいので、画面右上の "New repository secret" ボタンをクリックします:
2022060903


秘密鍵を登録する画面が表示されるので、NAME 欄に SSH_PRIVATE_KEY と入力し、Value 欄には手順1で作成した秘密鍵ファイルの内容("-----BEGIN RSA PRIVATE KEY-----" から "-----END RSA PRIVATE KEY-----" まで)をそのままコピー&ペーストして入力し、最後に "Add secret" ボタンをクリックします:
2022060904


以下のように表示されればリポジトリへの秘密鍵の登録は完了です:
2022060905



【手順3 対象アプリケーションに GitHub Actions を設定】
ここまでの準備ができていれば、後はコミット時(プッシュ時)の GitHub Actions を登録することで dokku への自動デプロイを行うことができるようになります。ソースコードに .github/workflows/ というフォルダを追加し、このフォルダ内に以下の内容の deploy.yml ファイルを追加します:
---
name: 'deploy'

# yamllint disable-line rule:truthy
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Cloning repo
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Push to dokku
        uses: dokku/github-action@master
        with:
          # specify the `main` branch as the remote branch to push to
          branch: 'main'
          git_remote_url: 'ssh://dokku@yellowmix.net:22/hostname'
          ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

このうち、青字部分(main と書かれた部分)は「どのブランチが更新された時に、どのブランチを対象にデプロイするか」を指定しています。今回は「 main ブランチ更新時に main ブランチの内容をデプロイ」したいので、このような設定にしています。master ブランチを使いたい場合はここを master に変更してください。 また赤字部分はデプロイ先のリポジトリ URL です。この例では "yellowmix.net" というドメインホストを使って https://hostname.yellowmix.net/ という URL でアプリケーションを公開するための設定にしています。この部分は皆さんの dokku 環境に合わせて変更する必要があるのでご注意ください。 加えて、最終行に手順2で設定した秘密鍵を使う指定がされています。秘密鍵は外部に漏れてはまずい情報なので、ハードコートや公開変数などは使わずに、このような形で登録済みのシークレット情報を参照するよう指定しています。

なお、この deploy.yml ファイルの中で dokku/github-action というリポジトリが指定されている箇所がありますが、これは dokku から提供されている GitHub Actions 連携用のコンテナイメージです。このコンテナイメージを使ってデプロイを行う、という作業内容になっています。同コンテナイメージのソースコードはこちらで提供されています:


ここまで用意できたら、対象のプロジェクトを GitHub にコミット&プッシュします(以下の例は main ブランチに直接コミットしている想定です):
$ git add .

$ git commit -m 'node v14'

$ git push origin main

プッシュが成功すると GitHub リポジトリで GitHub Actions が起動するはずです。起動の様子はリポジトリ内の "Actions" メニューから確認できます(画面は Actions が実行中の様子):
2022060906


実行が(成功か失敗かで)完了すると、その結果が表示されます(この例では緑マークが付いているので成功しています。失敗は赤):
2022060907


成功・失敗に関係なく、Actions 名部分をクリックすると、Actions の実行ログを見ることができます。特に失敗した場合などはこのログが原因を調べるヒントになっている可能性が高いので、失敗に終わった場合はまずログを参照することになると思います:
2022060908


GitHub Actions が成功していた場合、更新されたアプリケーションが指定 URL で稼働できているはずです。実際にアクセスして動作確認してみましょう:
2022060900


以上、GitHub Actions を使って dokku にアプリケーションを自動デプロイするための設定を紹介しました。dokku は専用のリポジトリに git push するだけでもデプロイが出来て便利ですが、一度 CI/CD の便利さを知ってしまうと「わざわざデプロイする手間が面倒」に感じられてしまうほど便利なので、今後 dokku を使って動かすアプリについては全てこの設定を有効にしてもいいかな、と感じています。


 

無料でウェブアプリケーションをデプロイできる数少ない環境となった Heroku ですが、この無料環境内で GitHub 連携による自動デプロイ(いわゆる "GitOps" っぽいデプロイ)を簡単に実現できそうなことがわかりました。手順を以下に紹介しますが、前提条件として以下の環境が揃っていることを事前に確認してください:

(1) Heroku アカウント取得&セットアップ済み
(2) GitHub アカウント取得&セットアップ済み
(3) 自分の PC に git コマンドと Node.js がインストール済み※

※ (3) は手元での動作確認が不要であればインストールされていなくても可


【アプリケーションの準備】
まず自動デプロイを行う対象となるウェブアプリケーションを GitHub に用意します。これは Heroku の dyno で動かせる状態になっていれば何でもいいのですが、以下ではこちらで用意したシンプルなウェブアプリケーションを使って紹介します(自分のアプリを使う場合は、そのアプリを GitHub のリポジトリとして登録してください)。

用意したサンプルはこちらです:
https://github.com/dotnsf/simpleweb

2022020601



内容は後述する動作確認時にも再度紹介しますが、シンプルなメッセージを表示するだけのウェブアプリケーションです:
20220206


このサンプルを使う場合は GitHub リポジトリのページ右上のボタンから "fork" して使ってください:
2022020602


fork すると、用意したサンプルアプリケーションが fork した人のリポジトリとして利用できるようになります。以下では fork 先のリポジトリ URL が https://github.com/yourname/simpleweb となったと仮定して説明を続けます(yourname の部分を自分のものに読み替えてください)。なおパイプライン連携の対象となるリポジトリは public でも private でも以下の作業はできます。

実はこの状態から heroku 側の設定に進むこともできるのですが、このアプリケーションを一度手元で動かしてみることにします(上述の (3) の準備ができていれば可能です。できていない人は読み飛ばしてください)。ターミナルから以下のコマンドを実行して、fork した自分のリポジトリを clone します:
$ git clone https://github.com/yourname/simpleweb

そして依存ライブラリを導入後に node コマンドで実行します:
$ cd simpleweb

$ npm install

$ node app

アプリケーションが起動すると 8080 番ポートで HTTP リクエストを待ち受けます。最後にウェブブラウザで http://localhost:8080/ にアクセスします:
2022020701

↑こんな感じの「ハローワールド!」が表示されれば成功です。機能としてはこれだけの、ごくシンプルなウェブアプリケーションです。このアプリケーションが GitHub のリポジトリに登録された状態になっています。


【heroku 連携】
ではこのアプリケーション(自分で用意したアプリケーションを使う場合はそのアプリケーション)の GitHub リポジトリを heroku のパイプラインやデプロイ先と連携して動くように設定します。

大まかな流れとしては、
1. heroku に(空の)アプリを登録
2. 1. のアプリにデプロイするためのパイプラインを作成
3. GitHub リポジトリとパイプラインを連携(GitHub が変更されたら自動的にパイプラインが実行されるようにする)

のようになります。1. から順に行っていきます。

1. まずは heroku にログインします。heroku アカウント未取得の場合は "Sign Up" リンクから作成することもできます:
https://www.heroku.com/

2022020702


ログインに成功すると以下のような画面になります。過去にアプリケーションを登録したことがあると初期画面にアプリケーション一覧が表示されますが、初めての場合はアプリケーションは表示されず、このような登録画面になります:
2022020703


では先程 GitHub に用意(fork)したアプリケーションが heroku 上で動かすことができるように登録します。画面右上のボタンから "New" - "Create new app" を選択します:
2022020704


そしてアプリの名前とデプロイ先リージョンを指定します。名前は他に使われていないものを指定する必要があります(下図では "dotnsf-simpleweb" という名称を指定しています)。リージョンは(無料版の場合は)United States か Europe のどちらかを選択します。単にアプリを作るだけならここで "Create app" ボタンを押せばいいのですが、今回はアプリと同時にパイプラインも作ってしまいましょう。というわけで "Add to pipeline" ボタンをクリックします:
2022020705


2. 続けてパイプラインの作成を行います。上の続きで "Add to pipeline" ボタンをクリックすると "Choose a pipeline" という選択エリアが現れるので、ここで "+ Create new pipeline" を選択します(このアプリ用の新しいパイプラインを作る、という意味です):
2022020706


するとパイプラインの名前(下図では "dotnsf-simpleweb-pipeline" としています)と、デプロイ先ステージを指定する画面が現れます。名前は好きな名前でいいのですが、デプロイ先ステージは "staging"(検証用) か "production" (本番用)かを選択します。下図ではそのまま本番環境にデプロイする想定で "production" を選択しています※。 最後に "Create app" ボタンをクリックします:
2022020707

※別のアプリケーションをもう1つ作って staging に指定し、GitHub → Staging(検証用アプリ) → Production(本番運用アプリ) というパイプラインを作ることもできますが、今回は割愛します。


するとこのような画面になります。ここまでで 1. のアプリにデプロイするための 2. のパイプラインができあがりました:
2022020708


そして最後の 3. の連携設定を行います。「GitHub のリポジトリが更新された」というタイミングに合わせて、同リポジトリの main ブランチの内容をパイプラインで指定されたアプリにデプロイする、というものです。

まず同じブラウザで GitHub にログインしておきましょう。GitHub のセッションがある状態ですすめることで面倒な認証部分を省略できます:
https://github.com/

そして先程の画面の続きです。まずはデプロイ方法として "GitHub" と書かれたアイコンをクリックします:
2022020701


すると画面下部が変わり、"Connect to Github" と書かれたボタンが現れます。このボタンをクリックします:
2022020702


するとどのリポジトリと連携するかを指定する画面に切り替わります。上部で GitHub へのログインを済ませていれば、後は自分のリポジトリを指定するだけです。自分の GitHub ID が指定されている状態で、対象リポジトリ(fork したリポジトリ)を指定するので "simpleweb" と入力して "Search" ボタンをクリックします:
2022020703


すると自分のリポジトリ内の simpleweb リポジトリ(fork したリポジトリ)が見つかります。その横の "Connect" ボタンをクリック:
2022020704


ここまでの作業でパイプラインと GitHub の simpleweb リポジトリとがつながりました。後は対象ブランチを選択しての自動デプロイ機能の設定だけです:
2022020705


画面下にスクロールすると、GitHub リポジトリのどのブランチからアプリケーションをビルドして自動デプロイするか、という選択画面になります。今回は main ブランチをそのまま使うことにして(つまり main ブランチに直接変更を加えることにして)"main" を指定したまま "Enable Automatic Deploys" ボタンをクリックします:
2022020706


すると画面の表示内容が切り替わり、main ブランチからの自動デプロイが有効になったことがわかります。これで事前準備が整いました:
2022020707


ちなみに、この時点で heroku のパイプライン画面を表示すると、このように GitHub から連携したパイプラインが自分の simpleweb アプリ(Production)につながっている様子が確認できます:
2022020705



【動作確認】
ではここまで設定した内容が正しく動作することを確認してみます。ある程度 git コマンドの操作がわかる人であれば、ローカルリポジトリの内容を変更して、git add して、git commit して、git push する・・・という一連の流れを実施いただいてもいいのですが、今回はウェブ画面だけで同じことを行ってみます。

改めて GitHub の(自分の)デプロイ対象リポジトリ(https://github.com/yourname/simpleweb)をウェブブラウザで開きます:
2022020701


ここに変更を加えます。見た目で変更がわかりやすいのは web/ フォルダ以下にファイルを追加したり、既存の index.html を変更したりすることですが、今回は後者でいきましょう。web/ フォルダを選択後に index.html ファイルを選択し、画面右上の鉛筆マークをクリックして編集画面に切り替えます:
2022020702


index.html をブラウザで編集する画面に切り替わるので、何か変更を加えてみます。以下の例では見た目にもわかりやすそうな所で、26 行目の「ハローワールド!」と表示されていた部分を「ハロー heroku ワールド!」と書き換えてみました:
2022020703


画面を下にスクロールすると、git commit のオプション入力画面になります。コミットメッセージ(下の例では "index.html メッセージ変更")を入力し、対象ブランチはデフォルトの "Commit directly to the main branch" が選択されたままにします。最後に "Commit changes" ボタンをクリックして git commit します(サーバー側を直接 git commit するので、 git push は不要です):
2022020704


コミットしました:
2022020706


先程のパイプライン画面を表示すると、GitHub リポジトリのコミットが行われたことを検知して、パイプラインが稼働している様子が確認できます(シンプルなパイプラインなのですぐに稼働後、つまりデプロイ後の状態に切り替わってしまいます・・。下図はデプロイ後の画面です):
2022020707


では heroku 上にデプロイされた変更後のアプリケーションを確認してみます。https://(アプリ名).herokuapp.com/ を開くか、またはパイプライン画面右下の "Open app" と書かれた箇所をクリックして、heroku 上のアプリケーションをウェブブラウザで開きます:
2022020707


heroku 上で稼働しているアプリケーションが開き、先程加えた変更が有効になっていることを確認します:
2022020708


これで GitHub リポジトリと連携した heroku デプロイ用パイプラインが作れて、かつ自動実行も有効になり、GitHub リポジトリに変更が加わればウェブアプリケーションにも反映される、という仕組みの構築ができました。

現実的にはステージング環境やローカル環境で動作確認してから反映、という流れになると思いますが、もちろんその方法(手動で git コマンドを使ってコミット&プッシュ)でも自動的に新しいアプリケーションに切り替えることもできます。またそもそもの Git ブランチ戦略をどうするか?という課題も出てくると思っています。

一方で、CLI を使わなくても(Git リポジトリをウェブで更新さえすれば)公開アプリケーションの更新ができるので、普段 CLI を使わないような、あまり詳しくない人でも(直接アプリケーションの挙動に影響しない範囲での変更であれば)コンテンツの変更管理ができる仕組みが構築できたと思っています。こういう仕組みが簡単に作れてしまう点は heroku の強みだと思います。

このページのトップヘ