蚊章汇 - Chris Collins
type: channel
link: https://linuxcn.bwsl.wang/author/935/
description: Chris Collins
items: 使用树莓派做一个倒计时器
items: 使用 Go 和树莓派排查 WiFi 问题
items: 探索 Kubernetes 生态系统（2021 版）
items: 用树莓派构建 Kubernetes 集群
items: 把你的树莓派家庭实验室变成一个网络文件系统
items: 使用 Cloud-init 将节点添加到你的私有云中
items: 在你的树莓派家庭实验室中使用 Cloud-init
items: 修改磁盘镜像来创建基于树莓派的家庭实验室
items: 如何用 GVM 管理 Go 项目
items: 如何打造更小巧的容器镜像

使用树莓派做一个倒计时器
type: item
date: 2022-06-19T22:51:37+08:00
description: 使用树莓派和电子纸显示屏开始倒计时你的下一个假期。 圆周率日 Pi Day （3 月 14 日） 来了又走，留下美好的回忆以及 许多树莓派项目 等待我们去尝试。在任何令人精神振奋、充满欢乐的假期后回到工作中都很难，圆周率日也不例外。当我们回望三月的时候，渴望那些天的快乐。但是不用害怕，亲爱的圆周率日庆祝者们，我们开始下一个节日的漫长倒计时！好了，严肃点。我做了一个圆周率日倒计时器，你也可以！不久前，我购买了一个 树莓派 Zero W，并且用它来 解决 WiFi 信号较差的原因 。我也对使用 电子纸 ePaper 来作为它的显示屏十分感兴趣。虽然我不知道该用它来干什么，但是！它看起来真的很有趣！我买了一个十分适合放在树莓派的顶部的 2.13 英寸的 WaveShare 显示器 。安装很简单：只需要将显示器接到树莓派的 GPIO 上即可。我使用 树莓派操作系统 来实现该项目，虽然其他的操作系统肯定也能完成。但是下面的 raspi-config 命令在树莓派系统上很容易使用。设置树莓派和电子纸显示屏设置树莓派和电子纸显示屏一起工作，需要你在树莓派软件中启用串行外设接口（SPI），安装 BCM2835 C 库（来访问树莓派上的博通 BCM 2835 芯片的 GPIO 功能），安装 Python GPIO 库来控制电子纸显示屏。最后，你需要安装 WaveShare 的库来使用 Python 控制这个 2.13 英寸的显示屏。下面是完成这些的步骤。启用 SPI树莓派上启用 SPI 最简单的方式是使用 raspi-config 命令。SPI 总线允许与设备进行串行数据通信——在本例中，电子纸显示：$ sudo raspi-config 从弹出的菜单中, 选择 “ 接口选项 Interfacing Options -> SPI -> 是 Yes ” 来启用 SPI 接口，然后启动。安装 BCM2835 库如上所述，BCM2835 库是用于树莓派博通 BCM2385 芯片的软件，它允许访问 GPIO 引脚来控制设备。在我写这篇文章之时，用于树莓派的最新博通 BCM2385 库版本是 v1.68 。安装此库需要下载软件压缩包然后使用 make 来安装：# 下载 BCM2853 库并解压 $ curl -sSL http://www.airspayce.com/mikem/bcm2835/bcm2835-1.68.tar.g> -o - | tar -xzf - # 进入解压后的文件夹 $ pushd bcm2835-1.68/ # 配置、检查并安装 BCM2853 库 $ sudo ./configure $ sudo make check $ sudo make install # 返回上级目录 $ popd 安装需要的 Python 库你用 Python 控制电子纸显示屏需要安装 Python 库 RPi.GPIO，还需要使用 python3-pil 包来画图。显然，PIL 包已经不行了，但 Pillow 可以作为代替方案。我还没有为该项目测试过 Pillow ，但它可行：# 安装需要的 Python 库 $ sudo apt-get update $ sudo apt-get install python3-pip python3-pil $ sudo pip3 install RPi.GPIO 注意：这些是 Python3 的指令。你可以在 WaveShare 网站查到 Python2 的指令。下载 WaveShare 示例和 Python 库Waveshare 维护了一个 Python 和 C 的 Git 库，用于使用其电子纸显示屏和一些展示如何使用它们的示例。对这个倒计时时钟而言，你需要克隆这个库并使用用于 2.13 英寸显示屏的库：# 克隆这个 WaveShare e-Paper git 库 $ git clone https://github.com/waveshare/e-Paper.gi> 如果你用不同的显示器或者其他公司产品，需要使用适配软件。Waveshare 提供了很多指导：WaveShare 电子纸设置指导WaveShare 电子纸库安装指导获得有趣的字体（选做）你可以随心所欲的使用显示器，为什么不搞点花样？找一个炫酷的字体！这有大量 开放字体许可 的字体可供选择。我十分喜爱 Bangers 字体。如果你看过 YouTube 那你见过这种字体了，它十分流行。你可以下载到本地的共享字体目录文件中，并且所有的应用都可以使用，包括这个项目：# “Bangers” 字体是 Vernon Adams 使用 Google 字体开放许可授权的字体 $ mkdir -p ~/.local/share/fonts $ curl -sSL…

使用 Go 和树莓派排查 WiFi 问题
type: item
date: 2022-05-08T16:50:28+08:00
description: 实现一个 WiFi 扫描器玩玩～去年夏天，我和妻子变卖了家产，带着我们的两只狗移居了夏威夷。这里有美丽的阳光、温暖的沙滩、凉爽的冲浪等你能想到的一切。我们同样遇到了一些意料之外的事：WiFi 问题。不过，这不是夏威夷的问题，而是我们租住公寓的问题。我们住在一个单身公寓里，与房东的公寓仅一墙之隔。我们的租房协议中包含了免费的网络连接！好耶！只不过，它是由房东的公寓里的 WiFi 提供的，哇哦……说实话，它的效果还不错……吧？好吧，我承认它不尽如人意，并且不知道是哪里的问题。路由器明明就在墙的另一边，但我们的信号就是很不稳定，经常会自动断开连接。在家的时候，我们的 WiFi 路由器的信号能够穿过层层墙壁和地板。事实上，它所覆盖的区域比我们居住的 600 平方英尺（大约 55 平方米）的公寓还要大。在这种情况下，一个优秀的技术人员会怎么做呢？既然想知道为什么，当然是开始排查咯！幸运的是，我们在搬家之前并没有变卖掉树莓派 Zero W。它是如此小巧便携! 我当然就把它一起带来了。我有一个机智的想法：通过树莓派和它内置的 WiFi 适配器，使用 Go 语言编写一个小程序来测量并显示从路由器收到的 WiFi 信号。我打算先简单快速地把它实现出来，以后再去考虑优化。真是麻烦！我现在只想知道这个 WiFi 是怎么回事！谷歌搜索了一番后，我发现了一个比较有用的 Go 软件包 mdlayher/wifi，它专门用于 WiFi 相关操作，听起来很有希望！获取 WiFi 接口的信息我的计划是查询 WiFi 接口的统计数据并返回信号强度，所以我需要先找到设备上的接口。幸运的是，mdlayher/wifi 包有一个查询它们的方法，所以我可以创建一个 main.go 来实现它，具体代码如下：package main import ( "fmt" "github.com/mdlayher/wifi" ) func main() { c, err := wifi.New() defer c.Close() if err != nil { panic(err) } interfaces, err := c.Interfaces() for _, x := range interfaces { fmt.Printf("%+v\n", x) } } 让我们来看看上面的代码都做了什么吧！首先是导入依赖包，导入后，我就可以使用 mdlayher/wifi 模块就在 main 函数中创建一个新的客户端（类型为 *Client）。接下来，只需要调用这个新的客户端（变量名为 c）的 c.Interfaces() 方法就可以获得系统中的接口列表。接着，我就可以遍历包含接口指针的切片（变长数组），然后打印出它们的具体信息。注意到 %+v 中有一个 + 了吗？它意味着程序会详细输出 *Interface 结构体中的属性名，这将有助于我标识出我看到的东西，而不用去查阅文档。运行上面的代码后，我得到了机器上的 WiFi 接口列表：&{Index:0 Name: HardwareAddr:5c:5f:67:f3:0a:a7 PHY:0 Device:3 Type:P2P device Frequency:0} &{Index:3 Name:wlp2s0 HardwareAddr:5c:5f:67:f3:0a:a7 PHY:0 Device:1 Type:station Frequency:2412} 注意，两行输出中的 MAC 地址（HardwareAddr）是相同的，这意味着它们是同一个物理硬件。你也可以通过 PHY: 0 来确认。查阅 Go 的 wifi 模块文档，PHY 指的就是接口所属的物理设备。第一个接口没有名字，类型是 TYPE: P2P。第二个接口名为 wpl2s0，类型是 TYPE: Station。wifi 模块的文档列出了 不同类型的接口，以及它们的用途。根据文档，P2P（点对点传输） 类型表示“该接口属于点对点客户端网络中的一个设备”。我认为这个接口的用途是 WiFi 直连 ，这是一个允许两个 WiFi 设备在没有中间接入点的情况下直接连接的标准。Station（基站）类型表示“该接口是具有 控制接入点 controlling access point 的客户端设备管理的 基本服务集 basic service set （BSS）的一部分”。这是大众熟悉的无线设备标准功能：作为一个客户端来连接到网络接入点。这是测试 WiFi 质量的重要接口。利用接口获取基站信息利用该信息，我可以修改遍历接口的代码来获取所需信息：for _, x := range interfaces { if x.Type == wifi.InterfaceTypeStation { // c.S…

探索 Kubernetes 生态系统（2021 版）
type: item
date: 2021-06-22T00:26:22+08:00
description: 这份可下载的指南充满了有用的教程，让 SRE 和系统管理员使用 Kubernetes 获得便利。Kubernetes 是容器编排的事实标准，在基础设施管理和应用开发方面已经迅速发展成为容器环境的主导。作为一个拥有庞大的爱好者和专业人士社区的开源平台，以及作为云原生计算基金会的一部分，Kubernetes 不仅成为一个强大而令人印象深刻的编排系统本身，而且它还促进了一个庞大的相关工具和服务的生态系统，使其更容易使用，并通过更强大和复杂的组件扩展其功能。在这本新的电子书《给 SRE 和系统管理员的 Kubernetes 指导》中，Jess Cherry（Ben Finkel 也有贡献）涵盖了一系列用于管理和整合 Kubernetes 的工具和服务。Cherry 和 Finkel 提供了一些有用的 入门 指南，包括 Kubernetes 和一些工具。他们甚至还分享了面试问题，以帮助读者为在这个快速增长的大规模生态系统中工作做好准备。了解 Kubernetes如果你刚开始接触 Kubernetes 和容器，Ben Finkel 的 《Kubernetes 入门》文如其题，也是一篇对你需要了解的相关概念的出色介绍。它也是一本轻量级的快速入门指南，用于设置和使用单节点集群进行测试。没有什么比亲身体验技术并直接进入学习更好的方法了。什么是 吊舱 Pod ？ 如何在集群上部署一个应用程序？ Ben 一一为你做了介绍。与集群交互的主要方式是 kubectl 命令，这是一种 CLI 工具，它提供了一种与管理集群本身的 API 服务器交互的适合方式。例如，你可以使用 kubectl get 来列出上述的吊舱和部署，但正如你对 Kubernetes 这样复杂的东西所期望的那样，它的 CLI 界面有很强的功能和灵活性。Jess Cherry 的《9 个系统管理员需要知道的 kubectl 命令》速查表是一个很好的介绍，是使用 kubectl 的入门好方法。同样，Cherry 的《给初学者的 Kubernetes 命令空间》也很好地解释了什么是命名空间以及它们在 Kubernetes 中的使用方式。简化 Kubernetes 的工作在一个复杂的系统中工作是很困难的，尤其是使用像 kubectl 这样强大而极简的 CLI 工具。幸运的是，在围绕 Kubernetes 的生态系统中，有许多工具可用于简化事情，使扩展服务和集群管理更容易。可用于在 Kubernetes 上部署和维护应用和服务的 kubectl 命令主要使用的是 YAML 和 JSON。然而，一旦你开始管理更多应用，用 YAML 的大型仓库这样做会变得既重复又乏味。一个好的解决方案是采用一个模板化的系统来处理你的部署。Helm 就是这样一个工具，被称为 “Kubernetes 的包管理器”，Helm 提供了一种方便的方式来打包和共享应用。Cherry 写了很多关于 Helm 的有用文章：创建有效的 《Helm 海图》和有用的《Helm 命令》。kubectl 也为你提供了很多关于集群本身的信息：上面运行的是什么，以及正在发生的事件。这些信息可以通过 kubectl 来查看和交互，但有时有一个更直观的 GUI 来进行交互是有帮助的。K9s 符合这两个世界的要求。虽然它仍然是一个终端应用，但它提供了视觉反馈和一种与集群交互的方式，而不需要长长的 kubectl 命令。Cherry 也写了一份很好的《k9s 使用入门》的指南。建立在 Kubernetes 的强大和灵活性之上的扩展幸运的是，尽管 Kubernetes 是复杂而强大的，但它惊人的灵活并且开源。它专注于其核心优势：容器编排，并允许围绕它的爱好者和专业人士的社区扩展其能力，以承担不同类型的工作负载。其中一个例子是 Knative，在 Kubernetes 之上提供组件，它为无服务器和事件驱动的服务提供工具，并利用 Kubernetes 的编排能力在容器中运行最小化的微服务。事实证明，这样做非常高效，既能提供在容器中开发小型、易于测试和维护的应用的好处，又能提供仅在需要时运行这些应用的成本优势，可以在特定事件中被触发，但在其他时候处于休眠。在这本电子书中，Cherry 介绍了 Knative 和它的事件系统，以及为什么值得自己研究使用 Knative。有一个完整的世界可以探索通过 Jess Cherry 和 Ben Finkel 的这本新的电子书，可以开始了解 Kubernetes 和围绕它的生态系统。除了上述主题外，还有一些关于有用的 Kubernetes 扩展和第三方工具的文章。via: https://opensource.com/article/21/6/kubernetes-ebook作者：Chris Collins 选题：lujun…

用树莓派构建 Kubernetes 集群
type: item
date: 2020-07-16T07:43:26+08:00
description: 将 Kubernetes 安装在多个树莓派上，实现自己的“家庭私有云”容器服务。Kubernetes 从一开始就被设计为云原生的企业级容器编排系统。它已经成长为事实上的云容器平台，并由于接受了容器原生虚拟化和无服务器计算等新技术而继续发展。从微型的边缘计算到大规模的容器环境，无论是公有云还是私有云环境，Kubernetes 都可以管理其中的容器。它是“家庭私有云”项目的理想选择，既提供了强大的容器编排，又让你有机会了解一项这样的技术 —— 它的需求如此之大，与云计算结合得如此彻底，以至于它的名字几乎就是“云计算”的代名词。没有什么比 Kubernetes 更懂“云”，也没有什么能比树莓派更合适“集群起来”！在廉价的树莓派硬件上运行本地的 Kubernetes 集群是获得在真正的云技术巨头上进行管理和开发的经验的好方法。在树莓派上安装 Kubernetes 集群本练习将在三个或更多运行 Ubuntu 20.04 的树莓派 4 上安装 Kubernetes 1.18.2 集群。Ubuntu 20.04（Focal Fossa）提供了针对 64 位 ARM（ARM64）的树莓派镜像（64 位内核和用户空间）。由于目标是使用这些树莓派来运行 Kubernetes 集群，因此运行 AArch64 容器镜像的能力非常重要：很难找到 32 位的通用软件镜像乃至于标准基础镜像。借助 Ubuntu 20.04 的 ARM64 镜像，可以让你在 Kubernetes 上使用 64 位容器镜像。AArch64 vs. ARM64；32 位 vs. 64 位；ARM vs. x86请注意，AArch64 和 ARM64 实际上是同一种东西。不同的名称源于它们在不同社区中的使用。许多容器镜像都标为 AArch64，并能在标为 ARM64 的系统上正常运行。采用 AArch64/ARM64 架构的系统也能够运行 32 位的 ARM 镜像，但反之则不然：32 位的 ARM 系统无法运行 64 位的容器镜像。这就是 Ubuntu 20.04 ARM64 镜像如此有用的原因。这里不会太深入地解释不同的架构类型，值得注意的是，ARM64/AArch64 和 x86\_64 架构是不同的，运行在 64 位 ARM 架构上的 Kubernetes 节点无法运行为 x86\_64 构建的容器镜像。在实践中，你会发现有些镜像没有为两种架构构建，这些镜像可能无法在你的集群中使用。你还需要在基于 Arch64 的系统上构建自己的镜像，或者跳过一些限制以让你的常规的 x86\_64 系统构建 Arch64 镜像。在“家庭私有云”项目的后续文章中，我将介绍如何在常规系统上构建 AArch64 镜像。为了达到两全其美的效果，在本教程中设置好 Kubernetes 集群后，你可以在以后向其中添加 x86\_64 节点。你可以通过使用 Kubernetes 的 污点 taint 和 容忍 toleration 能力，由 Kubernetes 的调度器将给定架构的镜像调度到相应的节点上运行。关于架构和镜像的内容就不多说了。是时候安装 Kubernetes 了，开始吧！前置需求这个练习的要求很低。你将需要：三台（或更多）树莓派 4（最好是 4GB 内存的型号）。在全部树莓派上安装 Ubuntu 20.04 ARM64。为了简化初始设置，请阅读《修改磁盘镜像来创建基于树莓派的家庭实验室》，在将 Ubuntu 镜像写入 SD 卡并安装在树莓派上之前，添加一个用户和 SSH 授权密钥（authorized_keys）。配置主机在 Ubuntu 被安装在树莓派上，并且可以通过 SSH 访问后，你需要在安装 Kubernetes 之前做一些修改。安装和配置 Docker截至目前，Ubuntu 20.04 在 base 软件库中提供了最新版本的 Docker，即 v19.03，可以直接使用 apt 命令安装它。请注意，包名是 docker.io。请在所有的树莓派上安装 Docker：# 安装 docker.io 软件包 $ sudo apt install -y docker.io 安装好软件包后，你需要做一些修改来启用 cgroup（控制组）。cgroup 允许 Linux 内核限制和隔离资源。实际上，这可以让 Kubernetes 更好地管理其运行的容器所使用的资源，并通过让容器彼此隔离来增加安全性。在对所有树莓派进行以下修改之前，请检查 docker info 的输出：# 检查 `docker info` # 省略了某些输出 $ sudo docker info (...) Cgroup Driver: cgroups (...) WARNING: No memory limit suppor…

把你的树莓派家庭实验室变成一个网络文件系统
type: item
date: 2020-07-14T23:44:15+08:00
description: 使用 NFS 服务器将共享文件系统添加到你的家庭实验室。共享文件系统是为家庭实验室增加通用性和功能性的好方法。在实验室中为客户端共享一个集中的文件系统，使得组织数据、进行备份和共享数据变得相当容易。这对于在多个服务器上进行负载均衡的 Web 应用和 Kubernetes 使用的持久化卷来说，尤其有用，因为它允许在任何数量的节点上用持久化数据来轮转 Pod。无论你的家庭实验室是由普通计算机、多余的企业服务器，还是树莓派或其他单板计算机（SBC）组成，共享文件系统都是一种有用的资产，而网络文件系统（NFS）服务器是创建共享文件系统的好方法。我之前写过关于建立“家庭私有云”的文章，这是一个由树莓派或其他 SBC 组成的家庭实验室，也许还有其他一些消费类硬件或台式 PC。NFS 服务器是这些组件之间共享数据的理想方式。由于大多数 SBC 的操作系统是通过 SD 卡运行的，所以存在一些挑战。尤其是在用作计算机的操作系统磁盘时，SD 卡的故障率会增加，它们并不是用来不断地读写的。你实际需要的是一个真正的硬盘：它们通常比 SD 卡的每 GB 价格便宜，特别是对于较大的磁盘，而且它们不太可能持续发生故障。树莓派 4 现在带有 USB 3.0 接口，而 USB 3.0 硬盘无处不在，价格也很实惠。这是一个完美的搭配。在这个项目中，我将使用一个 2TB 的 USB 3.0 外置硬盘插入到运行 NFS 服务器的树莓派 4 中。安装 NFS 服务器软件我在树莓派上运行 Fedora 服务器，但这个项目也可以在其他发行版上运行。要在 Fedora 上运行 NFS 服务器，你需要 nfs-utils 包，幸运的是它已经安装好了（至少在 Fedora 31 中是这样）。如果你打算运行 NFSv3 服务，你还需要 rpcbind 包，但它不是 NFSv4 的严格要求。如果你的系统中还没有这些软件包，请使用 dnf 命令安装它们。# 安装 nfs-utils 和 rpcbind $ sudo dnf install nfs-utils rpcbind Raspbian 是另一个与树莓派一起使用的流行操作系统，设置几乎完全相同。软件包名称不同而已，但这是唯一的主要区别。要在运行 Raspbian 的系统上安装 NFS 服务器，你需要以下软件包。nfs-common：这些文件是 NFS 服务器和客户端的通用文件。nfs-kernel-server：主要的 NFS 服务器软件包。Raspbian 使用 apt-get 来管理软件包（而不是像 Fedora 那样使用 dnf），所以用它来安装软件包。# 对于 Raspbian 系统，使用 apt-get 来安装 NFS 软件包 $ sudo apt-get install nfs-common nfs-kernel-server 准备一个 USB 硬盘作为存储设备正如我上面提到的，USB 硬盘是为树莓派或其他 SBC 提供存储的好选择，尤其是用于操作系统磁盘镜像的 SD 卡并不适合这个用途。对于家庭私有云，你可以使用廉价的 USB 3.0 硬盘进行大规模存储。插入磁盘，使用 fdisk 找出分配给它的设备 ID，就可以使用它工作了。# 使用 fdisk 找到你的硬盘 # 无关的硬盘信息已经省略 $ sudo fdisk -l Disk /dev/sda: 1.84 TiB, 2000398933504 bytes, 3907029167 sectors Disk model: BUP Slim BK Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xe3345ae9 Device Boot Start End Sectors Size Id Type /dev/sda1 2048 3907028991 3907026944 1.8T 83 Linux 为了清楚起见，在上面的例子输出中，除了我感兴趣的那个磁盘，我省略了所有其它磁盘的信息。你可以看到我想使用的 USB 磁盘被分配了设备 /dev/sda，你可以看到一些关于型号的信息（Disk model: BUP Slim BK），这有助于我识别正确的磁盘。该磁盘已经有了一个分区，它的大小也证实了它就是我要找的磁盘。注意：请确保正确识别你的设备的磁盘和分区。它可能与上面的例子不同。在驱动器上创建的每个分区都有一个特殊的通用唯一标识符（UUID）。计算机使用 UUID…

使用 Cloud-init 将节点添加到你的私有云中
type: item
date: 2020-07-12T18:09:52+08:00
description: 像主流云提供商的处理方式一样，在家中添加机器到你的私有云。Cloud-init 是一种广泛使用的行业标准方法，用于初始化云实例。云提供商使用 Cloud-init 来定制实例的网络配置、实例信息，甚至用户提供的配置指令。它也是一个可以在你的“家庭私有云”中使用的很好的工具，可以为你的家庭实验室的虚拟机和物理机的初始设置和配置添加一点自动化 —— 并了解更多关于大型云提供商是如何工作的信息。关于更多的细节和背景，请看我之前的文章《在你的树莓派家庭实验室中使用 Cloud-init》。运行 Cloud-init 的 Linux 服务器的启动过程（Chris Collins，CC BY-SA 4.0）诚然，Cloud-init 对于为许多不同客户配置机器的云提供商来说，比对于由单个系统管理员运行的家庭实验室更有用，而且 Cloud-init 解决的许多问题对于家庭实验室来说可能有点多余。然而，设置它并了解它的工作原理是了解更多关于这种云技术的好方法，更不用说它是首次启动时配置设备的好方法。本教程使用 Cloud-init 的 NoCloud 数据源，它允许 Cloud-init 在传统的云提供商环境之外使用。本文将向你展示如何在客户端设备上安装 Cloud-init，并设置一个运行 Web 服务的容器来响应客户端的请求。你还将学习如何审查客户端从 Web 服务中请求的内容，并修改 Web 服务的容器，以提供基本的、静态的 Cloud-init 服务。在现有系统上设置 Cloud-initCloud-init 可能在新系统首次启动时最有用，它可以查询配置数据，并根据指令对系统进行定制。它可以包含在树莓派和单板计算机的磁盘镜像中，也可以添加到用于 配给 provision 虚拟机的镜像中。对于测试用途来说，无论是在现有系统上安装并运行 Cloud-init，还是安装一个新系统，然后设置 Cloud-init，都是很容易的。作为大多数云提供商使用的主要服务，大多数 Linux 发行版都支持 Cloud-init。在这个例子中，我将使用 Fedora 31 Server 来安装树莓派，但在 Raspbian、Ubuntu、CentOS 和大多数其他发行版上也可以用同样的方式来完成。安装并启用云计算初始服务在你想作为 Cloud-init 客户端的系统上，安装 Cloud-init 包。如果你使用的是 Fedora：# Install the cloud-init package dnf install -y cloud-init Cloud-init 实际上是四个不同的服务（至少在 systemd 下是这样），这些服务负责检索配置数据，并在启动过程的不同阶段进行配置更改，这使得可以做的事情更加灵活。虽然你不太可能直接与这些服务进行太多交互，但在你需要排除一些故障时，知道它们是什么还是很有用的。它们是：cloud-init-local.servicecloud-init.servicecloud-config.servicecloud-final.service启用所有四个服务：# Enable the four cloud-init services systemctl enable cloud-init-local.service systemctl enable cloud-init.service systemctl enable cloud-config.service systemctl enable cloud-final.service 配置数据源以查询启用服务后，请配置数据源，客户端将从该数据源查询配置数据。有许多数据源类型，而且大多数都是为特定的云提供商配置的。对于你的家庭实验室，请使用 NoCloud 数据源，（如上所述）它是为在没有云提供商的情况下使用 Cloud-init 而设计的。NoCloud 允许以多种方式包含配置信息：以内核参数中的键/值对，用于在启动时挂载的 CD（或虚拟机中的虚拟 CD）；包含在文件系统中的文件中；或者像本例中一样，通过 HTTP 从指定的 URL（“NoCloud Net” 选项）获取配置信息。数据源配置可以通过内核参数提供，也可以在 Cloud-init 配置文件 /etc/cloud/cloud.cfg 中进行设置。该配置文件对于使用自定义磁盘镜像设置 Cloud-init 或在现有主机上进行测试非常方便。Cloud-init 还会合并在 /etc/cloud/cloud.cfg.d/ 中找到的任何 *.cfg 文件中的配置数据，因此为了保持整洁，请在 /etc/cloud/cloud.cfg.d/10_datasource.cfg 中配置数据源。Cloud-init 可以通过使用以…

在你的树莓派家庭实验室中使用 Cloud-init
type: item
date: 2020-07-02T04:38:02+08:00
description: 了解了云行业的标准，该向你的家庭实验室自动添加新设备和用户了。Cloud-init（可以说）是一个标准，云提供商用它来为云实例提供初始化和配置数据。它最常用于新实例的首次启动，以自动完成网络设置、账户创建和 SSH 密钥安装等使新系统上线所需的任何事情，以便用户可以访问它。在之前的一篇文章《修改磁盘镜像来创建基于树莓派的家庭实验室》中，我展示了如何为像树莓派这样的单板计算机定制操作系统镜像以实现类似的目标。有了 Cloud-init，就不需要向镜像中添加自定义数据。一旦在镜像中启用了它，你的虚拟机、物理服务器，甚至是小小的树莓派都可以表现得像你自己的 “家庭私有云” 中的云计算实例。新机器只需插入、打开，就可以自动成为你的家庭实验室的一部分。说实话，Cloud-init 的设计并没有考虑到家庭实验室。正如我所提到的，你可以很容易地修改给定的一套系统磁盘镜像，以启用 SSH 访问并在第一次启动后对它们进行配置。Cloud-init 是为大规模的云提供商设计的，这些提供商需要容纳许多客户，维护一组小的镜像，并为这些客户提供访问实例的机制，而无需为每个客户定制一个镜像。拥有单个管理员的家庭实验室则不会面临同样的挑战。不过，Cloud-init 在家庭实验室中也不是没有可取之处。教育是我的家庭私有云项目的目标之一，而为你的家庭实验室设置 Cloud-init 是一个很好的方式，可以获得大大小小的云提供商大量使用的技术的经验。Cloud-init 也是其他初始配置选项的替代方案之一。与其为家庭实验室中的每台设备定制每个镜像、ISO 等，并在你要进行更改时面临繁琐的更新，不如直接启用 Cloud-init。这减少了技术债务 —— 还有什么比个人技术债务更糟糕的吗？最后，在你的家庭实验室中使用 Cloud-init 可以让你的私有云实例与你拥有的或将来可能拥有的任何公有云实例表现相同 —— 这是真正的混合云。关于 Cloud-init当为 Cloud-init 配置的实例启动并且服务开始运行时（实际上是 systemd 中的四个服务，以处理启动过程中的依赖关系），它会检查其配置中的数据源，以确定其运行在什么类型的云中。每个主要的云提供商都有一个数据源配置，告诉实例在哪里以及如何检索配置信息。然后，实例使用数据源信息检索云提供商提供的配置信息（如网络信息和实例识别信息）和客户提供的配置数据（如要复制的授权密钥、要创建的用户账户以及许多其他可能的任务）。检索数据后，Cloud-init 再对实例进行配置：设置网络、复制授权密钥等，最后完成启动过程。然后，远程用户就可以访问它，准备好使用 Ansible 或 Puppet 等工具进行进一步的配置，或者准备好接收工作负载并开始分配任务。配置数据如上所述，Cloud-init 使用的配置数据来自两个潜在来源：云提供商和实例用户。在家庭实验室中，你扮演着这两种角色：作为云提供商提供网络和实例信息，作为用户提供配置信息。云提供商元数据文件在你的云提供商角色中，你的家庭实验室数据源将为你的私有云实例提供一个元数据文件。这个元数据文件包含实例 ID、云类型、Python 版本（Cloud-init 用 Python 编写并使用 Python）或要分配给主机的 SSH 公钥等信息。如果你不使用 DHCP（或 Cloud-init 支持的其他机制，如镜像中的配置文件或内核参数），元数据文件还可能包含网络信息。用户提供的用户数据文件Cloud-init 的真正价值在于用户数据文件。用户数据文件由用户提供给云提供商，并包含在数据源中，它将实例从一台普通的机器变成了用户舰队的一员。用户数据文件可以以可执行脚本的形式出现，与正常情况下脚本的工作方式相同；也可以以云服务配置 YAML 文件的形式出现，利用 Cloud-init 的模块 来执行配置任务。数据源数据源是由云提供商提供的服务，它为实例提供了元数据和用户数据文件。实例镜像或 ISO 被配置为告知实例正在使用什么数据源。例如，亚马逊 AWS 提供了一个 link-local 文件，它将用实例的自定义数据来响应实例的 HTTP 请求。其他云提供商也有自己的机制。幸运的是，对于家庭私有云项目来说，也有 NoCloud 数据源。NoCloud 数据源允许通过内核命令以键值对的形式提供配置信息，或通过挂载的 ISO 文件系统以用户数据和元数据文件的形式提供。这些对于虚拟机来说很有用，尤其是与自动化搭配来创建虚拟机。还有一个 NoCloudNet 数据源，它的行为类似于 AWS EC2 数据源，提供一个 IP 地址或 DNS 名称，通过 HTTP 从这里检索用户数据和元数据。这对于你的家庭实验室中的物理机器来说是最有帮助的，比如树莓派、NUC 或多余的服务器设备。虽然 NoC…

修改磁盘镜像来创建基于树莓派的家庭实验室
type: item
date: 2020-06-03T20:34:48+08:00
description: 使用树莓派或其它单板机创建一个“家庭私有云”。构建一个家庭实验室可以是一个有趣的方式，可以让你学习的新概念和实验新技术时还能自娱自乐。得益于以 树莓派 为首的单板计算机（SBC）的流行，在舒适的家里就可以轻松构建一个多计算机实验室。比起试图在主流的云服务商建立的相同配置，创建一个“家庭私有云”以花更少的钱来体验到云原生技术，也是一个极好的方法。这篇文章阐述如何修改树莓派或其它的单板机的磁盘镜像，预配置主机的 SSH，并禁用首次启动时强制竞选交互配置的服务。这是一个让你的设备“即启动，即运行”的极好方法，类似于云端实例。之后，你可以使用自动化的流程通过 SSH 连接来进行更专业和更深入的配置。此外, 当向你的实验室添加更多的树莓派时，修改磁盘镜像可以来让你只需要将该镜像写到一个 SD 卡、放入树莓派中就可以了！解压缩和挂载镜像对于这个项目，你需要修改一个服务器磁盘镜像。在测试期间，我使用 Fedora Server 31 ARM。在你下载该磁盘镜像并验证其校验和之后，你需要将其解压缩并挂载其到宿主机的文件系统的某个位置上，以便你可以根据需要修改它。你可以使用 xz 命令通过 --decompress 参数来解压缩 Fedora 服务器镜像：xz --decompress Fedora-Server-armhfp-X-y.z-sda.raw.xz这会留下一个解压缩后的原始磁盘镜像（它会自动地替换 .xz 压缩文件）。这个原始磁盘镜像就像它听起来的那样：一个包含格式化后安装好的磁盘上的所有数据的文件。这包含分区信息、启动分区、root 分区以及其它分区。你需要挂载你打算在其中进行修改的分区，但是要做到这一点，你需要知道磁盘镜像中的分区起始位置和扇区大小，这样你才可以挂载该文件正确的扇区。幸运的是，你可以在一个磁盘镜像上使用 fdisk 命令，就像在实际磁盘上使用一样容易。使用 --list 或 -l 参数来查看分区的列表和其信息：# 使用 fdisk 来列出原始镜像文件的分区： $ fdisk -l Fedora-Server-armhfp-31-1.9-sda.raw Disk Fedora-Server-armhfp-X-y.z-sda.raw: 3.2 GiB, 3242196992 bytes, 6332416 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xdaad9f57 Device Boot Start End Sectors Size Id Type Fedora-Server-armhfp-X-y.z-sda.raw1 8192 163839 155648 76M c W95 F Fedora-Server-armhfp-X-y.z-sda.raw2 * 163840 1163263 999424 488M 83 Linux Fedora-Server-armhfp-X-y.z-sda.raw3 1163264 6047743 4884480 2.3G 83 Linux你需要的所有信息都可在上面的输出中获得。第 3 行表示扇区大小（包括逻辑和物理的）：512 字节 / 512 字节。设备列表显示的是原始磁盘镜像中的分区。第一个，Fedora-Server-armhfp-X-y.z-sda.raw1 毫无疑问是引导程序分区，因为它是第一个，很小（仅仅 76MB），而且类型被标识为 c，即 W95 FAT32（LBA），这是一个从 SD 卡启动的 FAT32 分区。第二个分区也不是非常大，只有 488MB。这个分区是一个 Linux 原生类型分区（Id 83），它可能是包含内核和 initramfs 的 Linux 启动分区。第三个分区可能是你需要的东西：它有 2.3GB 大小，所以在它其中应该有发行版的主要部分，并且它是一个 Linux 原生分区类型，这也是在预料之中的。这个分区应该包含了你需要修改的分区和数据。第三个分区从扇区 1163264 开始（在 fdisk 的输出中被显示为 Start 列），所以你的挂载偏移量是 595591168，计算方法是将扇区大小（512）乘以起始扇区（1163264）（即 512 * 1163264）。这意味着你需要以偏移量 595591168 挂载该文件，才能挂载到正确位置。装备了这些信息，现在你可以将第三个分区挂载到你的家目录中了：$ mkdir ~/…

如何用 GVM 管理 Go 项目
type: item
date: 2019-10-11T19:22:41+08:00
description: 使用 Go 版本管理器管理多个版本的 Go 语言环境及其模块。Go 语言版本管理器（GVM）是管理 Go 语言环境的开源工具。GVM “pkgsets” 支持安装多个版本的 Go 并管理每个项目的模块。它最初由 Josh Bussdieker 开发，GVM（像它的对手 Ruby RVM 一样）允许你为每个项目或一组项目创建一个开发环境，分离不同的 Go 版本和包依赖关系，以提供更大的灵活性，防止不同版本造成的问题。有几种管理 Go 包的方式，包括内置于 Go 中的 Go 1.11 的 Modules。我发现 GVM 简单直观，即使我不用它来管理包，我还是会用它来管理 Go 不同的版本的。安装 GVM安装 GVM 很简单。GVM 存储库安装文档指示你下载安装程序脚本并将其传送到 Bash 来安装:bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)尽管越来越多的人采用这种安装方法，但是在安装之前先看看安装程序在做什么仍然是一个很好的想法。以 GVM 为例，该安装程序脚本:检查一些相关依赖性克隆 GVM 存储库使用 shell 脚本：安装 Go 语言管理 GOPATH 环境变量向 bashrc、zshrc 或配置文件中添加一行内容如果你想确认它在做什么，你可以克隆该存储库并查看 shell 脚本，然后运行 ./binscripts/gvm-installer 这个本地脚本进行设置。注意： 因为 GVM 可以用来下载和编译新的 Go 版本，所以有一些预期的依赖关系，如 Make、Git 和 Curl。你可以在 GVM 的自述文件中找到完整的发行版列表。使用 GVM 安装和管理 GO 版本一旦安装了 GVM，你就可以使用它来安装和管理不同版本的 Go。gvm listall 命令显示可下载和编译的可用版本的 Go：[chris@marvin ]$ gvm listall gvm gos (available) go1 go1.0.1 go1.0.2 go1.0.3 <输出截断>安装特定的 Go 版本就像 gvm install <版本> 一样简单，其中 <版本> 是 gvm listall 命令返回的版本之一。假设你正在进行一个使用 Go1.12.8 版本的项目。你可以使用 gvm install go1.12.8 安装这个版本:[chris@marvin]$ gvm install go1.12.8 Installing go1.12.8... * Compiling... go1.12.8 successfully installed!输入 gvm list，你会看到 Go 版本 1.12.8 与系统 Go 版本（使用操作系统的软件包管理器打包的版本）一起并存：[chris@marvin]$ gvm list gvm gos (installed) go1.12.8 => systemGVM 仍在使用系统版本的 Go ，由 => 符号表示。你可以使用 gvm use 命令切换你的环境以使用新安装的 go1.12.8：[chris@marvin]$ gvm use go1.12.8 Now using version go1.12.8 [chris@marvin]$ go version go version go1.12.8 linux/amd64GVM 使管理已安装版本的 Go 变得极其简单，但它不止于此！使用 GVM pkgset开箱即用，Go 有一种出色而令人沮丧的管理包和模块的方式。默认情况下，如果你 go get 获取一个包，它将被下载到 $GOPATH 目录中的 src 和 pkg 目录下，然后可以使用 import 将其包含在你的 Go 程序中。这使得获得软件包变得很容易，特别是对于非特权用户，而不需要 sudo 或 root 特权（很像 Python 中的 pip install --user）。然而，在不同的项目中管理相同包的不同版本是非常困难的。有许多方法可以尝试修复或缓解这个问题，包括实验性 Go Modules（Go 1.11 版中增加了初步支持）和 Go dep（Go Modules 的“官方实验”并且持续迭代）。在我发现 GVM 之前，我会在一个 Go 项目自己的 Docker 容器中构建和测试它，以确保分离。GVM 通过使用 “pkgsets” 将项目的新目录附加到安装的 Go 版本的默认 $GOPATH 上，很好地实现了项目之间包的管理和隔离，就像 $PATH 在 Unix/Linux 系统上工作一样。想象它如何运行的。首先，安装新版 Go 1.12.…

如何打造更小巧的容器镜像
type: item
date: 2019-03-28T07:07:29+08:00
description: 五种优化 Linux 容器大小和构建更小的镜像的方法。Docker 近几年的爆炸性发展让大家逐渐了解到容器和容器镜像的概念。尽管 Linux 容器技术在很早之前就已经出现，但这项技术近来的蓬勃发展却还是要归功于 Docker 对用户友好的命令行界面以及使用 Dockerfile 格式轻松构建镜像的方式。纵然 Docker 大大降低了入门容器技术的难度，但构建一个兼具功能强大、体积小巧的容器镜像的过程中，有很多技巧需要了解。第一步：清理不必要的文件这一步和在普通服务器上清理文件没有太大的区别，而且要清理得更加仔细。一个小体积的容器镜像在传输方面有很大的优势，同时，在磁盘上存储不必要的数据的多个副本也是对资源的一种浪费。因此，这些技术对于容器来说应该比有大量专用内存的服务器更加需要。清理容器镜像中的缓存文件可以有效缩小镜像体积。下面的对比是使用 dnf 安装 Nginx 构建的镜像，分别是清理和没有清理 yum 缓存文件的结果：# Dockerfile with cache FROM fedora:28 LABEL maintainer Chris Collins <collins.christopher@gmail.com> RUN dnf install -y nginx ----- # Dockerfile w/o cache FROM fedora:28 LABEL maintainer Chris Collins <collins.christopher@gmail.com> RUN dnf install -y nginx \ && dnf clean all \ && rm -rf /var/cache/yum ----- [chris@krang] $ docker build -t cache -f Dockerfile . [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}" | head -n 1 cache: 464 MB [chris@krang] $ docker build -t no-cache -f Dockerfile-wo-cache . [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}" | head -n 1 no-cache: 271 MB从上面的结果来看，清理缓存文件的效果相当显著。和清除了元数据和缓存文件的容器镜像相比，不清除的镜像体积接近前者的两倍。除此以外，包管理器缓存文件、Ruby gem 的临时文件、nodejs 缓存文件，甚至是下载的源码 tarball 最好都全部清理掉。层：一个潜在的隐患很不幸（当你往下读，你会发现这是不幸中的万幸），根据容器中的层的概念，不能简单地向 Dockerfile 中写一句 RUN rm -rf /var/cache/yum 就完事儿了。因为 Dockerfile 的每一条命令都以一个层的形式存储，并一层层地叠加。所以，如果你是这样写的：RUN dnf install -y nginx RUN dnf clean all RUN rm -rf /var/cache/yum你的容器镜像就会包含三层，而 RUN dnf install -y nginx 这一层仍然会保留着那些缓存文件，然后在另外两层中被移除。但缓存实际上仍然是存在的，当你把一个文件系统挂载在另外一个文件系统之上时，文件仍然在那里，只不过你见不到也访问不到它们而已。在上一节的示例中，你会看到正确的做法是将几条命令链接起来，在产生缓存文件的同一条 Dockerfile 指令里把缓存文件清理掉：RUN dnf install -y nginx \ && dnf clean all \ && rm -rf /var/cache/yum这样就把几条命令连成了一条命令，在最终的镜像中只占用一个层。这样只会浪费一点缓存的好处，稍微多耗费一点点构建容器镜像的时间，但被清理掉的缓存文件就不会留存在最终的镜像中了。作为一个折衷方法，只需要把一些相关的命令（例如 yum install 和 yum clean all、下载文件、解压文件、移除 tarball 等等）连接成一个命令，就可以在最终的容器镜像中节省出大量体积，你也能够利用 Docker 的缓存加快开发速度。层还有一个更隐蔽的特性。每一层都记录了文件的更改，这里的更改并不仅仅已有的文件累加起来，而是包括文件属性在内的所有更改。因此即使是对文件使用了 chmod 操作也会被在新的层创建文件的副本。下面是一次 docker images 命令的输出内容。其中容器镜像 layer_tes…