Chemmy's Blog

chengming0916@outlook.com

环境变量

用于配置 Ollama 的运行环境和行为,以下是一些常见的环境变量及其用途:

网络配置

  • OLLAMA_HOST:定义 Ollama 服务器的协议和主机地址。默认为 127.0.0.1:11434,仅本机地址可通过 11434 端口访问该服务。可以通过此变量自定义 Ollama 服务的监听地址和端口,例如设置为 0.0.0.0:8080 ,可允许其他电脑访问 Ollama(如:局域网中的其他电脑)。默认使用 http 协议。若要使用 https 协议,可设置为:https://0.0.0.0:443

  • OLLAMA_ORIGINS:配置允许跨域请求的来源列表。默认包含 localhost127.0.0.10.0.0.0 等本地地址以及一些特定协议的来源。通过设置此变量,可以指定哪些来源可以访问 Ollama 服务,例如 OLLAMA_ORIGINS=*,https://example.com 允许所有来源以及 https://example.com 的跨域请求。

模型管理

  • OLLAMA_MODELS:指定模型文件的存储路径。默认为用户主目录下的 .ollama/models 文件夹。通过设置此变量,可以自定义模型文件的存储位置,例如 OLLAMA_MODELS=/path/to/models 将模型存储在指定的路径下。

  • OLLAMA_KEEP_ALIVE:控制模型在内存中的存活时间。默认为 5 分钟。负值表示无限存活,0 表示不保持模型在内存中。此变量用于优化模型加载和运行的性能,例如 OLLAMA_KEEP_ALIVE=30m 可以让模型在内存中保持 30 分钟。

  • OLLAMA_LOAD_TIMEOUT:设置模型加载过程中的超时时间。默认为 5 分钟。0 或负值表示无限超时。此变量用于防止模型加载过程过长导致服务无响应,例如 OLLAMA_LOAD_TIMEOUT=10m 可以将超时时间设置为 10 分钟。

  • OLLAMA_MAX_LOADED_MODELS:限制同时加载的模型数量。默认为 0,表示不限制。此变量用于合理分配系统资源,避免过多模型同时加载导致资源不足,例如 OLLAMA_MAX_LOADED_MODELS=4 可以限制同时加载 4 个模型。

  • OLLAMA_MAX_QUEUE:设置请求队列的最大长度。默认为 512。此变量用于控制并发请求的数量,避免过多请求同时处理导致服务过载,例如 OLLAMA_MAX_QUEUE=1024 可以将队列长度设置为 1024。

  • OLLAMA_MAX_VRAM:设置 GPU 显存的最大使用量(以字节为单位)。默认为 0,表示不限制。此变量用于控制 GPU 资源的使用,避免显存不足导致的问题,例如 OLLAMA_MAX_VRAM=8589934592 可以将显存限制为 8GB。

  • OLLAMA_GPU_OVERHEAD:为每个 GPU 预留的显存(以字节为单位)。默认为 0。此变量用于确保每个 GPU 有一定的显存余量,避免显存不足导致的问题,例如 OLLAMA_GPU_OVERHEAD=1073741824 可以为每个 GPU 预留 1GB 的显存。

性能与调度

  • OLLAMA_NUM_PARALLEL:设置同时处理的并行请求数量。默认为 0,表示不限制。此变量用于优化服务的并发处理能力,例如 OLLAMA_NUM_PARALLEL=8 可以同时处理 8 个并行请求。

  • OLLAMA_SCHED_SPREAD:允许模型跨所有 GPU 进行调度。默认为 false。启用此变量可以提高模型运行的灵活性和资源利用率,例如 OLLAMA_SCHED_SPREAD=1 可以启用跨 GPU 调度。

调试与日志

  • OLLAMA_DEBUG:启用额外的调试信息。默认为 false。开启此变量可以获取更多的调试日志,帮助排查问题,例如 OLLAMA_DEBUG=1 可以启用调试模式。

  • OLLAMA_NOHISTORY:禁用 readline 历史记录。默认为 false。启用此变量可以避免保存命令历史记录,例如 OLLAMA_NOHISTORY=1 可以禁用历史记录。

  • OLLAMA_NOPRUNE:在启动时不清理模型文件。默认为 false。启用此变量可以保留所有模型文件,避免不必要的清理操作,例如 OLLAMA_NOPRUNE=1 可以禁用模型文件的清理。

特性开关

  • OLLAMA_FLASH_ATTENTION:启用实验性的 Flash Attention 特性。默认为 false。此变量用于测试和使用新的注意力机制特性,例如 OLLAMA_FLASH_ATTENTION=1 可以启用 Flash Attention。

  • OLLAMA_MULTIUSER_CACHE:为多用户场景优化提示缓存。默认为 false。启用此变量可以提高多用户环境下的缓存效率,例如 OLLAMA_MULTIUSER_CACHE=1 可以启用多用户缓存优化

代理设置

  • HTTP_PROXY:设置 HTTP 代理服务器地址。此变量用于配置 Ollama 在进行 HTTP 请求时使用的代理服务器,例如 HTTP_PROXY=http://proxy.example.com:8080 可以让 Ollama 使用指定的 HTTP 代理。

  • HTTPS_PROXY:设置 HTTPS 代理服务器地址。此变量用于配置 Ollama 在进行 HTTPS 请求时使用的代理服务器,例如 HTTPS_PROXY=https://proxy.example.com:8080 可以让 Ollama 使用指定的 HTTPS 代理。

  • NO_PROXY:设置不使用代理的地址列表。此变量用于指定哪些地址在进行请求时不使用代理,例如 NO_PROXY=localhost,example.com 可以让 Ollama 在访问 localhost 和 example.com 时不使用代理.


CLI命令

命令 用途
ollama serve 在本地系统上启动 Ollama。
ollama create 从现有模型创建一个新模型,用于定制或训练。
ollama show 显示特定模型的详细信息,例如其配置和发布日期。
ollama run 运行指定的模型,使其准备好进行交互。
ollama pull 将指定的模型下载到您的系统。
ollama list 列出所有已下载的模型。
ollama ps 显示当前正在运行的模型。
ollama stop 停止指定的正在运行的模型。
ollama rm 从您的系统中移除指定的模型。

在终端,输入上面的命令 + ‘-h’,可查看具体命令的帮助文档(如,ollama show -h, 可查看 show 命令的帮助文档)。下面列出每个命令的具体用法和示例。

启动 Ollama

在本地系统上启动 Ollama:ollama server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ollama serve -h

---
Start ollama

Usage:
ollama serve [flags]

Aliases:
serve, start

Flags:
-h, --help help for serve

Environment Variables:
OLLAMA_DEBUG Show additional debug information (e.g. OLLAMA_DEBUG=1)
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
OLLAMA_KEEP_ALIVE The duration that models stay loaded in memory (default "5m")
OLLAMA_MAX_LOADED_MODELS Maximum number of loaded models per GPU
OLLAMA_MAX_QUEUE Maximum number of queued requests
OLLAMA_MODELS The path to the models directory
OLLAMA_NUM_PARALLEL Maximum number of parallel requests
OLLAMA_NOPRUNE Do not prune model blobs on startup
OLLAMA_ORIGINS A comma separated list of allowed origins
OLLAMA_SCHED_SPREAD Always schedule model across all GPUs

OLLAMA_FLASH_ATTENTION Enabled flash attention
OLLAMA_KV_CACHE_TYPE Quantization type for the K/V cache (default: f16)
OLLAMA_LLM_LIBRARY Set LLM library to bypass autodetection
OLLAMA_GPU_OVERHEAD Reserve a portion of VRAM per GPU (bytes)
OLLAMA_LOAD_TIMEOUT How long to allow model loads to stall before giving up (default "5m")
---

模型管理

1. 列出所有已下载的模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ollama list -h
List models

Usage:
ollama list [flags]

Aliases:
list, ls

Flags:
-h, --help help for list

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

2. 查看某个模型的详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ollama show -h
Show information for a model

Usage:
ollama show MODEL [flags]

Flags:
-h, --help help for show
--license Show license of a model
--modelfile Show Modelfile of a model
--parameters Show parameters of a model
--system Show system message of a model
--template Show template of a model

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

3. 显示正在运行的大模型

1
2
3
4
5
6
7
8
9
10
11
ollama ps -h
List running models

Usage:
ollama ps [flags]

Flags:
-h, --help help for ps

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

4. 停止指定的正在运行的模型

1
2
3
4
5
6
7
8
9
10
11
ollama stop -h
Stop a running model

Usage:
ollama stop MODEL [flags]

Flags:
-h, --help help for stop

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)

5. 删除已下载的大模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ollama rm -h

---
Remove a model

Usage:
ollama rm MODEL [MODEL...] [flags]

Flags:
-h, --help help for rm

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
---

6. 下载大模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ollama pull -h

---
Pull a model from a registry

Usage:
ollama pull MODEL [flags]

Flags:
-h, --help help for pull
--insecure Use an insecure registry

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
---

运行大模型

尽管我们可以通过第三方的 Web UI 等方式与基于 Ollama 部署的本地大模型进行交互,但通过 run 命令的方式与大模型交互,具有以下的优点:

  • 直接在终端运行 run 命令,可便捷高效地验证本地部署的大模型的响应效果
  • 记录保存大模型的响应:可将大模型的响应记录到本地文件。
  • 基于 run 命令开发自动化执行的脚本,定时调度执行,实现与大模型交互的自动化工作流程。

run 命令的帮助文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ollama run -h

---
Run a model

Usage:
ollama run MODEL [PROMPT] [flags]

Flags:
--format string Response format (e.g. json)
-h, --help help for run
--insecure Use an insecure registry
--keepalive string Duration to keep a model loaded (e.g. 5m)
--nowordwrap Don't wrap words to the next line automatically
--verbose Show timings for response

Environment Variables:
OLLAMA_HOST IP Address for the ollama server (default 127.0.0.1:11434)
OLLAMA_NOHISTORY Do not preserve readline history
---

Longhorn 单机部署指南

Longhorn 支持单机部署,它是一款基于 Kubernetes 的分布式块存储系统,同时也可以在单节点 Kubernetes 集群中完成部署和使用,适合开发测试、边缘计算等轻量场景。

一、单机部署的核心前提

  1. 环境要求

    1. 单节点 Kubernetes 集群(如 Minikube、k3s、MicroK8s 或手动搭建的单节点集群)

    2. 节点满足 Longhorn 基础条件:

      • 操作系统:Linux(内核 4.14+)

      • 容器运行时:Docker、containerd 等

      • 磁盘:至少 1 块空闲磁盘或目录(用于存储数据)

      • 依赖工具:open-iscsi(必须安装并启动)

  2. 网络要求

    1. 节点能访问互联网(用于拉取 Longhorn 镜像)

    2. Kubernetes 集群网络插件正常运行(如 Calico、Flannel)

二、单机部署的具体步骤(以 k3s 为例)

1. 部署单节点 k3s 集群

1
2
3
4
# 安装 k3s(禁用默认存储,避免冲突)
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --disable local-storage
# 配置 kubectl 环境变量
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

2. 安装依赖组件 open-iscsi

1
2
3
4
5
6
7
# Ubuntu/Debian
sudo apt update && sudo apt install -y open-iscsi
sudo systemctl enable --now iscsid

# CentOS/RHEL
sudo yum install -y iscsi-initiator-utils
sudo systemctl enable --now iscsid

3. 部署 Longhorn

推荐使用 Helm 部署(更易管理):

1
2
3
4
5
6
7
8
9
# 安装 Helm 3
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 添加 Longhorn Helm 仓库
helm repo add longhorn https://charts.longhorn.io
helm repo update

# 部署 Longhorn 到 longhorn-system 命名空间
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace

4. 验证部署状态

1
2
3
4
5
6
# 检查 Pod 是否全部 Running
kubectl -n longhorn-system get pods

# 检查 Longhorn 存储类是否创建
kubectl get sc
# 正常会看到 longhorn 存储类,且标注为 default

三、单机部署的注意事项

  1. 高可用限制

    1. 单机部署下,Longhorn 无法提供数据冗余(默认副本数为 3,需手动调整为 1),节点故障会导致数据丢失,仅适合非生产环境。

    2. 修改副本数:在 Longhorn UI 中设置默认副本数为 1,或在 PVC 注解中指定 numberOfReplicas: 1

  2. 存储路径配置

    1. 如果节点没有空闲磁盘,可以指定目录作为存储后端:

      1
      2
      3
      4
      5
      6
      # 创建本地目录
      sudo mkdir -p /data/longhorn
      sudo chmod 777 /data/longhorn
      # 部署时通过 Helm 参数指定
      helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace \
      --set defaultSettings.defaultDataPath=/data/longhorn
  3. 访问 Longhorn UI

    1. 单机环境下可通过 NodePort 访问 UI:

      1
      2
      kubectl -n longhorn-system get svc longhorn-frontend
      # 访问 http://<节点IP>:<NodePort>

四、单机部署的适用场景

  • 开发/测试环境:快速验证 Longhorn 功能、测试存储与应用的兼容性。

  • 边缘节点:资源受限的边缘设备,仅需单节点提供块存储。

  • 个人学习:熟悉 Kubernetes 存储编排和 Longhorn 运维操作。

是否需要我为你整理单机部署 Longhorn 后的测试用例,比如创建 PVC 并挂载到 Nginx 容器进行验证?

我们首先来了解一下 Go 语言中 string 类型的结构定义,先来看一下官方定义:

1
2
3
4
// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

string 是一个 8 位字节的集合,通常但不一定代表UTF-8编码的文本。string可以为空,但是不能为nil。 string的值是不能改变的

string 类型本质也是一个结构体,定义如下:

1
2
3
4
type stringStruct struct {
str unsafe.Pointer
len int
}

stringStructslice 还是很相似的, str 指针指向的是某个数组的首地址, len 代表的就是数组长度。怎么和 slice 这么相似,底层指向的也是数组,是什么数组呢?我们看看他在实例化时调用的方法:

1
2
3
4
5
6
//go:nosplit
func gostringnocopy(str *byte) string {
ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
s := *(*string)(unsafe.Pointer(&ss))
return s
}

入参是一个 byte 类型的指针,从这我们可以看出 string 类型底层是一个 byte 类型的数组,所以我们可以画出这样一个图片:

![[Golang字符串拼接的6种方式/IMG-20251216170853116.png]]

string 类型本质上就是一个 byte 类型的数组,在 Go 语言中 string 类型被设计为不可变的,不仅是在 Go 语言,其他语言中 string 类型也是被设计为不可变的,这样的好处就是:在并发场景下,我们可以在不加锁的控制下,多次使用同一字符串,在保证高效共享的情况下而不用担心安全问题。

string 类型虽然是不能更改的,但是可以被替换,因为 stringStruct 中的 str 指针是可以改变的,只是指针指向的内容是不可以改变的,也就说每一个更改字符串,就需要重新分配一次内存,之前分配的空间会被 gc 回收。

关于 string 类型的知识点就描述这么多,方便我们后面分析字符串拼接。

字符串拼接的6种方式及原理

原生拼接方式”+”

Go 语言原生支持使用 + 操作符直接对两个字符串进行拼接,使用例子如下:

1
2
3
var s string
s += "hello"
s += "world"

这种方式使用起来最简单,基本所有语言都有提供这种方式,使用 + 操作符进行拼接时,会对字符串进行遍历,计算并开辟一个新的空间来存储原来的两个字符串。

字符串格式化函数fmt.Sprintf

Go 语言中默认使用函数 fmt.Sprintf 进行字符串格式化,所以也可使用这种方式进行字符串拼接:

1
2
str := "hello "
str = fmt.Sprintf("%s%s", str, str)

fmt.Sprintf 实现原理主要是使用到了反射,具体源码分析因为篇幅的原因就不在这里详细分析了,看到反射,就会产生性能的损耗,你们懂得!!!

Strings.builder

Go 语言提供了一个专门操作字符串的库 strings ,使用 strings.Builder 可以进行字符串拼接,提供了 writeString 方法拼接字符串,使用方式如下:

1
2
3
4
var builder strings.Builder
builder.WriteString("hello ")
builder.WriteString("world")
builder.String()

strings.builder 的实现原理很简单,结构如下:

1
2
3
4
type Builder struct {
addr *Builder // of receiver, to detect copies by value
buf []byte // 1
}

addr 字段主要是做 copycheckbuf 字段是一个 byte 类型的切片,这个就是用来存放字符串内容的,提供的 writeString() 方法就是像切片 buf 中追加数据:

1
2
3
4
5
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}

提供的 String 方法就是将 []]byte 转换为 string 类型,这里为了避免内存拷贝的问题,使用了强制转换来避免内存拷贝:

1
2
3
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}

bytes.Buffer

因为 string 类型底层就是一个 byte 数组,所以我们就可以 Go 语言的 bytes.Buffer 进行字符串拼接。 bytes.Buffer 是一个一个缓冲 byte 类型的缓冲器,这个缓冲器里存放着都是 byte 。使用方式如下:

1
2
3
buf := new(bytes.Buffer)
buf.WriteString("hello")
buf.String()

bytes.buffer 底层也是一个 []byte 切片,结构体如下:

1
2
3
4
5
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
}

因为 bytes.Buffer 可以持续向 Buffer 尾部写入数据,从 Buffer 头部读取数据,所以 off 字段用来记录读取位置,再利用切片的 cap 特性来知道写入位置,这个不是本次的重点,重点看一下 WriteString 方法是如何拼接字符串的:

1
2
3
4
5
6
7
8
func (b *Buffer) WriteString(s string) (n int, err error) {
b.lastRead = opInvalid
m, ok := b.tryGrowByReslice(len(s))
if !ok {
m = b.grow(len(s))
}
return copy(b.buf[m:], s), nil
}

切片在创建时并不会申请内存块,只有在往里写数据时才会申请,首次申请的大小即为写入数据的大小。如果写入的数据小于64字节,则按64字节申请。采用动态扩展 slice 的机制,字符串追加采用 copy 的方式将追加的部分拷贝到尾部, copy 是内置的拷贝函数,可以减少内存分配。

但是在将 []byte 转换为 string 类型依旧使用了标准类型,所以会发生内存分配:

1
2
3
4
5
6
7
func (b *Buffer) String() string {
if b == nil {
// Special case, useful in debugging.
return "<nil>"
}
return string(b.buf[b.off:])
}

strings.join

Strings.join 方法可以将一个 string 类型的切片拼接成一个字符串,可以定义连接操作符,使用如下:

1
2
baseSlice := []string{"hello", "world"}
strings.Join(baseSlice, "")

strings.join 也是基于 strings.builder 来实现的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func Join(elems []string, sep string) string {
switch len(elems) {
case 0:
return ""
case 1:
return elems[0]
}
n := len(sep) * (len(elems) - 1)
for i := 0; i < len(elems); i++ {
n += len(elems[i])
}

var b Builder
b.Grow(n)
b.WriteString(elems[0])
for _, s := range elems[1:] {
b.WriteString(sep)
b.WriteString(s)
}
return b.String()
}

唯一不同在于在 join 方法内调用了 b.Grow(n) 方法,这个是进行初步的容量分配,而前面计算的n的长度就是我们要拼接的slice的长度,因为我们传入切片长度固定,所以提前进行容量分配可以减少内存分配,很高效。

切片append

因为 string 类型底层也是 byte 类型数组,所以我们可以重新声明一个切片,使用 append 进行字符串拼接,使用方式如下:

1
2
3
4
buf := make([]byte, 0)
base = "hello"
buf = append(buf, base...)
string(base)

如果想减少内存分配,在将 []byte 转换为 string 类型时可以考虑使用强制转换。

Benchmark对比

上面我们总共提供了6种方法,原理我们基本知道了,那么我们就使用 Go 语言中的 Benchmark 来分析一下到底哪种字符串拼接方式更高效。我们主要分两种情况进行分析:

  • 少量字符串拼接
  • 大量字符串拼接

我们先定义一个基础字符串:

1
var base  = "123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASFGHJKLZXCVBNM"

少量字符串拼接的测试我们就采用拼接一次的方式验证,base拼接base,因此得出benckmark结果:

1
2
3
4
5
6
7
8
9
10
11
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkSumString-16 21338802 49.19 ns/op 128 B/op 1 allocs/op
BenchmarkSprintfString-16 7887808 140.5 ns/op 160 B/op 3 allocs/op
BenchmarkBuilderString-16 27084855 41.39 ns/op 128 B/op 1 allocs/op
BenchmarkBytesBuffString-16 9546277 126.0 ns/op 384 B/op 3 allocs/op
BenchmarkJoinstring-16 24617538 48.21 ns/op 128 B/op 1 allocs/op
BenchmarkByteSliceString-16 10347416 112.7 ns/op 320 B/op 3 allocs/op
PASS
ok 8.412s

大量字符串拼接的测试我们先构建一个长度为200的字符串切片:

1
2
3
4
var baseSlice []string
for i := 0; i < 200; i++ {
baseSlice = append(baseSlice, base)
}

然后遍历这个切片不断的进行拼接,因为可以得出 benchmark:

1
2
3
4
5
6
7
8
9
10
11
12
goos: darwin
goarch: amd64

cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkSumString-16 7396 163612 ns/op 1277713 B/op 199 allocs/op
BenchmarkSprintfString-16 5946 202230 ns/op 1288552 B/op 600 allocs/op
BenchmarkBuilderString-16 262525 4638 ns/op 40960 B/op 1 allocs/op
BenchmarkBytesBufferString-16 183492 6568 ns/op 44736 B/op 9 allocs/op
BenchmarkJoinstring-16 398923 3035 ns/op 12288 B/op 1 allocs/op
BenchmarkByteSliceString-16 144554 8205 ns/op 60736 B/op 15 allocs/op
PASS
ok 10.699s

通过两次 benchmark 对比,我们可以看到

  • 当进行少量字符串拼接时,直接使用 + 操作符进行拼接字符串,效率还是挺高的,但是当要拼接的字符串数量上来时, + 操作符的性能就比较低了;
  • 函数 fmt.Sprintf 还是不适合进行字符串拼接,无论拼接字符串数量多少,性能损耗都很大,还是老老实实做他的字符串格式化就好了;
  • strings.Builder 无论是少量字符串的拼接还是大量的字符串拼接,性能一直都能稳定,这也是为什么 Go 语言官方推荐使用 strings.builder 进行字符串拼接的原因,在使用 strings.builder 时最好使用 Grow 方法进行初步的容量分配,观察 strings.join 方法的benchmark就可以发现,因为使用了 grow 方法,提前分配好内存,在字符串拼接的过程中,不需要进行字符串的拷贝,也不需要分配新的内存,这样使用 strings.builder 性能最好,且内存消耗最小。
  • bytes.Buffer 方法性能是低于 strings.builder 的, bytes.Buffer  转化为字符串时重新申请了一块空间,存放生成的字符串变量,不像 strings.buidler 这样直接将底层的 []byte 转换成了字符串类型返回,这就占用了更多的空间。

同步最后分析的结论:

无论什么情况下使用 strings.builder 进行字符串拼接都是最高效的,不过要主要使用方法,记得调用 grow 进行容量分配,才会高效。 strings.join 的性能约等于 strings.builder ,在已经字符串slice的时候可以使用,未知时不建议使用,构造切片也是有性能损耗的;如果进行少量的字符串拼接时,直接使用 + 操作符是最方便也是性能最高的,可以放弃 strings.builder 的使用。

综合对比性能排序:

1
strings.join  ≈ strings.builder > bytes.buffer > []byte转换string > "+" > fmt.sprintf

在使用 Qt 开发时,qDebug 打印包含中文的 QString 可能会出现乱码。这通常是由于字符编码不一致导致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <QDebug>
#include <QTextCodec>
#include <Windows.h>

int main() {

//设置QTextCodec
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec);

// 将字符串转换为 UTF-8 编码,然后再传递给 qDebug 打印。
qDebug() << QString::fromUtf8(u8"你好,世界!");
return 0;
}

系统要求

  • 操作系统: Ubuntu 18.04 (Bionic Beaver)
  • ROS 版本: Melodic Morenia (官方长期支持版本)
  • Python 版本: 2.7.x (ROS Melodic 默认使用)

安装步骤

1. 配置软件源和密钥

1
2
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

2. 更新软件包列表

1
sudo apt update

3. 安装 ROS Melodic

完整桌面版 (推荐,包含 GUI 工具、仿真器和常用库):

1
sudo apt install ros-melodic-desktop-full

其他可选版本:

1
2
sudo apt install ros-melodic-desktop    # 基础桌面版(无仿真器)
sudo apt install ros-melodic-ros-base # 最小核心版(仅通信库和工具)

4. 初始化 rosdep

1
2
sudo rosdep init
rosdep update

5. 设置环境变量

1
2
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

6. 安装构建工具和依赖

1
sudo apt install python-rosinstall python-rosinstall-generator python-wstool build-essential

7. 创建示例工作空间 (可选)

1
2
3
4
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make
source devel/setup.bash

验证安装

打开新终端,运行:

1
roscore

如果看到类似以下输出,说明安装成功:

1
2
3
4
5
6
7
... logging to /home/username/.ros/log/xxx/roslaunch-hostname-xxx.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://hostname:xxx/
ros_comm version 1.14.3

常见问题解决

1. 密钥获取失败

如果 apt-key adv 失败,可以手动下载并添加:

1
curl -sSL 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xC1CF6E31E6BADE8868B172B4F42ED6FBAB17C654' | sudo apt-key add -

2. 网络问题

如果下载速度慢,可以替换为国内镜像源(如清华源):

1
sudo sh -c '. /etc/lsb-release && echo "deb https://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list'

3. Python 版本验证

确保系统中已安装 Python 2.7:

1
python --version  # 应显示 Python 2.7.x

测试命令

安装完成后,可以使用以下命令测试 ROS 功能:

  • roscore - 启动 ROS master
  • rosrun roscpp_tutorials talker - 运行发布者节点
  • rosrun roscpp_tutorials listener - 运行订阅者节点

参考资源

概述

LIO-SAM (Lidar Inertial Odometry and Mapping) 是一个紧耦合的激光雷达惯性里程计框架,集成了 IMU 预积分和 GPS 数据,适用于机器人建图和定位。

环境要求

组件 版本 下载地址
Ubuntu 18.04+ -
ROS Melodic -
gtsam 4.0.2 GitHub
Eigen 3.3.7 GitLab
LIO-SAM 最新版 GitHub

安装步骤

1. 安装系统依赖

1
2
3
4
5
# 更新系统包列表
sudo apt update

# 安装必要的开发工具和依赖库
sudo apt install -y build-essential cmake libboost-all-dev

2. 安装 Eigen 库

1
2
3
4
5
6
7
8
9
10
11
12
# 下载并解压 Eigen
wget https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.tar.gz
tar -zxvf eigen-3.3.7.tar.gz

# 编译安装
cd eigen-3.3.7
mkdir build && cd build
sudo cmake ..
sudo make install

# 创建符号链接以便系统找到 Eigen 头文件
sudo cp -r /usr/local/include/eigen3/Eigen/ /usr/local/include/

3. 安装 ROS Melodic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 设置 ROS 软件源(清华镜像)
sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ $DISTRIB_CODENAME main" > /etc/apt/sources.list.d/ros-latest.list'

# 添加 ROS 密钥
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654

# 更新包列表并安装 ROS
sudo apt update
sudo apt-get install -y ros-melodic-desktop-full

# 安装 ROS 开发工具
sudo apt-get install -y ros-melodic-rqt* python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential

# 初始化 rosdep
sudo rosdep init
rosdep update

4. 配置 ROS 环境

1
2
3
# 将 ROS 环境变量添加到 bashrc
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

5. 安装 gtsam

1
2
3
4
5
6
7
8
9
10
11
12
# 克隆 gtsam 仓库
git clone https://github.com/borglab/gtsam.git
cd gtsam

# 切换到 4.0.2 版本
git checkout 4.0.2

# 编译安装
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install

6. 安装 LIO-SAM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src

# 克隆 LIO-SAM
git clone https://github.com/TixiaoShan/LIO-SAM.git

# 安装依赖
cd ~/catkin_ws
rosdep install --from-paths src --ignore-src -y

# 编译
catkin_make -j$(nproc)

# 设置工作空间环境
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

验证安装

测试 ROS 安装

1
2
3
4
5
# 启动 ROS 核心
roscore

# 新终端中检查 ROS 环境
echo $ROS_PACKAGE_PATH

测试 LIO-SAM 编译

1
2
3
4
5
# 检查 LIO-SAM 包是否存在
rospack find lio_sam

# 尝试运行节点(需要相应的启动文件)
roslaunch lio_sam run.launch

常见问题解决

1. Eigen 头文件找不到

问题:编译时出现 fatal error: Eigen/Dense: No such file or directory

解决方案

1
2
# 确保 Eigen 头文件在正确位置
sudo ln -s /usr/local/include/eigen3/Eigen /usr/local/include/Eigen

2. gtsam 版本不兼容

问题:需要特定版本的 gtsam

解决方案

1
2
3
# 确保使用 gtsam 4.0.2 版本
cd gtsam
git checkout 4.0.2

3. ROS 依赖问题

问题:缺少 ROS 包依赖

解决方案

1
2
# 安装所有缺失的依赖
rosdep install --from-paths src --ignore-src -y

使用说明

启动 LIO-SAM

1
2
3
4
5
# 启动 LIO-SAM 主要节点
roslaunch lio_sam run.launch

# 启动可视化工具
roslaunch lio_sam visualization.launch

数据播放

1
2
# 播放 bag 文件
rosbag play your_data.bag

目录结构

1
2
3
4
5
6
7
~/catkin_ws/
└── src/
└── LIO-SAM/
├── config/ # 配置文件
├── launch/ # 启动文件
├── src/ # 源代码
└── package.xml # ROS 包配置

注意事项

  1. 版本匹配:确保所有组件的版本兼容性
  2. 内存要求:编译过程需要足够的内存,建议 8GB+ RAM
  3. 网络连接:下载依赖需要稳定的网络连接
  4. 权限问题:某些操作需要 sudo 权限

后续步骤

  1. 配置参数:根据你的传感器调整 config/params.yaml
  2. 数据采集:使用你的传感器采集数据
  3. 性能调优:根据实际场景调整算法参数

参考资源

准备

建议准备一个干净、换好源的 Ubuntu 16.04 及以上版本(建议 清华源 ),本教程也适用其他 ROS1版本。

查看ubuntu 版本

1
lsb_release -a

根据自己的 Ubuntu 的版本选择 ROS 版本 (示例是 Ubuntu 18.04 所以对应ROS版本为 melodic
![[Ubuntu部署ROS/IMG-20250829234441526.png]]

ROS安装

1. 安装源

1
sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list'

2. 设置密钥

1
2
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

3. 安装

1
2
3
4
sudo apt update
sudo apt install ros-melodic-desktop
# 其他版本替换对应的版本(例如 noetic )
# sudo apt install ros-noetic-desktop

4. 配置环境变量

1
2
3
4
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc #使环境生效
# 替换对应版本同上
# echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc

5. 配置rosdep

在使用许多 ROS 工具之前,需要初始化 rosdep,有些功能包源码编译需要rosdep 来安装这些系统依赖项,不配置也不影响ros使用,所以后面需要时再来配置也可以。 rosdep 请求的文件都放在 github 上的, 推荐使用代理。

1
2
3
4
5
6
7
8
9
10

# 安装依赖
sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential
# 对于Ubuntu20
# sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential

# 初始化
sudo rosdep init
rosdep update

测试

1
2
3
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key

参考
官方文档(melodic)
ubuntu18.04安装ROS Melodic(最详细配置)-CSDN博客
基于Ubuntu18.04的ROS Melodic环境详细配置(含各种大坑及填坑)
[ROS 系列学习教程] ROS与操作系统版本对应关系_ros版本-CSDN博客

1. K3s指定集群管理IP

在k3s.service中添加启动参数

1
--advertise-address=<192.168.x.x>

详细参考官方文档以及 K3S安装

查看当前Context

1
kubectl config current-context

2. 配置集群信息

查看context列表

1
kubectl config get-contexts

输出中带有*的Context表示当前活动的Context

切换到指定Context

1
2
3
4
5
kubectl config use-context <context_name>

# 示例:切换到dev-ctx
kubectl config use-context dev-ctx

在指定Context中执行命令,一般用于临时使用

1
2
3
4
kubectl  --context=<context_name> <exec_cmd>

# 示例:在dev-ctx下执行get pods
kubectl --context=dev-ctx get pods

3. 合并配置文件

在 Kubernetes 环境中,使用 kubectl 管理多个集群非常常见。通过配置 kubeconfig 文件,可以轻松切换和管理多个集群。以下是实现方法的详细步骤。

方法 1: 合并多个配置文件

  • 准备配置文件 假设已有两个集群的配置文件:_/.kube/config1_ 和 _/.kube/config2_。

  • 合并配置文件 使用以下命令将多个配置文件合并为一个:

KUBECONFIG=/.kube/config1:/.kube/config2 kubectl config view –merge –flatten > ~/.kube/config

  • 验证合并结果 查看合并后的配置:

kubectl config view

方法 2: 配置环境变量

  • 设置环境变量 将多个配置文件路径添加到 KUBECONFIG 环境变量中:

export KUBECONFIG=/.kube/config:/.kube/test-config

  • 验证配置 执行以下命令查看所有集群信息:

kubectl config get-contexts

方法 3: 手动编辑配置文件

  • 打开配置文件 编辑 ~/.kube/config 文件,将其他集群的 cluster_、_context 和 user 信息粘贴到现有配置中。

  • 格式示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

apiVersion: v1

clusters:

- cluster:

server: https://127.0.0.1:6443

name: cluster1

- cluster:

server: https://192.168.0.1:6443

name: cluster2

contexts:

- context:

cluster: cluster1

user: user1

name: context1

- context:

cluster: cluster2

user: user2

name: context2

current-context: context1

切换集群上下文

  • 查看当前上下文:

    1
    kubectl config current-context
  • 切换到其他上下文:

    1
    2

    kubectl config use-context <context_name>

    最佳实践

  • 使用合并或环境变量的方法更高效,避免手动编辑出错。

  • 定期备份 kubeconfig 文件,防止误操作导致数据丢失。

  • 确保每个集群的访问凭证和权限正确无误。

通过以上方法,您可以轻松管理多个 Kubernetes 集群,提高运维效率。

大家好!在 云原生 的世界里,和 Kubernetes 打交道是家常便饭。如果我们像我一样,需要同时管理多个 Kubernetes 集群——比如一个用于严谨发布的 生产环境 ,一个用于大胆实验的 测试环境 ,甚至还有本地开发环境——那么高效、安全地在它们之间切换就成了必备技能。

很多朋友(包括我自己有时也会!)可能会因为一段时间没用而忘记 kubectl 中那些用于切换配置的命令。别担心,这很正常!今天,我们就来系统地回顾一下 kubectl 配置管理的核心概念—— 上下文(Context) ,以及如何利用它在不同集群间自如切换。

核心概念:kubeconfig 文件与上下文(Context)

kubectl 的所有配置信息都存储在一个或多个 YAML 文件中,默认情况下是 $HOME/.kube/config 。这个文件我们通常称为 kubeconfig 文件。把它想象成我们的 Kubernetes “护照”,里面记录了我们能访问哪些集群,用什么身份访问。

一个 kubeconfig 文件通常包含三个主要部分:

  1. Clusters(集群) :定义了我们要连接的 Kubernetes 集群的信息,比如 API Server 的地址和集群的 CA 证书。
  2. Users(用户) :定义了访问集群所使用的凭证,可能是用户名/密码、Token 或客户端证书。
  3. Contexts(上下文) :这是连接 集群用户 的桥梁。一个 Context 定义了使用哪个 User 凭证去访问哪个 Cluster。

关键点: 我们可以通过切换 Context 来改变 kubectl 当前操作的目标集群和使用的身份。

管理 kubeconfig 的常用 kubectl config 命令

kubectl 提供了一套 config 子命令来帮助我们查看和管理 kubeconfig 文件。以下是几个最核心、最常用的命令:

1. 查看当前配置:kubectl config view

这个命令会显示我们当前的 kubeconfig 文件内容(或者合并后的内容,如果我们配置了多个文件)。它会隐藏敏感信息(如证书和 Token 的具体内容),非常适合快速检查配置概览。

1
2
kubectl config view
bash1

如果我们想看某个特定 Context 的详细信息,可以加上 --context 参数:

1
2
3
# 查看名为 'prod-cluster' 的 context 细节
kubectl config view --context=prod-cluster
bash12
2. 列出所有可用的上下文:kubectl config get-contexts

这是 最常用 的命令之一,它会列出我们在 kubeconfig 文件中定义的所有 Context。当前正在使用的 Context 会在名称前用星号 * 标记。

1
2
3
4
5
6
7
kubectl config get-contexts
# 输出示例:
# CURRENT NAME CLUSTER AUTHINFO NAMESPACE
# * test-cluster kubernetes-test user-test
# prod-cluster kubernetes-prod user-prod production
# docker-desktop docker-desktop docker-desktop
bash123456

从上面的输出可以清晰地看到:

  • 当前激活的 Context 是 test-cluster
  • 还有名为 prod-clusterdocker-desktop 的 Context 可供切换。
3. 查看当前使用的上下文:kubectl config current-context

如果我们只想快速确认当前 kubectl 命令会作用于哪个 Context(哪个集群),这个命令最直接:

1
2
3
4
kubectl config current-context
# 输出示例:
# test-cluster
bash123
4. 切换上下文:kubectl config use-context

这绝对是 核心中的核心 !当我们需要将 kubectl 的操作目标从一个集群切换到另一个集群时,就使用这个命令。

假设我们想从当前的 test-cluster 切换到 prod-cluster

1
2
3
4
kubectl config use-context prod-cluster
# 输出示例:
# Switched to context "prod-cluster".
bash123

切换成功后,我们可以再次使用 kubectl config current-contextkubectl config get-contexts 来验证当前上下文是否已更改。

1
2
3
4
5
6
7
8
9
10
11
kubectl config current-context
# 输出示例:
# prod-cluster

kubectl config get-contexts
# 输出示例:
# CURRENT NAME CLUSTER AUTHINFO NAMESPACE
# test-cluster kubernetes-test user-test
# * prod-cluster kubernetes-prod user-prod production
# docker-desktop docker-desktop docker-desktop
bash12345678910

现在,所有后续的 kubectl 命令(如 kubectl get pods, kubectl apply -f ... 等)都会默认发送到 prod-cluster 所定义的集群,并使用 user-prod 的身份进行认证。

实践场景:在生产和测试集群间切换

假设我们的 kubeconfig 文件中已经配置好了代表生产环境和 测试环境 的 Context,可能分别命名为 productiontesting

我们的日常操作流程可能是这样的:

  1. 检查当前在哪:
    1
    2
    kubectl config current-context
    bash1
    或者看列表:
    1
    2
    kubectl config get-contexts
    bash1
  2. 需要操作测试环境:
    1
    2
    3
    4
    5
    6
    kubectl config use-context testing
    # 验证一下(可选但推荐)
    kubectl config current-context
    # 现在可以对测试环境执行操作了
    kubectl get pods -n test-namespace
    bash12345
  3. 需要紧急处理生产环境问题:
    1
    2
    3
    4
    5
    6
    kubectl config use-context production
    # 验证一下
    kubectl config current-context
    # 操作生产环境(请务必小心!)
    kubectl get deployment -n critical-app
    bash12345
  4. 完成生产环境操作,切回测试环境继续工作:
    1
    2
    kubectl config use-context testing
    bash1

提升效率的小贴士

  1. 清晰命名 Context :给我们的 Context 起一个能清晰表明环境和用途的名字,比如 gke-prod-eu, eks-dev-us, local-minikube 等。避免使用模糊不清的名字。
  2. 使用 Shell 别名 :很多人喜欢为 kubectl 设置别名,比如 alias k=kubectl 。这样我们的命令可以更短: k config get-contexts, k config use-context my-context
  3. 考虑使用辅助工具 :社区有一些流行的小工具可以让我们更方便地切换 Context 和 Namespace,例如:
    • kubectx (用于切换 Context)
    • kubens (用于切换 Namespace)
      这些工具通常提供交互式选择或更简洁的命令,可以显著提高效率。可以通过包管理器(如 Homebrew, apt, yum)或直接下载二进制文件来安装它们。
  4. 注意 kubeconfig 文件的安全性kubeconfig 文件包含了访问集群的凭证,务必妥善保管,不要泄露给未授权的人员。

总结

管理多个 Kubernetes 集群配置并不复杂,核心就在于理解和运用 kubeconfig 文件中的 Context 概念。通过掌握 kubectl config 的几个关键子命令:

  • view: 查看配置概览
  • get-contexts: 列出所有可用上下文
  • current-context: 显示当前激活的上下文
  • use-context <context-name>: 切换到指定的上下文

我们就能轻松地在不同的 Kubernetes 环境(如生产和测试)之间安全、高效地切换了。希望这篇回顾能帮我们重新找回操作 kubectl 多集群配置的熟悉感!

0%