在K8S实战笔记–3中,我们已对YAML文件的编写以及容器生命周期有了一定的了解,现将从部署应用程序开始,从编写YAML与操作Kuboard的角度来学习对Kubernetes的具体操作。
1. 部署应用程序
本次实验以部署Nginx为例。我们可以使用传统的YAML方法部署应用,或直接使用Kuboard借助图形化界面进行部署。
1.1 使用YAML
kubectl api-versions
可以从查看当前K8S集群支持的apiVersion版本。首先创建YAML:
apiVersion: apps/v1 #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本
kind: Deployment #该配置的类型,我们使用的是 Deployment
metadata: #译名为元数据,即 Deployment 的一些基本属性和信息
name: nginx-deployment #Deployment 的名称
labels: #标签,可以灵活定位一个或多个资源,其中key和value均可自定义,可以定义多组,目前不需要理解
app: nginx #为该Deployment设置key为app,value为nginx的标签
spec: #这是关于该Deployment的描述,可以理解为你期待该Deployment在k8s中如何使用
replicas: 1 #使用该Deployment创建一个应用程序实例
selector: #标签选择器,与上面的标签共同作用,目前不需要理解
matchLabels: #选择包含标签app:nginx的资源
app: nginx
template: #这是选择或创建的Pod的模板
metadata: #Pod的元数据
labels: #Pod的标签,上面的selector即选择包含标签app:nginx的Pod
app: nginx
spec: #期望Pod实现的功能(即在pod中部署)
containers: #生成container,与docker中的container是同一种
- name: nginx #container的名称
image: nginx:1.7.9 #使用镜像nginx:1.7.9创建container,该container默认80端口可访问
使用kubectl get pods
可以查看Pod状态;使用kubectl describe pod nginx
可以查看Pod运行情况:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m default-scheduler Successfully assigned default/nginx-deployment-746fbb99df-29mpn to k8sworker
Normal Pulling 2m kubelet Pulling image "nginx:1.7.9"
Normal Pulled 2m kubelet Successfully pulled image "nginx:1.7.9" in 13.126408886s
Normal Created 2m kubelet Created container nginx
Normal Started 2m kubelet Started container nginx
可以看到包括拉取镜像在内的一系列操作都已完成。由于我们创建的控制器类型是Deployment,所以我们也可以使用kubectl get deployments
来查看当前已有的Deployment。现在我们已经有了一个运行在default
名称空间下的Pod,里面运行了一个Nginx容器,登录Kuboard,可以查看当前Pod的详细信息。
1.2 使用Kuboard
使用Web图形化界面部署应用相比YAML而言要简单许多。
- 登录Kuboard,选择要操作的集群;
- 使用管理员身份(
ServiceAccount kuboard-admin
)访问集群,选择切换到default
名称空间下; - 在“常用操作”中选择创建工作负载,在“基本信息”中选择控制器类型为Deployment,将服务分层选为展现层,填入服务名称,在此处我们可以定义弹性伸缩范围,本次选择1个副本;
- 在“容器信息”中,我们可以定义容器名、镜像、拉取策略、命令、环境变量以及容器端口等;
- 保存并应用
此时我们通过Kuboard创建了一个运行的Nginx的工作负载,在Kuboard中,点击代理,可以直接测试部署结果。
2. 公布应用程序
我们已经了解到,Service通过将请求路由到一组Pod的方式,动态地调度Pod。在部署应用程序中,我们通过含有标签app = Nginx
的Deployment创建出了含有此标签的Pod,接下来为了使此Pod包含的服务可以被外界访问到,需要创建一个Service来链接此Pod。
2.1 使用YAML
Service的类型有以下三种,主要体现在YAML的type
字段中:
ClusterIP
:type的默认值。这种类型的发布只在集群的内部IP上公布服务,只能在集群内部访问到;NodePort
:在集群中每个节点的同一端口上公布服务,可以通过节点IP + 端口号的方式访问服务,此时集群内也可以访问该服务;LoadBalancer
:使用云主机作为服务的访问地址,此时集群内以及端口发布仍然可用。
创建Service的YAML:
apiVersion: v1
kind: Service
metadata:
name: nginx-service #Service 的名称
labels: #Service 自己的标签
app: nginx #为该 Service 设置 key 为 app,value 为 nginx 的标签
spec: #这是关于该 Service 的定义,描述了 Service 如何选择 Pod,如何被访问
selector: #标签选择器
app: nginx #选择包含标签 app:nginx 的 Pod
ports:
- name: nginx-port #端口的名字
protocol: TCP #协议类型 TCP/UDP
port: 80 #集群内的其他容器组可通过 80 端口访问 Service
nodePort: 30090 #通过任意节点的 32600 端口访问 Service
targetPort: 80 #将请求转发到匹配 Pod 的 80 端口
type: NodePort #Serive的类型,ClusterIP/NodePort/LoaderBalancer
此时我们已通过端口发布的方式向外界公布了我们的服务,使用kubectl get serivces
可以查看集群中已存在的Service。在开放云主机端口后,我们也可以通过节点IP + 端口号的方式在浏览器查看到服务的运行情况。
2.2 使用Kuboard
- 在工作负载中选择“编辑”;
- 在“服务/应用路由”栏下填写服务端口、节点端口与容器端口;
- 保存
3. 服务伸缩与滚动更新
通常我们为增加应用的可用性,防止流量增加时出现服务繁忙的状况,我们需要设置服务的弹性伸缩。在K8S中,服务的伸缩通过改变Pod的副本数目来实现。
在使用YAML时,我们只需要在详细描述中修改副本数目即可:
spec:
replicas: 3
若使用Kuboard,可以在相应的工作负载中查看当前副本数目的图形化表示,使用加减按钮可以便捷地修改副本数目。在拥有大于1个副本数目时,我们就可以实施对应用程序的滚动更新。我们在之前的学习中了解到通过Deployment管理RS可以很好地避免由不同机制引起的兼容性问题,其中就包括滚动更新,在实际的应用场景中,用户有时要求应用程序是随时可用的,这需要开发与运维人员将应用程序的更新迭代分为多次进行。在K8S中,Service将流量均衡到符合副本数目的旧版本Pod中,在更新YAML中的镜像版本或在Kuboard中配置“调整镜像版本”后,K8S会使用新版本的Pod逐个替换旧版本的Pod,即每次新建一个新版本Pod并删除一个旧版本Pod,保证Pod的最大新建数目与最大不可用数目都为1,直到Pod全部更新完成为止。回滚版本也是相同的原理,此时无需考虑停机。
4. 节点描述
在K8S中,一台节点(node
)代表一个工作机器,每个节点都由master组件管理,包含并运行了Pod所需的服务。以下以master节点为例,描述节点相关内容。
节点通过kubelet收集这些信息,可以使用kubectl describe
命令查看,输出如下:
[root@k8smaster ~]# kubectl describe node k8smaster
Name: k8smaster
Roles: master
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=k8smaster
kubernetes.io/os=linux
node-role.kubernetes.io/master=
Annotations: kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
node.alpha.kubernetes.io/ttl: 0
projectcalico.org/IPv4Address: 172.19.191.190/20
projectcalico.org/IPv4IPIPTunnelAddr: 10.100.16.128
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Mon, 17 Jul 2023 10:16:53 +0800
Taints: node-role.kubernetes.io/master:NoSchedule
Unschedulable: false
Lease:
HolderIdentity: k8smaster
AcquireTime: <unset>
RenewTime: Sun, 23 Jul 2023 15:12:11 +0800
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
NetworkUnavailable False Sun, 23 Jul 2023 15:08:46 +0800 Sun, 23 Jul 2023 15:08:46 +0800 CalicoIsUp Calico is running on this node
MemoryPressure False Sun, 23 Jul 2023 15:07:41 +0800 Mon, 17 Jul 2023 10:16:48 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Sun, 23 Jul 2023 15:07:41 +0800 Mon, 17 Jul 2023 10:16:48 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Sun, 23 Jul 2023 15:07:41 +0800 Mon, 17 Jul 2023 10:16:48 +0800 KubeletHasSufficientPID kubelet has sufficient PID available
Ready True Sun, 23 Jul 2023 15:07:41 +0800 Mon, 17 Jul 2023 10:17:32 +0800 KubeletReady kubelet is posting ready status
Addresses:
InternalIP: 172.19.191.190
Hostname: k8smaster
Capacity:
cpu: 2
ephemeral-storage: 41152812Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3733564Ki
pods: 110
Allocatable:
cpu: 2
ephemeral-storage: 37926431477
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3631164Ki
pods: 110
System Info:
Machine ID: 20200914151306980406746494236010
System UUID: 7F940571-2A55-438F-AE29-CF050E00A217
Boot ID: 38057d4b-c304-49eb-86b5-18213478a629
Kernel Version: 3.10.0-1127.19.1.el7.x86_64
OS Image: CentOS Linux 7 (Core)
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://19.3.11
Kubelet Version: v1.19.5
Kube-Proxy Version: v1.19.5
PodCIDR: 10.100.0.0/24
PodCIDRs: 10.100.0.0/24
Non-terminated Pods: (13 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE
--------- ---- ------------ ---------- --------------- ------------- ---
kube-system calico-kube-controllers-6c89d944d5-ppb9f 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kube-system calico-node-nkj7x 250m (12%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kube-system coredns-59c898cd69-9xtxz 100m (5%) 0 (0%) 70Mi (1%) 170Mi (4%) 6d4h
kube-system coredns-59c898cd69-mfprl 100m (5%) 0 (0%) 70Mi (1%) 170Mi (4%) 6d4h
kube-system etcd-k8smaster 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kube-system kube-apiserver-k8smaster 250m (12%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kube-system kube-controller-manager-k8smaster 200m (10%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kube-system kube-proxy-kphhp 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kube-system kube-scheduler-k8smaster 100m (5%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kuboard kuboard-agent-2-5c7475f4d-d9nfs 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kuboard kuboard-agent-74c4d97bc6-btgbz 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kuboard kuboard-etcd-4wvkw 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
kuboard kuboard-v3-59ccddb94c-pnmmn 0 (0%) 0 (0%) 0 (0%) 0 (0%) 6d4h
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 1 (50%) 0 (0%)
memory 140Mi (3%) 340Mi (9%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-1Gi 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Starting 145m kubelet Starting kubelet.
Normal NodeHasSufficientMemory 145m (x8 over 145m) kubelet Node k8smaster status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 145m (x8 over 145m) kubelet Node k8smaster status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 145m (x7 over 145m) kubelet Node k8smaster status is now: NodeHasSufficientPID
Normal NodeAllocatableEnforced 145m kubelet Updated Node Allocatable limit across pods
Normal Starting 143m kube-proxy Starting kube-proxy.
Normal Starting 4m40s kubelet Starting kubelet.
Normal NodeHasSufficientMemory 4m39s (x8 over 4m39s) kubelet Node k8smaster status is now: NodeHasSufficientMemory
Normal NodeHasNoDiskPressure 4m39s (x7 over 4m39s) kubelet Node k8smaster status is now: NodeHasNoDiskPressure
Normal NodeHasSufficientPID 4m39s (x8 over 4m39s) kubelet Node k8smaster status is now: NodeHasSufficientPID
Normal NodeAllocatableEnforced 4m39s kubelet Updated Node Allocatable limit across pods
Normal Starting 3m27s kube-proxy Starting kube-proxy.
[root@k8smaster ~]#
在如上节点信息中,节点的主要状态被包含在如下几个字段中:
Address
:描述节点IP,主要包括:Hostname
:与终端执行hostname的值相同,也可用参数--hostname-override
覆盖;InternalIP
:从节点内部可以访问的IP地址;ExternalIP
:可以从集群外访问的内网IP地址,在本次实验中,此字段为空。
Conditions
:描述了节点状态,常见的状态包括:-
NetworkUnvaliable
:如果节点的网络配置有问题,则此字段为True
,否则为False
; -
MemoryPressure
:如果节点内存紧张,则此字段为True
,否则为False
; -
DiskPressure
:如果节点磁盘紧张,则此字段为True
,否则为False
; -
PIDPressure
:如果节点进程过多,则此字段为True
,否则为False
; -
Ready
:如果节点是健康的,且能够接受新的Pod,则此字段为True
,否则为False
;
未在上文输出中出现的字段如:OutOfDisk
:表明节点上空白磁盘空间短缺,无法接受增加新的节点,此时此字段为True
;在
Ready
类型的Status
持续为Unknow
或False
超过pod-eviction-timeout
限制的时间后,控制器会对该节点上的所有Pod实行删除操作。pod-eviction-timeout
是kube-controller-manager
的一个参数,默认值为5。但在某些情况下,APIServer无法与该node上的kubelet进行通信(例如该node网络发生了故障),此时无法将删除Pod的指令下达到该node的kubelet上,需要等到通信恢复后,该node的kubelet才会接收并执行这些指令。于是,出现了即使已经下达了删除Pod的指令,但Pod仍在这个失联的node上运行。如何处理这种情况?在K8S
v1.5
以前,控制器会强制删除APIServer上这些运行在已失联节点上的Pod,但在K8Sv1.5
及以后的版本,直到控制器确认这些Pod已停止运行之前,不会对其进行强制删除,如此,在该失联的node上执行docker ps
命令,就会发现这些Pod仍然在运行,而在APIServer中,他们的状态早已变为终止(Terminating
)或未知(Unknow
),此时只能通过手动执行kubectl delete
命令来从APIServer中删除已失联的节点,该节点上的所有Pod都会被删除。在K8S
v1.12
中,条件化的污点(TaintNodesByCondition
)特性进入Beta阶段,此时node lifecycle controller
将自动创建该 Condition对应的污点。相应地,调度器在选择合适的节点时,不再关注节点的Condition,而是检查节点的污点和 Pod 的容忍。有关污点的学习,我们会在后续内容中提到。
-
Capacity
&Allocatable
:Capacity
描述了节点资源总量,Allocatable
描述了该节点上可分派给普通Pod的资源总数。包括CPU、内存、可调度最大Pod数量;Info
:包含了节点的基本信息,如Linux版本、K8S版本、Docker版本、操作系统名称等。