docker入门

docker,运维,虚拟化技术

Posted by Chris on November 13, 2019

1 docker出现的背景

在docker出现之前,一台机器上部署多个应用,得通过虚拟机技术来实现。虚拟机技术有如下缺点:

  • 启动慢
  • 系统资源耗费大

2 docker能够做的事情

docker是一种新的虚拟化技术,docker相对于虚拟机技术,是更加轻量的虚拟化技术。此外,docker通过image镜像的分发,能够做到更加快速的构建和部署。具体的的应用场景包括:

  • 部署应用程序更加节省资源
    相比较于虚拟机技术,docker占用的资源较少。
  • 部署应用程序更加快速
    docker可以做到秒级启动,而虚拟机技术是分钟级启动。
  • 保证一致的环境
    通过镜像,可以保证除了内核之外,线下和线上一致的运行环境。

正因为docker启动快速和节省资源,因此,在很多场景下应用很广泛。

3 概念

镜像(image):镜像可以认为是运行中的应用程序的一个快照。镜像包含了命令、依赖包、环境变量和配置文件。类似于面向对象编程中的类。
容器(container):容器是镜像的一个实例,具有运行状态。类似于面向对象编程中的对象。
注册服务器(registry):注册服务器类似于远程仓库,用于存储镜像。在本地创建完镜像之后,可以提交到注册服务器供其他人使用。
仓库(repository):仓库用于存放镜像,一般用于存放某个镜像的多个版本。可以认为仓库位于注册服务器上面。

4 docker的具体命令

安装docker以及配置镜像源

在mac上安装docker请参考:Install Docker for Mac
mac上配置镜像源,请参考:docker for mac更换国内镜像源

镜像的CRUD

  • 搜索远程注册服务器的镜像
    语法
      > docker search {image_name}
    

    image_name:镜像的名字

    示例

      > docker search python
      NAME                             DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
      python                           Python is an interpreted, interactive, objec…   4664                [OK]                
      django                           Django is a free web application framework, …   896                 [OK]                
      pypy                             PyPy is a fast, compliant alternative implem…   217                 [OK] 
    

    注意:使用docker {cmd} --help可以查看该命令{cmd}的具体用法。

  • 拉取远程仓库的镜像
    拉取镜像是指从注册服务器的仓库下载一个镜像。
    语法
      docker pull [repository]:[tag]
    

    repository:表示镜像的仓库名
    tag:表示镜像的仓库标签。一个仓库里面可以有多镜像的不同版本,可以通过该标签来区分。比如ubuntu有18.04版本和15.10等多个版本。

    示例

      > docker pull ubuntu:18.04
    
  • 查看本地的镜像

    执行docker images可以查看本地的所有镜像

      > docker images
      REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      ubuntu              18.04               775349758637        9 days ago          64.2MB
      hello-world         latest              fce289e99eb9        10 months ago       1.84kB
    

    REPOSITORY:表示镜像的仓库名
    TAG:表示镜像的仓库标签。一个仓库里面可以有多镜像的不同版本,可以通过该标签来区分。比如ubuntu有18.04版本和15.10等多个版本。 IMAGE ID:表示镜像的id
    CREATED:表示镜像的创建时间
    SIZE:表示镜像的大小

  • 运行镜像

    语法
    下面的语法,运行一个镜像。

      > docker run [-t] [-i] [repository]:[tag] [your_cmd] 
    

    参数解释:
    -t:可省略参数。在新容器内指定一个伪终端或终端。加上该参数,运行完命令之后,就会进入该容器的终端。 -i:可省略参数。允许用户对容器内的标准输入 (STDIN) 进行交互。
    your_cmd:启动容器之后执行的命令,需要与容器搭配使用。例如这里启动一个ubuntu容器,就可以终端命令/bin/bash。如果启动的是python容器环境,则使用/bin/bash会报错。
    repositorytag的解释见前面的介绍。

    示例
    下面的例子,启动容器之后,执行命令/bin/echo "hello docker"

      > docker run ubuntu:18.04 /bin/echo "hello docker"
      hello docker
    

    下面的例子,启动容器之后,执行命令/bin/bash "hello docker"。结果是打开一个终端,系统为ubuntu。

      > docker run -i -t ubuntu:18.04 /bin/bash
    
  • 删除本地的镜像

    语法

      > docker rmi {image_name}
    

    注释:rmi的英文全称是remove image。

    示例

      > docker rmi python
    

container的CRUD

  • 创建container

    语法

      > docker create --name <container_name> <image_name>:<tag>
    

    示例
    下面的例子,使用镜像ubuntu:18.04创建一个名为ubuntuContainer的容器

      > docker images
      REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      ubuntu              18.04               775349758637        9 days ago          64.2MB
      hello-world         latest              fce289e99eb9        10 months ago       1.84kB
      > docker create --name ubuntuContainer ubuntu:18.04
      7ae6cda2f3094b8265c0574a8d46f78e992d2845f4bb9d0857bfa60ec1ccac8a
    
  • 查询container

    查看所有的容器

      > docker ps -a
    

    查看活跃的容器

      > docker ps
    
  • 启动container

    下面的示例从镜像创建容器并启动,然后打开一个/bin/bash终端。

      > docker run -it ubuntu /bin/bash
    

    启动一个已经停止的容器

      > docker start <container_id>
    

    container_id:容器的id。注意不能只输入容器前缀。

    例如下面的例子启动某个停止的容器。

      > docker ps -al
      CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
      8f688b7ec9b5        ubuntu:18.04        "/bin/echo 'hello do…"   26 minutes ago      Exited (0) 2 minutes ago                       pensive_shannon
      > docker start 8f688b7ec9b5
      8f688b7ec9b5
    

    让容器后台运行
    加上-d这个参数即可。例如下面的例子会创建一个容器,但不会打开一个终端。

      > docker run -itd ubuntu:18.04 /bin/bash
    

    重启一个已经停止或者正在运行的容器

      > docker restart <container_id>
    
  • 停止运行中的container

      > docker stop <container_id>
    
  • 进入容器

    当容器正在后台运行时,可以进入通过命令进入该容器,从而进行交互。注意当容器没有正在运行时,进入该容器则会报错。

    语法

      > docker exec -it <container_id> <cmd>
    

    cmd:进入容器之后执行的命令

    示例

      > docker start cbeda6b012eb
      cbeda6b012eb
      > docker ps
      CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
      cbeda6b012eb        ubuntu:18.04        "/bin/bash"         4 hours ago         Up 5 seconds                            hungry_perlman
      > docker exec -it cbeda6b012eb /bin/bash
      root@cbeda6b012eb:/# 
    
  • 删除容器

    语法

      > docker rm -f <container_id>
    

    示例

      > docker ps -a
      CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS                     PORTS               NAMES
      cbeda6b012eb        ubuntu:18.04         "/bin/bash"              4 hours ago         Up 3 minutes                                   hungry_perlman
      > docker rm -f cbeda6b012eb
      cbeda6b012eb
    

    下面的命令会删除所有处于停止状态的容器

      > docker container prune
    

镜像从创建到提交

当远程仓库里的镜像无法满足我们的需求时,我们可以有如下两种做法:

  1. 从远程仓库中拉取镜像,进行更改。
  2. 使用Dockerfile指令来创建新的镜像。

下面看看这两种方式。

  • 更新镜像

    步骤如下:

    1. 从仓库拉取镜像
    2. 为镜像创建一个容器
      下面的命令拉取一个镜像,创建一个容器并启动,然后进入该容器
       > docker run -it ubuntu:18.04 /bin/bash
      
    3. 对容器进行更新
      下面的命令,更新ubuntu上的包管理器apt-get,更新的信息是其的源索引信息。
       > apt-get update
       > exit
      

      更新容器信息之后,然后使用exit退出容器。

    4. 提交容器副本
      使用docker commit来提交容器副本。
       > docker commit -m=<your_message> -a=<author_name> <container_id> <your_image_name>:<tag>
      

      示例

       > docker commit -m="docker demo" -a="chris" fc14a9f41d64 chris/ubuntu:v1
       sha256:b4912972ed4a490a9ac276c5e4f0250e85175e7438c1dc8b6b391cb971cc093e
       > docker images
       REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
       chris/ubuntu        v1                  b4912972ed4a        11 seconds ago      80.4MB
      
  • 新建镜像
    Dockerfile是一个由一堆命令+参数构成的脚本,使用docker build来构建脚本创建镜像。通过Dockerfile可以让脚本自动的完成一些任务。

    Dockerfile的格式为:

      # Comment
      FROM <base_image>
      RUN <instruction> <arguments>
      ...
    

    规定如下:

    第一行必须为FROM <base_image>,指定基础镜像

    下面为Dockerfile的一个示例。

    1. 新建一个名为Dockerfile的文件 写入内容如下
       > cat Dockerfile
       FROM ubuntu:18.04
       MAINTAINER chris "chris@qq.com"
      
       RUN mkdir a
      

      可以看出,基础镜像为ubuntu:18.04,执行的任务主要是在根目录创建一个名为a的文件夹。

    2. 构建镜像
       > docker build -t chris/ubuntu:v2 .
       Sending build context to Docker daemon  228.9kB
       Step 1/3 : FROM ubuntu:18.04
       ---> 775349758637
       Step 2/3 : MAINTAINER chris "chris@qq.com"
       ---> Running in fb148b02a6f0
       Removing intermediate container fb148b02a6f0
       ---> ace7f89e7560
       Step 3/3 : RUN mkdir a
       ---> Running in c51b0d3cf690
       Removing intermediate container c51b0d3cf690
       ---> 81baf38da9d3
       Successfully built 81baf38da9d3
       Successfully tagged chris/ubuntu:v2
      

    经过以上两步,就新建了一个镜像。下面验证我们的确创建了。

      > docker images
      REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      chris/ubuntu        v2                  81baf38da9d3        4 seconds ago       64.2MB
      > docker run -it chris/ubuntu:v2 /bin/bash
      > ls
      a  bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr
    

    可以看出,根目录多了文件夹a。

5 docker的原理

先看看虚拟机的实现原理,然后看docker虚拟化的实现原理。

在上图中,虚拟机主要的部分是Guest OS和Hypervisor虚拟机管理程序。Guest OS是一个模拟的操作系统,具有完整的操作系统内核,就像linux等系统内核一样。Hypervisor虚拟机管理程序模拟了硬件环境,主要是模拟(虚拟化)了CPU、内存和IO设备,虚拟的技术主要是将虚拟机的设备映射到物理的设备上。

  • 虚拟化CPU: 为了模拟CPU,需要模拟CPU的寄存器等结构
  • 虚拟化内存:虚拟机物理内存 -> 真实的宿主机的物理内存
  • 虚拟化IO设备:虚拟机物理内存 -> IO设备。虚拟机会将某个物理内存地址映射到一个IO设备,当虚拟机上的应用程序向该物理内存地址进行IO操作时,就会映射到宿主机的IO设备。

可以看出,虚拟机是很重量级的,需要一个模拟硬件环境的Hypervisor和多个操作系统内核。

上图是docker来实现虚拟化环境的架构图。docker实现虚拟化并没有实际模拟真实的环境,而是通过linux提供的namespaces(命名空间)、Cgroup(control group)和UnionFS(镜像管理文件系统)来实现资源隔离。

  • namespaces(命名空间):实现进程、网络和文件系统的隔离
  • Cgroup:实现CPU、内存和网络等物理资源的隔离
  • UnionFS(镜像管理文件系统):用于实现镜像和容器环境的管理

关于docker原理的部分,具体可以参考Docker 核心技术与实现原理

最后是虚拟机和docker的比较

内容 docker 虚拟机
启动时间 秒级 分钟级
占用资源 磁盘占用一般为MB 磁盘占用一般为GB
性能 接近原生 弱于原生
单机可以支持的数量 单机可以支持上千个容器 一般为几十个

6 参考

从 0 开始了解 Docker
Difference between Docker registry and repository
introduction
Docker与虚拟机的区别
Docker 核心技术与实现原理