如何在Ubuntu 18.04上使用Docker和Nginx部署Go Web应用程序

在本教程中,您将部署一个示例Go Web应用程序,其中gorilla / mux作为请求路由器,Nginx作为Web服务器,所有这些都在Docker容器内,由Docker Compose编排。您将使用带有Let's Encrypt附加组件的nginx-proxy作为反向代理。

作者选择了自由和开源基金作为Write for DOnations计划的一部分进行捐赠。

介绍

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

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

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

先决条件

第1步 - 创建示例Go Web App

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

sammy身份登录开始:

ssh sammy@your_server_ip

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

mkdir ~/go-docker

导航到它:

cd ~/go-docker

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

nano main.go

添加以下行:

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加密证书配置部署nginx-proxy

第2步 - 使用Let的加密部署nginx-proxy

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

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

nano nginx-proxy-compose.yaml

将以下行添加到文件中:

nginx的代理,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的加密附加组件命名图像。 然后,通过定义卷,然后从代理容器中继承现有卷,配置对Docker套接字的访问。 两个容器都将restart属性设置为always ,这指示Docker始终保持它们(在崩溃或系统重新引导的情况下)。

保存并关闭文件。

通过运行以下命令来部署nginx-proxy

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

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

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

Creating 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 App进行Docker化

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

使用文本编辑器创建Dockerfile

nano Dockerfile

添加以下行:

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

FROM alpine:3.9
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 base,其中包含预装的Go on Alpine Linux。

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

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

保存并关闭文件。

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

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

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

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

nano go-app-compose.yaml

将以下行添加到此文件:

去-APP-compose.yaml
version: '2'
services:
  go-web-app:
    restart: always
    build:
      dockerfile: Dockerfile
      context: .
    environment:
      - VIRTUAL_HOST=example.com
      - LETSENCRYPT_HOST=example.com

请记住使用您的域名替换example.com 保存并关闭文件。

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

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

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

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

Creating 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:// example.com /以查看您的主页。 在您的Web应用程序的家庭地址,您看到的页面是您在第一步中定义的/ route的结果。

这是主页。试试/你好和/你好/sammy

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

来自Docker的你好!

最后,尝试在Web应用程序的地址中添加一个名称来测试其他路径,例如: https:// example.com /hello/Sammy

你好,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服务器模块,例如定义自定义超时,请访问官方文档