大家好,我今天给大家带来的是基于云原生的私有化交付PAAS平台介绍。
这个话题有点大我们尽量慢慢来讲把,首先我们会渐进式的从云原生起源讲起,让大家对云原生有个大体认识,再由PAAS平台业务痛点引出基于云原生的私有化解决方案,最后我们会针对私有化交付三大核心问题详细做分析。
标题包含三大关键词。云原生、PAAS平台、私有化交付。
云原生:暂时可以简单理解为一种理念或者一种解决思想。
PAAS平台:多个核心业务服务作为一个整体平台去封装,以平台形式提供服务。
私有化交付:平台需要部署私有云环境中,要面对无网情况下依然可以运转。
下面我们针对以上概览详细做下介绍:
云原生的概念一直以来都很模糊,虽然云原生计算基金会(CNCF)给出了所谓的定义,但是并不能让大家很好的理解云原生的理念,为什么说是理念呢,因为云原生是一种思想,是一种解决方案,很抽象。
2017年, 云原生应用的提出者之一的Pivotal在其官网上将云原生的定义概况为 微服务、DevOps、持续交付、容器这四大特征,这也成了很多人对 Cloud Native的基础印象。
微服务 (Microservice)
几乎每个云原生的定义都包含微服务,跟微服务相对的是单体应用,微服务有理论基础,那就是康威定律,指导服务怎么切分。
DevOps
DevOps(Development和Operations的组合词)即开发、运维一体化。涉及软件在整个开发生命周期中的持续开发,持续测试,持续集成,持续部署和持续监控。
持续交付
持续交付:持续交付是不误时开发,不停机更新,小步快跑,反传统瀑布式开发模型,这要求开发版本和稳定版本并存,其实需要很多流程和工具支撑。
容器 (Container)
2013年,Docker项目正式发布,2014年,K8s项目也正式发布。
Docker是应用最为广泛的容器引擎,在思科谷歌等公司的基础设施中大量使用。K8S是容器编排系统,用于容器管理,容器间的负载均衡
容器技术为何如此重要?
2013 年的后端技术领域,以 Cloud Foundry 为代表的开源 PaaS 项目,成为了当时云计算技术中的一股清流。它最核心的组件就是一套应用的打包和分发机制。会调用操作系统的 Cgroups 和 Namespace 机制为每一个应用单独创建一个称作“沙盒”的隔离环境,然后在“沙盒”中启动这些应用进程,这正是 PaaS 项目最核心的能力。
Docker 在 Cloud Foundry基础之上增加了镜像功能。镜像解决的恰恰就是打包这个根本性的问题。镜像中包含了完整的操作系统文件和目录,他可以做到本地环境和云端环境的高度一致,这正是 Docker 镜像的精髓。
在 2014 年到 2015 年这段时间里,Docker 项目的迅速走红催生出了一个非常繁荣的“Docker 生态”。在这个生态里,围绕着 Docker 在各个层次进行集成和创新的项目层出不穷。Docker 公司通过并购来完善自己的平台层能力。其中一个最成功的案例是对 Fig 项目的收购,后来改名为Fig Compose。
2014 年底的 DockerCon 上,Docker 公司雄心勃勃地对外发布了自家研发的 “Docker 原生” 容器集群管理项目 Swarm,不仅将这波“CaaS”热推向了一个前所未有的高潮,更是寄托了整个 Docker 公司重新定义 PaaS 的宏伟愿望。
Mesos 有着天生的两层调度机制让它非常容易从大数据领域抽身,转而去支持受众更加广泛的 PaaS 业务。在这种思路的指导下,Mesosphere 公司发布了一个名为 Marathon 的项目,而这个项目很快就成为了 Docker Swarm 的一个有力竞争对手。
2014 年注定是一个神奇的年份。就在这一年的 6 月,基础设施领域的翘楚 Google 公司突然发力,正式宣告了一个名叫 Kubernetes 项目的诞生。而这个项目,不仅挽救了当时的 CoreOS 和 RedHat,还如同当年 Docker 项目的横空出世一样,再一次改变了整个容器市场的格局。
Google、RedHat等开源基础设施领域玩家们,共同牵头发起了一个名为CNCF(Cloud Native Computing Foundation)的基金会。这个基金会的目的其实很容易理解:它希望,以Kubernetes项目为基础,建立一个由开源基础设施领域厂商主导的、按照独立基金会方式运营的平台级社区,来对抗以Docker公司为核心的容器商业生态。
为了打造出这样一条围绕Kubernetes项目的“护城河”,CNCF社区就需要至少确保两件事情:
Kubernetes项目让人耳目一新的设计理念和号召力,很快就构建出了一个与众不同的容器编排与管理的生态。Kubernetes项目在GitHub上的各项指标开始一骑绝尘,将Swarm项目远远地甩在了身后。从API到容器运行时的每一层,Kubernetes项目都为开发者暴露出了可以扩展的插件机制,鼓励用户通过代码的方式介入Kubernetes项目的每一个阶段。
Kubernetes社区在2016年之后得到了空前的发展。更重要的是,不同于之前局限于“打包、发布”这样的PaaS化路线,这一次容器社区的繁荣,是一次完全以Kubernetes项目为核心的“百家争鸣”。
2017年10月,Docker公司出人意料地宣布,将在自己的主打产品Docker企业版中内置Kubernetes项目,这标志着持续了近两年之久的“编排之争”至此落下帷幕。
CNCF基提供了一个中立的合作平台,汇聚全球顶尖开发人员、终端用户和厂商,联合了华为、阿里、腾讯、亚马逊、微软、Salesforce等超过500家国际知名科技公司,共同努力打造一个良性发展的云计算生态。
目前,已经有多个项目从孵化到成熟,最终进入到毕业阶段。如:Kubernetes、Prometheus、Envoy、CoreDNS、containerd、TUF、Jaeger、Fluentd、Vitess、Helm。这些项目作为云原生的基础组件为后续其他提供底层依赖和发展参考。
这张全景图(原图地址)试图从云原生的层次结构,以及不同的功能组成上,让用户了解云原生体系的全貌,并帮助用户在不同组件层次去选择恰当的软件和工具进行支持。从总体来看,它将云原生生态分为以下几层:
综上所述,CNCF Landscape全景图中包含了CNCF社区成熟或使用范围较广、具有最佳实践的产品和方案供用户在实际应用中选择,原来需要闭源方式自己开发个东西,现在你完全可以从CNCF的大社区中找到有社区维护的解决方案,这也许是就是云原生的魅力所在。
了解完云原生下面进入正题就是如何利用云原生解决私有化交付中的问题,进而打造一个PAAS平台,提升业务平台的复用性。
基于以上要求面临的挑战大概有几点:
我们的原则是 拥抱云原生和复用已有能力,近可能使用业界已存在且成熟技术方案。
我们采用Kubesphere+K8S作为服务编排,处于安全性及简洁性考虑对Syncd进行二次开发完整Devops能力,监控系统上采用Nightingale+Prometheus方案。
如上图架构图
下面我们针对这三部分做下介绍。
KubeSphere 是打造一个以 Kubernetes 为内核的云原生分布式操作系统,它的架构可以非常方便地使第三方应用与云原生生态组件进行即插即用(plug-and-play)的集成,支持云原生应用在多云与多集群的统一分发和运维管理,同时它又是CNCF项目拥有活跃的社区。
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组件部署难度。
私有化场景构建高可用服务实例部署,保障单实例挂掉不影响整体使用,我们要保证以下几点。
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: diyu
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
....
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- ${env_project_name}
topologyKey: kubernetes.io/hostname
....
kind: Endpoints
apiVersion: v1
metadata:
name: redis-cluster
namespace: diyu
subsets:
- addresses:
- ip: 10.86.67.11
ports:
- port: 6379
---
kind: Service
apiVersion: v1
metadata:
name: redis-cluster
namespace: diyu
spec:
ports:
- protocol: TCP
port: 6379
targetPort: 6379
# nslookup abc-service.diyu.10.86.67.11.nip.io
Server: 169.254.25.10
Address: 169.254.25.10:53
Non-authoritative answer:
Name: abc-service.diyu.10.86.67.11.nip.io
Address: 10.86.67.11
因此我们可以在构建ingress时直接使用该域名:
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: gatekeeper
namespace: diyu
spec:
rules:
- host: gatekeeper.diyu.10.86.67.11.nip.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: gatekeeper
port:
number: 8000
...
spec:
spec:
...
volumeMounts:
- name: vol-data
mountPath: /home/user/data1
volumes:
- name: vol-data
hostPath:
path: /data0
apiVersion: apps/v1
kind: StatefulSet
metadata:
namespace: diyu
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: diyu
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: diyu
spec:
rules:
- host: gatekeeper.diyu.10.86.67.11.nip.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: gatekeeper
port:
number: 8000
- host: gatekeeper.diyu.10.86.68.66.nip.io
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: gatekeeper
port:
number: 8080
DevOps选型有很多,这里我们没有采用 Jenkins、GitRunner等等,而是使用了我们团队内部比较熟悉的Syncd进行二次开发。原因有两点:
Syncd核心思路:
1、从使用本地工具链构建打包镜像,这里可以把docker push当作git push理解。
2、通过Syncd拉取镜像包完成部署流程打包上线操作,通过打包时设置版本号便于服务回滚。
#创建目录
cd /Users/niuyufu/goproject/abc-service
mkdir -p devops
cd devops
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
$cd ~/goproject/abc-service/
$sh build.sh
abc-service build ok
make output ok
build done
$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/traj-service]
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/traj-service:latest
#最后登陆Harbor测试镜像是否上传
https://dockerhub.kubekey.local/harbor/projects/52/repositories/traj-service
新增项目
设置tool.sh中生成的镜像地址。
设置构建脚本。
参照有状态工作负载填写构建脚本。
2. 创建上线单
3. 构建部署包执行部署
4. 切换到kubesphere查看部署效果。
至此已完成devops与kubesphere的功能打通。
git clone https://github.com/flashcatcloud/n9e-helm.git
helm install nightingale ./n9e-helm -n n9e --create-namespace
私有化交付下因业务场景不同,对云原生的应用选型也不相同。本文仅对我们自身业务场景做了介绍如有问题欢迎指正,另外其他场景下的云原生应用也随时大家来交流,今天就介绍到这里谢谢大家。
参考链接:
https://www.cnblogs.com/gongxianjin/p/16396152.html
https://www.cnblogs.com/gongxianjin/p/16396155.html
https://www.bilibili.com/read/cv16088829
https://www.cnblogs.com/gongxianjin/p/16396162.html
https://segmentfault.com/a/1190000040304693?utm_source=tag-newest
https://kubesphere.com.cn/docs/v3.3/introduction/what-is-kubesphere/
https://github.com/dreamans/syncd
https://github.com/flashcatcloud/n9e-helm
https://github.com/kubesphere/kubekey/releases/