Kubernetes优雅下线微服务应用
对于微服务来说,服务的优雅上下线是必要的。就上线来说,如果组件或者容器没有启动成功,就不应该对外暴露服务,对于下线来说,如果机器已经停机了,就应该保证服务已下线,如此可避免上游流量进入不健康的机器。
优雅下线
基础下线(Spring/SpringBoot/内置容器)
首先JVM本身是支持通过shutdownHook的方式优雅停机的。
Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { close(); } });
此方式支持在以下几种场景优雅停机:
- 程序正常退出
- 使用System.exit()
- 终端使用Ctrl+C
- 使用Kill pid干掉进程
那么如果你偏偏要kill -9
程序肯定是不知所措的。
而在Springboot中,其实已经帮你实现好了一个shutdownHook,支持响应Ctrl+c或者kill -15 TERM信号。
随便启动一个应用,然后Ctrl+c一下,观察日志就可知, 它在AnnotationConfigEmbeddedWebApplicationContext这个类中打印出了疑似Closing…的日志,真正的实现逻辑在其父类
AbstractApplicationContext中(这个其实是spring中的类,意味着什么呢,在spring中就支持了对优雅停机的扩展)。
public void registerShutdownHook() { if (this.shutdownHook == null) { this.shutdownHook = new Thread() { public void run() { synchronized(AbstractApplicationContext.this.startupShutdownMonitor) { AbstractApplicationContext.this.doClose(); } } }; Runtime.getRuntime().addShutdownHook(this.shutdownHook); } } public void destroy() { this.close(); } public void close() { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.doClose(); if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException var4) { ; } } } } protected void doClose() { if (this.active.get() && this.closed.compareAndSet(false, true)) { if (this.logger.isInfoEnabled()) { this.logger.info("Closing " + this); } LiveBeansView.unregisterApplicationContext(this); try { this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this))); } catch (Throwable var3) { this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3); } if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable var2) { this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2); } } this.destroyBeans(); this.closeBeanFactory(); this.onClose(); this.active.set(false); } }
我们能对它做些什么呢,其实很明显,在doClose方法中它发布了一个ContextClosedEvent的方法,不就是给我们扩展用的么。于是我们可以写个监听器监听ContextClosedEvent,在发生事件的时候做下线逻辑,对微服务来说即是从注册中心中注销掉服务。
@Component public class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> { @Override public void onApplicationEvent(ContextClosedEvent contextClosedEvent){ //注销逻辑 zookeeperRegistry.unregister(mCurrentServiceURL); ... } }
可能会有疑问的是,微服务中一般来说,注销服务往往是优雅下线的第一步,接着才会执行停机操作,那么这个时候流量进来怎么办呢?
个人会建议是,在注销服务之后就可开启请求挡板拒绝流量了,通过微服务框架本身的故障转移功能去处理被拒绝的流量即可。
Docker中的下线
好有人说了,我用docker部署服务,支不支持优雅下线。
那来看看docker的一些停止命令都会干些啥:
一般来说,正常人可能会用docker stop或者docker kill 命令去关闭容器(当然如果上一步注册了USR2自定义信息,可能会通过docker exec kill -12去关闭)。
对于docker stop来说,它会发一个SIGTERM(kill -15 term信息)给容器的PID1进程,并且默认会等待10s,再发送一个SIGKILL(kill -9 信息)给PID1。
那么很明显,docker stop允许程序有个默认10s的反应时间去做一下优雅停机的操作,程序只要能对kill -15 信号做些反应就好了,如上一步描述。那么这是比较良好的方式。
当然如果shutdownHook方法执行了个50s,那肯定不优雅了。可以通过docker stop -t 加上等待时间。
外置容器的shutdown脚本(Jetty)
如果非要用外置容器方式部署(个人认为浪费资源并提升复杂度)。那么能不能优雅停机呢。
可以当然也是可以的,这里有两种方式:
首先RPC框架本身提供优雅上下线接口,以供调用来结束整个应用的生命周期,并且提供扩展点供开发者自定义服务下线自身的停机逻辑。同时调用该接口的操作会封装成一个preStop操作固化在jetty或者其他容器的shutdown脚本中,保证在容器停止之前先调用下线接口结束掉整个应用的生命周期。shutdown脚本中执行类发起下线服务 -> 关闭端口 -> 检查下线服务直至完成 -> 关闭容器
的流程。
而更简单的另一种方法是直接在脚本中加入kill -15命令。
优雅上线
优雅上线相对来说可能会更加困难一些,因为没有什么默认的实现方式,但是总之呢,一个原则就是确保端口存在之后才上线服务。
springboot内置容器优雅上线
这个就很简单了,并且业界在应用层面的优雅上线均是在内置容器的前提下实现的,并且还可以配合一些列健康检查做文章。
参看sofa-boot的健康检查的源码,它会在程序启动的时候先对springboot的组件做一些健康检查,然后再对它自己搞得sofa的一些中间件做健康检查,整个健康检查的流程完毕之后(sofaboot 目前是没法对自身应用层面做健康检查的,它有写相关接口,但是写死了port is ready…)才会暴露服务或者说优雅上线,那么它健康检查的时机是什么时候呢:
@Override public void onApplicationEvent(ContextRefreshedEvent event) { healthCheckerProcessor.init(); healthIndicatorProcessor.init(); afterHealthCheckCallbackProcessor.init(); publishBeforeHealthCheckEvent(); readinessHealthCheck(); }
可以看到它是监听了ContextRefreshedEvent这个事件。在内置容器模式中,内置容器模式的start方法是在refreshContext方法中,方法执行完成之后发布一个ContextRefreshedEvent事件,也就是说在监听到这个事件的时候,内置容器必然是启动成功了的。
但ContextRefreshedEvent这个事件,在一些特定场景中由于种种原因,ContextRefreshedEvent会被监听到多次,没有办法保证当前是最后一次event,从而正确执行优雅上线逻辑。
在springboot中还有一个更加靠后的事件,叫做ApplicationReadyEvent,它的发布藏在了afterRefresh还要后面的那一句listeners.finished(context, null)
中,完完全全可以保证内置容器 端口已经存在了,所以我们可以监听这个事件去做优雅上线的逻辑,甚至可以把中间件相关的健康检查集成在这里。
@Component public class GracefulStartupListener implements ApplicationListener<ApplicationReadyEvent> { @Override public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent){ //注册逻辑 优雅上线 apiRegister.register(urls); ... } }
外置容器(Jetty)优雅上线
目前大多数应用的部署模式不管是jetty部署模式还是docker部署模式(同样使用jetty镜像),本质上用的都是外置容器。那么这个情况就比较困难了,至少在应用层面无法观察到外部容器的运行状态,并且容器本身没有提供什么hook给你实现。
那么和优雅上线一样,需要RPC框架提供优雅上线接口来初始化整个应用的生命周期,并且提供扩展点给开发者供执行自定义的上线逻辑(上报版本探测信息等)。同样将调用这个接口封装成一个postStart操作,固化在jetty等外置容器的startup脚本中,保证应用在容器启动之后在上线。容器执行类似启动容器 -> 健康检查 -> 上线服务逻辑 -> 健康上线服务直至完成
的流程。
hyperledger-fabric v1.2 身份验证
什么是身份?
区块链网络中的不同参与者包括同伴,订购者,客户端应用程序,管理员等。这些参与者中的每一个 – 网络内部或外部能够使用服务的活动元素 – 都具有封装在X.509数字证书中的数字身份。这些身份确实很重要,因为它们确定了对资源的确切权限以及对参与者在区块链网络中拥有的信息的访问权限。
此外,数字身份还具有Fabric用于确定权限的一些其他属性,并且它为身份和关联属性的并集提供了特殊名称 – 主体。Principal就像userIDs或groupIDs,但更灵活一点,因为它们可以包含actor的身份的各种属性,例如actor的组织,组织单位,角色甚至是actor的特定身份。当我们谈论主体时,它们是决定其权限的属性。
要使身份可以验证,它必须来自可信任的权威机构。一成员的服务提供商 (MSP)是这是如何实现的面料。更具体地说,MSP是定义管理该组织的有效身份的规则的组件。Fabric中的默认MSP实现使用X.509证书作为身份,采用传统的公钥基础结构(PKI)分层模型(稍后将详细介绍PKI)。
一个简单的场景来解释身份的使用
想象一下,你去超市购买一些杂货。在结账时,您会看到一个标志,表示只接受Visa,Mastercard和AMEX卡。如果您尝试使用其他卡付款 – 我们称之为“ImagineCard” – 无论该卡是否真实且您的帐户中有足够的资金都无关紧要。它不会被接受。
拥有有效的信用卡是不够的 – 它也必须被商店接受!PKI和MSP以相同的方式协同工作 – PKI提供身份列表,MSP说哪些是参与网络的给定组织的成员。
PKI证书颁发机构和MSP提供了类似的功能组合。PKI就像一个卡提供商 – 它分配了许多不同类型的可验证身份。另一方面,MSP类似于商店接受的卡提供商列表,确定哪些身份是商店支付网络的可信成员(参与者)。MSP将可验证的身份转变为区块链网络的成员。
让我们更详细地深入研究这些概念。
什么是PKI?
公钥基础结构(PKI)是一组互联网技术,可在网络中提供安全通信。这是PKI是放小号在HTTPS -如果你正在阅读的网页浏览器这个文档,你可能会使用PKI来确保它来自一个验证源。
公钥基础结构(PKI)的元素。PKI由向各方(例如,服务的用户,服务提供商)发布数字证书的证书颁发机构组成,然后他们使用它们在与其环境交换的消息中对自己进行身份验证。CA的证书吊销列表(CRL)构成不再有效的证书的参考。证书的撤销可能由于多种原因而发生。例如,证书可能被撤销,因为与证书相关联的加密私有材料已被公开。
虽然区块链网络不仅仅是一个通信网络,但它依赖于PKI标准来确保各个网络参与者之间的安全通信,并确保在区块链上发布的消息得到适当的认证。因此,了解PKI的基础知识以及为什么MSP如此重要是非常重要的。
PKI有四个关键要素:
- 数字证书
- 公钥和私钥
- 证书颁发机构
- 证书撤销列表
数字证书
数字证书是包含与证书持有者有关的一组属性的文档。最常见的证书类型是符合X.509标准的证书,它允许在其结构中编码一方的识别细节。
例如,玛丽·莫里斯米切尔汽车的制造部在密歇根州底特律可能有一个带有数字证书SUBJECT
的属性C=US
,ST=Michigan
,L=Detroit
,,,。玛丽的证书类似于她的政府身份证 – 它提供了玛丽的信息,她可以用来证明关于她的重要事实。X.509证书中还有许多其他属性,但现在让我们专注于这些。O=Mitchell Cars
OU=Manufacturing
CN=Mary Morris /UID=123456
描述一个名为Mary Morris的派对的数字证书。Mary是SUBJECT
证书的副本,突出显示的SUBJECT
文本显示了关于Mary的重要事实。如您所见,证书还包含更多信息。最重要的是,Mary的公钥是在她的证书中分发的,而她的私人签名密钥则不是。此签名密钥必须保密。
重要的是,玛丽的所有属性都可以使用称为密码学(字面意思,“ 秘密写作 ”)的数学技术进行记录,这样篡改将使证书无效。只要对方信任证书颁发者(称为证书颁发机构(CA)),密码学就允许Mary将证书提交给其他人以证明其身份。只要CA安全地保存某些加密信息(意味着它自己的私人签名密钥),任何阅读证书的人都可以确定有关Mary的信息没有被篡改 – 它将始终具有Mary Morris的特定属性。将Mary的X.509证书视为无法改变的数字身份证。
身份验证,公钥和私钥
身份验证和消息完整性是安全通信中的重要概念。身份验证要求确保交换消息的各方创建特定消息的身份。对于具有“完整性”的消息意味着在其传输期间不能被修改。例如,您可能希望确保与真正的玛丽莫里斯而不是模仿者进行沟通。或者,如果Mary向您发送了一条消息,您可能希望确保其在传输过程中没有被其他任何人篡改过。
传统的身份验证机制依赖于数字签名,顾名思义,它允许一方对其消息进行数字签名。数字签名还可以保证签名消息的完整性。
从技术上讲,数字签名机制要求每一方保存两个加密连接的密钥:广泛可用的公钥和充当认证锚的私钥,以及用于在消息上产生数字签名的私钥 。数字签名消息的接收者可以通过检查附加签名在预期发送者的公钥下是否有效来验证接收消息的来源和完整性。
私钥和相应公钥之间的唯一关系是使安全通信成为可能的加密魔法。密钥之间的唯一数学关系使得私钥可以用于在仅对应的公钥可以匹配的消息上产生签名,并且仅在相同的消息上。
在上面的示例中,Mary使用她的私钥对邮件进行签名。任何使用她的公钥查看签名消息的人都可以验证签名。
证书撤销列表
证书撤销列表(CRL)很容易理解 – 它只是CA知道由于某种原因而被撤销的证书的引用列表。如果您回想一下商店场景,CRL就像被盗信用卡列表一样。
当第三方想要验证另一方的身份时,它首先检查颁发CA的CRL以确保证书尚未被撤销。验证者不必检查CRL,但如果不是,则他们冒着接受受损身份的风险。
使用CRL检查证书是否仍然有效。如果模仿者试图将受损的数字证书传递给验证方,则可以首先检查颁发CA的CRL,以确保其未列为不再有效。
请注意,被撤销的证书与证书过期非常不同。撤销的证书尚未过期 – 按其他措施,它们是完全有效的证书。
阿里云SLB配置https证书
nginx配置了https证书,现在想把博客放到slb负载均衡上面,由于证书是 crt和key类型,已经转换成Pem ,访问网站的时候报502,通过绑定本地Hosts去测试正常,唯独浏览器不行。
在阿里云help看到slb 配置https 证书的话,后端ecs不需要监听 443 端口,查看对应 443 的 监听后端ecs 服务器健康检查是异常的。 登陆对应后端服务器,执行下
echo -e “HEAD / HTTP/1.0\r\n” |nc -t 内网IP 端口
七层的监听健康检查的判断依据是 head 请求获取到的状态码判断健康检查是否正常,查看之前提供的截图是 400 状态码,在健康检查配置中 将4xx 勾选上,或者是设置健康检查路径为一个 head 请求 返回 200 的 页面文件,再查看是否正常
后端n’ginx是否需要配置强制https?
答:如果没有http 强制 跳转https 的需求,或者是slb 已经配置了跳转,后端ecs 是不会配置的。
1、没有开启强制https,页面出现样式乱掉
2、配置了一个正常页面test.html健康检查200, 会出现502
3、后端开启强制https则会出现502
关于样式乱的问题,您通过谷歌的F12 访问
访问样式改变的问题,查看下是否是https 的页面中的相关资源是通过http 的方式调用的导致显示不正常。
502问题:
测试是重定向过多,在ecs 上配置http 强制跳转,需要配置以下对应监听。 SLB 443 -> ECS 80(nginx) SLB 80 -> ECS 81(nginx) 后端ecs81端口专门做重定向,重定向到SLB的443端口上,80端口提供网站服务即可。可以新建一个站点监听该端口,这个站点是用来做重定向的。
SLB 80 -> ECS 81(nginx) 的话,其它的站点没有https会不会受到影响?
如果后端已经配置了HTTPS了,那么SLB使用TCP 443监听好了,这样不需要调整后端的ECS的设置。 如果需要有跳转,可以使用 SLB 80监听对应后端http站点,后端HTTP设置跳转, 跳转到SLB 443端口 注意:HTTP站点上跳转时不能使用$server 一定要写SLB的地址
k8s部署elk收集应用日志信息
- 一共有两个Pod:ELK和web应用;
- ELK的Pod会暴露两个服务,一个暴露logstash的5044端口,给filebeat用,另一个暴露kibana的5601端口,给搜索日志的用户访问的时候用;
- web应用暴露一个服务,给用户通过浏览器访问;
实战步骤简介
- 部署ELK的pod和服务;
- 部署web应用的pod和服务;
- web应用的pod从一个扩展为三个;
- 体验ELK;
- 部署ELK
一、部署ELK
1. ssh登录到可以执行kubectl命令的机器上去;
2. 创建elk的部署脚本elkhost.yaml,内容如下:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: elkhost spec: replicas: 1 template: metadata: labels: name: elkhost spec: containers: - name: elkhost image: sebp/elk:622 tty: true ports: [{ "containerPort": 5601 },{ "containerPort": 5044 }]
如上所示,暴露了两个端口:kibana的5601和logstash的5044;
3. 在elkhost.yaml所在目录执行命令kubectl create -f elkhost.yaml,即可创建elk对应的pod,如下:
root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl get pods NAME READY STATUS RESTARTS AGE elkhost-54c9bbd8d5-ffq68 1/1 Running 0 3m
4. 将kibana的5601端口以NodePort的方式对外暴露,这样外部就可以通过节点IP地址来访问kibana服务了,创建部署脚本elkkibana-svc.yaml,kibana的服务通过node节点的30001端口对外暴露,内容如下:
apiVersion: v1 kind: Service metadata: name: elkkibana spec: type: NodePort ports: - port: 5601 nodePort: 30001 selector: name: elkhost
5. 将logstash的5044端口以ClusterIP的方式对外暴露,这样其他pod的filebeat就可以通过服务名加5044端口来访问logstash服务了,创建部署脚本elkhost-svc.yaml,logstash的服务通过5044端口对K8S内部的pod暴露,内容如下:
apiVersion: v1 kind: Service metadata: name: elkhost spec: type: ClusterIP ports: - port: 5044 targetPort: 5044 selector: name: elkhost
6. 在elkhost-svc.yaml所在目录执行命令kubectl create -f elkhost-svc.yaml && kubectl create -f elkkibana-svc.yaml,即可创建elkhost和elkkibana这两个服务,如下:
root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl create -f elkhost-svc.yaml && kubectl create -f elkkibana-svc.yaml service "elkhost" created service "elkkibana" created root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elkhost ClusterIP 10.43.103.244 <none> 5044/TCP 9s elkkibana NodePort 10.43.219.137 <none> 5601:30001/TCP 9s kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 16d
7. 执行命令查看elkhost的pod部署在K8S的哪个node节点上:
root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl describe pod elk Name: elkhost-54c9bbd8d5-ffq68 Namespace: default Node: willzhao-vostro-3267/192.168.31.89 Start Time: Mon, 30 Apr 2018 16:22:04 +0800 Labels: name=elkhost pod-template-hash=1075668481 Annotations: kubernetes.io/created-by={"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicaSet","namespace":"default","name":"elkhost-54c9bbd8d5","uid":"9096cde8-4c4f-11e8-a776-024f8a041a1a"... Status: Running ...
如上所示,Node: willzhao-vostro-3267/192.168.31.89显示了这个pod部署的节点IP是192.168.31.89;
8. 打开浏览器,输入192.168.31.89:30001,即可访问到Kibana服务,如下图:
二、部署Web应用
1. 创建elkwebdemo的部署脚本elkwebdemo.yaml,内容如下:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: elkwebdemo spec: replicas: 1 template: metadata: labels: name: elkwebdemo spec: containers: - name: elkwebdemo image: bolingcavalry/elkdemo:0.0.1-SNAPSHOT tty: true ports: - containerPort: 8080
2. 在elkwebdemo.yaml所在目录执行命令kubectl create -f elkwebdemo.yaml,即可创建elk对应的pod,如下:
root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl get pod NAME READY STATUS RESTARTS AGE elkhost-944bcbcd4-8vpbs 1/1 Running 0 4m elkwebdemo-dddbcfc6f-x4pk6 1/1 Running 0 9s
3. 创建elk对外服务的部署脚本elkwebdemo-svc.yaml,web的服务通过node节点的30002端口对外暴露,内容如下:
apiVersion: v1 kind: Service metadata: name: elkwebdemo spec: type: NodePort ports: - port: 8080 nodePort: 30002 selector: name: elkwebdemo
5. 在elkwebdemo-svc.yaml所在目录执行命令kubectl create -f elkwebdemo-svc.yaml,即可创建elkwebdemo对应的pod,如下:
root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elkhost ClusterIP 10.43.103.244 <none> 5044/TCP 6m elkkibana NodePort 10.43.219.137 <none> 5601:30001/TCP 6m kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 16d
6. 执行命令查看elkwebdemo的pod部署在K8S的哪个node节点上:
root@willzhao-Vostro-3267:/usr/local/work/elkk8s# kubectl describe pod elkwebdemo Name: elkwebdemo-dddbcfc6f-8bbrb Namespace: default Node: willzhao-vostro-3267/192.168.31.89 Start Time: Mon, 30 Apr 2018 16:28:37 +0800 Labels: name=elkwebdemo pod-template-hash=888679729 Annotations: kubernetes.io/created-by={"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicaSet","namespace":"default","name":"elkwebdemo-dddbcfc6f","uid":"7a9364d1-4c50-11e8-a776-024f8a041a1... Status: Running ...
如上所示,Node: willzhao-vostro-3267/192.168.31.89显示了这个pod部署的节点IP是192.168.31.89;
7. 打开浏览器,输入http://192.168.31.89:30002/hello/tom,即可访问到web服务,如下图:
三、配置kibana
再次打开kibana页面,如下图所示,点击红框中的“Discover”,发现已经搜集到了上报的日志,如绿框所示:
如下图设置:创建索引名称
继续设置,如下图: 设置时间格式
再此点击左上角的”Discover”,既可开始搜索web应用日志,如下图:
四、Web应用扩容
现在我们模拟生产环境的在线扩容:
1. 将web应用从一个扩展到三个,执行以下命令:
kubectl scale deployment elkwebdemo --replicas=3
2. 可以看到web应用对应的pod已经扩展了,如下所示:
root@willzhao-Vostro-3267:~# kubectl scale deployment elkwebdemo --replicas=3 deployment "elkwebdemo" scaled root@willzhao-Vostro-3267:~# kubectl get pod NAME READY STATUS RESTARTS AGE elkhost-944bcbcd4-8vpbs 1/1 Running 0 1h elkwebdemo-dddbcfc6f-crzcp 1/1 Running 0 11s elkwebdemo-dddbcfc6f-tsppk 1/1 Running 0 11s elkwebdemo-dddbcfc6f-x4pk6 1/1 Running 0 1h
3. 为了能多上报一些日志,在浏览器上多刷新几次这个地址:http://192.168.31.89:30002/hello/tom
4. 回到kibana页面,如下图,点击红框中的“host”,展开的信息显示,这些日志来自三个host:
习惯晚睡
很久没有像今天这样坐下来写东西了,也不知道抽的什么风,可能是实在想不到大好的周末能做些什么吧。其实也倒不是心血来潮,最近一段时间都在思考过去,规划以后的方向,零零碎碎的记录了一些,但至今未能成文。但今天不是讲规划,而是——习惯,晚睡的习惯。
很多人可能会反驳晚一点睡算什么,此时不任性何时才任性。我倒不是说晚睡不行,问题是我因何而晚睡。最近一直在看池建强老师的《MacTalk-人生元编程》这本书,无论Apple的联合创始人沃茨,还是微软的艾伦,在初期哪一个不是没日没夜的工作。当然咱无法与这些传奇人物比拟,但至少说明熬夜,要值得,如果我是在熬夜积累自己,或能对改善以后的状况,我就觉得值得。
可是现在我们大多数人熬夜在干嘛呢,包括我也有反思自己。
不过较好的一点是没那么严重,平日晚一点到1点,因为考虑到第二天还要上班这样一个事实,一到周末,又毅然决然的熬到凌晨三四点,而大部分情况是在看电影电视,似乎周五的晚上是从11点开始,周六的白天从下午一点开始……
写到这里,我倒不是谴责自己晚睡这样一个习惯,因为我相信肯定还是有很大一部分人跟我一样有着凌晨一点才睡的习惯,更不是告诉自己“熬夜对身体有害”这样一个众所周知的烂道理。有些人熬夜是迫不得已,而有些人是因为三四年时间形成的习惯。熬夜疯一下可以,问题是干什么。有多少人可以熬夜而不用考虑明天。
最近上班没什么特别的工作,拿出不少时间去简书上看看文章,或搜到某个人博客,就闲来无事的点开Archive列表,随机点开看看。看到人家几年来做的点点滴滴,同龄人群里,我却丝毫想不起那时候我在做什么,依旧是同龄人我现在正在学的东西,人家两三年前就在用了。这也不难解释我现在的处境了。不止一次有过努力追赶别人的想法,可是晚上一回到家,不是迷上电视就是电脑手机。回家前我计划要做的事情实施了吗?这样是会有一点点累,过着如此安逸的生活,这不是我应得的。来自家里的压力,我应该是努力在工作技能上积累,在生活情商上积累,而我没那么多时间,慢慢来…
一个人的努力是孤独的,也是幸运的。毫无理由的晚睡,第二天迷迷糊糊起床,这不是我想要的状态。想要有所作为,可能就因为一个不经意的习惯而磨损了自己的动力。
我想象这样一个场景:
- 早上提前30分钟(七点)起来,轻轻松松的洗漱,打扮,然后从容的吃个早饭,车上看看知乎。
- 上班时间,没事少刷朋友圈,早中晚看几次就行了。中午看看新闻,午休一觉,快速进入工作状态,不能再迷糊半个小时(这也成习惯了吧)。
- 下午三四点上班累了,走动走动,聊聊天,打打水。眼睛酸了,滴滴眼药水(买了别浪费)
- 晚上下班车上,听听歌。
- 10点以后是自己的时间,我可以舒舒服服的洗个澡,不开电脑,然后看一个半小时的书,或者看看新闻和喜欢的节目。
- 周末,九点钟起来。偶尔聚聚,逛逛,但至少拿出一天时间去图书馆,看看电影什么的,家里真不适合学习、阅读。
有人会说,不就是晚睡而已吗,至于这样吗,功利性太强了!
就我目前各方面不稳定因素来说,我觉得有必要这样一个list。把平时和睡前那些零碎时间利用起来,安安静静的坚持多看几书,不再拘泥于自己知道的小圈子,才会发现更多机会。
——闹钟真是一个伟大的发明,它让你放心的入睡,也无情的把你叫醒,为了减轻你的埋怨还允许你贪睡。
高效Linux bash快捷键及alias总结
bash快捷键
习惯使用编辑的快捷键可以大大提高效率,记忆学习过程要有意识的忽略功能键、方向键和数字小键盘。以下快捷键适用在bash处于默认的Emacs模式下,是由一个名为Readline的库实现的,用户可以通过命令bind添加新快捷键,或者修改系统中已经存在的快捷键。(如果你有set -o vi
,就处于 vi 模式就不适用了)
另外下面的内容并不包含所有快捷键,只是我个人适用频率最高的几种,但相信已经可以大大提高工作效率了。以下所有 Alt 键可以以 Esc 键代替。
Ctrl + l
:清除屏幕,同clearCtrl + a
:将光标定位到命令的开头Ctrl + e
:与上一个快捷键相反,将光标定位到命令的结尾Ctrl + u
:剪切光标之前的内容,在输错命令或密码Ctrl + k
:与上一个快捷键相反,剪切光标之后的内容Ctrl + y
:粘贴以上两个快捷键所剪切的内容。Alt+y粘贴更早的内容Ctrl + w
:删除光标左边的参数(选项)或内容(实际是以空格为单位向前剪切一个word)Ctrl + /
:撤销,同Ctrl+x
+Ctrl+u
Ctrl + f
:按字符前移(右向),同→Ctrl + b
:按字符后移(左向),同←Alt + f
:按单词前移,标点等特殊字符与空格一样分隔单词(右向),同Ctrl+→Alt + b
:按单词后移(左向),同Ctrl+←Alt + d
:从光标处删除至字尾。可以Ctrl+y粘贴回来Alt + \
:删除当前光标前面所有的空白字符Ctrl + d
:删除光标处的字符,同Del键。没有命令是表示注销用户Ctrl + h
:删除光标前的字符Ctrl + r
:逆向搜索命令历史,比history好用Ctrl + g
:从历史搜索模式退出,同ESCCtrl + p
:历史中的上一条命令,同↑Ctrl + n
:历史中的下一条命令,同↓Alt + .
:同!$,输出上一个命令的最后一个参数(选项or单词)。
还有如Alt+0 Alt+. Alt+.,表示输出上上一条命令的的第一个单词(即命令)。
另外有一种写法!:n
,表示上一命令的第n个参数,如你刚备份一个配置文件,马上编辑它:cp nginx.conf nginx.conf
,vi !:1
,同vi !^
。!^
表示命令的第一个参数,!$
最后一个参数(一般是使用Alt + .
代替)。
这里提一下按字符或字符串,向左向后搜索字符串的命令:
Ctrl + ]
c :从当前光标处向右定位到字符
c 处Esc
Ctrl + ]
c :从当前光标向左定位到字符
c 处。( bind -P 可以看到绑定信息)Ctrl + r
str :可以搜索历史,也可以当前光标处向左定位到字符串
str,Esc
后可定位继续编辑Ctrl -s
str :从当前光标处向右定位到字符串
str 处,Esc 退出。注意,Ctrl + S
默认被用户控制 XON/XOFF ,需要在终端里执行stty -ixon
或加入profile。
注意上述所有涉及Alt键的实际是Meta键,在xshell中默认是没有勾选“Use Alt key as Meta key”,要充分体验这些键带来的快捷,请在对应的terminal设置。
参考:高效操作Bash ,Bash (Unix shell) Keyboard shortcuts ,bash中的命令基本操作。
常用alias
以下bash中别名设置我还并没有完全使用,也是个人觉得非常有用的(多了记起来也麻烦),所以收集在一起,习惯就好。
/etc/profile.d/alias.sh
:
要去掉别名,请用unalias aliasname
,或者临时执行不用别名,执行原始命令\alias
。
vim编辑器技巧备忘(初级-中级)
“学习vim并且其会成为你最后一个使用的文本编辑器”
学习建议:
丢弃鼠标和小键盘
具有搭配使用各种按键的意识
1. 初级
1.1 编辑模式(Insert Mode)
编辑模式包括以下动作:
- insert:
i
在光标所在字符前插入,I
在当前行首第一个非空格字符前插入 - append:
a
在光标所在字符后插入,A
在行末尾开始插入 - open:
o
在下一行插入新行,O
在光标所在行的上一行插入新行 - replace:
r
将光标处字符替换成r紧接的字符;R
一直替换字符串,知道ESC
键退出,同windows下的Insert键 Ctrl+p
:自动提示[ESC]
:回到普通模式
1.2 普通模式(Normal Mode)
h, j, k, l
,分别对应 左← 下↓ 上↑ 右→:q, :q!, :wq
退出 不保存强行退出 保存退出- 移动光标到当前行首/非空格,同^,:0
$
移动光标到当前行尾,同:$G
移动光标到文档最后一行首30G
转到第30行,同 :309-
光标向上移动9行,同9k9+
光饼向下移动9行,同9[space],9jgg
转到文档第一行(1G)H
移动到屏幕的第一列M
移动到屏幕的中间列L
移动到屏幕的最后列w
移动到下一个单词的首字母,(标点符号认为是一个单词,W表示单词以空格分隔)e
移动到下一个单词的末尾,(E认为单词以空格分隔)b
移动到上一个单词的首字母,(B认为单词以空格分隔)fx
移动到下一个字母是x的位置。Fx向上移动。%
匹配括号移动,包括 (, {, [ ,你需要把光标先移到括号上CTRL+b
向上(前)翻页CTRL+f
向后翻页d
删除开始。其实是放到寄存器中,p或P命令调用。c即change,删完进入编辑模式d$
删除光标到行尾的所有文字,同D。C指令进入编辑模式dw
删除当前字符到单词尾的所有字符。cw删除光标后的单词并进入插入模式,等同替换dd
删除当前一行2dd
删除当前两行x
剪切光标处字符,可以p粘贴出来y
复制开始。yank起来,p或P命令调用yy p
复制当前行并粘贴到下一行5yy p
复制当前行以下5行,在合适的地方粘贴yw p
复制一个单词并粘贴。yi{
复制光标所在{}中的内容。很容易知道ci{ 和 di “ 的意思。u
撤销CTRL+r
重做.
点号重复做上一个命令`.
移动光标到上一次的修改点Ctrl+g
查看我当前位置
1.3 命令模式
/word
向下查找word,n定位到下一个。?word 向上开始查找,同 /word 配合N。* 直接查找光标所在处单词:%s/word1/word2/g
替换所有 word1 为 word2 (^$ []):set noic
区分大小写 (即set no igorecase,set ic不区分大小写):set nu
显示行号。:set nonu相反,不显示行号:set paste
设置为粘贴模式,解决Ctrl+Insert粘贴时缩进错乱问题。:set nopaste设置回默认:se ff=unix
设置文本文件的格式为unix,去掉windows系统文件中的^M。:!ls
执行bash下的命令ls,回车后继续回到vi。一般在一个不存在的目录中创建文件时用到:set all
查看vim说有设置属性值:map
查看绑定的快捷键:marks
查看可用标记
熟练上面的命令,已经可以满足日常工作要求,要提高效率可以学习vim的高级用法,如分屏显示、分页、标签功能、代码折叠、键盘映射。
1.4 可视化模式
visual mode多用于用键盘灵活选择文本。v
或V
键进入,可以实现同时编辑多行(如注释)
2. 中级
2.1 高级组合命令
:sh
暂时离开vi,进入shell命令行:!ls
!表示要执行一个shell命令q:
调出历史命令窗口cc
替换一行,清空一行cw
替换一个单词,进入插入模式ea
在当前单词最后插入vw
visual模式选择一个单词VU
全选一行,转换成大写:5,12 co 13
将5至12行复制到第13行下gg=G
自动缩排文件
%
: 匹配括号移动,包括 (, {, [. ,你需要把光标先移到括号上
*
和#
:匹配光标当前所在的单词,移动光标到下一个(或上一个)匹配单词(*是下一个,#是上一个)
当光标在( )
, [ ]
,< >
, { }
, " "
, ''
内时,可以用ci
, di
, 或yi
,加上(
, [
, <
,{
, "
, '
。这样可以改写/删除/复制( )
内,[ ]
内,< >
内,{ }
内," "
内,' '
内的内容
多行注释(块操作)
- 首先按
esc
进入命令行模式下,按下Ctrl + v
,进入列(也叫区块)模式; - 在行首使用上下键
kj
选择需要注释的多行; - 按下键盘(大写)
I
键,进入插入模式; - 然后输入注释符(“//”、“#”等);
- 最后按下
esc
键
删除多行开头的一个字符与此类似,删除多个字符也只要左右键,然后使用d。
另外一种多行注释的方式就是替换:
:5,9s/^/#/g
添加注释#:5,9s/^#//g
删除注释#:5,9s#^#//#g
添加注释//,同:5,9s/^/\/\/#/g:5,9s#^//##g
删除注释//
多行缩进
[ESC]
, Ctrl+v
选择行 <或>左右缩进,=自动缩进(gg=G的局部功能)
多行行末尾加上 >> ${log}:Ctrl+v /^echo
>>
相当于tab键,一个缩进
多行删除
- 首先在命令模式下,输入
set nu
显示行号; - 通过行号确定你要删除的行;
- 命令输入
:32,65d
,回车键,32-65行就被删除了
自动补全
在Insert模式下,你可以输入一个词的开头,然后按 <C-p>
或是<C-n>
,自动补齐功能就出现了
2.2 分屏(split)
分割窗口(同时编辑多个文件)
vi -o file1 file2
水平分屏,也可以在先打开file1后,使用:sp file2
vi -O file1 file2
垂直分屏,也可以在先打开file1后,使用:vsp file2
Ctrl+w w
光标切换到另一个屏k
关闭当前窗口,如果有改动则提示要先保存。:wqa!
保存退出所有文件Ctrl+w v
左右分割当前打开的文件。Ctrl+w s
上下分割显示Ctrl+w H
左移该屏(大写,L
右移,K
上移—改变vertical为horizontal):set scb
分屏同步滚动
另外一种不分屏,同时编辑多个文件的方法
vi file1 file2
:ls
展示全部打开的文档。其中%
代表正在打开的文件(buffer),#
代表上一个编辑过的文件,a
代表当前激活的buffer空间,+
代表有修改过的内容但还未保存:buffer 2
转到打开的第二个文件。或简写为:b2
:bn
转到下一个文件。没有保存不能离开这个文件。:bp
上一个文件:b#
转到上一个编辑过的文件,同:e #
。类似于多屏中Ctrl+w w
:e file3
编辑一个新的文件Ctrl+g
显示当前文件名和行号,同:f
2.3 折叠(fold)
主要应用在复杂脚本的场合,特别是函数比较多,逻辑结构比较复杂的代码。(:help folding
)
有6种方式来折叠代码
- manual //手工定义折叠
- indent //用缩进表示折叠
- expr //用表达式来定义折叠
- syntax //用语法高亮来定义折叠
- diff //对没有更改的文本进行折叠
- marker //用标志折叠
indent
:set foldmethod=indent
设置缩进折叠,fdm为简写。只对当前会话有效,要使每次打开vim时折叠都生效,则在.vimrc文件中添加设置,如添加:set fdm=indent
zc
折叠与当前缩进相同的行zo
打开折叠zj
移到下一个折叠处zk
移到上一个折叠处zm
折叠所有可折叠标记zr
展开所有折叠[z
到当前打开折叠的开始]z
到当前打开折叠的结束
marker
注意与后文的mark不是同一东西,:set fdm=marker
zf
创建marker,默认# { { {
,# } } }
Ctrl+v zf
进入可视化模式,选择需要折叠的行,zc执行折叠。会自动加入# { { {
code block# } } }
zf9j
创建从当前行起至以下9行的折叠标记,zc
进行折叠。同9zf
。类似有zf30G
,从当前行折叠刀第30行zf%
创建匹配的{}
,[]
,()
,<>
的fold。不用执行zc
zd
删除当前行上存在的折叠标记。仅当 ‘foldmethod’ 设为 “manual” 或 “marker” 时有效
2.4 标记
标记(mark)是vim提供的精确定位技术,只要你知道标记的名字,就可以使用命令直接跳转到该标记所在的位置。vim中的标记都有一个名字,这个名字用单一的字符表示。大写和小写字母(A-Za-z)都可以做为标记的名字,这些标志的位置可以由用户来设置;而数字标记0-9,以及一些标点符号标记,用户不能进行设置,由vim来自动设置。(:help mark-motions查看更多帮助)
ms
定义一个标记s`s
跳转到标记s的地方's
跳转到调剂s所在行首`.
jump to position where last change occurred in current buffer- “ jump back (to position in current buffer where jumped from)
:delmarks aA
使用 :delmarks! 删除所有小写(a-z)的标记:marks
查看所有标记
成长之路
1、坚持
学习是一个很漫长的过程,是我们每个人需要用一生去坚持的事业。
贵在坚持,难在坚持,成在坚持!
2、目标
没有目标的不叫工作,没有量化的不叫目标
每到一个阶段,制定一个目标
3、分享
学会分享,技术的价值在于能有效地将知识传递到外界,让更多的人知道它。
只要人人都拿出一点东西来,想想会变成什么样?
只要方向对了,就不怕路远了。
IT漫长人生路
IT人生,技术路上不孤单。面对生活,做一个有想法的人,唤醒心中的巨人,找到自己的梦想,坚定的走下去,成功大门终会为你开启。
只有再对自己残忍一点,要不只有别人对你残忍了。无数个夜晚,闷着头看书做实验,双休日也从不休息,几个月下来什么都会了,虽然苦点,但那种心底油然而生的自信和快乐,一切付出都值了。
学习中的苦与乐都是相对的。以苦为苦,只能使我们消沉;不以苦为苦,就会使我们无视自己的不足;化苦为乐,则可能使我们在学习和工作中取得超常的成就。
苦尽甘来,耕耘时的苦是为了收获时的乐。运维的路上,有风有雨,更有我们的坚持,让我们苦乐相随!
运维体系架构中的关键点:监控报警、负载均衡、冗余、高可用、数据库集群、存储、安全、虚拟化等的部署和设计。
身为IT人,写文档可谓是家常便饭。系统集成设计方案、应用部署方案、生产环境测试方案、割接方案、安全方案、监控方案、应急方案、升级方案、回退方案,每个方案都能让你绞尽脑汁、费心劳神。
网上虽有模板或类似的文档可下载,但是不能全部照搬,还是自己要写要改要配图的。改完,交给客户,客户不挑剔还好说,配上挑剔的客户,一篇文档也能让你给改七八回,让你想死的心都有。
升级扩容
硬件升级、系统升级、软件升级、数据库升级、安全补丁升级,一升级基本就要停应用做备份,然后等着业务不忙的时候进行操作。升级完了再启再测,没问题了消停一阵,过段时间还会再来一遍,周而复始永不停歇。
还有就是扩容,硬盘容量再大总闲不够,从G到T再到P,数据库是越来越大。存储迁移一般都是大项目,这么多的数据和资料都要迁移出去,还要保证安全可靠无风险,前期规范和测试的时间都需要很长。
IT人都在痛并快乐着,升级了扩容了,系统性能提高了,当然心里高兴。但是为升级为扩容花费的时间、花费的脑力甚至体力,让人心痛。错失了多少与家人一起的时间?错失了多少与朋友一起HAPPY的时间。。。。此处省略300字。
产品的需求是计划性的,运营的需求是计划外的,销售的需求的紧急的,这些需求都是要响应的……
不巧的是,最近的线上bug有些多,细节决定成败,遇事一定要冷静沉着,问题面前不要轻易的说放弃。
我觉得男人最重要的一点是率真。率真就是坦率真诚。不虚伪不做作,像学技术一样,来不得半点虚的。
《时间都去哪了》—IT版歌词
代码开发加测试
需求变更又重来
心里存了好多话
埋进了键盘鼠标
睡梦中的电话声
记忆中的改BUG
一生把爱交给他
只为那项目上线
时间都去哪了
还没好好享受周末就老了
机器代码一辈子
满脑子都是BUG没了OK了
时间都去哪了
还没好好陪陪你眼睛就花了
辛辛苦苦半辈子
转眼就只剩下满脸的皱纹了