kind之Kubernetes部署简单应用(端口暴露法)

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)。

2. Pod

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

比如,kube-system namespace内运行的pods

 

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

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

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

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

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

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

下图中,第一行backend pod 中就运行了两个容器,READY 显示2/2,代表这个pod中有两个容器在运行

 

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

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

kubectl describe pods/POD_NAME -n NAMESPACE_NAME

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

3. 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。

4. 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 空间内部署的Deployment

  

查看Deployment的详细信息

kubectl describe deployments/coredns -n kube-system

 

  

说明:这里可以看到selector,以及Pod模板等信息

5. 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,只能从内部访问

  kubectl get svc -n kube-system

 

 

6. Namespaces

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

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

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

查看cluster中所有Namespaces

kubectl get namespaces

  

7. DNS

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

 

实战1

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

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

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

目前可以看到只有38325这个端口可以从外部访问

 

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

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

kind delete cluster --name tsk8s

  

运行下列命令创建新的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上

运行结果

  

这时可以看到80、443、30000端口已经暴露出来了

  

 注意:如果是在公司代理环境下,我们要在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结果

 

 

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

kubectl get pods -o wide

 

 

 说明:可以看到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结果

 

  查看Service信息

kubectl get svc/httpd-svc

 

 

说明:Service的IP是不会变化的(除非重建Service)

查看Service详细信息

kubectl describe svc/httpd-svc

 

  

 说明:

可以看到这里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 命令

运行结果

 

 再次查看Service信息,可以看到端口中多了一个30000

 

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

在本地可以访问到30000端口上的httpd应用

 

Window下cmd访问:

curl 192.168.192.140:30000

 

 

 

实战2-创建Mysql数据库服务

先删除前面的服务,因为需要使用30000端口。


kubectl delete -f my-dep.yaml
kubectl delete -f myl-svc.yaml

 

 

 

创建mysql服务步骤

  • 1、创建一个新的namespace
  • 2、在该namespace下创建一个deployment
  • 3、deployment自动部署好replicaSet和pod
  • 4、创建对应服务
  • 5、验证是否成功

下面是具体的操作说明

1、创建一个新的namespace

#创建namespace ,命令行直接创建

$ kubectl create namespace test

 

  

2、在该namespace下创建一个deployment(env中设置了mysql的root用户的密码为mysql)

(1)编写deployment的对应yaml文件: mysql-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deployment
  namespace: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.6
        imagePullPolicy: IfNotPresent
        args:
          - "--ignore-db-dir=lost+found"
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "1234"

  

注意:密码的地方一定要加引号。

2)创建deployment

kubectl create -f mysql-deployment.yaml --record

  

3、deployment自动部署好replicaSet和pod

执行一下命令可以查看test namespace 下自动部署好replicaSet和pod

kubectl get rs -n test

 kubectl get pod -n test

 

4、创建对应服务(注意定义type=NodePort,并对应的nodeport端口号,以便集群外访问该服务)

(1)创建对应的service的yaml文件:mysql-svc.yaml 

 

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
  namespace: test
  labels:
    name: mysql-svc
spec:
  selector:
    app: mysql
  type: NodePort
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
    name: http
    nodePort: 30000
  selector:
    app: mysql

 

(2)创建对应的service

kubectl create -f mysql-svc.yaml --record

  

5、验证是否成功

在远程客户端上下载mysql客户端Navicat,进行验证

其中

主机:service对应的pod所在的node的ip

端口:上面service中的nodeport端口号30000

密码:deployment文件env中设置的root用户的密码)

 

引申


除了用NodePort暴露服务,还可以用ingress/ingress controller实现相同的功能。

后记


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

鸣谢:

https://zhuanlan.zhihu.com/p/431311301 

https://blog.csdn.net/runner668/article/details/100112969

 

posted @ 2022-07-07 22:13  春光牛牛  阅读(1927)  评论(0编辑  收藏  举报