Hero Image
Intel SGX

概述 https://www.intel.com/content/www/us/en/developer/tools/software-guard-extensions/overview.html https://blog.quarkslab.com/overview-of-intel-sgx-part-1-sgx-internals.html Intel Software Guard Extensions(Intel SGX) 保护选定的代码和数据不被泄露的和修改。开发者可以把应用程序划分到CPU强化的enclave中或者内存中可执行的保护区域,即使在受攻击的平台中也可以提高安全性。使用这种新的应用层可信执行环境,开发者能够启用身份和记录隐私,安全浏览和数据保护(DRM)或者任何需要安全存储机密或者保护数据的高保障安全应用场景中。 机密性和完整性, 即使在OS、BIOS、VMM或者SMM层存在特权恶意软件的情况下也可以保证安全。 低学习曲线,和父应用程序类似的OS编程模型,并且在主CPU上执行 远程认证 远程部分能够认证一个应用程序的enclave的身份,并且安全的将密钥、凭据和敏感数据提供为enclave 最小的可能攻击面, CPU边界成为攻击面外围,所有的数据、内存、外围之外的IO都是加密的。 最小攻击面的硬件辅助可信执行环境。 intel SGX保护的应用程序 Intel SGX应用程序由两个部分组成: 不可信代码和可信Enclave. 开发者可以创建一对多的可信enclave用来支持分布式体系结构。 常用应用有密钥,专有算法,生物识别数据和CSR生成等。 程序运行时, Intel SGX指令在一个特定的保护内存区域中创建和执行enclave,该区域有由开发者定义的受限入口和出口函数。能够防止数据泄露。在CPU范围中的enclave和数据运行在一个clean的环境中, enclave数据写入到磁盘会被加密,并且校验其完整性。 上图中的流程 Application由可信和不可信部分构成 App运行和创建evclave, enclave放入到可信内存中 可信函数被调用,执行会转换到enclave中 enclave可以访问所有进程数据,外部要访问enclave数据被禁止 可信函数返回enclave数据 对enclave有未授权的访问和内存侦听是有可能的 认证Enclave和加密数据 当前,ODM(原始设备制造上)和ISV(独立软件提供商) 通常在制造时或通过无法以机密方式证明XXX。 Intel SGX使用enclave之间本地认证或者第三方远程认证的方式来保证应用程序没有受到破坏。 应用程序受保护的部分会加载到一个Enclave,它的代码和数据都会收到监测。会发送一个请求到远端服务器,用来验证这个Enclave是否是可靠的Intel 处理器生成的。 如果认证了Enclave的身份,远端就会信任Enclave并安全的提供密钥,凭证和数据. Intel SGX 包括一个生成CPU和Enclave特定“密封密钥”的指令。密钥能够用来安全的存储和取回可鞥你需要保存在磁盘中的敏感信息。 Intel SGX 实现新的安全模型 Intel SGX 是在很多公司、大学的安全研究人员以及政府安全机构的支持下创建的,上百家ISV与Intel合作,使用Intel SGX来保护关键任务应用程序。 Set up SGX develop environment Install SGX driver Install SGX SDK Install SGX PSW 直接按照官方文档依次安装上述3个组件 Sample Enclave Demo 1############# SGX SDK Setting 2# 编译平台和模式配置 3 4############## APP Setting 5# 主要是指定 6# App_Cpp_Files 需要编译的cpp文件 7# App_Include_Paths 包含目录 8# App_Cpp_Objects 输出 9 10############### Enclave setting 11# Enclave_Cpp_Files 12# Enclave_Include_Paths 13# Enclave_Cpp_Objects 14 15# eld 编译配置 16################ App Object 17################ Enclave Object Enclave 相关开发流程 使用edl文件定义不可信app和enclave之间的接口 实现app和enclave函数 编译 app 和enclave。编译中 Edger8r生成可信和不可信的代理/桥函数,Enclave签名工具生成enclave的metadata和签名 在模拟和硬件模式下运行和调试app,详细看debug enclave的内容 准备发布app 和enclave 编写Enclave函数 在app角度,使用不可信代理函数调用enclave函数(ECALL)跟调用其他函数没有区别。Enclave函数只是有些显示的c/c++函数。

Hero Image
iptables

防火墙 逻辑上,防火墙可以分为主机防火墙和网络防火墙。主机防火墙针对单个主机进行防护;网络防火墙往往处于网络入口或者边缘,针对网络入口进行防护,服务于防火墙背后的本地局域网。 物理上,防火墙可以分为硬件防火墙和软件防火墙。硬件防火墙在硬件级别实现部分防火墙功能,另一部分基于软件实现,性能高,成本高;软件防火墙应用软件处理逻辑运行于通用计算平台之上的防火墙,性能低,成本低。 iptables/netfilter iptables其实不是真正的防火墙,可以把它理解陈伟一个客户端代理,用户通过iptables这个代理,将用户的安全设定执行到对应的“安全框架”中,这个“安全框架”才是真正的防火墙, 这个框架的名字叫netfilter。 netfilter才是防火墙真正的安全框架,位于内核空间。iptables其实是一个命令行工具, 位于用户空间,我们使用iptables这个工具操作真正的框架netfilter。 netfilter/iptables组成的linux平台下的包过滤防火墙,是免费的,具有以下功能: 网络地址转换(NAT, Network Address Translate) 数据包内容修改 数据包过滤(防火墙功能) 我们虽然可以使用service iptables start启用iptables服务,但其实准确的来说iptables并没有一个守护进程,不能算是真正意义上的服务,而算是内核提供的功能。 iptables是按照规则(rules)来办事的, rules 就是我们预定义的条件。规则一般定义为“如果数据包头符合这样的条件,就这样处理这个数据包”。规则存储在内核空间的信息包过滤表中。这些规则分别指定了源地址、目的地址、传输协议(如TCP、UDP、ICMP)和服务类型(如HTTP、FTP和SMTP)等。当数据包与规则匹配时,iptables就根据规则所定义的方法来处理这些数据包, 如放行(accept)、拒绝(reject)和丢弃(drop)等。配置防火墙主要工作就是添加、修改和删除这些规则。 ewctl hubDevice Objcet true 默认hub

Hero Image
K8s Deployments

蓝绿部署 蓝绿部署是一种比较常见的部署方式,经常会用于新旧版本不兼容的情况。 先将新版本(绿版)的服务运行起来。 将所有的流量切到新版本的服务上 删除旧版本(蓝版)的服务 创建新版本的ReplicaSet,将Service指向新的Pods,删除旧版本的ReplicaSet 滚动升级 蓝绿部署可以有效保证业务的正确性,但是也带来了一定的风险,例如稳定性。 假设新部署的应用是有问题的,一旦切换之后就会导致业务的崩溃,造成损失。于是就有了稍微友好的升级方式,滚动升级。 先关闭一个旧版本的实例 开启一个新版本的实例用于替换旧版本 替换成功时候循环1和2,直到所有的实例升级完成。 在整个过程中,如果中途发现异常可以及时停手,及时止损。而且Kubernetes也在客户端中支持了这个特性。kubectl rolling-update。 升级前后RC的Selector都被改变了 操作都是在客户端执行的? 金丝雀发布 金丝雀发布是滚动发布的一种特例,在滚动发布中,是不会等待的,除非中间出错了。但是有些时候,我们并不想要全都升级,可能只是处于POC的一些原因,我们只希望部分实例是新的,大部分是旧的,而这种情形,我们就称之为金丝雀发布。 升级少部分实例 查看效果,如果好,全部升级 如果不好,则不升级 声明式升级 前面介绍的这些升级发布方式在K8s上很多时候是半手工方式执行的,而Kubernetes作为一款DevOPS友好的系统,已经内置了对于部署方式的一种资源抽象,这个资源就是:Deployment。 Deployment –> ReplicaSet –> Pods Deployment 存在的意义为:在升级应用程序时,需要引入额外的ReplicaSet,并协调新旧两个RS,使他们再根据彼此不断修改,而不会造成干扰。Deployment将这些运维过程都代码化,内置为自己的逻辑,从而让升级变得简单。 首先我们使用Deployment创建3个实例 1# deploy.yaml 2--- 3apiVersion: apps/v1 4kind: Deployment 5metadata: 6 name: first-deployment 7 labels: 8 app: simple-pod-deployment 9spec: 10 replicas: 3 11 selector: 12 matchLabels: 13 app: simple-pod-deployment 14 template: 15 metadata: 16 name: simple-pod-deployment 17 labels: 18 app: simple-pod-deployment 19 spec: 20 containers: 21 - name: simple-pod-de 22 image: lukelau/rest-docker:0.

Hero Image
K8s Job

Replicas 和deployment这两类资源都是用于控制workload的。这两种类型的资源一般都是持续运行的,同时还有一些辅助方式帮助workload出现异常时恢复,以及根据情况进行动态伸缩的特性。 Job 根据Job的定义创建出对应的Pod,然后关注Pod的状态, 直到满足定义。例如Pod执行成功了或者执行失败了,并且达到了重试次数。 1--- 2apiVersion: batch/v1 3kind: Job 4metadata: 5 name: pi 6spec: 7 template: 8 spec: 9 containers: 10 - name: pi 11 image: perl 12 command: ["perl", "-Mbignum=bpi","-wle", "print bpi(2000)"] 13 restartPolicy: Never 14 backoffLimit: 4 Job正常执行结束后结果如上图。这是一个只执行一次的Job。它的操作方式就是创建一个Pod,然后运行一遍,然后就退出。如果想执行多次,则只需要增加一个参数 1completions: 2 执行2次时创建了两个Pod,然后保证这两个Pod都执行成功。 我们在使用Deployment等Workload的时候,一般会指定restartPolicy,默认都是RestartOnFail。在Job中不能这么指定,因为这个逻辑应该由Job来控制, 而不是让Pod来控制。 CronJob 定时任务, CronJob就是在Job的基础上加上了周期定义的API 1--- 2apiVersion: batch/v1 3kind: CronJob 4metadata: 5 name: batch-job-pi 6spec: 7 schedule: "0,15,30,45 * * * *" 8 jobTemplate: 9 spec: 10 template: 11 metadata: 12 labels: 13 app: pi-job 14 spec: 15 containers: 16 - name: pi 17 image: perl 18 command: ["perl", "-Mbignum=bpi","-wle", "print bpi(2000)"] 19 restartPolicy: Never

Hero Image
K8s network

容器网络 每一个容器都可以有一个自己独立的网络栈,这个独立的网络栈是基于Linux的Network Namespace实现的。 这个独立的网络栈包含了: Network Interface、Loopback Device、Routing Table和IPtables规则。对于一个进程来说,这些要素就构成了它发起和响应网络请求的基本环境。 容器可以使用自己独立的网络栈(创建属于自己的Network Namespace),也可以直接使用Host的网络栈(不创建Network Namespace)。 1# Uset -net=host to share the host network 2docker run -d -net=host --name nginx-host nginx 直接使用Host的网络栈可以提供良好的网络性能,但是不可避免的会引入网络资源共享的问题,比如端口冲突。大多数应用场景下,我们希望容器能够有自己独立的IP地址和端口,即有自己独立的Namespace。 这个时候,就会出现一个问题,在这个被隔离的容器进程中如何与其他的Network Namespace里的容器进程进行交互呢。 一般我们如果希望两台主机之间的通信,直接用网线把这两台主机连接起来即可;而如果是多台主机之间通信我们可以将其连接在同一台交换机上。 在Linux系统中,能够起到虚拟交换机作用的虚拟网络设备是Bridge,是二层网络设备。主要功能是根据MAC地址来将数据包转发到网桥的不同Port上。 为了实现上述目的,docker项目会在Host上创建一个docker0的网桥,凡是连接在docker0网桥上的容器,就相当于在同一个二层网络。 接下来就是如何把容器连接到docker0网桥上,这就需要veth pair的虚拟设备了。veth pair创建出来以后总是以两张虚拟网卡veth peer的形式成对出现的。并且从其中一个peer发出的数据包可以直接出现在与之对应的另一个peer上,即使veth pair的两端不在同一个Network Namespace 中。因此,veth pair常常用作连接不同Network Namespace的网线。

Hero Image
K8s Overview

Node 节点主要负责容器的管理,用于运行容器和保证容器的状态。默认情况下Master节点不承担Node节点的功能,但是可以通过特殊的配置让Master节点也可作为Node节点。 Etcd用于存储Kubernetes的元数据,但是不要求一定要以容器的形式运行。 1kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.0.22 --pod-network-cidr pod 的ippool, --apiserver-advertise-address 为暴露的k8sAPI调用IP. 节点(Node) 一个Node是一个运行K8s的主机,作为K8s worker, 通常称之为Minion。每个节点都运行如下K8s关键组件: kubelet:主节点代理 kube-proxy: service使用其将链接路由到Pod docker/rocket: k8s 使用容器技术创建容器 容器组(Pod) 一个Pod对应若干容器组成的一个容器组,同一个Pod内的容器共享一个存储卷(volume),同一个Pod中的容器共享一个网络Namespace,可以使用localhost互相通信。 Pod是短暂的,不是持续性实体。一个Pod是一个特定的应用打包集合,包含一个或者多个容器。和运行的容器类似,一个Pod只有很短的运行周期。Pod被调度到一个Node运行,直到容器的生命周期结束或者其被删除。 容器组生命周期(Pod Status) 包含所有容器状态集合,包括容器组状态类型,容器组生命周期,事件,重启策略,以及replication controllers. 标签(labels) 标签是用来连接一组对象的,比如容器组Pod。lable可以用来组织和选择子对象。 一个Label是attach到Pod的一对键值对,用来传递用户定义的属性。 Replication Controllers 主要负责指定数量的Pod在同一时间一起运行。Replication controller 确保任意时间都有指定数量的Pod副本在运行。如果为某个Pod创建了Replication Controller并且指定为3副本,它会创建3个Pod,并持续监控他们。如果某个Pod不响应,那么Replication controller 会替换它,保持Pod总数为3. 当创建Replication Controller时,需要指定两个东西。 Pod模板: 用来创建Pod副本的模板 Label:Replication Controller 需要监控的Pod的Label 现在已经创建了Pod的一些副本,那么在这些副本上如何负载均衡呢,我们需要的是service Service: 如果Pod是短暂的,那么重启时IP地址可能会变,怎么才能从前端容器正确可靠的指向后台容器呢。 Service是定义一系列Pod以及访问这些Pod的策略的一层抽象。Service通过Label找到Pod组。因为service是抽象的,所在在图表里通常看不到他们的存在。 现在假定有两个后台Pod,并且定义后台service名称为“backend-service”,label选择器为(tier=backend,app=myapp)。backend-service的Service会完成如下两件重要的事情: 会为Service创建一个本地集群的DNS入口,因此前端只需要DNS查找主机名为“backend-service”就能够解析出前端应用程序可用的IP地址 现在前端已经得到了后台服务的IP地址,但是它应该访问2个后台Pod中的哪一个呢。Service在这两个后台Pod之间提供透明的负载均衡,会将请求发给其中的任意一个。通过每个Node上运行的代理 kube-proxy完成。 Kubernetes Master 集群拥有一个K8s Master,K8s Master 提供集群的独特视角,并拥有一系列组件,如Kubernetes API server. API server 提供可以用来和集群交互的REST 端点。Master 节点包含用来创建和复制Pod的Replication Controller. 参考资料:

Hero Image
K8s StafulSet

前面介绍的几种workload都有一个共性,那就是创建出来的Pod都是一致的。所谓的一致就是说,假设我们使用的是ReplicaSet,创建了3个Pod,那么这3个Pod创了名字一定不一样之外,其他属性可能都是一样的,包括运行时的参数和模式以及数据存储。 如果是Web Service,数据保存到后端的DB中,上述逻辑是没有问题的。 如果使用ReplicaSet来部署一个DB的多实例, 就可肯能存在问题了。 数据持久化,一般使用PVC,当使用PVC 和PV的时候 ​ ReplicaSet -> Pods(3 个) -> 持久卷声明 -> 持久卷 3个Pod的数据都写到同一个Pv中,这样肯定是不行的??? StatefulSet 为了解决Pod的状态性的问题,K8s引入了StatefulSet的概念 Pod有单独的存储和固定的网络标识 需要配备一个headless Service,用于DNS 可以通过DNS快速发现其他的Pod 可以直接通过Pod DNS通信 每个Pod都可以通过DNS访问到,这种特性在其他workload中是不能实现的。通过StatefulSet可以让Pod持有状态,即使因为故障Pod重建了,那么对应的Pod的名字和数据都会保留,和重建之前没有什么区别。 使用StatefulSet 必须建立一个HeadlessService,然后绑定这个headlessservice到StatefulSet。 以MongoDB为例,创建一个StatefulSet,因为还没有介绍到PVC和PV的内容,所以MongoDB将使用本地存储卷。 创建Headless Service 创建 StatefulSet 通过DNS访问Pod 在StatefulSet的Pod中可以通过DNS直接访问其他Pod。 1mongo --host mongo-1.mongo 可以通过<pod-name>.<service-name>的形式访问Pod。这其实和StatefulSet的设计是有关系的,在类似的Deployment中Pod的名字是不固定的,而在StatefulSet中,Pod的名字是固定的。 和ReplicaSet对比 因为有状态的Pod彼此不同,通常希望操作的是其中的特定的一个,所以StatefulSet通常要求你创建一个用来记录每个Pod网络标记的HeadlessService。通过这个Service,每个Pod都拥有独立的DNS记录,而这在ReplicaSet中是不行的?(如果为ReplicaSet创建一个Headless Service会发生啥?) 因为StatefulSet缩容任何时候只会操作一个Pod实例,所以有状态应用的缩容不会很迅速。 StatefulSet在有实例不健康的情况下,是不允许缩容的。 持久存储 一个StatefulSet可以拥有一个或者多个卷声明模板,这些声明会在创建Pod前创建出来,绑定到一个Pod的实例上。 扩容StatefulSet会创建两个API对象,一个Pod和一个卷声明;但是缩容StatefulSet却会删除一个Pod对象,而会留下PVC,因为一旦删除PVC则意味着PV会被回收。 StatefulSet at-most-one Kubernetes 必须保证两个拥有相同标记和绑定相同持久卷声明的有状态的Pod实例不会同时运行。一个StatefulSet必须保证有状态的实例的 at-most-one 语义。也就是说StatefulSet必须保证一个Pod不再运行后,才会去创建它的替换Pod。