前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 docker 轻松部署你的 Go 项目

使用 docker 轻松部署你的 Go 项目

原创
作者头像
陈明勇
发布2024-10-24 23:51:19
3840
发布2024-10-24 23:51:19
举报
文章被收录于专栏:Go 实战Go 技术Go技术干货

前言

如果你是一名 Go 开发者,你一定知道用 Go 写代码是一件多爽的事,高效而简洁。那么如果我告诉你,部署 Go 项目同样也可以这么轻松愉快呢?这就是 Docker 的魅力所在。

本文将详细介绍如何使用 Docker 轻松部署 Go 项目,借助 Docker,你可以告别复杂的环境配置,不再纠结各种依赖版本的问题,只需要几个命令,就可以把 Go 项目打包成一个在任何地方都能运行的容器。正如使用 Go 写代码的体验一样,Docker 让部署过程变得高效而便捷。

准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。

环境准备

如果你打算跟着本文进行实操,你需要准备好相应的开发环境。首先是 Go 环境,本文基于 Go 1.23.0 版本进行编码,如果你的 Go 版本低于该版本,需要先升级一下。

其次是 Docker 的环境配置。本文不会详细介绍如何安装和配置 Docker,如果你尚未准备好 Docker 环境,请参考官方文档:Install Docker,根据你的操作系统查看对应的安装教程。

simple-web-app 项目搭建

以一个简单的 Web 应用为例。

创建项目目录

在你的工作区或任意一个目录下创建新项目的目录。

代码语言:bash
复制
$ mkdir simple-web-app
$ cd simple-web-app

初始化 Go 模块

使用 Go Modules 初始化项目,这个操作将创建一个 go.mod 文件用于管理依赖。

代码语言:bash
复制
$ go mod init simple-web-app
go: creating new go.mod: module simple-web-app

这样我们就初始化好了 simple-web-app 项目了,项目目录结构如下所示:

代码语言:shell
复制
├── simple-web-app/
│   └── go.mod

go.mod 文件内容如下所示:

代码语言:go
复制
module simple-web-app

go 1.23.0

创建主程序文件

创建 main.go 文件,并编写基本的 HTTP 服务器代码。

代码语言:go
复制
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)
	}
}

运行项目

在项目根目录下,运行以下命令来启动服务器:

代码语言:bash
复制
go run main.go

你会看到控制台输出:

代码语言:bash
复制
Starting server at http://localhost:8080

然后可以在浏览器中访问 http://localhost:8080/posts/1,不出意外的话你就会看到 id: 1 的字样。

编写 Dockerfile

Dockerfile 是一个用于定义 Docker 容器镜像构建过程的脚本文件。它包含了一组指令,告诉 Docker 如何创建镜像。

接下来我们需要在 simple-web-app 项目根目录下创建并编写 Dockerfile 文件,以帮助我们实现自动化构建和高效部署。

代码语言: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)。

构建 Docker 镜像

Dockerfile 所在的目录运行以下命令来构建 Docker 容器:

代码语言:bash
复制
docker build -t simple-web-app .

解释:

  • docker build:这是 Docker 用于构建镜像的命令。
  • -t simple-web-appt--tag 的缩写,用来给构建的 Docker 镜像命名。
  • .:指定了构建上下文的路径,. 表示当前目录,Docker 引擎会从当前目录读取 Dockerfile 以及所有相关的项目文件并打包进镜像中。

运行 Docker 容器

使用以下命令运行刚刚构建的镜像:

代码语言:bash
复制
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 的字样。

docker compose

在前面的 simple-web-app 项目中,我们只实现了一个简单的 Web 服务,它并没有依赖任何第三方中间件。因此,只需要一个 Docker 容器就可以完成整个项目的部署,过程相对简单。

然而,在实际的项目中,即使是类似的 Web 应用,往往也会需要依赖其他第三方中间件,例如数据库(如 MySQLPostgreSQLMongoDB)、缓存(如 Redis)、或者消息队列(如 Kafka)等。这些依赖服务需要与 Web 应用协同工作,如果我们需要手动启动每个服务的 Docker 容器,并且管理它们之间的启动顺序和网络配置,整个过程会非常繁琐且容易出错。

为了简化这些管理和配置工作,使得多服务应用的部署更加简便和一致,我们可以使用 Docker Compose 工具。Docker ComposeDocker 的一个工具,允许我们通过编写一个简单的 YAML 文件(通常命名为 docker-compose.yml),来描述多服务应用所需的容器、网络配置、卷等资源。借助这个配置文件,我们只需一条命令就可以启动所有相关的服务,使得整个流程更高效且易于管理。

docker compose 工具的安装教程请参考 Overview of installing Docker Compose

接下来,我们来改造之前的 simple-web-app 项目,引入 MongoDB 数据库,并使用 Docker Compose 工具来统一管理应用和数据库的启动和配置。

引入 mongo 库

通过以下命令在 simple-web-app 项目中引入 mongo 库:

代码语言:bash
复制
go get github.com/chenmingyong0423/go-mongox

这里引入的是 go-mongox 库,它是一个基于官方驱动程序的 Go Mongo 的泛型库,扩展了 MongoDB 的官方框架。

修改主程序文件

代码语言:go
复制
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)
	}
}

代码主要变化:

  • 引入了 MongoDB: 通过 go-mongox 库和 MongoDB 驱动,代码新增了对 MongoDB 的连接与查询功能。
  • 接口处理改变: 之前只是简单地返回 URL 中的 id,现在通过 MongoDB 查询 id 对应的 Post 数据,并返回查询结果。

编写 MongoDB 脚本

编写一个 MongoDB 脚本,用于在运行 MongoDB 容器时自动创建用户账号,以便 simple-web-app 能够连接到 MongoDB

simple-web-app 项目的根目录下创建 script 目录,并在其中新建 mongo-init.sh 文件,将以下内容添加到该文件中:

代码语言:bash
复制
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

编写 docker-compose.yaml 文件

代码语言:yaml
复制
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 命令来构建并启动服务:

代码语言:bash
复制
docker-compose up -d

这个命令会在后台做以下事情:

  • 构建 simple-web-appDocker 镜像。
  • 启动 MongoDB 服务,并通过 mongo-init.sh 初始化数据库、用户和权限。
  • 启动 simple-web-app 容器。

当构建完成并启动相关容器之后,通过浏览器访问 http://localhost:8080/posts/1,应该会看到 error: mongo: no documents in result 的字样,这是正常的,因为没有 _id1 的数据,到这里我们就完成了 simple-web-app 项目的部署了。


当你不再需要服务运行时,可以使用以下命令停止并删除所有相关容器:

代码语言:bash
复制
docker-compose down

小结

本文详细介绍了如何使用 Docker 部署 Go 项目,涵盖了从创建项目、编写 Dockerfile 到利用 Docker Compose 管理多容器服务的全过程。通过 Docker,我们能够将 Go 项目和其依赖服务(如 MongoDB)容器化,从而确保项在各种环境中的一致性和可移植性,极大简化了复杂项目的部署过程。

值得一提的是,对于单体应用或个人项目,Docker 已足够应对开发和部署需求。然而,随着项目规模扩大,尤其是微服务架构中,单靠 Docker 难以满足复杂的服务编排需求。这时,引入 KubernetesK8s)等编排工具能更有效地管理多服务、扩展和自动化运维,特别适用于高可用性和动态扩展的分布式系统。


你好,我是陈明勇,一名热爱技术、乐于分享的开发者,同时也是开源爱好者。

成功的路上并不拥挤,有没有兴趣结个伴?

关注我,加我好友,一起学习一起进步!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 环境准备
  • simple-web-app 项目搭建
    • 创建项目目录
      • 初始化 Go 模块
        • 创建主程序文件
          • 运行项目
            • 编写 Dockerfile
              • 构建 Docker 镜像
                • 运行 Docker 容器
                • docker compose
                  • 引入 mongo 库
                    • 修改主程序文件
                      • 编写 MongoDB 脚本
                        • 编写 docker-compose.yaml 文件
                          • 运行 docker-compose 命令
                          • 小结
                          相关产品与服务
                          容器服务
                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档