Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
調べながらGCPやってみた話/gcpug-osaka-3
Search
kadota kyohei
July 20, 2017
Programming
540
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
調べながらGCPやってみた話/gcpug-osaka-3
GCPUG In Osaka #3 で発表した資料。
色々サービス使ってみました。
https://gcpug-osaka.connpass.com/event/61297/
kadota kyohei
July 20, 2017
More Decks by kadota kyohei
See All by kadota kyohei
govulncheckの紹介/govulncheck-intro
lufia
1
83
パッケージ設計の黒魔術/Kyoto.go#63
lufia
3
590
最近変わった開発時のあれこれ/features-of-recent-go
lufia
0
970
GCPとGoの話/gcpug-osaka-6
lufia
0
530
REST is not only (web) API interface
lufia
1
1.1k
Go駆動開発で超速Pushエンジンを作った話
lufia
19
7.5k
Other Decks in Programming
See All in Programming
LL言語やWebフレームワークのPostgreSQL対応 〜DBの機能がユーザーに届くまで〜
kentaroutakeda
0
140
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
150
FastAPI の並行処理モデルを完全に理解する
hoto17296
9
3.8k
AI に Inclusive UI を書かせよう — Design Rules Skill で Compose UI を作り直す
theoriatec2024
1
460
20260828_品質と開発生産性を両立させる、AI時代のE2Eテストの考え方
magicpod
0
170
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
130
XHTMLが残したもの
yosuke_furukawa
PRO
2
460
Go × SIMDで高速化するベクトル検索 ~ルーフラインモデルでSIMDが効く境界を探れ! ~
po3rin
1
160
Can LLMs Replicate 4 Years of Compose Migration? Exploring the boundaries of automation with 279 XML files from a real product
makun
0
130
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
220
一参加者から『中の人』へ 〜全通PHPerがブースに立って学んだ、カンファレンスを100倍楽しむコツ〜
wp_daisuke
0
130
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
120
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
Design in an AI World
tapps
1
310
ラッコキーワード サービス紹介資料
rakko
1
4.8M
GitHub's CSS Performance
jonrohan
1033
470k
How to make the Groovebox
asonas
2
2.4k
Mobile First: as difficult as doing things right
swwweet
225
10k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
270
For a Future-Friendly Web
brad_frost
183
10k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Optimizing for Happiness
mojombo
378
71k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
270
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.6k
Transcript
ௐͳ͕ΒGCPͬͯ Έͨ GCPUG In Osaka #3 @plan9user (2017-07-19)
ࣗݾհ • ଟګฏ • Favorites • OS: Plan 9 •
Editor: acme • Shell: rc • ϑΣϯϦϧͰಇ͍͍ͯ·͢
None
Ϟνϕʔγϣϯ • GoΛॻ͍͍ͯͨͷͰڵຯͣͬͱ͋ͬͨ • ࣮Compute EngineͰPlan 9͕ಈ͘ • ಈ͚ͩ͘͡Όͳͯ͘ίϯιʔϧ͕͑Δ •
Plan 9ͷཧʹίϯιʔϧඞਢ
ใऩू • Google Cloud Platform Japan ެࣜϒϩά • Publickey •
Google Cloud Platform Advent Calendar • ໘നͦ͏ͳαʔϏευΩϡϝϯτಡΉ
Կ͔͠·ͤΜ͔?
• ཉ͔ͬͨ͠αʔϏεΛ(్த·Ͱ)࡞ͬͨ • ॳΊͯͷਓ • Ͳ͏͍͏αʔϏε͕͋Δͷ͔ • ͍ͬͯΔਓ • ڭ͍͑ͯͩ͘͞
ࠓ͢͜ͱ
͜Μͳ͜ͱ͋Γ·ͤΜ͔? • ಡΜͩ͜ͱ͋Δ͚ͲݕࡧͰ͖ͳ͍ • ϒοΫϚʔΫ͢ΔͷΛΕ͍ͯͨ • هࣄͦͷͷ͕ͳ͘ͳͬͯ͠·ͬͨ ޙഐʮͳΜͰ͜Ε೩͑ͯΔΜͰ͔͢ʯʮͦΕ͔֬ݩهࣄ͕...ʯ
ಡΜͩهࣄશ෦͍ͨ͠
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
༻ҙ͢Δͷ • GAE/Go SDK • Go (͋Δͱศར) $ nix-env -i
go-1.8.3 $ nix-env -i google-app-engine-go-sdk
ڥΛબͿ
Computing Option • App Engine Standard Environment (GAE SE) •
App Engine Flexible Environment (GAE FE) • Container Engine (GKE) • Compute Engine (GCE) GCP ͷίϯϐϡʔτ Φϓγϣϯ ΨΠυΛެ։ https://cloudplatform-jp.googleblog.com/2016/06/gcp_17.html
GAE/Go SEΛ͏ • ғ͍ࠐΈͷϦεΫ͋Δ͚Ͳ... • ӡ༻ʹखؒΛ͔͚ͨ͘ͳ͔ͬͨ • ແྉ͕͋Δͷخ͍͠ • RDB͑ͳͯ͘ࠔΒͳ͍
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
Ϣʔβೝূ(1) • Users API (google.golang.org/appengine/user) • ͱͯ؆୯ʹ͑Δ • Current(); CurrentOAuth();
LoginURL() • GoogleΞΧϯτ͔͠ରԠ͍ͯ͠ͳ͍ • GitHubΞΧϯτͰϩάΠϯ͍ͨ͠... • ࣗલ࣮ • ໘͍͘͞
Ϣʔβೝূ(2) • Cloud Identity-Aware Proxy (IAP) • Cloud Endpoints
Cloud Identity-Aware Proxy • ೝূ͞ΕͨϢʔβʹΞϓϦΛެ։͢Δ • GoogleΞΧϯτલఏͷΑ͏ͳͷͰະ༻
Cloud Endpoints • REST APIΛ࡞ΔͨΊͷαʔϏε • ϩΪϯάɺϞχλϦϯάɺอޢͳͲߦ͏ • GAE SE:
Cloud Endpoints Framework • ͦΕҎ֎: Extensible Service Proxy Extensible Service Proxy ͱ Endpoints Frameworks ͷൺֱ https://cloud.google.com/endpoints/docs/frameworks-extensible-service-proxy?hl=ja
Cloud Endpoints • Cloud Endpoints Framework github.com/GoogleCloudPlatform/go-endpoints/endpoints • ඪ४ͰIssuer =
accounts.google.com • AuthenticatorΛ࣮͢Ε৭ʑΕͦ͏
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
go-endpointsΛ͏ • app.yamlʹhandlerΛՃ application: [app-name] version: 1 runtime: go api_version:
go1 handlers: - url: /_ah/spi/.* script: _go_app
Go 1.8Λ͏ • api_versionΛมߋ͢Δ(1.9.55Ҏ্) application: [app-name] version: 1 runtime: go
api_version: go1.8 handlers: - url: /_ah/spi/.* script: _go_app Google App Engine for Go͕Go1.8ʹରԠͨ͠ͷͰࢼͯ͠Έͨ http://qiita.com/tenntenn/items/0b92fc089f8826fabaf1
go-endpointsͰAPI࣮ • APIͱͯ͠ѻ͏ϝιουಛఆͷϧʔϧΛຬͨ͢ • JSONͷมউखʹͬͯ͘ΕΔ type Service struct{} type ListOptions
struct { Limit int `json:"limit" endpoints:"d=10"` } type ArticleList struct { Items []*Article `json:"items"` } type Article struct { URL string `json:"url"` } func (s *Service) ArticleList(c context.Context, r *ListOptions) (*ArticleList, error) { return &ArticleList{ Items: []*Article{ &Article{URL: "http://example.com/"}, }, }, nil }
go-endpointsͷURL Router • ࡞ͬͨAPIΛURLʹϚοϓ͢Δ func init() { srv := new(Service)
api, err := endpoints.RegisterService(srv, "logs", "v1", "Reading Log API", true) if err != nil { log.Fatal(err) } m := api.MethodByName("ArticleList") if m == nil { log.Fatal("Missing ArticleList method") } info := m.Info() info.Name = "article.list" info.Path = "articles" info.HTTPMethod = "GET" info.Desc = "List most recent reading articles." endpoints.HandleHTTP() } APIαʔϏεొ URLͷઃఆ ͜ͷྫ https://[app-name].appspot.com/_ah/api/logs/v1/articles ͱͳΔ
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
go-endpointsͰೝূ • infoʹɺೝূ͢ΔͨΊʹඞཁͳΛઃఆ͢Δ • API Manager / ೝূใ / ΫϥΠΞϯτIDͷ࡞
ͰWeb Application Λબ • ੜ͞ΕͨΫϥΠΞϯτIDͱγʔΫϨοτΛอଘ const ClientID = "xxxx" func init() { info := m.Info() info.Name = "article.list" info.Path = "articles" info.HTTPMethod = "GET" info.Desc = "List most recent reading articles." info.Scopes = []string{endpoints.EmailScope} info.Audiences = []string{"[app-name].appspot.com"} info.ClientIds = []string{ClientID, endpoints.APIExplorerClientID} endpoints.HandleHTTP() }
go-endpointsͰೝূ • infoͱಉ͡ΛͬͯCurrentUser()Λݺͼग़͢ func (s *Service) ArticleList(c context.Context, r *ListOptions)
(*ArticleList, error) { scopes := []string{endpoints.EmailScope} audiences := []string{"[app-name].appspot.com"} clientIDs := []string{ClientID, endpoints.APIExplorerClientID} user, err := endpoints.CurrentUser(c, scopes, audiences, clientIDs) if err != nil { return nil, err } return &ArticleList{ Items: []*Article{ &Article{URL: "http://example.com/"}, }, }, nil }
API ExplorerͰςετ • https://[app-name].appspot.com/_ah/api/explorer • ೝূ͕ඞཁͱѻΘΕΔΑ͏ʹͳ͍ͬͯΔ
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
DatastoreΛ͏ • NoSQLܕσʔλϕʔε • Keyͷઃܭ͕͍͠ • Cloud StorageSQLܕDB͋Δ͚Ͳ...
Datastoreอଘ • ϢʔβIDͱهࣄͷURLͰ֊ʹ͍ͯ͠Δ type Page struct { URL string LastMod
time.Time } func AddEntry(c context.Context, u *User, url string) error { p := datastore.NewKey(c, "User", u.ID, 0, nil) key := datastore.NewKey(c, "Page", url, 0, p) return datastore.RunInTransaction(c, func(c context.Context) error { var page Page err := datastore.Get(c, key, &page) if err != nil && err != datastore.ErrNoSuchEntity { return err } page.URL = url page.LastMod = time.Now() _, err = datastore.Put(c, key, &page) return err }, nil) }
Datastoreอଘ • datastore.RunInTransaction()Ͱߋ৽͢Δ type Page struct { URL string LastMod
time.Time } func AddEntry(c context.Context, u *User, url string) error { p := datastore.NewKey(c, "User", u.ID, 0, nil) key := datastore.NewKey(c, "Page", url, 0, p) return datastore.RunInTransaction(c, func(c context.Context) error { var page Page err := datastore.Get(c, key, &page) if err != nil && err != datastore.ErrNoSuchEntity { return err } page.URL = url page.LastMod = time.Now() _, err = datastore.Put(c, key, &page) return err }, nil) } ࣦഊͷ߹ϦτϥΠ͞ΕΔͷͰɺ ࠷৽ͷΛಡΈ͔ͯ͠Βߋ৽
هࣄ༰ͷऔಘ • Goඪ४ͷhttp.Get()੍ݶ͞Ε͍ͯΔ • ΘΓʹurlfetch.ClientΛ͏ • API࣮ߦͷཪͰऔಘΛ͍ͨ͠ • GAE/Go SEͷϦΫΤετ60ඵͰλΠϜΞτ
• Task QueueΛ͏ • Cloud Pub/SubSubscribe͢Δଆ͕ෳͷ߹ʹศརͦ͏
Task Queue • Push QueueͱPull Queueͷ2छྨ͋Δ • GAE/Go SEPush Queue
• ಛఆͷΤϯυϙΠϯτʹϦΫΤετ͞ΕΔ • GAE͔ΒͷΈ࣮ߦ͍ͤͨ͞
Task Queue༻URLՃ • Task Queue͕ϦΫΤετ͢ΔURLΛՃ application: [app-name] version: 1 runtime:
go api_version: go1.8 handlers: - url: /_ah/spi/.* script: _go_app - url: /_ah/queue/.* script: _go_app login: admin ཧऀͷΈΞΫηεՄೳ
Ωϡʔͷઃఆ • queue.yamlΛ࡞ͯ͠ΩϡʔΛઃఆ • ৭ʑͰ͖ΔͷͰެࣜυΩϡϝϯτΛಡ͏ queue: - name: snap rate:
1/s bucket_size: 5
Task Queue࣮ • ΩϡʔʹೖΕ࣮ͯߦɺΤϥʔͳΒϦτϥΠ func AddTask(c context.Context, url string) error
{ v := url.Values{} v.Set("url", url) t := taskqueue.NewPOSTTask("/_ah/queue/snap", v) _, err := taskqueue.Add(c, t, "snap") return err } func SnapQueueHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) url := r.FormValue("url") cli := urlfetch.Client(c) resp, err := cli.Get(uri) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } ... }
Task QueueϋϯυϥՃ • go-endpoints͕endpoints.HandleHTTP()͍ͯ͠Δ͕ɺ ී௨ʹͦͷ··Goඪ४net/httpͰՃ͢Ε͍͍ const ClientID = "xxxx" func
init() { info := m.Info() info.Name = "article.list" info.Path = "articles" info.HTTPMethod = "GET" info.Desc = "List most recent reading articles." info.Scopes = []string{endpoints.EmailScope} info.Audiences = []string{"[app-name].appspot.com"} info.ClientIds = []string{ClientID, endpoints.APIExplorerClientID} http.HandleFunc("/_ah/queue/snap", SnapQueueHandler) endpoints.HandleHTTP() }
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
Χʔιϧͷར༻ • DatastoreҙͷϖʔδʹҠಈ͢Δͷ͔ͳ͍ ༷͔Β֎ͨ͠΄͏͕͍͍ • Query.Offset()ಡΈඈ͕͢ಡΈࠐΈ͍ͯ͠Δ ಡΈࠐΈճͰ՝ۚ͞ΕΔ... • ΧʔιϧΛͬͯલޙͷΛऔಘ͢Δͱ͍͍ •
ͨͩ͠Χʔιϧ͕࡞ΒΕͨΫΤϦͱಉ͕݅͡ඞཁ
Χʔιϧͷ͍ํ func FetchEntries(c context.Context, u *User, cursor string) ([]*Page, string,
error) { key := datastore.NewKey(c, "User", u.ID, 0, nil) q := datastore.NewQuery("Page").Ancestor(key).Limit(20) if cursor != "" { p, err := datastore.DecodeCursor(cursor) if err != nil { return nil, "", err } q = q.Start(p) } var a []*Page t := q.Run(c) for { var page Page _, err := t.Next(&page) ... লུ ... } p, err := t.Cursor() if err != nil { return nil, "", err } return a, p.String(), nil } ಉͳΫΤϦͰऔಘͨ͠Χʔιϧ
࡞ΔαʔϏεͷ༷ • Ϣʔβຖʹ͚ͯཧ͢Δ • APIͱ࣮ͯ͢͠Δ • ಡΜͩهࣄΛऔࣺબͤͣશ෦อଘ͢Δ • ࣌ܥྻʹಡΜͩϦετΛऔಘ͢Δ •
อଘͨ͠هࣄ͔Βશจݕࡧ͢Δ
Search API • DatastoreΩʔϫʔυݕࡧ͕ۤख • Search APIͰ༻͢Δ • ͨͩ͠DatastoreͱผͷཧʹͳΔ
• ΠϯσοΫεʹɺෳͷυΩϡϝϯτΛొ Search APIͷ͍ํ import "google.golang.org/appengine/search" type Document struct {
Content search.HTML LastMod time.Time } func AddContent(c context.Context, uid string, body []byte) error { doc := Document{ Content: search.HTML(body), LastMod: time.Now(), } x, err := search.Open("article_" + uid) if err != nil { return err } if _, err := x.Put(c, uri, &doc); err != nil { return err } return nil } υΩϡϝϯτΛఆٛ ΠϯσοΫεՃ
ݕࡧͯ͠ΈΔ Query String https://cloud.google.com/appengine/docs/standard/go/search/query_strings
• ΠϯσοΫε͔ΒΩʔϫʔυݕࡧ...ͷ͕ͣ... Search APIͷ͍ํ func Query(c context.Context, uid, expr string,
cursor Cursor) ([]*Document, string, error) { x, err := search.Open("article_" + uid) if err != nil { return nil, "", err } var a []*Document if cursor != "" { options.Cursor = search.Cursor(cursor) } t := x.Search(c, expr, nil) for { var doc Document _, err := t.Next(&doc) if err == search.Done { return a, string(t.Cursor()), nil } if err != nil { return nil, "", err } a = append(a, &doc) } }
·ͱΊ • ϚωʔδυαʔϏε͓खܰ • झຯͰॻ͘ίʔυͳΒ໎Θͣ͏ • ϕϯμʔϩοΫΠϯؾΛ͚ͭͯ...
͋Γ͕ͱ͏͍͟͝·ͨ͠