婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > Docker+DockerCompose封裝web應用的方法步驟

Docker+DockerCompose封裝web應用的方法步驟

熱門標簽:威海人工外呼系統供應商 寧夏房產智能外呼系統要多少錢 烏海智能電話機器人 藍點外呼系統 撫順移動400電話申請 做外呼系統的公司違法嗎 400電話申請方案 在百度地圖標注車輛 貴陽教育行業電話外呼系統

這篇文章會介紹如何將后端、前端和網關通通使用 Docker 容器進行運行,并最終使用 DockerCompose 進行容器編排。

技術棧

前端

  • React
  • Ant Design

后端

  • Go
  • Iris

網關

  • Nginx
  • OpenResty
  • Lua
  • 企業微信

后端構建 api

這里雖然我們寫了 EXPOSE 4182,這個只用在測試的時候,生產環境實際上我們不會將后端接口端口進行暴露,
而是通過容器間的網絡進行互相訪問,以及最終會使用 Nginx 進行轉發。

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" ]

前端構建 web

這里值得一提的是,因為前端肯定會去調用后端接口,而且這個接口地址是根據部署而改變,
所以這里我們使用了 ARG 指令進行設置后端的接口地址,這樣我們只需要在構建鏡像的時候傳入 --build-arg REACT_APP_BASE_URL=https://example.com/api 就可以調整后端接口地址了,而不是去改動代碼。

還有一點,有朋友肯定會發現這里同時使用到了 Entrypoint 和 CMD,這是為了可以在運行的時候調整前端的端口,但實際上我們這里沒必要去調整,因為這里最終也是用 Nginx 進行轉發。

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" ]

網關構建 gateway

Nginx 配置

這里我們就分別設置了后端和前端的上游,然后設置 location 規則進行轉發。
這里有幾個點可以說一下:

  • 通過 set_by_lua 獲取容器的環境變量,最終在運行的時候通過設置 environment 設置這些環境變量,更加靈活
  • server_name 使用到了 $hostname,運行時需要設置容器的 hostname
  • ssl_certificate 和 ssl_certificate_key 不能使用變量設置
  • 加載 gateway.lua 腳本實現企業微信的網關認證
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 實現基于企業微信的網關認證

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

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 進行容器編排

這里需要講幾個點:

  • 設置前端的 args 可以在前端構建時傳入后端接口地址
  • 設置網關的 hostname 可以設置網關容器的 hostname
  • 設置網關的 environment 可以傳入相關配置
  • 最終運行時只有網關層進行暴露端口
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

到此這篇關于Docker+DockerCompose封裝web應用的文章就介紹到這了,更多相關Docker+DockerCompose封裝web應用內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

標簽:慶陽 周口 銅川 泰州 松原 蕪湖 那曲 朝陽

巨人網絡通訊聲明:本文標題《Docker+DockerCompose封裝web應用的方法步驟》,本文關鍵詞  Docker+DockerCompose,封裝,web,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《Docker+DockerCompose封裝web應用的方法步驟》相關的同類信息!
  • 本頁收集關于Docker+DockerCompose封裝web應用的方法步驟的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产精品网站在线观看| 91九色最新地址| 日本少妇一区二区| 久久精品一区蜜桃臀影院| 不卡免费追剧大全电视剧网站| 国产精品久久久久一区| 欧美日韩在线一区二区| 不卡在线观看av| 色丁香久综合在线久综合在线观看| 制服丝袜亚洲精品中文字幕| 久久亚洲精华国产精华液| 国产欧美一区二区三区在线老狼 | 日本一区二区三区四区| 久久久五月婷婷| 中文字幕不卡的av| 亚洲va天堂va国产va久| 久久国产乱子精品免费女| 成人免费视频一区二区| 91蜜桃网址入口| 久久久精品蜜桃| 在线观看亚洲成人| 亚洲成av人片一区二区三区| 欧美色综合网站| 亚洲人xxxx| 国产精品白丝jk白祙喷水网站| 99精品久久只有精品| 99riav久久精品riav| 欧美岛国在线观看| 成人高清伦理免费影院在线观看| 国产69精品久久99不卡| 蜜桃视频一区二区三区| 亚洲电影视频在线| 一本大道久久a久久综合婷婷 | 亚洲成av人片一区二区三区| 五月天激情综合网| 91视频xxxx| 欧美日韩大陆在线| 欧美一级片在线| 狠狠色伊人亚洲综合成人| 久久久亚洲精品一区二区三区| 亚洲二区在线观看| 国产真实精品久久二三区| 99久久精品久久久久久清纯| 精品欧美一区二区在线观看| 日韩avvvv在线播放| 欧美蜜桃一区二区三区| 欧美大片在线观看| 欧美国产一区在线| 在线欧美小视频| 一区二区三区中文字幕在线观看| 欧美午夜影院一区| 综合激情网...| 看片的网站亚洲| 一区二区三区毛片| 亚洲韩国一区二区三区| 中文一区二区在线观看| 韩国一区二区视频| 成人精品免费网站| 国产精品久久久久久久久久久免费看| 成人av电影免费在线播放| 久久久久99精品一区| 豆国产96在线|亚洲| 国产成人精品免费| 国产日韩欧美麻豆| 91亚洲大成网污www| 日韩三级免费观看| 经典三级视频一区| 亚洲欧美激情一区二区| 日韩一级黄色片| 精品一区二区在线播放| 国产精品乱子久久久久| 亚洲午夜久久久| 国产午夜亚洲精品不卡| 精品在线免费观看| 在线91免费看| 成人中文字幕在线| 色哟哟日韩精品| 国产一区中文字幕| 日韩国产高清在线| 亚洲 欧美综合在线网络| 最新国产精品久久精品| 亚洲欧美日韩中文字幕一区二区三区| 91精品国产一区二区| 成人综合婷婷国产精品久久蜜臀| 色婷婷综合中文久久一本| 国产三级精品三级在线专区| 美女尤物国产一区| 久久欧美一区二区| 一二三区精品福利视频| 国产日韩欧美a| 高清不卡一区二区在线| 色综合久久久久久久| 国产成人免费视频精品含羞草妖精| 国产精选一区二区三区| 中文字幕高清不卡| 欧美一区二区三区免费大片 | 一本色道a无线码一区v| 成人a级免费电影| 国产精品888| av激情成人网| 精品国产自在久精品国产| 国产成人综合自拍| 亚洲午夜在线视频| 国产美女视频91| 成人三级伦理片| 国产精品久久久久久妇女6080 | 欧美一区二区三区人| 91网站最新地址| 91久久线看在观草草青青| 99re成人在线| 国产激情一区二区三区桃花岛亚洲 | 国产精品蜜臀在线观看| 欧美另类变人与禽xxxxx| 欧美日韩中文字幕一区二区| 一区二区免费看| 中文字幕免费观看一区| 亚洲夂夂婷婷色拍ww47| 久久疯狂做爰流白浆xx| 欧美日韩精品电影| 亚洲日本欧美天堂| 一区二区三区蜜桃| 美脚の诱脚舐め脚责91| 久久精品久久久精品美女| 这里只有精品视频在线观看| 亚洲高清在线视频| 欧美电影在线免费观看| 欧美人牲a欧美精品| 久久久www成人免费无遮挡大片| 国产日韩欧美综合在线| 国产99一区视频免费| 国产精品一区二区视频| 国产精品久久久久一区| 免费在线观看成人| 一卡二卡三卡日韩欧美| 亚洲国产日韩综合久久精品| 亚洲欧美二区三区| 精品欧美一区二区在线观看| 视频一区二区不卡| aaa亚洲精品一二三区| 午夜精品一区在线观看| 337p粉嫩大胆噜噜噜噜噜91av| 久久aⅴ国产欧美74aaa| 欧美成人性福生活免费看| 欧美肥大bbwbbw高潮| 成人av网站在线| 欧美高清视频不卡网| 欧美久久婷婷综合色| 精品免费视频.| av资源网一区| 国产美女精品在线| 久久久久久毛片| 在线播放日韩导航| 欧美制服丝袜第一页| 久久91精品国产91久久小草| 欧美高清在线视频| 日韩一级完整毛片| 欧美中文字幕一区二区三区| 国产精品一区二区不卡| 国产麻豆欧美日韩一区| 裸体一区二区三区| 美腿丝袜在线亚洲一区| 青青青伊人色综合久久| 日韩精品亚洲专区| 一区二区三区不卡在线观看| 午夜精品一区二区三区电影天堂 | 黄色资源网久久资源365| 成人av综合在线| 91麻豆精品国产91久久久久久 | 国产精品家庭影院| 精品欧美一区二区久久| 国产欧美日韩综合精品一区二区| 一区二区三区欧美久久| 亚洲国产综合在线| 人妖欧美一区二区| 久久99这里只有精品| 国产一区二区伦理| 成人激情图片网| 欧美第一区第二区| 国产精品另类一区| 久久久亚洲高清| 亚洲一区二区偷拍精品| 亚洲欧美在线观看| 中文字幕一区在线观看视频| 免费成人av资源网| 亚洲国产精品人人做人人爽| 韩国视频一区二区| 国产精品沙发午睡系列990531| 色一区在线观看| 亚洲va中文字幕| 高清beeg欧美| www.在线成人| 欧美精品高清视频| 精品日韩一区二区三区| 免费看黄色91| 欧美激情在线看| 精品福利一区二区三区免费视频| 国产精品午夜免费| xvideos.蜜桃一区二区| 色婷婷狠狠综合| 国产精品久久久久永久免费观看|