Docker Machine 基础概念和相关操作

前言

此篇博文是笔者所总结的 Docker 系列之一;

本文为作者的原创作品,转载需注明出处;

什么是 Docker Machine

先来看官网的一段描述,

Docker Machine is a tool that lets you install Docker Engine on virtual hosts, and manage the hosts with docker-machine commands. You can use Machine to create Docker hosts on your local Mac or Windows box, on your company network, in your data center, or on cloud providers like Azure, AWS, or Digital Ocean.

Docker Machine 是一个可以在虚拟机上快速安装 Docker Engine 和使用命令行对 Docker Engine 其进行管理的工具;你可以使用 Docker Machine 在你的本机、你公司的网络,在你的数据中心,或者是一些云服务中心诸如 Azure、AWS 或者 Digital Ocean 上创建 Docker 主机(备注,这里的 Docker Host 指的就是 Docker Engine);

笔者解读,官网上的这段描述还是过于笼统;为什么要使用 Docker Machine 工具,就要从以前我们如何使用 Docker 说起,以前,使用 Docker,首先要安装 Linux 虚拟机,然后再安装 Docker 的服务器端既是 Docker Daemon,可能你要说这些都不算麻烦,但是当你有几十上百个 Docker Daemon 需要管理的时候,频繁的在 Linux 虚拟机上进行连接切换,输入命令行进行管理,这个步骤就够你烦的了吧,特别是当你的集群规模进一步扩大的时候,成百上千个 Docker Daemon 需要被管理的时候,想要在本地通过 Commond Line 的方式来进行管理那将会是一个噩梦;所以 Docker Machine 孕育而生,它不但创建 Docker Engine,并且还要创建 Linux 虚拟机,也就是 Docker Engine 的宿主机,在后面可以看到,Docker Machine 将会下载并安装一个最精简的且适合于 Docker 的 Linux 虚拟机 boot2docker,总共大小不到 50M,用来运行 Docker Daemon 和 Docker Engnine;

备注,一些重要的名词解释,

  • Docker Engine
    是 Docker Daemon 的一部分,但是因为 Docker Engine 是核心,所以往往当称呼 Docker Engine 的时候就是表示 Docker Daemon,关于什么是 Docker Daemon 和 Docker Engine 查看笔者之前的博文 《Docker 原理篇(一)Docker 是什么?》;

  • Docker Host
    从名称上看指的是 Docker 的宿主机,但其实,在官网中,该名词的意思表达两层意思,一是宿主机,二是 Docker Engine;所以,当官文中提到这个名词的时候,要知道,它实际上涵盖了这两部分意思;

Using docker-machine commands, you can start, inspect, stop, and restart a managed host, upgrade the Docker client and daemon, and configure a Docker client to talk to your host.

使用 docker-machine 的命令便可以直接对 docker host(注意,这里的 host 指的是两部分,一是 docker 的宿主机另一个是 docker engine),进行启动、侦测、关闭、重启、设计以及配置的工作;

Point the Machine CLI at a running, managed host, and you can run docker commands directly on that host. For example, run docker-machine env default to point to a host called default, follow on-screen instructions to complete env setup, and run docker ps, docker run hello-world, and so forth.

当你使用 docker-machine CLI(命令行客户端) 行指向一个由 docker-machine 所创建且正在运行的 docker engine 的时候,你就可以直接使用在该本地 CLI 中使用 docker 命令来操作该 docker engine 了;比如,你可以使用命令行 docker-machine env default 指向一个名为 default 的 docker engine,然后就可以直接使用 docker 命令对其进行管理了;(官网上这里使用了这么一个生动的例子阐述了我们的 docker-machine 在管理 docker engine 上会是多么的方便);

所以,综上所述,Docker Machine 就是一个便于开发者在本地开发调试本地或者远程 Docker 的一款利器,也是手动运维过程中不可多得的一款非常棒工具;

安装

如果已经在 mac 上安装了 Docker,那么该 app 上会自带 Docker Machine 所以也就不用额外对其进行安装;但是如果你只是想安装 Docker Machine,那么也可以单独安装;具体过程参考 https://docs.docker.com/machine/install-machine/ 这里不再赘述;

driver

driver 在 Docker Engine 中有着它特殊的含义,它并非是驱动的意思,笔者认为,将它理解为目标更为确切;意思是在某个目标上安装 Docker Host;比如

1
$ docker-machine create --driver virtualbox default

的含义就是,通过 virtualbox 创建一个名为 default 的 docker machine,也就是对应的 docker host;

目前所支持的 driver 可以参考 https://docs.docker.com/machine/drivers/ 遗憾的是,并没有发现笔者目前正在使用的阿里云;

实战

本小节,笔者将会在本地使用 docker machine 通过 virtualbox 创建一个名为 default 的 machine,来看看 docker machine 的一系列基础的用法;注意,在开始下面的实战训练的时候,要先安装 virtual box,且必须安装 5.0 版本或以上版本,否则不能被 docker machine 所支持;

创建一个 Docker machine

  1. 检查本机的安装情况

    1
    2
    $ docker-machine ls
    NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
  2. 使用 virtualbox 创建一个名为 default 的 docker machine

    1
    $ docker-machine create --driver virtualbox default

    This command downloads a lightweight Linux distribution (boot2docker) with the Docker daemon installed, and creates and starts a VirtualBox VM with Docker running.

    上述命令将会下载一个最轻量级的并且已经安装好了 docker deamon 的 linux 发行版,boot2docker,并且会在该系统中创建一个以 default 命名 Docker Host (又名 Docker Machine,是由 boot2docker + Docker Daemon 所构成);笔者补充,要特别特别注意的是,boot2docker 只包含了 linux 的内核,并不包含常用的 Linux 命令或者工具软件,所以,如果要向使用 linux 更为复杂的功能,可以安装 busybox,它集成了三百多个最常用的 linux 命令和工具软件

    上述命令执行过程中的日志输出如下,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    Creating CA: /Users/mac/.docker/machine/certs/ca.pem
    Creating client certificate: /Users/mac/.docker/machine/certs/cert.pem
    Running pre-create checks...
    (default) Image cache directory does not exist, creating it at /Users/mac/.docker/machine/cache...
    (default) No default Boot2Docker ISO found locally, downloading the latest release...
    (default) Latest release for github.com/boot2docker/boot2docker is v18.01.0-ce
    (default) Downloading /Users/mac/.docker/machine/cache/boot2docker.iso from https://github.com/boot2docker/boot2docker/releases/download/v18.01.0-ce/boot2docker.iso...
    (default) Creating VirtualBox VM...
    (default) Creating SSH key...
    (default) Starting the VM...
    (default) Check network to re-create if needed...
    (default) Found a new host-only adapter: "vboxnet1"
    (default) Waiting for an IP...
    Waiting for machine to be running, this may take a few minutes...
    Detecting operating system of created instance...
    Waiting for SSH to be available...
    Detecting the provisioner...
    Provisioning with boot2docker...
    Copying certs to the local machine directory...
    Copying certs to the remote machine...
    Setting Docker configuration on the remote daemon...
    Checking connection to Docker...
    Docker is up and running!
    To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env default

    如果是初次安装,首先会下载 boot2docker.iso 并将其安装到目录 /Users/mac/.docker/machine/cache 中;然后便开始安装 docker host 并开始配置好相关的 SSL 证书;

  3. 再次检查安装状态,确保安装成功

    1
    2
    3
    $ docker-machine ls
    NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
    default * virtualbox Running tcp://192.168.99.100:2376 v18.01.0-ce
  4. 获取新建的 docker machine 既 docker host 的配置信息 (env cmd)

    1
    2
    3
    4
    5
    6
    7
    $ docker-machine env default
    export DOCKER_TLS_VERIFY="1"
    export DOCKER_HOST="tcp://192.168.99.100:2376"
    export DOCKER_CERT_PATH="/Users/mac/.docker/machine/machines/default"
    export DOCKER_MACHINE_NAME="default"
    # Run this command to configure your shell:
    # eval $(docker-machine env default)

    可以看到,里面包含了此 docker machine 的名字,以及相关的 docker host 信息;

  5. default docker host 建立连接

    1
    $ eval "$(docker-machine env default)"

    注意,上面这个步骤并不是真正的使用目标主机上的 shell 来执行命令,其实只是将发生在本地有关的 Docker 命令发送到远程目标主机上而已;它是怎么做到的呢?其实很简单,执行完上述命令以后,它会把目标 default 主机的 Docker 信息作为环境变量配置到本地,然后但凡是通过本地 shell 所发送的 Docker 命令都会自动的转发到目标主机 default 上执行;这种方式有一个特别好的好处是,我可以将我的 docker 命令是发送到远程 default 上执行,但是我依然可以使用我在本机上所安装的应用和命令,比如安装在本地的 docker-machine 命令;因此要特别注意与 ssh 命令的区别,ssh 命令直接登录到目标 docker host 上使用目标机的 shell 命令了;有关这部分的详细内容笔者将还会在 eval 小节中进一步进行阐述;

执行 Machine 命令

下面我们尝试在目标主机 default 上安装 docker 容器;

  1. 使用 eval 的方式连接 default 主机

    1
    $ eval "$(docker-machine env default)"

    正如上一小节所描述的那样,经过上面的命令执行以后,本地相关的 Docker 指令都会自动发送到远程 default 上执行;

  2. 在 default 上执行第一个容器;下载 busybox 镜像,然后通过 echo 命令输出一段问候语,备注,有前文描述可知,boot2docker 只包含了 linux 的内核,并不包含常用的命令行,所以需要安装 busybox;

    1
    2
    3
    4
    5
    6
    7
    8
    $ docker run busybox echo hello world
    Unable to find image 'busybox' locally
    Pulling repository busybox
    e72ac664f4f0: Download complete
    511136ea3c5a: Download complete
    df7546f9f060: Download complete
    e433a6c5b276: Download complete
    hello world
  3. 得到目标宿主机上的 IP 地址;可以看到,我依然可以在当前 shell 窗口中使用本地命令 docker-machine 来进行相关操作;

    1
    2
    $ docker-machine ip default
    192.168.99.100
  4. 在 default 上执行第二个容器;运行 Nginx;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ docker run -d -p 8000:80 nginx
    Unable to find image 'nginx:latest' locally
    latest: Pulling from library/nginx
    e7bb522d92ff: Pull complete
    6edc05228666: Pull complete
    cd866a17e81f: Pull complete
    Digest: sha256:285b49d42c703fdf257d1e2422765c4ba9d3e37768d6ea83d7fe2043dad6e63d
    Status: Downloaded newer image \for nginx:latest
    98d48bbf3c757ca7534627cc51f4e70049ddcc5d6c577444596b5ce2d1fa63c0

    注意,上述命令执行完毕以后将会直接退出,并在后台执行,-d 表示 detached 模式,也就是将当前 docker 容器放在后台执行;更多命令解释可以参考 《Docker 原理篇(三)Docker 基础操作和基础概念》;然后在地址栏中输入 http://192.168.99.100:8000 便可进行访问了;

  5. 使用 ssh 登录目标主机 default 进行检查

    1
    2
    $ docker-machine ssh default
    docker@default:~$

    然后检查正在 default 主机上执行的容器,可见,我们刚才在本地 shell 所创建的 Nginx 容器的确运行在 default 主机上;

    1
    2
    3
    docker@worker1:~$ docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    b5c316fc8bc9 nginx "nginx -g 'daemon of…" 11 seconds ago Up 10 seconds 0.0.0.0:8000->80/tcp brave_easley

开启、结束和删除

  • 结束,

    1
    $ docker-machine stop default

    关闭以后,http://192.168.99.100:8000/ 便不能再被访问了;同时可以使用 docker-machine ls 查看当前的状态;

  • 开启,

    1
    $ docker-machine start default
  • 删除,

    1
    2
    3
    4
    5
    $ docker-machine rm default
    About to remove default
    WARNING: This action will delete both local reference and remote instance.
    Are you sure? (y/n): y
    Successfully removed default

    验证是否删除

    1
    2
    $ docker-machine ls
    NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS

添加一个已经存在的 host

我们知道,通过 docker-machine create 命令所创建的 machine host 默认是会下载并安装 boot2docker 操作系统以及 docker machine 的;但是,通常情况下,我们使用的云环境,已经默认安装好了操作系统,比如 ubuntu 或者 centos 等,那么如何才能够将这样的系统添加到 docker-machine 的管理中来呢?这部分内容参考 https://segmentfault.com/q/1010000006207572 写得比较的详细,这里不再赘述;

命令

在本章节,笔者将重点对一些容易混淆的命令进行阐述和解释;

eval

正如笔者在实战章节中的创建一个 Docker machine 小节的第 5 点中所描述的那样,通过类似下面这样的 eval 指令,

1
$ eval "$(docker-machine env default)"

其并不是真正的将本地 shell 切换远程 shell 上,这一点与 ssh 命令截然不同;而只是将 docker 命令分发到远程 default 主机上执行,而其它的命令依然在本地上执行,比如安装在本地的 docker-machine 命令;其实笔者好奇的是,它是如何做到的呢?其实原理很简单,就是将 default 的信息作为环境变量写到当前 shell 作用域的环境变量中;笔者做了如下的试验,

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
27
28
29
30
31
32
33
34
35
36
macdeMacBook-Pro:~ mac$ env
TERM_PROGRAM=iTerm.app
ANDROID_HOME=/Volumes/Elements/programs/android-sdk
TERM=xterm-256color
SHELL=/bin/bash
CLICOLOR=1
HADOOP_HOME=/usr/local/Cellar/hadoop/2.7.2/libexec
CATALINA_HOME=/Users/mac/programs/apache-tomcat-7.0.61
TMPDIR=/var/folders/ks/3_sl6tr16357yrw94hwq20d80000gn/T/
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.NtOHwQyznI/Render
TERM_PROGRAM_VERSION=3.1.5
OPENSSL_INCLUDE_DIR=/usr/local/Cellar/openssl/1.0.2j/include/
TERM_SESSION_ID=w1t0p0:DB4EB8C6-63B3-49EB-BEE9-986A255114F5
JRE_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre
USER=mac
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.SziJT5rNHo/Listeners
__CF_USER_TEXT_ENCODING=0x0:25:52
MAVEN_OPTS=-Xms1024m
LSCOLORS=gxfxcxdxbxegedabagacad
PATH=/Library/Frameworks/Python.framework/Versions/3.6/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/share/dotnet:/opt/X11/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/Applications/Wireshark.app/Contents/MacOS:/usr/local/mysql-5.6.24-osx10.8-x86_64/bin:/Volumes/Elements/programs/android-sdk/platform-tools:/Volumes/Elements/programs/android-sdk/tools:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin:/usr/local/Cellar/hadoop/2.7.2/libexec/sbin:/usr/local/Cellar/openssl/1.0.2k/bin/openssl
PWD=/Users/mac
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home
LANG=zh_CN.UTF-8
ITERM_PROFILE=Default
XPC_FLAGS=0x0
XPC_SERVICE_NAME=0
OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2j/
SHLVL=1
HOME=/Users/mac
COLORFGBG=15;0
SCRAPY_PYTHON_SHELL=ipython
ITERM_SESSION_ID=w1t0p0:DB4EB8C6-63B3-49EB-BEE9-986A255114F5
LOGNAME=mac
DISPLAY=/private/tmp/com.apple.launchd.D92DFBODeb/org.macosforge.xquartz:0
COLORTERM=truecolor
_=/usr/bin/env

这是笔者当前 shell 中的所有环境变量信息,注意,里面并不包含任何类似 DOCKER_HOST 这样的信息,可以使用下面的命令进行检查,

1
2
macdeMacBook-Pro:~ mac$ env | grep DOCKER
macdeMacBook-Pro:~ mac$

线面,我们来执行 eval 命令,

1
$ eval "$(docker-machine env default)"

在来查看本地 shell 中的环境变量参数,看看发生了什么变化

1
2
3
4
macdeMacBook-Pro:~ mac$ env | grep DOCKER
DOCKER_HOST=tcp://192.168.99.100:2376
DOCKER_MACHINE_NAME=default
DOCKER_TLS_VERIFY=1

笔者惊奇的发现,原来 shell 其实只是将 default 相关的主机和端口信息以及 docker machine 的名字缓存在了本地 shell 中而已;怪不得它可以将 docker 命令发送给 default 主机上执行了;上面的信息同样可以通过 docker-machine env 命令查询得到,

1
2
3
4
5
6
7
$ docker-machine env default
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/mac/.docker/machine/machines/default"
export DOCKER_MACHINE_NAME="default"
# Run this command to configure your shell:
# eval $(docker-machine env default)

Reference

https://docs.docker.com/machine/overview/
https://docs.docker.com/machine/get-started/
docker machine CLI 所支持的所有命令: https://docs.docker.com/machine/reference/
docker machine 管理阿里云: https://yq.aliyun.com/articles/7495?commentId=1091