Docker+DockerCompose を使用して Web アプリケーションをカプセル化する方法

Docker+DockerCompose を使用して Web アプリケーションをカプセル化する方法

この記事では、Docker コンテナを使用してバックエンド、フロントエンド、ゲートウェイを実行する方法を紹介し、最後に DockerCompose を使用してコンテナ オーケストレーションを行う方法について説明します。

テクノロジースタック

フロントエンド

  • 反応する
  • アリのデザイン

後部

  • 行く
  • 虹彩

ゲートウェイ

  • エンギンクス
  • オープンレスティ
  • ルア
  • WeChatビジネス

バックエンドビルドAPI

ここでは EXPOSE 4182 と書いていますが、これはテスト時にのみ使用されます。実際には、本番環境ではバックエンドインターフェースポートを公開しません。
代わりに、コンテナはそれらの間のネットワークを介して相互にアクセスし、最終的に Nginx を使用して転送します。

golang:1.15.5より

ラベル メンテナー="K8sCat <[email protected]>"

エクスポーズ4182

ENV GOPROXY=https://goproxy.cn,direct \
    GO111MODULE=オン

ワークディレクトリ /go/src/github.com/k8scat/containerized-app/api

コピー 。 。

実行 go mod ダウンロード && \
ビルド -o api main.go && \
chmod +x API を実行する

エントリポイント [ "./api" ]

フロントエンドウェブ構築

ここで言及する価値があるのは、フロントエンドは必ずバックエンド インターフェイスを呼び出し、このインターフェイスのアドレスはデプロイメントに応じて変わるということです。
したがって、ここでは ARG 命令を使用してバックエンド インターフェイス アドレスを設定します。これにより、コードを変更する代わりに、イメージをビルドしてバックエンド インターフェイス アドレスを調整するときに--build-arg REACT_APP_BASE_URL=https://example.com/apiを渡すだけで済みます。

もう 1 つのポイントは、Entrypoint と CMD が同時に使用されていることに気付く友人もいるでしょう。これは、操作中にフロントエンド ポートを調整するためですが、実際にはここで調整する必要はありません。ここでの転送には最終的に Nginx が使用されるためです。

ノード:ltsから

ラベル メンテナー="K8sCat <[email protected]>"

ワークディレクトリ /web

コピー 。 。

引数 REACT_APP_BASE_URL

npm config set registry https://registry.npm.taobao.org && \ を実行します。
npm インストール && \
npm 実行ビルド && \
npm インストール -g サーブ

エントリポイント [ "serve", "-s", "build" ]
コマンド [ "-l", "3214" ]

ゲートウェイ建設ゲートウェイ

Nginx の設定

ここでは、バックエンドとフロントエンドのアップストリームをそれぞれ設定し、転送のロケーションルールを設定します。
ここでいくつか言及すべき点を挙げます:

  • set_by_luaを介してコンテナの環境変数を取得し、最後に実行時に環境を設定することでこれらの環境変数を設定します。これはより柔軟です。
  • server_nameは$hostnameを使用し、コンテナのホスト名は実行時に設定する必要があります。
  • ssl_certificate と ssl_certificate_key は変数を使用して設定できません
  • WeChat for Enterpriseのゲートウェイ認証を実装するためにgateway.luaスクリプトをロードします。
上流ウェブ{
    サーバー ca-web:3214;
}

アップストリームAPI {
 サーバー ca-api:4182;
}

サーバー{
 set_by_lua $corp_id 'os.getenv("CORP_ID") を返します';
 set_by_lua $agent_id 'os.getenv("AGENT_ID") を返します';
 set_by_lua $secret 'os.getenv("SECRET") を返します';
 set_by_lua $callback_host 'os.getenv("CALLBACK_HOST") を返します';
 set_by_lua $callback_schema 'os.getenv("CALLBACK_SCHEMA") を返します';
 set_by_lua $callback_uri 'os.getenv("CALLBACK_URI") を返します';
 set_by_lua $logout_uri 'os.getenv("LOGOUT_URI") を返します';
 set_by_lua $token_expires 'os.getenv("TOKEN_EXPIRES") を返します';
 set_by_lua $use_secure_cookie 'os.getenv("USE_SECURE_COOKIE") を返します';

 443 ssl http2 をリッスンします。
 サーバー名 $ホスト名;
 リゾルバ 8.8.8.8;
 ssl_certificate /certs/cert.crt;
 ssl_certificate_key /certs/cert.key;
 ssl_session_cache 共有:SSL:1m;
 ssl_session_timeout 5分;
 ssl_プロトコル TLSv1 TLSv1.1 TLSv1.2;
 ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
 ssl_prefer_server_ciphers をオン;
 lua_ssl_verify_depth 2;
    lua_ssl_trusted_certificate /etc/pki/tls/certs/ca-bundle.crt;

 ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") の場合 {
  $year を $1 に設定します。
  $month $2 を設定します。
  $dayを$3に設定します。
 }
 access_log ログ/access_$year$month$day.log メイン;
 error_log ログ/error.log;

 access_by_lua_file "/usr/local/openresty/nginx/conf/gateway.lua";

 場所 ^~ /ゲートウェイ {
        ルートhtml;
        インデックス index.html index.htm;
    }

 場所 ^~ /api {
        proxy_pass http://api;
        プロキシ読み取りタイムアウト 3600;
        プロキシ_http_バージョン 1.1;
        proxy_set_header X_FORWARDED_PROTO https;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header ホスト $host;
        proxy_set_header 接続 "";
    }

 場所 ^~ / {
        proxy_pass http://web;
        プロキシ読み取りタイムアウト 3600;
        プロキシ_http_バージョン 1.1;
        proxy_set_header X_FORWARDED_PROTO https;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header ホスト $host;
        proxy_set_header 接続 "";
    }

 エラーページ 500 502 503 504 /50x.html;
 場所 = /50x.html {
  ルートhtml;
 }
}

サーバー{
 聞く 80;
 サーバー名 $ホスト名;

 位置 / {
  ^/(.*) https://$server_name/$1 リダイレクトを書き換えます。
 }
}

Dockerファイル

openresty/openresty:1.19.3.1-centos から

ラベル メンテナー="K8sCat <[email protected]>"

コピー gateway.conf /etc/nginx/conf.d/gateway.conf
コピー gateway.lua /usr/local/openresty/nginx/conf/gateway.lua
nginx.conf をコピー /usr/local/openresty/nginx/conf/nginx.conf

# lua-resty-http をインストールします
/usr/local/openresty/luajit/bin/luarocks install lua-resty-http を実行します。

LuaはエンタープライズWeChatに基づくゲートウェイ認証を実装

ここでの設定パラメータの一部は、Nginx によって設定された変数を取得することによって取得されます。

ローカル json = require("cjson")
ローカル http = require("resty.http")

ローカル URI = ngx.var.uri
ローカル uri_args = ngx.req.get_uri_args()
ローカルスキーム = ngx.var.scheme

ローカル corp_id = ngx.var.corp_id
ローカルエージェントID = ngx.var.agent_id
ローカルシークレット = ngx.var.secret
ローカル callback_scheme = ngx.var.callback_scheme または scheme
ローカル callback_host = ngx.var.callback_host
ローカル callback_uri = ngx.var.callback_uri
ローカル use_secure_cookie = ngx.var.use_secure_cookie == "true" または false
ローカル callback_url = callback_scheme .. "://" .. callback_host .. callback_uri
ローカルの redirect_url = callback_scheme .. "://" .. callback_host .. ngx.var.request_uri
ローカル logout_uri = ngx.var.logout_uri または "/logout"
ローカル token_expires = ngx.var.token_expires または "7200"
トークンの有効期限 = tonumber(トークンの有効期限)

ローカル関数 request_access_token(コード)
    ローカルリクエスト = http.new()
    リクエスト:set_timeout(7000)
    ローカル res、err = request:request_uri("https://qyapi.weixin.qq.com/cgi-bin/gettoken", {
        メソッド = "GET"、
        クエリ = {
            企業ID = 企業ID、
            corpsecret = 秘密、
        },
        ssl_verify = 真、
    })
    そうでなければ
        nil を返します (err または「アクセス トークン要求が失敗しました:」.. (err または「不明な理由」))
    終わり
    res.status ~= 200の場合
        nil を返します。「https://qyapi.weixin.qq.com/cgi-bin/gettoken から「.. res.status ..」を受け取りました:「.. res.body」
    終わり
    ローカルデータ = json.decode(res.body)
    data["errcode"] ~= 0の場合
        nil、データ["errmsg"]を返す
    それ以外
        データを返す["access_token"]
    終わり
終わり

ローカル関数 request_user(access_token, code)
    ローカルリクエスト = http.new()
    リクエスト:set_timeout(7000)
    ローカル res、err = request:request_uri("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo", {
        メソッド = "GET"、
        クエリ = {
            アクセストークン = アクセストークン、
            コード = コード、
        },
        ssl_verify = 真、
    })
    そうでなければ
        nil を返します。「プロファイル取得要求が失敗しました:」.. (エラーまたは「不明な理由」)
    終わり
    res.status ~= 200の場合
        nil を返します。「https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo から ".. res.status.." を受信しました」
    終わり
    ローカルユーザー情報 = json.decode(res.body)
    userinfo["errcode"] == 0の場合
        userinfo["UserId"]の場合
            res、err = リクエスト:request_uri("https://qyapi.weixin.qq.com/cgi-bin/user/get", {
                メソッド = "GET"、
                クエリ = {
                    アクセストークン = アクセストークン、
                    ユーザーID = userinfo["ユーザーID"],
                },
                ssl_verify = 真、
            })
            そうでなければ
                nil を返します。「ユーザー取得要求に失敗しました:」.. (エラーまたは「不明な理由」)
            終わり
            res.status ~= 200の場合
                nil を返します。「https://qyapi.weixin.qq.com/cgi-bin/user/get から ".. res.status.." を受信しました」
            終わり
            ローカルユーザー = json.decode(res.body)
            user["errcode"] == 0の場合
                ユーザーを返す
            それ以外
                nilを返す、ユーザー["errmsg"]
            終わり
        それ以外
            nil を返します。「UserId が存在しません」
        終わり
    それ以外
        nilを返す、userinfo["errmsg"]
    終わり
終わり

ローカル関数 is_authorized()
    ローカルヘッダー = ngx.req.get_headers()
    ローカルの有効期限 = tonumber(ngx.var.cookie_OauthExpires) または 0
    ローカル user_id = ngx.unescape_uri(ngx.var.cookie_OauthUserID または "")
    ローカル トークン = ngx.var.cookie_OauthAccessToken または ""
    有効期限が0でheaders["OauthExpires"]の場合
        有効期限 = tonumber(headers["OauthExpires"])
    終わり
    user_id:len() == 0 かつ headers["OauthUserID"] の場合
        user_id = ヘッダー["OauthUserID"]
    終わり
    token:len() == 0かつheaders["OauthAccessToken"]の場合
        トークン = ヘッダー["OauthAccessToken"]
    終わり
    ローカル expect_token = callback_host .. user_id .. 期限切れ
    トークン == expect_token かつ有効期限が切れている場合
        期限が ngx.time() より大きい場合
            真を返す
        それ以外
            偽を返す
        終わり
    それ以外
        偽を返す
    終わり
終わり

ローカル関数 redirect_to_auth()
    ngx.redirect("https://open.work.weixin.qq.com/wwopen/sso/qrConnect?" を返します。.. ngx.encode_args({
        appid = 企業ID、
        エージェントID = エージェントID、
        リダイレクトURI = コールバックURL、
        状態 = リダイレクトURL
    }))
終わり

ローカル関数 authorize()
    uri ~= callback_uri の場合
        リダイレクト_to_auth() を返す
    終わり
    ローカルコード = uri_args["code"]
    コードでなければ
        ngx.log(ngx.ERR, "https://open.work.weixin.qq.com/wwopen/sso/qrConnect からコードを受信できませんでした")
        ngx.exit(ngx.HTTP_FORBIDDEN) を返します
    終わり

    ローカル access_token、request_access_token_err = request_access_token(コード)
    access_tokenでない場合は
        ngx.log(ngx.ERR, "アクセス トークン要求中にエラーが発生しました: " .. request_access_token_err)
        ngx.exit(ngx.HTTP_FORBIDDEN) を返します
    終わり

    ローカルユーザー、request_user_err = request_user(access_token, code)
    ユーザーでない場合は
        ngx.log(ngx.ERR, "プロファイル要求中にエラーが発生しました: " .. request_user_err)
        ngx.exit(ngx.HTTP_FORBIDDEN) を返します
    終わり
    ngx.log(ngx.ERR, "ユーザーID: " .. user["userid"])

    ローカル有効期限 = ngx.time() + token_expires
    ローカル cookie_tail = "; version=1; path=/; Max-Age=" .. 期限切れ
    use_secure_cookieの場合
        cookie_tail = cookie_tail .. "; 安全"
    終わり

    ローカル user_id = user["userid"]
    ローカル user_token = callback_host .. user_id .. 有効期限

    ngx.header["Cookieの設定"] = {
        "OauthUserID=" .. ngx.escape_uri(user_id) .. cookie_tail,
        "OauthAccessToken=" .. ngx.escape_uri(user_token) .. cookie_tail,
        "OauthExpires=" .. 有効期限 .. cookie_tail,
    }
    ngx.redirect(uri_args["state"]) を返します。
終わり

ローカル関数 handle_logout()
    uri == logout_uriの場合
        ngx.header["Set-Cookie"] = "OauthAccessToken==削除済み; パス=/; 有効期限=木、1970 年 1 月 1 日 00:00:00 GMT"
        --return ngx.redirect("/")
    終わり
終わり

ハンドル_ログアウト()
if (not is_authorized()) then
    承認()
終わり

DockerCompose を使用したコンテナ オーケストレーション

ここで言及すべき点がいくつかあります:

  • フロントエンドを構築するときにバックエンドインターフェースアドレスを渡すようにフロントエンド引数を設定します。
  • ゲートウェイのホスト名を設定します。ゲートウェイ コンテナのホスト名を設定できます。
  • ゲートウェイ環境を設定すると、関連する構成を渡すことができます
  • 最終的なランタイムでは、ゲートウェイ層のみがポートを公開します。
バージョン: "3.8"

サービス:
  API:
    ビルド: ./api
    イメージ: ca-api:latest
    コンテナ名: ca-api

  ウェブ:
    建てる:
      コンテキスト: ./web
      引数:
        REACT_APP_BASE_URL: https://example.com/api
    画像: ca-web:最新
    コンテナ名: ca-web
    
  ゲートウェイ:
    ビルド: ./gateway
    画像: ca-gateway:最新
    ホスト名: example.com
    ボリューム:
      - ./gateway/certs/fullchain.pem:/certs/cert.crt
      - ./gateway/certs/privkey.pem:/certs/cert.key
    ポート:
      - 80:80
      -443:443
    環境:
      -法人ID=
      -エージェントID=
      -秘密=
      - CALLBACK_HOST=example.com
      - CALLBACK_SCHEMA=https
      - CALLBACK_URI=/ゲートウェイ/oauth_wechat
      -LOGOUT_URI=/ゲートウェイ/oauth_logout
      -トークン有効期限=7200
      - USE_SECURE_COOKIE=true
    コンテナ名: ca-gateway

オープンソースコード

GitHub https://github.com/k8scat/コンテナ化アプリ
Gitee https://gitee.com/k8scat/containerized-app

これで、Web アプリケーションの Docker+DockerCompose パッケージングに関するこの記事は終了です。Web アプリケーションの Docker+DockerCompose パッケージングに関する関連コンテンツの詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • docker-compose ネットワーク設定についての簡単な説明
  • Docker Compose ネットワーク設定の説明
  • docker-compose でデプロイしたときに MySQL にアクセスできなくなる問題の簡単な分析
  • docker-composeをインストールする最も簡単な方法2つ
  • Docker-composeのインストールと設定の詳細な手順
  • Docker Compose で利用可能な環境変数の詳細な説明
  • docker と docker-compose による eureka の高可用性の実現の詳細な説明

<<:  優れたWebフォームデザイン事例20選

>>:  ヘッダーのチェックボックスをテキスト実装コードに変更するための選択テーブルを持つ要素

推薦する

VMware15.5でcentos8.1をインストールし、物理メモリが不足する問題に対処する最も完全なチュートリアル

1. 仮想マシンの準備1. 新しい仮想マシンを作成する 2. 仮想マシンのカスタマイズを選択する 3...

MySQL でテーブルを作成するときの NULL と NOT NULL の使用方法の詳細な説明

MySQL の仕様によっては、テーブル作成仕様にすべてのフィールドが空であってはならないという要件を...

ReactにおけるRefの相互利用の詳細な説明

目次1. まずRefとは何かを説明しましょう2. フックでのrefの使用1. HTMLDomフックで...

Docker を使用して Redis マスター スレーブ レプリケーション クラスターを構築する

マスタースレーブレプリケーションモードのクラスターでは、通常、1 つのマスターノードと 2 つ以上の...

Centos7 でスーパーバイザ デーモンをインストールして設定する方法

初心者は自分で録音しましょう1. スーパーバイザーをインストールします。 Supervisor は ...

Win 8 以降での最新の MySQL バージョン 5.7.17 (64 ビット ZIP グリーン バージョン) のインストールと展開のチュートリアル

まず、ブロガーはコミュニティ バージョンをプレイしていますが、学習とテストにはこれで十分です。 Bl...

Vue3 のウォッチの使用方法とベストプラクティスガイド

目次序文🌟 1. APIの紹介2. 複数のデータソースの監視3. リスニングアレイ4. 監視対象5....

MySQLの明示的な型変換の簡単な分析

CAST関数前回の記事では、型変換を表示するために使用する CAST 関数について説明しました。暗黙...

RGBA の「a」は何を意味するのでしょうか? CSS RGBA カラー ガイド

RGBAは色の値と透明度を設定できるCSSカラーです以下は、rgba() を使用して白色を 50% ...

Vue3におけるキーの役割と動作原理についての簡単な説明

このキー属性の機能は何ですか?まずは公式の説明を見てみましょう。 kekey 属性は主に、新しいノー...

MySQL レプリケーション問題の 3 つのパラメータの分析

目次01 sql_slave_skip_counter パラメータ02 スレーブスキップエラーパラメ...

Pure CSS3はdivの出入りを順番に実現します

この記事は主に、純粋な CSS3 を使用して div が順番に出入りする効果を紹介します。一定の参考...

Windows に Docker と docker-compose スイートをインストールするための詳細なチュートリアル

目次導入ダウンロードしてインストールする設定docker-composeをインストールする導入Doc...

Tomcatでcatalina.batがUTF-8に設定されている場合、コンソールに文字化けした文字が表示されます

1. catalina.bat は UTF-8 に設定する必要があります。UTF-8 に設定しないと...

Linux のソフトリンクとハードリンクの詳細な説明

目次1. ファイルとディレクトリの基本的な保存2. Inコマンドの紹介(1)lnコマンドの基本情報を...