从零开始写 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实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import