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 legen Sie eine Verzeichnis-Whitelist und eine IP-Whitelist in Nginx fest

1. Legen Sie eine Verzeichnis-Whitelist fest: Leg...

Eine detaillierte Einführung in die Linux-Verzeichnisstruktur

Wenn Sie mit dem Erlernen von Linux beginnen, müs...

Data URI und MHTML Komplettlösung für alle Browser

Daten-URI Data URI ist ein durch RFC 2397 definie...

So richten Sie den Ziellink eines Tags auf ein Iframe

Code kopieren Der Code lautet wie folgt: <ifra...

Vue-Ereignisparameter $event = Ereigniswertfall

Vorlage <el-table :data="Datenliste"...

Eine "klassische" Falle der MySQL UPDATE-Anweisung

Inhaltsverzeichnis 1. Problematische SQL-Anweisun...

Implementierung des WeChat-Applet-Nachrichten-Pushs in Nodejs

Auswählen oder Erstellen einer Abonnementnachrich...

Detaillierte Erläuterung des Zeitdarstellungsbeispiels des Linux-Zeitsubsystems

Vorwort Um zum Originalcode kompatibel zu sein, b...

Analysieren Sie das Arbeitsprinzip von Tomcat

SpringBoot ist wie eine riesige Python, die sich ...

So verwenden Sie Docker zum Bereitstellen von Front-End-Anwendungen

Docker erfreut sich immer größerer Beliebtheit. E...

Auszeichnungssprache - Stylesheets drucken

Klicken Sie hier, um zum Abschnitt „HTML-Tutorial“...

Detaillierte Erklärung der Methoden des fs-Moduls und des Path-Moduls in Node.js

Überblick: Das Dateisystemmodul ist ein einfacher...

Tutorial zur Installation von jdk1.8 auf Ubuntu14.04

1. Laden Sie die JDK-Download-Adresse herunter我下載...