如何在Ubuntu18.04上使用Docker和Nginx部署GoWeb应用程序

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。

介绍

Docker 是当今最常用的容器化软件。 它使开发人员能够轻松地将应用程序与其环境一起打包,从而实现更快的迭代周期和更好的资源效率,同时在每次运行时提供相同的所需环境。 Docker Compose 是一种容器编排工具,可满足现代应用程序的需求。 它允许您同时运行多个互连的容器。 编排工具不是手动运行容器,而是让开发人员能够同时控制、扩展和扩展容器。

使用 Nginx 作为前端 Web 服务器的好处是它的性能、可配置性和 TLS 终止,这使应用程序免于完成这些任务。 nginx-proxy 是一个用于 Docker 容器的自动化系统,它极大地简化了将 Nginx 配置为反向代理的过程。 它的 Let's Encrypt 附加组件 可以伴随 nginx-proxy 自动生成和更新代理容器的证书。

在本教程中,您将部署一个示例 Go Web 应用程序,其中 gorilla/mux 作为请求路由器,Nginx 作为 Web 服务器,所有这些都在 Docker 容器中,由 Docker Compose 编排。 您将使用 nginx-proxy 和 Let's Encrypt 插件作为反向代理。 在本教程结束时,您将部署一个可在您的域中访问的 Go Web 应用程序,该应用程序具有多个路由,使用 Docker 并使用 Let's Encrypt 证书进行保护。

先决条件

  • 具有 root 权限和辅助非 root 帐户的 Ubuntu 18.04 服务器。 您可以按照 这个初始服务器设置指南 进行设置。 对于本教程,非 root 用户是 sammy
  • 按照如何在Ubuntu 18.04上安装Docker的前两步安装Docker。
  • 按照如何在Ubuntu 18.04上安装Docker Compose的第一步安装Docker Compose。
  • 完全注册的域名。 本教程将自始至终使用 your_domain。 您可以在 Freenom 上免费获得一个,或使用您选择的域名注册商。
  • 带有 your_domain 的 DNS “A” 记录指向您服务器的公共 IP 地址。 您可以关注这个介绍到DigitalOcean DNS,详细了解如何添加它们。
  • 了解 Docker 及其架构。 有关 Docker 的介绍,请参阅 Docker 生态系统:通用组件简介

第 1 步 - 创建示例 Go Web 应用程序

在此步骤中,您将设置工作区并创建一个简单的 Go Web 应用程序,稍后您将对其进行容器化。 Go 应用程序将使用功能强大的 gorilla/mux 请求路由器,选择它的灵活性和速度。

对于本教程,您将在 ~/go-docker 下存储所有数据。 运行以下命令来执行此操作:

mkdir ~/go-docker

导航到它:

cd ~/go-docker

您将把示例 Go web 应用程序存储在一个名为 main.go 的文件中。 使用您的文本编辑器创建它:

nano main.go

添加以下行:

~/go-docker/main.go

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()

    r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>This is the homepage. Try /hello and /hello/Sammy\n</h1>")
    })

    r.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello from Docker!\n</h1>")
    })

    r.HandleFunc("/hello/{name}", func(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        title := vars["name"]

        fmt.Fprintf(w, "<h1>Hello, %s!\n</h1>", title)
    })

    http.ListenAndServe(":80", r)
}

您首先导入 net/httpgorilla/mux 包,它们提供 HTTP 服务器功能和路由。

gorilla/mux 包实现了更简单、更强大的请求路由器和调度程序,同时保持与标准路由器的接口兼容性。 在这里,您实例化一个新的 mux 路由器并将其存储在变量 r 中。 然后,定义三个路由://hello/hello/{name}。 第一个 (/) 用作主页,您包含该页面的消息。 第二个 (/hello) 向访问者返回问候语。 对于第三条路由 (/hello/{name}),您指定它应该将名称作为参数并显示插入名称的问候消息。

在文件的末尾,您使用 http.ListenAndServe 启动 HTTP 服务器,并使用您配置的路由器指示它在端口 80 上侦听。

保存并关闭文件。

在运行您的 Go 应用程序之前,您首先需要编译并打包它以在 Docker 容器中执行。 Go 是一种 编译语言 ,因此在程序可以运行之前,编译器会将编程代码翻译成可执行的机器代码。

您已设置工作区并创建了一个示例 Go Web 应用程序。 接下来,您将使用自动化的 Let's Encrypt 证书配置部署 nginx-proxy

第 2 步 — 使用 Let's Encrypt 部署 nginx-proxy

使用 HTTPS 保护您的应用程序非常重要。 为此,您将通过 Docker Compose 部署 nginx-proxy 及其 Let's Encrypt 附加组件 。 这可以保护使用 nginx-proxy 代理的 Docker 容器,并通过自动处理 TLS 证书创建和更新来通过 HTTPS 保护您的应用程序。

您将把 nginx-proxy 的 Docker Compose 配置存储在一个名为 nginx-proxy-compose.yaml 的文件中。 通过运行创建它:

nano nginx-proxy-compose.yaml

将以下行添加到文件中:

~/go-docker/nginx-proxy-compose.yaml

version: '2'

services:
  nginx-proxy:
    restart: always
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/etc/nginx/vhost.d"
      - "/usr/share/nginx/html"
      - "/var/run/docker.sock:/tmp/docker.sock:ro"
      - "/etc/nginx/certs"

  letsencrypt-nginx-proxy-companion:
    restart: always
    image: jrcs/letsencrypt-nginx-proxy-companion
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    volumes_from:
      - "nginx-proxy"

在这里,您定义了两个容器:一个用于 nginx-proxy,另一个用于 Let's Encrypt 附加组件 (letsencrypt-nginx-proxy-companion)。 对于代理,您指定图像 jwilder/nginx-proxy,公开和映射 HTTP 和 HTTPS 端口,最后定义容器可访问的卷,以保存 Nginx 相关数据。

在第二个块中,您为 Let's Encrypt 附加配置的图像命名。 然后,通过定义一个卷,然后从代理容器中继承现有卷来配置对 Docker 套接字的访问。 两个容器都将 restart 属性设置为 always,这指示 Docker 始终保持它们运行(在崩溃或系统重新启动的情况下)。

保存并关闭文件。

通过运行部署 nginx-proxy

docker-compose -f nginx-proxy-compose.yaml up -d

Docker Compose 通过 -f 标志接受自定义命名文件。 up 命令运行容器,-d 标志,分离模式,指示它在后台运行容器。

您的最终输出将如下所示:

OutputCreating network "go-docker_default" with the default driver
Pulling nginx-proxy (jwilder/nginx-proxy:)...
latest: Pulling from jwilder/nginx-proxy
a5a6f2f73cd8: Pull complete
2343eb083a4e: Pull complete
...
Digest: sha256:619f390f49c62ece1f21dfa162fa5748e6ada15742e034fb86127e6f443b40bd
Status: Downloaded newer image for jwilder/nginx-proxy:latest
Pulling letsencrypt-nginx-proxy-companion (jrcs/letsencrypt-nginx-proxy-companion:)...
latest: Pulling from jrcs/letsencrypt-nginx-proxy-companion
...
Creating go-docker_nginx-proxy_1 ... done
Creating go-docker_letsencrypt-nginx-proxy-companion_1 ... done

您已经使用 Docker Compose 部署了 nginx-proxy 及其 Let's Encrypt 伴侣。 接下来,您将为 Go Web 应用程序创建一个 Dockerfile。

第 3 步 — 将 Go Web 应用程序 Docker 化

在本节中,您将创建一个 Dockerfile,其中包含有关 Docker 如何为您的 Go Web 应用程序创建不可变映像的说明。 Docker 使用 Dockerfile 中的指令构建一个不可变的应用程序映像(类似于容器的快照)。 每次运行基于特定图像的容器时,图像的不变性保证了相同的环境。

使用文本编辑器创建 Dockerfile

nano Dockerfile

添加以下行:

~/go-docker/Dockerfile

FROM golang:alpine AS build
RUN apk --no-cache add gcc g++ make git
WORKDIR /go/src/app
COPY . .
RUN go mod init webserver
RUN go mod tidy
RUN GOOS=linux go build -ldflags="-s -w" -o ./bin/web-app ./main.go

FROM alpine:3.13
RUN apk --no-cache add ca-certificates
WORKDIR /usr/bin
COPY --from=build /go/src/app/bin /go/bin
EXPOSE 80
ENTRYPOINT /go/bin/web-app --port 80

这个 Dockerfile 有两个阶段。 第一阶段使用 golang:alpine 基础,其中包含在 Alpine Linux 上预安装的 Go。

然后安装 gccg++makegit 作为 Go 应用程序的必要编译工具。 您将工作目录设置为 /go/src/app,它位于默认的 GOPATH 下。 您还将当前目录的内容复制到容器中。 第一阶段以递归方式从代码中获取使用的包并编译 main.go 文件以在没有符号和调试信息的情况下发布(通过传递 -ldflags="-s -w")结束。 当你编译一个 Go 程序时,它会保留二进制文件的一个单独部分,用于调试,但是,这些额外的信息会占用内存,并且在部署到生产环境时不需要保留。

第二阶段基于 alpine:3.13 (Alpine Linux 3.13)。 它安装受信任的 CA 证书,将编译的应用程序二进制文件从第一阶段复制到当前映像,公开端口 80,并将应用程序二进制文件设置为映像入口点。

保存并关闭文件。

你已经为你的 Go 应用程序创建了一个 Dockerfile,它将获取它的包,编译它以发布,并在容器创建时运行它。 在下一步中,您将创建 Docker Compose yaml 文件并通过在 Docker 中运行它来测试应用程序。

第 4 步 - 创建和运行 Docker Compose 文件

现在,您将创建 Docker Compose 配置文件并编写运行在上一步中创建的 Docker 映像所需的配置。 然后,您将运行它并检查它是否正常工作。 通常,Docker Compose 配置文件指定应用程序所需的容器、它们的设置、网络和卷。 您还可以指定这些元素可以同时启动和停止。

您将把 Go Web 应用程序的 Docker Compose 配置存储在一个名为 go-app-compose.yaml 的文件中。 通过运行创建它:

nano go-app-compose.yaml

将以下行添加到此文件中:

~/go-docker/go-app-compose.yaml

version: '2'
services:
  go-web-app:
    restart: always
    build:
      dockerfile: Dockerfile
      context: .
    environment:
      - VIRTUAL_HOST=your_domain
      - LETSENCRYPT_HOST=your_domain

请记住将 your_domain 两次都替换为您的域名。 保存并关闭文件。

此 Docker Compose 配置包含一个容器 (go-web-app),它将成为您的 Go Web 应用程序。 它使用您在上一步中创建的 Dockerfile 构建应用程序,并将包含源代码的当前目录作为构建的上下文。 此外,它设置了两个环境变量:VIRTUAL_HOSTLETSENCRYPT_HOSTnginx-proxy 使用 VIRTUAL_HOST 知道从哪个域接受请求。 LETSENCRYPT_HOST指定生成TLS证书的域名,必须与VIRTUAL_HOST相同,除非指定通配符域。

现在,您将使用以下命令通过 Docker Compose 在后台运行您的 Go Web 应用程序:

docker-compose -f go-app-compose.yaml up -d

您的最终输出将如下所示:

OutputCreating network "go-docker_default" with the default driver
Building go-web-app
Step 1/12 : FROM golang:alpine AS build
 ---> b97a72b8e97d
...
Successfully tagged go-docker_go-web-app:latest
WARNING: Image for service go-web-app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating go-docker_go-web-app_1 ... done

如果您查看运行命令后显示的输出,Docker 会根据 Dockerfile 中的配置记录构建应用程序映像的每个步骤。

您现在可以导航到 https://your_domain/ 以查看您的主页。 在您的 Web 应用程序的家庭地址中,您看到的页面是您在第一步中定义的 / 路由的结果。

现在导航到 https://your_domain/hello。 您将看到您在代码中为步骤 1 中的 /hello 路由定义的消息。

最后,尝试将名称附加到您的 Web 应用程序的地址以测试其他路由,例如:https://your_domain/hello/Sammy

注意: 如果您收到有关无效 TLS 证书的错误,请等待几分钟,让 Let's Encrypt 插件配置证书。 如果您在短时间内仍然收到错误,请根据此步骤中显示的命令和配置仔细检查您输入的内容。


您已经创建了 Docker Compose 文件并编写了用于在容器中运行 Go 应用程序的配置。 最后,您导航到您的域以检查 gorilla/mux 路由器设置是否正确地为您的 Dockerized Go Web 应用程序提供请求。

结论

您现在已经在 Ubuntu 18.04 上使用 Docker 和 Nginx 成功部署了 Go Web 应用程序。 使用 Docker,维护应用程序变得不那么麻烦,因为每次运行应用程序的环境都保证是相同的。 gorilla/mux 软件包具有出色的文档并提供更复杂的功能,例如命名路由和提供静态文件。 如需对 Go HTTP 服务器模块的更多控制,例如定义自定义超时,请访问 官方文档