Quantcast
Channel: Google Cloud Platform その1 Advent Calendarの記事 - Qiita
Viewing all 23 articles
Browse latest View live

App Engine Standard Go 1.9 migration to Go 1.11


Objectifyのv6でmemcacheを使う方法(SDK混在による混乱を防ぎながら)

Cloud Datastoreのクエリでがんばるハナシ

$
0
0

昨今Cloud SpannerやCloud Firestoreなど上位の製品も出てきて徐々に存在感を失いつつありますが、「オレはまだDatastoreを愛してるんだ」「Spanner使う金ねえし」という人もいると思ってCloud Datastore(以降Datastoreと記述します)のクエリのハナシを書きます。

はじめに

普段使用されている方はご存知の通りDatastoreのクエリにはRDBのSQLと比較して制限があります。

  • Inequality Filter(>,>=,<,<=)は単一プロパティにのみ使用可能
  • 複数プロパティソート(ORDER BY)を併用する場合、Inequality Filterは最初にソートするプロパティに対してしか使えない
  • LIKEが使えない
  • NOT(!=)が使えない(後述するがライブラリレベルでは可能なものもある)
  • OR、INが使えない(後述するがライブラリレベルでは可能なものもある)

Datastoreを使用したアプリを作っていてUI(特に検索画面など)を設計する際は上記制限をよく把握しておかないと、実装フェーズに入ってからお客様に「この要件は実現出来ませんでした」と謝る羽目になります。

制限を緩和する為にはSearch APIを併用するのが(自分の中では)定石でしたが、Search APIはインデックスサイズに上限があり、Datastore最大の長所スケーラビリティを失うというトレードオフもあります。
またGAE/Go 1.11 runtimeでは "Instead of using the App Engine Search API, host any full-text search database such as ElasticSearch on Compute Engine and access it from your service." と述べられており、Search APIは将来利用されなくなっていく可能性があります。(参考)

Datastoreクエリの制限は、要件や仕様を調整したり実装を工夫することである程度緩和させることができます。
本記事では、できるだけDatastoreの標準機能だけを用いてクエリの制限を回避(緩和)する方法を提示します。
※全ての制限を回避できるわけではありませんし、トレードオフもあります。悪しからず

サンプルコード

GAE/Go 1.11 runtime上で、appengine標準API(google.golang.org/appengine/datastore) を用いて記述しています。
https://github.com/knightso/sandbox/blob/master/extra-ds-query/gae_books.go

Cloud Datastore Client Library(cloud.google.com/go/datastore)用のサンプルコードも用意しました。
https://github.com/knightso/sandbox/blob/master/extra-ds-query/gcd_books.go

動作確認は旧Datastore及びCloud Firestore in Datastore modeで行なっています。

NOT EQUAL

NOT EQUALフィルタ(!=)は、JavaやPythonのライブラリには用意されていますがGoには用意されていません。不便に思った人もいるかと思いますが、そもそもNOT EQUALはDatastore APIの機能として用意されているものではなく、JavaもPythonもSDKライブラリレベルで実現されている機能です。

Hoge != 999 は、 内部的に Hoge > 999Hoge < 999 の複数クエリに分割実行され結果がマージされます。

Goでも自前で同様の実装を行えば(かなり面倒ですが)実現は可能ですが、本記事では割愛します。

上記の様にInequality Filterを用いてNOT EQUAL検索することは可能ですが、同時にInequality Filterの制限もくっついてきます。つまり、他プロパティに対するInequality Filterの適用が出来なくなり、また他プロパティをソートの第一条件にすることが出来なくなります。
さらに、複数クエリを組み合わせている都合上、カーソルが利用できなくなる、という制限も発生します。

そこで、もう一つ別の方法を紹介します。
NOT EQUALの比較対象が固定の場合、boolの派生プロパティを持つことによってEquality Filterで処理できる様になります。

サンプルとして、下記の様なBookモデルを考えます。

// BookStatus describles status of Book
type BookStatus int

// BookStatus contants
const (
    BookStatusUnpublished BookStatus = 1 << iota
    BookStatusPublished
    BookStatusDiscontinued
)

// Book is sample model.
type Book struct {
    Title       string
    Price       int
    Category    string
    Status      BookStatus
}

例えば Status != BookStatusUnpublished で検索する、という要件があった場合、その条件をboolで表すIsPublishedの様なプロパティを別途保存しておくことで、IsPublised = false のEquality Filterが使用できる様になります。

// Book is sample model.
type Book struct {
    Title       string
    Price       int
    Category    string
    Status      BookStatus
    IsPublished bool // 追加
}
    // Book保存時に派生プロパティを補完
    book.IsPublished = book.Status == BookStatusPublished
    // 派生プロパティに対してフィルタして検索
    var books []Book
    _, err := datastore.NewQuery("Book").Filter("IsPublished =", false).GetAll(ctx, &books)

この様にすることで、他プロパティの比較やソート用にInequality Filter用インデックスを節約しておくことができます。

IN(OR)

INもNOT EQUAL同様JavaやPythonがサポートしていてGoではサポートされていない機能となります。これもJavaやPythonは複数の条件でクエリを実行して結果をマージしています。(こちらもカーソルは使えません)
Goでも自前で同様の実装を行えば(やはり面倒ですが)実現可能です。本記事では割愛します。

INも条件が固定されているのであれば、派生プロパティを用意してEquality Filterに置き換える手法が使えます。

あまりよい例ではないですが例えばCategoryプロパティが"sports"または"cooking"のものを抽出したいという仕様があった場合、その条件をboolで保存する派生プロパティを用意します。

// Book is sample model.
type Book struct {
    Title       string
    Price       int
    Category    string
    Status      BookStatus
    IsPublished bool
    IsHobby     bool // 追加
}
    // Book保存時に派生プロパティを補完
    book.IsHobby = book.Category == "sports" || book.Category == "cooking"
    // 派生プロパティに対してフィルタして検索
    var books []Book
    _, err := datastore.NewQuery("Book").Filter("IsHobby =", true).GetAll(ctx, &books)

これでInequality Filterを消費せずに検索できます。

さらに別の力技を紹介します。IN条件の全組み合わせをListプロパティに保持しておくことで、比較値が不定でも検索できる様になります。

// Book is sample model.
type Book struct {
    Title         string
    Price         int
    Category      string
    Status        BookStatus
    StatusORIndex []int // 追加
    IsPublished   bool
    IsHobby       bool
}
    // book.Statusを含むOR条件組み合わせを全て保存しておく
    for j := 1; j < 1<<uint(len(BookStatuses))+1; j++ {
        if j&int(book.Status) != 0 {
            book.StatusORIndex = append(book.StatusORIndex, j)
        }
    }
    // Status IN (BookStatusUnpublished, BookStatusPublished)で検索
    var books []Book
    _, err := datastore.NewQuery("Book").Filter("StatusORIndex =", BookStatusUnpublished|BookStatusPublished).GetAll(ctx, &books)

選択肢が多いと指数関数的にListプロパティのサイズが増えていくので注意してください。選択肢8個(128通り)くらいまでなら余裕で保存可能です。

OR条件も基本的にINと同じ考えで実装することができます。

大小比較

数値などの大小比較についてテクニックを紹介します。

1プロパティまでであれば普通にInequality Filter(> >= < <=)が使えますが、前述の通り、他のプロパティに対してInequality Filterが使えなくなったりソートに制限がかかるので、可能ならばInequality Fitlerを使わずにおきたいところです。

例えば検索画面UIなどで下のイメージの様な金額を入力して絞り込むフィールドがあるとします。

price-form.png

これだとInequality Filterが必須になります、これを下記の選択肢を持つプルダウンに仕様変更できればEquality Filterで検索できる様になります。

price-form2.png

実際の数値プロパティとは別に派生プロパティを用意し、そこにプルダウンのオプションに対応する値を保存しておきます。

// Book is sample model.
type Book struct {
    Title         string
    Price         int
    PriceRange    string // 追加
    Category      string
    Status        BookStatus
    StatusORIndex []int
    IsPublished   bool
    IsHobby       bool
}
    switch {
    case book.Price < 3000:
        book.PriceRange = "p<3000"
    case book.Price < 5000:
        book.PriceRange = "3000<=p<5000"
    case book.Price < 10000:
        book.PriceRange = "5000<=p<10000"
    default:
        book.PriceRange = "10000<=p"
    }

検索する際はその派生プロパティに対してフィルタをかけます。

    // 5000円以上〜10000円未満で検索
    var books []Book
    _, err := datastore.NewQuery("Book").Filter("PriceRange =", "5000<=p<10000").GetAll(ctx, &books)

後ろの条件が「未満」になっていることに注意してください。これが「以下」という仕様になると複数選択肢にまたがってhitする金額が発生する為、インデックスをListプロパティにする必要が生じます。

部分一致検索

Datastoreではプロパティに対する部分一致検索(LIKE)が出来ません。これはJava、Pythonも同様です。

しかし、検索用インデックスを自前で作成しListプロパティに保存することで、(完全なLIKE互換ではないですが)部分一致検索を行うことができます。

インデックスの作成にはN-Gramを利用します。

私は二文字ずつ分割するbigramと、一文字に分けるunigramを併用して利用しています。

AppEngine → ap, pp, pe, en, ng, gi, in, ne, a, p, e, n, g, i

// Book is sample model.
type Book struct {
    Title         string
    TitleIndex    []string // 追加
    Price         int
    PriceRange    string
    Category      string
    Status        BookStatus
    StatusORIndex []int
    IsPublished   bool
    IsHobby       bool
}
    // Book保存時に派生プロパティを補完
    book.TitleIndex = biunigrams(book.Title)

検索パラメータも分割する必要があります。

    q := datastore.NewQuery("Book")

    if runeLen := utf8.RuneCountInString(title); runeLen == 1 {
        // パラメータが1文字の場合はunigramで検索
        q = q.Filter("TitleIndex =", title)
    } else if runeLen > 1 {
        // パラメータが2文字以上の場合はbigramで検索
        for _, gram := range bigrams(title) {
            q = q.Filter("TitleIndex =", gram)
        }
    }

    var books []Book
    _, err := q.GetAll(ctx, &books)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

bigrams, biunigrams関数の詳細は割愛します。興味ある方はこちら

注意

検索にノイズが発生することがあります。たとえば「東京都」で検索した場合に、「東京」「京都」を含んでいれば「東京都」を含まないデータもhitします。
また、インデックスが巨大になりノイズも増えることからあまり長い文章には適しません。Cloud Datastoreはエンティティあたりのサイズ制限(1Mbyte)があるので、超過するとエラーになります。

本テクニックを使用した場合、検索実行時に内部でマージ・ジョインが行われます。エンティティの件数と検索条件によっては検索にかなり時間がかかる可能性があります。

全文検索

部分一致と同様にListプロパティにインデックスを保存すれば、形態素解析の使用も理論的には可能です。
本記事では割愛しますが、GAEで形態素解析動かした方もいらっしゃるので興味ある方はチャレンジしてみてください。

参考: 形態素解析器 kagome を Google App Engine の最も安いインスタンスで動かす

注意

エンティティあたりのサイズに制限(1Mbytes)があるので、あまり長文になるとエラーになります。

前方一致検索

Inequality Filterを利用して前方一致検索することができます。

例えば'abc'から前方一致で検索したい場合は、
Hoge >= "abc"

Hoge < "abc" + "\ufffd"
の条件を組み合わせれば検索できます。

datastore.NewQuery(kind).Filter("Hoge>=", hoge).Filter("Hoge<", hoge+"\ufffd").GetAll(ctx, &fuga)

Inequality Filterを節約したい場合はEquality Filterでも実現可能です。
Listプロパティに、データの先頭から一文字ずつ増やしたプレフィクスをインデックスとして保存しておきます。

AppEngine → a, ap, app, appe, appen, appeng, appengi, appengin, appengine

// Book is sample model.
type Book struct {
    Title         string
    TitleIndex    []string
    TitlePrefix   []string // 追加
    Price         int
    PriceRange    string
    Category      string
    Status        BookStatus
    StatusORIndex []int
    IsPublished   bool
    IsHobby       bool
}
    // Book保存時に派生プロパティを補完
    book.TitleIndex = biunigrams(book.Title)
    book.TitlePrefix = prefixes(book.Title)

検索パラメータはそのまま渡します。大文字小文字区別しない様にする為に小文字にだけ変換しています(インデックスも小文字で統一しています)。

    var books []Book
    _, err := datastore.NewQuery("Book").Filter("TitlePrefix =", strings.ToLower(title)).GetAll(ctx, &books)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

N-Gramよりもインデックス量は増えるので、対象データが大きい場合は解析先頭文字数を制限するとよいでしょう。

後方一致検索

前方一致検索と同様の手法で実現可能です。

Inequality Filterを利用する場合は、あらかじめ内容を逆順に並び替えた派生プロパティを用意しておき、それに対して前方一致検索を行います。

Equality Filterを利用する場合は前方一致同様のインデックスを後方から作成すればOKです。

AppEngine → e, ne, ine, gine, ngine, engine, pengine, ppengine, appengine

実際に後方一致検索が必要となるケースはそれほど多くなさそうです(自分は今まで経験ありません)。
例えば拡張子検索などが挙げられるかもしれませんが、その場合はあらかじめ拡張子だけを抜き出して派生プロパティに保存しておいた方がインデックスを節約できます。

注意点

インデックス爆発

本記事で紹介してきた手法のうちListプロパティを用いたものは、他のListプロパティと組み合わせてカスタムインデックス作成すると組み合わせの数だけインデックスが作成されてしまう(インデックス爆発)ので注意してください。
後述するライブラリではマージジョインを利用することでインデックス爆発を避ける仕組みを組み込んでいます。

マイグレーション

派生プロパティを用いて解決する手法をいくつか紹介しましたが、派生プロパティに影響する仕様変更があった場合は保存済Entityを全て保存しなおす必要が生じます。

Cloud Firestore in Datastore mode

現在Datastoreのバックエンドに従来のCloud DatastoreとCloud Firestore in Datastore modeが選択可能となっており、近い将来全てのDatastoreがCloud Firestore in Datastore modeに置き換わっていくらしいです。

Cloud Firestore in Datastore modeはクエリが全て強整合参照で実行できるという超強力なメリットがありますが、その分インデックスサイズが大きいと保存時のパフォーマンスに対して悪影響が予測されます。
本記事で紹介しているテクニックで使用インデックスが増えた場合にどの程度パフォーマンスに影響あるかは、追って検証したいと考えています。

さいごに

本記事で提示したように、Datastore標準クエリ機能だけでも工夫次第でもう少し複雑な検索を行うことができます。決して万能ではないですが、単純に「IN出来ない」「LIKE使えない」の理解だけで終わってしまうと実現できる要件の幅が狭くなってしまいます。それはとても勿体ないと思います。

クエリとインデックスの性質をよく把握して、要件定義の時点からそれを意識して行うことで、できることの範囲が広がります。

コツは、常に「この要件、Equality Filterだけで実現できないかな?」と考えるクセをつけることです(^^)

おまけ

本記事で行なっている様な検索をサポートするライブラリをつくってます。
2年くらいまえから作る作る言ってましたが、忙しさにかまけて放置してました(´・ω・`)
来週のGCP Advent Calendarその2にも記事を予定してるので、それまでに動くものをつくってアップします(宣言!)

→つくりました!\(^o^)/
https://qiita.com/hogedigo/items/02dcce80f6197faae1fb

Cloud DNSのプライベートDNSゾーンがリリースされたので試してみた

GKEの快適なオペレーション

$
0
0

はじめに

最近、業務でGKEを使いはじめたので、gcloudコマンドやkubectlコマンドを使ったオペレーションが増えてきました。
gcloudコマンドやkubectlコマンドはそのまま使っても便利ですが、より快適なオペレーションを実現するために自分が工夫していることを書いておきます。

GCPのプロジェクト、アカウント切り替えを快適に

業務では複数のGCPプロジェクトを切り替えて作業します。
gcloudを使う場合、以下のコマンドを実行します。

# プロジェクトのリストを表示
$ gcloud projects list
# プロジェクトの切り替え
$ gcloud config set project PROJECT_ID

頻繁に切り替えることが多いので、この手順は結構手間になります。
そこでfzfでプロジェクトをインタラクティブに選択できる関数を作成し、alias登録してあります。

function _gcloud_change_project() {
  local proj=$(gcloud projects list | fzf --header-lines=1 | awk '{print $1}')
  if [ -n $proj ]; then
    gcloud config set project $proj
    return $?
  fi
}
alias gcp=_gcloud_change_project

現在のプロジェクトは以下のコマンドで確認できます。

$ gcloud config get-value project

こちらもプロジェクトと同じように、アカウント切り替えもalias登録してあります。

function _gcloud_change_account() {
  local account=$(gcloud auth list --format="value(account)" | fzf | awk '{print $1}')
  if [[ -n $account ]]; then
    gcloud config set account $account
    return $?
  fi
}
alias gca=_gcloud_change_account

kubectlのcontextとnamespaceの切り替え快適に

通常、kubectlを使ってcontextを切り替える場合は、このコマンドを実行します。

# contextのリストを表示
$ kubectl config get-contexts
# contextを切り替え
$ kubectl config use-context CONTEXT

また、namespaceはコマンド実行時に--namespaceオプションで指定することが多いと思います。

こういった手順が面倒なときは、こちらのkubectx/kubensというコマンドを使うとcontextやnamespaceを補完してくれるので、切り替えが楽になります。

また、fzfがインストールされている際はインタラクティブにcontextやnamespaceを切り替えることができます。

プロンプトに現在のプロジェクトを表示して快適に

複数のプロジェクトやcontextを切り替えているとどのコンテキストで作業しているか分かりづらいので、
常にプロンプトに表示するようにしています。

自分は以下のプラグインを参考にフォーマットを変えて表示しています。
- https://github.com/jonmosco/kube-ps1
- https://github.com/katsew/zsh-gkeadm-prompt

kubctlの補完を有効にして快適に

標準でbashとzsh用の補完機能が提供されているので、ぜひ有効にしましょう。

# bash
source <(kubectl completion bash)
# zsh
source <(kubectl completion zsh)

自分は.zshrcに以下を追加してあります。

if [ $commands[kubectl] ]; then
  source <(kubectl completion zsh)
fi

Kubernetesのリソースの指定を快適に

kubectl getkubectl describeなどで指定するリソース名は省略することができます。

$ kubectl get pod
$ kubectl get po

$ kubectl get deployment
$ kubectl get deploy

$ kubectl get service
$ kubectl get svc

以下が、リソース名と省略形の対応関係の一覧です。
kubectl api-resources で確認することができます。

リソース名 省略形
certificatesigningrequests csr
clusterrolebindings
clusterroles
componentstatuses cs
configmaps cm
controllerrevisions
cronjobs
customresourcedefinition crd
daemonsets ds
deployments deploy
endpoints ep
events ev
horizontalpodautoscalers hpa
ingresses ing
jobs
limitranges limits
namespaces ns
networkpolicies netpol
nodes no
persistentvolumeclaims pvc
persistentvolumes pv
poddisruptionbudgets pdb
podpreset
pods po
podsecuritypolicies psp
podtemplates
replicasets rs
replicationcontrollers rc
resourcequotas quota
rolebindings
roles
secrets
serviceaccounts sa
services svc
statefulsets sts
storageclasses sc

kubectlを省略して快適に

kubectl自体も毎回入力するのは手間なので省略しちゃいましょう。
こちらは好みも別れますが、以下のようなaliasを登録してます。
kubectxとkubensも合わせて登録してます。

alias k=kubectl
alias kx=kubectx
alias kn=kubens

ロギングを快適に

複数のログをまとめて見る場合は、Stackdriver loggingが便利ですが、CLIで手軽に見たい場合があります。

また、kubectlには標準でkubectl -f logs POD_IDでログを出力することができますが、PodのIDを個別に指定してしなければいけないので面倒です。

sternを使うとPodのラベルを指定して手軽にログを出力することができます。

こちらも補完を有効にしておくとさらに便利です。

if [ $commands[stern] ]; then
  source <(stern --completion=zsh)
fi

まとめ

他にも良いTipsがあればぜひ教えていただきたいです!
快適なGKEライフを!

Cloud Spanner をつかったテストのやり方

$
0
0

この記事は Google Cloud Platform その1 Advent Calendar 2018 の6日目の記事です。

GCP には Cloud Spanner というデータベースがあります。分散技術をふんだんに使ったとにかくすごいデータベースなのですが、最低料金がお高いこともあってか、情報がまだ少ない印象です。

実際に Cloud Spanner をつかってアプリを開発する場合、テストはどのように書くとよいのでしょうか。

この記事で解説すること

  • Cloud Spanner 開発用環境の構築方法
  • Cloud Spanner データベースの初期化のやり方
  • 初期データの挿入のやり方
  • Cloud Spanner 特有の引っかかりやすい点と解決策

Cloud Spanner 開発環境の構築

Cloud Spanner は完全なマネージドサービスで、現在は残念ながら Cloud Spanner 自体をローカルで動かすことはできません :cry:

よって、Cloud Spanner を使ったテストを行いたい場合、実際に Cloud Spanner のインスタンスを作っておいて、そこにテストのたびに接続する形となります。

ロジックのテストを行うだけであれば、ノード数 1 で十分です。(1 nodeでもお高いですが・・・ :sweat_smile:

認証情報の作成

Cloud Spanner は GCP のサービスなので、ADC と呼ばれる仕組みで認証を行います。
テスト用の環境では、データベースを作ったり壊したりできたほうがいいので、データベースの削除などを含めた強めの権限 を持っておくとテストしやすいです。(実運用の環境とは別のインスタンスでやりましょう!)

自分は Cloud Spanner の databaseAdmin の権限を持ったサービスアカウントを作成し、その鍵ファイルを環境変数 GOOGLE_APPLICATION_CREDENTIALS に入れることで認証しています。

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json

ADC の仕組みにより、この環境変数をセットするだけでソースコード側で何も認証を指定せずに Cloud Spanner に接続できます!

データベース テストのやり方

Cloud Spanner はデータベースです。まず最初は一般的なデータベースにおけるテストのやり方を見てみましょう。

テストの初期状態では、常にデータベースの状態が一定になっていることが望ましいです。

  1. データベースをまっさらな状態にする
  2. テストのための初期データ(フィクスチャ) を入れる
  3. テストを実行する
  4. データベースをまっさらな状態にする

データベースをまっさらな状態にする

最も簡単な方法は、データベースごと作り直す方法です。

  1. DROP DATABASE ... データベースを削除する(あれば)
  2. CREATE DATABASE ... データベース自体をつくる
  3. CREATE TABLE ... テーブルやインデックスを作る

Cloud Spanner でこれをやってみましょう。サンプルコードは Go で書いていますが、ベースは gRPC なので他の言語でもやり方は基本同じです。

package main

import (
    "context"
    "log"
    "cloud.google.com/go/spanner/admin/database/apiv1"
    adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
    "fmt"
    "time"
    "google.golang.org/grpc/status"
    "google.golang.org/grpc/codes"
)

func main() {
    ctx := context.Background()
    client, err := database.NewDatabaseAdminClient(ctx)
    if err != nil {
        log.Fatal(err)
    }

    projectID := "xxx"
    instanceID := "xxx"
    databaseID := "xxx"
    dsn := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, databaseID)
    dsnParent := fmt.Sprintf("projects/%s/instances/%s", projectID, instanceID)

    {
        exists := true
        if _, err := client.GetDatabase(ctx, &adminpb.GetDatabaseRequest{Name: dsn}); err != nil {
            st, ok := status.FromError(err)
            if ok && st.Code() == codes.NotFound {
                exists = false
            } else {
                log.Fatal(err)
            }
        }

        if exists {
            fmt.Printf("begin (DROP DATABASE)\n")
            start := time.Now()
            if err := client.DropDatabase(ctx, &adminpb.DropDatabaseRequest{
                Database: dsn,
            }); err != nil {
                log.Fatal(err)
            }
            fmt.Printf("end (DROP DATABASE) (%s)\n", time.Since(start))
        }
    }

    {
        fmt.Printf("begin (CREATE DATABASE)\n")
        start := time.Now()
        op, err := client.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
            Parent:          dsnParent,
            CreateStatement: fmt.Sprintf("CREATE DATABASE `%s`", databaseID),
        })
        if err != nil {
            log.Fatal(err)
        }
        if _, err := op.Wait(ctx); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("end (CREATE DATABASE) (%s)\n", time.Since(start))
    }

    {
        fmt.Printf("begin (CREATE TABLE & INDEX)\n")
        start := time.Now()
        op, err := client.UpdateDatabaseDdl(ctx, &adminpb.UpdateDatabaseDdlRequest{
            Database: dsn,
            Statements: []string{
                `CREATE TABLE Singers (
                    SingerId   INT64 NOT NULL,
                    FirstName  STRING(1024),
                    LastName   STRING(1024),
                    SingerInfo BYTES(MAX)
                ) PRIMARY KEY (SingerId)`,
                `CREATE INDEX SingersByFirstName ON Singers(FirstName)`},
        })
        if err != nil {
            log.Fatal(err)
        }
        if err := op.Wait(ctx); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("end (CREATE TABLE & INDEX) (%s)\n", time.Since(start))
    }
}

これを実行してみたところ、次の結果となりました。

begin (DROP DATABASE)
end (DROP DATABASE) (2.471652346s)

begin (CREATE DATABASE)
end (CREATE DATABASE) (7.146112054s)

begin (CREATE TABLE & INDEX)
end (CREATE TABLE & INDEX) (8m1.547491812s)
  • DROP DATABASE 約3秒
  • CREATE DATABASE 約7秒
  • CREATE TABLE & INDEX 約8分

・・・・8分!??

image.png

データベースの作成・削除はともかく、テーブルとインデックスの作成がめちゃ遅いです 😥

その上、作るテーブルやインデックスの数が増えるとさらに遅くなってきて地獄です。
テストのたびにこんなに待たされてるのではさすがに辛いですね。

ExtraStatements を使ってテーブルとインデックスの作成を爆速にする

テーブルやインデックスの作成が遅いのは Cloud Spanner の仕様かと思って諦めかけていたのですが、解決策がひとつ見つかりました。 ExtraStatements というパラメータです。

CREATE TABLECREATE INDEX といったものを、データベース作成時に一緒に送ることができるパラメータです。

使い方は簡単で、 CREATE DATABASE するときに追加で与えるだけでOKです。

        op, err := client.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
            Parent:          dsnParent,
            CreateStatement: fmt.Sprintf("CREATE DATABASE `%s`", databaseID),
+           ExtraStatements: []string{
+               `CREATE TABLE Singers (
+                   SingerId   INT64 NOT NULL,
+                   FirstName  STRING(1024),
+                   LastName   STRING(1024),
+                   SingerInfo BYTES(MAX)
+               ) PRIMARY KEY (SingerId)`,
+               `CREATE INDEX SingersByFirstName ON Singers(FirstName)`},
        })

これでもう一度同じ初期化を実行してみます

begin (DROP DATABASE)
end (DROP DATABASE) (2.507284052s)

begin (CREATE DATABASE + ExtraStatements)
end (CREATE DATABASE + ExtraStatements) (8.352879087s)
  • DROP DATABASE 約3秒
  • CREATE DATABASE + ExtraStatements 約8秒

・・・・・8秒!!!

image.png

この速度なら繰り返しテストを実行しても大丈夫そうですね!

初期データを挿入する

初期データを入れておきたい場合は、特別なことをしなくても普通に INSERT を使って入れると良いでしょう!

初期の Cloud Spanner は INSERT UPDATE DELETE などの文が使えなかったのですが、最近のアップデートで使えるようになりました 🎉

client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *spanner.ReadWriteTransaction) error {
    sql := "INSERT INTO User (...) VALUES (...)"
    if _, err := tx.Update(ctx, spanner.NewStatement(sql)); err != nil {
        return err
    }
    return nil
})

しかし、大量のデータを入れたい場合は注意が必要です! Cloud Spanner で INSERT などの更新系の命令に制限があります。

  • 1回のトランザクションにつき、20,000 mutations 以下に収める
  • クエリパラメータの数は 950 個 まで

mutations??? とは何でしょうか? :thinking:

ミューテーションとは挿入、更新、削除といった操作の一つの単位です。変更を加える行数と列数、そして対象の列を含むインデックスの数で決まります。

ミューテーションの数え方については、次の動画の解説がわかりやすいです。

https://www.youtube.com/watch?v=12yXz2eS-T0

image.png

1つのトランザクションで 20,000 ミューテーションを超えるようなデータを入れる場合は、トランザクションを複数回に分割するなどして工夫する必要があります。

ミューテーション数の制限を受けない Partitioned DML

ミューテーション数の制限を受けない特殊な文 Partitioned DML というものもあります。これは、ひとつの UPDATE DELETE 文を分散して実行することで大規模な更新が可能になるものです。

ただし、INSERT 文では使用できないので、テスト用データの大量挿入には使えなさそうです。。

クエリパラメータの制約

クエリパラメータとは、クエリの一部分をパラメータ化して再利用可能にするものです。 MySQL でいうとプリペアドステートメントという機能に近いです。

SELECT * FROM Singers WHERE ID = @param1

クエリパラメータを使うことで、次のような効果が期待できます。

  • SQLインジェクションの防止
  • 類似クエリを連続して実行する際のパフォーマンス向上

ただし、これにも制約がいくつかあるので注意です。

  • 1つの文にクエリパラメータは 950 個まで
  • LIKE '%test%' のようなワイルドカードを含むクエリは最適化が効かず、インデックスが使用されない場合がある(参考ページ

この制約を解除する方法は現在ではないので、ミューテーション数の制約を Partitioned DML で解決したとしても、こっちのクエリパラメータ数の制約にかかってしまう場合があります。

回避策としては、次のような感じでしょうか。

  • 複数のトランザクションに分割する
  • クエリパラメータを使わない

まとめ

  • Cloud Spanner はローカル環境上に立てることはできないので、テスト用の環境を作ってがんばる
  • データベースの初期化は、毎回データベースごと作り直すと簡単。その際、 ExtraStatements の指定を忘れずに。
  • ミューテーション数やクエリパラメータ数といった Cloud Spanner 特有の制限があるので知っておくとよい

Go + Cloud Spanner によるテストの実例

Cloud Spanner 含む複数データベース間で差分を取るツール tamate というものの開発のお手伝いをしたことがありまして、そこで Cloud Spanner をつかったテストを書いています。

こんな感じになるのか〜って実例として参考になれば幸いです!

https://github.com/Mitu217/tamate-spanner/blob/master/datasource_test.go

以上、Google Cloud Platform その1 Advent Calendar 2018 の6日目の記事でした。次回の担当は @cvusk さんです!

Google Cloud Spanner Transaction Abort 探検記

GASを使った認可パターン


BigQuery GISとFlaskとMapbox GL JSを組み合わせて可視化してみた

BigQueryMLとScheduled Queryで機械学習モデル運用を自動化しよう

$
0
0

こんにちは、この記事はGCPアドベントカレンダー10日目です。

はじめに

今年はBigQueryGIS、BigQueryMLという大きいリリースがあり、どんどんBigQueryが便利になっていきますね。
中でもBigQueryMLは、SQLのように一連の機械学習プロセスを実行できるということで、かなり注目が集まっていると思います。

今回はBigQueryMLで構築した機械学習モデルについて、同じく今年リリースされたScheduled QueryというBigQueryの新しい機能を用いて、その後のモデル運用プロセスの一部を自動化できるのでは?というお話です。

公式Docs:
すべての BigQuery ML ドキュメント
クエリのスケジューリング

機械学習モデルを構築し終わった後、することって何?

一般的な機械学習プロセスは、以下の図のようになります。
データの下処理、モデルの構築、そしてそれが未知のデータについても適合することの検証という流れを数回繰り返し、実際に予測モデルをデプロイし予測をしていくというプロセスになります。

image.png

しかし、デプロイして終わりではありません。
その後モデルを運用していくためには、性能をモニタリングし続けることが必要です。
それは、デプロイした時には性能が十分だったモデルでも、時間の経過と共にデータの特性が変化し、新しいデータに対しての性能が不十分になってしまう可能性があるからです。

これって毎回そんなに時間は取られないけれども定期的にやってくるルーティンワークで、手動でやろうとするとすごく面倒くさそうですよね。

そこで、そんなところに時間をかけず、Scheduled Queryを使って自動化してしまいましょう!

Scheduled Queryによる自動化

今回は、BigQueryMLでモデルを構築済みの状況を想定します。
モデルの作成方法など、気になる方は公式ドキュメントを読んでみて下さい。(最初の方にリンクあります)

Scheduled Queryは、その名の通り、特定のクエリを定期的に実行し、その結果をテーブルに保存したりできるものです。
詳しい仕様などここでは述べませんので気になる方はドキュメントを読んでみて下さい。書き込み先のテーブル名の指定や、実行間隔など、設定は簡単です。

また、テーブルへの書き込み形式も設定でき、全て置き換える場合と既存のテーブルに追加する場合を選択できます。(今回は後者)

それでは、具体的に仕込むクエリを描いてみます。
例えば、こんな風にBigQueryMLのML.EVALUEATE関数を使ったコードをScheduled Queryで設定します。(下の例は3ヶ月毎の実行を想定してます。)

SELECT
  CURRENT_DATE() AS run_date,
  mean_squared_error,
  r2_score,
  ...
FROM
  ML.EVALUATE(MODEL `モデル名`,
    (
    SELECT
      ...
    FROM
      `テーブル名`
    WHERE timestamp BETWEEN CURRENT_DATE() AND DATE_ADD(CURRENT_DATE(), INTERVAL 3 MONTH)))

すると、このようなイメージで新しい検証結果がどんどん追加されていきます。

image.png

そんな頻繁にデータの特性が変わることは考えづらいので、実行は1ヶ月置き、3ヶ月置きなどで大丈夫だと思います。

この時点で、定期的にモデルの精度を検証するクエリが走るように設定できましたが、ついでにDataStudioでモニタリング用のダッシュボードも作成してみましょう!

DataStudioでのモニタリング

Google DataStudio (Data Portal)

生データでも良いと思いますが、

  • データが増えてきた場合に比較しづらい
  • 指標の変化を一覧化できない

など課題があるため、個人的にはDataStudioなどの可視化ツールで折れ線プロットを作成してモニタリングするのが良いと思います。

image.png

DataStudioを使えばこのように指標の変化を一覧化し一目で把握できます。
また、ページネーションも効くので、複数のモデルも1つのURLにアクセスするだけで性能のモニタリングができます。

終わりに

ここまでご覧いただきありがとうございました。
BigQueryML, Scheduled Query共にとても便利な機能なので、今後のアップデートが楽しみです。
BigQueryMLについて、前に弊社で勉強会をやった際の資料もあるのでもしご興味あればご参照下さい:bow_tone1:

BigQueryMLハンズオン勉強会@麹町

また、先日行われたbq_sushiでのwantedlyの方の発表では実例が豊富に紹介されており、とても参考になるのでぜひ合わせてご覧ください!

BigQueryML を使ってみた話

Professional Cloud Architectへのロードマップ

$
0
0

image.png

この記事は Google Cloud Platform その1 Advent Calendar 2018 の11日目の記事です。

ちゃんとしたGCPの紹介が続く中で若干の気後れをしていますが、今回はGCPの資格試験である Professional Cloud Architect を受けてきたので、その準備内容をまとめます。

既にネットを探せばある情報ですが、2018/11に試験内容がアップデートした可能性があるためきっと誰かの役に立つ(かもしれない)と思うことにします。

なお私は平均的なサーバーサイドエンジニアでインフラを主とした仕事はしていません。
その前提でお読みいただければと思います。

Professional Cloud Architect って何?


Googleが用意している GOOGLE CLOUD CERTIFIED の一つです。

Professional Cloud Architect とは、Google Cloud の技術を組織が活用するために必要なクラウド アーキテクチャと Google Cloud Platform に関する専門的な知識を有することを認められ、ビジネス目標を実現するために、スケーラブルで高可用性を備え、堅牢でかつ安全な動的ソリューションを設計、開発、管理できる者をいいます。

Google Cloud Certified - Professional Cloud Architect 試験では、以下のことを行う能力が評価されます。
[✓] クラウド ソリューション アーキテクチャの設計と計画
[✓] クラウド ソリューション インフラストラクチャの管理とプロビジョニング
[✓] セキュリティとコンプライアンスに対応した設計
[✓] 技術プロセスやビジネス プロセスの分析と最適化
[✓] クラウド アーキテクチャの実装の管理
[✓] ソリューションとオペレーションの信頼性の確保

他には

  • Associate Cloud Engineer
  • Professional Data Engineer
  • G Suite

などの資格が存在します。

結論

(公式)ドキュメントを読み通す が正解です。が、それが難しいためいくつか他の材料を併用することをおすすめします(後述)

特に Computing / Storage / Network / Bigdata / Stackdriver は詳細に把握しておく必要があります。
加えて、 IAMの具体的な運用は複数題でていました。どれも場の設定理解が重要になります。
そして意外とDataTransfer系やData Loss Prevention APIが問われました。

またインフラレイヤーの一般的な知識と、kubernetesの知識が前提となった問題が多く出ますので、kubeはシンプルなクラスタを組める程度の理解が必要になります。(これは業務でカバーしていたのでラッキーでした)

問題を通じてGCPは

  • オンプレからの移行・連携
  • ビッグデータ処理(とML、そこからのサービスへのリアルタイム反映)

に力を入れているのだというメッセージを感じるようなお題でありました。

話をもどしますが、公式ドキュメントは中々のボリュームがあることと、表現が独特な箇所があり、それだけを読むということが難しい内容になっています。
というわけで以下の副教材的なものを併用して準備しました。
公式ではCourseraのコースを推奨していましたがお値段がそこそこしたので、そちらは見送っています。

なお公開模試がありますが、それの難易度が本番のそれだと思うと大怪我するので、そこだけ注意が必要です。(問題の雰囲気はつかめる)

教材

特に中心となるのが太字にした、公式ドキュメント/Udemyの動画/書籍/Udemyの模試 でした。

独断と偏見でサービス群別に、試験範囲とそのカバー範囲を適当に図示します

  • 黄色 -> 書籍
  • 青色 -> udemyの動画
  • ピンクの線 -> 実際に体感した難易度です (問題を公開してはいけない規約なので雰囲気で語っています)

公式ドキュメントは全てをカバーしています。

黄色と青色でカバーできない範囲を模試で確認しつつ、公式ドキュメントを読む...を繰り返して穴を埋めていく後半戦といったイメージです。

進め方

※ あくまで私個人が振り返ってこうすれば最適だったと思う進め方です

  1. まず公開模試を受ける
  2. 黄色本を読む
  3. Udemyの動画をみすすめながら、適宜ドキュメントを読む
  4. みおわったら二度目の公開模試と動画最後にある模試を受ける
    • そこそこできるようになっているはず
  5. (必要あれば)ここまでで出てきたGCP以外の知識を習得する
    • kubernetes
      • k8sはシンプルなクラスタ(Deployment/StatefuleSet/Service/Ingress/Job/Configmap/Secretなど)が組める程度に習熟する必要があります
    • spanner ... 概要だけ
    • CIツール
    • deployment各種手法
    • 災害対策
    • BGP
  6. Udemyの模試セットを受ける
    • 激ムズ. これと公開模試の中間が本番の難易度のイメージ
    • あとはここで浮き彫りになった不明点を模試の解説とドキュメントで補強していく

以下使用した副教材の紹介です。

書籍

Google Cloud Platform エンタープライズ設計ガイド

  • point
    • これで 30%はわかる。わかりやすい
    • でもこれだけじゃ足りない。
    • 2時間くらいで読める。

公開模試

本番の難易度ではないが、雰囲気を掴むのに最適
公式の公開模試

  • point
    • 雰囲気を掴むのには最適
    • ただレベルは本番のそれよりもだいぶ低い
    • 2周くらいすると回答を暗記して最初の3文字くらいで選択肢を選べるようになる。

動画

レクチャー

ドキュメントの機能詳細を丁寧に解説してくれる。本当に丁寧。やりすぎなところもたまにある。
ただ聞くだけでも知識が入ってくるのでドキュメントを読み進めるのと比べるとかなり進みがいい。

動画: google-cloud-architect-certifications

  • point
    • かなり詳細に説明してくれる。小テストで間違えたところは何度かトライ
    • 機能説明に特化しているのであくまでベースとなる知識を固めるためのもの。実際のテストではこれをもとに判断するケースしかでない(機能単体を問われることはない)
    • thとrの癖が強い

模試

インド人の動画だけではちょっと試験慣れに不安が残ったので模試だけのコースを購入。
結論買ってよかった。

模試セット: google-cloud-certified-professional-cloud-architect-practice-test

  • point
    • ゲキムズ。
    • でも方向性はこちらのほうがリアル。
    • 心が折れそうになる。
    • これを80%とれたらまずうかる(最後まで70%超えなかった)

公式ドキュメント

全てはここにある。ありすぎる。
公式ドキュメント

  • point
    • 全て読めるわけがない
    • 全てのサービスの役目と概要は確実に抑えておく必要がある
      • ML系やDataLossPrevension APIなど、普段使わないものも理解だけはしておく
      • 抑えるポイント
        • その役目、特徴
        • 他のサービスとの使い分け
        • 他サービスとの連携
    • 基本サービスカテゴリはスペックも確実に抑える(Computing, Storage, Network, Stackdriver, Bigdata)
    • 概念を通し見したあとに利用シーンなどを把握する
      • ベストプラクティスや入門がそれに当たる

あとは基本サービスカテゴリはちゃんと自分で手を動かして操作できるようにしておくと安心です。ここはこの1-2ヶ月GCPで開発をしていたのでよかった。(でも普段使っているからと言ってこの試験はパスできないのが辛い)

終わりに

そんなこんなで大体20-30時間くらいの勉強時間で無事合格できました :muscle:

なお教材の選定については先輩エンジニア複数名からアドバイスをもらいました。
その一人が今年10月に資格を取得した際の記事がありますのでこちらもぜひ!
https://www.wantedly.com/companies/anypay/post_articles/138266

資格とっても~という声もありますが、これをきっかけによりインフラやネットワークについて学ぶこともできたので個人的にはとてもいい機会でした。

この冬皆様ももしよければぜひチャレンジしてみてはいかがでしょうか!

それでは以上です。

余談

PCAの証明書が届いたので噂のグッズを貰おうと思ったら Item is currently on backorder. Merchandise options are subject to change. Please check back in 3-4 weeks. とのことでした :innocent:

TypeScript で Google Cloud Functions やりたい人向けのテンプレートをつくった

$
0
0

TypeScriptでCloud Functionsをやるときに毎回同じようなことを書いていたので、テンプレートを作りました。※Firebaseコマンドで Firebase Functions をやるときは JavaScriptかTypeScriptか選択することができます。

リポジトリ

https://github.com/flatfisher/cloud-functions-typescript-template

オススメの設定や何か問題などあればIssueやプルリクエストをいただけると嬉しいです:smile:

下準備

configを変更します。 runtimeの違いによって動作しない機能もございますので 、詳しくはドキュメントをご参照ください。

package.json
"config": {
    "function_name": "helloWorld",
    "region": "リージョンをいれる 例:asia-northeast1",
    "gcp_project": "GCPのプロジェクトIDをいれる",
    "runtime": "ランタイムを指定 例:nodejs8"
  },

Lint

$ npm run lint

Build

functions/src/ に Node.js のプロジェクトがビルドされます

$ npm run build

Test

$ npm install -g mocha // mochaがインストールされていればスキップ

$ npm run test

  Hello function
    ✓ Get 200 response

  1 passing (31ms)

Deploy

Cloud Functionsにデプロイします

$ npm run deploy --prefix functions/src/

リクエスト

デプロイ後に表示されるhttpsTriggerURLにリクエストしHello Worldが表示されればデプロイに成功しています。

$ curl https://asia-northeast1-foo.cloudfunctions.net/helloWorld
$ Hello World

Cloud DatastoreのCLIツールを作った

$
0
0

GoogleCloudPlatform シェアフル Advent Calendar 2018 13 日目の記事です。

みんな大好きCloudDatastore。もちろん私も大好きなのですが、コマンドラインで操作(データの抽出とか削除とか)が出来ないのが個人的な不満点でした。
そこで、CloudDatastoreをCLIで操作するGo製のツール datastore-tools を作成しました。何番煎じかわからないくらい他にもありそうなツールですが、せっかく作ったし、おまけに年末でもありますからツール供養がてらご紹介させて頂きたいと思います。

何が出来るの?

  • エンティティを出力(Json形式、テーブル形式)
  • エンティティを更新
  • エンティティをtruncate

インストール方法

Goが入っている環境であれば、

$ go get github.com/keitaro1020/datastore-tools

でインストール出来ます。また、Windows/Mac/Linuxのバイナリも用意しています。

使い方

事前準備

CloudDatastoreにアクセスするために gcloud auth をするか、datastore.user のロールを持ったサービスアカウントを作成しキーファイルをダウンロードしておく必要があります。

エンティティを出力

datastore-tools select でエンティティを検索し、出力することが出来ます

$ datastore-tools select -p [project-name] -k [kind-name]
[
  {
    "__key__": {
      "Kind": "Fruits",
      "ID": 5634612826996736,
      "Name": "",
      "Namespace": ""
    },
    "description": "オレンジ",
    "name": "orange"
  },
  {
    "__key__": {
      "Kind": "Fruits",
      "ID": 5643096528257024,
      "Name": "",
      "Namespace": ""
    },
    "description": "りんご",
    "name": "apple"
  }
]

json形式で出力されるので、jqコマンドと組み合わせれば加工も容易です。
-tオプションをつけると、テーブル形式で出力することも出来ます

~ $ datastore-tools select -p [project-name] -k [kind-name] -t
+------------------+--------+-------------+
|       KEY        |  NAME  | DESCRIPTION |
+------------------+--------+-------------+
| 5634612826996736 | orange | オレンジ    |
+------------------+--------+-------------+
| 5643096528257024 | apple  | りんご      |
+------------------+--------+-------------+

エンティティを更新

datastore-tools update でエンティティを更新することが出来ます。
-sオプションに Property=value の形式で値を指定します。
-wオプションで条件を指定し、指定した条件の値のみを変更することも出来ます。

~ $ datastore-tools update -p [project-name] -k [kind-name] -w name=apple -s description="りんご とても美味しい"
~ $ datastore-tools select -p [project-name] -k [kind-name]
[
  {
    "__key__": {
      "Kind": "Fruits",
      "ID": 5634612826996736,
      "Name": "",
      "Namespace": ""
    },
    "description": "オレンジ",
    "name": "orange"
  },
  {
    "__key__": {
      "Kind": "Fruits",
      "ID": 5643096528257024,
      "Name": "",
      "Namespace": ""
    },
    "description": "りんご とても美味しい",
    "name": "apple"
  }
]

エンティティをtruncate

datastore-tools truncate でエンティティを全件削除することが出来ます。

~ $ datastore-tools truncate -p [project-name] -k [kind-name]
truncate finish, count = 2 
~ $ datastore-tools select -p [project-name] -k [kind-name]
[
]

まとめ

以上が現在の datastore-tools で出来ることになります。まだまだ至らない点もたくさんあるかと思いますが、多くの方に使っていただけたら嬉しいです。
もし不具合や改善提案等ありましたら、Githubのissueとかに書いてもらえれば順次対応していきます。

GCPのgcloudコマンドのインストールと最初の認証までを初心者向けに細かく解説

$
0
0

最近AWSだけではなく、マルチクラウドも当たり前につかえておかないと生きていけなそうなのでそろそろGCPを使い始めました。今回はGCPのリソースをCLIで操作できるgcloudコマンドのインストールと初期設定について初心者向けに細かく説明していきます。

gcloudコマンドをインストール

基本的には以下のコマンド一発で gcloud コマンドをインストールすることができます。
Google Cloud SDKをインストールするとついでに gcloudコマンドも使えるといった感じのようですね。

$ curl https://sdk.cloud.google.com | bash

それにしてもcurlでシェルスクリプト取得してインストールさせるなんておしゃれなことするな。Googleさん

$ curl https://sdk.cloud.google.com
#!/bin/bash

URL=https://dl.google.com/dl/cloudsdk/channels/rapid/install_google_cloud_sdk.bash

function download {
  scratch="$(mktemp -d -t tmp.XXXXXXXXXX)" || exit
  script_file="$scratch/install_google_cloud_sdk.bash"

  echo "Downloading Google Cloud SDK install script: $URL"
  curl -# "$URL" > "$script_file" || exit
  chmod 775 "$script_file"

  echo "Running install script from: $script_file"
  "$script_file" "$@"
}

download "$@"

macOS の場合は Google Cloud SDK は Homebrew Cask からインストールできるようです。

$ brew cask install google-cloud-sdk

さっそくインストールしていきましょう。
途中で色々聞かれますが基本的にはYesを答えていけばよいです。Enterキー押しまくったらOKです。

$ curl https://sdk.cloud.google.com | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   443    0   443    0     0   1381      0 --:--:-- --:--:-- --:--:--  1909
Downloading Google Cloud SDK install script: https://dl.google.com/dl/cloudsdk/channels/rapid/install_google_cloud_sdk.bash
######################################################################## 100.0%
Running install script from: /var/folders/0w/fhc_kcts3rz9r36c9qlx2vkr0000gn/T/tmp.XXXXXXXXXX.VPSuXP9n/install_google_cloud_sdk.bash
which curl
curl -# -f https://dl.google.com/dl/cloudsdk/channels/rapid/google-cloud-sdk.tar.gz
######################################################################## 100.0%

Installation directory (this will create a google-cloud-sdk subdirectory) (/Users/daisuke):
mkdir -p /Users/daisuke
tar -C /Users/daisuke -zxvf /var/folders/0w/fhc_kcts3rz9r36c9qlx2vkr0000gn/T/tmp.XXXXXXXXXX.nExxemcm/google-cloud-sdk.tar.gz
x google-cloud-sdk/
x google-cloud-sdk/lib/
x google-cloud-sdk/lib/third_party/
==========================================================
~ 略 ~
==========================================================
x google-cloud-sdk/.install/core.manifest
x google-cloud-sdk/.install/.download/

/Users/daisuke/google-cloud-sdk/install.sh
Welcome to the Google Cloud SDK!

To help improve the quality of this product, we collect anonymized usage data
and anonymized stacktraces when crashes are encountered; additional information
is available at <https://cloud.google.com/sdk/usage-statistics>. You may choose
to opt out of this collection now (by choosing 'N' at the below prompt), or at
any time in the future by running the following command:

    gcloud config set disable_usage_reporting true

Do you want to help improve the Google Cloud SDK (Y/n)? # <--------------------ここで "Enter"


This will install all the core command line tools necessary for working with
the Google Cloud Platform.



Your current Cloud SDK version is: 228.0.0
Installing components from version: 228.0.0

┌────────────────────────────────────────────────────────────────────────────┐
│                    These components will be installed.                     │
├─────────────────────────────────────────────────────┬────────────┬─────────┤
│                         Name                        │  Version   │   Size  │
├─────────────────────────────────────────────────────┼────────────┼─────────┤
│ BigQuery Command Line Tool                          │     2.0.39 │ < 1 MiB │
│ BigQuery Command Line Tool (Platform Specific)      │     2.0.34 │ < 1 MiB │
│ Cloud SDK Core Libraries (Platform Specific)        │ 2018.09.24 │ < 1 MiB │
│ Cloud Storage Command Line Tool                     │       4.34 │ 3.5 MiB │
│ Cloud Storage Command Line Tool (Platform Specific) │       4.34 │ < 1 MiB │
│ Default set of gcloud commands                      │            │         │
│ gcloud cli dependencies                             │ 2018.08.03 │ 1.5 MiB │
└─────────────────────────────────────────────────────┴────────────┴─────────┘

For the latest full release notes, please visit:
  https://cloud.google.com/sdk/release_notes

╔════════════════════════════════════════════════════════════╗
╠═ Creating update staging area                             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: BigQuery Command Line Tool                   ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: BigQuery Command Line Tool (Platform Spec... ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud SDK Core Libraries (Platform Specific) ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud Storage Command Line Tool              ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud Storage Command Line Tool (Platform... ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Default set of gcloud commands               ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: gcloud cli dependencies                      ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Creating backup and activating new installation          ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.

Update done!


Modify profile to update your $PATH and enable shell command
completion?

Do you want to continue (Y/n)? # <--------------------ここで "Enter"
The Google Cloud SDK installer will now prompt you to update an rc
file to bring the Google Cloud CLIs into your environment.

Enter a path to an rc file to update, or leave blank to use
[/Users/daisuke/.bash_profile]: # <--------------------ここで "Enter"
Backing up [/Users/daisuke/.bash_profile] to [/Users/daisuke/.bash_profile.backup].
[/Users/daisuke/.bash_profile] has been updated.

==> Start a new shell for the changes to take effect.


For more information on how to get started, please visit:
  https://cloud.google.com/sdk/docs/quickstarts

.bash_profile内に以下が追記されていることを確認します。
ちなみにこのファイルたちは

.bash_profile
# The next line updates PATH for the Google Cloud SDK.
if [ -f '/Users/daisuke/google-cloud-sdk/path.bash.inc' ]; then . '/Users/daisuke/google-cloud-sdk/path.bash.inc'; fi

# The next line enables shell command completion for gcloud.
if [ -f '/Users/daisuke/google-cloud-sdk/completion.bash.inc' ]; then . '/Users/daisuke/google-cloud-sdk/completion.bash.inc'; fi

あとはこの.bash_profileの設定を再読み込みして、gcloudコマンドが実行できることを確認すればOKです🎉

# 再読み込みして
$ source .bash_profile
# gcloudコマンドが叩ければ完了です
$ gcloud version
Google Cloud SDK 228.0.0
bq 2.0.39
core 2018.12.07
gsutil 4.34

初期設定

インストールが終わったら、認証情報などを設定するために gcloud init コマンドを実行しましょう。

$ gcloud init 
Welcome! This command will take you through the configuration of gcloud.

Your current configuration has been set to: [default]

You can skip diagnostics next time by using the following flag:
  gcloud init --skip-diagnostics

Network diagnostic detects and fixes local network connection issues.
Checking network connection...done.
Reachability Check passed.
Network diagnostic passed (1/1 checks passed).

You must log in to continue. Would you like to log in (Y/n)? # <------------- ここで "Enter"

ログインするように求めらるのでEnter(Y)を押すとログイン画面がブラウザで立ち上がります。

image.png

最後にこの画面までくれば認証は完了です。

image.png

次にプロジェクトを選べと言われますのでここはご自由に。
GCPをコンソール画面から使用したことがある人ならプロジェクトは当然作っているはずですね。
もしプロジェクトを作ったことがなければこのままCLIで作ってしまうこともできます。

You are logged in as: [gee.awa@gmail.com].

Pick cloud project to use:
 [1] global-phalanx-xxxxxxx
 [2] Create a new project
Please enter numeric choice or text value (must exactly match list
item):  # <---------- 1 or 2を入力

あとはリージョンを選べと言われるのでここもご自由にどうぞ

Do you want to configure a default Compute Region and Zone? (Y/n)? <----------- ここで "Enter"

Which Google Compute Engine zone would you like to use as project
default?
If you do not specify a zone via a command line flag while working
with Compute Engine resources, the default is assumed.
 [1] us-east1-b
 [2] us-east1-c
 [3] us-east1-d
 [4] us-east4-c
 [5] us-east4-b
 [6] us-east4-a
 [7] us-central1-c
 [8] us-central1-a
 [9] us-central1-f
 [10] us-central1-b
 [11] us-west1-b
 [12] us-west1-c
 [13] us-west1-a
 [14] europe-west4-a
 [15] europe-west4-b
 [16] europe-west4-c
 [17] europe-west1-b
 [18] europe-west1-d
 [19] europe-west1-c
 [20] europe-west3-b
 [21] europe-west3-c
 [22] europe-west3-a
 [23] europe-west2-c
 [24] europe-west2-b
 [25] europe-west2-a
 [26] asia-east1-b
 [27] asia-east1-a
 [28] asia-east1-c
 [29] asia-southeast1-b
 [30] asia-southeast1-a
 [31] asia-southeast1-c
 [32] asia-northeast1-b
 [33] asia-northeast1-c
 [34] asia-northeast1-a
 [35] asia-south1-c
 [36] asia-south1-b
 [37] asia-south1-a
 [38] australia-southeast1-b
 [39] australia-southeast1-c
 [40] australia-southeast1-a
 [41] southamerica-east1-b
 [42] southamerica-east1-c
 [43] southamerica-east1-a
 [44] asia-east2-a
 [45] asia-east2-b
 [46] asia-east2-c
 [47] europe-north1-a
 [48] europe-north1-b
 [49] europe-north1-c
 [50] northamerica-northeast1-a
Did not print [6] options.
Too many options [56]. Enter "list" at prompt to print choices fully.
Please enter numeric choice or text value (must exactly match list
item):  # <--------------------------------------------------- 1 ~ 50で選ぶ

Your project default Compute Engine zone has been set to [us-east1-b].
You can change it by running [gcloud config set compute/zone NAME].

Your project default Compute Engine region has been set to [us-east1].
You can change it by running [gcloud config set compute/region NAME].

Created a default .boto configuration file at [/Users/daisuke/.boto]. See this file and
[https://cloud.google.com/storage/docs/gsutil/commands/config] for more
information about configuring Google Cloud Storage.
Your Google Cloud SDK is configured and ready to use!

* Commands that require authentication will use gee.awa@gmail.com by default
* Commands will reference project `global-phalanx-225913` by default
* Compute Engine commands will use region `us-east1` by default
* Compute Engine commands will use zone `us-east1-b` by default

Run `gcloud help config` to learn how to change individual settings

This gcloud configuration is called [default]. You can create additional configurations if you work with multiple accounts and/or projects.
Run `gcloud topic configurations` to learn more.

Some things to try next:

* Run `gcloud --help` to see the Cloud Platform services you can interact with. And run `gcloud help COMMAND` to get help on any gcloud command.
* Run `gcloud topic --help` to learn about advanced features of the SDK like arg files and output formatting

完了です。

これで快適なgcloudライフを送る準備が整いましたね。
クラウド生活をエンジョイしましょう!:smirk:

Cloud Shell環境を使ってCloud Shellのベースイメージを変更する

$
0
0

この記事は Google Cloud Platform その1 Advent Calendar 2018 の15日目の記事です。
なんだかんだでQiita初投稿となります(アドベントカレンダーが初って人も珍しい気が…)。

弊社内にてGCP上でAnsibleとTerraformを用いたハンズオンを実施している際、「AnsibleとTerraformをはじめからインストールしている環境が欲しい」という意見をいただきました。(内容については弊社内ブログにて公開しております。)
ハンズオンはCloud Shellを利用していましたので、いつの間にかアルファ版として実装されていたCloud Shell環境を利用してコンテナイメージを作成してみます。

Cloud Shell環境の確認

Cloud Shellを開き、右上にあるアイコン一覧の一番左にあるモニターアイコンをクリックします。
CloudShell環境1.PNG

クリックするとCloud Shell環境の画面が新規タブでCloud Shell環境が開きます。
CloudShell環境2.PNG

Cloud Shellコンテナの作成

早速コンテナを作成します。
手順は以下の通りです。

  1. カスタムイメージの作成よりイメージを作成する
  2. Dockerfileを編集する
  3. イメージが作成されるので確認する

カスタムイメージの作成よりイメージを作成する

「カスタムイメージの作成」から、「新しいカスタムイメージを作成」をクリックします。

CloudShell環境2.5.PNG

今回は以下のように作成します。

項目名 設定値
イメージストレージ用のプロジェクト 作成するプロジェクト名
イメージ名 ansible_terraform

またチェックについては両方にチェックをつけ、「作成」ボタンをクリックします。

CloudShell環境3.PNG

※必要なAPIが許可されていない場合、「APIを有効にしてください」と表示されるため、APIを有効化します。

CloudShell環境3-1.PNG

Dockerfileを編集する

作成が終わると、gitリポジトリのクローンを確認されるため、続行をクリックしてリポジトリを作成します。
リポジトリ作成後、Dockerfileが表示されます。

CloudShell環境4.PNG

Dockerfile内に以下ソースを追加します。

RUN wget https://releases.hashicorp.com/terraform/0.11.8/terraform_0.11.8_linux_amd64.zip
RUN unzip terraform_0.11.8_linux_amd64.zip -d /usr/local/bin
RUN pip install ansible

作成後、gitリポジトリにaddします。

git add Dockerfile
git commit -m "edit Dockerfile"
git push origin master

pushすると、自動でCloud Buildが走ってDockerイメージを作成してくれます。すごいですね…。

CloudShell環境6.PNG

作成は10分程度で終わります。作成終了後、Cloud Shell環境より[編集]をクリックします。
環境の編集にて、「プロジェクトからイメージを選択する」を選択します。

CloudShell環境7.PNG

イメージを選択から、先ほど作成したイメージを選択します。

CloudShell環境8.PNG

先程作成したイメージを選択して、「保存」をクリックするとベースイメージが変更されます。
Cloud Shellコンソールを開いている場合はセッションを再起動してください表示されるため、再起動します。
再起動するとコンテナに接続しますが、結構長く接続中とでるためのんびり待っていると起動します。

起動後、イメージ内にAnsibleとTerraformがインストールされているか確認します。

CloudShell環境9.PNG

これでCloud Shell上にAnsibleとTerraformがインストールされたイメージの作成ができました。
Dockerfileを作成するだけでイメージが簡単に作成できるため、Cloud Shellを利用した研修を行うならかなりいいかもしれないです。


CloudBuildか、GKE

GCR(Google Container Registry)を高速で安価なDockerのプライベートリポジトリとして利用する(?)

$
0
0

モチベーション

dockerイメージをgcrからpullするだけなら gcloud docker pull/push を利用するのが一番手軽で良いと思いますが、稀にgcloudコマンドをインストールせずにdockerコマンドのみある環境で GCR の認証を行いたい場合もあると思います。
そんな人のための記事になります。

追記 : GCRを高速なプライベートリポジトリとして利用できるので、嬉しい人は多いかもしれない。ということでタイトルも変更。

事前準備

  • gcloudコマンドインストールした端末を用意する
  • gcpのプロジェクトは作成しておく

そのぐらい、gcloudインストールしたくないとはいっても、認証のトークンを取得するためにどこかの端末にインストールし、 gcloud auth login は済ませて置く必要があります

認証のトークンを取得する

以下のコマンドを実行し、出力されるトークン(割と長めな文字列)をメモしておく。

gcloud auth print-access-token

GCRにdocker loginする

以下のようなコマンドで docker login する

docker login -u oauth2accesstoken -p {先程のトークン} https://asia.gcr.io

Login Succeeded と表示されればOK

試しに docker push/pull してみる

Dockerfileを配置し、コンテナをbuildする

Dockerfileは以下のように

FROM alpine
CMD echo "hello! ore-dayo!"

Dockerfileを配置したディレクトリで以下のコマンドでdocker buildを行う

docker build -t asia.gcr.io/{project id}/oreore .

コンテナをdockerコマンドでpushしてみる

以下のコマンドを実行

docker push asia.gcr.io/{project id}/oreore

問題なし。

GCPのコンソールからGCRないにpushしたイメージが見えているはず。

コンテナをdockerコマンドでpullしてみる(runする)

事前にbuildしたイメージは消しておく。

docker rmi {containerのid}

コンテナを実行する

docker run --rm asia.gcr.io/{project id}/oreore

コンテナイメージのpullが完了した後に、 hello! ore-dayo! と出れば成功。

これで gcloud をインストールしていない環境でも GCR からイメージをpush/pullできそう。

enjoy!

BQやDataStudioについて書く予定 → 書きました『BigQueryのコスト可視化ダッシュボードを10分で作る』

GCP MarketPlaceからWordPress環境を5分で構築する

$
0
0

Wordpressを使ってみる必要があったので、
ローカルに構築しようと思ったけど、MySQL入れたり割と面倒だったので
いっそのことGCPの無料枠で使ってみようかと思い構築してみました。

MarketPlace使えば一瞬という話ですが、真相は如何に
(ちなみに最近GCP触り始めたのでMarketPlace使うのも初めてです)

プロジェクトを作成

対象のプロジェクト名を入力して作成ボタンをクリック

image.png

WordPressをデプロイ

MarketPlaceから「WorkPress(Google Click to Deploy タイプ 仮想サーバー)」を選択

image.png

「COMPUTE ENGIN上で起動」をクリック

image.png

image.png

「デプロイ」ボタンをクリックすると数分で環境が構築される。

image.png

WordPressにログインする

環境構築完了後表示されるWordPressの構成情報にサイトおよび管理画面のURLを記載されている。
(したの方にユーザーとパスワードも)

image.png

image.png

あとは、Wordpressの管理画面から記事を書いたりPlugin入れたり色々できる。

image.png

結論

まじで5分でできた!
構成情報から、次の操作へのリンクなどが漏らさずついてるなど
GCPは本当にUIが素晴らしいですね

イメージの展開とVMインスタンスのスケーリング運用

$
0
0

はじめに

今回は、GCPでTerraformとPackerを用いた運用の一例を紹介させていただきます。

  • Packerで取ったイメージを展開
    Ansibleでプロビジョニングしています。

  • 定期的にスケーリング
    弊社が運用しているサービスは、昼と夜の決まった時間にトラフィックが上がるのでサーバの負荷が上がるタイミングをある程度予測することができるという特徴を持っています。そのため定刻にスケーリングさせたいという要望がありました。


本記事で説明に使用するサンプルです→https://github.com/Takashi-Kawano/scaling_sample

インスタンステンプレート作成

terraform/providers/dev/terraform.tfvars
project_id      = "meta-iterator-225901"
region          = "us-west1"
project_name    = "scaling-sample"
dev_target_size = "1"
zone            = "us-west1-a"
network         = "default"

machine_dev     = {
    "name"              = "dev"
    "machine_type"      = "f1-micro"
    "env_name"          = "dev"
    "group_name"        = "dev"

    "disk_type"         = "pd-standard"
    "disk_size"         = "30"

    "update_strategy"   = "NONE"
}

"instance_group_zones"  = [
    "us-west1-a",
]
scaling_sample/terraform/providers/dev/managed_group.tf
resource "google_compute_instance_template" "instance_template" {
  name_prefix  = "${var.project_name}-${lookup(var.machine_dev, "name")}-"
  machine_type = "${lookup(var.machine_dev, "machine_type", "f1-micro")}"
  region       = "${var.region}"

  disk {
    source_image = "${coalesce(var.dev_image, data.terraform_remote_state.output.dev_image)}"
    disk_type    = "${lookup(var.machine_dev, "disk_type", "pd-standard")}"
    disk_size_gb = "${lookup(var.machine_dev, "disk_size", "30")}"
    device_name  = "${lookup(var.machine_dev, "device_name", "persistent-disk-0")}"
  }

  network_interface {
    network = "${var.network}"
    access_config {}
  }

  lifecycle {
    create_before_destroy = true
  }
}

source_imageの値を変数(dev_image)にすることで、Terraformコマンドを実行する際にイメージを指定することができます。

実行例
$ terraform apply -var "dev_image=scaling-sample-dev-instance-1545206547"

マネージドインスタンスグループ作成

マネージドインスタンスグループに割り当てられているインスタンステンプレートに割り当てられているビルドイメージを元にVMインスタンスが作成されるようになります。

scaling_sample/terraform/providers/dev/managed_group.tf
resource "google_compute_instance_group_manager" "instance_group_manager" {
  name = "instance-group-manager"
  instance_template = "${google_compute_instance_template.instance_template.self_link}"
  base_instance_name = "dev-instance"
  zone = "${var.zone}"
  target_size = "${var.dev_target_size}"
  update_strategy = "ROLLING_UPDATE"
  rolling_update_policy {
    minimal_action = "REPLACE"
    type = "PROACTIVE"
    max_unavailable_fixed = 0
  }
}

target_sizeの値を変数(dev_target_size)にすることで、Terraformコマンドを実行する際に起動するVMインスタンスの数を指定することができます。

実行例
$ terraform apply -var "dev_target_size=1"

また、update_strategyを使用して、rolling_update_policyにローリングアップデートの条件を設定しています。
既に起動しているVMインスタンスに対しては、再生成をして新しいVMインスタンスに置き換えることでイメージを適用させます。Terraformコマンド実行時にローリングアップデート処理を開始し、再生成中に停止しているVMインスタンスの数を0と設定しています。こうすることで、運用中のサービスでも止めることなくローリングアップデートができます。
こちらはbeta版ということらしいです。https://www.terraform.io/docs/providers/google/r/compute_instance_group_manager.html

今回サンプルでは使用してみたのですが、実際の運用ではまだ導入はしておらず、イメージの割り当て後にGCPコンソールから手動でローリングアップデートを実行しています。

Packerでイメージ作成

Packerを使ってインスタンスイメージをビルドします。プロビジョニングにAnsibleを使用しています。

scaling_sample/ansible/playbook/build_image.yml
- name: Execute Base Setting...
  hosts: "{{ packer_build_target }}"
  become: yes
  gather_facts: yes

  tasks:
    - name: Create sample file
      copy:
        content: helloworld
        dest: /message
ansible/packer/packer.json
{
  "builders": [{
    "type": "googlecompute",
    "account_file": "{{user `credential_file`}}",
    "project_id": "{{user `project_id`}}",
    "source_image_family": "centos-7",
    "zone": "{{user `zone`}}",
    "instance_name": "{{user `project`}}-{{user `app_stage`}}-instance-base",
    "machine_type": "{{user `machine_type`}}",
    "disk_size": "{{user `disk_size`}}",
    "image_family": "{{user `project`}}-{{user `app_stage`}}-instance",
    "image_name": "{{user `project`}}-{{user `app_stage`}}-instance-{{timestamp}}",
    "network": "default",
    "subnetwork": "default",
    "omit_external_ip": false,
    "use_internal_ip": false,
    "tags": [
        "packer-build"
    ],
    "ssh_username": "ansible",
    "ssh_private_key_file": "../credentials/ansible_key"
  }],
  "provisioners": [
    {
      "type": "ansible",
      "playbook_file": "{{user `playbook_file`}}",
      "extra_arguments": [
          "-i", "./inventory",
          "-e", "packer_build_target={{user `project`}}-{{user `app_stage`}}-instance-base",
          "-e", "packer_build=yes"
      ]
    }
  ]
}
ansible/packer/vars/scaling-sample-dev.json
{
    "credential_file": "../credentials/scaling-sample-427451a6e5ec.json",
    "project_id": "meta-iterator-225901",
    "zone": "us-west1-a",
    "project": "scaling-sample",
    "machine_type": "f1-micro",
    "disk_size": "30",
    "app_stage": "dev",
    "group": "dev",
    "network": "default",
    "playbook_file": "playbook/build_image.yml"
}

以下のコマンドを実行してイメージビルドします。

$ packer build -var-file=packer/vars/scaling-sample-dev.json packer/packer.json

処理が正常に終了すると、以下のようにビルドされたイメージファイルの名前が表示されます。

(略)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

dev_image = scaling-sample-dev-instance-1545206547
dev_target_size = 1

イメージ割り当て(ローリングアップデート)+スケーリング

上で説明したイメージの割り当てとスケーリングをひとまとめに実行します。

$ terraform apply -var "dev_target_size=1" -var "dev_image=scaling-sample-dev-instance-1545206547"

GCPコンソール上で確認すると、新しいVMインスタンスが起動し、既存のVMインスタンスが停止→削除される様子が確認できます。
スクリーンショット 2018-12-19 22.24.41.png

VMインスタンスにsshログインして以下のようになれば、正常にイメージが割り当てられていることを確認できます。

[dev-instance-b2rv ~]$ cat /message
helloworld

Jenkinsでスケーリングを定期実行

上記のTerraformコマンドでスケーリングとイメージの割り当て(とローリングアップデート)ができるので、これをシェルスクリプトで実行し、定期的にジョブを走らせるようにcronを設定しています。
JenkinsでTerraformコマンドを実行させるためのサンプル用の設定は気が向いたらやっておきます:bow:

なぜAutoScalingを使っていないのか

GCPのマネージドインスタンスグループにはAutoScalingという機能が提供されています。これは平均CPU使用率や秒間リクエスト数、StackdriverMonitoringの指標に閾値を設定して、設定した閾値に到達した際に自動でVMインスタンスを増やすという機能です。

現在のアーキテクチャーの仕組みだと新規にVMインスタンスを追加したときに、デプロイ処理を行ってロードバランサーのヘルスチェックが通るまでの間に時間が掛かかります。なので、ヘルスチェックが通るまでの時間差と負荷に対する閾値の考慮が必要でそれを検証するための時間がまず割けていません。。尚且つ、最初に説明した通りある程度トラフィックが上がるタイミングを予測できるので、Jenkinsを使って定期的にスケーリングをするという方針をとっています。

プリエンプティブインスタンスを利用

トラフィックが上がってくるタイミングでVMインスタンスを増やす際にはプリエンプティブインスタンスを利用しています。これを利用することでコストを大幅に削減することができています。ですが、東京リージョンを使用している方々が多いせいか、一時プリエンプティブリソースが充足していない時があって必要な数のプリエンプティブインスタンスが起動せずに焦ることがあったりなど、コスト削減と引き換えに運用面で苦労する場面があるので利用する際には注意が必要です。弊社での運用例の詳細はこちらの記事を参照してください。

終わりに

定刻でスケーリングするだけでなく、トラフィックが多くなると予想されるイベントが始まる前に事前にスケールアウトして備えておくといったことも簡単にできるのでとても便利です。

今後の展望としては、今回のサンプルで使用したローリングアップデートの自動化を適用したり、デプロイ処理をもっと早くできるようにして、よりスムーズにイメージの適用とスケーリングを行えるようにしていきたいと考えています。

以上となります。
最後まで読んでいただきありがとうございました!

Viewing all 23 articles
Browse latest View live