阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

如何使用 Docker 部署 Go Web 应用程序

  • 2016-06-11
  • 本文字数:9708 字

    阅读完需:约 32 分钟

简介

虽然大部分 Go 应用程序可以编译为一个单一的二进制文件,但 Web 应用程序可能还有自己的模板和配置文件。如果一个项目中包含大量文件,可能会因为文件不同步而导致出错并造成更多严重问题。

您将通过本文了解如何使用 Docker 部署 Go Web 应用程序,以及 Docker 如何帮您改善开发工作流和部署过程。各种规模的团队都能从本文内容中获益。

目标

通过阅读本文,您将能:

  • 对 Docker 有一些基本了解,
  • 了解 Docker 如何帮您开发 Go 应用程序,
  • 知道如何为 Go 应用程序创建生产用 Docker 容器,并
  • 知道如何使用 Semaphore 将 Docker 容器持续部署到您的服务器。

前提要求

为了完成本文,您需要:

  • 在您的计算机和服务器上安装 Docker,并具备
  • 一台可以使用 SSH 密钥对 SSH 请求进行身份验证的服务器。

理解 Docker

Docker 可以帮您为自己的应用程序创建一个单一的可部署“单位”。这样的单位也叫做容器,其中包含了应用程序需要的一切。例如代码(或二进制文件)、运行时、系统工具,以及系统库文件。将所有这些需要的内容打包为一个单一的单位,可确保无论将应用程序部署在何处,都能提供完全相同的环境。这种技术还可以帮您维持完全一致的开发和生产环境,通常这些环境是很难被追踪的。

一旦搭建完成,容器的创建和部署将可自动进行。这本身就可以避免一系列问题。这些问题中大部分都是因为文件不同步,或开发和生产环境之间的差异导致的。Docker 可以解决这些问题。

相对于虚拟机的优势

容器提供了与虚拟机类似的资源分配和隔离等好处。然而相似之处仅此而已。

虚拟机需要自己的来宾操作系统,容器则能与宿主操作系统共享内核。这意味着容器更轻,需要的资源更少。虚拟机从本质上来说,实际上就是一个操作系统内部运行的另一个操作系统。然而容器更像是操作系统内部运行的一个应用程序。通常容器需要的资源(内存、磁盘空间等)远低于虚拟机,同时启动速度也比虚拟机快很多。

在开发过程中使用 Docker 所获得的收益

在开发工作中使用 Docker 可以获得的部分收益包括:

  • 所有团队成员共同使用一个标准的开发环境,
  • 集中更新依存组件,在任何位置使用相同的容器,
  • 从开发到生产可以使用完全相同的环境,并且
  • 更易于修复只可能在生产环境中遇到的潜在问题。

为何通过 Docker 使用 Go Web 应用程序?

大部分 Go 应用程序都是简单的二进制文件。这就引出了另一个问题 - 为何通过 Docker 使用 Go 应用程序?通过 Docker 使用 Go 的部分原因包括:

  • Web 应用程序通常包含模板和配置文件,Docker 有助于确保这些文件在库中保持完全同步。
  • Docker 能为开发和生产提供完全相同的环境。很多人经常遇到某个应用程序在开发环境中运行正常,但发布至生产环境中无法运行。使用 Docker 后将不再需要担心此类问题。
  • 在大型团队中,不同成员的计算机、操作系统,以及所安装的软件可能存在非常大的差异。Docker 提供了一种确保整个开发环境保持一致的机制。团队成员可以更高效,并可减少开发过程中的冲突和其他本可避免的问题。

创建一个简单的 Go Web 应用程序

我们将使用 Go 创建一个简单的 Web 应用程序作为本文的范例。这个我们称之为 _MathApp_ 的应用程序可以:

  • 暴露不同数学运算的过程,
  • 使用 HTML 模板创建视图,
  • 使用配置文件对应用程序进行定制,并
  • 针对所选功能提供测试。

访问/sum/3/6会打开一个显示了36相加后结果的页面。同理,访问/product/3/6会打开一个显示了36相乘后结果的页面。

本文中我们使用了 _Beego_ 框架。请注意,您自己的应用程序可以使用任何框架(或者完全不使用)。

最终的目录结构

完成后的 MathApp 其目录结构应该是类似这样的:

复制代码
MathApp
├── conf
│ └── app.conf
├── main.go
├── main_test.go
└── views
├── invalid-route.html
└── result.html

我们会假设MathApp目录位于/app目录下。

程序主文件是位于应用程序根目录的main.go,这个文件中包含了应用的所有功能。main.go的部分功能可使用main_test.go进行测试。

views文件夹包含视图文件invalid-route.htmlresult.html。配置文件app.conf位于conf文件夹中。_Beego_ 可使用该文件对应用程序进行定制。

应用程序文件的内容

应用程序主文件 (main.go) 包含应用程序的所有逻辑。该文件的内容如下:

复制代码
// main.go
package main
import (
"strconv"
"github.com/astaxie/beego"
)
// The main function defines a single route, its handler
// and starts listening on port 8080 (default port for Beego)
func main() {
/* This would match routes like the following:
/sum/3/5
/product/6/23
...
*/
beego.Router("/:operation/:num1:int/:num2:int", &mainController{})
beego.Run()
}
// This is the controller that this application uses
type mainController struct {
beego.Controller
}
// Get() handles all requests to the route defined above
func (c *mainController) Get() {
//Obtain the values of the route parameters defined in the route above
operation := c.Ctx.Input.Param(":operation")
num1, _ := strconv.Atoi(c.Ctx.Input.Param(":num1"))
num2, _ := strconv.Atoi(c.Ctx.Input.Param(":num2"))
//Set the values for use in the template
c.Data["operation"] = operation
c.Data["num1"] = num1
c.Data["num2"] = num2
c.TplName = "result.html"
// Perform the calculation depending on the 'operation' route parameter
switch operation {
case "sum":
c.Data["result"] = add(num1, num2)
case "product":
c.Data["result"] = multiply(num1, num2)
default:
c.TplName = "invalid-route.html"
}
}
func add(n1, n2 int) int {
return n1 + n2
}
func multiply(n1, n2 int) int {
return n1 * n2
}

在您的应用程序中,这些内容可能分散保存在多个文件中。但是出于演示的用途,我们希望尽量确保内容足够简单。

测试文件的内容

main.go文件包含一些需要测试的功能。对这些功能的测试位于main_test.go,这个文件的内容如下:

复制代码
// main_test.go
package main
import "testing"
func TestSum(t *testing.T) {
if add(2, 5) != 7 {
t.Fail()
}
if add(2, 100) != 102 {
t.Fail()
}
if add(222, 100) != 322 {
t.Fail()
}
}
func TestProduct(t *testing.T) {
if multiply(2, 5) != 10 {
t.Fail()
}
if multiply(2, 100) != 200 {
t.Fail()
}
if multiply(222, 3) != 666 {
t.Fail()
}
}

如果希望进行持续部署,应用程序的测试就显得尤为有用。如果您已经准备好相应的测试,即可在无需担心为应用程序引入错误的情况下进行持续部署。

视图文件的内容

视图文件其实是 HTML 模板。应用程序可以使用这些文件显示对请求做出的回应。result.html的内容如下:

复制代码
<!-- result.html -->
<!-- This file is used to display the result of calculations -->
<!doctype html>
<html>
<head>
<title>MathApp - {{.operation}}</title>
</head>
<body>
The {{.operation}} of {{.num1}} and {{.num2}} is {{.result}}
</body>
</html>

invalid-route.html的内容如下:

复制代码
<!-- invalid-route.html -->
<!-- This file is used when an invalid operation is specified in the route -->
<!doctype html>
<html>
<head>
<title>MathApp</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
</head>
<body>
Invalid operation
</body>
</html>

配置文件的内容

_Beego_ 可以使用app.conf文件配置应用程序,该文件的内容如下:

复制代码
; app.conf
appname = MathApp
httpport = 8080
runmode = dev

在这个文件中,

  • appname是运行该应用程序的进程对应的名称,
  • httpport是应用程序使用的端口,而
  • runmode决定了应用程序的运行模式。可用值包括对应开发环境的dev,以及对应生产环境的prod

在开发过程中使用 Docker

本节将介绍在开发过程中使用 Docker 所能获得的收益,并会指导您完成在开发过程中使用 Docker 的步骤。

针对开发工作配置 Docker

我们会使用一个Dockerfile配置开发工作所用的 Docker。配置工作需要满足的开发环境要求如下:

  • 我们会使用上文提到的应用程序,
  • 文件必须能够从容器内部和外部访问,
  • 我们将使用beego包含的bee工具。该工具可用于在开发过程中实时重载 (Live reload) 应用(应用位于 Docker 容器内部),
  • Docker 将通过8080端口暴露该应用程序,
  • 在我们的计算机上,该应用程序位于/app/MathApp
  • 在 Docker 容器中,该应用程序位于/go/src/MathApp
  • 我们为开发工作创建的 Docker 映像名为ma-image,并且
  • 开发过程中所运行的 Docker 容器的名称为ma-instance

第 1 步 - 创建 Dockerfile

满足上述要求的 Dockerfile 内容如下:

复制代码
FROM golang:1.6
# Install beego and the bee dev tool
RUN go get github.com/astaxie/beego && go get github.com/beego/bee
# Expose the application on port 8080
EXPOSE 8080
# Set the entry point of the container to the bee command that runs the
# application and watches for changes
CMD ["bee", "run"]

第一行,

FROM golang:1.6使用 Go 的官方映像作为基础映像。这个映像是 Go _1.6_ 预安装的。该映像的$GOPATH值已被设置为/go。所有安装在/go/src的程序包都能通过go命令访问。

第二行,

RUN go get github.com/astaxie/beego && go get github.com/beego/bee安装 _beego_ 程序包和 _bee_ 工具。_beego_ 程序包将在应用程序内部使用,_bee_ 工具将用于在开发过程中实时重载代码。

第三行,

EXPOSE 8080通过开发计算机上容器的8080端口暴露该应用程序。最后一行,

CMD ["bee", "run"]使用bee命令开始对我们的应用程序进行实时重载。

第 2 步 - 构建映像

创建好 Docker 文件之后,可运行下列命令创建映像:

docker build -t ma-image .执行上述命令可创建一个名为ma-image的映像。随后所有负责开发这个应用程序的人都可以使用这个映像。这样即可确保整个团队获得完全一致的开发环境。

要查看您系统中的映像列表,请运行下列命令:

docker images执行该命令可以看到类似下面的内容:

复制代码
REPOSITORY TAG IMAGE ID CREATED SIZE
ma-image latest 8d53aa0dd0cb 31 seconds ago 784.7 MB
golang 1.6 22a6ecf1f7cc 5 days ago 743.9 MB

请注意,实际的映像名称和数量可能各不相同,不过您至少应该可以在列表中看到golangma-image映像。

第 3 步 - 运行容器

准备好ma-image之后,即可使用下列命令启动一个容器:

复制代码
docker run -it --rm --name ma-instance -p 8080:8080 \
-v /app/MathApp:/go/src/MathApp -w /go/src/MathApp ma-image

分别来看看上述命令的作用。

  • docker run命令可用于通过映像运行容器,
  • -it标记可以用交互式模式启动该容器,
  • --rm标记可以在容器关闭后清理其中的内容,
  • --name ma-instance可以将容器命名为ma-instance
  • -p 8080:8080标记使得容器可以通过8080端口访问,
  • -v /app/MathApp:/go/src/MathApp略微复杂,可以将/app/MathApp从计算机映射至容器的/go/src/MathApp目录。这样可以确保在容器内部和外部均可访问这些开发文件,并且
  • ma-image部分指定了容器内部使用的映像名称。

执行上述命令可启动 Docker 容器。这个容器可以将您的应用程序暴露到8080端口,还可以在您修改代码后自动重新构建您的应用程序。您将在控制台中看到下列输出结果:

复制代码
bee :1.4.1
beego :1.6.1
Go :go version go1.6 linux/amd64
2016/04/10 13:04:15 [INFO] Uses 'MathApp' as 'appname'
2016/04/10 13:04:15 [INFO] Initializing watcher...
2016/04/10 13:04:15 [TRAC] Directory(/go/src/MathApp)
2016/04/10 13:04:15 [INFO] Start building...
2016/04/10 13:04:18 [SUCC] Build was successful
2016/04/10 13:04:18 [INFO] Restarting MathApp ...
2016/04/10 13:04:18 [INFO] ./MathApp is running...
2016/04/10 13:04:18 [asm_amd64.s:1998][I] http server Running on :8080

若要检查整个环境,请通过浏览器访问http://localhost:8080/sum/4/5。您应该能看到类似下图的结果:

注意:上述步骤假设您在自己的本地计算机上执行操作。

第 4 步 - 开发应用程序

随后一起来看看这种做法能对开发工作起到什么帮助。执行下文操作的过程中请确保容器始终处于运行状态。在main.go文件中,将第 _#34_ 行从

c.Data["operation"] = operation修改为

c.Data["operation"] = "real " + operation保存改动的同时,您应该能看到类似下面的内容:

复制代码
2016/04/10 13:17:51 [EVEN] "/go/src/MathApp/main.go": MODIFY
2016/04/10 13:17:51 [SKIP] "/go/src/MathApp/main.go": MODIFY
2016/04/10 13:17:52 [INFO] Start building...
2016/04/10 13:17:56 [SUCC] Build was successful
2016/04/10 13:17:56 [INFO] Restarting MathApp ...
2016/04/10 13:17:56 [INFO] ./MathApp is running...
2016/04/10 13:17:56 [asm_amd64.s:1998][I] http server Running on :8080

要查看这些改动,请用浏览器访问http://localhost:8080/sum/4/5。您应该能看到类似下图的结果:

如您所见,保存改动后,您的应用程序可以自动构建并开始提供服务。

在生产环境中使用 Docker

本节将介绍如何在 Docker 容器中部署 Go 应用程序。我们将使用 Semaphore 实现下列目标:

  • 改动的代码推送到 Git 代码库后自动进行构建,
  • 自动运行测试,
  • 如果构建成功并通过测试,创建一个 Docker 映像,
  • 将 Docker 映像推送至 Docker Hub,并
  • 更新服务器以使用最新的 Docker 映像。

为生产环境创建 Dockerfile

在开发过程中,我们的目录结构如下:

复制代码
MathApp
├── conf
│ └── app.conf
├── main.go
├── main_test.go
└── views
├── invalid-route.html
└── result.html

由于我们希望为这个项目构建 Docker 映像,因此需要创建一个在生产环境中使用的 Dockerfile。请在项目的根目录下创建 Dockerfile,随后新的目录结构应该是类似下面这样的:

复制代码
MathApp
├── conf
│ └── app.conf
├── Dockerfile
├── main.go
├── main_test.go
└── views
├── invalid-route.html
└── result.html

在 Dockerfile 中输入下列内容:

复制代码
FROM golang:1.6
# Create the directory where the application will reside
RUN mkdir /app
# Copy the application files (needed for production)
ADD MathApp /app/MathApp
ADD views /app/views
ADD conf /app/conf
# Set the working directory to the app directory
WORKDIR /app
# Expose the application on port 8080.
# This should be the same as in the app.conf file
EXPOSE 8080
# Set the entry point of the container to the application executable
ENTRYPOINT /app/MathApp

详细看看上述每条命令的作用。第一条命令,

FROM golang:1.6指定了在开发过程中所用的同一个golang:1.6映像基础之上构建映像。第二条命令,

RUN mkdir /app可以在容器的根目录下创建名为app的目录。这个目录将用于保存项目文件。第三组命令,

复制代码
ADD MathApp /app/MathApp
ADD views /app/views
ADD conf /app/conf

可以将库、视图文件夹,以及配置文件夹从计算机复制到映像内的应用程序文件夹中。第四条命令,

WORKDIR /app可将映像的工作目录设置为/app。第五条命令,

EXPOSE 8080可以从容器中暴露8080端口。这个端口应该与应用程序的app.conf文件中指定的端口完全一致。最后一条命令,

ENTRYPOINT /app/MathApp可将映像的入口点 (Entry point) 设置为应用程序的二进制文件。这样即可启动该二进制文件,并开始在8080端口提供服务。

自动构建和测试

一旦将改动的代码推送至代码库, Semaphore 就可以帮您轻松地自动构建并测试代码。这里介绍了如何添加您的GitHub 或Bitbucket 项目,以及在Semaphore 上设置Golang 项目的方法。

Go 项目的默认配置可解决下列问题:

  • 抓取依存项,
  • 构建项目,并
  • 运行测试。

完成上述过程后,便可通过 Semaphore 仪表板查看最新构建的状态并对其进行测试。如果构建或测试失败,整个过程将暂停,且不部署任何内容。

通过 Semaphore 为自动化开发创建初始环境

设置好构建过程后,下一步需要配置部署过程。若要部署应用程序,您需要:

  1. 构建 Docker 映像,
  2. 将 Docker 映像推送至 Docker Hub,并
  3. 更新服务器以拉取新的映像,随后据此启动一个新的 Docker 容器。

首先我们需要在 Semaphore 上设置项目以进行持续部署

前三个步骤相对较为简单:

  • 选择部署方法,
  • 选择部署策略,并
  • 选择部署过程所使用的代码库分支。

第 4 步(设置部署命令)我们将使用下一节介绍的命令。目前可以留空并执行下一个步骤。

第 5 步,输入服务器上用户的 SSH 密钥。这样即可在无需密码的情况下,以安全的方式在服务器上执行某些部署命令。

第 6 步,可以为服务器命名。如果不指定名称,Semaphore 会为服务器分配类似server-1234这样的随机名称。

在服务器上设置更新脚本

接下来需要设置部署过程,这样 Semaphore 即可构建新映像并将其上传至 Docker Hub。设置完成后,Semaphore 将通过一条命令在服务器上执行脚本,发起更新过程。

为此我们需要将下列文件以update.sh为名放置在服务器上。

复制代码
#!/bin/bash
docker pull $1/ma-prod:latest
if docker stop ma-app; then docker rm ma-app; fi
docker run -d -p 8080:8080 --name ma-app $1/ma-prod
docker rmi $(docker images --filter "dangling=true" -q --no-trunc)

使用下列命令使得该文件可以执行:

chmod +x update.sh随后来看看这个文件是如何生效的。这个脚本可以接受单一参数,并在自己的命令中使用这个参数。这个参数可以是您在 Docker Hub 上的用户名。例如可以通过下面这样的格式使用该命令:

./update.sh docker_hub_username为了理解具体的用途,随后再来看看文件中的每条命令。

第一条命令,

docker pull $1/ma-prod:latest将最新映像从 Docker Hub 拉取到服务器上。如果您在 Docker Hub 上的用户名是demo_user,这条命令将从 Docker Hub 拉取名为demo_user/ma-prod,且被标记为latest的映像。

第二条命令,

if docker stop ma-app; then docker rm ma-app; fi可以停止并移除任何曾以ma-app为名启动的容器。

第三条命令,

docker run -d -p 8080:8080 --name ma-app $1/ma-prod使用能够反映最新构建中所有变动的最新映像启动一个新的容器(名为ma-app)。

最后一条命令,

docker rmi $(docker images --filter "dangling=true" -q --no-trunc)可从服务器上删除任何未使用的映像。这个清理工作可以确保服务器整洁,并能降低磁盘空间的使用。

注意:这个文件必须放置在上一步骤中所用 SSH 密钥对应用户的根目录下。如果文件位置有变化,下文使用的部署命令也需要酌情更新。

设置项目配合 Docker 工作

默认情况下,Semaphore 上的新项目会使用Ubuntu 14.04 LTS v1603平台。这个平台并非 Docker 自带的。因为我们感兴趣的是 Docker 的使用,因此需要更改Semaphore 的平台设置以使用 Ubuntu 14.04 LTS v1603(支持 Docker 的 Beta 测试版)平台。

设置环境变量

为了在开发过程中以安全的方式使用 Docker Hub,我们需要将自己的凭据存储在 Semaphore 自动初始化出来的环境变量内。

我们需要存储下列变量:

  • DH_USERNAME - Docker Hub 用户名
  • DH_PASSWORD - Docker Hub 密码
  • DH_EMAIL - Docker Hub 邮件地址

可以参考这里了解如何用安全的方式设置环境变量

设置部署命令

虽然已经完成初始设置,但目前还无法进行部署。原因在于还没有配置需要运行的命令。

首先我们需要输入完成部署过程所需的命令,为此请在Semaphore 上打开您的项目首页。

"> 在这个页面上,点击 Servers选项下的服务器名称,随后可以看到下列界面:

点击页面右侧,页头下方的Edit server按钮。

在下列页面上,我们需要注意底部名为Deploy commands的选项。点击其中的Change deploy commands链接,开始编辑要运行的命令。

在编辑框中输入下列命令,并点击Save Deploy Commands按钮:

复制代码
go get -v -d ./
go build -v -o MathApp
docker login -u $DH_USERNAME -p $DH_PASSWORD -e $DH_EMAIL
docker build -t ma-prod .
docker tag ma-prod:latest $DH_USERNAME/ma-prod:latest
docker push $DH_USERNAME/ma-prod:latest
ssh -oStrictHostKeyChecking=no your_server_username@your_ip_address "~/update.sh $DH_USERNAME"

注意:请将上述命令中your_server_username@your_ip_address内容替换为您的实际值。

随后一起来仔细看看上述这些命令的用途。

前两条命令go getgo build是标准的 Go 命令,分别用于拉取依存项和构建项目。请注意go build命令指定的可执行文件的名称应该是MathApp。这个名称应该与 Dockerfile 中所用名称一致。

第三条命令,

docker login -u $DH_USERNAME -p $DH_PASSWORD -e $DH_EMAIL使用(上文操作中设置的)环境变量与 Docker Hub 进行身份验证,这样我们才可以推送最新映像。第四条命令,

docker build -t ma-prod .根据最新代码基构建一个名为ma-prod的 Docker 映像。第五条命令,

docker tag ma-prod:latest $DH_USERNAME/ma-prod:latest为最新创建的映像添加your_docker_hub_username/ma-prod:latest标签。这样我们就可以将该映像推送至 Docker Hub 上相应的代码库中。第六条命令,

docker push $DH_USERNAME/ma-prod:latest可将该映像推送至 Docker Hub。最后一条命令,

ssh -oStrictHostKeyChecking=no your_server_username@your_ip_address "~/update.sh $DH_USERNAME"使用ssh命令登录到您的服务器,执行我们在上文步骤中创建的 update.sh 脚本。这个脚本可以从 Docker Hub 获取最新映像,并据此启动一个新的容器。

部署应用程序

截至目前我们并未真正将应用程序部署到服务器,因此先来进行手工部署。请注意这一操作并非是必须的。当您下一次将改动的代码推送至代码库后,只要构建和测试都能成功完成,Semaphore 将自动部署您的应用程序。我们这里进行手工部署只是为了测试一切都能正常工作。

您可以阅读 Semaphore 文档了解如何在构建页面手工部署应用程序。

应用程序部署完毕后,可通过下列地址访问:

http://your_ip_address:8080/sum/4/5

访问结果应该类似下图所示:

结果与开发过程中看到的完全相同。唯一的差异在于此处访问时使用的 URL 并不是localhost,而是服务器的 IP 地址。

对配置进行测试

至此已配置了自动构建和部署过程,我们的工作流也得以大幅简化。让我们对代码进行一些细小的改动,然后看看服务器上的应用程序是如何自动更新以反映这些改动的。

试试看将文字的颜色由黑色改为红色。为此请在views/result.html文件中,将第 _#8_ 行由

<body>改为

<body style="color: red">随后保存文件,并在您的应用程序目录中使用下列命令提交改动:

复制代码
git add views/result.html
git commit -m 'Change the color of text from black (default) to red'

使用下列命令将改动推送至代码库:

git push origin mastergit push命令成功执行后,Semaphore 会检测到代码库中的改动,并自动开始构建过程。构建过程(包括测试过程)成功完成后,Semaphore 将启动部署过程。Semaphore 仪表板会实时显示构建和部署过程的状态。

一旦 Semaphore 仪表板显示构建和部署过程均已完成,请刷新下列页面:

http://your_ip_address:8080/sum/4/5

随后您将可以看到类似下图的结果:

结论

在这篇文章中,我们了解到如何为 Go 应用程序创建 Docker 容器,并使用 Semaphore 将 Docker 容器部署至服务器。

现在您已经可以使用 Docker 简化下一个 Go 应用程序的开发工作。如果您有任何问题,欢迎发布到下方的评论区。

作者

Kulshekhar Kabra是一位独立开发者,他的工作可以方便地接触到各种新技术,并将其运用到新项目中。只要工作之余有闲时间,他还很喜欢撰写技术文章并制作视频教程。

阅读英文原文 How To Deploy a Go Web Application with Docker


感谢陈兴璐对本文的策划和审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-06-11 17:2911215
用户头像

发布了 283 篇内容, 共 102.1 次阅读, 收获喜欢 61 次。

关注

评论

发布
暂无评论
发现更多内容

week03-总结

seki

设计模式相关

莫莫大人

week03-作业

seki

架构师训练营第三周作业

养乐多

第三周作业

慵秋

极客大学架构师训练营

架构师训练营学习总结(第三周)

战峰

设计模式,单例模式与组合模式

Jeannette

架构师训练营 - 学习笔记 - 第三周

chinsun1

设计模式&重构

Amy

学习 极客大学架构师训练营 第三周

架构师训练营 week3 - 学习总结

devfan

单例模式手写

破晓_dawn

架构师训练营 第三周 学习心得

LiJun

极客大学架构师训练营

大型互联网应用的技术方案

GalaxyCreater

架构

架构师如何去进行软件设计 (设计模式篇)?

李小匪

架构 极客大学架构师训练营

架构师训练营第三周作业

王权富贵

极客大学架构师训练营

架构师训练营第三周命题作业

sljoai

week3 学习总结

张健

架构师训练营 第三周 作业

CR

极客大学架构师训练营

架构师训练营 - 作业 - Week3

chinsun1

架构师训练营第 0 期第3 周学习总结

upupup

第三周总结

远方

第三周作业

远方

架构师训练营 -week03 作业

GunShotPanda

Week 03 总结

鱼_XueTr

设计模式 OOD

第三周课程总结

Geek_a327d3

作业

模式和重构

GalaxyCreater

设计模式

架构师训练营第3周总结

aoeiuvzcs

第三周总结

毛叫

极客大学架构师训练营

啊,原来可以不传封面照,找得好辛苦

慵秋

架构师训练营第 0 期第3周作业

upupup

极客大学架构师训练营

架构师训练营第三周作业

fenix

如何使用Docker部署Go Web应用程序_语言 & 开发_大愚若智_InfoQ精选文章