IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> [kubernetes in Action实践]六、将磁盘挂载到容器 -> 正文阅读

[开发工具][kubernetes in Action实践]六、将磁盘挂载到容器

介绍卷

Kubernetes 的卷是 pod 的一个组成部分, 因此像容器一样在 pod 的规范中就定义了,它和 pod 共享相同的生命周期,这意味着在 pod 启动时创建卷, 并在删除 pod时销毁卷。它们不是独立的Kubernetes对象, 也不能单独创建或删除。 pod 中的所有容器都可以使用卷,但必须先将它挂载在每个需要访问它的容器中。在每个容器中,都可以在其文件系统的任意位置挂载卷。

卷的应用示例

在这里插入图片描述
假设有一个带有三个容器的 pod(如图6.1所示), 一个容器运行了一个 web 服务器, 该 web 服务器的 HTML 页面目录位于 /var/htdocs, 并将站点访问日志存储到/var/logs 目录中。第二个容器运行了一个代理来创建 HTML 文件, 并将它们存放在/var/html 中, 第三个容器处理在 /var/logs 目录中找到的日志(转换、 压缩、 分析它们或者做其他处理)。
在这个例子中, 只需要在三个容器中挂载两个卷, 这样三个容器将可以一起工作。

pod有一个名为pubilcHtml的卷,这个卷被挂载在WebServer容器的/var/htdocs中,因为这是web服务器的服务目录。在ContentAgent 容器中也挂载了相同的卷, 但在/var/html中, 因为代理将文件写入/var/html中。通过这种方式挂载这个卷, web服务器现在将为contentagent生成的内容提供服务。
pod还有一个名为logVol的卷,用于存放日志,此卷在WebServer和LogRotator容器中的/var/log中挂载, 注意, 它没有挂载在ContentAgent容器中, 这个容器不能访问它的文件,即使容器和卷是同一个pod的一部分,在pod的规范中定义卷是不够的。 如果我们希望容器能够访问它, 还需要在容器的规范中定义一个VolumeMount。
在这里插入图片描述
而且本例中2个卷初始都为空,因此可以使用emptyDir的卷。Kubernetes还支持其它类型的卷,这些卷要么是在从外部源初始化卷时填充的, 要么是在卷内挂载现有目录。 这个填充或装入卷的过程是在pod内的容器启动之前执行的。

卷被绑定到pod的lifecycle(生命周期)中,只有在pod存在时才会存在,但取决于卷的类型, 即使在pod和卷消失之后,卷的文件也可能保待原样,并可以挂载到新的卷中。让我们来看看卷有哪些类型

卷的可用类型

  • emptyDir——用于存储临时数据的简单空目录
  • hostPath——用于将目录从工作节点的文件系统挂载到pod中。
  • gitRepo——通过检出Git仓库的内容来初始化的卷。
  • nfs——挂载到pod中的NFS共享卷。
  • gcePersistentDisk (Google高效能型存储磁盘卷)、awsElasticBlockStore (AmazonWeb务弹性块存储卷)、azureDisk (MicrosoftAzure 磁盘卷)——用于挂载云服务商提供的特定存储类型。
    ? cinder、cephfs、iscsi、flocker、glusterfs、quobyte、rbd、flexVolume、vsphere-Volume、photoPersistentDisk、scaleIO——用于挂载其他类型的网络存储。
    ? configMap、secret、downwardAPI——用于将 Kubernetes 部分资源和集群信息公开给 pod 的特殊类型的卷。
    ? persistentVolumeClaim——一种使用预置或者动态配置的持久存储类型(我们将在本 的最后 节对此展开讨论)

通过卷在容器之间共享数据

卷是如何在一个pod的多个容器之间共享数据的

使用emptyDir卷

最简单的卷类型是emptyDir卷,,卷从一个 目录开始,运行在 pod 内的应用程序可以写入它需要的任何文件。因为卷的生存周期与 pod 的生存周期相关联,所以 删除 pod 时,卷的内容就会丢失。
一个 emptyDir 卷对于在同 pod 中运行的容器之间共享文件特别有用。但是它也可以被单个容器用于将数据临时写入磁盘,例如在大型数据集上执行排序操作时,没有那么多内存可供使用。数据也可以写入容器的文件系统本身,但是这两者之间存在着细微的差别(容器的文件系统甚至可能是不可写的),所以写到挂载的卷可能是唯一的选择。

fortune镜像介绍:
系统ubuntu,nginx,fortune 命令每次运行时都会输出一个随机引用 ,可以创建一个脚本每 10 秒调用一次执行,井将其输出存储在index.html中。

创建pod

apiVersion: v1
kind: Pod
metadata:
  name: fortune
spec:
  containers:
  - image: luksa/fortune      # 容器html-generator:运行luksa/fortune镜像
    name: html-generator
    volumeMounts:
    - name: html              # 名为html的卷挂载在/var/htdocs
      mountPath: /var/htdocs
  - image: nginx:alpine       # 容器web-server:运行nginx:alpine镜像
    name: web-server
    volumeMounts:
    - name: html              # 与上面相同的卷挂载在/usr/share/nginx/html,只读
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:                      # 一叫名为html的单独emptyDir卷,挂载在上面的两个容器中
  - name: html
    emptyDir: {}
    # emptyDir:
    #   mediem: Memory            # 改用内存而不是硬盘

当html-generator容器启动时,它每10秒启动一次 fortune 命令输出到/var/htdocs/index.html 文件。因为卷是在/var/htdocs上挂载的,所以index.html文件被写入卷中,而不是容器的顶层。一旦web-server容器启动,它就开始为/usr/share/nginx/html 目录中的任意 HTML 文件提供服务(这是 Nginx 服务的默认服务文件目录) 。

创建好pod后,选择用端口转发的形式访问看看吧
kubectl port-forward fortune 8080:80
在这里插入图片描述

指定emptyDir的介质

作为卷来使用的emptyDir,是在承载 pod的 工作节点的实际磁盘上创建的,因此其性能取决于节点的磁盘类型。但我们可以通知 Kubernetes 在tmfs文件系统(存在内存而非硬盘)上创建emptyDir 。因此,可将emptyDir的medium设置为Memory(见上面yaml的注释)

emptyDir卷是最简单的卷类型,但是其他类型的卷都是在它的基础上构建的,在创建空目录后,它们会用数据填充它。

使用Git仓库作为存储卷

gitRepo卷基本上也是 emptyDir 卷,它通过克隆 Git 仓库并在pod启动时(但在创建容器之前) 检出特定版本来填充数据。
在这里插入图片描述

在创建gitRepo卷后,它并不能和对应 repo 保持同步当向仓库推送新增的提交时,卷中的文件将不会被更新。如果所用 pod 是由ReplicationController 管理的,删除这个 pod 将触发新建一个新的 pod,而这个新pod的卷中将包含最新的提交。

我们可以使用 Git 仓库来存放网站的静态 HTML 文件,并创建一个包含web 服务器容器和 gitRepo 卷的 pod 。每当 pod创建时,它会拉取网站的最新版本并开始托管网站。唯一的缺点是,每次将更改推送到 gitRepo 时,都需要删除pod才能托管新版本的网站。

运行一个web服务器pod,使用gitRepo

首先fork一下https://github.com/Arrowarcher/kubia-website-example(在github上创建自己的repo副本,别人的你无权限修改)
然后创建pod,我们需要一个Nginx容器和gitRepo卷:

apiVersion: v1
kind: Pod
metadata:
  name: gittrepo-volume-pod
spec:
  containers:
  - image: nginx:alpine       # 容器web-server:运行nginx:alpine镜像
    name: web-server
    volumeMounts:
    - name: html              # 与上面相同的卷挂载在/usr/share/nginx/html,只读
      mountPath: /usr/share/nginx/html
      readOnly: true
    ports:
    - containerPort: 80
      protocol: TCP
  volumes:                      # 一名为html的单独gitRepo卷,挂载在上面的容器中
  - name: html
    gitRepo: 
      repository: https://github.com/Arrowarcher/kubia-website-example.git
      revision: master
      directory: .              # 将repo克隆到卷的根目录,即mountPath下

创建好后使用端口转发的方式测试访问:
kubectl port-forward gitrepo-volume-pod 8080:80
在这里插入图片描述

踩坑记录:kind创建的节点没有安装git
所以要先去节点里安装git(kind的节点都是docker容器)
docker exec -it kind-worker3 bash
安装git忽略

要查看新版本的站点,需要删除 pod 并重建,每次进行更改时,没必要每次都删除 pod ,可以运行个附加进程来使卷Git 库保持同步 。在这里不详细解释如何实现。

介绍sidecar

Git的同步进程不应该运行在与nginx站点服务器相同的容器中。在第二容器sidecar container增加对pod主容器操作,而不是将附加逻辑加入主应用的代码中(更复杂和不可复用)
为了找到一个保持本地目录与 Git 仓库同步的现有容器镜像, 转到 Docker Hub 并搜索 “git syc”,可以看到很多可以实现的镜像。 然后在示例中,从 pod 的一个新容器使用镜像, 挂载 pod 现有的 gitRepo 卷到新容器中,并配置 Git 同步容器来保持文件与 Git repo 同步。 如果正确设置了所有的内容, 应该能看到 web 服务器正在加载的文件与 GitHub repo 同步。

对于使用私有git仓库的gitrepo卷,书中说不可行,要保持gitrepo卷的简单性,不添加任何通过ssh协议克隆私有库的支持,因为这需要向卷添加额外的配置选项。应该使用gitsync sidecar或类似的方法,而不是gitrepo卷。

访问工作节点文件系统上的文件

大多数 pod 应该忽略它们的主机节点, 因此它们不应该访问节点文件系统上的任何文件。 但是某些系统级别的 pod( 切记, 这些通常由 DaemonSet 管理)确实需要读取节点的文件或使用节点文件系统来访问节点设备。 Kubernetes 通过 hostPath卷实现了这一点。

hostPath卷

hostPath卷指向文件系统上的特定文件或目录。在同一节点上运行并在其hostPath卷使用相同的路径的pod可以看到相同的文件。
在这里插入图片描述
hostPath 卷是我们介绍的第一种类型的持久性存储,因为gitRepo和emptyDir卷的内容都会在 pod 被删除时被删除, 而 hostPath 卷的内容则不会被删除。如果删除了一个pod, 并且下一个pod使用了指向主机上相同路径的hostPath卷,则新pod将会发现上一个 pod 留下的数据,但前提是必须将其调度到与第一个 pod 相同的节点上。(所以对常规pod使用hostPath卷不是一个好主意,在别的节点会找不到数据)

检查使用hostPath卷的系统pod

kubectl describe pod kindnet-lr9ss -n kube-system
在这里插入图片描述
如果检查其他 pod, 则会看到其中没有一个使用 hostPath 卷来存储自己的数据,都是使用这种卷来访问节点的数据。hostPath 卷通常用于尝试单节点集群中的持久化存储,譬如 Minikube、kind 创建的集群。

提示 请记住仅当需要在节点上读取或写入系统文件时才使用 hostPath,切勿使用它们来持久化跨 pod的数据。

使用持久化存储

当运行在一个 pod 中的应用程序需要将数据保存到磁盘上, 并且即使该 pod 重新调度到另 一个节点时也要求具有相同的数据可用。 这就不能使用到目前为止我们提到的任何卷类型, 由于这些数据需要可以从任何集群节点访问, 因此必须将其存储在某种类型的网络存储 (NAS) 中。
要了解允许保存数据的卷, 我们将创建一个运行 MongoDB的 pod。 除了测试目的,仅仅运行没有卷或非持久卷的数据库 pod 没有任何意义,所以需要为该 pod 添加适当类型的卷并将其挂载在MongoDB 容器中。

使用 GCE 持久磁盘作为 pod 存储卷

书中是使用Google Kubemetes Engine,集群节点运行在GCE上,这里不做继续实践。

# 查看集群区域
gcloud container clusters list
# 创建一个 1GiB 容量并命名为 mongodb的GCE持久磁盘
gcloud compute disks create --size=1GiB --zone=europe-west1-b mongodb

在这里插入图片描述

由于我是使用kind,不能使用GCE持久磁盘,但是我部署了另一个hostpath卷,指定节点上目录作为持久储存

# mongodb-pod-hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mongodb
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: mongodb-data
    hostPath:
        path: /tmp/mongodb

pod 包含一个容器和一个卷,被之前创建的 GCE 持久磁盘支持(如图 6.5 所示)。因为 MongoDB 就是在 /data/db 上存储数据的, 所以容器中的卷也要挂载在这个路径上。
在这里插入图片描述

# pod中执行mongdb shell
kubectl exec -it mongodb mongo
>use mystore
>db.foo.insert({name:'foo'})
>db.foo.find()
# { "_id" : Objectid("57a6leb9deOcfd512374cc75"), "name" : "foo" }

在这里插入图片描述

kubectl delete pod mongodb
kubectl create -f mongodb-pod-gcepd.yaml
# pod中执行mongdb shell
kubectl exec -it mongodb mongo
>use mystore
>db.foo.find()
# { "_id" : Objectid("57a6leb9deOcfd512374cc75"), "name" : "foo" }

在这里插入图片描述

新的 pod 使用与前一个 pod 完全相同的 GCE Persistent Disk, 所以运行在其中的 MongoDB 容器应该会看到完全相同的数据, 即便将 pod 调度到不同的节点也是一样的。(hostpath是利用节点机器目录做持久化,别的节点没有数据哦)

通过底层持久化存储使用其他类型的卷

因为 Kubernetes 集群运行在 Google Kubernetes 引擎上所以需要创建 GCEpersistent disk。当在其他地方运行 Kubernetes 集群时,应该根据不同的基础设施用其他类型的卷。

例如:
Amazon的AWS EC2:awsElasticBlockStore卷
Microsoft Azure: azureFile或azureDisk卷
在这里插入图片描述

使用NFC卷

如果集群是运行在自有的一组服务器上,那么就有大量其他可移植的选项用于在卷内挂载外部存储。

volumes:
- name: mongodb-data
  nfs:					# 受NFS共享的卷
    seerver: 1.2.3.4	# nfs服务的IP
    path: /some/path # 服务器提供的路径

其他储存技术

要了解每个卷类型设置需要哪些属性的详细信息,可以转到KubernetesAPI引用中的KubernetesAPI定义,或者通过第三章展示的通过kubectl explain查找信息。

从底层储存技术解耦pod

到目前为止, 我们探索过的所有待久卷类型都要求 pod 的开发人员了解集群可用的真实网络存储的基础结构。 例如, 要创建支持 NFS 协议的卷, 开发人员必知道 NFS 节点所在的实际服务器。 Kubernetes 理念旨在向应用程序及其开发人员隐藏真实的基础设施, 使他们不必担心基础设施的具体状态, 并使应用程序可在大量云服务商和数据企业之间进行功能迁移。
理想的清况是, 在 Kubernetes 上部署应用程序的开发入员不需要知道底层使用的是哪种存储技术, 同理他们也不需要了解应该使用哪些类型的物理服务器来运行pod,与基础设施相关的交互是集群管理员独有的控制领域。

介绍持久卷和持久卷声明

研发人员无须向他们的 pod 中添加特定技术的卷, 而是由集群管理员设置底层存储, 然后通过Kubernetes API 服务器创建持久卷并注册。 在创建持久卷时, 管理员可以指定其大小和所支持的访问模式。
当集群用户需要在其 pod 中使用持久化存储时, 他们首先创建持久卷声明(Persistent VolumeClaim, 简称 PVC) 清单, 指定所需要的最低容量要求和访问模式,然后用户将持久卷声明清单提交给 Kubernetes API 服务器, Kubernetes 将找到可匹配的持久卷并将其绑定到持久卷声明。
持久卷声明可以当作 pod 中的一个卷来使用, 其他用户不能使用相同的持久卷,除非先通过删除持久卷声明绑定来释放。
在这里插入图片描述

创建持久卷

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mongodb-pv
spec:
  capacity: 
    storage: 1Gi # 定义 PersistentVolume 的大小
  accessModes: # 可以被单个客户端挂在为读写模式或者被多个客户端挂在为只读模式
    - ReadWriteOnce
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain # 当声明被释放后,PersistentVolume 将会被保留(不清理和删除)
  # gcePersistentDisk:          # 不能用gce持久磁盘,改用hostPath
  #   pdName: mongedb
  #   fsType: ext4
  hostPath:
    path: /tmp/mongodb

在这里插入图片描述

持久卷不属于任何命名空间,它跟节点一样是集群层面的资源

在这里插入图片描述

通过创建持久卷声明来获取持久卷

假设现在需要部署一个需要持久化存储的 pod ,将要用到之前创建的持久卷,但是不能直接在 pod 内使用,需要先声明一个。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc     # 声明的名称,之后把声明当作pod的卷使用时要用到
spec:
  resources:
    requests:
      storage: 1Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: ""  # 将于动态配置的章节中了解这里设置的用意

建好声明, Kubernetes 就会找到适当的持久卷并将其绑定到声明 ,持久卷容量必须足够大以满足声明的需求,并且卷的访问模式必须包含声明中指定的访问模式。在该示例中,声明请求1 GiB 的存储空间和 ReadWriteOnce 访问模式。

在这里插入图片描述

RWO:ReadWriteOnce,仅允许单个节点挂载读写
ROX:ReadOnlyMany,允许多个节点只读
RWX:ReadWriteMany,允许多个节点挂载读写这个卷
RWO ROX RWX 涉及可以同时使用卷的工作节点的数量而并非 pod 数量。

查看持久卷发现状态已经变成bound。

持久卷显示被绑定在 default/mongodb-pvc 的声明上,default是声明所在的命名空间。
持久卷是集群范围的,不能在命名空间中创建
持久卷声明只能在特定命名空间创建,因此只能被同命名空间的pod使用

在pod中使用持久卷声明

持久卷现在已经可用了,除非先释放掉卷,否则没有人可以申明相同的卷。要在pod 中使用持久卷,需要在 pod 的卷中引用持久卷声明名称,如下面的代码清单所示。

# 在pod中使用持久卷声明mongodb-pod-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mongodb
spec:
  nodeName: kind-worker		# 指定之前相同的节点
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:
    - name: mongodb-data
      mountPath: /data/db
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:
  - name: mongodb-data
    persistentVolumeClaim:
      claimName: mongodb-pvc    # 在pod卷中通过名称引用持久卷声明。

在这里插入图片描述

了解使用持久卷和持久卷声明的好处

在这里插入图片描述
上图展示了pod直接使用持久磁盘和通过持久卷和持久卷声明使用持久磁盘。使用后者对开发人员来说更加简单,研发人员不需要关心底层实际使用的储存技术,如mongodb-pod-pvc.yaml一样。
此外,现在可以在许多不同的Kubernetes集群上使用相同的 pod 和持久卷声明清单,因为它们不涉及任何特定依赖于基础设施的内容。声 明说:“我需要x存储量,并且我需要能够支持一个客户端同时读取和写入 。”然后pod通过其中一个卷的名称来引用声明。

回收持久卷

在这里插入图片描述
之前创建声明的时候立即绑定到持久卷,现在变成了pending。持久卷状态为Released,所以它可能包含前1个声明人的数据,如果集群管理员还没来得及清理,那么不应该将这个卷绑定到全新的声明中。除此之外,通过使用相同的持久卷,新 pod 可以读取由前一个 pod 存放的数据,即使声明和pod 是在不同的命名空间中创建的(因此有可能属于不同的集群租户)

手动回收持久卷
persistentVolumeReclaimPolicy: Retain # 当声明被释放后,PersistentVolume 将会被保留(不清理和删除)
因以上配置,持久卷和其数据会保留,手动回收持久卷并使其恢复可用的唯一方法是删除和重新创建持久卷资源。当这样操作时,你将决定如何处理底层存储中的文件:可以删除这些文件,也可以闲置不用,以便在下个 pod 中复用它们。

自动回收持久卷
存在两种其他可行的回收策略 Recycle和Delete

  • Recycle
    第一种删除卷的内容并使卷可用于再次声明,通过这种方式,持久卷可以被不同的持久卷声明和 pod 反复使用,如图 6.9 所示
    在这里插入图片描述

  • Delete策略删除底层储存

    GCE 持久磁盘无法使用Recycle 选项。这种类型的持久卷只支持 Retain和 Delete 策略,其他持久磁盘可能支持。因此创建持久卷之前,一定要检查持久卷用到的特定底层储存支持什么回收策略。

持久卷的动态卷配置。

从之前得知,使用持久卷和持久卷声 明可以轻松获得持久化存储资源,无须研发人员处理下面实际使用的存储技术,但这仍然需要一个集群管理员来支持实际的存储。幸运的是, Kubernetes 还可以通过动态配置持久卷来自动执行此任务。
管理员可以创建一个持久卷配置,定义一个或多个StorageClass对象,用户在持久卷声明中引用它,可以选择想要的持久卷类型。

通过 StorageClass 资源定义可用存储类型

在用户创建持久卷声明之前,管理员需要创建一个或多个StorageClass 资源然后才能创建新的持久卷。

# storageclass-fast-hostpath.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath   # 用于配置持久卷的插件
parameters:
  type: pd-ssd

这里我本想书中推荐的minikube的provisioner插件来测试,但是我用的是kind,通过查询kind-issue得知,kind含有默认的storageclass
在这里插入图片描述
change-default-storage-class

因此现在直接创建持久卷声明,指定StorageClass

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc     # 声明的名称,之后把声明当作pod的卷使用时要用到
spec:
  resources:
    requests:
      storage: 100Mi       # 1Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: standard

在这里插入图片描述
创建后发现是pending状态,跟书中不一样,原来是StorageClass的模式是延迟创建。

在这里插入图片描述
如果使用书中的yaml创建的sc,应该会创建好持久卷和持久卷声明了。
在这里插入图片描述
总结:StorageClass的好处是可以通过名称引用,来创建持久卷声明甚至创建持久卷。一旦部署了存储类,作为集群用户,就可以像以前那样部署完全相同的 PVC 清单和完全相同的 pod 清单。

不指定存储类的动态配置

如果持久卷声明没有明确指出要使用哪个存储类, 默认存储类会
用于动态提供持久卷的内容

在这里插入图片描述
创建pvc后会根据standard的配置,生成持久卷

当你想让 PVC 绑定到你手动配置的 PV 时,这里要指定空字符串,否则就会被指定成默认的storageClass,而后新生成一个PV。
在这里插入图片描述

总之,将持久化存储附加到一个容器的最佳方式是仅仅创建PVC(有需要就指定明确的storageClass)和容器,其他所有内容都由动态配置持久卷置备程序处理。
在这里插入图片描述

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2021-09-08 10:56:38  更:2021-09-08 10:58:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 4:24:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码