著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。

序章

Docker は、今日使用されている最も一般的なコンテナ化ソフトウェアです。 これにより、開発者はアプリを環境と一緒に簡単にパッケージ化できるため、実行ごとに同じ目的の環境を提供しながら、反復サイクルを短縮し、リソース効率を向上させることができます。 Docker Compose は、最新のアプリ要件を容易にするコンテナーオーケストレーションツールです。 これにより、相互接続された複数のコンテナーを同時に実行できます。 コンテナーを手動で実行する代わりに、オーケストレーションツールを使用すると、開発者はコンテナーを同時に制御、スケーリング、および拡張できます。

NginxをフロントエンドWebサーバーとして使用する利点は、そのパフォーマンス、構成可能性、およびTLSターミネーションであり、アプリがこれらのタスクを完了する必要がなくなります。 nginx-proxy は、Dockerコンテナー用の自動システムであり、リバースプロキシとして機能するようにNginxを構成するプロセスを大幅に簡素化します。 そのLet’sEncrypt アドオンは、nginx-proxyに付随して、プロキシされたコンテナの証明書の生成と更新を自動化できます。

このチュートリアルでは、 gorilla / mux をリクエストルーターとして、NginxをWebサーバーとして、すべてDockerコンテナー内に配置し、DockerComposeによって調整されたGoWebアプリケーションの例をデプロイします。 Let’sEncryptアドオンをリバースプロキシとしてnginx-proxyを使用します。 このチュートリアルの最後に、Dockerを使用し、Let’s Encrypt証明書で保護された、複数のルートを使用してドメインでアクセス可能なGoWebアプリをデプロイします。

前提条件

  • ルート権限を持つUbuntu18.04サーバー、およびセカンダリの非ルートアカウント。 これは、この初期サーバーセットアップガイドに従ってセットアップできます。 このチュートリアルでは、root以外のユーザーはsammyです。
  • Ubuntu18.04にDockerをインストールする方法の最初の2つの手順に従ってDockerをインストールします。
  • Ubuntu18.04にDockerComposeをインストールする方法の最初の手順に従ってインストールされたDockerCompose。
  • 完全に登録されたドメイン名。 このチュートリアルでは、全体を通してyour_domainを使用します。 Freenom で無料で入手するか、選択したドメインレジストラを使用できます。
  • サーバーのパブリックIPアドレスを指すyour_domainを含むDNS「A」レコード。 それらを追加する方法の詳細については、この紹介に従ってDigitalOceanDNSを参照できます。
  • Dockerとそのアーキテクチャーの理解。 Dockerの概要については、 Dockerエコシステム:一般的なコンポーネントの概要を参照してください。

ステップ1—サンプルのGoWebアプリを作成する

このステップでは、ワークスペースを設定し、後でコンテナ化する単純なGoWebアプリを作成します。 Goアプリは、柔軟性と速度で選択された強力な gorilla /muxリクエストルーターを使用します。

このチュートリアルでは、すべてのデータを~/go-dockerの下に保存します。 これを行うには、次のコマンドを実行します。

  1. mkdir ~/go-docker

そこに移動します:

  1. cd ~/go-docker

サンプルのGoWebアプリを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/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}の3つのルートを定義します。 最初の(/)はホームページとして機能し、ページへのメッセージを含めます。 2番目(/hello)は、訪問者に挨拶を返します。 3番目のルート(/hello/{name})では、パラメーターとして名前を取り、名前が挿入されたグリーティングメッセージを表示するように指定します。

ファイルの最後で、http.ListenAndServeでHTTPサーバーを起動し、構成したルーターを使用して、ポート80でリッスンするように指示します。

ファイルを保存して閉じます。

Goアプリを実行する前に、まずコンパイルして、Dockerコンテナー内で実行できるようにパックする必要があります。 Goはコンパイル言語であるため、プログラムを実行する前に、コンパイラはプログラミングコードを実行可能マシンコードに変換します。

ワークスペースを設定し、サンプルのGoWebアプリを作成しました。 次に、自動化されたLet’sEncrypt証明書のプロビジョニングを使用してnginx-proxyを展開します。

ステップ2—Let’sEncryptを使用してnginx-proxyをデプロイする

HTTPSでアプリを保護することが重要です。 これを実現するには、DockerComposeを介してnginx-proxyを、Let’sEncryptアドオンとともにデプロイします。 これにより、nginx-proxyを使用してプロキシされたDockerコンテナーが保護され、TLS証明書の作成と更新が自動的に処理されるため、HTTPSを介してアプリが保護されます。

nginx-proxyのDockerCompose構成をnginx-proxy-compose.yamlという名前のファイルに保存します。 次のコマンドを実行して作成します。

  1. nano nginx-proxy-compose.yaml

次の行をファイルに追加します。

〜/ go-docker / 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"

ここでは、2つのコンテナーを定義しています。1つはnginx-proxy用で、もう1つはLet’s Encryptアドオン(letsencrypt-nginx-proxy-companion)用です。 プロキシの場合、イメージjwilder/nginx-proxyを指定し、HTTPポートとHTTPSポートを公開してマッピングし、最後にNginx関連データを永続化するためにコンテナーにアクセスできるボリュームを定義します。

2番目のブロックでは、Let’sEncryptアドオン構成のイメージに名前を付けます。 次に、ボリュームを定義し、次に継承するプロキシコンテナーから既存のボリュームを定義することにより、Dockerのソケットへのアクセスを構成します。 両方のコンテナーのrestartプロパティはalwaysに設定されており、Dockerに常にコンテナーを維持するように指示します(クラッシュまたはシステムの再起動の場合)。

ファイルを保存して閉じます。

次のコマンドを実行して、nginx-proxyをデプロイします。

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

Docker Composeは、-fフラグを介してカスタムの名前付きファイルを受け入れます。 upコマンドはコンテナーを実行し、-dフラグ(デタッチモード)は、コンテナーをバックグラウンドで実行するように指示します。

最終的な出力は次のようになります。

Output
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

DockerComposeを使用してnginx-proxyとそのLet’sEncryptコンパニオンをデプロイしました。 次に、GoWebアプリ用のDockerfileを作成します。

ステップ3—GoWebアプリをDocker化する

このセクションでは、DockerがGoWebアプリの不変イメージを作成する方法に関する手順を含むDockerfileを作成します。 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.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には2つの段階があります。 最初のステージでは、golang:alpineベースを使用します。このベースには、AlpineLinuxにプリインストールされたGoが含まれています。

次に、Goアプリに必要なコンパイルツールとして、gccg++make、およびgitをインストールします。 作業ディレクトリを/go/src/appに設定します。これは、デフォルトのGOPATHの下にあります。 また、現在のディレクトリの内容をコンテナにコピーします。 最初の段階は、コードから使用されるパッケージを再帰的にフェッチし、シンボルとデバッグ情報なしでリリースするためにmain.goファイルをコンパイルすることで終了します(-ldflags="-s -w"を渡すことによって)。 Goプログラムをコンパイルすると、デバッグに使用されるバイナリの別の部分が保持されますが、この追加情報はメモリを使用するため、実稼働環境にデプロイするときに保持する必要はありません。

第2段階は、alpine:3.13(Alpine Linux 3.13)に基づいています。 信頼できるCA証明書をインストールし、コンパイルされたアプリバイナリを最初のステージから現在のイメージにコピーし、ポート80を公開し、アプリバイナリをイメージエントリポイントとして設定します。

ファイルを保存して閉じます。

Goアプリ用のDockerfileを作成しました。このファイルは、パッケージをフェッチし、リリース用にコンパイルして、コンテナーの作成時に実行します。 次のステップでは、Docker Compose yamlファイルを作成し、Dockerで実行してアプリをテストします。

ステップ4—Docker作成ファイルの作成と実行

次に、Docker Compose構成ファイルを作成し、前の手順で作成したDockerイメージを実行するために必要な構成を記述します。 次に、それを実行して、正しく機能するかどうかを確認します。 一般に、Docker Compose構成ファイルは、アプリが必要とするコンテナー、それらの設定、ネットワーク、およびボリュームを指定します。 これらの要素を同時に開始および停止できるように指定することもできます。

GoWebアプリのDockerCompose構成をgo-app-compose.yamlという名前のファイルに保存します。 次のコマンドを実行して作成します。

  1. nano go-app-compose.yaml

このファイルに次の行を追加します。

〜/ go-docker / 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を両方ともドメイン名に置き換えることを忘れないでください。 ファイルを保存して閉じます。

このDockerCompose構成には、Go Webアプリとなる1つのコンテナー(go-web-app)が含まれています。 前の手順で作成したDockerfileを使用してアプリをビルドし、ビルドのコンテキストとしてソースコードを含む現在のディレクトリを取得します。 さらに、VIRTUAL_HOSTLETSENCRYPT_HOSTの2つの環境変数を設定します。 nginx-proxyは、VIRTUAL_HOSTを使用して、要求を受け入れるドメインを認識します。 LETSENCRYPT_HOSTは、TLS証明書を生成するためのドメイン名を指定します。ワイルドカードドメインを指定しない限り、VIRTUAL_HOSTと同じである必要があります。

次に、次のコマンドを使用して、DockerComposeを介してGoWebアプリをバックグラウンドで実行します。

  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/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://your_domain/に移動してホームページを表示できます。 Webアプリのホームアドレスに、最初のステップで定義した/ルートの結果としてページが表示されます。

This is the homepage. Try /hello and /hello/Sammy

次に、https://your_domain/helloに移動します。 手順1の/helloルートのコードで定義したメッセージが表示されます。

Hello from Docker!

最後に、https://your_domain/hello/Sammyのように、Webアプリのアドレスに名前を追加して、他のルートをテストしてみてください。

Hello, Sammy!

注:無効なTLS証明書に関するエラーを受け取った場合は、Let’sEncryptアドオンが証明書をプロビジョニングするまで数分待ちます。 しばらくしてもエラーが発生する場合は、入力した内容をこの手順に示されているコマンドと構成と照らし合わせて再確認してください。

Docker Composeファイルを作成し、コンテナー内でGoアプリを実行するための構成を記述しました。 最後に、ドメインに移動して、gorilla/muxルーターのセットアップがDockerizedGoWebアプリへのリクエストを正しく処理していることを確認しました。

結論

これで、Ubuntu18.04にDockerとNginxを使用してGoWebアプリを正常にデプロイできました。 Dockerを使用すると、アプリが実行される環境が実行されるたびに同じであることが保証されるため、アプリケーションの保守が簡単になります。 gorilla / mux パッケージには優れたドキュメントがあり、ルートの命名や静的ファイルの提供など、より高度な機能を提供します。 カスタムタイムアウトの定義など、Go HTTPサーバーモジュールをさらに制御するには、公式ドキュメントにアクセスしてください。