我与阿里云认证的故事
我是谭位,现在阿里巴巴数据中心从事IT运维工作。我于20年入职阿里IDC,担任IT运维工程师,可以说阿里云见证了我能力的提升。初入阿里IDC现场运维我始终秉持着积极向上、努力进取的工作态度,在工作的同时,也开始着手准备考取阿里云数据中心IT初级运维工程师。(AlibabaCloudCertifiedTechnicalService-L2)在入职两个月后,我成为了属地某区域网络城市接口人,主要负责某项目的10余子项网络工作,这时的工作对能力和专业技术的要求略高,我被认证为阿里云数据中心IT初级运维工程师的过程中所学到的东西给了我很多的帮助,课程从走进数据中心开始,到能够根据胜任数据中心现场运维工作,熟悉运维的基础流程,对现场运维的工作内容,风险项等有一定的了解,以及能够熟练的进行数据中心常见服务器,网络设备的现场运维操作,具备一定的服务器维修基础,能够独立对网络设备常见的维护,配置操作等丰盛知识。在此期间畅行无碍地完成了此项工作。在2021年晋升IDC现场运维IT组长,自任职以来带领的团队以及我本人未出现过任何失误,而这些的基础都是在认证过程中所打下的。通过我的成长和就业经历,可以发现得到阿里云认证是非常重要,通过这个过程不仅可以学到很多的知识,而且在获得认证后可以提高任职岗位,承担更多的责任。我的阿里云数据中心IT初级运维工程师的认证是出于工作的需求加上我自己追求更高层度的工作,所以在考取证书时要有目的,不能抱着盲目的心态,高质量才能获得最大的收益。在获得阿里云数据中心IT初级运维工程师认证的过程中,首先是对自己的能力有所提升,由于存在考证的压力,所以在学习时更能一心一意,快速地学到了许多自己曾未了解过的专业知识。在获得阿里云数据中心IT初级运维工程师认证后,领导对我的能力也有了很大的了解,开始让我接触更多的东西,在我较为出色地完成了领导安排的工作后,我很快被提拔为负责人。现今越来越多的人在不断的考取各个大厂的认证,学历是我们找工作的一块敲门砖,而认证也是一块。认证它能够直接反映了我们的学习能力和专业水平,所以它对于我们进入某个行业起着非常大的作用。随着在阿里现场运维工作时间的增长,我也越来越意识到自己应该更加努力进取,向更优秀的同事们靠齐,争取得到更多的认证。不仅仅是为了认证而认证,而是在认证的过程中学习更多专业知识,更快地提高自己的能力,现阶段准备考取更多更高方向的阿里云认证。写在最后备考的方法不胜枚举,我主要通过“组块学习法”, 根据某种意义将信息碎片组成的集合,在备考Alibaba Cloud Certified Technical Service - L2时先通过“十块拼图”去了解整体架构(如下图):通过拼图可以指明学习目标及考试范围,零散的知识价值可能不能最大化,通过一块拼图可能不能了解整体的架构,但是通过多个组块可以更好的帮助我们合理的运用习得的知识, 总的来说Chunk(组块)让我们知道事物是什么、怎么用,而在工作中或者生活中告诉我们组块间的联系、什么时候用它,组块可以帮助我们快速理解新的知识,也可以把已知领域的方法和观点带到新的领域,通过组块学习法及学习技巧快速了解阿里云体系并备考冲刺更多的阿里云认证。
agent实现apm上报
# 前言最近刚刚换了工作,进了公司的架构组,有些项目蛮有意思,也是很感兴趣,也会工作之余自行学习,比如说有个自研apm项目> 当然在此声明一下,本篇代码属于个人学习编写,并非copy公司代码对于springcloud,有一套sleuth(主要是traceId+spanId生成)+zipkin(数据统计功能)skywalking,淘宝的鹰眼,蚂蚁金服sofatrace等等# 动手实现apm上报功能 1. 首先agent用的是byte-buddy 2. 定义一个context方便储存traceid,spanid以及上报的数据 3. 只是一个比较简陋的demo,需要后续一些功能优化## 代码**编写一个agent**```package com.example.demo.agent;import com.example.demo.interceptor.MyInterceptor;import net.bytebuddy.agent.builder.AgentBuilder;import net.bytebuddy.description.type.TypeDescription;import net.bytebuddy.dynamic.DynamicType;import net.bytebuddy.implementation.MethodDelegation;import net.bytebuddy.matcher.ElementMatchers;import net.bytebuddy.utility.JavaModule;import java.lang.instrument.Instrumentation;public class MyAgent { ? ?public static final ThreadLocal<TraceContext> LOCAL = new ThreadLocal<>(); ? ?public static void premain(String agentArgs, Instrumentation inst) { ? ? ? ?System.out.println("this is an perform monitor agent."); ? ? ? ?AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader) -> { ? ? ? ? ? ?return builder ? ? ? ? ? ? ? ? ? ?.method(ElementMatchers.any()) // 拦截任意方法 ? ? ? ? ? ? ? ? ? ?.intercept(MethodDelegation.to(MyInterceptor.class)); // 委托 ? ? ? ?}; ? ? ? ?AgentBuilder.Listener listener = new AgentBuilder.Listener() { ? ? ? ? ? ?@Override ? ? ? ? ? ?public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, DynamicType dynamicType) { ? ? ? ? ? ?} ? ? ? ? ? ?@Override ? ? ? ? ? ?public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { ? ? ? ? ? ?} ? ? ? ? ? ?@Override ? ? ? ? ? ?public void onError(String typeName, ClassLoader classLoader, JavaModule module, Throwable throwable) { ? ? ? ? ? ?} ? ? ? ? ? ?@Override ? ? ? ? ? ?public void onComplete(String typeName, ClassLoader classLoader, JavaModule module) { ? ? ? ? ? ?} ? ? ? ?}; ? ? ? ?new AgentBuilder ? ? ? ? ? ? ? ?.Default() ? ? ? ? ? ? ? ?.type(ElementMatchers.nameStartsWith("com.example.demo").and(ElementMatchers.not(ElementMatchers.nameStartsWith("com.example.demo.agent")))) ? ? ? ? ? ? ? ?// 指定需要拦截的类 ? ? ? ? ? ? ? ?.transform(transformer) ? ? ? ? ? ? ? ?.with(listener) ? ? ? ? ? ? ? ?.installOn(inst); ? ?}}```然后写下agent拦截下这些类之后需要做的操作```package com.example.demo.interceptor;import com.alibaba.fastjson.JSON;import com.example.demo.agent.TraceContext;import net.bytebuddy.implementation.bind.annotation.Origin;import net.bytebuddy.implementation.bind.annotation.RuntimeType;import net.bytebuddy.implementation.bind.annotation.SuperCall;import java.lang.reflect.Method;import java.util.concurrent.Callable;import static com.example.demo.agent.MyAgent.LOCAL;public class MyInterceptor { ? ?@RuntimeType ? ?public static Object intercept(@Origin Method method, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @SuperCall Callable<?> callable) throws Exception { ? ? ? ?long start = System.currentTimeMillis(); ? ? ? ?try { ? ? ? ? ? ?// 原有函数执行 ? ? ? ? ? ?return callable.call(); ? ? ? ?} finally { ? ? ? ? ? ?TraceContext context = LOCAL.get(); ? ? ? ? ? ?if(context != null){ ? ? ? ? ? ? ? ?context.setMethodType(method.getDeclaringClass().getName()+"."+method.getName()); ? ? ? ? ? ? ? ?context.setTime("调用方法时间:"+ (System.currentTimeMillis() - start) +"ms"); ? ? ? ? ? ? ? ?//上报操作,rpc,这里还需要修改sql等等打印到logback也上报到收集中心 ? ? ? ? ? ? ? ?System.out.println(JSON.toJSONString(context)); ? ? ? ? ? ? ? ?context.clear(); ? ? ? ? ? ?} ? ? ? ?} ? ?}}```**看下自定义上下文context**```package com.example.demo.agent;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;import java.util.concurrent.atomic.AtomicInteger;/** * @author M */@Builder@Data@AllArgsConstructor@NoArgsConstructorpublic class TraceContext implements Serializable { ? ?private String traceId; ? ?private String spanId; ? ?private String parentSpaceId; ? ?private String time; ? ?private String methodType; ? ?private String data; ? ?//spanId separator ? ?public static final String RPC_ID_SEPARATOR = "."; ? ?/** ? ? * sub-context counter ? ? */ ? ?private static AtomicInteger childContextIndex = new AtomicInteger(0); ? ?/** ? ? * 如果rpc调用的时候需要将spanid传递成这个方法的值 ? ? * ? ? * @return ? ? */ ? ?public String nextChildContextId() { ? ? ? ?return this.spanId + RPC_ID_SEPARATOR + childContextIndex.incrementAndGet(); ? ?} ? ?public static TraceContext cloneContext(TraceContext context) { ? ? ? ?if(context==null){ ? ? ? ? ? ?return new TraceContext(); ? ? ? ?} ? ? ? ?return TraceContext.builder() ? ? ? ? ? ? ? ?.spanId(context.nextChildContextId()) ? ? ? ? ? ? ? ?.parentSpaceId(context.getSpanId()) ? ? ? ? ? ? ? ?.traceId(context.getTraceId()) ? ? ? ? ? ? ? ?.build(); ? ?} ? ?public void clear() { ? ? ? ?//上报之后需要清理之前的一些数据 ? ? ? ?this.data = ""; ? ?}}```**生成agent jar包**这个自行百度,启动的时候加上 **-javaagent:D:\github\agent-apm\out\artifacts\MyAgent\MyAgent.jar****单元测试**```package com.example.demo.test;import com.alibaba.fastjson.JSON;import com.example.demo.agent.TraceContext;import java.util.UUID;import static com.example.demo.agent.MyAgent.LOCAL;public class AgentTest { ? ?private void fun1() throws Exception { ? ? ? ?TraceContext context = LOCAL.get(); ? ? ? ?if (context != null) { ? ? ? ? ? ?//由于没有集成sleuth,spaceId需要自己模调用的时候简单的自增 ? ? ? ? ? ?String spaceId = context.getSpanId(); ? ? ? ? ? ?//rpc调用的时候需要+1 ? ? ? ? ? ?context.setSpanId(spaceId); ? ? ? ? ? ?context.setParentSpaceId(spaceId); ? ? ? ? ? ?context.setData("fun1需要上报的数据"); ? ? ? ?} ? ? ? ?System.out.println("this is fun 1."); ? ? ? ?Thread.sleep(500); ? ?} ? ?private void fun2() throws Exception { ? ? ? ?TraceContext context = LOCAL.get(); ? ? ? ?if (context != null) { ? ? ? ? ? ?//由于没有集成sleuth,spaceId需要自己模调用的时候简单的自增 ? ? ? ? ? ?String spaceId = context.getSpanId(); ? ? ? ? ? ?//rpc调用的时候需要+1 ? ? ? ? ? ?context.setSpanId(spaceId); ? ? ? ? ? ?context.setParentSpaceId(spaceId); ? ? ? ? ? ?if (!"".equals(context.getData())) { ? ? ? ? ? ? ? ?System.out.println("fun2可以拿到之前context上传数据:" + context.getData()); ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?System.out.println("this is fun 2."); ? ? ? ?Thread.sleep(500); ? ? ? ?//模拟调用rpc ? ? ? ?TraceContext rpcContext = TraceContext.cloneContext(context); ? ? ? ?System.out.println("上报rpc context:" + JSON.toJSONString(rpcContext)); ? ?} ? ?/** ? ? * 可以重写logback append逻辑,打印日志也上报到收集数据的系统 ? ? * ? ? * @param args ? ? * @throws Exception ? ? */ ? ?public static void main(String[] args) throws Exception { ? ? ? ?//实际开发由sleuth来生成traceId ? ? ? ?String traceId = UUID.randomUUID().toString(); ? ? ? ?String spaceId = "0"; ? ? ? ?TraceContext context = TraceContext.builder() ? ? ? ? ? ? ? ?.spanId(spaceId) ? ? ? ? ? ? ? ?.parentSpaceId("0") ? ? ? ? ? ? ? ?.traceId(traceId) ? ? ? ? ? ? ? ?.build(); ? ? ? ?//如果是rpc的话,需要使用拦截器,将context塞到LOCAL里面 ? ? ? ?LOCAL.set(context); ? ? ? ?AgentTest test = new AgentTest(); ? ? ? ?test.fun1(); ? ? ? ?test.fun2(); ? ? ? ?//实际开发需要拦截器去删除本地变量 ? ? ? ?LOCAL.remove(); ? ?}}```**打印结果**```this is an perform monitor agent.this is fun 1.{"data":"fun1需要上报的数据","methodType":"com.example.demo.test.AgentTest.fun1","parentSpaceId":"0","spanId":"0","time":"调用方法时间:503ms","traceId":"add0ac18-9918-4c59-846d-a04802000bae"}this is fun 2.上报rpc context:{"parentSpaceId":"0","spanId":"0.1","traceId":"add0ac18-9918-4c59-846d-a04802000bae"}{"data":"","methodType":"com.example.demo.test.AgentTest.fun2","parentSpaceId":"0","spanId":"0","time":"调用方法时间:507ms","traceId":"add0ac18-9918-4c59-846d-a04802000bae"}```## 检验结果有traceid、spanid可以**构建一条调用链路**,其次的话会打印方法名,执行时间,可以进行**后续相应的代码优化**,以及加上了需要**自定义上报的数据**## 总结我们可以看到打印结果,可以看出同一个traceId,以及同一个应用spanid也是一样的,如果说跨应用,spanid需要重新设置,然后进行传递**spanId生成规则**参考下sofatrace 生成规则就是上面的0.1,0.1.1 spanId# 等待优化点 1. rpc部分需要重新,从header头拿到context赋值到threadlocal 2. mysql打印sql以及执行时间,也需要重写 3. 重写logback append逻辑,我们平时打印的日志也需要上报的数据中心# 项目链接github:[https://github.com/dajitui/agent-apm](https://github.com/dajitui/agent-apm)
为 Serverless Devs 插上 Terraform 的翅膀,实现企业级多环境部署(下)
在上篇中,主要介绍了 Serverless Devs 多环境功能的使用,用户读完可能会些疑问,本文会就一些常见问题进行下回答。1、Serverless Devs 和 Terraform 的关系可能有些用户会问,既然你们已经支持了 Terraform,那 Serverless Devs 还有什么作用,是不是直接用 Terraform 就可以了?Serverless Devs 和 Terraform 的定位还是明显不同的。Serverless Devs 面向应用管理及 DevOps,Terraform 面向云资源,是两个不同的领域,但并不表示不能在某些层面有交集或者不能集成,集成和被集成能力本来就是开源工具是否标准化的一个衡量标准。Terraform 解决的是云资源的 Provisioning,这个领域是有非常清晰的方法论的。而 Serverless Devs 更应该强调如何使用好云资源,两者的关系可以用几个场景说明:Serverless Devs 更多关注如何把代码或者安装依赖分片上传到NAS上,更少关注VPC/交换机/安全组/NAS挂载点如何创建出来;Serverless Devs 更多关注如何把文件上传到 OSS,并且自动触发函数完成报表的生成,更少关注 OSS Bucket 如何创建;Serverless Devs 更多关注如何构建代码/镜像、制作 Layer、部署代码、发布版本、灰度放量来构造完整的 CI/CD 体验,更少关注 FC 的网络、日志仓库、ACR 实例如何创建出来;Serverless Devs 更多关注如何远程调试代码,如何登陆到线上实例,如何通过日志以及监控快速发现业务的异常;可以看到 Serverless Devs 更加重点关注的是应用运行态以及运维态的操作,这也是 Serverless 架构的工具最重要的使命,但 Serverless Devs 负责的是 Serverless 应用全生命周期管理,必然少不了资源的管理,我们在实践过程中发现,无论是用云产品 SDK 还是 Pulumi 这类 GPLs 都需要投入很大精力在资源生命周期的对接上,这对于组件开发者对接更多云产品来说是非常低效的。而 Terraform 在这方面是最专业的,无论是标准化程度、受认可程度以及资源的丰富度都能很好满足终端用户及开发者的需求,因此才触发 Serverless Devs 和 Terraform 结合这一想法。Serverless Devs 没有和 Terraform 耦合,相反的是让 Terraform 的 HCL 语言自然的在 Serverless Devs 的组件规范里玩转起来,可以认为是 Serverless Devs 支持多语言的一种能力。对开发者的价值是可以比较低代码的完成基础设施的搭建,把精力投入到和 Serverless 应用生命周期管理相关的开发上,不然就得开发大量的资源CRUD代码,这个是非常低效的。2、多环境功能和直接用Terraform有什么不同既然多环境部署也走的是Terraform,那和我直接用Terraform部署资源有什么区别?Terraform 是个人版的工具,需要本地管理ak/sk、本地安装 Provider;而多环境是个多租的服务,不需要用户自己来维护这些多环境功能重要的是"管理"的能力,比如模板有版本管理能力,当模板发布了新版本并且 IaC 的变更是不兼容的,此时用户如果更新环境会导致未知问题,这种情况下系统会自动识别并且保证存量环境的变更还使用旧版本,不受不兼容变更带来的影响Terraform 是纯面向资源的编排工具,和应用的关联很弱;而环境和服务、流水线可以天然地形成连接关系,比如通过环境可以感知到资源被哪些服务所使用、服务可以通过环境的授权来获取访问资源的权限、可以在流水线中将服务一次性部署到所有环境上,而这些是 Terraform 做不了的Terraform 只是多环境实现 IaC 的一个技术选型,未来还计划对接 ROS、Pulumi 等 IaC 项目3、多环境和环境变量的关系在 CI/CD 中使用环境变量,环境变量中配置VPC、NAS啥的,s.yaml中引用环境变量似乎就可以了,为什么还要造一个环境概念?环境和环境变量从名字就能区分出定位的差异,环境变量就是一组静态配置,虽然可以将一些资源配置写到环境变量内并在 CI/CD 流水线中引用,但这种方式不具备资源纳管的能力。而环境是个实体资源,具备基础设施的生命周期管理能力,通过环境可以完成基础设施的增删改查,并可以通过访问控制的方式授予用户的操作权限,更新环境时还可以对接一些安全检查的能力。通过环境可以让基础设施受到保护,比如当多个服务共享环境时,如果发起环境删除,系统会自动发现环境被其他服务所依赖,此时删除会被拒绝。4、只能企业用户使用么,个人开发者怎么用我是个人开发者,不懂 Terraform,文章中各种模板定义看的有点晕,那我还适合用这个功能么?个人开发者一样适用,但不应该让这部分用户承担写模板的工作,而是由平台提供各种业务场景化的模板,开发者开箱即用,这也是我们后续的主要工作。对个人用户来说,上阿里云最复杂的某过于RAM、VPC、ECS、SLB、NAS这些复杂的概念,学习曲线太长。在 Serverless 架构下这个问题尤为明显,Serverless 宣称低门槛、低成本、低运维,但是上手Serverless需要了解一大堆概念,配置一大堆东西,很多用户在这过程中就被"劝退"了,而环境模板和环境可以极大地简化云产品的上手成本,同时又能很安全地操作。举个例子,用户选择一个模板部署环境,就可以一键拉起所有云资源,这样才算是真正的 Serverless。5、实现原理遵循 Serverless Devs 组件开发规范,通过实现一个组件来完成和后端服务的对接后端服务采用 Serverless + K8s 的架构,通过消息触发函数,来完成模板的渲染以及部署任务的执行采用 KubeVela 来完成 K8s 资源的管理以及 Terraform 任务的执行6、多环境为什么是组件级的能力,而不是CLI的能力Serverless Devs 分为 CLI 和 组件:CLI 提供最通用的能力,不依赖任何组件,比如:s init、s config、s verify、--template、--debug组件提供特定的功能,比如 s deploy、s build、s invoke 这些是fc组件的能力从 env 命令的通用性以及要解决的问题上看,做到 CLI 内也是合适的。但从实现上看,因为需要一个服务端的控制平面来完成用户资源的部署,出于安全性考虑必须要特定的云服务来完成,所以才通过一个组件来完成。使用 Serverless 1 分钟轻松搭建你的首个个人网站!免费额度,轻松上手!小白也可极速建站:无需考虑服务器和网站源码,我们为你提供免费计算资源,运维管理服务器。活动期间完成场景体验,即有机会领取天猫超市 10 元代金券。活动时间:2022年6月20日-7月1日(工作日期间领取)答疑群:钉钉搜索 “44700570”体验地址:点击阅读原文 or 浏览器访问(建议pc端体验)/adc/series/activity/serverless2022
我与阿里云认证的故事
我是李东峰,现在阿里巴巴数据中心从事IT运维工作。本人从事IT现场运维工作近6年,主要负责现场工作安排、安全检查、技术支撑、现场应急响应等工作,负责过机房从0到1的建设工作,拥有丰富的现场运维经验。2021年8月,阿里云数据中心 IT 初级运维工程师认证(Alibaba Cloud Certified Technical Service - L2)是我从事运维行业取得的第一个认证,取得相关认证是对自己工作能力的一种认可,目前准备考取更高方向的认证,不断学习,不断成长!认证与项目:在认证学习时,正好赶上机房DE楼新交付,开始进行机房综合布线、网络设备上架、网络设备配置、并网,服务器工具机上架装机、调试等等。这些工作内容和阿里云数据中心 IT 初级运维工程师认证所学习的内容完全吻合,习惯了日常运维,对于这种开荒交付一些关键点记忆不是很深刻,认证学习的过程帮助我顺利完成了DE楼的交付接维,也是把这些知识点用于到实践的最好证明,这些理论知识与我们工作是完全一致的,学习这些认证知识,取得认证,也意味着真真切切的理解了“IT现场运维”这项工作,也是具备IT现场运维能力的最好证明!认证开始:2021年6月,着手准备阿里云数据中心 IT 初级运维工程师认证,开始对认证并不是很了解,后续通过相关课程的学习,了解到阿里巴巴数据中心运维类人员的专业技术认证,主要涉及阿里数据中心的服务器技术,网络技术,综合布线技术,运维规范,资产管理等。通过该技术认证可以有效证明该认证人员具备以下能力:?具备IT、网络、数据中心、服务器维修等相关从业的基础知识 ?能够胜任数据中心现场运维工作,熟悉运维的基础流程,对现场运维的工作内容,风险项等有一定的了解。 ?能够熟练的进行数据中心常见服务器,网络设备的现场运维操作,具备一定的服务器维修基础,能够独立对网络设备进行常见的维护、配置等操作 。?对数据中心运维规范有一定了解,能够快速上手现场运维工作,并对现场常见故障有应急响应及事件处理能力。认证过程:在认证学习的过程中,发现这些知识点是自己平时所了解的、知道的,但之间的关联性、系统性是自己所忽略的,认证学习的过程是对自己平时工作所用知识的一种有效回顾,也是对自己平时工作积累的一种查漏补缺,更是对IT运维工作的一个全方位总结,自己边学习边总结,相关知识点整理如下:走进数据中心:1.了解数据中心的定义与发展2.熟悉数据中心的物理区域划分3.了解数据中心的分级标准与设计标准4.了解数据中心的行业现状及面临的挑战 5.熟悉阿里数据中心的发。数据中心网络计算基础:1.了解网络和重要的网络协议,理解网络通信的原理2.了解局域网和交换机的工作原理,理解VLAN的工作原理和重要意义3.了解IP地址,学会网络分解和汇总,理解路由原理,理解网络通信的过程4.学会在本地网络中应用增强技术,构建健壮的本地网络5.学会使用NAT技术和ACL技术,了解公网接入和访问控制的基本方法。数据中心网络组成与维护:1.了解数据中心的传统的三层网络结构2.了解数据中心网络结构的演变3.了解数据中心的虚拟化技术4.了解以SDN 为代表的网络虚拟服务器硬件技术:1.熟悉服务器的基本概念与组成2.掌握常见计算硬件及其性能参数3.掌握常见存储硬件及其性能。服务器软件技术:熟悉服务器常见软件系统2.掌握 Linux 硬盘、网卡管理操作3.熟悉 BIOS 固件刷新操作4.熟悉BMC带外管理系统组成5.掌握FRU规范与修改操。数据中心综合布线:1.了解综合布线系统特点和应用2.熟悉综合布线的参考标准3.掌握传输介质的分类和使用规范4.掌握综合布线的规划、设计和实施 5.了解综合布线的施工质量要求6.掌握综合布线维护工具的使用。数据中心运营规范:1.熟悉数据中心运营组织的组成2.熟悉数据中心运营目标3.了解数据中心运营面临的挑战认证备考:首先熟悉阿里云数据中心 IT 初级运维工程师认证学习大纲,了解都包含哪些知识点,然后去分解这些知识点,一块一块去学习,学习过程中,自己之前不太清晰的知识点,进行笔记记录,单项强化学习,学习完成后,站在整个运维的角度,把知识点串联起来,参考新建项目进程把知识框架整合起来,采用总分总的一个形势进行备考。认证考试:考试内容包括了各个领域的知识点和目标,与学习内容相符,是对自己学习过程和日常工作能力的最好验证,同时也会更好的发现自己的不足之处。最后:我与阿里云认证的故事——如果说,大学毕业证书是对我学习生涯的认可,那么阿里云认证便是对我工作专业性的认可。
云起第一期学习体会(报告)
这个实验室整体感觉不错。单击屏幕右侧 创建资源 ,免费创建当前实验所需云产品资源。完成实验资源的创建后,您可以在 云产品资源 列表查看已创建的资源信息,例如:IP地址、子用户名称和子用户密码等。单击 收起 即可收起云产品资源栏。云产品资源超出资源使用时长后云产品将会自动释放,所以实验时需要尽快完成实验保留实验数据。其中浏览器、双击Firefox ECR图标 进入浏览器(子账号登录界面)。登陆子账号后可进行相关操作。LX终端,双击 LX终端图标 即可打开shell终端窗口。桌面虚拟终端基于Debian系统,您可以使用桌面LX终端执行基础Linux命令,也可以通过终端来连接ECS服务器。终端内快捷键使用复制:Ctrl+Shift+C 粘贴:Ctrl+Shift+V网页内复制可同步到 终端剪切板(不支持中文)。回归到本次的主题,主要是学习了一下ECS的使用和安装LAMP应用、Java应用。让大家动手起来,这是一个加深记忆、快速学习的一个重要的途径。体验实验室是为开发者打造的一站式体验学习平台,在这里我们可以了解并亲自动手体验各类云产品和云计算基础,无需关注资源开通和底层产品,无需任何费用。只要有一颗想要了解云、学习云、体验云的心,这里就是你的上云第一站。传统服务器具有价格高昂,采购周期长,资源闲置浪费率高,维护成本高的缺点。阿里云ECS覆盖50多款操作系统,包含开源LAMP组合和常见的Windows平台。包含传统x86服务器及GPU和FPGA的异构计算,包含通用型,内存型,网络增强型,本地SSD型和计算型,甚至企业级Oracle数据库所需的超大规格独占物理机,配置及使用更为灵活。ECS在底层对每份数据进行多次备份,物理层面拥有超A级数据中心,通过双路独市电引入、三路网络和N+1柴油发电机后备电源确保数据安全。第一期的学习,学习到了很多知识,而且只一天只需要10多分钟。就是开设的专题都太简单了。欢迎大家加入到队伍一起学习。
用户指南—实例管理—只读实例
背景信息主实例存储节点采用了基于Paxos的三副本高可用架构。而只读实例的存储节点,则通过增加Learner副本的方式来保证物理资源隔离,同时能够基于全局时钟来确保只读查询的强一致性。注意事项只读实例与主实例必须属于同一个地域,但是可以在不同的可用区。只读实例无法单独存在,必须隶属于某个主实例。若您在主实例上创建了数据库,该数据库也会被复制到只读实例上。同理,若您在主实例上删除数据库时,只读实例上对应的数据库也会被删除。操作步骤登录云原生分布式数据库控制台。在页面左上角选择目标实例所在地域。在实例列表页,单击PolarDB-X 2.0页签。在目标实例右侧的操作栏中,单击只读实例。您还可以点击进入实例详情页面,在页面右侧的只读实例区域中,单击添加只读实例。背景信息主实例存储节点采用了基于Paxos的三副本高可用架构。而只读实例的存储节点,则通过增加Learner副本的方式来保证物理资源隔离,同时能够基于全局时钟来确保只读查询的强一致性。注意事项只读实例与主实例必须属于同一个地域,但是可以在不同的可用区。只读实例无法单独存在,必须隶属于某个主实例。若您在主实例上创建了数据库,该数据库也会被复制到只读实例上。同理,若您在主实例上删除数据库时,只读实例上对应的数据库也会被删除。操作步骤登录云原生分布式数据库控制台。在页面左上角选择目标实例所在地域。在实例列表页,单击PolarDB-X 2.0页签。在目标实例右侧的操作栏中,单击只读实例。您还可以点击进入实例详情页面,在页面右侧的只读实例区域中,单击添加只读实例。
前端笔试题面试题记录(下)
前言接上篇前端笔试题面试题记录(上)。趁清明小长假,把上篇剩下的部分也写一下,因为最近比较忙这篇已经拖了很久了。现在刚刚开始银四了,应该还是有些小伙伴在找工作,时间还不算太晚,希望本篇可以帮到这些小伙伴。本文主要目录如下:面试题QA面试遇到的一些小问题Vue系列问题面试题QAQ:了解ES6的let和const变量声明吗?跟ES5的var有哪些区别?let在同一个作用域里面:var可以重复声明变量,let不能重复声明同一个变量。es5是函数作用域,即一个函数里面才是一个作用域,es6是块级作用域(花括号'{这里面是一个作用域}'),如:if、for花括号里面都是一个作用域。var有变量提升,可以在变量声明之前使用,let不存在变量提升,在变量之前使用会报错。let 有暂时性死区,阮一峰大佬es6入门文档解释如下:constconst的很多特性跟let的特性一样,都有:不可重复声明,不存在变量提升,有暂时性死区,都是块级作用域。还有一些跟let命令不一样的地方:const必须在声明的时候赋值,不然就会报错。const声明的常量不能更改。这里的常量指的是:数值、字符串、布尔值,对于引用类型(数组和对象),const只能保证指针是固定的,至于数组和对象内部有没有改变就是const不能控制的地方Q:Css的优先级。这类也通常出现在笔试题中,具体的题目记不太清了。权重优先级:
!important>style(1000)>id(100)>class(10)
!important是优先级最高的不管权重多少,始终采取important。
如果两个选择器作用在同一元素上,计算权重值,相加。权重高者属性生效。
(笔试题就出现过层叠的class id选择器,作用在同一个标签上,然后问最后哪个css属性生效)有兴趣的可以看一下我写的你对CSS权重真的足够了解吗Q:继承函数对象的实例方法、原型的继承。函数对象的继承,在面试的时候,一般出现在笔试题那边,也碰到过几次,下面给出一个答案。function father(name){//父函数
this.name=name|'koro1';
this.code=function(){ //父类的实例方法
console.log(this.name+'coding');
}
};
father.prototype.add=function(food){ //父类的原型方法
console.log(this.name+'eat'+food);
}
function son(name){ //子函数
father.call(this); //将this绑定到子类,绑定父类的实例方法code(原型方法add还未绑定)
this.name=name|| 'OBKoro1';
}
son.prototype = new father();//把父类的原型方法绑定到子类,实现继承
var sonVar= new son('faker');//这里也可以传参name
son.prototype.constructor = son;//修复构造函数的指向
console.log(sonVar.code());
console.log(sonVar.add());//可以调用父类的方法了Q:通过reduce函数来实现简单的数组求和,示例数组[3,4,8,0,9];这是一个简单的笔试题,下面写了两种方法,一种是常见遍历的方法,还有一种是使用eval()方法。let reduce=(arr)=>{ //第一种常规遍历。
let num=0;
for(let [index,value] of arr.entries()){
num+=value;
}
return num;
}
let reduce=(arr)=>eval(arr.join("+")); //第二种
//join() 方法把数组元素放入字符串 上面的栗子: arr.join("+")得到字符串:'3+4+8+0+9';
// eval() 函数计算字符串 ,并执行其中的的 JavaScript 代码
//经提醒:原来有一个reduce()数组求和的方法,把这个方法加上去
let result=[3,4,8,0,9].reduce((total,value)=>{ //这两个参数是默认参数不用设置的
return total+value
});Q:call()和apply()有什么区别?call()和apply()第一个参数将用作函数内 this 的值,用于改变函数的this指向。call和apply的区别在于call()方法接受逗号分隔的参数作为后面的参数,apply()接受一个参数数组作为后面的参数。从别的博客那边看到一个简单的记忆方法:从call中的 C 联想到逗号分隔(comma-separated),从apply中的 A 联想到数组(array)。Q:position有哪些值?有什么作用?static。默认值,不脱离文档流,top,right,bottom,left等属性不生效。relative。不脱离文档流,依据自身位置进行偏离,当子元素设置absolute,将依据它进行偏离。absolute。脱离文档流,依据top,right,bottom,left等属性在正常文档流中偏移位置。fixed。通过浏览器窗口进行定位,出现滚动条的时候,不会随之滚动。Q:如何实现一个闭包?闭包的作用有哪些?在一个函数里面嵌套另一个函数,被嵌套的那个函数的作用域是一个闭包。作用:创建私有变量,减少全局变量,防止变量名污染。可以操作外部作用域的变量,变量不会被浏览器回收,保存变量的值。Q:请从2017-05-15T09:10:23 Europe/Paris提取出结果["2017","05","15","09","10","23"]这是一道笔试题,正则相关的,看他们要的内容就可以知道,要把所有的数字都提取出来,可以写一个只获取数字的正则表达式。let str = '2017-05-15T09:10:23 Europe/Paris';
let arr = str.match( /\d{1,}/g);
//match会返回一个数组,
// \d 查找数字
// {1,} 表示至少重复几次
// /g表示全局搜索Q:请描述一下Promise的使用场景,'Promise'它所解决的问题以及现在对于异步操作的解决方案。这是一道笔试题,这是我当时的回答。Promise的使用场景:ajax请求,回调函数,复杂操作判断。Promise是ES6为了解决异步编程所诞生的。异步操作解决方案:Promise、Generator、定时器(不知道算不算)、还有ES7的async面试遇到的一些小问题:这里主要是面试中遇到的一些小的问题,一两句话就可以说清的东西,大家可以稍微看一看。函数参数变量提升function aa(val){ //函数参数的变量也会提升
console.log(val);//'函数传参'
var val='变量声明';
console.log(val);//'变量声明'
}
aa('函数传参');js有哪些方法定义对象?var obj=new Object();//new 一个对象
var obj={name:"OBKoro1"} //对象字面量写法字符串数字转换运算的问题console.log(1+'2'+'2'); //122
console.log(+'1'+-'2'+'2');//-12
console.log('A'+'B'+'2');//AB2
console.log('A'-'B'+2);//NaN
//两个都是数字才能相加,否则都是以字符串形式拼接。
//相减只能两个都是数字,字符串也会转成数字,如果不能转换,值就为NaNsplit()、join()的区别?split()是将字符分割成一个数组,join()将数组转为一个字符串pop()push()unshift()shift()的作用?pop()删除并返回数组的最后一个元素。
push()可向数组的末尾添加一个或多个元素,并返回新的长度。
unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。判断一个数是否是整数function isIntefer(x){
return x%1===0; //返回布尔
}如何将字符串转为数字,如:12.3b?var num=parseFloat('12.3b')什么是外边距合并?当两个垂直外边距相遇时,它们将形成一个外边距,合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者。Q:你常用的git/svn 命令行有哪些?行内元素有哪些,块状元素有哪些?css有哪些选择器?自行谷歌,类似的还有css3新增的属性,H5新增的属性之类的,有空的话,稍微背一背,都碰到过。Vue系列问题:我在面试过程中很少被问到框架,很多是在笔试题中碰到的,在介绍自己项目的时候,有时候自己会解释这里用到了Vue的什么技术,这个时候面试官可能就会顺着问一问Vue,总的来说问的也不会太深。以下是笔试真题:vue-router怎么定义动态路由?怎么获取传过来的动态参数?定义:path:'a/:value' 获取:this.$route.params.value。说出至少4中vue当中的指令和它的用法。vue文档显示13个指令vue如何绑定事件。@click="事件名"v-show和v-if指令的共同点和不同点?1.v-if是删除/添加Dom标签,不占据文档位置,v-show切换css的display属性,控制显示隐藏,还会占据文档位置。
2.v-if会删除dom标签所以v-if性能消耗会高一些,需要频繁切换的话,使用v-show会好一点。的作用的是什么?`<keep-alive>`是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。列举三个Vue常用的生命周期钩子函数?Vue文档分别写出webpack打包给服务器和本地开发预览的命令代码:npm run build npm run dev 我都觉得很弱智。。vue 父子组件是怎么进行传参的?父组件传参给子组件通过props,子组件传参给父组件是用事件传递的。细节见文档。v-model是用来做什么的,怎么使用。用来为input输入框或者单选框、select选择框这类的东西做双向绑定的。
使用栗子:<input v-model="inputData"/>Vuex的工作流程,以及它的作用,使用场景。vuex的工作流程:在vue组件里面,通过dispatch来触发actions提交修改数据的操作。然后再通过actions的commit来触发mutations来修改数据。mutations接收到commit的请求,就会自动通过Mutate来修改state(数据中心里面的数据状态)里面的数据。最后由store触发每一个调用它的组件的更新Vuex的作用:项目数据状态的集中管理,复杂组件(如兄弟组件、远房亲戚组件)的数据通信问题。vue的服务端渲染vue的双向绑定两个问题都不会...
K8S面试题
1、 k8s是什么?请说出你的了解?答:Kubenetes是一个针对容器应用,进行自动部署,弹性伸缩和管理的开源系统。主要功能是生产环境中的容器编排。K8S是Google公司推出的,它来源于由Google公司内部使用了15年的Borg系统,集结了Borg的精华。2、 K8s架构的组成是什么?答:和大多数分布式系统一样,K8S集群至少需要一个主节点(Master)和多个计算节点(Node)。主节点主要用于暴露API,调度部署和节点的管理;计算节点运行一个容器运行环境,一般是docker环境(类似docker环境的还有rkt),同时运行一个K8s的代理(kubelet)用于和master通信。计算节点也会运行一些额外的组件,像记录日志,节点监控,服务发现等等。计算节点是k8s集群中真正工作的节点。K8S架构细分:1、Master节点(默认不参加实际工作):Kubectl:客户端命令行工具,作为整个K8s集群的操作入口;Api Server:在K8s架构中承担的是“桥梁”的角色,作为资源操作的唯一入口,它提供了认证、授权、访问控制、API注册和发现等机制。客户端与k8s群集及K8s内部组件的通信,都要通过Api Server这个组件;Controller-manager:负责维护群集的状态,比如故障检测、自动扩展、滚动更新等;Scheduler:负责资源的调度,按照预定的调度策略将pod调度到相应的node节点上;Etcd:担任数据中心的角色,保存了整个群集的状态;2、Node节点:Kubelet:负责维护容器的生命周期,同时也负责Volume和网络的管理,一般运行在所有的节点,是Node节点的代理,当Scheduler确定某个node上运行pod之后,会将pod的具体信息(image,volume)等发送给该节点的kubelet,kubelet根据这些信息创建和运行容器,并向master返回运行状态。(自动修复功能:如果某个节点中的容器宕机,它会尝试重启该容器,若重启无效,则会将该pod杀死,然后重新创建一个容器);Kube-proxy:Service在逻辑上代表了后端的多个pod。负责为Service提供cluster内部的服务发现和负载均衡(外界通过Service访问pod提供的服务时,Service接收到的请求后就是通过kube-proxy来转发到pod上的);container-runtime:是负责管理运行容器的软件,比如dockerPod:是k8s集群里面最小的单位。每个pod里边可以运行一个或多个container(容器),如果一个pod中有两个container,那么container的USR(用户)、MNT(挂载点)、PID(进程号)是相互隔离的,UTS(主机名和域名)、IPC(消息队列)、NET(网络栈)是相互共享的。我比较喜欢把pod来当做豌豆夹,而豌豆就是pod中的container;3、 容器和主机部署应用的区别是什么?答:容器的中心思想就是秒级启动;一次封装、到处运行;这是主机部署应用无法达到的效果,但同时也更应该注重容器的数据持久化问题。另外,容器部署可以将各个服务进行隔离,互不影响,这也是容器的另一个核心概念。4、请你说一下kubenetes针对pod资源对象的健康监测机制?答:K8s中对于pod资源对象的健康状态检测,提供了三类probe(探针)来执行对pod的健康监测:1) livenessProbe探针可以根据用户自定义规则来判定pod是否健康,如果livenessProbe探针探测到容器不健康,则kubelet会根据其重启策略来决定是否重启,如果一个容器不包含livenessProbe探针,则kubelet会认为容器的livenessProbe探针的返回值永远成功。2) ReadinessProbe探针同样是可以根据用户自定义规则来判断pod是否健康,如果探测失败,控制器会将此pod从对应service的endpoint列表中移除,从此不再将任何请求调度到此Pod上,直到下次探测成功。3) startupProbe探针启动检查机制,应用一些启动缓慢的业务,避免业务长时间启动而被上面两类探针kill掉,这个问题也可以换另一种方式解决,就是定义上面两类探针机制时,初始化时间定义的长一些即可。每种探测方法能支持以下几个相同的检查参数,用于设置控制检查时间:initialDelaySeconds:初始第一次探测间隔,用于应用启动的时间,防止应用还没启动而健康检查失败periodSeconds:检查间隔,多久执行probe检查,默认为10s;timeoutSeconds:检查超时时长,探测应用timeout后为失败;successThreshold:成功探测阈值,表示探测多少次为健康正常,默认探测1次。上面两种探针都支持以下三种探测方法:1)Exec: 通过执行命令的方式来检查服务是否正常,比如使用cat命令查看pod中的某个重要配置文件是否存在,若存在,则表示pod健康。反之异常。Exec探测方式的yaml文件语法如下:spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe: #选择livenessProbe的探测机制
exec: #执行以下命令
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5 #在容器运行五秒后开始探测
periodSeconds: 5 #每次探测的时间间隔为5秒在上面的配置文件中,探测机制为在容器运行5秒后,每隔五秒探测一次,如果cat命令返回的值为“0”,则表示健康,如果为非0,则表示异常。2)Httpget: 通过发送http/htps请求检查服务是否正常,返回的状态码为200-399则表示容器健康(注http get类似于命令curl -I)。Httpget探测方式的yaml文件语法如下:spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
livenessProbe: #采用livenessProbe机制探测
httpGet: #采用httpget的方式
scheme:HTTP #指定协议,也支持https
path: /healthz #检测是否可以访问到网页根目录下的healthz网页文件
port: 8080 #监听端口是8080
initialDelaySeconds: 3 #容器运行3秒后开始探测
periodSeconds: 3 #探测频率为3秒上述配置文件中,探测方式为项容器发送HTTP GET请求,请求的是8080端口下的healthz文件,返回任何大于或等于200且小于400的状态码表示成功。任何其他代码表示异常。3)tcpSocket: 通过容器的IP和Port执行TCP检查,如果能够建立TCP连接,则表明容器健康,这种方式与HTTPget的探测机制有些类似,tcpsocket健康检查适用于TCP业务。tcpSocket探测方式的yaml文件语法如下:spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
#这里两种探测机制都用上了,都是为了和容器的8080端口建立TCP连接
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20在上述的yaml配置文件中,两类探针都使用了,在容器启动5秒后,kubelet将发送第一个readinessProbe探针,这将连接容器的8080端口,如果探测成功,则该pod为健康,十秒后,kubelet将进行第二次连接。除了readinessProbe探针外,在容器启动15秒后,kubelet将发送第一个livenessProbe探针,仍然尝试连接容器的8080端口,如果连接失败,则重启容器。探针探测的结果无外乎以下三者之一:Success:Container通过了检查;Failure:Container没有通过检查;Unknown:没有执行检查,因此不采取任何措施(通常是我们没有定义探针检测,默认为成功)。若觉得上面还不够透彻,可以移步其官网文档:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/5、 如何控制滚动更新过程?答:可以通过下面的命令查看到更新时可以控制的参数:[root@master yaml]# kubectl explain deploy.spec.strategy.rollingUpdatemaxSurge: 此参数控制滚动更新过程,副本总数超过预期pod数量的上限。可以是百分比,也可以是具体的值。默认为1。(上述参数的作用就是在更新过程中,值若为3,那么不管三七二一,先运行三个pod,用于替换旧的pod,以此类推)maxUnavailable: 此参数控制滚动更新过程中,不可用的Pod的数量。(这个值和上面的值没有任何关系,举个例子:我有十个pod,但是在更新的过程中,我允许这十个pod中最多有三个不可用,那么就将这个参数的值设置为3,在更新的过程中,只要不可用的pod数量小于或等于3,那么更新过程就不会停止)。推荐:250期面试题精选6、K8s中镜像的下载策略是什么?答:可通过命令“kubectl explain pod.spec.containers”来查看imagePullPolicy这行的解释。K8s的镜像下载策略有三种:Always、Never、IFNotPresent;Always:镜像标签为latest时,总是从指定的仓库中获取镜像;Never:禁止从仓库中下载镜像,也就是说只能使用本地镜像;IfNotPresent:仅当本地没有对应镜像时,才从目标仓库中下载。默认的镜像下载策略是:当镜像标签是latest时,默认策略是Always;当镜像标签是自定义时(也就是标签不是latest),那么默认策略是IfNotPresent。7、 image的状态有哪些?Running:Pod所需的容器已经被成功调度到某个节点,且已经成功运行,Pending:APIserver创建了pod资源对象,并且已经存入etcd中,但它尚未被调度完成或者仍然处于仓库中下载镜像的过程Unknown:APIserver无法正常获取到pod对象的状态,通常是其无法与所在工作节点的kubelet通信所致。8、 pod的重启策略是什么?答:可以通过命令“kubectl explain pod.spec”查看pod的重启策略。(restartPolicy字段)Always:但凡pod对象终止就重启,此为默认策略。OnFailure:仅在pod对象出现错误时才重启9、 Service这种资源对象的作用是什么?答:用来给相同的多个pod对象提供一个固定的统一访问接口,常用于服务发现和服务访问。10、版本回滚相关的命令?[root@master httpd-web]# kubectl apply -f httpd2-deploy1.yaml --record
#运行yaml文件,并记录版本信息;
[root@master httpd-web]# kubectl rollout history deployment httpd-devploy1
#查看该deployment的历史版本
[root@master httpd-web]# kubectl rollout undo deployment httpd-devploy1 --to-revision=1
#执行回滚操作,指定回滚到版本1
#在yaml文件的spec字段中,可以写以下选项(用于限制最多记录多少个历史版本):
spec:
revisionHistoryLimit: 5
#这个字段通过 kubectl explain deploy.spec 命令找到revisionHistoryLimit <integer>行获得11、 标签与标签选择器的作用是什么?标签:是当相同类型的资源对象越来越多的时候,为了更好的管理,可以按照标签将其分为一个组,为的是提升资源对象的管理效率。标签选择器:就是标签的查询过滤条件。目前API支持两种标签选择器:基于等值关系的,如:“=”、“”“==”、“!=”(注:“==”也是等于的意思,yaml文件中的matchLabels字段);基于集合的,如:in、notin、exists(yaml文件中的matchExpressions字段);注:in:在这个集合中;notin:不在这个集合中;exists:要么全在(exists)这个集合中,要么都不在(notexists);使用标签选择器的操作逻辑:在使用基于集合的标签选择器同时指定多个选择器之间的逻辑关系为“与”操作(比如:- {key: name,operator: In,values: [zhangsan,lisi]} ,那么只要拥有这两个值的资源,都会被选中);使用空值的标签选择器,意味着每个资源对象都被选中(如:标签选择器的键是“A”,两个资源对象同时拥有A这个键,但是值不一样,这种情况下,如果使用空值的标签选择器,那么将同时选中这两个资源对象)空的标签选择器(注意不是上面说的空值,而是空的,都没有定义键的名称),将无法选择出任何资源;在基于集合的选择器中,使用“In”或者“Notin”操作时,其values可以为空,但是如果为空,这个标签选择器,就没有任何意义了。两种标签选择器类型(基于等值、基于集合的书写方法):selector:
matchLabels: #基于等值
app: nginx
matchExpressions: #基于集合
- {key: name,operator: In,values: [zhangsan,lisi]} #key、operator、values这三个字段是固定的
- {key: age,operator: Exists,values:} #如果指定为exists,那么values的值一定要为空12、 常用的标签分类有哪些?标签分类是可以自定义的,但是为了能使他人可以达到一目了然的效果,一般会使用以下一些分类:版本类标签(release):stable(稳定版)、canary(金丝雀版本,可以将其称之为测试版中的测试版)、beta(测试版);环境类标签(environment):dev(开发)、qa(测试)、production(生产)、op(运维);应用类(app):ui、as、pc、sc;架构类(tier):frontend(前端)、backend(后端)、cache(缓存);分区标签(partition):customerA(客户A)、customerB(客户B);品控级别(Track):daily(每天)、weekly(每周)。13、 有几种查看标签的方式?答:常用的有以下三种查看方式:[root@master ~]# kubectl get pod --show-labels #查看pod,并且显示标签内容
[root@master ~]# kubectl get pod -L env,tier #显示资源对象标签的值
[root@master ~]# kubectl get pod -l env,tier #只显示符合键值资源对象的pod,而“-L”是显示所有的pod14、 添加、修改、删除标签的命令?#对pod标签的操作
[root@master ~]# kubectl label pod label-pod abc=123 #给名为label-pod的pod添加标签
[root@master ~]# kubectl label pod label-pod abc=456 --overwrite #修改名为label-pod的标签
[root@master ~]# kubectl label pod label-pod abc- #删除名为label-pod的标签
[root@master ~]# kubectl get pod --show-labels
#对node节点的标签操作
[root@master ~]# kubectl label nodes node01 disk=ssd #给节点node01添加disk标签
[root@master ~]# kubectl label nodes node01 disk=sss –overwrite #修改节点node01的标签
[root@master ~]# kubectl label nodes node01 disk- #删除节点node01的disk标签15、 DaemonSet资源对象的特性?DaemonSet这种资源对象会在每个k8s集群中的节点上运行,并且每个节点只能运行一个pod,这是它和deployment资源对象的最大也是唯一的区别。所以,在其yaml文件中,不支持定义replicas,除此之外,与Deployment、RS等资源对象的写法相同。它的一般使用场景如下:在去做每个节点的日志收集工作;监控每个节点的的运行状态;16、 说说你对Job这种资源对象的了解?答:Job与其他服务类容器不同,Job是一种工作类容器(一般用于做一次性任务)。使用常见不多,可以忽略这个问题。#提高Job执行效率的方法:
spec:
parallelism: 2 #一次运行2个
completions: 8 #最多运行8个
template:
metadata:17、描述一下pod的生命周期有哪些状态?Pending:表示pod已经被同意创建,正在等待kube-scheduler选择合适的节点创建,一般是在准备镜像;Running:表示pod中所有的容器已经被创建,并且至少有一个容器正在运行或者是正在启动或者是正在重启;Succeeded:表示所有容器已经成功终止,并且不会再启动;Failed:表示pod中所有容器都是非0(不正常)状态退出;Unknown:表示无法读取Pod状态,通常是kube-controller-manager无法与Pod通信。18、 创建一个pod的流程是什么?客户端提交Pod的配置信息(可以是yaml文件定义好的信息)到kube-apiserver;Apiserver收到指令后,通知给controller-manager创建一个资源对象;Controller-manager通过api-server将pod的配置信息存储到ETCD数据中心中;Kube-scheduler检测到pod信息会开始调度预选,会先过滤掉不符合Pod资源配置要求的节点,然后开始调度调优,主要是挑选出更适合运行pod的节点,然后将pod的资源配置单发送到node节点上的kubelet组件上。Kubelet根据scheduler发来的资源配置单运行pod,运行成功后,将pod的运行信息返回给scheduler,scheduler将返回的pod运行状况的信息存储到etcd数据中心。19、 删除一个Pod会发生什么事情?答:Kube-apiserver会接受到用户的删除指令,默认有30秒时间等待优雅退出,超过30秒会被标记为死亡状态,此时Pod的状态Terminating,kubelet看到pod标记为Terminating就开始了关闭Pod的工作;关闭流程如下:pod从service的endpoint列表中被移除;如果该pod定义了一个停止前的钩子,其会在pod内部被调用,停止钩子一般定义了如何优雅的结束进程;进程被发送TERM信号(kill -14)当超过优雅退出的时间后,Pod中的所有进程都会被发送SIGKILL信号(kill -9)。20、 K8s的Service是什么?答:Pod每次重启或者重新部署,其IP地址都会产生变化,这使得pod间通信和pod与外部通信变得困难,这时候,就需要Service为pod提供一个固定的入口。Service的Endpoint列表通常绑定了一组相同配置的pod,通过负载均衡的方式把外界请求分配到多个pod上21、 k8s是怎么进行服务注册的?答:Pod启动后会加载当前环境所有Service信息,以便不同Pod根据Service名进行通信。22、 k8s集群外流量怎么访问Pod?答:可以通过Service的NodePort方式访问,会在所有节点监听同一个端口,比如:30000,访问节点的流量会被重定向到对应的Service上面。23、 k8s数据持久化的方式有哪些?答:1)EmptyDir(空目录):没有指定要挂载宿主机上的某个目录,直接由Pod内保部映射到宿主机上。类似于docker中的manager volume。主要使用场景:只需要临时将数据保存在磁盘上,比如在合并/排序算法中;作为两个容器的共享存储,使得第一个内容管理的容器可以将生成的数据存入其中,同时由同一个webserver容器对外提供这些页面。emptyDir的特性:同个pod里面的不同容器,共享同一个持久化目录,当pod节点删除时,volume的数据也会被删除。如果仅仅是容器被销毁,pod还在,则不会影响volume中的数据。总结来说:emptyDir的数据持久化的生命周期和使用的pod一致。一般是作为临时存储使用。2)Hostpath:将宿主机上已存在的目录或文件挂载到容器内部。类似于docker中的bind mount挂载方式。这种数据持久化方式,运用场景不多,因为它增加了pod与节点之间的耦合。一般对于k8s集群本身的数据持久化和docker本身的数据持久化会使用这种方式,可以自行参考apiService的yaml文件,位于:/etc/kubernetes/main…目录下。3)PersistentVolume(简称PV):基于NFS服务的PV,也可以基于GFS的PV。它的作用是统一数据持久化目录,方便管理。在一个PV的yaml文件中,可以对其配置PV的大小,指定PV的访问模式:ReadWriteOnce:只能以读写的方式挂载到单个节点;ReadOnlyMany:能以只读的方式挂载到多个节点;ReadWriteMany:能以读写的方式挂载到多个节点。以及指定pv的回收策略:recycle:清除PV的数据,然后自动回收;Retain:需要手动回收;delete:删除云存储资源,云存储专用;PS:这里的回收策略指的是在PV被删除后,在这个PV下所存储的源文件是否删除)。若需使用PV,那么还有一个重要的概念:PVC,PVC是向PV申请应用所需的容量大小,K8s集群中可能会有多个PV,PVC和PV若要关联,其定义的访问模式必须一致。定义的storageClassName也必须一致,若群集中存在相同的(名字、访问模式都一致)两个PV,那么PVC会选择向它所需容量接近的PV去申请,或者随机申请。
彻底搞定Dart的异步(下)
三. Dart的异步补充3.1. 任务执行顺序3.1.1. 认识微任务队列在前面学习学习中,我们知道Dart中有一个事件循环(Event Loop)来执行我们的代码,里面存在一个事件队列(Event Queue),事件循环不断从事件队列中取出事件执行。但是如果我们严格来划分的话,在Dart中还存在另一个队列:微任务队列(Microtask Queue)。微任务队列的优先级要高于事件队列;也就是说事件循环都是优先执行微任务队列中的任务,再执行 事件队列 中的任务;那么在Flutter开发中,哪些是放在事件队列,哪些是放在微任务队列呢?所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等;而微任务通常来源于Dart内部,并且微任务非常少。这是因为如果微任务非常多,就会造成事件队列排不上队,会阻塞任务队列的执行(比如用户点击没有反应的情况);说道这里,你可能已经有点凌乱了,在Dart的单线程中,代码到底是怎样执行的呢?1、Dart的入口是main函数,所以main函数中的代码会优先执行;2、main函数执行完后,会启动一个事件循环(Event Loop)就会启动,启动后开始执行队列中的任务;3、首先,会按照先进先出的顺序,执行 微任务队列(Microtask Queue)中的所有任务;4、其次,会按照先进先出的顺序,执行 事件队列(Event Queue)中的所有任务;? 代码执行顺序3.1.2. 如何创建微任务在开发中,我们可以通过dart中async下的scheduleMicrotask来创建一个微任务:import "dart:async";
main(List<String> args) {
scheduleMicrotask(() {
print("Hello Microtask");
});
}在开发中,如果我们有一个任务不希望它放在Event Queue中依次排队,那么就可以创建一个微任务了。Future的代码是加入到事件队列还是微任务队列呢?Future中通常有两个函数执行体:Future构造函数传入的函数体then的函数体(catchError等同看待)那么它们是加入到什么队列中的呢?Future构造函数传入的函数体放在事件队列中then的函数体要分成三种情况:情况一:Future没有执行完成(有任务需要执行),那么then会直接被添加到Future的函数执行体后;情况二:如果Future执行完后就then,该then的函数体被放到如微任务队列,当前Future执行完后执行微任务队列;情况三:如果Future是链式调用,意味着then未执行完,下一个then不会执行;// future_1加入到eventqueue中,紧随其后then_1被加入到eventqueue中
Future(() => print("future_1")).then((_) => print("then_1"));
// Future没有函数执行体,then_2被加入到microtaskqueue中
Future(() => null).then((_) => print("then_2"));
// future_3、then_3_a、then_3_b依次加入到eventqueue中
Future(() => print("future_3")).then((_) => print("then_3_a")).then((_) => print("then_3_b"));3.1.3. 代码执行顺序我们根据前面的规则来学习一个终极的代码执行顺序案例:import "dart:async";
main(List<String> args) {
print("main start");
Future(() => print("task1"));
final future = Future(() => null);
Future(() => print("task2")).then((_) {
print("task3");
scheduleMicrotask(() => print('task4'));
}).then((_) => print("task5"));
future.then((_) => print("task6"));
scheduleMicrotask(() => print('task7'));
Future(() => print('task8'))
.then((_) => Future(() => print('task9')))
.then((_) => print('task10'));
print("main end");
}代码执行的结果是:main start
main end
task7
task1
task6
task2
task3
task5
task4
task8
task9
task10代码分析:1、main函数先执行,所以main start和main end先执行,没有任何问题;2、main函数执行过程中,会将一些任务分别加入到EventQueue和MicrotaskQueue中;3、task7通过scheduleMicrotask函数调用,所以它被最早加入到MicrotaskQueue,会被先执行;4、然后开始执行EventQueue,task1被添加到EventQueue中被执行;5、通过final future = Future(() => null);创建的future的then被添加到微任务中,微任务直接被优先执行,所以会执行task6;6、一次在EventQueue中添加task2、task3、task5被执行;7、task3的打印执行完后,调用scheduleMicrotask,那么在执行完这次的EventQueue后会执行,所以在task5后执行task4(注意:scheduleMicrotask的调用是作为task3的一部分代码,所以task4是要在task5之后执行的)8、task8、task9、task10一次添加到EventQueue被执行;事实上,上面的代码执行顺序有可能出现在面试中,我们开发中通常不会出现这种复杂的嵌套,并且需要完全搞清楚它的执行顺序;但是,了解上面的代码执行顺序,会让你对EventQueue和microtaskQueue有更加深刻的理解。3.2. 多核CPU的利用3.2.1. Isolate的理解在Dart中,有一个Isolate的概念,它是什么呢?我们已经知道Dart是单线程的,这个线程有自己可以访问的内存空间以及需要运行的事件循环;我们可以将这个空间系统称之为是一个Isolate;比如Flutter中就有一个Root Isolate,负责运行Flutter的代码,比如UI渲染、用户交互等等;在 Isolate 中,资源隔离做得非常好,每个 Isolate 都有自己的 Event Loop 与 Queue,Isolate 之间不共享任何资源,只能依靠消息机制通信,因此也就没有资源抢占问题。但是,如果只有一个Isolate,那么意味着我们只能永远利用一个线程,这对于多核CPU来说,是一种资源的浪费。如果在开发中,我们有非常多耗时的计算,完全可以自己创建Isolate,在独立的Isolate中完成想要的计算操作。如何创建Isolate呢?创建Isolate是比较简单的,我们通过Isolate.spawn就可以创建了:import "dart:isolate";
main(List<String> args) {
Isolate.spawn(foo, "Hello Isolate");
}
void foo(info) {
print("新的isolate:$info");
}3.2.2. Isolate通信机制但是在真实开发中,我们不会只是简单的开启一个新的Isolate,而不关心它的运行结果:我们需要新的Isolate进行计算,并且将计算结果告知Main Isolate(也就是默认开启的Isolate);Isolate 通过发送管道(SendPort)实现消息通信机制;我们可以在启动并发Isolate时将Main Isolate的发送管道作为参数传递给它;并发在执行完毕时,可以利用这个管道给Main Isolate发送消息;import "dart:isolate";
main(List<String> args) async {
// 1.创建管道
ReceivePort receivePort= ReceivePort();
// 2.创建新的Isolate
Isolate isolate = await Isolate.spawn<SendPort>(foo, receivePort.sendPort);
// 3.监听管道消息
receivePort.listen((data) {
print('Data:$data');
// 不再使用时,我们会关闭管道
receivePort.close();
// 需要将isolate杀死
isolate?.kill(priority: Isolate.immediate);
});
}
void foo(SendPort sendPort) {
sendPort.send("Hello World");
}但是我们上面的通信变成了单向通信,如果需要双向通信呢?事实上双向通信的代码会比较麻烦;Flutter提供了支持并发计算的compute函数,它内部封装了Isolate的创建和双向通信;利用它我们可以充分利用多核心CPU,并且使用起来也非常简单;注意:下面的代码不是dart的API,而是Flutter的API,所以只有在Flutter项目中才能运行main(List<String> args) async {
int result = await compute(powerNum, 5);
print(result);
}
int powerNum(int num) {
return num * num;
}
Berkley CS162 操作系统第一课文字版-课程介绍(上)
熟肉视频地址:CS162操作系统课程第一课-课程介绍(上)CS162操作系统课程第一课-课程介绍(下)第一节课主要是关于课程介绍以及操作系统是什么、为什么这么重要的简介。现代最伟大的发明之一是互联网,它把全世界不同规模的设备都通过统一的网络连接在了一起:互联网的发展很迅猛,像最初的ARPANET,不能处理超过256个设备,大概是在90年代初万维网开始发展的时候,互联网突然变成了很多人可以使用的东西,现在我们的互联网上大概有 45 亿台设备,覆盖了世界的 60% 的人口。另一件有趣的事情是在这张图片中也有设备的多样性:我们再来看看贝尔定律,他表示一个人拥有的设备的数量:最初,数百万人使用一台计算机,然后随着发展,现在你们每个人可能都有数百个设备在为你们工作,比如现代汽车有数百个处理器,你们都有手机,笔记本电脑等等。每个人的电脑数量随着电脑的大小变小而增加,这很有趣。如何让这么多系统正常运作并且连接在一起,我们就要考虑这个耗时尺度的问题:这个图也是大家经常能看到的,大小越小的存储离 CPU 越近访问速度越快,大小越大离 CPU 越远访问速度越慢。不管怎样,操作系统必须在这些不同的耗时尺度正常运作,让系统正常运行。操作系统基本上是所有这些的核心,你在底层技术上不断取得令人难以置信的进步的同时,会造成每个设备都有一些不同的差异性,每一代技术的发展都会有所不同,但是不管硬件有多复杂,你都要为应用程序提供一个一致的编程抽象;同时,需要管理不同应用之间的资源共享,我们连接的设备越多,可以共享的资源就越多。另外,到这学期的最后三分之一的时候,我们将开始讨论其中的一些非常有趣的点对点系统,这样我们可以拥有跨越许多设备的巨大存储系统。我们会学习操作系统的关键组成:进程线程,并发,调度,协调地址空间保护,隔离,共享,安全通信,协议持久化存储,事务,一致性,弹性设备接口在你想访问一个网页的时候,背后发生了什么?首先,会发出一个 DNS 请求试图找出这个网站的 IP 地址,这个请求会到网络上的 DNS 服务器,他们会返回有用的信息,之后使用这些信息,将实际网页请求发出;经过网络路由,之后它可能会被送到一个有负载均衡器的数据中心;然后将从几个可能的设备中选择一个可用的设备;然后,它可能会从页面存储中进行搜索和检索信息,把它拼成你可以用的页面之后返回。一旦我们开始深入思考这个流程中的细节,就会有很多有趣的问题,比如 DNS 服务器是如何保持一致的,为什么很难黑进它们呢?事实上,在 2000 年中期,有人侵入了它们。还有就是你如何确保数据包有足够的优先权当它们进入一个操作系统时,确保你的特响应查询不会被延迟很长时间,这是一个调度相关的问题。希望在这堂课结束时,你将对操作系统的各个部分有足够的了解,能够帮助你对这些问题有更好的理解。那什么是操作系统呢?这方面其实没有统一的概念,但是我们可以从操作系统做的事情来理解什么是操作系统。操作系统负责:内存管理IO管理CPU 调度通信多任务处理或多处理器编程操作系统和什么有关呢?文件系统?多媒体系统?用户界面?还有浏览器?对于操作系统的定义,我们很难给出,一个近似的定义是当你订购操作系统时供应商提供的所有东西,但是不同的供应商提供的东西差别很大,它可能是电脑上一直在运行的一个程序即内核(kernel),随着学期的进行,你们会学到很多关于内核的知识。没有人会否认内核是操作系统的核心,但是当我们试图深入研究什么是操作系统时,你们要记住,我们要讲的是它的作用和重要的部分,但也许你永远不会完全知道到底操作系统是什么。其实操作系统可以理解为是一个典型的软件层,它提供应用程序对硬件资源的访问,这是对复杂硬件设备的方便抽象以及对共享资源的受保护访问与通信,以及提供安全性保护和认证。