docker run 的时候如果出现类似:

1
/usr/bin/docker: Error response from daemon: linux mounts: path /tmp is mounted on / but it is not a shared mount.

之类的错误, 可以通过修改Docker启动参数解决, 注释掉mountFlags或者改为shared:

1
2
$vi /usr/lib/systemd/system/docker.service
MountFlags=slave

修改后发现新的错误:

1
2
/usr/bin/docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:402: container init caused \"open /dev/console: input/output error\"": unknown.
FATA[0007] exit status 125

网上查有如下回复:

1
I met this problem while I suspend my computer, then I restart my computer, this error was solved. I guess it was because the docker daemon missed driver library path.

所以重启大法好…

但是真正原因没搞清楚, 待更新.

Labels and Selectors

  1. label
    label是k8s中所有资源都有的一个域, 他是一个=对, 表示这个资源具有某种特定属性, key必须不能重复. 这样user和其他资源可以通过label来选择具有这种属性的资源集合.

    1
    2
    3
    4
    5
    6
    "metadata": {
    "labels": {
    "key1" : "value1",
    "key2" : "value2"
    }
    }

    label的key有两个段,分别是前缀和名称, 通过’/‘分开. 名称字段必须少于63个字符, 必须以’[a-z0-9A-Z]’, ‘-‘, ‘.’, ‘_’和数字 开头和结尾.前缀是可选的, 一旦指定, 不能超过253个字符. label的value指和key中的名称要求一样.

  2. Selectors的使用
    通过api中使用 -l 来选择特定集合的objects: $ kubectl get pods -l 'environment in (production, qa)'

一些k8s资源, 比如Service and ReplicationController 会有selector字段, 用来选择pod.

1
2
selector:
component: redis

基于相等的匹配和基于集合的匹配:

1
2
3
4
5
6
selector:
matchLabels:
component: redis
matchExpressions:
- {key: tier, operator: In, values: [cache]}
- {key: environment, operator: NotIn, values: [dev]}

注意区分概念, label永远都是key=value, 多个label在一起能组成一个集合, 类似map. 只有selector有集合的操作, label本身就是一个<k,v> 对, 没有这样的label.
集合方式有四种operator: In, NotIn, Exist, DoesNotExist. {key: tier, operator: In, values: [cache]}这条表达式等价于matchLabels中的: tier: cache.
必须满足所有的selector表达式, 才算匹配.
Job, Deployment, Replica Set, and Daemon Set都是支持基于集合的匹配.

  1. nodeSelector
    nodeSelector用于选择某个node, 再次之前, 需要在node上添加label: kubectl label nodes <node-name> <label-key>=<label-value>. 然后在pod中配置相应的nodeSelector, 就能保证pod调度到符合语义的node:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: v1
    kind: Pod
    metadata:
    name: nginx
    labels:
    env: test
    spec:
    containers:
    - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
    nodeSelector:
    disktype: ssd
    k8s node不同版本会有一些内置labels:
    1
    2
    3
    4
    5
    6
    kubernetes.io/hostname
    failure-domain.beta.kubernetes.io/zone
    failure-domain.beta.kubernetes.io/region
    beta.kubernetes.io/instance-type
    beta.kubernetes.io/os
    beta.kubernetes.io/arch

Affinity

nodeSelector是比较简单的pod选择节点的方式, k8s提供了affinity/anti-affinity用来更复杂的提供节点选择方案. 他有两种: node affinity 和 inter-pod affinity/anti-affinity:

  • node affinity
    nodeAffinity与nodeSelector相似, 也是基于label, 包括两种类型, 可以分别理解为’hard’和’soft’, 一个强制要求, 一个尽可能要求.

    • requiredDuringSchedulingIgnoredDuringExecution: hard类型
    • preferredDuringSchedulingIgnoredDuringExecution: soft类型
      内容和label selector基本一致:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      apiVersion: v1
      kind: Pod
      metadata:
      name: with-node-affinity
      spec:
      affinity:
      nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
      - key: kubernetes.io/e2e-az-name
      operator: In
      values:
      - e2e-az1
      - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
      preference:
      matchExpressions:
      - key: another-node-label-key
      operator: In
      values:
      - another-node-label-value
      containers:
      - name: with-node-affinity
      image: k8s.gcr.io/pause:2.0
      weight 是1-100.
  • pod affinity
    podAffinity/podAnti-Affinity是比较已经调度到节点上的pod而不是看node本身. 他也有两种类型hard和soft:

    • requiredDuringSchedulingIgnoredDuringExecution: hard
    • preferredDuringSchedulingIgnoredDuringExecution: soft
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0

Taints and Tolerations

affinity是站在pod的角度, 而taints是站在node的角度,

1
kubectl taint nodes node1 key=value:NoSchedule

这个命令将node1加了一个taint, 表示无法调度, 除非你有相应的toleration:

1
2
3
4
5
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
1
2
3
4
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"

这两个tolerations都能匹配那个taint. operator默认是Equal
一个空的tolerations匹配所有的taint:

1
2
tolerations:
- operator: "Exists"

以下这个tolerations匹配所有key为key的taints:

1
2
3
tolerations:
- key: "key"
operator: "Exists"

effect有两种: NoExecute和NoSchedule, 如果加了NoExecute, 那么所有不匹配的pod将会立即被evicted掉, 如果加了Noschedule, 那么只是Pod无法被调度, 已经调度的Pod不受影响。NoExecute还可以指定一个可选的域tolerationSeconds, 表示尽管匹配了taint可以不被立即evicted掉, 但在一定时间之后就会被evicted掉。

通过kubectl taint nodes node1 key:NoSchedule- 取消taint

更有意思的是多个taint和多个tolerations的情况.

例如创建三个taint:

1
2
3
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

一个pod拥有两个tolerations:

1
2
3
4
5
6
7
8
9
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"

该pod没有匹配所有的taint, 因此他不能被调度到这个节点, 但如果调度了,他不会被移除, 因为他只有第三个NoSchedule的taint不匹配。

注意

在1.6之后, 官方加了一些内置的taint, 他会将pod evctied掉:

1
2
3
4
5
6
7
node.kubernetes.io/not-ready: notready.
node.alpha.kubernetes.io/unreachable: Unknown
node.kubernetes.io/out-of-disk: Node becomes out of disk.
node.kubernetes.io/memory-pressure: Node has memory pressure.
node.kubernetes.io/disk-pressure: Node has disk pressure.
node.kubernetes.io/network-unavailable: Node’s network is unavailable.
node.cloudprovider.kubernetes.io/uninitialized: When kubelet is started with “external” cloud provider, it sets this taint on a node to mark it as unusable. When a controller from the cloud-controller-manager initializes this node, kubelet removes this taint.

​ 一直感觉对kubernetes中的qos是一个盲点, 借着复习下scheduler的一些资源调度策略来学习下k8s中的qos策略, 然后再辐射到一般性的qos策略.

动机:

k8s 用非常简单的方式分配资源. 用户能够指定容器的资源限制. 比如一个用户能够指定某个container 只能使用1GB的内存. scheduler通过资源限制去调度容器. 如果某个节点的内存只有4GB, 那个一个有5Gb请求的容器就将不能调度到这个节点. 目前, k8s不能保证容器在一个超卖的系统中运行稳定.

目前的实现中, 如果用户指定limts给所有的container, 那么集群资源的利用率将会非常低下. 因为容器往往无法充分使用用户指定的那些资源. 一个可能的方式是不指定limits, 这样container就可以无限制的使用, 但是如果这些container无限制的使用资源, 就可能使得指定了limits的容器由于机器资源不够而被杀掉. 这是用户不愿意看到的, 他们希望自己指定了某个大小, 那么启动之后系统就应该保证这个容器的顺利运行.

qos机制就是在节点资源超卖的环境下, 通过提供不同级别的保证来满足资源的需求. 容器可以用request请求一个最小资源, request与limit不同, container可以使用超过request的值. best-effort级别的container相当于request为0的container. Best-effort container只使用那些其他container没有使用的资源, 可以用于资源清理(这个没看懂).

Request and limits

对于每种资源, container可以指定request和limits, 0 <= request <= limit <= infinity. 如果container成功调度, 意味着container能够保证至少有request的值的资源. container不能超过limit的值. 而request 和 limit 如何执行要看资源是compressible还是incompressible.

Compressible Resource Guarantees

  • 目前只有cpu
  • container至少得到request的cpu请求
  • 剩余的cpu会按照cpu request比例分配给container. 比如container A request 60%, container B request 30%. 假设两个container都尝试拿到更多的cpu, 那么剩余的10%将按照2:1的比例分配.
  • 如果超过limits, Containers 会被节流(不是被杀死).

Incompressible Resouce Guarantees

  • 目前只有内存. (我认为本地存储也在这一类)
  • Containers能够得到request的大小, 如果超过这个大小, 它们可能会被杀掉(如果其他container需要内存), 但是如果containers消耗的少于request值, 他们不会被删除(除非系统任务或者daemonset需要更多的内存)
  • Containers会被杀掉如果他们使用了超过limit的内存.

Kubelet admission 策略

kubelet通过统计containers的request来确保系统资源不会被超载.

QoS 分级

概述

k8s Qos 分成三个等级:

  • Guaranteed
  • Burstable
  • BestEffort

理论上QoS与limit, request应该是互不干扰的, 但实际上他们的联系非常紧密. QoS class不是自己设置的, 而是在创建pod的时候, 根据limit和request系统自动确认的.

Guaranteed

这是级别最高的, 他的触发条件是:

  • 每个pod中container必须有内存的limit和request, 而且必须相同
  • 每个pod中cpu必须有内存的limit和request, 而且必须相同

效果: 这些containers是最高优先级

注意: 如果设置了limit没有设置request, 系统将会自动填充request跟limit相同.

####Burstable

这个的触发条件是:

  • pod不满足Guaranteed
  • pod中至少一个container有内存或者cpu的请求.

效果: 能够保证request的请求, 但是不保证limit, 如果超出request请求的内存大小, 发生oom时可能会被杀死.

BestEffort

触发条件: 很明显, 没有指定任何request和limit或者值都是0就是BestEffort级别.

效果: 这些containers没有请求资源保障, 会被认为是最低优先级的, 如果系统发生oom, 他们会被首先杀死. 他们只会使用集群中没有被使用的那部分资源.

总结

所以k8s的qos主要是通过两方面: request,limit 和 qos class来实现的. 这两方面又有很多交叉的地方. 而且在面对不同的资源的时候, 他们的策略是不一样的.

Storage QoS

由于k8s中没有关于存储的qos机制, 于是关于存储的qos, 查看了一些文档.

最近在调查一个kubernetes中发现Kubelet的pods目录:

1
/var/lib/kubelet/pods/xxx/volumes/

下出现了大量的包含”deleting~” 的目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~859156558
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~912994645
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~096627888
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~361944655
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~827756898
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~850958169
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~435144420
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~573873907
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~817019830
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~300298653
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~414447192
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~453118423
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~634999626
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~329196065
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~705907980
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~060876539
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~371568670
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~473777381
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~852926720
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~911951455
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~221614642
/var/lib/kubelet/pods/612f2e76-3ce1-11e8-b2c9-0cc47ae2b22c/volumes/transwarp.io~tosdisk/pvc-f39e86b9-019b-11e8-b2c9-0cc47ae2b22c.deleting~643761641

导致每次reconciler将这些多余的”deleting~”加入到”ActualOfWorld”中, 然后触发大量的Umount操作, 使得reconciler很久才Loop一次, 现象就是pod create和delete都变得非常得慢.
一. 一开始, 我发现自己写的plugin中使用了pkg/volume/volume.go中的RenameDirectory函数, 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func RenameDirectory(oldPath, newName string) (string, error) {
newPath, err := ioutil.TempDir(filepath.Dir(oldPath), newName)
if err != nil {
return "", err
}

// os.Rename call fails on windows (https://github.com/golang/go/issues/14527)
// Replacing with copyFolder to the newPath and deleting the oldPath directory
if runtime.GOOS == "windows" {
err = copyFolder(oldPath, newPath)
if err != nil {
glog.Errorf("Error copying folder from: %s to: %s with error: %v", oldPath, newPath, err)
return "", err
}
os.RemoveAll(oldPath)
return newPath, nil
}

err = os.Rename(oldPath, newPath)
if err != nil {
return "", err
}
return newPath, nil
}

在每次删除目录时, 并不是直接删除, 而是先创建一个随机的空目录, 然后将原目录rename到随机目录, 最后再将这个随机目录删除掉.
看似没有什么问题, 但不巧的是, 在golang1.8之后, os.Rename的实现发生了变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func rename(oldname, newname string) error {
fi, err := Lstat(newname)
if err == nil && fi.IsDir() {
// There are two independent errors this function can return:
// one for a bad oldname, and one for a bad newname.
// At this point we've determined the newname is bad.
// But just in case oldname is also bad, prioritize returning
// the oldname error because that's what we did historically.
if _, err := Lstat(oldname); err != nil {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
return &LinkError{"rename", oldname, newname, err}
}
return &LinkError{"rename", oldname, newname, syscall.EEXIST}
}
//------------------------版本分割线1.8
err = syscall.Rename(oldname, newname)
if err != nil {
return &LinkError{"rename", oldname, newname, err}
}
return nil
}

在虚线以上是go1.8之后新加的内容, 如果rename之后的目录存在, 就会打印”File Exits”错误, 这样就会创建大量的”deleting~”目录. 相关修改和讨论在Bugs in EmptyDir Teardown path.

以为问题就这样解决了, 然而不是, 我检查了版本, 使用的是1.7.4, 同时查看了日志, 并没有打印”FILE Exist”的log, 而是打印了”device or resource busy”. 通过多种测试, 发现rename操作无论是进程占用还是进程对文件的读写, 都不会导致device busy问题. 这说明我找到了出问题的地方, 却没找到背后的原因.

二. 如果rename没有返回错误, 那么只能是之后的remove操作返回的错误. 然而却有几个疑点:

  1. 查看rename出来的deleting目录中都是空目录, 里面并没有数据
  2. 手动可以remove掉这些目录
  3. 原目录并没有消失, 而是存在且有大量的正在更新的数据.

​ 显然问题比我想象的更加复杂. 有一个可能的解释是在我操作delete之后由于太长时间没有删掉导致Kubelet直接暴力删除了这个pod, statefulset又重启了新的pod, 原来的container虽然删掉但相应的namespaces中依然有应用程序在读写, 这种情况导致remove busy, 而且数据在更新.

这些问题要调查起来都很费力. 显然直接删掉rename逻辑可以解决这个问题, 于是不再追究.

在linux的权限中, 发现不仅仅有rwx, 还有一个t, 如下所示:

1
drwxrwxr-t   2 shentao shentao       4096  8月 24  2016 test/

t只能加在最后一个组, 也就是其他组的权限中, 他的意思是sticky bit, 表示其他组成员不能对该文件进行删除和重命名操作, 这是为了防止某个文件无意被其他组的成员删除和重命名后导致owner找不到文件.

添加的方式是:

1
chmod o+t test

或者

1
chmod +t test

或者使用数字的方式, 在首位设置1

1
chmod 1775 test

结果如下:

1
2
ll test
drwxrwxr-t 2 shentao shentao 4096 8月 24 2016 test/

移除方式如下

1
chmod o-t test

查看结果

1
2
3
ll test

drwxrwxr-x 2 shentao shentao 4096 8月 24 2016 test/

从这里也能看出, t是比x更高一个级别的权限, 即t包含了可执行权限, 只是不能delete和rename. 其次它与rw也不冲突.

星环讲座: 初识微服务

主讲人: 吴伟

三本书:

  • Building Microservices
  • 微服务: 从设计到部署
  • 微服务设计

大纲:

  1. 什么是微服务
  2. 核心概念
  3. 开源微服务框架
  4. 国内微服务产品

内容:

  1. 什么是微服务: 相互独立微小的组建, 能够相互合作: 在一组小的服务, 独立的进程, 通信机制, 基于业务, 独立部署, 没有集中式管理.
  2. 为什么要做微服务: 单体服务膨胀太大, 代码太多, 开发太慢, 发布速度太慢, 新人学习成本太高.
  3. 微服务目标: 敏捷迭代, 灵活扩展, 服务服用.
  4. 微服务核心概念: api网关, 服务发现, 熔断, 限流, 降级, 配置中心, 自动化部署和测试, 日志监控和分部署追踪, 安全, 服务拆分, 服务接口定义, 有状态服务集群, 无状态服务
    • 服务发现: 怎么找到另一个服务的地址. 通常使用服务中心或者服务代理.
    • api网关: 对外统一接入访问; 内外协议转换(http -> gRPC), 统一认证,监控,负载均衡,缓存; 智能路由: Kong, Nginx Plus, Traefik
    • 服务容错(熔断): 假设错误一定会发生, 想办法把损失降到最小: custom fallback, fail silent, fail fast. 超时与重试, 降级熔断, 连接隔离
    • 配置中心: 程序运行时动态调整行为的能力.
    • 自动化测试部署: CI/CD 改动一行代码能够多久部署成功
    • 服务监控: 系统在做什么, 哪些组建流量比较大, 这个请求在哪个地方失败了, 哪个调用比较慢.
    • 安全:
    • 分布式事务: 每个服务解决一个问题, 要解决一个逻辑上的一致性.

cloud native = container + CICD/devops + microservice

问题描述

今天在执行一个命令是遇到一个which路径与实际执行不同步的问题, 如下:

1
2
3
4
5
6
7
8
9
shentao@shentao-ThinkPad-T450:~/blog$ which ss-local
/usr/local/bin/ss-local
shentao@shentao-ThinkPad-T450:~/blog$ /usr/local/bin/ss-local -v | grep shadowsocks-libev
shadowsocks-libev 2.5.6 with OpenSSL 1.0.1f 6 Jan 2014
shentao@shentao-ThinkPad-T450:~/blog$ ss-local -v | grep shadowsocks-libev
shadowsocks-libev 3.1.3
shentao@shentao-ThinkPad-T450:~/blog$ /usr/bin/ss-local -v | grep shadowsocks-libev
shadowsocks-libev 3.1.3

which 现实路径是/usr/local/bin/ss-local, 但实际执行的路径却是/usr/bin/ss-local. 查看which的文档, 发现which是按顺序搜索PATH环境变量来查找的:

1
2
shentao@shentao-ThinkPad-T450:~/blog$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/go/bin:/usr/local/go/bin:/home/shentao/gopath//bin:/opt/idea-IC-145.1617.8/bin:/usr/local/go/bin:/home/shentao/gopath//bin:/opt/idea-IC-145.1617.8/bin:/usr/local/go/bin

所以最先在/usr/local/bin中找到.

问题解决

于是去调查了下bash的执行过程和顺序. 得到了答案. https://crashingdaily.wordpress.com/2008/04/21/hashing-the-executables-a-look-at-hash-and-type/

原来在每一次执行过程中shell都会有一个hash table作为cache将调用过的可执行命令存入hash table, 这样再下次调用的时候就可以直接从缓存中读取, 而不用每次去搜索PATH. 我们通过type命令可以查看:

1
2
shentao@shentao-ThinkPad-T450:~/blog$ type ss-local
ss-local is hashed (/usr/bin/ss-local)

可以通过hash命令删除:

1
2
3
shentao@shentao-ThinkPad-T450:~/blog$ hash -d ss-local
shentao@shentao-ThinkPad-T450:~/blog$ ss-local -v | grep shadowsocks-libev
shadowsocks-libev 2.5.6 with OpenSSL 1.0.1f 6 Jan 2014

知识回顾

我们再来回顾下关于bash 的一些知识:

关于bash可以执行的类型

  • Aliases: An alias is a word that is mapped to a certain string, 命令的别名.

    1
    2
    $ alias nmapp="nmap -Pn -A --osscan-limit"
    $ nmapp 192.168.0.1
  • Functions: A function contains shell commands, and acts very much like a small script. 用function定义一个函数:

    1
    function gcc { echo “just a test for gcc”; }
  • Builtins: 内置函数, cd之类的

  • Keywords: Keywords are like builtins, with the main difference being that special parsing rules apply to them. For example, [ is a Bash builtin, while [[ is a Bash keyword. They are both used for testing stuff. [[ is a keyword rather than a builtin and is therefore able to offer an extended test:

    1
    2
    3
    $ [ a < b ]
    -bash: b: No such file or directory
    $ [[ a < b ]]

    第一个< 是重定向, 第二个加了[[的关键词之后 < 成了小于号.

  • Executables: (Executables may also be called external commands or applications.) Executables are commonly invoked by typing only their name. This can be done because a pre-defined variable makes known to Bash a list of common, executable, file paths. This variable is called PATH. 外部命令, 也就是PATH中找到的那些命令

  • Script: 脚本. 比如test.sh

关于bash的搜索顺序总结

bash搜索的顺序是: 当前路径和绝对路径的目录->alias->keyword->function->built-in->Executables, Script(hash)->Executables, Script($PATH)

相关命令

bash, hash, type, which, alias, function

Istio: 一个开源平台去链接, 管理和保障微服务: 分布式微服务代理。

传统代理: 类似于一个线, 任何服务都经过这个代理。

分布式代理: 代理运行在每个应用上,接管应用进出的流量并和统一的控制中心通信获取动态配置。

微服务代理: service mesh: 互连的分布式代理。

Istio:

  1. Pilot: 基于http做流量的;

  2. Mixer: 做数据统计, 授权认证

  3. CA-Auth: 做安全认证

Envoy: 微服务代理。

苗艳强,闫学安

ZTE 工程师,主要从事容器相关开源工作,Kubernetes 社区 member, containerd 社区 member

课程详情:

演讲主题:

目前跟 Kubernetes 对接的默认容器运行时是 docker,然而 docker 从 1.12 版本开始加入了 swarm 功能,随着版本的演进,功能越来越庞大,已经成为一个跟 Kubernetes 同级甚至高一级的编排工具,且前不久又宣布在编排测要无缝对接 Kubernetes,因此,docker 作为 Kubernetes 默认容器运行时的位子必将被其他工具代替。本次分享首先简单介绍 Kubernetes 的运行时接口规范,然后着重为大家分享一款新的容器运行时工具 containerd,包括项目介绍,功能组成等,以及使其原生对接 Kubernetes 的插件 cri-containerd。

纲要/提纲:

  1. Kubernetes 容器容器运行时接口(CRI)

  2. containerd 项目介绍

  3. containerd 的 CRI 实现


Containerd的CRI实现
kubelet -> cri-containerd -> containerd

在hexo中, 在themes目录下git clone了一个子目录themes/next. git push 到远程仓库后没有看到themes/next中的内容, 也无法git add

1
2
3
4
5
6
shentao@shentao-ThinkPad-T450:~/blog$ git commit -m "fix" themes/next/
On branch master
Changes not staged for commit:
modified: themes/next (modified content)
shentao@shentao-ThinkPad-T450:~/blog$ git add themes/next/*
fatal: Pathspec 'themes/next/bower.json' is in submodule 'themes/next'

当创建一个子git项目是, 项目就叫做git submodule结构, 外部无法对子模块进行控制, 有两种方式(我知道的):
1.

1
2
3
cd themes/next
git add .
git commit

再到外部提交

1
2
3
git add themes/next
git commit
git push
  1. 1
    2
    git rm --cached themes/next  
    git add themes/next
0%