作者:牛玉富,某闻名互联网公司专家工程师。喜爱开源 / 热心共享,对 K8s 及 golang 网关有较深入研究。

本文将解读怎么运用云原生处理私有化交给中的问题,从而打造一个 PaaS 渠道,提高事务渠道的复用性。在进入正题之前,有必要先清晰两个关键词:

  • PaaS 渠道:多个核心事务服务作为一个整体渠道去封装,以渠道方法提供服务。
  • 私有化交给:渠道需求布置私有云环境中,要面对无网情况下依然能够运转。

传统交给痛点

基于云原生的私有化 PaaS 平台交付实践

如上图:私有云会有清晰的安全性要求

  1. 私有云服务无法连接外网,数据只能经过单向网闸方法进行摆渡到内网私有云。
  2. 源代码只能存储在公司机房中,私有云只布置编译文件。
  3. 服务会不定期迭代,别的为了保证服务稳定性需求自建独立事务监控。

根据以上要求面临的挑战大约有几点:

  1. 架构可迁移性差:服务之间装备杂乱,多种异构言语需求修改装备文件,无固定服务 DNS。
  2. 布置运维本钱高:服务依靠环境需支撑离线装置,服务更新需本地运维人员手动完结,杂乱场景下,完好一次布置大约需求 数人 / 月 的时刻。
  3. 监控运维本钱高:监控需支撑体系级 / 服务级 / 事务级监控,告诉方法需支撑短信、Webhook 等多种类型。

架构计划

基于云原生的私有化 PaaS 平台交付实践

咱们的原则是 拥抱云原生和复用已有才能,近可能运用业界已存在且成熟技术计划。 咱们选用 KubeSphere+K8S 作为服务编列,处于安全性及简洁性考虑对 Syncd 进行二次开发完好 DevOps 才能,监控体系上选用 Nightingale+Prometheus 计划。

如上图架构图

  1. 蓝色框内是咱们底层 PaaS 集群,咱们对事务服务通用服务一致进行了服务编列晋级,用以处理架构迁移性差问题。
  2. 红色框内,监控体系作为一种编列服务方法存在,所有监控项交给前装备好。用以处理监控体系运维本钱高问题。
  3. 紫色框内,服务容器能够完结跨网段自动拉取并自动化布置。用以处理服务服务布置本钱高问题。

下面咱们针对这三部分做下介绍。

服务编列: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 组件布置难度。

可视化界面极大简化操作流程

  • 创立布置:流水线式创立一个容器服务的布置、存储、服务访问。

基于云原生的私有化 PaaS 平台交付实践

  • 资源约束:约束容器的资源运用率 & 约束租户资源运用率。

基于云原生的私有化 PaaS 平台交付实践

  • 远程登陆:容器远程登陆功用。

基于云原生的私有化 PaaS 平台交付实践

根据 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 进行装备。

基于云原生的私有化 PaaS 平台交付实践

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 进行二次开发。原因有两点:

  1. 处于安全考虑:咱们的源码无法在本地寄存,所以根据 gitlab 构建打包的计划,对咱们用途不是很大,运用是一种资源浪费。
  2. 功用简洁性:尽管 Syncd 已经停更 2 年多可是,但其核心的 CICD 功用比较完善且前后端拓展性强,咱们能够很轻松拓展相应的功用。

Syncd 核心思路:

  1. 从运用本地东西链构建打包镜像,这儿能够把 docker push 当作 git push 理解。
  2. 经过 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、项目装备

新增项目

基于云原生的私有化 PaaS 平台交付实践

设置 tool.sh 中生成的镜像地址。

基于云原生的私有化 PaaS 平台交付实践

设置构建脚本。

基于云原生的私有化 PaaS 平台交付实践

参照有状况作业负载填写构建脚本。

基于云原生的私有化 PaaS 平台交付实践

2、创立上线单

基于云原生的私有化 PaaS 平台交付实践

3、构建布置包履行布置

基于云原生的私有化 PaaS 平台交付实践

4、切换到 KubeSphere 查看布置效果。

基于云原生的私有化 PaaS 平台交付实践

至此已完结 DevOps 与 KubeSphere 的功用打通。

服务监控:根据 Nightingale 构建企业级监控

选型理由

  1. 可视化引擎:内置模板,开箱即用。

基于云原生的私有化 PaaS 平台交付实践

  1. 告警分析引擎:灵敏办理、告警自愈、开箱即用。

基于云原生的私有化 PaaS 平台交付实践

  1. 支撑 Helm Chart 一键完结运用及服务布置,私有化场景中咱们只需求关心容器融合本地化即可。
$ git clone https://github.com/flashcatcloud/n9e-helm.git
$ helm install nightingale ./n9e-helm -n n9e --create-namespace

实际规矩装备演示

  1. 装备告警规矩,无缝支撑 PromQL 灵敏编写各种规矩。

基于云原生的私有化 PaaS 平台交付实践

  1. 装备告警接纳组

基于云原生的私有化 PaaS 平台交付实践

  1. 实际接纳告警音讯及恢复音讯

基于云原生的私有化 PaaS 平台交付实践
基于云原生的私有化 PaaS 平台交付实践
基于云原生的私有化 PaaS 平台交付实践

总结

私有化交给下因事务场景不同,对云原生的运用选型也不相同。本文仅对咱们本身事务场景做了介绍,如有问题欢迎纠正,别的其他场景下的云原生运用也随时欢迎咱们来和我交流讨论。

本文由博客一文多发渠道 OpenWrite 发布!