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

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

タグ:git

GitHub から提供されている静的サイト公開機能である GitHub Pages 。Git 的には「特定ブランチの特定フォルダ以下を静的サイトとして公開する」機能です。GitHub のリポジトリとしてコミットされていれば、GitHub のウェブ UI を使って設定できます。

が、いくつかの制約事項はあるものの git CLI だけでも(GitHub のウェブ UI から操作しなくても) GitHub Pages を公開することもできます。その手順を確認したので以下に記載しておきます。


【前提条件】
GitHub リポジトリに静的サイトのページが公開登録されている前提が必要です。また GitHub ページとして公開したいコンテンツは "/" (つまりリポジトリ直下のフォルダ)にまとまっているものとします。

というわけで以下のようなリポジトリを作りました。リポジトリ直下に README.md, page1.html, page2.html という3つのファイルが存在しているものとします:

(https://github.com/dotnsf/gh-pages-sample)
2023012200


それぞれの中身は以下のようになっています。3つのファイルでそれぞれリンクが設定されている、というだけの単純な内容です:
README.md
# README.md

- [ページ1](./page1.html)

- [ページ2](./page2.html)

page1.html
<html>
<head>
<title>ページ1</title>
</head>
<body>
<h1>ページ1</h1>

<a href="./">README.md</a>
</body>
</html>

page2.html
<html>
<head>
<title>ページ2</title>
</head>
<body>
<h1>ページ2</h1>

<a href="./">README.md</a>
</body>
</html>

このリポジトリをそのまま GitHub Pages で公開することにして、その手順を↓に紹介します(最後に応用の形でサブフォルダを GitHub Pages で公開する方法も紹介します)。


【git CLI での公開手順】
改めて GitHub Pages として公開したいリポジトリを参照し、その URL を確認します。今回の例であれば https://github.com/dotnsf/gh-pages-sample となります:
2023012201


このリポジトリを git clone します:
$ git clone https://github.com/dotnsf/gh-pages-sample

$ cd gh-pages-sample

次に gh-pages という名前のブランチ(←ここが重要!)を作成して checkout します:
$ git checkout -b gh-pages

このブランチをリモートの gh-pages ブランチとして git push します:
$ git push origin gh-pages

ここまでの手順が成功すると、GitHub のリモートリポジトリ側にも gh-pages ブランチが作成されていることが確認できます:
2023012202


(ここまでの手順で GitHub 上で GitHub Pages の設定はしていないのですが)"Settings" - "Pages" を選択して GitHub Pages の設定画面を確認すると、gh-pages ブランチの /(root) フォルダ以下が GitHub Pages として、 https://dotnsf.github.io/gh-pages-sample/ という URL で公開されていることが分かります:
2023012203


この URL にウェブブラウザでアクセスしてみると、README.md の内容が GitHub Pages として確認できます:
2023012204


ページ内のリンク部分をクリックすると想定通りのページに推移できます。GitHub ページとして期待通りに動いていることが確認できました:
2023012205


2023012206


ここで紹介したように gh-pages というブランチを作ってコミット&プッシュすることで、そのコンテンツを GitHub Pages として公開することができるようです。これによってウェブ画面からの操作なしで GitHub Pages 公開が実現できます。


【(応用)特定のサブフォルダ以下を GitHub Pages として公開したい場合】
GitHub で gh-pages という特殊な名前のブランチに登録することで GitHub Pages として公開できることがわかりました。ただ上で説明した方法ではリポジトリのフォルダ全体を公開することになりました。特定のサブフォルダの中だけを GitHub Pages として公開する方法はないのでしょうか?

その方法がこちらです。例えばローカルリポジトリ内の web フォルダ以下を GitHub Pages として公開する場合は以下のように指定します:
$ git subtree push --prefix web origin gh-pages

gh-pages というブランチを指定するのは↑で説明したのと変わりませんが、"git push" ではなく "git subtree push" というコマンドを使います。またこの時に "--prefix フォルダ名" を指定することで指定フォルダの内容だけを gh-pages ブランチとして(=GitHub Pages として)登録する※ことになります。


※少しややこしいのですが、この subtree push を使う方法で GitHub Pages を設定した場合、gh-pages ブランチに全ファイルが登録されて、その中の web フォルダ以下が GitHub Pages になる、というわけではありません。gh-pages ブランチに web フォルダ以下の全ファイルが登録されて、ルートフォルダ以下のファイル(つまりブランチ内の全ファイル)が GitHub Pages になります。つまり main ブランチと gh-pages ブランチではリポジトリのファイル構成が変わってしまう点に注意が必要です。



自分で開発したウェブサービスの公開先として、最近は 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 を使って動かすアプリについては全てこの設定を有効にしてもいいかな、と感じています。


 

前回dokku を使ったプライベート PaaS 環境の構築、およびシンプルなアプリケーションのデプロイ手順を紹介しました。 今回はより実践的なアプリケーションとして PostgreSQL データベースを併用するアプリケーションのデプロイ手順を紹介します(といっても、実は heroku を CLI で操作する時の手順とあまり変わりません・・)。

なお今回紹介する内容は、前回のセットアップ時に "withcorona.world" という独自ドメインを設定している想定で紹介しています。異なるドメインで設定されている場合は自分で設定したドメインに適宜読み替えてください。


【dokku 内で PostgreSQL データベースを動かす】
まず dokku 環境内にデータベースサーバーを用意します。今回は PostgreSQL を使うケースを想定して以下で紹介します。まずは dokku サーバーにログインしておきます。

dokku ではいくつかのサービスが「プラグイン」という形で連携できるよう用意されています。PostgreSQL もその1つです。というわけで、まずは dokku にログインして PostgreSQL プラグインをインストールします:
# dokku plugin:install https://github.com/dokku/dokku-postgres.git

インストールが完了すると PostgreSQL データベースをインスタンス化することができるようになります。例えば "mydb" という名前を付けて1インスタンス作るには以下のように入力します:
# dokku postgres:create mydb

作成後に作ったデータベースインスタンスの情報を確認する場合は以下のように入力します:
# dokku postgres:info mydb

=====> mydb postgres service information
       Config dir:          /var/lib/dokku/services/postgres/mydb/data
       Config options:
       Data dir:            /var/lib/dokku/services/postgres/mydb/data
       Dsn:                 postgres://postgres:XXXXXXXX@dokku-postgres-mydb:5432/mydb
       Exposed ports:       -
       Id:                  a017a6896694987cb0e729b4ec1042f831eecd0d8f726d52eeea435ecd9fcf4e
       Internal ip:         172.17.0.3
       Links:               -
       Service root:        /var/lib/dokku/services/postgres/mydb
       Status:              running
       Version:             postgres:14.2

この確認結果の Dsn 値として紹介されている "postgres://" で始まる文字列がいわゆる接続文字列になっていて、(後述する)環境変数の値になります。 このインスタンスのシェルに入って PostgreSQL の CLI を使ってテーブルを1つ定義しておきたいので、以下のように実行してください(データベースに接続する時は接続文字列の "dokku-postgres-mydb" 部分を "localhost" に変えて実行してください):
# dokku postgres:enter mydb (シェルにログイン)

/# psql "postgres://postgres:XXXXXXXX@localhost:5432/mydb" (psql でデータベースに接続)

mydb=# create table if not exists items ( id varchar(50) not null primary key, name varchar(50) default '', price int default 0, created bigint default 0, updated bigint default 0 ); (SQL で items テーブル作成)

mydb=# \q (データベースから切断)

/# exit (シェルからログアウト)

これで dokku 環境内に mydb という名前の PostgreSQL データベースを1つ作り、items という名前のテーブルを1つ定義する所まで用意できました。続いて、この mydb データベースと items テーブルを使ったウェブアプリケーションを dokku 内で動かします。


【dokku 内で PostgreSQL データベースに接続するアプリケーションを動かす】
dokku 内で PostgreSQL データベースを使うアプリケーションを動かします。今回は以下のサンプルを使います(上記で作成した items テーブルを使うアプリケーションです):
https://github.com/dotnsf/cnapp_postgresql


このアプリケーションの PostgreSQL と接続する部分は以下のように記述されています:
  :
  :
var database_url = 'DATABASE_URL' in process.env ? process.env.DATABASE_URL : settings.database_url; 
var pg = null;
if( database_url ){
  console.log( 'database_url = ' + database_url );
  pg = new PG.Pool({
    connectionString: database_url,
    idleTimeoutMillis: ( 3 * 86400 * 1000 )
  });
  :
  :


具体的な挙動としては、アプリケーション実行時の "DATABASE_URL" という環境変数値を参照し、値が設定されていたらその内容を接続文字列とみなして PostgreSQL サーバーに接続する、という実装内容になっています(説明は省略しますが、接続後に items テーブルを読み書きする内容になっています)。

なので、このアプリケーションが dokku 内で実行される時に、先ほど作成した mydb データベースへの接続文字列が環境変数 DATABASE_URL として定義されていればこのアプリケーションは正しくデータベースに接続して動く、ということになります。

この辺りは heroku ユーザーであればなんとなく「同じだ・・」とわかると思います。で、その環境変数を設定するためには dokku 内のアプリケーションとデータベースがリンクさえされていれば実現できるようになっています(この辺りはクラウドネイティブアプリケーションを開発する際の 12 factors と呼ばれるベストプラクティスに沿った仕様となっています)。

というわけで、まずは dokku にアプリケーションを追加し、データベースとのリンクを設定します。今回は "cnapp" という名前のアプリケーションを作ることにして、この "cnapp" アプリケーションと "mydb" データベースをリンクしておきます(これで cnapp アプリの実行時にデータベース mydb に接続するための接続文字列が環境変数 DATABASE_URL にセットされて起動します):
# dokku apps:create cnapp

# dokku postgres:link mydb cnapp

そして前回同様にこのサンプルアプリを Git clone して、リモート接続先に dokku を追加して、main ブランチを push します:
# git clone https://github.com/dotnsf/cnapp_postgresql

# cd cnapp_postgresql

# git remote add dokku dokku@withcorona.world:cnapp

# git push dokku main

ここまでのコマンドが正しく実行されていると http://cnapp.withcorona.world/ でアクセスできるようになります※:

2022060101


※稀にこの URL では想定していないページ(Nginx のデフォルトページなど)が表示されることがあります。その場合は http://cnapp.withcorona:8080/ のようにポート番号をつけてアクセスするとうまくいきます。その後に以下のコマンドを実行するとポート番号指定なしでも正しく表示できるようになります:
# dokku proxy:ports-add cnapp http:80:8080


これだけでも一応動きますが、ついでに(?) https 接続できるよう、Let's Encrypt プラグインの設定も行っておきます:
# dokku letsencrypt:enable cnapp

最後にウェブブラウザで https://cnapp.withcorona.world/ にアクセスして動作確認します:
2022060102


最初は何も登録されていませんが、名前(name)と価格(price)を入力して追加(Create)すると、そのデータが( PostgreSQL の)mydb データベースの items テーブルに登録され、一覧として表示されるようになります:
2022060103


以上、dokku でデータベース連携アプリケーションを作って動かすための設定でした。PostgreSQL 以外にも dokku には公式機能として MySQL や Redis 、ElasticSearch といったプラグインが用意されているので、クラウドで認証した結果をセッション共有するようなアプリケーションでも動かすことができると思います。




ある程度 heroku を使ったことがある人であれば、dokku は文字通りに heroku ライクなプライベート環境に感じることができると思います(ただ dokku だとほとんどの作業が CLI からのコマンドになる点が、ウェブ GUI で色々用意されている heroku とは異なる点です)。また Cloud Foundry と比較しても、Cloud Foundry では
$ cf push (app)

のようにしてアプリをデプロイしていましたが、dokku はほぼ同様にして、
$ git remote add dokku (app) (を一度実行してから)
$ git push dokku main

といった形でアプリのデプロイができるので、Cloud Foundry の代わりとしても使いやすい環境のように感じています。今回紹介した withcorona.world というドメインは実験用の捨てドメインなのでおそらくこの紹介記事でしか使うことはないと思っていますが、他の取得ドメイン(とちょっと規模の大きめな IaaS 環境)を使って自分のプライベート Cloud Foundry 環境を作って運用してみるつもりでいます。



唐突ですが、自分も使っている IBM Cloud の PaaS 機能の1つである Cloud Foundry ランタイムのサービス終了がアナウンスされました:
Cloud Foundry on IBM Cloud サービス提供終了のお知らせ


個人的にもサービス開始とほぼ同時期から使っていたユーザーとして、またソースコードから一発でクラウド環境で動かすことができる環境として使っていただけに、とても残念なニュースでした。実は少し前から知ってはいたのですが、公開できるのが今日からということで公にできずに悶々としていました(代わりにこのブログの公開準備を進めていました)。

で、IBM Cloud として推奨している移行先は Kubernetes 系サービスや Code Engine とされています:
IBM Cloud FoundryからCode Engineへのマイグレーションに関するベスト・プラクティスの紹介:サービス・バインディングとコード


Code Engine は Kubernetes の K-native をベースとしたコンテナ・ランタイム環境です。無料で使用可能なリソース枠も用意され、これまで Cloud Foundry で動いていたアプリケーションを比較的容易に移行できる環境となっております。


ただ、私自身はこの Code Engine に加えて、以下で紹介する dokku 環境も Cloud Foundry からの移行先としてアリだと思っています。Cloud Foundry よりも heroku と比較されることが多く、また残念ながら無料で運用できるわけではない(プライベート環境なので、そのサーバーリソースが必要)のですが、一方で独自ドメインが使える上にコンテナイメージを意識することなくソースコードから稼働環境を簡単に作れる、という点で相性は悪くないと思っています。 そんな意味も含めて、このタイミングで紹介させていただくことにしました。

予定としては2回に分けて、前半である今回はセットアップ部分を中心に、次回の後半でデータベースなど外部リソースとの連携について触れるつもりでいます。





昨年あたりから heroku を使うことが多くなってきました。無料でもある程度利用できるクラウドリソースが PaaS で提供され、Git と連動してアプリケーションのデプロイができるのが非常に便利です。

とても便利なので利用頻度が高くなっていき、あっという間に無料枠の限界が近づき・・・そして同時に無料枠の制約(インスタンスが使われていないと自動的に止まっちゃうとか、複数インスタンスで運用できないとか)を超えた使い方にも興味が出てきます。そうなってくるとパブリックな heroku 利用もいいけど、プライベートな heroku 環境を自由に使えたらいいなあ、というエンジニア欲も出てきます。

そんな背景もあって、「プライベート版 heroku 」という側面もある dokku を使ってみることにしました。dokku は内部的に docker を使って1台のサーバー内に仮想的な PaaS 環境を構築するものです。heroku (や Cloud Foundry )同様にビルドパックを使った git push デプロイが可能なので、手元のソースコードを簡単に(Dockerfile とか docker イメージ化などを意識せずに)ウェブ上に公開することもできます。1台のサーバーで運用することを想定しているため可用性という面では足りないと感じる面があるかもしれませんが、自分のように作ったアプリを試験的に公開する、という機会が多い場合に非常に重宝します。またプライベート PaaS はインフラ部分の管理を自分で行うことを意味していますが、その点では1台のサーバーの面倒だけみればよい、というのはある意味でアドバンテージにもなり得るものと考えています。

今回は以下の条件で環境を構築してみました:
 ・(2022/06/01 時点で最新の)dokku v0.27.5 を使用する
 ・IBM Cloud 上に Ubuntu サーバーを1台用意して、この中に dokku 環境を導入する
 ・GoDaddy.com で取得した独自ドメイン(withcorona.world)を使った PaaS 環境を作る

IBM Cloud 上に IaaS サーバー(Ubuntu 18.04)を用意するので、このサーバー料金が必要です。IBM Cloud である必要はありませんが、後述のように DNS の設定ができる IaaS 環境が必要です(Vultr.com では同様の設定ができることを確認しています)。なお、dokku は独自ドメインを使わなくても、sslip.io サービスを併用した疑似サブドメインを使って環境構築することもできますが、今回の紹介内容では軽く触れる程度とし、動作確認などは除外させていただきます。


【dokku 環境構築】
では早速 dokku 環境を用意して、アプリケーションを dokku 上で動かしてみます。まずは1度行う必要のある環境構築の手順を紹介します。

最初にクラウド上に Ubuntu サーバーを用意します。今回利用する dokku v0.27.5 では Ubuntu 18.04, Ubuntu 20.04, Ubuntu 22.04 までがサポート対象となっていましたが、古い記事で Ubuntu 18.04 だけがサポートされていたドキュメントを参照していたこともあり、自分も Ubuntu 18.04 を使うことにしました。なお Debian 系はサポート対象ですが、RHEL/CentOS 系は 2022/06/01 時点では Experimental 機能としての提供です。

(自分の場合は)IBM Cloud 上に Ubuntu 18.04 サーバーを1台用意します。スペックは 1 vCPU で 2GB RAM のものとしましたが、ここで選ぶスペックが自分の dokku 環境となるので、比較的多くのアプリケーションを動かす場合や、(多くの DB など)多くのリソースを使うことが想定される場合は少し大きめのサーバーを用意してください:
2022060101


サーバーが用意できたら次はネームサーバーをはじめとする DNS の設定が必要です(独自ドメインを使わない場合はここを無視して dokku のインストール作業まで進んでください)。まずは自分が使うサーバー(今回だと IBM Cloud のサーバー)で DNS を設定するために必要なネームサーバー設定を確認します。IBM Cloud の場合は以下のように
 ・プライマリサーバー: ns1.softlayer.com
 ・セカンダリサーバー: ns2.softlayer.com
を設定するように指定されていることが分かります。ここは皆さんが用意したクラウドサーバーのプロバイダーによって異なるので、自分の環境のネームサーバー設定を調べる必要があります:
2022060102


このネームサーバー設定を自分が取得した独自ドメインに適用します。今回の例では GoDaddy.com で取得した独自ドメインを使うので、GoDaddy.com の DNS 設定を変更することになります。 なお今回は "withcorona.world" という独自ドメインを使って、app1.withcorona.world, app2.withcorona.world, ... といった名称のアプリケーションを運用する前提とします。まずは自分がドメインを取得したベンダーのネームサーバー設定画面(DNS 設定画面)に移動します:
2022060103


ここでネームサーバーを変更します。GoDaddy.com の場合は DNS 管理画面の少し下に設定済みのネームサーバーが表示されている画面があるので、ここの「変更」ボタンをクリックします:
2022060104


そして先ほど確認したプライマリ/セカンダリサーバーを指定します。IBM Cloud の場合はプライマリが ns1.softlayer.com 、セカンダリが ns2.softlayer.com だったので、以下のように入力し、最後に「保存」ボタンをクリックします:
2022060105


この作業はこれまで使っていた独自ドメインの DNS 設定を大きく変えることになるので警告メッセージが表示されることがあります。内容を確認して「続行」します:
2022060106


無事に設定が変更されていることを確認します(この変更内容が実際に有効になるまで1時間程度かかることがあります):
2022060107


ネームサーバーの設定変更が出来たら、次は DNS を dokku 向けのものに変更します。新しい DNS 設定画面(今回の例では IBM Cloud の DNS 設定画面)に移動し、まずメインサーバーとなる独自ドメイン名(今回の例では "withcorona.world")と、 Ubuntu サーバーの IP アドレスを入力してドメインを登録します:
2022060108



そして残りの設定を行います。dokku を独自ドメインで利用するには、以下の内容を設定する必要があります:
レコードターゲット
A@(サーバーの IP アドレス)
CNAME*(独自ドメイン名)


なお IBM Cloud の場合は上述の設定ではなく、以下の設定が必要でした(* は CNAME レコードではなく A レコードとして、ターゲットは独自ドメイン名ではなく IP アドレスで指定する必要がありました):
レコードターゲット
A@(サーバーの IP アドレス)
A*(サーバーの IP アドレス)


最終的にこのような DNS 設定となりました:
2022060109


ここまでの作業で dokku 導入前の事前準備が完了です。ここからは dokku のインストール作業を紹介します。


まず SSH 等で Ubuntu サーバーのシェルにログインします。root 以外でログインした場合は "sudo -i" を実行するなどして root に切り替え、"apt update" と "apt upgrade" を済ませておきます:
$ sudo -i

# apt update -y

# apt upgrade -y

以下のコマンドを実行して dokku を(dokku v0.27.5 を)インストールします:
# wget https://raw.githubusercontent.com/dokku/dokku/v0.27.5/bootstrap.sh;

# DOKKU_TAG=v0.27.5 bash bootstrap.sh

2つ目のコマンドが完了(5~10分くらい)すると(docker ごと)dokku が導入されています。

独自ドメインを利用する場合はそのドメインを登録するため、以下のコマンドを実行します(最後の withcorona.world 部分に独自ドメイン名を指定します):
# dokku domains:set-global withcorona.world

このコマンド実行後に、/home/dokku/VHOST ファイルの中身が指定した独自ドメインになっていることを確認してください:
# cat /home/dokku/VHOST

withcorona.world (と表示されることを確認)

また独自ドメインを所有していない(使わない)場合は以下のコマンドを実行して、sslip.io サービスを使った疑似サブドメインを登録します(最後の 11.22.33.44 部分に Ubuntu サーバーの IP アドレスを指定します):
# dokku domains:set-global 11.22.33.44

# dokku domains:set-global 11.22.33.44.sslip.io


次に、実際に dokku を使う前に(dokku の git 利用時に必要な)秘密鍵と公開鍵のペアを登録する必要があります。普段使っている秘密鍵&公開鍵があればそれを使っても構いませんし、今回の作業のために新たに1ペア作って使っても構いません。鍵ファイルの作り方はこちらなどを参照してください。ここでは秘密鍵: id_rsa 、公開鍵: id_rsa.pub という2つの鍵ファイルが手元にあるものとします。

これらを dokku のサーバー環境に登録します。まずは sftp などでこれら2つのファイルを Ubuntu サーバーの /root/.ssh/id_rsa および /root/.ssh/id_rsa.pub となるよう転送しておきます。ここまでの作業ができているものとして以下の説明を続けます。

まず鍵ファイルはファイルパーミッションが正しくないと正しい挙動になりません。これら2つのファイルのパーミッションを 400 にしておきます:
# chmod 400 /root/.ssh/id_rsa*

これら2つのファイルを dokku 環境に登録します。まずは以下のコマンドを実行して秘密鍵を登録します:
# eval `ssh-agent`

# ssh-add -k ~/.ssh/id_rsa (秘密鍵のパスフレーズ入力を求められるので正しく入力します)

続けて公開鍵も登録します:
# cat ~/.ssh/id_rsa.pub | dokku ssh-keys:add admin

dokku 作業としてはここまででほぼ完了しているのですが、ついでに SSL(https) 接続を想定した準備もしておきましょう。以下の2つのコマンドで Let's Encrypt プラグインを導入・設定しておきます:
# dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

# dokku config:set --global DOKKU_LETSENCRYPT_EMAIL=(自分のメールアドレス)

実際にアプリケーションをデプロイする前の、dokku 環境構築に必要な(1回だけの)作業は以上で終わりです。


【dokku でウェブアプリを動かす】
では構築した dokku 環境を使って実際にウェブアプリケーションを稼働させてみます。ここからの内容は dokku に新しいアプリケーションをデプロイするたびに必要な作業です(ここよりも上で紹介した作業は環境構築時に1回行うだけ)。

まずはウェブアプリケーション(のソースコード)が必要ですが、今回は自分が作ったシンプルな「ハローワールド」アプリであるこれを使って紹介することにします(自分で作ったウェブアプリがある場合はそちらを使っていただいても構いません):
https://github.com/dotnsf/simpleweb


このサンプルはアプリというほどの内容ではないのですが、起動してアクセスすると以下のような画面が表示される、というものです。GitHub ページでも公開しているので、実際のサンプルを見たい場合はこちらにアクセスしてください:
https://dotnsf.github.io/simpleweb/

2022060112
(機能はこれだけ)


過去に heroku や Cloud Foundry、Docker、Kubernetes などのコンテナ環境でクラウドネイティブなアプリを作ったことがある人であれば問題ないと思いますが、ウェブアプリケーション起動時のポート番号を環境変数 PORT から取得するようにしている点にご注意ください(以下はこの simpleweb アプリの app.js のソースコード):
//. app.js
var express = require( 'express' ),
    app = express();

app.use( express.static( __dirname + '/web' ) );

var port = process.env.PORT || 8080;
app.listen( port );
console.log( "server starting on " + port + " ..." );

では改めてこのコードを例にして dokku 環境で動かすまでの手順を紹介します。まずは "dokku apps:create" コマンドで新しいアプリケーション(今回はアプリケーション名を simpleweb としています)を作成します:
# dokku apps:create simpleweb

次に git clone でソースコードを入手して、ディレクトリ内に移動します:
# git clone https://github.com/dotnsf/simpleweb

# cd simpleweb

(heroku と同様ですが)このソースコードに新しいリモート Git オリジン(dokku)として、dokku 内の Git リポジトリを追加します:
# git remote add dokku dokku@withcorona.world:simpleweb

そして今追加したオリジン dokku に main ブランチを git push します:
# git push dokku main

(秘密鍵のパスフレーズを聞かれるので入力する)

秘密鍵のパスフレーズを聞かれるので入力すると、GitHub ではなく dokku の内部ソースコードリポジトリにコードが Push され、該当ソースコード向けのビルドパック(今回の例であれば Node.js ビルドパック)を使ってソースコードが dokku 内のコンテナとしてデプロイされて起動します(1分くらいかかります)。この辺りの一連の流れは Cloud Foundry のものに近いです。


無事にデプロイが完了すると、http://(アプリ名).(ドメイン名)/ という URL でパブリックアクセスできるようになります。今回の例であれば http://simpleweb.withcorona.world/ です(この時点ではまだ https ではアクセスできません)※。 なおドメイン名を使わない場合は http://(アプリ名).xx.xx.xx.xx.sslip.io/ でアクセスできます(xx.xx.xx.xx は Ubuntu サーバーの IP アドレス):
2022060110


※稀にこの URL では想定していないページ(Nginx のデフォルトページなど)が表示されることがあります(そうなったりならなかったりします・・・)。Nginx のデフォルトページが表示されてしまう場合は http://(アプリ名).(ドメイン名):8080/ のようにポート番号をつけてアクセスするとうまくいきます。その後に以下のコマンドを実行するとポート番号指定なしでも正しく表示できるようになります:
# dokku proxy:ports-add simpleweb http:80:8080



https でアクセスできるようにするにはもう少しコマンドが必要です。単に https でアクセスできるようにするには(Let's Encrypt で証明書を取得して適用するだけであれば)以下の dokku コマンドを入力するだけです:
# dokku letsencrypt:enable simpleweb

更に証明書の自動更新までを有効にする場合は、続けて以下のコマンドを実行します(無効にする場合は "add" を "remove" にします):
# dokku letsencrypt:cron-job --add

ここまで正しく完了すると SSL 証明書が発行&適用されて https://simpleweb.withcorona.world/ でもアクセスできるようになります(ドメインを使わない場合はこの作業を省略しても https アクセスできます):
2022060111


なお、この方法で dokku にデプロイされたアプリケーションは heroku の無料利用時のように(アクセスが一定時間以上なかった場合に)自動停止することはなく、また以下のコマンドでスケールアウトすることもできます(この例ではインスタンス数=3):
# dokku ps:scale simpleweb web=3

利用想定規模と環境構築先の Ubuntu サーバーの規模を合わせて構築することで、非常に有用なプライベート PaaS 環境になりうると思いました。


【まとめ】
今回紹介した dokku を使ったプライベート環境は(無料ではありませんが) heroku ユーザーにとってもメリットを感じられるものだと思います。

今回は dokku 環境の構築と、ランタイム部分(ウェブアプリケーション部分)を dokku 環境でデプロイするまでの手順を紹介しました。次回は(これも heroku での作業とほぼ一緒だったりしますが)PostgreSQL などのデータベースサービスを dokku 内に作ったり、データベースと組み合わせてウェブアプリケーションを動かす方法を紹介する予定です。


【参照】
今回は最小限のインストール手順やコマンドだけを紹介しましたが、詳しくは以下も参照ください:

dokku 公式インストール手順
dokku デプロイコマンド


(2022/06/04 追記)
後編はこちら
http://dotnsf.blog.jp/archives/1080505175.html
 

自作のマークダウン CMS を一週間ほど自分だけで使ってみて、まあまあな感じだったので一般公開します。

"My info" を略して "Mynfo" と名付けています。ソースコードは GitHub に公開してます。自分でも使ってみたい場合はフォークして使ってください(フォーク先は public でも private でも構いませんが、public の場合は GitHub 上のコンテンツが公開される点に注意してください):
https://github.com/dotnsf/mynfo


また、サンプルとして(皆さんには編集権限はありませんが)この公開ソースコードで作ったコンテンツサービスを以下で公開しています:
https://mynfo.herokuapp.com/


実際にみなさんもこの仕組みを使ってコンテンツサービスを公開する場合、必須ではありませんが heroku のアカウントを所有している場合は自分の GitHub リポジトリにコンテンツをコミットすることで CI/CD で最新コンテンツに更新できる仕組みがあります(しかもこの方法だとコンテンツの更新履歴が Git 側に残る、という大きなメリットがあります)。この機能を使う想定で設定方法を README.md 内で解説しているので、heroku アカウントと併せて使うことを推奨します(試しにローカルホストで動かしてみる、程度であれば不要です)。


【ローカルホストで動かしてみる】
試しにローカルホストで動かすには Node.js 環境が必要です。事前にセットアップしておきましょう。

フォークしたコードを git clone して、npm install してから node app で実行します:
$ git clone https://github.com/dotnsf/mynfo.git ("dotnsf" 部分はあなたの Github アカウント名に読み替えてください)

$ cd mynfo

$ npm install

$ node app

デフォルトでは 8080 番ポートで HTTP リクエストを待ち受けます。ウェブブラウザで localhost:8080 にアクセスします。以下のような画面が表示されれば起動成功です(初期状態では README.md にシンボリックリンクした md/index.md の内容が(マークダウンが HTML に変換されて)表示されています。英語で一通りの使い方が紹介されています):
2022021901

(↑皆さんが使うときには md/index.md と README.md とのシンボリックリンクは切っちゃって構いません)


画面右上のハンバーガーメニューをクリックすると、リポジトリ内の md/ フォルダ以下を対象フォルダとするファイル選択画面が表示されます(デフォルト状態では md/ フォルダ以下に about_markdown.md, index.md, sample.md の3つのマークダウンファイルが存在しており、それらが(.md 拡張子が省略された形で)一覧表示されています:
2022021902


試しに一番上の about_markdown を選択すると md/about_markdown.md ファイルの内容が HTML に変換されて表示されます:
2022021901


また初期画面で表示されている README.md の最初にも記述していますが、実行時の環境変数や settings.js ファイル内で値を指定することで挙動を変えることができます。例えばサービスを不特定多数の人に公開するのではなく、一部の人向けにクローズドな形で公開したい場合は環境変数 BASIC_USERNAME と BASIC_PASSWORD を指定して実行することでベーシック認証が有効になり、正しい ユーザー ID とパスワードを入力しないとコンテンツは見ることができません。

見栄えに関しては環境変数 CONTENTS_TITLE でサイトの題名(デフォルト値は "Mynfo")を、BOOTSTRAP_THEME で Bootstrap のテーマ(デフォルト値は "warning")を指定できます。例えば前者を ".NSF Mynfo"、後者を "info" に指定するとこのような見栄えになります:
2022021904


基本機能としてはこのような感じです。プロジェクトフォルダ内の md/ フォルダ内にあるマークダウン(.md)ファイルを参照することができる、というものです。ただこのサービス自体にはマークダウンの編集機能はありません。このままサーバー上で(普通に)公開する場合は、別途 md/ フォルダ内のマークダウンファイルを編集する仕組み(サーバー上で直接編集するなど)が必要です。

動くか動かないかでは動くのですが、個人的にはこのような使い方はあまり想定していません。あくまで後述のような、GitHub リポジトリと連携する使い方を想定して開発しています。


【heroku のデプロイパイプラインを使う】
サーバー上の .md ファイルを直接編集するような使い方は本来想定したものではありません。この Mynfo の最大の特徴は以下のような使い方ができることです:

・ローカルホストで(好きなマークダウンエディタで).md ファイルを編集して、
・Github にコミット&プッシュすると、
・デプロイパイプラインによって、サービス側のコンテンツも更新される

この流れを heroku を使って実現する方法も README.md に(英語で)記載していますが、このブログでは同じ内容を日本語で紹介します。

まず heroku のアカウントを作成します。heroku には複数種類のアカウントが存在していますが、ただ動かすだけであれば(容量や可用性などを考慮せずに、小規模1インスタンスだけで動かす前提であれば)無料プランで構いません。

※なお heroku の無料プランで作成するアプリは 30 分間アクセスがないと止まってしまい、次にアクセスした時に再起動してからアプリが稼働します(この場合、アプリ画面が表示されるまで再起動のぶん少し時間がかかります)。24 時間ずっと稼働状態をキープしたい場合は有料プランで運用することを推奨します。

そして heroku にログインし、画面右上から New - Create new app を選択します:



次にアプリケーション名(下図では "mynfo")を入力し、サービスを作成する地域(下図では "United States")を指定します。そして次に(Create app ではなく) Add to pipeline を選択します:



Choose a pipeline と書かれた箇所をクリックし、Create new pipeline を選択します:



そして作成するパイプラインの名称(下図では "mynfo-pipeline")を適当に入力し、デプロイ先のステージを指定します。例えば「一度ステージング環境にデプロイし、動作確認ができたらプロダクション環境へデプロイ」するようなケースも想定できますが、今回はそのままプロダクション環境へデプロイするシンプルな例を紹介します。というわけでここではパイプラインのデプロイ先として production を直接指定します。最後に Create app をクリックします:



これでパイプラインそのものは作成されましたが、まだ GitHub リポジトリとの連携ができていません。このまま作業を続けます。

デプロイ方法として GitHub 連携をしたいので Deployment Method に GitHub を選択します:



そして対象となる GitHub リポジトリ(クローンしたリポジトリ)を指定します。Connect to GitHub 欄で自分の GitHub アイコンを選択し、対象リポジトリ名(そのままであれば "mynfo")を入力して Search します:



すると該当の GitHub リポジトリが見つかるので、横にある Connect ボタンをクリックします:



これで GitHub との接続ができました。最後に自動デプロイのための対象ブランチを指定するので、このまま続けます:



Automatic Deploy 欄でパイプラインが参照する GitHub リポジトリのブランチを指定します。特にブランチを作っていない場合は main だけが選択できるので、この main が選択された状態で Enable Automatic Deploy ボタンをクリックします(別途ブランチを作成して、main 以外のブランチを指定することも可能です):



これでフォークした GitHub リポジトリの main ブランチに変更がコミットされると、パイプラインが動いて最新コードが heroku 上のアプリケーションとして https://(アプリ名).herokuapp.com/ という URL で動き出すようになります:



また、この時点で作成したパイプラインの状態を参照すると、GitHub リポジトリの指定したブランチを接続されて、更新時の自動デプロイが有効になっている旨を確認できます:



この状態で試しに1ファイルを追加して動作確認しています。ローカルホスト上で1ファイル追加して git コマンドでコミットしてもいいのですが、今回は GitHub のウェブ画面を使って追加コミットする例を紹介します。

まずはウェブブラウザで該当 GitHub リポジトリのページ(フォークしたリポジトリのページ)を開き、md フォルダを選択します:



ここからファイルを指定して編集することもできますが、今回は新しいファイルを1つ追加することにします。画面右の Add file ボタンをクリックして、表示されるメニューから Create new file を指定します:



GitHub リポジトリに直接ファイルを追加する編集画面が表示されるので、試しにファイル名を test.md 、ファイルの中身には "# テスト" と見出し行だけ入力します:



画面を下にスクロールすると、GitHub に直接コミットする情報を指定する箇所が現れます。ここでコミットコメント(下図では "md/test.md added.")、を指定し、"Commit directly to the main branch" が選択された状態のまま Commit new file ボタンをクリックします:



これで GitHub リポジトリにマークダウンファイルが1つ追加でコミットされました(ローカルホストでも編集する場合はこの状態を git pull して、ローカルホスト側も更新しておいてください)。

そして指定ブランチがコミットされたことで、heroku に作成したパイプラインが自動実行されます。パイプライン実行そのものも数秒で終わってしまいますが、下図はビルド中(デプロイ完了前)のパイプライン画面です。アプリケーションのビルド中である旨が表示されています:



デプロイが完了するとこのような画面になり、ここから最新の(追加コードが反映された状態の)アプリケーションを開くこともできます:



確認のため、アプリケーションを開きます(Open app と書かれたボタンで開きます)。そして画面右上のハンバーガーメニューをクリックします:



すると存在していなかった test.md というファイルが確認できます。試しにこのファイルを選択します:



作成した通りの内容( "# テスト")であることが確認できます:



このように GitHub リポジトリと連携して、ローカルホストや GitHub 画面でマークダウンファイルを追加・更新・削除してサーバーにコミットすると、自動で公開アプリにも反映される仕組みができました。こうしておくことでローカルホストでは VSCode など好きなマークダウンエディタで編集することができるようになって、後は編集後にコミット&プッシュすればアプリにも反映される、という仕組みになりました。

冒頭でも書きましたが、このコンテンツサービスそのものはベーシック認証を有効にすることで一部の人だけに公開することもできます。ただそれとは別に GitHub リポジトリを公開するか/しないかを考慮する点があることに注意してください。基本的にベーシック認証をかけるサービスであれば、フォーク先の GitHub リポジトリも private 扱いにするべきだと思っています。

また環境変数 GITHUB_REPO_URL (リポジトリURL)と GITHUB_BRANCH (ブランチ名)を指定して実行すると、コンテンツの各画面内に GitHub のコンテンツメニューが追加され、各ページやファイル選択画面内から GitHub の対象ページ画面に移動したり、直接編集ページに移動したり、フォルダに新しいファイルを追加することも可能です(編集は編集権限を持っているユーザーのみ使えます):
2022021906

2022021905


この GitHub と連携した使い方はちょっとクセがあるので、普段 Git を使っていない人からすると(編集画面も無いし)むしろ不便に感じるかもしれません。が、Git やマークダウンに慣れたエンジニアであれば、むしろこの使い方で(慣れたマークダウンエディタとも連携して)コンテンツを更新情報まで含めて管理ができることになって便利だと思っていて、このようなサービスを作ってみました。

ちなみに、このアプリでブログ的な使い方をする場合は、コンテンツのファイル名を日付(例えば 20220219.md)にして、環境変数 REVERSE_FILES の値を 1 にして実行することで(新しい日付のファイルがリストの上にくるように並ぶので)使いやすいのでは、と思っています。



Git やマークダウンをバッチリ使っている自分からはなかなか便利に使えているサービスなので、同様の属性を持った人に使ってみていただきたいです。

なお現時点では GitHub と heroku のパイプラインを使う想定で README.md を記述していますが、GitLab など GitHub 以外の Git システムや、GitHub Actions など heroku パイプライン以外の自動デプロイ化の仕組みを使って運用することもできると考えています。ぜひ多くのパターンで挑戦していただきたいです。

このページのトップヘ