首页 文章 精选 留言 我的

精选列表

搜索[持久记忆系统],共8038篇文章
优秀的个人博客,低调大师

从0到1使用Kubernetes系列(六):数据持久化实战

本文是从0到1使用Kubernetes系列第六篇,上一篇《从0到1使用Kubernetes系列(五):Kubernetes Scheduling》介绍了Kubernetes调度器如何进行资源调度,本文将为大家介绍几种常用储存类型。 默认情况下Pod挂载在磁盘上的文件生命周期与Pod生命周期是一致的,若Pod出现崩溃的情况,kubelet 将会重启它,这将会造成Pod中的文件将丢失,因为Pod会以镜像最初的状态重新启动。在实际应用当中,开发者有很多时候需要将容器中的数据保留下来,比如在Kubernetes中部署了MySql,不能因为MySql容器挂掉重启而上面的数据全部丢失;其次,在 Pod 中同时运行多个容器时,这些容器之间可能需要共享文件。也有的时候,开发者需要预置配置文件,使其在容器中生效,例如自定义了mysql.cnf文件在MySql启动时就需要加载此配置文件。这些都将是今天将要实战解决的问题。 今天这篇文将讲解下面几种常用存储类型: secret configMap emptyDir hostPath nfs persistentVolumeClaim SECRET Secret对象允许您存储和管理敏感信息,例如密码,OAuth令牌和ssh密钥。将此类信息放入一个secret中可以更好地控制它的用途,并降低意外暴露的风险。 ▌使用场景 鉴权配置文件挂载 ▌使用示例 在CI中push构建好的镜像就可以将docker鉴权的config.json文件存入secret对象中,再挂载到CI的Pod中,从而进行权限认证。 首先创建secret $ kubectl create secret docker-registry docker-config --docker-server=https://hub.docker.com --docker-username=username --docker-password=password secret/docker-config created 新建docker-pod.yaml文件,粘贴以下信息: apiVersion: v1 kind: Pod metadata: name: docker spec: containers: - name: docker image: docker command: - sleep - "3600" volumeMounts: - name: config mountPath: /root/.docker/ volumes: - name: config secret: secretName: docker-config items: - key: .dockerconfigjson path: config.json mode: 0644 Docker Pod挂载secret $ kubectl apply -f docker-pod.yaml pod/docker created 查看挂载效果 $ kubectl exec docker -- cat /root/.docker/config.json {"auths":{"https://hub.docker.com":{"username":"username","password":"password","auth":"dXNlcm5hbWU6cGFzc3dvcmQ="}}} 清理环境 $ kubectl delete pod docker $ kubectl delete secret docker-config ConfigMap 许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。这些配置信息需要与docker image解耦ConfigMap API给我们提供了向容器中注入配置信息的机制,ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件。 ▌使用场景 配置信息文件挂载 ▌使用示例 使用ConfigMap中的数据来配置Redis缓存 创建example-redis-config.yaml文件,粘贴以下信息: apiVersion: v1 kind: ConfigMap metadata: name: example-redis-config data: redis-config: | maxmemory 2b maxmemory-policy allkeys-lru 创建ConfigMap $ kubectl apply -f example-redis-config.yaml configmap/example-redis-config created 创建example-redis.yaml文件,粘贴以下信息: apiVersion: v1 kind: Pod metadata: name: redis spec: containers: - name: redis image: kubernetes/redis:v1 env: - name: MASTER value: "true" ports: - containerPort: 6379 resources: limits: cpu: "0.1" volumeMounts: - mountPath: /redis-master-data name: data - mountPath: /redis-master name: config volumes: - name: data emptyDir: {} - name: config configMap: name: example-redis-config items: - key: redis-config path: redis.conf Redis Pod挂载ConfigMap测试 $ kubectl apply -f example-redis.yaml pod/redis created 查看挂载效果 $ kubectl exec -it redis redis-cli $ 127.0.0.1:6379> CONFIG GET maxmemory 1) "maxmemory" 2) "2097152" $ 127.0.0.1:6379> CONFIG GET maxmemory-policy 1) "maxmemory-policy" 2) "allkeys-lru" 清理环境 $ kubectl delete pod redis $ kubectl delete configmap example-redis-config EmptyDir 当使用emptyDir卷的Pod在节点创建时,会在该节点创建一个新的空目录,只要该Pod运行在该节点,该目录会一直存在,Pod内的所有容器可以将改目录挂载到不同的挂载点,但都可以读写emptyDir内的文件。当Pod不论什么原因被删除,emptyDir的数据都会永远被删除(一个Container Crash 并不会在该节点删除Pod,因此在Container crash时,数据不会丢失)。默认情况下,emptyDir支持任何类型的后端存储:disk、ssd、网络存储。也可以通过设置 emptyDir.medium 为Memory,kubernetes会默认mount一个tmpfs(RAM-backed filesystem),因为是RAM Backed,因此 tmpfs 通常很快。但是会在容器重启或者crash时,数据丢失。 ▌使用场景 同一Pod内各容器共享存储 ▌使用示例 在容器a中生成hello文件,通过容器b输出文件内容 创建test-emptydir.yaml文件,粘贴以下信息: apiVersion: v1 kind: Pod metadata: name: test-emptydir spec: containers: - image: alpine name: container-a command: - /bin/sh args: - -c - echo 'I am container-a' >> /cache-a/hello && sleep 3600 volumeMounts: - mountPath: /cache-a name: cache-volume - image: alpine name: container-b command: - sleep - "3600" volumeMounts: - mountPath: /cache-b name: cache-volume volumes: - name: cache-volume emptyDir: {} 创建Pod kubectl apply -f test-emptydir.yaml pod/test-emptydir created 测试 $ kubectl exec test-emptydir -c container-b -- cat /cache-b/hello I am container-a 清理环境 $ kubectl delete pod test-emptydir HostPath 将宿主机对应目录直接挂载到运行在该节点的容器中。使用该类型的卷,需要注意以下几个方面: 使用同一个模板创建的Pod,由于不同的节点有不同的目录信息,可能会导致不同的结果 如果kubernetes增加了已知资源的调度,该调度不会考虑hostPath使用的资源 如果宿主机目录上已经存在的目录,只可以被root可以写,所以容器需要root权限访问该目录,或者修改目录权限 ▌使用场景 运行的容器需要访问宿主机的信息,比如Docker内部信息/var/lib/docker目录,容器内运行cadvisor,需要访问/dev/cgroups ▌使用示例 使用Docker socket binding模式在列出宿主机镜像列表。 创建test-hostpath.yaml文件,粘贴以下信息: apiVersion: v1 kind: Pod metadata: name: test-hostpath spec: containers: - image: docker name: test-hostpath command: - sleep - "3600" volumeMounts: - mountPath: /var/run/docker.sock name: docker-sock volumes: - name: docker-sock hostPath: path: /var/run/docker.sock type: Socket 创建test-hostpath Pod $ kubectl apply -f test-hostpath.yaml pod/test-hostpath created 测试是否成功 $ kubectl exec test-hostpath docker images REPOSITORY IMAGE ID CREATED SIZE docker 639de9917ae1 13 days ago 171MB ... NFS存储卷 NFS 卷允许将现有的 NFS(网络文件系统)共享挂载到您的容器中。不像 emptyDir,当删除 Pod 时,nfs 卷的内容被保留,卷仅仅是被卸载。这意味着 nfs 卷可以预填充数据,并且可以在 pod 之间共享数据。 NFS 可以被多个写入者同时挂载。 重要提示:您必须先拥有自己的 NFS 服务器然后才能使用它。 ▌使用场景 不同节点Pod使用统一nfs共享目录 ▌使用示例 创建test-nfs.yaml文件,粘贴以下信息: apiVersion: apps/v1 kind: Deployment metadata: name: test-nfs spec: selector: matchLabels: app: store replicas: 2 template: metadata: labels: app: store spec: volumes: - name: data nfs: server: nfs.server.com path: / affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: "kubernetes.io/hostname" containers: - name: alpine image: alpine command: - sleep - "3600" volumeMounts: - mountPath: /data name: data 创建测试deployment $ kubectl apply -f test-nfs.yaml deployment/test-nfs created 查看pod运行情况 $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE test-nfs-859ccfdf55-kkgxj 1/1 Running 0 1m 10.233.68.245 uat05 <none> test-nfs-859ccfdf55-aewf8 1/1 Running 0 1m 10.233.67.209 uat06 <none> 进入Pod中进行测试 # 进入uat05节点的pod中 $ kubectl exec -it test-nfs-859ccfdf55-kkgxj sh # 创建文件 $ echo "uat05" > /data/uat05 # 退出uat05节点的pod $ edit # 进入uat06节点的pod中 $ kubectl exec -it test-nfs-859ccfdf55-aewf8 sh # 查看文件内容 $ cat /data/uat05 uat05 清理环境 $ kubectl delete deployment test-nfs PersistentVolumeClaim 上面所有例子中我们都是直接将存储挂载到的pod中,那么在kubernetes中如何管理这些存储资源呢?这就是Persistent Volume和Persistent Volume Claims所提供的功能。 ● PersistentVolume 子系统为用户和管理员提供了一个 API,该 API 将如何提供存储的细节抽象了出来。为此,我们引入两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。 PersistentVolume(PV)是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含 Volume 的实现,即 NFS、iSCSI 或特定于云供应商的存储系统。 PersistentVolumeClaim(PVC)是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)。虽然 PersistentVolumeClaims 允许用户使用抽象存储资源,但用户需要具有不同性质(例如性能)的 PersistentVolume 来解决不同的问题。集群管理员需要能够提供各种各样的 PersistentVolume,这些PersistentVolume 的大小和访问模式可以各有不同,但不需要向用户公开实现这些卷的细节。对于这些需求,StorageClass 资源可以实现。 ● 在实际使用场景里,PV 的创建和使用通常不是同一个人。这里有一个典型的应用场景:管理员创建一个 PV 池,开发人员创建 Pod 和 PVC,PVC 里定义了Pod所需存储的大小和访问模式,然后 PVC 会到 PV 池里自动匹配最合适的 PV 给 Pod 使用。 ▌使用示例 创建PersistentVolume apiVersion: v1 kind: PersistentVolume metadata: name: mypv spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: slow mountOptions: - hard - nfsvers=4.0 nfs: path: /tmp server: 172.17.0.2 创建PersistentVolumeClaim kind: PersistentVolumeClaim apiVersion: v1 metadata: name: myclaim spec: accessModes: - ReadWriteOnce volumeMode: Filesystem resources: requests: storage: 5Gi volumeName: mypv 创建Pod绑定PVC kind: Pod apiVersion: v1 metadata: name: mypod spec: containers: - name: myfrontend image: nginx volumeMounts: - mountPath: "/var/www/html" name: mypd volumes: - name: mypd persistentVolumeClaim: claimName: myclaim 查看pod运行情况验证绑定结果 $ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE mypod 1/1 Running 0 1m 10.233.68.249 uat05 <none> $ kubectl exec -it mypod sh $ ls /var/www/html 清理环境 $ kubectl delete pv mypv $ kubectl delete pvc myclaim $ kubectl delete po mypod 总结 本次实战中使用了secret存储docker认证凭据,更好地控制它的用途,并降低意外暴露的风险。 使用configMap对redis进行缓存配置,这样即使redis容器挂掉重启configMap中的配置依然会生效。接着又使用emptyDir来使得同一Pod中多个容器的目录共享,在实际应用中开发者通常使用initContainers来进行预处理文件,然后通过emptyDir传递给Containers。然后再使用hostPath来访问宿主机的资源,当网路io达不到文件读写要求时,可考虑固定应用只运行在一个节点上然后使用hostPath来解决文件读写速度的要求。 NFS和PersistentVolumeClaim的例子实质上都是试容器挂载的nfs服务器共享目录,但这些资源一般都只掌握在了管理员手中,开发人员想要获取这部分资源那么就不是这么友好了,动态存储类(StorageClass)就能很好的解决此类问题。 更多关于Kubernetes系列的文章: 从0到1使用Kubernetes系列(五):Kubernetes Scheduling 从0到1使用Kubernetes系列(四):搭建第一个应用程序 从0到1使用Kubernetes系列(三):使用Ansible安装Kubernetes集群 从0到1使用Kubernetes系列(二)——安装工具介绍 从0到1使用Kubernetes系列——Kubernetes入门 关于Choerodon猪齿鱼 Choerodon猪齿鱼开源多云集成平台,基于开源技术Kubernetes,Istio,knative,Gitlab和Spring Cloud来实现本地和云端环境的集成,实现企业多云/混合云应用环境的一致性。平台通过提供精益敏捷、持续交付、容器环境、微服务、DevOps等能力来帮助组织团队来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。 大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献: 官网:http://choerodon.io 论坛:http://forum.choerodon.io Github:https://github.com/choerodon 微信公众号:Choerodon猪齿鱼 微博:Choerodon猪齿鱼

优秀的个人博客,低调大师

【许晓笛】EOS 数据库与持久化 API —— 实战

EOS 数据库开发实战 上次的文章详细讲解了 EOS 数据库的架构,本文将以官方示例为基础,详解 EOS 数据库的开发实战。 基本步骤 在智能合约里与 EOS 数据库交互,首先要定义存储的数据: 定义对象:具体就是定义一个 C++ 类或者 C++ 结构体,数据表就由一个个对象组成。 定义主键:在刚才的类/结构体中,定义一个const类型的成员函数primary_key(),返回值必须为uint64_t类型,返回值即为主键。 定义索引:EOS 数据表不光可以按照主键搜索数据,还可以定义多达 16 种索引。而且索引键(Key)不止支持64位无符号整数,还支持 128、256位整数以及双精度、四精度浮点数。 为每个索引定义 键提取器(key extractor)。 存储数据定义好之后,就可以与数据库交互了: 建立数据表:实例化 multi_index,建立数据表。 增删数据:使用emplace方法在表中添加数据;使用erace方法删除数据。 修改数据:使用modify方法修改数据。 查询数据:使用get、find方法和其他迭代器操作查询数据。 需求分析 我们参考 EOS 的官方示例,建立一个“汽车修理店”智能合约所需要的数据库。数据库服务的对象是维修技师和车主。每次车辆维修保养后,维修技师都可以添加本次维修服务的信息,可以更科学地管理每位客户的车辆维修保养服务。而且维修技师和车主都可以更新车辆目前的里程,以便技师确定车辆是否应该保养。我们需要一个数据表:维修数据表(service Table)。 建立数据对象 维修数据表中,每一条数据对象就是一次车辆维修保养的数据,包含以下成员: 主键:因为数据表主键必须是唯一的,所以无法用顾客的账户名作为主键(同一个顾客有多条维修记录)。这里我们让系统自动生成主键。 顾客账户:存储每次维修服务的顾客账户名。 维修日期:每次维修服务的日期。 车辆里程:每次服务时,车辆的里程信息。 我们还想方便的查询每个顾客的维修记录,所以需要一个以顾客账户名为键(Key)的索引。 这样我们就得到了 service_rec 结构体: struct service_rec { uint64_t pkey; // 主键 account_name customer; // 顾客账户 uint32_t service_date; // 维修日期 uint32_t odometer; // 车辆里程 //设置主键 auto primary_key()const { return pkey; } //设置索引 account_name get_customer()const { return customer; } //SERIALIZE 宏可以帮助提高编译速度 EOSLIB_SERIALIZE( service_rec, (pkey)(customer)(service_date)(odometer) ) }; 建立数据表 下面就可以建立数据表了,首先,multi_index是个模板类:(对 C++ 模板不熟悉的可以百度一下) eosio::multi_index <uint64_t TableName, typename T, typename... Indices> 我们需要填入以下multi_index的模板参数: TableName为数据表名称,12字符以内,只能使用小写字母,数字1-5,小数点“.”。 T为数据对象类型,这里就是我们定义的service_rec结构体。 Indices为索引列表,最多十六个。为了降低开发难度,官方推荐使用const_mem_fun模板,大家可以模仿官方的做法: 按照需求,我们这样设置multi_index的模板参数: using service_table_type = multi_index<service/*<-数据表名称*/, service_rec,/*<-数据对象类型*/ /*设置索引->*/indexed_by< N(bycustomer), const_mem_fun<service_rec, account_name, &service_rec::get_customer> > >; 这里并没有实例化multi_index,只是将填入相应模板参数的multi_index设置了一个别名:service_table_type。依然,对这里的做法不熟悉的可以看一下 C++ 模板类以及 C++ 的 using 关键字。 下面我们实例化multi_index,构造函数需要两个参数: multi_index( uint64_t code, uint64_t scope ) 其中,code为数据表的拥有者,scope为数据表的细分名称。这里有两种理解,一种理解是不同的 scope 就是不同的数据表,也就是说,在同一个账户下,存在着TableName相同的多个数据表,他们的scope互不相同;另一种理解:scope表示了同一个数据表的不同部分,互相独立读写。这两种理解的结果是一样的,就是唯一确定一个数据表需要三个参数:TableName,code,scope。 实例化multi_index: service_table_type service_table( current_receiver(), mechanic ); 上面的code = current_receiver(),表示当前的智能合约,即“汽车维修店合约”。如果这里的code为其他合约,那么说明这个multi_index指向了其他账户名下的数据表,在本合约中就只能进行读取操作了。scope = mechanic表明实例化的这个multi_index指向了细分名称为mechanic(以维修技师账户命名)的数据表。 我们所建立的数据表结构如下图所示。 操作数据 一般数据库的基本操作是增、删、改、查,EOS 数据库当然也具有这些功能。 新增数据 新增数据需要用到multi_index的emplace方法: const_iterator emplace( unit64_t payer, Lambda&& constructor ) 其中的payer参数位储存空间支付账户,也就是由谁来提供新加入的这个数据对象的存储空间,这里填入维修技师mechanic账户。constructor是个 Lambda 表达式,也叫匿名函数,是向emplace方法传入了一个构造函数,用来构造这个新的数据对象。 service_table.emplace(mechanic,/*<-储存空间支付账户*/ [&]( auto& s_rec )/*<-匿名函数*/ { s_rec.pkey = service_table.available_primary_key(); /*<-系统生成可用主键*/ //匿名函数体 s_rec.customer = eosio::chain::string_to_name(customer_name); //匿名函数体 s_rec.service_date = service_date; //匿名函数体 s_rec.odometer = odometer; //匿名函数体 }); 其中的customer_name、service_date、odometer要在实际开发时使用有意义的变量。 查询数据 由于service_table数据表的主键是没有意义的,所以我们需要使用bycustomer索引来根据顾客账户名(customer)查询数据。 auto customer_index = service_table.template get_index<N(bycustomer)>(); 这样我们就得到了bycustomer索引,我们可以使用索引的find方法来按照索引查找特定customer的数据对象。 //建立要查找的账户,注意这里的customer_name要使用有意义的字符串 account_name customer_acct = eosio::chain::string_to_name(customer_name); //使用`find`方法查找数据,使cust_itr(迭代器)指向所需数据 auto cust_itr = customer_index.find(customer_acct); 如果没有查找到,cust_itr(迭代器)就是service_table.end(),也就是搜索到最后也没有找到对应的数据。如果查找成功,cust_itr(迭代器)就会指向所需的数据对象。 之后,可以使用下面的代码可以遍历数据表中所有我们所需的条目。(因为顾客账户名不是唯一的,用find方法会找到符合条件的第一条数据) while (cust_itr != service_table.end() /*<-判断迭代器位置*/&& cust_itr->customer == customer_acct/*<-判断数据是否符合*/) { // 业务逻辑,对数据进行处理 cust_itr++;//迭代器自增,指向下一条数据 } 修改数据 在迭代器指向数据后,可以对数据进行修改,使用modify方法: service_table.modify(cust_itr,/*<-迭代器*/, mechanic, /*<-储存空间支付账户*/ [&]( auto& s_rec )/*<-匿名函数*/ { s_rec.customer = new_customer; //匿名函数体 s_rec.service_date = new_service_date; //匿名函数体 s_rec.odometer = new_odometer; //匿名函数体 }); 匿名函数中的new_customer、new_service_date、new_odometer请使用有意义的变量。也可以只修改其中部分变量。 删除数据 在迭代器指向数据后,可以对数据进行删除,使用erase方法: service_table.erase( cust_itr/*<-迭代器*/ ); 至此,带领大家了初步解了 EOS 数据库开发的思路与方法,EOS 数据库还有很多 API 可以供智能合约使用,大家可以查阅官方 Wiki:https://github.com/EOSIO/eos/wiki/Persistence-API

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册