10 分で書ける cloud foundry route service
TRANSCRIPT
RouteService@公式
Introduction
CloudFoundryapplicaAondevelopersmaywishtoapplytransformaAonorprocessingtorequestsbeforetheyreachanapplicaAon.ex.)authenAcaAon/ratelimiAng/cachingservices
AppへのRequest到達前に何か処理加えたくない?
RouteServiceって何
CloudFoundryRouteService
≒
(CloudFoundryにおいて) Appにくっついて お仕事してくれる君
App MySQLService
RMQService
applicaAonservice
RouteServiceって何
CloudFoundryRouteService
≒
(CloudFoundryにおいて) Appにくっついて お仕事してくれる君
App MySQLService
RMQService
bind
applicaAonservice
アプリへのAccessRoute
Client LoadBalancer
CFRouter App
CF
アプリを使う人ロードバランサあってもなくても
CFアプリCFの中で
アクセス割り振る人
()① ② ③
CloudFoundryRouteService
Client LoadBalancer
CFRouter App
CF
RouteService
①②
③
④
⑤
⑥
アプリへのRoute到達前に一旦自分を経由させる
CloudFoundryRouteService
Client LoadBalancer
CFRouter App
CF
RouteService
①②
③
④
⑤
⑥
基本的に全てHTTPなので・・・
CloudFoundryRouteService
Client LoadBalancer
CFRouter App
CF
RouteService
①②
③
④
⑤
⑥
RouteServiceの正体 = HTTPサーバ
hiroaki@HMP:~/demo$ tree.!"" hello# !"" Staticfile# !"" index.html# %"" manifest.yml%"" s-rs !"" Godeps # !"" Godeps.json # !"" Readme # %"" _workspace !"" Procfile !"" README.md !"" main.go %"" manifest.yml
4 directories, 9 files
Client LoadBalancer
CFRouter App
CF
RouteService
hello/配下はこの後BackendのAppに、s-rs/配下はRoute-Serviceになる予定です。
ソースコードは下記Repositoryでも見ることができますh[ps://github.com/hiroakiukaji/simple-route-service-handson
hiroaki@HMP:~/demo$ cd hello/hiroaki@HMP:~/demo/hello$ lsStaticfile index.html manifest.ymlhiroaki@HMP:~/demo/hello$ cf pushUsing manifest file /Users/hiroaki/demo/hello/manifest.yml
Creating app staticapp in org ukaji_org / space ukaji_space as ukaji...OK
:::::
App started
OKApp staticapp was started using this command `sh boot.sh`
Showing health and status for app staticapp in org ukaji_org / space ukaji_space as ukaji...OK
requested state: startedinstances: 1/1usage: 256M x 1 instancesurls: staticapp.mcf.nttlabs.infolast uploaded: Mon Aug 8 02:36:03 UTC 2016stack: unknownbuildpack: staticfile 1.3.9
state since cpu memory disk details#0 running 2016-08-08 11:36:17 AM 0.0% 0 of 256M 0 of 1Ghiroaki@HMP:~/demo/hello$ curl staticapp.mcf.nttlabs.info(´・_・`)
Client LoadBalancer
CFRouter App
CF
まずはBackendのアプリをデプロイ
hiroaki@HMP:~/demo/hello$ cd ../s-rs/hiroaki@HMP:~/demo/s-rs$ lsGodeps Procfile README.md main.go manifest.ymlhiroaki@HMP:~/demo/s-rs$ cf push
:::::
requested state: startedinstances: 1/1usage: 256M x 1 instancesurls: simple-rs.mcf.nttlabs.infolast uploaded: Mon Aug 8 04:19:13 UTC 2016stack: unknownbuildpack: go_buildpack
state since cpu memory disk details#0 running 2016-08-08 01:19:32 PM 0.0% 0 of 256M 0 of 1Ghiroaki@HMP:~/demo/s-rs$ cf create-user-provided-service simplerouteservice \ -r https://simple-rs.mcf.nttlabs.infoCreating user provided service simplerouteservice in org ukaji_org / space ukaji_space as ukaji...OKhiroaki@HMP:~/demo/s-rs$ cf servicesGetting services in org ukaji_org / space ukaji_space as ukaji...OK
name service plan bound apps last operationsimplerouteservice user-provided
RouteServiceとして使うHTTPサーバをデプロイ。(※)その後HTTPサーバをServiceとして認識させる。
※HTTPSで到達できる場所ならばどこでも構いませんが、今回はたまたまCloudFoundry上に同居させています。
Client LoadBalancer
CFRouter App
CF
RouteService
hiroaki@HMP:~/demo/s-rs$ cf bind-route-service mcf.nttlabs.info simplerouteservice -n staticappBinding route staticapp.mcf.nttlabs.info to service instance simplerouteservice in org ukaji_org / space ukaji_space as ukaji...OKhiroaki@HMP:~/demo/s-rs$ curl staticapp.mcf.nttlabs.info(´・_・`) RouteServiceとしてバインドすれば完成。
Client LoadBalancer
CFRouter App
CF
RouteService
hiroaki@HMP:~$ cf logs simple-rsConnected, tailing logs for app simple-rs in org ukaji_org / space ukaji_space as ukaji...
2016-08-08T13:36:58.24+0900 [RTR/0] OUT simple-rs.mcf.nttlabs.info - [08/08/2016:04:36:58.238 +0000] "GET / HTTP/1.0" 200 0 13 "-" "curl/7.43.0" 192.168.12.34:35667 x_forwarded_for:"192.168.10.40, 192.168.12.34, 192.168.13.51" x_forwarded_proto:"https" vcap_request_id:ab5cfd36-1548-4f95-7fb2-8c9fc7dc3262 response_time:0.008521804 app_id:2b11f3ec-d733-48e2-b141-5ac98020eee3
何も処理をしていないので振る舞いは変わりませんが、RouteServiceのアプリのログを見てみると何らかのHTTPアクセスが飛んでいることが分かるはず。
RouteServicebyGolangの実装
小構成で60行ぐらいpackage main
import (:)
type SimpleRoundTripper struct {transport http.RoundTripper
}
func newSimpleRoundTripper() *SimpleRoundTripper {transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipSslValidation()},}return &SimpleRoundTripper{
transport: transport,}
}
func (s *SimpleRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {var response *http.Responsevar err errorresponse, err = s.transport.RoundTrip(request)if err != nil {
return nil, err}return response, err
}
func main() {http.Handle("/", newProxy())log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))
}
func newProxy() http.Handler {proxy := &httputil.ReverseProxy{
Director: func(r *http.Request) {url, err := url.Parse(r.Header.Get("X-Cf-Forwarded-Url"))if err != nil {
log.Fatalln(err.Error())}r.URL = urlr.Host = url.Host
},Transport: newSimpleRoundTripper(),
}return proxy
}
func skipSslValidation() bool {var skipSslValidation boolvar err errorif skipSslValidation, err = strconv.ParseBool(os.Getenv("SKIP_SSL_VALIDATION")); err != nil {
skipSslValidation = true}return skipSslValidation
}
RouteServicebyGolangの実装
Entrypoint
func main() {http.Handle("/", newProxy())
log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))}
GET / を待ち受けるただのHTTPサーバ
RouteServicebyGolangの実装
RouteServiceの本体を担うメソッド
func (s *SimpleRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {var response *http.Responsevar err error
response, err = s.transport.RoundTrip(request)if err != nil {
return nil, err}
return response, err}
あからさまにRequestを受けてResponseを返している人がいます
※この例は本当に素通しで何もしていません
RouteServicebyGolangの実装
RouteServiceの本体を担うメソッドを書いてみる
func (s *SimpleRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {var response *http.Responsevar err error
response, err = s.transport.RoundTrip(request)if err != nil {
return nil, err}
return response, err}
適当な処理を書いてResponseを投げ返してみましょう
こ の へ ん
hiroaki@HMP:~/demo/s-rs$ lsGodeps Procfile README.md main.go manifest.ymlhiroaki@HMP:~/demo/s-rs$ vi main.go
:
func (s *SimpleRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { var response *http.Response var err error
str := "_人人人人人_\n> (´・_・`) <\n ̄Y^Y^Y^Y^Y ̄\n" dummyResponse := &http.Response{ StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBufferString(str)), } return dummyResponse, err
response, err = s.transport.RoundTrip(request) if err != nil { return nil, err } return response, err}
:
ダミーのHTTPレスポンスを生成して返してしまうだけのコードを追加
hiroaki@HMP:~/demo/s-rs$ cf push
:::::
requested state: startedinstances: 1/1usage: 256M x 1 instancesurls: simple-rs.mcf.nttlabs.infolast uploaded: Mon Aug 8 04:54:37 UTC 2016stack: unknownbuildpack: go_buildpack
state since cpu memory disk details#0 running 2016-08-08 01:55:10 PM 0.0% 3.2M of 256M 7.7M of 1Ghiroaki@HMP:~/demo/s-rs$ curl staticapp.mcf.nttlabs.info_人人人人人_> (´・_・`) <
 ̄Y^Y^Y^Y^Y ̄
コードを書き換えたので再デプロイ。HTTPサーバの中身が入れ替わるだけなので、Service登録のやり直し等は特に必要ありません。
Client LoadBalancer
CFRouter App
CF
RouteService
hiroaki@HMP:~/demo/s-rs$ cf unbind-route-service mcf.nttlabs.info simplerouteservice -n staticapp -fUnbinding route staticapp.mcf.nttlabs.info from service instance simplerouteservice in org ukaji_org / space ukaji_space as ukaji...OKhiroaki@HMP:~/demo/s-rs$ curl staticapp.mcf.nttlabs.info(´・_・`)hiroaki@HMP:~/demo/s-rs$ cf bind-route-service mcf.nttlabs.info simplerouteservice -n staticappBinding route staticapp.mcf.nttlabs.info to service instance simplerouteservice in org ukaji_org / space ukaji_space as ukaji...OKhiroaki@HMP:~/demo/s-rs$ curl staticapp.mcf.nttlabs.info_人人人人人_> (´・_・`) < ̄Y^Y^Y^Y^Y ̄
今度はRouteServiceのbind/unbind毎にHTTPレスポンスの差を視認できるはず。
A.落ちます。
こ の へ ん
RouteService無し 素通しRouteService
1000REQ/1par 124.11 34.04
1000REQ/5par 512.31 118.04
1000REQ/10par 793.65 174.64
1000REQ/20par 1377.21 207.25
VS
RouteService無し 素通しRouteService
ただし・・・
A.落ちます。ただし、
諸事情により採用したアーキテクチャがnetworkhop嵩む系
FullyBrokered StaAcBrokered UserProvided
今回はこれ
使う側が超楽◯
× 経路増加大
経路に無駄がない◯
× インフラ構成要変更
お手軽◯
× 経路増加大
公式RouteServiceArchitecture3パターン
まとめ
• CloudFoundryRouteService =CFアプリへのリクエストに処理を加えるService
– 実体はほぼただのHTTPサーバ– ソースコードは平易なので一読がおすすめ– 性能は当然落ちるので導入要否は各自検証
参考
RouteServices|CloudFoundryDocs
h[ps://github.com/cloudfoundry-samples/logging-route-service
h[ps://github.com/cloudfoundry-samples/ratelimit-serviceh[ps://docs.cloudfoundry.org/services/route-services.html
Github:cloudfoundry-samples/ratelimit-service
Github:cloudfoundry-samples/logging-route-service
h[p://www.slideshare.net/gwennetourneau/cloud-foundry-meetup-tokyo-1-route-service
CloudFoundryMeetupTokyo#1RouteService