Kubernetes应用包管理器Helm
创始人
2025-05-31 00:37:00

目录

一、Helm概述

1.1 为什么需要Helm?

1.2 Helm 介绍

1.3 Helm v3 变化

1.4 Helm客户端

1.4.1、部署Helm客户端

1.4.2、Helm常用命令

1.4.3、配置国内Chart仓库

二、Helm基本使用

2.1 Helm基本使用

2.1.1、使用chart部署一个应用

2.1.2、安装前自定义chart配置选项

2.1.3、构建一个Helm Chart

2.1.4、升级、回滚和删除

2.2 Chart模板

2.2.1、模板和调试

2.2.2、内置对象

2.2.3、Values

2.2.4、管道与函数

2.2.5、流程控制

2.2.6、变量

2.2.7、命名模板

2.3 使用Harbor作为Chart仓库


一、Helm概述


1.1 为什么需要Helm?

K8S上的应用对象,都是由特定的资源描述组成,包括deployment、service等。都保存各自文件中或者集中写到一个配置文件。然后kubectl apply –f 部署。

如果应用只由一个或几个这样的服务组成,上面部署方式足够了。

而对于一个复杂的应用,会有很多类似上面的资源描述文件,例如微服务架构应用,组成应用的服务可能多达十个,几十个。如果有更新或回滚应用的需求,可能要修改和维护所涉及的大量资源文件,而这种组织和管理应用的方式就显得力不从心了。

且由于缺少对发布过的应用版本管理和控制,使Kubernetes上的应用维护和更新等面临诸多的挑战,主要面临以下问题:

  • 如何将这些服务作为一个整体管理

  • 这些资源文件如何高效复用

  • 不支持应用级别的版本管理

1.2 Helm 介绍

Helm是一个Kubernetes的包管理工具,就像Linux下的包管理器,如yum/apt等,可以很方便的将之前打包好的yaml文件部署到kubernetes上。

Helm有两个重要概念:

  • helm:一个命令行客户端工具,主要用于Kubernetes应用chart的创建、打包、发布和管理。

  • Chart:应用描述,一系列用于描述 k8s 资源相关文件的集合。

  • Release:基于Chart的部署实体,一个 chart 被 Helm 运行后将会生成对应的一个 release;将在k8s中创建出真实运行的资源对象。

1.3 Helm v3 变化

2019年11月13日, Helm团队发布 Helm v3的第一个稳定版本。

该版本主要变化如下:

  • 架构变化

最明显的变化是 Tiller的删除

  • Release名称可以在不同命名空间重用

  • 支持将 Chart 推送至 Docker 镜像仓库中

  • 使用JSONSchema验证chart values

  • 其他

  1. 为了更好地协调其他包管理者的措辞 Helm CLI个别更名

helm delete` 更名为 `helm uninstall
helm inspect` 更名为 `helm show
helm fetch` 更名为 `helm pull

但以上旧的命令当前仍能使用。

2)移除了用于本地临时搭建 Chart Repository的 helm serve 命令。

3)自动创建名称空间

在不存在的命名空间中创建发行版时,Helm 2创建了命名空间。Helm 3遵循其他Kubernetes对象的行为,如果命名空间不存在则返回错误。

4) 不再需要requirements.yaml, 依赖关系是直接在chart.yaml中定义。

1.4 Helm客户端

1.4.1、部署Helm客户端

Helm客户端下载地址:https://github.com/helm/helm/releases

解压移动到/usr/bin/目录即可。

wget https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz
tar zxvf helm-v3.0.0-linux-amd64.tar.gz 
mv linux-amd64/helm /usr/bin/

1.4.2、Helm常用命令

命令

描述

create

创建一个chart并指定名字

dependency

管理chart依赖

get

下载一个release。可用子命令:all、hooks、manifest、notes、values

history

获取release历史

install

安装一个chart

list

列出release

package

将chart目录打包到chart存档文件中

pull

从远程仓库中下载chart并解压到本地 # helm pull stable/mysql --untar

repo

添加,列出,移除,更新和索引chart仓库。可用子命令:add、index、list、remove、update

rollback

从之前版本回滚

search

根据关键字搜索chart。可用子命令:hub、repo

show

查看chart详细信息。可用子命令:all、chart、readme、values

status

显示已命名版本的状态

template

本地呈现模板

uninstall

卸载一个release

upgrade

更新一个release

version

查看helm客户端版本

1.4.3、配置国内Chart仓库

  • 微软仓库(http://mirror.azure.cn/kubernetes/charts/)这个仓库强烈推荐,基本上官网有的chart这里都有。

  • 阿里云仓库(https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts )

  • 官方仓库(https://hub.kubeapps.com/charts/incubator)官方chart仓库,国内有点不好使。

添加存储库:

helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts 
helm repo update
## 查看配置的存储库    
helm repo list
helm search repo stable


二、Helm基本使用


2.1 Helm基本使用

主要介绍三个命令:

  • chart install

  • chart update

  • chart rollback


2.1.1、使用chart部署一个应用

查找chart:

helm search repo
helm search repo mysqlhelm show values stable/mysql
## 查看chart信息
helm show chart stable/mysql
安装包:
helm install db stable/mysql
查看发布状态:
helm status db 

我们直接使用helm 安装MySQL 后 发现 MySQL pod 处于 Pending ,因为默认模板资源会要求创建 PV,我们这里使用 NFS。

创建 MySQL-PV 资源为8G ,mysql peding 状态变为 Running

[root@k8s-master1 helm]# cat mysql-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:name: pv0003
spec:capacity:storage: 8GiaccessModes:- ReadWriteOncenfs:path: /ifs/kubernetes/dbserver: 192.168.2.118 

进入MySQL容器内部, MySQL可以正常访问

2.1.2、安装前自定义chart配置选项

上面部署的mysql并没有成功,这是因为并不是所有的chart都能按照默认配置运行成功,可能会需要一些环境依赖,例如PV。

所以我们需要自定义chart配置选项,安装过程中有两种方法可以传递配置数据:

  • --values(或-f):指定带有覆盖的YAML文件。这可以多次指定,最右边的文件优先

  • --set:在命令行上指定替代。如果两者都用,--set优先级高

--values使用,先将修改的变量写到一个文件中

## 将修改的变量写到一个文件中
helm show values stable/mysql > config.yaml## 新建数据库,和初始化密码
cat config.yaml 
persistence:enabled: truestorageClass: "managed-nfs-storage"accessMode: ReadWriteOncesize: 8Gi
mysqlUser: "k8s"
mysqlPassword: "123456"
mysqlDatabase: "k8s"# 重新 install db2 
helm install db2 -f config.yaml stable/mysql

db2-mysql pod 部署成功

进入 db2 容器 可以看到 数据库和用户成功创建。

以上成功创建MySQL用户k8s,并授予此用户访问新创建的k8s数据库权限,并接受该图表的所有其余默认值。

##命令行替代变量:
helm install db --set persistence.storageClass="managed-nfs-storage" stable/mysql##也可以把chart包下载下来查看详情:
helm pull stable/mysql

该helm install命令可以从多个来源安装:

  • chart存储库

  • 本地chart存档(helm install foo-0.1.1.tgz)

  • chart目录(helm install path/to/foo)

  • 完整的URL(helm install https://example.com/charts/foo-1.2.3.tgz)

2.1.3、构建一个Helm Chart

创建目录和各个文件。

## 创建 mychart
helm create mychart[root@k8s-master1 helm]# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
│  ├── deployment.yaml
│  ├── _helpers.tpl
│  ├── ingress.yaml
│  ├── NOTES.txt
│  ├── serviceaccount.yaml
│  ├── service.yaml
│  └── tests
│      └── test-connection.yaml
└── values.yaml
  • Chart.yaml:用于描述这个 Chart的基本信息,包括名字、描述信息以及版本等。

  • values.yaml :用于存储 templates 目录中模板文件中用到变量的值。

  • Templates: 目录里面存放所有yaml模板文件。

  • charts:目录里存放这个chart依赖的所有子chart。

  • NOTES.txt :用于介绍Chart帮助信息, helm install 部署后展示给用户。例如:如何使用这个 Chart、列出缺省的设置等。

  • _helpers.tpl:放置模板助手的地方,可以在整个 chart 中重复使用

创建Chart后,接下来就是将其部署:


## 导出 deployment  模板 ,自行定义value变量
kubectl create deployment mychart --image=nginx:1.16 -o yaml  --dry-run >  deployment.yaml[root@k8s-master1 mychart]# tree .
.
├── charts
├── Chart.yaml
├── templates
│   └── deployment.yaml
└── values.yaml2 directories, 3 files
[root@k8s-master1 mychart]# cat templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:name: {{ .Values.name }}
spec:replicas: {{ .Values.replicas }}selector:matchLabels:app: mycharttemplate:metadata:labels:app: mychartspec:containers:- image: {{ .Values.image }}:{{ .Values.imageTag }}name: nginxcat  mychart/values.yaml 
name: hello
replicas: 3
image: nginx
imageTag: 1.17## 部署 hello 应用
helm install hello mychart/

创建了3副本的 hello pod ,查看渲染后的 deployement 成功获取到 values中定义的变量。

2.1.4、升级、回滚和删除

发布新版本的chart时,或者当您要更改发布的配置时,可以使用该helm upgrade 命令

helm upgrade --set imageTag=1.16 hello mychart
helm upgrade -f values.yaml hello mychart

如果在发布后没有达到预期的效果,则可以使用helm rollback回滚到之前的版本。

例如将应用回滚到第一个版本:

## 回滚到上一个版本
helm rollback hello ## 回滚到 指定版本
helm rollback hello 2## 查看历史记录
helm history hello## 打包
helm package mychart## 卸载发行版,请使用以下helm uninstall命令:
helm uninstall web

2.2 Chart模板


Helm最核心的就是模板,即模板化的K8S manifests文件。

它本质上就是一个Go的template模板。Helm在Go template模板的基础上,还会增加很多东西。如一些自定义的元数据信息、扩展的库以及一些类似于编程形式的工作流,例如条件语句、管道等等。这些东西都会使得我们的模板变得更加丰富。

2.2.1、模板和调试

有了模板,我们怎么把我们的配置融入进去呢?用的就是这个values文件。这两部分内容其实就是chart的核心功能。

Helm也提供了--dry-run --debug调试参数,帮助你验证模板正确性。在执行helm install时候带上这两个参数就可以把对应的values值和渲染的资源清单打印出来,而不会真正的去部署一个release。

比如我们来调试上面创建的 chart 包:

接下来,部署nginx应用,熟悉模板使用,先把templates 目录下面所有文件全部删除掉,这里我们自己来创建模板文件:

rm -rf mychart/templates/*
vim templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- image: nginx:1.16name: nginx

实际上,这已经是一个可安装的Chart包了,通过 helm install命令来进行安装:

helm install web mychart

这样部署,其实与直接apply没什么两样。

然后使用如下命令可以看到实际的模板被渲染过后的资源文件:

helm get manifest web

可以看到,这与刚开始写的内容是一样的,包括名字、镜像等,我们希望能在一个地方统一定义这些会经常变换的字段,这就需要用到Chart的模板了。如下 定义 deployment.yaml 和service模板。

[root@k8s-master1 helm]# cat  nginx/templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:labels:chart: {{ .Chart.Name }}app: {{ .Release.Name }}name: {{ .Release.Name }}
spec:replicas: {{ .Values.replicas }}selector:matchLabels:app: {{ .Values.label }}template:metadata:labels:app: {{ .Values.label }}spec:containers:- image: {{ .Values.image }}:{{ .Values.imageTag }}name: {{ .Release.Name }}
[root@k8s-master1 helm]# 
[root@k8s-master1 helm]# 
[root@k8s-master1 helm]# cat  nginx/templates/service.yaml 
apiVersion: v1
kind: Service
metadata:labels:chart: {{ .Chart.Name }}app: {{ .Release.Name }}name: {{ .Release.Name }}
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:run: {{ .Values.label }} 
[root@k8s-master1 helm]# cat  nginx/values.yaml 
replicas: 3
image: nginx
imageTag: 1.17
label: nginx

这个deployment就是一个Go template的模板,这里定义的Release模板对象属于Helm内置的一种对象,是从values文件中读取出来的。这样一来,我们可以将需要变化的地方都定义变量。

再执行helm install chart 可以看到现在生成的名称变成了web-XXX,证明已经生效了。也可以使用命令helm get manifest查看最终生成的文件内容。

2.2.2、内置对象

刚刚我们使用 {{.Release.Name}}将 release 的名称插入到模板中。这里的 Release 就是 Helm 的内置对象,下面是一些常用的内置对象:

Release.Name

release 名称

Release.Name

release 名字

Release.Namespace

release 命名空间

Release.Service

release 服务的名称

Release.Revision

release 修订版本号,从1开始累加

2.2.3、Values

Values对象是为Chart模板提供值,这个对象的值有4个来源:

  • chart 包中的 values.yaml 文件

  • 父 chart 包的 values.yaml 文件

  • 通过 helm install 或者 helm upgrade 的 -f或者 --values参数传入的自定义的 yaml 文件

  • 通过 --set 参数传入的值

chart 的 values.yaml 提供的值可以被用户提供的 values 文件覆盖,而该文件同样可以被 --set提供的参数所覆盖。

[root@k8s-master1 helm]# cat  nginx/templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:labels:chart: {{ .Chart.Name }}app: {{ .Release.Name }}name: {{ .Release.Name }}
spec:replicas: {{ .Values.replicas }}selector:matchLabels:app: {{ .Values.label }}template:metadata:labels:app: {{ .Values.label }}spec:containers:- image: {{ .Values.image }}:{{ .Values.imageTag }}name: {{ .Release.Name }}
[root@k8s-master1 helm]# 
[root@k8s-master1 helm]# 
[root@k8s-master1 helm]# cat  nginx/templates/service.yaml 
apiVersion: v1
kind: Service
metadata:labels:chart: {{ .Chart.Name }}app: {{ .Release.Name }}name: {{ .Release.Name }}
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:run: {{ .Values.label }} 
[root@k8s-master1 helm]# cat  nginx/values.yaml 
replicas: 3
image: nginx
imageTag: 1.17
label: nginx

2.2.4、管道与函数

前面讲的模块,其实就是将值传给模板引擎进行渲染,模板引擎还支持对拿到数据进行二次处理。

例如从.Values中读取的值变成字符串,可以使用quote函数实现:

[root@k8s-master1 helm]# cat   nginx/templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:labels:chart: {{ .Chart.Name }}app: {{ .Release.Name }}name: {{ .Release.Name }}
spec:replicas: {{ .Values.replicas }}selector:matchLabels:app: {{ .Values.label }}template:metadata:labels:app: {{ .Values.label | quote }}test: {{ default "kangll" .Values.test }} test1: {{  default "kangll" .Values.test | indent 8 }} spec:containers:- image: {{ .Values.image }}:{{ .Values.imageTag }}name: {{ .Release.Name }}

函数生效,包括双引号、默认值和缩进函数

2.2.5、流程控制

流程控制是为模板提供了一种能力,满足更复杂的数据逻辑处理。

Helm模板语言提供以下流程控制语句:

  • if/else 条件块

  • with 指定范围

  • range 循环块

if

if/else块是用于在模板中有条件地包含文本块的方法,条件块的基本结构如下:

{{ if PIPELINE }}# Do something
{{ else if OTHER PIPELINE }}# Do something else
{{ else }}# Default case
{{ end }}

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:labels:chart: {{ .Chart.Name }}app: {{ .Release.Name }}name: {{ .Release.Name }}
spec:replicas: {{ .Values.replicas }}selector:matchLabels:app: {{ .Values.label }}template:metadata:labels:app: {{ .Values.label | quote }}{{- if eq  .Values.hadoop "hbase" }} ## 判断hadoop: regionServer{{- else }}hadoop: dataNode{{- end }}test: {{ default "kangll" .Values.test }} spec:containers:- image: {{ .Values.image }}:{{ .Values.imageTag }}name: {{ .Release.Name }}

在上面条件语句使用了eq运算符判断是否相等,除此之外,还支持ne、 lt、 gt、 and、 or等运算符。

通过模板引擎来渲染一下,会得到如下结果:

可以看到渲染出来会有多余的空行,这是因为当模板引擎运行时,会将控制指令删除,所有之前占的位置也就空白了,需要使用{{- if ...}} 的方式消除此空行:

        {{- if eq  .Values.hadoop "hbase" }} ## 判断hadoop: regionServer{{- else }}hadoop: dataNode{{- end }}test: {{ default "kangll" .Values.test }} 

条件判断就是判断条件是否为真,如果值为以下几种情况则为false:

  • 一个布尔类型的 假

  • 一个数字 零

  • 一个 空的字符串

  • 一个 nil(空或 null)

  • 一个空的集合( map、 slice、 tuple、 dict、 array)

除了上面的这些情况外,其他所有条件都为 真。

[root@k8s-master1 helm]# cat nginx/templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment...spec:containers:- image: {{ .Values.image.repository }}:{{ .Values.image.tag }}name: {{ .Release.Name }}{{- if .Values.resources.enable }}resources:limits:cpu: {{ .Values.resources.limits.cpu }}memory: {{ .Values.resources.limits.memory }}{{- else }}resources: {}{{- end }}  

values.yaml

[root@k8s-master1 helm]# cat nginx/values.yaml 
# Declare variables to be passed into your templates.replicaCount: 3image:repository: nginxtag: 1.17pullPolicy: IfNotPresent
hadoop: namenode
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""resources:enable: falselimits:cpu: 100mmemory: 128Mi# requests:#   cpu: 100m#   memory: 128Mi

with

with :控制变量作用域。

还记得之前我们的 {{.Release.xxx}}或者 {{.Values.xxx}}吗?其中的 . 就是表示对当前范围的引用, .Values就是告诉模板在当前范围中查找 Values对象的值。而 with语句就可以来控制变量的作用域范围,其语法和一个简单的 if语句比较类似:

[root@k8s-master1 helm]# cat   nginx/templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment...spec:{{- with .Values.nodeSelector }}nodeSelector:{{- toYaml . | nindent 8 }} # toYaml 打印每一行,"." 取值 ,nindent换行缩进{{- end }}containers:- image: {{ .Values.image.repository }}:{{ .Values.image.tag }}name: {{ .Release.Name }}{{- if .Values.resources.enable }}resources:limits:cpu: {{ .Values.resources.limits.cpu }}memory: {{ .Values.resources.limits.memory }}{{- else }}resources: {}{{- end }} 

values.yaml

# Declare variables to be passed into your templates.replicaCount: 3image:repository: nginxtag: 1.17pullPolicy: IfNotPresent
hadoop: namenode
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""...nodeSelector:team: agpu: long

我们打印下遍历出的结果

range

在 Helm 模板语言中,使用 range关键字来进行循环操作。

我们在 values.yaml文件中添加上一个变量列表:

# cat values.yaml 
test:- 1- 2- 3

configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:name: {{ .Release.Name }}
data:test: |{{- range .Values.test }}{{ . }}{{- end }}

循环内部我们使用的是一个" . ",这是因为当前的作用域就在当前循环内,这个 "."引用的当前读取的元素。

2.2.6、变量

在模板中,使用变量的场合不多,但我们将看到如何使用它来简化代码,并更好地利用with和range。

问题1:with中不能使用内置对象

with语句块内不能再 .Release.Name对象,否则报错。

我们可以将该对象赋值给一个变量可以来解决这个问题:

apiVersion: apps/v1
kind: Deployment
metadata:...spec:{{- with .Values.nodeSelector }}nodeSelector:app: {{ .Release.Name }}  ##出错{{- toYaml . | nindent 8 }} # toYaml 打印每一行,"." 取值 ,nindent换行缩进{{- end }}containers:- image: {{ .Values.image.repository }}:{{ .Values.image.tag }}name: {{ .Release.Name }}{{- if .Values.resources.enable }}resources:limits:cpu: {{ .Values.resources.limits.cpu }}memory: {{ .Values.resources.limits.memory }}{{- else }}resources: {}{{- end }}  

使用变量赋值


spec:{{- $releaseName := .Release.Name -}}{{- with .Values.nodeSelector }}nodeSelector:app: {{ $releaseName }}{{- toYaml . | nindent 8 }} # toYaml 打印每一行,"." 取值 ,nindent换行缩进{{- end }}

可以看到在 with语句上面增加了一句 {{-$releaseName:=.Release.Name-}},其中 $releaseName就是后面的对象的一个引用变量,它的形式就是 $name,赋值操作使用 :=,这样 with语句块内部的 $releaseName变量仍然指向的是 .Release.Name。

问题2:获取列表键值

# cat ../values.yaml
env:JAVA_OPTS: "-Xmx1G"NAME: "JAVA"# cat deployment.yaml 
...spec:{{- $releaseName := .Release.Name -}}{{- with .Values.nodeSelector }}nodeSelector:app: {{ $releaseName }}{{- toYaml . | nindent 8 }} # toYaml 打印每一行,"." 取值 ,nindent换行缩进{{- end }}containers:- image: {{ .Values.image.repository }}:{{ .Values.image.tag }}name: {{ .Release.Name }}env:{{- range $key,$val := .Values.env }}- name: {{ $key }}value: {{ $val }}{{- end }}

结果如下:

上面在 range循环中使用 $key和 $value两个变量来接收后面列表循环的键和值。

2.2.7、命名模板

命名模板:使用define定义,template引入,在templates目录中默认下划线开头的文件为公共模板(_helpers.tpl)

[root@k8s-master1 templates]# cat _helpers.tpl 
{{- define "name" -}}{{ .Release.Name }}
{{- end -}}{{- define "labels" -}}
app: {{ template "name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Name }}
release: {{ .Release.Name }}
{{- end -}}

查看渲染后的结果

2.3 使用Harbor作为Chart仓库

  1. 启用Harbor的Chart仓库服务

./install.sh --with-chartmuseum

启用后,默认创建的项目就带有helm charts功能了。

  1. 安装push插件

helm plugin install https://github.com/chartmuseum/helm-push

https://github.com/chartmuseum/helm-push/releases/download/v0.10.3/helm-push_0.10.3_linux_amd64.tar.gz

可以离线 下载 在该路径下创建helm-push文件夹,并将安装包拷贝到该文件夹下解压即可。

mkdir /root/.local/share/helm/plugins/helm-pushcp helm-push_0.10.3_linux_amd64.tar.gz /root/.local/share/helm/plugins/helm-pushtar zxvf /root/.local/share/helm/plugins/helm-push/helm-push_0.10.3_linux_amd64.tar.gz 

插件安装好后,使用方式从:helm push 变为 helm cm-push

3、添加repo

helm repo add  --username admin --password Harbor12345 myrepo http://harbor.winnerinf.com/chartrepo/library# 查看仓库
helm repo list# 更新仓库
helm repo update

4、推送与安装Chart

 helm  cm-push  mychart-0.1.0.tgz myrepohelm install web --version 0.1.0  myrepo/mychart

安装

😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅

相关内容

热门资讯

【微服务】—— Nacos设计... 文章目录一、简介Nacos起源Nacos 定位Nacos 优势二、Nacos 总体设计1࿰...
axios 请求其他服务器地址... 场景还原: Vue2项目中在生产环境调用其他服务器请求地址时候会在请求地址默认加上一串当前浏览器域...
一斤便宜10元还要降?榴莲可以... 最近几年,各种知名水果的价格可以说都出现了比较大的变化,特别是最近榴莲的价格持续下降,甚至还有降价的...
图解redis对象(hash ... 哈希表 哈希对象的编码可以是ziplist或者hashtable 条件 ·哈希对象保存的所有键值对的...
C语言再学习 -- C 标准库... 参看:C 标准库 - stdlib.h C 标准库 - 简介 stdlib .h 头...
欧佩克+连续第三次大幅增产,油... 欧佩克连续第三次大幅增产,这一举措无疑给油价带来了巨大压力,恐使其承压下跌。欧佩克作为全球重要的石油...
python 安装包相关命令 查看匹配的版本(大小写敏感)pip -V 查询已经安装了的包,并可以显示相应的版本&...
“2025外贸优品中华行——天... 中新网天津5月31日电 (记者 王君妍)31日,“2025外贸优品中华行—天津站”活动正式启幕。本次...
基于SpringBoot+Vu... 您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。 &#...
网络技巧|远程桌面连接不上的多... 写在前面的话专注于网络各种技巧和实用工具的分享,都是日常工作中遇到的大大小小问题记录下...
IM即时通讯软件系统源码安卓、... demo软件园每日更新资源,请看到最后就能获取你想要的: ​ 1.《计算机系统结构:解...
讲解一下关于MySQL数据库的... 对于数据库,市面上有着不少的数据库!比如:Oracle数据...
3.1.2数据库体系结构:分布... 3.1.2数据库体系结构:分布式数据库、分布式数据库特点、分布式数据库结构、数据分片、...
学习streamlit-6 系列目录 学习streamlit-1,简介学习streamlit-2,s...
冒泡 VS 插入 VS 选择—... 文章目录什么样的“排序算法”更加优质?排序算法的执行效率排序算法的内存消耗排序算法的稳...
Python 多线程 文章目录一、简介1.1 多线程的特性1.2 GIL二、线程1.2 单线程1.3 多线程三、线程池3....
基于树莓派实现超声波测距 目录 一,写在前面 二,超声波模块说明 ● 模块基本参数 ● IO口接线...
Linux(网络基础---数据... 文章目录0. 前言1. 以太网的帧格式2. 再谈局域网原理3. 汇总整体通信流程,补全...
瑞萨Renesas RA2L1... 前言(1)首先感谢 李肯前辈的活动,从而申请到了RA2L1...
在 Python 中如何删除指... 文章目录删除字符后的所有内容,保留分隔符删除最后一次出现的字符后的所有内容删除最后一次...
如何将字符串反转? 参考答案 使用 StringBuilder 或 StringBuffer 的 reverse 方法&...
初级指针的简单介绍 目录 1.什么是指针? 2.指针和指针类型 2.1指针+-整数 2.2指针的解...
常见的CMS后台getshel... 目录 WordPress dedecms aspcms 南方数据企业系统 phpmyadmin日志 ...
CVPR 2023 | 旷视研... 近日,CVPR 2023 论文接收结果出炉。近年来,CVPR 的投稿数量...
【Linux】进程的概念--程... 语言级别的地址以前我们在学习C语言指针的时候,会打印地址,会有内存级别的...
ChatGPT重量级对手产品:... 什么是ClaudeClaude是下一代人工智能助手,基于 Anthropic 对训练有...
全球与中国乳胶防水涂料市场规模... 全球与中国乳胶防水涂料市场规模预测及未来动向前瞻报告2025-2031年 【全新修订】:2025年...
外国剁手党们,催着中国电商集体... 文 | 潮汐商业评论 同欧美的朋友喜欢催更新中国霸道总裁等短剧一样,海外的剁手党们也爱装满来自中国...
nginx快速入门.跟学B站n... nginx快速入门.跟学B站nginx一小时精讲课程笔记nginx简介及环境准备nginx简介环境准...
C++ static静态成员变... 对象的内存中包含了成员变量,不同的对象占用不同的内存,这使得不同对象的成...