为普通用户授权生成kubeconfig

概述 开发同学用kubectl查看线上Kubernetes集群中的一些情况,如何生成kubeconfig? 对指定的用户赋予合适的权限,首先需要明确这里的授权对象(用户)、该对象的认证方式、授权权限大小及授权方式几个概念。 在k8s集群中,当一个请求到达APIServer时,会经过多个阶段以执行访问验证、控制的行为,阶段顺序依次是: 传输安全,一般我们APIServer只开启https端口,建立TLS连接以确保传输安全 认证,请求到达APIServer后会依次尝试每个认证模块,有一个认证通过即可 鉴权,认证通过后进入到鉴权阶段,即判断特定的用户对特定的对象进行特定的操作是否有相应的权限 准入,鉴权通过则进入准入阶段,准入模块可以对请求进行验证和修改,多个准入控制器会被依次调用。有一个准入失败则立即响应拒绝服务 下面概括了认证授权相关的概念 用户 Kubernetes集群中的用户有两类: 服务账户serviceaccount,由k8s管理,绑定到指定的名称空间,每个sa与一个secret关联用以保存相关凭据 普通用户,集群中没有对应的资源专门管理普通用户,一般是在集群外管理的。这里当然也有用户组的概念 认证策略 身份认证策略有下面几种: 客户端证书:传递给APIServer的--client-ca-file=SOMEFILE参数指定了一个或多个证书机构,用此证书机构验证客户端提供的证书,验证通过则表示认证通过 持有者令牌 静态令牌:给APIServer传递--token-auth-file=SOMEFILE选项以启用 启动引导令牌:动态管理的令牌,一般用作平滑启动引导新集群 服务账户令牌:sa关联的secret中保存APIServer公开的CA证书和一个已签名的JWT令牌。一般在pod内使用,当然也可以在集群外部使用 OpenID Connect令牌:OAuth2方式 webhook令牌:用回调机制来验证令牌,Webhook插件用POST请求发送一个JSON序列化的对象到远程服务 静态密码 HTTP基本认证 用户伪装 身份认证代理 匿名 鉴权 鉴权主要有下面四个模块: Node:限制kubelet对APIServer的请求 ABAC:基于属性的访问控制 RBAC:基于角色的访问控制 Webhook:http回调,查询外部的REST服务 生成kubeconfig 利用ssl工具一步步生成 一般我们给开发同学的权限是只读的,那如何给单个开发同学或者开发组生成对应的kubeconfig以只读权限访问集群? 在kubeconfig文件中,包含的信息有用户(组)、集群地址、客户端的数字证书(或Bearer token,或basic auth)等信息。由集群CA签名的有合法证书的用户都是通过认证的用户,Kubernetes使用证书中的subject的通用名称(Common Name)字段作为用户名,Organization字段作为用户组信息。 因此可以为指定用户或用户组生成集群CA机构签发的客户端证书,以证书认证的方式访问APIServer。 常用的证书生成工具有easyrsa、cfssl和openssl,这里以openssl为例: 1 2 3 4 5 6 7 8 9 mkdir developer; cd developer/ # 生成私钥 openssl genrsa -out developer.key # 生成签名请求文件,CN为developer,可指定organization字段值作为组名 openssl req -new -key developer.key -out developer.csr -subj "/CN=developer" # 查看csr openssl req -noout -text -in developer.csr # 通过签名请求文件、集群CA证书及其私钥生成客户端签名证书 openssl x509 -req -in developer.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out developer.crt -days 3650 有了用户(组),且通过了集群身份认证,开始对其授权。集群中内置了一些常用的role和clusterrole,可以作为参考或者直接拿来用。此处将developer用户和内置的view这个clusterrole进行绑定,也可以为生成数字证书的时候指定的Organization组绑定对应的role或clusterrole ...

November 9, 2020 · 4 min · 703 words · erpan

Nginx优化

参考:https://www.nginx.com/blog/tuning-nginx/ nginx一般用作高性能反向代理。在http七层代理的情况下,它首先接收客户端请求,经过处理后再请求upstream server,因此nginx服务器上的连接数是客户端请求数的2倍,在高并发情况下,建立的连接数非常多,因此系统相关资源如打开文件数、tcp连接、可分配端口等很可能称为其瓶颈。可以从系统内核参数及nginx自身做一些优化。下面提到的相关参数按需调整。 相关内核参数 tcp连接队列调整 在内核中,为tcp连接维护了两个队列。一个是已经建立了连接的accept队列,这时候连接三次握手已经完毕,处于 established 状态;一个是还没有完全建立连接的队列,这个时候三次握手还没完成,处于 syn_rcvd 的状态。过程如下: 当server接收到client的 SYN 报⽂时,会将其加⼊到内核的SYN半连接队列 接着发送SYN+ACK给client,等待client回应ACK报⽂,client收到此包后connect()调用返回,进入establish状态 server接收到ACK报⽂后,把此连接从SYN队列移除并放⼊到Accept队列 此时server端应⽤调⽤accpet() socket接⼝从Accept 队列取出连接用于数据通信 如何查看这两个队列情况? 全连接查看:ss -ant 半连接查看:ss -ant | grep -i syn_recv | wc -l LISTEN: Recv-Q 表示当前socket完成三次握手等待用户进程(此处即nginx)accept的连接个数,即全连接队列使用量。一般我们常常看到此值为0,没有堆积 Send-Q 表示当前socket全连接队列最大长度 非LISTEN: Recv-Q 表示receive queue中已收到未读取的字节大小 Send-Q 表示send queue中已发送未收到确认的字节大小 队列满了的情况 半连接满 此时会发生syn包被丢弃的现象,因而客户端会重试,性能下降。可能是syn攻击导致 1 2 $ netstat -s | grep -i syn 612 SYNs to LISTEN sockets dropped 全连接满 并发量大,server端accept()调用过慢时出现,可能由于服务器负载、应用程序本身等问题导致。 满了后新的连接直接丢弃或者回复reset包,此行为由内核参数tcp_abort_on_overflow控制(为0直接丢弃,为1回reset包),且内核socket统计信息里面listen overflowed数加1。 1 2 $ netstat -s | grep -i overflow 612 times the listen queue of a socket overflowed 调优上可以适当增大全连接和半连接队列 ...

August 2, 2020 · 2 min · 228 words · erpan

bash中alias不生效

alias不生效 在修改一段shell脚本时,需要将函数里面的kubectl命令替换为kubectl --insecure-skip-tls-verify=True,使得脚本中的所有kubectl命令执行时会自动加上选项--insecure-skip-tls-verify=True。首先想到的就是利用alias别名,示例如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function func1(){ ... kubectl get deployment -n ${NAMESPACE} ${CI_PROJECT_NAME} # 不生效 ... } ... function main(){ ... alias kubectl="kubectl --insecure-skip-tls-verify=True" kubectl get ns # 不生效 ... } 但是用上面方式改来改去kubectl选项不生效。 原因是:alias不能直接在同一个命令解析单元里面定义并使用,必须在定义alias的命令解析单元解析执行完成后,才能生效。 因此,在函数里面使用alias时,可以用下面函数的方式: 1 2 3 4 function main(){ kubectl() { /usr/bin/kubectl --insecure-skip-tls-verify=True "$@"; } kubectl get ns # 此处等同于kubectl加了--insecure-skip-tls-verify=True选项 } 当然也可以把alias放在函数外、脚本的前面。 ...

June 12, 2020 · 1 min · 117 words · erpan

读redis-py客户端源码

前言 看别人的代码也是对自己思维和经验的学习丰富过程 作为一个初学者,之前写过的代码量较少,很少涉及到完整的项目开发,看完redis-py库后,get到其中的 连接保活机制 连接池的实现 开辟buffer存入从socket接收来的数据及buffer管理 熟悉了RESP协议 多进程多线程的情况下,利用锁确保连接池数据结构安全 我读的过程中觉得值得注意的是,_in_use_connections使用集合结构;此连接池不是在初始化时创建好一定数量的tcp连接;其中用了两个互斥锁,一个保护连接池,一个保护多进程的池;较多的连接重连,确保连接可用 执行过程 该库主要有Redis、Connection、ConnectionPool、PythonParse、SocketBuffer几个类,下面大概理了一下redis-py的执行过程 开始使用redis-py客户端 class Redis实例化 可以关注下面几个参数: socket_timeout=None, socket_connect_timeout=None, socket_keepalive=None, socket_keepalive_options=None, connection_pool=None, retry_on_timeout=False, max_connections=None, single_connection_client=False, # 是否单个连接,不用连接池 health_check_interval=0, class ConnectionPool连接池初始化ConnectionPool(),此时尚未创建连接 # 此处定义连接池最大连接数 max_connections = max_connections or 2 ** 31 # fork_safe,在_checkpid()方法中用到,保护临界区的锁。这个锁是在进程id改变时获得的。比如fork出一个子进程后,子进程id和池对象中保存的id不一致,那么子进程中的多个线程都可能会先获取此锁,第一个获得锁的线程将重置此池的数据结构并最终释放锁对象,后续的线程再执行时,pid已于子线程池中的pid熟悉一致,不再做其他操作,在下面也会有提到 self._fork_lock = threading.Lock() # 定义了并初始化已创建连接数、使用中的链接、可用的连接等数据结构 self._lock = threading.Lock() self._created_connections = 0 self._available_connections = [] self._in_use_connections = set() 注意此处_in_use_connections使用了集合存储池中的连接对象,这个与python数据类型时间复杂度有关,可点此参考官网,集合的内部实现与字典极为相似,此集合对象只用到两个操作,add和remove,时间复杂度均为O(1),(有误烦请指正🤡🤡🤡) 初始化Redis-Client状态信息完毕,此时还没有任何连接被创建 假设开始执行 r.set('foo', 'baiqi'),此方法返回r.excute_command()的结果 首先尝试从池中获取一个Connection对象 pool.get_connection(command_name, **options) 获取时得先执行下_checkpid()方法,再执行get_connection() ...

March 6, 2020 · 1 min · 208 words · erpan

Jenkins Pipeline初体验

使用jenkins pipeline共享库,各应用都可以引用共享库方法,更改共享库即可应用到所有使用此库的jenkins-job。我目前没有用到vars目录,但完全能够满足我们日常需求,使用方式上可能较low,直接开始体验。 下面列出了定义的部分方法,以作参考。 共享库目录结构: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 jenkins-pipeline-libraries git:(master) ✗ tree . . ├── jenkins-ci │ └── jenkinsfile-java ├── out │ └── production ├── src │ ├── ops │ │ └── jk │ │ ├── appDetail.groovy │ │ └── tools.groovy │ └── pipeline.gdsl └── vars └── pipelineCfg.groovy 7 directories, 6 files appDetail.groovy文件里面部分函数如下: ...

January 12, 2020 · 6 min · 1266 words · erpan