命令式 vs 声明式
命令式
1 2
| # kubectl create -f nginx.yaml # kubectl replace -f nginx.yaml
|
声明式
1
| # kubectl apply -f nginx.yaml
|
- 所谓声明式,即只需要提交一个定义好的API对象来声明所期待的状态即可
- 声明式API允许多个API写端,以PATCH的方式对API对象进行修改,而无需关心本地原始YAML文件的内容
- 声明式API是Kubernetes编排能力赖以生存的核心所在
本质区别
|
kubectl replace |
kubectl apply |
执行过程 |
使用新API对象替换旧API对象 |
执行对旧API对象的PATCH操作 类似:kubectl set image、kubectl edit |
kube-apiserver |
一次只能处理一个写请求,否则可能产生冲突 |
一次能处理多个写请求,具备Merge能力 |
Kubernetes编程范式
使用控制器模式,与Kubernetes里API对象的『增、删、改、查』进行协作,进而完成用户业务逻辑的编写
Istio
Istio是基于Kubernetes的微服务治理框架
架构
- Envoy是一个高性能的C++网络代理,以Sidecar容器的方式运行在每个被治理的应用Pod中
- Pod里所有的容器都共享同一个Network Namespace
- Envoy容器可以通过配置Pod里的iptables规则,接管整个Pod的进出流量
- Control Plane里的Pilot组件,能够通过调用每个Envoy容器的API来对Envoy代理进行配置,从而实现微服务治理
- 对Envoy容器的部署和对Envoy代理的配置,对用户和应用来说是透明的 – 借助于Kubernetes的Dynamic Admission Control
Initializer – Dynamic Admission Control
Admission(编译进APIServer):当一个API对象被提交给APIServer后,被Kubernetes正式处理前的一些初始化工作
myapp-pod.yaml
Pod里目前只有一个用户容器(myapp-container)
myapp-pod.yaml1 2 3 4 5 6 7 8 9 10 11 12 13 14
| apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: myapp-container image: busybox command: - sh - '-c' - echo Hello Kubernetes! && sleep 3600
|
Istio的任务:myapp-pod.yaml被提交给Kubernetes后,对应的API对象里会自动加上Envoy容器的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: myapp-container image: busybox command: - sh - '-c' - echo Hello Kubernetes! && sleep 3600 - name: envoy image: 'lyft/envoy:845747b88f102c0fd262ab234308e9e22f693a1' command: - /usr/local/bin/envoy ...
|
envoy-initializer – 容器定义
- 首先,将Envoy容器本身的定义,以ConfigMap的方式保存在Kubernetes中
- ConfigMap的data字段是一个Pod对象的一部分定义
- Initializer控制器的任务
- 把Envoy相关的字段自动添加到用户提交的Pod的API对象里
- 但用户提交的Pod里本来就有containers字段和volumes字段 – PATCH API(声明式API最主要的能力)
envoy-initializer.yaml1 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
| apiVersion: v1 kind: ConfigMap metadata: name: envoy-initializer data: config: | containers: - name: envoy image: lyft/envoy:845747db88f102c0fd262ab234308e9e22f693a1 command: ["/usr/local/bin/envoy"] args: - "--concurrency 4" - "--config-path /etc/envoy/envoy.json" - "--mode serve" ports: - containerPort: 80 protocol: TCP resources: limits: cpu: "1000m" memory: "512Mi" requests: cpu: "100m" memory: "64Mi" volumeMounts: - name: envoy-conf mountPath: /etc/envoy volumes: - name: envoy-conf configMap: name: envoy
|
envoy-initializer:0.0.1 – 控制器
- envoy-initializer:0.0.1为自定义控制器
- 控制循环:不断获取实际状态,然后与期望状态对比,以此作为依据来决定下一步的操作
- Initializer控制器:实际状态(用户新创建的Pod),期望状态(该Pod里被添加了Envoy容器的定义)
1 2 3 4 5 6 7 8 9
| for { pod := client.GetLatestPod() if !isInitialized(pod) { doSomething(pod) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| func doSomething(pod) { cm := client.Get(ConfigMap, "envoy-initializer")
newPod := Pod{} newPod.Spec.Containers = cm.Containers newPod.Spec.Volumes = cm.Volumes
patchBytes := strategicpatch.CreateTwoWayMergePatch(pod, newPod)
client.Patch(pod.Name, patchBytes) }
|
将Initializer作为Pod部署
envoy-initializer-pod.yaml1 2 3 4 5 6 7 8 9 10 11
| apiVersion: v1 kind: Pod metadata: labels: app: envoy-initializer name: envoy-initializer spec: containers: - name: envoy-initializer image: 'envoy-initializer:0.0.1' imagePullPolicy: Always
|
配置需要进行Initialize的资源
envoy-config
Kubernetes要对所有的Pod进行Initialize操作,名称为envoy-initializer(envoy.initializer.kubernetes.io)
envoy-config.yaml1 2 3 4 5 6 7 8 9 10 11 12 13
| apiVersion: admissionregistration.k8s.io/v1alpha1 kind: InitializerConfiguration metadata: name: envoy-config initializers: - name: envoy.initializer.kubernetes.io rules: - apiGroups: - '' apiVersions: - v1 resources: - pods
|
- 一旦该InitializerConfiguration被创建,所有新创建的Pod的metadata.initializers.pending上会出现该Initializer的名字
- 该Initializer会借助Pod的metadata.initializers.pending来判断该Pod有没有执行过自己所负责的初始化操作(
isInitialized()
)
- 当Initializer完成了初始化操作后,要负责清除掉metadata.initializers.pending标志
1 2 3 4 5 6 7 8 9 10
| apiVersion: v1 kind: Pod metadata: initializers: pending: - name: envoy.initializer.kubernetes.io name: myapp-pod labels: app: myapp ...
|
在Pod的Annotation声明使用了某个Initializer,如使用了envoy-initializer
1 2 3 4 5 6
| apiVersion: v1 kind: Pod metadata annotations: "initializer.kubernetes.io/envoy": "true" ...
|
参考资料
- 深入剖析Kubernetes