如果不能正常显示,请查看原文 , 或返回

Docker 基本命令使用

Docker 基本命令使用

| Comments

从刚刚开始接触docker一直到现在,关于docker基本操作的整理都放在这里,每次用到一点就整理一点,还是放在blog上,比较好。

一个镜像可以生成多个容器 这个就像是版本控制一样 每次执行完一个命令 容器的版本信息都会更新一下 对应的容器的id号 就会相应地更新 这样 想恢复 也是十分容易的

由于每次的 sudo docker的命令基本上都是固定的 因此可以采用alias设置别名 alias dk=‘sudo docker’

关于docker的基本命令

http://blog.tankywoo.com/docker/2014/05/08/docker-4-summary.html

docker命令格式:[sudo] docker [flags] [command] [arguments]

docker info

这个会显示出当前docker容器的一些基本信息,以及当前docker daemon的一些基本信息和配置。如果想查看比较细致的信息,可以使用docker inspect的命令

docker pull

这个命令可以手动的来下载某些镜像的命令 不用等到运行的时候再去下载 这样会快一些 通常情况下 可以在docker hub中把相关的镜像查询下载好之后 再使用docker pull命令进行下载 通常是 docker pull 仓库名/镜像名这样来下载

docker ps

这个命令可以列出当前 host 中所有的容器 后面可以跟 -a 或者是 -l -l表示仅仅显示出最近的一个容器的 相关的信息 使用docker ps –n x 可以查看最后x个容器的信息,这个形式查看起来比较方便。 使用 –notrunc 命令可以查看出镜像的全部的信息 使用-q参数可以仅仅列出id 批量删除的时候会方便很多 比如使用几个命令结合起来 docker kill $(docker ps -a -q) 这个命令可以删除所有的正在正在运行和停止运行的容器,-a参数是列出全部的容器,-q参数是仅仅列出容器的id,kill命令会结束对应的容器的进程。 还有许多类似的批量清理的操作,比如http://www.jb51.net/article/56051.htm ,可以参考网上的相关内容。 关于批量删除none镜像的命令 这个后续要好好调研一下 http://www.tuicool.com/articles/R7jMZfq 注意其中这个

删除机器上启动失败的容器

删除机器上启动失败的容器 :docker rm $(docker ps -a -q) 由于正常运行着的容器需要采用 -f 参数才能删除,所以这样的话直接可以把没有启动成功的镜像删除。

删除机器上 repository以及 tag 都为 none (可能在build的过程中不成功)的镜像,就是先把none含有none标记的镜像都截取出来,再通过awk取出来id的那一列,再把这些id都整理成参数的形式,采用docker rmi $( ) 的形式直接批量删除:

docker rmi $(docker images | grep “none” | awk ‘{print $3}’)

也可以用其他的类似命令:

docker images -a |grep ‘^’|tr -s ‘ ’|cut -d' ‘ -f 3|xargs docker rmi

tr命令可以把中间多余的空格都去掉, 之后中间的间隔就只剩下一个空格了,之后使用cut命令切割出第三段的id的一列,之后传入给 docker rmi 的命令中,放在最后面。

注意awk命令的语法 后面接 {print $3} 的时候要使用单引号 才能正常工作

docker images

列出当前host中所有的镜像 注意有时候一个repository中可能有多个镜像 这时候就用tag标签来进行区别 每次使用镜像的时候 最好还是指明比较好 比如ubuntu:12.04 注意使用一些参数简化操作 比如-q 参数,只列出所有的镜像ID,删除多个敬相镜像的时候比较好用,同样的-q参数在使用ps的时候也很方便。

docker images –tree参数 可以显示出所有镜像的树形结构关系,这个用于查看依赖的话效果比较好。

在新的版本中,docker images -t的参数被取消了,可以采用这个工具来代替:

至于取消的原因可以参考这个issue,看得出来还是引发了比较激烈的讨论的:https://github.com/docker/docker/pull/5001

https://github.com/justone/dockviz 可以直接显示出树形结构,可以将代码直接跑在容器里,运行代码的时候,相当于每次启动一个容器。 也可以参考这个项目:

https://github.com/CenturyLinkLabs/docker-image-graph

docker logs + 容器的id

列出某个容器的输出的日志信息 show the things happening in a container

docker stop

表示停止某个运行的容器 具体的容器状态的信息可以根据status参数来看 后面可以接多个容器name名称 还是比较方便的 可以一次性stop多个容器

docker top + 容器id

可以查看运行在这个容器中的进程信息 docker inspect + 容器id 可以查看出这个容器的 基本的配置信息 返回的是一个json格式的信息 比如每个容器的 id之类的相关的信息 可以结合一些linux的命令比如grep选择自己所需要的关键的信息

docker rm + 容器id

是要删除某一个容器 删除之前要先通过docker stop命令 使这个容器停止运行 后面加上-f之后可以强制删除 即使原先的容器没有stop任然可以删除

docker rmi

这个命令可以用来删除host中的某个镜像文件 首先要确定一下,在当前已经激活的实例中,没有在这个镜像的基础上生成的容器,这样才能删除这个镜像

docker version

列出当前docker的相关信息 语言版本 客户端 go语言版本。。。

docker tag

命令可以来修改已经存在的镜像的标签例如下面的格式: sudo docker tag 5db5f8471261 ouruser/sinatra:devel 5db5f8… 表示这个镜像的id ouruser表示用户名 /sinatra表示仓库名 后面接着的是新的标签。原来的镜像还没变,用新的标签对原来的镜像做了一份copy

docker save/ load

这两个命令和在一起使用比较好,save是将一个镜像文件存储成tar包的形式,而load是将一个tar包装载成为docker daemon可以使用的镜像。 比如直接 sudo save imageid > imagename.tar就会将这个镜像所打包成的tar文件存储在当前的目录下。 之后ls –sh imagename.tar命令可以查看当前这个镜像的大小。之后把这个tar文件拷贝到另外的一个docker daemon上,就可以直接通过docker load来加载镜像,之后直接 sudo docker load < busybox.tar 这样就可以将busybox.tar文件中所包含的镜像导入,加载成image的形式,之后再docker image就可以发现,新的镜像已经加入了进来,之前一直没怎么用过这个命令,一直忽略掉了,并不是只能通过registry来上传下载镜像,直接通过load命令就可以导入镜像所构成的tar文件,以后对待具体知识点的时候一定要虚心点。

docker push

这个命令可以将已经构建好的容器推送到自己的仓库当中,这个命令可以push一个本地私有仓库或者是某一个镜像到远程的仓库中 push的过程中会自动提示进行登录。 一般推送某个固定的镜像的话用 docker push user/repo:tag 来进行 中途会提示 进行用户登录的相关操作 要注意一点 在通常push之前要先使用tag 函数对本地的镜像加上用户名以及仓库信息 这样才能正常push具体可以参考docker push的标记

docker pull

采用这个命令可以从docker hub中拉镜像下来,要注意的是,如果对应的hub类型为private的,一定要先docker Login才可以拉镜像下来 比如: docker pull wangzhe/test:tag

docker build

docker build [OPTIONS] PATH | URL | -

这个主要是通过dockerfile来创建新的镜像 注意build的时候如果要指定新的dockerfile 需要使用-f 的参数 最后的那个路径就是build镜像时候的上下文环境。

build的时候是有许多参数的,之前使用的时候都采用直接用默认的情况了,这次遇到一个小坑,特别记录一下,主要是–no-cache参数的使用,默认情况下,是使用缓存的,但我的案例是,要把一个修改过的tomcat build到一个镜像中,作为基础镜像使用,由于要不断修改那个tomcat中的配置,不断重新build,之后再看是否ok但是在Dockerfile中 对应的那句 COPY ./tomcat /apache/tomcat 这个是不变的,如果采用默认参数的话,这一层就会使用之前已经build好的,不再重新build,里面的tomcat也还是旧的。所以要显式地加上 –no-cache的参数,这样就会重新构建新的那一层,才能达到对应的效果。

docker commit

docker commit /redis

docker start/stop/wait+ container id

对容器中运行着的进程进行操作。特别要说明一下 stop命令仅仅是发送了一个SIGTERM信号给daocker容器中正在运行着的进程。如果要想比较彻底的结束容器中进程的运行,可以使用Kill命令,这会发送一个SIGKILL(终止信号)给容器中运行的进程。 注意这个时候仅仅是容器中的进程结束了,但是整个容器还并没有被删除掉。除非用rm命令,注意这里都是对容器中的进程进行的操作。

docker attach

通过这个命令可以进入一个运行中的容器。比如一个容器,先start之后,再直接attach+容器id 就可以进入容器的内部,相当于直接进入了命令行,在里面进行操作。

docker port

这个还是比较有用的 可以查询容器的端口的相关的信息 docker port + 容器的id + 容器的端口 这个命令是用来查询 容器的对应端口和主机的哪个端口是对应的映射关系,这个返回的是主机的对应端口。 要是想映射多个端口的话,只需要在run的时候多次指定-p参数即可 –p port1:port2 –p part3:port4 这个样子

docker inspect

可以根据指定的格式 查询某个配置的具体的信息 要是不指定的话 会显示出容器的全部的信息 We can also narrow down the information we want to return by requesting a specific element, for example to return the container’s IP address we would:

$ sudo docker inspect -f ‘’ nostalgic_morse 172.17.0.5

docker cp

将容器中的文件夹直接copy到宿主机上 docker cp :/file/path/within/container /host/path/target

docker exec

这个是1.3中新加入的命令,具体可以参考 http://dockerpool.com/article/1413517352

这个功能比较给力,感觉像是一种注入的意思。 就像是在给运行中的容器打了一个洞,通过这个命令,可以与运行中的容器进行交互(即使是最初被指定了background参数的容器),比如在运行中的容器之中启动一个进程或者执行一个命令,这在以前的版本都是不可以做到的,貌似可以用nsenter来进行(这个具体还没看 不知道是什么)交互。

docker exec -t -i 298a5dd8 /bin/bash 这个例子中我们为已运行的容器创建了一个新的bash session,之后我们可以利用这个session来想容器发布其他命令。 这样可以通过-t –i 并且使用exec命令来进入这个运行时容器的终端里,或者不需要进入终端,比如要添加一个目录,直接在后面运行对应的命令就好了。 如果这个容器在开始的时候被指定里的-d的参数,那在使用exec的时候貌似也要加上-d的参数。 动态添加命令的方式确实还是比较方便的

关于创建images

第一种方式 利用base image

在原有的base image基础上改造(base image->实例->执行更新操作->保存为新的镜像) 首先下载好一个 base image 比如一个ubuntu的base image 每次在这个image中执行一次相关的操作 对应的版本号就会更新一次 相应的镜像也会更新 将更新好的镜像存起来 可以上传到docker hub中 这里可以采用commit命令

docker commit 命令后面 -m参数表示这次提交修改的信息 就像是提交了一个新的版本一样 这个与git很像了 比如-m=”Add json” -a 参数用来指明作者的信息 是谁提交了这次的修改操作

先要把docker containr容器改变成一个images 用下面的语句: dk commit -m=“add apt-get” -a=“goudan” a3debc1 wangzhe/test:V2 之后再使用 docker images查看镜像 就有了我们所添加进去的那个镜像了

第二种方式 利用Dockerfile

第一种虽然可以对一个镜像进行扩展,但是比较笨重 我们可以使用docker build的方式来扩展一个镜像 sudo docker build -t=“ouruser/sinatra:v2” Dockerfile文件的路径 -t参数表示的是新生成的镜像属于用户ouruser 这个用户的仓库名是sinatra tag标记的V2 若是已经到了Docker文件的目录下 直接用一个 . 来表示Dockerfile文件所在的路径为当前路径,或者你也可以自动进行检测。 一下是一个dockerfile文件中的内容:

# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith 
RUN apt-get -qq update
RUN apt-get -qqy install ruby ruby-dev
RUN gem install sinatra

具体的解释 可以参考https://docs.docker.com/userguide/dockerimages/ 之后再用docker images命令 就可以查看到我们新生成的镜像了 一些常见的dockerfile的命令

FROM 指定base image 必须作为dockerFile的第一行的开头
MAINTAINER 确定镜像的作者
RUN 
RUN has 2 forms:
RUN  (the command is run in a shell - /bin/sh -c)
RUN ["executable", "param1", "param2"] (exec form)
这个最好理解,就是说要在base image的基础上执行哪些命令。
EXPOSE
EXPOSE  [...]
这个会告诉Docker,容器在运行期间会监听指定的端口,
ENTRYPOINT
表示每次在镜像初始化的时候需要执行的命令,一般只设置一个,要是有多个命令的话,彼此之间需要用&&来隔离开。

关于docker run的参数

run 中的具体参数还是很多的,这里只总结了平时自己常用的几个,具体细节官方文档中已经很详细了。 当指明某个镜像 docker首先会在本机上的镜像文件中进行查找 如果没有找到 就从docker Hub 中下载一个 sudo docker run + 参数信息 + 镜像的名称 + 可以运行的指令 具体的可以参考下面连接 https://docs.docker.com/reference/run/ http://docs.docker.com/reference/commandline/cli/#run 注意参数信息一定要写在镜像名称的前面

-i

保持对于容器的stdin为打开的状态 这个貌似一般都加上-i参数

-t

为这个容器分配一个虚拟的终端,一般 -i 与 -t 参数都是结合在一起使用的 这样交互比较好一点

-d

让docker容器在后台中运行

-P

这个表示的是将任何容器内部锁需要的网络端口号映射到我们的host上 web应用一定要加上这个参数的,比如添加之后容器跑起来,然后在ports的状态信息的位置。端口会显示成:0.0.0.0:49155->5000/tcp 之后在其他机器上 直接 访问 hostip:49155就可以显示出对应的web应用了,当然,端口的映射关系也可以通过手动的模式来指,不加的时候端口映射是默认进行的。sudo docker run -d -p 5000:5000 training/webapp python app.py,这个会把容器内部的5000端口,映射到host的5000端口上,但是这样可能不太灵活,限制了一个容器一个端口,即使这个容器不运行的时候,其他容器也无法使用5000这个端口了。(要是多用户的时候 这个还是很重要的一个问题)当然可以直接使用 docker port CONTAINER PRIVATE_PORT来查询 容器内部的 private_port 映射到 host上的端口号是多少。 -p参数可以运行很多次来指定多个服务的映射关系。 要是直接 -p :8080的话,这样系统会找一个没有被占用的host端口来映射到容器的8080端口上面,在多个用户使用同样一个服务的情况下这样还是比较好一点的。

-v

挂载命令比较神奇,可以把本机上的一个文件目录挂载到容器中,这样数据共享就会非常方便。 sudo docker run -d -P –name web -v /src/webapp:/opt/webapp training/webapp python app.py 比如上面这个命令就是exit的目录,若是容器中没有对应的目录的话,就会新建一个出来。更具体的使用方式参考官方文档。

-name

这个参数可以指定由镜像所生成的container的名称。

–rm

通常情况下,当容器退出的时候,相关的数据还会持久化在本地,便于查看错误信息。若是对于一些运行时间短的前端进程,再退出之后不希望相关信息保留,就可以在run的时候,使用–rm参数,这样就可以在容器退出之后将其自动删除。

–restart

通过设置这个参数,可以指定容器的restart策略。比如指定成always,如果exit code是非零的,容器就会重启。 Docker linking System Docker的linking系统可以将多个容器连接在一起并且在它们之间共享连接信息。Lingking system可以使容器形成父子结点的关系,父容器可以查看子容器的相关信息。 Docker通过容器的名字来对容器进行连接,每次由镜像新生成一个容器的时候,可以自己来对一个容器进行命名操作。可以通过容器名称来提示容器的功能 sudo docker run -d -P –name web training/webapp python app.py 这个新生成的容器的名称就是 web 注意 容器的名称在本地仓库中必须要是唯一的,你要是想新生成一个名为web的容器,除非先用rm命令把旧的web容器删除掉才可以。

–link标签

可以是两个容器之间以安全的方式进行交互。 sudo docker run -d –name db training/postgres 生成一个名为db的容器 sudo docker run -d -P –name web –link db:db training/webapp python app.py 生成一个名为web的容器,并且将web容器与之前的db容器link起来,link的名称为db –link标记的命名格式: –link name:alias 其中 name表示我们要link的容器的名称,alias表示这个链接的别名,上面的一个命令是将我们创建好的web容器连接箱db容器,连接的别名设为 db.

在db容器的那一行中,显示出来的是上面的信息 在db部分 web/db 表示的关系是 parent/child的关系(这里与指导手册中不一致?) link哪一个容器,斜杠后面就是哪一个容器。 Parent container 可以通过连接获得子容器的信息,Docker在两个容器之间内部创建了一个安全的隧道,而不需要将端口暴露出来。 关于link标记的更具体的内容可以参考 《第一本Docker书》的p113 sp114页

镜像构建的相关资料

数据库镜像的构建:

数据库服务的构建采用的是这个 注意要通过修改-e参数来调整 密码 要不然就是 随机生成的密码 这个还是比较麻烦的 https://registry.hub.docker.com/u/tutum/mysql/

Java 基本环境的构建

这个还是有点麻烦,总体上参考这个: https://www.digitalocean.com/community/tutorials/how-to-install-java-on-ubuntu-with-apt-get 其中有一个地方某些情况下不行 就是执行刚开始的三条命令的时候 会出现add-apt-repository not found 的错误 于是用到了下面的解决方案 这个可能与ubuntu的版本信息有关 http://stackoverflow.com/questions/13018626/add-apt-repository-not-found 要用这条命令来代替: sudo apt-get install software-properties-common python-software-properties 关于Tomcat的环境 这个是最想吐槽的。千万不要用apt-get的那种方式,明明tomgcat已经启动了,可还是显示fail错误,相当痛苦。 直接从官方下载下来最新的.tsr.gz文件,直接解压后就能用了,只要执行bin文件夹下的startup.sh这个会自动检测所需要的环境变量,挺不错的。 注意闪退的现象 以下无效方式:

ENTRYPOINT service tomcat7 start #运行几秒钟之后,容器就会退出
    CMD service tomcat7 start  #运行几秒钟之后,容器就会退出

这样有效:

ENTRYPOINT service tomcat7 start && tail -f /var/lib/tomcat7/logs/catalina.out
 或者
CMD service tomcat7 start && tail -f /var/lib/tomcat7/logs/catalina.out

要一直有东西输出的才可以 最后一个命令是循环运行的 对于开机自动启动 还是不知道怎么弄 还是通过dockerfiles的命令来操作吧 注意制作镜像之前要把之前测试过的 catalina.out中的内容清理干净

最后弄好的这个镜像: wangzhe/test java7+tomcatv2 294ca9748b58 这个版本是可以用的 比较干净的一个 是通过Dockerfie中设置的tomcat自启动的模式 这个版本还没有挂载硬盘 直接 sudo docker run -i -d -t -p :8080 294ca9 这样就直接可以在后台持续输出catalina.out中的内容了,并且容器持久地不会被关闭掉,还是很赞的。(这个是之前弄的 感觉比较渣 还是用官方的好一点)

后来又看了一下官方制作的tomcat镜像的dockerfile,最后写的是 CMD [“catalina.sh”, “run”] 这个命令,就是镜像在启动的时候就会执行 catalina.sh run 貌似这样就会显示出控制台,信息就会一直输出出来,容器就会不自动关闭。直接采用startup.sh的话,容器启动一下之后就会自动关闭。 总之在制作镜像的时候要注意CMD命令的使用

挂载的问题

怎么样通过挂载 来把war包直接放在webapps的目录下?将环境和目录分开 运行run命令的时候 在-v参数的选取上要进行如下的设置 挂载目录设置: -v app所在的文夹(可以是war包之类的 具体上编写应用的时候可以通过 以每个用户名作为一个文件目录):/lib/tomcat7/apache-tomcat-7.0.54/webapps/(这个是容器中tomcat的war包所在位置 就是那个V2版本的) 但是还有个问题 这样挂载会把tomcat的root文件夹覆盖掉 就找不到toncat初始页面的内容了 在host机器上生成初始的挂载目录的时候 就将相关的的内容一并 初始化时 先将mounttemplete文件夹复制一份 修改名称为 用户名 然后将上传的 app 拷贝一份进去 就OK了

当时的挂载信息

(docker run -i -d -p :8080 -v /home/wangzhe/webapps:/lib/tomcat7/apache-tomcat-7.0.54/webapps/) 注意哪里有斜杠 哪里没有斜杠

关于AUFS

http://docs.docker.com/terms/layer/ AUFS (AnotherUnionFS) 是一种 Union FS, 简单来说就是支持将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)的文件系统, 更进一步的理解, AUFS支持为每一个成员目录(类似Git Branch)设定readonly、readwrite 和 whiteout-able 权限, 同时 AUFS 里有一个类似分层的概念, 对 readonly 权限的 branch 可以逻辑上进行修改(增量地, 不影响 readonly 部分的)。 典型的启动Linux运行需要两个FS: bootfs + rootfs:

bootfs (boot file system) 主要包含 bootloader 和 kernel, bootloader主要是引导加载kernel, 当boot成功后 kernel 被加载到内存中后 bootfs就被umount了. rootfs (root file system) 包含的就是典型 Linux 系统中的 /dev, /proc,/bin, /etc 等标准目录和文件。 对于不同的发行版,bootfs基本上是一致的但是rootfs可能有些差别。

典型的Linux系统是首先将rootfs设置为readonly,进行一系列检查之后将其改为read-write模式来供用户使用。

当Docker挂载rootfs的时候,开始是以readonly的方式进行的,就像是传统的Linux的boot的挂载方式一样,之后利用union mount来挂载一个read-write file system在一个read-only file system之上,并且允许再次将下层的FS设置成readonly模式的并且向上叠加,实际上可能有多层read-only file system彼此叠加在一起,我们把这些文件系统中的每一个成为一个layer,这样一组readonly的文件和一个read-write的文件层共同构成了一个container在运行时候的文件系统状态。

首先,顶层的可读写层里边什么都没有,任何时候进程创建一个文件,这就发生在顶层layer中。如果更低的只读层需要升级某些文件,之后对应要修改的文件层就会被复制到最顶端的可读写层,并且在副本里面进行修改。之后底层文件的版本就不会再被任何app看到了,但是之前的文件仍然存在那里,并没有发生改变。

得益于AUFS的特性, 每一个对readonly层文件/目录的修改都只会存在于上层的writeable层中。这样由于不存在竞争, 多个container可以共享readonly的FS层。 所以Docker将readonly的FS层称作 “image” - 对于container而言整个rootfs都是read-write的(表面上),但事实上所有的修改都写入最上层的writeable层中, image不保存用户状态,只用于模板、新建和复制使用。

上层的image依赖下层的image,因此Docker中把下层的image称作父image,没有父image的image称作base image。因此想要从一个image启动一个container,Docker会先加载这个image和依赖的父images以及base image,用户的进程运行在writeable的layer中。所有parent image中的数据信息以及 ID、网络和lxc管理的资源限制等具体container的配置,构成一个Docker概念上的container。

返回