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

mixi tech note #07

mixi tech note #07

#技術書典12 に出典されたミクシィグループエンジニア有志による技術書です。

<< 目次 >>
1章:Elm + Haskell で作る Web アプリ with Bazel
2章:ガチャシステムで見る、マイクロサービスと整合性の話
3章:Cloud Build を使い倒そう!
4章:TIPSTAR の開発で学んだ Cloud Spanner Tips
5章:エンジニアリングマネジャー半年くらいやってみた
6章:Unleash を使った feature toggle 管理

<< TECH NOTE 一覧 >>
mixi tech note #01
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-01

mixi tech note #02
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-02

mixi tech note #03
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-03

mixi tech note #04
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-04

mixi tech note #05
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-05

mixi tech note #06
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-06

mixi tech note #07
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-07

MIXI TECH NOTE #08
https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-08

XFLAG Tech Note Vol.01
https://speakerdeck.com/mixi_engineers/xflag-tech-note-vol-dot-01

XFLAG Tech Note vol.02
https://speakerdeck.com/mixi_engineers/xflag-tech-note-vol-dot-02

Avatar for MIXI ENGINEERS

MIXI ENGINEERS

February 09, 2022
Tweet

More Decks by MIXI ENGINEERS

Other Decks in Technology

Transcript

  1. ·͕͖͑ ຊॻʮmixi tech note #07ʯ͸ɺϛΫγΟάϧʔϓʹॴଐ͢Δ༗ࢤୡʹΑͬͯࣥචɾ੍࡞͞Εͨ ٕज़ॻͰ͢ɻ࣮ࡍͷϓϩμΫτ։ൃͷݱ৔Ͱ࢖༻͞Εٕͨज़ͷ࿩΍ɺ೔ʑͷۀ຿ͷ๣Βɺݸਓతʹௐ ΂ͨ͜ͱͳͲɺࢥ͍ࢥ͍ʹࣥච͍ͯ͠·͢ɻͦͷͨΊɺ֤ষͦΕͧΕͰ׬͍݁ͯ͠Δ಺༰ʹͳ͍ͬͯ ·͢ͷͰɺ޷͖ͳষ͔Β޷͖ͳॱ൪Ͱָ͓͠Έ͍ͩ͘͞ɻ ·ͨɺຊॻ͸ɺϛΫγΟάϧʔϓʹ͋Δٕज़త஌ݟ΍ΞΠσΞΛੵۃతʹڞ༗ɾެ։͍ͯ͘͜͠ͱ ͰɺੈͷதʹΑΓྑ͍αʔϏε͕ҲΕग़͢͜ͱΛئͬͯץߦ͞Ε͍ͯ·͢ɻܝࡌ͞Ε͍ͯΔ৘ใ͸ɺ

    ࣥචऀࣗ਎ͷ؀ڥͰݕূࣥ͠ච͞Εͨ΋ͷͰ͢ͷͰɺ͝ࢀߟʹ͞ΕΔࡍ͸ɺࣗ͝਎ͷ൑அͱ੹೚ʹ͓ ͍ͯ͝׆༻͍ͩ͘͞ɻͳ͓ɺจষදݱʹ͖ͭ·ͯ͠΋ɺࣥචऀࣗ਎ͷݴ༿Ͱ఻͑ͨ͘ɺϑϥϯΫͳද ݱͱͳ͓ͬͯΓ·͢͜ͱ͝ཧղ͍͚ͨͩΕ͹ͱࢥ͍·͢ɻ גࣜձࣾϛΫγΟ CTO ࣨ DevRel άϧʔϓҰಉ ˗ຊॻʹؔ͢Δ͓໰͍߹Θͤઌ ɹ https://twitter.com/mixi_engineers ˗ϛΫγΟάϧʔϓʹ͍ͭͯ ɹ https://mixi.co.jp/ ˞ʠϛΫγΟʡ ɺ ʠmixiʡ ɺ ʠmixi ϩΰʡ ͸ɺגࣜձࣾϛΫγΟͷ঎ඪ·ͨ͸ొ࿥঎ඪͰ͢ɻ·ͨɺ֤ ࣾͷձ໊ࣾɺαʔϏεٴͼ੡඼ͷ໊শ͸ɺͦΕͧΕͷॴ༗͢Δ঎ඪ·ͨ͸ొ࿥঎ඪͰ͢ɻ iii
  2. ໨࣍ ·͕͖͑ iii ୈ 1 ষ Elm + Haskell Ͱ࡞Δ

    Web ΞϓϦ with Bazel 1 1.1 ୊ࡐɿऩೖάϥϑ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 ओཁٕज़ͷ঺հ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Elm + Haskell + Bazel Ͱ࡞Δ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.4 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 17 2.1 ઃఆ؀ڥͱΨνϟϑϩʔͷఆٛ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.2 γʔέϯεਤͰߟ͑ͯΈΔ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.3 3 ͭͷ੔߹Խઓུ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.4 ϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.5 ͓ΘΓ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 29 3.1 ͸͡Ίʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.2 Cloud Build Deep Dive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏ . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏ . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.5 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 65 4.1 TIPSTAR ͷ։ൃͱ Google Cloud Spanner . . . . . . . . . . . . . . . . . . . . . 65 4.2 Cloud Spanner ώϯτू . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.4 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 77 5.1 ͸͡Ίʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 5.2 ϛΫγΟʹ͓͚ΔϚωδϟʔɾEM . . . . . . . . . . . . . . . . . . . . . . . . . . 77 v
  3. ໨࣍ 5.3 γεςϜ 2G ͱ͸ . . . . .

    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 5.4 EM ͱͯ͠औΓ૊Μͩ͜ͱ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 5.5 ͜Ε͔Β΍Γ͍ͨ͜ͱ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.6 ·ͱΊ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 85 6.1 τϥϯΫϕʔε։ൃ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 6.2 Unleash ͱ͸ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 6.3 Unleash ͷಋೖ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 6.4 ܕ҆શʹ feature toggle Λ؅ཧ͢Δ . . . . . . . . . . . . . . . . . . . . . . . . . . 89 6.5 ϩʔΧϧͷΈͰҰ࣌తʹ feature toggle Λ੾Γସ͑Δ . . . . . . . . . . . . . . . . 91 6.6 ऴΘΓʹ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 ஶऀ঺հ 93 vi
  4. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel ද୊ͷ௨ΓɺElm ͱ Haskell Ͱ Web ΞϓϦέʔγϣϯ࡞੒͠·͢ɻաڈʹʮmixi tech note #02*1ʯ΁ʮElm + Haskell Ͱ࡞Δ Web ΞϓϦ (2019 ೥౓ฤ)ʯͱ͍͏ͷΛدߘ͠·ͨ͠ɻաڈͷ ΋ͷͱͷҧ͍͸ҎԼͷ௨ΓͰɺ಺༰ͱͯ͠͸΄΅ผ෺Ͱ͢ɻ • Ϗϧυπʔϧͱͯ͠ Bazel Λಋೖ͠য఺Λ౰ͯ·͢ • GHC ͷόʔδϣϯΛݱࡏ࠷৽ͷ 9.2.1 ʹ͠·͢ʢաڈͷ͸ 8.6.5ʣ • Elm ʹΑΔΞϓϦέʔγϣϯ։ൃʹ͍ͭͯ͸େ෯ʹׂѪ͠·͢ 1.1 ୊ࡐɿऩೖάϥϑ ࣗ਎ͷऩೖΛ࣍ͷΑ͏ʹάϥϑԽͯ͠දࣔͰ͖ΔΑ͏ͳ௒؆қతͳ Web ΞϓϦΛ୊ࡐͱ͠·͢*2ɻ *1 https://techbookfest.org/product/5256674994552832 *2 ྫͱͯ͠ࡌͤͨάϥϑͷऩೖ͸චऀͷ΋ͷͰ͸ͳ͘ద౰ʹͰ্ͬͪ͛ͨσʔλͰ͢ɻ 1
  5. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.2 ओཁٕज़ͷ঺հ σʔλ͸ JSON ͰϩʔΧϧʹอଘͯ͋͠Δͱ͠·͢ɻElm Ͱॻ͍ͨ Web ϑϩϯτϖʔδ͸ɺ όο ΫΤϯυ͔Β API Λհͯ͠σʔλΛड͚औΓάϥϑ΁ඳࣸ͠·͢ɻϑϩϯτଆ͔ΒೖྗΛड͚ͳ͍ ʢPOST ͢ΔΑ͏ͳ API ͸ଘࡏ͠ͳ͍ʣͨΊɺWeb ΞϓϦͱ͓ͯ͠΋͠ΖΈʹ͚ܽ·͕͢ɺࠓճͷ ओ୊͸Ϗϧυπʔϧͷ Bazel ͳͷͰ͝༰͍ࣻͩ͘͞ɻιʔείʔυ͸ matsubara0507/salary-graph ͱ͍͏ϦϙδτϦ໊Ͱ GitHub ʹެ։͓͖ͯ͠·ͨ͠*3ɻ 1.2 ओཁٕज़ͷ঺հ ຊߘͷओཁٕज़ͱͯ͠ɺόοΫΤϯυΛهड़͢ΔϓϩάϥϛϯάݴޠʮHaskellʯͱϑϩϯτΤϯ υΛهड़͢ΔϓϩάϥϛϯάݴޠʮElmʯ ɺͦͯ͜͠ΕΒΛϏϧυ͢ΔͨΊͷϏϧυπʔϧʮBazelʯ ͕͋Γ·͢ɻ͜ΕΒΛ؆୯ʹͰ͕͢঺հ͠·͢ɻ Haskell Haskell ͸࣍ͷΑ͏ͳಛ௃Λ࣋ͭϓϩάϥϛϯάݴޠͰ͢ɻ • ڧྗͳ੩తܕ෇͚ʢܕϨϕϧϓϩάϥϛϯάͳͲ΋Մೳʣ • ڧྗͳϝλϓϩάϥϛϯάʢTemplate Haskell ͳͲʣ • ஗ԆධՁʢࣜ͸ར༻͢ΔλΠϛϯά·ͰධՁ͞Εͳ͍ʣ • ڧྗͳฒߦॲཧʢܰྔεϨου΍ιϑτ΢ΣΞτϥϯβΫγϣϯϝϞϦͳͲʣ ·ͨɺͦͷσϑΝΫτελϯμʔυͳॲཧܥͱͯ͠ GHCʢGlasgow Haskell Compilerʣͱ͍͏ͷ Λ࢖͍·͢ɻࠓճར༻͢Δ GHC 9.2.1 Ͱ͸࣍ͷΑ͏ͳػೳ͕௥Ճ͞Ε·ͨ͠*4ɻ • ϨίʔυܕͷϑΟʔϧυΞΫηεʹ . ه๏͕࢖͑ΔΑ͏ʹͳͬͨ • ແ֐ͰރΕͨ GHC ֦ுΛ·ͱΊͨ GHC2021 ͱ͍͏ݴޠ֦ு͕௥Ճ͞Εͨ • ܕϨϕϧจࣈ΍ܕϨϕϧൺֱԋࢉࢠͷ௥Ճ • ͳͲͳͲ ಛʹɺ 1 ͭ໨ͷػೳ͸ Haskeller ʹͱͬͯ଴ͪʹ଴ͬͨͱ͍͏Α͏ͳػೳͰ͢ɻHaskeller ͱϨίʔ υܕͷྺ࢙ʹ͍ͭͯ͸ ʮHaskell Day 2021ʯ ͷ ʮHaskell ͸ผݴޠʹͳΓ·ͨ͠ʕʕRecordDotSyntax ͱ NoFieldSelectorsʯͱ͍͏ൃද͕࠷΋ৄ͍͠ͷͰɺڵຯ͕͋Δํ͸ൃදಈըΛࢼௌͯ͠Έͯͩ͘͞ ͍*5ɻ *3 https://github.com/matsubara0507/salary-graph *4 GHC9.2.1 ʹ͍ͭͯ͸ɺͩΊΆࢯͷʮGHC 9.2 ͷ৽ػೳͱɺGHC ͷಈ޲ 2021ʯ͕ͱͯ΋ࢀߟʹͳΓ·͢ɻhttps: //zenn.dev/mod_poppo/articles/ghc-9-2-and-future *5 https://haskell.jp/haskell-day-2021/#timetable-fumieval 2
  6. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ Elm Elm ͸ JavaScript ΁τϥϯεύΠϧ͞ΕΔϓϩάϥϛϯάݴޠͰɺ࣍ͷΑ͏ͳಛ௃Λ࣋ͪ·͢*6ɻ • The Elm Architecture ͱ͍͏ಠࣗͷύλʔϯΛ࣮૷͢ΔͨΊͷݴޠ • ΠϯλϑΣʔε΍ܕΫϥεͷΑ͏ͳΞυϗοΫଟ૬ͷͨΊͷػೳ͸ͳ͍ • ύϥϝτϦοΫଟ૬ʢ͍ΘΏΔδΣωϦΫεʣ͸͋Δ • Haskell Ͱ࣮૷͞Ε͍ͯΔ Haskell ʹΠϯεύΠΞ͞Ε͔ͯɺͱͯ΋͍ۙߏจΛ࣋ͪ·͕͢ɺHaskell ͷΑ͏ͳଟछଟ༷ͳݴ ޠػೳ͸͋Γ·ͤΜɻThe Elm Architecture ͱ͍͏ύλʔϯͰ Single Page Application Λ࡞Δͨ Ί͚ͩʹಛԽͨ͠ϓϩάϥϛϯάݴޠͰ͢ɻ༨ஊͰ͕͢ɺHaskell ๊͕͍͑ͯͨʮϨίʔυܕ໰୊ʯ ͸ͳ͍Α͏ʹσβΠϯ͞Ε͍ͯ·͢ɻ Bazel Bazel ͸࣍ͷΑ͏ͳಛ௃Λ࣋ͭϏϧυπʔϧͰ͢*7ɻ • ϏϧυΛಠࣗͷαϯυϘοΫε؀ڥͷதͰߦ͏ • ґଘؔ܎Λ໌ه͢ΔͨΊΩϟογϡޮ཰͕ྑ͍ • Starlark ͱ͍͏ Python αϒηοτͳݴޠͰ֦ுՄೳ ·ͨɺ͍ΘΏΔϥΠϒϥϦͷΑ͏ͳ΋ͷΛ࢖͏͜ͱͰɺ؆୯ʹ͞·͟·ͳϓϩάϥϜͷϏϧυ΍ς ετͷ࣮ߦΛಉ͡ΠϯλϑΣʔεͰߦ͏͜ͱ͕Ͱ͖·͢ɻϥΠϒϥϦʹ͸ɺͨͱ͑͹࣍ͷΑ͏ͳ΋ͷ ͕͋Γ·͢ɻ • bazelbuild/rules_docker : Docker ΠϝʔδͷϏϧυ΍ϓογϡͳͲ*8 • tweag/rules_haskell : Haskell ϓϩάϥϜͷϏϧυ΍ςετͳͲ*9 • matsubara0507/rules_elm : Elm ϓϩάϥϜͷϏϧυ΍ςετͳͲʢࣗ࡞ʣ *10 αʔυύʔςΟ੡΋ؚΊͯ͞·͟·ͳϥΠϒϥϦʢϧʔϧʣ͕͋Γ·͢ɻ 1.3 Elm + Haskell + Bazel Ͱ࡞Δ ͍Α͍Αຊ୊Ͱ͢ɻ·ͣ͸ɺΞϓϦέʔγϣϯΛ Bazel ͰϏϧυ͢ΔͨΊͷ४උΛ͠·͢ɻ *6 https://elm-lang.org/ *7 https://bazel.build/ *8 https://github.com/bazelbuild/rules_docker *9 https://github.com/tweag/rules_haskell *10 https://github.com/matsubara0507/rules_elm 3
  7. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ Bazel ͷ४උ Bazel Ͱ͸ओʹ WORKSPACE ϑΝΠϧͱ BUILD ϑΝΠϧͱ͍͏ 2 छྨͷϑΝΠϧΛར༻͠ ·͢ɻWORKSPACE ϑΝΠϧ͸ɺϓϩδΣΫτຖʹҰͭ༻ҙ͢ΔϑΝΠϧͰϓϩδΣΫτͷϧʔ τʹஔ͖·͢ɻWORKSPACE ϑΝΠϧʹ͸ɺBazel ͷґଘϥΠϒϥϦʢϧʔϧʣΛهड़ͨ͠Γɺ Bazel ಺Ͱར༻͢ΔπʔϧʢGHC ͳͲʣͷઃఆΛهड़ͨ͠Γ͠·͢ɻ·ͣ͸ɺHaskell ΛϏϧυ͢ ΔͨΊʹ rules_haskell Λར༻͢ΔͷͰɺWORKSPACE ϑΝΠϧʹͦͷ͜ͱΛهड़͠·͢ɻ workspace(name = "salary-graph") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_haskell", strip_prefix = "rules_haskell-0.14", url = "https://github.com/tweag/rules_haskell/archive/refs/tags/v0.14.tar.gz", sha256 = "851e16edc7c33b977649d66f2f587071dde178a6e5bcfeca5fe9ebbe81924334", ) load("@rules_haskell//haskell:repositories.bzl", "rules_haskell_dependencies") rules_haskell_dependencies() load("@rules_haskell//haskell:toolchain.bzl", "rules_haskell_toolchains") rules_haskell_toolchains(version = "9.2.1") GHC 9.2.1 Λ࢖͏ʹ͸ɺrules_haskell ͷ v0.14 Ҏ߱Λ࢖͏ඞཁ͕͋Δ఺ʹ஫ҙ͍ͯͩ͘͠͞ɻ BUILD ϑΝΠϧ͸ϧʔϧʹΑΓ·͕͢ɺಠཱͨ͠σΟϨΫτϦຖʹஔ͖·͢ɻࠓճͷ৔߹͸ɺϓϩ δΣΫτϧʔτʹҰͭ͋Ε͹े෼Ͱ͢ɻ Haskell Λ Bazel ͰϏϧυ લड़ͨ͠௨Γɺ Haskell ϓϩάϥϜΛ Bazel ͰϏϧυ͢Δʹ͸ tweag/rules_haskell Λ࢖͏ͱྑ͍Ͱ ͠ΐ͏ɻrules_haskell Ͱ͸ɺ Haskell ϓϩδΣΫτ͕ґଘ͢Δ Haskell ύοέʔδΛ WORKSPACE ʹྻڍ͢Δඞཁ͕͋Γ·͢ɻΑΓݫີʹ͸ɺґଘύοέʔδ͕ґଘ͢Δύοέʔδ·Ͱ haskell_ca bal_library Λར༻ͯ͠ྻڍ͢Δඞཁ͕͋Γ·͕͢ɺ͜ΕΒΛେ෯ʹ؆ུԽ͢ΔͨΊͷϧʔϧͱ͠ ͯ stack_snapshot ͱ͍͏ͷ͕͋ΔͷͰɺࠓճ͸͜ΕΛ࢖͍·͢ɻ load("@rules_haskell//haskell:cabal.bzl", "stack_snapshot") 4
  8. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ stack_snapshot( name = "stackage", packages = [ "aeson", "base", "bytestring", ... ], snapshot = "lts-18.21", ) stack ͱ͍͏ͷ͸ Haskell ͷϏϧυπʔϧ stack ͷ͜ͱΛࢦ͍ͯ͠·͢*11ɻstack ʹ͸ʮඞͣϏ ϧυͰ͖Δύοέʔδͷόʔδϣϯͷ૊Έ߹Θͤηοτʢsnapshotʣ ʯ͕ Stackage ͱ͍͏αΠτ Ͱ؅ཧ͞Ε͓ͯΓ*12ɺ͜ΕΛར༻͍ͯ͠ΔͷͰ͢ɻ͜͜Ͱ໰୊͕Ұͭ͋Γ·͢ɻlts-18.21 ͱ͍͏ snapshot ͸ GHC 8.10.7 ༻ͷ΋ͷͷͨΊɺࠓճ࢖͍͍ͨ GHC 9.2.1 Ͱ͸ಈ࡞͠·ͤΜɻ࢒೦ͳ͕ Βɺຊߘࣥච࣌ʹ͸ GHC 9.2.1 ༻ͷ snapshot ͸·ͩͳ͍ͨΊɺࣗ࡞͢Δඞཁ͕͋Γ·͢ɻͦΕΛ stack-snapshot.yaml ͱ͍͏ϑΝΠϧ໊Ͱอଘ͠ɺstack_snapshot Λ࣍ͷΑ͏ʹॻ͖׵͑·͢ɻ stack_snapshot( name = "stackage", packages = [ ... ], local_snapshot = "//:stack-snapshot.yaml", ) ༨ஊͰ͕͢ɺStackage ʹ͋Δ snapshot ʹ͸ͳ͍ύοέʔδΛར༻͍ͨ͠৔߹΋ɺಉ༷ͷํ๏Λͱ Δඞཁ͕͋Γ·͢ɻͰ͸࣍ʹɺBUILD ϑΝΠϧଆΛهड़͠·͢ɻࠓճ͸࣍ͷΑ͏ͳσΟϨΫτϦߏ ଄Λ࣋ͭϓϩδΣΫτΛ૝ఆ͍ͯ͠·͢ɻ / |- app | |- server | |- Main.hs : Haskell Ͱͷ Main ίʔυ |- src | |- ͦͷ΄͔ Haskell ιʔείʔυ |- BUILD.bazel |- stack-snapshot.yaml |- WORKSPACE *11 https://docs.haskellstack.org/ *12 https://www.stackage.org/ 5
  9. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ rules_haskell Ͱ͸ɺMain ίʔυΛϏϧυͯ͠όΠφϦΛੜ੒͢ΔͨΊͷ haskell_binary ϧʔ ϧͱʮͦͷ΄͔ Haskell ιʔείʔυʯΛύοέʔδͱͯ͠Ϗϧυ͢ΔͨΊͷ haskell_library ϧʔϧ͕͋Γ·͢ɻ load("@rules_haskell//haskell:defs.bzl", "haskell_library", "haskell_binary") haskell_library( name = "salary-graph-library", src_strip_prefix = "src", srcs = glob(["src/**/*.hs"]), deps = [ "@stackage//:aeson", "@stackage//:base", "@stackage//:bytestring", ... ], ) haskell_binary( name = "salary-graph", srcs = glob(["app/server/**/*.hs"]), deps = [ ":salary-graph-library", "@stackage//:base", "@stackage//:servant-server", "@stackage//:warp", ], ) ͜ΕͰɺbazel build //:salary-graph Ͱ Haskell ϓϩάϥϜͷϏϧυ͕Ͱ͖ΔΑ͏ʹͳΓ· ͨ͠ɻ ίϯύΠϧϑϥά Haskell ϓϩάϥϜΛϏϧυ͢Δͱ͖ʹ͸ίϯύΠϧϑϥάΛ౉͢͜ͱ͕ଟ͍Ͱ͢ɻrules_haskell ͷ৔߹͸ compiler_flags Ҿ਺Ͱࢦఆ͠·͢ɻ·ͨɺຖճྻڍ͢Δͷ͸͍ͨ΁ΜͰ͢ͷͰɺม਺ʹ ͓ͯ͘͠ͱศརͰ͢ɻ GHC_FLAGS = [ "-Wall", "-Wcompat", "-optP-Wno-nonportable-include-path", "-DBAZEL_BUILD=1", "-XGHC2021", "-XDataKinds", "-XNoFieldSelectors", 6
  10. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ "-XOverloadedRecordDot", "-XOverloadedStrings", ] haskell_library( name = "salary-graph-library", src_strip_prefix = "src", srcs = glob(["src/**/*.hs"]), deps = [ ... ], compiler_flags = GHC_FLAGS, ) haskell_binary( name = "salary-graph", srcs = glob(["app/server/**/*.hs"]), deps = [ ... ], compiler_flags = GHC_FLAGS, ) ύοέʔδόʔδϣϯΛ౉͢ CLI πʔϧΛ࡞Δࡍʹɺ--version ΦϓγϣϯͰόʔδϣϯΛද͍ࣔͨ͠৔߹͕͋Γ·͢ΑͶɻ Haskell ͷ΄͔ͷϏϧυπʔϧͷ৔߹ɺPaths Ϟδϡʔϧͱ͍͏΋ͷ͕࡞ΒΕͯɺઃఆϑΝΠϧʹ͋ ΔόʔδϣϯΛࢀরͰ͖·͢ɻ {-# LANGUAGE DuplicateRecordFields, MultiWayIf #-} module Main where import Paths_salary_graph (version) import Data.Version qualified as Version import System.Console.GetOpt import System.Environment (getArgs) main :: IO () main = do opts <- compilerOpts usage =<< getArgs if | opts.help -> putStrLn usage | opts.version -> putStrLn $ Version.showVersion version | otherwise -> runServer opts where usage = usageInfo "Usage: salary-graph [OPTION...]" options 7
  11. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ runServer :: Options -> IO () runServer opts = ... -- ׂѪ data Options = Options { help :: Bool , version :: Bool } -- GetOpt ϞδϡʔϧʹΑΔίϚϯυϥΠϯҾ਺ͷύʔε͸ׂѪ Paths_salary_graph ͱ͍͏ͷ͕ Paths ϞδϡʔϧͰ͢ɻ͔͠͠ɺ rules_haskell ͷ৔߹͸ Paths Ϟδϡʔϧ͕ੜ੒͞Ε·ͤΜɻͰ͢ͷͰɺStarlark Λॻ͍ͯಠࣗϧʔϧΛهड़ͪ͠Ό͍·͢ɻ௕͘ ͳΔͷͰಠࣗϧʔϧࣗମ͸ׂѪ͠·͢ʢϦϙδτϦͷ build/rules/haskell ഑Լʹ͋ΔͷͰڵຯ ͷ͋Δਓ͸ࢀরͯ͠Έ͍ͯͩ͘͞ʣ ɻఆٛͨ͠ಠࣗϧʔϧ paths_module Λ࢖ͬͯ Main ίʔυʹ Paths ϞδϡʔϧΛ౉ͯ͠Έ·͢ɻ load("//build/rules/haskell:def.bzl", "paths_module") paths_module( name = "paths_module", package = "salary-graph", version = "1.0.0", ) haskell_binary( name = "salary-graph", srcs = glob(["app/server/**/*.hs"]), deps = [ ":salary-graph-library", ":paths_module", # ௥Ճ "@stackage//:base", "@stackage//:servant-server", "@stackage//:warp", ], compiler_flags = GHC_FLAGS, ) ϑΝΠϧͷຒΊࠐΈ ৄࡉ͸ׂѪ͠·͕͢ɺࠓճ͸ Servant ͱ͍͏ύοέʔδΛར༻ͯ͠ Web όοΫΤϯυΛهड़ͯ͠ ͍·͢*13ɻServant ͸ API ఆٛΛܕͱͯ͠هड़Ͱ͖Δಛ௃Λ͍࣋ͬͯ·͢ɻ *13 https://docs.servant.dev/ 8
  12. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ type API = Get ’[HTML] ByteString :<|> "api" :> SalaryAPI type SalaryAPI = "salaries" :> Capture "year" Year :> Get ’[JSON] [Salary] :<|> "appointments" :> Capture "year" Year :> Get ’[JSON] [Salary.Appointment] Get ’[HTML] ByteString ͷ෦෼͸ϧʔτͰ index.html ʹ૬౰͢Δ΋ͷΛฦ͢ͱ͍͏ͷදݱ͠ ͍ͯΔܕͰ͢ɻࠓճ͸ɺ͜ͷ index.html ΛίϯύΠϧ࣌ʹຒΊࠐΜͰΈ·͢ɻϑΝΠϧΛίϯύΠ ϧ࣌ʹຒΊࠐΉʹ͸ file-embed ύοέʔδ*14ͱ Template Haskell Λར༻͠·͢ɻ {-# LANGUAGE TemplateHaskell #-} import Data.FileEmbed (embedFile) indexHtml :: ByteString indexHtml = $(embedFile "static/index.html") લड़ͨ͠௨ΓɺBazel ͸ґଘ͢Δ΋ͷΛࡉ͔͘ྻڍ͢Δඞཁ͕͋Γ·͢ɻຒΊࠐΜͰ͍Δ static/index.html ΋ྫ֎Ͱ͸͋Γ·ͤΜɻͰ͢ͷͰɺ࣍ͷΑ͏ʹ BUILD ϑΝΠϧΛॻ͖׵͑ Δඞཁ͕͋Γ·͢ɻ haskell_library( name = "salary-graph-library", src_strip_prefix = "src", srcs = glob(["src/**/*.hs"]), deps = [ ... ], extra_srcs = ["static/index.html"], # ௥Ճ compiler_flags = GHC_FLAGS, ) Haskell ͔Β Elm ίʔυΛੜ੒ ൒͹͓·͚Ͱ͕͢ɺ Haskell ͔Β Elm ͷίʔυΛੜ੒͢Δ͜ͱͰ࣮૷ͷҰ෦Λڞ༗͠·͢ɻHaskell ͸ܕఆ͔ٛΒԿ͔Λࣗಈੜ੒͢Δͷ͕ಘҙͰ͢ɻͭ·ΓɺHaskell ͷܕఆ͔ٛΒ Elm ͷܕఆٛΛੜ *14 https://hackage.haskell.org/package/file-embed-0.0.15.0 9
  13. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ ੒ͨ͠ΓɺServant ʹΑΔ API ఆ͔ٛΒ API ϦΫΤετ͢Δ Elm ίʔυΛੜ੒ͨ͠ΓͰ͖·͢ɻ લऀΛ͢Δͷʹ elm-bridge ύοέʔδ*15ΛɺޙऀΛ͢Δͷʹ servant-elm ύοέʔδ*16Λར༻͠ ·͢ɻ Elm ͷܕఆٛΛੜ੒͢Δʹ͸ɺੜ੒͍ͨ͠ܕͰ elm-bridge ύοέʔδʹ͋ΔܕΫϥεͷΠϯελ ϯεΛఆٛ͢Δඞཁ͕͋Γ·͢ɻ͜Ε΋ Template Haskell Λར༻͢Δ͜ͱͰɺ؆୯ʹΠϯελϯε ఆ͕ٛͰ͖·͢ɻ {-# LANGUAGE DerivingStrategies, DuplicateRecordFields, TemplateHaskell #-} module SalaryGraph.Salary where import Data.Aeson (FromJSON, ToJSON, ToJSONKey) import Elm.Derive import Web.HttpApiData (FromHttpApiData) newtype Year = Year Int deriving newtype (Show, Eq, Ord, FromJSON, ToJSON, ToJSONKey, FromHttpApiData) deriveElmDef defaultOptions ’’Year newtype Month = Month Int deriving newtype (Show, Eq, FromJSON, ToJSON) deriveElmDef defaultOptions ’’Month data Salary = Salary { year :: Year , month :: Month , gross :: Int , net :: Int } deriving (Show, Eq) deriveBoth defaultOptions ’’Salary data Appointment = Appointment { year :: Year , month :: Month , before :: Int , after :: Int } deriving (Show, Eq) deriveBoth defaultOptions ’’Appointment deriveElmDef ͱ deriveBoth ͷ෦෼͕ Template Haskell Λ׆༻ͨ͠ΠϯελϯεఆٛʹͳΓ ·͢ɻ༨ஊͰ͕࣮͢͸ɺ͜ͷίʔυ͸࠷ۙͷ GHC ػೳ͕ͦΕͳΓʹ͍ࠞͬͯ͟·͢ɻ *15 https://hackage.haskell.org/package/elm-bridge-0.8.0 *16 https://hackage.haskell.org/package/servant-elm-0.7.2 10
  14. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ ಉҰ໊ͷϑΟʔϧυ ·ͣɺSalary ܕͱ Appointment ܕͷ྆ํʹಉҰ໊ͷϑΟʔϧυ͕͋Γ·͢ɻ͜ΕΒΛՄೳʹ͠ ͍ͯΔͷ͕ GHC 8.0 Ͱೖͬͨ DuplicateRecordFields ֦ுͱ GHC 9.2 Ͱೖͬͨ NoFieldSele ctors ֦ுͰ͢ɻৄࡉ͸ׂѪ͠·͕͢ɺ͜Εͱ GHC 9.2 ͔Βͷυοτه๏֦ுΛར༻͢Δ͜ͱͰɺ ΄͔ͷϨίʔυܕͷϑΟʔϧ໊΍ؔ਺໊ʹؾ݉Ͷͳࣗ͘વͳϑΟʔϧυ໊Λ෇͚Δ͜ͱ͕Ͱ͖·͢ɻ newtype ܕએݴ ·ͨɺderiving newtype ͱ͍͏ͷ͸ GHC 8.2 Ͱೖͬͨ DerivingStrategies ֦ுͷػೳͰ ͢*17ɻHaskell ͷ newtype ܕએݴ͸ʮܕݕࠪ࣌͸ผͷܕͱͯ͠ѻ͍ɺ࣮ߦ࣌͸θϩίετͰಉҰͷ ܕͱͯ͠ػೳͯ͠΄͍͠ܕʯΛදݱͰ͖·͢ɻHaskell ͸ type ܕએݴΛ࢖͏͜ͱͰܕʹผ໊Λ༩͑ Δ͜ͱ͕Ͱ͖·͕͢ɺ͜ͷ৔߹͸ܕݕࠪ࣌ʹ۠ผ͞ΕͣܕΫϥεͷΠϯελϯε΋ڞ༗ͯ͠͠·͍· ͢ɻͭ·ΓɺԾʹ Year ܕͱ Month ܕʹ type ܕએݴΛར༻͢Δͱ deriveElmDef ͕Ͱ͖ͳ͍ͷͰ ͢ɻٯʹ newtype ܕએݴΛ࢖͏ͱɺڞ༗ͯ͠΄͍͠ Int ܕͷܕΫϥεΠϯελϯεΛࣗ෼Ͱఆٛ͠ ௚͢ඞཁ͕͋Γ·ͨ͠ɻͦ͜Ͱ DerivingStrategies Λ࢖͏͜ͱͰɺ໌ࣔͨ͠ܕΫϥεͷΠϯελ ϯε͚ͩΛڞ༗Ͱ͖ΔͷͰ͢ɻ ؓ࿩ٳ୊ Elm ͷܕఆٛΛੜ੒͢ΔͨΊͷ४උ͕Ͱ͖ͨͷͰɺ࣮ࡍʹੜ੒͢ΔίʔυΛඳ͖·͢ɻ {-# LANGUAGE TypeApplications, TypeOperators #-} module Main where import Data.Maybe (fromMaybe, listToMaybe) import SalaryGraph.API (SalaryAPI) import SalaryGraph.Salary as Salary import Servant ((:>)) import Servant.Elm import System.Environment (getArgs) main :: IO () main = do dirPath <- fromMaybe "elm-src" . listToMaybe <$> getArgs generateElmModuleWith defElmOptions ["Generated", "SalaryAPI"] defElmImports dirPath [ DefineElm (Proxy @Salary), DefineElm (Proxy @Salary.Appointment) , DefineElm (Proxy @Salary.Year), DefineElm (Proxy @Salary.Month) ] (Proxy @("api" :> SalaryAPI)) *17 newtype ܕએݴͷΑΓৄ͍͠ղઆʹ͍ͭͯ͸ Hiromi Ishii ࢯͷʮຊ౰͸͍͢͝ newtypeʯͱ͍͏εϥΠυ͕Θ͔Γ ΍͍͢Ͱ͢ɻhttps://speakerdeck.com/konn/ben-dang-hasugoi-newtype 11
  15. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ ͜ΕΛ app/generate/Main.hs ͱ͠·͢ɻ͋ͱ͸ɺBUILD ϑΝΠϧʹϧʔϧΛهड़͠·͢ɻ haskell_binary( name = "generateElm", srcs = glob(["app/generate/**/*.hs"]), deps = [ ":salary-graph-library", "@stackage//:base", "@stackage//:elm-bridge", "@stackage//:servant-elm", "@stackage//:servant-server", ], compiler_flags = GHC_FLAGS, ) ͜ΕͰɺbazel run //:generateElm -- $(pwd)/elm-src Ͱ Haskell ίʔυ͔Β Elm ίʔυ Λ elm-src ഑Լʹੜ੒͞Ε·͢ɻ Elm Λ Bazel ͰϏϧυ ࠷ޙʹɺElm Λ Bazel ͰϏϧυͯ͠Έ·͢ɻBazel Ͱ Elm ίʔυΛϏϧυ͢Δʹ͸ɺචऀ͕ࣗ࡞ ͍ͯ͠Δ matsubara0507/rules_elm Λ࢖͏ͱྑ͍Ͱ͢ɻ·ͣ͸ɺWORKSPACE ϑΝΠϧʹར༻ ͢Δ Bazel ϥΠϒϥϦͷఆٛΛهड़͠·͢ɻ http_archive( name = "rules_elm", sha256 = "a9db7f55e3693ab94a60cbf602221095514aec6541253b21cc89f0ba1365d87c", url = "https://github.com/matsubara0507/rules_elm/releases/download/v1.0.0/rules_elm-v1.0.0.zip", ) load("@rules_elm//elm:repositories.bzl", rules_elm_repositories = "repositories") rules_elm_repositories() load("@rules_elm//elm:toolchain.bzl", rules_elm_toolchains = "toolchains") rules_elm_toolchains(version = "0.19.1") Elm ϓϩδΣΫτ͸ elm.json ϑΝΠϧʹɺιʔείʔυ͸ elm-src σΟϨΫτϦ഑Լʹ͋Δͱ͠ ·͢ɻBUILD ϑΝΠϧʹ͸࣍ͷΑ͏ʹ௥ه͠·͢ɻ 12
  16. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ load("@rules_elm//elm:def.bzl", "elm_dependencies", "elm_make") elm_dependencies( name = "deps", elm_json = "elm.json", ) elm_make( name = "mainjs", srcs = glob(["elm-src/**"]), elm_home = ":deps", elm_json = "elm.json", main = "elm-src/Main.elm", output = "static/javascript/main.js", ) ͜ͷఆ͕ٛ elm make elm-src/Main.elm --output=static/javascript/main.js ͱ౳Ձʹ ͳΓ·͢ɻੜ੒ͨ͠ main.js ͸ɺindex.html ಉ༷ʹ Haskell ίʔυ΁ίϯύΠϧ࣌ʹຒΊࠐΈ͍ͨ Ͱ͢ΑͶɻͦ͜Ͱ໰୊͕Ұͭ͋Γ·͢ɻBazel Ͱੜ੒ͨ͠ main.js ΁ͷύε͕Θ͔Βͳ͍఺Ͱ͢ɻͦ ͷ໰୊Λղܾ͢ΔͨΊʹɺCPP ݴޠ֦ுʹΑΔίϯύΠϧ࣌ม਺Λར༻͠·͢ɻ {-# LANGUAGE CPP, TemplateHaskell #-} import Data.FileEmbed (embedDir) import System.FilePath (takeDirectory) type API = Get ’[HTML] ByteString :<|> "static" :> Raw :<|> "api" :> SalaryAPI server :: Server API server = pure indexHtml :<|> serveDirectoryEmbedded $(embedDir (takeDirectory MAINJS_FILE)) :<|> getSalaries :<|> getAppointments MAINJS_FILE ͕ίϯύΠϧ࣌ʹஔ͖׵ΘΔม਺Ͱ͢ɻࠓճ͸ɺϑΝΠϧ୯ମͰ͸ͳ͘σΟϨΫτ ϦΛؙͬͱຒΊࠐΈ͍ͨͷͰ embedDir ΍ takeDirectory Λར༻͍ͯ͠·͢ɻMAINJS_FILE ͱஔ ͖׵͑Δ஋ʢύεʣΛ౉͢ʹ͸ίϯύΠϧϑϥάΛར༻͠·͢ɻ haskell_library( name = "salary-graph-library", 13
  17. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ src_strip_prefix = "src", srcs = glob(["src/**/*.hs"]), deps = [ ... ], extra_srcs = [ "static/index.html", ":mainjs", ], compiler_flags = ["-DMAINJS_FILE=\"$(execpath :mainjs)\""] + GHC_FLAGS, ) Bazel ͷཪٕʢʁʣͱͯ͠ɺ$(execpath :mainjs) ͱ͢Δ͜ͱͰ Bazel ʹΑͬͯੜ੒ͨ͠ϑΝΠ ϧͷύεΛϏϧυ࣌ʹग़͢͜ͱ͕Ͱ͖·͢ɻ·ͨɺ͜ΕͰ Haskell ͷϏϧυ͕ Elm ͷϏϧυʹґଘ ͢ΔΑ͏ʹͳͬͨͨΊɺbazel build //:salary-graph ͱ͢Δ͚ͩͰ྆ํͷϏϧυ͕࣮ߦ͞ΕΔ Α͏ʹͳΓ·ͨ͠*18ɻ ͓·͚ɿDocker ΠϝʔδͷϏϧυ ͓·͚ͱͯ͠ɺElm ͱ Haskell ͷϏϧυͷ΄͔ʹ Docker ΠϝʔδΛ࡞Δ෦෼΋ Bazel Ͱߦ͏͜ ͱͰɺ1 ίϚϯυͰ Docker Πϝʔδͷϓογϡ·ͰΛߦ͑ΔΑ͏ʹͯ͠Έ·͢ɻDocker Πϝʔδ ͷૢ࡞Λ Bazel Ͱߦ͏ʹ͸ rules_docker Λ࢖͏ͷͰɺWORKSPACE ϑΝΠϧʹͦͷ͜ͱΛ௥ه ͠·͢ɻ http_archive( name = "io_bazel_rules_docker", sha256 = "59536e6ae64359b716ba9c46c39183403b01eabfbd57578e84398b4829ca499a", strip_prefix = "rules_docker-0.22.0", url = "https://github.com/bazelbuild/rules_docker/releases/download/v0.22.0/rules_docker-v0.22.0.tar.gz", ) load("@io_bazel_rules_docker//repositories:repositories.bzl", container_repositories = "repositories") container_repositories() load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps") container_deps() ࠓճ͸ϕʔεΠϝʔδͱͯ͠ɺචऀ͕ࣗ࡞ͯ͠ GitHub Container Registory ʹ͍͋͛ͯΔ΋ͷΛ ར༻͠·͢ɻϕʔεΠϝʔδͳͲɺBazel ಺Ͱར༻͢Δ֎෦ͷ Docker Πϝʔδ͸ container_pul *18 ͨͩ͠ɺHaskell ίʔυ͔Β Elm ίʔυΛੜ੒͢Δ෦෼͸ɺbazel run Ͱߦ͏ඞཁ͕͋ΔͨΊผͰ࣮ߦ͢Δඞཁ͕͋ Γ·͢ɻ 14
  18. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.3 Elm + Haskell + Bazel Ͱ࡞Δ l ϧʔϧΛར༻ͯ͠ WORKSPACE ϑΝΠϧʹهड़͢Δඞཁ͕͋Γ·͢ɻ load("@io_bazel_rules_docker//container:container.bzl", "container_pull") container_pull( name = "ubuntu_for_haskell", tag = "18.04", registry = "ghcr.io", repository = "matsubara0507/ubuntu-for-haskell", ) ͋ͱ͸ BUILD ϑΝΠϧʹΠϝʔδͷϏϧυͷઃఆͱϓογϡͷઃఆΛهड़͢Δ͚ͩͰ͢ɻϓο γϡઌ΋චऀͷ GitHub Container Registory ʹͯ͋͠Γ·͢ɻ load("@rules_pkg//:pkg.bzl", "pkg_tar") load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push") pkg_tar( name = "bin", srcs = [":salary-graph"], mode = "0755", package_dir = "/usr/local/bin", ) container_image( name = "image", base = "@ubuntu_for_haskell//image", tars = [":bin"], entrypoint = ["/usr/local/bin/salary-graph"], ) container_push( name = "push", format = "Docker", image = ":image", registry = "ghcr.io", repository = "matsubara0507/salary-graph", ) ͜ΕͰɺbazel run //:push ͱ͢Δ͚ͩͰ Elm ͱ Haskell ͷϏϧυ͔ΒΠϝʔδͷ࡞੒ɺϓο γϡ·ͰΛ΍ͬͯ͘Ε·͢*19ɻ *19 ͨͩ͠ɺ͜ͷίϚϯυΛ macOS ΍ Windows Ͱ࣮ߦ͢ΔͱɺLinux ͷΠϝʔδʹ macOS ΍ Windows ͷ࣮ߦϑΝ Πϧؚ͕·Εͯ͠·͍·͢ɻ͜ΕΛղܾ͢Δʹ͸ΫϩείϯύΠϧ͕Ͱ͖Δඞཁ͕͋Γɺrules_haskell Ͱ͸·ͩ͏· ͘Ͱ͖·ͤΜɻ 15
  19. ୈ 1 ষ Elm + Haskell Ͱ࡞Δ Web ΞϓϦ with

    Bazel 1.4 ऴΘΓʹ 1.4 ऴΘΓʹ Bazel ͸ෳ਺ͷϓϩάϥϛϯάݴޠͳͲ͕ࠞͬͯ͘͟ΔͱޮՌ͕ग़ͯ͘ΔϏϧυπʔϧͰ͢ɻैདྷ Ͱ͋Ε͹ɺͦΕͧΕͷϓϩάϥϛϯάݴޠͷϏϧυπʔϧΛར༻͠ɺMakefile ͰΑ͠ͳʹͭͳ͗߹Θ ͤΔͷ͕ओྲྀͩͱࢥ͍·͢ɻMakefile ʹ͍ͭͯචऀ͸͋·Γ஌Βͳ͍ͷͰൺֱ͸Ͱ͖·ͤΜɻ͔͠ ͠ Bazel Ͱ͋Ε͹ɺ ʢ஌໊౓ͷߴ͍ʣ֤ϓϩάϥϛϯάݴޠͷઃఆ͕ϥΠϒϥϦͱͯ͠ఏڙ͞Εͯͨ ΓɺPython ϥΠΫͳϓϩάϥϛϯάݴޠͰϓϩάϥϛϯάͷΑ͏ʹ֦ுͰ͖ͨΓͳͲͷར఺͕͋Γ ·͢ɻ΋ͪΖΜɺಠࣗͷαϯυϘοΫε؀ڥʹΑΔϏϧυ΍ґଘؔ܎Λ໌ࣔతʹ؅ཧ͢Δ͜ͱʹΑΔ ࠶ݱੑͷߴ͞΋େ͖ͳར఺Ͱ͢ɻͨͩܽ͠఺ͱͯ͠ɺBazel ͷ৘ใ͸·ͩগͳ͍ʢಛʹ೔ຊޠͰ͸ʣ ͨΊɺࣗ਎ͰΰϦΰϦͱ͍͡ΕΔਓ͕ඞཁ͔΋͠Ε·ͤΜɻ 16
  20. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏ εͱ੔߹ੑͷ࿩ օ͞ΜΨνϟճ͍ͯ͠·͔͢ʁ ࢲ͸ຊݪߘࣥච࣌Ͱ͋Δ 2021 ೥ 12

    ݄ 31 ೔ 22 ࣌ 35 ෼ɺ೥຤ ೥࢝ΨνϟͰരࢮ͍ͯ͠·͢ɻ ΑΖ͓͘͠ئ͍͠·͢ɻ Ϣʔβʔͷ໨ઢ͔Βͷલ࠲͸͓͖ͯ͞ɺࠓճͷ࿩୊͸ɺιʔγϟϧήʔϜͳͲͷΨνϟʹ͍ͭͯɺ ઃܭͱ࣮૷ʹ·ͭΘΔ੔߹ੑͷ͓࿩Ͱ͢ɻ 2.1 ઃఆ؀ڥͱΨνϟϑϩʔͷఆٛ ઃܭͱ࣮૷ʹ·ͭΘΔ੔߹ੑͷ͓࿩ΛਐΊ͍ͯ͘ʹ͋ͨΓɺ·ͣࠓճઃఆͨ͠ɺΨνϟγεςϜ͕ ৐͔͍ͬͬͯΔϓϩμΫτʹ͍ͭͯ঺հ͠·͢ɻ ؆୯ͳߏ੒ਤΛ ਤ 2.1 ʹࣔ͠·͢ɻͳ͓ɺෛՙ෼ࢄ΍৑௕Խɺ ϩάͷӬଓԽͷ෦෼͸దؚٓ·Ε ͍ͯΔ΋ͷͱ͠·͢ɻ 17
  21. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.1 ઃఆ؀ڥͱΨνϟϑϩʔͷఆٛ ਤ 2.1: ઃఆ؀ڥͷΞʔΩςΫνϟਤ ΍ͨΒόοΫΤϯυͷొ৔ਓ෺͕ଟ͘ͳ͍ʁ

    ͱ͍͏ͷ͕ݟͨ໨ͷୈҰҹ৅Ͱ͋Δͱࢥ͍·͢ɻ҆ ৺͍ͯͩ͘͠͞ɻ͜͜ͰऔΓѻ͏ͷ͸ɺιʔγϟϧήʔϜαʔϏεͱࢿܾۚࡁαʔϏεͷ͚̎ͭͩ Ͱ͢ɻ 18
  22. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.2 γʔέϯεਤͰߟ͑ͯΈΔ ߏ੒ਤΛ঺հͨ͠ͱ͜ΖͰɺ࣍ʹࠓճऔΓѻ͏Ψνϟͷ۩ମతͳྲྀΕΛઃఆ͠·͢ɻ 1. ೥຤ΨνϟͰେരࢮͨ͠ E

    ͞Μ͸ɺ·ͣ՝ۚॲཧΛߦ͍ɺΨνϟͷͨΊͷήʔϜ಺ࢿ࢈Λ֫ ಘ͠·͢ 2. ήʔϜ಺ࢿ࢈Λ֫ಘͨ͠ E ͞Μ͸ɺ10 ࿈ΨνϟΛҾ͖ɺ͓໨౰ͯͷظؒݶఆΩϟϥΫλΛ֫ ಘ͠·͢ɻ 3. ظؒݶఆΩϟϥΫλΛ֫ಘ͠ӻʹೖͬͨ E ͞Μ͕ϗʔϜը໘ʹ໭Δͱɺ ʮಛఆ৚݅ͷΩϟϥΫ λͷऔಘ਺͕نఆΛ௒͑ͨͷͰϛογϣϯୡ੒ϘʔφεΛड͚औΔ͜ͱ͕Ͱ͖Δʯͱ͍͏ϔο μ௨஌Λड͚औΓ·͢ɻ ͜ͷ͋ͱ E ͞Μ͸΢Ω΢ΩͰϛογϣϯใुड͚औΓը໘΁͍͖·ͨ͠ɻԿͳΒ΋͏ 30 ࿈͘Β ͍ΨνϟΛճ͠·͢ɻճ͢ͷͰɺͥͻҾ͔͍͖͍ͤͯͨͩͨͰ͢ɻ 2.2 γʔέϯεਤͰߟ͑ͯΈΔ લઅͰΨνϟϑϩʔΛఆٛͰ͖ͨͱ͜ΖͰɺ࣍ʹ͜ͷΨνϟͷྲྀΕΛγʔέϯεਤͰղऍͯ͠Έ ·͢ɻ 19
  23. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.2 γʔέϯεਤͰߟ͑ͯΈΔ ՝ۚϑϩʔ ·ͣ͸՝ۚϑϩʔ Λ ਤ

    2.2 Ͱࣔ͠·͢ɻήʔϜͷΫϥΠΞϯτը໘͔ΒɺࢿܾۚࡁαʔϏεͷܾ ࡁը໘΁ભҠɺܾࡁͰ͖Ε͹ήʔϜ಺ࢿ࢈͕ߋ৽͞ΕΫϥΠΞϯτը໘ʹ൓ө͞ΕΔɺͱ͍͏ྲྀΕ͕ ֬ೝͰ͖Δͱࢥ͍·͢ɻ ਤ 2.2: ՝ۚϑϩʔ: γʔέϯεਤ 20
  24. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.2 γʔέϯεਤͰߟ͑ͯΈΔ Ψνϟϑϩʔ ࣍ʹΨνϟϑϩʔ͸ͪ͜Β ਤ 2.3ɻ՝ۚͷγʔέϯεਤʹൺ΂Δͱɺ͔ͳΓ޻ఔ͕গͳ͘ײ͡Δͷ

    Ͱ͸ͳ͍Ͱ͠ΐ͏͔ʁ ஫ҙͯ͠΄͍͠ͷ͕ɺ PublishPlayGachaEvent ͱ͍͏෦෼Ͱ͢ɻ͜ͷ෦෼Ͱ͸ Google Cloud Pub/Sub *1 ͷΑ͏ͳϝοηʔδϯάαʔϏεΛ༻͍ͯɺΨνϟΛҾ͍ͨ͜ͱͱͦͷ݁ՌΛϛογϣ ϯॲཧϫʔΧʹ౤͚͍͛ͭͯ·͢ɻͭ·Γɺ ϛογϣϯॲཧͷ݁Ռʹ͍ͭͯɺΨνϟϑϩʔͰ͸औ ΓѻΘͳ͍ɺͱ͍͏͜ͱΛҙຯ͍ͯ͠·͢ ɻ ਤ 2.3: Ψνϟϑϩʔ: γʔέϯεਤ *1 https://cloud.google.com/pubsub 21
  25. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.2 γʔέϯεਤͰߟ͑ͯΈΔ ϛογϣϯॲཧϑϩʔ ࠷ޙʹϛογϣϯॲཧϑϩʔʹ͍ͭͯ ਤ 2.4

    Ͱࣔ͠·͢ɻϛογϣϯॲཧϑϩʔ͸͜Ε·Ͱͷϑ ϩʔͱ͸ҟͳΓɺΫϥΠΞϯτ͔ΒͷϦΫΤετىҼͰಈ͘Θ͚Ͱ͸ͳ͘ɺΞϓϦαʔό͔Βͷϝο ηʔδϯάͰಈ͘఺͕ͪΐͬͱҧ͍·͢ɻ ਤ 2.4: ϛογϣϯॲཧϑϩʔ: γʔέϯεਤ 22
  26. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.3 3 ͭͷ੔߹Խઓུ 2.3 3 ͭͷ੔߹Խઓུ

    લষ·ͰΨνϟϑϩʔͷॲཧͷྲྀΕΛ 1. ՝ۚॲཧ 2. ΨνϟΛҾ͘ॲཧ 3. ϛογϣϯͷॲཧ ͷ 3 ͭʹ෼ׂͯ͠঺հ͖ͯ͠·ͨ͠ɻ ͜ͷ 3 ͭ͸ͦΕͧΕɺ (Ϣʔβʔ໨ઢతʹ / αʔϏεతʹ) ݪࢠతͳ୯Ґͱͯ͠ѻΘΕ͍ͯ·͢ *2ɻ͓ͦͯ͠΋͠Ζ͍͜ͱʹɺͦΕͧΕͰߦ͍ͬͯΔ୯ҐͷҰ؏ੑΛอͭͨΊͷ੔߹Խઓུ͕ҟͳΓ ·͢ɻ *3 ࣄલʹͦΕΒʹͦΕͬΆ͍ݴ༿ͰλΠτϧΛ͚ͭΔͱ͢Ε͹ɺ࣍ͷΑ͏ͳܗʹͳΓ·͢ɻ • TCC ͳ ෼ࢄτϥϯβΫγϣϯ (՝ۚॲཧ) • DB ͱ ACID τϥϯβΫγϣϯ (Ψνϟॲཧ) • ݁Ռ੔߹ͳσʔλߋ৽ͷಛघύλʔϯ (ϛογϣϯॲཧ) ͔͜͜Βɺ͜ΕΒͷ 3 ͭͷ੔߹Խઓུʹ͍ͭͯ঺հ͍͖ͯ͠·͢ɻ *2 Ϣʔβʔ΍αʔϏεͷશମ૾͔ΒӅณ͞Ε͍ͯΔ෦෼ΛؚΜͩɺݫີͳݪࢠ୯ҐͰ͸ͳ͍Ͱ͢ɻ *3 ͜ͷઓུ͕ͦΕͧΕҟͳΔͷ͸ɺͦ͏͍͏঺հΛ͢ΔͨΊͰɺඞͣΨνϟϑϩʔΛ࣮૷͢Δͱ͖ʹ͸͜ͷܗࣜʹै͑ɺ ͱ͍͏΋ͷͰ͸ͳ͍Ͱ͢ͷͰ͔͋͠Βͣɻ 23
  27. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.3 3 ͭͷ੔߹Խઓུ TCC ͳ෼ࢄτϥϯβΫγϣϯ ·ͣ͸

    ՝ۚॲཧͰ͋Δɺ TCC (Try Confirm Cancel pattern) ͳ෼ࢄτϥϯβΫγϣϯʹ͍ͭͯ ঺հ͍͖ͯ͠·͢ɻ෼ࢄτϥϯβΫγϣϯͱ͸ɺಛʹϚΠΫϩαʔϏεͷจ຺Ͱग़ͯ͘Δɺෳ਺ͷ αʔϏεͰσʔλͷ੔߹ੑΛอͭͨΊͷઓུΛࢦ͍ͯ͠·͢ɻTCC ͸ͦͷதͷҰͭͰɺ֤αʔϏε Ͱσʔλͷߋ৽ॲཧΛɺ Try Phase ͱ Confirm / Cancel Phase ͱ͍͏ɺ2 ஈ֊ͷॲཧʹ෼͚ͯߦ ͏ύλʔϯʹͳΓ·͢ɻ Try Phase σʔλߋ৽Ͱ͋Δ͜ͱΛอূ͢ΔϑΣʔζɻ ܾࡁॲཧͰ͋Ε͹ɺݱࡏͷ࢒ߴ͔ΒܾࡁֹΛࠩ͠Ҿ͘͜ͱ͕Ͱ͖Δ͜ͱΛ֬ೝ͠ɺͦͷঢ়ଶΛ ֬อ͢Δ ஈ֊Ͱ͢ɻ ஫ҙ͢Δ఺ͱͯ͠͸ɺTry Phase ͸ Confirm / Cancel Phase ͕ݺͼग़͞ΕΔ·Ͱ֬อͨ͠ όοϑΝΛख์͞ͳ͍͜ͱΛอূ͠ͳ͚Ε͹ͳΒͳ͍ɺͱ͍͏఺Ͱ͢ɻͭ·Γ Try Phase Ͱ 10 ສԁͷ࢒ߴ͔Β 9 ສ ֬อͨ͠৔߹ɺͦͷޱ࠲͔Β͸͞Βʹ 2 ສԁҾ͖ग़ͤͳ͍Α͏ʹ͠ͳ ͚Ε͹͍͚·ͤΜɻ Confirm / Cancel Phase Try Phase Ͱ֬อͨ͠όοϑΝΛ։์͢ΔϑΣʔζɻ ܾࡁॲཧͰ͋Ε͹ɺCancel ͷͱ͖͸֬อֹ͍ͯͨۚ͠Λ࠶ͼҾ͖ग़͠Մೳʹ͠ɺ Confirm ͷ ৔߹Ͱ͋Ε͹ɺݱࡏͷ࢒ߴ͔Β֬อֹ͍ͯͨۚ͠Λࠩ͠Ҿ͖·͢ɻ ஫ҙ͢Δ఺ͱͯ͠͸ɺCancel / Confirm Phase ࣗମʹ΋Τϥʔ͸ى͜Γ͏Δɺͱ͍͏ళͰ͢ɻ ͭ·Γ Try Phase Ͱ͸ Cancel / Confirm Phase ͕ ࣮ߦͰ͖Δ ͜ͱΛอূ͓ͯ͠Γɺ੒ޭ͢ Δ ͜ͱΛอূ͍ͯ͠ΔΘ͚Ͱ͸ͳ͍ɺͱ͍͏͜ͱͰ͢ɻͰ͢ͷͰɺ Cancel / Confirm Phase ͕ࣦഊͨ͠ͱ͖ʹ͸ɺԿΒ͔ͷํ๏ͰϦτϥΠ͢ΔػߏΛ༻ҙ͢Δඞཁ͕͋Γ·͢ɻ ՝ۚॲཧͰ͸ɺιʔγϟϧήʔϜαʔϏε͚ͩͰ͸ͳ͘ɺࢿܾۚࡁαʔϏεΛ·͍ͨͩॲཧʹͳͬ ͍ͯ·͢ (ʮ2.1 ઃఆ؀ڥͱΨνϟϑϩʔͷఆٛʯ > ʮ՝ۚϑϩʔʯ) ɻΑͬͯɺݱ͚ۚͩҾ͖ԼΖ ͞ΕͯɺήʔϜ಺ࢿ࢈͕૿͑ͳ͍ɺͱ͍͏Α͏ͳࣗମ͕ى͜͞ͳ͍ͨΊʹ΋෼ࢄτϥϯβΫγϣϯͷ TCC ͸༗ޮͳखஈͱݴ͑·͢ɻ ͨͩ͠ TCC Λ࣮૷͢Δʹ͸ɺجຊతʹͦΕͧΕͷαʔϏεͰ TCC ύλʔϯΛ࣮૷Ͱ͖͍ͯΔ͜ ͱ͕ඞཁͰ͋Γɺͦ͏Ͱͳ͍৔߹ʹ͸ TCC ύλʔϯΛԠ༻ͨ͠ผͷॲཧ͕ඞཁʹͳΓ·͢ *4ɻ *4 ͨͩ͠ࠓճͷύλʔϯͰ͸ɺࢿܾۚࡁαʔϏε͕ TCC Λ࣮૷ͯ͠ͳ͘ͱ΋ɺΞϓϦαʔό͚ͩ TCC ͕࣮૷Ͱ͖͍ͯ Ε͹ɺࢿܾۚࡁαʔϏεͷ Succeed / Failed ͰΞϓϦαʔϏεͷ Confirm / Cancel Λ͢Ε͹ྑ͍ͷͰ໰୊ͳ͍Ͱ ͢ɻ 24
  28. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.3 3 ͭͷ੔߹Խઓུ DB ͱ ACID

    τϥϯβΫγϣϯ ଓ͍ͯɺΨνϟΛҾ͘ॲཧͰ͋Δɺ DB ͱ ACID τϥϯβΫγϣϯʹ͍ͭͯ঺հ͍͖ͯ͠·͢ɻ ACID ͱ͸ɺAtomicityɺConsistencyɺIsolationɺDurability ͷ಄จࣈΛฒ΂ུͨޠʹͳΓ·͢ɻͭ ·Γ ACID τϥϯβΫγϣϯͱ͸ɺ ACID ͳੑ࣭Λຬͨ͢τϥϯβΫγϣϯͰ͢ɻ Atomicity (ݪࢠੑ) τϥϯβΫγϣϯͰͬͨ͘͘୯Ґͷ࣮ߦ͕ɺ࣮ߦ͞ΕΔ / ͞Εͳ͍ͷ 2 ஋ΛऔΔ͜ͱɻ·ͨ ͦͷ్தܦաΛ֎෦͔ΒࢀরͰ͖ͳ͍͞·Λࣔ͠·͢ɻ Consistency (Ұ؏ੑ) τϥϯβΫγϣϯͷલதޙͰυϝΠϯͷෆม৚݅Λຬͨ͢͜ͱɻ Isolation (ಠཱੑ) τϥϯβΫγϣϯॲཧ಺ͷग़དྷࣄ͸ɺ΄͔ͷಡΈࠐΈɺॻ͖ࠐΈʹରͯ͠ӨڹΛٴ͹͞ͳ͍͜ ͱɻݴ͍׵͑Ε͹ɺ్தܦաΛ֎෦͔Βࢀর͞Εͳ͍͜ͱΛࣔ͠·͢ɻ Durability (Ӭଓੑ) τϥϯβΫγϣϯͷޙͷ݁Ռ͕อ࣋͞ΕΔ͜ͱɻ ͜͜Ͱɺ෼ࢄτϥϯαΫγϣϯ͸ ACID Ͱ͸ͳ͍ͷʁɺͱ͍͏ٙ໰͕ग़ͯ͘Δͱࢥ͍·͢ɻ݁࿦ ͔Βߦͬͯ͠·͏ͱɺ࢒೦ͳ͕Βɺ෼ࢄτϥϯβΫγϣϯ͸ ACID Ͱ͸͋Γ·ͤΜ *5 ɻ ͨͱ͑͹Ұͭલͷ՝ۚॲཧͰ͸ɺܾࡁ DB ΁ͷ Confirm ॲཧͱΞϓϦέʔγϣϯ DB ͷ Confirm ॲཧ͸͍͘Β͔ͷλΠϜϥά͕ੜ͡·͢ɻͦͷͨΊɺ͜ͷॠؒʹࢿܾۚࡁαʔϏεͷ࢒ߴͱιʔγϟ ϧήʔϜαʔϏεͷήʔϜ಺ࢿ࢈͸௰᧒͕߹Θͳ͍ঢ়ଶʹͳ͍ͬͯ·͢ɻ(TCC ͸ɺ͜ͷঢ়ଶͰࢿۚ ܾࡁαʔϏε͔ΒࢿۚΛԼΖͤͳ͘͢ΔηʔϑςΟωοτΛு͍ͬͯ·͢ɻ) ՝ۚॲཧͷ࿩͸͓͖ͯ͞ΨνϟͷτϥϯβΫγϣϯʹ໭ΔͱɺPostgreSQL *6 ΍ Google Cloud Spanner *7 Ͱ͸ɺಉҰ DB ΁ͷ ACID ͳτϥϯβΫγϣϯΛαϙʔτ͍ͯ͠·͢ɻΨνϟΛҾ͘ ॲཧͰ͸ɺσʔλͷૢ࡞͸ιʔγϟϧήʔϜαʔϏεʹด͓ͯ͡ΓɺDB ͱͯ͠͸ΞϓϦέʔγϣϯ DB ͷΈΛ࢖͍ͬͯΔͨΊɺACID ͳτϥϯβΫγϣϯΛద༻Ͱ͖·͢ɻ ·ͨଟ͘ͷ DB Ͱ͸ τϥϯβΫγϣϯॲཧ͕ࣦഊͨ͠ͱ͖ͷ (ςʔϒϧ಺ͷσʔλʹ͍ͭͯͷ) ϩʔϧόοΫ͕࣮૷͞Ε͓ͯΓ *8 ɺॲཧࣦഊͷͱ͖ͷॲཧΛ؆୯ʹ࣮૷Ͱ͖·͢ɻۙ೥ͷ DB ͸͔ ͍͜͠Ͱ͢Ͷɻ *5 ෼ࢄτϥϯβΫγϣϯ͕ৗʹ ACID τϥϯβΫγϣϯͰ͸ͳ͍ূ໌ʹ͍ͭͯ͸ɺCAP ఆཧΛࢀর͍ͯͩ͘͠͞ *6 https://www.postgresql.org *7 https://cloud.google.com/spanner *8 https://cloud.google.com/spanner/docs/transactions 25
  29. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.3 3 ͭͷ੔߹Խઓུ ݁Ռ੔߹ͳσʔλߋ৽ͷಛघύλʔϯ ࠷ޙʹϛογϣϯͷॲཧͰ͋Δɺ݁Ռ੔߹ͳσʔλߋ৽ͷಛघύλʔϯΛ঺հ͠·͢ɻ݁Ռ੔߹ɺ ͱ͸

    “࠷ऴతʹσʔλ͸Ұ؏ੑΛ࣋ͬͯߋ৽͞ΕΔ” ͱ͍͏ಛੑΛࢦ͠·͢ɻ ࠓճͷϛογϣϯॲཧ͸ɺ୺తʹݴͬͯ͠·͑͹ɺΨνϟͷ݁Ռ͕ग़͔ͯΒ ͠͹Βͯ͘͠ ϛο γϣϯσʔλͷߋ৽͕ߦΘΕ·͢ɻ͞Βʹ͓΋͠Ζ͍͜ͱʹɺԾʹϛογϣϯॲཧ͕ࣦഊͨ͠ͱͯ͠ ΋Ψνϟ݁ՌΛϩʔϧόοΫ͢Δػߏ͕༻ҙ͞Ε͍ͯ·ͤΜɻ গͳ͘ͱ΋ҟৗܥΛߟ͑Ε͹ɺϛογϣϯσʔλͱΨνϟσʔλ͸݁Ռ੔߹Ͱ͸ͳ͍ͷͰ͸ʁɺͱ ࢥ͏͔΋͠Ε·ͤΜɻ͔͠͠੔ཧ͢ΔͱɺυϝΠϯͷࣄલ/ࣄޙ/ෆม৚͕݅࿦ཧతʹਖ਼͘͠໢ཏੑ ͕͋Δ৔߹ʹ͸ɺ͜ͷॲཧ͸݁Ռ੔߹Λอͭ͜ͱ͕Ͱ͖͍ͯ·͢ɻಛघͳ఺Λڍ͛Δͱ͢Ε͹ɺϛο γϣϯॲཧͷ੒ޭ / ࣦഊʹ͔͔ΘΒͣɺઌʹΨνϟॲཧΛ֬ఆ͍ͤͯͯ͞ɺϛογϣϯॲཧ͕ࣦഊ͠ ͯ΋ΨνϟσʔλΛϩʔϧόοΫ͠ͳ͍ɺͱ͍͏఺Ͱ͢ɻ ද 2.1: ϛογϣϯσʔλͱΨνϟσʔλͷ੔߹ԽରԠ Ψνϟ\ϛογϣϯ ੒ޭ ࣦഊ ੒ޭ ਖ਼ৗܥ ϛογϣϯॲཧϫʔΧ͸ࣦഊ࣌ʹϦτϥΠ͢Δ ࣦഊ ϛογϣϯॲཧϫʔΧ͸ݺͼग़͞Εͳ͍ ϛογϣϯॲཧϫʔΧ͸ݺͼग़͞Εͳ͍ ͜͜Ͱαʔό࣮૷͔Β཭ΕͯɺαʔϏεશମͷ࿩΁ࢹ఺Λ޿͛·͢ɻ Ծʹݱۚ׵ࢉͰ 1000 ສԁ / 1 ࣌ؒചΓ্͛ΔΠϕϯτΨνϟظؒதʹɺϛογϣϯॲཧͷ࣮૷͕ Կ͔͠Βࣦഊ͢ΔΠϯγσϯτ͕ൃੜͨ࣌͠ɺҰॹʹΨνϟ΋Ҿ͚ͳ͘ͳΔ࣮૷ͩͬͨͱ͠·͢ɻ͜ ͷΠϯγσϯτରԠʹ 2 ͔͔࣌ؒͬͨͱ͢Δͱɺ୯७ܭࢉͰ 2000 ສଛࣦΛੜΉΘ͚Ͱ͢ *9ɻٯʹ ϛογϣϯॲཧ͕Ψνϟॲཧʹରͯ͠෼཭͞Ε͍ͯΕ͹ɺো֐ൣғΛϛογϣϯ಺ʹऩΊΔ͜ͱ͕Ͱ ͖·͢ɻͦͯ֘͠౰࣮૷Λमਖ਼͠ϛογϣϯॲཧΛϦτϥΠ͢Ε͹ ݁Ռతʹ Ϣʔβʔ΁ਖ਼͍͠σʔ λ൓өΛߦ͏͜ͱ͕Ͱ͖·͢ *10ɻ ݴ͍׵͑Ε͹ɺΨνϟͱϛογϣϯͷΑ͏ͳυϝΠϯʹ෼཭͞Εͨσʔλͷ੔߹ԽରԠΛ͢Δͱ ͖ɺͪ͜Βͷύλʔϯͷ࣮૷Λߦ͏͜ͱͰো֐࣌ͷӨڹൣғΛ཈͑Δ͜ͱ͕ݟࠐΊ·͢ɻ *9 ͍ͣΕʹͤΑൃੜ͢Δɺϛογϣϯॲཧ࣮૷ࣦഊʹ·ͭΘΔӡӦ΁ͷෆ৴ײ͔Βൃੜ͢Δଛࣦ͸আ͖·͢ *10 ͨͩ͠ɺ໰୊ͷϛογϣϯॲཧʹႈ౳ੑ͕ͳ͔ͬͨΓشൃੑσʔλʹґଘ͍ͯͨ͠Γ͢Δ৔߹͸ʜ 26
  30. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.3 3 ͭͷ੔߹Խઓུ ˙ίϥϜ: αʔϏεͷ࢓༷ఆٛͱ݁Ռ੔߹ͷ࿩ (

    ͜͜·Ͱॻ͍͓͍ͯͯඇৗʹਃ͠༁ͳ͍ͱ͜ΖͰ͕͢ ) ͜Ε͸αʔϏεͱը໘ઃܭʹΑͬͯ ͸ ઃܭ͕ڐ͞Εͳ͍ύλʔϯ ʹͳΓ·͢ɻ ͨͱ͑͹࣍ͷΑ͏ͳը໘ઃܭɺΫϥΠΞϯτઃܭΛ͍ͯ͠Δ৔߹ɺ݁Ռ੔߹ͳσʔλߋ৽͸ෆ ޲͖Ͱ͢ɻ • Ψνϟը໘ͷԣʹϛογϣϯୡ੒֬ೝը໘͕͋Δ • ϗʔϜը໘΍ϛογϣϯୡ੒ը໘ͷσʔλΛ࠶औಘ͢ΔํࣜʹɺPush ܕͷΞϓϩʔν (e.g. Web socket or ΠϕϯτϦεφ) ͔ɺϙʔϦϯάͳΞϓϩʔνΛऔ͍ͬͯͳ͍ ͨͩ͠ڧҾͳಀ͛ಓͱͯ͠ɺϛογϣϯॲཧͷਖ਼ৗܥॲཧ͚ͩΨνϟॲཧʹؚΊΔ͜ͱͰɺ্ ͷཁ݅Ͱ΋࣮૷Ͱ͖ΔՄೳੑ͸͋Γ·͢ɻ ਤ 2.5: Ψνϟϑϩʔ with ϛογϣϯॲཧ (ਖ਼ৗܥ): γʔέϯεਤ 27
  31. ୈ 2 ষ ΨνϟγεςϜͰݟΔɺϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.4 ϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ 2.4 ϚΠΫϩαʔϏεͱ੔߹ੑͷ࿩ ͜͜·ͰϚΠΫϩαʔϏεͱ͍͏୯ޠ͕Ұͭ΋ग़͖͍ͯͯͳ͍ͷͰɺ͜ͷࢠͷ࿩Λ͠·͢ɻϚΠ ΫϩαʔϏεͱ͸ɺϏδωεཁ݅ʹ෼͚ͯখ͞ͳαʔϏεΛ࡞Γɺͦͷू߹ͱͯ͠ҰͭͷΞϓϦ

    έʔγϣϯ (e.g. ιʔγϟϧήʔϜ) Λߏங͢Δઃܭख๏Ͱ͢ɻରԠ͢Δ༻ޠͱͯ͠ ϞϊϦγοΫ (monolithic) ͱ͍͏΋ͷ͕͋Γɺ͜Ε͸ΞϓϦέʔγϣϯશମΛҰͭͷαʔϏεͰදݱ͢Δɺͱ͍͏ ઃܭख๏Ͱ͢ɻ ࠓճͷઃఆ؀ڥ͸ɺࢿܾۚࡁαʔϏεɺ ιʔγϟϧήʔϜͷϝΠϯॲཧαʔϏεɺ ιʔγϟϧ ήʔϜͷϛογϣϯॲཧαʔϏεɺ ΞΧ΢ϯτͷೝূ/ೝՄαʔϏε ʹ෼͔Ε͍ͯΔͱ͍͏఺ͰϚΠ ΫϩαʔϏεدΓͷ࣮૷Λ͍ͯ͠·͢ɻ͜Ε͕ϞϊϦγοΫͰ͋Ε͹શ෦ͻͬ͘ΔΊͯɺ͑ʔ͍ɺͱ ҰͭͷαʔόΞϓϦέʔγϣϯʹ৐͔ͬΔײ͡ʹͳΓ·͢ɻ ࠓ·Ͱ্͖͛ͯͨ 3 ͭͷ੔߹ੑͷ࿩Ͱ͸ɺҰͭͷαʔϏεͰ׬݁͢Δ΋ͷ (Ψνϟॲཧ) ͱɺෳ਺ ͷαʔϏεΛ·͕ͨΔ΋ͷ (՝ۚॲཧɺϛογϣϯॲཧ) ʹ෼͚Δ͜ͱ͕Ͱ͖·͢ɻ͜͜Ͱ࿩͍ͨ͠ ͷ͸ෳ਺ͷαʔϏεʹ·͕ͨΔํͰɺϚΠΫϩαʔϏεͰ͸සग़͢Δ੔߹ੑͷॲཧύλʔϯʹͳΓ ·͢ɻ ҰൠʹϚΠΫϩαʔϏεͷ੔߹ੑपΓͷॲཧ͸ϞϊϦεαʔϏεͷ੔߹ॲཧपΓʹൺ΂ͯෳࡶʹͳ Δ͜ͱ͕஌ΒΕ͓ͯΓɺγʔέϯεਤ΍঺հ಺༰ΛಡΉͱɺҰͭͷαʔϏεͰ׬݁͢Δ΋ͷʹൺ΂ͯ ෳࡶͰ͋Δ͜ͱ͕͝ཧղ͍͚ͨͩΔͱࢥ͍·͢ɻ ͭ·ΓԿ͕ݴ͍͍͔ͨͱ͍͏ͱɺϚΠΫϩαʔϏε͸ϞϊϦεαʔϏεʹରͯ͠੔߹ੑઓུͱ͍͏ ఺Ͱ͸ઃܭɾ࣮૷ίετΛଟ͘ࢧ෷͏ඞཁ͕͋Δɺͱ͍͏͜ͱͰ͢ɻ͜ͷͨΊɺεέʔϦϯά΍୯Ұ ো֐఺ͷݮগͳͲͷએ఻޿ࠂ͔Β҆қʹϚΠΫϩαʔϏεʹඈͼͭ͘ͷͰ͸ͳ͘ɺ͍ͬͨΜαʔϏ εͱͦͷ࢓༷Λ੔ཧͯ͠ϚΠΫϩԽͷํ਑ΛܾΊ͍ͯ͘ඞཁ͕͋Δͱ͍͏͜ͱͰ͢ɻͦ͏Ͱͳ͍ͱ Ԇʑʹ੔߹ੑʹ͍ͭͯͷઃܭɾ࣮૷ʹίετΛऔΒΕɺ݁Ռͱͯ͠αʔϏεͷ඼࣭ɺఏڙ଎౓͕Ϟϊ ϦγοΫΑΓ΋ྼͬͯ͠·͏ࣗମΛҾ͖ى͔͜͠Ͷ·ͤΜɻ 2.5 ͓ΘΓ ࠓճ͸ΨνϟΛ୊ࡐʹͯ͠੔߹ੑʹ·ͭΘΔ࿩ΛΨνϟΨνϟͱ঺հ͖ͯ͠·ͨ͠ɻ͓ͦΒ͘ຊষ ΛݟΔਓ͸ɺαʔϏεͷ࢓༷ʹै࣮ͬͯ૷ํ਑ΛܾΊΔํͳͷ͔ͳͱࢥ͍·͕͢ɺͦΜͳํ΁ͷࢀߟ ʹͳΕ͹޾͍Ͱ͢ɻ ͱ͜ΖͰࢲ͸ΨνϟΛఱҪ *11 ͠·ͨ͠ɻޙչ͸͍ͯ͠·ͤΜɻ *11 Ұ෦ͷιʔγϟϧήʔϜͳͲͰ͸͋ΔҰఆ (e.g. 9 ສԁ) ෼ͷΨνϟΛҾ͘ͱɺͦͷΨνϟͷ໨ۄͱͳΔΩϟϥΫλΛ ަ׵Ͱ͖ΔγεςϜ͕ଘࡏ͍ͯ͠·͢ɻͦͷจ຺Ͱ "ͨ͘͞ΜҾ͍͓ͨ৘͚Ͱ͓໨౰ͯͷΩϟϥΫλΛ֫ಘ͢Δ" ͜ͱ ΛɺఱҪ͢Δɺͱදݱ͠·͢ɻ 28
  32. 29

  33. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.1 ͸͡Ίʹ 3.1 ͸͡Ίʹ

    ຊॻͰ͸ Google Cloud Ͱఏڙ͞Ε͍ͯΔ Cloud Build Λ࢖͍౗ͨ͢Ίͷ͞·͟·ͳ৘ใΛ঺հ ͠·͢ɻ ର৅ಡऀ • CI/CD ͕Կ͔Λ஌͓ͬͯΓɺར༻͓Αͼߏஙͷܦݧ͕͋Δ • Cloud Build ͷجຊతͳಈ࡞ɾ࢖͍ํΛཧղ͍ͯ͠Δ ஫ҙ ຊষʹ͸ Cloud Build ͷެࣜυΩϡϝϯτʹهࡌͷͳ͍಺༰Λଟ਺঺հ͍ͯ͠·͢ɻͦΕΒ͸ࣥ ච࣌఺ (2021 ೥ 12 ݄ 21 ೔) ʹ චऀ (riddle) ͕࣮ػͰ֬ೝͨ͠΋ͷͷͨΊɺαʔϏεͷঢ়گ΍Ξο ϓσʔτʹΑͬͯࠓޙมߋʹͳΔՄೳੑ͕͋Δ఺ʹ͝஫ҙ͍ͩ͘͞ɻ ·ͨ Cloud Build ʹ͸ϓϥΠϕʔτϓʔϧ*1΍ϋΠϒϦοτϓʔϧ*2ͱ͍͏ػೳ͕ଘࡏ͠·͕͢ɺ ຊষͰ͸͜ΕΒΛѻ͍ͬͯͳ͍఺ʹ͝஫ҙ͍ͩ͘͞ɻ߲ͨͩ͠໨ʹΑͬͯ͸ൺֱͷͨΊ৘ใΛܝࡌ͠ ͍ͯΔՕॴ΋͋Γ·͢ɻ Cloud Build ͱ͸ Google Cloud Ͱఏڙ͞Ε͍ͯΔϑϧϚωʔδυͳϏϧυ؀ڥͰ͢ɻΠϯελϯεͷ؅ཧ΍ɺ໘౗ ͳઃఆͳ͠ʹ CI/CD ͕࣮ߦͰ͖·͢ɻ(ಉछͷαʔϏεʹ CircleCI ΍ GitHub Actionsɺ AWS ͷ CodeBuild ͳͲ͕ଘࡏ͠·͢) ಛ௃ͱͯ͠͸ Google Cloud ͷઃఆΛαʔϏεΞΧ΢ϯτܦ༝ͰηΩϡΞʹߦ͑Δ఺͕͋͛ΒΕɺ Google Kubernetes Engine ΍ Cloud Run ΁ͷγʔϜϨεͳσϓϩΠ͕ηΩϡΞʹߦ͑Δͷ͸ັྗ ͷ 1 ͭͰ͠ΐ͏ɻ·ͨϏϧυδϣϒͷ࡞੒ʹඞཁͳઃఆ΋؆୯ͰϦετ 3.1 ʹॻ͍ͨ಺༰͚ͩͰ࠷௿ *1 https://cloud.google.com/build/docs/private-pools/private-pools-overview *2 https://cloud.google.com/build/docs/hybrid/overview 31
  34. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive ݶͷδϣϒΛ࣮ߦͰ͖·͢ɻ Ϧετ 3.1: cloudbuild.yaml steps: - name: "bash" args: - -c - echo - "hello" 3.2 Cloud Build Deep Dive ຊষͰ͸ Cloud Build ͷػೳ΍ཪଆͷ͘͠Έʹ͍ͭͯ঺հ͠·͢ɻ ΫΠοΫελʔτϏϧυ ΫΠοΫελʔτϏϧυ*3ͱ͸͋Β͔͡Ίىಈ͍ͯ͠Δ Google Compute Engine (Ҏ߱ GCE ͱུ͠·͢) ্Ͱ͙͢ʹϏϧυδϣϒΛ։࢝Ͱ͖Δ Cloud Build ͷػೳͰ͢ɻ(ϓϩϏδϣχϯάͷ ஗Ԇͱ͸ GCE ͕ىಈ͢Δ·Ͱͷ଴ͪ࣌ؒͷ͜ͱͰ͢) ࣥච࣌఺Ͱ͸ϚγϯλΠϓ͕ e2-medium ͷ GCE ͷΈ͕ର৅Ͱɺ΄͔ͷϚγϯλΠϓ*4ΑΓ΋ߴ ଎ʹϏϧυΛ։࢝Ͱ͖·͢ɻ(͋͘·Ͱىಈ͕ૣ͍͚ͩͳͷͰ CPU ΍ ϝϞϦ͕ඞཁͳ৔߹͸ద੾ͳ ϚγϯλΠϓΛ࢖͏ඞཁ͕͋Γ·͢) *3 https://cloud.google.com/build/pricing *4 https://cloud.google.com/build/docs/api/reference/rest/v1/projects.builds#machinetype 32
  35. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive Ͳͷ͙Β͍ىಈ͕଎͍ͷ͔ʁ "hello" Λग़ྗ͢Δ͚ͩͷδϣϒΛ࢖֤ͬͯϚγϯλΠϓ (Cloud Build Ͱ࢖༻Մೳͳ΋ͷ) ͰͲͷ ఔ౓ɺىಈʹ͕͔͔͍࣌ؒͬͯΔͷ͔Λௐࠪ͠·ͨ͠ɻ(ϓϥΠϕʔτϓʔϧ΍ϋΠϒϦοτϓʔϧ Ͱ࢖༻ՄೳͳϚγϯλΠϓ͸ܭଌ͍ͯ͠·ͤΜ) Ϧετ 3.2: cloudbuild.yaml steps: - name: "bash" args: - -c - echo - "hello" ਤ 3.1: ϚγϯλΠϓ͝ͱͷ࣮ߦ࣌ؒ • ίϚϯυ࣮ߦ࣌ؒ : time gcloud builds submit --machine-type=MACHINE_TYPE Λ࣮ ߦ͔ͯ͠Β݁Ռ͕ؼͬͯ͘Δ·Ͱͷ࣌ؒ 33
  36. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive • δϣϒ࣮ߦ࣌ؒ : Cloud Build δϣϒͷॲཧʹ͔͔ͬͨ࣌ؒ • GCE ͷϓϩϏδϣχϯά࣌ؒ : ίϚϯυ࣮ߦ͔࣌ؒΒδϣϒ࣮ߦ࣌ؒΛҾ͍ͨ࣌ؒ – ݫີʹ͸ϑΝΠϧΛ Google Cloud Storage (Ҏ߱͸ GCS ͱུ͠·͢) ʹΞοϓϩʔυ͢ Δؚ͕࣌ؒ·ΕΔͨΊϓϩϏδϣχϯά࣌ؒ͸΋͏গ͠୹͘ͳΓ·͢ • هࡌ͍ͯ͠Δ࣌ؒ͸͢΂ͯෳ਺ճ࣮ߦ্ͨ͠Ͱͷதԝ஋Ͱ͢ ͜ͷ݁Ռ͔ΒϚγϯλΠϓ͕ e2-medium ͷ৔߹͸ GCE ͷϓϩϏδϣχϯά͕࣌ؒѹ౗తʹ଎ ͍͜ͱ͕Θ͔Γ·͢ɻͦͷͨΊδϣϒ͕ܰྔͰϚγϯϦιʔεΛඞཁͱ͠ͳ͍৔߹͸ɺՄೳͳݶΓ - -machine-type Λ࢖Θͣʹ࣮ߦ͢΂͖Ͱ͠ΐ͏ɻ *5(ఆٛϑΝΠϧΛར༻͢Δ৔߹͸ options.mach ineType Λ࢖༻͠ͳ͍) Cloud Build ͷཪଆͷ͘͠Έ Cloud Build ͕ͲͷΑ͏ʹಈ͍͍ͯΔͷ͔Λ஌ΔͨΊʹɺϦετ 3.3 Λ࢖ͬͯδϣϒΛىಈͯ͠Έ ·͢ɻ͜ͷఆٛϑΝΠϧ͸ҎԼͷ 2 ͭͷεςοϓͰ੒Γཱ͍ͬͯ·͢ɻ 1. docker-compose.yaml ʹఆٛ͞Εͨ Apache Λىಈ͢Δ 2. Hello ͱදࣔ͢Δ Ϧετ 3.3: cloudbuild.yaml steps: - id: Setup Middlewares name: "docker/compose" args: - -f - docker-compose.yaml - up - --build - -d - id: test step name: "gcr.io/google.com/cloudsdktool/cloud-sdk:cloudbuild_cache" entrypoint: bash args: - -c - | echo "hello test" waitFor: ["-"] *5 e2-medium Λ໌ࣔతʹ࢖͏ઃఆ͸ͳ͍ͷͰɺ࢖͍͍ͨͱ͖͸ machine-type Λઃఆ͠ͳ͍Α͏ʹ͠·͠ΐ͏ 34
  37. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive Ϧετ 3.4: docker-compose.yaml version: "3.8" services: http1: container_name: http1 image: httpd networks: default: external: name: cloudbuild ਤ 3.2 ͸Ϧετ 3.3 Ͱ࣮ߦ͞Ε֤ͨϏϧυεςοϓ͕ Cloud Build ͰͲͷΑ͏ʹಈ࡞͍ͯ͠Δ͔ Λਤࣔͨ͠ΠϥετͰ͢ɻ ਤ 3.2: Cloud Build ͕Քಇ͢Δ GCE ͷ಺෦ߏ଄ Cloud Build ͸ Google ʹ؅ཧ͞Εͨ GCE Ͱಈ͍͍ͯ·͢ɻ֤Ϗϧυεςοϓ͝ͱʹίϯςφ ͕ step_਺ࣈ ͱ͍͏໊લͰىಈ͠ɺ͜ͷίϯςφͷதͰ Ϧετ 3.3 Ͱఆٛ͞ΕͨίϚϯυ͕࣮ߦ ͞Ε·͢ɻͦͯ͠ Ϧετ 3.4 Ͱఆٛ͞Εͨ Apache ίϯςφ͸ http1 ͱ͍͏໊લͰىಈ͠ɺϏϧυ εςοϓͷίϯςφͱಉ༷ʹ cloudbuild ʹॴଐ͢ΔωοτϫʔΫΠϯλʔϑΣʔε (NIC) Λ࣋ͪ ·͢ɻ 35
  38. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive   ˙ϝϞ: cloudbuild network ʹ͍ͭͯ ωοτϫʔΫ໊ ͷ cloudbuilda ͸ Google Cloud ʹΑͬͯ༧໿͞Ε͍ͯ·͢ɻ ·ͨɺcloudbuild ωοτϫʔΫʹॴଐ͢Δίϯςφ͸ΞϓϦέʔγϣϯͷσϑΥϧτೝূ৘ใ (ADCb) ͕࢖༻Ͱ͖ΔͨΊɺCloud Build Λ࣮ߦ͍ͯ͠ΔαʔϏεΞΧ΢ϯτͷݖݶ͕࢖༻Ͱ͖ ·͢ɻ a https://cloud.google.com/build/docs/build-config?hl=ja#network b https://cloud.google.com/docs/authentication/production   ͢΂ͯͷίϯςφ͕ಉҰͷωοτϫʔΫ (cloudbuild) ʹଘࡏ͢ΔͨΊɺϦετ 3.5 ͷΑ͏ʹϏϧ υεςοϓͷίϯςφ͔Β http1 ίϯςφ΁ͷ௨৴΋ՄೳͰ͢ɻ Ϧετ 3.5: step_0 ίϯςφ͔Β http1 ίϯςφʹ޲͚ͯ curl Λ࣮ߦͨ݁͠Ռ $ curl http1 <html><body><h1>It works!</h1></body></html> ͦͷͨΊෳ਺ͷίϯςφΛಉ࣌ʹىಈ͢Δํ๏Λ༻͍Δ͜ͱͰ Cloud Build ্ʹσʔλϕʔε΍ key-value ετΞͳͲͷϛυϧ΢ΣΞΛಈ࡞ͤͨ͞ΓɺϚΠΫϩαʔϏεΛىಈͯ͠ͷςετ͕Մೳ ʹͳΓ·͢ɻ metadata ίϯςφ Cloud Build ্Ͱ͸Ϣʔβʔ͕ఆٛ͠ͳͯ͘΋ metadata ίϯςφ͕ىಈ͍ͯ͠·͢ɻ͜Ε͸ GCE ͷ VM ϝλσʔλ*6Λฦ٫͢ΔΞϓϦέʔγϣϯͰ͢ɻ ௨ৗ͸ϝλσʔλαʔόͱΑ͹ΕΔ http://metadata.google.internal (·ͨ͸ 169.254.169.254) Ͱ Listen ͢ΔαʔόʹΞΫηε͠ GCE ࣗ਎ͷ৘ใΛऔಘ͢ΔͷͰ͕͢ɺCloud Build ্ͷ֤ίϯ ςφͷ৔߹͸ಉ༷ͷ URL Λୟ͍ͯ΋ϦΫΤετ͸ metadata ίϯςφʹసૹ͞Ε·͢ɻ ࣮ࡍʹίϚϯυΛୟ͍ͯΈΔͱɺҟͳͬͨѼઌʹ௨৴͕޲͍͍ͯΔࣄ͕Θ͔Γ·͢ɻ Ϧετ 3.13: ίϯςφ಺Ͱϝλσʔλͷ໊લղܾΛͨ͠৔߹ $ nslookup metadata.google.internal Server: 127.0.0.11 *6 https://cloud.google.com/compute/docs/metadata/overview 36
  39. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive Address: 127.0.0.11#53 Non-authoritative answer: Name: metadata.google.internal Address: 192.168.10.5 $ docker inspect metadata -f "{{json .NetworkSettings.Networks.cloudbuild.IPAddress}}" "192.168.10.5" Ϧετ 3.13: GCE ಺Ͱϝλσʔλͷ໊લղܾΛͨ͠৔߹ $ nslookup metadata.google.internal Server: 169.254.169.254 Address: 169.254.169.254#53 Non-authoritative answer: Name: metadata.google.internal Address: 169.254.169.254 Docker outside of Docker ֤Ϗϧυεςοϓ্ͷίϯςφͰ͸ (Docker ίϚϯυ͕Πϯετʔϧ͞Ε͍ͯΕ͹) Docker ίϚ ϯυͷ࣮ߦ͕ՄೳͰ͢ɻ Ϧετ 3.13: ίϯςφ಺Ͱͷ docker ps ͷ݁Ռ $ docker ps --format="table {{.Names}}\t{{.Image}}\t{{.Command}}" NAMES IMAGE COMMAND http1 httpd "httpd-foreground" step_0 docker/compose "sh /usr/local/bin/dʜ" step_1 gcr.io/google.com/cloudsdktool/cloud-sdk "bash -c ’echo hellʜ" metadata gcr.io/cloud-builders/metadata "/metadata" ͔͠͠ίϯςφ಺ʹ͸ Docker σʔϞϯ͕ଘࡏͤͣɺϦϞʔτͷ Docker σʔϞϯΛ࢖͏ͨΊͷઃ ఆ΋ೖ͍ͬͯ·ͤΜɻͰ͸͍͍ͬͨͲ͏΍ͬͯϏϧυεςοϓ্ͷίϯςφ͸ Docker σʔϞϯʹ઀ ଓ͍ͯ͠ΔͷͰ͠ΐ͏͔ʁ Cloud Build Ͱ͸ Docker outside of Docker (Ҏ߱͸ DooD ͱུ͠·͢) ͱ͍͏ํ๏Λ࢖ͬͯ ͜ΕΛ࣮ݱ͍ͯ͠·͢ɻDooD ͸ίϯςφ͔Βϗετͷ docker.sock (/var/run/docker.sock*7) Λ *7 https://docs.docker.jp/engine/userguide/basics.html#docker-unix 37
  40. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.2 Cloud Build Deep

    Dive ϘϦϡʔϜϚ΢ϯτ͠ɺίϯςφ؀ڥͷ docker ίϚϯυͷ࣮ߦΛϗετଆͰ࣮ݱ͢ΔํࣜͰ͢ɻ ͜ΕʹΑΓ CI ؀ڥࣗମ͕ίϯςφԽͨ͠ͱͯ͠΋ɺDocker ϓϩηεΛίϯςφ಺Ͱ্ཱͪ͛Δ ඞཁͳ͘ɺϗετͷ Docker σʔϞϯʹ໋ྩΛ౤͛Δ͜ͱͰ৽ͨͳίϯςφ (δϣϒͳͲ) Λ CI ίϯ ςφ͔Β࡞੒Ͱ͖·͢ɻ   ˙ϝϞ: Docker in Docker (DinD) DooD ͷ΄͔ʹ Docker in Docker (DinD) a ͱݺ͹ΕΔίϯςφ಺Ͱ Docker σʔϞϯΛىಈ ͠ɺίϯςφͷதͰ͞ΒʹίϯςφΛىಈ͢Δํ๏΋͋Γ·͢ɻ(Cloud Build Ͱ͸͜ͷํࣜ͸ ࢖ΘΕ͍ͯ·ͤΜ) ਤ 3.3: Docker in Docker ͷߏ੒ਤ a http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/   લ߲ͷʮCloud Build ͷཪଆͷ͘͠Έʯ Ͱࣔͨ͠ͱ͓ΓɺϏϧυεςοϓͰىಈ֤ͨ͠ίϯςφ ͸ docker.sock (/var/run/docker.sock) ΛϚ΢ϯτ͍ͯ͠ΔͨΊɺίϯςφ಺ʹ docker ίϚϯυ͞ 38
  41. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    ͑ଘࡏ͍ͯ͠Ε͹ϗετଆͷ Docker Λ௨ͯ͡ίϯςφͷૢ࡞͕ՄೳͰ͢ɻ Ϧετ 3.13: Ϗϧυεςοϓ 1 ͷδϣϒ͕ /var/run/docker.sock ΛϚ΢ϯτ͍ͯ͠Δ༷ࢠ $ docker inspect step_1 \ | jq -r ’.[]|.Mounts|.[]|select(.Source == "/var/run/docker.sock")’ { "Type": "bind", "Source": "/var/run/docker.sock", "Destination": "/var/run/docker.sock", "Mode": "", "RW": false, "Propagation": "rprivate" } 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏ 1. Ϗϧυ݁ՌΛ௨஌͢Δ Cloud Build Ͱςετ΍ϏϧυΛࣗಈԽͰ͖ͨΒ࣍͸Ϗϧυ݁Ռͷ௨஌Λߦ͍͍ͨͰ͢Ͷɻ Google Cloud Ͱ͸ Cloud Build Notifiers*8 ͱ͍͏ Cloud Run ্Ͱಈ࡞͢Δ௨஌πʔϧ͕ఏ ڙ͞Ε͍ͯ·͢ɻ͜ͷπʔϧ͸ҎԼͷ௨஌ઌʹରԠ͍ͯ͠ΔͷͰɺ΄ͱΜͲͷϢʔεέʔεͰ͸͜Ε Λ࢖͑͹ྑ͍Ͱ͠ΐ͏ɻ • BigQuery • HTTP • Slack • SMTP *8 https://github.com/GoogleCloudPlatform/cloud-build-notifiers 39
  42. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    ·ͨ͜ͷπʔϧ͸ Go Ͱॻ͔Ε͓ͯΓ಺༰΋γϯϓϧͳͨΊɺϝοηʔδΛΧελϚΠζ͍ͨ͠৔ ߹͸ Git Clone ͯ͠खΛೖΕΔͱྑ͍Ͱ͠ΐ͏ɻ ࢀߟ·Ͱʹզʑͷ؀ڥͰ͸ҎԼͷΑ͏ͳ௨஌Λߦ͍ͬͯ·͢ɻίʔυͷ؅ཧʹ GitHub Λ࢖ͬͯ ͍ΔͨΊɺͲͷ Pull Request Ͱ୭͕࣮ߦͨ͠ͷ͔͕Θ͔Δͱࣦഊ࣌ʹݪҼ͕௥͍΍͘͢ॏๅ͍ͯ͠ ·͢ɻ ਤ 3.4: ݱ৔Ͱͷ Slack ௨஌ 2. Τϥʔऴྃ࣌ʹཧ༝Λ௨஌͢Δ ଓ͍ͯ͸௨஌ͷதͰ΋ಛʹδϣϒ͕ࣦഊͨ͠ࡍͷ࿩Ͱ͢ɻδϣϒ͕ࣦഊͨ͠ࡍ͸δϣϒ͕ࣦഊͨ͠ ཧ༝Λ஌Γ͍ͨͰ͢ΑͶɻ Cloud Build ͷ REST Ϧιʔεͷ projects.builds ʹ͸ FailureInfo*9 ͱ͍͏ϑΟʔϧυʹ ࣦഊཧ༝ؚ͕·Ε͍ͯ·͢ɻͨͩ͠ FailureInfo ͸ʮͲͷϏϧυεςοϓͰࣦഊͨ͠ͷ͔ʁʯͱ ʮऴྃίʔυʯ͔͠σʔλΛ֨ೲ͍ͯ͠ͳ͍ͨΊɺ۩ମతʹʮͳͥδϣϒ͕ࣦഊͨ͠ͷ͔ʁʯ ʹ͍ͭ ͯ͸ϏϧυϩάΛ௚઀֬ೝ͠ͳ͍ͱݪҼΛ೺ѲͰ͖·ͤΜɻ *9 https://cloud.google.com/build/docs/api/reference/rest/v1/projects.builds#Build.FailureInfo 40
  43. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    Ϧετ 3.13: failureInfo Ͱग़ྗ͞ΕͨΤϥʔཧ༝ failureInfo: detail: ’Build step failure: build step 1 "gcr.io/xxx/xxx:xxx" failed: step exited with non-zero status: 1’ δϣϒࣦഊ࣌ʹʮεςοϓ಺ͷͲͷίϚϯυ࣮ߦͰࣦഊͨ͠ͷ͔ʁʯ΍ʮίϚϯυ݁Ռͷग़ྗʯ Λ஌ΔͨΊʹ͸গʑτϦοΩʔͳํ๏Λߦ͏ඞཁ͕͋Γ·͢ɻͦ͜ͰຊষͰ͸ GitHub ͷ Pull Request ΛτϦΨͱͨ͠ϏϧυͰൃੜͨ͠ΤϥʔΛίϝϯτͱͯ͠౤ߘ͢Δํ๏Λ঺հ͠·͢ɻ   ˙ϝϞ: Pull Request τϦΨҎ֎Ͱ΋࢖͑Δͷ͔ʁ ͜Ε͔Β঺հ͢Δํ๏͸΄͔ͷτϦΨͷ৔߹Ͱ΋ར༻ՄೳͰ͢ɻͨͩ͠ίϝϯτ౤ߘ෦෼ΛҰ ෦มߋ͢Δඞཁ͕͋Γ·͢ɻ͜͜ͰτϦΨΛݶఆ͍ͯ͠Δͷ͸ Pull Request ͷ URL Λऔಘ͢ Δͷʹ Cloud Build ͷ _PR_NUMBER ม਺aΛ࢖༻Ͱ͖Δͷ͕౎߹͕ྑ͍͚ͩͰɺͦΕҎ֎ ʹཧ༝͸͋Γ·ͤΜɻ a https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values   ۩ମతͳ௨஌ͷϑϩʔ ਤ 3.5: Τϥʔऴྃ࣌ͷ௨஌ϑϩʔ ۩ମతͳྲྀΕ͸ҎԼͷͱ͓ΓͰ͢ɻ 41
  44. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    1. GitHub ͷ Pull Request ʹมߋΛ Push 2. Cloud Build Ͱςετͷ࣮ࢪ 3. Cloud Build ͕ࣦഊͨ͠৔߹͸ GitHub ʹίϝϯτΛ౤ߘ 4. GitHub ͷίϝϯτΛ Slack ʹ௨஌ ˞ 4 ͷ Slack ͱ GitHub ͷ࿈ܞʹ͍ͭͯ͸ຊॻͰ͸ѻ͍·ͤΜɻSlack ެࣜαΠτͷʮGitHub ͱ Slack Λ࿈ܞͤ͞Δʯ *10Λࢀߟʹ͍ͯͩ͘͠͞ɻ Ϗϧυεςοϓ಺ͰΤϥʔऴྃΛϋϯυϦϯά͢Δ Ͱ͸ͬͦ͘͞Ϗϧυεςοϓ಺ͷΤϥʔΛ GitHub ʹ౤ߘ͢Δํ๏Λݟ͍͖ͯ·͠ΐ͏ɻ Ϧετ 3.7: cloudbuild.yaml availableSecrets: # ࣄલʹ Secret Manager ʹ GitHub ͷϓϥΠϕʔτΞΫηετʔΫϯΛ֨ೲ͓ͯ͘͠ # ΞΫηετʔΫϯʹ͸ Pull Request ΁ͷίϝϯτ͕ՄೳͳݖݶΛ෇༩͠·͢ secretManager: - versionName: projects/${PROJECT_ID}/secrets/github-access-token/versions/latest env: "GITHUB_TOKEN" substitutions: _COMMIT_URL: "$(commit.html_url)" steps: - id: failure step name: gcr.io/cloud-builders/curl secretEnv: ["GITHUB_TOKEN"] entrypoint: "bash" args: - -c - | set -o pipefail # pipe ॲཧͰࣦഊͨ͠Βऴྃ͢Δ set -o nounset # ม਺͕ະఆٛͷ৔߹͸ऴྃ͢Δ set -o errexit # ίϚϯυͷ࣮ߦʹࣦഊͨ͠Βऴྃ͢Δ # ऴྃίʔυΛνΣοΫ͠ɺਖ਼ৗऴྃ(0)Ҏ֎͸ GitHub ʹίϝϯτΛ౤ߘ͢Δ function catch () { # ؔ਺Λݺͼग़͢௚લͷऴྃίʔυΛอଘ͓ͯ͘͠ original_exit=$? if [ "$original_exit" -eq 0 ]; then # ਖ਼ৗऴྃ(0)ͷ৔߹͸Կ΋͠ͳ͍ true else # ҟৗऴྃͷ৔߹͸GitHubʹ౤ߘ͢Δ(github-commenterͳͲΛ࢖͏ͷ΋ྑ͍) *10 https://slack.com/intl/ja-jp/help/articles/232289568-GitHub-%E3%81%A8-Slack- %E3%82%92%E9%80%A3%E6%90%BA%E3%81%95%E3%81%9B%E3%82%8B 42
  45. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    # url ͔Β Φʔφʔ ͱ ϦϙδτϦ໊ Λऔಘ͢Δ # ˞ιʔεϦϙδτϦ͕ GitHub Ͱ͋ΔલఏͰ͢ read owner repo <<< $(echo "$_COMMIT_URL" \ | sed -re "s@.*github\.com/([^/]*)/([^/]*)/commit.*@\1 \2@g") pr=$_PR_NUMBER curl_opts=( -s -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $$GITHUB_TOKEN" -d @- "https://api.github.com/repos/${owner}/${repo}/issues/${pr}/comments" ) # GitHub ʹίϝϯτΛ౤ߘ curl "${curl_opts[@]}" << CURL_BODY { # վߦΛߦ͏ͨΊ֤ߦͷ຤ඌʹ \n Λ෇༩͢Δ "body": "$(cat $result_file | sed -e ’s/$/\\n/g’)" } CURL_BODY fi # ݩʑͷऴྃίʔυͰδϣϒΛऴྃ͢Δ exit "$original_exit" } # γΣϧͷऴྃΛΩϟον͠ɺऴྃલʹ _catch ؔ਺Λݺͼग़͢ trap catch EXIT # Ҏ߱͸͜ͷϏϧυεςοϓͰ࣮ࡍʹ࣮ߦ͍ͨ͠ॲཧΛॻ͘ # ؔ਺ʹ͍ͯ͠Δͷ͸Τϥʔग़ྗΛͻͱ·ͱΊʹ͠΍͍ͨ͢Ί function main() { echo "test step 1 - success" echo "test step 2 - success" # ͜͜Ͱࣦഊͤ͞Δ echo "test step 3 - fail" >&2 echo "this step is failed" >&2 echo "reason is ’hoge hoge hoge’" >&2 exit 1 } # ίϚϯυͷΤϥʔ݁ՌΛ֨ೲ͢ΔϑΝΠϧ result_file="/tmp/result" # main ؔ਺Λݺͼग़ඪͯ͠ඪ४ΤϥʔΛϑΝΠϧʹग़ྗ͠·͢ main 2> "$result_file" ্هͷఆٛϑΝΠϧΛ࣮ߦ͢Δͱδϣϒ͕ࣦഊͯ͠ҎԼͷίϝϯτ͕ Pull Request ʹ౤ߘ͞Ε ·͢ɻ 43
  46. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    ਤ 3.6: GitHub ʹΤϥʔΛ౤ߘͨ͠ਤ ࢲͷνʔϜͰ͸ GitHub Markdown ͰଟগϨΠΞ΢τΛมߋͯ͜͠ͷΑ͏ͳίϝϯτΛ౤ߘͯ͠ ͍·͢ɻ ਤ 3.7: GitHub ʹΤϥʔΛ౤ߘͨ͠ਤ_ݱ৔   ˙ϝϞ: Τϥʔ௨஌ʹ Cloud Build Notifiers Λ࢖Θͳ͍ཧ༝ Cloud Build Notifiers ͸ Cloud Build ͷ݁ՌΛ Cloud Pub/Sub ͔Βड͚औΓɺͦΕΛ Cloud Run Ͱղऍͯ͠௨஌Λߦ͏ͷͰ͕͢ɺCloud Run Ͱड͚औͬͨஈ֊ͰৄࡉͳΤϥʔ݁Ռؚ͕· Ε͍ͯͳ͍ͨΊɺϋϯυϦϯά͠Α͏ʹ΋ͦ΋ͦ΋σʔλ͕ଘࡏ͠ͳ͍ͨΊར༻Ͱ͖·ͤΜɻ   44
  47. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    3. ϏϧυͷϑϩʔΛਤղԽ͢Δ ෳࡶͳϏϧυΛ࣮ߦ͍ͨ͠৔߹͸ waitFor Λ࢖ͬͯεςοϓؒͷґଘؔ܎ΛنఆͰ͖·͢ɻ͜Ε ʹΑΓδϣϒͷฒྻ࣮ߦ͕ՄೳͰ͢ɻ ෳࡶͳॲཧΛ Cloud Build Ͱ࣮ݱ͢Δͷ͸ͱͯ΋ָ͍͠ͷͰ͕͢ɺνʔϜͰ։ൃΛߦ͏৔߹΍࣌ ͕ؒܦͬͯ಺༰Λݟฦͨ͠ࡍͳͲʹɺશମ૾ͷ೺Ѳʹ͕͔͔࣌ؒΓ·͢ɻͦΜͳͱ͖ʹ໾ʹཱͭͷ͕ ͜Ε͔Β঺հ͢ΔϏϧυεςοϓΛਤղԽ͢ΔπʔϧͰ͢ɻ ·ͣ͸ෳࡶͳؔ࿈ੑΛ΋ͬͨఆٛϑΝΠϧ (Ϧετ 3.8) Λ༻ҙ͠·͢ɻ(؆ུԽͷͨΊ֤Ϗϧυε ςοϓͷ࣮ߦίϚϯυΛলུ͍ͯ͠·͢) Ϧετ 3.8: cloudbuild.yaml steps: - id: test1 name: bash - id: test2 name: bash waitFor: ["-"] - id: test3 name: bash waitFor: [ "test2" ] - id: test4 name: bash waitFor: [ "test1", "test3" ] - id: test5 name: bash waitFor: [ "test1" ] 45
  48. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    - id: test6 name: bash waitFor: [ "test5", "test3" ] - id: test7 name: bash waitFor: [ "test1", "test2", "test6" ] - id: test8 name: bash waitFor: [ "test2" ] - id: test9 name: bash waitFor: [ "test2", "test7", "test8" ] - id: test10 name: bash waitFor: [ "test1", "test9" ] ͜ΕΛπʔϧΛ࢖ͬͯΘ͔Γ΍ͯ͘͢͠Έ·͠ΐ͏ʂ cloudbuildgraph ·ͣ͸ patrickhoefler ࢯʹΑͬͯ࡞ΒΕͨ cloudbuildgraph*11 Ͱ͢ɻ͜ͷπʔϧ͸ఆٛϑΝΠϧ ΛಡΈࠐΜͰԣ޲͖ʹґଘؔ܎Λਤࣔͯ͘͠Ε·͢ɻ Ϧετ 3.13: cloudbuildgraph ͷ࣮ߦ $ cloudbuildgraph cloudbuild.yaml *11 https://github.com/patrickhoefler/cloudbuildgraph 46
  49. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    ਤ 3.8: cloudbuildgraph Ͱग़ྗͨ͠ਤ cloudbuildgraph ͸ΧϨϯτσΟϨΫτϦʹଘࡏ͢Δ cloudbuild.yaml/cloudbuild.yml/cloudbuild.json ͷ͍ͣΕ͔ͷϑΝΠϧ͔͠ಡΈऔΔ͜ͱ͕Ͱ͖·ͤΜɻͦͷͨΊϑΝΠϧ໊Λࣗ༝ʹมߋ͍ͨ͠৔߹ ͸ɺϑΥʔΫ͞Εͨ cloudbuildgraph*12Λ࢖༻͢Δͱࣗ༝ͳϑΝΠϧ໊ͰਤղԽͰ͖·͢ɻ gcb-visualizer ଓ͍ͯ͸ RyanSiu1995 ࢯʹΑͬͯ࡞ΒΕͨ gcb-visualizer*13 Ͱ͢ɻ͜ͷπʔϧ͸ cloudbuild- graph ͱ͸ҟͳΓॎʹґଘؔ܎͕ਤղԽ͞Ε·͢ɻ Ϧετ 3.13: gcb-visualizer ͷ࣮ߦ $ gcb-visualizer visualize cloudbuild.yaml *12 https://github.com/lirlia/cloudbuildgraph *13 https://github.com/RyanSiu1995/gcb-visualizer 47
  50. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    ਤ 3.9: gcb-visualizer Ͱग़ྗͨ͠ਤ ͲͪΒ΋खܰʹ࢖͑ΔπʔϧͳͷͰݟ΍͍͢ํΛબΜͰ࢖ͬͯΈ͍ͯͩ͘͞ɻ ·ͱΊ ෳࡶͳϏϧυεςοϓΛ࣋ͭδϣϒͰ΋ਤղԽ͢Δ͜ͱͰɺҰؾʹશମ૾͕͸͖ͬΓͯ͠ཧղ͠΍ ͘͢ͳΓ·͢ɻͲͪΒͷίϚϯυ΋؆୯ʹΠϯετʔϧͰ͖·͢ͷͰɺ·ͣ͸ࠓ࢖͍ͬͯΔ Cloud Build ͷఆٛϑΝΠϧͷը૾Λ࡞੒͢Δͱ͜Ζ͔Β࢝ΊͯΈ·͠ΐ͏ɻ ׳Ε͖ͯͨΒఆٛϑΝΠϧͷमਖ਼ΛτϦΨʹͯ͠ਤղԽͷ CI Λ૸ΒͤͯɺϨϏϡʔͷࡍʹ৽ɾچ ͷఆٛϑΝΠϧͷਤΛදࣔ͢Δͷ΋ศརͰ͠ΐ͏ɻ 48
  51. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    4. δϣϒΛ଴ػ͠ଓ͚Δ Cloud Build ୯ମͰ͸ಉ͡τϦΨͷδϣϒΛγϦΞϧʹ࣮ߦ͢Δ͜ͱ͸Ͱ͖·ͤΜɻ(ಉ࣌ʹ 2 ճ ࣮ߦ͢Ε͹ฒྻʹδϣϒ͕࣮ߦ͠·͢) ͦͷͨΊ֎෦ϦιʔεΛδϣϒ͕ϩοΫͨ͠Γઐ༗͍ͯ͠Δ৔߹͸ɺԿΒ͔ͷखஈΛ༻͍ͯδϣϒ ΛΩϡʔʹஷΊΔΑ͏ͳ͘͠Έ͕ඞཁʹͳΓ·͢ɻ(ઌߦ͍ͯ͠ΔδϣϒΛΩϟϯηϧ͢Δ͚ͩͰࣄ ଍ΓΔ৔߹͸ ʮCancelotʯ Λࢀর͍ͯͩ͘͠͞) ΩϡʔʹஷΊΔ͘͠ΈΛ Google Cloud Ͱ࡞Δ৔߹ʹ͸ Cloud Pub/Sub ΍ Cloud Tasks ͳͲ͕ ڍ͛ΒΕ·͕͢ɺδϣϒͷछྨ͕૿͑ͨΓखܰʹ࣮ݱ͠Α͏ͱࢥ͏ͱগ͠޻਺͕͔͔Γ·͢ɻͦ͜Ͱ ࠓճ͸ࢦఆͷδϣϒͷ׬ྃΛ଴ͪଓ͚Δํ๏Λ঺հ͠·͢ɻ δϣϒͷ׬ྃΛ଴ػ͢ΔεΫϦϓτ Ϧετ 3.9 ͸δϣϒ͔Βݺͼग़͢εΫϦϓτͰɺࢦఆͷ৚݅Λຬͨ͢ϏϧυδϣϒͷҰཡΛग़ྗ͠ ·͢ɻ Ϧετ 3.9: get-filtered-cloudbuild-job-list.sh #!/bin/bash # ࢦఆͷϏϧυIDΑΓલʹ࣮ߦ͞Εͨɺ֤छ৚݅ʹώοτ͢Δ Cloud Build δϣϒͷϏϧυIDΛऔಘ͠·͢ set -o pipefail set -o nounset set -o errexit CMDNAME=${0##*/} function prefail() { echo "$@" 1>&2; usage; exit 1; } function usage() { cat <<USAGE >&2 Usage: $CMDNAME [OPTIONS] Options 49
  52. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    --build_id [BUILD_ID] ݕࡧର৅ʹ࢖͏δϣϒ --check_trigger_name_regex [NAME_REGEX] ϏϧυτϦΨ໊ͷݕࡧ࣌ʹ࢖༻͢Δਖ਼نදݱͰ͢ --limit num ݕࡧ਺ͷ্ݶͰ͢ --disable_on_going ௨ৗ͸࣮ߦதͷδϣϒͷΈΛର৅ʹ͠·͕͢ɺ ͜ͷϑϥάͷར༻࣌͸͢΂ͯͷδϣϒΛର৅ʹ͠·͢ --region [REGION_NAME] Private Pool ར༻࣌͸ϦʔδϣϯΛࢦఆ͍ͯͩ͘͠͞ --help / -h USAGE } LIMIT=10 # Cloud Build ͷδϣϒΛݕࡧ͢Δ਺Ͱ͢ BUILD_ID=dummy # ݕࡧͷى఺ͱͳΔδϣϒIDͰ͢ TRIGGER_NAME_REGEX=dummy # ݕࡧର৅ͱ͢ΔδϣϒΛ࣮ߦͨ͠τϦΨ໊Ͱ͢ ONGOING_FLAG=true # ݱࡏ࣮ߦதͷδϣϒͷΈΛର৅ʹ͢Δ৔߹͸ true REGION="" # Private Pool Ͱར༻͢Δ৔߹ඞཁͰ͢ # Process arguments while [[ $# -gt 0 ]]; do case "$1" in --build_id) BUILD_ID="$2" shift 2 ;; --check_trigger_name_regex) TRIGGER_NAME_REGEX="$2" shift 2 ;; --limit) LIMIT="$2" shift 2 ;; --disable_on_going) ONGOING_FLAG="false" shift 1 ;; --region) REGION="--region $2" shift 2 ;; --help|-h) usage exit 0 ;; *) prefail "Unknown argument: $1" ;; esac done # ৚݅ʹϚονͨ͠δϣϒͷҰཡΛฦ٫͠·͢ function getOngoingJobListBeforeSpecificJob() { local build_start_time=$(\ gcloud builds describe $BUILD_ID --format=’value(startTime)’ $REGION) 50
  53. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.3 Cloud Build Λ΋ͬͱศརʹ࢖͏ํ๏

    local args=( # δϣϒ࡞੒ॱʹιʔτ --sort-by=create_time # ݕࡧ਺ --limit "${LIMIT}" # ϏϧυIDΛऔಘ --format="value(ID)" # ݕࡧͷϑΟϧλʔͰ͢ --filter="id!=$BUILD_ID \ AND startTime<=$build_start_time \ AND substitutions.TRIGGER_NAME~$TRIGGER_NAME_REGEX" # Private Pool Ͱ࢖͏ϦʔδϣϯͰ͢ $REGION ) [ $ONGOING_FLAG = true ] && args+=(--ongoing) # ࣮ߦதͷδϣϒͷϏϧυIDҰཡΛऔಘ gcloud builds list "${args[@]}" } getOngoingJobListBeforeSpecificJob ͜ͷγΣϧΛϦετ 3.10 ͷΑ͏ʹϏϧυεςοϓʹ૊ΈࠐΉ͜ͱͰ৚݅Λຬͨ͢Ϗϧυδϣϒ͕ ׬ྃ͢Δ·Ͱϧʔϓ͠ଓ͚ΔϏϧυεςοϓΛ࣮ݱͰ͖ɺಉ༷ͷδϣϒͷγϦΞϧ࣮ߦ͕ՄೳʹͳΓ ·͢ɻ(ͨͩ͠δϣϒ͕Քಇ͠ଓ͚ΔͨΊɺ଴ػத͸Ϗϧυδϣϒͷҡ࣋ʹඅ༻͕ൃੜ͢Δ఺ʹ஫ҙ ͍ͯͩ͘͠͞) Ϧετ 3.10: cloudbuild.yaml - id: check another jobs name: gcr.io/google.com/cloudsdktool/cloud-sdk:slim_cloudbuild_cache entrypoint: bash args: - -c - | set -o pipefail set -o nounset set -o errexit while true do # ${PROJECT_ID}-hogehoge-job ͷ਺͕ 0 ͳΒਖ਼ৗऴྃ͠ɺ # ͦΕҎ֎ͷ৔߹͸ɺδϣϒͷऴྃΛ଴ͪ·͢ # ࣮ߦ͢Δ Git ϦϙδτϦͷϧʔτ௚Լʹ get-filtered-cloudbuild-job-list.sh ͕ # ࣮ߦݖݶ෇͖Ͱอଘ͞Ε͍ͯΔඞཁ͕͋Γ·͢ job_num=$(./get-filtered-cloudbuild-job-list.sh \ --build_id ${BUILD_ID} \ 51
  54. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    --check_trigger_name_regex "${PROJECT_ID}-hogehoge-job" \ --limit 500 | wc -l) [ $job_num -eq 0 ] && break sleep 30 # sec done 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏ CI ͸ඇৗʹศརͳ൒໘ɺ։ൃਓ਺͕ଟ͘ͳͬͨΓɺδϣϒ਺͕૿͑ͯ͘ΔͱߴίετʹͳΓ͕ͪ Ͱ͢ɻϚωʔδυͳπʔϧ͸؅ཧ͕ෆཁͳ෼खܰͰྑ͍Ͱ͕͢ɺར༻ճ਺͕૿͑Δͱར༻ίετ͕૿ ͑Δͷ͕ؾʹͳΓ·͢ɻ ͦ͜ͰຊষͰ͸ Cloud Build ͷίετΛ཈͑ΔίπΛ͍͔ͭ͘঺հ͠·͢ɻ Cloud Build ͷίετߏ଄Λ஌Δ ίπΛ஌Δલʹ·ͣ͸ Cloud Build ίετߏ଄Λ཈͑·͠ΐ͏ɻ ਤ 3.10: cloudbuild pricing 52
  55. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    *14   ˙ϝϞ: ͦͷ΄͔ͷίετ Cloud Build Ͱ͸ωοτϫʔΫసૹྉ͕Ϗϧυྉۚʹ͋Β͔͡Ί૊Έࠐ·Ε͍ͯΔͨΊɺGCE ͱ͸ҟͳΓΠϯλʔωοτ΁ͷϑΝΠϧͷΞοϓϩʔυ͕δϣϒ಺ͰߦΘΕͯ΋௥Ճͷඅ༻͸ ൃੜ͠·ͤΜɻͨͩ͠ GCS ΍ Cloud Logging ʹର͢ΔϩάΞοϓϩʔυ΍σʔλͷอଘʹͭ ͍ͯ͸ɺผ్ετϨʔδଆͷωοτϫʔΫసૹྉ΍ετϨʔδ࢖༻ྉ͕ൃੜ͢Δ఺ʹ஫ҙͯ͘͠ ͍ͩ͞ɻ   ίετઅ໿ํ๏ͷछྨ Cloud Build Ͱ͸Ϗϧυ࣮ߦ࣌ؒʹରͯ͠՝ۚ͞ΕΔͨΊɺίετͷઅ໿ํ๏ͱͯ͠େ͖͘෼͚ͯ 2 ͭͷํ๏͕ଘࡏ͠·͢ɻ 1. Ϗϧυ࣌ؒΛ୹ॖ͢Δ 2. Ϗϧυʹඞཁͳ CPU / ϝϞϦαΠζΛখ͘͢͞Δ ࠓճ͸Ϗϧυ࣌ؒΛ୹ॖ͢Δํ๏ʹ͍͍͔ͭͯͭ͘ͷίπΛ঺հ͠·͢ɻ ͳ͓ίϯςφΠϝʔδͷϏϧυʹొ৔͢Δ͞·͟·ͳπʔϧ΍ɺܰྔͳΠϝʔδΛ࢖ͬͨίϯςφ ΠϝʔδαΠζͷॖখํ๏ʹ͍ͭͯ͸ Cloud Build ͱ͸ؔ܎͕ͳ͍ͨΊ͜͜Ͱ͸঺հ͠·ͤΜ 1. ϏϧυΩϟογϡΛ࢖͏ *14 https://cloud.google.com/build/pricing ΑΓҾ༻ 53
  56. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    ·ͣ͸ఆ൪Ͱ͕͢ɺϏϧυΩϟογϡΛ͏·͘࢖͍·͠ΐ͏ɻ ެࣜαΠτ*15Ͱ΋঺հ͞Ε͍ͯΔ௨ΓɺҎԼͷํ๏ͰΩϟογϡΛ࢖͍ճ͢͜ͱ͕Ͱ͖·͢ɻ • Kaniko Ωϟογϡ ·ͨ͸ ΩϟογϡࡁΈͷ Docker Πϝʔδͷར༻ • GCS Docker Build Λ͢Δࡍ͸ɺࣄલʹऔಘͨ͠ Kaniko Ωϟογϡ΍ΩϟογϡࡁΈͷ Docker Π ϝʔδΛ࢖༻͢Δ͜ͱͰϏϧυΛεΩοϓͰ͖ΔΑ͏ʹͳΔͨΊϏϧυ࣌ؒΛ࡟ݮͰ͖·͢ɻ·ͨɺ ίϯςφҎ֎ͷ৔߹Ͱ͋Ε͹தؒ੒Ռ෺Λ GCS ʹอଘ͓ͯ͘͜͠ͱͰɺಉ༷ʹϏϧυ࣌ؒΛ࡟ݮͰ ͖·͢ɻ 2. ϩέʔγϣϯʹ஫ҙ͠Α͏ ϓϥΠϕʔτϓʔϧ ΍ ϋΠϒϦουϓʔϧ ͱ͍ͬͨಛผͳ Cloud Build ؀ڥΛ࢖͍ͬͯΔ৔߹ Λআ͍ͯɺ௨ৗ͸ global Ͱδϣϒ͕࣮ߦ͞Ε͍ͯ·͢ɻͰ͸ global ͱ͸ Google Cloud ͷͲ͜ͷ ϦʔδϣϯͳͷͰ͠ΐ͏͔ʁ global ͬͯͲ͜ͷ͜ͱʁ Ϗϧυδϣϒͷ VM ϝλσʔλʹΞΫηεͯ͠ɺδϣϒίϯςφͷϩέʔγϣϯΛνΣοΫͯ͠ Έ·͠ΐ͏ɻ Ϧετ 3.11: cloudbuild.yaml steps: - name: gcr.io/cloud-builders/curl args: [ "-sS", "-H", "Metadata-Flavor: Google", "http://metadata.google.internal/computeMetadata/v1/instance/zone", ] *15 https://cloud.google.com/build/docs/speeding-up-builds 54
  57. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    Ϧετ 3.13: δϣϒͷϩέʔγϣϯΛௐࠪ $ gcloud builds submit 2>&1 | grep zones projects/xxxxxxxxxxxxx/zones/us-central1-f ݁Ռ͸ʮzones/us-central1-fʯͱදࣔ͞Ε·ͨ͠ɻ͔͠͠ຊ౰ʹͦΕͰྑ͔ͬͨͷͰ͠ΐ͏͔ʁ લষͷ ʮCloud Build ͷཪଆͷ͘͠Έʯ Ͱ͸ Cloud Build ʹΑͬͯىಈ͞Εͨίϯςφ͕ɺGCE ্Ͱಈ͍͍ͯΔ͜ͱΛઆ໌͠·ͨ͠ɻ·ͨࠓճ curl Λ࣮ߦͨ͠δϣϒͷ઀ଓઌ͸ಉ͡ GCE ্ʹଘ ࡏ͢Δ metadata ίϯςφͰ͋Δ͜ͱ΋આ໌͠·ͨ͠ɻ ࣮͸ strings ίϚϯυͰ metadata ίϯςφ಺ͷΞϓϦέʔγϣϯΛݕࡧ͢Δͱ us-central1-f ͱ ͍͏จݴ͕ຒΊࠐ·Ε͍ͯ·͢ɻ Ϧετ 3.13: metadata ʹຒΊࠐ·ΕͨϦʔδϣϯ $ docker run -it --entrypoint strings gcr.io/cloud-builders/metadata:latest /metadata \ | grep us-central1-f ʙʙলུʙʙ trace StackTab trailing data trigger Ratio=unknown time run supported: us-central1-f user canceled value method xadd64 failed xchg64 failed ʙʙলུʙʙ ΋͔ͨ͠͠ΒϏϧυδϣϒ͕Ͳ͜Ͱಈ࡞͍ͯ͠Δͷ͔ʹݶΒͣ Ϧετ 3.11 Λ࣮ߦͨ͠ࡍʹ͸ຒΊ ࠐ·Εͨ us-central1-f ͕ৗʹදࣔ͞ΕΔͷͰ͸ͳ͍Ͱ͠ΐ͏͔ʁ ͜ΕΛ֬ೝ͢ΔͨΊʹ౦ژ (asia-northeast1) ʹཱͯͨϓϥΠϕʔτϓʔϧͰϦετ 3.12 Λ࣮ߦ ͯ͠ɺίϯςφ͔ΒΈͨ VM ϝλσʔλͱίϯςφ͕ଘࡏ͢Δ GCE ͔ΒΈͨ VM ϝλσʔλΛ νΣοΫͯ͠Έ·͢ɻ Ϧετ 3.12: cloudbuild.yaml options: pool: name: ’projects/${PROJECT_ID}/locations/asia-northeast1/workerPools/POOL_NAME’ steps: 55
  58. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    - id: container zone name: gcr.io/cloud-builders/curl args: [ "-sS", "-H", "Metadata-Flavor: Google", "http://metadata.google.internal/computeMetadata/v1/instance/zone", ] - id: virtual machine zone name: "gcr.io/google.com/cloudsdktool/cloud-sdk:cloudbuild_cache" entrypoint: bash args: - -c - | docker_opts=( # ಛݖݖݶΛ෇༩͢Δ --privileged --ipc=host --net=host --pid=host --rm # chroot ༻ʹϚ΢ϯτ͢Δ -v /:/mnt --entrypoint sh # ࢖༻͢ΔΠϝʔδ busybox # args -c "chroot /mnt curl \ -s ’http://metadata.google.internal/computeMetadata/v1/instance/zone’ \ -H ’Metadata-Flavor: Google’ \ " ) docker run "${docker_opts[@]}" waitFor: [’-’] Ϧετ 3.13: ϓϥΠϕʔτϓʔϧ (౦ژ) Ͱͷϩέʔγϣϯͷ֬ೝ $ gcloud builds submit --region asia-northeast1 2>&1 | grep zones Step #0 - "container zone": projects/MY_PROJECT_ID/zones/us-central1-f Step #1 - "virtual machine zone": projects/xxxxxxxxxxxxx/zones/asia-northeast1-c ݁Ռ͸ɺGCE ͔ΒΞΫηε͢Δͱ asia-northeast1 ͱදࣔ͞Ε͍ͯ·͕͢ɺίϯςφͰͷ௨৴͸ us-central1-f ͱදࣔ͞Εͯ͠·͍ͬͯ·͢ɻ͜ͷ݁Ռ͔Β metadata ίϯςφ͕ΞϓϦʹຒΊࠐ· 56
  59. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    Εͨ us-central1-f Λͦͷ··ฦ͍ͯ͠Δઆ͕ೱްʹͳ͖ͬͯ·ͨ͠ɻ ຊ౰ͷ global ͸Ͳ͜ʁ Ͱ͸͍͍ͬͨ Cloud Build ͷ global Λࢦఆ͞Εͨδϣϒ͸Ͳ͜Ͱಈ࡞͍ͯ͠ΔͷͰ͠ΐ͏͔ʁ ͦΕΛ͔֬ΊΔͨΊʹ Cloud Build Ͱ࢖༻͢Δ GCE ͕Ͳ͜ͷϦʔδϣϯͰಈ࡞͍ͯ͠Δͷ ͔ʁ ΛϚγϯλΠϓ͝ͱʹ֤ 100 ճͣͭௐࠪͯ͠Έ·ͨ͠ɻ Ϧετ 3.13: cloudbuild.yaml steps: - id: get zone name: "gcr.io/google.com/cloudsdktool/cloud-sdk:cloudbuild_cache" entrypoint: bash args: - -c - | docker_opts=( # ಛݖݖݶΛ෇༩͢Δ --privileged --ipc=host --net=host --pid=host --rm # chroot ༻ʹϚ΢ϯτ͢Δ -v /:/mnt --entrypoint sh # ࢖༻͢ΔΠϝʔδ busybox # shell args -c "chroot /mnt curl \ -H ’Metadata-Flavor: Google’ \ -s ’http://metadata.google.internal/computeMetadata/v1/instance/zone’ \ " 57
  60. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    ) docker run "${docker_opts[@]}" ݁ՌΛ·ͱΊͨͷ͕ͪ͜ΒͰ͢ɻ ਤ 3.11: ϚγϯλΠϓผͷ࣮ߦ࣌ؒ δϣϒ࣮ߦճ਺͕Ұ൪ଟ͍ͷ͸ us-east4 (όʔδχΞभ) Ͱɺ͍ͭͰ us-central1(ΞΠΦϫभ) ͱ ͳ͍ͬͯ·͢ɻ͜ͷ͜ͱ͔Βগͳ͘ͱ΋ Cloud Build ʹ͓͚Δ global ͸ΞϝϦΧͷ͍ͣΕ͔ͷϦʔ δϣϯΛ͍ࣔͯ͠Δͱߟ͑ͯྑ͍Ͱ͠ΐ͏ɻ ·ͱΊΔͱ Cloud Build ͕ global ͷ৔߹͸ΞϝϦΧͷϦʔδϣϯͰಈ࡞͍ͯ͠Δ͕ɺδϣϒࣗ਎ ͸ࣗ෼͕ us-central1-f ʹ͍Δͱࡨ͍֮ͯ͠Δ (metadata ίϯςφ͕ৗʹ us-central1-f Λฦ͢) ͱ ͍͏͜ͱʹͳΓ·͢ɻ GCS ͷྉۚΛ཈͑Δ Cloud Build ʹ͓͍ͯ Container Registry (Ҏ߱ GCR ͱུ͠·͢) ΍ GCS ͔ΒࢿࡐΛμ΢ϯ ϩʔυͨ͠ΓɺϏϧυͷ੒Ռ෺ΛΞοϓϩʔυ͢Δͱ GCS ͷωοτϫʔΫసૹྉ͕ൃੜ͠·͢ɻ (GCS ΁ͷσʔλอ؅ྉ΍ΦϖϨʔγϣϯྉ͸ࠓճ͸ߟྀʹೖΕ·ͤΜ) 58
  61. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    ਤ 3.12: GCS ͷωοτϫʔΫసૹྉ *16 ͜ͷ࣌ʮಉ͡ϩέʔγϣϯ಺ͷσʔλͷҠಈʯ΍ʮϚϧνϦʔδϣϯʹ͋Δ GCS όέοτ͔Β͋ Δ 1 ͭͷϦʔδϣϯʹ͋Δผͷ Google Cloud αʔϏε΁ͷσʔλͷҠಈͰɺ྆ํͷϩέʔγϣϯ ͕ಉ͡େ཮ʹ͋Δʯ͸ແྉͱॻ͔Ε͍ͯΔͨΊɺGCS ͱ Cloud Build Λಉ͡ϩέʔγϣϯͰಈ ͔͢͜ͱͰωοτϫʔΫసૹྉΛ 0 ԁʹͰ͖·͢ɻ ઌఔͷݕূʹΑͬͯɺglobal ར༻࣌͸ΞϝϦΧͷϦʔδϣϯͰಈ࡞͍ͯ͠Δ͜ͱ͕Θ͔Γ·ͨ͠ͷ *16 https://cloud.google.com/storage/pricing ΑΓҾ༻ 59
  62. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    Ͱ GCS ΋ΞϝϦΧΛར༻͠·͠ΐ͏ɻ   ˙ϝϞ: εςʔδϯάྖҬ (Ұ࣌σʔλ֨ೲྖҬ) Cloud Build Ͱ͸Ұ࣌తͳσʔλ֨ೲྖҬ (εςʔδϯάྖҬ) ͱͯ͠ GCS Λ࢖༻͠·͢ɻσ ϑΥϧτͰ͸ gs://[PROJECT_ID]_cloudbuild/source ͱ͍͏໊લͰ࡞੒͞ΕɺΞϝϦΧͷ ϚϧνϦʔδϣϯͰಈ࡞͠·͢ɻ ͦͷͨΊɺCloud Build ͷ global ઃఆΛ࢖͏৔߹͸సૹྉ͕ۚൃੜ͠·ͤΜ͕ɺϓϥΠϕʔτ ϓʔϧ΍ϋΠϒϦουϓʔϧΛ࢖͏৔߹͸ --gcs-source-staging-dir=GCS_SOURCE_STAGI NG_DIR ͷύϥϝʔλΛ࢖ͬͯɺ೚ҙͷϩέʔγϣϯʹσʔλ֨ೲྖҬΛมߋ͢Δඞཁ͕͋Γ ·͢ɻ   GCR ͷྉۚΛ཈͑Δ GCR ͷཪଆ͸ GCS ͷͨΊɺಉ༷ʹ GCS ͷ௨৴ྉͷΈߟྀ͢Ε͹໰୊͋Γ·ͤΜɻ(GCS ΁ͷ σʔλอ؅ྉ΍ΦϖϨʔγϣϯྉ͸ࠓճ͸ߟྀʹೖΕ·ͤΜ) ͨͩ͠ GCR ʹΑͬͯ͸ϗετ͞ΕΔϩέʔγϣϯ͕มΘΔͷͰ Cloud Build Ͱ docker pull / push ͢ΔΠϝʔδͷ֨ೲ৔ॴ͸ͳΔ΂͘ us.gcr.io Λ࢖͏ͱྑ͍Ͱ͠ΐ͏ɻ 60
  63. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    ਤ 3.13: GCR ͷϩέʔγϣϯ *17 ͨͩ͠ɺ֨ೲͨ͠ίϯςφΠϝʔδΛ౦ژ΍େࡕͷϦʔδϣϯͰ࢖༻͢Δ৔߹͸ɺίϯςφىಈ࣌ ʹΞϝϦΧ͔Β pull ͢Δ͜ͱʹͳΔͨΊɺͦ͜ͰωοτϫʔΫసૹྉ͕ൃੜͨ͠Γɺμ΢ϯϩʔυ ʹ͕͔͔࣌ؒΔΑ͏ʹͳΔͨΊ஫ҙ͠·͠ΐ͏ɻϏϧυʹ࢖͏πʔϧ΍Ωϟογϡͷஔ͖৔ΛΞϝϦ Χʹ͢Δ͙Β͍͕ͪΐ͏ͲΑ͍Ԙകͩͱࢥ͍·͢ɻ ·ͱΊ ௨৴ྉۚͷ࿩Λ·ͱΊΔͱ͜ͷΑ͏ʹͳΓ·͢ɻ ਤ 3.14: Cloud Build ͱ GCS / GCR ͷ௨৴ྉ *17 https://cloud.google.com/container-registry/docs/overview#registries ΑΓҾ༻ 61
  64. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    ͋͘·Ͱඅ༻ͱ͍͏؍఺ͷΈʹண໨͍ͯ͠ΔͨΊɺ࣮ࡍʹར༻͢Δࡍ͸࠷ऴతʹੜ੒ͨ͠σʔλ͕ ͲͷΑ͏ʹ࢖ΘΕΔͷ͔ʁ Λߟྀͯ͠શମతͳઃܭΛߦ͏ඞཁ͕͋Δ఺ʹ஫ҙ͍ͯͩ͘͠͞ɻ 3. δϣϒΛΩϟϯηϧ͢Δ GitHub ͷ Pull Request ΛτϦΨʹͯ͠ Cloud Build Λಈ͔͍ͯ͠ΔͱɺGit Push ͞Εͨ͢΂ ͯͷίϛοτʹରͯ͠δϣϒ͕ಈ͍͍ͯΔ͜ͱ΋௝͋͘͠Γ·ͤΜɻ ͔ͨ͠ʹෳ਺ͷδϣϒ͕ىಈ͢Δͷ͕ਖ਼͍͠ͷͰ͕͢ɺຊ౰ʹඞཁͳͷ͸Ұ൪࠷ޙʹ Git Push ͞Εͨͱ͖ͷίϛοτʹର͢ΔδϣϒͰ͢ɻ(Ұ൪࠷ޙʹੵ·Εͨίϛοτ͕Ұ൪ਖ਼͍͠ͱ͍͏લఏ Ͱ͢) Cloud Build ͸δϣϒ͕ಈ͍ͨඵ਺ʹରͯ͠՝͕ۚߦΘΕΔͨΊɺෆཁͳδϣϒΛ͍ͪૣ͘ Ωϟϯηϧ͢Δ͜ͱͰແବͳඅ༻Λ཈͑Δ͜ͱ͕Ͱ͖·͢ɻ ·ͨδϣϒʹΑͬͯ͸Ϧιʔε (σʔλϕʔε΍ςετ؀ڥ) ͷઐ༗͕ඞཁͰɺδϣϒͷฒྻ࣮ߦ͕ ڐ༰Ͱ͖ͳ͍δϣϒ΋͋ΔͰ͠ΐ͏ɻͦΜͳͱ͖ʹɺෆཁͳδϣϒΛΩϟϯηϧ͢Δ͜ͱ͸ CI/CD ͷߴ଎Խʹ΋ߩݙ͠·͢ɻ Ͱ͸ͬͦ͘͞ Cloud Build ͰδϣϒͷΩϟϯηϧΛ࣮ݱ͢Δํ๏Λݟ͍͖ͯ·͠ΐ͏ɻ Cancelot Cancelot*18 ͸ cloud-builders-community Ͱެ։͞Ε͍ͯΔπʔϧͰɺࢦఆͨ͠৚݅Λຬͨ͢ δϣϒΛΩϟϯηϧͰ͖·͢ɻ ࢖͍ํ͸ඇৗʹ୯७ͰΩϟϯηϧΛ࣮ߦ͍ͨ͠δϣϒఆٛͷதʹઃఆΛ௥Ճ͢Δ͚ͩͰ͢ɻ ˞ίϯςφΠϝʔδ͸ࣗ෼Ͱ༻ҙ͢Δඞཁ͕͋Γ·͢ Ϧετ 3.14: cloudbuild.yaml steps: - name: "gcr.io/$PROJECT_ID/cancelot" args: [ "--current_build_id", "$BUILD_ID", *18 https://github.com/GoogleCloudPlatform/cloud-builders-community/blob/master/cancelot/README.md 62
  65. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.4 Cloud Build ͷίετΛઅ໿͢Δํ๏

    "--branch_name", "$BRANCH_NAME", "--same_trigger_only", ] ͜ͷఆٛ͸ʮࣗ෼ͱಉ͡τϦΨ ID ͱ Git ϒϥϯν໊Λ࣋ͭδϣϒΛΩϟϯηϧ͢ΔʯΛҙຯ͠ ͓ͯΓɺݴ͍׵͑Δͱʮ͋Δ Pull Request ʹෳ਺ճ Git Push Λͯ͠ੜ੒͞ΕͨɺݱࡏՔಇத Ͱࣗ෼ΑΓલʹ࣮ߦ͞Εͨ Cloud Build δϣϒΛΩϟϯηϧ͢Δʯͱಉ͡ҙຯʹͳΓ·͢ɻ ͨͩ͠࢒೦ͳ͕Βࣥච࣌఺Ͱ͸ Cancelot ͸ਖ਼͘͠ಈ࡞͠·ͤΜɻҎલ͸ಈ࡞͍ͯͨ͠ͷͰ͕͢ Cloud SDK ͷΞοϓσʔτʹ൐͍ಈ࡞͠ͳ͘ͳͬͨΑ͏Ͱ͢ɻ(ৄࡉ͸ Issue*19 Λ͝ཡ͍ͩ͘͞) ͦͷͨΊ Cancelot Ͱ͸ͳ͘ siberex ࢯ͕ެ։͍ͯ͠Δ cancelot.sh*20 Λ࢖ͬͯδϣϒͷΩϟϯη ϧΛ࣮ݱͯ͠Έ·͠ΐ͏ɻ͜ͷγΣϧ͸ Cancelot ͱಉ͡ػೳͱΠϯλʔϑΣʔεΛ͍࣋ͬͯ·͢ͷ Ͱɺ΄΅ಉ͡࢖༻ํ๏Ͱར༻͢Δ͜ͱ͕Ͱ͖·͢ɻ Ϧετ 3.15: cloudbuild.yaml steps: - name: "gcr.io/google.com/cloudsdktool/cloud-sdk:slim_cloudbuild_cache" entrypoint: "bash" args: - -c - | # ࣮ߦ͢Δ Git ϦϙδτϦͷϧʔτ௚Լʹ cancelot.sh ͕ # ࣮ߦݖݶ෇͖Ͱอଘ͞Ε͍ͯΔඞཁ͕͋Γ·͢ ./cancelot.sh \ --current_build_id "$BUILD_ID" \ --branch_name "$BRANCH_NAME" \ --same_trigger_only *19 https://github.com/GoogleCloudPlatform/cloud-builders-community/issues/386 *20 https://gist.github.com/siberex/bb0540b208019382d08732cc6dd59007 63
  66. ୈ 3 ষ Cloud Build Λ࢖͍౗ͦ͏ʂ 3.5 ऴΘΓʹ  

    ˙ϝϞ: cancelot.sh ͷϓϥΠϕʔτϓʔϧ΁ͷରԠ cancelot.sh ͸ϓϥΠϕʔτϓʔϧͷδϣϒΩϟϯηϧʹରԠ͍ͯ͠·ͤΜɻ ͱ͍͏ͷ΋ϓϥΠϕʔτϓʔϧͰδϣϒΛಈ͔ͨ͢Ίʹ͸ --region ΦϓγϣϯΛ gcloud ί Ϛϯυʹ෇༩͢Δඞཁ͕͋ΔͷͰ͕͢ɺcancelot.sh Ͱ͸ͦͷରԠ͕͞Ε͍ͯͳ͍ͨΊͰ͢ɻͦ ͷͨΊϓϥΠϕʔτϓʔϧͰར༻͢Δ৔߹͸ cancelot.sh ʹखΛՃ͑ͯ --region Λ֤ gcloud ίϚϯυʹ͚ͭΔΑ͏ʹ͠·͠ΐ͏ɻ   4. ·ͱΊ Cloud Build ͷՁ֨ߏ੒͔ΒՔಇ͢ΔϦʔδϣϯɺͦͯ͠δϣϒͷΩϟϯηϧΛݟ͖ͯ·ͨ͠ɻCI ͷίετ͸͍͖ͳΓߴ͘ͳΔͷͰ͸ͳ͘ɺঃʑʹ૿Ճͯ͘͠ΔͨΊಋೖ࣌ʹ͋Δఔ౓ CI ͷશମߏ੒ Λ೺Ѳͨ͠͏͑Ͱઃܭ͢Δ͜ͱΛ͓קΊ͠·͢ɻ 3.5 ऴΘΓʹ ͜͜·Ͱ͓෇͖߹͍Λ͍͖ͨͩ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻCloud Build Λ΋ͬͱศརʹ࢖͏ํ๏Λ ͍Ζ͍Ζͱ঺հ͍͖ͤͯͨͩ͞·ͨ͠ɻ1 ͭͰ΋ࢀߟʹͳΕ͹޾͍Ͱ͢ɻ ΋͠΄͔ͷςΫχοΫ΍πʔϧΛ͝ଘ͡ͷํ͸ɺͥͻڭ͍͚͑ͯͨͩ·͢ͱ޾͍Ͱ͢ʂ 64
  67. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.1 TIPSTAR

    ͷ։ൃͱ Google Cloud Spanner TIPSTAR*1ͱ͸චऀ͕։ൃʹܞΘ͍ͬͯΔϛΫγΟࣾͷαʔϏεͰ͢ɻެӦڝٕͷ౤ථΛѻͬͯ ͍ΔαʔϏεͰɺं݊ͷߪೖ͸΋ͪΖΜͷ͜ͱɺϨʔεө૾഑৴ͷ΄͔ɺιʔγϟϧήʔϜͷΑ͏ͳ ϛογϣϯɺϚϧνϓϨΠɺΨνϟ΍ߪೖͨ͠ं݊Λࣗ෼ͷ༧૝ͱͯ͠ڞ༗͢ΔͳͲଟ͘ͷػೳΛఏ ڙ͍ͯ͠·͢ɻϝΠϯͷόοΫΤϯυʹ͸ Google Cloud Λ࠾༻͓ͯ͠ΓɺGoogle Cloud Spanner ʢҎԼ: Cloud Spannerʣ΍ Google Kubernetes Engine Λ࢝Ίɺ͞·͟·ͳϓϩμΫτΛ࠾༻͍ͯ͠ ·͢ɻͦͷதͰࠓճ͸ Cloud Spanner ʹ͍ͭͯ঺հ͍ͨ͠ͱࢥ͍·͢ɻ΄͔ʹ΋঺հ͍ͨ͠ Google Cloud ͷ׆༻ࣄྫ͕͋ΔͷͰ͕͢ɺࠓճ͸ׂѪ͠ผͷػձʹ঺հͰ͖Ε͹ͱࢥ͍·͢ɻTIPSTAR ͷ ։ൃͰֶΜͩ Cloud Spanner ͷώϯτ΍ɺۀ຿Ͱ Cloud Spanner Λ࢖͏্Ͱҙ͓͖͍ࣝͯͨ͠ϙΠ ϯτͳͲɺҰൠతͳσʔλϕʔεͷཹҙ఺ͱަ͑ͳ͕Β঺հ͠·͢ɻ Google Cloud Spanner ͱ͸ Google Cloud Spanner ͸ਫฏεέʔϦϯά͕ՄೳͰ͋Γɺڧ੔߹ੑ΍Մ༻ੑΛඋ͍͑ͯΔϑϧϚ ωʔδυͷϦϨʔγϣφϧσʔλϕʔεͰ͢ɻଏʹݴ͏ NewSQL ͱΑ͹ΕΔσʔλϕʔεͷҰछͱ ͳΓ·͢ɻྑ͍͜ͱͮ͘͠ͳσʔλϕʔεͳͷͰ͕͢ɺςʔϒϧઃܭ΍ΫΤϦઃܭʹෆඋ͕͋Δͱɺ ϗοτεϙοτ͕ൃੜ͠ CPU ͷෛՙ্͕͛ͯ͠·ͬͨΓɺ૝ఆ֎ͷͱ͜ΖͰΤϥʔ͕ଟ͘ͳΔՄೳ ੑ͕͋ΔͨΊɺCloud Spanner ͷಛ௃Λཧղ͠ઃܭ͢Δඞཁ͕͋Γ·͢ɻmixi tech note #04*2Ͱ TIPSTAR ͷΞʔΩςΫνϟͱ Cloud Spanner ͱͷ෇͖߹͍ํͱ͍͏ςʔϚͷهࣄ͕͋ΔͷͰɺڵ ຯ͕͋Δํ͸ͪ͜Β΋͝ཡ͍͚ͨͩΕ͹ͱࢥ͍·͢ɻ *1 TIPSTAR ͸ɺڝྠɾ৽ KEIRIN PIST6ɾΦʔτϨʔεͷωοτ౤ථΛɺ༑ୡͱָ͠Ή͜ͱ͕Ͱ͖ΔαʔϏεͰ͢ɻ https://tipstar.com *2 mixi tech note #04 https://speakerdeck.com/mixi_engineers/mixi-tech-note-number-04 65
  68. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.2 Cloud

    Spanner ώϯτू ଟػೳͳ TIPSTAR Λࢧ͑Δ Cloud Spanner TIPSTAR ͸ 2020 ೥ 6 ݄ 30 ೔ͷϩʔϯνҎ߱ɺଟ͘ͷػೳΛϦϦʔε͖ͯ͠·ͨ͠ɻػೳΛ ։ൃ্͍ͯ͘͠Ͱ੾ͬͯ΋੾Γ཭ͤͳ͍ͷ͕ओཁσʔλϕʔεͰ͋Δ Cloud Spanner ͷଘࡏͰ͢ɻ TIPSTAR ͷػೳ։ൃ͸ɺاը৬ͷํ͔ΒΩοΫΦϑϛʔςΟϯάͰػೳཁ͕݅ల։͞Εͨ͋ͱɺ֤ νʔϜ͕࣋ͪؼΓݒ೦఺Λચ͍ग़͠ɺ։ൃظؒ΍ػೳ಺༰Λ֬ఆ͢Δϑϩʔͱͳ͍ͬͯ·͢ɻΩο ΫΦϑͰ࢓༷ॻ͕ల։͞ΕͨλΠϛϯά͔Βɺ஗͘ͱ΋αʔόαΠυνʔϜͰٞ࿦͢Δલʹ Cloud Spanner ͷݒ೦఺Λҙࣝ͢Δ͜ͱΛ৺͕͚͍ͯ·͢ɻݒ೦఺Λҙࣝ͢Δ͜ͱͰϦϦʔε௚લͰઃܭ ϛε͕ൃ֮͠ख໭Γ͕ى͖ͨΓɺϦϦʔεޙʹ૝ఆ֎ͷෛՙ΍Τϥʔ͕ຄൃ͢Δͱ͍ͬͨՄೳੑΛܰ ݮͰ͖·͢ɻ 4.2 Cloud Spanner ώϯτू ຊઅͰ͸ TIPSTAR ͷ։ൃΛ͢Δ্Ͱීஈ͔Βҙ͍ࣝͯ͠Δ Cloud Spanner ͷཹҙ఺Λ঺հ ͠·͢ɻࠓճ͸σʔλϕʔεʹ໰͍߹ΘͤΛߦ͏ API Λ։ൃ͢Δ্Ͱͷཹҙ఺ͱͳΔͷͰ Cloud Spanner ʹݶͬͨ࿩͠Ͱ͸ͳ͘ɺRDB ΍ NoSQL ͱ͍ͬͨ΄͔ͷσʔλϕʔεʹ΋ڞ௨͢Δ஌ݟ΋ ؚ·Ε·͢ɻ͜Ε͔Β Cloud Spanner Λ࢖༻͢Δ༧ఆͷํ͸΋ͪΖΜͷ͜ͱɺσʔλϕʔεઃܭͷ ܦݧ͕ઙ͘͜Ε͔ΒֶΜͰ͍͖͍ͨͱࢥ͍ͬͯΔํͷࢀߟʹͳΕ͹޾͍Ͱ͢ɻ ΠϯλʔϦʔϒ Cloud Spanner ʹ͸ΠϯλʔϦʔϒͱݺ͹ΕΔςʔϒϧͷ਌ࢠؔ܎Λఆٛ͢Δએݴ͕͋Γ·͢ɻ ΠϯλʔϦʔϒ͞ΕͨࢠςʔϒϧͷϨίʔυͱ਌ςʔϒϧͷϨίʔυ͸ɺಉ͡εϓϦοτ*3ʹอଘ͞ ΕΔͨΊɺσΟεΫΞΫηεͱωοτϫʔΫτϥϑΟοΫΛ࠷খݶʹ཈͑Δ͜ͱ͕Ͱ͖·͢ɻ ͨͱ͑͹ҎԼͷΑ͏ͳ User ςʔϒϧͱ User ͷཤྺςʔϒϧͷಋೖΛݕ౼ͨ͠ͱ͖ɺUser ཤྺ͸ ඞͣ User ͕ଘࡏ͍ͯ͠Δ͜ͱ͕৚݅Ͱ͋ΓɺUser ࣗ਎͕ࣗ෼ͷཤྺΛಡΈࠐΉͱ͍ͬͨཁ͕݅͋Δ ͱ͠·͢ɻ͜ͷ৔߹ڞ௨͍ͯ࣋ͬͯ͠Δ user_id Λ༻͍ͯݕࡧ͢Δ૝ఆͰ͢ɻ͜ͷ৔߹ΠϯλϦʔ ϒͰ਌ࢠؔ܎Λ࡞Γɺusers ςʔϒϧʹ͋ΔϢʔβʔ A ͞Μͷ৘ใͱ user_histories ςʔϒϧʹ͋ ΔϢʔβʔ A ͞Μͷཤྺ৘ใ͸෺ཧతʹಉ͡ετϨʔδʹσʔλ͕อଘ͞ΕΔͨΊɺಡΈࠐΈͷύ ϑΥʔϚϯε޲্͕ظ଴Ͱ͖·͢ɻ CREATE TABLE users ( user_id STRING(36) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_id) *3 Cloud Spanner ͸ओΩʔͷൣғʹΑͬͯςʔϒϧΛ෼ׂ͓ͯ͠Γɺͦͷ෼ׂ͞Εͨ୯ҐΛεϓϦοτͱݺͿ 66
  69. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.2 Cloud

    Spanner ώϯτू CREATE TABLE user_histories ( user_history_id STRING(36) NOT NULL, user_id STRING(36) NOT NULL, history_id STRING(MAX) NOT NULL, state STRING(MAX) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_history_id) INTERLEAVE Λ࢖͍ users ͱ user_histories Λ਌ࢠؔ܎ʹ͢Δɻ CREATE TABLE users ( user_id STRING(36) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_id); CREATE TABLE user_histories ( user_history_id STRING(36) NOT NULL, user_id STRING(36) NOT NULL, history_id STRING(MAX) NOT NULL, state STRING(MAX) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_id, user_history_id), INTERLEAVE IN PARENT users ON DELETE NO ACTION; ҙ͍ࣝͨ͠ϙΠϯτ: ΠϯλϦʔϒͰ͖ͳ͍͔ߟ͑Δ users ΍ user_histories ͷΑ͏ͳΠϯλϦʔϒͰ਌ࢠؔ܎ͱͳΔςʔϒϧΛ࡞Εͳ͍͔ߟ͑ΔͱΑ ͦ͞͏Ͱ͢ɻϢʔβʔ΍άϧʔϓͱ͍ͬͨ͋Δଐੑ͕΋ͭσʔλΛ؅ཧ͢Δςʔϒϧͷ৔߹͸ɺ͜ͷ ΠϯλϦʔϒΛ࢖͑Δέʔε͕ଟ͍ҹ৅Ͱ͢ɻ ϗοτεϙοτ͕ൃੜ͠ͳ͍͔ Cloud Spanner ͸ओΩʔͷࣙॻॱʹϨίʔυ͕ฒΜͰ͓ΓʢσϑΥϧτ͸ঢॱʣ ɺΩʔͷൣғʹԠ ͯ͡ςʔϒϧΛ෼ׂ͠ɺεϓϦοτͱ͍͏୯Ґʹ෼͚ Spanner ϊʔυ΁σʔλΛׂΓ౰ͯ·͢ɻͦ ͷߏ଄ͨΊओΩʔ͕୯ௐ૿Ճ͢ΔΑ͏ͳ஋Λ࢖༻͢Δͱ৽͍͠Ϩίʔυ͸࠷ޙͷεϓϦοτʹ௥Ճ͞ Ε·͢ɻ ಉҰͷεϓϦοτʹ஋͕อଘ͞Εͯ͠·͏ͱɺͦ͜ʹϦΫΤετ͕ूத͠ϗοτεϙοτ͕ൃੜ ͢ΔՄೳੑ͕͋Γ·͢ɻͦͷͨΊओΩʔʹγʔέϯγϟϧͳ஋ΛೖΕΔͷ͸ඇਪ঑ͱͳ͓ͬͯΓɺ UUID v4 ౳Λ࢖͏͜ͱ͕ਪ঑͞Ε͍ͯ·͢ɻ͜ͷݱ৅͸ΠϯσοΫεͰ΋ಉ͡໰୊͕ൃੜ͠·͢ɻ 67
  70. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.2 Cloud

    Spanner ώϯτू ΠϯσοΫε͸಺෦Ͱ͸ςʔϒϧͷΑ͏ͳߏ଄Ͱ࣮૷͞Ε͍ͯΔͨΊɺΠϯσοΫεͰ΋୯ௐ૿Ճ· ͨ͸ݮগ͢Δ஋Λ࢖Θͳ͍͜ͱ͕ਪ঑͞Ε͍ͯ·͢ɻͨͩ͠ΠϯλϦʔϒ͞ΕͨΠϯσοΫεͰ͋Ε ͹εϓϦοτ෼ׂ͞Ε͍ͯΔͨΊ໰୊͋Γ·ͤΜɻ ҙ͍ࣝͨ͠ϙΠϯτ: ओΩʔʹγʔέϯγϟϧͳ஋Λ࢖Θͳ͍ ओΩʔʹ͸ UUID v4 Λ࢖͏ɻͱʹ͔͘ओΩʔʹ͸୯ௐ૿Ճ·ͨ͸ݮগ͢Δγʔέϯγϟϧͳ஋Λ ࢖༻͠ͳ͍ɻCloud Spanner Λ࢖͏্Ͱ࠷ॳͷํʹ঺հ͞ΕΔݒ೦ͷͨΊɺ͋ΒͨΊͯ܁Γฦ͢಺ ༰Ͱ͸ͳ͍ͱࢥ͍·͕͢ɺCloud Spanner Λ࢖͏্ͰॏཁͳϙΠϯτͱͳΔͷͰ঺հ͠·ͨ͠ɻ ಡΈࠐΈ ಡΈࠐΈ͸ϑϧεΩϟϯ͕૸Βͳ͍͔ͳͲجຊతͳ஫ҙΛ࢝Ίɺର৅Ϩίʔυ͕ಉҰͷεϓϦοτ ಺Ͱݕࡧͳͷ͔ Spanner શϊʔυΛލ͙ݕࡧͱͳΔ͔Λҙࣝͯ͠ઃܭ͢Δඞཁ͕͋Γ·͢ɻΞϓϦ έʔγϣϯͷ࢓༷ཁ݅ʹΑͬͯ͸શϊʔυݕࡧ΍ฒͼସ͕͑ඞਢͱͳΔέʔε΋ଟʑ͋Δͱࢥ͏ͷͰ ղܾͷώϯτΛ঺հ͠·͢ɻ ݕࡧର৅͕ந৅తͰϊʔυΛ௒͑ͨݕࡧ͍ͨ͠ͱ͖ લड़ͷ User ཤྺςʔϒϧͷ৔߹ɺuser_id Λࢦఆࣗ͠਎ͷཤྺͷΈΛݕࡧ͢Δͷ͕Ұ൪ޮ཰ͷྑ ͍ΫΤϦͱͳΔͷͰ͕͢ɺόονॲཧͳͲͰશϢʔβʔͷத͔Βಛఆͷ history_id ͱ state Λࢦఆ ͠ର৅ϨίʔυΛநग़͍ͨ͜͠ͱ΋͋Δͱࢥ͍·͢ɻͦΜͳͱ͖͸ shard_key Λ௥Ճ͠ઐ༻ͷΠϯ σοΫεΛ࡞Δ͜ͱͰϑϧεΩϟϯΛճආͰ͖·͢ɻ CREATE TABLE user_histories ( user_history_id STRING(36) NOT NULL, user_id STRING(36) NOT NULL, shard_key INT64 NOT NULL, history_id STRING(MAX) NOT NULL, state STRING(MAX) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_id, user_history_id), INTERLEAVE IN PARENT users ON DELETE NO ACTION; CREATE INDEX idx_user_histories_shard_key_history_id_state ON user_histories(shard_key, history_id, state); ΞϓϦέʔγϣϯଆ͔Βݺͼग़͢ͱ͖͸ҎԼͷΑ͏ͳΫΤϦͰݕࡧ͠·͢ɻཁ݅ʹ΋ΑΔͱࢥ͍· ͕͢ɺͪ͜ΒͷΫΤϦ͸શϊʔυΛލ͙ݕࡧͱͳΔͷͰɺΞϓϦέʔγϣϯଆͰ࢖༻͍ͨ͠ΧϥϜΛ ߜΓɺ͞Βʹ WHERE ۟Λ࢖͍ state Ҏ߱ʹ΋ର৅ϨίʔυΛߜΔͱΞϓϦέʔγϣϯαʔόͷϝ ϞϦͷ࢖༻ྔΛ཈͑ΒΕ·͢ɻ 68
  71. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.2 Cloud

    Spanner ώϯτू SELECT user_id, history_id FROM user_histories WHERE shard_key BETWEEN 0 AND 99 AND state = "finish"; ҙ͍ࣝͨ͠ϙΠϯτ: ݕࡧൣғ͕޿͍ͱ͖͸ shard_key ͷ௥ՃΛݕ౼͢Δ shard_key ͷൣғʹ͍ͭͯ͸ޙड़Ͱ΋৮Ε·͕͢ɺ0-99 ͳͲൣғΛܾΊΞϓϦέʔγϣϯଆͰ౎ ౓ϥϯμϜͰઃఆ͢Δͱྑ͍Ͱ͠ΐ͏ɻ ฒͼସ͕͑ඞཁͳςʔϒϧ͔ ཤྺͷΑ͏ͳςʔϒϧͷ৔߹৽͍͠ॱʹݕࡧ͍ͨ͠ͱ͍ͬͨཁ͕݅Ͱͯ͘Δ͜ͱ΋͋Δͱࢥ͍· ͢ɻCloud Spanner Ͱ͸ओΩʔͷࣙॻॱʢσϑΥϧτ͸ঢॱʣʹϨίʔυ͕ฒΜͰ͍ΔͨΊɺͦͷ· ·ιʔτ͕ඞཁͳݕࡧʹ໾ཱͯΔ͜ͱ͕Ͱ͖·͢ɻํ๏͸ user_history_id ΛλΠϜελϯϓؚ͕ ·ΕΔ ID Λಋೖ͠ɺDESC Λࢦఆ͢Δ͜ͱͰ߱ॱͰϨίʔυ͕อଘ͞Ε·͢ɻςʔϒϧ࡞੒࣌ʹܾ Ί͓ͯ͘ඞཁ͕͋ΔͷͰɺΞϓϦέʔγϣϯͷ࢓༷ཁ݅ͱͯ߱͠ॱͰऔΓ͍͔ͨͳͲઌʹԡ͓͑ͯ͞ ͖͍ͨϙΠϯτͰ͢ɻଞͷฒ΂ସ͕͑ඞཁʹͳͬͨ৔߹͸ΠϯσοΫεͷ௥ՃͰରԠͰ͖·͢ɻ CREATE TABLE user_histories ( user_history_id STRING(36) NOT NULL, user_id STRING(36) NOT NULL, shard_key INT64 NOT NULL, history_id STRING(MAX) NOT NULL, state STRING(MAX) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_id, user_history_id DESC), INTERLEAVE IN PARENT users ON DELETE NO ACTION; ҙ͍ࣝͨ͠ϙΠϯτ: ฒͼସ͑ʹओΩʔͷಛੑΛར༻Ͱ͖ͳ͍͔ݕ౼͢Δ DESC ࢦఆ͞Εͨ ID ͸࣌ܥྻʹฒ΂Δඞཁ͕͋ΔͷͰɺULID*4ͳͲΛ࢖͏ͱྑ͍Ͱ͠ΐ͏ɻ ॻ͖ࠐΈ σʔλϕʔε΁ͷॻ͖ࠐΈ͸ɺ୹ظతʹ୯ҰϨίʔυ΁ͷॻ͖ࠐΈ͕ूத͠ϩοΫ଴ͪΛൃੜͤ͞ ͨΓɺෛՙΛ͋͛ͯ͠·Θͳ͍͔ͳͲ஫ҙ͕ඞཁͰ͢ɻ·ͨ Cloud Spanner ͸ mutations ͱݺ͹Ε Δ୯Ґ͕͋ΓɺҰ౓ʹॲཧͰ͖Δྔͷ੍ݶ΋͋ΔͷͰͦͪΒ΋঺հ͠·͢ɻ *4 Universally Unique Lexicographically Sortable Identifier ͷུͰɺཚ਺੒෼ͱλΠϜελϯϓ੒෼ΛؚΜͰ͓Γɺ ॏෳ͠ͳ͍Ұҙͳ ID ͔ͭɺϛϦඵ୯ҐͰ࣌ܥྻͳιʔτ͕Մೳͳ ID 69
  72. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ

    ϦΞϧλΠϜ൓ө͕ඞਢͰ͸ͳ͍έʔε ΞϓϦέʔγϣϯͷ࢓༷ཁ݅ʹΑͬͯ͸ɺ୯ҰϨίʔυ΁ߋ৽͕ଟ͘ͳΔ͜ͱ΋͋Δͱࢥ͍·͢ɻ ͦΜͳͱ͖͸ߋ৽ස౓ΛԼ͛ΒΕͳ͍͔ݕ౼͢ΔͱΑͦ͞͏Ͱ͢ɻ ྫ͑͹ SNS ͷʮ͍͍Ͷʯ΍౤ߘͷӾཡ਺ͳͲɺඞͣ͠΋ϦΞϧλΠϜͷՃࢉ͕ඞཁͰ͸ͳ͍έʔ ε͕ଟ͍Ͱ͢ɻ ඇಉظͷߋ৽Ͱ໰୊͕ͳ͍৔߹͸ɺҰ࣌αʔόͷϝϞϦͳͲʹ஋ΛՃࢉ͓͖ͯ͠ɺҰఆ͕࣌ؒܦա ͨ͠Βσʔλϕʔε΁൓ө͢Δͱ͍ͬͨ͜ͱΛݕ౼͢Δͱྑͦ͞͏Ͱ͢ɻ·ͨσʔλͷܽଛ͕ڐ༰ Ͱ͖ͳ͍৔߹΍ɺ͋ΔΧϥϜͷ஋Λݟͯߋ৽಺༰Λมߋ͍ͨ͠৔߹͸ Cloud Pub/Sub ͷΑ͏ͳϝο ηʔδΩϡʔΠϯάͷಋೖ͕͓קΊͰ͢ɻ τϥϯβΫγϣϯ Cloud Spanner ͸ 1 ճͷτϥϯβΫγϣϯʹ͖ͭ 20,000 mutations ҎԼʹ͢Δͱ੍͍ͬͨݶ͕ ͋Γ·͢ɻmutations ͱ͸ INSERTɺUPDATEɺDELETE ͳͲΛૢ࡞͢Δ࣌ͷ୯ҐͰ͋ΓมߋΛ Ճ͑Δߦ਺ɺྻ਺ɺΠϯσοΫε਺ͳͲͰ਺͕ܾ·Γ·͢ɻ20,000 mutations Λ௒͑ͦ͏ʹͳͬͨ ৔߹͸τϥϯβΫγϣϯΛ෼͚ɺඇಉظૠೖ͢ΔΑ͏ͳσʔϞϯϫʔΧΛ࡞੒͢Δඞཁ͕͋Γ·͢ɻ mutations ͷܭࢉ͸ Commit statistics*5΍ spanner-cli*6ͷ -v ΦϓγϣϯͰ֬ೝՄೳͰ͢ɻτϥϯ βΫγϣϯΛ෼͚Δ৔߹͸ෆ੔߹͕ൃੜ͢ΔϦεΫ͕͋ΔͷͰɺCloud Pub/Sub ͳͲͷϝοηʔδ ΩϡʔΠϯάαʔϏεΛ׆༻͠ɺΤϥʔ͕͋ͬͨͱͯ͠΋τϥϯβΫγϣϯ͕੒ޭ͢Δ·Ͱ࠶࣮ߦ͞ ΕΔ͘͠Έʹ͢Δͷ͕๬·͍͠Ͱ͢ɻ 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ ͜͜·Ͱ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner ͷώϯτΛ঺հ͠·ͨ͠ɻຊઅͰ͸࣮ࡍʹ TIPSTAR ͷ։ൃͰ͋ͬͨέʔεΛࢀߟʹɺ࠷ऴతʹͱͬͨߦಈͳͲΛ঺հ͠·͢ɻ ෆಛఆଟ਺ͷϢʔβʔ͕ಡΈॻ͖͢ΔςʔϒϧΛ࡞Γ͍ͨ ෆಛఆଟ਺ͷϢʔβʔʹΑΔಡΈॻ͖Λ૝ఆ͠ɺ͞Βʹ a_countɺb_countɺc_count ͱ͍͏Χϥ ϜΛ༻ҙ͠ɺͦΕͧΕιʔτ΋΍Γ͍ͨͱ͍ͬͨཁ͕݅͋Γ·ͨ͠ɻॻ͖ࠐΈΑΓ΋ಡΈࠐΈස౓͕ ѹ౗తʹଟ͍ςʔϒϧͷͨΊಡΈࠐΈͷύϑΥʔϚϯεΛ࠷༏ઌͱ͍ͯ͠·͢ɻ ૝ఆ͍ͯ͠Δςʔϒϧ ҎԼͷΑ͏ͳςʔϒϧͰ͋Γɺtype_id Λࢦఆ͠ର৅ϨίʔυΛநग़͢Δ૝ఆͰ͢ɻ *5 Commit statistics https://cloud.google.com/spanner/docs/commit-statistics *6 spanner-cli https://github.com/cloudspannerecosystem/spanner-cli 70
  73. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ

    CREATE TABLE user_posts ( user_post_id STRING(36) NOT NULL, user_id STRING(36) NOT NULL, type_id STRING(36) NOT NULL, a_count INT64 NOT NULL, b_count INT64 NOT NULL, c_count INT64 NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ) PRIMARY KEY (user_id, user_post_id), INTERLEAVE IN PARENT users ON DELETE NO ACTION; ιʔτʹ͸༏ઌॱҐ͕͋ΓɺಡΈࠐΈͷΫΤϦཁ݅͸਺ύλʔϯଘࡏ͠·͢ɻ 1. a_count 2. b_count 3. c_count SELECT * FROM user_posts where type_id = "xxx" ORDER BY a_count DESC, b_count DESC, c_count DESC; SELECT * FROM user_posts where type_id = "xxx" ORDER BY b_count DESC, a_count DESC, c_count DESC; SELECT * FROM user_posts where type_id = "xxx" ORDER BY c_count DESC, a_count DESC, b_count DESC; ͜ͷΑ͏ͳཁ͕݅͋ͬͨͱ͖ʹɺҙ͍ࣝͨ͠ϙΠϯτ͸̏ͭ͋Γ·ͨ͠ɻ • ಡΈࠐΈͷίετ • ॻ͖ࠐΈͷίετ • ॻ͖ࠐΈ࣌ͷϗοτεϙοτͷݒ೦ ಡΈࠐΈͷίετ ಡΈࠐΈͷίετΛԼ͍͛ͨ৔߹ɺҰൠతʹ͸ಡΈࠐΈͷ࢓༷ཁ݅ʹԠͯ͡ΠϯσοΫεΛ௥Ճ͢ ΔͱύϑΥʔϚϯε޲্͕ظ଴Ͱ͖·͢ɻॻ͖ࠐΈͷίετΛҙࣝͯ͠ΠϯσοΫεΛ௥Ճ͠ͳ͍ͱ ͍͏Ҋ΋͋Γ·͕͢ɺ SELECT * FROM user_posts where type_id = "xxx" 71
  74. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ

    ORDER BY a_count DESC, b_count DESC, c_count DESC; ্هͷΑ͏ͳΫΤϦΛൃߦͨ͠ͱ͖ʹ where type_id = "xxx"Ͱ਺ສɾ਺ेສͱ͍ͬͨϨίʔ υ਺ͩͬͨ৔߹ɺͦΕΛ͢΂ͯιʔτ͢ΔͱͳΔͱ CPU ࣌ؒΛ਺ඵ࢖ͬͯ͠·͏Մೳੑ͕͋Δͨ ΊɺಡΈࠐΈίετΛԼ͍͛ͨ৔߹͸ૉ௚ʹɺa_countɺb_countɺc_count ΧϥϜΛΠϯσοΫε ʹؚΊͨ΄͏Αͦ͞͏Ͱ͢ɻ CREATE INDEX idx_user_posts ON user_posts(type_id, a_count DESC, b_count DESC, c_count DESC); ॻ͖ࠐΈͷίετ ࣍ʹಡΈࠐΈͷίετΛԼ͛ΔͨΊʹɺa_count, b_count, c_count ΧϥϜΛΠϯσοΫεʹؚ Ίͨ৔߹ͷॻ͖ࠐΈͷίετΛߟ͑ͯΈ·͢ɻҰൠతʹΠϯσοΫεΛ࡞੒͢ΔͱɺͦͷΠϯσοΫ εʹؚΊͨΧϥϜͷߋ৽͸ϕʔεςʔϒϧͱͷ 2-Phase Commit ʹͳΔͨΊ (ΠϯλϦʔϒΠϯσο ΫεͰͳ͍৔߹)ɺॻ͖ࠐΈͷϨΠςϯγ͕एׯѱԽ͢Δ͜ͱʹͳΓ·͢ɻ·ͨ a_count, b_count, c_count ΧϥϜ͕සൟʹߋ৽͞ΕΔ΋ͷͳΒɺͦΕͧΕͷߋ৽ͷ౓ʹΠϯσοΫεߋ৽͕૸ΔͷͰͦ Ε΋ίετ͕͕͋ΔཁҼͱͳΔՄೳੑ͕͋Γ·͢ɻ͜ΕΒͷ՝୊Λղফ͢ΔͨΊʹɺલઅͰ঺հͨ͠ ඇಉظʹߋ৽͢Δ͘͠ΈΛಋೖΛ͢Δͱྑͦ͞͏Ͱ͢ɻ·ͨ Cloud Spanner ͷ Node Λ௥Ճ͠ॻ͖ ࠐΈΛ෼ࢄ͢Δ͜ͱͰॻ͖ࠐΈͷίετΛԼ͛Δ͜ͱ΋Ͱ͖·͢ɻ ॻ͖ࠐΈ࣌ͷϗοτεϙοτͷՄೳੑ a_count, b_count, c_count ΧϥϜΛΠϯσοΫεʹؚΊͨ৔߹ʹɺॻ͖ࠐΈ࣌ʹϗοτεϙο τ͕Ͱ͖ΔՄೳੑ͕͋Γ·͢ɻCloud Spanner Ͱ͸ΠϯσοΫε΋ςʔϒϧͱಉ౳ͷߏ଄Λ͍ͯ͠ Δͱ͞Ε͍ͯ·͢ɻࠓճͷ user_posts ςʔϒϧͷεΩʔϚͰ͢ͱɺσʔλ͸ԼهͷΑ͏ʹอଘ͞Ε ͍ͯ·͢ɻ ද 4.1: user_posts ςʔϒϧͷσʔλΠϝʔδ user_id user_post_id type_id a_count b_count c_count user_id_1 user_post_id_1 type_id_1 8 3 10 user_id_2 user_post_id_2 type_id_1 9 8 3 user_id_3 user_post_id_3 type_id_1 9 7 3 user_id_4 user_post_id_4 type_id_1 10 9 7 ͪ͜Βͷςʔϒϧʹର͠ɺ 72
  75. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ

    CREATE INDEX idx_user_posts ON user_posts(type_id, a_count DESC, b_count DESC, c_count DESC); ্هͷΑ͏ͳΠϯσοΫεΛ࡞੒͢ΔͱҎԼͷΑ͏ͳΠϯσοΫεςʔϒϧ͕࡞੒͞Ε·͢ɻ ද 4.2: ΠϯσοΫεςʔϒϧͷσʔλΠϝʔδ type_id a_count b_count c_count user_id user_post_id type_id_1 10 9 7 user_id_4 user_post_id_4 type_id_1 9 8 3 user_id_2 user_post_id_2 type_id_1 9 7 3 user_id_3 user_post_id_3 type_id_1 8 3 10 user_id_1 user_post_id_1 ͭ·Γ͜ͷΠϯσοΫε͸ type_id ͷঢॱ ˠ a_count ͷ߱ॱ ˠ b_count ͷ߱ॱ ˠ c_count ͷ ߱ॱ ˠ user_id ͷঢॱ ˠ user_post_id ͷঢॱͰϨίʔυ͕ฒͿ૝ఆͱͳΓ·͢ɻa_count ͕Ұ ൪༏ઌॱҐ͕ߴ͍ͨΊ a_count ͷ஋͕Ұ൪େ͖͍ user_id_4 ͷϨίʔυ͕Ұ൪্ʹ͖·͢ɻ ϗοτεϙοτ͕ൃੜ͢Δͷ͸ɺ͜ͷΠϯσοΫεʹରͯ͠ɺ • ৗʹઌ಄ʹϨίʔυ͕ૠೖ͞ΕΔ • ৗʹ࠷ޙʹϨίʔυ͕ૠೖ͞ΕΔ • ৗʹ్தͷͲ͔͜ͷϨίʔυҎ߱ʹૠೖ͞ΕΔ ͷ͍ͣΕ͔ͷύλʔϯ͕ى͖ɺ͔ͭͦͷ QPS ͕ߴ͍৔߹ʢྫ: ୯Ұͷ Spanner ϊʔυͷ࠷େॻ͖ ࠐΈ਺͸ 2,000QPS) ͱͳΓ·͢ɻTIPSTAR ͰࠓճͷΑ͏ͳέʔεͷςʔϒϧΛઃܭͨ͠ࡍ͸େ͖ ͳ໰୊͸͓͖ͳ͍ͱ൑அ͠ɺ࣮ࡍʹॻ͖ࠐ·ΕΔ஋Λ໛ͨ͠ϕϯνʔϚʔΫΛ࣮ࢪ͠໰୊ͳ͍͜ͱΛ ֬ೝ͍ͯ͠·͢ɻ·ͨϗοτεϙοτͷ໰୊͸*7load-based splitting Ͱղফ͞ΕΔͱਪଌ͍ͯͨ͠ ͨΊɺҰ࣌తʹϨΠςϯγ͕ѱԽͯ͠΋͙͢ʹղফ͞ΕΔ૝ఆͰ͍·ͨ͠ɻ ࠷ऴతʹͱͬͨߦಈ ಡΈࠐΈͷύϑΥʔϚϯεΛ࠷΋༏ઌ͓ͯ͠ΓɺࣄલͷෛՙςετͰ΋େ͖ͳݒ೦͕ͳ͔ͬͨͨ ΊɺಡΈࠐΈཁ݅ʹԠͨ͡ιʔτΞϊςʔγϣϯΛ෇͖ͷઐ༻ͷΠϯσοΫεΛ࡞੒͢Δ͜ͱͰରԠ ͠·ͨ͠ɻ *7 load-based splitting ɹෛՙ্ঢʹΑͬͯςʔϒϧ͕εϓϦοτ෼ׂ͞ΕΔ͘͠Έɻσʔλྔ૿ՃʹΑͬͯεϓϦοτ ෼ׂ͞ΕΔɺsize-based spliting ͱ͍͏ͷ΋͋Δɻ 73
  76. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ

    shard_key ͷௐ੔ લઅͰ shard_key Λ΋͍ͪͨΠϯσοΫεͷ঺հΛ͠·͕ͨ͠ɺ͜ͷ shard_key ൣғ΋࣌ͱ৔߹ ʹΑͬͯௐ੔ͨ͠΄͏͕๬·͍͠έʔε͕͋Γ·͢ɻ͋Δςʔϒϧʹରͯ͜͠ͷΑ͏ͳΠϯσοΫε ͷ௥ՃΛߦ͓͏ͱ͠·ͨ͠ɻ CREATE INDEX idx_user_posts_shard_key_code_id ON user_posts(shard_key, code, id); ͜ͷςʔϒϧʹରͯ͠ಡΈࠐΈͷෛՙࢼݧΛ࣮ࢪͨ͠ͱ͜ΖɺύϑΥʔϚϯε͕ѱ͔ͬͨͨΊɺ shard_key ൣғΛ 0-99 ͔Β 0-1 ͷൣғ΁มߋͯ͠Έ·ͨ͠ɻ͢Δͱ݁ՌతʹಡΈࠐΈͷύϑΥʔϚ ϯε͕޲্͠·ͨ͠ɻ͜Ε͸ 0-1 ͷൣғʹมߋ͢Δ͜ͱͰεϓϦοτͷ෼ࢄ਺͕ݮΔͨΊॻ͖ࠐΈϨ Πςϯγ͸͕͋Γ·͕͢ɺ݁ՌతʹಡΈࠐΈͷύϑΥʔϚϯε্͕͕Γ·ͨ͠ɻൣғ͕ 0ʙ1 ͚ͩͰ ͋ͬͨͱͯ͠΋ɺগͳ͘ͱ΋ 2 ͭͷαʔόʹ͸ॻ͖ࠐΈΛ෼ࢄ͞ΕΔ͜ͱ͕ظ଴Ͱ͖ΔͷͰɺಡΈࠐ ΈͷύϑΥʔϚϯεΛ༏ઌ͠ shard_key Λ 0ʙ1 ʹ͠·ͨ͠ɻͪ͜ΒͷέʔεͰ͸୹ظతʹେྔͷ ॻ͖ࠐΈ͕ൃੜ͢ΔςʔϒϧͰ͸ͳ͔ͬͨͷͰ 0ʙ1 ͷ shard_key ൣғͰ΋໰୊ͳ͘ಈ࡞ͯ͠·͢ɻ Optimizer ͷௐ੔ GROUP BY Λ͢ΔΫΤϦͰΦϓγϣϯͷઃఆΛ͢Δ͜ͱͰύϑΥʔϚϯε͕޲্ͨ͠έʔε͕ ͋ͬͨͷͰ঺հ͠·͢ɻ CREATE INDEX idx_user_posts_shard_key_code_id ON user_posts(shard_key, code, id); ͷΠϯσοΫεΛ༻͍ͨΫΤϦͰ id Λऔಘ͍ͨ͠έʔε͕͋Γ·ͨ͠ɻid ͷ࢓༷͸ UUID ͷΑ͏ ͳҰҙͳ ID Ͱ͸ͳ͘ɺ͋ΔಛఆͷࣄฑΛࣔ͢ڞ௨Ͱ࢖͑Δ ID Ͱ͢ɻ SELECT id FROM user_posts WHERE shard_key BETWEEN @shared_key_min AND @shared_key_max AND code IN UNNEST(@codes) AND id IN UNNEST(@ids) GROUP BY id ্هͷΑ͏ʹ GROUP BY ࢦఆͰಛఆͷ ID Λݕࡧ͢Δ৔߹ɺ GROUPBY_SCAN_OPTIMIZATION Λࢦఆ͢ΔͱύϑΥʔϚϯε޲্͕๬Ί·͢ɻ 74
  77. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.3 ࣮ࡍͷέʔεʹج͍ͮͨղܾྫ

    SELECT id FROM user_posts@{GROUPBY_SCAN_OPTIMIZATION=TRUE} WHERE shard_key BETWEEN @shared_key_min AND @shared_key_max AND code IN UNNEST(@codes) AND id IN UNNEST(@ids) GROUP BY id ͜ͷΦϓγϣϯΛࢦఆ͢Δͱɺ͢Ͱʹಉ͡ id ͕εΩϟϯࡁͷ৔߹ͦ͜ͰεΩϟϯ͕தࢭ͞Ε࣍ͷ id ͷεΩϟϯʹҠΔڍಈΛߦͬͯ͘Ε·͢ɻΑͬͯಉҰͷ id ͕ଟ͚Ε͹ଟ͍΄Ͳ͜ͷޮՌ͕ߴ͘ͳ Δ͜ͱ͕๬ΊΔͷͰɺͥͻ׆༻͍ͨ͠ΦϓγϣϯͰ͢ɻ΄͔ͷ SQL ߏจʹ͍ͭͯ΋ͬͱ஌Γ͍ͨͱ ͍͏ํ͸ɺQuery syntax in Google Standard SQL*8 Λ͝ࢀর͍͚ͨͩΕ͹ͱࢥ͍·͢ɻ ຊ൪ͷσʔλΛ҆શʹमਖ਼͢Δ ΞϓϦέʔγϣϯͷෆ۩߹ʹΑΓɺҰ෦ͷσʔλ͕ҙਤ͠ͳ͍஋Ͱอଘ͞ΕಛఆͷΧϥϜͷ஋Λम ਖ਼͍ͨ͠έʔεʹ͍ͭͯߟ͑ͯΈ·͢ɻͪ͜Βέʔε͸ҰൠϢʔβʔʹӨڹ͠ͳ͍ܰඍͳमਖ਼Λ૝ఆ ͓ͯ͠Γɺमਖ਼಺༰ͱର৅Ϩίʔυͷநग़͕Ͱ͖Δલఏͱ͠·͢ɻ ର৅Ϩίʔυͷநग़ ͜ͷΑ͏ͳέʔεͷ৔߹૝ఆͨ͠நग़Ͱ͸ͳ͍ͨΊɺΠϯσοΫε౳͸࡞੒͓ͯ͠Βͣநग़͕ࠔ೉ ͳ৔߹͕͋Γ·͢ɻͦͷͨΊࣄલʹ BigQuery ͳͲʹόοΫΞοϓͨ͠σʔλ͔Βର৅ϨίʔυΛࢉ ग़͓ͯ͘͠ඞཁ͕͋Γ·͢ɻΞϓϦέʔγϣϯͷෆ۩߹ʹΑΔ΋ͷͰ͋ͬͨ৔߹͸ɺෆ۩߹ൃੜ೔࣌ ͔Βमਖ਼͕൓ө͞Εͨ೔࣌ͷൣғͰର৅ϨίʔυΛ୳͢ͱྑͦ͞͏Ͱ͢ɻࠓճ͸ΧϥϜͷमਖ਼ͱͳΔ ͨΊɺର৅ϨίʔυͷओΩʔΛࣄલʹऔಘ͓͖ͯ͠·͢ɻ ର৅ΧϥϜΛ҆શʹߋ৽͢Δ ର৅ϨίʔυͷओΩʔΛநग़ͨ͋͠ͱ͸ɺ͍Α͍Αमਖ਼࡞ۀͱͳΓ·͕͢ɺ͜͜Ͱ΋͍͔ͭ͘ϙΠ ϯτ͕͋Γ·͢ɻ • ్தτϥϯβΫγϣϯΤϥʔͳͲͰεΫϦϓτ͕ࣦഊͨ͠ͱͯ͠΋࠶ࢼߦͰ͖ΔΑ͏ʹႈ౳ੑ Λ୲อͨ͠࡞Γʹ͓ͯ͘͠ • ର৅Ϩίʔυ͕ଟ͍৔߹τϥϯβΫγϣϯͷ mutatios Τϥʔ͕ൃੜ͠ͳ͍Α͏ߟྀ͢Δ • ຊ൪ͷσʔλϕʔεʹӨڹ͕Ͱͳ͍Α͏ɺΫΤϦͷ*9Priority Λ Low ࢦఆ͠༏ઌ౓௿Ͱ࣮ߦ ͢Δ *8 Query syntax in Google Standard SQL https://cloud.google.com/spanner/docs/reference/ standard-sql/query-syntax#sql_syntax *9 Priority ʹ͸ LowɺMediumɺHigh ͕͋Γɺঢ়گʹԠͯ͡ CPU ࢖༻཰͕ௐ੔͞Ε·͢ɻͨͱ͑͹ High ͷ ॲཧ͕ଟ͍ͱ͖͸ Low ͷ CPU ࢖༻཰͸࠷େ 60% ʹͳΓ·͢ɻ https://cloud.google.com/blog/topics/ developers-practitioners/introducing-request-priorities-cloud-spanner-apis 75
  78. ୈ 4 ষ TIPSTAR ͷ։ൃͰֶΜͩ Cloud Spanner Tips 4.4 ऴΘΓʹ

    Ҏ্ͷϙΠϯτΛ཈͑ͨमਖ਼εΫϦϓτΛ༻ҙ͠ରԠ͢ΔͱΑͦ͞͏Ͱ͢ɻ 4.4 ऴΘΓʹ ຊষͰ͸ TIPSTAR ͷ։ൃΛ௨ͯ͡ಘͨ Cloud Spanner ͷ Tips Λ঺հ͠·ͨ͠ɻσʔλϕʔ εʹ Cloud Spanner Λ࠾༻͍ͯ͠ΔҎ্ɺͦͷಛ௃Λ཈͑ͭͭ࢓༷ཁ݅Λ੔ཧ͢Δஈ֊Ͱݒ೦఺ Λચ͍ग़͓ͯ͘͜͠ͱ͕๬·͍͠ͱײ͍ͯ͡·͢ɻCloud Spanner ͷجຊతͳ֓೦͸΋ͪΖΜͰ ͕͢ɺҎલ͸ݒ೦ͩͬͨՕॴ͕ΞοϓσʔτʹΑΓࠓ͸ղফ͞Ε͍ͯΔͱ͍ͬͨ͜ͱ΋͋ΔͷͰɺ QueryOptimizer ΍ΫϥΠΞϯτϥΠϒϥϦΛ࢝Ί Cloud Spanner ͷ੍ݶ؇࿨ͳͲͷΞοϓσʔτ ʹ΋໨Λ޲͚͓ͯ͘ͱྑͦ͞͏Ͱ͢ɻචऀ͕ܦݧͨ͠ҰྫΛ͋͛ΔͱɺҎલ͸ΠϯλϦʔϒͰ͖Δα Πζʹ্ݶ͕͋Γ·͕ͨ͠ɺݱࡏ͸͜ͷ੍ݶ͕ͳ͘ͳ͍ͬͯ·͢ɻ͜Ε͔Β΋ Cloud Spanner ͷಈ ޲Λ௥͍ͳ͕ΒɺTIPSTAR ͷ։ൃʹߩݙͰ͖ΔΑ͏νʔϜҰಉਫ਼ਐ͍ͨ͠ͱߟ͍͑ͯ·͢ɻಉ͡Α ͏ʹ Cloud Spanner Λ࢖༻͍ͯ͠Δํɺ͋Δ͍͸͜Ε͔Β Cloud Spanner ͷಋೖΛݕ౼͍ͯ͠Δํ ʹຊষ͕গ͠Ͱ΋ࢀߟʹͳΕ͹޾͍Ͱ͢ɻ 76
  79. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍ ΍ͬͯΈͨ 5.1 ͸͡Ίʹ 2021 ೥ 4

    ݄ɺචऀ͸ TIPSTAR*1 ͱ͍͏αʔϏεͷ։ൃΛ͍ͯ͠ΔʮTIPSTAR ։ൃ෦ γες Ϝ 2 άϧʔϓʢγεςϜ 2Gʣ ʯͷϚωδϟʔʹब͖ɺ੖ΕͯʮΤϯδχΞϦϯάϚωδϟʔ (EM)ʯ ͱͯ͠ͷΩϟϦΞΛาΈ࢝Ί·ͨ͠ɻ ຊষͰ͸ɺචऀ͕ EM ͱͯ͠ͲͷΑ͏ͳߦಈΛऔΓɺͲͷΑ͏ͳ͜ͱΛߟ͑ͨͷ͔Λड़΂·͢ɻ ର৅ಡऀ EM ʹब͍ͨ͹͔Γͷಉࢤͷํɺ͢Ͱʹ EM ͱͯ͠͝׆༂͞Ε͍ͯΔํɻͦͯ͠ɺEM Λࢤͯ͠ ͍Δํɻ 5.2 ϛΫγΟʹ͓͚ΔϚωδϟʔɾEM υϥοΧʔ͸ʮϚωδϝϯτʲΤοηϯγϟϧ൛ʳ ʯ *2ͷதͰɺϚωδϟʔΛʮ૊৫ͷ੒Ռʹ੹೚Λ ࣋ͭऀʯͱఆ͍ٛͯ͠·͢ɻ ϛΫγΟάϧʔϓʹ͓͍ͯ΋ɺϚωδϝϯτ૚ʹٻΊΒΕΔ໾ׂͱͯ͠ʮ૊৫੒Ռͷ࠷େԽʯΛܝ ͍͛ͯ·͢ɻͦͷ໾ׂΛՌͨͨ͢Ίʮ૊৫ͷΰʔϧΛఆΊΔʯ ʮ૊৫ͷϑΥʔϝʔγϣϯΛ૊Ήʯ ʮ૊ ৫͕ΰʔϧ΁޲͔͑ΔΑ͏ʹࢦش͢Δʯ ʮ૊৫ͷϝϯόʔΛҭ੒͢Δʯͱ͍͏ 4 ͭͷ۩ମతߦಈΛఆ Ί͍ͯ·͢ɻ EM ͱͯ͠ͷϙδγϣϯ͕໌֬ʹଘࡏ͍ͯ͠ΔΘ͚Ͱ͸ͳ͘ɺචऀͷϙδγϣϯ͸ʮΤϯδχΞ૊ ৫ͷϚωδϟʔʯͰ͋Δͱ΋ݴ͑·͢ɻͦͷͨΊɺଟ༷ͳಇ͖ํɾ໾ׂΛ࣋ͬͨ EM ͕ଘࡏ͍ͯ͠ ·͢ɻ *1 TIPSTAR ͸ɺڝྠɾ৽ KEIRIN PIST6ɾΦʔτϨʔεͷωοτ౤ථΛɺ༑ୡͱָ͠Ή͜ͱ͕Ͱ͖ΔαʔϏεͰ͢ɻ https://tipstar.com/ *2 https://www.diamond.co.jp/book/9784478410233.html 77
  80. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 5.3 γεςϜ 2G ͱ͸ 5.3 γεςϜ

    2G ͱ͸ ͯ͞ɺචऀ͕Ϛωδϝϯτ͍ͯ͠ΔʮγεςϜ 2Gʯ *3ʹ͍ͭͯ؆୯ʹઆ໌͠·͢ɻ γεςϜ 2G ͸ɺϝϯόʔ͕ 5 ໊ఔ౓Ͱɺશһ͕αʔόαΠυΞϓϦέʔγϣϯ΍Πϯϑϥͷε ϖγϟϦςΟΛ͍࣋ͬͯ·͢ɻචऀ͸ɺٕज़ʹඇৗʹਂ͍Θ͚Ͱ͸ͳ͍ͷͰɺϝϯόʔʹ͍Ζ͍Ζڭ ΘͬͨΓ࣭໰ͨ͠Γ͠ͳ͕ΒमߦΛ͍ͯ͠Δͱ͜ΖͰ͢ɻ ͜ͷ૊৫ͷͻͱ·ͣͷϛογϣϯ͸ʮϢʔβʔ΁ͷՁ஋ఏڙ଎౓ͷ࠷େԽʯͰ͢*4ɻTIPSTAR ͸ 2020 ೥ 6 ݄ͷϩʔϯνҎདྷɺ೔ʑଟ͘ͷࢪࡦͷ։ൃ͕ਐΈɺ਺ʑͷ৽ػೳΛϢʔβʔʹఏڙ͍ͯ͠ ·͢ɻ͜Ε·Ͱ͸શΤϯδχΞϝϯόʔ͕৽ػೳ։ൃʹ஫ྗ͍ͯ͠·͕ͨ͠ɺ2021 ೥ 4 ݄ʹγες Ϝ 2G Λ૊৫͠ɺ࢓༷ࡦఆɾ։ൃɾϦϦʔεɾϢʔβʔ͔ΒͷϑΟʔυόοΫΛड͚ΔαΠΫϧΛΑ Γૣ͘ճͨ͢Ίͷ͘͠Έͮ͘ΓΛ͢Δ͜ͱʹ͠·ͨ͠ɻಛʹɺϦʔυλΠϜΛ୹͘͢ΔͨΊʹඞཁͳ ͞·͟·ͳࢪࡦʹऔΓ૊Έ·͢ɻ ·ͨɺ10 ݄ʹϩʔϯν͞Εͨ PIST6*5 ͷϓϥοτϑΥʔϜγεςϜͱ TIPSTAR ͱΛ઀ଓ͢Δ ͨΊͷϚΠΫϩαʔϏεͷ։ൃ΋ߦ͍ͬͯ·ͨ͠ɻTIPSTAR ಺ʹ͓͚Δ PIST6 ͷ࢓༷ݕ౼ΑΓ΋ ઌʹϓϥοτϑΥʔϜγεςϜͱͷ࿈ܞ෦෼ΛઌΜ࣮ͯ͡૷͢Δ͜ͱͰɺϝΠϯετϦʔϜͷ։ൃϦ ιʔεΛ TIPSTAR ಺ͷػೳ։ൃͷΈʹ஫ྗͤ͞Δ͜ͱ͕໨తͰ͢ɻ 5.4 EM ͱͯ͠औΓ૊Μͩ͜ͱ γεςϜ 2G ͸౎߹ͷΑ͍͜ͱʹɺචऀ͕ EM ʹब͘λΠϛϯάͰ৽ઃ͞ΕͨάϧʔϓͰ͢ɻά ϧʔϓͱͯ͠ͷΰʔϧɾϛογϣϯ΍νʔϜ࡞Γ͔ΒؔΘΔ͜ͱ͕Ͱ͖·͢ɻ EM ʹब͘ʹ͋ͨͬͯҎԼΛࣗ਎ͷ໾ׂͩͱఆٛ͠·ͨ͠ɻ • ૊৫ͷ੒ՌΛ࠷େԽ͢ΔͨΊɺ ʮ੒ՌʯͷఆٛΛ໌֬ʹߦ͍ɺ ʮ࠷େԽʯ͢ΔͨΊͷαϙʔτΛ ͢Δ͜ͱ • ֎ͷνʔϜͱͷϋϒʹͳΔ͜ͱ • ϝϯόʔͷҭ੒΍ϞνϕʔςΟϯάΛؚΊͨϐʔϓϧϚωδϝϯτ΍ϦιʔείϯτϩʔϧΛ ͢Δ͜ͱʹΑΓ҆ఆͨ͠੒ՌΛ͋͛ΒΕΔΑ͏ʹ͢Δ͜ͱ චऀࣗ਎͸͚ͬͯ͠ΤϯδχΞϦϯάೳྗ͕ߴ͍ͱ͍͏Θ͚Ͱ͸ͳ͘ɺר͖ࠐΈೳྗ΍՝୊ղܾ΁ ͷ࢟੎ͳͲΛධՁͯ͠΋Β͍Ϛωδϟʔʹब͔ͤͯ΋Βͬͨͱࢥ͍ͬͯ·͢ɻϝϯόʔͷ΄͏͕ٕज़ తεΩϧ͸ѹ౗తʹ্Ͱ͢ͷͰɺ൴ΒͷਐΉ΂͖ಓΛ໌Β͔ʹ͠ɺ҆৺ͯ͠ಥ͖ਐΜͰ΋Β͑Δ؀ڥ Λ࡞Δ͜ͱʹઐ೦͢Δͷ͕࢓ࣄͰ͋Δͱཧղ͍ͯ͠·͢ɻ ͦͷ໾ׂΛ͔ͬ͠Γ੒͠ͱ͛ΔͨΊɺ ʮ૊৫ͷΰʔϧΛఆΊΔʯ ʮ૊৫ͷϑΥʔϝʔγϣϯΛ૊Ήʯ *3 ຊ౰͸΋ͬͱεϚʔτͳ໊લʹ͍͚ͨ͠ΕͲɺϐϯͱ͘Δ໊લ͕·ͩࢥ͍ු͔͹ͣʜɻ *4 ݱঢ়ͷγεςϜ 2G ͕Ռͨͦ͏ͱ͍ͯ͠Δ໾ׂ͸ɺࠓޙ։ൃνʔϜ͕ओମతʹߦ͏͜ͱ͕๬·͍͠ͱߟ͍͑ͯΔͨΊɺ γεςϜ 2G ͕ࠓޙͲͷΑ͏ͳ׆ಈΛ͍͔ͯ͘͠Λݱࡏઈࢍݕ౼தɻ *5 ʮPIST6ʯ͸ɺ6 ਓͷબख͕ͨͪ 1 प 250 ̼ͷόϯΫΛ 6 प͠ɺ࠷΋଎͘ΰʔϧͨ͠ऀ͕উར͢ΔࣗసंڝٕͰ͢ɻઍ ༿ެԂ಺ʹ͋Δ TIPSTAR DOME CHIBA Ͱ΄΅ຖिܹಆ͕܁Γ޿͛ΒΕ͍ͯ·͢ɻ https://pist6.com/ 78
  81. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 5.4 EM ͱͯ͠औΓ૊Μͩ͜ͱ ʮ૊৫͕ΰʔϧ΁޲͔͑ΔΑ͏ʹࢦش͢Δʯ ʮ૊৫ͷϝϯόʔΛҭ੒͢Δʯͱ͍͏ 4

    ͭͷ؍఺Ͱ૊৫ͮ ͘ΓʹऔΓ૊Έ·ͨ͠ɻ ૊৫ͷΰʔϧΛఆΊΔ Ϛωδϟʔʹब͍͔ͯΒͷ൒೥ؒ͸ɺ ʮPIST6 ͱͷ઀ଓ͢ΔͨΊͷϚΠΫϩαʔϏεΛ࡞Δʯͷ ͕άϧʔϓͷେ͖ͳϛογϣϯͰͨ͠ɻͦͷͨΊɺͦͷظؒ͸ TIPSTAR ͷ։ൃঢ়گ΍ͦͷ՝୊Λ ͔ͬ͠Γ؍࡯͠ɺߟ࡯͢Δظؒͱͯ͠ඇৗʹ༗ҙٛͰͨ͠ɻ ·ͣ͸্࢘΍΄͔ͷϚωδϟʔͱձ࿩͠·ͨ͠ɻཁ໿͢ΔͱɺϢʔβʔ΁ͷػೳఏڙͷ଎౓Λ޲্ ͍ͤͨ͞ʢ։ൃ଎౓Λ޲্͍ͨ͠ʣͱ͍͏ཁ๬͕ڧ͍Α͏Ͱͨ͠ɻ ϝϯόʔͱ΋ͨ͘͞Μٞ࿦Λ͠·ͨ͠ɻͦͷதͰग़͖ͯͨٞ୊͸ɺClean Architecture ͷΑ͏ͳ ϨΠϠʔυΞʔΩςΫνϟͷΑ͏ʹͳ͍ͬͯΔ΋ͷͷ UseCase ͕ް͘ͳΓ͍͗ͯ͢Δ*6ͱ͔ɺͦΕ ʹΑͬͯςετίʔυͰέʔεΛे෼ʹ໢ཏͰ͖͍ͯͳ͍ͱ͔ɺΞϓϦέʔγϣϯͷϏϧυͱσϓϩ ΠΛ෼཭͍ͨ͠ͱ͔ɻ ϝϯόʔͱͷձ࿩Ͱ্͕ͬͨٞ୊͸͍ͣΕ΋՝୊ͦͷ΋ͷͷΈͳΒͣɺԿ͔͠Βͷ՝୊ʹରॲ͢ ΔͨΊͷखஈ΋ଟ͍Α͏ʹײ͡ΒΕ·ͨ͠ɻͦ͜Ͱɺ ʮͳΜͰͦͷٞ୊Λʰ՝୊ʱͱͱΒ͑ͨͷͩΖ ͏ʁʯͱ͍͏໰͍͔͚Λͯ͠Έ·ͨ͠ɻͦͷ݁Ռݟ͖͑ͯͨͷ͸ɺকདྷతʹ໨ࢦ͍͖͍ͯͨ࢟͠ͱݱ ঢ়ͱͷဃ཭ɺͦͯ͠ͳͥͦͷ࢟Λ໨ࢦ͍ͨ͠ͷ͔ͱ͍͏ϝϯόʔͷҙࢥͰͨ͠ɻ ্ҐϨΠϠ΋ϝϯόʔϨΠϠ΋ɺ͍ͣΕ΋ʮόάͷগͳ͍ΞϓϦέʔγϣϯΛΑΓૣ͘Ϣʔβʔʹ ಧ͚͍ͨʯͱ͍͏ࢥ͍͕͋Γɺͦ͜Λΰʔϧɾϛογϣϯͱ͢Δͷ͕ద౰ͩΖ͏ͱߟ͑·ͨ͠ɻͦ͜ ͰγεςϜ 2G ͷϛογϣϯ͸ʮϢʔβʔ΁ͷՁ஋ఏڙ଎౓Λ޲্ͤ͞඼࣭Λຏ͖ଓ͚Δʯͱ͠·͠ ͨɻՁ஋ఏڙɾԾઆݕূʹऔΓֻ͔Δ·ͰͷεϐʔυΛ޲্ͤ͞Δ͜ͱͰɺαʔϏεվળͷεϐʔυ Λຏ্͖͛ΔͨΊͷࢪࡦΛଧͭάϧʔϓͰ͋Δͱ໌֬ʹఆٛ͢Δ͜ͱͰɺ֤ϝϯόʔͷऔΓ૊Ήۀ຿ ʹର͢Δ՝୊Λ໌֬ʹͰ͖ΔΑ͏ʹͳͬͨͷͰ͸ͳ͍͔ͱࢥ͍·͢ɻ ҰํͰɺ·ͩ௕ظ໨ઢͰγεςϜ 2G ͕ͲͷΑ͏ʹ׆༂͍͔ͯ͘͠͸ݕ౼தͰ͢ɻ͜͜ʹ͍ͭͯ͸ Ҿ͖ଓ͖ϝϯόʔ΍্௕ͱ΋ٞ࿦ΛਂΊ͍͖ͯ·͢ɻ ্هͰهࡌͨ͠ϛογϣϯΛ੒͠਱͛ΔͨΊɺFY2022 Լظ͸ҎԼͷ՝୊ʹऔΓ૊Ή͜ͱʹ͠· ͨ͠ɻ ։ൃνʔϜͷύϑΥʔϚϯεࢦඪΛ΢Υον͢Δ Google ͷ DevOps Research and Assesment νʔϜཱ͕֬ͨ͠ Four Keys*7 ͱ͍͏ 4 ͭͷࢦඪ Λ༻͍ͯɺݱঢ়ͷ TIPSTAR αʔόαΠυΞϓϦέʔγϣϯ։ൃνʔϜͷύϑΥʔϚϯεΛ΢Υο νͰ͖ΔΑ͏ʹ͠·͢ɻऔΓ૊Ή΂͖՝୊Λ໌֬Խ͠ɺͦΕͧΕͷࢪࡦͷޮՌΛܭଌ͢Δࢦඪͱͯ͠ ༻͍·͢ɻ *6 ϏδωεϩδοΫ͸ Domain ʹɺUseCase ͸ॲཧͷϑϩʔͱӬଓԽͷॲཧΛஔ͘Α͏ʹ͍ͨ͠ͱ͍͏ࢥ૝͕ͩͬͨɺ UseCase ʹϏδωεϩδοΫ͕ʹ͡Έग़ͯ͠·͍ͬͯΔঢ়ଶɻ *7 https://cloud.google.com/blog/ja/products/gcp/using-the-four-keys-to-measure-your-devops-performance 79
  82. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 5.4 EM ͱͯ͠औΓ૊Μͩ͜ͱ αʔόαΠυΞϓϦέʔγϣϯͷσϓϩΠϑϩʔͷ؆ܿԽ ݱঢ়͸ɺCircleCI ্ͰΞϓϦέʔγϣϯͷϏϧυͱσϓϩΠΛߦ͓ͬͯΓɺαʔόαΠυΞϓϦ

    έʔγϣϯͷ Git ϦϙδτϦ্ʹσϓϩΠʹؔ͢Δϑϩʔ͕هࡌ͞Ε͍ͯ·͢ɻҰํͰɺTerraform ΍ Kubernetes ͷ Manifest ͳͲΛ؅ཧ͢Δ ops ϦϙδτϦ΋ଘࡏ͓ͯ͠ΓɺͦͪΒʹ΋ΞϓϦέʔ γϣϯͷσϓϩΠϑϩʔ͕هࡌ͞Ε͍ͯΔঢ়ଶͰ͢ɻͦͷͨΊɺσϓϩΠʹؔ͢ΔઃఆΛมߋ͍ͨ͠ ৔߹ʹɺͦΕͧΕͷϦϙδτϦʹมߋΛՃ͑Δඞཁ͕͋Γɺඇৗʹ൥ࡶʹͳͬͯ͠·͍ͬͯ·͢ɻ৽ ؔ͘͠ΘΔϝϯόʔ΋ཧղͮ͠Β͍ঢ়گͱͳΓɺ໰୊΋ى͜Γ΍͍ͨ͢Ίɺ͜ΕΛվળ͠·͢ɻ ۩ମతʹ͸ɺΞϓϦέʔγϣϯͷϦϙδτϦ͸Ϗϧυ͕੹຿ɾops ͷϦϙδτϦ͸σϓϩΠ͕੹຿ɺ ͱ໌֬ʹ੹຿Λ෼཭͢Δ͜ͱͱ͠·͢ɻ͢ͳΘͪɺCIOps ͔Β GitOps ʹมߋ͍ͯ͘͠ͱ͍͏͜ͱ ʹͳΓ·͢ɻ ࠓظ͸ɺϏϧυͱσϓϩΠΛ෼཭͢Δ͜ͱͱɺGitOps ʹ͢ΔͨΊͷϚΠϧετʔϯͷ໌֬ԽΛਐ Ί·͢ɻ αʔόαΠυΞϓϦέʔγϣϯΞʔΩςΫνϟͷվળܒ໤׆ಈ ઌʹड़΂ͨͱ͓ΓɺαʔόαΠυΞϓϦέʔγϣϯͷ UseCase ͕ް͘ͳΓ͍͗ͯ͢Δͱ͍͏՝୊ ʹ΋औΓ૊Έ·͢ɻ͜ΕʹΑΓɺద੾ͳςετ͕ߦΘΕ͍ͯͳ͍ʢద੾ͳςετέʔεΛ໢ཏͰ͖ͯ ͍ͳ͍ʣͱ͍͏໰୊͕ੜ͍ͯ͡·͢ɻ ͦ͜ͰɺҎԼͷ 2 ͭͷΞϓϩʔνʹऔΓ૊Έ·͢ɻ • ͜͏͍͏࣮૷Α͍ͶʗΑ͘ͳ͍Ͷ Λ໌֬ʹ͠ɺυΩϡϝϯτԽ • طଘͷ UseCase ࣮૷΍ςετͷվળΛߦ͍ɺ։ൃϝϯόʔʹ΋ϨϏϡʔͯ͠΋Β͏ ଞʹ΋ɺUseCase ͷೝ஌తෳࡶ౓΍ςετΧόϨοδͷܭଌͱ͍͏Ҋ΋͋ΔͷͰɺͦΕΒͷखஈ΋ ݕ౼͠·͢ɻ ૊৫ͷϑΥʔϝʔγϣϯΛ૊Ή ૊৫ͷΰʔϧ΍ظͷϛογϣϯ͕໌֬ʹͳͬͨͱ͜ΖͰɺݱঢ়ͷϝϯόʔͷεϖγϟϦςΟ΍ຊਓ ͷر๬͢ΔΩϟϦΞύεɾ਎ʹ͚ͭͯ΄͍͠εΩϧͳͲΛՃຯ͠ɺ૊৫ͷϑΥʔϝʔγϣϯΛݕ౼͠ ·͢ɻ ֤ϝϯόʔͷՁ஋؍΍ڧΈ/ऑΈΛ 1on1 ͳͲͰ֬ೝͨ͠͏͑Ͱɺϝϯόʔͷ໾ׂΛఆٛ͠·ͨ͠ɻ ςοΫϦʔυͷΑ͏ͳ໾ׂΛ͓ئ͍͢ΔϝϯόʔɺϦʔμʔͱͯ͠ͷ׆༂Λظ଴͢Δϝϯόʔɺ΄ ͔ͷνʔϜͱίϛϡχέʔγϣϯΛऔΓͳ͕Β෺ࣄΛਪਐ͢ΔྗΛ͚ͭͯ΋Β͍͍ͨϝϯόʔͳͲ ͳͲɻ ݱࡏ͸γεςϜ 2G ͷ഑ԼʹϝϯόʔΛ௚઀഑ஔ͍ͯ͠Δঢ়گͰ͕͢ɺαʔϏεʹର͢Δ໾ׂ͝ͱ ʹνʔϜΛ࡞Δ͜ͱ΋ݕ౼͍ͯ͠·͢ɻ௕ظతͳ૊৫ͷϛογϣϯΛݕ౼ͨ͠͏͑Ͱɺࠓޙͷମ੍Λ ݕ౼͢Δ༧ఆͰ͢ɻ 80
  83. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 5.5 ͜Ε͔Β΍Γ͍ͨ͜ͱ ૊৫͕ΰʔϧ΁޲͔͑ΔΑ͏ʹࢦش͢Δ ղܾ͍ͨ͠՝୊ͱͦͷͨΊͷํ޲ੑ͸͋Δఔ౓ઌͰ໌֬ʹͳ͍ͬͯΔͨΊɺͦΕͧΕͷ՝୊ʹϝϯ όʔ͕͔ͬ͠ΓͱऔΓ૊ΊΔΑ͏ɺҎԼͷΑ͏ͳऔΓ૊ΈΛ͍ͯ͠·͢ɻ •

    ֤՝୊ʹϝϯόʔΛΞαΠϯ͠ɺ۩ମతͳखஈ͸ͦͷϝϯόʔͷࡋྔͰܾఆ͢ΔʢΦʔφʔ γοϓΛൃشͯ͠΋Β͍ɺϦʔυͯ͠΋Β͏ʣ • ि࣍ͷఆྫͰਐ௙΍೰ΜͰ͍Δ͜ͱΛνʔϜ಺Ͱڞ༗͢Δ – ํ޲ੑͷೝࣝ߹Θͤͱɺ஌ݟڞ༗͕໨త – ϙδςΟϒͳ੒Ռʹؔͯ͠͸ΈΜͳͰশ͑߹͏ චऀࣗ਎͸ݸผͷ՝୊Λ࣋ͨͣɺͦΕͧΕͷ՝୊ղܾͷαϙʔτΛߦ͍·͢ɻఆྫͰͦΕͧΕͷࢪ ࡦͷํ޲ੑ΍εέδϡʔϧײͷೝࣝ߹ΘͤΛ͢Δ͜ͱͰɺඞཁͳ൑அΛߦͬͨΓɺಈػ͚ͮɾํ޲ੑ ͷೝࣝ߹ΘͤͳͲΛߦ͍·͢ɻ ૊৫ͷϝϯόʔΛҭ੒͢Δ લड़ͷʮ֤՝୊ʹϝϯόʔΛΞαΠϯ͠ɺ۩ମతͳखஈ͸ͦͷϝϯόʔͷࡋྔͰܾఆ͢Δʯͱ͍͏ ΍Γํ͸ҭ੒ͷଆ໘΋ඇৗʹڧ͍Ͱ͢ɻγεςϜ 2G ͕औΓ૊Ή՝୊͸ɺγεςϜ 2G ͷΈͳΒͣ΄ ͔ͷνʔϜͷϝϯόʔͷר͖ࠐΈΛඞཁͱ͢Δ΋ͷ͕ଟ͋͘Γ·͢ɻ͕ͨͬͯ͠ɺ֤ϝϯόʔʹϦʔ μʔγοϓ΍εέδϡʔϦϯάͷೳྗ͕ߴ͘ٻΊΒΕɺલ޲͖ʹ෺ࣄΛ਱ߦ͢Δೳྗ͕ٻΊΒΕ· ͢ɻϦʔμʔγοϓͷൃشͷ͠ํΛ֤ϝϯόʔ͕࣮ફ͠ɺ͢ͰʹϦʔμʔγοϓͷೳྗ͕ߴ͍ϝϯ όʔ͕ΑΓΑ͍ਐΊํΛΞυόΠε͢Δ͜ͱͰɺࠓޙͷ૊৫ͱͯ͠ͷڧ͞΍ɺ֤ϝϯόʔͷՁ஋Λߴ ΊΔ͜ͱΛҙ͍ࣝͯ͠·͢*8ɻ ·ͨɺΞαΠϯ͢Δ՝୊͸ϝϯόʔͱ૬ஊ͠ͳ͕Βܾఆ͠·͢ɻܾͯ͠ৄ͘͠ͳ͍ྖҬͰ͋ͬͯ ΋ɺຊਓ͕৳͹͍ͨ͠ྖҬͰ͋Ε͹҆৺ͯ͠೚ͤΔ͜ͱ͕Ͱ͖ΔΑ͏͓ޓ͍͕αϙʔτͰ͖Ε͹ͱ ࢥ͍ͬͯ·͢ɻ 1on1 ͸ִिͰߦ͍ͬͯ·͢ɻςʔϚ͸͔ͬ͠Γͱ͸ܾΊ͓ͯΒͣɺ֤ϝϯόʔ͔ΒఏҊͯ͠΋Β ͏Α͏ʹ͍ͯ͠·͢ɻ࿩୊͕ͳ͍ͱ͖ʹ͸ɺࡶஊͰऴΘΔ͜ͱ΋͋Ε͹ɺΩϟϦΞʹ͍ͭͯͷ࿩Λ͠ ͨΓɺ࠷ۙͷߦಈʹؔ͢ΔϑΟʔυόοΫΛͨ͠Γ͍ͯ͠·͢ɻ·ͩ·ͩ 1on1 ͷεΩϧ͸ߴΊΒΕ Δͱײ͡ΔͷͰɺճ਺ΛॏͶͳ͕ΒɺϞνϕʔςΟϯά΍ίʔνϯάͳͲͷख๏Λ਎ʹண͚͍͚ͯΕ ͹ͱࢥ͍·͢ɻ 5.5 ͜Ε͔Β΍Γ͍ͨ͜ͱ γεςϜ 2G ͸͜Ε·Ͱड़΂ͨΑ͏ʹɺ৽͘͠࡞ͬͨ૊৫Ͱ͋ΓɺͲͷΑ͏ʹࣄۀ੒Ռʹߩݙͯ͠ ͍͔͘Λ໛ࡧ͍ͯ͠Δஈ֊Ͱ͢ɻ͔͠͠ͳ͕Βɺ͢Ͱʹଟ͘ͷ੒ՌΛ࢒͢͜ͱ͕Ͱ͖͍ͯ·͢ɻ *8 ΋ͪΖΜɺखΛಈ͔͢͜ͱ͕࠷΋ॏཁͳͷͰϝϯόʔʹԠͯ͡ෛՙ͕େ͖͘ͳΓ͗͢ͳ͍Α͏චऀ͕αϙʔτ͠·͢ɻ 81
  84. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 5.5 ͜Ε͔Β΍Γ͍ͨ͜ͱ • ૊৫΍։ൃΛ၆ᛌ͠ɺվળΛ࣮ࢪ – ൃੜͨ͠Πϯγσϯτʹର͢ΔޮՌతͳ࠶ൃ๷ࢭࡦͷ࣮ࢪ

    ∗ Τϥʔ͕ద੾ʹ Trace ͞Ε͍ͯͳ͔ͬͨ ˰ ద੾ʹ Trace ͞Εͳ͍Α͏ͳΤϥʔΛ PR ্Ͱܯࠂ͢Δ Linter ͷ௥Ճ ∗ Ξοϓσʔτ͞Εͨ͘ͳ͍σʔλ͕ߋ৽͞Ε͍ͯͨ ˰ Go ͷ struct tag Λ༻͍ͯߋ ৽Λ๷͙Α͏ͳ͘͠Έͮ͘Γ ∗ اը୲౰ऀͷϚελσʔλೖߘϛε͕ଟ͔ͬͨ ˰ PR ্ͰϨϏϡʔΛ͠΍͘͢͢Δ ͘͠Έͮ͘Γ – ։ൃͷ଎౓ɾ඼࣭ͷ޲্ ∗ CircleCI ͷ config ϑΝΠϧ͕๲େ͔ͭॏෳͨ͠ॲཧ͕ଟ͔ͬͨͨΊ matrix job ʹ ΑͬͯεϦϜԽ ∗ Table Driven Test ͷ෍ڭ ∗ TIPSTAR ͷ֤ΫϥΠΞϯτͱڞ༗͍ͯ͠Δ IDL Λ؅ཧ͢ΔϦϙδτϦΛαʔόα ΠυΞϓϦέʔγϣϯͷϦϙδτϦ͔Β෼཭ ∗ ٸᬎඞཁͱͳΔγεςϜͷΠϯϑϥߏஙͱϊ΢ϋ΢ͷ஝ੵ • PIST6 ͷαʔϏε։ൃͱӡ༻ – ϝΠϯετϦʔϜͷ։ൃΑΓઌߦͯ͠ணख͢Δ͜ͱͰɺϓϩμΫτ։ൃͷ଎౓Λอͬͨ· ·ϦϦʔε – ӡ༻Λݟਾ͑ͨ؅ཧը໘ͷ࣮૷ – TIPSTAR ىҼͰͷ PIST6 ো֐Λىͣ͜͞ӡ༻ ϝΠϯͷ։ൃνʔϜ͔Β͸ҰาҾཱ͍ͨͪҐஔͰ՝୊Λݟ͚ͭɺղܾࡦΛଧ͍ͬͯ͘ͱ͍͏ϙδ γϣϯ͔ͩΒͦ͜औΓ૊Ίͨ՝୊΋਺ଟ͋͘Γ·͢ɻ·ͨɺͦΕΒΛ࣋ͬͯଟ͘ͷϊ΢ϋ΢Λάϧʔ ϓ಺ʹཷΊࠐΉ͜ͱ͕Ͱ͖·ͨ͠ɻ ࠓޙͷେ͖ͳςʔϚ͸ҎԼͷ 2 ͭʹͳΔ͔ͱࢥ͍·͢ɻ ։ൃ૊৫΍αʔϏεͷঢ়گͷϞχλϦϯά TIPSTAR Ͱ͸೔ʑଟ͘ͷࢪࡦΛ։ൃ͍ͯ͠·͕͢ɺͦΕΒͷύϑΥʔϚϯε΍։ൃ૊৫ͷঢ়ଶΛ ͔ͬ͠ΓͱϞχλϦϯά͍ͨ͠ͱࢥ͍ͬͯ·͢ɻզʑͷϛογϣϯ͸ʮϢʔβʔ΁ͷՁ஋ఏڙ଎౓ͷ ࠷େԽʯͰ͢ɻͨͱ͑͹ো֐͕ൃੜͨ͠Βɺͦͯͦ͠ͷো֐͕௕Ҿ͘ͱ౰વ։ൃͷखΛ΍Ίͯௐࠪɾ ϢʔβʔରԠΛߦ͏ඞཁ͕ग़͖ͯ·͢ɻো֐Λى͜͞ͳ͍γεςϜͮ͘Γ΍ɺͦΕΛͰ͖Δ૊৫ͮ͘ ΓΛ͍ͯ͘͠ඞཁ͕͋Γ·͢ɻ ͍͍ͬͨԿ͕ݪҼͰো֐͕ى͜Δͷ͔Λ͔ͬ͠Γߟ࡯͠ɺվળ͍ͯͨ͘͠Ίʹ΋ϞχλϦϯά͸ඇ ৗʹॏཁͩͱߟ͍͑ͯ·͢ɻͦͷͨΊͷج൫ͮ͘ΓʹऔΓ૊ΜͰ͍͖·͢ɻ 82
  85. ୈ 5 ষ ΤϯδχΞϦϯάϚωδϟʔ൒೥͘Β͍΍ͬͯΈͨ 5.6 ·ͱΊ ։ൃͷվળΛϝΠϯετϦʔϜͷ։ൃ૊৫͕औΓ૊Ήαϙʔτ γεςϜ 2G ͸։ൃͷվળʹશྗͰऔΓ૊ΜͰ͍·͢ɻཧ૝ͱ͢Δͱ͜Ζ͸ɺ͜ΕΒͷվળΛγε

    ςϜ 2G ͚͕ͩߦ͏ͷͰ͸ͳ͘ɺ։ൃͷϝΠϯετϦʔϜ͕ओମతʹվળʹऔΓ૊ΊΔঢ়ଶʹ͢Δ͜ ͱͰ͢ɻͦ͏͍ͬͨจԽ΍͘͠ΈΛ࡞͍ͬͯ͘͜ͱ͕զʑͷ࠷ऴతͳ໨ඪͰ͋ΓɺͦΕΛ੒͠਱͛ͨ ͱ͖ʹϛογϣϯΛୡ੒ͨ͠ͱݴ͑Δͱࢥ͍ͬͯ·͢ɻ ͦͷͨΊʹ͸ɺυΩϡϝϯτͰ͋ͬͨΓɺطଘͷΞϓϦέʔγϣϯ΍։ൃελΠϧɾϫʔΫϑϩʔ ͷվળͳͲ͕ඇৗʹॏཁͰ͢ɻγεςϜ 2G ࣗ਎͕ϦʔμʔγοϓΛ࣋ͬͯͦ͏͍ͬͨ౔৕Λ࡞ͬͯ ͍͘͜ͱɺͦͯͦ͠ͷϊ΢ϋ΢ΛେऺԽ͍͖ͯ͠·͢ɻ 5.6 ·ͱΊ EM ͕Ռͨ͢΂͖໾ׂͱ͸ʮϝϯόʔͷεϖγϟϦςΟΛ׆͔͠ɾ৳͹͠ͳ͕Β૊৫੒Ռʹߩݙ͠ ͍ͯ͘͜ͱʯͰ͋Δͱɺ͜ͷ൒೥ͪΐͬͱͰؾ෇͘͜ͱ͕Ͱ͖·ͨ͠ɻͦͷͨΊʹ EM ͸ɺ֤ϝϯ όʔͷεϖγϟϦςΟΛ೺Ѳ͢Δඞཁ͕͋Δ͠ɺͦΕΒʹֶ͍ͭͯͿඞཁ͕͋Γ·͢ɻͦͯ͠·ͨ αʔϏεʹؔΘΔ૊৫શମͷঢ়گ΍ώτɾϞϊɾΧωͷಈ͖Λཧղͨ͠͏͑Ͱɺࣗ෼ͨͪͷ૊৫͕Ͳ ͜Λ໨ࢦ͢ͷ͔໌֬ʹࣔ͢͜ͱ͕ॏཁͰ͢ɻ ൒೥ͱ͍͏୹͍ظؒͰ͸͋Γ·͕͢ɺͨ͘͞Μ೰Έɺ׉౻͠ɺ౰વࣦഊ΋ͨ͘͞Μ͖ͯ͠·͕ͨ͠ɺ ͦΕʹ͍͖ͭͯͯ͘Εͯͨ͘͞Μͷ੒ՌΛ্͛ͯ͘Ε͍ͯΔϝϯόʔʹײँ͔͋͠Γ·ͤΜɻҾ͖ଓ ͖൴Β͕࠷େݶ׆༂Ͱ͖Δ؀ڥͮ͘Γʹᬏਐͭͭ͠ɺචऀࣗ਎΋ΤϯδχΞͱͯ͠ɺϚωδϟʔͱ͠ ͯɺ੒௕͍͚ͯ͠Ε͹ͱࢥ͍ͬͯ·͢ɻ 83
  86. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ feature toggle

    ʢผ໊ feature flagʣͱ͸ ։ൃ͢Δػೳͷಈ࡞Λ༗ޮԽ͢Δ͔ແޮԽ͢Δ͔Λ੍ޚ ͢ΔͨΊͷख๏Ͱ͢ɻ Ϧετ 6.1: featureToggleExample.ts 1: function someFunction () { 2: if (featureToggle) { 3: newFunc(); // feature toggle ͕༗ޮԽ͞Ε͍ͯΔͱ͖ͷΈ࣮ߦ 4: return; 5: } 6: 7: oldFunc(); 8: } ্هͷίʔυྫͷΑ͏ʹ boolean ͳͲͷ஋Λ࣋ͬͨ feature toggle Λ༻͍ͯॲཧΛ෼ذ͢Δ͜ͱ Ͱ࣮ݱ͠·͢ɻ͜Ε͸ɺτϥϯΫϕʔε։ൃͱ͍͏։ൃख๏Λ࣮ફ͢ΔͨΊͷख๏ͰɺػೳϦϦʔ ελΠϛϯάͱ main ϒϥϯν΁ͷػೳࠩ෼ͷϚʔδͷλΠϛϯάΛ෼཭͢Δ͜ͱͰɺখ͞ͳࠩ෼ Ͱͷ Pull Request ΍ɺϦϦʔεස౓ͷ૿ՃΛ࣮ݱ͠΍͘͢ͳΓ·͢ɻ·ͨɺػೳϦϦʔελΠϛϯ άΛ੍ޚ͢Δ͜ͱͰҰ෦ϢʔβʔʹػೳΛ։์ͯ͠ AB ςετ͢Δ͜ͱ΋ՄೳʹͳΓ·͢ɻGoogle Analytics ͳͲͷ෼ੳπʔϧΛҰॹʹ࢓ࠐΉ͜ͱͰશϢʔβʔʹϦϦʔε͢Δલʹ৽͍͠ػೳͷޮՌ ଌఆ͕Ͱ͖·͢ɻ஫ҙ఺ͱͯ͠ɺfeature flag Ͱಈ࡞Λ੍ޚ͍ͯͯ͠΋৽ػೳͷιʔείʔυࣗମ͸ όϯυϧ͞ΕΔͷͰɺWeb ΫϥΠΞϯτͳͲϢʔβʔ͔Βιʔείʔυ͕ݟ͑Δ؀ڥͷ৔߹ɺϩʔ ΧϧͰॲཧΛॻ͖׵͑Δ͜ͱͰ༗ޮԽ͞ΕΔՄೳੑ͕͋Δ͜ͱΛཹҙ͢Δඞཁ͕͋Γ·͢ɻ 85
  87. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.1 τϥϯΫϕʔε։ൃ

    6.1 τϥϯΫϕʔε։ൃ τϥϯΫϕʔε։ൃͱ͸ main ϒϥϯνʢτϥϯΫʣʹ feature ϒϥϯνͳͲͷࠩ෼Λ௚઀Ϛʔ δ͠ͳ͕Βɺmain ϒϥϯνΛৗʹσϓϩΠՄೳͳঢ়ଶΛ࡞ΔͨΊͷϒϥϯνઓུͰ͢ɻGitflow ͳ ͲͷΑ͏ʹ develop ϒϥϯν͸࣋ͨͣɺ CI ͷࣗಈςετʹΑͬͯίʔυͷ඼࣭Λอূ͠·͢ɻϦ Ϧʔεલͷࠩ෼΋ main ϒϥϯνʹϚʔδͯ͠ɺfeature toggle Λιʔείʔυʹ࢓ࠐΉ͜ͱͰಈ࡞ Λ੍ޚ͠·͢ɻҰͭ͋ͨΓͷ Pull Request ͷࠩ෼ΛͰ͖Δ͚ͩখ͘͢͞Δ͜ͱΛҙࣝ͢Δ͜ͱͰϨ Ϗϡʔ޻਺ΛݮΒ͠ɺϚʔδ·ͰͷظؒΛ୹ͯ͘͠ɺϦϦʔεස౓ɾ։ൃ଎౓ͷ޲্Λ໨ࢦ͠·͢ɻ feature toggle ͷ؅ཧํ๏ʹ͸ɺࣗલͰ؅ཧج൫Λ༻ҙ͢Δํ๏΍ɺOSS ΍ SaaS Λར༻͢Δํ๏͕ ͋Γ·͢ɻࠓճ͸ OSS Ͱ͋Δ Unleash ͱ͍͏ feature toggle ؅ཧαʔϏεʹ͍ͭͯ঺հ͠·͢ɻ 6.2 Unleash ͱ͸ Unleash ͱ͸ɺfeature toggle Λ؅ཧ͢ΔͨΊͷ OSS Ͱ͢ɻηϧϑϗεςΟϯάͷ΄͔ɺ͢Ͱʹ Unleash ଆͰϗεςΟϯά͞Ε͍ͯΔ΋ͷΛར༻͢Δ͜ͱ΋Ͱ͖ɺӡ༻ํ๏ͷࣗ༝౓͕ߴ͍αʔϏ εͰ͢ɻGUI ͷ؅ཧը໘͕༻ҙ͞Ε͍ͯͯඇΤϯδχΞͰ΋ feature toggle ͷ༗ޮɾແޮͷ੍ޚͰ ͖ΔͷͰɺػೳͷϦϦʔεΛΤϯδχΞҎ֎ͷਓ͕ߦ͏͜ͱ͕ՄೳʹͳΓ·͢ɻ·ͨɺUnleash ͷ ΞΧ΢ϯτ͝ͱʹݖݶΛ؅ཧ͢Δ͜ͱ΋Ͱ͖Ұ෦ͷΞΧ΢ϯτʹ͚ͩॻ͖ࠐΈݖݶΛ༩͑ΔͳͲ͠ ͯɺ feature toggle ͷૢ࡞Λ҆શʹߦ͏͜ͱ͕Ͱ͖·͢ɻWeb hook ΍ slack ௨஌ʹ΋ରԠ͍ͯͯ͠ feature toggle ͷมߋΛ͙͢ʹνʔϜʹ఻͑Δ͜ͱ͕Ͱ͖ͨΓ๛෋ͳػೳΛඋ͍͑ͯ·͢ɻUnleash ͷγεςϜ͸͍͔ͭ͘ͷαʔϏεΛ૊Έ߹ΘͤΔ͜ͱͰ࣮ݱ͍ͯ͠·͢ɻ ਤ 6.1: Unleash ͷγεςϜߏ੒ਤ Unleash Admin UI Unleash ؅ཧը໘Λఏڙ͢ΔϑϩϯτΤϯυ Unleash API Unleash Admin UI ΍ Unleash ϓϩΩγ ͳͲͱ΍ΓͱΓͯ͠σʔλϕʔε্ͷ feature 86
  88. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.3 Unleash

    ͷಋೖ toggle Λ؅ཧ͢Δ API Unleash ϓϩΩγ ΫϥΠΞϯτͷ Unleash SDK ͱ Unleash API ͷؒʹଘࡏ͢ΔϓϩΩγɻΫϥΠΞϯτͷ Ϣʔβʔ͝ͱʹެ։͢Δ feature toggle Λ੍ݶͨ͠Γɺ API ϦΫΤετΛΩϟογϡ͢Δ͜ ͱͰύϑΥʔϚϯεΛ޲্ͤ͞Δ໾ׂͳͲΛ୲͍ͬͯ·͢ɻ Unleash SDK Unleash API ͱ௚઀௨৴ͯ͠ feature toggle ͕༗ޮ͔ແޮ͔Λ֬ೝ͢ΔػೳΛఏڙ͍ͯ͠· ͢ɻαʔόαΠυͰͷΞϓϦέʔγϣϯͰ࢖༻͢Δ SDK Ͱ͢ɻ Unleash ϓϩΩγ SDK ΫϥΠΞϯτͷΞϓϦέʔγϣϯͱ Unleash ϓϩΩγ ͷ΍ΓͱΓ΍ɺ feature toggle ͕༗ޮ ͔ແޮ͔Λ֬ೝ͢ΔػೳΛఏڙ͍ͯ͠·͢ɻ feature toggle ͷछྨ Unleash Ͱ͸༻్ʹ߹Θͤͯ͞·͟·ͳछྨͷ feature toggle ͕༻ҙ͞Ε͍ͯ·͢ɻfeature toggle ͷछྨʹΑͬͯϥΠϑλΠϜͱ͍͏ Deprecated ʹͳΔ·ͰͷϦϛοτ͕ઃఆ͞Ε͍ͯ· ͢ɻ͜ͷϥΠϑλΠϜ͕ઃఆ͞Ε͍ͯΔ͜ͱʹΑͬͯ͢Ͱʹ Deprecated ʹͳͬͨ feature toggle Λ slack Ͱ௨஌͢ΔͳͲɺ͍ͭ·Ͱ΋ϓϩδΣΫτίʔυʹ࢒Γଓ͚Δ͜ͱΛ๷͙͜ͱ͕Ͱ͖·͢ɻ Release ༧ఆ͞ΕͨϦϦʔε೔·ͰػೳΛແޮԽ͢ΔͨΊͷϑϥάɻϥΠϑλΠϜ͸ 40 ೔ɻ Experiment AB ςετʹ࢖༻͢ΔػೳΛ੾Γସ͑ΔͨΊͷϑϥάɻϥΠϑλΠϜ͸ 40 ೔ɻ Permission ಛఆͷϢʔβʔͷΈػೳΛ੾Γସ͑ΔͨΊͷϑϥάɻϥΠϑλΠϜ͸ಛʹͳ͘ɺ Deprecated ʹͳΔ͜ͱ͸ͳ͍ɻ Operational γεςϜͷӡ༻ͷͨΊʹ͘͝୹ظ͚ؒͩѻ͏ϑϥάɻϥΠϑλΠϜ͸ 7 ೔ɻ Kill switch ͢ͰʹϦϦʔε΋͞Ε͍ͯΔ͕ܧଓͯ͠ػೳͷແޮԽɾ༗ޮԽΛ੍ޚ͍ͨ͠έʔεͷͨΊͷϑ ϥάɻϥΠϑλΠϜ͸ಛʹͳ͘ɺ Deprecated ʹͳΔ͜ͱ͸ͳ͍ɻ 6.3 Unleash ͷಋೖ Unleash ΛϓϩδΣΫτʹಋೖ͍ͯͨ͘͠ΊͷαϯϓϧΛ Web ΫϥΠΞϯτͷ࣮૷Λத৺ʹ঺հ ͠·͢ɻαʔόଆ͸΄΅ύοέʔδΛϗεςΟϯά͢ΔͷΈͰ͢ͷͰɺಛผίʔυΛॻ͍ͨΓͤͣʹ ࡁΈ·͢ɻ 87
  89. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.3 Unleash

    ͷಋೖ αʔόͷ༻ҙ Unleash Ͱ͸ Admin UI ͱ Unleash API ΛηϧϑϗεςΟϯάͰ͖·͢ɻެࣜͷ Docker Πϝʔ δ ΍ Helm νϟʔτ͕༻ҙ͞Ε͍ͯΔͷͰ͜ΕΛར༻ͯ͠ Unleash API ͱ Admin UI Λ༻ҙͰ͖ ·͢ɻ Unleash ϓϩΩγ ͷ༻ҙ લड़ͷ௨ΓΫϥΠΞϯτͱ Unleash API ͱͷ௨৴ʹ͸ Unleash ϓϩΩγ ΛڬΉඞཁ͕͋Γ· ͢ɻUnleash ϓϩΩγ ͸ Node.js ͔ Docker Ͱಈ͔͢͜ͱ͕Ͱ͖·͢ɻجຊతʹ͸؀ڥม਺ͱͯ͠ Unleash API ͷΤϯυϙΠϯτ΍ API τʔΫϯΛ౉͚ͩ͢ͰࡁΈ·͢ɻAPI τʔΫϯ͸ Admin UI ͔ΒൃߦͰ͖·͢ɻ ΫϥΠΞϯτʹ Unleash SDK Λಋೖ αϯϓϧίʔυͱͯ͠ Vanilla JS *1 ͷྫΛ঺հ͠·͢ɻJavaScript ༻ͷ Unleash SDK Λϓϩ δΣΫτʹΠϯετʔϧͯ͠ɺॳظԽॲཧΛॻ͖·͢ɻ Ϧετ 6.2: shell 1: npm install unleash-proxy-client ॳظԽͷࡍʹ͸ͦΕͧΕ؀ڥม਺Λ౉͠·͢ɻ Ϧετ 6.3: featureToggle.ts 1: import { UnleashClient } from "unleash-proxy-client"; 2: 3: const unleash = new UnleashClient({ 4: url: UNLEASH_PROXY_URL, // Unleash Proxy ΤϯυϙΠϯτ 5: clientKey: UNLEASH_CLEANT_KEY, // Unleash API Ωʔ 6: appName: UNLEASH_APP_NAME, // ೚ҙͷ໊લ 7: }); 8: 9: unleash.updateContext({ userId: UNLEASH_USER_ID }); 10: 11: unleash.start(); ॳظԽ͕׬ྃͨ͠Β isEnabled ϝιουʹΑͬͯ೚ҙͷ feature toggle ͷ஋ΛݕূͰ͖·͢ɻ *1 ϥΠϒϥϦ΍ϑϨʔϜϫʔΫΛ࢖͍ͬͯͳ͍ૉͷ JS ͷ͜ͱ 88
  90. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.4 ܕ҆શʹ

    feature toggle Λ؅ཧ͢Δ Ϧετ 6.4: featureToggleExample.ts 1: function someFunction () { 2: if (unleash.isEnabled("someFeatureToggle")) { 3: newFunc(); // feature toggle ͕༗ޮԽ͞Ε͍ͯΔͱ͖ͷΈ࣮ߦ 4: return; 5: } 6: 7: oldFunc(); 8: } جຊతʹ͸্هͷखॱͰ Unleash ΛΫϥΠΞϯτͰར༻Ͱ͖ΔͷͰ͕͢ɺҎԼͷΑ͏ͳ໰୊఺͕ ߟ͑ΒΕ·͢ɻ • ݕূ͠Α͏ͱ͍ͯ͠Δ feature toggle ͕ଘࡏ͢Δ͔Ͳ͏͔Λ੩తʹݕ஌Ͱ͖ͳ͍ɻ • σόοάͳͲݕূ࣌ʹ feature toggle ΛҰ࣌తʹ੾Γସ͍ͨ࣌ʹಉ͡؀ڥͷΫϥΠΞϯτ͢ ΂ͯʹӨڹͯ͠͠·͏ɻ ࠓճ͸͜ΕΒͷ໰୊ʹ͍ͭͯɺϩʔΧϧͰ΋ feature toggle ͷ஋Λอ࣋͢ΔͳͲͯ͠ղܾΛࢼΈͯ Έ·͢ɻ 6.4 ܕ҆શʹ feature toggle Λ؅ཧ͢Δ ܕ҆શʹ feature toggle Λ؅ཧ͢Δ͜ͱʹΑͬͯɺଘࡏ͠ͳ͍ feature toggle Λ CI ͳͲͰͷܕ νΣοΫͰݕ஌ͯ͠όάͷࠞࡏΛ๷͙͜ͱ͕Ͱ͖·͢ɻfeature toggle ͕ଘࡏ͢Δ͔Ͳ͏͔੩తʹݕ ஌͢ΔͨΊʹ͸ɺίʔυϕʔεͰඞཁͳ feature toggle Λอ࣋ͯ͠ TypeScript ͕ܕͱͯ͠ feature toggle Λਪ࿦Ͱ͖Δඞཁ͕͋Γ·͢ɻͦͷͨΊʹࣄલʹଘࡏ͢Δ feature toggle ͢΂ͯΛऔಘͯ͠ ܕਪ࿦ՄೳͳܗͷϑΥʔϚοτͰอଘͯ͠Έ·͢ɻࠓճ͸ Node.js Ͱଘࡏ͢Δ feature toggle Ұཡ ͷ഑ྻͷ TypeScript ϑΝΠϧΛు͖ग़͢εΫϦϓτΛ૊ΜͰΈ·ͨ͠ɻ Node.js ༻ͷ SDK ΛΠϯετʔϧ͠·͢ɻ Ϧετ 6.5: shell 1: npm install -D unleash-client ೚ҙͷσΟϨΫτϦʹҎԼͷΑ͏ͳ feature toggle Ұཡͷ഑ྻͷ TypeScript ϑΝΠϧΛు͖ग़͢ εΫϦϓτϑΝΠϧΛ࡞੒͠·͢ɻదٓ Unleash ͷ ؀ڥม਺Λ౉͍ͯͩ͘͠͞ɻ 89
  91. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.4 ܕ҆શʹ

    feature toggle Λ؅ཧ͢Δ Ϧετ 6.6: generateFeatureToggles.js 1: const fs = require("fs"); 2: const path = require("path"); 3: const unleash = require("unleash-client"); 4: 5: const OUTPUT_FILE = path.resolve("featureToggleNames.ts"); 6: const MODULE_NAME = "featureToggles"; 7: 8: const main = async () => { 9: await unleash.startUnleash({ 10: url: UNLEASH_API_URL, 11: appName: UNLEASH_APP_NAME, 12: customHeaders: { 13: Authorization: UNLEASH_CLEANT_KEY, 14: }, 15: }); 16: 17: const toggles = unleash.getFeatureToggleDefinitions(); 18: 19: const toggleNames = toggles.map((toggle) => toggle.name); 20: 21: const fileContent = ‘export const ${MODULE_NAME} = ${JSON.stringify( 22: toggleNames 23: )} as const;‘; 24: 25: await fs.promises.writeFile(OUTPUT_FILE, fileContent); 26: }; 27: 28: main().catch((error) => { 29: console.error(error); 30: process.exit(1); 31: }); node ίϚϯυ͔Β࡞੒ͨ͠εΫϦϓτΛ࣮ߦ͢Δ͜ͱͰ TypeScript ϑΝΠϧ͕ు͖ग़͞ΕΔͱ ࢥ͍·͢ɻ path/to ʹ͋ͨΔͱ͜Ζ͸దٓॻ͖׵͍͑ͯͩ͘͞ɻ Ϧετ 6.7: shell 1: node path/to/generateFeatureToggles.js ࠓճ࡞੒ͨ͠εΫϦϓτͰ͸ featureToggles Ϟδϡʔϧ ͱͯ͠ feature toggle ҰཡΛ഑ྻͷఆ਺ ͱͯ͠ export ͨ͠΋ͷΛੜ੒͢ΔΑ͏ʹ͍ͯ͠·͢ɻ 90
  92. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.5 ϩʔΧϧͷΈͰҰ࣌తʹ

    feature toggle Λ੾Γସ͑Δ Ϧετ 6.8: featureToggleNames.ts 1: export const featureToggles = [...] as const; ੜ੒ͨ͠ feature toggle Ұཡͷ഑ྻ͔ΒϩʔΧϧͰͷ feature toggle ͷ map Λੜ੒ͨ͠Γɺ feature toggle ͷ஋Λऔಘ͢Δࡍʹ feature toggle Ұཡͷ഑ྻʹؚ·ΕΔ஋ͷΈ͕ࢦఆ͞ΕΔΑ͏ʹ ܕΛઃఆ͠·͢ɻ Ϧετ 6.9: featureToggle.ts 1: import { UnleashClient } from "unleash-proxy-client"; 2: import { featureToggles } from "path/to/featureToggleNames"; 3: 4: const toggles = Object.fromEntries(featureToggles.map((key) => [key, false])); 5: 6: export const startUnleash = async () => { 7: const updateToggles = () => { 8: const featureToggles = unleash.getAllToggles(); 9: 10: featureToggles.forEach((toggle) => { 11: if (toggle.name in toggles) { 12: toggles[toggle.name] = toggle.enabled; 13: } 14: }); 15: }; 16: 17: unleash.on("ready", updateToggles); 18: 19: await unleash.start(); 20: }; 21: 22: export const getFeatureToggleEnabled = (name: typeof featureToggles[number]) => { 23: return toggles[name]; 24: }; 6.5 ϩʔΧϧͷΈͰҰ࣌తʹ feature toggle Λ੾Γସ͑Δ ϩʔΧϧͰ feature toggle ͷ஋Λอ࣋͢Δ͜ͱͰҰ࣌తʹϩʔΧϧ؀ڥͷΈͰ feature toggle ͷ ੾Γସ͑ΛͰ͖ΔΑ͏ʹͯ͠Έ·͢ɻܕ҆શʹ feature toggle Λ؅ཧ͢Δ ͷ߲ͰɺεΫϦϓτͰੜ ੒ͨ͠഑ྻΛ΋ͱʹϩʔΧϧͰ feature toggle ͷ஋Λ࣋ͭΑ͏ʹͨ͠ͷͰɺ͜ͷ஋Λߋ৽͢Δؔ਺Λ ༻ҙ͠·͢ɻ 91
  93. ୈ 6 ষ Unleash Λ࢖ͬͨ feature toggle ؅ཧ 6.6 ऴΘΓʹ

    Ϧετ 6.10: featureToggle.ts 1: const toggles = Object.fromEntries(featureToggles.map((key) => [key, false])); 2: 3: export const updateFeatureToggles = (featureToggles: typeof toggles) => { 4: Object.keys(toggles).forEach((toggle) => { 5: if (toggle in featureToggles) { 6: toggles[toggle] = featureToggles[toggle]; 7: } 8: }); 9: }; ·ͨɺࠓճ͸ϝϞϦ্Ͱͷ feature toggle ͷ஋ͷ؅ཧํ๏Λ঺հ͠·͕ͨ͠ɺlocal storage ΍ session storage ͳͲΛ׆༻͢Δ͜ͱͰϖʔδϦϩʔυͯ͠΋ϩʔΧϧͰͷ feature toggle ͷ஋ΛӬ ଓԽ͢Δ͜ͱ΋Ͱ͖·͢ɻ 6.6 ऴΘΓʹ feature toggle ͷ؅ཧํ๏ͻͱͭͱͬͯ΋ɺͱͯ΋ͨ͘͞Μͷํ๏͕͋Γ·ͩ·ͩվળ͍͚ͯ͠Δ ͜ͱ΋ଟ͍ͱࢥ͏ͷͰΑΓྑ͍։ൃମݧͷ࣮ݱͷͨΊʹ΋ͬͱݚڀ͍͖͍ͯͨ͠ͳͱࢥ͍ͬͯ·͢ɻ Ͳ͔͜ͷϓϩδΣΫτͰ͜ͷ஌ݟ͕গ͠Ͱ΋໾ཱͭͱ͏Ε͍͠Ͱ͢ɻ 92
  94. ஶऀ঺հ দݪ ৴஧ (ୈ 1 ষ୲౰) ॴଐ͸ϞϯεταʔόνʔϜͰ Ruby ΍ Go

    Λॻ͍ͯΔɻϓϩάϥϛϯά͕޷͖Ͱɺීஈ͸ਪ ͠ݴޠͷ Haskell Ͱ༡ΜͩΓɺ৽͍͠ϓϩάϥϛϯάݴޠΛษڧͨ͠Γ͍ͯ͠ΔɻHaskell-jp ΍ Elm-jp Ͱͪΐͪ͜ΐ͜׆ಈ΋͍ͯ͠Δɻ ɹ ߐാ ୓࠸ (ୈ 2 ষ୲౰, Github: MokkeMeguru, Twitter: @MeguruMokke) ੜ·Ε͔ͯΒ Lisp ͔͠ॻ͍ͨ͜ͱ͕ͳ͍ਓͰ͢ɻઌ೔ιγϟήͷ͓Έ͘͡ͰେڟΛҾ͖· ͨ͠ɻ ɹ riddle (ୈ 3 ষ୲౰, Medium: @riddle) ։ൃຊ෦ CTO ࣨ SRE άϧʔϓ ॴଐɻपΓͷਓ͕͢͜͠Ͱ΋ศརʹͳΔΑ͏ͳ Developer Experience(։ൃମݧ) ͷ޲্΍ɺޙͰ࢖͍·Θ͠Λ͠΍͍͢ίʔυ΍ίϝϯτΛॻ͘ͷʹ࠷ ۙ͸ϋϚ͍ͬͯ·͢ʂʂ ɹ ௡ా ګฏ (ୈ 4 ষ୲౰, GitHub,Medium,Qiita: flatfisher, Twitter: @canoefishing) ࣍ੈ୅ΤϯλʔςΠϯϝϯτࣄۀຊ෦ TIPSTAR ։ൃ෦ γεςϜ 1 άϧʔϓ Ϛωʔδϟʔɻ TIPSTAR ͷαʔόʔαΠυνʔϜͰ։ൃɾӡ༻΍ϓϩδΣΫτϦʔυΛ͍ͯ͠·͢ɻ௼Γ͕ झຯͳͷͰʮ௼ΓʷςΫϊϩδʔʯͱ͍ͬͨςʔϚʹऔΓ૊Ήͷ͕޷͖Ͱ͢ɻ ɹ ഡݪ ྋհ a.k.a. ΒΓΐ͢ (ୈ 5 ষ୲౰, Twitter: @raryosu) 2018 ೥৽ଔɻ՝୊ղܾ԰͞Μͱͯ͠ TIPSTAR ͷαʔϏεӡӦɾ։ൃʹؔ͢Δ՝୊ղܾʹऔ Γ૊Ήɻ2021 ೥ 4 ݄͔ΒɺTIPSTAR ͰΤϯδχΞϦϯάϚωʔδϟʔʹब͘ɻ࠷ۙδϜʹ ௨͍ͩͯ͠ےτϨͨ͠Γ਎ମΛಈ͔͢ͷ͕޷͖ɻ૫݈ͤͯ߁ମʹͳΓ͍ͨɻ ɹ ɹ 93
  95. ෇࿥ ஶऀ঺հ ٢઒ ༐ଠ࿠ (ୈ 6 ষ୲౰, GitHub: yutaroyoshikawa) 2021

    ೥৽ଔೖࣾɻ࣍ੈ୅ΤϯλʔςΠϯϝϯτࣄۀຊ෦ TIPSTAR ։ൃ෦ ΫϥΠΞϯτά ϧʔϓ Web ։ൃνʔϜॴଐɻWebGL ৮ΔͷͱΠϯυΧϨʔ͕޷͖Ͱ͢ɻ ɹ ౢࣇ ະདྷ (Graphic designer / Art director) σβΠϯຊ෦ ΫϦΤΠςΟϒࣨɻΞΠίϯΠϥετɾදࢴσβΠϯ౳Λ୲౰ɻҾ͖ଓ͖୲౰ ͍͖ͤͯͨͩ͞·ͨ͠ɻײँʂ ɹ تଟ ޭ࣍ (੍࡞ਐߦ, Twitter: @kojikita) CTO ࣨ DevRel άϧʔϓͰษڧձ΍ΠϕϯτͷӡӦΛ͍ͯ͠·͢ɻ࠷ۙͷ஫ྗࣄۀ͸ࢠҭͯ Ͱ͢ɻ ɹ ӹࢁ ઍय़ (੍࡞ਐߦ, Twitter: @charlielog_ggg) IT اۀͰͷҰਓ࠾༻޿ใΛܦͯɺ2021 ೥ 11 ݄ʹϛΫγΟͷ CTO ࣨ DevRel άϧʔϓʹδϣ Πϯ͠·ͨ͠ɻ࠷ۙ͸ΤάΊͷϛεςϦখઆͱ͔Θ͍͍ύϑΣ͕޷͖Ͱ͢ɻ ɹ 94
  96. mixi tech note #07 2022 ೥ 1 ݄ 22 ೔ɹॳ൛ୈ

    1 ࡮ɹൃߦ ஶɹऀ גࣜձࣾϛΫγΟ ༗ࢤ ൃߦॴ גࣜձࣾϛΫγΟ ҹ࡮ॴ ೔ޫاը ɹ ˜ mixi, Inc.