kubernetes应用部署的简单示例

部署应用

文档:https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/
Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。

一、最简部署

命令运行
#运行一个镜像版本为6.2.5 的redis程序
kubectl run redis --image=redis:6.2.5

[root@k8s1 redis]# kubectl get pods -o wide 
NAME                         READY   STATUS    RESTARTS   AGE     IP            NODE   NOMINATED NODE   READINESS GATES
redis                        1/1     Running   0          7m27s   10.244.0.15   k8s2   >           >
YAML文件
[root@k8s1 redis]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  # 定义容器,可以多个
  containers:
    - name: redis # 容器名字
      image: redis:6.2.5 # 镜像

#启动pod
[root@k8s1 redis]# kubectl apply -f pod.yaml

#关闭pod
[root@k8s1 redis]# kubectl delete -f pod.yaml

二、部署到指定Node

若存在多Pod的情况下,某些应用可能需要在指定节点上运行。当然目前数据还未持久化,后面再说。

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  nodeName: node01 # 调度 Pod 到特定的节点
  containers:
    - name: redis # 容器名字
      image: redis:6.2.5 # 镜像

三、Service

Service是高于Pod的抽象,特性如下:

  • Service 通过 label 关联对应的 Pod
  • Servcie 生命周期不跟 Pod 绑定,不会因为 Pod 重创改变 IP
  • 提供了负载均衡功能,自动转发流量到不同 Pod
  • 可对集群外部提供访问端口
  • 集群内部可通过服务名字访问

外部访问需要先经过Service层,再由Service转到不同的Pod。
kubernetes应用部署的简单示例_第1张图片
PodService可写在同一yaml,只需要使用—分割。

apiVersion: v1
kind: Pod
metadata:
  name: redis
  labels:
    app: redis
spec:
  containers:
    - name: redis # 容器名字
      image: redis:6.2.5 # 镜像
      ports:
        - containerPort: 6379

---
apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  selector:
    app: redis
  type: ClusterIP
  ports:
    - port: 6379
      targetPort: 6379

启动一个Pod与一个Service

[root@k8s1 redis]# kubectl apply -f redis.yaml 
pod/redis created
service/my-service created
[root@k8s1 redis]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
my-service   ClusterIP   10.103.246.122   >        6379/TCP       54s

四、工作负载分类

yaml文件中的kind类别为工作负载分类

  • Deployment
    适合无状态应用,所有pod等价,可替代
  • StatefulSet
    有状态的应用,适合数据库这种类型。
  • DaemonSet
    在每个节点上跑一个 Pod,可以用来做节点监控、节点日志收集等
  • Job & CronJob
    Job 用来表达的是一次性的任务,而 CronJob 会根据其时间规划反复运行。

Deployment

文档:https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/deployment/

一个 Deployment 为 **Pod **和 **ReplicaSet **提供声明式的更新能力
**ReplicaSet :**负责在后台创建 Pod。 检查 ReplicaSet 的上线状态,查看其是否成功。

下面是一个 Deployment 示例。其中创建了一个 ReplicaSet,负责启动两个 nginx Pod

apiVersion: apps/v1
kind: Deployment
metadata:
  # 部署名字
  name: redis-test
spec:
  #声明启动副本数,意味着启动了两个副本
  replicas: 2
  # 用来查找关联的 Pod,所有标签都匹配才行
  selector:
    matchLabels:
      app: redis-test
  # 定义 Pod 相关数据
  template:
    metadata:
      labels:
        app: redis-test
    spec:
      containers:
      - name: redis-test # 容器名字
        image: redis:6.2.5 # 镜像

启动成功

image.png
Deployment启动Pod后,将会一直保持两个redis副本,除非更改副本数。

#查看deployments
[root@k8s1 redis]# kubectl get deployments
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
redis-test   2/2     2            2           4m37s

StatefulSet

StatefulSet 适用情况:

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和扩缩。
  • 有序的、自动的滚动更新。
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis
  replicas: 4
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:6.2.5
          # IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
          imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  selector:
    app: redis
  type: ClusterIP
  ports:
    - port: 6379
      targetPort: 6379

运行中PodName出现明显变化。

[root@k8s1 redis]# kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP            NODE   NOMINATED NODE   READINESS GATES
redis-0                      1/1     Running   0          92s     10.244.0.24   k8s3   >           >
redis-1                      1/1     Running   0          92s     10.244.0.22   k8s2   >           >
redis-2                      1/1     Running   0          91s     10.244.0.23   k8s2   >           >
redis-3                      1/1     Running   0          90s     10.244.0.25   k8s3   >           >

要连接指定 Pod,可以这样pod-name.service-name。

数据持久化

以上运行的Pod只要重启数据就会丢失,需要手动将数据挂载到本地磁盘上,但是这需要限定 pod 在这个节点上运行,否则就会挂载失败。

一、抽象


层级关系如图所示,SC为实际磁盘存储区,PVSC进行划分,PVCPV的空间进行申请,最后交由Pod使用。

Storage Class (SC)

将存储卷划分为不同的种类,例如:SSD,普通磁盘,本地磁盘,按需使用。文档

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4

Persistent Volume (PV)

描述卷的具体信息,例如磁盘大小,访问模式,可以由SC动态创建。文档

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mongodata
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem  # Filesystem(文件系统) Block(块)
  accessModes:
    - ReadWriteOnce       # 卷可以被一个节点以读写方式挂载
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /root/data
  nodeAffinity:
    required:
      # 通过 hostname 限定在某个节点创建存储卷
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - k8s2

Persistent Volume Claim (PVC)

对存储需求的一个申明,可以理解为一个申请单,系统根据这个申请单去找一个合适的 PV
PVC与PV必是一对一互相绑定的关系。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redisdata
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "local-storage"
  resources:
    requests:
      storage: 2Gi

二、使用示例

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis
  replicas: 4
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:6.2.5
          # IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - mountPath: /data
              name: redis-data
      volumes:
        - name: redis-data
          persistentVolumeClaim:
             claimName: redisdata
---
apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  selector:
    app: redis
  type: ClusterIP
  ports:
    - port: 6379
      targetPort: 6379

因为SC指定存储在节点k8s2上,所以所有的redis也都运行在k8s2上。

[root@k8s1 redis]# kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP            NODE   NOMINATED NODE   READINESS GATES
redis-0                      1/1     Running   0          11s     10.244.0.24   k8s2   >           >
redis-1                      1/1     Running   0          10s     10.244.0.25   k8s2   >           >
redis-2                      1/1     Running   0          9s      10.244.0.26   k8s2   >           >
redis-3                      1/1     Running   0          8s      10.244.0.27   k8s2   >           >

NFS

为了不受集群影响,数据可以挂载到NFS上实现任意Pod都可访问存储。

NFS(Network File System)是一种用于在网络上共享文件系统的协议。它允许多台计算机通过网络访问共享存储资源,就像访问本地文件系统一样。
NFS通过客户端-服务器模型工作,其中服务器上的文件系统称为NFS服务器,客户端计算机通过网络连接到该服务器以访问共享的文件和目录。NFS提供了对文件的透明访问,使得客户端可以像访问本地文件一样读取、写入和执行远程文件系统上的文件。

一、搭建nfs

1.安装nfs

#安装nfs
[root@k8s1 /]# yum -y install nfs-utils

2.Master节点暴露目录

# 创建文件夹
[root@k8s1 /]# mkdir -p /nfs/data/

#修改权限
[root@k8s1 /]# chmod -R 777 /nfs/data

# 暴露文件夹
#编辑export文件,这个文件就是nfs默认的配置文件,要共享多个文件就都在这里注册一下,并创建相应目录并给与权限,也可以将*改为指定的IP地址,多个ip就空格隔开
[root@k8s1 /]# echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports

#配置生效
[root@k8s1 /]# exportfs -r
#查看生效
[root@k8s1 /]# exportfs
/nfs/data       <world>

/etc/exports文件配置说明:

<输出目录> [客户端1 选项(访问权限,用户映射,其他)] [客户端2 选项(访问权限,用户映射,其他)]
a. 输出目录:
输出目录是指NFS系统中需要共享给客户机使用的目录;
b. 客户端:
客户端是指网络中可以访问这个NFS输出目录的计算机
客户端常用的指定方式
指定ip地址的主机:192.168.0.200
指定子网中的所有主机:192.168.0.0/24 192.168.0.0/255.255.255.0
指定域名的主机:david.bsmart.cn
指定域中的所有主机:.bsmart.cn
所有主机:

c. 选项:
选项用来设置输出目录的访问权限、用户映射等。NFS主要有3类选项:
访问权限
设置输出目录只读:ro
设置输出目录读写:rw
用户映射
all_squash:将远程访问的所有普通用户及所属组都映射为匿名用户或用户组(nfsnobody);
no_all_squash:与all_squash取反(默认设置);
root_squash:将root用户及所属组都映射为匿名用户或用户组(默认设置);
no_root_squash:与rootsquash取反;
anonuid=xxx:将远程访问的所有用户都映射为匿名用户,并指定该用户为本地用户(UID=xxx);
anongid=xxx:将远程访问的所有用户组都映射为匿名用户组账户,并指定该匿名用户组账户为本地用户组账户(GID=xxx);
其它选项
secure:限制客户端只能从小于1024的tcp/ip端口连接nfs服务器(默认设置);
insecure:允许客户端从大于1024的tcp/ip端口连接服务器;
sync:将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性;
async:将数据先保存在内存缓冲区中,必要时才写入磁盘;
wdelay:检查是否有相关的写操作,如果有则将这些写操作一起执行,这样可以提高效率(默认设置);
no_wdelay:若有写操作则立即执行,应与sync配合使用;
subtree:若输出目录是一个子目录,则nfs服务器将检查其父目录的权限(默认设置);
no_subtree:即使输出目录是一个子目录,nfs服务器也不检查其父目录的权限,这样可以提高效率;

二、启动NFS

# 启动rpcbind服务
[root@k8s1 /]# systemctl enable rpcbind --now

# 启动nfs服务器
[root@k8s1 /]# systemctl enable nfs-server --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
#查看 RPC 服务的注册状况
[root@k8s1 /]# rpcinfo -p localhost
   program vers proto   port  service
    100000    4   tcp    111  portmapper
    100000    3   tcp    111  portmapper
    100000    2   tcp    111  portmapper
    100000    4   udp    111  portmapper

#showmount测试
[root@k8s1 /]# showmount -e 192.168.2.61
 Export list for 192.168.2.61:#挂载对应的nfs目录到本地, 挂载前要检查此目录是否存在

子节点配置

[root@k8s2 /]# yum -y install nfs-utils

[root@k8s2 /]# mkdir /nfs/data

#挂载对应的nfs目录到本地, 挂载前要检查此目录是否存在
#为了提高NFS的稳定性,使用TCP协议挂载,NFS默认用UDP协议
[root@k8s1 /]# mount -t nfs 192.168.2.61:/nfs/data /nfs/data -o proto=tcp -o nolock

#取消挂载(对应的本地路径)
[root@k8s1 /]# umount /nfs/data

#设置开机自动挂载
[root@k8s-node1 ~]# vi /etc/fstab
#
# /etc/fstab
# Created by anaconda on Tue Nov 23 09:19:43 2021
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=8af70500-d87e-4b91-b88e-dff450511d3f /                       ext4    defaults        1 1
192.168.2.61:/nfs/data                   /nfs/data               nfs     defaults        0 0

三、测试之配置直连

创建pod

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nfs-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nfs-nginx
    spec:
      containers:
      - name: nginx-web
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html
          mountPath: "/usr/share/nginx/html"
        - name: vhost
          mountPath: "/etc/nginx/conf.d"
      volumes:
      - name: html
        nfs:
          server: 192.168.2.61
          path: "/nfs/data/nginx/html"
      - name: vhost
        nfs:
          server: 192.168.2.61
          path: "/nfs/data/nginx/conf.d"
---
#service
apiVersion: v1
kind: Service
metadata:
  name: nfs-nginx
  namespace: default
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 31681
  selector:
    app: nfs-nginx

nginx.conf单文件挂载

#创建configmap from-file=/nfs/data/nginx/nginx.conf为真实可用的nginx.conf文件
[root@k8s1 nginx]# kubectl create configmap nginx-config -- from-file=/nfs/data/nginx/nginx.conf

#导出文件
[root@k8s1 nginx]# kubectl get cm nginx-config -o yaml >> configmap.yaml

#編輯pod.yaml
[root@k8s1 nginx]# vi pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nfs-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nfs-nginx
    spec:
      containers:
      - name: nginx-web
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html
          mountPath: "/usr/share/nginx/html"
        - name: vhost
          mountPath: "/etc/nginx/conf.d"
        - name: nginx-config
          mountPath: "/etc/nginx/conf.d"
					subPath: nginx.conf
      volumes:
      - name: html
        nfs:
          server: 192.168.2.61
          path: "/nfs/data/nginx/html"
      - name: vhost
        nfs:
          server: 192.168.2.61
          path: "/nfs/data/nginx/conf.d"
			- name: nginx-config
          configMap:
            name: nginx-config
            items:
            - key: nginx.conf
              path: nginx.conf

测试功能是否正常(同四)

master节点创建1.html文件
kubernetes应用部署的简单示例_第2张图片
自动同步到node节点
kubernetes应用部署的简单示例_第3张图片
kubernetes应用部署的简单示例_第4张图片
进入容器

[root@k8s1 ~]# kubectl exec -it nfs-nginx-6cdf65c4d4-dfm87 -- /bin/bash
root@nfs-nginx-6cdf65c4d4-dfm87:/# cd /usr/share/nginx/html/
root@nfs-nginx-6cdf65c4d4-dfm87:/usr/share/nginx/html# ls    
1.html
root@nfs-nginx-6cdf65c4d4-dfm87:/usr/share/nginx/html# cat < 2.html
<html>
<body>Test02</body>
</html>
EOF

kubernetes应用部署的简单示例_第5张图片
重启此service

[root@k8s1 nfs]# kubectl delete -f nginx.yaml 
deployment.apps "nfs-nginx" deleted
service "nfs-nginx" deleted
[root@k8s1 nfs]# kubectl apply -f nginx.yaml 
deployment.apps/nfs-nginx created
service/nfs-nginx created
#重启后访问依旧没问题,证明文件已本地持久化
[root@k8s1 nfs]# curl -X GET http://k8s2:31681/2.html
<html>
<body>Test02</body>
</html>

四、测试之PV、PVC指定创建

创建pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
  namespace: default
  labels:
    pv: nfs-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  # storageClassName: nfs 可以不指定,但要指定前必须先创建,否则pvc会报错
  nfs:  
    server: 192.168.2.66
    path: "/nfs/data"   #NFS目录,需要该目录在NFS上存在

[root@k8s1 /]# kubectl apply -f pv.yaml

创建pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi  #容量
  selector:
    matchLabels:
      pv: nfs-pv   #关联pv 的label,key/value要一致,可以不指定,pvc会自动找到匹配的pv,否则会一直处于pending状态直到有合适的pv

[root@k8s1 /]# kubectl apply -f pvc.yaml

创建pod

[root@k8s1 /]# vim nginx.yaml 
#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nfs-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nfs-nginx
    spec:
      containers:
      - name: nginx-web
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: html
      volumes:
      - name: html
        persistentVolumeClaim:
          claimName: nfs-pvc
---
#service
apiVersion: v1
kind: Service
metadata:
  name: nfs-nginx
  namespace: default
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 31681
  selector:
    app: nfs-nginx
[root@k8s1 nfs]# kubectl apply -f nginx.yaml
[root@k8s1 data]# kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP           NODE   NOMINATED NODE   READINESS GATES
nfs-nginx-6cdf65c4d4-xpnmm   1/1     Running   0          16h   10.244.0.2   k8s2   <none>           <none>
nfs-nginx-6cdf65c4d4-zhv5l   1/1     Running   0          16h   10.244.0.2   k8s3   <none>           <none>

pod可能存在的问题

[root@k8s1 ~]# kubectl describe pod nfs-nginx-6cdf65c4d4-xpnmm
............省略
Events:
  Type     Reason                  Age                     From     Message
  ----     ------                  ----                    ----     -------
  Warning  FailedCreatePodSandBox  5m21s (x4432 over 16h)  kubelet  (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "83a7c29e045657bcfa992c7d102c6a81a3a44dfa1a0d4a8b9dbd271623c93150": plugin type="flannel" failed (add): loadFlannelSubnetEnv failed: open /run/flannel/subnet.env: no such file or directory

#出现此问题,创建对应的文件并且分发到所有节点即可
[root@k8s1 ~]# vi /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

命名空间

如果一个集群中部署了多个应用,所有应用都在一起,就不太好管理,也可以导致名字冲突等。
我们可以使用 namespace 把应用划分到不同的命名空间,跟代码里的 namespace 是一个概念,只是为了划分空间。

# 创建命名空间
kubectl create namespace testapp

# 部署应用到指定的命名空间
kubectl apply -f app.yml --namespace testapp

# 查询
kubectl get pod --namespace kube-system

可以用 kubens 快速切换 namespace

# 切换命名空间
kubens kube-system

# 回到上个命名空间
kubens -

# 切换集群
kubectx minikube

# 位于名字空间中的资源
kubectl api-resources --namespaced=true

# 不在名字空间中的资源
kubectl api-resources --namespaced=false

你可能感兴趣的:(kubernetes,docker,java)