docker-compose查看服务状态(Docker+DockerCompose封装web应用的方法步骤)
类别:服务器 浏览量:2032
时间:2021-09-29 01:04:22 docker-compose查看服务状态
Docker+DockerCompose封装web应用的方法步骤目录
- 技术栈
- 后端构建 api
- 前端构建 web
- 网关构建 gateway
- Nginx 配置
- Dockerfile
- Lua 实现基于企业微信的网关认证
- 使用 DockerCompose 进行容器编排
这篇文章会介绍如何将后端、前端和网关通通使用 Docker 容器进行运行,并最终使用 DockerCompose 进行容器编排。
技术栈前端
- React
- Ant Design
后端
- Go
- Iris
网关
- Nginx
- OpenResty
- Lua
- 企业微信
这里虽然我们写了 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" ]
这里值得一提的是,因为前端肯定会去调用后端接口,而且这个接口地址是根据部署而改变,
所以这里我们使用了 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" ]
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
这里需要讲几个点:
- 设置前端的 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应用内容请搜索开心学习网以前的文章或继续浏览下面的相关文章希望大家以后多多支持开心学习网!
您可能感兴趣
- dockerpull的镜像位置(解决docker pull镜像报错的问题)
- docker节点不能启动(解决docker中ifconfig不可用的问题)
- docker如何搭建gitlab(docker+gitlab+gitlab-runner部署详解)
- docker怎么部署node-exporter(Docker搭建部署Node项目的方法步骤)
- docker容器的创建启动和停止操作(docker版es、milvus、minio启动命令详解)
- docker显示容器的gc日志(解决docker使用GDB,无法进入断点的问题)
- docker和k8s怎么部署(在docker中部署k8s的方法)
- 查看docker容器进程运行状态命令(docker容器内要启动两个进程时Dockerfile的实现代码)
- docker和golang哪个好(使用Golang玩转Docker API的实践)
- docker怎么连主机数据库(docker 安装nacos并配置数据库的教程详解)
- dockerbuild清除缓存(Docker自动化构建Automated Build实现过程图解)
- docker搭建elasticsearch(docker安装ElasticSearch:7.8.0集群的详细教程)
- docker容器端口和内部进程(Docker动态给容器Container暴露端口操作)
- docker容器连接宿主机(docker 实现容器与宿主机无缝调用shell命令)
- dockerfile镜像案例(Dockerfile构建自定义镜像的实现)
- docker查看管理配置信息(Docker Secret的管理和使用详解)
- 这部动漫中的女孩子,可比101女孩更加励志(这部动漫中的女孩子)
- 《白狐的人生》热拍 贾征宇偶像包袱难自弃 图(白狐的人生热拍)
- 七夕取消了,牛郎织女没做核酸七夕已经取消(牛郎织女没做核酸七夕已经取消)
- 网友抵制 多地取消 夏日祭 为何惹众怒(网友抵制多地取消)
- 兄弟萌,今年的七夕又取消了 思考 思考(今年的七夕又取消了)
- 七夕取消是什么梗(七夕取消是什么梗)
热门推荐
- cssanimation效果(使用CSS transition和animation改变渐变状态的实现方法)
- JS文件智能提示另一个JS文件中的成员
- vue按需引入elementui组件(vue ElementUI实现异步加载树)
- JS匿名函数的用法
- css3 box-sizing
- sqlserver2012卸载工具(Windows下SQL Serever 2012彻底卸载删除教程)
- python自动识别旋转验证码(Python实现字符型图片验证码识别完整过程详解)
- 服务器怎么创建网站(网站如何选择服务器空间?)
- mysql自定义安装教程5.7(MySQL系列-源码编译安装v5.7.34)
- 目前主流的web服务器有哪些(什么是WEB服务器? 常用的WEB服务器有哪些?)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9