GlusterFS在Kubernetes中的应用

GlusterFS服务简介

GlusterFS是一个可扩展,分布式文件系统,集成来自多台服务器上的磁盘存储资源到单一全局命名空间,以提供共享文件存储。

特点:

  • 可以扩展到几PB容量
  • 支持处理数千个客户端
  • 兼容POSIX接口
  • 使用通用硬件,普通服务器即可构建
  • 能够使用支持扩展属性的文件系统,例如ext4,XFS
  • 支持工业标准的协议,例如NFS,SMB
  • 提供很多高级功能,例如副本,配额,跨地域复制,快照以及bitrot检测
  • 支持根据不同工作负载进行调优

GFS存储的一些术语:

  • Brick:GlusterFS中的存储单元,通常是一个受信存储池中的服务器的一个目录。可以通过主机名和目录名来标识,如'SERVER:EXPORT'。
  • Node:一个拥有若干brick的设备。
  • Volume:一组bricks的逻辑集合。
  • Client:挂载了GlusterFS卷的设备。
  • GFID:GlusterFS卷中的每个文件或目录都有一个唯一的128位的数据相关联,其用于模拟inode

    Namespace:每个Gluster卷都导出单个ns作为POSIX的挂载点。
  • RDMA:远程直接内存访问,支持不通过双方的OS进行直接内存访问。
  • RRDNS:round robin DNS是一种通过DNS轮转返回不同的设备以进行负载均衡的方法
  • Self-heal:用于后台运行检测复本卷中文件和目录的不一致性并解决这些不一致。
  • Split-brain:脑裂
  • Volfile:Glusterfs进程的配置文件,通常位于/var/lib/glusterd/vols/volname

GFS卷(volume)的模式:

Volume是一组Brick的组合,一个gfs集群中可以有多个volume,可以供客户端挂载、存储数据等,它有很多种模式供选择:

  1. 分布式卷(默认模式):即DHT,文件通过hash算法分布存在不同的brick里,单个brick失效会带来数据丢失,无需额外元数据服务器。
  2. 条带卷:即Striped,文件切分成一个个的chunk,存放于不同的brick上,只建议在非常大的文件时使用。
  3. 复制卷:即AFR,同一份数据会同步在多个Brick中,单一节点故障时保持数据高可用,事务性操作,保持一致性。
  4. 分布式复制卷:即AFR和DHT的组合,最少需要4台服务器才能创建,读操作可以做到负载均衡。
  5. 条带复制卷:Striped与AFR 的组合,最少需要4台服务器才能创建。
  6. 分布式条带复制卷:至少需要8台服务器才能创建,每四台服务器一组。

GlusterFS服务搭建及进程解析

主机名 ip地址 角色
master 192.168.72.100 k8s-master、glusterfs、heketi
node1 192.168.72.101 k8s-work节点
node2 192.168.72.102 k8s-work节点
  1. 获取gluster rpm包,我这里是7.5版本的,需要下载以下几个rpm包:

    # ls
    glusterfs-7.5-1.el7.x86_64.rpm glusterfs-libs-7.5-1.el7.x86_64.rpm
    glusterfs-api-7.5-1.el7.x86_64.rpm glusterfs-server-7.5-1.el7.x86_64.rpm
    glusterfs-api-devel-7.5-1.el7.x86_64.rpm psmisc-22.20-15.el7.x86_64.rpm
    glusterfs-cli-7.5-1.el7.x86_64.rpm userspace-rcu-0.10.0-3.el7.x86_64.rpm
    glusterfs-client-xlators-7.5-1.el7.x86_64.rpm userspace-rcu-0.7.16-1.el7.x86_64.rpm
    glusterfs-fuse-7.5-1.el7.x86_64.rpm

    地址:https://buildlogs.centos.org/centos/7/storage/x86_64/gluster-9/Packages/;如果有外网可以直接配置yum源安装需要的版本。

  2. 安装rpm包

    ## 先安装依赖的环境包
    yum -y install attr psmisc rpcbind ## 安装glusterfs
    rpm -ivh glusterfs-libs-7.5-1.el7.x86_64.rpm
    rpm -ivh glusterfs-7.5-1.el7.x86_64.rpm
    rpm -ivh glusterfs-api-7.5-1.el7.x86_64.rpm glusterfs-cli-7.5-1.el7.x86_64.rpm glusterfs-client-xlators-7.5-1.el7.x86_64.rpm
    rpm -ivh glusterfs-fuse-7.5-1.el7.x86_64.rpm
    rpm -ivh userspace-rcu-0.10.0-3.el7.x86_64.rpm
    rpm -ivh glusterfs-server-7.5-1.el7.x86_64.rpm
    rpm -ivh glusterfs-api-devel-7.5-1.el7.x86_64.rpm

    因为包之间会有依赖关系,按照以上顺序可以顺利安装,如果不想这么麻烦,可强制安装--force --nodeps。

  3. 启动服务

    ## 需要启动两个服务glusterd和glusterfsd
    systemctl start glusterd glusterfsd
  4. glusterd和glusterfsd两个服务的区别,以及gluster和glusterfs命令

    glusterd是其服务的守护进程,是一个管理模块,处理gluster发过来的命令,处理集群管理、存储池管理、brick管理、负载均衡、快照管理等,默认端口24007:

    # ss -tnlp | grep 24007
    LISTEN 0 128 *:24007 *:* users:(("glusterd",pid=7873,fd=10)) ## 进程
    # ps -ef | grep gluster
    root 7873 1 0 Mar28 ? 00:00:41 /usr/sbin/glusterd -p /var/run/glusterd.pid --log-level INFO

    glusterfsd是服务端模块,存储池中的每个brick(卷组)都会启动一个glusterfsd进程。此模块主要是处理客户端的读写请求,默认端口是从49152开始,后续端口都+1。我这里启动了两个卷组:

    # ss -tnlp | grep gluster
    LISTEN 0 128 *:49152 *:* users:(("glusterfsd",pid=7898,fd=11))
    LISTEN 0 128 *:49153 *:* users:(("glusterfsd",pid=44275,fd=11)) ## 进程
    # ps -ef | grep gluster
    root 7898 1 0 Mar28 ? 00:04:46 /usr/sbin/glusterfsd -s 192.168.72.100 --volfile-id xtest-fs.192.168.72.100.data-text-mind -p /var/run/gluster/vols/xtest-fs/192.168.72.100-data-text-mind.pid -S /var/run/gluster/3b5c03678c749dfd.socket --brick-name /data/text-mind -l /var/log/glusterfs/bricks/data-text-mind.log --xlator-option *-posix.glusterd-uuid=46bfd5ab-07be-4a97-993f-1ddab13e0ee9 --process-name brick --brick-port 49152 --xlator-option xtest-fs-server.listen-port=49152

    glusterfs命令是一个客户端模块,负责通过mount挂载集群中某台服务器的存储池,以目录的形式呈现给用户。用户mount几个卷组,就是出现几个glusterfs的进程,如下:

    # ps -ef | grep -w glusterfs | grep -v glusterfsd
    root 31496 1 0 Mar25 ? 00:02:46 /usr/sbin/glusterfs --process-name fuse --volfile-server=yq01-aip-aikefu10.yq01.baidu.com --volfile-id=xtest-fs /data/mnt

    gluster命令就是客户端请求命令,负责发起请求,比如创建、查看当前卷组等:

    # gluster volume create xtest-fs 192.168.72.100:/data/xtest-fs/brick force
    # gluster volume list
  5. 如果是多台gfs构成集群,在服务安装完毕之后,要进行一个添加的动作

    ## 添加
    # gluster peer probe node1
    # gluster peer probe node2 ## 查看当前集群
    # gluster pool list

heketi服务搭建以及管理GFS

heketi提供一个RESTful管理节点界面,可以用来管理GlusterFS卷的生命周期,通过heketi,就可以像使用Opentack Manila,kubernete和openShift一样申请可以动态配置GlusterFS卷,Heketi会动态在集群内选择bricks构建所需的volumes,这样以确保数据的副本会分散到集群不同的故障域内。

  1. 安装heketi服务,有源可以直接yum安装,没有可以单独下载rpm包安装,或者通过docker、k8s安装

    # yum -y install heketi heketi-client
    
    # heketi --version
    Heketi 9.0.0 # systemctl start heketi.service
  2. 配置ssh秘钥,使heketi节点可以免密访问glusterfs节点

    # ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ''
    # ssh-copy-id -i /etc/heketi/heketi_key.pub master
    # chown heketi:heketi /etc/heketi/
  3. 编辑heketi的配置文件/etc/heketi/heketi.json

    heketi有三种执行方式,分别为mock,ssh,kubernetes,官方建议在测试和开发环境使用mock,生产使用ssh,如果glusterfs是部署在k8s上,则使用kubernetes方式。

    下边对配置部分做出解释:

    {
    ## heketi服务的默认端口,可以更改。
    "_port_comment": "Heketi Server Port Number",
    "port": "8080", ## 是否开启认证,一般选是。
    "_use_auth": "Enable JWT authorization. Please enable for deployment",
    "use_auth": true, ## 若开启认证,则需要编辑认证的用户及密码。
    "_jwt": "Private keys for access",
    "jwt": {
    "_admin": "Admin has access to all APIs",
    "admin": {
    "key": "My Secret"
    },
    "_user": "User only has access to /volumes endpoint",
    "user": {
    "key": "My Secret"
    }
    }, "_glusterfs_comment": "GlusterFS Configuration",
    "glusterfs": {
    "_executor_comment": [
    "Execute plugin. Possible choices: mock, ssh",
    "mock: This setting is used for testing and development.",
    " It will not send commands to any node.",
    "ssh: This setting will notify Heketi to ssh to the nodes.",
    " It will need the values in sshexec to be configured.",
    "kubernetes: Communicate with GlusterFS containers over",
    " Kubernetes exec api."
    ],
    ## 选择操作gfs的方式,这里选择ssh。
    "executor": "ssh", ## 配置ssh秘钥
    "_sshexec_comment": "SSH username and private key file information",
    "sshexec": {
    "keyfile": "/etc/heketi/heketi_key",
    "user": "root",
    "port": "22",
    "fstab": "/etc/fstab"
    }, ## 配置k8s证书
    "_kubeexec_comment": "Kubernetes configuration",
    "kubeexec": {
    "host" :"https://kubernetes.host:8443",
    "cert" : "/path/to/crt.file",
    "insecure": false,
    "user": "kubernetes username",
    "password": "password for kubernetes user",
    "namespace": "OpenShift project or Kubernetes namespace",
    "fstab": "Optional: Specify fstab file on node. Default is /etc/fstab"
    }, "_db_comment": "Database file name",
    ## heketi的数据存储位置
    "db": "/var/lib/heketi/heketi.db", "_loglevel_comment": [
    "Set log level. Choices are:",
    " none, critical, error, warning, info, debug",
    "Default is warning"
    ],
    ## heketi的日志级别
    "loglevel" : "debug"
    }
    }
  4. 启动heketi服务,设置开启自启,测试访问

    # systemctl start heketi
    # systemctl enable heketi
    # curl http://master:8080/hello
    Hello from Heketi
  5. 配置heketi连接gfs服务

    (1) 在集群的主节点设置环境变量,注意端口根据实际情况修改

    # export HEKETI_CLI_SERVER=http://master:8080

    (2) 在集群的主节点修改/usr/share/heketi/topology-sample.json配置文件,执行添加节点和添加device的操作:

    {
    "clusters": [
    {
    "nodes": [
    {
    "node": {
    "hostnames": {
    "manage": [
    "master"
    ],
    "storage": [
    "192.168.72.100"
    ]
    },
    "zone": 1
    },
    "devices": [
    {
    "name": "/dev/sdb",
    "destroydata": false
    }
    ]
    }
    ]
    }
    ]
    }

    manage指定gfs主机域名,storage指定gfs主机ip,devices下指定要存储gfs数据的数据盘,此盘应该是一块未分区的干净盘,分区应该也可以,我这里没有试。

    可以添加多个主机和多块盘,具体格式如下:

    {
    "clusters": [
    {
    "nodes": [
    {
    "node": {
    "hostnames": {
    "manage": [
    "192.168.10.100"
    ],
    "storage": [
    "192.168.10.100"
    ]
    },
    "zone": 1
    },
    "devices": [
    {
    "name": "/dev/sdb",
    "destroydata": false
    },
    {
    "name": "/dev/sdc",
    "destroydata": false
    },
    {
    "name": "/dev/sdd",
    "destroydata": false
    },
    {
    "name": "/dev/sde",
    "destroydata": false
    },
    {
    "name": "/dev/sdf",
    "destroydata": false
    },
    {
    "name": "/dev/sdg",
    "destroydata": false
    },
    {
    "name": "/dev/sdh",
    "destroydata": false
    },
    {
    "name": "/dev/sdi",
    "destroydata": false
    }
    ]
    },
    {
    "node": {
    "hostnames": {
    "manage": [
    "192.168.10.101"
    ],
    "storage": [
    "192.168.10.101"
    ]
    },
    "zone": 2
    },
    "devices": [
    {
    "name": "/dev/sdb",
    "destroydata": false
    },
    {
    "name": "/dev/sdc",
    "destroydata": false
    },
    {
    "name": "/dev/sdd",
    "destroydata": false
    },
    {
    "name": "/dev/sde",
    "destroydata": false
    },
    {
    "name": "/dev/sdf",
    "destroydata": false
    },
    {
    "name": "/dev/sdg",
    "destroydata": false
    },
    {
    "name": "/dev/sdh",
    "destroydata": false
    },
    {
    "name": "/dev/sdi",
    "destroydata": false
    }
    ]
    },
    {
    "node": {
    "hostnames": {
    "manage": [
    "192.168.10.102"
    ],
    "storage": [
    "192.168.10.102"
    ]
    },
    "zone": 1
    },
    "devices": [
    {
    "name": "/dev/sdb",
    "destroydata": false
    },
    {
    "name": "/dev/sdc",
    "destroydata": false
    },
    {
    "name": "/dev/sdd",
    "destroydata": false
    },
    {
    "name": "/dev/sde",
    "destroydata": false
    },
    {
    "name": "/dev/sdf",
    "destroydata": false
    },
    {
    "name": "/dev/sdg",
    "destroydata": false
    },
    {
    "name": "/dev/sdh",
    "destroydata": false
    },
    {
    "name": "/dev/sdi",
    "destroydata": false
    }
    ]
    },
    {
    "node": {
    "hostnames": {
    "manage": [
    "192.168.10.103"
    ],
    "storage": [
    "192.168.10.103"
    ]
    },
    "zone": 2
    },
    "devices": [
    {
    "name": "/dev/sdb",
    "destroydata": false
    },
    {
    "name": "/dev/sdc",
    "destroydata": false
    },
    {
    "name": "/dev/sdd",
    "destroydata": false
    },
    {
    "name": "/dev/sde",
    "destroydata": false
    },
    {
    "name": "/dev/sdf",
    "destroydata": false
    },
    {
    "name": "/dev/sdg",
    "destroydata": false
    },
    {
    "name": "/dev/sdh",
    "destroydata": false
    },
    {
    "name": "/dev/sdi",
    "destroydata": false
    }
    ]
    }
    ]
    }
    ]
    }

    (3) 导入配置文件到heketi,使heketi管理gfs

    # heketi-cli -s $HEKETI_CLI_SERVER --user admin --secret 'My Secret' topology load --json=/usr/share/heketi/topology-sample.json
    Creating cluster ... ID: 6b7b8affdb10add55ea367e7e2f8e091
    Allowing file volumes on cluster.
    Allowing block volumes on cluster.
    Creating node master ... ID: 96bbfbf66bc8ad551fe5a8c8ef289218
    Adding device /dev/sdb ... OK

    如果报错:Error: Unable to get topology information: Invalid JWT token: Token missing iss claim。是因为没加用户名和密码 --user admin --secret 'My Secret'。

    (4) 测试创建一个volume,看是否成功

    # heketi-cli volume create --size=10 --durability=none --user "admin" --secret "My Secret"
    Name: vol_38412746b79cf0d7ffa69a3969c1cfed
    Size: 10
    Volume Id: 38412746b79cf0d7ffa69a3969c1cfed
    Cluster Id: 6b7b8affdb10add55ea367e7e2f8e091
    Mount: 192.168.72.100:vol_38412746b79cf0d7ffa69a3969c1cfed
    Mount Options: backup-volfile-servers=
    Block: false
    Free Size: 0
    Reserved Size: 0
    Block Hosting Restriction: (none)
    Block Volumes: []
    Durability Type: none
    Distribute Count: 1

    创建成功,heketi已经可以管理gfs服务。

k8s集群创建StorageClass实现动态管理pv pvc

  1. 创建StorageClass

    vi storageclass-gfs-heketi-distributed.yaml

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: gluster-distributed
    provisioner: kubernetes.io/glusterfs
    reclaimPolicy: Retain
    parameters:
    resturl: "http://192.168.72.100:8080"
    restauthenabled: "true"
    restuser: "admin"
    restuserkey: "My Secret"
    gidMin: "40000"
    gidMax: "50000"
    volumetype: "none"
    allowVolumeExpansion: true
    volumeBindingMode: Immediate

    provisioner:表示存储分配器,需要根据后端存储的不同而变更;

    reclaimPolicy: 默认即“Delete”,删除pvc后,相应的pv及后端的volume,brick(lvm)等一起删除;设置为”Retain”时则保留数据,需要手工处理

    resturl:heketi API服务提供的url;

    restauthenabled:可选参数,默认值为"false",heketi服务开启认证时必须设置为"true";

    restuser:可选参数,开启认证时设置相应用户名;

    restuserkey:可选参数。开启认证时输入对应用户的密码;

    secretNamespace:可选参数,开启认证时可以设置为使用持久化存储的namespace;

    secretName:可选参数,开启认证时,需要将heketi服务的认证密码保存在secret资源中;

    clusterid:可选参数,指定集群id,也可以是1个clusterid列表,格式为"id1,id2";

    volumetype:可选参数,设置卷类型及其参数,如果未分配卷类型,则有分配器决定卷类型;如"volumetype: replicate:3"表示3副本的replicate卷,"volumetype: disperse:4:2"表示disperse卷,其中‘4’是数据,’2’是冗余校验,"volumetype: none"表示distribute卷;

    allowVolumeExpansion:表示是否支持动态扩容,默认为true;

    volumeBindingMode:表示是否立即bound pv,可选值WaitForFirstConsumer和Immediate;

    创建sc:

    # kubectl create -f storageclass-gfs-heketi-distributed.yaml
    
    # kubectl get sc
    NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
    gluster-distributed kubernetes.io/glusterfs Retain Immediate true
  2. 创建pvc

    vi glusterfs-pvc.yaml

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: glusterfs-test
    spec:
    accessModes:
    - ReadWriteMany
    resources:
    requests:
    storage: 5Gi
    storageClassName: gluster-distributed

    创建pvc

    # kubectl create -f glusterfs-pvc.yaml
    # kubectl get pvc
    NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
    glusterfs-test Bound pvc-0a3cffea-36b0-4609-a983-88df995b99e8 5Gi RWX gluster-distributed 5s

    这个时候 df -h 就可以看到有新的分区被挂载

    # df -h | tail -n 1
    /dev/mapper/vg_5b4ef948902b85845afd387ca71c6858-brick_0fd0afa344ca798d7a9fccab5bb60a9e 5.0G 33M 5.0G 1% /var/lib/heketi/mounts/vg_5b4ef948902b85845afd387ca71c6858/brick_0fd0afa344ca798d7a9fccab5bb60a9e
  3. 创建一个deployment,测试读写

    vi glusterfs-deploy.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: glusterfs-deploy
    labels:
    app: nginx
    spec:
    replicas: 2
    selector:
    matchLabels:
    app: nginx
    template:
    metadata:
    labels:
    app: nginx
    spec:
    containers:
    - name: nginx
    image: registry:5000/nginx:1.13
    imagePullPolicy: IfNotPresent
    ports:
    - name: web
    containerPort: 80
    volumeMounts:
    - name: pvc-glusterfs
    mountPath: /data
    volumes:
    - name: pvc-glusterfs
    persistentVolumeClaim:
    claimName: glusterfs-test

    创建deployment

    # kubectl create -f glusterfs-deploy.yaml
    deployment.apps/glusterfs-deploy created # kubectl get pod | grep glus
    glusterfs-deploy-cfd8c8578-45r9z 1/1 Running 0 72s
    glusterfs-deploy-cfd8c8578-vjrzn 1/1 Running 0 104s

    测试读写

    # kubectl exec -it glusterfs-deploy-cfd8c8578-45r9z -- bash
    # cd /data/
    # for i in `seq 1 10`; do echo $i > $i ; done
    # ls
    1 10 2 3 4 5 6 7 8 9
    # cat 1
    1

至此,gfs已经可以作为k8s的storageclass来动态管理pv pvc了。

k8s中应用GlusterFS类型StorageClass的更多相关文章

  1. 【Kubernetes】在K8s中创建StatefulSet

    在K8s中创建StatefulSet 遇到的问题: 使用Deployment创建的Pod是无状态的,当挂在Volume之后,如果该Pod挂了,Replication Controller会再run一个 ...

  2. 在K8s中创建StatefulSet

    在K8s中创建StatefulSet 遇到的问题: 使用Deployment创建的Pod是无状态的,当挂在Volume之后,如果该Pod挂了,Replication Controller会再run一个 ...

  3. k8s 中的 Pod 细节了解

    k8s中Pod的理解 基本概念 k8s 为什么使用 Pod 作为最小的管理单元 如何使用 Pod 1.自主式 Pod 2.控制器管理的 Pod 静态 Pod Pod的生命周期 Pod 如何直接暴露服务 ...

  4. k8s中yaml文常见语法

    在k8s中,所有的配置都是 json格式的.但为了读写方便,通常将这些配置写成yaml 格式,其运行的时候,还是会靠yaml引擎将其转化为json,apiserver 也仅接受json的数据类型. y ...

  5. 在k8s中的基本概念

    在k8s中的基本概念 一.Pod1. podk8s下最重要也最基本的概念,由一个根容器Pause和许多用户业务容器组成,是容器的载体. 2. pod的yaml定义格式及字段 apiVersion: v ...

  6. [转帖]k8s 中的服务如何沟通

    k8s 中的服务如何沟通 https://www.jianshu.com/p/9fae09876eb7 本文将介绍 k8s 中的服务如何相互访问,例如后端服务访问数据库,不同类型的服务间的相互访问.并 ...

  7. K8s中的网络

    Kubernetes的网络通信问题: 1. 容器间通信: 即同一个Pod内多个容器间通信,通常使用loopback来实现. 2. Pod间通信: K8s要求,Pod和Pod之间通信必须使用Pod-IP ...

  8. Kubernetes之在k8s中部署Java应用

    部署好了k8s以后 部署参考https://www.cnblogs.com/minseo/p/12055731.html 怎么在k8s部署应用 项目迁移到k8s平台是怎样的流程 1,制作镜像 2,控制 ...

  9. K8s中Pod健康检查源代码分析

    了解k8s中的Liveness和Readiness Liveness: 表明是否容器正在运行.如果liveness探测为fail,则kubelet会kill掉容器,并且会触发restart设置的策略. ...

随机推荐

  1. 解释 Java 堆空间及 GC?

    当通过 Java 命令启动 Java 进程的时候,会为它分配内存.内存的一部分用于 创建堆空间,当程序中创建对象的时候,就从对空间中分配内存.GC 是 JVM 内 部的一个进程,回收无效对象的内存用于 ...

  2. Spring 应用程序有哪些不同组件?

    Spring 应用一般有以下组件:接口 - 定义功能.Bean 类 - 它包含属性,setter 和 getter 方法,函数等.Spring 面向切面编程(AOP) - 提供面向切面编程的功能.Be ...

  3. 图灵机器人 V1 和 V2 接入方法

    API1.0使用方法: import requests import json import yuyinhecheng as hc def Tuling(words):     Tuling_API_ ...

  4. Netty之非阻塞处理

    Netty 是一个异步的.基于事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络 IO 程序. 一.异步模型 同步I/O : 需要进程去真正的去操作I/O: 异步I/O:内核在I/O操作完成后 ...

  5. volist标签的各种属性

    volist标签通常用于查询数据集(select方法)的结果输出,通常模型的select方法返回的结果是一个二维数组,可以直接使用volist标签进行输出. 在控制器中首先对模版赋值: $User = ...

  6. 用JS写一个计算器(兼容手机端)

    先看成果:1.PC端2. 首先确立html,有哪些东西我们要知道.布局大概的样子在心里有个数 <!DOCTYPE html> <html> <head> <m ...

  7. python-图的字典表示

    图的字典表示.输入多行字符串,每行表示一个顶点和该顶点相连的边及长度,输出顶点数,边数,边的总长度.比如上图0点表示:{'O':{'A':2,'B':5,'C':4}}.用eval函数处理输入,eva ...

  8. eclipse 执行main方法 错误: 找不到或无法加载主类

    检查环境变量是否正确 JAVA_HOME JAVA的实际安装目录 CLASSPATH .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\ ...

  9. Elasticsearch8.1-ElasticsearchClient-Java客户端简单增删查改-随笔

    环境准备 Springboot 基本环境 自行前往https://start.spring.io/ 构建一个即可 Elasticsearch服务端 简单说下windows版本的安装  https:// ...

  10. 用来创建用户docker registry认证的Secret

    集群环境:1.k8s用的是二进制方式安装2.操作系统是linux (centos)3.操作系统版本为 7.4/7.94.k8s的应用管理.node管理.pod管理等用rancher.k8s令牌以及ma ...