So verwenden Sie Docker+DockerCompose zum Kapseln von Webanwendungen

So verwenden Sie Docker+DockerCompose zum Kapseln von Webanwendungen

In diesem Artikel wird erläutert, wie Sie Backend, Frontend und Gateway mithilfe von Docker-Containern ausführen und schließlich DockerCompose zur Container-Orchestrierung verwenden.

Technologie-Stack

Frontend

  • Reagieren
  • Ameisen-Design

hinteres Ende

  • Gehen
  • Iris

Tor

  • Nginx
  • OpenResty
  • Lua
  • WeChat-Geschäft

Backend-Build-API

Obwohl wir hier EXPOSE 4182 geschrieben haben, wird dies nur während des Tests verwendet. Tatsächlich werden wir den Backend-Schnittstellenport in der Produktionsumgebung nicht verfügbar machen.
Stattdessen greifen Container über das Netzwerk zwischen ihnen aufeinander zu und leiten die Daten schließlich mithilfe von Nginx weiter.

VON golang:1.15.5

LABEL-Betreuer="K8sCat <[email protected]>"

EXPOSE 4182

ENV GOPROXY=https://goproxy.cn,direkt \
    GO111MODULE=ein

ARBEITSVERZEICHNIS /go/src/github.com/k8scat/containerized-app/api

KOPIEREN . .

RUN go mod herunterladen && \
gehe bauen -o api main.go && \
chmod +x api

EINSTIEGSPUNKT [ "./api" ]

Frontend-Web-Erstellung

Erwähnenswert ist hier, dass das Front-End definitiv die Back-End-Schnittstelle aufruft und sich die Adresse dieser Schnittstelle je nach Bereitstellung ändert.
Daher verwenden wir hier die ARG-Anweisung, um die Back-End-Schnittstellenadresse festzulegen, sodass wir beim Erstellen des Images nur --build-arg REACT_APP_BASE_URL=https://example.com/api übergeben müssen, um die Back-End-Schnittstellenadresse anzupassen, anstatt den Code zu ändern.

Ein weiterer Punkt ist, dass einige Freunde definitiv feststellen werden, dass Entrypoint und CMD hier gleichzeitig verwendet werden. Dies dient dazu, den Front-End-Port während des Betriebs anzupassen, aber tatsächlich müssen wir ihn hier nicht anpassen, da Nginx hier letztendlich zum Weiterleiten verwendet wird.

VON Knoten:lts

LABEL-Betreuer="K8sCat <[email protected]>"

ARBEITSVERZEICHNIS /web

KOPIEREN . .

ARG REACT_APP_BASE_URL

Führen Sie npm config set registry https://registry.npm.taobao.org aus und führen Sie Folgendes aus:
npm installieren && \
npm führt Build aus && \
npm install -g serve

EINSTIEGSPUNKT [ "dienen", "-s", "bauen" ]
CMD [ "-l", "3214" ]

Torbau Torbau

Nginx-Konfiguration

Hier legen wir jeweils den Upstream des Backends und des Frontends fest und legen dann die Standortregeln für die Weiterleitung fest.
Hier sind einige Punkte, die erwähnt werden sollten:

  • Holen Sie sich die Umgebungsvariablen des Containers über set_by_lua und legen Sie diese Umgebungsvariablen schließlich fest, indem Sie die Umgebung zur Laufzeit festlegen, was flexibler ist
  • server_name verwendet $hostname, und der Hostname des Containers muss zur Laufzeit festgelegt werden
  • ssl_certificate und ssl_certificate_key können nicht mit Variablen festgelegt werden
  • Laden Sie das Skript gateway.lua, um die Gateway-Authentifizierung von WeChat for Enterprise zu implementieren
Upstream-Web {
    Server ca-web:3214;
}

Upstream-API {
 Server ca-API:4182;
}

Server {
 set_by_lua $corp_id 'return os.getenv("CORP_ID")';
 set_by_lua $agent_id 'return os.getenv("AGENT_ID")';
 set_by_lua $secret 'return os.getenv("GEHEIM")';
 set_by_lua $callback_host 'return os.getenv("CALLBACK_HOST")';
 set_by_lua $callback_schema 'return os.getenv("CALLBACK_SCHEMA")';
 set_by_lua $callback_uri 'return os.getenv("CALLBACK_URI")';
 set_by_lua $logout_uri 'return os.getenv("LOGOUT_URI")';
 set_by_lua $token_expires 'Rückgabe os.getenv("TOKEN_EXPIRES")';
 set_by_lua $use_secure_cookie 'return os.getenv("USE_SECURE_COOKIE")';

 hören Sie 443 SSL http2;
 Servername $Hostname;
 Resolver 8.8.8.8;
 SSL-Zertifikat /certs/cert.crt;
 SSL-Zertifikatschlüssel /certs/cert.key;
 ssl_session_cache geteilt:SSL:1m;
 SSL-Sitzungszeitüberschreitung 5 Min.
 SSL-Protokolle TLSv1 TLSv1.1 TLSv1.2;
 ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
 ssl_prefer_server_ciphers ein;
 : lua_ssl_verify_depth 2;
    lua_ssl_trusted_certificate /etc/pki/tls/certs/ca-bundle.crt;

 wenn ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
  Setze $year auf $1;
  Setze $Monat $2;
  Setze $Tag $3;
 }
 access_log Protokolle/access_$Jahr$Monat$Tag.log Haupt;
 Fehlerprotokollprotokolle/Fehler.log;

 Zugriff über Lua-Datei "/usr/local/openresty/nginx/conf/gateway.lua";

 Standort ^~ /gateway {
        Stamm-HTML;
        Index Index.html Index.htm;
    }

 Standort ^~ /api {
        Proxy-Passwort http://API;
        Proxy_Lese_Timeout 3600;
        Proxy_http_Version 1.1;
        Proxy_Set_Header X_FORWARDED_PROTO https;
        Proxy_Set_Header X-Real-IP $Remote_Addr;
        proxy_set_header X-Weitergeleitet-Für $remote_addr;
        proxy_set_header X-Weitergeleitet-Für $proxy_add_x_forwarded_for;
        Proxy_Set_Header Host $host;
        proxy_set_header Verbindung "";
    }

 Standort ^~ / {
        Proxy-Passwort http://Web;
        Proxy_Lese_Timeout 3600;
        Proxy_http_Version 1.1;
        Proxy_Set_Header X_FORWARDED_PROTO https;
        Proxy_Set_Header X-Real-IP $Remote_Addr;
        proxy_set_header X-Weitergeleitet-Für $remote_addr;
        proxy_set_header X-Weitergeleitet-Für $proxy_add_x_forwarded_for;
        Proxy_Set_Header Host $host;
        proxy_set_header Verbindung "";
    }

 Fehlerseite 500 502 503 504 /50x.html;
 Standort = /50x.html {
  Stamm-HTML;
 }
}

Server {
 hören Sie 80;
 Servername $Hostname;

 Standort / {
  Umleitung von ^/(.*) https://$server_name/$1 neu schreiben;
 }
}

Docker-Datei

VON openresty/openresty:1.19.3.1-centos

LABEL-Betreuer="K8sCat <[email protected]>"

KOPIEREN gateway.conf /etc/nginx/conf.d/gateway.conf
KOPIEREN Sie gateway.lua /usr/local/openresty/nginx/conf/gateway.lua
KOPIEREN Sie nginx.conf /usr/local/openresty/nginx/conf/nginx.conf

# Installieren Sie lua-resty-http
RUN /usr/local/openresty/luajit/bin/luarocks installiere lua-resty-http

Lua implementiert Gateway-Authentifizierung basierend auf Enterprise WeChat

Einige der hier aufgeführten Konfigurationsparameter werden durch den Abruf von von Nginx festgelegten Variablen ermittelt.

lokales JSON = erforderlich("cjson")
lokales http = erforderlich("resty.http")

lokale URI = ngx.var.uri
lokale uri_args = ngx.req.get_uri_args()
lokales Schema = ngx.var.scheme

lokale Firmen-ID = ngx.var.corp_id
lokale Agenten-ID = ngx.var.agent_id
lokales Geheimnis = ngx.var.secret
lokales Callback-Schema = ngx.var.callback_scheme oder Schema
lokaler Rückrufhost = ngx.var.callback_host
lokale Rückruf-URI = ngx.var.callback_uri
lokales use_secure_cookie = ngx.var.use_secure_cookie == "true" oder false
lokale Rückruf-URL = Rückrufschema .. "://" .. Rückrufhost .. Rückruf-URI
lokale Umleitungs-URL = Rückrufschema .. "://" .. Rückrufhost .. ngx.var.request_uri
lokale Logout-URI = ngx.var.logout_uri oder „/logout“
lokales token_expires = ngx.var.token_expires oder „7200“
token_expires = bisNummer(token_expires)

lokale Funktion request_access_token(code)
    lokale Anfrage = http.new()
    Anfrage:set_timeout(7000)
    lokale Auflösung, Fehler = Anfrage: Anfrage-URI("https://qyapi.weixin.qq.com/cgi-bin/gettoken", {
        Methode = "GET",
        Abfrage = {
            corpid = Firmen-ID,
            corpsecret = geheim,
        },
        ssl_verify = wahr,
    })
    wenn nicht res dann
        returniere nil, (Fehler oder „Anforderung des Zugriffstokens fehlgeschlagen: “ .. (Fehler oder „unbekannter Grund“))
    Ende
    wenn res.status ~= 200 dann
        return nil, „erhalten „ .. res.status .. “ von https://qyapi.weixin.qq.com/cgi-bin/gettoken: „ .. res.body
    Ende
    lokale Daten = json.decode(res.body)
    wenn data["errcode"] ~= 0 dann
        returniere nil, data["errmsg"]
    anders
        Daten zurückgeben["Zugriffstoken"]
    Ende
Ende

lokale Funktion request_user(access_token, code)
    lokale Anfrage = http.new()
    Anfrage:set_timeout(7000)
    lokale Auflösung, Fehler = Anfrage: Anfrage-URI("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo", {
        Methode = "GET",
        Abfrage = {
            Zugriffstoken = Zugriffstoken,
            Code = Code,
        },
        ssl_verify = wahr,
    })
    wenn nicht res dann
        return nil, „Anforderung zum Abrufen des Profils fehlgeschlagen:“ .. (Fehler oder „unbekannter Grund“)
    Ende
    wenn res.status ~= 200 dann
        return nil, „erhalten „ .. res.status .. „ von https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo“
    Ende
    lokale Benutzerinformationen = json.decode(res.body)
    wenn userinfo["errcode"] == 0 dann
        wenn userinfo["Benutzer-ID"] dann
            res, err = Anfrage: Anfrage-URI("https://qyapi.weixin.qq.com/cgi-bin/user/get", {
                Methode = "GET",
                Abfrage = {
                    Zugriffstoken = Zugriffstoken,
                    Benutzer-ID = Benutzerinfo["Benutzer-ID"],
                },
                ssl_verify = wahr,
            })
            wenn nicht res dann
                return nil, „Abrufen der Benutzeranforderung fehlgeschlagen:“ .. (Fehler oder „unbekannter Grund“)
            Ende
            wenn res.status ~= 200 dann
                return nil, „erhalten „ .. res.status .. „ von https://qyapi.weixin.qq.com/cgi-bin/user/get“
            Ende
            lokaler Benutzer = json.decode(res.body)
            wenn Benutzer["Fehlercode"] == 0 dann
                Benutzer zurückgeben
            anders
                returniere nil, Benutzer["errmsg"]
            Ende
        anders
            return nil, „Benutzer-ID existiert nicht“
        Ende
    anders
        returniere nil, Benutzerinfo["errmsg"]
    Ende
Ende

lokale Funktion is_authorized()
    lokale Header = ngx.req.get_headers()
    lokales Ablaufdatum = tonumber(ngx.var.cookie_OauthExpires) oder 0
    lokale Benutzer-ID = ngx.unescape_uri(ngx.var.cookie_OauthUserID oder "")
    lokales Token = ngx.var.cookie_OauthAccessToken oder ""
    wenn expires == 0 und headers["OauthExpires"] dann
        läuft ab = tonumber(headers["OauthExpires"])
    Ende
    wenn user_id:len() == 0 und headers["OauthUserID"] dann
        Benutzer-ID = Header["OauthUserID"]
    Ende
    wenn token:len() == 0 und headers["OauthAccessToken"] dann
        Token = Header["OauthAccessToken"]
    Ende
    lokales expect_token = callback_host .. user_id .. läuft ab
    wenn token == expect_token und abläuft dann
        wenn abläuft > ngx.time() dann
            returniere wahr
        anders
            return false
        Ende
    anders
        return false
    Ende
Ende

lokale Funktion redirect_to_auth()
    return ngx.redirect("https://open.work.weixin.qq.com/wwopen/sso/qrConnect?" .. ngx.encode_args({
        appid = Firmen-ID,
        agentid = agenten-id,
        Umleitungs-URI = Rückruf-URL,
        Status = Umleitungs-URL
    }))
Ende

lokale Funktion autorisieren()
    wenn uri ~= callback_uri dann
        returniere redirect_to_auth()
    Ende
    lokaler Code = uri_args["Code"]
    wenn nicht Code, dann
        ngx.log(ngx.ERR, „Code von https://open.work.weixin.qq.com/wwopen/sso/qrConnect nicht erhalten“)
        gibt ngx.exit(ngx.HTTP_FORBIDDEN) zurück.
    Ende

    lokales Zugriffstoken, Anforderungszugriffstoken_Fehler = Anforderungszugriffstoken(Code)
    wenn nicht access_token dann
        ngx.log(ngx.ERR, „Fehler bei der Anforderung des Zugriffstokens: " .. request_access_token_err)
        gibt ngx.exit(ngx.HTTP_FORBIDDEN) zurück.
    Ende

    lokaler Benutzer, request_user_err = request_user(Zugriffstoken, Code)
    wenn nicht Benutzer, dann
        ngx.log(ngx.ERR, "Fehler bei der Profilanforderung: " .. request_user_err)
        gibt ngx.exit(ngx.HTTP_FORBIDDEN) zurück.
    Ende
    ngx.log(ngx.ERR, "Benutzer-ID: " .. Benutzer["Benutzer-ID"])

    lokal läuft ab = ngx.time() + token_expires
    local cookie_tail = "; version=1; path=/; Max-Age=" .. läuft ab
    wenn use_secure_cookie dann
        cookie_tail = cookie_tail .. "; sicher"
    Ende

    lokale Benutzer-ID = Benutzer["Benutzer-ID"]
    lokales Benutzertoken = Rückrufhost .. Benutzer-ID .. läuft ab

    ngx.header["Cookie setzen"] = {
        "OauthUserID=" .. ngx.escape_uri(Benutzer-ID) .. cookie_tail,
        "OauthAccessToken=" .. ngx.escape_uri(Benutzertoken) .. cookie_tail,
        "OauthExpires=" .. läuft ab .. cookie_tail,
    }
    returniere ngx.redirect(uri_args["Status"])
Ende

lokale Funktion handle_logout()
    wenn uri == logout_uri dann
        ngx.header["Set-Cookie"] = "OauthAccessToken==gelöscht; Pfad=/; läuft ab=Do, 01. Jan. 1970 00:00:00 GMT"
        --return ngx.redirect("/")
    Ende
Ende

handle_logout()
wenn (nicht is_authorized()), dann
    autorisieren()
Ende

Container-Orchestrierung mit DockerCompose

Hier sind einige Punkte zu erwähnen:

  • Legen Sie die Front-End-Argumente fest, um die Back-End-Schnittstellenadresse beim Erstellen des Front-Ends zu übergeben.
  • Legen Sie den Hostnamen des Gateways fest. Sie können den Hostnamen des Gateway-Containers festlegen.
  • Das Einstellen der Gateway-Umgebung kann relevante Konfigurationen beinhalten
  • In der endgültigen Laufzeit stellt nur die Gateway-Schicht den Port zur Verfügung.
Version: "3.8"

Leistungen:
  API:
    Erstellen: ./api
    Bild: ca-api:latest
    Containername: ca-api

  Webseite:
    bauen:
      Kontext: ./Web
      Argumente:
        REACT_APP_BASE_URL: https://example.com/api
    Bild: ca-web:latest
    Containername: ca-web
    
  Tor:
    Erstellen: ./gateway
    Bild: ca-gateway:latest
    Hostname: example.com
    Bände:
      - ./gateway/certs/fullchain.pem:/certs/cert.crt
      - ./gateway/certs/privkey.pem:/certs/cert.key
    Häfen:
      - 80:80
      -443:443
    Umfeld:
      -CORP_ID=
      -AGENT_ID=
      -GEHEIM=
      - CALLBACK_HOST=example.com
      – CALLBACK_SCHEMA=https
      – CALLBACK_URI=/gateway/oauth_wechat
      -LOGOUT_URI=/gateway/oauth_logout
      -TOKEN_EXPIRES=7200
      - USE_SECURE_COOKIE=true
    Containername: ca-gateway

Offener Quellcode

GitHub https://github.com/k8scat/containerized-app
Gitee https://gitee.com/k8scat/containerized-app

Dies ist das Ende dieses Artikels über die Docker+DockerCompose-Verpackung von Webanwendungen. Weitere relevante Inhalte zur Docker+DockerCompose-Verpackung von Webanwendungen finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Eine kurze Diskussion über die Docker-Compose-Netzwerkeinstellungen
  • Erläuterung der Docker Compose-Netzwerkeinstellungen
  • Eine kurze Analyse des Problems, dass MySQL bei der Bereitstellung mit Docker-Compose nicht zugänglich ist
  • Zwei einfachste Möglichkeiten zur Installation von Docker-Compose
  • Detaillierte Schritte zur Installation und Einrichtung von Docker-compose
  • Detaillierte Erklärung der verfügbaren Umgebungsvariablen in Docker Compose
  • Detaillierte Erläuterung zum Erreichen einer hohen Verfügbarkeit von Eureka durch Docker und Docker-Compose

<<:  Teilen Sie 20 hervorragende Beispiele für Webformular-Design

>>:  Element mit Auswahltabelle zum Ändern des Kontrollkästchens in der Kopfzeile in Textimplementierungscode

Artikel empfehlen

So erhöhen Sie die Ladegeschwindigkeit von HTML-Seiten

(1) Reduzieren Sie HTTP-Anfragen. (Ressourcendate...

Nginx-Anti-Crawler-Strategie, um UA am Crawlen von Websites zu hindern

Anti-Crawler-Richtliniendatei hinzugefügt: vim /u...

CentOS7-Installations-Tutorial für Zabbix 4.0 (Abbildung und Text)

Deaktivieren Sie SeLinux setenforce 0 Dauerhaft g...

Ein- und Ausblenden von HTML-Elementen durch Anzeige oder Sichtbarkeit

Manchmal müssen wir steuern, ob HTML-Elemente auf ...

Vue implementiert scrollbaren Popup-Fenstereffekt

In diesem Artikel wird der spezifische Code von V...

Grundlegende Schritte zur Sicherheitseinstellung für den CentOS7-Server

Schalten Sie den Ping-Scan aus, obwohl dies nicht...

Gutes Website-Copywriting und gute Benutzererfahrung

Das Betrachten einer Website ist eigentlich wie di...

Das Docker-Maven-Plugin-Plugin kann das entsprechende JAR-Paket nicht abrufen

Bei Verwendung des Plug-Ins „Docker-Maven-Plugin“...

Detaillierte Erklärung zur Verwendung von Titel-Tags und Absatz-Tags in XHTML

XHTML-Überschriftenübersicht Wenn wir Word-Dokume...

Was sind HTML-Inline-Elemente und Block-Level-Elemente und ihre Unterschiede

Ich erinnere mich an eine Frage, die der Intervie...

So halten Sie eine lange Verbindung aufrecht, wenn Sie den Nginx-Reverse-Proxy verwenden

· 【Szenenbeschreibung】 Nach HTTP1.1 unterstützt d...