k8s教程
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]#
页面访问