Docker command line 学习笔记
一个镜像可以生成多个容器 这个就像是版本控制一样
每次执行完一个命令 容器的版本信息都会更新一下 对应的容器的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}')
**注意awk命令的语法 后面接 {print $3} 的时候要使用单引号 才能正常工作
docker images
列出当前host中所有的镜像 注意有时候一个repository中可能有多个镜像 这时候就用tag标签来进行区别 每次使用镜像的时候 最好还是指明比较好 比如ubuntu:12.04
注意使用一些参数简化操作 比如-q 参数,只列出所有的镜像ID,删除多个敬相镜像的时候比较好用,同样的-q参数在使用ps的时候也很方便。
docker images –tree参数 可以显示出所有镜像的树形结构关系,这个用于查看依赖的话效果比较好。
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镜像时候的上下文环境
docker commit
docker commit <container_id>
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 '{{ .NetworkSettings.IPAddress }}' nostalgic_morse
172.17.0.5
docker cp
将容器中的文件夹直接copy到宿主机上
docker cp
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 <ksmith@example.com>
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 <command> (the command is run in a shell - /bin/sh -c)
RUN ["executable", "param1", "param2"] (exec form)
这个最好理解,就是说要在base image的基础上执行哪些命令。
EXPOSE
EXPOSE <port> [<port>...]
这个会告诉Docker,容器在运行期间会监听指定的端口,
ENTRYPOINT
表示每次在镜像初始化的时候需要执行的命令,一般只设置一个,要是有多个命令的话,彼此之间需要用&&来隔离开。
关于docker 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
比如上面这个命令就是把主机中的 /src/webapp目录挂载到容器的/opt/webapp/目录下,注意前面的是主机中的目录,后面的是容器中的目录,若是容器中没有对应的目录的话,就会新建一个出来。更具体的使用方式参考官方文档。
-name
这个参数可以指定由镜像所生成的container的名称。
--rm
Automatically remove the container when it exits
--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。