Go + Gin 实战 RESTful API:从环境搭建(WSL/Docker/VS Code)到 Git 提交与 GitHub 托管
1、参考:使用 Go 和 Gin 构建 RESTful API(Go.dev) https://go.dev/doc/tutorial/web-service-gin — 使用 Gin 构建简单 Web 服务的 Go 官方教程。
2、安装 Go。Docker Compose 一键启动(参考 Hyperf 项目化)。我想要 “写好配置,一条命令启动整个开发环境”,用 docker-compose.yml 最为方便,和 Hyperf 的 Docker Compose 用法几乎一样。创建项目文件夹:web-service-gin
ubuntu@DESKTOP-H4MGQIU:~/wwwroot$ mkdir web-service-gin ubuntu@DESKTOP-H4MGQIU:~/wwwroot$ cd web-service-gin ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ pwd /home/ubuntu/wwwroot/web-service-gin
3、创建 docker-compose.yml
version: '3.8' services: go: image: golang:1.23-alpine container_name: go-gin-tutorial working_dir: /app volumes: - ./:/app # 本地代码挂载 ports: - "8080:8080" # 映射端口最好加引号 tty: true stdin_open: true environment: - GOPROXY=https://goproxy.io,direct command: tail -f /dev/null # 容器启动后保持运行状态
4、启动容器(进入 Go 环境), 报错:WSL Ubuntu 里只装了 Docker,没装 Docker Compose。
ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ docker-compose up -d Command 'docker-compose' not found, but can be installed with: sudo snap install docker # version 28.4.0, or sudo apt install docker-compose # version 1.29.2-1 See 'snap info docker' for additional versions. ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ sudo apt update && sudo apt install -y docker-compose [sudo] password for ubuntu: Get:1 http://security.ubuntu.com/ubuntu jammy-security InRelease [129 kB] Hit:2 https://download.docker.com/linux/ubuntu jammy InRelease Hit:3 http://archive.ubuntu.com/ubuntu jammy InRelease Hit:4 https://packages.redis.io/deb jammy InRelease Get:5 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [128 kB] Get:6 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [127 kB] Get:7 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [3328 kB] Get:8 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 c-n-f Metadata [30.4 kB] Fetched 3743 kB in 7s (575 kB/s) Reading package lists… Done Building dependency tree… Done Reading state information… Done 62 packages can be upgraded. Run 'apt list --upgradable' to see them. Reading package lists… Done Building dependency tree… Done Reading state information… Done The following additional packages will be installed: docker-ce docker-ce-cli python3-attr python3-certifi python3-chardet python3-distutils python3-docker python3-dockerpty python3-docopt python3-dotenv python3-idna python3-jsonschema python3-lib2to3 python3-pyrsistent python3-requests python3-setuptools python3-texttable python3-urllib3 python3-websocket Suggested packages: cgroupfs-mount | cgroup-lite python-attr-doc python-jsonschema-doc python3-openssl python3-socks python-requests-doc python-setuptools-doc Recommended packages: docker.io The following NEW packages will be installed: docker-compose python3-attr python3-certifi python3-chardet python3-distutils python3-docker python3-dockerpty python3-docopt python3-dotenv python3-idna python3-jsonschema python3-lib2to3 python3-pyrsistent python3-requests python3-setuptools python3-texttable python3-urllib3 python3-websocket The following packages will be upgraded: docker-ce docker-ce-cli 2 upgraded, 18 newly installed, 0 to remove and 60 not upgraded. Need to get 40.4 MB of archives. After this operation, 7757 kB of additional disk space will be used. Get:1 https://download.docker.com/linux/ubuntu jammy/stable amd64 docker-ce-cli amd64 5:29.3.1-1~ubuntu.22.04~jammy [16.4 MB] Get:2 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-lib2to3 all 3.10.8-1~22.04 [77.6 kB] Get:3 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-distutils all 3.10.8-1~22.04 [139 kB] Get:4 http://archive.ubuntu.com/ubuntu jammy/main amd64 python3-certifi all 2020.6.20-1 [150 kB] Get:5 http://archive.ubuntu.com/ubuntu jammy/main amd64 python3-chardet all 4.0.0-1 [98.0 kB] Get:6 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-idna all 3.3-1ubuntu0.1 [52.1 kB] Get:7 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-urllib3 all 1.26.5-1~exp1ubuntu0.6 [98.7 kB] Get:8 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-requests all 2.25.1+dfsg-2ubuntu0.3 [48.8 kB] Get:9 http://archive.ubuntu.com/ubuntu jammy/universe amd64 python3-websocket all 1.2.3-1 [34.7 kB] Get:10 http://archive.ubuntu.com/ubuntu jammy/universe amd64 python3-docker all 5.0.3-1 [89.3 kB] Get:11 http://archive.ubuntu.com/ubuntu jammy/universe amd64 python3-dockerpty all 0.4.1-2 [11.1 kB] Get:12 http://archive.ubuntu.com/ubuntu jammy/universe amd64 python3-docopt all 0.6.2-4 [26.9 kB] Get:13 http://archive.ubuntu.com/ubuntu jammy/universe amd64 python3-dotenv all 0.19.2-1 [20.5 kB] Get:14 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-attr all 21.2.0-1ubuntu1 [43.9 kB] Get:15 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 python3-setuptools all 59.6.0-1.2ubuntu0.22.04.3 [340 kB] Get:16 http://archive.ubuntu.com/ubuntu jammy/main amd64 python3-pyrsistent amd64 0.18.1-1build1 [55.5 kB] Get:17 http://archive.ubuntu.com/ubuntu jammy/main amd64 python3-jsonschema all 3.2.0-0ubuntu2 [43.1 kB] Get:18 http://archive.ubuntu.com/ubuntu jammy/universe amd64 python3-texttable all 1.6.4-1 [11.4 kB] Get:19 http://archive.ubuntu.com/ubuntu jammy/universe amd64 docker-compose all 1.29.2-1 [95.8 kB] Get:20 https://download.docker.com/linux/ubuntu jammy/stable amd64 docker-ce amd64 5:29.3.1-1~ubuntu.22.04~jammy [22.6 MB] Fetched 40.4 MB in 10s (3969 kB/s) (Reading database … 24579 files and directories currently installed.) Preparing to unpack …/00-docker-ce-cli_5%3a29.3.1-1~ubuntu.22.04~jammy_amd64.deb … Unpacking docker-ce-cli (5:29.3.1-1~ubuntu.22.04~jammy) over (5:29.3.0-1~ubuntu.22.04~jammy) … Preparing to unpack …/01-docker-ce_5%3a29.3.1-1~ubuntu.22.04~jammy_amd64.deb … Unpacking docker-ce (5:29.3.1-1~ubuntu.22.04~jammy) over (5:29.3.0-1~ubuntu.22.04~jammy) … Selecting previously unselected package python3-lib2to3. Preparing to unpack …/02-python3-lib2to3_3.10.8-1~22.04_all.deb … Unpacking python3-lib2to3 (3.10.8-1~22.04) … Selecting previously unselected package python3-distutils. Preparing to unpack …/03-python3-distutils_3.10.8-1~22.04_all.deb … Unpacking python3-distutils (3.10.8-1~22.04) … Selecting previously unselected package python3-certifi. Preparing to unpack …/04-python3-certifi_2020.6.20-1_all.deb … Unpacking python3-certifi (2020.6.20-1) … Selecting previously unselected package python3-chardet. Preparing to unpack …/05-python3-chardet_4.0.0-1_all.deb … Unpacking python3-chardet (4.0.0-1) … Selecting previously unselected package python3-idna. Preparing to unpack …/06-python3-idna_3.3-1ubuntu0.1_all.deb … Unpacking python3-idna (3.3-1ubuntu0.1) … Selecting previously unselected package python3-urllib3. Preparing to unpack …/07-python3-urllib3_1.26.5-1~exp1ubuntu0.6_all.deb … Unpacking python3-urllib3 (1.26.5-1~exp1ubuntu0.6) … Selecting previously unselected package python3-requests. Preparing to unpack …/08-python3-requests_2.25.1+dfsg-2ubuntu0.3_all.deb … Unpacking python3-requests (2.25.1+dfsg-2ubuntu0.3) … Selecting previously unselected package python3-websocket. Preparing to unpack …/09-python3-websocket_1.2.3-1_all.deb … Unpacking python3-websocket (1.2.3-1) … Selecting previously unselected package python3-docker. Preparing to unpack …/10-python3-docker_5.0.3-1_all.deb … Unpacking python3-docker (5.0.3-1) … Selecting previously unselected package python3-dockerpty. Preparing to unpack …/11-python3-dockerpty_0.4.1-2_all.deb … Unpacking python3-dockerpty (0.4.1-2) … Selecting previously unselected package python3-docopt. Preparing to unpack …/12-python3-docopt_0.6.2-4_all.deb … Unpacking python3-docopt (0.6.2-4) … Selecting previously unselected package python3-dotenv. Preparing to unpack …/13-python3-dotenv_0.19.2-1_all.deb … Unpacking python3-dotenv (0.19.2-1) … Selecting previously unselected package python3-attr. Preparing to unpack …/14-python3-attr_21.2.0-1ubuntu1_all.deb … Unpacking python3-attr (21.2.0-1ubuntu1) … Selecting previously unselected package python3-setuptools. Preparing to unpack …/15-python3-setuptools_59.6.0-1.2ubuntu0.22.04.3_all.deb … Unpacking python3-setuptools (59.6.0-1.2ubuntu0.22.04.3) … Selecting previously unselected package python3-pyrsistent:amd64. Preparing to unpack …/16-python3-pyrsistent_0.18.1-1build1_amd64.deb … Unpacking python3-pyrsistent:amd64 (0.18.1-1build1) … Selecting previously unselected package python3-jsonschema. Preparing to unpack …/17-python3-jsonschema_3.2.0-0ubuntu2_all.deb … Unpacking python3-jsonschema (3.2.0-0ubuntu2) … Selecting previously unselected package python3-texttable. Preparing to unpack …/18-python3-texttable_1.6.4-1_all.deb … Unpacking python3-texttable (1.6.4-1) … Selecting previously unselected package docker-compose. Preparing to unpack …/19-docker-compose_1.29.2-1_all.deb … Unpacking docker-compose (1.29.2-1) … Setting up python3-dotenv (0.19.2-1) … Setting up python3-attr (21.2.0-1ubuntu1) … Setting up python3-texttable (1.6.4-1) … Setting up python3-docopt (0.6.2-4) … Setting up python3-chardet (4.0.0-1) … Setting up python3-certifi (2020.6.20-1) … Setting up python3-idna (3.3-1ubuntu0.1) … Setting up python3-urllib3 (1.26.5-1~exp1ubuntu0.6) … Setting up docker-ce-cli (5:29.3.1-1~ubuntu.22.04~jammy) … Setting up python3-pyrsistent:amd64 (0.18.1-1build1) … Setting up python3-lib2to3 (3.10.8-1~22.04) … Setting up python3-websocket (1.2.3-1) … Setting up python3-dockerpty (0.4.1-2) … Setting up python3-distutils (3.10.8-1~22.04) … Setting up python3-setuptools (59.6.0-1.2ubuntu0.22.04.3) … Setting up python3-jsonschema (3.2.0-0ubuntu2) … Setting up docker-ce (5:29.3.1-1~ubuntu.22.04~jammy) … Setting up python3-requests (2.25.1+dfsg-2ubuntu0.3) … Setting up python3-docker (5.0.3-1) … Setting up docker-compose (1.29.2-1) … Processing triggers for man-db (2.10.2-1) … ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ docker-compose --version docker-compose version 1.29.2, build unknown ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ docker-compose up -d Creating network "web-service-gin_default" with the default driver Pulling go (golang:1.23-alpine)… 1.23-alpine: Pulling from library/golang 4f4fb700ef54: Pull complete 9824c27679d3: Pull complete 8371a51cbc44: Pull complete d5791340ef18: Pull complete d3178a7b2709: Pull complete 28f6f92c256d: Download complete d1ac4f409af1: Download complete Digest: sha256:383395b794dffa5b53012a212365d40c8e37109a626ca30d6151c8348d380b5f Status: Downloaded newer image for golang:1.23-alpine Creating go-gin-tutorial … done ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ docker-compose exec go sh /app # ls -la total 12 drwxr-xr-x 2 1000 1000 4096 Mar 30 03:36 . drwxr-xr-x 1 root root 4096 Mar 30 03:40 .. -rw-r--r-- 1 1000 1000 272 Mar 30 03:36 docker-compose.yml
5、创建一个可以管理依赖项的模块。运行该go mod init命令,并指定代码所在模块的路径。
/app # go mod init example/web-service-gin go: creating new go.mod: module example/web-service-gin
6、打算用 GoLand(Windows)直接编辑 WSL 里的 Go 代码,以配合我的 Docker 环境。我的 Go 代码存在 WSL Ubuntu 的文件夹里,GoLand 可以直接访问 WSL 文件,就像访问本地文件夹一样。现在的代码在这里:~/wwwroot/web-service-gin。在 Windows 里访问这个路径,就是:\wsl$\Ubuntu\home\ubuntu\wwwroot\web-service-gin。 GoLand 打开这个文件夹,打开 GoLand,点 Open(打开),把上面复制的路径直接粘贴到文件夹地址栏,按回车,选择文件夹 → OK。现在就能在 Windows GoLand 里编辑、修改、保存 WSL 里的 Go 代码了。而且修改会实时同步到 Docker 容器里!如图1

7、配置 GoLand 连接 WSL 里的 Docker(关键)
重启后:
文件 → 设置 → 构建、执行、部署 → Docker
左上角 + 添加连接
连接类型选择:WSL(重点:不要选 Docker Desktop)
发行版下拉选择:Ubuntu
底部显示「连接成功」即可
应用 → 确定。如图2

8、文件 → 设置 → 运行目标,添加 → 选 Docker Compose,点击右上角 +选择:✅ Docker Compose。运行目标(Docker Compose)配置完成后,GoLand 会获得:只管【运行】,不管【代码提示】
它的作用只有:
点 ▶️ 运行时
代码在 Docker 容器里执行。如图3

9、项目默认目标:go。如图4

10、代码提示、自动补全、语法识别 必须靠 GOROOT
GoLand 要想识别:
string
int
fmt
函数跳转
自动补全
必须配置:本地 GOROOT这是 IDE 硬性规则。打开 WSL Ubuntu 终端,复制运行:
<h1>1. 下载 Go 1.23(和容器版本一样)</h1> wget https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz <h1>2. 安装到 Linux 标准路径:/usr/local/go</h1> sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz <h1>3. 配置环境变量</h1> echo 'export PATH=$PATH:/usr/local/go/bin' &gt;&gt; ~/.bashrc source ~/.bashrc <h1>4. 验证安装成功</h1> go version
11、打开 GoLand 设置 → Go → GOROOT
点 + 添加
选择 WSL
自动找到 /usr/local/go
确定 → 应用。如图5

12、创建数据,创建一个名为 main.go 的文件。将在这个文件中编写 Go 代码。
package main
import (
"net/http"
<pre><code>"github.com/gin-gonic/gin"</code></pre>
)
// album represents data about a record album.
type album struct {
ID string <code>json:"id"</code>
Title string <code>json:"title"</code>
Artist string <code>json:"artist"</code>
Price float64 <code>json:"price"</code>
}
// albums slice to seed record album data.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
<pre><code>router.Run("0.0.0.0:8080") // 监听所有网卡</code></pre>
}
// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
13、现在 IDE 有一些报错的提示:
无法解析符号’github.com’
无法解析符号’gin-gonic’
无法解析符号’gin’
未解析的引用 ‘Default’。如图6

14、打开 WSL 终端,在 WSL 直接执行:go get github.com/gin-gonic/gin
ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ go get github.com/gin-gonic/gin go: downloading github.com/gin-gonic/gin v1.12.0 go: github.com/gin-gonic/gin@v1.12.0 requires go &gt;= 1.25.0; switching to go1.25.8 go: downloading github.com/gin-contrib/sse v1.1.0 go: downloading github.com/mattn/go-isatty v0.0.20 go: downloading github.com/quic-go/quic-go v0.59.0 go: downloading golang.org/x/net v0.51.0 go: downloading github.com/bytedance/sonic v1.15.0 go: downloading github.com/goccy/go-json v0.10.5 go: downloading github.com/json-iterator/go v1.1.12 go: downloading github.com/goccy/go-yaml v1.19.2 go: downloading github.com/pelletier/go-toml/v2 v2.2.4 go: downloading github.com/ugorji/go/codec v1.3.1 go: downloading go.mongodb.org/mongo-driver/v2 v2.5.0 go: downloading google.golang.org/protobuf v1.36.10 go: downloading github.com/go-playground/validator/v10 v10.30.1 go: downloading golang.org/x/sys v0.41.0 go: downloading github.com/quic-go/qpack v0.6.0 go: downloading github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd go: downloading github.com/modern-go/reflect2 v1.0.2 go: downloading github.com/gabriel-vasile/mimetype v1.4.12 go: downloading github.com/go-playground/universal-translator v0.18.1 go: downloading github.com/leodido/go-urn v1.4.0 go: downloading golang.org/x/crypto v0.48.0 go: downloading golang.org/x/text v0.34.0 go: downloading github.com/bytedance/gopkg v0.1.3 go: downloading github.com/cloudwego/base64x v0.1.6 go: downloading golang.org/x/arch v0.22.0 go: downloading github.com/go-playground/locales v0.14.1 go: downloading github.com/klauspost/cpuid/v2 v2.3.0 go: downloading github.com/bytedance/sonic/loader v0.5.0 go: downloading github.com/twitchyliquid64/golang-asm v0.15.1 go: updating go.mod: open /home/ubuntu/wwwroot/web-service-gin/go.mod: permission denied
15、反省一下,感觉现在这样太痛苦了。这种 “容器里装依赖、容器外装依赖” 的用法,是错误、混乱、反人类的!** 容器的优势就是环境统一、隔离、不用乱装东西。现在被用成了:一边容器、一边 WSL,精神分裂!真正正确的开发模式只有两种:只用 WSL 的 Go,不用 Docker 跑开发;全部在 Docker 里,WSL 什么都不装;
16、仔细排查原因,找到了根源,在 文件 → 设置 → 构建、执行、部署 → Docker 中,虚拟机路径:/home/ubuntu/wwwroot/web-service-gin,这个路径是 WSL 中的路径。** 正确的路径 不应该是 WSL 路径!应该是【容器内部的路径】!**。虚拟机路径修改为:/app。如图7

17、发现 GOROOT 中仍然不提供 Docker 的选项。GoLand 在「没有 Docker Desktop」的环境下,就是无法直接选择容器内的 GOROOT!最终决定,放弃 GoLand,改用 VS Code + Dev Containers
18、Windows 安装 VS Code,VS Code 扩展商店安装:Dev Containers(微软官方)、WSL、Docker
19、启动容器,报错:ERROR: for go ‘ContainerConfig’,这是老容器 + Python V1 的兼容性问题
ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ docker-compose up -d
Recreating 814a800e96e6_go-gin-tutorial …
ERROR: for 814a800e96e6_go-gin-tutorial 'ContainerConfig'
ERROR: for go 'ContainerConfig'
Traceback (most recent call last):
File "/usr/bin/docker-compose", line 33, in <module>
sys.exit(load_entry_point('docker-compose==1.29.2', 'console_scripts', 'docker-compose')())
File "/usr/lib/python3/dist-packages/compose/cli/main.py", line 81, in main
command_func()
File "/usr/lib/python3/dist-packages/compose/cli/main.py", line 203, in perform_command
handler(command, command_options)
File "/usr/lib/python3/dist-packages/compose/metrics/decorator.py", line 18, in wrapper
result = fn(*args, **kwargs)
File "/usr/lib/python3/dist-packages/compose/cli/main.py", line 1186, in up
to_attach = up(False)
File "/usr/lib/python3/dist-packages/compose/cli/main.py", line 1166, in up
return self.project.up(
File "/usr/lib/python3/dist-packages/compose/project.py", line 697, in up
results, errors = parallel.parallel_execute(
File "/usr/lib/python3/dist-packages/compose/parallel.py", line 108, in parallel_execute
raise error_to_reraise
File "/usr/lib/python3/dist-packages/compose/parallel.py", line 206, in producer
result = func(obj)
File "/usr/lib/python3/dist-packages/compose/project.py", line 679, in do
return service.execute_convergence_plan(
File "/usr/lib/python3/dist-packages/compose/service.py", line 579, in execute_convergence_plan
return self._execute_convergence_recreate(
File "/usr/lib/python3/dist-packages/compose/service.py", line 499, in _execute_convergence_recreate
containers, errors = parallel_execute(
File "/usr/lib/python3/dist-packages/compose/parallel.py", line 108, in parallel_execute
raise error_to_reraise
File "/usr/lib/python3/dist-packages/compose/parallel.py", line 206, in producer
result = func(obj)
File "/usr/lib/python3/dist-packages/compose/service.py", line 494, in recreate
return self.recreate_container(
File "/usr/lib/python3/dist-packages/compose/service.py", line 612, in recreate_container
new_container = self.create_container(
File "/usr/lib/python3/dist-packages/compose/service.py", line 330, in create_container
container_options = self._get_container_create_options(
File "/usr/lib/python3/dist-packages/compose/service.py", line 921, in _get_container_create_options
container_options, override_options = self._build_container_volume_options(
File "/usr/lib/python3/dist-packages/compose/service.py", line 960, in _build_container_volume_options
binds, affinity = merge_volume_bindings(
File "/usr/lib/python3/dist-packages/compose/service.py", line 1548, in merge_volume_bindings
old_volumes, old_mounts = get_container_data_volumes(
File "/usr/lib/python3/dist-packages/compose/service.py", line 1579, in get_container_data_volumes
container.image_config['ContainerConfig'].get('Volumes') or {}
KeyError: 'ContainerConfig'
20、进入目录:~/wwwroot/web-service-gin,执行 :code .。是否信任此文件夹中的文件的作者? 是。如图8

ubuntu@DESKTOP-H4MGQIU:~/wwwroot/web-service-gin$ code . Installing VS Code Server for Linux x64 (cfbea10c5ffb233ea9177d34726e6056e89913dc) Downloading: 100% Unpacking: 100% Unpacked 3690 files and folders to /home/ubuntu/.vscode-server/bin/cfbea10c5ffb233ea9177d34726e6056e89913dc. Looking for compatibility check script at /home/ubuntu/.vscode-server/bin/cfbea10c5ffb233ea9177d34726e6056e89913dc/bin/helpers/check-requirements.sh Running compatibility check script Compatibility check successful (0)
21、删除 .idea 目录,这是 Goland 留下的。准备 Dev Container 配置文件,直接用已有 Docker 镜像,新建:.devcontainer/devcontainer.json
[json]
{
“name”: “Go Gin Dev Container”,
“dockerComposeFile”: [“../docker-compose.yml”],
“service”: “go”,
“workspaceFolder”: “/app”,
“settings”: {
“terminal.integrated.shell.linux”: “/bin/sh”
},
“extensions”: [
“golang.Go”
],
“forwardPorts”: [8080],
“remoteUser”: “root”
}
[/json]
解释:
dockerComposeFile 指向 docker-compose.yml
service 是希望 VS Code 连接的服务(这里是 go)
workspaceFolder 是代码挂载目录 /app
extensions 自动在容器里安装 Go 插件
forwardPorts 映射容器端口到 VS Code
remoteUser 可选,如果容器里默认用户不是 root
22、VS Code 打开容器
打开 VS Code,进入项目目录
按 F1 → 输入 Dev Containers: Reopen in Container
VS Code 会根据 devcontainer.json 和 docker-compose.yml 启动容器并挂载代码
打开终端,会发现直接在容器里(/app),Go 版本和 Docker 镜像一致
⚡ 提示:如果容器启动慢,可以先用 docker-compose up -d 启动容器,再用 Dev Container 连接。如图9

23、开发流程建议
用 Dev Container 打开项目
直接在容器终端运行 Go 或 PHP 命令
容器里安装依赖(go mod tidy、composer install 等)
在 VS Code 中调试、运行、测试
不需要关心 WSL 上是否安装 Go/PHP
24、多项目 + 多容器流程图
┌─────────────────────────────────────────────────────────────┐
│ VS Code │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Project-Go │ │ Project-PHP74 │ │ Project-PHP81 │ │
│ │ Workspace │ │ Workspace │ │ Workspace │ │
│ │ (.code-workspace) │ │ (.code-workspace) │ │ (.code-workspace) │ │
│ │ Dev Container │ │ Dev Container │ │ Dev Container │ │
│ │ Service: go │ │ Service: php74 │ │ Service: php81 │ │
│ │ Image: golang:1.23 │ │ Image: php:7.4-fpm │ │ Image: php:8.1-fpm │ │
│ │ Ports: 8080 │ │ Ports: 8000 │ │ Ports: 8001 │ │
│ └─────────────┬───────┘ └─────────────┬───────┘ └─────────────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Docker Container Go │ │ Docker Container │ │ Docker Container │ │
│ │ Version: 1.23 │ │ PHP 7.4 FPM │ │ PHP 8.1 FPM │ │
│ │ /app ← 挂载项目代码 │ │ /app ← 挂载项目代码 │ │ /app ← 挂载项目代码 │ │
│ │ 端口 8080 映射本地 │ │ 端口 8000 映射本地 │ │ 端口 8001 映射本地 │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
说明:
- 每个 VS Code 窗口对应一个独立 Workspace(项目)
- 每个 Workspace 使用独立 Dev Container 连接对应 Docker 容器
- 容器内挂载 /app 到对应项目代码,开发和运行环境完全一致
- 不同项目端口独立映射,本地访问互不冲突
25、以下是 web-service-gin 项目的完整流程图,显示 VS Code、Dev Container、Docker 容器、挂载路径和端口映射,完全贴合本地开发环境。
┌───────────────────────────────────────────────────────────────────────────────┐
│ VS Code Window │
│ (web-service-gin Workspace) │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Dev Container (Go) │ │
│ │ Service: go │ │
│ │ Image: golang:1.23-alpine │ │
│ │ WorkspaceFolder: /app │ │
│ │ Extensions: golang.Go │ │
│ │ ForwardPorts: 8080 │ │
│ │ RemoteUser: root │ │
│ └───────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Docker Container (Go) │ │
│ │ Container Name: go-gin-tutorial │ │
│ │ Go Version: 1.23 │ │
│ │ Command: tail -f /dev/null │ │
│ │ Working Dir: /app │ │
│ │ Volume Mount: ./ (本地代码) → /app │ │
│ │ Port Mapping: 8080 → 8080 (本地访问 Gin Web) │ │
│ │ Environment: GOPROXY=https://goproxy.io,direct │ │
│ └───────────────────────────────────────────────┘ │
│ │
│ 编辑流程:VS Code 编辑器 → Dev Container → Docker 容器运行 → 本地端口访问 Web │
└───────────────────────────────────────────────────────────────────────────────┘
26、以下是 宽屏版多项目组合图,展示 Go + PHP74 + PHP81 三个项目的 VS Code Workspace、Dev Container、Docker 容器、挂载路径和端口映射,横向排列,方便直观理解多项目、多语言、多容器的本地开发环境:
┌───────────────────────────────────────────────────────────────────────────────────────────────┐
│ VS Code Windows │
│ (各自 Workspace 独立窗口) │
│ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ Project-Go Workspace │ │ Project-PHP74 Workspace │ │ Project-PHP81 Workspace │ │
│ │ (.code-workspace) │ │ (.code-workspace) │ │ (.code-workspace) │ │
│ │ Dev Container: go │ │ Dev Container: php74 │ │ Dev Container: php81 │ │
│ │ Image: golang:1.23-alpine │ │ Image: php:7.4-fpm │ │ Image: php:8.1-fpm │ │
│ │ ForwardPorts: 8080 │ │ ForwardPorts: 8000 │ │ ForwardPorts: 8001 │ │
│ │ RemoteUser: root │ │ RemoteUser: root │ │ RemoteUser: root │ │
│ └───────────────┬─────────────┘ └───────────────┬─────────────┘ └───────────────┬─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ Docker Container Go │ │ Docker Container PHP 7.4 │ │ Docker Container PHP 8.1 │ │
│ │ Name: go-gin-tutorial │ │ Name: php74-dev │ │ Name: php81-dev │ │
│ │ Version: 1.23 │ │ Version: 7.4 FPM │ │ Version: 8.1 FPM │ │
│ │ Working Dir: /app │ │ Working Dir: /app │ │ Working Dir: /app │ │
│ │ Volume Mount: ./ → /app │ │ Volume Mount: ./ → /app │ │ Volume Mount: ./ → /app │ │
│ │ Port Mapping: 8080 → 8080 │ │ Port Mapping: 8000 → 8000 │ │ Port Mapping: 8001 → 8001 │ │
│ │ Command: tail -f /dev/null │ │ Command: tail -f /dev/null │ │ Command: tail -f /dev/null │ │
│ │ Environment: GOPROXY=… │ │ │ │ │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘ │
│ │
│ 开发流程说明: │
│ VS Code 编辑 → Dev Container → Docker 容器运行 → 挂载代码 → 本地端口访问 Web/服务 │
│ 多项目、多语言版本独立,端口互不冲突,环境一致 │
└───────────────────────────────────────────────────────────────────────────────────────────────┘
图示说明
- VS Code Windows / Workspace
每个项目对应一个 VS Code 窗口
.code-workspace 配置独立
插件、调试、终端完全隔离 - Dev Container
连接对应 Docker 容器
WorkspaceFolder 映射 /app
ForwardPorts 映射本地访问端口
插件和调试工具在容器内部运行 - Docker 容器
Go / PHP 版本与镜像一致
挂载本地代码,容器内修改即生效
容器端口映射到本地端口,访问服务方便
每个容器独立运行,不会影响其他项目 - 开发流程
在 VS Code 编辑器编辑代码
代码挂载到 Dev Container → Docker 容器
容器运行服务或编译命令
本地浏览器访问对应端口
优势
每个项目都是独立环境,类似 PHPStorm SDK + 项目隔离
多语言版本支持(Go + PHP74 + PHP81)
端口映射互不冲突,可同时运行多个服务
VS Code 窗口对应项目 Workspace,完全独立
27、我现在要如何将 APP [开发容器: GO GIN DEV CONTAINER] 转换为一个工作区呢?在 VS Code 中直接引用容器路径
打开 命令面板(Ctrl+Shift+P / Cmd+Shift+P)
输入 Save Workspace As…
点击 显示本地,保存至 C:\Users\Thinkpad\VSCodeWorkspaces\go-gin-tutorial-app.code-workspace。如图10
![我现在要如何将 APP [开发容器: GO GIN DEV CONTAINER] 转换为一个工作区呢?](https://www.shuijingwanwq.com/wp-content/uploads/2026/04/10.jpg)
28、新开 vs code 后,文件 – 从文件打开工作区 – 显示本地 – C:\Users\Thinkpad\VSCodeWorkspaces\go-gin-tutorial-app.code-workspace。
29、现在在 vs code 中,问题:could not import github.com/gin-gonic/gin (no required module provides package “github.com/gin-gonic/gin”)。这是 Go Modules 依赖没安装或者未初始化 的典型错误。如图11

30、运行代码 开始将 Gin 模块作为依赖项进行跟踪。在命令行中,使用该go get 命令将 github.com/gin-gonic/gin 模块添加为模块的依赖项。使用点号参数表示“获取当前目录中代码的依赖项”。在 VS Code 里进入容器执行命令,默认打开的就是容器内的终端,路径应该是 /app,Go 已解析并下载此依赖项,以满足import 在上一步中添加的声明。如图12

/app # pwd /app /app # go get . go: github.com/gin-gonic/gin@v1.12.0 requires go &gt;= 1.25.0 (running go 1.23.12; GOTOOLCHAIN=local) /app #
31、在包含 main.go 的目录中,从命令行运行代码。使用点号参数表示“在当前目录中运行代码”。报错:
/app # go run . main.go:6:2: no required module provides package github.com/gin-gonic/gin; to add it: go get github.com/gin-gonic/gin /app #
32、找到了根源,当前容器里的 Go 版本是 1.23.12,Gin v1.12.0 需要 Go ≥ 1.25.0,所以即使执行了 go get . 或 go get github.com/gin-gonic/gin,也会失败,VS Code 就提示:no required module provides package github.com/gin-gonic/gin。
33、编辑 docker-compose.yml,升级 Go 镜像版本,golang:1.23-alpine 修改为 golang:1.26-alpine
version: '3.8' services: go: image: golang:1.26-alpine container_name: go-gin-tutorial working_dir: /app volumes: - ./:/app # 本地代码挂载 ports: - "8080:8080" # 映射端口最好加引号 tty: true stdin_open: true environment: - GOPROXY=https://goproxy.io,direct command: tail -f /dev/null # 容器启动后保持运行状态
34、容器重建流程(VS Code 内完成)
修改 docker-compose.yml 升级 Go 镜像版本
命令面板 Ctrl+Shift+P → 输入:
Dev Containers: Rebuild and Reopen in Container
VS Code 会销毁旧容器并基于新镜像创建新容器
打开终端执行第 4 步命令,Gin 环境就绪
✅ 工作区 .code-workspace 和本地代码不会丢失
✅ 容器里的 Go 版本和 Gin 依赖都是最新
35、再次执行 go get . 命令,未提示版本问题,正确执行。
/app # go get . go: downloading github.com/gin-gonic/gin v1.12.0 go: downloading github.com/gin-contrib/sse v1.1.0 go: downloading github.com/mattn/go-isatty v0.0.20 go: downloading golang.org/x/net v0.51.0 go: downloading github.com/quic-go/quic-go v0.59.0 go: downloading github.com/go-playground/validator/v10 v10.30.1 go: downloading github.com/goccy/go-yaml v1.19.2 go: downloading github.com/pelletier/go-toml/v2 v2.2.4 go: downloading github.com/bytedance/sonic v1.15.0 go: downloading github.com/ugorji/go/codec v1.3.1 go: downloading github.com/goccy/go-json v0.10.5 go: downloading go.mongodb.org/mongo-driver/v2 v2.5.0 go: downloading github.com/json-iterator/go v1.1.12 go: downloading google.golang.org/protobuf v1.36.10 go: downloading golang.org/x/sys v0.41.0 go: downloading github.com/quic-go/qpack v0.6.0 go: downloading github.com/gabriel-vasile/mimetype v1.4.12 go: downloading github.com/go-playground/universal-translator v0.18.1 go: downloading github.com/leodido/go-urn v1.4.0 go: downloading golang.org/x/crypto v0.48.0 go: downloading golang.org/x/text v0.34.0 go: downloading github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd go: downloading github.com/modern-go/reflect2 v1.0.2 go: downloading github.com/go-playground/locales v0.14.1 go: downloading github.com/bytedance/gopkg v0.1.3 go: downloading github.com/cloudwego/base64x v0.1.6 go: downloading golang.org/x/arch v0.22.0 go: downloading github.com/klauspost/cpuid/v2 v2.3.0 go: downloading github.com/bytedance/sonic/loader v0.5.0 go: downloading github.com/twitchyliquid64/golang-asm v0.15.1 go: upgraded go 1.23.12 =&gt; 1.25.0 go: added github.com/bytedance/gopkg v0.1.3 go: added github.com/bytedance/sonic v1.15.0 go: added github.com/bytedance/sonic/loader v0.5.0 go: added github.com/cloudwego/base64x v0.1.6 go: added github.com/gabriel-vasile/mimetype v1.4.12 go: added github.com/gin-contrib/sse v1.1.0 go: added github.com/gin-gonic/gin v1.12.0 go: added github.com/go-playground/locales v0.14.1 go: added github.com/go-playground/universal-translator v0.18.1 go: added github.com/go-playground/validator/v10 v10.30.1 go: added github.com/goccy/go-json v0.10.5 go: added github.com/goccy/go-yaml v1.19.2 go: added github.com/json-iterator/go v1.1.12 go: added github.com/klauspost/cpuid/v2 v2.3.0 go: added github.com/leodido/go-urn v1.4.0 go: added github.com/mattn/go-isatty v0.0.20 go: added github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd go: added github.com/modern-go/reflect2 v1.0.2 go: added github.com/pelletier/go-toml/v2 v2.2.4 go: added github.com/quic-go/qpack v0.6.0 go: added github.com/quic-go/quic-go v0.59.0 go: added github.com/twitchyliquid64/golang-asm v0.15.1 go: added github.com/ugorji/go/codec v1.3.1 go: added go.mongodb.org/mongo-driver/v2 v2.5.0 go: added golang.org/x/arch v0.22.0 go: added golang.org/x/crypto v0.48.0 go: added golang.org/x/net v0.51.0 go: added golang.org/x/sys v0.41.0 go: added golang.org/x/text v0.34.0 go: added google.golang.org/protobuf v1.36.10 /app #
36、在包含 main.go 的目录中,从命令行运行代码。使用点号参数表示“在当前目录中运行代码”。
/app # go run .
代码运行后,就拥有了一个可以向其发送请求的 HTTP 服务器。
37、Windows 浏览器访问:http://localhost:8080/albums ,响应状态:(失败)net::ERR_EMPTY_RESPONSE。修改 main.go,router.Run(“0.0.0.0:8080”) // 监听所有网卡。显示预先提供给服务的数据。如图13

[
{
"id": "1",
"title": "Blue Train",
"artist": "John Coltrane",
"price": 56.99
},
{
"id": "2",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
},
{
"id": "3",
"title": "Sarah Vaughan and Clifford Brown",
"artist": "Sarah Vaughan",
"price": 39.99
}
]
68、生成 GitHub 的 Personal Access Token(个人访问令牌)。
我30 秒手把手教生成 Token,一次搞定,以后再也不用输密码!
一步一步生成 GitHub Token
打开 GitHub 右上角头像 → Settings
左侧最下面 → Developer settings
点击 Personal access tokens → Tokens (classic)
点击右上角 Generate new token → Generate new token (classic)
填写:
Note:填 go-gin-learning(随便写,方便自己记)
Expiration:选 90 days 或者 No expiration
Select scopes:勾选 repo(全勾第一项就行)
拉到最下面 → Generate token
复制生成的一串以 ghp_ 开头的代码(只显示一次,关掉就没了!)如图14

69、决定将代码提交至 GitHub,创建仓库:https://github.com/shuijingwan/go-gin-learning 。在容器里的 /app 目录(vs code 下的终端),直接执行下面 全部命令:
/app # git init /bin/sh: git: not found /app # apk add git ( 1/13) Installing brotli-libs (1.2.0-r0) ( 2/13) Installing c-ares (1.34.6-r0) ( 3/13) Installing libunistring (1.4.1-r0) ( 4/13) Installing libidn2 (2.3.8-r0) ( 5/13) Installing nghttp2-libs (1.68.0-r0) ( 6/13) Installing nghttp3 (1.13.1-r0) ( 7/13) Installing libpsl (0.21.5-r3) ( 8/13) Installing zstd-libs (1.5.7-r2) ( 9/13) Installing libcurl (8.17.0-r1) (10/13) Installing libexpat (2.7.5-r0) (11/13) Installing pcre2 (10.47-r0) (12/13) Installing git (2.52.0-r0) (13/13) Installing git-init-template (2.52.0-r0) Executing busybox-1.37.0-r30.trigger OK: 23.4 MiB in 32 packages /app # git init hint: Using 'master' as the name for the initial branch. This default branch name hint: will change to "main" in Git 3.0. To configure the initial branch name hint: to use in all of your new repositories, which will suppress this warning, hint: call: hint: hint: git config --global init.defaultBranch <name> hint: hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and hint: 'development'. The just-created branch can be renamed via this command: hint: hint: git branch -m <name> hint: hint: Disable this message with "git config set advice.defaultBranchName false" Initialized empty Git repository in /app/.git/ /app # echo "# go-gin-learning" &gt;&gt; README.md /app # cat &gt; .gitignore &lt;&lt; 'EOF' <blockquote>/bin /dist /vendor/ .env .env.test .idea/ .vscode/ .DS_Store EOF /app # git add . fatal: detected dubious ownership in repository at '/app' To add an exception for this directory, call:</blockquote> <pre><code> git config --global --add safe.directory /app</code></pre> /app # git config --global --add safe.directory /app /app # git add . /app # git commit -m "feat: 初始化Go Gin项目,集成容器开发环境" Author identity unknown *** Please tell me who you are. Run git config --global user.email "you@example.com" git config --global user.name "Your Name" to set your account's default identity. Omit --global to set the identity only in this repository. fatal: unable to auto-detect email address (got 'root@e4dccfba01fb.(none)') /app # git config --global user.name "shuijingwan" /app # git config --global user.email "shuijingwanwq@163.com" /app # git commit -m "feat: 初始化Go Gin项目,集成容器开发环境" [master (root-commit) 023e682] feat: 初始化Go Gin项目,集成容器开发环境 7 files changed, 188 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go /app # git branch -M main /app # git remote add origin https://github.com/shuijingwan/go-gin-learning.git /app # git push -u origin main Username for 'https://github.com': shuijingwan Password for 'https://shuijingwan@github.com': Enumerating objects: 10, done. Counting objects: 100% (10/10), done. Delta compression using up to 4 threads Compressing objects: 100% (8/8), done. Writing objects: 100% (10/10), 5.21 KiB | 313.00 KiB/s, done. Total 10 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0) remote: Resolving deltas: 100% (1/1), done. To https://github.com/shuijingwan/go-gin-learning.git <ul> <li>[new branch] main -&gt; main branch 'main' set up to track 'origin/main'. /app #
70、查看 GitHub 上的仓库目录,发现 .devcontainer 被提交了。现在仓库目录结构如下
/go-gin-learning
├── .devcontainer/ ✅ 应该提交
│ └── devcontainer.json
├── .gitignore ✅ 应该提交
├── README.md ✅ 应该提交
├── docker-compose.yml ✅ 应该提交
├── go.mod
├── go.sum
└── main.go