当前位置: 首页 > news >正文

网站建设填空题汕头网站推广费用

网站建设填空题,汕头网站推广费用,建设一个自己的网站需要多少钱,偷别人的WordPress主题说在前面#xff1a; 现在拿到offer超级难#xff0c;甚至连面试电话#xff0c;一个都搞不到。 尼恩的技术社群#xff08;50#xff09;中#xff0c;很多小伙伴凭借 “左手云原生右手大数据”的绝活#xff0c;拿到了offer#xff0c;并且是非常优质的offer#…说在前面 现在拿到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 版本之后分为 CECommunity Edition: 社区版 和 EEEnterprise Edition: 企业版 对于开发人员来说用 CECommunity 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平台的创业公司从事LXCLinux 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 InitiativeOCI开放容器标准。 OCI 开源社区旨在于将 Docker 的发展权利回归社区当然反过来讲Docker 公司也希望更多的厂商安心贡献代码到Docker 项目促进 Docker 项目的发展。 Docker 将自己容器格式和运行时 runC 捐给了 OCIOCI 在此基础上制定了 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 ServerCLI接口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 包括三个基本概念: 镜像ImageDocker 镜像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 RegistryDocker 仓库用来保存镜像可以理解为代码控制中的代码仓库。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、硬件要求。 本文硬件总体要求如下表 序号硬件要求1CPU至少2核2内存至少8G3硬盘至少100G磁盘空间 2、本地虚拟机环境 软件版本Winwin10以上virtual box6以上vagrant2以上 dockerK8S学习环境非常复杂尼恩搞这个 前前后后起码折腾了一周 其中很多头疼的工作包括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 [rootlocalhost ~]# 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-shim3、将解压出来的docker文件内容移动到 /usr/bin/ 目录下 cp docker/* /usr/bin/ 4、将docker注册为service cat /etc/systemd/system/docker.service vi /etc/systemd/system/docker.service [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service Wantsnetwork-online.target[Service] Typenotify # 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.LimitNOFILEinfinity LimitNPROCinfinity LimitCOREinfinity# Uncomment TasksMax if your systemd version supports it. # Only systemd 226 and above support this version. #TasksMaxinfinity TimeoutStartSec0# set delegate yes so that systemd does not reset the cgroups of docker containersDelegateyes# kill only the docker process, not all processes in the cgroupKillModeprocess# restart the docker process if it exits prematurelyRestarton-failure StartLimitBurst3 StartLimitInterval60s[Install] WantedBymulti-user.target5、启动 chmod x /etc/systemd/system/docker.service #添加文件权限并启动dockersystemctl daemon-reload #重载unit配置文件systemctl start docker #启动Dockersystemctl enable docker.service #设置开机自启[rootlocalhost ~]# vi /etc/systemd/system/docker.service [rootlocalhost ~]# chmod x /etc/systemd/system/docker.service [rootlocalhost ~]# systemctl daemon-reload [rootlocalhost ~]# systemctl start docker [rootlocalhost ~]# 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[rootlocalhost ~]# systemctl status dockerdocker.service - Docker Application Container EngineLoaded: loaded (/etc/systemd/system/docker.service; enabled; vendor preset: disabled)Active: active (running) since Sat 2021-10-09 15:25:44 CST; 29s agoDocs: https://docs.docker.comMain PID: 1916 (dockerd)CGroup: /system.slice/docker.service├─1916 /usr/bin/dockerd└─1927 containerd --config /var/run/docker/containerd/containerd.toml --log-level infoOct 09 15:25:43 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:43.67140799608:00 levelinfo msgscheme \unix\ not r...egrpc Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:43.67144036808:00 levelinfo msgccResolverWrapper: se...egrpc Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:43.67146293508:00 levelinfo msgClientConn switching ...egrpc Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:43.75068778108:00 levelinfo msgLoading containers: start. Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:44.07296086208:00 levelinfo msgDefault bridge (docke...dress Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:44.15344407108:00 levelinfo msgLoading containers: done. Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:44.17524929908:00 levelinfo msgDocker daemon commit...9.03.6 Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time2021-10-09T15:25:44.17533783408:00 levelinfo msgDaemon 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]: time2021-10-09T15:25:44.19508410608:00 levelinfo msgAPI listen on /var/ru....sock Hint: Some lines were ellipsized, use -l to show in full. [rootlocalhost ~]# docker -v Docker version 19.03.6, build 369ce74a3c [rootlocalhost ~]# 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 后如果出现下面的内容则代表安装成功 [rootVM-24-9-centos ~]# docker -v Docker version 20.10.23, build 7155243Docker镜像加速国内使用 # 需要确定/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是否可以使用 [rootVM-24-9-centos ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES方式三 用现成的 大大的省事 dockerK8S 是一整套技术体系。 但是dockerK8S学习环境非常复杂尼恩搞这个 前前后后起码折腾了一周。 其中很多头疼的工作包括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结果如下 [rootVM-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如何查找容器名称? 很多的命令用到容器名称 如何查找容器名称可以使用下面的命令 [rootlocalhost ~]# 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.jsonEOF{registry-mirrors:[http://10.24.2.30:5000,https://tnxkcso1.mirrors.aliyuncs.com],insecure-registries:[10.24.2.30:5000] }EOFDocker容器进入的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 addresspingtelnetsstcpdump等等命令这就给调试容器网络带来相当大的困扰只能通过docker inspect ContainerID命令获取到容器IP以及无法测试和其他网络的连通性。这时就可以使用nsenter命令仅进入该容器的网络命名空间使用宿主机的命令调试容器网络。 在了解了什么是nsenter之后系统默认将我们需要的nsenter安装到主机中 nsenter --help 查看帮助 nsenter --help 查看帮助 $ nsenter --helpnsenter [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/bin3、nsenter的 使用 nsenter可以访问另一个进程的名称空间。 所以为了连接到某个容器我们还需要获取该容器的第一个进程的PID。 怎么办呢 可以使用docker inspect命令来拿到该 进程的 PID。 docker inspect 命令使用如下 sudo docker inspect --helpinspect命令可以分层级显示一个镜像或容器的信息。 比如我们当前有一个正在运行的容器 可以使用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 autoCreateTopicEnabletrue root 26491 24084 0 16:16 pts/1 00:00:00 grep --colorauto 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下。 站在这些进程的角度看它们会以为自己跑在一个独立的机器上看不到其他进程也看不到其他文件。 这是其实是操作系统为它虚拟出来的一个独立的、隔离的环境是假的。 查看子进程信息 [rootVM-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:tagdocker image pull rancher/rke-tools:v0.1.52[rancher/rke-tools:v0.1.52保存镜像 docker save 镜像id -o /home/mysql.tardocker save 镜像id /home/mysql.tar docker save docker.io/rancher/rancher-agent -o /home/rancher-agent .tardocker save f29ece87a195 -o /home/rancher-agent.tardocker 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.tardocker load -i /usr/local/rancher-agent.tardocker inspect f29ece87a1954772accb8a2332ee8c3fe460697e3f102ffbdc76eb9bc4f4f1d0docker load -i /usr/local/rke-tools-v0.1.52.tardocker load -i mysql.tar[rootlocalhost ~]# 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.5docker tag f29ece87a195 172.18.8.104/rancher/rancher-agent:v2.3.5docker tag 6e421b8753a2 172.18.8.104/rancher/rke-tools:v0.1.52 83fe4871cf67docker rmi image_namedocker 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-rancher1docker rmi hub.doge.net/ubuntu:latest保存镜像 docker export 镜像id -o /home/mysql-export.tardocker save 镜像tag -o /home/mysql-export.tar 载入镜像 docker import mysql-export.tar Harbor私有镜像仓库 Harbor 港口港湾是一个用于存储和分发Docker镜像的企业级Registry服务器。 除了Harbor这个私有镜像仓库之外还有Docker官方提供的Registry。 相对RegistryHarbor具有很多优势 提供分层传输机制优化网络传输 Docker镜像是是分层的而如果每次传输都使用全量文件(所以用FTP的方式并不适合)显然不经济。必须提供识别分层传输的机制以层的UUID为标识确定传输的对象。提供WEB界面优化用户体验 只用镜像的名字来进行上传下载显然很不方便需要有一个用户界面可以支持登陆、搜索功能包括区分公有、私有镜像。支持水平扩展集群 当有用户对镜像的上传下载操作集中在某服务器需要对相应的访问压力作分解。良好的安全机制 企业中的开发团队有很多不同的职位对于不同的职位人员分配不同的权限具有更好的安全性。 Harbor安装 harbor是用过docker-compose 编排的。 所以 安装的过程中会检查docker、docker-compose 进程确保提前启动。 这些在咱们的虚拟机里边docker、docker-compose 进程已经预装好了的。 查看docker是否安装成功 [rootcentos1 ~]# docker -v Docker version 20.10.23, build 7155243查看docker-compose是否安装成功 docker-compose -version[rootcentos1 ~]# docker-compose -version docker-compose version 1.25.1, build a82fef07 [rootcentos1 ~]#接下来开始安装harbor 1、下载 Harbor的压缩包 https://github.com/goharbor/harbor/releases咱们用这个包 harbor-offline-installer-v2.3.2.tgz 咱们的学习网盘当中尼恩已经上传了哈 2、上传压缩包到虚拟机并解压 [rootcentos1 ~]# cd /usr/local/ [rootcentos1 local]# mkdir harber [rootcentos1 local]# cd harber/tar -zxvf harbor-offline-installer-v2.3.2.tgz3、创建harbor访问域名证书 OpenSSL是一个强大的安全套接字层密码库Apache使用它加密HTTPSOpenSSH使用它加密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 4096openssl req -x509 -new -nodes -sha512 -days 3650 \-subj /CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNcdh1 \-key tls.key \-out tls.cert第一步创建ssl文件夹用来存储证书第二步生成key (私钥)最后一步使用生成的key自签证书。自签证书包含公钥 days后面是你自签证书的有效时间可以自行修改。 CN后面就写你自己的IP地址或者你自己的域名。 生成完成之后显示如下 4、配置harbor 修改Harbor的配置harbor.yml 修改主机地址hostname: 192.168.56.121 修改端口默认端口80port: 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服务不然会报错 这个过程可能有点长需要耐心等待 /prepare6、./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) [rootcentos1 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] }[rootcentos1 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.crtmkdir -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 :: GeneralNamesGeneralNames :: SEQUENCE SIZE (1..MAX) OF GeneralNameGeneralName :: 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命令行来生成KEYCSR2个文件使用KEYTOOL来生成JKS和CSR文件 1、生成CA证书私钥 openssl genrsa -out ca.key 4096 [rootdocker-compose-harbor CA]# openssl genrsa -out ca.key 4096 Generating RSA private key, 4096 bit long modulus ............................................................... ............................................................ e is 65537 (0x10001) [rootdocker-compose-harbor CA]# [rootdocker-compose-harbor CA]# [rootdocker-compose-harbor CA]# ls ca.key [rootdocker-compose-harbor CA]#2、生成CA证书 openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj /CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNcdh1 \ -key ca.key \ -out ca.crt自签名的证书不被浏览器信任适合内部或者测试使用。 3、生成服务器证书 生成私钥 [rootdocker-compose-harbor CA]# openssl genrsa -out cdh1.key 4096 Generating RSA private key, 4096 bit long modulus ......................................................................................................................... ........................................................................................................................................................................................................... e is 65537 (0x10001) [rootdocker-compose-harbor CA]# [rootdocker-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 [rootdocker-compose-harbor CA]#生成证书签名请求CSR 制作过程中系统会产生2个密钥一个是私钥存放在服务器上,一个是CSR文件公钥需要ca签名。 openssl req -sha512 -new \ -subj /CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNcdh1 \ -key cdh1.key \ -out cdh1.csr需要依次输入国家地区城市组织组织单位Common Name和Email。 其中Common Name可以写自己的名字或者域名如果要支持httpsCommon Name应该与域名保持一致否则会引起浏览器警告。 可以将证书发送给证书颁发机构CACA验证过请求者的身份之后会出具签名证书需要花钱。 另外如果只是内部或者测试需求也可以使用OpenSSL实现自签名。 执行过程 [rootdocker-compose-harbor CA]# openssl req -sha512 -new \-subj /CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNcdh1 \-key cdh1.key \-out cdh1.csr[rootdocker-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 [rootdocker-compose-harbor CA]# 生成一个x509 v3扩展文件 cat v3.ext -EOF authorityKeyIdentifierkeyid,issuer basicConstraintsCA:FALSE keyUsage digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage serverAuth subjectAltName IP:cdh1 EOF4、使用该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[rootdocker-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/CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNcdh1 Getting CA Private Key [rootdocker-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 [rootdocker-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 [rootdocker-compose-harbor harbor]#6、运行prepare脚本以启用HTTPS ./prepare [rootdocker-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.confClean up the input dir [rootdocker-compose-harbor harbor]#7、运行install.sh脚本来启动harbor ./install.sh [rootdocker-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 ... Not证书复制到 docker 并且启动后登录 cp /usr/local/harbor/ssl/cdh1.cert /etc/docker/certs.d/cdh1/ca.crtsystemctl daemon-reload systemctl restart dockerhostname push失败 问题 push本地镜像到 harbor私服时push 到 docker.io仓库去了 原因 在配置insecure-registry时docker 必须配置服务器的 FQDN或者IP地址.不能是服务器的hostname比如harbor 尼恩配置的 是cdh1推不上去。 FQDN是什么意思 FQDNfully qualified domain name完全限定域名是互联网上特定计算机或主机的完整域名。 FQDN 由两部分组成主机名和域名。 例如假设邮件服务器的 FQDN 可能是 mail.chenweiliang.com 。 主机名为mail主机位于域名chenweiliang.com。 DNSDomain Name System负责将 FQDN 转换为 IP地址是 Internet 上大多数应用程序的寻址方式。 FQDNFully 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错误提升 [rootcentos1 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 /CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNharbor.daemon.io \ -key ca.key \ -out ca.crtopenssl genrsa -out harbor.daemon.io.key 4096openssl req -sha512 -new \ -subj /CCN/STBeijing/LBeijing/Oexample/OUPersonal/CNharbor.daemon.io \ -key harbor.daemon.io.key \ -out harbor.daemon.io.csr生成一个x509 v3扩展文件 cat harbor.daemon.io.v3.ext -EOF authorityKeyIdentifierkeyid,issuer basicConstraintsCA:FALSE keyUsage digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage serverAuth subjectAltName DNS:harbor.daemon.io EOFopenssl 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.crtopenssl x509 -inform PEM -in harbor.daemon.io.crt -out harbor.daemon.io.certharbor.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.iocp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/ca.crtcp /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.crtdocker-compose downpreparesystemctl restart docker docker-compose up -ddocker 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可以共享相同的layerImage本身是read-only的 Image的获取 方式1Build from Dockerfile方式2Pull from Registry 例如输入命令 sudo docker pull ubuntu:14.04则会从Registry中拉出ImageRegistry类似于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 commitCreate a new image from a container’s changes 这个命令表示当你在容器中做出了改变之后可以重新构建Image 通过这个例子可以看出其就在centos镜像上重新构建了一层 命令二docker image build(Build an image from a Dockerfile) Dockerfile语法 FROM 原则尽量使用官方的image作为base image FROM scratch #制作base image FROM centos #使用base image FROM ubuntu14.04LABEL 原则Metadata不可少相当于代码的注释 LABEL maintaineryunduangmail.com LABEL version1.0 LABEL descriptionThis is descriptionRUN 作用执行命令并创建新的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 $HOMEWORKDIR 作用设定当前目录类似于cd 原则用WORKDIR不要用RUN cd尽量使用绝对目录 WORKDIR /root WORKDIR /test #如果没有会自动创建test目录 WORKDIR demo RUN pwd #输出结果应该是 /test/demoADD and COPY 作用把本地文件添加到Docker image里面 原则大部分情况COPY优先于ADDADD除了COPY还有额外功能解压添加远程文件/目录请使用curl或者wget ADD hello / ADD test.tar.gz / #添加到根目录并解压 WORKDIR /root ADD hello test/ # /root/test/hello WORKDIR /root COPY hello test/ENV 作用设置一个环境变量引用常量 原则尽量使用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 docker2.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 $nameDockerfile2 FROM centos ENV name Dokcer ENTRYPOINT [/bin/bash, -c,echo hello $name ]镜像发布 docker logindocker push Docker进程与宿主机进程的对应关系 Linux通过进程ID查看文件路径 子进程的文件路径 [rootVM-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输出 [rootVM-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输出 [rootVM-4-17-centos ~]# docker inspect 460d68823930 [rootVM-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: [0,0],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/ [rootVM-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 [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs 4962 11539 [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat pids.max max [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat tasks 4962 11539 [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.clone_children 0 [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# pwd /sys/fs/cgroup/pids/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd查看容器目录里的进程号 进程号就存在一个文件里面 [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs 4962与前面利用docker top命令可以让我们从宿主机操作系统中看到容器的进程信息。 [rootVM-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进程 [rootVM-4-17-centos ]# docker exec -d centos6-2 sleep 2000 [rootVM-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查看宿主机的进程号 [rootVM-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 容器的启动进程。 查看容器目录里的进程号 进程号就存在一个文件里面 [rootVM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs 4962 11539docker exec -d centos6-2 pstree -pdocker exec -d centos6-2 ps -auxfdocker exec -d centos6-2 ll /proc输出 [rootVM-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 [rootVM-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 [rootVM-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进程 dockerdefault:~$ docker exec -d redis sleep 2000 dockerdefault:~$ 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 dockerdefault:~$ 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容器会自动结束而容器名空间中所有进程也会退出。 dockerdefault:~$ PID$(docker inspect --format{{.State.Pid}} redis) dockerdefault:~$ sudo kill $PID dockerdefault:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 356eca186321 redis /entrypoint.sh redis 23 minutes ago Up 4 minutes 6379/tcp redis2 f6bc57cc1b46 redis /entrypoint.sh redis 23 minutes ago Exited (0) 4 seconds ago redis通过以上示例 每个容器有独立的PID名空间容器的生命周期和其PID1进程一致利用docker exec可以进入到容器的名空间中启动进程 Docker文件目录和容器内部操作 Docker默认的文件目录位于Linux server的/var/lib/docker 下面。目录结构如下 |-----containers用于存储容器信息 |-----image用来存储镜像中间件及本身信息大小依赖信息 |-----network |-----swarm |-----tmpdocker临时目录 |-----trustdocker信任目录 |-----volumesdocker卷目录还可以通过docker指令确认文件位置 docker info查看某个容器的文件目录 docker exec 容器name lsdocker exec centos6-2 ls /proc[rootVM-4-17-centos containers]# docker exec centos6-2 ls /proc 1 103 acpi buddyinfo bus cgroups cmdline consoles cpuinfo crypto devices diskstats dma driver execdomains fb filesystems fs interrupts iomem ioports irq kallsyms kcore key-users keys kmsg kpagecount kpageflags loadavg locks mdstat meminfo misc modules mounts mtrr net pagetypeinfo partitions sched_debug schedstat scsi self slabinfo softirqs stat swaps sys sysrq-trigger sysvipc timer_list timer_stats tty uptime version vmallocinfo vmstat zoneinfoDocker Daemon 底层原理 作为Docker容器管理的守护进程Docker Daemon从最初集成在docker命令中1.11版本前 到后来的独立成单独二进制程序1.11版本开始其功能正在逐渐拆分细化被分配到各个单独的模块中去。 演进Docker守护进程启动 从Docker服务的启动脚本也能看见守护进程的逐渐剥离 在Docker 1.8之前Docker守护进程启动的命令为 docker -d这个阶段守护进程看上去只是Docker client的一个选项。 Docker 1.8开始启动命令变成了 docker daemon这个阶段守护进程看上去是docker命令的一个模块。 Docker 1.11开始守护进程启动命令变成了 dockerd其服务的配置文件为 [Service] Typenotify # 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此时已经和Docker client分离独立成一个二进制程序了。 当然守护进程模块不停的在重构其基本功能和定位没有变化。和一般的CS架构系统一样守护进程负责和Docker client交互并管理Docker镜像、容器。 OCIOpen Container Initiative Open Container Initiative也就是常说的OCI是由多家公司共同成立的项目并由linux基金会进行管理致力于container runtime的标准的制定和runc的开发等工作。 官方的介绍是 An open governance structure for the express purpose of creating open industry standards around container formats and runtime. – Open Containers Official Site 所谓container runtime主要负责的是容器的生命周期的管理。oci的runtime spec标准中对于容器的状态描述以及对于容器的创建、删除、查看等操作进行了定义。 目前主要有两个标准文档容器运行时标准 runtime spec和 容器镜像标准image spec。 这两个协议通过 OCI runtime filesytem bundle 的标准格式连接在一起OCI 镜像可以通过工具转换成 bundle然后 OCI 容器引擎能够识别这个 bundle 来运行容器。 image spec OCI 容器镜像主要包括几块内容 文件系统以 layer 保存的文件系统每个 layer 保存了和上层之间变化的部分layer 应该保存哪些文件怎么表示增加、修改和删除的文件等 config 文件保存了文件系统的层级信息每个层级的 hash 值以及历史信息以及容器运行时需要的一些信息比如环境变量、工作目录、命令参数、mount 列表指定了镜像在某个特定平台和系统的配置。比较接近我们使用 docker inspect image_id 看到的内容 manifest 文件镜像的 config 文件索引有哪些 layer额外的 annotation 信息manifest 文件中保存了很多和当前平台有关的信息 index 文件可选的文件指向不同平台的 manifest 文件这个文件能保证一个镜像可以跨平台使用每个平台拥有不同的 manifest 文件使用 index 作为索引 runtime spec OCI 对容器 runtime 的标准主要是指定容器的运行状态和 runtime 需要提供的命令。下图可以是容器状态转换图 Docker CLI客户端工具 /usr/bin/dockerDocker 的客户端工具通过CLI与 dockerd API 交流。 CLI 的例子比如docker build … docker run … Docker Daemon守护进程 dockerd /usr/bin/dockerd当然守护进程模块不停的在重构其基本功能和定位没有变化。 和一般的CS架构系统一样守护进程负责和Docker client交互并管理Docker镜像、容器。 Containerd /usr/bin/docker-containerdcontainerd是容器技术标准化之后的产物为了能够兼容OCI标准将容器运行时及其管理功能从Docker Daemon剥离。 理论上即使不运行dockerd也能够直接通过containerd来管理容器。 当然containerd本身也只是一个守护进程容器的实际运行时由后面介绍的runC控制。 最近Docker刚刚宣布开源containerd。从其项目介绍页面可以看出containerd主要职责是镜像管理镜像、元信息等、容器执行调用最终运行时组件执行。 containerd向上为Docker Daemon提供了gRPC接口使得Docker Daemon屏蔽下面的结构变化确保原有接口向下兼容。向下通过containerd-shim结合runC使得引擎可以独立升级避免之前Docker Daemon升级会导致所有容器不可用的问题。 containerd fully leverages the **OCI runtime specification1, image format specifications and OCI reference implementation (runc). containerd includes a daemon exposing gRPC API over a local UNIX socket. The API is a low-level one designed for higher layers to wrap and extend. Containerd uses RunC to run containers according to the OCI specification. docker-shim 容器进程 docker-shim是一个真实运行的容器的真实垫片载体每启动一个容器都会起一个新的docker-shim的一个进程 他直接通过指定的三个参数容器idboundle目录containerd的对应某个容器生成的目录一般位于/var/run/docker/libcontainerd/containerID 运行是二进制默认为runc来调用runc的api创建一个容器比如创建容器最后拼装的命令如下runc create 。。。。。 /usr/bin/docker-containerd-shim每启动一个容器都会起一个新的docker-shim的一个进程. 他直接通过指定的三个参数来创建一个容器 容器idboundle目录containerd的对应某个容器生成的目录一般位于/var/run/docker/libcontainerd/containerID运行是二进制默认为runc来调用runc的api比如创建容器时最后拼装的命令如下runc create 。。。 他的作用是 它允许容器运行时(即 runC)在启动容器之后退出简单说就是不必为每个容器一直运行一个容器运行时(runC)即使在 containerd 和 dockerd 都挂掉的情况下容器的标准 IO 和其它的文件描述符也都是可用的向 containerd 报告容器的退出状态 前两点尤其重要有了它们就可以在不中断容器运行的情况下升级或重启 dockerd(这对于生产环境来说意义重大)。 runc (OCI reference implementation) OCI定义了容器运行时标准runC是Docker按照开放容器格式标准OCF, Open Container Format制定的一种具体实现。 runC是从Docker的libcontainer中迁移而来的实现了容器启停、资源隔离等功能。 Docker默认提供了docker-runc实现事实上通过containerd的封装可以在Docker Daemon启动的时候指定runc的实现。 /usr/bin/docker-runc OCI定义了容器运行时标准OCI Runtime Spec support (aka runC) runC是Docker按照开放容器格式标准OCF, Open Container Format制定的一种具体实现。 runC是从Docker的libcontainer中迁移而来的实现了容器启停、资源隔离等功能。 Docker默认提供了docker-runc实现事实上通过containerd的封装可以在Docker Daemon启动的时候指定runc的实现。 我们可以通过启动Docker Daemon时增加–add-runtime参数来选择其他的runC现。例如 docker daemon --add-runtime custom/usr/local/bin/my-runc-replacementDocker、containerd, containerd-shim和runc之间的关系 他们之间的关系如下图 我们可以通过启动一个Docker容器来观察进程之间的关联。 通过runc来启动一个container的过程 查看进程信息 利用docker top命令可以让我们从宿主机操作系统中看到容器的进程信息。 尼恩提示这里比较复杂具体的视频介绍请参见稍后的 穿透云原生视频进行介绍。 查看父进程信息 [rootVM-4-17-centos containerd]# ps -ef | grep 3401尼恩提示这里比较复杂具体的视频介绍请参见稍后的 穿透云原生视频进行介绍。 查看进程树 pstree -l -a -A 3401 -p虽然pstree命令截断了命令但我们还是能够看出当Docker daemon启动之后dockerd和docker-containerd进程一直存在。 当启动容器之后docker-containerd进程也是这里介绍的containerd组件会创建docker-containerd-shim进程其中的参数e9eaef999da9183b9be0b3239881bc6b9c2070f13057c322dfed3d072820e962就是要启动容器的id。 最后docker-containerd-shim子进程已经是实际在容器中运行的进程。 docker-containerd-shim另一个参数是一个和容器相关的目录 /var/run/docker/containerd/e9eaef999da9183b9be0b3239881bc6b9c2070f13057c322dfed3d072820e962其中包括了容器配置和标准输入、标准输出、标准错误三个管道文件。 使用下面的 ll /var/run/docker/containerd/e9eaef999da9183b9be0b3239881bc6b9c2070f13057c322dfed3d072820e962尼恩提示这里比较复杂具体的视频介绍请参见稍后的 穿透云原生视频进行介绍。 CRI 运行时接口 kubernetes在初期版本里就对多个容器引擎做了兼容因此可以使用docker、rkt对容器进行管理。 以docker为例kubelet中会启动一个docker manager通过直接调用docker的api进行容器的创建等操作。 在k8s 1.5版本之后kubernetes推出了自己的运行时接口api–CRI(container runtime interface)。 cri接口的推出隔离了各个容器引擎之间的差异而通过统一的接口与各个容器引擎之间进行互动。 与oci不同cri与kubernetes的概念更加贴合并紧密绑定。 cri不仅定义了容器的生命周期的管理还引入了k8s中pod的概念并定义了管理pod的生命周期。 在kubernetes中pod是由一组进行了资源限制的在隔离环境中的容器组成。 而这个隔离环境称之为PodSandbox。 在cri开始之初主要是支持docker和rkt两种。 其中kubelet是通过cri接口调用docker-shim并进一步调用docker api实现的。 如上文所述docker独立出来了containerd。 kubernetes也顺应潮流孵化了cri-containerd项目用以将containerd接入到cri的标准中。 为了进一步与oci进行兼容kubernetes还孵化了cri-o成为了架设在cri和oci之间的一座桥梁。 通过这种方式可以方便更多符合oci标准的容器运行时接入kubernetes进行集成使用。 可以预见到通过cri-okubernetes在使用的兼容性和广泛性上将会得到进一步加强。 Docker的技术底座 Linux 命名空间、控制组和 UnionFS 三大技术支撑了目前 Docker 的实现也是 Docker 能够出现的最重要原因。 底层技术支持 其主要利用Linux的底层技术 Namespaces做隔离pidnetipcmntutsControl groups做资源限制Union file systemsContainer和image的分层 namespace命名空间 命名空间容器隔离的基础保证A容器看不到B容器. 6个命名空间UserMntNetworkUTSIPCPid cgroupsCgroups 是 Control Group 的缩写控制组 cgroups 容器资源统计和隔离 主要用到的cgroups子系统cpublkiodevicefreezermemory 实际上 Docker 是使用了很多 Linux 的隔离功能让容器看起来像一个轻量级虚拟机在独立运行容器的本质是被限制了的 Namespacescgroup具有逻辑上独立文件系统网络的一个进程。 unionfs 联合文件系统 典型aufs/overlayfs分层镜像实现的基础 UnionFS 联合文件系统 什么是镜像 那么问题来了没有操作系统怎么运行程序 可以在Docker中创建一个centos的镜像文件这样就能将centos系统集成到Docker中运行的应用就都是centos的应用。 Image 是 Docker 部署的基本单位一个 Image 包含了我们的程序文件以及这个程序依赖的资源的环境。Docker Image 对外是以一个文件的形式展示的更准确的说是一个 mount 点。 UnionFS 与AUFS UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版它能够提供更优秀的性能和效率。 AUFS 作为先进联合文件系统它能够将不同文件夹中的层联合Union到了同一个文件夹中这些文件夹在 AUFS 中称作分支整个『联合』的过程被称为联合挂载Union Mount。 概念理解起来比较枯燥最好是有一个真实的例子来帮助我们理解 首先我们建立 company 和 home 两个目录并且分别为他们创造两个文件 $ tree . . |-- company | |-- code | -- meeting -- home|-- eat-- sleep然后我们将通过 mount 命令把 company 和 home 两个目录「联合」起来建立一个 AUFS 的文件系统并挂载到当前目录下的 mnt 目录下 $ mkdir mnt $ ll total 20 drwxr-xr-x 5 root root 4096 Oct 25 16:10 ./ drwxr-xr-x 5 root root 4096 Oct 25 16:06 ../ drwxr-xr-x 4 root root 4096 Oct 25 16:06 company/ drwxr-xr-x 4 root root 4096 Oct 25 16:05 home/ drwxr-xr-x 2 root root 4096 Oct 25 16:10 mnt/# mount -t aufs -o dirs./home:./company none ./mnt # ll total 20 drwxr-xr-x 5 root root 4096 Oct 25 16:10 ./ drwxr-xr-x 5 root root 4096 Oct 25 16:06 ../ drwxr-xr-x 4 root root 4096 Oct 25 16:06 company/ drwxr-xr-x 6 root root 4096 Oct 25 16:10 home/ drwxr-xr-x 8 root root 4096 Oct 25 16:10 mnt/ rootrds-k8s-18-svr0:~/xuran/aufs# tree ./mnt/ ./mnt/ |-- code |-- eat |-- meeting -- sleep4 directories, 0 files通过 ./mnt 目录结构的输出结果可以看到原来两个目录下的内容都被合并到了一个 mnt 的目录下。 默认情况下如果我们不对「联合」的目录指定权限内核将根据从左至右的顺序将第一个目录指定为可读可写的其余的都为只读。那么当我们向只读的目录做一些写入操作的话会发生什么呢 $ echo apple ./mnt/code $ cat company/code $ cat home/code apple通过对上面代码段的观察我们可以看出当写入操作发生在 company/code 文件时 对应的修改并没有反映到原始的目录中。 而是在 home 目录下又创建了一个名为 code 的文件并将 apple 写入了进去。 看起来很奇怪的现象其实这正是 Union File System 的厉害之处 Union File System 联合了多个不同的目录并且把他们挂载到一个统一的目录上。 在这些「联合」的子目录中 有一部分是可读可写的但是有一部分只是可读的。 当你对可读的目录内容做出修改的时候其结果只会保存到可写的目录下不会影响只读的目录。 比如我们可以把我们的服务的源代码目录和一个存放代码修改记录的目录「联合」起来构成一个 AUFS。前者设置只读权限后者设置读写权限。 那么一切对源代码目录下文件的修改都只会影响那个存放修改的目录不会污染原始的代码。 在 AUFS 中还有一个特殊的概念需要提及一下 branch – 就是各个要被union起来的目录。 Stack 结构 - AUFS 它会根据branch 被 Union 的顺序形成一个 Stack 的结构从下至上最上面的目录是可读写的其余都是可读的。如果按照我们刚刚执行 aufs 挂载的命令来说最左侧的目录就对应 Stack 最顶层的 branch。 所以下面的命令中最为左侧的为 home而不是 company mount -t aufs -o dirs./home:./company none ./mnt什么是 Docker 镜像分层机制 首先让我们来看下 Docker Image 中的 Layer 的概念 Docker Image 是有一个层级结构的最底层的 Layer 为 BaseImage一般为一个操作系统的 ISO 镜像然后顺序执行每一条指令生成的 Layer 按照入栈的顺序逐渐累加最终形成一个 Image。 直观的角度来说是这个图所示 每一次都是一个被联合的目录从目录的角度来说大致如下图所示 Docker Image 如何而来呢 简单来说一个 Image 是通过一个 DockerFile 定义的然后使用 docker build 命令构建它。 DockerFile 中的每一条命令的执行结果都会成为 Image 中的一个 Layer。 这里我们通过 Build 一个镜像来观察 Image 的分层机制 Dockerfile: # Use an official Python runtime as a parent image FROM python:2.7-slim# Set the working directory to /app WORKDIR /app# Copy the current directory contents into the container at /app COPY . /app# Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt# Make port 80 available to the world outside this container EXPOSE 80# Define environment variable ENV NAME World# Run app.py when the container launches CMD [python, app.py]构建结果 rootrds-k8s-18-svr0:~/xuran/exampleimage# docker build -t hello ./ Sending build context to Docker daemon 5.12 kB Step 1/7 : FROM python:2.7-slim--- 804b0a01ea83 Step 2/7 : WORKDIR /app--- Using cache--- 6d93c5b91703 Step 3/7 : COPY . /app--- Using cache--- feddc82d321b Step 4/7 : RUN pip install --trusted-host pypi.python.org -r requirements.txt--- Using cache--- 94695df5e14d Step 5/7 : EXPOSE 81--- Using cache--- 43c392d51dff Step 6/7 : ENV NAME World--- Using cache--- 78c9a60237c8 Step 7/7 : CMD python app.py--- Using cache--- a5ccd4e1b15d Successfully built a5ccd4e1b15d通过构建结果可以看出构建的过程就是执行 Dockerfile 文件中我们写入的命令。构建一共进行了7个步骤每个步骤进行完都会生成一个随机的 ID来标识这一 layer 中的内容。 最后一行的 a5ccd4e1b15d 为镜像的 ID。由于我贴上来的构建过程已经是构建了第二次的结果了所以可以看出对于没有任何修改的内容Docker 会复用之前的结果。 如果 DockerFile 中的内容没有变动那么相应的镜像在 build 的时候会复用之前的 layer以便提升构建效率。并且即使文件内容有修改那也只会重新 build 修改的 layer其他未修改的也仍然会复用。 通过了解了 Docker Image 的分层机制我们多多少少能够感觉到Layer 和 Image 的关系与 AUFS 中的联合目录和挂载点的关系比较相似。 而 Docker 也正是通过 AUFS 来管理 Images 的。 Namespaces 在Linux系统中Namespace是在内核级别以一种抽象的形式来封装系统资源通过将系统资源放在不同的Namespace中来实现资源隔离的目的。不同的Namespace程序可以享有一份独立的系统资源。 命名空间namespaces是 Linux 为我们提供的用于分离进程树、网络接口、挂载点以及进程间通信等资源的方法。在日常使用 Linux 或者 macOS 时我们并没有运行多个完全分离的服务器的需要但是如果我们在服务器上启动了多个服务这些服务其实会相互影响的每一个服务都能看到其他服务的进程也可以访问宿主机器上的任意文件这是很多时候我们都不愿意看到的我们更希望运行在同一台机器上的不同服务能做到完全隔离就像运行在多台不同的机器上一样。 Linux 的命名空间机制提供了以下七种不同的命名空间包括 : CLONE_NEWCGROUPCLONE_NEWIPCCLONE_NEWNETCLONE_NEWNSCLONE_NEWPIDCLONE_NEWUSERCLONE_NEWUTS 通过这七个选项, 我们能在创建新的进程时, 设置新进程应该在哪些资源上与宿主机器进行隔离。具体如下 NamespaceFlagPageIsolatesCgroupCLONE_NEWCGROUPcgroup_namespacesCgroup root directoryIPCCLONE_NEWIPCipc_namespacesSystem V IPC,POSIX message queues 隔离进程间通信NetworkCLONE_NEWNETnetwork_namespacesNetwork devices,stacks, ports, etc. 隔离网络资源MountCLONE_NEWNSmount_namespacesMount points 隔离文件系统挂载点PIDCLONE_NEWPIDpid_namespacesProcess IDs 隔离进程的IDTimeCLONE_NEWTIMEtime_namespacesBoot and monotonic clocksUserCLONE_NEWUSERuser_namespacesUser and group IDs 隔离用户和用户组的IDUTSCLONE_NEWUTSuts_namespacesHostname and NIS domain name 隔离主机名和域名信息 这里提出一个问题在宿主机上启动两个容器在这两个容器内都各有一个 PID1的进程众所周知Linux 里 PID 是唯一的既然 Docker 不是跑在宿主机上的两个虚拟机那么它是如何实现在宿主机上运行两个相同 PID 的进程呢 这里就用到了 Linux Namespaces它其实是 Linux 创建新进程时的一个可选参数在 Linux 系统中创建进程的系统调用是 clone()方法。 int clone(int (*fn) (void *)void *child stack,int flags void *arg . . ./* pid_ t *ptid, void *newtls, pid_ t *ctid */ ) ;通过调用这个方法这个进程会获得一个独立的进程空间它的 pid 是1并且看不到宿主机上的其他进程这也就是在容器内执行 PS 命令的结果。 不仅仅是 PID当你启动启动容器之后Docker 会为这个容器创建一系列其他 namespaces。 这些 namespaces 提供了不同层面的隔离。容器的运行受到各个层面 namespace 的限制。 Docker Engine 使用了以下 Linux 的隔离技术: The pid namespace: 管理 PID 命名空间 (PID: Process ID). The net namespace: 管理网络命名空间(NET: Networking). The ipc namespace: 管理进程间通信命名空间(IPC: InterProcess Communication). The mnt namespace: 管理文件系统挂载点命名空间 (MNT: Mount). The uts namespace: Unix 时间系统隔离. (UTS: Unix Timesharing System). 通过这些技术运行时的容器得以看到一个和宿主机上其他容器隔离的环境。 进程隔离 进程是 Linux 以及现在操作系统中非常重要的概念它表示一个正在执行的程序也是在现代分时系统中的一个任务单元。 在每一个 *nix 的操作系统上我们都能够通过 ps 命令打印出当前操作系统中正在执行的进程比如在 Ubuntu 上使用该命令就能得到以下的结果 |$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 Apr08 ? 00:00:09 /sbin/init root 2 0 0 Apr08 ? 00:00:00 [kthreadd] root 3 2 0 Apr08 ? 00:00:05 [ksoftirqd/0] root 5 2 0 Apr08 ? 00:00:00 [kworker/0:0H] root 7 2 0 Apr08 ? 00:07:10 [rcu_sched] root 39 2 0 Apr08 ? 00:00:00 [migration/0] root 40 2 0 Apr08 ? 00:01:54 [watchdog/0]当前机器上有很多的进程正在执行在上述进程中有两个非常特殊一个是 pid 为 1 的 /sbin/init 进程另一个是 pid 为 2 的 kthreadd 进程这两个进程都是被 Linux 中的上帝进程 idle 创建出来的其中前者负责执行内核的一部分初始化工作和系统配置也会创建一些类似 getty 的注册进程而后者负责管理和调度其他的内核进程。 如果我们在当前的 Linux 操作系统下运行一个新的 Docker 容器并通过 exec 进入其内部的 bash 并打印其中的全部进程我们会得到以下的结果 UID PID PPID C STIME TTY TIME CMD root 29407 1 0 Nov16 ? 00:08:38 /usr/bin/dockerd --raw-logs root 1554 29407 0 Nov19 ? 00:03:28 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc root 5006 1554 0 08:38 ? 00:00:00 docker-containerd-shim b809a2eb3630e64c581561b08ac46154878ff1c61c6519848b4a29d412215e79 /var/run/docker/libcontainerd/b809a2eb3630e64c581561b08ac46154878ff1c61c6519848b4a29d412215e79 docker-runc在新的容器内部执行 ps 命令打印出了非常干净的进程列表只有包含当前 ps -ef 在内的三个进程在宿主机器上的几十个进程都已经消失不见了。 当前的 Docker 容器成功将容器内的进程与宿主机器中的进程隔离如果我们在宿主机器上打印当前的全部进程时会得到下面三条与 Docker 相关的结果 在当前的宿主机器上可能就存在由上述的不同进程构成的进程树 这就是在使用 clone(2) 创建新进程时传入 CLONE_NEWPID 实现的也就是使用 Linux 的命名空间实现进程的隔离Docker 容器内部的任意进程都对宿主机器的进程一无所知。 containerRouter.postContainersStart └── daemon.ContainerStart └── daemon.createSpec└── setNamespaces└── setNamespaceDocker 的容器就是使用上述技术实现与宿主机器的进程隔离当我们每次运行 docker run 或者 docker start 时都会在下面的方法中创建一个用于设置进程间隔离的 Spec func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {s : oci.DefaultSpec()// ...if err : setNamespaces(daemon, s, c); err ! nil {return nil, fmt.Errorf(linux spec namespaces: %v, err)}return s, nil } 在 setNamespaces 方法中不仅会设置进程相关的命名空间还会设置与用户、网络、IPC 以及 UTS 相关的命名空间 func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error {// user// network// ipc// uts// pidif c.HostConfig.PidMode.IsContainer() {ns : specs.LinuxNamespace{Type: pid}pc, err : daemon.getPidContainer(c)if err ! nil {return err}ns.Path fmt.Sprintf(/proc/%d/ns/pid, pc.State.GetPID())setNamespace(s, ns)} else if c.HostConfig.PidMode.IsHost() {oci.RemoveNamespace(s, specs.LinuxNamespaceType(pid))} else {ns : specs.LinuxNamespace{Type: pid}setNamespace(s, ns)}return nil } 所有命名空间相关的设置 Spec 最后都会作为 Create 函数的入参在创建新的容器时进行设置 daemon.containerd.Create(context.Background(), container.ID, spec, createOptions)所有与命名空间的相关的设置都是在上述的两个函数中完成的Docker 通过命名空间成功完成了与宿主机进程和网络的隔。 网络隔离 如果 Docker 的容器通过 Linux 的命名空间完成了与宿主机进程的网络隔离但是却有没有办法通过宿主机的网络与整个互联网相连就会产生很多限制。 所以 Docker 虽然可以通过命名空间创建一个隔离的网络环境但是 Docker 中的服务仍然需要与外界相连才能发挥作用。 每一个使用 docker run 启动的容器其实都具有单独的网络命名空间Docker 为我们提供了四种不同的网络模式Host、Container、None 和 Bridge 模式。 在这一部分我们将介绍 Docker 默认的网络设置模式网桥模式。 在这种模式下除了分配隔离的网络命名空间之外Docker 还会为所有的容器设置 IP 地址。 当 Docker 服务器在主机上启动之后会创建新的虚拟网桥 docker0随后在该主机上启动的全部服务在默认情况下都与该网桥相连。 在默认情况下每一个容器在创建时都会创建一对虚拟网卡两个虚拟网卡组成了数据的通道其中一个会放在创建的容器中会加入到名为 docker0 网桥中。 我们可以使用如下的命令来查看当前网桥的接口 $ brctl show bridge name bridge id STP enabled interfaces docker0 8000.0242a6654980 no veth3e84d4fveth9953b75docker0会为每一个容器分配一个新的 IP 地址并将 docker0 的 IP 地址设置为默认的网关。 网桥 docker0 通过 iptables 中的配置与宿主机器上的网卡相连所有符合条件的请求都会通过 iptables 转发到 docker0 并由网桥分发给对应的机器。 $ iptables -t nat -L Chain PREROUTING (policy ACCEPT) target prot opt source destination DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCALChain DOCKER (2 references) target prot opt source destination RETURN all -- anywhere anywhere我们在当前的机器上使用 docker run -d -p 6379:6379 redis 命令启动了一个新的 Redis 容器在这之后我们再查看当前 iptables 的 NAT 配置就会看到在 DOCKER 的链中出现了一条新的规则 DNAT tcp -- anywhere anywhere tcp dpt:6379 to:192.168.0.4:6379上述规则会将从任意源发送到当前机器 6379 端口的 TCP 包转发到 192.168.0.4:6379 所在的地址上。 这个地址其实也是 Docker 为 Redis 服务分配的 IP 地址如果我们在当前机器上直接 ping 这个 IP 地址就会发现它是可以访问到的 $ ping 192.168.0.4 PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data. 64 bytes from 192.168.0.4: icmp_seq1 ttl64 time0.069 ms 64 bytes from 192.168.0.4: icmp_seq2 ttl64 time0.043 ms ^C --- 192.168.0.4 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev 0.043/0.056/0.069/0.013 ms从上述的一系列现象我们就可以推测出 Docker 是如何将容器的内部的端口暴露出来并对数据包进行转发的了当有 Docker 的容器需要将服务暴露给宿主机器就会为容器分配一个 IP 地址同时向 iptables 中追加一条新的规则。 当我们使用 redis-cli 在宿主机器的命令行中访问 127.0.0.1:6379 的地址时经过 iptables 的 NAT PREROUTING 将 ip 地址定向到了 192.168.0.4重定向过的数据包就可以通过 iptables 中的 FILTER 配置最终在 NAT POSTROUTING 阶段将 ip 地址伪装成 127.0.0.1到这里虽然从外面看起来我们请求的是 127.0.0.1:6379但是实际上请求的已经是 Docker 容器暴露出的端口了。 $ redis-cli -h 127.0.0.1 -p 6379 ping PONGDocker 通过 Linux 的命名空间实现了网络的隔离又通过 iptables 进行数据包转发让 Docker 容器能够优雅地为宿主机器或者其他容器提供服务。 Libnetwork 整个网络部分的功能都是通过 Docker 拆分出来的 libnetwork 实现的它提供了一个连接不同容器的实现同时也能够为应用给出一个能够提供一致的编程接口和网络层抽象的容器网络模型。 The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications. libnetwork 中最重要的概念容器网络模型由以下的几个主要组件组成分别是 Sandbox、Endpoint 和 Network 在容器网络模型中每一个容器内部都包含一个 Sandbox其中存储着当前容器的网络栈配置包括容器的接口、路由表和 DNS 设置Linux 使用网络命名空间实现这个 Sandbox每一个 Sandbox 中都可能会有一个或多个 Endpoint在 Linux 上就是一个虚拟的网卡 vethSandbox 通过 Endpoint 加入到对应的网络中这里的网络可能就是我们在上面提到的 Linux 网桥或者 VLAN。 挂载点 虽然我们已经通过 Linux 的命名空间解决了进程和网络隔离的问题在 Docker 进程中我们已经没有办法访问宿主机器上的其他进程并且限制了网络的访问但是 Docker 容器中的进程仍然能够访问或者修改宿主机器上的其他目录这是我们不希望看到的。 在新的进程中创建隔离的挂载点命名空间需要在 clone 函数中传入 CLONE_NEWNS这样子进程就能得到父进程挂载点的拷贝如果不传入这个参数子进程对文件系统的读写都会同步回父进程以及整个主机的文件系统。 如果一个容器需要启动那么它一定需要提供一个根文件系统rootfs容器需要使用这个文件系统来创建一个新的进程所有二进制的执行都必须在这个根文件系统中。 想要正常启动一个容器就需要在 rootfs 中挂载以上的几个特定的目录除了上述的几个目录需要挂载之外我们还需要建立一些符号链接保证系统 IO 不会出现问题。 为了保证当前的容器进程没有办法访问宿主机器上其他目录我们在这里还需要通过 libcotainer 提供的 pivor_root 或者 chroot 函数改变进程能够访问个文件目录的根节点。 // pivor_root put_old mkdir(...); pivot_root(rootfs, put_old); chdir(/); unmount(put_old, MS_DETACH); rmdir(put_old);// chroot mount(rootfs, /, NULL, MS_MOVE, NULL); chroot(.); chdir(/);到这里我们就将容器需要的目录挂载到了容器中同时也禁止当前的容器进程访问宿主机器上的其他目录保证了不同文件系统的隔离。 Chroot 在这里不得不简单介绍一下 chrootchange root在 Linux 系统中系统默认的目录就都是以 / 也就是根目录开头的chroot 的使用能够改变当前的系统根目录结构通过改变当前系统的根目录我们能够限制用户的权利在新的根目录下并不能够访问旧系统根目录的结构个文件也就建立了一个与原系统完全隔离的目录结构。 CGroups物理资源限制分组 我们通过 Linux 的命名空间为新创建的进程隔离了文件系统、网络并与宿主机器之间的进程相互隔离但是命名空间并不能够为我们提供物理资源上的隔离比如 CPU 或者内存如果在同一台机器上运行了多个对彼此以及宿主机器一无所知的『容器』这些容器却共同占用了宿主机器的物理资源。 如果其中的某一个容器正在执行 CPU 密集型的任务那么就会影响其他容器中任务的性能与执行效率导致多个容器相互影响并且抢占资源。如何对多个容器的资源使用进行限制就成了解决进程虚拟资源隔离之后的主要问题而 Control Groups简称 CGroups就是能够隔离宿主机器上的物理资源例如 CPU、内存、磁盘 I/O 和网络带宽。 每一个 CGroup 都是一组被相同的标准和参数限制的进程不同的 CGroup 之间是有层级关系的也就是说它们之间可以从父类继承一些用于限制资源使用的标准和参数。 Linux 的 CGroup 能够为一组进程分配资源也就是我们在上面提到的 CPU、内存、网络带宽等资源通过对资源的分配。 Linux 使用文件系统来实现 CGroup我们可以直接使用下面的命令查看当前的 CGroup 中有哪些子系统 $ lssubsys -m cpuset /sys/fs/cgroup/cpuset cpu /sys/fs/cgroup/cpu cpuacct /sys/fs/cgroup/cpuacct memory /sys/fs/cgroup/memory devices /sys/fs/cgroup/devices freezer /sys/fs/cgroup/freezer blkio /sys/fs/cgroup/blkio perf_event /sys/fs/cgroup/perf_event hugetlb /sys/fs/cgroup/hugetlb大多数 Linux 的发行版都有着非常相似的子系统而之所以将上面的 cpuset、cpu 等东西称作子系统是因为它们能够为对应的控制组分配资源并限制资源的使用。 如果我们想要创建一个新的 cgroup 只需要在想要分配或者限制资源的子系统下面创建一个新的文件夹然后这个文件夹下就会自动出现很多的内容如果你在 Linux 上安装了 Docker你就会发现所有子系统的目录下都有一个名为 Docker 的文件夹 $ ls cpu cgroup.clone_children ... cpu.stat docker notify_on_release release_agent tasks$ ls cpu/docker/ 9c3057f1291b53fd54a3d12023d2644efe6a7db6ddf330436ae73ac92d401cf1 cgroup.clone_children ... cpu.stat notify_on_release release_agent tasks9c3057xxx 其实就是我们运行的一个 Docker 容器启动这个容器时Docker 会为这个容器创建一个与容器标识符相同的 CGroup在当前的主机上 CGroup 就会有以下的层级关系 每一个 CGroup 下面都有一个 tasks 文件其中存储着属于当前控制组的所有进程的 pid作为负责 cpu 的子系统cpu.cfs_quota_us 文件中的内容能够对 CPU 的使用作出限制如果当前文件的内容为 50000那么当前控制组中的全部进程的 CPU 占用率不能超过 50%。 如果系统管理员想要控制 Docker 某个容器的资源使用率就可以在 docker 这个父控制组下面找到对应的子控制组并且改变它们对应文件的内容当然我们也可以直接在程序运行时就使用参数让 Docker 进程去改变相应文件中的内容。 当我们使用 Docker 关闭掉正在运行的容器时Docker 的子控制组对应的文件夹也会被 Docker 进程移除Docker 在使用 CGroup 时其实也只是做了一些创建文件夹改变文件内容的文件操作不过 CGroup 的使用也确实解决了我们限制子容器资源占用的问题系统管理员能够为多个容器合理的分配资源并且不会出现多个容器互相抢占资源的问题。 总之dockersLXCAUFS docker为LXCAUFS组合 LXC负责资源管理AUFS负责镜像管理 而LXC包括cgroupnamespacechroot等组件并通过cgroup资源管理 那么从资源管理的角度来看DockerLXC, Cgroup三者的关系是怎样的呢 cgroup是在底层落实资源管理LXC在cgroup上面封装了一层随后docker有在LXC封装了一层 Cgroup其实就是linux提供的一种限制记录隔离进程组所使用的物理资源管理机制 也就是说Cgroup是LXC为实现虚拟化所使用资源管理手段我们可以这样说底层没有cgroup支持也就没有lxc更别说docker的存在了这是我们需要掌握和理解的三者之间的关系概念 我们在把重心转移到LXC这个相当于中间件上上述我们提到LXC是建立在cgroup基础上的 我们可以粗略的认为LXCCgroupnamespaceChrootveth用户控制脚本 LXC利用内核的新特性cgroup来提供用户空间的对象用来保证资源的隔离和对应用系统资源的限制 Docker容器的文件系统最早是建立在Aufs基础上的Aufs是一种Union FS 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统之下并实现一种 layer的概念, 由于Aufs未能加入到linux内核中考虑到兼容性的问题便加入了Devicemapper的支持Docker目前默认是建立在Devicemapper基础上 devicemapper用户控件相关部分主要负责配置具体的策略和控制逻辑 比如逻辑设备和哪些物理设备建立映射怎么建立这些映射关系等而具体过滤和重定向IO请求的工作有内核中相关代码完成 因此整个device mapper机制由两部分组成–内核空间的device mapper驱动 用户控件的device mapper库以及它提供的dmsetup工具 深入解读docker网络 当你开始大规模使用docker时你会发现需要了解很多关于网络的知识。docker作为目前最火的轻量级容器技术有很多令人称道的功能如docker的镜像管理。然而docker同样有着很多不完善的地方网络方面就是Docker比较薄弱的部分。因此作为一名运维工程师有必要深入了解docker的网络知识以满足更高的网络需求。作为一名微服开发工程师简单了解docker网络环节即可。本章节首先介绍了Docker自身的3种local网络工作方式然后介绍一些docker自定义网络模式。 docker安装后会自动创建3种网络: bridgehostnone docker network lsdocker网络理论部分 docker使用Linux桥接网卡在宿主机虚拟一个docker容器网桥(docker0)docker启动一个容器时会根据docker网桥的网段分配给容器一个IP地址称为Container-IP同时docker网桥是每个容器的默认网关。 因为在同一宿主机内的容器都接入同一个网桥这样容器之间就能够通过容器的Container-IP直接通信。 docker网桥是宿主机虚拟出来的并不是真实存在的网络设备外部网络是无法寻址到的这也意味着外部网络无法通过直接Container-IP访问到容器。 如果容器希望外部访问能够访问到可以通过映射容器端口到宿主主机端口映射即docker run创建容器时候通过 -p 或 -P 参数来启用访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。 # 使用命令查看docker网络部分 docker infoDocker网络模式 Docker网络模式配置说明host模式–nethost容器和宿主机共享Network namespace。容器将不会虚拟出自己的网卡配置自己的IP 等而是使用宿主机的IP和端口。container模式–netcontainer:NAME_or_ID容器和另外一个容器共享Network namespace。kubernetes中的pod就是多个容器共享一个Network namespace。创建的容器不会创建自己的网卡配置自己的IP而是和一个指定的容器共享IP、端口范围。none模式–netnone容器有独立的Network namespace并没有对其进行任何网络设置如分配veth pair 和网桥连接配置IP等。该模式关闭了容器的网络功能。bridge模式–netbridge默认为该模式。此模式会为每一个容器分配、设置IP等并将容器连接到一个docker0虚拟网桥通过docker0网桥以及Iptables nat表配置与宿主机通信。Macvlan network无容器具备Mac地址使其显示为网络上的物理设备Overlay无(覆盖网络 利用VXLAN实现的bridge模式 bridge模式 默认的网络模式。bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的,但容器通过宿主机的NAT规则后可以访问外网。 Bridge 桥接模式的实现步骤主要如下 Docker Daemon 利用 veth pair 技术在宿主机上创建两个虚拟网络接口设备假设为veth0 和 veth1。而veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文都会将报文传输给另一方。Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0网桥上。保证宿主机的网络报文可以发往 veth0Docker Daemon 将 veth1 添加到 Docker Container 所属的 namespace 下并被改名为eth0。如此一来保证宿主机的网络报文若发往 veth0则立即会被 eth0 接收实现宿主机到Docker Container网络的联通性同时也保证 Docker Container 单独使用 eth0实现容器网络环境的隔离性。 Bridge桥接模式的缺陷 最明显的是该模式下 Docker Container 不具有一个公有 IP即和宿主机的 eth0 不处于同一个网段。导致的结果是宿主机以外的世界不能直接和容器进行通信。虽然 NAT 模式经过中间处理实现了这一点但是 NAT 模式仍然存在问题与不便如容器均需要在宿主机上竞争端口容器内部服务的访问者需要使用服务发现获知服务的外部端口等。另外 NAT 模式由于是在三层网络上的实现手段故肯定会影响网络的传输效率。 注意 veth设备是成双成对出现的一端是容器内部命名为eth0一端是加入到网桥并命名的veth通常命名为veth它们组成了一个数据传输通道一端进一端出veth设备连接了两个网络设备并实现了数据通信 host模式 相当于Vmware中的NAT模式与宿主机在同一个网络中但没有独立IP地址。 如果启动容器的时候使用host模式那么这个容器将不会获得一个独立的Network Namespace而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡配置自己的IP等而是使用宿主机的IP和端口。但是容器的其他方面如文件系统、进程列表等还是和宿主机隔离的。 使用host模式的容器可以直接使用宿主机的IP地址与外界通信容器内部的服务端口也可以使用宿主机的端口不需要进行NAThost最大的优势就是网络性能比较好但是docker host上已经使用的端口就不能再用了网络的隔离性不好。 host 网络模式需要在容器创建时指定–networkhost host 模式是 bridge 桥接模式很好的补充。采用 host 模式的 Docker Container可以直接使用宿主机的 IP地址与外界进行通信若宿主机的 eth0 是一个公有 IP那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口无需额外进行 NAT 转换。 host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。 Host 网络模式的缺陷 最明显的是 Docker Container 网络环境隔离性的弱化。即容器不再拥有隔离、独立的网络环境。 另外使用 host 模式的 Docker Container 虽然可以让容器内部的服务和传统情况无差别、无改造的使用但是由于网络隔离性的弱化该容器会与宿主机共享竞争网络栈的使用 另外容器内部将不再拥有所有的端口资源原因是部分端口资源已经被宿主机本身的服务占用还有部分端口已经用以 bridge 网络模式容器的端口映射。 Container网络模式 一种特殊host 网络模式 Container 网络模式是 Docker 中一种较为特别的网络的模式。在容器创建时使用– networkcontainer:vm1指定。(vm1指定的是运行的容器名) 处于这个模式下的 Docker 容器会共享一个网络环境,这样两个容器之间可以使用localhost高效快速通信。 缺陷它并没有改善容器与宿主机以外世界通信的情况和桥接模式一样不能连接宿主机以外的其他设备。 这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace而不是和宿主机共享。新创建的容器不会创建自己的网卡配置自己的 IP而是和一个指定的容器共享 IP、端口范围等。 同样两个容器除了网络方面其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。 none模式 使用none模式Docker容器拥有自己的Network Namespace但是并不为Docker容器进行任何网络配置。也就是说这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。 这种网络模式下容器只有lo回环网络没有其他网卡。none模式可以在容器创建时通过--networknone来指定。这种类型的网络没有办法联网封闭的网络能很好的保证容器的安全性。 overlay 网络模式 Overlay 网络也称为覆盖网络。主要用于docker集群部署。 Overlay 网络的实现方式和方案有多种。Docker自身集成了一种基于VXLAN隧道技术实现。 Overlay 网络主要用于实现跨主机容器之间的通信。 应用场景需要管理成百上千个跨主机的容器集群的网络时 macvlan 网络模式 macvlan网络模式最主要的特征就是他们的通信会直接基于mac地址进行转发。 这时宿主机其实充当一个二层交换机。Docker会维护着一个MAC地址表当宿主机网络收到一个数据包后直接根据mac地址找到对应的容器再把数据交给对应的容器。 容器之间可以直接通过IP互通通过宿主机上内建的虚拟网络设备创建macvlan网络时自动创建但与主机无法直接利用IP互通。 应用场景由于每个外来的数据包的目的mac地址就是容器的mac地址这时每个容器对于外面网络来说就相当于一个真实的物理网络设备。因此当需要让容器来的网络看起来是一个真实的物理机时使用macvlan模式 Macvlan是一个新的尝试是真正的网络虚拟化技术的转折点。Linux实现非常轻量级因为与传统的Linux Bridge隔离相比它们只是简单地与一个Linux以太网接口或子接口相关联以实现网络之间的分离和与物理网络的连接。 Macvlan提供了许多独特的功能并有充足的空间进一步创新与各种模式。这些方法的两个高级优点是绕过Linux网桥的正面性能以及移动部件少的简单性。删除传统上驻留在Docker主机NIC和容器接口之间的网桥留下了一个非常简单的设置包括容器接口直接连接到Docker主机接口。由于在这些情况下没有端口映射因此可以轻松访问外部服务。 Macvlan Bridge模式每个容器都有唯一的MAC地址用于跟踪Docker主机的MAC到端口映射。 Macvlan驱动程序网络连接到父Docker主机接口。示例是物理接口例如eth0用于802.1q VLAN标记的子接口eth0.10.10代表VLAN 10或甚至绑定的主机适配器将两个以太网接口捆绑为单个逻辑接口。 指定的网关由网络基础设施提供的主机外部。 每个Macvlan Bridge模式的Docker网络彼此隔离一次只能有一个网络连接到父节点。 每个主机适配器有一个理论限制每个主机适配器可以连接一个Docker网络。 同一子网内的任何容器都可以与没有网关的同一网络中的任何其他容器进行通信macvlan bridge。 相同的docker network命令适用于vlan驱动程序。 在Macvlan模式下在两个网络/子网之间没有外部进程路由的情况下单独网络上的容器无法互相访问。这也适用于同一码头网络内的多个子网。 网络实操 拉取镜像 docker pull nginx:1.19.3-alpine备份镜像 docker save nginx:1.19.3-alpine -o nginx.1.19.3.alpine.tar导入镜像 docker load -i nginx.1.19.3.alpine.tarbridge网络 本章节学习docker的bridge网络bridge网络表现形式就是docker0这个网络接口。 容器默认都是通过docker0这个接口进行通信。 也可以通过docker0去和本机的以太网接口连接这样容器内部才能访问互联网。 # 查看docker0网络在默认环境中一个名为docker0的linux bridge自动被创建好了其上有一个 docker0 内部接口IP地址为172.17.0.1/16 ip a # 查看docker 网络 docker network ls # 查看bridge网络详情。主要关注Containers节点信息。 docker network inspect bridgedocker0详解 运行镜像 docker run -itd --name nginx1 nginx:1.19.3-alpine # 查看bridge网络详情。主要关注Containers节点信息。发现nginx1容器默认使用bridge网络 docker network inspect bridge容器创建时IP地址的分配 # 查看docker100主机网络。发现多出一块网卡veth62aef5eif8 ip aDocker 创建一个容器的时候会执行如下操作 创建一对虚拟接口/网卡也就是veth pair分别放到本地主机和新容器中本地主机一端桥接到默认的 docker0 或指定网桥上并具有一个唯一的名字如 vetha596da4容器一端放到新容器中并修改名字作为 eth0这个网卡/接口只在容器的名字空间可见从网桥可用地址段中也就是与该bridge对应的network获取一个空闲地址分配给容器的 eth0并配置默认路由到桥接网卡 vetha596da4。 完成这些之后容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。 如果不指定–network创建的容器默认都会挂到 docker0 上使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。 第一种方式 docker exec -it nginx1 sh ip a 第二种方式 docker exec -it nginx1 ip a安装brctl yum install -y bridge-utils运行命令 brctl show多容器之间通讯 docker run -itd --name nginx1 nginx:1.19.3-alpine docker run -itd --name nginx2 nginx:1.19.3-alpine docker network inspect bridge docker exec -it nginx1 sh ping 172.17.0.2 docker exec -it nginx2 sh ping 172.17.0.2 ping www.baidu.com ping nginx1容器IP地址会发生变化 docker stop nginx1 nginx2 先启动nginx2在启动nginx1 docker start nginx2 docker start nginx1 docker network inspect bridgelink容器 学习docker run命令的link参数 --link[]: 添加链接到另一个容器 使用link的场景 在企业开发环境中我们有一个mysql的服务的容器mysql_1还有一个web应用程序web_1肯定web_1这台容器肯定要连接mysql_1这个数据库。 前面网络命名空间的知识告诉我们两个容器需要能通信需要知道对方的具体的IP地址。 生产环境还比较好IP地址很少变化但是在我们内部测试环境容器部署的IP地址是可能不断变化的所以开发人员不能在代码中写死数据库的IP地址。 这个时候我们就可以利用容器之间link来解决这个问题。不推荐大家使用该参数 下面我们来介绍如何通过容器名称来进行ping而不是通过IP地址。 docker rm -f nginx2 docker run -itd --name nginx2 --link nginx1 nginx:1.19.3-alpine docker exec -it nginx2 sh ping 172.17.0.2 ping www.baidu.com ping nginx1上面link命令是在nginx2容器启动时link到nginx1容器因此在nginx2容器里面可以ping通 nginx1容器名link的作用相当于添加了DNS解析。这里提醒下在nginx1容器里去ping nginx2 容器是不通的因为link关系是单向的不可逆。实际工作中docker官网已经不推荐我们使用link参数。docker用其他方式替换掉link参数 新建bridge网络 docker network create -d bridge lagou-bridge上面命令参数-d 是指DRIVER的类型后面的lagou-bridge是network的自定义名称这个和docker0是类似的。下面开始介绍如何把容器连接到lagou-bridge这个网络。 启动一个nginx的容器nginx3并通过参数network connect来连接lagou-bridge网络。在启动容器 nginx3之前我们查看目前还没有容器连接到了lagou-bridge这个网络上。 brctl show docker network ls docker network inspect lagou-bridge docker run -itd --name nginx3 --network lagou-bridge nginx:1.19.3-alpine brctl show docker network inspect lagou-bridge把一个运行中容器连接到lagou-bridge网络 docker network connect lagou-bridge nginx2 docker network inspect lagou-bridge docker exec -it nginx2 sh ping nginx3 docker exec -it nginx3 sh ping nginx2none网络 环境准备先stop和rm掉全部之前开启的容器。并且把前面创建的lagou-bridge网络也删除。当然更简单的办法是使用快照方式。将docker-100主机恢复到docker初始化安装时。 docker rm -f $(docker ps -aq) docker network rm lagou-bridge docker network ls启动一个ngnix的容器nginx1并且连接到none网络。然后执行docker network inspect none看看容器信息 docker run -itd --name nginx1 --network none nginx:1.19.3-alpine docker network inspect none注意容器使用none模式是没有物理地址和IP地址。我们可以进入到nginx1容器里执行ip a命令看看。只有一个lo接口没有其他网络接口没有IP。也就是说使用none模式这个容器是不能被其他容器访问。这种使用场景很少只有项目安全性很高的功能才能使用到。例如密码加密算法容器。 docker exec -it nginx1 sh ip ahost网络 前面学习none网络模式特点就是容器没有IP地址不能和其他容器通信。下面来看看host网络是什么特点。我们使用前面命令启动一个nginx的nginx2容器连接到host网络。然后docker network inspect host看看容器信息。 docker run -itd --name nginx2 --network host nginx:1.19.3-alpine docker network inspect host这里来看也不显示IP地址。那么是不是和none一样肯定不是不然也不会设计none和host网络进行区分。下面我们进入nginx2容器执行ip a看看效果。我们在容器里执行ip a发现打印内容和在linux本机外执行ip a是一样的。 docker exec -it nginx2 sh ip a这说明什么呢 容器使用了host模式说明容器和外层linux主机共享一套网络接口。VMware公司的虚拟机管理软件其中网络设置也有host这个模式作用也是一样虚拟机里面使用网络和你自己外层机器是一模一样的。这种容器和本机使用共享一套网络接口缺点还是很明显的例如我们知道web服务器一般端口是80共享了一套网络接口那么你这台机器上只能启动一个nginx端口为80的服务器 了。否则出现端口被占用的情况。 本篇很简单就是简单了解下docker中none和host网络模式。学习重点还是如何使用bridge网络。 网络命令汇总 docker network --help 网络常用命令汇总 connect Connect a container to a network create Create a network disconnect Disconnect a container from a network inspect Display detailed information on one or more networks ls List networks prune Remove all unused networks rm Remove one or more networks查看网络 查看网络 – docker network ls # 作用 查看已经建立的网络对象 # 命令格式 docker network ls [OPTIONS] # 命令参数(OPTIONS) -f, --filter filter 过滤条件(如 driverbridge’)--format string 格式化打印结果 --no-trunc 不缩略显示 -q, --quiet 只显示网络对象的ID # 注意 默认情况下docker安装完成后会自动创建bridge、host、none三种网络驱动 # 命令演示 docker network ls docker network ls --no-trunc docker network ls -f driverhost 创建网络 创建网络 – docker network create # 作用 创建新的网络对象 # 命令格式 docker network create [OPTIONS] NETWORK # 命令参数(OPTIONS) -d, --driver string 指定网络的驱动(默认 bridge) --subnet strings 指定子网网段(如192.168.0.0/16、172.88.0.0/24) --ip-range strings 执行容器的IP范围格式同subnet参数 --gateway strings 子网的IPv4 or IPv6网关如(192.168.0.1) # 注意 host和none模式网络只能存在一个 docker自带的overlay 网络创建依赖于docker swarm(集群负载均衡)服务 192.168.0.0/16 等于 192.168.0.0~192.168.255.255192.168.8.0/24 172.88.0.0/24 等于 172.88.0.0~172.88.0.255 # 命令演示 docker network ls docker network create -d bridge my-bridge docker network ls网络删除 网络删除 – docker network rm # 作用 删除一个或多个网络 # 命令格式 docker network rm NETWORK [NETWORK...] # 命令参数(OPTIONS) 无查看网络详细信息 查看网络详细信息 docker network inspect # 作用 查看一个或多个网络的详细信息 # 命令格式 docker network inspect [OPTIONS] NETWORK [NETWORK...] 或者 docker inspect [OPTIONS] NETWORK [NETWORK...] # 命令参数(OPTIONS) -f, --format string 根据format输出结果使用网络 使用网络 – docker run –-network # 作用 为启动的容器指定网络模式 # 命令格式 docker run/create --network NETWORK # 命令参数(OPTIONS) 无 # 注意 默认情况下docker创建或启动容器时会默认使用名为bridge的网络网络连接与断开 网络连接与断开 – docker network connect/disconnect # 作用 将指定容器与指定网络进行连接或者断开连接 # 命令格式 docker network connect [OPTIONS] NETWORK CONTAINER docker network disconnect [OPTIONS] NETWORK CONTAINER # 命令参数(OPTIONS) -f, --force 强制断开连接(用于disconnect)Docker-Compose 简介 Docker-Compose 项目是Docker官方的开源项目负责实现对Docker容器集群的快速编排。 Docker-Compose 项目由 Python 编写调用 Docker 服务提供的API来对容器进行管理。因此只要所操作的平台支持 Docker API就可以在其上利用Compose 来进行编排管理。 Docker-Compose 用来实现Docker容器快速编排 通过 Docker-Compose 不需要使用shell脚本来启动容器而使用 YAML 文件来配置应用程序需要的所有服务然后使用一个命令根据 YAML 的文件配置创建并启动所有服务。 Docker-compose模板文件简介 Compose允许用户通过一个docker-compose.yml模板文件YAML 格式来定义一组相关联的应用容器为一个项目project。 Compose模板文件是一个定义服务、网络和卷的YAML文件。 Compose模板文件默认路径是当前目录下的docker-compose.yml可以使用.yml或.yaml作为文件扩展名。 Docker-Compose标准模板文件应该包含version、services、networks 三大部分最关键的是services和networks两个部分。 eg version: 3.5 services:nacos1:restart: alwaysimage: nacos/nacos-server:${NACOS_VERSION}container_name: nacos1privileged: trueports:- 8001:8001- 8011:9555deploy:resources:limits:cpus: 0.50memory: 1024M env_file: - ./nacos.env environment:NACOS_SERVER_IP: ${NACOS_SERVER_IP_1}NACOS_APPLICATION_PORT: 8001NACOS_SERVERS: ${NACOS_SERVERS} volumes:- ./logs_01/:/home/nacos/logs/- ./data_01/:/home/nacos/data/- ./config/:/home/nacos/config/networks:- ha-network-overlaynacos2:restart: alwaysimage: nacos/nacos-server:${NACOS_VERSION}container_name: nacos2privileged: trueports:- 8002:8002- 8012:9555deploy:resources:limits:cpus: 0.50memory: 1024M env_file: - ./nacos.env environment:NACOS_SERVER_IP: ${NACOS_SERVER_IP_2}NACOS_APPLICATION_PORT: 8002NACOS_SERVERS: ${NACOS_SERVERS}volumes:- ./logs_02/:/home/nacos/logs/- ./data_02/:/home/nacos/data/- ./config/:/home/nacos/config/networks:- ha-network-overlaynacos3:restart: alwaysimage: nacos/nacos-server:${NACOS_VERSION}container_name: nacos3privileged: trueports:- 8003:8003- 8013:9555deploy:resources:limits:cpus: 0.50memory: 1024M env_file: - ./nacos.env environment:NACOS_SERVER_IP: ${NACOS_SERVER_IP_3}NACOS_APPLICATION_PORT: 8003NACOS_SERVERS: ${NACOS_SERVERS} volumes:- ./logs_03/:/home/nacos/logs/- ./data_03/:/home/nacos/data/- ./config/:/home/nacos/config/networks:- ha-network-overlay networks:ha-network-overlay:external: trueDocker-Compose 的编排处出来的部署架构 docker-compose 快速编排 Docker-Compose 项目是Docker官方的开源项目负责实现对Docker容器集群的快速编排。 Docker-Compose 项目由 Python 编写调用 Docker 服务提供的API来对容器进行管理。因此只要所操作的平台支持 Docker API就可以在其上利用Compose 来进行编排管理。 首先检查 版本 [rootk8s-master ~]# /usr/local/bin/docker-compose -version docker-compose version 1.25.1, build a82fef07如果安装好了就ok了如果没有安装则安装docker 从github上下载docker-compose二进制文件安装 下载最新版的docker-compose文件 curl -L https://github.com/docker/compose/releases/download/1.25.1/docker-compose-uname -s-uname -m -o /usr/local/bin/docker-compose注明离线安装包已经提供。上传后复制到/usr/local/bin/即可 cp /root/docker-compose /usr/local/bin/添加可执行权限 chmod x /usr/local/bin/docker-compose测试安装结果 [rootlocalhost ~]# docker-compose --version docker-compose version 1.25.1, build a82fef07cp /root/docker-compose /usr/local/bin/ chmod x /usr/local/bin/docker-compose docker-compose --versionDocker-Compose 的编排结构 Docker-Compose 将所管理的容器分为三层 工程project一个工程包含多个服务服务service一个服务当中可包括多个容器实例容器container Docker-Compose 运行目录下的所有文件docker-compose.yml、extends文件 或 环境变量文件等组成一个工程若无特殊指定 工程名即为当前目录名。 Docker Compose 的核心就是其配置文件采用 YAML 格式默认为 docker-compose.yml 。 一个工程当中可包含多个服务每个服务中定义了容器运行的镜像、参数、依赖。 一个服务当中可包括多个容器实例**但是Docker-Compose 并没有解决负载均衡的问题**因此需要借助其它工具实现服务发现及负载均衡比如 Consul 技术。 Docker-Compose 的工程配置文件默认为 docker-compose.yml可通过环境变量 COMPOSB_FILE 或 -f 参数自定义配置文件其定义了多个有依赖关系的服务及每个服务运行的容器。 Compose 允许用户通过一个单独的 docker-compose.yml 模板文件YAML格式来定义一组相关联的应用容器为一个项目 (project)。 YAML模板文件语法 默认的模板文件是docker-compose.yml,其中定义的每个服务都必须通过image指令指定镜像也可以通过build指令需要Dockerfile来自动构建。 其他大部分都跟docker run 中类似。 如果使用build指令在Dockerfile中设置的选项例如CMD,EXPOSE,VOLUME,ENV等将自动被获取无需在docker-compose.yml中再次被设置。 docker-compose.yml 语法说明 1、image 指定为镜像名称或镜像ID。 如果镜像不存在Compose将尝试从互联网拉取这个镜像例如 image: ubuntu image: orchardup/postgresql image: a4bc65fd 指定服务的镜像名若本地不存在则 Compose 会去仓库拉取这个镜像: services:web:image: nginx2、build 指定Dockerfile所在文件夹的路径。Compose将会利用他自动构建这个镜像然后使用这个镜像。 build: ./dir 3、command 覆盖容器启动后默认执行的命令。 command: bundle exec thin -p 3000 4、links 链接到其他服务容器使用服务名称(同时作为别名)或服务别名SERVICE:ALIAS都可以 links:- db- db:database- redis注意使用别名会自动在服务器中的/etc/hosts 里创建如172.17.2.186 db相应的环境变量也会被创建。 5、external_links 链接到docker-compose.yml外部的容器甚至并非是Compose管理的容器。参数格式和links类似。 external_links: - redis_1- project_db_1:mysql- project_db_2:sqlserver6、ports 暴露端口信息。格式 宿主机器端口容器端口HOST:CONTAINER 或者仅仅指定容器的端口宿主机器将会随机分配端口都可以。 ports:- 3306- 8080:80- 127.0.0.1:8090:8001注意当使用 HOST:CONTAINER 格式来映射端口时如果你使用的容器端口小于 60 你可能会得到错误得结果因为 YAML 将会解析 xx:yy 这种数字格式为 60 进制。所以建议采用字符串格式。 7、expose 暴露端口与posts不同的是expose只可以暴露端口而不能映射到主机只供外部服务连接使用仅可以指定内部端口为参数。 expose:- 3000- 80008、volumes 设置卷挂载的路径。 可以设置宿主机路径:容器路径host:container或加上访问模式host:container:roro就是readonly的意思只读模式。 volumes:- /var/lib/mysql:/var/lib/mysql- /configs/mysql:/etc/configs/:ro9、volunes_from 挂载另一个服务或容器的所有数据卷。 volumes_from:- service_name- container_name10、environment 设置环境变量。可以属于数组或字典两种格式。 如果只给定变量的名称则会自动加载它在Compose主机上的值可以用来防止泄露不必要的数据。 environment:- RACK_ENVdevelopment- SESSION_SECRET11、env_file 从文件中获取环境变量可以为单独的文件路径或列表。 如果通过docker-compose -f FILE指定了模板文件则env_file中路径会基于模板文件路径。 如果有变量名称与environment指令冲突则以后者为准。 env_file: .env env_file:- ./common.env- ./apps/web.env- /opt/secrets.env环境变量文件中每一行都必须有注释支持#开头的注释行。 # common.env: Set Rails/Rack environment RACK_ENVdevelopment12、extends 基于已有的服务进行服务扩展。例如我们已经有了一个webapp服务模板文件为common.yml. # common.yml webapp: build: ./webapp environment:- DEBUGfalse- SEND_EMAILSfalse编写一个新的 development.yml 文件使用 common.yml 中的 webapp 服务进行扩展。 development.yml web: extends: file: common.yml service: webapp:ports:- 8080:80links:- dbenvelopment:- DEBUGtruedb:image: mysql:5.7后者会自动继承common.yml中的webapp服务及相关的环境变量。 13、net 设置网络模式。使用和docker client 的 --net 参数一样的值。 # 容器默认连接的网络是所有Docker安装时都默认安装的docker0网络. net: bridge # 容器定制的网络栈. net: none # 使用另一个容器的网络配置 net: container:[name or id] # 在宿主网络栈上添加一个容器容器中的网络配置会与宿主的一样 net: hostDocker会为每个节点自动创建三个网络 网络名称 作用 bridge 容器默认连接的网络是所有Docker安装时都默认安装的docker0网络 none 容器定制的网络栈 host 在宿主网络栈上添加一个容器容器中的网络配置会与宿主的一样 附录 操作名称 命令 创建网络 docker network create -d bridge mynet 查看网络列表 docker network ls 14、pid 和宿主机系统共享进程命名空间打开该选项的容器可以相互通过进程id来访问和操作。 pid: host15、dns 配置DNS服务器。可以是一个值也可以是一个列表。 dns: 8.8.8.8 dns:- 8.8.8.8- 9.9.9.916、cap_addcap_drop 添加或放弃容器的Linux能力Capability。 cap_add:- ALL cap_drop:- NET_ADMIN- SYS_ADMIN17、dns_search 配置DNS搜索域。可以是一个值也可以是一个列表。 dns_search: example.com dns_search:- domain1.example.com\ - domain2.example.com working_dir, entrypoint, user, hostname, domainname, mem_limit, privileged, restart, stdin_open, tty, cpu_shares这些都是和 docker run 支持的选项类似。 cpu_shares: 73 working_dir: /code entrypoint: /code/entrypoint.sh user: postgresql hostname: foo domainname: foo.com mem_limit: 1000000000 privileged: true restart: always stdin_open: true tty: true18、healthcheck 健康检查这个非常有必要等服务准备好以后再上线避免更新过程中出现短暂的无法访问。 healthcheck:test: [CMD, curl, -f, http://localhost/alive]interval: 5stimeout: 3s其实大多数情况下健康检查的规则都会写在 Dockerfile 中: FROM nginx RUN apt-get update apt-get install -y curl rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval5s --timeout3s CMD curl -f http://localhost/alive || exit 119、depends_on 依赖的服务优先启动例 depends_on:- redis20、deploy 部署相关的配置都在这个节点下例 deploy:mode: replicatedreplicas: 2restart_policy:condition: on-failuremax_attempts: 3update_config:delay: 5sorder: start-first # 默认为 stop-first推荐设置先启动新服务再终止旧的resources:limits:cpus: 0.50memory: 1g deploy:mode: global # 不推荐全局模式仅个人意见。placement:constraints: [node.role manager]若非特殊服务以上各节点的配置能够满足大部分部署场景了。 docker-compose.yml实例 version: 3.5 services:nacos1:restart: alwaysimage: nacos/nacos-server:${NACOS_VERSION}container_name: nacos1privileged: trueports:- 8001:8001- 8011:9555deploy:resources:limits:cpus: 0.50memory: 1024M env_file: - ./nacos.env environment:NACOS_SERVER_IP: ${NACOS_SERVER_IP_1}NACOS_APPLICATION_PORT: 8001NACOS_SERVERS: ${NACOS_SERVERS} volumes:- ./logs_01/:/home/nacos/logs/- ./data_01/:/home/nacos/data/- ./config/:/home/nacos/config/networks:- ha-network-overlaynacos2:restart: alwaysimage: nacos/nacos-server:${NACOS_VERSION}container_name: nacos2privileged: trueports:- 8002:8002- 8012:9555deploy:resources:limits:cpus: 0.50memory: 1024M env_file: - ./nacos.env environment:NACOS_SERVER_IP: ${NACOS_SERVER_IP_2}NACOS_APPLICATION_PORT: 8002NACOS_SERVERS: ${NACOS_SERVERS}volumes:- ./logs_02/:/home/nacos/logs/- ./data_02/:/home/nacos/data/- ./config/:/home/nacos/config/networks:- ha-network-overlaynacos3:restart: alwaysimage: nacos/nacos-server:${NACOS_VERSION}container_name: nacos3privileged: trueports:- 8003:8003- 8013:9555deploy:resources:limits:cpus: 0.50memory: 1024M env_file: - ./nacos.env environment:NACOS_SERVER_IP: ${NACOS_SERVER_IP_3}NACOS_APPLICATION_PORT: 8003NACOS_SERVERS: ${NACOS_SERVERS} volumes:- ./logs_03/:/home/nacos/logs/- ./data_03/:/home/nacos/data/- ./config/:/home/nacos/config/networks:- ha-network-overlay networks:ha-network-overlay:external: trueYAML 文件格式 及 编写注意事项 使用compose对Docker容器进行编排管理时需要编写docker-compose.yml文件初次编写时容易遇到一些比较低级的问题导致执行docker-compose up时先解析yml文件的错误。 比较常见的是yml对缩进的严格要求。 yml文件还行后的缩进不允许使用tab键字符只能使用空格而空格的数量也有要求经过实际测试发现每一行增加一个空格用于缩进是正常的。 aml 是一种标记语言它可以很直观的展示数据序列化格式可读性高。类似于XML数据描述语言语法比 XMAL简单的很多。 YAML数据结构通过缩进来表示连续的项目通过减号来表示键值对用冒号分隔数组用中括号[]括起来, hash用花括号{}括起来。 …省略1W字 尼恩说明由于公众号最多可以发布5W字 还有1W字放不下… 更多内容请参见《 Docker 学习圣经 》PDF 说在后面 通过此PDF尼恩希望能帮助大家早日实现 Docker 技术和底层原理的技术自由。 此文PDF 没有完后面还有N多内容需要补充。可以关注 尼恩的《技术自由圈》公众号。 本书 《 Docker 学习圣经 》PDF的 V1版本后面会持续迭代和升级。供后面的小伙伴参考提升大家的 3高 架构、设计、开发水平。 注本文以 PDF 持续更新最新尼恩 架构笔记、面试题 的PDF文件请从这里获取码云 参考资料 https://docs.docker.com/storage/storagedriver/aufs-driver/#how-the-aufs-storage-driver-works https://github.com/opencontainers/runc http://www.sel.zju.edu.cn/?p840 https://draveness.me/docker/ https://blog.csdn.net/wangqingjiewa/article/details/85000393 https://zhuanlan.zhihu.com/p/47683490 推荐阅读 尼恩N篇硬核架构 文章 帮你实现 架构自由 《吃透8图1模板人人可以做架构》 《10Wqps评论中台如何架构B站是这么做的》 《阿里二面千万级、亿级数据如何性能优化 教科书级 答案来了》 《峰值21WQps、亿级DAU小游戏《羊了个羊》是怎么架构的》 《100亿级订单怎么调度来一个大厂的极品方案》 《2个大厂 100亿级 超大流量 红包 架构方案》 … 更多架构文章正在添加中 实现你的响应式 自由 《响应式圣经10W字实现Spring响应式编程自由》 这是老版本 《Flux、Mono、Reactor 实战史上最全》 实现你的 spring cloud 自由 《Nacos (史上最全)》 《nacos高可用图解秒懂史上最全》 《sentinel 史上最全》 《Springcloud gateway 底层原理、核心实战 (史上最全)》 《SpringCloudDubbo3 王炸 》 《分库分表 Sharding-JDBC 底层原理、核心实战史上最全》 《一文搞定SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系史上最全》 实现你的 linux 自由 《Linux命令大全2W多字一次实现Linux自由》 实现你的 网络 自由 《TCP协议详解 (史上最全)》 《网络三张表ARP表, MAC表, 路由表实现你的网络自由》 实现你的 分布式锁 自由 《Redis分布式锁图解 - 秒懂 - 史上最全》 《Zookeeper 分布式锁 - 图解 - 秒懂》 实现你的 王者组件 自由 《队列之王 Disruptor 原理、架构、源码 一文穿透》 《缓存之王Caffeine 源码、架构、原理史上最全10W字 超级长文》 《缓存之王Caffeine 的使用史上最全》 《Java Agent 探针、字节码增强 ByteBuddy史上最全》 实现你的 面试题 自由 《阿里一面你做过哪些代码优化来一个人人可以用的极品案例》 《网易二面CPU狂飙900%该怎么处理》 《场景题假设10W人突访你的系统如何做到不 雪崩》 《Nginx面试题史上最全 持续更新》 《K8S面试题史上最全 持续更新》 《操作系统面试题史上最全、持续更新》 《Docker面试题史上最全 持续更新》 《clickhouse 超底层原理 高可用实操 史上最全》 《环形队列、 条带环形队列 Striped-RingBuffer 史上最全》 《单例模式史上最全》 《红黑树 图解 秒懂 史上最全》 《分布式事务 秒懂》 《Docker原理图解秒懂史上最全》 《Zookeeper Curator 事件监听 - 10分钟看懂》 《Netty 粘包 拆包 | 史上最全解读》 《Netty 100万级高并发服务器配置》 《Springcloud 高并发 配置 一文全懂》
http://www.dnsts.com.cn/news/89335.html

相关文章:

  • 做logo什么网站家乡网页制作模板
  • 江苏省建设工程质量监督站网站呼和浩特注册公司流程和费用
  • 万全做网站wl17581网站备案时间太长
  • 网站建设汇报稿网络管理中心
  • 聊城建网站服务玄幻小说排行榜百度风云榜
  • 创意设计网站推荐软件平台介绍
  • 表白网站制作软件手机安徽全过程网站搭建案例
  • 肥西县建设局网站怎么用dw做博客网站
  • 彩票网站开发 合法做调查问卷赚钱的网站
  • 可以在网上接网站做的网址免费关键词挖掘工具
  • 驻马店做网站哪家好wordpress 回复 验证码
  • 做网站需要哪些素材宁波外贸公司注册流程
  • 做的新网站做百度推广怎么弄wordpress删除dux主题
  • 网站建站哪家公司好一点网站建设推荐公司
  • 龙岗建网站公司百度推广关键词排名在哪看
  • 会计公司网站源码怎么免费做网站视频教学
  • 网站建设公司特点麻阳建设局网站
  • 品牌型网站制作价格网站分哪些种类
  • 襄阳高端网站建设软件排名工具
  • 做号网站国外云服务器推荐
  • 12306网站建设花了多少钱wordpress小工具位置
  • 网站开发工具js建设银行 福建 招聘网站
  • 哪些可以免费做网站wordpress添加评论
  • 淘宝网站建设成本大庆免费网站建设
  • 湘潭网站建设设计网站建设功能
  • 规范门户网站的建设和管理办法济南工程网站建设
  • 邯郸当地招聘网站长葛网站建设
  • 让自己的网站收录网站开发发帖语言
  • 网站报404错误怎么解决做响应式网站兼容哪几个尺寸
  • 东莞网站SEO优化托管flash代码做网站教程