从零开始写 Docker(十)---实现 mydocker logs 查看容器日志
本文为从零开始写 Docker 系列第十篇,实现类似 docker logs 的功能,使得我们能够查查看容器日志。
完整代码见: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 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.Stdin
cmd.Stdout = os.Stdout
cmd.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 + LogFile
stdLogFile, err := os.Create(stdLogFilePath)
if err != nil {
log.Errorf("NewParentProcess create file %s error %v", stdLogFilePath, err)
return nil, nil
}
cmd.Stdout = stdLogFile
cmd.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.LogFile
file, 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 4
PID 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 4
PID 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 查看容器日志的更多相关文章
- docker logs 查看容器日志操作
查看日志 官方文档:https://docs.docker.com/engine/reference/commandline/logs/ # 查看指定数量的实时日志 # docker logs -tf ...
- 【docker】docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志
如题: docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志 场景再现: docker部署并启动 ...
- docker logs 查看实时日志
docker logs -f -t --since="2017-05-31" --tail=10 edu_web_1 --since : 此参数指定了输出日志开始日期,即只输出指定 ...
- docker查看容器日志
原文:docker查看容器日志 前言 $ sudo docker logs -f -t --tail 行数 容器名 1 2 1.命令查看 root@c68d4b5dd583c4f4ea30da2989 ...
- docker启动失败如何查看容器日志
docker启动失败如何查看容器日志 在使用docker的时候,在某些未知的情况下可能启动了容器,但是过了没几秒容器自动退出了.这个时候如何排查问题呢? 通常碰到这种情况无非就是环境有问题或者应用有问 ...
- Docker笔记(十三):容器日志采集实践
日志是服务运行过程中的一个关键环节,借助日志,我们可以排查定位问题,也可以借助集中化的日志管理平台(如ELK)来做一些必要的数据统计分析.在Docker环境中,日志的采集比传统环境更为复杂,因此了解D ...
- docker查看容器
1.查看启动成功的容器,这个命令看不见的说明已经炸了: # docker ps 2.查看所有容器,死的活的都能看见: # docker ps -a 3.查看容器日志: # docker logs c8 ...
- Docker 学习基本操作与守护式容器
Docker 学习基本操作与守护式容器 容器操作 运行容器 docker run --name指定名字 -istdin -ttty虚拟终端 在终端中用 exit 即可退出容器,并结束运行 查看容器 p ...
- docker概述与安装及运行容器
传统虚拟化 传统虚拟化步骤 1.安装虚拟化软件以及虚拟化的管理软件 2.创建虚拟机 3.给虚拟机安装os 4.在虚拟机内部不是应用(http.db之类的应用) 传统虚拟化的特点 1.VM与VM之间是完 ...
- 【docker】【mysql】docker安装mysql,阿里云docker镜像加速器,docker搜索查看远程仓库上的镜像,docker拉取镜像,查看本地所有镜像,查看容器的运行状况,查看容器的详细信息
在docker上安装mysql有两种方式 1.通过Dockerfile构建 2.直接在docker hub上拉取镜像安装 =================本篇采用方法2=============== ...
随机推荐
- Taurus.MVC WebMVC 入门开发教程7:部分视图和页面片段(结束篇)
本系列的目录大纲为: Taurus.MVC WebMVC 入门开发教程1:框架下载环境配置与运行 Taurus.MVC WebMVC 入门开发教程2:一个简单的页面呈现 Taurus.MVC WebM ...
- STL-string模拟实现
1 #pragma once 2 3 #include<iostream> 4 #include<string.h> 5 #include<assert.h> 6 ...
- 测绘线性代数(四):数学期望、协方差、PCA
数学期望 E(X) = ∑pixi,X为所有xi的集合,pi为xi对应的概率. 通常来说,xi都是离散的,除非像高斯分布,假设xi不是离散的,才用上式. 当xi是离散的,那么: E(X) = 1 / ...
- Java 面向对象的特征一: * 封装与隐藏
1 * @ 面向对象的特征一: 2 * 封装与隐藏 3 * 创建一个类的对象以后,我们可以通过"对象.属性"的方式,对 4 * 对象的属性进行赋值,这里,赋值操作要受到属性的数据类 ...
- Thinkphp6 自定义命令创建类文件
以创建控制器为例 1.先通过 think 命令创建一个make文件,效果如下图: php think make:command make/MyController 2.修改上面创建的文件[MyCont ...
- 继续总结Python中那些简单好用的用法
上一篇文章Python中那些简单又好用的特性和用法发出后,群里的小伙伴又给补充了几个好用的用法,结合生产实用经验汇总整理如下,各位看官如有需要请自取 反射,反射是一种机制,可以在运行时获取.检查和修改 ...
- 测试打包失败 已解决 分析过程 - 关键字 Jenkins nexus package-lock.json npm install build
Jenkins 打包失败 npm run build 总是失败,每次失败报错还不一样. 然后 npm install 每次安装 还总有包超时 MobaXterm1_CHS1 SSH 链接,手工安装也不 ...
- 基于泰凌微的TLSR8355芯片的2.4G无线私有协议PCBA设计调试总结
一 前记 经常做物联网的类的产品,TLSR8355凭借着它的射频距离远,功能强大等优点成为很多客户的首选.TLSR8355系列专用于2.4GHz射频系统芯片解决方案,如零售/物流.专用网络.Beaco ...
- Hibernate之Criteria
1,Criteria Hibernate 设计了 CriteriaSpecification 作为 Criteria 的父接口,下面提供了 Criteria和DetachedCriteria . ...
- 大端(big endian) 小端(little endian) --- 在多字节存储 和 多字节通信中的含义(我还是太年轻了)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...