生成式AI领域的最新成果都在这里!抢 QCon 展区门票 了解详情
写点什么

细说 Windows 与 Docker 之间的趣事

  • 2016-08-29
  • 本文字数:2892 字

    阅读完需:约 9 分钟

众所周知,Docker 能打通开发和运维的任督二脉,所谓 DevOps 是也。有朋友说,这符合王阳明的"知行合一"之教。

而 Windows Server 2016 内置的 Windows Docker 亦已经出来一段时间,这里就来和诸公汇报一下测试结果。

Linux 和 Windows,Docker 里各有多少进程

在安装配置 Container Host 的时候,经常报错 Container OS Image 下载失败 (没办法,墙内的缘故)。

什么是 Container OS?顾名思义,是从容器角度看到的 OS。

Container OS 实际是应用所依赖的用户模式 (User mode)OS 组件,对于 Windows 容器来说,例如 ntdll.dll、kernel32.dll 或者 coresystem.dll 之类的 System DLL。主机上的所有容器共享内核模式 (Kernel mode)OS 组件,对于 Windows,就是 ntoskrnl.exe,还有驱动等。

例如对于以下命令,意味着 Windows 系统从 docker 映像中获取 Windows Server Core 的用户模式 OS 组件,并启动 cmd 获得 Shell。

复制代码
docker run -it windowsservercore cmd

Linux 也是一理,如果运行以下命令,意味着从 docker 映像中获取 Ubuntu 的用户模式组件,并且启动 Bash Shell。

复制代码
docker run -it ubuntu /bin/bash

对于以上两个容器,Linux 容器里的进程比较少,可以参考以下截图:

而 Windows 容器,则情况略有不同。

在 Windows 主机上启动 Process Explorer,可以看到这个 Windows 容器的进程相对多一些:

这是因为在 Windows 系统中,需要给应用提供一些用户模式的系统服务,例如 DNS、DHCP、RPC 等服务,这样从容器的角度来看,容器获得了自己独有的服务 (一般是在各自的 svchost 或者其他服务宿主进程里运行),构成了所谓的 Container OS。

我们可以用 PowerShell 命令查看容器内部启动的 Windows 服务,大概有 27 个,参考附图。

很可惜,这个版本的 Windows docker 里,虽然有远程桌面服务,但是目前还不支持远程桌面到容器,所以无法使用容器应用的图形化界面。

容器里的应用,到底应该启动多少 Windows 服务?由于 Windows 服务的具体作用是非文档化的,所以不像 Linux 可以做到最精简。但是由于这些服务几乎不占用什么额外的资源,对于容器性能没有影响。

Windows 容器的进程如何隔离

由于在最新的测试版本里,容器对象的权限设置有了改变,只有 SYSTEM 权限才能查看。所以要查看 Windows 容器的进程隔离,需要用 SYSTEM 权限启动 Winobj。这可以借助 Psexec 来实现:

复制代码
Psexec -i -d -s winobj.exe

可以看到 Windows 对象空间里多了一个 Containers 的节点,其下有若干个 GUID 分支,这些 GUID 代表系统里的容器。其下每个容器有自己独立的 BaseNamedObjects 等命名空间,包括互斥信号量、内存 Section、事件等。

可以用 PowerShell 查看容器的 GUID,参考附图。

每个容器节点下,有自己的 Session 分支,例如该容器,占据了 Windows 系统的 Session 2。如附图所示。

这就是为什么,不管用任务管理器,还是 PowerShell,抑或是 Process Explorer 等工具,我们都在 Windows 主机里看到容器里的所有进程都会标记 Session 为 2。

借助 Process Explorer,我们可以看到容器里的进程,所打开的 Handle,其中就指向先前所看到的 Windows 容器对象命名空间。

同时还能看到,容器进程所在的 WindowStation 并不是 WinSta0,而是 Service-0x0-3e7$,3e7 的 10 进制等于 999,等于九五之尊,这是 SYSTEM 服务所在的窗口站。所以容器进程无法在 Windows 桌面上拥有图形化界面。

还可以查看一个有意义的对象,Windows 容器所挂载的主机目录,类似于 Linux 容器的 Volume。

Windows 容器的文件系统如何隔离

和 Linux 一样,Windows 容器映像采用分层的文件系统,基于映像创建容器后,相当于在只读的分层文件系统上再覆盖一层可读写的文件系统层。如果要修改的文件在最上层的可读写层里没有,则沿着分层的 Layer 找到目标文件后,将其用 COW(Copy on write:写时复制) 复制到可读写层再修改。

让我们进入到 Windows 主机的以下目录:

复制代码
C:\ProgramData\Microsoft\Windows\Hyper-V\Containers

该目录下列出所有通过 PowerShell 命令创建的容器文件。其下有文件夹和文件,都以容器的 GUID 来命名。

其中的 926A300B-ACB7-4B28-9D86-45BF82C1211C.vhdx 就是该容器的最上层的可读写层,是一个 VHDX 文件。

记住该可读写层并不是一个完整的文件系统,它需要和 Image 的现有文件系统组成 Union File System。如果尝试双击该 VHDX(只能尝试挂载停止状态的容器 VHDX),试图挂载到 Windows 系统,会弹出以下报错信息,提示该虚拟硬盘无法挂载。

Image 的文件系统位于以下路径 (Windows Server Core 的 Container OS 文件):

复制代码
C:\ProgramData\Microsoft\Windows\Images\CN=Microsoft_WindowsServerCore_10.0.10586.0\Files

如果用 Process Explorer 查看容器进程访问的 Dll,可以看到其访问的路径为 Container OS 文件。

如果是用 docker 命令创建的进程,道理类似,但是其可读写层文件系统位于以下路径:

复制代码
C:\ProgramData\docker\windowsfilter

Windows 容器还有注册表

和 Linux 不一样,Windows 容器还需要考虑注册表的隔离问题。和文件系统命名空间隔离一样,注册表命名空间隔离也采用类似 Union FS 形式。

下面让我们进入 PowerShell 命令创建的 Windows 容器文件夹内部。

复制代码
C:\ProgramData\Microsoft\Windows\Hyper-V\Containers\926A300B-ACB7-4B28-9D86-45BF82C1211C\Hives

在这个 Hives 文件夹下方,有很多命名为 *_Delta 的文件,这是容器所访问的注册表配置单元文件。

从命名方式中可以看到,容器的注册表和文件系统一样,也采用分层架构,最上层的是可读写的注册表命名空间。而 Image 映像也有只读部分的注册表空间,路径如下。

复制代码
C:\ProgramData\Microsoft\Windows\Images\CN=Microsoft_WindowsServerCore_10.0.10586.0\Hives

在 Process Explorer 里可以看到可读写层、只读层注册表合并后所加载的内容。

Docker 命令所创建的容器,方法类似,位于类似以下路径:

Windows 容器的资源限制

大家知道,Docker 可以调用 CGroup 技术来限制 Linux 容器的 CPU、内存等资源占用。而在 Windows 容器里,内存资源的限制,则是通过 Windows 的 JO(作业对象) 技术来实现。

可以参考以下技术来限定 Windows 容器的 CPU、内存和磁盘 IO。例如可以将容器的内存限定为最大占用为 5GB。

复制代码
https://msdn.microsoft.com/en-us/virtualization/windowscontainers/management/manage_resources?f=255&MSPPError=-2147217396

然后用 Process Explorer 打开任意一个容器进程的属性对话框,切换到 Job 标签页。

可以看到所有容器进程共享一个作业对象,而且该作业对象的内存限额 (Job Memory Limit) 为 5GB。

作者简介

彭爱华 网名盆盆,微软混合云技术顾问。11 届微软最有价值专家 (MVP),微软高级认证讲师。出版过近 20 本技术图书,2005 年创建 ITECN 博客 (09 年全国 IT 博客五十强)。微信公众号 (华来四笑侃 Windows):sysinternal。曾获得微软技术大会 TechEd 最佳讲师、中国首届 IT 管理技术大会 (2009) 最佳讲师的光荣称号。


感谢陈兴璐对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-08-29 18:096687

评论

发布
暂无评论
发现更多内容

鲲鹏编译调试及原生开发工具基础知识

乌龟哥哥

8月月更

OAuth Client默认配置加载

阿提说说

Spring Security OAuth

Linux配置SSH免密码登录(非root账号)

程序员欣宸

SSH 8月月更

STM32入门开发 LWIP网络协议栈移植(网卡采用DM9000)

DS小龙哥

8月月更

数字钱包红海角逐,小程序生态快速引入可助力占领智慧设备入口

FinClip

开源一夏 | 参与开源能让人更幸福

石云升

开源 开源社区 8月月更

学习Apache ShardingSphere解析器源码(一)

我不吃六安茶

ANTLR Apache ShardingSphere

Open Office XML 格式中的 Style 设计原理

Jerry Wang

xml 微软 Office 8月月更 openOffice

ABAP应用服务器的HTTP响应状态码(Status Code)

Jerry Wang

前端开发 HTTP web开发 SAP 8月月更

Spring 全家桶之 Spring Data JPA(一)

小白

8月月更

C++运算符重载(三)之递增运算符重载

CtrlX

c c++ 代码 进阶员进阶 8月月更

React Redux 组件更新/渲染原理 connect 中的 mapStateToProps

HullQin

CSS JavaScript html 前端 8月月更

软件定制开发——企业定制开发app软件的优势

开源直播系统源码

软件开发 直播系统源码 app定制开发 软件定制开发

开源一夏 | 粗暴项目监控,快速上手Spring家族的亲儿子SpringAdmin监控项目

知识浅谈

spring 开源 8月月更

5 张弹珠图彻底弄清 RxJS 的拉平策略:mergeMap、switchMap、concatMap、exhaustMap

掘金安东尼

前端 RXJS 8月月更

vue高频面试题合集(一)附答案

helloworld1024fd

Vue

vue高频面试题合集(二)附答案

helloworld1024fd

Vue

云原生(十三) | Kubernetes篇之深入Kubernetes(k8s)概念

Lansonli

云原生 k8s 8月月更

全新FIDE 编译简单评测

Geek_99967b

小程序

在座的Python爬虫工程师,你敢爬律师事务所站点吗?

梦想橡皮擦

Python 爬虫 8月月更

Kubernetes 维护技术分享

CTO技术共享

开源 签约计划第三季 8月月更

【精通内核】计算机程序的本质、内存组成与ELF格式

小明Java问道之路

编译原理 ELF 链接 签约计划第三季 8月月更

【LeetCode】重新格式化字符串Java题解

Albert

LeetCode 8月月更

开源一夏|OpenHarmony如何选择图片在Image组件上显示(eTS)

坚果

开源 OpenHarmony 8月月更

开源一夏|5分钟快速为OpenHarmony提交PR(Web)

坚果

开源 OpenHarmony 8月月更

Android进阶(一)Android 发邮件与几种网络请求方式详解

No Silver Bullet

android 8月月更 邮件发送

前端面试 | 必知必会的10道Promise题!

千锋IT教育

SAP 产品增强技术回顾

Jerry Wang

SaaS SAP 企业级应用 云应用 8月月更

浅谈Java和SAP ABAP的静态代理和动态代理,以及ABAP面向切面编程的尝试

Jerry Wang

编程语言 oop aop spring aop 8月月更

数据库 SQL 优化大总结之:百万级数据库优化方案

TimeFriends

8月月更

突破次元壁垒,让身边的玩偶手办在屏幕上动起来!

HMS Core

细说Windows与Docker之间的趣事_Windows_彭爱华_InfoQ精选文章