架构
docker由三个部分组成:docker引擎、对象以及docker注册中心。
Docker引擎
Docker引擎(Docker Engine)是服务端-客户端(C/S)架构的应用,主要有这些部分:Docker守护进程(Docker daemon)、Docker Engine API、Docker客户端(Docker client)。其架构图如下所示:
- Docker守护进程(Docker daemon):也叫 dockerd,是一个持久化的进程,用户可以管理容器。守护进程会监听Docker Engine API的请求;
- Docker Engine API:用于与Docker守护进程交互用的的API。它是一个RESTful API,因此它不仅可以被Docker客户端调用,也可以被wget 和 curl等命令调用。
- Docker客户端(Docker client),是大部分用户与Docker交互的主要方式。用户通过客户端将命令发送给守护进程。
对象
Docker对象主要包含镜像、容器和服务。
- 镜像(image):是一个只读模板,用于创建容器。镜像是分层构建的,而定义这些层次的文件叫Dockerfile。docker build指令可以从一个Dockerfile文件构建出镜像。
- 容器(container):是镜像的可运行的实例,容器可通过API或CLI(命令行)进行操控。镜像和容器的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
docker注册中心
docker注册中心,也就是docker registry。docker registry用来存储镜像,类似GitHub用于存储代码。
docker有默认的镜像存储中心——dockerhub。dockerhub提供了大量的基础镜像用于使用。但是由于是国外的,国内访问会比较慢。
基本指令
login
登录到指定docker registry。命令格式如下:
docker login -u username -o password [SERVER]
Linux环境下,docker login
会将编码后的凭证存储到docker client
的~/.docker/config.json
文件中(Windows是%USERPROFILE%/.docker/config.json
)。
logout
登出指定docker registry。命令格式:
docker logout [SERVER]
执行docker logout
后,凭证会从默认存储中删除。
build
从dockerfile构建镜像,需要指定docker上下文以及dockerfile路径。dockerfile路径默认为Dockerfile
。指令格式:
docker build [OPTIONS] PATH | URL | -
下面的用例制作镜像名为runoob/ubuntu,tag为v1的镜像,PATH(docker上下文)为当前路径(.),dockerfile路径为当前路径下的Dockerfile文件:
docker build -t runoob/ubuntu:v1 .
docker会从指定的docker上下文查找dockerfile文件,因此如果dockerfile不在docker上下文的根路径下,需要使用-f FILEPATH
去指定,FILEPATH
路径为上下文相对路径。
tag
重新标记本地镜像。语句格式:
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:NEWTAG]
以下命令将名为ubuntu,tag为v1的镜像重新标记为runoob/ubuntu:v3:
docker tag ubuntu:v1 runoob/ubuntu:v3
run
用于启动容器。语句格式:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
以下命令使用镜像nginx:latest以交互模式启动一个容器,在容器内执行/bin/bash命令,并挂载了容器外的/var/run/docker.sock
文件到容器内的/var/run/docker.sock
:
docker run -it -v /var/run/docker.sock:/var/run/docker.sock nginx:latest /bin/bash
以下命令使用特权模式启动容器:
docker run --privileged nginx:latest
- 使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。
- privileged启动的容器,可以看到很多host上的设备,并且可以执行mount,否则,在容器中执行mount会报“permission denied”;
- 甚至允许你在docker容器中启动docker容器。
pull
从docker registry拉取镜像。对于私有的镜像,需要先执行docker login。命令格式:
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
以下命令用于从dockerhub中拉取 debian:jessie 镜像:
docker pull debian:jessie
push
将镜像推送到指定docker registry。同样的,如果是推送到私有的镜像仓库,需要先执行docker login。命令格式:
docker push [OPTIONS] NAME[:TAG]
以下命令将镜像推送到名为registry-host的镜像仓库:
docker push registry-host/myadmin/image:tag
dockerfile
dockerfile是一个用来制作镜像的文本文件,文本中包含了多条用于构建进行所需要的语句。一个典型的dockerfile如下所示:
FROM centos
ARG user=docker
COPY home.txt /mydir/
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
编写dockerfile常用指令
FROM
FROM后跟着的是制作镜像的基础镜像,任何定制的镜像制作都需要一个基础镜像。
ENV
设置环境变量,其作用类似export 变量名=变量值
。定义好之后,后续可以使用${变量名}
引用此环境变量。且镜像制作好之后,启动的容器中依然可以通过${变量名}
方式引用到。语句格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
ARG
构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build
的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build
中可以用 --build-arg <参数名>=<值>
来覆盖。命令格式:
ARG <参数名>=<参数值>]
COPY
从docker上下文目录中复制文件或者目录到容器里中,和ADD作用类似。指令格式:
COPY <源路径1><源路径2>... <容器中的目标路径>
RUN
用于执行后面跟着的命令行。
一个好的dockerfile是怎样的?
dockerfile除了要能看懂,还有一个很重要的点:做出来的镜像要尽可能的小。因此编写dockerfile时,最好能做到以下几点:
- 层要尽可能少。例如有多条RUN语句时,尽可能压缩成一条;
- ADD和COPY以及下载到容器中的东西,用完后如果后续不再使用,要删除。
更具体的做法可以参考:三个技巧,将 Docker 镜像体积减小 90%
配置文件
/var/run/docker.sock
有的容器运行时需要挂载/var/run/docker.sock
这个配置文件。比如当容器中需要执行docker命令,就需要在启动容器时将这个配置文件挂载上去,否则就会报错。
关于这个配置文件,网上查了相关资料后得出,它是Docker守护进程(Docker daemon)默认监听的Unix域套接字(Unix domain socket),容器中的进程可以通过它与Docker daemon
进行通信。容器挂载了/var/run/docker.sock
后,就相当于有了操控docker daemon
的能力,比如创建镜像或者启动容器。绑定了/var/run/docker.sock
的容器权限会变得很高,因此只有足够受信任的容器才给绑定。
/etc/docker/daemon.json
/etc/docker/daemon.json
是docker daemon
的默认配置文件,不管什么时候,docker都会默认先从这里读取配置文件。用户可以通过这个配置docker的加速器(registry-mirrors
)等配置。正常情况下,新安装的docker是没有这个配置文件的,需要用户手动创建。
说说两个可能会用到的配置:
- insecure-registries:“insecure-registries”: [],配置受信任的docker源。Docker如果需要从非SSL源管理镜像,需要加上此配置;
- registry-mirrors:“registry-mirrors”: [],配置docker的镜像加速地址,一般用于加速dockerhub的镜像加速。
docker in docker VS docker outside of docker
如何在启动的容器中使用docker呢?目前业界提供了两种使用方式:docker in docker
和docker outside of docker
。
docker in docker
"docker in docker
"是指在 Docker 容器中安装和运行另一个 Docker 引擎的方法。从 Docker 0.6 开始,docker启动容器时可以添加“privileged
”选项进入到特权模式,进入特权模式的容器,具有主机的几乎所有功能,包括内核功能和设备访问权限。
在这种模式下,容器中的 Docker Daemon
完全独立于外部,具有良好的隔离特性。但是这种方式却不被推荐,其原因是特权模式下的容器容易出现“容器逃逸”的安全问题。
Linux环境下,docker in docker
方式的启动脚本类似下面这样:
docker run --privileged ...
docker outside of docker
如上图所示,docker可以通过挂载/var/run/docker.sock
到容器中的方式,让容器可以访问容器外的Docker daemon
。此时的docker client
在容器中,docker daemon
在容器外的宿主机中。当在容器中执行docker命令时,容器外的docker daemon
就会作出响应。
Linux环境下,docker outside of docker方式的启动脚本类似下面这样:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
参考
[Docker] 掛載 /var/run/docker.sock 的用意?
关于/var/run/docker.sock
如何在 Docker 中使用 Docker
Docker-in-Docker vs Docker-out-of-Docker