1. 程式人生 > >如何在K8S平臺部署微服務

如何在K8S平臺部署微服務

Deploying Micro-services on Kubernetes

本文將介紹如何使用 kubernetes 部署微服務,包括 服務發現,監控,路由,日誌。用實際的例子來演示自動化流程。主要分為以下幾個部分:

  1. 5分鐘搭建 K8S 叢集

  2. 部署 CNI 網路

  3. 部署監控服務

  4. 部署閘道器

  5. 部署日誌服務

  6. 部署一個應用

5分鐘搭建 K8S 叢集

第一次完全手動搭建叢集大約花了一週時間,主要的問題是在於

  1. K8S的元件多,每個程式的引數有不少,哪些是關鍵的引數需要花時間搞清楚。

  2. 萬惡的牆,代理訪問外網比較慢

  3. CNI網路問題,主要是 CNI 網段和雲上的區域網網段衝突了,基礎知識缺失導致

  4. K8S 的證書和驗證方式不清楚

本文相關程式碼位於github, 歡迎star。

手動部署可以參考我之前的博文,即便是完全熟悉部署流程,不寫指令碼的情況下,如果純手動 setup 或者 tear down 一個叢集,都是比較耗時間的。直到發現了這個工具 kubeadm, 世界美好了。

這個工具對作業系統有限制, ubuntu 16.04 或 centos 7 以上。其實當初也看到了這個工具, 不過 因為系統限制,並且kubeadm還在alpha版本,又想手動擼一遍部署過程,所以沒直接採用。 不過 kubeadm 不建議在生產環境中使用,在 官方文件中的 limitation 中有詳細解釋.

文件 中第一點就說了, kubeadm部署的是 single master,意味著不是高可用,謹慎使用。 但是作為演示例項再合適不過。

小插曲: 因為最近釋出的 k8s 1.6 的 kubeadm 有一個bug,導致用以下步驟安裝會有問題,為此社群裡有人提了一個patch, 步驟有些多,我寫在本文最後了。kubeadm v1.6.1 已經修復了bug。

開始部署步驟:

  • 在 Digital Ocean 中開三臺機器, centos 7,建議2C2G,按小時計費用不了多少錢,用完就銷燬。 如果還沒有註冊賬號,並且覺得本文對你有幫助,可以用我的 referral link 註冊,可以得到 10美金, 連結. 發現一個更便宜的vps, vultr, 還是SSD

  • 登入三臺機器,安裝必要元件.

補充幾點

1. 因為1.6開始K8S開啟了 RBAC 驗證,需要在啟動 kubelet 的引數中新增 --authentication-token-webhook

vim vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf 新增 --authentication-token-webhook

2. 如果是 centos, 還需要新增 --cgroup-driver=systemd

3. v1.6開始,apiserver禁用了 insecure-port, 需要拷貝 /var/kubernetes/ 下的 admin config, 作為驗證配置.

" title="" data-original-title=“複製”>

yum clean
yum update -y
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http?/yum.kubernetes.io/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
setenforce 0
yum install -y docker kubelet kubeadm kubectl kubernetes-cni
systemctl enable docker && systemctl start docker
systemctl enable kubelet && systemctl start kubelet

補充幾點

1. 因為1.6開始K8S開啟了 RBAC 驗證,需要在啟動 kubelet 的引數中新增 –authentication-token-webhook

vim vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf 新增 –authentication-token-webhook

2. 如果是 centos, 還需要新增 –cgroup-driver=systemd

3. v1.6開始,apiserver禁用了 insecure-port, 需要拷貝 /var/kubernetes/ 下的 admin config, 作為驗證配置.

  • 選擇一臺作為master, 執行

輸出

Your Kubernetes master has initialized successfully!

You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: http://kubernetes.io/docs/admin/addons/

You can now join any number of machines by running the following on each node:

kubeadm join --token=e344fa.e007ce406eb41f07 104.236.166.119" title="" data-original-title=“複製”>

kubeadm init

# 輸出 Your Kubernetes master has initialized successfully!

You should now deploy a pod network to the cluster. Run “kubectl apply -f [podnetwork].yaml” with one of the options listed at: http?/kubernetes.io/docs/admin/addons/

You can now join any number of machines by running the following on each node:

kubeadm join –token=e344fa.e007ce406eb41f07 104.236.166.119

完成後會看到提示: `kubeadm join --token=311971.7260777a25d70ac8 104.236.166.119`
  1. 在其他兩臺機器上分別執行以上提示的命令

  2. 在 master 上檢視狀態, kubectl get nodes, 如果看到一共有2個node,一個master, 則表示叢集建立成功。

部署CNI網路

kubeadm 自動部署了一個外掛,就是 kube-dns, 用於服務發現,但是到這裡你會發現 kube-dns 這個服務沒有啟動成功,因為我們還沒有部署CNI網路。

kubectl get pods --all-namespaces | grep dns

配置檔案在cni目錄下,或者可以直接在master執行:

# for v1.6.0
kubectl apply -f http://docs.projectcalico.org/v2.1/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml

再次檢視 dns 服務是否執行成功吧。

# 按需安裝 git 和 dig
yum install -y bind-utils git 

監控

在部署之前,我們需要對兩臺node標記角色,k8s是通過label來自定義各個資源的型別的。首先確定兩臺node的name, 通過 kubectl get nodes來檢視,之後挑選其中一臺作為前端機器(frontend).

kubectl label node centos-2gb-sfo1-03 role=frontend

這裡把centos-2gb-sfo2-node1換成你的 node name

Prometheus

應用 monitor 目錄下的兩個配置檔案,如下

kubectl create -f prometheus.config.yaml
kubectl create -f prometheus.deploy.yaml

圖片描述

Grafana

kubectl create -f grafana.deploy.yaml
  1. 還需要新增一個 Data Source. 選擇 Promethues, 地址填上:http://promethues:9090, 因為有kube-dns,所以這樣就能訪問 pod 中的 service 圖片描述

  2. 新增模板,內容為 grafana.config.k8s.json, 這個模板是針對 k8s 叢集的儀表模板,新增時選擇對應的 Data Source,然後就能看到效果。 圖片描述圖片描述

閘道器

類似上面的步驟,配置檔案在 gateway 目錄下,執行

kubectl create -f traefik.yaml

traefik 可以監聽 etcd 中註冊的 ingress 的變化,根據 ingress 資源來自動配置路由, 下面會有具體的示例。最後的效果是, 後端服務的配置檔案中定義他自己的 服務domain 和 prefix, traefik會自動新增這個路由, 這樣就可以通過gateway來訪問後端服務了。

日誌收集

官方有推薦的Log系統: cAdvisor 和 Heapster. 我比較偏愛 ELK, 主要是生態比較好。有兩種方式應用:

  1. 第一種是每個Pod都多加一個 sidecar - Filebeat, 在每個後端服務配置檔案中指定本地log的路徑(利用 k8s 的 emptyDir 這個volume),在filebeat的配置中指定這個路徑,實現日誌收集

  2. 還有一種是Filebeat作為 DaemonSet 執行在每臺機器,利用 k8s 的 hostPath 這個volume, 這樣每臺機器只有一個 filebeat 執行,監聽一個指定目錄;後端服務約定好log都寫入這個目錄的子目錄中,這樣也能達到收集效果。

我比較推薦第二種方式,工作量稍微小一些。

第一個服務

終於到了這個緊張刺激的環節。

原始檔在 hello-app 目錄下,一個簡單的 http service, 主要包含兩個路由:

  1. /metrics 返回 prometheus 抓取的資料格式

  2. / 其他Path,返回一個隨機id和URI

log 日誌輸入 /tmp/hello-log/hello-app.log;

想要達到的效果是:

  1. 配置檔案中配好路由,自動註冊到 gateway

  2. 日誌能夠自動收集

app 的配置檔案位於 hello-app 目錄下, 執行:

kubectl create -f hello.yaml

接著去 gateway 和 prometheus 的 dashboard 看下,會發現服務已經被發現;

圖片描述圖片描述

再測試一下通過gateway是否能訪問到 hello-app 這個服務:

curl http://front-end-ip:30087/v1/hello -H 'Host: www.hello.local'
#結果為:
ID:5577006791947779410 path:/hello

編譯安裝 kubeadm

  1. 下載 kubernetes 專案, checkout v1.6.0, 必須是這個tag

  2. cherry-pick 89557110ed4693a7d23e515e738ced266e099365

  3. KUBE_BUILD_PLATFORMS=linux/amd64 hack/make-rules/build.sh cmd/kubeadm

  4. 把生成的 _output 檔案打包,放入伺服器上

  5. 按照本文第一部分的步驟 yum 安裝 docker, kubelet

  6. 編輯檔案 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf 新增 引數--cgroup-driver=systemd

  7. sudo systemctl daemon-reload && sudo systemctl restart kubelet.service

  8. kubeadm init 能完成,但是 node 狀態是 not-ready,因為 cni 沒有配置.

  9. 複製 /etc/kubernetes/admin.conf 檔案到 ~/.kube/config 然後 執行 kubectl get nodes才可以,因為新版的apiserver啟動時,把 insecure-port 禁用了,8080埠不再可用.

Alpine Linux

這次還遇到一個問題, alpine的docker映象使用不順利,ubuntu, centos下編譯的檔案在 alpine 下無法執行, 記得之前還執行成功過,這次得仔細找找原因。

如何打包出最小映象

靜態編譯

CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o hello hello-app/*.go

the -a flag means to rebuild all the packages we’re using, which means all the imports will be rebuilt with cgo disabled.https://github.com/golang/go/...

建議使用 bash 作為 base image, 畢竟還有一些操作例如 mkdir, mv , cp 等需要執行。

如何檢視 二進位制檔案 的 動態依賴?

linux-vdso.so.1 => (0x00007ffde7df8000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff931ae5000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff93171e000) /lib64/ld-linux-x86-64.so.2 (0x00005637b0ae4000)

readelf -d hello

Dynamic section at offset 0x697100 contains 19 entries: Tag Type Name/Value 0x0000000000000004 (HASH) 0xa959e0 0x0000000000000006 (SYMTAB) 0xa95e60 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000005 (STRTAB) 0xa95c40 0x000000000000000a (STRSZ) 518 (bytes) 0x0000000000000007 (RELA) 0xa95650 0x0000000000000008 (RELASZ) 24 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x0000000000000003 (PLTGOT) 0xa97000 0x0000000000000015 (DEBUG) 0x0 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] // 動態依賴庫 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] // 動態依賴庫 0x000000006ffffffe (VERNEED) 0xa95960 0x000000006fffffff (VERNEEDNUM) 2 0x000000006ffffff0 (VERSYM) 0xa95920 0x0000000000000014 (PLTREL) RELA 0x0000000000000002 (PLTRELSZ) 648 (bytes) 0x0000000000000017 (JMPREL) 0xa95680 0x0000000000000000 (NULL) 0x0" title="" data-original-title=“複製”>

# ldd hello

linux-vdso.so.1 => (0x00007ffde7df8000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff931ae5000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff93171e000) /lib64/ld-linux-x86-64.so.2 (0x00005637b0ae4000)

readelf -d hello

Dynamic section at offset 0x697100 contains 19 entries: Tag Type Name/Value 0x0000000000000004 (HASH) 0xa959e0 0x0000000000000006 (SYMTAB) 0xa95e60 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000005 (STRTAB) 0xa95c40 0x000000000000000a (STRSZ) 518 (bytes) 0x0000000000000007 (RELA) 0xa95650 0x0000000000000008 (RELASZ) 24 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x0000000000000003 (PLTGOT) 0xa97000 0x0000000000000015 (DEBUG) 0x0 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] // 動態依賴庫 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] // 動態依賴庫 0x000000006ffffffe (VERNEED) 0xa95960 0x000000006fffffff (VERNEEDNUM) 2 0x000000006ffffff0 (VERSYM) 0xa95920 0x0000000000000014 (PLTREL) RELA 0x0000000000000002 (PLTRELSZ) 648 (bytes) 0x0000000000000017 (JMPREL) 0xa95680 0x0000000000000000 (NULL) 0x0

Alpine 編譯

alpine linux 使用 musl libc,而 ubuntu, centos 使用的是 glibc, 所以在 ubuntu,centos下編譯的檔案一般不能直接使用在 alpine 環境。

docker run --rm -v KaTeX parse error: Expected 'EOF', got '&' at position 23: …src:/go/src -v &̲quot;PWD"?{WORKDIR} -w ${WORKDIR} golang:1.8-alpine go build -o hello-alpine hello-app/main.go

" title="" data-original-title=“複製”>

# export WORKDIR=/go/src/hello-app

docker runrm -v GOPATH&lt;/span&gt;/src:/go/srcv&lt;spanclass=&quot;hljsstring&quot;&gt;&quot;GOPATH&lt;/span&gt;/src:/go/src -v &lt;span class=&quot;hljs-string&quot;&gt;&quot;PWD":WORKDIR&lt;/span&gt;w&lt;spanclass=&quot;hljsvariable&quot;&gt;{WORKDIR}&lt;/span&gt; -w &lt;span class=&quot;hljs-variable&quot;&gt;{WORKDIR} golang:1.8-alpine go build -o hello-alpine hello-app/main.go

注意,編譯的依賴package 被對映入了 /go/src 中; -w 表示 working directory.

            </div>