Git Product home page Git Product logo

me's Introduction

研发

IoT

容器

架构

系统

DevOps

运营

运维

产品

产品思维

产品设计

技能

me's People

Contributors

bobenut avatar

Stargazers

宋亮亮 avatar

Watchers

 avatar  avatar  avatar

me's Issues

Istio入个门

实现微服务要解决的两大问题

  1. 微服务的划分。
  2. 服务治理。

微服务的划分

微服务实现的第一个问题是如何划分,引用一段Martin Fowler的文章《Organized around Business Capabilities》,原文是:

The microservice approach to division is different, splitting up into services organized around business capability. Such services take a broad-stack implementation of software for that business area, including user-interface, persistant storage, and any external collaborations. Consequently the teams are cross-functional, including the full range of skills required for the development: user-experience, database, and project management.
简意就是微服务划分方法是围绕着业务能力进行,微服务是业务域。
但是,Martin Folwer并没讲清楚怎么确定一个业务域,怎样设计一个业务域,产生哪些业务对象,内部怎样划分,对代码如何映射。这些东西在Domain Driven Design中得到了进一步的明确。

服务治理

微服务实现的第二个问题是服务治理,把一个大型的单体系统拆成若干以业务域名未范围的微服务,不是拆了就完了,整理了一下常规要进行的治理工作。

  • 服务发现。
  • 服务间调用负载均衡。
  • 服务间调用重试。
  • 服务降级、熔断。
  • 流控,限流、金丝雀发布、按特征自定义路由。
  • 监测,监测日志,运行工况监控。
  • 鉴权。
  • 端到端的安全控制。

Istio是什么

它是一个开源的Service Mesh。

Service Mesh(服务网格)又是什么

首先它是新一代的微服务模式,其次,按照对它的定义:服务网格是一个可以添加进应用程序的专门的基础设施层。它允许您透明地添加服务治理所需的功能,而无需将它们添加到您自己的代码中,通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但对应用程序透明。
概念文字有点繁琐,整理下,抽取出几个好理解的关键部分:

  • 它是一个基础设施。
  • 对应用程序透明,应用程序只需要管好业务。
  • 通过代理为每一个应用程序都能提服务治理功能。

Istio和当下主流的微服务架构的差异在哪里

当下主流的微服务框架主要是Spring Cloud、Dubbo,这两款非常优秀,没必要再背一遍书了,在优秀的背后也存在着一些问题:

  • 他们是框架,但不是作为基础设施,程序员要花大量精力去掌握和管理复杂的框架本身,要用的好,必须要大量阅读源代码次才能更透彻的理解框架。
  • 基本就认为是Java技术栈,那其他技术栈的就很难受,与开发语言太相关。
  • 代码侵入性太强,像服务降级这类治理代码,要写进微服务里。

那么,相反的,Istio的差异同样体现在这三个地方。

  • Istio是微服务基础设施,不是框架,服务治理的功能是基础设施必备的功能。
  • 与语言无关,各技术栈写的微服务都是打在容器中,容器运行在k8s中。
  • 微服务本身就关心业务,没有任何服务治理的代码,治理交给Istio的大量配置来完成。

MQTT协议3.1.1笔记

协议报文

报文 描述 流向 固定报头 可变报头 负载
CONNECT 客户端请求与服务端建立连接 C->S 1
CONNACK 服务端确认连接建立 S->C 2
PUBLISH 发布消息 C<->S 3
PUBACK 发布消息确认(QoS等级为1时执行) C<->S 4
PUBREC 发布消息收到(QoS等级为2时执行) C<->S 5
PUBREL 发布消息释放(QoS等级为2时执行) C<->S 6
PUBCOMP 发布消息完成(QoS等级为2时执行) C<->S 7
SUBSCRIBE 订阅请求 C->S 8
SUBACK 订阅确认 S->C 9
UNSUBSCRIBE 取消订阅 C->S 10
UNSUBACK 取消订阅确认 S->C 11
PING 客户端发送ping连接保持命令 C->S 12
PINGRSP ping命令回复 S->C 13
DISCONNECT 断开连接 C->S 14

报文组成

固定报头+可变报头+负载(有效载荷)

  1. 固定报头
    固定报头大小是2个字节到5个字节。
    第一个字节,高四位是报文类型,就是上面表里的值,第四位是保留位,各报文都有自己的定义。
    第二个字节开始,表示报文后续内容的剩余长度,其实就是可变报头和负载算在一起的长度。
    不同的剩余长度由不同的字节数表示:
  • 第一个字节表示:0~127个字节数。
  • 第二个字节表示:128~16383个字节数。
  • 第三个字节表示:16384~2097151个字节数。
  • 第四个字节表示:2097152~268435455个字节数。
    注意:剩余长度的每个字节只用了7位,最高第8位用来表示字节数还有更多一个字节,可以通过判断第8位是否为1(二进制)并且判断到一个最高位为0的字节,用来知道剩余长度占了几个字节,实际长度值是多少。
  1. 可变报头和负载
    参考协议文档中定义的各报文的内容。

协议文档

MQTT协议3.1.1中文版PDF
提取码:ld2d

调试工具

  • MQTT服务端调试平台可以使用OneNet、阿里云的物联网应用开发。
  • 报文发送工具可以使用网络调试助手。
  • 截包器可以使用Wireshark。

梁宁的《增长思维》学习笔记,跨越周期

停留不动

在胶片相机的时代,柯达占了2/3的市场规模和90%的利润。而且柯达在1975年,就做出了世界第一台数码相机。但是柯达不但没有引领市场,在转折点到来的时候,直接摔了下去。原因是核心利益层为了自己当下的利益,主观忽视、抗拒面对周期级变化,而这条船的领导者没有应变的决心,没有能力去搞定这一船的人。

乱冲乱撞

惠普曾经影响了**无数企业家的惠普之道:第一,重视技术创新,坚持在研发上大幅投入。第二,倡导激发员工的主动性。第三,为员工提供利润分享。但是换了四任ceo的过程中,一会儿2B,一会儿2C,一会儿买,一会儿卖,因为削减研发投入已经不再是重视技术创新,不再激发员工的主动性,企业凝聚力大大挫伤。长期的利益分享也早就废除了。惠普的支点已经变了。后来惠普拆分,变成了2B和2C两家公司。

华为的跨越

华为跨越四次周期,企业内部设置红蓝对抗,创造比市场更市场的内部竞争环境,在这样无止尽的内部对抗里,像柯达那样,有武器不用,停留在过去不动,其实不容易。像惠普那样随便下注,也不容易。这样就解决了求变的决心和组织调动问题。
华为会用军事**来管理企业,它把它的组织分成了军区和兵种。

  • 军区主建,管中长期的资源建设;
  • 兵种主战,管中短期的项目战役。
    中长期建设和中短期建设。
  • 中长期建设重要资源、人才培养、干部梯队建设、核心技术储备。
  • 中短期作战管具体的项目机会。
    华为所有的资源都有成本,所有业务申请资源,都要进行交易结算。

总结

设定内部竞争环境,建设能够持续前进的组织,短、中、长期建设。

k8s projected volumn

四种projected volumn(投射卷)

  1. Secret
  2. ConfigMap
  3. ServiceAccountToken
  4. Downward API

Secret

后台微服务少不了需要连接数据库,连接数据库用到的用户名和密码一般会配置在微服务的配置文件,或者编排进deployment文件的环境变量中,不管是哪一种,生产数据库的用户名和密码不可避免地会向多人暴露,安全管理上存在漏洞。
在这种背景下,就可以统一地由专人创建用户名和密码的Secret对象,并且为他们指定Secret名,在编排deployment时,以projected volumn的方式挂在进pod,挂在的时候只需要写明Secret的名字,挂在后用户名和密码就会以文件的形式出现在挂在的路径下,这个挂在可能会存在延时,所以pod中的服务要注意在运行的时候要能够从文件中读取用户名和密码,并且能够在失败重练的时候再此读取文件。

通过命令行创建连接mongodb用到的用户名和密码明文文件。

kongxianghai@kongxianghaideMacBook-Pro hello1 % echo opt >> mongodb-conn-username
kongxianghai@kongxianghaideMacBook-Pro hello1 % echo Abc123! >> mongodb-conn-password
kongxianghai@kongxianghaideMacBook-Pro hello1 % ls
deployment.yaml		mongodb-conn-username
mongodb-conn-password	service.yaml

通过kubectl创建用户名和密码的secret对象,创建后的secret保存在etcd中。

kongxianghai@kongxianghaideMacBook-Pro hello1 % kubectl create secret generic mongodb-conn-user --from-file=./mongodb-conn-username
secret/mongodb-conn-user created
kongxianghai@kongxianghaideMacBook-Pro hello1 % kubectl create secret generic mongodb-conn-pass --from-file=./mongodb-conn-password
secret/mongodb-conn-pass created
kongxianghai@kongxianghaideMacBook-Pro hello1 % kubectl get secrets
NAME                  TYPE                                  DATA   AGE
default-token-cqpzr   kubernetes.io/service-account-token   3      63d
mongodb-conn-pass     Opaque                                1      11s
mongodb-conn-user     Opaque                                1      36s
registry-tencent      kubernetes.io/dockerconfigjson        1      60d

创建完username、password的secret对象后,搞一下怎么挂进pod中,写一个Deployment文件,就用官方nginx镜像。
在Deployment除了容器的内容外,再写volumes,随便取一个名字mongodb-secret,在下面写上projected,加上两个secret,把上面创建的两个secret对象的名字写上。
最后,在containers里面加上volumeMounts配置项,配置项的name就引用mongodb-secret,下面的pod内挂载的路径随意。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx 
  namespace: default
  labels:
    app: nginx 
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: nginx 
  template: 
    metadata: 
      labels: 
        app: nginx 
    spec:
      containers: 
      - name: nginx 
        image: nginx 
        imagePullPolicy: Always
        volumeMounts:
        - name: mongodb-secret
          mountPath: /mongodb-secret
          readOnly: true
      restartPolicy: Always
      volumes:
      - name: mongodb-secret
        projected:
          sources:
          - secret:
              name: mongodb-conn-user
          - secret:
              name: mongodb-conn-pass

接着么,就创建pod。
创建后,进nginx pod的挂载位置看看挂了个啥。

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl get pod                  
NAME                      READY   STATUS             RESTARTS   AGE
nginx-76c6b77cc5-hvfpw    1/1     Running            0          20s

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl exec -it nginx-76c6b77cc5-hvfpw -- bash

看一下吧,首先进入到挂载路径中,能看到两个secret对象对应的文件。
显示一下这两个secret文件,就这么投射进来了。

root@nginx-76c6b77cc5-hvfpw:/# cd /mongodb-secret
root@nginx-76c6b77cc5-hvfpw:/mongodb-secret# ls
mongodb-conn-password  mongodb-conn-username
root@nginx-76c6b77cc5-hvfpw:/mongodb-secret# cat mongodb-conn-password 
Abc123!
root@nginx-76c6b77cc5-hvfpw:/mongodb-secret# cat mongodb-conn-username 
opt

ConfigMap

很多时候,程序里有许多的配置,根据不同的环境也有不同的配置,配置项又特别多,通过环境变量注入的方式太麻烦,太难维护,那么这时候可以用ConfigMap将配置文件直接挂载进pod,那么在development的时候,就不用啰里八嗦写一堆环境变量的配置。
创建ConfigMap当然可以向上面Secret一样通过kubectl create configMap命令进行创建,上面已经玩过命令了,在实际使用中通过写一个ConfigMap的yml文件,更容易交给研发来写配置。

先写一个ConfigMap的yml文件:

apiVersion: v1
kind: ConfigMap
metadata:
  name: service-a-test-config
  namespace: default
data:
  config.properties: |-
    prop.item1=v1
    prop.item2=v2
    prop.item3=v3
    prop.item4=v4
    prop.item5=v5
    prop.item6=v6
    prop.item7=v7

metadata.name是这个ConfigMap的名字,下面在挂载进pod的时候,引用的就是这个名字。
在data项下面配的就是一个key对应配置字符串,这里的key就是config.properties,以 |- 开始的就是配置串。

创建ConfigMap。

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl apply -f test-configmap.yaml
configmap/service-a-test-config created
kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl get configMap
NAME                    DATA   AGE
service-a-test-config   1      10s

在Pod的部署Deployment中声明挂载ConfigMap。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx 
  namespace: default
  labels:
    app: nginx 
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: nginx 
  template: 
    metadata: 
      labels: 
        app: nginx 
    spec:
      containers: 
      - name: nginx 
        image: nginx 
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        volumeMounts:
        - name: app-config-volume
          mountPath: /app
          readOnly: true
      restartPolicy: Always
      volumes:
      - name: app-config-volume
        configMap:
          name: service-a-test-config 
          items:
          - key: config.properties
            path: config.properties 

先是在volumes定义一个volume,名字自己定。在下面的configMap.name中引用上面定义过的ConfigMap的名字。在items.key引用ConfigMap中data里面定义的配置key。最后在Pod的containers下面搞一个volumeMounts挂载项将ConfigMap Volume引用过来,并定义一个挂载路径。

创建Pod。

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl apply -f deployment.yaml
deployment.apps/nginx created
kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl get pod
NAME                      READY   STATUS             RESTARTS   AGE
nginx-849cb95c7d-kmdhl    1/1     Running            0          7s

进到pod中,可以看到配置文件挂进去了,也能看到配置文件的内容。

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl exec -it nginx-849cb95c7d-kmdhl -- bash
root@nginx-849cb95c7d-kmdhl:/# cd /app
root@nginx-849cb95c7d-kmdhl:/app# ls
config.properties
root@nginx-849cb95c7d-kmdhl:/app# cat config.properties 
prop.item1=v1
prop.item2=v2
prop.item3=v3
prop.item4=v4
prop.item5=v5
prop.item6=v6

ServiceAccountToken

本身就是一种Secret投射方式,目的是满足容器调用k8s api时需要有授权信息和文件。
ServiceAccountToken就是一种Secret,默认k8s会自动的将默认授权挂载进容器的固定目录下/var/run/secrets/kubernetes.io/serviceaccount,然后程序就可以读取授权文件访问k8s的api。

看一下一个pod的详细信息,能看到下面的Volumes里面有一个default-token-cqpzr,这就是ServiceAccountToken。

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl describe pod nginx-96fc65d7d-p5gq9

Volumes:
  app-config-volume:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      service-a-test-config
    Optional:  false
  podinfo:
    Type:         Projected (a volume that contains injected data from multiple sources)
    DownwardAPI:  true
  default-token-cqpzr:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-cqpzr
    Optional:    false

进到容器中看一下由k8s默认挂载的固定授权目录下的内容

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl exec -it nginx-96fc65d7d-p5gq9 -- bash
root@nginx-96fc65d7d-p5gq9:/# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt	namespace  token

当然,自己可以根据实际情况,生成Secret挂载到容器目录下,由程序读取。

Downward API

Downward API该种挂载方式,是可以让容器能够访问到Pod对象上的信息,这些信息包括:

Pod基本信息

  • metadata.name
  • metadata.namespace
  • metadata.uid
  • metadata.labels
  • metadata.annotations

Pod的资源信息

  • A Container's CPU limit
  • A Container's CPU request
  • A Container's memory limit
  • A Container's memory request
  • A Container's hugepages limit (providing that the DownwardAPIHugePages feature gate is enabled)
  • A Container's hugepages request (providing that the DownwardAPIHugePages feature gate is enabled)
  • A Container's ephemeral-storage limit
  • A Container's ephemeral-storage request

我现在就搞一个pod,假设容器需要读取pod的label,内存的请求大小。
还是一样,折腾deployment文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx 
  namespace: default
  labels:
    app: nginx 
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: nginx 
  template: 
    metadata: 
      labels: 
        app: nginx 
    spec:
      containers: 
      - name: nginx 
        image: nginx 
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        resources:
          requests:
            memory: "101Mi"
          limits:
            memory: "301Mi"
        volumeMounts:
        - name: app-config-volume
          mountPath: /app
          readOnly: true
        - name: podinfo
          mountPath: /app/podinfo
      restartPolicy: Always
      volumes:
        - name: app-config-volume
          configMap:
            name: service-a-test-config 
            items:
              - key: config.properties
                path: config.properties 
        - name: podinfo
          projected:
            sources:
            - downwardAPI:
                items:
                  - path: "labels"
                    fieldRef:
                      fieldPath: metadata.labels
                  - path: "mem_request" 
                    resourceFieldRef:
                      containerName: nginx
                      resource: requests.memory
                      divisor: 1Mi
  1. 先配置resources,设置内存的requests。
  2. 配置volumes,定义我要做一个投射卷,类型是downwardAPI。
  3. 在downwardAPI下面,做两个引用,一个是label,另一个是上面设置的请求内存。
  4. 在volumeMounts中挂载到实际的目录。

创建pod,进容器看一下挂载到文件的lable,和请求内存的实际内容。

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl apply -f deployment.yaml 
deployment.apps/nginx created

kongxianghai@kongxianghaideMacBook-Pro nginx % kubectl exec -it nginx-96fc65d7d-p5gq9 -- bash

root@nginx-96fc65d7d-p5gq9:/# ls /app/podinfo
labels	mem_request
root@nginx-96fc65d7d-p5gq9:/# cat /app/podinfo/labels 
app="nginx"
pod-template-hash="96fc65d7d"
root@nginx-96fc65d7d-p5gq9:/# cat /app/podinfo/mem_request 
101

梁宁的《增长思维》学习笔记,在生死进退中增长

维持和增长

判断维持和真正增长,年利润增长低于国家GDP增长水平就是维持,能看到是跑输还是跑赢大盘。
几种增长:

  • 惯性增长,符合行业发展,具备品牌效应,那么GDP 4倍的增长是可实现的
  • 爆发增长,就是来自于市场空白,空白来自于市场限制的突破,天花板被打开。
  • 在生死进退中增长。

在生死进退中增长

先了解死、退。

《总体战》提出的定义,战争是以一方失去战斗意志为结束,企业的死是以主导者失去战斗意识为结束的。

案列:

诺基亚倒下的时候,全球市场份额最多的是三星,但是三星完成了跨越,诺基亚是投资和经理人作为公司主导,拥有各种退路。三星是家族主导,决定家族的兴亡,所以战斗意志完全不一样。

史玉柱破产后,还能再找到50w,做成脑白金,东山再起。

退

诸葛亮:“善败者不亡”。就是要懂得如何处理失败。

怎么处理:在维持线上,要注意抗风险的系统设计。求增长之前,要设计抗风险的兜底方案。要学会指定撤退方案,硬扛就是种不负责任。

总之

勇于走出去,无论胜负,战斗都会让你变强。企业发展起起伏伏,大体过程就是苦苦维持惯性增长,冒险进入无人区,主动撤退被打死,缴枪投降再次增长。就是在生死进退中求增长。只要战斗意志还在,就可以继续打下去。还有增长机会。

记录下网络栈有哪些内容

只要是看容器方面的资料,免不了会看到每个容器有自己独立的网络栈,具体什么内容,自己记录下,怕忘了。
本质还是通过Namespace技术实现独立的网络栈,具体包含:

  • 网卡设备,Network Interface。
  • 回环设备,Loopback Device。
  • 路由表,Routing Table。
  • iptables规则。

梁宁的《增长思维》学习笔记,共生

人人都是夹缝求生

每个人都不可避免空间受限,资源受限,在曲折的处境里求生。
一个萝卜一个坑,小种子小缝隙,大树进大坑。
夹缝求生最重要的事情,自我探索与长期关系。

夹缝生存,寻求共生

美国微生物学家玛葛莉丝,认为共生是生物演化的机制,她说:“大自然的本性就厌恶任何生物独占世界,所以地球上绝对不会有单独存在的生物。”
人也一样,都会以某种方式进入一种共生状态。与一个人,或者与一群人建立长期关系,接着在关系里彼此互动、压迫和交换,养成自己。共生,意味着要改变自己,部分让渡自己。

怎样开始共生

  • 价值观判断,价值观即道路,价值观就是每一个微小的动作,累积成你人生的一切,价值观的区别,也是未来道路的分叉。
    价值观判断游戏,先请你写下三个你最钦佩的人的名字,然后在每个名字后,列出三点最佩服他什么特性。这样就是3*3=9点你想拥有的品质。最后请你把这9点进行排序,就大概可以看到你自己的价值观列表。而把两个人的列表放在一起,就可以初步感知到你们之间的价值观差异。
  • 有了共生关系就需要持续的探索,没有教条法则。根据奥德赛时期这一概念,人生必然要经历一段不确定不稳定的状态。需要经历这样的自我探索,才能实现内心的确定。必然受伤,受伤就是成长的一部分。因为伤口最敏感。
  • 一种方式就是“共生效应”,当一株植物单独生长时,会矮小和单调,而与众多同类植物一起生长时,就会根深叶茂。因为一群树在一起长,必须往高长,否则就没阳光。森林里的大树,其实都是被逼的。人们把植物界中这种相互影响、相互促进的现象。孟母三迁就是为了共生效应。引入优秀的成员,是公司给员工最大的福利。

总之

创造共生生态环境,引入优秀。

梁宁的《增长思维》学习笔记,抓住机会

0到1

抓住机会,就是从0到1破局,从0到1叫做冷启动。
在客观校验的最后,得到了看似很合理的用户直接回答过的机会,至少不是幻想,接近于用户的希望,那么这些机会就真的马上都要去完成吗,此时要了解这些看似用户希望的机会存在的问题:

  • 很多看似挺不错的机会或者需求实际可能是低频的,这里有一个逻辑,正因为低频的事情,记忆才会深刻,聊起来才会很happy。直觉给人的错误引导。
  • 实际结果可能还是机会方向会较多,从0到1的状态并不是什么都要做,要注意高低频率机会,用低频机会进行冷启动,太挑战。

继续收敛

客观校验后,进一步收敛。进行用户研究。可对机会进行投票,按照投票最多的作为方向。进一步分析每个机会的实际场景模拟演练,区分高低频率。
机会要足够广谱,人人需要,足够高频,足够清晰,能清洗到扑什么样的资源上去把机会搞定。

总结

破局点特性:

  1. 相对广谱,尽量多的用户需要的点
  2. 高频打低频,高频可以带动低频。案例:美团和携程,订餐和订机票哪个高频。
  3. 体验可控,初始资源可以做到的,就是可以保证用户满意度。

梁宁的《增长思维》学习笔记,组织的创新机制

要像做产品一样做公司。

字节跳动创新

创新动力图:动力->助力->阻力->工具。
企业文化:透明!透明!透明!
创新的动力来自哪里,是人,是人内心的不满现状,希望改变。

字节跳动如何用文化和系统工具,降低阻力,让创新想发可以涌现

  1. 入职筛人,要有拥抱变化的心态,不考虑只关心流程清晰,固定岗位工作的人。入职后只会告诉一个着落点,后续要个人在整个协同网络中自己去生长。
  2. 信息环境,尽最大可能让信息透明,尽量让多元声音发声,防止公司的信息环境扭曲。
    • 比如,建设内部论坛,可以匿名发言。但联想因为老板看到大量吐槽选择关闭论坛。字节跳动选择让信息传递,联想选择维护权威。要缩小自我,缩小到原子那么小。
    • 比如,字节跳动有产品吐槽大会,吐槽自己公司的产品,可以赞美对手的产品。
    • 沟通工具采用飞书,飞书聊天可以导出历史作为纪要,新进群聊的可以看到所有历史聊天内容,可以沉淀,鼓励群聊。
  3. 协同网络,工具是OKR,目标对齐和管理。
    • 每个员工都可以看到张一鸣的OKR。
    • 团队leader制定OKR参考的内容: 参考本部门的业务,参考张一鸣的OKR,参考其他部门的okr。
    • 普通员工制定OKR参考的内容:本岗位,leader,与自己有交集的同事。也可选择直接支持张一鸣。

总之

创新涌现带来的组织红利是有好胜心的人会被激发。

梁宁的《增长思维》学习笔记,组织成长的五个阶段

观念

组织始终是人的组织。
企业里没有人,全是角色。
是人如人,不是工具人。

组织的五个阶段

  1. 几个人到十几个人,是一个家庭。创始人团队应该是3F团队,Family,Friend,Fool,不是家人,又不是朋友,傻子才跟你干。十八罗汉是Family因为能凑到一块儿。创业团队因能力匹配在一起的,散伙的多,因为变化后不匹配了。关键因素是,看到一个人,我想要一个这样的家人吗。你得靠自己,管人就是管缘分。

  2. 几十人,是一个部落。得打仗,要主动出击获得猎物才能生存。需要设计刚性原则,需要利益设计,需要利益一致,更多人才能进来。打仗讲原则,围炉出关系,是人如人,军心齐。管理战斗,不能打就不能活。

  3. 几百人,是一个村庄。大部分企业停留在该阶段。解决三个困难:

    • 必须传帮带,年轻人干得不如自己,你也不能自己上。
    • 分工协作,激励先进,共享收成。
    • 再找机会开新田。

    以创始人为中心,形成习惯,老人走,新人来,留下的形成一致性,就是文化。管理资源,规则,节奏。

  4. 成千上万人,是一个城市。建立基础设施和秩序,用基础设施为人赋能,用秩序保证发展。比如字节跳动的OKR,美团的智慧大脑,麦当劳的BI系统。

  5. 数万人,是一个国家。战略,战略,文化,文化。马云和任正非他们都是自己企业唯一的精神领袖,领袖一定要控制自己企业的文化。

总之

都是人组成的,人少的,自家兄弟一起冲;多点了,手足情谊讲原则,主动出击共获利;大几百人传帮带,分工激励开心田;成千上万一个城,保证秩序,基础设施为赋能;好几万人,战略文化是重点;是人如人,自身硬。

解决问题学习笔记,解决问题的逻辑框架

框架:

  1. 明确和理解问题;
  2. 拆分问题;
  3. 定位问题;
  4. 提出解决方案;
  5. 总结问题;

1.明确和理解问题

1)要找出对方关心的问题点,并且复述一遍问题,并询问:“老板,你想让我解决的问题是不是这个?”。

2)明确解决问题的目标,意思就是要明确我要解决这个问题,要解决到什么程度,并确认行不行。

3)明确用来解决这个问题的资源,要判断解决问题涉及的人的或者是事情东西的范围,申请可以使用的资源。

2.拆分问题

1)问题分为复杂问题和元问题,任何一个问题基本都是复杂问题,需要将复杂问题拆分为元问题,拆分后的一组元问题,能够更看的清楚复杂问题的原貌。

2)拆分的元问题的颗粒度按照MECE法则进行拆解,MECE法则(Mutually Exclusive Collectively Exhaustive)中文意思就是“完全穷尽、相互独立”。

3)将拆分后的原问题,按照他们之间的数学关系进行公示化,比如:

  • 广告收入=展现量 X 点击率 X 每个点击的价格。
  • 北京地铁每日的客运量=北京地铁线数 X 每条线同时运行的地铁数量 X 每辆地铁每天运行次数 X 每辆地铁车厢数 X 每节车厢核定人数 X 上座率

公式化后,就相对容易看的出来我要解决一个问题,首先要解决哪些元问题。

4.定位问题

1)假设驱动法
分析一组元问题,先假设最有可能是关键问题的元问题,根据假设去收集数据,再验证假设,修改假设,然后不断地重复这个流程,最终得到的就是最接近真实的那个结果。

2)每次假设都可以构建问题树
五步,将元问题构建成一个树形脑图:

  1. 分析后,假设元问题中存在的核心元问题和起始元问题。这点特别重要,之后的每一步都是基于这一点;
  2. 要确定导致核心元问题和起始元问题的主要原因;
  3. 要确定核心元问题和起始元问题导致的主要后果;
  4. 根据以上的因果关系画出这个问题树;
  5. 反复审查问题树。通过数据进行假设审查,看看哪里还缺东西,进行最后的补充和修改。

5.提出解决方案

针对被定为到的MECE的元问题以及相互关系,提出解决办法,不要多,针对性一定要强,能够被量化到可以被反复测试。

6.总结问题

1) 从结论不断地分拆,拆出到那个不可辩驳的事实。
2)总结的要点数目尽可能少,要精简,突出不可辩驳事实的关键重点。

梁宁的《增长思维》学习笔记,建立组织

雷军对梁宁说道

  • 你遇到了这么大的问题,却不能和你的伙伴分担,这说明你们之间不是真正的伙伴关系。
  • 创业就是九九八十一难,没有可能全靠一个人搞定,其实你现在困难都不算大,你都不敢让他们分担,或者他们不愿意和你分担,那这样的伙伴或团队必然是失败的。

其实,人都是好强的,报喜不报忧,为什不愿说不愿分担,很多人都习惯是问题先自己搞,特别是要强的人,只说成绩,掩盖问题,实在不行了再说,不想让别人看到自己的不足。

人际容纳度

决定了组织是丰盛的还是单薄的。是否能真正容纳对方。

人和人关系的四个阶段

  • 理想期,相互感觉挺好,和预期一致(存在假象),没有真正关系。
  • 冲突期,开始不符合预期,永远面领冲突。指责,自责。需要积极处理好冲突。
  • 整合期,说别人的问题就是球踢给对方,自身位于评判者,会产生负面冲突。把问题这个词换成差距这个词,可以把指责换成预期差异,大家站在一起评估差距,缩小差距。哪些是做的不够,哪些是预期的问题,一起在关系中成长。
  • 创作期,一起冒险,一起创新,一起接受对方的短板。

总之

建立组织,建立团队,既要在某方面很强,又要在对人上足够包容,积极对待,用差距解决负面,目标是一起冒险,一起创新,一起共赢。

梁宁的《增长思维》学习笔记,用发散与收敛洞察机会

发散

通过发散就是要容纳更多的信息点,先达到信息的广度,目的是为了看到机会,记会不至于漏掉。
目的是让洞察机会有更多的可能。

收敛

站在用户的角度,或者是直接让客户来指出所谓的机会,是幻想还是希望。

发散与收敛的方法

  1. 穷举法,把用户干过的事儿全部列出来,制作一张全景地图,找不同的线索,再根据线索整理归纳列出用户干的事儿,不建议归纳高度概念化的东西,只要实际发生的事儿。
  2. 洞察机会,工具是机会paper。就是张空白纸,画表格,分三栏。
    • 第一栏,问题点,线索上哪件事特别不爽就作为一个问题,具体就是痛点,爽点,痒点。
    • 第二栏,抽象问题抽取问题本质是什么。
    • 第三栏,根据本质提出解决方案。每个解决方案就是机会。
  3. 客观校验,不讨论,不争论,逐个过机会paper上得出的每个机会,面对面拉着用户访谈三个问题,:
    1. 你看我们觉得这一点让你不爽,你对这一点有问题,那我的感受对吗,我觉得你不爽,你是真的不爽吗?
    2. 我是这样理解你为什不爽的,我对问题的理解抽象是对的么?
    3. 我给了你解决方案,帮你解决问题了,问题被解决了么?

方法中的注意事项

  • 客观校验的目,如果是幻想,那么可以通过早点客观校验进行验证。让用户直接回答,是幻想还是希望。
  • 人是会被情景裹挟的,人是会集体吸流的,人是会逻辑自洽越想越对的,今天看似合理的决策,某一天看可能无比荒谬,一定要分组。
  • 客观校验这件事情要通过分组完成,分组的价值就是不要让权威太快的控场。如果就一个组,基本上这个组得出的结果就是那个最有权利的人得出的结果。因为创新是非共识(老罗和梁宁总结出的概念),所以让非共识有涌现和发育的空间。
  • 不要高估自己的主观判断,要谨慎自己的直觉,做一个无比现实的人。

梁宁的《增长思维》学习笔记,战略支点,战略杠杆

有意识用杠杆

用杠杆,是每一个希望改变自己命运的普通人,都应建立意识,都不断去体会和练习的能力。

用杠杆,先找支点

穷爸爸富爸爸故事,绝大多数人的一生是,第一次希望得到一份工作,第二次希望加点工资,然后这个过程无线循环。
要从这样一个没有增长的循环中跳出去。
怎么跳,找支点,用杠杆。

支点、杠杆

支点是你的初心。
杠杆就是你的自由度,就是在一个你可以自由动作的地方,你可以做和竞争对手不一样的动作。然后你在这个地方,通过你的不同的动作,形成资源的连锁反应。这个就是你的杠杆。

找杠杆,找自由度

杠杆不是大招,在日常生活中跳出来可以用穷举法把个人生活中,企业经营每个环节都穷举一遍,审视自由度。
吃亏是种自由度,通过价格的吃亏,至少可以先拿到机会,用来对接和撬动相关资源,用一套组合拳获取利益。
一个人,什么都没,至少可以先吃亏。

打造杠杆

每个新空间都有自由度,如何打造杠杆。
设定战略支点,战略指北针。

  • 战略支点,是企业的使命,是基石性假设,才能支撑一个企业。阿里的“让天下没有难做的生意”。个人的支点必须是真实的,很多人跳不出某种循环是因为没有支点,因为改变的期望不够强烈,观察就到不了。所以,那个你每天看到它没有变,都会让你痛苦的点,才是你的支点。
  • 战略指北针就是目的,简单理解就是使命的目的,是支点要达到的目的

总之

跳出循环找支点,找到自由度是杠杆,吃亏也是种自由度。

安装Traefik Ingress Controller

环境

  • 3个节点,每个节点都是虚拟机,每个节点4核10G,1个master,2个worker
  • 每个节点设置使用netplan设置静态地址
    • master01节点:172.16.30.151
    • worker01节点:172.16.30.152
    • worker02节点:172.16.30.153
  • k8s版本1.22.1
  • Ubuntu Server版本20.04.3

helm

安装helm

curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
sudo apt-get install apt-transport-https --yes
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

准备traefik镜像

在master01上拉取traefik镜像

sudo docker pull traefik:v2.5.4

安装Traefik CRD

写一个traefik-crd.yml文件,从官方的Kubernetes Custom Resource把CRD内容复制下来放到yml文件里。

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: ingressroutes.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRoute
    listKind: IngressRouteList
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: IngressRoute is an Ingress CRD specification.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: IngressRouteSpec is a specification for a IngressRouteSpec
              resource.
            properties:
              entryPoints:
                items:
                  type: string
                type: array
              routes:
                items:
                  description: Route contains the set of routes.
                  properties:
                    kind:
                      enum:
                      - Rule
                      type: string
                    match:
                      type: string
                    middlewares:
                      items:
                        description: MiddlewareRef is a ref to the Middleware resources.
                        properties:
                          name:
                            type: string
                          namespace:
                            type: string
                        required:
                        - name
                        type: object
                      type: array
                    priority:
                      type: integer
                    services:
                      items:
                        description: Service defines an upstream to proxy traffic.
                        properties:
                          kind:
                            enum:
                            - Service
                            - TraefikService
                            type: string
                          name:
                            description: Name is a reference to a Kubernetes Service
                              object (for a load-balancer of servers), or to a TraefikService
                              object (service load-balancer, mirroring, etc). The
                              differentiation between the two is specified in the
                              Kind field.
                            type: string
                          namespace:
                            type: string
                          passHostHeader:
                            type: boolean
                          port:
                            anyOf:
                            - type: integer
                            - type: string
                            x-kubernetes-int-or-string: true
                          responseForwarding:
                            description: ResponseForwarding holds configuration for
                              the forward of the response.
                            properties:
                              flushInterval:
                                type: string
                            type: object
                          scheme:
                            type: string
                          serversTransport:
                            type: string
                          sticky:
                            description: Sticky holds the sticky configuration.
                            properties:
                              cookie:
                                description: Cookie holds the sticky configuration
                                  based on cookie.
                                properties:
                                  httpOnly:
                                    type: boolean
                                  name:
                                    type: string
                                  sameSite:
                                    type: string
                                  secure:
                                    type: boolean
                                type: object
                            type: object
                          strategy:
                            type: string
                          weight:
                            description: Weight should only be specified when Name
                              references a TraefikService object (and to be precise,
                              one that embeds a Weighted Round Robin).
                            type: integer
                        required:
                        - name
                        type: object
                      type: array
                  required:
                  - kind
                  - match
                  type: object
                type: array
              tls:
                description: "TLS contains the TLS certificates configuration of the
                  routes. To enable Let's Encrypt, use an empty TLS struct, e.g. in
                  YAML: \n \t tls: {} # inline format \n \t tls: \t   secretName:
                  # block format"
                properties:
                  certResolver:
                    type: string
                  domains:
                    items:
                      description: Domain holds a domain name with SANs.
                      properties:
                        main:
                          type: string
                        sans:
                          items:
                            type: string
                          type: array
                      type: object
                    type: array
                  options:
                    description: Options is a reference to a TLSOption, that specifies
                      the parameters of the TLS connection.
                    properties:
                      name:
                        type: string
                      namespace:
                        type: string
                    required:
                    - name
                    type: object
                  secretName:
                    description: SecretName is the name of the referenced Kubernetes
                      Secret to specify the certificate details.
                    type: string
                  store:
                    description: Store is a reference to a TLSStore, that specifies
                      the parameters of the TLS store.
                    properties:
                      name:
                        type: string
                      namespace:
                        type: string
                    required:
                    - name
                    type: object
                type: object
            required:
            - routes
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: ingressroutetcps.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRouteTCP
    listKind: IngressRouteTCPList
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: IngressRouteTCP is an Ingress CRD specification.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: IngressRouteTCPSpec is a specification for a IngressRouteTCPSpec
              resource.
            properties:
              entryPoints:
                items:
                  type: string
                type: array
              routes:
                items:
                  description: RouteTCP contains the set of routes.
                  properties:
                    match:
                      type: string
                    middlewares:
                      description: Middlewares contains references to MiddlewareTCP
                        resources.
                      items:
                        description: ObjectReference is a generic reference to a Traefik
                          resource.
                        properties:
                          name:
                            type: string
                          namespace:
                            type: string
                        required:
                        - name
                        type: object
                      type: array
                    services:
                      items:
                        description: ServiceTCP defines an upstream to proxy traffic.
                        properties:
                          name:
                            type: string
                          namespace:
                            type: string
                          port:
                            anyOf:
                            - type: integer
                            - type: string
                            x-kubernetes-int-or-string: true
                          proxyProtocol:
                            description: ProxyProtocol holds the ProxyProtocol configuration.
                            properties:
                              version:
                                type: integer
                            type: object
                          terminationDelay:
                            type: integer
                          weight:
                            type: integer
                        required:
                        - name
                        - port
                        type: object
                      type: array
                  required:
                  - match
                  type: object
                type: array
              tls:
                description: "TLSTCP contains the TLS certificates configuration of
                  the routes. To enable Let's Encrypt, use an empty TLS struct, e.g.
                  in YAML: \n \t tls: {} # inline format \n \t tls: \t   secretName:
                  # block format"
                properties:
                  certResolver:
                    type: string
                  domains:
                    items:
                      description: Domain holds a domain name with SANs.
                      properties:
                        main:
                          type: string
                        sans:
                          items:
                            type: string
                          type: array
                      type: object
                    type: array
                  options:
                    description: Options is a reference to a TLSOption, that specifies
                      the parameters of the TLS connection.
                    properties:
                      name:
                        type: string
                      namespace:
                        type: string
                    required:
                    - name
                    type: object
                  passthrough:
                    type: boolean
                  secretName:
                    description: SecretName is the name of the referenced Kubernetes
                      Secret to specify the certificate details.
                    type: string
                  store:
                    description: Store is a reference to a TLSStore, that specifies
                      the parameters of the TLS store.
                    properties:
                      name:
                        type: string
                      namespace:
                        type: string
                    required:
                    - name
                    type: object
                type: object
            required:
            - routes
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: ingressrouteudps.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRouteUDP
    listKind: IngressRouteUDPList
    plural: ingressrouteudps
    singular: ingressrouteudp
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: IngressRouteUDP is an Ingress CRD specification.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: IngressRouteUDPSpec is a specification for a IngressRouteUDPSpec
              resource.
            properties:
              entryPoints:
                items:
                  type: string
                type: array
              routes:
                items:
                  description: RouteUDP contains the set of routes.
                  properties:
                    services:
                      items:
                        description: ServiceUDP defines an upstream to proxy traffic.
                        properties:
                          name:
                            type: string
                          namespace:
                            type: string
                          port:
                            anyOf:
                            - type: integer
                            - type: string
                            x-kubernetes-int-or-string: true
                          weight:
                            type: integer
                        required:
                        - name
                        - port
                        type: object
                      type: array
                  type: object
                type: array
            required:
            - routes
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: middlewares.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: Middleware
    listKind: MiddlewareList
    plural: middlewares
    singular: middleware
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: Middleware is a specification for a Middleware resource.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: MiddlewareSpec holds the Middleware configuration.
            properties:
              addPrefix:
                description: AddPrefix holds the AddPrefix configuration.
                properties:
                  prefix:
                    type: string
                type: object
              basicAuth:
                description: BasicAuth holds the HTTP basic authentication configuration.
                properties:
                  headerField:
                    type: string
                  realm:
                    type: string
                  removeHeader:
                    type: boolean
                  secret:
                    type: string
                type: object
              buffering:
                description: Buffering holds the request/response buffering configuration.
                properties:
                  maxRequestBodyBytes:
                    format: int64
                    type: integer
                  maxResponseBodyBytes:
                    format: int64
                    type: integer
                  memRequestBodyBytes:
                    format: int64
                    type: integer
                  memResponseBodyBytes:
                    format: int64
                    type: integer
                  retryExpression:
                    type: string
                type: object
              chain:
                description: Chain holds a chain of middlewares.
                properties:
                  middlewares:
                    items:
                      description: MiddlewareRef is a ref to the Middleware resources.
                      properties:
                        name:
                          type: string
                        namespace:
                          type: string
                      required:
                      - name
                      type: object
                    type: array
                type: object
              circuitBreaker:
                description: CircuitBreaker holds the circuit breaker configuration.
                properties:
                  expression:
                    type: string
                type: object
              compress:
                description: Compress holds the compress configuration.
                properties:
                  excludedContentTypes:
                    items:
                      type: string
                    type: array
                type: object
              contentType:
                description: ContentType middleware - or rather its unique `autoDetect`
                  option - specifies whether to let the `Content-Type` header, if
                  it has not been set by the backend, be automatically set to a value
                  derived from the contents of the response. As a proxy, the default
                  behavior should be to leave the header alone, regardless of what
                  the backend did with it. However, the historic default was to always
                  auto-detect and set the header if it was nil, and it is going to
                  be kept that way in order to support users currently relying on
                  it. This middleware exists to enable the correct behavior until
                  at least the default one can be changed in a future version.
                properties:
                  autoDetect:
                    type: boolean
                type: object
              digestAuth:
                description: DigestAuth holds the Digest HTTP authentication configuration.
                properties:
                  headerField:
                    type: string
                  realm:
                    type: string
                  removeHeader:
                    type: boolean
                  secret:
                    type: string
                type: object
              errors:
                description: ErrorPage holds the custom error page configuration.
                properties:
                  query:
                    type: string
                  service:
                    description: Service defines an upstream to proxy traffic.
                    properties:
                      kind:
                        enum:
                        - Service
                        - TraefikService
                        type: string
                      name:
                        description: Name is a reference to a Kubernetes Service object
                          (for a load-balancer of servers), or to a TraefikService
                          object (service load-balancer, mirroring, etc). The differentiation
                          between the two is specified in the Kind field.
                        type: string
                      namespace:
                        type: string
                      passHostHeader:
                        type: boolean
                      port:
                        anyOf:
                        - type: integer
                        - type: string
                        x-kubernetes-int-or-string: true
                      responseForwarding:
                        description: ResponseForwarding holds configuration for the
                          forward of the response.
                        properties:
                          flushInterval:
                            type: string
                        type: object
                      scheme:
                        type: string
                      serversTransport:
                        type: string
                      sticky:
                        description: Sticky holds the sticky configuration.
                        properties:
                          cookie:
                            description: Cookie holds the sticky configuration based
                              on cookie.
                            properties:
                              httpOnly:
                                type: boolean
                              name:
                                type: string
                              sameSite:
                                type: string
                              secure:
                                type: boolean
                            type: object
                        type: object
                      strategy:
                        type: string
                      weight:
                        description: Weight should only be specified when Name references
                          a TraefikService object (and to be precise, one that embeds
                          a Weighted Round Robin).
                        type: integer
                    required:
                    - name
                    type: object
                  status:
                    items:
                      type: string
                    type: array
                type: object
              forwardAuth:
                description: ForwardAuth holds the http forward authentication configuration.
                properties:
                  address:
                    type: string
                  authRequestHeaders:
                    items:
                      type: string
                    type: array
                  authResponseHeaders:
                    items:
                      type: string
                    type: array
                  authResponseHeadersRegex:
                    type: string
                  tls:
                    description: ClientTLS holds TLS specific configurations as client.
                    properties:
                      caOptional:
                        type: boolean
                      caSecret:
                        type: string
                      certSecret:
                        type: string
                      insecureSkipVerify:
                        type: boolean
                    type: object
                  trustForwardHeader:
                    type: boolean
                type: object
              headers:
                description: Headers holds the custom header configuration.
                properties:
                  accessControlAllowCredentials:
                    description: AccessControlAllowCredentials is only valid if true.
                      false is ignored.
                    type: boolean
                  accessControlAllowHeaders:
                    description: AccessControlAllowHeaders must be used in response
                      to a preflight request with Access-Control-Request-Headers set.
                    items:
                      type: string
                    type: array
                  accessControlAllowMethods:
                    description: AccessControlAllowMethods must be used in response
                      to a preflight request with Access-Control-Request-Method set.
                    items:
                      type: string
                    type: array
                  accessControlAllowOriginList:
                    description: AccessControlAllowOriginList is a list of allowable
                      origins. Can also be a wildcard origin "*".
                    items:
                      type: string
                    type: array
                  accessControlAllowOriginListRegex:
                    description: AccessControlAllowOriginListRegex is a list of allowable
                      origins written following the Regular Expression syntax (https://golang.org/pkg/regexp/).
                    items:
                      type: string
                    type: array
                  accessControlExposeHeaders:
                    description: AccessControlExposeHeaders sets valid headers for
                      the response.
                    items:
                      type: string
                    type: array
                  accessControlMaxAge:
                    description: AccessControlMaxAge sets the time that a preflight
                      request may be cached.
                    format: int64
                    type: integer
                  addVaryHeader:
                    description: AddVaryHeader controls if the Vary header is automatically
                      added/updated when the AccessControlAllowOriginList is set.
                    type: boolean
                  allowedHosts:
                    items:
                      type: string
                    type: array
                  browserXssFilter:
                    type: boolean
                  contentSecurityPolicy:
                    type: string
                  contentTypeNosniff:
                    type: boolean
                  customBrowserXSSValue:
                    type: string
                  customFrameOptionsValue:
                    type: string
                  customRequestHeaders:
                    additionalProperties:
                      type: string
                    type: object
                  customResponseHeaders:
                    additionalProperties:
                      type: string
                    type: object
                  featurePolicy:
                    description: 'Deprecated: use PermissionsPolicy instead.'
                    type: string
                  forceSTSHeader:
                    type: boolean
                  frameDeny:
                    type: boolean
                  hostsProxyHeaders:
                    items:
                      type: string
                    type: array
                  isDevelopment:
                    type: boolean
                  permissionsPolicy:
                    type: string
                  publicKey:
                    type: string
                  referrerPolicy:
                    type: string
                  sslForceHost:
                    description: 'Deprecated: use RedirectRegex instead.'
                    type: boolean
                  sslHost:
                    description: 'Deprecated: use RedirectRegex instead.'
                    type: string
                  sslProxyHeaders:
                    additionalProperties:
                      type: string
                    type: object
                  sslRedirect:
                    description: 'Deprecated: use EntryPoint redirection or RedirectScheme
                      instead.'
                    type: boolean
                  sslTemporaryRedirect:
                    description: 'Deprecated: use EntryPoint redirection or RedirectScheme
                      instead.'
                    type: boolean
                  stsIncludeSubdomains:
                    type: boolean
                  stsPreload:
                    type: boolean
                  stsSeconds:
                    format: int64
                    type: integer
                type: object
              inFlightReq:
                description: InFlightReq limits the number of requests being processed
                  and served concurrently.
                properties:
                  amount:
                    format: int64
                    type: integer
                  sourceCriterion:
                    description: SourceCriterion defines what criterion is used to
                      group requests as originating from a common source. If none
                      are set, the default is to use the request's remote address
                      field. All fields are mutually exclusive.
                    properties:
                      ipStrategy:
                        description: IPStrategy holds the ip strategy configuration.
                        properties:
                          depth:
                            type: integer
                          excludedIPs:
                            items:
                              type: string
                            type: array
                        type: object
                      requestHeaderName:
                        type: string
                      requestHost:
                        type: boolean
                    type: object
                type: object
              ipWhiteList:
                description: IPWhiteList holds the ip white list configuration.
                properties:
                  ipStrategy:
                    description: IPStrategy holds the ip strategy configuration.
                    properties:
                      depth:
                        type: integer
                      excludedIPs:
                        items:
                          type: string
                        type: array
                    type: object
                  sourceRange:
                    items:
                      type: string
                    type: array
                type: object
              passTLSClientCert:
                description: PassTLSClientCert holds the TLS client cert headers configuration.
                properties:
                  info:
                    description: TLSClientCertificateInfo holds the client TLS certificate
                      info configuration.
                    properties:
                      issuer:
                        description: TLSClientCertificateDNInfo holds the client TLS
                          certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
                        properties:
                          commonName:
                            type: boolean
                          country:
                            type: boolean
                          domainComponent:
                            type: boolean
                          locality:
                            type: boolean
                          organization:
                            type: boolean
                          province:
                            type: boolean
                          serialNumber:
                            type: boolean
                        type: object
                      notAfter:
                        type: boolean
                      notBefore:
                        type: boolean
                      sans:
                        type: boolean
                      serialNumber:
                        type: boolean
                      subject:
                        description: TLSClientCertificateDNInfo holds the client TLS
                          certificate distinguished name info configuration. cf https://tools.ietf.org/html/rfc3739
                        properties:
                          commonName:
                            type: boolean
                          country:
                            type: boolean
                          domainComponent:
                            type: boolean
                          locality:
                            type: boolean
                          organization:
                            type: boolean
                          province:
                            type: boolean
                          serialNumber:
                            type: boolean
                        type: object
                    type: object
                  pem:
                    type: boolean
                type: object
              plugin:
                additionalProperties:
                  x-kubernetes-preserve-unknown-fields: true
                type: object
              rateLimit:
                description: RateLimit holds the rate limiting configuration for a
                  given router.
                properties:
                  average:
                    format: int64
                    type: integer
                  burst:
                    format: int64
                    type: integer
                  period:
                    anyOf:
                    - type: integer
                    - type: string
                    x-kubernetes-int-or-string: true
                  sourceCriterion:
                    description: SourceCriterion defines what criterion is used to
                      group requests as originating from a common source. If none
                      are set, the default is to use the request's remote address
                      field. All fields are mutually exclusive.
                    properties:
                      ipStrategy:
                        description: IPStrategy holds the ip strategy configuration.
                        properties:
                          depth:
                            type: integer
                          excludedIPs:
                            items:
                              type: string
                            type: array
                        type: object
                      requestHeaderName:
                        type: string
                      requestHost:
                        type: boolean
                    type: object
                type: object
              redirectRegex:
                description: RedirectRegex holds the redirection configuration.
                properties:
                  permanent:
                    type: boolean
                  regex:
                    type: string
                  replacement:
                    type: string
                type: object
              redirectScheme:
                description: RedirectScheme holds the scheme redirection configuration.
                properties:
                  permanent:
                    type: boolean
                  port:
                    type: string
                  scheme:
                    type: string
                type: object
              replacePath:
                description: ReplacePath holds the ReplacePath configuration.
                properties:
                  path:
                    type: string
                type: object
              replacePathRegex:
                description: ReplacePathRegex holds the ReplacePathRegex configuration.
                properties:
                  regex:
                    type: string
                  replacement:
                    type: string
                type: object
              retry:
                description: Retry holds the retry configuration.
                properties:
                  attempts:
                    type: integer
                  initialInterval:
                    anyOf:
                    - type: integer
                    - type: string
                    x-kubernetes-int-or-string: true
                type: object
              stripPrefix:
                description: StripPrefix holds the StripPrefix configuration.
                properties:
                  forceSlash:
                    type: boolean
                  prefixes:
                    items:
                      type: string
                    type: array
                type: object
              stripPrefixRegex:
                description: StripPrefixRegex holds the StripPrefixRegex configuration.
                properties:
                  regex:
                    items:
                      type: string
                    type: array
                type: object
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: middlewaretcps.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: MiddlewareTCP
    listKind: MiddlewareTCPList
    plural: middlewaretcps
    singular: middlewaretcp
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: MiddlewareTCP is a specification for a MiddlewareTCP resource.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: MiddlewareTCPSpec holds the MiddlewareTCP configuration.
            properties:
              ipWhiteList:
                description: TCPIPWhiteList holds the TCP ip white list configuration.
                properties:
                  sourceRange:
                    items:
                      type: string
                    type: array
                type: object
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: serverstransports.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: ServersTransport
    listKind: ServersTransportList
    plural: serverstransports
    singular: serverstransport
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: ServersTransport is a specification for a ServersTransport resource.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: ServersTransportSpec options to configure communication between
              Traefik and the servers.
            properties:
              certificatesSecrets:
                description: Certificates for mTLS.
                items:
                  type: string
                type: array
              disableHTTP2:
                description: Disable HTTP/2 for connections with backend servers.
                type: boolean
              forwardingTimeouts:
                description: Timeouts for requests forwarded to the backend servers.
                properties:
                  dialTimeout:
                    anyOf:
                    - type: integer
                    - type: string
                    description: The amount of time to wait until a connection to
                      a backend server can be established. If zero, no timeout exists.
                    x-kubernetes-int-or-string: true
                  idleConnTimeout:
                    anyOf:
                    - type: integer
                    - type: string
                    description: The maximum period for which an idle HTTP keep-alive
                      connection will remain open before closing itself.
                    x-kubernetes-int-or-string: true
                  responseHeaderTimeout:
                    anyOf:
                    - type: integer
                    - type: string
                    description: The amount of time to wait for a server's response
                      headers after fully writing the request (including its body,
                      if any). If zero, no timeout exists.
                    x-kubernetes-int-or-string: true
                type: object
              insecureSkipVerify:
                description: Disable SSL certificate verification.
                type: boolean
              maxIdleConnsPerHost:
                description: If non-zero, controls the maximum idle (keep-alive) to
                  keep per-host. If zero, DefaultMaxIdleConnsPerHost is used.
                type: integer
              peerCertURI:
                description: URI used to match against SAN URI during the peer certificate
                  verification.
                type: string
              rootCAsSecrets:
                description: Add cert file for self-signed certificate.
                items:
                  type: string
                type: array
              serverName:
                description: ServerName used to contact the server.
                type: string
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: tlsoptions.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TLSOption
    listKind: TLSOptionList
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: TLSOption is a specification for a TLSOption resource.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: TLSOptionSpec configures TLS for an entry point.
            properties:
              alpnProtocols:
                items:
                  type: string
                type: array
              cipherSuites:
                items:
                  type: string
                type: array
              clientAuth:
                description: ClientAuth defines the parameters of the client authentication
                  part of the TLS connection, if any.
                properties:
                  clientAuthType:
                    description: ClientAuthType defines the client authentication
                      type to apply.
                    enum:
                    - NoClientCert
                    - RequestClientCert
                    - RequireAnyClientCert
                    - VerifyClientCertIfGiven
                    - RequireAndVerifyClientCert
                    type: string
                  secretNames:
                    description: SecretName is the name of the referenced Kubernetes
                      Secret to specify the certificate details.
                    items:
                      type: string
                    type: array
                type: object
              curvePreferences:
                items:
                  type: string
                type: array
              maxVersion:
                type: string
              minVersion:
                type: string
              preferServerCipherSuites:
                type: boolean
              sniStrict:
                type: boolean
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: tlsstores.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TLSStore
    listKind: TLSStoreList
    plural: tlsstores
    singular: tlsstore
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: TLSStore is a specification for a TLSStore resource.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: TLSStoreSpec configures a TLSStore resource.
            properties:
              defaultCertificate:
                description: DefaultCertificate holds a secret name for the TLSOption
                  resource.
                properties:
                  secretName:
                    description: SecretName is the name of the referenced Kubernetes
                      Secret to specify the certificate details.
                    type: string
                required:
                - secretName
                type: object
            required:
            - defaultCertificate
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.6.2
  creationTimestamp: null
  name: traefikservices.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TraefikService
    listKind: TraefikServiceList
    plural: traefikservices
    singular: traefikservice
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: TraefikService is the specification for a service (that an IngressRoute
          refers to) that is usually not a terminal service (i.e. not a pod of servers),
          as opposed to a Kubernetes Service. That is to say, it usually refers to
          other (children) services, which themselves can be TraefikServices or Services.
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: ServiceSpec defines whether a TraefikService is a load-balancer
              of services or a mirroring service.
            properties:
              mirroring:
                description: Mirroring defines a mirroring service, which is composed
                  of a main load-balancer, and a list of mirrors.
                properties:
                  kind:
                    enum:
                    - Service
                    - TraefikService
                    type: string
                  maxBodySize:
                    format: int64
                    type: integer
                  mirrors:
                    items:
                      description: MirrorService defines one of the mirrors of a Mirroring
                        service.
                      properties:
                        kind:
                          enum:
                          - Service
                          - TraefikService
                          type: string
                        name:
                          description: Name is a reference to a Kubernetes Service
                            object (for a load-balancer of servers), or to a TraefikService
                            object (service load-balancer, mirroring, etc). The differentiation
                            between the two is specified in the Kind field.
                          type: string
                        namespace:
                          type: string
                        passHostHeader:
                          type: boolean
                        percent:
                          type: integer
                        port:
                          anyOf:
                          - type: integer
                          - type: string
                          x-kubernetes-int-or-string: true
                        responseForwarding:
                          description: ResponseForwarding holds configuration for
                            the forward of the response.
                          properties:
                            flushInterval:
                              type: string
                          type: object
                        scheme:
                          type: string
                        serversTransport:
                          type: string
                        sticky:
                          description: Sticky holds the sticky configuration.
                          properties:
                            cookie:
                              description: Cookie holds the sticky configuration based
                                on cookie.
                              properties:
                                httpOnly:
                                  type: boolean
                                name:
                                  type: string
                                sameSite:
                                  type: string
                                secure:
                                  type: boolean
                              type: object
                          type: object
                        strategy:
                          type: string
                        weight:
                          description: Weight should only be specified when Name references
                            a TraefikService object (and to be precise, one that embeds
                            a Weighted Round Robin).
                          type: integer
                      required:
                      - name
                      type: object
                    type: array
                  name:
                    description: Name is a reference to a Kubernetes Service object
                      (for a load-balancer of servers), or to a TraefikService object
                      (service load-balancer, mirroring, etc). The differentiation
                      between the two is specified in the Kind field.
                    type: string
                  namespace:
                    type: string
                  passHostHeader:
                    type: boolean
                  port:
                    anyOf:
                    - type: integer
                    - type: string
                    x-kubernetes-int-or-string: true
                  responseForwarding:
                    description: ResponseForwarding holds configuration for the forward
                      of the response.
                    properties:
                      flushInterval:
                        type: string
                    type: object
                  scheme:
                    type: string
                  serversTransport:
                    type: string
                  sticky:
                    description: Sticky holds the sticky configuration.
                    properties:
                      cookie:
                        description: Cookie holds the sticky configuration based on
                          cookie.
                        properties:
                          httpOnly:
                            type: boolean
                          name:
                            type: string
                          sameSite:
                            type: string
                          secure:
                            type: boolean
                        type: object
                    type: object
                  strategy:
                    type: string
                  weight:
                    description: Weight should only be specified when Name references
                      a TraefikService object (and to be precise, one that embeds
                      a Weighted Round Robin).
                    type: integer
                required:
                - name
                type: object
              weighted:
                description: WeightedRoundRobin defines a load-balancer of services.
                properties:
                  services:
                    items:
                      description: Service defines an upstream to proxy traffic.
                      properties:
                        kind:
                          enum:
                          - Service
                          - TraefikService
                          type: string
                        name:
                          description: Name is a reference to a Kubernetes Service
                            object (for a load-balancer of servers), or to a TraefikService
                            object (service load-balancer, mirroring, etc). The differentiation
                            between the two is specified in the Kind field.
                          type: string
                        namespace:
                          type: string
                        passHostHeader:
                          type: boolean
                        port:
                          anyOf:
                          - type: integer
                          - type: string
                          x-kubernetes-int-or-string: true
                        responseForwarding:
                          description: ResponseForwarding holds configuration for
                            the forward of the response.
                          properties:
                            flushInterval:
                              type: string
                          type: object
                        scheme:
                          type: string
                        serversTransport:
                          type: string
                        sticky:
                          description: Sticky holds the sticky configuration.
                          properties:
                            cookie:
                              description: Cookie holds the sticky configuration based
                                on cookie.
                              properties:
                                httpOnly:
                                  type: boolean
                                name:
                                  type: string
                                sameSite:
                                  type: string
                                secure:
                                  type: boolean
                              type: object
                          type: object
                        strategy:
                          type: string
                        weight:
                          description: Weight should only be specified when Name references
                            a TraefikService object (and to be precise, one that embeds
                            a Weighted Round Robin).
                          type: integer
                      required:
                      - name
                      type: object
                    type: array
                  sticky:
                    description: Sticky holds the sticky configuration.
                    properties:
                      cookie:
                        description: Cookie holds the sticky configuration based on
                          cookie.
                        properties:
                          httpOnly:
                            type: boolean
                          name:
                            type: string
                          sameSite:
                            type: string
                          secure:
                            type: boolean
                        type: object
                    type: object
                type: object
            type: object
        required:
        - metadata
        - spec
        type: object
    served: true
    storage: true
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

安装CRD。

kubectl -n kube-system apply -f traefik-crd.yaml

用helm安装traefik ingress controller

为helm添加traefik chart仓库。

helm repo add traefik https://helm.traefik.io/traefik

安装traefik ingress controller,通过--set设置打开traefik dashboard,并且让dashboard单独暴露一个端口可以被外部访问。

helm install traefik-ingress-controller traefik/traefik --version 10.6.2 --set service.type=NodePort,ingressRoute.dashboard.enabled=true,ports.traefik.expose=true  --namespace kube-system

版本信息,以及支持的的参数可以参考ArtifactHub

在安装前如果想看一下安装的配置是不是符合要求,可以通过下面的命令来执行,执行的结果不会实际安装,会将安装时需要的所有yml文件都显示出来,方便查看,方便定制参数。

helm install traefik-ingress-controller traefik/traefik --version 10.6.2 --set service.type=NodePort,ingressRoute.dashboard.enabled=true,ports.traefik.expose=true  --namespace kube-system --dry-run

如果装错,可以删掉。

helm list --namespace kube-system
helm uninstall traefik-ingress-controller --namespace kube-system

安装后访问dashboard

先看一下安装的traefik service,看一下对外暴露的端口号。

kubectl -n kube-system get svc

结果显示。

NAME                         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                                     AGE
kube-dns                     ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP                      7d
traefik-ingress-controller   NodePort    10.101.109.248   <none>        9000:30563/TCP,80:32159/TCP,443:30375/TCP   70m

可以看到9000映射到主机端口30563。
通过查看treafik ingress controller pod的详细信息,找到是部署在哪个worker上。

通过主机地址和对应的外部端口号构建成下面的地址,就能访问到dashboard页面。

http://172.16.30.152:30563/dashboard/

梁宁的《增长思维》学习笔记,爆品机会

爆品公式

爆品机会=技术 / 供应链创新 * 爆发品类 * 新流量

爆发的背后

新品类的爆发靠的是消费升级,技术升级,人口换代,就是某一处天花板打破。
爆品背后一定是供应链成熟,技术和供应链成熟是基础,背后一定是供应链成熟,技术领先,性价比,决定了成本结构,交付能力,能够带来结构性的竞争优势。

爆品后靠的是品牌护城河

爆品后,品类爆炸式发展一定会产生过度竞争。案例,倍舒特卫生巾。
爆品后能不能扎下根来,靠的就是品牌护城河。顶级品牌都是传奇。满足更多的是心里需求。品牌需要储蓄精神能量。

如何衡量成了品牌:

  • 用户接受它的品牌议价,接受它卖的贵。
  • 有更强的博弈能力,为了引入它给折扣等主动性优惠。
  • 有很多人愿意和它自拍。

星巴克是品牌,瑞幸不是,茅台(茅台的成立是与国家建立有着很大关系的传奇故事)是,江小白不是。

总结

始于爆品,终于品牌。

梁宁的《增长思维》学习笔记,结盟

也是种共生关系

结盟就是相对外圈一点的共生关系,利益结盟。
内圈的共生关系,更多是基于共同价值观的互利与陪伴。

外圈,该如何培养自己的生态环境

工具是利益相关人地图,不是厚黑学。

  1. 图:横轴是利益,从左到右是利益小到利益大。纵轴是影响力,从低到高是影响力小还是影响力大。
  2. 下一个动作,就是把每一个角色在你这件事里获利的大小,和影响力的大小,一个一个放在象限的不同位置。
  3. 你一边画图一边问自己,对你这件事影响力最大的人,在你这件事里有利益吗?如果影响力最大的人,与你利益不一致,这就是风险。
    对自己提问,对你这件事影响力最大的人,在你这件事里有利益吗?如果影响力最大的人,与你利益不一致,这就是风险。

武当老爹

淘宝案例-武当老爹。
老爹特点:

  • 操作电脑非常不熟。
  • 商务能力,文字能力,营销能力差。
  • 交流也不行。
  • 离现代商业远。

淘宝的选择,“系统正义”,考虑的是:

  • 与强者的关系。扶植弱者,制衡强者,保持淘宝对资源的绝对控制权。
  • 弱者的杠杆意义,**是农业国,农村拥有大量的人口,土地,没有足够方法改善生活的现实。也就是因为这个点的勾连,淘宝与无数的村、镇、县,与**广袤的社会,息息相关,血脉相连。反过来看弱者因为量的关系,影响力可以变得很大,但利益却很小。

淘宝的做法:
大动筋骨,扶植武当老爹们,有了后来的淘宝村。

  • 调整淘宝的搜索引擎机制,避免强者恒强,让弱者有露出的机会。
  • 建设人对人的帮助体系,以武当老爹的能力为范本,就是这种电脑不行、商务不行、沟通不行的人,每一处短板,都需要设置组织来提供系统帮助。

总结:

使用利益相关人地图,理清利益相关的所有角色,他们的位置,看到风险,看到机会,看到抓手。

Linux基本路由表解析

路由表是系统中用于管理网络数据包转发走向的路由记录表。
通过route命令添加、删除、显示等操作路由表。
如果route命令没有安装,可以先 yum install net-tools。

通过route命令查看和管理的路由表只是众多路由表中的一个,系统中有多个路由策略,每一个路由策略对应一张路由表。
查看路由策略命令:

ip rule show

显示如:
| -- | -- |
| ---- | ---- |
| 0: | from all lookup local |
| 32766: | from all lookup main |
| 32767: | from all lookup local |

每一条都是一条rule,冒号左边的数字越小rule的优先级越高,lookup右边的就是一条策略对应的一个路由表。
main路由表就是route命令操作的路由表。所以,route命令的应的就是基本路由表。

基本路由表

看一下route命令显示的路由表:

Destination Gateway Genmask Flags Metric Ref Use Iface
default 192.168.203.2 0.0.0.0 UG 100 0 0 ens33
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.203.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33

常用路由设置

1. 默认路由,数据包发送到网关

Destination: 值设置为default,表示默认路由,无法匹配到路由表中的路由时会匹配默认路由。
Gateway:设置为网关的ip。
Genmask:掩码设置为0.0.0.0。
Flags:设置为“UG”,“U”表示启用该路由,“G”表示数据包要经过网关。
Iface:通过哪一个网卡设备发送,当然可以是虚拟的设备。

栗子:

Destination Gateway Genmask Flags Metric Ref Use Iface
default 192.168.203.2 0.0.0.0 UG 100 0 0 ens33

默认路由是,数据包通过ens33的网卡设备发送到网关192.168.203.2。

2. 主机路由,数据包发送到一个主机

Destination: 设置一个主机的ip。
Gateway:设置为网关的IP,如果Flags只填了“U”,那么这里设置0.0.0.0,表示直连,数据包不经过网关。
Genmask:掩码。
Flags:设置为“UH”,“H”表示数据包发给主机,可以只填“U”,表示直连。
Iface:通过哪一个网卡设备发送,当然可以是虚拟的设备。

栗子:

Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.20.3 192.168.20.1 255.255.255.0 UH 100 0 0 ens33
192.168.203.10 0.0.0.0 255.255.255.0 U 100 0 0 br-0ab63c131848

第一条路由,数据包通过网卡ens33,经过网关192.168.20.1,发送到主机192.168.20.3。
第二条路由,数据包通过网卡br-0ab63c131848,不经过网关,直接发送到主机192.168.203.10。

3. 网络路由,数据包发送到一个网段

Destination: 设置一个网络或者网段的网络号ip。
Gateway:设置为网关地址,也可设置成0.0.0.0,不经过网关。
Genmask:对应网络号位数的掩码。
Flags:设置为“UG”,“G”表示数据包经过网关发给一个网段,可以只填“U”,表示直连。
Iface:通过哪一个网卡设备发送,当然可以是虚拟的设备。

栗子:

Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.0.0 192.168.0.1 255.255.255.0 UG 0 0 0 ens33
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0

第一条路由,数据包通过网卡ens33,经过网关192.168.0.1,发送到192.168.0.0/24这个网络。
第二条路由,数据包通过网卡docker0,不经过网关,直接发送到172.17.0.0/16这个网络。

Docker原理,容器就是特殊的进程

能够成为容器的特性

容器之所以能够以沙箱的方式运行进程或者程序,离不开两个特性:

  1. 隔离,容器可以让应用程序或者进程在一个看上去只有它自己的系统中运行,有独立的进程空间,网络环境,挂载点。
  2. 限制,可以限制容器运行时使用的系统资源,比如cpu,内存等。

隔离和限制技术,并不是Docker独有提供的技术,而是Linux内核提供的特性。

隔离

Linux中,隔离特性是通过Namespace机制实现,并不很高深的一个机制,是在通过系统调用创建进程时,设置进程启动的控制参数来进行控制,可以支持PID,Mount、UTS、IPC、Network 、User 这些 Namespace类的参数,让进程在启动后能够拥有被映射后看似独立的进程空间,挂载点,主机名和域名,进程间通信,网络环境,用户。用来对各种不同的进程上下文进行“障眼法”操作。

限制

虽然,通过Namespace机制,可以使得进程拥有自身的进程空间,比如PID是1,在宿主机上真实的PID是50。虽然各容器中的进程的PID都是从1开始,但是他们却共享了系统中的cpu、内存等资源,某个容器对资源的耗尽,也一定会造成其他容器没有足够的资源被使用。这种情况下,显然不是一个沙箱理应表现出来的合理行为。

还是同样,利用了Linux Control Group机制,简称CGroups,可以限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。Cgroups 还能够对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。
在 Linux 中,Cgroups实际就是一个一个对应了每一中资源的文件,通过把进程和资源限制量填写该文件即可限制进程资源。它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。

总结

Docker通过进程机制Namespace给进程造了一个沙箱,再通过CGroups为每一个沙箱(就是进程)限制资源。

所以,容器,其实是一种特殊的进程而已。
一切都是障眼法。

用户故事地图

说说产品需求

一个产品的初始,到定义出产品需求为止的流程上来说,基本包含商业调查、竞品分析、可行性研究、产品愿景、产品需求说明。做这件事情的基本是产品经理,由产经负责做这些事情,研发负责人和公司领导层参与Review并提出意见。最终会产生一份产品需求说明书PRD给到研发,作为研发的工作输入,需求的优先级由产经来确定,产经会为研发说明需求,研发有问题会来和产经核对需求,周而复始的研发下去。

会发生的一些事儿

在进行产品需求流程中,始终要放在心头的点是产品解决了用户的什么需求,需要满足的是用户的是痛点,还是爽点,还是痒点,满足的用户的点是高频的,还是低频的,就这两个部分的内容,产经会组织相关的领导,研发,测试共同讨论,更类似于头脑风暴,参与的人基本上属于讲过算过,讨论的时候也更具有跳跃性,产经或者说是产品设计的直接责任人肯定会做记录,记录完的内容会转变成PRD,写完的PRD再交给参与讨论的人进行Rewiew,再修改,修改完的PRD文档会给到研发和测试进行后续的开发工作,开发前产经还要再为大家讲一遍,开发和测试过程中还会存在变更PRD的情况。这个过程还是很完整,平时就这么经历的过程,运作的好,当然也能产出好的产品,从持续精益的角度来讲,有没有更减少环节的办法,从抓住客户真正需求的角度讲,讨论的产品需求是不是客户高频的需求,产品的商业价值能不能更大化。
可以看到上面的这个过成会产生一些有趣儿的事情:

  • 产经是责任人,产经组织,产经就是产品需求的爹。
  • 参与讨论的其他人,更像是提出建议,反正这事儿不是他负责。
  • 需求讨论后的结果需要产经二次整理,产经就是全盘掌握,深入理解需求的那个唯一的人,在他面前能看到森林也能看到树木。
  • 参与讨论的研发也就是看到一些树木,整体是啥,可能都看不到,也想不明白,等着产经来普及。
  • 需求发散的量就取决于参与人的建议量,收敛的过程,纯属产经的事情。
  • 产出的需求文字文档,从需求点展开,介绍需求的内在逻辑和规则,也是种格式文档,格式内容更多,还会带点功能性描述,是不是能表述清楚客户使用产品日常所发生的逻辑和规则,其实更多的还是为了实现。
  • 缺少一个统一的视角,能看清使用产品的用户是哪些用户,在没有系统时,用户会去做哪些事情,哪些是必须做的,哪些是非必须的,在有系统的时候,系统提供了哪些额外的事情需要用户完成。用户需要做的事情在时间顺序上,在需要的顺序上,就目前的PRD文档来说,很难看得清楚,还是靠产经的脑子和嘴,
  • 用户对每个需求的急迫程度随着时间、持续交付、技术架构的变化而变化,这个过程全靠产经,其他参与人都要产经来解释。
  • 所以产经就是产品狗,他得不停的吠。

用户故事地图

就像我们生活中,要去往一个未知的地点,往往会求助于一份导航地图,借助于导航地图,能够知道当前所处的世界全局是长的什么样子,能为自己提供一个参照认知,又能够知道当下所处的局部位置在世界的哪个角落,身处哪条街,哪个拐角,帮助我们的大脑在全局、局部之上建立一个完整的认知印象。再借助于导航,指明前往最终的目的,从最近的位置如何一步一步到达彼岸。这就是地图的作用。
同样的,在体现用户的需求这一方面,我们也可以为用户制作一张针对于需求的用户故事地图,由团队主要的成员一同充分发散用户的使用行为,按着时间线进行罗列,整理,归类,邀请用户进行讨论和测试故事,进一步整理,排序,得到一张完整的体现用户需求的故事地图,其中可以实现两个目的:

  • 团队主要成员一同发散需求,一起思考,深度讨论,一起收敛,每个参与的成员都会充分理解需求,协同共创地图。
  • 即能看到整体,又能细致的看清树木。

哪些人参与

产经,主要研发成员,销售,客户。

过程

  1. 确定有哪些用户角色。
  2. 按照时间线,确定一个初始的用户行为。
  3. 从用户初始的行为出发,按顺序发散出每一步的用户行为,刚开始一定要广度优先于深度。
  4. 在广度都覆盖情况下,再对每一个用户行为进行深度方向上的行为讨论,罗列出进一步的可能的行为。
  5. 对用户行为进行分类归并,抽取出核心的行为分类以及归类。
  6. 邀请不同类别的用户按照时间线模拟用户行为,对行为进行测试重点,关注是否是高频的,必须的,没有就很难受但不常发生的,不断重复并机械的,高度流程化的,再关注用户的心里特征是否在一些行为上很沮丧,很厌烦,很兴奋,还是无所谓。
  7. 根据用户的测试反馈,按照频率的高低,能够解决用户负面情绪,激发用户兴奋情绪的顺序进行整理和排列优先级。
  8. 按照优先级和急迫程度划分所有行为实现的阶段,标注各行为故事的依赖关系
  9. 这样,一个相对完整的地图就诞生了。

参考样式

QQ20160119112655

梁宁的《增长思维》学习笔记,从连接器到整合模式

怎样整合

通过功能性和资源的整合,从功能分发到把用户留在自己这里。逐渐将视觉和体验深入到用户的潜意识中。

心理学策略

密度影响心智,看多了就会下意识的选择。

去哪儿,从连接器到整合

  • 1.0 展现最廉价机票,链接到票源。
  • 2.0 让用户填表单信息,留住用户资源。
  • 3.0 将ui元素全部换为去哪儿的元素,截住用户体验,不会感知到机票源头上家。
  • 4.0 建立售后团队,由该团队联系供应商。做到搜索,比价,购买,售后完成供应链的整合。

连接器特点

很轻,太薄,没做到资源整合,把自己作厚,很容易被击穿。

总结

以连接器模式切入入口,逐步整合资源,把自己作厚度,形成完整的体系,形成护城河。

Docker原理,隔离,Namespace

创建容器

首先,创建一个busybox容器,观察它的PID

运行容器,命令:

docker run --name busybox-test -d busybox /bin/sh -c "while true;do echo hello docker;sleep 10;done"

查看容器内PID

进入busybox-test容器,并查看进程信息。
进入容器命令:

docker exec -it busybox-test /bin/sh

查看容器内的进程信息,命令:

ps

显示(部分):

PID - - COMMAND
1 root 0:00 /bin/sh -c while true;do echo hello docker;sleep 10;done

很清楚,容器内进程的PID是1.

映射到宿主机上的PID

查看宿主机上的该进程。
命令:

ps -aux | grep /bin/sh

显示(部分):

- PID - - - - - - - - -
root 7184 0.0 0.0 1304 236 ? Ss 14:05 0:00 /bin/sh -c while true;do echo hello docker;sleep 10;done

可以看到在宿主机上该进程的PID是7184。

原理

容器实际是一种特殊的进程,实际就是使用Linux的Namespace机制,施加障眼法,为进程提供一个设定好的视图,在这个视图内,进程只能看到属于它自己的PID,Mount、UTS、IPC、Network 和 User。

比如前面写的PID,在容器里看到的是1,在宿主机上看到的是7184,通过PID Namespace,为进程创建了1个PID视图,进程只能在该视图内,看到全新的PID。在宿主机上,能看到该进程实际的PID,这之间的映射关系由系统内核完成。

Namespace到底是个啥?
没那么神秘,在Linux系统中创建进程的系统调用是clone(),比如:

int pid = clone(main_function, stack_size, SIGCHLD, NULL);

这个系统调用就会为我们创建一个新的进程,并且返回它的进程号 pid。

如果,要使用PID Namespace机制,只需要在带上CLONE_NEWPID参数即可,比如:

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

新创建的这个进程将会“看到”一个全新的进程空间,在这个进程空间里,它的 PID 是 1。

除了提供PID Namespace意外,还提供的常用Namespace:

Namespace 对应参数
PID CLONE_NEWPID
Mount CLONE_NEWNS
UTS CLONE_NEWUTS
IPC CLONE_NEWIPC
Network CLONE_NEWNET
User CLONE_NEWUSER

其他的参考clone函数说明

产品经理的基本思维模型笔记,需求三角思维模型

本篇笔记记录,站在产品经理的角度所具备的基本思维模型。

需求三角思维模型

需求三角是百度副总裁李靖(李叫兽)在混沌大学上了一堂营销课中提出的理论。
需求三角的三个顶点分别是:

  • 缺乏感
  • 目标物
  • 能力

缺乏感

就是用户的痛点,来源于用户的心理落差,包括五个维度:

视角 心里状态 落差来源 对应心理
任务 完成一件事 完成-不完成 目标趋近-目标阻碍
时间 考虑未来和过去 现在-过去-未来 怀旧、求新、恐惧、经验
关系 跟某个人的交互 别人-自己 亏钱、结识、互惠
群体 群体的参照 群体-自己 渴望、从众、回避、融入
角色 自我角色对比 角色-自己 角色、一致性
举栗
  • 任务视角,我在家门口装一个摄像头,心里状态就是装了以后放在门口的东西就能被监控着,心里就会有安全感,这里的落差来源就是,我对自己家门口摆放物品的安全感有无,装了以后我的目标是能达到安全感提升力,放心很多了。
  • 时间视角,比如之前大火,而国内无法玩的一款游戏《精灵宝可梦GO》,它主攻的心里状态就是曾今的大IP,曾今对精灵世界的幻想,落差来源于对过去的怀旧以及现实当中的体验实现的情感集中爆发,在目标上既能满足怀旧的心态,也能在现实当中探寻精灵的世界。

目标物

就是为了填补用户的缺乏感而提供给的产品。如何验证对用户缺乏感的判断是否正确,就是要在实际落地中观察验证产品有没有真的解决用户的问题。

关于用户真正想要什么,有两大经典引用:

福特说过,如果你问顾客想要什么,他们会告诉你是一辆更快的马车,但事实上汽车出现后完全改变了这一现状。

乔布斯说过,用户根本不知道自己想要什么。

所以,现实就是用户可能并不知道能满足他们缺乏感的是什么,但是缺乏感一定是客观存在的,实际在销售、项目落地过程中,我们可能通过营销、政策、关系等手段强加给用户,并且用户还能接受的产品,也是因为生理、心理机制所决定的。

所以,做产品的人,一定要学习心理学,要去思考用户最底层的心里需要,提供给能够满足用户缺乏感的目标物,也就是产品。

能力

能力是指如果要把用户的缺乏感转化成买单,要考虑用户付出的成本:

  • 金钱成本
  • 形象成本
  • 学习成本
  • 决策成本
  • 健康成本
  • 行动成本
举栗
  • 金钱成本顾名思义,免费的比收费的更有吸引力,低价肯定是要比高价有优势,但要注意的是,这个金钱成本一定是相对于满足用户缺乏感而言的,上万的Prada和地摊货,完全是满足不同用户的不同的缺乏感。
  • 形象成本,一个最好理解的例子,就是情趣用品线上销售一定要比线下卖的多的多,里面道理成年人都懂,hiahia。
  • 学习成本,要让一位老年人使用APP前,通过填一堆表格进行注册,显然学习成本是非常高,结果就是老年人不会用。
  • 决策成本,一套企业的管理系统,由企业高管决策买单,结果还无法提供有效的经营决策数据,老板怎么能不上火呢。
  • 健康成本,一份卖到40元上下的沙律在城市白领中特别有市场,说明现在健康饮食非常能被接受。
  • 行动成本,通过智能设备可以提前设置好每日空调打开的时间,在夏天里,一进家门就能享受到那种爽心的感觉,谁不乐意呢。

梁宁的《增长思维》学习笔记,设计以用户增长为核心的模式

用户要的是什么

用户要的是确定性,确定性的效果。

让用户增长

可以不需要发明,不需要创新,以用户体验为核心交付进行的集成,能够服务大部分人群。案例:Keep,三只松鼠。
用户体验,70%是生理体验,30%是心理体验
一定要设计增强回路。注意传播效应,场景越清晰,场景强化的同时,很容易同场景铺开。

避免让用户看似合理的损失

人的天性是厌恶损失的,如果你曾经让谁因为你的原则而遭受损失,你会觉得你没有错,但对方会很不爽,对,你觉得你没有错,但这就给了对手机会。
人厌恶损失的天性,哪怕是便宜五块钱也是有很大诱惑力,因为用户害怕买贵了。

总结

以确定性体验为核心交付,注意护城河的建设。

Google SRE稳定性保障要点参考

概念

什么是SRE

SRE这一词汇源于 Google 的一本书《Site Reliability Engineering: How Google Runs Production Systems》。由 Google SRE 关键成员分享他们是如何对软件进行生命周期的整体性关注,以及为什么这样做能够帮助 Google 成功地构建、部署、监控和运维世界上现存最大的软件系统。

SRE的定义:

站点可靠性工程(SRE)是一门结合了软件工程的各个方面并将其应用于基础设施和运营问题的学科。主要目标是创建可伸缩和高度可靠的软件系统。

SRE日常工作划分

  • 50% 的时间精力处理操作相关事宜。
  • 50% 以上的精力通过软件工程保障基础设施的稳定性和可扩展性。

敲敲小桌板

一个软件系统的 40%~90% 的精力都是在开发建设完成之后不断维护过程中。

针对这一规律,团队里要有两种对应的角色

  1. 对产品和基础技术架构的研发。
  2. 针对稳定性的系统性治理。

SRE如何确保稳定性

稳定性特征

  • 人为操作导致、通过实操经验解决。
  • 由于缺乏感知、缺少日志、缺少监控、缺乏经验等一系列综合问题导致,通过针对性治理,系统性解决。
  • 突发流量、未覆盖测试的输入、服务器故障等不可避免的问题发生导致,通过留有余量、弹性扩容、应急处置手段进行兜底。
  • 100%保障没有必要。

确保稳定性要点

1. 可控性

  • 发布管理
    • 重点解决发布导致的人为稳定性问题。
    • 包括发布前重要变更评审和发布中变更动作管理等。
  • 操作管理
    • 重点解决黑屏操作导致的人为稳定性问题。
    • 包括统一集群操作入口、集群操作权限管理、集群操作审计等。
  • 设计评审
    • 重点解决软件系统设计阶段应用稳定性保障最佳实践。
    • 包括集群方案评审和重要功能设计评审等。

2. 可观测

  • 监控
    • 重点解决软件系统运行态的感知能力。
    • 包括监控收集/可视化系统的搭建和维护等。
  • 日志
    • 重点解决软件系统的问题可排查能力。
    • 包括日志收集/存储/查询/分析系统的搭建和维护等。
  • 巡检
    • 重点解决软件系统功能是否正常的主动探测能力。
    • 包括巡检服务的搭建、通用巡检逻辑的开发维护等。
  • 告警
    • 重点解决异常的及时触达需求。
    • 包括告警系统的搭建、告警配置管理、告警途径管理、告警分析等。

3. 稳定性保障最佳实践

  • 项目质量验收标准。
  • 项目安全生产标准。
  • 项目发布前 checklist。
  • 项目 TechReview 模板。
  • 项目 Kick-off 模板。
  • 项目管理规范。

k8s pod阶段和容器状态

POD阶段

k8s显示的是status,实际是phase(阶段)

通过两种命令看到status。

  1. 列出pod,在pod列表中有一列是status。
kubectl [-n namespace] get pod
  1. 显示pod的祥细信息,有一个字段是status。
kubectl describe [-n namespace] pod <pod name>

根据官方文档定义,status实际是一个PodStatus对象,该对象内有一个phase字段,表示pod的阶段,还有个字段reason,在出错的时候可以看这个reason。上面两个命令显示的status的内容实际是phase的内容。

pod定义了哪些阶段

阶段 解释
Pending Pod 创建的指令已被k8s接受,但有一个或者多个容器尚未创建也没有运行,正等待 Pod 被调度,或者通过网络下载镜像。
Running Pod 已经部署到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器正处于启动、运行中,或重启中。
Succeeded Pod 中的所有容器都已成功终止,并且不会再重启。
Failed Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。失败终止表示容器以非 0 状态退出或者被系统终止。
Unknown 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。

容器状态

通过显示pod的祥细信息,有一个字段是Containers,该字段显示了pod中初infra容器外所有容器的信息,其中State字段显示了容器的状态。

kubectl describe [-n namespace] pod <pod name>

容器状态包括:

状态 解释
Waiting 容器不在 Running 或 Terminated 状态之一,就处在 Waiting 状态,表示容器仍在运行它完成启动所需要的操作,包括:拉取容器镜像,应用 Secret 数据,配置等等。 可以describe pod ,有一个 Reason 字段,能看到等待状态的原因。
Running 容器正在执行并且没有问题。 如果配置了 postStart 回调,那么该回调已经执行且已完成。
Terminated 容器正常结束或者结束时因为某些原因失败。 可以describe pod,产看容器具体的原因、退出代码以及容器执行期间的起止时间。 如果容器配置了 preStop 回调,则该回调会在容器进入 Terminated 状态之前执行。

梁宁的《增长思维》学习笔记,选择模式

模式清晰,推进坚决

正例:

  • 星巴克的模式就是围绕咖啡,这是被设计的的,当你在商场里闻到咖啡香,就想到星巴克,星巴克宁愿放弃热食,因为热食的气味会影响咖啡的气味,目的就是要让咖啡的气味和品牌直接挂钩,直接植入人的潜意识。一切为了品牌,为了盈利能力,获得了品牌议价。模式清晰,推进坚决。
  • 瑞信的目标是速度,砸钱买流量,获得资本杠杆,进行资本运作。模式清晰,推进坚决。

反例

  • 雕刻时光,开始时的基因就是慢生活,但是资本进入后,目标发生了变化,创始人内心却在抗拒新的模式。模式变得模糊,也不坚决。

总结

做一件事,真正的目标是什么,设计的模式和目标匹配吗,确定模式后,推进坚决。

Docker原理,Network

在docker中运行了多个容器后,容器之间可以相互访问,也可以通过所在主机地址访问到容器,这是怎么做到的?
要做到上面的网络通信,需要一个容器技术,两个虚拟网络设备,再这些基础上才能够让整个网络通信流转起来。

涉及的容器和网络技术

  • 容器技术:Network Namesapce
  • 虚拟网络设备:docker0网桥,Veth Pair虚拟网络设备

Network Namespace

容器实际是一种特殊的进程,使用Linux的Namespace机制将为进程提供一个视图,在属于进程自己的视图内只能看到自己的资源,比如Network Namespace就可以为每个进程提供一个只属于它自己的网络栈,所谓的网络栈包含:网卡设备、回环设备、路由表、iptables规则,简单来讲每个容器都有独立的网卡设备。

docker0网桥

首先,啥是网桥(Networking Bridging)?
网桥是个网络设备,它的作用是把多个网络或者网络进行汇聚,让他们相互之间能够通信。网桥是工作在OSI的第二层,数据链路层,简单讲就是一个二层网络的交换机,工作在这一层的网桥有两个很重要的特点:

  • 通过网桥的数据包是使用目标设备的MAC的地址进行通信。
  • 一张虚拟网卡插在网桥上之后,虚拟网卡会作为网桥的从设备,从设备不再具备处理数据包的能力,会作为网桥的一个端口,经过这张网卡的数据包会直接交给网桥,网桥通过mac地址进行寻址和发送到不同的端口上,这些端口是就是另一张虚拟网卡。
  • 既然使用MAC地址进行通信,那么就要获取各个容器的MAC地址,实际上是通过ARP协议获取ip对应的mac地址。

显示下系统中的网卡设备,可以很清楚的看到docker0这个设备。

[root@localhost ~]# ip addr
//省略
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:4f:bb:2e:25 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:4fff:febb:2e25/64 scope link
       valid_lft forever preferred_lft forever
//省略

Veth Pair虚拟网络设备

特性

有了容器独立的网卡,宿主机上有了docker0网桥后,为了把容器连接到docker0网桥上,需要通过Veth Pair的虚拟设备来实现。
通过Veth Pair,可以在两个Namespace中各生成成对的一个虚拟网卡,首先就是在容器中生产一张名为eth0的网卡,在宿主机中生成另一张虚拟网卡,Veth Pair打通着两张网卡的通信,从易张网卡上发出的数据,可以直接出现在另外一张网卡上。

容器的一端

查看容器中的网卡和路由信息。
本机运行了两个nginx容器。

[root@localhost ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                                                 NAMES
eb17fe413e0b        nginx               "/docker-entrypoint.…"   About a minute ago   Up About a minute   80/tcp                                                nginx-2
28d97a8bfe9b        d1a364dc548d        "/docker-entrypoint.…"   24 minutes ago       Up 24 minutes       80/tcp                                                nginx-1

进入容器nginx-1看一下网卡信息和路由信息

[root@localhost ~]# docker exec -it nginx-1 /bin/bash
root@28d97a8bfe9b:/#
root@28d97a8bfe9b:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 16  bytes 1296 (1.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

root@28d97a8bfe9b:/#
root@28d97a8bfe9b:/# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

可以看到:

  1. Veth Pair生成的网卡eth0的ip地址是172.17.0.2。
  2. 通过route命令显示的路由表中,能看到第一条eth0的网关是172.17.0.1。
  3. 路由表的第三行能看到,所有对于172.17.0.0/16这个网段发包都会经过网卡eth0,要往172.17.0.0/16这个地址段发数据包首先会匹配这条路由规则,这条规则的Gateway是0.0.0.0代表是条直连规则,也就是从容器eth0网卡发出去的数据包会直接出现在对端虚拟网卡上。
  4. 通过Veth Pair的特性能知道,向eth0网卡发包后,直接会出现在Veth Pair的另一端。

宿主机的一端

回到主机上,看一下主机上的网卡。

[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:f9:a5:23 brd ff:ff:ff:ff:ff:ff
    inet 192.168.113.128/24 brd 192.168.113.255 scope global dynamic ens33
       valid_lft 985sec preferred_lft 985sec
    inet6 fe80::20c:29ff:fef9:a523/64 scope link
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:76:6e:f2:65 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:76ff:fe6e:f265/64 scope link
       valid_lft forever preferred_lft forever
5: vethacc1dea@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
    link/ether a2:bf:9c:51:06:87 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::a0bf:9cff:fe51:687/64 scope link
       valid_lft forever preferred_lft forever
7: veth5d8e6e8@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
    link/ether 3e:d8:2d:d8:01:99 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::3cd8:2dff:fed8:199/64 scope link
       valid_lft forever preferred_lft forever

在docker0的下面,编号5、7的两个虚拟网卡vethacc1dea,veth5d8e6e8,分别对应nginx-1、nginx-2容器Veth Pair的对端。
用网桥管理命令show一下网桥的接入情况。

[root@localhost ~]#
[root@localhost ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242766ef265       no              veth5d8e6e8
                                                        vethacc1dea

可以看到有一个网桥docker0,两块由Veth Pair生成的容器的对端虚拟网卡插在了docker0上。
虚拟网卡插在网桥上,成为网桥的附属设备,也就是变成了网桥的一个端口,虚拟网卡上收到的数据包会直接交由网桥处理。

通信是如何工作的

画一张我的主机上容器网络拓扑图:

未命名文件

通信过程

假设nginx-1容器要反向代理到nginx-2,也就是数据包要从nginx-1容器地址172.17.0.2发到nginx-2容器地址172.17.0.3。
过程:

  1. 先从nginx-1容器内的路由表中匹配到第二条直连路由规则,对172.17.0.0/16网段的访问,从容器的eth0网卡发包。
  2. docker0网桥是一个二层交换机,是通过MAC地址进行寻址,所以,这一步,在nginx-1容器内先要发送ARP广播包,广播包经过docker0到达nginx-2容器,nginx-2容器收到ARP包后会把自己的MAC地址经过docker0再回给nginx-1容器。
  3. 在nginx-1容器中构建的数据包是二层数据报文,报文中的目标MAC地址填写上面第二步获取到的nginx-2的MAC地址后,将数据包通过路由使用nginx-1容器eth0发到docker0上。
  4. docker0收到ningx-1发来的二层数据报文后,继续扮演二层交换机的角色查询二层交换机地址表(CAM),能够查到nginx-2的MAC地址对应的是网桥的端口veth5d8e6e8,那么docker0就会将数据包从veth5d8e6e8发走。
  5. 因为veth5d8e6e8和nginx-2的eth0是一对Veth Pair,数据包就会传到niginx-2的eth0网卡上,至此,完成整个数据的发送。

产品UI的情感化设计笔记

引用

出自Fyin印迹的总结:

心理学认为,情感是人对客观事物是否满足自己的需求而产生的态度体验;只有当产品触及到用户的内心时,使他产生情感的变化,那么产品便不再冷冰冰;他透过眼前的东西,看到的是设计师为了他的使用体验,对每一个魔鬼细节的用心琢磨,人们会产生愉快、喜爱和幸福的情感。

最近在看一些UI的情感化互动细节对用户体验性方面的改善,收获一些所谓的魔鬼细节,^-^。
总结:

  • 有感情的文字
  • 生动的下拉刷新
  • 默认头像设计
  • 化解负面情绪的缺省页面
  • 操作动效
  • 预知用户行为

有感情的文字

看到一句话写的特别好:

语言是情感化设计最直接的利器,拟人化的对白相比冰冷的话语更能获得用户的好感

比如:
“哎呀,APP不小心开小差了,麻烦小主再试一下” 的拟人表达方式可能要比“数据加载失败”、“页面打开失败”要有趣的多。
“努力为小主搬运信息”可能更胜过呆板的显示进度条,或者直白的表示“加载中”的冰冷的表达方式。

另外,还有些APP的消息推送也是通过低成本、但有趣的文字撩动小主们的心。

比如:
随手记APP会推送:“5天没花钱,赞啊”。
小红书APP会推送:“这才是恋综天花板!!!”。
百度地图APP会推送:“预计1小时内闵行区会有小雨,出门别忘记带伞哦”。

生动的下拉刷新

下拉刷新,现在几乎是APP端刷新列表的最主流操作方式。下拉刷新是一种临时的状态,在这个状态显示时通过丰富的细节表现可已让整个刷新非常生动,甚至于在这个位置可以加深品牌价值的潜意识植入和推广。

今天,在寻找下拉刷新比较有趣的设计方案前,先看了一些APP,采用的是最为通常的显示“刷新中”,“刷新完成”等字样的设计,并且由于这种状态的临时性,画面变化较快,字体较小等原因,会使得眼睛的焦点处于一段飘忽期,并不是很舒服,快速实现功能可以,还有提升空间。

个人目前比较喜欢的方案是小红书,闲鱼APP的下拉表现方式。
小红书APP的表现和大多的下拉交互表现的不太一样,在下拉时,会拉下一个正在旋转的圆形图标,下来动作弹性较为舒适,列表刷新完成之后,圆形图标会自动弹性收起,互动起来,让人感觉比较有手感。
闲鱼APP的下拉列表还是大致通用的表现方式,但个人喜欢的是,它将闲鱼logo作为刷新动画,让一条鱼萌萌的张着大眼肥嘟嘟的泡在水里,即能够生动地表现刷新,又能够将品牌logo植入在此,加深用户对品牌形象的认知程度,所以强烈收下此种方案。

默认头像设计

很多APP的个人中心,关于我,首页的界面上都会放置用户头像的显示位置,默认的这个位置都是显示的是APP提供的默认的头像,不少APP都会放一个人头形状的图标,很是呆板,甚至我在一款银行的APP中看到我的默认头像是一个看上去很漂亮,讲不出是男是女的头像,总之很奇怪。

个人目前,感觉体验还不错的头像设计是,在这个位置放一个APP品牌的logo在这个位置,APP品牌价值宣传植入导向,略微会有点意思。

化解负面情绪的缺省页面

个人理解就是,尽可能不要有空白页面,比如我在小红手的我的笔记、收藏、赞过这些页面中,我刚用app的时候这些功能的数据一定是没有的,小红书此时的处理是,收藏页面会显示一个代表收藏的图标和一行文字”你还没有收藏任何笔记哦“。

也可在一些负面场景发生时用更动态的有趣的提示性设计来化解一些,比如在加载失败,网络出问题的时候,适当使用些贱萌的logo表达方式,也可加上动画,尽可能为用户带来有趣的体验。

看到的一个优秀的设计是在小红书上点选笔记栏目时,该栏目里一定是空的,但,下方会直接弹着”发布你的第一篇笔记“的提示性按钮,效果做的不错,为平淡的空白页面增添了很亲近的操作,个人总结是倒贴式操作,当然也会带些诱导作用,但总比呆呆的来个添加按钮要好的多。

操作动效

之前,我会觉得,必须要给操作按钮,菜单,一些拖拽等明显交互的操作加上动效,现在呢,还是觉得要加,但是会给自己带个紧箍咒,为什么要带紧箍咒,因为虽然觉得很牛逼,但不知道是非善恶。
找了半天,看了很多的长篇大论,偶然间看到了ant motion的动效原则,觉得够简单,简单就是王道,现抄录下:

为什么要有动效

界面动效能加强用户认知且增加活力

动效价值

  • 增加体验舒适度: 让用户认知过程更为自然。
  • 增加界面活力:第一时间吸引注意力,突出重点。
  • 描述层级关系:体现元素之间的层级与空间关系。
  • 提供反馈、明确意向:助力交互体验。

衡量动效意义

衡量一个动效是否有意义,我们可以通过以下几个标准来考核:

  • 一个动效的存在是否合理:是否带有明确的目的性,助力交互体验,没有多余的动效。
  • 动效与性能:不能出现大幅度波动丢帧或者卡顿现象, 动效的体验须是流畅的,并且不影响产品的性能。

Ant Motion动效原则官方

预知用户行为

网上看了一篇文章,说起我们一直在用,但又无法立刻认知到这个功能的设计思维是有多贴近用户的使用,核心点就是知道用户要干这件事儿,大概率会干接下来的一件事,并且已经为用户准备好,这个功能就是手机截屏后,用微信发送图片时,默认会跳出刚截屏的图片。刚开始用这个功能的时候,第一感觉就是很自然,很保姆的感觉,特别自然舒服,但也没有深入思考,现在细细想来,我想,接下去在考虑用户操作行为的时候,会去想一想”操作的因果关系“。

Docker原理,容器镜像,DeviceMapper

Docker利用Linux Namespace技术,给每个进程隔离在一个看似属于它自己的环境中运行,利用Cgroups技术限制进程使用的资源量。
那么当进入容器时,看到的完整的操作系统的文件系统又是如何实现的呢?
容器和镜像中能看到属于一个容器自己的操作系统的完整目录,个人认为这是docker中最最精华的部分,正因为有了这个能力,容器才有了一个被反复宣称的重要特性:一致性,所谓的一致性指的就是应用以及它所运行所需要的所有依赖,都被封装在了一起,部署到任何系统中,依赖都固定不变,对一个应用来说,操作系统本身(不含内核)才是它运行所需要的最完整的依赖库。

镜像容器的存储架构

  • rootfs,根文件系统
  • device mapper,存储驱动
    • snapshot(快照)
    • base device(基础设备)
    • thin pool(精简配置资源池)
    • loopback device(回环设备)
    • sparse file(稀疏文件)

之前网上很多讲docker容器和镜像存储引擎很多都是AUFS,中文叫做联合文件系统,它并非是linux内核天生支持的存储驱动,所以,仅在ubuntu或者是debain中支持。而device mapper天生就是linux内核提供的存储驱动,所以支持 CentOS, Fedora, Ubuntu, Debian。一定要注意。

device mapper,容器和镜像的存储驱动

sparse file(稀疏文件)

稀疏文件的特性就是按块分配空间,并且可以在写入数据的时候通过指针偏移跳过某些块,被跳过的块不占用实际存储空间形成空洞,在稀疏文件的索引中这些空洞的值就是0,稀疏文件的特性主要就是为thin provisioning提供支持,达到的效果就是,提前分配存储所需的块,实际没数据的时候可以不用占据实际的存储空间,提高空间利用率。
创建稀疏文件:

dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/metadata bs=1 count=0 seek=2G
dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/data bs=1 count=0 seek=100G

两个稀疏文件,metadata用来存放配置等元数据,data用来存储实际的镜像和容器的快照数据。

loopback device(回环设备)

回环设备允许用户以一个普通磁盘文件虚拟一个块设备(就是以块为读写单位的设备)。就是虚拟一个磁盘设备,对它的所有读写操作都将被重定向到读写一个名为 virtualfs 的普通文件而非操作实际磁盘或分区的轨道和扇区。说白了就是用稀疏文件虚拟出一个块设备。
创建data和metadata对应的两个回环设备:

losetup /dev/loop0 /var/lib/docker/devicemapper/devicemapper/metadata
losetup /dev/loop1 /var/lib/docker/devicemapper/devicemapper/data

创建后,可以打个命令查看一下已创建的回环设备:

[root@localhost mapper]# losetup -a
/dev/loop0: [2051]:67961585 (/var/lib/docker/devicemapper/devicemapper/data)
/dev/loop1: [2051]:68008970 (/var/lib/docker/devicemapper/devicemapper/metadata)

再打命令看一下系统中创建的块设备:

[root@localhost ~]# lsblk
NAME                                                                                   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                                                                                      8:0    0   20G  0 disk
├─sda1                                                                                   8:1    0  300M  0 part /boot
├─sda2                                                                                   8:2    0    2G  0 part [SWAP]
└─sda3                                                                                   8:3    0 17.7G  0 part /
sr0                                                                                     11:0    1 1024M  0 rom
loop0                                                                                    7:0    0  100G  0 loop
└─docker-8:3-790008-pool                                                               253:0    0  100G  0 dm
  ├─docker-8:3-790008-cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5 253:1    0   10G  0 dm   /var/lib/docker/devicemapper/mnt/cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5
  └─docker-8:3-790008-3d23ac827972fc39055a166b0e21d044e974d55547f8622d6352fa3b5d378f1a 253:2    0   10G  0 dm   /var/lib/docker/devicemapper/mnt/3d23ac827972fc39055a166b0e21d044e974d55547f8622d6352fa3b5d378f1a
loop1                                                                                    7:1    0    2G  0 loop
└─docker-8:3-790008-pool                                                               253:0    0  100G  0 dm
  ├─docker-8:3-790008-cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5 253:1    0   10G  0 dm   /var/lib/docker/devicemapper/mnt/cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5
  └─docker-8:3-790008-3d23ac827972fc39055a166b0e21d044e974d55547f8622d6352fa3b5d378f1a 253:2    0   10G  0 dm   /var/lib/docker/devicemapper/mnt/3d23ac827972fc39055a166b0e21d044e974d55547f8622d6352fa3b5d378f1a

其中的loop0和loop1就是两个块设备,也叫回环设备。

thin pool(精简配置资源池,简称精简池)

thin pool基于Thinly-Provisioned Logical Volumes(精简配置逻辑卷)技术创建的资源池。
通过精简配置这项技术,表面上看上去可以过量使用物理存储,实际上占用多少就分配多少的存储。例如,如果十个应用程序各自都请求一个100GB的文件,则系统为每个程序创建一个看上去是100GB的文件,但实际分配给程序的空间是文件实际内容占用的空间,仅在文件保存新的内容时才占用更多的空间。可以很清楚看到,其实利用的就是稀疏文件的特性实现该技术。
通过精简配置这项技术,实际的功能是由thin pool实现,可以在应用程序需要时将其分配给任意数量的设备。精简池可以在需要时进行动态扩展,以节省成本地分配存储空间。

创建thin pool的例子:

sudo dmsetup create docker-8:3-790008-pool \
                  --table "0 20971522 thin-pool /dev/loop0 /dev/loop1 \
                           128 65536 1 skip_block_zeroing"

显示thin pool

[root@localhost mapper]# dmsetup ls
docker-8:3-790008-pool  (253:0)

或者

root@localhost mapper]# ll /dev/mapper/dock*
lrwxrwxrwx. 1 root root 7 Apr 25 13:08 /dev/mapper/docker-8:3-790008-pool -> ../dm-0

base device(基础设备)

base device是最低级别的对象,就是thin pool本身,它只是包含一个文件系统,是每个image和container层的起点,这一起点并不代表它是层,它是设备映射执行所需的的详细信息。
从结构上来说,每个image的最低层是base device的快照,每个image的层都是其下一层的快照,当运行容器时,容器也是一个基于下一层image的快照。所有的container都是构建在一个base device的快照上。

snapshot(快照)

先看一下镜像和容器的结构

贴一张官方的结构图:
two_dm_container
结构特性:

  • 从结构上看,每一层都是下一层的快照。
  • 快照的实际内容就是一套文件系统,最底层的快照可能就是包含了一个实际的除了内核以外的操作系统文件系统结构和文件。
  • 容器之间共享的镜像层只存储在磁盘上一次。
  • 运行容器后,会生成容器的一层快照,处于最顶部的位置,注意容器的快照中没有实际的数据块,只是数据块的指针,实际数据块都在镜像的快照层中。
  • 对容器的进行读写操作时,采用的的时copy on write(写时复制CoW)策略,意思就是在操作容器修改文件等数据时,会把对应的数据块读入容器的读写层,然后对相应的数据块进行修改,修改后容器快照该数据块的指针就遮盖住了镜像快照中的原数据块指针,这样从顶层的视角看下去,就只能看到修改后的文件。

再看一下容器的读写

读原理

dm_container
应用程序从容器读写层快照中读取数据,假设读取的数据块在容器快照中的地址是0x44f。 由于容器的快照中只存储了在镜像快照中实际数据块的地址信息,地址信息包含镜像快照的地址和数据块在镜像快照中的地址,如上图所示,容器地址0x44f块中指向的实际数据块地址是a005e@0xf33,其中a005e是镜像快照的地址用来标识是哪个快照,0xf33是用来标识a005e这个快照内的数据块地址。通过地址从上往下逐层对镜像层进行寻找,找到包含该块的最近镜像层后将块上的数据读入容器的内存中。记住这里读取的是最近镜像快照中的数据块,意思就是读取最新的层数据(可能是刚修改过的文件等)。

写原理
  1. 容器中写文件,device mapper驱动按需分配,先将thin pool中新块(大小为64KB,128的扇区)分配到容器的读写层,然后再向块中写入数据。
  2. 容器中更改文件,从镜像层中将想要更改文件的内容对应的块复制到容器的读写层中,然后执行更改数据操作。
  3. 容器中删除文件,删除容器可读写层中的文件或目录,或者,删除镜像层其父层中存在的文件时后,device mapper会进行标记对应的数据块不可用,继续访问被删除文件或目录时,device mapper驱动将截获对该文件或目录的进一步读取尝试,并响应该文件或目录不存在。

参考:Device Mapper存储驱动官网文档

最后看一下容器快照内的文件系统结构

按照官方文档写的,容器的读写层挂载的目录是在/var/lib/docker/devicemapper/mnt/下。注意,这个地方的挂载不是volumn的挂载,而是容器内整个文件系统目录结构的挂载。

看一下要看哪个容器的文件系统结构,列一下机器上的容器。

[root@localhost mnt]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                 NAMES
4ca3203b1d46        7471fb821b97        "docker-entrypoint.s…"   2 weeks ago         Up 2 weeks          4369/tcp, 5671-5672/tcp, 15691-15692/tcp, 25672/tcp   rabbitmq
90fc8f316cb8        redis               "docker-entrypoint.s…"   2 weeks ago         Up 2 weeks          6379/tcp                                              redis

假设我要看第一个rabbitmq这个容器的文件系统结构,那么inspect一下这个容器。

[root@localhost mnt]# docker inspect 4ca3203b1d46
[
    {   
        // 省略
        ,
        "GraphDriver": {
            "Data": {
                "DeviceId": "101",
                "DeviceName": "docker-8:3-790008-cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5",
                "DeviceSize": "10737418240"
            },
            "Name": "devicemapper"
        },
        // 省略。。。
    }
]

找到DeviceName里面的‘cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5’。
按照容器挂载路径的根目录加上上面这串就是容器文件系统结构的挂载路径,并且看一下这个目录下都有些什么东西。

[root@localhost /]# cd /var/lib/docker/devicemapper/mnt/cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5
[root@localhost cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5]# ls
id  rootfs
[root@localhost cd9ff4c90ed54f4c3f9f6a9bc484249d760033f7d69e8e815673cbee69c070a5]# ls rootfs
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  plugins  proc  root  run  sbin  srv  sys  tmp  usr  var

可以很清楚的看到,在容器的挂载目录下有一个叫做rootfs的目录,这个目录称作”根文件系统“,再看一下rootfs下面有什么,一看就很清楚,下面是操作系统除了内核以外的全部包含文件的目录结构。这才是容器牛逼的地方,它有一个完整的操作系统目录,才是一致性确保的关键技术。

rootfs,根文件系统

通过观察发现容器读写层挂载出来的目录结果发现有一个rootfs目录,在这个目录下包含了除内核以外系统的目录结构和文件,那么是怎么做到让进程只看到对应它自己的rootfs下的目录和文件呢,原理还是Namespace,用的是Mount Namespace,会将rootfs挂载给进程,进程只能看到属于它自己的rootfs下的目录结构和文件。这个就叫做”根文件系统“。
那么Mount Namespace又是怎么做的?它是在chroot这个命令的基础上改进而来,chroot命令的作用就是给一个进程改变视角,为进程设定一个目录,进程以该目录作为工作的根目录,所有的读写都在这个文件系统中执行。

为了看明白,用chroot命令完成一个为进程切换目录视角的功能。接下去为/bin/bash设定一个根目录。
先为/bin/bash创建一个工作根目录,目录名为kxhroot,然后在其下创建必要的bin,lib,lib64文件夹,为了和系统的目录以示区分,另外增加了helloworld文件夹。

[root@localhost ~]# KXHROOT=$HOME/kxhroot
[root@localhost ~]# mkdir -p $KXHROOT
[root@localhost ~]# mkdir -p $KXHROOT/{bin,lib,lib64,helloworld}
[root@localhost ~]# ls $KXHROOT
bin  helloworld  lib  lib64

把当前系统中的bash,ls命令复制到kxhroot/bin下,为了最后在已kxhroot为根目录的环境中执行ls命令查看目录结构。

[root@localhost ~]# cp -v /bin/{bash,ls} $KXHROOT/bin
‘/bin/bash’ -> ‘/root/kxhroot/bin/bash’
‘/bin/ls’ -> ‘/root/kxhroot/bin/ls’
[root@localhost ~]# ls $KXHROOT/bin
bash  ls

把bash、ls命令所需的库文件全部复制到kxhroot/lib64的目录下,确保命令是可独立被使用

[root@localhost ~]# list="$(ldd /bin/bash | egrep -o '/lib.*\.[0-9]')"
[root@localhost ~]# echo $list
/lib64/libtinfo.so.5 /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2
[root@localhost ~]# for i in $list; do cp -v "$i" "${KXHROOT}${i}"; done
‘/lib64/libtinfo.so.5’ -> ‘/root/kxhroot/lib64/libtinfo.so.5’
‘/lib64/libdl.so.2’ -> ‘/root/kxhroot/lib64/libdl.so.2’
‘/lib64/libc.so.6’ -> ‘/root/kxhroot/lib64/libc.so.6’
‘/lib64/ld-linux-x86-64.so.2’ -> ‘/root/kxhroot/lib64/ld-linux-x86-64.so.2’

[root@localhost ~]# list="$(ldd /bin/ls | egrep -o '/lib.*\.[0-9]')"
[root@localhost ~]# for i in $list; do cp -v "$i" "${KXHROOT}${i}"; done
‘/lib64/libselinux.so.1’ -> ‘/root/kxhroot/lib64/libselinux.so.1’
‘/lib64/libcap.so.2’ -> ‘/root/kxhroot/lib64/libcap.so.2’
‘/lib64/libacl.so.1’ -> ‘/root/kxhroot/lib64/libacl.so.1’
cp: overwrite ‘/root/kxhroot/lib64/libc.so.6’? n
‘/lib64/libpcre.so.1’ -> ‘/root/kxhroot/lib64/libpcre.so.1’
cp: overwrite ‘/root/kxhroot/lib64/libdl.so.2’? n
cp: overwrite ‘/root/kxhroot/lib64/ld-linux-x86-64.so.2’? n
‘/lib64/libattr.so.1’ -> ‘/root/kxhroot/lib64/libattr.so.1’
‘/lib64/libpthread.so.0’ -> ‘/root/kxhroot/lib64/libpthread.so.0’

目录结构,执行文件和库文件都已经准备好,最后,使用chroot命令执行/bin/bash,并设置执行所见的根目录是kxhroot
执行后,再执行ls命令,可以很清楚看到bash命令所见到的一级目录正是kxhroot目录,并且命令的执行环境就是chroot设置的根目录。这就是容器进程看到的目录结构视角是属于它的rootfs目录结构的原因。可以通过exit命令退出chroot运行环境,看到这个exit老熟人命令,那是更清楚不过了吧。

[root@localhost ~]# sudo chroot $KXHROOT /bin/bash
bash-4.2# ls
bin  helloworld  lib  lib64
bash-4.2# exit
exit

The End

在本地,使用minikube,完成hello1内部调用hello2

目标

本地k8s环境使用minikube。
hello1运行两个pod,能够向cluster外部提供服务。
hello2运行两个pod,仅能够在cluster内部提供服务。
在本地环境中,请求hello1的接口,在hello1中以service name的方式请求hello2的接口。
hello1与hello2的代码已完成,并以打成镜像上传至私有的镜像仓库中。

准备

创建k8s拉取镜像所需的secret

使用下列命令创建secret,将下列的镜像仓库地址、用户名、密码换成实际内容。
参数docker-registry表示使用docker镜像仓库。
参数registry-tencent是自定义的secret的名字,用于写在部署文件的imagePullSecrets设置项上,表示采用该secret。

kubectl create secret docker-registry registry-tencent --namespace=default \
    --docker-server=镜像仓库地址 --docker-username=用户名 \
    --docker-password=密码

创建结果:

secret/registry-tencent created

准备部署文件

本地目录结构。

.
|
|---hello
| |---depoly
| | |---hello2
| | | |---deployment.yaml
| | | |---service.yaml
| | |---hello1
| | | |---deployment.yaml
| | | |---service.yaml

hello1的部署文件

hello1的deployment.yaml文件内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello1 
  namespace: default
  labels:
    app: hello1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello1
  template: 
    metadata: 
      labels: 
        app: hello1 
    spec:
      containers: 
      - name: hello1
        image: xxx.tencentcloudcr.com/hello/hello1:0.0.3
        imagePullPolicy: Always
        ports:
        - containerPort: 3000
      restartPolicy: Always
      imagePullSecrets:
      - name: registry-tencent

hello1的service.yaml文件内容。

apiVersion: v1
kind: Service
metadata:  
  name: hello1
  namespace: default
spec:
  externalTrafficPolicy: Cluster
  type: NodePort
  selector:   
    app: hello1
  ports:  
    - port: 3000
      targetPort: 3000
      protocol: TCP

注意,hello1要对cluster外部提供服务,type可以设置成NodePort,在ports设置项下面,nodePort不写端口号的时候,k8s会自动生成一个节点端口号。

hello2的部署文件

hello2的deployment.yaml文件内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello2
  namespace: default
  labels:
    app: hello2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello2
  template: 
    metadata: 
      labels: 
        app: hello2 
    spec:
      containers: 
      - name: hello2
        image: xxx.tencentcloudcr.com/hello/hello2:0.0.3
        imagePullPolicy: Always
        ports:
        - containerPort: 3001
      restartPolicy: Always
      imagePullSecrets:
      - name: registry-tencent

hello2的service.yaml文件内容。

apiVersion: v1
kind: Service
metadata:  
  name: hello2
  namespace: default
spec:
  selector:   
    app: hello2
  ports:  
    - port: 3001
      targetPort: 3001
      protocol: TCP

注意,hello2仅对cluster内部提供服务,type设置成ClusterIP,如果没有设置type,默认就是ClusterIP。

部署

部署hello1

进入hello1目录。
创建deployment。

kubectl apply -f deployment.yaml   

创建结果:

deployment.apps/hello1 created

创建service。

kubectl apply -f service.yaml

创建结果:

service/hello1 created

部署hello2

进入hello2目录。
创建deployment。

kubectl apply -f deployment.yaml   

创建结果:

deployment.apps/hello2 created

创建service。

kubectl apply -f service.yaml

创建结果:

service/hello2 created

查看部署后的状态

显示部署后hello1和hello2已经运行的pod。

kubectl get pods

NAME                      READY   STATUS    RESTARTS   AGE
hello1-759f5c79c6-47jtk   1/1     Running   0          4h40m
hello1-759f5c79c6-m5rsk   1/1     Running   0          4h40m
hello2-97bfd5b4d-fs85v    1/1     Running   0          3m44s
hello2-97bfd5b4d-tsnls    1/1     Running   0          3m44s

STATUS这一列是运行状态,Running就代表至少容器已经运行起来,如果不是Running状态,可以执行下面这条pod描述命令查看pod的运行信息,失败的信息具体可以在执行结果的Events中看到。

kubectl describe pod hello1-759f5c79c6-47jtk

如果想看一下容器内,服务程序在控制台输出的内容可以执行日志显示命令进行查看。

kubectl logs -f hello1-759f5c79c6-47jtk

查看hello1 service的ip和端口信息。

kubectl describe service hello1
Name:                     hello1
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=hello1
Type:                     NodePort
IP Families:              <none>
IP:                       10.97.21.16
IPs:                      10.97.21.16
Port:                     <unset>  3000/TCP
TargetPort:               3000/TCP
NodePort:                 <unset>  30662/TCP
Endpoints:                172.17.0.3:3000,172.17.0.4:3000
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

查看hello2 service的ip和端口信息。

kubectl describe service hello2
Name:              hello2
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=hello2
Type:              ClusterIP
IP Families:       <none>
IP:                10.110.139.92
IPs:               10.110.139.92
Port:              <unset>  3001/TCP
TargetPort:        3001/TCP
Endpoints:         172.17.0.5:3001,172.17.0.6:3001
Session Affinity:  None
Events:            <none>

执行,方式1,本地端口映射

将本地的9000端口映射的hello1 service的服务端口上,这里说的服务端口就是service.yaml中的ports.port。

kubectl port-forward service/hello1 9000:3000 &

第一次执行:

curl http://127.0.0.1:9000/api/hello1                 

执行结果:

Handling connection for 9000
{"requestServiceIp":"172.17.0.4","responseServiceIp":"172.17.0.5"}%                                                           

第二次执行:

curl http://127.0.0.1:9000/api/hello1

执行结果:

Handling connection for 9000
{"requestServiceIp":"172.17.0.4","responseServiceIp":"172.17.0.6"}%                                                           

可以看到,这种方法因为是始终是经过hello1的一个pod进行请求,美中不足。
hello2的两个pod轮番处理请求。

执行,方式2,minikube代理到NodePort

执行下面的命令,minikube会对hello1 service的两个NodePort进行代理和映射,命令执行后会显示映射后的访问url,默认会打开浏览器访问映射的根地址。

minikube service hello1

第一次执行:

curl http://127.0.0.1:61937/api/hello1                 

执行结果:

{"requestServiceIp":"172.17.0.4","responseServiceIp":"172.17.0.6"}%                                                           

第二次执行:

curl http://127.0.0.1:61937/api/hello1

执行结果:

{"requestServiceIp":"172.17.0.3","responseServiceIp":"172.17.0.5"}%                                                           

可以看到,这种方式,hello1的两个pod的ip轮番切换,轮番接受请求。

MQTT协议QoS质量等级

使用PUBLISH报文在推送消息时,在报文的固定报头这一个字节的第1位和第2位设置值,用于控制QoS等级,也就是消息发送的质量等级,QoS等级有三种:

  • 最多分发一次
  • 至少分发一次
  • 只分发一次。

最多发送一次

QoS值为00(二进制),也就是无需签收。
客户端发送PUBLISH报文给服务端,服务端无需回复PUBACK报文,服务端再发送PUBLISH报文给订阅了当前主题的其他客户端发送PUBLISH报文后,其他客户端也无需回复PUBACK报文。

至少发送一次

QoS值为01(二进制),也就是需要一次签收。
客户端发送PUBLISH报文给服务端,服务端会回复PUBACK报文,表示服务端签收,服务端再发送PUBLISH报文给订阅了当前主题的其他客户端,发送PUBLISH报文后,按照协议要求其他客户端也要回复PUBACK报文,如果其他客户端没有回复PUBACK的,按照协议标准,服务端会重发PUBLISH报文,但是具体实现的服务端肯定不会无限制重发,会根据实际实现的性能要求和配置来决定重发次数,甚至于不重发,比如OneNet平台在没有收到其他客户端的签收报文时,默认就不会重发消息。

按照协议标准,服务端再没有收到其他客户端的签收报文时,可能会启动离线消息功能,服务端会将未签收的消息全部存下来,在下次客户端上线后,都会发给客户端,注意,此时如果不签收,还是会被存为离线消息。离线功能是否开启,依据于CONNECT报文的非固定报头中的Clean Session这一位,这一位如果设置的二进制码为0,就开启离线消息功能,设置1就代表关闭。

只发送一次

QoS值为02(二进制),需要二次确认签收。
客户端发送PUBLISH报文给服务端,服务端立即回复PUBREC报文进行签收,表示服务端收到发布的消息;然后,客户端回复PUBREL报文进行二次问询,服务端再回复PUBCOMP报文进行二次签收确认,表示发布消息整个过程完成。

服务端只有完成了PUBCOMP报文发送后,才能将消息再推送给其他的客户端;在PUBCOMP报文前,任何一步失败了,就不满足服务等级为2(十进制)的要求,那么服务端都不会把消息推送给其他客户端。

客户端与服务端推送消息,在满足整个质量等级的确认和签收过程后,服务端采用同样的质量等级和过程将消息推送给其他客户端,其他客户端要是没有正常回复两次签收的过程,服务端会要么将消息扔掉,要么依据Clean Session的设置将消息作为离线消息处理(按照标准协议的定义)

注意:大部分云平台都没有实现质量等级为2的过程,如果要实现,需要自行搭建。

演讲学习笔记,TED演讲方法学习总结

TED演讲框架

1. 演讲的核心规律
2. 构思和编排一场演讲
3. 让演讲内容更加出彩

1. 演讲的核心规律

TED演讲的核心规律就是:每次只传播一个观点

为什么是一个观点

TED演讲的时长是18分钟,在短时间内,不可能讲的清楚所有你认为的重点,都是重点就等于没有重点,真正的核心**就是单点突破,只讲一个最让人印象深刻的观点,观点要能够吸引人、打动人、容易传播、有价值、口号式的,比如:

  • 充电5分钟,通话半小时。
  • 人如何有激情地活着。
  • 人们买的不是你的产品,而是你的信仰。
  • 诗歌一思考,狗狗就发笑。

一个观点的好处

  1. 观点明确,准备材料时,目的明确,演讲逻辑趋向简单,听的人相对容易理解。
  2. 演讲时,就算准备再充分,也肯定会紧张,只讲一个观点,大大减少卡壳的机会,对脑容量要求就会少些。

2. 构思和编排一场演讲

三个步骤:
2.1. 设计演讲的形式。
2.2. 设计内容的来源。
2.3. 设计演讲的模式。

2.1. 设计演讲的形式

要讲故事,而不是讲道理

讲道理的案例:

由于现代社会人口的自由迁移越来越普遍,很多城市的居民是外来移民,大家因为不同的原因,怀着不同的目的,聚集到一个城市。所以,我们的社会结构由过去的彼此都认识的乡村小县城熟人模式,变成了一个电梯走廊的居民却素不相识的陌生人。因此,今天的社会,人们有更大的概率遭遇和陌生人相处的危险。有人就呼吁:一定要注意安全,尤其是单身女生。不要单独夜里上街,听到敲门声不要随便给陌生人开门,一定要有安全意识。

同样的事情,讲故事的案列:

罗辑思维有一期节目,罗振宇老师找《奇葩说》的黄执中代班,黄执中开篇讲了这么一个故事:你出差到一个城市,晚上在酒吧和一个刚认识的女孩儿喝酒,结果两人推杯问盏,不一会儿自己就喝到神志不清。也不知过了多久,自己等醒来发现,我怎么躺在一个浴缸里面,而浴缸的水却是冰冷的,血红色的水面还漂浮着大块的冰块。这时你看到浴缸旁边有一张纸条,上面写着“不要乱动,请立刻给医院打电话等等”。

没人喜欢听别人讲大道理,还没讲到一半,估计就刷手机,一般听的人的反应就是,这道理我还不懂,要你来讲。
虽然这个案例的故事本身就是个传的很普遍的谣言,但听的人更容易听进去,印象还会比较深刻,也很容易讲,讲起来也顺口,随口就能讲,不容易卡壳。

2.2. 设计内容的来源。

三个方向:
2.2.1. 一堂课
2.2.2. 关键时刻
2.2.3. 克服弱点

2.3. 设计演讲的模式。

3. 让演讲内容更加出彩

安装k8s实践一(在线镜像安装、root账号)

环境

  • 2个节点,每个节点都是虚拟机,每个节点2核2G
  • 每个节点设置使用netplan设置静态地址
  • k8s 1.22
  • Ubuntu Server 20.04.3
  • 使用root账户安装

1.安装docker

系统安装过程中不要选装docker,版本不一样,如果安装过docker,先卸载。
卸载docker。

sudo apt-get remove docker docker-engine docker.io containerd runc

安装docker。

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io

2.安装kubeadm

apt-get update
apt-get install -y apt-transport-https ca-certificates curl

Ubuntu系统中如果安装了ca-certificates、curl,会跳过已安装的组件。

curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
sudo tee /etc/apt/sources.list.d/kubernetes.list <<-'EOF'
deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main
EOF
apt-get update
apt-get install kubeadm

在上述安装 kubeadm 的过程中,kubeadm 和 kubelet、kubectl、kubernetes-cni 这几个二进制文件都会被自动安装好。

3.关闭swap

vi /ect/fstab,注释关于swap的配置。

echo vm.swappiness=0 >> /etc/sysctl.conf

reboot

重启完成后,使用命令free -m,其中的swap均为0。

4.安装集群

4.1.准备k8s镜像

直接使用kubeadm init命令安装,基本都会报下载镜像失败,因为国内连不上https://k8s.gcr.io/v2/

可以手动从docker hub把需要的镜像下载下来,再修改tag到和官方的一致。

写一个拉k8镜像的shell,vi pull_k8s_images.sh

set -o errexit
set -o nounset
set -o pipefail

## define urls
GCR_URL=k8s.gcr.io
DOCKERHUB_URL=gotok8s

## images list
images=(
kube-apiserver:v1.22.1
kube-controller-manager:v1.22.1
kube-scheduler:v1.22.1
kube-proxy:v1.22.1
pause:3.5
etcd:3.5.0-0
coredns:v1.8.4
)

## pull images, rename to k8s.gcr.io tag prefix
for imageName in ${images[@]} ; do
  docker pull $DOCKERHUB_URL/$imageName
  docker tag $DOCKERHUB_URL/$imageName $GCR_URL/$imageName
  docker rmi $DOCKERHUB_URL/$imageName
done

## modify coredns url
docker tag $GCR_URL/coredns:v1.8.4 $GCR_URL/coredns/coredns:v1.8.4
docker rmi $GCR_URL/coredns:v1.8.4

执行pull_k8s_images.sh

./pull_k8s_images.sh

4.2.修改docker的cgroup驱动为systemd

“cgroupfs”作为Docker cgroup驱动程序,k8s推荐的驱动程序是“systemd”。
手动改一下。

vi /etc/docker/daemon.json

编辑内容内容:

{
 "exec-opts":["native.cgroupdriver=systemd"]
}

保存退出,重启docker。

systemctl restart docker

4.3.初始化k8s的control-plane

kubeadm init --kubernetes-version=v1.22.1 --pod-network-cidr=10.0.2.0/24

如果这一步报错,纠正错误后,一定要执行kubeadm reset后再进行init。
正常初始化后,显示成功信息,注意信息中提示要配环境变量、配置pod网络、使用token加入节点:

Your Kubernetes control-plane has initialized successfully!
 
To start using your cluster, you need to run the following as a regular user:
 
  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config
 
Alternatively, if you are the root user, you can run:
 
  export KUBECONFIG=/etc/kubernetes/admin.conf
 
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/
 
Then you can join any number of worker nodes by running the following on each as root:
 
kubeadm join 192.168.113.131:6443 --token xa595l.7siamzikrh69n82n \
        --discovery-token-ca-cert-hash sha256:a52af970bd732927096348ead7cb9a043d03667434248b4234c409b58a147d47

4.4.配置环境变量

按照上面的显示内容配置环境变量。
非root用户配置环境变量。

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

root用户配置下面的环境变量:

export KUBECONFIG=/etc/kubernetes/admin.conf

配置完成后,看一下node有几个

kubectl get nodes

结果显示有一个node,状态是notready。
显示一下这个node的详细信息。

kubectl describe node kxh

在显示的一堆信息中conditions的表中最后有个Ready开头的行,在这一行的最右侧有Message显示NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized,意思就是要装网络插件。

4.5.安装网络插件

在kubeadm init完成后的显示的成功信息中🈶️提示了一个链接,访问该链接。

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

在显示的官网页面中,点击Flannel is an overlay network provider that can be used with Kubernetes.

接下去会跳转套Flannel的github页面,找到Deploying flannel manually这一节,有一串flannel配置文件地址:

https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

下载flannel配置文件,因为可能要翻墙,提前下好传到系统中。

wget url --no-check-certificate https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

执行flannel配置

kubectl apply -f kube-flannel.yml

成功执行后,通过命令查看flannel容器的运行情况。

kubectl get pods -n kube-system

可以看到kube-flannel跑了一个是实例,处于running状态。
再看一下master节点的状态。

kubectl get nodes

能看到master节点已经是Ready的状态。

5.部署work节点

5.1.确认hostname

所有节点的hostname必须设置不同,worker节点的hostname设置成要求的worker节点的名字。

vi /etc/hostname

比如可以设置成k8s-worker01

5.2.确保docker的cgroup驱动为systemd。

如果不是systemd的,kubelet运行会失败。

5.3.准备必要的镜像

必须要在worker节点上提前准备好两个docker images。

k8s.gcr.io/kube-proxy:v1.22.1
k8s.gcr.io/pause:3.5

可以先在master节点上docker save镜像文件。

docker save -o kube-proxy-v1.22.1.tar k8s.gcr.io/kube-proxy:v1.22.1
docker save -o pause-3.5.tar k8s.gcr.io/pause:3.5

再到worker节点上docker load镜像文件。

docker load < kube-proxy-v1.22.1.tar
docker load < pause-3.5.tar

5.4.worker节点加入集群

执行kubeadm init完成时显示的join命令。
如果有显示证书等文件已经存在的,先执行kubeadm reset后,再执行kubeadm join

kubeadm join 192.168.113.131:6443 --token xa595l.7siamzikrh69n82n \
        --discovery-token-ca-cert-hash sha256:a52af970bd732927096348ead7cb9a043d03667434248b4234c409b58a147d47

执行成功后,稍过一会,在master查询所有节点信息,能看到worker节点已经是reader了。

kubectl get nodes

也可以查看worker节点的详细启动信息。

kubectl describe node k8s-worker01

如果第一次做的时候,没有在worker节点上准备好必要的镜像,那么join完的woker节点是notready的状态,这是后可以在master上驱逐以及删除woker节点,并进行修复后,再join。

kubectl drain k8s-worker01 --force
kubectl delete nodes k8s-worker01

5.5 可能出现的podCIDR问题

在节点看似都Ready后,要看一下master上pod的状态,会看到有一个运行在worker节点上的flannel处于crashBackOff状态。

kubectl get pods -n kube-system

通过查看出错flannel pod的日志,会发现日志中显示未设置podCIDR。

kubectl logs kube-flannel-ds-dsw2k -n kube-system

按照这篇帖子,对worker节点进行CDIR的设置,可以解决,原因参考这个链接。

按照解决方法,对worker节点打一个patch,打完以后,过一小会就能看到flannel pod都正常运行了。

kubectl patch node k8s-worker01 -p '{"spec":{"podCIDR":"10.0.2.0/24"}}'

6.测试

docker pull nginx
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get pods,svc

可以看到nginx service对节点映射的端口号,我这里是32416,使用worker节点地址加上端口号在浏览器中访问可以看到nginx默认网页内容。

DDD学习总结笔记

笔记内容

  • 理论篇
    • 战略设计,使用事件风暴
      • 产品愿景
      • 业务场景分析
      • 领域建模
      • 微服务拆分
    • 战术设计
      • 分层设计
      • 领域对象设计
      • 代码结构设计
  • 案例篇
    • 在线请假 、考勤管理作为案例项目
    • 需求分析
    • 战略设计
    • 战术设计
    • 案例代码

笔记文件

DDD学习总结笔记PDF
提取码:6d6c

梁宁的《增长思维》学习笔记,增长是能力

增长是什么

增长是能力,是每个人,每个组织永远思考的能力。这个能力就是持续做出正确决定或者是正确判断的能力。判断正确就增长,判断错误就损失。

为什么学

为了年轻时,人生能够拥有主动,为了年老时,人生能够拥有智慧。这种能力终将成为人一生的财富。

如何修习

经历几十年世事,不断迭代,在更复杂处境中,面对变化做出判断。体会为什么增长,为什么不增长。

实现增长

创始人具备的痛包括:战略痛,产品痛,组织痛。天天痛,天天琢磨。打通一个痛,就是一波新增长。真正的增长就是解决了某个痛后实现的。

企业和个人的增长是同一个思维框架。对个人也是一样,解决痛,打破界限。个人也可以改善生存处境。

决定的误区

区分决定和惯性的动作,人往往都是在进行惯性的动作,并非在做决定,往往用战术的努力回避做战略上真正的思考。了解这一点,逐步学会主动从惯性中抽离,能够全局思考。

做决定的学习框架

做决定首先是技术,然后再上升到艺术。技术就是思考框架和决策模型,包括:

  • 全景作战地图,各种级别玩家的打法以及是如何参与竞争的。
  • 四个增长关卡,机会拿捏,模式取巧,组织扩容,战略借势。突破关卡就是增长。
  • 几十个真实案例。从企业的生死进退中学习。

总之

发展才是硬道理。

演讲学习笔记,演讲的结构

演讲的结构

  1. 黄金圈法则结构;
  2. PREP结构;
  3. 时间轴结构;
  4. 金字塔结构;

黄金圈法则结构

从里到外有三个圈:
*(Why)我为什么要做这个项目?
*(How)这个项目如何帮助、改变他人?
*(What)这个项目有什么价值?

在这三个部分中,各加入一个事,将是一个杀手级的演讲。

案例:
2015年,Facebook创始人扎克伯格在清华经管学院做过一次中文演讲:“今天我想讨论改变世界的话题。今天我想告诉你三个故事,就三个故事。”
第一层,Why,自己为什么要做Facebook,为什么想改变世界。
第二层,How,如何改变世界,有了目标和使命之后,怎么才能做好,他的回答关键是——专注。
第三层,what,故事是关于向前看,不要放弃,要一直向前看,你们可以成为全球的领导者,可以提高人们的生活,可以用互联网影响全世界。

PREP结构

  • Point,观点;
  • Reason,理由;
  • Example,案例;
  • Point,再次讲观点;

PREP结构的关键是,上来就要抛出观点,不要有半点犹豫;后面的理由两三个即可,通常建议有两个,这样比较容易hold住;案例部分,最好讲自己的经历或故事,会更有说服力;最后再重复和强调一下你的观点。

案例
罗永浩介绍锤子手机:
Point,观点:每一个内心拒绝平庸的人都应该配备一台锤子手机。
Reason,理由:第一个理由是锤子手机总是充满了话题,无论是品牌、外观、配置,还是软件,都会让你处处引人注目,随时随地成为话题中心。第二个理由是锤子手机非常低调,锤子手机的低调全世界都知道。
Example,例子:前几天,朋友聚会,来了一位漂亮的姑娘,她的手机要没电了,当时还正好没有充电宝和充电器。这个时候,我从容地拿出了锤子坚果Pro手机,帮美女的手机续航成功。美女第一次知道锤子手机还有帮苹果充电的功能,后来我们就加了微信,成了好友……再后来,我们就不说了。
Point,论点:想要你的低调被全世界都知道,锤子是你最好的选择。

时间轴结构

时间就是:

  • 过去;
  • 现在;
  • 未来;

时间的前后关系是一种强逻辑关系,时间轴结构的意义在于,通过时间线索,可以将不同的事物或者故事联系起来,并赋予清晰的逻辑。

案例
美国前总统亚伯拉罕·林肯就用“时间轴结构”来设计了这一演讲。
过去,八十七年以前,我们的祖先在这片大陆上建立了一个国家,它孕育于自由,并且献身给一种理念,即所有人都是生来平等的。
当前,我们正在从事一次伟大的内战,我们在考验,究竟这个国家,或任何一个有这种主张和这种信仰的国家,是否能长久存在。
未来,我们应该在此献身于我们面前所留存的伟大工作,要使那民有、民治、民享的政府不致从地球上消失。”
在这里,林肯总统用的时间轴是“过去——现在——未来”。这种结构,背后的逻辑是:“过去——现在”的发展过程,体现了事物发展的规律或趋势。而通过这种趋势,我们可以去预测未来。

金字塔结构

“问题——原因——对策——结果”结构模型。
这种模型在公司会议、报告、发言上,非常实用。

k8s 最小调度单位是Pod而不是容器

再总结下容器

容器就是对进程采用Namespace做隔离,用CGroups做资源限制,用DeviceMapper等存储驱动管文件存储,用rootfs映射文件系统,这就是大名顶顶的容器。

容器的进程模型

容器是单进程模型,不是说一个容器就只能对应一个进程,所谓的单进程模型是指容器没有管理多个进程的能力。
多个进程的管理能力体现在:

  1. 搞一个运行web服务的镜像,容器跑起来后,在容器内手动再跑一个nginx,有nginx对容器外部提供web服务的反向代理,如果nginx挂掉了,对外的代理就会断掉,站在容器角度是不知道nginx,也不会有后续重启等操作。要多进程协同工作,必须依靠进程自身的代码去生成子进程,并且管理子进程,比如nodejs的cluster。
  2. 进程工作并非就是独立的,往往会需要几个进程协同完成工作,所以系统中还有进程组的概念,多个进程之间共享信号,进程间通信,共享文件,使用容器将各个进程打包在内,关键是要确保他们在一台主机上。

进程组就是一组进程的集合,向一个进程发送信号,在组内的其他进程都会收到相同信号,在系统中进程组往往和会话绑在一起,一个会话的ID就是进程组中leader进程的PID,方便管理进程的创建,销毁。比如使用tty登录一个系统,登录后,启动shell时将会创建新的会话,shell进程会作为会话的的leader进程,随后shell里面运行的进程都将属于这个会话,当shell退出后,所有该用户运行的进程将退出。会话的leader进程会成为tty的控制进程,当会话的前端进程组发生变化时,控制进程负责更新tty上关联的前端进程组,当tty要关闭的时候,控制进程所在会话的所有进程都会收到SIGHUP信号。

使用容器为调度单位的问题-成组调度

假设做三个容器,分别是:main,work1,work2。
假设有两个,分别是:node1(内存资源给三个容器不够),node2(内存资源给三个容器够)。
不用k8s,而用Docker Swarm就能看出差异。
要求是这三个容器因为相互之间要进行进程间通信、共享文件,所以必须部署在同一台主机上,所以work1和work2的亲密性设置affinity=main。
顺序执行:"docker run main" "docker run work1" "docker run work2"
创建容器后,进入Swarm调度序列,在将main、work1调度到node1上之后,发现node1上剩余的内存资源已经不足以再调度work2了,但是按照亲密性要求,work2又必须调度到node1之上,结果就work2的调度报错,main和work1也不能正常运行。
这就是一个典型的成组调度问题。

k8s的成组调度解决方案就是以pod为单位

在k8s中,pod是最小的调度单位,pod又可以包含多个容器,在调度的时候必须按照pod的资源需求进行调度计算,这样的话,在上面的案例中,三个容器就属于同一个pod,在调度前通过计算pod的内存资源需求就会发现node1的资源是不够的,node2才符合要求,那么就会将pod直接调度到node2上。这就是pod存在的一个很重要的结果。
还有为什么现在说k8s是操作系统,也是因为它在容器领域实现了进程组这一概念。

并不存在pod,它是逻辑概念,关键要解决的还是容器的共享问题

网络共享

pod只是一个逻辑概念,不存在一个所谓的pod的边界或者隔离环境,说白了pod就是一组共享了某些资源的容器。最常说的共享的资源就是Network Namespace和Volume。
问题就是两个容器如何共享Network Namespace,可以通过Join Network Namespace的方式让B容器的网络依赖于A容器的网络,但是这样就有个问题,那就是B的网络要可用,一定要先启动A,这样就造成两个容器不是对等的关系,A挂了,B的网络也就不通了。
k8s的解决方案:
截屏2021-07-27 下午5 46 25

在图中可以看到,每个pod中都会预装一个infra容器,而且永远是第一个被安装的容器,它会提供一个Network Namespace,用户建的容器会执行Join Network Namespace加入到infra的网络中,意味着:

  • 一个pod的ip地址就一个。
  • 在pod中的容器看到的网络设备是一样的。
  • 容器用localhost就可以相互访问。
  • pod的生命周期由infra决定,并不是用户容器。

Infra 容器一定要占用极少的资源,所以它使用的是一个非常特殊的镜像,叫作:k8s.gcr.io/pause。这个镜像是一个用汇编语言编写的、永远处于“暂停”状态的容器,解压后的大小也只有 100~200 KB 左右。

volumn共享

正因为pod这一设计,pod内的容器都是部署在一个主机上,所以天然就具备对主机上相同目录的映射能力。
一个案列:

apiVersion: v1
kind: Pod
metadata:
  name: volumn-share-demo
spec:
  restartPolicy: Never
  volumes:
  - name: share-dir
    hostPath:      
      path: /page
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: share-dir
      mountPath: /home/nginx/html
  - name: build-container
    image: centos
    volumeMounts:
    - name: share-dir
      mountPath: /dist
    command: ["/bin/sh"]
    args: ["-c", "echo Hello World > /dist/index.html"]

可以看到先声明了一个volumn,并挂在到主机的/page目录下。
然后,build-container这个容器将主机目录/page挂载进自身容器目录/dist,并且执行命令生成一个hello world内容的html文件。
最后,nginx-container这个容器将主机目录/page挂载进自身容器目录/home/nginx/html,并且能够读到index.html这个文件。
以上就是volumn的共享。

总结

为什么是pod:

  • 进程组到容器组。
  • 成组调度。
  • 网络共享。
  • volumn共享。

梁宁的《增长思维》学习笔记,看到大机会

学习、磨练

通过学习长见识,逐渐构建知识结构,历经挫折向前走,拔高视野,提高认知维度看全局,看到大机会。

安立欣的知识结构的案列

安立欣的第一桶金通过分析上海和北京两地房地产成交量的重大差别,分析并找到贷款周期过长的原因,成立融资担保公司为购买二手房的人提供担保,赚取了人生第一桶金,靠的是他的学术素养,大机会不容易看得见,怎么看到,就是靠知识结构,来源于对不同国家发展周期的对照,同一个国家不同地区的差异。

谭智历经挫折的案列

谭智,40到50岁看似风光的外企高管身份实则是历经挫折,从高管上升到了熟悉资本运作的投资人,并且拥有全球视野的能力,通过资本运作和操盘整合框架广告并卖给了分众。谭智说过一句话:年龄是财富,挫折也是财富。

认知维度看全局,看到未来

1987年,张晓彬走访了美国三十多个城市,去美国看到了股市,以及认为在**的可能性,积极参与并推动,建立了证券交易所联合办公室任联办秘书长,联办极大推动了**股市的诞生。

总结:

你的知识结构就是你的天眼,所以要终身学习,多长见识。

梁宁的《增长思维》学习笔记,踩中风口闪电式扩张

野蛮人法则

一旦踩中一个风口,别打磨产品,别打磨组织架构,就砸各种资源,野蛮生长,抢占阵地,踩中风口,面对市场的不确定,优先考虑的是速度,而不是效率。俗称野蛮人法则。
再说一遍,突然你眼前的市场,巨大又不确定时,速度第一,速度第一。

周航的易到是网约车的先驱。3个月后,卡兰尼克才在美国成立Uber。2年后的2012年,程维才用80万启动了滴滴的创业。但是易到输给了滴滴。
周航认同的是:商业常识、道德、经济学原理,以及一个受到广泛认同的理念,没有效率的增长,结果在速度第一的阶段选择了效率,是慢性自杀。
滴滴烧钱补贴、混乱管理、产品重复开发、资源浪费等等,速度第一,抢占了市场。

快手和抖音的推广。
快手曾经有一种观念:“花钱买推广是产品力不足的表现。”所以,他们喜欢说一句话:“我们没有花过一分钱推广。”
张一鸣在2019年春节请抖音做一个激进的推广预算,提交的方案是8天1个亿人民币。张一鸣问CFO,最大化能调用多少钱?CFO说5亿美金。张一鸣说,那就5亿美金8天,全砸下去。春节8天所有人都闲,娱乐是核心,抖音和快手就这样拉开了差距。

四个阶段

海盗,海军陆战队,军队,警察

  • 海盗,灵活性,成员彼此熟悉和默契,没有正式流程,判断机会快速出击,承担成熟公司不能或者不愿意承担的风险。取得创业第一阶段的积累。
  • 海军陆战队阶段攻下海滩,攻打上岸需要一定的组织规模,层级少,全员面对着一个目标,面对市场的炮火,随时应对,随时反馈,灵活应变。
    小米的去管理化,当时的小米只有三层构架:高管、核心主管、员工。办公室的布局准则是:一层产品、一层营销、一层硬件、一层电商,每层一个创始人坐镇,能实现一竿子扎到底的执行。几个合伙人之间互不干涉。如果没有什么事情的话,连彼此在干嘛都不知道。自己的事情自己说了算,这保证了决策非常非常地快。
  • 军队占领整个大陆。海军陆战队冲上滩头之后,不可能永远这样打下去。闪电式扩张也有尽头,就是市场净空不足的时候。
    人人都用智能手机的时候,市场净空只能依靠换机。这时市场净空不足,闪电式扩张当然要停止。小米开始抓管理,组织架构调整,引入职级,从3层变成10个层级,小米在管理制度上,就越来越接近一个更久远的军队的组织形态。
  • 警察维持秩序,成熟的组织,比如银行、外企。它们相当比例的员工,主要工作都是确保客户与业务是按照自己的秩序来运作的。

总之

该抢就得抢,该冲就要充,该打磨就要打磨,该秩序的时候要秩序,搭配对应的团队和组织,什么阶段就该干什么事儿。

Docker原理,资源限制,CGroups

隔离做不到的事情

通过Linux Namespace技术,可以给每个进程在一个看似属于它自己的环境中运行,但是并不是系统中所有的的功能都能隔离,也就是隔离的并不彻底,这也是与虚拟化技术比较起来较弱的地方。

也就是说系统中有很多资源是不能被Namespace化的,比如cpu,内存,磁盘,带宽用量等,这时候就要用到Linux Cgroups技术限制进程使用的资源量。

如何实现的

Linux系统提供的Cgroups是通过配置相关的资源文件来实现。

Cgroups配置文件的根目录是:

/sys/fs/cgroup

可以限制的资源种类

列一下系统中可以被Cgroups限制的资源种类:

[root@localhost ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

上面列出来的cgroup路径下的每一个文件夹的名字就是一种资源的名字。
在每一个资源文件夹下可以自行创建一个文件,对应一个进程的限制。

动手限制一个进程的CPU用量

创建一个文件夹限制一个进程的cpu用量

在/sys/fs/cgroup/cpu路径下创建一个demo文件夹。

[root@localhost cpu]# pwd
/sys/fs/cgroup/cpu
[root@localhost cpu]# mkdir demo01
[root@localhost cpu]# cd demo01
[root@localhost demo01]# ls
cgroup.clone_children  cgroup.event_control  cgroup.procs  cpuacct.stat  cpuacct.usage  cpuacct.usage_percpu  cpu.cfs_period_us  cpu.cfs_quota_us  cpu.rt_period_us  cpu.rt_runtime_us  cpu.shares  cpu.stat  notify_on_release  tasks

能够看到,创建完demo01文件夹后,系统自动生成了许多文件,每一个文件都代表了cpu资源的一种参数。
比如,我这里实验会用到cfs_period_us 和 cfs_quota_us文件,这两个文件的组合含义是:

限制进程执行在长度为 cfs_period 的一段时间内,只能被分配到总量为 cfs_quota 的 CPU 时间,文件名结尾的us表示单位是微秒。

运行一个百分百占用CPU的死循环脚本进程

执行脚本(后台运行)

[root@localhost cpu]# while : ; do : ; done &
[1] 18323

执行下top命令看一下cpu状况,能看到cpu被bash进程打到90%以上了,能感觉到风扇开始狂飙的感觉,hiahia~~。

[root@localhost cpu]# top
top - 13:01:38 up 1 day, 22:02,  2 users,  load average: 0.69, 0.24, 0.15
Tasks: 379 total,   3 running, 376 sleeping,   0 stopped,   0 zombie
%Cpu(s):100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1001332 total,   125980 free,   212364 used,   662988 buff/cache
KiB Swap:  2097148 total,  2095500 free,     1648 used.   574592 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 18323 root      20   0  115444    604    152 R 92.4  0.1   1:09.50 bash

快点把cpu占用限制下来

看一下整个cpu时间周期默认设置了100ms。

[root@localhost demo01]# cat cpu.cfs_period_us
100000

那么把cpu占用设置到30%,就是cfs_quota_us设置成30ms,也就是30000us。

[root@localhost demo01]# echo 30000 > cpu.cfs_quota_us
[root@localhost demo01]# cat cpu.cfs_quota_us
30000

最后,把进程ID,18323写入tasks文件。

[root@localhost demo01]# echo 18323 > tasks
[root@localhost demo01]# cat tasks
18323

加上限制后的效果

执行top命令后,发现18323进程的cpu用量很快就限制到30%以内,风扇一下就消停了,nice。

[root@localhost demo01]# top
top - 13:18:52 up 1 day, 22:19,  2 users,  load average: 0.24, 0.82, 0.71
Tasks: 379 total,   3 running, 376 sleeping,   0 stopped,   0 zombie
%Cpu(s): 30.7 us,  0.3 sy,  0.0 ni, 68.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1001332 total,   124852 free,   212404 used,   664076 buff/cache
KiB Swap:  2097148 total,  2095500 free,     1648 used.   574392 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 18323 root      20   0  115444    604    152 R 29.9  0.1  15:43.76 bash

原来如此,有意思。

docker怎么用

很简单,docker run命令后面带上和上面相同的参数即可,就是上面的原理,有意思。

$ docker run -it --cpu-period=100000 --cpu-quota=30000 busybox /bin/bash

在kubernetes中部署metallb

简介

k8s中,把service的类型配成LoadBalancer的时候,需要依赖外部的云供应商提供的Load Balancer。
在私有化的研发/测试k8s环境中需要用到LoadBalancer时非常不方便,往往是采用NodePort方式部署Service,再前置Nginx来完成,还是比较麻烦。
开源的metallb是一个不错的选择,可以部署在k8s中为我们提供一个LoadBalancer,开箱即用。

官网参考

两种工作模式

Layer2模式

metallb-l2
研发/测试(不含性能测试)环境下面常用的模式。
M在这种模式下,会从k8s节点中选一个Leader节点,在这个节点上面响应LB地址段的ARP请求,从而使上层路由把发往LB的流量都发到Leader节点,不需要路由器支持BGP。
不适合生产环境,所有对LB的请求都会发往Leader节点。如果当前Service下面的Pod分布在不同节点,那么这个流量还会从Leader发往相应的节点。

BGP模式

metallb-bgp
需要路由器支持接收Metallb的BGP广播,从而把请求分布到正确的节点上。跟L2模式的区别就是能够通过BGP协议正确分布流量了,不再需要一个Leader节点。
需要注意的是需要上层路由器支持BGP。而且因为BGP单session的限制,如果Calico也是使用的BGP模式,就会有冲突从而导致metallb无法正常工作。

部署环境

  • 3个节点,每个节点都是虚拟机,每个节点4核10G,1个master,2个worker
  • 每个节点设置使用netplan设置静态地址
    • master01节点:172.16.30.151
    • worker01节点:172.16.30.152
    • worker02节点:172.16.30.153
  • k8s版本1.22.1
  • Ubuntu Server版本20.04.3

安装helm

未安装helm的先安装helm。

curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
sudo apt-get install apt-transport-https --yes
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

用helm安装metallb controller

为helm添加chart仓库。

helm repo add metallb https://metallb.github.io/metallb

创建命名空间。

kubectl create namespace metallb-system

安装metallb controller。

helm install metallb metallb/metallb --version 0.11.0 --set configInline.address-pools[0].addresses[0]=172.16.30.152-172.16.30.153,configInline.address-pools[0].protocol=layer2,configInline.address-pools[0].name=defaule,controller.image.repository=quay.mirrors.ustc.edu.cn/metallb/controller,speaker.image.repository=quay.mirrors.ustc.edu.cn/metallb/speaker --namespace metallb-system

版本信息,以及支持的的参数可以参考ArtifactHub

部署后显示。

W0109 15:10:40.902296 1202329 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
W0109 15:10:40.907657 1202329 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
W0109 15:10:40.980741 1202329 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
W0109 15:10:40.980924 1202329 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
NAME: metallb
LAST DEPLOYED: Sun Jan  9 15:10:40 2022
NAMESPACE: metallb-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
MetalLB is now running in the cluster.
LoadBalancer Services in your cluster are now available on the IPs you
defined in MetalLB's configuration:

config:
  address-pools:
  - addresses:
    - 172.16.30.152-172.16.30.153
    name: defaule
    protocol: layer2

To see IP assignments, try `kubectl get services`.

警告信息不影响使用。

在安装前如果想看一下安装的配置是不是符合要求,可以通过下面的命令来执行,执行的结果不会实际安装,会将安装时需要的所有yml文件都显示出来,方便查看,方便定制参数。

helm install metallb metallb/metallb --version 0.11.0 --set configInline.address-pools[0].addresses[0]=172.16.30.152-172.16.30.153,configInline.address-pools[0].protocol=layer2,configInline.address-pools[0].name=defaule,controller.image.repository=quay.mirrors.ustc.edu.cn/metallb/controller,speaker.image.repository=quay.mirrors.ustc.edu.cn/metallb/speaker --namespace metallb-system --dry-run

如果装错,可以删掉。

helm list --namespace metallb-system
helm uninstall metallb --namespace metallb-system

梁宁的《增长思维》学习笔记,权力设计

衡量权力大小的指标:

  • 信息流,从信息流可以看出权力,哪些信息流经过你,哪些不经过你,就可以看出你在组织中的位置。失去权力从失去信息开始
  • 利益链,苏特尔虽然拥有法律赋予的证明,但是没有利益链,没有人可以为他提供保护和壁垒,拥有黄金城却最后不属于他,因为没有形成利益链,就是没有保护他的城堡。
  • 调动资源的能力。

信息环境,强调

信息环境,因为可以塑造人的观念,左右人的决策,你最容易接触的几个人,就是你信息环境的基本构成。

双向信息流是会被扭曲和过滤的

一个企业,信息流从上往下,衰减的非常厉害。从下往上也是一样,此时的衰减是过滤和迎合的。最终会导致企业上层的信息环境会和社会脱离甚至扭曲。
做正确决定的前置条件是决策者的信息环境。
优秀得领导者应该要觉察自己的信息环境,以及优化它。

数据中台巩固了决策者的权利

麦当劳拥有BI数据中台,其中有一项特别的能力,是收集末梢店铺的数据透视到典型市场的相对表现,同一个典型市场的店,在同样营销投放的情况下,可以横向比较,呈现出的不同的经营结果,可以判断管理水平的差异。15分钟为单位,汇总到全球总部给到COO。一个典型市场的店铺,在同样营销情况下的经营数据,改善性数据能说明管理水平。

总之

信息流就是权利,再强调,正确决策的前置是决策者的信息环境。重要工具是数据中台。

使用sar监控主机网络性能

干什么

最近在做一件事儿的时候,需要分析服务器网卡的收发速率,收发帧率等网络性能数据,看看带宽占用多少,能不能上去,找了一些命令,发现sar比较实用,能看到各网卡上的收发数据。sar命令显示的性能不仅限于网络。

sar是sysstat工具包内的一种工具,
不是所有版本的系统都装了,我自己的centos 7.2最简版默认不带。
装一下:

yum install sysstat

使用

每隔3秒刷新显示网络性能:

sar -n DEV 3

-n: 表示显示网络性能,还支持io,磁盘,cpu,内存等等等。

DEV:表示看网卡上的网络性能,还支持IP,ICMP,UDP流性能等等等,可以把DEV换成EDEV就能显示网卡传送数据时错误的数据。

3: 表示每隔几秒刷新一次。

网络性能显示:

Average: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s
Average: lo 0.01 0.01 0.00 0.00 0.00 0.00 0.00
Average: ens33 4000.33 15023 1223.71 12900.1 0.00 0.00 0.00
Average: docker0 0.00 0.00 0.00 0.00 0.00 0.00 0.00

IFACE:网卡,当前我的主机上的网卡是ens33。

rxpck/s:每秒收到的数据包数量,也叫每秒接受帧率。

rxpck/s:每秒发送的数据包数量,也叫每秒发送帧率。

rxkB/s:每秒收到的数据量,也叫接收速率,注意这里是大B,算成小b,乘以8。

txkB/s: 每秒发送的数据量,也叫发送速率。

rxcmp/s:每秒钟接受的压缩数据包。

txcmp/s:每秒发送的压缩包。

rxmcst/s:每秒接收的多播数据包。

梁宁的《增长思维》学习笔记,作战地图

什么是作战地图

作战地图就是一张展现市场或行业中存在哪些级别企业的的地图,这些企业会作哪些工作,或者是要成为某个级别的企业,至少要明白其要作的工作方向。当一个团队冲进市场的时候,参考地图至少明白自身的定位,目标定位,相对应重要的事儿,市场中会出现哪些级别的竞争对手,看到不同级别的企业都有对应的资源配置和作战姿势。

四种级别的企业

  • 草莽企业
  • 腰部企业
  • 头部企业
  • 顶级企业

草莽企业

就是一个小团队,找到一个机会,冲进去,并活在当下的机会里。
草莽企业要成功必做两件事:

  • 第一勇敢走出去。
  • 第二是找到破局点坚决锁资源。

腰部企业

在生态系统中找到一个生态位,脱离草莽企业,长期的生存下来,成为腰部企业。此时,因为系统需要他,所以需要保护他。

从草莽到腰部的关键是要了解我们都是附着于一个生态系统中,要观察所处的生态系统是怎样的,站在系统的角度看问题,了解系统为什么存在,再找我们在生态系统中的位置,对于在一个位置是否能扎下根来的决定性依据。

养成习惯要从自我强调中跳出来观察所处的生态位给自己带来什么。

案例:

茶马古道,云海肴案例,都是云南菜,但是云海肴抓住了shopping mall这一全年流量入口的生态,找到了一个位置,成为餐饮业的一家腰部企业。

格瓦拉通过一家提供一个地区的线上订票接口的服务商(原先只能线下购票的天花板)提供了一个地区的线上订票,线下取票,完成了体验闭环,构建了自己的产业增值,构建了自己的生态位置,成为腰部企业。

头部企业

终结一场战役的就是头部企业,头部企业发起的终结战,头部企业就是占领制高点。

互联网基本就是集中市场,头部通吃。

案例:

还是格瓦拉,2014年全国电影院订票接口互通了,这一天花板被打破,问题是产业成熟的同时,头部企业入场,开始终结战役,BAT美团等巨头一起下场,直接有人掀起了桌子,该补贴补贴,该收购收购,格瓦拉无力对抗,卖给了别家,当家的撤退。

这个案例也表明,一个巨大的天花板的打开,头部企业会入场开始终结战役,身处该位置的时候一定要注意到头部企业的对手,或者调整战略成为头部企业。腰部企业在自己的生态位上的精耕细作式的努力可能毫无意义。

顶级企业

顶级企业,不仅面临行业内竞争问题,还有个大敌是周期,周期包括:技术,产业,用户生命,用户体验等等等周期。跨越周期才能成为顶级企业。
大部分战略部都是在研究机会。战略部应该是研究周期,而不是机会。
小成靠机会和技巧,大成靠趋势和周期。
跨越周期就是要连续做出正确的决定。

如何作决定取决于信息环境,决策模型:

  • 信息就是权力,获得信息优先,就是比别人知道的更早就能更早的做决定。是否优先取决于自身信息来源的宽度。
  • 什么叫决策,就是发散收敛决而能行。

案例:

马云95年在硅谷看到了互联网这一重要的信息。马云全世界飞,找政治家,经济学家,艺术家交流,就是能够更早的获取重要信息。

阿里的决策架构,马云负责信息广度的收集,曾鸣基于未来进行战略收敛,彭蕾基于组织的价值进行收敛,张勇基于业务财务做判断并且把决策落地。

自身水平的评估方式:
你的水平就是你最长接触五个人的平均值。

总之

活在历史的周期里,观察利用周期,做好跨越周期的准备。
一切为了长期价值而展开。

梁宁的《增长思维》学习笔记,组织能力就是把握机会的能力

规则,关系

组织能力不等于管理能力。管理讲的是规则。组织讲的还关系。

马云谈组织

  • 三流组织共同规则
  • 二流组织共同利益
  • 一流组织共同信仰
  • 顶级组织至情至性

规则怎么守

没有钱和刀怎么守得住规则~~。

组织是人构成

人是活物,每件事情都会默默的思考。
人不是机器,人有情感有恐惧,也有欲望,讲的佛性点,贪嗔痴疑慢。
强者需要通过组织寻求意志,弱者需要通过组织寻求安全。
人是社会动物,不是迫不得已,没有人会成为孤单个体。大家都希望成为组织一员,让自己的能量安全得以安放。
不同难度的任务会挑战组织的强度。

总之

现实是骨感的,先清楚不同的级别的组织和人的内在,抱着善念,积极的心态往前走。

演讲学习笔记,明确演讲的目标

用三个问题来明确目标

  1. Why?我为什么要做这个演讲?
  2. Who?听众是谁?听众关心什么?听众要什么?
  3. What?我希望通过这个演讲,能让听众做什么?

Why?我为什么要做这个演讲?

演讲的目的最常见的有六种:

  1. 告知
  2. 说服
  3. 激励
  4. 娱乐
  5. 传播
  6. 教育

达成它们的难度从低到高,告知是最容易的,教育是最难的。

案例:
我们总以为乔布斯的发布会的目的是告知,告知新品发布。但其实乔布斯自己,是把演讲目标设置成“教育”。他的演讲,在内容和环节上很多设计都是为了让这个发布会能改变听众的认知。
比如说,乔布斯在Macbook Air一代的发布会上,特意设计了一个画面:从牛皮纸信封里抽出了薄薄的Macbook Air,同时,在这演示中,他在开始和结尾两次重复了一句话:“世上最薄的笔记本”。他把“全球最薄”这个概念牢牢灌输进了听众脑海中,所有人看完发布会后都被这个概念植入了。

Who?听众是谁?听众关心什么?听众要什么?

听众是指一群人,识别并提取出听众人群最大公约数,做出人群画像。是为了在演讲中,目标听众熟悉的语言模式、词汇、事物,激发他们的共鸣。做出人群画像,要考虑的点:

  1. 年龄段
  2. 教育背景
  3. 生活习惯、兴趣爱好
  4. 价值观、信仰
  5. 关注什么,最近关注的热点
  6. 听众要什么

案例
国产电影《战狼2》为什么打破了国产票房的所有纪录?就是因为它深深地了解用户,知道它的观众,会被“强大的祖国、强大的**人”这样一个价值观给召唤起来。

What?我希望通过这个演讲,能让听众做什么?

除了告知以外的演讲,本质上是希望听众听完,能够按照演讲的意图进行行动。

案例
前美国总统特朗普的女儿——伊万卡·特朗普的精彩助选演讲。强烈建议听一听,这被称为“全美年度演讲好声音”。
伊万卡的演讲目标非常清晰,那就是助选。伊万卡希望用户听完演讲,行动起来去给她父亲投票,于是她在15分钟的演讲中,设计了60多次提及“父亲”和“特朗普”。而且不断画饼,明示和暗示父亲当选后将给美国公民提供怎样的福祉,来鼓动听众去为特朗普投票。最后看美国大选结果,伊万卡做到了。

安装k8s实践二(离线镜像安装、非root账号)

环境

  • 2个节点,每个节点都是虚拟机,每个节点2核2G
  • 每个节点设置使用netplan设置静态地址
    • master节点:192.168.113.131
    • worker01节点:192.168.113.132
  • k8s版本1.22.1
  • Ubuntu Server版本20.04.3
  • 使用k8sopt账户安装,权限和root一致

准备

静态IP修改

赋权和修改netplan配置文件。

sudo chmod o+w /etc/netplan/00-installer-config.yaml
vi /etc/netplan/00-installer-config.yaml

地址内容。

# This is the network config written by 'subiquity'
network:
  ethernets:
    ens33:
      dhcp4: no
      addresses: [192.168.113.132/24]
      gateway4: 192.168.113.2
      nameservers:
        addresses: [114.114.114.114,8.8.8.8]
  version: 2

使用netplan应用配置。

sudo netplan apply

创建k8sopt用户

使用root用户创建账号。

adduser k8sopt

创建用户后,执行下面的命令,在出现的编辑画面中添加一个k8sopt,和root使用同样的权限配置。

visudo

离线文件

  • k8s镜像文件
    • flannel-v0.15.0.tar
    • mirrored-flannelcni-flannel-cni-plugin-v1.2.tar
    • coredns-v1.8.4.tar
    • kube-controller-manager-v1.22.1.tar
    • kube-scheduler-v1.22.1.tar
    • pause-3.5.tar
    • etcd-3.5.0-0.tar
    • kube-apiserver-v1.22.1.tar
    • kube-proxy-v1.22.1.tar
  • flannel部署配置文件
    • kube-flannel.yml

1.安装docker

系统安装过程中不要选装docker,版本不一样,如果安装过docker,先卸载。
卸载docker。

sudo apt-get remove docker docker-engine docker.io containerd runc

安装docker。

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

2.安装kubeadm

sudo apt-get install -y apt-transport-https ca-certificates curl

Ubuntu系统中如果安装了ca-certificates、curl,会跳过已安装的组件。

curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
sudo tee /etc/apt/sources.list.d/kubernetes.list <<-'EOF'
deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install kubeadm

在上述安装 kubeadm 的过程中,kubeadm 和 kubelet、kubectl、kubernetes-cni 这几个二进制文件都会被自动安装好。

安装后执行显示版本信息命令,正常显示版本即为安装完成。

kubeadm version
kubectl version
kubelet --version

3.关闭swap

先为下面两个文件添加其他用户可写的权限

sudo chmod o+w /etc/sysctl.conf
sudo chmod o+w /ect/fstab

编辑/ect/fstab并注释关于swap的配置。

vi /ect/fstab

/etc/sysctl.conf中添加swap设置,然后重启。

echo vm.swappiness=0 >> /etc/sysctl.conf
sudo reboot

重启完成后,使用命令free -m,其中的swap均为0。

4.安装集群

4.1.准备k8s镜像

docker save保存出来的镜像放到/home/k8sopt/k8simages目录下,并赋予可读可写可执行权限。

sudo chmod -R +rwx k8simages

进入k8simages导入images,可以将下列命令写一个shell执行。

sudo docker load < flannel-v0.15.0.tar
sudo docker load < mirrored-flannelcni-flannel-cni-plugin-v1.2.tar
sudo docker load < coredns-v1.8.4.tar
sudo docker load < kube-controller-manager-v1.22.1.tar
sudo docker load < kube-scheduler-v1.22.1.tar
sudo docker load < pause-3.5.tar
sudo docker load < etcd-3.5.0-0.tar
sudo docker load < kube-apiserver-v1.22.1.tar
sudo docker load < kube-proxy-v1.22.1.tar

镜像都导入后,可查看一下所有的imags以及tag。

sudo docker images

4.2.修改docker的cgroup驱动为systemd

“cgroupfs”作为Docker cgroup驱动程序,k8s推荐的驱动程序是“systemd”。
手动改一下。

sudo vi /etc/docker/daemon.json

编辑内容内容:

{
 "exec-opts":["native.cgroupdriver=systemd"]
}

保存退出,重启docker。

sudo systemctl restart docker

执行命令查看docker的信息

sudo docker info

在显示的信息中找到Cgroup Driver,确认值是systemd

4.3.初始化k8s的control-plane

sudo kubeadm init --kubernetes-version=v1.22.1 --pod-network-cidr=10.0.2.0/24

如果这一步报错,纠正错误后,一定要执行kubeadm reset后再进行init。
正常初始化后,显示成功信息,注意信息中提示要配环境变量、配置pod网络、使用token加入节点:

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.113.131:6443 --token eb6vwx.ogq90ew0rjmaavxw \
        --discovery-token-ca-cert-hash sha256:357143b69f278c2dc07fef4d325087ff19a86d1b1553c01f829f4826fedd5cb8

4.4.配置环境变量

按照上面的显示内容配置环境变量。
非root用户配置环境变量。

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

配置完成后,看一下node有几个

kubectl get nodes

结果显示有一个node,状态是notready。
显示一下这个node的详细信息。

kubectl describe node kxh

在显示的一堆信息中conditions的表中最后有个Ready开头的行,在这一行的最右侧有Message显示NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized,意思就是要装网络插件。

4.5.安装网络插件

将提前准备好的flannel部署配置文件kube-flannel.yml复制到/home/k8sopt目录下,在该目录下应用该文件。

kubectl apply -f kube-flannel.yml

成功执行后,通过命令查看flannel容器的运行情况。

kubectl get pods -n kube-system

可以看到kube-flannel跑了一个是实例,处于running状态。
再看一下master节点的状态。

kubectl get nodes

能看到master节点已经是Ready的状态。

5.部署work节点

5.1.确认hostname

所有节点的hostname必须设置不同,worker节点的hostname设置成要求的worker节点的名字。

sudo o+w /etc/hostname
vi /etc/hostname

比如可以设置成k8s-worker01

修改hostname完成后重启。

5.2.确保docker的cgroup驱动为systemd。

如果不是systemd的,kubelet运行会失败。

5.3.准备必要的镜像

必须要在worker节点上提前准备好两个docker images。
参考部署master节点时导入镜像的方法,导入以下两个images。

sudo docker load < kube-proxy-v1.22.1.tar
sudo docker load < pause-3.5.tar

5.4.worker节点加入集群

执行kubeadm init完成时显示的join命令。
如果有显示证书等文件已经存在的,先执行kubeadm reset后,再执行kubeadm join

sudo kubeadm join 192.168.113.131:6443 --token eb6vwx.ogq90ew0rjmaavxw \
        --discovery-token-ca-cert-hash sha256:357143b69f278c2dc07fef4d325087ff19a86d1b1553c01f829f4826fedd5cb8

注意:如果执行失败的,在修复问题后执行sudo kubeadm reset重置后,再进行join。
执行成功后,显示已加入集群的信息。

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

稍过一会,在master查询所有节点信息,能看到worker节点已经是Ready了。

kubectl get nodes

也可以查看worker节点的详细启动信息,注意其中的conditions里面有没有未满足和失败的条件。

kubectl describe node k8s-worker01

如果第一次做的时候,没有在worker节点上准备好必要的镜像,那么join完的woker节点是notready的状态,这是后可以在master上驱逐以及删除woker节点,并进行修复后,再join。

kubectl drain k8s-worker01 --force
kubectl delete nodes k8s-worker01

5.5 可能出现的podCIDR问题

在节点看似都Ready后,要看一下master上pod的状态,会看到有一个运行在worker节点上的flannel处于crashBackOff状态。

kubectl get pods -n kube-system

通过查看出错flannel pod的日志,会发现日志中显示未设置podCIDR。

kubectl logs kube-flannel-ds-dsw2k -n kube-system

按照这篇帖子,对worker节点进行CDIR的设置,可以解决,原因参考这个链接。

按照解决方法,对worker节点打一个patch,打完以后,过一小会就能看到flannel pod都正常运行了。

kubectl patch node k8s-worker01 -p '{"spec":{"podCIDR":"10.0.2.0/24"}}'

6.测试

sudo docker pull nginx
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get pods,svc

可以看到nginx service对节点映射的端口号,我这里是32416,使用worker节点地址加上端口号在浏览器中访问可以看到nginx默认网页内容。

梁宁的《增长思维》学习笔记,设计模式

模式四周连接:

  1. 因果,因为所以,因为自律,少吃多运动,所以减肥。
  2. 增强回路,持续有外部正反馈,强化因果,costco的廉价促进了供应链能力,反过来进一步增强它的议价能力。
  3. 调节回路,拖后腿的负反馈,为什么九成的减肥会失败,就是负反馈。懒是生物本能,生物都会厌恶让自己高能耗的事情,高度自律是反生物性的,调节回路必然存在,需要不断的设定增强反馈。
  4. 滞后反馈,拖后腿,很努力的付出,但是短时间内看不到结果,就会有质疑,有犹豫。

一时的热情并不能保持长久,关键是模式

90%减肥会失败,60%的健身房亏损,只能拉新,客户不成功怎么让健身房成功。
新东方的特点,老师都是段子手,是给学生提供坚持下去的心里能量,这是要点不是花边,是一种带增强回路的模式。
所以,对人,要判断一个人哪一点的心理能量充足,就要设计针对性的增强回路。

总结

意志力是靠不住的,要靠模式,激活心理能量。

为什么是MQTT,梳理一下

物联网中,所谓的万物互联,都讲的是大而全的概念,互联最基本的是要让各种设备具备双向通信能力,这种通信能力具体一点是能主动汇报状态,中心端或者智能控制端才能根据状态进行处理,下达控制指令,这才是互联的基础。物联网中的设备非常宽泛,到处都能查的到,个人认为,传感设备,智能电器等这些设备跟具有意义些,先假定统称为智能硬件。

这里面有一个前提,MQTT只是当下最被认可的物联网通信协议,未来总是会被替代。智能硬件,有大有小,作为一个通用协议,是要考虑将大小硬件,以及他们的工作环境在内整个生态体系作为范围。从通常意义上来讲,物联网设备具有几个很重要的自身条件和环境特征:

  1. 计算能力、存储资源受到严重限制。
  2. 网络带宽及其有限。
  3. 高延迟的网络传输是家常便饭。

为什么一定是MQTT而不是HTTP,WebSocket,AMPQ这类协议。

MQTT特性

为适应物联网设备的自身的和环境的特征,个人角度最重要的几个适应特征的优势:

  • 轻量级协议
  • 协议天生就是订阅、发布模式。
  • 交互协议精简,没有多余。

轻量级协议

作为协议的控制部分-报头,非常精简,协议只有1个字节的固定报头,2~4剩余字节长度,最小的心跳报文一共才只有2个字节。
相比较而言HTTP的首行、头部加起来再经过编码后就太多的多了。

订阅/发布模式

MQTT的一大特点就是典型的订阅/发布系统,系统定义了当所有设备都可以与同一通道对话时,不同设备如何相互对话。发布者设备可以在频道上发布具有定义主题的任何消息,订阅这些主题的设备可以接收那些消息。这因为这个特性,让各设备相互之间具备了直接汇报和控制的能力。
相对HTTP而言,WebSocket协议也是比较轻量,协议中的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message),一帧最小的长度也是足够精简,大小在2~10个字节。但WebSocket协议本身是一个点对点的双向通信的协议,解决了在HTTP协议工作下的客户端/Web前端只能轮询后台的局面,只要客户端/前端连接到服务端,后台就可能主动发命令给到客户端/前端,但要让各设备之间针对某一特定的主题进行相互通知的应用场景,WebSocket协议本身并不具备。

交互协议精简,没有多余

AMQP协议也是基于订阅/发布模式的协议,有54个指令,MQTT只有14个指令,没有多余。
相比较而言,AMQP的指令交互流程要比MQTT要复杂的多,可以看一下他们各自的连接过程。

MQTT:

  1. TCP连接。
  2. Client发送CONNECT报文给Broker,包含设备,用户名、密码信息。
  3. Broker回复CONNACK报文,连接完成。

AMQP:

  1. TCP连接。
  2. Client发送协议版本报文给到Server,比如Protocol Header 0-9-1的报文头。
  3. Server回复Connectionion.Start开始建立连接。
  4. Client回复Connection.Start-Ok报文,包含用户名、密码信息,连接建立。
  5. Server回复Connection.Tune报文。
  6. Client回复Connection.Tune-Ok报文。
  7. Client继续发送Connection.Open报文。
  8. Server回复Connection.Open-Ok报文,连接完成。

从体量上来看,AMQP协议的报文长度也是远大于MQTT,比如Connectionion.Start整个报文长度都快到500个字节了,而MQTT的CONNECT不考虑用户名密码长度的情况下,看了下也就10来个字节。
相比较而言,MQTT体量更轻,协议更精简,更适合物联网设备自身条件和所处的网络环境。

只是适合,并不万能

MQTT协议体量轻,协议简单这两个特点成为目前适合物联网设备互联的主流协议,但也正因为这种简单,并不适合数据交互量大的点对点通信中,灵活的多路由消息队列方式,千万不能滥用,这些场景还是要多借助于WebSocket,AMQP协议完成相关功能。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.