添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

文章持续更新,建议收藏起来,慢慢读! 疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 : 《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 : 《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书: 《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书: 《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书: 《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领

免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值 >10000元 加尼恩领取

说在前面:

现在 拿到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 学习圣经 》PDF 封面
  • Docker基础
  • Docker 巨大的价值
  • Docker 的本质:
  • Docker的广泛应用场景:
  • Docker的在DevOps(开发、运维)场景的应用
  • Docker 的历史
  • Docker 的入门知识
  • Docker Engine
  • Docker Platform
  • 到底什么是docker:
  • 什么是容器?
  • docker基本组成
  • Docker 与虚拟机有何区别
  • docker和kvm都是虚拟化技术,它们的主要差别:
  • 与传统VM特性对比:
  • docker 与操作系统比较
  • Docker 的安装
  • docker安装的三种方式
  • 方式一 :离线安装docker
  • 方式二 :在线安装docker
  • 方式三 :用现成的 (大大的省事)
  • Docker Container概述
  • 什么是Container 容器
  • 容器与镜像的关系
  • Docker本地容器相关的操作
  • Container相关命令
  • 查看活跃容器 docker ps
  • 查看全部容器
  • 查看容器的进程信息
  • 如何查找容器名称?
  • docker最为常用的几个命令
  • docker的守护进程查看
  • docker 镜像查看
  • docker 容器查看
  • Docker Registry配置和查看
  • Docker容器进入的4种方式
  • 方式1:使用docker attach进入Docker容器
  • 方式2:使用SSH进入Docker容器
  • 方式3:使用nsenter进入Docker容器
  • 1 什么是nsenter?
  • 2、nsenter安装
  • 3、nsenter的 使用
  • docker隔离应用应用涉及到的六大名称空间
  • 1、pid 命名空间(进程ID)
  • 2、net 命名空间(网络)
  • 3、ipc 命名空间(进程间通信)
  • 4、mnt 命名空间(挂载文件系统)
  • 5、UTS 命名空间(主机名/域名)
  • 6、user 命名空间(用户)
  • docker-shim 容器进程
  • runc (OCI reference implementation)
  • Docker、containerd, containerd-shim和runc之间的关系
  • 通过runc来启动一个container的过程
  • 查看进程信息
  • 查看父进程信息
  • 查看进程树
  • CRI 运行时接口
  • Docker的技术底座:
  • 底层技术支持
  • UnionFS 联合文件系统
  • 什么是镜像
  • UnionFS 与AUFS
  • 什么是 Docker 镜像分层机制?
  • Docker Image 如何而来呢?
  • Namespaces
  • Libnetwork
  • Chroot
  • CGroups物理资源限制分组
  • 总之:dockers=LXC+AUFS
  • 深入解读docker网络
  • docker网络理论部分
  • Docker网络模式
  • bridge模式
  • host模式
  • Container网络模式
  • none模式
  • overlay 网络模式
  • macvlan 网络模式
  • bridge网络
  • docker0详解
  • 多容器之间通讯
  • link容器
  • 新建bridge网络
  • 把一个运行中容器连接到lagou-bridge网络
  • none网络
  • host网络
  • 网络命令汇总
  • 查看网络详细信息
  • 网络连接与断开
  • Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,实现轻量级虚拟化。

    docker为什么有这么巨大的价值呢?

    因为,在容器技术出来之前,大家都是使用虚拟机技术,比如在 window中装一个VMware,通过这个软件我们可以虚拟出来一台或者多台电脑,实现硬件资源的细粒度分割和使用隔离。

    但是 ,虚拟机技术太笨重啦!模式太重。

    Docker容器技术,也是一种虚拟化技术,也是实现硬件资源的细粒度分割和使用隔离。但是,Docker是一种轻量级的虚拟机技术。

    Docker容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

    Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),

    对于开发人员来说,用 CE(Community Edition) 社区版就可以了

    Docker的广泛应用场景:

  • Web 应用的自动化打包和发布。
  • 自动化测试和持续集成、发布。
  • 在服务型环境中部署和调整数据库或其他的后台应用。
  • 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。
  • 一般来说,测试环境、生产环境,基本都已经全面docker化了。

    可见,docker技术,是那么那么的重要。

    Docker的在DevOps(开发、运维)场景的应用

    一般来说,怎么的微服务应用,有多套环境:

    (1)开发
    (2)测试
    (3)预生产
    (4)生产
    

    四套环境,导致环境配置是十分的麻烦,每一个环境都要部署各种组件(如Redis、ES、zk) ,非常的费时费力。

    更要命的是,在生产环境上, 吞吐量一上来,需要动态扩容。

    使用docker,咱们可以将DevOps(开发、运维)的工作,高速完成:

    (1)快速完成 发布工作

    开发环境一般是 Windows/mac,最后发布到Linux。

    没有docker之前,使用jar包发布,配上大量的shell脚本,然后各种配置,及其复杂。

    有了docker之后,做好镜像,开发打包部署上线,一套流程做完!

    (2)快速完成 交付工作

    传统的交付工作,要给用户提供各种安装的帮助文档,安装程序,基础环境安装,依赖的中间件安装,等等等等。

    有了docker之后,能更快速的交付和部署。 给他一套镜像,通过Docker命令,一键运行,啥都是好的。

    那你看,docker 是不是,真香。

    (3)更便捷的升级和扩缩容

    使用了 Docker之后,我们项目打包为一个镜像,部署应用就和搭积木一样

    扩展服务器A,启动一个容器就ok。

    扩展服务器B,启动一个容器就ok。

    如果要动态扩展,使用K8S这类分布式容器管理基础设施,配上一个HPA 控制器组件,就能自动的完成动态扩容,动态缩容。

    那你看,docker 是不是,真香。

    (4)服务器的性能可以被压榨到极致

    Docker是内核级别的虚拟化,可以在一个物理机上可以运行很多的容器实例。

    服务器的性能可以被压榨到极致

    那你看,docker 是不是,真香。

    接下来,随着40岁老架构师一起,来穿透docker的原理和实操吧。

    只有先穿透docker,才能穿透K8S,最终穿透云原生+大数据,实现你的技术自由。

    Docker的历史

    2010年,几个的年轻人在美国的旧金山成立了一家公司 dotcloud。dotcloud 是一个Paas平台的创业公司,从事LXC(Linux Container容器)有关的容器技术。

    Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。他们将自己的技术(容器化技术)命名就是 Docker。

    Docker刚刚延生的时候,没有引起行业的注意!

    虽然获得了创业孵化器(Y Combinator)的支持、也获得过一些融资,但随着IT巨头们(微软、谷歌、亚马逊等厂商)也进入PaaS凭他,dotCloud举步维艰,眼看就活不下去!

    2013年,dotCloud的创始人,28岁的Solomon Hykes做了一个艰难的决定,将dotCloud的核心引擎开源,这项核心引擎技术能够将Linux容器中的应用程序、代码打包,轻松的在服务器之间进行迁移。

    2013发布了 Docker-compose 组件提供容器的编排工具。

    2014年 Docker 发布1.0版本,2015年Docker 提供 Docker-machine,支持 windows 平台。

    docker火了。

    这个基于LXC技术的核心管理引擎开源后,让全世界的技术人员感到惊艳。

    大家感叹这一切太方便了!!于是,越来越多的人发现docker的优点,使用他!

    虽然,Docker 项目在开源社区大受追捧,同时也被业界诟病的是: Docker 公司对于 Docker 发展具有绝对的话语权,比如 Docker 公司推行了 libcontainer 难以被社区接受。

    为了防止 Docker 这项开源技术被Docker 公司控制,几个核心贡献代码的厂商诸如 Redhat,谷歌的倡导下,成立了 OCI 开源社区,制定了 OCI 开放容器标准,Open Container Initiative(OCI,开放容器标准)。

    OCI 开源社区旨在于将 Docker 的发展权利回归社区,当然反过来讲,Docker 公司也希望更多的厂商安心贡献代码到Docker 项目,促进 Docker 项目的发展。

    Docker 将自己容器格式和运行时 runC 捐给了 OCI,OCI 在此基础上制定了 2 个标准:

    运行时标准 Runtime Specification (runtime-spec)

    镜像标准 Image Specification (image-spec) :

    于是通过OCI建立了 runc 项目,替代 libcontainer,这为开发者提供了除 Docker 之外的容器化实现的选择。

    OCI 社区提供了 runc 的维护,而 runc 是基于 OCI 规范的运行容器的工具。

    换句话说,你可以通过 runc,提供自己的容器实现,而不需要依赖 Docker。

    当然,Docker 的发行版底层也是用的 runc。在 Docker 宿主机上执行 runc,你会发现它的大多数命令和 Docker 命令类似,感兴趣的读者可以自己实践如何用 runc 启动容器。

    至2017年,Docker 项目转移到 Moby 项目,基于 Moby 项目,Docker 提供了两种发行版,Docker CE 和 Docker EE, Docker CE 就是目前大家普遍使用的版本,Docker EE 成为付费版本,提供了容器的编排,Service 等概念。

    Docker 公司承诺 Docker 的发行版会基于 Moby 项目。这样一来,通过 Moby 项目,你也可以自己打造一个定制化的容器引擎,而不会被 Docker 公司绑定。

    Docker 的入门知识

    从大家常用的Docker Engine开始说起。

    Docker Engine

    当人们说“Docker”时,他们通常是指 Docker Engine,它是一个客户端 - 服务器应用程序,

    Docker 引擎由如下主要的组件构成:Docker 客户端(Docker Client)、Docker 守护进程(Docker daemon)、containerd 以及 runc。

    Docker Engine 从 CLI 中接受docker 命令,完成容器的管理:

    例如使用 docker run 、docker ps 来列出正在运行的容器、

    例如使用docker images 来列出镜像,

    Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上,

    Client通过Socket连接从客户端访问Docker守护进程。Docker守护进程从客户端接受命令,并按照命令,管理运行在主机上的容器。

  • 后台进行(dockerd)
  • REST API Server
  • CLI接口(docker)
  • Docker Platform

  • Docker提供了一个开发,打包,运行app的平台
  • 把app和底层infrastructure隔离开来
  • 其三层模型如图:

    到底什么是docker:

    到底什么是docker:

  • docker是一个软件,可以运行在window、linux、mac等各种操作系统上。
  • docker 是一个开源的应用容器引擎,基于Go 语言开发并遵从 Apache2.0 协议开源,项目代码托管在github上进行维护
  • docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。
  • 容器是完全使用沙箱机制,相互之间不会有任何接口,更重要的是容器性能开销极低。
  • 什么是容器?

    什么是容器?

  • 对软件和其依赖的标准化打包
  • 应用之间相互隔离
  • 共享同一个OS Kernel
  • 可以运行在很多主流操作系统上
  • 注:容器和虚拟机的区别在于容器是APP层面的隔离,而虚拟化是物理资源层面的隔离

    容器解决了什么问题?

  • 解决了开发和运维之间的矛盾
  • 在开发和运维之间搭建了一个桥梁,是实现devops最佳解决方案
  • 一个docker 容器,是一个运行时环境,可以简单理解为进程运行的集装箱。

    docker基本组成

    docker主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统之上);

    docker仓库(Registry):用来保存各种打包好的软件镜像;仓库分为公有仓库和私有仓库。(很类似 maven)

    docker镜像(Images):软件打包好的镜像;放在docker仓库中;

    docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用

    Docker 包括三个基本概念:

  • 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
  • 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
  • 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
  • Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。

    Docker 容器通过 Docker 镜像来创建。

    Docker 客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。 Docker 主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。 Docker Registry Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

    Docker 与虚拟机有何区别

    Docker 的误解:Docker 是轻量级的虚拟机。

    很多人将docker理解为, Docker 实现了类似于虚拟化的技术,能够让应用跑在一些轻量级的容器里。这么理解其实是错误的。

    docker和kvm都是虚拟化技术,它们的主要差别:

    1、Docker有着比虚拟机更少的抽象层

    2、docker利用的是宿主机的内核,VM需要的是Guest OS

    二者的不同:

  • VM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;
  • Container(Docker容器),在宿主机器、宿主机器操作系统上创建Docker引擎,在引擎的基础上再安装应用。
  • 所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统,避免引导。docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级!

    虚拟机是加载Guest OS ,这是分钟级别的

    与传统VM特性对比:

    作为一种轻量级的虚拟化方式,Docker在运行应用上跟传统的虚拟机方式相比具有显著优势:

  • Docker 容器很快,启动和停止可以在秒级实现,这相比传统的虚拟机方式要快得多。
  • Docker 容器对系统资源需求很少,一台主机上可以同时运行数千个Docker容器。
  • Docker 通过类似Git的操作来方便用户获取、分发和更新应用镜像,指令简明,学习成本较低。
  • Docker 通过Dockerfile配置文件来支持灵活的自动化创建和部署机制,提高工作效率。
  • Docker 容器除了运行其中的应用之外,基本不消耗额外的系统资源,保证应用性能的同时,尽量减小系统开销。
  • Docker 利用Linux系统上的多种防护机制实现了严格可靠的隔离。从1.3版本开始,Docker引入了安全选项和镜像签名机制,极大地提高了使用Docker的安全性。
  • 传统的虚拟机方式提供的是相对封闭的隔离。

    Docker利用Linux系统上的多种防护技术实现了严格的隔离可靠性,并且可以整合众多安全工具。

    从 1.3.0版本开始,docker重点改善了容器的安全控制和镜像的安全机制, 极大提高了使用docker的安全性。

    Docker 的安装

    安装docker前置条件

    当我们安装 Docker 的时候,会涉及两个主要组件:

  • Docker CLI:客户端
  • Docker daemon:有时也被称为“服务端”或者“引擎”
  • 硬件总体要求,可以参考尼恩的本地硬件情况:

    1、硬件要求。

    本文硬件总体要求如下表:

    docker+K8S学习环境非常复杂,尼恩搞这个 前前后后起码折腾了一周,

    其中,很多头疼的工作,包括linux内核升级、磁盘扩容等等, 苦不堪言。

    现在把这个环境,以虚拟机box镜像的方式,导出来直接给大家,

    大家一键导入后,直接享受docker 的实操,享受K8S的实操,可以说,爽到不要不要的。

    以上软件和 尼恩个人的虚拟机box镜像,可以找尼恩获取。

    docker安装的三种方式

    安装docker的三种方式

    (1)离线安装

    (2)在线安装

    (3)用现成的

    方式一 :离线安装docker

    这里以 19.03.9 版本进行介绍, 其他版本是一样的。

    这种安装方式,可以用于没有 互联网的 场景。

    比如,很多公司,并不能直接上外网。

    一、基础环境

    1、操作系统:CentOS 7.3

    2、Docker版本:19.03.9 官方下载地址

    3、官方参考文档:https://docs.docker.com/install/linux/docker-ce/binaries/#install-static-binaries

    二、Docker安装

    wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.9.tgz

    注意:如果事先下载好了可以忽略这一步

    把压缩文件存在指定目录下(如root),并进行解压

    tar -zxvf docker-19.03.9.tgz

    cd root
    [root@localhost ~]# tar -zxvf docker-19.03.6.tgz
    docker/
    docker/containerd
    docker/docker
    docker/ctr
    docker/dockerd
    docker/runc
    docker/docker-proxy
    docker/docker-init
    docker/containerd-shim
    

    3、将解压出来的docker文件内容移动到 /usr/bin/ 目录下

    cp docker/* /usr/bin/

    4、将docker注册为service

    cat /etc/systemd/system/docker.service

    vi /etc/systemd/system/docker.service

    [Unit]
    Description=Docker Application Container Engine
    Documentation=https://docs.docker.com
    After=network-online.target firewalld.service
    Wants=network-online.target
    [Service]
    Type=notify
    # the default is not to use systemd for cgroups because the delegate issues still
    # exists and systemd currently does not support the cgroup feature set required
    # for containers run by docker
    ExecStart=/usr/bin/dockerd
    ExecReload=/bin/kill -s HUP $MAINPID
    # Having non-zero Limit*s causes performance problems due to accounting overhead
    # in the kernel. We recommend using cgroups to do container-local accounting.
    LimitNOFILE=infinity
    LimitNPROC=infinity
    LimitCORE=infinity
    # Uncomment TasksMax if your systemd version supports it.
    # Only systemd 226 and above support this version.
    #TasksMax=infinity
    TimeoutStartSec=0
    # set delegate yes so that systemd does not reset the cgroups of docker containers
    Delegate=yes
    # kill only the docker process, not all processes in the cgroup
    KillMode=process
    # restart the docker process if it exits prematurely
    Restart=on-failure
    StartLimitBurst=3
    StartLimitInterval=60s
    [Install]
    WantedBy=multi-user.target
    
    chmod +x /etc/systemd/system/docker.service  #添加文件权限并启动docker
    systemctl daemon-reload               #重载unit配置文件
    systemctl start docker     #启动Docker
    systemctl enable docker.service       #设置开机自启
    
    [root@localhost ~]# vi /etc/systemd/system/docker.service
    [root@localhost ~]# chmod +x /etc/systemd/system/docker.service
    [root@localhost ~]# systemctl daemon-reload
    [root@localhost ~]# systemctl start docker
    [root@localhost ~]# systemctl enable docker.service
    Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /etc/systemd/system/docker.service.
    
    systemctl status docker #查看Docker状态
    docker -v #查看Docker版本
    docker info
    
    [root@localhost ~]# systemctl status docker
     docker.service - Docker Application Container Engine
       Loaded: loaded (/etc/systemd/system/docker.service; enabled; vendor preset: disabled)
       Active: active (running) since Sat 2021-10-09 15:25:44 CST; 29s ago
         Docs: https://docs.docker.com
     Main PID: 1916 (dockerd)
       CGroup: /system.slice/docker.service
               ├─1916 /usr/bin/dockerd
               └─1927 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
    Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.671407996+08:00" level=info msg="scheme \"unix\" not r...e=grpc
    Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.671440368+08:00" level=info msg="ccResolverWrapper: se...e=grpc
    Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.671462935+08:00" level=info msg="ClientConn switching ...e=grpc
    Oct 09 15:25:43 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:43.750687781+08:00" level=info msg="Loading containers: start."
    Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.072960862+08:00" level=info msg="Default bridge (docke...dress"
    Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.153444071+08:00" level=info msg="Loading containers: done."
    Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.175249299+08:00" level=info msg="Docker daemon" commit...9.03.6
    Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.175337834+08:00" level=info msg="Daemon has completed ...ation"
    Oct 09 15:25:44 localhost.localdomain systemd[1]: Started Docker Application Container Engine.
    Oct 09 15:25:44 localhost.localdomain dockerd[1916]: time="2021-10-09T15:25:44.195084106+08:00" level=info msg="API listen on /var/ru....sock"
    Hint: Some lines were ellipsized, use -l to show in full.
    [root@localhost ~]# docker -v
    Docker version 19.03.6, build 369ce74a3c
    [root@localhost ~]# docker info
    

    方式二 :在线安装docker

    如果可以连接公网,建议在线安装。

    这里注意 linux和 docker的版本。

    尼恩安装 docker 最新版本的时候,发现依赖了 Centos 8 以上的版本。

    在线安装docker步骤

  • 更新yum
  • yum update
    
  • 安装工具包
  • yum -y install yum-utils
    
  • 设置yum源
  • yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    

    以腾讯源为例

    https://mirrors.cloud.tencent.com/docker-ce/linux/centos/docker-ce.repo
    
  • 安装Docker-Ce(社区版)
  • yum install docker-ce
    
  • 查看docker版本(用来确认是否安装成功)
  • # 输入 docker-v 后如果出现下面的内容则代表安装成功
    [root@VM-24-9-centos ~]# docker -v
    Docker version 20.10.23, build 7155243
    
  • Docker镜像加速(国内使用)
  • # 需要确定/etc下面是否有docker这个文件夹,若没有则需要使用下面的命令进行创建
    mkdir -p /etc/docker
    # 创建配置文件daemon.json
    vi /etc/docker/daemon.json
    # 写入以下内容
    "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com" # 可以替换为其他厂商的地址
    # 重载一下配置
    systemctl daemon-reload
    
  • 启动Docker服务
  • systemctl start docker
    
  • 验证docker是否可以使用
  • [root@VM-24-9-centos ~]# docker ps
    CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
    

    方式三 :用现成的 (大大的省事)

    docker+K8S 是一整套技术体系。

    但是,docker+K8S学习环境非常复杂,尼恩搞这个 前前后后起码折腾了一周。

    其中,很多头疼的工作,包括linux内核升级、磁盘扩容等等, 苦不堪言。

    现在把这个环境,以虚拟机box镜像的方式,导出来直接给大家,

    大家一键导入后,直接享受docker 的实操享受K8S的实操

    可以说,爽到不要不要的。

    以上环境和 尼恩个人的虚拟机box镜像,可以找尼恩获取。

    Docker Container概述

    什么是Container 容器

  • 通过Image创建(copy)
  • 在Image layer之上建立一个container layer(可读写)
  • 类比面向对象:类和实例
  • Image负责app的存储和分发,Container负责运行app
  • 容器与镜像的关系

  • 镜像:镜像是只读文件,提供运行程序完整的软硬件资源。
  • 容器:容器是镜像的实例,由docker负责创建,容器之间彼此隔离。
  • Docker本地容器相关的操作

    Container相关命令

  • 创建容器 docker run centos
  • 创建容器 docker run -it centos
  • 查看活跃容器 docker ps
  • 查询容器状态 docker container ls -a
  • 移除容器 docker container rm + [container ID]
  • 移除镜像 docker image rm + [image ID]
  • 显示所有containerID docker container ls -aq
  • 移除所有的container docker rm $(docker container ls -aq)
  • 创建名为"centos6"的容器,并在容器内部和宿主机中查看容器中的进程信息

    docker run -itd -p 6080:80 -p 6022:22 docker.io/lemonbar/centos6-ssh:latest
    
    [root@VM-4-17-centos ~]#    docker run -itd -p 80:80 -p 6022:22 docker.io/lemonbar/centos6-ssh:latest
    Unable to find image 'lemonbar/centos6-ssh:latest' locally
    latest: Pulling from lemonbar/centos6-ssh
    a3ed95caeb02: Pull complete
    f79eb1f22352: Pull complete
    67c1aaa530c8: Pull complete
    80447774eee7: Pull complete
    6d67b3a80e5a: Pull complete
    f1819e4b2f8f: Pull complete
    09712b5b9acc: Pull complete
    8bc987c5494f: Pull complete
    c42b021d0ff2: Pull complete
    Digest: sha256:093c2165b3c6fe05d5658343456f9b59bb7ecc690a7d3a112641c86083227dd1
    Status: Downloaded newer image for lemonbar/centos6-ssh:latest
    a4f1c9b8abcda78c8764cc285183dfa56cd1aa4ce6d111d4d9e77f3a57f3d5fc
    

    查看活跃容器 docker ps

    docker ps

    查看全部容器

    查询容器状态

    docker ps -a

    docker container ls -a

    两个命令,效果差不多

    docker stop id

    docker rm id

    查看容器的进程信息

    docker top:查看容器中运行的进程信息,支持 ps 命令参数。

    docker top [OPTIONS] CONTAINER [ps OPTIONS]
    

    容器运行时不一定有/bin/bash终端来交互执行top命令,而且容器还不一定有top命令,

    可以使用docker top来实现查看container中正在运行的进程。

    docker top 容器名称
    

    尼恩的虚拟机中,存在容器zookeeper/ mysql,如果想查看zookeeper/ mysql 容器内的运行进程信息,

    可以使用下述命令:

    docker top zookeeper
    docker top mysql
    

    如何查找容器名称?

    很多的命令,用到容器名称

    如何查找容器名称,可以使用下面的命令

    [root@localhost ~]# docker ps --format "{{.Names}}"
    

    结果如下:

    docker最为常用的几个命令

    docker的守护进程查看

    systemctl status docker

    docker 镜像查看

    docker image ls

    docker 容器查看

    docker ps

    Docker Registry配置和查看

    cat /etc/docker/daemon.json

    配置私有仓库
    cat>/etc/docker/daemon.json<<EOF
      "registry-mirrors":["http://10.24.2.30:5000","https://tnxkcso1.mirrors.aliyuncs.com"],
      "insecure-registries":["10.24.2.30:5000"]
    

    Docker容器进入的4种方式

    在使用Docker创建了容器之后,大家比较关心的就是如何进入该容器了,

    其实进入Docker容器有好几多种方式,这里我们就讲一下常用的几种进入Docker容器的方法。

    进入Docker容器比较常见的几种做法如下:

  • 使用docker attach
  • 使用SSH
  • 使用nsenter
  • 使用exec
  • 方式1:使用docker attach进入Docker容器

    Docker提供了attach命令来进入Docker容器。

    接下来我们创建一个守护态的Docker容器,然后使用docker attach命令进入该容器。

    sudo docker run -itd ubuntu:14.04 /bin/bash  
    

    然后我们使用docker ps查看到该容器信息,接下来就使用docker attach进入该容器

     docker attach c1437f4bd302  
    

    可以看到我们已经进入到该容器中了。

    但在,使用该命令有一个问题:

    当多个窗口同时使用该命令进入该容器时,所有的窗口都会同步显示。

    如果有一个窗口阻塞了,那么其他窗口也无法再进行操作。

    因为这个原因,所以docker attach命令不太适合于生产环境,平时自己开发应用时可以使用该命令。

    方式2:使用SSH进入Docker容器

    在生产环境中排除了使用docker attach命令进入容器之后,相信大家第一个想到的就是ssh。

    在镜像(或容器)中安装SSH Server,这样就能保证多人进入。

    容器且相互之间不受干扰了,相信大家在当前的生产环境中(没有使用Docker的情况)也是这样做的。

    但是使用了Docker容器之后不建议使用ssh进入到Docker容器内。

    方式3:使用nsenter进入Docker容器

    在上面两种方式都不适合的情况下,还有一种比较方便的方法,即使用nsenter进入Docker容器。

    1、什么是nsenter?

    nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。

    util-linux 是一个开放源码的软件包,是一个对任何 Linux 系统的基本工具套件。含有一些标准 Unix 工具,如 login。
    util-linux 软件包包含许多工具。其中比较重要的是加载、卸载、格式化、分区和管理硬盘驱动器,打开 tty 端口和得到内核消息。

    nsenter用途 ?

    一个最典型的用途就是进入容器的网络命令空间。相当多的容器为了轻量级,是不包含较为基础的命令的,比如说ip address,ping,telnet,ss,tcpdump等等命令,这就给调试容器网络带来相当大的困扰:只能通过docker inspect ContainerID命令获取到容器IP,以及无法测试和其他网络的连通性。这时就可以使用nsenter命令仅进入该容器的网络命名空间使用宿主机的命令调试容器网络。

    在了解了什么是nsenter之后,系统默认将我们需要的nsenter安装到主机中, nsenter --help 查看帮助

    nsenter --help 查看帮助

    $ nsenter --help
    nsenter [options] [program [arguments]]
    options:
    -t, --target pid:指定被进入命名空间的目标进程的pid
    -m, --mount[=file]:进入mount命令空间。如果指定了file,则进入file的命令空间
    -u, --uts[=file]:进入uts命令空间。如果指定了file,则进入file的命令空间
    -i, --ipc[=file]:进入ipc命令空间。如果指定了file,则进入file的命令空间
    -n, --net[=file]:进入net命令空间。如果指定了file,则进入file的命令空间
    -p, --pid[=file]:进入pid命令空间。如果指定了file,则进入file的命令空间
    -U, --user[=file]:进入user命令空间。如果指定了file,则进入file的命令空间
    -G, --setgid gid:设置运行程序的gid
    -S, --setuid uid:设置运行程序的uid
    -r, --root[=directory]:设置根目录
    -w, --wd[=directory]:设置工作目录
    如果没有给出program,则默认执行$SHELL。
    

    2、nsenter安装

    如果没有安装的话,按下面步骤安装即可(注意是主机而非容器或镜像)

    具体的安装命令如下:

    wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz
    tar -xzvf util-linux-2.24.tar.gz
    cd util-linux-2.24/
    ./configure --without-ncurses
    make nsenter
    sudo cp nsenter /usr/local/bin
    

    3、nsenter的 使用

    nsenter可以访问另一个进程的名称空间。

    所以为了连接到某个容器我们还需要获取该容器的第一个进程的PID。

    怎么办呢 ? 可以使用docker inspect命令来拿到该 进程的 PID。

    docker inspect 命令使用如下:

    sudo docker inspect --help
    

    inspect命令可以分层级显示一个镜像或容器的信息。

    比如我们当前有一个正在运行的容器

    可以使用docker inspect来查看该容器的详细信息。

    sudo docker inspect c1437f4bd302
    

    由其该信息非常多,此处只截取了其中一部分进行展示。如果要显示该容器第一个进行的PID可以使用如下方式

    sudo docker inspect -f {{.State.Pid}}    c1437f4bd302
    

    在拿到该进程PID之后我们就可以使用nsenter命令访问该容器了。

    sudo nsenter --target 22299 --mount --uts --ipc --net --pid
    

    其中的 22299 即刚才拿到的进程的PID

    输入该命令便进入到容器中

    $ nsenter --target 上面查到的进程id --mount --uts --ipc --net --pid
    

    解释nsenter指令中进程id之后的参数的含义:

    –mount参数是进去到mount namespace中 (文件系统)
    –uts参数是进入到uts namespace中 (主机名与域名)
    –ipc参数是进入到System V IPC namaspace中 (信号量、消息队列和共享内容)
    –net参数是进入到network namespace中 (网络设备、网络栈、端口)
    –pid参数是进入到pid namespace中 (进程编号)
    –user参数是进入到user namespace中 (用户和用户组)

    看看下面的例子,进入到容器的 network namespace中 ,看看IP地址,是不是变了。

    docker隔离应用应用涉及到的六大名称空间

    1、pid 命名空间(进程ID)

    不同用户的进程就是通过 pid 命名空间隔离开的,且不同命名空间中可以有相同 pid。

    所有的 LXC (Linux 容器)进程在 Docker 中的父进程为 Docker 进程,每个 LXC 进程具有不同的命名空间。

    同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。

    2、net 命名空间(网络)

    有了 pid 命名空间,每个命名空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。

    网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的 网络设备,IP 地址,路由表,/proc/net 目录。这样每个容器的网络就能隔离开来。

    Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。

    3、ipc 命名空间(进程间通信)

    容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。

    然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。

    4、mnt 命名空间(挂载文件系统)

    类似 chroot,将一个进程放到一个特定的目录执行。

    mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间 中的进程所看到的文件目录就被隔离开了。

    同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的 mount point。

    5、UTS 命名空间(主机名/域名)

    UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。

    6、user 命名空间(用户)

    每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

    nsenter查看docker的连接

    由于使用DOCKER的时候,ESTABLISHED连接不会出现在netstat中,在运行中的docker容器中列出打开的套接字的方法

    查看连接:

    sudo docker inspect -f {{.State.Pid}}    e9eaef999da9
    sudo nsenter -t  3473  -n netstat | grep ESTABLISHED    
    

    方式4:使用docker exec进入Docker容器

    除了上面几种做法之外,docker在1.3.X版本之后还提供了一个新的命令exec用于进入容器,这种方式相对更简单一些,下面我们来看一下该命令的使用:

    sudo docker exec --help
    

    接下来我们使用该命令进入一个已经在运行的容器

    sudo docker ps  
    sudo docker exec -it 容器id  /bin/bash  
    

    在容器内部和宿主机中查看容器中的进程信息

    进入一个名称为 rmqbroker-ha-b 的容器,查看进程信息

    docker exec -it rmqbroker-ha-b  /bin/bash   ps -ef
    

    结果如下:

    我们可以使用docker exec命令进入容器PID名空间,并执行应用。

    通过ps -ef命令,可以看到每个 rmqbroker-ha-b 容器都包含一个PID为1的进程,
    容器的启动进程是 "sh mqbroker -c /opt/rocketmq.....",具有特殊意义。

    利用docker top命令,可以让我们从宿主机操作系统中看到容器的进程信息。

    "sh mqbroker -c /opt/rocketmq....." 这个命令的进程,在 容器里边是1 ,在容器外部 3473

    查看其父进程信息

    ps -ef | grep 3401

    root        3401       1  0 12:06 ?        00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id e9eaef999da9183b9be0b3239881bc6b9c2070f13057c322dfed3d072820e962 -address /run/containerd/containerd.sock
    3000        3473    3401  0 12:06 ?        00:00:00 sh mqbroker -c /opt/rocketmq-4.6.0/conf/broker.conf autoCreateTopicEnable=true &
    root       26491   24084  0 16:16 pts/1    00:00:00 grep --color=auto 3401
    

    我们使用docker run 启用一个容器时,docker 会给每个容器都启动一个containerd-shim-runc-v2 父进程,这个进程又启动了一个ttrpc server(类似grpc/httpserver), containerd 通过 ttrpc和containerd-shim-runc-v2 通信来管理容器

    从父亲进程可以看到:容器的本质是进程。

    containerd-shim-runc-v2 后面的参数:namespace用来做命名空间隔离,cgroup用来做资源限制。

    containerd-shim-runc-v2 进程很特殊,它们跑在一些特定的namespace和cgroup下。

    站在这些进程的角度看,它们会以为自己跑在一个独立的机器上,看不到其他进程,也看不到其他文件。

    这是其实是操作系统为它虚拟出来的一个独立的、隔离的环境,是假的。

    查看子进程信息

    [root@VM-4-17-centos ~]#  ps aux | grep 27880
    

    总计三个命令

    docker top  rmqbroker-ha-b  #查看容器进程
    ps -ef | grep 3401 #查看父进程
    ps aux | grep 3473  #查看子进程(容器)
    

    Docker本地镜像载入与载出

  • 保存镜像(保存镜像载入后获得跟原镜像id相同的镜像)
  • 保存容器(保存容器载入后获得跟原镜像id不同的镜像)
  • 通过命令可以从镜像仓库中拉取镜像,默认从Docker Hub 获取。

    命令格式:

    docker image pull <repository>:<tag>
    docker image pull   rancher/rke-tools:v0.1.52
    [rancher/rke-tools:v0.1.52
    
  • docker save 镜像id -o /home/mysql.tar
  • docker save 镜像id > /home/mysql.tar
  • docker save  docker.io/rancher/rancher-agent   -o /home/rancher-agent .tar
    docker save  f29ece87a195  -o /home/rancher-agent.tar
    docker save  docker.io/rancher/rke-tools   -o /home/rke-tools-v0.1.52.tar
    
  • docker load -i mysql.tar
  • docker load -i /usr/local/rancher-v2.3.5.tar
    docker load -i /usr/local/rancher-agent.tar
    docker  inspect  f29ece87a1954772accb8a2332ee8c3fe460697e3f102ffbdc76eb9bc4f4f1d0
    docker load -i /usr/local/rke-tools-v0.1.52.tar
    
    docker load -i mysql.tar
    [root@localhost ~]# docker load -i /usr/local/rancher-v2.3.5.tar
    43c67172d1d1: Loading layer [==================================================>]  65.57MB/65.57MB
    21ec61b65b20: Loading layer [==================================================>]  991.2kB/991.2kB
    1d0dfb259f6a: Loading layer [==================================================>]  15.87kB/15.87kB
    f55aa0bd26b8: Loading layer [==================================================>]  3.072kB/3.072kB
    e0af200d6950: Loading layer [==================================================>]  126.1MB/126.1MB
    088ed892f9ad: Loading layer [==================================================>]  6.656kB/6.656kB
    6aa3142b4130: Loading layer [==================================================>]   34.5MB/34.5MB
    f4e84c05ab29: Loading layer [==================================================>]  70.41MB/70.41MB
    11a6e4332b53: Loading layer [==================================================>]  224.8MB/224.8MB
    46d1ac556da7: Loading layer [==================================================>]  3.072kB/3.072kB
    0f8b224a5802: Loading layer [==================================================>]  57.87MB/57.87MB
    519eba7d586a: Loading layer [==================================================>]  99.58MB/99.58MB
    3f8bb7c0c150: Loading layer [==================================================>]  4.608kB/4.608kB
    c22c9a5a8211: Loading layer [==================================================>]  3.072kB/3.072kB
    Loaded image: rancher/rancher:v2.3.5
    

    打个tag

    docker tag  f29ece87a1954772accb8a2332ee8c3fe460697e3f102ffbdc76eb9bc4f4f1d0 rancher/rancher-agent:v2.3.5
    docker tag  f29ece87a195   172.18.8.104/rancher/rancher-agent:v2.3.5
    docker tag 6e421b8753a2  172.18.8.104/rancher/rke-tools:v0.1.52 
    83fe4871cf67
    
    docker rmi image_name
    docker rmi  -f  172.18.8.104/rancher/coredns-coredns:1.6.5 
    docker rmi   -f   172.18.8.104/rancher/coredns-coredns:v3.4.3-rancher1
    docker rmi hub.doge.net/ubuntu:latest
    
  • docker export 镜像id -o /home/mysql-export.tar
  • docker save 镜像tag -o /home/mysql-export.tar
  • docker import mysql-export.tar
  • Harbor私有镜像仓库

    Harbor (港口,港湾)是一个用于存储和分发Docker镜像的企业级Registry服务器。

    除了Harbor这个私有镜像仓库之外,还有Docker官方提供的Registry。

    相对Registry,Harbor具有很多优势:

  • 提供分层传输机制,优化网络传输 Docker镜像是是分层的,而如果每次传输都使用全量文件(所以用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定传输的对象。
  • 提供WEB界面,优化用户体验 只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界面可以支持登陆、搜索功能,包括区分公有、私有镜像。
  • 支持水平扩展集群 当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力作分解。
  • 良好的安全机制 企业中的开发团队有很多不同的职位,对于不同的职位人员,分配不同的权限,具有更好的安全性。
  • Harbor安装

    harbor是用过docker-compose 编排的。

    所以 安装的过程中,会检查docker、docker-compose 进程,确保提前启动。

    这些,在咱们的虚拟机里边,docker、docker-compose 进程已经预装好了的。

    查看docker是否安装成功;

    [root@centos1 ~]# docker -v
    Docker version 20.10.23, build 7155243
    

    查看docker-compose是否安装成功;

    docker-compose -version
    [root@centos1 ~]# docker-compose -version
    docker-compose version 1.25.1, build a82fef07
    [root@centos1 ~]#
    

    接下来,开始安装harbor

    1、下载 Harbor的压缩包;

    https://github.com/goharbor/harbor/releases
    

    咱们用这个包: harbor-offline-installer-v2.3.2.tgz

    咱们的学习网盘当中,尼恩已经上传了哈

    2、上传压缩包到虚拟机,并解压;

    [root@centos1 ~]# cd /usr/local/
    [root@centos1 local]# mkdir harber
    [root@centos1 local]# cd harber/
    tar -zxvf harbor-offline-installer-v2.3.2.tgz
    

    3、创建harbor访问域名证书

    OpenSSL是一个强大的安全套接字层密码库,Apache使用它加密HTTPS,OpenSSH使用它加密SSH,但是,你不应该只将其作为一个库来使用,它还是一个多用途的、跨平台的密码工具。

    Harbor docs | Configure HTTPS Access to Harbor (goharbor.io)

    mkdir -p /usr/local/harbor/ssl && cd /usr/local/harbor/ssl
    openssl genrsa -out tls.key 4096
     openssl req -x509 -new -nodes -sha512 -days 3650 \
     -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \
     -key tls.key \
     -out tls.cert
    
  • 第一步创建ssl文件夹用来存储证书,
  • 第二步生成key (私钥),
  • 最后一步使用生成的key自签证书。自签证书包含公钥
  • days后面是你自签证书的有效时间可以自行修改。

    'CN='后面就写你自己的IP地址或者你自己的域名。

    生成完成之后显示如下

    4、配置harbor

    修改Harbor的配置(harbor.yml);
    修改主机地址:hostname: 192.168.56.121;
    修改端口(默认端口80):port: 85;

    cd /usr/local/harbor/harbor    #进入到harbor目录
    cp   harbor.yml.tmpl    harbor.yml
    vim harbor.yml   #编辑harbor的配置文件
    #修改以下内容
    hostname = 192.168.56.121  #修改harbor的启动ip,这里需要依据系统ip设置
    port: 85 #harbor的端口,有两个端口,http协议(80)和https协议(443)
    harbor_admin_password = 123456   #修改harbor的admin用户的密码
    data_volume: /harbor/data #修改harbor存储位置
    

    故harbor.yml中certificate填写为如上tls.cert的文件目录地址:/usr/local/harbor/ssl/tls.cert

    故harbor.yml中private_key填写为如上tls.key的文件目录地址:/usr/local/harbor/ssl/tls.key

    5、./prepare 准备

    启动之前需要使用./prepare命令进行一些预置工作,下载相关依赖;

    此时需要开启docker服务,不然会报错;

    这个过程可能有点长,需要耐心等待;

    /prepare
    

    6、./install.sh

    准备工作完成后,使用./install.sh进行Harbor的安装;
    这个过程会持续一段时间,耐心等待;

    ./install.sh
    

    http://cdh1:85/

    默认登录名:admin;
    默认登录密码:Harbor12345; 被尼恩改成了 12345
    具体可以查看harbor.yml;

    7、停止或者重启 Harbor

    cd /usr/local/harber/harbor/

    docker-compose up -d 启动
    docker-compose start 启动
    docker-compose stop 停止
    docker-compose restart 重新启动
    

    修改docker配置文件,使docker支持harbor

    编辑客户机/etc/docker/daemon.json文件

    {"insecure-registries":["192.168.56.121:85"]}

    重启客户机docker服务

    systemctl restart docker #或者(service docker restart)

    [root@centos1 harbor]# cat  /etc/docker/daemon.json
      "registry-mirrors": [
        "https://bjtzu1jb.mirror.aliyuncs.com",
        "http://f1361db2.m.daocloud.io",
        "https://hub-mirror.c.163.com",
        "https://docker.mirrors.ustc.edu.cn",
        "https://reg-mirror.qiniu.com",
        "https://dockerhub.azk8s.cn",
        "https://registry.docker-cn.com"
      "insecure-registries":["192.168.56.121:85"]
    
    [root@centos1 ssh]# cat /etc/docker/daemon.json
      "registry-mirrors": [
        "https://bjtzu1jb.mirror.aliyuncs.com",
        "http://f1361db2.m.daocloud.io",
        "https://hub-mirror.c.163.com",
        "https://docker.mirrors.ustc.edu.cn",
        "https://reg-mirror.qiniu.com",
        "https://dockerhub.azk8s.cn",
        "https://registry.docker-cn.com",
       "https://cdh1"
    

    Harbor使用

    可以 尝试上传镜像到 Harbor。

    如果是另一台机器,需要添加本地hosts

    echo '192.168.56.121 cdh' >> /etc/hosts
    

    因为我们的证书是自签的是不受到其他机器信任的,所以我们要在 harber的客户端机器上放上我们的证书,才能拉取镜像。

    使用下面的代码,在要使用 harber的客户端机器 上保存证书,其中 cdh1 部分使用你自己的域名或者是IP地址。

    mkdir -p /etc/docker/certs.d/cdh1
    scp 192.168.56.122:/usr/local/harbor/ssl/tls.cert /etc/docker/certs.d/cdh1/
    

    本地docker 登录的话

    mkdir -p /etc/docker/certs.d/cdh1
    cp /usr/local/harbor/ssl/tls.cert /etc/docker/certs.d/cdh1/ca.crt
    mkdir -p /etc/docker/certs.d/192.168.56.121
    

    然后使用如下语句登录harbor,如果没有使用80端口一定要用IP地址加上你的端口号,冒号之后填端口号,查看自己harbor.yml里http或https里的端口详情。

    docker login cdh1
    

    报错 证书问题, 证书不含SAN

    什么是含有SAN的证书

    docker 的新版本使用golang 1.15+版本上,老的x509 证书不行了

    SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。

    subjectAltName 在 RFC 5280 4.2.1.6.中提供了详细的说明,subjectAltName 是 X.509 version 3 的一个扩展项,该扩展项用于标记和界定证书持有者的身份。

    在 X.509 格式的证书中,一般使用 Issuer 项标记证书的颁发者信息,该项必须是一个非空的 Distinguished Name 名称。除此之外还可以使用扩展项 issuerAltName 来标记颁发者的其他名称,这是一个非关键的扩展项。

    对于证书持有者,一般使用 Subject 项标记,并使用 subjectAltName 扩展项提供更详细的持有者身份信息。 subjectAltName 全称为 Subject Alternative Name,缩写为 SAN。它可以包括一个或者多个的电子邮件地址,域名,IP地址和 URI 等,详细定义如下:

    SubjectAltName ::= GeneralNames
       GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
       GeneralName ::= CHOICE {
            otherName                       [0]     OtherName,
            rfc822Name                      [1]     IA5String,
            dNSName                         [2]     IA5String,
            x400Address                     [3]     ORAddress,
            directoryName                   [4]     Name,
            ediPartyName                    [5]     EDIPartyName,
            uniformResourceIdentifier       [6]     IA5String,
            iPAddress                       [7]     OCTET STRING,
            registeredID                    [8]     OBJECT IDENTIFIER 
    

    当颁发的证书不存在 Subject 项的时候,证书必须包含扩展项 subjectAltName,并且标记为关键(critical)的。当颁发的证书存在 Subject 项的时候,必须将扩展项 subjectAltName 标记为非关键(no-critical)的。注意:用于颁发证书的 CA 证书是必须包含 Subject 项的。

    根据 RFC 6125 中的规定,当一个网站使用证书标记自己的身份时,如果证书中包含 subjectAltName,在识别证书持有者时会忽略 Subject 子项,而是通过 subjectAltName 来识别证书持有者。

    在早期颁发的证书中一般通过 Subject 的 CommonName 来识别持有者的身份,不包含 subjectAltName 扩展项。

    这会导致最新版本的浏览器Chrome、Firefox 等在通过 HTTPS 访问 web 网站时,触发 NET::ERR_CERT_COMMON_NAME_INVALID 错误。

    SSL证书格式

    证书主要的文件类型和协议有:PEM、DER、PFX、JKS、KDB、CER、KEY、CSR、CRT、CRL 、OCSP、SCEP等。

    1. KEY

    一般指PEM格式的私钥文件。

    2. CRT

    证书文件。可以是PEM格式。

    3. PEM

    PEM格式的证书文件(*.pem)由Base64编码的二进制内容和开头行(-----BEGIN CERTIFICATE-----)、结束行(-----END CERTIFICATE-----)组成,支持使用notepad++等文本编辑器打开。对于CER、CRT格式的证书,您可通过直接修改证书文件扩展名的方式将其转换成PEM格式。例如,将server.crt证书文件直接重命名为server.pem。

    4. CSR

    证书请求文件(Certificate Signing Request)。生成 X509 数字证书前,一般先由用户提交证书申请文件,然后由 CA 来签发证书。大致过程如下(X509 证书申请的格式标准为 pkcs10 和 rfc2314):

  • 用户生成自己的公私钥对;
  • 构造自己的证书申请文件,符合 PKCS10 标准。该文件主要包括了用户信息、公钥以及一些可选的属性信息,并用自己的私钥给该内容签名;
  • 用户将证书申请文件提交给 CA;
  • CA 验证签名,提取用户信息,并加上其他信息(比如颁发者等信息),用 CA 的私钥签发数字证书;
  • 说明:数字证书(如x.509)是将用户(或其他实体)身份与公钥绑定的信息载体。一个合法的数字证书不仅要符合 X509 格式规范,还必须有 CA 的签名。用户不仅有自己的数字证书,还必须有对应的私钥。X509v3 数字证书主要包含的内容有:证书版本、证书序列号、签名算法、颁发者信息、有效时间、持有者信息、公钥信息、颁发者 ID、持有者 ID 和扩展项。
  • 5. DER

    辨别编码规则 (DER) 可包含所有私钥、公钥和证书。它是大多数浏览器的缺省格式,并按 ASN1 DER 格式存储。它是无报头的 - PEM 是用文本报头包围的 DER。

    6. PFX

    公钥加密标准 12 (PKCS12) 可包含所有私钥、公钥和证书。其以二进制格式存储,也称为 PFX 文件。通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式合并转换为标准的PFX文件,你可以将PFX文件格式导入到微软IIS 5/6、微软ISA、微软Exchange Server等软件。转换时需要输入PFX文件的加密密码。

    7. JKS

    通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式”转换为标准的Java Key Store(JKS)文件。JKS文件格式被广泛的应用在基于JAVA的WEB服务器、应用服务器、中间件。你可以将JKS文件导入到TOMCAT、 WEBLOGIC 等软件。

    8. KDB

    通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式转换为标准的IBM KDB文件。KDB文件格式被广泛的应用在IBM的WEB服务器、应用服务器、中间件。你可以将KDB文件导入到IBM HTTP Server、IBM Websphere 等软件。

    9. OCSP

    在线证书状态协议(OCSP,Online Certificate Status Protocol,rfc2560)用于实时表明证书状态。OCSP 客户端通过查询 OCSP 服务来确定一个证书的状态,可以提供给使用者一个或多个数字证书的有效性资料,它建立一个可实时响应的机制,让用户可以实时确认每一张证书的有效性,解决由CRL引发的安全问题。。OCSP 可以通过 HTTP协议来实现。rfc2560 定义了 OCSP 客户端和服务端的消息格式。

    10. CER

    一般指使用DER格式的证书。

    11. CRL

    证书吊销列表 (Certification Revocation List) 是一种包含撤销的证书列表的签名数据结构。CRL 是证书撤销状态的公布形式,CRL 就像信用卡的黑名单,用于公布某些数字证书不再有效。CRL 是一种离线的证书状态信息。它以一定的周期进行更新。CRL 可以分为完全 CRL和增量 CRL。在完全 CRL 中包含了所有的被撤销证书信息,增量 CRL 由一系列的 CRL 来表明被撤销的证书信息,它每次发布的 CRL 是对前面发布 CRL 的增量扩充。基本的 CRL 信息有:被撤销证书序列号、撤销时间、撤销原因、签名者以及 CRL 签名等信息。基于 CRL 的验证是一种不严格的证书认证。CRL 能证明在 CRL 中被撤销的证书是无效的。但是,它不能给出不在 CRL 中的证书的状态。如果执行严格的认证,需要采用在线方式进行认证,即 OCSP 认证。一般是由CA签名的一组电子文档,包括了被废除证书的唯一标识(证书序列号),CRL用来列出已经过期或废除的数字证书。它每隔一段时间就会更新,因此必须定期下载该清单,才会取得最新信息。

    12. SCEP

    简单证书注册协议。基于文件的证书登记方式需要从您的本地计算机将文本文件复制和粘贴到证书发布中心,和从证书发布中心复制和粘贴到您的本地计算机。 SCEP可以自动处理这个过程但是CRLs仍然需要手工的在本地计算机和CA发布中心之间进行复制和粘贴。

    13. PKCS7

    加密消息语法(pkcs7),是各种消息存放的格式标准。这些消息包括:数据、签名数据、数字信封、签名数字信封、摘要数据和加密数据。

    14. PKCS12

    – pkcs12 (个人数字证书标准)用于存放用户证书、crl、用户私钥以及证书链。pkcs12 中的私钥是加密存放的。

    生成含有SAN的证书

  • 使用OPENssl命令行来生成KEY+CSR2个文件,
  • 使用KEYTOOL来生成JKS和CSR文件
  • 1、生成CA证书私钥

    openssl genrsa -out ca.key 4096

    [root@docker-compose-harbor CA]# openssl genrsa -out ca.key 4096
    Generating RSA private key, 4096 bit long modulus
    ...............................................................++
    ............................................................++
    e is 65537 (0x10001)
    [root@docker-compose-harbor CA]#
    [root@docker-compose-harbor CA]#
    [root@docker-compose-harbor CA]# ls
    ca.key
    [root@docker-compose-harbor CA]#
    

    2、生成CA证书

    openssl req -x509 -new -nodes -sha512 -days 3650 \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \
    -key ca.key \
    -out ca.crt
    

    自签名的证书,不被浏览器信任,适合内部或者测试使用。

    3、生成服务器证书

    [root@docker-compose-harbor CA]# openssl genrsa -out cdh1.key 4096
    Generating RSA private key, 4096 bit long modulus
    .........................................................................................................................++
    ...........................................................................................................................................................................................................++
    e is 65537 (0x10001)
    [root@docker-compose-harbor CA]#
    [root@docker-compose-harbor CA]# ll -h
    total 12K
    -rw-r--r--. 1 root root 3.2K Jul 20 04:52 cdh1.key
    -rw-r--r--. 1 root root 2.0K Jul 20 04:51 ca.crt
    -rw-r--r--. 1 root root 3.2K Jul 20 04:48 ca.key
    [root@docker-compose-harbor CA]#
    
    生成证书签名请求(CSR)

    制作过程中,系统会产生2个密钥,一个是私钥,存放在服务器上,一个是CSR文件公钥,需要ca签名。

    openssl req -sha512 -new \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \
    -key cdh1.key \
    -out cdh1.csr
    

    需要依次输入国家,地区,城市,组织,组织单位,Common Name和Email。

    其中Common Name,可以写自己的名字或者域名,如果要支持https,Common Name应该与域名保持一致,否则会引起浏览器警告。

    可以将证书发送给证书颁发机构(CA),CA验证过请求者的身份之后,会出具签名证书,需要花钱。

    另外,如果只是内部或者测试需求,也可以使用OpenSSL实现自签名。

    [root@docker-compose-harbor CA]# openssl req -sha512 -new \
    > -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1" \
    > -key cdh1.key \
    > -out cdh1.csr
    [root@docker-compose-harbor CA]# ll -h
    total 20K
    -rw-r--r--. 1 root root 1.7K Jul 20 04:59 cdh1.csr
    -rw-r--r--. 1 root root 3.2K Jul 20 04:52 cdh1.key
    -rw-r--r--. 1 root root 2.0K Jul 20 04:51 ca.crt
    -rw-r--r--. 1 root root 3.2K Jul 20 04:48 ca.key
    [root@docker-compose-harbor CA]#
    生成一个x509 v3扩展文件
    cat > v3.ext <<-EOF
    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = IP:cdh1
    

    4、使用该v3.ext文件为Harbor主机生成证书cdh1.crt

    openssl x509 -req -sha512 -days 3650 \
    -extfile v3.ext \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -in cdh1.csr \
    -out cdh1.crt
    
    [root@docker-compose-harbor CA]# openssl x509 -req -sha512 -days 3650 \
    > -extfile v3.ext \
    > -CA ca.crt -CAkey ca.key -CAcreateserial \
    > -in cdh1.csr \
    > -out cdh1.crt
    Signature ok
    subject=/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=cdh1
    Getting CA Private Key
    [root@docker-compose-harbor CA]#
    

    当服务端向 CA 机构申请证书的时候,CA 签发证书的过程:

  • 首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
  • 然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
  • 最后将 Certificate Signature 添加在文件证书上,形成数字证书;
  • 5、转换cdh1.crt为cdh1.cert,供Docker使用

    转换证书格式

    Docker守护程序将.crt文件解释为CA证书,并将.cert文件解释为客户端证书

    openssl x509 -inform PEM -in cdh1.crt -out cdh1.cert
    

    修改harbor.yml文件key路径

    修改对应的证书地址:

  • cdh1.cert 的地址
  • cdh1.key 的地址
  • 故harbor.yml中certificate填写为如上cdh1.cert的文件目录地址:/usr/local/harbor/ssl/cdh1.cert

    故harbor.yml中private_key填写为如上cdh1.key的文件目录地址:/usr/local/harbor/ssl/cdh1.key

    [root@docker-compose-harbor harbor]# egrep -v "^$|^#" harbor.yml |head -10
    hostname: cdh1
    http:
    # port for http, default is 80. If https enabled, this port will redirect to https port
    port: 80
    https:
    # https port for harbor, default is 443
    port: 443
    # The path of cert and key files for nginx
    certificate: /opt/CA/harbor/cert/cdh1.crt
    private_key: /opt/CA/harbor/cert/cdh1.key
    [root@docker-compose-harbor harbor]#
    

    6、运行prepare脚本以启用HTTPS

    ./prepare

    [root@docker-compose-harbor harbor]# ./prepare
    prepare base dir is set to /opt/harbor/harbor
    Generated configuration file: /config/portal/nginx.conf
    Generated configuration file: /config/log/logrotate.conf
    Generated configuration file: /config/log/rsyslog_docker.conf
    Generated configuration file: /config/nginx/nginx.conf
    Generated configuration file: /config/core/env
    Generated configuration file: /config/core/app.conf
    Clean up the input dir
    [root@docker-compose-harbor harbor]#
    

    7、运行install.sh脚本来启动harbor

    ./install.sh

    [root@docker-compose-harbor harbor]# ./install.sh 
    [Step 0]: checking if docker is installed ...
    Note: docker version: 20.10.17
    [Step 1]: checking docker-compose is installed ...
    

    证书复制到 docker 并且启动后登录

    cp /usr/local/harbor/ssl/cdh1.cert /etc/docker/certs.d/cdh1/ca.crt
    systemctl daemon-reload && systemctl restart docker
    

    hostname push失败

    push本地镜像到 harbor私服时,push 到 docker.io仓库去了

    在配置insecure-registry时,docker 必须配置服务器的 FQDN或者IP地址.不能是服务器的hostname(比如harbor)

    尼恩配置的 是cdh1,推不上去。

    FQDN是什么意思?
    FQDN(fully qualified domain name)完全限定域名,是互联网上特定计算机或主机的完整域名。

    FQDN 由两部分组成:主机名和域名。

    例如,假设邮件服务器的 FQDN 可能是 mail.chenweiliang.com 。
    主机名为mail,主机位于域名chenweiliang.com。
    DNS(Domain Name System),负责将 FQDN 转换为 IP地址,是 Internet 上大多数应用程序的寻址方式。
    FQDN:(Fully Qualified Domain Name)完全限定域名:同时包含主机名和域名的名称。 (通过符号“.”)

    以下为解决方法:

    配置harbor服务器的 /etc/hosts,将本地ip地址对应为一条FQDN记录,

    比如 192.168.56.121 harbor.daemon.io

    停止harbor后,修改harbor配置 harbor.yml 文件,将 hostname 配置项改为 harbor.daemon.io (一个FQDN),然后重新配置 harbor.

    docker-compose down -v

    生成配置文件

    ./prepare

    启动habror

    docker-comp up -d

    (可以在另外一台能访问harbor服务器的机器上配置 hosts 记录为 FQDN,然后web访问 harbor检查是否能正常登陆.)

    重新配置 docker daemon 中的配置 registry-mirrors 和 insecure-registries 然后重启 docker.

    编辑客户机/etc/docker/daemon.json文件

    {"insecure-registries":["http://harbor.daemon.io:85"]}

    重启客户机docker服务

    systemctl restart docker #或者(service docker restart)

    之后就能正常pull,并且push本地镜像到 harbor私服中.

    附上完整的:

    "registry-mirrors": [ "https://bjtzu1jb.mirror.aliyuncs.com", "http://f1361db2.m.daocloud.io", "https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn", "https://reg-mirror.qiniu.com", "https://dockerhub.azk8s.cn", "https://registry.docker-cn.com", "https://harbor.daemon.io" "insecure-registries":["http://harbor.daemon.io:85"]

    推送镜像到Harber

    Docker 推送命令

    在项目中标记镜像:

    docker tag SOURCE_IMAGE[:TAG] harbor.daemon.io/demo/REPOSITORY[:TAG]

    docker tag  nginx:latest  harbor.daemon.io/demo/nginx:latest
    

    推送镜像到当前项目:

    docker push harbor.daemon.io/demo/REPOSITORY[:TAG]

    docker push harbor.daemon.io/demo/nginx:latest
    

    错误提升:

    [root@centos1 harbor]# docker push harbor.daemon.io/demo/nginx:latest
    The push refers to repository [harbor.daemon.io/demo/nginx]
    Get "https://harbor.daemon.io/v2/": x509: certificate is valid for cdh1, not harbor.daemon.io
    

    需要生成证书

    openssl req -x509 -new -nodes -sha512 -days 3650 \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=harbor.daemon.io" \
    -key ca.key \
    -out ca.crt
    openssl genrsa -out harbor.daemon.io.key 4096
    openssl req -sha512 -new \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=harbor.daemon.io" \
    -key harbor.daemon.io.key \
    -out harbor.daemon.io.csr
    生成一个x509 v3扩展文件
    cat > harbor.daemon.io.v3.ext <<-EOF
    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = DNS:harbor.daemon.io
    openssl x509 -req -sha512 -days 3650 \
    -extfile harbor.daemon.io.v3.ext \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -in harbor.daemon.io.csr \
    -out harbor.daemon.io.crt
    openssl x509 -inform PEM -in harbor.daemon.io.crt -out harbor.daemon.io.cert
    

    harbor.yml中certificate填写为如上cdh1.cert的文件目录地址:

    /usr/local/harbor/ssl/harbor.daemon.io.cert

    harbor.yml中private_key填写为如上cdh1.key的文件目录地址:

    /usr/local/harbor/ssl/harbor.daemon.io.key

    mkdir /etc/docker/certs.d/harbor.daemon.io
    cp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/ca.crt
    cp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/harbor.daemon.io.cert 
    cp /usr/local/harbor/ssl/harbor.daemon.io.cert /etc/docker/certs.d/harbor.daemon.io/ca.crt
    docker-compose down
    prepare
    systemctl restart docker 
    docker-compose up -d
    docker login  harbor.daemon.io
    

    推送镜像到当前项目:

    docker push harbor.daemon.io/demo/REPOSITORY[:TAG]

    docker push harbor.daemon.io/demo/nginx:latest
    

    http://cdh1:85/

    Docker Image概述

    什么是Image

  • 文件和meta data的集合(root filesystem)
  • 分层的,并且每一层都可以添加改变删除文件,成为一个新的image
  • 不同的image可以共享相同的layer
  • Image本身是read-only的
  • Image的获取

  • 方式1:Build from Dockerfile
  • 方式2:Pull from Registry
  • 例如:输入命令 sudo docker pull ubuntu:14.04,则会从Registry中拉出Image,Registry类似于github的作用,默认都是从docker hub上面拉取,上面会有官方和第三方的版本

    输入命令 sudo docker image ls则可以显示所拉去的Image

    如何做一个自己的Base Image

    1.首先创建一个可以执行的程序,下面用一个C语言的hello程序做例子

    2.通过dockerfile把这个可执行文件打成一个Image
    我们在hellow文件当前目录下创建一个Dockerfile文件,如下:

    3.执行命令:docker build -t yunduan/hello-world . (-t表示指示表情,"."表示在当前路径下寻找dockerfile),执行以后,出现如下界面表示执行成功。

    4.查看image,则可以看到成功build了一个image,执行命令docker history +[IMAGE ID]则可以查看镜像的层级
    执行命令docker run + [镜像标签名]则可以生成一个container运行程序。

    构建自己的Docker镜像

  • 命令一:docker container commit(Create a new image from a container's changes)
  • 这个命令表示当你在容器中做出了改变之后,可以重新构建Image

    通过这个例子可以看出,其就在centos镜像上重新构建了一层

  • 命令二:docker image build(Build an image from a Dockerfile)
  • Dockerfile语法

    原则:尽量使用官方的image作为base image!

    FROM scratch #制作base image
    FROM centos  #使用base image
    FROM ubuntu:14.04
    
  • LABEL
  • 原则:Metadata不可少!相当于代码的注释

    LABEL maintainer="[email protected]"
    LABEL version="1.0"
    LABEL description="This is description"
    

    作用:执行命令并创建新的Image Layer

    原则:为了美观,复杂的RUN请用反斜线换行!避免无用分层,合并多条命令成一行!

    RUN yum update && yum install -y vim \
         python-dev   #反斜线换行
    RUN apt-get update && apt-get install -y perl \
        pwgen --no-install-recommends && rm -rf \
        /var/lib/apt/lists/*    #注意清理cache
    RUN /bin/bash -c 'source $HOME/.bashrc;echo
    $HOME'
    
  • WORKDIR
  • 作用:设定当前目录,类似于cd

    原则:用WORKDIR,不要用RUN cd!尽量使用绝对目录

    WORKDIR /root
    WORKDIR /test  #如果没有会自动创建test目录
    WORKDIR demo
    RUN pwd        #输出结果应该是 /test/demo
    
  • ADD and COPY
  • 作用:把本地文件添加到Docker image里面

    原则:大部分情况,COPY优先于ADD!ADD除了COPY还有额外功能(解压)!添加远程文件/目录请使用curl或者wget!

    ADD hello /
    ADD test.tar.gz /   #添加到根目录并解压
    WORKDIR /root
    ADD hello test/     # /root/test/hello
    WORKDIR /root
    COPY hello test/
    

    作用:设置一个环境变量,引用常量

    原则:尽量使用ENV增加可维护性!

    ENV MYSQL_VERSION 5.6    # 设置常量
    RUN apt-get install -y mysql-server= "${MYSQL_VERSION}" \
        && rm -rf /var/lib/apt/lists/*   # 引用常量
    
  • VOLUME and EXPOSE (存储和网络)
  • CMD and ENTRYPOINT
  • CMD:设置容器启动后默认执行的命令和参数

    注释:1.容器启动时默认执行的命令 2.如果docker run指定了其他命令,CMD命令被忽略 3.如果定义了多个CMD,只有最后一个会执行

    ENTRYPOINT:设置容器启动时运行的命令

    注释:1.让容器以应用程序或者服务的形式运行 2.不会被忽略,一定会执行 3.最佳实践:写一个shell脚本作为entrypoint

    1.Shell格式

    RUN apt-get install -y vim
    CMD echo "hello docker"
    ENTRYPOINT echo "hello docker"
    

    2.Exec格式

    RUN ["apt-get","install","-y","vim"]
    CMD [" /bin/echo" , "hello docker" ]
    ENTRYPOINT ["/bin/echo" , "hello docker"]
    

    3.Shell和Exec格式

  • Dockerfile1 A
  • FROM centos
    ENV name Docker
    ENTRYPOINT echo "hello $name"
    
  • Dockerfile2
  • FROM centos
    ENV name Dokcer
    ENTRYPOINT ["/bin/bash", "-c","echo hello $name" ]
    
  • docker login
  • docker push
  • Docker进程与宿主机进程的对应关系

    Linux通过进程ID查看文件路径

    子进程的文件路径

    [root@VM-4-17-centos ~]#  ls -l /proc/27880
    total 0
    dr-xr-xr-x 2 root root 0 Nov  3 16:41 attr
    -rw-r--r-- 1 root root 0 Nov  3 16:41 autogroup
    -r-------- 1 root root 0 Nov  3 16:41 auxv
    -r--r--r-- 1 root root 0 Nov  3 16:14 cgroup
    --w------- 1 root root 0 Nov  3 16:41 clear_refs
    -r--r--r-- 1 root root 0 Nov  3 16:15 cmdline
    -rw-r--r-- 1 root root 0 Nov  3 16:41 comm
    -rw-r--r-- 1 root root 0 Nov  3 16:41 coredump_filter
    -r--r--r-- 1 root root 0 Nov  3 16:41 cpuset
    lrwxrwxrwx 1 root root 0 Nov  3 16:41 cwd -> /
    -r-------- 1 root root 0 Nov  3 16:41 environ
    lrwxrwxrwx 1 root root 0 Nov  3 16:14 exe -> /usr/sbin/sshd
    dr-x------ 2 root root 0 Nov  3 16:14 fd
    dr-x------ 2 root root 0 Nov  3 16:41 fdinfo
    -rw-r--r-- 1 root root 0 Nov  3 16:41 gid_map
    -r-------- 1 root root 0 Nov  3 16:41 io
    -r--r--r-- 1 root root 0 Nov  3 16:41 limits
    -rw-r--r-- 1 root root 0 Nov  3 16:41 loginuid
    dr-x------ 2 root root 0 Nov  3 16:41 map_files
    -r--r--r-- 1 root root 0 Nov  3 16:41 maps
    -rw------- 1 root root 0 Nov  3 16:41 mem
    -r--r--r-- 1 root root 0 Nov  3 16:14 mountinfo
    -r--r--r-- 1 root root 0 Nov  3 16:41 mounts
    -r-------- 1 root root 0 Nov  3 16:41 mountstats
    dr-xr-xr-x 5 root root 0 Nov  3 16:41 net
    dr-x--x--x 2 root root 0 Nov  3 16:14 ns
    -r--r--r-- 1 root root 0 Nov  3 16:41 numa_maps
    -rw-r--r-- 1 root root 0 Nov  3 16:41 oom_adj
    -r--r--r-- 1 root root 0 Nov  3 16:41 oom_score
    -rw-r--r-- 1 root root 0 Nov  3 16:41 oom_score_adj
    -r--r--r-- 1 root root 0 Nov  3 16:41 pagemap
    -r-------- 1 root root 0 Nov  3 16:41 patch_state
    -r--r--r-- 1 root root 0 Nov  3 16:41 personality
    -rw-r--r-- 1 root root 0 Nov  3 16:41 projid_map
    lrwxrwxrwx 1 root root 0 Nov  3 16:41 root -> /
    -rw-r--r-- 1 root root 0 Nov  3 16:41 sched
    -r--r--r-- 1 root root 0 Nov  3 16:41 schedstat
    -r--r--r-- 1 root root 0 Nov  3 16:41 sessionid
    -rw-r--r-- 1 root root 0 Nov  3 16:41 setgroups
    -r--r--r-- 1 root root 0 Nov  3 16:41 smaps
    -r--r--r-- 1 root root 0 Nov  3 16:41 stack
    -r--r--r-- 1 root root 0 Nov  3 16:14 stat
    -r--r--r-- 1 root root 0 Nov  3 16:41 statm
    -r--r--r-- 1 root root 0 Nov  3 16:14 status
    -r--r--r-- 1 root root 0 Nov  3 16:41 syscall
    dr-xr-xr-x 3 root root 0 Nov  3 16:41 task
    -r--r--r-- 1 root root 0 Nov  3 16:41 timers
    -rw-r--r-- 1 root root 0 Nov  3 16:14 uid_map
    -r--r--r-- 1 root root 0 Nov  3 16:41 wchan
    

    以下是/proc目录中进程27880的信息说明:

    proc/27880 pid为N的进程信息
    /proc/27880/cmdline 进程启动命令
    /proc/27880/cwd 链接到进程当前工作目录
    /proc/27880/environ 进程环境变量列表
    /proc/27880/exe 链接到进程的执行命令文件
    /proc/27880/fd 包含进程相关的所有的文件描述符
    /proc/27880/maps 与进程相关的内存映射信息
    /proc/27880/mem 指代进程持有的内存,不可读
    /proc/27880/root 链接到进程的根目录
    /proc/27880/stat 进程的状态
    /proc/27880/statm 进程使用的内存的状态
    /proc/27880/status 进程状态信息,比stat/statm更具可读性
    

    容器的PID namespace(命名空间)

    在Docker中,进程管理的基础就是Linux内核中的PID名空间技术。

    在不同PID名空间中,进程ID是独立的;即在两个不同名空间下的进程可以有相同的PID。

    在Docker中,每个Container进程缺省都具有不同的PID名空间。通过名空间技术,Docker实现容器间的进程隔离。

    docker中运行的容器进程,本质上还是运行在宿主机上的,所以也会拥有相对应的PID

    找出容器ID
    docker ps
    
    [root@VM-4-17-centos ~]# docker ps
    CONTAINER ID        IMAGE                                     COMMAND                  CREATED             STATUS                          PORTS                                              NAMES
    460d68823930        lemonbar/centos6-ssh:latest               "/bin/sh -c '/usr/sb…"   32 minutes ago      Up 32 minutes                   0.0.0.0:6021->22/tcp, 0.0.0.0:6081->80/tcp         centos6-2
    

    查看容器信息

    docker inspect id
    
    [root@VM-4-17-centos ~]# docker inspect  460d68823930
    [root@VM-4-17-centos ~]#  docker inspect  460d68823930
            "Id": "460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd",
            "Created": "2021-11-03T08:24:36.934129599Z",
            "Path": "/bin/sh",
            "Args": [
                "-c",
                "/usr/sbin/sshd -D"
            "State": {
                "Status": "running",
                "Running": true,
                "Paused": false,
                "Restarting": false,
                "OOMKilled": false,
                "Dead": false,
                "Pid": 4962,
                "ExitCode": 0,
                "Error": "",
                "StartedAt": "2021-11-03T08:24:37.223255812Z",
                "FinishedAt": "0001-01-01T00:00:00Z"
            "Image": "sha256:efd998bd6817af509d348b488e3ce4259f9f05632644a7bf574b785bbc8950b8",
            "ResolvConfPath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/resolv.conf",
            "HostnamePath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/hostname",
            "HostsPath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/hosts",
            "LogPath": "/var/lib/docker/containers/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd-json.log",
            "Name": "/centos6-2",
            "RestartCount": 0,
            "Driver": "overlay2",
            "Platform": "linux",
            "MountLabel": "",
            "ProcessLabel": "",
            "AppArmorProfile": "",
            "ExecIDs": null,
            "HostConfig": {
                "Binds": null,
                "ContainerIDFile": "",
                "LogConfig": {
                    "Type": "json-file",
                    "Config": {}
                "NetworkMode": "default",
                "PortBindings": {
                    "22/tcp": [
                            "HostIp": "",
                            "HostPort": "6021"
                    "80/tcp": [
                            "HostIp": "",
                            "HostPort": "6081"
                "RestartPolicy": {
                    "Name": "no",
                    "MaximumRetryCount": 0
                "AutoRemove": false,
                "VolumeDriver": "",
                "VolumesFrom": null,
                "CapAdd": null,
                "CapDrop": null,
                "Dns": [],
                "DnsOptions": [],
                "DnsSearch": [],
                "ExtraHosts": null,
                "GroupAdd": null,
                "IpcMode": "shareable",
                "Cgroup": "",
                "Links": null,
                "OomScoreAdj": 0,
                "PidMode": "",
                "Privileged": false,
                "PublishAllPorts": false,
                "ReadonlyRootfs": false,
                "SecurityOpt": null,
                "UTSMode": "",
                "UsernsMode": "",
                "ShmSize": 67108864,
                "Runtime": "runc",
                "ConsoleSize": [
                "Isolation": "",
                "CpuShares": 0,
                "Memory": 0,
                "NanoCpus": 0,
                "CgroupParent": "",
                "BlkioWeight": 0,
                "BlkioWeightDevice": [],
                "BlkioDeviceReadBps": null,
                "BlkioDeviceWriteBps": null,
                "BlkioDeviceReadIOps": null,
                "BlkioDeviceWriteIOps": null,
                "CpuPeriod": 0,
                "CpuQuota": 0,
                "CpuRealtimePeriod": 0,
                "CpuRealtimeRuntime": 0,
                "CpusetCpus": "",
                "CpusetMems": "",
                "Devices": [],
                "DeviceCgroupRules": null,
                "DiskQuota": 0,
                "KernelMemory": 0,
                "MemoryReservation": 0,
                "MemorySwap": 0,
                "MemorySwappiness": null,
                "OomKillDisable": false,
                "PidsLimit": 0,
                "Ulimits": null,
                "CpuCount": 0,
                "CpuPercent": 0,
                "IOMaximumIOps": 0,
                "IOMaximumBandwidth": 0
            "GraphDriver": {
                "Data": {
                    "LowerDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6-init/diff:/var/lib/docker/overlay2/7139bf0b716c6e0b6a0c709b7043466f9bbfd7024f8ae584061c00b0bd97348c/diff:/var/lib/docker/overlay2/66a3e278259cdcf50741ce30a115baa3bd6247a60c487e4118e85f2f39328f11/diff:/var/lib/docker/overlay2/20e22c4c28ebadb615eb4c7c290253d3eb91cb49722ee2931b0ee628352a5857/diff:/var/lib/docker/overlay2/a3fa9dbebc83a853083205b8f7921c632cd67f64531f4a25cab419a43172e3ae/diff:/var/lib/docker/overlay2/3af7958c9a4e54d24598058a9fa1e85eb35e3d40f766fa498a674b52724ae73e/diff:/var/lib/docker/overlay2/becb65af4396137ed41fe6d516e834e6e6e9120f4edfac8e2ca8dd67cce23268/diff:/var/lib/docker/overlay2/fef055305158cc96906514c447f0eaea05945138896b0b35ac4146b6a2a3e273/diff:/var/lib/docker/overlay2/79158cdf3ba832493ab0d02d560c784208fe51c74236a5a86f7fb4fb50ab6e44/diff:/var/lib/docker/overlay2/86258a18e1110582b819719593687f11f0404d00a41667b3432c3b974fb1ce42/diff:/var/lib/docker/overlay2/8826b2e0068653fb2c5e8a3dbf839470e2b8eef8cf752b5fe901bea1b210849f/diff:/var/lib/docker/overlay2/145301e2738a8a7581c2bbd5beb9bf7a49b247e46642b8084efbc026a1826116/diff:/var/lib/docker/overlay2/f621f37535e0db1fe44902e22dba7ef0844b9a8b562a9daa39a842a49e9cc9bb/diff:/var/lib/docker/overlay2/7b493e4a97907aaa18b97ad2e9120b5bf87c0e9908ee390a35ea6ff546d8cec6/diff",
                    "MergedDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6/merged",
                    "UpperDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6/diff",
                    "WorkDir": "/var/lib/docker/overlay2/6835c1b48237aafe27e2efabeda92a3a6623f254f88d54b5e6acce454e560dd6/work"
                "Name": "overlay2"
            "Mounts": [],
            "Config": {
                "Hostname": "460d68823930",
                "Domainname": "",
                "User": "",
                "AttachStdin": false,
                "AttachStdout": false,
                "AttachStderr": false,
                "ExposedPorts": {
                    "22/tcp": {},
                    "80/tcp": {}
                "Tty": true,
                "OpenStdin": true,
                "StdinOnce": false,
                "Env": [
                    "HOME=/",
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
                "Cmd": [
                    "/bin/sh",
                    "-c",
                    "/usr/sbin/sshd -D"
                "Image": "docker.io/lemonbar/centos6-ssh:latest",
                "Volumes": null,
                "WorkingDir": "",
                "Entrypoint": null,
                "OnBuild": null,
                "Labels": {}
            "NetworkSettings": {
                "Bridge": "",
                "SandboxID": "ea66261fb6d8089d5b2d585a2dc32b2003365df7118f5f5e898a152fb5b35773",
                "HairpinMode": false,
                "LinkLocalIPv6Address": "",
                "LinkLocalIPv6PrefixLen": 0,
                "Ports": {
                    "22/tcp": [
                            "HostIp": "0.0.0.0",
                            "HostPort": "6021"
                    "80/tcp": [
                            "HostIp": "0.0.0.0",
                            "HostPort": "6081"
                "SandboxKey": "/var/run/docker/netns/ea66261fb6d8",
                "SecondaryIPAddresses": null,
                "SecondaryIPv6Addresses": null,
                "EndpointID": "09ad719a4e9115ee56c5fb0f5b0d39c50bf5acaf0a1afacedc13969c82a2969f",
                "Gateway": "172.17.0.1",
                "GlobalIPv6Address": "",
                "GlobalIPv6PrefixLen": 0,
                "IPAddress": "172.17.0.6",
                "IPPrefixLen": 16,
                "IPv6Gateway": "",
                "MacAddress": "02:42:ac:11:00:06",
                "Networks": {
                    "bridge": {
                        "IPAMConfig": null,
                        "Links": null,
                        "Aliases": null,
                        "NetworkID": "2586283d16a08210c955d705f05e0f6999b59523a84b0c163e33f535af809ddd",
                        "EndpointID": "09ad719a4e9115ee56c5fb0f5b0d39c50bf5acaf0a1afacedc13969c82a2969f",
                        "Gateway": "172.17.0.1",
                        "IPAddress": "172.17.0.6",
                        "IPPrefixLen": 16,
                        "IPv6Gateway": "",
                        "GlobalIPv6Address": "",
                        "GlobalIPv6PrefixLen": 0,
                        "MacAddress": "02:42:ac:11:00:06",
                        "DriverOpts": null
    

    进入相应目录

    cd /sys/fs/cgroup/memory/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/
    
    cd /sys/fs/cgroup/memory/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd/
    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# ll
    total 0
    -rw-r--r-- 1 root root 0 Nov  3 16:24 cgroup.clone_children
    --w--w--w- 1 root root 0 Nov  3 16:24 cgroup.event_control
    -rw-r--r-- 1 root root 0 Nov  3 16:24 cgroup.procs
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.failcnt
    --w------- 1 root root 0 Nov  3 16:24 memory.force_empty
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.failcnt
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.limit_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.max_usage_in_bytes
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.slabinfo
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.tcp.failcnt
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.tcp.limit_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.tcp.max_usage_in_bytes
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.tcp.usage_in_bytes
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.kmem.usage_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.limit_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.max_usage_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.memsw.failcnt
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.memsw.limit_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.memsw.max_usage_in_bytes
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.memsw.usage_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.move_charge_at_immigrate
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.numa_stat
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.oom_control
    ---------- 1 root root 0 Nov  3 16:24 memory.pressure_level
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.soft_limit_in_bytes
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.stat
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.swappiness
    -r--r--r-- 1 root root 0 Nov  3 16:24 memory.usage_in_bytes
    -rw-r--r-- 1 root root 0 Nov  3 16:24 memory.use_hierarchy
    -rw-r--r-- 1 root root 0 Nov  3 16:24 notify_on_release
    -rw-r--r-- 1 root root 0 Nov  3 16:24 tasks
    
    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs
    11539
    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat pids.max
    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat tasks
    11539
    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.clone_children
    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# pwd
    /sys/fs/cgroup/pids/docker/460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd
    

    查看容器目录里的进程号

    进程号就存在一个文件里面

    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# 
    cat cgroup.procs
    

    与前面利用docker top命令,可以让我们从宿主机操作系统中看到容器的进程信息。

    [root@VM-4-17-centos ~]# docker top centos6-2
    UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
    root                4962                4948                0                   16:24               pts/0               00:00:00            /usr/sbin/sshd -D
    

    启动一个进程

    我们下面会在 centos6-2容器中,利用docker exec命令启动一个"sleep"进程

    [root@VM-4-17-centos ]# docker exec -d  centos6-2  sleep 2000
    [root@VM-4-17-centos ]# docker exec  centos6-2  ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 08:24 pts/0    00:00:00 /usr/sbin/sshd -D
    root         6     0  0 09:06 ?        00:00:00 sleep 2000
    root        10     0  0 09:06 ?        00:00:00 ps -ef
    

    查看宿主机的进程号

    [root@VM-4-17-centos ]#  docker top centos6-2
    UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
    root                4962                4948                0                   16:24               pts/0               00:00:00            /usr/sbin/sshd -D
    root                11539               4948                0                   17:06               ?                   00:00:00            sleep 2000
    

    我们可以清楚的看到exec命令创建的sleep进程属 centos6-2 容器的名空间,但是它的父进程是Docker 容器的启动进程。

    查看容器目录里的进程号

    进程号就存在一个文件里面

    [root@VM-4-17-centos 460d688239304172f39bb9586bfc5959e0c3db64e7c3a0937f1003f94408ebbd]# cat cgroup.procs
    11539
    
     docker exec -d  centos6-2 pstree -p
     docker exec -d  centos6-2 ps -auxf
      docker exec -d  centos6-2 ll /proc
    
    [root@VM-4-17-centos docker]#  docker exec  centos6-2  ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 08:24 pts/0    00:00:00 /usr/sbin/sshd -D
    root         6     0  0 09:06 ?        00:00:00 sleep 2000
    root        40     0  0 09:26 ?        00:00:00 ps -ef
    [root@VM-4-17-centos docker]#  docker exec  centos6-2  ps -auxf
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root        44  0.0  0.0  13360  1012 ?        Rs   09:26   0:00 ps -auxf
    root         6  0.0  0.0   4120   316 ?        Ss   09:06   0:00 sleep 2000
    root         1  0.0  0.0  66664  3068 pts/0    Ss+  08:24   0:00 /usr/sbin/sshd -D
    Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
    [root@VM-4-17-centos docker]#  docker exec  centos6-2  pstree -p
    sshd(1)
    

    docker daemon (docker守护进程)

    pidof dockerd   #查看docker守护进程pid
    lsof -p 3197 | wc -l #docker守护进程打开的文件数
    

    在两个容器中的"centos "是两个独立的进程,但是他们拥有相同的父进程 Docker Daemon。

    所以Docker可以父子进程的方式在Docker Daemon和Redis容器之间进行交互。

    另一个值得注意的方面是,docker exec命令可以进入指定的容器内部执行命令。由它启动的进程属于容器的namespace和相应的cgroup。

    但是这些进程的父进程是Docker Daemon而非容器的PID1进程。

    我们下面会在Redis容器中,利用docker exec命令启动一个"sleep"进程

    docker@default:~$ docker exec -d redis sleep 2000
    docker@default:~$ docker exec redis ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    redis        1     0  0 02:26 ?        00:00:00 redis-server *:6379
    root        11     0  0 02:26 ?        00:00:00 sleep 2000
    root        21     0  0 02:29 ?        00:00:00 ps -ef
    docker@default:~$ docker top redis
    UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
    999                 9955                1264                0                   02:12               ?                   00:00:00            redis-server *:6379
    root                9984                1264                0                   02:13               ?                   00:00:00            sleep 2000
    

    我们可以清楚的看到exec命令创建的sleep进程属Redis容器的名空间,但是它的父进程是Docker Daemon。

    如果我们在宿主机操作系统中手动杀掉容器的启动进程(在上文示例中是redis-server),容器会自动结束,而容器名空间中所有进程也会退出。

    docker@default:~$ PID=$(docker inspect --format="{{.State.Pid}}" redis)
    docker@default:~$ sudo kill $PID
    docker@default:~$ 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
    |-----tmp:docker临时目录
    |-----trust:docker信任目录
    |-----volumes:docker卷目录
    

    还可以通过docker指令确认文件位置:

    docker info
    

    查看某个容器的文件目录:

    docker exec 容器name ls
    
     docker exec centos6-2   ls /proc
    
    [root@VM-4-17-centos containers]#  docker exec centos6-2  ls /proc
    buddyinfo
    cgroups
    cmdline
    consoles
    cpuinfo
    crypto
    devices
    diskstats
    driver
    execdomains
    filesystems
    interrupts
    iomem
    ioports
    kallsyms
    kcore
    key-users
    kpagecount
    kpageflags
    loadavg
    locks
    mdstat
    meminfo
    modules
    mounts
    pagetypeinfo
    partitions
    sched_debug
    schedstat
    slabinfo
    softirqs
    swaps
    sysrq-trigger
    sysvipc
    timer_list
    timer_stats
    uptime
    version
    vmallocinfo
    vmstat
    zoneinfo
    

    Docker 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]
    Type=notify
    # the default is not to use systemd for cgroups because the delegate issues still
    # exists and systemd currently does not support the cgroup feature set required
    # for containers run by docker
    ExecStart=/usr/bin/dockerd
    ExecReload=/bin/kill -s HUP $MAINPID
    

    此时已经和Docker client分离,独立成一个二进制程序了。

    当然,守护进程模块不停的在重构,其基本功能和定位没有变化。和一般的CS架构系统一样,守护进程负责和Docker client交互,并管理Docker镜像、容器。

    OCI(Open 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/docker
    

    Docker 的客户端工具,通过CLI与 dockerd API 交流。 CLI 的例子比如docker build … docker run …

    Docker Daemon守护进程 (dockerd)

    /usr/bin/dockerd
    

    当然,守护进程模块不停的在重构,其基本功能和定位没有变化。

    和一般的CS架构系统一样,守护进程负责和Docker client交互,并管理Docker镜像、容器。

    Containerd

    /usr/bin/docker-containerd
    

    containerd是容器技术标准化之后的产物,为了能够兼容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的一个进程,

    他直接通过指定的三个参数:容器id,boundle目录(containerd的对应某个容器生成的目录,一般位于:/var/run/docker/libcontainerd/containerID),

    运行是二进制(默认为runc)来调用runc的api创建一个容器(比如创建容器:最后拼装的命令如下:runc create 。。。。。)

    /usr/bin/docker-containerd-shim
    

    每启动一个容器都会起一个新的docker-shim的一个进程. 他直接通过指定的三个参数来创建一个容器:

  • boundle目录(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-replacement"
    

    Docker、containerd, containerd-shim和runc之间的关系

    他们之间的关系如下图:

    我们可以通过启动一个Docker容器,来观察进程之间的关联。

    通过runc来启动一个container的过程

    查看进程信息

    利用docker top命令,可以让我们从宿主机操作系统中看到容器的进程信息。

    尼恩提示:这里比较复杂,具体的视频介绍,请参见稍后的 穿透云原生视频进行介绍。

    查看父进程信息

    [root@VM-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