这里也放2个疑问: 自己构建的镜像为啥这么大? 查看容器时,Names 是啥?
前面也说了,docker 分为5个部分,但是 docker client 和 daemon 我们平常一般不用碰。(或者配置一次整体环境即可)操作基本还是集中在:
- 镜像
- 容器
- 仓库
整体上都是 docker COMMAND
,除了: docker version
, docker info
, docker --help
这类查看信息的指令。
Run ‘docker COMMAND –help’ for more information on a command
主要指令,如下: (但大都是围绕 镜像,容器,仓库展开的)
命令选项
也就是 –help 打印的。
主要就是 -l log等级
, -D
运行时指定为debug模式,-H
指定运行在哪个daemon上。
镜像命令
前面已经添加了官方的仓库 https://download.docker.com/linux/ubuntu
,可以直接从上面拉去内容。
测试一下,先看一下有哪些版本:(每个仓库会有多个镜像,用tag标示,如果不加tag,默认使用latest镜像)
查看当前系统中已经存在的 images 信息
上面一共使用了3个命令:
- docker search 镜像名字
- docker pull 镜像名字=tag
- docker images
注意: docker images
等同于 docker image ls -a
。
如果指定查看某类image,可以自己指定 docker image ls 关键字
:
1 | $ docker image ls ubuntu |
也可以使用 Go 模板语法:
1 | ## 类似 jsp 语法 |
或者指定间距: table { }
1 | $ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" |
如何创建一个自定义的镜像呢?
- 利用已有的镜像 (运行,修改,提交,更新镜像)—下面演示给 ubuntu 镜像安装 git 再打包镜像
安装完成后,退出;并查看当前 docker 引擎中运行的容器:
然后commit,即可生成一个新的镜像。(使用 container id 提交)
-m 指定了本次提交的信息,-a 指定了用户信息,并且用户信息之后还会用到。(tag信息跟在库名的冒号后面)
并且可以看到镜像中,已经存在了 merlin/ubuntu
,可以运行看看:
上面又设计了3个命令:
- docker run -it 库名:tag信息 命令
- docker ps -a
docker commit -m "提交信息" -a "用户信息" container_id 新库名:tag信息
但是 docker commit 慎用,实际环境中并不会这样使用。(这个操作是黑箱操作,除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知)
注意: docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit 定制镜像,定制镜像应该使用 Dockerfile 来完成。
用 docker diff 可以查看哪些东西被修改了(很多相关的都被修改了)。docker diff 容器名称或者容器id
。
利用 dockerfile 配置文件进行构建镜像:
实际上和 make 根据 makefile 编译文件没有两样,只不过这里的产物是镜像。
简单的文法指定了 一些版本信息,构建时需要运行的命令等,例如:
1 | # 说明该镜像以哪个镜像为基础 |
新建这个 Dockerfile 文件,然后执行 docker build
命令即可:
-t 用来指定库和tag信息,然后制定以下 Dockerfile 的目录。
但是明显看到,自己构建的镜像非常大!!! 还是删除了吧: docker rm
删除镜像前,必须删除与之关联的容器(容器已经退出,则不必关心了)
1 | docker rm container_name或者container_id |
为什么要删除?因为即使容器停止运行了,但他还是占用着存储空间(硬盘空间)。
这里涉及了一下命令: (docker build -t 库名:tag信息
也行)
- docker build -t=”库名:tag信息”
- docker rm 库名或者哈希值
- docker rmi 镜像名或者哈希值
也可以指定摘要信息进行删除: ( 库名@sha256:摘要信息
)
1 | $ docker image ls --digests |
另外,如果运行时就指定了 –rm 那么运行结束后,自动删除容器:
1 | docker run --rm -it merlin/gitdir /bin/bash |
批量删除: (利用 docker image ls -q
)
1 | ## 列出相关的 repository id, 然后一一删除 |
镜像也可以保存在 tar 文件,然后加载出来:(因为我本机已经有这个镜像了,所以看不出效果)
1 | docker save -o xxx.tar 库名:tag信息 |
tar 文件就可以把该镜像发送给另外的 daemon 进行加载。
<none>
镜像是啥?
docker pull 或者 docker build 的时候,如果新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none>
。
类无标签镜像也被称为 虚悬镜像(dangling image) ,可以用 docker image ls -f dangling=true
命令专门显示这类镜像。
但是这类镜像通常是没有意义的,一般可以用 docker image prune
进行删除。
容器命令
主要是运行,停止相关的命令,还算比较少。
- docker run
- docker start
- docker stop
- docker restart
- docker attatch
- docker rm 这个用于删除容器的,不说了,前面玩够了
run 的话每次都启动一个新的容器(hash值可以标识);start或者restart则是重用以前启动过得容器
先把简单的玩一下, docker start
, docker restart
, docker stop
,一看就是操作容器的。
运行,停止,重新运行都不成问题。但是为什么 stop 这么慢?想迅速杀死容器可以用 docker kill 容器id
。
- docker kill 相当于向容器里面的主进程发出 SIGKILL 信号 (强行终止,不能忽略)
- docker stop 相当于向容器里面的主进程发出 SIGTERM 信号 (先扫尾,后终止;信号可以被进程忽略)
有个问题是,我如何进入这个容器呢?(进去操作)
使用 docker attach container_id
附上去,就好像 gdb 调试进程一样。
但是 attach 还有一个作用,即运行的容器没有和终端绑定的时候,即在后台运行,不受终端控制的时候,例如 docker run 镜像 命令
此时没有用 -i 指定可以接收 stdin,也没有用 -t 指定控制终端(默认输出是stdout),那么进去是完全不受控制的:
这个时候要终止,只能再打开一个终端,然后 docker stop; 但是关闭当前终端没有任何用。(还有一个办法,看下面)
如果指定 -d
选项后台运行会如何?
即使是
docker run -it
指定接收当前 stdin 的控制命令;指定绑定当前终端;也无法停止这种疯狂。
但是进去(不论是 attach 还是 -it指定的),如果想退出来,并且保持容器运行怎么办? Ctrl+p, Ctl+q
,此时容器还是 up 状态。
总结一下就是:
- docker run 可以后台,可以指定 stdin控制,绑定终端
- docker attach 可以后期绑定终端,如果前期运行时没有绑定 -t 参数
- Ctrl+p, Ctrl+q 可以切出容器环境,而不停止容器的运行 (exit直接让容器退出了)
btw: docker ps -a
相当于 docker container ls -a
还有一个 docker run -p yyyy:xxxx
指定容器的 xxxx 端口映射到本机的 yyyy 端口; 运行时跟着的 /bin/bash
是让容器跑起来之后执行这个命令,即用户有 bash 可以使用。
下面补充三个容器命令:
- docker logs container_id
这个命令用于查看那些没有绑定到当前终端或者没有指定为标准输出的容器的运行信息,查看这些容器的删除。
- docker exec
一个容器运行时没有指定 -it,如果不用 docker attach,还可以用 docker exec 进入该容器:
1 | ## 进入正在运行的容器 (同时指定了进去之后运行 shell 命令) |
区别是,如果都指定了 -i 参数,那么从容器内部 exit 时,exec不会导致容器退出,但是 attach 则会。
- docker cp
在容器和虚拟机之间进行拷贝文件或者目录:
1 | ## 容器到本地 |
仓库命令
github 也就算了,代码&项目管理;这样好,又来了个 dockerhub: (区分 Repository 和 Registry服务器)
- 访问 dockerhub,如果没有账号,需要先注册一个
- 利用命令 docker login 登录 DockerHub,输入用户名、密码即可登录成功
- 将本地的镜像推送到 DockerHub 上,这里的库名前的 user 信息要和登录时的 username 一致
docker pull 或者 docker push 的库名都是 username/库名:tag信息
。
仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
因为登录后默认的远端仓库就是 docker hub,如果是你自己构造的私人远端库(利用 docker registry镜像构建),那么:
1 | $ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] |
总体的流程是:
从仓库(一般为DockerHub)下载(pull)一个镜像,Docker执行run方法得到一个容器,用户在容器里执行各种操作。Docker执行commit方法将一个容器转化为镜像。Docker利用login、push等命令将本地镜像推送(push)到仓库。其他机器或服务器上就可以使用该镜像去生成容器,进而运行相应的应用程序了。
下面是我的一个案例:
但是本地的 image 根本不是这个名字怎么办?自己 tag 标注一下
Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE。
1 | ## 为本地 image 标注用户名及版本信息 |
当然也可以不用重新标注,重新构建一个就好了。
其实这里也引发一个问题: 并非所有的 docker rmi
都会真正删除镜像。有可能只是取消了 tag,让某个镜像编程 Untagged
。
因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。
当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。
(正在运行的容器依赖某个镜像,那么这个镜像也是不能被删除的;应该先删除容器,再删除镜像)
如果我利用 docker 配置了一个开发环境,每次修改都基于这个版本,岂不是很爽? 反正 docker 是夸平台的。
跨平台那种事儿就交给 docker,我负责处理好镜像就可以了。(笑) — 实际并没有想的这么容易,因为 Golang 最终也是依赖 OS 环境的。
Merlin 2018.2 docker 用于开发环境应该很赞