Docker
——前部分
Docker是什么:
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。
为什么用Docker:
高效生产活动的一种工具。
其优点:三大核心技术+1:chroot,namespace,cgroups(基于namespace) ,Overlayfs
- chroot : 用ftp理解-->ftp在客户局端使用时,来回切换路径一直是保持在/var/ftp中,即它是一个 UNIX 操作系统的系统调用,将一个进程及其子进程的根目录改变到文件系统中的一个新位置,让这些进程只能访问到这个新的位置,从而达到了进程隔离的目的。
- namesapce : LXC是利用内核namespace技术,进行进程隔离。其中pid, net, ipc, mnt, uts 等namespace将container的进程, 网络, 消息, 文件系统和hostname 隔离开。
- cgroups : 加入到 Linux 内核中,这是划时代的,为后期容器的资源配额提供了技术保障。
- Overlayfs: Overlayfs是一种类似aufs的一种堆叠文件系统,于2014年正式合入Linux-3.18主线内核,目前其功能已经基本稳定(虽然还存在一些特性尚未实现)且被逐渐推广,特别在容器技术中更是势头难挡。
它依赖并建立在其它的文件系统之上(例如ext4fs和xfs等等),并不直接参与磁盘空间结构的划分,仅仅将原来底层文件系统中不同的目录进行"合并",然后向用户呈现。因此对于用户来说,它所见到的overlay文件系统根目录下的内容就来自挂载时所指定的不同目录的"合集"。
实质:
vm与docker四大不同(docker实质是一个进程,vm是一个系统。):
- docker基于宿主机的内核,vm基于自己独立的内核
- docker是对进程的隔离,vm是对资源的隔离
- docker相比与vm会更省资源,即更小
- docker容器启动是秒级,VM启动一般是分钟级
优势:
- 交付物标准化
- 一次构建,多次交付
- 应用隔离
基本概念:
- 镜像 images
- 容器 registeries
- 仓库 containers
前部分命令:
pull daocloud.io/library/centos:7 #拉取镜像
search centos -f stars=100 #查找star大于等于100的centos镜像
image ls #查看所有镜像
info #查看docker运行状态
images #查看所有镜像
image -q #查看镜像的id
image list #查看镜像
container ls #查看镜像
ps -a #产看容器
ps -aq #查看容器的id
ps -aqf status=exited #查看status为exited的容器
ps -l #产看最近一次的容器信息
history centos:6 #查看制作过程
inspect 容器id/名 #容器详情
image inspect 镜像id/名 #镜像详情
rmi 镜像id/名 #删除镜像
rmi $(docker image -q) #删除所有镜像
rm 容器id/名 #删除容器
rm 容器id/名 --force #强制删除
create -it daocloud.io/library/centos:5 /bin/bash #创建新容器但不启动
run -it daocloud.io/library/centos:7 /bin/bash #启动centos7的镜像
-i #捕获标准输入输出
-t #分配一个终端或控制台
--restart=always #跟随docker的启动而启动容器
--rm #推出容器后数据不保存(不能与-d一起使用)
-d #daemon后台运行
--name #指定容器的名字(names)
Ctrl+p+q #将容器放入后台运行
exec -it 容器id/名 /bin/bash #进入后台运行的容器
docker attach #容器id,前提是该容器是定义的交互的。
start 容器id/名 #启动容器
stop 容器id/名 #关闭容器
restart 容器id/名 #重启容器
kill 容器id/名 #强制终止容器
kill $(docker ps -q) #终止所有容器
pause #暂停容器进程
unpause #恢复容器所有暂停的进程
vim /usr/bin/de
添加 docker exec -it $1 /bin/bash
使用:de 容器id/names
dive docker_image_id/docker_image_name 分析docker镜像的详情
Docker Inspect语法:
docker inspect image_id:tags
$ docker inspect --help
Usage: docker inspect [OPTIONS] NAME|ID [NAME|ID...]
Return low-level information on Docker objects
Options:
-f, --format string Format the output using the given Go template
--help Print usage
-s, --size Display total file sizes if the type is container
--type string Return JSON for specified type
通过docker inspect image_id:tags看到的信息,都可以通过
docker inspect --format='{{ .key}}' 的方式获得,类似于Python的字典似的
如果想取到多层级的
docker inspect --format=‘{{}}’
获取容器的IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
获取容器Name
docker inspect --format='{{.Name}}' $(docker ps -aq)
获取容器Hostname
docker inspect --format '{{ .Config.Hostname }}' `docker ps -q`或docker inspect --format '{{ .Config.Hostname }}' $(docker ps -q)
获取Hostname Name IP
docker inspect --format 'Hostname:{{ .Config.Hostname }} Name:{{.Name}} IP:{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
对应个格式为 一级属性{{.属性}} 二级属性 {{.属性.属性}} 三级属性 {{.属性.属性.属性}}
可以写脚本获取容器Hostname、IP和Name
获取service实例的Ip
docker service ps pbspro-maya-workerdocker inspect ` docker service ps pbspro-maya-worker -q` --format '{{range .NetworksAttachments}}{{.Addresses}}{{end}}'
docker inspect ` docker service ps pbspro-maya-worker -q` --format '{{range .NetworksAttachments}}{{.Addresses}}{{end}}' | cut -d '[' -f2|cut -d ']' -f110.0.9.5/2410.0.9.6/2410.0.9.7/2410.0.9.8/2410.0.9.9/24
获取服务示例的所有Ip
[root@manager ansible]# docker inspect ` docker service ps pbspro-maya-worker -q` --format '{{range .NetworksAttachments}}{{.Addresses}}{{end}}' | cut -d '[' -f2|cut -d '/' -f1 10.0.9.510.0.9.610.0.9.710.0.9.810.0.9.9
获取container ID
docker inspect ` docker service ps pbspro-maya-worker -q` --format '{{ .Status.ContainerStatus.ContainerID }}'
获取容器PID
docker inspect -f '{{with .State}} {{.Pid}} {{end}}'
+++++++++++
很多 Docker 用户都知道 docker inspect 命令,该命令用于获取容器/镜像的元数据,其中 -f 参数可以用于获取指定的数据,例如使用 docker inspect -f {{.IPAddress}} 来获取容器的 IP 地址。不过很多用户容易被该特性的语法搞晕,并很少有人能将它的优势发挥出来(大部分人都是通过 grep 来获取指定数据,虽然有效但比较零散混乱)。本文将详细介绍 -f 参数,并给出一些例子来说明如何使用它。
docker inspect
简单地说,-f 的实参是个 Go 模版,并在容器/镜像的元数据上以该 Go 模版作为输入,最终返回模版指定的数据。第一个问题就是该命令说明文档中的用词“Go 模版”对于没有 Go 开发经验的人来说很模糊——我的第一感觉就是它类似恐怖的 C++ 模版。还好 Go 模版和 C++ 模版/泛型毫不相干,Go 模版是一种模板引擎,让数据以指定的模式输出。这个概念对于 Web 开发者是非常熟悉的,Web 领域有很多模版引擎,比如 Jinga2(用于 Python 和 Flask)、Mustache、JSP 等等,看下面的简单示例:
$ docker inspect -f 'Hello from container {{.Name}}' jenkins
Hello from container /jenkins
我们可以看到,-f 指定了一个简单的模式(或称之为模版)并应用于容器的元数据。当然,对于元数据,我们也可以什么都不指定:
$ docker inspect -f "This is a bit pointless" jenkins
This is a bit pointless
下面让我们来进一步看看 Go 模版的奇妙之处,例如我们可以通过模版来查找所有退出码为非 0 的容器名:
$ docker inspect -f '{{if ne 0.0 .State.ExitCode }}{{.Name}} {{.State.ExitCode}}{{ end }}' $(docker ps -aq)
/tender_colden 1
/clever_mcclintock 126
/grave_bartik 1
(无论是否匹配到,对于每个容器都会输出一行)
或者在 jenkins-data 容器中查找卷 /var/jenkins_home 对应在 host 的目录:
docker inspect -f '{{index .Volumes "/var/jenkins_home"}}' jenkins-data
/var/lib/docker/vfs/dir/5a6f7b306b96af38723fc4d31def1cc515a0d75c785f3462482f60b730533b1a
上面的例子可能稍微有点难理解,不过没关系,我们先来了解一下 Go 模版的基本用法。
模版指令
{{ }} 语法用于处理模版指令,大括号外的任何字符都将直接输出。
上下文
“.” 表示“当前上下文”。大多数情况下表示了容器元数据的整个数据结构,但在某些情况下可以重新规定上下文,比如使用 with 函数:
$ docker inspect -f '{{.State.Pid}}' jenkins
6331
$ docker inspect -f '{{with .State}} {{.Pid}} {{end}}' jenkins
6331
可以使用 $ 来获取根上下文,例如:
$ docker inspect -f '{{with .State}} {{$.Name}} has pid {{.Pid}} {{end}}' jenkins
/jenkins has pid 6331
注意,单独使用 “.” 本身也是可以的,将输出未格式化的完整元数据:
$ docker inspect -f '{{.}}' jenkins
...
数据类型
inspect 数据可以由浮点数、字符串和布尔组成,可以使用 Go 模版内置函数进行比较判断。虽然 Go 模版支持整数,但目前 inspect 数据中的数值类型都是浮点数,而整数应该对于大多数场景更方便(详见该Issue)。使用字符串时可以使用双引号。
数据中不存在的值是不可以用来比较的:
$ docker inspect -f '{{.ExecIDs}}' jenkins
<no value>
$ docker inspect -f '{{eq .ExecIDs .ExecIDs}}' jenkins
FATA[0000] template: :1:2: executing "" at <eq .ExecIDs .ExecIDs>: error calling eq: invalid type for comparison
数据结构
inspect 数据使用 map 以及数组保存。Map 结构非常简单,前面我们曾经展示过,可以通过 . 的链式来访问 map 内部数据:
$ docker inspect -f '{{.State.ExitCode}}' jenkins
0
不过有些情况(比如 map 的键不是字符串)是不能直接使用 . 方式来获取 map 值的,此时我们可以使用index 函数,前面卷的例子可以这样写:
docker inspect -f '{{index .Volumes "/var/jenkins_home"}}' jenkins-data
/var/lib/docker/vfs/dir/5a6f7b306b96af38723fc4d31def1cc515a0d75c785f3462482f60b730533b1a
我们也可以使用 index 来获取指定下标的数组值:
$ docker inspect -f '{{.HostConfig.Binds}}' jenkins
[/var/run/docker.sock:/var/run/docker.sock /usr/bin/docker:/usr/bin/docker]
$ docker inspect -f '{{index .HostConfig.Binds 1}}' jenkins
/usr/bin/docker:/usr/bin/docker
函数
除了 index 函数,其他很多函数也很常用。比如逻辑函数 and、or 可以返回布尔结果。注意,函数是不能放在中间:
$ docker inspect -f '{{and true true}}' jenkins
true
而不是:
$ docker inspect -f '{{true and true}}' jenkins
FATA[0000] template: :1:2: executing "" at <true>: can't give argument to non-function true
下面是一些常用的比较函数:
eq (等于)
ne (不等于)
lt (小于)
le (小于等于)
gt (大于)
ge (大于等于)
我们可以用这些函数来比较字符串、浮点数或整数:
$ docker inspect -f '{{eq "abc" "abc"}}' jenkins
true
$ docker inspect -f '{{ge 1 -1}}' jenkins
true
$ docker inspect -f '{{lt 4.5 4.6}}' jenkins
true
$ docker inspect -f '{{ne 4.5 4.5}}' jenkins
false
要注意的是操作数类型必须匹配,数字比较时使用浮点数:
$ docker inspect -f '{{eq "4.5" 4.5}}' jenkins
FATA[0000] template: :1:2: executing "" at <eq "4.5" 4.5>: error calling eq: incompatible types for comparison
$ docker inspect -f '{{gt .State.Pid 1}}' jenkins
FATA[0000] template: :1:2: executing "" at <gt .State.Pid 1>: error calling gt: incompatible types for comparison
$ docker inspect -f '{{gt .State.Pid 1.0}}' jenkins
true
另外,可以使用 json 函数来生成 JSON 输出:
$ docker inspect -f '{{json .NetworkSettings.Ports}}' jenkins
{"50000/tcp":null,"8080/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"}]}
我们也可以使用 jq 工具来组合结果:
$ docker inspect -f '{{json .State}}' jenkins-data | jq '.StartedAt'
"2015-03-15T20:26:30.526796706Z"
当然,docker inspect 的默认输出结果就是 JSON,所以下面这样也可以:
$ docker inspect jenkins-data | jq '.[] | .State.StartedAt'
"2015-03-15T20:26:30.526796706Z"
更多函数请参考 Go 官方文档,不过很奇怪的是官方文档中并没有描述 json 函数(我是从 Nathan LeClaire’s blog 中学到的),你如果知道其中原因,记得告诉我!
If 语句
条件语句 if 可以和前面的比较函数一起使用:
$ docker inspect -f '{{if eq .State.ExitCode 0.0}}
Normal Exit
{{else if eq .State.ExitCode 1.0}}
Not a Normal Exit
{{else}}
Still Not a Normal Exit
{{end}}' jenkins
Normal Exit
注意,{{end}} 语句必须有,else if 和 else 按需使用。
获取docker连接数的方法,或查看docker的连接
由于使用DOCKER的时候,ESTABLISHED连接不会出现在netstat中:
在运行中的docker容器中列出打开的套接字的方法
方法:
1. 查找docker的进程号 :
docker inspect -f '{{.State.Pid}}' <containerid>
$ docker inspect -f '{{.State.Pid}}' 49b98b2fbad2
1840
2. 查看连接:
sudo nsenter -t <pid> -n netstat | grep ESTABLISHED
$ nsenter -t 1840 -n netstat |grep ESTABLISHED
udp 0 0 node-2:45963 10.254.0.2:domain ESTABLISHED