Ubuntu 22.04上使用Docker和Nginx部署Go Web应用:完整指南

简介

Docker是一种流行的容器化软件,它使开发人员能够轻松打包应用程序及其运行环境,从而实现更快的迭代周期和更高的资源利用效率,同时确保每次运行都能提供一致的预期环境。Docker Compose则是一款强大的容器编排工具,专为满足现代应用程序的需求而设计,它允许同时运行多个相互连接的容器。与手动启动容器不同,容器编排工具赋予开发人员同时控制、扩展和管理容器的能力。

选择Nginx作为前端Web服务器具有显著优势,包括卓越的性能、灵活的配置能力以及强大的TLS终止功能。这些特性能够有效减轻应用程序在处理这些任务上的负担。而nginx-proxy是一个自动化的Docker容器系统,它极大地简化了将Nginx配置为反向代理的过程。其Let’s Encrypt插件可以与nginx-proxy无缝协作,自动为代理容器生成并更新SSL证书。

在本教程中,您将学习如何在一个由Docker Compose编排的Docker容器环境中部署一个示例Go Web应用程序。该应用程序将使用gorilla/mux作为请求路由器,并以Nginx作为Web服务器。您还将利用带有Let’s Encrypt插件的nginx-proxy作为反向代理。完成本教程后,您将能够使用Docker部署一个具有多个路由的Go Web应用程序,该应用程序可通过您的域名访问,并由Let’s Encrypt证书提供安全保护。

先决条件

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

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

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

  1. mkdir ~/go-docker

然后进入该目录:

  1. cd ~/go-docker

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

  1. nano main.go

添加以下内容:

这是文章《如何在Ubuntu 22.04上使用Docker和Nginx部署Go Web应用程序》的第2部分(共4部分)。

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>这是主页。请尝试访问 /hello 和 /hello/James
</h1>")
	})

	r.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "<h1>来自 Docker 的问候!
</h1>")
	})

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

		fmt.Fprintf(w, "<h1>你好,%s!
</h1>", title)
	})

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

首先,您需要导入 net/httpgithub.com/gorilla/mux 包。net/http 包提供了HTTP服务器功能,而 gorilla/mux 包则提供了更强大的请求路由器和分发器,同时保持与标准路由器的接口兼容性。

您需要实例化一个新的 mux 路由器,并将其保存在变量 r 中。

然后,您定义了三个路由://hello/hello/{name}。第一个路由(/)作为主页,并包含了一条提示信息。第二个路由(/hello)向访问者返回问候语。对于第三个路由(/hello/{name}),您指定它应该接受一个名为 name 的参数,并显示一条包含该名称的问候信息。

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

保存并关闭文件。

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

至此,您已经搭建好了工作空间并创建了一个示例的Go Web应用程序。接下来,您将使用自动化的Let’s Encrypt证书部署Nginx代理。

步骤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

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

在第二个模块中,您为Let’s Encrypt附加组件配置了命名镜像。然后,您通过定义一个卷以及从代理容器继承现有卷来配置对Docker套接字的访问。两个容器都将restart属性设置为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

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。

然后,您将gccg++makegit安装为Go应用程序的必要编译工具。您将工作目录设置为/go/src/app,该目录位于默认的GOPATH下。您还将当前目录的内容复制到容器中。第一阶段通过递归获取代码中使用的软件包,并使用-ldflags="-s -w"参数编译main.go文件,以在发布时去除符号和调试信息。

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

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

保存并关闭文件。

你已经为你的Go应用程序创建了一个Dockerfile,它能够在容器创建时获取依赖、编译并运行应用程序。下一步,你将创建Docker Compose YAML文件,并在Docker中运行它来测试这个应用程序。

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

通过Docker Compose在后台运行你的Go Web应用程序。

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

将打印出以下输出(为便于阅读,已进行截断):

输出
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/来访问你的首页。在你的Web应用程序的主要地址,你可以通过在第一步中定义的/路由来访问该页面。

显示“这是主页。尝试/hello和/hello/James”的域名页面截图

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

显示“来自Docker的问候!”的/hello路由截图

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

显示“你好,James!”的带名字路由截图,其中名字输入为“James”

注意:

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

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

结论

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

bannerAds