Linux:集群工具ClusterShell

ClusterShell 是一个轻量级的运维工具,可以在一台机器上向多台机器发送指令,轻松实现类似《黑客帝国》中批量关闭电厂的效果:

ClusterShell每天在Linux超级计算机(拥有超过5000个计算节点)上使用。使用很简单,只要在主控机上配置好子节点的ssh密钥登陆,同时做好节点配置即可,非常便捷。这篇文章介绍它的安装和简单使用。

安装

yum install -y clustershell
// or
apt-get install clustershell

配置

在/etc/clustershell目录下,手动创建groups文件

$ vim /etc/clustershell/groups

all: a1 host1 host2
name:host3 host4
adm: example0
oss: example4 example5
mds: example6
io: example[4-6]
compute: example[32-159]
gpu: example[156-159]
hadoop: z[1-4]

# 需要注意的是all 是必须配置的。

hadoop: z[1-4],是指定hadoop组中有四个节点,分别是z1,z2,z3,z4。

其它的配置也类似,可以加入多个组,使用的时候通过-g hadoop来选择。

命令

clush -a 全部 等于 clush -g all
clush -g 指定组
clush -w 操作主机名字,多个主机之间用逗号隔开
clush -g 组名 -c  --dest 文件群发     (-c等于--copy)

注意:clush 是不支持环境变量的 $PATH

实例

输出所有节点的信息

$ clush -a "uptime"
$ clush -b -a "uptime"

删除指定节点的文件

$ clush -w z2,z3,z4 rm -rf /mnt/zhao/soft/jdk

集群分发文件

$ clush -b -g hadoop --copy /mnt/zhao/package/jdk-7u79-linux-x64.tar.gz --dest /mnt/zhao/package/

集群查看文件

查看所有hadoop组中/mnt/zhao/package/目录下的文件,输出结果合并。

$ clush -b -g hadoop ls /mnt/zhao/package/

交互模式

启动clush,后面不带命令,就进入交互模式:

$ clush -w hadoop

参考资料


解决 Firefox is already running, but is not responding 错误

远程vnc的时候,发现firefox没办法启动,报了这个错误:

Firefox is already running, but is not responding. To open a new window, you must first close the existing Firefox process, or restart your system.

即使将 Firefox 的进程全部杀死,仍然会报这个错误。

解决方法如下:

在linux的终端输入:

firefox -profilemanager 

# 或者
firefox -p

在出现的页面中将当前出错的 Profile 删除掉,然后新建个即可。


kubernetes helm chart

这篇文章记录如何自建 helm chart 和 helm repo。一些前置的背景知识可以在这里了解:kubernetes helm 入门

1. chart

创建chart

helm create alpine

目录结构如下所示,我们主要关注目录中的这三个文件即可:Chart.yaml、values.yaml和NOTES.txt。

alpine
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── NOTES.txt
│   └── service.yaml
└── values.yaml

打开Chart.yaml,填写应用的详细信息

打开并根据需要编辑 values.yaml

chart校验/打包

对Chart进行校验

$ helm lint alpine

==> Linting alpine
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, no failures

可以添加一个图标,在Chart.yaml最后一行里添加:

$ vi alpine/Chart.yaml

icon: https://cdn.kelu.org/kelu.jpg

对Chart进行打包:

$ helm package alpine --debug

Successfully packaged chart and saved it to: /var/local/k8s/helm/alpine-0.1.0.tgz
[debug] Successfully saved /var/local/k8s/helm/alpine-0.1.0.tgz to /root/.helm/repository/local

chart中定义依赖

在chart目录中创建一个requirements.yaml文件定义该chart的依赖。

$ cat > ./alpine/requirements.yaml <<EOF
dependencies:
- name: mariadb
  version: 0.6.0
  repository: https://kubernetes-charts.storage.googleapis.com
EOF

通过helm命令更新和下载cahrt的依赖

$ helm dep update ./alpine

Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "local" chart repository
...Successfully got an update from the "monocular" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading mariadb from repo https://kubernetes-charts.storage.googleapis.com
Deleting outdated charts

在次安装运行chart时会把依赖中定义的chart运行起来。

部署本地repo

使用Helm serve命令启动一个repo server,该server缺省使用’$HELM_HOME/repository/local’目录作为Chart存储,并在8879端口上提供服务。

helm serve --address 0.0.0.0:8879 &

启动本地repo server后,将其加入Helm的repo列表。

helm repo add local http://127.0.0.1:8879
"local" has been added to your repositories

也可以把每个chart打包的文件集中存放到charts目录,使用以下命令生成index.yaml文件。

mkdir -p charts
mv alpine-0.1.0.tgz charts/

$ helm serve --repo-path ./charts --address 0.0.0.0:8879 &

测试可以看到index.yaml的内容为:

我们可以自定义repo地址:

helm serve --address 0.0.0.0:8879 --url http://eur2.kelu.org:8879 &
helm serve --repo-path ./charts --address 0.0.0.0:8879 --url http://eur2.kelu.org:8879 &

可以发现index.yaml 的 url 地址变了

重建 chart 链接

helm repo index charts --url http://192.168.122.1:81/charts

或者,在index.yaml中之增加新cahrt的元数据信息。

helm repo index charts --url http://192.168.122.1:81/charts --merge

2. repo

添加 repo

通过以下命令增加repo

helm repo add charts http://192.168.122.1:81/charts
[root@k8s-master ~]# helm repo list
NAME        URL
local       http://127.0.0.1:8879/charts
stable      https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
monocular   https://kubernetes-helm.github.io/monocular
charts      http://192.168.122.1:81/charts

更新repo

如果repo有更新,执行repo update命令会更新所以已增加的repo

helm repo update

使用mongo删除在monocular的repo

monocular的repo是存在数据库中的,与命令行的helm完全独立。

当你在monocular中添加一个拥有很多内容的源的时候,api容器组会不断缓存,只有缓存完成后才会提供服务,此时你心急如焚想删掉这个该死的源,可以直接在mongo数据库里删除:

mongo
show databases;
use monocular;
db.repos.remove(xxx)

参考资料


容器化 nfs 服务器安装

这篇文章记录如何安装和使用容器化的nfs,目前只是临时使用验证某个服务,只记录安装使用过程,不做过多描述。

什么是 nfs

它的主要功能是通过网络让不同的机器系统之间可以彼此共享文件和目录。

NFS服务器可以允许NFS客户端将远端NFS服务器端的共享目录挂载到本地的NFS客户端中。一般用来存储共享视频,图片等静态数据。

安装

  1. 加载内核模块 nfs

    modprobe nfs
    modprobe nfsd
    
  2. 安装nfs-utils

    apt-get install nfs-common
    # 或者
    yum install nfs-utils
    
  3. 安装docker

    curl -sSL https://get.docker.com/ | sh
    usermod -aG docker $USER
    systemctl enable docker
    systemctl start docker
    
  4. 准备nfs配置文件

    例如:配置文件位于 ./exports.txt

    /nfs        *(rw,fsid=0,sync,no_root_squash)
    
  5. 运行服务器

    参考ehough/docker-nfs-server - github

    version: '3.2'
       
    services:
      nfs:
        image: erichough/nfs-server:latest
        container_name: nfs
        network_mode: bridge
        restart: always
        volumes:
          - /home/kelu/Workspace:/nfs
          - ./exports.txt:/etc/exports
        ports:
          - 2049:2049
          - 2049:2049/udp
          - 32765:32765
          - 32765:32765/udp
          - 32767:32767
          - 32767:32767/udp
        cap_add:
          -  SYS_ADMIN
        privileged: true
    

    将主机/home/kelu/Workspace 文件夹作为共享根目录。

  6. 客户端连接

    服务器ip为 172.10.1.100 ,将共享目录挂载到客户端的 /kelu 目录下

    mount -o nfsvers=4 172.10.1.100:/ /kelu
    mount # 查看挂载
    umount -v /kelu   # 解除挂载
    umount -f -l /app/Downloads # 服务端挂了的情况下接触挂载
    

错误参考

无法启动,显示“rpc.statd already running”

==================================================================
      STARTING SERVICES ...
==================================================================
----> starting rpcbind
----> starting exportfs
exportfs: /etc/exports [1]: Neither 'subtree_check' or 'no_subtree_check' specified for export "*:/nfs".
  Assuming default behaviour ('no_subtree_check').
  NOTE: this default has changed since nfs-utils version 1.0.x

----> starting rpc.mountd on port 32767
----> starting rpc.statd on port 32765 (outgoing from port 32766)
Statd service already running!
---->
----> ERROR: /sbin/rpc.statd failed
---->

先stop掉这个服务即可:

systemctl stop rpc-statd.service

参考资料


kubernetes kubectl 命令行

本文记录常用的kubectl命令行。

官方参考手册:https://kubernetes.io/docs/reference/

蚂蚁金服的 Jimmy Song(宋净超) 主导了一个Kubernetes Handbook 的开源项目,里面有官方手册中这一部分的中文参考,对英文苦手的可以看看:https://jimmysong.io/kubernetes-handbook/guide/command-usage.html

在此之前我们可以先看看命令行的帮助:

kubectl help

从帮助给我们划分了几个 kubectl 的命令主题:

  • 入门命令
  • 部署命令 deployment
  • 集群管理命令 cluster
  • 问题定位命令
  • 高级命令
  • 设置命令
  • 其它

下文的分类是我自己分配的,不按照帮助的显示顺序。

自动补全

$ source <(kubectl completion bash) # setup autocomplete in bash, bash-completion package should be installed first.
$ source <(kubectl completion zsh)  # setup autocomplete in zsh

或者永久性设置(bash):
kubectl completion bash >> ~/.bashrc
source ~/.bashrc

(zsh):
plugins=(kubectl)
source <(kubectl completion zsh)

帮助命令

$ kubectl help
$ kubectl explain pods,svc                       # get the documentation for pod and svc manifests

入门命令

  1. kubectl create

    kubectl run

    也可以用 kubectl apply

    $ kubectl create -f ./my-manifest.yaml           # create resource(s)
    $ kubectl create -f ./my1.yaml -f ./my2.yaml     # create from multiple files
    $ kubectl create -f ./dir                        # create resource(s) in all manifest files in dir
    $ kubectl create -f https://git.io/vPieo         # create resource(s) from url
    
    $ kubectl run nginx --image=nginx                # start a single instance of nginx
    
  2. $ kubectl delete -f ./pod.json                                              # Delete a pod using the type and name specified in pod.json
    $ kubectl delete pod,service baz foo                                        # Delete pods and services with same names "baz" and "foo"
    $ kubectl delete pods,services -l name=myLabel                              # Delete pods and services with label name=myLabel
    $ kubectl delete pods,services -l name=myLabel --include-uninitialized      # Delete pods and services, including uninitialized ones, with label name=myLabel
    $ kubectl -n default delete pv --all                                      # 删除 default 下所有的pv
    $ kubectl delete node xxx                                                 # 删除 node
    
  3. kubectl get xxx

    kubectl describe nodes xxx

    # 查询资源
    $ kubectl get services                          # List all services in the namespace
    $ kubectl get pods --all-namespaces             # List all pods in all namespaces
    $ kubectl get pods -o wide                      # List all pods in the namespace, with more details
    $ kubectl get deployment my-dep                 # List a particular deployment
    $ kubectl get pods --include-uninitialized      # List all pods in the namespace, including uninitialized ones
    
    # 资源详细描述
    $ kubectl describe nodes my-node
    $ kubectl describe pods my-pod
    
    # 排序
    $ kubectl get services --sort-by=.metadata.name
    $ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
    
    # 选择标签
    $ kubectl get pods --selector=app=cassandra rc -o \
      jsonpath='{.items[*].metadata.labels.version}'
    $ kubectl get pods --field-selector=status.phase=Running
    
    # ExternalIPs
    $ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'
    
    # 列出所有密钥
    $ kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq
    
    # 列出事件按时间排序
    $ kubectl get events --sort-by=.metadata.creationTimestamp
    
  4. 用于更新 API 对象的命令有:

    kubectl patch,

    kubectl annotate,

    kubectl edit,

    kubectl replace,

    kubectl scale,

    kubectl apply,

    kubectl expose

    • 更改pod

      # 从json文件滚动升级pods的镜像
      $ kubectl rolling-update frontend-v1 -f frontend-v2.json       
      
      # 重命名 + 升级pod镜像
      $ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2  
      
      # 回滚pod
      $ kubectl rolling-update frontend-v1 frontend-v2 --rollback
      
      # 从json文件替换pod
      $ cat pod.json | kubectl replace -f - 
      
      # 强制替换pod
      $ kubectl replace --force -f ./pod.json
      
      # 暴露端口
      $ kubectl expose rc nginx --port=80 --target-port=8000
      
      # 更新pod镜像
      $ kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -
      
      $ kubectl label pods my-pod new-label=awesome                      # Add a Label
      $ kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # Add an annotation
      $ kubectl autoscale deployment foo --min=2 --max=10                # Auto scale a deployment "foo"
      
      $ kubectl edit svc/docker-registry                      # Edit the service named docker-registry
      $ KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # Use an alternative editor
      
    • patch 补丁

      kubectl patch 命令接受 YAML 或 JSON 格式的补丁,且补丁能够以文件或直接以命令行参数的形式进行传递

      kubectl patch 命令拥有一个 type 参数,可以将其设置为以下值:

      参数值 合并类型
      json JSON 补丁, RFC 6902
      merge JSON 合并补丁, RFC 7386
      strategic 默认值,策略性合并补丁

      使用JSON 合并补丁更新一个列表,必须重新定义整个列表。新的列表会完全替换掉原先的列表。

      # 策略性合并补丁
      $ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' 
      $ kubectl patch deployment patch-demo --patch "$(cat patch-file.yaml)"
      $ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'
      
      # 查看补丁情况
      # kubectl get deployment patch-demo --output yaml
      
      $ kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'
      $ kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'
      
      # 增加新值
      $ kubectl patch sa default --type='json' -p='[{"op": "add", "path": "/secrets/1", "value": {"name": "whatever" } }]'
      
    • scale

      $ kubectl scale --replicas=3 rs/foo                                 # Scale a replicaset named 'foo' to 3
      $ kubectl scale --replicas=3 -f foo.yaml                            # Scale a resource specified in "foo.yaml" to 3
      $ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # If the deployment named mysql's current size is 2, scale mysql to 3
      $ kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # Scale multiple replication controllers
      
      
  5. 资源类型

    资源类型 简写
    all  
    certificatesigningrequests csr
    clusterrolebindings  
    clusterroles  
    componentstatuses cs
    configmaps cm
    controllerrevisions  
    cronjobs  
    customresourcedefinition crd, crds
    daemonsets ds
    deployments deploy
    endpoints ep
    events ev
    horizontalpodautoscalers hpa
    ingresses ing
    jobs  
    limitranges limits
    namespaces ns
    networkpolicies netpol
    nodes no
    persistentvolumeclaims pvc
    persistentvolumes pv
    poddisruptionbudgets pdb
    podpreset  
    pods po
    podsecuritypolicies psp
    podtemplates  
    replicasets rs
    replicationcontrollers rc
    resourcequotas quota
    rolebindings  
    roles  
    secrets  
    serviceaccount sa
    services svc
    statefulsets sts
    storageclasses sc
  6. 输出格式

    -o 或者 -output 标签

Output format Description
-o=custom-columns=<spec> Print a table using a comma separated list of custom columns
-o=custom-columns-file=<filename> Print a table using the custom columns template in the <filename> file
-o=json Output a JSON formatted API object
-o=jsonpath=<template> Print the fields defined in a jsonpath expression
-o=jsonpath-file=<filename> Print the fields defined by the jsonpath expression in the <filename> file
-o=name Print only the resource name and nothing else
-o=wide Output in the plain-text format with any additional information, and for pods, the node name is included
-o=yaml Output a YAML formatted API object
  1. 输出debug级别

    -v 或者 --v 标志

级别 描述
--v=0 Generally useful for this to ALWAYS be visible to an operator.
--v=1 A reasonable default log level if you don’t want verbosity.
--v=2 Useful steady state information about the service and important log messages that may correlate to significant changes in the system. This is the recommended default log level for most systems.
--v=3 Extended information about changes.
--v=4 Debug level verbosity.
--v=6 Display requested resources.
--v=7 Display HTTP request headers.
--v=8 Display HTTP request contents.
--v=9 Display HTTP request contents without truncation of contents.

问题定位命令

  1. 集群信息

    $ kubectl cluster-info                                                  # 集群信息
    $ kubectl cluster-info dump                                             # 更详细的集群信息
    $ kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 输出到文件
    
    $ kubectl config current-context
    
  2. top

    $ kubectl top pod POD_NAME --containers               # Show metrics for a given pod and its containers
    $ kubectl top node my-node                                              # Show metrics for a given node
    
  3. 维护模式

    $ kubectl cordon my-node                                                # 设置节点不可调度
    $ kubectl drain my-node                                                 # 将节点的pod 平滑 迁移到其他节点
    $ kubectl uncordon my-node                                              # 取消节点不可调度。
    
    # 参考 Kubernetes中的Taint和Toleration(污点和容忍): https://jimmysong.io/posts/kubernetes-taint-and-toleration/
    # Taint(污点)和 Toleration(容忍)可以作用于 node 和 pod 上,其目的是优化 pod 在集群间的调度,
    # 具有 taint 的 node 和 pod 是互斥关系,而具有节点亲和性关系的 node 和 pod 是相吸的。
    
    # 为 node1 设置 taint:
    kubectl taint nodes node1 key1=value1:NoSchedule
    kubectl taint nodes node1 key1=value1:NoExecute
    kubectl taint nodes node1 key2=value2:NoSchedule
    # 删除 taint:
    kubectl taint nodes node1 key1:NoSchedule-
    kubectl taint nodes node1 key1:NoExecute-
    kubectl taint nodes node1 key2:NoSchedule-
    
    # 为 pod 设置 toleration
    只要在 pod 的 spec 中设置 tolerations 字段即可,可以有多个 key:
    tolerations:
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoSchedule"
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoExecute"
    - key: "node.alpha.kubernetes.io/unreachable"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 6000
    value 的值可以为 NoSchedule、PreferNoSchedule 或 NoExecute。
    tolerationSeconds 是当 pod 需要被驱逐时,可以继续在 node 上运行的时间。
    
  4. Pods 互动

    kubectl logs

    kubectl attach

    kubectl exec

    $ kubectl logs my-pod                                 # dump pod logs (stdout)
    $ kubectl logs my-pod -c my-container                 # dump pod container logs (stdout, multi-container case)
    $ kubectl logs -f my-pod --namespace="xx"                             # stream pod logs (stdout)
    $ kubectl logs -f my-pod -c my-container              # stream pod container logs (stdout, multi-container case)
    $ kubectl run -i --tty busybox --image=busybox -- sh  # Run pod as interactive shell
    $ kubectl attach my-pod -i                            # Attach to Running Container
    
    $ kubectl exec my-pod -- ls /                         # Run command in existing pod (1 container case)
    $ kubectl exec my-pod -c my-container -- ls /         # Run command in existing pod (multi-container case)
    
    
  5. 暴露端口

    kubectl port-forward 暴露本地端口给pod

    kubectl proxy 使API server监听在本地端口

    $ kubectl port-forward my-pod 5000:6000               # Listen on port 5000 on the local machine and forward to port 6000 on my-pod
    
    $ kubectl proxy --address='0.0.0.0'  --accept-hosts='^*$'