为什么把系统与Docker结合起来用于生产部署

现代基础设施要求集装箱化服务能幸存到意外的重boots、硬件故障或软件包更新。虽然Docker提供了重启政策(),但只要Docker守护进程运行,这些政策就只能起作用。 系统化 — — Ubuntu、Debian、Fedora、CentOS和大多数现代Linux发行版使用的init系统 — — 通过管理Docker守护进程本身的生命周期,并甚至在Docker套接字可用之前就能够启动容器,其好处包括:

  • 通过依赖指令(如网络后.object, 之后的 docker. service)保证启动命令.
  • 通过统一记录,使调试直接
  • 使用系统化单元指令对资源限制(CPU、内存、I/O)进行精细控制
  • 失败时自动重新启动, 并设定可配置的延迟和爆破限制
  • 支持套接字激活和定时启动

通过将每个Docker容器包在一个系统化的服务文件中,操作团队获得一个一致的接口,用于启动,停止,以及监测容器,减少对临时操作脚本的依赖和人工干预.

为单一的嵌入容器创建系统服务

标准方法包括写一个服务单元文件,要求Docker命令运行并停止容器。下面我们一步一步地走过过程,从一个基本的例子开始,然后涵盖共同的生产要求。

步骤1: 写服务单元文件

创建一个名为 [[FLT: 2]] 的文件。 使用以下模板作为起点 :

[Unit]
Description=My Application Container
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Restart=always
RestartSec=10
StartLimitBurst=3
ExecStartPre=-/usr/bin/docker kill myapp
ExecStartPre=-/usr/bin/docker rm myapp
ExecStart=/usr/bin/docker run --rm --name myapp \
 -e DB_HOST=10.0.1.50 \
 -e DB_PORT=5432 \
 -v /data/myapp:/app/data \
 -p 8080:8080 \
 myregistry/myapp:latest
ExecStop=/usr/bin/docker stop -t 10 myapp
ExecStopPost=-/usr/bin/docker rm myapp

[Install]
WantedBy=multi-user.target

关键指令的说明:

  • ] –确保Docker守护进程在开始容器前运行.
  • ]——如果停靠多克,这种服务也停止.
  • ] –清理前一次运行中任何遗留的容器(]]前缀表示此处的故障为非致命的).
  • ] –使用在容器停放时自动移除.
  • ] – 优雅地以超时(10秒)停放容器.
  • ] – 不论退出代码,重启容器.
  • ] – 10秒后重启.
  • ] – 将重启限制在每间隔3次尝试(默认为10秒)以避免重启循环.

步骤2:启用并启动服务

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service

告诉系统重读服务文件. ]创建了连锁,使服务从启动开始.

以标准系统命令管理该处

一旦服务运行,你就能像其他系统服务一样控制它:

  • 开始:[]]]
  • 停止:]]
  • 重新启动:[]]]
  • 现状:]]
  • 日志:[](按活日志)

高级配置模式

生产部署通常需要的不仅仅是简单的。下面是常见的增强功能,您可以添加到您的系统化服务文件中。

传递环境变量

不推荐服务文件中的硬编码秘密或配置。 相反, 使用单独的环境文件 :

[Service]
EnvironmentFile=-/etc/myapp/env.conf
ExecStart=/usr/bin/docker run --rm --name myapp \
 --env-file /etc/myapp/env.conf \
 myregistry/myapp:latest

在路径表示服务即使不存在文件(在初始设置中有用)之前,前缀也会启动服务.

联网和港口绑定

对于需要在同一主机上相互通信的容器,考虑使用或用户定义的桥网。

ExecStart=/usr/bin/docker run --rm --name web \
 --network=my-net \
 -p 443:443 \
 -v /etc/ssl/certs:/etc/ssl/certs:ro \
 myregistry/web:latest

如果使用自定义网络, 请在服务开始前确保网络存在。 您可以添加一个 [[FLT: 27] ] 命令来创建它 :

ExecStartPre=/usr/bin/docker network create my-net

集装箱间附属关系

当一个容器要求另一个容器在开始前准备好(例如等待数据库的网络应用程序),系统可以执行命令。为数据库创建第二个服务文件,然后:

[Unit]
Description=Web App Container
After=network-online.target docker.service mydb.service
BindsTo=mydb.service

将网络应用的生命周期与数据库容器联系起来 — 如果数据库停止,网络应用也会停止.

健康检查和准备情况

嵌入式健康检查可以与系统相结合,以防止过早提供服务。使用 ,并使用一个脚本,用于对健康终点进行投票:

ExecStartPost=/usr/local/bin/wait-for-health.sh http://localhost:8080/health 30

脚本只有在容器健康时才应退出 0。如果失败,系统标记单元为失败 。

通过系统限制资源d

您可以在 cgroup 级别上限制容器的CPU 和内存, 而无需多克自己的资源旗。 这在运行单个主机的多个容器时特别有用 :

[Service]
MemoryMax=512M
CPUQuota=50%

这些设置会创建一个硬限制,系统执行时会独立于Docker.

管理多个集装箱:系统化对多克编译

对于少数容器(例如2-5),单个系统化的服务文件是简单和可以维护的。然而,当一个项目涉及许多互联服务时,Docker Compose就变得更加方便了。您仍可利用系统化来通过创建一个呼叫的单一服务单元来协调整个Docker Compose堆栈。例如:

[Unit]
Description=My Application Stack
After=network-online.target docker.service
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/docker-compose up -d
ExecStop=/usr/local/bin/docker-compose down

[Install]
WantedBy=multi-user.target

这种方法可以使您简单掌握“编译”定义服务与系统周期管理相结合的功能。请注意,使用是因为立即退出。 将单位保持“活动”状态,直到调用。

你该选哪种方法?

  • 独立系统化服务 – 最适合遗留应用,严格启动命令的服务,或需要每个容器的资源限制时.
  • Docker 编译系统 – 理想的微服务堆栈,其中依赖性由Compose内部处理,您需要单个单位来管理整个组.

解决共同问题

即使设置得小心,你也会遇到问题。下面是经常发生的陷阱及其解决办法。

服务失败, 包含“ 无法连接到 Docker 守护进程 ”

这通常意味着服务在 Docker 套接字准备好之前启动。 请确保您的单位包含 [ [FLT: 40] 和 [[FLT: 41] ]。 同时检查是否启用了 Docker 守护进程 : [[FLT: 42]]] 。

循环中容器重新打开

如果容器立即退出,系统化的系统将按和]继续重新启动。用]检查容器日志。增加(例如,30秒)并设定],以防止繁忙循环。

服务不停止干净

配置不正确的 [[FLT: 48] ] 可能让容器运行。 请检查是否使用正确的容器名称 。 如果停止失败, 请使用 [[FLT: 50] 强制删除容器 。

未装入的环境变量

如果您使用 [[FLT: 51]] , 请确认文件存在, 并且可以通过 root 读取。 请避免引用问题 — — 系统条从变量值中引用。 对于秘密注射, 请考虑使用系统化证书或专用的秘密管理器 。

安全考虑

通过系统运行的嵌入容器可提高几个安全点:

  • 尽可能将系统化服务作为非根用户运行(使用]和指令),但确保用户能够访问Docker套接字或以无根模式运行).
  • 除非绝对必要,否则避免在系统单元中使用]。
  • 当容器不需要写给主机时,使用只读绑定挂载().
  • 利用系统和],使部队难以抵御逃跑。
[Service]
ProtectSystem=strict
ReadWritePaths=/var/log/myapp
PrivateTmp=true
User=myappuser

外部资源

欲进一步阅读,请参考这些正式参考资料:

结论

与 Docker 容器整合系统, 将您与 Linux 系统的其他部分无缝整合的强大、 自动化的启动机制。 通过写入结构完善的服务单元文件, 您可以控制启动命令, 管理依赖性, 设定资源限制, 并利用您操作团队已经知道的工具来监控日志 。 无论是为每个容器选择单个服务, 还是单个单位来协调一个 Compose 堆, 系统化都提供了生产环境所需的可靠性和可预测性 。

首先从简单的单元文件开始,彻底测试,然后在环境文件、健康检查和安全硬化等高级选项上分层。通过这种方法,您的多克容器将幸存到重波、崩溃和配置变化中,而无需人工干预,从而释放出团队专注于构建应用程序。