Logo
Published on

Docker

Authors

0x00 引言

  Docker是一个开源的应用容器引擎,可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,类似于一种轻型的虚拟机。

  • Docker架构由三个部分组成:
    • 仓库: 一个仓库能存放大量镜像
    • 镜像: 相当于是一个root文件系统
    • 容器: 一个镜像运行时的实体,可以被创建、启动、停止、删除、暂停。镜像与容器的关系就像是类和实例

0x01 Docker基础知识

1 镜像分层
2 镜像存储
  2.1 写时复制与用时分配
  2.2 存储驱动

1、镜像分层
  单个Docker镜像能被实例化为多个容器,那么基于同一镜像的多个容器同时运行时是共用一个镜像,还是每个容器各复制一份镜像呢?
  为了回答上述问题,首先我们来看一个用于构建镜像的基础Dockerfile:

FROM ubuntu:18.04      # 基础镜像
COPY . /app            # 拷贝文件
RUN make /app          # 运行指令
CMD python /app/app.py # 执行Shell

  一个docker镜像由多个可读的镜像层组成,然后运行的容器会在这个docker的镜像上面多加一层可写的容器层,任何的对文件的更改都只存在此容器层。因此任何对容器的操作均不会影响到镜像。因此,基于同一镜像的多个可读写容器同时运行时是共用一个只读镜像。

2、镜像存储
  由上一节可知,容器 = 镜像 + 读写层。那么这些镜像与容器是如何在磁盘中存储的呢?
2.1、写时复制与用时分配

  • 目前所有的存储驱动都使用了CoW技术(copy-on-write),即写时复制与用时分配:
    • 写时复制: 只有容器要对镜像中的文件进行修改时,才会将镜像文件副本拷贝到自己的文件系统进行修改;
    • 用时分配: 启动一个容器,并不会为这个容器预分配一些磁盘空间,而是当有新文件(不存在于镜像中的文件)写入时,才按需分配新空间。

2.2、存储驱动

  • 常见的存储驱动为以下几个:
    • AUFS - AnotherUnionFS,是一种Union FS,文件级的存储驱动。镜像在存储时被分为多层;
    • OverlayFS - 是一种Union FS,文件级的存储驱动(Linux内核3.18后支持的)。镜像在存储时为单层;
    • Devicemapper - 块级的存储驱动(Linux内核2.6.9后支持的)。所有的操作都是直接对块进行操作,而不是文件。

0x02 Docker基础用法

1 仓库
2 镜像
3 容器
4 特殊指令

1、仓库

# 登陆Docker Hub注册账号 https://hub.docker.com

# 终端登陆/登出Docker账号
$ docker login
$ docker logout

# 向个人仓库推送镜像
$ docker push '用户名'/'仓库名':'tag号'

# 在个人仓库中寻找镜像
$ docker search '用户名'/'仓库名'

2、镜像

# 列出镜像列表
$ docker images

# 查找镜像
$ docker search ubuntu

# 获取镜像
$ docker pull ubuntu

# 删除镜像
$ docker rmi ubuntu

# 更新镜像
$ docker commit -m="XXX" -a="XXX" e218edb10161 runoob/ubuntu:v2
  -m: 提交的描述信息
  -a: 指定镜像作者
  e218edb10161: 容器ID
  runoob/ubuntu: 镜像所在仓库
  v2: 镜像tag号

3、容器

# 新建容器
$ docker run -itd '镜像名:版本' /bin/bash
$ docker run -itd '镜像名' /bin/bash # 默认版本 latest
$ docker run -itd '镜像ID' /bin/bash
  -i: 交互式操作
  -t: 终端
  -d: 后台运行
  /bin/bash: 交互式使用的终端
  
# 删除容器
$ docker rm -f '容器ID'

# 查看所有容器
$ docker ps -a

# 继续/停止/重新运行容器
$ docker start '容器ID'
$ docker stop '容器ID'
$ docker restart '容器ID'

# 进入容器 - attach(退出容器会导致其停止)
$ docker attach '容器ID'

# 进入容器 - exec(退出容器不会导致其停止)
$ docker exec -it '容器ID' /bin/bash

# 宿主机拷贝文件至容器
$ docker cp '宿主机文件路径' '容器ID':'容器文件路径'

# 容器拷贝文件至宿主机
$ docker cp '容器ID':'容器文件路径' '宿主机文件路径'

# 容器日志查看
$ docker logs -f '容器ID'
  -f : 跟踪日志输出
  --since :显示某个开始时间的所有日志
  -t : 显示时间戳
  --tail :仅列出最新N条容器日志

4、特殊指令

# 获取容器/镜像的元数据
$ docker inspect '容器ID/镜像ID'

# 显示Docker系统信息,包括镜像和容器数
$ docker info

# 查看容器资源使用情况
$ docker stats

0x03 Dockerfile构建镜像

  Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

1 命令摘要
2 Dockerfile实例
3 镜像构建
4 多阶段构建

1、命令摘要

  • FROM - 构建镜像基于哪个镜像
  • MAINTAINER - 镜像维护者姓名或邮箱地址
  • RUN - 构建镜像时运行的指令
  • CMD - 运行容器时执行的shell环境
  • VOLUME - 指定容器挂载点到宿主机自动生成的目录或其他容器
  • USER - 为RUN、CMD、和 ENTRYPOINT 执行命令指定运行用户
  • WORKDIR - 为 RUN、CMD、ENTRYPOINT、COPY 和 ADD 设置工作目录,就是切换目录
  • HEALTHCHECH - 健康检查
  • ARG - 构建时指定的一些参数
  • EXPOSE - 声明容器的服务端口(仅仅是声明)
  • ENV - 设置容器环境变量
  • ADD - 拷贝文件或目录到容器中,如果是URL或压缩包便会自动下载或自动解压
  • COPY - 拷贝文件或目录到容器中,跟ADD类似,但不具备自动下载或解压的功能
  • ENTRYPOINT - 运行容器时执行的shell命令

2、Dockerfile实例

FROM ubuntu:latest
ADD edr-server /home/dev/edr-server
ADD agent_service.sh /home/dev/agent_service.sh
RUN chmod a+x /home/dev/edr-server \
&& chmod a+x /home/dev/agent_service.sh
WORKDIR /home/dev
ENTRYPOINT ["./agent_service.sh","XXX"]

注意1: CMD或者ENTRYPOINT在容器运行后会执行命令,程序运行结束,容器也就结束。并且上述两个命令如果由多个,则只有最后一个起效 注意2: 如果需要使用CMD或者ENTRYPOINT在容器运行后开启多个服务,需要用其运行一个脚本,在脚本中使用'&'方式后台启动多个服务。 注意3: 如果希望CMD或者ENTRYPOINT脚本在运行完毕后不结束容器,则可在最后添加'/bin/bash'语句

3、镜像构建

# 进入Dockerfile所在目录
$ cd 'Dockerfile目录'

# 开始构建
# '.'代表上下文目录,即构建开始时所在目录
$ docker build -t '仓库名':'tag号' .

# 查看新镜像
$ docker images

4、多阶段构建
  Docker 17.05版本以后,支持了多阶段构建,允许一个Dockerfile 中出现多个 FROM 指令。   多个 FROM 指令时,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃。但是在后面的 FROM 指令中, 能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。使用示例:

# 编译阶段
FROM golang:1.10.3
COPY server.go /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

# 运行阶段
FROM scratch
COPY --from=0 /build/server / # 从编译阶段的中拷贝编译结果到当前镜像中
ENTRYPOINT ["/server"]
注意1: COPY 指令的--from=0 参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,0代表第一个阶段 注意2: scratch 是内置关键词,并不是一个真实存在的镜像。FROM scratch 会使用一个完全干净的文件系统,不包含任何文件(当然也可以替换为已存在的系统) 注意3: --from=0也可以替换为类似 --from=quay.io/coreos/etcd:v3.3.9 这种直接从hub镜像上的文件提取

0x04 特殊技巧

1 容器执行参数
2 容器/镜像导入与导出

1、容器执行参数

# --net 设定网络模式
# host模式: 相当于NAT模式
# none模式
# bridge模式(默认): 相当于桥接模式
# container模式: 与某个容器使用相同网卡信息
$ docker run -it --net=host xxx:xxx

# --privileged 使用容器内的root账户拥有真正的root权限
$ docker run -it --privileged=true xxx:xxx

2、容器/镜像导入与导出
  docker导入导出镜像可以使用docker save/load命令,导入导出容器可以使用docker export/import命令。具体区别如下:

作用导入/导出对象命令示例
docker save导出镜像(tar包)当目标为镜像时直接导出镜像; 当目标为容器时导出容器所使用的镜像docker save -o '压缩包名'.tar '镜像名'('容器名')
docker load导入镜像(tar包)导入镜像(不能指定镜像名称),覆盖已存在的同名镜像docker load -i '压缩包名'.tar
docker export导出镜像(tar包)导出容器当前状态为镜像docker export -o '压缩包名'.tar '容器名'
docker import导入镜像(tar包)导入镜像(能指定镜像名称),夺取已存在的同名镜像名,原镜像只保留IDdocker import '压缩包名'.tar '新镜像名'
注意1: docker save保存的是镜像(image),docker export保存的是容器(container) 注意2: docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像 注意3: docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称

0x05 引用文献

[1]https://www.runoob.com/docker/docker-tutorial.html [2]https://www.cnblogs.com/s-b-b/p/8533936.html [3]https://blog.csdn.net/iov_aaron/article/details/97135158