Skip to content

Go环境与基础语法

Go 常用于写运维工具、Exporter、控制器、命令行程序和 Kubernetes 相关组件。它编译成单个二进制文件,部署时不依赖目标机器上的解释器;标准库里自带 HTTP、JSON、并发、文件和系统命令能力,适合写长期维护的小工具。

Shell 适合直接串系统命令,Python 适合快速处理脚本和接口,Go 更适合做需要长期运行、并发处理、交付成二进制的工具。比如 Exporter、Webhook、Kubernetes Controller、批量巡检 Agent 这类程序,用 Go 会更自然。

一、Go 程序运行链路

Go 源码先通过 go build 编译成二进制文件,二进制再在目标机器运行。

和 Python 脚本不同,目标机器上通常不需要安装 Go 才能运行已经编译好的程序。目标机器只需要能执行这个二进制,并且系统架构匹配。

二、安装 Go

安装前先确认环境:

bash
go version
which go

Rocky / RHEL / CentOS 可以先用系统包:

bash
dnf install -y golang

go version
go env GOPATH GOROOT GOPROXY

Ubuntu / Debian:

bash
apt-get update
apt-get install -y golang-go

go version
go env GOPATH GOROOT GOPROXY

系统仓库的 Go 版本可能偏旧。需要固定新版本时,通常用官方二进制包安装到 /usr/local/go,再把 /usr/local/go/bin 加入 PATH。

bash
# 先清理旧的官方安装目录,避免多个版本混在一起
rm -rf /usr/local/go

# go<version>.linux-amd64.tar.gz 按实际版本替换
tar -C /usr/local -xzf go<version>.linux-amd64.tar.gz

cat >/etc/profile.d/go.sh <<'EOF'
export PATH=/usr/local/go/bin:$PATH
EOF

source /etc/profile.d/go.sh
go version

Windows 上通常用官方安装包,装完后重新打开 PowerShell:

powershell
go version
go env GOPATH GOROOT GOPROXY

GOROOT 是 Go 自身安装目录,GOPATH 是旧工作区和下载缓存相关目录。现在写项目主要用 Go Modules,不再把项目放进 GOPATH 下面。

三、配置模块代理

国内网络拉 Go 模块时经常慢或超时,可以配置 GOPROXY

bash
go env -w GOPROXY=https://goproxy.cn,direct
go env GOPROXY

direct 表示代理找不到时直接访问原始仓库。公司内部私有模块还要配置 GOPRIVATE,避免私有模块路径被发到公共代理和校验服务。

bash
go env -w GOPRIVATE=git.example.com/*
go env GOPRIVATE

常用相关变量:

变量作用
GOPROXY模块下载代理
GOSUMDB模块校验数据库
GOPRIVATE私有模块匹配规则
GONOSUMDB不走公共校验数据库的模块
GO111MODULE模块模式开关,现代 Go 通常保持默认

排查模块下载问题时先看 go env。代理、私有仓库、认证、DNS 都可能影响 go mod tidy

四、第一个 Go 项目

创建项目:

bash
mkdir -p ops-go-demo
cd ops-go-demo

# module 路径通常写成仓库路径,示例先用本地名字
go mod init example.com/ops-go-demo

main.go

go
package main

import "fmt"

func main() {
	// main 是程序入口,go run 和编译后的二进制都会从这里开始执行
	fmt.Println("hello go")
}

运行:

bash
go run .

编译:

bash
go build -o ops-go-demo .
./ops-go-demo

项目目录:

text
ops-go-demo/
├── go.mod
├── main.go
└── ops-go-demo

go.mod 记录模块名和依赖,main.go 是源码,ops-go-demo 是编译出来的二进制文件。

五、变量与类型

Go 是静态类型语言,变量有明确类型。编译阶段会检查类型错误。

go
package main

import "fmt"

func main() {
	var service string = "nginx"
	var port int = 80
	var enabled bool = true

	// := 是短变量声明,函数内部最常用
	usage := 78.5

	fmt.Println(service, port, enabled, usage)
}

常见基础类型:

类型示例场景
string"nginx"服务名、路径、日志文本
int80端口、数量
float6478.5使用率、延迟
booltrue开关、检查结果
errorerr错误返回

常量:

go
const defaultTimeout = 5
const appName = "ops-checker"

Go 里未使用的变量会导致编译失败。这一点对脚本型思维不太一样,但能减少“写了但忘了用”的脏代码。

六、条件判断

Go 的 if 不需要括号,但代码块必须有 {}

go
package main

import "fmt"

func main() {
	cpuUsage := 82

	if cpuUsage >= 90 {
		fmt.Println("critical")
	} else if cpuUsage >= 70 {
		fmt.Println("warning")
	} else {
		fmt.Println("ok")
	}
}

if 可以带一个短语句,常用于错误处理:

go
if err := checkDisk("/"); err != nil {
	fmt.Println("check failed:", err)
}

这类写法让 err 的作用域限制在 if 语句里,避免后面误用。

七、循环

Go 只有 for,没有 while 关键字。

go
for i := 0; i < 3; i++ {
	fmt.Println(i)
}

类似 while:

go
retry := 0
for retry < 3 {
	fmt.Println("retry", retry)
	retry++
}

遍历切片:

go
services := []string{"nginx", "mysql", "redis"}

for index, service := range services {
	fmt.Println(index, service)
}

只要值时可以用 _ 忽略索引:

go
for _, service := range services {
	fmt.Println(service)
}

八、函数

函数用 func 定义。参数和返回值都带类型。

go
func add(a int, b int) int {
	return a + b
}

连续同类型参数可以合并:

go
func add(a, b int) int {
	return a + b
}

Go 常见返回值是“结果 + 错误”:

go
package main

import (
	"fmt"
	"os"
)

func readConfig(path string) (string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

func main() {
	content, err := readConfig("/etc/hosts")
	if err != nil {
		fmt.Println("read config failed:", err)
		return
	}

	fmt.Println(content)
}

nil 表示没有错误。Go 不用异常处理普通错误,函数调用方显式检查 err

九、错误处理

创建错误:

go
import "errors"

func validatePort(port int) error {
	if port < 1 || port > 65535 {
		return errors.New("invalid port")
	}
	return nil
}

带上下文的错误:

go
import "fmt"

func validatePort(port int) error {
	if port < 1 || port > 65535 {
		return fmt.Errorf("invalid port: %d", port)
	}
	return nil
}

包装错误:

go
data, err := os.ReadFile("/etc/app.conf")
if err != nil {
	return fmt.Errorf("read config: %w", err)
}

%w 会保留原始错误,后面可以用 errors.Iserrors.As 判断错误类型。

十、格式化与检查

Go 官方工具链自带格式化:

bash
go fmt ./...

检查和测试:

bash
go test ./...
go vet ./...

编译 Linux 二进制:

bash
GOOS=linux GOARCH=amd64 go build -o bin/ops-checker .

Go 的工具链比较统一。日常开发里 go fmtgo testgo mod tidygo build 是最常用的几条命令。