注意:docker 正常退出和异常退出都不会自动发起SIGTERM,java正常或异常退出,jvm会发起SIGTERM信号

docker kill 直接杀死容器进程
docker stop是向容器进程发送SIGTERM信号,本文介绍容器中的进程捕获 SIGTERM 信号,优雅的退出。

github地址:https://github.com/626626cdllp/k8s/tree/master/test/docker-signal

先来了解一下信号

SIGINT

程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。

SIGQUIT

和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。

SIGTERM

程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。

SIGSTOP

停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.

添加退出处理逻辑

假设源dockerfile中定义的entrypoint为 python /app/server.py

现在需要重新写一个脚本entrypoint.sh

#!/bin/sh
# 实际运行的工作程序,将源主程序一般后台运行形势打开
nohup python /app/server.py &
# 中断信号处理函数,关闭python主程序
prog_exit()
    ps -ef| grep python |grep -v grep |awk '{print $2}'|xargs kill -15
# 注册中断处理函数
trap "prog_exit" 15
flag=1
# 前端形式运行睡眠,并交叉是否可以退出(python进程已不存在,也能在python进程自己退出的情况下关闭容器)
while [ $flag -ne 0 ];do
    sleep 3;
    flag=`ps -ef| grep atm |grep -v grep | wc -l`
done;

然后将dockerfile中的entrypoint改成启动新的脚本

FROM ubuntu:18.04 RUN apt-get update && \ apt-get install -y python3.6 && \ rm -rf /var/lib/apt/lists/* COPY ./server.py /data/ COPY ./entrypoint.sh /data/ WORKDIR /data/ # 使用sh做优雅退出 ENTRYPOINT ["/data/entrypoint.sh"] CMD ["bash"] # 直接向python发起退出命令 # ENTRYPOINT ["python3.6","/data/server.py"]

在业务代码中添加退出处理逻辑(python)

优雅退出的目的一般为处理业务没有处理完的事情。则一般需要在业务代码中处理退出信号。比如在上面的server.py中添加处理逻辑。

第一种方式,
上面我们使用ps -ef| grep python |grep -v grep |awk '{print $2}'|xargs kill -15命令向python服务发起了kill信号,我们可以在python脚本中也接收这个bash发起的kill信号。

第二种方式,
我们直接在业务代码中接收容器发起的SIGTERM 信号(方法更直接),这样dockerfile中就还是使用源入口点

ENTRYPOINT ["python3.6","/data/server.py"]

比如我们可以这样编写server.py

import signal
import time
is_running = True
def sigint_handler(num, stack):
    print(num,stack)
    print('receive sigint')
    global is_running
    is_running = False
def sigterm_handler(num, stack):
    print(num,stack)
    print('receive sigterm')
    global is_running
    is_running = False
def main():
    signal.signal(signal.SIGINT, sigint_handler)
    signal.signal(signal.SIGTERM, sigterm_handler)
    signal.siginterrupt(signal.SIGINT, False)
    signal.siginterrupt(signal.SIGTERM, False)
    while is_running:
        print('begin sleep')
        # 启动你的业务函数
        time.sleep(3)
    print("prepare exit")
    print("sleep 10")
    time.sleep(10)
    print("exit")
if __name__ == "__main__":
    main()

执行python3.6 server.py将看到这样的日志

begin sleep
begin sleep
begin sleep
15 <frame object at 0x2371b18>
receive sigterm
prepare exit
sleep 10

go语言接收退出信号

关于Go语言对系统Signal的处理,可以参考《Go中的系统Signal处理》一文。

https://tonybai.com/2014/10/09/gracefully-shutdown-app-running-in-docker/

java语言接收退出信号

https://blog.csdn.net/zhangpeterx/article/details/89454050

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// import com.lmax.disruptor.LifecycleAware;
//要注意让java进程的pid为1,不然docker stop信号接收不到
public class demo {
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Exited!");
        }));
        System.out.println("begin!");
        try{
            Thread.sleep(1000*60);
        } catch (Exception e) {
            e.printStackTrace();

k8s容器生命周期钩子

参考https://kubernetes.io/zh/docs/concepts/containers/container-lifecycle-hooks/

有两个钩子暴露在容器中:

PostStart

这个钩子在创建容器之后立即执行。 但是,不能保证钩子会在容器入口点之前执行。 没有参数传递给处理程序。

PreStop

在容器终止之前是否立即调用此钩子,取决于 API 的请求或者管理事件,类似活动探针故障、资源抢占、资源竞争等等。 如果容器已经完全处于终止或者完成状态,则对 preStop 钩子的调用将失败。 它是阻塞的,同时也是同步的,因此它必须在删除容器的调用之前完成。 没有参数传递给处理程序

k8s示例

# Source: superset/templates/secret.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: python-config
data:
  pre_start.py: |-
    import time
    import os
    if __name__=='__main__':
        print('pre start process %s.' % os.getpid())
        print('end')
        time.sleep(10)
        print('Process start.')
  pre_stop.py: |-
    import time
    import os
    if __name__=='__main__':
        print('pre stop process %s.' % os.getpid())
        print('end')
        print('Process end.')
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: python
  name: python-pod
spec:
  volumes:
    - name: python-configmap
      configMap:
        name: python-config
        items:
          - key: pre_stop.py  
            path: pre_stop.py
          - key: pre_start.py  
            path: pre_start.py
  containers:
  - command: ['xxxxx','xxxxx']
    name: python
    workingDir: /
    image: xxxxxxxxx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/bash","-c","python /pre_start.py >> pre_start.txt"]
      preStop:
        exec:
          command: ["/bin/bash","-c","python /pre_stop.py >> pre_stop.txt"]
    volumeMounts:
      - name: python-configmap
        mountPath: /pre_stop.py
        subPath: pre_stop.py
      - name: python-configmap
        mountPath: /pre_start.py
        subPath: pre_start.py

钩子处理程序的实现(可能会重复触发)

容器可以通过实现和注册该钩子的处理程序来访问该钩子。 针对容器,有两种类型的钩子处理程序可供实现:

  • Exec - 执行一个特定的命令,例如 pre-stop.sh,在容器的 cgroups 和名称空间中。 命令所消耗的资源根据容器进行计算。
  • HTTP - 对容器上的特定端点执行 HTTP 请求

对于PostStart 钩子,容器入口点和钩子异步触发。两个都运行完成,容器才进入running状态。
对于PreStop钩子。如果PreStop钩子在执行过程中挂起,Pod 阶段将保持在 Terminating 状态,并在 Pod 结束的 terminationGracePeriodSeconds 之后被杀死。 也就是说PreStop钩子的最多运行时间为terminationGracePeriodSeconds属性配置的值。

如果 PostStart 或 PreStop 钩子失败,它会杀死容器。

钩子处理程序的日志不会在 Pod 事件中公开。 如果处理程序由于某种原因失败,它将播放一个事件。 对于 PostStart,这是 FailedPostStartHook 事件,对于 PreStop,这是 FailedPreStopHook 事件。 您可以通过运行 kubectl describe pod <pod_name> 命令来查看这些事件

docker kill 直接杀死容器进程docker stop是向容器进程发送SIGTERM信号,本文介绍容器中的进程捕获 SIGTERM 信号,优雅的退出。先来了解一下信号SIGINT程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。SIGQUIT和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. ... ╰─➤ docker stop --help Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] Stop one or more running containers Options: --help Print usage -t, --t
要想完全掌握k8s 就需要有扎实的docker基础 镜像构建 熟练编写dockerfile。而完全掌握docker又需要精通Linux,包扩各种版本的Linux 尤其是微服务架构专用,基于musllibc库的alpine与deiban的slim镜像版本。服务的部署搭建流程、安全与调优相关。不然呢dockerfile给如何写 怎么写 写什么对于新手来说都是满满的疑问。本人从事培训行业多年 本着踏实出好课的原则推出一套,企业容器云实战指南-全民学容器课程。 希望通过技术与大家交朋友。 本次课程主要讲解了K8S的几大核心组件功能,并可以快速部署K8S,以及在维护K8S的常见问题总归纳。安装dashboard可视化界面组件,通过dashboard界面管理维护docker ,实现端口映射,创建本地仓库registry,详细讲解dockerfile文件,通过dockerfile制作镜像,实现docker volume数据卷共享。 企业容器云实战指南-全民学容器课程共分为三步骤。由简到难 由浅入深 循序渐进。 企业容器云实战指南-全民学容器课程①:docker k8s入门与进阶 企业容器云实战指南
如何利用termination GracePeriodSeconds 优雅地关闭你的服务 当涉及到分布式系统,处理故障是关键。Kubernetes通过利用可以监视系统状态并重新启动已停止执行的服务的控制器(controllers)来解决这个问题。另一方面,Kubernetes通常可以强制终止您的应用程序,作为系统正常运行的一部分。 在容器出现之前,大多数应用运行在虚拟机或者物理机上。如果应用程序崩溃,启动替换程序需要很长时间。如果您只有一台或两台机器来运行应用程序,那么这种恢复时间是不可接受的。 相反,在崩
在工作中遇到k8s利用脚本启动业务的容器,在停止容器时,总需要最大的停止时间。但直接在容器中启动业务程序,则可以比较快地退出。 同时,在k8s中,用busybox作为镜像启动无限循环停留一秒打印日志的脚本,则清除pod时也遭遇了比较长的时间。根据k8s官网介绍,如果没有提供preStop配置,则直接发送SIGTERM信号,明显启动脚本对于此信号没有处理好。对启动脚本加入截获信号操作,则可以正常退出。 # 主动截获信号 trap "exit" TERM while True; do echo "pri
所需要的命令主要为 docker run 。 例如,下面的命令输出一个 “Hello World”,之后终止容器。 $ docker run ubuntu:18.04 /bin/echo 'Hello world' Hello w... Docker 是一个开源项目,为开发人员和系统管理员提供了一个开放平台,可以将应用程序构建、打包为一个轻量级容器,并在任何地方运行。Docker 会在软件容器中自动部署应用程序。 在本篇中,我将介绍如何 docker 化一个 Python Django 应用程序,然后使用一个 docker-compose 脚本将应用程序作为容器部署到 docker 环境。 dbnuo@localhost ~ sw_vers ProductName: Mac OS X ProductVersion
Docker容器退出操作有两种情况,1、容器外面,关闭容器,这种情况退出方式多样,存在着是否保存他的运行情况,Ctrl+P和Ctrl+Q分别按,可以退出容器,让容器仍运行,命令Ctrl+C和exist退出容器,不运行、在容器里面退出可以使用docker stop 容器名/id。第二方面,是关于启动的,我们可以使用 docker attach 容器名/id 连接已经启动的容器docker start 容器名/id启动关闭的容器 ——.........
执行完docker start /etc/docker/daemon.json:/etc/docker/daemon.json jenk报错Error response from daemon: No such container: etc/docker/daemon.json:/etc/docker/daemon.json jenkins Error: failed to start containers: /etc/docker/daemon.json:/etc/docker/daemon.json
你的命令有误,应该是 `docker start jenkins`,不需要指定 `/etc/docker/daemon.json:/etc/docker/daemon.json` 这个参数。这个参数是用来指定 Docker daemon 的配置文件的路径的。如果你需要修改 Docker daemon 的配置,可以先使用 `docker container stop jenkins` 停止 Jenkins 容器,然后再使用 `docker run` 命令启动 Jenkins 并指定配置文件路径。例子如下: docker run -d \ --name jenkins \ --restart always \ -p 8080:8080 \ -p 50000:50000 \ -v /opt/jenkins:/var/jenkins_home \ -v /etc/docker/daemon.json:/etc/docker/daemon.json \ jenkins/jenkins:lts 其中 `-v /etc/docker/daemon.json:/etc/docker/daemon.json` 指定了 Docker daemon 的配置文件路径。
shm5945: Base64 base64 = new Base64(); String result = base64.encodeToString(base64str.getBytes("UTF-8"));//编码 System.out.println(result); byte[] b = base64.decode(result.getBytes());//解码 OPNET网络仿真分析-目 录 拉格朗易: 购买opnet书籍,已赞赏,1198796453@qq.com OPNET网络仿真分析-目 录 m0_48261282: 购买opnet书籍,已付款,1764641922@qq.com OPNET网络仿真分析-目 录 m0_48261282: 购买opnet书籍,已付款,1764641922@qq.com