[{"data":1,"prerenderedAt":493},["ShallowReactive",2],{"navigation":3,"\u002Fprojects\u002Fgshop-web":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":476,"demoUrl":477,"description":478,"extension":479,"image":480,"meta":481,"navigation":399,"path":482,"seo":483,"stem":484,"tags":485,"url":491,"__hash__":492},"projectPages\u002Fprojects\u002Fgshop-web.md","gshop-web",{"name":38,"avatar":39},"Gary",{"src":40,"alt":38},"\u002Fimages\u002Fselfie.webp",{"type":42,"value":43,"toc":466},"minimark",[44,48,52,87,90,101,104,110,113,118,128,137,146,149,153,158,163,168,177,224,237,292,294,298,306,315,320,349,353,356,462],[45,46,47],"h2",{"id":47},"架構概覽",[49,50,51],"p",{},"gshop-web 是面向消費者的電商購物前台，提供商品瀏覽、加入購物車、結帳等功能，使用 Nuxt.js SSR 確保 SEO 效果與首屏載入速度。",[53,54,55,63,69,75,81],"ul",{},[56,57,58,62],"li",{},[59,60,61],"strong",{},"Framework","：Nuxt.js（SSR 模式）",[56,64,65,68],{},[59,66,67],{},"Port","：3003",[56,70,71,74],{},[59,72,73],{},"部署環境","：GKE Autopilot，asia-east1",[56,76,77,80],{},[59,78,79],{},"網域","：web.garydemo.com",[56,82,83,86],{},[59,84,85],{},"CDN \u002F Proxy","：Cloudflare（Full Strict TLS）",[45,88,89],{"id":89},"部署架構",[91,92,97],"pre",{"className":93,"code":95,"language":96},[94],"language-text","GitHub (main) → GitHub Actions → docker build (amd64) → Artifact Registry\n                                                               ↓\n                                                       GKE pull image\n                                                               ↓\n                                                  kubectl rollout restart\n","text",[98,99,95],"code",{"__ignoreMap":100},"",[49,102,103],{},"流量路徑：",[91,105,108],{"className":106,"code":107,"language":96},[94],"使用者瀏覽器\n    ↓ HTTPS\nCloudflare Proxy（DNS + TLS 終止）\n    ↓ HTTPS + Origin Certificate\nGCP Load Balancer（34.160.168.110）\n    ↓\nIngress（Host: web.garydemo.com）\n    ↓\ngshop-web Pod（:3003）\n",[98,109,107],{"__ignoreMap":100},[45,111,112],{"id":112},"遇到的問題與解法",[114,115,117],"h3",{"id":116},"_1-本地-build-的-image-在-gke-無法執行","1. 本地 Build 的 Image 在 GKE 無法執行",[49,119,120,123,124,127],{},[59,121,122],{},"問題","：本地 ",[98,125,126],{},"docker build"," 後推上去，GKE Pod 啟動失敗。",[49,129,130,133,134,136],{},[59,131,132],{},"原因","：開發機是 Apple Silicon（arm64），",[98,135,126],{}," 預設產出 arm64 image，但 GKE 節點是 amd64 架構，無法執行。",[49,138,139,142,143,145],{},[59,140,141],{},"解法","：CI\u002FCD 改用 GitHub Actions ubuntu runner 執行 ",[98,144,126],{},"，runner 本身是 amd64，build 出的 image 天生就是 amd64，不需要額外設定或使用 Cloud Build。",[147,148],"hr",{},[114,150,152],{"id":151},"_2-cloudflare-full-strict-tls-設定","2. Cloudflare Full (Strict) TLS 設定",[49,154,155,157],{},[59,156,122],{},"：設定 Cloudflare SSL\u002FTLS 為 Full (Strict) 後，網站無法正常載入。",[49,159,160,162],{},[59,161,132],{},"：Full (Strict) 要求 Cloudflare 到 GCP 這段也必須使用合法憑證，自簽憑證不被接受。",[49,164,165,167],{},[59,166,141],{},"：",[169,170,171,174],"ol",{},[56,172,173],{},"在 Cloudflare Dashboard → SSL\u002FTLS → Origin Server 建立 Origin Certificate 並下載",[56,175,176],{},"存為 Kubernetes TLS Secret：",[91,178,182],{"className":179,"code":180,"language":181,"meta":100,"style":100},"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",[98,183,184,210,218],{"__ignoreMap":100},[185,186,189,193,197,200,203,206],"span",{"class":187,"line":188},"line",1,[185,190,192],{"class":191},"sBMFI","kubectl",[185,194,196],{"class":195},"sfazB"," create",[185,198,199],{"class":195}," secret",[185,201,202],{"class":195}," tls",[185,204,205],{"class":195}," cloudflare-origin-cert",[185,207,209],{"class":208},"sTEyZ"," \\\n",[185,211,213,216],{"class":187,"line":212},2,[185,214,215],{"class":195},"  --cert=certificate.pem",[185,217,209],{"class":208},[185,219,221],{"class":187,"line":220},3,[185,222,223],{"class":195},"  --key=private.key\n",[169,225,226],{"start":220},[56,227,228,229,232,233,236],{},"在 ",[98,230,231],{},"ingress.yaml"," 的 ",[98,234,235],{},"tls"," 區塊掛載：",[91,238,242],{"className":239,"code":240,"language":241,"meta":100,"style":100},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","spec:\n  tls:\n    - secretName: cloudflare-origin-cert\n      hosts:\n        - web.garydemo.com\n","yaml",[98,243,244,254,261,275,283],{"__ignoreMap":100},[185,245,246,250],{"class":187,"line":188},[185,247,249],{"class":248},"swJcz","spec",[185,251,253],{"class":252},"sMK4o",":\n",[185,255,256,259],{"class":187,"line":212},[185,257,258],{"class":248},"  tls",[185,260,253],{"class":252},[185,262,263,266,269,272],{"class":187,"line":220},[185,264,265],{"class":252},"    -",[185,267,268],{"class":248}," secretName",[185,270,271],{"class":252},":",[185,273,274],{"class":195}," cloudflare-origin-cert\n",[185,276,278,281],{"class":187,"line":277},4,[185,279,280],{"class":248},"      hosts",[185,282,253],{"class":252},[185,284,286,289],{"class":187,"line":285},5,[185,287,288],{"class":252},"        -",[185,290,291],{"class":195}," web.garydemo.com\n",[147,293],{},[114,295,297],{"id":296},"_3-ingress-class-設定錯誤","3. Ingress Class 設定錯誤",[49,299,300,302,303,305],{},[59,301,122],{},"：套用 ",[98,304,231],{}," 後，GKE 沒有建立對應的 Cloud Load Balancer。",[49,307,308,310,311,314],{},[59,309,132],{},"：使用了 ",[98,312,313],{},"spec.ingressClassName: gce","，GKE Ingress Controller 不認識這個欄位。",[49,316,317,319],{},[59,318,141],{},"：改用 annotation 方式指定：",[91,321,323],{"className":239,"code":322,"language":241,"meta":100,"style":100},"metadata:\n  annotations:\n    kubernetes.io\u002Fingress.class: gce\n",[98,324,325,332,339],{"__ignoreMap":100},[185,326,327,330],{"class":187,"line":188},[185,328,329],{"class":248},"metadata",[185,331,253],{"class":252},[185,333,334,337],{"class":187,"line":212},[185,335,336],{"class":248},"  annotations",[185,338,253],{"class":252},[185,340,341,344,346],{"class":187,"line":220},[185,342,343],{"class":248},"    kubernetes.io\u002Fingress.class",[185,345,271],{"class":252},[185,347,348],{"class":195}," gce\n",[45,350,352],{"id":351},"cicd-設定","CI\u002FCD 設定",[49,354,355],{},"每次 push 到 main，GitHub Actions 自動執行：",[91,357,359],{"className":239,"code":358,"language":241,"meta":100,"style":100},"- name: Build and push image\n  run: |\n    docker build -t asia-east1-docker.pkg.dev\u002Fgshop-497319\u002Fgshop\u002Fweb:latest .\n    docker push asia-east1-docker.pkg.dev\u002Fgshop-497319\u002Fgshop\u002Fweb:latest\n\n- uses: google-github-actions\u002Fget-gke-credentials@v2\n  with:\n    cluster_name: gshop-cluster\n    location: asia-east1\n\n- run: kubectl rollout restart deployment\u002Fgshop-web\n",[98,360,361,374,385,390,395,401,414,422,433,444,449],{"__ignoreMap":100},[185,362,363,366,369,371],{"class":187,"line":188},[185,364,365],{"class":252},"-",[185,367,368],{"class":248}," name",[185,370,271],{"class":252},[185,372,373],{"class":195}," Build and push image\n",[185,375,376,379,381],{"class":187,"line":212},[185,377,378],{"class":248},"  run",[185,380,271],{"class":252},[185,382,384],{"class":383},"s7zQu"," |\n",[185,386,387],{"class":187,"line":220},[185,388,389],{"class":195},"    docker build -t asia-east1-docker.pkg.dev\u002Fgshop-497319\u002Fgshop\u002Fweb:latest .\n",[185,391,392],{"class":187,"line":277},[185,393,394],{"class":195},"    docker push asia-east1-docker.pkg.dev\u002Fgshop-497319\u002Fgshop\u002Fweb:latest\n",[185,396,397],{"class":187,"line":285},[185,398,400],{"emptyLinePlaceholder":399},true,"\n",[185,402,404,406,409,411],{"class":187,"line":403},6,[185,405,365],{"class":252},[185,407,408],{"class":248}," uses",[185,410,271],{"class":252},[185,412,413],{"class":195}," google-github-actions\u002Fget-gke-credentials@v2\n",[185,415,417,420],{"class":187,"line":416},7,[185,418,419],{"class":248},"  with",[185,421,253],{"class":252},[185,423,425,428,430],{"class":187,"line":424},8,[185,426,427],{"class":248},"    cluster_name",[185,429,271],{"class":252},[185,431,432],{"class":195}," gshop-cluster\n",[185,434,436,439,441],{"class":187,"line":435},9,[185,437,438],{"class":248},"    location",[185,440,271],{"class":252},[185,442,443],{"class":195}," asia-east1\n",[185,445,447],{"class":187,"line":446},10,[185,448,400],{"emptyLinePlaceholder":399},[185,450,452,454,457,459],{"class":187,"line":451},11,[185,453,365],{"class":252},[185,455,456],{"class":248}," run",[185,458,271],{"class":252},[185,460,461],{"class":195}," kubectl rollout restart deployment\u002Fgshop-web\n",[463,464,465],"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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":100,"searchDepth":212,"depth":212,"links":467},[468,469,470,475],{"id":47,"depth":212,"text":47},{"id":89,"depth":212,"text":89},{"id":112,"depth":212,"text":112,"children":471},[472,473,474],{"id":116,"depth":220,"text":117},{"id":151,"depth":220,"text":152},{"id":296,"depth":220,"text":297},{"id":351,"depth":212,"text":352},"2026-06-15","https:\u002F\u002Fweb.garydemo.com\u002F","使用 Nuxt.js SSR 建構電商購物前台，部署於 GKE Autopilot，透過 Cloudflare Proxy 以 Full (Strict) TLS 對外提供服務。","md","\u002Fprojects\u002Fweb.jpg",{},"\u002Fprojects\u002Fgshop-web",{"title":36,"description":478},"projects\u002Fgshop-web",[486,487,488,489,490],"Nuxt.js","SSR","GKE","Cloudflare","CI\u002FCD","https:\u002F\u002Fgithub.com\u002Fvery-cool-gshop\u002Fgshop-web","lrc9zCX2veeid0IpdMehNxWXLODrtE-gikhMEWvQY2Y",1781661891541]