CLOVER🍀

That was when it all began.

TerraformでアプリケーションをHerokuにデプロイしてみる

これは、なにをしたくて書いたもの?

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アプリケーションのサンプルより。

https://github.com/heroku/node-js-getting-started/blob/787a0397af4e2a295607cced85aa882931c18d3d/index.js#L3

で、このスクリプトを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の指定が必要なのですが、それは今回は環境変数で与えることにします。

Environment variables

次に、「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」の時にコンソール上に表示されるかどうかです。

それ以外に変わったことは特にありません。

"Sensitive" is not secret

リソースをデプロイしてみる

それでは、リソースのデプロイを行ってみましょう。

最初に、「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で指定した名前でアプリケーションができています。

f:id:Kazuhira:20190506235207p:plain

Config Varsも見てみると、ちゃんと値が入っています。見ての通り、sensitive_varsの指定はTerraform上以外では効果がないことが
わかります。

f:id:Kazuhira:20190506235257p:plain

では、デプロイされたアプリケーションの動作確認をしてみましょう。

$ 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.

これで、アプリケーションも削除されました、と。

f:id:Kazuhira:20190506235911p:plain