docker

logo

获取 Docker

  • Docker 有两个可用的版本,社区版:ommunity Edition (CE), 企业版:Enterprise Edition (EE),Docker Community Edition(CE)非常适合希望开始使用Docker并尝试使用基于容器的应用程序的开发人员和小型团队,社区版有两个主要的更新通道,stage, edge:

    • stage: 每个季度更新一次,稳定可靠
    • edge: 每月更新一次,能够体验最新的功能
  • Docker企业版(EE)专为企业级开发人员和IT团队而设计,他们在大规模生产中构建,发布和运行关键业务应用程序.

安装 Docker

  • 针对 Mac 安装 CE 版本

  • 其他的版本可以参考官方文档

  • Homebrew的Cask已经支持docker for Mac,因此可以使用Homebrew Cask 来进行安装:

    1
    brew cask install docker
  • 启动终端,查看docker是否安装成功:

    1
    2
    3
    4
    5
    6
    ➜  ~ docker --version
    Docker version 17.12.0-ce, build c97c6d6
    ➜ ~ docker-machine --version
    docker-machine version 0.13.0, build 9ba6da9
    ➜ ~ docker-compose --version
    docker-compose version 1.18.0, build 8dd22a9
  • 如果docker version,docker info 都运行正确的话,可以尝试启动一个nginx试试:

    1
    docker run -d -p 8090:80 --name webserver nginx
  • 一切正常的话,打开浏览器,输入:http://localhost:8090你将看到:
    nginx

  • 列出你创建的容器:

    1
    docker ps -a
  • 停止启动的容器:

    1
    docker stop xxxx
  • 删除你创建的容器:

    1
    docker rm xxxxxx
  • 查看已经下载的镜像:

    1
    docker images
  • 除此之外,一些常用的命令如下:

    docker命令 意义
    docker version 查看docker版本
    docker search xxx 从仓库中查找镜像
    docker pull xxx/xxx(用户名/镜像名) 从仓库中下载镜像
    docker run xxx/xxx(用户名/镜像名) 启动容器
    docker ps (-l , -a…) 查看所有容器
    docker inspect 容器id 查看具体容器
    docker commit 容器id 从容器创建一个新的镜像
    docker push xxx/xxx(用户名/镜像名) 提交镜像
    docker port 容器名 查看容器的端口映射情况

镜像服务(加速器)

  • 我们常用的Registry是docker官方的Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。还有quay.io。由于某些原因,在国内访问这些服务可能会比较慢。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为加速器。常见的有:阿里云加速器,DaoCloud加速器;

使用Docker

容器的简要说明

  • 镜像(image)是一个轻量级的,独立的可执行程序包,包含运行一个软件所需的所有东西,包括代码运行时依赖库,环境变量和配置文件。

  • 容器是镜像的运行时示例,镜像运行时在内存中变成的内容,默认情况下,它与主机完全隔离,只有在配置的情况下才可访问主机的端口和文件。

  • 容器在系统内核上就地运行应用程序,同通过管理程序获得一个接入主机资源的虚拟机相比,他有更好的性能特点,容器可以获得本地访问权限,每个容器都以独立的进程运行,相比其他的应用程序,不需要更多的内存。

虚拟机 VS 容器

  • 虚拟机通过中间层将一台或者多台独立的及其运行于物理硬件当中;而容器则是直接运行在操作系统内核之上的用户空间

  • 容器技术的磁盘占有空间少,虚拟机部署应用包含应用和其依赖的库,还需要包含完整的操作系统,容器只需前两者。另外,虚拟机需要模拟硬件的行为,对cpu,内存消耗较大

  • 虚拟机运行原理:
    virtual-machine

  • 虚拟机运行客户操作系统, 注意在每个box中都有一个系统层。这是资源密集型,这将导致磁盘镜像,应用程序状态同系统设置,系统安装的依赖以及系统补丁纠缠在一起,而且容易丢失,很难复制。传统虚拟机技术就是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;

  • 容器运行原理:
    container-diagram

  • 容器可以共享一个内核,并且唯一需要在容器镜像中的信息是可执行文件及其包依赖关系,它们永远不需要安装在主机系统上。这些进程的运行就如同本地的进程一样,你可以使用docker ps命令管理他们,就像你在linux平台使用ps命令查看本地进程一样,最后,因为他们包含了所有的依赖关系,就没有配置依赖的纠葛。一个集装箱化的应用程序可以随处运行。

构建并运行你的第一个应用程序

Introduction

  • 现在是时候用docker的方式构建一个应用程序了,我们将从这个应用程序的底部开始构建,这个应用程序是一个容器,然后我们再在这一层上添加新的东西。在这个层次上面是一个服务,它定义了容器在生产环境中的行为,这个将在接下里的一节中讲到。最后在顶层是stacks,定义所有服务的交互,将在第五部分讲到。结构层次大概是这样式的:

    • stacks

    • Services

    • containers(我们现在在这里)

Your new development environment

  • 过去,你写一个PythonAPP的时候,流程的第一步就是在你的电脑上安装Python运行库。但是这将造成这样的情况,不仅需要一个能完美运行应用程序的本地环境,同时还需要一个与之匹配的生产环境。

  • 使用docker,你可以将一个基础的可移植的Python运行环境作为一个镜像,然后你的构建将包括Python基础的镜像和你的应用程序代码,确保你的应用程序,它的依赖和运行时都是在一起的。

  • 这些可移植的镜像可以由一个叫做Dockerfile的文件来进行定义。

Dockerfile

  • 接下来要跟着一起来动手了,只有在动手的过程中才能体会到生命的乐趣。创建一个新的目录,并且切换到新的目录下创建一个文件:Dockerfile,复制粘贴下面的内容然后保存,可以看看里面的解释。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 使用一个官方的Python运行时作为基础
    FROM python:3.6-slim

    # 设置工作目录为/app
    WORKDIR /app

    # 复制当前目录的内容到/app
    ADD . /app

    # 安装requirements.txt中声明包
    RUN pip install --trusted-host mirrors.aliyun.com --index http://mirrors.aliyun.com/pypi/simple/ -r requirements.txt

    # 暴露端口80给容器外部的环境
    EXPOSE 80

    # 定义一个环境变量
    ENV NAME World

    # 当容器启动的时候运行app.py
    CMD ["python", "app.py"]
  • Dockerfile中涉及到几个文件我们还没有创建,app.py以及requirements.txt。

应用程序

  • 我们在Dockerfile所在的目录下再创建两个文件:app.py以及requirements.txt,文件内容如下所述,这就算是补全我们的应用程序,虽说简单,但是五脏俱全。
  • requirements.txt
    1
    2
    Flask
    Redis

-app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)


@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"

html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)


if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
  • 现在我们将看到pip install -r requirements.txt安装了flask和redis的Python扩展,应用将打印环境变量NAME以及socker.gethostname()的输出,但是由于我们没有安装redis服务,所以我们期望这里打印错误信息。

build app

  • 现在我们构建我们的APP,在开始构建的使我们,在我们的应用目录下执行ls,应该至少看到以下这几个文件:Dockerfile, requirements.txt, app.py

  • 现在我们执行构建命令,这将创建一个新的镜像,我们也使用-t创建一个友好的标签。

    1
    docker build -t firstapp .
  • 构建的镜像在哪里?可以使用docker images 查看

    1
    docker images

Run the app

  • 运行容器的时候,我们将使用-p参数将主机的4040端口映射到容器的80端口:
1
docker run -p 4040:80 firstapp
  • 启动成功的时候你将看到一个来自容器内部的消息:* Running on http://0.0.0.0:80/ (Press CTRL+C to quit), 因为容器并不知道我们将80端口映射到了4040,所以在浏览器中输入以下URL进行访问:http://localhost:4040,我们将看到以下的结果:
    firstapp-run

  • 使用CTRL+C进行退出

  • 或者我们可以让容器在后台启动:

    1
    docker run -d -p 4040:80 firstapp

share your image

  • 为了验证可移植性,我们将刚刚创建的镜像上传然后在其他地方运行。毕竟你需要知道当部署应用到生产环境的时候如何推送镜像到registry。一个registry是一系列的集合,一个仓库是一系列镜像的集合。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。

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

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

  • 仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。

  • 执行命令docker login使用dockerID进行登录。

  • 执行命令docker tag image username/repository:tag 给镜像打标签,例如:docker tag firstapp gamelife1314/firstapp:0.1

  • 执行命令docker push gamelife1314/firstapp:0.1发布镜像;

  • 发布成功之后我们可以使用docker run -p 4040:80 gamelife1314/firstapp:0.1在任何机器上运行我们的应用程序了。

Services

  • 在这部分中,我们扩展了应用程序并实现了负载平衡。要做到这一点,我们必须在分布式应用程序的层次结构中上一级:服务。

  • 在分布式应用程序中,应用程序的不同部分被称为“服务”。例如,如果您想象一个视频共享站点,它可能包括一个用于将应用程序数据存储在数据库中的服务,后台运行的转码服务,一个用于前端的服务等等。

  • 服务实际上只是“生产中的容器”。服务只运行一个镜像,但它定义了镜像运行的方式 - 应该使用哪个端口,容器应该运行多少个副本以便达到所需的容量,以及等等。Scaling a service changes the number of container instances running that piece of software, assigning more computing resources to the service in the process(扩展一个服务会改变运行该软件的容器实例的数量,已分配给程序中的服务更多的计算资源).

第一个docker-compose.yaml文件格式

  • docker-compose.yaml是一个YAML格式的文件,描述了生产环境中docker的行为方式。

  • docker-compose.yaml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    version: "3"
    services:
    web:
    # 使用前一节创建的镜像
    image: gamelife1314/firstapp:0.1
    deploy:
    replicas: 5
    resources:
    limits:
    cpus: "0.1"
    memory: 50M
    restart_policy:
    condition: on-failure
    ports:
    - "4040:80"
    networks:
    - webnet
    networks:
    webnet:
  • 这个docker-compose.yaml 告诉docker做以下的事情:

    • 从registry中拉取创建的镜像;
    • 运行镜像的5个实例作为一个服务叫做web,限制每一个实例使用资源的上限 10% of the CPU (across all cores), 以及 50MB of RAM.
    • 如果有一个失败,立即重启容器;
    • 映射主机的4040端口到容器的80端口;
    • 指示web容器通过叫做webnet的负载均衡网络共享端口80;
    • 使用默认设置定义webnet网络;
  • 然后运行:docker stack deploy -c docker-compose.yaml firstapp firstapp是应用程序名称;

  • 我们单个service stack 在一个主机上运行了镜像的5个实例,可以通过docker srevice ls查看我们应用的服务id;

Docker 概述

  • docker 基于linux内核,是一种容器技术(容器虚拟化的方案)
  • docker 是一个可以把开发的应用程序自动的部署到容器中的一个开源引擎(Golang)
  • docker 在虚拟化的容器执行环境中,增加了一个应用程序部署引擎
  • CS架构。docker客户端和docker的守护进程进行通信。Docker守护进程负责构建,运行和分发Docker容器。docker客户端可以运行在同一个机器上,但是docker客户端也可以连接远端的docker服务。docker客户端和docker守护进程的交互通过一个restfulAPI,或者Unix socket,或者一盒网络接口。

docker组成

  • Docker Client 客户端
  • Docker Daemon 守护进程
  • Docker Image 镜像
  • Docker Container 容器
  • Docker Registry 仓库

Docker Client / Docker Daemon (c/s架构)

  • 终端中的一些docker命令(eg : docker pull, docker run …)通过client传给daemon(支持远程),然后返回结果
  • 通过客户端访问守护进程,进而操作容器

Docker Image

  • 容器的基石,容器基于镜像启动和运行
  • 一个镜像可以作为另外镜像的底层镜像(父镜像)

Docker Container

  • 通过镜像启动,是docker的执行单元,一个container可以运行一个/多个用户进程

Docker Registry

  • 用户保存用户的镜像,分为public和private
  • docker 官方提供公开的仓库 Docker Hub

Docker registries

  • docker registry 用于存储docker镜像。docker hub 和 docker cloud 是公共的 registry,任何人都可以使用。docker默认从docker hub 获取镜像。当然你也可以使用自己的registry。

Docker 容器的基本操作

交互式容器

  • docker run IMAGE [COMMAND] [ARG…]

    • 在新的容器中执行命令

      • docker run -i -t IMAGE /bin/bash

        • eg : docker run -i -t ubuntu /bin/bash
          • -i : –interactive = true (打开标准输入)
          • -t : –tty = true (打开tty终端)
      • 自定义容器名称 : docker run –name=自定义名字 -i -t IMAGE /bin/bash

  • docker start [-i] 容器名

    • 重新启动容器(不产生新的容器)
  • docker rm 容器名

    • 删除已经停止的容器

守护式容器

  • docker run -d IMAGE [COMMAND] [ARG…]

    • -d : 后台方式运行
  • docker attach 容器名

    • 附加到运行中的容器
  • docker logs [-f][-t][–tail] 容器名

    • -f : –follows=true : 追踪日志
    • -t : – timestamps=true : 日志加上时间戳
    • –tail=”all” : tail 的意思是返回结尾出多少数量的日志,如果不指定,返回所有
  • docker top 容器名

    • 查看运行中容器内所有的进程
    • docker exec -d [-d][-i][-t] 容器名 [COMMAND] [ARG…]
      • 在容器内启动新的进程
  • docker stop 容器名

    • 向容器发送信号,等待容器停止
  • docker kill 容器名

    • 直接停止容器

容器中的部署

  • 容器的端口映射
    • run [-P][-p] (大小写)
      • -P : –publish-all=true 为容器所有暴露的端口进行映射
        • eg : docker run -P -i -t ubuntu /bin/bash
      • -p : –publish=[] 指定映射容器的哪些端口, 有多种格式
        • 只指定容器的端口 (containerPort)
          • 宿主机的端口随机映射
          • eg : docker run -p 80 -i -t ubuntu /bin/bash
        • 同时指定宿主机和容器端口 (hostPort : containerPort)
          • 一一对应的
          • eg : docker run -p 80 -i -t ubuntu /bin/bash
        • ip对应容器端口 (ip : containerPort)
          • eg : docker run -p 0.0.0.0:80 -i -t ubuntu /bin/bash
        • ip+端口对应容器端口 (ip+port : containerPort)
          • eg : docker run -p 0.0.0.0:8080:80 -i -t ubuntu /bin/bash

对镜像的操作

  • 镜像的两个重要属性

    • repository 镜像的仓库, eg : ubuntu, centos …
    • tag 标签
  • 列出镜像

    • docker images [OPTSIONS] [REPOSITORY]
      • -a 查看所有的镜像
      • -f, –filter=[] 添加过滤条件
      • -q 只查看镜像的唯一id
  • 查看镜像的具体信息

    • docker inspect [OPTIONS] CONTAINER/IMAGE (这个命令既支持容器的查看,也支持镜像的查看)
      • -f : –format=””
  • 删除镜像

    • docker rmi [OPTIONS] IMAGE
      • -f 强制删除
      • 删除所有镜像 : docker rmi $(docekr images -q)
  • 查找镜像

    • docker search [OPTIONS] TERM
  • 拉取镜像

    • docker pull [OPTIONS] NAME[:TAG]
      • -a 下载所有匹配NAME的有tag的
  • 推送镜像

    • docker push NAME[:TAG]

Dockerflie

  • FROM

    • FROM [image]:[tag]
    • dockerfile第一条非注释的指令
  • MAINTAINER

    • 作者信息,一般包含所有者以及联系方式
  • RUN

    • 镜像构建过程中的指令,包含当前镜像中运行的命令,包含两种模式(shell & exec)

    • RUN [command] (shell 模式)

      • 以 /bin/bash -c command 执行命令
        • eg : RUN echo hello
    • RUN [“executable”, “param1”, “param2”] (exec 模式)

    • 可以指定其他形式的shell来运行指令

      • eg : RUN [“/bin/bash”, “-c”, “echo hello”]
  • EXPOSE

    • 指定暴露的容器的端口
  • CMD

    • 容器运行的默认命令,与前面的RUN不同的是 : 后者是构建镜像的过程中执行的命令
    • 如果在使用 docker run 命令运行容器的时候加上了容器运行时候的指令,则cmd中的指令会被覆盖,不会执行。既:cmd用于指定容器的默认行为
    • CMD指令的两种模式(shell & exec & 特殊模式)
    • CMD command param1 param2 (shell 模式)
      • 以 /bin/bash -c command 执行命令
        • eg : CMD echo hello
    • CMD [“executable”, “param1”, “param2”] (exec 模式)
      • 可以指定其他形式的shell来运行指令
      • eg : CMD [“/bin/bash”, “-c”, “echo hello”]
    • CMD [“param1”, “param2”] (只有一些参数)
      • 通常与ENTERYPOINT指令配合使用,作为其默认参数
  • ENTERYPOINT

    • 与CMD指令类似,也有两种模式(shell & exec),但是它不会被 docker run 指令中的指令覆盖
    • 如果需要覆盖,需要在 docker run 指令中加上--enterypoint选项
  • ADD & COPY

    • 将文件/目录复制到使用dockerfile构建的镜像中
    • 用法 : ADD[COPY] [src]…[dest]
      • src : 可以是本地地址(必须是构建目录的相对地址),也可以是url(不推荐用)
      • dest : 镜像中的绝对路径
    • 区别:
      • ADD 包含类似tar的解压功能,如果只是单纯的复制文件,推荐使用COPY
  • VOLUME

    • 用于向基于镜像创建的容器添加卷,可以提供例如共享数据/数据持久化的功能
  • WORKDIR

    • 在使用镜像创建容器时,在容器内部设置工作目录,ENTRYPOINT & CMD 的命令都会在这个目录下执行
    • 通常使用绝对路径
  • ENV

    • 设置环境变量,与WORKDIR类似,构建 & 运行 过程中都有效
  • USER

    • 指定镜像以什么样的用户去运行
    • 默认使用root用户
  • ONBUILD

    • 为镜像添加触发器,当这个镜像被当作基础镜像的时候,这个指令就会被执行,这个镜像被构建时,会添加触发器中的指令

Docker 数据卷

容器的数据卷

  • 什么是数据卷(Data Volume)
    • 数据卷设计的目的之一就是在于数据的永久化,既数据卷的生命周期与容器的生命周期独立,既Docker不会在容器删除的时候删除其挂载的数据卷
    • docker 数据卷是一个特殊设计的目录,可以绕过联合文件目录(UFS),为一个/多个容器提供访问
      docker