AWSとGCPのマルチクラウドインフラ検証の進め方

こんにちは!プレイドの@ikemonnです。
前回はAWSとGCPをVPNで接続した話をしました。
今回はGCPでの検証を行った際の進め方について説明していきます。

インフラを作り込みすぎずに検証したことで、早めに想定外のボトルネックに気づくことができ、予定していなかったインフラ構成の変更が必要であることが確認できました。

マルチクラウドインフラを構築する際には「小さく始めて、検証できる環境を作る」ことがポイントになります。

目次

  • 小さく始める
  • 検証する
  • まとめ

小さく始める

最初に検証するサーバの選定

今回は下記の3点を基準にどのサーバをまずGCPへ移行するかを選定しました。

  1. お客様への影響が少ないサーバ
  2. GCPへ移行のメリットが大きいサーバ(パフォーマンス向上等)
  3. 関連するコンポーネントが少ないサーバ

KARTEでは処理の内容毎にサーバ群が分かれており、大きく分けると「エンドユーザからのアクセスを受けるWebサーバ群」と「タスクキューからタスクを取得して処理していくサーバ群」の2種類があります。

  1. Cloud BigTableを一番使用していて移行のメリットが大きい
  2. LB等の関連するコンポーネントが少ないので構築が楽

という特性の「タスクキューからタスクを取得して処理していくサーバ群」から移行を始めることにしました。

検証用サーバの構築

構築の際には下記の点を意識しながら構築を進めていきました。

  1. GCPの操作に慣れ、特徴をつかむためにまずWebコンソールから手動でサーバを立てる
  2. GCPに慣れた後は今後のことを考えて使いまわせるようにする

Webコンソールからサーバを構築する

まずはWebコンソールから手動でサーバを構築することから始めました。

もともとAWS上のサーバはTerraformを用いて構築していました。
しかし、いきなりGCPもTerraformで構築しても設定すべきパラメータや各コンポーネントとの関連が理解できておらず効率が悪いです。
まず最初は手動で作るのがオススメです。

Webコンソールから構築をすることで、「AWSとGCPではブロックデバイスの扱いが異なっている(GCPではImageにブロックデバイスの情報は付与されていない)」ことや「AutoScaling Groupで設定すべき項目が大きく異なる」ことなど、プラットフォームの違いが分かり、後のTerraform化作業がスムーズに行えました。

下記がTerraformでのlaunch config/instance templateコードです。
disk周りの設定が異なっていることがわかります。

# GCPのinstance template (Imageにdisk情報が含まれていないので指定する必要がある)
resource "google_compute_instance_template" "foobar" {
  name        = "terraform-test"
  machine_type         = "n1-standard-1"

  // Create a new boot disk from an image
  disk {
    source_image = "debian-cloud/debian-8"
    auto_delete  = true
    boot         = true
  }

  // Use an existing disk resource
  disk {
    source      = "foo_existing_disk"
    auto_delete = false
    boot        = false
  }
...(中略)
}
# AWSのlaunch config (AMIにdisk情報も含まれているので指定する必要が無い)
resource "aws_launch_configuration" "as_conf" {
  name_prefix   = "terraform-lc-example-"
  image_id      = "${data.aws_ami.ubuntu.id}"
  instance_type = "t2.micro"

  lifecycle {
    create_before_destroy = true
  }
}

「この部分はAWSでいう〇〇だ」というようなことを考えながら構築するのがオススメです。

最低限使いまわせるようにする

手動でサーバを構築することで、設定すべき項目のあたりがつくようになりました。
今後のことを考えると、手動構築ではサーバをスケールさせてパフォーマンスを見たい時や、他の役割のサーバ群を立てる時に大変です。
そこで、後々使いまわせるようにサーバの構成管理とImage作成に関しては、再利用できるようにすることにしました。

様々なツールがでているのですが、「マルチプラットフォームで使用できる」という点を踏まえて下記のツールを選びました。

AWSでもサーバの構成管理はTerraformで行っていたので引き続き使うことにしました。
問題はImageを作成する部分でした。
もともと、AWS CloudFormationで作っていたのですが、CloudFormationではGCEのイメージは作成できません。

また、GoogleのサービスGoogle Cloud Deployment Manager もありますが、CloudFront/Deployment Managerの双方を使いコードを2重管理するのは面倒です。

「マルチプラットフォームのimageを簡単に作れる」「コードを2重管理しなくて良い」という点を考えてツールを探した結果、Packerを使用することにしました。

PackerとはTerraformやVagrantを作っているHashiCorp社の製品で、ユーザが準備した設定ファイルに従って、仮想マシン等のイメージ作成とプロビジョニングを行ってくれるツールです。
Packerを使うことでAWS/GCP両方のイメージを作成することができます。

下記のように、AWSとGCPのサーバの設定をそれぞれ指定して書いていきます。

{
  "variables": {
     "node_ver": "6.9.2",
     "npm_ver": "3.8.8",
     ...
     },
  "builders": [
    {
      "type": "googlecompute",
      "instance_name": "{{user `environment`}}-node-{{isotime \"2006-01-02-1504\"}}",
      "image_name": "{{user `environment`}}-node-{{isotime \"2006-01-02-1504\"}}",
      "account_file": "{{user `gcp_account_file`}}",
      "project_id": "{{user `gcp_project_id`}}",
      "disk_type": "pd-ssd",
      "machine_type": "n1-standard-2",
      "disk_size": "{{user `root_volume_size`}}",
      "ssh_username": "root",
      "source_image": "{{user `gcp_source_image`}}",
      "zone": "asia-east1-a"
    },
    {
      "type": "amazon-ebs",
      "access_key": "{{user `aws_access_key`}}",
      "secret_key": "{{user `aws_secret_key`}}",
      "region": "{{user `aws_region`}}",
      "iam_instance_profile": "{{user `aws_iam_instance_profile`}}",
      "source_ami": "{{user `aws_source_image`}}",
      "instance_type": "c4.large",
      "ssh_username": "ubuntu",
      "associate_public_ip_address": "true",
      "ami_name": "{{user `environment`}}_nodeami_{{isotime \"2006-01-02-1504\"}}",
      "enhanced_networking": "true",
      "launch_block_device_mappings":[
        {
          "device_name": "/dev/sda1",
          "volume_size": "{{user `root_volume_size`}}",
          "volume_type": "gp2",
          "delete_on_termination": "true"
        },
        {
          "device_name": "/dev/sdf",
          "volume_size": "10",
          "volume_type": "gp2",
          "delete_on_termination": "true"
        }
      ],
      "vpc_id":"{{user `vpc_id`}}",
      "subnet_id":"{{user `subnet_id`}}",
      "communicator": "ssh",
      "user_data_file": "common/aws_user_data.sh"
    }
  ],
  "provisioners": [
    {
      "only": ["amazon-ebs"],
      "type": "shell",
      "script": "common/init_for_aws.sh",
      "execute_command": "{{ .Vars }} sudo -E sh '{{ .Path }}'"
    },
    {
      "only": ["googlecompute"],
      "type": "shell",
      "script": "common/init_for_gcp.sh",
      "execute_command": "{{ .Vars }} sudo -E sh '{{ .Path }}'"
    },
    {
      "type": "shell",
      "scripts": ["common/exec_serverspec.sh"],
      "execute_command": "{{ .Vars }} sudo -E sh '{{ .Path }}'"
    }
  ]

Packerの良い点は、コマンドを実行するだけでイメージを作成できるところです。
CloudFormationを使っていた時はインスタンスを立てた後、AMIを作成する & image作成後のインスタンス削除は手動で行っていました。
Packerに変えることで、image作成&インスタンス削除が全てコマンドラインだけで行えるので作業効率化につながりました。

また、下記のようにimage作成処理は並列で実行されるところも良い点です。

実際にイメージを作ってみたところ、GCPのimageの方がAWSよりも2~3分早く作成されました。
弊社はNode.jsを使用しており、Node.jsのinstallまではAWSの方が1.5分ほど早いのですが、node_moduleのinstallになるとGCPが逆転していました。
処理内容を見てみると、EBSへのファイル書き込みで時間がかかっているようなので、GCPの方がそのあたりは速いようです。

検証する

Packer + Terraformでサーバを構築できたので検証に移ります。

弊社ではDatadogというモニタリングツールを使い、インフラ&アプリケーションの監視を行っています。(詳しくはこちら→ 【freee×プレイド】Tech Meetup 〜インフラ監視編〜を開催しました!)
今回もAWSとGCPのパフォーマンスをDatadogで可視化し、どの処理に時間がかかっているかを調べました。

弊社の @makinoy が作成したmakinoy/libs-dogstatsdという、処理の実行時間や処理回数をDatadogに送信できるラッパーを使用して実行時間を計測しました。

var dogstatsd = require('libs-dogstatsd')

const stats = dogstatsd.start();
// some method
stats.tick(‘test’, 1, 1, [pid:1234]);

この時点では、上記のような構成でGCP上のサーバからAWSのDBを見に行くようになっているので、VPN越しのDBへの接続がボトルネックになっていることが分かりました。

一方、BigTableへの接続は同じGCP上にあることで、AWS環境で動かしていた時よりも早いということも分かりました。

また、一気に大量のインスタンスを立てて検証していた時に、想定外のボトルネックが見つかりました。
一定台数以上のインタンスを立てるとパフォーマンスが出なくなったのです。
よくよく調べてみると、ドキュメントには明記されていなかったのですが、どうもVPNの帯域に上限があるようでした。
このままではGCP上でサーバをスケールできないので、DBの配置を変更してVPN経由の通信を減らす必要があることが分かりました。

このような想定外のボトルネックも早めに検証することで確認できたのが大きかったです。

DB構成については、次回以降の記事でどのように改善したのかお伝えしていきます!

まとめ

マルチプラットフォームでの検証を進める際には、下記を意識して進めていくと良いと思います。

  • まずは手動でサーバを立ててみて、そのプラットフォームに慣れる
  • 初めは極力作り込まずにサーバを立てて検証を始める

「頑張りすぎずに小さく始めて、検証していく」ことでマルチプラットフォーム化を効率よく進めていきましょう!

関連リンク

最後に

ウェブ接客プラットフォーム「KARTE」を運営するプレイドでは、マルチクラウドインフラを成長させたい、KARTEを使ってこんなアプリケーションが作りたい! KARTE自体の開発に興味がある!というエンジニア(インターンも!)を募集しています。
詳しくは弊社採用ページ、またはWantedlyをご覧ください。 もしくはお気軽に、下記の「話を聞きに行きたい」ボタンを押してください!