如果你是一名 Go
开发者,你一定知道用 Go
写代码是一件多爽的事,高效而简洁。那么如果我告诉你,部署 Go
项目同样也可以这么轻松愉快呢?这就是 Docker
的魅力所在。
本文将详细介绍如何使用 Docker
轻松部署 Go
项目,借助 Docker
,你可以告别复杂的环境配置,不再纠结各种依赖版本的问题,只需要几个命令,就可以把 Go
项目打包成一个在任何地方都能运行的容器。正如使用 Go
写代码的体验一样,Docker
让部署过程变得高效而便捷。
准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。
如果你打算跟着本文进行实操,你需要准备好相应的开发环境。首先是 Go
环境,本文基于 Go 1.23.0
版本进行编码,如果你的 Go
版本低于该版本,需要先升级一下。
其次是 Docker
的环境配置。本文不会详细介绍如何安装和配置 Docker
,如果你尚未准备好 Docker
环境,请参考官方文档:Install Docker,根据你的操作系统查看对应的安装教程。
以一个简单的 Web
应用为例。
在你的工作区或任意一个目录下创建新项目的目录。
$ mkdir simple-web-app
$ cd simple-web-app
使用 Go Modules
初始化项目,这个操作将创建一个 go.mod
文件用于管理依赖。
$ go mod init simple-web-app
go: creating new go.mod: module simple-web-app
这样我们就初始化好了 simple-web-app
项目了,项目目录结构如下所示:
├── simple-web-app/
│ └── go.mod
go.mod
文件内容如下所示:
module simple-web-app
go 1.23.0
创建 main.go
文件,并编写基本的 HTTP
服务器代码。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("GET /posts/{id}", func(writer http.ResponseWriter, request *http.Request) {
id := request.PathValue("id")
fmt.Fprintf(writer, "id: %s", id)
})
fmt.Println("Starting server at http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
在项目根目录下,运行以下命令来启动服务器:
go run main.go
你会看到控制台输出:
Starting server at http://localhost:8080
然后可以在浏览器中访问 http://localhost:8080/posts/1
,不出意外的话你就会看到 id: 1
的字样。
Dockerfile
是一个用于定义 Docker
容器镜像构建过程的脚本文件。它包含了一组指令,告诉 Docker
如何创建镜像。
接下来我们需要在 simple-web-app
项目根目录下创建并编写 Dockerfile
文件,以帮助我们实现自动化构建和高效部署。
# 使用官方的 Go 语言镜像作为基础镜像
# 这里使用 Go 1.23.0 版本的 Alpine Linux 镜像
FROM golang:1.23.0-alpine AS builder
# 设置工作目录为 /app
# 所有后续操作都会在这个目录下进行
WORKDIR /app
# 将当前项目目录的所有文件拷贝到容器的 /app 目录中
COPY . .
# 设置 Go 模块代理为 https://goproxy.cn(在中国加速模块下载),并下载项目的依赖
RUN go env -w GOPROXY=https://goproxy.cn,direct && go mod download
# 编译 Go 项目,生成可执行文件 simple-web-app
RUN go build -o simple-web-app
# 使用一个更小的基础镜像(Alpine)来运行应用程序
# Alpine 是一个极简的 Linux 发行版,适合部署阶段
FROM alpine:latest
# 安装 tzdata 包,确保支持时区的配置
RUN apk add --no-cache tzdata
# 设置工作目录为 /app
WORKDIR /app
# 从编译阶段的镜像中拷贝编译后的二进制文件到运行镜像中
COPY --from=builder /app/simple-web-app /app/simple-web-app
# 暴露容器的 8080 端口,用于外部访问
EXPOSE 8080
# 设置容器启动时运行的命令
# 这里是运行编译好的可执行文件 simple-web-app
CMD ["/app/simple-web-app"]
说明:
golang:1.23.0-alpine
镜像来编译 Go
项目,然后使用 alpine:latest
来部署。这样可以减少最终的镜像大小。EXPOSE
指定应用程序监听的端口(这里是 8080
)。在 Dockerfile
所在的目录运行以下命令来构建 Docker
容器:
docker build -t simple-web-app .
解释:
docker build
:这是 Docker
用于构建镜像的命令。-t simple-web-app
:t
是 --tag
的缩写,用来给构建的 Docker
镜像命名。.
:指定了构建上下文的路径,.
表示当前目录,Docker
引擎会从当前目录读取 Dockerfile
以及所有相关的项目文件并打包进镜像中。使用以下命令运行刚刚构建的镜像:
docker run -p 8080:8080 simple-web-app
其中:
docker run
:基于指定的 Docker
镜像创建并启动一个容器实例。-p 8080:8080
:将本地主机的 8080
端口映射到容器的 8080
端口,以便你可以通过 localhost:8080
访问容器内的应用。
simple-web-app
:基于 simple-web-app
镜像来创建并运行容器。这样,Go
项目将会在本地的 8080
端口上运行。你可以通过浏览器访问 http://localhost:8080/posts/1
,应该会看到 id: 1
的字样。
在前面的 simple-web-app
项目中,我们只实现了一个简单的 Web
服务,它并没有依赖任何第三方中间件。因此,只需要一个 Docker
容器就可以完成整个项目的部署,过程相对简单。
然而,在实际的项目中,即使是类似的 Web
应用,往往也会需要依赖其他第三方中间件,例如数据库(如 MySQL
、PostgreSQL
、MongoDB
)、缓存(如 Redis
)、或者消息队列(如 Kafka
)等。这些依赖服务需要与 Web
应用协同工作,如果我们需要手动启动每个服务的 Docker
容器,并且管理它们之间的启动顺序和网络配置,整个过程会非常繁琐且容易出错。
为了简化这些管理和配置工作,使得多服务应用的部署更加简便和一致,我们可以使用 Docker Compose
工具。Docker Compose
是 Docker
的一个工具,允许我们通过编写一个简单的 YAML
文件(通常命名为 docker-compose.yml
),来描述多服务应用所需的容器、网络配置、卷等资源。借助这个配置文件,我们只需一条命令就可以启动所有相关的服务,使得整个流程更高效且易于管理。
docker compose
工具的安装教程请参考 Overview of installing Docker Compose
接下来,我们来改造之前的 simple-web-app
项目,引入 MongoDB
数据库,并使用 Docker Compose
工具来统一管理应用和数据库的启动和配置。
通过以下命令在 simple-web-app
项目中引入 mongo
库:
go get github.com/chenmingyong0423/go-mongox
这里引入的是 go-mongox
库,它是一个基于官方驱动程序的 Go Mongo
的泛型库,扩展了 MongoDB
的官方框架。
package main
import (
"context"
"fmt"
"net/http"
"github.com/chenmingyong0423/go-mongox"
"github.com/chenmingyong0423/go-mongox/builder/query"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type Post struct {
mongox.Model `bson:",inline"`
Title string `bson:"title"`
Author string `bson:"author"`
Content string `bson:"content"`
}
// 示例代码,不是最佳的创建方式
func newCollection() *mongo.Collection {
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://mongo:27017").SetAuth(options.Credential{
Username: "test",
Password: "test",
AuthSource: "db-test",
}))
if err != nil {
panic(err)
}
err = client.Ping(context.Background(), readpref.Primary())
if err != nil {
panic(err)
}
collection := client.Database("db-test").Collection("test_post")
return collection
}
func main() {
// 创建一个 *mongo.Collection 对象
mongoColl := newCollection()
// 使用 Post 结构体作为泛型参数创建一个 collection
postColl := mongox.NewCollection[Post](mongoColl)
http.HandleFunc("GET /posts/{id}", func(writer http.ResponseWriter, request *http.Request) {
id := request.PathValue("id")
post, err := postColl.Finder().Filter(query.Id(id)).FindOne(context.Background())
if err != nil {
fmt.Fprintf(writer, "error: %s", err.Error())
} else {
fmt.Fprintf(writer, "post: %v", post)
}
})
fmt.Println("Starting server at http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
代码主要变化:
go-mongox
库和 MongoDB 驱动,代码新增了对 MongoDB
的连接与查询功能。URL
中的 id
,现在通过 MongoDB
查询 id
对应的 Post
数据,并返回查询结果。编写一个 MongoDB
脚本,用于在运行 MongoDB
容器时自动创建用户账号,以便 simple-web-app
能够连接到 MongoDB
。
在 simple-web-app
项目的根目录下创建 script
目录,并在其中新建 mongo-init.sh
文件,将以下内容添加到该文件中:
mongosh -- "$MONGO_INITDB_DATABASE" <<EOF
db = db.getSiblingDB('admin')
db.auth('$MONGO_INITDB_ROOT_USERNAME', '$MONGO_INITDB_ROOT_PASSWORD');
db = db.getSiblingDB('$MONGO_INITDB_DATABASE')
db.createUser({
user: '$MONGO_USERNAME',
pwd: '$MONGO_PASSWORD',
roles:[
{
role: 'readWrite',
db: '$MONGO_INITDB_DATABASE'
}
]
});
EOF
version: '3.8'
services:
# 定义 simple-web-app 服务
web:
# 使用 Dockerfile 构建镜像
build: .
# 容器启动后暴露的端口,映射宿主机的8080端口到容器的8080端口
ports:
- "8080:8080"
# 依赖于 db 服务,确保 MongoDB 服务在 web 服务之前启动
depends_on:
- mongo
# 定义 MongoDB 服务
mongo:
# 使用官方的 MongoDB 镜像
image: mongo:latest
# 设置 MongoDB 初始化时的根用户名、密码和默认数据库
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
MONGO_INITDB_DATABASE: db-test
MONGO_USERNAME: test # 应用程序用户的名称
MONGO_PASSWORD: test # 应用程序用户的密码
# 暴露 MongoDB 的默认端口27017,便于外部访问
ports:
- "27017:27017"
# 使用 --auth 命令启动 MongoDB 以启用身份验证
command:
- --auth
# 使用卷来持久化数据库数据,避免容器删除时数据丢失
volumes:
- db_data:/data/db
# 将初始化 MongoDB 用户的脚本挂载到容器中,确保在启动时执行
- ./script/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh
# 定义卷,用于持久化存储 MongoDB 的数据,避免数据在容器重启时丢失
volumes:
db_data:
使用 docker-compose
命令来构建并启动服务:
docker-compose up -d
这个命令会在后台做以下事情:
simple-web-app
的 Docker
镜像。MongoDB
服务,并通过 mongo-init.sh
初始化数据库、用户和权限。simple-web-app
容器。当构建完成并启动相关容器之后,通过浏览器访问 http://localhost:8080/posts/1
,应该会看到 error: mongo: no documents in result
的字样,这是正常的,因为没有 _id
为 1
的数据,到这里我们就完成了 simple-web-app
项目的部署了。
当你不再需要服务运行时,可以使用以下命令停止并删除所有相关容器:
docker-compose down
本文详细介绍了如何使用 Docker
部署 Go
项目,涵盖了从创建项目、编写 Dockerfile
到利用 Docker Compose
管理多容器服务的全过程。通过 Docker
,我们能够将 Go
项目和其依赖服务(如 MongoDB
)容器化,从而确保项在各种环境中的一致性和可移植性,极大简化了复杂项目的部署过程。
值得一提的是,对于单体应用或个人项目,Docker
已足够应对开发和部署需求。然而,随着项目规模扩大,尤其是微服务架构中,单靠 Docker
难以满足复杂的服务编排需求。这时,引入 Kubernetes
(K8s
)等编排工具能更有效地管理多服务、扩展和自动化运维,特别适用于高可用性和动态扩展的分布式系统。
你好,我是陈明勇,一名热爱技术、乐于分享的开发者,同时也是开源爱好者。
成功的路上并不拥挤,有没有兴趣结个伴?
关注我,加我好友,一起学习一起进步!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。