从零开始写 Docker(十)---实现 mydocker logs 查看容器日志

2024-04-09 13:36

本文主要是介绍从零开始写 Docker(十)---实现 mydocker logs 查看容器日志,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

mydocker-logs.png

本文为从零开始写 Docker 系列第十篇,实现类似 docker logs 的功能,使得我们能够查查看容器日志。


完整代码见:https://github.com/lixd/mydocker
欢迎 Star


推荐阅读以下文章对 docker 基本实现有一个大致认识:

  • 核心原理:深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs
  • 基于 namespace 的视图隔离:探索 Linux Namespace:Docker 隔离的神奇背后
  • 基于 cgroups 的资源限制
    • 初探 Linux Cgroups:资源控制的奇妙世界
    • 深入剖析 Linux Cgroups 子系统:资源精细管理
    • Docker 与 Linux 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 ps 命令,可以查看到当前运行中的容器了。

本篇主要实现 mydocker logs,让我们可以随时查看容器日志。

一般来说,对于容器中运行的进程,使日志打印到标准输出是一个非常好的实现方案,因此需要将容器中的标准输出保存下来,以便需要的时候访问。

我们就以此作为思路来实现 mydocker logs 命令:

  • 启动时将容器进程的标准输出挂载到/var/lib/mydocker/containers/{containerId}/{containerId}-json.log文件中
  • mydocker logs 则读取这个文件以获取容器日志

实际上 docker 实现也类似,他会把容器日志存储在var/lib/docker/containers/{containerId}/{containerId}-json.log 文件中。

2. 实现

具体实现包括两部分:

  • 1)重定向输出到文件
  • 2)实现 mydocker logs 命令

输出重定向

首先,需要修改一下原来的实现,在创建后台运行容器的时候,把进程的标准输出重新定向一下到日志文件。

前台容器依旧打印到 Stdout 即可

func NewParentProcess(tty bool, volume, containerId string) (*exec.Cmd, *os.File) {
// 省略其他内存if tty {cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderr} else {// 对于后台运行容器,将 stdout、stderr 重定向到日志文件中,便于后续查看dirPath := fmt.Sprintf(InfoLocFormat, containerId)if err := os.MkdirAll(dirURL, constant.Perm0622); err != nil {log.Errorf("NewParentProcess mkdir %s error %v", dirURL, err)return nil, nil}stdLogFilePath := dirPath + LogFilestdLogFile, err := os.Create(stdLogFilePath)if err != nil {log.Errorf("NewParentProcess create file %s error %v", stdLogFilePath, err)return nil, nil}cmd.Stdout = stdLogFilecmd.Stderr = stdLogFile}
// ...
}

实现 logs 命令

在 main_command.go 中添加一个 logCommand:

var logCommand = cli.Command{Name:  "logs",Usage: "print logs of a container",Action: func(context *cli.Context) error {if len(context.Args()) < 1 {return fmt.Errorf("please input your container name")}containerName := context.Args().Get(0)logContainer(containerName)return nil},
}

并加到 main 函数中。

func main(){// 省略其他内容app.Commands = []cli.Command{initCommand,runCommand,commitCommand,listCommand,logCommand,}
}

具体实现如下:

func logContainer(containerName string) {logFileLocation := fmt.Sprintf(container.InfoLocFormat, containerName) + container.LogFilefile, err := os.Open(logFileLocation)defer file.Close()if err != nil {log.Errorf("Log container open file %s error %v", logFileLocation, err)return}content, err := ioutil.ReadAll(file)if err != nil {log.Errorf("Log container read file %s error %v", logFileLocation, err)return}_, err = fmt.Fprint(os.Stdout, string(content))if err != nil {log.Errorf("Log container Fprint  error %v", err)return}
}

实现很简单,根据 containerId 拼接出完整路径,读取文件内容并重定向到标准输出即可。

3. 测试

启动一个后台容器

root@mydocker:~/feat-logs/mydocker# go build .
root@mydocker:~/feat-logs/mydocker# ./mydocker run -d -name mytop top
{"level":"info","msg":"createTty false","time":"2024-01-26T11:25:53+08:00"}
{"level":"info","msg":"resConf:\u0026{ 0  }","time":"2024-01-26T11:25:53+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-26T11:25:53+08:00"}
{"level":"error","msg":"mkdir dir /root/merged error. mkdir /root/merged: file exists","time":"2024-01-26T11:25:53+08:00"}
{"level":"error","msg":"mkdir dir /root/upper error. mkdir /root/upper: file exists","time":"2024-01-26T11:25:53+08:00"}
{"level":"error","msg":"mkdir dir /root/work error. mkdir /root/work: file exists","time":"2024-01-26T11:25:53+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-26T11:25:53+08:00"}
{"level":"info","msg":"command all is top","time":"2024-01-26T11:25:53+08:00"}

查看容器列表

root@mydocker:~/feat-logs/mydocker# ./mydocker ps
{"level":"error","msg":"read file /var/lib/mydocker/containers/0439540405/config.json error open /var/lib/mydocker/containers/0439540405/config.json: no such file or directory","time":"2024-01-26T11:26:13+08:00"}
{"level":"error","msg":"get container info error open /var/lib/mydocker/containers/0439540405/config.json: no such file or directory","time":"2024-01-26T11:26:13+08:00"}
ID           NAME         PID         STATUS      COMMAND     CREATED
0439540405   mytop        171754      running     top         2024-01-26 11:25:53

容器 Id 为 0439540405,查看对应目录是否生成了日志文件

root@mydocker:~/feat-logs/mydocker# ls /var/lib/mydocker/containers/0439540405/
0439540405-json.log config.json

其中的0439540405-json.log 就是日志文件,config.json 则是上一次添加的容器信息记录文件。

查看日志文件内容

root@mydocker:~/feat-logs/mydocker# cat /var/lib/mydocker/containers/0439540405/0439540405-json.log
Mem: 1793456K used, 241932K free, 1064K shrd, 91276K buff, 1354272K cached
CPU:  0.4% usr  0.0% sys  0.0% nic 99.3% idle  0.0% io  0.0% irq  0.2% sirq
Load average: 0.01 0.01 0.00 1/141 4PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND

说明,日志存储是正常的。

接下里执行 mydocker logs 看看是否能查询到日志

root@mydocker:~/feat-logs/mydocker# ./mydocker logs 0439540405
Mem: 1793424K used, 241964K free, 1064K shrd, 91316K buff, 1354280K cached
CPU:  0.0% usr  0.0% sys  0.0% nic  100% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 0.00 0.00 0.00 1/141 4PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND

可以看到,mydocker logs 命令成功运行并输出了容器的日志。

至此,说明我们的 mydocker logs 命令实现是 ok 的。

4. 小结

本篇主要实现 mydocker logs 命令,和 docker 实现基本类似:

  • 容器启动把 stdout、stderr 重定向到 /var/lib/mydocker/container/{containerId}/{containerId}-json.log 文件
  • 执行 mydocker logs 则根据容器 Id 找到对应文件,读取文件内容并打印

**【从零开始写 Docker 系列】**持续更新中,搜索公众号【探索云原生】订阅,文章。



完整代码见:https://github.com/lixd/mydocker
欢迎关注~

相关代码见 feat-logs 分支,测试脚本如下:

需要提前在 /root 目录准备好 busybox.tar 文件,具体见第四篇第二节。

# 克隆代码
git clone -b feat-logs https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试 
./mydocker run -d -name c1 top
# 查看容器 Id
./mydocker ps
# 根据 Id 查询日志
./mydocker logs ${containerId}

这篇关于从零开始写 Docker(十)---实现 mydocker logs 查看容器日志的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/888294

相关文章

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

MySQL双主搭建+keepalived高可用的实现

《MySQL双主搭建+keepalived高可用的实现》本文主要介绍了MySQL双主搭建+keepalived高可用的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、测试环境准备二、主从搭建1.创建复制用户2.创建复制关系3.开启复制,确认复制是否成功4.同

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.