Docker 是一个容器化平台,可以简化应用程序的打包、分发和部署。您可以利用 Go 和 Docker 的优势来提高应用程序的效率、可移植性和安全性。
本教程致力于教您如何使用 Docker 构建和部署 Go 应用程序。您将通过使用 Gorilla Mux 和 GORM 包构建 RESTful API 来学习,并将其容器化和部署。
您需要在计算机上安装 Go 和 Docker,才能使用 Docker 构建和容器化您的 Go 应用程序。
确保您的系统上安装了 Go 和 Docker。您可以从 Go 官方下载网站下载 Go,从 Docker Hub 下载 Docker。如果尚未访问该网页,请访问该网页,然后按照您的特定操作系统的安装说明进行操作。
本文介绍如何使用 Docker 部署 Go 应用程序,并介绍有关安装和设置 Docker 和 Postgres 数据库的更多信息,包括容器化 Go 应用程序。
安装完成后,根据需要设置环境变量和路径来配置Go开发环境。确保您有一个具有所需目录结构的工作 Go 工作区。
此外,您还可以熟悉 Docker 的命令行界面 (CLI) 和基本 Docker 概念。
为此项目创建一个新目录,并运行 go mod init 命令将该目录初始化为 Go 项目。
go mod init
初始化 Go 项目后,运行此命令将 GORM 和 Gorilla Mux 包作为依赖项添加到您的项目中。
go get github.com/gorilla/mux go get gorm.io/gorm go get gorm.io/driver/postgres
您将使用 Gorilla Mux 包进行路由。 GORM 包提供了一个接口,供您使用 Go 类型进行 SQL 数据库操作以及您安装的驱动程序包(在本例中为 Postgres)。
在本教程中,您将使用流行的 Go 分层架构风格并使用界面与我们应用程序的各个组件进行交互。
这是应用程序的目录结构。
go mod init
这个项目结构看起来组织良好,清楚地分离了不同组件之间的关注点。随着 Go API 的发展,这个组织可以让您更轻松地维护和扩展它。
这不是 Go 标准。然而,许多 Go 开发人员和开源项目在您的应用程序中使用这种结构。
您将为您的应用程序设置数据库功能。您必须使用结构体定义模型,连接到数据库,并为数据库上的插入操作设置迁移。
这是数据库实现所需的导入列表。
go get github.com/gorilla/mux go get gorm.io/gorm go get gorm.io/driver/postgres
第一个任务是定义一个与您的应用程序的数据库架构相匹配的支柱。 GORM 提供了用于指定字段的附加选项和约束的标签。
. ├── Dockerfile ├── cmd │ └── server │ └── main.go └── internal ├── http │ ├── handlers.go │ └── users.go ├── models │ ├── database.go │ ├── migrations.go │ └── users.go └── users └── user.go 6 directories, 11 files
User 结构体表示处理数据库中用户数据的模型。
在您的database.go 文件中,声明一个结构体来封装数据库连接实例。您将使用该结构从数据库实现包的其他部分连接到数据库。
go mod init
接下来,创建一个数据库连接函数,将数据库实现与数据库程序连接到数据库:
go get github.com/gorilla/mux go get gorm.io/gorm go get gorm.io/driver/postgres
NewDatabase 函数创建一个新的数据库实例并建立与数据库的连接。它返回一个指向数据库实例的指针,并且在此过程中发生错误(如果有)。
成功连接数据库后,您可以使用以下函数为数据库实现设置迁移功能:
. ├── Dockerfile ├── cmd │ └── server │ └── main.go └── internal ├── http │ ├── handlers.go │ └── users.go ├── models │ ├── database.go │ ├── migrations.go │ └── users.go └── users └── user.go 6 directories, 11 files
MgrateDB 函数使用数据库客户端 AutoMigrate 函数为 User 结构设置自动迁移,如果在此过程中遇到任何问题,则返回错误。
在为数据库模式定义结构的 users.go 文件中,您可以继续定义数据库实现的函数。
这里是负责数据库CRUD操作的CreateUser、GetUserByID、UpdateUser和DeleteUser函数。
package models import ( // imports from the user implementation "BetterApp/internal/users" "context" "gorm.io/gorm" "fmt" "gorm.io/driver/postgres" "gorm.io/gorm/schema" "os" )
您的用户实现将调用这些函数来访问数据库功能。
您的用户实现在将数据从数据库中继到 HTTP 实现方面发挥着重要作用。
您将定义一个与数据库实现中的结构相匹配的结构,并将 JSON 标签添加到字段中以供使用;然后,您将定义使用 HTTP 实现中的数据调用数据库函数的函数。
以下是用户实现所需的导入:
// internal/models/users.go type User struct { gorm.Model Username string `gorm:"unique;not null"` Email string `gorm:"unique;not null"` IsActive bool `gorm:"not null"` }
这是带有 JSON 标签的结构。 gorm.Model 字段中的 json:"-" 指定您要从 JSON 操作中排除该字段。
// internal/models/database.go type Database struct { Client *gorm.DB }
接下来,您将声明一个接口,其中包含用户实现函数的方法、用户实现的服务结构以及初始化服务实现的函数。
// internal/models/database.go func NewDatabase() (*Database, error) { // Construct a connection string using environment variables for database configuration. configurations := fmt.Sprintf("host=%v port=%v user=%v password=%v dbname=%v sslmode=%v", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USERNAME"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"), os.Getenv("SSL_MODE")) // Open a connection to the database using GORM and PostgreSQL driver. db, err := gorm.Open(postgres.New(postgres.Config{ DSN: configurations, PreferSimpleProtocol: true, }), &gorm.Config{NamingStrategy: schema.NamingStrategy{ SingularTable: true, }}) if err != nil { return nil, err } // Enable connection pooling by configuring maximum idle and open connections. sqlDB, err := db.DB() if err != nil { return nil, err } sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) // Return the Database instance with the established database connection. return &Database{ Client: db, }, nil }
接口和服务将帮助管理用户实现之外的与用户相关的操作。
接下来,您可以定义调用数据库实现的 UserService 结构实现的方法。
// internal/models/migrations.go func (d *Database) MigrateDB() error { log.Println("Database Migration in Process...") // Use GORM AutoMigrate to migrate all the database schemas. err := d.Client.AutoMigrate(&User{}) if err != nil { return err } log.Println("Database Migration Complete!") return nil }
CreateUser、GetUserByID、UpdateUser 和 DeleteUser 函数负责调用数据库实现上的 CRUD 操作。 HTTP 实现将调用这些函数来访问数据库。
HTTP 实现是应用程序的一部分,用于接收传入请求并与之交互。
以下是您在 HTTP 实现中需要的导入列表:
go mod init
首先,声明一个结构体并包含一个 Router 实例、一个 HTTP 实例和一个用户服务实例。
go get github.com/gorilla/mux go get gorm.io/gorm go get gorm.io/driver/postgres
然后创建一个返回指向 Handler 结构的指针的函数,您可以在其中配置服务器和处理程序。
. ├── Dockerfile ├── cmd │ └── server │ └── main.go └── internal ├── http │ ├── handlers.go │ └── users.go ├── models │ ├── database.go │ ├── migrations.go │ └── users.go └── users └── user.go 6 directories, 11 files
NewHandler 函数设置并配置 HTTP 请求处理程序,使其准备好处理特定服务的传入 HTTP 请求,同时定义服务器设置和路由。
您在 NewHandler 函数中调用的 mapRoutes 函数通过将路由映射到各自的处理函数来设置路由。
package models import ( // imports from the user implementation "BetterApp/internal/users" "context" "gorm.io/gorm" "fmt" "gorm.io/driver/postgres" "gorm.io/gorm/schema" "os" )
接下来,定义处理函数及其功能。 这里有CreateUser、GetUserByID、UpdateUser和DeleteUser函数,它们负责拦截HTTP请求并根据操作进行响应。
// internal/models/users.go type User struct { gorm.Model Username string `gorm:"unique;not null"` Email string `gorm:"unique;not null"` IsActive bool `gorm:"not null"` }
现在,您可以编写启动服务器的功能了。
// internal/models/database.go type Database struct { Client *gorm.DB }
Serve 函数在指定端口上启动服务器,如果过程中出现错误,则返回错误。
导入 main.go 文件中的实现以耦合实现并运行您的应用程序。
// internal/models/database.go func NewDatabase() (*Database, error) { // Construct a connection string using environment variables for database configuration. configurations := fmt.Sprintf("host=%v port=%v user=%v password=%v dbname=%v sslmode=%v", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USERNAME"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"), os.Getenv("SSL_MODE")) // Open a connection to the database using GORM and PostgreSQL driver. db, err := gorm.Open(postgres.New(postgres.Config{ DSN: configurations, PreferSimpleProtocol: true, }), &gorm.Config{NamingStrategy: schema.NamingStrategy{ SingularTable: true, }}) if err != nil { return nil, err } // Enable connection pooling by configuring maximum idle and open connections. sqlDB, err := db.DB() if err != nil { return nil, err } sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) // Return the Database instance with the established database connection. return &Database{ Client: db, }, nil }
您可以在 main.go 文件中声明一个 Run 函数来实例化应用程序的启动,然后在 main 函数中调用该函数。
// internal/models/migrations.go func (d *Database) MigrateDB() error { log.Println("Database Migration in Process...") // Use GORM AutoMigrate to migrate all the database schemas. err := d.Client.AutoMigrate(&User{}) if err != nil { return err } log.Println("Database Migration Complete!") return nil }
Run 函数创建一个数据库实例,初始化迁移功能,初始化 HTTP 和 User 实现并启动服务器。
您可以在主函数中调用 Run 函数来启动您的应用程序。
// internal/models/users.go func (d *Database) CreateUser(ctx context.Context, user *users.User) error { newUser := &User{ Username: user.Username, Email: user.Email, IsActive: false, } if err := d.Client.WithContext(ctx).Create(newUser).Error; err != nil { return err } return nil } // GetUserByID returns the user with a specified id func (d *Database) GetUserByID(ctx context.Context, id int64) (users.User, error) { user := users.User{} if err := d.Client.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { return users.User(User{}), err } return users.User(User{ Username: user.Username, Email: user.Email, IsActive: user.IsActive, }), nil } // UpdateUser updates an existing user in the database func (d *Database) UpdateUser(ctx context.Context, updatedUser users.User, id uint) error { // Check if the user with the specified ID exists var existingUser User if err := d.Client.WithContext(ctx).Where("id = ?", id).First(&existingUser).Error; err != nil { return err } // Update the fields of the existing user with the new values existingUser.Username = updatedUser.Username existingUser.Email = updatedUser.Email existingUser.IsActive = updatedUser.IsActive // Save the updated user back to the database if err := d.Client.WithContext(ctx).Save(&existingUser).Error; err != nil { return err } return nil } // DeleteUser deletes a user from the database by their ID func (d *Database) DeleteUser(ctx context.Context, id uint) error { // Check if the user with the specified ID exists var existingUser User if err := d.Client.WithContext(ctx).Where("id = ?", id).First(&existingUser).Error; err != nil { return err } // Delete the user from the database if err := d.Client.WithContext(ctx).Delete(&existingUser).Error; err != nil { return err } return nil }
在考虑使用 Docker 对其进行容器化之前,应用程序应该运行良好。
现在您已经成功构建并运行了程序,您可以继续使用 Docker 将其容器化。
您的 Dockerfile 将有两个阶段:构建阶段和最终阶段。这种方法可以减小图像大小,通过减少攻击面来最大限度地降低安全风险,确保高效的运行时性能,并促进不同开发和部署阶段的可重复性。
您还将使用 Alpine Linux 作为 Docker 镜像的基础镜像,因为它们更高效、更安全,并且极简设计可实现更小的镜像大小、更快的构建速度和更少的攻击面。
使用 Dockerfile 中的构建和最终阶段可以高效创建 Docker 映像。构建阶段从包含构建工具和依赖项的基础映像开始,编译应用程序工件,并生成可能较大的中间映像。
这是构建阶段的 Dockerfile 的内容:
go mod init
最后阶段采用较小的基础映像,仅复制必要的运行时组件,并生成针对生产优化的紧凑映像。
以下是最后阶段的 Dockerfile 内容:
go mod init
编写 Dockerfile 后,您可以继续构建并运行该文件。
运行此命令以使用 build 命令从文件构建 Docker 映像。
go get github.com/gorilla/mux go get gorm.io/gorm go get gorm.io/driver/postgres
-t 标志指定 Docker 镜像的标签为 betterapp,后面的点 (.) 指定您要在当前目录中构建 Dockerfile。
您可以使用 run 命令运行映像,并使用 -p 标志指定从容器到主机的端口映射。
. ├── Dockerfile ├── cmd │ └── server │ └── main.go └── internal ├── http │ ├── handlers.go │ └── users.go ├── models │ ├── database.go │ ├── migrations.go │ └── users.go └── users └── user.go 6 directories, 11 files
后续的 -e 标志用于为您的应用程序指定环境变量。
Docker Compose 是一个容器编排工具,可以简化多个 Docker 容器的使用。您可以使用 Docker compose 来编排您的 Go 应用程序及其组件。
您将使用 YAML 文件来指定指令,Docker compose 将设置您的应用程序以节省您的时间和复杂性。
首先,使用以下命令创建一个 Docker Compose YAML 文件,然后在编辑器中打开该文件:
package models import ( // imports from the user implementation "BetterApp/internal/users" "context" "gorm.io/gorm" "fmt" "gorm.io/driver/postgres" "gorm.io/gorm/schema" "os" )
创建 Dockerfile 后,您可以开始编写用于部署应用程序的命令和指令:
// internal/models/users.go type User struct { gorm.Model Username string `gorm:"unique;not null"` Email string `gorm:"unique;not null"` IsActive bool `gorm:"not null"` }
YAML 文件定义了两个服务:my-postgres(数据库容器实例)和 Web 服务(在配置环境变量、端口和依赖项之前您的 Go 应用程序)。
现在,您可以继续使用 docker-compose build 命令来构建镜像。
// internal/models/database.go type Database struct { Client *gorm.DB }
您的输出应与此类似:
最后,您可以使用 docker-compose up 命令运行容器。
go mod init
-d 标志以分离模式运行容器,这使得它与终端会话无关。
这是运行命令的结果:
您可以关闭终端,容器应该继续运行。
容器启动后,您可以运行 CURL 请求来测试您的 API:
go get github.com/gorilla/mux go get gorm.io/gorm go get gorm.io/driver/postgres
恭喜,您已经使用 Docker 和 Docker Compose 成功部署并运行了一个工作 Go 应用程序。
您已经学习了如何使用 Docker 和 Docker Compose 构建和简化 Go 应用程序的部署。当您继续您的开发之旅时,您在这里获得的技能和理解将被证明是确保顺利部署和卓越运营的重要资产。
考虑探索高级 Docker 功能,例如优化 Dockerfile 构建或为大型应用程序实施 Docker Swarm。
以上是如何使用 Docker 部署 Go 应用程序的详细内容。更多信息请关注PHP中文网其他相关文章!