本篇文章将聊聊如何快速搭建 Linux 环境中的 Golang 开发环境。
在《基础篇》[1]的内容中,我们聊过了如何基于 Ubuntu 22.04 搭建基础的 Linux 学习环境。接下来的文章里,我们先来聊聊如何在 Linux 环境中,快速安装配置各种可维护的语言环境。
写在前面
在开始聊如何做之前,我们首先要了解为什么要这么做。
我知道有不少同学有安装好环境之后,然后持续使用一个环境,并“天长地久”的用下去的习惯。这样做的好处是“看起来简单省心”,但实际上却埋藏了许多隐患,举几个常见的例子:
•同事说代码、程序有问题,跑不起来,你说程序在你这边是正常的,你觉得他的使用方式不对,他觉得你的程序“兼容性”/“健壮性”有问题。•当你想把半年、一年前的项目跑起来,发现运行的时候多了非常多的“警告”,甚至是“报错”,尤其是当你换了一台电脑的时候。•项目扩充人手,你的同事也需要配置一套环境,你和他折腾的半天,虽然有着重重困难,什么版本不对,配置散落在系统的各个“犄角旮旯”等等,但是你们最终克服了困难,并加深了革命友情。•项目遇到大版本升级,因为一些历史原因,你需要同时使用两个不同的语言版本来做调试,不同版本存在兼容性问题(包括依赖兼容性问题),你的本地环境是升级还是不升呢。
解决这个问题的最佳方案有两个:
•尽可能简化你的环境,简化环境依赖(因为项目的多样性和复杂性,这有一些难)。•尽可能参考“基础架构即代码(IaC)”的思想去维护我们自己的开发环境,让我们所使用的内容,尽可能配置化,透明化,可复现。
它除了能够完成 golang 开发环境的快速安装之外,还能够保障多个版本的 golang 共存,不同版本的软件依赖包都保持正常工作。并且,它的实现和社区大名鼎鼎的 nvm-sh/nvm 、shyiko/jabba 是一致的,都是由 BASH 编写,和所需要管理的 Runtime 语言无关,能够更稳定的完成“管理工作”。
Golang 环境安装和配置使用
关于 Golang 的多版本管理和安装,我曾经写过两篇相关的内容,一篇是半年前的内容,分享如何对 “Golang 进行多版本管理[2]”,另外一篇则是这篇的补充内容,分享如何针对 Mac M1 这类 ARM 设备使用 Golang 版本管理工具:《M1 芯片 Mac 上更好的 Golang 使用方案》[3]。
如果你希望了解本章节之外的实践内容,或者过程中的思考,可以翻阅上面两篇内容。本篇文章的重点在于如何快速安装和配置,所以就不再展开相关“折腾安装工具”的细节啦。
安装 Golang 版本管理工具:soulteary/gvm
关于 Golang 的安装和版本升降级,因为老牌开源软件 GVM (Go Version Manager)“年久失修”,所以我做了一个修正版:https://github.com/soulteary/gvm
想要正常使用这个工具,我们需要先完成工具的基础依赖的安装:
代码语言:javascript复制sudo apt install -y binutils bison gcc make
接着,执行下面的命令,通过网络获取安装脚本,然后在本地执行脚本完成安装。(如果你因为网络或其他原因,无法执行这条命令,可以使用下文中的替代方案):
代码语言:javascript复制curl -sSL https://github.com/soulteary/gvm/raw/master/binscripts/gvm-installer | bash
不出意外的话,你将会看到类似下面的日志输出,意味着工具就此刻已经安装好啦。
代码语言:javascript复制Cloning from https://github.com/soulteary/gvm.git to /home/soulteary/.gvm
No existing Go versions detected
Installed GVM v1.0.24
Please restart your terminal session or to get started right away run
`source /home/soulteary/.gvm/scripts/gvm`
这里选择执行或者不执行 source /home/soulteary/.gvm/scripts/gvm
这条命令都可以(注意调整路径中的用户名),因为在接下来的文章中,我们将使用更靠谱的方式来将命令注册到我们所使用的 SHELL 环境中。
使用国内镜像来安装:soulteary/gvm
为了让安装过程更加顺利,我们可以使用从国内镜像下载包含安装脚本的仓库代码,然后直接执行安装脚本,来完成 gvm
这个开源软件的安装。
先使用 git clone
下载完整的软件仓库:
git clone https://gitcode.net/soulteary/gvm.git
指定 SRC_REPO
参数为国内镜像地址,然后运行安装脚本:
SRC_REPO=https://gitcode.net/soulteary/gvm.git bash gvm/binscripts/gvm-installer
当脚本运行完毕,我们将会看到上文中提到过的日志输出,此刻 gvm
就安装完毕啦。
Cloning from https://gitcode.net/soulteary/gvm.git to /home/soulteary/.gvm
No existing Go versions detected
Installed GVM v1.0.24
Please restart your terminal session or to get started right away run
`source /home/soulteary/.gvm/scripts/gvm`
为了更方便的使用 gvm
,我们还需要进行一些配置。
配置 gvm 加速 Golang 下载/切换
gvm 支持使用两种方式来下载 “Golang”,然而不论是“下载源码编译安装”,还是下载适合当前操作系统的“预编译好的二进制文件”,我们都需要访问官方地址。
为了避免下载过程中因为网络问题,出现下载慢,或者无法下载的情况,节约我们的时间,我们需要对 gvm
进行一些简单的配置。
我们可以在当前使用的 “SHELL
” 的 “rc
” 文件中(比如.bashrc
或者 .zshrc
),添加下面的内容,来在当前的环境中让 gvm
命令生效,同时,让我们能够使用更快的下载源来下载我们所需要的 “Golang”:
export GO_BINARY_BASE_URL=https://golang.google.cn/dl/
[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"
export GOROOT_BOOTSTRAP=$GOROOT
在“rc
” 文件中添加了上述内容后,需要重启终端会话,才能够让会话生效。你可以使用 CTRL D
退出登录,然后再重新使用 SSH
进行终端连接或者直接在本地创建一个新的会话(具体怎么做,取决于你是如何开启的会话)。
为了让配置过程清晰透明,上面的三条命令,我们来依次看看上面的命令都“做了什么事情”。
代码语言:javascript复制export GO_BINARY_BASE_URL=https://golang.google.cn/dl/
命令中的 GO_BINARY_BASE_URL
变量,定义了我们将从何处下载 Golang 的二进制文件或源码压缩包进行安装。当然,你也可以将其替换为下面的任意一个。
# 官方地址
https://go.dev/dl/
# 官方国内镜像地址
https://golang.google.cn/dl/
# 中科大镜像
http://mirrors.ustc.edu.cn/golang/
接下来,我们来看看三条命令中看似最复杂的命令:
代码语言:javascript复制[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"
这条命令,是根据软件的实际安装情况来选择性加载 gvm
。相比较前文中安装完毕 gvm
日志输出内容推荐我们直接使用 source
命令加载 gvm
,这样可以更安全的执行命令,当且仅当 ~/.gvm
存在的时候才会加载程序,将 gvm
注册到你当前的 SHELL
环境中。
export GOROOT_BOOTSTRAP=$GOROOT
最后一条命令,则是为了确保 Golang 使用源码编译安装时,不会出错(golang 1.14后需要 ),感兴趣可以围观官方开源项目中的这个 issue[4]。
gvm 简明实用教程
gvm
是一个特别简单的命令,我们日常使用中其实只需要记得两个命令就好,第一个是 gvm install
,第二个是 gvm use
。
假设我们想安装 Golang 最新版本 1.18.3,那么只需要执行下面的命令:
代码语言:javascript复制gvm install go1.18.3 -B
在执行完毕命令之后,稍等片刻,当我们看到 Installing go1.18.3 from binary source
这条日志输出结果后,就意味着 Golang 已经被下载完毕了。如果你希望使用编译源码的方式安装 Golang 的话,可以去掉上面命令中的-B
参数:
gvm install go1.18.3
虽然我们已经完成了 Golang 1.18.3 的安装,但是目前我们还不能直接使用它,需要再执行一条命令,将这个版本的 Golang “激活”:
代码语言:javascript复制gvm use go1.18.3 --default
在执行完命令之后,我们能够立刻看到类似 Now using version go1.18.3
的日志输出结果,接下来我们就可以随意的使用 go
这个命令了。
我们可以使用 go version
来验证刚刚下载的程序是否符合我们的诉求:
go version
go version go1.18.3 linux/amd64
未来如果 Golang 推出了新版本,我们想升级只需要按照上面的玩法,调整版本号,然后再执行一遍 install
和 use
命令就好了,是不是很简单!
当然,如果你只是想临时性的使用某个版本,比如 Golang 1.17 这个旧版本,可以稍微调整一下上面的命令,去掉 use
命令中的 --default
参数,只在当前 SHELL
会话中,让这个版本的 Golang 生效,随着我们关闭终端会话,Golang 的版本也会恢复到我们指定的默认版本,再也不需要担心系统环境混乱的问题啦。
gvm install go1.17 -B
gvm use go1.17
# 再次执行查看版本,可以看到版本号已经变化了
go version
go version go1.17 linux/amd64
配置 Golang 软件包镜像
在日常开发和学习过程中,我们更多的是使用 Golang 来初始化项目和下载必要的软件包依赖。所以如何快速的下载到各种软件包也很重要,好在 Golang 提供了软件包代理配置选项 GOPROXY
,我们可以通过在 “SHELL
” 的 “rc
” 文件中配置这个参数来完成下载提速:
export GO111MODULE=on
export GOPROXY="https://goproxy.cn"
和上文中配置 gvm
一样,我们将上面的内容添加到所使用的 SHELL 的 “rc
” 配置后,需要重新创建一个终端会话,让配置生效。
这里有一个题外话,初见“goproxy”的两个域名的时候,觉得域名十分相似,一番搜索,发现这两个域名虽然归属不同的开发者在维护,但是它们之间确实有一段缘分:“goproxy.io 和 goproxy.cn 的关系”[5]。
Golang 环境验证:GoJieba
在完成环境配置之后,我们使用一个比较实用的 Golang 项目(https://github.com/yanyiwu/gojieba),来验证环境是否“好用”。
随便创建一个程序目录,然后在其中创建一个名为 main.go
的文件,引用 “gojieba”,并对一些句子和词汇进行处理:
package main
import (
"fmt"
"strings"
"github.com/yanyiwu/gojieba"
)
func main() {
var s string
var words []string
use_hmm := true
x := gojieba.NewJieba()
defer x.Free()
s = "北京西站南广场东"
words = x.CutAll(s)
fmt.Println(s)
fmt.Println("全模式:", strings.Join(words, "/"))
words = x.Cut(s, use_hmm)
fmt.Println(s)
fmt.Println("精确模式:", strings.Join(words, "/"))
s = "向量数据库"
words = x.Cut(s, use_hmm)
fmt.Println(s)
fmt.Println("精确模式:", strings.Join(words, "/"))
x.AddWord("向量数据库")
s = "向量数据库"
words = x.Cut(s, use_hmm)
fmt.Println(s)
fmt.Println("添加词典后,精确模式:", strings.Join(words, "/"))
s = "前门到了,请您后门下车"
words = x.Cut(s, use_hmm)
fmt.Println(s)
fmt.Println("新词识别:", strings.Join(words, "/"))
s = "小明先去了北京西站南广场东,然后又去了南京东路北大街西"
words = x.CutForSearch(s, use_hmm)
fmt.Println(s)
fmt.Println("搜索引擎模式:", strings.Join(words, "/"))
s = "朝阳区三里屯优衣库"
words = x.Tag(s)
fmt.Println(s)
fmt.Println("词性标注:", strings.Join(words, ","))
s = "元宇宙"
words = x.Tag(s)
fmt.Println(s)
fmt.Println("词性标注:", strings.Join(words, ","))
s = "长江大桥"
words = x.CutForSearch(s, !use_hmm)
fmt.Println(s)
fmt.Println("搜索引擎模式:", strings.Join(words, "/"))
wordinfos := x.Tokenize(s, gojieba.SearchMode, !use_hmm)
fmt.Println(s)
fmt.Println("Tokenize:(搜索引擎模式)", wordinfos)
wordinfos = x.Tokenize(s, gojieba.DefaultMode, !use_hmm)
fmt.Println(s)
fmt.Println("Tokenize:(默认模式)", wordinfos)
keywords := x.ExtractWithWeight(s, 5)
fmt.Println("Extract:", keywords)
}
在准备好程序文件之后,我们先执行 go mod init main
,完成 Go 项目的初始化:
go: creating new go.mod: module main
go: to add module requirements and sums:
go mod tidy
在执行完上面的命令后,我们的目录中将会多出来 “go.mod” 和 “go.sum” 两个文件,接着,我们来执行 go mod tidy
命令,让程序完成相关依赖的下载:
go: finding module for package github.com/yanyiwu/gojieba
go: found github.com/yanyiwu/gojieba in github.com/yanyiwu/gojieba v1.1.2
因为我们配置了软件包镜像,所以应该在几秒内就能够完成项目的初始化。
在完成了项目初始化之后,我们执行 go run main.go
来验证下程序是否能运行,不出意外,将看到类似下面的输出结果:
北京西站南广场东
全模式: 北京/北京西/北京西站/京西/西站/南/广场/东
北京西站南广场东
精确模式: 北京西站/南/广场/东
向量数据库
精确模式: 向量/数据库
向量数据库
添加词典后,精确模式: 向量数据库
前门到了,请您后门下车
新词识别: 前门/到/了/,/请/您/后门/下车
小明先去了北京西站南广场东,然后又去了南京东路北大街西
搜索引擎模式: 小明/先去/了/北京/京西/西站/北京西/北京西站/南/广场/东/,/然后/又/去/了/南京/京东/东路/南京东路/北大/大街/北大街/西
朝阳区三里屯优衣库
词性标注: 朝阳区/ns,三里屯/ns,优衣库/x
元宇宙
词性标注: 元/m,宇宙/n
长江大桥
搜索引擎模式: 长江/大桥/长江大桥
长江大桥
Tokenize:(搜索引擎模式) [{长江 0 6} {大桥 6 12} {长江大桥 0 12}]
长江大桥
Tokenize:(默认模式) [{长江大桥 0 12}]
Extract: [{长江大桥 11.1926274509}]
当然,除了 run
之外,我们最常用的命令还有 test
和 build
,本篇文章暂时不聊如何写单元测试,所以我们就先只验证 build
命令,执行 go build .
,我们将在程序目录得到一个名为 main
的可执行文件。
手动执行命令 ./main
,不出意外,将得到和上面 run
一样的输出结果。至此,Golang 环境验证也就结束啦。
最后
目前为止,我们已经聊完了“基础 Linux 环境搭建”、“Docker 环境安装和配置”、“Golang 的开发环境搭建”。
接下来的文章中,我会继续完成上篇文章中提到的几种不同的 K8S “发行版”的安装和配置,以及当今世界上流行的编程语言的环境配置。
希望对你有帮助。
--EOF
引用链接
[1]
《基础篇》: https://soulteary.com/2022/06/21/building-a-cost-effective-linux-learning-environment-on-a-laptop-the-basics.html
[2]
Golang 进行多版本管理: https://soulteary.com/2021/12/15/golang-multi-version-management.html
[3]
《M1 芯片 Mac 上更好的 Golang 使用方案》: https://soulteary.com/2022/05/12/better-golang-usage-on-m1-mac.html
[4]
这个 issue: https://github.com/golang/go/issues/12214
[5]
“goproxy.io 和 goproxy.cn 的关系”: https://github.com/goproxy/goproxy.cn/issues/61