6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Terraform for さくらのクラウド スタートガイド(第4回)

Last updated at Posted at 2016-05-26

2017/05追記

当記事の情報はすでに古くなっています。
新しい記事を投稿していますのでこれからTerraform for さくらのクラウドを利用される方はこちらを参照ください。

Terraform for さくらのクラウド スタートガイド(全5回)

こちらの記事は一応残しておきます。

連載目次

第1回:導入編

  • 概要
  • セットアップ
  • 実践 Step1:サーバー1台構成

第2回:実践編

  • 実践 Step2:構成/設定の変更
    • リソースの追加
    • リソースの変更
    • count構文
    • output機能

第3回:実践編2

  • 実践 Step3:プロビジョニング
    • プロビジョニング接続設定
    • fileプロビジョニング
    • remote-execプロビジョニング
    • さくらのクラウドDNSリソースの利用

第4回:応用編(当記事)

  • 実践 Step4:Web/DB 2-Tier構成
    • MySQLの利用
    • スイッチによるプライベートネットワークの構築
    • パケットフィルタ/シンプル管理:Slack通知の利用

第5回:応用編2

  • 実践 Step5:東京/石狩 マルチゾーン構成
    • 東京と石狩でマルチゾーン構成
    • GSLBによるゾーン間HA構成
    • MySQL レプリケーション + PHP(mysqlnd_ms)によるDBのクラスタリング
    • null_resourcetemplate_fileなどの特殊なリソース
    • tfファイルのリファクタリングとモジュール化

連載第4回です。
第1回から順番にお読みください。

第4回サンプルコード / 第3回との差分表示


実践 Step4:Web/DB 2-Tier構成

いよいよ応用編になります。
Terraformの基本的な操作を駆使して実運用に耐えるインフラを構築していきましょう。

前回(第3回)は以下のような構成でした。

servers03.png

2台のWebサーバに加えてDNSへの登録も行っていましたね。
現時点でも外部からの接続ができる状態なのですが、
今回は本番運用意識して構成を変更していきます。

Web層/DB層の2階層(2-tier)化

スイッチを追加して、DBを配置するプライベートなネットワークを構築します。
以下のリソースを追加します。

  • プライベートネットワーク用にスイッチ(sw01)を追加
  • プライベートネットワーク内にDBサーバー(server_db + disk_db)を追加

DBサーバーではMySQLを稼働させます。

非機能要件対応

本番運用時にはセキュリティ対応や監視/測定、バックアップなどの非機能要件も満たす必要があります。
今回はバックアップは手動、監視は通知だけ来るようにしておき復旧は手動で行うものとして、
以下の対応だけ行います。

  • パケットフィルタを追加してセキュリティ対策
  • シンプル監視で監視 + Slackへ通知

今回の最終的な構成

server04.png

なんだか一気にリソースが追加されて複雑になってきましたね。

でも大丈夫です。
Terraformを使うのであれば、トライ&エラーで定義ファイル(tfファイル)を
:star:育てて:star: いけばいいんです。
tfファイルを作ってテストしておけばその環境の再現は簡単なんですからね。

では早速実践していきましょう。

実践

第1段階:DBサーバー追加

まずはDBサーバーを追加してみましょう。
この段階では以下のような構成になります。

server04_part1.png

DBサーバーとWebサーバーとではセキュリティ要件が異なるため、
SSH公開鍵はWeb層(server01と02)とは別のものを使うようにしておきます。
追加するリソースは以下の通りです。

  • サーバー(server_db)
  • ディスク(disk_db)
  • SSH公開鍵(dbkey)

加えて、DBサーバーのプロビジョニングも行っておきます。
さくらのクラウドのパブリックアーカイブからCentOS7.2をインストールした場合、
mysql communityのyumリポジトリがすでに登録されており、インストールが容易なため
MySQL5.6をインストールしていきます。

すでに前回までに出てきたテクニックだけで対応できますね!

準備

DBサーバー用にキーペアを作成しましょう。

SSH用キーペア作成
$ ssh-keygen -C "" -f ./id_rsa_db
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):  #何も入力せずEnter
Enter same passphrase again:                 #何も入力せずEnter

カレントディレクトリにid_rsa_dbid_rsa_db.pubが作成されているはずです。

定義ファイル(tfファイル)編集

それではtfファイルを編集しましょう。前回までのtfファイルは以下の通りです。

sakura.tf(前回までのtfファイル全体)
provider "sakuracloud" {
    token = "[ACCESS_TOKEN]"
    secret = "[ACCESS_TOKEN_SECRET]"
}

resource "sakuracloud_disk" "disk" {
    name = "${format("disk%02d" , count.index+1)}"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
    count = 2
}

resource "sakuracloud_server" "server" {
    name = "${format("server%02d" , count.index+1)}"
    disks = ["${element(sakuracloud_disk.disk.*.id,count.index)}"]
    count = 2
    # 1: サーバーにはSSHで接続
    connection {
       user = "root"
       host = "${self.base_nw_ipaddress}"
       private_key = "${file("./id_rsa")}"
    }

    # 2: yumでapache+PHPのインストール
        provisioner "remote-exec" {
        inline = [
          "yum install -y httpd httpd-devel php php-mbstring",
          "chkconfig httpd on"
        ]
   }

   # 3: Webコンテンツをアップロード
   provisioner "file" {
        source = "webapps/"
        destination = "/var/www/html"
   }

}

resource "sakuracloud_ssh_key" "mykey" {
    name = "mykey"
    public_key = "${file("./id_rsa.pub")}"
}

resource "sakuracloud_dns" "dns" {
    zone = "fe-bc.net"
    records = {
        name = "web"
        type = "A"
        value = "${sakuracloud_server.server.0.base_nw_ipaddress}"
    }
    records = {
        name = "web"
        type = "A"
        value = "${sakuracloud_server.server.1.base_nw_ipaddress}"
    }
}

output "global_ip" {
    value = "${join("\n" , formatlist("%s : %s" , sakuracloud_server.server.*.name , sakuracloud_server.server.*.base_nw_ipaddress))}"
}

ここに今回のリソースを足していきましょう。
以下を追記します。MySQLのパスワードは任意のものを設定してください。

sakura.tf(追加分)

variable "mysql_values" {
   default = {
      # MySQLのrootユーザーのパスワード
      root_password = "mysql_password"
      # MySQLデモアプリ用ユーザーの名前
      user_name = "demo"
      # MySQLデモアプリ用ユーザーのパスワード
      user_password = "demo_password"
   }
}

resource "sakuracloud_ssh_key" "dbkey" {
    name = "dbkey"
    public_key = "${file("./id_rsa_db.pub")}"
}

resource "sakuracloud_disk" "disk_db" {
    name = "disk_db"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.dbkey.id}"]
    disable_pw_auth = true
}

resource "sakuracloud_server" "server_db" {
    name = "server_db"
    disks = ["${sakuracloud_disk.disk_db.id}"]
    # サーバーにはSSHで接続
    connection {
       user = "root"
       host = "${self.base_nw_ipaddress}"
       private_key = "${file("./id_rsa_db")}"
    }

    provisioner "remote-exec" {
        inline = [
          "yum install -y mysql-community-server",
          "systemctl start mysql.service",
          "mysql -uroot -e 'GRANT ALL ON *.* TO ${var.mysql_values.user_name}@\"192.168.2.%\" IDENTIFIED BY \"${var.mysql_values.user_password}\"'" ,
          "mysqladmin -u root password '${var.mysql_values.root_password}'",
          "systemctl stop firewalld.service",
          "systemctl disable firewalld.service"
        ]
    }
}

ポイント:variable

variableという記述が新たに出てきましたね。
これはtfファイル内で利用出来る変数を定義するものです。
variableで定義しておいた値は、${var.変数名}で参照できます。
また、variableはコマンドラインから与えたり、別途設定ファイルを用意しておいて
与えることもできます。(tfvarファイル)
variableについての詳細な説明はTerraformドキュメントを参照ください。

ここではMySQLのインストール、MySQLユーザーの追加、MySQLのrootパスワードの設定までを行っています。

注意
今回は設定を簡単にするためにmysqlのrootユーザーのパスワードをmysqladminコマンドで
実行するだけの初期化としていますが、実際に運用する際はmysql_secure_instration相当の処理を行うなど、十分にセキュリティに配慮ください。

outputの追加

このままではoutputにDBサーバーのグローバルIPが表示されませんので、
DBサーバーのグローバルIP表示用にoutputを定義しましょう。
ついでにSSH接続補助もoutput定義しておきます。

以下を追記してください。

sakura.tf(SSH接続補助output追記)
output "global_ip_db" {
    value = "${format("%s : %s\n" , sakuracloud_server.server_db.name , sakuracloud_server.server_db.base_nw_ipaddress)}"
}
output "ssh_web01" {
    value = "${format("ssh root@%s -i %s/id_rsa" , sakuracloud_server.server.0.base_nw_ipaddress , path.root)}"
}
output "ssh_web02" {
    value = "${format("ssh root@%s -i %s/id_rsa" , sakuracloud_server.server.1.base_nw_ipaddress , path.root)}"
}
output "ssh_db" {
    value = "${format("ssh root@%s -i %s/id_rsa_db" , sakuracloud_server.server_db.base_nw_ipaddress , path.root)}"
}

これでterraform output ssh_web01と実行すればsshコマンドが出力されます。
$(terraform output ssh_web01)などとすれば実行できますよ!

以後作成したサーバーにSSHする際は以下コマンドでOKです。

  • Webサーバ(server01) : $(terraform output ssh_web01)
  • Webサーバ(server02) : $(terraform output ssh_web02)
  • DBサーバ(server_db) : $(terraform output ssh_db)

planapplyの実行

さて、いつも通りterraform planしてからterraform applyしましょう。
DBサーバーが追加されましたか?
SSH接続するなどで動作確認もしておきましょう。

第2段階:スイッチ追加でプライベートネットワークと接続

では続いてスイッチを追加しましょう。
こんな感じになります。

server04_part2.png

やることは

  • スイッチ用リソース(sakuracloud_switch)の追加
  • スイッチとサーバーの接続
  • プライベートIPの割り振り(プロビジョニングで実施)

です。
また、WebサーバーとDBサーバー間をプライベートネットワークで接続できるようになりますので、
デモ用のWebアプリ(PHP)もDBを使うように変更していきます。

順番にいきましょう。

スイッチ用リソースの定義

いつも通りtfファイルに追記しましょう。
以下を追記してください。

sakura.tf(スイッチ追加)
resource "sakuracloud_switch" "sw01"{
    name = "sw01"
}

えっ?これだけ?
はい。スイッチは設定項目が少ないのです、、

スイッチとサーバーの接続

サーバーの定義部分を修正しましょう。

sakura.tf(サーバーとスイッチの接続)
# Webサーバー
resource "sakuracloud_server" "server" {
    #(中略) 

    # スイッチと接続するインターフェースを定義
    additional_interfaces = ["${sakuracloud_switch.sw01.id}"]
 
   # 以下プロビジョニング(省略)
}
# DBサーバー
resource "sakuracloud_server" "server_db" {
    #(中略) 

    # スイッチと接続するインターフェースを定義
    additional_interfaces = ["${sakuracloud_switch.sw01.id}"]
 
   # 以下プロビジョニング(省略)
}

プライベートIPの割り当て(プロビジョニング)

これで各サーバーに先ほど追加したスイッチに接続した状態のNICが追加されています。(eth1)
eth1にIPアドレスを設定するようにプロビジョニングしましょう。

まずは手元のマシンで以下のスクリプトを作成しておきます。

IP設定用スクリプト作成
$ vi provision_private_ip.sh
provision_private_ip.sh
#!/bin/sh

# eth1のIP設定
cat << EOS >> /etc/sysconfig/network-scripts/ifcfg-eth1
BOOTPROTO=static
PREFIX0=24
DEVICE=eth1
IPADDR0=$1
ONBOOT=yes
EOS

#反映
ifdown eth1; ifup eth1

引数で受け取ったIPアドレスをeth1に設定するスクリプトです。
このスクリプトをTerraformのプロビジョニング機能でアップロード&実行することでIP設定します。

ではtfファイルに記載しましょう。

sakura.tf(プライベートIP割り振り)

variable "private_ip_addresses" {
    default {
      server01 : "192.168.2.101"
      server02 : "192.168.2.102"
      server_db : "192.168.2.201"
    }
}

# Webサーバー
resource "sakuracloud_server" "server" {
    #(中略) 
        
    # プロビジョニング定義に以下を追記
    # IP設定スクリプトをアップロード
    provisioner "file" {
        source = "provision_private_ip.sh"
        destination = "/tmp/provision_private_ip.sh"
    }

    # IP設定実行
    provisioner "remote-exec" {
        inline = [
          "chmod +x /tmp/provision_private_ip.sh",
          "/tmp/provision_private_ip.sh ${lookup(var.private_ip_addresses , self.name)}"
        ]
    }
}

# DBサーバー
resource "sakuracloud_server" "server" {
    #(中略) 
        
    # プロビジョニング定義に以下を追記
    # IP設定スクリプトをアップロード
    provisioner "file" {
        source = "provision_private_ip.sh"
        destination = "/tmp/provision_private_ip.sh"
    }

    # IP設定実行
    provisioner "remote-exec" {
        inline = [
          "chmod +x /tmp/provision_private_ip.sh",
          "/tmp/provision_private_ip.sh ${lookup(var.private_ip_addresses , self.name)}"
        ]
    }
}

ポイント:lookup

ここでは、サーバー名とプライベートIPの対応関係をvariableとして定義しておき、
${lookup}でサーバー名をキーとして持つ値を参照しています。
tfファイルの記述をスッキリさせるのに有効なテクニックです。

Webアプリの修正

デモ用のWebアプリをDBに繋ぐように修正してみましょう。
今回はデモ目的ですので単純にDBに接続して値が取れていれば十分です。
よって、index.phpにDB接続処理を直接書いちゃいます。
webapps/index.phpを以下のように変更しましょう。

MySQLのユーザー名/パスワードを変更している場合は
new mysqliの引数部分も修正してください

webapps/index.php
<?php
    date_default_timezone_set('Asia/Tokyo');
    echo "Terraform for さくらのクラウド スタートガイド用デモ". "<br /><br />";
    echo "IPアドレス:" . $_SERVER[ 'SERVER_ADDR' ] . "<br />";
    echo "時刻:" . date("Y/m/d H:i:s"). "<br />";
   
    //DB接続してSQLで時刻取得
    $mysqli = new mysqli('192.168.2.201', 'demo', 'demo_password');
    if ($mysqli->connect_error) {
        echo "fail on connect:".$mysqli->connect_error;
        exit();
    } 
    $mysqli->set_charset("utf8");
    $res = $mysqli->query("SELECT sysdate()");
    echo "DB時刻:" . $res->fetch_row()[0] . "<br />";
    $res->close();
    $mysqli->close();    

WebサーバーにPHP-MySQL用のライブラリも追加しておきましょう。
プロビジョニング部分を変更します。

sakura.tf(Webサーバのプロビジョニング)
resource "sakuracloud_server" "server" {
#中略
    provisioner "remote-exec" {
        inline = [
          # php-mysqlndを追記
          "yum install -y httpd httpd-devel php php-mbstring php-mysqlnd",
          "systemctl restart httpd.service",
          "systemctl enable httpd.service",
          "systemctl stop firewalld.service",
          "systemctl disable firewalld.service"
        ]
    }
#中略

planapplyの実行

今回は実行前にterraform destroyしてから
terraform planterraform applyしましょう。

うまく反映されましたね?
SSH接続した上でip addr showpingを試しておくのも良いでしょう。

この段階でブラウザでWebサーバーにアクセスすると、以下のような表示になるはずです。
server04_webtest.png

第3段階:パケットフィルタ、シンプル監視の追加

いよいよ最終形です。

server04_part3.png

  • パケットフィルタの追加
  • パケットフィルタをサーバーに適用
  • シンプル監視の追加

を行いましょう。

パケットフィルタの追加

パケットフィルタを合計2種類定義します。

  • 1: Webサーバー用(pf_web)
  • 2: DBサーバー用(pf_db)

基本的なルールは

  • ICMP応答は許可
  • SSH接続は許可
  • 応答パケットは許可
  • 内部から外部へのパケットは許可
  • 以外はすべて拒否

とし、Webサーバー用については80番、443番ポートを許可します。

これをそれぞれのサーバーのeth0(グローバルIPを持っているNIC)に適用します。
DB接続についてはプライベートネットワーク(192.168.2.0/24)経由でのアクセスのため、
このパケットフィルタの適用対象外となり、サーバー間ではきちんと接続できます。

tfファイルは以下の通りです。

パケットフィルタ定義
resource "sakuracloud_packet_filter" "pf_web" {
    name = "pf_web"
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "22"
        description = "Allow SSH"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "80"
        description = "Allow www"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "443"
        description = "Allow www(ssl)"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(tcp)"
        allow = true
    }
    expressions = {
        protocol = "udp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(udp)"
        allow = true
    }
    expressions = {
        protocol = "icmp"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all icmp"
    }
    expressions = {
        protocol = "fragment"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all fragment"
    }
    expressions = {
        protocol = "ip"
        source_nw = "0.0.0.0"
        allow = false
        description = "Deny all"
    }
}

resource "sakuracloud_packet_filter" "pf_db" {
    name = "pf_db"
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "22"
        description = "Allow SSH"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(tcp)"
        allow = true
    }
    expressions = {
        protocol = "udp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(udp)"
        allow = true
    }
    expressions = {
        protocol = "icmp"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all icmp"
    }
    expressions = {
        protocol = "fragment"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all fragment"
    }
    expressions = {
        protocol = "ip"
        source_nw = "0.0.0.0"
        allow = false
        description = "Deny all"
    }
}

パケットフィルタをサーバーに接続

tfファイルのサーバー部分を修正しましょう。

sakura.tf(パケットフィルタ接続)
# webサーバー
resource "sakuracloud_server" "server" {
    # (中略)

    # パケットフィルタ接続
    packet_filter_ids = ["${sakuracloud_packet_filter.pf_web.id}"]
 
   # 以下プロビジョニング(省略)
}

# DBサーバー
resource "sakuracloud_server" "server_db" {
    # (中略)

    # パケットフィルタ接続
    packet_filter_ids = ["${sakuracloud_packet_filter.pf_db.id}"]
 
   # 以下プロビジョニング(省略)
}

シンプル監視の追加

以下のような監視を行います。

  • pingによる死活監視(1分間隔)
  • WebサーバーへのHTTP監視(/index.phpへリクエスト、1分間隔)
  • 通知はslackに対して行う

事前にslack連携用にwebhookのURLが必要です。
別途slackの設定を行っておいてください。

slack_webhook.png

tfファイルは以下の通りです。webhookのURLは各自置き換えてください。

sakura.tf(シンプル監視)
variable "slack_webhook" {
    default = "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX"
}

# ping監視(DBサーバー)
resource "sakuracloud_simple_monitor" "ping_monitor_db" {
    target = "${sakuracloud_server.server_db.base_nw_ipaddress}"
    health_check = {
        protocol = "ping"
        delay_loop = 60
    }
    notify_email_enabled = false
    notify_slack_enabled = true
    notify_slack_webhook = "${var.slack_webhook}"
}

# ping監視(webサーバー2台分)
resource "sakuracloud_simple_monitor" "ping_monitor_web" {
    count = 2
    target = "${element(sakuracloud_server.server.*.base_nw_ipaddress , count.index)}"
    health_check = {
        protocol = "ping"
        delay_loop = 60
    }
    notify_email_enabled = false
    notify_slack_enabled = true
    notify_slack_webhook = "${var.slack_webhook}"
}
# web監視(webサーバー2台分)
resource "sakuracloud_simple_monitor" "http_monitor_web" {
    count = 2
    target = "${element(sakuracloud_server.server.*.base_nw_ipaddress , count.index)}"
    health_check = {
        protocol = "http"
        delay_loop = 60
        path = "/index.php"
        status = "200"
    }
    notify_email_enabled = false
    notify_slack_enabled = true
    notify_slack_webhook = "${var.slack_webhook}"
}

planapplyの実行

いよいよですね!早速実行してみましょう。
うまくいきましたよね?

監視ができているか試しにサーバーを落としてみたりしてください。
slackに通知がきちんと届けばOKです。

最終的な定義ファイル(tfファイル)

今回の最終的なtfファイルは以下のようになっているはずです。
(コメントなど入れて整形しています)
うまくいかない方はこちらと比較してみてください。

sakura.tf(第4回の最終形)
/*********************
 * Provider settings
 *********************/
provider "sakuracloud" {
    token = "[ACCESS_TOKEN]"
    secret = "[ACCESS_TOKEN_SECRET]"
}

/*****************
 * Variables 
 *****************/
variable "slack_webhook" {
    default = "https://hooks.slack.com/services/X0XXX0XXX/X0XXXXXXX/9XXXOxxxxO8XxxX4XX1xxxxx"
}
variable "mysql_values" {
   default = {
      root_password = "mysql_password"
      user_name = "demo"
      user_password = "demo_password"
   }
}
variable "private_ip_addresses" {
    default = {
      server01 = "192.168.2.101"
      server02 = "192.168.2.102"
      server_db = "192.168.2.201"
    }
}

/*****************
 * Disk
 *****************/
resource "sakuracloud_disk" "disk" {
    name = "${format("disk%02d" , count.index+1)}"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
    count = 2
}
resource "sakuracloud_disk" "disk_db" {
    name = "disk_db"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.dbkey.id}"]
    disable_pw_auth = true
}

/*****************
 * Server 
 *****************/
resource "sakuracloud_server" "server" {
    name = "${format("server%02d" , count.index+1)}"
    disks = ["${element(sakuracloud_disk.disk.*.id,count.index)}"]
    additional_interfaces = ["${sakuracloud_switch.sw01.id}"]
    packet_filter_ids = ["${sakuracloud_packet_filter.pf_web.id}"]
    count = 2
    # サーバーにはSSHで接続
    connection {
       user = "root"
       host = "${self.base_nw_ipaddress}"
       private_key = "${file("./id_rsa")}"
    }

    # yumでapache+PHPのインストール
    provisioner "remote-exec" {
        inline = [
          "yum install -y httpd httpd-devel php php-mbstring php-mysqlnd",
          "systemctl restart httpd.service",
          "systemctl enable httpd.service",
          "systemctl stop firewalld.service",
          "systemctl disable firewalld.service"
        ]
    }

    # Webコンテンツをアップロード
    provisioner "file" {
        source = "webapps/"
        destination = "/var/www/html"
    }

    # IP設定スクリプトをアップロード
    provisioner "file" {
        source = "provision_private_ip.sh"
        destination = "/tmp/provision_private_ip.sh"
    }

    # IP設定実行
    provisioner "remote-exec" {
        inline = [
          "chmod +x /tmp/provision_private_ip.sh",
          "/tmp/provision_private_ip.sh ${lookup(var.private_ip_addresses , self.name)}"
        ]
    }


}

resource "sakuracloud_server" "server_db" {
    name = "server_db"
    disks = ["${sakuracloud_disk.disk_db.id}"]
    additional_interfaces = ["${sakuracloud_switch.sw01.id}"]
    packet_filter_ids = ["${sakuracloud_packet_filter.pf_db.id}"]

    # サーバーにはSSHで接続
    connection {
       user = "root"
       host = "${self.base_nw_ipaddress}"
       private_key = "${file("./id_rsa_db")}"
    }

    # yumでmysqlのインストール
    provisioner "remote-exec" {
        inline = [
          "yum install -y mysql-community-server",
          "systemctl start mysql.service",
          "mysql -uroot -e 'GRANT ALL ON *.* TO ${var.mysql_values.user_name}@\"192.168.2.%\" IDENTIFIED BY \"${var.mysql_values.user_password}\"'" ,
          "mysqladmin -u root password '${var.mysql_values.root_password}'",
          "systemctl stop firewalld.service",
          "systemctl disable firewalld.service"
        ]
    }

    # IP設定スクリプトをアップロード
    provisioner "file" {
        source = "provision_private_ip.sh"
        destination = "/tmp/provision_private_ip.sh"
    }

    # IP設定実行
    provisioner "remote-exec" {
        inline = [
          "chmod +x /tmp/provision_private_ip.sh",
          "/tmp/provision_private_ip.sh ${lookup(var.private_ip_addresses , self.name)}"
        ]
    }

}

/*****************
 * SSH key 
 *****************/
resource "sakuracloud_ssh_key" "mykey" {
    name = "mykey"
    public_key = "${file("./id_rsa.pub")}"
}

resource "sakuracloud_ssh_key" "dbkey" {
    name = "dbkey"
    public_key = "${file("./id_rsa_db.pub")}"
}

/*****************
 * Switch
 *****************/
resource "sakuracloud_switch" "sw01" {
  name = "sw01"
}

/*****************
 * PacketFilter
 *****************/
resource "sakuracloud_packet_filter" "pf_web" {
    name = "pf_web"
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "22"
        description = "Allow SSH"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "80"
        description = "Allow www"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "443"
        description = "Allow www(ssl)"
        allow = true
    }
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(tcp)"
        allow = true
    }
    expressions = {
        protocol = "udp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(udp)"
        allow = true
    }
    expressions = {
        protocol = "icmp"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all icmp"
    }
    expressions = {
        protocol = "fragment"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all fragment"
    }
    expressions = {
        protocol = "ip"
        source_nw = "0.0.0.0"
        allow = false
        description = "Deny all"
    }
}
resource "sakuracloud_packet_filter" "pf_db" {
    name = "pf_db"
    expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "22"
        description = "Allow SSH"
        allow = true
    }
   expressions = {
        protocol = "tcp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(tcp)"
        allow = true
    }
    expressions = {
        protocol = "udp"
        source_nw = "0.0.0.0/0"
        dest_port = "32768-61000"
        description = "Allow return packet(udp)"
        allow = true
    }
    expressions = {
        protocol = "icmp"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all icmp"
    }
    expressions = {
        protocol = "fragment"
        source_nw = "0.0.0.0"
        allow = true
        description = "Allow all fragment"
    }
    expressions = {
        protocol = "ip"
        source_nw = "0.0.0.0"
        allow = false
        description = "Deny all"
    }
}

/*****************
 * SimpleMonitor
 *****************/

# ping監視(DBサーバー)
resource "sakuracloud_simple_monitor" "ping_monitor_db" {
    target = "${sakuracloud_server.server_db.base_nw_ipaddress}"
    health_check = {
        protocol = "ping"
        delay_loop = 60
    }
    notify_email_enabled = false
    notify_slack_enabled = true
    notify_slack_webhook = "${var.slack_webhook}"
}

# ping監視(webサーバー2台分)
resource "sakuracloud_simple_monitor" "ping_monitor_web" {
    count = 2
    target = "${element(sakuracloud_server.server.*.base_nw_ipaddress , count.index)}"
    health_check = {
        protocol = "ping"
        delay_loop = 60
    }
    notify_email_enabled = false
    notify_slack_enabled = true
    notify_slack_webhook = "${var.slack_webhook}"
}
# web監視(webサーバー2台分)
resource "sakuracloud_simple_monitor" "http_monitor_web" {
    count = 2
    target = "${element(sakuracloud_server.server.*.base_nw_ipaddress , count.index)}"
    health_check = {
        protocol = "http"
        delay_loop = 60
        path = "/index.php"
        status = "200"
    }
    notify_email_enabled = false
    notify_slack_enabled = true
    notify_slack_webhook = "${var.slack_webhook}"
}


/*****************
 * DNS
 *****************/
resource "sakuracloud_dns" "dns" {
    zone = "fe-bc.net"
    records = {
        name = "web"
        type = "A"
        value = "${sakuracloud_server.server.1.base_nw_ipaddress}"
    }
    records = {
        name = "web"
        type = "A"
        value = "${sakuracloud_server.server.0.base_nw_ipaddress}"
    }
}

/*****************
 * Output
 *****************/
output "global_ip" {
    value = "${join("\n" , formatlist("%s : %s" , sakuracloud_server.server.*.name , sakuracloud_server.server.*.base_nw_ipaddress))}"
}
output "global_ip_db" {
    value = "${format("%s : %s\n" , sakuracloud_server.server_db.name , sakuracloud_server.server_db.base_nw_ipaddress)}"
}
output "ssh_web01" {
    value = "${format("ssh root@%s -i %s/id_rsa" , sakuracloud_server.server.0.base_nw_ipaddress , path.root)}"
}
output "ssh_web02" {
    value = "${format("ssh root@%s -i %s/id_rsa" , sakuracloud_server.server.1.base_nw_ipaddress , path.root)}"
}
output "ssh_db" {
    value = "${format("ssh root@%s -i %s/id_rsa_db" , sakuracloud_server.server_db.base_nw_ipaddress , path.root)}"
}

まとめ

今回は盛りだくさんでしたね!

  • DBサーバーの追加
  • スイッチの追加 + プライベートネットワークの構築
  • パケットフィルタの追加
  • シンプル監視の追加

シンプルな構成のシステムなら十分に実用できるレベルまで近づきましたね!

次回はいよいよ仕上げとして

  • MySQL レプリケーション + PHP(mysqlnd_ms)によるクラスタリング
  • 東京と石狩でマルチゾーン構成
  • GSLBによるゾーン間HA構成

を行います。お楽しみに :bangbang:

次回:実践 Step5:東京/石狩 マルチゾーン構成

おまけ:さくらのクラウドで503エラー

terraform destroy実行時に以下のようなエラーが出ることがあります。

さくらのクラウド503エラー
Error applying plan:

1 error(s) occurred:

* sakuracloud_server.server.0: 
Error deleting SakuraCloud Server resource: Error in response:
&sacloud.ResultErrorValue{
    IsFatal:true, 
    Serial:"c843d04c9fc1caaa26e4b1eba649245d", 
    Status:"503 Service Unavailable", 
    ErrorCode:"busy", 
    ErrorMessage:"サービスが利用できません。サーバが混雑しています。しばらく時間をおいてから再度お試しください。"
}

Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.

たくさんのリソースを一気にdestroyした場合などに出ることがあります。
慌てず騒がずちょっと時間をおいてterraform destroyを再実行しましょう。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?