简介:本文是一篇 kubernetes(下文用 k8s 代替)的入门文章,将会波及 k8s 的架构、集群搭建、一个 Redis 的例子,以及如何应用 operator-sdk 开发 operator 的教程。在文章过程中,会交叉引出 Pod、Deployment、StatefulSet 等 k8s 的概念,这些概念通过例子引出来,更容易了解和实际。
作者 | 凡澈
起源 | 阿里技术公众号
前言
本文是一篇 kubernetes(下文用 k8s 代替)的入门文章,将会波及 k8s 的架构、集群搭建、一个 Redis 的例子,以及如何应用 operator-sdk 开发 operator 的教程。在文章过程中,会交叉引出 Pod、Deployment、StatefulSet 等 k8s 的概念,这些概念通过例子引出来,更容易了解和实际。文章参考了很多博客以及材料,放在最初参考资料局部。
一 k8s 架构
咱们看下 k8s 集群的架构,从左到右,分为两局部,第一局部是 Master 节点(也就是图中的 Control Plane),第二局部是 Node 节点。
Master 节点个别包含四个组件,apiserver、scheduler、controller-manager、etcd,他们别离的作用是什么:
- Apiserver:上知地理下知天文,上连其余组件,下接 ETCD,提供各类 api 解决、鉴权,和 Node 上的 kubelet 通信等,只有 apiserver 会连贯 ETCD。
- Controller-manager:管制各类 controller,通过控制器模式,致力于将以后状态转变为冀望的状态。
- Scheduler:调度,打分,分配资源。
- Etcd:整个集群的数据库,也能够不部署在 Master 节点,独自搭建。
Node 节点个别也包含三个组件,docker,kube-proxy,kubelet
- Docker:具体跑利用的载体。
- Kube-proxy:次要负责网络的买通,晚期利用 iptables,当初应用 ipvs 技术。
- Kubelet:agent,负责管理容器的生命周期。
总结一下就是 k8s 集群是一个由两局部组件 Master 和 Node 节点组成的架构,其中 Master 节点是整个集群的大脑,Node 节点来运行 Master 节点调度的利用,咱们后续会以一个具体的调度例子来解释这些组件的交互过程。
二 搭建 k8s 集群
下面说完了 k8s 集群中有哪些组件,接下来咱们先看下如何搭建一个 k8s 集群,有以下几种办法(参考文末链接):
- 当咱们装置了 Docker Desktop APP 之后,勾选 k8s 反对就能搭建起来。
- 应用 MiniKube 来搭建,社区提供的一键装置脚本。
- 间接在云平台购买,例如阿里云 ack。
- 应用 kubeadmin,这是 k8s 社区举荐的能够部署生产级别 k8s 的工具。
- 应用二进制,下载各组件装置,此教程须要留神,下载的各组件版本要和博客中保持一致,就能够胜利。
本文前面的例子均采纳本地 Docker Desktop APP 搭建的 k8s。
➜ ~ kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.4", GitCommit:"3cce4a82b44f032d0cd1a1790e6d2f5a55d20aae", GitTreeState:"clean", BuildDate:"2021-08-11T18:16:05Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.4", GitCommit:"3cce4a82b44f032d0cd1a1790e6d2f5a55d20aae", GitTreeState:"clean", BuildDate:"2021-08-11T18:10:22Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"linux/amd64"}
三 从需要登程
上面咱们从一个理论的需要登程,来看看如何在 k8s 上部署 Redis 服务。
- 部署一个 Redis 服务
- 反对高可用
- 提供对立的 EndPoint 拜访地址
1 部署单机版
如果咱们想在 k8s 上部署一个单机版本 Redis,咱们执行上面的命令即可:
➜ ~ kubectl run redis --image=redis
pod/redis created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 5s
能够用 kubectl exec 来进入到 Pod 外部连贯 Redis 执行命令:
➜ ~ kubectl exec -it redis -- bash
root@redis:/data# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
那么 Pod 和 Redis 是什么关系呢?这里的 Redis 其实是一个 Docker 过程启动的服务,然而在 k8s 中,它叫 Pod。
2 Pod 与 Deployment
咱们来讲下第一个 k8s 的概念 Pod,Pod 是 k8s 中最小的调度单元,一个 Pod 中能够蕴含多个 Docker,这些 Docker 都会被调度到同一台 Node 上,这些 Docker 共享 NetWork Namespace,并且能够申明共享同一个 Volume 来共享磁盘空间。
这样的益处是什么呢?其实在实在的世界中,很多利用是有部署在同一台机器的需要的,比方 Redis 日志采集插件要采集日志,必定须要和 Redis 部署在同一台机器上能力读到 Redis 的日志,咱们后面讲述背景的时候说到了 Docker Swarm 存在一些问题,其中之一就是它只是基于 Docker 调度,尽管也能够设置亲和度让两台 Docker 调度在同一个机器上,然而因为不能一起调度,所以会存在一个 Docker 提前被调度到了一个资源少的机器上,从而导致第二个 Docker 调度失败。
例如咱们一共有 2 台容器,A 和 B,别离为 Redis 和 日志采集组件,各须要 2g 内存,当初有两台 node,node1 3.5 内存,node2 4g 内存,在 Docker Swarm 的调度策略下,先调度 Redis,有可能被调度到了 node1 上,接下来再来调度日志采集组件,发现 node1 只有 1.5g 内存了,调度失败。然而在 k8s 中,调度是依照 pod 来调度的,两个组件在一个 pod 中,调度就不会思考 node1。
尽管 Pod 曾经能够运行 Redis 服务了,然而他不具备高可用性,因为一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定产生了变动(pod.spec.node 字段被批改),否则它永远都不会来到这个节点,这也就意味着,如果这个宿主机宕机了,这个 Pod 也不会被动迁徙到其余节点下来。为了让服务能够始终在,须要应用 Deployment 这样的控制器。
➜ ~ kubectl create deployment redis-deployment --image=redis
deployment.apps/redis-deployment created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 32m
redis-deployment-866c4c6cf9-8z8k5 1/1 Running 0 8s
➜ ~
redis-deployment-866c4c6cf9-8z8k5 就是方才通过 kubectl create 创立的新的 Deployment,为了验证高可用,咱们把用 kubectl delete pod 把 redis 和 redis-deployment-866c4c6cf9-8z8k5 都删掉看会产生什么。
➜ ~ kubectl delete pod redis redis-deployment-866c4c6cf9-8z8k5
pod "redis" deleted
pod "redis-deployment-866c4c6cf9-8z8k5" deleted
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 10s
➜ ~
redis 曾经隐没了,然而 redis-deployment-866c4c6cf9-zskkb 换了个名字又呈现了!
Deployment 能够定义多正本个 Pod,从而为利用提供迁徙能力,如果单纯应用 Pod,实际上当利用被调度到某台机器之后,机器宕机利用也无奈主动迁徙,然而应用 Deployment,则会调用 ReplicaSet(一种控制器)来保障以后集群中的利用正本数和指定的统一。
3 k8s 应用 yaml 来形容命令
k8s 中,能够应用 kubectl 来创立简略的服务,然而还有一种形式是对应创立简单的服务的,就是提供 yaml 文件。例如下面的创立 Pod 的命令,咱们能够用上面的 yaml 文件替换,执行 kubectl create 之后,能够看到 redis Pod 又被创立了进去。
➜ ~ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
➜ ~ kubectl create -f pod.yaml
pod/redis created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 6s
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 6m32s
四 k8s 组件调用流程
上面咱们看下 kubectl create deployment redis-deployment –image=redis 下发之后,k8s 集群做了什么。
- 首先 controller-manager, scheduler, kubelet 都会和 apiserver 开始进行 List-Watch 模型,List 是拿到以后的状态,Watch 是拿到冀望状态,而后 k8s 集群会致力于将以后状态达到达冀望状态。
- kubectl 下发命令到 apiserver,鉴权解决之后将创立信息存入 etcd,Deployment 的实现是应用 ReplicaSet 控制器,当 controller-manager 提前拿到以后的状态(pod=0),接着接管到冀望状态,须要创立 ReplicaSet(pod=1),就会开始创立 Pod。
- 而后 scheduler 会进行调度,确认 Pod 被创立在哪一台 Node 上。
- 之后 Node 上的 kubelet 真正拉起一个 docker。
这些步骤中,apiserver 的作用是显而易见的,所以说上接其余组件,下连 ETCD,然而 apiserver 是能够横向扩容的,而后通过负载平衡,倒是 ETCD 在 k8s 架构中成了瓶颈。
最开始看这架构的时候,会想着为啥 apiserver, scheduler, controller-manager 不合成一个组件,其实在 Google Borg 中,borgmaster 就是这样的,性能也是这些性能,然而合在了一起,最初他们也发现集群大了之后 borgmaster 会有些性能上的问题,包含 kubelet 的心跳就是很大一块,所以 k8s 从一开始开源,设计中有三个组件也是更好保护代码吧。
五 部署主从版本
下面咱们曾经部署了 Redis 的单机版,并通过 Deployment 实现了服务继续运行,接下来来看下主从版本如何部署,其中一个比拟艰难的中央就是如何确定主从的同步关系。
1 StatefulSet
k8s 为有状态利用设计了 StatefulSet 这种控制器,它次要通过上面两个个性来服务有状态利用:
- 拓扑状态:实例的创立程序和编号是程序的,会依照 name-index 来编号,比方 redis-0,redis-1 等。
- 存储状态:能够通过申明应用内部存储,例如云盘等,将数据保留,从而 Pod 重启,从新调度等都能读到云盘中的数据。
上面咱们看下 Redis 的 StatefulSet 的例子:
apiVersion: apps/v1
kind: StatefulSet # 类型为 statefulset
metadata:
name: redis-sfs # app 名称
spec:
serviceName: redis-sfs # 这里的 service 上面解释
replicas: 2 # 定义了两个正本
selector:
matchLabels:
app: redis-sfs
template:
metadata:
labels:
app: redis-sfs
spec:
containers:
- name: redis-sfs
image: redis # 镜像版本
command:
- bash
- "-c"
- |
set -ex
ordinal=`hostname | awk -F '-' '{print $NF}'` # 应用 hostname 获取序列
if [[$ordinal -eq 0]]; then # 如果是 0,作为主
echo > /tmp/redis.conf
else
echo "slaveof redis-sfs-0.redis-sfs 6379" > /tmp/redis.conf # 如果是 1,作为备
fi
redis-server /tmp/redis.conf
接着启动这个 StatefulSet,发现呈现了 redis-sfs-0 和 redis-sfs-1 两个 pod,他们正式依照 name-index 的规定来编号的
➜ ~ kubectl create -f server.yaml
statefulset.apps/redis-sfs created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 65m
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 71m
redis-sfs-0 1/1 Running 0 33s # 依照
redis-sfs-1 1/1 Running 0 28s
接着咱们持续看下主从关系失效了没,查看 redis-sfs-1 的日志,却发现:
➜ ~ kubectl logs -f redis-sfs-1
1:S 05 Nov 2021 08:02:44.243 * Connecting to MASTER redis-sfs-0.redis-sfs:6379
1:S 05 Nov 2021 08:02:50.287 # Unable to connect to MASTER: Resource temporarily unavailable
...
2 Headless Service
仿佛 redis-sfs-1 不意识 redis-sfs-0,起因就在于咱们还没有让它们相互意识,这个相互意识须要应用 k8s 一个服务叫 Headless Service,Service 是 k8s 我的项目中用来将一组 Pod 裸露给外界拜访的一种机制。比方,一个 Deployment 有 3 个 Pod,那么我就能够定义一个 Service。而后,用户只有能拜访到这个 Service,它就能拜访到某个具体的 Pod,个别有两种形式:
- VIP:拜访 VIP 随机返回一个后端的 Pod
- DNS:通过 DNS 解析到后端某个 Pod 上
Headless Service 就是通过 DNS 的形式,能够解析到某个 Pod 的地址,这个 DNS 地址的规定就是:
上面咱们创立集群对应的 Headless Service:
apiVersion: v1
kind: Service
metadata:
name: redis-sfs
labels:
app: redis-sfs
spec:
clusterIP: None # 这里的 None 就是 Headless 的意思,示意会被动由 k8s 调配
ports:
- port: 6379
name: redis-sfs
selector:
app: redis-sfs
再次查看,发现 redis-sfs-1 曾经主备同步胜利了,因为创立 Headless Service 之后,redis-sfs-0.redis-sfs.default.svc.cluster.local 在集群中就是惟一可拜访的了。
➜ ~ kubectl create -f service.yaml
service/redis-sfs created
➜ ~ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24d
redis-sfs ClusterIP None <none> 6379/TCP 33s
➜ ~ kubectl logs -f redis-sfs-1
...
1:S 05 Nov 2021 08:23:31.341 * Connecting to MASTER redis-sfs-0.redis-sfs:6379
1:S 05 Nov 2021 08:23:31.345 * MASTER <-> REPLICA sync started
1:S 05 Nov 2021 08:23:31.345 * Non blocking connect for SYNC fired the event.
1:S 05 Nov 2021 08:23:31.346 * Master replied to PING, replication can continue...
1:S 05 Nov 2021 08:23:31.346 * Partial resynchronization not possible (no cached master)
1:S 05 Nov 2021 08:23:31.348 * Full resync from master: 29d1c03da6ee2af173b8dffbb85b6ad504ccc28f:0
1:S 05 Nov 2021 08:23:31.425 * MASTER <-> REPLICA sync: receiving 175 bytes from master to disk
1:S 05 Nov 2021 08:23:31.426 * MASTER <-> REPLICA sync: Flushing old data
1:S 05 Nov 2021 08:23:31.426 * MASTER <-> REPLICA sync: Loading DB in memory
1:S 05 Nov 2021 08:23:31.431 * Loading RDB produced by version 6.2.6
1:S 05 Nov 2021 08:23:31.431 * RDB age 0 seconds
1:S 05 Nov 2021 08:23:31.431 * RDB memory usage when created 1.83 Mb
1:S 05 Nov 2021 08:23:31.431 # Done loading RDB, keys loaded: 0, keys expired: 0.
1:S 05 Nov 2021 08:23:31.431 * MASTER <-> REPLICA sync: Finished with success
^C
➜ ~ kubectl exec -it redis-sfs-1 -- bash
root@redis-sfs-1:/data# redis-cli -h redis-sfs-0.redis-sfs.default.svc.cluster.local
redis-sfs-0.redis-sfs.default.svc.cluster.local:6379> ping
PONG
redis-sfs-0.redis-sfs.default.svc.cluster.local:6379>
此时无论咱们删除哪个 Pod,它都会依照原来的名称被拉起来,从而能够保障筹备关系,这个例子只是一个 StatefulSet 的示例,剖析下来能够发现,尽管它能够保护主备关系,然而当主挂了的时候,此时备无奈切换上来,因为没有组件能够帮咱们做这个切换操作,一个方法是用 Redis Sentinel,能够参考这个我的项目的配置:k8s-redis-ha-master,如果你的 k8s 较新,须要 merge 此 PR.
六 Operator
尽管有了 StatefulSet,然而这只能对根底版有用,如果想本人定制更加简单的操作,k8s 的解法是 operator,简而言之,operator 就是定制本人 k8s 对象及对象所对应操作的解法。
那什么是对象呢?一个 Redis 集群,一个 etcd 集群,zk 集群,都能够是一个对象,事实中咱们想形容什么,就来定义什么,实际上咱们定一个是 k8s yaml 中的 kind,之前的例子中,咱们应用过 Pod,Deployment,StatefulSet,它们是 k8s 默认实现,当初如果要定义本人的对象,有两个流程:
- 定义对象,比方你的集群默认有几个节点,都有啥组件
- 定义对象触发的操作,当创建对象时候要做什么流程,HA 时候要做什么流程等
operator 的形式是基于编程实现的,能够用多种语言,用的最多的就是 go 语言,通常大家会借助 operator-sdk 来实现,因为有很多代码会主动生成。相当于 operator 会生成框架,而后咱们实现对应的业务逻辑。
1 筹备工作
- 装置好 go 环境
- 装置 operator-sdk
2 初始化我的项目
而后咱们依照官网的 sdk 例子,来一步一步实现一个 memcached 的 operator,这里也能够换成 Redis,然而为了保障和官网统一,咱们就依照官网来创立 memcached operator。
➜ ~ cd $GOPATH/src
➜ src mkdir memcached-operator
➜ src cd memcached-operator
➜ memcached-operator operator-sdk init --domain yangbodong22011 --repo github.com/yangbodong22011/memcached-operator --skip-go-version-check // 这里须要留神 domain 最好是和你在 https://hub.docker.com 的注册名称雷同,因为后续会公布 docker 镜像
Writing kustomize manifests for you to edit...
Writing scaffold for you to edit...
Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.9.2
Update dependencies:
$ go mod tidy
Next: define a resource with:
$ operator-sdk create api
3 创立 API 和 Controller
➜ memcached-operator operator-sdk create api --group cache --version v1alpha1 --kind Memcached --resource --controller
Writing kustomize manifests for you to edit...
Writing scaffold for you to edit...
api/v1alpha1/memcached_types.go
controllers/memcached_controller.go
Update dependencies:
$ go mod tidy
Running make:
$ make generate
go: creating new go.mod: module tmp
Downloading sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1
go get: installing executables with 'go get' in module mode is deprecated.
To adjust and download dependencies of the current module, use 'go get -d'.
To install using requirements of the current module, use 'go install'.
To install ignoring the current module, use 'go install' with a version,
like 'go install example.com/cmd@latest'.
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
...
go get: added sigs.k8s.io/yaml v1.2.0
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
➜ memcached-operator
下面的步骤实际上生成了一个 operator 的框架,接下来咱们首先来定义 memcached 集群都包含啥,将默认实现批改为 Size,示意一个 Memcached 集群中 Memcached 的数量,最初调用 make generate 和 make manifests 来主动生成 deepcopy 和 CRD 资源。
➜ memcached-operator vim api/v1alpha1/memcached_types.go // 批改上面 Memcached 集群的定义
// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
//+kubebuilder:validation:Minimum=0
// Size is the size of the memcached deployment
Size int32 `json:"size"`
}
// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
// Nodes are the names of the memcached pods
Nodes []string `json:"nodes"`}
➜ memcached-operator make generate
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
➜ memcached-operator make manifests
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
➜ memcached-operator
4 实现 Controller
接下来是第二步,定义当创立一个 Memcached 集群时候,具体要干啥。
➜ memcached-operator vim controllers/memcached_controller.go
https://raw.githubusercontent.com/operator-framework/operator-sdk/latest/testdata/go/v3/memcached-operator/controllers/memcached_controller.go //
将 example 换成 yangbodong22011,留神,// 正文中的也要换,理论不是正文,而是一种格局
➜ memcached-operator go mod tidy; make manifests
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
5 公布 operator 镜像
➜ memcached-operator vim Makefile
将 -IMG ?= controller:latest 改为 +IMG ?= $(IMAGE_TAG_BASE):$(VERSION)
➜ memcached-operator docker login // 提前登录下 docker
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: yangbodong22011
Password:
WARNING! Your password will be stored unencrypted in /Users/yangbodong/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
➜ memcached-operator sudo make docker-build docker-push
...
=> => writing image sha256:a7313209e321c84368c5cb7ec820fffcec2d6fcb510219d2b41e3b92a2d5545a 0.0s
=> => naming to docker.io/yangbodong22011/memcached-operator:0.0.1 0.0s
fac03a24e25a: Pushed
6d75f23be3dd: Pushed
0.0.1: digest: sha256:242380214f997d98186df8acb9c13db12f61e8d0f921ed507d7087ca4b67ce59 size: 739
6 批改镜像和部署
➜ memcached-operator vim config/manager/manager.yaml
image: controller:latest 批改为 yangbodong22011/memcached-operator:0.0.1
➜ memcached-operator vim config/default/manager_auth_proxy_patch.yaml
因为国内拜访不了 gcr.io
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 批改为 kubesphere/kube-rbac-proxy:v0.8.0
➜ memcached-operator make deploy
...
configmap/memcached-operator-manager-config created
service/memcached-operator-controller-manager-metrics-service created
deployment.apps/memcached-operator-controller-manager created
➜ memcached-operator kubectl get deployment -n memcached-operator-system // ready 阐明 operator 曾经部署了
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-operator-controller-manager 1/1 1 1 31s
➜ memcached-operator
7 创立 Memcached 集群
➜ memcached-operator cat config/samples/cache_v1alpha1_memcached.yaml
apiVersion: cache.yangbodong22011/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 1
➜ memcached-operator kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
memcached.cache.yangbodong22011/memcached-sample created
➜ memcached-operator kubectl get pods
NAME READY STATUS RESTARTS AGE
memcached-sample-6c765df685-xhhjc 1/1 Running 0 104s
redis 1/1 Running 0 177m
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 3h4m
redis-sfs-0 1/1 Running 0 112m
redis-sfs-1 1/1 Running 0 112m
➜ memcached-operator
能够通过 kubectl logs 来查看 operator 的日志:
➜ ~ kubectl logs -f deployment/memcached-operator-controller-manager -n memcached-operator-system
2021-11-05T09:50:46.042Z INFO controller-runtime.manager.controller.memcached Creating a new Deployment {"reconciler group": "cache.yangbodong22011", "reconciler kind": "Memcached", "name": "memcached-sample", "namespace": "default", "Deployment.Namespace": "default", "Deployment.Name": "memcached-sample"}
至此,咱们的 operator-sdk 的工作临时告一段落。
七 总结
本文介绍了 k8s 的架构,各组件的性能,以及通过一个循序渐进的 Redis 例子介绍了 k8s 中 Pod, Deployment, StatefulSet 的概念,并通过 operator-sdk 演示了一个残缺的 operator 制作的例子。
八 参考资料
[1]《深刻分析 Kubernetes》张磊,CNCF TOC 成员,at 阿里巴巴。
[2]《Kubernetes 权威指南》第五版
[3]《Large-scale cluster management at Google with Borg》
https://research.google/pubs/…
[4] https://www.redhat.com/zh/top…
[5] https://www.infoworld.com/art…
[6] https://landscape.cncf.io/
[7] https://docs.docker.com/deskt…
[8] https://minikube.sigs.k8s.io/…
[9] https://www.aliyun.com/produc…
[10] https://github.com/kubernetes…
[11] https://www.cnblogs.com/chian…
[12] https://github.com/tarosky/k8…
[13] https://sdk.operatorframework…
原文链接
本文为阿里云原创内容,未经容许不得转载。