默认的情况下,k8s 对于 pod 在单个节点的资源分配并不会考虑到 NUMA 架构。比如 cpu 默认会采用 cgroup CFS 来做资源的分配,并未考虑到 NUMA 架构。为了提升 pod 的性能,需要 pod 在分配资源时感知 NUMA 架构。 为此,k8s 在 kubelet 中通过 CPU Manager、Memory Manager、Device Manager、Topology Manager 等特性对 NUMA 做了支持,支持的 pod QoS 类型要求为 Granteed pod。 各特性的支持版本情况如下:
特性
alpha
beta
stable
CPU Manager
1.12
1.26
Memory Manager
1.21
1.22
-
Topology Manager
1.16
1.18
-
CPU Manager
在 k8s 中使用 cgroup 的 CFS 配额来执行 pod 的 CPU 约束,在 CFS 的模式下,pod 可能会运行在不同的核上,会导致 pod 的缓存失效的问题。对于性能要求非常高的 pod,为了提升性能,可以通过 cgroup 中的 cpuset 绑核的特性来提升 pod 的性能。
Mar 06 15:00:02 iZt4nd5yyw9vfuxn3q2g3tZ kubelet[102800]: E0306 15:00:02.463939 102800 cpu_manager.go:223] "Could not initialize checkpoint manager, please drain node and remove policy state file" err="could not restore state from checkpoint: configured policy \"static\" differs from state checkpoint policy \"none\", please drain this node and delete the CPU manager checkpoint file \"/var/lib/kubelet/cpu_manager_state\" before restarting Kubelet" Mar 06 15:00:02 iZt4nd5yyw9vfuxn3q2g3tZ kubelet[102800]: E0306 15:00:02.463972 102800 kubelet.go:1392] "Failed to start ContainerManager" err="start cpu manager error: could not restore state from checkpoint: configured policy \"static\" differs from state checkpoint policy \"none\", please drain this node and delete the CPU manager checkpoint file \"/var/lib/kubelet/cpu_manager_state\" before restarting Kubelet"
// TopologyHint is a struct containing the NUMANodeAffinity for a Container type TopologyHint struct { // 记录了 NUMA Node 满足资源请求的位掩码 NUMANodeAffinity bitmask.BitMask // Preferred is set to true when the NUMANodeAffinity encodes a preferred // allocation for the Container. It is set to false otherwise. // 亲和性的结果是否为首选的 Preferred bool }
// HintProvider is an interface for components that want to collaborate to // achieve globally optimal concrete resource alignment with respect to // NUMA locality. type HintProvider interface { // GetTopologyHints returns a map of resource names to a list of possible // concrete resource allocations in terms of NUMA locality hints. Each hint // is optionally marked "preferred" and indicates the set of NUMA nodes // involved in the hypothetical allocation. The topology manager calls // this function for each hint provider, and merges the hints to produce // a consensus "best" hint. The hint providers may subsequently query the // topology manager to influence actual resource assignment. GetTopologyHints(pod *v1.Pod, container *v1.Container) map[string][]TopologyHint // GetPodTopologyHints returns a map of resource names to a list of possible // concrete resource allocations per Pod in terms of NUMA locality hints. GetPodTopologyHints(pod *v1.Pod) map[string][]TopologyHint // Allocate triggers resource allocation to occur on the HintProvider after // all hints have been gathered and the aggregated Hint is available via a // call to Store.GetAffinity(). Allocate(pod *v1.Pod, container *v1.Container) error }
CPU Manager、Memory Manager 和 Device Manager 均实现了该接口。在 Topology Manager 中根据各个 Manager 返回的 TopologyHint 数据,从而决定最终的 NUMA Node 分配,并调用各个 Manager 的 Allocate 来做最终的 NUMA Node 分配。
其中的 Subject 中的 O 对应的 k8s 中的 Group,CN 对应的 k8s 中的 User。kube-apiserver 会通过证书的 O 和 CN 获取到 User 和 Group 信息。在 k8s 系统中,实际上并没有存储 Group 和 User 信息,而是完全依赖该证书中的信息。
有了遥控器按键和编码之间的映射关系后,需要将其放到 Android TV 系统的 /boot/rc_keymap.txt 文件中,让 Android TV 开启自动加载该配置。该文件默认情况下不存在需要创建。
分区 /boot 默认为只读模式,不允许在其中增加文件:
1 2
127|:/boot # echo "123" > aa sh: can't create aa: Read-only file system
将 /boot 目录重新 mount 为读写模式:
1 2 3 4 5 6 7
$ mount | grep '/boot ' /dev/block/mmcblk0p1 on /boot type vfat (ro,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
$ mount -o remount,rw /boot
$ mount | grep '/boot ' /dev/block/mmcblk0p1 on /boot type vfat (rw,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
找到树莓派对应的 Android TV 镜像是非常重要的前提,找到合适的 ROM 永远是 Android 系统刷机中非常重要的一环,一般每个设备总有那么几个大神在提供各类 ROM。 查找一个设备的资源最快的方式就是去 github.com 上查找 awesome,树莓派的项目地址为:awesome-raspberry-pi。在 OS Images 章节中包含了支持树莓派的多种 OS,单纯搜索 Android TV 并不能找到对应的 OS,但实际上 KonstaKANG 对应就是 Android 镜像,这是文档不好的地方,并没有将简介写清楚。
KonstaKANG 并非一个系统,而是一个网站,包含了多种设备的 OS 镜像。对应的 Raspberry 5 的页面包含了 AOSP 和 LineageOS 两种 OS 镜像,AOSP 和 LineageOS 均为 android 的发型版。但只提供了 LineageOS 20 一个 Android TV 的版本,剩下的两个为 Android 版本。Android TV 和 Android 并非同一个系统,Android TV 是针对电视使用的系统,而 Android 是针对智能手机和平板使用的系统,用户体验上还是有较大区别。
因此,LineageOS 20 Android TV (Android 13) 即变为了目前唯一一个可以在树莓派 5 上使用 Android TV 系统,除此之外,别无他选。无论该系统是否完善,这就是目前的唯一选择了。在文章中介绍了非常多的系统方面的支持,建议精读一遍,包括文章后面的评论。 文中的两个镜像,一个是原始镜像,另外一个是 ota 补丁包,两个均需要安装。
Google Apps 包为必须用到的包,通过 Recovery 模式刷入包 MindTheGapps-13.0.0-arm64-ATV-full-20240104_210039.zip。包安装完成后,重新进入系统发现界面发生了变化,多出了应用 Google Play Store,左上角的语音和搜索功能虽然不可以用,但是点击后提示信息已经发生了变化。
原来的文件应用在这里消失不见了,实际上在所有应用中还可以找到。
在系统中使用 Google 账号登录 Google Play Store,即使在可以访问 Google 的网络下,发现也一直会失败。
查询并注册 Android ID
因为该 Android TV 设备并不被信任,需要将 Android ID 在 Android 网站注册。如果不注册,那么 Google 账号登录不成功。 将树莓派的 SD 卡插入到笔记本,查看 SD 卡中的文件 gsf-android_id.txt,该文件对应的内容即为 Android ID。 还有另外一种办法可以获取到 Android ID,在笔记本上通过 adb 命令查询到当前设备的 Android ID。
1 2 3 4 5 6 7
adb root # 找到 google service 的 sqlite3 数据库文件 adb shell 'find /data -name "gservices.db"'
# 通过数据库查询到 android id # 其中 sqlite3 命令后的为上面步骤查询出的文件路径,如果查询出多个,可以任选一个 adb shell 'sqlite3 /data/data/com.google.android.gsf/databases/gservices.db "select * from main where name = \"android_id\";"'
通过 Recovery 模式刷入包 lineage-20.0-rpi-magisk-v25.2.zip,进入到 Android TV 系统后通过文件工具安装包 Magisk-v25.2.apk。
解决网络连接受限
如果本地的网络无法访问 Google,默认情况下,网络会提示网络连接受限,原因主要还是跟访问不了 Google 的域名有关,以至于 Android TV 系统不能识别出可以连接互联网。
网络连接受限状态的 WIFI,经测试机器重启后无法自动连接,需要每次都手工连接 WIFI。
执行如下的命令来系统进行设置:
1 2 3 4 5 6 7 8 9 10
adb connect 192.168.31.166
# 设置时间服务器 adb shell settings put global ntp_server ntp1.aliyun.com
# 该值默认为 0 adb shell settings put global captive_portal_detection_enabled 1
# 默认没有这两个值 adb shell settings put global captive_portal_https_url https://connect.rom.miui.com/generate_204
设置完成后重启系统,即可看到网络的连接受限已经消除,并且 WIFI 已经可以自动连接了。
常用 apk 软件安装
我这里使用了 https://kxsw.gitbook.io/tv/ 中的方法安装了 File Commands 和 Clash 软件。File Commands 可以用来管理本地的文件,甚至可以提供 HTTP Server,供远程来下载或者上传文件。 国内的常见应用在 Google Play Store 中并不存在,而且通过 Google Play Store 直接安装应用很可能会失败,跟使用的网络有很大关系。国内的应用我直接使用了当贝市场来安装电视应用即可。
在电脑的浏览器上登录红米路由器的管理页面,红米路由器管理页面为:https://miwifi.com/,或者 http://192.168.31.1/。如果本地设置过其他的 DNS 服务器,需要使用 ip 地址的形式访问。 登录后可以获取到当前红米路由器的版本,我的已经是最新的 1.0.67,该版本的固件可以支持开启 SSH 协议。
# 开启 ssh 服务 nvram set ssh_en=1 nvram set telnet_en=1 nvram set uart_en=1 nvram set boot_wait=on nvram commit
sed -i 's/channel=.*/channel="debug"/g' /etc/init.d/dropbear /etc/init.d/dropbear restart
# 设置 ssh 服务开机自启动 mkdir /data/auto_ssh cd /data/auto_ssh curl -O https://fastly.jsdelivr.net/gh/lemoeo/AX6S@main/auto_ssh.sh chmod +x auto_ssh.sh uci set firewall.auto_ssh=include uci set firewall.auto_ssh.type='script' uci set firewall.auto_ssh.path='/data/auto_ssh/auto_ssh.sh' uci set firewall.auto_ssh.enabled='1' uci commit firewall
# 设置时区 uci set system.@system[0].timezone='CST-8' uci set system.@system[0].webtimezone='CST-8' uci set system.@system[0].timezoneindex='2.84' uci commit