【Kubernetes】K8s笔记(十二):中级篇实战和小结

Kubernetes 技术要点回顾

Kubernetes 是云原生时代的操作系统,它能够管理大量节点构成的集群,让计算资源“池化”,从而能够自动地调度运维各种形式的应用。

使用 kubeadm 搭建集群

【Kubernetes】K8s笔记(七):中级篇 - 搭建多节点实验环境

搭建多节点的 Kubernetes 集群是一件颇具挑战性的工作,好在社区里及时出现了 kubeadm 这样的工具。

kubeadm 使用容器技术封装了 Kubernetes 组件,所以只要节点上安装了容器运行时(cri-o containerd 等),它就可以自动从网上拉取镜像,然后以容器的方式运行组件,非常简单方便。

配置 Control-Plane

$ sudo kubeadm init \
	--apiserver-advertise-address=<host_ip> \
	--image-repository registry.aliyuncs.com/google_containers \
	--kubernetes-version=<version>\
	--pod-network-cidr=10.10.0.0/16

配置 Worker

$ sudo kubeadm join 192.168.165.133:6443 --token <token> \
        --discovery-token-ca-cert-hash <hash>

Deployment API 对象

【Kubernetes】K8s笔记(八):Deployment 发布无状态的应用

Deployment 是用来管理 Pod 的一种对象,它代表了运维工作中最常见的一类在线业务,在集群中部署应用的多个实例,而且可以很容易地增加或者减少实例数量,从容应对流量压力。

Deployment 的定义里有两个关键字段:一个是 replicas,它指定了实例的数量;另一个是 selector,它的作用是使用标签“筛选”出被 Deployment 管理的 Pod,这是一种非常灵活的关联机制,实现了 API 对象之间的松耦合。

DaemonSet API 对象

【Kubernetes】K8s笔记(九):DaemonSet 守护进程集

DaemonSet 是另一种部署在线业务的方式,它很类似 Deployment,但会在集群里的每一个节点上运行一个 Pod 实例,类似 Linux 系统里的“守护进程”,适合日志、监控等类型的应用。

DaemonSet 能够任意部署 Pod 的关键概念是“污点”(taint)和“容忍度”(toleration)。Node 会有各种“污点”,而 Pod 可以使用“容忍度”来忽略“污点”,合理使用这两个概念就可以调整 Pod 在集群里的部署策略。

Service API 对象

【Kubernetes】K8s笔记(十):Service 解决服务发现的关键问题

Service 是对 Pod IP 地址的抽象,它拥有一个固定的 IP 地址,再使用 iptables 规则把流量负载均衡到后面的 Pod,节点上的 kube-proxy 组件会实时维护被代理的 Pod 状态,保证 Service 只会转发给健康的 Pod。Service 还基于 DNS 插件支持域名,所以客户端就不再需要关心 Pod 的具体情况,只要通过 Service 这个稳定的中间层,就能够访问到 Pod 提供的服务。

Ingress / Ingress Class API 对象和 Ingress Controller 组件

【Kubernetes】K8s笔记(十一):Ingress 集群进出流量总管

Ingress 定义了基于 HTTP 协议的路由规则。

Ingress Controller 是真正的集群入口,控制集群南北向流量,应用 Ingress 规则调度、分发流量,此外还能够扮演反向代理的角色,提供安全防护、TLS 卸载等更多功能。

Ingress Class 是用来管理 Ingress 和 Ingress Controller 的概念,方便我们分组路由规则,降低维护成本。

不过 Ingress Controller 本身也是一个 Pod,想要把服务暴露到集群外部还是要依靠 Service。Service 支持 NodePort、LoadBalancer 等方式,但 NodePort 的端口范围有限,LoadBalancer 又依赖于云服务厂商,都不是很灵活。折中的办法是用少量 NodePort 暴露 Ingress Controller,用 Ingress 路由到内部服务,外部再用反向代理或者 LoadBalancer 把流量引进来。

一个好消息是 Kubernetes 正在进行 Gateway API 对象的开发,现在它已经进入 Beta 阶段

Gateway API 是以 Gateway 资源(代表底层网络网关/代理服务器)为中心的资源集合, Kubernetes 服务网络的健壮性得益于众多供应商实现、得到广泛行业支持且极具表达力、可扩展和面向角色的各个接口。
Gateway API 最初被认为是知名 Ingress API 的继任者, Gateway API 的好处包括(但不限于)对许多常用网络协议的显式支持 (例如 HTTP、TLS、TCP 、UDP) 以及对传输层安全 (TLS) 的紧密集成支持。 特别是 Gateway 资源能够实现作为 Kubernetes API 来管理网络网关的生命周期。

实战架构

这次部署 WordPress,所有的相关应用都运行在 Kubernetes 集群之中,部署方式都是 Deployment。之前的 Nginx 换成了 Nginx Ingress Controller。WordPress 现在有了多个实例。MariaDB 因为要保证数据一致性暂时还只有一个实例,当然后面我们还可以使用 TiDB 这类分布式数据库。

因为 Kubernetes 内置了服务发现机制 Service,我们再也不需要去手动查看 Pod 的 IP 地址了,只要为它们定义 Service 对象,然后使用域名就可以访问 MariaDB、WordPress 这些服务。

网站对外提供服务的两种方式:

  • 让 WordPress 的 Service 对象以 NodePort 的方式直接对外暴露端口 30088,方便测试

  • 给 Nginx Ingress Controller 添加 hostNetwork 属性,直接使用节点上的端口号,类似 Docker 的 host 网络模式,好处是可以避开 NodePort 的端口范围限制

1. 部署 MariaDB

首先使用一个 ConfigMap 对象定义数据库的环境变量:

# mariadb/mariadb-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: maria-cm

data:
  DATABASE: 'db'
  USER: 'wp'
  PASSWORD: '123'
  ROOT_PASSWORD: '123'

然后我们描述一个 Deployment 来描述 MariaDB 部署后期望的状态,这里 replicas 设置为 1,然后使用 envFrom 把配置信息以环境变量的方式注入 Pod:

# mariadb/mariadb-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: maria-dep
  name: maria-dep

spec:
  replicas: 1
  selector:
    matchLabels:
      app: maria-dep

  template:
    metadata:
      labels:
        app: maria-dep
    spec:
      containers:
      - image: mariadb:10
        name: mariadb
        ports:
        - containerPort: 3306

        envFrom:
        - prefix: 'MARIADB_'
          configMapRef:
            name: maria-cm

还需要再为 MariaDB 定义一个 Service 对象,映射端口 3306,让其他应用不再关心 IP 地址,直接用 Service 对象的名字来访问数据库服务:

# mariadb/mariadb_svc.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: maria-dep
  name: maria-svc

spec:
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    app: maria-dep

当然,我们也可以将这三个对象放到同一个 YAML 文件中,对象之间使用 --- 隔开,这样就可以一次性创建好:

# mariadb/mariadb-all.yaml

# mariadb ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: maria-cm

data:
  DATABASE: 'db'
  USER: 'wp'
  PASSWORD: '123'
  ROOT_PASSWORD: '123'

---
# mariadb Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: maria-dep
  name: maria-dep

spec:
  replicas: 1
  selector:
    matchLabels:
      app: maria-dep

  template:
    metadata:
      labels:
        app: maria-dep
    spec:
      containers:
      - image: mariadb:10
        name: mariadb
        ports:
        - containerPort: 3306

        envFrom:
        - prefix: 'MARIADB_'
          configMapRef:
            name: maria-cm

---
# mariadb Service
apiVersion: v1
kind: Service
metadata:
  labels:
    app: maria-dep
  name: maria-svc

spec:
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    app: maria-dep

然后创建这些对象:

$ kubectl apply -f mariadb-all.yaml 
configmap/maria-cm created
deployment.apps/maria-dep created
service/maria-svc created

最后检查一下对象的状态:

$ kubectl get deploy 
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
maria-dep   1/1     1            1           19m

$ kubectl get pods -o wide 
NAME                        READY   STATUS    RESTARTS      AGE     IP           NODE         NOMINATED NODE   READINESS GATES
maria-dep-658f54c96-9zrsw   1/1     Running   0             20m     10.10.1.58   worker1      <none>           <none>

$ kubectl get svc -o wide 
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        5d22h   <none>
maria-svc    ClusterIP   10.104.144.64   <none>        3306/TCP       21m     app=maria-dep

2. 部署 WordPress

因为刚才创建了 MariaDB 的 Service,所以在写 ConfigMap 配置的时候 HOST 就不应该是 IP 地址了,而应该是 DNS 域名,也就是 Service 的名字 maria-svc

# wordpress/wp-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: wp-cm

data:
  HOST: 'maria-svc'
  USER: 'wp'
  PASSWORD: '123'
  NAME: 'db'

编写 WordPress 的 Deployment YAML,replicas 设置 3 个,然后用 envFrom 设置环境变量:

# wordpress/wp-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: wp-dep
  name: wp-dep

spec:
  replicas: 3
  selector:
    matchLabels:
      app: wp-dep

  template:
    metadata:
      labels:
        app: wp-dep
    spec:
      containers:
      - image: wordpress:5
        name: wordpress
        ports:
        - containerPort: 80

        envFrom:
        - prefix: 'WORDPRESS_DB_'
          configMapRef:
            name: wp-cm

此时仍然要为 WordPress 创建 Service 对象,这里使用了 NodePort 类型,并且手工指定了端口号 30088(必须在 30000~32767 之间):

# wordpress/wp-svc.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wp-dep
  name: wp-svc

spec:
  ports:
  - name: http80
    port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30088

  selector:
    app: wp-dep
  type: NodePort

我们还是可以使用 --- 把它们合并为一个文件:

# wordpress/wp-all.yaml

# wp-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: wp-cm

data:
  HOST: 'maria-svc'
  USER: 'wp'
  PASSWORD: '123'
  NAME: 'db'

---
# wp-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: wp-dep
  name: wp-dep

spec:
  replicas: 3
  selector:
    matchLabels:
      app: wp-dep

  template:
    metadata:
      labels:
        app: wp-dep
    spec:
      containers:
      - image: wordpress:5
        name: wordpress
        ports:
        - containerPort: 80

        envFrom:
        - prefix: 'WORDPRESS_DB_'
          configMapRef:
            name: wp-cm

---
# wp-svc.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wp-dep
  name: wp-svc

spec:
  ports:
  - name: http80
    port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30088

  selector:
    app: wp-dep
  type: NodePort

现在创建 WordPress 相关的对象:

$ kubectl apply -f wp-all.yaml 
configmap/wp-cm created
deployment.apps/wp-dep created
service/wp-svc created

然后检查一下创建的所有对象:

$ kubectl get deploy -o wide 
NAME        READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES        SELECTOR
maria-dep   1/1     1            1           23m     mariadb      mariadb:10    app=maria-dep
wp-dep      3/3     3            3           4m46s   wordpress    wordpress:5   app=wp-dep

$ kubectl get pods -o wide 
NAME                        READY   STATUS    RESTARTS   AGE     IP           NODE      NOMINATED NODE   READINESS GATES
maria-dep-658f54c96-9zrsw   1/1     Running   0          23m     10.10.1.58   worker1   <none>           <none>
wp-dep-78647ffc4d-kz956     1/1     Running   0          5m13s   10.10.1.61   worker1   <none>           <none>
wp-dep-78647ffc4d-mz6x9     1/1     Running   0          5m13s   10.10.1.59   worker1   <none>           <none>
wp-dep-78647ffc4d-xchdz     1/1     Running   0          5m13s   10.10.1.60   worker1   <none>           <none>

$ kubectl get svc -o wide 
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        5d22h   <none>
maria-svc    ClusterIP   10.104.144.64   <none>        3306/TCP       23m     app=maria-dep
wp-svc       NodePort    10.100.17.219   <none>        80:30088/TCP   5m27s   app=wp-dep

因为 WordPress 的 Service 对象是 NodePort 类型的,我们可以在集群的每个节点上访问 WordPress 服务。

查看节点 IP:

$ kubectl get nodes -o wide 
NAME         STATUS   ROLES           AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
k8s-master   Ready    control-plane   5d22h   v1.25.2   172.16.63.128   <none>        Ubuntu 22.04.1 LTS   5.15.0-52-generic   containerd://1.5.9-0ubuntu3
worker1      Ready    <none>          5d22h   v1.25.2   172.16.63.129   <none>        Ubuntu 22.04.1 LTS   5.15.0-52-generic   containerd://1.5.9-0ubuntu3

根据节点 IP 访问 30088 端口上的 WordPress:

image

3. 部署 Nginx Ingress Controller

首先定义 Ingress Class,名字就叫 wp-ink

# wp-ingress/wp-ink.yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: wp-ink

spec:
  controller: nginx.org/ingress-controller

然后用 kubectl create 命令生成 Ingress 的样板文件,指定域名是 wp.test,后端 Service 是 wp-svc:80,Ingress Class 就是刚定义的 wp-ink

$ export out="--dry-run=client -o yaml"
$ kubectl create ing wp-ing --rule="wp.test/=wp-svc:80" --class=wp-ink $out

下面是得到的 Ingress YAML,注意路径匹配类型是 Prefix

# wp-ingress/wp-ing.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wp-ing

spec:
  ingressClassName: wp-ink
  rules:
  - host: wp.test
    http:
      paths:
      - backend:
          service:
            name: wp-svc
            port:
              number: 80
        path: /
        pathType: Prefix

编写 Ingress Controller 对象的 YAML,它仍然需要从 Nginx 项目的示例 YAML 修改而来,要改动名字、标签,还有参数里的 Ingress Class:

*kic for kubernetes ingress contrller

# wp-ingress/wp-kic.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wp-kic-dep
  namespace: nginx-ingress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wp-kic-dep

  template:
    metadata:
      labels:
        app: wp-kic-dep

    spec:
      serviceAccountName: nginx-ingress
      # use host network
      hostNetwork: true
      automountServiceAccountToken: true
      containers:
      - image: nginx/nginx-ingress:2.2-alpine
        imagePullPolicy: IfNotPresent
        name: nginx-ingress
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
        - name: readiness-port
          containerPort: 8081
        - name: prometheus
          containerPort: 9113
        readinessProbe:
          httpGet:
            path: /nginx-ready
            port: readiness-port
          periodSeconds: 1
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"

        securityContext:
          allowPrivilegeEscalation: true
          runAsUser: 101 #nginx
          runAsNonRoot: true
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        args:
          - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
          - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
          - -ingress-class=wp-ink

这个 Ingress Controller 不使用 Service,而是给它的 Pod 加上一个特殊字段 hostNetwork,让 Pod 能够使用宿主机的网络,相当于另一种形式的 NodePort。

创建上述对象:

$ kubectl apply -f wp-ink.yaml -f wp-ing.yaml -f wp-kic.yaml 
ingressclass.networking.k8s.io/wp-ink created
ingress.networking.k8s.io/wp-ing created
deployment.apps/wp-kic-dep created

检查一下所有对象是否正常运行:

$ kubectl get deploy -n nginx-ingress -o wide 
NAME          READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS      IMAGES                           SELECTOR
wp-kic-dep    1/1     1            1           62s   nginx-ingress   nginx/nginx-ingress:2.2-alpine   app=wp-kic-dep

$ kubectl get pods -n nginx-ingress -o wide 
NAME                           READY   STATUS    RESTARTS       AGE   IP              NODE      NOMINATED NODE   READINESS GATES
wp-kic-dep-6577bf795f-2zq89    1/1     Running   0              82s   172.16.63.129   worker1   <none>           <none>

$ kubectl get ing -o wide 
NAME      CLASS     HOSTS      ADDRESS   PORTS   AGE
wp-ing    wp-ink    wp.test              80      2m24s

4. 测试

首先向 /etc/hosts 文件中添加一条记录,IP 地址是 nginx-ingress 所在节点的 IP 地址:

172.16.63.129   wp.test

现在浏览器直接访问 wp.test 即可:

image

总结

这个网站离真正实用还差得比较远,但框架已经很完善了,可以在这个基础上添加其他功能,比如创建证书 Secret、让 Ingress 支持 HTTPS 等等。

另外,对于数据库 MariaDB 来说,虽然 Deployment 在发生故障时能够及时重启 Pod,新 Pod 却不会从旧 Pod 继承数据,之前网站的数据会彻底消失,这个后果是完全不可接受的。所以后面会继续学习持久化存储对象 PersistentVolume,以及有状态的 StatefulSet 等对象。

posted @ 2022-10-24 10:39  joexu01  阅读(202)  评论(0编辑  收藏  举报