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,容器启动的初始化可用数量
View Code

额外属性

参数名    字段说明    类型
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将无法在同一台宿主机上启动第二个副本。
View Code

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]#

页面访问

 

posted @ 2023-05-09 23:43  zhenjingcool  阅读(197)  评论(0编辑  收藏  举报