⛈️
Terraform:GCPでプロキシサーバー構築の自動化を試した
はじめに
前回、GCP上でプロキシサーバーを構築し、後段のバックエンドへリクエストを行うモックを作ってみました。
そこで、Terraformをつかって自動で環境構築できるよう試してみました。
コード全体
Terraformで実行する設定の流れは下記のようになっています。
- GCP上でプロジェクト作成(ここだけ手動)
a. プロキシサーバーとバックエンドサーバー用に2つ作成 - VPCネットワークの作成
- VPCネットワークピアリングを設定
- firewallの設定を追加
- VMインスタンスの作成
a. プロキシサーバー側では、Nginxのインストールと設定
b. バックエンドサーバー側では、Node.jsのインストールと簡易なバックエンドサーバーの実装
コード全体
# 定数
locals {
project_name_1 = "test-project-441812"
project_name_2 = "test-reverse-proxy-441812"
vpc_network_name_1 = "backend-server-network"
vpc_network_name_2 = "proxy-server-network"
subnet_name_1 = "backend-server-subnet"
subnet_ip_1 = "10.1.0.0/24"
subnet_name_2 = "proxy-server-subnet"
subnet_ip_2 = "10.0.0.0/24"
region = "us-central1"
}
# VPCネットワーク1 作成
resource "google_compute_network" "vpc_network_1" {
name = local.vpc_network_name_1
project = local.project_name_1
auto_create_subnetworks = false
}
# サブネット1 作成
resource "google_compute_subnetwork" "subnet_1" {
name = local.subnet_name_1
project = local.project_name_1
ip_cidr_range = local.subnet_ip_1
region = local.region
network = google_compute_network.vpc_network_1.id
}
# VPCネットワーク2 作成
resource "google_compute_network" "vpc_network_2" {
name = local.vpc_network_name_2
project = local.project_name_2
auto_create_subnetworks = false
}
# サブネット2 作成
resource "google_compute_subnetwork" "subnet_2" {
name = local.subnet_name_2
project = local.project_name_2
ip_cidr_range = local.subnet_ip_2
region = local.region
network = google_compute_network.vpc_network_2.id
}
# VPC peering
resource "google_compute_network_peering" "peering_1" {
name = "peering-1"
network = google_compute_network.vpc_network_1.self_link
peer_network = google_compute_network.vpc_network_2.self_link
}
resource "google_compute_network_peering" "peering_2" {
name = "peering-2"
network = google_compute_network.vpc_network_2.self_link
peer_network = google_compute_network.vpc_network_1.self_link
}
# firewall settings
resource "google_compute_firewall" "firewall_backend_ssh" {
project = local.project_name_1
name = "firewall-backend-ssh"
network = google_compute_network.vpc_network_1.name
allow {
protocol = "tcp"
ports = ["22"]
}
source_tags = ["ssh-backend-server"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "firewall_proxy_server_ssh" {
project = local.project_name_2
name = "firewall-proxy-server-ssh"
network = google_compute_network.vpc_network_2.name
allow {
protocol = "tcp"
ports = ["22"]
}
source_tags = ["ssh-proxy-server"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "firewall_backend" {
project = local.project_name_1
name = "firewall-backend"
network = google_compute_network.vpc_network_1.name
allow {
protocol = "tcp"
ports = ["8080"]
}
source_tags = ["tcp-backend-server"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "firewall_proxy_server" {
project = local.project_name_2
name = "firewall-proxy-server"
network = google_compute_network.vpc_network_2.name
allow {
protocol = "tcp"
ports = ["80"]
}
source_tags = ["tcp-proxy-server"]
source_ranges = ["0.0.0.0/0"]
}
# create vm instace
resource "google_compute_instance" "backend_server" {
name = "vm-backend-server"
machine_type = "e2-micro"
zone = "us-central1-a"
project = local.project_name_1
boot_disk {
initialize_params {
image = "ubuntu-minimal-2210-kinetic-amd64-v20230126"
}
}
tags = [ "tcp-backend-server", "ssh-backend-server" ]
network_interface {
network = google_compute_network.vpc_network_1.name
subnetwork = google_compute_subnetwork.subnet_1.name
subnetwork_project = local.project_name_1
access_config {}
}
# 公開鍵を設定
metadata = {
ssh-keys = "{$USER_NAME}:ssh-rsa {$PUBLIC_KEY}"
}
provisioner "remote-exec" {
connection {
type = "ssh"
# SSH接続を行うユーザー名
user = "{$USER_NAME}"
# 秘密鍵を設定
private_key = file("./sshkey/google_compute_engine_for_terraform")
host = self.network_interface.0.access_config.0.nat_ip
timeout = "10m"
}
# Node.jsのインストールと簡易なバックエンドサーバーの実装
inline = [
# Node.jsをインストール
"curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash",
"export NVM_DIR=\"$HOME/.nvm\"",
"[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"",
"nvm install 23",
"node -v",
"npm -v",
"mkdir sample",
"npm init -y", # 自動的にデフォルト設定でpackage.jsonを作成
"npm install express",
"npm install pm2 -g",
# 簡易なバックエンドサーバーの実装
"echo \"const express = require('express')\" > sample/index.js",
"echo \"const app = express()\" >> sample/index.js",
"echo \"const port = 8080\" >> sample/index.js",
"echo \"app.get('/', (req, res) => { res.send('Hello World!') })\" >> sample/index.js",
"echo \"app.listen(port, () => { console.log(`Example app listening on port `) }) \" >> sample/index.js",
# サーバーの永続化
"pm2 start sample/index.js --name my-app",
"pm2 startup",
"pm2 save"
]
}
}
resource "google_compute_instance" "proxy_server" {
name = "vm-proxy-server"
machine_type = "e2-micro"
zone = "us-central1-a"
project = local.project_name_2
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
tags = [ "tcp-proxy-server", "ssh-proxy-server" ]
network_interface {
network = google_compute_network.vpc_network_2.name
subnetwork = google_compute_subnetwork.subnet_2.name
subnetwork_project = local.project_name_2
access_config {}
}
# 公開鍵を設定
metadata = {
ssh-keys = "{$USER_NAME}:ssh-rsa {$PUBLIC_KEY}"
}
provisioner "remote-exec" {
connection {
type = "ssh"
# SSH接続を行うユーザー名
user = "{$USER_NAME}"
# 秘密鍵を設定
private_key = file("./sshkey/google_compute_engine_for_terraform")
host = self.network_interface.0.access_config.0.nat_ip
timeout = "10m"
}
# Nginxのインストールとプロキシサーバーの実装
inline = [
# Nginxのインストール
"echo \" IP ADDRESS ${google_compute_instance.proxy_server.network_interface.0.access_config.0.nat_ip}\"",
"sudo apt install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring",
"curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null",
"gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg",
"echo \"deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian `lsb_release -cs` nginx\" | sudo tee /etc/apt/sources.list.d/nginx.list",
"echo \"Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n\" | sudo tee /etc/apt/preferences.d/99nginx | sudo apt install -y nginx",
"sudo apt update",
"sudo apt install -y nginx",
# パスを通す
"echo 'export PATH=$PATH:/usr/sbin' | sudo tee -a /etc/profile",
# Nginxの起動
"sudo systemctl start nginx",
# proxy serverの実装
"sudo touch /etc/nginx/conf.d/costom.conf",
"echo \"server{\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"listen 80;\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"server_name ${google_compute_instance.proxy_server.network_interface.0.access_config.0.nat_ip};\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"proxy_set_header Host \\$host;\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"proxy_set_header X-Real-IP \\$remote_addr;\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"proxy_set_header X-Forwarded-For \\$proxy_add_x_forwarded_for;\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"proxy_set_header X-Forwarded-Proto \\$scheme;\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"location /sample/ { proxy_pass http://${google_compute_instance.backend_server.network_interface.0.network_ip}:8080/; }\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
"echo \"}\" | sudo tee -a /etc/nginx/conf.d/costom.conf > /dev/null",
# Nginxの再起動
"sudo nginx -s reload",
]
}
}
詳細
定数の設定
定数はlocals
で管理してます
# 定数
locals {
project_name_1 = "test-project-441812"
project_name_2 = "test-reverse-proxy-441812"
vpc_network_name_1 = "backend-server-network"
vpc_network_name_2 = "proxy-server-network"
subnet_name_1 = "backend-server-subnet"
subnet_ip_1 = "10.1.0.0/24"
subnet_name_2 = "proxy-server-subnet"
subnet_ip_2 = "10.0.0.0/24"
region = "us-central1"
}
VPCネットワークの作成
VPCネットワークとサブネットの作成は下記のとおりです。
# VPCネットワーク1 作成
resource "google_compute_network" "vpc_network_1" {
name = local.vpc_network_name_1
project = local.project_name_1
auto_create_subnetworks = false
}
# サブネット1 作成
resource "google_compute_subnetwork" "subnet_1" {
name = local.subnet_name_1
project = local.project_name_1
ip_cidr_range = local.subnet_ip_1
region = local.region
network = google_compute_network.vpc_network_1.id
}
VPCピアリング
プロキシサーバーとバックエンドサーバーを相互にピアリングします。
# VPC peering
resource "google_compute_network_peering" "peering_1" {
name = "peering-1"
network = google_compute_network.vpc_network_1.self_link
peer_network = google_compute_network.vpc_network_2.self_link
}
resource "google_compute_network_peering" "peering_2" {
name = "peering-2"
network = google_compute_network.vpc_network_2.self_link
peer_network = google_compute_network.vpc_network_1.self_link
}
firewallの設定
プロキシサーバー、バックエンドサーバー側、それぞれのネットワークのfirewallをこのように設定しました。
- プロキシサーバー
-
22
、80
ポートからのTCP通信を許可
-
- バックエンドサーバー
-
22
、8080
ポートからのTCP通信を許可
-
プロキシサーバー側ではNginxのインストールと設定を行い、
バックエンドサーバー側ではNode.jsのインストールと簡易なバックエンドサーバーの実装を行います。
そのためSSH接続を行う必要があるので、22
ポートからのアクセスを許可しています。
またプロキシサーバー側は80
ポートからのアクセスを許可します。
そしてバックエンドサーバー側は8000
ポートのアクセスを許可することで、プロキシサーバー側から後段のバックエンドサーバーへリクエストできるようにしています。
それぞれのfirewallでsource_ranges = ["0.0.0.0/0"]
と設定しています。
しかし、これでは全てのIPアドレスからのアクセスを許可してしまうので実用的には見直した方が良いですね。
コード詳細
# firewall settings
resource "google_compute_firewall" "firewall_backend_ssh" {
project = local.project_name_1
name = "firewall-backend-ssh"
network = google_compute_network.vpc_network_1.name
allow {
protocol = "tcp"
ports = ["22"]
}
source_tags = ["ssh-backend-server"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "firewall_proxy_server_ssh" {
project = local.project_name_2
name = "firewall-proxy-server-ssh"
network = google_compute_network.vpc_network_2.name
allow {
protocol = "tcp"
ports = ["22"]
}
source_tags = ["ssh-proxy-server"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "firewall_backend" {
project = local.project_name_1
name = "firewall-backend"
network = google_compute_network.vpc_network_1.name
allow {
protocol = "tcp"
ports = ["8080"]
}
source_tags = ["tcp-backend-server"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "firewall_proxy_server" {
project = local.project_name_2
name = "firewall-proxy-server"
network = google_compute_network.vpc_network_2.name
allow {
protocol = "tcp"
ports = ["80"]
}
source_tags = ["tcp-proxy-server"]
source_ranges = ["0.0.0.0/0"]
}
バックエンドサーバーのVM作成
全体のコードは下記のとおりです。
コード詳細
# create vm instace
resource "google_compute_instance" "backend_server" {
name = "vm-backend-server"
machine_type = "e2-micro"
zone = "us-central1-a"
project = local.project_name_1
# ブートディスクの設定
boot_disk {
initialize_params {
image = "ubuntu-minimal-2210-kinetic-amd64-v20230126"
}
}
tags = [ "tcp-backend-server", "ssh-backend-server" ]
# ネットワークの設定
network_interface {
network = google_compute_network.vpc_network_1.name
subnetwork = google_compute_subnetwork.subnet_1.name
subnetwork_project = local.project_name_1
access_config {}
}
# 公開鍵を設定
metadata = {
ssh-keys = "{$USER_NAME}:ssh-rsa {$PUBLIC_KEY}"
}
provisioner "remote-exec" {
connection {
type = "ssh"
# SSH接続を行うユーザー名
user = "{$USER_NAME}"
# 秘密鍵を設定
private_key = file("./sshkey/google_compute_engine_for_terraform")
host = self.network_interface.0.access_config.0.nat_ip
timeout = "10m"
}
# Node.jsのインストールと簡易なバックエンドサーバーの実装
inline = [
# Node.jsをインストール
"curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash",
"export NVM_DIR=\"$HOME/.nvm\"",
"[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"",
"nvm install 23",
"node -v",
"npm -v",
"mkdir sample",
"npm init -y", # 自動的にデフォルト設定でpackage.jsonを作成
"npm install express",
"npm install pm2 -g",
# 簡易なバックエンドサーバーの実装
"echo \"const express = require('express')\" > sample/index.js",
"echo \"const app = express()\" >> sample/index.js",
"echo \"const port = 8080\" >> sample/index.js",
"echo \"app.get('/', (req, res) => { res.send('Hello World!') })\" >> sample/index.js",
"echo \"app.listen(port, () => { console.log(`Example app listening on port `) }) \" >> sample/index.js",
# サーバーの永続化
"pm2 start sample/index.js --name my-app",
"pm2 startup",
"pm2 save"
]
}
}
Discussion