Docker的底层原理实现(二十)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker的底层原理实现(二十)相关的知识,希望对你有一定的参考价值。
参考技术A Docker 采用了 C/S 架构,包括客户端和服务端。Docker 守护进程 ( Daemon )作为服务端接受来自客户端的请求,并处理这些请求(创建、运行、分发容器)。客户端和服务端既可以运行在一个机器上,也可通过 socket 或者 RESTful API 来进行通信。
命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的 应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。
不同用户的进程就是通过 pid 命名空间隔离开的,且不同命名空间中可以有相同 pid。所有的 LXC 进程在 Docker 中的父进程为Docker进程,每个 LXC 进程具有不同的命名空间。同时由 于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。
有了 pid 命名空间, 每个命名空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端 口。网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的 网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。Docker 默认采用 veth 的方式,将 容器中的虚拟网卡同 host 上的一个 Docker 网桥 docker0 连接在一起。
容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。
类似 chroot,将一个进程放到一个特定的目录执行。mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的 mount point。
UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非主机上的一个进程。
每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。
控制组(cgroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离、限制、审计 等。只有能控制分配到容器的资源,才能避免当多个容器同时运行时的对系统资源的竞争。
联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父 镜像),可以制作各种具体的应用镜像。
另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层, 大大提高了存储的效率。
Docker 中使用的 AUFS(AnotherUnionFS)就是一种联合文件系统。 AUFS 支持为每一个 成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteoutable)权限, 同时 AUFS 里有一个类似分层的概念, 对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分的)。
Docker 目前支持的联合文件系统包括 OverlayFS, AUFS, Btrfs, VFS, ZFS 和 Device Mapper。
最初,Docker 采用了 LXC 中的容器格式。从 0.7 版本以后开始去除 LXC,转而使用自行开 发的 libcontainer ,从 1.11 开始,则进一步演进为使用 runC 和 containerd 。
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势之一是转发效率较高。 Linux 通 过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包 被直接复制到接收接口的接收缓存中。对于本地系统和容器内系统看来就像是一个正常的以 太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。
Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它 们彼此连通(这样的一对接口叫做 veth pair )。
Docker 创建一个容器的时候,会执行如下操作:
完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。可以在 docker run 的时候通过 --net 参数来指定容器的网络配置:
Docker圣经:大白话说Docker底层原理,6W字实现Docker自由
说在前面:
现在拿到offer超级难,甚至连面试电话,一个都搞不到。
尼恩的技术社群(50+)中,很多小伙伴凭借 “左手云原生+右手大数据”的绝活,拿到了offer,并且是非常优质的offer,据说年终奖都足足18个月。
而云原生的核心组件是 Docker + K8S,但是 Docker 又很难。在这里,尼恩从架构师视角出发,Docker + K8S 核心原理做一个宏观的介绍。
由于内容确实太多, 所以写两个pdf 电子书,并且后续会持续升级:
(1) 《 Docker 学习圣经 》PDF
(2) 《 K8S 学习圣经 》PDF
带大家穿透Docker + K8S ,实现Docker + K8S 自由,让大家不迷路。
本书 《 Docker 学习圣经 》PDF的 V1版本,后面会持续迭代和升级。供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从这里获取:码云
《 Docker 学习圣经 》PDF 封面
Docker基础
作为大神或者准架构师/架构师,一定要了解一下docker的底层原理。
首先还是简单, 说明一下Docker 巨大的价值
Docker 巨大的价值
Docker 是一个开源的应用容器引擎,基于 Go 语言开发。
Docker 遵从 Apache2.0 协议开源。
Docker 的本质:
先来说说Docker 的本质
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,实现轻量级虚拟化。
docker为什么有这么巨大的价值呢?
因为,在容器技术出来之前,大家都是使用虚拟机技术,比如在 window中装一个VMware,通过这个软件我们可以虚拟出来一台或者多台电脑,实现硬件资源的细粒度分割和使用隔离。
但是 ,虚拟机技术太笨重啦!模式太重。
Docker容器技术,也是一种虚拟化技术,也是实现硬件资源的细粒度分割和使用隔离。但是,Docker是一种轻量级的虚拟机技术。
Docker容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),
对于开发人员来说,用 CE(Community Edition) 社区版就可以了
Docker的广泛应用场景:
- Web 应用的自动化打包和发布。
- 自动化测试和持续集成、发布。
- 在服务型环境中部署和调整数据库或其他的后台应用。
- 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。
一般来说,测试环境、生产环境,基本都已经全面docker化了。
可见,docker技术,是那么那么的重要。
Docker的在DevOps(开发、运维)场景的应用
一般来说,怎么的微服务应用,有多套环境:
(1)开发
(2)测试
(3)预生产
(4)生产
四套环境,导致环境配置是十分的麻烦,每一个环境都要部署各种组件(如Redis、ES、zk) ,非常的费时费力。
更要命的是,在生产环境上, 吞吐量一上来,需要动态扩容。
使用docker,咱们可以将DevOps(开发、运维)的工作,高速完成:
(1)快速完成 发布工作
开发环境一般是 Windows/mac,最后发布到Linux。
没有docker之前,使用jar包发布,配上大量的shell脚本,然后各种配置,及其复杂。
有了docker之后,做好镜像,开发打包部署上线,一套流程做完!
(2)快速完成 交付工作
传统的交付工作,要给用户提供各种安装的帮助文档,安装程序,基础环境安装,依赖的中间件安装,等等等等。
有了docker之后,能更快速的交付和部署。 给他一套镜像,通过Docker命令,一键运行,啥都是好的。
那你看,docker 是不是,真香。
(3)更便捷的升级和扩缩容
使用了 Docker之后,我们项目打包为一个镜像,部署应用就和搭积木一样
扩展服务器A,启动一个容器就ok。
扩展服务器B,启动一个容器就ok。
如果要动态扩展,使用K8S这类分布式容器管理基础设施,配上一个HPA 控制器组件,就能自动的完成动态扩容,动态缩容。
那你看,docker 是不是,真香。
(4)服务器的性能可以被压榨到极致
Docker是内核级别的虚拟化,可以在一个物理机上可以运行很多的容器实例。
服务器的性能可以被压榨到极致
那你看,docker 是不是,真香。
接下来,随着40岁老架构师一起,来穿透docker的原理和实操吧。
只有先穿透docker,才能穿透K8S,最终穿透云原生+大数据,实现你的技术自由。
Docker的历史
2010年,几个的年轻人在美国的旧金山成立了一家公司 dotcloud。dotcloud 是一个Paas平台的创业公司,从事LXC(Linux Container容器)有关的容器技术。
Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。他们将自己的技术(容器化技术)命名就是 Docker。
Docker刚刚延生的时候,没有引起行业的注意!
虽然获得了创业孵化器(Y Combinator)的支持、也获得过一些融资,但随着IT巨头们(微软、谷歌、亚马逊等厂商)也进入PaaS凭他,dotCloud举步维艰,眼看就活不下去!
2013年,dotCloud的创始人,28岁的Solomon Hykes做了一个艰难的决定,将dotCloud的核心引擎开源,这项核心引擎技术能够将Linux容器中的应用程序、代码打包,轻松的在服务器之间进行迁移。
2013发布了 Docker-compose 组件提供容器的编排工具。
2014年 Docker 发布1.0版本,2015年Docker 提供 Docker-machine,支持 windows 平台。
docker火了。
这个基于LXC技术的核心管理引擎开源后,让全世界的技术人员感到惊艳。
大家感叹这一切太方便了!!于是,越来越多的人发现docker的优点,使用他!
虽然,Docker 项目在开源社区大受追捧,同时也被业界诟病的是: Docker 公司对于 Docker 发展具有绝对的话语权,比如 Docker 公司推行了 libcontainer 难以被社区接受。
为了防止 Docker 这项开源技术被Docker 公司控制,几个核心贡献代码的厂商诸如 Redhat,谷歌的倡导下,成立了 OCI 开源社区,制定了 OCI 开放容器标准,Open Container Initiative(OCI,开放容器标准)。
OCI 开源社区旨在于将 Docker 的发展权利回归社区,当然反过来讲,Docker 公司也希望更多的厂商安心贡献代码到Docker 项目,促进 Docker 项目的发展。
Docker 将自己容器格式和运行时 runC 捐给了 OCI,OCI 在此基础上制定了 2 个标准:
运行时标准 Runtime Specification (runtime-spec)
镜像标准 Image Specification (image-spec) :
于是通过OCI建立了 runc 项目,替代 libcontainer,这为开发者提供了除 Docker 之外的容器化实现的选择。
OCI 社区提供了 runc 的维护,而 runc 是基于 OCI 规范的运行容器的工具。
换句话说,你可以通过 runc,提供自己的容器实现,而不需要依赖 Docker。
当然,Docker 的发行版底层也是用的 runc。在 Docker 宿主机上执行 runc,你会发现它的大多数命令和 Docker 命令类似,感兴趣的读者可以自己实践如何用 runc 启动容器。
至2017年,Docker 项目转移到 Moby 项目,基于 Moby 项目,Docker 提供了两种发行版,Docker CE 和 Docker EE, Docker CE 就是目前大家普遍使用的版本,Docker EE 成为付费版本,提供了容器的编排,Service 等概念。
Docker 公司承诺 Docker 的发行版会基于 Moby 项目。这样一来,通过 Moby 项目,你也可以自己打造一个定制化的容器引擎,而不会被 Docker 公司绑定。
Docker 的入门知识
从大家常用的Docker Engine开始说起。
Docker Engine
当人们说“Docker”时,他们通常是指 Docker Engine,它是一个客户端 - 服务器应用程序,
Docker 引擎由如下主要的组件构成:Docker 客户端(Docker Client)、Docker 守护进程(Docker daemon)、containerd 以及 runc。
Docker Engine 从 CLI 中接受docker 命令,完成容器的管理:
例如使用 docker run 、docker ps 来列出正在运行的容器、
例如使用docker images 来列出镜像,
等等。
Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上,
Client通过Socket连接从客户端访问Docker守护进程。Docker守护进程从客户端接受命令,并按照命令,管理运行在主机上的容器。
- 后台进行(dockerd)
- REST API Server
- CLI接口(docker)
Docker Platform
- Docker提供了一个开发,打包,运行app的平台
- 把app和底层infrastructure隔离开来
其三层模型如图:
到底什么是docker:
到底什么是docker:
- docker是一个软件,可以运行在window、linux、mac等各种操作系统上。
- docker 是一个开源的应用容器引擎,基于Go 语言开发并遵从 Apache2.0 协议开源,项目代码托管在github上进行维护
- docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。
- 容器是完全使用沙箱机制,相互之间不会有任何接口,更重要的是容器性能开销极低。
什么是容器?
什么是容器?
- 对软件和其依赖的标准化打包
- 应用之间相互隔离
- 共享同一个OS Kernel
- 可以运行在很多主流操作系统上
注:容器和虚拟机的区别在于容器是APP层面的隔离,而虚拟化是物理资源层面的隔离
容器解决了什么问题?
- 解决了开发和运维之间的矛盾
- 在开发和运维之间搭建了一个桥梁,是实现devops最佳解决方案
一个docker 容器,是一个运行时环境,可以简单理解为进程运行的集装箱。
docker基本组成
docker主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统之上);
docker仓库(Registry):用来保存各种打包好的软件镜像;仓库分为公有仓库和私有仓库。(很类似 maven)
docker镜像(Images):软件打包好的镜像;放在docker仓库中;
docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用
Docker 包括三个基本概念:
- 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
- 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
- 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
Docker 容器通过 Docker 镜像来创建。
概念 | 说明 |
---|---|
Docker 镜像(Images) | Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统。 |
Docker 容器(Container) | 容器是独立运行的一个或一组应用,是镜像运行时的实体。 |
Docker 客户端(Client) | Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。 |
Docker 主机(Host) | 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。 |
Docker Registry | Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。 |
Docker 与虚拟机有何区别
Docker 的误解:Docker 是轻量级的虚拟机。
很多人将docker理解为, Docker 实现了类似于虚拟化的技术,能够让应用跑在一些轻量级的容器里。这么理解其实是错误的。
docker和kvm都是虚拟化技术,它们的主要差别:
1、Docker有着比虚拟机更少的抽象层
2、docker利用的是宿主机的内核,VM需要的是Guest OS
二者的不同:
- VM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;
- Container(Docker容器),在宿主机器、宿主机器操作系统上创建Docker引擎,在引擎的基础上再安装应用。
所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统,避免引导。docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级!
虚拟机是加载Guest OS ,这是分钟级别的
与传统VM特性对比:
作为一种轻量级的虚拟化方式,Docker在运行应用上跟传统的虚拟机方式相比具有显著优势:
- Docker 容器很快,启动和停止可以在秒级实现,这相比传统的虚拟机方式要快得多。
- Docker 容器对系统资源需求很少,一台主机上可以同时运行数千个Docker容器。
- Docker 通过类似Git的操作来方便用户获取、分发和更新应用镜像,指令简明,学习成本较低。
- Docker 通过Dockerfile配置文件来支持灵活的自动化创建和部署机制,提高工作效率。
- Docker 容器除了运行其中的应用之外,基本不消耗额外的系统资源,保证应用性能的同时,尽量减小系统开销。
- Docker 利用Linux系统上的多种防护机制实现了严格可靠的隔离。从1.3版本开始,Docker引入了安全选项和镜像签名机制,极大地提高了使用Docker的安全性。
特性 | 容器 | 虚拟机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
硬盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于原生 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
docker与操作系统比较
docker是一种轻量级的虚拟化方式。与传统操作系统技术的特性比较如下表:
特 性 | 容 器 | 虚 拟 机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
性能 | 接近原生 | 较弱 |
内存代价 | 很小 | 较多 |
硬盘使用 | 一般为MB | 一般为GB |
运行密度 | 单机支持上千个容器 | 一般几十个 |
隔离性 | 安全隔离 | 完全隔离 |
迁移性 | 优秀 | 一般 |
传统的虚拟机方式提供的是相对封闭的隔离。
Docker利用Linux系统上的多种防护技术实现了严格的隔离可靠性,并且可以整合众多安全工具。
从 1.3.0版本开始,docker重点改善了容器的安全控制和镜像的安全机制, 极大提高了使用docker的安全性。
Docker 的安装
安装docker前置条件
当我们安装 Docker 的时候,会涉及两个主要组件:
- Docker CLI:客户端
- Docker daemon:有时也被称为“服务端”或者“引擎”
环境准备
硬件总体要求,可以参考尼恩的本地硬件情况:
1、硬件要求。
本文硬件总体要求如下表:
序号 | 硬件 | 要求 |
---|---|---|
1 | CPU | 至少2核 |
2 | 内存 | 至少8G |
3 | 硬盘 | 至少100G磁盘空间 |
2、本地虚拟机环境
软件 | 版本 |
---|---|
Win | win10以上 |
virtual box | 6以上 |
vagrant | 2以上 |
docker+K8S学习环境非常复杂,尼恩搞这个 前前后后起码折腾了一周,
其中,很多头疼的工作,包括linux内核升级、磁盘扩容等等, 苦不堪言。
现在把这个环境,以虚拟机box镜像的方式,导出来直接给大家,
大家一键导入后,直接享受docker 的实操,享受K8S的实操,可以说,爽到不要不要的。
以上软件和 尼恩个人的虚拟机box镜像,可以找尼恩获取。
docker安装的三种方式
安装docker的三种方式
(1)离线安装
(2)在线安装
(3)用现成的
方式一 :离线安装docker
这里以 19.03.9 版本进行介绍, 其他版本是一样的。
这种安装方式,可以用于没有 互联网的 场景。
比如,很多公司,并不能直接上外网。
一、基础环境
1、操作系统:CentOS 7.3
2、Docker版本:19.03.9 官方下载地址
3、官方参考文档:https://docs.docker.com/install/linux/docker-ce/binaries/#install-static-binaries
二、Docker安装
1、下载
wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.9.tgz
注意:如果事先下载好了可以忽略这一步
2、解压
把压缩文件存在指定目录下(如root),并进行解压
tar -zxvf docker-19.03.9.tgz
cd root
[root@localhost ~]# tar -zxvf docker-19.03.6.tgz
docker/
docker/containerd
docker/docker
docker/ctr
docker/dockerd
docker/runc
docker/docker-proxy
docker/docker-init
docker/containerd-shim
3、将解压出来的docker文件内容移动到 /usr/bin/ 目录下
cp docker/* /usr/bin/
4、将docker注册为service
cat /etc/systemd/system/docker.service
vi /etc/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
[Install]
WantedBy=multi-user.target
5、启动
chmod +x /etc/systemd/system/docker.service #添加文件权限并启动docker
systemctl daemon-reload #重载unit配置文件
systemctl start docker #启动Docker
systemctl enable docker.service #设置开机自启
[root@localhost ~]# vi /etc/systemd/system/docker.service
[root@localhost ~]# chmod +x /etc/systemd/system/docker.service
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start docker
[root@localhost ~]# systemctl enable docker.service
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /etc/systemd/system/docker.service.
6、验证
systemctl status docker #查看Docker状态
docker -v #查看Docker版本
docker info
[root@localhost ~]# systemctl status docker
docker.service - Docker Application Container Engine
Loaded: loaded (/etc/systemd/system/docker.service; enabled; vendor preset: disabled)
Active: active (running) since Sat 2021-10-09 15:25:44 CST; 29s ago
Docs: https://docs.docker.com
Main PID: 1916 (dockerd)
CGroup: /system.slice/docker.service
├─1916 /usr/bin/dockerd
└─1927 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.671407996+08:00" level=info msg="scheme \\"unix\\" not r...e=grpc
Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.671440368+08:00" level=info msg="ccResolverWrapper: se...e=grpc
Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.671462935+08:00" level=info msg="ClientConn switching ...e=grpc
Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.750687781+08:00" level=info msg="Loading containers: start."
Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.072960862+08:00" level=info msg="Default bridge (docke...dress"
Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.153444071+08:00" level=info msg="Loading containers: done."
Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.175249299+08:00" level=info msg="Docker daemon" commit...9.03.6
Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.175337834+08:00" level=info msg="Daemon has completed ...ation"
Oct 09 15:25:44 localhost.localdomain systemd[1]: Started Docker Application Container Engine.
Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.195084106+08:00" level=info msg="API listen on /var/ru....sock"
Hint: Some lines were ellipsized, use -l to show in full.
[root@localhost ~]# docker -v
Docker version 19.03.6, build 369ce74a3c
[root@localhost ~]# docker info
方式二 :在线安装docker
如果可以连接公网,建议在线安装。
这里注意 linux和 docker的版本。
尼恩安装 docker 最新版本的时候,发现依赖了 Centos 8 以上的版本。
在线安装docker步骤
- 更新yum
yum update
- 安装工具包
yum -y install yum-utils
- 设置yum源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
以腾讯源为例
https://mirrors.cloud.tencent.com/docker-ce/linux/centos/docker-ce.repo
- 安装Docker-Ce(社区版)
yum install docker-ce
- 查看docker版本(用来确认是否安装成功)
# 输入 docker-v 后如果出现下面的内容则代表安装成功
[root@VM-24-9-centos ~]# docker -v
Docker version 20.10.23, build 7155243
- Docker镜像加速(国内使用)
# 需要确定/etc下面是否有docker这个文件夹,若没有则需要使用下面的命令进行创建
mkdir -p /etc/docker
# 创建配置文件daemon.json
vi /etc/docker/daemon.json
# 写入以下内容
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com" # 可以替换为其他厂商的地址
]
# 重载一下配置
systemctl daemon-reload
- 启动Docker服务
systemctl start docker
- 验证docker是否可以使用
[root@VM-24-9-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
方式三 :用现成的 (大大的省事)
docker+K8S 是一整套技术体系。
但是,docker+K8S学习环境非常复杂,尼恩搞这个 前前后后起码折腾了一周。
其中,很多头疼的工作,包括linux内核升级、磁盘扩容等等, 苦不堪言。
现在把这个环境,以虚拟机box镜像的方式,导出来直接给大家,
大家一键导入后,直接享受docker 的实操,享受K8S的实操,
可以说,爽到不要不要的。
以上环境和 尼恩个人的虚拟机box镜像,可以找尼恩获取。
Docker Container概述
什么是Container 容器
- 通过Image创建(copy)
- 在Image layer之上建立一个container layer(可读写)
- 类比面向对象:类和实例
- Image负责app的存储和分发,Container负责运行app
容器与镜像的关系
- 镜像:镜像是只读文件,提供运行程序完整的软硬件资源。
- 容器:容器是镜像的实例,由docker负责创建,容器之间彼此隔离。
Docker本地容器相关的操作
Container相关命令
- 创建容器 docker run centos
- 创建容器 docker run -it centos
- 查看活跃容器 docker ps
- 查询容器状态 docker container ls -a
- 移除容器 docker container rm + [container ID]
- 移除镜像 docker image rm + [image ID]
- 显示所有containerID docker container ls -aq
- 移除所有的container docker rm $(docker container ls -aq)
创建容器
创建名为"centos6"的容器,并在容器内部和宿主机中查看容器中的进程信息
docker run -itd -p 6080:80 -p 6022:22 docker.io/lemonbar/centos6-ssh:latest
结果如下
[root@VM-4-17-centos ~]# docker run -itd -p 80:80 -p 6022:22 docker.io/lemonbar/centos6-ssh:latest
Unable to find image 'lemonbar/centos6-ssh:latest' locally
latest: Pulling from lemonbar/centos6-ssh
a3ed95caeb02: Pull complete
f79eb1f22352: Pull complete
67c1aaa530c8: Pull complete
80447774eee7: Pull complete
6d67b3a80e5a: Pull complete
f1819e4b2f8f: Pull complete
09712b5b9acc: Pull complete
8bc987c5494f: Pull complete
c42b021d0ff2: Pull complete
Digest: sha256:093c2165b3c6fe05d5658343456f9b59bb7ecc690a7d3a112641c86083227dd1
Status: Downloaded newer image for lemonbar/centos6-ssh:latest
a4f1c9b8abcda78c8764cc285183dfa56cd1aa4ce6d111d4d9e77f3a57f3d5fc
查看活跃容器 docker ps
docker ps
查看全部容器
查询容器状态
docker ps -a
docker container ls -a
两个命令,效果差不多
停止容器
docker stop id
删除容器
docker rm id
查看容器的进程信息
docker top
:查看容器中运行的进程信息,支持 ps 命令参数。
语法
docker top [OPTIONS] CONTAINER [ps OPTIONS]
容器运行时不一定有/bin/bash终端来交互执行top命令,而且容器还不一定有top命令,
可以使用docker top来实现查看container中正在运行的进程。
docker top 容器名称
尼恩的虚拟机中,存在容器zookeeper/ mysql,如果想查看zookeeper/ mysql 容器内的运行进程信息,
可以使用下述命令:
docker top zookeeper
docker top mysql
如何查找容器名称?
很多的命令,用到容器名称
如何查找容器名称,可以使用下面的命令
[root@localhost ~]# docker ps --format ".Names"
结果如下:
docker最为常用的几个命令
docker的守护进程查看
systemctl status docker
docker 镜像查看
docker image ls
docker 容器查看
docker ps
Docker Registry配置和查看
cat /etc/docker/daemon.json
配置私有仓库
cat>/etc/docker/daemon.json<<EOF
"registry-mirrors":["http://10.24.2.30:5000","https://tnxkcso1.mirrors.aliyuncs.com"],
"insecure-registries":["10.24.2.30:5000"]
EOF
Docker容器进入的4种方式
在使用Docker创建了容器之后,大家比较关心的就是如何进入该容器了,
其实进入Docker容器有好几多种方式,这里我们就讲一下常用的几种进入Docker容器的方法。
进入Docker容器比较常见的几种做法如下:
- 使用docker attach
- 使用SSH
- 使用nsenter
- 使用exec
方式1:使用docker attach进入Docker容器
Docker提供了attach命令来进入Docker容器。
接下来我们创建一个守护态的Docker容器,然后使用docker attach命令进入该容器。
sudo docker run -itd ubuntu:14.04 /bin/bash
然后我们使用docker ps查看到该容器信息,接下来就使用docker attach进入该容器
docker attach c1437f4bd302
可以看到我们已经进入到该容器中了。
但在,使用该命令有一个问题:
当多个窗口同时使用该命令进入该容器时,所有的窗口都会同步显示。
如果有一个窗口阻塞了,那么其他窗口也无法再进行操作。
因为这个原因,所以docker attach命令不太适合于生产环境,平时自己开发应用时可以使用该命令。
方式2:使用SSH进入Docker容器
在生产环境中排除了使用docker attach命令进入容器之后,相信大家第一个想到的就是ssh。
在镜像(或容器)中安装SSH Server,这样就能保证多人进入。
容器且相互之间不受干扰了,相信大家在当前的生产环境中(没有使用Docker的情况)也是这样做的。
但是使用了Docker容器之后不建议使用ssh进入到Docker容器内。
方式3:使用nsenter进入Docker容器
在上面两种方式都不适合的情况下,还有一种比较方便的方法,即使用nsenter进入Docker容器。
1、什么是nsenter?
nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。
util-linux 是一个开放源码的软件包,是一个对任何 Linux 系统的基本工具套件。含有一些标准 Unix 工具,如 login。
util-linux 软件包包含许多工具。其中比较重要的是加载、卸载、格式化、分区和管理硬盘驱动器,打开 tty 端口和得到内核消息。
nsenter用途 ?
一个最典型的用途就是进入容器的网络命令空间。相当多的容器为了轻量级,是不包含较为基础的命令的,比如说ip address,ping,telnet,ss,tcpdump等等命令,这就给调试容器网络带来相当大的困扰:只能通过docker inspect ContainerID命令获取到容器IP,以及无法测试和其他网络的连通性。这时就可以使用nsenter命令仅进入该容器的网络命名空间,使用宿主机的命令调试容器网络。
在了解了什么是nsenter之后,系统默认将我们需要的nsenter安装到主机中, nsenter --help
查看帮助
nsenter --help
查看帮助
$ nsenter --help
nsenter [options] [program [arguments]]
options:
-t, --target pid:指定被进入命名空间的目标进程的pid
-m, --mount[=file]:进入mount命令空间。如果指定了file,则进入file的命令空间
-u, --uts[=file]:进入uts命令空间。如果指定了file,则进入file的命令空间
-i, --ipc[=file]:进入ipc命令空间。如果指定了file,则进入file的命令空间
-n, --net[=file]:进入net命令空间。如果指定了file,则进入file的命令空间
-p, --pid[=file]:进入pid命令空间。如果指定了file,则进入file的命令空间
-U, --user[=file]:进入user命令空间。如果指定了file,则进入file的命令空间
-G, --setgid gid:设置运行程序的gid
-S, --setuid uid:设置运行程序的uid
-r, --root[=directory]:设置根目录
-w, --wd[=directory]:设置工作目录
如果没有给出program,则默认执行$SHELL。
2、nsenter安装
如果没有安装的话,按下面步骤安装即可(注意是主机而非容器或镜像)
具体的安装命令如下:
wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz
tar -xzvf util-linux-2.24.tar.gz
cd util-linux-2.24/
./configure --without-ncurses
make nsenter
sudo cp nsenter /usr/local/bin
3、nsenter的 使用
nsenter可以访问另一个进程的名称空间。
所以为了连接到某个容器我们还需要获取该容器的第一个进程的PID。
怎么办呢 ? 可以使用docker inspect
命令来拿到该 进程的 PID。
docker inspect
命令使用如下:
sudo docker inspect --help
inspect命令可以分层级显示一个镜像或容器的信息。
比如我们当前有一个正在运行的容器
可以使用docker inspect
来查看该容器的详细信息。
sudo docker inspect c1437f4bd302
由其该信息非常多,此处只截取了其中一部分进行展示。如果要显示该容器第一个进行的PID可以使用如下方式
sudo docker inspect -f .State.Pid c1437f4bd302
在拿到该进程PID之后我们就可以使用nsenter命令访问该容器了。
sudo nsenter --target 22299 --mount --uts --ipc --net --pid
其中的 22299 即刚才拿到的进程的PID
输入该命令便进入到容器中
$ nsenter --target 上面查到的进程id --mount --uts --ipc --net --pid
解释nsenter指令中进程id之后的参数的含义:
–mount参数是进去到mount namespace中 (文件系统)
–uts参数是进入到uts namespace中 (主机名与域名)
–ipc参数是进入到System V IPC namaspace中 (信号量、消息队列和共享内容)
–net参数是进入到network namespace中 (网络设备、网络栈、端口)
–pid参数是进入到pid namespace中 (进程编号)
–user参数是进入到user namespace中 (用户和用户组)
看看下面的例子,进入到容器的 network namespace中 ,看看IP地址,是不是变了。
docker隔离应用应用涉及到的六大名称空间
1、pid 命名空间(进程ID)
不同用户的进程就是通过 pid 命名空间隔离开的,且不同命名空间中可以有相同 pid。
所有的 LXC (Linux 容器)进程在 Docker 中的父进程为 Docker 进程,每个 LXC 进程具有不同的命名空间。
同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。
2、net 命名空间(网络)
有了 pid 命名空间,每个命名空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。
网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的 网络设备,IP 地址,路由表,/proc/net 目录。这样每个容器的网络就能隔离开来。
Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。
3、ipc 命名空间(进程间通信)
容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。
然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。
4、mnt 命名空间(挂载文件系统)
类似 chroot,将一个进程放到一个特定的目录执行。
mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间 中的进程所看到的文件目录就被隔离开了。
同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的 mount point。
5、UTS 命名空间(主机名/域名)
UTS(“UNIX Time-sharing System”) 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。
6、user 命名空间(用户)
每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。
nsenter查看docker的连接
由于使用DOCKER的时候,ESTABLISHED连接不会出现在netstat中,在运行中的docker容器中列出打开的套接字的方法
查看连接:
sudo docker inspect -f .State.Pid e9eaef999da9
$ sudo nsenter -t 3473 -n netstat | grep ESTABLISHED
方式4:使用docker exec进入Docker容器
除了上面几种做法之外,docker在1.3.X版本之后还提供了一个新的命令exec用于进入容器,这种方式相对更简单一些,下面我们来看一下该命令的使用:
sudo docker exec --help
接下来我们使用该命令进入一个已经在运行的容器
sudo docker ps
sudo docker exec -it 容器id /bin/bash
在容器内部和宿主机中查看容器中的进程信息
进入一个名称为 rmqbroker-ha-b 的容器,查看进程信息
docker exec -it rmqbroker-ha-b /bin/bash ps -ef
结果如下:
我们可以使用docker exec
命令进入容器PID名空间,并执行应用。
通过ps -ef
命令,可以看到每个 rmqbroker-ha-b 容器都包含一个PID为1的进程,
容器的启动进程是 “sh mqbroker -c /opt/rocketmq…”,具有特殊意义。
利用docker top
命令,可以让我们从宿主机操作系统中看到容器的进程信息。
“sh mqbroker -c /opt/rocketmq…” 这个命令的进程,在 容器里边是1 ,在容器外部 3473
查看其父进程信息
ps -ef | grep 3401
root 3401 1 0 12:06 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id e9eaef999da9183b9be0b3239881bc6b9c2070f13057c322dfed3d072820e962 -address /run/containerd/containerd.sock
3000 3473 3401 0 12:06 ? 00:00:00 sh mqbroker -c /opt/rocketmq-4.6.0/conf/broker.conf autoCreateTopicEnable=true &
root 26491 24084 0 16:16 pts/1 00:00:00 grep --color=auto 3401
我们使用docker run 启用一个容器时,docker 会给每个容器都启动一个containerd-shim-runc-v2 父进程,这个进程又启动了一个ttrpc server(类似grpc/httpserver), containerd 通过 ttrpc和containerd-shim-runc-v2 通信来管理容器
从父亲进程可以看到:容器的本质是进程。
containerd-shim-runc-v2 后面的参数:namespace用来做命名空间隔离,cgroup用来做资源限制。
containerd-shim-runc-v2 进程很特殊,它们跑在一些特定的namespace和cgroup下。
站在这些进程的角度看,它们会以为自己跑在一个独立的机器上,看不到其他进程,也看不到其他文件。
这是其实是操作系统为它虚拟出来的一个独立的、隔离的环境,是假的。
查看子进程信息
[root@VM-4-17-centos ~]# ps aux | grep 27880
总计三个命令
docker top rmqbroker-ha-b #查看容器进程
ps -ef | grep 3401 #查看父进程以上是关于Docker的底层原理实现(二十)的主要内容,如果未能解决你的问题,请参考以下文章
Docker圣经:大白话说Docker底层原理,6W字实现Docker自由