2020年8月

https://stackoverflow.com/questions/46667659/kubernetes-cannot-access-nodeport-from-other-machines

本来第一次配置集群的时候,是可以在局域网内访问的,之后关机重启,遇到DiskPressure这个Taint“污点(标记)”的时候,重新配置了一次集群(即kubeadm resetkubeadm init),然后以为是服务没跑起来,测试以后发现只能在本机访问了。

nmap扫了一遍端口,目标端口显示的不是closed,而是filtered,然后也隐隐猜测和防火墙有关,万万没想到,原因正是出在iptables上。而后了解到,firewalldiptables是两个不同的Linux防火墙工具,有着各自独立的规则。因而在配置Kubernetes时禁用了firewalld之后,iptables还是在工作的。而NodePort的访问,需要进行流量转发。

简单粗暴的解决方式(非生产环境):

iptables -A FORWARD -j ACCEPT

不过这个地方我有点没想明白:kube-proxy可以将Pod内的端口映射到主机端口的监听上,而在Linux中,默认的端口监听是监听在所有可用的interface网络接口上的,监听特定的网络接口是需要编程显式声明的,理论上不会需要进行不同网络接口间的流量转发。

这一次历时接近一天的CentOS 8扩容翻车和修复事件,终于让我对之前一直觉得深奥而不愿接触的Linux LVM(Logical Volume Management,逻辑卷管理)分区管理机制有了基本而全面的认识,算是又填上了Linux基本操作在存储管理方面的一个大坑。

LVM采用了一种三层的整体结构,自底向上依次是Physival Volume物理卷Volume Group(物理)卷组Logical Volume逻辑卷。其中物理卷对应传统意义上,在诸如Windows上磁盘管理,或者Linux上GParted中为分区分配的物理空间;卷组和RAID 0十分相似,即在逻辑地址空间中将某些指定的物理卷“融合”起来,形成逻辑意义上的“磁盘”;逻辑卷则和物理卷相似,只不过其是在一个卷组内进行分区空间的指派,最后一个分区对应的就是一个逻辑卷,对逻辑卷进行分区格式化,初始化文件系统就可以使用(当然,前提是系统支持且识别这种LVM分区布局)。

用一个十分有意思的方法来解释就是,物理硬盘加上LVM,会形成一个“螺旋上升”的结构:

物理磁盘(硬盘)->物理分区(物理卷)->逻辑磁盘(卷组)->逻辑分区(逻辑卷)

这决定了建立一个可用的LVM逻辑卷,需要依次进行物理卷、卷组和逻辑卷的创建三个步骤。

可以看到,即使加了LVM,仍然没有逃脱磁盘需要先分区后使用的本质。但是LVM的一个优势在于,上层系统所使用的是“逻辑卷”,而不是“物理卷”,也就是说,此时的上层系统不知道底层的物理数据分布的同时,还可以正常使用(当然,Linux内核是知道数据的物理分布的,毕竟LVM需要在此处实现)。这意味着,LVM可以实现跨磁盘的分区创建和管理;也可以实现分区本身在磁介质上的非连续存储。对于个人用户来说,可能算不上有意义的功能;但是对于企业用户来说,对于可能大到一块磁盘都装不下的单文件来说,这是一种解决方案。换个角度来说,LVM方案能做到的不仅仅是实现一种无RAID情况下超大文件存储的可能,还可以起到简化mount操作的作用。将一组完成同一业务功能的磁盘建立为LVM逻辑卷,可以极大地简化分区挂载/卸在脚本的复杂度,并且增加指令的可读性。

在Linux的LVM实现中,划分了三个指令簇,分别对应LVM三个层次的操作,如下表所示。

功能 PV管理命令 VG管理命令 LV管理命令
s 摘要 pvs vgs lvs
scan 扫描 pvscan vgscan lvscan
create 创建 pvcreate vgcreate lvcreate
display 显示 pvdisplay vgdisplay lvdisplay
remove 移除 pvremove vgremove lvremove
extend 扩展 - vgextend lvextend
reduce 压缩 - vgreduce lvreduce

在某些场景下(比如dracut中),这些指令不会单独存在,需要添加lvm前缀,将指令作为参数使用,如lvm pvscan

在对LVM有了初步的认识之后,进行这次修改的流程介绍:删除LVM卷组内的swap分区,将多出来的空间分配给同一卷组内的系统根分区root

步骤依次如下:

  1. 删除cl卷组中的swap分区:由于swap分区本身就属于缓存的性质,无需进行数据的保存。

    lvremove /dev/cl/swap
  2. 建议先保存数据)扩容根分区:
    有两种方式可以选择,区别在于手动或者自动对于文件系统的大小调整。在本示例中,删除swap分区释放的空间为2GiB。
    手动:
    lvresize -L +2G /dev/cl/root
    xfs_growfs /dev/cl/root

    自动:

    lvresize --resizefs -L +2G /dev/cl/root

需要注意的一点是,xfs只支持分区扩容(ext4支持分区压缩)。如果想要分区压缩的话,只能手动备份数据,重建分区再恢复数据。

这个操作平凡无奇,网上也能找到不少教程,重点是完成操作准备重启开机的时候,问题来了:

[ OK ] Reached target initrd root device.

一看到这个,就知道大事不妙,一定是磁盘配置出了问题。果不其然,等待数分钟后,dracut开头的命令行带着日志文件出现在了我的屏幕上。

仔细阅读日志文件,发现其中提到了“swap分区找不到”的相关消息,而swap分区又是我手动操作进行的删除操作,看来是有哪里的配置残留没有清理干净。折腾了半天dracut重建之后,发现无济于事,后来突然想起来可能与内核参数有关,遂修改/etc/sysconfig/grub文件,将其中与swap分区有关的参数全部删除,重新生成配置文件后,重启。

然后与预期一样的是,系统启动过程恢复了正常。其中有一点很奇怪的地方是,可能是由于我手动重建initramfs镜像的原因,并无法使用升级后的内核启动,而只能使用与安装盘版本一致的初始内核启动成功。于是出现了偷懒的新内核修复方法:

dnf reinstall kernel*-4.18.0-193.14.2.el8_2 

后面的版本信息根据自己的需要进行更改,上方示例中的版本信息是文章撰写时的最新内核信息,可能随着时间推移发生改变。

参考:https://www.cnblogs.com/diantong/p/10554831.html

这道题目有两种解法:记忆化搜索和线性动态规划。实际上,两者都属于动态规划的范畴之内,而记忆化搜索因为具有更为直接的思路,因而更容易想到(虽然不一定了解这个术语)。记忆化搜索的原理是利用搜索子问题的重复性,本质就是使用额外的空间存储已经进行过的搜索结果,是一种以空间换时间的思路,在此不做赘述。

本篇题记主要描述在本题中,将二维平面搜索转换为线性动态规划的一些理解。

第一步:分析问题结构

将问题动态规划化需要三个条件:最优子结构、无后效性和子问题重复。其中最优子结构的“问题最优解基于子问题的最优解”结论,从理论上允许我们通过求解子问题的最优解,从而推断出原问题的最优解;无后效性则确保了子问题一定是可解、易解的,从理论上杜绝了循环依赖的问题;子问题重复则是对于效率上的一种保证。前两个条件确保了动态规划的可行性,第三个则确保了动态规划的有效性。

但是在本题中,数据是按照二维坐标排列的。这样就会产生两个问题:

  1. 索引有两个维度,排序复杂;
  2. 现有的索引并不利于子问题的划分(即坐标与“高度”之间无关联性)。

于是注意到题目中,“斜坡”是按照点的高度进行定义的,所以考虑将点按照高度进行排序,这样恰好满足了最优子问题的结构:当考虑“斜坡”是以在现有“斜坡”的基础上追加新的、长度为1的小“斜坡”的话,恰好形成了一系列以高度为顺序的子问题序列。也就是说,这个时候,只需要按照高度顺序进行某种最长升序子序列的状态转移方程进行求解即可。

第二步:明确状态转移方程

状态转移方程可以视作一类递归函数,不过实现的时候用的是数组,而这也正是对子问题重复特性的利用。实际上,状态转移方程就是动态规划算法的实现原型。完成目标参数值下的状态转移方程值求解,也就完成了对问题的求解。

这里的状态转移思路非常简单,以当前坐标为起点的最长路径是:所有可到达该点、且高度高于该点的坐标中,所具有的最长路径。

公式表述如下:

L_{i} = \mathit{max}(L_{i}, L_{j} + 1)\ \text{where}\ \mathit{i, j}\ \text{is adjacent and}\ h_{i} > h_{j}

第三步:实现

基于上述思路,进行程序语言的书写就可以了。由于要设计高效算法,应该尽可能避免不必要的操作,在进行数据读入的时候就可以按照高度作为索引的数据结构进行保存。其余部分没有什么需要特别注意的,就是条件记得写对、不写漏就行。

在CentOS 8上配置Kubernetes。

步骤可以大致简述如下:

  1. 如果在国内,换源
  2. 安装Docker(需要手动添加repo)
  3. 安装Kubernetes配置工具Kubeadm,会自动顺带完成其他Kubernetes配件的安装(也需要手动添加repo)
  4. 决定一个CNI(容器网络接口)组件
  5. 使用kubeadm init初始化集群,记得带上CNI可能会需要指定的参数(至少FlannelCalico在默认情况下是要求手动显式指定默认的CIDR的)
  6. 使用kubectl apply(更新)或者kubectl apply(重置)安装对应CNI组件的Pod
  7. 使用kubectl taint nodes --all node-role.kubernetes.io/master-取消集群master节点的“特殊地位”,允许在master节点上部署应用容器 Google传统艺能

然后关于Kubernetes的镜像,默认用的是Google的GCR仓库,显然在国内是访问不到的,在拉取镜像的时候会报超时错误。网上有不少的解决方法,比如建立私有仓库,换源,但是要么太复杂,要么说的含糊不清。还是弄个透明代理最爽了

参考:https://coreos.com/flannel/docs/latest/kubernetes.html

最近在研究编码参数的时候,随手点开了某番剧的MediaInfo作为参考。结果一看,虽然MediaInfo上显示是AVC(H.264的一种)编码,但是居然没有编码参数Encode Settings

然后很是疑惑,遂到网上寻找隐藏H.264编码参数的方法。H.264的没找到,不过H.265的好像倒是有。

http://forum.doom9.org/archive/index.php/t-171681.html
这里有一群外国人讨论过这个问题,从中能够获取的信息之一是,x264编码器的开发者们本意似乎是希望编码参数跟随视频一同流传的,从某种程度上还能方便大家相互学习参数的调整。也就是说,AVC编码的视频本身没有携带编码参数是一种不正常的现象(视频编码程序本身并没有提供这种功能)。

然后仔细对比了一下MediaInfo里的参数,也没有发现什么猫腻。

不过后来用WinHex查看了一下文件头,发现这个文件的类型是ftypmp42,和常见的自带编码参数的ftypavc1有区别。说不定是因为数据流版本太老,所以不支持记录编码参数?(问题是,上个世纪的DV编码格式都能记录编码参数的啊)

难道是,失传已久的S级记忆消除秘术?

坑+1