thirose’s blog

openstackやpythonなどなど

バンクーバーに行ってきた

 

OpenStack Summitに参加するため、バンクーバーに行ってきました。そっちの感想等は他のブログにまとめています。

  

最近は仕事で英語が必要になり、レアジョブで英会話レッスンをひたすらしてたけど不安といえば少しは不安でしたが、心強い英語が堪能な同僚が4名いたし、実際に一緒に行動する時間はランチとディナーの時だけだったけど、それでも困ることは無かったです。

そんなバンクーバーでのサミット以外のことをまとめておこうと思います。

 

バンクーバー(カナダ)

電車が無人運転ゆりかもめみたいな感じになってて、先頭と最後部は外を覗ける席になっていて、ちょっとしたアトラクションっぽくなってて、飛行機で寝なかったからウトウトしながら景色を楽しめました。 Compassカードというのを買ってタッチする仕組み。空港があるSea Islandエリアから電車に乗ろうとすると特別料金が発生し、+5カナダドル必要になります。

f:id:hirosetakahito:20180525222249j:image

 

生活について

まず、ご飯とかどこでもカードがあれば大体の支払いはどうにかなる。どうにかならなそうなのハウスキーピングのチップぐらいな気がする。そのぐらい支払いはカードで対応出来てた。  

交通網も大体compassカードというスイカみたいのでなんとかなるっぽいし本当にカードで困らなかった。  

最終日は念のため持ってったカナダドルをどう使おうか悩みになやんだぐらいだ。  

 

次に意外と思うかもしれないが、緯度が高い割に暖かいこと。日本の北海道より少し高いかもしれない。日陰に10分ぐらいいると少し肌寒くなるけど、基本的に暖かい。タンクトップの人もたくさんいた。そして緯度が高いから21時ぐらいまで明るい。20時過ぎに撮った外の様子

f:id:hirosetakahito:20180525074622j:image

時間感覚がすごく狂いそうになった(むしろ時差ボケで狂ってた)けど、不思議な感覚だった。  

でもサミット最終日の終了後スタンレーパークを散歩してたら、仕事帰りに自転車でサイクリングしてる人が居て、とても良いなーと思った。  

明るいし、仕事のオン・オフがハッキリしてるのか、すごく楽しんでるように見えた。  

スタンレーパークは自転車道と歩道がしっかり分かれてて、皆好き好きに楽しんでいた。終わってしまったサミット会場を眺めてもう終わったなーと感慨深くなれた。そんな場所だった。  

全く関係ないけど、ここの水族館は、ドリーにでてくる水族館のモデルにも少しなってるらしい。  

食事について

まずは太りそうな食事が多かった。これが初日に食べたハンバーガーとポテトのセット。寝不足の身体には重かったけどとても美味しかった。ポテトはおかわりしたいぐらいに。

f:id:hirosetakahito:20180525074907j:image

 

次にマクドナルド。まず驚いたのは注文が行った店舗ではタッチパネル式で、支払いもカード決済で楽ちんなやつになってたこと。

f:id:hirosetakahito:20180525160234j:image

あと、カナダの伝統料理のプーティンが注文できます。プーティンとは、ポテトにチーズが乗っていて、その上からグレイビーソースがかかったカナダの伝統料理。マクドナルドで4ドル20セントぐらいだった気がするので結構お手軽です。

f:id:hirosetakahito:20180524115713j:image

コーラは空のコップだけ渡されるので、大体ドリンクバーの要領で出来るんだけど、まぁ最初は何が何だかわかんなくてあたふたしてました。  

あと、ドリンクと、ポテトはSサイズにしても、日本のSよりは大きく、日本のMよりは若干小さいかなーぐらいのサイズ感でかなり満腹になれます。

 

自動販売機とコンビニが少なく、tim hortonsやマクドナルド、スターバックスがやたらと多かった。

 

感想

子連れでの旅行は子供が小学生になるぐらいまで難しいかなーという気はしたけど、今回の目的はサミットであって、とても良い場所でサミットに参加出来てとても良い体験ができました。が、英語でのコミュニケーションがもっと取れるともっと楽しめたんだろうなと思うので、今後も継続して練習する必要性を感じました。

OpenStack Summit Vancouver 2018

最近仕事でOpenStackの開発・運用に関わっていてその関係もあり、OpenStack Summitに参加させてもらいました。
OpenStack Summit | Vancouver 2018
f:id:hirosetakahito:20180525160434j:plain:w300

OpensStackって何?

そもそもOpenStackって何?って思った方もいるかと思いますが、プライベートクラウドをいい感じに構築するソフトウェア群のことをさします。

2010年ごろに始まって当初はIaaS環境の構築が主な仕組みだったようですが、最近では、containerにも積極的です。 今後もvGPU機能に対応してく姿勢を持っていたり、柔軟に今やこれからをフォローしている印象が強いです。

セッションについて

セッションは、初心者がアップストリームにコミットしていけるようにハンズオンを開催したり、流行りのコンテナーサービスに関する発表だったり、コアコンポーネントに関する今後の議論をするだけのセッションだったり、多岐に渡っています。
今回のサミットで参加しようと考えたセッションは、OpenStackのコアコンポーネントの現状と今後について聞きけるセッションを考えていましたが、k8sやedge computingなどの話も豊富でだったので、ざっくばらんに参加してきました。   

参加セッション

  • 05/21

    • Let's Build the Open Infrastructure Economy
    • OpenStack community-wide goals and project updates
    • Meet Zuul V3 (release 28/march 2018)
    • Kata Containers Overview
    • OpenStack and Kubernetes in a Hybrid Cloud World
    • Open Source Infrastructure
    • Integration testing on an OpenStack public cloud
    • Canonical Sponsored Keynote
    • Marrying OpenStack’s Virtual & Bare Metal Cloud Networks
    • Non-native English speakers in OpenStack communities: A True Story
    • Excitingly simple multi-path OpenStack networking: LAG-less, L2-less, yet fully redundant
  • 0522

    • How did OpenStack improve for Public Cloud in recent releases and what are we still missing
    • Integrating Keystone with large-scale centralized authentication.
    • Evolution of OpenStack Networking at CERN: Nova Network, Neutron and SDN
    • Build Your Serverless Container Cloud with OpenStack and Kubernetes
    • Upgrading OpenStack - war stories
    • nova/neutron + ops cross-project session
    • Ansible Lightning Talk
    • CI/CD Build and Pipelines For OpenStack At Scale
    • Kubernetes network-policies and Neutron security groups - two sides of the same coin?
    • Highly resilient, multi-region Keystone deployments
    • OpenDev CI/CD Happy Hour
  • 0523

    • OpenStack Roadmap: Learn what's coming in the Rocky release
    • Delivering Next Generation Integrated Infrastructure Solutions for Private and Public Cloud Environments
    • Add intelligence to your CI/CD with Spinnaker
    • Nova - Project Onboarding
    • Lesson learned and best practices for large-scale Enterprise Private Cloud
    • OpenStack for AWS Architects - Similarities, differences and bridging the gap
    • Better SSH management for clouds. Introducing Tatu: "SSH as a Service"
  • 0524

    • Cloud Operational Data at your finger tips
    • Istio: How to make multicloud applications Real
    • Monitoring and Logging your kubernetes cluster using OpenStack helm

発表については、最終日の Istioと helmの話が面白いかなーと思いました。k8sが今後標準的につかわれていくんだろうとは思うので、各社の考え方や取り組みがたくさん聞けたのが収穫でした。
最後の日は現地で知り合った人と話す時間が多かった気がします。

同僚の発表

また今回は同僚も発表しました。
OpenStack Summit | Summit Schedule

新しいリージョンを作ったので、その時の弊社の取り組みです。
ネットワーク周りもレイヤーが低いので参加者も多く発表後はたくさんの質問や意見をいただいていたので、興味がある方はリンクから動画も確認できるので、見ていただければ幸いです。

OpenStack Summitで良かったなと思った事

  • フレンドリーなコミュニティ
  • 電源やwifiも机がいたるところにあり、雑談や仕事ができるスペースが十分にあったこと
  • 朝ご飯, 昼ご飯, 夕ご飯, おやつ, 飲み物が用意されてたらホテルと会場の往復でだいたいどうにかなる

感想

初めて参加した感想としては、OpenStackについて知りたければ参加すると良いと思います。
OpenStackのコンポーネントに関わりだして2ヶ月ぐらいですが、
疑問をもった部分や修正したい部分などの質問もできとても良い時間を過ごせたかと思います。
コミュニティは初心者に優しく入りやすい印象を持てました。 サミット会場で食事まで用意されてるので、本当にサミットに集中できる環境が整っていて、余分な心配をしなくても良い環境で助かりました。

openstackのnovaclient使ってvm instanceを立ち上げる

最近まで知らないことだらけだったので、vm instanceを作るときはnovaclient以外にも
neutronclientとかglanceclientとかを使いnetworkやIamageやflavorの情報をとってinstanceを作成してました。
今回は、novaclientでできることはnovaclientでやることにしたので、novaclient以外は使ってません。
なぜnovaclientにまとめたかというと、multi-regionでopenstackの運用を行うとclientを宣言する時にregionの指定が必要になります。
その時に、修正漏れが発生する可能性がありそうだなと思いなるべく必要なさそうなclientは使わないようにしてみました。

# -*- coding: utf-8 -*-

from novaclient import client as nova_client
from keystoneauth1 import session
from keystoneauth1.identity import v3


def get_keystone_session():
    params = {
        'auth_url': KEYSTONE_ENDPOINT,
        'username': USERNAME,
        'password': PASSWORD,
        'user_domain_name': 'Default',
        'project_id': PROJECT_ID,
    }
    auth = v3.Password(**params)
    sess = session.Session(auth=auth)
    return session


def create_instance(my_session):
    nova = nova_client.Client(YOUR_NOVA_VERSION, session=my_session)
    # When your environment uses the multi-region, you need the region_name. 
    # nova = nova_client.Client(YOUR_NOVA_VERSION, session=my_session, region_name='RegionOne')
    net = nova.neutron.find_network(NETWORK_TYPE)
    flavor = nova.flavors.find(name=FLAVOR_NAME)
    image = nova.glance.find_image(OS_NAME)
    instance = nova.servers.create(name=INSTANCE_NAME, flavor=flavor, image=image, nics=[{'net-id': net.id}])
    return instance


if __name__ == '__main__':
    my_session = get_keystone_session()
    result = create_instance(my_session)
    print(result)

コメントアウトしてありますが、もしregion情報必要な場合はregion_nameを指定すると問題なく作成できます。

ちなみに、今までは

neutron = neutron_client.Client(session=YOUR_SESSION)
net = neutron.list_networks(name=NETWORK_TYPE)

のように、必要なcomponentから必要な情報をとってくるために、各clientを使うというやり方をしてたけどその必要がないととてもラクですね。

configでのエラー対応

python3.5を使っていて、import config したらパッケージ内部でエラーが出てきた。 まずは、例外処理がpython2での書き方だったっぽく Exception as e みたいに書き直せば大丈夫

File "/home/develop/.pyenv/versions/3.5.3/lib/python3.5/site-packages/config.py", line 733
except Exception, e:
                ^
SyntaxError: invalid syntax


File "/home/develop/.pyenv/versions/3.5.3/lib/python3.5/site-packages/config.py", line 1308
  except Exception, e:
                  ^
SyntaxError: invalid syntax

こっちも from types import * と修正するとpython3でも問題なく使える。

File "/home/develop/.pyenv/versions/3.5.3/lib/python3.5/site-packages/config.py", line 91, in <module>
    from types import StringType, UnicodeType
werkzeug.utils.ImportStringError: import_string() failed for 'config'. Possible reasons are:

使っていたconfigのバージョンは0.3.9

Python3 compatibility · Issue #3 · pep-dortmund/pars · GitHub

stackoverflow.com

issueだったり、stackoverflowにも上がってるから既知の問題なんだろうな。

MongoDBを使う時に使うODM

mongoDBでデータを管理する

MongoDBとは

スキーマを定義しなくても使用できるドキュメント指向データベースである。 MySQLなどと違い、スキーマを定義しなくても使えるということ。でもそれってwebアプリケーションを作るとき 危なくないのかなーという思いもあり、今まで使ってこなかった。 なおかつ、過去に一回みたことあるコードで本当に定義されてなく、これがwebサービスだったら怖いなーと思ったりもした。 どんなデータが入っているのか未知数。そういったコードに限ってドキュメントが整備されてなかったりもする。

MySQLとの違いは

普段使ってるMySQLとの違いは当然あるので、簡単に MongoDBの情報をMySQLとの比較をまとめる。

  • メリット
    • 高パフォーマンスかつスケービリティを保持してること
    • 比較的簡単に使えること
  • デメリット
    • トランザクションやリレーショナルな機能をサポートしていない
    • データtypeがバラバラになる可能性がある
    • bsonというフォーマット(バイナリ型JSON)
      • 最初扱い方がわからなかった

ざっくりまとめたが、トランザクションについては今後サポートされる見込み。 2018年の夏にリリースされる4.0に組み込まれる予定である。 参照元: https://techcrunch.com/2018/02/15/mongodb-gets-support-for-multi-document-acid-transactions/

意味するものは同じでも言葉が違っているので、それも覚えておいた方が良いかもしれない。

MongoDB MySQL
Database Database
Collection Table
Document Raw
Field Column

実際に使う

検索してみると、MongoEngineというODM(Object Document Mapper)がよく使われてるようだけど、
今回はFlask-MongoAlchemyを使って、なるべく普段使ってるFlask-SQLAlchemyと同じように使えるようにしてみた。
https://pythonhosted.org/Flask-MongoAlchemy/
MongoEngineを使ってもさほど使い方は変わらないし、一般的に使われてるのはMongoEngineっぽい。
MongoEngineは機能が豊富でとても便利そうだということもわかったけど、とりあえずSQLAlchemyをずっと使っていたというだけで、 mongoalchemyを選択した。

mongoengineとmongoalchemyの検索件数の比較: mongoengine, mongoalchemy - Google トレンド

webアプリケーションを作る

その前に整合性を求められるようなシステムならそもそも使うなよって話ではあるけど。
このように型の整合性やvaldationを使いながら使えばデータの不備は回避できるんじゃないかなーと思ったり。
bsonで返ってくるからresponseを返すときは、from bson.json_util import dumps を使って、dumpすると良い感じに 結果を返却できる。

$ curl -X POST  http://127.0.0.1:5000/api/user -d '{"name":"hoge", "age":30, "address": {"prefecture":"hogehoge", "city":"shizuoka"}}' -H 'Content-Type:application/json'
"registered"
$ curl http://127.0.0.1:5000/api/user
{
    "data": [
        {
            "name": "hoge",
            "age": 30,
            "address": {
                "prefecture": "hogehoge",
                "city": "shizuoka"
            },
        }
    ]
}

テストで作ったコード。 https://github.com/hirosetakahito/mongodb-sample
実際作ってみると、Flask-SQLAlchemy つかうのとそんなに変わらない感じで使えるのでとても使いやすい。
また、MongoEngineもMongoAlchemyと使い勝手は変わらないからどちらか気に入ってるほうを使えばいいんじゃないかな。
Flask-MongoEngine — Flask-MongoEngine 0.9.5 documentation

キャッシュのデータを削除しても参照できてしまう。

今回のケースはpythonでsimplecacheを使っていて、キャッシュを消しても消えてないという事象に遭遇したので原因を調べてまとめてみました。

from werkzeug.contrib.cache import SimpleCache
import threading
import time
import datetime


class TestThread(threading.Thread):
    def __init__(self, number, sleep_time):
        super(TestThread, self).__init__()
        self.number = number
        self.sleep_time = sleep_time

    def run(self):
        for i in range(self.number):
            print('{}: thread-0: {}'.format(i, cache.get('key1')))
            if i == 1:
                cache.delete('key1')
                print('cache delete')
                cache.set('key1', 'test-data2')
            time.sleep(self.sleep_time)

def sub_thread(number, sleep_time, no):
    for i in range(number):
        print('{}: thread-{}: {}'.format(i, no, cache.get('key1')))
        time.sleep(sleep_time)

if __name__ == '__main__':
    cache = SimpleCache(10)
    cache.set('key1', 'test-data1')
    thread_main = TestThread(3, 2)
    thread_main.start()
    for i in range(1, 5):
        thread_sub = threading.Thread(target=sub_thread, name="th", args=(3, 2, i))
        thread_sub.start()

これを何回か実行してたらたしかに残ってた

$ python sample.py
0: thread-0: test-data1
0: thread-1: test-data1
0: thread-2: test-data1
0: thread-3: test-data1
0: thread-4: test-data1
1: thread-1: test-data1
1: thread-0: test-data1
cache delete
1: thread-3: test-data1
1: thread-2: test-data1
1: thread-4: test-data1
2: thread-1: test-data2
2: thread-0: test-data2
2: thread-3: test-data2
2: thread-2: test-data2
2: thread-4: test-data2

こういった場合は、素直にredisとか使うのが早そうだなと思い、redisに変更

import redis
import threading
import time
import datetime


class TestThread(threading.Thread):
    def __init__(self, number, sleep_time):
        super(TestThread, self).__init__()
        self.number = number
        self.sleep_time = sleep_time

    def run(self):
        for i in range(self.number):
            print('{}: thread-0: {}'.format(i, cache.get('key1')))
            if i == 1:
                cache.delete('key1')
                print('cache delete')
                cache.set('key1', 'test-data2')
            time.sleep(self.sleep_time)

def sub_thread(number, sleep_time, no):
    for i in range(number):
        print('{}: thread-{}: {}'.format(i, no, cache.get('key1')))
        time.sleep(sleep_time)

if __name__ == '__main__':
    cache = redis.StrictRedis(host='localhost', port=6379, db=0)
    cache.set('key1', 'test-data1')
    thread_main = TestThread(3, 2)
    thread_main.start()
    for i in range(1, 5):
        thread_sub = threading.Thread(target=sub_thread, name="th", args=(3, 2, i))
        thread_sub.start()

このように変更することで、deleteされてからの挙動は、

$ python sample-redis.py
0: thread-0: b'test-data1'
0: thread-1: b'test-data1'
0: thread-2: b'test-data1'
0: thread-3: b'test-data1'
0: thread-4: b'test-data1'
1: thread-3: b'test-data1'
1: thread-0: b'test-data1'
1: thread-1: b'test-data1'
cache delete
1: thread-2: None
1: thread-4: None
2: thread-2: b'test-data2'
2: thread-3: b'test-data2'
2: thread-1: b'test-data2'
2: thread-0: b'test-data2'
2: thread-4: b'test-data2'

のように、登録される前かと思いますが、消されたデータを参照するようなことはなくすことはできました。 原因はなんだよ?って思って調べてたら、しっかりドキュメントにも、以下のように書かれてました。

class werkzeug.contrib.cache.SimpleCache(threshold=500, default_timeout=300) Simple memory cache for single process environments. This class exists mainly for the development server and is not 100% thread safe. It tries to use as many atomic operations as possible and no locks for simplicity but it could happen under heavy load that keys are added multiple times.

Parameters:
threshold – the maximum number of items the cache stores before it starts deleting some. default_timeout – the default timeout that is used if no timeout is specified on set(). A timeout of 0 indicates that the cache never expires.

引用元: http://werkzeug.pocoo.org/docs/0.14/contrib/cache/

thread saveじゃないなら仕方ないね... しっかりドキュメント読みましょうということですね。 開発環境以外で一時的に保持したいデータがあるなら、simplecacheじゃなくて別のものを使うようにしよう。