我们做了多分支管理环境隔离,一套满足开发调试、测试、产品验收、多需求并行开发的千人千面的环境。请求进行精准路由,其中有个环节是需要将前端项目作为一个服务注册到注册中心。
这个项目用得是nacos
通过shell
构建脚本如下:
#!/bin/bash
set -e
SERVICE_ADDR=xxx.xxx.xxx.xxx:8848
IP=${POD_IP}
PORT=80
SERVICE_NAME=portal
NAMESPACE_ID=xxxx-xxxx-xxx-xxx-xxxxxxxx
METADATA=$(cat <<EOF
{"preserved.register.source":"shell","branch":"${BRANCH}"}
EOF
)
urlencode(){
echo "$1" | tr -d '\n' | od -An -tx1 | tr ' ' '%' | tr -d '[:cntrl:]'
}
#registe params
registerData="serviceName="$SERVICE_NAME
registerData=$registerData"&healthy=false"
registerData=$registerData"&ip="$IP
registerData=$registerData"&port="$PORT
registerData=$registerData"&weight=1.0"
registerData=$registerData"&namespaceId="$NAMESPACE_ID
registerData=$registerData"&ephemeral=false"
registerData=$registerData"&metadata="$(urlencode "$METADATA")
#beat params
beatParam="serviceName="$SERVICE_NAME
beatParam=$beatParam"&ip="$IP
beatParam=$beatParam"&port="$PORT
beatParam=$beatParam"&namespaceId="$NAMESPACE_ID
beatData=$(cat <<EOF
{"cluster":"DEFAULT","ip":"$IP","port":$PORT,"metadata":$METADATA,"scheduled":true,"serviceName":"$SERVICE_NAME","weight":1}
EOF
)
beat=$(urlencode "$beatData")
beatParam=$beatParam"&beat=$beat"
function register () {
curl -X POST "http://$SERVICE_ADDR/nacos/v1/ns/instance?$registerData"
}
function delete(){
curl -X DELETE "http://$SERVICE_ADDR/nacos/v1/ns/instance?$registerData"
}
function beat(){
while true
do
curl -X PUT "http://$SERVICE_ADDR/nacos/v1/ns/instance/beat?$beatParam"
echo ''
sleep 3
done
}
case "$1" in
register)
register
;;
delete)
delete
;;
beat)
beat
;;
*)
echo "USAGE:${0} register|delete|beat"
;;
esac
在docker
打包时将脚本打包进容器,并传入必要的环境变量,并授予全部权限
FROM /basefront:v1.3.0
RUN echo "Asia/shanghai" > /etc/timezone;
WORKDIR /root
ADD dist/ /usr/share/nginx/html
ARG gitbranch
ENV BRANCH=${gitbranch}
COPY dev/register.sh /root/register.sh
RUN chmod 777 /root/register.sh
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
EXPOSE 80
在k8s
环境下我们可以利用生命周期回调来执行脚本,在项目启动成功后执行注册,项目停止前删除实例,由于前端项目不容易挂,所以直接注册一个永久节点,不用临时节点所以,不用心跳。
preStop:
exec:
command:
- "/bin/bash"
- "-cx"
- "/root/register.sh delete"
postStart:
exec:
command:
- "/bin/bash"
- "-cx"
- "/root/register.sh register"
这样就完成了前端项目服务化。重点来了此时如果你的前端项目用的时history
模式会有个很坑的问题。就是你的nginx
配置是这样的,当然如果你是hash
模式就不会存在这个问题,因为nginx
最终会忽略掉#
后面的内容。
location / {
root /usr/share/nginx/html;
if ($request_filename ~* .*\.(?:htm|html)$) {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
index index.html;
try_files $uri $uri/ /index.html;
}
try_files
指令解析:
uri
文件能在本地服务器找的到,那么就直接放回./
变成目录,如果目录存在返回一个重定向301
告诉浏览器到这个目录下找index.html
.index.html
.在history
模式下,将url
上的#
去掉了
http://域名/#/user/login
变成了http://域名/user/login
所以此时走到了第二个参数,发生了301
重定向。
由于此时前端项目是通过gateway
进行访问的,而gateway
是通过注册中心获取到的容器ip
进行访问的,所以此时重定向的地址变成了 http://ip:port/user/login/
然后在浏览器上直接就跳到了这个地址。即使你用的不是gateway
,在双层nginx
下也会出现这个问题。所以此时,你需要在改下nginx
的配置改成如下方式,为什么在第二个路径后面加index.html
。因为history
会在每个目录下都放一个index.html
。此时找到的是文件就不会发生重定向,而是直接访问了文件。所以就没问题了。还有一种方案就是如果你就是单页应用,索性直接返回 /index.html
。把第二个参数去掉也是可以的。
location / {
root /usr/share/nginx/html;
if ($request_filename ~* .*\.(?:htm|html)$) {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
index index.html;
try_files $uri $uri/index.html /index.html;
}