Docker 学习

来源:互联网 发布:mac c语言编程软件 编辑:程序博客网 时间:2024/06/15 14:06

一 Hello Docker

#拉取busybox官方镜像,启动容器并执行输出"Hello Docker"

#********** Begin *********#
docker pull busybox:latest
docker run --name my_container busybox:latest echo "Hello Docker"

#********** End **********#


  • Docker的发展历程

Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3月以 Apache 2.0 授权协议开源),主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动开放容器联盟。

Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 3 万 6 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用Docker。

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和containerd。

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

  • Docker的特性

在docker的官方网站上提到了docker的典型场景:

  1. 自动化打包与部署应用
  2. 创建轻量级、私密的PAAS环境
  3. 自动化测试和持续的集成/部署
  4. 部署与扩展webapp、数据库和后台服务
  • Docker 架构

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。Docker 容器通过 Docker 镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。

Docker采用 C/S架构 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者RESTful API 来进行通信。

Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。

  • Docker与虚拟机对比

虚拟化的核心是对资源进行抽象,目标往往是为了在同一个机器上运行多个系统或应用,从而提高系统资源的利用率。虚拟化分为很多类型,比如常见的硬件辅助虚拟化(VMware workstation、 KVM等)。Docker所代表的容器虚拟化技术属于操作系统级虚拟化:内核通过创建多个虚拟的操作系统实例(内核和库)来隔离不同的进程。

如下图所示,传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

  • Docker的优势:

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。

  1. 更高效的利用系统资源
    由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

  2. 更快速的启动时间:
    传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

  3. 一致的运行环境
    开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 “这段代码在我机器上没问题啊” 这类问题。

  4. 持续交付和部署
    对开发和运维( DevOps) 人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合持续部署(Continuous Delivery/Deployment) 系统进行自动部署。而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

  5. 更轻松的迁移
    由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

  6. 更轻松的维护和扩展
    Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

Docker的基本概念

 镜像( Image)
 容器( Container)
 仓库( Repository)

Docker 镜像
我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而Docker 镜像(Image),就相当于是一个 root文件系统。比如官方镜像 ubuntu:14.04 就包含了完整的一套Ubuntu 14.04 最小系统的 root 文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数( 如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

Docker容器
镜像( Image) 和容器( Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。

Docker Registry
仓库(Repository)是集中存放镜像的地方。另外一个非常相似的单词Registry是注册服务器(例如Docker Hub就是一个官方的Registry)。注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。

从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址dl.dockerpool.com/ubuntu来说,dl.dockerpool.com 是注册服务器地址,ubuntu 是仓库名。(一般而言,一个仓库会存放同一种类型的镜像,例如ubuntu的仓库。)

一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像会,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

以 Ubuntu 镜像为例, ubuntu是仓库的名字,其内包含有不同的版本标签,比如,14.04 , 16.04 。我们可以通过 ubuntu:14.04 ,或者 ubuntu:16.04来具体指定所需哪个版本的镜像。如果忽略了标签,比如ubuntu ,那将视为ubuntu:latest 。

仓库名经常以两段式路径形式出现,比如training/webapp,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。

参考文献:

  1. docker官网,https://www.docker.com/
  2. 百度百科,docker,https://baike.baidu.com/item/Docker/13344470?fr=aladdin
  3. 浙江大学SEL实验室著,Docker容器与容器云: 人民邮电出版社 2015年9月出版.https://book.douban.com/subject/26593175/
  4. DockerOne著,Docker入门:百度阅读 2015年6月出版. https://yuedu.baidu.com/ebook/d817967416fc700abb68fca1


二、获取指定镜像

  • 任务描述
  • 相关知识
    • 获取镜像
    • docker pull命令背后的工作
    • docker pull的两个基本实例
    • 设置镜像加速器
  • 任务要求
  • 评测说明

    任务描述

    在没有使用Docker之前,如果我们要准备一个ubuntu的运行环境,那么首先我们得安装一个ubuntu的系统,多么麻烦,多么耗时啊!!而使用Docker之后,只需要从Docker Hub中拉取一个ubuntu镜像,基于该ubuntu镜像启动一个容器,就可以在容器中做任何在ubuntu系统中做的事情,整个过程大约2-3分钟,而且主要的时间是用于下载镜像!是不是非常方便呢?

    本关的任务是学习准备一个容器的运行环境,更准确的说,应该是拉取一个具备某个运行环境的镜像,要求学习者参照示例完成“从Docker Hub中拉取一个busybox:1.27镜像”的功能。

    相关知识

    在Docker的官方镜像仓库Docker Hub中保存了各种各样的镜像,这些镜像中保存了各种各样的运行环境。例如包含Linux运行环境的“ubuntu”镜像、“centos”镜像、“busybox”镜像等,提供数据库服务的“mysql”镜像、“oracle”镜像、“redis”镜像等。提供程序运行环境的“java”镜像、“python”镜像、“c++”镜像等等。基本上我们日常工作所需要的运行环境在Docker Hub中都会有对应的镜像(Docker Hub官网:https://hub.docker.com/ )(这些镜像不是凭空出现的,这是镜像构建者们辛勤的劳动成果。每一个Docker的使用者都应该感谢这些镜像构建者们!!)

    但是在安装完Docker之后,本地是没有任何镜像的。下面介绍如何从Docker Hub中拉取镜像(或者说下载镜像)。

    获取镜像

    默认情况下,使用docker pull命令,会从官方的Docker Hub库中将镜像拉取到本地。

    首先介绍这条命令的格式:

    1. docker pull [OPTIONS] <仓库名>:<标签>

    其中,

    • docker pull: Docker拉取镜像的命令关键词
    • [OPTIONS]:命令选项
    • 仓库名:仓库名的格式一般为<用户名>/<软件名>。对于Docker Hub,如果不指定用户名,则默认为library,即官方镜像。
    • 标签:标签是区分镜像不同版本的一个重要参数,<仓库名>:<标签>会唯一确定一个镜像。默认为latest

    例如,我们要从Docker Hub官方仓库拉取一个Ubuntu 14.04的官方镜像,其语句如下:

    1. docker pull ubuntu:14.04`

    docker pull命令背后的工作

    首先,如果tag值为空,即没有指定标签,就会使用默认tag,也就是latest,如果tag值不为空,就使用指定的tag

    然后,默认情况下,会在Docker Hub中寻找名为“repoName”的仓库,如果仓库不存在,返回错误信息。如果仓库存在,就从仓库中拉取对应tag的镜像。例如如果执行docker pull ubuntu:14.04,那么将从“ubuntu”仓库中拉取tag14.04的镜像,而如果执行docker pull ubuntu,会从“ubuntu”仓库中拉取taglatest的镜像
    (在Docker Hub中有很多个镜像仓库,一般情况下会将同一类型的镜像放在同一个仓库中,例如在一个ubuntu仓库中由很多个ubuntu镜像组成,包括ubuntu:14.04ubuntu:16.04`ubuntu:latest等等镜像)

    最后,将拉取的镜像存储到本地的指定位置。

    docker pull的两个基本实例

    执行docker pull ubuntu之后,会有下面的执行结果。(执行命令之前,本地并不存在ubuntu:latest镜像)。

    如下图所示,因为没有显示地指定tag,所以就使用默认tag,也就是latest。于是会在Docker Hub中从ubuntu仓库中拉取ubuntu:latest镜像,当将镜像一层层下载完成后,存储到本地。

    1. [root@localhost Desktop]# docker pull ubuntu
    2. Using default tag: latest
    3. latest: Pulling from library/ubuntu
    4. e0a742c2abfd: Pull complete
    5. 486cb8339a27: Pull complete
    6. dc6f0d824617: Pull complete
    7. 4f7a5649a30e: Pull complete
    8. 672363445ad2: Pull complete
    9. Digest: sha256:84c334414e2bfdcae99509a6add166bbb4fa4041dc3fa6af08046a66fed3005f
    10. Status: Downloaded newer image for ubuntu:latest
    11. [root@localhost Desktop]#

    那从Docker Hub中拉取一个不存在的镜像会怎么样呢?如下所示,执行docker pull aaa后,会在Docker Hub中查找aaa仓库,由于不存在aaa仓库,所以没有找到,返回了错误信息。

    1. [root@localhost Desktop]# docker pull aaa
    2. Using default tag: latest
    3. Error response from daemon: repository aaa not found: does not exist or no pull access

    设置镜像加速器

    由于“伟大的墙”的原因,在国内从Docker Hub中拉取镜像的速度可能会比较慢,国内很多云服务商都提供了镜像加速器服务,例如阿里、网易等等。

    以Linux系统配置阿里云加速器为例,只需要将下面的命令复制到Linux的终端,以root用户的身份执行之后,就成功的配置了阿里云加速器了!

    1. #以root用户执行以下操作
    2. mkdir -p /etc/docker
    3. tee /etc/docker/daemon.json <<-'EOF'
    4. {#下面的URL可以替换为你自己的阿里云加速地址
    5. "registry-mirrors": ["https://jxus37ad.mirror.aliyuncs.com "]
    6. }
    7. EOF
    8. systemctl daemon-reload
    9. systemctl restart docker

    三、启动一个docker


    • 任务详情
    • 相关知识
      • 第一种方式:新建并启动。
        • docker run背后的工作
        • docker run实例一
        • docker run实例二
      • 第二种方式:启动一个已经终止的容器
      • 查看容器信息
    • 任务要求
    • 评测说明

      任务详情

      如果将Docker比作一艘轮船,那容器就是轮船上一个一个的集装箱,而镜像就是组成集装箱的基本材料。docker容器是Docker中至关重要的一部分,而这一节我们介绍如何启动一个容器。

      本关任务是学习启动一个容器,要求学习者参照示例完成“创建并启动一个名为firstContainer的容器,该容器具备busybox的运行环境,并在启动时输出一个hello world”。

      相关知识

      在拉取到一个镜像之后,也就为容器准备了运行环境。下面我们将介绍如何使用镜像启动一个容器。

      启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。

      第一种方式:新建并启动。

      docker run命令会基于指定的镜像创建一个容器并且启动它。docker run的基本语法如下:

      1. docker run [OPTIONS] 镜像名 [COMMAND] [ARG]

      其中,

      • docker run: Docker创建并启动容器的命令关键词
      • OPTIIONS: 命令选项,最常用的包括-d后台运行容器并返回容器ID,-i以交互模式运行容器,-t为容器分配一个伪输入终端,--name指定启动容器的名称。更多选项请参考docker帮助文档。
      • 镜像名: 以<仓库名>:<标签>的方式来指定
      • COMMAND: 设置启动命令,该命令在容器启动后执行
      • ARG: 其他一些参数
      docker run背后的工作

      Docker 在后台运行的标准操作包括:

      1. 检查本地是否存在指定的镜像,不存在就从公有仓库下载启动
      2. 利用镜像创建并启动一个容器
      3. 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
      4. 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
      5. 从地址池配置一个 ip地址给容器
      6. 执行用户指定的启动命令
      7. 执行完毕后容器被终止

      第三条到第五条看不懂没有什么关系,后面我们会介绍相关的知识,相信到时你一定能够明白,但是现在我们要知道这个过程,了解这个过程能够帮助我们更好的使用命令。好,接下来我们介绍几个实例。

      docker run实例一

      创建并启动一个容器,容器中具有ubuntu的运行环境,输出hello docker。

      只需要一条命令:docker run ubuntu:14.04 echo 'hello docker',问题就解决了,是不是很简单啊!!是的,现在我们来讲一下它里面的操作,用来之前学过的知识点。

      首先由于本地不存在ubuntu:14.04镜像,所以先到Docker Hub中下载镜像;(其实也就是先执行docker pull ubuntu:14.04

      然后在下载完镜像之后,使用镜像创建。由于一个ubuntu镜像包含了一个ubuntu系统的所有内容,使用镜像启动后,容器中就具备了ubuntu的运行环境了。

      启动容器后,执行echo 'hello docker'启动命令,执行完启动命令之后终止容器。

      docker run实例二

      创建并启动一个容器,容器中具有ubuntu的运行环境,容器名为firstContainer,为容器分配一个终端,与用户进行交互。

      其中,-i选项告诉Docker保持标准输入输出流对容器开放,-t选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上。--name为容器设置容器名。

      注意docker run是创建一个新容器并启动,所以这条命令创建的容器与上个实例的创建的容器不是同一个容器。而且由于本地已经存在ubuntu:latest镜像了,所以并不需要再次从Docker Hub中下载,而是直接使用本地的ubuntu:latest镜像构建容器。

      启动容器之后,我们进入容器内部并在终端进行与容器交互。我们可以根据左侧的命令提示符判断自己是否在容器内部。例如上面的例子,当左侧的命令提示符为root@localhost时,表示我们在容器外部,而命令提示符为:root@fe263c9359dd/时,表示我们在容器内部,且容器的ID是fe263c9359dd。我们可以通过exit退出当前的容器。

      第二种方式:启动一个已经终止的容器

      虽然Docker容器是非常轻量的,这意味着一般情况下,我们在启动完容器并完成操作之后都会将容器删除掉。但是有些时候我们会进入之前创建的容器,而docker run每次都会创建一个新容器,显然不符合我们的需求。这种时候,可以使用docker start命令,使用容器名或者容器id启动一个已经终止的容器。

      1. docker start [OPTIONS] 容器 [容器2...]

      其中,

      • docker start: Docker启动容器的命令关键词
      • OPTIIONS: 命令选项
      • 容器: 需要启动的容器,该容器用“容器ID”或“容器名”表示,如果指定了多个容器,那么就将这些容器都启动。

      假设一个名为firstContainer的容器处于终止状态,现在需要将它启动,可以这么做:执行docker start firstContainer,命令执行后,尝试启动firstContainer容器,并执行该容器的启动命令。

      但是如果想启动第一个实例创建的容器,既不知道容器的名字(因为我没有指定)而且也不知道它的id。该怎么办呢?

      查看容器信息

      docker中有这样一条命令docker ps,可以查看容器的信息,包括容器ID,基础镜像,启动命令,创建时间,当前状态,端口号,容器名字。

      如果不加任何参数,只执行docker ps,将会显示所有运行中的容器。例如执行docker ps,如下图所示,在当前的Docker环境中,只有一个正在运行的容器,它的容器Id是fe263c9359dd,基于ubuntu:latest镜像,启动命令为”/bin/bash”,创建时间为2分钟之前,当前状态为”Up 2 minutes”,也就是已经运行了2分钟了,容器名为:firstContainer。

      而如果docker ps –a命令,可以查看Docker环境中所有的容器,包括已经停止的容器。执行docker ps –a后,如下图所示:除了名为firstContainer的容器外,还可以看到一个id为ee826f1d58ff的容器容器(容器id随机生成)。但是这个容器的当前状态为Exited (0) 3 minutes ago,这表示它是处于终止状态的,而且是在3分钟前退出的。

      对于这个处于终止状态的容器,可以通过docker start ee826f1d58ff或者docker start gracious_lewin启动该容器了。

      实际情况中,使用docker start ee826f1d58ff去启动第一个实例的容器,然后使用docker ps查看,会看不到该容器。这也就是说明了在执行docker ps时,容器Id为ee826f1d58ff的容器并不是处于运行状态,而是处于终止状态。

      docker ps的执行结果如下所示:容器Id为ee826f1d58ff的容器的当前状态为Exited (0) 2 seconds ago!!!这表示ee826f1d58ff容器确实处于终止状态,但是它是2秒之前退出的,注意是2秒前!这表明2秒前启动过容器,但是由于某种原因,容器终止了!!

      实际情况下,执行docker start ee826f1d58ff启动容器id为ee826f1d58ff的容器了!!但是在执行完启动命令之后,该容器就立即结束了,至于为什么会结束,下一关我们将为您揭晓答案!!

      任务要求

      本关的编程任务是补全step3/startcontainer.sh文件中的内容,要求创建并启动一个容器。具体要求如下:

      • 创建并启动一个容器,容器名为firstContainer,具备busybox的运行环境。并输出hello world
      #创建并启动一个容器,容器名为firstContainer,具备busybox的运行环境。并输出hello world
      #********** Begin *********#
      docker run --name firstContainer busybox:latest echo "hello world"
      #********** End **********#


      #以ubuntu镜像为基础,创建并在后台启动了一个名为firstContainer的容器(-d看不懂没关系,下一关会介绍的)
      docker run -itd --name firstContainer ubuntu /bin/bash
      #将firstContainer容器停止!
      #********** Begin *********#
      docker stop firstContainer
      #********** End **********#


      #删除所有容器
      #********** Begin *********#
      docker rm -f $(docker ps -a -q)
      #********** End **********#

      原创粉丝点击