环境:

K8S版本:v1.19.3

名称系统IP主机名
MasterCentOS 7.8.2003 x86_64172.16.135.10k8s-master01
Node01CentOS 7.8.2003 x86_64172.16.135.11k8s-node01
Node02CentOS 7.8.2003 x86_64172.16.135.12k8s-node02
Node03CentOS 7.8.2003 x86_64172.16.135.13k8s-node03

备注:此集群仅用于测试使用,不可用户生产环境。生产环境需要考虑Master节点的高可用,且需要做适当的优化,后面可能会写一篇文章单独说

环境配置

时间同步

K8S集群应保持时间同步,避免出现奇怪的问题。

# 在所有节点上执行
yum install chrony
systemctl start chronyd
systemctl enable chronyd

关闭swap

k8s默认不允许主机开启swap,这里将其关闭。如果需要启用swap忽略错误,需要在kubelet启动参数上添加--ignore-preflight-errors=Swap。或者修改/etc/sysconfig/kubeletKUBELET_EXTRA_ARGS="--fail-swap-on=false"

关闭swap

# 在所有节点上执行
# 修改 /etc/fstab,注释swap一行
# 然后执行swapoff命令立即关闭swap
swapoff -a

- 阅读剩余部分 -

文章出处,做了一些修改

在一次例行的安全扫描中有发现安全风险提示:SSH服务配置不建议使用arcfour流密码或无任何密码,RFC 4253 不建议使用arcfour弱加密算法,仅使用CTR模式加密算法,如AES-CTR。所以花了一些时间来简单地查找资源并分析了相关的原因,在此写下笔记。

为了确保信息的传输,SSH 在事务中的各个点采用了许多不同类型的数据操作技术。这些包括对称加密,非对称加密和散列的形式。如果配置为CBC模式的话,OpenSSH没有正确地处理分组密码算法加密的SSH会话中所出现的错误,导致可能泄露密文中任意块最多32位纯文本。在以标准配置使用OpenSSH时,攻击者恢复32位纯文本的成功概率为2^{-18},此外另一种攻击变种恢复14位纯文本的成功概率为2^{-14}。查询本机上的ssh服务所支持并启用了的算法:

sshd -T | grep ciphers | perl -pe 's/,/\n/g' | sort -u
sshd -T | grep "\(ciphers\|macs\|kexalgorithms\)"

SSH的配置文件中加密算法没有指定的话,较低版本的ssh-server默认支持所有加密算法,包括arcfour,arcfour128,arcfour256等这些弱加密算法,这就可能会导致安全风险。sshd -T会将显示全量的配置,ssh -Q会查询这些配置中分类的一些参数方法等:

[-Q cipher | cipher-auth | mac | kex | key]

查询支持的加密算法:

ssh -Q cipher-auth    #启用的
ssh -Q cipher    #所有的

- 阅读剩余部分 -

Kubernetes 1.18.x版本部署metrics-server组件时,采集不到数据

表现为:

  • kubectl top nodeskubectl top nodes报错信息如下

    Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io)
    Error from server (ServiceUnavailable): the server is currently unable to handle the request (get pods.metrics.k8s.io)

查看kube-apiserver的日志信息
kubectl -n kube-system logs -f kube-apiserver-master-1 --tail 10

E0826 04:25:10.111976       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
E0826 04:25:15.112635       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
W0826 04:25:37.762783       1 handler_proxy.go:102] no RequestInfo found in the context
E0826 04:25:37.762890       1 controller.go:114] loading OpenAPI spec for "v1beta1.metrics.k8s.io" failed with: failed to retrieve openAPI spec, http error: ResponseCode: 503, Body: service unavailable
, Header: map[Content-Type:[text/plain; charset=utf-8] X-Content-Type-Options:[nosniff]]
I0826 04:25:37.762915       1 controller.go:127] OpenAPI AggregationController: action for item v1beta1.metrics.k8s.io: Rate Limited Requeue.
E0826 04:25:41.763211       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
E0826 04:25:46.764318       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
E0826 04:26:11.763745       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
E0826 04:26:16.764339       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
E0826 04:26:41.763920       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
E0826 04:26:46.764574       1 available_controller.go:420] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: Get https://10.108.243.54:443/apis/metrics.k8s.io/v1beta1: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

这段日志看不出什么问题。

最终解决:kube-apiserver添加--enable-aggregator-routing=true启动参数,原因不明,因为在我另一个k8s 1.16.x集群中,并没有添加这项参数,工作正常。有知道原因的伙伴请不吝告知,在此谢过。

PS:网上有文章说如果master节点没有运行kube-proxy进程才需要加上这个启动参数,而我的集群中master节点是有运行kube-proxy的。

参考链接1:https://github.com/kubernetes-sigs/metrics-server/issues/448
参考链接2:https://blog.z0ukun.com/?p=1462

邮件内容支持附件、图片、文字

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time     : 2020/5/13 6:05 下午
# @Author   : 陈日志
# @Email    : [email protected]
# @File     : Mail.py
import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.header import Header


class Mail(object):
    def __init__(self, smtp_server, smtp_port=25, ssl=False):
        self._message = MIMEMultipart()
        if ssl:
            self.smtp = smtplib.SMTP_SSL(smtp_server, 465 if smtp_port == 25 else smtp_port)
        else:
            self.smtp = smtplib.SMTP(smtp_server, smtp_port)
        self._receivers = None
        self._sender = None
        self._content = ''
        self._imageid = 0

    def login(self, user, password):
        try:
            self.smtp.login(user, password)
        except Exception as e:
            print(e)
            raise smtplib.SMTPAuthenticationError(1, "login failed")
        self._message["From"] = user
        self._sender = user

    @property
    def receives(self):
        return self._receivers

    @property
    def receives(self):
        return self._receivers

    @receives.setter
    def receives(self, receivers):
        type_ = type(receivers)
        if type_ is str:
            receivers = receivers.split(',')
            self._message["To"] = receivers
            self._receivers = receivers
        elif type_ is list or type_ is tuple:
            self._message["To"] = ','.join(receivers)
            self._receivers = receivers

    @property
    def title(self):
        return self._message.get("Subject")

    @title.setter
    def title(self, title):
        self._message["Subject"] = Header(title, 'utf-8')

    @property
    def content(self):
        return self._message

    @content.setter
    def content(self, content):
        self._content = content

    @property
    def imageid(self):
        self._imageid += 1
        return self._imageid

    def append_content(self, content):
        self._content += content

    def add_attachment(self, file):
        att = MIMEText(open(file, 'rb').read(), 'base64', 'utf-8')
        att["Content-Type"] = 'application/octet-stream'
        # att["Content-Disposition"] = 'attachment; filename=%s' % os.path.basename(file).encode('utf-8')
        att.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', os.path.basename(file)))
        self._message.attach(att)

    def add_image(self, img):
        imageid = 'image%s' % self.imageid
        msg = MIMEImage(open(img, 'rb').read())
        msg.add_header('Content-ID', imageid)
        self._content += '<br/><img src="cid:%s">' % imageid
        self._message.attach(msg)

    def send(self):
        self._message.attach(MIMEText(self._content, 'html', 'utf-8'))
        try:
            self.smtp.sendmail(self._sender, self._receivers, self._message.as_string())
        except Exception as e:
            print(e)
            raise smtplib.SMTPConnectError(1, "Send mail error")

    def logout(self):
        self.smtp.quit()

- 阅读剩余部分 -

使用 Haproxy、Nginx 等软件反向代理 HTTP 协议时,往往会设置 X-Forwarded-For 等头部让后端 Web server 能正确取到客户端真实 IP。如果做 4 层代理,那么就得借助 Linux 的特性 TProxy 来实现,本文讲解如何配置 Haproxy、Nginx 来实现透明代理。

Haproxy

网上很多文章都说到 Haproxy 要重新编译,编译参数加上USE_LINUX_TPROXY=1。其实这种说法已经过时了,以 RHEL 系 Linux 发行版为例,RHEL6、RHEL7、RHEL8 官方提供的 rpm 包默认就加上了这个编译参数,因此如对版本无特殊要求,使用官方提供的版本就可以了,使用 yum install haproxy 安装即可。

使用限制

  • 代理服务器与后端服务器IP地址必须在同一个网段
  • 后端服务器的默认网关要指向代理服务器的地址*

代理服务器配置

haproxy

listen 80
    bind :80
    mode tcp
    source 0.0.0.0 usesrc clientip
    server server1 server1:8080 weight 1

关键配置 source 0.0.0.0 usesrc clientip

- 阅读剩余部分 -