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_resource
やtemplate_file
などの特殊なリソース - tfファイルのリファクタリングとモジュール化
連載第4回です。
第1回から順番にお読みください。
実践 Step4:Web/DB 2-Tier構成
いよいよ応用編になります。
Terraformの基本的な操作を駆使して実運用に耐えるインフラを構築していきましょう。
前回(第3回)は以下のような構成でした。
2台のWebサーバに加えてDNSへの登録も行っていましたね。
現時点でも外部からの接続ができる状態なのですが、
今回は本番運用意識して構成を変更していきます。
Web層/DB層の2階層(2-tier)化
スイッチを追加して、DBを配置するプライベートなネットワークを構築します。
以下のリソースを追加します。
- プライベートネットワーク用にスイッチ(sw01)を追加
- プライベートネットワーク内にDBサーバー(server_db + disk_db)を追加
DBサーバーではMySQLを稼働させます。
非機能要件対応
本番運用時にはセキュリティ対応や監視/測定、バックアップなどの非機能要件も満たす必要があります。
今回はバックアップは手動、監視は通知だけ来るようにしておき復旧は手動で行うものとして、
以下の対応だけ行います。
- パケットフィルタを追加してセキュリティ対策
- シンプル監視で監視 + Slackへ通知
今回の最終的な構成
なんだか一気にリソースが追加されて複雑になってきましたね。
でも大丈夫です。
Terraformを使うのであれば、トライ&エラーで定義ファイル(tfファイル)を
育てて いけばいいんです。
tfファイルを作ってテストしておけばその環境の再現は簡単なんですからね。
では早速実践していきましょう。
実践
第1段階:DBサーバー追加
まずはDBサーバーを追加してみましょう。
この段階では以下のような構成になります。
DBサーバーとWebサーバーとではセキュリティ要件が異なるため、
SSH公開鍵はWeb層(server01と02)とは別のものを使うようにしておきます。
追加するリソースは以下の通りです。
- サーバー(server_db)
- ディスク(disk_db)
- SSH公開鍵(dbkey)
加えて、DBサーバーのプロビジョニングも行っておきます。
さくらのクラウドのパブリックアーカイブからCentOS7.2をインストールした場合、
mysql communityのyumリポジトリがすでに登録されており、インストールが容易なため
MySQL5.6をインストールしていきます。
すでに前回までに出てきたテクニックだけで対応できますね!
準備
DBサーバー用にキーペアを作成しましょう。
$ 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_db
とid_rsa_db.pub
が作成されているはずです。
定義ファイル(tfファイル)編集
それでは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のパスワードは任意のものを設定してください。
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
定義しておきます。
以下を追記してください。
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)
plan
とapply
の実行
さて、いつも通りterraform plan
してからterraform apply
しましょう。
DBサーバーが追加されましたか?
SSH接続するなどで動作確認もしておきましょう。
第2段階:スイッチ追加でプライベートネットワークと接続
では続いてスイッチを追加しましょう。
こんな感じになります。
やることは
- スイッチ用リソース(sakuracloud_switch)の追加
- スイッチとサーバーの接続
- プライベートIPの割り振り(プロビジョニングで実施)
です。
また、WebサーバーとDBサーバー間をプライベートネットワークで接続できるようになりますので、
デモ用のWebアプリ(PHP)もDBを使うように変更していきます。
順番にいきましょう。
スイッチ用リソースの定義
いつも通りtfファイルに追記しましょう。
以下を追記してください。
resource "sakuracloud_switch" "sw01"{
name = "sw01"
}
えっ?これだけ?
はい。スイッチは設定項目が少ないのです、、
スイッチとサーバーの接続
サーバーの定義部分を修正しましょう。
# 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アドレスを設定するようにプロビジョニングしましょう。
まずは手元のマシンで以下のスクリプトを作成しておきます。
$ vi 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ファイルに記載しましょう。
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
の引数部分も修正してください
<?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用のライブラリも追加しておきましょう。
プロビジョニング部分を変更します。
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"
]
}
#中略
plan
とapply
の実行
今回は実行前にterraform destroy
してから
terraform plan
とterraform apply
しましょう。
うまく反映されましたね?
SSH接続した上でip addr show
やping
を試しておくのも良いでしょう。
この段階でブラウザでWebサーバーにアクセスすると、以下のような表示になるはずです。
第3段階:パケットフィルタ、シンプル監視の追加
いよいよ最終形です。
- パケットフィルタの追加
- パケットフィルタをサーバーに適用
- シンプル監視の追加
を行いましょう。
パケットフィルタの追加
パケットフィルタを合計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ファイルのサーバー部分を修正しましょう。
# 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の設定を行っておいてください。
tfファイルは以下の通りです。webhookのURLは各自置き換えてください。
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}"
}
plan
とapply
の実行
いよいよですね!早速実行してみましょう。
うまくいきましたよね?
監視ができているか試しにサーバーを落としてみたりしてください。
slackに通知がきちんと届けばOKです。
最終的な定義ファイル(tfファイル)
今回の最終的なtfファイルは以下のようになっているはずです。
(コメントなど入れて整形しています)
うまくいかない方はこちらと比較してみてください。
/*********************
* 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構成
を行います。お楽しみに
おまけ:さくらのクラウドで503エラー
terraform destroy
実行時に以下のようなエラーが出ることがあります。
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
を再実行しましょう。