Kind Kubernetes 部署简单应用 上篇_服务器




本文承接《代理环境下在 WSL2 中用 Kind 创建 Kubernetes 集群》一文,主要讲述如何在K8s中部署应用。

首先,我们先讲述后续部署实战中会用到的一些概念


  • Pod
  • Deployment
  • Service
  • Namespaces
  • DNS

然后,重建Kind K8s环境,并部署一个可以从本地访问的简单网页。

目录



- 环境(配置)

- K8s 部署应用相关概念

  1. Workloads

      - Pod

  2. Workloads Resources

      - Deployment

  3. Service

  4. Namespaces

  5. DNS

- 实战步骤

  1. 重建 K8s,暴露 80、443 端口

  2. 部署 Deployment、Service

      - 部署 Deployment

      - 部署 Service

      - 更改 Serivce(nodePort)

- 总结

- 引申

- 后记


环境(配置)



  • Win10 + WSL2
  • Docker desktop 3.3.3 或以上版本
  • kind

K8s 部署应用相关概念


1. Workloads

Workloads(工作负载)是运行在 K8s 上的应用,可以是单独一个组件(比如一个 Job)也可以是多个组件协同运行(比如 deployment,service,ingress 等一起构成一个应用)。

无论是哪种形式,在 K8s 上负载最终都是以 Pod 为基本单位运行的。

Pod 本身有一个生命周期,比如当一个 node 出现故障宕机了,则此 node 上运行的所有 Pod 会失败,这时我们需要重新在其它节点创建 Pod。

一般情况下,我们不直接运行 Pod,我们通过 Workloads Resources 来帮我们管理 Pod。

每个 Workloads Resources 有不同的 controller 相对应,controller 按我们的要求维护 Pod 的状态(比如在一个 node 失败后,在其它节点重建 pod)。

Pod

Pod 是我们在 K8s 中可以创建使用的最小计算单元。每个 Pod 可以由一个或者多个容器组成。

比如,kube-system namespace 内运行的 podsKind Kubernetes 部署简单应用 上篇_docker_02

K8s 为每个 Pod 分配一个集群内部的 IP 和 DNS。

多个容器组成的 Pods 中,容器之间共享 Pod 的存储和网络。一个 Pod 中的多个容器总是安排运行在相同的 node 上,并且同时启动或者停止。

下图展示了一个 Pod 中运行的两个容器

  • Web Server: 网站服务器,用来向用户提供访问网页
  • FilePuller: Job 服务,定期从另一个数据源下载文件到 Pod 内的存储中

Kind Kubernetes 部署简单应用 上篇_docker_03

Pod 中多容器的常用应用例子还有

在 Pod 中运行应用容器之外,再运行一个 Filebeat 容器,用来把应用容器产生的日志输出到 ElasticSearch。

下图中,第一行backend pod 中就运行了两个容器,READY 显示2/2,代表这个pod中有两个容器在运行Kind Kubernetes 部署简单应用 上篇_docker_04

我们可以使用 kubectl describe 命令,查看 backend pod中具体有哪两个容器。

从下图可以看到,其中一个是backend应用容器,还有一个是filebeat容器

kubectl describe pods/POD_NAME -n NAMESPACE_NAME可

Kind Kubernetes 部署简单应用 上篇_docker_05

另外,在 Pod 中我们还可以设置 init 容器,init 容器在其它容器(应用容器)运行之前启动运行并终止,init 容器可以帮我们做一些初始化的工作。

2. Workloads Resources

我们通过 Workloads Resources 来管理 Pods,K8s 提供了一些定义好的 Workloads Resources 类型来满足不同的业务要求。


  • Deployment and ReplicaSet:适合管理无状态应用,Doployment 中任意 Pod 都是可随意替换的。
  • StatefulSet:有状态的应用场景,我们可以运行一个或多个相关连的 Pods 并记录其状态。
  • DaemonSet:适合为 cluster node 提供服务的 Pod,比如管理集群网络或者插件。当在 K8s 集群中新增 Node 时,由 Control Plane 根据 DaemonSet 中的定义安排在新的 Node 上运行 Pod
  • Job and CronJob:一次性任务的 Pod。

Deployment

我们后面实验用的是 Deployment,所以我们再多介绍一下 Workloads Resources 中的 Deployment。

我们在 Deployment 中描述需要的状态,然后 Deployment Controller 帮我们把 Pod 运行在指定状态。

简单点说就是,我们在 Deployment Yaml 文件中定义自己的 Pod(容器)模板、运行数量等,然后 Deployment Controller 帮我们维护这些 Pod。

这时如果 Pod 运行所在的 Node 宕机了,Deployment Controller 会自动帮我们把 Pod 运行到其它可用的 Node 上。

Deployment 通过 selector(利用 labels)来选择哪些 Pods 归其管理。

下面就是一个 Deployment Yaml 文件,后面我们会用 kubectl 命令运行这个 yaml 文件,在 kind K8s 中部署。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

说明:


  • .kind:定义这个 K8s 资源类型为 Deployment
  • .metadata.name:定义 Deployment 的名称是“nginx-deployment”
  • .spec.replicas:指定运行三个同样的 Pod
  • .spec.selector:定义哪些 Pod 由这个 Deployment 来管理,这里选择 Pod 标签(labels)中带有“app: nginx”的。
  • template:这里就是 Pod 模板

    • .metadata.labels:给 Pods 定义标签“app: nginx”
    • .template.spec:定义容器镜像的具体信息,公共仓库的 nginx 镜像,开放端口 80


以后部署任何 K8s 应用,都会以这种 yaml 文件的形式来做部署。

当然我们也可以使用 Helm 来做部署,但 Helm 最终也是生成上面这种 yaml 文件来部署应用。

现在,我们观察一下 Kind K8s 中 kube-system 空间内部署的 DeploymentKind Kubernetes 部署简单应用 上篇_docker_06

查看 Deployment 的详细信息

kubectl describe deployments/coredns -n kube-system

Kind Kubernetes 部署简单应用 上篇_服务器_07说明:这里可以看到 selector,以及 Pod 模板等信息

3. Service

在用 Deployment 管理 Pod 时,Deployment 会帮助我们重启失败的 Pod,维护 Pod 设定的状态。

但是重启后的 Pod 会分配一个新的内部 IP 和 DNS,这就会造成一些问题。

比如,一个应用运行了两组 Pod,一组做为前端,另一组做为后端。当后端的 Pod 重启并分配新 IP 之后,前端如何找到新的后端?(服务发现)

这就是 Service 提供的功能。

Service 用来把应用运行的 Pods 做为网络服务暴露出来。

可以简单理解为,Pods 没有固定 IP,但 Service 有固定 IP,把 Pods 和 Service 关联起来之后,我们就可以通过 Service 来找到需要的 Pods。

与 Deployment 一样,Service 也用 yaml 文件来定义,下面一个 Service 的例子

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

说明:


  • .kind: k8s 资源类型为 Service
  • .metadata.name: 定义 Service 的名称,与 Deployment 类似
  • .spec.selector:定义哪些 Pod 会关联到这个 Service,这里选择 Pod 标签中带有“app: nginx”的,所以上面定义的 Deployment 中的 Pods 会关联到这个 Service
  • .spec.ports:这部分定义 Service 对外暴露的端口

    • port: 这里 Service 会对外暴露 80 端口
    • targetPort: Pods 对 Service 暴露 80 端口port 和 targetPort 可以不同,也可以同时暴露多个端口


我们观察 Kind K8s 中 kube-system 空间内部署的 Service,目前这个 Service 的类型是 Clsuter IP,只能从内部访问Kind Kubernetes 部署简单应用 上篇_docker_08

4. Namespaces

K8s 支持在 cluster 中创建 virtual cluster,这些 virtual cluster 就叫做 namespaces。

当一个 K8s 集群有很多用户或项目使用时,我们可以通过创建 namespace 来区分用户和资源。

在一个 Cluster 中,一个 K8s 对象的名称是不能重复的,但在 namespace 中,只要对象名称不在当前 namespace 中重复即可。

查看 cluster 中所有 Namespaces

kubectl get namespaces

Kind Kubernetes 部署简单应用 上篇_docker_09

5. DNS

这里的 DNS 是特指 K8s 内部的 DNS。K8s 会自动为每个 Pod 和 Service 创建 DNS,我们可以通过 DNS 或者 IP 来访问 Pod 和 Service。

实战步骤

1. 重建 K8s,暴露 80、443 端口

在上一篇文章中,我们介绍过用 kind 创建 K8s 时,是相当于在本地运行了一个容器,而K8s Cluster 就运行在这个容器中。

所以,如果我们想从外部访问 kind K8s 的话,就需要把这个容器的端口(K8s 的端口)暴露出来。

目前可以看到只有 38325 这个端口可以从外部访问Kind Kubernetes 部署简单应用 上篇_服务器_10

为了下一步测试,我们重新创建一个新的 K8s cluster 并且把 80 和 443 端口暴露出来。

运行下列命令删除现在的 k8s cluster

kind delete cluster --name tsk8s

Kind Kubernetes 部署简单应用 上篇_服务器_11

运行下列命令创建新的 k8s cluster

cat <<EOF | kind create cluster --name tsk8s --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
  - containerPort: 30000
    hostPort: 30000
    protocol: TCP
EOF

说明:


  • extraPortMappings:把 K8s 容器(相当于 K8s 所在的服务器)端口暴露出来,这里暴露了 80、443、30000
  • node-labels:只允许 Ingress controller 运行在有"ingress-ready=true"标签的 node 上

运行结果Kind Kubernetes 部署简单应用 上篇_docker_12

这时可以看到 80、443、30000 端口已经暴露出来了Kind Kubernetes 部署简单应用 上篇_nginx_13

注意:如果是在公司代理环境下,我们要在 K8s 容器中设置代理,才可以正常进行下面的测试,设置代理请参考上一篇文章《代理环境下在 WSL2 中用 Kind 创建 Kubernetes 集群》”

2. 部署 Deployment、Service

部署 Deployment

新建文件 my-dep.yaml,添加以下内容

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-dep
spec:
  replicas: 1  # number of replicas of frontEnd application
  selector:
    matchLabels:
      app: httpd-app
  template:
    metadata:
      labels: # Must match 'Service' and 'Deployment' labels
        app: httpd-app
    spec:
      containers:
      - name: httpd
        image: httpd # docker image of frontend application
        ports:
        - containerPort: 80

说明:


  • Deployment 的名称为“httpd-dep”
  • 管理的 Pods 需要带有“app: httpd-app”标签
  • Pod 模板中指定运行的镜像为 Docker 公共仓库中的 httpd

运行以下命令创建 Deployment

kubectl apply -f my-dep.yaml

创建 Deployment 结果Kind Kubernetes 部署简单应用 上篇_nginx_14

查看新建的 Pod,-o wide 可以看到更多的信息

kubectl get pods -o wide

Kind Kubernetes 部署简单应用 上篇_服务器_15说明:可以看到 Pod 被分配了一个 K8s 内部 IP,这个 IP 不能从 K8s 外部访问到,而且这个 IP 在 Pod 重建后会变化

部署 Service

新建文件 my-svc.yaml,添加以下内容

kind: Service
apiVersion: v1
metadata:
  name: httpd-svc
spec:
  selector:
      app: httpd-app
  ports:
  - port: 80

运行以下命令创建 Service

kubectl apply -f my-svc.yaml

创建 Service 结果Kind Kubernetes 部署简单应用 上篇_服务器_16

查看 Service 信息

kubectl get svc/httpd-svc

Kind Kubernetes 部署简单应用 上篇_docker_17说明:Service 的 IP 是不会变化的(除非重建 Service)

查看 Service 详细信息

kubectl describe svc/httpd-svc

Kind Kubernetes 部署简单应用 上篇_服务器_18说明:

可以看到这里 Service 关联的 Endpoint 的 IP 和端口就是上面 Pod 的 IP 和端口。每次 Pod 重建后这里的 Endpoint 就会刷新为新的 IP。

目前这个 Service 只有内部 IP 和端口,所以这个 Service 还只能用在 K8s 内部暴露 Pods。

下面我们修改 Service 配置,使用 K8s 外部也可以访问到这个 Service

更改 Serivce(nodePort)

修改 my-svc.yaml,添加以下内容

kind: Service
apiVersion: v1
metadata:
  name: httpd-svc
spec:
  selector:
      app: httpd-app
  type: NodePort #1
  ports:
  - port: 80
    nodePort: 30000 #2

说明:


  • #1 Service type 默认为 ClusterIP,即只有内部 IP。改为 NodePort 后,Service 会把 K8s 内部的端口映射到集群所在的服务器上
  • #2 指定 Service 映射到服务器上的端口为 30000

再次运行 kubectl apply 命令

运行结果Kind Kubernetes 部署简单应用 上篇_服务器_19

再次查看 Service 信息,可以看到端口中多了一个 30000Kind Kubernetes 部署简单应用 上篇_服务器_20

30000 这个端口被映射到了 K8s 集群所在的服务器上(即 K8s 运行的容器),而我们在创建 kind K8s 时把容器的 30000 端口又映射到本地,所以现在我们可以在本地用浏览器访问 30000 端口。

在本地可以访问到 30000 端口上的 httpd 应用Kind Kubernetes 部署简单应用 上篇_服务器_21

引申


除了用 NodePort 暴露服务,我们还可以用 ingress/ingress controller 实现相同的功能。我们将在下一篇文章中测试 ingress/ingress controller

后记


K8s 的内容多且杂,估计因为 K8s 是一个开源框架而非产品,所以官网上基本没有实操的例子,都是大段的概念介绍。

当年开始学习 K8s 时,就是感觉无从下手。后来搭建起来 K8s 环境后,边学边试才好了很多。

这篇及下一篇文章挑了一些最常用的 K8s 对象来讲解,然后加上实际操作,应该能让 K8s 初学者找到点门道儿。


喜欢请点赞,禁止转载,转发请标明出处

关注 B 站 UP 主“我是手拉面” 观看更多视频

微信公众号“全是 AWS 干货”



Kind Kubernetes 部署简单应用 上篇_nginx_22