kubernetes K8S 挂载分布式存储 ceph

目录

一、Ceph简介

二、Ceph核心组件介绍

三、安装Ceph集群

1初始化实验环境

1.1、配置静态IP:

1.2、配置主机名:

1.3、配置hosts文件:

1.4、配置互信

1.5、关闭防火墙

1.6、关闭selinux

1.7、配置Ceph安装源

1.8、配置时间同步

1.9、安装基础软件包

2、安装ceph集群

2.1 安装ceph-deploy

2.2 创建monitor节点

2.3 安装ceph-monitor

2.4 部署osd服务

2.5 创建ceph文件系统

2.6 测试k8s挂载ceph rbd

2.7 基于ceph rbd生成pv

2.8 基于storageclass动态生成pv

2.9 k8s挂载cephfs


文档中的YAML文件配置直接复制粘贴可能存在格式错误,故实验中所需要的YAML文件以及本地包均打包至网盘

链接:https://pan.baidu.com/s/1oKhAqeXvk8JYhtMNzUYciA 
提取码:cpsg 
 

一、Ceph简介

官方:

Ceph.io — Home

https://docs.ceph.com/en/latest/start/intro/

ceph是一种开源的分布式的存储系统

包含以下几种存储类型:

块存储(rbd),对象存储(RADOS Fateway),文件系统(cephfs)

1.块存储(rbd):

块是一个字节序列(例如,512字节的数据块)。 基于块的存储接口是使用旋转介质(如硬盘,CD,软盘甚至传统的9轨磁带)存储数据的最常用方法;Ceph块设备是精简配置,可调整大小并存储在Ceph集群中多个OSD条带化的数据。 Ceph块设备利用RADOS功能,如快照,复制和一致性。 Ceph的RADOS块设备(RBD)使用内核模块或librbd库与OSD进行交互;Ceph的块设备为内核模块或QVM等KVM以及依赖libvirt和QEMU与Ceph块设备集成的OpenStack和CloudStack等基于云的计算系统提供高性能和无限可扩展性。 可以使用同一个集群同时运行Ceph RADOS Gateway,CephFS文件系统和Ceph块设备。

linux系统中,ls /dev/下有很多块设备文件,这些文件就是我们添加硬盘时识别出来的;

rbd就是由Ceph集群提供出来的块设备。可以这样理解,sda是通过数据线连接到了真实的硬盘,而rbd是通过网络连接到了Ceph集群中的一块存储区域,往rbd设备文件写入数据,最终会被存储到Ceph集群的这块区域中;

总结:块设备可理解成一块硬盘,用户可以直接使用不含文件系统的块设备,也可以将其格式化成特定的文件系统,由文件系统来组织管理存储空间,从而为用户提供丰富而友好的数据操作支持。

2.文件系统cephfs

Ceph文件系统(CephFS)是一个符合POSIX标准的文件系统,它使用Ceph存储集群来存储其数据。 Ceph文件系统使用与Ceph块设备相同的Ceph存储集群系统。

用户可以在块设备上创建xfs文件系统,也可以创建ext4等其他文件系统,Ceph集群实现了自己的文件系统来组织管理集群的存储空间,用户可以直接将Ceph集群的文件系统挂载到用户机上使用,Ceph有了块设备接口,在块设备上完全可以构建一个文件系统,那么Ceph为什么还需要文件系统接口呢?

主要是因为应用场景的不同,Ceph的块设备具有优异的读写性能,但不能多处挂载同时读写,目前主要用在OpenStack上作为虚拟磁盘,而Ceph的文件系统接口读写性能较块设备接口差,但具有优异的共享性。

3.对象存储

Ceph对象存储使用Ceph对象网关守护进程(radosgw),它是一个用于与Ceph存储集群交互的HTTP服务器。由于它提供与OpenStack Swift和Amazon S3兼容的接口,因此Ceph对象网关具有自己的用户管理。 Ceph对象网关可以将数据存储在用于存储来自Ceph文件系统客户端或Ceph块设备客户端的数据的相同Ceph存储集群中

使用方式就是通过http协议上传下载删除对象(文件即对象)。

老问题来了,有了块设备接口存储和文件系统接口存储,为什么还整个对象存储呢?

Ceph的块设备存储具有优异的存储性能但不具有共享性,而Ceph的文件系统具有共享性然而性能较块设备存储差,为什么不权衡一下存储性能和共享性,整个具有共享性而存储性能好于文件系统存储的存储呢,对象存储就这样出现了。

分布式存储的优点:

高可靠:既满足存储读取不丢失,还要保证数据长期存储。 在保证部分硬件损坏后依然可以保证数据安全

高性能:读写速度快

可扩展:分布式存储的优势就是“分布式”,所谓的“分布式”就是能够将多个物理节点整合在一起形成共享的存储池,节点可以线性扩充,这样可以源源不断的通过扩充节点提升性能和扩大容量,这是传统存储阵列无法做到的

二、Ceph核心组件介绍

在ceph集群中,不管你是想要提供对象存储,块设备存储,还是文件系统存储,所有Ceph存储集群部署都是从设置每个Ceph节点,网络和Ceph存储开始 的。 Ceph存储集群至少需要一个Ceph Monitor,Ceph Manager和Ceph OSD(对象存储守护进程)。 运行Ceph Filesystem客户端时也需要Ceph元数据服务器。

Monitors:Ceph监视器(ceph-mon)维护集群状态的映射,包括监视器映射,管理器映射,OSD映射和CRUSH映射。这些映射是Ceph守护进程相互协调所需的关键集群状态。监视器还负责管理守护进程和客户端之间的身份验证。冗余和高可用性通常至少需要三个监视器。

Managers:Ceph Manager守护程序(ceph-mgr)负责跟踪运行时指标和Ceph集群的当前状态,包括存储利用率,当前性能指标和系统负载。 Ceph Manager守护进程还托管基于python的模块来管理和公开Ceph集群信息,包括基于Web的Ceph Dashboard和REST API。高可用性通常至少需要两名Managers。

Ceph OSD:Ceph OSD(对象存储守护进程,ceph-osd)存储数据,处理数据复制,恢复,重新平衡,并通过检查其他Ceph OSD守护进程来获取心跳,为Ceph监视器和管理器提供一些监视信息。冗余和高可用性通常至少需要3个Ceph OSD。

MDS:Ceph元数据服务器(MDS,ceph-mds)代表Ceph文件系统存储元数据(即,Ceph块设备和Ceph对象存储不使用MDS)。 Ceph元数据服务器允许POSIX文件系统用户执行基本命令(如ls,find等),而不会给Ceph存储集群带来巨大负担。

三、安装Ceph集群

1初始化实验环境

机器配置:

Centos7.6

网络模式:NAT

准备三台机器,每台机器需要三个硬盘,配置4GiB/4vCPU/60G

master1-admin是管理节点 :192.168.40.200

node1-monitor是监控节点:192.168.40.201

node2-osd是对象存储节点:192.168.40.202

1.1、配置静态IP:

把虚拟机或者物理机配置成静态ip地址,这样机器重新启动后ip地址也不会发生改变。以master1-admin主机为例,修改静态IP:

修改/etc/sysconfig/network-scripts/ifcfg-ens33文件,变成如下:

TYPE=Ethernet

PROXY_METHOD=none

BROWSER_ONLY=no

BOOTPROTO=static

IPADDR=192.168.40.200

NETMASK=255.255.255.0

GATEWAY=192.168.40.2

DNS1=192.168.40.2

DEFROUTE=yes

IPV4_FAILURE_FATAL=no

IPV6INIT=yes

IPV6_AUTOCONF=yes

IPV6_DEFROUTE=yes

IPV6_FAILURE_FATAL=no

IPV6_ADDR_GEN_MODE=stable-privacy

NAME=ens33

DEVICE=ens33

ONBOOT=yes

#修改配置文件之后需要重启网络服务才能使配置生效,重启网络服务命令如下:

service network restart

注:/etc/sysconfig/network-scripts/ifcfg-ens33文件里的配置说明:

NAME=ens33    #网卡名字,跟DEVICE名字保持一致即可

DEVICE=ens33   #网卡设备名,大家ip addr可看到自己的这个网卡设备名,每个人的机器可能这个名字不一样,需要写自己的

BOOTPROTO=static   #static表示静态ip地址

ONBOOT=yes    #开机自启动网络,必须是yes

IPADDR=192.168.40.200   #ip地址,需要跟自己电脑所在网段一致

NETMASK=255.255.255.0  #子网掩码,需要跟自己电脑所在网段一致

GATEWAY=192.168.40.2   #网关,在自己电脑打开cmd,输入ipconfig /all可看到

DNS1=192.168.40.2     #DNS,在自己电脑打开cmd,输入ipconfig /all可看到 

    

1.2、配置主机名:

在192.168.40.200上执行如下:

hostnamectl set-hostname master1-admin && bash

在192.168.40.201上执行如下:

hostnamectl set-hostname node1-monitor && bash

在192.168.40.202上执行如下:

hostnamectl set-hostname node2-osd && bash

1.3、配置hosts文件:

修改master1-admin、node1-monitor、node2-osd机器的/etc/hosts文件,增加如下三行:

192.168.40.200   master1-admin  

192.168.40.201   node1-monitor  

192.168.40.202   node2-osd  

1.4、配置互信

生成ssh 密钥对

[root@master1-admin ~]# ssh-keygen -t rsa  #一路回车,不输入密码

把本地的ssh公钥文件安装到远程主机对应的账户

[root@master1-admin ~]# ssh-copy-id node1-monitor

[root@master1-admin ~]# ssh-copy-id node2-osd

[root@master1-admin ~]# ssh-copy-id master1-admin

[root@node1-monitor ~]# ssh-keygen  #一路回车,不输入密码

把本地的ssh公钥文件安装到远程主机对应的账户

[root@node1-monitor ~]# ssh-copy-id  master1-admin

[root@node1-monitor ~]# ssh-copy-id  node1-monitor

[root@node1-monitor ~]# ssh-copy-id node2-osd

[root@node2-osd ~]# ssh-keygen  #一路回车,不输入密码

把本地的ssh公钥文件安装到远程主机对应的账户

[root@node2-osd ~]# ssh-copy-id master1-admin

[root@node2-osd ~]# ssh-copy-id node1-monitor

[root@node2-osd ~]# ssh-copy-id node2-osd

1.5、关闭防火墙

[root@master1-admin ~]# systemctl stop firewalld ; systemctl disable firewalld

[root@node1-monitor ~]# systemctl stop firewalld ; systemctl disable firewalld

[root@node2-osd ~]# systemctl stop firewalld ; systemctl disable firewalld

1.6、关闭selinux

所有ceph节点的selinux都关闭

#临时关闭

setenforce 0

#永久关闭

sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config

#注意:修改selinux配置文件之后,重启机器,selinux才能永久生效

1.7、配置Ceph安装源

#配置阿里云的repo源,master1-admin、node1-monitor、node2-osd上操作:

#配置ceph的repo源,master1-admin、node1-monitor、node2-osd上操作:

yum install -y yum-utils && sudo yum-config-manager --add-repo https://dl.fedoraproject.org/pub/epel/7/x86_64/ && sudo yum install --nogpgcheck -y epel-release && sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 && sudo rm /etc/yum.repos.d/dl.fedoraproject.org*

#把ceph.repo上传到master1-admin、node1-monitor、node2-osd上

cat  /etc/yum.repos.d/ceph.repo

[Ceph]

name=Ceph packages for $basearch

baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/x86_64/

enabled=1

gpgcheck=0

type=rpm-md

gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc

priority=1

[Ceph-noarch]

name=Ceph noarch packages

baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/noarch/

enabled=1

gpgcheck=0

type=rpm-md

gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc

priority=1

[ceph-source]

name=Ceph source packages

baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/SRPMS/

enabled=1

gpgcheck=0

type=rpm-md

gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc

priority=1

1.8、配置时间同步

配置机器时间跟网络时间同步,在ceph的每台机器上操作

service ntpd stop

ntpdate cn.pool.ntp.org

crontab -e

* */1 * * * /usr/sbin/ntpdate   cn.pool.ntp.org

service crond restart

1.9、安装基础软件包

#安装基础软件包,在ceph的每台机器上操作

yum install -y yum-utils device-mapper-persistent-data lvm2 wget net-tools nfs-utils lrzsz gcc gcc-c++ make cmake libxml2-devel openssl-devel curl curl-devel unzip sudo ntp libaio-devel wget vim ncurses-devel autoconf automake zlib-devel  python-devel epel-release openssh-server socat  ipvsadm conntrack ntpdate telnet deltarpm

2、安装ceph集群

2.1 安装ceph-deploy

#在master1-admin节点安装ceph-deploy

[root@master1-admin ~]# yum install python-setuptools  ceph-deploy -y

#在master1-admin、node1-monitor和node2-osd节点安装ceph

[root@master1-admin]#  yum install ceph ceph-radosgw  -y

[root@node1-monitor ~]# yum install ceph ceph-radosgw  -y

[root@node2-osd ~]# yum install ceph ceph-radosgw  -y

[root@master1-admin ~]# ceph --version

ceph version 10.2.11 (e4b061b47f07f583c92a050d9e84b1813a35671e)

2.2 创建monitor节点

#创建一个目录,用于保存 ceph-deploy 生成的配置文件信息的

[root@master1-admin ceph ~]# cd /etc/ceph

[root@master1-admin ceph]# ceph-deploy new master1-admin node1-monitor node2-osd

[root@master1-admin ceph]# ls

#生成了如下配置文件

ceph.conf  ceph-deploy-ceph.log  ceph.mon.keyring

Ceph配置文件、一个monitor密钥环和一个日志文件

2.3 安装ceph-monitor

1、修改ceph配置文件

#把ceph.conf配置文件里的默认副本数从3改成1 。把osd_pool_default_size = 2

加入[global]段,这样只有2个osd也能达到active+clean状态:

[root@master1-admin]# vim /etc/ceph/ceph.conf

[global]

fsid = af5cd413-1c53-4035-90c6-95368eef5c78

mon_initial_members = node1-monitor

mon_host = 192.168.40.201

auth_cluster_required = cephx

auth_service_required = cephx

auth_client_required = cephx

filestore_xattr_use_omap = true

osd_pool_default_size = 2

mon clock drift allowed = 0.500

mon clock drift warn backoff = 10

mon clock drift allowed #监视器间允许的时钟漂移量默认值0.05

mon clock drift warn backoff #时钟偏移警告的退避指数。默认值5

ceph对每个mon之间的时间同步延时默认要求在0.05s之间,这个时间有的时候太短了。所以如果ceph集群如果出现clock问题就检查ntp时间同步或者适当放宽这个误差时间。

cephx是认证机制是整个 Ceph 系统的用户名/密码

2、配置初始monitor、收集所有的密钥

[root@master1-admin]# cd /etc/ceph

[root@master1-admin]# ceph-deploy mon create-initial

[root@master1-admin]# ls *.keyring

ceph.bootstrap-mds.keyring  ceph.bootstrap-mgr.keyring  ceph.bootstrap-osd.keyring  ceph.bootstrap-rgw.keyring  ceph.client.admin.keyring  ceph.mon.keyring

2.4 部署osd服务

#准备osd

[root@ master1-admin ceph]# cd /etc/ceph/

[root@master1-admin ceph]# ceph-deploy osd prepare master1-admin:/dev/sdb [root@master1-admin ceph]# ceph-deploy osd prepare node1-monitor:/dev/sdb

[root@master1-admin ceph]# ceph-deploy osd prepare node2-osd:/dev/sdb

#激活osd

[root@master1-admin ceph]# ceph-deploy osd activate master1-admin:/dev/sdb1

[root@master1-admin ceph]# ceph-deploy osd activate node1-monitor:/dev/sdb1

[root@master1-admin ceph]# ceph-deploy osd activate node2-osd:/dev/sdb1

查看状态

[root@ master1-admin ceph]# ceph-deploy osd list master1-admin node1-monitor node2-osd

要使用Ceph文件系统,你的Ceph的存储集群里至少需要存在一个Ceph的元数据服务器(mds)。

2.5 创建ceph文件系统

创建mds

[root@ master1-admin ceph]# ceph-deploy mds create master1-admin node1-monitor node2-osd

查看ceph当前文件系统

[root@ master1-admin ceph]# ceph fs ls   

No filesystems enabled

一个cephfs至少要求两个librados存储池,一个为data,一个为metadata。当配置这两个存储池时,注意:

1. 为metadata pool设置较高级别的副本级别,因为metadata的损坏可能导致整个文件系统不用

2. 建议,metadata pool使用低延时存储,比如SSD,因为metadata会直接影响客户端的响应速度。

创建存储池

[root@ master1-admin ceph]# ceph osd pool create cephfs_data 128

pool 'cephfs_data' created

[root@ master1-admin ceph]# ceph osd pool create cephfs_metadata 128

pool 'cephfs_metadata' created

关于创建存储池

确定 pg_num 取值是强制性的,因为不能自动计算。下面是几个常用的值:

*少于 5 个 OSD 时可把 pg_num 设置为 128

*OSD 数量在 5 到 10 个时,可把 pg_num 设置为 512

*OSD 数量在 10 到 50 个时,可把 pg_num 设置为 4096

*OSD 数量大于 50 时,你得理解权衡方法、以及如何自己计算 pg_num 取值

*自己计算 pg_num 取值时可借助 pgcalc 工具

随着 OSD 数量的增加,正确的 pg_num 取值变得更加重要,因为它显著地影响着集群的行为、以及出错时的数据持久性(即灾难性事件导致数据丢失的概率)。

创建文件系统

创建好存储池后,你就可以用 fs new 命令创建文件系统了

[root@ master1-admin ceph]# ceph fs new xianchao cephfs_metadata cephfs_data

new fs with metadata pool 2 and data pool 1

其中:new后的fsname  可自定义

[root@ master1-admin ceph]# ceph fs ls              #查看创建后的cephfs

[root@ master1-admin ceph]# ceph mds stat          #查看mds节点状态

xianchao:1 {0=master1-admin=up:active} 2 up:standby

active是活跃的,另1个是处于热备份的状态

[root@master1-admin ceph]# ceph -s

    cluster cd296ab9-1f61-4b9f-8cc3-0a57dfab00eb

     health HEALTH_OK

     monmap e1: 3 mons at {master1-admin=192.168.40.200:6789/0,node1-monitor=192.168.40.201:6789/0,node2-osd=192.168.40.202:6789/0}

            election epoch 4, quorum 0,1,2 master1-admin,node1-monitor,node2-osd

      fsmap e7: 1/1/1 up {0=node2-osd=up:active}, 2 up:standby

     osdmap e20: 3 osds: 3 up, 3 in

            flags sortbitwise,require_jewel_osds

      pgmap v51: 116 pgs, 3 pools, 2068 bytes data, 20 objects

            323 MB used, 164 GB / 164 GB avail

                 116 active+clean

HEALTH_OK表示ceph集群正常

2.6 测试k8s挂载ceph rbd

需要有一套k8s环境

[root@xianchaomaster1 ~]# kubectl get nodes

NAME              STATUS   ROLES                  AGE   VERSION

xianchaomaster1   Ready    control-plane,master        24d   v1.23.1

xianchaonode1     Ready    worker                    24d   v1.23.1

xianchaomaster1节点ip:192.168.40.180

xianchaonode1节点ip:192.168.40.181

# kubernetes要想使用ceph,需要在k8s的每个node节点安装ceph-common,把ceph节点上的ceph.repo文件拷贝到k8s各个节点/etc/yum.repos.d/目录下,然后在k8s的各个节点yum install ceph-common -y

[root@master1-admin]# scp /etc/yum.repos.d/ceph.repo 192.168.40.180:/etc/yum.repos.d/

[root@master1-admin]# scp /etc/yum.repos.d/ceph.repo 192.168.40.181:/etc/yum.repos.d/

[root@xianchaomaster1 ~]# yum install ceph-common -y

[root@xianchaonode1 ~]# yum install ceph-common -y

#将ceph配置文件拷贝到k8s的控制节点和工作节点

[root@master1-admin ~]# scp /etc/ceph/* 192.168.40.180:/etc/ceph/

[root@master1-admin ~]# scp /etc/ceph/* 192.168.40.181:/etc/ceph/

#创建ceph rbd

[root@master1-admin ~]# ceph osd pool create k8srbd1 6

pool 'k8srbd' created

[root@master1-admin ~]# rbd create rbda -s 1024 -p k8srbd1

[root@master1-admin ~]# rbd feature disable  k8srbd1/rbda object-map fast-diff deep-flatten

#创建pod,挂载ceph rbd

#把nginx.tar.gz 上传到xianchaonode1上,手动解压

[root@xianchaonode1 ~]# docker load -i nginx.tar.gz

[root@xianchaomaster1 ~]# vim pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: testrbd

spec:

  containers:

    - image: nginx

      name: nginx

      imagePullPolicy: IfNotPresent

      volumeMounts:

      - name: testrbd

        mountPath: /mnt

  volumes:

    - name: testrbd

      rbd:

        monitors:

        - '192.168.40.201:6789'

        - '192.168.40.200:6789'

        - '192.168.40.202:6789'

        pool: k8srbd1

        image: rbda

        fsType: xfs

        readOnly: false

        user: admin

        keyring: /etc/ceph/ceph.client.admin.keyring

#更新资源清单文件

[root@xianchaomaster1 ~]# kubectl apply -f pod.yaml

#查看pod是否创建成功

[root@xianchaomaster1 ~]# kubectl get pods -o wide

注意: k8srbd1下的rbda被pod挂载了,那其他pod就不能占用这个k8srbd1下的rbda了

例:创建一个pod-1.yaml

[root@xianchaomaster1 ~]# cat pod-1.yaml

apiVersion: v1

kind: Pod

metadata:

  name: testrbd1

spec:

  containers:

    - image: nginx

      name: nginx

      imagePullPolicy: IfNotPresent

      volumeMounts:

      - name: testrbd

        mountPath: /mnt

  volumes:

    - name: testrbd

      rbd:

        monitors:

        - '192.168.40.201:6789'

        - '192.168.40.200:6789'

        - '192.168.40.202:6789'

        pool: k8srbd1

        image: rbda

        fsType: xfs

        readOnly: false

        user: admin

        keyring: /etc/ceph/ceph.client.admin.keyring

[root@xianchaomaster1 ~]# kubectl apply -f pod-1.yaml

pod/testrbd1 created

[root@xianchaomaster1 ~]# kubectl get pods

NAME       READY   STATUS    RESTARTS   AGE

testrbd    1/1     Running   0          51s

testrbd1   0/1     Pending   0          15s

#查看testrbd1详细信息

[root@xianchaomaster1 ~]# kubectl describe pods testrbd1

Warning  FailedScheduling  48s (x2 over 48s)  default-scheduler  0/2 nodes are available: 1 node(s) had no available disk, 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.

上面一直pending状态,通过warnning可以发现是因为我的pool: k8srbd1

        image: rbda

已经被其他pod占用了。

2.7 基于ceph rbd生成pv

1、创建ceph-secret这个k8s secret对象,这个secret对象用于k8s volume插件访问ceph集群,获取client.admin的keyring值,并用base64编码,在master1-admin(ceph管理节点)操作

[root@master1-admin ~]# ceph auth get-key client.admin | base64

QVFBWk0zeGdZdDlhQXhBQVZsS0poYzlQUlBianBGSWJVbDNBenc9PQ==

2.创建ceph的secret,在k8s的控制节点操作:

[root@xianchaomaster1 ~]# cat ceph-secret.yaml

apiVersion: v1

kind: Secret

metadata:

  name: ceph-secret

data:

  key: QVFBWk0zeGdZdDlhQXhBQVZsS0poYzlQUlBianBGSWJVbDNBenc9PQ==

[root@xianchaomaster1 ~]# kubectl apply -f ceph-secret.yaml

3.回到ceph 管理节点创建pool池

[root@master1-admin ~]# ceph osd pool create k8stest 6

pool 'k8stest' created

[root@master1-admin ~]# rbd create rbda -s 1024 -p k8stest

[root@master1-admin ~]# rbd feature disable  k8stest/rbda object-map fast-diff deep-flatten

4、创建pv

[root@xianchaomaster1 ~]# cat pv.yaml

apiVersion: v1

kind: PersistentVolume

metadata:

  name: ceph-pv

spec:   

   capacity:     

     storage: 1Gi   

   accessModes:     

   - ReadWriteOnce   

   rbd:     

         monitors:       

         - '192.168.40.201:6789'

         - '192.168.40.200:6789'

         - '192.168.40.202:6789'    

         pool: k8stest     

         image: rbda     

         user: admin     

         secretRef:       

             name: ceph-secret     

         fsType: xfs     

         readOnly: false   

   persistentVolumeReclaimPolicy: Recycle

[root@xianchaomaster1 ~]# kubectl apply -f pv.yaml

persistentvolume/ceph-pv created

[root@xianchaomaster1 ~]# kubectl get pv

5、创建pvc

[root@xianchaomaster1 ~]# cat pvc.yaml

kind: PersistentVolumeClaim

apiVersion: v1

metadata:   

  name: ceph-pvc

spec:   

  accessModes:     

  - ReadWriteOnce   

  resources:     

   requests:       

    storage: 1Gi

[root@xianchaomaster1 ~]# kubectl apply -f pvc.yaml

[root@xianchaomaster1 ceph]# kubectl get pvc

NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE

ceph-pvc      Bound    ceph-pv                                    1Gi        RWO         

6、测试挂载pvc

[root@xianchaomaster1 ~]# cat pod-2.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

  name: nginx-deployment

spec:

  selector:

    matchLabels:

     app: nginx

  replicas: 2 # tells deployment to run 2 pods matching the template

  template: # create pods using pod definition in this template

    metadata:

      labels:

        app: nginx

    spec:

      containers:

      - name: nginx

        image: nginx

        imagePullPolicy: IfNotPresent

        ports:

        - containerPort: 80

        volumeMounts:

          - mountPath: "/ceph-data"

            name: ceph-data

      volumes:

      - name: ceph-data

        persistentVolumeClaim:

            claimName: ceph-pvc

[root@xianchaomaster1 ~]# kubectl apply -f pod-2.yaml

[root@xianchaomaster1 ~]# kubectl get pods -l app=nginx

NAME                                 READY   STATUS    RESTARTS   AGE

nginx-deployment-fc894c564-8tfhn    1/1      Running   0             78s

nginx-deployment-fc894c564-l74km    1/1     Running   0             78s

[root@xianchaomaster1 ~]# cat pod-3.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

  name: nginx-1-deployment

spec:

  selector:

    matchLabels:

     appv1: nginxv1

  replicas: 2 # tells deployment to run 2 pods matching the template

  template: # create pods using pod definition in this template

    metadata:

      labels:

        appv1: nginxv1

    spec:

      containers:

      - name: nginx

        image: nginx

imagePullPolicy: IfNotPresent

        ports:

        - containerPort: 80

        volumeMounts:

          - mountPath: "/ceph-data"

            name: ceph-data

      volumes:

      - name: ceph-data

        persistentVolumeClaim:

            claimName: ceph-pvc

[root@xianchaomaster1 ~]# kubectl apply -f pod-3.yaml

[root@xianchaomaster1 ~]# kubectl get pods -l appv1=nginxv1

NAME                                 READY   STATUS    RESTARTS   AGE

nginx-1-deployment-cd74b5dd4-jqvxj   1/1     Running   0          56s

nginx-1-deployment-cd74b5dd4-lrddc   1/1     Running   0          56s

通过上面实验可以发现pod是可以以ReadWriteOnce共享挂载相同的pvc的

注意:ceph rbd块存储的特点

ceph rbd块存储能在同一个node上跨pod以ReadWriteOnce共享挂载

ceph rbd块存储能在同一个node上同一个pod多个容器中以ReadWriteOnce共享挂载

ceph rbd块存储不能跨node以ReadWriteOnce共享挂载

如果一个使用ceph rdb的pod所在的node挂掉,这个pod虽然会被调度到其它node,但是由于rbd不能跨node多次挂载和挂掉的pod不能自动解绑pv的问题,这个新pod不会正常运行

Deployment更新特性:

deployment触发更新的时候,它确保至少所需 Pods 75% 处于运行状态(最大不可用比例为 25%)。故像一个pod的情况,肯定是新创建一个新的pod,新pod运行正常之后,再关闭老的pod。

默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 25%

问题:

结合ceph rbd共享挂载的特性和deployment更新的特性,我们发现原因如下:

由于deployment触发更新,为了保证服务的可用性,deployment要先创建一个pod并运行正常之后,再去删除老pod。而如果新创建的pod和老pod不在一个node,就会导致此故障。

解决办法:

1,使用能支持跨node和pod之间挂载的共享存储,例如cephfs,GlusterFS等

2,给node添加label,只允许deployment所管理的pod调度到一个固定的node上。(不建议,这个node挂掉的话,服务就故障了)

2.8 基于storageclass动态生成pv

[root@master1-admin]# chmod 777  -R  /etc/ceph/*

[root@node1-monitor ~]# chmod 777  -R  /etc/ceph/*

[root@node2-osd]# chmod 777  -R  /etc/ceph/*

[root@xianchaomaster1~]# chmod 777  -R  /etc/ceph/*

[root@xianchaonode1]# chmod 777  -R  /etc/ceph/*

[root@master1-admin]# mkdir /root/.ceph/

[root@master1-admin]# cp -ar /etc/ceph/ /root/.ceph/

[root@node1-monitor ~]#mkdir /root/.ceph/

[root@node1-monitor ~]#cp -ar /etc/ceph/ /root/.ceph/

[root@node2-osd]# mkdir /root/.ceph/

[root@node2-osd]# cp -ar /etc/ceph/ /root/.ceph/

[root@xianchaomaster1~]#mkdir /root/.ceph/

[root@xianchaomaster1 ~]#cp -ar /etc/ceph/ /root/.ceph/

[root@xianchaonode1]# mkdir /root/.ceph/

[root@xianchaonode1 ~]#cp -ar /etc/ceph/ /root/.ceph/

1、创建rbd的供应商provisioner

#把rbd-provisioner.tar.gz上传到xianchaonode1上,手动解压

[root@xianchaonode1 ~]# docker load -i rbd-provisioner.tar.gz

[root@xianchaomaster1 ~]# cat rbd-provisioner.yaml

kind: ClusterRole

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: rbd-provisioner

rules:

  - apiGroups: [""]

    resources: ["persistentvolumes"]

    verbs: ["get", "list", "watch", "create", "delete"]

  - apiGroups: [""]

    resources: ["persistentvolumeclaims"]

    verbs: ["get", "list", "watch", "update"]

  - apiGroups: ["storage.k8s.io"]

    resources: ["storageclasses"]

    verbs: ["get", "list", "watch"]

  - apiGroups: [""]

    resources: ["events"]

    verbs: ["create", "update", "patch"]

  - apiGroups: [""]

    resources: ["services"]

    resourceNames: ["kube-dns","coredns"]

    verbs: ["list", "get"]

---

kind: ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: rbd-provisioner

subjects:

  - kind: ServiceAccount

    name: rbd-provisioner

    namespace: default

roleRef:

  kind: ClusterRole

  name: rbd-provisioner

  apiGroup: rbac.authorization.k8s.io

---

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

  name: rbd-provisioner

rules:

- apiGroups: [""]

  resources: ["secrets"]

  verbs: ["get"]

- apiGroups: [""]

  resources: ["endpoints"]

  verbs: ["get", "list", "watch", "create", "update", "patch"]

---

apiVersion: rbac.authorization.k8s.io/v1

kind: RoleBinding

metadata:

  name: rbd-provisioner

roleRef:

  apiGroup: rbac.authorization.k8s.io

  kind: Role

  name: rbd-provisioner

subjects:

- kind: ServiceAccount

  name: rbd-provisioner

  namespace: default

---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: rbd-provisioner

spec:

  selector:

    matchLabels:

      app: rbd-provisioner

  replicas: 1

  strategy:

    type: Recreate

  template:

    metadata:

      labels:

        app: rbd-provisioner

    spec:

      containers:

      - name: rbd-provisioner

        image: quay.io/xianchao/external_storage/rbd-provisioner:v1

        imagePullPolicy: IfNotPresent

        env:

        - name: PROVISIONER_NAME

          value: ceph.com/rbd

      serviceAccount: rbd-provisioner

---

apiVersion: v1

kind: ServiceAccount

metadata:

  name: rbd-provisioner

[root@xianchaomaster1 ~]# kubectl apply -f rbd-provisioner.yaml

[root@xianchaomaster1 ~]# kubectl get pods -l app=rbd-provisioner

NAME                               READY   STATUS    RESTARTS   AGE

rbd-provisioner-685746688f-8mbz5   1/1     Running   0          111s

2、创建ceph-secret

#创建pool池

[root@master1-admin ~]# ceph osd pool create k8stest1 6

[root@xianchaomaster1 ~]# cat ceph-secret-1.yaml

apiVersion: v1

kind: Secret

metadata:

  name: ceph-secret-1

type: "ceph.com/rbd"

data:

  key: QVFBWk0zeGdZdDlhQXhBQVZsS0poYzlQUlBianBGSWJVbDNBenc9PQ==

[root@xianchaomaster1 ~]# kubectl apply -f ceph-secret-1.yaml

3、创建storageclass

[root@xianchaomaster1 ~]# cat storageclass.yaml

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

  name: k8s-rbd

provisioner: ceph.com/rbd

parameters:

  monitors: 192.168.40.201:6789

  adminId: admin

  adminSecretName: ceph-secret-1

  pool: k8stest1

  userId: admin

  userSecretName: ceph-secret-1

  fsType: xfs

  imageFormat: "2"

  imageFeatures: "layering"

[root@xianchaomaster1 ~]# kubectl apply -f storageclass.yaml

注意:

k8s1.20版本通过rbd  provisioner动态生成pv会报错:

[root@xianchaomaster1 ~]# kubectl logs rbd-provisioner-685746688f-8mbz

E0418 15:50:09.610071       1 controller.go:1004] provision "default/rbd-pvc" class "k8s-rbd": unexpected error getting claim reference: selfLink was empty, can't make reference,报错原因是1.20版本仅用了selfLink,解决方法如下;

编辑/etc/kubernetes/manifests/kube-apiserver.yaml

在这里:

spec:

  containers:

  - command:

    - kube-apiserver

添加这一行:

- --feature-gates=RemoveSelfLink=false

[root@xianchaomaster1 ~]# systemctl restart kubelet

[root@xianchaomaster1 ~]# kubectl get pods -n kube-system

NAME                                       READY   STATUS             RESTARTS   AGE        

kube-apiserver-xianchaomaster1                    1/1     Running                 

4、创建pvc

[root@xianchaomaster1 ~]# cat rbd-pvc.yaml

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

  name: rbd-pvc

spec:

  accessModes:

    - ReadWriteOnce

  volumeMode: Filesystem

  resources:

    requests:

      storage: 1Gi

  storageClassName: k8s-rbd

[root@xianchaomaster1 ~]# kubectl apply -f rbd-pvc.yaml

[root@xianchaomaster1 ~]# kubectl get pvc

创建pod,挂载pvc

[root@xianchaomaster1 ~]# cat pod-sto.yaml

apiVersion: v1

kind: Pod

metadata:

  labels:

    test: rbd-pod

  name: ceph-rbd-pod

spec:

  containers:

  - name: ceph-rbd-nginx

    image: nginx

    imagePullPolicy: IfNotPresent

    volumeMounts:

    - name: ceph-rbd

      mountPath: /mnt

      readOnly: false

  volumes:

  - name: ceph-rbd

    persistentVolumeClaim:

      claimName: rbd-pvc

[root@xianchaomaster1 ~]# kubectl apply -f pod-sto.yaml

[root@xianchaomaster1 ~]# kubectl get pods -l test=rbd-pod

NAME           READY   STATUS    RESTARTS   AGE

ceph-rbd-pod   1/1     Running   0          50s

2.9 k8s挂载cephfs

[root@master1-admin ~]# ceph fs ls

name: xianchao, metadata pool: cephfs_metadata, data pools: [cephfs_data ]

1、创建ceph子目录

为了别的地方能挂载cephfs,先创建一个secretfile

[root@master1-admin ~]# cat /etc/ceph/ceph.client.admin.keyring |grep key|awk -F" " '{print $3}' > /etc/ceph/admin.secret

挂载cephfs的根目录到集群的mon节点下的一个目录,比如xianchao_data,因为挂载后,我们就可以直接在xianchao_data下面用Linux命令创建子目录了。

[root@master1-admin ~]# mkdir xianchao_data

[root@master1-admin ~]# mount -t ceph 192.168.40.201:6789:/ /root/xianchao_data -o name=admin,secretfile=/etc/ceph/admin.secret

[root@master1-admin ~]# df -h

192.168.40.201:6789:/    165G  106M  165G   1% /root/xianchao_data

在cephfs的根目录里面创建了一个子目录lucky,k8s以后就可以挂载这个目录 

[root@master1-admin ~]# cd /root/xianchao_data/

[root@master1-admin xianchao_data]# mkdir lucky

[root@master1-admin xianchao_data]# chmod 0777 lucky/

2、测试k8s的pod挂载cephfs

1)创建k8s连接ceph使用的secret

   将/etc/ceph/ceph.client.admin.keyring里面的key的值转换为base64,否则会有问题

[root@master1-admin xianchao_data]# echo "AQBvBdZgsDSZKxAAan+5rnsjr2ziA/atqFnQOA==" | base64

QVFCdkJkWmdzRFNaS3hBQWFuKzVybnNqcjJ6aUEvYXRxRm5RT0E9PQo=

[root@xianchaomaster1 ceph]# cat cephfs-secret.yaml

apiVersion: v1

kind: Secret

metadata:

  name: cephfs-secret

data:

  key: QVFCdkJkWmdzRFNaS3hBQWFuKzVybnNqcjJ6aUEvYXRxRm5RT0E9PQo=

[root@xianchaomaster1 ceph]# kubectl apply -f cephfs-secret.yaml

[root@xianchaomaster1 ceph]# cat cephfs-pv.yaml

apiVersion: v1

kind: PersistentVolume

metadata:

  name: cephfs-pv

spec:

  capacity:

    storage: 1Gi

  accessModes:

    - ReadWriteMany

  cephfs:

    monitors:

      - 192.168.40.201:6789

    path: /lucky

    user: admin

    readOnly: false

    secretRef:

        name: cephfs-secret

  persistentVolumeReclaimPolicy: Recycle  

[root@xianchaomaster1 ceph]# kubectl apply -f cephfs-pv.yaml

[root@xianchaomaster1 ceph]# cat cephfs-pvc.yaml

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

  name: cephfs-pvc

spec:

  accessModes:

    - ReadWriteMany

  volumeName: cephfs-pv

  resources:

    requests:

      storage: 1Gi

[root@xianchaomaster1 ceph]# kubectl apply -f cephfs-pvc.yaml

[root@xianchaomaster1 ceph]# kubectl get pvc

NAME          STATUS   VOLUME                             CAPACITY   

                  

cephfs-pvc    Bound    cephfs-pv                        1Gi        RWX                       

创建第一个pod,挂载cephfs-pvc

[root@xianchaomaster1 ceph]# cat cephfs-pod-1.yaml

apiVersion: v1

kind: Pod

metadata:

  name: cephfs-pod-1

spec:

  containers:

    - image: nginx

      name: nginx

      imagePullPolicy: IfNotPresent

      volumeMounts:

      - name: test-v1

        mountPath: /mnt

  volumes:

  - name: test-v1

    persistentVolumeClaim:

      claimName: cephfs-pvc

[root@xianchaomaster1 ceph]# kubectl apply -f cephfs-pod-1.yaml

创建第二个pod,挂载cephfs-pvc

[root@xianchaomaster1 ceph]# cat cephfs-pod-2.yaml

apiVersion: v1

kind: Pod

metadata:

  name: cephfs-pod-2

spec:

  containers:

    - image: nginx

      name: nginx

      imagePullPolicy: IfNotPresent

      volumeMounts:

      - name: test-v1

        mountPath: /mnt

  volumes:

  - name: test-v1

    persistentVolumeClaim:

      claimName: cephfs-pvc

[root@xianchaomaster1 ceph]# kubectl apply -f cephfs-pod-2.yaml

[root@xianchaomaster1 ceph]# kubectl exec -it cephfs-pod-1 -- /bin/sh

# cd /mnt         

# touch hello

[root@xianchaomaster1 ceph]# kubectl exec -it cephfs-pod-2 -- /bin/sh

# cd /mnt  

# touch welcome

回到master1-admin上,可以看到在cephfs文件目录下已经存在内容了

[root@master1-admin lucky]# pwd

/root/xianchao_data/lucky

[root@master1-admin lucky]# ls

hello  welcome

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/431656.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【自学笔记】支持向量机(4)——支持向量回归SVR

引入 SVM解决了分类问题,而用类似方法解决回归问题的模型称为支持向量回归。目标是得到一个模型,使输出的 f ( x ⃗ ) f(\vec{x}) f(x )与 y y y尽可能接近。 传统的回归模型直接计算 f ( x ⃗ ) f(\vec{x}) f(x )与 y y y的差距作为损失,当两…

Linux驱动开发(速记版)--驱动基础

第一章 初识内核源码 Linux系统源码提供了操作系统的核心功能,如进程管理、内存管理、文件系统等。 BusyBox这类的文件系统构建工具,则提供了在这些核心功能之上运行的一系列实用工具和命令,使得用户能够执行常见的文件操作、文本处理、网络配…

爬虫逆向学习(八):Canvas画图滑块验证码解决思路与绕过骚操作

此分享只用于学习用途,不作商业用途,若有冒犯,请联系处理 逆向站点 aHR0cHM6Ly93d3cuYm9odWF5aWNhaS5jbi8/VTU4Iy9jaGVtaWNhbC9sb2dpbj9yZWRpcmVjdD0lMkZjaGVtaWNhbA 滑块验证码样式 滑块验证码研究 一般的滑块验证码都是会直接提供滑块和…

Diffusion Model Stable Diffusion(笔记)

参考资料: 文章目录 DDPM架构模型如何拥有产生逼真图片的能力Denoise模型功能Denoise模型如何训练考虑进文字 文生图流程(Stable Diffusion) DDPM架构 模型如何拥有产生逼真图片的能力 Denoise模型功能 通过Denoise将一个噪音图一步步生成为目标图像 Denoise实际…

【开源免费】基于SpringBoot+Vue.JS墙绘产品展示交易平台(JAVA毕业设计)

本文项目编号 T 049 ,文末自助获取源码 \color{red}{T049,文末自助获取源码} T049,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

echarts根据容器宽度动态截取展示横坐标名称

效果如下: 初始状态: 缩放页面后: 代码地址:代码地址-面包多

Oracle 19c 使用EMCC 监控当前所有数据库

一.EMCC简介 EMCC,全称Oracle Enterprise Manager Cloud Control,是Oracle提供的一套集中化监控工具,可以对数据库、操作系统、中间件等进行监控,通过OMS(Oracle Management Service)收集监控数据并将监控信…

赛氪作媒体支持单位受邀参加首届科普翻译与跨学科专业学术研讨会

2024年9月22日,正值全国科普日之际,首届科普翻译与跨学科专业学术研讨会在上海健康与营养研究所信息中心励志厅成功举行并圆满结束。此次研讨会汇聚了来自全国各地的近60名专家学者、学界及企业界代表,共同探讨科普翻译与跨学科专业的发展。作…

OpenAI首款芯片曝光 专为文生视频Sora打造

近日,OpenAI首款芯片的消息终于曝光。据了解OpenAI已经预定了台积电的A16工艺制程,用于制造AI芯片,专为Sora视频应用打造,旨在提升Sore的视频生成能力。 年初曾有报道称,OpenAI CEO奥特曼打算募集7万亿美元和台积电合…

新160个crackme - 064-CR-Game0.7

运行分析 需破解Name、Company、Serial PE分析 32位程序,EP Section为.text,猜测无壳 静态分析&动态调试 ida搜索关键字符串,双击进入函数 level 0 动调sub_401403函数,注释如上Serial输入JPL-168-39,level 0通过 …

比较器(算法中排序)

方式一&#xff1a;不常用 让实体类实现Comparable接口&#xff0c;泛型是需要比较的类型&#xff0c;同时重写compareTo方法 缺点&#xff1a;对代码有侵入性。 public class Student implements Comparable<Student> {private String name;private double score;// …

计算机毕业设计 基于SpringBoot框架的网上蛋糕销售系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

基于springboot+vue的新闻推荐系统

基于springbootvue的新闻推荐系统 摘要 随着信息互联网购物的飞速发展&#xff0c;国内放开了自媒体的政策&#xff0c;一般企业都开始开发属于自己内容分发平台的网站。本文介绍了新闻推荐系统的开发全过程。通过分析企业对于新闻推荐系统的需求&#xff0c;创建了一个计算机…

探索AI编程新境界:aider库揭秘

文章目录 **探索AI编程新境界&#xff1a;aider库揭秘**背景&#xff1a;为何选择aider&#xff1f;简介&#xff1a;aider是什么&#xff1f;安装指南&#xff1a;如何安装aider&#xff1f;功能演示&#xff1a;aider的简单用法实战应用&#xff1a;aider在不同场景下的使用常…

[产品管理-33]:实验室技术与商业化产品的距离,实验室技术在商业化过程中要越过多少道“坎”?

目录 一、实验室技术 1.1 实验室研究性技术 1.2 技术发展的S曲线 技术发展S曲线的主要阶段和特点 技术发展S曲线的意义和应用 二、实验室技术商业化的路径 2.1 实验室技术与商业化产品的距离 1、技术成熟度与稳定性 - 技术自身 2、市场需求与适应性 - 技术是满足需求 …

写一个智慧物流园区的提货滚动看板系统

先看效果&#xff1a; 需求分析 作为5A级智慧物流园&#xff0c;每天几百辆的车流量&#xff0c;前台人工查询效率慢&#xff0c;客户急需解决客户查询货单慢的问题提升效率&#xff0c;减少车辆堵塞 第一步编写接口读取数据库数据&#xff1a; 第二步写前端展示代码&#xff1…

AI 文生图快速入门教程:让 Stable Diffusion 更易于上手

Stable Diffusion 是一个强大的 AI 图像生成工具&#xff0c;但它可能会消耗大量资源。在本指南中&#xff0c;我们将学习如何使用 AUTOMATIC1111 的 Stable Diffusion WebUI 来设置它。同时&#xff0c;我们将在 DigitalOcean GPU Droplet 云服务器上运行它&#xff0c;通过 H…

虚拟硬盘数据恢复(vmdk、vdi、vhd等虚拟磁盘文件)

我们用的虚拟机有时候用着用着就启动不起来了&#xff0c;可能是操作系统启动扇区出了问题或者硬盘数据损坏。如果还有重要的数据文件在虚拟机里面&#xff0c;不要慌&#xff0c;一般只是操作系统的问题&#xff0c;硬盘里面的文件一般是不会丢失损坏的&#xff0c;即使磁盘有…

UE学习篇ContentExample解读------Blueprint_Communication-下

文章目录 总览描述批次阅览2.1 Using an Event Dispatcher function to call an event in the level Blueprint2.2 Binding an Event Dispatcher function to a custom event2.3 Binding an Event Dispathcer to a custom event on spawn3.1 Basic communication using a Bluep…

jQuery——函数的使用

1、作为一般函数调用&#xff1a;$ ( param ) ① 参数为函数&#xff1a;当 DOM 加载完成后&#xff0c;执行此回调函数 ② 参数为选择器字符串&#xff1a;查找所有匹配的标签&#xff0c;并将它们封装成 jQuery 对象 ③ 参数为 DOM 对象&#xff1a;将 DOM 对象封装成 jQu…