转载备份
影子 DOM(Shadow DOM)
你的 docker stop,它优雅吗? - 无糖拿铁,谢谢
清理Docker的container,image与volume · 零壹軒·笔记
Create a PyPI Mirror Site with devpi-server – SRE
优雅的终止 docker 容器 | iTimothy
Odoo 14 开发者指南第二十一章 性能优化 | Alan Hou 的个人博客
Odoo 14 开发者指南第八章 高级服务端开发技巧 | Alan Hou 的个人博客
kafka 系列:设置日志数据保存过期时间(含某个 topic)、日志策略_NIO4444-CSDN 博客_kafka 配置数据过期时间
Chromium 历史版本离线安装包 - 下载方法
怎样将 props 传递给 {this.props.children} | WebFuse
HappyBaseDoc
用户指南 — HappyBase 1.2.0 文档
安装指南 — HappyBase 1.2.0 文档
API 参考 — HappyBase 1.2.0 文档
PostgreSQL 时间转换
JS 中创建给定长度的数组
GSAP 入门 - 学习中心 - 绿袜
操作系统复习 | Happy Coding
如何理解 ip 路由和操作 linux 的路由表 - CodeAntenna
Elasticsearch 7.11 tokenizer, analyzer and filter 以及 IK 分词配置同义词、远程拓展词库 – Brave new world
podman 容器内访问 host 主机的端口 - 知识库 - BSMI KB 基础标准矿产工业
吐血总结!100 道经典 Python 面试题集锦上(附答案)
中共党史简表(1919 年 - 1949 年)
Dockerfile 详解_万 wu 皆可爱的博客 - CSDN 博客_dockerfile
为你的 Python 应用选择一个最好的 Docker 映像 | 亚马逊 AWS 官方博客
Ubuntu Server 支持中文
docker push | Docker Documentation
docker 创建本地仓库详解 (push/pull)_乱红飞的博客 - CSDN 博客_docker push 本地仓库
基于 Ubuntu 20.04 安装 Kubernetes 1.18
PostgreSQL 集群篇——PostgreSQL 的配置文件解析_51CTO 博客_postGresql
【PostgreSQL】——主从流复制_Teingi 的博客 - CSDN 博客_postgresql 主从复制
PostgreSQL: Documentation: 14: 27.4. Hot Standby
postgresql 主从复制、主从切换_偷懒的小陈的博客 - CSDN 博客_postgresql 主从
Postgres 用户、角色与权限 :: 68hub — 技术博客
中国共产党第二十次全国代表大会在京开幕 一图速览二十大报告
配置 docker 通过代理服务器拉取镜像
IPVS no destination available - Kubernetes 实践指南
Python 风格规范 — Google 开源项目风格指南
互动测试!党的二十大报告 100 题
自定义 ESlint 规则
Java 读取 OpenSSL 生成的秘钥, 进行 RSA 加解密 | 数字魔法
CSS(一)chrome 浏览器表单自动填充默认样式 - autofil_半个 GIS 半个前端的博客 - CSDN 博客
Nginx 多级代理下的真实 IP 透传 - CodeAntenna
Jenkins 环境变量
人民币金额大写规范 - 内蒙古农业大学财务处
[转]nginx 开启 websocket - 浅忆博客
ceph 创建使用 rbd
《三》配置 ceph 存储池 pool - Buxl's blog
基于 K8S 搭建 Ceph 分部署存储 – 唐玥璨 | 博客
序言 · Kubernetes 中文指南——云原生应用架构实战手册
服务器配置 - Redis 安装配置 | 灰帽子 - 任令仓的技术博客
Ubuntu 配置 sudo 命令不需要输入密码_ubuntu sudo 免密_一路向前 - 执着的博客 - CSDN 博客
修改 Docker 数据目录位置,包含镜像位置 - 腾讯云开发者社区 - 腾讯云
微服务架构实践(API Gateway)
微服务网关:从对比到选型,由理论到实践 | Java 程序员进阶之路
聊聊微服务网关
微服务网关:从对比到选型,由理论到实践
odoo 实现表分区 partition
使用 keepalived 搭建高可用服务 - 简书
业务网关的落地实践_文化 & 方法_Qunar 技术沙龙_InfoQ 精选文章
部署 Kubernetes PostgreSQL 实例 | domac 的菜园子
一套包含完整前后端的系统如何在 K8S 中部署?_k8s 前端_木讷大叔爱运维的博客 - CSDN 博客
前端安全系列(二):如何防止 CSRF 攻击? - 美团技术团队
traefik 自定义中间件 | coolcao 的小站
CSRF 原理和实战利用 - FreeBuf 网络安全行业门户
安全运维 - 如何在 Kubernetes 中使用注释对 ingress-nginx 及后端应用进行安全加固配置实践_唯一极客知识分享的技术博客_51CTO 博客
Kubernetes 进阶使用之 Helm,Kustomize
各种加密算法比较
Docker 的三种网络代理配置 · 零壹軒 · 笔记
本文档使用 MrDoc 发布
-
+
首页
优雅的终止 docker 容器 | iTimothy
> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [xiaozhou.net](https://xiaozhou.net/stop-docker-container-gracefully-2016-09-08.html) 发表于 2016-09-08 | 分类于 [技术控](https://xiaozhou.net/categories/%E6%8A%80%E6%9C%AF%E6%8E%A7/) | [](https://xiaozhou.net/stop-docker-container-gracefully-2016-09-08.html#comments) | 阅读次数: 字数统计: 1.9k 字 | 阅读时长 ≈ 7 分钟 在 Docker 大行其道的今天,我们能够非常方便的使用容器打包我们的应用程序,并且将它在我们的服务器上部署并运行起来。但是,谈论到如何停掉运行中的 docker 容器并正确的终止其中的程序,这就成为一个非常值得讨论的话题了。 事实上,在我们日常的项目当中,这是我们经常需要面对和处理的问题: * 场景 A:假如我们打包在容器中的程序,提供 HTTP 方式的服务,负责处理各种 HTTP requests 并返回结果,我们必然希望在容器被停掉的时候,能够让程序有时间把已经在处理中的请求继续处理完毕,并返回结果给客户端。 * 场景 B:又比如我们打包在容器中的程序,负责写入数据到某个数据文件中,我们希望程序能够在容器被停掉的时候,有时间把内存中缓存的数据持久化到存储设备中,以防数据丢失。 * 场景 C:再比如现在流行的微服务架构中,一般会有服务发现的机制,也即每一个微服务在启动之后,都会主动把自己的地址信息注册到服务发现模块当中,让其他的服务可以知道自己的存在。而在容器被停掉的时候,微服务需要即时从服务发现模块中注销自己,以防止从 API Gateway 而来的请求被错误的路由到了已经被停止掉的微服务。 如上的各种场景中,都要求打包在容器中的应用程序能够被优雅的终止 (也即 gracefully shutdown),这种 gracefully shutdown 的方式,允许程序在容器被停止的时候,有一定时间做一些后续处理操作,这也是我们需要进一步探讨的话题。 [](#docker-stop-与-docker-kill-的区别 "docker stop 与 docker kill 的区别")docker stop 与 docker kill 的区别 ----------------------------------------------------------------------------------------------- Docker 本身提供了两种终止容器运行的方式,即 docker stop 与 docker kill。 ### [](#docker-stop "docker stop")docker stop 先来说说 docker stop 吧,当我们用 docker stop 命令来停掉容器的时候,docker 默认会允许容器中的应用程序有 10 秒的时间用以终止运行。所以我们查看 docker stop 命令帮助的时候,会有如下的提示: ``` → docker stop --help Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] Stop one or more running containers Options: --help Print usage -t, --time int Seconds to wait for stop before killing it (default 10) ``` 在 docker stop 命令执行的时候,会先向容器中 PID 为 1 的进程发送系统信号 SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认的 10 秒,会继续发送 SIGKILL 的系统信号强行 kill 掉进程。在容器中的应用程序,可以选择忽略和不处理 SIGTERM 信号,不过一旦达到超时时间,程序就会被系统强行 kill 掉,因为 SIGKILL 信号是直接发往系统内核的,应用程序没有机会去处理它。在使用 docker stop 命令的时候,我们唯一能控制的是超时时间,比如设置为 20 秒超时: ``` docker stop --time=20 container_name ``` ### [](#docker-kill "docker kill")docker kill 接着我们来看看 docker kill 命令,默认情况下,docker kill 命令不会给容器中的应用程序有任何 gracefully shutdown 的机会。它会直接发出 SIGKILL 的系统信号,以强行终止容器中程序的运行。通过查看 docker kill 命令的帮助,我们可以看到,除了默认发送 SIGKILL 信号外,还允许我们发送一些自定义的系统信号: ``` → docker kill --help Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...] Kill one or more running containers Options: --help Print usage -s, --signal string Signal to send to the container (default "KILL") ``` 比如,如果我们想向 docker 中的程序发送 SIGINT 信号,我们可以这样来实现: ``` docker kill --signal=SIGINT container_name ``` 与 docker stop 命令不一样的地方在于,docker kill 没有任何的超时时间设置,它会直接发送 SIGKILL 信号,以及用户通过 signal 参数指定的其他信号。 其实不难看出,docker stop 命令,更类似于 Linux 系统中的 kill 命令,二者都是发送系统信号 SIGTERM。而 docker kill 命令,更像是 Linux 系统中的 kill -9 或者是 kill -SIGKILL 命令,用来发送 SIGKILL 信号,强行终止进程。 [](#在程序中接收并处理信号 "在程序中接收并处理信号")在程序中接收并处理信号 ----------------------------------------- 了解了 docker stop 与 docker kill 的区别,我们能够知道,docker kill 适合用来强行终止程序并实现快速停止容器。而如果希望程序能够 gracefully shutdown 的话,docker stop 才是不二之选。这样,我们可以让程序在接收到 SIGTERM 信号后,有一定的时间处理、保存程序执行现场,优雅的退出程序。 接下来我们可以写一个简单的 Go 程序来实现信号的接收与处理,程序在启动过后,会一直阻塞并监听系统信号,直到监测到对应的系统信号后,输出控制台并退出执行。 ``` // main.go package main import ( "fmt" "os" "os/signal" "syscall" ) func main() { fmt.Println("Program started...") ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM) s := <-ch if s == syscall.SIGTERM { fmt.Println("SIGTERM received!") //Do something... } fmt.Println("Exiting...") } ``` 接下来使用交叉编译的方式来编译程序,让程序可以在 Linux 下运行: ``` CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o graceful ``` 编译好之后,我们还需要打包程序到容器中运行。于是,我们还得有个 Dockerfile。在这里,我们选择使用体积小又轻盈的 alpine 镜像作为基础镜像,打包这个 Go 程序: ``` from alpine:latest MAINTAINER Timothy ADD graceful /graceful CMD ["/graceful"] ``` 这里需要避开的一个坑,是 Dockerfile 中 CMD 命令的用法。CMD 命令有两种方式: * CMD /graceful 使用 **CMD command param1 param2** 这种方式,其实是以 shell 的方式运行程序。最终程序被执行时,类似于 / bin/sh -c 的方式运行了我们的程序,这样会导致 / bin/sh 以 PID 为 1 的进程运行,而我们的程序只不过是它 fork/execs 出来的子进程而已。前面我们提到过 docker stop 的 SIGTERM 信号只是发送给容器中 PID 为 1 的进程,而这样,我们的程序就没法接收和处理到信号了。 * CMD [“/graceful”] 使用 **CMD [“executable”,”param1”,”param2”]** 这种方式启动程序,才是我们想要的,这种方式执行和启动时,我们的程序会被直接启动执行,而不是以 shell 的方式,这样我们的程序就能以 PID=1 的方式开始执行了。 话题转回来,我们开始执行容器构建操作,打包程序: ``` docker build -t registry.xiaozhou.net/graceful:latest . ``` 打包过后的镜像,才 6MB 左右: ``` λ Timothy [workspace/src/graceful] → docker images REPOSITORY TAG IMAGE ID CREATED SIZE registry.xiaozhou.net/graceful latest b2210a85ca55 20 hours ago 6.484 MB ``` 启动并运行容器: ``` λ Timothy [workspace/src/graceful] → docker run -d --name graceful b2210a85 ``` 查看容器运行状态: ``` λ Timothy [workspace/src/graceful] → docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fd18eedafd16 b221 "/graceful" 3 seconds ago Up 2 seconds graceful ``` 查看容器输出,能看到程序已经正常启动: ``` λ Timothy [workspace/src/graceful] → docker logs graceful Started... ``` 接着我们要使用 docker stop 大法,看程序能否响应 SIGTERM 信号: ``` λ Timothy [workspace/src/graceful] → docker stop graceful graceful ``` 最后,查看容器的日志,检验输出: ``` λ Timothy [workspace/src/graceful] → docker logs graceful Started... SIGTERM received! Exiting... ``` [](#总结 "总结")总结 -------------- 用 docker kill 命令,可以简单粗暴的终止 docker 容器中运行的程序,但是想要优雅的终止掉的话,我们需要使用 docker stop 命令,并且在程序中多花一些功夫来处理系统信号,这样能保证程序不被粗暴的终止掉,从而实现 gracefully shutdown。
幻翼
2021年11月22日 10:57
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码