K8S
对 K8S 的理解
Kubernetes(K8S)是一个开源容器编排系统,它可以帮助用户快速部署、管理和扩展应用程序。K8S有助于将多个容器打包成一个应用,它为容器提供自动化运维、故障恢复、负载均衡、存储集成和许多其他功能。
K8S的常见使用场景有:
- 容器编排:K8S可以自动部署和扩缩容多个容器集群,使用者可以控制容器的部署和管理,非常适用于开发和运行分布式应用。
- 扩展应用:K8S可以帮助用户快速扩展应用,它可以自动添加新的容器来响应用户的需求,可以快速响应扩展应用的需求。
- 故障恢复:K8S可以帮助用户快速恢复容器,当某个容器出现故障时,K8S可以自动重启容器使之恢复正常。
- 网络管理:K8S可以帮助用户快速管理网络,它可以自动配置网络,确保容器之间的网络通信正常。
一些介绍一些开发需要了解的常见 K8S 技术。
容器编排
Deployment
K8S Deployment 可以帮助用户实现自动化的部署,更新和管理应用程序,从而提高部署的效率和可靠性。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Deployment 是常见的工作负载,Deployment 资源文件可以指定容器具体的镜像以及它的启动命令,通过 replicas 字段可以指定运行的 pod 数目,Deployment 会自动调度 pod 运行,我们可以很方便的扩缩容 pod 资源,并且 k8s 会自动重启出错的 pod,实现全自动化的运维。
Statefulset
Statefulset 可以看作特殊的 Deployment,由 Deployment 创建的 pod 是无状态的,其名称、网络都是随机的,而 Statefulset 则带有状态。
K8S StatefulSet 和 Deployment 的区别主要有以下几点:
- StatefulSet 是一种特殊的 Deployment,它提供了有状态的 Pod 管理,而 Deployment 只支持无状态的 Pod 管理。
- StatefulSet 会为每个 Pod 分配一个唯一的标识符(0~N),而 Deployment 不会。
- StatefulSet 会保证多副本的 Pod 的按照标识符的顺序启动和停止,而 Deployment 不会。
- StatefulSet 支持持久化存储,相同的 Pod 重新部署后其文件内容(通过 PVC)、网络标识符均不变,而 Deployment 不支持。
Daemonset
K8S 集群可能部署在多个节点上,Daemonset 提供这样一种编排方式,如果一个 Pod 以 Daemonset 模式部署,则其会被部署在每一个节点上。
在我实习的时候就曾用到了 Daemonset,部署一个日志收集服务收集所有节点产生的日志文件,使用 Daemonset 非常合适,使用 subPathExpr 能挂在一个和 pod name 相关联的目录,然后就可以对这个目录进行监听。
Job 与 CronJob
Job 是 K8S 中的一个特殊的资源,Job 是只运行一次的程序,通常可以使用 Job 来完成一些初始化或者收集日志等工作,CronJob 支持定时调度,请注意如果 Job 异常退出,K8S 会持续调度 Job 直到它们正常退出。
持久化
这里只介绍三种常见的卷,在 Pod 中引入卷的方式为:通过 volumes 声明卷,在 volumeMounts 将卷挂载到某个目录下。
emptyDir
emptyDir 是一个临时卷,卷最初是空的,当 Pod 被创建是,卷也被创建,多个 Pod 可以共享卷的内容,这是该卷的主要用途,当 Deployment 或所有 Pod 被移除时,卷会被自动删除。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir:
sizeLimit: 500Mi
configMap
ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。
使用 ConfigMap 的用途是将配置与程序分离开来,以便能随时更新配置数据,例如使用 ConfigMap 保存一段脚本,Job 运行这个脚本,这样我们可以随时更改 ConfigMap 来完成热更新的目的。
例如一个初始化数据库的 Job,初始化 SQL 可能随时更改,这时候就可以使用 ConfigMap,这个例子配置如下:
#configmap:sql-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-initdb-sql-config
namespace: default
data: # key - val
initdb.sql: |
you sql here
#Jod:init-sql.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: mysql-init
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: init-sql
image: monasca/mysql-init
volumeMounts:
- name: mysql-initdb
mountPath: app/config # 挂载
command: ["sh","-c","mysql -u root -pxxx -P3306 -h host < app/config/init.sql"]
volumes:
- name: mysql-initdb
configMap:
name: mysql-initdb-sql-config
items:
- key: initdb.sql
path: init.sql # 意思是 configMap 中 key 为 initdb.sql 的 val 被写入到 init.sql 文件中
restartPolicy: OnFailure
除了作为文件外,ConfigMap 还能作为 ENV 被引入。
hostPath
允许将主机目录挂载到容器内,类似于 docker -v,持久化存储。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# 宿主上目录位置
path: /data
# 此字段为可选
type: DirectoryOnCreate
网络管理
Service
Service 将多个 Pod 看作一组服务,服务可以在集群内外暴露,并提供负载均衡服务。Service 是微服务通信的重要组件,一个简单的 Service 资源如下:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80 # 访问虚拟 IP 端口
targetPort: 9376 # POD 端口
nodePort: 8080 # 可选的,访问任意一个节点使用的 IP
Service 通过 selector 绑定 Pod,例如所有 key 为 app,val 为 MyApp 的 Pod 都被绑定在 my-service 之上,这些 Pod 的服务名称均为 my-service。
Service 会为这一组服务分配一个内部的 Cluster IP(虚拟的,只能在内部访问),通过这个 IP:PORT 即可负载均衡到某个 Pod 中

Service 支持 NodePort 方式访问服务,这样可以将一个 Service 与 Node 的一个端口绑定,访问 NodeIP:PORT 也可以访问服务。
Service 具体是由 kube-proxy 实现的,这是运行在每一个节点上的网络代理进程,它监听 api-server 的信息,对每一个 Service 创建转发表,并进行负载均衡,默认的方式是轮询。
DNS
DNS 是由 K8S 插件实现的,它抽象了一个服务注册与服务发现功能,安装了这个插件之后,每一个 Service 都会被注册,同一个命名空间内的服务可以通过 Service 的 name 直接访问,例如 http://my-service:80,不同命名空间的服务需要加上命名空间后缀访问。
我实习的公司私有化部署就是使用了 DNS,这样不需要改变公有化中服务注册服务发现的代码逻辑,写一个轻量级的注册中心,根据原来的服务名返回一个 DNS 给服务即可。
Ingress
Ingress 可以看作是 Service 的 Service,Ingress 是一种路由规则,用于将外部 HTTP/HTTPS 请求路由到集群内部的服务。它使用一组规则,根据请求的主机名,路径或其他属性,将请求路由到不同的 Service。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-example
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
- http: ...
上诉例子中定义了一个 Ingress 资源,任何以 /testpath 为前缀的路径都将会被路由到 my-service 服务中去。Ingress 之间也支持互相转发,可以通过 ingressClassName 来互相引用。
Ingress 只是一系列规则,我们需要额外的实现来进行转发,这就是 Ingress Controller。
ingress-controller 并不是k8s自带的组件,实际上ingress-controller只是一个统称,用户可以选择不同的ingress-controller实现,目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,具体可以参考官方文档。但是不管哪一种ingress-controller,实现的机制都大同小异,只是在具体配置上有差异。
一般来说,ingress-controller 的形式都是一个pod,里面跑着daemon程序和反向代理程序。daemon负责不断监控集群的变化,根据 ingress 对象生成配置并应用新配置到反向代理,比如 nginx-ingress 就是动态生成 nginx 配置,动态更新 upstream,并在需要的时候reload 程序应用新配置。