在虚拟化和云高度发展的今天,容器云已经成了生产现实,那么容器编排技术无疑成了每个必须掌握的技能之一。作为事实上的容器编排标准K8S则是每个人的必修课,今天我们就来给大家补充K8S的这一课。
概述
本文我们也坚持一贯的行文风格那就是理论知识和实践动手操作相结合的方式。为了动手联系相关内容,我们必须要安装一些工具:
Docker Desktop
Kubernetes CLI(kubectl)
一个单节点的Kubernetes集群构建工具
kind
kubectl基础和K8S集群上下文
首先是Kubernetes CLI工具。它是一个单一的二进制文件kubectl。可以使用Linux发行版的软件包管理工具安装:
比如对于CentOS 可以使用:
yum install kubectl
安装成功后,我们就可以使用它对集群运行命令进行管理:
运行kubectl get nodes以列出Kubernetes集群中的所有节点结果类似:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
docker-desktop Ready master 63d v1.16.6-beta.0
Kubernetes使用名为的配置文件config来查找连接到集群所需的信息。该文件位于主机主文件夹的:~/.kube/config。上下文是该配置文件中的一个元素,它包含对集群,名称空间和用户的引用。如果要访问或运行单个群集,则配置文件中将只有一个上下文。我们可以定义多个指向不同集群的上下文。
使用kubectl config命令可以查看这些上下文并在它们之间切换。
可以运行以下current-context命令来查看当前上下文:
kubectl config current-context
docker-desktop
如果没有得到与上述相同的输出,则可以使用use-context命令切换到docker-desktop上下文:
kubectl config use-context docker-desktop
Switched to context "docker-desktop".
还可以运行kubectl config get-contexts以获取所有Kubernetes上下文的列表。
kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
docker-desktop docker-desktop docker-desktop
* minikube minikube minikube
chongjk8s chongjk8s clusterUser_mykubecluster_chongjk8s
其中加*号的列表表示当前上下文
还有其它的命令,如use-context,set-context,get-contexts等。
对应kubectl,还有一个更好用的工具kubectx。kubectx可以让我们在不同上下文间快速切换。例如,配置文件中设置了三个集群(上下文),则运行kubectx输出:
kubectx
docker-desktop
peterj-cluster
minikube
运行命令时,当前选中的上下文也会突出显示。要切换到minikube上下文,可以使用:
kubectx minikube
如果使用kubectl则
先要使用kubectl config get-contexts查看所有上下文。
然后用kubectl config use-context minikube切换上下文。
容器编排
随着docker容器越来越多,对其管理就是个问题。为了解决这个问题,业界引入了容器编排的概念。容器编排就是用一系列的工具和统一的界面来自动化和管理容器的部署,包括其节点、网络、调度、存储、监控等等各个方面。容器编排可以帮助我们管理容器的整个生命周期。容器编排系统主要的功能包括:
根据可用资源配置和部署容器;
对各个容器进行健康监控;
负载平衡和服务发现;
不同容器之间的资源分配;
容器的上下缩放;
常见的容器编排系统有Kubernetes,Marathon,Docker Swarm,我们学习由谷歌开源并且使用最人数最多的Kubernetes,Kubernetes在国内也简称为K8S。
其实中文的的K8S和其发音也类似。
Kubernetes是由谷歌开源项目,是容器集群管理和调度最流行方案之一。使用Kubernetes可以用来运行容器,进行零停机时间部署,以在不影响用户的情况下更新应用程序,以及其他更多的我们后面要介绍的东西。
K8S与Docker
Kubernetes和Docker区别新手遇到的一个困惑的问题。Docker是容器的实现技术之一,还有其他的容器技术比如podman。
K8S是一个容器编排系统,是用来管理容器的工具。Kubernetes使用Pod等更高级别的架构来打包容器并统一管理它们。
K8S与Docker Swarm
和K8S一样,Docker Swarm也是一个容器编排工具,又有Docker公司自己推出的Docker管理工具。使用Swarm,可以将多个Docker主机连接到一个虚拟主机中。然后,用Docker CLI一次与多个主机通信,并在其上运行Docker容器。
K8S架构
K8S集群是运行容器化应用程序所需的一组物理或虚拟机以及其他基础结构资源。K8S集群中的每台机器都称为一个节点。每个K8S集群中有两种类型的节点:
主节点:承载Kubernetes控制平面并管理集群
工作节点:运行容器化应用程序
主节点
主节点上的主要组件之一是API服务器。当创建K8S资源或管理集群时,API服务器负责和kubectl(工作节点)客户端通讯和对话。
调度器组件用对工作节点的应用程序进行工作负载和调度。还用来管理节点上可用的资源以及工作负载所请求的资源。利用这些资源用来对工作负荷进行调度。
其次控制器管理器,在主节点上运行的控制器管理器有两种类型:
KUBE控制器管理运行多个控制器处理。这些控制器监视群集的状态,并尝试将群集的当前状态(例如"工作负载A运行有5个副本")与所需状态(例如"希望在工作负载A运行10个副本")进行协调。控制器包括节点控制器,复制控制器,端点控制器以及服务帐户和令牌控制器。
云控制器管理运行控制器所特有的云提供商和可以管理资源群集之外。仅当K8S集群在云中运行时,此控制器才运行。如果在计算机上运行K8S集群,则该控制器将不会运行。该控制器的目的是使集群与云提供商进行对话,以管理节点,负载均衡器或路由。
最后,etcd是分布式键值存储数据库,K8S集群和API对象的状态都存储在etcd中。
工作节点
和主节点上一样,工作节点也运行有不同组件。首先是kubelet,该服务在每个工作程序节点上运行,其工作是管理容器。它可以确保容器正常运行,并且可以连接回控制平面。Kubelet与API服务器进行通信,它负责管理运行在其上的节点上的资源。
将新的工作程序节点添加到群集时,kubelet会自我介绍并提供其拥有的资源(例如"我有X CPU和Y内存")。然后,它询问是否需要运行任何容器。可以将kubelet视为工作程序节点管理器。
Kubelet使用容器运行时接口(CRI)与容器运行时进行对话。容器运行时负责使用容器。除了Docker外,Kubernetes还支持其他容器运行时,例如containerd或cri-o。
容器在Pod中运行,由上图中的蓝色矩形表示(容器是每个容器内的红色方块)。Pod是在K8S集群上创建和可管理的最小可部署单元。容器是构成应用程序的容器的逻辑集合。在同一Pod内运行的容器共享网络和存储空间。
每个工作节点还具有一个代理proxy,代理充当工作节点上运行的工作负载的网络代理和负载平衡器。通过外部负载平衡器发出的客户端请求将通过这些代理重定向到在pod内运行的容器。
Kubernetes资源
K8S API定义了许多称为资源的对象,例如命名空间,pod,服务,机密,配置映射等。也可以使用自定义资源定义或CRD定义自己的自定义资源。
配置K8S CLI和集群后,应尝试运行kubectl api-resources命令。它将列出所有已定义的资源-将会有很多资源。
可以使用YAML定义K8S中的资源。每个K8S资源拥有apiVersion和kind字段来描述创建时的资源(例如,K8S API的版本apps/v1)和一个什么样的资源创建(例如Deployment,Pod,Service等)。
其中元数据metadata包括正在创建的资源的数据。通常包括name(例如mydeployment)和namespace在何处创建资源。还可以在元数据中提供其他字段,例如labels和annotations,并且在创建资源后会添加其中的几个字段(例如creationTimestamp)。
Pods
Pod是Kubernetes中最常见的资源之一。它们是一个或多个容器的集合。Pod中的容器共享网络和存储,同一Pod中的任何容器都可以通过localhost进行相互通信。
Pod被设计为非持久化的,可以随时将Pod重新部署到不同的节点上运行。只要的Pod重启,容器也也相应会重启。
Pod创建后,会被分配一个唯一的IP地址。pod内的容器可以监听不同的端口。要访问容器,可以使用容器的IP地址。例如上图中的实例中,可以通过curl 10.1.0.1:3000和一个容器进行通信,而curl 10.1.0.1:5000则是和另一个容器通信。但是,如果想在容器之间进行交互。比如,从下面的一个容器调用上面的容器,可以使用http://localhost:3000。
如果Pod重启,就会分配另一个IP地址。因此,不能依赖于IP地址。通过IP直接与Pod进行通讯可能会有问题。
可以与Pod进行通信的抽象称为K8S服务。K8S服务为我们提供稳定的IP地址和DNS名称。
缩放Pod
pod内的所有容器均按比例缩放。下图显示了如何从单个pod缩放到四个。注意,无法缩放pod中的单个容器。必须以pod为单位进行缩放。
pod创建
通常,不应该手动创建pod。虽然允许这样做。因为,如果pod崩溃或被删除,它会永远消失。可以在YAML定义单个pod。
apiVersion: v1
kind: Pod
metadata:
name: hello-chongchong
labels:
app: hello
spec:
containers:
- name: hello-container
image: busybox
command: ['sh', '-c', 'echo Hello chongchong! && sleep 3600']
在该配置中,我们定义了资源(Pod)和元数据的类型。元数据包括pod的名称(hello-pod)和一组简单的键/值对(app=hello)的标签。
在spec段中,定义pod具体构成。在该容器中将有一个名为hello-container的容器它运行名为的busybox的镜像。容器启动后,command将执行该字段中定义的命令。
要手动创建该pod,需要将上述YAML保存到一个名为的文件中pod.yaml。然后,使用kubectl创建容器:
kubectl create -f pod.yaml
pod/hello-pod created
K8S会以资源类型和创建的名称返回。可以通过kubectl get pods用来获取运行的默认集群名称空间的所有Pod的列表。
kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-chongchong 1/1 Running 0 7s
使用logs命令查看在pod内运行的容器的输出:
kubectl logs hello-chongchong
Hello chongchong!
如果同一容器中有多个容器运行,则可以使用该-c标志指定要获取日志的容器名称。
kubectl logs hello-pod -c hello-container
要删除该pod,可以使用kubectl delete pod hello-chongchong
这样一来pod就会永远消失,无法再重启它。
使用kubectl get pods再次运行,会发现hello-container pod已经消失。
kubectl get pods
No resources found in default namespace.
这与我们期望的行为相反。如果工作负载在Pod中运行,我希望能在出现问题时自动重新计划并重新启动它。为了确保可以对崩溃的Pod重启,我们需要一个可以管理Pod生命周期的控制器。利用该控制器可确保Pod在删除或发生意外情况下时能够自动恢复。
副本集
ReplicaSet副本集的工作是维护稳定数量的Pod副本或副本。副本集控制器可确保始终运行指定数量的相同容器。副本数由replicas资源定义中的字段控制。
如果从单个Pod开始,想要缩放到5个Pod,则副本集控制器将使用集群中的当前状态(一个Pod),然后创建另外四个Pod以满足所需的状态(5 Pod)。副本集还可以监视Pod,因此,如果删除一个Pod或按比例放大或缩小,则它会满足所需的副本数。要创建容器,副本集使用资源定义中包含的容器模板。
定义副本集的格式:
apiVersion: apps/v1
kind: replicaset
metadata:
name: hello
labels:
app: hello
spec:
replicas: 5
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello-container
image: busybox
command: ['sh', '-c', 'echo Hello Chognchong! && sleep 3600']
由副本集创建的Pod都可以通过该metadata.ownerReferences字段进行标识。该字段指定由哪个副本集拥有该pod。如果删除了副本集拥有的Pod,则副本集会立即对此进行操作并自动重新创建Pod。
副本集也使用该selector对象和matchLabel检查拥有的任何新容器。如果有一个与选择器标签副本集相匹配的新容器,并且它没有所有者引用,或者所有者不是控制器(比如,手动创建的pod),则副本集将接管它并开始控制它。
上面的配置保存到replicaset.yaml文件,然后通过kubectl运行:
kubectl create -f replicaset.yaml
replicaset.apps/hello created
可以通过运行以下命令来查看副本集:
kubectl get replicaset
NAME DESIRED CURRENT READY AGE
hello 5 5 5 30s
该命令将显示副本集的名称以及目标pod个数,当前个数和完成Pod副本数。
用get po命令列出了Pod,可以看到有5个Pod在运行:
kubectl get po
NAME READY STATUS RESTARTS AGE
hello-dwx89 1/1 Running 0 31s
hello-fchvr 1/1 Running 0 31s
hello-fl6hd 1/1 Running 0 31s
hello-n667q 1/1 Running 0 31s
hello-rftkf 1/1 Running 0 31s
也可以按标签列出pod。例如,执行kubectl get po -l=app=hello,可以看到所有标签设置了app=hello的Pod。
还可以使用该-o yaml标志来获取K8S中任何对象的YAML表达形式。我也可以通过管道用grep对结果进行搜索
kubectl get po hello-dwx89 -o yaml | grep -A5 ownerReferences
...
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: replicaset
name: hello
在中ownerReferences,所有者的名称设置为hello,种类设置为副本集。这是拥有该窗格的副本集。
部署
部署资源是对副本集的打包,它允许对pod进行受控更新。例如,如果要更新所有Pod的镜像名称,则可以更新Pod模板,并且部署控制器将使用新镜像重新创建Pod。
使用前面使用的示例,则部署将如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
labels:
app: hello
spec:
replicas: 5
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello-container
image: busybox
command: ['sh', '-c', 'echo Hello from my container! && sleep 3600']
K8S部署的YAML看起来与副本集完全相同。有副本数,选择器标签和pod模板。
将上面的YAML配置保存到deployment.yaml,然后用下面命令创建部署:
kubectl create -f deployment.yaml --record
deployment.apps/hello created
使用—record标记,我们告诉K8S将执行的命令存储在名为kubernetes.io/change-cause的注释中。跟踪部署时执行的更改或命令该标记很有用。
要列出所有部署,可以也使get命令:
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
hello 5/5 5 5 2m8s
输出与列出副本集时的输出相同。在创建部署时,还创建了一个副本集:
kubectl get rs
NAME DESIRED CURRENT READY AGE
hello-6fcbc8bc84 5 5 5 3m17s
最后,用get po列出pod列表:
kubectl get po
NAME READY STATUS RESTARTS AGE
hello-6fcbc8bc84-27s2s 1/1 Running 0 4m2s
hello-6fcbc8bc84-49852 1/1 Running 0 4m1s
hello-6fcbc8bc84-7tpvs 1/1 Running 0 4m2s
hello-6fcbc8bc84-h7jwd 1/1 Running 0 4m1s
hello-6fcbc8bc84-prvpq 1/1 Running 0 4m2s
可以观察到和以前副本集创建的pod相比较pod命名更复杂hello-6fcbc8bc84-27s2s。名称中的中间随机部分6fcbc8bc84对应于副本集名称的随机部分,组合了部署名称,副本集名称和随机字符串来Pod名。
如果删除其中一个Pod,部署和副本集将确保始终保持所需副本的数量。
缩放pod
K8S CLI中有一个方便的命令,称为scale。使用该命令,我们可以按比例扩大(或缩小)由部署或副本集控制的Pod的数量。
比如,如果要将Pod缩小到3个副本,可以使用:
kubectl scale deployment hello --replicas=3
deployment.apps/hello scaled
再查看pod,结果:
kubectl get po
NAME READY STATUS RESTARTS AGE
hello-6fcbc8bc84-49852 1/1 Running 0 48m
hello-6fcbc8bc84-7tpvs 1/1 Running 0 48m
hello-6fcbc8bc84-h7jwd 1/1 Running 0 48m
可以再扩容到5个(或者更多数目)pod副本:
kubectl scale deployment hello --replicas=5
结果:
kubectl get po
NAME READY STATUS RESTARTS AGE
hello-6fcbc8bc84-49852 1/1 Running 0 49m
hello-6fcbc8bc84-7tpvs 1/1 Running 0 49m
hello-6fcbc8bc84-h7jwd 1/1 Running 0 49m
hello-6fcbc8bc84-kmmzh 1/1 Running 0 6s
hello-6fcbc8bc84-wfh8c 1/1 Running 0 6s
更新Pod模板
使用副本集时,当更新镜像名称时,pod不会自动重启。用部署则可以。
可以使用set image命令将Pod模板中的镜像更新busybox为busybox:1.31.1。
该命令set可用于更新Pod模板的各个部分,例如镜像名称,环境变量,资源以及其他信息。
kubectl set image deployment hello hello-container=busybox:1.31.1 --record
deployment.apps/hello image updated
结果:
kubectl get po
NAME READY STATUS RESTARTS AGE
hello-6fcbc8bc84-49852 1/1 Terminating 0 57m
hello-6fcbc8bc84-7tpvs 0/1 Terminating 0 57m
hello-6fcbc8bc84-h7jwd 1/1 Terminating 0 57m
hello-6fcbc8bc84-kmmzh 0/1 Terminating 0 7m15s
hello-84f59c54cd-8khwj 1/1 Running 0 36s
hello-84f59c54cd-fzcf2 1/1 Running 0 32s
hello-84f59c54cd-k947l 1/1 Running 0 33s
hello-84f59c54cd-r8cv7 1/1 Running 0 36s
hello-84f59c54cd-xd4hb 1/1 Running 0 35s
四个Pod正在终止,还有五个新Pod正在运行。Pod名称也有了变化,部署减小老副本集的容器,并创建了新镜像生成的副本集。
可以使用rollout history命令查看之前的部署:
kubectl rollout history deploy hello
deployment.apps/hello
REVISION CHANGE-CAUSE
1 kubectl create --filename=deployment.yaml --record=true
2 kubectl set image deployment hello hello-container=busybox:1.31.1 --record=true
历史记录中显示了对该部署所做的所有修改。第一个版本是最初创建资源时的版本,第二个版本是更新镜像时的版本。
假设推出了新的镜像版本,但是由于某些原因,想回退到以前的状态。可以使用rollout命令回滚到该资源的先前版本。
该回退操作通过使用以下rollout undo命令来完成:
kubectl rollout undo deploy hello
deployment.apps/hello rolled back
使用undo命令,会回滚到以前的版本,这是我们在更新镜像之前所处的原始状态:
kubectl rollout history deploy hello
deployment.apps/hello
REVISION CHANGE-CAUSE
2 kubectl set image deployment hello hello-container=busybox:1.31.1 --record=true
3 kubectl create --filename=deployment.yaml --record=true
通过运行删除部署:
kubectl delete deploy hello
deployment.apps "hello" deleted
部署策略
部署使用两种不同的策略来用新的替换旧pod。重建策略和回滚升级(RollingUpdate)策略,默认策略为了回滚升级策略。
重建策略是重新创建5个pod,然后把所有服务都调度到新建的pod。在此期间会有10个pod在工作。
回滚升级策略的工作方式是通过maxUnavailable和maxSurge设置指定了不可用的Pod的最大数量以及可以同时运行的新旧Pod的最大数量,然后在配置的数量下来进行调度和服务升级。
服务
pod应该是非持久、短暂的。一旦崩溃,就会消失了,副本集确保会自动创建新的Pod来维持所需数量的副本。
假设我们在容器内的容器中运行一个Web前端。每个Pod都有一个唯一的IP地址,但是,由于其短暂的特性,不能依赖这个IP地址。我们需要创建一个运行Web前端的部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend labels:
app: web-frontend
spec:
replicas: 1
selector:
matchLabels:
app: web-frontend
template:
metadata:
labels:
app: web-frontend
spec:
containers:
- name: web-frontend-container
image: learncloudnative/helloworld:0.1.0
ports:
- containerPort: 3000
将此部署与上一个部署进行比较,会注意到资源名称和使用的镜像都做了变化。
ports部分新添加的资源。使用containerPort字段,设置网站服务器监听的端口号。端口由learncloudnative/helloworld:0.1.0一个简单Node.js Express应用程序来实现。
保存为web-frontend.yaml并创建部署:
kubectl create -f web-frontend.yaml
deployment.apps/web-frontend created
运行kubectl get pods以确保Pod已启动并正在运行,然后获取该容器的日志:
kubectl get po
NAME READY STATUS RESTARTS AGE
web-frontend-68f784d855-rdt97 1/1 Running 0 65s
kubectl logs web-frontend-68f784d855-rdt97
> helloworld@1.0.0 start /app
> node server.js
Listening on port 3000
从日志中已经显示到容器正在监听了3000端口。如果将日志输出标记设置为放弃宽输出(-o wide),则会进一步能看到Pod监听的IP地址10.244.0.170:
kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-frontend-68f784d855-rdt97 1/1 Running 0 65s 10.244.0.170 docker-desktop <none> <none>
如果删除该Pod,则会自动创建新的Pod来代替它,并给出一个全新的IP地址:
kubectl delete po web-frontend-68f784d855-rdt97
pod "web-frontend-68f784d855-rdt97" deleted
kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-frontend-68f784d855-8c76m 1/1 Running 0 7s 10.244.0.171 docker-desktop <none> <none>
同理,如果将部署扩展到4个Pod,将有四个不同的IP地址:
kubectl scale deploy web-frontend --replicas=4
deployment.apps/web-frontend scaled
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-frontend-68f784d855-8c76m 1/1 Running 0 5m23s 10.244.0.171 docker-desktop <none> <none>
web-frontend-68f784d855-jrqq4 1/1 Running 0 18s 10.244.0.172 docker-desktop <none> <none>
web-frontend-68f784d855-mftl6 1/1 Running 0 18s 10.244.0.173 docker-desktop <none> <none>
web-frontend-68f784d855-stfqj 1/1 Running 0 18s 10.244.0.174 docker-desktop <none> <none>
无服务情况下访问Pod
如果用curl试着访问这些IP地址,则请求无法正常工作:
curl -v 10.244.0.173:3000
* Trying 10.244.0.173...
* TCP_NODELAY set
* Connection failed
* connect to 10.244.0.173 port 3000 failed: Network is unreachable
* Failed to connect to 10.244.0.173 port 3000: Network is unreachable
* Closing connection 0
curl: (7) Failed to connect to 10.244.0.173 port 3000: Network is unreachable
Pod在群集内运行,并且只能从群集内访问该IP地址。
如果在集群内部运行Pod,然后通过该Pod去访问,则可以访问:
kubectl run curl --image=radial/busyboxplus:curl -i –tty
然后请求
curl -v 10.244.0.173:3000
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 10.244.0.173:3000
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 111
< ETag: W/"6f-U4ut6Q03D1uC/sbzBDyZfMqFSh0"
< Date: Wed, 20 May 2020 22:10:49 GMT
< Connection: keep-alive
<
<link rel="stylesheet" type="text/css" href="css/style.css" />
我们收到了Pod的回复。运行exit返回到终端,curl Pod将继续运行,并再次访问它,可以使用attach命令:
kubectl attach curl -c curl -i –t
使用服务访问Pod
Kubernetes服务是一种抽象,它确保能够可靠地到达Pod IP。服务控制器(类似于副本集控制器)维护端点或Pod IP地址的列表。控制器使用选择器和标签来监控Pod。每当创建或删除与选择器匹配的Pod时,控制器就会从端点列表中添加或删除Pod的IP地址。
服务的配置格式如下(Web):
kind: Service
apiVersion: v1
metadata:
name: web-frontend
labels:
app: web-frontend
spec:
selector:
app: web-frontend
ports:
- port: 80
name: http
targetPort: 3000
除了kind字段为新类型外,其他基本和Pods,副本集以及部署都类似。
其中selector部分是定义服务用于查询pod的标签。
最后,在该ports字段下,定义可在80端口访问该服务,用targetPort说明该服务容器的端口。targetPort值与containerPortDeployment YAML中的值匹配。
将其保存为web-frontend-service.yaml文件并进行部署:
kubectl create -f web-frontend-service.yaml
service/web-frontend created
要查看创建的服务,使用get service命令:
kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d
web-frontend ClusterIP 10.100.221.29 <none> 80/TCP 24s
web-frontend服务的IP地址不会更改,可用于可靠地访问基础容器。
使用之前curl容器,来访问服务:
kubectl attach curl -c curl -i –t
curl 10.100.221.29
<link rel="stylesheet" type="text/css" href="css/style.css" />
<div class="container">
Hello World!
</div>
尽管服务的IP地址是固定不变的,更好的方式是使用域名来进行访问。在K8S中创建的每个服务都会自动按照如下格式分配一个DNS名称:
service-name.namespace.svc.cluster-domain.sample
到目前为止,我们已将所有内容都部署到defaul名称空间,而集群域是cluster.local。
所以可以使用web-frontend.default.svc.cluster.local来访问服务:
除了使用全域名之外,还可以仅使用服务名称,服务名称和命名空间名称组合来访问:
使用Kubernetes代理
群集内部可用服务的另一种方法是通过代理。
kubectl proxy命令在本地计算机(localhost)和K8S API服务器之间创建一个网关。
这样我们通过其可以访问K8S API以及访问K8S服务。
请注意,为了安全,请不要将该代理对外开放和泄露代理地址,它应该仅应用于调试或故障排除。
打开一个单独的终端窗口,然后运行以下命令来启动代理服务器,该代理服务器会将来自代理的请求代理localhost:8080到集群内的Kubernetes API:
kubectl proxy --port=8080
然后在浏览器中打开 localhost:8080/,将能看到来自K8S API列表:
如果要查看群集中正运行的所有Pod,则可以访问接口:localhost:8080/api/v1/pods
或通过localhost:8080/api/v1/namespaces查看所有命名空间。
使用代理,还可以访问部署的服务web-frontend。因此,可以创建代理服务器并使用以下URL来访问服务,而不是在集群内部运行pod并向服务或pod发出cURL请求:
在浏览器上访问服务的URL将会显示带有Hello World简单HTML站点。
查看服务详细信息
使用describe命令,可以在K8S中查看对象的详细信息。比如,我们查看一下看一下web-frontend服务的详细信息:
该命令提供了比get命令更多的信息:名称,命名空间,标签,选择器,服务类型,IP和端口。此外,还会注意到个pod端点情况,包括Pod对应的IP地址和端口。
还可以使用以下get endpoints命令查看端点:
要查看正在管理这些端点的控制器,可以使用该--watch标志来监视端点,如下所示:
kubectl get endpoints –watch
在单独的终端窗口中,可以将部署收缩到到一个Pod:
kubectl scale deploy web-frontend --replicas=1
deployment.apps/web-frontend scaled
收缩后,可以看到端点对应自动更新。
Kubernetes服务类型
从之前看到的服务描述输出中,可能已经注意到以下行:
Type: ClusterIP
每个Kubernetes服务都有一个类型。如果不提供服务类型,则默认情况下会分配ClusterIP。除了ClusterIP,K8S中还有其他三种服务类型,分别为NodePort,LoadBalancer和ExternalName。
让我们解释一下这些类型之间的区别。
ClusterIP
ClusterIP服务类型用于通过群集内部IP地址从集群内访问Pod。在大多数情况下,将为群集中运行的应用程序使用这种服务。使用ClusterIP类型,可以通过ports YAML文件中的部分来定义希望服务监听的端口。
Kubernetes将群集IP分配给服务。然后,可以使用群集IP地址和在YAML中指定的端口来访问服务。
NodePort
在某个时候,需要向公众公开的服务,并允许外部流量访问集群。NodePort服务类型打开集群中的每一个工作节点上的一个端口。将节点IP和端口号的所有流量都将转发到服务和Pod。
例如,如果使用上面所示的节点IP和端口,可以访问服务和Pod。要创建NodePort服务,需要指定服务类型,如下列表所示。
默认情况下,节点端口将分配在30000到32767之间(可在API服务器中配置)。
要控制负载平衡的方式时,可以使用NodePort类型。可以通过NodePort公开服务,然后将负载均衡器配置为使用节点IP和节点端口。例如,如果要将现有解决方案迁移到K8S,则可以使用此方法。在这种情况下,可能已经有一个现有的负载均衡器,并且可以将节点IP和节点端口添加到负载均衡池中。
LoadBalancer
LoadBalancer负载均衡服务类型是将K8S服务公开给外部流量的方式。如果正在运行云托管集群并创建LoadBalancer类型的服务,则Kubernetes云控制器会在云帐户中创建一个实际的负载均衡器。
删除前面的NodePort服务,
kubectl delete svc web-frontend并创建一个使用负载均衡类型的服务:
现在查看服务,会注意到类型已更改为LoadBalancer,并且获得了一个外部IP地址:
当使用云托管的Kubernetes集群时,外部IP将是公共的,可以使用外部IP地址访问服务。
ExternalName
ExternalName服务类型是一种特殊类型的不使用选择一个服务,而是使DNS名称。
使用ExternalName可以将Kubernetes服务映射到DNS名称。当向服务提出请求时,它将返回CNAME记录,其中包含value externalName字段,而不是服务的群集IP。
一个ExternalName服务的示例:
例如,将工作负载迁移到K8S时,可以使用ExternalName服务类型。可以使数据库在集群外部运行,还使用my-database服务从集群内部运行的工作负载访问数据库。
总结
本文我们从零到入门,介绍了K8S的概念,架构和各个部件,并实例演示了K8S各个部件的运行,希望该篇文档能对大家K8S的学习有所帮助。