パーセプトロンの実装
概要
先日、「タコス DE 機械学習」と名打った、機械学習初学者向けの勉強会で発表する機会がありました。 勉強会の主旨は、機械学習の基本的な分類器であるパーセプトロンを好きな言語で実装しながら、タコスを食べようという、楽しげな勉強会でございました。 かくゆう私も、機械学習を学習し始めたばかり。発表内容は、Pythonでの開発環境の説明とアイリスデータを用いたパーセプトロンの実装例の紹介です。本記事では、その発表をまとめます。 connpass.com
パーセプトロンとは
勉強会でも発表された、こちらの資料が分かりやすいので是非お読みください。
www.slideshare.netPythonでの実装について
勉強会では、Pythonに限らず、Scala、Java、Ruby等さまざまな言語で参加者はパーセプトロンを実装しました。 私はPythonでの実装に関して発表しました。 方々で語られていますが、以下の点で機械学習の初学者にとってPythonはオススメです。
- ライブラリが豊富
- リファレンスが豊富
- Jupyter Notebookがイケてる
この辺のこと発表した資料です。
で、こちら実装例。
https://github.com/yamatsuka-hiroto/ml/blob/master/perceptron_iris.ipynb
簡易のため、アイリスデータのsetosaとversicolorの額の長さ、幅をもとに分類してます。
まとめ
今回は、Pythonでの開発環境、パーセプトロンの実装を発表しました。
余談
普段、Golangを使っているので、Golangで同様のことをするにはどうするかを検討しました。
gonum/matrix
PythonにはNumPyなど、便利な行列演算ライブラリがあります。本当便利。 Golangにはないかなあと思っていたのですが、相当するものがありました。 基本的な行列演算はできそう。
GitHub - gonum/matrix: Matrix packages for the Go language.
gonum/plot
Pythonのmatplotlibに相当するのものです。
GitHub - gonum/plot: A repository for plotting and visualizing data Example plots · gonum/plot Wiki · GitHub
Jupyter Notebook
Jupyter Notebook自体は40以上の言語で使え、もちろんGolangの使用できます。 github.com
インストールして使ってみたのですが、 グラフ表示がNotebook内できなかったりで、ちょっと微妙でしたね。。
Vaultの本番導入を見据えて(2)
概要
前回の記事では、ローカル環境でvaultのbackgroundをZookeeperに設定し、基本的な操作を行いました。
なんだか、便利そうだけどよく分からないVault。
今回は、実運用を想定して、Auth Backendを App ID
にして、tokenの動的な発行を試しました。
Auth Backendって何?
Auth Backendとは、ユーザーの認証やアクセス制限をを行うVaultのコンポーネントです。 (https://www.vaultproject.io/docs/auth/index.html) 現状は以下のAuth Backendを使用して認証を行うことができます。 GitHub・AppID・LDAP・MFA・TLS Certificates・Tokens・Username&Password
この中でも、今回、App ID
注目しました。
その理由としては、App ID
のbackendは 、個々のサーバー/コンテナに動的に
Vaultとの認証を行うことを想定した機構であり、クラウド環境と相性が良さそうだからです。
準備
前回同様、ZookeeperをBackendにして、Vaultを起動し、unsealの状態にしておきます。
* 完全に余談ですが、前回に馬鹿なことをやってしまっていたことに気付きました。 前回、発行したrootのtokenを書き止めておくのを忘れいました。 そのため、seal状態のVaultさんはうんともすんとも言ってくれず、 結果、前回作成したZookeeperのvaultデータを全て消さなければならなくなりました。。 皆さんも、まあないでしょうけど、気をつけて下さいね。
auth のAPIからapp_id
と user_id
を登録します。
① app_id
の登録
app_id: hogehoge
$ curl \ -X POST \ -H "X-Vault-Token:$VAULT_TOKEN" \ -d '{"value":"root", "display_name":"demo"}' \ http://localhost:8200/v1/auth/app-id/map/app-id/hogehoge
② user_id
の登録
user_id: fugafuga
$ curl \ -X POST \ -H "X-Vault-Token:$VAULT_TOKEN" \ -d '{"value":"hogehoge"}' \ http://localhost:8200/v1/auth/app-id/map/user-id/fugafuga
③ app_id
と user_id
から token を発行
$ curl \ -X POST \ -d '{"app_id":"hogehoge", "user_id": "fugafuga"}' \ "http://127.0.0.1:8200/v1/auth/app-id/login"
レスポンス
{ "lease_id": "", "renewable": false, "lease_duration": 0, "data": null, "warnings": null, "auth": { "client_token": "10921cd8-001c-25cd-2e5c-2a9c2fa43e16", "policies": [ "root" ], "metadata": { "app-id": "sha1:3b2c6c10d0e78072d14e02cc4c587814d0f10f3a", "user-id": "sha1:39975bb0ba31825c4fdd3de775dd468081b3522b" }, "lease_duration": 0, "renewable": true } }
client-token
としてtokenが発行されました。
簡単ですね。
いつtokenを発行するか
VaultのサイトAuth Backend: App ID - Vault by HashiCorp に想定される使い方があったので、手順まとめてみます。
- appIDを登録し、権限を与える。(上記①。例ではroot権限)
- configuration management service にappIDを保存する。
- cloud-init system がマシン毎の
user_id
を登録する。 (上記②) - マシンを起動。マシンはconfiguration management serviceから
appID
を取得。 割り振られたuser_id
でvaultと認証を行う。(上記③)
ん。。。なんか分かりそうでわからない。 クラウド環境でいろいろ試してみます。
今回のまとめ
前回、今回とローカルで試しましたが、Vault自体がクラウド環境を想定して設計されているぽいので、 クラウド環境で実環境に即した使い方を模索します。
Vaultの本番導入を見据えて
概要
タイトルは今にもVaultを本番導入する勢いのようですが、触ってみた系の記事です。
最近、遅ればせながらDevOps界の雄 Hashicorpさんが開発しているVaultが気になっていました。
今回は、業務で実際導入するにあたって、試しにローカル環境でZookeeperをbackendに設定し、基本的な操作を行いました。
動機
例えば、ソースの中にDBのuser/passwordやAPIキーなどをベタ書きしちゃってることありません?
しかも、それぞれのプロジェクト毎にいちいち、その情報書いたりしてません?
ぼくは、めちゃくちゃあります。むしろ、それしかない。
心苦しくはあったのですが、良さそうな手もなかったので目をつむっていました。
Hashicorpが開発するVaultを導入することで、そういった情報を管理しちゃおうっていうのが動機です。
Vault とは
機密情報を安全に管理するツールです。
2015年4月末にリリースされまして、最近 、0.5 がリリースされました。
基本的な機能としては、以下です。(参考:Introduction - Vault by HashiCorp)
・ Secure Secret Storage(安全に機密情報を保持)
・ Dynamic Secrets(動的に機密情報を管理)
・ Leasing and Renewal(機密情報の貸与と更新)
・ Data Encryption(データの暗号化)
・ Revocation(無効化)
入門
以下のチュートリアルでVaultの基本操作が大体わかりました。
Vaultのバージョンが上がって、若干異なるところもありますが、
日本語訳とても助かりました。
pocketstudio.jp
backendをZookeeperに
さて、入門が終わったところでとりあえず、backendをZookeeperに設定してみます。
なぜ、Zookeeperかと言いますと、業務でVaultを導入するにあたり、
既存のシステムで実績のあるZookeeperで使い始めようかなという構想。
Hashicorp的にはやはり、自社製品のConsulをbackendに使用するがmost recomendedだそうです。(Server Configuration - Vault by HashiCorp)
サンプルとしては、Consulの場合しか載っていなかったのでZookeeperでは以下のようにbackendを設定しました。
Jsonとも互換性のあるHCL(HashiCorp Configuration Language)で記述します。
example.hcl
# backendを"zookeeper"を指定 backend "zookeeper" { # localのZookeeperのアドレス address = "192.168.12.11:2181" # ZookeeperのNodeを指定 path = "vault" } # Vaultサーバー listener "tcp" { address = "127.0.0.1:8200" tls_disable = 1 }
さて、これで以下のコマンドを実行。
$ vault server -config=example.hcl
が、しかし、下記のようなえらーが発生。
Error initializing core: missing advertisement address
advertisementのaddressがないとのこと。
`VAULT_ADVERTISE_ADDR`の設定が必要でした。
$ export VAULT_ADVERTISE_ADDR='http://192.168.12.11:2181'
もう一度、以下を実行するとVaultサーバーが起動しました。
$ vault server -config=example.hcl
次は、実際にVaultで設定した値がZookeeperに書き込もれるかの確認。
チュートリアルでの同様に`vault init`を実行し、アンシールも実行。
vault authで初期化時のtokenを入力すればrootユーザとして操作できます。
試しに以下を実行します。
$ vault write secret/hello value=world
Zookeeperを確認すると、/vault/logical/{暗号化されたパス}/_hello のChildrenに
暗号化されたデータを確認することができました。
もちろん、以下で暗号化した値を取得できます。
$ vault read secret/hello Key Value lease_duration 2592000 value
所感
backendをZookeeperに設定し、ローカル環境でVaultの基本操作を行いました。
あとは、本番導入するにあたって、各サーバーとVaultの認証をどうしようとかも考えなきゃですね。
触ってみて感じたのですが、Vaultはかなり柔軟なので 、これからどんどん導入の場面が増えるのかなと思っています。
gRPC x Go言語 x Unixドメインソケット
概要
最近の業務でのこと。
Go言語で書かれたサービスが色々なロジックを持つようなってきたので、俗にいうマイクロサービスへの分割を行いました。
分割したサービス同士の通信には、速い速いと噂のgRPC(http://www.grpc.io/)を適用しました。分割後のサービスは同じサーバー内で稼働させることに。
それなら、通信はデフォルトのTCPでなく、Unixドメインソケットでプロセス間通信をしたほうが速いんじゃね?ということで、今回はGo言語でgRPC使う際に、Unixドメインソケットでプロセス間通信する実装方法、また、TCPとのパフォーマンス比較などを書きます。
実装方法
Unixドメインソケットを使用は、コネクションの取得時に指定します。
何も指定しない場合は、以下のような感じ。
// デフォルトでTCP. conn, err := grpc.Dial("127.0.0.1:9000", grpc.WithInsecure())
grcp.Dialは、DialOptionを指定してコネクションを設定することができます(grpc - GoDoc)。
例えば、WithTimeoutでタイムアウトを、WithCodecでメッセージのコーデックを設定することができます。Unixドメインソケットを使用する場合は以下のように、WithDialerを用いてnet.dialerを指定します。
// dialer を作成して grpc.WithDialer(dialer)でDialオプションを追加する. dialer := func(a string, t time.Duration) (net.Conn, error) { return net.Dial("unix", a) } conn, err := grpc.Dial("/tmp/test.sock", grpc.WithInsecure(), grpc.WithDialer(dialer))
パフォーマンス
ローカル環境(MBP15, 2015)で簡単なサーバとクライアントを実装して、10,000回リクエストした場合のレスポンスタイムを比較しました。ソースは末尾。
比較のために、1コネクション、1クライアントで固定。
結果から言うと、案の定、Unixドメインソケットの方が速かったです。TCP: 115 μs / req に対し、Unixドメインソケット: 90 μs /req。
所感
現在、gRPCを用いてサービスを分割したものが、本番環境で動いています。リリース前には負荷試験などを行い、分割前と後で性能を比較しました。
結果は、サービスを分割することにより、ほとんど性能の劣化はなかったです。
gRPCを調べていると、etcdでも使われている様子。関連の記事も面白かったので、etcdのソースを読んでみようと思います。
検証で使ったソース
protoファイル
syntax = "proto3"; package hello; service Hello { rpc SayHello (HelloRequest) returns (HelloReply){} } message HelloRequest{ string name = 1; } message HelloReply { string message = 1; }
server側
var network = "unix" var address = "/tmp/grpc_test.sock" type server struct{} // リクエスト(Name)を受け取り、レスポンス(Message)を返す func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: fmt.Sprintf("Hello, %s.", in.Name)}, nil } func main() { lis, err := net.Listen(network, address) if err != nil { panic(err) } defer func(){ if network == "unix" { _, err := os.Stat(address) if err == nil { if err := os.Remove(address); err != nil { panic(err) } } } }() s := grpc.NewServer() pb.RegisterHelloServer(s, &server{}) s.Serve(lis) }
client側
var network = "unix" var address = "/tmp/grpc_test.sock" var requestNum = 100000 func main() { // コネクション数は1 conn, err := genConn(network, address) if err != nil { panic(err) } defer conn.Close() // クライアント数も1 client := pb.NewHelloClient(conn) // 名前の作成(name_i) names := make([]string, requestNum) for i := 0; i < requestNum; i++ { names[i] = fmt.Sprintf("name_%d", i) } st := time.Now() for i := 0; i < requestNum; i++ { _, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: names[i]}) if err != nil { panic(err) } } tt := time.Since(st) pt := tt / time.Duration(requestNum) fmt.Println(pt) } // コネクションを生成 func genConn(network, address string) (*grpc.ClientConn, error) { switch network { case "tcp": // Dial を指定しない場合はでデフォルトでTCP. return grpc.Dial(address, grpc.WithInsecure()) case "unix": // dialer を作成して grpc.WithDialer(dialer)でDialオプションを追加する. dialer := func(a string, t time.Duration) (net.Conn, error) { return net.Dial(network, a) } return grpc.Dial(address, grpc.WithInsecure(), grpc.WithDialer(dialer)) default: panic("invalid network") } }
メディアサーバーKurento+Node.jsを動かしてみる
概要
オープンソースのWebRTCメディアサーバー・KurentoをGCE上に構築し、チュートリアルを試してみました。 構築手順等を書いていきます。
動機
昨年末、社内のキャンペーンで使用する 生放送配信システムを自前で作ろうという話になりました。 当初、WebRTCを用いた1対多数の配信システムを手元のMBPで完結させて配信していた。 しかし、接続数が20を超えてくると配信しているMBPにかなりの負荷がかかった。 配信に駆り出された使用されていない老MBPはお死にになられました。 ということで、メディアサーバーを立てて、MCUを実現したいなと思ったのでとりあえず、勉強がてらKurentoを触ってみました。
Kurentoとは
KurentoはオープンソースのWebRTCメディアサーバーです。多人数でのテレビ会議や、生放送の配信などに使え、録画の機能もあるようです。JavaやJavaScriptをクライアントとして使用できます。内部はCで書かれているようです。 今回は、Node.jsでチュートリアルを動かしてみました。
KurentoとWebRTCに関して、こちらの資料が勉強になりました。
www.slideshare.net環境
GCE環境
マシンタイプ:n1-standard-1(vCPU x 1、メモリ 3.75 GB)
→ メディアサーバーと使用するにはスペック的にかなりショボいかと。。。
OS: Ubuntu 14.04 LTS (64 bits)
→ Kurento Media Serverを動かす必須環境のようです。
Kurento Media Server のインストール
echo "deb http://ubuntu.kurento.org trusty kms6" | sudo tee /etc/apt/sources.list.d/kurento.list wget -O - http://ubuntu.kurento.org/kurento.gpg.key | sudo apt-key add - sudo apt-get update sudo apt-get install kurento-media-server-6.0
kurento-media-serverが起動しています。
STUNサーバーの設定
STUNとは、異なるNAT環境下どうしでもWebRTCを実現する仕組みです。STUNサーバーはNATの外側から見える端末の情報(グローバルIPやUDPのポート)を教えてくれます。取得した自らの情報をシグナリングサーバーを用いて、端末間で交換します。今回、シグナリングサーバーは、後ほど出てくるNode.jsのサーバに含まれています。
STUNサーバに関しては、チュートリアルのページに利用可能なサーバーのIPとポートが掲載されていたので
、(http://www.kurento.org/docs/6.1.0/installation_guide.html)そちらを使用させていただきました。
下記ファイルを修正。
/etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
stunServerAddress=54.172.47.69 stunServerPort=3478
TURNサーバーに関しては、設定していません。
Tutorial: Hello Worldを動かす
チュートリアルそのまま http://www.kurento.org/docs/6.1.0/tutorials/node/tutorial-1-helloworld.html
Node.jsとBowerのインストール
curl -sL https://deb.nodesource.com/setup | sudo bash - sudo apt-get install -y nodejs sudo npm install -g bower
チュートリアルのソースを落としてくる
git clone https://github.com/Kurento/kurento-tutorial-node.git cd kurento-tutorial-node/kurento-hello-world npm install npm start
この際、server.js
では下記のようになっているため、
GCEのインスタンスのポートを開けておく必要があります。
今回は、そのまま8443と8888を解放しました。
var argv = minimist(process.argv.slice(2), { default: { as_uri: 'https://localhost:8443/', // アプリケーション用 ws_uri: 'ws://localhost:8888/kurento' // シグナリング用 } });
https://<インスタンスのIP>:8443/
上記にアクセスし、start
をクリックすると、ウェブカメラからの映像とKurento Media Serverと通して配信された映像が映し出されるかと思います。start
を押しても映像が映らない場合は、一度stop
してもう一度試すと、映し出されました。
感想
その他のチュートリアルも触ってみたのですが、1対1、1対多数、多数対多数など、拡張性が容易そうです。 また、画像処理を含むチュートリアルを動かしてみると、配信の時間のズレがかなりあったので、サーバーのスペックがある程度必要そうです。