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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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

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

Avatar for Masaaki Saito

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ͷΈͰͰ͖·͢ʂ ϛυϧ΢ΣΞʹ΍ΒΕͳ͍ʂʂ