首页 文章 精选 留言 我的

精选列表

搜索[提高],共10000篇文章
优秀的个人博客,低调大师

物联网安全性正在不断提高 但还远远不够安全

联网设备现在已经越来越成为一种常见的科技产品。在过去的几年,通常只有电脑、平板电脑和智能手机才能接入网络,而随着传感器和芯片的成本越来越低,任何产品现在都可以变成一种联网设备,包括家用电器在内的许多产品都开始融入了各种各样的新特性。 通过各种传感器,接入网络后的产品能够具有各种各样的新奇功能。但是,围绕着物联网产品的壮大,隐私与安全问题自然而然的又出现在了我们的面前。毕竟没有人愿意别人能够看到自己办公室或家里的网络信息。 消费者的担心 消费者最大的担心,就是一些联网产品,比如汽车或者冰箱,可能会变成安全漏洞,让不法分子进入你的网络。这些不法分子通过进入你的网络当中的设备,如存有个人信息的计算机,便有可能暴露你的个人数据。 要让物联网变得安全, 其实有很多的方法。而最重要的就是在芯片中置入某种防火墙或安全措施,并在设备上置入附加传感器。冰箱能告知主人今天需要开会,这听起来挺方便的。但我认为没有人会因为懒得打开手机里的日历行程,而愿意把冰箱变成自家网络中一个巨大的安全漏洞。 操作过程有漏洞 物联网传感器、设备以及你的网络之间的连接将会被加密,从而保护这些数据,但这可能是不够的。在拉斯维加斯国际黑客大会中,一款三星联网冰箱被攻击,研究人员发现它的安保功能是不够的。特别是这款三星RF28HMELBSR智能冰箱用的是SSL加密通讯协议,但它并不能核实认证是否有效。这样,黑客就能变成中间人攻击窃取数据。 使用这款具有联网功能的冰箱,用户很方便就能知道自己的谷歌日程,还有其他东西。但这样黑客就可能通过冰箱窃取用户的密码和安全令牌,进而窃取另一个更有用的目标:用户的Gmail帐号,黑客动动手指,就可能窃取到很多个人和商务的信息了。 进步 未来几年,物联网产品公司一定要更多地去关注产品的安保问题。冰箱被黑客入侵,这听起来好像有点微不足道,但如果因此暴露了你的邮箱帐号,暴露了纳税申报单,你的个人信息有可能被盗,那最后就有可能酿成大祸。 物联网产品制造商需要把每一个产品都当成有可能被入侵的接入点,并建立强健的安保功能。此外,这些功能在产品投放入市场之前就必须进行广泛的测试,而不是产品投放以后才进行测试。我们不能阻止物联网的萌芽,我们所希望的仅仅是制造商们能认真思考安保方面的问题,因为这些产品,就像车和冰箱,都有可能成为小偷进入你家网络的门。如果制造商们只制造支持物联网的芯片和设备,而不考虑任何安全问题,那么顾客们可能会因为考虑到安全问题而不购买联网产品。能保证安全的物联网产品是个好东西,但没有严格执行安全功能的物联网产品,就有可能产生意外。 美国联邦贸易委员会已经在尽力促进物联网产品制造商更关注安保问题。而作为消费者,随着越来越多的物联网设备成为主流,我们需要向制造商要求更多的安全责任。但是,物联网产品早期使用者,比如我,却打开了这个漏洞,但这也是我们自己选择的。而对于那些仍旧持观望态度的消费者来说,我会建议等到大部分物联网产品都成熟以后再选择购买。 作者:何妍 来源:51CTO

优秀的个人博客,低调大师

《UX最佳实践:提高用户体验影响力的艺术 》一2.4 项目阶段

2.4 项目阶段 项目要经历三个阶段,如图2-4所示。在第一个阶段,我们有意将开发流程与UCD流程分开进行。解决方案管理团队负责通过外部驱动研究方法定义目标解决方案,与此同时,开发团队并行开发技术平台。从一开始,ByDesign用户体验团队就同时协助解决方案管理团队和负责开发技术平台的UI架构团队。用户体验设计师与解决方案经理紧密合作,一起定义应用程序的目标设计。同时,我们也必须确保在UI架构中能完美地实现出UI模式,撰写详尽的UI规范文档,并在开发阶段协助框架开发人员。在第二阶段,我们得让已实现的解决方案更接近目标设计。等到了第三阶段,我们就开始转入精益开发(LEAN)与敏捷开发(agile development)模式。刚开始时,我们并没有明确地区分每个阶段。但随着经验的积累,情况逐渐清晰起来。每个阶段我们都学到许多技术、流程和

优秀的个人博客,低调大师

Android Go 使用重新设计的“Recents”视图以提高效率

除了特别优化的应用程序,如 YouTube Go,Android Go 将采用全新的 Recents UI,以实现更快速,更节能的切换。 今年 Google I/O 最令人兴奋的消息之一就是推出了 Android Go。Android Go 类似于 Android One,但不是用来取代它的。具有 1GB 或更小内存的 Android 智能手机可在 Android One 上运行,Android One拥有完全修改的系统,可提供显着的节能效果和低数据消耗。 现在我们来看看 Android Go 的部分 UI 设计。谷歌已经对系统用户界面进行了一些重要更改,特别是 Recents 视图,以便于改进延迟并减少内存占用。在设计上,使用 Android Go,不会再看到最近使用过的应用程序的堆叠卡片视图;相反,将会看到完整的预览卡片视图,它足够大且可以占据整个屏幕: 谷歌表示,95% 的入门级设备用户不会进行大量的切换,通常在四个最近使用的 APP 之间来回切换。事实上,对于耗电量大的用户来说,在 Recents 视图中使用几乎全屏卡片视图的效果肯定比在多个应用程序之间来回切换时使用堆叠卡片视图的效率更低。但这对于入门级设备用户来说可能并不那么麻烦;其实这更有意义。毕竟,这个 UI 的更改能够使具有 1GB 甚至 512MB 内存的设备进行快速和流畅的切换。 你对 Android Go 有什么想法?喜欢新的 Recents UI 吗?在评论区分享你的想法吧~~ 本文来自开源中国社区 [http://www.oschina.net]

优秀的个人博客,低调大师

京东云开发者|提高IT运维效率,深度解读京东云AIOps落地实践

基于深度学习对运维时序指标进行异常检测,快速发现线上业务问题 时间序列的异常检测是实际应用中的一个关键问题,尤其是在 IT 行业。我们没有采用传统的基于阈值的方法来实现异常检测,而是通过深度学习提出了一种无阈值方法:基于 LSTM 网络的基线(一个 LSTM 框架辅助几个优化步骤)和无监督检测(神经网络和多种机器学习算法的组合)协同综合分析时间序列。当时间序列显示出清晰的周期性形态的情况下基线表现良好,而无监督检测在效率要求高且周期性不太清晰的情况下表现出色。通过两个并行模块的互补设计,可以在不依赖阈值设定和调整的情况下实现无阈值异常检测。京东云内部实践证明,我们所提出的无阈值方法获得了准确的预测和可靠的检测。 在过去的几年中,aiops业界提出了各种解决异常检测问题的方法。机器学习 (ML) 和深度学习 (DL) 颇受欢迎。在传统的 ML 中,通常采用 K-means、基于密度的空间聚类和隔离森林 (IForest)等聚类方法。除了 ML,由于其强大的逼近能力,使用深度神经网络 (DNN) 进行时间序列预测和异常检测被越来越多的算法同学使用。多层感知器 (MLP) 是一种基本的 DNN 架构,用于评估时间序列上异常检测的性能。此外,循环神经网络 (RNN) 及其变体,如长短期记忆 (LSTM) 网络和门控循环单元 (GRU) 是解决与时间序列相关的问题的常用方法。 对于大多数上述用于解决异常检测的方法,一般是时间序列是否超出预定义的上限和下限。然而,固定阈值无法表征具有内在动态趋势变化的时间序列,从而导致异常分析不准确。此外,由于单个阈值无法涵盖所有​异常情况,因此该方法也容易遗漏异常。此外,设置上限和下限的过程是一项复杂且重要的任务,总是需要为各种情况定义新的阈值,耗时长且迁移性差。 为了解决上述问题,我们介绍一种新方法,即通过 DL 进行无阈值异常检测。 我们的方法不需要预定义上限和下限,而是通过抽取一些易于调整的参数,在小范围内自动搜索适配不同场景的监控数据,进而实现无阈值异常检测:基于 LSTM 网络的基线模块(LnB)和无监督检测模块(UnD)。具体来说,LnB 生成基线,该基线能够以自适应和自动的方式表征时间序列的动态特征。 LnB 的框架是用 LSTM 网络构建的,长短周期识别方法是此框架的贡献之一,它引入了一种纠正机制,可以实现更准确的拟合,生成的基线描述了检测到的时间序列的主要特征,提供了替代传统阈值的限制。 UnD是一种DL和多种ML算法的合并模型,基于投票机制从各个角度检测到的时间序列是否正常。两个模块中的任何一个检测到异常表明发生了异常。两个模块的融合使我们所提出的方法能够以互补和全面的方式有效地分析具有不确定性或各种周期性的时间序列。 时间序列X=(x1, x2, ..., xt),我们的目标是确定下一步 xt+1 的值是否异常。历史值有助于模型学习指标当前和未来的状态,但与预测值距离越近的点对模型预测的影响越大。因此,我们选择使用时间序列 Xt-T:t 的序列,而不是取时间序列的单个步长或整个历史序列来进行异常检测。 T 是选择作为模型训练输入的序列长度。下图1为无阈值异常检测的总体框架包括两个阶段,即训练过程和在线检测。 LnB 和 UnD两个模块都可以单独完成异常检测。但是两个模块有不同的擅长方面,每个模块的结构差异为检测到的时间序列提供了不同维度的检测结果。其中,LnB将更长时期的历史数据输入到模块中,它可以很好地说明特定时间序列的长期行为,但是 LnB 对那些周期性不明确的指标的异常检测能力较弱。相反,UnD 从一个 DL 模型和多个 ML 模型中获得投票结果,对具有不确定性或各种周期性的时间序列具有更强的鲁棒性。此外,UnD 在输入的检测指标的历史数据不足的情况下提供了更合理的检测。 图 1 中的实线箭头表示前向流,而虚线箭头表示反向传播训练。在得到每个模块的检测结果后,根据为每个模块设置的损失函数分别对LnB和UnD进行反向传播训练,LnB和UnD都进行更新,即模型训练。在模型训练之后,LnB 学习生成一个自适应基线,同时,LnB为UnD赋予 基于无监督学习预测未来异常状态的能力。 在线检测不需要训练步骤,所以按入参格式输入时间序列,可直接得到检测结果。这训练和在线检测两个模块的详细介绍如下: 其中 f 表示模型学习所采用的网络,Ti 表示第 i 天的数据。 在训练阶段,a 和 b 会及时随着传入的指标数据自动更新,形成可适应的基线。在测试状态下,y ' final 是我们的最终预测。长短周期识别的重点是引入校正项,为历史上最有价值的“记忆”赋予更多的权重。 如上所述,有两种方法用于识别长短周期,即峰值检测和 SBD距离计算。每天的峰值数量、每天的峰值最大值以及每天第一和第二个最大值的残差是用于识别长短周期,除了这种峰值检查,SBD 是识别长短周期的替代方法。假设我们有两个输入序列 X 和 Y(在我们的例子中,14 天的数据被平均分成两部分)。两个序列的SBD结果可以根据以下等式计算, 其中 SBD 的范围从 0 到 2,在我们的案例中 s=0。 SBD 越小,说明两个序列属于同一周期的相似度越高。 最佳开始时间通过寻找不同时间粒度(如10s 和 1min)下的最佳开始时间来关注拟合精度。待检测的时间序列总是遵循一定的周期性,但根据我们的实验验证,在不同位置选择的开始时间可能会导致拟合精度不同。我们选取均方根误差 (RMSE) 用作优化搜索过程的目标函数: 其中 y' 代表预测结果,而 y 代表基本事实。 k 表示检测到的序列中的第 k 个起始位置。采用L-BFGS通过最小化目标函数实现自动搜索。 基线生成 LnB 的核心过程是基线生成。与RNN相比,LSTM包含了三个门,即遗忘门、输入门和输出门,这种门设计在识别历史中的重要信息方面表现出更好的性能,减轻了对远程历史的依赖和梯度消失。输入数据经 LSTM,输出理论上暗示了正常数据的期望。因此,我们将损失函数训练为: 通过减少实际值和预测值之间的误差,网络可以学习预测时间序列的正常行为。 我们选择 95% 置信区间,计算基线的上限和下限: LnB 的最后一步是自适应调整,这是实现“自适应”的关键步骤。通过 LSTM 获得的上限和下限是初始基线。然后通过极值点平滑和插值修改初始基线。即初始基线中的所有峰点和谷点都形成了初始上限和下限。然后采用拉格朗日插值进行细粒度数据填充以形成平滑的基线。 DL (GRU) 和 ML(IForest、基于角度的异常值检测-ABOD 和基于集群的局部异常值因子-CBLOF)从多个级别检测异常,不需要标签信息或阈值定义。作为回归任务,GRU 学习给定时间序列的正态分布并输出对未来的预测。与 LSTM 从长期历史中捕捉内在特征的能力相比,GRU 在数据量不足且需要效率的情况下理论上表现良好。与 LnB 不同,UnD 将较短的序列作为输入。因此,UnD 中的 GRU 单元是 LnB 的补充。另一方面,IForest、ABOD 和 CBLOF 是用于异常检测的三种基于 ML 的聚类算法。 UnD的最终检测是GRU、IForest、ABOD和CBLOF通过投票方案的合并结果。 对于 GRU,我们采用与 LnB 相同的损失函数。区别在于输入长度(在下一节中解释)。训练有素的 GRU 会给出预测的准确值 y'。在这里,定义异常权重 (AW) 以确定预测是否异常。 AW 是异常识别的关键决定因素,并且根据经验知识自动学习以满足在我们的案例中检测到的异常百分比应在 1%-3% 以内的条件。当涉及到不同的领域或数据集时,也可以根据经验知识确定 AW。 IForest、ABOD和CBLOF是常用的异常值检测方法,它们的输出结果可以看作是一个描述异常概率的分数。然后将所有 GRU、IForest、ABOD 和 CBLOF 的检测结果编码并拼接成一个 one-hot 矩阵,其中 0 表示正常,1 表示异常,如图 3 所示的示例。接下来,我们得到每个时间步对应的“1”的总数。通过与投票数 n (在我们的例子中 n = 2)的比较,如果“1”的总数不小于 n ,则合并结果被检测为异常,反之亦然。 n 是一个参数,需要通过几个简单的试验来确定,例如逐渐增加值或缩小范围。 通过投票方案的合并结果可以从不同方面揭示内在特征,因为 GRU 的回归结果包含显示增加或减少趋势的精确值,而 ML 结果呈现 0 或 1 仅表示异常与否,但具有更准确的决策,因为这些模型可以利用从附加维度或测量中捕获的信息(例如,基于角度视角的 ABOD 和基于概率视角的 CBLOF)。因此,多个高级算法的合并结果可以充分利用给定的数据进行全面的预测。 我们的模型主要有三个步骤,详细介绍如下: 第一步:数据预处理 LnB和UnD对数据拆分和连接有不同的要求,两个模块的输入数据是不同的。例如,当前时间为 t,时间序列的周期性为 T(如 7 天)。我们的目标是检测 t+1 时刻的值是否异常。在这种情况下,LnB 的输入是过去 2*T 周期(即 14 天)收集的历史数据。选择 2*T 周期的原因是 14 天之前的历史数据重要性较低,如果只收集一个周期的数据,可能会受到异常事件的影响。相反,UnD采用最相关的信息而不是使用长历史,并且选择三个滑动窗口覆盖的序列作为输入数据。三个窗口的长度分别为 30 分钟、60 分钟和 60 分钟。 从图4可以看出,UnD输入中有3个段串联,即[Xt-30min:t, Xt+1-1day-30min:t+1- 1day+30min, Xt+1-7days-30min: t+1-7 天+30 分钟]。这种连接提供了一种新的输入结构设计,为特征学习和未来预测提供了最相关的信息。总之,LnB 将过去 14 天的序列作为输入,而 UnD 将过去 30 分钟、1 天前的 60 分钟和 7 天前的 60 分钟作为输入。 数据填充采用 K-NN 作为数据填充方法,以确保所有输入样本的长度相同且可读。数据过滤为保证输入数据的有效性,对输入数据进行平滑过滤,以消除因噪声引起的毛刺。数据转换对训练结果和快速收敛非常重要。在输入训练过程之前,原始数据还需要一个转换过程,包括归一化和对数转换,如(7)所示。 归一化避免了不同维度的副作用,有利于模型快速收敛。此外,它还确保输出不会超过输入的最小值和最大值,因为在输出上实施了指数变换。同时,对数变换可以在不改变数据特征和数据相关性的情况下,减轻方差,平滑变化。 第二步:模型训练 如图 1 中的流程图所示,LnB 和 UnD 都是根据训练数据分别训练的。但是,如上所述,两个模块的输入是不同的。 LnB 将较长的历史数据作为输入,并尝试捕获检测到的时间序列的丰富信息,而 UnD 将最相关但较短的序列部署为训练数据。 UnD 中的GRU和 LnB通过减少第二部分中介绍的损失函数来学习检测到的序列的正常行为。同时,IForest、ABOD 和 CBLOF 学习了无监督聚类模型。上述单元的所有输出都是一步超前的异常检测。 第三步:在线异常检测 在运行时,传入的数据首先进入预处理模块,然后同时进入LnB和UnD。输入数据的格式应与训练阶段一致。如果两个模块的任一结果异常,则提示待检测数据异常。LnB和UnD的融合机制对时间序列进行了全面的检测,降低了潜在异常遗漏的概率。另一方面,LnB 中较长的历史输入和 UnD 中的多模型投票方案有效地避免了将正常的误认为是异常的。 经过京东内部多场景多组数据验证,模型在线上运行的效果评估如下表所示: 此外,可以灵活选择“and”或“or”来整合LnB和UnD的结果。没有统一的规则,要看实际场景的需求。在我们的落地实践场景中,这两个数据集都需要保证召回率,因此我们采取“或”操作,这意味着无论哪个检测到异常都会报警。如果需要较低的警告级别,我们可以选择“和”作为积分运算。选择三个流行的基线 IForest、ABOD 和 CBLOF 进行比较。此外。我们还比较了我们的方法和单独使用 LnB 或 UnD 的方法的结果,如上表所示。从定量比较中,很明显,所提出的方法,即 LnB+UnD 在两者中都获得了最高的 F1 分数数据集。 LnB+UnD 的组合比单独采用 LnB 或 UnD 效果更好。而且我们的模型优于其他三个基线,这也证明了我们并行机制的有效性和必要性。 我们提出的一种用于时间序列分析的无阈值异常检测方法,即 LSTM 构建的 LnB和DL、ML 模型融合机制构建的 UnD,以互补和智能的方式实现异常检测。在具有不同长短周期和变化趋势的真实实践场景的两个数据集上进行了实验,比较结果证明了我们方法的有效性和准确性。 Threshold-free Anomaly Detection for Streaming Time Series through Deep Learning. ICMLA. ieeexplore检索:https://ieeexplore.ieee.org/abstract/document/9680175 作者:张静

优秀的个人博客,低调大师

Java 引入预览版虚拟线程(协程)功能,大幅提高应用吞吐量

OpenJDK 的 JEP 425 :虚拟线程(预览版)功能提案显示: Java 平台将引入虚拟线程特性(期待已久的协程)。虚拟线程是轻量级线程,可显著地减少编写、维护和观察高吞吐量并发应用程序的工作量。 Java 开发人员一直依赖线程作为并发服务器应用程序的构建块,每个方法中的语句都在一个线程内执行,每个线程提供一个堆栈来存储局部变量和协调方法调用,以及报错时的上下文捕获。线程是 Java 的并发单元,也是 Java 工具的核心基础:调试器逐步执行线程方法中的语句,分析器则可视化多个线程的行为。 目前,JDK 将其平台线程实现为操作系统 (OS) 线程的包装器,JDK 中每个实例都是一个平台线程,平台线程在底层操作系统线程上运行 Java 代码 ,并在代码的整个生命周期内捕获 OS 线程。平台线程数受限于 OS 线程数,而OS 线程的成本很高,不能占用太多。因此,目前 JDK 的这种线程实现方法限制了其应用程序的吞吐量,使吞吐量远低于硬件支持的水平。 关于虚拟线程 虚拟线程java.lang.Thread是在底层操作系统线程(OS 线程)上运行 Java 代码,但在代码的整个生命周期内不捕获 OS 线程的实例。这意味着许多虚拟线程可以在同一个 OS 线程上运行 Java 代码,从而有效地共享它。 虚拟线程是由 JDK 而不是操作系统提供的线程的轻量级实现,也是用户模式线程的一种形式。用户模式线程在 Java 的早期版本中被称为“绿色线程”,当时操作系统线程的概念还不够成熟和普及, Java 的所有绿色线程都共享一个 OS 线程(M:1 调度),随着线程概念的发展,绿色线程最终被现在的平台线程超越,实现为 OS 线程的包装器(1:1 调度),而最新引入的虚拟线程采用 M:N 调度,其中大量 (M) 虚拟线程被调度为在较少数量 (N) 的 OS 线程上运行。 更高的吞吐量 开发者可以选择使用虚拟线程还是平台线程,但虚拟线程在高吞吐量的服务器应用程序中表现更好。比如下面这段休眠一秒钟的代码就创建了大量的虚拟线程,程序首先获得一个ExecutorService,它为每个提交的任务创建一个新的虚拟线程,然后提交 10000 个任务并等待所有任务完成:: try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i -> { executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return i; }); }); } // executor.close() is called implicitly, and waits 现代硬件可以很容易地支持 10000 个虚拟线程同时运行这样的代码。如果该程序使用为每个任务都创建一个新平台线程的 ExecutorService,例如 Executors.newCachedThreadPool() ,那么它将尝试创建 10000 个平台线程,也就意味着 10000 个 OS 线程,那么这个程序在大多数操作系统上都会崩溃。又或者这个程序使用从池中获取平台线程的 ExecutorService,如 Executors.newFixedThreadPool(200),也好不到哪去。 ExecutorService 将创建 200 个平台线程供这 10000 个任务共享,任务将按顺序运行而不是同时运行,程序需要很长时间才能跑完。 对于上述程序来说,具有 200 个平台线程的池只能实现每秒 200 个任务的吞吐量,而虚拟线程可以实现大约每秒 10000 个任务的吞吐量(在充分预热之后)。此外,如果将示例程序中的 10000 更改为 1,000,000 ,则程序将提交 1,000,000 个任务,创建 1,000,000 个并发运行的虚拟线程,并且(在充分预热后)达到大约 1,000,000 个任务/秒的吞吐量。 总而言之,虚拟线程不是更快的线程 —— 它们运行代码的速度并不比平台线程快。它们的存在是为了提供规模(更高的吞吐量),而不是速度(更低的延迟)。 如何启用虚拟线程? 目前虚拟线程在其他多线程语言中被广泛使用(例如 Go 中的协程 和 Erlang 中的进程,在 C++ 中也是一个稳定特性),但在 Java 中还是一个预览 API,默认禁用。如要在 JDK XX 上尝试该功能,则必须通过以下方法启用预览 API: 使用 javac --release XX --enable-preview Main.java 编译程序,并使用 java --enable-preview Main 运行 使用源代码启动器时,使用 java --release XX --enable-preview Main.java 运行程序 使用 jshell 时,用 jshell --enable-preview 启动 有关虚拟线程的更多信息可在 OpenJDK 的JDK Issue-8277131中查看,目前该提案于 2021/11/15 创立,目前还处于 JEP 流程的第一阶段,距离稳定版本还需要一段时间。

优秀的个人博客,低调大师

提高运维效率,那就把MySQL数据库部署到Kubernetes 集群中

摘要:Kubernetes 很多看起来比较“繁琐”的设计的主要目的,都是希望为开发者提供更多的“可扩展性”,给使用者带来更多的“稳定性”和“安全感”。 本文分享自华为云社区《如何在 Kubernetes 集群中搭建一个复杂的 MySQL 数据库?》,作者:zuozewei 。 前言 实际生产环境中,为了稳定和高可用,运维团队一般不会把 MySQL 数据库部署在 Kubernetes 集群中,一般是用云厂商的数据库或者自己在高性能机器(如裸金属服务器)上搭建。 但是,对于测试开发环境,我们完全可以把 MySQL 部署到各自的 Kubernetes 集群中,非常有助于提升运维效率,而且还有助于Kubernetes 使用的经验积累。 简易部署 ​如下所示,我们仅需设置 root 用户密码(环境变量 MYSQL_ROOT_PASSWORD), 便可轻松的使用 MySQL 官方镜像构建一个 MySQL 数据库。 apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: mysql-min name: mysql-min spec: replicas: 1 selector: matchLabels: app: mysql-min template: metadata: labels: app: mysql-min spec: containers: - image: centos/mysql-57-centos7:latest name: mysql-min imagePullPolicy: IfNotPresent env: - name: MYSQL_ROOT_PASSWORD value: admin@123 ​创建一 Service 以便集群内外均可访问数据库,其中集群外需通过 nodePort 设置的 30336 端口访问。 apiVersion: v1 kind: Service metadata: labels: app: mysql-min release: mysql-min name: mysql-min namespace: default spec: ports: - name: mysql port: 3306 protocol: TCP nodePort: 30336 targetPort: mysql selector: app: mysql-min #目前sessionAffinity可以提供"None""ClientIP"两种设定: #None: 以round robin的方式轮询下面的Pods。 #ClientIP: 以client ip的方式固定request到同一台机器。 sessionAffinity: None type: NodePort #status: # loadBalancer: {} 接着,访问数据库并验证其运行正常: # kubectl get pod # 当前Pod名称 NAME READY STATUS RESTARTS AGE mysql-min-5b5668c448-t44ml 1/1 Running 0 3h # 通过本机访问 # kubectl exec -it mysql-min-5b5668c448-t44ml -- mysql -uroot -padmin@123 mysql> select 1; +---+ | 1 | +---+ | 1 | +---+ # 集群内部通过mysql service访问: # kubectl exec -it mysql-min-5b5668c448-t44ml -- mysql -uroot -padmin@123 -hmysql mysql> select now(); +---------------------+ | now() | +---------------------+ | 2021-03-13 07:19:14 | +---------------------+ # 集群外部,可通过任何一个 K8S 节点访问数据库: # mysql -uroot -padmin@123 -hworker-1 -P30336 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 扩展部署 持久化存储 若要确保 MySQL 重启后数据仍然存在,我们需为其配置可持久化存储,我这里的实验环境使用的是 Local Persistent Volume,也就是说,我希望 Kubernetes 能够直接使用宿主机上的本地磁盘目录,而不依赖于远程存储服务,来提供“持久化”的容器 Volume。这样做的好处很明显,由于这个 Volume 直接使用的是本地磁盘,尤其是 SSD 盘,它的读写性能相比于大多数远程存储来说,要好得多。这个需求对本地物理服务器部署的私有 Kubernetes 集群来说,非常常见。 值得指出的是其次,相比于正常的 PV,一旦这些节点宕机且不能恢复时,本地存储 Volume 的数据就可能丢失。这就要求使用 其的应用必须具备数据备份和恢复的能力,允许你把这些数据定时备份在其他位置。 不难想象, Local Persistent Volume 的设计,主要面临两个难点。 第一个难点在于:如何把本地磁盘抽象成 PV。 可能你会说,Local Persistent Volume 不就等同于 hostPath 加 NodeAffinity 吗? 比如,一个 Pod 可以声明使用类型为 Local 的 PV,而这个 PV 其实就是一个 hostPath 类型的 Volume。如果这个 hostPath 对应的目录,已经在节点 A 上被事先创建好了。那么,我只需要再给这个 Pod 加上一个 nodeAffinity=nodeA,不就可以使用这个 Volume 了吗? 事实上,你绝不应该把一个宿主机上的目录当作 PV 使用。这是因为,这种本地目录的存储行为完全不可控,它所在的磁盘随时都可能被应用写满,甚至造成整个宿主机宕机。而且,不同的本地目录之间也缺乏哪怕最基础的 I/O 隔离机制。 所以,一个 本地存储 Volume 对应的存储介质,一定是一块额外挂载在宿主机的磁盘或者块设备(“额外”的意思是,它不应该是宿主机根目录所使用的主硬盘)。这个原则,我们可以称为“一个 PV 一块盘”。 第二个难点在于:调度器如何保证 Pod 始终能被正确地调度到它所请求的本地 Volume 所在的节点上呢? 造成这个问题的原因在于,对于常规的 PV 来说,Kubernetes 都是先调度 Pod 到某个节点上,然后,再通过“两阶段处理”来“持久化”这台机器上的 Volume 目录,进而完成 Volume 目录与容器的绑定挂载。 可是,对于 Local PV 来说,节点上可供使用的磁盘(或者块设备),必须是运维人员提前准备好的。它们在不同节点上的挂载情况可以完全不同,甚至有的节点可以没这种磁盘。 所以,这时候,调度器就必须能够知道所有节点与 Local Persistent Volume 对应的磁盘的关联关系,然后根据这个信息来调度 Pod。 这个原则,我们可以称为“在调度的时候考虑 Volume 分布”。在 Kubernetes 的调度器里,有一个叫作 VolumeBindingChecker 的过滤条件专门负责这个事情。在 Kubernetes v1.11 中,这个过滤条件已经默认开启了。 基于上述讲述,在开始使用 Local Persistent Volume 之前,你首先需要在集群里配置好磁盘或者块设备。在公有云上,这个操作等同于给虚拟机额外挂载一个磁盘,比如 GCE 的 Local SSD 类型的磁盘就是一个典型例子。 而在我们部署的私有环境中,你有两种办法来完成这个步骤。 第一种,当然就是给你的宿主机挂载并格式化一个可用的本地磁盘,这也是最常规的操作; 第二种,对于实验环境,你其实可以在宿主机上挂载几个 RAM Disk(内存盘)来模拟本地磁盘。 接下来,我会使用第二种方法,在我们之前部署的 Kubernetes 集群上进行实践。首先,在名叫 node-1 的宿主机上创建一个挂载点,比如 /mnt/disks;然后,用几个 RAM Disk 来模拟本地磁盘,如下所示: # 在node-1上执行 $ mkdir /mnt/disks $ for vol in vol1 vol2 vol3; do mkdir /mnt/disks/$vol mount -t tmpfs $vol /mnt/disks/$vol done 需要注意的是,如果你希望其他节点也能支持 Local Persistent Volume 的话,那就需要为它们也执行上述操作,并且确保这些磁盘的名字(vol1、vol2 等)都不重复。接下来,我们就可以为这些本地磁盘定义对应的 PV 了,如下所示: apiVersion: v1 kind: PersistentVolume metadata: name: mysql-min-pv-local namespace: default spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce storageClassName: "mysql-min-storageclass-local" persistentVolumeReclaimPolicy: Retain #表示使用本地存储 local: path: /mnt/disks/vol1 #使用local pv时必须定义nodeAffinity,Kubernetes Scheduler需要使用PV的nodeAffinity描述信息来保证Pod能够调度到有对应local volume的Node上。 #创建local PV之前,你需要先保证有对应的storageClass已经创建。 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: # pod 需要分不到的主机名,这台主机上开启了 local-pv 资源。 - node-1 可以看到,这个 PV 的定义里:local 字段,指定了它是一个 Local Persistent Volume;而 path 字段,指定的正是这个 PV 对应的本地磁盘的路径,即:/mnt/disks/vol1。 当然了,这也就意味着如果 Pod 要想使用这个 PV,那它就必须运行在 node-1 上。所以,在这个 PV 的定义里,需要有一个 nodeAffinity 字段指定 node-1 这个节点的名字。这样,调度器在调度 Pod 的时候,就能够知道一个 PV 与节点的对应关系,从而做出正确的选择。这正是 Kubernetes 实现“在调度的时候就考虑 Volume 分布”的主要方法。 接下来要创建一个 StorageClass 来描述这个 PV,如下所示: kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: mysql-min-storageclass-local #指定存储类的供应者,比如aws, nfs等,具体取值参考官方说明。 #存储类有一个供应者的参数域,此参数域决定PV使用什么存储卷插件。参数必需进行设置 #由于demo中使用的是本地存储,所以这里写kubernetes.io/no-provisioner. provisioner: kubernetes.io/no-provisioner #volumeBindingMode 参数将延迟PVC绑定,直到 pod 被调度。 volumeBindingMode: WaitForFirstConsumer 这个 StorageClass 的名字,叫作 local-storage。需要注意的是,在它的 provisioner 字段,我们指定的是 no-provisioner。这是因为 Local Persistent Volume 目前尚不支持 Dynamic Provisioning,所以它没办法在用户创建 PVC 的时候,就自动创建出对应的 PV。也就是说,我们前面创建 PV 的操作,是不可以省略的。 与此同时,这个 StorageClass 还定义了一个 volumeBindingMode=WaitForFirstConsumer 的属性。它是 Local Persistent Volume 里一个非常重要的特性,即:延迟绑定。 通过这个延迟绑定机制,原本实时发生的 PVC 和 PV 的绑定过程,就被延迟到了 Pod 第一次调度的时候在调度器中进行,从而保证了这个绑定结果不会影响 Pod 的正常调度。 接下来,我们只需要定义一个非常普通的 PVC,就可以让 Pod 使用到上面定义好的 Local Persistent Volume 了,如下所示: apiVersion: v1 items: - apiVersion: v1 kind: PersistentVolumeClaim metadata: #当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用。 #可以看到,当 PVC 的状态为 Teminatiing 时,PVC 受到保护,Finalizers 列表中包含 kubernetes.io/pvc-protection: finalizers: - kubernetes.io/pvc-protection labels: app: mysql-min release: mysql-min name: mysql-min namespace: default spec: #PV 的访问模式(accessModes)有三种: #ReadWriteOnce(RWO):是最基本的方式,可读可写,但只支持被单个 Pod 挂载。 #ReadOnlyMany(ROX):可以以只读的方式被多个 Pod 挂载。 #ReadWriteMany(RWX):这种存储可以以读写的方式被多个 Pod 共享。 accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: mysql-min-storageclass-local #表示使用本地磁盘,实际生产中一般都使用nfs。 volumeMode: Filesystem volumeName: mysql-min-pv-local # status: # accessModes: # - ReadWriteOnce # capacity: # storage: 1Gi kind: List 可以看到,这个 PVC 没有任何特别的地方。唯一需要注意的是,它声明的 storageClassName 是 mysql-min-storageclass-local。所以,将来 Kubernetes 的 Volume Controller 看到这个 PVC 的时候,不会为它进行绑定操作。 ​ 最后,我们创建 Local Persistent Volume 资源文件: kubectl apply -f mysql-min-pv-local.yaml kubectl apply -f mysql-min-storageclass-local.yaml kubectl apply -f mysql-min-pvc.yaml 而后,调整 Deploy 并挂载卷: spec: containers: - image: centos/mysql-57-centos7:latest ... volumeMounts: - name: data mountPath: /var/lib/mysql volumes: - name: data persistentVolumeClaim: claimName: mysql-min 自定义配置文件 通过创建 configmap 并挂载到容器中,我们可自定义 MySQL 配置文件。如下所示,名为 mysql-config 的 cm 包含一个 my.cnf 文件: apiVersion: v1 kind: ConfigMap metadata: name: mysql-config data: my.cnf: | [mysqld] default_storage_engine=innodb skip_external_locking lower_case_table_names=1 skip_host_cache skip_name_resolve max_connections=2000 innodb_buffer_pool_size=8589934592 init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake query_cache_type=0 innodb_flush_log_at_trx_commit = 0 sync_binlog = 0 query_cache_size = 104857600 slow_query_log =1 slow_query_log_file=/var/lib/mysql/slow-query.log log-error=/var/lib/mysql/mysql.err long_query_time = 0.02 table_open_cache_instances=16 table_open_cache = 6000 skip-grant-tables sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 将 configmap 挂载到容器内: spec: ... containers: - image: centos/mysql-57-centos7:latest ... volumeMounts: - name: mysql-config mountPath: /etc/my.cnf.d/my.cnf subPath: my.cnf ... volumes: - name: mysql-config - name: mysql-config configMap: name: mysql-config ... 设置容器时区 最傻瓜也最方便的处理方式,设置宿主机时区和时间文件与容器的映射。 spec: ... containers: - image: centos/mysql-57-centos7:latest ... volumeMounts: - name: localtime readOnly: true mountPath: /etc/localtime ... volumes: - name: localtime hostPath: type: File path: /etc/localtime ... 加密敏感数据 用户密码等敏感数据以 Secret 加密保存,而后被 Deployment 通过 volume 挂载或环境变量引用。如本例,我们创建root、user用户,将用户的密码加密保存: apiVersion: v1 data: #将mysql数据库的所有user的password配置到secret,统一管理 mysql-password: YWRtaW4= mysql-root-password: OVplTmswRGdoSA== kind: Secret metadata: labels: app: mysql-min release: mysql-min name: mysql-min namespace: default #Secret有三种类型: #Opaque:base64编码格式的Secret,用来存储密码、密钥等;但数据也通过base64 –decode解码得到原始数据,所有加密性很弱。 #kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息。 #kubernetes.io/service-account-token: 用于被serviceaccount引用。serviceaccout创建时Kubernetes会默认创建对应的secret。Pod如果使用了serviceaccount,对应的secret会自动挂载到Pod目录/run/secrets/ kubernetes.io/serviceaccount中。 type: Opaque Secret 创建完成后,我们将用户明文密码从 Deployment 去除,采用环境变量方式引用 Secret 数据,参见如下 Yaml 修改: root 用户及 MYSQL_USER 用户,其密码均通过 secretKeyRef 从 secret 获取。 spec: ... containers: - image: centos/mysql-57-centos7:latest name: mysql-min imagePullPolicy: IfNotPresent env: #password存储在secret中 - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: key: mysql-root-password name: mysql-min - name: MYSQL_PASSWORD valueFrom: secretKeyRef: key: mysql-password name: mysql-min - name: MYSQL_USER value: zuozewei 容器健康检查 K8S 镜像控制器可通过 livenessProbe 判断容器是否异常,进而决定是否重建容器;而 Service 服务可通过 readinessProbe 判断容器服务是否正常,从而确保服务可用性。 ​本例配置的 livenessProbe 与 readinessProbe 是一样的,即连续 3 次查询数据库失败,则定义为异常。对 livenessProbe 与readinessProbe 详细用法,不在本文的讨论范围内,可参考 K8S 官方文档: Configure Liveness and Readiness Probes Pod Lifecycle spec: containers: image: centos/mysql-57-centos7:latest ... #kubelet 使用 liveness probe(存活探针)来确定何时重启容器。例如,当应用程序处于运行状态但无法做进一步操作,liveness 探针将捕获到 deadlock,重启处于该状态下的容器,使应用程序在存在 bug 的情况下依然能够继续运行下去 livenessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" failureThreshold: 3 #探测成功后,最少连续探测失败多少次才被认定为失败。默认是 3。最小值是 1。 initialDelaySeconds: 30 #容器启动后第一次执行探测是需要等待多少秒。 periodSeconds: 10 #执行探测的频率。默认是10秒,最小1秒。 successThreshold: 1 #探测失败后,最少连续探测成功多少次才被认定为成功。默认是 1。对于 liveness 必须是 1。最小值是 1。 timeoutSeconds: 5 #探测超时时间。默认1秒,最小1秒。 #Kubelet 使用 readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量。只有当 Pod 中的容器都处于就绪状态时 kubelet 才会认定该 Pod处于就绪状态。该信号的作用是控制哪些 Pod应该作为service的后端。如果 Pod 处于非就绪状态,那么它们将会被从 service 的 load balancer中移除。 readinessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" failureThreshold: 3 initialDelaySeconds: 5 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 容器初始化 容器的一些初始化操作显然适合通过 InitContainer 来完成,这里的 initContainer 是为了保证在 POD 启动前,PV盘 要先行绑定成功,同时为了避免 MySQL 数据库目录内的 lost+found 目录被误认为是数据库,初始化容器中将其删除; #Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同,在下面 资源 处有说明。 而且 Init 容器不支持 Readiness Probe,因为它们必须在 Pod 就绪之前运行完成。 #如果为一个 Pod 指定了多个 Init 容器,那些容器会按顺序一次运行一个。 每个 Init 容器必须运行成功,下一个才能够运行。 当所有的 Init 容器运行完成时,Kubernetes 初始化 Pod 并像平常一样运行应用容器。 #mysql这里的initContainer是为了保证在POD启动前,PV盘要先行绑定成功。 initContainers: - command: - rm - -fr - /var/lib/mysql/lost+found image: busybox:1.29.3 imagePullPolicy: IfNotPresent name: remove-lost-found resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/lib/mysql name: data restartPolicy: Always #scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。 schedulerName: default-scheduler securityContext: {} #如果您的Pod通常需要超过30秒才能关闭,请确保增加优雅终止宽限期。可以通过在Pod YAML中设置terminationGracePeriodSeconds选项来实现. #如果容器在优雅终止宽限期后仍在运行,则会发送SIGKILL信号并强制删除。与此同时,所有的Kubernetes对象也会被清除。 terminationGracePeriodSeconds: 30 #定义数据卷PVC,与PV匹配。 volumes: - name: data persistentVolumeClaim: claimName: mysql-min - name: mysql-config configMap: name: mysql-config - name: localtime hostPath: type: File path: /etc/localtime 完整Deployment 通过如上多步调整,MySQL 数据库的 Deplyment 如下所示: apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: "1" generation: 1 labels: app: mysql-min release: mysql-min name: mysql-min namespace: default spec: replicas: 1 selector: matchLabels: app: mysql-min strategy: rollingUpdate: maxSurge: 1 #滚动升级时会先启动1个pod maxUnavailable: 1 #滚动升级时允许的最大Unavailable的pod个数 type: RollingUpdate #滚动升级 template: metadata: labels: app: mysql-min spec: containers: - env: #password存储在secret中 - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: key: mysql-root-password name: mysql-min - name: MYSQL_PASSWORD valueFrom: secretKeyRef: key: mysql-password name: mysql-min - name: MYSQL_USER value: apollo image: centos/mysql-57-centos7:latest imagePullPolicy: IfNotPresent #kubelet 使用 liveness probe(存活探针)来确定何时重启容器。例如,当应用程序处于运行状态但无法做进一步操作,liveness 探针将捕获到 deadlock,重启处于该状态下的容器,使应用程序在存在 bug 的情况下依然能够继续运行下去 livenessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" failureThreshold: 3 #探测成功后,最少连续探测失败多少次才被认定为失败。默认是 3。最小值是 1。 initialDelaySeconds: 30 #容器启动后第一次执行探测是需要等待多少秒。 periodSeconds: 10 #执行探测的频率。默认是10秒,最小1秒。 successThreshold: 1 #探测失败后,最少连续探测成功多少次才被认定为成功。默认是 1。对于 liveness 必须是 1。最小值是 1。 timeoutSeconds: 5 #探测超时时间。默认1秒,最小1秒。 name: mysql-min ports: - containerPort: 3306 name: mysql protocol: TCP #Kubelet 使用 readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量。只有当 Pod 中的容器都处于就绪状态时 kubelet 才会认定该 Pod处于就绪状态。该信号的作用是控制哪些 Pod应该作为service的后端。如果 Pod 处于非就绪状态,那么它们将会被从 service 的 load balancer中移除。 readinessProbe: exec: command: - /bin/sh - "-c" - MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" - mysql -h 127.0.0.1 -u root -e "SELECT 1" failureThreshold: 3 initialDelaySeconds: 5 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 resources: requests: cpu: 100m memory: 256Mi #为了达到一个相当高水平的实用性,特别是为了积极开发应用,快速调试失败是很重要的。除了一般的日志采集,Kubernetes还能通过查出重大错误原因来加速调试,并在某种程度上通过kubectl或者UI陈列出来。可以指定一个’terminationMessagePath’来让容器写下它的“death rattle“,比如声明失败消息,堆栈跟踪,免责条款等等。默认途径是‘/dev/termination-log’。 terminationMessagePath: /dev/termination-log # 此字段默认为 “File“,这意味着仅从终止消息文件中检索终止消息。 通过将 terminationMessagePolicy 设置为 “FallbackToLogsOnError“,你就可以告诉 Kubernetes,在容器因错误退出时,如果终止消息文件为空,则使用容器日志输出的最后一块作为终止消息。 日志输出限制为 2048 字节或 80 行,以较小者为准。 terminationMessagePolicy: File #要使用的数据盘目录,在initContainer中会关联此处目录。 volumeMounts: - mountPath: /var/lib/mysql name: data - name: mysql-config mountPath: /etc/my.cnf.d/my.cnf subPath: my.cnf - name: localtime readOnly: true mountPath: /etc/localtime dnsPolicy: ClusterFirst #Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。 然而,Init 容器对资源请求和限制的处理稍有不同,在下面 资源 处有说明。 而且 Init 容器不支持 Readiness Probe,因为它们必须在 Pod 就绪之前运行完成。 #如果为一个 Pod 指定了多个 Init 容器,那些容器会按顺序一次运行一个。 每个 Init 容器必须运行成功,下一个才能够运行。 当所有的 Init 容器运行完成时,Kubernetes 初始化 Pod 并像平常一样运行应用容器。 #mysql这里的initContainer是为了保证在POD启动前,PV盘要先行绑定成功。 initContainers: - command: - rm - -fr - /var/lib/mysql/lost+found image: busybox:1.29.3 imagePullPolicy: IfNotPresent name: remove-lost-found resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/lib/mysql name: data restartPolicy: Always #scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。 schedulerName: default-scheduler securityContext: {} #如果您的Pod通常需要超过30秒才能关闭,请确保增加优雅终止宽限期。可以通过在Pod YAML中设置terminationGracePeriodSeconds选项来实现. #如果容器在优雅终止宽限期后仍在运行,则会发送SIGKILL信号并强制删除。与此同时,所有的Kubernetes对象也会被清除。 terminationGracePeriodSeconds: 30 #定义数据卷PVC,与PV匹配。 volumes: - name: data persistentVolumeClaim: claimName: mysql-min - name: mysql-config configMap: name: mysql-config - name: localtime hostPath: type: File path: /etc/localtime 创建此 Deployment 后,我们有如下组件: # kubectl get all,pvc,cm,secret -l app=mysql-min # MySQL pod: NAME READY STATUS RESTARTS AGE pod/mysql-min-f9c9b7b5-q9br4 1/1 Running 6 14d # Service: NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/mysql-min NodePort 10.96.184.130 <none> 3306:30336/TCP 16d # MySQL Deployment: NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/mysql-min 1/1 1 1 16d # 副本集ReplicaSet被Deployment调用,其是自动生成的 NAME DESIRED CURRENT READY AGE replicaset.apps/mysql-min-587cf9fd48 0 0 0 16d replicaset.apps/mysql-min-589bf8cdc5 0 0 0 16d replicaset.apps/mysql-min-6b7447c7dd 0 0 0 14d replicaset.apps/mysql-min-6cc9887459 0 0 0 16d replicaset.apps/mysql-min-7759579d77 0 0 0 16d replicaset.apps/mysql-min-84d4d6bd56 0 0 0 15d replicaset.apps/mysql-min-f9c9b7b5 1 1 1 14d # Pvc: NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/mysql-min Bound mysql-min-pv-local 5Gi RWO mysql-min-storageclass-local 16d # Secret: NAME TYPE DATA AGE secret/mysql-min Opaque 2 16d 定期自动备份 考虑到数据安全性,我们定期备份数据库,在K8S集群中,我们可配置 CronJob 实现自动备份作业。首先,创建一个持久化存储供备份用: apiVersion: v1 items: - apiVersion: v1 kind: PersistentVolumeClaim metadata: #当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用。 #可以看到,当 PVC 的状态为 Teminatiing 时,PVC 受到保护,Finalizers 列表中包含 kubernetes.io/pvc-protection: finalizers: - kubernetes.io/pvc-protection labels: app: mysql-min release: mysql-min name: mysql-min-backup namespace: default spec: #PV 的访问模式(accessModes)有三种: #ReadWriteOnce(RWO):是最基本的方式,可读可写,但只支持被单个 Pod 挂载。 #ReadOnlyMany(ROX):可以以只读的方式被多个 Pod 挂载。 #ReadWriteMany(RWX):这种存储可以以读写的方式被多个 Pod 共享。 accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: mysql-min-storageclass-nfs #表示使用本地磁盘,实际生产中一般都使用nfs。 volumeMode: Filesystem volumeName: mysql-min-pv-local # status: # accessModes: # - ReadWriteOnce # capacity: # storage: 1Gi kind: List 继而,配置实际的自动化作业任务,如下所示,每天凌晨零点点将使用 mysqldump 备份 mall 数据库。 apiVersion: batch/v1beta1 kind: CronJob metadata: name: mysql-backup spec: schedule: "0 0 * * *" jobTemplate: spec: template: spec: containers: - name: mysql-min-backup imagePullPolicy: IfNotPresent image: centos/mysql-57-centos7:latest env: #password存储在secret中 - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: key: mysql-root-password name: mysql-min - name: MYSQL_PASSWORD valueFrom: secretKeyRef: key: mysql-password name: mysql-min - name: MYSQL_HOST value: mysql-min command: - /bin/sh - -c - | set -ex mysqldump --host=$MYSQL_HOST --user=$MYSQL_ROOT_PASSWORD \ --password=$mysql-root-password \ --routines --databases mall --single-transaction \ > /mysql-backup/mysql-`date +"%Y%m%d"`.sql volumeMounts: - name: mysql-min-backup mountPath: /mysql-min-backup restartPolicy: OnFailure volumes: - name: mysql-min-backup persistentVolumeClaim: claimName: mysql-min-backup 小结 ​Kubernetes 很多看起来比较“繁琐”的设计的主要目的,都是希望为开发者提供更多的“可扩展性”,给使用者带来更多的“稳定性”和“安全感”。这两个能力的高低,是衡量开源基础设施项目水平的重要标准。 示例中揉合 Kubernetes 多项技术,构建了一个复杂且可做生产使用的单实例数据库。 本文源码:https://github.com/zuozewei/blog-example/tree/master/Kubernetes/k8s-mysql-pv-local 参考资料: [1]:《深入剖析Kubernetes》 点击关注,第一时间了解华为云新鲜技术~

优秀的个人博客,低调大师

自动化会提高测试覆盖率,那测试覆盖率是什么?

测试覆盖率是什么? 测试覆盖率(test coverage)是2018年公布的计算机科学技术名词,它是测试质量的度量标准之一,告诉我们测试了多少代码。它定义了系统的某些实体,目的是用测试覆盖它们。这是一种用来指示我们什么时候进行了充分的测试,并告诉我们还需要测试什么(从而扩大了覆盖范围)的方法。 如果把测试比喻为打扫房子,那些不常用的房间总会被遗漏,我们总会忘记诸如次卧卫生间这样的区域,所以打扫的覆盖范围不包括就那个卫生间。 想象一下,如果我们的打扫标准只包括打扫卧室。按照这个标准,如果打扫了所有的卧室,就意味着整个房子都是干净的吗? 并非如此,因为我们完全忽略了厨房、餐厅、卫生间等房子内的其他部分!因此,务必要细致精准地度量测试覆盖率。具有一定程度的覆盖率是测试质量的一项指标,但它从来不是系统质量的指标,也不能保证所有内容都经过测试。测试覆盖率告诉我们测试代码的百分比,但这并不意味着它在每种情况下都被测试过。 测试覆盖率有什么优势? 依然是以打扫房屋为例,测试覆盖率可以: ● 度量打扫的质量; ● 指示何时该停止打扫; ● 提醒我们还有其他地方需要清理。 某些标准可能比其他标准更强大。了解它们可以让我们知道测试的深度以及何时应用某个或其他标准。例如,如果涵盖标准A的任何测试用例集TS也涵盖标准B,则可以说标准A包括另一个标准B。 按照上述原则,清洁房屋就可以遵循以下标准: 1. 打扫每个卧室; 2. 打扫房子的每一部分(包括卧室、厨房、卫生间等); 3. 打扫每一个小地方,甚至是角落,因为它们很可能会积聚灰尘。 显而易见,标准3包括2,而2又包括1,关系是传递的,即3包括1。如果我们为标准3设计一个测试用例,它也应该包括前两个标准。对于测试软件,标准通常包括程序中的各种路径、条件、语句、函数等。 另一个真实的例子可能是等价类划分,先划分若干个等价类,然后从每个部分中选取具有代表性的数据当做测试用例进行合理的分类,这样就覆盖了所有类。如果考虑白盒测试,又有语句覆盖率、分支覆盖率、路径覆盖率等,特别是对于状态机,我们有指示覆盖所有节点、所有转换等的标准。 自动化测试如何适用? 现在就可以想象一下扔掉扫帚,换成一个超高速的扫地机器人。它可以解放双手、更快地清理地板、减少被遗漏的区域,还可以让自己在工作中专注于更重要的事情。 至此,我们可以得出结论:测试自动化=更高的测试覆盖率。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册