k8s 介绍

  |  

全部的 K8S学习笔记总目录,请点击查看。

接下来学习kubernetes的架构及工作流程,重点介绍如何使用Workload管理业务应用的生命周期,实现服务不中断的滚动更新,通过服务发现和集群内负载均衡来实现集群内部的服务间访问,并通过ingress实现外部使用域名访问集群内部的服务。

k8s 介绍

  1. 纯容器模式的问题

    1. 业务容器数量庞大,哪些容器部署在哪些节点,使用了哪些端口,如何记录、管理,需要登录到每台机器去管理?
    2. 跨主机通信,多个机器中的容器之间相互调用如何做,iptables规则手动维护?
    3. 跨主机容器间互相调用,配置如何写?写死固定IP+端口?
    4. 如何实现业务高可用?多个容器对外提供服务如何实现负载均衡?
    5. 容器的业务中断了,如何可以感知到,感知到以后,如何自动启动新的容器?
    6. 如何实现滚动升级保证业务的连续性?
    7. ……
  2. 容器调度管理平台

    • Docker Swarm
    • Mesos
    • Google Kubernetes

    2017年开始Kubernetes凭借强大的容器集群管理功能, 逐步占据市场,目前在容器编排领域一枝独秀 https://kubernetes.io/

架构图

如何设计一个容器管理平台?需要满足以下几个需求:

  • 集群架构,管理节点分发容器到数据节点
  • 如何部署业务容器到各数据节点
  • N个数据节点,业务容器如何选择部署在最合理的节点
  • 容器如何实现多副本,如何满足每个机器部署一个容器的模型
  • 多副本如何实现集群内负载均衡

分布式系统,一般包含两类角色:管理节点和工作节点

architecture

核心组件

k8s核心主要包括以下几个组件:

  • ETCD:分布式高性能键值数据库,存储整个集群的所有元数据
  • ApiServer: API服务器,集群资源访问控制入口,提供restAPI及安全访问控制
  • Scheduler:调度器,负责把业务容器调度到最合适的Node节点
  • Controller Manager:控制器管理,确保集群资源按照期望的方式运行
    • Replication Controller
    • Node controller
    • ResourceQuota Controller
    • Namespace Controller
    • ServiceAccount Controller
    • Token Controller
    • Service Controller
    • Endpoints Controller
    • ……
  • kubelet:运行在每个节点上的主要的“节点代理”,主要干脏活累活
    • pod 管理:kubelet 定期从所监听的数据源获取节点上 pod/container
      的期望状态(运行什么容器、运行的副本数量、网络或者存储如何配置等等),并调用对应的容器平台接口达到这个状态。
    • 容器健康检查:kubelet 创建了容器之后还要查看容器是否正常运行,如果容器运行出错,就要根据 pod 设置的重启策略进行处理.
    • 容器监控:kubelet 会监控所在节点的资源使用情况,并定时向 master 报告,资源使用数据都是通过 cAdvisor
      获取的。知道整个集群所有节点的资源情况,对于 pod 的调度和正常运行至关重要
  • kube-proxy:维护节点中的iptables或者ipvs规则
  • kubectl: 命令行接口,用于对 Kubernetes 集群运行命令 https://kubernetes.io/zh/docs/reference/kubectl/

根据k8s部署的方式不同,组件的运行方式也是不同的。

  1. 如果是通过kubeadmin方式部署的,组件运行方式是以静态Pod的方式运行的,静态Pod是通过kubelet启动的,kubelet会定期检查/etc/kubernetes/manifests目录下的yaml文件,如果有新的yaml文件,kubelet会启动一个Pod,如果yaml文件被删除,kubelet会删除对应的Pod。
1
2
3
4
## etcd、apiserver、controller-manager、kube-scheduler
# kubectl 是一个二进制的命令行工具,用于与 Kubernetes 集群进行通信。kubectl 通过 RESTful API 与 Kubernetes API Server 交互,从而管理集群。
# k8s 安装完成后,kubectl 会被主动安装到 /usr/local/bin 目录下,可以直接使用。
$ kubectl -n kube-system get po
  1. 如果是二进制方式部署的,组件运行方式通常是以systemd服务的方式运行的,systemd服务的配置文件在/etc/systemd/system目录下,可以通过systemctl命令查看服务的运行状态。

    1
    2
    3
    4
    5
    6
    ## etcd、apiserver、controller-manager、kube-scheduler
    $ systemctl status etcd
    $ systemctl status kube-apiserver
    $ systemctl status kube-controller-manager
    $ systemctl status kube-scheduler
    $ systemctl status kubelet

集群资源

组件是为了支撑k8s平台的运行,安装好的软件。资源是如何去使用k8s的能力的定义。

比如,k8s可以使用Pod来管理业务应用,那么Pod就是k8s集群中的一类资源,集群中的所有资源可以提供如下方式查看:

1
$ kubectl api-resources

namespace 也是 k8s 的一类资源,那么该如何理解 namespace 呢?

namespace 就是 k8s 的命名空间,是集群内一个虚拟的概念,类似于资源池的概念,一个池子里可以有各种资源类型,绝大多数的资源都必须属于某一个 namespace 。集群初始化安装好之后,会默认有如下几个 namespace:

1
2
3
4
5
6
7
$ kubectl get namespaces
NAME STATUS AGE
default Active 84m
kube-node-lease Active 84m
kube-public Active 84m
kube-system Active 84m
kubernetes-dashboard Active 71m
  • 所有 NAMESPACED 的资源,在创建的时候都需要指定 namespace ,若不指定,默认会在 default 命名空间下
  • 相同 namespace 下的同类资源不可以重名,不同类型的资源可以重名
  • 不同 namespace 下的同类资源可以重名
  • 通常在项目使用的时候,我们会创建带有业务含义的 namespace 来做逻辑上的整合

kubectl的使用

类似于docker,kubectl是命令行工具,用于与APIServer交互,内置了丰富的子命令,功能极其强大。 https://kubernetes.io/docs/reference/kubectl/overview/

1
2
3
4
$ kubectl -h
$ kubectl get -h
$ kubectl create -h
$ kubectl create namespace -h

最小调度单元 Pod

docker调度的是容器,在k8s集群中,最小的调度单元是Pod(豆荚)

pod-demo

为什么引入Pod

  • 与容器引擎解耦。平台设计与引擎的具体的实现解耦。
  • 多容器共享网络|存储|进程 空间, 支持的业务场景更加灵活。

pod 定义

使用yaml格式定义Pod

k8s中定义pod的方式一种是通过yaml文件。

下面是一个 mariadb 数据库的 yaml 文档的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Pod
metadata:
name: blog
namespace: test
labels:
component: blog
spec:
containers:
- name: blog
image: harbor.mydomain.com/test/blog:v1
env:
- name: MYSQL_HOST # 指定root用户的用户名
value: "127.0.0.1"
- name: MYSQL_PASSWD
value: "123456"
ports:
- containerPort: 8002
- name: mysql
image: mysql:5.7
args:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
- name: MYSQL_DATABASE
value: "blog"

资源

k8s创建的 pod 等都成为一种资源,资源有对应的种类和版本信息。

apiVersion 含义
alpha 进入K8s功能的早期候选版本,可能包含Bug,最终不一定进入K8s
beta 已经过测试的版本,最终会进入K8s,但功能、对象定义可能会发生变更。
stable 可安全使用的稳定版本
v1 stable 版本之后的首个版本,包含了更多的核心对象
apps/v1 使用最广泛的版本,像Deployment、ReplicaSets都已进入该版本

资源类型与apiVersion对照表

Kind apiVersion
ClusterRoleBinding rbac.authorization.k8s.io/v1
ClusterRole rbac.authorization.k8s.io/v1
ConfigMap v1
CronJob batch/v1beta1
DaemonSet extensions/v1beta1
Node v1
Namespace v1
Secret v1
PersistentVolume v1
PersistentVolumeClaim v1
Pod v1
Deployment v1、apps/v1、apps/v1beta1、apps/v1beta2
Service v1
Ingress extensions/v1beta1
ReplicaSet apps/v1、apps/v1beta2
Job batch/v1
StatefulSet apps/v1、apps/v1beta1、apps/v1beta2

快速获得资源和版本可以在终端通过命令行获取:

1
2
$ kubectl explain pod
$ kubectl explain Pod.apiVersion

pod对外暴露服务

创建和访问Pod

我们创建pod的最终目的是为了访问pod中的服务,所以我们需要将pod中的服务暴露出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## 创建namespace, namespace是逻辑上的资源池
$ kubectl create namespace test

## 使用指定文件创建Pod
$ kubectl create -f pod.yaml

## 查看pod,可以简写po
## 所有的操作都需要指定namespace,如果是在default命名空间下,则可以省略
$ kubectl -n test get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
blog 2/2 Running 0 3m 10.244.1.146 k8s-slave1

## 回顾流程

## 使用 Pod Ip 访问服务 3306 和 8002,此时因为是新启动的环境,所以还无法正常访问
$ curl 10.244.1.120:8002/blog/index/

## 进入容器,执行初始化, 不必到对应的主机执行 docker exec
$ kubectl -n test exec -it blog -c mysql bash
/ # mysql -p123456

$ kubectl -n test exec -it blog -c blog bash
/ # env
/ # python3 manage.py migrate

## 再次访问服务 3306 和 8002
$ curl 10.244.1.120:8002/blog/index/

Infra容器

登录k8s-slave1节点

1
2
3
4
5
6
7
8
9
10
$ docker ps -a | grep blog  ## 发现有三个容器
## 其中包含 mysql 和 blog 程序以及 Infra 容器
## 为了实现 Pod 内部的容器可以通过 localhost 通信,每个 Pod 都会启动 Infra 容器,然后 Pod 内部的其他容器的网络空间会共享该 Infra 容器的网络空间(Docker网络的container模式), Infra 容器只需要 hang 住网络空间,不需要额外的功能,因此资源消耗极低。

## 登录 master 节点,查看 pod 内部的容器 ip 均相同,为 pod ip
$ kubectl -n test exec -it blog -c blog bash
/ # ifconfig

$ kubectl -n test exec -it blog -c mysql bash
/ # ifconfig

pod 操作

k8s 中 pod 容器的命名有一套固定的规则:k8s_<container_name>_<pod_name>_<namespace>_<random_string>

查看pod详细信息

1
2
3
4
5
6
## 查看 pod 调度节点及 pod_ip
$ kubectl -n test get pods -o wide
## 查看完整的 yaml
$ kubectl -n test get po blog -o yaml
## 查看 pod 的明细信息及事件
$ kubectl -n test describe pod blog

Troubleshooting and Debugging

1
2
3
4
5
#进入Pod内的容器
$ kubectl -n <namespace> exec <pod_name> -c <container_name> -it /bin/sh

#查看Pod内容器日志,显示标准或者错误输出日志
$ kubectl -n <namespace> logs -f <pod_name> -c <container_name>

更新服务版本

1
$ kubectl apply -f demo-pod.yaml

删除Pod服务

1
2
3
4
5
#根据文件删除
$ kubectl delete -f demo-pod.yaml

#根据 pod_name 删除
$ kubectl -n <namespace> delete pod <pod_name>

Pod 启动多容器

这里我们使用一个 pod 启动两个容器,并且将镜像仓库设置为私有,需要登录才能拉取镜像,我们使用 secret 来管理镜像仓库的登录信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
## 创建 secret
## 注意:这种方式创建的 secret 只能在当前 namespace 下使用,跨 namespace 无法使用
$ kubectl create secret docker-registry harbor-registry --docker-server=harbor.mydomain.com:10443 --docker-username=admin --docker-password=Harbor12345 --docker-email=admin@mydomain.com

## 查看 secret
$ kubectl get secret harbor-registry -o yaml

## 编写多个 pod 的 yaml 文件
$ cat > pods.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: blog
namespace: test
labels:
component: blog
spec:
containers:
- name: blog-api
image: harbor.mydomain.com:10443/test/blog-backend:v1
env:
- name: DB_HOST
value: "my-mysql"
- name: DB_USER
value: "root"
- name: DB_PASSWD
value: "123456"
ports:
- containerPort: 8000
- name: blog-web
image: harbor.mydomain.com:10443/test/blog-frontend:v1
ports:
- containerPort: 80
imagePullSecrets:
- name: harbor-registry
EOF

## 创建 pod
$ kubectl create -f pods.yaml

## 查看 pod
$ kubectl -n test get pods -o wide

## 访问服务
$ curl http://<pod_ip>:80

## 进入指定容器需要添加 -c 参数指定容器名称,如果不指定,默认进入第一个容器
$ kubectl -n test exec -it blog -c blog-web -- bash

k8s 工作流程

流程图与过程描述

process

  1. 用户准备一个资源文件(记录了业务应用的名称、镜像地址等信息),通过调用APIServer执行创建Pod
  2. APIServer收到用户的Pod创建请求,将Pod信息写入到etcd中
  3. 调度器通过list-watch的方式,发现有新的pod数据,但是这个pod还没有绑定到某一个节点中
  4. 调度器通过调度算法,计算出最适合该pod运行的节点,并调用APIServer,把信息更新到etcd中
  5. kubelet同样通过list-watch方式,发现有新的pod调度到本机的节点了,因此调用容器运行时,去根据pod的描述信息,拉取镜像,启动容器,同时生成事件信息
  6. 同时,把容器的信息、事件及状态也通过APIServer写入到etcd中

架构设计的思考

  1. 系统各个组件分工明确(APIServer是所有请求入口,CM是控制中枢,Scheduler主管调度,而Kubelet负责运行),配合流畅,整个运行机制一气呵成。
  2. 除了配置管理和持久化组件ETCD,其他组件并不保存数据。意味 除ETCD外 其他组件都是无状态的。因此从架构设计上对kubernetes系统高可用部署提供了支撑。
  3. 同时因为组件无状态,组件的升级,重启,故障等并不影响集群最终状态,只要组件恢复后就可以从中断处继续运行。
  4. 各个组件和kube-apiserver之间的数据推送都是通过list-watch机制来实现。
文章目录
  1. 1. k8s 介绍
    1. 1.1. 架构图
    2. 1.2. 核心组件
    3. 1.3. 集群资源
    4. 1.4. kubectl的使用
  2. 2. 最小调度单元 Pod
    1. 2.1. 为什么引入Pod
    2. 2.2. pod 定义
      1. 2.2.1. 使用yaml格式定义Pod
      2. 2.2.2. 资源
    3. 2.3. pod对外暴露服务
      1. 2.3.1. 创建和访问Pod
    4. 2.4. Infra容器
    5. 2.5. pod 操作
      1. 2.5.1. 查看pod详细信息
      2. 2.5.2. Troubleshooting and Debugging
      3. 2.5.3. 更新服务版本
      4. 2.5.4. 删除Pod服务
    6. 2.6. Pod 启动多容器
  3. 3. k8s 工作流程
    1. 3.1. 流程图与过程描述
    2. 3.2. 架构设计的思考