没有不值得去解决的问题,也没有不值得去学习的技术!

Build RESTful APIs With Go and Gin: From Environment Setup to GitHub Hosting

Access via Windows browser: http://localhost:8080/albums

1. Reference: Building RESTful APIs with Go and Gin (Go.dev) https://go.dev/doc/tutorial/web-service-gin — The official Go tutorial for building a simple web service using Gin.

2. Install Go. Use Docker Compose for one-click startup (referencing the Hyperf project setup). I want to “write the configuration and start the entire development environment with a single command,” so using docker-compose.yml is the most convenient approach, almost identical to how Docker Compose is used with Hyperf. Create the project folder: web-service-gin

Plaintext

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. Create docker-compose.yml

YAML

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. Start the container (to enter the Go environment). Error: Only Docker is installed in WSL Ubuntu, Docker Compose is not.

Plaintext

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. Create a module to manage dependencies. Run the go mod init command, specifying the module path where your code will reside.

Plaintext

/app # go mod init example/web-service-gin
go: creating new go.mod: module example/web-service-gin

6. I plan to use GoLand (on Windows) to directly edit the Go code in WSL to work with my Docker environment. My Go code is stored in a folder inside WSL Ubuntu, and GoLand can access WSL files directly, just like accessing local folders. The code is currently here: ~/wwwroot/web-service-gin. To access this path from Windows, it is: \wsl$\Ubuntu\home\ubuntu\wwwroot\web-service-gin. Open this folder in GoLand by launching GoLand, clicking Open, pasting the path copied above directly into the folder address bar, pressing Enter, selecting the folder → OK. You can now edit, modify, and save the Go code in WSL from GoLand on Windows. Moreover, modifications will sync to the Docker container in real time! See Figure 1.

To access this path from Windows, it is: \wsl$\Ubuntu\home\ubuntu\wwwroot\web-service-gin

7. Configure GoLand to connect to Docker in WSL (Key step)
After restarting:
File → Settings → Build, Execution, Deployment → Docker
Click + in the top left corner to add a connection
For Connection type, select: WSL (Important: Do not select Docker Desktop)
For the Distribution dropdown, select: Ubuntu
Once “Connection successful” appears at the bottom, it’s ready
Apply → OK. See Figure 2.

Configure GoLand to connect to Docker in WSL

8. File → Settings → Run Targets, Add → select Docker Compose, click + in the top right corner and choose: ✅ Docker Compose. Once the Run Target (Docker Compose) is configured, GoLand will: only handle [Running], not [Code completion]
Its only functions are:
When you click ▶️ Run
The code executes inside the Docker container. See Figure 3.

File → Settings → Run Targets, Add → select Docker Compose

9. Default project target: go. See Figure 4.

Default project target: go

10. Code completion, auto-completion, and syntax recognition rely entirely on GOROOT
For GoLand to recognize:
string
int
fmt
Function jumping
Auto-completion
You must configure: Local GOROOT. This is a strict IDE rule. Open the WSL Ubuntu terminal, copy and run:

Bash

# 1. 下载 Go 1.23(和容器版本一样)
wget https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz

# 2. 安装到 Linux 标准路径:/usr/local/go
sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz

# 3. 配置环境变量
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc

# 4. 验证安装成功
go version

11. Open GoLand Settings → Go → GOROOT
Click + to add
Select WSL
It will automatically find /usr/local/go
OK → Apply. See Figure 5.

Open GoLand Settings → Go → GOROOT

12. Create the data and create a file named main.go. You will write the Go code in this file.

Go

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// album represents data about a record album.
type album struct {
	ID     string  `json:"id"`
	Title  string  `json:"title"`
	Artist string  `json:"artist"`
	Price  float64 `json:"price"`
}

// 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)

	router.Run("0.0.0.0:8080") // 监听所有网卡
}

// getAlbums responds with the list of all albums as JSON.
func getAlbums(c *gin.Context) {
	c.IndentedJSON(http.StatusOK, albums)
}

13. Now the IDE shows some error prompts:

Cannot resolve symbol ‘github.com’
Cannot resolve symbol ‘gin-gonic’
Cannot resolve symbol ‘gin’
Unresolved reference ‘Default’. See Figure 6.

Now the IDE shows some error prompts

14. Open the WSL terminal and execute directly in WSL: go get github.com/gin-gonic/gin

Plaintext

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 >= 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. Reflecting on this, I feel the current approach is too painful. This usage of “installing dependencies inside the container and outside the container” is wrong, messy, and anti-human! ** The advantage of containers is a unified, isolated environment where you don’t have to install things haphazardly. Now it’s being used as: half container, half WSL—schizophrenic! There are only two truly correct development modes: use only WSL’s Go without Docker for development; or do everything inside Docker and install nothing in WSL;

16. After carefully investigating the cause, I found the root of the problem. In File → Settings → Build, Execution, Deployment → Docker, the Virtual machine path: /home/ubuntu/wwwroot/web-service-gin, is a WSL path. ** The correct path should not be a WSL path! It should be the [path inside the container]! **. Change the Virtual machine path to: /app. See Figure 7.

Change the Virtual machine path to: /app

17. I found that GOROOT still does not provide a Docker option. GoLand simply cannot directly select the GOROOT inside a container in an environment “without Docker Desktop”! I ultimately decided to abandon GoLand and switch to VS Code + Dev Containers.

18. Install VS Code on Windows, and install from the VS Code extension marketplace: Dev Containers (official Microsoft), WSL, and Docker.

19. Start the container, error: ERROR: for go ‘ContainerConfig’. This is a compatibility issue with old containers and Python V1.

Plaintext

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 
    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. Navigate to the directory: ~/wwwroot/web-service-gin, and execute: code . . Do you trust the authors of the files in this folder? Yes. See Figure 8.

Navigate to the directory: ~/wwwroot/web-service-gin, and execute: code . . Do you trust the authors of the files in this folder? Yes
Plaintext

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. Delete the .idea directory, which was left by GoLand. Prepare the Dev Container configuration file, directly using the existing Docker image, and create: .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"
}

Explanation:

dockerComposeFile points to docker-compose.yml
service is the service you want VS Code to connect to (here, go)
workspaceFolder is the code mount directory /app
extensions automatically installs the Go extension in the container
forwardPorts maps the container port to VS Code
remoteUser is optional, if the default user in the container is not root

22. Open the container in VS Code
Open VS Code and navigate to the project directory
Press F1 → type Dev Containers: Reopen in Container
VS Code will start the container and mount the code based on devcontainer.json and docker-compose.yml
Open the terminal, and you will find yourself directly inside the container (/app), with the Go version matching the Docker image

⚡ Tip: If the container starts slowly, you can start it first with docker-compose up -d, and then connect using Dev Container. See Figure 9.

Open the container in VS Code

23. Recommended development workflow
Open the project using Dev Container
Run Go or PHP commands directly in the container terminal
Install dependencies inside the container (go mod tidy, composer install, etc.)
Debug, run, and test in VS Code
No need to worry about whether Go/PHP is installed on WSL

24. Multi-project + multi-container workflow diagram

┌─────────────────────────────────────────────────────────────┐
│ 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 ← Mount project code │ │ /app ← Mount project code │ │ /app ← Mount project code │ │
│ │ Port 8080 mapped to local │ │ Port 8000 mapped to local │ │ Port 8001 mapped to local │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Notes:

  • Each VS Code window corresponds to an independent Workspace (project)
  • Each Workspace uses an independent Dev Container to connect to the corresponding Docker container
  • Mounting /app inside the container to the corresponding project code ensures the development and runtime environments are completely identical
  • Different project ports are mapped independently, so local access does not conflict

25. Below is the complete workflow diagram for the web-service-gin project, showing VS Code, Dev Container, Docker container, mount paths, and port mapping, fully aligned with the local development environment.

┌───────────────────────────────────────────────────────────────────────────────┐
│ 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: ./ (local code) → /app │ │
│ │ Port Mapping: 8080 → 8080 (local access to Gin Web) │ │
│ │ Environment: GOPROXY=https://goproxy.io,direct │ │
│ └───────────────────────────────────────────────┘ │
│ │
│ Editing workflow: VS Code editor → Dev Container → Docker container runtime → local port access to Web │
└───────────────────────────────────────────────────────────────────────────────┘

26. Below is a widescreen multi-project combination diagram, showing the VS Code Workspace, Dev Container, Docker container, mount paths, and port mapping for three projects: Go + PHP74 + PHP81. They are arranged horizontally to provide an intuitive understanding of a local development environment with multiple projects, multiple languages, and multiple containers:

┌───────────────────────────────────────────────────────────────────────────────────────────────┐
│ VS Code Windows │
│ (Independent windows for each 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=… │ │ │ │ │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ └─────────────────────────────┘ │
│ │
│ Development workflow notes: │
│ VS Code editing → Dev Container → Docker container runtime → mount code → local port access to Web/service │
│ Multiple projects and multiple language versions are independent, ports do not conflict, and environments are consistent │
└───────────────────────────────────────────────────────────────────────────────────────────────┘

Diagram description

  1. VS Code Windows / Workspace
    Each project corresponds to a VS Code window
    .code-workspace configuration is independent
    Extensions, debugging, and terminal are completely isolated
  2. Dev Container
    Connects to the corresponding Docker container
    WorkspaceFolder maps to /app
    ForwardPorts maps the local access port
    Extensions and debugging tools run inside the container
  3. Docker container
    Go / PHP version matches the image
    Local code is mounted, so modifications inside the container take effect immediately
    Container ports are mapped to local ports for convenient service access
    Each container runs independently without affecting other projects
  4. Development workflow
    Edit code in the VS Code editor
    Code is mounted to the Dev Container → Docker container
    The container runs the service or compilation commands
    Access the corresponding port via the local browser

Advantages

Each project has an independent environment, similar to PHPStorm SDK + project isolation
Supports multiple language versions (Go + PHP74 + PHP81)
Port mappings do not conflict, allowing multiple services to run simultaneously
The VS Code window corresponds to the project Workspace, completely independent

27. How do I now convert the APP [Development Container: GO GIN DEV CONTAINER] into a workspace? Directly reference the container path in VS Code
Open the Command Palette (Ctrl+Shift+P / Cmd+Shift+P)
Type Save Workspace As…
Click Show Local, and save it to C:\Users\Thinkpad\VSCodeWorkspaces\go-gin-tutorial-app.code-workspace. See Figure 10.

How do I now convert the APP [Development Container: GO GIN DEV CONTAINER] into a workspace?

28. After opening a new VS Code window, go to File – Open Workspace from File – Show Local – C:\Users\Thinkpad\VSCodeWorkspaces\go-gin-tutorial-app.code-workspace.

29. Now in VS Code, there is a problem: could not import github.com/gin-gonic/gin (no required module provides package “github.com/gin-gonic/gin”). This is a typical error when Go Modules dependencies are not installed or initialized. See Figure 11.

Now in VS Code, there is a problem: could not import github.com/gin-gonic/gin (no required module provides package "github.com/gin-gonic/gin")

30. Run the code to start tracking the Gin module as a dependency. From the command line, use the go get command to add the github.com/gin-gonic/gin module as a dependency for your module. The dot argument means “get dependencies for the code in the current directory.” Execute the command inside the container from VS Code; the terminal opened by default is inside the container, and the path should be /app. Go will resolve and download this dependency to satisfy the import statement added in the previous step. See Figure 12.

From the command line, use the go get command to add the github.com/gin-gonic/gin module as a dependency for your module.
Plaintext

/app # pwd
/app
/app # go get .
go: github.com/gin-gonic/gin@v1.12.0 requires go >= 1.25.0 (running go 1.23.12; GOTOOLCHAIN=local)
/app # 

31. In the directory containing main.go, run the code from the command line. The dot argument means “run the code in the current directory.” Error:

Plaintext
/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. Found the root cause: the Go version in the current container is 1.23.12, and Gin v1.12.0 requires Go ≥ 1.25.0. Therefore, even if you execute go get . or go get github.com/gin-gonic/gin, it will fail, and VS Code will prompt: no required module provides package github.com/gin-gonic/gin.

33. Edit docker-compose.yml to upgrade the Go image version, changing golang:1.23-alpine to golang:1.26-alpine.

YAML

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. Container rebuild workflow (completed within VS Code)
Modify docker-compose.yml to upgrade the Go image version
Command Palette Ctrl+Shift+P → type:
Dev Containers: Rebuild and Reopen in Container
VS Code will destroy the old container and create a new one based on the new image
Open the terminal and execute the command from step 4, and the Gin environment will be ready

✅ The workspace .code-workspace and local code will not be lost
✅ The Go version and Gin dependencies inside the container are up to date

35. Execute the go get . command again. No version issue was reported, and it executed correctly.

Plaintext
/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 => 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. In the directory containing main.go, run the code from the command line. The dot argument means “run the code in the current directory.”

Plaintext

/app # go run .



Once the code runs, you will have an HTTP server that you can send requests to.

37. Access via Windows browser: http://localhost:8080/albums, response status: (failed) net::ERR_EMPTY_RESPONSE. Modify main.go, router.Run(“0.0.0.0:8080”) // listen on all network interfaces. It displays the data previously provided to the service. See Figure 13.

Access via Windows browser: http://localhost:8080/albums
JSON

[
    {
        "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. Generate a GitHub Personal Access Token.
A 30-second step-by-step guide to generating a Token. Do it once, and you’ll never have to enter your password again!
Step-by-step to generate a GitHub Token
Click your avatar in the top right corner of GitHub → Settings
At the bottom left → Developer settings
Click Personal access tokens → Tokens (classic)
Click Generate new token in the top right corner → Generate new token (classic)
Fill in:
Note: enter go-gin-learning (anything you like, for your own convenience)
Expiration: choose 90 days or No expiration
Select scopes: check repo (just check the entire first item)
Scroll to the bottom → Generate token
Copy the generated string starting with ghp_ (it is only shown once, and will be gone if you close it!) See Figure 14.

Generate a GitHub Personal Access Token

69. Decided to commit the code to GitHub and created a repository: https://github.com/shuijingwan/go-gin-learning . In the /app directory inside the container (terminal under VS Code), directly execute all the following commands:

Plaintext
/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 
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 
hint:
hint: Disable this message with "git config set advice.defaultBranchName false"
Initialized empty Git repository in /app/.git/
/app # echo "# go-gin-learning" >> README.md
/app # cat > .gitignore << 'EOF'
> /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:

        git config --global --add safe.directory /app
/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
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.
/app # 

70. Checking the repository directory on GitHub, I found that .devcontainer was committed. The current repository directory structure is as follows

/go-gin-learning
├── .devcontainer/ ✅ Should be committed
│ └── devcontainer.json
├── .gitignore ✅ Should be committed
├── README.md ✅ Should be committed
├── docker-compose.yml ✅ Should be committed
├── go.mod
├── go.sum
└── main.go

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系