我们先制作上一篇文章中提及的两个镜像。
frontend_toolkit 镜像
FROM ubuntu:latest
# 将nodejs复制到容器下,防止因网络原因导致镜像打包慢或失败的问题
# 版本按需选择
COPY nodejs_setup_13.x.sh /home/
# 安装nodejs
RUN bash /home/nodejs_setup_13.x.sh
# 安装基础工具
RUN apt-get install -y git nodejs curl
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update && apt-get install -y yarn vim
服务基础镜像
FROM nginx:latest
WORKDIR /var/wwwroot
# 将nginx配置文件复制到容器下
COPY ./gaia/nginx/* /etc/nginx/conf.d/
# 将静态资源复制到访问路径下
COPY ./dist/ /var/wwwroot
EXPOSE 80
server {
listen 80;
server_name _;
# 服务的配置
location / {
alias /var/wwwroot/;
try_files $uri $uri/ /index.html =404;
}
}
上述docker文件编写好后,分别进行build即可。
发现和注册服务
通过K8S注册一个服务后,如果是做了集群的,可以参考配置Ingress
来启用服务发现,但我们目前仅使用到单集群单节点的方式,实际上是可以直接通过服务名来访问的。
比如我们部署了一个服务叫username-func-test-k8s
,那么我们可以通过命令curl username-func-test-k8s
来访问到服务本身。
回想我们要的效果是通过cookie来识别我们要访问的服务,所以只需要通过配置nginx解析cookie来做对应的反向代理即可。
这里值得说明的是nginx的反向代理是无法支持配置search
的,也就是说没有办法通过变量的形式直接进行反向代理,比如:
# 这样是不支持的
proxy_pass http://$SVC_NAME/
所以每次有新服务,我们都需要重新配置一条规则,看似需要有一个专门的服务来管理这件事,还记得我们CI/CD的最后一个阶段所做的事吗?
没错,它所访问的地址,正是我们的这个服务。可随意搭建一个http服务即可,核心逻辑如下:
function updateNginxConfig (conf) {
// 读取nginx模板文件
const nginxTemplate = _.template(fs.readFileSync('nginx.template', { encoding: 'utf-8' }))
// 读取服务列表
const conf = getSvcList()
// 根据服务列表生成新的nginx文件
fs.writeFileSync('/etc/nginx/conf.d/default.conf', nginxTemplate({ services: conf }))
}
function getSvcList () {
// 读取k8s服务列表,以json输出方便使用
const svcQuery = childProcess.spawnSync('kubectl', ['get', 'svc', '-o', 'json'], { encoding: 'utf-8' })
if (svcQuery.stdout) {
const svc = JSON.parse(svcQuery.stdout)
// 排除一些基础服务,我们只要符合`${username}-${branch}-svc`规则的服务
const gaiaSvc = svc.items.filter(item => /\w+-(\w+-)*svc/.test(item.metadata.name))
return gaiaSvc.map(item => ({ name: item.metadata.name }))
} else {
throw new Error(svcQuery.stderr)
}
}
// 这个接口用于返回符合规则的所有的服务列表
router.get('/', (req, res) => {
try {
return res.status(200).json({ list: getSvcList() })
} catch (err) {
return res.status(500).json({ msg: err })
}
})
// CI中第四个阶段调用的方法
router.post('/', (req, res) => {
// 生成并更新nginx配置文件
updateNginxConfig()
// 重启nginx服务
reloadServer()
return res.status(201).json()
})
最后我们来看看nginx的模板长什么样子:
server {
server_name server.com;
listen 80;
proxy_next_upstream off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Accept-Encoding "";
charset utf-8;
location / {
set $svc '';
# 获取cookie信息
if ($http_cookie ~* "gaia_svc=(.+?)(?=;|$)") {
set $svc $1;
}
# 如果没有指定环境,那么反向代理到一个默认的服务上去,避免出现50x错误
if ($svc = '') {
proxy_pass https://10.10.10.10;
}
# 根据k8s的服务,循环出判断体,最终根据cookie设置的服务名做反向代理
<% _.forEach(services, function (svc) { %>
if ($svc = <%= svc.name %>) {
proxy_pass http://<%= svc.name %>;
}
<% }) %>
# 注入环境选择器
# 我们之前一直没有说明测试人员绑定了这个服务器后,应该怎么切换环境呢?
# 其实就是通过这个脚本来完成,我们可以注入一些页面元素,或者通过快捷键唤起环境选择器
sub_filter_types text/html;
sub_filter_once on;
sub_filter '