Docker 笔记

Posted on July 16, 2015

核心概念

  • Registry 注册服务器: Registry包含一个或多个Repository
  • Repository 仓库: Repository包含一个或多个Image(有一个或多个Tag与之关联)
  • Image 镜像: 用GUID表示,和tag对应
  • Container 容器

镜像

  • pull

    docker pull 注册服务器/Repository:镜像标签 注册服务器可选, 标签可选

    docker pull ubuntu

  • docker images

    • REPOSITORY: 省略默认注册服务器
    • TAG
    • IMAGE ID
    • CREATED
    • VIRTUAL SIZE

    --filter "dangling=true" 显示悬挂镜像层

    --digests 显示镜像digest

  • docker 已存在的镜像 镜像:新标签 增加本地镜像标签

    docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest

  • docker inspect 镜像ID

  • docker search mysql 搜索镜像

  • docker rmi 镜像名或者id 删除镜像

    删除正在运行容器的镜像会报错, -f可以强制删除(不推荐)

  • 创建镜像

    • 基于容器创建: docker commit -m "提交消息" -a "作者" 容器ID 仓库/镜像:标签

      仓库可以在这里新创建, 标签可选

      docker commit -m "Added a new file" -a "Docker Newbee" a925cb40b3f0 test

    • 基于本地模板导入 TODO

  • docker save

    docker save -o ubuntu_14.04.tar ubuntu:14.04 -o 存于文件, 而不是标准输出

  • docker load

    docker load --input ubuntu_14.04.tardocker load < ubuntu_14.04.tar

  • docker push

    docker push user/test:latest


容器

  • 创建

    • docker create -it ubuntu:latest i 打开容器STDIN, t 分配伪终端, 创建后处于停止状态
    • docker run -t -i ubuntu:14.04 /bin/bash

      -d 以守护进程运行容器

      --name XXX 指定名字, 如果不指定, 名字将是随机生成; 命名规则: [a-zA-Z0-9_.-]

      --log-diver=XXX 改变日志驱动 TODO

      --restart 重启模式 默认不会自动重启; 模式可选(always 不管退出码) (on-failure 非0退出码才重启) (on-failure:数字 非0重启次数)

      -v 将宿主目录中文件作为卷挂载到运行容器中 -v $PWD/website[:/var/www/html/website][:rw|:ro] 宿主文件改变动态生效; 容器中也可以改变这个宿主的文件!

      如果容器目录不存在, docker 会自动创建一个

      都会返回一个容器ID(也是容器的主机名)

      sample_job=$(docker run -d busybox /bin/sh -c "while true; do echo Docker; sleep 1; done")

      docker logs $sample_job

      -p [宿主端口:]docker端口 如果没有宿主端口, 将随机分配32768~61000的其中一个

      -P 将容器的EXPOSE的端口都公开, 绑定宿主机器随机端口

      --net XXX 启动时指定网络

      -h XXX 指定启动容器的hostname

      --entrypoint 覆盖dockerfile里的ENTERPOINT

      -w 覆盖WORKDIR

      -e 指定环境变量

      -u 指定USER

  • 停止

    docker stop [-t|--time[=10]] 容器ID 它会首先向容器发送SIGTERM信号,等待一段时间后(默认为10秒),再发送SIGKILL信号终止容器。

    docker kill 命令会直接发送SIGKILL信号来强行终止容器。

  • 查看

    • docker ps

      -a Show all containers (default shows just running)

      -l 列出最后运行的容器, 无论其当前是运行还是停止

      -n 数字 类似-l 指定个数

      -q 只展示容器id

      --filter , -f 条件过滤 如 -f status=exited

    • docker top id_or_name

      查看运行容器的内部当前进程

    • docker stats id_or_name

      动态查看容器资源消耗, CPU, 内存, IO等

    • docker inspect id_or_name

  • 启动

    处于终止状态的容器,可以通过docker start命令来重新启动

    启动或者重启, 会沿用run时的命令进行启动

  • 重启

    docker restart $sample_job

  • attach

    docker attach name_or_id

    附着在相同容器上的….完全同步

    只能附着正在运行的容器

    TODO: 如果执行没有shell, attach什么?

  • exec

    在正在运行的容器中执行额外命令

    • 执行后台任务: -d, 如果没有这个参数执行前台任务(有回显)
    • 执行交互式: -it

    容器被修改后, id不会变

  • 删除

docker rm ...

  • logs

    docker logs id_or_name

    -f 持续监控输出日志

    -t 日志输出时带上时间戳

    --tail 数字 从后面多少行开始, 而不用从头开始

    容器日志统一保存在/var/log TODO

  • 将容器的状态保存为镜像

    docker commit $sample_job job1 镜像名称只能取字符[a-z]和数字[0-9] (貌似下划线也可以)

  • 查看宿主端口

    docker port id_or_name 80 80 为容器内的端口


Dockerfile

  • INSTRUCTION argument

  • 指令不区分大小写。命名约定为全部大写

  • FROM 必须是第一条指令

  • RUN

    每次执行, 会创建一个新的镜像层并commit

    默认使用/bin/sh -c来执行后续命令(TODO 使用数组的区别)

  • EXPOSE

    可以指定多个

  • CMD

    指定容器启动时要运行的命令

    • 数组形式
    • 字符串形式: 将把字符串传递给/bin/sh -c执行, 不推荐

    run 的执行命令可以覆盖CMD命令

    CMD 只有最后一条有效

  • ENTERPONIT

    类似CMD

    不会被run的命令覆盖, run传递的参数可以传给ENTERPONIT

    run 时可以指定--entrypoint参数覆盖ENTERPONIT指令

    同时使用ENTERPONIT, CMD时, CMD 退化为参数传递给ENTERPONIT, 不过CMD 同样可能被run参数覆盖

  • WORKDIR

    工作目录, CMD ENTERPONIT 的工作目录

    run 时 -w 可以覆盖

  • ENV

    环境变量可以在后续指令中使用 WORKDIR $DIR

    docker run 时可以从参数传递: -e "WEB_PORT=8080"

  • USER

    指定用户, 默认是root

    可以指定用户组: USER user:group

  • build dockfile所在PATH

    -t 仓库:镜像tag 如果不指定tag, 默认将是latest

    -f Name of the Dockerfile (Default is ‘PATH/Dockerfile’)

    --no-cache 不使用构建缓存

    构建上下文将传入docker daemon, 如果有不希望传入的文件, 可以在构建上线文中使用.dockerignore文件声明

  • VOLUME

    用于共享代码, 数据, 管理日志等.

  • ADD 构建环境中的相对path 容器中的绝对path

    如果是压缩文件, 会自动解压

    构建环境中的path: 不能超出构建环境, 可以是文件, 目录(需要以斜线结尾), url

    容器中的path: 如果不存在, docker会自动创建全路径

    ADD 会破坏缓存

    貌似不会覆盖已有文件(TODO)

  • COPY

    类似ADD, 不会进行解压

  • 破坏缓存技巧(有时需要执行软件源更新)

    声明ENV REFERSHED_AT 时间 如果修改这个时间, 后续的操作将不使用模板缓存

  • 查看镜像构建历史

    docker history image_id 可以看到镜像的每个构建命令以及对应的中间镜像

  • LABEL

    增加镜像元数据(key-value), 推荐元数据都放在一条LABEL中, 防止创建过多的镜像层

  • ARG

    用于指定docker build 时可以传递进来的参数(白名单和可选的默认值), 参数形式: --build-arg 参数名=值

    Dockerfile 使用参数形式: $参数名

    在ARG指令定义变量之前引用这个变量的得,都会得到空值; 不像ARG指令,ENV的值始终会存在于镜像中.

    使用ENV定义的环境变量始终会覆盖同一名称的ARG指令定义的变量

    不推荐在构建期间的命令行传递密码如github密钥,用户凭证等数据。构建期间设置的变量可以通过docker history命令来查看

  • ONBUILD

    添加构建触发器: 当本镜像被用作其他镜像的基础镜像, 此命令包含的语言会插入到子镜像的from后面, 在build时自动执行

    只能被继承一次, 孙子镜像中不会触发


Network

  • 创建

    docker network create 网络名称

  • 查看

    docker network inspect 网络名称或id

    d network ls

  • run时加入

    --net XXX 启动时指定网络

    网络中其他容器的hosts中会自动记录刚加入的容器的 ip host映射2条: (TODO 貌似现在的版本没有记录, 但是还是连得上)

    ip 容器主机名.网络名 ip 容器主机名

  • 动态加入

    docker network connect 网络名 容器名称

  • 断开

    docker network disconnect 网络名 容器名称

一个容器可以属于多个Network, TODO: 一个容器的ip是一个还是多个?


VOLUME

  • run 时挂载

    可以指定在容器上挂载指定的主机目录

    -v 将宿主目录中文件作为卷挂载到运行容器中 -v $PWD/website[:/var/www/html/website][:rw|:ro] 宿主文件改变动态生效; 容器中也可以改变这个宿主的文件!

    如果容器中的目录存在, 将被隐藏掉, 展示宿主目录内容

  • VOLUME 指令

    不能指定容器中挂载目录

  • -volumes-from

    授权一个容器访问另一个容器的Volume


docker-compose

见官方安装指南

Linux 安装:

sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-uname -s-uname -m -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose


技巧/杂记

删除容器

  • 删除正在运行的容器: docker kill $(docker ps -a -q)

  • 删除已经停止的容器 docker rm $(docker ps -a -q)

    docker rm没有加-f参数,运行中的容器不会删掉

  • 删除已经停止的容器: docker rm $(docker ps -aq -f status=exited)

  • 删除指定镜像的容器 docker ps -a | awk '{ print $1,$2 }' | grep '镜像名' | awk '{print $1 }' | xargs -I {} docker rm {}

删除镜像

  • 删除untagged images,也就是那些id为None的image docker rmi $(docker images | grep "^<none>" | awk "{print $3}")

  • 删除所有未打标签的镜像 docker rmi $(docker images -q -f dangling=true)

    https://segmentfault.com/a/1190000011153919

  • 删除所有镜像: docker rmi $(docker images -q)

  • 删除指定镜像的所有tag, shell 函数:

    dockercleanrepo(){
      docker images --no-trunc --format ' ' \
      | grep "[email protected]" | awk '{ print $1 }' | xargs -t docker rmi
    }
    

Prune

不同状态的镜像:

  • 已使用镜像(used image): 指所有已被容器(包括已停止的)关联的镜像。即 docker ps -a 看到的所有容器使用的镜像。
  • 未引用镜像(unreferenced image):没有被分配或使用在容器中的镜像,但它有 Tag 信息。
  • 悬空镜像(dangling image):未配置任何 Tag (也就无法被引用)的镜像,所以悬空。这通常是由于镜像 build 的时候没有指定 -t 参数配置 Tag 导致的

docker system df

查询镜像(Images)、容器(Containers)和本地卷(Local Volumes)等空间使用大户的空间占用情况:

  • Images
  • Containers
  • Local Volumes
  • Build Cache

docker system df -v 详情

docker system prune

默认会清除:

  • 已停止的容器(container)
  • 未被任何容器所使用的卷(volume)
  • 未被任何容器所关联的网络(network)
  • 所有悬空镜像(image)

添加 -a 或 --all 参数后,可以一并清除所有未使用的镜像和悬空镜像

可以添加 -f 或 --force 参数用以忽略相关告警确认信息

  • 通过kubectl删除以及完成的pod:

    kubectl get pods -a | grep 'Completed' | awk '{ print $1 }' | while read line; do kubectl delete pod $line; done

镜像清理:

  • 删除所有悬空镜像,但不会删除未使用镜像

docker rmi $(docker images -f "dangling=true" -q)

docker image prune

  • 删除所有未使用镜像和悬空镜像

    docker rmi $(docker images -q)

    docker image prune -a

  • 加过滤删除

    docker image prune -a --filter "until=24h" created more than 24 hours ago

容器清理

  • 删除所有已退出的容器

    docker rm -v $(docker ps -aq -f status=exited)

  • 删除所有状态为 dead 的容器

    docker rm -v $(docker ps -aq -f status=dead)

卷清理

  • 删除所有未被任何容器关联引用的卷:

    docker volume rm $(docker volume ls -qf dangling=true)

    docker volume rm $(docker volume ls -q)

参考:


volume

  • docker volume ls
  • docker inspect volume XXX
  • docker volume rm XXX 不能删除还在使用的卷

Namespace

一个Pod里的多个容器:

[email protected]:~# ls -al /proc/6567/ns/
lrwxrwxrwx 1 root root 0 Sep 23 22:52 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Sep 23 22:52 ipc -> ipc:[4026532470]
lrwxrwxrwx 1 root root 0 Sep 23 22:52 mnt -> mnt:[4026532545]
lrwxrwxrwx 1 root root 0 Sep 23 22:52 net -> net:[4026532473]
lrwxrwxrwx 1 root root 0 Sep 23 22:52 pid -> pid:[4026532547]
lrwxrwxrwx 1 root root 0 Sep 23 22:52 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Sep 23 22:52 uts -> uts:[4026532546]
[email protected]:~# ls -al /proc/6703/ns/
lrwxrwxrwx 1 root root 0 Sep 23 22:55 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Sep 23 22:55 ipc -> ipc:[4026532470]
lrwxrwxrwx 1 root root 0 Sep 23 22:55 mnt -> mnt:[4026532548]
lrwxrwxrwx 1 root root 0 Sep 23 22:55 net -> net:[4026532473]
lrwxrwxrwx 1 root root 0 Sep 23 22:55 pid -> pid:[4026532550]
lrwxrwxrwx 1 root root 0 Sep 23 22:55 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Sep 23 22:55 uts -> uts:[4026532549]
  • 相同的namespae: cgroup, ipc, net, user
  • 不同的namespae: mnt, pid, uts

联合文件系统

讲得很清楚:

https://blog.csdn.net/luckyapple1028/article/details/77916194

https://blog.csdn.net/luckyapple1028/article/details/78075358


TODO

How to generate a Dockerfile from an image?

深入了解Docker存储驱动

Docker五种存储驱动原理及应用场景和性能测试对比


参考资料