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

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

タグ:heroku

前回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
 

最近のブログでは独自ドメインを扱うネタが多くなっています(これとか、これとか)。この背景としては旧 Google Apps(現 Google Workspace)の無料版が廃止になって、猶予期間の終了が迫り、これまで取得したドメインの運用を Google Apps に任せていたことに起因して、無料または安価な代替運用先を探していることにありました。ただ(自分的に優先順位の高かった)独自ドメインのメールサーバーについてはある程度対応できたと思っています。

上述のように独自ドメインのメール環境についてはある程度解決しました。次は独自ドメインウェブサイトの問題です。独自ドメインのウェブサイトやウェブサービスを Google Apps を使わずに用意する方法です。まあ、ホスト名だけであれば DNS の CNAME 設定だけでなんとかなるのですが、問題になるのは SSL を使いたいケース(つまり https で始まる URL で独自ドメインのウェブサイトを公開したいケース)です。

これも手間を惜しまないのであれば、Let's Encrypt などで独自ドメインの証明書を無料で取得・更新できます。その証明書を使ってサービスを公開すれば https 対応はできます。ただ、これはこれで(アプリケーション側で証明書を意識するのも)面倒だし、もし証明書の更新を忘れてしまうとサービスが見えなくなってしまったり、証明書を更新した際の再起動なども行う必要があるので、手順としても(コマンドラインで作業するなど)面倒だったりします。独自ドメインのウェブサイトを運用する上でここを(できれば無料、または安価で)どうにかできないだろうか、という課題があります。

そして本ブログエントリはこの部分を
 ・アプリケーションは無料の Heroku で公開して、
 ・無料の Cloudflare で SSL 対応した独自ドメイン名でアクセスできるようにする
という構成を実現するための手順を紹介します。


【アプリケーションを Heroku で公開する】
対象のウェブアプリケーションを Heroku で公開します。アカウントを所有していない場合はサインアップします。

ここでは特別な設定はほぼ不要ですが、1点だけ。Heroku で独自ドメインを使うには、有料版を利用するか、クレジットカードを登録する形で無料枠を使う必要がある点に注意してください。Heroku 自体はクレジットカードを登録せずに無料範囲内で利用することができ、その範囲内でウェブアプリケーションを公開することもできますが、この後独自ドメインを使う場合は無料で使う場合であってもクレジットカードを登録する必要がある、という意味です。

Heroku は GitHub と連携してアプリケーションをデプロイしますが、ここを CLI で手動で行ってもいいし、Git コミットをハンドリングして自動デプロイにしても構いません。いずれかの方法でアプリケーションを Heroku にデプロイしてください。なお後者の方法については過去にこのブログでも紹介したことがあるので、よかったら参考にしてください:
Heroku のパイプラインで GitHub コミット時に自動デプロイする


以下では dotnsf-www.herokuapp.com というホスト名で Heroku 上でアプリケーションが稼働しているものとして説明を続けます。Heroku 上で独自ドメイン向けの設定を行う必要もありますが、 Cloudflare 側での設定をまず先に済ませてから Heroku 上での設定を行います。


【Cloudflare で独自ドメインの設定をする】
次に Cloudflare 側の設定を行います。アカウントを取得していない場合は Cloudflare の無料プランでサインアップし、所有している独自ドメインを登録します。なお以下では独自ドメイン(pi314.jp というドメインを使う例を紹介します)は取得済みで、そのドメインの DNS 管理を Cloudflare にさせる前提で紹介します。

独自ドメインを登録した後、まずは "SSL" のメニューを選択して、「暗号化モード」が「フル」に設定されていることを確認します。もし異なる設定になっている場合は「フル」に変更します。この設定にすることで利用者のブラウザから、Cloudflare を経由して(今回の場合は Heroku 上の)アプリケーション・サーバーまでの全ての経路の通信を暗号化することができるようになります。また独自ドメインの SSL 証明書の更新も自動化されます:
2022040801


そして "DNS" メニューを選択して DNS の設定を行います。今回は上述の dotnsf-www.herokuapp.com というサーバーを www.pi314.jp という名前で公開できるよう設定します。「レコードを追加」と書かれたボタンをクリックします:
2022040802


追加するレコードは以下のようにして「保存」ボタンをクリックします:
 タイプ: CNAME
 名前: www (www.pi314.jp という名前で公開する、という設定です)
 ターゲット: dotnsf-www.herokuapp.com
 プロキシ: ON(プロキシ済み、と表示されます)
2022040803


入力した内容が DNS 画面に表示されるようになります。これで https://www.pi314.jp/ という URL にアクセスがあると、https://dotnsf-www.herokuapp.com/ に(利用者から見えない所で)転送されてページが表示できるようになります。また Cloudflare を使っているので、セキュリティプロキシも有効になっています:
2022040804



【heroku 側のアプリケーションを再設定する】
これでアプリケーションの独自ドメイン利用ができる・・・わけではありません。最後に SSL を使えるように実際には dotnsf-www.herokuapp.com 上で動いているアプリケーションが www.pi314.jp というホスト名でアクセスされる可能性があることを予め認識させておく必要があります。そのためには Heroku の CLI を使って(CLI でログイン後に)以下のコマンドを実行※します:
$ heroku domains:add www.pi314.jp --app dotnsf-www

※このコマンドを実行するには heroku を有料サービスで使うか、または無料利用であってもクレジットカードを登録しておく必要があります。


このコマンドを実行後、しばらく(数分)待ってからウェブブラウザで https://www.pi314.jp/ にアクセスすると、https://dotnsf-www.herokuapp.com/ の画面が https://www.pi314.jp/ というホスト名で表示できることが確認できます。


以上、Heroku も Cloudflare も有償サービスを使わないのであれば、この方法でも無料の範囲内で独自ドメインでウェブアプリケーションを公開することができそうです。



初めて Heroku Connect を使ったアプリケーションを作ってみました。普段使わない環境や手順もあったことに加え、日本語資料をあまり多く見つけられなかったので、自分の備忘録も兼ねて一連の手順をまとめてみました:
2022022700



【Heroku Connect とは】
Heroku Connect は Heroku のアプリケーションリソースの1つで、SalesForce のデータと Heroku Postgres( PostgreSQL データベース)との双方向同期機能です。これによって SalesForce 上のデータを Postgres データベースのデータとして読み書きできるようになる(Heroku アプリケーションからは PostgreSQL DB に接続することで SalesForce のデータを読み書きできるようになる)というものです。ある意味で閉じられた SalesForce データを、オープンな Heroku アプリケーション環境の一部として取り扱うことができるようになります。

なお Heroku Connect にも Heroku Postgres にも無料枠があり、一定条件内であれば無料で動作確認程度はできるものです。

Heroku Connect について、詳しくはこちらも参照ください:
https://devcenter.heroku.com/articles/heroku-connect


【Heroku Connect の設定】
実際に Heroku アプリケーション上で Heroku Connect を有効に設定し、SQL でデータを取り出す、という手順を行うまでの設定手順を紹介します。

前提条件として、Heroku のアカウントはもちろんですが、SalesForce.com でオブジェクト開発のできるアカウントが必要です。無料の Developer Edition も用意されているので、アカウントを持っていない場合はまずはアカウントを作成しておいてください。Developer Edition はこちらから:
https://developer.salesforce.com/ja


順序としてはもう少し後でもいいと思うのですが、SalesForce.com 側の話になっているこのタイミングで Heroku Connect で読み書きするデータオブジェクトを決めておきます。私自身が SalesForce.com にあまり詳しくないので標準オブジェクトから1つ選択しますが、ここでの対象はカスタムオブジェクトでも構わないはずです。

SalesForce.com (Developer Edition)にログインし、「オブジェクトマネージャ」と書かれた箇所をクリックします:
2022022701


標準で用意されている(と、カスタムオブジェクトを追加した場合はカスタムオブジェクトも含めた)オブジェクトの一覧が表示されます。この中から Heroku Connect で取り出す対象を1つ決めます。私はよく分かっていないこともあって、標準オブジェクトでサンプルデータもはじめからいくつか格納済みの「取引先」オブジェクトを対象にする前提で以下を紹介します。別のオブジェクトを使うこともできると思いますが、適宜読み替えてください:
2022022702


オブジェクトの表示を絞り込む場合は「クイック検索」フィールドに名前を一部入力すると、オブジェクトのラベルでフィルタリングされます。下の例では「取引先」でフィルタリングした結果です。「取引先」オブジェクトは API 参照名が "Account" となっていることがわかります。この名称は後で使うことになるのでメモしておきましょう:
2022022703


SalesForce.com 側の準備はこれだけです。Heroku Connect で同期するオブジェクトデータが決まっていれば Heroku 側の準備にとりかかります。

改めて Heroku にログインし、アプリケーションを1つ作成します。以下の例では "forceobject" というアプリケーションに対して Heroku Connect を設定する想定で紹介しているので、アプリケーション名は自分のアプリケーション名に読み替えてください。またこの時点では Heroku Connect を含めたアドオンは1つも設定していないものとします:
2022022701


このアプリケーションに Heroku Connect をアドオンします。上記画面の "Configure Add-ons" と書かれた箇所をクリックします:
2022022702


アプリケーションのリソース画面に移動します。この "Add-ons" と書かれたフィールドに "Heroku Connect" と入力すると "Heroku Connect" が見つかります。見つかった名前の部分をクリックします:
2022022703


アプリケーションに Heroku Connect を追加する確認ダイアログが表示されます。使用条件と、プランが "Demo Edition - Free"(無料)となっていることを確認して "Submit Order Form" ボタンをクリックします※1:
2022022704


※1 Heroku Connect Demo Edition には以下の制約があるようです:
 ・最大 10,000 行のデータまで同期
 ・同期間隔の最小値は 10 分
 ・ログは7日間保持


アプリケーションに Heroku Connect がアドオンされました:
2022022701


続けて同期を行うデータベースである Heroku Postgres をアドオンします。先程と同様に "Add-ons" フィールドに "Heroku Postgres" と入力して、候補として見つかる "Heroku Postgres" をクリックします:
2022022701


アプリケーションに Heroku Postgres を追加する確認ダイアログが表示されます。使用条件と、プランが "Hobby Dev - Free"(無料)となっていることを確認して "Submit Order Form" ボタンをクリックします:
2022022702


これで Heroku Postgres もアドオンとして追加できました。次にこの2つ(Heroku Connect と Heroku Postgres)を接続するための設定が必要ですが、これにはスキーマと呼ばれる DB の情報が必要になります。スキーマを確認するため、画面上部の "Settings" と書かれたタブをクリックします:
2022022703


"Config Vars" 節の "Reveal Config Vars" ボタンをクリックして環境変数を確認します:
2022022702


すると環境変数 DATABASE_URL が設定されていることがわかります。その値を確認するため、 "DATABASE_URL" と書かれた行の右にある鉛筆ボタンをクリックします:
2022022703


以下のようなダイアログが表示され、"Value" と書かれたフィールドに環境変数 DATABASE_URL に設定された値を確認できます:
2022022704


この値は以下のような形式になっているはずです。この最後の "/" 文字から右にある部分がスキーマです。このスキーマ値を確認した上で Heroku Connect の設定に移ります:
postgres://*****:*******@ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com:5432/(スキーマ)


改めて Heroku Connect の設定を続けるため、"Heroku Connect" と書かれた箇所をクリックします:
2022022701



Heroku Connect の設定画面が表示されます。"Setup Connection" ボタンをクリックします:
2022022702


以下のような画面に切り替わります。"Enter schema name" と書かれたフィールド(デフォルトでは "salesforce" と入力されたフィールド)に先程確認したスキーマ名を入力します。正しく入力できたら画面右上の "Next" ボタンをクリック:
2022022701


ここで SalesForce.com のアカウント認証が必要になります。画面右上の "Authorize" ボタンをクリック:
2022022702


SalesForce.com のログイン画面が表示されるので、ID とパスワードを入力してログインします:
2022022703


正しく認証が行われると Heroku Connection の接続が行われ、以下のような画面になります:
2022022704


Heroku Connect 側の準備としての最後にマッピングを行います。画面上部の "Mapping" タブを選択し、右下の "Create Mapping" ボタンをクリックします:
2022022701


SalesForce 側のオブジェクトの一覧が表示されます。今回は「取引先」を対象に同期したいので、"Account" オブジェクトを選択します:
2022022702


Account オブジェクトのマッピング条件を指定する画面が表示されます。デフォルトでは10分おきに SalesForce からデータベースへの同期のみが有効になっていますが、その条件を変えたい場合はここで指定します。また画面下部には Account オブジェクトの中のどの属性値を同期の対象とするかを指定する表があります:
2022022703


デフォルトでもいくつか指定されていて、そのままでもいいと思います。とりあえず取引先名称となる Name がチェックされていることを確認しておきましょう:
2022022704


画面上部に戻り、最後に "Save" ボタンをクリックして、この条件を保存します:
2022022703


指定された条件が保存され、最初の同期が行われます。少し待つと Status が "OK" となります。これで SalesForce の取引先情報が Heroku Postgres のデータベースに格納されました。Heroku Connect の設定が完了です:
2022022701



【アプリケーションから同期されたデータを参照する】
ここまでの設定ができていれば、アプリケーションから(PostgreSQL を参照して)SalesForce のデータを取り出すことができます。といっても、ここまでの設定が済んでいれば該当の PostgreSQL サーバーに接続して、以下の SQL を実行するだけ(この SQL を実行するプログラムを書くだけ)です:
select * from (スキーマ名).Account

例えば Node.js の Express フレームワークを使ったウェブアプリとして実装するとこんな感じになります:
//. app.js
var express = require( 'express' ),
    app = express();

var PG = require( 'pg' );
PG.defaults.ssl = true;
var database_url = 'DATABASE_URL' in process.env ? process.env.DATABASE_URL : ''; 
var schema = '';
if( database_url.indexOf( '/' ) > -1 ){
  var tmp = database_url.split( '/' );
  schema = tmp[tmp.length-1];
}

var pg = null;
if( database_url ){
  console.log( 'database_url = ' + database_url );
  pg = new PG.Pool({
    connectionString: database_url,
    ssl: { require: true, rejectUnauthorized: false },
    idleTimeoutMillis: ( 3 * 86400 * 1000 )
  });
}


app.get( '/', async function( req, res ){
  res.contentType( 'application/json; charset=utf-8' );

  try{
    var conn = await pg.connect();
    var sql = 'select * from ' + schema + '.Account';
    var query = { text: sql, values: [] };
    conn.query( query, function( err, result ){
      if( err ){
        console.log( err );
        res.status( 400 );
        res.write( JSON.stringify( err, null, 2 ) );
        res.end();
      }else{
        res.write( JSON.stringify( result, null, 2 ) );
        res.end();
      }
    });
  }catch( e ){
    console.log( e );
    res.status( 400 );
    res.write( JSON.stringify( e, null, 2 ) );
    res.end();
  }finally{
    if( conn ){
      conn.release();
    }
  }

});

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

(サンプルはこちら)
https://github.com/dotnsf/forceobject


上述の環境変数 DATABASE_URL を指定して、以下のように実行します:
$ DATABASE_URL=postgres://*****:*******@ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com:5432/ssssss node app

するとピンク色の部分で DATABASE_URL からスキーマを取り出し、青字の部分で SQL 文を生成して実行する、というものです。実行後にウェブブラウザで http://localhost:8080/ にアクセスすると以下のような SQL 実行結果を得ることができます:
2022022700


実行結果の JSON オブジェクト内の rows キーの配列値として SQL の実行結果が格納されています。各要素に含まれる属性はマッピング時に指定したものになっていて、特に name 属性には取り出したオブジェクト名称(つまり取引先の名称)が格納されていることが確認できます。


これを有効活用するには、まず自分自身がもう少し SalesForce 自体に詳しくなる必要がありそうですが、今は SalesForce 傘下にある Heroku らしいビジネス色の濃い機能が、Heroku の機能にうまく統合されて提供されているようでした。設定段階が比較的面倒な気もしていますが、一度設定できてしまえば後は普通に PostgreSQL を操作する感覚で使えるし、双方向同期を有効にすればウェブアプリ側から SalesForce のデータを活用して作成・更新・削除といった変更処理もできるようになると思います。


 

無料でウェブアプリケーションをデプロイできる数少ない環境となった 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 の強みだと思います。

このページのトップヘ