Kubernetes架構師:基於世界500強的k8s實戰課程2020年最新 K8S實戰基礎篇


Kubernetes架構師:基於世界500強的k8s實戰課程2020年最新 K8S實戰基礎篇:一文帶你深入了解K8S實戰部署SpringBoot項目

1.前言
雲原生可以說是當下互聯網行業最火爆的概念和技術,雲原生從字面意思上來看可以分成雲和原生兩個部分。
雲是和本地相對的,傳統的應用必須跑在本地服務器上,現在流行的應用都跑在雲端,雲包含了IaaS,、PaaS和SaaS。
原生就是土生土長的意思,我們在開始設計應用的時候就考慮到應用將來是運行雲環境里面的,要充分利用雲資源的優點,比如️雲服務的彈性和分布式優勢。
聊到雲原生,避不開的就是容器技術,而docker作為最流行的容器技術,已經經過很多年的線上實戰。今天我們不深入聊雲原生,docker這些技術概念,今天我們聊一聊時下最火的容器編排技術:K8S-實戰部署SpringBoot項目。

2.簡介
2.1.為什么寫這篇文章
前言中提到雲原生、docker、K8S,我是18年第一次docker,也是在18年接觸K8S,相對這門技術來說,我接觸的時候已經有些晚了,因為在之后的面試中,已經感受到這些技術在大廠已經用的很成熟了,之前都在小公司,並不了解這些技術是什么,干什么用,加上國內這方面的資料又比較少,學起來是相當吃力。而到大廠之后,發現這些技術無處不在,並且基礎設施建設已經很完備,一鍵部署雲端的騷操作,讓開發只需要關心業務而無需關心安裝部署等繁瑣的工作。禍兮福之所倚;福兮禍之所伏,大廠的技術設施完備的同時,另一方面也消弱了你去了解基礎設施背后的技術原理能力。正是認識到這一點,今天才寫這篇文章,為迷途中的孩子找到回家的路。廢話不多,擼起袖子,干就完了!

這里沒有任何馬后炮套話,只有粗暴的干貨。寫大家看得懂、用得着、賺得到的文章是唯一宗旨!

2.2.需求描述
我有一個簡單的Springboot項目,想部署在K8S集群中,能夠實現擴縮容,負載均衡,同時我有一個互聯網域名,我想把這個域名綁定在這個服務上,能夠在有網絡的地方訪問。

2.3.需求分析
這個需求我想在很多剛開始接觸docker,k8s等技術的老鐵身上都會遇到過,真正實現起來,並不是那么容易,聽我一一道來:

image—Springboot項目一般是以jar包的形式跑在像centos等服務器上,運行nohup java -jar xxx.jar &命令就能啟動起來。但是在k8s中,運行起來的的並不是jar,而是image,因此我們需要把jar打包成image;
自動擴縮—最基礎的image有了,接下來就要考慮的是自動擴縮:顧名思義,比如說就是在服務訪問量大的時候,我可以添加實例,來減少每個服務實例的壓力,在訪問量小的時候,我可以刪除一部分實例,來實現資源的高效利用。
負載均衡—當我們的實例越來越多,我並不希望把所有的請求都落在一個實例上,如若不然,我們自動擴縮也就沒了意義,傳統方法我們可以用Nginx等實現負載均衡,待會來看看K8S能做些什么
域名綁定—這個就沒什么好說的了。
3. 部署實戰
3.1 環境准備
工欲善其事,必先利其器:

Springboot jar包
K8S集群環境
K8S集群環境部署我就不在這里展開講了,我們准備一個最簡單的Springboot項目,里面只有一個接口,訪問localhost:8088,返回服務器的hostname,當整個部署工作完成之后,我們通過域名訪問這個接口,返回的應該是不同的container的hostname,那我們的任務就完成了。

@GetMapping("/")
public String sayHello() throws UnknownHostException {
String hostname = "Unknown";
InetAddress address = InetAddress.getLocalHost();
hostname = address.getHostName();
return hostname;
}

3.2 image准備
我們都知道,所有image的生成都離不開Dockerfile技術,我們有了一個jar包,要利用Dockerfile技術生成一個image。廢話不多,上代碼:

#使用jdk8作為基礎鏡像
FROM java:8
#指定作者
MAINTAINER ***
#暴漏容器的8088端口
#EXPOSE 8088
#將復制指定的docker-demo-0.0.1-SNAPSHOT.jar為容器中的job.jar,相當於拷貝到容器中取了個別名
ADD docker-demo-0.0.1-SNAPSHOT.jar /job.jar
#創建一個新的容器並在新的容器中運行命令
RUN bash -c 'touch /job.jar'
#設置時區
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
#相當於在容器中用cmd命令執行jar包 指定外部配置文件
ENTRYPOINT ["java","-jar","/job.jar"]

#使用jdk8作為基礎鏡像
FROM java:8
#指定作者
MAINTAINER ***
#暴漏容器的8088端口
#EXPOSE 8088
#將復制指定的docker-demo-0.0.1-SNAPSHOT.jar為容器中的job.jar,相當於拷貝到容器中取了個別名
ADD docker-demo-0.0.1-SNAPSHOT.jar /job.jar
#創建一個新的容器並在新的容器中運行命令
RUN bash -c ‘touch /job.jar’
#設置時區
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
#相當於在容器中用cmd命令執行jar包 指定外部配置文件
ENTRYPOINT [“java”,”-jar”,”/job.jar”]

Dockerfile文件里面有注釋,具體的每一行代碼什么意思我就不展開多講了,這不是今天的重點,接下來,我們把docker-demo-0.0.1-SNAPSHOT.jar,Dockerfile文件放在同一個目錄,上傳到K8S的master 節點上,在目錄內執行如下命令生成images

$ docker build .
1
我們可以看到生成image的過程,通過docker images 查看鏡像

生成一個 docker-demo:latest的image鏡像。
注意:我們部署的是集群,要想K8S集群中都能拉到這個鏡像,那我們有以下兩種方式:

方法一:我們把這個docker-demo:latest上傳到遠端倉庫,這個倉庫可以是我們自己的,或者是像我一樣注冊一個阿里雲的賬號,上傳到阿里雲自己的容器鏡像服務倉庫,如下圖:

具體步驟:
1.1 docker登陸阿里雲容器鏡像服務,需要輸入密碼
$ docker login –username=24k不怕(寫自己的用戶名) registry.cn-hangzhou.aliyuncs.com
1
1.2 在阿里雲上創建命名空間:例:cuixhao-docker-demo

1.3 鏡像打標簽

$ docker tag docker-demo:latest registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest
1
1.4 push到阿里雲

$ docker push registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest
1
1.5 刪除掉docker-demo:latest

$ docker rmi docker-demo:latest
1
方法二 把剛才創建image的過程,在集群中每一台節點上都執行一遍,保證集群中每一台都有這個鏡像。我采用的是二者的結合:先在master上把鏡像生成,上傳到阿里雲,然后在另外的節點上,通過docker pull registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest 命令,從阿里雲上拉到本地,然后在通過 docer tag registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest docker-demo:latest命令打標簽,然后刪掉拉取到的鏡像:docker rmi registry.cn-hangzhou.aliyuncs.com/cuixhao-docker-demo/docker-demo:latest,
為什么這么做?因為我阿里雲建的命名空間中的image都是私有,K8S拉取image的時候是需要集群中都配置ca證書的,如果設置為公開則不存在這個問題。所以我用docker-demo:latest這個鏡像,直接用本地的,部署的時候不用再去阿里雲拉取。
3.3 部署2個實例
3.3.1 編寫yaml文件
基礎鏡像准備好了,那我們就開始部署吧。我們知道,k8s有deployment ,service等概念,這里不詳細講,簡單描述一下:deployment(命名空間),管理pod集群,service,管理pod中的服務。我們在master 節點編輯一個 ingress-docker-docker-deployment.yaml 文件

$ vi ingress-docker-docker-deployment.yaml
1
鍵入以下內容

apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-docker-demo-deployment
labels:
app: ingress-docker-demo
spec:
replicas: 2
selector:
matchLabels:
app: ingress-docker-demo
template:
metadata:
labels:
app: ingress-docker-demo
spec:
containers:
- name: docker-demo
image: docker-demo
imagePullPolicy: Never
ports:
- containerPort: 8088
---
apiVersion: v1
kind: Service
metadata:
name: ingress-docker-demo-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8088
selector:
app: ingress-docker-demo

 

由yaml文件內容我們可以讀出:我們創建了一個 名字我為ingress-docker-demo-deployment的deployment,里面有2個 docker-demo 的pod (replicas: 2),和一個名字為ingress-docker-demo-service的service,管理名字為ingress-docker-demo的pod服務,目標端口8088,即我們的springboot服務所用端口,對外提供80端口。

3.3.2 啟動
在master執行如下命令啟動deployment 和service:

$ kubectl apply -f ingress-docker-docker-deployment.yaml
1
查看啟動結果:

$ kubectl get pod -o wide
1

我們可以看到啟動結果,兩個container分別在 worker01,worker02這兩個節點上,可以看到,K8S集群給我們分配了兩個IP:192.168.14.11,192.168.221.73。我們思考以下三個問題:
a. 在集群中,我們通過以上訪問這兩個服務,能訪問通嗎,是通過8088端口還是80端口?
我們不妨嘗試一下,在集群中任何一個節點執行如下命令:

$ curl 192.168.14.11:8088
$ curl 192.168.221.73:8088
1
2

我們可以看到,接口返回了各自container的hostname,說明我們的服務是部署啟動成功了,訪問80端口是不通的,有興趣的老鐵可以試一下,因為80是我們對外的端口,所以用container ip是訪問不通的。
b. 在集群內部訪問,我們如何做到負載均衡?
有老鐵可能會考慮一個問題,K8S集群中的pod有可能銷毀或者重啟,每次重啟之后的ip不能保證一致,那以上訪問方式肯定是不可采用的。想法很對,我們想訪問一個固定的地址,不管pod如何重啟,ip如何變化,我只訪問這一個ip,這豈不美哉?那我們能不能做到呢?且看如下騷操作:

$ kubectl get svc
1

通過以上命令,我們找到了ingress-docker-docker-deployment.yaml中定義的名字為 ingress-docker-demo-service的service,它有一個 CLUSTER-IP,PORT為80,那我們根據K8S中的service的作用,做一個大膽的猜測:我們是不是可以固定的通過 10.103.19.71 (省略默認80端口)或者 10.103.19.71:80 來永久訪問這兩個服務呢?

$ curl 10.103.19.71
$ curl 10.103.19.71:80
1
2

答案是肯定的!,它給我們做了負載均衡!

c. 在集群外部我們如何訪問這兩個服務並且負載均衡?
集群內訪問服務,負載均衡都已經做好了。有的老鐵會問:集群外服想訪問集群內的服務,該如何做呢?別急,還沒完!

3.3.3 引入Ingress
3.3.3.1 Ingress簡介
我們傳統的集群負載均衡做法是在一台機器上安裝Nginx,把我們的服務配置在Nginx上,外部直接訪問你Nginx,它幫我們做負載,做限流,但是今天我們玩了K8S,就不能在用這種方法了,我們大膽的想一下,我們把所有的東西都在K8S做了,豈不美哉!想法很好,”好事者”已經替我們想到了,並且替我們做到了。
kubernetes ingress 文檔
我來簡單介紹一下:

如圖:在K8S中,Ingress 提供 controller接口,由各個負載均衡廠家實現,傳統Nginx是配置在nginx.conf 中,在K8S中,我們只需要配置Ingress 資源yaml就可以,聽起來是不是方便多了,我們可以像管理deployment,service,pod一樣管理Ingress

3.3.3.2 Ingress 安裝
我們使用 Nginx Ingress Controller 來一波騷操作:
編寫 ingress-nginx.yaml

$ vi ingress-nginx.yaml
1
鍵入以下內容:

apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
 
---
 
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
 
---
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
 
---
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
 
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses/status
verbs:
- update
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: nginx-ingress-role
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- pods
- secrets
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
# Defaults to "<election-id>-<ingress-class>"
# Here: "<ingress-controller-leader>-<nginx>"
# This has to be adapted if you change either parameter
# when launching the nginx-ingress-controller.
- "ingress-controller-leader-nginx"
verbs:
- get
- update
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: nginx-ingress-role-nisa-binding
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: nginx-ingress-role
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
 
---
 
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
# wait up to five minutes for the drain of connections
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
hostNetwork: true
nodeSelector:
name: ingress
kubernetes.io/os: linux
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 33
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
 
---

 

這個文件並不是我胡編亂造自己寫的,是”好事者”幫我們做好了,我只是稍作修改:設置網絡模式為hostNetwork:true,我希望我在集群中一台機器上開一個80端口,用這台機器作為負載均衡入口,因此:nodeSelector: name: ingress。這是節點選擇器配置參數,設置這個,ingress服務會在節點名字為ingress的機器上部署。

接下來我們在集群中的除master節點之外的一個機器上執行下個命令:給這台hostname為worker01-kubeadm-k8s的機器取個別名ingress

$ kubectl label node worker01-kubeadm-k8s name=ingress
1
接下來,我們在master節點執行安裝ingress操作

$ kubectl apply -f ingress-nginx.yaml
1
安裝過程有點兒慢,因為有個鏡像比較難拉取:quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1,建議執行 docker pull 先拉取到本地,上傳到阿里雲,然后在各個節點從阿里雲拉取,然后在打tag的騷操作,都是有經驗的程序員,你們知道我在說什么!

3.3.3.3 Ingress 配置啟動
編寫Ingress yaml資源:

$ vi nginx-ingress.yaml
1
鍵入以下內容:

#ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
spec:
rules:
- host: test.test.com
http:
paths:
- path: /
backend:
serviceName: ingress-docker-demo-service
servicePort: 80

 

文件定義了一種Ingress的資源,配置host為:test.test.com,代理K8S中名字為ingress-docker-demo-service的 Service, Service端口為80.看起來是不是和Nginx配置有點兒類似?
最后一步:啟動

$ kubectl apply -f nginx-ingress.yaml
1
3.3.3.4 驗證
因為test.test.com是不存在的域名,我們都是有經驗的開發人員,很自然的想到去修改本地host : 添加 ip test.test.com,ip為K8S節點中設置的別名為ingress的ip地址
瀏覽器訪問:

完美!

3.3.3.4 自動擴縮
使用如下命令,可以然服務實現自動擴縮:

$ kubectl autoscale ingress-docker-docker-deployment.yaml –min=2 –max=5 –cpu-percent=80
1
還有很多自動擴縮的規則,老鐵們自己探討!

4. 總結
K8S實戰還有很多玩法,我今天只是講了最簡單的服務部署,在不同的生產環境中,需求也是不一樣的,比如說:一個簡單的web應用,有mysql數據庫,有redis,有springboot應用,都要在K8S中實踐,這又是另一種部署方法,但萬變不離其宗,核心都是要深入了解K8S網絡,只有網絡打通了,各個組件才會暢通無阻的運行。有興趣的老鐵可以關注一波,一起Hello World!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM