转载,原文 张建飞(Frank)解决问题黄金三步:定义问题—分解问题—归

我们经常说软件开发中,没有银弹。的确,单看软件领域,很少有什么方法论是普适的。然而再拔高一些,跳出软件的范畴,是有一些东西是普世的,比如老子说的“道”。

好吧,“道”说的有点大。不过,最近我的确有一个新发现——我发现“归类分组”在我们解决问题中起着巨大的作用。而这里的“问题”不仅仅限于软件设计,从日常生活、产品设计到公司战略、生物分类。都有它的身影。

这是为什么呢?究其原因可能有两个:

一、归类分组是抽象的重要方法,解决复杂问题我们离不开抽象。

二、归类分组是结构化的重要步骤,结构化的表达离不开归类分组。

当然,归类分组要先有素材可以“归类”才行。因此,完整的方法论是这样的,我给他起了一个好听的名字——解决问题黄金三步:定义问题——分解问题——归类分组

这三步的详细操作如下:

第一步,定义问题:也就是要清楚我们要解决的问题是什么?
第二步,分解问题:对问题进行分析拆解,形成平铺的多个子问题,此步可以尽量发散。
第三步,归类分组:对子问题进行归纳、剪枝,将趋同的子问题,合并成一类问题。

image.png

如上图所示,通过黄金三步以后,我们就可以得到一个形同“金字塔”的结构,也就是我们经常说的金字塔结构。根据问题的复杂程度,这个金字塔结构可能是三层,也可能是二层和多层。

看似简单的三步操作会有那么大的作用吗?废话不多说,直接上案例。

在日常生活中的运用

你出门买报纸,你老婆说家里冰箱空了,顺便带点东西回来吧。她给你列了一个清单,里面有葡萄,橘子,咸鸭蛋,土豆,鸡蛋。你说就这么多了吗?她说苹果和胡萝卜也可以买一点。当你准备出门的时候,她说家里的牛奶和酸奶也没有了,最好也买一点回来。

你觉得你能把老婆交代的东西都买齐吗?我看很难,因为我们的大脑短期记忆无法一次容纳7个以上的记忆项目,超过5个时,我们就会开始将不同的项目归类到不同的逻辑范畴,以便于记忆

如果我们将葡萄,橘子,牛奶,咸鸭蛋,土豆,鸡蛋、胡萝、苹果,酸奶。按照逻辑关系,进行下归类分组,比如把葡萄、橘子、苹果归为水果类,把土豆、胡萝卜归为蔬菜类,便可以大大帮助我们提高记忆效率。

注意,这里分类的作用不只是将一组9个概念,分成每组各有4个、3个和2个概念的3组概念,因为这样还是9个概念,你所要做的是提高一个抽象层次,将大脑需要处理的9个项目变成3个项目。

如果你已经这么做了,恭喜你,你已经在实践黄金三步了:

定义问题:冰箱空了,需要购买补给。
分解问题:你要分析购买哪些东西呢?
归类分组:为了方便记忆,你将要买的9个东西按性质分成了三组,形成如下的金字塔结构。

image.png

这就是结构化思维,下次你再接到老婆这样的“需求”,记得把这个方法论用上,她一定会对你刮目相看。

在工作汇报中的运用

定义问题

“小张,客户对销售报告和库存报告不满意,你去看一下什么原因。” 面对这种典型的工作问题,你打算怎么处理呢?

分解问题

首先,你肯定要去调研客户不满意的原因,经过调查你发现,客户不满意主要有以下原因造成的:

  1. 提交报告的周期不恰当;
  2. 库存数据不可靠;
  3. 获得库存数据的时间太迟;
  4. 库存数据与销售数据不吻合;
  5. 客户希望能改进报告的格式;
  6. 客户希望除去无意义的数据;
  7. 客户希望突出说明特殊情况;
  8. 客户希望减少手工计算。

虽然你做了大量的工作,调查也很充分。但是,如果你要是把这8个原因直接给老板汇报,估计效果不会太好。 你可以自己做个试验,仔细阅读上面的列表3分钟,你能从中获得什么?同样的,你老板看到这样的罗列也会是一脸懵逼,不知道重点在哪。

归类分组

所以我们有必要加上第三步,对问题进行进一步的归类分组,我们可以将8个问题概括为3组:

  1. 报告中含有不可靠的数据;
  2. 报告的格式混乱;
  3. 产生报告的时间太晚,无法采取有效措施

进行分组之后,我们就可以得到如下的金字塔结构:

image.png

这种结构化的表达,很明显让问题的表述更加清晰。领导也可以很快抓住问题的要点,并作出相应的决策。当然,这里是为了着重介绍归类分组,真正的汇报你还要提供对应的解决方案,然后让老板做“选择题”。

在写代码时的运用

你遇到一个相当复杂的业务场景,在这个业务操作中,涉及到大量的校验和执行操作。这种代码,如果没有一定的策略,很容易写成大泥球。

定义问题

例如,在我们的业务中有一个商品上架的操作,是一个非常复杂的业务操作。

分解问题

对这种业务问题的分解,通常是产品经理的职责,但是作为工程师也不能完全依赖PRD。因为产品视角和工程视角还是有差别的。

针对“商品上架”,我们做了如下的功能分解:
image.png

归类分组

同样,在分解之后,我们需要有一个归类分组的过程。否则,这些步骤的平铺会让代码显得凌乱,不方便记忆和维护。通过分析,我们可以把分解后的步骤分成三个阶段:

  1. 初始化阶段
  2. 校验阶段
  3. 执行阶段

通过黄金三步,我们可以得到如下的金字塔结构:

image.png

最后,我们按照这个结构去组织我们的代码,整个代码结构会更加清晰,代码的可维护性也会好很多。

image.png

在应用架构中的运用

应用架构主要解决的是模块、组件定义和模块、组件关系的问题。

image.png

从宏观层面来说,架构设计也是遵循这三个步骤的。比如,我们的架构要如何分层,分模块、分组件就是在做问题分解。然后,模块和组件要归属在哪个层次,要如何命名,就是在做抽象,在做分类归组。

在综合考虑功能属性+质量属性,然后通过黄金三步,就能得到我们想要的架构设计。例如,我们的COLA 2.0架构也是在这个方法论的指导下完成的。
image.png

在产品架构中的运用

定义问题

“小张,为了做新零售,我们打算做一款智能互联网POS机,你先做一下产品设计。”

分解问题

通过调研你发现,作为POS机,其核心功能是收银和经营管理。所以至少需要包含收银的功能、服务核销的功能、商品管理的功能、库存管理的功能等等。

但如果仅仅是满足这些功能,和传统的POS并没有多大的区别,为了满足“智能”和“互联网”的要求,你去深入百度了一下智能POS应该具备的功能,大致包含以下功能:

刷脸支付;
支持品牌商营销;
支持自主营销;
智能定价;
外卖对接;
彩票对接;
虚拟充值等等。
在问题分解阶段,我们应该尽量多的收集信息,多发散,多头脑风暴。

归类分组

发散完,我们还是要收回来。在收敛之前,我们先看一下产品框架应该包含哪些东西,通常,一个产品架构至少要包含三个层次:

用户感知层(在何种场景下通过何种方式触达用户);
功能模块层(通过哪些功能模块实现产品的核心功能,和哪些外部平台功能有信息交互);
数据层(产品的数据从哪里来、产品的数据沉淀到何处去)。
在这三个层次的基础上,我们再对每个层次内的模块进行分组。例如在功能模块层,我们要对功能进行分类,让分散的功能点内聚成更大的产品模块(体现在用户界面上,往往是一级菜单和子菜单的关系)。

比如对于POS的收银产品模块,我们可以提供以下的产品功能:

  1. 支付宝收银
  2. 现金收银
  3. 微信收银
  4. 刷脸支付
  5. 记账等

通过层次划分,模块划分我们就可以得到一个相对清晰的产品架构,以智能POS为例,我们可以画出如下的产品架构:

image.png

分类是科学也是艺术

通过上面的案例,我想你已经领会到黄金三步:定义问题——分解问题——归类分组的要义了。其中前两步相对比较直观,而第三步往往是不容易做好,也容易被忽略的关键步骤。

实际上,对事物的归类分组是我们人类的天性。人类大脑会自动将发现的所有事物以某种持续组织起来。基本上,大脑会认为同时发生的任何事物之间都存在某种关联,并且会将这些事物按某种逻辑模式组织起来。

比如,下面这张图片:
image.png

无论是谁,乍一看到上面的六个黑点,都会认为共有两组墨点,每组三个。造成这种印象的原因主要是有些黑点之间的距离比另一些黑点之间的距离大。

空间是一个相对比较直观的逻辑关系,然而,并不是所有的逻辑关系都是如此的显性化。实际上,很多的概念会在多个维度进行交叉耦合,这就给我们的归类分组带来了很大的挑战。

生物分类学

生物分类学通常直接称分类学(Taxonomy),是一门研究生物类群间的异同以及异同程度,阐明生物间的亲缘关系、基因遗传、物种进化过程和发展规律的基础科学。

最流行的分类是五界系统。通常包括七个主要级别:界(Kingdom)、门(Phylum)、纲(Class)、目(Order)、科(Family)、属(Genus)、种(Species)。种(物种)是基本单元,近缘的种归合为属,近缘的属归合为科,科隶于目,目隶于纲,纲隶于门,门隶于界。

不过分类学到不是一门很严谨的“科学”。就像比尔.布莱森在《万物简史》里说的:

分类学有时候被描述成一门科学,有时候被描述成一种艺术,但实际上那是一个战场。即使到了今天,那个体系比许多人认为的还要混乱。以描述生物基本结构的门的划分为例。许多生物学家坚持认为总数30个门,但有的认为20来个门,而爱德华在《生命的多样性》一书里提出的数字高达令人吃惊的89门。

由此可见,分类并不像我们想的那么简单。我们观察事物的视角不同,对问题的认知程度不同,得出来的分类很可能也完全不同。

特别是当概念之间有交叉情况,分类就会变得更加棘手。比如,在你的笔记本中,有“读书笔记”和“哲学笔记”两个平级的分类,此时你阅读了一本哲学书籍,那么你会把这本书的读书笔记放在哪个分类里呢?

分类的原则

分类的基本原则是MECE法则。透过结构看世界,说的就是MECE法则。

MECE法则即mutually exclusive collectively exhaustive的缩写,是麦肯锡咨询顾问芭芭拉·明托在《金字塔原理》中提出的一个思考工具,意思是“相互独立,完全穷尽”,也常被称为“不重叠,不遗漏”。

image.png

MECE原则的思想精髓,就是全维度的去分析一件事情,不要有遗漏和重复的部分。

我们可以借助已有的结构化思维模型(分类的框架)来分析问题,确保每一层要素之间“不重复、不遗漏”。

分类的思维模型

实际上,在上文中我们已经提到了一些分类的思维模型。比如,在应用架构中,我们通常有展现层、控制层、应用层、领域层和基础实施层;在产品架构中,有用户感知层、功能模块层、数据层。这些框架可以有效的指导我们在各自领域中开展工作。

类似于这样的分类思考模型还有很多,比如:
1、制定市场营销策略的“4P”模型,即产品策略(Product Strategy)、价格策略(Price Strategy)、渠道策略(Place Strategy)、促销策略(Promotion Strategy)。

2、分析问题的“5W2H”模型,即Why、What、Who、When、Where、How和How much。

3、思考组织战略的“7S”模型,即经营策略(Srategy)、组织结构(Structure)、运营系统(System)、经营风格(Style)、职员(Staff)、组织技能(Skill)和共享价值观(Shared value)。

4、分析竞争力的SWOT模型,SWOT分析代表分析企业优势(Strengths)、劣势(Weakness)、机会(Opportunity)和威胁(Threats)。

5、制定目标的SMART模型,即制定目标要满足确定性(Specific) 、可度量性(Measurable)、可实现性(Attainable)、相关性(Relevant)和时效性(Time-based)。

这些思维模型都是宝贵的经验总结,相当于已经帮我们做好了第三步“归类分组”的工作,我们只需要按照模型制定的框架往里面填充要素即可。

因此擅用模型,活用框架。可以极大的提升我们解决问题的效率,同时帮助我们做更加全面的、更加结构化的思考。做了“无遗漏,不重复”。

Android、iOS的webview注入JavaScript代码修改网页内容

需要修改webview中的网页内容、网页元素动作

1.实现原理

webview导航栏方法中执行JavaScript代码

浏览器地址栏是支持运行JavaScript代码的

javascript:开头后跟要执行的语句

// 弹窗
javascript:alert('hello world!');

ps:不可以复制粘贴的方式来测试,这样是无效的,至少“javascript:”是手写补全才可以

那么webview是否也可以呢?

在webview中同样适用本方法

虽然webview没有可见的地址栏,但是webview提供操作导航导航栏的方法

2.常用JavaScript代码片段

// 通过class查找隐藏本element
javascript:(function() {
   document.getElementsByClassName('your_class_name')[0].style.display='none'; 
})();
                
// 通过id查找因此本element
javascript:(function() {
   document.getElementById('your_id').style.display='none';
})();


// 某个element点击事件 并且修改打开一个弹窗后的页面element
javascript:(function() {
// 首次页面加载必须有的element
var url = 'https://www.baidu.com';
var text = 'p标签文本被替换了';
var bottom = document.getElementsByClassName('bottom')[0];
bottom.onclick = function(){
  // 弹窗element
    var dialogButton = document.getElementsByClassName('button')[0];
    var dialogItem = document.getElementsByClassName('item')[2].getElementsByTagName('p')[1];
    dialogButton.onclick = function(){window.open(url, '_self');}; // 点击事件跳转
    dialogItem.replaceWith(text); // 修改标签文本内容
};
})();

// 如果某些页面元素是在页面完成后出现的
// 也就是webview 到了 onPageFinished 周期中,页面元素还未加载出来,需要使用定时器来处理
// 定时器轮询检查页面元素对象,直到找到需要的处理的页面元素对象后进行处理,然后销毁定时器
var timer = setInterval(function () {
    if (document.getElementsByClassName("class name")[0]) {
      // 你的业务代码
      
      clearInterval(timer); // 销毁定时器
    }
  }, 1000);

3.Android中实现webview注入JavaScript代码

// java
final WebView webview = (WebView)findViewById(R.id.browser);
 
    webview.getSettings().setJavaScriptEnabled(true);
 
    webview.setWebViewClient(new WebViewClient() {
     @Override
    public void onPageFinished(WebView view, String url)
    {
        // hide element by class name
        webview.loadUrl("javascript:(function() { " +
                "document.getElementsByClassName('your_class_name')[0].style.display='none'; })();");
        // hide element by id
        webview.loadUrl("javascript:(function() { " +
                "document.getElementById('your_id_name').style.display='none';})();");
 
    }
    });
 
webview.loadUrl(url);

4.iOS中实现webview注入JavaScript代码

// objective-c

5.Flutter中实现webview注入JavaScript代码

参考 Android 和 iOS

6.进阶注入外部引入的hook.js代码

注入的JavaScript代码需要修改重新发包?

注入的JavaScript很大一段硬编码到APP包中如何管理?

进阶实现注入外部hook.js

把JavaScript代码单独写在hook.js并放置在cdn上

// https://www.iamle.com/hook.js
'v0.0.1 app webview hook'

;(function (window) {
    //your code
})(window)


// app 用webview.loadUrl方法中注入js
javascript:(function() {
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = 'https://www.iamle.com/hook.js';
     document.body.appendChild(script);
})();

// Android java为例
 @Override
    public void onPageFinished(WebView view, String url)
    {
        String js = "javascript:(function() {";
        js += "var script = document.createElement('script');";
        js += "script.type = 'text/javascript';";
        js += "script.src = 'https://www.iamle.com/hook.js';";
        js += "document.body.appendChild(script);";
        js += "})();";
        view.loadUrl(js);
    }

这样后续修改只需要修改hook.js即可,APP不用重新打包📦

7.经验

在webview中不支持window.location.href进行网址导航

但是支持使用window.open, window.open(‘https://www.iamle.com‘, ‘_self’);

1.综述

一句话: 关注 CNCF 基金会 Cloud Native 云原生互动全景图

CNCF云原生互动全景图
打开网站,全景图是可以点击的,在图中找你关注的领域

2.找到你关注的分类领域

比如我关注“API Gateway API网关”,就点击他的图标就可以看到相关信息
APISIX

KrakenD

非常直观的一个概览
项目开发语言,代码提交柱状图,等开源代码维护情况信息

可以说CNCF的全景图就是一张开启云原始大门的大地图,地图在手开始遨游吧

3.CNCF分类大纲 (截止2020年02月08日)

CNCF云原生互动全景图

App Definition and Development 应用定义和开发

  • Database 数据库
  • Streaming & Messaging 流处理和消息系统
  • Application Definition & Image Build 应用程序定义和图像构建
  • Continuous Integration & Delivery 持续集成与交付

Orchestration & Management 编排和管理

  • Scheduling & Orchestration 计划与编排
  • Coordination & Service Discovery 协调与服务发现
  • Remote Procedure Call 远程过程调用
  • Service Proxy 服务代理
  • API Gateway API网关
  • Service Mesh 服务网格

Runtime 运行时

  • Cloud Native Storage 云原生存储
  • Container Runtime 容器运行时
  • Cloud Native Network 云原生网络

Provisioning 提供者

  • Automation & Configuration 自动化与配置
  • Container Registry 容器注册中心
  • Security & Compliance 安全与合规
  • Key Management 密钥管理

Platform 平台

  • Certified Kubernetes – Distribution k8s认证过的产品
  • Certified Kubernetes – Hosted k8s认证过的主机
  • Certified Kubernetes – Installer k8s认证过的安装工具
  • PaaS/Container Service PaaS平台/容器服务

Observability and Analysis 可观察性和分析

  • Monitoring 监控
  • Logging 日志
  • Tracing 跟踪
  • Chaos Engineering 混沌工程

Serverless 无服务器

Members 加入CNCF的会员

Special 特别的

  • Kubernetes Certified Service Provider k8s认证服务提供商
  • Kubernetes Training Partner k8s培训合作伙伴

api网关的本质

不用扯那么多,也不用画图,一句话说清楚
api网关:流量总入口,得以集中控制!
就这么简单

api网关协议上最基本要支持HTTP 和 WebSocket,功能强大点的更会支持tcp/udp的负载均衡接入
正因为支持的是http协议,所以api网关不仅仅可以作为 RESTful API 接入,接入带页面的web都可以的,完全可以当成一个web负载均衡器使用

api网关的作用

解决:认证、鉴权、安全、流量管控、缓存、服务路由,协议转换、服务编排、熔断、灰度发布、监控报警等问题
本质上,流量从我过,我就可以做想做的控制,上面列的就是我需要的控制
有了api网关才不至于裸奔,才不至于在业务层“重复建设”,才不至于在业务层去用redis+lua实现“亲,你访问过于频繁,请稍后再试”,这个事交给api网关就成

api网关比较

开源api网关大全

之前流水理鱼把市面上开源的api网关整理了个大全 “开源API网关大全20款+” https://www.iamle.com/archives/2591.html ,大部分都加入了CNCF

以下api网关3Scale、Ambassador、APISIX、Express Gateway、Gloo、Kong、KrakenD、Mia-Platform、MuleSoft、Reactive Interaction Gateway、Sentinel、Tyk、WSO2 API Microgateway
加入了CNCF

开源api网关技术栈情况

api网关技术栈,老一派的使用java,新派的使用golang、openresty+lua
小众Node.js、.net、C++ 技术栈虽然不一样,达到的目的却是一样的。
用静态语言编写api网关是有弊端的
使用静态语言编写的api网关都会有插件编写不方便的问题
使用java编写的老牌网关性能差,历史包袱重
openresty+lua也许是最佳的api网关、waf、web防火墙解决方案
依托于openresty平台具备超高性能,又依托于lua获得了动态性
CloudFlare也是春哥当年用openresty+lua技术栈做的引擎

我们从不同的技术栈来做个api网关分类
openresty+lua开源api网关
代表有Kong、APISIX、3scale、、API Umbrella

Kong不用做太多介绍,应该是开源里面最热的一个api网关了,相对庞大复杂
APISIX,轻巧+极致性能+热插件,值得一提到是插件中有个serverless的支持,简单说就是写一段自定义lua脚本,挂载到openresty任意阶段执行!

golang开源api网关
代表有Tky、Manba、GOKU API Gateway、Ambassador(基于Envoy)、Gloo(基于Envoy)、KrakenD、BFE

java开源api网关
代表有Gravitee、Zuul、Sentinel、MuleSoft、WSO2、Soul

Erlang开源api网关
代表有RIG – Reactive Interaction Gateway

.net开源api网关
代表有Ocelot

Node.js开源api网关
代表有express-gateway

闭源商业api网关

从gartner(艾瑞咨询类似)的权威报告可以找到老牌的api网关玩家是谁
行业老大:Apigee、3Scale、Amazon等
各大云都是玩家,比如阿里云api网关、腾讯云api网关、Amazon API Gateway
国内还有几家也在做商业api网关,具体搜索下就能找到

总结下api网关选型建议

  • 前提满足功能需求
  • 不在乎商业闭源绑定,不想麻烦,选你最容易获取的商业api网关例如云平台卖的商业网关
  • 国内用户选 apisix 为代表的openresty+lua技术栈api网关,可以得到中文群组支持
  • 希望国际化的选 kong 为代表的openresty+lua技术栈api网关
  • 有大量的某语言开发人员,可以选基于这个技术栈的api网关,例如java选Gravitee,golang选tyk、Manba

采用多级拦截,后置拦截的方式体系化解决

1.分层拦截

1.1第一层 商业web应用防火墙(WAF)

直接用商业服务

传统的F5硬件,不过现在用的很少了
云时代就用云时代的产品,典型代表 阿里云 web应用防火墙

1.2第二层 API 网关(API Gateway)层

API 网关(API Gateway)

kong为代表的开源 API 网关 实现
openresty + lua 自实现
windows平台 安全狗、云锁 实现

1.3第三层 应用层

用Redis内置lua脚本

redis是块砖,哪里需要哪里搬
redis内置了lua引擎,2.6版本后你可以编写一段lua脚本,完成逻辑判断流程

常见的有对某维度计数器法 对某维度令牌桶法
维度的概念比如就是IP或者IP+模块等, 多个字段合并成一个维度

本方案满足绝大多数应用层的限流需求
当然也可以自己用应用层程序实现,前提是redis+lua满足不了你的需求

2.后置拦截

基本的套路其实很简单,从日志这里计算出恶意IP,恶意用户,再给其他系统用
分控的基本思想也是这样的

已经在用ELK日志系统:可以用ES中定时查询高频IP,送入WAF做拦截
已经在用流计算系统:flink和spark等流计算系统计算出高频恶意IP,用户等

然后就可以应用这些计算出的结果数据做限制,封禁等

3.一+二+三+后置协同工作

第一层Waf当然有拦截,但是对于新IP他不会马上生效, 会有几分钟的时间才会拦截
特别是恶意爬虫IP池一上,大量新IP就来了,第一层会放过来,如果只是一层,结果就是数据库慢查询告警叮叮叮

配合上二层 三层 一层一层拦截
如果没有精力搞二层,那么第一层用买的,第二层不做,搞第三层

后置拦截的结果可以作为长期封禁使用
这种多次拦截的策略和多级缓存的概念是不是很像
多层次的拦截保障源站监控告警静悄悄

面向C端的产品被爬虫,被恶意访问的概率会大很多
面向B端的网页也不是没有风险
面向B端的API也有限流的需求

1.场景

容器化+K8s编排已经是现在进行时
把网站的多个项目设计为云原生(Cloud Native)或老项改造为云原生可以获得诸多能力
例如无云绑定、弹性、部署环境一致性、微服务、DevOps、持续交付
同时下一代微服务框架 服务网格(ServiceMesh) 也能无痛接入

博主现有项目后端开发语言为 PHP、Golang
Golang做一些基础公共服务(短信、消息、搜索等)
这些公共服务化的项目已经容器化
PHP的项目做应用逻辑层,会调用Golang写的一些公共基础服务
PHP项目中直接通过服务名调用服务

需求: PHP项目A 依赖 短信、消息、搜索这3个服务,开发人员无需在本机启动依赖的服务,通过服务直接名透明的调用开发环境dev下的服务,开发人员只需要关注PHP项目A的开发。

☆本文的方案完成了开发人员开发机透明的直接访问K8s服务,从而满足了本需求☆

需要开发机透明访的直接问Kubernetes群集内的服务本文讲的方案都适用

开发机直接访问Kubernetes群集内的服务 示意图

2.基本信息和完成的目标

2.1基本信息

开发办公内网 192.168.1.0/24
开发机2 192.168.1.2

运行K8s群集的Node网络 10.2.0.0/16
Node1 10.2.1.4
Node2 10.2.1.8
K8s 群集网络 10.3.0.0/16

部署deployment whoami服务用于测试
命名空间 default (这里可以用命名空间来区分环境例如dev为开发环境)
镜像 wwek/whoami
服务名 whoami

Pod ip
10.3.0.8在node1 10.2.1.4
10.3.0.70在node2 10.2.1.8

2.3完成的目标

开发机2 192.168.1.2 可以直接访问 whoami服务
也就是可以直接 curl http://whomai 就可以访问服务
本目标即完成需求

3.网络互通1 开发办公内网 <==> 公有云VPC(私有云内网)基础互通

开发办公内网 和 公有云VPC、私有云内网 网络互通

和公有云互通的方案
公有云VPN
专线(SDWAN)

私有云互通就不多讲了,很多公司内网的K8s开发测试群集和开发网络本身就是互通的

各家网络情况各有各的不同,相同的是这些有用的Tips
无论是在公有云VPC、私有云、K8s群集非常关键的一点,子网网段不要冲突不要冲突、子网网段不要冲突、子网网段不要冲突
做基础互通的时候善用公有云的VPC和路由配置功能
甚至你可以不用买公有云自带的VPN网关服务,直接配合VPC路由表用一台VM充当路由器网关、VPN网关
开发测试环境下用zerotier来打通各个内网性价比极高

最终要完成是 开发办公内网 和 公有云VPC(私有云内网) 基础互通

4.网络互通2 开发办公内网 <==> K8s群集内部Pod和Service网络

☆☆☆ K8s Node本身是直接可以访问K8s群集内部Pod网络的!☆☆☆
在Node1上用ping/curl测试 whoami服务 分布在2个Node的2个Pod

可以看到,whoami的2个pod ip都能ping通,用curl测试也能访问到

通过Edge Node互通K8s群集和开发办公之间的网络
那么用Node1作为 开发办公内网 和 K8s群集内部网络的“连接”点我们把这个Node1节点叫做 边缘节点(Edge Node)
边缘节点(Edge Node)可以在运行K8s群集中Node中随便选一个
这里选择Node1,他的网卡信息如下
eth0 vm网卡 ip 10.2.1.4
cbr0 K8s群集创建的网卡 ip 10.3.0.1

可以有2种方式
方式1 在 Edge Node eth0 上启用NAT 这样其他的子网的访问在K8s群集中看到的IP是 10.2.1.4
方式2 K8s群集子网和开发办公内网完全对等互通(公有云VPC路由表、开发办公网络路由表配合做)

完成后
开发办公网络中 在开发机2 192.168.1.2 ping/curl K8s群集中的pod ip

可ping/curl 属于whoami的2个Pod ip

㊗️网络搞通了,那么再解决DNS解析的问题就可以了

5.打通K8s群集中的DNS (开发办公内网的DNS,设置K8s中KubeDns为上游DNS)

在Edge Node1上可以直接访问到KubeDns

kubectl get svc -n kube-system
#kube-dns ip 10.3.254.107

那么在Edge Node1上面装一个DNS Server做个中间转发(使用CoreDNS)
开发网络中的电脑无法直接使用kube-dns,非Edge Node解析结果为空
所以需要在Edge Node1上转一个 DNS Server 做一个Proxy
CoreDNS的安装使用参考我的另外一篇文章
使用CoreDNS作为你的内网DNS服务器
可用CoreDNS配置文件参考
/etc/coredns/Corefile

# kubernetes设置
cluster.local:53 {
  # kube-dns
  forward . 10.3.254.107:53
  log
  errors
  #debug
}

# 默认设置
.:53 {
  # 先走本机的hosts
  # https://coredns.io/plugins/hosts/
  hosts {
    # 自定义sms.service search.service 的解析
    # 因为解析的域名少我们这里直接用hosts插件即可完成需求
    # 如果有大量自定义域名解析那么建议用file插件使用 符合RFC 1035规范的DNS解析配置文件
    #10.6.6.2 servicename
    # ttl
    ttl 60
    # 重载hosts配置
    reload 1m
    # 继续执行
    fallthrough
  }

  # file enables serving zone data from an RFC 1035-style master file.
  # https://coredns.io/plugins/file/
  # file service.signed service
  # 最后所有的都转发到系统配置的上游dns服务器去解析
  forward . /etc/resolv.conf

  # 缓存时间ttl  s
  #cache 6
  # 自动重新加载配置文件的间隔时间
  reload 6s
  # 输出日志
  log
  # 输出错误
  errors
  #debug
}

启动CoreDNS

dig @10.2.1.4 whoami.default.svc.cluster.local
# 测试下确保可以解析

在 开发机2
配置DNS为 Edge Node1 IP 10.2.1.4
配置搜索域为 default.svc.cluster.local

这个2个配置可以在开发办公网络中的DHCP服务器上统一配置

-w564

来测试下DNS解析 curl访问

6.关键实现总结

  • 网络互通1 开发办公内网 <==> 公有云VPC(私有云内网)基础互通
  • 网络互通2 开发办公内网 <==> K8s群集内部Pod和Service网络(通过Edge Node)
  • 打通K8s群集中的DNS (开发办公内网的DNS,设置K8s中KubeDns为上游DNS)
  • 开发机DNS配置 Edge Node DNS 和 搜索域设置default.svc.cluster.local

7.QA

问1:这也太复杂了吧,有没有简单点的?
答:
解决的目标不同

如果只是单纯的能访问k8s中的服务有以下的方式
访问K8s中的服务还有这些方式
telepresence (快速,本地开发面向k8s的微服务)
K8s中装个openvpn 拨入群集内网络
K8s自带的服务暴露方式 NodePort Ingress

这些方法在一个应用有多个服务依赖的时候无法做到让所有开发人员透明的直接通过服务名调用

为了做到对多个开发人员透明,所有人都不需要安装项目依赖的服务,只需要运行项目本身,本项目透明的调用依赖的服务。
所以才有了本文的“复杂”方案

问2:这样直通了暴露K8s群集后岂不是不安全?
答:
是的,但是可以解决,我是这么解决的
K8s群集分为线上和线下实现了隔离
线上为准生产、生产,线下为开发、测试
k8s中可以用命名空间(namespace)来做环境的区分
dev、testing、staging、prod

问题3:开发机中DNS用K8s的DNS作为上游后网站的CDN乱解析了!
答:
开发办公网络和公有云的网络运营商和地理位置都不同,
也就是如果网络出口不一样这会导致CDN解析的IP是错的

需要在开发办公网络也部署一个DNS Server成为二级DNS
开发办公网络 开发机设置DNS为这个二级DNS
cluster.local转发到 Edge Node DNS上
其他的走本地默认的DNS
同样采用CoreDNS,配置文件参考

cluster.local:53 {
  # Edge Node DNS
  forward . 10.2.1.4:53
  log
  errors
  #debug
}
.:53 {
   ....
}

私有云或者自己在开发办公网络部署的K8s群集,因为是同一个网络出口那么网站的DNS解析应该不会有问题