Shawn's Blog

一个伪程序员的伪技术博客

0X00 虚拟化

首先虚拟化 Virtualization它是一种技术,通过软件技术虚拟一张网卡(例如 Linux Bridge)、虚拟一个磁盘分区(例如 vmdk 文件)甚至直接虚拟一整台电脑(例如 VMware/VirtualBox)出来都是虚拟化技术的实装。

虚拟机就是通过虚拟化技术虚拟了一整套电脑所需的硬件,例如CPU、内存、磁盘、网卡等等,然后将它们拼在一起就是一台虚拟机了。

vm

0X01 容器

区分这些的重点就在容器 Container这里了。最早接触容器的时候很多人都说简单理解成轻量化虚拟机,不需要开关机、不需要操作系统、甚至是秒级启动的。当时我就死活不理解,明明是虚拟机为啥不需要操作系统不需要开关机的呢,其实但凡当时多解释几句,也就不会有这种困扰了。

tips: 这里说的容器仅仅指代 Linux 容器

容器的本质是进程。在 Linux 环境下创建一个进程,如果我们将它与其它进程隔离开,让它发现不了其它进程、再分配虚拟网卡、临时的文件系统等等等等,那它其实就是一个「容器」了。所以容器的本质是进程,这样以来就说的通了。而且为什么不需要操作系统?因为它运行在一个现有的操作系统之上。为什么不需要开关机?因为它只是个进程。为什么是秒级启动?依旧因为它只是个进程~

关于 Linux 是如何将一个进程隔离开的,可以参考下面这一系列文章

virtualization-vs-containers

0X02 Docker

Docker 就简单了,它只是容器技术的一种实现,类似于 KVM 只是虚拟化技术的一种。不仅 Docker 是容器技术,还有 LXC、cri-o、containerd 等等一些。

0X03 总结

最后进行一个总结:虚拟化技术是通过软件虚拟出硬件来的技术;虚拟机是虚拟化的集大成者,将一堆虚拟出来的硬件组装成一台电脑;容器技术的重点是将进程隔离出来,使之独立运行;Docker 则是容器技术的一种实现

0X00 Linux 的进程关系

既然想搞清楚容器的单进程模型,那自然需要先复习一下 Linux 下的基本进程关系了。你说你用的是 Windows Container?不懂,不会,打扰了😢

我们知道 Linux 下会有一个 PID=1 的进程来带动其他进程,以前 PID=1 的进程是 init 后来大家都在用 systemd ,这里就不多说了,只来回顾一下「孤儿进程」和「僵尸进程」这两个概念。

这里来模拟一个场景,默认 PID=1 的是 systemd

  1. 打开运行一个新进程
  2. 新进程 fork 了一个子进程出来
  3. 子进程持续运行
  4. 此时父进程终止了
  5. 子进程成为孤儿进程(因为他的父进程挂了)
  6. 该子进程会交由 systemd 接管
  7. 当子进程结束之后 systemd 会替它「收尸」,也就是释放、回收资源之类的

还有另一个场景:某菜鸡程序员(可能是我)写了个程序,现在运行起来了。该进程会 fork 新进程,每隔一会儿就会 fork 一个,但是该进程并没有 wait/waitpid 这种操作(俗称管杀不管埋)。所以当子进程结束后资源并没有被回收,甚至 PID 都还占着。这种已经结束了但是没被回收的进程就叫僵尸进城噢不,叫僵尸进程

需要注意的一点就是,孤儿进程并不是什么大问题,systemd 会解决它的;僵尸进程也不都有问题,任何进程在结束之后和被回收之前,都处于这个状态,真正要注意的是源源不断产生僵尸进程的那个进程,就算只是一直占着 PID 也不是个事儿嘛。

阅读全文 »

0X00 🛡️叠buff

  • 我是个 Kubernetes 纯新手,并不懂很多原理和概念,可能会有误导;
  • 我只是将自己遇到过的问题列出来,如果你想找「权威」请看官方文档;
  • 每个人的基础环境不同,Kubernetes 版本也不同,参考应该学会变通;
  • 此处记录的仅为个人部署过程中遇到的问题记录,并非「教程」或「指南」;

0X01 正文

注意交换分区

部署 Kubernetes 的节点是不允许使用交换分区的,临时禁用可以 swapoff -a。然后在 /etc/fstab 中将交换分区的自动挂载给注释掉就可以了。

k8s_swap

不使用 docker

Kubernetes 已经不建议使用 docker 作为容器运行时了,可以考虑使用 containerd 或者 CRI-O。注意这里提到的 Dockershim 已经移除并不意味着不能再用 Docker 了,而是说 Kubernetes 并不会原生支持 Docker 了,以后 Docker 的地位和其他运行时的地位相同了。

k8s-container-runtime

Containerd 的默认配置

如果使用 Containerd 作为容器运行时的话,安装好 Containerd 之后要检查一下配置文件 /etc/containerd/config.toml。如果没有的话需要手动创建一个默认配置

1
2
mkdir /etc/containerd
containerd config default > /etc/containerd/config.toml

kubelet 疯狂重启

如果你还没有搞定 kubeadm init 的话,kubelet 疯狂重启是正常行为,它在等待 kubeadm。

需要 AppArmor

我之前手贱把 AppArmor 给卸掉了,自以为是得认为它会影响到 Kubernetes 的运行。其实它确实会影响,因为 Kubernetes 的运行时需要它的,所以后来看日志在报错就又装回来了。

正确配置 cni 网络插件

如果你是使用 Containerd 的话,可以参考 https://github.com/containerd/containerd/blob/main/script/setup/install-cni

容器运行时的冲突

如果要用 Containerd 的话就记得先把 docker 卸载干净,否则可能会导致一些冲突和不兼容的状况。

正确加载内核模块和内核参数

我当时使用的是 Ubuntu 22.04,默认没加载 br_netfilter 模块,所以需要使用 modprobe br_netfilter 临时加载,并且使用 echo br_netfilter >> /etc/modules 持久化加载,保证其重启后也是被加载的。可以使用 lsmod | br_netfilter 的方式检查是否已经加载了该模块。

然后就是内核参数,首先可以通过 sysctl -a | grep xxx 的方式检查当前参数是否设置正确了。如果没设置正确的话需要在 /etc/sysctl.conf 中进行修改。改好之后 sysctl --system 可以使改动生效。

1
2
3
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1

使用 crictl 命令

如果你想使用 crictl 命令查看容器/镜像,则需要修改其配置文件让它能够连接到你的容器运行时,配置文件在 /etc/crictl.yaml。如果你用的是 Containerd 的话配置文件可以写成

1
2
3
4
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: true

关于认证文件

kubeadm init 成功之后,记得关注一下它打出来的内容,里面包含了你该如何让其他节点加入,还包含了如何从其他机器上管理 Kubernetes 集群。

kubeadm_init

get nodes

当使用 kubectl get nodes 命令查看到的所有节点都处于 Ready 并且过一两分钟也没有异常状态时,也就意味着你的 Kubernetes 集群已经部署完成了🎉

关于 dashboard

刚刚部署了一个 kubenetes-dashboard 结果 401/403/40x 了?可以尝试使用端口转发的方案:https://github.com/kubernetes/dashboard/issues/5542#issuecomment-706395744

dashboard 打开了,但是没有 Token 登陆不了?那就创建一个 token 喽 https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md

0X00 为嘛用 kind

作为一个纯新手想要学习 Linux、MySQL、Python… 的第一步往往都是先装一个来看看,当然 Kubernetes 也不例外。装 Linux 也许跟着教程在虚拟机里一会儿就装好了,尤其是现在很多发行版本都有图形化安装界面了,但是 Kubernetes 就不一样了,如果你去部署一套 K8S 集群的话极有可能会遇到一系列问题,包括但不限于:

  • 交换分区没关,导致服务异常
  • 搞不清楚 CRI-O/docker/containerd 之间的关系
  • 刚装好的 kubelet 服务疯狂重启
  • cni 网络插件搞不明白,导致 kubeadm init 一直不成功
  • 缺少内核模块导致的集群初始化异常
  • 缺少内核参数导致的集群初始化异常
  • 想用 crictl 却怎么都看不到 container 状态

这些在熟手眼里可能根本不是问题,但是对于第一次接触的人来说还是挺麻烦的。所以我们需要一种简单的方式来快速部署一个 Kubernetes 来看一看试一试,而不是上来就先部署一套三四个节点的真正意义上的集群。

Kubernetes 官方推荐了几种单节点/单机部署的方式,kind 就是其中一种。它是基于 docker 的部署方式,如果你还不熟悉 docker 的话建议先把 docker 学一学再开始 Kubernetes 的学习。如果你已经了解 docker 并能简单的使用它了,那接下来就开始安装 Kubernetes 吧~

tips: 建议在虚拟机中安装,分配 2C4G 然后随便装一个比较新比较常用的 Linux 发行版本就可以

阅读全文 »

0X00 这是一个描述

下面介绍几个我自己常用的小技巧,均可以在日常工作中给自己带来一些小小的便利🤪

0X01 临时 web 服务器

如果你电脑上装了 Python3 则可以使用 python3 -m http.server --bind 0.0.0.0 2333 这个命令在当前目录启动一个简单的 web 服务,监听在 0.0.0.0:2333 上。这样一来别人就可以访问你的 ip 来下载当前目录下的文件了。不过使用这个方法的时候要注意自己当前的工作目录哈,不要傻乎乎的在自己的 $HOME 下面用这个命令,小心别人下载你的隐私数据喔。

不过如果你搞不懂什么是「监听、0.0.0.0、端口」的话,还是先去搞一下计算机网络吧。
python_http

0X02 使用 pdb/ipdb 调试脚本

我们都知道用 pdb 模块可以逐行调试 Python 脚本,只需要在脚本里 import pdb 然后在需要打断点的地方加上 pdb.set_trace() 就可以了。但其实 Python 还有一个三方库叫做 ipdbpdb 的升级版,应该各位也都知道吧,通过简单的 pip install 就可以装好。不论是 pdb 还是 ipdb 其实都有这个用法:不修改脚本内容,仅通过 python3 -m ipdb script_filename.py 来从头调试脚本。并且会在每次脚本结束之后从头开始,直到手动退出为止。

tips: pdb/ipdb 运行时可以使用 b line_number 来在指定行打上断点,然后再 c 跳到下一个断点处从而方便调试。

0X03 指定版本

我们都知道可以用 virtualenv 之类的东西处理 Python 的虚拟环境,但是还有这么一种简单粗犷的方式也可以凑合一用(尤其是同时存在 Python 2.7/3.7/3.8/3.9… 的这种环境)。

使用 python2.7 -m pip install requests 或者 python3.9 -m pip install xxx 可以指定具体的 Python 版本,如果你有多个虚拟环境也可以直接指定到 Python 的二进制文件,再通过 -m pip 的方式将所需的库安装到正确的位置~

0X04 一行流

这个「一行流」比较骚气,相当于把 Python 当成普通命令来用,简单的说就是 python -c "xxxxx" 可以在一行命令里执行一个 Python 脚本。例如有下面这些用法(其实就是把脚本写成一行)

比如说使用 python3 -c "import json; print(json.dumps(json.load(open('data.json')), indent=4))" 这样的方法可以直接将 json 文件格式化输出。(当然了这个需求其实有 jq 命令可以做的更好,只是这里举个例子而已)

one_liner

具体这个一行流的用法有多强,那就要看各位的脑洞了,唯一一个需要提示的就是 Python 其实是允许使用分号来终止一个语句的,类似 C 那样,所以才使得一行流可以实现~

0%