CKA 004
@ -197,7 +197,7 @@
|
||||
<div class="col-sm-6 col-md-7">
|
||||
<div class="hero-content">
|
||||
<h1 class="text-primary">快速在 Kubernetes 上落地微服务</h1>
|
||||
<p class="text-primary mt-3"><span style="font-weight: 500;">Kuboard 提供:</span>Kubernetes安装脚本,Kubernetes教程、Kubernetes管理面板、SpringCloud在Kubernetes上的实战</p>
|
||||
<p class="text-primary mt-3"><span style="font-weight: 500;">Kubernetes:</span> 安装、教程、管理面板、微服务实战</p>
|
||||
<ul class="pair-btns-list">
|
||||
<li> <a class="cbtn btn-grad-s btn-shadow btn-width"
|
||||
target="_blank"
|
||||
|
||||
@ -27,3 +27,11 @@ description: 熟悉Kuboard_on_Kubernetes_的最佳途径。
|
||||
* 根据微服务上下文查看监控结果
|
||||
|
||||
[在微服务上下文中监控 example](example/monitor.html) <Badge text="alpha" type="warn"/>
|
||||
|
||||
* 了解Kuboard的套件
|
||||
|
||||
[Kuboard套件](addon/)
|
||||
|
||||
* 了解Kuboard与CICD的集成
|
||||
|
||||
[CI/CD集成](cicd/)
|
||||
|
||||
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 581 KiB |
@ -245,3 +245,66 @@ DNS Pod 被暴露为 Kubernetes 中的一个 Service,该 Service 及其 Cluste
|
||||
CoreDNS 的工作方式与 `kubedns` 类似,但是通过插件化的架构构建,因而灵活性更强。自 Kubernetes v1.11 开始,CoreDNS 是 Kubernetes 中默认的 DNS 实现。
|
||||
|
||||
## Internet-to-Service的网络
|
||||
|
||||
前面我们已经了解了 Kubernetes 集群内部的网络路由。下面,我们来探讨一下如何将 Service 暴露到集群外部:
|
||||
* 从集群内部访问互联网
|
||||
* 从互联网访问集群内部
|
||||
|
||||
### 出方向 - 从集群内部访问互联网
|
||||
|
||||
将网络流量从集群内的一个节点路由到公共网络是与具体网络以及实际网络配置紧密相关的。为了更加具体地讨论此问题,本文将使用 AWS VPC 来讨论其中的具体问题。
|
||||
|
||||
在 AWS,Kubernetes 集群在 VPC 内运行,在此处,每一个节点都被分配了一个内网地址(private IP address)可以从 Kubernetes 集群内部访问。为了使访问外部网络,通常会在 VPC 中添加互联网网关(Internet Gateway),以实现如下两个目的:
|
||||
* 作为 VPC 路由表中访问外网的目标地址
|
||||
* 提供网络地址转换(NAT Network Address Translation),将节点的内网地址映射到一个外网地址,以使外网可以访问内网上的节点
|
||||
|
||||
在有互联网网关(Internet Gateway)的情况下,虚拟机可以任意访问互联网。但是,存在一个小问题:Pod 有自己的 IP 地址,且该 IP 地址与其所在节点的 IP 地址不一样,并且,互联网网关上的 NAT 地址映射只能够转换节点(虚拟机)的 IP 地址,因为网关不知道每个节点(虚拟机)上运行了哪些 Pod (互联网网关不知道 Pod 的存在)。接下来,我们了解一下 Kubernetes 是如何使用 iptables 解决此问题的。
|
||||
|
||||
#### 数据包的传递:Node-to-Internet
|
||||
|
||||
下图中:
|
||||
1. 数据包从 Pod 的 network namespace 发出
|
||||
2. 通过 `veth0` 到达虚拟机的 root network namespace
|
||||
3. 由于网桥上找不到数据包目标地址对应的网段,数据包将被网桥转发到 root network namespace 的网卡 `eth0`。在数据包到达 `eth0` 之前,iptables 将过滤该数据包。
|
||||
4. 在此处,数据包的源地址是一个 Pod,如果仍然使用此源地址,互联网网关将拒绝此数据包,因为其 NAT 只能识别与节点(虚拟机)相连的 IP 地址。因此,需要 iptables 执行源地址转换(source NAT),这样子,对互联网网关来说,该数据包就是从节点(虚拟机)发出的,而不是从 Pod 发出的
|
||||
5. 数据包从节点(虚拟机)发送到互联网网关
|
||||
6. 互联网网关再次执行源地址转换(source NAT),将数据包的源地址从节点(虚拟机)的内网地址修改为网关的外网地址,最终数据包被发送到互联网
|
||||
|
||||
在回路径上,数据包沿着相同的路径反向传递,源地址转换(source NAT)在对应的层级上被逆向执行。
|
||||
|
||||
<p style="max-width: 600px">
|
||||
<img src="./network.assets/pod-to-internet.gif" alt="K8S教程_Kubernetes网络模型_数据包的传递_pod-to-internet"/>
|
||||
</p>
|
||||
|
||||
### 入方向 - 从互联网访问Kubernetes
|
||||
|
||||
入方向访问(从互联网访问Kubernetes集群)是一个非常棘手的问题。该问题同样跟具体的网络紧密相关,通常来说,入方向访问在不同的网络堆栈上有两个解决方案:
|
||||
1. Service LoadBalancer
|
||||
2. Ingress Controller
|
||||
|
||||
#### Layer 4:LoadBalancer
|
||||
|
||||
当创建 Kubernetes Service 时,可以指定其类型为 [LoadBalancer](/learning/k8s-intermediate/service/service-types.html#loadbalancer)。 LoadBalancer 的实现由 [cloud controller](https://kubernetes.io/docs/concepts/architecture/cloud-controller/) 提供,cloud controller 可以调用云供应商 IaaS 层的接口,为 Kubernetes Service 创建负载均衡器(如果您自建 Kubernetes 集群,可以使用 NodePort 类型的 Service,并手动创建负载均衡器)。用户可以将请求发送到负载均衡器来访问 Kubernetes 中的 Service。
|
||||
|
||||
在 AWS,负载均衡器可以将网络流量分发到其目标服务器组(即 Kubernetes 集群中的所有节点)。一旦数据包到达节点,Service 的 iptables 规则将确保其被转发到 Service 的一个后端 Pod。
|
||||
|
||||
#### 数据包的传递:LoadBalancer-to-Service
|
||||
|
||||
接下来了解一下 Layer 4 的入方向访问具体是如何做到的:
|
||||
1. Loadbalancer 类型的 Service 创建后,cloud controller 将为其创建一个负载均衡器
|
||||
2. 负载均衡器只能直接和节点(虚拟机沟通),不知道 Pod 的存在,当数据包从请求方(互联网)到达 LoadBalancer 之后,将被分发到集群的节点上
|
||||
3. 节点上的 iptables 规则将数据包转发到合适的 Pod 上 (同 [数据包的传递:Service-to-Pod](#数据包的传递:service-to-pod))
|
||||
|
||||
从 Pod 到请求方的相应数据包将包含 Pod 的 IP 地址,但是请求方需要的是负载均衡器的 IP 地址。iptables 和 `conntrack` 被用来重写返回路径上的正确的 IP 地址。
|
||||
|
||||
下图描述了一个负载均衡器和三个集群节点:
|
||||
1. 请求数据包从互联网发送到负载均衡器
|
||||
2. 负载均衡器将数据包随机分发到其中的一个节点(虚拟机),此处,我们假设数据包被分发到了一个没有对应 Pod 的节点(VM2)上
|
||||
3. 在 VM2 节点上,kube-proxy 在节点上安装的 iptables 规则会将该数据包的目标地址判定到对应的 Pod 上(集群内负载均衡将生效)
|
||||
4. iptables 完成 NAT 映射,并将数据包转发到目标 Pod
|
||||
|
||||
<p style="max-width: 600px">
|
||||
<img src="./network.assets/internet-to-service.gif" alt="K8S教程_Kubernetes网络模型_数据包的传递_internet-to-service"/>
|
||||
</p>
|
||||
|
||||
### Layer 7:Ingress控制器
|
||||
|
||||
@ -46,4 +46,6 @@ CKA证书的含金量如何?考不考这个证完全取决于个人,因为
|
||||
|
||||
[CKA每日一题 - Day 3](./daily/003.html)
|
||||
|
||||
[CKA每日一题 - Day 4](./daily/004.html)
|
||||
|
||||
<JoinCKACommunity/>
|
||||
|
||||
@ -70,3 +70,9 @@ External Attacher 组件负责Attach阶段。Mount阶段由kubelet里的VolumeMa
|
||||
|
||||
</b-card>
|
||||
</b-collapse>
|
||||
|
||||
|
||||
> CKA 考试每日一题系列,全部内容由 [我的小碗汤](https://mp.weixin.qq.com/s/5tYgb_eSzHz_TMsi0U32gw) 创作,本站仅做转载
|
||||
|
||||
|
||||
<JoinCKACommunity/>
|
||||
|
||||
@ -94,11 +94,11 @@ cronjob.batch/pi created
|
||||
|
||||
**kubectl run执行后,到底发生了什么?**有必要看看kubectl源码,入口函数在$GOPATH\src\k8s.io\kubernetes\cmd\clicheck\checkcliconventions.go中
|
||||
|
||||

|
||||

|
||||
|
||||
其中cmd.NewKubectlCommand为构建kubectl以及其子命令行参数。最终的执行业务逻辑的代码都在pkg\kubectl包下面。不同的子命令:apply、run、create入口对应的在pkg\kubectl\cmd下面:
|
||||
|
||||

|
||||

|
||||
|
||||
最重要的o.Run(f, cmd, args)中会对kubectl run传入的参数进行一系列校验,填充默认值。
|
||||
|
||||
@ -106,11 +106,11 @@ cronjob.batch/pi created
|
||||
|
||||
如果设置了expose为true,在372行,同样的调用o.createGeneratedObject生成并创建service。
|
||||
|
||||

|
||||

|
||||
|
||||
方法第649行,根据不同的generator实现生成不同的资源对象。
|
||||
|
||||

|
||||

|
||||
|
||||
run命令对应的generator实现有以下几种,代码位于pkg\kubectl\generate\versioned\generator.go中的DefaultGenerators函数。
|
||||
|
||||
@ -130,7 +130,7 @@ case"run":
|
||||
|
||||
o.createGeneratedObject方法第689行对生成的资源对象向APIServer发送http创建请求。
|
||||
|
||||

|
||||

|
||||
|
||||
具体的kubectl run命令的代码,感兴趣的同学可以进一步深挖,我也会在后续的源码分析系列文章中进行更详细的解析。
|
||||
|
||||
@ -138,3 +138,8 @@ o.createGeneratedObject方法第689行对生成的资源对象向APIServer发送
|
||||
|
||||
</b-card>
|
||||
</b-collapse>
|
||||
|
||||
> CKA 考试每日一题系列,全部内容由 [我的小碗汤](https://mp.weixin.qq.com/s/5tYgb_eSzHz_TMsi0U32gw) 创作,本站仅做转载
|
||||
|
||||
|
||||
<JoinCKACommunity/>
|
||||
|
||||
BIN
t/cka/daily/004.assets/640-20191125195206658
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
t/cka/daily/004.assets/640-20191125195206740
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
t/cka/daily/004.assets/640-20191125195206742
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
t/cka/daily/004.assets/640-20191125195206744
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
t/cka/daily/004.assets/640-20191125195206745
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
t/cka/daily/004.assets/640.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
t/cka/daily/004.assets/image-20191125195308865.png
Normal file
|
After Width: | Height: | Size: 438 KiB |
BIN
t/cka/daily/004.assets/image-20191125200103548.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
t/cka/daily/004.assets/image-20191125200118155.png
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
t/cka/daily/004.assets/image-20191125200130735.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
195
t/cka/daily/004.md
Normal file
@ -0,0 +1,195 @@
|
||||
---
|
||||
vssueId: 166
|
||||
# layout: StepLayout
|
||||
sharingTitle: CKA备考打卡 - 每日一题 - Day 4
|
||||
description: CKA备考打卡 - 每日一题 - Day 4
|
||||
meta:
|
||||
- name: keywords
|
||||
content: Kubernetes,K8S,CKA,Certified Kubernetes Administrator
|
||||
---
|
||||
|
||||
# CKA每日一题 --- Day 4
|
||||
|
||||
<AdSenseTitle/>
|
||||
|
||||
::: tip 考题
|
||||
|
||||
通过命令行,使用nginx镜像创建一个pod并手动调度到节点名为node1121节点上,Pod的名称为cka-1121,答题最好附上,所用命令、创建Pod所需最精简的yaml;如果评论有限制,请把注意点列出,主要需列出手动调度怎么做?
|
||||
|
||||
> 注意:手动调度是指不需要经过kube-scheduler去调度。
|
||||
|
||||
:::
|
||||
|
||||
|
||||
<b-button v-b-toggle.collapse-join-error variant="danger" size="sm" style="margin-top: 1rem;" v-on:click="$sendGaEvent('cka-daily', 'cka-daily', 'CKA每日一题003')">答案及解析</b-button>
|
||||
<b-collapse id="collapse-join-error" class="mt-2">
|
||||
<b-card style="background-color: rgb(254, 240, 240); border: solid 1px #F56C6C;">
|
||||
|
||||
### 答案
|
||||
|
||||
将名称为cka-1121的Pod,调度到节点node1121:
|
||||
|
||||
``` yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: cka-1121
|
||||
labels:
|
||||
app: cka-1121
|
||||
spec:
|
||||
containers:
|
||||
- name: cka-1121
|
||||
image: busybox
|
||||
command: ['sh', '-c', 'echo Hello CKA! && sleep 3600']
|
||||
nodeName: node1121
|
||||
```
|
||||
|
||||
### 解析
|
||||
|
||||
|
||||
|
||||
## 昨日解析
|
||||
|
||||
**官网中调度器地址:**
|
||||
|
||||
https://kubernetes.io/docs/concepts/scheduling/kube-scheduler/
|
||||
|
||||
中文地址: [https://kuboard.cn/learning/k8s-advanced/schedule/](https://kuboard.cn/learning/k8s-advanced/schedule/)
|
||||
|
||||
**调度器命令行参数:**
|
||||
|
||||
https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/
|
||||
|
||||
**调度器kube-scheduler分为预选、优选、pod优先级抢占、bind阶段;**
|
||||
|
||||
**预选**:从podQueue的待调度队列中弹出需要调度的pod,先进入预选阶段,预选函数来判断每个节点是否适合被该Pod调度。
|
||||
|
||||
**优选**:从预选筛选出的满足的节点中选择出最优的节点。
|
||||
|
||||
**pod优先级抢占**:如果预选和优选调度失败,则会尝试将优先级低的pod剔除,让优先级高的pod调度成功。
|
||||
|
||||
**bind**:上述步骤完成后,调度器会更新本地缓存,但最后需要将绑定结果提交到etcd中,需要调用Apiserver的Bind接口完成。
|
||||
|
||||
> 以下k8s源码版本为1.13.2
|
||||
|
||||
我们去查看kube-scheduler源码,调度器通过list-watch机制,监听集群内Pod的新增、更新、删除事件,调用回调函数。指定nodeName后将不会放入到未调度的podQueue队列中,也就不会走上面这几个阶段。具体可以来到pkg\scheduler\factory\factory.go源码中的NewConfigFactory函数中:
|
||||
|
||||

|
||||
|
||||
其中在构建pod资源对象新增、更新、删除的回调函数时,分已被调度的和未被调度的回调。
|
||||
|
||||
**已被调度的回调:**
|
||||
|
||||
已被调度的pod根据FilterFunc中定义的逻辑过滤,nodeName不为空,返回true时,将会走Handler中定义的AddFunc、UpdateFunc、DeleteFunc,这个其实最终不会加入到podQueue中,但需要加入到本地缓存中,因为调度器会维护一份节点上pod列表的缓存。
|
||||
|
||||
```go
|
||||
// scheduled pod cache 已被调度的
|
||||
args.PodInformer.Informer().AddEventHandler(
|
||||
cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj interface{}) bool{
|
||||
switch t := obj.(type) {
|
||||
case*v1.Pod://nodeName不为空,返回true;且返回true时将被走AddFunc、UpdateFunc、DeleteFunc,这个其实最终不会加入到podQueue中
|
||||
return assignedPod(t)
|
||||
case cache.DeletedFinalStateUnknown:
|
||||
if pod, ok := t.Obj.(*v1.Pod); ok {
|
||||
return assignedPod(pod)
|
||||
}
|
||||
runtime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, c))
|
||||
return false
|
||||
default:
|
||||
runtime.HandleError(fmt.Errorf("unable to handle object in %T: %T", c, obj))
|
||||
return false
|
||||
}
|
||||
},
|
||||
Handler: cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.addPodToCache,
|
||||
UpdateFunc: c.updatePodInCache,
|
||||
DeleteFunc: c.deletePodFromCache,
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**未被调度的回调:**
|
||||
|
||||
未被调度的pod根据FilterFunc中定义的逻辑过滤,nodeName为空且pod的SchedulerName和该调度器的名称一致时返回true;返回true时,将会走Handler中定义的AddFunc、UpdateFunc、DeleteFunc,这个最终会加入到podQueue中。
|
||||
|
||||
```go
|
||||
// unscheduled pod queue 没有被调度的
|
||||
args.PodInformer.Informer().AddEventHandler(
|
||||
cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj interface{}) bool{
|
||||
switch t := obj.(type) {
|
||||
case*v1.Pod://nodeName为空且pod的SchedulerName和该调度器的名称一致时返回true;且返回true时将被加入到pod queue
|
||||
return !assignedPod(t) && responsibleForPod(t, args.SchedulerName)
|
||||
case cache.DeletedFinalStateUnknown:
|
||||
if pod, ok := t.Obj.(*v1.Pod); ok {
|
||||
return !assignedPod(pod) && responsibleForPod(pod, args.SchedulerName)
|
||||
}
|
||||
runtime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, c))
|
||||
return false
|
||||
default:
|
||||
runtime.HandleError(fmt.Errorf("unable to handle object in %T: %T", c, obj))
|
||||
return false
|
||||
}
|
||||
},
|
||||
Handler: cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.addPodToSchedulingQueue,
|
||||
UpdateFunc: c.updatePodInSchedulingQueue,
|
||||
DeleteFunc: c.deletePodFromSchedulingQueue,
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**手动调度适用场景:**
|
||||
|
||||
- 调度器不工作时,可设置nodeName临时救急 ;
|
||||
- 可以封装成自己的调度器;
|
||||
|
||||
**扩展点:**
|
||||
|
||||
- 过去几个版本的Daemonset都是由controller直接指定pod的运行节点,不经过调度器。
|
||||
- 直到1.11版本,DaemonSet的pod由scheduler调度才作为alpha特性引入
|
||||
|
||||
|
||||
|
||||
**昨天的留言中,有人提到static Pod,这种其实也属于节点固定,但这种Pod局限很大,比如:不能挂载configmaps和secrets等,这个由Admission Controllers控制。**
|
||||
|
||||
下面简单说一下静态Pod:
|
||||
|
||||
**静态Pod官网说明:**
|
||||
|
||||
https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/
|
||||
|
||||
静态 pod指在特定的节点上直接通过 kubelet守护进程进行管理,APIServer无法管理。它没有跟任何的控制器进行关联,kubelet 守护进程对它进行监控,如果崩溃了,kubelet 守护进程会重启它。Kubelet 通过APIServer为每个静态 pod 创建 镜像 pod,这些镜像 pod 对于 APIServer是可见的(即kubectl可以查询到这些Pod),但是不受APIServer控制。
|
||||
|
||||
具体static pod yaml文件放到哪里,需要在kubelet配置中指定,先找到kubelet配置文件:
|
||||
|
||||
```
|
||||
systemctl status kubelet
|
||||
```
|
||||
|
||||

|
||||
|
||||
找到config.yaml文件:
|
||||
|
||||

|
||||
|
||||
里面指定了staticPodPath:
|
||||
|
||||

|
||||
|
||||
kubeadm安装的集群,master节点上的kube-apiserver、kube-scheduler、kube-controller-manager、etcd就是通过static Pod方式部署的:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
</b-card>
|
||||
</b-collapse>
|
||||
|
||||
> CKA 考试每日一题系列,全部内容由 [我的小碗汤](https://mp.weixin.qq.com/s/5tYgb_eSzHz_TMsi0U32gw) 创作,本站仅做转载
|
||||
|
||||
|
||||
<JoinCKACommunity/>
|
||||