首页 > 其他分享 >如何使用Docker部署 Go WEB应用

如何使用Docker部署 Go WEB应用

时间:2023-08-21 15:45:52浏览次数:43  
标签:WEB app build Go 镜像 Docker COPY bubble

如何使用Docker部署 Go WEB应用

本文介绍了如何使用Docker以及Docker-compose部署我们的Go Web程序。

为什么需要使用Docker?

使用docker的主要目标是容器化。也就是为你的应用程序提供一致的环境,而不依赖于它运行的主机。

想象一下你是否也会遇到下面这个场景,你在本地开发了你的应用程序,它很可能有很多的依赖环境或包,甚至对依赖的具体版本都有严格的要求,当开发过程完成后,你希望将应用程序部署到web服务器。这个时候你必须确保所有依赖项都安装正确并且版本也完全相同,否则应用程序可能会崩溃并无法运行。如果你想在另一个web服务器上也部署该应用程序,那么你必须从头开始重复这个过程。这种场景就是Docker发挥作用的地方。

对于运行我们应用程序的主机,不管是笔记本电脑还是web服务器,我们唯一需要做的就是运行一个docker容器平台。从以后,你就不需要担心你使用的是MacOS,Ubuntu,Arch还是其他。你只需定义一次应用,即可随时随地运行。

Docker部署实例

准备代码

这里我先使用net/http编写的简单代码为例如何使用Docker进行部署,后面使用稍微复杂一点的项目部署案例:

 package main
 ​
 import (
   "fmt"
   "net/http"
 )
 ​
 func main() {
   http.HandleFunc("/", hello)
   server := &http.Server{
     Addr: ":8888",
   }
   fmt.Println("server startup...")
   if err := server.ListenAndServe(); err != nil {
     fmt.Printf("server startup failed, err:%v\n", err)
   }
 }
 ​
 func hello(w http.ResponseWriter, _ *http.Request) {
   w.Write([]byte("hello wrold"))
 }

上面的代码通过8888端口对外提供服务,返回一个字符串响应:hello wrold

创建Docker镜像

镜像image包含运行应用程序所需的东西——代码或二进制文件、运行时、依赖以及所需的任何其他文件系统对象。

或者简单的说,镜像(image)是定义应用程序及其运行所需的一切。

编写DOckerfile

要创建Docker镜像(image)必须在配置文件中指定步骤。这个文件默认我们通常称之为Dockerfile。(虽然这个文件名可以随意命名它,但最好还是使用默认的Dockerfile。)

现在我们可是编写Dockerfile,具体内容如下:

注意:某些步骤不是唯一的,可以根据自己的需要修改诸如文件路径、最终可执行文件的名称等

 FROM golang:alpine
 ​
 # 为我们的镜像设置必要的环境变量
 ENV GO111MODULE=on \
     GOPROXY=https://goproxy.cn,direct \
     CGO_ENABLED=0 \
     GOOS=linux \
     GOARCH=amd64
 ​
 # 移动到工作目录:/build
 WORKDIR /build
 ​
 # 将代码复制到容器中
 COPY . .
 ​
 # 将我们的代码编译成二进制可执行文件app
 RUN go build -o app .
 ​
 # 移动到用于存放生成的二进制文件的 /dist 目录
 WORKDIR /dist
 ​
 # 将二进制文件从 /build 目录复制到这里
 RUN cp /build/app .
 ​
 # 声明服务端口
 EXPOSE 8888
 ​
 # 启动容器时运行的命令
 CMD ["/dist/app"]

Dockerfile解析

From

我们正在使用基础镜像golang:alpine来创建我们的镜像。这和我们要创建的镜像一样是一个我们能够访问的存储在Docker仓库的基础镜像。这个镜像运行的是alpine Linux发行版,该发行版的大小很小并且内置了Go,非常适合我们的用例。有大量公开可用的Docker镜像,请查看https://hub.docker.com/_/golang

Env

用来设置我们编译阶段需要用的环境变量。

WORKDIR,COPY,RUN

这几个命令做的事都写在注释里了,很好理解。

EXPORT,CMD

最后,我们声明服务端口,因为我们的应用程序监听的是这个端口并通过这个端口对外提供服务。并且我们还定义了在我们运行镜像的时候默认执行的命令CMD ["/dist/app"]

构建镜像

在项目目录下,执行下面的命令创建镜像,并指定镜像名称为goweb_app

 docker build . -t goweb_app

等待构建过程结束,输出如下提示:

 ...
 Successfully built 90d9283286b7
 Successfully tagged goweb_app:latest

现在我们已经准备好了镜像,但是目前它什么也没做。我们接下来要做的是运行我们的镜像,以便它能够处理我们的请求。运行中的镜像称为容器。

执行下面的命令来运行镜像:

 docker run -p 8888:8888 goweb_app

标志位-p用来定义端口绑定。由于容器中的应用程序在端口8888上运行,我们将其绑定到主机端口也是8888。如果要绑定到另一个端口,则可以使用-p $HOST_PORT:8888。例如-p 5000:8888

现在就可以测试下我们的web程序是否工作正常,打开浏览器输入http://127.0.0.1:8888就能看到我们事先定义的响应内容如下:

 hello world

分阶段构建示例

我们的Go程序编译之后会得到一个可执行的二进制文件,其实在最终的镜像中是不需要go编译器的,也就是说我们只需要一个运行最终二进制文件的容器即可。

Docker的最佳实践之一是通过仅保留二进制文件来减小镜像大小,为此,我们将使用一种称为多阶段构建的技术,这意味着我们将通过多个步骤构建镜像。

 FROM golang:alpine AS builder
 ​
 # 为我们的镜像设置必要的环境变量
 ENV GO111MODULE=on \
     GOPROXY=https://goproxy.cn,direct \
     CGO_ENABLED=0 \
     GOOS=linux \
     GOARCH=amd64
 ​
 # 移动到工作目录:/build
 WORKDIR /build
 ​
 # 将代码复制到容器中
 COPY . .
 ​
 # 将我们的代码编译成二进制可执行文件 app
 RUN go build -o app .
 ​
 ###################
 # 接下来创建一个小镜像
 ###################
 FROM scratch
 ​
 # 从builder镜像中把/dist/app 拷贝到当前目录
 COPY --from=builder /build/app /
 ​
 # 需要运行的命令
 ENTRYPOINT ["/app"]

使用这种技术,我们剥离了使用golang:alpine作为编译镜像来编译得到二进制可执行文件的过程,并基于scratch生成一个简单的、非常小的新镜像。我们将二进制文件从命名为builder的第一个镜像中复制到新创建的scratch镜像中。有关scratch镜像的更多信息,请查看https://hub.docker.com/_/scratch

附带其他文件的部署实例

如果项目中带有静态文件或配置文件,需要将其拷贝到最终的镜像文件中。

我们的bubble项目用到了静态文件和配置文件,具体目录结构如下:

 bubble
 ├── README.md
 ├── bubble
 ├── conf
 │   └── config.ini
 ├── controller
 │   └── controller.go
 ├── dao
 │   └── mysql.go
 ├── example.png
 ├── go.mod
 ├── go.sum
 ├── main.go
 ├── models
 │   └── todo.go
 ├── routers
 │   └── routers.go
 ├── setting
 │   └── setting.go
 ├── static
 │   ├── css
 │   │   ├── app.8eeeaf31.css
 │   │   └── chunk-vendors.57db8905.css
 │   ├── fonts
 │   │   ├── element-icons.535877f5.woff
 │   │   └── element-icons.732389de.ttf
 │   └── js
 │       ├── app.007f9690.js
 │       └── chunk-vendors.ddcb6f91.js
 └── templates
     ├── favicon.ico
     └── index.html

我们需要将templatesstaticconf三个文件夹中的内容拷贝到最终的镜像文件中。更新后的Dockerfile如下

 FROM golang:alpine AS builder
 ​
 # 为我们的镜像设置必要的环境变量
 ENV GO111MODULE=on \
     GOPROXY=https://goproxy.cn,direct \
     CGO_ENABLED=0 \
     GOOS=linux \
     GOARCH=amd64
 ​
 # 移动到工作目录:/build
 WORKDIR /build
 ​
 # 将代码复制到容器中
 COPY . .
 ​
 # 下载依赖信息
 RUN go mod download
 ​
 # 将我们的代码编译成二进制可执行文件 bubble
 RUN go build -o bubble .
 ​
 ###################
 # 接下来创建一个小镜像
 ###################
 FROM scratch
 ​
 # 从builder镜像中把静态文件拷贝到当前目录
 COPY ./templates /templates
 COPY ./static /static
 ​
 # 从builder镜像中把配置文件拷贝到当前目录
 COPY ./conf /conf
 ​
 # 从builder镜像中把/dist/app 拷贝到当前目录
 COPY --from=builder /build/bubble /
 ​
 # 需要运行的命令
 ENTRYPOINT ["/bubble", "conf/config.ini"]

简单来说就是多了几步COPY的步骤,大家看一下Dockerfile中的注释即可。

Tips: 这里把COPY静态文件的步骤放在上层,把COPY二进制可执行文件放在下层,争取多使用缓存。

关联其他容器

又因为我们的项目中使用了MySQL,我们可以选择使用如下命令启动一个MySQL容器,它的别名为mysql8019;root用户的密码为root1234;挂载容器中的/var/lib/mysql到本地的/Users/q1mi/docker/mysql目录;内部服务端口为3306,映射到外部的13306端口。

 docker run --name mysql8019 -p 13306:3306 -e MYSQL_ROOT_PASSWORD=root1234 -v /Users/q1mi/docker/mysql:/var/lib/mysql -d mysql:8.0.19

这里需要修改一下我们程序中配置的MySQL的host地址为容器别名,使它们在内部通过别名(此处为mysql8019)联通。

 [mysql]
 user = root
 password = root1234
 host = mysql8019
 port = 3306
 db = bubble

修改后记得重新构建bubble_app镜像:

 docker build . -t bubble_app

我们这里运行bubble_app容器的时候需要使用--link的方式与上面的mysql8019容器关联起来,具体命令如下:

 docker run --link=mysql8019:mysql8019 -p 8888:8888 bubble_app

Docker Compose模式

除了像上面一样使用--link的方式来关联两个容器之外,我们还可以使用Docker Compose来定义和运行多个容器。

Compose是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,你可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

使用Compose基本上是一个三步过程:

  1. 使用Dockerfile定义你的应用环境以便可以在任何地方复制。

  2. 定义组成应用程序的服务,docker-compose.yml 以便它们可以在隔离的环境中一起运行。

  3. 执行 docker-compose up命令来启动并运行整个应用程序。

我们的项目需要两个容器分别运行mysqlbubble_app,我们编写的docker-compose.yml文件内容如下:

 # yaml 配置
 version: "3.7"
 services:
   mysql8019:
     image: "mysql:8.0.19"
     ports:
       - "33061:3306"
     command: "--default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql"
     environment:
       MYSQL_ROOT_PASSWORD: "root1234"
       MYSQL_DATABASE: "bubble"
       MYSQL_PASSWORD: "root1234"
     volumes:
       - ./init.sql:/data/application/init.sql
   bubble_app:
     build: .
     command: sh -c "./wait-for.sh mysql8019:3306 -- ./bubble ./conf/config.ini"
     depends_on:
       - mysql8019
     ports:
       - "8888:8888"

这个 Compose 文件定义了两个服务:bubble_appmysql8019。其中:

bubble_app

使用当前目录下的Dockerfile文件构建镜像,并通过depends_on指定依赖mysql8019服务,声明服务端口8888并绑定对外8888端口。

mysql8019

mysql8019 服务使用 Docker Hub 的公共 mysql:8.0.19 镜像,内部端口3306,外部端口33061。

注意:

这里有一个问题需要注意,我们的bubble_app容器需要等待mysql8019容器正常启动之后再尝试启动,因为我们的web程序在启动的时候会初始化MySQL连接。这里共有两个地方要更改,第一个就是我们Dockerfile中要把最后一句注释掉:

 # Dockerfile
 ...
 # 需要运行的命令(注释掉这一句,因为需要等MySQL启动之后再启动我们的Web程序)
 # ENTRYPOINT ["/bubble", "conf/config.ini"]

第二个地方是在bubble_app下面添加如下命令,使用提前编写的wait-for.sh脚本检测mysql8019:3306正常后再执行后续启动Web应用程序的命令:

 command: sh -c "./wait-for.sh mysql8019:3306 -- ./bubble ./conf/config.ini"

当然,因为我们现在要在bubble_app镜像中执行sh命令,所以不能在使用scratch镜像构建了,这里改为使用debian:stretch-slim,同时还要安装wait-for.sh脚本用到的netcat,最后不要忘了把wait-for.sh脚本文件COPY到最终的镜像中,并赋予可执行权限哦。更新后的Dockerfile内容如下:

 FROM golang:alpine AS builder
 ​
 # 为我们的镜像设置必要的环境变量
 ENV GO111MODULE=on \
     GOPROXY=https://goproxy.cn,direct \
     CGO_ENABLED=0 \
     GOOS=linux \
     GOARCH=amd64
 ​
 # 移动到工作目录:/build
 WORKDIR /build
 ​
 # 将代码复制到容器中
 COPY . .
 ​
 # 下载依赖信息
 RUN go mod download
 ​
 # 将我们的代码编译成二进制可执行文件 bubble
 RUN go build -o bubble .
 ​
 ###################
 # 接下来创建一个小镜像
 ###################
 FROM debian:stretch-slim
 ​
 # 从builder镜像中把脚本拷贝到当前目录
 COPY ./wait-for.sh /
 ​
 # 从builder镜像中把静态文件拷贝到当前目录
 COPY ./templates /templates
 COPY ./static /static
 ​
 # 从builder镜像中把配置文件拷贝到当前目录
 COPY ./conf /conf
 ​
 ​
 # 从builder镜像中把/dist/app 拷贝到当前目录
 COPY --from=builder /build/bubble /
 ​
 RUN set -eux; \
   apt-get update; \
   apt-get install -y \
     --no-install-recommends \
     netcat; \
         chmod 755 wait-for.sh
 ​
 # 需要运行的命令
 # ENTRYPOINT ["/bubble", "conf/config.ini"]

所有的条件都准备就绪后,就可以执行下面的命令跑起来了:

 docker-compose up

完整版代码示例,请查看我的github仓库:https://github.com/Q1mi/deploy_bubble_using_docker

总结

使用Docker容器能够极大简化我们在配置依赖环境方面的操作,但同时也对我们的技术储备提了更高的要求。对于Docker不管你是熟悉抑或是不熟悉,技术发展的车轮都滚滚向前。

参考链接:

https://levelup.gitconnected.com/complete-guide-to-create-docker-container-for-your-golang-application-80f3fb59a15e

标签:WEB,app,build,Go,镜像,Docker,COPY,bubble
From: https://www.cnblogs.com/xiaohaoge/p/17646174.html

相关文章

  • 每日一库:gosec
    gosec是一个用于在Go代码中查找安全问题的开源工具,它可以帮助发现可能的漏洞和潜在的安全风险。以下是关于gosec的详细介绍:1.工具概述:gosec是一个静态分析工具,用于扫描Go代码以查找潜在的安全问题。它可以识别常见的代码漏洞、敏感信息泄露和其他安全问题,帮助开发人员......
  • go开发exe怎么加壳
    go开发语言开发的程序可以编译成exe程序,发布之前需要做加壳防止被逆向出源码。exe加壳工具的选择至关重要,以下是go开发的程序加密的技术。虚拟化:将原始指令转换为⾃定义的虚拟机指令,交由配套虚拟机系统模拟执⾏。此方式实现的安全度目前最高,同样性能损耗最大,故建议核心函数保护即可......
  • 视频直播点播平台EasyDSS排查WebRTC搭建TURN服务时openssl路径问题。
    我们曾经介绍了WebRTC所必需的STUN/TURN服务,并尝试了在Windows上搭建TURN服务的过程。为了在Windows上编译并使用TURN服务,我们需要安装Cygwin64环境,并进行相应的配置和编译工作。然而,在我们下载、编译和安装coturn时,遇到了一个报错:“ERROR:OpenSSLCrypto开发库未在所需位置正确安......
  • Webstorm 2023.2 最新安装教程(附激活码,亲测有效)
    前言WebStorm 是JetBrains公司旗下一款JavaScript开发工具。已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScriptIDE”等。它提供了许多功能,例如代码自动完成、错误突出显示、重构、调试、版本控制等,可以帮助开发人员提高效率和代码......
  • WebDAV之π-Disk派盘 + 沙拉查词
    沙拉查词是支持多重查词模式混合使用,无论是单击、双击、图标、悬浮还是快捷键,总有适合您的搭配,整合了有道翻译、百度翻译、必应翻译、腾讯翻译君、Google翻译和彩云小译等,自动发音,可配置词典。π-Disk派盘®–知识管理专家派盘是一款面向个人和企业的本地云存储解决方案,它可以......
  • Asp.net Core Web API运行后返回的实体属性首字母全为小写(实际应该为大写)
    1、Asp.netCoreWebAPI项目运行后打开swagger,通过点击控制器下的方法,以此点击Tryitout-Execute,然后观察Responsebody中内容,发现所有的实体属性均为小写,如下图,但其实实际的实体属性值首字母为大写,如图二,出现这种情况的解决办法如下:  2、第一步:右键项目中依赖项-管理NuGe......
  • docker
     编写Dockerfile将Dcokerfile文件置于项目根目录下,文件内容如下FROMpython:3.7WORKDIR./docker_demoADD..RUNpipinstall-rrequirements.txtCMD["python","./src/main.py"] ......
  • Asp.net Core Web API 启动时出现报错Failed to load API definition
    1、新建的Asp.netCoreWebAPI项目启动时一般是没有问题的,如果在controller下增加多个操作后再启动会出现swagger的报错-FailedtoloadAPIdefinition,基本是由于以下两种原因,分别排查即可: (1)如上图1位置:默认webapi模板创建时只有一个方法所以没有[action] 当我们增加多个......
  • 如何利用Go编辑Nginx规则实现动态操作Nginx配置
    随着微服务的普及和容器化技术的发展,Nginx作为一款高性能的Web服务器和反向代理服务器,被广泛应用于各种场景。然而,传统的静态编辑Nginx配置文件的方式,往往无法满足动态修改配置的需求。本文将介绍如何使用Go语言编辑Nginx规则,实现动态操作Nginx配置。安装依赖库在开始之......
  • Go语法糖
    :=和var短变量声明:使用:=操作符可以在不显式指定变量类型的情况下,根据右侧的表达式自动推断变量类型。这种方法声明和定义绑定在一起一步到位。name:="Pey"var:go中定义过的变量必须使用,不然会报错。使用var既可以只声明变量,也可以同时定义变量。varnamestring ......