これは、なにをしたくて書いたもの?
Terraformの勉強がてら、どこか適当にリソースをデプロイ可能なProviderはないかな?と思っていたところ、HerokuのProviderが
あったので、こちらで少し遊んでみようと。
Provider: Heroku - Terraform by HashiCorp
お題
以下のお題でやってみます。
- 簡単なNode.jsアプリケーションを作成し、GitHubリポジトリ上でソースコード管理(要タグ)
- HerokuのGitはとりあえず気にしない
- アプリケーションは、環境変数から値を読み出すものを含む
- Heroku Providerを使ったTerraformの設定ファイルを作成し、Herokuにデプロイ
- アプリケーションから読み出す環境変数の設定も含める
環境
今回の環境は、こちら。
$ terraform version Terraform v0.11.13
Herokuは、Freeプランで使用しています。
サンプルプログラム
それでは、最初にHerokuにデプロイするサンプルアプリケーションを作成しましょう。
Node.jsで、Expressを使った簡単なアプリケーションを作ります。
$ npm i express
インストールされたExpressのバージョン。
"dependencies": { "express": "^4.16.4" }
作成したアプリケーション。3つ、エンドポイントを用意しています。
index.js
const express = require('express'); const app = express(); const port = process.env.PORT || 5000; app.get('/', (req, res) => res.send('Hello Node.js App!!')); app.get('/config/var', (req, res) => res.send(process.env.VAR_MESSAGE)); app.get('/config/sensitive', (req, res) => res.send(process.env.SENSITIVE_MESSAGE)); app.listen(port, () => console.log(`[${new Date()}] Server startup, listen port = ${port}`));
ひとつは固定の値を返しますが、環境変数から読み出した値を返すエンドポイントを2つ用意しています。
また、リッスンポートはHerokuから指定されるものを使用するようにしています。
const port = process.env.PORT || 5000;
HerokuのNode.jsアプリケーションのサンプルより。
で、このスクリプトをpackage.jsonに指定し、「npm start」で起動できるようにしておきます。
"scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" },
作成したソースコードはGitHubに登録し、さらにタグをつけておきます。
$ git tag v1.0 $ git push origin v1.0
実際のソースコードは、こちら。
https://github.com/kazuhira-r/hello-heroku-nodejs-app
Terraformの設定ファイルの作成
では、Terraformの設定ファイルを作成します。 heroku-app.tf
provider "heroku" { } resource "heroku_app" "hello-app" { name = "hello-heroku-nodejs-app" region = "us" } resource "heroku_build" "hello-build" { app = "${heroku_app.hello-app.id}" source = { url = "https://github.com/kazuhira-r/hello-heroku-nodejs-app/archive/v1.0.tar.gz" } } resource "heroku_config" "hello-config" { vars = { VAR_MESSAGE = "Its' Message" } sensitive_vars = { SENSITIVE_MESSAGE = "It's Secret Message" } } resource "heroku_app_config_association" "hello-config-association" { app_id = "${heroku_app.hello-app.id}" vars = "${heroku_config.hello-config.vars}" sensitive_vars = "${heroku_config.hello-config.sensitive_vars}" }
設定ファイルの内容を、順に見ていきます。
最初は、Providerの指定です。
provider "heroku" { }
Provider: Heroku - Terraform by HashiCorp
Heroku Providerにはemailとapi_keyの指定が必要なのですが、それは今回は環境変数で与えることにします。
次に、「heroku_app」リソース。
resource "heroku_app" "hello-app" { name = "hello-heroku-nodejs-app" region = "us" }
ここで指定した「name」が、Heroku上のアプリケーション名になります。
※リソースの次に指定するNAMEとは別ですね
Heroku: heroku_app - Terraform by HashiCorp
「heroku_build」リソースを使っての、ビルドの指定。
resource "heroku_build" "hello-build" { app = "${heroku_app.hello-app.id}" source = { url = "https://github.com/kazuhira-r/hello-heroku-nodejs-app/archive/v1.0.tar.gz" } }
Heroku: heroku_build - Terraform by HashiCorp
source urlにはtar.gzであれば指定できるようなので、ここにGitHub上のURLを指定しました。リリースタグを作ったのは、このためです。
※zipではダメでした
appには、参照する「heroku_app」のNAME.idを参照するように指定します。
最後に、「heroku_config」でConfig Varsを定義し、「heroku_app_config_association」でアプリケーションとConfig Varsの関連付けを行います。
resource "heroku_config" "hello-config" { vars = { VAR_MESSAGE = "Its' Message" } sensitive_vars = { SENSITIVE_MESSAGE = "It's Secret Message" } } resource "heroku_app_config_association" "hello-config-association" { app_id = "${heroku_app.hello-app.id}" vars = "${heroku_config.hello-config.vars}" sensitive_vars = "${heroku_config.hello-config.sensitive_vars}" }
これで、アプリケーションからは環境変数として参照することができるようになります。
Heroku: heroku_config - Terraform by HashiCorp
Heroku: heroku_app_config_association - Terraform by HashiCorp
varとsensitive_varsの違いですが、「terraform plan」や「terraform apply」の時にコンソール上に表示されるかどうかです。
それ以外に変わったことは特にありません。
リソースをデプロイしてみる
それでは、リソースのデプロイを行ってみましょう。
最初に、「terraform init」を行い、Providerプラグインをダウンロードします。
$ terraform init
今回利用するHeroku Providerは、1.9.0のようです。
Initializing provider plugins... - Checking for available provider plugins on https://releases.hashicorp.com... - Downloading plugin for provider "heroku" (1.9.0)...
emailとapi_keyを、環境変数として設定します。api_keyは、Herokuのアカウント情報から確認してください。
$ export HEROKU_EMAIL=... $ export HEROKU_API_KEY=...
「terraform plan」で確認してみます。
$ terraform plan
こんな感じのプランになりました。
------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + heroku_app.hello-app id: <computed> all_config_vars.%: <computed> config_vars.#: <computed> git_url: <computed> heroku_hostname: <computed> internal_routing: <computed> name: "hello-heroku-nodejs-app" region: "us" sensitive_config_vars.#: <computed> stack: <computed> uuid: <computed> web_url: <computed> + heroku_app_config_association.hello-config-association id: <computed> app_id: "${heroku_app.hello-app.id}" sensitive_vars.%: "1" sensitive_vars.SENSITIVE_MESSAGE: <sensitive> vars.%: "1" vars.VAR_MESSAGE: "Its' Message" + heroku_build.hello-build id: <computed> app: "${heroku_app.hello-app.id}" buildpacks.#: <computed> local_checksum: <computed> output_stream_url: <computed> release_id: <computed> slug_id: <computed> source.%: "1" source.url: "https://github.com/kazuhira-r/hello-heroku-nodejs-app/archive/v1.0.tar.gz" stack: <computed> status: <computed> user.%: <computed> uuid: <computed> + heroku_config.hello-config id: <computed> sensitive_vars.%: "1" sensitive_vars.SENSITIVE_MESSAGE: <sensitive> vars.%: "1" vars.VAR_MESSAGE: "Its' Message" Plan: 4 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------
では、「terraform apply」してリソースをデプロイしてみます。
$ terraform apply
完了したようです。
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Herokuのダッシュボードを確認すると、「heroku_app」のnameで指定した名前でアプリケーションができています。
Config Varsも見てみると、ちゃんと値が入っています。見ての通り、sensitive_varsの指定はTerraform上以外では効果がないことが
わかります。
では、デプロイされたアプリケーションの動作確認をしてみましょう。
$ curl https://hello-heroku-nodejs-app.herokuapp.com Hello Node.js App!! $ curl https://hello-heroku-nodejs-app.herokuapp.com/config/var Its' Message $ curl https://hello-heroku-nodejs-app.herokuapp.com/config/sensitive It's Secret Message
OKですね。
最後に、「terraform destroy」でリソースを破棄しておしまいにしましょう。
$ terraform destroy
削除完了。
Destroy complete! Resources: 4 destroyed.
これで、アプリケーションも削除されました、と。