最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Docker+DockerCompose封裝web應(yīng)用的方法步驟

 更新時(shí)間:2021年08月25日 10:29:23   作者:K8sCat  
這篇文章會(huì)介紹如何將后端、前端和網(wǎng)關(guān)通通使用 Docker 容器進(jìn)行運(yùn)行,并最終使用 DockerCompose 進(jìn)行容器編排,感興趣的可以了解一下

這篇文章會(huì)介紹如何將后端、前端和網(wǎng)關(guān)通通使用 Docker 容器進(jìn)行運(yùn)行,并最終使用 DockerCompose 進(jìn)行容器編排。

技術(shù)棧

前端

  • React
  • Ant Design

后端

  • Go
  • Iris

網(wǎng)關(guān)

  • Nginx
  • OpenResty
  • Lua
  • 企業(yè)微信

后端構(gòu)建 api

這里雖然我們寫了 EXPOSE 4182,這個(gè)只用在測(cè)試的時(shí)候,生產(chǎn)環(huán)境實(shí)際上我們不會(huì)將后端接口端口進(jìn)行暴露,
而是通過容器間的網(wǎng)絡(luò)進(jìn)行互相訪問,以及最終會(huì)使用 Nginx 進(jìn)行轉(zhuǎn)發(fā)。

FROM golang:1.15.5

LABEL maintainer="K8sCat <k8scat@gmail.com>"

EXPOSE 4182

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

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

COPY . .

RUN go mod download && \
go build -o api main.go && \
chmod +x api

ENTRYPOINT [ "./api" ]

前端構(gòu)建 web

這里值得一提的是,因?yàn)榍岸丝隙〞?huì)去調(diào)用后端接口,而且這個(gè)接口地址是根據(jù)部署而改變,
所以這里我們使用了 ARG 指令進(jìn)行設(shè)置后端的接口地址,這樣我們只需要在構(gòu)建鏡像的時(shí)候傳入 --build-arg REACT_APP_BASE_URL=https://example.com/api 就可以調(diào)整后端接口地址了,而不是去改動(dòng)代碼。

還有一點(diǎn),有朋友肯定會(huì)發(fā)現(xiàn)這里同時(shí)使用到了 Entrypoint 和 CMD,這是為了可以在運(yùn)行的時(shí)候調(diào)整前端的端口,但實(shí)際上我們這里沒必要去調(diào)整,因?yàn)檫@里最終也是用 Nginx 進(jìn)行轉(zhuǎn)發(fā)。

FROM node:lts

LABEL maintainer="K8sCat <k8scat@gmail.com>"

WORKDIR /web

COPY . .

ARG REACT_APP_BASE_URL

RUN npm config set registry https://registry.npm.taobao.org && \
npm install && \
npm run build && \
npm install -g serve

ENTRYPOINT [ "serve", "-s", "build" ]
CMD [ "-l", "3214" ]

網(wǎng)關(guān)構(gòu)建 gateway

Nginx 配置

這里我們就分別設(shè)置了后端和前端的上游,然后設(shè)置 location 規(guī)則進(jìn)行轉(zhuǎn)發(fā)。
這里有幾個(gè)點(diǎn)可以說一下:

  • 通過 set_by_lua 獲取容器的環(huán)境變量,最終在運(yùn)行的時(shí)候通過設(shè)置 environment 設(shè)置這些環(huán)境變量,更加靈活
  • server_name 使用到了 $hostname,運(yùn)行時(shí)需要設(shè)置容器的 hostname
  • ssl_certificate 和 ssl_certificate_key 不能使用變量設(shè)置
  • 加載 gateway.lua 腳本實(shí)現(xiàn)企業(yè)微信的網(wǎng)關(guān)認(rèn)證
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("SECRET")';
 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 'return os.getenv("TOKEN_EXPIRES")';
 set_by_lua $use_secure_cookie 'return os.getenv("USE_SECURE_COOKIE")';

 listen 443 ssl http2;
 server_name $hostname;
 resolver 8.8.8.8;
 ssl_certificate /certs/cert.crt;
 ssl_certificate_key /certs/cert.key;
 ssl_session_cache shared:SSL:1m;
 ssl_session_timeout 5m;
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
 ssl_prefer_server_ciphers on;
 lua_ssl_verify_depth 2;
    lua_ssl_trusted_certificate /etc/pki/tls/certs/ca-bundle.crt;

 if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
  set $year $1;
  set $month $2;
  set $day $3;
 }
 access_log logs/access_$year$month$day.log main;
 error_log logs/error.log;

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

 location ^~ /gateway {
        root   html;
        index  index.html index.htm;
    }

 location ^~ /api {
        proxy_pass http://api;
        proxy_read_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-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header Connection "";
    }

 location ^~ / {
        proxy_pass http://web;
        proxy_read_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-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header Connection "";
    }

 error_page 500 502 503 504 /50x.html;
 location = /50x.html {
  root html;
 }
}

server {
 listen 80;
 server_name $hostname;

 location / {
  rewrite ^/(.*) https://$server_name/$1 redirect;
 }
}

Dockerfile

FROM openresty/openresty:1.19.3.1-centos

LABEL maintainer="K8sCat <k8scat@gmail.com>"

COPY gateway.conf /etc/nginx/conf.d/gateway.conf
COPY gateway.lua /usr/local/openresty/nginx/conf/gateway.lua
COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf

# Install lua-resty-http
RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-http

Lua 實(shí)現(xiàn)基于企業(yè)微信的網(wǎng)關(guān)認(rèn)證

這里面的一些配置參數(shù)都是通過獲取 Nginx 設(shè)置的變量。

local json = require("cjson")
local http = require("resty.http")

local uri = ngx.var.uri
local uri_args = ngx.req.get_uri_args()
local scheme = ngx.var.scheme

local corp_id = ngx.var.corp_id
local agent_id = ngx.var.agent_id
local secret = ngx.var.secret
local callback_scheme = ngx.var.callback_scheme or scheme
local callback_host = ngx.var.callback_host
local callback_uri = ngx.var.callback_uri
local use_secure_cookie = ngx.var.use_secure_cookie == "true" or false
local callback_url = callback_scheme .. "://" .. callback_host .. callback_uri
local redirect_url = callback_scheme .. "://" .. callback_host .. ngx.var.request_uri
local logout_uri = ngx.var.logout_uri or "/logout"
local token_expires = ngx.var.token_expires or "7200"
token_expires = tonumber(token_expires)

local function request_access_token(code)
    local request = http.new()
    request:set_timeout(7000)
    local res, err = request:request_uri("https://qyapi.weixin.qq.com/cgi-bin/gettoken", {
        method = "GET",
        query = {
            corpid = corp_id,
            corpsecret = secret,
        },
        ssl_verify = true,
    })
    if not res then
        return nil, (err or "access token request failed: " .. (err or "unknown reason"))
    end
    if res.status ~= 200 then
        return nil, "received " .. res.status .. " from https://qyapi.weixin.qq.com/cgi-bin/gettoken: " .. res.body
    end
    local data = json.decode(res.body)
    if data["errcode"] ~= 0 then
        return nil, data["errmsg"]
    else
        return data["access_token"]
    end
end

local function request_user(access_token, code)
    local request = http.new()
    request:set_timeout(7000)
    local res, err = request:request_uri("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo", {
        method = "GET",
        query = {
            access_token = access_token,
            code = code,
        },
        ssl_verify = true,
    })
    if not res then
        return nil, "get profile request failed: " .. (err or "unknown reason")
    end
    if res.status ~= 200 then
        return nil, "received " .. res.status .. " from https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"
    end
    local userinfo = json.decode(res.body)
    if userinfo["errcode"] == 0 then
        if userinfo["UserId"] then
            res, err = request:request_uri("https://qyapi.weixin.qq.com/cgi-bin/user/get", {
                method = "GET",
                query = {
                    access_token = access_token,
                    userid = userinfo["UserId"],
                },
                ssl_verify = true,
            })
            if not res then
                return nil, "get user request failed: " .. (err or "unknown reason")
            end
            if res.status ~= 200 then
                return nil, "received " .. res.status .. " from https://qyapi.weixin.qq.com/cgi-bin/user/get"
            end
            local user = json.decode(res.body)
            if user["errcode"] == 0 then
                return user
            else
                return nil, user["errmsg"]
            end
        else
            return nil, "UserId not exists"
        end
    else
        return nil, userinfo["errmsg"]
    end
end

local function is_authorized()
    local headers = ngx.req.get_headers()
    local expires = tonumber(ngx.var.cookie_OauthExpires) or 0
    local user_id = ngx.unescape_uri(ngx.var.cookie_OauthUserID or "")
    local token = ngx.var.cookie_OauthAccessToken or ""
    if expires == 0 and headers["OauthExpires"] then
        expires = tonumber(headers["OauthExpires"])
    end
    if user_id:len() == 0 and headers["OauthUserID"] then
        user_id = headers["OauthUserID"]
    end
    if token:len() == 0 and headers["OauthAccessToken"] then
        token = headers["OauthAccessToken"]
    end
    local expect_token = callback_host .. user_id .. expires
    if token == expect_token and expires then
        if expires > ngx.time() then
            return true
        else
            return false
        end
    else
        return false
    end
end

local function redirect_to_auth()
    return ngx.redirect("https://open.work.weixin.qq.com/wwopen/sso/qrConnect?" .. ngx.encode_args({
        appid = corp_id,
        agentid = agent_id,
        redirect_uri = callback_url,
        state = redirect_url
    }))
end

local function authorize()
    if uri ~= callback_uri then
        return redirect_to_auth()
    end
    local code = uri_args["code"]
    if not code then
        ngx.log(ngx.ERR, "not received code from https://open.work.weixin.qq.com/wwopen/sso/qrConnect")
        return ngx.exit(ngx.HTTP_FORBIDDEN)
    end

    local access_token, request_access_token_err = request_access_token(code)
    if not access_token then
        ngx.log(ngx.ERR, "got error during access token request: " .. request_access_token_err)
        return ngx.exit(ngx.HTTP_FORBIDDEN)
    end

    local user, request_user_err = request_user(access_token, code)
    if not user then
        ngx.log(ngx.ERR, "got error during profile request: " .. request_user_err)
        return ngx.exit(ngx.HTTP_FORBIDDEN)
    end
    ngx.log(ngx.ERR, "user id: " .. user["userid"])

    local expires = ngx.time() + token_expires
    local cookie_tail = "; version=1; path=/; Max-Age=" .. expires
    if use_secure_cookie then
        cookie_tail = cookie_tail .. "; secure"
    end

    local user_id = user["userid"]
    local user_token = callback_host .. user_id .. expires

    ngx.header["Set-Cookie"] = {
        "OauthUserID=" .. ngx.escape_uri(user_id) .. cookie_tail,
        "OauthAccessToken=" .. ngx.escape_uri(user_token) .. cookie_tail,
        "OauthExpires=" .. expires .. cookie_tail,
    }
    return ngx.redirect(uri_args["state"])
end

local function handle_logout()
    if uri == logout_uri then
        ngx.header["Set-Cookie"] = "OauthAccessToken==deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"
        --return ngx.redirect("/")
    end
end

handle_logout()
if (not is_authorized()) then
    authorize()
end

使用 DockerCompose 進(jìn)行容器編排

這里需要講幾個(gè)點(diǎn):

  • 設(shè)置前端的 args 可以在前端構(gòu)建時(shí)傳入后端接口地址
  • 設(shè)置網(wǎng)關(guān)的 hostname 可以設(shè)置網(wǎng)關(guān)容器的 hostname
  • 設(shè)置網(wǎng)關(guān)的 environment 可以傳入相關(guān)配置
  • 最終運(yùn)行時(shí)只有網(wǎng)關(guān)層進(jìn)行暴露端口
version: "3.8"

services:
  api:
    build: ./api
    image: ca-api:latest
    container_name: ca-api

  web:
    build:
      context: ./web
      args:
        REACT_APP_BASE_URL: https://example.com/api
    image: ca-web:latest
    container_name: ca-web
    
  gateway:
    build: ./gateway
    image: ca-gateway:latest
    hostname: example.com
    volumes:
      - ./gateway/certs/fullchain.pem:/certs/cert.crt
      - ./gateway/certs/privkey.pem:/certs/cert.key
    ports:
      - 80:80
      - 443:443
    environment:
      - CORP_ID=
      - AGENT_ID=
      - SECRET=
      - CALLBACK_HOST=example.com
      - CALLBACK_SCHEMA=https
      - CALLBACK_URI=/gateway/oauth_wechat
      - LOGOUT_URI=/gateway/oauth_logout
      - TOKEN_EXPIRES=7200
      - USE_SECURE_COOKIE=true
    container_name: ca-gateway

開源代碼

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

到此這篇關(guān)于Docker+DockerCompose封裝web應(yīng)用的文章就介紹到這了,更多相關(guān)Docker+DockerCompose封裝web應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Docker 7 docker在阿里云的使用詳解

    Docker 7 docker在阿里云的使用詳解

    這篇文章主要介紹了Docker 7 docker在阿里云的使用詳解的相關(guān)資料,需要的朋友可以參考下
    2016-11-11
  • 一文詳解如何更改Docker鏡像存儲(chǔ)路徑

    一文詳解如何更改Docker鏡像存儲(chǔ)路徑

    在Docker中,默認(rèn)情況下鏡像的存儲(chǔ)路徑是在C盤,然而,隨著時(shí)間的推移,您可能會(huì)發(fā)現(xiàn)這個(gè)默認(rèn)路徑占用了大量的磁盤空間,這篇文章主要介紹了如何更改Docker鏡像存儲(chǔ)路徑的相關(guān)資料,需要的朋友可以參考下
    2025-08-08
  • Docker中如何通過docker-compose部署ELK

    Docker中如何通過docker-compose部署ELK

    Docker?Compose適用于不同的操作系統(tǒng)和云平臺(tái),這篇文章主要介紹了Docker中如何通過docker-compose部署ELK,需要的朋友可以參考下
    2024-05-05
  • docker利用單個(gè)鏡像映射到多個(gè)端口操作

    docker利用單個(gè)鏡像映射到多個(gè)端口操作

    這篇文章主要介紹了docker利用單個(gè)鏡像映射到多個(gè)端口操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2020-11-11
  • Docker如何訪問到宿主機(jī)MYSQL的實(shí)現(xiàn)方法

    Docker如何訪問到宿主機(jī)MYSQL的實(shí)現(xiàn)方法

    使用?Docker?能實(shí)現(xiàn)服務(wù)的容器化,并使用容器間網(wǎng)絡(luò)在它們之間進(jìn)行通信,本文主要介紹了Docker如何訪問到宿主機(jī)MYSQL的實(shí)現(xiàn)方法,感興趣的可以了解一下,感興趣的可以了解一下
    2023-09-09
  • docker多個(gè)容器的相互通信實(shí)現(xiàn)步驟

    docker多個(gè)容器的相互通信實(shí)現(xiàn)步驟

    本文介紹了在宿主機(jī)上運(yùn)行多個(gè)Docker容器時(shí)的幾種通信方式,包括默認(rèn)的橋接網(wǎng)絡(luò)、自定義網(wǎng)絡(luò)、--link參數(shù)、Host網(wǎng)絡(luò)、Docker-compose和共享數(shù)據(jù)卷等,每種方式都有其特點(diǎn)和適用場(chǎng)景,感興趣的朋友跟隨小編一起看看吧
    2025-02-02
  • Docker 解決容器時(shí)間與主機(jī)時(shí)間不一致的問題三種解決方案

    Docker 解決容器時(shí)間與主機(jī)時(shí)間不一致的問題三種解決方案

    這篇文章主要介紹了Docker 解決容器時(shí)間與主機(jī)時(shí)間不一致的問題的相關(guān)資料,這里提供了三種方法,供大家參考,需要的朋友可以參考下
    2016-12-12
  • docker-compose安裝mongoDB全過程

    docker-compose安裝mongoDB全過程

    這篇文章主要介紹了docker-compose安裝mongoDB全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 基于Docker搭建Redis一主兩從三哨兵的實(shí)現(xiàn)

    基于Docker搭建Redis一主兩從三哨兵的實(shí)現(xiàn)

    這篇文章主要介紹了基于Docker搭建Redis一主兩從三哨兵的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 安裝Docker Desktop報(bào)錯(cuò)WSL 2 installation is incomplete的問題(解決報(bào)錯(cuò))

    安裝Docker Desktop報(bào)錯(cuò)WSL 2 installation is incomplete的問題(解決報(bào)錯(cuò))

    這篇文章主要介紹了安裝Docker Desktop報(bào)錯(cuò)WSL 2 installation is incomplete的問題,解決方法很簡(jiǎn)單只需我們自己手動(dòng)更新一下,我們根據(jù)提示去微軟官網(wǎng)下載最新版的wsl2安裝后即可正常打開,需要的朋友可以參考下
    2021-06-06

最新評(píng)論

宜兰市| 清流县| 马公市| 黔西县| 静乐县| 武夷山市| 西盟| 昌图县| 保靖县| 汉川市| 筠连县| 西藏| 江孜县| 客服| 宜兰县| 阿拉善盟| 腾冲县| 旅游| 津市市| 苏尼特左旗| 阳西县| 高州市| 武义县| 德昌县| 宜丰县| 辽宁省| 双牌县| 祁连县| 德州市| 定西市| 黄山市| 勐海县| 格尔木市| 霞浦县| 鹤山市| 武乡县| 泽普县| 金湖县| 德阳市| 阿克苏市| 舞钢市|