分类 Linux 下的文章

如果有一天,你按需求配置完了环境,检查完交付别组使用,过几天那组的家伙找过来说他们折腾了一通之后现在ssh加了密钥登陆还是得输密码,那么,你需要按照以下步骤解决问题:

  1. 检查sshd端口和服务
  2. 检查密钥文件以及目录,以及他们所在的位置
  3. 检查.ssh及.ssh/*的权限是否为700,600
  4. 检查.ssh及.ssh/*的属主是否是对应的用户
  5. 告诉他们这是他们自己折腾出来的问题,不在你的负责范围内

image.png

kube-dns

主要参考:https://zhuanlan.zhihu.com/p/80141656
上面这篇可比官方那个不讲人话的文档要讲的清楚多了
https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/dns-debugging-resolution/
aliyunhttps://help.aliyun.com/document_detail/188179.html?utm_content=g_1000230851&spm=5176.20966629.toubu.3.f2991ddcpxxvD1#title-b7y-d6a-bcy
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/?spm=a2c4g.11186623.0.0.23c66b886YeKE2
集群默认使用的是kube-dns

[root@master ~]# kubectl get pods -n kube-system
NAME                             READY   STATUS    RESTARTS        AGE
coredns-7f6cbbb7b8-hvpcg         1/1     Running   0               3d17h
coredns-7f6cbbb7b8-pphjt         1/1     Running   0               3d17h
etcd-master                      1/1     Running   0               3d17h
kube-apiserver-master            1/1     Running   0               3d17h
kube-controller-manager-master   1/1     Running   1 (3d17h ago)   3d17h
kube-proxy-fjmj9                 1/1     Running   0               3d17h
kube-proxy-ht5xt                 1/1     Running   0               3d17h
kube-scheduler-master            1/1     Running   0               3d17h
[root@master ~]# kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   3d17h

我们可以看到的dns的地址,现在让我们随便进一个pod里面来查看一下

[root@master ~]# kubectl exec -it -n devops-tools jenkins-559d8cd85c-qg9zx  -c jenkins -- bash
jenkins@jenkins-559d8cd85c-qg9zx:/$ cat /etc/resolv.conf 
nameserver 10.96.0.10
search devops-tools.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

可以看到这个容器里面的域名解析地址指向了上面的kube-dns

上面的例子是dns策略为ClusterFirst时kube-dns会自动为pod生成的,当域名里面的点不超过options里面时,k8s会去在search里面一个个匹配
比如我找个a
那么他就会从a.devops-tools.svc.cluster.local一直找到a.cluster.local
让我们来康康官方是怎么讲的

"ClusterFirst": 与配置的集群域后缀不匹配的任何 DNS 查询(例如 "www.kubernetes.io") 都会由 DNS 服务器转发到上游名称服务器。集群管理员可能配置了额外的存根域和上游 DNS 服务器

嗯,确实是很有官方风格的叙(不)事(讲)方(人)式(话)

上面的例子是访问内部域名的,如果你想要访问一个外部域名,但是依旧还要经过几轮的search轮询的话,那未免有点浪费,避免这种情况的方式有两种,一种是直接访问绝对域名,如原本xxx.com 选择写成xxx.com.
另外一种是在dnsConfig里面修改option,大于等于一个点就不走search

几种dns模式的差别

ClusterFirstWithHostNet
当一个 Pod 以 HOST 模式(和宿主机共享网络,hostNetwork: true)启动时,这个 POD 中的所有容器都会使用宿主机的 /etc/resolv.conf 配置进行 DNS 查询,但是如果在 Pod 中仍然还想继续使用 k8s 集群 的 DNS 服务时,就需要将 dnsPolicy 设置为 ClusterFirstWithHostNet。
ClusterFirst
使用这种方式表示 Pod 内的 DNS 优先会使用 k8s 集群内的 DNS 服务,也就是会使用 kubedns 或者 coredns 进行域名解析。如果解析不成功,才会使用宿主机的 DNS 配置进行解析。
Default
这种方式,会让 kubelet 来决定 Pod 内的 DNS 使用哪种 DNS 策略。kubelet 的默认方式,其实就是使用宿主机的 /etc/resolv.conf 来进行解析。你可以通过设置 kubelet 的启动参数, --resolv-conf=/etc/resolv.conf 来决定该 DNS 服务使用的解析文件的地址
当我们部署集群 DNS 服务的时候,一般就需要将 dnsPolicy 设置成 Default,而并非使用默认值 ClusterFirst,否则该 DNS 服务的上游解析地址会变成它自身的 Service 的 ClusterIP(我解析我自己),导致域名无法解析

None
这种方式顾名思义,不会使用集群和宿主机的 DNS 策略。而是和 dnsConfig 配合一起使用,来自定义 DNS 配置,否则在提交修改时报错。

kube-dns的组成

  1. kubedns: 依赖 client-go 中的 informer 机制监视 k8s 中的 Service 和 Endpoint 的变化,并将它们转换为 skydns 能够理解的格式,以目录树的形式存在内存中,来服务内部 DNS 解析请求。skydns 是以 etcd 的标准作为后端存储的,所以为了兼容 etcd ,kubedns 在某些错误信息方面也都以 etcd 的格式进行定义的。因此 kubedns 的作用其实可以理解为为 skydns 提供存储。
  2. dnsmasq: 区分 Domain 是集群内部还是外部,给外部域名提供上游解析,内部域名发往 10053 端口,并将解析结果缓存,提高解析效率。
  3. sidecar: 对 kubedns 和 dnsmasq 进行健康检查和收集监控指标。

配置你的dns

自设dns

参考https://jimmysong.io/blog/configuring-kubernetes-kube-dns/
当你运行一个pod时,kubelet会将预先设定好的dns给你的pod布上,如果你想给他换个dns,那就需要另外的设置,使用 --resolv-conf来指定一个合法的文件,这个pod将以这个文件为标准来部署你的dns,而非使用默认的
/etc/resolv.conf来作为继承对象
这种方式只对clusterfirst有效,对default和none无效

通过为kube-system:kube-dns添加一个configmap来为pod添加存根域和上游域名解析(外部)

apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-dns
  namespace: kube-system
data:
  stubDomains: |
        {“acme.local”: [“1.2.3.4”]}
  upstreamNameservers: |
        [“8.8.8.8”, “8.8.4.4”]

none

apiVersion: v1
kind: Pod
metadata:
  name: alpine
  namespace: default
spec:
  containers:
  - image: alpine
    command:
      - sleep
      - "10000"
    imagePullPolicy: Always
    name: alpine
  dnsPolicy: None
  dnsConfig:
    nameservers: ["169.254.xx.xx"]
    searches:
    - default.svc.cluster.local
    - svc.cluster.local
    - cluster.local
    options:
    - name: ndots
      value: "2"
参数描述
nameservers将用作Pod的DNS服务器的IP地址列表。最多可以指定3个IP地址。当Pod的dnsPolicy设置为None时,列表必须至少包含一个IP地址,否则此属性是可选的。列出的DNS的IP列表将合并到基于dnsPolicy生成的域名解析文件的nameserver字段中,并删除重复的地址。
searchesPod中主机名查找的DNS搜索域列表。此属性是可选的。指定后,提供的列表将合并到从所选DNS策略生成的基本搜索域名中,并删除重复的域名。Kubernetes最多允许6个搜索域。
options可选的对象列表,其中每个对象可以具有name属性(必需)和value属性(可选)。此属性中的内容将合并到从指定的DNS策略生成的选项中,并删除重复的条目。

https://www.ichenfu.com/2018/12/20/k8s-pod-dns-policy/

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

default

不走coredns,直接走ecs的解析

apiVersion: v1
kind: Pod
metadata:
  name: alpine
  namespace: default
spec:
  containers:
  - image: alpine
    command:
      - sleep
      - "10000"
    imagePullPolicy: Always
    name: alpine
  dnsPolicy: Default

在部署jenkins的时候,布完了通过外网能够访问,但是jenkins无法访问官网以加载插件,登录容器,发现Ping ip和域名都不通,于是判断是网络问题
去查看flannel,结果发现都寄了

root@iZ0jl23h85dco8du531om7Z ~]# kubectl get pods --all-namespaces
NAMESPACE      NAME                                              READY   STATUS             RESTARTS          AGE
devops-tools   jenkins-597c5679df-gxtzd                          1/1     Running            0                 3h47m
kube-flannel   kube-flannel-ds-p924c                             0/1     CrashLoopBackOff   650 (2m16s ago)   2d7h
kube-flannel   kube-flannel-ds-xvxns                             0/1     CrashLoopBackOff   50 (58s ago)      3h53m

查看flannel的日志

[root@iZ0jl23h85dco8du531om7Z ~]# kubectl logs pod/kube-flannel-ds-p924c -n kube-flannel
I0111 09:10:40.142875       1 main.go:204] CLI flags config: {etcdEndpoints:http://127.0.0.1:4001,http://127.0.0.1:2379 etcdPrefix:/coreos.com/network etcdKeyfile: etcdCertfile: etcdCAFile: etcdUsername: etcdPassword: version:false kubeSubnetMgr:true kubeApiUrl: kubeAnnotationPrefix:flannel.alpha.coreos.com kubeConfigFile: iface:[] ifaceRegex:[] ipMasq:true ifaceCanReach: subnetFile:/run/flannel/subnet.env publicIP: publicIPv6: subnetLeaseRenewMargin:60 healthzIP:0.0.0.0 healthzPort:0 iptablesResyncSeconds:5 iptablesForwardRules:true netConfPath:/etc/kube-flannel/net-conf.json setNodeNetworkUnavailable:true}
W0111 09:10:40.142952       1 client_config.go:617] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
I0111 09:10:40.157983       1 kube.go:126] Waiting 10m0s for node controller to sync
I0111 09:10:40.158186       1 kube.go:431] Starting kube subnet manager
I0111 09:10:41.158774       1 kube.go:133] Node controller sync successful
I0111 09:10:41.158799       1 main.go:224] Created subnet manager: Kubernetes Subnet Manager - iz0jl23h85dco8du531om7z
I0111 09:10:41.158805       1 main.go:227] Installing signal handlers
I0111 09:10:41.158986       1 main.go:467] Found network config - Backend type: vxlan
I0111 09:10:41.159019       1 match.go:206] Determining IP address of default interface
I0111 09:10:41.159448       1 match.go:259] Using interface with name eth0 and address 172.30.254.89
I0111 09:10:41.159500       1 match.go:281] Defaulting external address to interface address (172.30.254.89)
I0111 09:10:41.159577       1 vxlan.go:138] VXLAN config: VNI=1 Port=0 GBP=false Learning=false DirectRouting=false
E0111 09:10:41.159891       1 main.go:327] Error registering network: failed to acquire lease: node "iz0jl23h85dco8du531om7z" pod cidr not assigned
I0111 09:10:41.159950       1 main.go:447] Stopping shutdownHandler...


[root@iZ0jl23h85dco8du531om7Z ~]# kubectl logs pod/kube-flannel-ds-xvxns -n kube-flannel
I0111 09:11:58.477079       1 main.go:204] CLI flags config: {etcdEndpoints:http://127.0.0.1:4001,http://127.0.0.1:2379 etcdPrefix:/coreos.com/network etcdKeyfile: etcdCertfile: etcdCAFile: etcdUsername: etcdPassword: version:false kubeSubnetMgr:true kubeApiUrl: kubeAnnotationPrefix:flannel.alpha.coreos.com kubeConfigFile: iface:[] ifaceRegex:[] ipMasq:true ifaceCanReach: subnetFile:/run/flannel/subnet.env publicIP: publicIPv6: subnetLeaseRenewMargin:60 healthzIP:0.0.0.0 healthzPort:0 iptablesResyncSeconds:5 iptablesForwardRules:true netConfPath:/etc/kube-flannel/net-conf.json setNodeNetworkUnavailable:true}
W0111 09:11:58.477143       1 client_config.go:617] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
I0111 09:11:58.490684       1 kube.go:126] Waiting 10m0s for node controller to sync
I0111 09:11:58.490829       1 kube.go:431] Starting kube subnet manager
I0111 09:11:59.490979       1 kube.go:133] Node controller sync successful
I0111 09:11:59.491024       1 main.go:224] Created subnet manager: Kubernetes Subnet Manager - worker-node01
I0111 09:11:59.491039       1 main.go:227] Installing signal handlers
I0111 09:11:59.491118       1 main.go:467] Found network config - Backend type: vxlan
I0111 09:11:59.491139       1 match.go:206] Determining IP address of default interface
I0111 09:11:59.491491       1 match.go:259] Using interface with name eth0 and address 172.30.254.88
I0111 09:11:59.491512       1 match.go:281] Defaulting external address to interface address (172.30.254.88)
I0111 09:11:59.491574       1 vxlan.go:138] VXLAN config: VNI=1 Port=0 GBP=false Learning=false DirectRouting=false
E0111 09:11:59.491826       1 main.go:327] Error registering network: failed to acquire lease: node "worker-node01" pod cidr not assigned
W0111 09:11:59.491932       1 reflector.go:347] github.com/flannel-io/flannel/subnet/kube/kube.go:432: watch of *v1.Node ended with: an error on the server ("unable to decode an event from the watch stream: context canceled") has prevented the request from succeeding
I0111 09:11:59.492064       1 main.go:447] Stopping shutdownHandler...

谷歌一下 Error registering network: failed to acquire lease: node "worker-node01" pod cidr not assigned
查到这个帖子
https://www.talkwithtrend.com/Article/251751
解决方法

编辑 master 机器上的 /etc/kubernetes/manifests/kube-controller-manager.yaml
启动文件加上下面两句话,下面这个cluster-cidr要和kube-flannel.yml里面的地址一致,要和kube-proxy.config.yaml里面的clusterCIDR一致

- --allocate-node-cidrs=true
- --cluster-cidr=10.244.0.0/16

出现这个问题的原因是kubeadm初始化的时候没有指定cidr

部署jenkins的时候,按照官方yaml,部署下来却发现pod一直处于pending状态
describe看一下

Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  23s (x944 over 15h)  default-scheduler  0/2 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had volume node affinity conflict.

这个学习用的集群只有一个master节点和一个普通节点,master默认是肯定不亲和的,那么问题就出在另外一个节点的1 node(s) had volume node affinity conflict
查看下挂载的pvc和pv,describe一下发现有亲和要求

Node Affinity:     
  Required Terms:  
    Term 0:        kubernetes.io/hostname in [worker-node01]

他需要部署在一个名叫 [worker-node01]的节点上,好吧,我没有初始化节点名,所以节点名是一长串字符

参考(并没有用上):
https://www.jianshu.com/p/c0b80e35d562
真-参考:
https://tsov.net/uupee/49604/

操作流程

修改节点名

如果这个节点上已经有应用在跑了,那么需要先驱逐上面的pod,不过如果只有一个node慎用,不然会使得所有pod都pending

kubectl drain nodename --delete-local-data --ignore-daemonsets

https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/safely-drain-node/

登录机器修改

登上需要修改的节点的机器,修改kubectl和kube-proxy的配置
↑虽然教程里面是这么讲的,但是我并没有看到教程里面说的文件里面有他要修改的那个参数
然后我对比了一下,发现这个node名称其实就是hostname,于是hostnamectl set-hostname修改主机名,kubectl delete node nodename删除原来的node
kubeadm token list查看token过期没,过期了重新生成一个
kubeadm token create
计算哈希

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

初始化节点

kubeadm reset

加入集群

kubeadm kubeadm join master:6443 --token mastertoken --discovery-token-ca-cert-hash sha256:hashcode

再次kubectl get nodes已经能够看到ready的worker-node01

RBAC是基于角色来控制访问的机制,通过rbac.authorization.k8s.io 这个api来实现
RBAC会涉及的资源大致分为三类:
角色(Role,ClusterRole):本质是权限的集合,角色并不是真正意义上的用户,而是像一种权限模板,他规定了采用这个模板的用户能够拥有怎样的权限,角色权限设定里面没有“拒绝”。Role需要指定namespace,二Cluster是集群范围的
主体/作用对象(subject):规定了创建的用户他的权限生效范围,包含User、Group、Service Account这三种类型
角色绑定(RoleBinding,ClusterRoleBingding):将创建好的用户和主题绑定在一起,RoleBinding限定在namespace,而Cluster是集群范围的

https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/
https://blog.yingchi.io/posts/2020/7/k8s-rbac.html
https://www.qikqiak.com/post/use-rbac-in-k8s/

标准实例

Role

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] #  资源组,"" 缺省为 core 组资源,其它诸如 apps 等;
  resources: ["pods"] #资源,比如 pods、deployments、services、secrets 等  
  verbs: ["get", "watch", "list"] #操作动词,如 get、list、watch、create、delete、update 等。

ClusterRole

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
  name: secret-reader
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Secret 资源的名称为 "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

RoleBingding

一个 RoleBinding 可以引用同一的名字空间中的任何 Role
RoleBinding 也可以引用 ClusterRole,以将对应 ClusterRole 中定义的访问权限授予 RoleBinding 所在名字空间的资源,以实现角色复用

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pod
# 你需要在该命名空间中有一个名为 “pod-reader” 的 Role
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
  name: jane # "name" 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
  kind: Role        # 此字段必须是 Role 或 ClusterRole
  name: pod-reader  # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
  apiGroup: rbac.authorization.k8s.io
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
  name: read-secrets
  # RoleBinding 的名字空间决定了访问权限的授予范围。
  # 这里隐含授权仅在 "development" 名字空间内的访问权限。
  namespace: development
subjects:
- kind: User
  name: dave # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBingding

apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 Secret 资源
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager      # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

绑定示例

Role+RoleBingding

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
  name: read-secrets
  # RoleBinding 的名字空间决定了访问权限的授予范围。
  # 这里隐含授权仅在 "development" 名字空间内的访问权限。
  namespace: development
subjects:
- kind: User
  name: dave # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRole+RoleBingding

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pods-reader-binding
  namespace: ns-a
subjects:
- kind: User
  name: yingchi
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

jenkins官方示例

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: jenkins-admin
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins-admin
  namespace: devops-tools
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins-admin
subjects:
- kind: ServiceAccount
  name: jenkins-admin
  namespace: devops-tools