[{"data":1,"prerenderedAt":1415},["ShallowReactive",2],{"article-\u002Farticles\u002Fk8s-core-objects":3},{"id":4,"title":5,"body":6,"date":1402,"description":360,"excerpt":1403,"extension":1404,"meta":1405,"navigation":1194,"path":1406,"seo":1407,"stem":1408,"tags":1409,"__hash__":1414},"articles\u002Farticles\u002Fk8s-core-objects.md","Kubernetes 入門：Pod、Deployment、Service、Ingress 是怎麼把一個請求送到容器的",{"type":7,"value":8,"toc":1388},"minimark",[9,14,18,34,46,49,56,59,87,90,94,104,110,128,139,142,146,151,181,184,188,199,222,229,233,243,269,272,335,344,348,353,363,370,373,377,380,394,413,417,434,449,453,456,1166,1169,1292,1302,1305,1378,1381,1384],[10,11,13],"h2",{"id":12},"kubernetes-在解決什麼問題","Kubernetes 在解決什麼問題",[15,16,17],"p",{},"用 Docker 把服務包成容器之後，接下來的問題是：",[19,20,21,25,28,31],"ul",{},[22,23,24],"li",{},"要跑 3 份，其中一份掛了，誰負責把它重新開起來？",[22,26,27],{},"3 份容器的 IP 都不一樣、重開還會變，前面的人要連哪一個？",[22,29,30],{},"要更新版本時，怎麼一份一份換，而不是全部停掉再開？",[22,32,33],{},"設定檔、密碼要怎麼給容器，又不要寫死在映像檔裡？",[15,35,36,37,41,42,45],{},"Kubernetes（K8s）做的事情，就是讓你",[38,39,40],"strong",{},"用 YAML 描述「期望的狀態」","——「我要 3 份這個版本的 API，對外開 80 Port」——然後由它持續比對實際狀態，不一樣就自動修正。你不再是一步步下指令，而是",[38,43,44],{},"宣告結果","。",[10,47,48],{"id":48},"一個請求的路徑",[15,50,51],{},[52,53],"img",{"alt":54,"src":55},"Kubernetes 請求路徑示意圖：使用者的請求先到 Ingress Controller，依 Ingress 規則中的網域 api.example.com 與路徑 \u002Fhouses 轉給 house-api 這個 Service；Service 有固定的 ClusterIP 與 DNS 名稱，以 Label Selector app=house-api 找到三個 Pod 並做負載平衡；三個 Pod 由 Deployment 透過 ReplicaSet 維持數量，Deployment 指定映像檔版本與副本數；Pod 的設定來自 ConfigMap，密碼來自 Secret；全部位於 realty 這個 Namespace 中","\u002Fimages\u002Farticles\u002Fk8s-request-path.svg",[15,57,58],{},"從外到內，一個請求會經過：",[60,61,62,68,74,80],"ol",{},[22,63,64,67],{},[38,65,66],{},"Ingress","：依網域和路徑，決定要轉給哪個 Service",[22,69,70,73],{},[38,71,72],{},"Service","：一個固定的入口，把請求平均分給後面的 Pod",[22,75,76,79],{},[38,77,78],{},"Pod","：真正在跑容器的地方",[22,81,82,83,86],{},"而 Pod 的數量和版本，由 ",[38,84,85],{},"Deployment"," 管理",[15,88,89],{},"下面由內到外，一個一個看。",[10,91,93],{"id":92},"pod最小的部署單位","Pod：最小的部署單位",[15,95,96,98,99,103],{},[38,97,78],{}," 是 K8s 裡最小的單位，裡面放一個（偶爾多個）容器。同一個 Pod 裡的容器共用網路，可以用 ",[100,101,102],"code",{},"localhost"," 互相連線。",[15,105,106,107,45],{},"Pod 最重要的特性是：",[38,108,109],{},"它是用完即丟的",[19,111,112,119,125],{},[22,113,114,115,118],{},"Pod 掛了，K8s 不會「修好」它，而是",[38,116,117],{},"建立一個新的 Pod"," 取代",[22,120,121,122],{},"新的 Pod 會有",[38,123,124],{},"新的名稱和新的 IP",[22,126,127],{},"Pod 裡寫入的檔案，Pod 被刪掉就沒了",[15,129,130,131,134,135,138],{},"所以兩件事要記住：",[38,132,133],{},"不要依賴 Pod 的 IP","，",[38,136,137],{},"不要把資料存在 Pod 裡","。需要保存的資料，要放到資料庫、物件儲存，或是掛載 PersistentVolume。",[15,140,141],{},"實務上幾乎不會直接建立 Pod，而是透過 Deployment。",[10,143,145],{"id":144},"deployment-與-replicaset維持數量管理版本","Deployment 與 ReplicaSet：維持數量、管理版本",[15,147,148,150],{},[38,149,85],{}," 描述「我要哪個映像檔、跑幾份」：",[19,152,153,164,174],{},[22,154,155,156,159,160,163],{},"它會建立一個 ",[38,157,158],{},"ReplicaSet","，由 ReplicaSet 負責「",[38,161,162],{},"隨時維持 3 個 Pod","」。一個 Pod 被刪掉，ReplicaSet 馬上補一個",[22,165,166,167,170,171],{},"更新映像檔版本時，Deployment 會建立一個",[38,168,169],{},"新的 ReplicaSet","，逐步把新版本的 Pod 加上去、舊版本的減下來，這就是",[38,172,173],{},"滾動更新（Rolling Update）",[22,175,176,177,180],{},"新版本有問題，",[100,178,179],{},"kubectl rollout undo"," 就能退回上一個 ReplicaSet",[15,182,183],{},"一般只需要操作 Deployment，ReplicaSet 由它自動管理。",[10,185,187],{"id":186},"label-與-selector物件之間怎麼找到彼此","Label 與 Selector：物件之間怎麼找到彼此",[15,189,190,191,194,195,198],{},"K8s 的物件之間",[38,192,193],{},"不是用名稱互相指定","，而是用 ",[38,196,197],{},"Label","（標籤）：",[19,200,201,208,215],{},[22,202,203,204,207],{},"Pod 身上貼著 ",[100,205,206],{},"app: house-api"," 這個 Label",[22,209,210,211,214],{},"Deployment 用 ",[100,212,213],{},"selector.matchLabels: app: house-api"," 來認領「哪些 Pod 歸我管」",[22,216,217,218,221],{},"Service 也用 ",[100,219,220],{},"selector: app: house-api"," 決定「要把流量送給哪些 Pod」",[15,223,224,225,228],{},"這個設計很有彈性，但也是新手最常出錯的地方：",[38,226,227],{},"Service 的 Selector 和 Pod 的 Label 只要有一個字不一樣，Service 就找不到任何 Pod","，請求一律失敗，而且 YAML 套用時不會有任何錯誤。",[10,230,232],{"id":231},"service固定的入口","Service：固定的入口",[15,234,235,236,239,240,242],{},"Pod 的 IP 會變、數量會變，所以前面需要一個",[38,237,238],{},"固定的入口","，這就是 ",[38,241,72],{},"：",[19,244,245,260,266],{},[22,246,247,248,251,252,255,256,259],{},"有一個",[38,249,250],{},"固定的虛擬 IP（ClusterIP）","，和一個 ",[38,253,254],{},"DNS 名稱","，例如同一個 Namespace 裡直接用 ",[100,257,258],{},"http:\u002F\u002Fhouse-api"," 就能連到",[22,261,262,263],{},"依 Selector 找到所有符合的 Pod，",[38,264,265],{},"只把請求送給已經就緒（Ready）的 Pod",[22,267,268],{},"Pod 增加、減少、重建時，Service 自動更新後面的清單",[15,270,271],{},"Service 有幾種類型：",[273,274,275,291],"table",{},[276,277,278],"thead",{},[279,280,281,285,288],"tr",{},[282,283,284],"th",{},"類型",[282,286,287],{},"誰能連",[282,289,290],{},"用途",[292,293,294,309,322],"tbody",{},[279,295,296,303,306],{},[297,298,299,302],"td",{},[100,300,301],{},"ClusterIP","（預設）",[297,304,305],{},"只有叢集內部",[297,307,308],{},"服務之間互相呼叫，最常用",[279,310,311,316,319],{},[297,312,313],{},[100,314,315],{},"NodePort",[297,317,318],{},"叢集外，透過任一 Node 的 IP 加固定 Port（30000～32767）",[297,320,321],{},"測試、沒有負載平衡器的環境",[279,323,324,329,332],{},[297,325,326],{},[100,327,328],{},"LoadBalancer",[297,330,331],{},"叢集外，透過雲端建立的負載平衡器",[297,333,334],{},"在雲端直接對外開放單一服務",[15,336,337,338,340,341,343],{},"如果每個要對外的服務都用 ",[100,339,328],{},"，雲端上就會建立一堆負載平衡器，成本很高。所以通常內部都用 ",[100,342,301],{},"，對外統一經過 Ingress。",[10,345,347],{"id":346},"ingress依網域與路徑分流","Ingress：依網域與路徑分流",[15,349,350,352],{},[38,351,66],{}," 是 HTTP 層的路由規則：",[354,355,361],"pre",{"className":356,"code":358,"language":359,"meta":360},[357],"language-text","api.example.com\u002Fhouses   → house-api Service\napi.example.com\u002Fmembers  → member-api Service\nadmin.example.com        → admin-web Service\n","text","",[100,362,358],{"__ignoreMap":360},[15,364,365,366,369],{},"要注意，Ingress 只是「規則」，",[38,367,368],{},"真正處理流量的是 Ingress Controller","（例如 NGINX Ingress Controller），它要另外安裝在叢集裡。只寫了 Ingress、卻沒有安裝 Controller，規則不會有任何作用。",[15,371,372],{},"TLS 憑證也通常在 Ingress 這一層處理，後面的服務只需要處理 HTTP。",[10,374,376],{"id":375},"configmap-與-secret設定與密碼","ConfigMap 與 Secret：設定與密碼",[15,378,379],{},"設定不應該寫死在映像檔裡，否則每個環境都要建一個不同的映像檔：",[19,381,382,388],{},[22,383,384,387],{},[38,385,386],{},"ConfigMap","：一般設定，例如外部 API 的網址、功能開關",[22,389,390,393],{},[38,391,392],{},"Secret","：敏感資料，例如資料庫連線字串、API Key",[15,395,396,397,400,401,404,405,408,409,412],{},"兩者都可以用",[38,398,399],{},"環境變數","或",[38,402,403],{},"掛載成檔案","的方式給 Pod。ASP.NET Core 會自動讀取環境變數，而且 ",[100,406,407],{},"ConnectionStrings__Default"," 這種用兩個底線的名稱，會對應到設定裡的 ",[100,410,411],{},"ConnectionStrings:Default","，不用改程式。",[10,414,416],{"id":415},"namespace分隔環境與團隊","Namespace：分隔環境與團隊",[15,418,419,422,423,426,427,426,430,433],{},[38,420,421],{},"Namespace"," 把叢集切成多個邏輯區塊，例如 ",[100,424,425],{},"realty","、",[100,428,429],{},"member",[100,431,432],{},"monitoring","。不同 Namespace 可以有同名的 Service，也可以設定各自的資源配額與權限。",[15,435,436,437,440,441,444,445,448],{},"同一個 Namespace 裡用 ",[100,438,439],{},"house-api"," 就能連到 Service；跨 Namespace 要寫完整名稱 ",[100,442,443],{},"house-api.realty.svc.cluster.local","（或簡寫 ",[100,446,447],{},"house-api.realty","）。",[10,450,452],{"id":451},"使用情境部署一個-net-api","使用情境：部署一個 .NET API",[15,454,455],{},"把物件查詢 API 部署上去，需要的完整 YAML：",[354,457,461],{"className":458,"code":459,"language":460,"meta":360,"style":360},"language-yaml shiki shiki-themes github-dark","apiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: house-api\n  namespace: realty\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: house-api            # 認領帶有這個 Label 的 Pod\n  template:\n    metadata:\n      labels:\n        app: house-api          # Pod 的 Label，要和上面、和 Service 一致\n    spec:\n      containers:\n        - name: house-api\n          image: registry.example.com\u002Fhouse-api:1.3.0   # 不要用 latest\n          ports:\n            - containerPort: 8080\n          env:\n            - name: ASPNETCORE_URLS\n              value: http:\u002F\u002F+:8080\n            - name: ConnectionStrings__Default\n              valueFrom:\n                secretKeyRef:\n                  name: house-api-secret\n                  key: db-connection\n          envFrom:\n            - configMapRef:\n                name: house-api-config\n          resources:\n            requests:\n              cpu: 250m\n              memory: 256Mi\n            limits:\n              memory: 512Mi\n          readinessProbe:\n            httpGet:\n              path: \u002Fhealth\u002Fready\n              port: 8080\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: house-api\n  namespace: realty\nspec:\n  selector:\n    app: house-api\n  ports:\n    - port: 80                  # Service 對內的 Port\n      targetPort: 8080          # 轉到容器的 Port\n---\napiVersion: networking.k8s.io\u002Fv1\nkind: Ingress\nmetadata:\n  name: house-api\n  namespace: realty\nspec:\n  ingressClassName: nginx\n  rules:\n    - host: api.example.com\n      http:\n        paths:\n          - path: \u002Fhouses\n            pathType: Prefix\n            backend:\n              service:\n                name: house-api\n                port:\n                  number: 80\n","yaml",[100,462,463,480,491,500,511,522,530,542,550,558,572,580,588,596,609,617,625,638,652,660,674,682,694,705,717,725,733,744,755,763,773,784,792,800,811,822,830,840,848,856,867,877,884,894,904,911,920,929,936,943,953,961,978,992,997,1007,1017,1024,1033,1042,1049,1060,1068,1081,1089,1097,1111,1122,1130,1138,1147,1155],{"__ignoreMap":360},[464,465,468,472,476],"span",{"class":466,"line":467},"line",1,[464,469,471],{"class":470},"s4JwU","apiVersion",[464,473,475],{"class":474},"s95oV",": ",[464,477,479],{"class":478},"sU2Wk","apps\u002Fv1\n",[464,481,483,486,488],{"class":466,"line":482},2,[464,484,485],{"class":470},"kind",[464,487,475],{"class":474},[464,489,490],{"class":478},"Deployment\n",[464,492,494,497],{"class":466,"line":493},3,[464,495,496],{"class":470},"metadata",[464,498,499],{"class":474},":\n",[464,501,503,506,508],{"class":466,"line":502},4,[464,504,505],{"class":470},"  name",[464,507,475],{"class":474},[464,509,510],{"class":478},"house-api\n",[464,512,514,517,519],{"class":466,"line":513},5,[464,515,516],{"class":470},"  namespace",[464,518,475],{"class":474},[464,520,521],{"class":478},"realty\n",[464,523,525,528],{"class":466,"line":524},6,[464,526,527],{"class":470},"spec",[464,529,499],{"class":474},[464,531,533,536,538],{"class":466,"line":532},7,[464,534,535],{"class":470},"  replicas",[464,537,475],{"class":474},[464,539,541],{"class":540},"sDLfK","3\n",[464,543,545,548],{"class":466,"line":544},8,[464,546,547],{"class":470},"  selector",[464,549,499],{"class":474},[464,551,553,556],{"class":466,"line":552},9,[464,554,555],{"class":470},"    matchLabels",[464,557,499],{"class":474},[464,559,561,564,566,568],{"class":466,"line":560},10,[464,562,563],{"class":470},"      app",[464,565,475],{"class":474},[464,567,439],{"class":478},[464,569,571],{"class":570},"sAwPA","            # 認領帶有這個 Label 的 Pod\n",[464,573,575,578],{"class":466,"line":574},11,[464,576,577],{"class":470},"  template",[464,579,499],{"class":474},[464,581,583,586],{"class":466,"line":582},12,[464,584,585],{"class":470},"    metadata",[464,587,499],{"class":474},[464,589,591,594],{"class":466,"line":590},13,[464,592,593],{"class":470},"      labels",[464,595,499],{"class":474},[464,597,599,602,604,606],{"class":466,"line":598},14,[464,600,601],{"class":470},"        app",[464,603,475],{"class":474},[464,605,439],{"class":478},[464,607,608],{"class":570},"          # Pod 的 Label，要和上面、和 Service 一致\n",[464,610,612,615],{"class":466,"line":611},15,[464,613,614],{"class":470},"    spec",[464,616,499],{"class":474},[464,618,620,623],{"class":466,"line":619},16,[464,621,622],{"class":470},"      containers",[464,624,499],{"class":474},[464,626,628,631,634,636],{"class":466,"line":627},17,[464,629,630],{"class":474},"        - ",[464,632,633],{"class":470},"name",[464,635,475],{"class":474},[464,637,510],{"class":478},[464,639,641,644,646,649],{"class":466,"line":640},18,[464,642,643],{"class":470},"          image",[464,645,475],{"class":474},[464,647,648],{"class":478},"registry.example.com\u002Fhouse-api:1.3.0",[464,650,651],{"class":570},"   # 不要用 latest\n",[464,653,655,658],{"class":466,"line":654},19,[464,656,657],{"class":470},"          ports",[464,659,499],{"class":474},[464,661,663,666,669,671],{"class":466,"line":662},20,[464,664,665],{"class":474},"            - ",[464,667,668],{"class":470},"containerPort",[464,670,475],{"class":474},[464,672,673],{"class":540},"8080\n",[464,675,677,680],{"class":466,"line":676},21,[464,678,679],{"class":470},"          env",[464,681,499],{"class":474},[464,683,685,687,689,691],{"class":466,"line":684},22,[464,686,665],{"class":474},[464,688,633],{"class":470},[464,690,475],{"class":474},[464,692,693],{"class":478},"ASPNETCORE_URLS\n",[464,695,697,700,702],{"class":466,"line":696},23,[464,698,699],{"class":470},"              value",[464,701,475],{"class":474},[464,703,704],{"class":478},"http:\u002F\u002F+:8080\n",[464,706,708,710,712,714],{"class":466,"line":707},24,[464,709,665],{"class":474},[464,711,633],{"class":470},[464,713,475],{"class":474},[464,715,716],{"class":478},"ConnectionStrings__Default\n",[464,718,720,723],{"class":466,"line":719},25,[464,721,722],{"class":470},"              valueFrom",[464,724,499],{"class":474},[464,726,728,731],{"class":466,"line":727},26,[464,729,730],{"class":470},"                secretKeyRef",[464,732,499],{"class":474},[464,734,736,739,741],{"class":466,"line":735},27,[464,737,738],{"class":470},"                  name",[464,740,475],{"class":474},[464,742,743],{"class":478},"house-api-secret\n",[464,745,747,750,752],{"class":466,"line":746},28,[464,748,749],{"class":470},"                  key",[464,751,475],{"class":474},[464,753,754],{"class":478},"db-connection\n",[464,756,758,761],{"class":466,"line":757},29,[464,759,760],{"class":470},"          envFrom",[464,762,499],{"class":474},[464,764,766,768,771],{"class":466,"line":765},30,[464,767,665],{"class":474},[464,769,770],{"class":470},"configMapRef",[464,772,499],{"class":474},[464,774,776,779,781],{"class":466,"line":775},31,[464,777,778],{"class":470},"                name",[464,780,475],{"class":474},[464,782,783],{"class":478},"house-api-config\n",[464,785,787,790],{"class":466,"line":786},32,[464,788,789],{"class":470},"          resources",[464,791,499],{"class":474},[464,793,795,798],{"class":466,"line":794},33,[464,796,797],{"class":470},"            requests",[464,799,499],{"class":474},[464,801,803,806,808],{"class":466,"line":802},34,[464,804,805],{"class":470},"              cpu",[464,807,475],{"class":474},[464,809,810],{"class":478},"250m\n",[464,812,814,817,819],{"class":466,"line":813},35,[464,815,816],{"class":470},"              memory",[464,818,475],{"class":474},[464,820,821],{"class":478},"256Mi\n",[464,823,825,828],{"class":466,"line":824},36,[464,826,827],{"class":470},"            limits",[464,829,499],{"class":474},[464,831,833,835,837],{"class":466,"line":832},37,[464,834,816],{"class":470},[464,836,475],{"class":474},[464,838,839],{"class":478},"512Mi\n",[464,841,843,846],{"class":466,"line":842},38,[464,844,845],{"class":470},"          readinessProbe",[464,847,499],{"class":474},[464,849,851,854],{"class":466,"line":850},39,[464,852,853],{"class":470},"            httpGet",[464,855,499],{"class":474},[464,857,859,862,864],{"class":466,"line":858},40,[464,860,861],{"class":470},"              path",[464,863,475],{"class":474},[464,865,866],{"class":478},"\u002Fhealth\u002Fready\n",[464,868,870,873,875],{"class":466,"line":869},41,[464,871,872],{"class":470},"              port",[464,874,475],{"class":474},[464,876,673],{"class":540},[464,878,880],{"class":466,"line":879},42,[464,881,883],{"class":882},"svObZ","---\n",[464,885,887,889,891],{"class":466,"line":886},43,[464,888,471],{"class":470},[464,890,475],{"class":474},[464,892,893],{"class":478},"v1\n",[464,895,897,899,901],{"class":466,"line":896},44,[464,898,485],{"class":470},[464,900,475],{"class":474},[464,902,903],{"class":478},"Service\n",[464,905,907,909],{"class":466,"line":906},45,[464,908,496],{"class":470},[464,910,499],{"class":474},[464,912,914,916,918],{"class":466,"line":913},46,[464,915,505],{"class":470},[464,917,475],{"class":474},[464,919,510],{"class":478},[464,921,923,925,927],{"class":466,"line":922},47,[464,924,516],{"class":470},[464,926,475],{"class":474},[464,928,521],{"class":478},[464,930,932,934],{"class":466,"line":931},48,[464,933,527],{"class":470},[464,935,499],{"class":474},[464,937,939,941],{"class":466,"line":938},49,[464,940,547],{"class":470},[464,942,499],{"class":474},[464,944,946,949,951],{"class":466,"line":945},50,[464,947,948],{"class":470},"    app",[464,950,475],{"class":474},[464,952,510],{"class":478},[464,954,956,959],{"class":466,"line":955},51,[464,957,958],{"class":470},"  ports",[464,960,499],{"class":474},[464,962,964,967,970,972,975],{"class":466,"line":963},52,[464,965,966],{"class":474},"    - ",[464,968,969],{"class":470},"port",[464,971,475],{"class":474},[464,973,974],{"class":540},"80",[464,976,977],{"class":570},"                  # Service 對內的 Port\n",[464,979,981,984,986,989],{"class":466,"line":980},53,[464,982,983],{"class":470},"      targetPort",[464,985,475],{"class":474},[464,987,988],{"class":540},"8080",[464,990,991],{"class":570},"          # 轉到容器的 Port\n",[464,993,995],{"class":466,"line":994},54,[464,996,883],{"class":882},[464,998,1000,1002,1004],{"class":466,"line":999},55,[464,1001,471],{"class":470},[464,1003,475],{"class":474},[464,1005,1006],{"class":478},"networking.k8s.io\u002Fv1\n",[464,1008,1010,1012,1014],{"class":466,"line":1009},56,[464,1011,485],{"class":470},[464,1013,475],{"class":474},[464,1015,1016],{"class":478},"Ingress\n",[464,1018,1020,1022],{"class":466,"line":1019},57,[464,1021,496],{"class":470},[464,1023,499],{"class":474},[464,1025,1027,1029,1031],{"class":466,"line":1026},58,[464,1028,505],{"class":470},[464,1030,475],{"class":474},[464,1032,510],{"class":478},[464,1034,1036,1038,1040],{"class":466,"line":1035},59,[464,1037,516],{"class":470},[464,1039,475],{"class":474},[464,1041,521],{"class":478},[464,1043,1045,1047],{"class":466,"line":1044},60,[464,1046,527],{"class":470},[464,1048,499],{"class":474},[464,1050,1052,1055,1057],{"class":466,"line":1051},61,[464,1053,1054],{"class":470},"  ingressClassName",[464,1056,475],{"class":474},[464,1058,1059],{"class":478},"nginx\n",[464,1061,1063,1066],{"class":466,"line":1062},62,[464,1064,1065],{"class":470},"  rules",[464,1067,499],{"class":474},[464,1069,1071,1073,1076,1078],{"class":466,"line":1070},63,[464,1072,966],{"class":474},[464,1074,1075],{"class":470},"host",[464,1077,475],{"class":474},[464,1079,1080],{"class":478},"api.example.com\n",[464,1082,1084,1087],{"class":466,"line":1083},64,[464,1085,1086],{"class":470},"      http",[464,1088,499],{"class":474},[464,1090,1092,1095],{"class":466,"line":1091},65,[464,1093,1094],{"class":470},"        paths",[464,1096,499],{"class":474},[464,1098,1100,1103,1106,1108],{"class":466,"line":1099},66,[464,1101,1102],{"class":474},"          - ",[464,1104,1105],{"class":470},"path",[464,1107,475],{"class":474},[464,1109,1110],{"class":478},"\u002Fhouses\n",[464,1112,1114,1117,1119],{"class":466,"line":1113},67,[464,1115,1116],{"class":470},"            pathType",[464,1118,475],{"class":474},[464,1120,1121],{"class":478},"Prefix\n",[464,1123,1125,1128],{"class":466,"line":1124},68,[464,1126,1127],{"class":470},"            backend",[464,1129,499],{"class":474},[464,1131,1133,1136],{"class":466,"line":1132},69,[464,1134,1135],{"class":470},"              service",[464,1137,499],{"class":474},[464,1139,1141,1143,1145],{"class":466,"line":1140},70,[464,1142,778],{"class":470},[464,1144,475],{"class":474},[464,1146,510],{"class":478},[464,1148,1150,1153],{"class":466,"line":1149},71,[464,1151,1152],{"class":470},"                port",[464,1154,499],{"class":474},[464,1156,1158,1161,1163],{"class":466,"line":1157},72,[464,1159,1160],{"class":470},"                  number",[464,1162,475],{"class":474},[464,1164,1165],{"class":540},"80\n",[15,1167,1168],{},"套用與檢查：",[354,1170,1174],{"className":1171,"code":1172,"language":1173,"meta":360,"style":360},"language-bash shiki shiki-themes github-dark","kubectl apply -f house-api.yaml\n\nkubectl get pods -n realty -l app=house-api       # Pod 有沒有起來\nkubectl get endpoints house-api -n realty          # Service 有沒有找到 Pod\nkubectl describe pod \u003Cpod-name> -n realty          # 起不來時看 Events\nkubectl logs \u003Cpod-name> -n realty                  # 看應用程式的 Log\n","bash",[100,1175,1176,1190,1196,1221,1240,1270],{"__ignoreMap":360},[464,1177,1178,1181,1184,1187],{"class":466,"line":467},[464,1179,1180],{"class":882},"kubectl",[464,1182,1183],{"class":478}," apply",[464,1185,1186],{"class":540}," -f",[464,1188,1189],{"class":478}," house-api.yaml\n",[464,1191,1192],{"class":466,"line":482},[464,1193,1195],{"emptyLinePlaceholder":1194},true,"\n",[464,1197,1198,1200,1203,1206,1209,1212,1215,1218],{"class":466,"line":493},[464,1199,1180],{"class":882},[464,1201,1202],{"class":478}," get",[464,1204,1205],{"class":478}," pods",[464,1207,1208],{"class":540}," -n",[464,1210,1211],{"class":478}," realty",[464,1213,1214],{"class":540}," -l",[464,1216,1217],{"class":478}," app=house-api",[464,1219,1220],{"class":570},"       # Pod 有沒有起來\n",[464,1222,1223,1225,1227,1230,1233,1235,1237],{"class":466,"line":502},[464,1224,1180],{"class":882},[464,1226,1202],{"class":478},[464,1228,1229],{"class":478}," endpoints",[464,1231,1232],{"class":478}," house-api",[464,1234,1208],{"class":540},[464,1236,1211],{"class":478},[464,1238,1239],{"class":570},"          # Service 有沒有找到 Pod\n",[464,1241,1242,1244,1247,1250,1254,1257,1260,1263,1265,1267],{"class":466,"line":513},[464,1243,1180],{"class":882},[464,1245,1246],{"class":478}," describe",[464,1248,1249],{"class":478}," pod",[464,1251,1253],{"class":1252},"snl16"," \u003C",[464,1255,1256],{"class":478},"pod-nam",[464,1258,1259],{"class":474},"e",[464,1261,1262],{"class":1252},">",[464,1264,1208],{"class":540},[464,1266,1211],{"class":478},[464,1268,1269],{"class":570},"          # 起不來時看 Events\n",[464,1271,1272,1274,1277,1279,1281,1283,1285,1287,1289],{"class":466,"line":524},[464,1273,1180],{"class":882},[464,1275,1276],{"class":478}," logs",[464,1278,1253],{"class":1252},[464,1280,1256],{"class":478},[464,1282,1259],{"class":474},[464,1284,1262],{"class":1252},[464,1286,1208],{"class":540},[464,1288,1211],{"class":478},[464,1290,1291],{"class":570},"                  # 看應用程式的 Log\n",[15,1293,1294,1297,1298,1301],{},[100,1295,1296],{},"kubectl get endpoints"," 是我最常用的除錯指令：",[38,1299,1300],{},"如果這裡是空的，代表 Service 沒有找到任何就緒的 Pod","，要嘛 Label 對不上，要嘛 Pod 的 Readiness 一直沒通過。",[10,1303,1304],{"id":1304},"剛開始最常踩到的坑",[19,1306,1307,1315,1328,1340,1350,1356,1362,1368],{},[22,1308,1309,1312,1313],{},[38,1310,1311],{},"Label 對不上","：Service 的 Selector、Deployment 的 Selector、Pod Template 的 Label 三者要一致。請求一直失敗時，先看 ",[100,1314,1296],{},[22,1316,1317,242,1324,1327],{},[38,1318,1319,1320,1323],{},"使用 ",[100,1321,1322],{},"latest"," 標籤",[100,1325,1326],{},"image: house-api:latest"," 無法知道現在跑的是哪一版，也無法退版，而且映像檔在某些拉取策略下不會更新。每次建置都用明確的版本號或 Commit SHA",[22,1329,1330,1336,1337,1339],{},[38,1331,1332,1335],{},[100,1333,1334],{},"targetPort"," 和容器的 Port 不一致","：Service 的 ",[100,1338,1334],{}," 要對到應用程式實際監聽的 Port。.NET 的容器映像檔預設監聽 80，自己改成 8080 時兩邊都要改",[22,1341,1342,1345,1346,1349],{},[38,1343,1344],{},"Secret 不是加密","：Secret 的內容只是 ",[38,1347,1348],{},"Base64 編碼","，任何能讀取它的人都能還原。要控管誰能讀取 Secret，敏感資料也不要把 YAML 直接放進 Git",[22,1351,1352,1355],{},[38,1353,1354],{},"沒有設定 Resource Requests","：Scheduler 不知道 Pod 需要多少資源，可能把太多 Pod 塞到同一台 Node 上，大家一起變慢，甚至被系統砍掉",[22,1357,1358,1361],{},[38,1359,1360],{},"沒有 Readiness Probe","：容器一啟動就會被 Service 加進清單，應用程式還在初始化，請求就進來了，部署時會出現一波錯誤",[22,1363,1364,1367],{},[38,1365,1366],{},"把資料寫在容器裡","：上傳的圖片存在容器的資料夾，Pod 重建就全部消失，而且 3 個 Pod 各自只有一部分的檔案",[22,1369,1370,1373,1374,1377],{},[38,1371,1372],{},"Pod 一直重啟（CrashLoopBackOff）","：先 ",[100,1375,1376],{},"kubectl logs --previous"," 看上一次當掉前的 Log，通常是設定或連線字串錯誤",[10,1379,1380],{"id":1380},"一句話總結",[15,1382,1383],{},"Deployment 透過 ReplicaSet 維持 Pod 的數量與版本，Service 用 Label Selector 找到就緒的 Pod 並提供固定的入口，Ingress 依網域與路徑把外部的 HTTP 請求分給不同的 Service，ConfigMap 與 Secret 讓同一個映像檔在不同環境使用不同的設定；物件之間全靠 Label 連結，請求不通時，先檢查 Endpoints 是不是空的。",[1385,1386,1387],"style",{},"html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}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 pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}",{"title":360,"searchDepth":482,"depth":482,"links":1389},[1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401],{"id":12,"depth":482,"text":13},{"id":48,"depth":482,"text":48},{"id":92,"depth":482,"text":93},{"id":144,"depth":482,"text":145},{"id":186,"depth":482,"text":187},{"id":231,"depth":482,"text":232},{"id":346,"depth":482,"text":347},{"id":375,"depth":482,"text":376},{"id":415,"depth":482,"text":416},{"id":451,"depth":482,"text":452},{"id":1304,"depth":482,"text":1304},{"id":1380,"depth":482,"text":1380},"2022-01-11",null,"md",{},"\u002Farticles\u002Fk8s-core-objects",{"title":5,"description":360},"articles\u002Fk8s-core-objects",[1410,1411,1412,1413],"Kubernetes","容器","DevOps","部署","1ct01HRPwAB7bvpxWyutbTxIBQJwWcev2RZkj_WuVfI",1792959150577]