docker 命令
Docker的坑
1. centos中显示中文乱码
echo $LANG
发现没有这个环境变量!
设置环境变量 ENV LANG=en_US.UTF-8 后正常
2.
一、在centos上安装Docker
安装
# 卸载旧版本 Docker
sudo yum remove -y docker docker-common docker-selinux docker-engine
# 安装必要的package
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加 repo
sudo yum-config-manager --add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 安装
sudo yum install docker-ce-17.06.2.ce
当然也可以自己下载 rpm 包 然后安装
curl -LJO https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.06.2.ce-1.el7.centos.x86_64.rpm
yum install ./docker-ce-17.06.2.ce-1.el7.centos.x86_64.rpm
如果提示audit-libs有多个版本
把多余的包remove掉
sudo yum remove -y audit-libs-2.4.1-5.el7.i686
升级
docker run --rm -v /var/lib/docker:/var/lib/docker docker/v1.10-migrator
# 如果你使用的是 devicemapper storage driver, 还需要加上 --privileged 参数
docker run --rm --privileged -v /var/lib/docker:/var/lib/docker docker/v1.10-migrator
启动&验证:
为了方便调试, 你可以使用 dockerd 命令来启动 docker 服务
dockerd
dockerd -h
# 启动 dockerd 服务时可以有很多配置选项, 可以使用 dockerd -h 查看,
# 例如 dockerd -D 表示开启 debugging
然后按 Ctrl+C 退出
一般使用 systemctl 来运行 Docker
/etc/docker/daemon.json 文件
这一版本的docker服务在启动时会读取 /etc/docker/daemon.json 文件, 推荐将dockerd启动参数放在其中
/etc/docker/daemon.json 文件是 json 格式的, 每个 key 表示配置选项, 值表示 配置值
注意: 不能同时在 daemon.json 中和命令行指定相同的选项,即使 选项具有相同的值, 否则 dockerd 会报错
因为使用 systemd 启动 dockerd 时已经设置了 -H , 所以你不能在 daemon.json 中设置 listening addresses.
如何开启调试 ?
dockerd -D
或者
在 daemon.json 中指定 "debug":true ,
同时确保 "log-level":"info|debug" , info是默认值.
开启调试对 Troubleshoot 固然重要, 但是即使daemon完全 non-responsive , 你仍然可以 kill -SIGUSR1 $(pidof dockerd) 来 force a full stack trace of all threads 到 daemon log 中.
如何查看 daemon log ?
vi /var/log/messages
journalctl -u docker.service
设置 http_proxy
mkdir /etc/systemd/system/docker.service.d
cd /etc/systemd/system/docker.service.d
vi http-proxy.conf
编辑该文件内容如下:
[Service]
Environment="HTTP_PROXY=http://10.0.209.105:17012/" "HTTPS_PROXY=http://10.0.209.105:17012/" "NO_PROXY=localhost,127.0.0.1"
然后
sudo systemctl daemon-reload
systemctl show --property=Environment docker
sudo systemctl restart docker
设置 insecure-registry
vi /etc/docker/daemon.json
{
"storage-driver": "devicemapper",
"insecure-registries":["10.0.209.140:5000","10.0.209.140:5000"]
}
设置 存储driver
vi /etc/docker/daemon.json
{
"storage-driver": "devicemapper",
"insecure-registries":["10.0.209.140:5000","10.0.209.140:5000"]
}
3. 创建一个unix用户组docker:
docker daemon绑定了一个Unix socket,而不是一个TCP端口。
默认情况下,这个Unix Socket被root用户拥有,其他用户通过sudo来访问它。
因此,docker daemon总是以root用户身份运行。
要避免在使用docker命令时加上sudo,你可以创建一个unix group,命名为docker,并将用户添加到这个组中,当docker daemon启动时,它会把所有权交给docker用户组。
groupadd docker
usermod -aG docker your_username
然后退出重新登陆以便让权限生效
4. 卸载docker
与Docker有关的本地资源都存放在/var/lib/docker/目录下,
其中container目录存放容器信息、graph目录存放镜像信息、aufs目录存放具体的镜像层文件
如果想更换该位置,可以使用软链接的方式
sudo yum remove -y docker docker-common docker-selinux docker-engine
#删除所有的images、containers、volumes,可选
rm -Rf /var/lib/docker
xxx #删除用户创建的配置文件。
5. 使用私服(local registry mirror):
运行一个local registry mirror
例如:
docker pull registry:2.1.1
docker run -d -v /docker_registry:/var/lib/registry -p 5000:5000 --restart=always --name registry registry:2.1.1
docker tag hello-world 127.0.0.1:5000/hello-world
docker push 127.0.0.1:5000/hello-world
如果使用 -p 5000:5000 不行, 请尝试使用 --net host 模式
如何检索私服上的镜像?
其实这个问题涉及到docker的api的问题,和私服并没有太大的关系
假设你的私服地址为 127.0.0.1:5000, 那么相关的api就两个
列出所有的repositories
可能的结果是 {"repositories":["centos","mymesos-master","mytomcat"]}
curl --noproxy 127.0.0.1 -X GET http://127.0.0.1:5000/v2/_catalog
列出特定repository的所有tags
可能的结果是 {"name":"centos","tags":["my"]}
curl --noproxy 127.0.0.1 -X GET http://127.0.0.1:5000/v2/centos/tags/list
如何删除私服上的镜像?
可以使用 https://github.com/burnettk/delete-docker-registry-image 这个工具。
#检索registry容器Mounts内容,发现/var/lib/registry目录挂载到了/docker_registry目录下
docker inspect ${registry_containerID}
export REGISTRY_DATA_DIR=/docker_registry/docker/registry/v2
#不实际删除,只是告诉你该操作将会删除哪些文件
delete_docker_registry_image --image testrepo/awesomeimage --dry-run
#实际删除某个大的repository的镜像
delete_docker_registry_image --image testrepo/awesomeimage
#实际删除某个tag的镜像
delete_docker_registry_image --image testrepo/awesomeimage:supertag
二、使用docker
系统操作
docker verison
docker info
docker events #显示server的实时的events,多用于调试
journalctl -u docker #systemd的日志系统
docker --help #显示可用的subcommand
docker attach --help #显示具体的subcommand命令帮助
镜像操作
docker images #列出本地的images
docker rmi -f ${imageId}|${imageName} #rm image
docker rmi `docker images -qf dangling=true` #批量删除临时(无标签的)镜像文件
docker save|load # 保存和导入镜像
docker build -t docker-whale . # .表示dockerfile所在的位置,-t表示打标打tag
docker commit -m "added json gem" -a "Kate Smith" ${conainerID_Name} ouruser/sinatra:v2
# -m 允许我们指定一个commit message;
# -a允许我们指定作者
# 以上为commit一个container和build一个dockerfile的方式创建image
docker tag ${imageID} ouruser/sinatra:devel #给image打tag
docker pull centos #如果你想pre-load一个image,你可以使用 docker pull命令。
docker pull dl.dockerpull.com:5000/ubuntu:12.04
docker inspect ${imageID} #获取镜像的详细信息
docker run 参数
-h=HOSTNAME #设定容器主机名
--link=CONTAINER_NAME:ALIAS #添加到另一个容器的连接
--net=bridge|none|container:NAME|host #设定容器的桥接模式
可以在container中直接修改/etc/hosts、/etc/hostname、/etc/resolve.conf,
但是这些修改都是临时的,容器终止或重启后修改会丢失,也不能被docker commit提交。
想要自定义只能在docker run/create的时候使用上面的参数来指定。
资源限制 cgroups 相关命令
block IO:
--blkio-weight value : 10-1000
--blkio-weight-device value : Block IO weight (relative device weight)
CPU :
--cpu-period int : Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int : Limit CPU CFS (Completely Fair Scheduler) quota
--cpu-shares int : CPU shares (relative weight)
--cpuset-cpus string : CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string : MEMs in which to allow execution (0-3, 0,1)
Memory:
--kernel-memory string : Kernel memory limit
--memory string : Memory limit
--memory-reservation string : Memory soft limit
--memory-swap string : Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--memory-swappiness int : Tune container memory swappiness (0 to 100) (default -1)
Capability:
容器默认拥有的能力有CHOWN、DAC_OVERRIDE(允许忽略文件的读、写、执行访问权限检查)
--cap-add=[]
--cap-drop=[]
查看容器进程的能力,需要安装 yum install libcap-ng-utils
pscap | grep [container_PID]
3. 容器及其中应用的生命周期操作
docker ps #列出列出运行中的containers
docker ps -a #显示所有的container
docker logs ${conainerID_Name} #查看container内部的输出
docker logs -f ${conainerID_Name} #执行类似tail -f的命令监视container的输出
docker stop ${conainerID_Name} #关闭container
docker kill ${conainerID_Name} # stop和kill的区别:stop是给容器内PID为1的进程发送SIGTERM,如果进程超时未退出,则继续发送SIGKILL强制杀掉。而kill是直接发送SIGKILL。
docker start|restart ${conainerID_Name}
docker rm ${conainerID_Name} # -f 表示强制删除 -l 表示删除容器的连接而不是容器 -v表示同时删除容器挂载的数据卷
docker rm -f `docker ps -aq` #删除所有容器
docker attach ${conainerID_Name} #连接到正在运行的container,如何退出容器而不停止容器?组合键:Ctrl+P+Q
docker exec -it ${conainerID_Name} bash #打开一个shell到你的container
docker rename ${conainerID_Name} #重命名一个container
docker stats ${conainerID_Name} #显示容器实时的资源消耗信息
docker update ${conainerID_Name} # Update configuration of one or more containers
docker exec ${conainerID_Name} #在运行中的容器内运行命令
docker cp # 在容器和local文件系统之间拷贝文件/文件夹
docker diff ${conainerID_Name} #检查容器内发生变化的文件
docker port ${conainerID_Name} 5000 #使用docker ps命令来返回映射的端口略显笨拙,所以可以使用docker port,这会直接显示匹配的端口。
docker top ${conainerID_Name} #查看container内部的进程
docker stats ${conainerID_Name} # 还有配套的API( GET /container/(id)/stats )供开发人员调用
docker inspect ${conainerID_Name} #更底层的方式进入到container内部,返回的是JSON字符串
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${conainerID_Name} #使用Go 模版,具体用法自己搜去吧,灵活使用,可以把获取pid、ip的指令存成一个shell脚本。有 range、json、join(多个值连接在一起)、split(将字符串按分隔符分开)、lower、upper、title(首字母大写)
docker inspect --format='{{json .NetworkSettings.Networks}}' ${conainerID_Name}
docker inspect --format='{{json .State}}' ${conainerID_Name} #一个容器在某个时刻可能处于以下几种状态之一:created、running、paused、restarting、exited(也就是stopped状态)、destroyed
docker export ${conainerID_Name} -o ./someFile #将一个容器的文件系统打包为一个压缩文件
docker import someFile someImageName -m "message" #从一个压缩文件创建一个镜像,这就实现了容器的迁移方法,当然要实现容器迁移也可以利用镜像,使用docker save和docker load进行迁移。
4. volume操作
docker run -d -P --name web -v /webapp training/webapp python app.py
#这将会为container内部的/webapp目录创建一个新的volume,也就是将宿主机的某个目录mount到/webapp
docker inspect web
从Mounts部分可以看到,Docker将本地一个_data目录mount到容器内的webapp目录了,
其实,在web容器被删除后,这个_data目录还是会保留下来,只是无法再使用这个目录而已
docker run -d -P --name web -v /src/webapp:/webapp training/webapp python app.py
docker run -d -P --name web -v /src/webapp:/webapp:ro training/webapp python app.py
docker inspect web
你可以看到Mounts中的内容,Source为本地的路径,Destination为container中的路径,RW为读写权限。
通过-v还可以将一个文件挂载到另一个文件,估计是使用了 mount --bind 的机制,mount --bind就是可以挂载文件,
例如
mount --bind filea fileb
就是把filea挂载到fileb,也就是说fileb会被掩埋,filea和fileb其实都是filea。
docker volume create --name vol1 #使用默认的 'local' driver 创建一个 volume
docker volume inspect vol1
docker volume ls -qf dangling=true # -q为quiet -f为过滤 dangling=true的
docker volumn rm $(docker volume ls -qf dangling=true) #删除孤儿卷
docker volume create -d flocker -o size=20GB my-named-volume #Flocker分布式卷解决方案,它的好处是数据会被写入Flocker后端存储而不是主机上,因此可以保证数据不依赖于主机而丢失。在容器迁移时,Flocker会自动地将卷从一个host移植到另一个host
docker run -d -P --volume-driver=flocker -v my-named-volume:/webapp --name web training/webapp python app.py # 使用volume
5. 网络操作
docker network ls #显示所有的network,默认使用的network是bridge
docker network inspect bridge #在输出中的Containers键中可以很容易地找出container的IP地址
docker network disconnect bridge ${container} #将container从网络中移除
s#-d表示新的driver使用bridge driver,你可以省略这个参数,因为bridge就是默认的driver。
docker network inspect my-bridge-network #你会发现没什么内容,containers和options都是空的。
docker run -d --network=my-bridge-network --name db training/postgres #再启动一个container name为 db,指定加入哪一个network
docker network inspect my-bridge-network #如果你检查你的my-bridge-network,你会看到它已经有一个container附属于它了。
docker inspect --format='{{json .NetworkSettings.Networks}}' db #你还可以检查你的container来看看container连接到哪里了:
docker network connect my-bridge-network networktest #docker networking允许你将一个container(即使是运行中的container)附加于任意多的network上。
5.1 基于VXLAN的overlay network
注意Linux内核不得低于3.16 !
需要启动一个Consul
设置docker daemon的启动选项
DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4 -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-advertise eth0:2375 --cluster-store consul://10.10.126.101:8500/network --storage-driver=devicemapper"
docker network create -d overlay net1 #创建一个使用overlay driver的网络net1,然后可以看到在其他的节点也会出现这个network
docker network ls # 此时iptables规则也并无变化
docker run -itd --name cc1 --network net1 centos bash #节点1启动
docker run -itd --name cc2 --network net1 centos bash #节点2启动
ping cc2 ; #在cc1中ping cc2,可以ping通
原理:
docker network create -d overlay net1 来创建使用overlay driver的网络 net1 时,这个命令会创建一个新的network namespace。
docker默认的net ns却放在 /var/run/docker/netns下。
将其中的内容ln -s到 /var/run/netns 后,就可以使用ip netns来查看这些 namespace 了。
具体就是这个namespace创建了一个bridge,container中的eth通过veth pair连接到这个bridge,同时这个bridge还加了一个VxLAN设备,从而侦测远端的mac地址。
5.2 swarm操作:
Docker的Overlay网络功能与其Swarm集群是紧密整合的,因此为了使用Docker的内置跨节点通信功能,最简单的方式就是采纳Swarm作为集群的解决方案。
docker swarm init --advertise-addr <MANAGER-IP> #初始化一个集群,然后加入该节点
docker swarm join-token manager|worker #显示加入swarm集群的命令或token
docker swam join --token SWMTKN-1-49n... IP:port
docker node ls #查看worker nodes
docker service create --replicas 1 --name helloworld alpine ping 10.0.209.56 #创建服务
docker service ls #查看运行中的服务
docker service inspect --pretty <SERVICE-ID> #显示service的细节,--pretty是为了更好的查看输出,不然就是json格式的输出。
docker service ps <SERVICE-ID> #查看哪个节点在运行服务,然后可以在单击上查看这个container
docker service scale <SERVICE-ID>=5 #更改service的scale
docker service ps <SERVICE-ID> #再次查看哪些节点正在运行服务,这个列表应该已经发生变化
docker service rm helloworld #虽然service被删除了,但是task container还会存在一会儿才会消失。
docker service create --replicas 3 --name redis --update-delay 10s --update-parallelism 1 redis:3.0.6 # --update-delay可以使用10m30s的格式来写,--update-parallelism为同时更新的task数量,默认为1,--update-failure-action的默认值为paused。 这些信息都可以使用 docker service inspect --pretty redis 来查看
docker service update --image redis:3.0.7 redis # update container image
docker service update # 重启 paused 的upate,但是为了防止重复出现错误,你可能应该提供update的参数
docker service ps redis # 查看task状态,注意看IMAGE列
docker node ls #查看节点状态
docker node update --availability drain|active worker1 #将节点的状态置为drain,那么manager就不再为该节点分配任务,并且会把当前节点执行的任务移走。 pause状态表示manager不会为节点分配新任务,但是不会把当前节点正在执行的任务移走。
docker node inspect --pretty swarm-node-2 #查看节点的信息
docker service create --name my-web --publish 8080:80 --replicas 2 nginx #发布端口,访问任意一个节点的8080都可以,默认协议是tcp,要想指定使用tcp还是udp,需要使用 -p 8080:80/udp,同时指定两个协议,需要使用两次-p
docker service update --publish-add 8080:80 nginx #更新服务,增加服务的发布端口
#如果service当前没有节点可以运行,那么service的状态就是pending。当然如果service的replicas数量为0,例如你不想让service被deployed,那么service的状态就会一直是pending。
#服务有两种类型的部署:replicated和global,global是全局的,每个节点上都运行的task,新加入的节点也会被自动运行这样的task,比较好的场景是监控agent。
docker node promote|demote node-2 node3 #将worker node提升为manager node
docker swarm leave #节点离开swarm
docker node rm node-2 #删除节点 无论是leave还是rm,必要的时候都需要使用--force强制执行
docker service create --name myservice --env MYVAR=myvalue --workdir /tmp --user my_user --mode global centos top #配置服务
docker service create ... --reserve-memory or --reserve-cpu #保留内存和CPU
docker network create --driver overlay --subnet 10.10.10.0/24 my-network #创建一个scope为Swarm的overlay网络
docker service create ... --network my-network nginx #使用网络
docker service create ... --mount src=<VOLUME-NAME>,dst=<CONTAINER-PATH> ... <IMAGE> #使用已存在的volume
docker service create ... --mount type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=<DRIVER>,volume-opt=<KEY0>=<VALUE0>,volume-opt=<KEY1>=<VALUE1> ... #在deploy时创建一个volume
docker service create ... --mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH> #bind的方式有时虽然很有用,但是也有危险,每个节点上都必须存在该路径,可以使用调度策略--constraints 来限制这个必要条件。 但是Host bind是完全不可移植的,没有任何保证你在开发环境和生产环境中应用的运行会相同。
5.3 pipework操作
5.4 跨主机通信的几种方法(pipework):
(1)桥接:宿主机必须在同一个二层网络中,一般需要宿主机有额外一块网卡,专门桥接到docker0中用于container通信,然后每台宿主机都需要通过 --bip=xxxx/16 限定docker0的IP地址和网络段,即每台宿主机的docker0必须是属于同一个二层网络,且IP地址不能冲突,然后通过 --fixed-cidr=xxxx/24,限定分配给container的地址段,避免网址冲突。
(2)直接路由:同样宿主机必须在同一个二层网络中,不需要额外的网卡,每台宿主机都需要通过 --bip=xxxx/24 限定docker0的IP地址和网络段,即每台宿主机的docker0必须是不属于同一个二层网络,因为这样才可以进行路由嘛,然后各自在主机上添加路由和配置iptables:
route add -net 172.17.2.0 netmask 255.255.255.0 gw 10.10.109.92
iptables -t nat -F POSTROUTING
# 因为启动docker daemon会自动创建一条规则
# -A POSTROUTING -s 172.17.0.0/16 -o !docker0 -j MASQUERADE
# 因为我们不想让容器间通信时“看不见对方”,所以先删除默认的POSTROUTING规则,
# 然后添加我们新的POSTROUTING
iptables -t nat -A POSTROUTING -s 172.17.1.0/24 -d !172.17.0.0/16 -j MASQUERADE
(3)OVS划分VLAN:主要命令 ovs-vsctl,也需要宿主机必须在同一个二层网络。
(4)OVS隧道模式:上述的组网模式都要求宿主机必须在同一个二层网络中,而跨数据中心的二层通信,目前比较普遍的解决方法是使用Overlay的虚拟化网络技术,Overlay网络其实就是隧道技术,即将一种网络协议包装在另一种协议中传输的技术。当前主要的Overlay技术有VXLAN(Virtual Extensible LAN)和NVGRE(Network Virtualization using Generic Routing Encapsulation)。VXLAN是将以太网报文封装在UDP传输层上的一种隧道转发模式,它采用24位比特标识二层网络分段,称为VNI,类似于VLAN ID的作用。NVGRE和VXLAN类似,它使用GRE的方法来打通二层与三层之间的同路,采用24比特的GRE key来作为网络标识(TNI)。 这里的例子是使用NVGRE,而我们要使Overlay,所以这里不做过多介绍,大概就是 使用ovs-vsctl创建一个ovs0网桥,并将其连在网桥docker0上,然后在ovs0上创建GRE隧道。如果要实现容器的跨网络通信,即容器的IP地址不在同一个二层网络中,而且宿主机的IP地址也不在同一个二层网络中,这就不能再使用直接路由的方式,需要依赖Open vSwitch建立的GRE隧道才行,也就是在上面的配置之后,再在ovs0网桥上创建一个internal类型的端口rou0:
ovs-vsctl add-port ovs rou0 -- set interface rou0 type=interval
ifconfig rou0 192.168.1.1/24
route add -net 172.17.0.0/16 dev rou0
# ... 然后创建gre隧道、删除docker创建的iptables规则再创建自己的规则,同上
6. 相关项目
6.1 Citadel
6.2 Shipyard
6.3 Kubernetes
6.4 DockerUI
6.5 Panamax
6.6 Seagull
6.7 docker-py :编程开发,对REST API进一步封装
6.8 Fig
7. 从开发到生产环境
如果Docker出现了不可控的风险,是否考虑了其他的解决方案
如何对Docker容器做资源限制(CPU、内存、网络、磁盘等等)
容器安全:使用第三方工具加强对容器的安全管理,如apparmor对容器的能力进行限制、使用更加严格的iptables规则、禁止root用户登录、限制普通用户以及做好系统日志的记录
docker daemon安全
使用unix域套接字而不是TCP、
使用TCP的同时设置--tlsverify(通过--tlscacert、--tlskey、--tlscert 3个参数来配置)
启用--selinux-enabled、
使用GRSecurity内核安全增强工具(主要防止内存破坏、预防0day漏洞等)、
镜像安全:
registry访问控制
镜像校验和、
内核安全:
公司内部私有仓库的管理,镜像的管理问题
dockerize:
解决应用依赖的配置信息
应用运行时候的输出日志信息
docker容器的监控:
主机维度:监控主机的CPU、内存、本地镜像情况、容器运行情况
镜像维度:镜像的基本信息、镜像与容器的对应关系、镜像构建的历史信息(层级的依赖信息)
容器维度:容器的基本信息、容器的运行状态、容器的用量信息
容器监控命令: docker ps、docker images、docker stats、docker inspect、docker top、docker port、
容器监控工具:cAdvisor、Datadog、Prometheus、
编排小神器Fig(被收购后更名为docker-compose):
为什么使用Fig?
因为Dockerfile重现一个容器,Fig重现容器的部署和集群
编排是什么?
Orchestration,定义的是被部署的对象的各组成部分之间的耦合关系,部署流程中各个动作的执行顺序,部署过程所需要的依赖文件和被部署文件的存储位置和获取方式,以及如何验证部署成功。这些信息都会在编排工具中以指定的格式(例如配置文件)来要求运维人员自主定义并保存起来,从而保证这个流程能够随时在全新的环境中有序地重现出来。
部署是什么?
Deployment,它是指按照编排锁指定的内容和流程,在目标机器上执行编排指定环境初始化,存放指定的依赖和文件,运行指定的部署动作,最终按照编排中的规则来确认部署成功。
使用Compose的基本3步:
定义你的app的Dockerfile文件,以便它可以被重复发布
在docker-compose.yml文件中定义服务
最后运行 docker-compose up ,compose会启动并运行你的整个app。
compose可以和swarm来结合使用
111. Dockerfile:
每一行都是 INSTRUCTION statement
FROM docker/whalesay:latest
RUN apt-get -y update && apt-get install -y fortunes # run指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令,结束后又提交容器为新镜像,新镜像被dockerfile的下一条指令使用
RUN ["/bin/bash","-c","echo hello"]
CMD /usr/games/fortune -a | cowsay # cmd指令提供容器运行时的默认值,这些默认值可以是一条指令,也可以是一些参数。dockerfile中可以有多条cmd指令,但只有最后一条有效。cmd指令在构建镜像时并不执行任何命令,而是在容器启动时默认将cmd指令作为第一条执行的命令。如果使用 docker run 命令时指定了命令参数,则会覆盖CMD指令中的命令。
ENTRYPOINT # 和cmd指令类似,都可以让容器在每次启动时执行相同的命令,同样也是最后一条ENTRYPOINT指令有效。但区别是,docker run 命令提供的运行命令参数不能覆盖ENTRYPOINT. CMD(或docker run命令中指定的命令) 和 ENTRYPOINT 都存在时,CMD 的指令作为 ENTRYPOINT 的参数。
CMD和ENTRYPOINT和RUN都有两种写法,
一种是直接写命令,
还有一种是["cmdOrParam1","cmdOrParam2"]这样的写法,
如果直接写命令就会被解释为["/bin/sh","-c","oriCmd"]
EXPOSE <port> [<port> ...]
ENV <key> <value>
ENV <key>=<value> ...
RUN <key>=<value> <command> #只在该命令行上有效的环境变量
WORKDIR /path #相当于cd
VOLUME ["/data"] #创建一个可以从本地主机或其他容器挂载的挂载点,需要1注意不能指定本地文件夹 2在Dockerfile中对/data进行修改的RUN命令会全部不生效,因为VOLUME指令是在容器运行时才去挂载,而RUN指令是在构造镜像的时候就会执行。
ONBUILD [INSTRUCTION] #当所创建的镜像作为其他新创建镜像的基础镜像时,所执行的操作指令
ADD/COPY <src> ... <dest> # 将文件或文件夹或远程URLs表示的文件拷贝到指定路径,使用COPY,不要使用ADD
HEALTHCHECK [OPTIONS] CMD command (check container health by running a command inside the container)
HEALTHCHECK NONE (disable any healthcheck inherited from the base image)
选项有:
--interval=DURATION (default: 30s)
--timeout=DURATION (default: 30s)
--retries=N (default: 3)
命令的exit状态表明健康状态,可能的值有0表示成功,1表示不健康,2还没有使用。 例子:
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
命令写到stdout或stderr的任何的输出文本(UTF-8编码)都会被存储到health status,并且可以被 docker inspect 检索到。这些输出只能保存4096 bytes。
当container的健康状态发送变化时,一个事件 health_status就被生成了。
九十九、基础知识
1. Linux namespace:
Linux 内核中实现了六种 namespace:
(1)Mount namespace 隔离文件系统挂接点,每个容器能看到不同的文件系统层次结构。
(2)UTS namespace 隔离nodename和domainname,每个容器可以有自己的 hostname 和 domainame。
(3)IPC namespace 隔离特定的进程间通信资源,包括System V IPC 和 POSIX message queues,每个容器有其自己的 System V IPC 和 POSIX 消息队列文件系统,因此,只有在同一个 IPC namespace 的进程之间才能互相通信。
(4)PID namespace 隔离进程ID,每个PID namespace中的进程可以有其独立的PID,每个容器可以有其PID为1的root进程;也使得容器可以在不同的host之间迁移,因为namespace中的进程ID和host无关了。这也使得容器中的每个进程有两个PID:容器中的PID和host上的PID。
(5)Network namespace 隔离网络相关的系统资源
(6)User namespace 隔离用户和组ID空间,每个container可以有不同的user和group id;一个host上的非特权用户可以成为user namespace中的特权用户。
查看/proc/[pid]/ns/目录下的文件,
有6个文件:mnt uts ipc pid net user ,
对应6种namespace,分别指向不同namespace号的文件,
如果有两个进程指向相同的namespace编号,就说明它们在同一个namespace下。
1.1 cgroup :
有了namespace进行资源隔离,还需要有controlGroups进行资源控制。它提供的功能有:
Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
Accounting: 一些审计或一些统计,主要目的是为了计费。
Control: 挂起进程,恢复执行进程。
相关命令
mount -t cgroup;
ls -l /sys/fs/cgroup
可以看到/sys/fs/cgroup目录中有若干个子目录,我们可以认为这些都是受 cgroups 控制的资源以及这些资源的信息。
默认情况下,Docker 启动一个容器后,会在 /sys/fs/cgroup 目录下的各个资源目录下生成以容器 ID 为名字的目录(group),比如:
ls /sys/fs/cgroup/cpu/docker/${containerID}
此时 cpu.cfs_quota_us 的内容为 -1,表示默认情况下并没有限制容器的 CPU 使用。
在容器被 stopped 后,该目录被删除。
还可以查看/proc/${pid}/cgroup文件查看进程属性哪个control groups。
control group会显示为一个相对路径,例如/意思就是“这个进程没有被指派到一个特定group中,/lxc/pumpkin意思是进程很可能是一个名为pumpkin的容器的进程”。
至于某个container的cgroup,可以查看/sys/fs/cgroup/memory/docker/<longid>/ 。
运行时的stat,可以查看cgroup下的文件,例如内存的 memory.stat 文件。
2. Docker引擎管理镜像,然后移交给containerd运行,containerd使用runC运行容器。
containerd是一个简单的守护进程,它可以使用 runC 管理容器,使用 gRPC 暴露容器的其他功能。
它管理容器的开始,停止,暂停和销毁。
由于容器运行时是孤立的引擎,引擎最终能够启动和升级而无需重新启动容器。
runC是一个轻量级的工具,它是用来运行容器的,只用来做这一件事,并且这一件事要做好。
runC基本上是一个小命令行工具且它可以不用通过Docker引擎,直接就可以使用容器。
因此,容器中的主应用在 host 上的父进程是 containerd-shim,是它通过工具 runC 来启动这些进程的。
这也能看出来,pid namespace 通过将 host 上 PID 映射为容器内的 PID, 使得容器内的进程看起来有个独立的 PID 空间。
3. docker网络
(1)bridge模式:默认的模式。使用一个 linux bridge,默认为 docker0,使用 veth 对,一头在容器的网络 namespace 中,一头在 docker0 上。
(2)Host模式:该模式下的 Docker 容器会和 host 宿主机共享同一个网络 namespace,故 Docker Container可以和宿主机一样,使用宿主机的eth0,实现和外界的通信。换言之,Docker Container的 IP 地址即为宿主机 eth0 的 IP 地址。 这种模式下的容器没有隔离的 network namespace。
(3)container模式: Container 网络模式是 Docker 中一种较为特别的网络的模式。处于这个模式下的 Docker 容器会共享其他容器的网络环境,因此,至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。
docker run -d --network container:${anotherContainer} training/webapp python app.py
(4)none模式:只能使用loopback网络设备,不会再有其他的网络资源。
3.1 多节点 Docker 网络
3.1.1 Docker 原生overlay 网络,基于VxLAN。
4. Docker存储
分为 分层文件系统和卷
4.1 AUFS 分层文件系统
先举个例子,假如有一个目录内容如下
.
-- fruits
-- apple
-- tomato
-- vegtables
-- carrots
-- tomato
现在
mkdir mnt
mount -t aufs -o dirs=./fruits:./vegtables none ./mnt
ls -R ./mnt
显示为
-- apple
-- carrots
-- tomato
AUFS是一种联合文件系统,它把若干目录按照顺序和权限mount为一个目录并呈现出来。
默认情况下,只有第一层(第一个目录)是可写的,其余层是只读的。
默认情况下,新增的文件都会被放在最上面的可写层中。
因为底下各层都是只读的,当需要删除这些层中的文件时,AUFS 使用 whiteout 机制,它的实现是通过在上层的可写的目录下建立对应的whiteout隐藏文件来实现的。
AUFS 利用其 CoW (copy-on-write)特性来修改只读层中的文件。AUFS 工作在文件层面,因此,只要有对只读层中的文件做修改,不管修改数据的量的多少,在第一次修改时,文件都会被拷贝到可写层然后再被修改。
AUFS 的 CoW 特性能够允许在多个容器之间共享分层,从而减少物理空间占用。
AUFS 的查找性能在层数非常多时会出现下降,层数越多,查找性能越低,因此,在制作 Docker 镜像时要注意层数不要太多。
AUFS 的 CoW 特性在写入大型文件时第一次会出现延迟。
4.2 Docker使用的AUFS文件系统
关于 Docker的分层镜像,除了 aufs,docker还支持btrfs, devicemapper和vfs,你可以使用 -s 或 –storage-driver 选项来指定相关的镜像存储。
在Ubuntu 14.04下,Docker 默认 Ubuntu的 AUFS。
因为 AUFS 还没有进入Linux 内核主干的原因,RedHat(包括centos) 上使用的是 devicemapper。
docker info #我们可以在该命令的输出中查看所使用的存储驱动
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




