y’s blog

勉強したことのメモなどを書きます

最近読んだ本

SOFT SKILLS ソフトウェア開発者の人生マニュアル

下記のようなテーマに関する書籍

  • キャリアの構築
  • 効率の良い学習方法
  • 生産性の向上
  • 資産管理
  • 健康管理
  • 精神状態の管理

キャリアの構築方法については結構なコミュ力、行動力を要求しており難易度が高いと感じた。コミュ障エンジニアが全部実践するのは難がある。
効率の良い学習方法、生産性を上げる方法に関する章ではかなり具体的な方法論が述べられていて、これら章だけでもこの本を読んだ価値があった。
あと文中でたくさんの書籍が紹介されているので次に読む本の指針になる。暇な時間があって本をたくさん読みたい人が最初に手を出すのに良さそう。

前処理大全

期待外れというのは言い過ぎだけど、正直言って自分にはあまり合わなかった。
エンジニアであれば知っていて当然のような知識が多いため、エンジニアである自分が読んでも得られるものは少なかった。(個人の経験に依存した話)
どちらかというと統計や分析を専門にしている人がデータ処理に手を出す際に読むと良い感じなんじゃないかと思う。

Docker/Kubernetes入門

チーム内でKubetnetesの勉強をしてみるという機運があったので読んだ。(実際に運用するかは未定) How toが内容の中心で入門者にはとても良い本だと思った。
docker/Kubernetesという技術の成り立ちに関して最低限説明さてているのも良い。
Kubernetesはモデルが複雑で用語がとても多いのだけど、それらを日本語で説明してくれているのがありがたかった。
入門者向けなのでこの本を読んだだけでKubernetesクラスタを本番運用できるかというとそれは厳しい。(というかKubernetesの知識が豊富でも厳しいっぽい)

Kubernetesのチュートリアルをやってみる

Kubernetesチュートリアルをやった時のメモです。
チュートリアルページをみると色々な選択肢がありますが、今回は「Kubernetesの基本を学ぶ」というチュートリアルをやりました。
Kubernetesの基本を学ぶ - Kubernetes

使用OSはMacです

HyperVisorをインストールする

VirtualBoxなど、Kubernetesを動作させるためのVMツールをインストールします。
VirtualBoxはhomebrew-caskでインストールできます。(VirtualBoxカーネル拡張機能を含むためシステム環境設定から許可が必要になります)

$ brew cask install virtualbix

もしDockerの入ったLinux環境を使用しているのであれば、下記のオプションを指定VMではなくホスト上で直接Minikubeを動作させることができるそうです。(Minikubeがローカル環境用であることを考えると使用機会はあまりなさそうですが)

--vm-driver=none

Minikubeをインストールする

Minikubeはローカルでの開発に最適化された軽量なKubernetes環境を提供するツールです。
ローカルマシン上にVMを作成し(ここでHyperVisorが必要)、1つのNodeのみを含むKubernetesクラスタをデプロイできます。
macの場合、homebrew-caskでインストールします。

$ brew cask install minikube

起動する

下記のコマンドで(VMが未作成の場合は)VMが作成され、VM上でminikubeが起動します。
この段階ではNode上ではまだ何も起動していません。

$ minikube start

Nodoにpodを配置します。
チュートリアルの例ではgoogle private resistry(gcr)からイメージを取得しています

$ kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.10 --port=8080
deployment.apps "hello-minikube" created

先ほどと同様のコマンドでpodが起動していることが確認できます

$ kubectl get pod
NAME                              READY     STATUS    RESTARTS   AGE
hello-minikube-56cdb79778-zzfl2   1/1       Running   0          1m

クラスタの状態確認

$ kubectl cluster-info
Kubernetes master is running at https://192.168.99.100:8443
KubeDNS is running at https://192.168.99.100:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

この段階ではpod内のコンテナと通信することはできません。
exposeコマンドで外側と通信できるようにします。

$ kubectl expose deployment hello-minikube --type=NodePort
service "hello-minikube" exposed

これでコンテナと通信できるようになりました。

$ curl $(minikube service hello-minikube --url)


Hostname: hello-minikube-56cdb79778-zzfl2

Pod Information:
    -no pod information available-

Server values:
    server_version=nginx: 1.13.3 - lua: 10008

Request Information:
    client_address=172.17.0.1
    method=GET
    real path=/
    query=
    request_version=1.1
    request_scheme=http
    request_uri=http://192.168.99.100:8080/

Request Headers:
    accept=*/*
    host=192.168.99.100:32252
    user-agent=curl/7.54.0

Request Body:
    -no body in request-

コンテナを終了するにはdeleteコマンドを使用します

$ kubectl delete services hello-minikube
service "hello-minikube" deleted

$ kubectl delete deployment hello-minikube
deployment.extensions "hello-minikube" deleted

所感

minikubeを使ったチュートリアルを試しましたが、ローカルに最適化されているだけあってとっつきやすかったです。
その反面チュートリアルだけでは実際の運用との間にかなりの乖離があるとも感じました。
下記のようなことを引き続き試していきたいと思います。 - 自前で用意したDockerイメージを利用するにはどうすれば良いか - Pod上で複数のコンテナを動かす方法 - Node上で複数のPodを動かす方法

elasticsearchがスケールする仕組み(参照編)

elastcisearchのスケール性能について調査したことをまとめました。

シャーディングとレプリケーション

elasticsearchはスケールアウトの仕組みにシャーディングとレプリケーションを採用しています。
シャーディングはデータを分割して保存することで参照を分散する仕組みです。
レプリケーションはデータ集合の複製を作成することで、同じデータにアクセスするリクエストを並列に実行したり可用性を高めたりできる仕組みです。
elasticsearchはインデックスをシャードに分割しつつ、それぞれのシャードをレプリケーションしています。

query phaseとfetch phase

elasticsearchの検索のプロセスはquery phaseとfetch phaseに別れています

query phase

elasticsearchクラスタは複数のノードで構成されますが、インデックスや検索などのリクエストはどのノードも受けることができます。
リクエストを受け取ったノードはcoodinating nodeと呼ばれます。

検索リクエストを受けると、coodinating node が各シャードのプライマリ or レプリカに検索リクエストを行います。
各シャードは size + from件のドキュメントを検索ソートし、リクエストを受け取ったノードに返します。(この段階ではドキュメントのIDとソート対象になるvalueをのみを返します。)
coodinating nodeは各シャードから返ってきた結果をマージ、ソートします。

fetch phase

query phaseで入手した情報を利用して、リクエストを行う必要があるシャードのみに対してmulti getリクエストを行います。
各シャードはドキュメントの内容を読み込んでcoodinating node返します。
coodinating node が各ノードから得たドキュメント情報をマージ し、最終的な結果をクライアントに返します。

シャード数とレプリカ数について

ノード数をいくら増やしてもシャード数やレプリカ数を増やさなければ処理は分散されません。
elasticsearchの性能を引き出すにはこれらの値を適切に設定する必要があります。
プライマリ/レプリカシャードは自動的に各ノードにいい感じに分散するように配置されます。

インデックスを複数のシャードに分割しているほど、1リクエストの処理が分散され、並列に行われます。
また、シャードのレプリカ数が多ければ、複数のリクエストの並列度が上がります。 ただしレプリカ数は多ければ多いほど良いわけではなく、レプリカ数 > ノード数になるとひとつのノードにシャードが重複するため意味はなくなってきます。

ボトルネックになりそうな箇所

単純に検索処理の負荷が大きい場合(複雑なaggregationなど)以外の要因を書きます。

ヒットする件数が多すぎる

query phaseで各シャードから取得した結果をソートする処理はcoodinating node1台で行われるためスケールアウトできません。
そのため検索結果の件数が多いことがボトルネックになってしまう可能性があります。

deep paging

query phaseにおいて各シャードでsize + from件の検索・ソートを行うため、fromが大きくなると処理の負担が大きくなり、ボトルネックになる可能性が出てきます。
このような検索はdeep pagingと呼ばれます。

対策

インデックスを分割する

インデックス内のドキュメントが増えるほど検索時の負荷も際限なく増えることになるため、インデックスを日付毎にするなど時系列で分割するといった方法を取ることが多いです。
index templateでマッピングをあらかじめ登録しておくと、時系列で分割されたインデックスの生成がやりやすくなります。

scroll searchやsearch afterを使う

elaticsearchはscroll searchやsearch afterなど、deep paging対策の仕組みを提供しています。
search afterはほぼfromと同様のことができるので、使えないか検討するべきだと思います。

参考にしたドキュメント

古いですが基本的な処理の流れは今も変わってないと思います。

Query Phase | Elasticsearch: The Definitive Guide [2.x] | Elastic

Fetch Phase | Elasticsearch: The Definitive Guide [2.x] | Elastic

macのコマンドラインからアプリケーションを開く

macでターミナルからアプリケーションを開く方法を最近知りました。

$ open -a 'Google Chrome'

#開くurlを指定することもできる
$ open -a 'Google Chrome' https://google.com

普段ターミナル上で作業することが多いので、結構重宝してます。
このままではコマンドが長いしアプリケーション名を覚えるのも大変ですが、エイリアスを設定すると使いやすくなります

#使ってるシェルに応じたファイルを編集して下記を追加
$ vim .bashrc

alias chrome='open -a "Google Chrome"'

$ source ~/.bashrc

# ブラウザが開く!
$ chrome

ブラウザの例を書いてますが、ファイルをエディタで開いたりするときなんかも便利です。
一応ターミナルからアプリケーションを終了するコマンドも探してみたのですが、linuxでも使えるkillkillallくらいしか見つかりませんでした。

pythonの環境を整えてjupyter notebookを動かすまで

自分は機械学習に入門したばかりの初心者なんですが、pythonの環境を整えるのにちょっと苦労したので書こうと思います。

pyenvのインストール

pyenvはpythonのバージョン切り替えが簡単に行えるようになるツールです。

macの人はbrewで入れることができます。

$ brew update | brew install pyenv

入れたら例によってbash_profileを編集します。
一応簡単に説明すると、シェルがコマンドを探す領域にpyenvのディレクトリを追加するということをやっています。 パスとか環境変数がわかんないという人はこちらの記事が参考になると思います。

qiita.com

これでpyenvを使用できるようになりましたが、まだpython自体のインストールは行なっていません。 pythonのバージョンを指定してインストールを実行します。

$ pyenv install version 3.6.0

インストール可能なバージョンの一覧は下記で確認できます。

$ pyenv install --list

インストールができたら、pythonのバージョンを切り替えます。

$ pyenv global 3.6.0

現在のディレクトリだけ使用するpythonのバージョンを変えたい場合はlocalを指定します。

$ pyenv local 3.6.0

これでインストールしたバージョンのpythonが使えるようになります。

$ python --version

jupyter notebookをインストールする

pipを利用してインストールできます。 pipがpyenvでpythonをインストールするとついてきます。

$ pip install jupyter

jupyter notebookを動かす

コマンドを実行すると、ブラウザが立ち上がりjupyter notebookのページが表示されます。 コマンドを実行したディレクトリがルートディレクトリになります。

$ jupyter notebook

まとめ

pyenvをローカルにインストールする方法について紹介しました。 rubyやnodeなんかにも似たようなバージョン管理のツールがあるので、それらを使ったことがある人ならすんなりインストールできると思います。

virtualenvといったのpythonのライブラリのバージョンを環境ごとに管理するツールもあるのですが、こちらはpythonでプロダクト開発するとき向けでただ機械学習をやるだけなら必要では良さそう。

最近だったらDockerという選択肢もあるので、そちらについてもいずれ試してみたいです。