实战入门
本章介绍如何在kubernetes集群中部署一个nginx服务,并能够对其进行访问。
1. Namespace
Namespace主要作用是实现多套环境的资源隔离或者多租户的资源隔离。
默认情况下,kubernetes 集群中的所有Pod都是可以相互访问的。但是在实际中,可能不希望让2个Pod之间进行相互访问,此时即可将两个Pod划分到不同的namespace下。Kubernetes通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的“组”,以方便不同的组的资源进行隔离使用和管理。
可以通过kubernetes的授权机制,将不同的namespace交给不同租户进行管理。这样就实现了多租户的资源隔离。此时还能结合kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等,来实现租户可用资源的管理。
例如可以给namespace-dev 的 ns 分配5GB的内存资源,而namespace-test 只分配3GB的资源。
Kubernetes集群在创建后,会默认创建几个namespaces:
$ kubectl get ns
NAME STATUS AGE
default Active 22h # 所有未指定Namespace的对象都会被分配在default 命名空间
kube-node-lease Active 22h # 集群节点之间的心跳维护,v1.13 开始引入
kube-public Active 22h # 此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system Active 22h # 所有由kubernetes 系统创建的资源都处于这个命名空间
下面是 namespaces 的资源操作示例:
# 查看
$ kubectl get ns kube-system
NAME STATUS AGE
kube-system Active 23h
=> Active 表示命名空间正在使用;另一种状态是Terminating :表示正在删除命名空间
# 描述
$ kubectl describe ns default
Name: default
Labels: <none>
Annotations: <none>
Status: Active
No resource quota. => ResourceQuota 针对namespace做的资源限制
No LimitRange resource. => LimitRange 针对namespace中的每个组件做的资源限制
# 创建
$ kubectl create ns dev
namespace/dev created
# 删除
$ kubectl delete ns dev
namespace "dev" deleted
#配置方式
首先准备一个 yaml 文件:ds-dev.yaml
$ cat yamls/ns-dev.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
$ kubectl apply -f yamls/ns-dev.yaml
namespace/dev created
$ kubectl delete -f yamls/ns-dev.yaml
namespace "dev" deleted
2. Pod
Pod 是kubernetes集群进行管理的最小单元,程序要运行必须部署在container中,而container必须存在于Pod中。
Pod可以认为是container的封装,一个Pod中可以存在一个或多个container。如下图所示,默认会有一个根容器(也就是这里的Pause),此容器不会在 get pods 时显示给用户。
Kubernetes在集群启动后,集群中的各个组件也都是以Pod方式运行的。可以通过以下命令查看:
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
aws-node-jtzb7 1/1 Running 0 22h
coredns-74c8b6b7dd-7nhz8 1/1 Running 0 23h
coredns-74c8b6b7dd-v6ztk 1/1 Running 0 23h
kube-proxy-lhd84 1/1 Running 0 22h
此结果为 aws eks 集群的结果。在自建的eks集群中,一般还会有 etcd-master、kube-apiserver-master、kube-controller-manager-master、kube-scheduler-master 等 Pods。但是在 aws eks 中,客户端并无法显示这些pods。
另外的aws-node-jtzb7 与kube-proxy-lhd84均对应当前集群中的1个节点(当前集群仅有1个节点)。
Pod 操作
在创建并运行一个pod时,kubernetes没有提供单独运行Pod的命令,都是通过Pod控制器来实现的:
# 命令格式:kubectl run (pod控制器名称) [参数]
# --image 指定Pod 镜像
# --port 指定服务暴露的端口
# --namespace 指定namespace
$ kubectl run nginx --image=nginx:1.17.1 --port=80 --namespace dev
pod/nginx created
$ kubectl get pods -o wide -n dev
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 2m51s 10.0.1.84 ip-10-0-1-217.cn-north-1.compute.internal <none> <none>
=> 这个 ip 是Pod 的ip,并非节点 ip
# 查看更详细内容
kubectl describe pod nginx -n dev
如何访问到此nginx 服务?
首先在 kubectl get pods -o wide -n dev 的结果中,可以看到 pod 的ip 为 10.0.1.84。结合我们指定暴露的端口为 80,在访问时通过curl 10.0.1.84:80 访问即可。
创建pod与创建deployment的不同:
- kubectl run 一个pod后,一般会显示 pod/nginx created。此时创建的是单个pod,可以直接通过delete pod xxx 进行删除
- kubectl create deploy nginx --image=nginx:1.17.1 -n dev 会创建一个deployment,一般会显示deployment.apps/nginx created。此时创建的是一个deployment,直接删除一个pod 的话,会立即自动再创建一个pod。需要通过 $ kubectl delete deployment nginx -n dev 删除一个deployment,返回的结果是:deployment.apps "nginx" deleted
基于配置文件进行操作
创建一个pod-nginx.yaml,内容为:
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
创建:
$ kubectl create -f yamls/pod-nginx.yaml
pod/nginx created
删除:
$ kubectl delete -f yamls/pod-nginx.yaml
pod "nginx" deleted
3. Label
Label 是kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
Label的特点:
- 一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等
- 一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去
- Label 通常在资源对象定义时确定,当然也可以在对象创建后动态添加或删除
可以通过Label实现资源的多维度分组,以灵活、方便地进行资源分配、调度、配置、部署等管理工作。常用的Label如:
• 版本标签:"version":"release", "version":"stable"
• 环境标签:"environment":"dev", "environment":"test", "environment":"pro"
• 架构标签:"tier":"frontend", "tier":"backend"
在标签定义完毕后,还要考虑标签的选择,这时就要使用到Label Selector,即:
- Label 用于给某个资源对象定义标识
- Label Selector 用于查询和筛选拥有某些标签的资源对象
当前有2种Label Selector:
- 基于等式的Label Selector:name = slave
- 选择所有包含 Label 中 key=”name” 且 value=”slave” 的对象;env != production:选择所有包含Label中key = “env” 且value !=”production” 的对象
- 基于集合的Label Selector:
- name in (master, slave):选择所有包含Label中的key=”name” 且value=”master” 或 “slave” 的对象
-
name not in (frontend):选择所有包含Label中的key=”name” 且value不等于”frontend” 的对象
标签的选择条件可以使用多个,此时将多个Label Selector 进行组合,使用逗号“,” 进行分隔即可。例如:
name=slave, env!=production
name not in (frontend), env!=production
Label操作
命令方式:
# 为pod资源打标签
$ kubectl label pod nginx version=1.0 -n dev
pod/nginx labeled
$ kubectl label pod nginx -n dev tier=back
pod/nginx labeled
# 查看标签
$ kubectl get pod -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 6m2s tier=back,version=1.0
# 为pod 资源更新标签
$ kubectl label pod nginx1 version=2.0 -n dev --overwrite
pod/nginx1 labeled
$ kubectl get pods -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 15m tier=back,version=1.0
nginx1 1/1 Running 0 89s run=nginx1,version=2.0
# 标签选择
$ kubectl get pods -l "version=2.0" -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx1 1/1 Running 0 2m run=nginx1,version=2.0
# 删除标签
$ kubectl label pod nginx -n dev tier-
pod/nginx labeled
$ kubectl get pods -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 18m version=1.0
nginx1 1/1 Running 0 4m10s run=nginx1,version=2.0
以配置方式:
在yaml文件中加上label字段:
$ cat yamls/pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
version: "3.0"
tier: "production"
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
4. Deployment
在kubernetes 中,Pod是最小的控制单元,但是kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成。Pod控制器用于pod的管理,确保pod资源符合预期的状态,当pod资源出现故障时,会尝试进行重启或重建pod。
在kubernetes中Pod控制器的种类有很多,下面主要介绍其中一种:Deployment。
Deployment操作
命令方式:
# 创建
$ kubectl create deploy nginx-dep --image=nginx:1.17.1 -n dev
deployment.apps/nginx-dep created
# 查看
$ kubectl get deploy -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx-dep 1/1 1 1 37s nginx nginx:1.17.1 app=nginx-dep
$ kubectl get pods -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-dep-74c95fbff9-lsngl 1/1 Running 0 19s app=nginx-dep,pod-template-hash=74c95fbff9
=> 可以看到 deployment 有个SELECTOR 的标签选择器,选择的标签即为启动的pod自动打上的名称标签
# 删除
$ kubectl delete deploy nginx-dep -n dev
deployment.apps "nginx-dep" deleted
配置文件方式:
$ cat yamls/deploy-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dep
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx:1.17.1
name: nginx
ports:
- containerPort: 80
protocol: TCP
selector 就是label选择器,pod哪里的label给予的是run:nginx
$ kubectl apply -f yamls/deploy-nginx.yaml
deployment.apps/nginx-dep created
$ kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dep 3/3 3 3 103s
$ kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx-dep-755c49cf64-462g5 1/1 Running 0 2m21s
nginx-dep-755c49cf64-s74rx 1/1 Running 0 2m21s
nginx-dep-755c49cf64-sgc88 1/1 Running 0 2m21s
$ kubectl delete -f yamls/deploy-nginx.yaml
5. Service
现在我们可以通过deployment创建一组Pods,用于提供高可用服务。
但是,虽然每个Pod都会被分配一个单独的Pod IP,仍会存在以下2个问题:
- Pod IP会随着Pod的重建产生变化
- Pod IP仅是集群内可见的虚拟IP,外部无法访问
这样对于访问服务带来了难度。因此,kubernetes设计了Service来解决此问题。
Service可以看作是一组同类Pod对外的访问接口。借助Service,应用可以很方便地实现服务发现和负载均衡。
如下图所示:
一个请求在访问Service时,Service会通过标签选择器选择需要访问的Pod,然后将请求转发到对应的Pod中,其中也涉及负载均衡算法。Service在其整个生命周期中,IP地址都是不变的。
Service操作
创建集群内部可访问的Service
# 暴露Service
$ kubectl expose deploy nginx-dep --name=svc-nginx1 --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx1 exposed
此时暴露的是deployment,而不是直接1个pod。--port 指定的是 service 80 端口,转发到pod上的 80 端口(--terget-port=80)
# 查看Service
$ kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx1 ClusterIP 172.20.246.209 <none> 80/TCP 65s
# 由于指定的是ClusterIP,所以仅有集群内的节点能访问到此服务
# 通过 eks node 访问此地址:
$ curl 172.20.246.209
…
<p><em>Thank you for using nginx.</em></p>
…
# 删除svc
$ kubectl delete svc svc-nginx2 -n dev
service "svc-nginx2" deleted
创建集群外部也能访问Service
# 若是需要外部也能访问,则需要修改type为NodePort
$ kubectl expose deploy nginx-dep --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx2 exposed
$ kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx1 ClusterIP 172.20.246.209 <none> 80/TCP 8m13s run=nginx
svc-nginx2 NodePort 172.20.140.200 <none> 80:31890/TCP 18s run=nginx
$ kubectl apply -f yamls/svc-nginx.yaml
service/svc-nginx created
$ kubectl delete -f yamls/svc-nginx.yaml
service "svc-nginx" deleted