问题背景

作为开发者,我们经常需要在终端(Terminal)中使用各种 CLI 工具,例如 gitnpmcurl 或者最近热门的 gemini-cli

你可能遇到过这种反直觉的情况:
明明电脑上的代理软件(Clash/v2rayN)已经开启了“全局模式”,浏览器访问 Google/GitHub 毫无压力,但只要在终端运行命令,依然报错 Connection timed out


Part 1: Windows 本地终端 (PowerShell/CMD)

1.1 原理:此“代理”非彼“代理”

当我们开启代理软件的“系统代理”时,实际上修改的是 Windows 的 IE 代理设置。这只对浏览器(Edge, Chrome)生效。
而大多数命令行工具(CLI)不读系统设置,它们只认特定的环境变量:HTTP_PROXYHTTPS_PROXY

1.2 解决方案:配置永久环境变量

为了让所有终端工具(包括 VS Code 内置终端)自动走代理,建议将配置写入系统环境变量。

  1. 获取代理端口:查看你的代理软件设置,找到 HTTP 端口(例如 Clash 默认为 7890,v2rayN 默认为 10809)。
  2. 打开设置:按 Win + S,搜索 “编辑系统环境变量”
  3. 添加变量:点击“环境变量”,在 “用户变量” 区域新建以下两项:
    • 变量名: HTTP_PROXY | 变量值: http://127.0.0.1:7890
    • 变量名: HTTPS_PROXY | 变量值: http://127.0.0.1:7890
  4. 重启终端:关闭并重新打开 PowerShell,配置即刻生效。

SystemPropertiesAdvanced_hpOEU6Pxj2.png

1.3 验证

1
2
3
curl -I [https://www.google.com](https://www.google.com)
# 预期输出: HTTP/1.1 200 OK

Part 3: 常见工具的独立配置

有些工具(如 Git, NPM)有时会忽略环境变量,或者需要单独设置。

3.1 Git

如果 git clone 依然很慢,手动指定代理:

1
2
3
4
5
6
7
# 设置
git config --global http.proxy [http://127.0.0.1:7890](http://127.0.0.1:7890)
git config --global https.proxy [http://127.0.0.1:7890](http://127.0.0.1:7890)

# 取消
# git config --global --unset http.proxy

(注:在 WSL 2 中,将 127.0.0.1 替换为 $hostip)

3.2 NPM / Yarn

1
2
3
npm config set proxy [http://127.0.0.1:7890](http://127.0.0.1:7890)
npm config set https-proxy [http://127.0.0.1:7890](http://127.0.0.1:7890)

3.3 Gemini CLI

如果你在使用 Google 的 gemini-cli,它强依赖 HTTPS_PROXY 环境变量。只要 Part 1 或 Part 2 配置正确,通常无需额外设置。如果报错,请检查 API Key 是否有效,或尝试强制指定:

1
2
3
# 强制指定代理运行
export HTTPS_PROXY=[http://127.0.0.1:7890](http://127.0.0.1:7890); gemini prompt "Hello"


总结

  • Windows 终端:设置永久用户环境变量 (HTTP_PROXY)。
  • WSL 2:开启代理软件的 Allow LAN,并在 .zshrc 中使用脚本动态指向 Host IP。
  • 核心原则:CLI 工具不走系统代理,必须显式喂给它环境变量。

WSL2 使用 Windows 主机代理

1. 问题背景

在使用 Windows 10 的 WSL2 (Windows Subsystem for Linux 2) 进行开发时,经常会遇到一个痛点:WSL2 无法直接使用 Windows 主机上已经配置好的代理软件

当你打开 WSL 终端时,可能会看到如下提示:

wsl: A localhost proxy configuration was detected but not mirrored into WSL. WSL in NAT mode does not support localhost proxies.

image-compressed.png

为什么会这样?

这涉及到底层的网络架构差异:

  • WSL 1:与 Windows 共享网络栈,localhost 指向同一个接口。
  • WSL 2 (NAT 模式):本质上是一个运行在 Hyper-V 里的轻量级虚拟机。它拥有独立的虚拟网卡和 IP 地址。
    • 在 WSL2 里访问 localhost,访问的是虚拟机自己,而不是外面的 Windows。
    • Windows 主机对于 WSL2 来说,类似于局域网里的“网关”。

注意:Windows 11 支持“镜像网络模式 (Mirrored Mode)”可以完美解决此问题,但 Windows 10 用户只能通过 NAT 穿透的方式来解决。


2. 解决方案核心步骤

我们要做的只有两件事:

  1. Windows 端:允许代理软件接受来自“局域网”的连接。
  2. WSL 端:动态获取 Windows 主机的 IP,并将流量转发过去。

第一步:配置 Windows 代理软件 (Allow LAN)

WSL2 对 Windows 来说是外部设备,所以必须开启“允许局域网连接”。

  1. 打开你的代理软件(Clash, v2rayN 等)。
  2. 找到 Allow LAN (允许局域网连接) 开关并开启
  3. 记下软件的 HTTP/Port 端口号。
    • Clash 默认为 7890
    • v2rayN 默认为 10809

提示:如果开启后仍然不通,请检查 Windows 防火墙,确保该代理软件在“允许应用通过防火墙”的列表中,或者暂时关闭防火墙测试。

image-compressed.png

第二步:配置 WSL 环境变量 (自动脚本)

由于 WSL2 每次重启后,宿主机的虚拟 IP 可能会变,我们不能写死 IP 地址。我们需要一段脚本来自动提取 IP。

在 WSL 终端中操作:

  1. 编辑 Shell 配置文件
    如果你用的是 zsh (Ubuntu 默认推荐):

    1
    nano ~/.zshrc

    (如果你用的是 bash,请编辑 ~/.bashrc)

  2. 添加自动配置脚本
    在文件末尾追加以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # ====================================================
    # WSL2 Proxy Auto-Configuration
    # ====================================================

    # 1. 获取 Windows 宿主机在虚拟网络中的 IP 地址
    # 原理:读取路由表,找到 default 路由,提取其网关 IP
    export hostip=$(ip route show | grep default | awk '{print $3}')

    # 2. 设置代理端口 (请根据你实际使用的软件修改端口号,例如 7890 或 10809)
    export PROXY_PORT=7890

    # 3. 设置环境变量
    export https_proxy="http://${hostip}:${PROXY_PORT}"
    export http_proxy="http://${hostip}:${PROXY_PORT}"
    export all_proxy="socks5://${hostip}:${PROXY_PORT}" #如果软件支持Socks5

    # 4. (可选) 调试信息:每次打开终端显示当前代理地址
    # echo "-> Proxy points to Windows Host: ${hostip}:${PROXY_PORT}"

    # ====================================================

    image-compressed.png

  3. 保存并生效

    • Ctrl + O 保存,Ctrl + X 退出编辑器。
    • 执行命令使配置生效:
      1
      source ~/.zshrc

3. 验证连接

配置完成后,使用 curl 命令测试连通性(建议测试 Google 或 GitHub):

curl -I [https://www.google.com](https://www.google.com)

Go 语言入门:LeetCode 常用数据结构

本文档旨在帮助新手快速掌握 Go 语言中常用的基础语法和数据结构。

Happy Coding!

基础语法

官方入门Go语言之旅

在深入数据结构之前,我们先快速了解一下 Go 的一些基础语法。

标准输出

Go 的 fmt 包提供了丰富的函数用于格式化和输出数据。fmt.Printlnfmt.Printf 是最常用的两个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
)

func main() {
a := 10

// 输出:10
fmt.Println(a)

// 可以串联输出
// 输出:Hello, World!
fmt.Println("Hello" + ", " + "World!")

s := "abc"
// 输出:abc 10
fmt.Println(s, a)

// 格式化输出
// 输出:abc 10
fmt.Printf("%s %d\n", s, a)
}

条件判断

Go 的 if-else 结构与其它语言类似,但条件不需要用括号括起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

func main() {
a := 10

if a > 5 {
fmt.Println("a > 5")
} else if a == 5 {
fmt.Println("a == 5")
} else {
fmt.Println("a < 5")
}
// 输出:a > 5
}

循环

Go 只有一个循环关键字 for,但它能实现多种循环方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
)

func main() {
// 输出:0 1 2 3 4
for i := 0; i < 5; i++ {
fmt.Print(i, " ")
}
fmt.Println()

num := 100
// 输出:100 50 25 12 6 3 1
for num > 0 {
fmt.Print(num, " ")
num /= 2
}
fmt.Println()
}

核心数据结构

掌握以下数据结构是刷 LeetCode 的基础。

动态数组(切片)

Go 的切片(Slice)是对底层数组的封装,提供了更强大、灵活的动态数组功能。

初始化

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
37
package main

import (
"fmt"
)

func main() {
// 初始化一个空的切片 nums
var nums []int

// 初始化一个大小为 7 的切片 nums,元素值默认都为 0
nums = make([]int, 7)

// 初始化一个包含元素 1, 3, 5 的切片 nums
nums = []int{1, 3, 5}

// 初始化一个大小为 7 的切片 nums,其值全都为 2
nums = make([]int, 7)
for i := range nums {
nums[i] = 2
}

fmt.Println(nums)

// 初始化一个大小为 3 * 3 的布尔切片 dp,其中的值都初始化为 true
var dp [][]bool
dp = make([][]bool, 3)
for i := 0; i < len(dp); i++ {
row := make([]bool, 3)
for j := range row {
row[j] = true
}
dp[i] = row
}

fmt.Println(dp)
}

常用操作

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
"fmt"
)

func main() {
n := 10
// 初始化切片,大小为 10,元素值都为 0
nums := make([]int, n)

// 输出:false
fmt.Println(len(nums) == 0)

// 输出:10
fmt.Println(len(nums))

// 在切片尾部插入一个元素 20
// append 函数会返回一个新的切片,所以需要将返回值重新赋值给 nums
nums = append(nums, 20)
// 输出:11
fmt.Println(len(nums))

// 得到切片最后一个元素
// 输出:20
fmt.Println(nums[len(nums)-1])

// 删除切片的最后一个元素
nums = nums[:len(nums)-1]
// 输出:10
fmt.Println(len(nums))

// 可以通过索引直接取值或修改
nums[0] = 11
// 输出:11
fmt.Println(nums[0])

// 在索引 3 处插入一个元素 99
// ... 是展开操作符,表示将切片中的元素展开
nums = append(nums[:3], append([]int{99}, nums[3:]...)...)

// 删除索引 2 处的元素
nums = append(nums[:2], nums[3:]...)

// 交换 nums[0] 和 nums[1]
nums[0], nums[1] = nums[1], nums[0]

// 遍历切片
// 输出:0 11 99 0 0 0 0 0 0 0
for _, num := range nums {
fmt.Print(num, " ")
}
fmt.Println()
}

Go 没有内置的栈结构,但可以用切片(Slice)轻松实现。栈遵循“后进先出”(LIFO)的原则。

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
package main

import (
"fmt"
)

func main() {
// 初始化一个空的整型栈 s
var s []int

// 向栈顶(切片末尾)添加元素
s = append(s, 10)
s = append(s, 20)
s = append(s, 30)

// 检查栈是否为空,输出:false
fmt.Println(len(s) == 0)

// 获取栈的大小,输出:3
fmt.Println(len(s))

// 获取栈顶元素,输出:30
fmt.Println(s[len(s)-1])

// 删除栈顶元素
s = s[:len(s)-1]

// 输出新的栈顶元素:20
fmt.Println(s[len(s)-1])
}

队列

队列遵循“先进先出”(FIFO)的原则。Go 的标准库 container/list 提供了一个双向链表,可以高效地实现队列。

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
package main

import (
"container/list"
"fmt"
)

func main() {
// 初始化一个空的整型队列 q
q := list.New()

// 在队尾添加元素
q.PushBack(10)
q.PushBack(20)
q.PushBack(30)

// 检查队列是否为空,输出:false
fmt.Println(q.Len() == 0)

// 获取队列的大小,输出:3
fmt.Println(q.Len())

// 获取队列的队头元素
// 输出:10
front := q.Front().Value.(int)
fmt.Println(front)

// 删除队头元素
q.Remove(q.Front())

// 输出新的队头元素:20
newFront := q.Front().Value.(int)
fmt.Println(newFront)
}

哈希表(Map)

Go 内置了 map 类型来实现哈希表,用于存储键值对。

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
)

func main() {
// 初始化一个空的哈希表 hashmap
var hashmap map[int]string
hashmap = make(map[int]string)

// 初始化一个包含一些键值对的哈希表 hashmap
hashmap = map[int]string{
1: "one",
2: "two",
3: "three",
}

fmt.Println(hashmap)
}

常用操作

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main

import (
"fmt"
)

func main() {
// 初始化哈希表
hashmap := make(map[int]string)
hashmap[1] = "one"
hashmap[2] = "two"
hashmap[3] = "three"

// 检查哈希表是否为空,输出:false
fmt.Println(len(hashmap) == 0)

// 获取哈希表的大小,输出:3
fmt.Println(len(hashmap))

// 查找指定键值是否存在
// 输出:Key 2 -> two
if val, exists := hashmap[2]; exists {
fmt.Println("Key 2 ->", val)
} else {
fmt.Println("Key 2 not found.")
}

// 获取指定键对应的值,若不存在会返回空字符串
// 输出:
fmt.Println(hashmap[4])

// 插入一个新的键值对
hashmap[4] = "four"

// 获取新插入的值,输出:four
fmt.Println(hashmap[4])

// 删除键值对
delete(hashmap, 3)

// 检查删除后键 3 是否存在
// 输出:Key 3 not found.
if val, exists := hashmap[3]; exists {
fmt.Println("Key 3 ->", val)
} else {
fmt.Println("Key 3 not found.")
}

// 遍历哈希表
// 输出(顺序可能不同):
// 1 -> one
// 2 -> two
// 4 -> four
for key, value := range hashmap {
fmt.Printf("%d -> %s\n", key, value)
}
}

哈希集合 (Set)

Go 没有内置的集合类型,但可以用 map[T]struct{} 巧妙地实现。struct{} 是一个空结构体,不占用任何内存空间,非常适合用作集合的值。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
"fmt"
)

func main() {
// 初始化一个包含一些元素的哈希集合 hashset
hashset := map[int]struct{}{
1: {},
2: {},
3: {},
4: {},
}

// 检查哈希集合是否为空,输出:false
fmt.Println(len(hashset) == 0)

// 获取哈希集合的大小,输出:4
fmt.Println(len(hashset))

// 查找指定元素是否存在
// 输出:Element 3 found.
if _, exists := hashset[3]; exists {
fmt.Println("Element 3 found.")
} else {
fmt.Println("Element 3 not found.")
}

// 插入一个新的元素
hashset[5] = struct{}{}

// 删除一个元素
delete(hashset, 2)
// 输出:Element 2 not found.
if _, exists := hashset[2]; exists {
fmt.Println("Element 2 found.")
} else {
fmt.Println("Element 2 not found.")
}

// 遍历哈希集合
// 输出(顺序可能不同):
// 1
// 3
// 4
// 5
for element := range hashset {
fmt.Println(element)
}
}

双向链表

Go 的 container/list 包提供了一个功能完备的双向链表实现。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package main

import (
"container/list"
"fmt"
)

func main() {
// 初始化链表
lst := list.New()
lst.PushBack(1)
lst.PushBack(2)
lst.PushBack(3)
lst.PushBack(4)
lst.PushBack(5)

// 检查链表是否为空,输出:false
fmt.Println(lst.Len() == 0)

// 获取链表的大小,输出:5
fmt.Println(lst.Len())

// 在链表头部插入元素 0
lst.PushFront(0)
// 在链表尾部插入元素 6
lst.PushBack(6)

// 获取链表头部和尾部元素,输出:0 6
front := lst.Front().Value.(int)
back := lst.Back().Value.(int)
fmt.Println(front, back)

// 删除链表头部元素
lst.Remove(lst.Front())
// 删除链表尾部元素
lst.Remove(lst.Back())

// 在链表中插入元素
// 移动到第三个位置
third := lst.Front().Next().Next()
lst.InsertBefore(99, third)

// 删除链表中某个元素
second := lst.Front().Next()
lst.Remove(second)

// 遍历链表
// 输出:1 99 3 4 5
for e := lst.Front(); e != nil; e = e.Next() {
fmt.Print(e.Value.(int), " ")
}
fmt.Println()
}

Go 语言开发环境安装与配置

本文档将引导你完成 Go 语言的下载、安装和基础环境配置,帮助你快速搭建一个高效的 Go 开发环境。

第 1 步:下载 Go

首先,我们需要从 Go 语言的官方网站下载安装包。

请根据你的操作系统选择对应的安装包。通常:

  • Windows: 选择 .msi 后缀的安装程序。
  • macOS: 选择 .pkg 后缀的安装程序。
  • Linux: 选择 .tar.gz 后缀的压缩包。

firefox_TdbY6YfmzL.png

第 2 步:安装 Go

Windows

  1. 下载 .msi 安装文件后,双击运行。
  2. 遵循安装向导的提示点击 “Next”。
  3. Go 将默认安装在 C:\Program Files\Go 目录下,并且安装程序会自动将 C:\Program Files\Go\bin 添加到你的系统环境变量 Path 中。
  4. 点击 “Install” 完成安装。

macOS

  1. 下载 .pkg 安装文件后,双击运行。
  2. 遵循安装向导的提示完成安装。
  3. Go 将默认安装在 /usr/local/go 目录下,安装程序会自动将 /usr/local/go/bin 添加到你的系统环境变量 PATH 中。

Linux

我们提供两种安装方法:

方法一:使用包管理器(最便捷)

Go 可以在大多数 Linux 发行版上通过其各自的包管理器安装。

  • Debian/Ubuntu 及其衍生版 (使用 apt):

    1
    2
    3
    4
    # 更新你的包列表
    sudo apt update
    # 安装 Go
    sudo apt install golang-go
  • RHEL/CentOS/Fedora 及其衍生版 (使用 yumdnf):

    1
    2
    3
    4
    # 更新你的包列表
    sudo yum update # 或者 sudo dnf update
    # 安装 Go
    sudo yum install golang # 或者 sudo dnf install golang

    注意: 包管理器中的 Go 版本可能不是最新的。你可以通过 go version 命令检查安装的版本。如果需要最新版本,请使用方法二。

方法二:手动安装(推荐,可安装最新版)

这种方法可以确保你安装的是 Go 官方发布的最新版本。

  1. 下载 .tar.gz 压缩包,并将其解压到 /usr/local 目录下。你可以使用以下命令(请将 go1.x.x.linux-amd64.tar.gz 替换为你下载的文件名):
    1
    2
    3
    4
    # 如果之前安装过,先删除旧版本
    sudo rm -rf /usr/local/go
    # 解压到 /usr/local
    sudo tar -C /usr/local -xzf go1.x.x.linux-amd64.tar.gz
  2. 将 Go 的二进制文件目录添加到环境变量 PATH 中。编辑你的 shell 配置文件(如 ~/.bashrc, ~/.zshrc~/.profile),在文件末尾添加以下这行:
    1
    export PATH=$PATH:/usr/local/go/bin
  3. 保存文件后,在终端中执行以下命令使配置生效:
    1
    2
    source ~/.bashrc 
    # 或者 source ~/.zshrc, source ~/.profile

第 3 步:验证安装

打开一个新的终端(Windows 用户请打开命令提示符 cmdPowerShell),然后输入以下命令:

1
go version

如果安装成功,你将看到类似以下的输出,显示了安装的 Go 版本号:

1
go version go1.21.4 windows/amd64

WindowsTerminal_moS2GhqaHd.png

第 4 步:配置你的 Go 环境

Go 语言使用一些环境变量来进行配置。你可以通过 go env 命令查看所有 Go 相关的环境变量。

GOPATH (工作区)

在现代 Go 开发中,GOPATH 已经不是必须的了。

自从 Go 1.11 引入 Go Modules 之后,你的项目可以存放在任何位置,不再需要强制放在 GOPATH 中。

那为什么还需要了解它?

GOPATH 现在的主要作用是作为存放 全局工具 的位置。当你运行 go install 安装一个第三方工具时(例如 gopls),这个工具的二进制文件会被默认存放在 $GOPATH/bin 目录下。

  • 如果你不设置 GOPATH:Go 会自动使用一个默认目录(在 Linux/macOS 上是 $HOME/go,在 Windows 上是 %USERPROFILE%\go)。
  • 如果你设置了 GOPATH:你可以自定义这些工具的安装位置。

建议的操作:
为了能够方便地在任何地方运行这些安装好的工具,建议将 GOPATHbin 目录添加到系统的 PATH 环境变量中。你可以通过 go env GOPATH 命令查看你当前的 GOPATH 路径。

GOPROXY (模块代理)

如果你在中国大陆,由于网络原因,可能无法直接从 golang.org 下载 Go 模块。配置一个国内的代理服务器可以解决这个问题。

推荐使用 goproxy.cn。在你的终端执行以下命令:

1
2
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

这会将你的模块下载请求代理到国内的服务器,direct 表示如果代理失败则尝试直连。

第 5 步:设置你的代码编辑器

一个好的代码编辑器能极大提升开发效率。Visual Studio Code (VS Code) 是目前最受欢迎的 Go 开发编辑器之一。

  1. 安装 VS Code: 从 https://code.visualstudio.com/ 下载并安装。
  2. 安装 Go 扩展:
    • 打开 VS Code。
    • 点击左侧边栏的 扩展 图标 (Extensions)。
    • 在搜索框中输入 “Go”。
    • 找到由 Microsoft 发布的官方 Go 扩展并点击 Install
  3. 安装 Go 工具: 安装完扩展后,VS Code 可能会在右下角提示你安装一些必要的 Go 工具(如 gopls, dlv 等)。点击 Install All 即可。你也可以随时通过按 Ctrl+Shift+P (或 Cmd+Shift+P),然后输入 Go: Install/Update Tools 来手动安装或更新这些工具。

第 6 步:编写你的第一个 Go 程序

现在,你的环境已经准备就绪。让我们来编写一个经典的 “Hello, World!” 程序来测试一下。

  1. 创建一个新的项目目录,例如 hello-go
    1
    2
    mkdir hello-go
    cd hello-go
  2. 初始化 Go 模块。这会创建一个 go.mod 文件来管理你项目的依赖。
    1
    go mod init example/hello
  3. 创建一个名为 main.go 的文件,并输入以下代码:
    1
    2
    3
    4
    5
    6
    7
    package main

    import "fmt"

    func main() {
    fmt.Println("Hello, World!")
    }
  4. 在终端中运行你的程序:
    1
    go run .
  5. 你应该会在终端看到输出:
    1
    Hello, World!

附录:在 WSL 中进行 Go 开发

对于 Windows 用户,使用 Windows Subsystem for Linux (WSL) 是一个非常流行且强大的选择。它让你可以在 Windows 系统上无缝地运行一个真实的 Linux 环境,从而获得与原生 Linux 一致的开发体验。

为什么使用 WSL?

  • 一致的环境: 确保你的开发环境与线上服务器(通常是 Linux)的环境保持一致。
  • 性能: 对于大量文件操作和并发任务,WSL 2 的文件系统性能非常高。
  • 工具链: 可以无缝使用 Linux 生态下的各种开发工具。

在 WSL 中配置 Go 和 VS Code

  1. 安装 WSL 和 Linux 发行版:

    • 打开 PowerShell 或 Windows 命令提示符,并以管理员身份运行。
    • 输入 wsl --install。这个命令会自动安装 WSL 并默认安装 Ubuntu 发行版。你也可以从 Microsoft Store 中选择其他发行版(如 Debian)。
  2. 在 WSL 中安装 Go:

    • 打开你的 WSL 终端(例如,在开始菜单中搜索 “Ubuntu”)。
    • 我们提供两种安装方法:

    方法一:使用包管理器(最便捷)

    这是最简单的方法。如果你的 Linux 发行版是 Ubuntu 或 Debian,可以直接使用 apt 来安装。

    1
    2
    3
    4
    # 更新你的包列表
    sudo apt update
    # 安装 Go
    sudo apt install golang-go

    注意: 包管理器中的 Go 版本可能不是最新的。你可以通过 go version 命令检查安装的版本。如果需要最新版本,请使用方法二。

    方法二:手动安装(可安装最新版)

    这种方法可以确保你安装的是 Go 官方发布的最新版本。

    • 按照文档开头 第 2 步:安装 Go 中的 Linux 部分的指引,在 WSL 环境中下载并安装 Go。
    • 这种方式需要你手动在 ~/.bashrc~/.zshrc 文件中配置 PATH 环境变量。

    无论使用哪种方法安装,都建议按照 第 4 步 的说明配置 GOPROXY 以优化模块下载速度。

  3. 安装 VS Code 的 WSL 扩展:

    • 在你的 Windows 系统 上打开 VS Code。
    • 前往扩展市场,搜索并安装名为 “WSL” 的扩展(由 Microsoft 发布)。
  4. 开始开发:

    • 关闭所有 VS Code 窗口。
    • 打开你的 WSL 终端
    • cd 到你存放 Go 项目的目录(例如 cd ~ 进入主目录)。
    • 创建一个项目目录,例如 mkdir my-wsl-project && cd my-wsl-project
    • 在这个目录下,输入 code . 命令。

    这个命令会触发 VS Code 在 Windows 上启动,并自动通过 WSL 扩展连接到你当前的 Linux 目录。你会看到 VS Code 左下角显示 WSL: Ubuntu(或你的发行版名称),这表明你已经连接成功。

  5. 工作流程:

    • 编辑器: VS Code UI 运行在 Windows 上,响应流畅。
    • 终端: VS Code 中打开的终端(Ctrl+`)现在是一个 **WSL 终端**,你可以在这里直接运行 go run, git 等 Linux 命令。
    • 工具和扩展: Go 扩展和所有相关的 Go 工具(gopls 等)都会被安装在 WSL 环境中,而不是 Windows 上。VS Code 会自动处理好这一切。你可能需要像在原生 VS Code 中一样,在首次连接时重新为 WSL 环境安装一次 Go 扩展。

现在,你就可以在享受 Windows 图形界面的同时,获得一个完整的 Linux Go 开发环境了。

Linux RHCSA专题结语

非常感谢每一位读者,能耐心跟随本系列教程,一直走到最后。我们已经完成了RH124和RH134的所有内容。

从最初的安装与基础命令,到后来的存储管理、网络服务、安全配置,乃至容器技术,我们共同走过了一段漫长而充实的学习之路。

在此,我也想坦诚地表达一丝歉意。Linux 世界浩瀚如海,由于篇幅和精力所限,本系列在许多地方无法做到尽善尽美,对于一些高级主题的探讨也只是浅尝辄止。若有疏漏或不够详尽之处,还请多多包涵。

编写此系列教程的初衷,是希望能够简单记录一下学习,并且为一些新手提供一张清晰的学习路线图,并为未来的深入探索打下坚实的基础。真正的精通源于持续不断的实践与好奇心,希望本教程能成为您探索之路的起点。

如果你在寻求认证,现在的知识已经足够你进行准备RHCSA的认证了。

再次感谢您的阅读与支持。我应该会继续不定期更新一些关于RHCSA认证相关的练习题。

下一个专题应该是Golang,期待和你一起学习。

最后祝您在 Linux 的世界里,玩得开心!

Linux 使用 Podman 运行容器

本章将介绍容器的基本概念,以及如何在 RHEL 系统上使用 podman 工具来运行和管理容器。

1. 容器技术简介

什么是容器?

“容器”这个术语和概念源于海运集装箱。

  • 这些集装箱在城市与城市、国家与国家之间运输。
  • 无论你走到世界哪个角落,你都会发现这些集装箱的尺寸完全相同…… 你知道为什么吗???
  • 因为世界各地的码头、卡车、轮船和仓库都是为了方便运输和存储这些标准化的集装箱而建造的。

当我们谈论 IT 领域的容器时,我们也在实现类似的目标。

请注意:

  • 容器技术主要由开发者程序员使用,他们编写代码来构建应用程序。
  • 作为系统管理员,你的工作是安装、配置和管理它们。

一个操作系统可以同时运行单个或多个容器。

容器软件:Docker 与 Podman

软件 Podman Docker
开发者 Red Hat Solomon Hykes
发布日期 2018年8月 2013年3月20日
特点 是 Docker 的一个替代品;RHEL 8 不支持 Docker;无守护进程,开源,Linux 原生。 最初的容器管理软件,通过后台守护进程工作。

2. 熟悉 Red Hat 容器技术

Red Hat 提供了一套无需容器引擎即可运行的命令行工具,包括:

  • podman: 用于直接管理 Pod、容器和容器镜像(run, stop, start, ps, attach 等)。
  • buildah: 用于构建、推送和签名容器镜像。
  • skopeo: 用于复制、检查、删除和签名镜像。
  • runc: 为 podmanbuildah 提供容器运行和构建功能。
  • crun: 一个可选的运行时,可提供更大的灵活性、控制力和安全性,用于无根 (rootless) 容器。

熟悉 podman 容器技术

当您听到“容器”时,您还应该了解以下术语:

  • images (镜像): 容器可以通过镜像创建,容器也可以被转换回镜像。
  • pods (Pod): 一组部署在同一主机上的容器。在 podman 的 Logo 中,有3个海豹组合在一起,象征着一个 Pod。

3. 构建、运行和管理容器

软件包安装与环境准备

  • 安装 podman
    1
    2
    3
    yum/dnf install podman -y
    # 对于 Docker 用户
    # yum install docker -y
  • 创建别名以兼容 docker 命令
    1
    alias docker=podman
  • 获取帮助
    1
    2
    3
    podman --help
    # 或者
    man podman
  • 检查版本
    1
    podman -v
  • 检查 podman 环境和仓库信息
    1
    2
    # 如果您尝试加载一个容器镜像,它会先查找本地机器,然后按顺序列出的每个注册中心进行查找。
    podman info

镜像管理

  • 在仓库中搜索特定镜像
    1
    podman search httpd
  • 下载可用镜像
    1
    podman pull docker.io/library/httpd
  • 列出本地已下载的镜像
    1
    podman images

容器生命周期管理

  • 运行一个下载好的 httpd 容器

    1
    podman run -dt -p 8080:80/tcp docker.io/library/httpd
    • d = detach (分离模式,即后台运行)
    • t = get the tty shell (获取 tty 终端)
    • p = port (端口映射)
  • 列出正在运行的容器

    1
    podman ps

    运行此命令后,您可以通过 Web 浏览器访问 http://<主机IP>:8080 来检查 httpd 服务是否正常。

  • 查看容器日志

    1
    2
    # -l 表示查看最新创建的那个容器的日志
    podman logs -l
  • 停止一个正在运行的容器

    1
    2
    # con-name/con-id 可以从 podman ps 命令的输出中获得
    podman stop <con-name or con-id>
  • 通过改变端口号来运行多个 httpd 容器

    1
    2
    3
    podman run -dt -p 8081:80/tcp docker.io/library/httpd
    podman run -dt -p 8082:80/tcp docker.io/library/httpd
    podman ps
  • 停止和启动一个先前运行的容器

    1
    podman stop|start <con-name or con-id>
  • 从已下载的镜像创建一个新容器(但不启动)

    1
    podman create --name httpd-con docker.io/library/httpd
  • 启动一个已创建的容器

    1
    podman start httpd-con

4. 通过 systemd 管理容器

为了让容器能作为系统服务被管理(例如开机自启),可以为其生成 systemd 单元文件。

  1. 生成单元文件

    首先,您必须有一个已创建的容器(例如我们之前创建的 httpd-con)。

    1
    podman generate systemd --new --files --name httpd-con
  2. 复制单元文件到 systemd 目录

    1
    cp container-httpd-con.service /etc/systemd/system/
  3. 启用服务

    1
    systemctl enable container-httpd-con.service
  4. 启动服务

    1
    systemctl start container-httpd-con.service

Linux 使用 firewalld 管理网络安全

本章将介绍 firewalld,这是现代 Linux 发行版中用于管理网络安全的动态防火墙守护进程。

1. 防火墙简介

  • 什么是防火墙?

    • 广义上,防火墙是一道阻止火势蔓延的墙。
    • 在 IT 领域,当数据包进出一个服务器时,防火墙会根据预设的规则来检测数据包信息,以决定是允许还是阻止其通过。
    • 简单来说,防火墙就像一个“看门人”、“保安”或“盾牌”,它根据给定的规则来决定谁可以进出。
  • 防火墙的两种类型:

    • 软件防火墙: 运行在操作系统之上。
    • 硬件防火墙: 内置了防火墙软件的专用设备。

Acrobat_UxVTFAFdh1.png

2. firewalld 概述

firewalld 的工作方式与 iptables 类似,但它有自己的命令行工具 firewall-cmd

  • 预定义服务: 它内置了一些预定义的服务规则(如 NFS, NTP, HTTPD 等),可以轻松开启或关闭。
  • 核心组件: firewalld 同样包含以下组件:
    • 表 (Table)
    • 链 (Chains)
    • 规则 (Rules)
    • 目标 (Targets)

3. firewalld 基础设置与命令

firewalldiptables

您可以选择运行 iptablesfirewalld,但通常只运行其中一个。为确保 firewalld 正常工作,建议停止并禁用 iptables

1
2
3
4
# 确保 iptables 服务被停止、禁用并屏蔽
systemctl stop iptables
systemctl disable iptables
systemctl mask iptables

安装与启动

1
2
3
4
5
6
# 检查 firewalld 软件包是否已安装
rpm -qa | grep firewalld

# 启动并设置 firewalld 开机自启
systemctl start firewalld
systemctl enable firewalld

常用查看命令

1
2
3
4
5
6
7
8
# 查看防火墙的完整规则集
firewall-cmd --list-all

# 获取所有 firewalld 支持的预定义服务列表
firewall-cmd --get-services

# 重新加载防火墙配置,使永久规则生效
firewall-cmd --reload

4. firewalld 实践示例

区域 (Zones) 管理

firewalld 拥有多个区域,可以为不同的网络环境设置不同的安全策略。

1
2
3
4
5
6
7
8
9
10
11
# 获取所有可用区域的列表
firewall-cmd --get-zones

# 获取当前活动的区域及其关联的网卡
firewall-cmd --get-active-zones

# 获取 public 区域的防火墙规则
# (如果 public 是默认区域,则 --zone=public 是可选的)
firewall-cmd --zone=public --list-all
# 或者
firewall-cmd --list-all

服务 (Service) 管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 添加 http 服务 (运行时配置,重启后失效)
firewall-cmd --add-service=http

# 移除 http 服务
firewall-cmd --remove-service=http

# 重新加载配置 (例如,在一个永久规则被修改后)
firewall-cmd --reload

# 永久添加 http 服务 (重启后保留)
firewall-cmd --add-service=http --permanent

# 永久移除 http 服务
firewall-cmd --remove-service=http --permanent
# (注意: 修改永久规则后需 --reload 才能立即生效)

端口 (Port) 管理

1
2
3
4
5
# 添加一个端口 (例如 1110/tcp)
firewall-cmd --add-port=1110/tcp

# 移除一个端口
firewall-cmd --remove-port=1110/tcp

高级规则:富规则 (Rich Rules) 与直接规则 (Direct Rules)

使用富规则拒绝特定 IP

1
2
# 拒绝来自 IP 192.168.0.25 的所有流量
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.0.25" reject'

阻止 ICMP (Ping) 流量

1
2
3
4
5
# 阻止传入的 ICMP 请求
firewall-cmd --add-icmp-block-inversion

# 解除 ICMP 阻止
firewall-cmd --remove-icmp-block-inversion

使用直接规则阻止出站流量

直接规则允许您插入原生的 iptables 规则。

1
2
3
4
5
6
# 1. 查找网站对应的 IP 地址
host -t a www.facebook.com

# 2. 添加一条直接规则来阻止到该 IP 的出站流量
# (假设查到的 IP 是 31.13.71.36)
firewall-cmd --direct --add-rule ipv4 filter OUTPUT 0 -d 31.13.71.36 -j DROP

5. 添加自定义服务

如果您有一个未被 firewalld 预定义的第三方服务,可以为其创建服务文件。

  1. 定位服务目录:
    firewalld 的服务文件位于 /usr/lib/firewalld/services/

  2. 创建服务文件:
    最简单的方式是复制一个现有的 .xml 文件,然后修改其服务名和端口号。下图演示了如何创建一个名为 test.xml 的文件来定义一个端口为 22SSH 服务。

Acrobat_mnt93iut9h.png

*   `<short>`: 服务的短名称。
*   `<description>`: 服务的描述。
*   `<port>`: 定义服务的协议和端口。
  1. 应用自定义服务:
    以添加一个非预定义的 sap 服务为例 (假设其服务文件 sap.xml 已按上述方法创建好)。
    1
    2
    3
    4
    5
    6
    7
    8
    # 重启 firewalld 以加载新的服务文件
    systemctl restart firewalld

    # 验证新服务是否已被识别
    firewall-cmd --get-services

    # 将新服务添加到防火墙规则中
    firewall-cmd --add-service=sap

Linux 控制引导过程

本章内容旨在帮助您理解现代 Linux 的引导过程,设置默认的 systemd 目标,以及处理一些常见的引导问题,如恢复 root 密码和修复文件系统损坏。

1. Linux 引导过程 (较新版本)

  • CentOS/Redhat 7 及以上版本的引导顺序发生了变化。
  • systemd 是 CentOS/RHEL 7 中的新服务管理器,负责管理引导序列。
  • 它向后兼容旧版 RedHat Linux (包括 RHEL 6) 中使用的 SysV init 脚本。
  • 每一位系统管理员都需要理解操作系统的引导过程,以便能够有效地进行故障排查。

systemd 引导流程详解

  1. BIOS = 基本输入输出设置 (固件接口)

    • 执行 POST = 开机自检。
  2. MBR = 主引导记录

    • 信息保存在硬盘的第一个扇区,它指明了 GRUB2 的位置,以便将其加载到计算机内存 (RAM) 中。
  3. GRUB2 = Grand Unified Boot Loader v2

    • 加载 Linux 内核。
    • 配置文件: /boot/grub2/grub.cfg
  4. Kernel = 操作系统核心

    • initrd.img 加载所需的驱动程序。
    • 启动第一个操作系统进程 (systemd)。
  5. Systemd = 系统守护进程 (PID # 1)

    • 启动所有必需的进程。
    • 读取 /etc/systemd/system/default.target 文件,将系统带入指定的运行级别。
    • 总共有 7 个运行级别 (0 到 6)。

2. 如何重启/关机

  • 要从命令行关闭或重启系统,您可以使用 systemctl 命令。
  • systemctl poweroff = 停止所有正在运行的服务,卸载所有文件系统,然后关闭系统。
  • systemctl reboot = 停止所有正在运行的服务,卸载所有文件系统,然后重启系统。
  • 您也可以使用这些命令的缩短版,如 shutdown, poweroffreboot,它们是指向 systemctl 对应功能的符号链接。

3. 选择 systemd 目标 (Target)

systemd 是决定操作系统需要进入哪个运行级别的第一个 Linux 进程。这些运行级别现在被称为“目标 (targets)”。

重要目标列表

目标 (Target) 描述
graphical.target 系统支持多用户、图形化及文本方式登录。
multi-user.target 系统支持多用户、仅支持文本方式登录。
rescue.target 完成基础系统初始化后,进入一个 sulogin 提示符。
emergency.target initramfs pivot 完成后,系统 root 被挂载为只读,并进入一个 sulogin 提示符。

管理和查看目标

  • 检查当前目标或运行级别
    1
    2
    systemctl get-default
    who -r
  • 目标的依赖关系
    一个目标可以是另一个目标的一部分。例如,graphical.target 包含了 multi-user.target,而后者又依赖于 basic.target 和其他目标。
    • 您可以使用以下命令查看这些依赖关系:
      1
      systemctl list-dependencies graphical.target | grep target
  • 显示新的运行级别/目标
    • 您可以通过以下命令显示新的运行级别/目标文件:
      1
      ls -al /lib/systemd/system/runlevel*
  • 设置默认目标
    1
    systemctl set-default graphical.target

4. 故障排查

场景一:恢复 root 密码

如果忘记了 root 密码,可以通过修改内核引导参数来重置。

  1. 重启计算机并在 GRUB2 引导菜单出现时按 e 键,进入编辑模式。
  2. 找到以 linuxlinux16 开头的行。
  3. 在该行的末尾添加 rd.break
  4. Ctrl + x 启动系统,这将使您进入一个临时的 root shell。
  5. 以读写模式重新挂载根文件系统:
    1
    mount -o remount,rw /sysroot
  6. 切换到系统的真实根环境:
    1
    chroot /sysroot
  7. 使用 passwd 命令修改 root 密码。
  8. 如果系统启用了 SELinux,需要创建一个 .autorelabel 文件以在下次启动时重建 SELinux 标签:
    1
    touch /.autorelabel
  9. 输入 exit 两次或直接重启 (reboot -f),系统将使用新密码重启。

场景二:修复文件系统损坏

当您在 /etc 配置文件中出错或磁盘级文件系统损坏时,都可能导致文件系统损坏。在这些情况下,systemd 将无法在预设的目标中启动系统,而是会将系统带入紧急模式。

常见问题与现象

问题 系统现象
文件系统损坏 systemd 会尝试自动修复。如果问题严重,系统会进入紧急 Shell。
/etc/fstab 中引用了不存在的设备或 UUID systemd 会等待一段时间。超时后,系统进入紧急 Shell。
/etc/fstab 中指定了不存在的挂载点 系统直接进入紧急 Shell。
/etc/fstab 中指定了错误的挂载选项 系统直接进入紧急 Shell。

修复步骤

  • 在任何情况下,管理员都可以使用紧急目标来诊断和修复问题,因为在显示紧急 shell 之前,没有文件系统被挂载。
  • 当使用紧急 shell 修复文件系统问题时,在编辑 /etc/fstab 后,不要忘记运行 systemctl daemon-reload。如果不执行此重新加载操作,systemd 可能会继续使用旧版本(的配置)。

Linux 网络附加存储:NFS 与 Samba

本章将介绍两种在 Linux 环境中常用的网络附加存储技术:NFS (网络文件系统) 和 Samba,内容将严格遵循原始教学材料。

1. 网络文件系统 (NFS)

NFS (Network File System) 是由 Sun Microsystems 开发的一种客户端/服务器文件系统协议,它允许用户像访问本地文件一样访问网络上其他计算机上的文件。

通过 NFS,服务器可以“导出”其文件系统的一部分,而客户端则可以“挂载”这些导出的目录,从而实现跨网络的文件共享。

NFS_Diagram.png

NFS 服务器配置步骤

以下是在服务器端设置 NFS 共享的基本步骤。

步骤 1: 安装 NFS 软件包

1
2
# (大多数情况下已经预装)
yum install nfs-utils libnfsidmap

步骤 2: 启用并启动 NFS 服务

1
2
3
4
5
6
systemctl enable rpcbind
systemctl enable nfs-server
systemctl start rpcbind
systemctl start nfs-server
systemctl start rpc-statd
systemctl start nfs-idmapd

步骤 3: 防火墙配置

确保防火墙允许 NFS 通信。对于生产环境,应添加永久规则。

1
2
3
4
5
6
7
# 允许NFS服务
firewall-cmd --permanent --add-service=nfs
# 允许RPC绑定和挂载服务
firewall-cmd --permanent --add-service=rpc-bind
firewall-cmd --permanent --add-service=mountd
# 重新加载防火墙配置
firewall-cmd --reload

测试环境注意: 如果在测试环境中遇到连接问题,可以临时禁用防火墙以进行故障排除,但不建议在生产环境中使用。

1
2
systemctl stop firewalld
systemctl disable firewalld

步骤 4: 创建并配置共享目录

  1. 创建一个用于共享的目录。

    1
    2
    mkdir /mypretzels
    chmod a+rwx /mypretzels
  2. 编辑 /etc/exports 文件,定义共享规则。

    /etc/exports 文件的格式为: <共享目录> <客户端地址>(<选项>)

    1
    2
    3
    4
    5
    # 示例1: 仅共享给单个主机
    /mypretzels 192.168.12.7(rw,sync,no_root_squash)

    # 示例2: 共享给所有主机
    /mypretzels *(rw,sync,no_root_squash)
    • rw: 允许读写操作。
    • sync: 所有文件系统更改会立即同步写入磁盘。
    • no_root_squash: 允许客户端的 root 用户以服务器端 root 用户的身份访问文件,具有最高权限。

步骤 5: 应用配置

使 exports 文件中的配置生效。

1
exportfs -rv

NFS 客户端配置步骤

以下是在客户端挂载 NFS 共享的步骤。

步骤 1: 安装 NFS 软件包

1
yum install nfs-utils rpcbind

步骤 2: 启动 RPC 服务

1
systemctl rpcbind start

步骤 3: 查看服务器上的可用共享

1
2
# (NFS 服务器 IP)
showmount -e 192.168.1.5

步骤 4: 创建挂载点并挂载

1
2
mkdir /mnt/kramer
mount 192.168.1.5:/mypretzels /mnt/kramer

步骤 5: 验证和卸载

1
2
3
4
# 验证挂载
df -h
# 卸载
umount /mnt/kramer

2. Samba:下载、安装和配置

  • Samba 是一个 Linux 工具或实用程序,允许将 Linux 资源(如文件和打印机)共享给其他操作系统。
  • 它的工作方式与 NFS 完全一样,但区别在于 NFS 在 Linux 或类 Unix 系统内部共享,而 Samba 则与其他操作系统(如 Windows, MAC 等)共享。
  • 例如,计算机 “A” 使用 Samba 共享其文件系统给计算机 “B”,那么计算机 “B” 将会看到这个共享文件系统,就好像它被挂载为本地文件系统一样。
  • Samba 通过一种名为 SMB (Server Message Block) 的协议来共享其文件系统,该协议由 IBM 发明。
  • 另一个用于共享 Samba 的协议是 CIFS (Common Internet File System),由微软发明,也称为 NMB (NetBios Name server)。
  • CIFS 成为了 SMB 的扩展,现在微软已经推出了更新版本的 SMB v2 和 v3,它们在行业中被广泛使用。
  • 简单来说,当人们使用 SMB 或 CIFS 时,他们谈论的是完全相同的东西。这两者不仅在讨论中可以互换,在应用中也是如此,即说 CIFS 的客户端可以与说 SMB 的服务器通信,反之亦然。因为 CIFS 是 SMB 的一种形式。

分步安装说明

第一步,请务必为您的虚拟机创建一个快照。

步骤 1: 安装 Samba 软件包

1
2
3
# 成为 root 用户
# 安装 samba 相关的包
yum install samba samba-client samba-common

步骤 2: 配置防火墙

  • 选项 A: (推荐) 允许 Samba 通过防火墙
    1
    2
    firewall-cmd --permanent --zone=public --add-service=samba
    firewall-cmd --reload
  • 选项 B: (仅测试) 停止并禁用防火墙
    1
    2
    systemctl stop firewalld
    systemctl disable firewalld

步骤 3: 创建共享目录并设置权限

1
2
3
4
mkdir -p /samba/morepretzels
chmod a+rwx /samba/morepretzels
# 将目录所有者设为 nobody,用于匿名访问
chown -R nobody:nobody /samba

步骤 4: 配置 SELinux

  • 选项 A: (推荐) 设置 SELinux 安全上下文

    (仅当 SELinux 启用时)

    1
    2
    # 为 samba 共享目录设置正确的上下文
    chcon -t samba_share_t /samba/morepretzels
  • 选项 B: (仅测试) 禁用 SELinux
    1
    2
    3
    4
    5
    6
    7
    # 检查 SELinux 状态
    sestatus
    # 编辑配置文件
    vi /etc/selinux/config
    # 将 SELINUX=enforcing 修改为 SELINUX=disabled
    # 重启
    reboot

步骤 5: 修改 smb.conf 文件以添加新的共享文件系统

注意: 请务必创建 smb.conf 文件的副本。
删除 smb.conf 文件中的所有内容,并添加以下参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[global]
workgroup = WORKGROUP
netbios name = centos
security = user
map to guest = bad user
dns proxy = no

[Anonymous]
path = /samba/morepretzels
browsable = yes
writable = yes
guest ok = yes
guest only = yes
read only = no

步骤 6: 验证设置并启动服务

1
2
3
4
5
6
7
# 验证配置
testparm
# 启用并启动 Samba 服务
systemctl enable smb
systemctl enable nmb
systemctl start smb
systemctl start nmb

客户端挂载

在 Windows 客户端上挂载

  1. 转到“开始”菜单或“搜索栏”。
  2. 输入 \\192.168.1.95 (这是我的服务器 IP,您可以通过运行 ifconfig 命令来检查您的 Linux CentOS IP)。
  3. 访问 Anonymous 共享。

在 Linux 客户端上挂载

1
2
3
4
5
6
7
# 成为 root 用户
# 安装 cifs-utils
yum -y install cifs-utils samba-client
# 创建挂载点目录
mkdir /mnt/sambashare
# 挂载 samba 共享 (入口无需密码)
mount -t cifs //192.168.1.95/Anonymous /mnt/sambashare/

配置安全的 Samba 服务器

步骤 1: 创建用于认证的用户和组

1
2
3
4
5
6
7
8
# 添加用户 larry 和组 smbgrp
useradd larry
groupadd smbgrp
# 将 larry 添加到 smbgrp 组
usermod -a -G smbgrp larry
# 为 larry 设置 Samba 密码
smbpasswd -a larry
# --> 输入并重复您的 SAMBA 密码

步骤 2: 创建新的安全共享目录并设置权限

1
2
3
4
mkdir /samba/securepretzels
chown -R larry:smbgrp /samba/securepretzels
chmod -R 0770 /samba/securepretzels
chcon -t samba_share_t /samba/securepretzels

步骤 3: 编辑配置文件 /etc/samba/smb.conf

(创建备份副本)
添加以下行:

1
2
3
4
5
6
[Secure]
path = /samba/securepretzels
valid users = @smbgrp
guest ok = no
writable = yes
browsable = yes

步骤 4: 重启服务

1
2
systemctl restart smb
systemctl restart nmb

现在,只有 smbgrp 组内的用户才能使用其 Samba 密码访问 Secure 共享。

Linux Stratis 存储管理

本节将介绍 Red Hat 8 中引入的下一代卷管理解决方案 Stratis。

1. Stratis 简介

Stratis 是一种新型的本地存储管理解决方案,旨在简化存储配置并提供类似 ZFS 和 Btrfs 的高级功能。它通过在后台整合现有的存储技术(如 LVM 和 XFS),为系统管理员提供一个更易于使用和功能更丰富的接口。

架构对比:LVM vs. Stratis

Stratis 的核心优势在于其简化的架构。传统的 LVM 存储栈层次较多,管理相对复杂,而 Stratis 将多个层次合并为一个统一的“存储池”,大大降低了管理的复杂度。

图示解读:

  • LVM 架构: 从底向上依次是:硬盘 -> 分区 -> 物理卷 (PV) -> 卷组 (VG) -> 逻辑卷 (LV) -> 文件系统。管理员需要分别对这些层次进行操作。
  • Stratis 架构: 从底向上依次是:一个或多个块设备 (Block devices) -> 单一的存储池 (Pool) -> 一个或多个文件系统 (Filesystem)。管理员的大部分操作都集中在“池”和“文件系统”这两个简单的概念上。

Acrobat_S8CkfiJbOV.png

核心概念

  • 块设备 (Block Devices): 指的是底层的物理存储,例如硬盘 (/dev/sda) 或磁盘分区 (/dev/sdb1)。
  • 池 (Pool): 由一个或多个块设备组成,是 Stratis 的核心存储单元。它汇集了所有物理存储,形成一个统一的、可灵活分配的资源池。
  • 文件系统 (Filesystems): 从池中创建,供用户实际使用。Stratis 的文件系统默认是精简配置 (thin-provisioned) 的,这意味着它们只在实际写入数据时才占用池空间,并且可以按需自动增长,无需手动扩容。

主要优势

  • 简化管理: 将复杂的存储层次抽象为“池”和“文件系统”,命令更直观。
  • 精简配置: 默认开启,提高了存储利用率。
  • 快照与回滚: 支持对文件系统创建写时复制 (copy-on-write) 快照,便于备份和恢复。
  • 池化管理: 轻松添加新磁盘来扩展存储池的容量。

2. 安装与配置 Stratis

以下是安装和启用 Stratis 服务的基本步骤。

步骤 1: 安装 Stratis 软件包

使用 yumdnf 安装 stratis-clistratisd

1
yum/dnf install stratis-cli stratisd

VirtualBoxVM_J1p09o0PLm.png

步骤 2: 启动并启用 Stratis 服务

使用 systemctl 启动并设置为开机自启。

1
systemctl enable --now stratisd

注意: enable --now 相当于同时执行 enablestart

3. 创建 Stratis 存储池 (Pool)

存储池是由一个或多个块设备组成的。

步骤 1: 准备块设备

首先,需要有可用的块设备。例如,可以从虚拟化软件中添加两个 5GB 的新磁盘。添加后,使用 lsblk 命令验证它们在操作系统中是否可见(例如 /dev/sdb, /dev/sdc)。

1
lsblk

步骤 2: 创建一个新的 Stratis 池

使用 stratis pool create 命令创建一个名为 pool1 的新池,并将设备 /dev/sdb 加入其中。

1
stratis pool create pool1 /dev/sdb

步骤 3: 查看池列表

验证池是否已成功创建。

1
stratis pool list

步骤 4: 扩展存储池

如果需要增加池的容量,可以将新的块设备(例如 /dev/sdc)添加到现有池中。

1
stratis pool add-data pool1 /dev/sdc

检查: 再次运行 stratis pool list 可以看到池的容量增加了。

4. 创建 Stratis 文件系统

文件系统是在 Stratis 池之上创建的。

步骤 1: 创建一个新的文件系统

pool1 池中创建一个名为 fs1 的文件系统。

1
stratis filesystem create pool1 fs1

说明: 文件系统的初始大小会很小(例如 546MB),因为它采用了精简配置,会根据数据的写入自动增长。

步骤 2: 查看文件系统列表

验证文件系统是否已创建。

1
stratis filesystem list

步骤 3: 创建挂载点并挂载

创建一个目录作为挂载点,然后将 Stratis 文件系统挂载上去。

1
2
3
mkdir /bigdata
mount /dev/stratis/pool1/fs1 /bigdata
lsblk

步骤 4: 实现开机自动挂载

为了让系统重启后能自动挂载,需要将条目添加到 /etc/fstab 文件中。

注意: 下面的 UUID 是一个示例,你需要用 lsblk -o +UUIDblkid 命令查找你自己的文件系统 UUID。

1
UUID="asf-0887afgdja-" /fs1 xfs defaults,x-systemd.requires=stratisd.service 0 0

5. 创建文件系统快照

Stratis 支持为其文件系统创建快照。

步骤 1: 创建快照

pool1 池中的 fs1 文件系统创建一个名为 fs1-snap 的快照。

1
stratis filesystem snapshot pool1 fs1 fs1-snap

注意: PDF原文中的 startis 疑似为 stratis 的拼写错误,已在此修正。

步骤 2: 查看快照

可以使用 stratis filesystem list 命令查看已创建的文件系统和快照。

1
stratis filesystem list
0%