Quantcast
Viewing all articles
Browse latest Browse all 191

GAE Managed VM & Custom Runtimeについて

このエントリーはGoogle Cloud Platform Advent Calendar 2015の14日目です。

10月に入社しましてブログには初めて投稿します石井です。休日はJavaScriptやGoで趣味の開発を行っていたり、家に知人を集めてボードゲーム会をしてたりします。

さて、ALBERTはAWSメインの開発を行っているのですが、GCP (Google Cloud Platform) も最近盛り上がって来ている!という事で、新しい案件をGCPで構築してみることにしました。その際にGAE (Google AppEngine) 周りの調査を色々と行ったので、今回は特にManaged VMとCustom Runtimeについて紹介できればと思います。

GAE Managed VM, Custom Runtimeは簡単に言うと、GAEが裏で管理していたコンテナを触れることができるようになったり、自分で定義したDockerのコンテナをGAE上で動かすことがでるようになるものです。


GAE sandbox

予めGCPのコンソールから適当なプロジェクトを作成しておきます。

まずは基本のGAEプロジェクトから段階的にVM使うようにしてみます。
app.yamlとそこから呼び出してるmain.pyを作成、デプロイします。

runtime: python27
api_version: 1
threadsafe: false

handlers:
- url: /
  script: main.app

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('Hello, World!')

app = webapp2.WSGIApplication([
    ('/', MainPage),
], debug=True)

$ gcloud preview app deploy app.yaml –promote
でデプロイします。

ブラウザでhttps://プロジェクト名.appspot.com/を見てみるとHello, World!が出ます。
ここまではよくあるsandboxのhello worldですね。


GAE Managed VM (MVM)

次に、このpythonアプリケーションをMVMに変更してみます。

runtime: python27
vm: true
api_version: 1
threadsafe: false

handlers:
- url: /
  script: main.app

manual_scaling:
  instances: 1

さっきと比べ、app.yamlに
vm: true
manual_scaling: {instances: 1}

が追加されただけです。

MVMで重要なのはvm: trueの方で、これを追加するとMVMモードでアプリケーションが動作するようになります。
manual_scalingはスケーリング・負荷分散の設定値です。MVMはデフォルトで2つのインスタンスを立ち上げ分散するようになっているのですが、今回は特に必要ないので1つのインスタンスで処理するように設定しています。

先ほどと同じように
$ gcloud preview app deploy app.yaml –promote
でデプロイします。

MVMモードでVMを立ち上げているのでsandboxの時より少し時間が掛かるかと思います。気長に待って、先ほどと同じhttps://プロジェクト名.appspot.com/にアクセス。ちゃんと動作しているのが確認できます。また、GCEのコンソールへ移動すると今デプロイしたアプリケーション用のインスタンスが起動しているのが確認できると思います。sandboxで隠されていたインスタンスの中身があれこれ確認できるのですね。感激です。

注意する点として、MVMで立ち上がったインスタンスは新しいバージョンのアプリケーションがデプロイされても削除されず動き続けます。消したい場合はGAEコンソールから過去のバージョンを削除しないといけません。GCEから直接インスタンスを削除しても、しばらくするとオートスケーリングの設定により自動的にインスタンスが立ち上がってしまいます。このへん新しいバージョンのアプリケーションがデプロイされたら自動で消えるようにしたかったのですが調べてもよくわかりませんでした。何かご存じの方はご教授頂ければと思います。

さて、せっかく立ち上げたインスタンスですが、これだけじゃVMの何が嬉しいのかわからないのでちょっとアプリケーションをいじってみます。と言ってもsqliteでローカルのディスクへ書き込む簡単なものですが。
GAE sandboxだとfsへのアクセスができないのですが、vmにすればfsにも触れる用になるので、sqliteでローカルにDBファイルを作って読み書きすることも可能なはずです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import webapp2
import sqlite3
from time import time

class MainPage(webapp2.RequestHandler):
    def __init__(self, *args, **kwargs):
        super(MainPage, self).__init__(*args, **kwargs)
        self.conn = sqlite3.connect('mydb')
        self.cur = self.conn.cursor()
        self.cur.execute('''
                    CREATE TABLE IF NOT EXISTS welcome
                    (time NUMBER DEFAULT 0)
                    ;''')

    def update_count(self):
        self.cur.execute('INSERT INTO welcome(time) VALUES (?);', (time(),))
        self.cur.execute('SELECT count(*) from welcome')
        self.conn.commit()
        return self.cur.fetchone()

    def get(self):
        count = self.update_count()
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('''Hello, World!
You are %sth visitor!
''' % count)

app = webapp2.WSGIApplication([
    ('/', MainPage),
], debug=True)

シンプルすぎるカウンター。
どうしようもないアプリケーションですがそこは気にせず、これを今までのようにデプロイするとアクセスのたびにカウンターがちゃんとインクリメントされているのが確認できます。
Image may be NSFW.
Clik here to view.
スクリーンショット 2015-12-14 13.02.37

何気ないアプリケーションですがGAEでローカルファイルへ書き込むことができるようになりました!

さて、MVMで立ち上がったインスタンスですが、GCEのインスタンスとして立ち上がっているのでもちろんSSH接続して中身を確認することができます。MVMで立ち上がったインスタンスはgoogle管理のインスタンスとなり、「途中で中身をいじって変更したまま動かし続ける」事はできませんが、弄ることだけはできます。試しにGCEインスタンスの一覧からsshボタンを押すと「ユーザー管理への切り替え」とダイアログが出てきて、変更は全て失われますと表示されます。
Image may be NSFW.
Clik here to view.
スクリーンショット 2015-12-14 13.05.07

何はともあれsshしてみるとそれらしいコンテナが動いているのが確認できますので、それに接続してみます。

$ sudo su -
$ docker ps
CONTAINER ID        IMAGE                                                                             COMMAND                  CREATED             STATUS              PORTS                              NAMES
9d2f45e52d31        gcr.io/google_appengine/mvm-agent                                                 "/usr/local/bin/gunic"   10 minutes ago      Up 10 minutes                                          condescending_allen
ca653eb06a3e        gcr.io/google_appengine/nginx-proxy                                               "/var/lib/nginx/bin/s"   10 minutes ago      Up 10 minutes       0.0.0.0:8080->8080/tcp, 8090/tcp   evil_meninsky
a68171a218ac        appengine.gcr.io/498347750172306472/プロジェクト名.default.20151214t123834   "/usr/bin/python2.7 /"   10 minutes ago      Up 10 minutes       8080/tcp                           gaeapp
73cb013cc55b        gcr.io/google_appengine/memcache-proxy                                            "/home/memcache/memca"   10 minutes ago      Up 10 minutes       11211/tcp                          memcache
40812862abb0        gcr.io/google_appengine/fluentd-logger                                            "/opt/google-fluentd/"   11 minutes ago      Up 11 minutes                                          boring_tesla

$ docker exec -it a68171a218ac bash --login

コンテナの中に入れました!

$ pwd
/home/vmagent/app
$ ls
Dockerfile  app.yaml  main.py  main.pyc  tmpTIqZW9
$ cat Dockerfile
# Dockerfile extending the generic Python image with application files for a
# single application. This is only used for pre- env: 2 deployments.
# The name of this image is for historical reasons 'python-compat', but it
# refers to a version of the runtime separate from the 'runtime: python-compat'
# image.
FROM gcr.io/google_appengine/python-compat
ADD . /app

$ Ctrl+p
$ Ctrl+q

最後はCtrl+p→Ctrl+qでコンテナから抜けます。豪快にexitするとコンテナが終了してアプリケーションが死んでしまうので気をつけてください。
いろいろと面白いものが見れましたね。ログインしたディレクトリにあったDockerfileが今まさにGAEで動いているコンテナのイメージファイルになります。


GAE MVM Custom Runtimes

実はこのDockerfileを自分で書いてグリグリするのもCustom Runtimeという形で公式にサポートされています。

GoogleCloudPlatform/appengine-nginx-hello
公式のサンプルではnginxを動かしています。これを真似てpythonのflaskを動かすコンテナを書いてみたのがこれです。以下はこれの説明です。
airtoxin/gae-mvm-custom-runtime-sample

runtime: custom
vm: true
api_version: 1
threadsafe: false

handlers:
- url: /
  script: dynamic

manual_scaling:
  instances: 1

nginxのyamlファイルを参考にしました。runtime: customとしてDockerfileを同梱するとそれを動かせるようになります。
handelrsのscript: dynamicは公式のマニュアルをどれだけ探しても出てこないのですが…おそらくリクエストをGAE側でハンドリングせず、サーバーに直接届けてくれるようなオプションかと思います。

で、動かすDockerfileですが、せっかくなのでpython3を動かしてみます。

FROM python:3.4.3

ADD requirements.txt requirements.txt
RUN pip install -r requirements.txt
ADD . .

ENTRYPOINT ["python3", "main.py"]

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from flask import Flask
app = Flask(__name__, static_folder='static')

@app.route("/")
def hello():
    return app.send_static_file('index.html')

@app.route("/_ah/start")
def _ah_start():
    return "ok"

@app.route("/_ah/health")
def _ah_health():
    return "ok"

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080)

こんな感じのDockerfileとpythonのスクリプトを用意して、doker runするとmain.pyでサーバーが起動するようにしてやります。

サーバーはflaskで立ててみます。sandboxだとdjangoかwebapp2くらいしか呼び出せず少々めんどくさかったのが、好きなライブラリを導入できるので軽量なライブラリでさっと書けるのがいいですね。
/へのルーティングはstatic/index.htmlを返すようにしていますので、これは適当に配置しておきます。/_ah/start/_ah/healthはGAEのライフサイクルイベントで、GAEが定期的にリクエストを送り、コンテナがreadyなのかhealthyなのかを確認するのでそれに応答するようにしています。healthの方はステータス200で何かメッセージを送ればよいようです。

あとはrequirements.txtに依存ライブラリを記述しておきます。これは先程のDockerfile内で、コンテナ内のpythonにインストールするようにしています。

Flask==0.10.1
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.11.2
wheel==0.24.0

ここまできたらデプロイはお馴染み
$ gcloud preview app deploy app.yaml –promote
ですね。
Image may be NSFW.
Clik here to view.
スクリーンショット 2015-12-14 14.45.48

来ました!勝手に定義したDockerfileがGAEで動いてます!


まとめ

GAE sandboxはVMの管理をGAEがやってくれる代わりにfsを触れないなどの制限がある。
そこでManaged VMにするとVMの管理をユーザーが行う必要がある代わりにそれらの制限を取っ払える。
さらにCustom RuntimeにするとVM自体をユーザーが定義したものを使用できる。

Custom RuntimeまでやるともはやGCEでやるのとほぼ同じ環境が手に入れられますね。スケーリング周りを引き続き面倒見てくれるので、この辺とリクエストのハンドリング周りの自由度で使い分ける感じでしょうか。


Viewing all articles
Browse latest Browse all 191

Trending Articles