Helm (Kubernetes) vol.3
前回: Helm(Kubernetes) vol.2 - 成らぬは人の為さぬなりけり Helmのライフサイクルや思想を理解したので、今度は実際にChartの中身を見ていく
Chart
chartの構成
wordpress/ Chart.yaml # A YAML file containing information about the chart LICENSE # OPTIONAL: A plain text file containing the license for the chart README.md # OPTIONAL: A human-readable README file requirements.yaml # OPTIONAL: A YAML file listing dependencies for the chart values.yaml # The default configuration values for this chart charts/ # OPTIONAL: A directory containing any charts upon which this chart depends. templates/ # OPTIONAL: A directory of templates that, when combined with values, # will generate valid Kubernetes manifest files. templates/NOTES.txt # OPTIONAL: A plain text file containing short usage notes
こんな感じ
主要な所を説明
Chart.yaml
まぁ、メタデータだな。 名前とか、versionとか、authorとかそーいうの。
values.yaml
こいつが、設定だ。
Ansibleでいう、defaults/main.yml
この設定を、外から上書きできる感じになる。
templates
この中に、実際に使う、yamlを吐くテンプレートを書く。
deploymentとかserviceとかingressとか。。。
そのyamlを吐くtemplateにvaluesの変数を埋め込む感じ
ぶっちゃけ、yamlじゃなくても普通にERB的なテンプレートなので、なんでもできそう。
(ただ、k8sで使うので、YAML以外使わないけど…)
実際に見てみよう
Helm Create !!!!
新しいChartを作る
helm create test
すると、あら不思議、中にはサンプルが! どうやら、nginxをdeployするサンプルくさい
Chart.yaml
apiVersion: v1 description: A Helm chart for Kubernetes name: test version: 0.1.0
スィンプル
values.yaml
# Default values for test. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx tag: stable pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 ingress: enabled: false # Used to create an Ingress record. hosts: - chart-example.local annotations: # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" tls: # Secrets must be manually created in the namespace. # - secretName: chart-example-tls # hosts: # - chart-example.local resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi
うんうん、やりたいことはよくわかる
templates/deployment.yaml
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: {{ template "fullname" . }} labels: app: {{ template "name" . }} chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} spec: replicas: {{ .Values.replicaCount }} template: metadata: labels: app: {{ template "name" . }} release: {{ .Release.Name }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: {{ .Values.service.internalPort }} livenessProbe: httpGet: path: / port: {{ .Values.service.internalPort }} readinessProbe: httpGet: path: / port: {{ .Values.service.internalPort }} resources: {{ toYaml .Values.resources | indent 12 }} {{- if .Values.nodeSelector }} nodeSelector: {{ toYaml .Values.nodeSelector | indent 8 }} {{- end }}
うん、k8sのDeploymentだ どうやら、Goの標準ライブラリに組み込まれてるtemplateの記法らしい https://golang.org/pkg/text/template/
templates/service.yaml
apiVersion: v1 kind: Service metadata: name: {{ template "fullname" . }} labels: app: {{ template "name" . }} chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.externalPort }} targetPort: {{ .Values.service.internalPort }} protocol: TCP name: {{ .Values.service.name }} selector: app: {{ template "name" . }} release: {{ .Release.Name }}
templates/ingress.yaml
{{- if .Values.ingress.enabled -}} {{- $serviceName := include "fullname" . -}} {{- $servicePort := .Values.service.externalPort -}} apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ template "fullname" . }} labels: app: {{ template "name" . }} chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} annotations: {{- range $key, $value := .Values.ingress.annotations }} {{ $key }}: {{ $value | quote }} {{- end }} spec: rules: {{- range $host := .Values.ingress.hosts }} - host: {{ $host }} http: paths: - path: / backend: serviceName: {{ $serviceName }} servicePort: {{ $servicePort }} {{- end -}} {{- if .Values.ingress.tls }} tls: {{ toYaml .Values.ingress.tls | indent 4 }} {{- end -}} {{- end -}}
あー、ingress作るかとか、template側で制御してんのかぁ んー、この思想イケてないな 設定ファイルみたいなのつくって、どのtemplateを使うかとか、ansible的な思想の方がイケてる configurationとtemplateは分けたほうが設計的に綺麗
templates/_helper.tpl
{{/* vim: set filetype=mustache: */}} {{/* Expand the name of the chart. */}} {{- define "name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). */}} {{- define "fullname" -}} {{- $name := default .Chart.Name .Values.nameOverride -}} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}}
どうやら、railsでいうhelper的な物をtemplate内で定義してるくさい(service.yamlとかで template “fullname” .
とかしてる)
これもまた、イケてないなー
まず新しいDSLが登場するのがいけてない
せめて、YAMLかGoで書けるようになっていてほしい
次回
次回は、↑このchartをいじり倒してみよう valuesは外から上書きした時にどうなるか、、、 特にdeep mergeされるのかが、気になる (ansibleはここがイケてない)
Helm(Kubernetes) vol.2
前回
TODO
- [ ] Helm上の概念の理解
- [ ] 独自Chartsを書いてみる
- [ ] PrivateRepositoryは使えるのか?
- 独自repositoryを作る
Helm上の概念の理解
~ ❯ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE errant-hare 1 Sat Mar 3 22:39:16 2018 DEPLOYED kubernetes-dashboard-0.4.1 default
まずこれだけ見ても
- Name
- revision
- chart
という用語が出て来る
それぞれ理解していくか
用語
用語 | 説明 |
---|---|
Package | k8s上にインストールするアプリケーション |
Chart | パッケージを構成するYAML達 |
Release | インストールされたパッケージ |
Name | -n, --name string release name. If unspecified, it will autogenerate one for you |
Revision | Releaseをupgradeするとrevisionが上がって履歴管理されるらしい |
んー、なんのコッチャ
ライフサイクル
- 12factor appに則っている
- ChartをReleaseとして管理している
- upgradeコマンドでReleaseを作る
- rollbackコマンドで戻せる
- historyで履歴を見れる
ふーん。 やってみるか。
dashboardの最新versionは0.4.1っぽいので、0.4.0をinstallしてみる
~ ❯ helm install stable/kubernetes-dashboard -n test-dash --version 0.4.0
~ ❯ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE test-dash 1 Tue Mar 6 22:35:32 2018 DEPLOYED kubernetes-dashboard-0.4.0 default
0.4.0がinstall出来た。nameも指定できた。
んじゃupgradeしてみよう。
> helm upgrade test-dash stable/kubernetes-dashboard --version 0.4.1 Error: UPGRADE FAILED: Deployment.apps "test-dash-kubernetes-dashboard" is invalid: spec.template.metadata.labels: Invalid value: map[string]string{"release":"test-dash", "app":"kubernetes-dashboard", "chart":"kubernetes-dashboard-0.4.1", "heritage":"Tiller", "kubernetes.io/cluster-service":"true"}: `selector` does not match template `labels`
( ゚д゚)ハッ! 失敗
ん?
version指定を消してみる、、、うまくいった。 0.4.0に戻してみたり、再度0.4.1にしてみたり。
0.4.0 -> 0.4.1(失敗) -> 0.4.1(成功) -> 0.4.0(成功) -> 0.4.1(失敗) -> 0.4.1(成功)
何故か2回やると上手くいく… なんかバグ踏んだ気がする
~ ❯ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE test-dash 7 Tue Mar 6 22:43:31 2018 DEPLOYED kubernetes-dashboard-0.4.1 default ~ ❯ helm history test-dash REVISION UPDATED STATUS CHART DESCRIPTION 1 Tue Mar 6 22:35:32 2018 SUPERSEDED kubernetes-dashboard-0.4.0 Install complete 2 Tue Mar 6 22:38:54 2018 SUPERSEDED kubernetes-dashboard-0.4.1 Upgrade "test-dash" failed: Deployment.apps "test-dash-ku... 3 Tue Mar 6 22:40:28 2018 SUPERSEDED kubernetes-dashboard-0.4.1 Upgrade complete 4 Tue Mar 6 22:40:49 2018 SUPERSEDED kubernetes-dashboard-0.4.1 Upgrade complete 5 Tue Mar 6 22:40:55 2018 SUPERSEDED kubernetes-dashboard-0.4.0 Upgrade complete 6 Tue Mar 6 22:43:03 2018 SUPERSEDED kubernetes-dashboard-0.4.1 Upgrade "test-dash" failed: Deployment.apps "test-dash-ku... 7 Tue Mar 6 22:43:31 2018 DEPLOYED kubernetes-dashboard-0.4.1 Upgrade complete
こんな感じ。 なるほどね。
構成
https://docs.helm.sh/architecture/
主な要素は以下の3つ
- helmコマンド
- Repository
- Chartの管理
- Tiller
- Chartと設定を使ってreleaseを作る
- k8sにデプロイする
- 要はこいつが肝心
ちなみに、それぞれのやり取りは全てgRPCらしい。
なるほどね。
今日はここまで。 次回は自前でChart書いてみるかな。
Helm (Kubernetes) vol.1
環境
Helm
そう、helm。 Emacserにはお馴染み、違う。 そのhelmじゃないw
xxx.sh
っていうドメイン(・∀・)イイネ!!
Helm is the best way to find, share, and use software built for Kubernetes.
そう、k8s上で動かすソフトウェアをパッケージ管理する為の物
k8sにMySQL入れたり、とかとか、楽にできるようにするやつ。
installation
kubernetes
まずは、兎にも角にもk8sが必要だ。
https://qiita.com/taishin/items/920d62a641c9cd58f289
gcloudコマンド経由でkubectlインストールしてると競合する… ので、先に消しとく
gcloud components remove kubectl
which kubectl
して
/usr/local/bin/kubectl
を参照してればOK
❯ kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE docker-for-desktop docker-for-desktop-cluster docker-for-desktop
いっぱいあるーね。 でもちゃんと、GKEのcontextも読めてるのでOK
docker-for-desktop
がローカルのやつ。
さぁ、contextを切り替えておこう。
❯ kubectl config use-context docker-for-desktop Switched to context "docker-for-desktop". ❯ kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * docker-for-desktop docker-for-desktop-cluster docker-for-desktop
OK
helm
今時のツールはdocumentがしっかりしてるし、構築は楽にできるようになってるね。 homebrewで終了。
brew install kubernetes-helm
Helmを使う
Init
まずは、K8sがHelmを使えるようにする為にinitする必要がある。
~ ❯ helm init $HELM_HOME has been configured at /Users/xxx/.helm. Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster. Happy Helming!
$HELM_HOME
が設定されるTiller
ってやつがk8sにinstallされたらしい
~ ❯ kubectl get po --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE docker compose-5d4f4d67b6-5d7d8 1/1 Running 0 20m docker compose-api-7bb7b5968f-8fwmw 1/1 Running 0 20m kube-system etcd-docker-for-desktop 1/1 Running 0 20m kube-system kube-apiserver-docker-for-desktop 1/1 Running 0 20m kube-system kube-controller-manager-docker-for-desktop 1/1 Running 0 20m kube-system kube-dns-6f4fd4bdf-lpjhf 3/3 Running 0 21m kube-system kube-proxy-sf4n4 1/1 Running 0 21m kube-system kube-scheduler-docker-for-desktop 1/1 Running 0 21m kube-system tiller-deploy-5bd98cbb68-skrqn 1/1 Running 0 1m
kube-system tiller-deploy-5bd98cbb68-skrqn 1/1 Running 0 1m
↑これ
パッケージを見てみよう
helmではパッケージをChartsと呼ぶ(海図らしい)
~ ❯ helm search NAME VERSION DESCRIPTION stable/acs-engine-autoscaler 2.1.0 Scales worker nodes within agent pools stable/artifactory 6.1.0 Universal Repository Manager supporting all maj... stable/aws-cluster-autoscaler 0.3.1 Scales worker nodes within autoscaling groups. stable/buildkite 0.2.0 Agent for Buildkite …
長いので、省略するけど、結構いろいろある。 デフォルトのレポジトリ。
Helm使って何か動かしてみるか
手始めにk8sのダッシュボードを入れてみるかな GKEはデフォルトではいってる
stable/kubernetes-dashboard
これ。
~ ❯ helm inspect stable/kubernetes-dashboard appVersion: 1.7.1 description: General-purpose web UI for Kubernetes clusters keywords: - kubernetes - dashboard maintainers: - email: Kevin.Fox@pnnl.gov name: kfox1111 name: kubernetes-dashboard version: 0.4.1 --- # Default values for kubernetes-dashboard # This is a YAML-formatted file. # Declare name/value pairs to be passed into your templates. # name: value image: gcr.io/google_containers/kubernetes-dashboard-amd64 imageTag: "v1.7.1" imagePullPolicy: "IfNotPresent" nodeSelector: {} httpPort: 80 serviceType: ClusterIP resources: limits: cpu: 100m memory: 50Mi requests: cpu: 100m memory: 50Mi ingress: ## If true, Kubernetes Dashboard Ingress will be created. ## enabled: false ## Kubernetes Dashboard Ingress annotations ## # annotations: # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: 'true' ## Kubernetes Dashboard Ingress hostnames ## Must be provided if Ingress is enabled ## # hosts: # - kubernetes-dashboard.domain.com ## Kubernetes Dashboard Ingress TLS configuration ## Secrets must be manually created in the namespace ## # tls: # - secretName: kubernetes-dashboard-tls # hosts: # - kubernetes-dashboard.domain.com rbac: ## If true, create & use RBAC resources # create: false ## Ignored if rbac.create is true # serviceAccountName: default
Default values for kubernetes-dashboard
って書いてある所以下が、パラメータになっていて、
Ansibleの defaults/main.yml
みたいな感じで、設定を外から渡せるようになっている。
ひとまず、そのまま立ち上げてみよう。
~ ❯ helm install stable/kubernetes-dashboard NAME: brown-whippet LAST DEPLOYED: Sat Mar 3 22:26:48 2018 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE brown-whippet-kubernetes-dashboard 1 1 1 0 0s ==> v1/Service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE brown-whippet-kubernetes-dashboard 10.107.90.189 <none> 80/TCP 0s NOTES: ********************************************************************************* *** PLEASE BE PATIENT: kubernetes-dashboard may take a few minutes to install *** ********************************************************************************* Get the Kubernetes Dashboard URL by running: export POD_NAME=$(kubectl get pods -n default -l "app=kubernetes-dashboard,release=brown-whippet" -o jsonpath="{.items[0].metadata.name}") echo http://127.0.0.1:9090/ kubectl -n default port-forward $POD_NAME 9090:9090 ~ ❯ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE brown-whippet 1 Sat Mar 3 22:26:48 2018 DEPLOYED kubernetes-dashboard-0.4.1 default
瞬殺。
~ ❯ kubectl get po NAME READY STATUS RESTARTS AGE brown-whippet-kubernetes-dashboard-6c546f54f8-tmccq 1/1 Running 0 3m
ちゃんと動いてる じゃあ、画面見てみるか。
Get the Kubernetes Dashboard URL by running: export POD_NAME=$(kubectl get pods -n default -l "app=kubernetes-dashboard,release=brown-whippet" -o jsonpath="{.items[0].metadata.name}") echo http://127.0.0.1:9090/ kubectl -n default port-forward $POD_NAME 9090:9090
丁寧に書いてくれてるね。
http://localhost:9090
でアクセスできる。
てか、なんか大分いろいろ見やすくなってる… 進化してるな。 こんどモニタリングでdashboard使ってみよう。
installしたChartsを落とす
helm delete
してみる。
~ ❯ helm delete stable/kubernetes-dashboard Error: invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53
Charts名じゃ消せないらしい。 デプロイしたChartsに対してnameが振られているので、そっちで消す。 (同じChartsでもname分けて複数デプロイできるようになっている)
~ ❯ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE brown-whippet 1 Sat Mar 3 22:26:48 2018 DEPLOYED kubernetes-dashboard-0.4.1 default ~ ❯ helm delete brown-whippet release "brown-whippet" deleted ~ ❯ helm ls ~ ❯ kubectl get po No resources found.
消えました。
今日はここまで。
TODO
- 独自Chartsを書いてみる
- PrivateRepositoryは使えるのか?
- 独自repositoryを作る
- etc…
Aerospikeを試す #2 HeartBeat & ReverseProxy
前回から引き続き、Aerospikeネタ。
前回はheartbeatのmesh-seed-address-port
に1つのインスタンスを指定しましたが、これが1個だとSPOFになってしまう。
mesh-seed-address-port
は複数指定できるっぽいので、いくつか指定しておけば良いのだろうけど、それはそれでめんどくさい。
インスタンスの追加・削除時に設定を撒かないといけないし。
というわけで、ReverseProxy挟んだらいけんじゃね?という仮説を検証してみようと思ったので、やってみました。
(どうやるのが正解なんだろうか?)
追記
やっぱりこの方法ダメっぽい。
Cluster Visibility
がずっとfalse
だったりとか
Repl'n Factor
が1
のまま(2に設定しているのに)だったりする。
やっぱり公式に書いてある通り
mesh-seed-address-port
を複数設定するのが正解なんだろうか。
今日のテーマ
- Proxyサーバを立てる
- heartbeatの向き先を変える
- AMCから確認する
以上
環境
- Mac OSX 10.10.3
- Vagrant 1.7.2
- Aerospike 3.5.14
Proxyサーバを立てる
前回同様vagrantでインスタンスを立てる。 今回は超単純なReverseProxyができれば良いのでHAProxyを使います。
Vagarntfileの修正
... config.vm.define "proxy" do |server| server.vm.box = "proxy-base" server.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130731.box" server.vm.network :private_network, ip: "192.168.33.200" end ...
インスタンス起動
vagrant up proxy
HAProxyのインストール&設定
まずはインストール
vagrant ssh proxy sudo yum install -y haproxy
したら、設定を書き換える
... frontend forward_mesh *:3002 mode tcp default_backend aerospike_mesh backend aerospike_mesh balance roundrobin server node1 192.168.33.101:3002 check server node2 192.168.33.102:3002 check ...
3002
ポートをroundrobinで前回立てたインスタンスに振り分けます
HAProxyを再起動
sudo /etc/init.d/haproxy restart
heartbeatの向き先を変える
以下、node1``node2
両方に行います
vagrant ssh node1 sudo vi /etc/aerospike/aerospike.conf
network { ... heartbeat { mode mesh port 3002 address 192.168.33.101 mesh-seed-address-port 192.168.33.200 3002 interval 5 timeout 10 } ... }
mesh-seed-address-port
をproxy
サーバのアドレスに変更する
aerospike
も再起動
sudo /etc/init.d/aerospike restart
AMCから確認してみる
http://localhost:8081
にアクセスします。
無事、2台いることが確認できました。 めでたしめでたし(?)
on EC2にするときは、PrivateELB的な物を立てるんだろうか。
けど、なんかAMCを見てるとRepilicationがうまくいって無い気がする... もうちょい調査する。
まとめ
- heartbeatはreverse proxy通してもいけたけど、なんか怪しい
- 次回以降
- プログラムからCRUD
- UDF
Aerospikeを試す #1 環境構築
最近ちょくちょく見るようになったAerospikeです。 OSSになってからほんとよく見かけますが、あんまり日本語の情報が多くないですね。
- 作者: 佐藤鉄平,小林明大,石村真吾,坂上卓史,上原誠,鳥居英,佐藤歩,泉水翔吾,うさみけんた,伊藤直也,高橋侑久,佐藤太一,hayajo,橋本翔,西尾泰和,中島聡,はまちや2,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2015/06/24
- メディア: 大型本
- この商品を含むブログを見る
これのAerospike入門を読んで触発されて触ってみることにしたので、 環境構築からやってみたいなと思います。
今日のテーマ
環境
- Mac OSX 10.10.3
- Vagrant 1.7.2
VagrantでAerospike謹製のboxを起動する
http://www.aerospike.com/docs/operations/install/vagrant/mac/
ここに書いてある通りなんですけど、今回は2台でクラスタ環境つくりたいので、
vagrant init
した後にちょいと書き換えます
vagrant init aerospike/centos-6.5
してVagrantfile
をこんな感じに
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure(2) do |config| aerospike_box = "aerospike/centos-6.5" config.vm.define "node1" do |server| server.vm.box = aerospike_box server.vm.network :private_network, ip: "192.168.33.101" server.vm.network "forwarded_port", guest: 8081, host: 8081 server.vm.network "forwarded_port", guest: 3000, host: 3000 end config.vm.define "node2" do |server| server.vm.box = aerospike_box server.vm.network :private_network, ip: "192.168.33.102" end end
- ゲスト間通信したいので、
private_network
でPrivateIPをふる - AMCにアクセスしたいので
node1
は8081
ポートをフォワーディング - Aerospikeにホストからアクセスしたいので、
node1
は3000
ポートをフォワーディング
では起動します。
vagrant up
起動したら、中に入って、ちょっと確認してみます。
$ vagrant ssh node1 $ asmonitor -e info ... 1 hosts in cluster: 127.0.0.1:3000 ...
てなのが出てればとりあえずOKかな。
AMCを見てみる
Aerospike Management Consoleを見てみます。
この為に8081
ポートでアクセスできるようにしたので、下記をブラウザで開きます。
http://localhost:8081/
なんかかっこいい画面が出てきましたね。
- Disk Usage
- RAM Usage
- Cluster Summary
- Cluster Throughput
- etc ...
これは良い感じですね。 マジ運用で使う場合は、踏み台経由のポートフォワーディングとかだけ見れるようにしとけば楽そう。
2台のクラスター環境を構築する
さて、ここからが今回のメインです。 クラスター環境を構築してみます。 (今回はAWSではないですが、最終的にAWSに構築する事を前提にしているので、multicastは使わず、meshでやります)
それぞれ以下のようにconfを書き換え
- node1
network { service { address any port 3000 access-address 192.168.33.101 network-interface-name eth1 } heartbeat { mode mesh port 3002 address 192.168.33.101 mesh-seed-address-port 192.168.33.101 3002 interval 5 timeout 10 } fabric { port 3001 } info { port 3003 } }
- node2
network { service { address any port 3000 access-address 192.168.33.102 network-interface-name eth1 } heartbeat { mode mesh port 3002 address 192.168.33.102 mesh-seed-address-port 192.168.33.101 3002 interval 5 timeout 10 } fabric { port 3001 } info { port 3003 } }
ポイントは
- service
- access-adress
をVagrantfileで指定したIPアドレスに書き換える
- network-interface-name
をeth1
にする
- heartbeat
- mode
はmesh
にする
- address
をVagrantfileで指定したIPアドレスにする
- mesh-seed-address-port
はnode1
を見るようにする(2台とも)
- ここは、両方のconfに両方のIP書いても大丈夫でした
network-interface-name
を変えないといけないことに気づくのにめっちゃ時間かかった...
[vagrant@localhost ~]$ ifconfig eth0 Link encap:Ethernet HWaddr 08:00:27:78:30:4D inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe78:304d/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:3323 errors:0 dropped:0 overruns:0 frame:0 TX packets:2423 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:267581 (261.3 KiB) TX bytes:579927 (566.3 KiB) eth1 Link encap:Ethernet HWaddr 08:00:27:CA:31:8D inet addr:192.168.33.102 Bcast:192.168.33.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:feca:318d/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:27960681 errors:0 dropped:0 overruns:0 frame:0 TX packets:27846070 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:5863570470 (5.4 GiB) TX bytes:5871987364 (5.4 GiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:7999 errors:0 dropped:0 overruns:0 frame:0 TX packets:7999 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:5992988 (5.7 MiB) TX bytes:5992988 (5.7 MiB)
両方再起動して確認
$ sudo /etc/init.d/aerospike restart $ asmonitor -e info Enter help for commands 2 hosts in cluster: 192.168.33.101:3000,192.168.33.102:3000 ===NODES=== 2015-07-10 00:29:19.964079 Sorting by IP, in Ascending order: ip:port Build Cluster Cluster Free Free Migrates Node Principal Replicated Sys . Size Visibility Disk Mem . ID ID Objects Free . . . pct pct . . . . Mem 192.168.33.101:3000 3.5.14 2 true 98 98 (1961,1) BB98262CE270008 BB98D31CA270008 197,932 87 192.168.33.102:3000 3.5.14 2 true 98 95 (2008,1) BB98D31CA270008 BB98D31CA270008 197,932 86 Number of nodes displayed: 2 ===NAMESPACE=== Total (unique) objects in cluster for test : 197,932 Note: Total (unique) objects is an under estimate if migrations are in progress. ip/namespace Avail Evicted Master Repl Stop Used Used Used Used hwm hwm Pct Objects Objects Factor Writes Disk Disk Mem Mem Disk Mem . . . . . . % . % . . 192.168.33.101/test 98 0 101,982 2 false 84.42 M 2 36.89 M 2 50 60 192.168.33.102/test 98 0 95,950 2 false 84.42 M 2 84.23 M 5 50 60 Number of rows displayed: 2
2 hosts in cluster: 192.168.33.101:3000,192.168.33.102:3000
2台のクラスタになっていることがわかりますね
AMCから見てみる
ちゃんと2台いることがわかりますね。
まとめ
Rubiniusを試す #1 (Install,Benchmark)
だいぶ久しぶりの投稿です。
ちょいとふと思い立ってやってみたので、メモ。
Macにrbenv(ruby-build)でRubinius
をインストールする。
Rubiniusとは
http://rubini.us/doc/ja/what-is-rubinius/
Rubinius は プログラミング言語 Ruby の実装です。 Rubiniusには、バイトコード仮想マシン、Rubyの構文解析器、バイトコードコンパイラ、世代別ガベージコレクタ、ジャストインタイム (JIT) ネイティブマシンコードコンパイラ、そして、Ruby のコアライブラリと標準ライブラリが含まれています。 Rubinius は Ruby 2.1.0 を目標としています。
とのこと。
https://github.com/rubinius/rubinius/tree/master/kernel/bootstrap 読みやすい(Rubyで実装されている)
昔見た時は「全部Rubyで書いてある」って聞いた気がするんだけどVMはC++で書かれてるな。
といっても、当時ちゃんと知らなかったので、何か変わったのか、変わってないのかは、わからない。
一番の特徴はスレッドがGVL(Giant VM Lock)の制約を受けない為、マルチコアが活かせること。
つまり、PumaやSidekiqのようにマルチスレッドで動作する場合に優位性があるのではないかと思う。
環境
インストールマシン
Rubinius
- version: 2.5.7
Requirements
事前に必要なモノを入れておく
といっても、多分LLVMだけ入れておけば良い。
が、Homebrewで最新のLLVMを入れると3.6
が入ってしまう。
Rubinius(rbx-2.5.7)はLLVM3.0-3.5
じゃないと怒られてしまうので、
LLVM3.5を入れる(インストールしなくてもバイナリがあれば良い)
- PreBuildされたバイナリがあるので、ダウンロード
- http://llvm.org/releases/download.html#3.5.2
Pre-built Binaries:
->Clang for Mac OS X
- 適当な場所に解凍しておく
もしかしたら
gem install rubinius-melbourne
しといた方がよいのかも
ruby-buildを最新にする
※以下、ruby-build
をbrewでinstallしている場合
ruby-build
のFormulaがまだ、2.5.7
が入ってるrevisionを見ていないので、強制的にmasterのHEADを向ける
brew upgrade --HEAD ruby-build
Installation
さて、やっとインストールするが、ちょいと環境変数が多くなるので、整理
LDFLAGS=-L{解凍先}/lib \ CPPFLAGS=-I{解凍先}/include \ CC={解凍先}/clang \ CXX={解凍先}/clang++ \ RUBY_CONFIGURE_OPTS="--llvm-config={解凍先}/bin/llvm-config" \ rbenv install -v rbx-2.5.7
こんな感じで多分行ける
試してみる
❯ rbenv versions system 2.0.0-p353 2.2.1 * 2.2.2 rbx-2.5.7
rbenv local rbx-2.5.7
❯ ruby -v rubinius 2.5.7 (2.1.0 d4fdeaad 2015-06-28 3.5.2 JI) [x86_64-darwin14.3.0]
ベンチってみる
簡単なfibonacciの関数書いて試してみる
require 'benchmark' def fib(n) if n == 0 0 elsif n == 1 1 else fib(n-1) + fib(n-2) end end Benchmark.bmbm do |x| x.report("fibonacci") { fib(40) } end
負荷がかかれば何でも良いので、中身はあまり気にしない
Ruby 2.2.2
❯ ruby fib.rb Rehearsal --------------------------------------------- fibonacci 20.300000 0.050000 20.350000 ( 21.297663) ----------------------------------- total: 20.350000sec user system total real fibonacci 20.620000 0.060000 20.680000 ( 21.640249)
Rubinius 2.5.7
❯ ruby fib.rb Rehearsal --------------------------------------------- fibonacci 7.426329 0.117764 7.544093 ( 7.756742) ------------------------------------ total: 7.544093sec user system total real fibonacci 5.223988 0.064434 5.288422 ( 6.797919)
感想
- パフォーマンス的には以外と差がでた
- ただ、実行する度に割りと速度にばらつきがある
- 5s〜10s
- 原因までは追ってない
- ただ、実行する度に割りと速度にばらつきがある
- 厳密にマルチスレッドになると、ちゃんとマルチスレッドプログラミングできてないと危険
- Rubiniusの運用ノウハウが無いので本番で使うことはきっと無いだろう
- 日本で使ってるところあるんだろうか?
- 最近も開発は割りと進んでいるようなので、もうすこしいろいろやってみたい
Goでテストを書く
久しぶりにちゃんとgolangを勉強していこうという事で、ログを残します。
今日のテーマ
testing
パッケージを使ってテストを書いてみるgom
でgospel
をインストールgospel
でテスト書いてみる- 独自matcherを書いてみる
環境
構成
$ tree . . ├── Gomfile ├── src │ └── model │ ├── user.go │ ├── user_gospel_test.go │ └── user_test.go └── vendor
testing
パッケージを使ってテストを書いてみる
まずは、標準のお作法にしたがってテストを書いてみます。
テスト対象はこんな感じです。
user.go
package model import ( "strings" ) type User struct { FirstName string LastName string Divisions []string } func NewUser(name string) *User { names := strings.Split(name, " ") if len(names) >= 2 { return &User{FirstName: names[0], LastName: names[1]} } else { return &User{FirstName: names[0]} } } func (user *User) FullName() string{ return user.FirstName + " " + user.LastName } func (user *User) AddDivision(division string) *User{ user.Divisions = append(user.Divisions, division) return user }
さて、テストを書いてみます。
user_test.go
package model import ( "testing" ) func TestConstructorWithFullname(t *testing.T) { user := NewUser("hoge aaa") if user.FirstName != "hoge" { t.Error("user's first name should be hoge") } if user.LastName != "aaa" { t.Error("user's last name should be aaa") } } func TestConstructorWithFirstName(t *testing.T) { user := NewUser("hoge") if user.FirstName != "hoge" { t.Error("user's first name should be hoge") } if user.LastName != "" { t.Error("user's last name should be empty") } } func TestConstructorWithEmptyString(t *testing.T) { user := NewUser("") if user.FirstName != "" { t.Error("user's first name should be empty") } if user.LastName != "" { t.Error("user's last name should be empty") } } func TestDevision(t *testing.T) { user := NewUser("hoge aaa") if len(user.Divisions) != 0 { t.Error("default divisions is empty slice") } } func TestFullName(t *testing.T) { fullname := "hoge aaa" user := NewUser(fullname) if user.FullName() != fullname { t.Errorf("fullname should be %s, but %s", fullname, user.FullName()) } } func TestAddDevision(t *testing.T) { user := NewUser("hoge aaa") division := "test" user.AddDivision(division) if user.Divisions[0] != division { t.Log(user.Divisions) t.Errorf("%s division was not added", division) } }
xxx_test.go
というファイル名にする- テスト関数は
Test
というprefixをつける - アサーションは自分で
if
などでチェックしてt.Error
でfailさせる
testing
にはアサートしてくれる関数がありません。
その辺の考え方的には
あたりを見るとよく分かります。 ふむ。
では、テスト実行してみます。
まずは、GOPATH
をカレントディレクトリにしておきます。
$ export GOPATH=`pwd`
したら、実行します。
$ go test -v ./src/... === RUN TestConstructorWithFullname --- PASS: TestConstructorWithFullname (0.00 seconds) === RUN TestConstructorWithFirstName --- PASS: TestConstructorWithFirstName (0.00 seconds) === RUN TestConstructorWithEmptyString --- PASS: TestConstructorWithEmptyString (0.00 seconds) === RUN TestDevision --- PASS: TestDevision (0.00 seconds) === RUN TestFullName --- PASS: TestFullName (0.00 seconds) === RUN TestAddDevision --- PASS: TestAddDevision (0.00 seconds) PASS ok model 0.010s
全部通りました。
gom
でgospel
をインストール
依存パッケージはやっぱり設定ファイルとかで管理したいので、今回はgom
を使ってみます。
(gondler
の方がスタンダードなのかな?依存パッケージのバージョンとか、あまり管理しないのかな?)
まずはgomをインストール
ここではgo get
つかいます。
$ go get github.com/mattn/gom
Gomfileを書く
gom 'github.com/r7kamura/gospel', :commit => 'd575dd12c2eb84612ae5c84fab56ccb4ce156a1e'
Rubyっぽい...
mattn/gom · GitHub
とか見てもほぼGemfileですね。
tag
も使えるし、group
も使える。
じゃあ、こんなのもいけるのか??
gom 'github.com/r7kamura/gospel', commit: 'd575dd12c2eb84612ae5c84fab56ccb4ce156a1e'
で
$ gom install gom: Syntax Error at line 1
怒られました... orz
気を取り直して、Gomfileを元に戻してインストールしなおします。
$ gom install downloading github.com/r7kamura/gospel
無事完了。
gospel
でテスト書いてみる
では、使い方を参考に書いてみます。
user_gospel_test.go
package model import ( . "github.com/r7kamura/gospel" "testing" ) func TestUser(t *testing.T) { Describe(t, "NewUser", func() { Context("フルネーム(hoge aaa)を指定した場合", func() { user := NewUser("hoge aaa") It("FirstName should be hoge", func() { Expect(user.FirstName).To(Equal, "hoge") }) It("LastName should be aaa", func() { Expect(user.LastName).To(Equal, "aaa") }) }) Context("空文字を渡した場合", func() { user := NewUser("") It("FirstName should be empty string", func() { Expect(user.FirstName).To(Equal, "") }) It("LastName should be empty string", func() { Expect(user.LastName).To(Equal, "") }) }) }) Describe(t, "Divisions", func() { user := NewUser("") It("default divisions is empty slice", func() { Expect(len(user.Divisions)).To(Equal, 0) }) }) }
Rspec風に書けてBDDな感じで書きやすいですね。
実行する時はgom exec
使います。
bundle exec
みたいな。
$ gom exec go test -v ./src/... === RUN TestUser NewUser フルネーム(hoge aaa)を指定した場合 FirstName should be hoge LastName should be aaa 空文字を渡した場合 FirstName should be empty string LastName should be empty string Divisions default divisions is empty slice PASS ok model 0.011s
※ 急に日本語で書いたのは気にしない ※ だいぶテストケースが減ってるのも気にしない
結果も見やすくて良い感じです。 (ちなみに、Describeをネストするとエラーになりました)
独自matcherを書いてみる
スライスとか配列のlengthをチェックするmatcherとかあっても良いかなぁという気がしていて、 この辺とか見ていたら、 自分で書けそうなので、書いてみました。
package model import ( . "github.com/r7kamura/gospel" "testing" "fmt" "reflect" ) func EqualLength(values ...interface{}) (failureMessage string) { actualValue := reflect.ValueOf(values[0]) if actualValue.Kind() != reflect.Slice && actualValue.Kind() != reflect.Array { failureMessage = fmt.Sprintf("`%v` is not slice or array", values[0]) return } if actualValue.Len() != values[1] { failureMessage = fmt.Sprintf("Expected `%v` length `%v` to equal `%v`", values[0], actualValue.Len(), values[1]) } return } func TestUser(t *testing.T) { Describe(t, "Divisions", func() { user := NewUser("") It("default divisions is empty slice", func() { Expect(user.Divisions).To(EqualLength, 0) }) }) }
reflect
パッケージ使って、TypeがSliceかArrayならlengthチェックするみたいな事をしています。
ためしに、
It("length", func() { Expect([]string{"hoge"}).To(EqualLength, 0) }) It("length", func() { Expect([1]string{"hoge"}).To(EqualLength, 0) }) It("length", func() { Expect(100).To(EqualLength, 0) })
こんなんでfailさせてみます。
=== RUN TestUser Divisions length Expected `[hoge]` length `1` to equal `0` /Users/natsuki/workspace/go-samples/test/unit-test/src/model/user_gospel_test.go:63 62. It("length", func() { 63. Expect([]string{"hoge"}).To(EqualLength, 0) 64. }) length Expected `[hoge]` length `1` to equal `0` /Users/natsuki/workspace/go-samples/test/unit-test/src/model/user_gospel_test.go:66 65. It("length", func() { 66. Expect([1]string{"hoge"}).To(EqualLength, 0) 67. }) length `100` is not slice or array /Users/natsuki/workspace/go-samples/test/unit-test/src/model/user_gospel_test.go:69 68. It("length", func() { 69. Expect(100).To(EqualLength, 0) 70. }) --- FAIL: TestUser (0.00 seconds) FAIL exit status 1 FAIL model 0.009s gom: exit status 1
おぉ、悪くないかも。