[{"data":1,"prerenderedAt":421},["ShallowReactive",2],{"navigation":3,"\u002Fprojects\u002Fgshop-architecture":34},[4],{"title":5,"path":6,"stem":7,"children":8,"page":33},"Articles","\u002Farticles","articles",[9,13,17,21,25,29],{"title":10,"path":11,"stem":12},"用 Daily Snapshot 提升統計查詢速度","\u002Farticles\u002Fdaily-snapshot","articles\u002Fdaily-snapshot",{"title":14,"path":15,"stem":16},"GKE 部署","\u002Farticles\u002Fgke-deployment","articles\u002Fgke-deployment",{"title":18,"path":19,"stem":20},"從 LLM 到 Agent：打通底層邏輯","\u002Farticles\u002Fllm-to-agent","articles\u002Fllm-to-agent",{"title":22,"path":23,"stem":24},"資訊安全實踐","\u002Farticles\u002Fsecurity-best-practices","articles\u002Fsecurity-best-practices",{"title":26,"path":27,"stem":28},"單機架構的性能優化","\u002Farticles\u002Fsingle-machine-performance","articles\u002Fsingle-machine-performance",{"title":30,"path":31,"stem":32},"伺服器渲染 SSR","\u002Farticles\u002Fssr","articles\u002Fssr",false,{"id":35,"title":36,"author":37,"body":41,"date":402,"demoNote":403,"demoUrl":403,"description":404,"extension":405,"image":406,"meta":407,"navigation":408,"path":409,"seo":410,"stem":411,"tags":412,"url":419,"__hash__":420},"projectPages\u002Fprojects\u002Fgshop-architecture.md","GShop 系統架構",{"name":38,"avatar":39},"Gary",{"src":40,"alt":38},"\u002Fimages\u002Fselfie.webp",{"type":42,"value":43,"toc":387},"minimark",[44,52,56,59,115,118,121,125,133,143,150,152,156,159,165,171,184,193,195,199,210,215,221,224,228,231,278,281,285,288,324,327,329,332,336,343,349,359,363,369,371,374,377,383],[45,46,47],"p",{},[48,49],"img",{"alt":50,"src":51},"GShop 系統架構圖","\u002Fimages\u002Fstructure.png",[53,54,55],"h2",{"id":55},"系統概覽",[45,57,58],{},"GShop 是一套電商系統，由三個獨立服務組成，統一部署在 GKE Autopilot 上：",[60,61,62,78],"table",{},[63,64,65],"thead",{},[66,67,68,72,75],"tr",{},[69,70,71],"th",{},"服務",[69,73,74],{},"角色",[69,76,77],{},"Port",[79,80,81,93,104],"tbody",{},[66,82,83,87,90],{},[84,85,86],"td",{},"gshop-api",[84,88,89],{},"Node.js REST API",[84,91,92],{},"3001",[66,94,95,98,101],{},[84,96,97],{},"gshop-dashboard",[84,99,100],{},"Nuxt.js 管理後台",[84,102,103],{},"3002",[66,105,106,109,112],{},[84,107,108],{},"gshop-web",[84,110,111],{},"Nuxt.js 前台購物網站",[84,113,114],{},"3003",[45,116,117],{},"三個服務共用同一個 GKE Cluster、同一個 Cloud Load Balancer IP，由 Ingress 依 Host header 分流。",[119,120],"hr",{},[53,122,124],{"id":123},"cicd-pipeline","CI\u002FCD Pipeline",[45,126,127,128,132],{},"三個 repo 分別位於 GitHub Org ",[129,130,131],"code",{},"very-cool-gshop","，各自有獨立的 GitHub Actions workflow。",[134,135,140],"pre",{"className":136,"code":138,"language":139},[137],"language-text","push main → GitHub Actions\n              ↓\n         docker build (ubuntu-latest \u002F amd64)\n              ↓\n         push → Artifact Registry\n              ↓\n         kubectl rollout restart\n","text",[129,141,138],{"__ignoreMap":142},"",[45,144,145,146,149],{},"GitHub Actions runner 是 ",[129,147,148],{},"ubuntu-latest","，天生就是 amd64，build 出的 image 直接相容 GKE，不需要 Cloud Build。",[119,151],{},[53,153,155],{"id":154},"user-edge-層","User & Edge 層",[45,157,158],{},"使用者請求先經過 Cloudflare，再進入 GCP：",[134,160,163],{"className":161,"code":162,"language":139},[137],"User Browser\n     ↓ HTTPS\n  Cloudflare\n  DNS + Proxy（Full Strict）\n     ↓ HTTPS\nGCP Cloud Load Balancer\n",[129,164,162],{"__ignoreMap":142},[45,166,167],{},[168,169,170],"strong",{},"Cloudflare 的作用：",[172,173,174,178,181],"ul",{},[175,176,177],"li",{},"DNS 解析 — 對外暴露 Cloudflare IP，不直接暴露 GCP 的真實 IP",[175,179,180],{},"Proxy — 所有流量過 Cloudflare 節點，提供 DDoS 防護與快取",[175,182,183],{},"TLS 終止 — 瀏覽器到 Cloudflare 這段的 HTTPS 由 Cloudflare 處理",[45,185,186,189,192],{},[168,187,188],{},"Full (Strict) 模式：",[190,191],"br",{},"\nCloudflare 到 GCP 這段也必須有合法憑證。使用 Cloudflare Origin Certificate，存成 K8s TLS Secret，由 Ingress 掛載使用。",[119,194],{},[53,196,198],{"id":197},"gke-autopilot-叢集","GKE Autopilot 叢集",[45,200,201,202,205,206,209],{},"叢集名稱 ",[129,203,204],{},"gshop-cluster","，部署在 ",[129,207,208],{},"asia-east1","。",[211,212,214],"h3",{"id":213},"ingress-host-based-路由","Ingress — host-based 路由",[134,216,219],{"className":217,"code":218,"language":139},[137],"api.garydemo.com      →  gshop-api:3001\ndashboard.garydemo.com →  gshop-dashboard:3002\nweb.garydemo.com      →  gshop-web:3003\n",[129,220,218],{"__ignoreMap":142},[45,222,223],{},"Cloud Load Balancer（IP：34.160.168.110）接收所有流量，Ingress Controller 根據 Host header 決定轉發目標。",[211,225,227],{"id":226},"tls-secret","TLS Secret",[45,229,230],{},"Cloudflare Origin Certificate 存成 K8s TLS Secret：",[134,232,236],{"className":233,"code":234,"language":235,"meta":142,"style":142},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","kubectl create secret tls cloudflare-origin-cert \\\n  --cert=certificate.pem \\\n  --key=private.key\n","bash",[129,237,238,264,272],{"__ignoreMap":142},[239,240,243,247,251,254,257,260],"span",{"class":241,"line":242},"line",1,[239,244,246],{"class":245},"sBMFI","kubectl",[239,248,250],{"class":249},"sfazB"," create",[239,252,253],{"class":249}," secret",[239,255,256],{"class":249}," tls",[239,258,259],{"class":249}," cloudflare-origin-cert",[239,261,263],{"class":262},"sTEyZ"," \\\n",[239,265,267,270],{"class":241,"line":266},2,[239,268,269],{"class":249},"  --cert=certificate.pem",[239,271,263],{"class":262},[239,273,275],{"class":241,"line":274},3,[239,276,277],{"class":249},"  --key=private.key\n",[45,279,280],{},"Ingress 掛載這個 Secret，讓 Cloudflare → GCP 這段走 HTTPS。",[211,282,284],{"id":283},"db-secret","DB Secret",[45,286,287],{},"資料庫連線字串存成 K8s Generic Secret：",[134,289,291],{"className":233,"code":290,"language":235,"meta":142,"style":142},"kubectl create secret generic gshop-api-secret \\\n  --from-literal=DATABASE_URL=\"postgresql:\u002F\u002F...\"\n",[129,292,293,309],{"__ignoreMap":142},[239,294,295,297,299,301,304,307],{"class":241,"line":242},[239,296,246],{"class":245},[239,298,250],{"class":249},[239,300,253],{"class":249},[239,302,303],{"class":249}," generic",[239,305,306],{"class":249}," gshop-api-secret",[239,308,263],{"class":262},[239,310,311,314,318,321],{"class":241,"line":266},[239,312,313],{"class":249},"  --from-literal=DATABASE_URL=",[239,315,317],{"class":316},"sMK4o","\"",[239,319,320],{"class":249},"postgresql:\u002F\u002F...",[239,322,323],{"class":316},"\"\n",[45,325,326],{},"gshop-api Pod 透過環境變數讀取，不會出現在 image 或 code 裡。",[119,328],{},[53,330,331],{"id":331},"外部服務",[211,333,335],{"id":334},"supabase-postgresql","Supabase PostgreSQL",[45,337,338,339,342],{},"gshop-api 透過 ",[168,340,341],{},"Session Pooler"," 連線，走 IPv4：",[134,344,347],{"className":345,"code":346,"language":139},[137],"aws-1-ap-southeast-1.pooler.supabase.com:5432\n",[129,348,346],{"__ignoreMap":142},[350,351,352],"blockquote",{},[45,353,354,355,358],{},"GKE Autopilot 節點只有 IPv4，Supabase 直連（",[129,356,357],{},"db.xxx.supabase.co","）是 IPv6 only，直接連會 ENOTFOUND。",[211,360,362],{"id":361},"google-cloud-storage","Google Cloud Storage",[45,364,365,366,209],{},"gshop-api 負責商品圖片的上傳與讀取，存放在 GCS Bucket ",[129,367,368],{},"gshop-files",[119,370],{},[53,372,373],{"id":373},"部署架構圖",[45,375,376],{},"整體流量與部署流向：",[134,378,381],{"className":379,"code":380,"language":139},[137],"GitHub push main\n       ↓\n  GitHub Actions\n  docker build → Artifact Registry\n       ↓\n  kubectl rollout restart\n       ↓\n  GKE Pod 拉新 image 滾動更新\n\nUser Browser → Cloudflare → Cloud LB (34.160.168.110)\n                                    ↓\n                             Ingress（host routing）\n                            ↙        ↓        ↘\n                      gshop-api  gshop-dashboard  gshop-web\n                           ↓             ↓\n                       Supabase       GCS Bucket\n                      PostgreSQL\n",[129,382,380],{"__ignoreMap":142},[384,385,386],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":142,"searchDepth":266,"depth":266,"links":388},[389,390,391,392,397,401],{"id":55,"depth":266,"text":55},{"id":123,"depth":266,"text":124},{"id":154,"depth":266,"text":155},{"id":197,"depth":266,"text":198,"children":393},[394,395,396],{"id":213,"depth":274,"text":214},{"id":226,"depth":274,"text":227},{"id":283,"depth":274,"text":284},{"id":331,"depth":266,"text":331,"children":398},[399,400],{"id":334,"depth":274,"text":335},{"id":361,"depth":274,"text":362},{"id":373,"depth":266,"text":373},"2026-06-20",null,"三服務電商系統的完整部署藍圖，從 CI\u002FCD 到 GKE 叢集、Cloudflare Proxy 到 Supabase 的全端架構設計。","md","\u002Fimages\u002Farchitecture.jpg",{},true,"\u002Fprojects\u002Fgshop-architecture",{"title":36,"description":404},"projects\u002Fgshop-architecture",[413,414,415,416,417,418],"GKE","Kubernetes","Cloudflare","CI\u002FCD","Supabase","GCS","https:\u002F\u002Fgithub.com\u002Forgs\u002Fvery-cool-gshop\u002Frepositories","y4KXw9ivzhVpoxYNezynOrpqVnB3byV5xZlc2eruhsQ",1782321733435]