ストリームからの記事

序章

オーケストレーションツールの助けを借りて、DevOpsの専門家はいくつかのAPI呼び出しを活用してスタックをデプロイできます。 Terraform は非常にシンプルでありながら強力なツールであり、スタックをコードとして記述し、 Git を使用して定義ファイルをコミットすることで、スタックを共有して最新の状態に保つことができます。 Terraformは、 HashiCorp Vagrant Packer Consulなどの人気のあるオープンソースツールの作成者によって作成されています。

Terraformは、物理サーバーや仮想サーバーから電子メールやDNSプロバイダーまで、インフラストラクチャを起動するための一般的な構成を提供します。 起動すると、Terraformは、構成の進化に応じてインフラストラクチャを安全かつ効率的に変更します。

このチュートリアルでは、 DigitalOcean Terraform Cloud-init [を使用して、完全に機能する洗練されたNode.jsアプリケーションの環境をセットアップする方法を示します。 Ubuntu 14.04では、およびPM2。 サンプルアプリケーションとして、を使用しますキャビン 、オープンソース React 戻ってきたによって開発されたNode.jsアプリケーション GetStream.io 。 最終的な出力は、機能が豊富でスケーラブルなソーシャルネットワークアプリになります。

まず、Terraformを使用して、事前定義された構成を使用してキャビンをデプロイします。 次に、その構成を深く掘り下げて、その構成を理解できるようにします。

DigitalOceanサーバーへのTerraformのインストールのみに関心がある場合は、DigitalOceanでTerraformを使用する方法を参照してください。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • このチュートリアルでTerraformを使用して作成する2GBのUbuntu14.04サーバー1台。
  • ローカルマシンにインストールされているGitクライアント。
  • キャビンはログインにFacebookを使用するため、Facebookアカウント。Facebookアプリケーションを作成できます。
  • cabin.example.comなどのドメイン。 このドメインをステップ4で取得するIPv4アドレスにポイントし、FacebookのサイトURLにこれが必要になります。

必須ではありませんが、このチュートリアルは、Streamのキャビンチュートリアルシリーズを完了していることを前提としています。 キャビンの機能に不可欠な役割を果たすため、キャビンが本番環境で機能するために必要ないくつかのプロバイダーのAPIキーと設定が必要になります。

これらのキーを取得しなくても、このチュートリアルは引き続き機能します。 Terraformを使用してキャビンアプリケーションをプロビジョニングおよびデプロイすることはできますが、必要なすべてのコンポーネントを構成するまで、アプリケーションは使用できません。

これらのサービスの詳細については、Streamの次のブログ投稿にアクセスしてください。

ステップ1—サンプルアプリケーションを入手する

キャビンサンプルアプリケーションをGitHubから、ローカルマシン上の選択したディレクトリに複製します。 私たちはMacを使用していますが、あなたもそうだと思います。

まず、ホームディレクトリに移動します。

  1. cd ~

次に、gitを使用してリポジトリのクローンを作成します。

  1. git clone https://github.com/GetStream/stream-react-example.git

これにより、サンプルアプリケーションがstream-react-exampleという新しいフォルダーに複製されます。 キャビンのTerraformプロジェクトが含まれているstream-react-example/terraform/do/cabinフォルダーに移動します。

  1. cd stream-react-example/terraform/do/cabin

このフォルダについては、後ほど説明します。 しかし、最初に、Terraformを設定しましょう。

ステップ2—Terraformをインストールする

OSXに簡単にインストールするには、次のコマンドを発行してHomebrewを使用してTerraformをインストールできます。

  1. brew install terraform

または、http://terraform.ioからTerraformをダウンロードすることもできます。 ダウンロードしたら、以下に示すように、コマンドパスで使用できるようにします。

  1. PATH=location/of/terraform:$PATH

これにより、一時的にTerraformがパスに追加されます。 この変更を永続的にする場合は、OSXでファイル~/.bash_profileを編集し、次の行を追加します。

〜/ .bash_profile
export PATH=location/of/terraform:$PATH

次に、Terraformが正しくインストールされていることを確認するには、次のコマンドを実行します。

  1. terraform

Terraformのオプションを示す次の出力が表示されます。

Output
usage: terraform [--version] [--help] <command> [<args>] Available commands are: apply Builds or changes infrastructure destroy Destroy Terraform-managed infrastructure fmt Rewrites config files to canonical format get Download and install modules for the configuration graph Create a visual graph of Terraform resources init Initializes Terraform configuration from a module output Read an output from a state file plan Generate and show an execution plan push Upload this Terraform module to Atlas to run refresh Update local state file against real resources remote Configure remote state storage show Inspect Terraform state or plan taint Manually mark a resource for recreation untaint Manually unmark a resource as tainted validate Validates the Terraform files version Prints the Terraform version

Terraformがインフラストラクチャを開始する前に、次の2つの設定を行う必要があります。

  1. DigitalOceanトークン
  2. SSHキーペア

それでは、最初にDigitalOceanトークンを処理しましょう。

ステップ2—DigitalOceanアクセストークンの構成

Terraformは、DigitalOcean APIを使用するために、DigitalOceanアクセストークンを必要とします。

DigitalOceanアカウントにログインし、APIリンクをクリックします。 次に、新しいトークンの生成ボタンをクリックします。 書き込みアクセスを必ず確認してください。 ページに再度アクセスするとキーが表示されないため、ユーザーインターフェイスに新しいアクセスキーが表示されます。これをクリップボードにコピーする必要があります。

次に、お気に入りのテキストエディタでファイルvariables.tfを開き、tokenセクションを見つけます。

変数.tf
variable "token" {
  description = "DO Token"
}

テキストdefault = で始まる新しい行を追加し、DigitalOceanAPIトークンを含めます。 トークンを引用符で囲むことを忘れないでください。

変数.tf
variable "token" {
  description = "DO Token"
  default = "57eaa5535910eae8e9359c0bed4161c895c2a40284022cbd2240..."
}

ファイルを保存して閉じます。

次に、SSHキーペアを使用するようにTerraformを構成しましょう。

ステップ3—SSHキーペアを追加する

Terraformは、作成後にサーバーに接続するためにSSHキーを必要とします。これにより、Terraformはパッケージをインストールし、アプリケーションをデプロイできます。

~/.sshディレクトリを調べて、キーペアがすでにあるかどうかを確認します。

  1. ls -al ~/.ssh

ほとんどの場合、秘密鍵と公開鍵で構成される少なくとも1つの鍵ペアがあります。 たとえば、id_rsa.pubid_rsaがあるとします。

警告:既存のキーペアがすでにDigitalOceanアカウントに関連付けられている場合は、競合を避けるために、DigitalOceanダッシュボードを使用して削除するか、新しいキーペアを生成する必要があります。

キーペアがない場合、または持っているキーがすでにDigitalOceanアカウントに関連付けられている場合は、SSHキーの設定に関するDigitalOceanのチュートリアルを参照して設定してください。

APIトークンの場合と同様に、.pubファイルの内容をvariables.tfファイルに貼り付ける必要があります。 Macを使用している場合は、次のコマンドを発行してSSH公開鍵をクリップボードにコピーできます。

  1. pbcopy < ~/.ssh/your_key.pub

catコマンドを使用して公開鍵の内容を画面に表示し、手動でクリップボードにコピーすることもできます。

  1. cat ~/.ssh/your_key.pub

次に、エディターでファイルvariables.tfを開き、SSH公開鍵ファイルのコンテンツをsshkey設定に追加します。

変数.tf
variable "sshkey" {
  description = "Public ssh key (for Cabin user)"
  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADA...== [email protected]"
}

この手順を完了したら、ファイルを保存して終了します。

TerraformとDigitalOceanで使用する新しいキーを生成した場合は、次の2つのコマンドを実行して、デフォルトのキーの代わりに新しいキーが使用されるようにする必要があります。

  1. eval "$(ssh-agent -s)"
  2. ssh-add ~/.ssh/your_id_rsa

代替キーペアを使用している場合は、新しいシェルを開くたびにこれを実行する必要がある場合があります。

Terraformに必要な変数を提供したので、サーバーを作成し、Terraformを使用してアプリをデプロイする準備が整いました。

ステップ4—Terraformを実行する

ここに楽しい部分があります! 構築するインフラストラクチャを見てみましょう。 Terraformは、サーバーのセットアップからアプリのデプロイまで、多くの作業を行います。 次のコマンドを使用して、Terraformに正確に何を実行するかを表示させることができます。

  1. terraform plan

このコマンドの出力は非常に冗長なので、次のステートメントに注目してください。

Output
+ digitalocean_droplet.cabin-web ... + digitalocean_floating_ip.cabin-web-ip ... + digitalocean_ssh_key.cabin-ssh-key ... + template_file.pm2_processes_conf ... + template_file.userdata_web ...

行頭の「+」記号は、リソースが作成されることを意味します。 digitaloceanのプレフィックスが付いたリソースは、DigitalOceanで作成されるリソースです。 この特定のケースでは、TerraformはDroplet、フローティングIPを作成し、SSHキーを追加します。

警告お客様のインスタンスまたはサードパーティのサービスがオンラインである間に発生する可能性のある料金について、当社は責任を負いません。 コマンドterraform applyは、2GBのRAM(〜$ 0.03 /時間)とDigitalOceanが無料で提供するフローティングIPを備えたドロップレットを作成します。 正確な数値については、DigitalOceanのWebサイトで更新された価格を再確認してください。

次に、Terraformを実行し、ドロップレットでキャビンをスピンアップします。

  1. terraform apply

しばらくすると、Terraformが次のように出力します。

Output
Apply complete! Resources: 6 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: terraform.tfstate Expected output: web_ipv4 = 111.111.111.111

web_ipv4は、ドロップレットへのアクセスに使用できるフローティングIPアドレスです。

web_ipv4に表示される値を使用して、新しく作成されたドロップレットにログインします。

  1. ssh cabin@your_value_for_web_ipv4

コマンドを使用することもできます

  1. terraform output web_ipv4

見逃した場合は、その値に関連付けられたIPアドレスを表示します。

ログインすると、次のウェルカムメッセージが表示されます。

   _____      _     _
  / ____|    | |   (_)
 | |     __ _| |__  _ _ __
 | |    / _` | '_ \| | '_ \
 | |___| (_| | |_) | | | | |
  \_____\__,_|_.__/|_|_| |_|

Initializing Cabin. Please wait... (up 1 minute) | CTRL+C to interrupt

DigitalOceanがインスタンスをプロビジョニングし、cloud-initがキャビンに必要なパッケージをインストールするまで数分待つ必要がある場合があります。 しかし、準備ができたら、次のように表示されます。

Cabin initialized!
Check running processes...
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ api      │ 0  │ fork │ 14105 │ online │ 0       │ 36s    │ 75.898 MB   │  enabled │
│ app      │ 1  │ fork │ 14112 │ online │ 0       │ 36s    │ 34.301 MB   │  enabled │
│ www      │ 2  │ fork │ 14119 │ online │ 0       │ 36s    │ 50.414 MB   │  enabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

キャビンが起動して実行されたら、モバイルブラウザでhttp://your_value_for_web_ipv4を指定します。 キャビンはライブで、ロード画面が表示されます。 しかし、サーバー上のコードにいくつかの変更を加えるまでは、これで十分です。

ステップ5—(オプションで)キャビンの構成

キャビンアプリケーションはデプロイされていますが、まだ使用できません。 キャビンを完全に機能させるには、Facebookやその他のいくつかのサービスを構成する必要があります。

まず、インストールプロセス中に生成されたweb_ipv4アドレスにマップされるcabin.example.comなどの有効なドメイン名を使用してFacebookアプリを作成する必要があります。 DNSにレコードを追加するか、ドメインをIPアドレスにマップするエントリを/etc/hostsファイルに追加します。

Facebookアプリを作成するには、次の手順に従います。

  1. https://developers.facebook.com/docs/apps/register#step-by-step-guideにアクセスします。
  2. Facebookにログインします。
  3. [マイアプリ]で、新しいアプリの追加をクリックします。
  4. アプリケーションの名前を入力します(例: Cabin - My Example App)。
  5. 連絡先メールアドレスを入力してください。
  6. カテゴリの場合、ドロップダウンメニューを使用してアプリのカテゴリを選択します。 私たちの場合、それはライフスタイルです。
  7. アプリIDの作成ボタンをクリックします。
  8. 必要に応じて、キャプチャを完了します。
  9. appIdをコピーします。 画面上部にある数値になります。 すぐに必要になります。
  10. 左側のサイドバーからダッシュボードを選択します。
  11. Facebook SDKの使用開始という見出しの下で、プラットフォームの選択をクリックします。
  12. プラットフォームにはWebを選択します。
  13. サイトのURLフィールドを見つけて、http://cabin.example.comと入力します。
  14. 次へをクリックします。

問題が発生した場合は、このステップバイステップガイドに従うことができます。 行き詰まった場合は、Facebookでのアプリケーション設定のデバッグに関するすばらしい記事があります。これはここにあります。

appIDを入手したら、サーバーのデフォルトのappID設定を置き換える必要があります。

したがって、サーバーにログインしていることを確認してください。 そうでない場合は、次のコマンドで再度ログインしてください。

  1. ssh cabin@your_value_for_web_ipv4

ログインしたら、ファイル~/stream-react-example/app/views/index.ejsを開きます。

  1. nano ~/stream-react-example/app/views/index.ejs

デフォルトのappIdをFacebookが提供するものに変更します。

strea-react-example / app / views / index.ejs
FB.init({
    appId   : 'your_facebook_app_id',
    xfbml   : true,
    version : 'v2.6',
    status  : true,
    cookie  : true,
})

このファイルを保存して閉じます。

次に、サーバーの作成時にTerraformによって生成されたCabinのデータベースパスワードを知る必要があります。 この値を取得するには、次のコマンドを入力します。

  1. grep DB_PASSWORD processes.yml

このパスワードをコピーします。 すぐに必要になります。

ファイルenv.shは、キャビンが依存するさまざまなプロバイダーとサービスのクレデンシャルを入力する場所です。 このファイルは、これらのクレデンシャルを環境変数に配置し、アプリケーションによって読み取られます。 これは、パスワードとキーをGitに入れないようにするため、セキュリティ上の予防措置です。

env.shを開きます:

  1. nano env.sh

次のコンテンツが表示されます。

Output
export NODE_ENV=production export JWT_SECRET=ABC123 export DB_USERNAME=cabin export DB_HOST=localhost export DB_PASSWORD=VALUE export DB_PORT=3306 export MAPBOX_ACCESS_TOKEN=ADD_VALUE_HERE export S3_KEY=ADD_VALUE_HERE export S3_SECRET=ADD_VALUE_HERE export S3_BUCKET=ADD_VALUE_HERE export STREAM_APP_ID=ADD_VALUE_HERE export STREAM_KEY=ADD_VALUE_HERE export STREAM_SECRET=ADD_VALUE_HERE export ALGOLIA_APP_ID=ADD_VALUE_HERE export ALGOLIA_SEARCH_ONLY_KEY=ADD_VALUE_HERE export ALGOLIA_API_KEY=ADD_VALUE_HERE export KEEN_PROJECT_ID=ADD_VALUE_HERE export KEEN_WRITE_KEY=ADD_VALUE_HERE export KEEN_READ_KEY=ADD_VALUE_HERE export IMGIX_BASE_URL=https://react-example-app.imgix.net/uploads export API_URL=http://localhost:8000

ご覧のとおり、このファイルは、キャビンが必要とするさまざまなサービスに関する情報を保持する一連の環境変数をエクスポートします。 キャビンが本番環境で機能するためには、これらの値をすべて入力する必要があります。

これらの設定の簡単な内訳は次のとおりです。

  1. NODE_ENV :Node.jsが実行される環境。 (本番環境では速度が向上します)。
  2. JWT_SECRET :APIとWeb(アプリ)インターフェース間のJSONWebトークン認証の認証シークレット。
  3. DB_USERNAME :データベースのユーザー名。
  4. DB_HOST :データベースのホスト名。
  5. DB_PASSWORD processes.ymlを見たときに表示したデータベースのパスワード。
  6. DB_PORT :データベースポート(MySQLのデフォルトポート3306)。
  7. MAPBOX_ACCESS_TOKEN :MapBoxのアクセストークン(写真の場所をマッピングするため)。
  8. S3_KEY :画像ストレージ用のAmazonS3キー。
  9. S3_SECRET :画像ストレージ用のAmazonS3シークレット。
  10. S3_BUCKET :画像ストレージ用のAmazonS3バケット。 このバケットが存在することを確認してください。
  11. STREAM_APP_ID :ストリームアプリID。 必要なすべてのフィードグループが、このIDに関連付けられたアプリに存在することを確認してください。
  12. STREAM_KEY :ストリームAPIキー。
  13. STREAM_SECRET :ストリームアプリシークレット。
  14. ALGOLIA_APP_ID :検索用のAlgoliaアプリID。
  15. ALGOLIA_SEARCH_ONLY_KEY :アルゴリアは検索用のキーのみを検索します。
  16. ALGOLIA_API_KEY :検索用のAlgoliaAPIキー。
  17. KEEN_PROJECT_ID :鋭い追跡プロジェクトID(統計用)。
  18. KEEN_WRITE_KEY :キーントラッキング書き込みキー(統計用)。
  19. KEEN_READ_KEY :キーントラッキング読み取りキー(統計用)。
  20. IMGIX_BASE_URL :ImgixベースURL(特定のサイズで写真をレンダリングするため)。
  21. API_URL :このアプリケーションがAPIに使用するURL。 これをlocalhostからcabin.example.comなどのIPアドレスを指すドメインに変更する必要があります。

参照される環境変数とサービスの詳細については、次のブログ投稿にアクセスし、各アプリケーションが指定どおりに構成されていることを確認してください。

すべてのプロバイダーを構成したら、データベースのパスワードとプロバイダーの値をenv.shファイルに入力します。

env.shファイルを終了して保存します。 次に、ファイルをソースし、Cabinが使用する環境値に値をロードします。

  1. source ./env.sh

次に、webpackコマンドを実行する必要があります。 Webpackは、キャビンのフロントエンドコードを管理するJavaScriptビルドツールです。 Webpackは、変更したenv.shファイルによって設定された値に基づいて、JavaScriptファイルとCSSファイルを再生成します。 したがって、appディレクトリに移動します。

  1. cd app

次に、webpackコマンドを実行して、フロントエンドJavaScriptファイルを再構築します。 これにより、プロバイダートークンの一部がフロントエンドコードに挿入されます。

  1. webpack --progress --color

次の出力が表示されます。

Output
Hash: 64dcb6ef9b46a0243a8c Version: webpack 1.13.1 Time: 21130ms Asset Size Chunks Chunk Names ./public/js/app.js 2.22 MB 0 [emitted] app ./public/css/styles.css 23 kB 0 [emitted] app [0] multi app 28 bytes {0} [built] + 685 hidden modules Child extract-text-webpack-plugin: + 2 hidden modules Child extract-text-webpack-plugin: + 2 hidden modules

設定が整ったら、PM2を実行してすべてのアプリケーションプロセスをリロードし、すべてのコンポーネントが新しい設定を使用するようにします。

  1. pm2 restart all
Output
[PM2] Applying action restartProcessId on app [all](ids: 0,1,2) [PM2] [api](0) ✓ [PM2] [app](1) ✓ [PM2] [www](2) ✓ ┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │ ├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤ │ api │ 0 │ fork │ 30834 │ online │ 516 │ 0s │ 39.027 MB │ enabled │ │ app │ 1 │ fork │ 30859 │ online │ 9 │ 0s │ 22.504 MB │ enabled │ │ www │ 2 │ fork │ 30880 │ online │ 9 │ 0s │ 19.746 MB │ enabled │ └──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘

それでおしまい! これで、リモートサーバーからログアウトできます。

  1. exit

最後に、ブラウザでhttp://your_value_for_web_ipv4に再度アクセスして、サイトを表示します。 これにより、Facebookにログインするためのリンクを含むカバー画像が表示されます。 ログインすると、後でアプリを探索できるようになります。

PM2はキャビンのプロセスを管理し、問題のデバッグに役立つ優れたツールになる可能性があります。 pm2 listを使用してアプリケーションのコンポーネントのステータスを確認したり、pm2 logsを使用してアプリのログのストリームを表示したりできます。これは、構成エラーの診断に役立ちます。

次に、この展開を可能にしたTerraform構成について詳しく見ていきましょう。

ステップ6—構成タイルを調べる

では、これはどのように機能しますか? ローカルマシンに複製したリポジトリ内のファイルを見てみましょう。 このセクションで変更することは何もありませんが、ピースがどのように組み合わされているかを感じることができるように、自分のマシンをフォローする必要があります。

Terraformプロジェクトは、アプリケーションをクリーンで理解しやすい状態に保つために、複数のファイルとディレクトリに分割されています。 すべてのDigitalOceanファイルをリポジトリのterraform/doディレクトリ内に配置しました。このディレクトリの構造は次のとおりです。

terraformフォルダー
do
└── cabin
    ├── files
    │   ├── cabin-web-nginx.conf
    │   └── cabin_mysql_init.sh
    ├── main.tf
    ├── outputs.tf
    ├── templates
    │   ├── processes.tpl
    │   └── web.tpl
    └── variables.tf

main.tfから始まる上記のファイルを見てみましょう。 お気に入りのテキストエディタで開きます。

最初に行うことは、使用するクラウドプロバイダーをTerraformに通知することです。

main.tf
provider "DigitalOcean" {
  token = "${var.token}"
}

DigitalOceanプロバイダーの定義はそれと同じくらい簡単です。 サポートされているプロバイダーの完全なリストは、Terraformのドキュメントにあります。

可変構成

Terraformを使用すると、変数を定義できます。つまり、デプロイメントのデフォルトを設定できます。 そうすれば、毎回詳細を入力したり、構成全体で値をハードコーディングしたりする必要がなくなります。 DigitalOceanにデプロイするための変数を設定する方法を見てみましょう。

キャビンアプリケーションの実行に必要な変数を定義した場所であるvariables.tfをご覧ください。

変数.tf
variable "token" {
  description = "DO Token"
}

variable "region" {
  description = "DO Region"
}

Terraform内で変数がどのように処理されるかをよりよく理解するために、上記の例を見てみましょう。

領域変数には、デフォルト値を指定しました。 デフォルト値を指定しない場合、次の例に示すように、Terraformはデフォルト値の入力を求めます。

Output
terraform plan var.token DO Token Enter a value:

terraform applyを実行するときに変数を指定することもできます。 たとえば、別のリージョンを指定する場合は、var引数を指定してTerraformを実行できます。

  1. terraform -var 'region=ams3' apply

これにより、構成された設定が上書きされます。

ドロップレットの設定

main.tfでは、DigitalOceanにドロップレットをプロビジョニングするようにTerraformに指示します。 デフォルトでは、次の特性を持つサーバーをデプロイします。

main.tf
resource "digitalocean_droplet" "cabin-web" {
  image = "ubuntu-14-04-x64"
  name = "cabin-web"
  region = "${var.region}"
  size = "2gb"
  ssh_keys = [ "${digitalocean_ssh_key.cabin-ssh-key.id}" ]
  user_data = "${template_file.userdata_web.rendered}"
}

Cabin-web と呼ばれる2GBのRAMを備えた新しいDigitalOceanドロップレットを作成し、イメージubuntu-14-04-x64を使用しています。 上記のリソース定義を見ると、サーバーのイメージとサイズを簡単に変更できることがわかります。

ユーザーデータとクラウド-Init

さて、user-dataとは正確には何ですか? これは、起動時にコマンドと命令をクラウドインスタンスに送信する最も簡単な方法です。 cloud-initと組み合わせると、ChefPuppetなどの不要なサードパーティアプリケーションを利用せずにインスタンスを構成するための強力な方法になります。

cloud-initプログラムは、多くのLinuxディストリビューションに組み込まれています。 これには、ユーザーの追加、グループの管理、ファイルの作成、root権限でのスクリプトまたはシェルコマンドの実行などの簡単なタスクを実行できる小さな一連の命令が含まれています。

user_data属性について詳しく見ていきましょう。そうすれば、それが何であるかをよりよく理解できます。

main.tf
resource "digitalocean_droplet" "cabin-web" {
  ...
  user_data = "${template_file.userdata_web.rendered}"
}

私たちの目標は、キャビンを稼働させた状態で新しいドロップレットを開始し、cloud-initに重い物を持ち上げてもらうことです。 user_dataフィールドはテンプレートファイルを指し、変数を使用してmain.tfの別の宣言を指します。

main.tf
resource "template_file" "userdata_web" {
  template = "${file("${path.module}/templates/web.tpl")}"

  vars {
    userdata_sshkey = "${var.sshkey}"
    userdata_nginx_conf = "${base64encode(file("${path.module}/files/cabin-web-nginx.conf"))}"
    userdata_mysql_init = "${base64encode(file("${path.module}/files/cabin_mysql_init.sh"))}"
    userdata_pm2_conf = "${base64encode("${template_file.pm2_processes_conf.rendered}")}"
    userdata_env = "${base64encode("${template_file.env.rendered}")}"
    userdata_motd = "${base64encode(file("${path.module}/files/motd"))}"
    userdata_motd_script = "${base64encode(file("${path.module}/files/motd.sh"))}"
    userdata_giturl = "${var.git_url}"
    userdata_index = "${base64encode(file("${path.module}/files/index.html"))}"
  }
}

Terraformは、テキストを変換できる関数を提供します。 この機能を使用して、ファイルを読み取り、コンテンツをBase64でエンコードされた文字列に変換して、API呼び出しを介して値を転送できるようにすることで、値をテンプレートに挿入できます。

この特定のセクションでは、サーバーで実行するすべての設定とコマンドを含むテンプレートtemplates/web.tplのデータを準備します。

web.tplファイルを調べて、その機能を見てみましょう。

最初の部分では、初期ユーザーを設定し、rootアクセスを無効にします。

テンプレート/web.tpl
#cloud-config
users:
  - name: cabin
    groups: sudo
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    shell: /bin/bash
    home: /home/cabin
    lock_passwd: true
    ssh-authorized-keys:
      - ${userdata_sshkey}

disable_root: true

web.tplの最初のステートメントは、#cloud-configである必要があります。 これを追加するのを忘れた場合、cloud-initは構成を取得せず、指定されたコマンドはターゲットインスタンスで実行されません。

このセクションのコマンドは次のことを行います。

  • スーパーユーザーになるための許可を得て、cabinユーザーをシステムに追加します
  • lock-passwd: trueはパスワード認証を拒否するため、cabinユーザーはサーバーにアクセスするためにSSHキー認証を使用する必要があります。
  • ssh-authorized-keysは、ユーザーのssh-keyをauthorized_keysファイルにインストールします。
  • disable_root: trueは、rootとしてのSSHアクセスを無効にするために使用されます

${userdata_sshkey}は、main.tfでテンプレートを呼び出したときに設定された変数であることに注意してください。

次に、MySQL、Nginx、Git、およびアプリケーションに必要なその他のパッケージをインストールします。

package_update: true
packages:
 - mysql-server-5.6
 - libmysqlclient-dev
 - iptables-persistent
 - git
 - nginx
 - npm
 - pwgen

cloud-initを使用してパッケージをインストールする最も簡単な方法は、Packageモジュールを利用して特定のパッケージのリストをインストールすることです。 このモジュールは、配布にデフォルトのパッケージマネージャーを使用します。 Ubuntuを使用しているため、このプロセスではaptを含むパッケージがインストールされます。

次に、テンプレートに渡したデータをファイルコンテンツとして使用して、いくつかのファイルをファイルシステムに書き込みます。

write_files:
 - encoding: b64
   content: ${userdata_nginx_conf}
   path: /tmp/cabin-web.conf
 - encoding: b64
   content: ${userdata_pm2_conf}
   path: /tmp/processes.yml
 - encoding: b64
   content: ${userdata_mysql_init}
   path: /tmp/cabin_mysql_init.sh
   permissions: '0554'

このセクションでは、write_fileモジュールを利用してファイルを作成します。 上記の例では、次のファイルを作成しています。

  • cabin-web.confにはNGINX構成が含まれています。
  • processes.ymlPM2がNode.jsプロセスを処理するために使用します。
  • cabin_mysql_init.shは、MySQLデータベースを初期化するために使用されるカスタムスクリプトです。

データをテンプレートに渡すときに、Base64としてエンコードしたことを思い出してください。 内容をデコードできるように、ファイルを書き込むときにエンコードを指定します。

次のセクションでは、runcmdモジュールを使用していくつかのシェルコマンドを実行し、iptablesを使用してファイアウォールルールを作成します。

runcmd:
 - iptables -A INPUT -i lo -j ACCEPT
 - iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
 - iptables -A INPUT -p tcp --dport ssh -j ACCEPT
 - iptables -A INPUT -p tcp --dport 80 -j ACCEPT
 - iptables -A INPUT -p tcp --dport 8000 -j ACCEPT
 - iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
 - iptables -A INPUT -j DROP
 - iptables -A OUTPUT -j ACCEPT
 - invoke-rc.d iptables-persistent save
…

次に、コードはiptables-persistentを使用して、インスタンスが再起動した場合にファイアウォール構成を使用できるようにします。

ファイアウォールルールが設定された後、キャビンを設定および開始するための残りのコマンドが実行されます。

 - apt-get update --fix-missing
 - curl -sL https://deb.nodesource.com/setup_5.x | bash && apt-get install -y nodejs
 - npm install pm2 webpack -g
 - cd /home/cabin && sudo -u cabin git clone ${userdata_giturl}
 - mv /tmp/env.sh /home/cabin/stream-react-example/env.sh
 - cd /home/cabin/stream-react-example/api && sudo -u cabin npm install
 - cd /home/cabin/stream-react-example/app && sudo -u cabin npm install
 - cd /home/cabin/stream-react-example/www && sudo -u cabin npm install
 - chown cabin.cabin /home/cabin/stream-react-example/env.sh && /home/cabin/stream-react-example/env.sh
 - mv /tmp/processes.yml /home/cabin/stream-react-example/processes.yml
 - chown cabin.cabin /home/cabin/stream-react-example/processes.yml
 - /tmp/cabin_mysql_init.sh
 - cd /home/cabin/stream-react-example && sudo -u cabin pm2 start processes.yml
 - mv /tmp/cabin-web.conf /etc/nginx/sites-available/cabin-web
 - rm /etc/nginx/sites-enabled/default
 - ln -s /etc/nginx/sites-available/cabin-web /etc/nginx/sites-enabled
 - service nginx reload

これらのコマンドはすべてroot権限で実行され、は最初の起動でのみ発生します。 本機を再起動すると、runcmdは再度実行されません。

Terraformについて詳しく学んだので、インフラストラクチャのライフサイクルを処理する方法を見ていきましょう。

ステップ7—スタックのライフサイクルを管理する

Terraformを使用すると、スタックの状態を保存し、スタックを更新し、破棄し、コード変更をデプロイすることができます。

terraform applyを実行すると、terraform.tfstateというファイルがcabinディレクトリに作成されることに気付いたかもしれません。

このファイルには、DigitalOceanで作成された実際のリソースへの参照が含まれているため、非常に重要です。 基本的に、このファイルはTerraformに管理するリソースの識別子を通知します。

terraform applyを再度実行すると、Terraformは最初からやり直して、作成したものをすべて消去しません。 代わりに、まだ完了していない部分のみを実行します。 したがって、ネットワークの問題またはAPIの問題が原因でプロセスが途中で失敗した場合は、問題に対処してコマンドを再実行できます。 Terraformは、中断したところから再開します。

ドロップレット構成の変更

terraform applyを使用して、ドロップレットの構成を変更することもできます。 たとえば、より多くのトラフィックに対応するためにデータセンターやリージョンを変更したり、Dropletが使用するメモリを増やしたりする必要がある場合、Terraformを使用すると両方のタスクが非常に簡単になります。

terraform applyコマンドを実行し、regionおよびdroplet_size変数をオーバーライドすることで、ドロップレット領域を調整できます。 これにより、Terraformは、既存のドロップレットを破棄する必要があり、要件を満たすために新しいドロップレットをプロビジョニングする必要があることを認識できます。

警告Terraformは既存のドロップレットを破棄します。 アプリケーションと同じサーバーでMySQLデータベースを実行している場合、これによりMySQLデータも破棄されます。。 これを回避するには、この手順の前にデータベースエクスポートを実行するか、専用のDropletでMySQLデータベースを実行することをお勧めします。

ドロップレットを保持するリージョンまたはデータセンターを変更する場合は、次のコマンドを実行します。

  1. terraform apply -var "region=sfo2"

また、ユーザーベースが拡大するにつれて、追加のトラフィックに対応するためにドロップレットサイズを変更する必要が生じる可能性があります。 次のようにdroplet_size変数を使用してこれを行うことができます。

  1. terraform apply -var "droplet_size=4gb"

ドロップレットが削除されて新しいドロップレットに置き換えられ、アプリケーションが再デプロイされて構成されます。

スタックを破壊する

Terraformの驚くべき点の1つは、スタックのライフサイクル全体を処理することです。 1つの単純なTerraformコマンド(破棄)を実行することで、作成したものを簡単に破棄できます。

  1. terraform destroy

次に、Terraformは、実際にすべてのリソースを破棄することを確認するように求めるプロンプトを表示します。

Output
Do you really want to destroy? Terraform will delete all your managed infrastructure. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes

Terraformが完了すると、最終的な出力は次のようになります。

Output
digitalocean_droplet.cabin-web: Destroying... digitalocean_droplet.cabin-web: Still destroying... (10s elapsed) digitalocean_droplet.cabin-web: Destruction complete digitalocean_ssh_key.cabin-ssh-key: Destroying... template_file.userdata_web: Destroying... template_file.userdata_web: Destruction complete template_file.pm2_processes_conf: Destroying... template_file.pm2_processes_conf: Destruction complete digitalocean_ssh_key.cabin-ssh-key: Destruction complete Apply complete! Resources: 0 added, 0 changed, 5 destroyed.

ご覧のとおり、すべてのリソースが破壊されました。

新しいバージョンのコードのデプロイ

コードベースに変更を加えた場合、ダウンタイムをほとんどまたはまったく発生させずに、変更をサーバーに反映させる必要があります。 サーバーにPM2がインストールされており、これで手間のかかる作業を処理できます。

PM2は、アプリケーションのファイルシステムの変更をリッスンします。 新しいバージョンのコードを実行するには、DropletにSSHで接続し、アプリケーションを含むディレクトリでgit pullコマンドを発行します。 これにより、リポジトリからプルするようにサーバーに指示されます。 ファイルが変更されると、PMZはノードプロセスを自動的に再開します。

たとえば、新しいバージョンのCabinがあり、最新バージョンのコードをサーバーにデプロイする場合は、サーバーにログインします。

ssh cabin@your_value_for_web_ipv4

次に、サーバーで、キャビンアプリケーションを含むフォルダーに移動します。

  1. cd ~/stream-react-example

そして最後に最新バージョンをプルダウンします。

  1. git pull

新しいコードが配置されると、アプリは自動的に再起動し、訪問者には最新バージョンが表示されます。 何らかの理由でPM2が変更をキャッチしない場合は、手動で再起動します。

  1. pm2 restart all

すべてのコンポーネントが再起動します。

結論

DigitalOcean、Terraform、Cloud-init、およびPM2を使用して、キャビンの本番環境を正常にセットアップしました。

Terraformを使用する場合、すべてのインフラストラクチャがコードとして保存されます。 これにより、チームは変更を追跡して共同作業を行うことが容易になります。 また、比較的簡単に大規模なインフラストラクチャの変更を行うことができます。