二进制包
注:推荐用二进制包部署Kubernetes集群,虽手动部署麻烦,但可以学习很多工作原理利于后期维护。
环境
可以使用VMware虚拟机,宿主机必须8G内存以上
• 服务器可以访问外网,有从网上拉取镜像的需求
单Master服务器规划:( 注:部署时候根据具体环境进行IP地址调整即可 )
这里使用3台组建集群,可容忍1台机器故障,当然,你也可以使用5台组建集群
etcd1: 1921683110 etcd2: 1921683112 etcd3: 1921683113
cfssl是一个开源的证书管理工具,使用json文件生成证书,相比openssl更方便使用。
找任意一台服务器 *** 作,这里用Master节点。
创建工作目录:
自签CA:
生成证书:
会生成capem和ca-keypem文件。
创建证书申请文件:
注:上述文件hosts字段中IP为所有etcd节点的集群内部通信IP,一个都不能少!为了方便后期扩容可以多写几个预留的IP。
生成证书:
会生成etcdpem和etcd-keypem文件。
>
Kubernetes(简称K8S) 是Google开源的分布式的容器管理平台,方便我们在服务器集群中管理我们容器化应用。
节点 (Master node and Worker node)
节点通常指的就是服务器,在k8s中有两种节点:管理节点(Master Node)和工作节点(Worker Node)
管理节点(Master Node):负责管理整个k8s集群,一般由3个管理节点组成HA的架构。
工作节点(Worker Node):主要负责运行容器。
命名空间 (Namespace)
k8s命名空间主要用于隔离集群资源、隔离容器等,为集群提供了一种虚拟隔离的策略;默认存在3个名字空间,分别是默认命名空间 default、系统命名空间 kube-system 和 kube-public。
Object
k8s 对象(Object)是一种持久化存储并且用于表示集群状态的实体。k8s 对象其实就是k8s自己的配置协议,总之我们可以通过定义一个object让k8s根据object定义执行一些部署任务、监控任务等等。
POD
Pod是 Kubernetes 部署应用或服务的最小的基本单位。一个Pod 封装多个应用容器(也可以只有一个容器)、存储资源、一个独立的网络 IP 以及管理控制容器运行方式的策略选项。
副本集 (Replica Set,RS)
是一种控制器,负责监控和维护集群中pod的副本(replicas)数,确保pod的副本数是我们期望的样子。
部署 (Deployment)
表示对k8s集群的一次更新 *** 作,是k8s集群中最常用的Object,主要用于部署应用。支持滚动升级。
服务 (service)
是对应用的抽象,也是k8s中的基本 *** 作单元,一个服务背后由多个pod支持,服务通过负载均衡策略将请求转发到容器中。
Ingress
是一种网关服务,可以将k8s服务通过>
无状态应用 & 有状态应用
无状态应用指的是应用在容器中运行时候不会在容器中持久化存储数据,应用容器可以随意创建、销毁;如果一个应用有多个容器实例,对于无状态应用,请求转发给任何一个容器实例都可以正确运行。例如:web应用
有状态应用指的是应用在容器中运行时候需要稳定的持久化存储、稳定的网络标识、固定的pod启动和停止次序。例如:mysql数据库
一、配置:
环境:
CentOS7
VMware
笔者配置了四台虚拟机:
K8S-Master节点: 3GB内存 2核CPU 20GB硬盘空间
K8S-node1节点: 2GB内存 2核CPU 30GB硬盘空间
K8S-node2节点: 2GB内存 2核CPU 30GB硬盘空间
镜像仓库节点: 2GB内存 2核CPU 50GB硬盘空间
二、节点规划:
使用三台虚拟机搭建K8S集群,使用一台虚拟机搭建镜像仓库。
每台虚拟机配置两块网卡,其中一块为“NAT模式”,用于拉取镜像等功能。
另外一块网卡为“仅主机模式”,用于集群节点间的通信。归划如下:
K8s-master节点:
仅主机模式:101010200
NAT模式: 192168200130
K8S-node1节点:
仅主机模式:101010201
NAT模式: 192168200131
K8S-node2节点:
仅主机模式:101010202
NAT模式: 192168200132
镜像仓库节点:
仅主机模式:101010101
NAT模式: 192168200150
三、版本信息
Linux内核版本:
Linux version 3100-862el7x86_64 (builder@kbuilderdevcentosorg)
(gcc version 485 20150623 (Red Hat 485-28) (GCC) )
#1 SMP Fri Apr 20 16:44:24 UTC 2018
K8s集群版本为1150版本:
四、基于StatefulSet与PV/PVC的MySql持久化存储实验
1 在每个节点安装nfs服务
在“镜像仓库”节点,执行以下命令:
yum install -y nfs-common nfs-utils rpcbind
在k8s集群,执行以下命令:
yum install -y nfs-utils rpcbind
2 在“镜像仓库”节点下,配置nfs服务器
mkdir /nfs_mysql
Chmod 777 /nfs_mysql/
(在测试环境中,为了不考虑用户属性,暂时赋予777权限,但在生产环境不推荐这样做)
Chown nfsnobody /nfs_mysql/
echo “/nfs_mysql (rw,no_root_squash,no_all_squash,sync)” >> /etc/exports
cat /etc/exports
/nfs_mysql (rw,no_root_squash,no_all_squash,sync)
systemctl start rpcbind
systemctl start nfs
3 测试nfs服务是否可用
mkdir /test
showmount -e 101010101
可见/nfs_mysql 已暴露于共享目录,接下来测试挂载是否可用:
在master节点下执行:
mount -t nfs 101010101:/nfs_mysql /test/
echo "hello-world">>/test/1txt
在镜像仓库节点下查看1txt是否存在,若存在则挂载成功:
可见nfs服务可以正常使用,接下来删除test目录和1txt
在镜像仓库下:
[root@hub nfs_mysql]# rm -f 1txt
在Master节点下:
[root@k8s-master ~]# umount /test/
[root@k8s-master ~]# rm -rf /test/
同理,依照以上步骤同时创建:(提供多个mysql副本进行挂载)
nfs_mysql1
nfs_mysql2
完成后需要重启nfs服务
systemctl restart rpcbind
systemctl restart nfs
最终效果:
4 将nfs封装成pv
创建mysql_test文件夹,将yaml文件统一保存在此目录下
mkdir mysql_test
cd mysql_test
vim mysql-pvyml
mysql-pvyml配置如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs_mysql
server: 101010101
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv1
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs_mysql1
server: 101010101
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv2
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs_mysql2
server: 101010101
注意:
在k8s集群15版本中recycle回收策略已被删除,只能用retain策略或者Delete策略。这里我们使用 persistentVolumeReclaimPolicy: Retain
执行命令:
kubectl create -f mysql-pvyml
kubectl get pv
如图所示,即为Pv创建成功。
5 部署MySQL,在mysql_test目录下编写mysqlyml,配置文件如下
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
name: mysql
clusterIP: None
selector:
app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
serviceName: "mysql"
replicas: 3
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:56
env:
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-persistent-storage
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs"
resources:
requests:
storage: 1Gi
执行以下命令,部署mysql服务:
kubectl create -f mysqlyml
如图可知,mysql按StatefulSet依次创建了mysql-0 mysql-1 mysql-2
查看各个Pod部在哪个节点:
6 通过创建临时容器,使用MySQL客户端发送测试请求给MySQL master节点
注意:
主机名为mysql-0mysql;跨命名空间的话,主机名请使用mysql-0mysql [NAMESPACE_NAME]如果没有指定命名空间,默认为default,即 mysql-0mysql default。
这里笔者打算关闭node2节点来模拟node2宕机,来测试是否实现数据的持久化存储,
所以我们向node2上的mysql1写入数据。
执行以下命令,访问mysql1:
kubectl run mysql-client --image=mysql:56 -it --rm --restart=Never -- mysql -h mysql-1mysqldefault -p password
创建数据库demo,并向messages表中写入hello-world
CREATE DATABASE demo;
CREATE TABLE demomessages (message VARCHAR(250));
INSERT INTO demomessages VALUES ('hello-world');
如图所示
接下来我们来关闭k8s-node2虚拟机,模拟宕机
查看nodes的运行状态,可知node2的状态已转变为NotReady
一段时间后,k8s将Pod MySql -1迁移到节点k8s-node1
由于时间过长,笔者把三个Pod都删除重启后,验证数据:
MySQL服务恢复,数据完好无损!
kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。
这个工具能通过两条指令完成一个kubernetes集群的部署:
在开始之前,部署Kubernetes集群机器需要满足以下几个条件:
31 安装相关包和keepalived
32配置master节点
master1节点配置
master2节点配置
33 启动和检查
在两台master节点都执行
启动后查看master1的网卡信息
41 安装
42 配置
两台master节点的配置均相同,配置中声明了后端代理的两个master节点服务器,指定了haproxy运行的端口为16443等,因此16443端口为集群的入口
43 启动和检查
两台master都启动
检查端口
Kubernetes默认CRI(容器运行时)为Docker,因此先安装Docker。
51 安装Docker
52 添加阿里云YUM软件源
53 安装kubeadm,kubelet和kubectl
由于版本更新频繁,这里指定版本号部署:
61 创建kubeadm配置文件
在具有vip的master上 *** 作,这里为master1
62 在master1节点执行
按照提示保存以下内容,一会要使用(kubeadm init中的回显内容):
按照提示配置环境变量,使用kubectl工具:
查看集群状态
创建kube-flannelyml,在master1上执行
安装flannel网络
检查
81 复制密钥及相关文件
从master1复制密钥及相关文件到master2
82 master2加入集群
执行在master1上init后输出的join命令,需要带上参数--control-plane表示把master控制节点加入集群(之前kubeadm init回显内容)
检查状态(master1上执行)
在node1上执行
向集群添加新节点,执行在kubeadm init输出的kubeadm join命令(之前kubeadm init回显内容,注意不加--control-plane):
集群网络重新安装,因为添加了新的node节点(在master1上执行)
检查状态(在master1上执行)
在Kubernetes集群中创建一个pod,验证是否正常运行:
访问地址:>自从我们的kubernetes集群部署到生产环境后,将流量从原有的服务器上切过来之后,部分节点出现挂载目录容量爆满的情况。
运维的同事报给我们之后,我们首先想到的是节点镜像过多,于是我们提供一个命令用于清理当前节点上无用的、报错的、镜像和docker资源文件
docker system prune 命令可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及dangling镜像(即无tag的镜像)
docker system prune -a 命令清理得更加彻底,可以将没有容器使用Docker镜像都删掉。
待运维执行之后,目录存储资源释放了一些,我们本以为这就告一段落了。然而,事与愿违,没过多久,再次容量报警。。。
我们开始重视起来,开始检视节点上工作的容器,发现在日志爆炸的节点上运行了定时任务,开发人员将定时任务的日志输出到控制台,于是我们回到节点docker的工作目录,通过 du -sh 方式查看每个文件夹大小,发现docker目录下containers目录占用空间巨大,进去看原来是每个运行的容器存放日志的目录,我们找出占用空间最大的日志目录,发现容器日志特别的大
我们可使用如下命令查看各个日志的文件大小
ls -lh $(find /var/lib/docker/containers/ -name -jsonlog)
那我们如何清理日志呢,如果docker容器正在运行,那么使用rm -rf 方式删除日志后,通过df -h会发现磁盘空间并没有释放
原因:在Linux或者Unix系统中,通过rm或者文件管理器删除文件将会从文件系统的目录结构上解除链接(unlink)然而如果文件是被打开的(有一个进程正在使用),那么进程将仍然可以读取该文件,磁盘空间也一直被占用
我们通过 cat /dev/null > -jsonlog 来清理相应的日志,然后重启
systemctl daemon-reload
systemctl restart docker
然而,我思考,不能每次满的时候找运维清理日志啊,这多麻烦,难道docker没有相应的机制应付输出到控制台的日志吗?答案是:当然不会
在新版的docker中我们可以通过设置 vim /etc/docker/daemonjson 来限制docker的日志量
"log-driver":"json-file","log-opts":{ "max-size" :"200m","max-file":"5"}
顾名思义max-size就是每个日志文件大小,max-file是最多生成的文件数,如上我设置成功后,每个容器运行的日志最多有五份每份200M大小,这样就基本限制了容器的日志大小。
然后你觉得结束了吗??并不!!
容器日志我们是限制完了,本以为高枕无忧,不用担心出现日志爆满的情况了,但是事与愿违,过几天硬盘容量又满了。。。
我们究其原因,发现在docker的运行目录下overlay这个文件夹里存放着所有的容器挂载目录,也就是容器的系统文件在这里放着,在容器中跑着的服务产生日志很可能并不是输出到控制台,而是保存到本地,容器内的日志文件也是会占用磁盘空间的,这就让我们犯愁了,这个不好限制开发团队不存日志或者规定团队存放目录啊,对于一个成熟的容器平台来说,海纳百川那是必须的~
于是我们打起了kubelet的主意
在 k8s中文社区中有详细的限制方法 那具体做法呢,其实就是为节点加上驱逐策略,当cpu或者内存或者硬盘空间不满足要求时,自动驱逐一些消耗资源大的容器,保证节点稳定性。
里面主要是有以下几个关键驱逐信号
上面的每个信号都支持整数值或者百分比。百分比的分母部分就是各个信号的总量。kubelet 支持两种文件系统分区。
nodefs:保存 kubelet 的卷和守护进程日志等。
imagefs:在容器运行时,用于保存镜像以及可写入层。
imagefs 是可选的。Kubelet 能够利用 cAdvisor 自动发现这些文件系统。Kubelet 不关注其他的文件系统。所有其他类型的配置,例如保存在独立文件系统的卷和日志,都不被支持。
因为磁盘压力已经被驱逐策略接管,因此未来将会停止对现有 垃圾收集 方式的支持。
具体的内容大家可以详细去看看社区里的介绍,我这里就不再赘述了,我这边献上我的驱逐方案~
执行vim /etc/systemd/system/kubeletserviced/10-kubeadmconf
在里面插入
Environment="KUBELET_OTHER_ARGS=
--eviction-hard=memoryavailable<2Gi,nodefsavailable<5Gi,imagefsavailable<5Gi
--eviction-minimum-reclaim=memoryavailable=500Mi,nodefsavailable=5Gi,imagefsavailable=5Gi
--node-status-update-frequency=10s
--eviction-pressure-transition-period=30s"
解读:内存小于2G驱逐,root目录磁盘空间小于5G驱逐,镜像目录磁盘空间小于5G驱逐,节点检测为每10秒一次,在跳出压力状态之前要等待的时间为30秒。
在某些场景下,驱逐 Pod 可能只回收了很少的资源。这就导致了 kubelet 反复触发驱逐阈值。另外回收资源例如磁盘资源,是需要消耗时间的。
要缓和这种状况,Kubelet 能够对每种资源定义 minimum-reclaim。kubelet 一旦发现了资源压力,就会试着回收至少 minimum-reclaim 的资源,使得资源消耗量回到期望范围。
也就是说当内存触发驱逐时,kubelet至少要让内存有25G,当root和镜像磁盘空间发生驱逐时,kubelet至少要让磁盘有10G的空间。
那驱逐的规则是什么呢,对什么样的容器做驱逐呢?这个我们下回分解哈。
那总的来说,若要解决节点镜像存储报警,我们可以从三个方面入手
1容器:通过docker限制容器日志大小
2k8s:通过kubelet来驱逐过大的容器
3跟开发人员沟通,精简容器,不让内存泄漏,不随意使用资源(很难啦~~~)
祝各位新春快乐~
由于Master和Minion为同性质工作,后来重启K8s集群的时候,有一个CoreDNS被分配到了Minion主机上。
当时也没太留意,只是在启动一个服务的时候,发现只能访问外网的IP,不能通过域名方面,感觉好奇怪。
查看了下/etc/resolvconf里面的配置啥的也都正确,按理来说不应该出现问题。
由于用的是Centos7 *** 作系统,就把Firewalld给关闭了尝试下,就可以正常通过域名访问,由此可见还是Firewalld给拦截了。
考虑到Firewalld的拦截,很奇怪,因为我们的pod是访问外网,不是外面访问它,应该不是开放端口的问题,最后查看了下宿主机上的NAT路由转发,结果发现,Master上的处于开启状态,Minion上的处于关闭状态,怪不得无法访问外网的域名服务器进行域名的正常解析,开启后,一切正常。
k8s是什么
Kubernetes 是一个可移植的,可扩展的开源容器编排平台,用于管理容器化的工作负载和服务,方便了声明式配置和自动化。它拥有一个庞大且快速增长的生态系统。Kubernetes 的服务,支持和工具广泛可用。
为什么现在流行使用容器
早期: 在物理服务器上面部署应用程序存在资源分配问题,因为其不能在物理服务器中的应用程序定义资源边界,导致应用程序资源利用不足而无法扩展
后来: 为了解决该问题,引入了虚拟化技术, 虚拟化技术是指允许你在单个物理服务器的 CPU 上运行多个虚拟机,可以让多个应用程序在虚拟机之间进行隔离,具有一定的安全性, 每一个虚拟机就是一台完整的计算机, 在虚拟化硬件之上运行所有组件
现在: 多数在物理服务器上面部署应用程序都是采kubectl用容器的方式,容器类似于虚拟机,它们都具有自己的文件系统、CPU、内存、进程空间等, 且由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。基于此特点被企业大范围使用
为什么需要使用k8s容器
若出现这样一个环境: 在生产环境中如果一个容器发生故障,则我们需要手动去启动另外一个容器,这样的 *** 作是对我们的管理员来说是不太方便的, 若一个容器出现故障,另一个容器可以自动启动容器接管故障的容器,这样是最好的
k8s就可以实现该效果,Kubernetes 提供了一个可d性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移、部署模式等。
k8s功能: 服务发现和负载均衡, 存储编排, 自动部署和回滚, 自动完成装箱计算, 自我修复, 密钥与配置管理
名词解释
secret
Secret有三种类型:
Service Account:用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的目录中;/run/secrets/kubernetesio/serviceaccountOpaque:base64编码格式的Secret,用来存储密码、密钥等;kubernetesio/dockerconfigjson:用来存储私有docker registry的认证信息。k8s的组成
k8s是由组件,API,对象等组成
包含所有相互关联组件的 Kubernetes 集群图如下:
组件
控制平面组件kube-apiserver: 为k8s的api服务器,公开了所有Kubernetes API, 其他所有组件都必须通过它提供的API来 *** 作资源数据保证集群状态访问的安全隔离集群状态访问的方式和后端存储实现的方式:API Server是状态访问的方式,不会因为后端存储技术etcd的改变而改变。etcd: 为k8s的键值数据库,保存了k8s所有集群数据的后台数据库。kube-scheduler: 收集和分析当前Kubernetes集群中所有Node节点的资源(内存、CPU)负载情况,然后依此分发新建的Pod到Kubernetes集群中可用的节点。 kube-controller-manager: 在主节点上运行 控制器 的组件。cloud-controller-manager: 云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件Node 组件kubelet: 一个在集群中每个节点(node)上运行的代理。 它保证容器(containers)都 运行在 Pod 中。kube-proxy: kube-proxy是集群中每个节点上运行的网络代理,维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。容器运行时: 负责运行容器的软件。插件(Addons)DNS: 集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。Web 界面(仪表盘): Dashboard 是Kubernetes 集群的通用的、基于 Web 的用户界面。容器资源监控: 容器资源监控 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中,并提供用于浏览这些数据的界面。集群层面日志: 集群层面日志 机制负责将容器的日志数据 保存到一个集中的日志存储中,该存储能够提供搜索和浏览接口。API
Kubernetes 控制面 的核心是 API 服务器。 API 服务器负责提供 >
对象
Kubernetes对象是Kubernetes系统中的持久实体。Kubernetes使用这些实体来表示集群的状态
具体来说,他们可以描述:
容器化应用正在运行(以及在哪些节点上)这些应用可用的资源关于这些应用如何运行的策略,如重新策略,升级和容错Kubernetes 架构
Kubernetes 架构由节点,控制面到节点通信, 控制器, 云控制器管理器组成
master 流程图
Kubecfg将特定的请求,比如创建Pod,发送给Kubernetes Client。Kubernetes Client将请求发送给API server。API Server根据请求的类型,比如创建Pod时storage类型是pods,然后依此选择何种REST Storage API对请求作出处理。REST Storage API对的请求作相应的处理。将处理的结果存入高可用键值存储系统Etcd中。在API Server响应Kubecfg的请求后,Scheduler会根据Kubernetes Client获取集群中运行Pod及Minion/Node信息。依据从Kubernetes Client获取的信息,Scheduler将未分发的Pod分发到可用的Minion/Node节点上。节点
节点可以是一个虚拟机或者物理机器,取决于所在的集群配置。 每个节点包含运行 Pods 所需的服务, 这些 Pods 由 控制面 负责管理
节点上的组件包括 kubelet、 容器运行时以及 kube-proxy。
节点状态
可以使用 kubectl 来查看节点状态和其他细节信息:
kubectl describe node <�节点名称>
一个节点包含以下信息:
地址HostName:由节点的内核设置。可以通过 kubelet 的 —hostname-override 参数覆盖。ExternalIP:通常是节点的可外部路由(从集群外可访问)的 IP 地址。InternalIP:通常是节点的仅可在集群内部路由的 IP 地址。状况(conditions 字段描述了所有 Running 节点的状态)Ready 如节点是健康的并已经准备好接收 Pod 则为 True;False 表示节点不健康而且不能接收 Pod;Unknown 表示节点控制器在最近 node-monitor-grace-period 期间(默认 40 秒)没有收到节点的消息DiskPressure为True则表示节点的空闲空间不足以用于添加新 Pod, 否则为 FalseMemoryPressure为True则表示节点存在内存压力,即节点内存可用量低,否则为 FalsePIDPressure为True则表示节点存在进程压力,即节点上进程过多;否则为 FalseNetworkUnavailable为True则表示节点网络配置不正确;否则为 False容量与可分配描述节点上的可用资源:CPU、内存和可以调度到节点上的 Pod 的个数上限。信息关于节点的一般性信息,例如内核版本、Kubernetes 版本(kubelet 和 kube-proxy 版本)、 Docker 版本(如果使用了)和 *** 作系统名称。这些信息由 kubelet 从节点上搜集而来。控制面到节点通信
节点到控制面apiserver在安全的 )上监听远程连接请求以客户端证书的形式将客户端凭据提供给 kubelet控制面到节点API 服务器到 kubelet连接用于获取 Pod 日志挂接(通过 kubectl)到运行中的 Pod提供 kubelet 的端口转发功能。(注: 在连接状态下, 默认apiserver 不检查 kubelet 的服务证书。容易受到中间人攻击,不安全)apiserver 到节点、Pod 和服务SSH 隧道(目前已经废弃)产生原因: 若无服务证书, 又要求避免在非受信网络或公共网络上进行连接,则可以在apiserver 和 kubelet 之间使用ssh隧道Kubernetes 支持使用 SSH 隧道来保护从控制面到节点的通信路径。Konnectivity 服务为ssh隧道的替代品, Konnectivity 服务提供 TCP 层的代理,以便支持从控制面到集群的通信。控制器
在 Kubernetes 中,控制器通过监控集群 的公共状态,并致力于将当前状态转变为期望的状态。
举个例子: 当前室内温度为20度, 我们通过调节遥控器,使其温度上升至24度, 这20度到24度的变化即为让其从当前状态接近期望状态。
控制器模式分为直接控制和通过API服务器来控制
云控制器管理器
云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件。 云控制器管理器允许您链接聚合到云提供商的应用编程接口中, 并分离出相互作用的组件与您的集群交互的组件。
云控制器管理器中的控制器包括:
节点控制器节点控制器负责在云基础设施中创建了新服务器时为之 创建 节点(Node)对象。 节点控制器从云提供商获取当前租户中主机的信息。执行功能:针对控制器通过云平台驱动的 API 所发现的每个服务器初始化一个 Node 对象利用特定云平台的信息为 Node 对象添加注解和标签获取节点的网络地址和主机名检查节点的健康状况。路由控制器Route 控制器负责适当地配置云平台中的路由,以便 Kubernetes 集群中不同节点上的 容器之间可以相互通信。服务控制器服务(Service)与受控的负载均衡器、 IP 地址、网络包过滤、目标健康检查等云基础设施组件集成。 服务控制器与云驱动的 API 交互,以配置负载均衡器和其他基础设施组件。Kubernetes 安全性
云原生安全
云原生安全4个C: 云(Cloud)、集群(Cluster)、容器(Container)和代码(Code)
云原生安全模型的每一层都是基于下一个最外层,代码层受益于强大的基础安全层(云、集群、容器)。我们无法通过在代码层解决安全问题来为基础层中糟糕的安全标准提供保护。
基础设施安全
Kubetnetes 基础架构关注领域
建议
通过网络访问 API 服务(控制平面)
所有对 Kubernetes 控制平面的访问不允许在 Internet 上公开,同时应由网络访问控制列表控制,该列表包含管理集群所需的 IP 地址集。
通过网络访问 Node(节点)
节点应配置为 仅能 从控制平面上通过指定端口来接受(通过网络访问控制列表)连接,以及接受 NodePort 和 LoadBalancer 类型的 Kubernetes 服务连接。如果可能的话,这些节点不应完全暴露在公共互联网上。
Kubernetes 云访问提供商的 API
每个云提供商都需要向 Kubernetes 控制平面和节点授予不同的权限集。为集群提供云提供商访问权限时,最好遵循对需要管理的资源的最小特权原则。Kops 文档提供有关 IAM 策略和角色的信息。
访问 etcd
对 etcd(Kubernetes 的数据存储)的访问应仅限于控制平面。根据配置情况,你应该尝试通过 TLS 来使用 etcd。更多信息可以在 etcd 文档中找到。
etcd 加密
在所有可能的情况下,最好对所有驱动器进行静态数据加密,但是由于 etcd 拥有整个集群的状态(包括机密信息),因此其磁盘更应该进行静态数据加密。
集群组件安全
运行的应用程序的安全性关注领域访问控制授权(访问 Kubernetes API)认证方式应用程序 Secret 管理 (并在 etcd 中对其进行静态数据加密)Pod 安全策略服务质量(和集群资源管理)网络策略Kubernetes Ingress 的 TLS 支持容器安全
容器安全性关注领域容器搭建配置(配置不当,危险挂载, 特权用户)容器服务自身缺陷Linux内核漏洞镜像签名和执行代码安全
代码安全关注领域仅通过 TLS 访问(流量加密)限制通信端口范围第三方依赖性安全静态代码分析动态探测攻击(黑盒)Kubernetes架构常见问题
Kubernetes ATTACK 矩阵
信息泄露
云账号AK泄露
API凭证(即阿里云AccessKey)是用户访问内部资源最重要的身份凭证。用户调用API时的通信加密和身份认证会使用API凭证
API凭证是云上用户调用云服务API、访问云上资源的唯一身份凭证。
API凭证相当于登录密码,用于程序方式调用云服务API
k8s configfile泄露
kubeconfig文件所在的位置:
$HOME/kube/config
Kubeconfig文件包含有关Kubernetes集群的详细信息,包括它们的位置和凭据。
云厂商会给用户提供该文件,以便于用户可以通过kubectl对集群进行管理 如果攻击者能够访问到此文件(如办公网员工机器入侵、泄露到Github的代码等),就可以直接通过API Server接管K8s集群,带来风险隐患。
Master节点SSH登录泄露
常见的容器集群管理方式是通过登录Master节点或运维跳板机,然后再通过kubectl命令工具来控制k8s。
云服务器提供了通过ssh登陆的形式进行登陆master节点
若Master节点SSH连接地址泄露,攻击者可对ssh登陆进行爆破,从而登陆上ssh,控制集群
容器组件未鉴权服务
Kubernetes架构下常见的开放服务指纹如下:
kube-apiserver: 6443, 8080kubectl proxy: 8080, 8081kubelet: 10250, 10255, 4149dashboard: 30000docker api: 2375etcd: 2379, 2380kube-controller-manager: 10252kube-proxy: 10256, 31442kube-scheduler: 10251weave: 6781, 6782, 6783kubeflow-dashboard: 8080注:前六个重点关注: 一旦被控制可以直接获取相应容器、相应节点、集群权限的服务
了解各个组件被攻击时所造成的影响
组件分工图:
假如用户想在集群里面新建一个容器集合单元, 流程如下:
用户与 kubectl进行交互,提出需求(例: kubectl create -f podyaml)kubectl 会读取 ~/kube/config 配置,并与 apiserver 进行交互,协议:apiserver 会协同 ETCD, kube-controller-manager, scheduler 等组件准备下发新建容器的配置给到节点,协议:apiserver 与 kubelet 进行交互,告知其容器创建的需求,协议:;kubelet 与Docker等容器引擎进行交互,创建容器,协议:容器已然在集群节点上创建成功攻击apiserver
apiserver介绍:
在Kubernetes中,对于未鉴权对apiserver, 能访问到 apiserver 一般情况下就能获取了集群的权限
在攻击者眼中Kubernetes APIServer
容器编排K8S总控组件pods, services, secrets, serviceaccounts, bindings, componentstatuses, configmaps,endpoints, events, limitranges, namespaces, nodes, persistentvolumeclaims,persistentvolumes, podtemplates, replicationcontrollers, resourcequotas …可控以上所有k8s资源可获取几乎所有容器的交互式shell利用一定技巧可获取所有容器母机的交互式shell默认情况下apiserver都有鉴权:
未鉴权配置如下:
对于这类的未鉴权的设置来说,访问到 apiserver 一般情况下就获取了集群的权限:
如何通过apiserver来进行渗透,可参考:>
攻击kubelet
每一个Node节点都有一个kubelet(每个节点上运行的代理)服务,kubelet监听了10250,10248,10255等端口。
10250端口,是kubelet与apiserver进行通信对主要端口, 通过该端口,kubelet可以知道当前应该处理的任务该端口在最新版Kubernetes是有鉴权的, 但在开启了接受匿名请求的情况下,不带鉴权信息的请求也可以使用10250提供的能力, 在Kubernetes早期,很多挖矿木马基于该端口进行传播
在配置文件中,若进行如下配置,则可能存在未授权访问漏洞
/var/bin/kubulet/config/yaml
若10250端口存在未授权访问漏洞,我们可以直接访问/pods进行查看
根据在pods中获取的信息,我们可以在容器中执行命令
curl -Gks >
上述命令得到websocket地址,连接websocket得到命令结果:
使用wscat工具连接websocket
wscat -c “>
即可得到我们执行命令的结果
获取token
/var/run/secrets/kubernetesio/serviceaccount
然后即可访问kube-api server,获取集群权限
curl -ks -H "Authorization: Bearer \ ttps://master:6443/api/v1/namespaces/{namespace}/secrets
"
攻击kubelet总体步骤如下:
访问pods获取信息获取namespace、podsname、containername执行exec获取token/var/run/secrets/kubernetesio/serviceaccount利用Token访问API Server进行对pods *** 作。攻击dashboard
dashboard登陆链接如下:
>
dashboard界面如下:
dashboard是Kubernetes官方推出的控制Kubernetes的图形化界面在Kubernetes配置不当导致dashboard未授权访问漏洞的情况下,通过dashboard我们可以控制整个集群。
默认情况下, dashboard是需要进行鉴权 *** 作的,当用户开启了enable-skip-login时可以在登录界面点击Skip跳过登录进入dashboard
通过skip登陆的dashboard默认是没有 *** 作集群的权限,因为Kubernetes使用RBAC(Role-based access control)机制进行身份认证和权限管理,不同的serviceaccount拥有不同的集群权限。
但有些开发者为了方便或者在测试环境中会为Kubernetes-dashboard绑定cluster-admin这个ClusterRole(cluster-admin拥有管理集群的最高权限)
为Kubernetes-dashboard绑定cluster-admin 设置如下:
新建dashboard-adminyaml内容apiVersion: rbacauthorizationk8sio/v1kind: ClusterRoleBindingmetadata: name: kubernetes-dashboardroleRef: apiGroup: rbacauthorizationk8sio kind: ClusterRole name: cluster-adminsubjects : kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboardkubectl create -f dashboard-adminyaml后通过skip登陆dashboard便有了管理集群的权限
创建Pod控制node节点,该pod主要是将宿主机根目录挂载到容器tmp目录下。
新建一个Pod如下:
通过该容器的tmp目录管理node节点的文件
攻击etcd
Kubernetes默认使用了etcd v3来存储数据, 若能na
etcd对内暴露2379端口,本地127001可免认证访问 其他地址要带—endpoint参数和cert进行认证。
未授权访问流程:
检查是否正常链接etcdctl endpoint health读取service account tokenetcdctl get / --prefix --keys-only | grep /secrets/kube-system/clusterrole通过token认访问API-Server端口6443,接管集群:kubectl --insecure-skip-tls-verify -s攻击docker remote api(Docker daemon公网暴露)
2375是docker远程 *** 控的默认端口,通过这个端口可以直接对远程的docker 守护进程进行 *** 作。Docker 守护进程默认监听2375端口且未鉴权
当机器以方式启动daemon时,可以在外部机器对该机器的docker daemon进行直接 *** 作:
docker daemon -H=0000:2375
之后依次执行systemctl daemon-reload、systemctl restart docker
外部主机使用 即可 *** 作暴露2375端口的主机
-H
因此当你有访问到目标Docker API 的网络能力或主机能力的时候,你就拥有了控制当前服务器的能力。我们可以利用Docker API在远程主机上创建一个特权容器,并且挂载主机根目录到容器
检测目标是否存在docker api未授权访问漏洞的方式也很简单,访问>
攻击kubectl proxy
二次开发所产生的问题
管理Kubernetes无论是使用 kubectl 或 Kubernetes dashboard 的UI功能,其实都是间接在和 APIServer 做交互
如果有需求对k8s进行二次开发的话,大部分的开发功能请求了 APIServer 的 Rest API 从而使功能实现的。
例如:
给用户销毁自己POD的能力DELETE类似于这样去调用apiserver, 攻击者若修改namespace、pod和容器名, 那么即可造成越权
推荐工具
Kube-Hunter扫描漏洞
kube-hunter是一款用于寻找Kubernetes集群中的安全漏洞扫描器
下载地址: >
CDK(强推)
CDK是一款为容器环境定制的渗透测试工具,在已攻陷的容器内部提供零依赖的常用命令及PoC/EXP。集成Docker/K8s场景特有的 逃逸、横向移动、持久化利用方式,插件化管理。
下载地址: >
参考链接
>国际惯例,以helloworld工程为例,从restful工程开发,打包,docker镜像制作。Ingress+service+pod的方式发布服务的例子。
由于没有远程镜像服务器,采用本地镜像的方式加载服务。
镜像设置成本地策略:
spec:
containers:
- name:
image:
imagePullPolicy: IfNotPresent
在开发之前,我们先了解一下k8s的命名空间,命名空间可以帮助我们管理和隔离pod等组件,增加权限租户管理等 *** 作。
以本文为例,创建一个名为dwayne的命名空间,新建一个create-namespaceyaml文件,内容如下
apiVersion: v1
kind: Namespace
metadata:
name: dwayne
labels:
name: dwayne
执行kubectl apply -f create-namespaceyaml
设置成默认命名空间
kubectl config set-context $(kubectl config current-context) --namespace=dwayne
工程很简单,一个get接口/helloworld,返回json
package comexampledemo;
import orgspringframeworkwebbindannotationRequestMapping;
import orgspringframeworkwebbindannotationRequestMethod;
import orgspringframeworkwebbindannotationRestController;
@RestController
public class AController {
@RequestMapping(value = "helloworld", method = RequestMethodGET)
public Response hello() {
Response res = new Response();
ressetMsg("helloworld");
return res;
}
}
打包成jar包,demo-001-SNAPSHOTjar
在节点服务器(所有节点)上新建目录作为工作目录,将jar包上传到节点服务器,并在同一目录下编辑Dockerfile:
FROM java:8
VOLUME /tmp
ADD demo-001-SNAPSHOTjar /demo-001-SNAPSHOTjar
ENTRYPOINT ["java","-Djavasecurityegd=file:/dev//urandom","-jar","/demo-001-SNAPSHOTjar"]
保存Dockerfile,退出,执行docker build命令
docker build -t demo-hello-world-master
生成demo-hello-world-master,先打个tag,方便后续使用:
docker tag demo-hello-world-master dw/demo-hello-world-master
这里部署两个helloworld服务实例(replicas: 2)。部署在dwayne命名空间,yaml文件内容如下:
apiVersion: v1
kind: Service
metadata:
name: helloworld-master
namespace: dwayne
spec:
type: NodePort
selector:
app: helloworld
release: master
ports:
- port: 7071
targetPort: 17001
nodePort: 30002
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-master
namespace: dwayne
spec:
replicas: 2
selector:
matchLabels:
app: helloworld
release: master
template:
metadata:
labels:
app: helloworld
release: master
spec:
containers:
- name: demo-hello-world-master
image: dw/demo-hello-world-master
imagePullPolicy: IfNotPresent
ports:
- name: >
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)