参考:https://www.linuxso.com/linuxpeixun/10330.html
iptables的数据包的流程介绍
iptables 相关概念
匹配(match)
:符合指定的条件,比如指定的 IP 地址和端口。
丢弃(drop)
:当一个包到达时,简单地丢弃,不做其它任何处理。
接受(accept)
:和丢弃相反,接受这个包,让这个包通过。
拒绝(reject)
:和丢弃相似,但它还会向发送这个包的源主机发送错误消息。这个错误消息可以指定,也可以自动产生。
目标(target)
:指定的动作,说明如何处理一个包,比如:丢弃,接受,或拒绝。
跳转(jump)
:和目标类似,不过它指定的不是一个具体的动作,而是另一个链,表示要跳转到那个链上。
规则(rule)
:一个或多个匹配及其对应的目标。
链(chain)
:每条链都包含有一系列的规则,这些规则会被依次应用到每个遍历该链的数据包上。每个链都有各自专门的用途, 这一点我们下面会详细讨论。
表(table)
:每个表包含有若干个不同的链,比如 filter 表默认包含有 INPUT,FORWARD,OUTPUT 三个链。iptables 有四个表,分别是:raw,nat,mangle和filter,每个表都有自己专门的用处,比如最常用filter表就是专门用来做包过滤的,而 nat 表是专门用来做NAT的。
策略(police)
:我们在这里提到的策略是指,对于 iptables 中某条链,当所有规则都匹配不成功时其默认的处理动作。
连接跟踪(connection track)
:又称为动态过滤,可以根据指定连接的状态进行一些适当的过滤,是一个很强大的功能,但同时也比较消耗内存资源。
经过iptables的数据包的流程介绍
一个数据包到达时,是怎么依次穿过各个链和表的(图)。
基本步骤如下:
-
数据包到达网络接口,比如 eth0。
-
进入
raw
表的PREROUTING
链,这个链的作用是赶在连接跟踪之前处理数据包。 -
如果进行了连接跟踪,在此处理。
-
进入
mangle
表的PREROUTING
链,在此可以修改数据包,比如 TOS 等。 -
进入
nat
表的PREROUTING
链,可以在此做DNAT,但不要做过滤。 -
决定路由,看是交给本地主机还是转发给其它主机。
到了这里我们就得分两种不同的情况进行讨论了,一种情况就是数据包要转发给其它主机,这时候它会依次经过:
-
进入
mangle
表的FORWARD
链,这里也比较特殊,这是在第一次路由决定之后,在进行最后的路由决定之前,我们仍然可以对数据包进行某些修改。 -
进入
filter
表的FORWARD
链,在这里我们可以对所有转发的数据包进行过滤。需要注意的是:经过这里的数据包是转发的,方向是双向的。 -
进入
mangle
表的POSTROUTING
链,到这里已经做完了所有的路由决定,但数据包仍然在本地主机,我们还可以进行某些修改。 -
进入
nat
表的POSTROUTING
链,在这里一般都是用来做 SNAT,不要在这里进行过滤。
利用cat 给文件写内容,追加的方式
cat >> proxy.sh <<EOF
export http_proxy=http://99.0.85.1:808
export https_proxy=http://99.0.85.1:808
EOF
cat给文件写内容,覆盖的方式
cat > proxy.sh <<EOF
export http_proxy=http://99.0.85.1:808
export https_proxy=http://99.0.85.1:808
EOF
普通操作可以使用冒号(:)井号(#)正斜杠(/)来作为分隔符
sed -i 's#abc#def#g' a.file #将文件a.file中的abc替换成def
sed -i 's/^abc.*/abc=def/' a.file # 将a.file中以abc开头的一行替换成abc=def
sed -i '/ABC/,$d' a.file # 将a.file中从ABC开始(包括ABC)以后的所有行删除
sed -i '$a aabbccdd' a.file # 给a.file追加aabbccdd
cat geng.file | sed 's/abc/def/g' ## 打印文件geng,并将其中的abc替换成def
参考:https://blog.csdn.net/genghongsheng/article/details/120432010
go中实现超时控制下执行函数功能
func RunWithTimeout(fun func() error, timeout time.Duration) error {
finished := make(chan struct{})
var err error
go func() {
err = fun()
finished <- struct{}{}
}()
select {
case <-finished:
return err
case <-time.After(timeout):
return fmt.Errorf("timeout")
}
}
如果不配置.gitignore的文件,带push代码的时候就会把一写不必要的文件push到远程仓库,如.idea文件。如果不小心出现此文件在远程仓库可以通过一下步骤delete此文件:
1.配置.gitignore文件(新建/编辑)
vim .gitignore-->i--->添加.idea--->esc--->:wq
# 将.gitignore文件上传到远程仓库
git pull
git add .gitignore
git commit -m 'edit .gitignore'
git push origin master
3.删除git的.idea文件
git rm --cached -r .idea
4.同步到远程仓库
git commit -m 'delete .idea'
git push origin master
完成之后就可以发现git仓库中的.idea文件已经被删除,而且之后push代码也不会再把.idea文件上传
你好2023
参考:https://zhuanlan.zhihu.com/p/434731896、https://www.cnblogs.com/zhrx/p/16388175.html
cgroup
目前我们所提到的容器技术、虚拟化技术(不论何种抽象层次下的虚拟化技术)都能做到资源层面上的隔离和限制。
对于容器技术而言,它实现资源层面上的限制和隔离,依赖于linux内核所提供的cgroup和namespace技术。
两项技术的概括
- cgroup主要作用:管理资源的分配、限制;
- namespace的主要作用:封装抽象,限制,隔离,使命名空间内的进程开起来拥有他们自己的全局资源;
cgroup是Linux内核的一个功能,用来限制、控制与分离一个进程组的资源(如CPU、内存、磁盘、输入输出等)。
cgroup需要限制的资源是:
- CPU
- 内存
- 网络
- 磁盘I/O
作用:
- 资源限制:可以配置cgroup,从而限制进程可以对特性资源的使用量
- 优先级:当资源发生冲突时,可以控制一个进程相比另一个cgroup中的进程可以使用的资源量(CPU、磁盘或网络等)
- 记录:在cgroup级别监控和报告资源限制
- 控制:可以使用单个命令更改cgroup中所有进程的状态(冻结、停止或重新启动)
依赖的四个核心概念
- 子系统
- 控制组
- 层技树
- 任务
控制组(group)
表示一组进程和一组带有参数的子系统的关联关系。例如,一个进程使用了CPU子系统来限制CPU的使用时间,则这个进程和CPU子系统的关联关系称为控制组。
层级树
有 一系列的控制组按照树状结构排列组成的。这种排列方式可以使得控制组拥有父子关系,子控制组默认拥有父控制组的属性,也就是子控制组会继承父控制组。
比如,系统中定义了一组控制组c1,限制了CPU可以使用1核,然后另一个控制组c2想实现既限制CPU使用1核,同时限制内存使用2G,那么c2就可以直接继承c1,无需重复定义CPU限制。
子系统
一个内核的组件,一个系统代表一类资源调度控制器。例如内存子系统可以限制内存的使用量,CPU子系统可以限制CPU的使用时间。
子系统是真正实现某类资源的限制的基础。
subsystems(子系统)cgroups中的子系统就是一个资源调度控制器(又叫controllers)。 在/sys/fs/cgroup/这个目录下可以看到cgroup子系统
[root@centos ~]# ll /sys/fs/cgroup/
total 0
dr-xr-xr-x 5 root root 0 Oct 23 06:38 blkio # 为块设备设定输入输出限制,比如物理驱动设备
lrwxrwxrwx 1 root root 11 Oct 23 06:38 cpu -> cpu,cpuacct # 使用调度控制程序控制对CPU的使用
lrwxrwxrwx 1 root root 11 Oct 23 06:38 cpuacct -> cpu,cpuacct # 自动生成cgroup中任务对cpu资源使用情况的报告
dr-xr-xr-x 3 root root 0 Oct 23 06:38 cpuset # 可以为cgroup中的任务分配独立的cpu和内存
dr-xr-xr-x 5 root root 0 Oct 23 06:38 devices # 可以开启或关闭cgroup中任务对设备的访问
dr-xr-xr-x 3 root root 0 Oct 23 06:38 freezer # 可以挂起或恢复cgroup中的任务
dr-xr-xr-x 3 root root 0 Oct 23 06:38 hugetlb
dr-xr-xr-x 6 root root 0 Oct 23 06:38 memory # 可以设定cgroup中任务对内存使用量的限定,并且自动生成这些任务对内存资源使用情况的报告
lrwxrwxrwx 1 root root 16 Oct 23 06:38 net_cls -> net_cls,net_prio # docker没有直接使用它,它通过使用等级识别符标记网络数据包,从而允许linux流量控制程序识别从具体cgroup中生成的数据包
lrwxrwxrwx 1 root root 16 Oct 23 06:38 net_prio -> net_cls,net_prio
dr-xr-xr-x 3 root root 0 Oct 23 06:38 perf_event # 使用后使cgroup中的任务可以进行统一的性能测试
dr-xr-xr-x 5 root root 0 Oct 23 06:38 pids # 限制任务数量
dr-xr-xr-x 3 root root 0 Oct 23 06:38 rdma
dr-xr-xr-x 6 root root 0 Oct 23 06:38 systemd
这篇文章好强,可以抽空研究复现下:https://www.cnblogs.com/zhrx/p/16388175.html
参考文章:https://developer.aliyun.com/learning/course/572/detail/7866
如何开发自己的CNI插件
CNI插件的实现通常包含两个部分:
- 一个二进制的CNI插件去配置Pod网卡和IP地址。这一步配置完成后相当于给Pod插上了一条网线:有了自己的IP、自己的网卡;
- 一个Daemon进程去管理Pod之间的网络打通。这一步相当于将Pod真正连上网络,让Pod之间能够互通。
给Pod插上网线
- 给Pod准备虚拟网卡
- 创建“veth”虚拟网卡对
- 将一端的网卡挪到Pod中
- 给Pod分配IP地址
- 给Pod分配集群中唯一的IP地址
- 一般把Pod网段按Node分段
- 每个Pod再从Node段中分配IP
- 配置Pod的IP和路由
- 给Pod的虚拟网卡网址分配到的IP
- 给Pod的网卡上配置集群网段的路由
- 在宿主机上配置到Pod的IP地址的路由到对端虚拟网卡上
给Pod连上网络
刚才是给Pod插上网线,也就是说分配了IP地址和路由表。接下来说明怎么让每一个Pod的IP地址在集群里都能被访问到。
一般是在CNI的daemon进程中去做这些网络打通的事情。
- 首先CNI在每个节点上运行的daemon进程会学习到集群所有Pod的IP地址及其所在节点的信息。学习的方式通过监听K8s APIserver,拿到现有Pod的IP地址以及节点,并且新的节点和新的Pod在创建的时候也能通知到每个daemon;
- 拿到Pod以及Node相关信息后,再去配置网络进行打通。
- 首先daemon回创建到整个集群所有节点的通道。这里的通道是个抽象的概念, 具体实现一般是通过overlay隧道等。
- 第二部是将所有Pod的IP地址跟上一步创建的通道关联起来。关联也是个抽象的概念,具体实现通常是通过linux路由、fdb转发表或者ovs流表完成的。
参考文章:https://zhuanlan.zhihu.com/p/102897620
CRI是什么
CRI: Container Runtime Interface,容器运行时接口;
CRI包括Protocol Buffers、gRPC API、运行库支持及开发中的标准规范和工具,是以容器为中心设计的API,设计CRI的初衷是不希望向容器(比如docker)暴露pod信息或pod的api。
CRI工作在kubelet与container runtime之间,目前常见的runtime有:
- docker
- containerd: 可以通过shim对接不同的low-level runtime
- cri-o:一种轻量级的runtime,支持runc和Clear container作为low-level runtimes。
CRI是如何工作的
CRI大体包含三部分接口:Sandbox、Container和Image,其中提供了一些操作容器的通用接口,包括Create、Delete、List等。
Sanbox为container提供一定的运行环境,这其中包括pod的网络等。Container包括容器生命周期的具体操作,Image则提供对镜像的操作。
kubelet回通过gRPC调用CRI接口,首先去创建一个环境,也就是所谓的PodSandbox。当Podsandbox可用后,继续调用image或container接口去拉取镜像和创建容器。
PodSandbox
从虚拟机和容器化两方面看,两者都是用了cgroups做资源配额,而且概念上都抽离出一个隔离的运行时环境,只是区别在于资源隔离的实现。 因此sandbox是k8s为兼容不同运行时环境所预留的空间,也就是说k8s允许low-level runtime依据不同的实现去创建不同的podsandbox,对于kata来说podsandbox就是虚拟机,对于docker来说就是linxe namespace。 当pod sandbox建立起来后,kubelet就可以在里面创建用户容器。当删除pod时,kubelet会先移除pod sandbox然后再停止里面的所有容器,对于container来说,当sandbox运行后,只需将新的container的namespace加入到已有的sandbox的 namespace中。
在默认情况下,cri体系里,pod sandbox其实就是pause容器。kubelet代码引用的defaultSandboxImage就是官方提供的gcr.io/google_containers/pause-amd64 镜像
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
原题链接复原 IP 地址
var res []string
func restoreIpAddresses(s string) []string {
res = make([]string, 0)
path := make([]string, 0)
tracingBack(s, 0, path)
return res
}
func tracingBack(s string, startIndex int, path []string){
if startIndex >= len(s) && len(path) == 4{
// 遍历结束且候选长度为4,则为一种解法
res = append(res, strings.Join(path, "."))
return
}
for index:= startIndex+1; index < len(s)+1; index++{
if isValidIpAddress(s[startIndex:index]){
// 该子序列为有效的序列,则加入候选进行遍历
path = append(path, s[startIndex:index])
tracingBack(s, index, path)
}
if len(path) > 0{
// case 1: 本次遍历的子序列为无效子序列,需将上一次分割结果抛弃
// case 2: 上一次分割结束,已经产生一种可能结果,需将上一次分割结果抛弃,开始回溯下一种解法
// 不管上次结果如何,需进行回溯,抛弃上一次的结果。
path = path[0:len(path)-1]
}
}
}
func isValidIpAddress(s string)bool{
a, _ := strconv.Atoi(s)
if a <= 255 && strconv.Itoa(a) == s{
return true
}
return false
}