Docker圣经:大白话说Docker底层原理,6W字实现Docker自由
云原生的核心组件是 Docker + K8S,但是 Docker又很难。在这里,尼恩从架构师视角出发,Docker+ K8S 核心原理做一个宏观的介绍。
说在前面:
现在 拿到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 学习圣经 》PDF 封面
- 本文目录
- Docker基础
- Docker 巨大的价值
- Docker 的入门知识
- Docker 的安装
- Docker Container概述
- Docker本地容器相关的操作
- docker最为常用的几个命令
- Docker容器进入的4种方式
-
- 方式1:使用docker attach进入Docker容器
- 方式2:使用SSH进入Docker容器
- 方式3:使用nsenter进入Docker容器
-
- 1、什么是nsenter?
- 2、nsenter安装
- 3、nsenter的 使用
- docker隔离应用应用涉及到的六大名称空间
- nsenter查看docker的连接
- 方式4:使用docker exec进入Docker容器
- 在容器内部和宿主机中查看容器中的进程信息
- Docker本地镜像载入与载出
- Harbor私有镜像仓库
- Docker Image概述
- 构建自己的Docker镜像
- Docker进程与宿主机进程的对应关系
- Docker Daemon 底层原理
- Docker的技术底座:
- UnionFS 联合文件系统
- Namespaces
- CGroups物理资源限制分组
- 总之:dockers=LXC+AUFS
- 深入解读docker网络
-
- docker网络理论部分
- Docker网络模式
- 网络实操
- 网络命令汇总
- Docker-Compose 简介
-
- Docker-Compose 用来实现Docker容器快速编排
- docker-compose 快速编排
- .....省略1W字
- 说在后面
- 参考资料
- 技术自由的实现路径 PDF:
-
-
- 实现你的 架构自由:
- 实现你的 响应式 自由:
- 实现你的 spring cloud 自由:
- 实现你的 linux 自由:
- 实现你的 网络 自由:
- 实现你的 分布式锁 自由:
- 实现你的 王者组件 自由:
- 实现你的 面试题 自由:
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
3、官方参考文档:https://docs.docker.com/install/linux/docker-ce/binaries/#install-static-binaries
二、Docker安装
wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.9.tgz
注意:如果事先下载好了可以忽略这一步
把压缩文件存在指定目录下(如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
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.
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"]
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 #查看父进程 ps aux | grep 3473 #查看子进程(容器)
Docker本地镜像载入与载出
- 保存镜像(保存镜像载入后获得跟原镜像id相同的镜像)
- 保存容器(保存容器载入后获得跟原镜像id不同的镜像)
通过命令可以从镜像仓库中拉取镜像,默认从Docker Hub 获取。
命令格式:
docker image pull <repository>:<tag> docker image pull rancher/rke-tools:v0.1.52 [rancher/rke-tools:v0.1.52
- docker save 镜像id -o /home/mysql.tar
- docker save 镜像id > /home/mysql.tar
docker save docker.io/rancher/rancher-agent -o /home/rancher-agent .tar docker save f29ece87a195 -o /home/rancher-agent.tar docker save docker.io/rancher/rke-tools -o /home/rke-tools-v0.1.52.tar
docker load -i mysql.tar
docker load -i /usr/local/rancher-v2.3.5.tar docker load -i /usr/local/rancher-agent.tar docker inspect f29ece87a1954772accb8a2332ee8c3fe460697e3f102ffbdc76eb9bc4f4f1d0 docker load -i /usr/local/rke-tools-v0.1.52.tar
docker load -i mysql.tar [root@localhost ~]# docker load -i /usr/local/rancher-v2.3.5.tar 43c67172d1d1: Loading layer [==================================================>] 65.57MB/65.57MB 21ec61b65b20: Loading layer [==================================================>] 991.2kB/991.2kB 1d0dfb259f6a: Loading layer [==================================================>] 15.87kB/15.87kB f55aa0bd26b8: Loading layer [==================================================>] 3.072kB/3.072kB e0af200d6950: Loading layer [==================================================>] 126.1MB/126.1MB 088ed892f9ad: Loading layer [==================================================>] 6.656kB/6.656kB 6aa3142b4130: Loading layer [==================================================>] 34.5MB/34.5MB f4e84c05ab29: Loading layer [==================================================>] 70.41MB/70.41MB 11a6e4332b53: Loading layer [==================================================>] 224.8MB/224.8MB 46d1ac556da7: Loading layer [==================================================>] 3.072kB/3.072kB 0f8b224a5802: Loading layer [==================================================>] 57.87MB/57.87MB 519eba7d586a: Loading layer [==================================================>] 99.58MB/99.58MB 3f8bb7c0c150: Loading layer [==================================================>] 4.608kB/4.608kB c22c9a5a8211: Loading layer [==================================================>] 3.072kB/3.072kB Loaded image: rancher/rancher:v2.3.5
打个tag
docker tag f29ece87a1954772accb8a2332ee8c3fe460697e3f102ffbdc76eb9bc4f4f1d0 rancher/rancher-agent:v2.3.5 docker tag f29ece87a195 172.18.8.104/rancher/rancher-agent:v2.3.5 docker tag 6e421b8753a2 172.18.8.104/rancher/rke-tools:v0.1.52 83fe4871cf67
docker rmi image_name docker rmi -f 172.18.8.104/rancher/coredns-coredns:1.6.5 docker rmi -f 172.18.8.104/rancher/coredns-coredns:v3.4.3-rancher1 docker rmi hub.doge.net/ubuntu:latest
- docker export 镜像id -o /home/mysql-export.tar
- docker save 镜像tag -o /home/mysql-export.tar
- docker import mysql-export.tar
Harbor私有镜像仓库
Harbor (港口,港湾)是一个用于存储和分发Docker镜像的企业级Registry服务器。
除了Harbor这个私有镜像仓库之外,还有Docker官方提供的Registry。
相对Registry,Harbor具有很多优势:
- 提供分层传输机制,优化网络传输 Docker镜像是是分层的,而如果每次传输都使用全量文件(所以用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定传输的对象。
- 提供WEB界面,优化用户体验 只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界面可以支持登陆、搜索功能,包括区分公有、私有镜像。
- 支持水平扩展集群 当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力作分解。
- 良好的安全机制 企业中的开发团队有很多不同的职位,对于不同的职位人员,分配不同的权限,具有更好的安全性。
Harbor安装
harbor是用过docker-compose 编排的。
所以 安装的过程中,会检查docker、docker-compose 进程,确保提前启动。
这些,在咱们的虚拟机里边,docker、docker-compose 进程已经预装好了的。
查看docker是否安装成功;
[root@centos1 ~]# docker -v Docker version 20.10.23, build 7155243
查看docker-compose是否安装成功;
docker-compose -version [root@centos1 ~]# docker-compose -version docker-compose version 1.25.1, build a82fef07 [root@centos1 ~]#
接下来,开始安装harbor
1、下载 Harbor的压缩包
https://github.com/goharbor/harbor/releases
咱们用这个包: harbor-offline-installer-v2.3.2.tgz
咱们的学习网盘当中,尼恩已经上传了哈
2、上传压缩包到虚拟机,并解压
[root@centos1 ~]# cd /usr/local/ [root@centos1 local]# mkdir harber [root@centos1 local]# cd harber/ tar -zxvf harbor-offline-installer-v2.3.2.tgz
3、创建harbor访问域名证书
OpenSSL是一个强大的安全套接字层密码库,Apache使用它加密HTTPS,OpenSSH使用它加密SSH,但是,你不应该只将其作为一个库来使用,它还是一个多用途的、跨平台的密码工具。
Harbor docs | Configure HTTPS Access to Harbor (goharbor.io)
mkdir -p /usr/local/harbor/ssl && cd /usr/local/harbor/ssl openssl genrsa -out tls.key 4096 openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \ -key tls.key \ -out tls.cert
- 第一步创建ssl文件夹用来存储证书,
- 第二步生成key (私钥),
- 最后一步使用生成的key自签证书。自签证书包含公钥
days后面是你自签证书的有效时间可以自行修改。
'CN='后面就写你自己的IP地址或者你自己的域名。
生成完成之后显示如下
4、配置harbor
修改Harbor的配置(harbor.yml);
修改主机地址:hostname: 192.168.56.121;
修改端口(默认端口80):port: 85;cd /usr/local/harbor/harbor #进入到harbor目录 cp harbor.yml.tmpl harbor.yml vim harbor.yml #编辑harbor的配置文件 #修改以下内容 hostname = 192.168.56.121 #修改harbor的启动ip,这里需要依据系统ip设置 port: 85 #harbor的端口,有两个端口,http协议(80)和https协议(443) harbor_admin_password = 123456 #修改harbor的admin用户的密码 data_volume: /harbor/data #修改harbor存储位置
故harbor.yml中certificate填写为如上tls.cert的文件目录地址:
/usr/local/harbor/ssl/tls.cert
故harbor.yml中private_key填写为如上tls.key的文件目录地址:
/usr/local/harbor/ssl/tls.key
5、./prepare 准备
启动之前需要使用
./prepare
命令进行一些预置工作,下载相关依赖;此时需要开启docker服务,不然会报错;
这个过程可能有点长,需要耐心等待;
/prepare
6、./install.sh
准备工作完成后,使用
./install.sh
进行Harbor的安装;
这个过程会持续一段时间,耐心等待;./install.sh
http://cdh1:85/
登录
默认登录名:admin;
默认登录密码:Harbor12345; 被尼恩改成了 12345
具体可以查看harbor.yml;7、停止或者重启 Harbor
cd /usr/local/harber/harbor/
docker-compose up -d 启动 docker-compose start 启动 docker-compose stop 停止 docker-compose restart 重新启动
修改docker配置文件,使docker支持harbor
编辑客户机/etc/docker/daemon.json文件
{"insecure-registries":["192.168.56.121:85"]}
重启客户机docker服务
systemctl restart docker #或者(service docker restart)
[root@centos1 harbor]# cat /etc/docker/daemon.json "registry-mirrors": [ "https://bjtzu1jb.mirror.aliyuncs.com", "http://f1361db2.m.daocloud.io", "https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn", "https://reg-mirror.qiniu.com", "https://dockerhub.azk8s.cn", "https://registry.docker-cn.com" "insecure-registries":["192.168.56.121:85"]
[root@centos1 ssh]# cat /etc/docker/daemon.json "registry-mirrors": [ "https://bjtzu1jb.mirror.aliyuncs.com", "http://f1361db2.m.daocloud.io", "https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn", "https://reg-mirror.qiniu.com", "https://dockerhub.azk8s.cn", "https://registry.docker-cn.com", "https://cdh1"
Harbor使用
可以 尝试上传镜像到 Harbor。
如果是另一台机器,需要添加本地hosts
echo '192.168.56.121 cdh' >> /etc/hosts
因为我们的证书是自签的是不受到其他机器信任的,所以我们要在 harber的客户端机器上放上我们的证书,才能拉取镜像。
使用下面的代码,在要使用 harber的客户端机器 上保存证书,其中 cdh1 部分使用你自己的域名或者是IP地址。
mkdir -p /etc/docker/certs.d/cdh1 scp 192.168.56.122:/usr/local/harbor/ssl/tls.cert /etc/docker/certs.d/cdh1/
本地docker 登录的话
mkdir -p /etc/docker/certs.d/cdh1 cp /usr/local/harbor/ssl/tls.cert /etc/docker/certs.d/cdh1/ca.crt mkdir -p /etc/docker/certs.d/192.168.56.121
然后使用如下语句登录harbor,如果没有使用80端口一定要用IP地址加上你的端口号,冒号之后填端口号,查看自己harbor.yml里http或https里的端口详情。
docker login cdh1
报错 证书问题, 证书不含SAN
什么是含有SAN的证书
docker 的新版本使用**golang 1.15+**版本上,老的x509 证书不行了
SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。
subjectAltName 在 RFC 5280 4.2.1.6.中提供了详细的说明,subjectAltName 是 X.509 version 3 的一个扩展项,该扩展项用于标记和界定证书持有者的身份。
在 X.509 格式的证书中,一般使用
Issuer
项标记证书的颁发者信息,该项必须是一个非空的 Distinguished Name 名称。除此之外还可以使用扩展项issuerAltName
来标记颁发者的其他名称,这是一个非关键的扩展项。对于证书持有者,一般使用
Subject
项标记,并使用subjectAltName
扩展项提供更详细的持有者身份信息。subjectAltName
全称为 Subject Alternative Name,缩写为 SAN。它可以包括一个或者多个的电子邮件地址,域名,IP地址和 URI 等,详细定义如下:SubjectAltName ::= GeneralNames GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName GeneralName ::= CHOICE { otherName [0] OtherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER
当颁发的证书不存在
Subject
项的时候,证书必须包含扩展项subjectAltName
,并且标记为关键(critical)的。当颁发的证书存在Subject
项的时候,必须将扩展项subjectAltName
标记为非关键(no-critical)的。注意:用于颁发证书的 CA 证书是必须包含Subject
项的。根据 RFC 6125 中的规定,当一个网站使用证书标记自己的身份时,如果证书中包含 subjectAltName,在识别证书持有者时会忽略 Subject 子项,而是通过 subjectAltName 来识别证书持有者。
在早期颁发的证书中一般通过 Subject 的 CommonName 来识别持有者的身份,不包含 subjectAltName 扩展项。
这会导致最新版本的浏览器Chrome、Firefox 等在通过 HTTPS 访问 web 网站时,触发 NET::ERR_CERT_COMMON_NAME_INVALID 错误。
SSL证书格式
证书主要的文件类型和协议有:PEM、DER、PFX、JKS、KDB、CER、KEY、CSR、CRT、CRL 、OCSP、SCEP等。
1. KEY
一般指PEM格式的私钥文件。
2. CRT
证书文件。可以是PEM格式。
3. PEM
PEM格式的证书文件(*.pem)由Base64编码的二进制内容和开头行(-----BEGIN CERTIFICATE-----)、结束行(-----END CERTIFICATE-----)组成,支持使用notepad++等文本编辑器打开。对于CER、CRT格式的证书,您可通过直接修改证书文件扩展名的方式将其转换成PEM格式。例如,将server.crt证书文件直接重命名为server.pem。
4. CSR
证书请求文件(Certificate Signing Request)。生成 X509 数字证书前,一般先由用户提交证书申请文件,然后由 CA 来签发证书。大致过程如下(X509 证书申请的格式标准为 pkcs10 和 rfc2314):
- 用户生成自己的公私钥对;
- 构造自己的证书申请文件,符合 PKCS10 标准。该文件主要包括了用户信息、公钥以及一些可选的属性信息,并用自己的私钥给该内容签名;
- 用户将证书申请文件提交给 CA;
- CA 验证签名,提取用户信息,并加上其他信息(比如颁发者等信息),用 CA 的私钥签发数字证书;
- 说明:数字证书(如x.509)是将用户(或其他实体)身份与公钥绑定的信息载体。一个合法的数字证书不仅要符合 X509 格式规范,还必须有 CA 的签名。用户不仅有自己的数字证书,还必须有对应的私钥。X509v3 数字证书主要包含的内容有:证书版本、证书序列号、签名算法、颁发者信息、有效时间、持有者信息、公钥信息、颁发者 ID、持有者 ID 和扩展项。
5. DER
辨别编码规则 (DER) 可包含所有私钥、公钥和证书。它是大多数浏览器的缺省格式,并按 ASN1 DER 格式存储。它是无报头的 - PEM 是用文本报头包围的 DER。
6. PFX
公钥加密标准 12 (PKCS12) 可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件。通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式合并转换为标准的PFX文件,你可以将PFX文件格式导入到微软IIS 5/6、微软ISA、微软Exchange Server等软件。转换时需要输入PFX文件的加密密码。
7. JKS
通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式”转换为标准的Java Key Store(JKS)文件。JKS文件格式被广泛的应用在基于JAVA的WEB服务器、应用服务器、中间件。你可以将JKS文件导入到TOMCAT、 WEBLOGIC 等软件。
8. KDB
通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式转换为标准的IBM KDB文件。KDB文件格式被广泛的应用在IBM的WEB服务器、应用服务器、中间件。你可以将KDB文件导入到IBM HTTP Server、IBM Websphere 等软件。
9. OCSP
在线证书状态协议(OCSP,Online Certificate Status Protocol,rfc2560)用于实时表明证书状态。OCSP 客户端通过查询 OCSP 服务来确定一个证书的状态,可以提供给使用者一个或多个数字证书的有效性资料,它建立一个可实时响应的机制,让用户可以实时确认每一张证书的有效性,解决由CRL引发的安全问题。。OCSP 可以通过 HTTP协议来实现。rfc2560 定义了 OCSP 客户端和服务端的消息格式。
10. CER
一般指使用DER格式的证书。
11. CRL
证书吊销列表 (Certification Revocation List) 是一种包含撤销的证书列表的签名数据结构。CRL 是证书撤销状态的公布形式,CRL 就像信用卡的黑名单,用于公布某些数字证书不再有效。CRL 是一种离线的证书状态信息。它以一定的周期进行更新。CRL 可以分为完全 CRL和增量 CRL。在完全 CRL 中包含了所有的被撤销证书信息,增量 CRL 由一系列的 CRL 来表明被撤销的证书信息,它每次发布的 CRL 是对前面发布 CRL 的增量扩充。基本的 CRL 信息有:被撤销证书序列号、撤销时间、撤销原因、签名者以及 CRL 签名等信息。基于 CRL 的验证是一种不严格的证书认证。CRL 能证明在 CRL 中被撤销的证书是无效的。但是,它不能给出不在 CRL 中的证书的状态。如果执行严格的认证,需要采用在线方式进行认证,即 OCSP 认证。一般是由CA签名的一组电子文档,包括了被废除证书的唯一标识(证书序列号),CRL用来列出已经过期或废除的数字证书。它每隔一段时间就会更新,因此必须定期下载该清单,才会取得最新信息。
12. SCEP
简单证书注册协议。基于文件的证书登记方式需要从您的本地计算机将文本文件复制和粘贴到证书发布中心,和从证书发布中心复制和粘贴到您的本地计算机。 SCEP可以自动处理这个过程但是CRLs仍然需要手工的在本地计算机和CA发布中心之间进行复制和粘贴。
13. PKCS7
加密消息语法(pkcs7),是各种消息存放的格式标准。这些消息包括:数据、签名数据、数字信封、签名数字信封、摘要数据和加密数据。
14. PKCS12
– pkcs12 (个人数字证书标准)用于存放用户证书、crl、用户私钥以及证书链。pkcs12 中的私钥是加密存放的。
生成含有SAN的证书
- 使用OPENssl命令行来生成KEY+CSR2个文件,
- 使用KEYTOOL来生成JKS和CSR文件
1、生成CA证书私钥
openssl genrsa -out ca.key 4096
[root@docker-compose-harbor CA]# openssl genrsa -out ca.key 4096 Generating RSA private key, 4096 bit long modulus ...............................................................++ ............................................................++ e is 65537 (0x10001) [root@docker-compose-harbor CA]# [root@docker-compose-harbor CA]# [root@docker-compose-harbor CA]# ls ca.key [root@docker-compose-harbor CA]#
2、生成CA证书
openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \ -key ca.key \ -out ca.crt
自签名的证书,不被浏览器信任,适合内部或者测试使用。
3、生成服务器证书
[root@docker-compose-harbor CA]# openssl genrsa -out cdh1.key 4096 Generating RSA private key, 4096 bit long modulus .........................................................................................................................++ ...........................................................................................................................................................................................................++ e is 65537 (0x10001) [root@docker-compose-harbor CA]# [root@docker-compose-harbor CA]# ll -h total 12K -rw-r--r--. 1 root root 3.2K Jul 20 04:52 cdh1.key -rw-r--r--. 1 root root 2.0K Jul 20 04:51 ca.crt -rw-r--r--. 1 root root 3.2K Jul 20 04:48 ca.key [root@docker-compose-harbor CA]#
生成证书签名请求(CSR)
制作过程中,系统会产生2个密钥,一个是私钥,存放在服务器上,一个是CSR文件公钥,需要ca签名。
openssl req -sha512 -new \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \ -key cdh1.key \ -out cdh1.csr
需要依次输入国家,地区,城市,组织,组织单位,Common Name和Email。
其中Common Name,可以写自己的名字或者域名,如果要支持https,Common Name应该与域名保持一致,否则会引起浏览器警告。
可以将证书发送给证书颁发机构(CA),CA验证过请求者的身份之后,会出具签名证书,需要花钱。
另外,如果只是内部或者测试需求,也可以使用OpenSSL实现自签名。
[root@docker-compose-harbor CA]# openssl req -sha512 -new \ > -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \ > -key cdh1.key \ > -out cdh1.csr [root@docker-compose-harbor CA]# ll -h total 20K -rw-r--r--. 1 root root 1.7K Jul 20 04:59 cdh1.csr -rw-r--r--. 1 root root 3.2K Jul 20 04:52 cdh1.key -rw-r--r--. 1 root root 2.0K Jul 20 04:51 ca.crt -rw-r--r--. 1 root root 3.2K Jul 20 04:48 ca.key [root@docker-compose-harbor CA]# 生成一个x509 v3扩展文件 cat > v3.ext <<-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = IP:cdh1
4、使用该v3.ext文件为Harbor主机生成证书cdh1.crt
openssl x509 -req -sha512 -days 3650 \ -extfile v3.ext \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -in cdh1.csr \ -out cdh1.crt
[root@docker-compose-harbor CA]# openssl x509 -req -sha512 -days 3650 \ > -extfile v3.ext \ > -CA ca.crt -CAkey ca.key -CAcreateserial \ > -in cdh1.csr \ > -out cdh1.crt Signature ok subject=/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1 Getting CA Private Key [root@docker-compose-harbor CA]#
当服务端向 CA 机构申请证书的时候,CA 签发证书的过程:
- 首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
- 然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
- 最后将 Certificate Signature 添加在文件证书上,形成数字证书;
5、转换cdh1.crt为cdh1.cert,供Docker使用
转换证书格式
Docker守护程序将.crt文件解释为CA证书,并将.cert文件解释为客户端证书
openssl x509 -inform PEM -in cdh1.crt -out cdh1.cert
修改harbor.yml文件key路径
修改对应的证书地址:
cdh1.cert
的地址cdh1.key
的地址
故harbor.yml中certificate填写为如上cdh1.cert的文件目录地址:
/usr/local/harbor/ssl/cdh1.cert
故harbor.yml中private_key填写为如上cdh1.key的文件目录地址:
/usr/local/harbor/ssl/cdh1.key
[root@docker-compose-harbor harbor]# egrep -v "^$|^#" harbor.yml |head -10 hostname: cdh1 http: # port for http, default is 80. If https enabled, this port will redirect to https port port: 80 https: # https port for harbor, default is 443 port: 443 # The path of cert and key files for nginx certificate: /opt/CA/harbor/cert/cdh1.crt private_key: /opt/CA/harbor/cert/cdh1.key [root@docker-compose-harbor harbor]#
6、运行prepare脚本以启用HTTPS
./prepare
[root@docker-compose-harbor harbor]# ./prepare prepare base dir is set to /opt/harbor/harbor Generated configuration file: /config/portal/nginx.conf Generated configuration file: /config/log/logrotate.conf Generated configuration file: /config/log/rsyslog_docker.conf Generated configuration file: /config/nginx/nginx.conf Generated configuration file: /config/core/env Generated configuration file: /config/core/app.conf Clean up the input dir [root@docker-compose-harbor harbor]#
7、运行install.sh脚本来启动harbor
./install.sh
[root@docker-compose-harbor harbor]# ./install.sh [Step 0]: checking if docker is installed ... Note: docker version: 20.10.17 [Step 1]: checking docker-compose is installed ...
证书复制到 docker 并且启动后登录
cp /usr/local/harbor/ssl/cdh1.cert /etc/docker/certs.d/cdh1/ca.crt systemctl daemon-reload && systemctl restart docker
hostname push失败
push本地镜像到 harbor私服时,push 到 docker.io仓库去了
在配置insecure-registry时,docker 必须配置服务器的 FQDN或者IP地址.不能是服务器的hostname(比如harbor)
尼恩配置的 是cdh1,推不上去。
FQDN是什么意思?
FQDN(fully qualified domain name)完全限定域名,是互联网上特定计算机或主机的完整域名。FQDN 由两部分组成:主机名和域名。
例如,假设邮件服务器的 FQDN 可能是 mail.chenweiliang.com 。
主机名为mail,主机位于域名chenweiliang.com。
DNS(Domain Name System),负责将 FQDN 转换为 IP地址,是 Internet 上大多数应用程序的寻址方式。
FQDN:(Fully Qualified Domain Name)完全限定域名:同时包含主机名和域名的名称。 (通过符号“.”)以下为解决方法:
配置harbor服务器的
/etc/hosts
,将本地ip地址对应为一条FQDN记录,比如
192.168.56.121 harbor.daemon.io
停止harbor后,修改harbor配置 harbor.yml 文件,将 hostname 配置项改为
harbor.daemon.io
(一个FQDN),然后重新配置 harbor.docker-compose down -v
生成配置文件
./prepare
启动habror
docker-comp up -d
(可以在另外一台能访问harbor服务器的机器上配置 hosts 记录为 FQDN,然后web访问 harbor检查是否能正常登陆.)
重新配置 docker daemon 中的配置 registry-mirrors 和 insecure-registries 然后重启 docker.
编辑客户机
/etc/docker/daemon.json
文件{"insecure-registries":["http://harbor.daemon.io:85"]}
重启客户机docker服务
systemctl restart docker #或者(service docker restart)
之后就能正常pull,并且push本地镜像到 harbor私服中.
附上完整的:
"registry-mirrors": [ "https://bjtzu1jb.mirror.aliyuncs.com", "http://f1361db2.m.daocloud.io", "https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn", "https://reg-mirror.qiniu.com", "https://dockerhub.azk8s.cn", "https://registry.docker-cn.com", "https://harbor.daemon.io" "insecure-registries":["http://harbor.daemon.io:85"]推送镜像到Harber
Docker 推送命令
在项目中标记镜像:
docker tag SOURCE_IMAGE[:TAG] harbor.daemon.io/demo/REPOSITORY[:TAG]
docker tag nginx:latest harbor.daemon.io/demo/nginx:latest
推送镜像到当前项目:
docker push harbor.daemon.io/demo/REPOSITORY[:TAG]
docker push harbor.daemon.io/demo/nginx:latest
错误提升:
[root@centos1 harbor]# docker push harbor.daemon.io/demo/nginx:latest The push refers to repository [harbor.daemon.io/demo/nginx] Get "https://harbor.daemon.io/v2/": x509: certificate is valid for cdh1, not harbor.daemon.io
需要生成证书
openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=harbor.daemon.io" \ -key ca.key \ -out ca.crt openssl genrsa -out harbor.daemon.io.key 4096 openssl req -sha512 -new \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=harbor.daemon.io" \ -key harbor.daemon.io.key \ -out harbor.daemon.io.csr 生成一个x509 v3扩展文件 cat > harbor.daemon.io.v3.ext <<-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = DNS:harbor.daemon.io openssl x509 -req -sha512 -days 3650 \ -extfile harbor.daemon.io.v3.ext \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -in harbor.daemon.io.csr \ -out harbor.daemon.io.crt openssl x509 -inform PEM -in harbor.daemon.io.crt -out harbor.daemon.io.cert
harbor.yml中certificate填写为如上cdh1.cert的文件目录地址:
/usr/local/harbor/ssl/harbor.daemon.io.cert
harbor.yml中private_key填写为如上cdh1.key的文件目录地址:
/usr/local/harbor/ssl/harbor.daemon.io.key
mkdir /etc/docker/certs.d/harbor.daemon.io cp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/ca.crt cp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/harbor.daemon.io.cert cp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/ca.crt docker-compose down prepare systemctl restart docker docker-compose up -d docker login harbor.daemon.io
推送镜像到当前项目:
docker push harbor.daemon.io/demo/REPOSITORY[:TAG]
docker push harbor.daemon.io/demo/nginx:latest
http://cdh1:85/
Docker Image概述
什么是Image
- 文件和meta data的集合(root filesystem)
- 分层的,并且每一层都可以添加改变删除文件,成为一个新的image
- 不同的image可以共享相同的layer
- Image本身是read-only的
Image的获取
- 方式1:Build from Dockerfile
- 方式2:Pull from Registry
例如:输入命令
sudo docker pull ubuntu:14.04
,则会从Registry中拉出Image,Registry类似于github的作用,默认都是从docker hub上面拉取,上面会有官方和第三方的版本输入命令 sudo docker image ls则可以显示所拉去的Image
如何做一个自己的Base Image
1.首先创建一个可以执行的程序,下面用一个C语言的hello程序做例子
2.通过dockerfile把这个可执行文件打成一个Image
我们在hellow文件当前目录下创建一个Dockerfile文件,如下:3.执行命令:
docker build -t yunduan/hello-world
. (-t
表示指示表情,"."
表示在当前路径下寻找dockerfile),执行以后,出现如下界面表示执行成功。4.查看image,则可以看到成功build了一个image,执行命令
docker history +[IMAGE ID]
则可以查看镜像的层级
执行命令docker run + [镜像标签名]
则可以生成一个container运行程序。构建自己的Docker镜像
- 命令一:docker container commit(Create a new image from a container’s changes)
这个命令表示当你在容器中做出了改变之后,可以重新构建Image
通过这个例子可以看出,其就在centos镜像上重新构建了一层
- 命令二:docker image build(Build an image from a Dockerfile)
Dockerfile语法
原则:尽量使用官方的image作为base image!
FROM scratch #制作base image FROM centos #使用base image FROM ubuntu:14.04
- LABEL
原则:Metadata不可少!相当于代码的注释
LABEL maintainer="yunduan@gmail.com" LABEL version="1.0" LABEL description="This is description"
作用:执行命令并创建新的Image Layer
原则:为了美观,复杂的RUN请用反斜线换行!避免无用分层,合并多条命令成一行!
RUN yum update && yum install -y vim \ python-dev #反斜线换行 RUN apt-get update && apt-get install -y perl \ pwgen --no-install-recommends && rm -rf \ /var/lib/apt/lists/* #注意清理cache RUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'
- WORKDIR
作用:设定当前目录,类似于cd
原则:用WORKDIR,不要用RUN cd!尽量使用绝对目录
WORKDIR /root WORKDIR /test #如果没有会自动创建test目录 WORKDIR demo RUN pwd #输出结果应该是 /test/demo
- ADD and COPY
作用:把本地文件添加到Docker image里面
原则:大部分情况,COPY优先于ADD!ADD除了COPY还有额外功能(解压)!添加远程文件/目录请使用curl或者wget!
ADD hello / ADD test.tar.gz / #添加到根目录并解压 WORKDIR /root ADD hello test/ # /root/test/hello WORKDIR /root COPY hello test/
作用:设置一个环境变量,引用常量
原则:尽量使用ENV增加可维护性!
ENV MYSQL_VERSION 5.6 # 设置常量 RUN apt-get install -y mysql-server= "${MYSQL_VERSION}" \ && rm -rf /var/lib/apt/lists/* # 引用常量
- VOLUME and EXPOSE (存储和网络)
- CMD and ENTRYPOINT
CMD:设置容器启动后默认执行的命令和参数
注释:1.容器启动时默认执行的命令 2.如果docker run指定了其他命令,CMD命令被忽略 3.如果定义了多个CMD,只有最后一个会执行
ENTRYPOINT:设置容器启动时运行的命令
注释:1.让容器以应用程序或者服务的形式运行 2.不会被忽略,一定会执行 3.最佳实践:写一个shell脚本作为entrypoint
1.Shell格式
RUN apt-get install -y vim CMD echo "hello docker" ENTRYPOINT echo "hello docker"
2.Exec格式
RUN ["apt-get","install","-y","vim"] CMD [" /bin/echo" , "hello docker" ] ENTRYPOINT ["/bin/echo" , "hello docker"]
3.Shell和Exec格式
- Dockerfile1 A
FROM centos ENV name Docker ENTRYPOINT echo "hello $name"
- Dockerfile2
FROM centos ENV name Dokcer ENTRYPOINT ["/bin/bash", "-c","echo hello $name" ]
- docker login
- docker push
Docker进程与宿主机进程的对应关系
Linux通过进程ID查看文件路径
子进程的文件路径
[root@VM-4-17-centos ~]# ls -l /proc/27880 total 0 dr-xr-xr-x 2 root root 0 Nov 3 16:41 attr -rw-r--r-- 1 root root 0 Nov 3 16:41 autogroup -r-------- 1 root root 0 Nov 3 16:41 auxv -r--r--r-- 1 root root 0 Nov 3 16:14 cgroup --w------- 1 root root 0 Nov 3 16:41 clear_refs -r--r--r-- 1 root root 0 Nov 3 16:15 cmdline -rw-r--r-- 1 root root 0 Nov 3 16:41 comm -rw-r--r-- 1 root root 0 Nov 3 16:41 coredump_filter -r--r--r-- 1 root root 0 Nov 3 16:41 cpuset lrwxrwxrwx 1 root root 0 Nov 3 16:41 cwd -> / -r-------- 1 root root 0 Nov 3 16:41 environ lrwxrwxrwx 1 root root 0 Nov 3 16:14 exe -> /usr/sbin/sshd dr-x------ 2 root root 0 Nov 3 16:14 fd dr-x------ 2 root root 0 Nov 3 16:41 fdinfo -rw-r--r-- 1 root root 0 Nov 3 16:41 gid_map -r-------- 1 root root 0 Nov 3 16:41 io -r--r--r-- 1 root root 0 Nov 3 16:41 limits -rw-r--r-- 1 root root 0 Nov 3 16:41 loginuid dr-x------ 2 root root 0 Nov 3 16:41 map_files -r--r--r-- 1 root root 0 Nov 3 16:41 maps -rw------- 1 root root 0 Nov 3 16:41 mem -r--r--r-- 1 root root 0 Nov 3 16:14 mountinfo -r--r--r-- 1 root root 0 Nov 3 16:41 mounts -r-------- 1 root root 0 Nov 3 16:41 mountstats dr-xr-xr-x 5 root root 0 Nov 3 16:41 net dr-x--x--x 2 root root 0 Nov 3 16:14 ns -r--r--r-- 1 root root 0 Nov 3 16:41 numa_maps -rw-r--r-- 1 root root 0 Nov 3 16:41 oom_adj -r--r--r-- 1 root root 0 Nov 3 16:41 oom_score -rw-r--r-- 1 root root 0 Nov 3 16:41 oom_score_adj -r--r--r-- 1 root root 0 Nov 3 16:41 pagemap -r-------- 1 root root 0 Nov 3 16:41 patch_state -r--r--r-- 1 root root 0 Nov 3 16:41 personality -rw-r--r-- 1 root root 0 Nov 3 16:41 projid_map lrwxrwxrwx 1 root root 0 Nov 3 16:41 root -> / -rw-r--r-- 1 root root 0 Nov 3 16:41 sched -r--r--r-- 1 root root 0 Nov 3 16:41 schedstat -r--r--r-- 1 root root 0 Nov 3 16:41 sessionid -rw-r--r-- 1 root root 0 Nov 3 16:41 setgroups -r--r--r-- 1 root root 0 Nov 3 16:41 smaps -r--r--r-- 1 root root 0 Nov 3 16:41 stack -r--r--r-- 1 root root 0 Nov 3 16:14 stat -r--r--r-- 1 root root 0 Nov 3 16:41 statm -r--r--r-- 1 root root 0 Nov 3 16:14 status -r--r--r-- 1 root root 0 Nov 3 16:41 syscall dr-xr-xr-x 3 root root 0 Nov 3 16:41 task -r--r--r-- 1 root root 0 Nov 3 16:41 timers -rw-r--r-- 1 root root 0 Nov 3 16:14 uid_map -r--r--r-- 1 root root 0 Nov 3 16:41 wchan
以下是/proc目录中进程27880的信息说明:
proc/27880 pid为N的进程信息 /proc/27880/cmdline 进程启动命令 /proc/27880/cwd 链接到进程当前工作目录 /proc/27880/environ 进程环境变量列表 /proc/27880/exe 链接到进程的执行命令文件 /proc/27880/fd 包含进程相关的所有的文件描述符 /proc/27880/maps 与进程相关的内存映射信息 /proc/27880/mem 指代进程持有的内存,不可读 /proc/27880/root 链接到进程的根目录 /proc/27880/stat 进程的状态 /proc/27880/statm 进程使用的内存的状态 /proc/27880/status 进程状态信息,比stat/statm更具可读性
容器的PID namespace(命名空间)
在Docker中,进程管理的基础就是Linux内核中的PID名空间技术。
在不同PID名空间中,进程ID是独立的;即在两个不同名空间下的进程可以有相同的PID。
在Docker中,每个Container进程缺省都具有不同的PID名空间。通过名空间技术,Docker实现容器间的进程隔离。
docker中运行的容器进程,本质上还是运行在宿主机上的,所以也会拥有相对应的PID
找出容器ID
docker ps
[root@VM-4-17-centos ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 460d68823930 lemonbar/centos6-ssh:latest "/bin/sh -c '/usr/sb…" 32 minutes ago Up 32 minutes 0.0.0.0:6021->22/tcp, 0.0.0.0:6081->80/tcp centos6-2
查看容器信息
docker inspect id
[root@VM-4-17-centos ~]# docker inspect 460d68823930 [root@VM-4-17-centos ~]# docker inspect 460d68823930 "Id": "460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd", "Created": "2021-11-03T08:24:36.934129599Z", "Path": "/bin/sh", "Args": [ "-c", "/usr/sbin/sshd -D" "State": { "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 4962, "ExitCode": 0, "Error": "", "StartedAt": "2021-11-03T08:24:37.223255812Z", "FinishedAt": "0001-01-01T00:00:00Z" "Image": "sha256:efd998bd6817af509d348b488e3ce4259f9f05632644a7bf574b785bbc8950b8", "ResolvConfPath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/resolv.conf", "HostnamePath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/hostname", "HostsPath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/hosts", "LogPath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd-json.log", "Name": "/centos6-2", "RestartCount": 0, "Driver": "overlay2", "Platform": "linux", "MountLabel": "", "ProcessLabel": "", "AppArmorProfile": "", "ExecIDs": null, "HostConfig": { "Binds": null, "ContainerIDFile": "", "LogConfig": { "Type": "json-file", "Config": {} "NetworkMode": "default", "PortBindings": { "22/tcp": [ "HostIp": "", "HostPort": "6021" "80/tcp": [ "HostIp": "", "HostPort": "6081" "RestartPolicy": { "Name": "no", "MaximumRetryCount": 0 "AutoRemove": false, "VolumeDriver": "", "VolumesFrom": null, "CapAdd": null, "CapDrop": null, "Dns": [], "DnsOptions": [], "DnsSearch": [], "ExtraHosts": null, "GroupAdd": null, "IpcMode": "shareable", "Cgroup": "", "Links": null, "OomScoreAdj": 0, "PidMode": "", "Privileged": false, "PublishAllPorts": false, "ReadonlyRootfs": false, "SecurityOpt": null, "UTSMode": "", "UsernsMode": "", "ShmSize": 67108864, "Runtime": "runc", "ConsoleSize": [ "Isolation": "", "CpuShares": 0, "Memory": 0, "NanoCpus": 0, "CgroupParent": "", "BlkioWeight": 0, "BlkioWeightDevice": [], "BlkioDeviceReadBps": null, "BlkioDeviceWriteBps": null, "BlkioDeviceReadIOps": null, "BlkioDeviceWriteIOps": null, "CpuPeriod": 0, "CpuQuota": 0, "CpuRealtimePeriod": 0, "CpuRealtimeRuntime": 0, "CpusetCpus": "", "CpusetMems": "", "Devices": [], "DeviceCgroupRules": null, "DiskQuota": 0, "KernelMemory": 0, "MemoryReservation": 0, "MemorySwap": 0, "MemorySwappiness": null, "OomKillDisable": false, "PidsLimit": 0, "Ulimits": null, "CpuCount": 0, "CpuPercent": 0, "IOMaximumIOps": 0, "IOMaximumBandwidth": 0 "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6-init/diff:/var/lib/docker/overlay2/7139bf0b716c6e0b6a0c709b7043466f9bbfd7024f8ae584061c00b0bd97348c/diff:/var/lib/docker/overlay2/66a3e278259cdcf50741ce30a115baa3bd6247a60c487e4118e85f2f39328f11/diff:/var/lib/docker/overlay2/20e22c4c28ebadb615eb4c7c290253d3eb91cb49722ee2931b0ee628352a5857/diff:/var/lib/docker/overlay2/a3fa9dbebc83a853083205b8f7921c632cd67f64531f4a25cab419a43172e3ae/diff:/var/lib/docker/overlay2/3af7958c9a4e54d24598058a9fa1e85eb35e3d40f766fa498a674b52724ae73e/diff:/var/lib/docker/overlay2/becb65af4396137ed41fe6d516e834e6e6e9120f4edfac8e2ca8dd67cce23268/diff:/var/lib/docker/overlay2/fef055305158cc96906514c447f0eaea05945138896b0b35ac4146b6a2a3e273/diff:/var/lib/docker/overlay2/79158cdf3ba832493ab0d02d560c784208fe51c74236a5a86f7fb4fb50ab6e44/diff:/var/lib/docker/overlay2/86258a18e1110582b819719593687f11f0404d00a41667b3432c3b974fb1ce42/diff:/var/lib/docker/overlay2/8826b2e0068653fb2c5e8a3dbf839470e2b8eef8cf752b5fe901bea1b210849f/diff:/var/lib/docker/overlay2/145301e2738a8a7581c2bbd5beb9bf7a49b247e46642b8084efbc026a1826116/diff:/var/lib/docker/overlay2/f621f37535e0db1fe44902e22dba7ef0844b9a8b562a9daa39a842a49e9cc9bb/diff:/var/lib/docker/overlay2/7b493e4a97907aaa18b97ad2e9120b5bf87c0e9908ee390a35ea6ff546d8cec6/diff", "MergedDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6/merged", "UpperDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6/diff", "WorkDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6/work" "Name": "overlay2" "Mounts": [], "Config": { "Hostname": "460d68823930", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "22/tcp": {}, "80/tcp": {} "Tty": true, "OpenStdin": true, "StdinOnce": false, "Env": [ "HOME=/", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" "Cmd": [ "/bin/sh", "-c", "/usr/sbin/sshd -D" "Image": "docker.io/lemonbar/centos6-ssh:latest", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} "NetworkSettings": { "Bridge": "", "SandboxID": "ea66261fb6d8089d5b2d585a2dc32b2003365df7118f5f5e898a152fb5b35773", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": { "22/tcp": [ "HostIp": "0.0.0.0", "HostPort": "6021" "80/tcp": [ "HostIp": "0.0.0.0", "HostPort": "6081" "SandboxKey": "/var/run/docker/netns/ea66261fb6d8", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "09ad719a4e9115ee56c5fb0f5b0d39c50bf5acaf0a1afacedc13969c82a2969f", "Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.6", "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "02:42:ac:11:00:06", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "2586283d16a08210c955d705f05e0f6999b59523a84b0c163e33f535af809ddd", "EndpointID": "09ad719a4e9115ee56c5fb0f5b0d39c50bf5acaf0a1afacedc13969c82a2969f", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.6", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:06", "DriverOpts": null
进入相应目录
cd /sys/fs/cgroup/memory/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/
cd /sys/fs/cgroup/memory/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/ [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# ll total 0 -rw-r--r-- 1 root root 0 Nov 3 16:24 cgroup.clone_children --w--w--w- 1 root root 0 Nov 3 16:24 cgroup.event_control -rw-r--r-- 1 root root 0 Nov 3 16:24 cgroup.procs -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.failcnt --w------- 1 root root 0 Nov 3 16:24 memory.force_empty -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.failcnt -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.limit_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.max_usage_in_bytes -r--r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.slabinfo -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.tcp.failcnt -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.tcp.limit_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.tcp.max_usage_in_bytes -r--r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.tcp.usage_in_bytes -r--r--r-- 1 root root 0 Nov 3 16:24 memory.kmem.usage_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.limit_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.max_usage_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.memsw.failcnt -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.memsw.limit_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.memsw.max_usage_in_bytes -r--r--r-- 1 root root 0 Nov 3 16:24 memory.memsw.usage_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.move_charge_at_immigrate -r--r--r-- 1 root root 0 Nov 3 16:24 memory.numa_stat -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.oom_control ---------- 1 root root 0 Nov 3 16:24 memory.pressure_level -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.soft_limit_in_bytes -r--r--r-- 1 root root 0 Nov 3 16:24 memory.stat -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.swappiness -r--r--r-- 1 root root 0 Nov 3 16:24 memory.usage_in_bytes -rw-r--r-- 1 root root 0 Nov 3 16:24 memory.use_hierarchy -rw-r--r-- 1 root root 0 Nov 3 16:24 notify_on_release -rw-r--r-- 1 root root 0 Nov 3 16:24 tasks
[root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs 11539 [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat pids.max [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat tasks 11539 [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.clone_children [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# pwd /sys/fs/cgroup/pids/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd
查看容器目录里的进程号
进程号就存在一个文件里面
[root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs
与前面利用
docker top
命令,可以让我们从宿主机操作系统中看到容器的进程信息。[root@VM-4-17-centos ~]# docker top centos6-2 UID PID PPID C STIME TTY TIME CMD root 4962 4948 0 16:24 pts/0 00:00:00 /usr/sbin/sshd -D
启动一个进程
我们下面会在 centos6-2容器中,利用
docker exec
命令启动一个"sleep"进程[root@VM-4-17-centos ]# docker exec -d centos6-2 sleep 2000 [root@VM-4-17-centos ]# docker exec centos6-2 ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 08:24 pts/0 00:00:00 /usr/sbin/sshd -D root 6 0 0 09:06 ? 00:00:00 sleep 2000 root 10 0 0 09:06 ? 00:00:00 ps -ef
查看宿主机的进程号
[root@VM-4-17-centos ]# docker top centos6-2 UID PID PPID C STIME TTY TIME CMD root 4962 4948 0 16:24 pts/0 00:00:00 /usr/sbin/sshd -D root 11539 4948 0 17:06 ? 00:00:00 sleep 2000
我们可以清楚的看到exec命令创建的sleep进程属 centos6-2 容器的名空间,但是它的父进程是Docker 容器的启动进程。
查看容器目录里的进程号
进程号就存在一个文件里面
[root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs 11539
docker exec -d centos6-2 pstree -p docker exec -d centos6-2 ps -auxf docker exec -d centos6-2 ll /proc
[root@VM-4-17-centos docker]# docker exec centos6-2 ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 08:24 pts/0 00:00:00 /usr/sbin/sshd -D root 6 0 0 09:06 ? 00:00:00 sleep 2000 root 40 0 0 09:26 ? 00:00:00 ps -ef [root@VM-4-17-centos docker]# docker exec centos6-2 ps -auxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 44 0.0 0.0 13360 1012 ? Rs 09:26 0:00 ps -auxf root 6 0.0 0.0 4120 316 ? Ss 09:06 0:00 sleep 2000 root 1 0.0 0.0 66664 3068 pts/0 Ss+ 08:24 0:00 /usr/sbin/sshd -D Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ [root@VM-4-17-centos docker]# docker exec centos6-2 pstree -p sshd(1)
docker daemon (docker守护进程)
pidof dockerd #查看docker守护进程pid lsof -p 3197 | wc -l #docker守护进程打开的文件数
在两个容器中的"centos "是两个独立的进程,但是他们拥有相同的父进程 Docker Daemon。
所以Docker可以父子进程的方式在Docker Daemon和Redis容器之间进行交互。
另一个值得注意的方面是,
docker exec
命令可以进入指定的容器内部执行命令。由它启动的进程属于容器的namespace和相应的cgroup。但是这些进程的父进程是Docker Daemon而非容器的PID1进程。
我们下面会在Redis容器中,利用
docker exec
命令启动一个"sleep"进程docker@default:~$ docker exec -d redis sleep 2000 docker@default:~$ docker exec redis ps -ef UID PID PPID C STIME TTY TIME CMD redis 1 0 0 02:26 ? 00:00:00 redis-server *:6379 root 11 0 0 02:26 ? 00:00:00 sleep 2000 root 21 0 0 02:29 ? 00:00:00 ps -ef docker@default:~$ docker top redis UID PID PPID C STIME TTY TIME CMD 999 9955 1264 0 02:12 ? 00:00:00 redis-server *:6379 root 9984 1264 0 02:13 ? 00:00:00 sleep 2000
我们可以清楚的看到exec命令创建的sleep进程属Redis容器的名空间,但是它的父进程是Docker Daemon。
如果我们在宿主机操作系统中手动杀掉容器的启动进程(在上文示例中是redis-server),容器会自动结束,而容器名空间中所有进程也会退出。
docker@default:~$ PID=
-