Harbor 安裝 參考強國人 或是另一個強國人(https://www.cnblogs.com/ExMan/p/11996944.html )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 wget https://github.com/goharbor/harbor/releases/download/v2.3.0/harbor-offline-installer-v2.3.0.tgz tar -xvf harbor-offline-installer-v2.3.0.tgz cd harbor cp harbor.yml.tmpl harbor.yml vim harbor.yml #把 https 註解掉 #並且把 hostname 直接換成機器的 ip hostname: 10.1.25.123 # https related config #https: # https port for harbor, default is 443 #port: 443 # The path of cert and key files for nginx #certificate: /your/certificate/path #private_key: /your/private/key/path ./prepare --with-trivy --with-chartmuseum docker-compose up -d
注意! 萬一之前有安裝 registry 先把他停掉或是移除 , 不然會起不來
1 2 docker container ls -a docker container rm ec1fadeecc80 5b5dfc3e0a31
到此可以先看一下結果在 chrome 輸入 10.1.25.123
即可進入 Harbor 畫面 預設登入的帳號密碼為 admin
Harbor12345
注意 H 是大寫 建立一個名稱為 test 私有的專案
接著確保 server 上 docker 的 daemon.json
屬性 insecure-registries
有正確 ip 加入 , 因為先前設定 harbor.yml 使用的是 80 , 此處要設定 80 另外 client 的 docker insecure-registries 也要加入正確的 ip 地址 詳細說明可以看官網
windows C:\ProgramData\docker\config\daemon.json
linux /etc/docker/daemon.json
1 2 3 4 5 6 7 8 9 10 11 12 cat /etc/docker/daemon.json { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts":{ "max-size": "100m" }, "storage-driver": "overlay2", "insecure-registries":[ "10.1.25.123" ] }
注意 修改後需要 restart docker , 並且要再次開啟 harbor
1 2 sudo service docker restart sudo docker-compose up -d
最後開一個新的 powershell or bash 測試用 client 登入私有的 registry 應該可以看到 Login Succeeded
1 docker login http://10.1.25.123 --username admin --password Harbor12345
若失敗則會看到記得檢查看看是否為 insecure-registries
設定錯誤 , 注意! 如果是在 k8s 整合上的話每個 node 都需要設定
1 Error response from daemon: Get https://10.1.25.123/v2/: dial tcp 10.1.25.123:443: connect: connection refused
打標籤在這個 image 上 , 並且 push
1 2 docker tag nginx:latest 10.1.25.123/test/nginx:latest docker push 10.1.25.123/test/nginx:latest
k8s 整合 詳細說明可參考官網 看網路上指定檔案都用這樣 ~/.docker/config.json
, 但是會炸 Error , 不曉得為啥 , 所以改成 $HOME 騙看看才過
1 kubectl create secret generic regcred --from-file=.dockerconfigjson=$HOME/.docker/config.json --type=kubernetes.io/dockerconfigjson
或是直接用這樣應該都會建立出一樣的結果
1 2 3 kubectl create secret docker-registry regcred --docker-server=10.1.25.123 \ --docker-username=admin \ --docker-password=Harbor12345
接著印出 secret regcred 看看
1 kubectl get secret regcred --output=yaml
會長這樣
1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 data: .dockerconfigjson: ewoJImF1dGhzIjogewoJCSIxMC4xLjMxxx... kind: Secret metadata: creationTimestamp: "2021-06-20T07:23:34Z" name: regcred namespace: default resourceVersion: "3050890" uid: 9b48e55e-89dc-4b61-b9ee-749cc47d9ae9 type: kubernetes.io/dockerconfigjson
最後建立 Pod
看看
1 2 3 4 5 6 7 8 9 10 apiVersion: v1 kind: Pod metadata: name: private-reg spec: containers: - name: private-reg-container image: 10.1.25.123/test/nginx:latest imagePullSecrets: - name: regcred
k8s 整合 .net core 可以參考這篇強國人
Pod 起手式 新增 HelloWorldController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [Route( "api/[controller]" )] [ApiController] public class HelloWorldController : ControllerBase { [HttpGet] public string Get() { string result = ""; string HostName = Dns.GetHostName(); Console.WriteLine( "Host Name of machine =" + HostName ); result += "Host Name of machine =" + HostName + Environment.NewLine; IPAddress[] ipaddress = Dns.GetHostAddresses( HostName ); Console.Write( "IPv4 of Machine is " ); result += "IPv4 of Machine is " + Environment.NewLine; foreach (IPAddress ip4 in ipaddress.Where( ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork )) { Console.WriteLine( ip4.ToString() ); result += ip4.ToString() + Environment.NewLine; } Console.Write( "IPv6 of Machine is " ); result += "IPv6 of Machine is " + Environment.NewLine; foreach (IPAddress ip6 in ipaddress.Where( ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6 )) { Console.WriteLine( ip6.ToString() ); result += ip6.ToString() + Environment.NewLine; } return result; } }
特別注意 Startup
內的 Configure
方法 , 需要把 UseHttpsRedirection 註解掉 , 不然會導向錯誤的網址
1 //app.UseHttpsRedirection();
新增 DockerFile
並且把檔案設定為 Always Copy
設定暴露的 PORT , 注意這個 ENV ASPNETCORE_URLS=http://+:80;https://+:443
一定要設定 , 不然會找不到位置
1 2 3 4 5 6 7 8 FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /app COPY . . ENV ASPNETCORE_URLS=http://+:80;https://+:443 EXPOSE 80 EXPOSE 443 ENTRYPOINT ["dotnet", "HelloWorldKubernetes.dll"]
切換到 bin\Debug 目錄底下進行編譯 , 用 docker 跑看看結果是否正確
1 2 docker build -t helloworldk8s . docker run --name helloworldk8s -d -p 5000:80 -p 5001:443 helloworldk8s:latest
先用以下命令查看看有無正確啟動 container 萬一沒啟動加上 -a
參數看看發生啥錯誤
1 2 3 docker container ls docker container ls -a
開啟 chrome 測試https://localhost:5001/api/helloworld
http://localhost:5000/api/helloworld
打標籤到自己私有的 Harbor Registry 並且推送上去
1 2 docker tag helloworldk8s:latest 10.1.25.123/test/helloworldk8s:latest docker push 10.1.25.123/test/helloworldk8s:latest
在 master 節點 pull image
1 2 3 4 docker pull 10.1.25.123/test/helloworldk8s:latest docker images #REPOSITORY TAG IMAGE ID CREATED SIZE #10.1.25.123/test/helloworldk8s latest 8d030c225a2a 18 minutes ago 635MB
建立 yaml 檔 , 可以參考[微軟這篇]來編(https://docs.microsoft.com/en-us/dotnet/architecture/containerized-lifecycle/design-develop-containerized-apps/build-aspnet-core-applications-linux-containers-aks-kubernetes )
1 2 vim private-reg-pod.yaml kubectl apply -f private-reg-pod.yaml
private-reg-pod.yaml
內容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion: v1 kind: Pod metadata: name: private-reg labels: app: private-reg spec: containers: - name: private-reg-container image: 10.1.25.123/test/helloworldk8s:latest ports: - containerPort: 80 protocol: TCP env: - name: ASPNETCORE_URLS value: http://+:80 imagePullSecrets: - name: regcred
查詢目前 pod
1 2 3 kubectl get po #NAME READY STATUS RESTARTS AGE IP NODE #private-reg 1/1 Running 0 5m45s 10.244.1.87 node02
萬一有問題 可以用 describe
除錯
1 2 3 4 5 6 7 8 9 10 11 kubectl describe po private-reg #正常的話會像是下面這樣 #Events: # Type Reason Age From Message # ---- ------ ---- ---- ------- # Normal Scheduled 20s default-scheduler Successfully assigned default/private-reg to node02 # Normal Pulling 19s kubelet Pulling image "10.1.25.123/test/helloworldk8s:latet" # Normal Pulled 2s kubelet Successfully pulled image "10.1.25.123/test/helloworldk8s:latest" in 17.048779767s # Normal Created 1s kubelet Created container private-reg-container # Normal Started 1s kubelet Started container private-reg-container
也可以跳進去 node 裡面看看 docker 狀況
1 2 3 docker container ls #CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES #ae35c6069640 10.1.25.123/test/helloworldk8s "dotnet HelloWorldKu…" 23 minutes ago Up 23 minutes k8s_private...
接著可以在 master 用 curl 測試
1 curl 10.244.1.87/api/helloworld
或是跳入 node 測試 , 這個兩槓符號 --
表示結尾 , 後面接 linux 命令 , 因為 dotnet core 的預設 shell 是用 bash , 所以寫 /bin/bash , 其他容器有可能只有 sh
1 2 3 4 kubectl exec -it private-reg -- /bin/bash #跳進去後會長這樣 , 接著執行 curl 測看看 root@private-reg:/app# curl localhost/api/helloworld
還可以用簡單粗暴的 k8s port-forward 在 master 來測看看
1 curl localhost:8087/api/helloworld
或是直接用 NodePort
1 kubectl expose pod private-reg --name private-reg --type=NodePort
Deployment Lab 將 Pod
改成佈署 Deployment
, 有的書上或是資料會寫用 ReplicaSet
, 實際運用上會直接使用 Deployment
讓他進行自動調度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: apps/v1 kind: Deployment metadata: name: asp-net-core-helloworld-k8s spec: replicas: 4 selector: matchLabels: app: asp-net-core-helloworld-k8s template: metadata: labels: app: asp-net-core-helloworld-k8s spec: containers: - name: asp-net-core-helloworld-k8s image: 10.1.25.123/test/helloworldk8s:latest imagePullPolicy: Always ports: - containerPort: 80 - containerPort: 443 env: - name: ASPNETCORE_URLS value: http://+:80;https://+:443 - name: ASPNETCORE_ENVIRONMENT value: Development imagePullSecrets: - name: regcred
本來沒睡飽少設定 ASPNETCORE_ENVIRONMENT
, 測試的時候發現 swagger 一直打不開 , 超無言 特別注意到 , 因為 asp.net core 預設是只有 IsDevelopment
才會開啟 , 所以需要加入 ASPNETCORE_ENVIRONMENT
區塊 可以看ASP.NET CORE in Action 作者的文章有說明
1 2 3 4 5 6 7 8 9 10 11 public void Configure( IApplicationBuilder app, IWebHostEnvironment env ) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI( c => c.SwaggerEndpoint( "/swagger/v1/swagger.json", "HelloWorldKubernetes v1" ) ); } //..... }
執行佈署 asp-net-core-helloworld-k8s.yaml
1 k apply -f asp-net-core-helloworld-k8s.yaml
用之前安裝的 tree
查看看 Deployment
的階層關係 , 可以發現其實後面有 ReplicaSet
1 2 3 4 5 6 7 8 9 k tree deployment asp-net-core-helloworld-k8s NAMESPACE NAME READY REASON AGE default Deployment/asp-net-core-helloworld-k8s - 3h42m default ├─ReplicaSet/asp-net-core-helloworld-k8s-66f969cb87 - 3h42m default └─ReplicaSet/asp-net-core-helloworld-k8s-7ff974f489 - 46m default ├─Pod/asp-net-core-helloworld-k8s-7ff974f489-bcj86 True 46m default ├─Pod/asp-net-core-helloworld-k8s-7ff974f489-fnntp True 46m default ├─Pod/asp-net-core-helloworld-k8s-7ff974f489-j9s6s True 46m default └─Pod/asp-net-core-helloworld-k8s-7ff974f489-l9j5f True 46m
查看看 ReplicaSet 縮寫為 rs , 可以用 k api-resources
來查詢縮寫
1 2 3 4 k get rs #NAME DESIRED CURRENT READY AGE #asp-net-core-helloworld-k8s-66f969cb87 0 0 0 3h52m #asp-net-core-helloworld-k8s-7ff974f489 4 4 4 56m
查目前 pods
1 2 3 4 5 k get po -o wide #NAME READY STATUS RESTARTS AGE IP NODE #asp-net-core-helloworld-k8s-67cb6f48cd-6xxd7 1/1 Running 0 101s 10.244.1.99 node02 #asp-net-core-helloworld-k8s-67cb6f48cd-nlhlw 1/1 Running 0 101s 10.244.1.100 node02
在 cluster 內用 curl 呼叫看看是否成功 , 這邊還有個 debug 的好法子就是加上 --dump-header -
這樣可以看到 header 更好 debug
1 2 3 4 5 6 7 8 9 curl 10.244.1.99:80/api/helloworld curl 10.244.1.100:80/api/helloworld #測個沒結果的 curl --dump-header - http://10.244.1.112:80 HTTP/1.1 404 Not Found Date: Wed, 30 Jun 2021 07:35:31 GMT Server: Kestrel Content-Length: 0
查 deployment 也可以用 deploy 等價縮寫
1 2 3 4 k get deployments k get deploy #NAME READY UP-TO-DATE AVAILABLE AGE #asp-net-core-helloworld-k8s 2/2 2 2 3m49s
暴露成 service NodePort
在 k8s 內 ClusterIP
為內部通信而 NodePort
為暴露給外部可以進行訪問
1 k expose deployment asp-net-core-helloworld-k8s --name asp-net-core-helloworld-k8s --type=NodePort
最後查看看結果 , 可以看到 cluster 內部訪問是 10.96.165.130 , 而外部則使用 master 機器的 ip 加上 31956
http http://10.1.25.123:31956
https https://10.1.25.123:32418
1 2 3 k get svc #NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE #asp-net-core-helloworld-k8s NodePort 10.96.165.130 <none> 80:31956/TCP,443:32418/TCP 3h4m
萬一想刪除則使用以下命令
1 2 k delete deployments asp-net-core-helloworld-k8s k delete service asp-net-core-helloworld-k8s
HPA Lab 嘗試做做 HPA 的 lab , 看書上寫的含糊 , 東西一直沒起來 , 查了下才發現要安裝 Metric-Server
1 2 3 4 5 6 k describe hpa asp-net-core-helloworld-k8s # #Type Reason Age From Message #---- ------ ---- ---- ------- #Warning FailedGetResourceMetric 1s (x8 over 107s) horizontal-pod-autoscaler failed to get cpu utilization: unable to get metrics for resource cpu: unable to fetch metrics from resource metrics API: the server could not find the requested resource (get pods.metrics.k8s.io) #Warning FailedComputeMetricsReplicas 1s (x8 over 107s) horizontal-pod-autoscaler invalid metrics (1 invalid out of 1), first error is: failed to get cpu utilization: unable to get metrics for resource cpu: unable to fetch metrics from resource metrics API: the server could not find the requested resource (get pods.metrics.k8s.io)
安裝可以參考這個官方文件 直接照著文件做還是會陣亡 , 要特別注意 Requirements
這段寫的內容 , 我的問題是要加上 kubelet-insecure-tls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #Requirements 完全沒問題的話官方作法 #kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml #我的作法 wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.5.0/components.yaml vim components.yaml #找到 Deployment 加入 kubelet-insecure-tls 參數 #spec: # containers: # - args: # - --cert-dir=/tmp # - --secure-port=443 # - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname # - --kubelet-use-node-status-port # - --metric-resolution=15s # - --kubelet-insecure-tls # image: k8s.gcr.io/metrics-server/metrics-server:v0.5.0 #驗證是否成功 k get po,deployment -n kube-system
安裝好以後可以調整 Deployment
的 resources
區塊來限定資源使用 , 注意使用 HorizontalPodAutoscaler
前需要先定義 Deployment
詳細說明可以看這個官網文件
asp-net-core-helloworld-k8s.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 apiVersion: apps/v1 kind: Deployment metadata: name: asp-net-core-helloworld-k8s spec: replicas: 1 selector: matchLabels: app: asp-net-core-helloworld-k8s template: metadata: labels: app: asp-net-core-helloworld-k8s spec: containers: - name: asp-net-core-helloworld-k8s image: 10.1.25.123/test/helloworldk8s:latest imagePullPolicy: Always ports: - containerPort: 80 - containerPort: 443 env: - name: ASPNETCORE_URLS value: http://+:80;https://+:443 - name: ASPNETCORE_ENVIRONMENT value: Development resources: limits: cpu: 500m requests: cpu: 200m imagePullSecrets: - name: regcred
asp-net-core-helloworld-k8s-hpa.yaml 限制 50% cpu 使用率
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: asp-net-core-helloworld-k8s spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: asp-net-core-helloworld-k8s minReplicas: 1 maxReplicas: 10 targetCPUUtilizationPercentage: 50
最後是佈署 HPA
1 2 k apply -f asp-net-core-helloworld-k8s.yaml k apply -f asp-net-core-helloworld-k8s-hpa.yaml
安裝 apache ab , 因為預設會有限制 , 這邊把限制解開 , 接著模擬多人 request 我們的 api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #萬一是被拔光的 ubuntu 才安裝以下命令 #apt upgrade #apt update #apt list --upgradable #apt-get install iputils-ping #apt-get install -y net-tools #普通 ubuntu 應該直接可以安裝 ab sudo apt-get install apache2-utils ulimit -a ulimit -n 204800 #查目前 ClusterIP k get svc #NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE #asp-net-core-helloworld-k8s NodePort 10.96.165.130 <none> 80:31956/TCP,443:32418/TCP 7d2h #測試模擬多人同時 request , 注意這邊不管是打 cluster-id 或是用對外的 port 都可以達到同樣效果 ab -r -c 2000 -n 204800 http://10.96.165.130/api/helloworld #懶得安裝 ab 的話直接用 curl + while 也可以測 while true; do curl http://10.96.165.130/api/helloworld; done
查看 HPA 是否有生效 , 注意這邊 TARGETS 超過了總使用量 50% , REPLICAS 也擴充到 10 個 , 至此就完成了整個自動擴容 最後等到 ab 把 request 打完以後 , 會自動縮容預設時間是 5 分鐘 , 要調整的話參數為 --horizontal-pod-autoscaler-downscale-stabilization
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #查 hpa k get hpa #NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE #asp-net-core-helloworld-k8s Deployment/asp-net-core-helloworld-k8s 146%/50% 1 10 10 78m #查 pod k get po #也可以用 sql plugin 的方式撈 #k sql get po where "name like '%asp%'" #NAME READY STATUS RESTARTS AGE #asp-net-core-helloworld-k8s-5766cd7cc5-4x452 1/1 Running 0 92s #asp-net-core-helloworld-k8s-5766cd7cc5-8kxw6 1/1 Running 0 107s #asp-net-core-helloworld-k8s-5766cd7cc5-gtcn9 1/1 Running 0 62s #asp-net-core-helloworld-k8s-5766cd7cc5-js4sj 1/1 Running 0 79m #asp-net-core-helloworld-k8s-5766cd7cc5-lw4b2 1/1 Running 0 62s #asp-net-core-helloworld-k8s-5766cd7cc5-m4stc 1/1 Running 0 62s #asp-net-core-helloworld-k8s-5766cd7cc5-mpblz 1/1 Running 0 107s #asp-net-core-helloworld-k8s-5766cd7cc5-rlnhs 1/1 Running 0 107s #asp-net-core-helloworld-k8s-5766cd7cc5-v6m2x 1/1 Running 0 62s #asp-net-core-helloworld-k8s-5766cd7cc5-zcptc 1/1 Running 0 62s
設定 dns 網路部分除了設定 hosts 之外還有 dnsPolicy 可以設定 , 分別為 Default
, ClusterFirst
, ClusterFirstWithHostNet
, None
因為在內網裡有自己的 dns server 以我環境使用 ubuntu 為例 , 查詢看看目前用啥 dns ip
1 2 3 4 #應以 netplan 內的為準 cat /etc/netplan/00-network-manager-all.yaml cat /run/systemd/resolve/resolv.conf cat /etc/resolv.conf
接著設定正確的 dns
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: apps/v1 kind: Deployment metadata: name: asp-net-core-helloworld-k8s spec: replicas: 1 selector: matchLabels: app: asp-net-core-helloworld-k8s template: metadata: labels: app: asp-net-core-helloworld-k8s spec: containers: - name: asp-net-core-helloworld-k8s image: 10.1.25.123/test/asp-net-core-helloworld-k8s:latest imagePullPolicy: Always ports: - containerPort: 80 dnsPolicy: ClusterFirstWithHostNet dnsConfig: nameservers: - 10.1.25.7 searches: - xxx.com.tw imagePullSecrets: - name: regcred
最後跳進去看看 , 可以看到 k8s 多把 dns 補進去了 , 原本只有 10.96.0.10 這個 nameserver , 跟這串 search default.svc.cluster.local svc.cluster.local cluster.local 現在多補進了之前我們在 deployment 內設定的區塊
1 2 3 4 5 6 7 k exec -it asp-net-core-helloworld-k8s-7cfc8db5f6-bk2j8 -- sh cat /etc/resolv.conf #nameserver 10.96.0.10 #nameserver 10.1.25.7 #search default.svc.cluster.local svc.cluster.local cluster.local xxx.com.tw #options ndots:5
最後可以參考這篇佛心老外 有其他解法
hosts 設定 除了 dns 以外可能會想要在自己的 container 內使用 hosts , k8s 有 hostalias 這個東東可以使用 先查自己有啥 hosts 需要補 linux /etc/hosts
windows C:\Windows\System32\drivers\etc\hosts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 apiVersion: apps/v1 kind: Deployment metadata: name: asp-net-core-helloworld-k8s spec: replicas: 1 selector: matchLabels: app: asp-net-core-helloworld-k8s template: metadata: labels: app: asp-net-core-helloworld-k8s spec: containers: - name: asp-net-core-helloworld-k8s image: 10.1.25.123/test/asp-net-core-helloworld:latest imagePullPolicy: Always ports: - containerPort: 8080 hostAliases: - ip: "10.1.17.46" hostnames: - "ggyy.com.tw" imagePullSecrets: - name: regcred
liveness 探針 接著來設定 liveness 探針 , 修改 Deployment , 固定每 10 秒戳一次看看有沒有活 , 啟動時給他個緩衝時間 5 秒 , 若回應不是 200 liveness 會重新啟動 Pod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 apiVersion: apps/v1 kind: Deployment metadata: name: asp-net-core-helloworld-k8s spec: replicas: 1 selector: matchLabels: app: asp-net-core-helloworld-k8s template: metadata: labels: app: asp-net-core-helloworld-k8s spec: containers: - name: asp-net-core-helloworld-k8s image: 10.1.25.123/test/asp-net-core-helloworld-k8s:latest imagePullPolicy: Always livenessProbe: httpGet: path: '/health' port: 5000 scheme: HTTP initialDelaySeconds: 5 periodSeconds: 10 ports: - containerPort: 5000 resources: limits: cpu: 500m requests: cpu: 200m hostAliases: - ip: "10.1.3.5" hostnames: - "xxooxx" imagePullSecrets: - name: regcred
先在程式碼裡面有插上這段 , 測試可以用 curl 10.244.1.29:5000/health
去戳他 , 現在每 10 秒 k8s 會自己去戳 , 正常狀況會給 Healthy
細部可以在自己依情境調整
1 2 3 4 5 6 7 8 9 10 11 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //... 略 app.UseEndpoints( endpoints => { endpoints.MapControllers(); endpoints.MapHealthChecks( "/health" ); } ); }
為了驗證是否有生效可以故意把路徑寫錯 , 改成 gg
1 2 3 4 5 6 7 livenessProbe: httpGet: path: '/gg' port: 5000 scheme: HTTP initialDelaySeconds: 5 periodSeconds: 10
接著 get po 看看會不會 restart
1 2 3 4 5 k get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES asp-net-core-helloworld-k8s-5d44fff86-4pbbw 1/1 Running 0 24s 10.244.1.221 node02 <none> <none> asp-net-core-helloworld-k8s-5d44fff86-kwd6w 1/1 Running 0 24s 10.244.1.222 node02 <none> <none> asp-net-core-helloworld-k8s-5d44fff86-lkr6b 1/1 Running 4 2m26s 10.244.1.220 node02 <none> <none>
nlog 設定要注意的問題事項 , 先跳進去 pod 內看看 , 發現居然有 D: 這個資料夾 原來之前有設定 nlog 寫入 log 會放在 D:/Log/${shortdate}_Now.log , 在 k8s 設定時需要特別注意一下
1 2 3 4 5 k exec -it asp-net-core-helloworld-k8s-7c9b78d948-j7mq9 -- /bin/bash ls #BouncyCastle.Crypto.dll Microsoft.EntityFrameworkCore.dll NLog.MailKit.dll System.IdentityModel.Tokens.Jwt.dll #D: Microsoft.Extensions.DependencyInjection.dll NLog.Web.AspNetCore.dll System.Runtime.Caching.dll #...略
Cronjob Lab 有時候我們可能希望定時做些任務接著回報給自己 , 可能用 line or mail , 這邊用 curl 去定時打一個 api 接著串 mail 來進行測試 cronjob , 這裡有好用的 crontab 工具方便 debug 另外要注意自己的檔案是什麼權限 , 可以加上 securityContext
區塊來設定使用權限 查詢權限指令可以用這個指令 cat /etc/passwd
1 2 3 #x:使用者:群組 root:x:0:0:root:/root:/bin/bash bin:x:2:2:bin:/bin:/usr/sbin/nologin
所以想用 root 的話就要設定這樣
1 2 3 securityContext: runAsUser: 0 runAsGroup: 0
此外我們有用 hostPath
把資料夾 /home/ladisai/curl_example
mount 上去 , 注意使用 hostPath 是指在 node 節點上的資料夾位置 , 實際用的話應該會 mount nfs , 這裡就偷懶 還有一點要特別小心 , 在 command 這個地方要執行多條的話需要在 args 內一直接下去 , 可以參考這篇
cronjob.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 apiVersion: batch/v1 kind: CronJob metadata: name: sendmail spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: sendmail image: curlimages/curl volumeMounts: - mountPath: /test name: test-volume command: ["/bin/sh"] args: [ "-c" , "timestamp=$(date +%s); cp /test/mail_template.txt /test/mail_template_$timestamp.txt; curl http://10.1.25.123:30612/api/helloworld >> /test/mail_template_$timestamp.txt; echo $timestamp >> /test/mail_template_$timestamp.txt; curl smtp://email.xxx.com.tw:25 \ --user 'yourname@xxx.com:yourpassword' \ --mail-from 'yourname@xxx.com' \ --mail-rcpt 'yourname@xxx.com' \ --upload-file /test/mail_template_$timestamp.txt" ] #args: ["-c" , "cd /test;pwd >> pwd.txt; cat /test/send_mail_command.sh >> qq.txt"] #args: ["-c" , "curl -L www.google.com >> /test/google.txt"] securityContext: runAsUser: 0 runAsGroup: 0 dnsPolicy: ClusterFirstWithHostNet dnsConfig: nameservers: - 10.1.30.5 volumes: - name: test-volume hostPath: path: /home/yourname/curl_example restartPolicy: Never
mail_template.txt
1 2 3 4 5 6 7 8 9 10 11 12 From: "yourname" <yourname@xxx.com> To: "yourname" <yourname@xxx.com> Subject: This is a test _____ < Log > ----- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||
最後看看執行結果 , 如果是 completed 才 ok
遇到 dns 問題或是權限問題 , 可以先建立一個 Pod 用這樣的方法測試看看 , 逐步偵錯 , 看是權限不足或是 dns 設定有問題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 apiVersion: v1 kind: Pod metadata: name: google-curl spec: containers: - name: google-curl image: curlimages/curl command: ["/bin/sh"] args: ["-c" , "echo nameserver 10.1.2.87 >> /etc/resolv.conf; curl -L www.google.com"] securityContext: runAsUser: 0 runAsGroup: 0 #k apply -f google-curl.yaml #k logs google-curl
nfs server 參考老外 跟這個大神 這邊直接裝在 master node 上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #nfs server sudo apt-get install nfs-kernel-server #nfs client sudo apt-get install nfs-common sudo mkdir /var/nfs/general -p sudo chown nobody:nogroup /var/nfs/general ls -la /var/nfs/general sudo chmod -R 777 /var/nfs/general #設定可以連線的 client sudo vim /etc/exports /var/nfs/general 10.1.25.124(rw,sync,no_subtree_check) #restart nfs sudo systemctl restart nfs-kernel-server
跳到其他 node 執行以下命令
1 2 3 4 5 6 7 8 9 10 sudo apt-get install nfs-common sudo mkdir -p /nfs/general #注意非常重要 , 這邊要 mount master 那台 nfs server 不要寫錯了 sudo mount 10.1.25.123:/var/nfs/general /nfs/general cd /nfs/general #測試看看是否可以正常建立檔案 , ok 的話 master & node 都可以看到 echo "qq" > qq cat qq
安裝 Nginx Ingress Controller 預設情況下 k8s 不會幫你安裝 ingress controller , 需一自己安裝官網有多種選擇 因為有選擇障礙這邊用 nginx ingress controller 參考官網 此外由於預設為 LoadBalancer
, 內部環境最好改為 NodePort
1 2 3 4 5 6 7 8 9 10 11 12 wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/cloud/deploy.yaml vim deploy.yaml #搜尋 LoadBalancer #/LoadBalancer #修改為 NodePort #spec: # type: NodePort # externalTrafficPolicy: Local k apply -f deploy.yaml
安裝會多加上一個 namespace
1 2 3 4 5 6 k get ns #NAME STATUS AGE #default Active 35d #development Active 14d #foo Active 22d #ingress-nginx Active 7m43s
所以要撈的話記得多加參數 -n ingress-nginx
, 或是直接用 -A
撈全部
1 2 3 4 k get svc -n ingress-nginx k get po -n ingress-nginx k get svc -A k get po -A
安裝好 nginx ingress controller 後會多加上一個 NodePort
來服務對外 , 剛好分配到好難聽的 port 名稱
1 2 3 4 k get svc -n ingress-nginx #NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE #ingress-nginx-controller LoadBalancer 10.106.114.179 <pending> 80:30678/TCP,443:31326/TCP 13m #ingress-nginx-controller-admission ClusterIP 10.104.100.19 <none> 443/TCP 13m
補充 , 如果不想要對外多加上 port 30678
這麼醜的話可以考慮把 hostPort
設定起來這樣就可以直接打 80 , 443
1 2 3 4 5 6 7 8 9 10 11 12 13 vim deploy.yaml #/Deployment #搜尋 Deployment 找到以下片段 # #ports: # - name: http # containerPort: 80 # hostPort: 80 # protocol: TCP # - name: https # containerPort: 443 # hostPort: 443
接著看 nginx-controller 被分配到哪個 node
1 2 3 4 5 6 k get po -o wide -n ingress-nginx # NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ingress-nginx-admission-create-sx9f4 0/1 Completed 0 16m 10.244.1.172 node02 <none> <none> ingress-nginx-admission-patch-jxfx4 0/1 Completed 1 16m 10.244.1.171 node02 <none> <none> ingress-nginx-controller-5b74xc9xx8-dg6k5 1/1 Running 0 16m 10.244.1.173 node02 <none> <none>
假設我 node02 的 vm ip 是 10.1.25.124
, 所以等等就要打 curl 10.1.25.124:30678/網址
, 試打看看會跳 nginx 頁面
1 2 3 4 5 6 7 8 curl 10.1.25.124:30678 #<html> #<head><title>404 Not Found</title></head> #<body> #<center><h1>404 Not Found</h1></center> #<hr><center>nginx</center> #</body> #</html>
接著把之前的 nodeport 修改一下讓他固定在 port 30345 asp-net-core-helloworld-k8s-service.yaml
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: v1 kind: Service metadata: name: asp-net-core-helloworld-k8s spec: type: NodePort ports: - port: 80 #service 的 port targetPort: 80 # .net core app 的 port nodePort: 30345 #外部的 port selector: app: asp-net-core-helloworld-k8s
先用 curl 測看看
1 2 3 4 5 curl 10.96.165.130/api/helloworld #Host Name of machine =asp-net-core-helloworld-k8s-5766cd7cc5-js4sj #IPv4 of Machine is #10.244.1.119 #IPv6 of Machine is
確定都 ok 以後 , 可以加入 ingress 的資源 (舊版 extensions/v1beta1) , 特別注意這種定義方式需要 path 完全 match 否則會找不到 asp-net-core-helloworld-k8s-ingress.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: asp-net-core-helloworld-k8s spec: rules: - host: asp-net-core-helloworld-k8s.com http: paths: - path: /api/helloworld #注意這邊要跟 app 內的路徑完全一樣 backend: serviceName: asp-net-core-helloworld-k8s servicePort: 80
修改 /etc/hosts , 若是想在 windows 上看的話位置在 C:\Windows\System32\drivers\etc\hosts
1 2 3 4 5 6 vim /etc/hosts #10.1.25.124 asp-net-core-helloworld-k8s.com #下面 example 會用到 #10.1.25.124 helloworld-k8s.com #10.1.25.124 helloworld-contour-k8s.com
最後用 curl 測試一下
1 curl asp-net-core-helloworld-k8s.com:30678/api/helloworld
最後用新版的 api 測試看看 (新版 networking.k8s.io/v1) , 這樣定義的話即可讓全部的路徑 mapping 到你的 url 詳細可以參考官網 asp-net-core-helloworld-k8s-ingress-new.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: asp-net-core-helloworld-k8s-new annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: helloworld-k8s.com http: paths: - path: / pathType: Prefix backend: service: name: asp-net-core-helloworld-k8s port: number: 80
可以用這兩個頁面測試http://helloworld-k8s.com:30678/swagger/index.html http://helloworld-k8s.com:30678/api/helloworld
最後補充 nginx ingress controller 會在 /etc/nginx/nginx.conf
補上與剛剛設定的 helloworld-k8s.com , 可以跳進去看
1 2 3 k exec -it ingress-nginx-controller-5b74bxx868-lg47d -n ingress-nginx -- bash #cat /etc/nginx/nginx.conf #vi /etc/nginx/nginx.conf
Helm 安裝 , 參考官網 1 2 3 4 5 curl https://baltocdn.com/helm/signing.asc | sudo apt-key add - sudo apt-get install apt-transport-https --yes echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list sudo apt-get update sudo apt-get install helm
其他操作上的小技巧 這裡定義 ${VERSION}
當作變數 , 並且定義 template.yaml , 可以接上 envsubst
快速替換內容來進行更版 另外注意到 --record
參數 , 可以更詳細記錄當時的指令
1 VERSION='v1.0' envsubst < template.yaml | k apply --record -f -
template.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: apps/v1 kind: Deployment metadata: name: asp-net-core-helloworld-k8s spec: replicas: 4 selector: matchLabels: app: asp-net-core-helloworld-k8s template: metadata: labels: app: asp-net-core-helloworld-k8s spec: containers: - name: asp-net-core-helloworld-k8s image: 10.1.25.123/test/helloworldk8s:${VERSION} imagePullPolicy: Always ports: - containerPort: 80 - containerPort: 443 env: - name: ASPNETCORE_URLS value: http://+:80;https://+:443 - name: ASPNETCORE_ENVIRONMENT value: Development imagePullSecrets: - name: regcred
apply 以後可以用以下指令去查目前更新狀態
1 k rollout status deployment asp-net-core-helloworld-k8s
列印出修改歷史
1 k rollout history deployment asp-net-core-helloworld-k8s
滾回之前的版本
1 2 3 4 5 #滾回上個版本 k rollout undo deployment asp-net-core-helloworld-k8s #滾回特定的版本 k rollout undo deployment asp-net-core-helloworld-k8s --to-revision 5
金絲雀佈署因為只多加一個新版的 pod , 所以可以用暫停命令來串
1 2 VERSION='v1.1' envsubst < template.yaml | k apply --record -f - && \ k rollout pause deployment asp-net-core-helloworld-k8s
Here Document 這個技巧可以直接在 command 內快速加上想補的內容
1 2 3 'cat <<EOF > /etc/resolv.conf nameserver 8.8.8.8 EOF'
其他補充 編輯東西前務必知道的官網說明
跳進去看看環境變數
1 2 exec -it asp-net-core-helloworld-k8s-5766cd7cc5-js4sj -- /bin/bash env | grep ASPNETCORE_URLS
開啟 chrome 測試這邊要改成用 curl 測試 , 因為 chrome 會用 keep-alive 連線 , 而 curl 每次都會開一個新的連線
如果想讓每個相同 client ip 都轉到同個 pod 上的話可以開啟這個選項
1 2 3 kind: Service spec: sessionAffinity: ClientIP