Slide 1

Slide 1 text

'3&4)-*7&ʹ͓͚Δ 4FSWFS4JEF,PUMJOͱ.JDSPTFSWJDFTͷࠓ ϩάϛʔ5FDI-JWF ໺ᖒ૱࢙!TPVTIJ@OP[BXB

Slide 2

Slide 2 text

BCPVUNF w αʔόαΠυΤϯδχΞ w ͍·͸"OESPJEΤϯδχΞ΍ͬͯ·͢! w ʙ'3&4)-*7&ॴଐLUྺ @soushi_nozawa @soushin blog.soushi.me

Slide 3

Slide 3 text

ੜ์ૹಈըϓϥοτϑΥʔϜ .JDSPTFSWJDFT"SDIJUFDUVSF'VMM%PDLFSOJ[FE"NB[PO8FC4FSWJDFT,VCFSOFUFT

Slide 4

Slide 4 text

ʮ΋ͣ͸ΌͪΌΜͶΔ'3&4)ʯ ήʔϜঁࢠ΋ͣ΂Τͱ࣮گ͕͑Δ͸Όʹ͑ʹΑΔ࣮ گϢχοτ IUUQTGSFTIMJWFUWNID IUUQTUXJUUFSDPNZPVTFJ@IBZBOJF ࢲͷਪ͠νϟϯωϧͰ͢"

Slide 5

Slide 5 text

"HFOEB  '3&4)-*7&ͱαʔόαΠυͱ,PUMJO  '3&4)-*7&ͷϚΠΫϩαʔϏεࣄྫ  4QSJOH#PPUͱ,PUMJO  ίϯςφͱϚΠΫϩαʔϏε  H31$ͱϚΠΫϩαʔϏε  ·ͱΊ

Slide 6

Slide 6 text

ʮ'3&4)-*7&ʹ͓͚ΔαʔόαΠυ,PUMJO Λத৺ͱͨ͠पลٕज़ͷίϯςφͱH31$ ʹ৮ΕϚΠΫϩαʔϏε։ൃͷ஌ݟΛڞ༗ʯ

Slide 7

Slide 7 text

'3&4)-*7&ͱαʔόαΠυͱ,PUMJO w ༷ʑͳϚΠΫϩαʔϏεʹ,PUMJOΛಋೖ w ܾࡁूܭɺΞφϦςΟΫεɺϓογϡ௨஌ɺϩάऩूɺ"1* ήʔτ΢ΣΠͳͲ w ࠷৽͸՝ۚج൫ͷϚΠΫϩαʔϏε w ༷ʑͳγεςϜཁ݅ʹॊೈʹରԠͰ͖͍ͯΔ

Slide 8

Slide 8 text

ϚΠΫϩαʔϏεͷར఺ w ୯Ұো֐఺Ͱཹ·Γશମ͸౗Εͳ͍ w ϚΠΫϩαʔϏε͸ೝূɺϩά ɺ՝ۚͳͲͷ୯ҐͰಈ͍͍ͯΔ w ՝͕ۚͰ͖ͳͯ͘΋ࢹௌ͸Ͱ͖Δ w εέʔϧઓཱུ͕ͯ΍͍͢ w αʔϏεઓུͱڞʹεέʔϧͤ͞ΔϚΠΫϩαʔϏε͕໌֬ʹͳΔ w ٕज़తνϟϯϨϯδͷෑډΛԼ͛Δ w ʮνϟϯϨϯδ͍ͨ͠ݴޠͰϚΠΫϩαʔϏεΛ࡞ͬͯΈΑ͏!ʯ w ʮখ͍͞αʔϏε͔ͩΒޙͰ࡞Γ௚ͦ͏ʯʢΤϯδχΞͷϞνϕʔγϣϯ⬆

Slide 9

Slide 9 text

ΞʔΩςΫνϟ w ̍ͭͷϚΠΫϩαʔϏεʹ̍ͭͷϨϙδτϦɺ̍ͭͷϦιʔεʢ%#ʣ͕ίϯηϓτ w ̍ͭͷϚΠΫϩαʔϏε͸%PDLFSpMFΛ΋ͪίϯςφԽ͢Δ w %PDLFSOJ[FE͞ΕͨϚΠΫϩαʔϏε͸,VCFSOFUFTͰΦʔέετϨʔγϣϯ

Slide 10

Slide 10 text

ݱࡏͷ'3&4)-*7& w (PMBOHͱ,PUMJOͷϋΠϒϦουߏ੒ w ഑৴γεςϜɺ௿ϨΠϠʔܥ͸(PMBOH w ৽نͷ"1*͸جຊ,PUMJOͰ w "1*Λ(PMBOHͰ͍ͬͺ͍ॻ͘ͷπϥΠʢ(Pے೑௧ʣ w +BWBࢿ࢈Λ׆༻͢Ε͹ૣ͘࡞ΕΔͱ͜Ζ΋͋Δ w ϚΠΫϩαʔϏε։ൃͷར఺Λ׆͔ͯ͠ݴޠʹറΒΕͣϑϥοτʹ

Slide 11

Slide 11 text

,PUMJOͷ޷͖ͳͱ͜Ζ w /VMMTBGFUZ w ߴ֊ؔ਺ w ֦ுؔ਺ w %BUBDMBTT w είʔϓؔ਺ 3FJpFEUZQFQBSBNFUFST ʜFUD w "OESPJEΤϯδχΞͱ஥ྑ͘ͳΕΔ w ,PUMJOΛѪͰͨ͘ͳΔͱ͜Ζ͕͍ͬͺ͍

Slide 12

Slide 12 text

4QSJOH#PPU w ओʹ࠾༻͍ͯ͠ΔϑϨʔϜϫʔΫ w WY3&-&"4&Λಋೖ w ,PUMJOTVQQPSU w ίʔυΛγϯϓϧʹݟ௨͠Α͘*EJPNBUJD ,PUMJO$PEFΛॻ͘͜ͱΛ৺͕͚Δ IUUQTEPDTTQSJOHJPTQSJOHEPDTDVSSFOUTQSJOH GSBNFXPSLSFGFSFODFLPUMJOIUNM

Slide 13

Slide 13 text

*EJPNBUJD,PUMJO$PEF 1 val context = GenericApplicationContext().apply { 2 registerBean() 3 registerBean { Bar(it.getBean()) } 4 } w είʔϓؔ਺΍֦ுؔ਺Λ࢖ͬͯ,PUMJOΒ͍͠ίʔυΛॻ͘ w SFHJTUFS#FBO HFU#FBO͸֦ுؔ਺ 1 GenericApplicationContext context=new GenericApplicationContext(); 2 context.registerBean(Foo.class); 3 context.registerBean(Bar.class,()->new 4 Bar(context.getBean(Foo.class)) 5 ); w +BWB w ,PUMJO

Slide 14

Slide 14 text

,PUMJOTVQQPSU w 4QSJOH'SBNFXPSLͷ,PUMJOαϙʔτ w 3FJpFEUZQFQBSBNFUFSͰίʔυதͷܕʹΞΫηεͰ͖Δ 1 inline fun BeanFactory.getBean(): T = 2 getBean(T::class.java) 3 4 inline fun GenericApplicationContext.registerBean( 5 vararg customizers: BeanDefinitionCustomizer) { 6 registerBean(T::class.java, *customizers) 7 } 8 9 inline fun GenericApplicationContext.registerBean( 10 vararg customizers: BeanDefinitionCustomizer, 11 crossinline function: (ApplicationContext) -> T) { 12 registerBean(T::class.java, 13 Supplier { function.invoke(this) }, *customizers) 14 }

Slide 15

Slide 15 text

3PVUFS'VODUJPO%4- 1 @Configuration 2 class TaskRoutes(private val taskHandler: TaskHandler, 3 private val authFilter: AuthFilter) { 4 5 @Bean 6 fun taskRouter() = router { 7 "/api".nest { 8 accept(APPLICATION_JSON).nest { 9 POST("/task", taskHandler::create) 10 GET("/task/{id}", taskHandler::fetchByTaskId) 11 } 12 } 13 }.filter(authFilter) 14 } w 4QSJOH'SBNFXPSL͔Βͷ৽ػೳͰ"1*ϧʔςΟϯάͷ%4-

Slide 16

Slide 16 text

3PVUFS'VODUJPO%4- w 5BTL"1*ͷ5BTL)BOEMFSΫϥε w SFR4FSWFS3FRVFTU ΛҾ਺ʹ.POP4FSWFS3FTQPOTFΛฦؔ͢਺͕ฒͿ 1 @Component 2 class TaskHandler(private val taskRepository: TaskRepository) { 3 4 fun fetchByTaskId(req: ServerRequest): Mono = 5 (req.pathVariable("id").toLongOrNull() 6 ?: throw WebAppException.BadRequestException("required id")).let { 7 ServerResponse.ok().json().body(Mono.just(taskRepository.getTaskById(it))) 8 } 9 10 fun create(req: ServerRequest): Mono = 11 Validator.validBody(req).let { 12 taskRepository.createTask(it.title, it.description).let { 13 ServerResponse.ok().json().body(Mono.just(it)) 14 } 15 } 16 }

Slide 17

Slide 17 text

4QSJOH8FC'MVY w͔Β௥Ճ͞Εͨίϯϙʔωϯτ w3FBDUJWF1SPHSBNNJOHΛαϙʔτ wαʔό1VTIܕͷ"1*͕ͭ͘ΕΔ w4FSWFS4FOU&WFOUTͰఏڙ w4QSJOHXFCqVYͱXFCNWD͸ڞଘՄ ೳ

Slide 18

Slide 18 text

4QSJOH8FC'MVY 1 @Configuration 2 class TaskRoutes(private val taskHandler: TaskHandler, 3 private val authFilter: AuthFilter) { 4 @Bean 5 fun taskRouter() = router { 6 "/api".nest { 7 accept(TEXT_EVENT_STREAM).nest { 8 GET("/task/count", taskHandler::fetchTaskCount) 9 } 10 } 11 }.filter(authFilter) 12 } 13 14 @Component 15 class TaskHandler(private val taskService: TaskService) { 16 fun fetchTaskCount(req: ServerRequest): Mono { 17 return ok().json().bodyToServerSentEvents( 18 taskService.subscribeTaskCount().map { TaskCount(it) } 19 ) 20 } 21 } w 4USFBNͳ"1*Λ࣮૷Ͱ͖Δ؆ศͳ࢓૊Έ͕͋Δ

Slide 19

Slide 19 text

IUUQTTQFBLFSEFDLDPNTPVTIJDBEPULUOVNCFS w ,PUMJOݴޠͷ׆༻ࣄྫΛத৺ʹͨ͠εϥΠυ

Slide 20

Slide 20 text

·ͱΊ4QSJOH#PPUͱ,PUMJO w ,PUMJOαϙʔτͰ,PUMJOΒ͍͠ίʔυΛॻ͘ w 3PVUFS'VODUJPO%4- w "1*։ൃʹมԽ w 4QSJOH8FC'MVY w ϦΞϧλΠϜͳίϯςϯπఏڙ͕Մೳʹ

Slide 21

Slide 21 text

ίϯςφͱϚΠΫϩαʔϏε w ϚΠΫϩαʔϏεͷϧʔϧ w %PDLFSpMFΛ࣋ͪίϯςφԽ͢Δ͜ͱ w ؀ڥม਺Λίϯςφʹ%* w LTͷ4FDSFUϦιʔεΛ׆༻ w LTͷ%BNFO4FUʹΑΔαʔϏεϩάऩूͷมԽ

Slide 22

Slide 22 text

؀ڥม਺Λίϯςφʹ%* 1 // src/main/resources/application.properties 2 3 auth.env=${AUTH_ENV:local} 1 // src/main/kotlin/app/config/AppProperties.kt 2 3 @ConfigurationProperties("auth") 4 class AppProperties { 5 var env: String = “local" // local͸σϑΥϧτม਺ 6 } w %#ઃఆͳͲҟͳΔ஋͸ίϯςφͷ؀ڥม਺ʹηοτ w 4QSJOHͷ$POpHVSBUJPO1SPQFSUJFTͰBQQMJDBUJPOQSPQFSUJFTΛϚοϐϯά w ֤؀ڥͷ؀ڥม਺͸ू໿ͨ͠ϨϙδτϦͰ؅ཧ w 4QSJOHʹ͸ίϯςφͱ਌࿨ੑΛ࣋ͭ࢓૊Έ͕͋Δ"

Slide 23

Slide 23 text

LTͷ4FDSFUϦιʔε 1 apiVersion: v1 2 kind: Secret 3 metadata: 4 name: mysecret 5 type: Opaque 6 data: 7 username: YWRtaW4= 8 password: MWYyZDFlMmU2N2Rm w ؀ڥຖʹҟͳΔ஋ɺओʹػີ৘ใΛѻ͏Ϧιʔε w /BNFTQBDFຖʹϦιʔεԽ

Slide 24

Slide 24 text

4FDSFUΛ؀ڥม਺Ͱ͔ͭ͏ 1 apiVersion: v1 2 kind: Pod 3 metadata: 4 name: secret-env-pod 5 spec: 6 containers: 7 - name: mycontainer 8 image: redis 9 env: 10 - name: SECRET_USERNAME 11 valueFrom: 12 secretKeyRef: 13 name: mysecret 14 key: username 15 - name: SECRET_PASSWORD 16 valueFrom: 17 secretKeyRef: 18 name: mysecret 19 key: password 20 restartPolicy: Never w ίϯςφͷ؀ڥม਺ʹηοτ w ϚΠΫϩαʔϏεͰԣஅతʹڞ௨ͷϦιʔεΛࢀরͰ͖Δ

Slide 25

Slide 25 text

αʔϏεϩάऩूͷมԽ w ͜Ε·ͰͷFDTͰ͸ϚΠΫϩαʔϏε୯ҐͰΫϥελΛ૊ΜͰ͍ͨ w ϚΠΫϩαʔϏεͱηοτͰαΠυΧʔίϯςφΛ഑ஔʢqVFOUʣ w LTͷ%BFNPO4FUͰqVFOUEΛ/PEFʹ഑ஔʢ/PEF͸QPEͷू߹ମʣ w /PEF಺ͷίϯςφͷϩάΛऩू w αΠυΧʔίϯςφͷఫഇ( w ΞϓϦͷϩάग़ྗઌΛඪ४ग़ྗͷΈͱ͢Δ w MPHCBDLͷઃఆ͕؆ૉʹ(

Slide 26

Slide 26 text

TUFSO wQPEͷϩάΛUBJMͯ͘͠ΕΔ wDMVTUFS OBNFTQBDF DPOUBJOFS  TFMFDUPSFUDͰϑΟϧλϦϯά wKRͱ߹Θͤͯϩά֬ೝ wೝূ͕௨Ε͹ϩʔΧϧ͔Βར༻Մೳ w։ൃͷ͓ڙʹ" IUUQTHJUIVCDPNXFSDLFSTUFSO

Slide 27

Slide 27 text

ίϯςφͱϚΠΫϩαʔϏεਤղ wίϯςφͷ؀ڥ৘ใ͸؀ڥม਺Ͱ %* wڞ௨Խ͍ͨ͠ม਺͸LTͷ4FDSFU ϦιʔεΛར༻ wίϯςφͷϩά͸LTͷ %BFNPO4FUΛ/PEFʹ഑ஔͯ͠ qVFUEͰऩू

Slide 28

Slide 28 text

%PDLFS,VCFSOFUFT࣮ફ։ൃೖ໳ w '3&4)-*7&5FDI-FBEͷࢁా໌ݑʢஶʣ w ग़൛ɿٕज़ධ࿦ࣾ ଓ͖͸ςΩετͰ"

Slide 29

Slide 29 text

·ͱΊίϯςφͱϚΠΫϩαʔϏε w ίϯςφͱͷ਌࿨ੑ͕େࣄ w ؀ڥม਺ͷϚοϐϯά w LTϦιʔεʹΑΓίϯςφߏ੒ʹมԽ w 4FDSFU %BFNPO4FU w TUFSOศར(

Slide 30

Slide 30 text

H31$ w (PPHMF͕։ൃͨ͠31$ϑϨʔϜϫʔΫ w )551ͱͷޮ཰తͳ઀ଓ w ૒ํ޲ετϦʔϛϯάͷ௨৴ํࣜ w 1SPUPCVGʹΑΓσʔλγϦΞϥΠζ w ๛෋ͳݴޠαϙʔτ w $ɺ+BWBɺ(Pɺ/PEFKTɺ1ZUIPOɺ1)1ɺ3VCZ w ϚΠΫϩαʔϏε։ൃͱ߹கͨ͠઀ଓ΍σʔλసૹ

Slide 31

Slide 31 text

ετϦʔϛϯά w ༷ʑͳ௨৴ํࣜ w 6OBSZ31$ʗ୯Ұ௨৴ w 4FSWFSTUSFBNJOH31$ʗ4FSWFS1VTIܕ w $MJFOUTUSFBNJOH31$ʗ$MJFOU1VTIܕ w #JEJSFDUJPOBMTUSFBNJOHʗ૒ํ޲௨৴ w ࢹௌ਺ɺίϝϯτ਺ɺ഑৴ঢ়گͳͲͷϦιʔ εΛετϦʔϛϯάͰྲྀ͢ 4FWFSTUSFBNJOH31$

Slide 32

Slide 32 text

ϚΠΫϩαʔϏεͱH31$ w 1VCMJDͱ*OUFSOBM޲͚ͷH31$4FSWFSͷఏڙ w 1PSUΛ෼͚ͯ̎ͭͷ4FSWFSΛىಈ w 4QSJOHGSBNFXPSL w $PNNBOE-JOF3VOOFS w %JTQPTBCMF#FBO

Slide 33

Slide 33 text

H31$4FWFS3VOOFS 1 @Configuration 2 class GrpcGlobalServerRunner(private val appProperties: AppProperties, private val authInterceptor: AuthInterceptor, 3 private val balanceServer: BalanceServer) : CommandLineRunner, DisposableBean { 4 5 lateinit var server: Server 6 7 override fun run(args: Array) { 8 NettyServerBuilder.forPort(appProperties.grpc.global.port) 9 .addService(ProtoReflectionService.newInstance()) 10 .apply { 11 balanceServer.let { 12 ServerInterceptors.intercept(it, authInterceptor) 13 addService(it) 14 } 15 } 16 .build().start() 17 .also { server = it } 18 } 19 20 override fun destroy() { 21 Optional.ofNullable(server).ifPresent { 22 it.shutdown() 23 } 24 } 25 }

Slide 34

Slide 34 text

4QSJOH'SBNFXPSLͱH31$ w 4QSJOHGSBNFXPSLͷFDPTZTUFNͱHSQDKBWBͷڞଘ( w 4FSWFSىಈɺ*OUFSDFQUPSͳͲ೉ͳ͘ߏஙՄೳ w ೖ೦ͳΫϥΠΞϯτૄ௨֬ೝʢಛʹ"OESPJEʣ w ఆظతͳपลϥΠϒϥϦͷΞοϓσʔτ

Slide 35

Slide 35 text

·ͱΊH31$ͱϚΠΫϩαʔϏε w ޮ཰తͳ௨৴ w ϚΠΫϩαʔϏεʹ߹கͨ͠։ൃ w 4QSJOH'SBNFXPSLͰH31$4FWFSͷߏங w 1PSUͷ׆༻ w ̍ͭͷKBSʹ1VCMJD *OUFSOBMͷ4FSWFSΛىಈ

Slide 36

Slide 36 text

·ͱΊ w ϚΠΫϩαʔϏε։ൃͰकΓ΋߈Ί΋Ռ׶ʹ w αʔόαΠυ,PUMJO։ൃʹ͸4QSJOH#PPUYΛΦεεϝ" w ,PUMJOαϙʔτ ॆ࣮ͨ͠4QSJOH'SBNFXPSLͷίϯϙʔωϯτ w ίϯςφͱͷ਌࿨ੑ w LTͷಋೖʹΑΓίϯςφ഑ஔઓུʹมԽ w ϦΞϧλΠϜͳίϯςϯπఏڙʹ࠷దͳ௨৴ํࣜͷબ୒ w 44& )551 w 4QSJOH8FC'MVY H31$

Slide 37

Slide 37 text

খ͞ͳϚΠΫϩαʔϏε͔Β ,PUMJOͰ4FSWFS4JEF։ൃΛ"

Slide 38

Slide 38 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ