Go + Gin actual RESTful API: from environment build (WSL/Docker/VS code) to Git commit and GitHub hosting
1. Reference: Build a RESTful API (go.dev) with Go and Ginhttps://go.dev/doc/tutorial/web-service-gin— Go official tutorial for building simple web services with GIN.
2. Install Go. Docker Compose One-click startup (refer to Hyperf project). I want to ‘write a good configuration, a command starts the entire development environment’, which is most convenient to use docker-compose.yml, which is almost the same usage of Hyperf’s docker compose. Create project folder: 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. Create 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. Start the container (enter the Go environment), and report an error: only docker is installed in wsl ubuntu, and docker compose is not installed.
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 that can manage dependencies. Run the go mod init command and specify the path to the module where the code is located.
/app # go mod init example/web-service-gin
go: creating new go.mod: module example/web-service-gin
6. I plan to use Goland (Windows) to directly edit the Go code in WSL to match my Docker environment. My Go code is stored in the WSL Ubuntu folder, and Goland can directly access WSL files, just like accessing a local folder. Now the code is here: ~/wwwroot/web-service-gin. Accessing this path in Windows is:\wsl$\Ubuntu\home\ubuntu\wwwroot\web-service-gin. Goland Open this folder, open Goland, click Open, paste the above copied path directly into the folder address bar, press Enter, and select Folder → OK. Now you can edit, modify, and save the Go code in WSL in Windows Goland. And the modifications will be synchronized to the Docker container in real time! as shown in Figure 1

7. Configure Docker in Goland to connect to WSL (key)
After restarting:
File → Settings → Build, Execute, Deploy → Docker
Upper left + add connection
Connection type selection: WSL (emphasis: do not select Docker Desktop)
Distribution drop-down selection: ubuntu
‘Connect successfully’ is displayed at the bottom
Application → OK. as shown in Figure 2

8. File → Settings → Run the target, add → select Docker Compose, click the upper right corner + select: ✅ Docker Compose. After the running target (Docker Compose) is configured, Goland will get: Just [Run], regardless of [Code Tips]
Its only function:
point ▶️ runtime
The code is executed in the Docker container. as shown in Figure 3

9. The default goal of the project: GO. as shown in Figure 4

10. Code prompts, automatic completion, syntax recognition must rely on goroot
Goland wants to identify:
string
int
fmt
function jump
automatic completion
Must be configured: Local goroot This is an IDE hard rule. Open the WSL Ubuntu terminal and copy the run:
# 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
point + add
Choose WSL
Automatically find /usr/local/go
OK → Apply. as shown in Figure 5

12. Create data and create a file called main.go. Go code will be written in this file.
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 has some error-reporting tips:
Unable to parse symbolsgithub.com
Unable to parse symbolsgin-gonic
Unable to parse symbolsgin
unresolved referencesdefault. as shown in Figure 6

14. Open the WSL terminal and execute it directly in 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 >= 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. Reflect on it, I feel that it is too painful now. This usage of ‘reliance in containers, dependencies on container exteriors’ is wrong, confusing, and anti-human! ** The advantage of containers is that the environment is unified, isolated, and there is no need to randomly pack things. Now it is used: one side of the container, the other wsl, schizophrenia! There are only two really correct development models: only use WSL Go, not docker running development; all in Docker, WSL is not installed;
16. Carefully check the cause and find the root cause, in File → Settings → Build, Execute, Deploy → Docker , the virtual machine path: /home/ubuntu/wwwroot/web-service-gin, this path is the path in WSL. ** The correct path should not be the WSL path! It should be [path inside the container]! **. The path of the virtual machine is modified to: /app. as shown in Figure 7

17. It is found that the option of docker is still not provided in goroot. In the environment of ‘without Docker Desktop’, Goland is unable to directly select goroot in the container! The final decision is to give up Goland and use VS Code + Dev Containers instead
18. Windows installation VS Code, VS Code extension store installation: Dev Containers (Microsoft official), WSL, Docker
19. Start the container, report an error: error: for gocontainerconfig, which is a compatibility issue with old container + python v1
<pre class="wp-block-syntaxhighlighter-code">
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'
</pre>
20. Enter the directory: ~/wwwroot/web-service-gin, execute: code . Trust the author of the file in this folder? Yes. as shown in Figure 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. Delete the .idea directory, which is left by goland. Prepare the dev container configuration file, directly use the existing docker image, create a new one: .devcontainer/devcontainer.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"
}
Explain:
dockercomposefile points to docker-compose.yml
Service is the service that wishes to connect to VS Code (here is Go)
workspacefolder is the code mount directory /app
Extensions automatically install Go plugins in containers
ForwardPorts Map container ports to VS Code
RemoteUser is optional, if the default user in the container is not root
22. VS Code open the container
Open VS Code and enter the project directory
Press F1 → enter dev containers: reopen in container
vs code starts the container according to devcontainer.json and docker-compose.yml and mounts the code
Open the terminal, you will find that it is directly in the container (/app), the Go version is the same as the Docker image
⚡ Tip: If the container starts slowly, you can start the container with docker-compose up -d and then connect with dev container. as shown in Figure 9

23. Development process suggestions
Open a project with Dev Container
Run the go or php command directly on the container terminal
Install dependencies in containers (Go mod tidy, composer install, etc.)
Debugging, running, testing in VS Code
No need to care if Go/PHP is installed on WSL
24. Multi-project + multi-container flow chart
┌─────────────────────────────����─────────────
│ 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 ← Mounted project code │ │ /app ← Mounted project code │ │ /app ← mounted project code │ │
│ │ Port 8080 Map Local │ │ Port 8000 Map Local │ │ Port 8001 Map Local │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘
└─────────────────────────────����───────────
Description:
- Each VS Code window corresponds to a separate Workspace (project)
- Each Workspace connects the corresponding Docker container using a standalone Dev Container
- Mount /app in the container to the corresponding project code, the development and operating environment are exactly the same
- Independent mapping of different project ports, local access does not conflict with each other
25. The following is the complete flowchart of the web-service-gin project, showing VS Code, Dev Container, Docker container, mount path and port mapping, which is fully suitable for 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 GIN Web) │ │
│ │ environment: goproxy=https://goproxy.io,direct│ │
│ └────────────────────────��������─────────
│ │
│ Editing process: VS Code Editor → Dev Container → Docker Container Run → Local Port Access Web │
└──────────────────────────── ──────────────────────────
26. The following is a multi-project combination diagram of the widescreen version, showing the VS Code Workspace, Dev Container, Docker of the three projects of Go + PHP74 + PHP81 Container, mount path and port mapping, horizontal arrangement, convenient and intuitive to understand the local development environment of multi-project, multi-language and multi-container:
┌──────────────────────────────── ─────────────────────────────
│ VS Code Windows │
│ (Each Workspace separate window) │
│ │
│ ┌───────────────────── ┌─────────────────────── ┌───────────────────────
│ │ 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 process description: │
│ VS Code Edit → Dev Container → Docker Container Run → Mount Code → Local Port Access Web/Service │
│ Multi-project and multi-language versions are independent, the ports do not conflict with each other, and the environment is consistent │
└───────────────────────────────── ───────────────────────────────
Imagine
- VS Code Windows / Workspace
Each item corresponds to a VS Code window
.code-workspace configuration independent
Plug-in, debugging, and terminal complete isolation - dev container
Connection to a Docker container
WorkspaceFolder Mapping /App
ForwardPorts maps local access ports
Plugins and debugging tools run inside the container - docker container
Go/PHP version is consistent with the image
Mount the local code, and the modification in the container will take effect
The container port is mapped to the local port, and the access service is convenient
Each container runs independently and does not affect other projects - development process
Edit the code in the VS Code editor
The code is mounted to the Dev Container → Docker container
Container run service or compile command
The local browser accesses the corresponding port
Advantage
Each project is a separate environment, similar to the PHPStorm SDK + project isolation
Multilingual version support (Go + PHP74 + PHP81)
Port mapping does not conflict with each other, and multiple services can be run at the same time
The VS Code window corresponds to the project workspace, completely independent
27. How do I change the app now[开发容器: GO GIN DEV CONTAINER]Convert to a workspace? Reference container path directly in VS Code
Open the command panel (Ctrl+Shift+P/CMD+Shift+P)
Enter Save Workspace As…
Click Show Local and save to C:\Users\ThinkPad\vscodeworkspaces\go-gin-tutorial-app.code-workspace. As shown in Figure 10
![How do I get the app now[开发容器: GO GIN DEV CONTAINER]Convert to a workspace?](https://www.shuijingwanwq.com/wp-content/uploads/2026/04/10.jpg)
28. After opening the VS Code, the file – opens the workspace from the file – shows the local – C:\Users\ThinkPad\vscodeworkspaces\go-gin-tutorial-app.code-workspace.
29. Now in VS Code, the problem: could not import github.com/gin-gonic/gin (no required module provides package ‘github.com/gin-gonic/gin’). This is a typical error that Go Modules depend on not installed or initialized. as shown in Figure 11

30. Run the code Start tracking the GIN module as a dependency. In the command line, use the go get command to add github.com/gin-gonic/gin module as a module dependency. Use the point number parameter to ‘get the dependencies of the code in the current directory’. Enter the container to execute the command in VS Code. The default is the terminal in the container. The path should be /app, and go has resolved and downloaded this dependency to satisfy the statement added by import in the previous step. as shown in Figure 12

/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. ‘Run code in the current directory’ with the point number parameter. Error:
/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, the current Go version in the container is 1.23.12, Gin v1.12.0 needs go ≥ 1.25.0, so even if go get . or go get is executed github.com/gin-gonic/gin will also fail, VS code will prompt: no required module provides package github.com/gin-gonic/gin.
33. Edit docker-compose.yml, upgrade the go image version, golang:1.23-alpine is modified to 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. Container reconstruction process (completed within VS code)
Modify docker-compose.yml to upgrade the Go image version
Command Panel Ctrl+Shift+P → Input:
Dev containers: Rebuild and reopen in containers
VS Code will destroy old containers and create new containers based on new images
Open the terminal and execute the command 4, the GIN environment is ready
✅ Workspace .code-workspace and local code will not be lost
✅ The Go version and Gin dependency in the container are the latest
35. Execute the go get. command again, the version problem is not prompted, and it is executed correctly.
/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. ‘Run code in the current directory’ with the point number parameter.
/app # go run .
After the code runs, you have an HTTP server that can send a request to it.
37. Windows browser access:http://localhost:8080/albums, Response Status: (failed) net::err_empty_response. Modify main.go, router.run(‘0.0.0.0:8080’) // Listen to all network cards. Displays data that is pre-provided to the service. as shown in Figure 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. Generate GitHub’s Personal Access Token (Personal Access Token).
I teach token by hand in 30 seconds, and I can do it at one time, and I will never have to enter the password in the future!
Generate github token step by step
Open the avatar in the upper right corner of GitHub → settings
At the bottom of the left side → developer settings
Click Personal Access Tokens → Tokens (Classic)
Click Generate New Token → Generate New Token (Classic)
Fill in:
Note: Fill in go-gin-learning
Expiration: Choose 90 days or no expiration
Select Scopes: Check repo (just check the first item)
Pull to the bottom → generate token
Copy the generated string of code starting with GHP_ (only show it once, and it will be gone if it is turned off!) as shown in Figure 14

69. Decided to submit the code to GitHub, create a repository:https://github.com/shuijingwan/go-gin-learning. In the /app directory in the container (the terminal under VS code), directly execute all the following commands:
<pre class="wp-block-syntaxhighlighter-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" >> 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 #
</pre>
70. Check the repository directory on github and find that the .devcontainer has been submitted. Now the repository directory structure is as follows
/go-gin-learning
├── .devcontainer/ ✅ should be submitted
│ └── devcontainer.json
├── .gitignore ✅ Should be submitted
├── readme.md ✅ should be submitted
├── docker-compose.yml ✅ Should be submitted
├── go.mod
├── go.sum
└── main.go