K8S升级kubelet和runc,解决CGROUP泄露问题

一,现象:

k8s集群运行日久以后,有的node无法再调试新的pod应用,并且出现如下错误,当重启服务器之后,才可以恢复正常使用。

applying cgroup … caused: mkdir …no space left on device

二,原因:

memcg 是 Linux 内核中用于管理 cgroup 内存的模块,整个生命周期应该是跟随 cgroup 的,但是在低版本内核中(已知3.10),一旦给某个 memory cgroup 开启 kmem accounting 中的 memory.kmem.limit_in_bytes 就可能会导致不能彻底删除 memcg 和对应的 cssid,也就是说应用即使已经删除了 cgroup (/sys/fs/cgroup/memory 下对应的 cgroup 目录已经删除), 但在内核中没有释放 cssid,导致内核认为的 cgroup 的数量实际数量不一致,我们也无法得知内核认为的 cgroup 数量是多少。

这个问题可能会导致创建容器失败,因为创建容器为其需要创建 cgroup 来做隔离,而低版本内核有个限制:允许创建的 cgroup 最大数量写死为 65535,如果节点上经常创建和销毁大量容器导致创建很多 cgroup,删除容器但没有彻底删除 cgroup 造成泄露(真实数量我们无法得知),到达 65535 后再创建容器就会报创建 cgroup 失败并报错 no space left on device,使用 kubernetes 最直观的感受就是 pod 创建之后无法启动成功。

三,解决方案:

升级内核暂不可行,动静太大,所以这次先选用为runc及kubelet增加编译参数的方案,希望能解决。

 

1 升级runc

  • λ https://github.com/opencontainers/runc克隆runc源代码最新版。
  • λ 从根目录下的dockerfile创建一个docker镜像(更改后的dockerfile见下文)
  • λ 启动并进入此容器,运行生成runc。
make BUILDTAGS="seccomp nokmem"

   .将runc更名为docker-runc,拷贝出容器,留待备用。

扫描二维码关注公众号,回复: 9756817 查看本文章

    2 升级kubelet

  • λ 从https://github.com/kubernetes/kubernetes克隆k8s的指定版本。
  • λ 运行如下命令,将k8s的项目挂载到容器中(此镜像的dockerfile见下文)。
docker run  -it --rm \
-v /Users/xxx/Downloads/kubernetes-1.14.1:\
/usr/local/gopath/src/k8s.io/kubernetes \
harbor.xxx.cn/build-k8s:centos-7.6-go-1.12.9-k8s-1.14.6 \
bash
  • λ 进入容器,运行如下命令,在当前目录的_output/bin/下生成kubelet文件。
  • λkubelet,拷出容器,留待备用。
KUBE_BUILD_PLATFORMS=linux/runrunamd64 make all \
WHAT=cmd/kubelet GOGCFLAGS="-N -l" GOFLAGS="-tags=nokmem"

四,操作步骤

1, 先将出现此问题的节点驱逐出集群(这一操作可以让集群的服务不受影响)。

kubectl cordon k8s-node-xxx

2, 使用如下命令先后停止kubelet和start服务。

systemctl stop kubelet
systemctl stop docker

3, 将生成的docker-runc文件和kubelet文件替换掉(事先备份这两个文件)。请确认这两个文件执行可执行权限。

Docker-runc文件位置:/usr/bin/
Kubelet文件位置:/usr/bin/; /usr/local/bin/

4, 重启服务器,验证docker进程和kubelet进程运行正常。

5, 将此节点拉回集群。

kubectl uncordon k8s-node-xxx

附一RUNC的dockerfile

FROM golang:1.12-stretch

RUN sed -i "s@http://deb.debian.org@http://mirrors.163.com@g" /etc/apt/sources.list \
    && apt-get update && apt-get install -y \
    build-essential \
    curl \
    sudo \
    gawk \
    iptables \
    jq \
    pkg-config \
    libaio-dev \
    libcap-dev \
    libprotobuf-dev \
    libprotobuf-c0-dev \
    libnl-3-dev \
    libnet-dev \
    libseccomp2 \
    libseccomp-dev \
    protobuf-c-compiler \
    protobuf-compiler \
    python-minimal \
    uidmap \
    kmod \
    --no-install-recommends \
    && apt-get clean

# Add a dummy user for the rootless integration tests. While runC does
# not require an entry in /etc/passwd to operate, one of the tests uses
# `git clone` -- and `git clone` does not allow you to clone a
# repository if the current uid does not have an entry in /etc/passwd.
RUN useradd -u1000 -m -d/home/rootless -s/bin/bash rootless

# install bats
RUN cd /tmp \
    && git clone https://github.com/sstephenson/bats.git \
    && cd bats \
    && git reset --hard 03608115df2071fff4eaaff1605768c275e5f81f \
    && ./install.sh /usr/local \
    && rm -rf /tmp/bats

# install criu
ENV CRIU_VERSION v3.12
RUN mkdir -p /usr/src/criu \
    && curl -sSL https://github.com/checkpoint-restore/criu/archive/${CRIU_VERSION}.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1 \
    && cd /usr/src/criu \
    && make install-criu \
    && rm -rf /usr/src/criu

# setup a playground for us to spawn containers in
ENV ROOTFS /busybox
RUN mkdir -p ${ROOTFS}

COPY script/tmpmount /
WORKDIR /go/src/github.com/opencontainers/runc
ENTRYPOINT ["/tmpmount"]

ADD . /go/src/github.com/opencontainers/runc

    附二编译k8s的dockerfile

FROM centos:centos7.6.1811

MAINTAINER 4k

ENV GOROOT /usr/local/go
ENV GOPATH /usr/local/gopath
ENV PATH /usr/local/go/bin:$PATH

RUN yum install rpm-build which where rsync gcc gcc-c++ automake autoconf libtool make -y \
    && curl -L https://studygolang.com/dl/golang/go1.12.9.linux-amd64.tar.gz | tar zxvf - -C /usr/local

 

 

猜你喜欢

转载自www.cnblogs.com/aguncn/p/12462181.html