作为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 编排。您将使用nginx-proxy
Let’s Encrypt 附加组件作为反向代理。在本教程结束时,您将部署一个 Go Web 应用程序,该应用程序可在您的域中使用多个路由访问,使用 Docker,并使用 Let’s Encrypt 证书进行保护。
先决条件
- 具有 root 权限的 Ubuntu 18.04 服务器和一个辅助的非 root 帐户。您可以按照此初始服务器设置指南进行设置。对于本教程,非 root 用户是
sammy
. - 按照如何在 Ubuntu 18.04 上安装 Docker的前两个步骤安装 Docker。
- 按照如何在 Ubuntu 18.04 上安装 Docker Compose 的第一步安装 Docker Compose。
- 完全注册的域名。本教程将
your_domain
贯穿始终。您可以在Freenom 上免费获得一个,或者使用您选择的域名注册商。 your_domain
指向您服务器的公共 IP 地址的DNS “A”记录。您可以按照此介绍给DigitalOcean DNS有关如何添加这些细节。- 了解 Docker 及其架构。有关 Docker 的介绍,请参阅The Docker Ecosystem: An Introduction to Common Components。
第 1 步 – 创建示例 Go Web 应用程序
在此步骤中,您将设置工作区并创建一个简单的 Go Web 应用程序,稍后您将对其进行容器化。Go 应用程序将使用强大的gorilla/mux请求路由器,因其灵活性和速度而被选中。
在本教程中,您将所有数据存储在~/go-docker
. 运行以下命令来执行此操作:
- mkdir ~/go-docker
导航到它:
- cd ~/go-docker
您将示例 Go 网络应用程序存储在名为main.go
. 使用文本编辑器创建它:
- nano 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/http
和gorilla/mux
包,它们提供 HTTP 服务器功能和路由。
该gorilla/mux
包实现了更简单、更强大的请求路由器和调度程序,同时保持与标准路由器的接口兼容性。在这里,您实例化一个新mux
路由器并将其存储在变量中r
。然后,定义三条路线:/
,/hello
,和/hello/{name}
。第一个 ( /
) 用作主页,您包含该页面的消息。第二个 ( /hello
) 向访问者返回问候。对于第三个路由 ( /hello/{name}
),您指定它应将名称作为参数并显示插入了名称的问候消息。
在文件的末尾,您启动 HTTP 服务器http.ListenAndServe
并指示它80
使用您配置的路由器侦听 port 。
保存并关闭文件。
在运行你的 Go 应用程序之前,你首先需要编译并打包它以在 Docker 容器中执行。Go 是一种编译语言,因此在程序运行之前,编译器会将编程代码翻译成可执行的机器代码。
您已经设置了工作区并创建了一个示例 Go Web 应用程序。接下来,您将nginx-proxy
使用自动化的 Let’s Encrypt 证书供应进行部署。
第 2 步 – 使用 Let’s Encrypt 部署 nginx-proxy
使用 HTTPS 保护您的应用程序非常重要。为此,您将nginx-proxy
通过 Docker Compose 及其 Let’s Encrypt插件进行部署。这可以保护使用 代理的 Docker 容器nginx-proxy
,并通过自动处理 TLS 证书创建和更新来通过 HTTPS 保护您的应用程序。
您将把 Docker Compose 配置存储nginx-proxy
在一个名为nginx-proxy-compose.yaml
. 通过运行创建它:
- nano 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
)。对于代理,您指定 image 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
您已经nginx-proxy
使用 Docker Compose部署了它的 Let’s Encrypt 伴侣。接下来,您将为 Go Web 应用程序创建 Dockerfile。
第 3 步 – Docker 化 Go Web 应用程序
在本节中,您将创建一个 Dockerfile,其中包含有关 Docker 如何为您的 Go Web 应用程序创建不可变映像的说明。Docker 使用 Dockerfile 中的指令构建一个不可变的应用程序映像(类似于容器的快照)。每次运行基于特定图像的容器时,图像的不变性保证了相同的环境。
Dockerfile
用你的文本编辑器创建:
- nano 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。
然后安装gcc
、g++
、make
和git
作为 Go 应用程序的必要编译工具。您将工作目录设置为/go/src/app
,它位于默认的GOPATH 下。您还将当前目录的内容复制到容器中。第一阶段以递归方式从代码中获取使用的包并编译main.go
文件以在没有符号和调试信息的情况下发布(通过传递-ldflags="-s -w"
)。当你编译一个 Go 程序时,它会保留一个单独的二进制部分,用于调试,但是,这些额外的信息使用内存,在部署到生产环境时不需要保留。
第二阶段基于alpine:3.13
(Alpine Linux 3.13)。它安装受信任的 CA 证书,将编译的应用程序二进制文件从第一阶段复制到当前映像,公开端口80
,并将应用程序二进制文件设置为映像入口点。
保存并关闭文件。
你已经为你的 Go 应用程序创建了一个 Dockerfile,它将获取它的包,编译它以发布,并在容器创建时运行它。在下一步中,您将创建 Docker Composeyaml
文件并通过在 Docker 中运行它来测试应用程序。
第 4 步 – 创建并运行 Docker Compose 文件
现在,您将创建 Docker Compose 配置文件并编写运行您在上一步中创建的 Docker 映像所需的配置。然后,您将运行它并检查它是否正常工作。通常,Docker Compose 配置文件指定应用程序所需的容器、它们的设置、网络和卷。您还可以指定这些元素可以同时作为一个开始和停止。
您将把 Go Web 应用程序的 Docker Compose 配置存储在一个名为go-app-compose.yaml
. 通过运行创建它:
- nano 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_HOST
和LETSENCRYPT_HOST
。nginx-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 中的配置记录构建应用程序映像的每个步骤。
您现在可以导航到查看您的主页。在您的 Web 应用程序的家庭地址中,您会看到作为您在第一步中定义的路线的结果的页面。https://your_domain/
/
现在导航到. 您将看到您在代码中为步骤 1 中的路由定义的消息。https://your_domain/hello
/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,维护应用程序变得不那么麻烦,因为每次运行应用程序时,执行的环境都保证相同。该大猩猩/ MUX封装具有出色的文档,并提供更先进的功能,如命名和路由服务静态文件。如需对 Go HTTP 服务器模块的更多控制,例如定义自定义超时,请访问官方文档。