GCP | AI tech studio https://cyberagent.ai AI事業本部は、サイバーエージェントグループのデジタルマーケティング分野におけるサービス開発を行う組織です。 Thu, 12 Aug 2021 22:49:22 +0000 ja hourly 1 https://wordpress.org/?v=5.3.26 MLflowをGoogle Kubernetes Engineで動かす快適な実験管理ハンズオン https://cyberagent.ai/blog/research/15272/ Thu, 12 Aug 2021 22:31:10 +0000 https://ai-blog-admin.cyberagent.ai/?post_type=blog&p=15272 AI Labの岩崎(@chck)です、こんにちは。今回は前記事よりも実践的な、AI Labにおける実験管理システムの話をしたいと思います。ここでいう実験とは、データを収集・加工し、統計や機械学習を用い、設定したタスクや仮説を明らかにすることです。実験管理とはその評価や使ったパラメータ及び実験コードを再現できる形で保管することを指します。

対象読者

  • 個人や大学、企業所属でJupyterLab上の実験管理に苦労している方
  • チームでKaggle等のデータ分析コンペに参加している方
  • Kubernetes、GCP、Terraformといったキーワードに興味のある方

tl;dr

MLflowをGKEに載せることで、高可用でユーザ認証を持つMLflow Tracking Serverを作りました。更にTerraformによる1command構築を目指しました。中規模以上の研究室を想定し、Load Balancingにより各ユーザで独立した実験管理を実現しました。個人や少人数チームな場合は、GKEよりもManagedなコンテナサービングサービスであるCloud RunでCloud IAPというユーザ認証が使えるようになったので、本記事をベースにそちらの構成で試すとシンプルで良いでしょう。

はじめに

皆さんは普段どのように実験管理していますか。AI Labには現在30名弱のResearcherが在籍しており、各人が大学のゼミのようにメインテーマを持って日々R&Dに励んでいます。実験のための計算リソースやデータ管理にGCP・AWS・オンプレが用意されており、個々が自由に使えるようになっています。ありがたいことにLabメンバーの増加に伴い、実験やデータ管理を各々が1から行うことで、似た構成のインフラが複数立ち、ノウハウの不透明化やメンテコストが目立つようになりました。そこで今回はResearcherが共通で使えるMLflow Clusterを構築してみます。

2021年現在、実験管理サービスといえばどんなものが思い浮かぶでしょうか。OSSであるMLflowやTensorBoard・Keepsakeの他、SaaSであるNeptune.ai、Comet.ml、Weights & Biases [1]あたりが有名ですね。ワンマンプロジェクトであればSaaSで十分事足りると思いますが、複数人での実験結果の共有や無料版での制限以上のことをやろうとするとMLflow等を自前で立てようと検討するはずです。そしてサーバーを立て、依存するDBやStorageを用意し、PublicにHostingするのであればユーザ認証を行い、HTTPS対応も一緒に…と予想以上に準備が大変なことに気づくでしょう。本来やりたいのはインフラのメンテではなく実験管理なのに、と。そう、各人がインフラ構築やメンテをする必要はなく、共通で1つの実験管理サービスをチームやゼミで使い回せたらそれでいいのです。早速始めましょう。

Architecture

今回はGCPを想定し、その全体構成図がこちらです。MLflowをGKE上に展開し、ユーザからのアクセスはCloud Identity Aware Proxyを通すことで、Whitelist形式のユーザ認証を実現します。更にIngressによるLoad BalancingによりUserやProject単位でMLflow Serviceごと分離します。依存するDBとStorageのInstanceとBucketは共通で持ちますが、DatabaseやStorage pathは前述したUserごとに分けます。これによりインフラリソースを必要最小限に保ちつつ、ユーザの実験管理スペースが干渉しない形を目指しています。これはEconomicsやCV、NLP、HCIと研究領域が多岐にわたるAI Labをベースにしているため、読者の皆さんの環境ではこの限りではなく、Userごとに分離しないシンプルな1 MLflow Serviceも良いと思います。

ハンズオン

ここからはTerraformの実コードを通じて、GCP上に前述した実験管理クラスタを立ててみます。コストの概算ですが、ベースがGKEとStorage、Cloud SQLで$76/month、ユーザ1人増えるたびに、+$10です。

https://cloud.google.com/products/calculator#id=5bffe958-d86b-43d7-844f-162bae9675fe

利用人数によっては可用性を下げ、Cloud SQLをServerのPersistent Diskに変更することで$76/month → $14/monthくらいに落とすことができます。

https://cloud.google.com/products/calculator#id=90e6e7bf-2b6b-48e0-964c-90c6e8e4eda5

値段感もわかったところで、GCPのProjectを用意しましょう。このハンズオンに使ってもらうのはおこがましいですが、初GCPの方は$300の無料トライアルクレジットが付与されるのでそれを利用しても良いでしょう。ここからはGithubのコードを使って進めますので適宜cloneして参照してください。

https://github.com/chck/mlflow-k8s

[mlflow-k8s] tree .
.
├── Dockerfile
├── Makefile
├── README.md
├── docker-compose.yml
├── infra
│   ├── 01_setting.tf
│   ├── 02_serviceaccount.tf
│   ├── 03_gke.tf
│   ├── 04_deployment.tf
│   ├── 05_network.tf
│   ├── 06_iap.tf
│   ├── 07_storage.tf
│   ├── 08_database.tf
│   ├── manifests
│   │   ├── backend_config.yaml
│   │   └── managed_certificate.yaml
│   └── secrets
├── poetry.lock
├── pyproject.toml
└── scripts
    ├── Makefile
    ├── poetry.lock
    ├── pyproject.toml
    └── tracking_sample.py

依存ツールのインストール

まずはREADME.mdにあるように、必要な依存をインストールしましょう。Macの例で申し訳ありませんが、LinuxやWindowsでも似たような方法で揃えられるはずです。

OAuthの設定

Architectureの説明で、「ユーザからのアクセスはCloud Identity Aware Proxyを通すことで、Whitelist形式のユーザ認証を実現」とあったと思いますが、その前提のOAuthの設定です。Cloud IAPは、GCPが用意しているOAuth認可によるProxyで、そのhostへアクセスした際にGoogle Loginが求められ、許可されたGoogle Accountでない限りはアクセスできないという仕組みです。

まずはOAuth同意画面を設定します。GCP Projectで1つなので、既に設定してある場合は飛ばしてもらって構いません。https://console.cloud.google.com/apis/credentials/consent から、EmailやApplication nameを入力し、保存すればOKです。なお、ここで設定した情報がApplicationの認証ページに表示されるため、公開情報のみ入力してください。

OAuth Credentialsのページに移動します。 
Create credentials → OAuth Client ID → Application type → Web applicationから、名前をmlflowとし、Createをクリックします。OAuth Client ID とClient Secretが生成され、OAuth Clientの画面に表示されます。
OK をクリックし、作成したClientを選択します。Client ID をクリップボードにコピーします。
Authorised redirect URIs フィールドに次の形式でリダイレクト URL を追加します。
https://iap.googleapis.com/v1/oauth/clientIds/CLIENT_ID:handleRedirect
ここで、CLIENT_ID は先程コピーした OAuth Client ID です。
ページの上部にある DOWNLOAD JSON ないしダウンロードアイコンをクリックし、JSONをダウンロードします。

このJSONはIAPの設定時にTerraformから参照するため、infra/secrets フォルダに oauth_client_secret.json という名前で置いておきましょう。なお、このフォルダは.gitignoreに指定しているので、mlflow-k8s repoで試す場合にcredentialsがgit remoteに上がる心配はありません。

ドメインの準備

Cloud IAPによるユーザ認証にはSSL/TLSが必要なので、任意のドメイン取得サービスから独自ドメインを取っておきましょう。会社や研究室でドメインが割り当てられている場合や既にドメインを持っている方は、そちらを使えば大丈夫です。本稿では仮にmlflow.sandbox.devとします。

Terraformの設定

次に、Terraformを介してGCPを操作するためのService Accountを作成します。TerraformとはGCPやAWSといったインフラの構築をコードで管理できるHashiCorp社製のOSSです。ポチポチで構築した場合と比べ、構築手順の再現性や設定の可視性という点で優位なため、今回はこちらを利用していきます。用意していただいたGCPの画面の左側のメニューからIAM & Admin → Service Accountsと進み、Create service accountから、Terraform用のService Accountを作成しましょう。本稿では mlflow-terraform と名付けますが、目的や所属によってxxx-terraformなど、何を司るaccountなのかがわかる名前にすると良いでしょう。

 

無事に作成できたらService Account 一覧に出てくるので、右の︙からManage keys → Add key → Create new keyからJSON形式のPrivate keyをDownloadします。

これをTerraformから参照することで、GCPの各サービスへのアクセスを可能にします。Privateとあるように、GCPへのアクセス権のあるKeyなのでPublicには上げないようにしましょう。このService Accountにはまだ何のRoleも付与していないので、Terraformが期待するRoleをIAMとして付与します。Service Accountとして発行されたEmail(mlflow-terraform@your-project-id.iam.gserviceaccount.com)に、IAM → Add から EditorとSecurity Admin の 2 Roleを付与します。ハンズオン的に簡単のため、この2つを付与していますが、Roleは必要最小限が基本なので、強すぎると思った方は適宜調整してください。

 

ここでREADME.mdに戻ります。Get Startedという項の通りに、.env.exampleをcopyして.envを編集します。

まずYOUR_GCP_PROJECTは自身のGCPのProject IDに置き換えます。
GCPのProject IDは、https://console.cloud.google.com/home/preview?project=your-project-id のproject=以下の部分のように、GCP ProjectのURLやGCPのDashboardから確認することができます。GOOGLE_APPLICATION_CREDENTIALS=/path/to/gcp-terraform-credentials.jsonには先程ダウンロードしたJSONの絶対パスを記述します。
TF_GCS_BUCKETについて、Terraformでは管理下のインフラの状態をstateとして持ち、構築時にはstateとの差分更新という戦略を取るのですが、今回そのstateの置き場所にGCSを使います。Bucketは何でもいいのですが、慣習的にbucketには-tfstateというsuffixをつけ、YOUR_PREFIXの部分はmlflowなり、mlflow-k8sなりに置き換えてください。(TF_GCS_BUCKET=YOUR_PREFIX-tfstate)
MLFLOW_GCS_BUCKETは、Terraformのstateが置かれるTF_GCS_BUCKETとは異なり、MLflowで実験管理する上でArtitectと呼ばれる画像などのバイナリ置き場になります。別のバケット名を命名しましょう。
MLFLOW_DOMAINには、ドメインの準備の項で用意した各々のドメインを記述します。本稿ではmlflow.sandbox.devとしています。

ここまで変更できたらREADMEで入れたdirenvの direnv allow . コマンドによって.envに定義した環境変数をそのディレクトリ下にいる間、自動で読み込むことができます。export要らずで便利ですね。

次に、Terraformの実体であるtfファイルに皆さんに用意して頂いた環境変数依存な部分があるので、以下のコマンドで置換します。Makefile内にmake tfコマンドの実体がありますが、内部で使っているsedのsyntaxはGNU sedではなくMac sedなので、お手元の環境によっては差し替えてください。

make tf

make tf までできたらTerraformを動かす前に、前述したterraformのstateを管理するために、GCSのBucketを作ります。Makefileに作成処理をまとめているので次のコマンドを叩きましょう(依存ツールのインストール項、要Google Cloud SDK)。

make bucket

無事にBucketが作られたら、infraフォルダに移動し、Terraform コマンドを叩いてみましょう。Terraformの依存packageとstateファイルの準備が行われます。

cd infra
terraform init

stateファイルができたところで、インフラ構築の実行計画を以下のDry-Runコマンドで確認します。

terraform plan

30近いリソースがAddされることがわかります。これらはinfra以下の各tfファイルを参照しており、各リソースの設定が記述されています。あとは以下の実行コマンドを叩いて内容を確認後console上でyesを入力し、数十分待つだけで実験管理クラスタが立ち上がります。

terraform apply

初回実行時に、以下のようにAPIが無効なエラーが出た場合は、ログで案内されているURLから有効化し、再度applyコマンドを実行してください。おそらくCloud DNS、Cloud SQLあたりで出ると思います。

Error: Error, failed to create instance mlflow-k8s: googleapi: Error 403: Cloud SQL Admin API has not been used in project xxx before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/sqladmin.googleapis.com/overview?project=xxx then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry., accessNotConfigured

また、GKEクラスタが立ち上がったら Makefileのあるdirectoryで make creds を叩くとローカルにGKEクラスタの設定を読み込んでくれるので、kubernetesに面識がある方はkubectlコマンドからdebugも可能です。ネットワーク周りが怪しい場合はGKEに割り振られたPublic IPとDNSの紐付けなど、用意したドメインのDNS周りの設定を見直してみましょう。Timeoutになった場合は、インフラが立ち上がりきっていない可能性があるので、数十分待ってから再度applyコマンドを実行してみてください。

Terraformによるインフラ構築を待っている間、各リソースごとにポイントを紹介します。はじめに、Terraformファイルの読み方を簡単に説明します。.tf拡張子が対象で、ファイルの分け方に厳密なルールはありませんが、今回はわかりやすくするためにGCPのサービスごとに分けています。

[mlflow-k8s] tree infra
infra
├── 01_setting.tf
├── 02_serviceaccount.tf
├── 03_gke.tf
├── 04_deployment.tf
├── 05_network.tf
├── 06_iap.tf
├── 07_storage.tf
├── 08_database.tf
├── manifests
│   ├── backend_config.yaml
│   └── managed_certificate.yaml
└── secrets
    └── oauth_client_secret.json

01_settings.tfを開いてみましょう。

terraform {
  required_version = "~> 1.0.0"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 3.77.0"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 3.77.0"
    }
    local = {
      source  = "hashicorp/local"
      version = "~> 2.1.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.1.0"
    }
    kubectl = {
      source  = "gavinbunney/kubectl"
      version = "~> 1.11.1"
    }
    postgresql = {
      source  = "cyrilgdn/postgresql"
      version = "~> 1.13.0"
    }
  }

  backend "gcs" {
    bucket = "_YOUR_BUCKET"
    prefix = "mlflow-k8s"
  }
}

variable "common" {
  type = map(string)
  default = {
    project = "_YOUR_PROJECT"
    region  = "asia-northeast1"
    zone    = "asia-northeast1-c"
  }
}

locals {
  common = var.common
  users  = var.users
}

variable "users" {
  type = set(string)
  default = [
    "user1", "user2", "projectz",
  ]
}

provider "google-beta" {
  project = local.common.project
}

provider "google" {
  project = local.common.project
}

JSONのような構文で、terraformやvariable、locals、providerといったkeyのようなものがterraformの予約語です。01_settings.tfでは、その名の通りTerraformの設定や依存、全体で使いそうな変数をまとめて定義しています。terraform句で操作したいリソースに必要なパッケージをVersion付きで指定します(requirements.txtのようなもの)。今回はGCPでMLflowを動かしたいという目的なので、googleやkubectl、postgresqlといった依存を加えています。backend “gcs” という部分は、Get Startedで説明したterraformのstateファイルの置き場所を指定しています(AWSでやるならbackend ”s3”ですね)。

variable句はterraform内で参照できる変数の定義で、typeによってhashmap型だったりset型など自由に記述できます。ここではGCPのProject IDやregionの他、複数人でのMLflowを想定しているので、そのUser IDをまとめています。参照するときにはvar.common等で呼べる他、locals句を挟むことでaliasのように、local.commonで呼べるようになります。今回の例ではlocalsの恩恵は少ないのですが、例えばdevとprdで変数だけ一部変更して同じインフラ構成にしたい場合、以下のように記述すると、TerraformのWorkspace機能を利用して1つのterraformファイルで挙動を変えることができるようになります。

variable "common" {
  type = map(map(string))
  default = {
    dev = {
      project = "_YOUR_DEV_PROJECT"
      machine_type = “e2-micro”  # コスパ重視
    }
    dev = {
      project = "_YOUR_PRD_PROJECT"
      machine_type = “e2-highmem-2”  # 性能重視
    }
  }
}

locals {
  common = var.common[terraform.workspace]
  users  = var.users
}

次に、07_storage.tfを開いてみましょう。

resource "google_storage_bucket" "artifact" {
  name          = "_YOUR_MLFLOW_BUCKET"
  location      = "ASIA"
  storage_class = "MULTI_REGIONAL"
}

このresource句が実際に操作を行う構文で、Terraform側で用意されている “google_storage_bucket” resourceに対して、”artifact” という任意の名前をつけています。このresourceが必要とする引数(name、location、storage_classなど)を元に、Storageが作成されます。GCPのストレージ作成画面から必要項目を入力するようなイメージです(基本的にフォーム入力とGCloud SDK、Terraform Resource間の引数が対応しているはず)。

 

tfファイルには01_から08_とprefixをつけているものの、Terraform的には01から逐次的に実行されるわけではなく、terraform planやterraform apply時に配下のtfファイルをまとめて読み込んだ上で実行順を決定してくれます。

Terraformの概要が説明できたところで、ポイントを端折りつつ内容の解説に入ります。まずは02_serviceaccount.tfの resource “google_service_account_iam_binding” “wi” の roles/iam.workloadIdentityUser についてです。

resource "google_service_account_iam_binding" "wi" {
  service_account_id = google_service_account.sa.name
  role               = "roles/iam.workloadIdentityUser"
  members            = formatlist("serviceAccount:${local.common.project}.svc.id.goog[%s]", ["${var.deployment.k8s_namespace}/${var.deployment.ksa_name}"])
}

GKE上のNodeからManaged DBなどの他のGCP Serviceにアクセスするためには、a)対象Instanceを同一VPCネットワークに置く方法、b)Service AccountのCredentialをNodeに置いて認証、c)Workload Identityによる認証、の大きく3パターンがあります。設定の煩雑さやセキュリティ上の観点から、Googleはc)Workload Identityを推奨しているため、本稿ではその方法を取ります。Workload Identity自体の説明は本稿の目的と離れてしまうため割愛します。

次に、03_gke.tfの resource “google_container_node_pool” “preemptible_nodes” のnode_config内、preemptible = trueです。

node_config {
    preemptible  = true
    machine_type = var.gke.machine_type
    disk_size_gb = var.gke.disk_size_gb
...

GCPのPreemptible Instanceとは、従来のinstanceと比べ24時間以内に1度は落ちる等、可用性を落とすことで最大8割OFFという格安で利用できるinstanceです。安定性が求められるようなProduction Serviceでの利用は向いていませんが、今回のような用途ではコスパ最強です。GCPのマネージドサーバーにはGKEの他にApp EngineやCloud Runがありますが、唯一このpreemptibleが利用できるGKEを選ぶ利点の一つにこれがあります。[2]

04_deployments.tfの resource “kubernetes_deployment” “mlflow” です。Kubernetes Clusterを構成するMLflow Containerの実体を定義しています。

resource "kubernetes_deployment" "mlflow" {
  for_each = local.users
  depends_on = [
    google_container_cluster.primary,
    kubectl_manifest.backend_config,
    kubernetes_service.internal,
  ]
  metadata {
    name = "mlflow-${each.value}"
  }
  spec {
    selector {
      match_labels = {
        tier = "mlflow-${each.value}"
      }
    }
    template {
      metadata {
        labels = {
          tier = "mlflow-${each.value}"
        }
      }
      spec {
        service_account_name = kubernetes_service_account.ksa.metadata[0].name
        node_selector = {
          "iam.gke.io/gke-metadata-server-enabled" = "true"
          "cloud.google.com/gke-nodepool"          = google_container_node_pool.preemptible_nodes.name
        }
        container {
          name  = "mlflow"
          image = "chck/mlflow:1.17.0-patch1"
          args = [
            "--static-prefix=/${each.value}",
            "--backend-store-uri=postgresql://$(DB_USER):$(DB_PASS)@$(DB_HOST)/u_${each.value}",
            "--default-artifact-root=gs://${google_storage_bucket.artifact.name}/${each.value}/mlruns",
            "--gunicorn-opts=--worker-class=gevent --access-logfile=- --timeout=180 --log-level=debug",
          ]
          port {
            container_port = 5000
          }
          env_from {
            secret_ref {
              name = kubernetes_secret.mlflow_secret.metadata[0].name
            }
          }
          readiness_probe {
            http_get {
              path = "/health"
              port = 5000
            }
          }
        }
        container {
          name    = "cloud-sql-proxy"
          image   = "gcr.io/cloudsql-docker/gce-proxy:1.23.1-alpine"
          command = ["/cloud_sql_proxy", "-instances=${var.common.project}:${var.common.region}:${var.database.instance_name}=tcp:5432"]
          security_context {
            run_as_non_root = true
          }
        }
      }
    }
  }
}

2行目の for_each = local.users により、for_eachのついたresourceはlocal.usersの分だけ繰り返され、結果的に各user(今回はuser1, user2, projectz)のresource “kubernetes_deployment”を1度に作成できます。

参照するdocker imageは、MLflow Tracking Serverにstatic-prefixというserver rootを変更するoptionを付けて立ち上げた場合に、mlflow clientからapiが届かない問題を解消するpatchを当てたものです。–static-prefix=/user1 という引数がuser毎に切り分ける実験管理クラスタの実験スペースの1pathを示します。backend-store-uriとdefault-artifact-rootはMLflowの依存middlewareを指定していて、DB_USERなど環境変数化されている部分は、env_from.secret_ref 項のkubernetes_secretからセキュアに参照されます。

同deployment上にもう一つ、cloud-sql-proxyというcontainerも定義されています。

container {
  name    = "cloud-sql-proxy"
  image   = "gcr.io/cloudsql-docker/gce-proxy:1.23.1-alpine"
  command = ["/cloud_sql_proxy", "-instances=${var.common.project}:${var.common.region}:${var.database.instance_name}=tcp:5432"]
  security_context {
    run_as_non_root = true
  }
}

これはMLflowのbackend-store-uri、DBとして利用するCloud SQLへの接続を、Workload Identityとこのproxyによって実現しています。[3]

次に 05_network.tfの resource “kubernetes_ingress” “mlflow” です。

resource "kubernetes_ingress" "mlflow" {
  depends_on = [
    google_container_cluster.primary,
    google_compute_global_address.mlflow_ip,
    kubectl_manifest.managed_certificate,
  ]
  metadata {
    name = "mlflow-ingress"
    annotations = {
      "kubernetes.io/ingress.allow-http"            = "false"
      "kubernetes.io/ingress.global-static-ip-name" = google_compute_global_address.mlflow_ip.name
      "networking.gke.io/managed-certificates"      = var.network.certificate_name
    }
  }
  spec {
    rule {
      http {
        dynamic "path" {
          for_each = local.users
          content {
            path = "/${path.value}"
            backend {
              service_name = "service-${path.value}"
              service_port = 5000
            }
          }
        }
        dynamic "path" {
          for_each = local.users
          content {
            path = "/${path.value}/*"
            backend {
              service_name = "service-${path.value}"
              service_port = 5000
            }
          }
        }
      }
    }
  }
}

Architectureの説明に、「ユーザからのアクセスはCloud Identity Aware Proxyを通すことで、Whitelist形式のユーザ認証を実現」とあったと思いますが、その定義がこちらです。再度の説明になりますがIAPは、GCPが用意しているOAuth認可によるProxyで、許可されたGoogle Accountでない限りはアクセスできないという仕組みです。GKEクラスタの管理側はUI上でアクセス可能なUserないしGoogle GroupsによるメーリスをIAP-secured Web App Userとして適宜追加することができます。

data "kubectl_path_documents" "backend_config" {
  pattern = "./manifests/backend_config.yaml"
  vars = {
    backend_config_name = var.iap.backend_config_name
    secret_name         = var.iap.secret_name
  }
}
resource "kubectl_manifest" "backend_config" {
  depends_on = [
    google_container_cluster.primary,
  ]
  count     = length(data.kubectl_path_documents.backend_config.documents)
  yaml_body = element(data.kubectl_path_documents.backend_config.documents, count.index)
}

 

今回のGKEクラスタのKubernetesリソースへのアクセス順はざっくり、Ingress(SSL, LoadBalancing) → Service(IAP) → Node(MLflow Container)ですが、そのKubernetes Service(IAP) に紐付けるために予め作成しておいたOAuth用のclient.jsonをkubernetes backend configに登録しておきます。04_deployment.tfや05_network.tfでは resource “kubernetes_deployment” や resource “kubernetes_ingress” などのTerraform builtinのresourceを用いて定義しましたが、backend configはTerraform由来のresourceが用意されていないため、kubernetes従来のyamlによる定義を行います。これは有志によるkubectlのTerraform Providerを利用していて、Terraformはこのように必要に応じてPluggableにProviderをUserが実装できるのも魅力です。

以上でtfファイルの説明は終わりです。さて、そろそろ terraform apply も終わりMLflowクラスタが構築されたか確認してこの記事を締めます。まずは 指定したdomainを含めたURLである $MLFLOW_DOMAIN/user1 にブラウザからアクセスし、MLflowのTop画面が表示されるかを確認しましょう。MLflowの画面ではなく、以下のようなBlocking画面が出たらIAPが働いている証拠なので心配無用です。

https://console.cloud.google.com/security/iap からBackend Services → service-user1 をクリックし、右に現れるメンバー編集UIから今自身がアクセスしているGoogle AccountをIAP-secured Web App Userとしてメンバー追加しましょう。追加できたら反映されるまで一息待ってから再度元のURLにアクセスし、以下のようなMLflowの画面が現れたら成功です。

実験結果を共有する側のメンバーはGoogle Groupsのメーリスなりでまとめておいて、そのメーリスのアドレスをIAP-secured Web App Userに指定すると、グループに対してアクセス権を付与できるので便利です。共有設定はこれでOKで、ログの飛ばし方を説明します。ブラウザ表示とは別のアクセスルートを辿るため、IAPを許可する情報も新たに設定します。

アクセス用Service Accountの設定

先程TerraformからGCPを操作するためのService Accountを設定しましたが、今度はMLflow ClientからServerにアクセスするためのService Accountを作ります。まずは同じようにIAM -> Service Accounts -> Create service accountと進み、mlflow-k8s という名前で作成します。roleは、ArtifactのUploadのためにStorage Object Creator を付与します。更に、Cloud IAPの設定ページから default/service-user1 に対し、該当のService AccountにIAP-secured Web App User を付与しましょう。

そして同じようにService Account 一覧の︙からManage keys → Add key → Create new keyからJSON形式のPrivate keyをDownloadし、その絶対パスをメモしておきます。

実験ログのトラッキング

ここまでできたらmlflow-k8sのscriptsフォルダに移動します。

cd scripts

必要情報が3つあるので.env.exampleを参考に、.envに環境変数を追加します。mlflow-k8s rootの.envとは別物なので注意しましょう。

cp .env.example .env

vi .env

=================
MLFLOW_CLIENT_ID=YOUR_MLFLOW_CLIENT.apps.googleusercontent.com
MLFLOW_TRACKING_URI=https://${YOUR_MLFLOW_DOMAIN}/user1/
GOOGLE_APPLICATION_CREDENTIALS=YOUR_LOCAL_CREDENTIAL_PATH

MLFLOW_CLIENT_IDには、terraform設定時にセットしたinfra/secrets/oauth_client_secret.json内のweb.client_idをコピペしましょう。MLFLOW_TRACKING_URIは、今回DeployしたMLflowのURL/user1/に、GOOGLE_APPLICATION_CREDENTIALSは先程メモしておいたToken key.jsonの絶対パスを使います。.envが設定できたら、依存をインストールし、tracking_sample.pyを実行してみます。

direnv allow .
poetry install
poetry run python tracking_sample.py

エラーなく実行されたら[4]、MLflowの画面に戻って、clientからlogが飛ばせているか確認して、この記事を締めたいと思います。tracking_sample.pyを動かす前と比べ、送ったログが表示されていることがわかります。

終わりに

今回対象にした実験管理は、ML Lifecycleにおけるほんの一部分です。データやモデル、パイプライン自体の共通管理については次回にご期待ください。この設計は一例なので、各々のライフスタイルに合わせてカスタマイズし、快適な実験生活を送りましょう。なお、ハンズオンをここまでお試し頂いた方はありがとうございました。大体理解できたのでインフラを一旦消したい、という場合は terraform destroyでapplyしたリソースをまとめて削除できます。PyData.Tokyoを通じてAI LabのMLOps事例として本件も解説している、CyberAgent AI Labを支えるCloud実験環境もぜひご覧ください:)


  1. 前回の記事で少しだけ紹介しました ↩
  2. 今回はpreemptible nodeで安く長くをテーマにしていますが今年出たばかりのGKE Autopilotも気になります ↩
  3. この方針だとコネクションがPod数だけ増えて気になる場合は、Service経由で接続するように変更しても良いでしょう(参考: https://unching-star.hatenablog.jp/entry/2019/02/22/000154) ↩
  4. 権限周りのエラーが出た場合は、GOOGLE_APPLICATION_CREDENTIALSで指定しているService Accountのroleを見直してみてください ↩
]]>
Google Cloud Pub/Sub にアップロードしたログを Logstash を使って Elasticsearch に流す https://cyberagent.ai/blog/tech/gcp/10211/ Sat, 09 Dec 2017 15:00:36 +0000 https://cyberagent.ai/techblog/?p=3856 みなさんこんにちは。

アドテクスタジオの CIA (Central Infrastructure Agency)でプロダクトサポートとしてインフラの構築・運用を行なっている浦野(@lanocci)と申します。

 

この記事は、CyberAgent Developers Advent Calendar 2017 10日目の記事です。

 

私が構築に携わっているプロダクトでは、GCP 上にシステムを構築しており、ログデータを流通させるために GCP の Cloud Pub/Sub (以下、 Pub/Sub)を利用しています。

Pub/Sub を使うと、一つのアプリケーションから発生したログを複数のサブスクライバーに配布することができるので、ビッグデータ解析のために BigQuery へログをロードさせつつ、 Elasticsearch にも並行してデータを流し、 Kibana を使ってログデータを可視化しておくというようなこともできます。

今回は Pub/Sub から Elasticsearch にデータを流す時に便利な Logstash の Google_pubsub input plugin をご紹介したいと思います。

 

モチベーション・本記事の概要

導入部でご説明した内容と一部重複しますが、 Logstash の Google_pubsub input plugin を導入したモチベーションとしては

  • GCP を使ってサービスを構築していて、 Pub/Sub にログデータをアップロードしており、そのログの可視化を Elasticsearch / Kibana で実現したい
  • 別途バッチジョブを用意して対応することもできるが、 Logstash に Pub/Sub から入力を受け付けるプラグインが用意されている→これを使えば簡単に実現できそう

といったものです。

実際、プラグインの導入はコマンド一つ実行するだけですし、プラグインを利用するための設定もシンプルなのですが、私たちのプロダクトで利用する上ではパフォーマンスの観点で気をつけないといけない点がありました。

本記事は、プラグインの導入方法についてと、私の経験を交えた注意点の二本立てでご紹介いたします。

 

本題に入る前に

Logstash 自体についてのご紹介ができていなかったので、ここで簡単にご説明いたします。

Logstash は、 Elasticsearch で有名な Elastic 社が提供するログ収集ツールで、複数のログの発生元( input )を監視し、発生したログに対してフィルタ処理( filter )を行なった上で様々なデータストアに出力( output )することができます。

Elastic 社が提供しているツールであるということもあり、 Elasticsearch への出力がデフォルトでサポートされていることが特徴の一つです。

詳細は公式サイトをご参照ください。

なお、本記事では GCE のインスタンス上で Linux OS(Debian)  Elasticsearch: 6.0 / Logstash 6.0 を利用することを前提としています。

 

プラグインの導入

Elasticsearch, Kibana, 及び Logstash 自体のインストールについては Elastic 社公式ページにわかりやすく記載されていますのでここでは割愛させていただきます。

 

Logstash に Google_pubsub input プラグインの導入するための作業としては、以下のコマンドを実行するだけです。

$ bin/logstash-plugin install logstash-input-google_pubsub

なお、参考リンクの手順通りに Logstash をインストールしていれば、上記コマンドの bin/logstash-plugin のフルパスは /usr/share/logstash/bin/logstash となっておりますので適宜読み替えていただければと思います。

 

Pub/Subからの入力の設定

Logstash でログを処理するためには、「パイプライン」を定義する必要があります。Structure of a Config File | Logstash

パイプラインには “input”, “filter”, “output” がありますが、今回の Google_pubsub input plugin は Pub/Sub からの “input” を受け付けるプラグインです。

(他にもStackdriverにログを転送する機能を持っていたりもしますが、今回は割愛します)

今回利用する “input” 定義の例は以下の通りです。

input {
    google_pubsub {
        project_id => "your-gcp-project"
        topic => "your-pubsub-topic"
        subscription => "your-pubsub-subscription"
        # json_key_file => "/path/to/your/credential.json"
    }
}

input としたい Pub/Sub トピックとサブスクリプション、及びその Pub/Sub が属しているプロジェクトの ID を指定するだけで特定のトピックに Push されたログを Pull してくることができます。トピックはすでに存在しているものから指定する必要がありますが、サブスクリプションは同名のものがなければ自動で作成してくれます。

なお、今回はコメントアウトしていますが、 GCP 外に logstash を構築した場合は Pub/Sub と連携するために、 GCP にログインするための鍵情報が必要となりますのでご注意ください。

 

パフォーマンス上の注意点

今回このプラグインを導入したプロダクトはまだ開発中ですが、先日負荷試験を実施した際に秒間50件のログを5分間流したところ、 Logstash がログを処理しきるのに15分程度かかってしまっていました。一秒間あたり20件弱程度しか処理できていない計算になります。

さすがに秒間20件では本番運用に耐えられません。

結論から言えば、このプラグインがデフォルトで5件ずつしか Pub/Sub から Pull してこないということがボトルネックとなっていたことが原因で、これを十分に大きな値に変えることで、確認できている限りで秒間1,000件程度を処理できるようになりました。

修正したパイプライン定義は以下のようになっています。

input {
    google_pubsub {
        project_id => "your-gcp-project"
        topic => "your-pubsub-topic"
        subscription => "your-pubsub-subscription"
        # json_key_file => "/path/to/your/credential.json"
        max_messages => "1000"
    }
}

最終行の max_messages が Pub/Sub から一度に Pull してくるメッセージ数の上限を定義しています。

マシンスペックや Logstash の設定にもよる可能性はありますが、確認できている限り、このプラグインでは秒間2~3回 Pub/Sub に対してリクエストを投げるようになっているため、トラフィックに合わせて一度に Pull する量を調整しないとどんどん Pub/Sub にログが累積し続けることになるため、ここで調整をしていく必要があります。

チューニング後、ログを Kibana で可視化した結果(秒間1,000件を処理)

まとめ

今回の記事では Cloud Pub/Sub から Elasticsearch にログをロードするための仕組みとして Logstash の Google_pubsub input plugin をご紹介し、パフォーマンスを上げるための注意点を簡単に共有させていただきました。

ログ収集ツールのパフォーマンスチューニングを行う場合、インプット元、アウトプット先の処理性能にも影響を受けるので診断が難しいことが多いかと思います。

本編ではご紹介しておりませんでしたが、 Logstash での処理が遅いかもしれないと疑うような場面では、 Logstash のスループットを確認できる、 “metrics” という機能が Logstash に付属していますので、 こちらを参考に、計測したいパイプラインに対して metrics を導入してスループット計測をしてみることをお勧めします。

また、本編の最後でご紹介したような、インプットの処理を高速化した後は、後続の処理にかかる負荷が大きくなってきます。

その影響で処理が遅くなっているようでしたら、こちらのガイドを参考に Logstash のスレッド( worker )数や一度に処理する queue の大きさをチューニングすると良いでしょう。

内部構造についてイメージを掴みたい場合は、こちらのブログに少し情報が古いかもしれませんが Logstash の queue や worker の関係が図で紹介されているのでヒントになるかと思います。

Google_pubsub input plugin は Pub/Sub と Elasticsearch を連携させて使いたい場合、設定がシンプルで簡単に導入できるツールだと思いますので、ぜひ機会があれば使ってみてください。

]]>
[Google Cloud Platform] エンジニアによる GCP 記事の総まとめ https://cyberagent.ai/blog/tech/gcp/2233/ Tue, 29 Nov 2016 04:40:30 +0000 https://cyberagent.ai/techblog/?p=2233 みなさんこんにちは。 アドテク本部の makocchi です。

10月から約 2 ヶ月間の間、Google Cloud Platform(以下GCP) について実際に社内のエンジニアに触ってもらい、使用感や感想を blog に書くということをやってまいりました。

なぜこのようなことをやったのかと言いますと、今回の目的として社内のエンジニアに GCP に慣れてもらうということがありました。

既にご存知の通り東京 GCP リージョンも開始され、今後 GCP の利用機会が増えていくだろうと見込みエンジニアとして 1 度は触っておかないとね!ということで、今まで触ってきたエンジニアはもちろん触ってこなかったエンジニアにも実際に GCP を触ってもらったというわけです。

 

結果的に記事の数は 11 になりましたので、ここで再度紹介させて頂きます。

記事が公開された順番に行きましょう。

 


%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-14-%e5%8d%88%e5%be%8c6-10-39

[Google Cloud Platform] Cloud Pub/Sub, Apache Kafka 機能比較 – これは利用すべきか?

 


geo_imagery_datasets

[Google Cloud Platform] public dataset の地理空間画像から日本の画像を探してみよう

 


gcp%e3%83%ad%e3%83%bc%e3%83%89%e3%83%90%e3%83%a9%e3%83%b3%e3%82%b7%e3%83%b3%e3%82%b0

 

[Google Cloud Platform]マルチリージョン対応のGCPのロードバランシング機能を試してみた

 


gcp-vpn

[Google Cloud Platform]簡単にできるGCPのVPN設定方法の紹介

 


%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-27-%e5%8d%88%e5%be%8c1-23-25

[Google Cloud Platform] GKEとSpinnaker

 


thumbnail

[Google Cloud Platform] Cloud Dataflowでフルマネージドなデータ処理を試してみた

 


google-cloud-network

[Google Cloud Platform]各 zone 毎の CPU 性能まとめ

 


eye2

[Google Cloud Platform] AWSしか使ったことがない私から見たGCE

 


gce_vs_ec2

[Google Cloud Platform] GCEはホントに速い?

 


fa6e7a57a3593f1e4d53d33265387fb4_s

[Google Cloud Platform] GKE と ECS を色んな角度から比較してみた

 


monohdddrive2013_tp_v

[Google Cloud Platform] 5分でわかる GCP のディスクのすべて with asia-northeast (Tokyo)

 


「使ってみた」「検証」「比較」といった内容から、「public dataset」「Spinnaker」というキワモノまでバラエティに富んだ内容になりました。

GCP について考える上で少しでも皆様の参考になれば幸いです。

 

これからも技術的な内容だけではなく、様々な情報発信をしていきますので今後もよろしくお願いします。

 

 

]]>
[Google Cloud Platform] 5分でわかる GCP のディスクのすべて with asia-northeast (Tokyo) https://cyberagent.ai/blog/tech/gcp/1430/ Thu, 24 Nov 2016 01:36:28 +0000 https://cyberagent.ai/techblog/?p=1430 こんにちは!
16新卒で6月から希望通り Central Infrastructure Agency (CIA) 配属となりました、
インフラエンジニアの青山 真也と申します。

今回は初テックブログデビューも兼ねて、GCP のディスク周りについてお伝えしたいと思います。

 

今回の検証内容

弊社のプライベートクラウド環境でもストレージを選定する際には、SDS がいいのか、NVMe のローカルストレージがいいのかなど、悩ましい検討をする必要がたくさんあります。そんな時はひとまず綿密な検証を行った上で、コストバランスを考えながら最適なアーキテクチャを選定していく必要があります。

この話は当然プライベートクラウドでも同じです。GCP にはディスクの種類がたくさんあり、それぞれのディスクの性能って一体どの程度パフォーマンスが出るのかを検証します。かつ、いよいよ東京にも新たに asia-northeast1-* Region (Tokyo) が追加されました。そこで各リージョンでディスク性能に差があるのかどうかも検証したいと思います。

 

まずは結論から!!

30 秒で理解したい人のために 3 行で結論も先に書いておきます。

1.東京リージョンのディスク性能はほんの少し良い

2.GCE の推定パフォーマンスの表示以上の性能はちゃんと出ている(特に低容量だと結構)

3.NVMe は一部リージョンの一部 I/O パターンや、RHEL 系だと性能が低い

詳しい根拠を知りたい方や、GCE のディスクの全てを知りたい方は、ぜひ読み進めてください。

 

GCE のディスクの種類

GCE には大きく分けて2種類のディスクが存在します。

1つ目は永続ディスク(ephemeral disk)と呼ばれ、ネットワーク越しの外部ストレージに実体が存在するタイプです。この永続ディスクには更に SSD と HDD の2種類が存在します。

2つ目はローカル SSD スクラッチディスクと呼ばれ、VM が動作するホストマシンのローカルディスクを利用するタイプです。ローカル SSD スクラッチディスクにも更に SCSI 接続と NVMe 接続の2種類が存在します。また、ディスクは 375 GB/ディスクデバイス 単位でしか増やせません。

以上をまとめると次のような分類となっています。

 

gce_disk

 

永続ディスクの場合には、インスタンス削除時にもディスク領域を保存しておくことができますが、ローカル SSD スクラッチディスクの場合はできません。しかし、ローカル SSD スクラッチディスクの場合はネットワーク越しではないためオーバーヘッドが小さく、非常に高い性能を出すことが可能です。

 

提示されるリージョン毎のディスク性能差はない

まず、HDD(375G, 10000G), SSD(375G, 1000G), SCSI(375G), NVMe(375G) それぞれの Google が提示する推定パフォーマンスを見ていきたいと思います。永続ディスクの場合にはディスク容量で性能差があるため、375G と最大性能を十分に出せる容量の2パターンで検証します。各リージョンでそれぞれの推定パフォーマンスを見てみると、すべてのリージョンで同じという結果となりました。外部ストレージの場合には QoS 制御を行うことで性能を調整することは可能ですが、果たして本当なのでしょうか…。

 

HDD (375 G) HDD (10000 G) SSD (375 G) SSD (1000 G) SCSI (375 G) NVMe (375 G)
Read 45 MB/s 180 MB/s 180 MB/s 240 MB/s – –
Write 45 MB/s 180 MB/s 180 MB/s 240 MB/s – –
Rendom Read 281.25 IOPS 3000 IOPS 11250 IOPS 15000 IOPS 100000 IOPS 170000 IOPS
Random Write 562.5 IOPS 15000 IOPS 11250 IOPS 15000 IOPS 70000 IOPS 90000 IOPS

 

 

NVMe インスタンスの注意点

NVMe インスタンスを利用する際には NVMe 互換イメージをブートソースとして利用するように下記のような警告が出ます。

NVMe では nvme-backports-debian-7-wheezy イメージまたはユーザー独自の NVMe 互換イメージをブートソースとして使用する必要があります。推定パフォーマンスは nvme-backports-debian-7-wheezy にのみ適用されます。

利用可能な Public NVMe 互換イメージは gce-nvme プロジェクトによって管理されており、gcloud コマンドで調べることができます。

# gcloud compute images list --show-deprecated --project gce-nvme | egrep "gce-nvme|^NAME"
 NAME                                                          PROJECT            FAMILY                    DEPRECATED  STATUS
 debian-8-jessie-v20160803-nvme                                gce-nvme           debian-8-nvme             DEPRECATED  READY
 debian-8-jessie-v20160923-nvme                                gce-nvme           debian-8-nvme                         READY
 nvme-backports-debian-7-wheezy-v20140904                      gce-nvme                                     DEPRECATED  READY
 nvme-backports-debian-7-wheezy-v20140926                      gce-nvme                                     DEPRECATED  READY
 nvme-backports-debian-7-wheezy-v20141108                      gce-nvme                                     DEPRECATED  READY
 nvme-backports-debian-7-wheezy-v20150423                      gce-nvme                                     DEPRECATED  READY
 nvme-backports-debian-7-wheezy-v20151104                      gce-nvme                                     DEPRECATED  READY

見て分かる通り、基本的には debian イメージしか NVMe のサポートは行われていません。(RHEL 環境での計測結果についても後述します)

実際にアタッチされた NVMe は/dev/disk/by-id/google-local-ssd-0 に実体がありますが、シンボリックリンクが貼られているため /dev/nvme0n1 を利用する形となります。これは物理環境と同じ命名規則ですね。

 

検証環境

今回は DEPRECATED ではない debian-8-jessie-v20160923-nvme(kernel: 4.7.0-0) を利用して検証します。また、fio は 2.1.11-2 を利用しました。

対象としたリージョンは次の6リージョンです。
下記のリージョンにてn1-standard-2 (vCPU:2, Memory 7.5 GB) を利用して、計測を行いました。
・asia-northeast1-c(NEW)
・asia-east1-c
・europe-west1-d
・us-central1-f
・us-east1-d
・us-west1-b

fio の設定は下記の通りです。bs と iodepth に関しては全パターンで計測し、収束した最高性能を測定値としました。

bs=? # 4k 16k 64k 256k
iodepth=? # 1 2 4 8 16 32 64 128 256 512
filename=? # /dev/sdb or /dev/nvme0n1
ioengine=libaio
direct=1
invalidate=1
fsync=0
end_fsync=1

 

検証結果

まずは結果から。結果は以下の通りになりました。

Read
libaio_read_read_bw_od1_ioc1

Write
libaio_write_write_bw_od1_ioc1

Random Read
libaio_randread_read_iops_od1_ioc1

Random Write
libaio_randwrite_write_iops_od1_ioc1

 

一部を除き、各リージョンでほとんどばらつきがないことが確認できると思います。asia-east-1cとus-central1-f の NVMe random read だけ著しく性能低下がありますね。

また、GCE の画面で表示される推定パフォーマンスと実測値を比べてみると、上記の 2 項目以外はしっかりと性能を担保していることが確認できました。中でも、低容量のディスクに関しては推定パフォーマンスよりも大きな向上が見られておりました。

 

よくありそうな質問

NVMe インスタンスをサポート対象外の RHEL 系の OS で利用するとどうなるか

さて、NVMe のサポートが行われた Debian イメージを利用してね。とのことですが、私は RHEL 派なのでどうしても CentOS や Scientific Linux で利用したいです。

というわけで、こちらも検証してみました。
検証対象は CentOS6.8(kernel: 2.6.32-642.4.2), CentOS 7.2.1511(kernel: 3.10.0-327.36.2) を対象としています。

こちらもまずは結果から。

all

Read と Write の結果を見たときは、性能が出そうだと思いました。しかし現実は甘くはなく、Random Read/Write では著しい性能低下が確認されました。

CentOS 7 と Debian では nvme.ko の version 1.0 、CentOS 6 では version 0.1 が入っているため、CentOS 7 では高い性能を発揮できるかと思ったものの…。RHEL 派としては、CentOS でも性能が出るようにチューニングや解決策を見出したいところです。

 

インスタンスグループでもローカルSSDスクラッチディスクは使えるのか?

残念ながらインスタンスグループでは利用できません。スケーリングさせるための機能なため、恐らくイメージ化されたブートディスク領域のみを利用していく運用を推奨しているのではないでしょうか。ここら辺の考え方はコンテナにストレージ領域が適さないのと少し似ていますね。

 

あとがき

GCP にもいよいよ東京リージョンがやってきましたね。
さすがに一番新しいリージョンだけあって、ディスクの性能も少しだけいい結果となりました。
NVMe に関しては、一部のリージョンの一部の IO パターンでのみ性能低下が見られたり、特定の Debian イメージしか性能がちゃんと出なかったりするため、まだ少し注意が必要かなという印象を受けました。

なお、今回の検証はインスタンスガチャ的なところまでは見ておらず、複数のインスタンスで実施したわけではないので、参考程度にお願いいたします。

]]>
[Google Cloud Platform] AWSしか使ったことがない私から見たGCE https://cyberagent.ai/blog/tech/gcp/1511/ Wed, 16 Nov 2016 10:10:30 +0000 https://cyberagent.ai/techblog/?p=1511 初めまして、アドテク本部でインフラエンジニアをしております山下といいます。

GCP東京リージョンの開設が近づいているこのタイミングで、GCPをネタに記事を書くことになりました。

GCPと言えば、先日ポケモンGoのバックエンドとして利用されていることでも話題になりました。

しかし、私自身GCPに全く触れたことが無く、まだ私にはパブリッククラウド=AWSのイメージがあります。

そこで、パブリッククラウドの基本的なサービスであり、必ずついて回るであろうIaaSにフォーカスし

AWSしか使ったことがない私がGCE(Google Compute Engine)を触ってみて感じたEC2との違いをまとめることにしました。

早速、私がGCEに触れてみて感じたAWS(EC2)との違いを一覧にまとめてみます。

 

比較表

項目 AWS(EC2) GCP(GCE)
サービス利用方法 AWSアカウントを作成 プロジェクトを作成
ユーザー AWSアカウントごとに必要なIAMを作成 Googleアカウントを必要なプロジェクトに適宜追加
SSH鍵 キーペアを作成し適宜共有

この時作成される公開鍵がインスタンス内に入っている状態で起動※1

GCEのメニューより

メタデータ -> SSH鍵からユーザ名/公開鍵を登録。

メタデータに追加したユーザと対になるユーザと公開鍵がインスタンス作成時に自動で設定される。

SSHログインユーザ ユーザはOSによって異なる(ec2-user or ubuntu etc…) メタデータに追加したユーザ名が起動時に作成される。
クローン AMIを取得(数分待つ)し、取得したAMIでインスタンスを作成 スナップショットを取得後、取得したスナップショットからインスタンスを作成。

インスタンス詳細画面にクローンボタンがあり1クリックすることで無停止でのインスタンス複製が可能

ただしストレージ周りはクローンされず、インスタンスの設定がクローン元と同じ空のインスタンスが作成される。

ネットワーク vpc作成時にあらかじめCIDRを設定し、CIDR範囲の中でサブネットを設定する vpcに相当するネットワークを作成。CIDRを指定する必要はない。

ネットワーク作成後好きなリージョンにサブネットを設定していく。

ファイヤーウォール Security Groupを作成し、インスタンスやELBなどの各リソースに適応する ファイヤーウォールを作成し、どのネットワークへ適応するかを選択。
またリソースにはタグ付けが出来、どのネットワークの・どのタグが付いているリソースへ適応させるか選択できる。
(ネットワーク全体への適応も可能)
ルートテーブル Routing Tableを作成し、サブネットへ適応 Routing Tableを作成し、ネットワークへ適応
Network ACL ACLを作成し、サブネットへ適応 なし

※1 Opsworksを使用すれば、IAMとIAMに設定した秘密鍵が設定された状態でインスタンスを作成することが可能です。

ざっとこんなところになります。この中でも特筆すべき点を抽出します。

Good

プロジェクト・ユーザ周り

GCPではちょうどGoogleドライブでスプレッドシートを共有するようなイメージで、プロジェクトへの招待(追加)が可能です。

また、プロジェクトへ追加した人に対してはメール通知を行うこともできます。

さらに利用者として見た場合、自分が参照できるプロジェクトを自由に行き来することができるため、アカウントを意識する必要が無くなりますので、以下の点がAWSより便利だと感じました。

  1. 管理者として
    1. AWSで新規アカウントを作成する際に、必要な人数分のIAMを作成する必要がある
    2. 作成したIAM情報の共有
  2. 利用者として
    1. AWSアカウントが複数ある場合、アカウント管理に工夫が必要
    2. AWSアカウントを行き来することはできず、複数のブラウザを使う必要がある

SSH鍵の管理

AWSは新しいエンジニアの方がジョインした場合、既存のキーペアを共有する必要がありますが、

GCPの場合、各々で鍵を登録できるため鍵を共有する側としては手間が省けて助かります。

迅速で無停止なクローン

EC2より早く、且つ無停止で複製できる点が素晴らしいです。

後述するライブマイグレーションの機能を使っているのでしょうか?

ただしここで言うクローンはインスタンスの設定をそのままに新規インスタンスを作成することを指しています。

本来の意味のクローンを行う場合はAWSと同じ要領でスナップショットを取得し、それを元にインスタンスを作成する必要があります。

注意

Network ACL

DDoSのような事態が起きて、緊急で特定のIP範囲からの接続を防ぎたい場合を想定するとACLが無いというのは不便です。

AWSではサブネット単位でACLの制御ができましたが、GCPはミドルウェア側で頑張るしかないのでしょうか。

今回はGCEしか使ってみていないので、もしかすると他の回避策があるのかもしれません。

ネットワーク

ポイントはネットワーク(AWSで言うところのVPC)内にリージョンを問わずサブネットを追加できる点だと思います。

これができることでリージョン間の通信も同一ネットワーク内であればプライベート通信が可能となっているようです。

そのネットワークですが、作成する際に以下2つのモードが選択できます。

公式ドキュメント

autoモード

  • 各リージョンに1つずつサブネットが自動生成される
  • 自動生成されるサブネットはユーザ側で追加、削除、変更ができない(CIDRも変更不可)

customモード

  • ユーザ側でサブネットの追加、削除を行う(追加時にはリージョンも選択可能)
  • サブネットワークが被らなければ、同じネットワーク内で好きなリージョンに自由にサブネットを追加できる。
  • 既存のネットワークで使用されているサブネットワークは、新規作成するネットワークでも使用可能。

他にもあった!良ポイント

調べているとGCEには他にも注目すべきポイントがありました。

自動割引

インスタンスの利用時間が毎月計算され、自動的な割引が発生し、1ヶ月間 24時間稼働させた場合、30%オフとなるようです。

ライブマイグレーション

ホスト障害等が発生した際にインスタンスを止めること無く無停止で別ホストに移動させることができます。

インスタンスタイプのカスタマイズ

インスタンスタイプのスペックをカスタマイズすることが可能なので、CPUに尖ったインスタンス・メモリに尖ったインスタンス…などといったものが作成できます。

コストアドバイス

インスタンスをしばらく起動し続けると一覧の画面上でリソースを使っていない旨の警告、及びおすすめのインスタンスタイプが表示されます。

advice_1

停止は伴いますが、その場でおすすめインスタンスタイプへの変更も可能です。

advice_2

AWSの場合はTrusted Advisorから得られるような情報でもひと目で把握できる点は非常に良いですね。

プリエンプティブルインスタンス(Preemptible VM)

24時間以内のどこかで終了することが決まっている分、コストの安いインスタンスです。(コア時間あたりわずか $0.01)

止まっても問題ない一時的な処理をする際の利用が考えられます。

また、インスタンスの作成画面からプリエンプティブルインスタンスの使用有無が選択できるため、AWSスポットインスタンスより手軽に利用できそうです。

UnixBench

さんざん各所でやり尽くされている内容と思いますがAWSとGCP 同等スペックのインスタンスでUnixBenchを取りました。

ただし、GCPはリージョンによってCPUの世代やスペックが微妙に異なるそうです。

東京リージョンのGCEではどうなるか分かりませんが、

東京から最寄りの台湾リージョンで立てたインスタンスと東京リージョンに立てたEC2でUnixBenchを取ってみました。

スペックと料金に関しては以下のとおりです。

なお、それぞれの料金は下記のページから算出しました。

AWS:https://calculator.s3.amazonaws.com/index.html

GCP:https://cloud.google.com/products/calculator/

AWS(EC2) GCP(GCE)
インスタンスタイプ m3.medium n1-standard
CPU 1 core

Intel Xeon E5-2670 v2 (Ivy Bridge)

1 core

 

メモリ 3.75GB 3.75GB
1ヶ月の料金 $ 70.28※1 $ 28.11※2 ※3

※1 EBSボリュームやEIP、データ転送は未入力で計算
※2 24時間を1週間フルで使う設定でAWSと同様にインスタンス以外の項目は未入力で計算。自動割引 30%引きが適応。
※3 日本リージョンの価格が出ました。台湾よりもやや高めの n1-standard で $33.88 です。

結果は以下です。

結論から言うとAWSに比べてGCPは1.5倍程度の性能が優れているようです。

私的には性能差以前にカタログスペックが同じなのであれば、半額以下で利用できるGCPを使ってみたいなと思いました。

EC2

# ./Run
:
:
System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   16767944.7   1436.8
Double-Precision Whetstone                       55.0       2062.2    374.9
Execl Throughput                                 43.0       2684.1    624.2
File Copy 1024 bufsize 2000 maxblocks          3960.0     690739.4   1744.3
File Copy 256 bufsize 500 maxblocks            1655.0     189510.3   1145.1
File Copy 4096 bufsize 8000 maxblocks          5800.0    1468204.9   2531.4
Pipe Throughput                               12440.0    1348736.8   1084.2
Pipe-based Context Switching                   4000.0     221731.6    554.3
Process Creation                                126.0       8068.3    640.3
Shell Scripts (1 concurrent)                     42.4       3466.3    817.5
Shell Scripts (8 concurrent)                      6.0        467.4    778.9
System Call Overhead                          15000.0    1940750.2   1293.8
                                                                   ========
System Benchmarks Index Score                                         949.8

GCE

# ./Run
:
:
System Benchmarks Index Values            BASELINE    RESULT INDEX
Dhrystone 2 using register variables      116700.0   35214310.9   3017.5
Double-Precision Whetstone                    55.0    4166.9 757.6
Execl Throughput                              43.0    4335.8   1008.3
File Copy 1024 bufsize 2000 maxblocks       3960.0  849444.9   2145.1
File Copy 256 bufsize 500 maxblocks         1655.0  238005.7   1438.1
File Copy 4096 bufsize 8000 maxblocks       5800.0 1945748.9   3354.7
Pipe Throughput                            12440.0 1561828.0   1255.5
Pipe-based Context Switching                4000.0  338198.5 845.5
Process Creation                             126.0   14503.5   1151.1
Shell Scripts (1 concurrent)                  42.4    5771.6   1361.2
Shell Scripts (8 concurrent)                   6.0     764.7   1274.4
System Call Overhead                       15000.0 2487150.4   1658.1
                                                               ========
System Benchmarks Index Score                                     1446.4

まとめ

GCP(GCE)はコスト面やリソースの自在さ、ライブマイグレーションなどAWSを意識していてかゆいところに手が届いているなと感じました。

とはいえ使っていく中でAWSに劣る点も見えてくるかと思いますし、AWSとGCPでネットワークに関する考え方の違いも今回知ることができました。

いよいよ東京リージョンがオープンするGCP!お試しとして1度本格的に使ってみたいと思いました。

]]>
[Google Cloud Platform]各 zone 毎の CPU 性能まとめ https://cyberagent.ai/blog/tech/gcp/2104/ Fri, 11 Nov 2016 00:44:39 +0000 https://cyberagent.ai/techblog/?p=2104 みなさんこんにちは。

アドテク本部の makocchi です。

 

みなさまもご存知の通り、東京 GCP リージョン(asia-northeast1)が正式に公開されました。

(東京 GCP リージョンってなんか言いにくいですよね・・つい GCP 東京リージョンって言っちゃいます)

 

GCP では各リージョン内に zone がある (asia-northeast1 であれば asia-northeast1-a/b/c) のですが、

zone を考える時に気をつけなければならないのは、同じリージョン内であっても zone で CPU の世代が違う(=性能が違う)ことがあるということです。

耐障害性を考えてアプリケーションを multi-zone で展開することはよくあることですが、同じアプリケーションなのに性能がバラつくということが出てくる可能性がありますので注意が必要です。

 

ではどの zone でどの CPU が使われているのかというと、実は GCP では情報として公開されています。

https://cloud.google.com/compute/docs/regions-zones/regions-zones#available

地域 場所 利用可能なゾーン 特長
米国西部 オレゴン州ザ・ダレス us-west1-a
us-west1-b
米国中部 アイオワ州カウンシル ブラフス us-central1-a
us-central1-b
us-central1-c
us-central1-f
米国東部 サウス カロライナ州バークレー郡 us-east1-b
us-east1-c
us-east1-d
西ヨーロッパ ベルギー、サン・ギスラン europe-west1-b
europe-west1-c
europe-west1-d
東アジア 台湾、彰化県 asia-east1-a
asia-east1-b
asia-east1-c
北東アジア 東京、日本 asia-northeast1-a
asia-northeast1-b
asia-northeast1-c

(2016年11月現在)

 

asia-east1 や asia-northeast1 は全ての zone で同じ世代ですが、eurepo-west1 では全ての zone で違ったりしています。

料金を考えて asia 圏より安い us の zone を選択している人もいると思うのですが、zone によって結構違うので気をつけて下さい。

 

それでは各 zone の CPU の性能を見てみましょう。

この値は各 zone で compute を立ち上げて /proc/cpuinfo から値を取得しました。

zone CPU MHz cache size(KB) CPU family CPU model CPU stepping microarchitecture
us-west1-a 2200 56320 6 79 0 Broadwell
us-west1-b 2200 56320 6 79 0 Broadwell
us-central1-a 2600 20480 6 45 7 Sandy Bridge
us-central1-b 2300 46080 6 63 0 Haswell
us-central1-c 2300 46080 6 63 0 Haswell
us-central1-f 2500 30720 6 62 4 Ivy Bridge
us-east1-b 2300 46080 6 63 0 Haswell
us-east1-c 2300 46080 6 63 0 Haswell
us-east1-d 2300 46080 6 63 0 Haswell
europe-west1-b 2600 20480 6 45 7 Sandy Bridge
europe-west1-c 2500 30720 6 62 4 Ivy Bridge
europe-west1-d 2300 46080 6 63 0 Haswell
asia-east1-a 2500 30720 6 62 4 Ivy Bridge
asia-east1-b 2500 30720 6 62 4 Ivy Bridge
asia-east1-c 2500 30720 6 62 4 Ivy Bridge
asia-northeast1-a 2200 56320 6 79 0 Broadwell
asia-northeast1-a 2200 56320 6 79 0 Broadwell
asia-northeast1-a 2200 56320 6 79 0 Broadwell

(2016年11月現在)

 

最新の Broadwell 世代が入っているのが us-west1 と asia-northeast1 になります。

CPU の clock だけを見ると Sandy Bridge の us-central1-a を選択するのが良さそうです。

もちろん CPU の性能は単純に clock だけで決まるわけではありません。

CPU は世代交代によって性能も改善されていっていますので、単純にこの数字だけを信じることはせずにあくまで参考程度に考えて頂ければと思います。

 

ちなみにどの CPU を使っているのかちょっと予想してみました。(あくまで予想です)

 

Broadwell

 

Haswell

 

Ivy Bridge

  • clock が 2.5GHz で cache が 30M だけど・・型番不明

 

Sandy Bridge

 

世代を新しくするにつれて cache が増えていってますね。clock よりも core 数や cache を重視している傾向がありそうです。

Ivy Bridge の CPU はちょっとわかりませんでした。多分カスタムオーダーで作っているかもしれませんね。

 

これらの CPU の情報は将来的に変更になる可能性があります。

最新情報は常にチェックしておきましょう!

]]>
[Google Cloud Platform] GKEとSpinnaker https://cyberagent.ai/blog/tech/gcp/1504/ Mon, 07 Nov 2016 01:00:01 +0000 https://cyberagent.ai/techblog/?p=1504 初めましてアドテク本部の李と申します。

今回は、Google Cloud Platform(GCP)を触ろうということでGoogle Container Engine(GKE) × Spinnakerというお題の基、GKEとSpinnakerの連携周りでどう設定すればいいのかを書こうと思います。

Spinnakerのドキュメントはまだまだ少ないのでこれからKubernetesとSpinnakerを触ってみようかな、もしくはSpinnakerのKubernetes周りの設定などでつまずいている方々の力になれればと思います。

まず、Kubernetesってなんなの??のおさらいです。Kubernetesについてまとめてみました。

 

概要

 

Kubernetesの概要を簡単に箇条書きにしました。

・複数のコンテナをpod単位でグルーピング

 -配備/削除、起動/停止を実行

 -pod作成時に使用するコンテナーイメージやCPU、メモリディスクスペースを指定

 -podの配備先を自動追尾、多重度の設定、スケールアウト

 -kubectlコマンドによりpodのイメージアップデートを自動化

・コンテナに割り当てるストレージの管理、IPアドレスの管理

・コンテナ間のネットワークルーティング管理

・複数のコンテナを利用した負荷分散

 -serviceを使用したリクエストのロードバランシング(内部的な処理)

・コンテナの監視(各種ヘルスチェック)

 -pod異常終了時の再配備

 -Webヘルスチェック

 -コンテナーExecヘルスチェック

 -TCP Socketヘルスチェック

 

構成

c8f7ab67-d952-9b1d-1b13-7a13deaf7473

pod

  ・Dockerコンテナをpodという単位で管理

  ・ストレージやIPアドレス等、複数のコンテナーのリソースをpod単位で共有

api server

  ・kube-apiserver、kube-schedulerで構成

  ・pod、Service、replicationcontrollerに対するRESTでの操作を受け付けてetcdに保存

  ・podのスケジューリングとpodの情報(Podの配置場所、ポート情報)とservice設定を同期

  ・kubecfg/kubectlコマンドにてREST実行

scheduler

  ・各ノードに対しコンテナを割り当て

  ・未スケジュールのPodをMinionに配置

replication controller

  ・podの多重度を設定し継続的に監視し調節

  ・pod数の調節を自動化

controller manager server

  ・コンテナの状態管理やノードの管理

  ・etcdにあるreplicationcontrollerオブジェクトを監視(watch)

  ・replicationを実行

minion

  ・Dockerコンテナ配置ノード

  ・minionの中では、Docker、kubelet、proxyが動作

  ・コンテナ数増加→minionを増加

kubelet

  ・minionをコントロールするエージェント(コンテナ作成/削除やボリュームの割り当て)

  ・master apiサーバから情報を受け取りcontainer manifestに従いコンテナを起動

proxy

  ・コンテナへのネットワークルーティングおよび負荷分散

  ・service設定によるTCP/UDPをPodに転送するためのネットワークプロキシ

service

  ・pod通信のエンドポイントを既定

  ・serviceはdocker空間のIPとポートを保持

  ・podはserviceにアクセスすることでPod間で通信を行う

  ・ロードバランサーの機能により、リクエストをPodに分散

  ・NodePort(外部からのアクセス)

   -ホストPortとリンク(= Docker expose設定)

   -LoadBalancerは外部のLBのIPアドレスを指定し通信を受け取る

kubectl

  API経由でKubenetesを操作するためのクライアントツール

 

ずらずら書きましたが、Kubernetesを使えば、コンテナクラスタが組めて、Dockerコンテナをグルーピングできて…要するにコンテナ環境を上手く管理できるよってことです。

このKubernetesコンテナクラスタを簡単に組めるのがGKEです。(もちろんLinux環境があれば問題なく組めます。) GKEではWeb UIもデフォルトで用意されています。接続方法はGKE → コンテナクラスタの接続から確認してください。

以下がWebUIです。YAMLファイルもしくはJSONファイルでデプロイするコンテナを設定できます。CUIとほとんど変わりません。ただコンテナのステータスやワークロードなんかも見れるので便利ですね。以前まではなかったように思いますが、最近変わった気がします。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-14-%e5%8d%88%e5%be%8c5-51-24

 

次にSpinnakerについてまとめておきます。

概要

Spinnakerの概要について箇条書きでまとめます。

・マルチクラウド対応の継続的デリバリープラットフォーム(Netflix製)

・アプリケーションのデプロイとクラスタの管理

・継続的デリバリーのプロセスを自動化

・デリバリープロセスを表す一連のパイプライン作成(アセットの作成から、デプロイで終了)

・パイプラインは拡張と再利用可能

・パイプラインの実行はコンパイルやJenkins、手動、cron等で開始(パラレルにもシリアルにも実行可)

・ビュー経由でデプロイしたクラスタのリサイズ、削除、停止

・Blue-Greenデプロイメントの切り替えも可能

 

機能

クラスタ管理

・基本構成はサーバグループ

 -複数のインスタンス、ロードバランサやセキュリティグループを含む

 -ソフトウェアが実行されているVMの集合体

・セキュリティグループ

 -IP範囲(CIDR)、TCP/UDP、ポート範囲のセットでアクセス制限

・ロードバランサ

 -サーバグループ内のインスタンス間のプロトコル、ポート範囲でバランシング

 -ヘルスチェック機能

 

デプロイ管理

・パイプラインは構成情報とステージで構成

 -構成情報にはトリガーやパラメータ、通知を含む

 -ステージではコンテキストなオペレーション定義(以下オペレーション)

  -Bake: 特定のregionにデプロイイメージ作成

  -Deploy: イメージのデプロイ

  -Destroy Server Group: サーバグループの削除

  -Disable Server Group: サーバグループの無効化

  -Enable Server Group: サーバグループの有効化

  -Find Image: 既存のクラスタにデプロイするイメージを検索

  -Jenkins: jenkinsジョブを実行

  -Manual Judgment: ユーザが承認するまでパイプラインの実行を待機

  -Modify Scaling Process: サーバーグループのスケーリング処理を一時停止/再開

  -Pipeline: パイプラインの実行

  -Quick Patch Server Group: サーバグループにパッチを適用

  -Resize Server Group: サーバグループをリサイズ

  -Script: 任意のシェルスクリプトを実行

  -Shrink Cluster: クラスタを縮小

  -Wait: 指定された期間待機

・通知

 -パイプラインの開始/完了/失敗に電子メール、 SMSまたはslack等に送信可

 

Spinnakerを構成しているマイクロサービス

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-21-%e5%8d%88%e5%be%8c4-24-45

deck

ユーザインターフェース

echo

通知サービスのメッセージングを制御

rosco

vmのイメージを作成

front50

パイプライン、通知およびアプリケーションのデータストア

clouddriver

・プラットフォームの統合
・プラットフォームの認証情報を管理

rush

汎用スクリプトエンジン、スクリプトの管理

orca

・ワークフローのオーケストレーションを提供
・pipelineとtaskの制御

gate

単一のエンドポイントとして異なるサブシステムのゲートウェイサーバとして動作

igor

jenkins、stash、およびGitHubへのインタフェース(ci連携)

 

Spinnaker…大体こんなところですかね。使いこなせればすごく便利ですよね?かなり多機能です。何といってもパイプライン制御でblue-greenデプロイができるのがいいです。

さらにデプロイもCloud Launcherからワンクリック?デプロイできます。今回はこれでデプロイしました。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-19-%e5%8d%88%e5%be%8c9-10-46

それでは、今回のSpinnakerの設定を見ていきましょう。現在、設定の詳細が書かれたドキュメントがあまり充実していないので、困っておられる方々は以下をベースにしていただけますとよろしいかと思います。

Spinnakerの設定

今回はDocker Hubの公開レポジトリを使用しています。もちろんGoogle Cloud Registry (GCR)も使えます。その際はこちらのGCRを参照していただくといいかもしれません。

Kubernetesを使用するには /opt/spinnaker/config/spinnaker-local.yml と /opt/spinnaker/config/clouddriver.ymlの編集が必要です。

以下に設定の変更点を記述します。まずは/opt/spinnaker/config/spinnaker-local.yml です。今回はKubernetesを使用するので、providersセクションのKubernetesとdockerRegistryを以下のように変更しました。

kubernetes:
    enabled: true (←kubernetesの使用の有無)
    primaryCredentials:
      name: my-kubernetes-account (←spinnaker上で使用するアカウント名:任意)
      dockerRegistryAccount: ${providers.dockerRegistry.primaryCredentials.name} (←以下のdockerRegistry:で使用するアカウント名)

  dockerRegistry:
    enabled: true (←Docker Registryの使用の有無)
    primaryCredentials:
      name: my-docker-registry (←spinnaker上で使用するアカウント名:任意)
      address: https://index.docker.io (←使用するdocker registryのaddress)
      repository: library/nginx (←使用するrepositry)

 

/opt/spinnaker/config/clouddriver.ymlは以下のように設定しています。

kubernetes:
  enabled: true
  accounts:
    - name: ${providers.kubernetes.primaryCredentials.name}
      dockerRegistries:
        - accountName: ${providers.kubernetes.primaryCredentials.dockerRegistryAccount}

dockerRegistry:
  enabled: true
  accounts:
    - name: my-docker-registry
      address: https://index.docker.io
      repositories:
       - library/nginx

設定完了後 restart spinnaker を実行します。

さて、SpinnakerのUIに接続してみましょう。SpinnakerのUIに接続するにはポートフォーワードする必要があります。ローカル環境で以下を実行してください。

gcloud compute ssh {Spinnakerが実行されているインスタンス名} --ssh-flag="-L 8084:localhost:8084" --ssh-flag="-L 9000:localhost:9000" --ssh-flag="-L 8087:localhost:8087"

完了後、 WEBブラウザから http://localhost:9000  でUIに接続ができます。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-19-%e5%8d%88%e5%be%8c9-18-28

ここまででSpinnakerを使用できる環境が揃いました。ここで注意すべきポイントがあります。

マイクロサービス群のログは全て/var/log/spinnaker以下に保存されています。clouddriverログに401 unauthorized errorが表示された場合、Kubernetesが動くGKEクラスタの認証情報が正確ではない、もしくはその情報を保持していない可能性があります。

Spinnaker(または構成するマイクロサービス群)はspinnakerユーザで実行されるため、/home/spinnaker/.kube/configをspinnakerユーザで作成しなければなりません。

その作成方法は以下のとおりです。

su - spinnaker
 gcloud container clusters get-credentials {Kubernetesが動いているGKEクラスタ} --zone {GKEクラスタがデプロイされたゾーン} --project {プロジェクト名}
 gcloud config set container/use_client_certificate true

完了後、 restart clouddriverを実行してください。ログを確認するとエラーが解消されます。以下のようにApplicationの作成画面でKubernetesとDocker Registryのアカウントが表示されていれば問題ないです。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-19-%e5%8d%88%e5%be%8c8-37-39

今回はここまでです。

Spinnakerは、参画されている企業様を見ても分かる通り、かなり期待できる継続デリバリープラットフォームです。
今後は、GKE × Spinnakerの利用もますます増えてきそうですね。

最後まで読んでいただきありがとうございました。

]]>
[Google Cloud Platform]簡単にできるGCPのVPN設定方法の紹介 https://cyberagent.ai/blog/tech/gcp/10183/ Fri, 04 Nov 2016 07:24:34 +0000 https://cyberagent.ai/techblog/?p=1553 こんにちは、アドテク本部でネットワークエンジニアをしている菊地です。
今記事では、弊社のプライベートクラウド環境とGCP間をVPN接続を試した際の設定方法について紹介します。

記事の流れはこのよう以下のようになっております。

  • GCPのVPNの特徴
  • GCPのVPNの設定方法
  • 設定がうまくいかない場合のトラブルシューティング方法
  • まとめ

このような流れで説明していきたいと思います。

 

★GCPのVPNの特徴

 

  • IPsecのEPSトンネルモードを使用している
  • サイト to サイトVPNをサポートしている
  • 鍵交換はIKEv1とIKEv2をサポートしている
  • IKEv1はIPsec通信を行う際に使用する暗号鍵を生成するための鍵交換プロトコルであり、IKEv2はIKEv1の拡張プロトコルです。IKEv1とIKEv2の間に互換性はなく、IKEv2はEAP認証等高度な認証が可能です。

  • ゲートウェイのIPアドレスのリージョンとVMインスタンスのリージョンは一致する必要がある

 

★GCPでVPNの接続設定を実施

 

1.Google Cloud Platformにログインし、ネットワーキングへ

ネットワーク→ネットワークを作成を押します
%e3%83%8d%e3%83%83%e3%83%88%e3%83%af%e3%83%bc%e3%82%af%e4%bd%9c%e6%88%90%e3%81%b8

 

2.ネットワークの作成画面へ

ネットワークの名前、サブネットワークの名前は共にプロダクト名やプロジェクト名を使用した分かりやすい名前にします
設定を記入後、作成ボタンを押します
%e3%83%8d%e3%83%83%e3%83%88%e3%83%af%e3%83%bc%e3%82%af%e4%bd%9c%e6%88%90

 

3.VPN用に新しい静的IPアドレスを予約します

名前を記載し、予約を押します
今回、新たに予約した静的IPアドレスがVPNゲートウェイのIPアドレスとなります
%e9%9d%99%e7%9a%84ip%e3%82%a2%e3%83%88%e3%82%99%e3%83%ac%e3%82%b9%e4%ba%88%e7%b4%84

 

4.VPNの設定画面へ

VPN→VPN接続を作成を押します
vpn%e4%bd%9c%e6%88%90%e3%81%b8

 

5.VPN接続設定の画面へ

vpn%e6%8e%a5%e7%b6%9a%e3%81%ae%e4%bd%9c%e6%88%90

VPN接続設定手順を以下に示しました

設定項目 設定内容
1 ネットワーク 手順2で作成したネットワークを指定
2 地域 手順2で作成したネットワークと同じリージョンを指定
3 IPアドレス 手順3で予約した静的なグローバルIPアドレスを選択
4 リモートピアIPアドレス GCPの対向機器側のVPNゲートウェイアドレスを指定
5 IKEバージョン GCPの対向機器側と同じIKEバージョンを選択する(IKEv1とIKEv2のどちらかを選択する)
6 共有シークレット GCPの対向機器側と同じパスワードを指定
7 リモートネットワークIPの範囲 GCPの対向機器側のVPN用ネットワークを指定
8 ローカルサブネットワーク 手順2で作成したネットワークを指定
9 ローカルIP範囲 ローカルサブネットワークを選択すると自動で割り振られる

 

6.GCPの対向側のVPN接続の設定を行う

GCPの対向側のVPN接続用ネットワークの作成を事前に行う必要があります
必要な情報は以下の通りです

VPN設定前に予め用意する必要のある設定

  • VPNネットワーク
  • VPNゲートウェイIPアドレス

各種ステータス情報

設定項目 設定内容
IPsecプロトコル ESP
IKEバージョン バージョン1と2両方可
ポリシータイプ Site to Site
認証方法 PSK(Pre-Shared Key)
共有シークレットパスワード GCPと同じパスワードを使用
IKE/ISAKMP aes128-sha1-modp1024
ESP aes128-sha1
PFS ON
PFSアルゴリズム Group2

IKEフェーズ1の設定内容(例)

Exchange Main Mode
DH Group Group2
Encription AES-128
Authentication SHA1
Life Time(seconds) 28800

IPsecフェーズ2の設定内容(例)

Protocol ESP
DH Group Group2
Encription AES-128
Authentication SHA1
PFS (Perfect Forward Secrecy) ON
Life Time(seconds) 28800

 

7.VPN設定完了後

このような画面になります。設定が全て終了した後、緑色になります。

vpn%e8%a8%ad%e5%ae%9a%e5%ae%8c%e4%ba%86%e5%be%8c

 

★設定がうまくいかない場合のトラブルシューティング方法

 

設定がうまくいかない場合、原因を探るためにVPN→ログの表示を選択します
vpn%e8%a8%ad%e5%ae%9a%e3%81%a6%e3%82%99%e5%9b%b0%e3%81%a3%e3%81%9f%e6%99%82

 

★まとめ

 
今記事で紹介したように、GCPのVPN設定はとても簡単にできます。トラブルシューティングの際も、どのフェーズの通信ができていないのかログで分かりやすく表示されるので、原因切り分けも手軽に行うことができます。GCPでVPNを手軽に導入することができるので、この機会に是非導入してみてはどうでしょうか?

]]>
[Google Cloud Platform]マルチリージョン対応のGCPのロードバランシング機能を試してみた https://cyberagent.ai/blog/tech/gcp/1414/ Mon, 31 Oct 2016 02:00:23 +0000 https://cyberagent.ai/techblog/?p=1414 こんにちは、アドテク本部でネットワークエンジニアをしている菊地です。
マルチリージョン対応のロードバランシングは現時点でGCPのみ可能という点に興味を持ち、実際に設定をして動作検証も試しました。
今記事では、マルチリージョン対応のロードバランシングで実現可能なことを紹介します。

記事の流れはこのよう以下のようになっております。

  • GCPのロードバランシングの特徴
  • GCPのロードバランシングの設定方法
  • 動作検証をして分かったこと
  • まとめ

このような流れで説明していきたいと思います。

 

★GCPでのロードバランシングの特徴

GCPのロードバランシングの特徴についていくつか挙げておきます。

  • L4とL7両方のロードバランシングが可能
  • 各リージョンごとにインスタンスグループを作成し、各インスタンスグループ配下には同じリージョンのVMインスタンスが所属
  • マルチリージョン対応のロードバランシングを実現したい場合、バックエンドサービスにバランシング対象となるインスタンスグループを設定
  • 1つのグローバルIPアドレスで複数のリージョン間を跨ぐ負荷分散や障害時の切り替えが可能

これらの特徴のうち、特に複数リージョンをまたぐ負荷分散や障害時の切り替えについて正しく動作するのか気になりました。

 

★GCPのロードバランシングの設定(マルチリージョン対応の場合)

 

1.ファイアウォールルールの作成

作成手順:
– ネットワーキング→ファイアウォール ルール→ファイアウォール ルールの作成

今回はHTTP通信をバランシングしたいため、全ての宛先からのHTTP通信を許可するルールを作成
ターゲットタグの名前も忘れずに設定

%e3%83%95%e3%82%a1%e3%82%a4%e3%82%a2%e3%82%a6%e3%82%a9%e3%83%bc%e3%83%ab%e3%83%ab%e3%83%bc%e3%83%ab%e3%81%ae%e4%bd%9c%e6%88%90

2.ロードバランス用の新たな静的アドレスを予約

予約手順:
– ネットワーキング→外部IPアドレスの静的アドレス予約を押す→名前を入力、タイプはグローバルを指定

%e9%9d%99%e7%9a%84%e3%82%a2%e3%83%88%e3%82%99%e3%83%ac%e3%82%b9%e4%ba%88%e7%b4%84

3.ヘルスチェックの設定

作成手順:
– Compute Engine→ヘルスチェック→ヘルスチェックの作成

ヘルスチェックの作成の設定項目

設定項目 設定内容
名前 任意の名前
プロトコル 今回はHTTPを選択
ポート 今回は80番を選択
リクエストパス 任意のパスを記述
ヘルスチェックの条件 チェック間隔(秒)、タイムアウト(秒)、正常しきい値、異常しきい値

%e3%83%98%e3%83%ab%e3%82%b9%e3%83%81%e3%82%a7%e3%83%83%e3%82%af

4.バランシング対象となるインスタンスを作成

作成手順:
– Compute Engine→VMインスタンス→インスタンスの作成

新しいVMインスタンスの作成画面の設定項目

設定項目 設定内容
名前 任意の名前
ゾーン 任意のゾーンを選択
マシンタイプ 任意のマシンタイプを選択

※ファイアウォールルールでhttp通信を許可するルールを割り当てる必要あり

5.インスタンスグループを作成する(各リージョンごと)

作成手順:
– Compute Engine→インスタンス グループ→インスタンス グループを作成

新しいインスタンス グループの作成画面の設定項目

設定項目 設定内容
名前 任意の名前
場所 シングルゾーン
ゾーン バランシング対象のVMインスタンスと同じゾーンを選択
作成方法 既存のインスタンスを選択
ネットワーク バランシング対象のVMのインスタンスの設定を参考に
VMインスタンス バランシング対象のVMインスタンスを指定(同じリージョンであれば複数指定可)

※マルチリージョンに対応したい場合、リージョンの種類ごとにインスタンスグループを作成する必要があります

%e6%96%b0%e8%a6%8f%e3%82%a4%e3%83%b3%e3%82%b9%e3%82%bf%e3%83%b3%e3%82%b9%e3%82%af%e3%82%99%e3%83%ab%e3%83%bc%e3%83%95%e3%82%9a

6.ロードバランシングの設定

設定手順:
– ネットワーキング→負荷分散→ロードバランサを作成
– 今回はHTTP(S)負荷分散を選択(設定の開始を選択)
– HTTP(S)負荷分散の編集

6-1.バックエンドの設定

– バックエンドをインスタンスグループごとに作成(マルチリージョン対応の場合、複数選択)

設定項目

設定項目 設定内容
インスタンスグループの選択 手順4で作成したインスタンスグループを選択
ポート番号 今回は80番を選択
分散モード 使用率(CPU使用率を選択)
最大CPU使用率 数値は自由選択
ヘルスチェックの選択 手順3で作成したヘルスチェックを選択

%e3%83%8f%e3%82%99%e3%83%83%e3%82%af%e3%82%a8%e3%83%b3%e3%83%88%e3%82%99%e3%81%ae%e8%a8%ad%e5%ae%9a

6-2.ホストとパスのルール

6.1で作成したバックエンドを指定

6-3.フロントエンドの設定

 

設定項目 設定内容
プロトコル HTTP
IP 手順2で予約したIPアドレス
ポート 今回は80番を指定

最後に更新を選択

7.ロードバランシング設定後

設定後、ロードバランサの詳細と監視の図は以下のようになります

ロードバランサの詳細画面
lb%e8%a9%b3%e7%b4%b0

ロードバランサの監視画面
lb%e7%9b%a3%e8%a6%96

以上で設定完了です

 

★マルチリージョン対応のロードバランシング検証結果・分かった事

設定終了後、動作確認を行いました。その際に分かったことについてまとめてみました。

●動作検証結果

 

    • ロードバランサの外部用IPアドレス宛てにHTTP通信を行った場合

– ローカルのクライアントからロードバランサへのHTTP通信
Asiaリージョン用のインスタンスグループ下の対象インスタンスに対して常に通信する

– USリージョンのインスタンスからロードバランサへのHTTP通信
USリージョン用のインスタンスグループ下の対象インスタンスに対して常に通信する

    • 1つのリージョンのインスタンスグループ下のインスタンスが全て落ちた場合

動作確認環境が所属する地域に関わらず、もう片方の他のリージョンのインスタンスグループへの通信に切り替わることが分かりました

    • インスタンスグループはシングルゾーンとマルチゾーン選択式

各ゾーンにおけるバランシング対象となるVMインスタンスの選択方法が異なります
– シングルゾーンの場合
バランシング対象の決め方:インスタンステンプレートの利用と既存のVMを両方選択可能です

– マルチゾーンの場合
バランシング対象の決め方:インスタンステンプレートの利用はできます
ただし、既存のVMを選択をすることはできません

 

●動作検証結果から分かった事

これらの動作結果により

  • 動作確認を行うクライアントの場所(地域:リージョン)に、より近い場所に存在するリージョンのVMインスタンスと通信を行う事
  • リクエストパスに応じてどのインスタンスにリクエストを送信するのか決定することも可能である事
  • インスタンスグループ内のVMインスタンスが全て落ちた場合、別のリージョンの異なるインスタンスグループ配下のVMインスタンスに切り替え可能である事

 

★まとめ

今回、GCPのマルチリージョン対応のロードバランシングに触れるよい機会でした。動作環境のリージョンに応じて、より近いリージョンのVMインスタンスと通信を行う仕組みになっており、レスポンス速度を向上させることが可能です。また障害時にリージョンが異なるVMインスタンスへの切り替える事が可能であると分かりました。これらのマルチリージョン対応のロードバランシングの強みを実感しました。

]]>
[Google Cloud Platform] public dataset の地理空間画像から日本の画像を探してみよう https://cyberagent.ai/blog/tech/gcp/1446/ Sat, 15 Oct 2016 01:00:13 +0000 https://cyberagent.ai/techblog/?p=1446 みなさんこんにちは。
アドテクスタジオの makocchi です。

先日 Google Cloud Platform(以下 GCP) の blog で「Powering geospatial analysis: public geo datasets now on Google Cloud」という記事が投稿されました。
衛星からの地理空間画像の public dataset が GCP で利用可能になったという内容なのですが、さっそく画像を取得してみました。

衛生の種類は 2 種類あって Landsat と Sentinel-2 があるのですが、今回は Sentinel-2 の衛星画像を見てみました。

 

地理空間画像データの保管場所

Sentinel-2 の画像は GCS の gs://gcp-public-data-sentinel-2/ に保存されています。
その中に緯度経度と画像のマッピング情報が index 化されて gs://gcp-public-data-sentinel-2/index.csv.gz に保存されています。

 

index.csv の中身

index.csv.gz のサイズは 49M 程度で解凍すると 422M になりました。
行数は 100 万程のようです。

$ ls -sh index.csv.gz 
49M index.csv.gz

$ ls -sh index.csv 
422M index.csv

$ wc -l index.csv 
1065634 index.csv

 

実際の中身ですが、csv の先頭行にカラムの説明が書かれています。

GRANULE_ID,PRODUCT_ID,DATATAKE_IDENTIFIER,MGRS_TILE,CAPTURE_TIME,GEOMETRIC_QUALITY_FLAG,GENERATION_TIME,NORTH_LAT,SOUTH_LAT,WEST_LON,EAST_LON,BASE_URL
S2A_OPER_MSI_L1C_TL_MTI__20160915T092853_A006437_T44UPU_N02.04,S2A_OPER_PRD_MSIL1C_PDMC_19700101T000000_R005_V20160915T053642_20160915T054003,GS2A_20160915T053642_006437_N02.04,44UPU,2016-09-15T05:40:03.107000Z,PASSED,1970-01-01T00:00:00.000000Z,48.7450740854,47.7533830954,82.3342021112,83.0997076228,gs://gcp-public-data-sentinel-2/tiles/44/U/PU/S2A_MSIL1C_20160915T053642_N0204_R005_T44UPU_19700101T000000.SAFE
S2A_OPER_MSI_L1C_TL_MTI__20160915T092853_A006437_T44TNS_N02.04,S2A_OPER_PRD_MSIL1C_PDMC_19700101T000000_R005_V20160915T053642_20160915T054003,GS2A_20160915T053642_006437_N02.04,44TNS,2016-09-15T05:40:03.107000Z,PASSED,1970-01-01T00:00:00.000000Z,46.9537991674,45.9623280677,80.9996053231,82.2915737646,gs://gcp-public-data-sentinel-2/tiles/44/T/NS/S2A_MSIL1C_20160915T053642_N0204_R005_T44TNS_19700101T000000.SAFE
S2A_OPER_MSI_L1C_TL_MTI__20160915T092853_A006437_T44TPT_N02.04,S2A_OPER_PRD_MSIL1C_PDMC_19700101T000000_R005_V20160915T053642_20160915T054003,GS2A_20160915T053642_006437_N02.04,44TPT,2016-09-15T05:40:03.107000Z,PASSED,1970-01-01T00:00:00.000000Z,47.8460110267,46.9998778483,82.3152219858,82.6907905848,gs://gcp-public-data-sentinel-2/tiles/44/T/PT/S2A_MSIL1C_20160915T053642_N0204_R005_T44TPT_19700101T000000.SAFE
S2A_OPER_MSI_L1C_TL_MTI__20160915T092853_A006437_T43TGN_N02.04,S2A_OPER_PRD_MSIL1C_PDMC_19700101T000000_R005_V20160915T053642_20160915T054003,GS2A_20160915T053642_006437_N02.04,43TGN,2016-09-15T05:40:03.107000Z,PASSED,1970-01-01T00:00:00.000000Z,47.7980551413,46.7937696349,78.221933898,79.13512592,gs://gcp-public-data-sentinel-2/tiles/43/T/GN/S2A_MSIL1C_20160915T053642_N0204_R005_T43TGN_19700101T000000.SAFE
.....

 

緯度経度の情報や GCS の保存場所とかが書かれていますね。

これらのデータの中から日本(東京)の衛生画像データを探してみましょう。

東京は北緯(NOTH_LAT) 35.6 度、東経(EAST_LON) 139.7 度あたりです。

 

index.csv を BigQuery に load する

 

csv を落としてきて grep や awk で頑張って東京付近の index を探すことも可能ですが、せっかくなので BigQuery にデータを入れて検索してみましょう。

BigQuery は GCS に置かれた file から簡単に load することができます。

$ bq load makocchi.sentinel_2 gs://gcp-public-data-sentinel-2/index.csv.gz GRANULE_ID,PRODUCT_ID,DATATAKE_IDENTIFIER,MGRS_TILE,CAPTURE_TIME,GEOMETRIC_QUALITY_FLAG,GENERATION_TIME,NORTH_LAT
,SOUTH_LAT,WEST_LON,EAST_LON,BASE_URL

$ bq show makocchi.sentinel_2
Table myproject:makocchi.sentinel_2

Last modified Schema Total Rows Total Bytes Expiration 
----------------- ----------------------------------- ------------ ------------- ------------ 
14 Oct 05:11:23 |- GRANULE_ID: string 1065634 454789840 
|- PRODUCT_ID: string 
|- DATATAKE_IDENTIFIER: string 
|- MGRS_TILE: string 
|- CAPTURE_TIME: string 
|- GEOMETRIC_QUALITY_FLAG: string 
|- GENERATION_TIME: string 
|- NORTH_LAT: string 
|- SOUTH_LAT: string 
|- WEST_LON: string 
|- EAST_LON: string 
|- BASE_URL: string

NORTH_LAT や WEST_LON のようなカラムは理想は float なのですが・・・string じゃないと駄目でした。(多分マイナスの値も入っていたからだと思われます)

 

それではさっそく東京のデータを探してみましょう。

$ bq query '                                                                                                                                     
SELECT
  NORTH_LAT,
  EAST_LON,
  BASE_URL
FROM
  [myproject:makocchi.sentinel_2]
WHERE
  CAST(NORTH_LAT AS float) BETWEEN 35 AND 36
  AND
  CAST(EAST_LON AS float) BETWEEN 138 AND 140
'

 

こんな感じで探してみたのですが、一番東京に近そうなのはこのデータのようでした。

+---------------+---------------+-----------------------------------------------------------------------------------------------------------------+
| NORTH_LAT     | EAST_LON      | BASE_URL                                                                                                        |
+---------------+---------------+-----------------------------------------------------------------------------------------------------------------+
| 35.225114032  | 138.934514048 | gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE |
+---------------+---------------+-----------------------------------------------------------------------------------------------------------------+

ちなみに北緯 35.225114032、東経 138.934514048 ってどこなのかっていうと芦ノ湖からちょっと西にいったところのようです。

 

さっそくデータを見てましょう。

BASE_URL に書かれている GCS の中身を見てみると

$ gsutil ls gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE
gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE/INSPIRE.xml
gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE/S2A_OPER_MTD_SAFL1C_PDMC_20161010T072654_R074_V20161010T012652_20161010T013613.xml
gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE/manifest.safe
gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE/DATASTRIP/
gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE/GRANULE/
gs://gcp-public-data-sentinel-2/tiles/54/S/TD/S2A_MSIL1C_20161010T012652_N0204_R074_T54STD_20161010T072654.SAFE/rep_info/

このようなデータ構造になっていて、実際の画像データは GRANULE の中に置かれているようです。

GRANULE からさらに掘り進めると、IMG_DATA というディレクトリがありその中に画像が置かれていました。

画像は .jp2 という拡張子になっていて jpeg 2000 の形式で置かれています。

 

早速見てみましょう。

sentinel2_image1
・・・暗くて何もわからない!(いやもしかしたら白黒なのか・・)

IMG_DATA のフォルダには何枚か画像が入ってますがいずれもほぼほぼ真っ黒。

 

 

このままでは終われない

 

他にも画像が無いかいろいろ探していたところ、IMG_DATA と同じ階層に QI_DATA というディレクトリがあり、その中にも .jp2 があったので見てみたところ・・

sentinel2_image2

雲!雲!雲!

というわけであまりいい画像が探せませんでした・・・
他の緯度経度でもいろいろ画像を探してみたんですけども、あまりいい画像が無かったです。悔しい。

駿河湾の画像は見つけました。

sentinel2_image3

 

 

まとめ

地理情報と衛星画像が GCP 内で処理できるようになったことで、いろいろな分析が GCP 内で完結できるようになりますね。

GCP の blog にも書かれていましたが、実際に分析をしている会社も既に何社かあるようです。

先日は気象データも BigQuery で分析できるようになりましたし、こうしたデータを public にしてくれるのはさすが Google です。

今回はあまりいい画像を探すことができませんでしたが、次回は Landsat の衛星画像でチャレンジしようと思います!

]]>