Ubuntu 22.04でDockerとNginxを使用してGoウェブアプリケーションを展開する方法
著者は、寄付の一環として「Write for Donations」プログラムの一環として、フリーでオープンソースのファンドを選択しました。
はじめに
Dockerはよく使われているコンテナ化ソフトウェアであり、開発者がアプリケーションとその環境を簡単にパッケージ化することができます。これにより、より迅速なイテレーションサイクルとリソース効率の向上が可能となり、各実行時に同じ環境を提供します。Docker Composeはコンテナオーケストレーションツールであり、現代のアプリケーション要件をサポートします。複数の相互接続されたコンテナを同時に実行することができます。手動でコンテナを実行する代わりに、オーケストレーションツールは開発者がコンテナを同時に制御、拡張、スケーリングする能力を提供します。
Nginxをフロントエンドウェブサーバーとして使用する利点は、パフォーマンス、設定可能性、およびTLS終端化にあり、これらのタスクをアプリから解放します。nginx-proxyは、Nginxをリバースプロキシとして設定するプロセスを簡素化するDockerコンテナ向けの自動化システムです。そのLet’s Encryptアドオンは、nginx-proxyと共に使用して、プロキシ化されたコンテナの証明書の生成と更新を自動化することができます。
このチュートリアルでは、gorilla/muxをリクエストルーターとして使用し、Nginxをウェブサーバーとして使用したGoのウェブアプリケーションの例をDockerコンテナ内で展開します。展開はDocker Composeによってオーケストレーションされます。リバースプロキシとして、nginx-proxyとLet’s Encryptのアドオンを使用します。このチュートリアルの最後には、Let’s Encryptの証明書で保護された複数のルートを持つGoのウェブアプリケーションを、あなたのドメインでアクセス可能に展開することができます。
前提条件
- 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 sammy.
- 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.
ステップ1 – サンプルのGoウェブアプリの作成
このステップでは、ワークスペースをセットアップし、後でコンテナ化するためのシンプルなGoウェブアプリを作成します。柔軟性と高速性を理由に選ばれた強力なgorilla/muxリクエストルーターを使用します。
このチュートリアルでは、すべてのデータを~/go-dockerの下に保存します。このフォルダを作成するために、以下のコマンドを実行します。
- mkdir ~/go-docker
それにアクセスしてください。
- cd ~/go-docker
あなたの例のGoウェブアプリを、main.goという名前のファイルに保存します。テキストエディタを使用してそれを作成してください。
- nano main.go
以下の行を追加してください。
~/go-docker/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/httpおよびgorilla/muxパッケージをインポートします。これらはHTTPサーバーの機能とルーティングを提供します。gorilla/muxパッケージは、より強力なリクエストルーターとディスパッチャーを実装しており、標準のルーターとのインターフェース互換性も保持しています。新しいmuxルーターをインスタンス化し、変数rに保存します。
次に、/、/hello、および/hello/{名前}の3つのルートを定義します。最初の(/)はホームページとして機能し、ページのメッセージを含めます。2番目の(/hello)は訪問者への挨拶を返します。3つ目のルート(/hello/{名前})では、名前をパラメータとして受け取り、名前が挿入された挨拶メッセージを表示するように指定します。
ファイルの最後で、http.ListenAndServeを使用してHTTPサーバーを起動し、設定したルーターを使用してポート80で受信するように指示します。
ファイルを保存して閉じてください。
Goのアプリを実行する前に、Dockerコンテナ内で実行するためにコンパイルしてパッケージ化する必要があります。Goはコンパイル言語なので、プログラムが実行される前に、コンパイラがプログラミングコードを実行可能な機械語に変換します。
ワークスペースをセットアップし、Go言語のウェブアプリの例を作成しました。次に、自動的に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 -> 「~/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-proxyとそのLet’s Encryptアドオン(letsencrypt-nginx-proxy-companion)の2つのコンテナを定義します。プロキシには、jwilder/nginx-proxyイメージを指定し、HTTPおよびHTTPSポートを公開およびマッピングし、Nginx関連データを永続化するためにコンテナからアクセス可能なボリュームを定義します。
2番目のブロックでは、Let’s Encryptのアドオン設定のイメージを指定します。そして、ボリュームを定義してDockerのソケットへのアクセスを設定し、プロキシコンテナから既存のボリュームを引き継ぎます。両方のコンテナには再起動のプロパティが常に設定されており、それによってDockerは常にコンテナを起動し続けるようにします(クラッシュやシステムの再起動の場合でも)。
ファイルを保存して閉じる。
「nginx-proxyを実行して展開してください。」
- docker compose -f nginx-proxy-compose.yaml up -d
Docker Composeは、-fフラグを通じてカスタム名のファイルを受け入れます。upコマンドはコンテナーを実行し、-dフラグ(デタッチモード)はコンテナーをバックグラウンドで実行するように指示します。
このような出力が届きます。
[+] 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ウェブアプリのためのDockerfileを作成します。
ステップ3 – Go WebアプリをDocker化する
このセクションでは、GoのWebアプリケーション用にDockerが不変なイメージを作成するための指示を含むDockerfileを準備します。DockerはDockerfile内の指示を使用して、コンテナのスナップショットのような不変なアプリケーションイメージを構築します。イメージの不変性は、特定のイメージをベースにしたコンテナが実行されるたびに、常に同じ環境を保証します。
あなたのテキストエディタでDockerfileを作成してください。
- nano 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には2つのステージがあります。最初のステージでは、golang:alpineベースを使用し、Alpine Linux上で事前にインストールされたGoが含まれています。
Goアプリの必要なコンパイルツールとしてgcc、g++、make、gitをインストールします。作業ディレクトリをデフォルトのGOPATHの/go/src/appに設定します。現在のディレクトリの内容もコンテナにコピーします。最初のステージでは、コードから使用されているパッケージを再帰的に取得し、main.goファイルをシンボルとデバッグ情報なしでリリース用にコンパイルします(-ldflags=”-s -w”を指定しています)。
Note
第2ステージはalpine:3.17(Alpine Linux 3.17)をベースにしています。信頼できるCA証明書をインストールし、最初のステージからコンパイルされたアプリバイナリを現在のイメージにコピーし、ポート80を公開し、アプリバイナリをイメージのエントリーポイントに設定します。
ファイルを保存して閉じる。
あなたはGoアプリのためのDockerfileを作成しました。このDockerfileは、パッケージを取得し、リリースのためにコンパイルし、コンテナーの作成時に実行するアプリケーションを取得します。次のステップでは、Docker Composeのyamlファイルを作成し、Dockerでアプリを実行してテストします。
ステップ4 – Docker Composeファイルの作成と実行
今度は、お前はDocker Composeの設定ファイルを作成し、前の手順で作成したDockerイメージを実行するための必要な設定を書く。そして、それを実行して正しく動作するかどうかを確認する。一般的に、Docker Composeの設定ファイルは、アプリが必要とするコンテナ、設定、ネットワーク、およびボリュームを指定します。また、これらの要素が開始および停止するようにも指定することができます。
GoのウェブアプリのDocker Composeの設定をgo-app-compose.yamlというファイルに保存します。次のコマンドを実行して作成してください。
- nano go-app-compose.yaml
このファイルに以下の行を追加してください。
以下は、ネイティブの日本語での表現です。一つのオプションを提供します。
「~/go-docker/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の設定には1つのコンテナ(go-web-app)が含まれており、それがGoのWebアプリケーションとなります。この設定では、前のステップで作成したDockerfileを使用してアプリケーションをビルドし、ソースコードが含まれる現在のディレクトリをビルドのコンテキストとして使用します。さらに、2つの環境変数(VIRTUAL_HOSTとLETSENCRYPT_HOST)を設定しています。nginx-proxyはVIRTUAL_HOSTを使用してどのドメインからのリクエストを受け入れるかを知るために使用します。LETSENCRYPT_HOSTはTLS証明書を生成するためのドメイン名を指定し、VIRTUAL_HOSTと同じでなければなりません。ワイルドカードドメインを指定しない限り、LETSENCRYPT_HOSTは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/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/に移動することができます。
今、https://your_domain/hello に移動してください。Step1で定義した/helloルートのコードによるメッセージが表示されます。
最後に、ウェブアプリのアドレスに名前を追加して別のルートをテストしてください。例えば、https://your_domain/hello/Sammy のようにしてください。
Note
Docker Compose ファイルを作成し、コンテナ内で Go アプリを実行するための設定を記述しました。最後に、ごりら/mux ルーターの設定が Docker 化された Go ウェブアプリに正しくリクエストを提供しているかを確認するために、ドメインに移動しました。
結論
現在、Ubuntu 22.04上でDockerとNginxを使用してGoのWebアプリを正常にデプロイしました。Dockerを使用することで、アプリケーションのメンテナンスにかかる時間が短縮されます。なぜなら、実行環境が毎回実行されるごとに同じであることが保証されるからです。また、gorilla/muxパッケージは優れたドキュメンテーションを提供しており、ルートの名前付けや静的ファイルの提供など、より高度な機能を提供しています。GoのHTTPサーバーモジュールのより細かな制御を行いたい場合は、公式のドキュメントにアクセスしてください。