译者 | 刘涛
审校 | 重楼
以 Kubernetes、Nomad为代表的任何云托管的服务平台(PaaS)提供了多种强大的功能。从扩展工作负载到保密管理再到部署策略,这些工作负载编排器已经被优化,以便以不同的方式对基础结构进行扩展。
但是,运营者总是需要为最大化可扩展性付出代价吗?有时,复杂性和抽象性的成本会超过它所带来的益处。许多构建者更倾向于部署更简单的架构,以便更容易管理。与跨容器主机集群的大规模微服务器集群相比,负载均衡器后面的两台虚拟专用服务器由一个大大简化的堆栈来管理。当系统出现问题需要调试,或者需要维护升级时,组件越少,移动的部件越少,越容易管理,就可以更快产生回报。
许多现代Linux发行版的基础是systemd(一个Linux系统的基本构建块套件),systemd拥有一系列可以和容器编排器或 PaaS系统相媲美的强大功能。在本文中,我们将探讨如何利用最新的systemd功能,以获得类似于其他大型系统的许多能力,同时避免管理上的麻烦,并将任何普通的Linux服务器升级为一个非常强大的应用平台。
Systemd入门知识
在单个主机上,编写systemd.service文件是运行托管进程的理想方式。大多数时候,您甚至根本不需要更改应用程序:systemd支持各种不同类型的服务,并且可以相应地进行调整。
例如,考虑这个定义如何运行简单Web服务的简单.service:
请记住systemd服务的默认配置:
ExecStart= 必须是绝对路径进程不应该派生到后台运行,您可能需要用Envirnotallow=选项设置必要的环境变量。
当放入像/etc/systemd/system/webapp.service这样的文件中时,这将创建一个可以用systemctl控制的服务:
- systemctl start webapp 将启动该进程。
- systemctl status webapp 将显示服务是否正在运行、运行时间、以及来自stderr和stdout的输出,以及进程ID和其他信息。
- systemctl stop webapp 将结束该服务。
此外,所有打印到stderr和stdout的输出将由journald(Linux系统中日志管理的一个系统守护进程)聚合并可通过系统日志(用journalctl)访问,或具体使用--unit标志来进行目标定位:
因为journald默认会进行日志轮转和存储管理,通过journal收集日志是管理日志存储的一个不错的策略。
本文的其余部分将探讨增强类似于此服务的选项。
保密信息
像Kubernetes这样的容器编排器支持安全地注入保密信息:从安全数据存储中提取值并公开给运行的工作负载。一些敏感数据,比如API密钥或口令,需要不同于环境变量或配置文件的处理方式,以避免无意暴露。
systemd的LoadCredential=选项支持从磁盘上的文件加载敏感值,并以安全的方式公开给运行的服务。像托管平台远程管理保密信息一样,systemd将以不同于环境变量等值的方式处理这些敏感凭证,以确保它们的安全。
要向systemd服务注入保密信息,首先将包含保密值的文件放在文件系统上的路径中。例如,要向.service单元公开API密钥,可在/etc/credstore/api-key创建一个文件,并在重启后永久保存该文件,或在/run/credstore/api-key创建一个文件,以避免永久保存该文件(路径可以是任意的,但systemd会将这些credstore路径视为默认值)。在任何一种情况下,您都应该使用诸如chmod 400 /etc/credstore/api-key之类的命令设置权限。
在.service文件的[Service]部分下,定义LoadCredential=选项,并传递两个用冒号(:)分隔的值:凭证的名称和路径。例如,要调用/etc/credstore/api-key文件“令牌”,定义以下systemd服务选项:
当systemd启动服务时,保密信息将在以下形式的路径下公开给正在运行的服务${CREDENTIALS_DIRECTORY}/token,其中${CREDENTIALS_DIRECTORY}是由systemd填充的环境变量。您的应用程序代码能够读取以这种方式定义的每个保密信息,以便在需要API令牌或密码等安全值的库或代码中使用。例如,在Python中,您可以使用如下代码读取此保密信息:
然后,对于任何可能需要API令牌或密码的库,您可以将secret变量与保密信息的内容一起使用。
重启
像Nomad这样的编排器还有一个功能,就是能自动重启崩溃的工作负载。无论是由于未处理的应用程序错误还是其他原因,重新启动失败的应用程序都是非常有用的功能,它往往是设计一个有弹性的应用程序的首要防线。
Restart=systemd选项用来控制systemd是否会自动重启正在运行的进程。此选项有几个潜在的值,但对于基本服务,on-failure设置非常适合满足大多数用例。
另一个需要考虑的设置是RestartSec=选项,它决定systemd重新启动服务之前等待的时间。通常情况下,您应该自定义这个值以避免在紧密循环中再次出现服务失败,并可能消耗过多的CPU时间在进程重启上。像5秒这样不会等太久的时间通常就足够了。
最后,另外两个选项决定了systemd在最终放弃之前会多么积极地尝试重新启动失败的单元。StartLimitIntervalSec=和StartLimitBurst=将控制一个单元在给定时间段内被允许启动的次数。例如,以下设置:
它只允许一个单元在10秒内尝试启动的次数最多为5次。如果配置的服务在10秒内尝试第6次启动,systemd将停止尝试重启该单元并将其标记为failed。
结合所有这些设置,您可以为.service单元设置以下选项来配置自动重启:
如果服务失败(即意外退出,例如使用了非0退出代码),此配置将在等待一秒后重新启动服务,如果在10秒内尝试启动的服务超过了5次,则尝试重新启动服务将会终止。
强化服务
在容器中运行的主要好处之一是安全沙盒。通过将应用程序进程与底层操作系统分开,任何可能出现在服务中的漏洞都更难进化成全面的威胁。例如Docker(一种容器)运行时,通过cgroup和其他安全原语(security primitives)的组合来实现这一点。
您可以启用多个systemd选项来强加类似的限制,这些限制有助于保护底层主机免受不可预测的工作负载行为的影响:
- ProtectSystem=可以限制对/boot和/usr等敏感系统路径的写入访问。此选项的文档枚举了所有可用选项,但一般来说,将此选项设置为full是保护这些文件系统路径的合理默认值。
- ProtectHome=可以将/home、/root和/run/user目录设置为只读read-only设置,或者当设置为true时,将它们作为空目录载入到服务的文件系统中。除非您的应用程序特别需要访问这些目录,否则将其设置为true可以安全强化系统以防止对这些目录的非法访问。
- PrivateTmp=为配置的服务维护单独的/tmp和/var/tmp,以便该服务和其他进程的临时文件被保存为私有文件。除非进程有令人信服的理由通过临时文件共享信息,那么这个选项就很有用了。
- NoNewPrivileges=是另一种安全和直接的方法 ,通过确保执行的进程不需要提升其权限就可以强化服务。如果您不确定是否能够使用其他强化选项,那么这种方法通常是使用问题最少的选项之一。
system.exec的操作手册是一个有用的资源,可以为诸如服务等可执行的工作负载提供不同的选项。
延伸阅读
system的操作手册内容丰富,也非常实用。您可以了解所有可用于运行您自己应用程序的选项。无论您是运行诸如 webserver之类的持久服务,还是定期运行.timer单元来替换cron任务(Linux系统上的一个任务调度器),systemd文档都可以提供有用的指导。
译者介绍
刘涛,本站社区编辑,某大型央企系统上线检测管控负责人。
原文标题:Micro-DevOps With Systemd: Supercharge Any Ordinary Linux Server,作者:Tyler