k8s集群安装见另一篇文章:https://www.cnblogs.com/zhenjingcool/p/17413490.html
发现另一专栏讲解k8s,可以做参考:https://blog.csdn.net/qq_38263083/category_11393640.html
1 k8s架构图
先对一些组件功能做一下简单说明:
- api server:所有服务访问统一入口
- controller manager:维持副本期望数目
- scheduler:负责接收任务,选择合适的节点进行分配任务
- ETCD:键值对数据库 存储k8s集群所有重要信息
- kubelet:直接跟容器引擎交互实现容器的生命周期管理
- kube-proxy:负责写入规则至iptables、IPVS实现服务映射访问的
- coredns:可以为集群中的svc创建一个域名ip对应关系解析
- dashboard:给k8s集群提供一个bs结构访问体系
2 基础概念
一个pod内部可以有1个或多个容器。pod启动时就会创建一个pause容器,pod中其他容器共享pause的网络和存储。也就是说同一个pod中的容器可以使用localhost互相访问。需要注意的是同一个pod中的容器不允许端口号冲突
ReplicationController,用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的pod来代替。
在新版本中的k8s中建议使用ReplicaSet来取代ReplicationController。因为ReplicaSet支持集合式的selector
ReplicaSet
Deployment,虽然ReplicaSet可以独立使用,但是一般还是建议使用Deployment来自动管理ReplicaSet。这样就无需担心跟其他机制的不兼容问题(比如ReplicaSet不支持滚动更新但Deployment支持)
Horizontal Pod Autoscaling水平自动扩展,支持根据内存和用户自定义的metric扩缩容
StatsfulSet是为解决有状态服务的问题,其应用场景包括:稳定的持久化存储;稳定的网络标志;有序部署,有序扩展;有序收缩,有序删除
DaemonSet确保全部node上运行一个Pod的副本
3 服务发现
Kubernetes Serivce是一组具有相同label 的Pod集合的抽象(可以简单的理解为集群内的LB),集群内外的各个服务可以通过Service进行互相通信。
4 网络通信模式
k8s的网络模型假定了所有的Pod都在一个可以直接连通的扁平的网络空间中
各组件之间的通信有如下几种情况
- 同一个pod的多个容器之间:走lo网卡
- 各pod之间的通信:overlay network
- pod与Service之间的通信:各节点的iptables规则
Flannel是CoreOs团队针对k8s设计的一个网络规划服务,简单的说,他的功能是让集群中的不同节点主机创建的docker容器都具有全集群唯一的虚拟ip地址。而且它还能在这些ip地址之间建立一个覆盖网络(Overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器内。
在真实主机上有4个pod,分别是webapp1-3和Backend
每个真实主机上有一个后台进程Flanneld,负责数据包的转发,同时,Flanneld会创建一个网桥Flannel0专门去收集Docker0转发出来的数据包,Docker0会分配具体的ip到对应的Pod上。
如果是同一主机上的不同pod通信,他们走的是Docker0的网桥
如果是不同主机上的pod通信,情况就复杂了,这里先不介绍。
通过下图可能有助于我们的理解
5 示例
之前学习docker的时候,我们在docker hub仓库中上传过一个镜像,我们以这个为例,看看通过k8s如何创建一个pod来运行我们的容器。
[root@k8s-master ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: zhexxxxxol
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
[root@k8s-master ~]#
5.1 使用kubectl运行一个镜像到pod中
我们可以使用kubectl run --help查看命令使用方法
[root@k8s-master ~]# kubectl run getting-started --image=zhenjingcool/getting-started --port=80 --replicas=1
Flag --replicas has been deprecated, has no effect and will be removed in the future.
pod/getting-started created
[root@k8s-master ~]#
然后我们查看这个pod
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
getting-started 1/1 Running 0 8m42s 10.2.36.65 k8s-node1 <none> <none>
[root@k8s-master ~]#
可以看到这个Pod已经处于Running状态,并且我们可以看到运行在k8s-node1节点,我们可以去k8s-node1节点查看一下docker容器
[root@k8s-node1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0b631fe523fb zhenjingcool/getting-started "docker-entrypoint.s…" 37 minutes ago Up 37 minutes k8s_getting-started_getting-started_default_ae54ba8b-7252-4b76-99fd-82f21444301f_0
5.2 创建一个deployment
我们先删除上面示例中创建的pod
[root@k8s-master ~]# kubectl delete pod getting-started
pod "getting-started" deleted
[root@k8s-master ~]# kubectl get pod
No resources found in default namespace.
[root@k8s-master ~]#
创建deployment
[root@k8s-master ~]# kubectl create deployment getting-started --image=zhenjingcool/getting-started
deployment.apps/getting-started created
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
getting-started-7876f88f95-cqj6v 1/1 Running 0 25s
[root@k8s-master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
getting-started 1/1 1 1 58s
[root@k8s-master ~]#
对pod进行扩容,扩容到3个副本集
[root@k8s-master ~]# kubectl scale --replicas=3 deployment/getting-started
deployment.apps/getting-started scaled
[root@k8s-master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
getting-started 2/3 3 2 3m12s
[root@k8s-master ~]#
5.3 验证维持副本期望数
我们删除一个pod,看k8s是否可以为我们创建一个新的pod来达到指定的副本数
[root@k8s-master ~]# kubectl get pod -o wide #删除特定pod之前
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
getting-started-7876f88f95-cqj6v 1/1 Running 0 10m 10.2.36.67 k8s-node1 <none> <none>
getting-started-7876f88f95-rscqw 1/1 Running 0 7m54s 10.2.36.68 k8s-node1 <none> <none>
getting-started-7876f88f95-t2zc4 0/1 ImagePullBackOff 0 7m54s 10.2.169.129 k8s-node2 <none> <none>
[root@k8s-master ~]# kubectl delete getting-started-7876f88f95-t2zc4 #删除特定pod
[root@k8s-master ~]# kubectl get pod -o wide #删除特定pod之后
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
getting-started-7876f88f95-cqj6v 1/1 Running 0 11m 10.2.36.67 k8s-node1 <none> <none>
getting-started-7876f88f95-lh5n5 0/1 ContainerCreating 0 6s <none> k8s-node2 <none> <none>
getting-started-7876f88f95-rscqw 1/1 Running 0 8m55s 10.2.36.68 k8s-node1 <none> <none>
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
getting-started-7876f88f95-cqj6v 1/1 Running 0 12m 10.2.36.67 k8s-node1 <none> <none>
getting-started-7876f88f95-lh5n5 1/1 Running 0 61s 10.2.169.130 k8s-node2 <none> <none>
getting-started-7876f88f95-rscqw 1/1 Running 0 9m50s 10.2.36.68 k8s-node1 <none> <none>
5.4 暴露服务
[root@k8s-master ~]# kubectl expose deployment getting-started --port=3000 --target-port=3000 --type=NodePort
service/getting-started exposed
[root@k8s-master ~]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
getting-started NodePort 10.1.36.76 <none> 3000:31620/TCP 23s app=getting-started
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 9h <none>
[root@k8s-master ~]#
然后我们浏览器访问:
6 资源清单
6.1 k8s中的资源
集群资源的分类:分为三种:名称空间级别 集群级别 元数据型
资源举例
[root@k8s-master ~]# kubectl get pod -n default #获取默认名称空间下的pod
NAME READY STATUS RESTARTS AGE
getting-started-7876f88f95-cqj6v 1/1 Running 0 42m
getting-started-7876f88f95-lh5n5 1/1 Running 0 31m
getting-started-7876f88f95-rscqw 1/1 Running 0 39m
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get pod -n kube-system # 获取系统名称空间下的pod
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-577f77cb5c-cd6n2 1/1 Running 0 9h
calico-node-4zhjj 1/1 Running 0 9h
calico-node-w8bjx 1/1 Running 0 9h
calico-node-z2vzl 1/1 Running 0 9h
coredns-6d56c8448f-kl89c 1/1 Running 0 9h
coredns-6d56c8448f-w9fnl 1/1 Running 0 9h
etcd-k8s-master 1/1 Running 0 9h
kube-apiserver-k8s-master 1/1 Running 0 9h
kube-controller-manager-k8s-master 1/1 Running 0 9h
kube-proxy-hf5dw 1/1 Running 0 9h
kube-proxy-hgzsn 1/1 Running 0 9h
kube-proxy-xjm7j 1/1 Running 0 9h
kube-scheduler-k8s-master 1/1 Running 0 9h
[root@k8s-master ~]#
6.1.1 名称空间级别的资源
工作负载型资源(workload):Pod、ReplicaSet、Deployment、StatefulSet、DaemonSet、Job、CronJob
服务发现及负载均衡型资源:Service、Ingress
配置与存储型资源:Volume、CSI
特殊类型存储卷:ConfigMap(当配置中心来使用的资源类型)、Secret(保存敏感数据)、DownwardAPI(把外部环境中的信息输出给容器)
6.1.2 集群级别的资源
Namespace、Node、Role
6.1.3 元数据型资源
HPA、PodTemplate、LimitRange
6.2 资源清单中的主要字段
必须属性
参数名 | 字段说明 | 类型 |
version | String | 这里是指的是K8SAPl的版本,目前基本上是v1,可以用 kubectl api versions命令查询 |
kind | String | 这里指的是yam文件定义的资源类型和角色,比如:od |
metadata | object | 元数据对象,下面是它的属性 |
metadata. name | String | 元数据对象的名字,这里由我们编写,比如命名pod的名字 |
metadata. namespace | String | 元数据对象的命名空间,由我们自身定义 |
Spec | object | 详细定义对象,下面是它的属性 |
spec.containers[] | list | 这里是Spec对象的容器列表定义,是个列表 |
spec. containers.name | String | 这里定义容器的名字 |
spec.containers.image | String | 这里定义要用到的镜像名称 |
主要属性
参数名 字段说明 类型
spec. containers.name String 这里定义容器的名字
spec.containers.image String 这里定义要用到的镜像名称
spec.containers[]. imagePullPolicy String 定义镜像拉取策略,有 Always、 Never 、IfNotPresent三个值可选(1) Always:意思是每次都尝试重新拉取镜像;(2) Never:表示仅使用本地镜像 ;(3)、IfNotPresent:如果本地有镜像就使用本地镜像,没有就拉取在线镜像。上面三个值都没设置的话,默认是 Always
spec.containers[].command[] List 指定容器启动命令,因为是数组可以指定多个,不指定则使用镜像打包时使用的启动命
spec.containers[].args[] List 指定容器启动命令参数,因为是数组可以指定多个。
spec.containers[].workingDir String 指容器的工作目录
spec.containers[]. volumeMounts[] List 指定容器内部的存储卷配置
spec.containers[].volumeMounts[].name String 指定可以被容器挂载的存储卷的名称
spec.containers[].volumeMounts[].mountPath String 指可以被容器挂载的存储卷的路径
spec.containers[].volumeMounts[]. readOnly String 设置存储卷路径的读写模式,ture或者 false,
spec.containers[].ports[] List 指定容器需要用到的端口列表
spec.containers[].ports[].name String 指定端口名称
spec.containers[].ports[].containerPort String 指定容器需要监听的端口号
spec. containers[.ports[].hostPort String 指定容器所在主机需要监听的端口号,默认跟上面 icontainerPort相同,注意设置了 hostPort,同一台主机无法启动该容器的相同副本(因为主机的端口号不能相同,这样会冲突)
spec. containers[]-ports[.protocol String 指定端口协议,支持TCP和UDP,默认值为 TCP
spec. containers[]. env[] List 指定容器运行前需设置的环境变量列表
spec. containers[].env[].name String 指环境变量名称
spec.containers[].env].value String 指定环境变量值
spec.containers[].resources Object 指定资源限制和资源请求的值(这里开始就是设置容器的资源上限)
spec.containers[].resources.limits Object 指设置容器运行时资源的运行上限
spec.containers[].resources.limits.cpu String 指定CPU的限制,单位为core数,将用于docker run-cpu-shares参数(这里前面文章 Pod资源限制有讲过 HAP 那个啥的,哈哈 主要我也忘记具体名字了)
spec.containers[].resources. limits.memory String 指定MEM内存的限制,单位为MB、GiB spec.containers[].resources.requests
spec.containers[].resources.requests.cpu string cpu请求,单位为core数,容器启动时初始化可用数量
spec.containers.resources.requests.memory String 内存请求,单位为MB、GB,容器启动的初始化可用数量
额外属性
参数名 字段说明 类型
spec.restartPolicy String 定义Pod的重启策略,可选值为 Always、 OnFailure,默认值为Always;1、Always:pod一旦终止运行,则无论容器是如何终止的, kubelet服务都将重启它。如果容器正常结束(退出码为0),则 kubelet将不会重启它; 2、OnFailure:只有pod以非零退出码终止时, kubelet会重启该容;3、Never:pod终止后, kubeletMast将退出码报告给,会重启该Pod.
spec.nodeSelector Object 定义node的 Label过滤标签,以 key value格式指定
spec.imagePullSecrets Object 定义pull镜像时 secret使用名称,以 name secretkey格式指定
spec.hostNetwork Boolean 定义是否使用主机网络模式,默认值为 false,设置true表示使用宿主机网络,不使用 docker网桥,同时设置了true将无法在同一台宿主机上启动第二个副本。
6.3 使用资源清单配置Pod并启动
编写一个资源清单文件pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: getting-started
namespace: default
labels:
app: myapp
version: v1
spec:
containers:
- name: app
image: zhenjingcool/getting-started
- name: test
image: zhenjingcool/getting-started
这里,我们配置了一个Pod,里面有两个container,分别是app和test。
我们启动这个pod
[root@k8s-master ~]# kubectl apply -f pod.yaml
pod/getting-started created
[root@k8s-master ~]#
理论上,因为两个container端口冲突,只会启动一个,我么验证一下
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
getting-started 1/2 CrashLoopBackOff 7 15m
[root@k8s-master ~]#
然后我们使用如下2个命令可以查看pod详情和查看pod中指定容器的日志,进而定位容器启动失败原因
[root@k8s-master ~]# kubectl describe pod getting-started
[root@k8s-master ~]# kubectl logs getting-started -c test
Using sqlite database at /etc/todos/todo.db
node:events:491
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (node:net:1740:16)
at listenInCluster (node:net:1788:12)
at Server.listen (node:net:1876:7)
at Function.listen (/app/node_modules/express/lib/application.js:635:24)
at /app/src/index.js:18:9
Emitted 'error' event on Server instance at:
at emitErrorNT (node:net:1767:8)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
code: 'EADDRINUSE',
errno: -98,
syscall: 'listen',
address: '::',
port: 3000
}
Node.js v18.16.0
[root@k8s-master ~]#
由此,我们可以看出,确实是端口占用导致的
7 Pod生命周期
一个pod里有一个容器,这个容器里面运行了一个进程,这个进程可能处于异常状态,但是容器还处于running状态,显然这种情况需要想办法解决
readness:比如容器中运行了tomcat,我们需要tomcat成功启动后,才能设置pod为running状态
liveness:比如容器中运行了nginx,而这个nginx可能已经假死,我们要有方式来判断nginx实际状态来设置pod状态
7.1 Init C(Init容器)
Pod中可以有一个或多个先于应用容器启动的Init容器。
Init容器的特点:
- Init容器总是运行到成功完成为止
- 每个Init容器都必须在下一个Init容器启动之前成功完成
如果Pod的Init容器失败,k8s会不断的重启该Pod,直到Init容器成功为止。
示例
init-pod.yaml
这个资源描述文件,描述了一个pod,这个pod包含一个容器:myapp-container,还有两个Init容器:init-myservice、init-mydb
其中busybox是一个镜像,里面包含了常用的linux命令
myapp-container容器启动过程中,会首先检查两个init-myservice、init-mydb是否已经存在,一直等到这两个Service启动之后,my-container容器才变更状态为Running状态。
其中init-myservice、init-mydb是两个Service
[root@k8s-master ~]# cat init-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
[root@k8s-master ~]#
myservice.yaml
[root@k8s-master ~]# cat myservice.yaml
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
[root@k8s-master ~]#
mydb.yaml
[root@k8s-master ~]# cat mydb.yaml
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
[root@k8s-master ~]#
我们首先对环境做一下初始化,删除已经存在的Service、Deployment、Pod。
[root@k8s-master ~]# kubectl delete svc --all
[root@k8s-master ~]# kubectl delete pod --all
[root@k8s-master ~]# kubectl delete deployment --all
然后,启动myapp-pod这个Pod
[root@k8s-master ~]# kubectl apply -f init-pod.yaml
pod/myapp-pod created
[root@k8s-master ~]#
查看pod,可以看到,正在等待0/2个Init容器完成初始化。
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 22s
[root@k8s-master ~]#
然后,我们启动第一个Init容器
[root@k8s-master ~]# kubectl apply -f myservice.yaml
service/myservice created
[root@k8s-master ~]#
查看pod,可以看到,正在等待1/2个Init容器完成初始化
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:1/2 0 68s
[root@k8s-master ~]#
同样,我们启动第二个Init容器。最终,我们可以看到pod的状态变为了Running
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 25m
[root@k8s-master ~]#
说明:
- 在Pod启动过程中,Init容器会按照顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出
- 如果由于运行时或失败退出,将导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试。然而,如果Pod的restartPolicy设置为Always,Init容器失败时会使用RestartPolicy策略
- 在所有的Init容器没有成功之前,Pod将不会变为Ready状态。Init容器的端口将不会在Service中进行聚集。正在初始化中的Pod处于Pending状态,但应该会将Initializing状态设置为true
- 如果Pod重启,所有Init容器必须重新执行
- 对Init容器spec的修改被限定在容器image字段,修改其他字段都不会生效。更改Init容器的image字段,等价于重启该Pod
7.2 探针
探针是由kubelet对容器执行的定期诊断。要执行诊断,kubelet调用由容器实现的Handler。有3种类型的处理程序:
- ExecAction:在容器内执行指定命令,如果命令退出时返回码为0则认为诊断成功
- TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查,如果端口打开,则诊断被认为是成功的
- HTTPGetAction:对指定的端口和路径上的容器的IP地址执行HTTP Get请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的
有2种类型的探针:
- livenessProbe:指示容器是否正在运行。如果存活探测失败,则kubelet会杀死容器,并且容器将根据重启策略进行相应的动作
- readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的IP地址
7.2.1 readinessProbe示例
在这个示例中,我们创建一个Pod,在Pod中我们使用readinessProbe探测是否存在某个页面,以此探测结果来决定Pod的READY的值
readinessProbe-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: zhenjingcool/getting-started
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
然后我们部署这个Pod
[root@k8s-master ~]# kubectl apply -f readinessProbe-httpget.yaml
pod/readiness-httpget-pod created
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 22 22h
readiness-httpget-pod 0/1 Running 0 2s
[root@k8s-master ~]#
我们看到,虽然Pod状态是Running,但是READY值为0/1.表示该Pod还未准备好。
我们查看日志
[root@k8s-master ~]# kubectl describe pod readiness-httpget-pod
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 76s default-scheduler Successfully assigned default/readiness-httpget-pod to k8s-node1
Normal Pulled 75s kubelet Container image "zhenjingcool/getting-started" already present on machine
Normal Created 75s kubelet Created container readiness-httpget-container
Normal Started 75s kubelet Started container readiness-httpget-container
Warning Unhealthy 10s (x22 over 73s) kubelet Readiness probe failed: Get "http://10.2.36.74:80/index1.html": dial tcp 10.2.36.74:80: connect: connection refused
[root@k8s-master ~]#
可以看到,Readiness probe failed: index1.html不存在字样。
7.2.2 livenessProbe示例
livenessProbe.yaml
这里我们定义一个Pod,在这个pod中我们创建一个文件/tmp/live,然后60秒后删除。然后定义个livenessProbe探针,检查/tmp/live是否存在,如果不存在则删除该pod,创建新pod。预期结果是Pod将会每60秒重启一次。
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","touch /tmp/live;sleep 60;rm -rf /tmp/live;sleep 3600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
然后我们部署这个Pod
[root@k8s-master ~]# kubectl apply -f livenessProbe.yaml
pod/liveness-exec-pod created
[root@k8s-master ~]#
然后大约过几分钟查看Pod
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 0/1 CrashLoopBackOff 7 17m
[root@k8s-master ~]#
可以看到RESTARTS重启了7次。
7.3 start和stop
这里我们演示一下start和stop生命周期过程
startStopProbe.yaml
在这个资源描述文件中,我们创建一个Pod,在这个Pod启动完成之后,会使用start生命周期向/usr/share/message中写入一行文本;在Pod退出之前,会使用stop生命周期也写入一行文本
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
namespace: default
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh","-c","echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh","-c","echo Hello from the preStop handler > /usr/share/message"]
启动这个Pod
[root@k8s-master ~]# kubectl apply -f startStopProbe.yaml
pod/lifecycle-demo created
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
lifecycle-demo 1/1 Running 0 38s
[root@k8s-master ~]#
然后我们进入容器内部看查看/usr/share/message文件,看是否有写入的文本
[root@k8s-master ~]# kubectl exec lifecycle-demo -it -- /bin/sh
# cat /usr/share/message
Hello from the postStart handler
#
同样,当Pod退出的时候也会向这个文件里写入文本。
8 控制器
8.1 Pod分类
Pod可以分为两种:
- 自主式Pod,Pod退出后不会被重新创建
- 控制器管理的Pod,在控制器的生命周期里,始终维持Pod的副本数量
下面我们介绍一下控制器
8.2 控制器类型
控制器有如下几种类型:
- ReplicationController和ReplicaSet
- Deployment
- DaemonSet
- SateFulSet
- Job/CronJob
- Horizontal Pod Autoscaling
8.2.1 ReplicationController和ReplicaSet
RC用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收
在新版本的k8s中建议使用RS来取代RC。RS和RC没有本质不同,只是名字不一样,并且RS支持集合式的selector
8.2.2 Deployment
Deployment为Pod和RS提供了一个声明式定义(declarative)方法,用来替代以前的RC来方便的管理应用,典型的应用场景包括
- 定义Deployment来创建Pod和ReplicaSet
- 滚动升级和回滚应用
- 扩容和缩容
- 暂停和继续Deployment
8.2.3 DaemonSet
DaemonSet确保全部(或者一些)Node上运行一个Pod的副本。当有Node加入集群时,也会为他们新增一个Pod。当有Node从集群移除时,这些Pod也会被回收。删除DaemonSet将会删除它创建的所有Pod
使用DaemonSet的一些典型用法
- 运行集群存储daemon,例如在每个Node上运行glusterd、ceph
- 在每个Node上运行日志收集daemon,例如fluentd、logstash
- 在每个Node上运行监控daemon,例如Prometheus Node Exporter、collectd、Datadog代理、New Relic代理,或Ganglia gmond
8.2.4 Job和CronJob
Job负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束
CronJob负责管理基于时间的Job
8.2.5 StatefulSet
StatefulSet作为Controller为Pod提供唯一的标识。他可以保证部署和scale的顺序
StatefulSet是为了解决有状态服务的问题,其应用场景包括:
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
- 有序收缩,有序删除(即从N-1到0)
8.2.6 Horizontal Pod Autoscaling
应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?这就依赖于Horizontal Pod Autoscaling了,顾名思义,使Pod水平自动缩放
8.3 示例
8.3.1 RS
rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-pod
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: getting-started-container
image: zhenjingcool/getting-started
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
创建
[root@k8s-master ~]# kubectl create -f rs.yaml
replicaset.apps/myapp-pod created
[root@k8s-master ~]#
查看
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-pod 3 3 0 22s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod-5xz6p 1/1 Running 0 28s
myapp-pod-c4cvz 1/1 Running 0 28s
myapp-pod-tsh49 1/1 Running 0 28s
[root@k8s-master ~]#
我们删除这些pod,RS会帮我们重新生成Pod
[root@k8s-master ~]# kubectl delete pod --all
pod "myapp-pod-5xz6p" deleted
pod "myapp-pod-c4cvz" deleted
pod "myapp-pod-tsh49" deleted
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod-7vh7d 1/1 Running 0 14s
myapp-pod-rfbtk 1/1 Running 0 14s
myapp-pod-wpc4x 1/1 Running 0 14s
[root@k8s-master ~]#
因为我们在资源配置文件中指定了标签,这里我们可以看到这些标签
[root@k8s-master ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-pod-c274d 1/1 Running 0 4m14s tier=frontend
myapp-pod-jqz4h 1/1 Running 0 4m14s tier=frontend
myapp-pod-km44q 1/1 Running 0 4m14s tier=frontend
[root@k8s-master ~]#
修改某一个Pod的标签,我们看看会有什么事情发生
[root@k8s-master ~]# kubectl label pod myapp-pod-c274d tier=frontend1 --overwrite=True
pod/myapp-pod-c274d labeled
[root@k8s-master ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-pod-c274d 1/1 Running 0 7m48s tier=frontend1
myapp-pod-h8rmb 1/1 Running 0 5s tier=frontend
myapp-pod-jqz4h 1/1 Running 0 7m48s tier=frontend
myapp-pod-km44q 1/1 Running 0 7m48s tier=frontend
[root@k8s-master ~]#
我们可以看到,修改某一个Pod标签后,RS会为我们创建一个新的Pod(因为资源清单文件中我们配置了RS的匹配标签)。也就是说RS中标签很重要。
8.3.2 RS与Deployment关联实现滚动更新
Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)方法,用来替代以前的ReplicationController来方便的管理应用。典型的应用场景包括
- 定义Deployment来创建Pod和ReplicaSet
- 滚动升级和回滚应用
- 扩容和缩容
- 暂停和继续Deployment
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
创建这个Deployment
[root@k8s-master ~]# kubectl apply -f deployment.yaml --record
deployment.apps/nginx-deployment created
[root@k8s-master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 0/3 3 0 2s
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 54s
[root@k8s-master ~]#
同时,k8s会同时创建RS和pod
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-7848d4b86f 3 3 3 3m48s
[root@k8s-master ~]#
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-7848d4b86f-cgvjj 1/1 Running 0 4m36s
nginx-deployment-7848d4b86f-l5wq9 1/1 Running 0 4m36s
nginx-deployment-7848d4b86f-rhgps 1/1 Running 0 4m36s
[root@k8s-master ~]#
8.3.3 为deployment扩容
以上面部署的Deployment为例,我们扩容到10个容器
[root@k8s-master ~]# kubectl scale deployment nginx-deployment --replicas=10
deployment.apps/nginx-deployment scaled
[root@k8s-master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/10 10 3 14m
[root@k8s-master ~]#
8.3.4 为Deployment更新镜像
以上面的Deployment为例,我们将镜像从nginx更新为getting-started
[root@k8s-master ~]# kubectl set image deployment/nginx-deployment nginx=zhenjingcool/getting-started
然后,我们看一下更新的过程。我们可以看到,会有新RS的创建来替代旧RS
8.3.5 为Deployment执行回滚
以上面的Deployment为例,我们执行回滚镜像操作
[root@k8s-master ~]# kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment rolled back
[root@k8s-master ~]#
我们查看一下回滚过程
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-pod 3 3 3 13h
nginx-deployment-76b74979db 8 8 8 7m32s
nginx-deployment-7848d4b86f 5 5 0 29m
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-pod 3 3 3 13h
nginx-deployment-76b74979db 7 8 8 7m34s
nginx-deployment-7848d4b86f 6 5 1 29m
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-pod 3 3 3 13h
nginx-deployment-76b74979db 1 1 1 7m43s
nginx-deployment-7848d4b86f 10 10 7 29m
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-pod 3 3 3 13h
nginx-deployment-76b74979db 0 0 0 7m46s
nginx-deployment-7848d4b86f 10 10 10 29m
[root@k8s-master ~]#
查看回滚状态
[root@k8s-master ~]# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@k8s-master ~]#
查看变更历史
[root@k8s-master ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
2 kubectl apply --filename=deployment.yaml --record=true
3 kubectl apply --filename=deployment.yaml --record=true
[root@k8s-master ~]#
回滚命令也可以指定回滚到某个指定的版本
kubectl rollout undo deployment/nginx-deployment --to-revision=1
8.3.6 DaemonSet
daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: daemonset-example
template:
metadata:
labels:
name: daemonset-example
spec:
containers:
- name: daemonset-example
image: nginx
部署资源描述文件,我们可以看到,在每个Node节点上都会创建一个Pod
[root@k8s-master ~]# kubectl create -f daemonset.yaml
daemonset.apps/daemonset-example created
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemonset-example-5v4qw 0/1 ContainerCreating 0 2s <none> k8s-node1 <none> <none>
daemonset-example-p9fqs 0/1 ContainerCreating 0 2s <none> k8s-node2 <none> <none>
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemonset-example-5v4qw 1/1 Running 0 7s 10.2.36.112 k8s-node1 <none> <none>
daemonset-example-p9fqs 1/1 Running 0 7s 10.2.169.169 k8s-node2 <none> <none>
[root@k8s-master ~]#
当我们删除一个Pod,将会自动创建一个新的Pod,以保证每个Node上都会有一个Pod存在
[root@k8s-master ~]# kubectl delete pod daemonset-example-5v4qw
pod "daemonset-example-5v4qw" deleted
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemonset-example-54s7h 0/1 ContainerCreating 0 1s <none> k8s-node1 <none> <none>
daemonset-example-p9fqs 1/1 Running 0 3m57s 10.2.169.169 k8s-node2 <none> <none>
[root@k8s-master ~]#
8.3.7 Job
job.yaml
这个资源描述文件,描述的是创建一个Job,里面配置了一个容器,这个容器创建后会执行perl来计算圆周率到小数点后2000位
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
metadata:
name: pi
spec:
containers:
- name: pi
image: perl
command: ["perl","-Mbignum=bpi","-wle","print bpi(2000)"]
restartPolicy: Never
部署
kubectl create -f job.yaml
查看,我们可以看到创建了一个名称为pi-lm7xp的Pod
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pi-lm7xp 0/1 ContainerCreating 0 40s <none> k8s-node1 <none> <none>
[root@k8s-master ~]# kubectl describe pod pi-lm7xp
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m56s default-scheduler Successfully assigned default/pi-lm7xp to k8s-node1
Normal Pulling 2m55s kubelet Pulling image "perl"
[root@k8s-master ~]#
我们也可以查看Job日志
[root@k8s-master ~]# kubectl describe job pi
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 12s job-controller Created pod: pi-t2496
Normal Completed 0s job-controller Job completed
[root@k8s-master ~]#
我们看到,这个Job运行成功了,我们看一下计算的圆周率结果(这里做了修改,只计算到200位)
[root@k8s-master ~]# kubectl logs pi-t2496
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303820
[root@k8s-master ~]#
8.3.8 CronJob
cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the K8s cluster
restartPolicy: OnFailure
部署时出错了,暂时还未解决,待后续解决
[root@k8s-master ~]# kubectl apply -f cronjob.yaml
error: error validating "cronjob.yaml": error validating data: ValidationError(CronJob.spec.jobTemplate): unknown field "template" in io.k8s.api.batch.v1beta1.JobTemplateSpec; if you choose to ignore these errors, turn validation off with --validate=false
[root@k8s-master ~]#
9 Service
k8s Service可以把它理解为微服务,Service可以包含若干个Pod,其起到了服务发现与注册的作用。
9.1 Service类型
- ClusterIP:默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP
- NodePort:在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过NodeIP:NodePort来访问该服务
- LoadBalancer:在NodePort的基础上,借助cloud provider创建一个外部负载均衡器,并将请求转发到NodeIP:NodePort
- ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建
9.2 ClusterIP示例
这里,我们创建一个Deployment,其管理3个Pod。我们再创建一个Service,其类型为ClusterIP,客户端可以通过这个ClusterIP来访问部署在三个Pod中的应用,且实现负载均衡访问
为实现上述功能,需要以下几个组件的协同工作
- apiserver:用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
- kube-proxy:k8s的每个节点中都有一个叫kube-proxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables(或ipvs)规则中
- iptables:使用NAT等技术将virtualIP的流量转至endpoint中
svc-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: stabel
template:
metadata:
labels:
app: myapp
release: stabel
env: test
spec:
containers:
- name: myapp
image: zhenjingcool/getting-started
ports:
- name: http
containerPort: 80
启动
[root@k8s-master ~]# kubectl apply -f svc-deployment.yaml
deployment.apps/myapp-deploy created
[root@k8s-master ~]#
svc-service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: ClusterIP
selector:
app: myapp
release: stabel
ports:
- name: http
port: 3000
targetPort: 3000
部署
[root@k8s-master ~]# kubectl apply -f svc-service.yaml
service/myapp created
[root@k8s-master ~]#
然后,我们查看一下部署的svc
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 4d1h
myapp ClusterIP 10.1.1.228 <none> 3000/TCP 12s
[root@k8s-master ~]#
然后,我们可以通过集群内部ip(ClusterIP)访问这个应用
[root@k8s-master ~]# curl 10.1.1.228:3000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0" />
<link rel="stylesheet" href="css/bootstrap.min.css" crossorigin="anonymous" />
<link rel="stylesheet" href="css/font-awesome/all.min.css" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css?family=Lato&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<title>Todo App</title>
</head>
<body>
<div id="root"></div>
<script src="js/react.production.min.js"></script>
<script src="js/react-dom.production.min.js"></script>
<script src="js/react-bootstrap.js"></script>
<script src="js/babel.min.js"></script>
<script type="text/babel" src="js/app.js"></script>
</body>
</html>
[root@k8s-master ~]#
9.3 Headless Service
有时不需要或者不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定ClusterIP的值为None来创建headless service。这类Service并不会分配Cluster IP,kube-proxy不会处理他们,而且平台也不会为他们进行负载均衡和路由
svc-headnessservice.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
type: ClusterIP
selector:
app: myapp
clusterIP: "None"
ports:
- port: 3000
targetPort: 3000
部署
[root@k8s-master ~]# kubectl apply -f svc-headlessservice.yaml
service/myapp-headless created
[root@k8s-master ~]#
查看
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 4d11h
myapp ClusterIP 10.1.1.228 <none> 3000/TCP 9h
myapp-headless ClusterIP None <none> 3000/TCP 49s
[root@k8s-master ~]#
9.4 NodePort
nodePort的原理在于在node上开了一个端口,将向该端口的流量导入到kube-proxy,然后由kube-proxy进一步到给对应的pod
svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: NodePort
selector:
app: myapp
release: stabel
ports:
- name: http
port: 3000
targetPort: 3000
部署
[root@k8s-master ~]# kubectl apply -f svc-nodeport.yaml
service/myapp created
[root@k8s-master ~]#
查看部署的服务
[root@k8s-master ~]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 119s <none>
myapp NodePort 10.1.137.221 <none> 3000:30964/TCP 20s app=myapp,release=stabel
[root@k8s-master ~]#
然后,我们访问当前节点的30964端口
可以看到,我们可以通过nodeport的方式暴露服务
9.5 ExternalName
这种类型的Service通过返回CNAME和它的值,可以将服务映射到externalName字段的内容(例如:szj.com)。ExternalName Service是Service的特例,它没有selector,也没有定义任何的端口和Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
svc-externalname.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: ExternalName
externalName: szj.com
部署
[root@k8s-master ~]# kubectl apply -f svc-externalname.yaml
service/myapp created
[root@k8s-master ~]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 34h <none>
myapp ExternalName <none> szj.com <none> 11s <none>
[root@k8s-master ~]#
9.6 ingress
ingress访问方案示意图
9.6.1 部署ingress-nginx
首先创建一个目录
[root@k8s-master ~]# mkdir /opt/ingress
[root@k8s-master ~]# cd /opt/ingress/
[root@k8s-master ingress]#
然后,下载mandatory.yaml
[root@k8s-master ingress]# wget https://gitee.com/mirrors/ingress-nginx/raw/nginx-0.25.0/deploy/static/mandatory.yaml
修改这个yaml的如下内容(标红的是要添加的内容)
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
- "networking.k8s.io"
这个资源描述文件中,用到了镜像,我们看一下,是哪个镜像
[root@k8s-master ingress]# less mandatory.yaml |grep image
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
[root@k8s-master ingress]#
因为这个镜像比较大,我们预先下载这个镜像
[root@k8s-master ingress]# docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
为便于拷贝到node1和node2节点,我们把这个镜像保存到本地并进行打包压缩
[root@k8s-master ingress]# docker save -o ingress.contr.tar quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0
这时,我们将得到一个ingress.contr.tar文件
[root@k8s-master ingress]# ll
total 501460
-rw------- 1 root root 513486848 May 30 05:50 ingress.contr.tar
-rw-r--r-- 1 root root 6032 May 30 05:41 mandatory.yaml
[root@k8s-master ingress]#
打包压缩
[root@k8s-master ingress]# tar -zcvf ingress.contr.tar.gz ingress.contr.tar
然后,我们将这个ingress.contr.tar.gz拷贝到node1和node2节点
[root@k8s-node2 ~]# scp root@k8s-master:/opt/ingress/ingress.contr.tar.gz .
然后解压
[root@k8s-node2 ~]# tar -zxvf ingress.contr.tar.gz
然后导入到docker本地仓库
[root@k8s-node2 ~]# docker load -i ingress.contr.tar
部署
[root@k8s-master ingress]# kubectl apply -f mandatory.yaml
然后,我们查看ingress-nginx名称空间下的pod
[root@k8s-master ingress]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-6dd687bfdf-zwm4m 1/1 Running 0 12s
[root@k8s-master ingress]#
官网下载service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30080 #修改下http访问端口
protocol: TCP
- name: https
port: 443
targetPort: 443
nodePort: 30443 #修改下https访问端口
protocol: TCP
selector:
app.kubernetes.io/name: ingress-nginx #绑定mandatory.yaml中nginx-ingress-controller的pod(对应相对的标签)
app.kubernetes.io/part-of: ingress-nginx #绑定mandatory.yaml中nginx-ingress-controller的pod(对应相对的标签)
---
部署
[root@k8s-master ingress]# kubectl apply -f service-nodeport.yaml
service/ingress-nginx created
[root@k8s-master ingress]#
[root@k8s-master ingress]#
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.1.154.118 <none> 80:30080/TCP,443:30443/TCP 13s
[root@k8s-master ingress]#
9.6.2 ingress http代理访问
ingress.http.yaml
这里面创建了一个Deployment和一个Service
apiVersion: apps/v1
kind: Deployment # 先创建一个deployment
metadata:
name: nginx-dm
spec:
replicas: 2
selector:
matchLabels:
app: nginx-dm
template:
metadata:
labels:
app: nginx-dm # 标签意义就不再重复说了
spec:
containers:
- name: nginx-dm
image: nginx # 我们部署一个nginx镜像
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:
name: nginx-svc
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: nginx-dm # 管理上面的pod
部署这个service
[root@k8s-master ingress]# kubectl apply -f ingress.http.yaml
查看
[root@k8s-master ingress]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 2d12h <none>
nginx-svc ClusterIP 10.1.13.189 <none> 80/TCP 24m name=nginx
[root@k8s-master ingress]#
9.6.3 创建ingress
创建我们的ingress1.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress # 注意这里是 ingress 的类型欧
metadata:
name: nginx-test
spec:
ingressClassName: "nginx"
rules:
- host: szj.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc # 说明外部访问 www1.atguigu.com 就是访问内部我们上面定义的那个 nginx-svc(前面定义的我们自己那个svc的名称)
port:
number: 80
部署
[root@k8s-master ingress]# kubectl apply -f ingress1.yaml
查看
[root@k8s-master ingress]# kubectl get ingress -o wide
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-test <none> szj.com 80 5m3s
[root@k8s-master ingress]#
因为这个域名不存在,我们需要修改hosts文件
192.168.3.136 szj.com
然后,我们查看ingress-nginx的端口
[root@k8s-master ingress]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.1.157.227 <none> 80:30080/TCP,443:30443/TCP 20m
[root@k8s-master ingress]#
然后,我们浏览器访问szj.com:30080查看效果
9.6.4 一个ingress对应多个svc
更进一步,我们实现下图的功能
我们删除上面例子中的如下svc和deployment
[root@k8s-master ingress]# kubectl delete -f ingress1.yaml
[root@k8s-master ingress]# kubectl delete -f ingress.http.yaml
然后创建一个文件夹
[root@k8s-master ingress]# mkdir vh
[root@k8s-master ingress]# cd vh
第一套deployment和svc
deployment1.yaml
apiVersion: apps/v1
kind: Deployment # 先创建一个deployment
metadata:
name: deployment1 # 名称为 deployment1
spec:
replicas: 2
selector:
matchLabels:
name: nginx1
template:
metadata:
labels:
name: nginx1 # 标签意义就不再重复说了
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:
name: svc-1
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx1 # 管理上面的pod
第二套deployment和svc
deployment2.yaml
apiVersion: apps/v1
kind: Deployment # 先创建一个deployment
metadata:
name: deployment2 # 名称为 deployment2
spec:
replicas: 2
selector:
matchLabels:
name: nginx2
template:
metadata:
labels:
name: nginx2 # 标签意义就不再重复说了
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:
name: svc-2
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx2 # 管理上面的pod
部署
[root@k8s-master vh]# kubectl apply -f deployment1.yaml
[root@k8s-master vh]# kubectl apply -f deployment2.yaml
查看
[root@k8s-master vh]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 3d21h
svc-1 ClusterIP 10.1.222.37 <none> 80/TCP 8m1s
svc-2 ClusterIP 10.1.55.121 <none> 80/TCP 32s
[root@k8s-master vh]#
创建ingress
ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress # 注意这里是 ingress 的类型欧
metadata:
name: nginx-test
spec:
ingressClassName: "nginx"
rules:
- host: szj1.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-1 # 说明外部访问 www1.atguigu.com 就是访问内部我们上面定义的那个 svc-1(前面定义的第一套我们自己那个svc的名称)
port:
number: 80
- host: szj2.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-2 # 说明外部访问 www1.atguigu.com 就是访问内部我们上面定>义的那个 svc-1(前面定义的第一套我们自己那个svc的名称)
port:
number: 80
部署
[root@k8s-master vh]# kubectl apply -f ingress.yaml
查看
[root@k8s-master vh]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-test nginx szj1.com,szj2.com 80 11s
[root@k8s-master vh]#
查看svc
[root@k8s-master ingress]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.1.223.64 <none> 80:30080/TCP,443:30443/TCP 83s
同样我们需要把szj1.com和szj2.com配置到hosts中去
访问查看效果
我们可以进入到pod中,查看一下ingress为我们自动生成的nginx配置
[root@k8s-master ~]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-6dd687bfdf-nbsz2 1/1 Running 0 23h
[root@k8s-master ~]# kubectl exec nginx-ingress-controller-6dd687bfdf-nbsz2 -n ingress-nginx -it -- /bin/bash
如下所示
www-data@nginx-ingress-controller-6dd687bfdf-nbsz2:/etc/nginx$ ls -l
total 36
drwxr-xr-x 1 www-data www-data 119 Jun 26 2019 geoip
drwxr-xr-x 6 www-data www-data 267 Jul 8 2019 lua
-rw-r--r-- 1 root root 5231 Jul 8 2019 mime.types
drwxr-xr-x 2 www-data www-data 53 Jun 26 2019 modsecurity
lrwxrwxrwx 1 root root 34 Jul 8 2019 modules -> /usr/local/openresty/nginx/modules
-rw-r--r-- 1 www-data www-data 20080 May 31 23:52 nginx.conf
-rw-r--r-- 1 www-data www-data 2 Jul 8 2019 opentracing.json
drwxr-xr-x 6 www-data www-data 4096 Jun 26 2019 owasp-modsecurity-crs
drwxr-xr-x 2 www-data www-data 24 Jul 8 2019 template
www-data@nginx-ingress-controller-6dd687bfdf-nbsz2:/etc/nginx$ cat nginx.conf |grep szj1.com
## start server szj1.com
server_name szj1.com ;
## end server szj1.com
www-data@nginx-ingress-controller-6dd687bfdf-nbsz2:/etc/nginx$
9.6.5 ingress配置https
创建证书以及cert存储方式
[root@k8s-master https]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
Generating a 2048 bit RSA private key
....+++
........................+++
writing new private key to 'tls.key'
-----
[root@k8s-master https]# ll
total 8
-rw-r--r-- 1 root root 1143 Jun 1 05:26 tls.crt
-rw-r--r-- 1 root root 1704 Jun 1 05:26 tls.key
[root@k8s-master https]# kubectl create secret tls tls-secret --key tls.key --cert tls.crt
secret/tls-secret created
[root@k8s-master https]#
创建一个新的Deployment和Service
deployment3.yaml
apiVersion: apps/v1
kind: Deployment # 先创建一个deployment
metadata:
name: deployment3 # 名称为 deployment3
spec:
replicas: 2
selector:
matchLabels:
name: nginx3
template:
metadata:
labels:
name: nginx3 # 标签意义就不再重复说了
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service # 再创建一个自己的 svc
metadata:
name: svc-3
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx3 # 管理上面的pod
部署
[root@k8s-master https]# kubectl apply -f deployment3.yaml
deployment.apps/deployment3 created
service/svc-3 created
[root@k8s-master https]#
[root@k8s-master https]#
[root@k8s-master https]#
[root@k8s-master https]#
[root@k8s-master https]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 4d10h
svc-1 ClusterIP 10.1.222.37 <none> 80/TCP 12h
svc-2 ClusterIP 10.1.55.121 <none> 80/TCP 12h
svc-3 ClusterIP 10.1.166.197 <none> 80/TCP 10s
[root@k8s-master https]#
创建一个ingress规则
ingress3.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress # 注意这里是 ingress 的类型欧
metadata:
name: nginx-https
spec:
tls:
- hosts:
- szj3.com
secretName: tls-secret
ingressClassName: "nginx"
rules:
- host: szj3.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-3 # 说明外部访问 www1.atguigu.com 就是访问内部我们上面定义的那个 svc-1(前面定义的第一套我们自己那个svc的名称)
port:
number: 80
部署
[root@k8s-master https]# kubectl apply -f ingress3.yaml
查看https访问的端口
[root@k8s-master https]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.1.223.64 <none> 80:30080/TCP,443:30443/TCP 12h
[root@k8s-master https]#
页面访问