作者:牛玉富,某知名互聯網公司專家工程師,喜歡開源 / 熱衷分享,對 K8s 及 golang 網關有較深入研究,
本文將解讀如何利用云原生解決私有化交付中的問題,進而打造一個 PaaS 平臺,提升業務平臺的復用性,在進入正題之前,有必要先明確兩個關鍵詞:
- PaaS 平臺:多個核心業務服務作為一個整體平臺去封裝,以平臺形式提供服務,
- 私有化交付:平臺需要部署私有云環境中,要面對無網情況下依然可以運轉,
傳統交付痛點

如上圖:私有云會有明確的安全性要求
- 私有云服務無法連接外網,資料只能通過單向網閘形式進行擺渡到內網私有云,
- 源代碼只能存盤在公司機房中,私有云只部署編譯檔案,
- 服務會不定期迭代,另外為了保證服務穩定性需要自建獨立業務監控,
基于以上要求面臨的挑戰大概有幾點:
- 架構可遷移性差:服務之間配置復雜,多種異構語言需要修改組態檔,無固定服務 DNS,
- 部署運維成本高:服務依賴環境需支持離線安裝,服務更新需本地運維人員手動完成,復雜場景下,完整一次部署大概需要 數人 / 月 的時間,
- 監控運維成本高:監控需支持系統級 / 服務級 / 業務級監控,通知方式需支持短信、Webhook 等多種型別,
架構方案

我們的原則是 擁抱云原生和復用已有能力,近可能使用業界已存在且成熟技術方案,
我們采用 KubeSphere+K8S 作為服務編排,處于安全性及簡潔性考慮對 Syncd 進行二次開發完整 DevOps 能力,監控系統上采用 Nightingale+Prometheus 方案,
如上圖架構圖
- 藍色框內是我們底層 PaaS 集群,我們對業務服務通用服務統一進行了服務編排升級,用以解決架構遷移性差問題,
- 紅色框內,監控系統作為一種編排服務形式存在,所有監控項交付前配置好,用以解決監控系統運維成本高問題,
- 紫色框內,服務容器可以實作跨網段自動拉取并自動化部署,用以解決服務服務部署成本高問題,
下面我們針對這三部分做下介紹,
服務編排:KubeSphere
KubeSphere 的愿景是打造一個以 K8s 為內核的云原生分布式作業系統,它的架構可以非常方便地使第三方應用與云原生生態組件進行即插即用(plug-and-play)的集成,支持云原生應用在多云與多集群的統一分發和運維管理,同時它還擁有活躍的社區,
KubeSphere 選型理由有以下幾點:
基于制品的方式定制自己的私有化交付方案
私有化鏡像檔案打包
創建制品清單 :
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Manifest
metadata:
name: sample
spec:
arches:
- amd64
...
- type: kubernetes
version: v1.21.5
components:
helm:
version: v3.6.3
cni:
version: v0.9.1
etcd:
version: v3.4.13
containerRuntimes:
- type: docker
version: 20.10.8
crictl:
version: v1.22.0
harbor:
version: v2.4.1
docker-compose:
version: v2.2.2
images:
- dockerhub.kubekey.local/kubesphere/kube-apiserver:v1.22.1
...
然后我們就可以通過命令進行匯出了,
$ ./kk artifact export -m manifest-sample.yaml -o kubesphere.tar.gz
私有化部署
創建部署清單:
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: sample
spec:
hosts:
- {name: kubesphere01.ys, address: 10.89.3.12, internalAddress: 10.89.3.12, user: kubesphere, password: "Kubesphere123"}
- {name: kubesphere02.ys, address: 10.74.3.25, internalAddress: 10.74.3.25, user: kubesphere, password: "Kubesphere123"}
- {name: kubesphere03.ys, address: 10.86.3.66, internalAddress: 10.86.3.66, user: kubesphere, password: "Kubesphere123"}
- {name: kubesphere04.ys, address: 10.86.3.67, internalAddress: 10.86.3.67, user: kubesphere, password: "Kubesphere123"}
- {name: kubesphere05.ys, address: 10.86.3.11, internalAddress: 10.86.3.11, user: kubesphere, password: "Kubesphere123"}
roleGroups:
etcd:
- kubesphere01.py
- kubesphere02.py
- kubesphere03.py
control-plane:
- kubesphere01.py
- kubesphere02.py
- kubesphere03.py
worker:
- kubesphere05.py
registry:
- kubesphere04.py
controlPlaneEndpoint:
internalLoadbalancer: haproxy
domain: lb.kubesphere.local
address: ""
port: 6443
kubernetes:
version: v1.21.5
clusterName: cluster.local
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
multusCNI:
enabled: false
registry:
type: harbor
auths:
"dockerhub.kubekey.local":
username: admin
password: Kubesphere123
...
執行安裝部署:
$ ./kk create cluster -f config-sample.yaml -a kubesphere.tar.gz --with-packages --with-kubesphere --skip-push-images
原來大量復雜的 K8s 部署、高可用方案、Harbor 私有化鏡像倉庫等,均可以完成自動化安裝,極大的簡化了私有化交付場景下 K8s 組件部署難度,
可視化界面極大簡化操作流程
- 創建部署:流水線式創建一個容器服務的部署、存盤、服務訪問,

- 資源限制:限制容器的資源利用率 & 限制租戶資源利用率,

- 遠程登陸:容器遠程登陸功能,

基于 KubeSphere 的業務部署經驗分享
私有化場景構建高可用服務實體部署,保障單實體掛掉不影響整體使用,我們要保證以下幾點,
1、由于服務都需要有固定的網路標識和存盤,所以我們需要創建 “有狀態副本集部署”,
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: project
name: ${env_project_name}
labels:
app: ${env_project_name}
spec:
serviceName: ${env_project_name}
replicas: 1
selector:
matchLabels:
app: ${env_project_name}
template:
metadata:
labels:
app: ${env_project_name}
spec:
containers:
- name: ${env_project_name}
image: ${env_image_path}
imagePullPolicy: IfNotPresent
2、有狀態副本集使用 host 反親和性保證服務分散到不同 host 中,
....
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- ${env_project_name}
topologyKey: kubernetes.io/hostname
....
3、服務與服務之間互相呼叫均使用 K8s 底層的 DNS 進行配置,

4、集群內部依賴外部資源時需要設定為 Service,然后在內部提供服務,
kind: Endpoints
apiVersion: v1
metadata:
name: redis-cluster
namespace: project
subsets:
- addresses:
- ip: 10.86.67.11
ports:
- port: 6379
---
kind: Service
apiVersion: v1
metadata:
name: redis-cluster
namespace: project
spec:
ports:
- protocol: TCP
port: 6379
targetPort: 6379
5、借助 nip.io 域名實作服務動態域名決議除錯,
nip.io 可以自動根據請求的域名中設定 IP 資訊,完成回應的 IP 資訊映射,
$ nslookup abc-service.project.10.86.67.11.nip.io
Server: 169.254.25.10
Address: 169.254.25.10:53
Non-authoritative answer:
Name: abc-service.project.10.86.67.11.nip.io
Address: 10.86.67.11
因此我們可以在構建 Ingress 時直接使用該域名:
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: gatekeeper
namespace: project
spec:
rules:
- host: gatekeeper.project.10.86.67.11.nip.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: gatekeeper
port:
number: 8000
6、掛載目錄到宿主機,有時候需要容器直接關聯宿主機目錄具體操作如下,
...
spec:
spec:
...
volumeMounts:
- name: vol-data
mountPath: /home/user/data1
volumes:
- name: vol-data
hostPath:
path: /data0
7、有狀態部署作業負載,主要涉及 StatefulSet、Service、volumeClaimTemplates、Ingress,示例如下:
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: project
name: gatekeeper
labels:
app: gatekeeper
spec:
serviceName: gatekeeper
replicas: 1
selector:
matchLabels:
app: gatekeeper
template:
metadata:
labels:
app: gatekeeper
spec:
containers:
- name: gatekeeper
image: dockerhub.kubekey.local/project/gatekeeper:v362
imagePullPolicy: IfNotPresent
ports:
- name: http-8000
containerPort: 8000
protocol: TCP
- name: http-8080
containerPort: 8080
protocol: TCP
resources:
limits:
cpu: '2'
memory: 4Gi
volumeMounts:
- name: vol-data
mountPath: /home/user/data1
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- gatekeeper
topologyKey: kubernetes.io/hostname
volumeClaimTemplates:
- metadata:
name: vol-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: gatekeeper
namespace: project
labels:
app: gatekeeper
spec:
ports:
- name: "http-8000"
protocol: TCP
port: 8000
targetPort: 8000
- name: "http-8080"
protocol: TCP
port: 8080
targetPort: 8080
selector:
app: gatekeeper
type: NodePort
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: gatekeeper
namespace: project
spec:
rules:
- host: gatekeeper.project.10.86.67.11.nip.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: gatekeeper
port:
number: 8000
- host: gatekeeper.project.10.86.68.66.nip.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: gatekeeper
port:
number: 8080
DevOps:基于 Syncd 構建服務自動化交付
DevOps 選型有很多,這里我們沒有采用 Jenkins、GitRunner 等等,而是使用了我們團隊內部比較熟悉的 Syncd 進行二次開發,原因有兩點:
- 處于安全考慮:我們的原始碼無法在本地存放,所以基于 gitlab 構建打包的方案,對我們用處不是很大,使用是一種資源浪費,
- 功能簡潔性:雖然 Syncd 已經停更 2 年多但是,但其核心的 CICD 功能比較完善且前后端拓展性強,我們可以很輕松拓展相應的功能,
Syncd 核心思路:
- 從使用本地工具鏈構建打包鏡像,這里可以把 docker push 當作 git push 理解,
- 通過 Syncd 拉取鏡像包完成部署流程打包上線操作,通過打包時設定版本號便于服務回滾,
構建本地工具鏈
1、基于專案創建目錄
#創建目錄
cd /Users/niuyufu/goproject/abc-service
mkdir -p devops
cd devops
2、匯入 Dockerfile,大家可基于業務自行創建,
3、創建 tool.sh 檔案
cat >> tool.sh << EOF
#!/bin/sh
###########配置區域##############
#模塊名稱,可變更
module=abc-service
#專案名稱
project=project1
#容器名稱
container_name=${project}"_"${module}
#鏡像名稱
image_name=${project}"/"${module}
#服務埠映射:宿主機埠:容器埠,多個逗號間隔
port_mapping=8032:8032
#鏡像hub地址
image_hub=dockerhub.kubekey.local
#鏡像tag
image_tag=latest
###########配置區域##############
#構建工具
action=$1
case $action in
"docker_push")
image_path=${image_hub}/${image_name}:${image_tag}
docker tag ${image_name}:${image_tag} ${image_path}
docker push ${image_path}
echo "鏡像推送完畢,image_path: "${image_path}
;;
"docker_login")
container_id=$(docker ps -a | grep ${container_name} | awk '{print $1}')
docker exec -it ${container_id} /bin/sh
;;
"docker_stop")
docker ps -a | grep ${container_name} | awk '{print $1}' | xargs docker stop
container_id=`docker ps -a | grep ${container_name} | awk '{print $1}' | xargs docker rm`
if [ "$container_id" != "" ];then
echo "容器已關閉,container_id: "${container_id}
fi
if [ "$images_id" != "" ];then
docker rmi ${images_id}
fi
;;
"docker_run")
docker ps -a | grep ${container_name} | awk '{print $1}' | xargs docker stop
docker ps -a | grep ${container_name} | awk '{print $1}' | xargs docker rm
port_mapping_array=(${port_mapping//,/ })
# shellcheck disable=SC2068
for var in ${port_mapping_array[@]}; do
port_mapping_str=${mapping_str}" -p "${var}
done
container_id=$(docker run -d ${port_mapping_str} --name=${container_name} ${image_name})
echo "容器已啟動,container_id: "${container_id}
;;
"docker_build")
if [ ! -d "../output" ]; then
echo "../output 檔案夾不存在,請先執行 ../build.sh"
exit 1
fi
cp -rf ../output ./
docker build -f Dockerfile -t ${image_name} .
rm -rf ./output
echo "鏡像編譯成功,images_name: "${image_name}
;;
*)
echo "可運行命令:
docker_build 鏡像編譯,依賴../output 檔案夾
docker_run 容器啟動,依賴 docker_build
docker_login 容器登陸,依賴 docker_run
docker_push 鏡像推送,依賴 docker_build"
exit 1
;;
esac
EOF
4、執行專案打包,請確保產出物在 ./output 中
$cd ~/goproject/abc-service/
$sh build.sh
abc-service build ok
make output ok
build done
5、利用 tool.sh 工具進行服務除錯
tools.sh 執行順序一般是這樣的:./output 產出物→docker_build→docker_run→docker_login→docker_push
$cd devops
$chmod +x tool.sh
#查看可運行命令
$sh tool.sh
可運行命令:
docker_build 鏡像編譯,依賴../output 檔案夾
docker_run 容器啟動,依賴 docker_build
docker_login 容器登陸,依賴 docker_run
docker_push 鏡像推送,依賴 docker_build
#docker_build舉例:
$sh tool.sh docker_build
[+] Building 1.9s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 37B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B
... 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:0a1fba79684a1a74fa200b71efb1669116c8dc388053143775aa7514391cdabf 0.0s
=> => naming to docker.io/project/abc-service 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
鏡像編譯成功,images_name: project/abc-service
#docker_run舉例:
$ sh tool.sh docker_run
6720454ce9b6
6720454ce9b6
容器已啟動,container_id: e5d7c87fa4de9c091e184d98e98f0a21fd9265c73953af06025282fcef6968a5
#可以使用 docker_login 登陸容器進行代碼除錯:
$ sh tool.sh docker_login
sh-4.2# sudo -i
root@e5d7c87fa4de:~$
#docker_push舉例:
$sh tool.sh docker_push 130 ?
The push refers to repository [dockerhub.kubekey.local/citybrain/gatekeeper]
4f3c543c4f39: Pushed
54c83eb651e3: Pushed
e4df065798ff: Pushed
26f8c87cc369: Pushed
1fcdf9b8f632: Pushed
c02b40d00d6f: Pushed
8d07545b8ecc: Pushed
ccccb24a63f4: Pushed
30fe9c138e8b: Pushed
6ceb20e477f1: Pushed
76fbea184065: Pushed
471cc0093e14: Pushed
616b2700922d: Pushed
c4af1604d3f2: Pushed
latest: digest: sha256:775e7fbabffd5c8a4f6a7c256ab984519ba2f90b1e7ba924a12b704fc07ea7eb size: 3251
鏡像推送完畢,image_path: dockerhub.kubekey.local/citybrain/gatekeeper:latest
#最后登陸Harbor測驗鏡像是否上傳
https://dockerhub.kubekey.local/harbor/projects/52/repositories/gatekeeper
基于 Syncd 進行服務打包構建
1、專案配置
新增專案

設定 tool.sh 中生成的鏡像地址,

設定構建腳本,

參照有狀態作業負載填寫構建腳本,

2、創建上線單

3、構建部署包執行部署

4、切換到 KubeSphere 查看部署效果,

至此已完成 DevOps 與 KubeSphere 的功能打通,
服務監控:基于 Nightingale 構建企業級監控
選型理由
- 可視化引擎:內置模板,開箱即用,

- 告警分析引擎:靈活管理、告警自愈、開箱即用,

- 支持 Helm Chart 一鍵完成應用及服務部署,私有化場景中我們只需要關心容器融合本地化即可,
$ git clone https://github.com/flashcatcloud/n9e-helm.git
$ helm install nightingale ./n9e-helm -n n9e --create-namespace
實際規則配置演示
- 配置告警規則,無縫支持 PromQL 靈活撰寫各種規則,

- 配置告警接收組

- 實際接收告警訊息及恢復訊息



總結
私有化交付下因業務場景不同,對云原生的應用選型也不相同,本文僅對我們自身業務場景做了介紹,如有問題歡迎指正,另外其他場景下的云原生應用也隨時歡迎大家來和我交流探討,
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/499994.html
標籤:其他
