前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux实践之自动注册系统服务

linux实践之自动注册系统服务

原创
作者头像
mariolu
修改2023-12-15 10:21:07
2060
修改2023-12-15 10:21:07
举报

服务简而言之就是一组后台进程,辅助App管理的系统组件。Linux作为服务器的使用最广的系统之一,本篇文章让我们来一起探究 “服务+Linux” 这个话题。

一、服务鼻祖之Init

学过Linux的同学应该都知道赫赫有名的init进程,那是Linux系统启动的第0号进程,在类Unix的计算机操作系统中,initinitialize的缩写)是一个守护进程,Init 负责生成所有其他进程并清除僵尸。它还负责重启和关闭操作。它会一直运行,直到系统关闭。它是所有其他进程的直接或间接父进程,并自动接管所有孤儿进程。Init 在boot过程中由内核启动。如果内核无法启动它,就会发生内核奔溃。Init 通常被分配进程id 1。

有关inittab鼻祖在如今的linux系统中还可见到一些痕迹,比如路径/etc/rc.d/里面的内容,可以看到init.d管理着网络,文件系统,控制台,还有分为6个不同优先级级的rc.0.d到rc.5.d的服务。

但如果你继续访问这些init.d的文件,比如说cat /etc/inittab,它会继续告诉你,init已经被systemd所取代。

init 的一个主要缺点是它按顺序启动任务,等待每个任务完成加载,然后再继续下一个任务。当启动进程被输入/输出(I/O) 阻止时,这可能会导致启动过程中出现长时间延迟。加快 I/O(例如通过使用 SSD)可能会缩短延迟,但并不能解决根本原因。

为了解决这一问题和其他设计问题,很多种方案替换传统的 init 守护程序,包括这里提到的systemd:

二、当今主流 systemd

2.1 认识systemd

systemd 是一个软件套件,为 Linux操作系统提供一系列系统组件。主要目的是统一 Linux发行版的服务配置和行为。它的主要组件是“系统和服务管理器”——一个用于引导用户空间和管理用户进程的init系统。它还提供了各种daemon程序和实用程序的替代品,包括设备管理、登录管理、网络连接管理和事件日志记录。之所以叫名字systemd是因为遵循Unix约定,即通过附加字母 d 来命名守护进程。

自2015 年以来,大多数 Linux 发行版都采用了 systemd,取代了 SysV init 等其他init系统。在启动过程中提供可靠并行的软件套件,以及对进程、守护进程、服务和挂载点的集中管理。

systemd丰富的套件
systemd丰富的套件

三、实践systemd

3.1 初窥systemd

systemd手册页很全面,但很容易迷失在细节中。所以本文从一个仅包含关键要素的最小示例开始,并尝试理解其工作原理;然后逐步扩展它:怎么注册一个服务,已经很多本文之外可以延申的探讨内容。

在实际系统上,有很多单元文件,例如,我的机器上有几百个条目。

代码语言:shell
复制
ls /lib/systemd/system

3.2 最小systemd示例

我们来创建一个最小的 systemd 示例,为了方便实验,使用容器来讲解。容器基于ubuntu镜像,来说明systemd怎么玩转。

我们这里使用podman进程来启动容器,需要最好你懂一些docker的知识,如果不懂,也没关系。你也可以按照步骤 敲出自动的每一步骤,也可以了解一些大概原理。

3.1.1 容器工具

podman的安装(以ubuntu为例):

代码语言:shell
复制
apt install podman

描述Dockerfile,这个Dockerfile生成方法如下:

代码语言:shell
复制
echo \
"FROM ubuntu:20.04

RUN apt-get update && \
    apt-get install --assume-yes systemd

RUN rm -rf /lib/systemd/system/* /etc/systemd/system/*

CMD [\"/lib/systemd/systemd\"]" > /root/Dockerfile

这里对不熟悉Dockerfile规则的同学稍作解释:Dockerfile的每行都是一条指令,指令的首个单词全大写,比如FROM,RUN,CMD

于是这个Dockerfile描述为”从ubunt:20.04的基础镜像上,安装systemd服务,并且删除systemd的两个目录。CMD来指定启动运行systemd“。

3.1.2 启动无systemd的容器

接着构建这个名叫systemd的容器

代码语言:shell
复制
podman build --tag systemd .

运行这个叫做systemd的容器

代码语言:shell
复制
podman run --tty --rm --name systemd systemd

接着就看到了报错信息。

到这里systemd告诉你运行容器失败,原因是缺失了default.target和rescue.target

3.1.3 default.target

于是创建一个default.target文件

代码语言:shell
复制
echo \
"[Unit]
Description=A minimal default target" > /root/default.target

并且在Dockerfile追加这条指令”COPY default.target /lib/systemd/system/“

编辑后的Dockfile如下图:

代码语言:shell
复制
root@ubun22 ~ [SIGKILL]# cat /root/Dockerfile
FROM ubuntu:20.04

RUN apt-get update &&     apt-get install --assume-yes systemd

RUN rm -rf /lib/systemd/system/* /etc/systemd/system/*

COPY default.target /lib/systemd/system/

CMD ["/lib/systemd/systemd"]

重新build和run,这里看到continer成功启动!!

代码语言:shell
复制
root@ubun22 ~# podman build --tag systemd .
STEP 1/5: FROM ubuntu:20.04
STEP 2/5: RUN apt-get update &&     apt-get install --assume-yes systemd
--> Using cache d98831e18f02b53175ea7eb151a8f1af7ba95d51f432e695b4aa75280018d532
--> d98831e18f0
STEP 3/5: RUN rm -rf /lib/systemd/system/* /etc/systemd/system/*
--> Using cache 26e1d135ecea935d582650d4213f10577b7a3486584fb3309991b1e62054d5ae
--> 26e1d135ece
STEP 4/5: COPY default.target /lib/systemd/system/
--> Using cache 8f95f38f622f76f8ac24afd9232526f2317422847cdedfa1515562393255a1f7
--> 8f95f38f622
STEP 5/5: CMD ["/lib/systemd/systemd"]
--> Using cache b57f4a766bcbf99ec368f811e5c9b488dc830b96ef11c032c13c7b9d2a94cfde
COMMIT systemd
--> b57f4a766bc
Successfully tagged localhost/systemd:latest
b57f4a766bcbf99ec368f811e5c9b488dc830b96ef11c032c13c7b9d2a94cfde


root@ubun22 ~# podman run --tty --rm --name systemd systemd
systemd 245.4-4ubuntu3.22 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid)
Detected virtualization podman.
Detected architecture x86-64.

Welcome to Ubuntu 20.04.6 LTS!

Set hostname to <38b6f9029637>.
[  OK  ] Reached target A minimal default target.
Startup finished in 145ms.

3.1.4 halt.target

打开另一个ssh窗口,执行

代码语言:shell
复制
podman stop systemd

来中断systemd服务,但是这里我们遇到了出错信息

它告诉我们缺失了halt.target

于是我们像刚刚创建default.target的方法再创建一个halt.target,并把它加入到systemd目录下

代码语言:shell
复制
echo \
"[Unit]
Description=A minimal halt target" > /root/halt.target

并且在Dockerfile追加这条指令”COPY halt.target /lib/systemd/system/“

编辑后的Dockfile如下图:

代码语言:shell
复制
root@ubun22 ~ [SIGKILL]# cat /root/Dockerfile
FROM ubuntu:20.04

RUN apt-get update &&     apt-get install --assume-yes systemd

RUN rm -rf /lib/systemd/system/* /etc/systemd/system/*

COPY default.target /lib/systemd/system/
COPY halt.target /lib/systemd/system/

CMD ["/lib/systemd/systemd"]

重新build和run

代码语言:shell
复制
podman build --tag systemd .
podman run --tty --rm --name systemd systemd

创建服务halt.service:来停止systemd服务

这里引入了新的名词 "[Service]" ,命令可以有ExecStart,ExecStop,ExecReload,DefaultDependencies

代码语言:shell
复制
[Unit]
Description=Halt systemd
DefaultDependencies=no

[Service]
ExecStart=systemctl --force halt

3.1.5 创建一个新的服务并注册halt.service

接着像添加default.target和halt.target一样的方法添加halt.service

同时在halt.target添加Requires选项,添加之后的halt.target如下,表示halt.target必须执行halt.service:

代码语言:shell
复制
[Unit]
Description=A minimal halt target
Requires=halt.service

在另外一个ssh窗口敲入

代码语言:shell
复制
podman stop systemd

就会出现这条warning信息

这里解释一些我们刚刚在halt.service添加的DefaultDependencies=no 其实是告诉systemd,该halt.service和不存在和别的有依赖关系。

实际上服务可以有依赖关系(并且 systemd 默认为每个服务添加一些依赖关系。其中一个缺省依赖项。在这条指令明确告诉 systemd 不要添加这些默认依赖项。sysinit.targetDefaultDependencies=no, 否则可能会报错。

四、添加日志记录系统

上诉的Waring信息是systemd告诉我们缺失journal socket日志套接字。journald 是 systemd 的日志记录框架。它接收来自不同来源的日志消息,如内核日志和系统日志。它还接收写入 systemd 服务的 stdout 和 stderr 的所有内容。

默认情况下,systemd 连接到日志。一旦日志消息存储在 journald 中,我们可以显示存储的所有日志消息,journal还可以优化查询,仅显示特定时间范围内的日志,或属于某个服务的日志。这非常有用,特别是如果出现问题并且我们想找出原因。

4.1 在default.target注册journald服务

要启动 journald,我们需要一个服务。这类似于上面的 halt 服务:我们提供应该执行的命令,然后将服务添加为目标的依赖项;这一次它是default.target,因为我们希望在容器“启动”后启动 journald。但除了服务之外,还需要其他东西:socket unit。

4.2 Socket和Service

systemd 将Socket与Service概念解绑。套接字成为可以存在于服务之外的概念。比如说,允许在不运行服务的情况下打开套接字,并且仅在套接字上有流量时才启动服务。在套接字单元文件中,我们可以指定要侦听的不同套接字类型,例如文件系统套接字或 IPv4 或 IPv6 套接字。

这里的例子system-journald,我们创建一个包含两个文件套接字的套接字单元,一个流套接字和一个数据报套接字。

创建/root/systemd-journald.socket内容如下

代码语言:shell
复制
[Unit]
Description=Journal Socket
DefaultDependencies=no

[Socket]
ListenStream=/run/systemd/journal/stdout
ListenDatagram=/run/systemd/journal/socket

创建/root/systemd-journald.service内容如下

代码语言:shell
复制
[Unit]
Description=Journal Service
DefaultDependencies=no

[Service]
ExecStart=/lib/systemd/systemd-journald
Sockets=systemd-journald.socket

然后我们创建一个服务单元,指定应该传入的套接字单元。然后在default.target更改内容如下:

代码语言:javascript
复制
[Unit]
Description=A minimal default target
Requires=systemd-journald.service

重新更改后的Dockerfile如下:

代码语言:shell
复制
FROM ubuntu:20.04

RUN apt-get update &&     apt-get install --assume-yes systemd

RUN rm -rf /lib/systemd/system/* /etc/systemd/system/*

COPY default.target /lib/systemd/system/
COPY halt.target /lib/systemd/system/
COPY halt.service /lib/systemd/system/
COPY systemd-journald.socket /lib/systemd/system/
COPY systemd-journald.service /lib/systemd/system/

CMD ["/lib/systemd/systemd"]

重新build和run

代码语言:shell
复制
podman build --tag systemd .
podman run --tty --rm --name systemd systemd

接下来在另一个ssh终端敲入

代码语言:shell
复制
podman exec systemd journalctl

可以看到日志系统输出的日志

只有两个来自 journald 本身的日志语句和一个来自 systemd 的日志语句。但是,如果我们有其他服务,它们的输出也会显示在这里。

最后关闭容器的时候,并不会像之前那样输出Warning

五、sysinit.target

最后,我们将添加一个 sysinit.target。这不是严格要求的,但如果我们以后想添加更多服务这样做会比较方便。所有服务都默认依赖此target,因此如果缺少它,则服务将无法启动。sysinit.target 内容如下:

代码语言:javascript
复制
[Unit]
Description=Empty sysinit target

然后添加为 default.target 的依赖项。

代码语言:javascript
复制
[Unit]
Description=A minimal default target
Requires=systemd-journald.service sysinit.target

这样以后服务都可以添加到sysinit.target的Requires里。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、服务鼻祖之Init
  • 二、当今主流 systemd
    • 2.1 认识systemd
    • 三、实践systemd
      • 3.1 初窥systemd
        • 3.2 最小systemd示例
          • 3.1.1 容器工具
          • podman的安装(以ubuntu为例):
          • 3.1.2 启动无systemd的容器
          • 3.1.3 default.target
          • 3.1.4 halt.target
          • 3.1.5 创建一个新的服务并注册halt.service
      • 四、添加日志记录系统
        • 4.1 在default.target注册journald服务
          • 4.2 Socket和Service
          • 五、sysinit.target
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档