Slide 1

Slide 1 text

オブザーバビリティ実践編 株式会社サイバーエージェント AI事業本部 岩 見 彰太 GitHub:@BIwashi X: @B_Sardine

Slide 2

Slide 2 text

自己 紹介 岩 見 彰太 / Iwamin 株式会社サイバーエージェント ೥౓৽ଔೖࣾ "*ࣄۀຊ෦ڠۀϦςʔϧϝσΟΞ%JW アプリ運 用 カンパニー @BIwashi @B_Sardine ࣗಈੜ੒Λ׆༻ͨ͠ɺӡ༻อकίετΛ཈͑Δ&SSPS"MFSU 3VOCPPLͷҰݩू໿؅ཧ 'FBUVSF'MBH%FFQ%JWF4QFBLFS%FDL

Slide 3

Slide 3 text

注意 • ハンズオンでは少し Go を書きます 多 言 語でも似たように記述したりするので雰囲気を掴んでもらえればOKです • 全員共通の AWS、Datadog アカウントを使います Perman で 入 れるようになっていればOK • ツール群に関してはさわりぐらいしか解説できません ハンズオンの時間に遊んでみましょう!

Slide 4

Slide 4 text

突然ですが

Slide 5

Slide 5 text

旅 行 で忘れ物をした時 どうやって 見 つけますか?

Slide 6

Slide 6 text

北海道旅 行 財布紛失編

Slide 7

Slide 7 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港

Slide 8

Slide 8 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光

Slide 9

Slide 9 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光

Slide 10

Slide 10 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光

Slide 11

Slide 11 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭 山 動物園

Slide 12

Slide 12 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭 山 動物園 旭川空港

Slide 13

Slide 13 text

๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 北海道旅 行 に 行 こう 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭 山 動物園 旭川空港 札幌 ・小 樽 知床 ・ 網 走 富良野 ・ 美瑛 ・ 旭川

Slide 14

Slide 14 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭 山 動物園 旭川空港 札幌 ・小 樽 知床 ・ 網 走 富良野 ・ 美瑛 ・ 旭川 空港 札幌 小 樽 網 走 監獄 旭 山 動物園 空港

Slide 15

Slide 15 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭 山 動物園 旭川空港 札幌 ・小 樽 知床 ・ 網 走 富良野 ・ 美瑛 ・ 旭川 空港 札幌 小 樽 網 走 監獄 旭 山 動物園 空港 財布紛失!

Slide 16

Slide 16 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭 山 動物園 旭川空港 Service A Sevice B Service C func 1 func 2 func 3 func 4 func 5 func 5 旅 行 (リクエスト)して返ってきたら 財布がなくっていた(エラー発 生 ) Error

Slide 17

Slide 17 text

北海道旅 行 に 行 こう ๺ւಓཱྀߦɾ๺ւಓπΞʔͳΒཱྀ֨҆ߦͷ+53*1 新千歳空港 札幌観光 小 樽観光 網 走 観光 旭川動物園 旭川空港 Service A Sevice B Service C func 1 func 2 func 3 func 4 func 5 func 5 旅 行 (リクエスト)して返ってきたら 財布がなくっていた(エラー発 生 ) Error Ͳ͜ͰԿ͕ى͖͍͔ͯͨ೺Ѳ͢Δͷ͕େ੾

Slide 18

Slide 18 text

Observability とは

Slide 19

Slide 19 text

Observability is なに?

Slide 20

Slide 20 text

Observability = 可観測性…? ߤۭػͷӡಈํఔࣜ౔԰ݚڀࣨ あの 行 列のやつ…? 航空宇宙 工 学科出 身

Slide 21

Slide 21 text

制御 工 学、機械 工 学における可観測性 • システムの出 力 を観測することで、そのシステムの全ての状態変数の成分を 知ることができるか否かという特性 システムの振る舞いを出 力 から知ることができるか否か

Slide 22

Slide 22 text

Observability is なに? ソフトウェアシステムにおける

Slide 23

Slide 23 text

ソフトウェアシステムにおけるo 11 y ؆୯ʹ͍͏ͱɺࢲ͕ͨͪߟ͑Διϑτ΢ΣΞγεςϜͷʮΦϒβʔόϏϦςΟʯͱ͸ɺ γεςϜ͕ͲͷΑ͏ͳঢ়ଶʹͳͬͨͱͯ͠΋ɺͦΕ͕ͲΜͳʹ࢐৽Ͱحൈͳ΋ͷͰ͋ͬ ͯ΋ɺͲΕ͚ͩཧղ͠આ໌Ͱ͖Δ͔Λࣔ͢ई౓Ͱ͢ɻ ·ͨɺͦͷΑ͏ͳ࢐৽Ͱحൈͳঢ়ଶʹରͯ͠΋ɺࣄલʹσόοάͷඞཁੑΛఆٛͨ͠Γ ༧ଌͨ͠Γ͢Δ͜ͱͳ͘ɺγεςϜͷঢ়ଶσʔλͷ͋ΒΏΔσΟϝϯγϣϯ΍ͦΕΒͷ ૊Έ߹Θͤʹ͍ͭͯΞυϗοΫʹௐࠪ͠ɺΑΓσόοά͕ՄೳʹͳΓΑ͏ʹ͢Δඞཁ͕ ͋Γ·͢ɻ ΋͠ɺ৽͍͠ίʔυΛσϓϩΠ͢Δඞཁ͕ͳ͘ɺͲΜͳ࢐৽Ͱحൈͳঢ়ଶͰ΋ཧղͰ͖ ΔͳΒɺΦϒβʔόϏϦςΟ͕͋Δͱݴ͑·͢ɻ by ΦϒβʔόϏϦςΟɾΤϯδχΞϦϯά

Slide 24

Slide 24 text

ソフトウェアシステムにおけるo 11 y ؆୯ʹ͍͏ͱɺࢲ͕ͨͪߟ͑Διϑτ΢ΣΞγεςϜͷʮΦϒβʔόϏϦςΟʯͱ͸ɺ γεςϜ͕ͲͷΑ͏ͳঢ়ଶʹͳͬͨͱͯ͠΋ɺͦΕ͕ͲΜͳʹ࢐৽Ͱحൈͳ΋ͷͰ͋ͬ ͯ΋ɺͲΕ͚ͩཧղ͠આ໌Ͱ͖Δ͔Λࣔ͢ई౓Ͱ͢ɻ ·ͨɺͦͷΑ͏ͳ࢐৽Ͱحൈͳঢ়ଶʹରͯ͠΋ɺࣄલʹσόοάͷඞཁੑΛఆٛͨ͠Γ ༧ଌͨ͠Γ͢Δ͜ͱͳ͘ɺγεςϜͷঢ়ଶσʔλͷ͋ΒΏΔσΟϝϯγϣϯ΍ͦΕΒͷ ૊Έ߹Θͤʹ͍ͭͯΞυϗοΫʹௐࠪ͠ɺΑΓσόοά͕ՄೳʹͳΓΑ͏ʹ͢Δඞཁ͕ ͋Γ·͢ɻ ΋͠ɺ৽͍͠ίʔυΛσϓϩΠ͢Δඞཁ͕ͳ͘ɺͲΜͳ࢐৽Ͱحൈͳঢ়ଶͰ΋ཧղͰ͖ ΔͳΒɺΦϒβʔόϏϦςΟ͕͋Δͱݴ͑·͢ɻ by ΦϒβʔόϏϦςΟɾΤϯδχΞϦϯά

Slide 25

Slide 25 text

ソフトウェアシステムにおけるo 11 y ؆୯ʹ͍͏ͱɺࢲ͕ͨͪߟ͑Διϑτ΢ΣΞγεςϜͷʮΦϒβʔόϏϦςΟʯͱ͸ɺ γεςϜ͕ͲͷΑ͏ͳঢ়ଶʹͳͬͨͱͯ͠΋ɺͦΕ͕ͲΜͳʹ࢐৽Ͱحൈͳ΋ͷͰ͋ͬ ͯ΋ɺͲΕ͚ͩཧղ͠આ໌Ͱ͖Δ͔Λࣔ͢ई౓Ͱ͢ɻ ·ͨɺͦͷΑ͏ͳ࢐৽Ͱحൈͳঢ়ଶʹରͯ͠΋ɺࣄલʹσόοάͷඞཁੑΛఆٛͨ͠Γ ༧ଌͨ͠Γ͢Δ͜ͱͳ͘ɺγεςϜͷঢ়ଶσʔλͷ͋ΒΏΔσΟϝϯγϣϯ΍ͦΕΒͷ ૊Έ߹Θͤʹ͍ͭͯΞυϗοΫʹௐࠪ͠ɺΑΓσόοά͕ՄೳʹͳΓΑ͏ʹ͢Δඞཁ͕ ͋Γ·͢ɻ ΋͠ɺ৽͍͠ίʔυΛσϓϩΠ͢Δඞཁ͕ͳ͘ɺͲΜͳ࢐৽Ͱحൈͳঢ়ଶͰ΋ཧղͰ͖ ΔͳΒɺΦϒβʔόϏϦςΟ͕͋Δͱݴ͑·͢ɻ by ΦϒβʔόϏϦςΟɾΤϯδχΞϦϯά

Slide 26

Slide 26 text

ソフトウェアシステムにおけるo 11 y • 制御理論の尺度として使われていたオブザーバビリティをソフトウェア領域 に拡張 • ソフトウェアにオブザーバビリティを持たせるための要件 by オブザーバビリティ ・ エンジニアリング 予想できないことが起こったとしても、どのような状態に陥っているか理解する アプリケーションの内部構造を理解する 外部ツールを使って観測 ・ 調査し、内部構造を理解する コードを改修することなく、内部情報を理解する

Slide 27

Slide 27 text

Monitoring と Observability の違い ϞχλϦϯάʢ؂ࢹʣ ΦϒβʔόϏϦςΟʢՄ؍ଌੑʣ ௐࠪ γεςϜͷঢ়ଶΛط஌ͷᮢ஋ͱর߹ ໰୊͕Ͳ͜Ͱͳͥൃੜ͍ͯ͠Δ͔൓෮ ୳ࠪతͳௐ͕ࠪՄೳ Ξϓϩʔν ϦΞΫςΟϒͳΞϓϩʔν ط஌ɾະ஌ʹؔΘΒͣɺੵۃతͳΞϓ ϩʔν ࠷ߴͷσόοΨʔ ૊৫ʹ௕͍͘Δऀ ࠷΋޷ح৺͕ߴ͍ΤϯδχΞ ӅΕͨ໰୊΍ൃݟʹରͯ͠ ຊ౰ͷ໰୊͕ݟ͑ʹ͍͘ɺঢ়ଶͷ؇ ࿨Λ·ͣͯ͠͠·͏ ਖ਼͍͠౴͑Λಋ͖ग़ͨ͢ΊʹσʔλΛ ௥͏ πʔϧؒ΍ඃٙՕॴؒͷ૬ؔ ݻ༗ͷඇޓ׵΍ໃ६͕ൃੜ͠ɺ૬ؔ Λಋ͖ग़͢ͷ͜ͱʹۤ࿑ ໌֬ͳσʔλʹͳ͓ͬͯΓɺͲͷΤϯ δχΞͰ΋୳ࡧՄೳ ো֐ྖҬͷ༧૝ ߥ͍ϨϕϧͷϝτϦΫεͱͻΒΊ͖ Λ૊Έ߹ΘͤΔ ෼ࢄτϨʔε΍ෳ਺ͷγάφϧΛ૊Έ ߹Θͤͯ၆ᛌ 【書評】オブザーバビリティ ・ エンジニアリング | DevelopersIO

Slide 28

Slide 28 text

Monitoring と Observability の違い ϞχλϦϯάʢ؂ࢹʣ ΦϒβʔόϏϦςΟʢՄ؍ଌੑʣ ௐࠪ γεςϜͷঢ়ଶΛط஌ͷᮢ஋ͱর߹ ໰୊͕Ͳ͜Ͱͳͥൃੜ͍ͯ͠Δ͔൓෮ ୳ࠪతͳௐ͕ࠪՄೳ Ξϓϩʔν ϦΞΫςΟϒͳΞϓϩʔν ط஌ɾະ஌ʹؔΘΒͣɺੵۃతͳΞϓ ϩʔν ࠷ߴͷσόοΨʔ ૊৫ʹ௕͍͘Δऀ ࠷΋޷ح৺͕ߴ͍ΤϯδχΞ ӅΕͨ໰୊΍ൃݟʹରͯ͠ ຊ౰ͷ໰୊͕ݟ͑ʹ͍͘ɺঢ়ଶͷ؇ ࿨Λ·ͣͯ͠͠·͏ ਖ਼͍͠౴͑Λಋ͖ग़ͨ͢ΊʹσʔλΛ ௥͏ πʔϧؒ΍ඃٙՕॴؒͷ૬ؔ ݻ༗ͷඇޓ׵΍ໃ६͕ൃੜ͠ɺ૬ؔ Λಋ͖ग़͢ͷ͜ͱʹۤ࿑ ໌֬ͳσʔλʹͳ͓ͬͯΓɺͲͷΤϯ δχΞͰ΋୳ࡧՄೳ ো֐ྖҬͷ༧૝ ߥ͍ϨϕϧͷϝτϦΫεͱͻΒΊ͖ Λ૊Έ߹ΘͤΔ ෼ࢄτϨʔε΍ෳ਺ͷγάφϧΛ૊Έ ߹Θͤͯ၆ᛌ 【書評】オブザーバビリティ ・ エンジニアリング | DevelopersIO γεςϜͷෳࡶ͞΍ن໛͕૿ͯ͠ɺϞχλϦϯάͷݶք͕ݱΕ࢝Ίͨ ΦϒβʔόϏϦςΟͷ஀ੜ👶

Slide 29

Slide 29 text

Observability の基礎

Slide 30

Slide 30 text

Primary Signals • オブザーバビリティの主要なシグナル(not 三本柱) UBHPCTFSWBCJMJUZXIJUFQBQFSNEBUNBJOuDODGUBHPCTFSWBCJMJUZ

Slide 31

Slide 31 text

Primary Signals • オブザーバビリティの主要なシグナル(not 三本柱) UBHPCTFSWBCJMJUZXIJUFQBQFSNEBUNBJOuDODGUBHPCTFSWBCJMJUZ

Slide 32

Slide 32 text

Primary Signals • オブザーバビリティの主要なシグナル(not 三本柱) There is a really good chance that you have heard about the "Three Observability Pillars", which are metrics, logs, and traces. They are commonly mentioned and probably what you're going to start with. We like to think of them as the "primary signals" instead of "three pillars" for two reasons: Pillars carry an implicit meaning of being foundational. They are a safe place to start, yet are not always required at the same time. In fact, basing on one or two signals with a small mix of others can be a valid trade-off to improve cost efficiency (e.g. metrics and logs with ad-hoc tracing). Recently, more signals are becoming popular in open-source communities like application profiles (continuous profiling) and crash dumps. New signals with new semantics may also arise in the near future, and those interested in this topic should keep an eye open for them. by cndf/tag-Observability

Slide 33

Slide 33 text

Primary Signals • オブザーバビリティの主要なシグナル(not 三本柱) ϝτϦΫεɺϩάɺτϨʔεͰ͋ΔʮՄ؍ଌੑͷ3ͭͷபʯʹ͍ͭͯฉ͍ͨ͜ͱ͕͋ΔՄೳੑ͸ඇৗʹߴ͍Ͱ͢ɻ͜ ΕΒ͸Ұൠతʹݴٴ͞Ε͓ͯΓɺ͓ͦΒ͘࠷ॳʹ࢝ΊΔ΋ͷͰ͢ɻࢲͨͪ͸͜ΕΒΛʮ3ͭͷபʯͰ͸ͳ͘ʮओཁͳ γάφϧʯͱߟ͍͑ͨͱߟ͍͑ͯ·͢ɻͦͷཧ༝͸࣍ͷ2ͭͰ͢ɻ பʹ͸ɺجૅͱͳΔͱ͍͏҉໧ͷҙຯ͕ࠐΊΒΕ͍ͯ·͢ɻ͜ΕΒ͸҆શʹ࢝ΊΔ͜ͱ͕Ͱ͖·͕͢ɺඞͣ͠΋ಉ࣌ ʹඞཁʹͳΔΘ͚Ͱ͸͋Γ·ͤΜɻ࣮ࡍɺ1ͭ·ͨ͸2ͭͷ৴߸ʹج͍ͮͯଞͷ৴߸Λগࠞͥ͠Δ͜ͱ͸ɺίετޮ ཰Λ޲্ͤ͞ΔͨΊͷ༗ޮͳτϨʔυΦϑͱͳΔՄೳੑ͕͋Γ·͢ (ΞυϗοΫ τϨʔεʹΑΔϝτϦΫεͱϩά ͳͲ)ɻ࠷ۙɺΦʔϓϯιʔε ίϛϡχςΟͰ͸ɺΞϓϦέʔγϣϯ ϓϩϑΝΠϧ (ܧଓతϓϩϑΝΠϦϯά) ΍ ΫϥογϡμϯϓͳͲͷγάφϧ͕ਓؾΛूΊ͍ͯ·͢ɻ͍ۙকདྷɺ৽͍͠ηϚϯςΟΫεΛඋ͑ͨ৽͍͠γάφϧ ΋ग़ݱ͢ΔՄೳੑ͕͋ΔͨΊɺ͜ͷτϐοΫʹڵຯ͕͋Δਓ͸ɺৗʹ໨ΛޫΒ͓ͤͯ͘ඞཁ͕͋Γ·͢ɻ by cndf/tag-Observability

Slide 34

Slide 34 text

Primary Signals • オブザーバビリティの主要なシグナル(not 三本柱) ϝτϦΫεɺϩάɺτϨʔεͰ͋ΔʮՄ؍ଌੑͷ3ͭͷபʯʹ͍ͭͯฉ͍ͨ͜ͱ͕͋ΔՄೳੑ͸ඇৗʹߴ͍Ͱ͢ɻ͜ ΕΒ͸Ұൠతʹݴٴ͞Ε͓ͯΓɺ͓ͦΒ͘࠷ॳʹ࢝ΊΔ΋ͷͰ͢ɻࢲͨͪ͸͜ΕΒΛʮ3ͭͷபʯͰ͸ͳ͘ʮओཁͳ γάφϧʯͱߟ͍͑ͨͱߟ͍͑ͯ·͢ɻͦͷཧ༝͸࣍ͷ2ͭͰ͢ɻ பʹ͸ɺجૅͱͳΔͱ͍͏҉໧ͷҙຯ͕ࠐΊΒΕ͍ͯ·͢ɻ͜ΕΒ͸҆શʹ࢝ΊΔ͜ͱ͕Ͱ͖·͕͢ɺඞͣ͠΋ಉ࣌ ʹඞཁʹͳΔΘ͚Ͱ͸͋Γ·ͤΜɻ࣮ࡍɺ1ͭ·ͨ͸2ͭͷ৴߸ʹج͍ͮͯଞͷ৴߸Λগࠞͥ͠Δ͜ͱ͸ɺίετޮ ཰Λ޲্ͤ͞ΔͨΊͷ༗ޮͳτϨʔυΦϑͱͳΔՄೳੑ͕͋Γ·͢ (ΞυϗοΫ τϨʔεʹΑΔϝτϦΫεͱϩά ͳͲ)ɻ࠷ۙɺΦʔϓϯιʔε ίϛϡχςΟͰ͸ɺΞϓϦέʔγϣϯ ϓϩϑΝΠϧ (ܧଓతϓϩϑΝΠϦϯά) ΍ ΫϥογϡμϯϓͳͲͷγάφϧ͕ਓؾΛूΊ͍ͯ·͢ɻ͍ۙকདྷɺ৽͍͠ηϚϯςΟΫεΛඋ͑ͨ৽͍͠γάφϧ ΋ग़ݱ͢ΔՄೳੑ͕͋ΔͨΊɺ͜ͷτϐοΫʹڵຯ͕͋Δਓ͸ɺৗʹ໨ΛޫΒ͓ͤͯ͘ඞཁ͕͋Γ·͢ɻ by cndf/tag-Observability

Slide 35

Slide 35 text

Primary Signals • オブザーバビリティの主要なシグナル(not 三本柱) UBHPCTFSWBCJMJUZXIJUFQBQFSNEBUNBJOuDODGUBHPCTFSWBCJMJUZ メトリクス ログ トレース

Slide 36

Slide 36 text

メトリクス • システムの健康状態 • システムの状態を常に記録する CPU、メモリ、I/O、レスポンスタイム、エラーレート etc … • 「既知の未知のもの」に対する最も効率的なシグナル 例   CPU使 用 率が徐々に増加している   レスポンスタイムがとあるデプロイから悪化している • トラフィックが増加してもボリュームが増えるようなものではないので、コ ストが予測可能

Slide 37

Slide 37 text

ログ • あらゆる操作などを保存するテキストデータ • 全てのイベントを記録しておくと、根本的な分析をする際や障害時にアプリケーションの状態を 理解できる • 種類 アプリケーションログ アプリケーション内で発 生 したイベント セキュリティーログ ログインの失敗、パスワード変更、認証の失敗、リソースアクセス、リソースの変更など システムログ 物理デバイスと論理デバイスと扱うカーネルレベルのメッセージなど 監査ログ ユーザーのアクティビティの記録、システムの応答 インフラストラクチャログ API、syslog やオンプレ ・ クラウドなどのインフラ管理部分

Slide 38

Slide 38 text

ログレベル %( τϥϒϧγϡʔςΟϯά΍ݪҼͷ෼ੳʹ࢖༻ ύϑΥʔϚϯεʹӨڹ͢Δ৔߹͕͋ΔͨΊɺτϥϒϧγϡʔςΟϯά΍ো֐ൃੜ࣌ͳ୹ظؒͷ ࢖༻Λ৺͕͚Δ -PHHFSͷϩάϨϕϧΛௐ੔ͯ͠%FW؀ڥͷΈදࣔ͞ΕΔΑ͏ʹ͢ΔͳͲ */'0 γεςϜ͕ͲͷΑ͏ʹಈ࡞͍ͯ͠Δ͔Λ೺Ѳ͢ΔͨΊʹ࢖༻ ෳࡶͰ࣌ؒΛཁ͢Δॲཧʹ͸εςοϓຖʹೖΕͨΓ΋͢Δ 8"3/*/( ࣦഊͰ͸ͳ͍͕஫ҙΛཁ͢ΔߴϨϕϧͷϝοηʔδʹ࢖༻ &3303 ো֐͕ൃੜͨ͠ཧ༝ͱৄࡉΛ֬ೝ͢ΔͨΊʹ࢖༻ ελοΫτϨʔεΛؚΊΔ͜ͱͰΑΓৄࡉͳௐ͕ࠪՄೳ ௐࠪʹ༗༻ͳ৘ใ͸Ͱ͖Δؚ͚ͩΊ͍ͯΔͷ͕ྑ͍

Slide 39

Slide 39 text

構造化イベント(ログ) • システムがどのような状態であっても、データを任意かつ詳細に繰り返し分 析できなくてはならない by cndf/tag-Observability • イベントとは つまりとあるリクエストが来てレスポンスを返すまでの間に内部に発 生 した全ての記録 • 任意かつ詳細に繰り返し分析 ユニークなリクエストIDや変数の値、callしたサービスなどを簡単に検索できるようにする αʔϏε΁ͷӨڹΛཧղ͢ΔͨΊʹɺ͋ΔಛఆͷϦΫΤετ͕ͦͷαʔϏεͱ΍ΓऔΓ Λ͍ͯ͠Δؒʹൃੜͨ͠શͯͷه࿥ͷ͜ͱ by ΦϒβʔόϏϦςΟɾΤϯδχΞϦϯά

Slide 40

Slide 40 text

time=2024-05-13T16:36:31.168Z level=INFO msg=dbQuery service=handson env=dev trace.id=hoge span.id=foo dd.git.commit.sha=sha dd.git.repository=github.com/ example/handson 構造化イベント(ログ) { "time": "2024-05- 13T16:39:39.585656469Z", "level": "INFO", "msg": "dbQuery", "service": "handson", "env": “dev", "trace.id": "hoge", "span.id": "foo", "dd.git.commit.sha": "sha", "dd.git.repository": "github.com/example/handson" }

Slide 41

Slide 41 text

time=2024-05-13T16:36:31.168Z level=INFO msg=dbQuery service=handson env=dev trace.id=hoge span.id=foo dd.git.commit.sha=sha dd.git.repository=github.com/ example/handson 構造化イベント(ログ) { "time": "2024-05- 13T16:39:39.585656469Z", "level": "INFO", "msg": "dbQuery", "service": "handson", "env": “dev", "trace.id": "hoge", "span.id": "foo", "dd.git.commit.sha": "sha", "dd.git.repository": "github.com/example/handson" } ,FZWBMVFʹ͢Δͱ,FZͰݕࡧͳͲͰ͖Δ

Slide 42

Slide 42 text

トレース(今 日 のメイン) • エンドユーザーによって開始されたリクエストとその結果アクセスされたダ ウンストリーム、マイクロサービス、データストアへのリクエストなど、分 散トランザクション中になのが起こったかを理解するためのシグナル +BFHFSEPDVNFOUBUJPO

Slide 43

Slide 43 text

Trace と Span 0QFO5FMFNFUSZ5SBDF

Slide 44

Slide 44 text

Trace と Span • Trace Span によって盲 目 的に定義される 全ての Span の 大 元、集約 • Span トランザクション内の操作 次の状態をカプセル化 操作名 開始と終了のタイムスタンプ 属性値 親 span の識別 子 Span参照に必要な SpanContext 0QFO5FMFNFUSZ5SBDF

Slide 45

Slide 45 text

{ "name": "hello", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x051581bf3cb55c13" }, "parent_id": null, "start_time": "2022-04-29T18:52:58.114201Z", "end_time": "2022-04-29T18:52:58.114687Z", "attributes": { "http.route": "some_route1" }, "events": [ { "name": "Guten Tag!", "timestamp": "2022-04-29T18:52:58.114561Z", "attributes": { "event_attributes": 1 } } ] } SpanContext { "name": "hello-greetings", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x5fb397be34d26b51" }, "parent_id": "0x051581bf3cb55c13", "start_time": "2022-04-29T18:52:58.114304Z", "end_time": "2022-04-29T22:52:58.114561Z", "attributes": { "http.route": "some_route2" }, "events": [ { "name": "hey there!", "timestamp": "2022-04-29T18:52:58.114561Z", "attributes": { "event_attributes": 1 } }, { "name": "bye now!", "timestamp": "2022-04-29T18:52:58.114585Z", "attributes": { "event_attributes": 1 } } ] } ਌TQBO ࢠTQBO

Slide 46

Slide 46 text

{ "name": "hello", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x051581bf3cb55c13" }, "parent_id": null, "start_time": "2022-04-29T18:52:58.114201Z", "end_time": "2022-04-29T18:52:58.114687Z", "attributes": { "http.route": "some_route1" }, "events": [ { "name": "Guten Tag!", "timestamp": "2022-04-29T18:52:58.114561Z", "attributes": { "event_attributes": 1 } } ] } SpanContext { "name": "hello-greetings", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x5fb397be34d26b51" }, "parent_id": "0x051581bf3cb55c13", "start_time": "2022-04-29T18:52:58.114304Z", "end_time": "2022-04-29T22:52:58.114561Z", "attributes": { "http.route": "some_route2" }, "events": [ { "name": "hey there!", "timestamp": "2022-04-29T18:52:58.114561Z", "attributes": { "event_attributes": 1 } }, { "name": "bye now!", "timestamp": "2022-04-29T18:52:58.114585Z", "attributes": { "event_attributes": 1 } } ] } ਌TQBO ࢠTQBO

Slide 47

Slide 47 text

Observability が必要な ユースケース

Slide 48

Slide 48 text

実際に必要 ・ 役 立 ったケース

Slide 49

Slide 49 text

アプリ運 用 カンパニー • 協業で開発しているサービス内部で別ベン ダーのAPIを複数叩く必要があった • 非 機能要件で「 自 分達で管理している部分 でのRPS」があったため、外部APIを除い た時間を計測する必要がった 0OMZ$"4FSWJDF $"4FSWJDF &YUFSOBM4FSWJDF"&YUFSOBM4FSWJDF#

Slide 50

Slide 50 text

ABEMA(FIFA W杯カタール 2022) • マイクロサービスをまたぐ負荷試験をしていたが、分散トレースを 入 れてい なかったためボトルネックを調査するには、各サービスのメトリクスやプロ ファイラ情報、実際のソースコードから仮説を 立 てながら調査する必要が あったのを改善 ʮ$"HPʙ"#&."ͷ(PΛ׆༻ͨ͠'*'"ϫʔϧυΧοϓੜதܧͷ෣୆ཪʙʯΛ։࠵͠·ͨ͠ʂ$"HPc$ZCFS"HFOU%FWFMPQFST#MPH

Slide 51

Slide 51 text

計装 Instrument

Slide 52

Slide 52 text

計装 Instrument • Trace や Span などのシグナルを送るためにロジックを実装すること 手 動計装 コードの変更が必要 OTel Registory のように、既存のライ ブラリを wrap することで計装してく れるものなどもある 自 動計装 既存のアプリケーションコードを変更 することなく計装 eBPF(後述)などを使 用 DataDog/orchestrion などの静的解析 を使 用 して 自 動計装するなどもある %BUB%PHPSDIFTUSJPO"UPPMGPS BEEJOHJOTUSVNFOUBUJPOUP(PDPE 3FHJTUSZc0QFO5FMFNFUSZ

Slide 53

Slide 53 text

Observability を 支 える技術

Slide 54

Slide 54 text

Observability を 支 える技術 • OpenTelemetry(後で詳細に説明) ベンダーやツールに依存せず、テメトリーシグナルを作成および管理するために設計されたo 1 1 y フレームワークおよびツールキット • eBPF Extend Berkeley Packet Filter Linux カーネルのソースコード変更やモジュールを追加することなく Linux カーネル内部でプログラム を実 行 できる技術 アプリケーション側のコードを変更することなくo 11 yを計装できるとして注 目 • Datadog, Grafana, Jaeger, Prometheus etc … 03FJMMZ+BQBOೖ໳F#1'

Slide 55

Slide 55 text

OpenTelemetry 入門

Slide 56

Slide 56 text

OpenTelemetry Protocol(OTLP) • Protocol Bu ff ers で定義され gRPC を使 用 する、テレメトリデータのプロト コル • ベンダーに依存せずに各サービス間でテレメトリーデータをやり取りする際 に使 用 可能

Slide 57

Slide 57 text

OpenTelemetry Collector • テレメトリーデータ(Signal)を集約する gateway 的な役割をする • 4つの component から構成 0QFO5FMFNFUSZΛཧղ͢ΔୈճίΞͷίϯϙʔωϯτc/FX3FMJD

Slide 58

Slide 58 text

OpenTelemetry Collector $PMMFDUPSc0QFO5FMFNFUSZ Reviver Processor Exporter Connector

Slide 59

Slide 59 text

Receiver • 送信されてきた signal を受け取り変換する • OTLP はもちろん Prometheus 形式( 一 部サポート外)にも対応 0QFO5FMFNFUSZͱ1SPNFUIFVT!"P5P ,FOUP,JNVSB 2JJUB

Slide 60

Slide 60 text

Processor • フィルタリングやサンプリング、エンリッチングなど Exporter に送信する前 に処理を 行 いデータを変換する • Datadog でも Datadog Agent 内部や Datadog に取り込んだ後の Grock パーサを 用 いた Log Pipeline でも 同じようなことができる 0QFO5FMFNFUSZͱ1SPNFUIFVT!"P5P ,FOUP,JNVSB 2JJUB

Slide 61

Slide 61 text

Exporter • 1つ以上のバックエンドのオブザーバビリティプラットフォームにデータを プッシュ ・ プル形式で送信 0QFO5FMFNFUSZͱ1SPNFUIFVT!"P5P ,FOUP,JNVSB 2JJUB

Slide 62

Slide 62 text

Connector • 二 つのテレメトリーパイプラインを繋ぐ • それぞれの Exporter、Receiver としての機能を持つ • Connector で扱うデータの型は同 一 のままでも変換することも可能 • データ型の変換を 行 う場合はこの component を使 用 する(Procssor で 行 う のは 非 推奨) 0QFO5FMFNFUSZͱ1SPNFUIFVT!"P5P ,FOUP,JNVSB 2JJUB

Slide 63

Slide 63 text

OpenTelemetryを 手 動計装してみる

Slide 64

Slide 64 text

OpenTelemetry x Golang • OpenTelemetry による計装例です • ハンズオンの時に実際に計装してもらいます

Slide 65

Slide 65 text

Tracer

Slide 66

Slide 66 text

import ( ... "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ otlptracehttp" "go.opentelemetry.io/otel/trace" ... ) var ( tracer trace.Tracer collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ) func Init(ctx context.Context) (exporter, error) { httpExporter, _ := otlptracehttp.New( ctx, otlptracehttp.WithEndpoint(collectorURL), otlptracehttp.WithInsecure(), ) exporter := httpExporter r, _ := newResouce() tp := sdktrace.NewTracerProvider( sdktrace.WithResource(r), sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) tracer = tp.Tracer(version.Get().ServiceName) otel.SetTracerProvider(tp) return exporter, nil } Tracer の作成 • サービスで 一 つ tracer を初期化

Slide 67

Slide 67 text

import ( ... "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ otlptracehttp" "go.opentelemetry.io/otel/trace" ... ) var ( tracer trace.Tracer collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ) func Init(ctx context.Context) (exporter, error) { httpExporter, _ := otlptracehttp.New( ctx, otlptracehttp.WithEndpoint(collectorURL), otlptracehttp.WithInsecure(), ) exporter := httpExporter r, _ := newResouce() tp := sdktrace.NewTracerProvider( sdktrace.WithResource(r), sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) tracer = tp.Tracer(version.Get().ServiceName) otel.SetTracerProvider(tp) return exporter, nil } Tracer の作成 • Exporter を作成 今回のハンズオンでは、sidecar してい る Datadog Agent に対して送信)

Slide 68

Slide 68 text

import ( ... "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ otlptracehttp" "go.opentelemetry.io/otel/trace" ... ) var ( tracer trace.Tracer collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ) func Init(ctx context.Context) (exporter, error) { httpExporter, _ := otlptracehttp.New( ctx, otlptracehttp.WithEndpoint(collectorURL), otlptracehttp.WithInsecure(), ) exporter := httpExporter r, _ := newResouce() tp := sdktrace.NewTracerProvider( sdktrace.WithResource(r), sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) tracer = tp.Tracer(version.Get().ServiceName) otel.SetTracerProvider(tp) return exporter, nil } Tracer の作成 • Tracer Provider を作成 OTLP を吐き出す Provider を作成

Slide 69

Slide 69 text

import ( ... "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ otlptracehttp" "go.opentelemetry.io/otel/trace" ... ) var ( tracer trace.Tracer collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ) func Init(ctx context.Context) (exporter, error) { httpExporter, _ := otlptracehttp.New( ctx, otlptracehttp.WithEndpoint(collectorURL), otlptracehttp.WithInsecure(), ) exporter := httpExporter r, _ := newResouce() tp := sdktrace.NewTracerProvider( sdktrace.WithResource(r), sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) tracer = tp.Tracer(version.Get().ServiceName) otel.SetTracerProvider(tp) return exporter, nil } Tracer の作成 • Tracer を作成

Slide 70

Slide 70 text

Span

Slide 71

Slide 71 text

import ( ... "go.opentelemetry.io/otel/trace" ... ) var ( tracer trace.Tracer ) func StartSpan(ctx context.Context, name string) (consaistext.Context, trace.Span) { return tracer.Start(ctx, name) } Span の実装 • Span の開始 Span を 入 れたい部分にこれを差し込む

Slide 72

Slide 72 text

func exampleFunction(ctx context.Context) { // NOTE: OpenTelemetry span ctx, span := tracer.StartSpan(ctx, "exampleFunction") defer span.End() ... } Span の実装 • Span の計装 Span を 入 れたい部分にこれを差し込む

Slide 73

Slide 73 text

Span をあらゆる場所に 計装していくの 大 変すぎでは?

Slide 74

Slide 74 text

Registry • 公式やサードパーティが 色 々なライ ブラリをラップして計装してくれて いるものを探せる • ここにあった場合、いつも使ってい るものをこれに置き換えると勝 手 に いい感じに計装してくれる

Slide 75

Slide 75 text

Registry • 公式が対応しているPackage ginとかechoとかnet/httpとかある PQFOUFMFNFUSZHPDPOUSJCJOTUSVNFOUBUJPO3&"%.&NEBUNBJOuPQFOUFMFNFUSZPQFOUFMFNFUSZHPDPOUSJC

Slide 76

Slide 76 text

import ( ... "net/http" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ... ) ... httpClient := &http.Client{ // NOTE: OpenTelemetry instrumentation Transport: otelhttp.NewTransport(http.DefaultTransport), } req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpbin.org/get", nil) resp, _ := h.httpClient.Do(req) defer resp.Body.Close() ... Registry • 例:net/http Span を 入 れたい部分にこれを差し込む Transporter に OTel のライブラリを噛ませる

Slide 77

Slide 77 text

import ( ... "net/http" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ... ) ... httpClient := &http.Client{ // NOTE: OpenTelemetry instrumentation Transport: otelhttp.NewTransport(http.DefaultTransport), } req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpbin.org/get", nil) resp, _ := h.httpClient.Do(req) defer resp.Body.Close() ... Registry • 例:net/http Span を 入 れたい部分にこれを差し込む Transporter に OTel のライブラリを噛ませる

Slide 78

Slide 78 text

番外編 Datadog • 実は Datadog にも同じようなものがある Datadog の 方 が多く対応していそう OSSなのでコントリビュートもできる %BUBEPH(Pޓ׵ੑཁ݅

Slide 79

Slide 79 text

Datadog 入門

Slide 80

Slide 80 text

Datadog • モニタリング SaaS の 一 つ • 社内だと 一 番使われてそう AI事業本部でもかなり採 用 されている印象 43&5FDIOPMPHZ.BQcגࣜձࣾαΠόʔΤʔδΣϯτ

Slide 81

Slide 81 text

Datadog • クラウドベンダーをはじめ、 大 体のサービ スの Integration に対応している なくても作れる • 高 度な Log 検索やグラフ描写ができる

Slide 82

Slide 82 text

Datadog • 詳しい使い 方 が知りたければ Datadog Learning Center がおすすめ 2週間ほど有効な Datadog アカウントが付与 (期限を過ぎても再度アクセスすればまた使える) instruqt というラーニングツールを 用 いて実際に vm を 立 てて、そこに対して Agent を 入 れ たり、WEBアプリのログを吐かせたり、はたまた動いているWEBアプリに対してSynthetic Testingを実 行 したり、実際に RUM を 入 れて 見 たりと 一 通りの Datadog の機能を試すことが できる • Datadog の 一 通りの使い 方 はこれでわかる %BUBEPHೝఆࢿ֨ʢ%BUBEPH'VOEBNFOUBMTʣΛऔͬͯΈͨ

Slide 83

Slide 83 text

機能の軽い紹介

Slide 84

Slide 84 text

Log • CloudWatch Logs や Datadog Agent など経由で送った Log を 見 れる • Log を構造化すると、facets など key で絞り込めて便利

Slide 85

Slide 85 text

Log • スタックトレースなども出して おくといい感じに表 示 してくれ る

Slide 86

Slide 86 text

APM Trace • APM = Application Performance Monitoring • Traceを 見 れる これを後で作ってもらいます

Slide 87

Slide 87 text

APM Error Tracking • 同じエラーをまとめて集約してくれる 特定の Error を無視したりもできる Error Log をまとめてくれるので、全体の Error を俯瞰して 見 れる

Slide 88

Slide 88 text

APM Error Tracking • Error に error.message / error.king / error.stack を 入 れると認識 %BUBEPHόοΫΤϯυΤϥʔͷ௥੻

Slide 89

Slide 89 text

Dashboards • ログやメトリクス、トレースなどをグラフとして描画できる

Slide 90

Slide 90 text

Monitors • ログやメトリクスなどの閾値を元に slack などにアラートを 飛 ばせる • On-call などもトリガーできる

Slide 91

Slide 91 text

Monitors • 適切なアラートをしようという話 ࣗಈੜ੒Λ׆༻ͨ͠ɺӡ༻อकίετΛ཈͑Δ&SSPS"MFSU3VOCPPLͷҰݩू໿؅ཧ

Slide 92

Slide 92 text

その他 • Watchdog、Metrics Explorer、Synthetics Test、NPM etc … • その他にも機能がたくさん! ぜひ実践編の時に 色 々触ってみよう!

Slide 93

Slide 93 text

PipeCD 入門

Slide 94

Slide 94 text

PipeCD • ハンズオンで使うため少し紹介します

Slide 95

Slide 95 text

GitOps

Slide 96

Slide 96 text

GitOps とは GitOps is an operational framework that takes DevOps best practices used for application development such as version control, collaboration, compliance, and CI/CD, and applies them to infrastructure automation. by GitLab

Slide 97

Slide 97 text

GitOps とは • GitOps は Weaveworks 社が提唱 • インフラとアプリケーションの両 方 を含めたシステム全体のコードを Git を 使って管理する • Git を信頼できる唯 一 の情報源とする • Git をみると全ての情報が分かる(宣 言 的に管理されている) GitOps ͸ɺόʔδϣϯ؅ཧɺίϥϘϨʔγϣϯɺίϯϓϥΠΞϯεɺCI/CD ͳͲͷΞϓϦέʔγϣϯ։ൃʹ࢖༻ ͞ΕΔ DevOps ͷϕετ ϓϥΫςΟεΛऔΓೖΕɺΠϯϑϥετϥΫνϟͷࣗಈԽʹద༻͢Δӡ༻ϑϨʔϜϫʔ ΫͰ͢ɻ by GitLab

Slide 98

Slide 98 text

GitOps のメリット • 直接環境にアクセスしなくていい • 全ての構成変更は Git(PR)を通して 行 われる • 今 Git で確認できる構成 == 今動いている構成 • 全ての変更が追跡可能

Slide 99

Slide 99 text

PipeCD

Slide 100

Slide 100 text

PipeCD • GitOps スタイルの CD • K 8 s だけでなく ECS、Lambda、CloudRun、Terraform などを統 一 したUX で管理可能 • 社内の DP 室がメインでメンテナンスしているOSS QJQFDEQJQFDE5IF0OF$%GPS"MM\BQQMJDBUJPOT QMBUGPSNT PQFSBUJPOT^ $/$'4BOECPYʹ΋࠾୒

Slide 101

Slide 101 text

PipeCD Overview • Git repository の変更を PipeCD が検知 • Git の内容を元にインフラリソースを PipeCD が変更する 0WFSWJFXc1JQF$%

Slide 102

Slide 102 text

PipeCD Concept • Piped 展開するネットワークであるクラスター内で実 行 するバイナリコンポーネント ステートレスなので単 一 のVMやローカルマシン などでも実 行 できる • Control Plane デプロイデータを 一 元管理 社内では SaaS としても提供されている 自 分で 立 てることもできる

Slide 103

Slide 103 text

ハンズオン

Slide 104

Slide 104 text

OpenTelemetry を計装して Datadog に送ってみよう!

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

概要 • OpenTelemetry を実際に計装してみよう • OTLP をDatadog Agent を経由して Datadog APM Trace で 見 れるようにし てみよう • Log と Trace を紐づけてみよう • Monitors でアラートを設定してみよう • ダッシュボードを作ってみよう • (時間が余った 人 )PipeCD のカナリアリリースを試してみよう • (時間が余った 人 )OTel SDK から Datadog SDK に載せ替えてみよう

Slide 107

Slide 107 text

事前準備

Slide 108

Slide 108 text

AWS • 全員で 一 つのアカウントを使います この30のアカウントにログインできることを確かめてください • 特に local からアクセスすることはないので、PERMANに表 示 されてログイ ンで来ていれば 大 丈夫です 社内向け

Slide 109

Slide 109 text

Datadog • 全員で 一 つのアカウントを使います この24のアカウントにログインできることを確かめてください 社内向け

Slide 110

Slide 110 text

PipeCD Control Plane • Project Name: ai-handson- 24 https://pipecd.jp/login?project=ai-handson- 24 • 自 分のアプリケーションの登録などをしてもらいます • Github でログインしてください(Github の SSO を使 用 しています) 社内向け

Slide 111

Slide 111 text

API

Slide 112

Slide 112 text

func (h *Handler) handson(w http.ResponseWriter, r *http.Request) (int, error) { ctx := r.Context() // Custom Function h.sleep50microsec(ctx) // External API Call if err := h.restHttpBin(ctx, http.MethodGet, "get"); err != nil { return http.StatusInternalServerError, err } // External API Call concurrently errGroup, errCtx := errgroup.WithContext(ctx) errGroup.Go(func() error { return h.restHttpBin(errCtx, http.MethodGet, "get") }) errGroup.Go(func() error { return h.restHttpBin(errCtx, http.MethodPost, "post") }) errGroup.Go(func() error { return h.restHttpBin(errCtx, http.MethodPut, "put") }) if err := errGroup.Wait(); err != nil { return http.StatusInternalServerError, err } // Database Query if err := h.dbQuery(ctx); err != nil { return http.StatusInternalServerError, err } // Random Error if err := h.randomError(radomErrorRate); err != nil { return http.StatusInternalServerError, err } return http.StatusOK, nil } /handson • 一 つだけエンドポイントが 生 えてる

Slide 113

Slide 113 text

import http from 'k6/http'; import { check } from 'k6'; export const options = { scenarios: { contacts: { executor: 'constant-arrival-rate', rate: 5, timeUnit: '1s', duration: '2h', preAllocatedVUs: 50, } } } export default function () { let url ='http://localhost:8080/handson'; let response = http.get(url); check(response, { 'is status 200': (r) => r.status === 200, }); } k 6 • 5 rps でサイドカーコンテナが ずっとリクエストを送り続けている

Slide 114

Slide 114 text

ECS on Fargate に 自 分のサービスを Deploy する

Slide 115

Slide 115 text

Github から Repository を Clone • https://github.com/CyberAgentAI/otel-handson

Slide 116

Slide 116 text

. ├ ─ ─ backend │ ├ ─ ─ app │ ├ ─ ─ … │ └ ─ ─ … │ ├ ─ ─ backend-base │ ├ ─ ─ app │ ├ ─ ─ … │ └ ─ ─ … ├ ─ ─ compose.yaml ├ ─ ─ … ├ ─ ─ infrastructure │ ├ ─ ─ env │ └ ─ ─ modules ├ ─ ─ … └ ─ ─ … Repository • backend/ すでに計装されたAPI 分からなくなったらカンニングしてくだ さい • backend-base/ 計装されていないベースのAPI これをコピーして使う • infrastructure/ Terraform などが 入 っています 少し修正してもらいます

Slide 117

Slide 117 text

backend-base をコピーする • backend-base をコピーして backed-{sukina-name} に変える • コピーしたディレクトリ内の backend-base を全て backed-{sukina- name} に置換する • .env.example をコピーして .env を作成する BACKEND_NAME に backed-{sukina-name} をいれる • go.work に backed-{sukina-name} を追加

Slide 118

Slide 118 text

> make run/backend > curl localhost:8080/handson {"message":"Hello, handson-iwami"} backend-base をコピーする • 以下コマンドが実 行 できて動くことを確認する(docker必須) • Hello, handson-{sukina-name} が返ってきたら正解 • ここまでできたら PR を出して merge する

Slide 119

Slide 119 text

locals { prefixes = [ "backend", "backend-base" ## TODO: Add your ecr repository name ] } ECR を作成 • infrastructure/env/base-infra/main.tf の locals.pre fi xes に backed- {sukina-name} を追加(PR出す、コンフリクトするので仲良く) • ${pre fi x}-server-image:handson-api 用 • ${pre fi x}-loadtest-image:loadtest 用

Slide 120

Slide 120 text

ECR を作成 • main にマージされると、PipeCD Agent が 自 動で terraform apply

Slide 121

Slide 121 text

locals { prefixes = [ "backend", "backend-base" ## TODO: Add your ecr repository name ] } ECR に GitHub からアクセスできるようにする • infrastructure/env/github-infra/main.tf の locals.pre fi xes に backed- {sukina-name} を追加(PR出す、コンフリクトするので仲良く) • OIDC を使 用 して AWS 認証できるようにする

Slide 122

Slide 122 text

ECR に GitHub からアクセスできるようにする • PR の pipecd plan preview で terraform plan の結果も 見 れる

Slide 123

Slide 123 text

ECR に image を push する actions を作成 • .github/work fl ows/distribute-backend-base.yaml を copy して.github/ work fl ows/distribute-backend-{sukina-name}.yaml とかにする • TODO コメントがついている場所を backend-base → backend-{sukina- name} に変更する • できたら PR を出して merge する

Slide 124

Slide 124 text

ECR に image を push する actions を作成 • できたら work fl ow dispatch で起動してみる

Slide 125

Slide 125 text

ECR に image を push する actions を作成 • 成功してればOK

Slide 126

Slide 126 text

PipeCD 用 の ECS の con fi g を追加 • infrastructure/env/handson-api-base を copy して infrastructure/env/ handson-api-{sukina-name} にする • 以下ファイルの TODO 部分を base → {sukina-name} に変更する app.pipecd.yaml PipeCD がアプリケーションを認識するための con fi g servicedef.yaml、taskdef.yaml ECS の設定

Slide 127

Slide 127 text

apiVersion: pipecd.dev/v1beta1 kind: ECSApp spec: name: handson-api-base labels: env: dev layer: backend input: serviceDefinitionFile: servicedef.yaml taskDefinitionFile: taskdef.yaml description: | Service to providing hands-on REST APIs to end- users. eventWatcher: - matcher: name: handson-api-image-update-base labels: app: handson-api-base env: dev handler: type: GIT_UPDATE config: commitMessage: "[backend-base] Upgrade handson-api service to the latest version" replacements: - file: taskdef.yaml yamlField: $.containerDefinitions[0].image ... app.pipecd.yaml • spec.name • PipeCD 側で認識する component の名前 • spec.eventWatcher • 実はさっきの actions の最後に コントロー ルプレーンに対して event を送っている • Event Name が 一 致した際に con fi g の データを書き換える • ECR のイメージが更新されたら、 自 動 的に ECS Task で指定している image の version も変えてくれるようになる

Slide 128

Slide 128 text

PipeCD CP で application を登録 • app.pipecd.yaml を main に merge したので管理対象にいれる • Applications ので +ADD を選択 • Platform Provider で ecs-dev を選択、Application で先ほど name にいれ たものを選択する

Slide 129

Slide 129 text

PipeCD CP で application を登録 • しばらくすると ECS に Service を追加してくれる

Slide 130

Slide 130 text

ECS に追加されているか 見 に 行 く • ECS -> handson-app-cluster に Service が追加されていれば成功 社内向け

Slide 131

Slide 131 text

Datadog に Log を 見 に 行 く • Datadog にログインしてログを 見 に 行 く

Slide 132

Slide 132 text

Datadog に Log を 見 に 行 く • 構造化されていないログが出ているはず

Slide 133

Slide 133 text

ログを構造化してみよう

Slide 134

Slide 134 text

// Before logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: slogLevelFromString(flags.LogLevel), }), ) // After logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slogLevelFromString(flags.LogLevel), }), ) ログを構造化してみよう • backend-{sukina-name}/pkg/cli/ entrypoint.go の handler を JSON Handler に変更する • 再び make run/backend を実 行 し て、コンテナ内のログが json 形式 になっていることを確認する • PR を出して merge する • PipeCD によって勝 手 に新しくデプロイさ れるようになっている

Slide 135

Slide 135 text

再び Datadog の Log を 見 ると • 左側の Service にうまく 行 っていれば 自 分の Service が出ているはず

Slide 136

Slide 136 text

再び Datadog の Log を 見 ると • 自 分の Service の Log だけを絞り込めるようになっている

Slide 137

Slide 137 text

再び Datadog の Log を 見 ると • ログを 見 ると、key value で 入 れたものが event attribute として認識

Slide 138

Slide 138 text

// backend-{sukina-name}/app/api/handson/log.go func LogAttributes(ctx context.Context, attrs []any) []any { lattrs := []any{ slog.String("env", "dev"), slog.String("service", version.Get().ServiceName), slog.String("version", version.Get().Version), slog.String("git.commit.sha", version.Get().GitCommit), slog.String("git.repository", version.Get().GitRepository), // TODO: Add span and trace IDs to log attributes } return append(lattrs, attrs...) }

Slide 139

Slide 139 text

OpenTelemetry を計装してみよう

Slide 140

Slide 140 text

OpenTelemetry × Datadog • シグナルと OTEL Collector を経由するパターンと Datadog Agent に直接送 るパターンに2種類存在 • 今回は下の 「Datadog Agent を経由」を採 用 %BUBEPHͷ0QFO5FMFNFUSZ

Slide 141

Slide 141 text

logger.InfoContext(ctx, "Version", slog.String("version", v.String())) // NOTE: Init OpenTelemetry cleanup, err := tracer.Init(ctx) if err != nil { logger.ErrorContext( ctx, "Failed to initialize tracer", handson.ErrorLogAttributes(ctx, err)..., ) return err } defer cleanup.Shutdown(ctx) httpServer.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { Tracerの初期化 • backend-{sukina-name}/app/ api/cmd.go に tracer の初期化処 理を追加 • Init の中 身 は 「 7 . OpenTelemetry を 手 動計装してみる」を参照

Slide 142

Slide 142 text

func (h *Handler) sleep50microsec(ctx context.Context) { // TODO: Instrument OpenTelemetry span time.Sleep(50 * time.Microsecond) } Spanの計装 • backend-{sukina-name}/app/ api/handson/handson.go の function sleep 50 micrisec に span を計装してみよう! • できたらPRをマージしてみよう

Slide 143

Slide 143 text

仕組み • Datadog Agent の OTLP Receiver で待ち受ける • サイドカー経由でシグナルを送る • taskdef.yaml の環境変数を覗いてみてください %BUBEPHͷ0QFO5FMFNFUSZ

Slide 144

Slide 144 text

Datadog の APM • Service に 自 分のものが出てたら送れている

Slide 145

Slide 145 text

Datadog の APM • Traceに 入 っている

Slide 146

Slide 146 text

Datadog の APM • Spanをうまく計装できていれば handson.sleep 50 microsec などが表 示 さ れるはず

Slide 147

Slide 147 text

func (h *Handler) sleep50microsec(ctx context.Context) { // NOTE: OpenTelemetry span ctx, span := tracer.StartSpan(ctx, "handson.sleep50microsec") defer span.End() time.Sleep(50 * time.Microsecond) } Spanの計装 • こんな感じに 入 れていれば正解

Slide 148

Slide 148 text

OpenTelemetry Registory を使って 計装してみよう

Slide 149

Slide 149 text

net/http • handson api は net/http を 用 いて作られている • この API のトレースを取りたい

Slide 150

Slide 150 text

net/http • https://httpbin.org というモックサーバに net/http を使 用 してアクセスし ている

Slide 151

Slide 151 text

Registry で検索して計装してみよう • 言 語に Go、検索欄に net/http など 入 れて検索してみよう • backend-{sukina-name}/pkg/http/httpserver/server.go あたり

Slide 152

Slide 152 text

計装できているとこんな感じになる

Slide 153

Slide 153 text

net/http

Slide 154

Slide 154 text

// backend-{sukina-name}/pkg/http/httpserver/server.go func NewServer(port int, gracePeriod time.Duration, logger slog.Logger) *Server { s := &Server{ … } s.mux = http.NewServeMux() // s.server.Handler = s.mux s.server.Handler = otelhttp.NewHandler(s.mux, "httpServer") return s } net/http

Slide 155

Slide 155 text

// backend-{sukina-name}/pkg/http/httpserver/server.go func (s *Server) HandleFunc(…) { // s.mux.Handle(pattern, http.HandlerFunc(handler)) otelHandler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handler)) s.mux.Handle(pattern, otelHandler) } net/http

Slide 156

Slide 156 text

// backend-{sukina-name}/app/api/cmd.go httpClient := &http.Client{ // NOTE: OpenTelemetry instrumentation Transport: otelhttp.NewTransport(http.DefaultTransport), } net/http

Slide 157

Slide 157 text

database/sql • sqlite 3 に対してアクセスしている • これも同じように計装してみよう

Slide 158

Slide 158 text

計装できているとこんな感じになる

Slide 159

Slide 159 text

func Init(ctx context.Context, logger slog.Logger) (*sql.DB, error) { … // db, err := sql.Open("sqlite3", "file::memory:?cache=shared") db, err := otelsql.Open( "sqlite3", "file::memory:?cache=shared", otelsql.WithAttributes(semconv.DBSystemSqlite), ) if err != nil { return nil, cerror.Wrap(err, "failed to open sqlite") } … return db, nil } database/sql

Slide 160

Slide 160 text

ログとトレースを紐付けよう

Slide 161

Slide 161 text

func LogAttributes(ctx context.Context, attrs []any) []any { span := trace.SpanFromContext(ctx) lattrs := []any{ slog.String("env", "dev"), slog.String("service", version.Get().ServiceName), slog.String("version", version.Get().Version), slog.String("git.commit.sha", version.Get().GitCommit), slog.String("git.repository", version.Get().GitRepository), slog.String("trace.id", span.SpanContext().TraceID().String()), slog.String("span.id", span.SpanContext().SpanID().String()), slog.String("dd.service", version.Get().ServiceName), slog.String("dd.version", version.Get().Version), slog.String("dd.env", "dev"), slog.String("dd.git.commit.sha", version.Get().GitCommit), slog.String("dd.git.repository", version.Get().GitRepository), slog.String("dd.trace_id", span.SpanContext().TraceID().String()), slog.String("dd.span_id", span.SpanContext().SpanID().String()), } return append(lattrs, attrs...) } Span ID/Trace ID • ログに Span ID、Trace ID を仕込む • ログが発 生 した際にどこの Trace か がわかるようになる • backend-{sukina-name}/app/ api/handson/log.go に追加してみ よう • Context の中に trace 情報が 入 って いるので取り出して使う

Slide 162

Slide 162 text

Trace → Log • Trace の下に Log が出てる

Slide 163

Slide 163 text

Log → Trace • Log の画 面 で Trace がみれる

Slide 164

Slide 164 text

エラーのアラート 飛 ばしてみよう

Slide 165

Slide 165 text

const ( // radomErrorRate = 0.0 radomErrorRate = 0.5 ) func (h *Handler) randomError(rate float64) error { // TODO: Instrument OpenTelemetry span if rate == 0 { return nil } if rand.Float64() < rate { return cerror.New("random error") } return nil } randomError • backend-{sukina-name}/app/ api/handson/handson.go で 一 定 確率で Error が発 生 する function がある • この rate を 0.5 にしてエラーを発 生 させてみよう

Slide 166

Slide 166 text

APM • Error が出ているのが分かるはず

Slide 167

Slide 167 text

APM Error Tracking • Error が Issue になっているのが分かる

Slide 168

Slide 168 text

Datadog の Monitors • Error Tracking をベースに Monitor を作成してみよう • チャンネルは #ai_kenshu 2 4 _o 11 y_handson に 飛 ばしてみよう

Slide 169

Slide 169 text

以下時間が余った 人

Slide 170

Slide 170 text

PipeCD のカナリアリリース • ドキュメントを 見 ながら ECS にカナリアリリースを導 入 してみよう $PO fi HVSJOH&$4BQQMJDBUJPOc1JQF$%

Slide 171

Slide 171 text

Datadog の SDK 使 用 した計装 • OpenTelemetry の SDK から Datadog の SDK に計装し直してみよう • ベンダーロックインがどんなことか分かるが、同時に恩恵も分かるはず (Pޓ׵ੑཁ݅

Slide 172

Slide 172 text

まとめ • オブザーバビリティは飽くなき探究 心 … • コストとトレードオフしながら、システム完全理解になるようなオブザーバ ビリティをぜひ実践してみてください!

Slide 173

Slide 173 text

出典 ・ 参考資料 • 北海道旅 行・ 北海道ツアーなら格安旅 行 のJ-TRIP • 航空機の運動 方 程式 土 屋研究室 • オブザーバビリティ ・ エンジニアリング • 【書評】オブザーバビリティ ・ エンジニアリング | DevelopersIO • tag-observability/whitepaper.md at main · cncf/tag-observability • cndf/tag-Observability • Jaeger documentation • OpenTelemetry.Trace • 「CA.go ~ ABEMAのGoを活 用 したFIFA ワールドカップ 生 中継の舞台裏 ~ 」を開催しました! #CAgo | CyberAgent Developers Blog • Registry | OpenTelemetry • DataDog/orchestrion: A tool for adding instrumentation to Go cod • O'Reilly Japan - 入門 eBPF • OpenTelemetryを理解する 第2回: コアのコンポーネント | New Relic • Collector | OpenTelemetry • OpenTelemetry と Prometheus @AoTo 0 33 0 (Kento Kimura) - Qiita • opentelemetry-go-contrib/instrumentation/README.md at main · open-telemetry/opentelemetry-go-contrib • Datadog Go 互換性要件 • SRE Technology Map | 株式会社サイバーエージェント • Datadog 認定資格(Datadog Fundamentals)を取ってみた

Slide 174

Slide 174 text

出典 ・ 参考資料 • Datadog バックエンドエラーの追跡 • GitLab • Overview | PipeCD • OpenTelemetry 入門 - zenn • 仕様と実装から理解するOpenTelemetryの全体像 • OpenTelemetryのTraceをGoで試してみる - Carpe Diem • cilium/ebpf: ebpf-go is a pure-Go library to read, modify and load eBPF programs and attach them to various hooks in the Linux kernel. • APM の 用 語と概念 Datadog • OpenTelemetryとGrafanaでLogsとMetricsとTracesを接続する #grafana - Qiita • eBPF Introduction - Speaker Deck • The Datadog Learning Center