精子最近开发了两个小工具分别是 dir2casthomebrew-leftover-scanner

2022 年度小结

2022 年疫情防控愈演愈烈,我们从三月开始经历了两个多月足不出户的严格封控。上海的封城可以说是非常严格,在一开始的半个月中物资供应受到了不小的影响,无法买到自己想要的食物,而除此之外的非生活必需品,更是直到六月解封才恢复正常。就在这样荒诞、焦虑和对未来的不确定的情绪中,我们浑浑噩噩地度过了这两个月。

2022/2022-pcr.png

但好在封城的这段时间我下定了决心要把英语学好,相比于之前几年断断续续、晒网多过打渔的学习,今年虽算不上高强度,但至少是持续性地在学习。我大量地增加了英文内容的输入:全部改听英文播客、对于英文视频直接看原视频而不是翻译、阅读英文的技术书籍和博客。为了弥补我词汇量的短板开始改用英英字典,除了去查见到的每一个单词之外,也专门背了一些单词。我甚至在封城期间甚至报名了一个线上的英文课程,但随着封城的结束生活回到正规,也因为工作方面的变化,这个课程并没有上完。

封城结束后,上海则开始要求 72 小时的核酸阴性证明,我们也开始了差不多两天一次核酸检测的生活。在此之前我只在去年和公司一起去桂林时做过 2 次核酸检测,而在今年一年我们就做了超过 100 次的核酸检测。但即使是这样,因为奥密克戎极强的传染性,在接近年底时,「动态清零」变得越来越难以持续下去,终于在 12 月 7 日随着「新十条」的发布,接近三年的疫情防控措施划上了一个句号,大家很快地就回到了正常的生活轨迹。

在「新十条」发布后,我和蛋黄马上就计划了去沈阳的行程,在沈阳领了结婚证。因为我们彼此早已确认我们会一直生活在一起;因为无论是客观原因,还是我们自己都不希望婚姻是很多人说的「两个家庭的事情」 ,而就是我们两个人的事情;也因为我们都很清楚接下来我们会有更大的挑战要面对,所以结婚这件事更像是我们旅途上的一个小站点 —— 然后就在我还没来得及回味更多时,在沈阳的最后一天我开始发烧,第二天顶着高烧回到了家里,之后通过抗原检测 确认了是新冠感染,这趟行程是如此地匆忙,等回过神来已经到了 2023 年。

三年的疫情重塑了我们每个人对于「风险」的认知,有人会冒着隔离和封城的风险继续旅行,也有的人会选择留在熟悉的城市或稳定的工作。随着阅历的增加,我们能越来越全面地预料到一件事情的风险,或者说其实去做每件事情都是有风险的。总的来说我觉得我自己在这三年中表现得过于谨慎,而我本来不应该是这样一个畏首畏尾的人。

这一年工作上面也发生了很多变化,很多一起工作多年的同事离开了我们。在外部环境好的时候大家当然都很开心,但当大公司面临压力时,这种压力自上而下传导时就会走样,好不容易建立的文化也会很快地被破坏掉。总的来说这一年的工作让我觉得不是很有成就感,花费了很多时间在难以对自己、公司或社会产生价值的事情上。在接下来的一年中,我希望自己能将之前仍有维护价值的 side project 捡起来,同时在开始新的项目时能够快速试错,用上我这么多年积累的工程经验,看看能否在几年内实现我理想中的自由职业。

今年我购买了我的第一台可更换镜头的相机 EOS M6 Mark II 和几支镜头,也拍摄了很多照片。此前我的照片一直是放在 iCloud 上的,编辑、整理、同步的体验非常好,但随着照片接近 200G 的容量上限,我花了一些时间去 寻找基于 NAS 的管理方案。但这些方案和 Photos 的差距实在是太大,再加上 iCloud 发布了 Shared Photo Library 和 Advanced Data Protection,最后我还是选择了升到 2T 继续用,NAS 仅作为备份使用。在近几年我开始越来越喜欢拍照,除了每张照片都是一个创作的过程中之外,整个照片库也是一个有关数字化的回忆的作品,所以我一直很重视照片库的管理,也是为什么只有 Photos 能够达到我的需求。

此外今年还购买了我的第一个全面屏手机 iPhone 13 mini 和 Steam Deck,也遇到了 Persona 5 这样相见恨晚的游戏,算是这沉重的一年中的一点色彩。

如何进行技术面试(面试官视角)

我参加过几十场技术面试,其中作为面试官的次数要远多于候选人。

说起来在我第一次做面试官之前,并没有人教过我应该怎么做,我则一直将面试视作通过一小时左右的沟通,对候选人形成一个整体的印象,最后给出一个主观的评价的过程。在这么多次的面试中,我也总结出了一些经验可以和大家分享。

验证简历真实性

首先花一些时间聊一聊简历上提到的项目,请对方进一步介绍这个项目的业务、自己在其中承担的职责和遇到的问题。然后针对其中自己了解的部分提几个问题,如「据我所知这类项目的难点是某某方面,请问你是否有遇到、是如何解决的」,确认对方的项目经验是否真实、是否比较深度地参与了项目。

围绕简历提问

尽量提问对方了解和擅长的话题,让对方有足够的表达机会,发挥出正常水平,考察候选人擅长的部分要比不擅长的部分更有价值。

开放性问题

避免问有标准答案的问题,而是可以问「遇到某种情况时可能是什么原因、你会怎么做」或「请解释一下某个事物是如何工作的」这样开放性的问题,给对方足够的发挥空间、主动提到一些自己的知识和经验。

也许有些候选人不喜欢这样的问题,认为缺少安全感、不知道该说什么,但我觉得开放性问题才有足够的区分度,才能在短时间内对候选人形成立体的印象。我们可以在候选人实在不知道从何说起时,对问题再做进一步的解释和提示。

围绕话题由浅入深

应该针对同一话题准备多个难度逐步增加的问题,让对方回答时思路能够相对流畅,更容易考察对方在这一话题上的深度;反过来应该避免大量零散、无联系、无难度区分的小问题。

没必要执着于答案

有时在反复提示下对方的答案仍差那么一点,在面试时间大多比较紧张的情况下,我们没必要花更多的时间去让对方答出答案。在候选人表示无法准确回答问题时,也可以引导他做出一些合理的猜测(这时的重点在于猜测是否合理而不是是否正确)。只要候选人在这一问题上说足够多的话,我们就可以考察到其水平了,不一定要等他说出最后的答案。

自己不懂也没关系

在一开始,我会担心问出的问题我自己也不了解,对方回答之后会把自己问倒或者无法考察对方水平。但后来发现完全没有必要,因为面试官没有义务即时地对候选人的回答做出评价(告诉对方对还是错),即使对于不了解的话题,自己也可以从对方的自信程度、逻辑层面,对回答有一个大体的判断的。

现场编码可以很简单

候选人在现场会非常紧张,面试时间也有限,在现场编码环节没必要出太难的题,重点在于考察候选人是否有最基本编程思维和编码能力。例如我觉得一些难度不高,但适合用递归解决的题目会比较合适,可以考虑在候选人编码的过程中一直保持沟通,注重过程而不是结果。

同时我反对为候选人在面试前或面试后布置编码的作业,这会让候选人付出不对等的时间去准备面试,也很容易从「考察候选人的能力」变成「考察候选人的诚意」。

重复使用一套题库没什么问题

可以自己建立自己的面试题库,在一次次面试中对其进行反复地打磨,让问题更准确、考察更全面。有些话题可能是永远都不过时的,例如对于后端工程师可能是并发模型及与之相关的线程、内存等话题。

给对方提问的机会

一般面试的最后一个步骤是让候选人提问,不要把这个过程敷衍掉,可以引导对方提问,告诉对方都可以问哪些问题。如可以补充一下没有提及的技术话题、可以问我司的技术架构或选型、可以问所面试的职位和如果入职之后所参与的项目、可以问日常工作环境和时间等。这样可以让对方有一个主动打开话题的机会,互相判断需求是否匹配,即使没有通过面试也让对方对公司有一个比较好的印象。

有任何顾虑都可以 pass 掉

我们前面做了这么多都是为了让候选人有更好的面试体验、发挥出自己最好的水平。这种情况下如果仍对候选人某一方面的表现有顾虑的话,应该果断地拒绝掉,招进来一个不合适的人损失远比错过一个合适的人大。

沟通是否顺畅很重要

沟通是后续开展一切工作的基础,如果觉得与候选人的沟通不顺畅,对方总是不能理解自己的意思,那么即使候选人的技术水平满足要求,也应该慎重考虑。

留下面试的记录

我每次面试结束后都会尽快开始写对候选人的评价,大多在一两百字,先列出面试聊到的内容、觉得好和不好的地方、了解到的对方的技术栈,然后总结一下对候选人技术水平和沟通情况的评价。这样可以帮助自己整理思路,在候选人比较多的时候也不会记混,最后给出通过与否的评价,同时还能给下一轮的面试官提供参考。

为什么在 Apple Silicon 上装 Docker 这么难

最近公司的很多同事都换上了搭载 M1 Pro 或 M1 Max 的新款 MacBook Pro,虽然日常使用的软件如 Chrome、Visual Studio Code 和 Slack 都已经适配得很好了,但面对 Docker 却犯了难。

众所周知,Docker 用到了 Linux 的两项特性:namespaces 和 cgroups 来提供隔离与资源限制,因此无论如何在 macOS 上我们都必须通过一个虚拟机来使用 Docker。

在 2021 年 4 月时,Docker for Mac(Docker Desktop)发布了 对 Apple Silicon 的实验性支持,它会使用 QEMU 运行一个 ARM 架构的 Linux 虚拟机,默认运行 ARM 架构的镜像,但也支持运行 x86 的镜像。

2022/docker-for-mac.png

QEMU 是一个开源的虚拟机(Virtualizer)和仿真器(Emulator),所谓仿真器是说 QEMU 可以在没有来自硬件或操作系统的虚拟化支持的情况下,去模拟运行一台计算机,包括模拟与宿主机不同的 CPU 架构,例如在 Apple Silicon 上模拟 x86 架构的计算机。而在有硬件虚拟化支持的情况下,QEMU 也可以使用宿主机的 CPU 来直接运行,减少模拟运行的性能开销,例如使用 macOS 提供的 Hypervisor.Framework

Docker for Mac 其实就是分别用到了 QEMU 的这两种能力来在 ARM 虚拟机上运行 x86 镜像,和在 Mac 上运行 ARM 虚拟机。

Docker for Mac 确实很好,除了解决新架构带来的问题之外它还对文件系统和网络进行了映射,容器可以像运行在本机上一样访问文件系统或暴露网络端口到本机,几乎感觉不到虚拟机的存在。但 LeanCloud 加入 TapTap 之后已经不是小公司了,按照 Docker Desktop 在 2021 年 8 月推出的 新版价格方案,我们每个人需要支付至少 $5 每月的订阅费用。倒不是我们不愿意付这个钱,只是我想要找一找开源的方案。

之前在 Intel Mac 上,我们会用 Vagrant 或 minikube 来创建虚拟机,它们底层会使用 VirtualBox 或 HyperKit 来完成实际的虚拟化。但 VirtualBox 和 HyperKit 都没有支持 Apple Silicon 的计划。实际上目前开源的虚拟化方案中只有 QEMU 对 Apple Silicon 有比较好的支持,QEMU 本身只提供命令行的接口,例如 Docker for Mac 调用 QEMU 时的命令行参数是这样:

/Applications/Docker.app/Contents/MacOS/qemu-system-aarch64 -accel hvf \
-cpu host -machine virt,highmem=off -m 2048 -smp 5 \
-kernel /Applications/Docker.app/Contents/Resources/linuxkit/kernel \
-append linuxkit.unified_cgroup_hierarchy=1 page_poison=1 vsyscall=emulate \
panic=1 nospec_store_bypass_disable noibrs noibpb no_stf_barrier mitigations=off \
vpnkit.connect=tcp+bootstrap+client://192.168.65.2:61473/f1c4db329a4a520d73a79eaa1360de7be7d09948a1ac348b04c8e01f6f6eb2c9 \
console=ttyAMA0 -initrd /Applications/Docker.app/Contents/Resources/linuxkit/initrd.img \
-serial pipe:/var/folders/12/_bbrd4692hv8r9bx_ggw5kp80000gn/T/qemu-console1367481183/fifo \
-drive if=none,file=/Users/ziting/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw,format=raw,id=hd0 \
-device virtio-blk-pci,drive=hd0,serial=dummyserial -netdev socket,id=net1,fd=3 -device virtio-net-device,netdev=net1,mac=02:50:00:00:00:01 \
-vga none -nographic -monitor none

为了实际使用 QEMU 进行开发,我们需要一个使用上更友好的封装,能够自动配置好 Docker 和 Kubernetes(或者至少方便编写像 Vagrantfile 一样的脚本),提供类似 Docker for Mac 的网络映射和文件映射,于是我找到了 Lima。

Lima 自称是 macOS 上的 Linux 子系统(macOS subsystem for Linux),它使用 QEMU 运行了一个 Linux 虚拟机,其中安装有 rootless 模式的 containerd,还通过 SSH 提供了文件映射和自动的端口转发。

但为什么是 containerd 而不是 Docker 呢?随着容器编排平台 Kubernetes 如日中天,社区希望将运行容器这个关键环节进行标准化,让引入 Docker 之外的其他容器运行时更加容易,于是 推出了 Container Runtime Interface (CRI)。containerd 就是从 Docker 中拆分出的一个 CRI 的实现,相比于 Docker 本体更加精简,现在也交由社区维护。

因此如 Lima 这样新的的开源软件会更偏好选择 containerd 来运行容器,因为组件更加精简会有更好的性能,也不容易受到 Docker 产品层面变化的影响。nerdctl 是与 containerd 配套的命令行客户端(nerdcontainerd 的末尾 4 个字母),用法与 docker 或 docker-compose 相似(但并不完全兼容)。

所谓 rootless 则是指通过替换一些组件,让容器运行时(containerd)和容器都运行在非 root 用户下,每个用户都有自己的 containerd,这样绝大部分操作都不需要切换到 root 来进行,也可以减少安全漏洞的攻击面。

但我们希望能在本地运行完整的 rootful 模式的 dockerd 和 Kubernetes 来尽可能地模拟真实的线上环境,好在 Lima 提供了丰富的 自定义能力,我基于社区中的一些脚本(docker.yamlminikube.yaml)实现了我们的需求,而且这些自定义的逻辑都被以脚本的形式写到了 yaml 描述文件中,只需一条命令就可以创建出相同的虚拟机。

~ ❯ limactl start docker.yaml
? Creating an instance "docker" Proceed with the default configuration
INFO[0005] Attempting to download the image from "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-arm64.img"
INFO[0005] Using cache "/Users/ziting/Library/Caches/lima/download/by-url-sha256/ae20df823d41d1dd300f8866889804ab25fb8689c1a68da6b13dd60a8c5c9e35/data"
INFO[0006] [hostagent] Starting QEMU (hint: to watch the boot progress, see "/Users/ziting/.lima/docker/serial.log")
INFO[0006] SSH Local Port: 55942
INFO[0006] [hostagent] Waiting for the essential requirement 1 of 5: "ssh"
INFO[0039] [hostagent] Waiting for the essential requirement 2 of 5: "user session is ready for ssh"
INFO[0039] [hostagent] Waiting for the essential requirement 3 of 5: "sshfs binary to be installed"
INFO[0048] [hostagent] Waiting for the essential requirement 4 of 5: "/etc/fuse.conf to contain \"user_allow_other\""
INFO[0051] [hostagent] Waiting for the essential requirement 5 of 5: "the guest agent to be running"
INFO[0051] [hostagent] Mounting "/Users/ziting"
INFO[0051] [hostagent] Mounting "/tmp/lima"
INFO[0052] [hostagent] Forwarding "/run/lima-guestagent.sock" (guest) to "/Users/ziting/.lima/docker/ga.sock" (host)
INFO[0092] [hostagent] Waiting for the optional requirement 1 of 1: "user probe 1/1"
INFO[0154] [hostagent] Forwarding TCP from [::]:2376 to 127.0.0.1:2376
INFO[0304] [hostagent] Forwarding TCP from [::]:8443 to 127.0.0.1:8443
INFO[0332] [hostagent] Waiting for the final requirement 1 of 1: "boot scripts must have finished"
INFO[0351] READY. Run `limactl shell docker` to open the shell.
INFO[0351] To run `docker` on the host (assumes docker-cli is installed):
INFO[0351] $ export DOCKER_HOST=tcp://127.0.0.1:2376
INFO[0351] To run `kubectl` on the host (assumes kubernetes-cli is installed):
INFO[0351] $ mkdir -p .kube && limactl cp minikube:.kube/config .kube/config

我还发现了另外一个基于 Lima 的封装 —— Colima,默认提供 rootful 的 dockerd 和 Kubernetes,但 Colima 并没有对外暴露 Lima 强大的自定义能力,因此我们没有使用,但对于没那么多要求的开发者来说,也是一个更易用的选择。

在默认的情况下,Lima 中的 Docker 在 Apple Silicon 上只能运行 ARM 架构的镜像,但就像前面提到的那样,我们可以使用 QEMU 的模拟运行的能力来运行其他架构(如 x86)的容器。qemu-user-static 是一个进程级别的模拟器,可以像一个解释器一样运行其他架构的可执行文件,我们可以利用 Linux 的一项 Binfmt_misc中文版)的特性让 Linux 遇到特定架构的可执行文件时自动调用 qemu-user-static,这种能力同样适用于容器中的可执行文件。

社区中也有 qus 这样的项目,对这些能力进行了封装,只需执行一行 docker run --rm --privileged aptman/qus -s -- -p x86_64 就可以让你的 ARM 虚拟机魔法般地支持运行 x86 的镜像。

/usr/bin/containerd-shim-runc-v2
\_ /qus/bin/qemu-x86_64-static /usr/sbin/nginx -g daemon off;
    \_ /qus/bin/qemu-x86_64-static /usr/sbin/nginx -g daemon off;
    \_ /qus/bin/qemu-x86_64-static /usr/sbin/nginx -g daemon off;
    \_ /qus/bin/qemu-x86_64-static /usr/sbin/nginx -g daemon off;
    \_ /qus/bin/qemu-x86_64-static /usr/sbin/nginx -g daemon off;

使用 qus 运行 x86 镜像的进程树如上,所有进程(包括创建出的子进程)都自动通过 QEMU 模拟运行。

回到题目中的问题,因为 Docker 依赖于 Linux 内核的特性,所以在 Mac 上必须通过虚拟机来运行;Apple Silicon 作为新的架构,虚拟机的选择比较受限,因为有些镜像并不提供 ARM 架构的镜像,所以有时还有模拟运行 x86 镜像的需求;Docker Desktop 作为商业产品,有足够的精力来去解决这些「脏活累活」,但它在这个时间点选择不再允许所有人免费使用;开源社区中新的项目都希望去 Docker 化,用 containerd 取代 dockerd,但这又带来了使用习惯的变化并且可能与线上环境不一致。因为这些原因,目前在 Apple Silicon 上安装 Docker 还是需要花一些时间去了解背景知识的,但好在依然有这些优秀的开源项目可供选择。

虽然 云引擎 也是基于 Docker 等容器技术构建的,但云引擎力图为用户提供开箱即用的使用体验而不必自己配置容器环境、编写构建脚本、收集日志和统计数据。如果想得到容器化带来的平滑部署、快速回滚、自动扩容等好处但又不想花时间配置,不如来试试云引擎。

其他参考资料:

12383

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过邮件订阅精子的博客日志、产品和项目的最新动态,精子承诺每一封邮件都会认真撰写(历史邮件),有想和精子说的话也可以直接回复邮件。

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2023-05-08.