从零开始写 Docker(十二)---实现 mydocker stop 停止容器
本文为从零开始写 Docker 系列第十二篇,实现类似 docker stop
的功能,使得我们能够停止指定容器。
完整代码见:https://github.com/lixd/mydocker
欢迎 Star
推荐阅读以下文章对 docker 基本实现有一个大致认识:
- 核心原理:深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs
- 基于 namespace 的视图隔离:探索 Linux Namespace:Docker 隔离的神奇背后
- 基于 cgroups 的资源限制
- 基于 overlayfs 的文件系统:Docker 魔法解密:探索 UnionFS 与 OverlayFS
- 基于 veth pair、bridge、iptables 等等技术的 Docker 网络:揭秘 Docker 网络:手动实现 Docker 桥接网络
开发环境如下:
root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal
root@mydocker:~# uname -r
5.4.0-74-generic
注意:需要使用 root 用户
1. 概述
之前实现了 mydocker run -d
让容器能够后台运行,但是没有实现停止功能,导致无法停止后台运行的容器。
本篇则是实现mydocker stop
命令,让我们能够直接停止后台运行的容器。
2. 实现
容器的本质是进程,那么停止容器就可以看做是结束进程。因此 mydocker stop 的实现思路就是先根据 containerId 查找到它的主进程 PID,然后 Kill 发送 SIGTERM 信号,等待进程结束就好。
整个流程如下图所示:
stopCommand
首先在 main_command.go 中增加 stopCommand:
var stopCommand = cli.Command{
Name: "stop",
Usage: "stop a container,e.g. mydocker stop 1234567890",
Action: func(context *cli.Context) error {
// 期望输入是:mydocker stop 容器Id,如果没有指定参数直接打印错误
if len(context.Args()) < 1 {
return fmt.Errorf("missing container id")
}
containerName := context.Args().Get(0)
stopContainer(containerName)
return nil
},
}
然后在 main 函数中加入该命令:
func main(){
// 省略其他内容
app.Commands = []cli.Command{
initCommand,
runCommand,
commitCommand,
listCommand,
logCommand,
execCommand,
stopCommand,
}
}
核心逻辑都在 stopContainer 中,command 这边只需要解析并传递参数即可。
stopContainer
stopContainer 中就是停止容器的具体实现了。实现也很简单,大致可以分为 3 步:
- 1)首先根据 ContainerId 找到之前记录的容器信息的文件并拿到容器具体信息,主要是 PID
- 2)然后调用 Kill 命令,给指定 PID 发送 SIGTERM
- 3)最后更新容器状态为 stop 并写回记录容器信息的文件;
具体代码如下:
func stopContainer(containerId string) {
// 1. 根据容器Id查询容器信息
containerInfo, err := getInfoByContainerId(containerId)
if err != nil {
log.Errorf("Get container %s info error %v", containerId, err)
return
}
pidInt, err := strconv.Atoi(containerInfo.Pid)
if err != nil {
log.Errorf("Conver pid from string to int error %v", err)
return
}
// 2.发送SIGTERM信号
if err = syscall.Kill(pidInt, syscall.SIGTERM); err != nil {
log.Errorf("Stop container %s error %v", containerId, err)
return
}
// 3.修改容器信息,将容器置为STOP状态,并清空PID
containerInfo.Status = container.STOP
containerInfo.Pid = " "
newContentBytes, err := json.Marshal(containerInfo)
if err != nil {
log.Errorf("Json marshal %s error %v", containerId, err)
return
}
// 4.重新写回存储容器信息的文件
dirPath := fmt.Sprintf(container.InfoLocFormat, containerId)
configFilePath := path.Join(dirPath, container.ConfigName)
if err := os.WriteFile(configFilePath, newContentBytes, constant.Perm0622); err != nil {
log.Errorf("Write file %s error:%v", configFilePath, err)
}
}
getInfoByContainerId 如下,根据 containerId 拼接出具体 path,读取文件内容拿到启动时记录的容器信息,其中就包括 PID。
func getInfoByContainerId(containerId string) (*container.Info, error) {
dirPath := fmt.Sprintf(container.InfoLocFormat, containerId)
configFilePath := path.Join(dirPath, container.ConfigName)
contentBytes, err := os.ReadFile(configFilePath)
if err != nil {
return nil, errors.Wrapf(err, "read file %s", configFilePath)
}
var containerInfo container.Info
if err = json.Unmarshal(contentBytes, &containerInfo); err != nil {
return nil, err
}
return &containerInfo, nil
}
3. 测试
测试流程为:
- 1)mydocker run -d创建一个 detach 的后台容器
- 2)mydocker stop 该容器
- 3)mydocker ps 查看容器状态是否变更,ps 查看容器进程是否消失
创建一个 detach 容器:
root@mydocker:~/feat-stop/mydocker# go build .
root@mydocker:~/feat-stop/mydocker# ./mydocker run -d -name bird top
{"level":"info","msg":"createTty false","time":"2024-01-30T14:04:13+08:00"}
{"level":"info","msg":"resConf:\u0026{ 0 }","time":"2024-01-30T14:04:13+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-30T14:04:13+08:00"}
{"level":"error","msg":"mkdir dir /root/merged error. mkdir /root/merged: file exists","time":"2024-01-30T14:04:13+08:00"}
{"level":"error","msg":"mkdir dir /root/upper error. mkdir /root/upper: file exists","time":"2024-01-30T14:04:13+08:00"}
{"level":"error","msg":"mkdir dir /root/work error. mkdir /root/work: file exists","time":"2024-01-30T14:04:13+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/root/busybox,upperdir=/root/upper,workdir=/root/work /root/merged]","time":"2024-01-30T14:04:13+08:00"}
{"level":"info","msg":"command all is top","time":"2024-01-30T14:04:13+08:00"}
分别使用 ps 命令和 mydocker ps 命令查询一下 PID
root@mydocker:~/feat-stop/mydocker# ./mydocker ps
ID NAME PID STATUS COMMAND CREATED
3184421796 bird 180831 running top 2024-01-30 14:04:1
root@mydocker:~/feat-stop/mydocker# ps -ef|grep top
root 180831 1 0 14:04 pts/10 00:00:00 top
可以看到,PID 为 180831 的进程就是我们的容器进程。
现在执行 stop 命令停止该容器
root@mydocker:~/feat-stop/mydocker# ./mydocker stop 3184421796
再通过 mydocker ps 命令查看一下
root@mydocker:~/feat-stop/mydocker# ./mydocker ps
ID NAME PID STATUS COMMAND CREATED
3184421796 bird stopped top 2024-01-30 14:04:13
可以看到,状态变成了 stopped,并且 PID 一栏也是空的。
最后执行 ps 查看一下是不是真的停掉了
root@mydocker:~/feat-stop/mydocker# ps -ef|grep top
root 180869 177607 0 14:06 pts/10 00:00:00 grep --color=auto top
可以看到,原来容器的进程已经退出了,说明 stop 是成功的。
4. 小结
本篇主要实现 mydocker stop
命令,根据 ContainerId 找到容器进程 PID,然后 Kill 并更新容器状态信息。
完整代码见:https://github.com/lixd/mydocker
欢迎关注~
【从零开始写 Docker 系列】持续更新中,搜索公众号【探索云原生】订阅,阅读更多文章。
相关代码见 feat-stop
分支,测试脚本如下:
需要提前在 /root 目录准备好 busybox.tar 文件,具体见第四篇第二节。
# 克隆代码
git clone -b feat-stop https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试
./mydocker run -d -name c1 top
# 查看容器 Id
./mydocker ps
# stop 停止指定容器
./mydocker stop ${containerId}
从零开始写 Docker(十二)---实现 mydocker stop 停止容器的更多相关文章
- Java从零开始学四十二(DOM解析XML)
一.DOM解析XML xml文件 favorite.xml <?xml version="1.0" encoding="UTF-8" standalone ...
- Docker(十二)-Docker Registry镜像管理
Registry删除镜像.垃圾回收 Docker仓库在2.1版本中支持了删除镜像的API,但这个删除操作只会删除镜像元数据,不会删除层数据.在2.4版本中对这一问题进行了解决,增加了一个垃圾回收命令, ...
- Java从零开始学三十二(正则表达式)
一.为什么要有正则 正则表达式可以方便的对数据进行匹配,可以执行更加复杂的字符串验证.拆份.替换功能. 例如:现在要求判断一个字符串是否由数字组成,则可以有以下的两种做法: 不使用正则完成 使用正则完 ...
- 从零开始学安全(十二)●建立自己的DNS服务器
我们的环境windows server 2012 虚拟机 打开服务器的添加角色和向导功能 添加DNF服务器安装 点击 在正向查找区域 反键新建区域 这里我一般输入一级域名 这是输入baidu.co ...
- 十二、Spring之IOC容器初始化
Spring之IOC容器初始化 前言 在前面我们分析了最底层的IOC容器BeanFactory,接着简单分析了高级形态的容器ApplicationContext,在ApplicationContext ...
- Spring核心技术(十二)——基于Java的容器配置(二)
使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bea ...
- 软工 · 第十二次作业 - Beta答辩总结
福大软工 · 第十二次作业 - Beta答辩总结 写第十二次的时候操作失误直接在Beta版本的博客里改了...第七次冲刺的作业链接补在这里 Beta(7/7) 组长本次博客作业链接 项目宣传视频链接 ...
- Hyperledger Fabric 1.0 从零开始(十二)——fabric-sdk-java应用【补充】
在 Hyperledger Fabric 1.0 从零开始(十二)--fabric-sdk-java应用 中我已经把官方sdk具体改良办法,即使用办法发出来了,所有的类及文件都是完整的,在文章的结尾也 ...
- 庐山真面目之十二微服务架构基于Docker搭建Consul集群、Ocelot网关集群和IdentityServer版本实现
庐山真面目之十二微服务架构基于Docker搭建Consul集群.Ocelot网关集群和IdentityServer版本实现 一.简介 在第七篇文章<庐山真面目之七微服务架构Consul ...
- 并发编程从零开始(十二)-Lock与Condition
并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 "可重入锁"是指当一个线程调用 object.l ...
随机推荐
- vue3.0 demo代码记录
最近在研究vue3.0,这里给大家分享下找到的介绍, 以下内容转载于:https://blog.csdn.net/baidu_31108777/article/details/114880810?op ...
- 工作中常用且容易遗忘的 CSS 样式清单
注:本文转载自公众号 Vue中文社区的一篇文章 1.文字超出部分显示省略号 单行文本的溢出显示省略号(一定要有宽度) p{ width:200rpx; overflow: hidden; ...
- Win10 如何在桌面显示我的电脑
Win10桌面右键鼠标,然后在弹出来的选项中选择个性化. 选择了个性化后会弹出设置界面,在设置中选择[主题] 找到[桌面图标设置] 点击[桌面图标设置],会弹出一个对话框,该对话框有可以设置显示的图标 ...
- 什么是HSV色彩空间
BGR色彩空间是基于三基色而言,即红色.绿色.蓝色.而HSV色彩空间则是基于色调.饱和度和亮度而言的. 色调(H)是指光的颜色,例如,彩虹中的赤,橙,黄,绿,青,蓝,紫分别表示不同的色调.在OpenC ...
- vivado的非嵌入ILA的使用
vivado非嵌入ILA的使用 1.实验原理 前面在vivado中联合vitis设计时接触过ila,那个时候采用的方法是直接调用IP核在原理图中连接.这个方法简单直接,可以将自己所需的测量信号转移到I ...
- Windows下mDNS查询API—DnsStartMulticastQuery/DnsStopMulticastQuery的使用
背景及问题: 目前很多局域网设备通过mNDS协议实现互联,IP地址为自动IP段-169.254.x.x,有时候设备厂家提供的API需要通过知晓局域网中的IP地址/设备名,才能连接该设备.这样要求每个软 ...
- 测试开发之前端篇-CSS层叠式样式表
CSS是层叠样式表(Cascading Style Sheets)的缩写,用于描述如何在屏幕.纸张或其他媒体上显示HTML元素,包括了定义网页及其元素的布局.风格.大小.位置和颜色等属性. CSS的语 ...
- 赋能千行百业数字化转型,OpenHarmony生态新成果即将亮相HDC2022
第四届华为开发者大会 2022(Together)将于11月4日-6日在东莞召开,OpenAtom OpenHarmony(以下简称"OpenHarmony")将携生态新成果亮相 ...
- 初学STM32 SDIO (二)
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输. 1.SD命令格式 SD命令格式固定为48bit,都是通过 ...
- 华为Push用户增长服务:精准触达,加速增长
速戳了解华为Push用户增长服务:通过精细化运营,助力开发者高效实现用户增长,提升用户活跃度和粘性! 合作咨询请点此链接 了解更多详情>> 访问华为开发者联盟官网 获取开发指导文档 华为移 ...