大佬教程收集整理的这篇文章主要介绍了第一篇:《Kubernetes 入门介绍》,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
前言:本文是一篇 kubernetes(下文用 k8s 代替)的入门文章c;将会涉及 k8s 的技术历史背景、架构、集群搭建、一个 redis 的例子c;以及如何使用 operator-sdk 开发 operator 的教程。在文章过程中c;会穿插引出 Pod、Deployment、StatefulSet 等 k8s 的概念c;这些概念通过例子引出来c;更容易理解和实践。文章参考了很多博客以及资料c;部分在文章中用链接指示c;部分放在最后参考资料部分。
虚拟机
c;开发结束后c;执行 cf push myapp
c;就可以一键部署起整个应用c;其存在一个主要问题:即开发人员需要为不同的平台维护多个脚本c;经常是本地运行好好的应用c;到了线上就启动不了c;还需要修改参数等c;调通整个上线过程非常的麻烦。docker build image
c;可以直接一键在远端平台上部署起来docker run image
。容器编排
概念的项目c;眼看着 Docker 公司就要一统江湖了c;但是一些仍旧存在的本质问题:调度问题c;还是渐渐显露了出来c;这里文章后面说到 k8s Pod 概念的时候也会讲。介绍完背景c;接下来我们看下 k8s 集群的架构c;这张图是 k8s 官网的架构图。从左到右c;分为两部分c;第一部分是 Master 节点(也就是图中的 Control Plane)c;第二部分是 Node 节点。
@H_281_6@master 节点一般包括四个组件c;apiserver、scheduler、controller-managerc;他们分别的作用是啥呢:Node 节点一般也包括三个组件c;dockerc;kube-proxyc;kubelet
总结一下就是 k8s 集群是一个由两部分组件 Master 和 Node 节点组成的架构c;其中 Master 节点是整个集群的大脑c;Node 节点来运行 Master 节点调度的应用c;我们后续会以一个具体的调度例子来解释这些组件的交互过程。
上面说完了 k8s 集群中有哪些组件c;接下来我们先看下如何搭建一个 k8s 集群c;有以下几种方法:
本文后面的例子均采用本地 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"}
下面我们从一个实际的需求出发c;来看看如何在 k8s 上部署 redis 服务。
如果我们想在 k8s 上部署一个单机版本 redisc;我们执行下面的命令即可:
➜ ~ 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 进程启动的服务c;但是在 k8s 中c;它叫 Pod。
我们来讲下第一个 k8s 的概念 Podc;Pod 是 k8s 中最小的调度单元c;一个 Pod 中可以包含多个 Dockerc;这些 Docker 都会被调度到同一台 Node 上c;这些 Docker 共享 NetWork Namespacec;并且可以声明共享同一个 Volume 来共享磁盘空间。
这样的好处是什么呢?其实在真实的世界中c;很多应用是有部署在同一台机器的需求的c;比如 redis 日志采集插件要采集日志c;肯定需要和 redis 部署在同一台机器上才能读到 redis 的日志c;我们前面讲述背景的时候说到了 Docker Swarm 存在一些问题c;其中之一就是它只是基于 Docker 调度c;虽然也可以设置亲和度让两台 Docker 调度在同一个机器上c;但是因为不能一起调度c;所以会存在一个Docker 提前被调度到了一个资源少的机器上c;从而导致第二个 Docker 调度失败。
例如我们一共有 2 台容器c;A和Bc;分别为 redis 和 日志采集组件c;各需要 2g 内存c;现在有两台 nodec;node1 3.5 内存c;node2 4G内存c;在 Docker swarm 的调度策略下c;先调度 redisc;有可能被调度到了 node1 上c;接下来再来调度日志采集组件c;发现 node1 只有 1.5g 内存了c;调度失败。但是在 k8s 中c;调度是按照 pod 来调度的c;两个组件在一个 pod 中c;调度就不会考虑 node1。
虽然 Pod 已经可以运行 redis 服务了c;但是他不具备高可用性
c;因为一旦一个 Pod 与一个节点(Node)绑定c;除非这个绑定发生了变化(pod.spec.node 字段被修改)c;否则它永远都不会离开这个节点c;这也就意味着c;如果这个宿主机宕机了c;这个 Pod 也不会主动迁移到其他节点上去。为了让服务可以一直在c;需要使用 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 创建的新的 Deploymentc;为了验证高可用
c;我们把 redis
和 redis-deployment-866c4c6cf9-8z8k5
都删掉看会发生什么。
➜ ~ kubectl @R_801_9421@e pod redis redis-deployment-866c4c6cf9-8z8k5
pod "redis" @R_801_9421@ed
pod "redis-deployment-866c4c6cf9-8z8k5" @R_801_9421@ed
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 10s
➜ ~
redis
已经消失了c;但是redis-deployment-866c4c6cf9-zskkb
换了个名字又出现了!
Deployment 可以定义多副本个 Podc;从而为应用提供迁移能力c;如果单纯使用 Podc;实际上当应用被调度到某台机器之后c;机器宕机应用也无法自动迁移c;但是使用 Deploymentc;则会调用 ReplicaSet(一种控制器) 来保证当前集群中的应用副本数和指定的一致。
k8s 中c;可以使用 kubectl 来创建简单的服务c;但是还有一种方式是对应创建复杂的服务的c;就是提供 yaml 文件。例如上面的创建 Pod 的命令c;我们可以用下面的 yaml 文件替换c;执行 kubectl create 之后c;可以看到 redis Pod 又被创建了出来。
➜ ~ cat pod.yaml
apiVersion: v1
kind: Pod
@H_475_175@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
下面我们看下kubectl create deployment redis-deployment --image=redis
下发之后c;k8s 集群做了什么。
List-Watch
模型c;List 是拿到当前的状态c;Watch 是拿到期望状态c;然后 k8s 集群会致力于将当前状态达到达期望状态。这些步骤中c;apiserver 的作用是@R_48_9772@的c;所以说上接其余组件c;下连 ETCDc;但是 apiserver 是可以横向扩容的c;然后通过负载均衡c;倒是 ETCD 在 k8s 架构中成了瓶颈。
最开始看这架构的时候c;会想着为啥 apiserver, scheduler, controller-manager 不合成一个组件c;其实在 Google Borg 中c;borgmaster 就是这样的c;功能也是这些功能c;但是合在了一起c;最后他们也发现集群大了之后 borgmaster 会有些性能上的问题c;包括 kubelet 的心跳就是很大一块c;所以 k8s 从一开始开源c;设计中有三个组件也是更好维护代码吧。
上面我们已经部署了 redis 的单机版c;并通过 Deployment 实现了服务持续运行c;接下来来看下主从版本如何部署c;其中一个比较困难的地方就是如何确定主从的同步关系。
k8s 为有状态应用设计了 StatefulSet 这种控制器c;它主要通过下面两个特性来服务有状态应用:
下面我们看下 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 # 如果是 0c;作为主
echo > /tmp/redis.conf
else
echo "slaveof redis-sfs-0.redis-sfs 6379" > /tmp/redis.conf # 如果是 1c;作为备
fi
redis-server /tmp/redis.conf
接着启动这个 StatefulSetc;发现出现了 redis-sfs-0 和 redis-sfs-1 两个 podc;他们正式按照 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
接着我们继续看下主从关系生效了没c;查看 redis-sfs-1 的日志c;却发现:
➜ ~ 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
...
似乎 redis-sfs-1 不认识 redis-sfs-0c;原因就在于我们还没有让它们互相认识c;这个互相认识需要使用 k8s 一个服务叫 Headless service
c;service 是 k8s 项目中用来将一组 Pod 暴露给外界访问的一种机制。比如c;一个 Deployment 有 3 个 Podc;那么我就可以定义一个 service。然后c;用户只要能访问到这个 servicec;它就能访问到某个具体的 Podc;一般有两种方式:
Headless service 就是通过 DNS 的方式c;可以解析到某个 Pod 的地址c;这个 DNS 地址的规则就是:
<pod-name>.<svc-name>.<namespace>.svc.cluster.local
apiVersion: v1
kind: service
metadata:
name: redis-sfs
labels:
app: redis-sfs
spec:
clusterIP: None # 这里的 None 就是 Headless 的意思c;表示会主动由 k8s 分配
ports:
- port: 6379
name: redis-sfs
SELEctor:
app: redis-sfs
再次查看c;发现 redis-sfs-1 已经主备同步成功了c;因为创建 Headless service 之后c;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>
此时无论我们删除哪个 Podc;它都会按照原来的名称被拉起来c;从而可以保证准备关系c;这个例子只是一个 StatefulSet 的示例c;分析下来可以发现c;虽然它可以维护主备关系c;但是当主挂了的时候c;此时备无法切换上来c;因为没有组件可以帮我们做这个切换操作c;一个办法是用 redis SenTinelc;可以参考这个项目的配置:k8s-redis-ha-masterc;如果你的 k8s 较新c;需要 merge 此 PR.
虽然有了 StatefulSetc;但是这只能对基础版有用c;如果想自己定制更加复杂的操作c;k8s 的解法是 operatorc;简而言之c;operator 就是定制自己 k8s 对象及对象所对应操作的解法。
那什么是对象呢?一个 redis 集群c;一个 etcd 集群c;zk 集群c;都可以是一个对象c;现实中我们想描述什么c;就来定义什么c;实际上我们定一个是k8s yaml 中的 kindc;之前的例子中c;我们使用过 Podc;Deploymentc;StatefulSetc;它们是 k8s 默认实现c;现在如果要定义自己的对象c;有两个流程:
operator 的方式是基于编程实现的c;可以用多种语言c;用的最多的就是 go 语言c;通常大家会借助 operator-sdk 来完成c;因为有很多代码会自动生成。相当于 operator 会生成框架c;然后我们实现对应的业务逻辑。
然后我们按照官网的 sdk 例子c;来一步一步实现一个 memcached 的 operatorc;这里也可以换成 redisc;但是为了保证和官网一致c;我们就按照官网来创建 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 的注册名称相同c;因为后续会发布 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
➜ 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 的框架c;接下来我们首先来定义 memcached 集群都包括啥c;将默认实现修改为 Sizec;表示一个 Memcached 集群中 Memcached 的数量c;最后调用 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
接下来是第二步c;定义当创建一个 Memcached 集群时候c;具体要干啥。
➜ 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 换成 yangbodong22011c;注意c;// 注释中的也要换c;实际不是注释c;而是一种格式
➜ 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
➜ 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/@R_801_10846@ne/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
➜ 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
➜ 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"}
至此c;我们的 operator-sdk 的任务暂时告一段落。
本文介绍了 k8s 的架构c;各组件的功能c;以及通过一个循序渐进的 redis 例子介绍了 k8s 中 Pod, Deployment, StatefulSet 的概念c;并通过 operator-sdk 演示了一个完整的 operator制作的例子。
[1] 《深入剖析Kubernetes》张磊c;CNCF TOC 成员c;@阿里云 [2] 《Kubernetes 权威指南》第五版 [3] 《Large-scale cluster management at Google with Borg》
以上是大佬教程为你收集整理的第一篇:《Kubernetes 入门介绍》全部内容,希望文章能够帮你解决第一篇:《Kubernetes 入门介绍》所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。