case study

๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ APIํ™” - ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๊ธฐ

์ด ๊ธ€์—์„œ๋Š” ํ™ˆ์‡ผํ•‘๋ชจ์•„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์—ฌ๋Ÿฌ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ์ค‘ ํ•˜๋‚˜์ธ ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๊ธฐ๋ฅผ APIํ™” ํ–ˆ๋˜ ๊ณผ์ •์„ ์†Œ๊ฐœํ•œ๋‹ค.

2021.2.5(๊ธˆ)
๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ APIํ™” - ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๊ธฐ

์ด ๊ธ€์—์„œ๋Š” ํ™ˆ์‡ผํ•‘๋ชจ์•„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์—ฌ๋Ÿฌ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ์ค‘ ํ•˜๋‚˜์ธ ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๊ธฐ๋ฅผ APIํ™” ํ–ˆ๋˜ ๊ณผ์ •์„ ์†Œ๊ฐœํ•œ๋‹ค.


๊ฐœ์š”

๊ธฐ์กด์— ํ™ˆ์‡ผํ•‘๋ชจ์•„์— ์ ์šฉ๋œ ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๊ธฐ๋Š” on-premise๋กœ ๊ตฌ์„ฑ๋œ ์„œ๋ฒ„์— ๋ฐฐํฌ๋ผ ์žˆ์—ˆ๋‹ค. on-premise์— ๊ตฌ์ถ•๋œ ์„œ๋ฒ„์— ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋‹ˆ ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์„œ๋ฒ„์˜ ์žฅ์• ๋‚˜, ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— ์˜ํ•ด ์„œ๋ฒ„์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ ์œ ๋ผ ๋ฒ„๋ฆฌ๊ฑฐ๋‚˜ ํ•˜๋Š” ์ด์Šˆ๋“ค์ด ์ƒ๊ธฐ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜๋‹ค.


๋•Œ๋ฌธ์— Kubernetes๋ฅผ ํ™œ์šฉํ•ด ์ž๋™ํ™”๋œ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ–ˆ๊ณ , Kubernetes๋ฅผ ํ™œ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๊ธฐ์กด ๋ชจ๋ธ API๋ฅผ ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์—ˆ๋‹ค. 


์ด ๊ธ€์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ชจ๋ธ์ด ์–ด๋–ค ํ˜•ํƒœ๋กœ APIํ™” ๋ผ ์žˆ์œผ๋ฉฐ, GKE(Google Kubernetes Engine)์— ์–ด๋–ค ๊ณผ์ •์„ ํ†ตํ•ด ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ–ˆ๋Š” ์ง€์— ๋Œ€ํ•ด ์„ค๋ช…ํ•  ๊ฒƒ์ด๋‹ค.


ํ™ˆ์‡ผํ•‘๋ชจ์•„์—์„œ ์‚ฌ์šฉ ์ค‘์ธ ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๊ธฐ API๋Š” ๋‹ค์Œ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ํฌ๊ฒŒ ๋‘ ๊ฐœ์˜ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋ผ ์žˆ๋‹ค.



์‚ฌ์šฉ์ž๊ฐ€ Client API๋ฅผ ํ†ตํ•ด ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๋ฅผ ์š”์ฒญํ•˜๋ฉด, Client API๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ๋ฐ›์€ ์ƒํ’ˆ๋ช…๊ณผ ์ด๋ฏธ์ง€ ์ฃผ์†Œ๋ฅผ ๋ฒกํ„ฐํ™”ํ•ด, Model API์— ํ•ด๋‹น ๊ฐ’์„ ์ „๋‹ฌํ•œ ํ›„, ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๋Š”๋‹ค.


์œ„ ๊ตฌ์กฐ์—์„œ Model API, Client API๊ฐ€ ๊ฐ๊ฐ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋ผ ์žˆ๊ณ , ์–ด๋–ป๊ฒŒ ๊ฐ API๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š”์ง€, ๊ทธ ๊ณผ์ •์— ๋Œ€ํ•ด ์ƒ์„ธํžˆ ์„ค๋ช…ํ•  ๊ฒƒ์ด๋‹ค.


Model API

์‹ค์ œ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ชจ๋ธ์ธ Model API๋Š” tensorflow-serving์„ ์ด์šฉํ•ด ๊ตฌ์„ฑ๋ผ ์žˆ๋‹ค.


Tensorflow Serving์€ Tensorflow ๋ชจ๋ธ์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ฐฐํฌ ๋ฐ ์„œ๋น„์Šคํ™” ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœ๋œ TFX์˜ ์ผ๋ถ€๋ถ„์ด๋ฉฐ, ํ•™์Šต๋œ ๋ชจ๋ธ์„ ์ ์€ ์ž์›์œผ๋กœ๋„ ๋น ๋ฅธ ์†๋„๋ฅผ ๊ฐ–๋Š” Serving API๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.


์ด Model API๋ฅผ GKE(Google Kubernetes Engine) ์œ„์— ๋ฐฐํฌํ•ด ์ž๋™์œผ๋กœ Scale-out์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•  ๊ฒƒ์ด๋‹ค.


์ค€๋น„ ๊ณผ์ •

์ค€๋น„ ๊ณผ์ •์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ํ•™์Šตํ•œ ๋ชจ๋ธ์„ Tensorflow Serving์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ์ค€๋น„ํ•˜๋Š” ๊ฒƒ๊ณผ ์ค€๋น„๋œ ๋ชจ๋ธ์„ Kubernete ์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ๋„๋ก GCS๋กœ ์—…๋กœ๋“œ ํ•˜๋Š” ๊ฒƒ, ๊ทธ๋ฆฌ๊ณ  ์ ์ ˆํ•˜๊ฒŒ Tensorflow Serving ์„ค์ • ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.


1. SavedModel Format์œผ๋กœ ํ•™์Šต๋œ ๋ชจ๋ธ ์ค€๋น„



Tensorflow Serving์„ ํ™œ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” SavedModel ํฌ๋งท์œผ๋กœ ํ•™์Šต๋œ ๋ชจ๋ธ์ด ํ•„์š”ํ•˜๋‹ค. ๋ชจ๋ธ์„ ํ•™์Šต์‹œํ‚ค๋Š” ์‹œ์ ์—์„œ SavedModel ํฌ๋งท์œผ๋กœ ์ €์žฅํ•˜์ง€ ์•Š์•˜๋”๋ผ๋„, ๋‹ค์Œ๊ณผ ๊ฐ™์ด SavedModel ํฌ๋งท์œผ๋กœ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


ํ˜น์€, tensorflow 1.x ์—์„œ ์ €์žฅํ•œ Keras ๋ชจ๋ธ๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์†์‰ฝ๊ฒŒ SavedModel ํฌ๋งท์œผ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•˜๋‹ค.



์ •์ƒ์ ์œผ๋กœ SavedModel ํฌ๋งท์œผ๋กœ ์ €์žฅ์ด ๋๋‹ค๋ฉด, saved_model.pb ํŒŒ์ผ๊ณผ ํ•จ๊ป˜ ์ธํผ๋Ÿฐ์Šค์— ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋“ค์ด ํด๋”๋กœ ์กด์žฌํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

 

2. ํ•™์Šต๋œ ๋ชจ๋ธ GCS๋กœ ์—…๋กœ๋“œ

์ตœ์ข…์ ์œผ๋กœ Model API๋ฅผ GKE์— ์—…๋กœ๋“œ ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, GKE์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ€ํ™˜๋œ SavedModel์„ GCS๋กœ ์—…๋กœ๋“œ ํ•ด์•ผ ํ•œ๋‹ค. ํ˜„์žฌ ๋ชจ๋ธ์˜ ์ €์žฅ์€ GCS์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์—…๋กœ๋“œ ๋ผ ์žˆ๋‹ค.



cate_dict ํด๋”๋Š” ๋ชจ๋ธ์—์„œ ์˜ˆ์ธกํ•œ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ช…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ ์ •๋ณด๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š” ํŒŒ์ผ๋“ค์ด ๋“ค์–ด ์žˆ๋Š” ํด๋”์ด๋ฉฐ, tokenizer ํด๋”๋Š” ์ƒํ’ˆ๋ช…์„ ์ „์ฒ˜๋ฆฌํ•ด ๋„์–ด์“ฐ๊ธฐ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ชจ๋“ˆ์ด ๋“ค์–ด์žˆ๋Š” ํด๋”์ด๋‹ค.


hybrid_classifier ํด๋”๋Š” ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ํ™œ์šฉํ•ด ์ƒํ’ˆ์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์˜ˆ์ธกํ•˜๋Š” ๋ชจ๋ธ์ด ์ €์žฅ๋œ ํด๋”์ด๋ฉฐ, text_classifier ํด๋”๋Š” ํ…์ŠคํŠธ ์ •๋ณด๋งŒ ํ™œ์šฉํ•ด ์ƒํ’ˆ์˜ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์˜ˆ์ธกํ•˜๋Š” ๋ชจ๋ธ์ด ์ €์žฅ๋œ ํด๋”์ด๋‹ค. 


๋‘ ๋ชจ๋ธ์„ ์ „๋ถ€ ์‚ฌ์šฉํ•ด ์ตœ์ข…์ ์œผ๋กœ ์•™์ƒ๋ธ”์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๋ชจ๋ธ ๋ชจ๋‘ SavedModel ํ˜•์‹์œผ๋กœ ์ €์žฅํ•ด GCS์— ์—…๋กœ๋“œ ํ–ˆ๋‹ค.


๊ฐ ๋ชจ๋ธ ํด๋”์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆซ์ž๋ฅผ ํด๋”๋ช…์œผ๋กœ ๊ฐ–๋Š” ๊ตฌ์กฐ๋กœ ๋ชจ๋ธ์ด ์—…๋กœ๋“œ ๋ผ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ์ดํ›„์— Tensorflow Serving์„ ์ด์šฉํ•ด ๋ชจ๋ธ์„ ์„œ๋น™ํ•  ๋•Œ, Tensorflow Serving์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ€์žฅ ์ตœ์‹  ๋ฒ„์ „์˜ ๋ชจ๋ธ ๋ฒˆํ˜ธ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค(์„ค์ •์„ ํ†ตํ•ด ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋™์‹œ์— ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ ๋ชจ๋ธ์„ ์„œ๋น™ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๋ฒ„์ „์˜ ๋ชจ๋ธ๋งŒ ์„œ๋น™ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค).


์ด ์ˆซ์ž๋“ค์ด ๊ฐ ๋ชจ๋ธ์˜ id๊ฐ€ ๋˜๋Š” ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ์ˆซ์ž๋กœ ์ด๋ค„์ง„ ํด๋”๋กœ ๋ชจ๋ธ์„ ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค.



3. models.config ํŒŒ์ผ ์„ค์ •

tensorflow serving์„ ์ด์šฉํ•ด Model API๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ ์ž ํ•  ๋•Œ์—๋Š”, ์„ค์ •ํŒŒ์ผ(์—ฌ๊ธฐ์„œ๋Š” models.config ํŒŒ์ผ)์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.


์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜ ๋ชจ๋ธ API๋ฅผ ๋„์šธ ๋•Œ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์„ค์ • ํŒŒ์ผ์„ ์ž‘์„ฑํ–ˆ๋‹ค.



Tensorflow Serving์„ ์œ„์™€ ๊ฐ™์€ ์„ค์ • ํŒŒ์ผ์„ ์ด์šฉํ•ด ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด, ํ•œ ๋ฒˆ์— ๋‘ ์ข…๋ฅ˜์˜ ๋ชจ๋ธ(name key๋กœ ๋ช…์‹œ๋œ text_classifier, hybrid_classifier)์„ APIํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.


text_classifier, hybrid_classifier ๋ชจ๋‘ ๊ฐ base_path์— ์กด์žฌํ•˜๋Š” ๋ชจ๋ธ๋“ค์„ ๋กœ๋“œํ•ด APIํ™” ํ•˜๊ฒŒ ๋˜๋ฉฐ, ์ด๋•Œ ๊ฐ base_path (/models/text_classifier, /models/hybrid_classifier)์—๋Š” ์œ„ ๊ณผ์ •์—์„œ GCS์— ์ €์žฅํ–ˆ๋˜ ๊ฒƒ ์ฒ˜๋Ÿผ ๋ฒˆํ˜ธ๋ฅผ ํด๋”๋ช…์œผ๋กœ ํ•˜๋Š” SavedModel ๋“ค์ด ์ €์žฅ๋ผ ์žˆ์–ด์•ผ ํ•œ๋‹ค. 


๊ทธ๋ž˜์•ผ Tensorflow Serving์ด ๋ชจ๋ธ์„ ๋กœ๋“œ ํ•  ๋•Œ, ๊ฐ€์žฅ ๋ฒˆํ˜ธ๊ฐ€ ํฐ ๋ชจ๋ธ์„ ์ž๋™์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ€๊ธ‰์ ์ด๋ฉด base_path์˜ ๊ฐ€์žฅ ํ•˜์œ„ ํด๋” ๋ช…๊ณผ name์€ ๋งค์นญ์‹œํ‚ค๋„๋กํ•œ๋‹ค.


์‹คํ–‰ ๊ณผ์ •

์‹คํ–‰ ๊ณผ์ •์€ ํฌ๊ฒŒ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ ์ ธ ์žˆ๋‹ค. ํ•˜๋‚˜๋Š” Dockerfile์„ ์ด์šฉํ•ด docker image ๋นŒ๋“œ & GKE deployment ๋ฐฐํฌ ๊ณผ์ •, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” GKE Ingressroute Service ๋ฐฐํฌ ๊ณผ์ •, ๊ทธ๋ฆฌ๊ณ  traefik ์˜ middleware ์„ค์ •์ด๋‹ค.


1. Docker image ๋นŒ๋“œ & GKE deployment ๋ฐฐํฌ

Model API์—์„œ ์‚ฌ์šฉ๋˜๋Š” Dockerfile๊ณผ Dockerfile ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜๋Š” serving_api_entrypoint.sh, GKE์— ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ deployment.yaml์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


์ด Dockefile์„ ์ด์šฉํ•ด GKE ์ƒ์—์„œ tensorflow serving API๋ฅผ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, tensorflow serving Dockerfile์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑํ–ˆ์œผ๋ฉฐ, serving_api_entrypoint.sh ํŒŒ์ผ์„ ์ˆ˜ํ–‰ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ค€๋น„ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ serving_api_entrypoint.sh๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ตฌ์กฐ๋‹ค.


serving_api_entirypoint.sh ํŒŒ์ผ์—์„œ๋Š” GCS์— ์—…๋กœ๋“œํ•œ SavedModel ํŒŒ์ผ์„ container ๋‚ด๋ถ€๋กœ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ๋ฏธ๋ฆฌ ์ž‘์„ฑํ•œ models.config ํŒŒ์ผ์„ ์ด์šฉํ•ด tensorflow-serving API๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.


prepare_serving.py ์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ๋ชจ๋ธ์„ ์„œ๋น™ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋“ค์„ GCS์—์„œ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€๋กœ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.


๋ณดํ†ต Dockerfile์„ ๋นŒ๋“œํ•ด Docker image repository๋กœ ์—…๋กœ๋“œํ•˜์ง€๋งŒ, Github CI๋ฅผ ํ™œ์šฉํ•ด ์ž๋™์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œ ํ›„ GCS ๋‚ด๋ถ€ ์ด๋ฏธ์ง€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์—…๋กœ๋“œ ํ•˜๋„๋ก ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ƒ์„ฑํ•œ Dockerfile์„ ์ง์ ‘ ๋นŒ๋“œํ•˜๋Š” ๊ณผ์ •์€ ์ด ๋ฌธ์„œ์—์„œ ์ œ์™ธ๋๋‹ค.


โ€“ Dockerfile์— ๋Œ€ํ•œ ์„ค๋ช…

expose 8500, 8501์„ ์ด์šฉํ•ด ๋‘ ํฌํŠธ๋ฅผ ์—ด์–ด์คฌ๋Š”๋ฐ, ์ด ๋‘ ํฌํŠธ๊ฐ€ tensorflow serving์—์„œ ์‚ฌ์šฉ๋˜๋Š” REST API์™€, gRPC ํ˜ธ์ถœ์— ์‚ฌ์šฉ๋˜๋Š” ํฌํŠธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋‘ ํฌํŠธ๋ฅผ ์—ด์–ด์คฌ๋‹ค.


deployment.yaml์—์„œ๋Š” asia.gcr.io/storage-273502/buzzni-ai-category-api-serving์— ์ €์žฅ๋œ ์ด๋ฏธ์ง€๋ฅผ ์ด์šฉํ•ด GKE์— ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

buzzni-ai-category-api-serving ์ด๋ฏธ์ง€๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋นŒ๋“œ๋˜๊ณ , ์ด๊ฒƒ์ด ์ด๋ฏธ์ง€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์ •์ƒ์ ์œผ๋กœ ์—…๋กœ๋“œ ๋๋‹ค๋ฉด, ๋กœ์ปฌ ํ„ฐ๋ฏธ๋„์—์„œ๋Š”

kubectl apply -f deployment.yaml

๋ช…๋ น์–ด๋งŒ ์ˆ˜ํ–‰ํ•ด Model API๋ฅผ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋‹ค.


์ด ๋ช…๋ น์–ด๊ฐ€ ์ˆ˜ํ–‰๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด GKE์— ์ •์ƒ์ ์œผ๋กœ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜๋ฅผ ์œ„ํ•œ Model API๋ฅผ ์œ„ํ•œ deployment pod์ด ์ƒ์„ฑ๋œ๋‹ค.



2. GKE Ingressroute Service ๋ฐฐํฌ

์œ„ ๊ณผ์ •์„ ์ง„ํ–‰ํ–ˆ๋‹ค๊ณ , ๋ฐ”๋กœ API Endpoint๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , URL์„ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. AI Lab์˜ GKE๋Š” Ingressroute๊ณผ Traefik์„ ์ด์šฉํ•ด ์™ธ๋ถ€ ์ ‘์†์„ GKE ๋‚ด๋ถ€์˜ Pod๊ณผ ์—ฐ๊ฒฐ์‹œ์ผœ ์ฃผ๊ณ  ์žˆ๋‹ค. 


์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋ณ„๋„์˜ Service Pod๋ฅผ ๋„์›Œ์•ผ ํ•˜๋ฉฐ, ํ•ด๋‹น service์˜ ์„ค์ • ํŒŒ์ผ์€ deployment_service.yaml ํŒŒ์ผ์ด๋‹ค.


์ด ์„ค์ • ํŒŒ์ผ์—์„œ ๋ณต์žกํ•œ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ๊ธฐ์กด์— ์ •์˜๋œ vpn-web entrypoint๋ฅผ ํ™œ์šฉํ•ด, ๋‚ด๋ถ€ IP ๋ง์œผ๋กœ๋ถ€ํ„ฐ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์„ ์„ค์ •ํ•œ ๋‚ด๋ถ€ deployment pod์—(selector: app: category-api-serving ์„ค์ •) ์ „๋‹ฌํ•ด ์ฃผ๊ฒ ๋‹ค๋Š” ์˜๋ฏธ๋‹ค.


๊ทธ๋ฆฌ๊ณ , URL์ด /category-serving/๊ณผ ๋งค์นญ๋˜๋Š” ์š”์ฒญ์— ๋Œ€ํ•ด์„œ api-strip์ด๋ผ๋Š” middle-ware๋ฅผ ํ†ตํ•˜๊ฒ ๋‹ค๋Š” ์„ค์ •์„ ์คฌ๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์ด์–ด์„œ ํ•˜๋„๋ก ํ•  ๊ฒƒ์ด๋‹ค.


๋•Œ๋ฌธ์— Ingressroute Service ๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋กœ์ปฌ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋งŒ ์ˆ˜ํ–‰ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.


kubectl apply -f buzzni-ai-category-api/kubernetes/deployment_service.yaml


3. traefik-middleware ์„ค์ •

์•ž์„œ ์„ค์ •ํ•œ middleware๋ฅผ ํ†ตํ•ด API ์š”์ฒญ ์ฃผ์†Œ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ํ˜•ํƒœ์˜ ์ ‘๊ทผ URL์ด ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์ด์— ๋งž๊ฒŒ ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ kubectl ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•ด ๊ธฐ์กด์— ์ •์˜๋œ ์„ค์ •๊ฐ’์— ์›ํ•˜๋Š” ์ฃผ์†Œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.


kubectl edit middlewares.traefik.containo.us api-strip


์œ„ ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•ด traefik ์„ค์ •์—์„œ api-strip ์ฃผ์†Œ์— deployment_service์—์„œ ์„ค์ •๋œ category-serving ํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.


๊ฒฐ๊ณผ ํ™•์ธ

์—ฌ๊ธฐ๊นŒ์ง€ ๋ฌธ์ œ ์—†์ด ์ˆ˜ํ–‰์ด ๋๋‹ค๋ฉด, ๋‹ค์Œ URL ์„ ํ†ตํ•ด ์ •์ƒ์ ์œผ๋กœ Model API๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


http://IngressRoute์ฃผ์†Œ/category-serving/v1/models/hybrid_classifier


์š”์ฒญ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.



๊ผญ ์œ„์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ์ด ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , Client API๋ฅผ ๋ฐฐํฌํ•˜๋„๋ก ํ•˜์ž.


Client API

Client API๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” API์ด๋ฉฐ, Swagger ๊ธฐ๋ฐ˜์˜ Flask App์œผ๋กœ ๊ฐœ๋ฐœ๋ผ ์žˆ์œผ๋ฉฐ, Model API์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ GKE์— ๋ฐฐํฌ๋ผ ์žˆ๋‹ค.


์ค€๋น„ ๊ณผ์ •

Model API์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, Client API ์—ญ์‹œ Client API์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ช‡๋ช‡ ์ž์›(์—ฌ๊ธฐ์—์„œ๋Š” Text Tokenizer, idx โ†’ plain text dict)์„ ๋ฏธ๋ฆฌ GCS์— ์—…๋กœ๋“œ ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค.


1. ํ•„์š”ํ•œ ํŒŒ์ผ GCS ์—…๋กœ๋“œ

Client API์—์„œ ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ํ•˜๋‚˜๋Š” ํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ์ธ์ฝ”๋”ฉ ํ•˜๊ธฐ ์œ„ํ•œ tokenizer ์ด๋ฉฐ, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ์˜ index ๊ฐ’์„ plain text๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ python dictionary(ํ…์ŠคํŠธ, ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๋‘ ๊ฐœ)์ด๋‹ค.

Model API์—์„œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋น„์Šทํ•œ ์ฝ”๋“œ์—์„œ ํ•ด๋‹น ์ž‘์—…์„ ์ง„ํ–‰ํ•ด ์ฃผ๊ณ  ์žˆ์œผ๋ฉฐ, ๋ฐฐํฌ ์ „์— ๋ฏธ๋ฆฌ ์‹คํ–‰ํ•ด GCS์— ํ•„์š”ํ•œ ํŒŒ์ผ๋“ค์„ ์—…๋กœ๋“œ ํ•ด์•ผ ํ•œ๋‹ค.


์‹คํ–‰ ๊ณผ์ •

์‹คํ–‰ ๊ณผ์ • ์—ญ์‹œ Model API์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, Dockerfile์„ ์ด์šฉํ•ด docker image ๋นŒ๋“œ & GKE deployment ๋ฐฐํฌ ๊ณผ์ •, ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” GKE Ingressroute Service ๋ฐฐํฌ ๊ณผ์ •, ๊ทธ๋ฆฌ๊ณ  traefik์˜ middleware ์„ค์ • ์„ธ ๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ์ด๋ค„์ ธ ์žˆ๋‹ค.


1. docker image ๋นŒ๋“œ & GKE deployment ๋ฐฐํฌ

Client API์—์„œ ์‚ฌ์šฉ๋˜๋Š” Dockerfile๊ณผ, Dockerfile ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜๋Š” client_api_entrypoint.sh, GKE์— ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ deployment.yaml์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


Client API ์—์„œ ์‚ฌ์šฉ๋˜๋Š” Dockerfile์€ Model API ์—์„œ ์‚ฌ์šฉ๋˜๋Š” Dockerfile๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ์œผ๋ฉฐ, client_api_entrypoint.sh ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ค€๋น„ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ client_api_entrypoint.sh ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค.


client_api_entrypoint.sh ์—์„œ๋Š” GCS์— ์—…๋กœ๋“œํ•œ ์—ฌ๋Ÿฌ ํŒŒ์ผ๋“ค์„ docker container ๋‚ด๋ถ€๋กœ ๋‹ค์šด๋กœ๋“œ ํ•˜๊ณ , client api ํ”„๋กœ์„ธ์Šค๋ฅผ ์ˆ˜ํ–‰์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.


ํ•˜์ง€๋งŒ Model API ์—์„œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, Github CI ์—์„œ ์ž๋™์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋„๋ก ์„ค์ •ํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘ Dockerfile์„ ๋นŒ๋“œํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ, ๋ฐฐํฌ ์—ญ์‹œ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค yaml ํŒŒ์ผ์ธ deployment.yaml ํŒŒ์ผ์„ ์ด์šฉํ•ด ์†์‰ฝ๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋‹ค.


Github CI ์—์„œ ์ •์ƒ์ ์œผ๋กœ ์ด๋ฏธ์ง€๊ฐ€ ๋นŒ๋“œ๋˜์—ˆ๋‹ค๋ฉด, ๋กœ์ปฌ ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Client API๋ฅผ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋‹ค.


kubectl apply -f deployment.yaml 


์œ„ ๋ช…๋ น์–ด๊ฐ€ ์ˆ˜ํ–‰๋˜๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด deployment pod์ด ์ƒ์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.



2. GKE Ingressroute Service ๋ฐฐํฌ

Client API ์—ญ์‹œ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Ingressroute Service๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.


Ingressroute Service๋ฅผ ์œ„ํ•œ ์„ค์ •ํŒŒ์ผ์€ deployment_service.yaml ํŒŒ์ผ์ด๋ฉฐ, ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•ด ์„œ๋น„์Šค ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.


kubectl apply -f deployment_service.yaml

 

3. traefik-middleware ์„ค์ •

๊ทธ๋ฆฌ๊ณ  traefik์˜ middleware ์—ญ์‹œ ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.


Model API์—์„œ ์„ค์ •ํ•˜์˜€๋˜ ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด api-strip ์ชฝ์„ ์ˆ˜์ •ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.


kubectl edit middlewares.traefik.containo.us api-strip


๊ฒฐ๊ณผ ํ™•์ธ

์—ฌ๊ธฐ๊นŒ์ง€ ๋ฌธ์ œ๊ฐ€ ์—†์ด ์‹คํ–‰์ด ๋๋‹ค๋ฉด, URL ์„ ํ†ตํ•ด ์ •์ƒ์ ์œผ๋กœ Client API๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


 

๊ฐœ์„ ์ 

์ง€๊ธˆ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ๋ถ€๋ถ„์— ํ•„์š”ํ•œ ๊ฐœ์„ ์ ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค(๋ชจ๋ธ ์ž์ฒด์— ๋Œ€ํ•œ ๊ฐœ์„ ์ ์„ ์ œ์™ธํ•œ).


์ฒซ ๋ฒˆ์งธ๋Š” Client API๋ฅผ ๋‹จ์ˆœ Flask๋งŒ ์ด์šฉํ–ˆ๋Š”๋ฐ, gunicorn์œผ๋กœ ํ•œ๋ฒˆ ๋” wrappingํ•ด multi-processing์„ ์ ์šฉํ•˜๊ฑฐ๋‚˜,  fast-api, sanic ๋“ฑ์„ ํ™œ์šฉํ•ด ๋น„๋™๊ธฐ๋กœ ๋™์‹œ์— ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 


ํ˜„์žฌ ๋ฐฐํฌ๋ผ ์‚ฌ์šฉ์ค‘์ธ API์—๋Š” ์ ์šฉ๋ผ ์žˆ์ง€๋งŒ, ์ด ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ–ˆ๋˜ ๋ชฉ์ ์€ ๋‹ค๋ฅธ AI ๋ฆฌ์„œ์น˜ ์—”์ง€๋‹ˆ์–ด๊ฐ€ ๊ฐœ๋ฐœํ•œ ๋ชจ๋ธ์„ ์ง์ ‘ ์„œ๋น™ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์ด๋“œ ์ž‘์„ฑ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ œ์™ธํ–ˆ์—ˆ๋‹ค.


๋‘ ๋ฒˆ์งธ๋Š”, Tensorflow Serving์„ ํ™œ์šฉํ•œ Model API์™€, Client API์˜ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.  ํ˜„์žฌ ๋‘ ๊ฐ€์ง€ API๊ฐ€ ํ•œ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ธˆ ๋ถˆํŽธํ•œ ๋ถ€๋ถ„๋“ค์ด ์žˆ๋Š”๋ฐ, ๊ตฌ๋ถ„์ง€์–ด ๋ณ„๋„์˜ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋กœ ํ•˜๋Š” ๊ฒƒ์ด ์–ด๋–จ๊นŒ ํ•œ๋‹ค.

๋ฒ„์ฆˆ๋‹ˆ
ํ™ˆ์‡ผํ•‘๋ชจ์•„
์Šคํƒ€ํŠธ์—…
API
๋จธ์‹ ๋Ÿฌ๋‹
์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ ๋ถ„๋ฅ˜
buzzni
pr@buzzni.com