Table of Contents
为什么把系统与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 堆, 系统化都提供了生产环境所需的可靠性和可预测性 。
首先从简单的单元文件开始,彻底测试,然后在环境文件、健康检查和安全硬化等高级选项上分层。通过这种方法,您的多克容器将幸存到重波、崩溃和配置变化中,而无需人工干预,从而释放出团队专注于构建应用程序。