Kubernetes 快速入门

https://lotabout.me/2020/Kubernetes-Introduction/

Kubernetes 快速入门
Kubernetes(简称 k8s[1]) 是一个容器编排系统,本文会实用的角度,讲解一些基本概念,基本操作。

概述
Kubernetes 是希腊语,含义是“舵手”,容器 (container) 也有“集装箱”的含义,k8s 是容器编排系统,就像舵手在开着一艘货轮,轮船上叠满了集装箱,可以说十分贴切。

k8s 在概念上主要分为资源对象和控制对象。资源对象包括容器、应用、配置、网络、存储等;控制对象则是方便管理这些资源而抽象的控制层,如 ReplicaSet 管理多副本, Deployment 管理版本的升级等。

容器(Container)
容器是最小的隔离单位,可以理解成一台虚拟机,一般上面只跑着一个(核心的)程序。也可以直接理解成一个容器就是一个 docker 实例。

实际上 k8s 定义了容器需要实现的接口 (CRI) ,理论上可以有多种实现,如 docker, containerd, CRI-O 等[2],但上手使用并不需要知道这些。

Pod
Pod 在 k8s 的语境下一般不翻译。它的英文含义是“豆荚”,想像一个 container 是一个豆子,一个豆荚里有一到多个豆子,并组装成一个“豆角”。对应地,一个 pod 可以包含一个或多个 container(实际中我还没见多对应多个container 的情形)。

对于我们使用来说,对 pod 最需要了解的有两点:

它是 k8s 最小的调度单位
每个 pod 都有自己的 IP,且 k8s 要保证 pod 间通过这个 IP 可以互相访问
Kubernetes Pod Network
如上图,k8s 需要保证能在 Pod 1 里直接 ping 通 10.1.20.2 这个 IP(Pod 3),尽管它们属于不同物理机。至于如何实现,与容器类似,也有多种实现方式 ,普通用户不需要了解。

跟 pod 相关的指令是平时用得最多的,例如:

kubectl get pods 列出当前 namespace 下的所有 pod (namespace 后面讨论)
kubectl get pod my-pod -o yaml 列出 my-pod 的配置
kubectl log my-pod 列出 my-pod 的所有日志
kubectl log -f --since=10m my-pod 列出 my-pod 近 10 分钟的日志并持续监控
kubectl describe pods my-pod 查看 my-pod 的状态(如重启,上次失败原因等)
kubectl exec -it my-pod bash “登录” my-pod 并执行 bash
ReplicaSet
一般在部署(微)服务时,我们会部署多个副本,一方面水平扩展,能承受更高的压力;另一方面可以防止单点故障影响服务整体的高可用。

ReplicaSet(RS) 就是这种需求的一种抽象概念,一个 ReplicaSet 相当于是一个副本的集合,它是一个控制器(controller)。例如当一个目标为 3 副本的 ReplicaSet 管理的pod 挂了,则这个 ReplicaSet 会启动新的/重启 pod 来满足副本数的需求。

一般我们不会直接和 ReplicaSet 打交道,而是通过 deployment 来做控制。

Deployment
ReplicaSet 可以控制 pod 的副本数,在实际部署中我们还会有更新、回溯等的需求,例如要将pod 更新到新的版本,希望能滚动升级(rolling update),希望先停止一个旧版本的 pod 并启动新版本的 pod,直到所有的 pod 都是新版本的。期间作为一个整体对外的服务(service)不中断。

Deployment 也是一个 controller 概念,通过 yaml 文件的配置让我们很方便控制 pod:部署、更新、回滚、扩展、收缩等等。下面是示例配置文件:

apiVersion: apps/v1 #
kind: Deployment #
metadata: #
name: nginx-deployment #
labels: #
app: nginx #
spec: #
replicas: 3 #–. 相当于 ReplicaSet 的定义
selector: # |
matchLabels: # |
app: nginx #–’
template: #–. 相当于单个 pod 的定义
metadata: # |
labels: # |
app: nginx # |
spec: # |
containers: # |
- name: nginx # |
image: nginx:1.14.2 # |
ports: # |
- containerPort: 80 #–’
通过 kubectl apply -f deployment.yml 可以应用这个配置,k8s 会为我们创建一个 Deployment,一个 ReplicaSet,同时会为我们启动 3 个 pod。可以通过如下命令查看相关状态:

kubectl get deploy 获取当前 namespace 下所有 deployments
kubectl get deploy my-deployment -o yaml 获取 my-deployment 的配置 yaml
kubectl describe deploy my-describe 获取 my-deployment 的一些详细状态
kubectl get rs 获取当前 namespace 下的所有 ReplicaSet,一般用不着
如果要更新 pod 版本,或是改变副本的数量,直接修改之前的 yaml 配置文件,再重新执行 kubectl apply -f deployment.yml 即可。k8s 会自动做出调整,滚动升级或回退。

Service
我们知道每个 pod 有自己的 IP,在更新版本或增减副本数时,一些 pod 可能被杀死,新的 pod 会被启动,那么其它服务如何决定连接到哪个 pod 呢?

Service 就是针对这种需求创建的抽象,对使用方屏蔽内部 pod 变化。使用方将流量发到 Service,而 Service 需要将流量转发到底层的 pod,于是衍生出下面几个问题:

使用方如何定位到 Service?
Service 如何找到目标的 Pod?
流量如何转发?
下面是一个 Service 配置的示例:

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp # 通过标签选择目标 Pod
ports:
- protocol: TCP
port: 80 # Service 暴露的端口
targetPort: 9376 # 转发到 pod 对应的端口
对着配置先回答第二个问题:Service 是通过 selector 配置项指定标签,如果 Deployment 里的 pod 的 labels 字段包含了 Service 中 selector 的标签,则会被选中。

流量转发方式比较多比较复杂,这里不做介绍。剩下的就是 Service 如何定位了。

内部访问
首先,我们注意到配置文件里有 name 字段,这是 Service 的名字。其次,在部署 Service 后 k8s 会为 Service 分配一个虚拟 IP[3],称作 Cluster IP。

在集群的 pod 里可以尝试 telnet 或 telnet 来访问对应的 Service。注意的是这个虚拟 IP 是 ping 不通的,因为它是 ip tables 实现的(也有其它实现方式)。

这里附上一个原理图(当然还有其它实现方式可选择),对细节没兴趣的话可直接跳过:

Kubernetes ClusterIP
当 Pod A 发起的网络请求会被 iptables 重定向到 kube-proxy,而它会监控集群内 Pod 的变化,并将流量转发到对应的 Pod 里,默认转发的方式是 round-robin。

外部访问
很明显 ClusterIP 只在集群内部有办法访问,那集群外要如何访问 Service?

对外暴露 Service 有多种方式,这里只说 NodePort 的方式:

kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
type: NodePort # 类型为 NodePort
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
nodePort: 30336 # 指定 NodePort 端口号
当指定 NodePort 时,k8s 会在集群所有节点(物理机)上开相应的端口,集群外的流量通过这个端口转发到 kube-proxy,再由 kube-proxy 转发到后台的 pod 中,如下图:

Kubernetes Service NodePort
因此在 NodePort 模式下,集群外可以通过 <node_ip>:<node_port> 访问服务。

常用命令
Service 一般我们只关心它的 NodePort,用下面的命令查询:

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-svc ClusterIP 10.42.51.51 80/TCP,81/TCP 10m
my-svc-external NodePort 10.42.51.52 80:30336/TCP 10m
上面的 30336 就是 NodePort。

ConfigMap
有了 Deployment 和 Service,部署服务已经不在话下,那么如何管理服务的配置信息呢?

ConfigMap 就是对配置文件的抽象,也是使用 yaml 配置,也可以类似 pod 一样部署/更新,不过 ConfigMap更新后需要重启 pod 才能应用新的配置。下面是配置示例(取自官方文档):

apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:

property-like keys; each key maps to a simple value

player_initial_lives: 3
ui_properties_file_name: “user-interface.properties”

file-like keys

game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
注意:配置里的 “file-like” 的配置项其实只是用 yaml 的多行语法写了配置的内容, ConfigMap 本身不区分 “property-like” 还是 “file-like”,是由使用方决定的。

通过 kubectl apply -f configmap.yaml 部署,部署后可通过 kubectl get cm -o yaml 查看详情。

那么部署后的 ConfigMap 要如何在 Pod 里引用呢?

apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: game.example/demo-game
env:
# Define the environment variable
- name: PLAYER_INITIAL_LIVES # Notice that the case is different here from the key name in the ConfigMap.
valueFrom:
configMapKeyRef:
name: game-demo # The ConfigMap this value comes from.
key: player_initial_lives # The key to fetch.
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: “/config”
readOnly: true
volumes:
# You set volumes at the Pod level, then mount them into containers inside that Pod
- name: config
configMap:
# Provide the name of the ConfigMap you want to mount.
name: game-demo
可以看到,有几种引用方式:

通过 valueFrom 和 configMapKeyRef 引用单个配置项
通过 Pod 层的 volumes 和 container 层的 volumeMounts 将每个配置项挂载成 Pod 里一个单独的文件。
Namespace
命名空间 (Namespace) 的作用是将隔离各种资源,像虚拟机一样虚拟一个集群。一般情况下不同 namespace 间的资源是不共享的,如 Pod 只能引用同一个 namespace 下的 ConfigMap。

在配置 Deployment、Service 及 ConfigMap 等资源时,可以通过 namespace 字段指定命名空间(需要提前创建),如下例:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: load-balance #<-- load-balance 为提前创建好的 namespace
labels:
app: nginx
#…
常用的命令:

kubectl get ns 列出所有的 namespace
kubectl -n … 在执行其它命令时通过 -n 指定作用于某个 namespace
kubectl --all-namespaces … 在执行其它命令时指定作用于所有 namespace
小结
文章对 k8s 的一些基本概念做了简单的讲解:

container 可以理解成一个 docker 实例,里面跑着一个程序/服务
pod 是 container 的抽象,有自己的 IP,不同 pod 网络互通,与 container 可以是一对一,也可以一对多
ReplicaSet 是对多副本 Pod 的抽象,会自动启动、停止 Pod 来达到目标副本数,一般不直接使用
Deployment 是一个控制概念,会创建、更新 ReplicaSet 从而实现 Pod 的部署、升级、回退、扩缩容等
Service 屏蔽 Pod 细节,提供了统一的、稳定的接口,有自己的虚拟 IP(ClusterIP) 和端口,外部访问需要单独暴露接口(如 NodePort)
ConfigMap 是对配置文件的管理,实现配置项和 Pod 的解耦,配置更新后需要重启 Pod
namespace 是对 k8s 集群的资源做一个隔离
K8s 的概念很多、功能也很丰富,本文是从基础使用的角度做一个介绍,尽量达到“不了解细节,但工作够用”的程度。一些其它的概念(如 volumn)因为博主接触不多,这里也不介绍了。

最后:本人非 k8s 专业人士,文中如果有错误,请在评论里指出,我会进行修正。

参考
https://kubernetes.io/docs/concepts/ 官方教程
Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what? Service 对外暴露的方法区别
kubernetes从入门到放弃3–(网络原理) 相对底层的网络原理
Learn the Kubernetes Key Concepts in 10 Minutes 图文并茂,推荐
首尾字母之间有 8 个字母,所以称为 k8s,类似的还有 i18n(internationalization)。 ↩

https://kubernetes.io/docs/concepts/overview/components/ ↩

严格来说,ExternalName 类型下不会分配 ↩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值