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

简介

Docker是一种常用的容器化软件,它使开发人员能够轻松打包应用程序和运行环境,以实现更快的迭代周期和更好的资源利用效率,同时在每次运行时提供相同的期望环境。Docker Compose是一种容器编排工具,可以实现现代应用程序的需求。它允许同时运行多个相互连接的容器。与手动运行容器不同,容器编排工具给开发人员提供了同时控制、扩展和管理容器的能力。

使用Nginx作为前端Web服务器的好处在于性能、可配置性和TLS终止功能,可使应用程序摆脱完成这些任务的负担。nginx-proxy是一个自动化的Docker容器系统,简化了配置Nginx作为反向代理的过程。它的Let’s Encrypt插件可以与nginx-proxy一起自动生成和更新代理容器的证书。

在本教程中,您将使用gorilla/mux作为请求路由器和Nginx作为Web服务器,在由Docker Compose编排的Docker容器中部署一个示例的Go Web应用程序。您将使用Let’s Encrypt插件的nginx-proxy作为反向代理。在本教程的最后,您将使用Docker部署一个可通过您的域名访问的具有多个路由的Go Web应用程序,并且使用Let’s Encrypt证书进行安全保护。

先决条件

  • An Ubuntu 22.04 server with root privileges, and a secondary, non-root account. You can set this up by following this initial server setup guide. For this tutorial, the non-root user is james.
  • Docker installed by following the first two steps of How To Install Docker on Ubuntu 22.04.
  • Docker Compose installed by following the first step of How To Install Docker Compose on Ubuntu 22.04.
  • A fully registered domain name. This tutorial will use your_domain throughout. You can get one for free on Freenom, or use the domain registrar of your choice.
  • A DNS “A” record with your_domain pointing to your server’s public IP address. You can follow this introduction to Silicon Cloud DNS for details on how to add them.
  • An understanding of Docker and its architecture. For an introduction to Docker, see The Docker Ecosystem: An Introduction to Common Components.

第一步:创建一个示例的Go Web应用程序。

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

对于本教程,您将把所有数据存储在~/go-docker文件夹下。运行以下命令创建该文件夹:

  1. mkdir ~/go-docker

前往该处:

  1. cd ~/go-docker

您将把您的示例Go网络应用存储在名为main.go的文件中。请使用您的文本编辑器创建它。

  1. 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/James\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.ListenAndServe启动HTTP服务器,并指示它监听使用你配置的路由器的端口80。

保存并关闭文件。

在运行您的Go应用程序之前,您需要将其编译并打包到Docker容器中以进行执行。Go是一种编译语言,所以在程序能够运行之前,编译器会将编程代码转换为可执行的机器代码。

你已经搭建好了你的工作空间并创建了一个示例的Go Web应用程序。接下来,你将使用自动化的Let’s Encrypt证书提供部署nginx-proxy。

步骤2—使用Let’s Encrypt部署nginx代理

重要的是通过HTTPS保护你的应用程序。为了实现这一点,你将使用Docker Compose部署nginx-proxy,以及它的Let’s Encrypt附加组件。通过这种设置,可以通过nginx-proxy代理保护Docker容器,并通过自动处理TLS证书的创建和更新来确保你的应用程序的HTTPS安全。

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

  1. nano nginx-proxy-compose.yaml

将以下行添加到文件中。

~/go-docker/nginx-proxy-compose.yaml 的中文表达:

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

version: '3'

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代理,另一个用于其Let’s Encrypt附加组件(letsencrypt-nginx-proxy-companion)。对于代理,你指定了镜像jwilder/nginx-proxy,暴露并映射HTTP和HTTPS端口,并定义了卷,这些卷可用于持久化Nginx相关数据的容器访问。

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

保存并关闭文件。

运行命令部署nginx代理。

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

Docker Compose可以通过-f标志接受自定义的文件名称。up命令用于运行容器,并且-d标志(后台模式)指示它在后台运行容器。

您会收到类似这样的输出:

Output
[+] Running 21/21 ⠿ letsencrypt-nginx-proxy-companion Pulled 6.8s ⠿ df9b9388f04a Pull complete 3.1s ⠿ 6c6cfd4eaf5b Pull complete 3.9s ⠿ 870307501973 Pull complete 4.3s ⠿ e8ff3435d14f Pull complete 4.5s ⠿ 5b78ba945919 Pull complete 4.8s ⠿ 973b2ca26006 Pull complete 5.0s ⠿ nginx-proxy Pulled 8.1s ⠿ 42c077c10790 Pull complete 3.9s ⠿ 62c70f376f6a Pull complete 5.5s ⠿ 915cc9bd79c2 Pull complete 5.6s ⠿ 75a963e94de0 Pull complete 5.7s ⠿ 7b1fab684d70 Pull complete 5.7s ⠿ db24d06d5af4 Pull complete 5.8s ⠿ e917373dbecf Pull complete 5.9s ⠿ 11e2be9775e9 Pull complete 5.9s ⠿ 9996fa75bc02 Pull complete 6.1s ⠿ d37674efdf77 Pull complete 6.3s ⠿ a45d84576e75 Pull complete 6.3s ⠿ a13c1f42faf7 Pull complete 6.4s ⠿ 4f4fb700ef54 Pull complete 6.5s [+] Running 3/3 ⠿ Network go-docker_default Created 0.1s ⠿ Container go-docker-nginx-proxy-1 Started 0.5s ⠿ Container go-docker-letsencrypt-nginx-proxy-companion-1 Started 0.8s

你已经使用Docker Compose部署了nginx-proxy及其Let’s Encrypt扩展。接下来,你将为你的Go Web应用创建一个Dockerfile。

第三步 — Docker化Go Web应用程序

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

使用您的文本编辑器创建Dockerfile。

  1. nano Dockerfile

请在下面添加以下内容:
请加上以下几行:

~/go-docker/Dockerfile 可以被翻译成 “~/go-docker/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.17
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下。您还将当前目录的内容复制到容器中。第一阶段通过递归获取代码中使用的软件包,并使用-ldflags=”-s -w”参数编译main.go文件,以发布时去除符号和调试信息。

Note

注意:当你编译一个Go程序时,它会保留一个用于调试的独立二进制部分;然而,这些额外的信息会占用内存,并且在部署到生产环境时是不必要保留的。

第二阶段以alpine:3.17(Alpine Linux 3.17)为基础。它安装了可信任的CA证书,将编译好的应用程序二进制文件从第一阶段复制到当前镜像,暴露端口80,并将应用程序二进制文件设置为镜像的入口点。

保存并关闭文件。

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

Step 4 — 创建并运行Docker Compose文件

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

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

  1. nano go-app-compose.yaml

将以下内容添加到此文件中。

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

~/go-docker/应用组合.yaml

version: '3'
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网络应用程序。它使用你在前一步创建的Dockerfile构建应用程序,并采用包含源代码的当前目录作为构建的上下文。此外,它设置了两个环境变量:VIRTUAL_HOST和LETSENCRYPT_HOST。nginx-proxy使用VIRTUAL_HOST来确定从哪个域名接受请求。LETSENCRYPT_HOST指定生成TLS证书的域名,必须与VIRTUAL_HOST相同,除非你指定了通配符域名。

通过Docker Compose在后台运行你的Go web应用。

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

这样的输出将被打印出来(为了易读,该输出已进行了截断)。

Output
Creating network "go-docker_default" with the default driver Building go-web-app Step 1/13 : FROM golang:alpine AS build ---> b97a72b8e97d ... Successfully built 71e4b1ef2e25 Successfully tagged go-docker_go-web-app:latest ... [+] Running 1/1 ⠿ Container go-docker-go-web-app-1 Started

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

现在你可以导航至https://your_domain/来访问你的首页。在你的网络应用的主要地址,你可以通过在第一步中定义的/路由来访问该页面。

Screencapture of the domain page, which reads

现在导航到 https://your_domain/hello。你在步骤1中为/hello路径定义的代码中的消息将会加载。

Screencapture of the  route, which reads

最后,在你的网址中添加一个名字来测试其他路由,例如:https://你的域名/hello/James。

Screencapture of the name route, which you have input as

Note

注意:如果收到无效的TLS证书错误,请等待几分钟,以便让Let’s Encrypt扩展配置证书。如果短时间内仍然出现错误,请仔细对照本步骤中的命令和配置,确认输入的内容是否正确。

你已经创建了Docker Compose文件,并编写了在容器内运行Go应用程序的配置。最后,你导航到你的域名上检查是否gorilla/mux路由器设置正确地为你的Docker化Go网络应用程序提供请求服务。

结论

你现在已经成功地使用Docker和Nginx在Ubuntu 22.04上部署了你的Go Web应用程序。通过使用Docker,应用程序的维护变得不那么耗费时间,因为每次运行时执行的环境都是保证一样的。gorilla/mux包有很好的文档,并提供更复杂的功能,比如命名路由和提供静态文件服务。如果你希望对Go HTTP服务器模块有更多的控制,比如定义自定义超时时间,可以查阅官方文档。

发表回复 0

Your email address will not be published. Required fields are marked *