kubernetes 无法添加节点

今天在新增kubernetes节点时,发现无法新增节点,这篇文章记录下解决的情况。

新增节点

k8s新增节点时通过 token 验证,并带上ca证书的ha值。默认token的有效期为24小时。

# 重新生成新的token
kubeadm token create
kubeadm token list

# 获取ca证书sha256编码hash值
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

# 节点加入集群
kubeadm join 172.10.1.100:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx --ignore-preflight-errors Swap

遇到问题

因为已经安装过好几次,只有这一次有问题,故而发现了本次安装不一样的地方:

在获取ca证书的hash值时,这次出现了这样的输出:

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

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
writing RSA key

最后一行命令添加节点时输出如下:

[discovery] Trying to connect to API Server "10.128.0.4:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://10.128.0.4:6443"
[discovery] Failed to connect to API Server "10.128.0.4:6443": cluster CA found in cluster-info configmap is invalid: public key sha256:9b263f52d90b62458a6a6c6.......02ddc34bf26e1ac not pinned

提示直接给出了另一个hash值。

解决问题

一个非常简单的方式解决:

将错误提示的hash值代替原先获得的hash值即可。

按常理来说,是不应该出现这样的问题的。显而易见是第二步获取hash值出了问题。对比以前使用的命令行,一下就找出了原因:

文档经过多次复制粘贴,命令行已经不完整了。参考官方文档https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/中的命令,重新运行得到正确的命令行:

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

参考资料


防止容器因 jvm 占用资源过多被杀死而频繁重启

本文记录 jvm 和 elasticsearch 在容器中的设置要点。

JVM

不完全翻译自https://blog.csanchez.org/2017/05/31/running-a-jvm-in-a-container-without-getting-killed/

JDK 8u131 在 JDK 9 中有一个特性,可以在 Docker 容器运行时能够检测到多少内存的能力,这个功能在我看来,颇为流氓。

在容器内部运行 JVM,它在大多数情况下将如何默认最大堆为主机内存的1/4,而非容器内存的1/4。如果对 Java 容器中的jvm虚拟机不做任何限制,当我们同时运行几个 java 容器时,很容易导致服务器的内存耗尽、负载飙升而宕机;而如果我们对容器直接进行限制,就会导致内核在某个时候杀死 jvm 容器而导致频繁重启。

例如:

$ docker run -m 100MB openjdk:8u121 java -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 444.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

下面我们尝试 JDK 8u131 中的实验性参数 -XX:+UseCGroupMemoryLimitForHeap

$ docker run -m 100MB openjdk:8u131 java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 44.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

JVM能够检测容器只有100MB,并将最大堆设置为44M。

下面尝试一个更大的容器

$ docker run -m 1GB openjdk:8u131 java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 228.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

嗯,现在容器有1GB,但JVM仅使用228M作为最大堆。

除了JVM正在容器中运行以外,我们是否还可以优化它呢?

$ docker run -m 1GB openjdk:8u131 java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XX:MaxRAMFraction=1 -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 910.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

使用-XX:MaxRAMFraction 我们告诉JVM使用可用内存/ MaxRAMFraction作为最大堆。使用-XX:MaxRAMFraction=1我们几乎所有可用的内存作为最大堆。

elasticsearch

不完全翻译自https://www.elastic.co/guide/en/elasticsearch/reference/5.4/heap-size.html

Elasticsearch 将通过Xms(最小堆大小)和Xmx(最大堆大小)设置来分配 jvm.options 指定的整个堆,默认使用最小和最大大小为2 GB的堆。这些设置的值取决于服务器上可用的内存。在生产环境时,要确保 Elasticsearch 有足够的可用堆。

一些好的经验是:

  • 将最小堆大小(Xms)和最大堆大小(Xmx)设置为彼此相等。

  • Elasticsearch 可用的堆越多,可用于缓存的内存就越多。但太多的堆会导致一直进行垃圾回收。

  • 将 Xmx 设置为不超过物理内存的50%,以确保有足够的物理内存留给内核文件系统缓存。

  • 不要将 Xmx 设置为J VM 用于压缩对象指针(压缩oops)的临界值以上,接近32 GB。可以通过在日志中查找一行来验证您是否处于限制之下,如下所示:

    heap size [1.9gb], compressed ordinary object pointers [true]
    
  • Even better, try to stay below the threshold for zero-based compressed oops; the exact cutoff varies but 26 GB is safe on most systems, but can be as large as 30 GB on some systems. You can verify that you are under the limit by starting Elasticsearch with the JVM options -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode and looking for a line like the following:

    heap address: 0x000000011be00000, size: 27648 MB, zero based Compressed Oops
    

    showing that zero-based compressed oops are enabled instead of

    heap address: 0x0000000118400000, size: 28672 MB, Compressed Oops with base: 0x00000001183ff000
    

以下是如何通过jvm.options文件设置堆大小的示例:

-Xms2g 
-Xmx2g 

也可以通过环境变量设置堆大小。

ES_JAVA_OPTS ="- Xms2g -Xmx2g"./bin/elasticsearch 
ES_JAVA_OPTS ="- Xms4000m -Xmx4000m"./bin/elasticsearch 

参考资料


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)

参考资料