Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mojoliciousではじめるマイクロサービスアーキテクチャ

 Mojoliciousではじめるマイクロサービスアーキテクチャ

モノリシックに配置されたアプリに限界を感じてませんか?
各アプリにいくつも出てくる共通ロジック。
「そろそろコピペではダメだ!」
そこで今話題の「マイクロサービス」化しよう!と思うわけです。
しかし、マイクロサービス自体、複雑なもので、私(たち)程度の
小規模では逆に導入障壁が高く、ミドルウェアにやられてしまう、
そう思われている事でしょう。
ここに、Perl Mojoliciousベースで小さくマイクロサービスを構築する方法があります!
サービスの規模に合わせて、マイクロサービス化やっていきましょう。
それと少しOkinawa.pmのご紹介。

Masaaki Saito

March 04, 2017
Tweet

Other Decks in Technology

Transcript

  1. w ͍͞ͱ͏·͖͋͞
 !NBTBLZTU w 0LJOBXBQNͷਓ w 8FCϋϧαʔΤϯδχΞ w ೶ۀɺ໦଄ݐங w

    ԭೄݝಡ୩ଜࡏॅ
 ʢ೔ຊҰਓޱͷଟ͍ଜʂʣ ࣗݾ঺հ
  2. ετʔϦʔ ͯ͞Ͳ͏෼ׂ͢Δ͔ʜ ෼ׂ̍ɿ൚༻తͳ"1*Λ෼͚Δ w ༣ศ൪߸ॅॴ"1*
 w ϝʔϧ഑৴"1* ֤ϑΥʔϜͰ%#͔ΒҾ͍͖ͯͯΔɻ,&/@"--͕ෳ਺͋Δɻ όοΫΤϯυͷ.5"Λ੾Γସ͑Մʹɻ
 QPTUpY

    KPCRVFVF.JOJPO $POP)B 4FOE(SJE "NB[PO4&4
 w νϟοτ௨஌"1* Τϥʔ௨஌ํ๏ɺϑΥʔϚοτ͕όϥόϥͩͬͨͷͰɺ౷Ұͯ͠)JQ$IBU΁ྲྀ͢
 ϏδωεϩδοΫʹӨڹ͕ͳ͍ҝ΍Γ΍͍͢
  3. 1 #!/usr/bin/env perl 2 use Mojolicious::Lite; 3 4 get '/'

    => sub { 5 my $c = shift; 6 $c->render(json => {yapc => 'kansai!!'}); 7 }; 8 9 app->start; % curl --dump-header - 'http://localhost:3000/' HTTP/1.1 200 OK Content-Length: 19 Date: Tue, 21 Feb 2017 01:54:25 GMT Content-Type: application/json;charset=UTF-8 Server: Mojolicious (Perl) {"yapc":"kansai!!"} .PKPMJDJPVTͱ͸ ϚΠΫϩϑϨʔϜϫʔΫͷ࠷খߏ੒
  4. helper mysql => sub { state $mysql = Mojo::mysql->new($CONFIG->{database}->{dsn}); };

    get '/:code' => [code => qr/(\d){7}/] => sub { my $c = shift; my $code = $c->param('code'); my $city = $c->mysql->db->query('SELECT * FROM ken_all WHERE code = ?', $code)->hash; unless ($city) { $city = {}; } $c->render(json => $city); }; ྫ͑͹ɺ༣ศ൪߸͔ΒॅॴΛݕࡧ͢Δ"1* % curl --dump-header - 'http://localhost:3000/9040322' HTTP/1.1 200 OK Server: Mojolicious (Perl) Content-Length: 183 Date: Tue, 21 Feb 2017 07:01:23 GMT Content-Type: application/json;charset=UTF-8 {"code":"9040322","kana1":"ΦΩφϫέϯ","kana2":"φΧΨϛάϯϤϛλϯιϯ","kana3":"φϛώϥ","name1":"ԭೄݝ ","name2":"த಄܊ಡ୩ଜ","name3":"೾ฏ"} "1*Λ࡞੒͢Δ
  5. use utf8; use t::Util; use Test::More; use Test::Mojo; use FindBin;

    require “$FindBin::Bin/../ken_all.pl"; my $t = Test::Mojo->new; $t->get_ok('/9040322') ->status_is(200) ->json_has('/9040322') ->json_is('/9040322/name1' => 'ԭೄݝ') ->json_is('/9040322/kana1' => 'ΦΩφϫέϯ'); +40/ͷςετ ok 1 - GET /9040322 ok 2 - 200 OK ok 3 - has value for JSON Pointer "/9040322" ok 4 - exact match for JSON Pointer "/9040322/name1" ok 5 - exact match for JSON Pointer "/9040322/kana1" 1..5 ok 3034 ms ( 0.00 usr 0.00 sys + 0.44 cusr 0.43 csys = 0.87 CPU) [12:44:50] All tests successful. "1*Λ࡞੒͢Δ { "9040322": { "kana1": "ΦΩφϫέϯ", "kana2": "φΧΨϛάϯϤϛλϯιϯ", "kana3": "φϛώϥ", "name1": "ԭೄݝ", "name2": "த಄܊ಡ୩ଜ", "name3": "೾ฏ" } }
  6. $c->delay( # ฒྻϦΫΤετ sub { my $delay = shift; $c->ua->get('127.0.0.1:5000/affiliate/list'

    => $delay->begin); $c->ua->get('127.0.0.1:5000/course/112345' => $delay->begin); $c->ua->get('127.0.0.1:5000/member/masakyst' => $delay->begin); }, # ݁ՌΛશͯड͚͔ͯΒϨϯμϦϯά sub { my ($delay, $affiliate, $course, $member) = @_; $c->stash({ affiliate => $affiliate->res->json, course => $course->res->json, member => $member->res->json, }); $c->render(template => 'index'); } ); ෳ਺ͷ"1*ݺͼग़͠Λฒྻʹ͢Δ "1*Λ࡞੒͢Δ ˞IZPOPUPBE NPSCPͰͳ͍ͱಈ͖·ͤΜ ˞಺෦"1*ݺͼग़͠ͷྫ
  7. ํ๏̍ɿ.PVOUʹΑΔ"1*ͷू໿ use Mojolicious::Lite; plugin Mount => {'/ken_all' => 'ken_all.pl' };

    plugin Mount => {'/keystone' => 'keystone.pl' }; plugin Mount => {'/mailsender' => 'mailsender.pl' }; plugin Mount => {'/alert' => 'alert.pl' }; plugin Mount => {'/store_info' => 'store_info.pl' }; plugin Mount => {'/members' => 'members.pl' }; app->start; "1*Λू໿͢Δ % carmel exec -- ./api-gateway.pl get -M GET /ken_all/9040322 (git)-[master] [Fri Feb 24 13:44:08 2017] [debug] Your secret passphrase needs to be changed [Fri Feb 24 13:44:08 2017] [debug] GET "/ken_all/9040322" [Fri Feb 24 13:44:08 2017] [debug] Routing to application "Mojolicious::Lite" [Fri Feb 24 13:44:08 2017] [debug] Routing to a callback [Fri Feb 24 13:44:08 2017] [debug] 200 OK (0.007251s, 137.912/s) {"code":"9040322","kana1":"ΦΩφϫέϯ","kana2":"φΧΨϛάϯϤϛλϯιϯ","kana3":"φϛώϥ","name1":"ԭೄݝ ","name2":"த಄܊ಡ୩ଜ","name3":"೾ฏ"} Ϛοϐϯά͍ͨ͠63-ɹɹϑΝΠϧ΁ͷύεʢ"1*͸ผʹ։ൃՄʣ BQJHBUFXBZQM
  8. "1*Λू໿͢Δ w ϝϦοτ w "1*ຖʹผͰ։ൃͯ͠౷߹Ͱ͖Δ w "1*ຖʹϓϩηεىಈ͍ͯ͠ͳ͍ͷͰɺݸʑͷ؂ࢹ෮چ͕ෆཁ w σϝϦοτ w

    ̍ͭͷΞϓϦʹͳΔͷͰɺϓϩηε͕େ͖͘ͳΔ w ಉ͡Ϟδϡʔϧͷผόʔδϣϯ͕࢖༻Ͱ͖ͳ͍ ํ๏̍ɿ.PVOUʹΑΔ"1*ͷू໿ ˞Ұ൪ͷσϝϦοτͰ͕͢ɺখن໛ͩͱͦΜͳʹ໰୊ʹͳΒͳ͍Ͱ͢
  9. "1*Λू໿͢Δ ํ๏̎ɿ1MBDL"QQ1SPYZʹΑΔ"1*ͷू໿ 14(MԽ use Plack::Builder; use Plack::App::Proxy; sub proxy {

    Plack::App::Proxy->new(remote => $_[0])->to_app; } builder { enable 'Proxy::RewriteLocation'; mount "/ken_all" => proxy(‘http://127.0.0.1:8810'); mount "/keystone" => proxy(‘http://127.0.0.1:8820'); mount "/members" => proxy(‘http://127.0.0.1:8860'); # ... }; όοΫΤϯυͷ"1*ϓϩηε΁3FWFSTF1SPYZ͢Δ
  10. % cat Procfile API Gateway: plackup -a api-gateway.psgi ken all

    API : morbo ./ken_all.pl -l http://*:8810 keystone API : morbo ./keystone.pl -l http://*:8820 members API : morbo ./members.pl -l http://*:8830 "1*Λू໿͢Δ ํ๏̎ɿ1MBDL"QQ1SPYZʹΑΔ"1*ͷू໿ 1SPDMFUʹΑΔ"1*ىಈͷ؅ཧ 14:38:21 keystone API .1 | Server available at http://127.0.0.1:8820 14:38:21 mailsender API .1 | Server available at http://127.0.0.1:8830 14:38:21 ken all API .1 | Server available at http://127.0.0.1:8810 14:38:21 members API .1 | Server available at http://127.0.0.1:8850 14:38:21 store service API .1 | Server available at http://127.0.0.1:8860 14:38:21 hipchat API .1 | Server available at http://127.0.0.1:8840 14:38:21 API Gateway.1 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ w ෳ਺ͷ"1*ϓϩηεͷҰ੪ىಈ w "1*ϓϩηεͷࢮ׆؂ࢹɺࣗಈ෮چ 'JMF3PUBUF-PHTͰϩά΋ू໿Մೳ
  11. "1*Λू໿͢Δ w ϝϦοτ w "1*ຖʹɺ׬શʹಠཱͨ͠ϓϩηεϞδϡʔϧͰ։ൃͰ͖Δ w ̍୆ͷαʔό͔Β࢝Ίͯɺ෦෼తʹผαʔόʹ෼ࢄ͠΍͍͢ w 1SPDMFUͷ͓͔͛Ͱɺ"1*ϓϩηεىಈ෮چͷख͕ؒͳ͘ͳΔ w

    1SPDMFU͸ɺຊ൪؀ڥͰ΋࢖͏ͱศར w σϝϦοτ w 1FSMʹΑΔ3FWFSTF1SPYZͳͷͰ/HJOYͳͲͱൺ΂ͯগ͠஗͍ ํ๏̎ɿ1MBDL"QQ1SPYZʹΑΔ"1*ͷू໿ "1*ήʔτ΢ΣΠ͸5XJHHZ͕͍͍Ͱ͢
  12. ηΩϡϦςΟೝূ w *%ύεϫʔυೝূ w 5PLFOೝূʢ"DDFTT5PLFOΛൃߦʣ w γϯϓϧͳ΋ͷΛࣗ࡞Ͱ΋͍͍ w 0"VUI w

    +40/8FC5PLFO "1*ݺͼग़͠ʹೝূΛ͔͚͍ͨ .PKPMJDJPVT1MVHJO0"VUI .PKPMJDJPVT1MVHJO0"VUI4FSWFS
  13. ηΩϡϦςΟೝূ w +40/8FC5PLFO +85 δϣοτͱಡΉ w ॺ໊ͷग़དྷΔ+40/ΛؚΜͩ63-4BGFͳτʔΫϯ w ҉߸ԽͰ͸ͳ͍ͷͰɺ+40/ͷத਎͸ݟΒΕΔ w

    Ͱ͕͢ɺվ͟Μ͢Δͱݕূʹࣦഊ͢ΔͷͰجຊతʹ҆શ w %#ͳͲͰͷτʔΫϯ؅ཧ͕ʢγϯϓϧͳ৔߹ʣෆཁ +40/8FC5PLFOʹΑΔೝূ IUUQTKXUJP
  14. use Crypt::JWT qw(encode_jwt decode_jwt); 
 $router->add("/authorize", sub { my ($req,

    $env) = @_; my $id = $req->body_parameters->{login_id}; my $pass = $req->body_parameters->{password}; ## DB઀ଓͯ͠ೝূ my $user = $db->query('SELECT * FROM users WHERE login_id = ? AND password = ?', $id, $pass)->hash; unless ($user) { die; } # JSON TokenԽ͢Δσʔλߏ଄Λ࡞੒ my $payload = { iss => ‘υϝΠϯ’, # ൃߦݩURL sub => $user->{app_id}, # Ϣʔβࣝผࢠ / ΞϓϦID iat => time, # τʔΫϯੜ੒࣌ࠁ exp => time + (60 * 60), # ༗ޮظݶ }; # ൿີ伴ͰJSONΛॺ໊ my $jws_token = encode_jwt(payload => $payload, alg => 'HS256', key => 'ൿີʂ'); return ["200", ["Content-Type" => "application/json"], [encode_json({token => $jws_token})]]; }); +85ʹΑΔೝূαʔό ηΩϡϦςΟೝূ
  15. enable sub { my $app = shift; my $unauthorized =

    sub { my $body = 'Authorization required jwt'; return [401, ['Content-Type' => 'text/plain', 'Content-Length' => length $body, 'WWW-Authenticate' => 'Bearer realm="invalid_token"'], [ $body ] ]; }; my $authenticator = sub { my $token = shift; my $data = decode_jwt(token => $token, key => 'ൿີ'); # ͜ΕͰtokenͷjsonσʔλ͕෮ݩͰ͖·ͨ͠ʂ }; sub { my $env = shift; if ($env->{PATH_INFO} =~ /^\/authorize$/) { return $app->($env); } my $token = $env->{HTTP_AUTHORIZATION} or return $unauthorized->(); if ($token =~ /^Bearer (.*)$/i) { if ($authenticator->($1, $env)) { return $app->($env); } } return $unauthorized->(); }; }; ηΩϡϦςΟೝূ +85ʹΑΔೝূϓϩΩγ
  16. ηΩϡϦςΟೝূ w .PKPMJDJPVT1MVHJO"$.& w "$.&ϓϩτίϧ %7ূ໌ൃߦͷࣗಈԽ  w 5XJHHZ5-4 w

    5XJHHZʹ5-4TVQQPSU ଞɺศརͳ44-ؔ܎ͷπʔϧͷ঺հ ೝূہ $" -FU`T&ODSZQU
  17. ηΩϡϦςΟೝূ .PKPMJDJPVT1MVHJO"$.& #!/usr/bin/env perl use Mojolicious::Lite; plugin 'ACME'; get '/'

    => {text => 'I ♥ YAPC!'}; app->start; $ sudo ./myapp.pl daemon -l http://*:80 [Tue Feb 28 11:34:06 2017] [info] Listening at "http://*:80" Server available at http://127.0.0.1:80 $ ./myapp.pl acme account register [Tue Feb 28 11:13:32 2017] [debug] Your secret passphrase needs to be changed Account Created Writing account.key $ ./myapp.pl acme cert generate υϝΠϯ [Tue Feb 28 11:21:03 2017] [debug] Your secret passphrase needs to be changed Writing myapp.key Writing myapp.crt requires 'Mojolicious'; requires 'Mojolicious::Plugin::ACME'; requires 'IO::Socket::SSL'; DQBOpMF NZBQQQM 5FSNJOBM
  18. ηΩϡϦςΟೝূ 5XJHHZ5-4 #!/usr/bin/env perl use Mojolicious::Lite; # plugin 'ACME'; get

    '/' => {text => 'I ♥ YAPC!'}; app->start; $ plackup -a ./myapp.pl psgi -s Twiggy::TLS --tls-key myapp.key --tls-cert myapp.crt Twiggy: Accepting connections at https://0.0.0.0:5000/ requires 'Plack'; requires 'Twiggy::TLS'; DQBOpMF NZBQQQM 5FSNJOBM $ curl --dump-header - https://api.υϝΠϯ.com:5000/ HTTP/1.0 200 OK Content-Type: text/html;charset=UTF-8 Date: Tue, 28 Feb 2017 03:05:49 GMT Content-Length: 11 I ♥ YAPC!
  19. ·ͱΊ w "1*࡞੒͸ɺ.PKPMJDJPVTͰॻ͘ͱָʂ w "1*ήʔ΢ΣΠ΋1FSMͰॻ͘ͱָʂ w ϓϩηε؅ཧ΋1FSM 1SPDMFU Ͱָʂ w

    ೝূ44-ؔ܎΋1FSMͰָʂ શ෦1FSMͷΈͰͰ͖·͢ʂ ϛυϧ΢ΣΞʹ΍ΒΕͳ͍ʂʂ