Docker: 集装箱;打包环境,run any-where.
Copy-on-write原理:container最上层是容器层,可读可写,下面的都是只读的镜像层;对镜像层文件的修改,都是复制一份到容器层然后再改的,改完后commit就会留在最上层,即mask下层镜像的同名文件;
==========================
run容器
注意:run的参数必须在docker image名称前方,否则会被认为是run该脚本!
--name
为新run的container起一个名字。在多人共用的服务器上尤其重要,知道是自己创建的,提醒别人别删,提醒自己docker ps -a然后去删。
docker run --name "smartcat2010_20230520" -p 80 httpd
--rm
Run完结束自动删除container,避免很多Exited状态的container(用docker ps -a才能看到)
docker run --rm -p 80 httpd
-p
把docker里面的端口,映射到外面。即让外面可以访问docker里面的端口。
docker run -p 80 httpd (外面随便挑一个端口和里面80映射起来)
docker run -p 8010:80 httpd (把外面的8010和里面80映射起来)
docker ps,可查看里外端口映射关系。
-it
以交互模型run
docker run -it
Ctrl+d, 退出并使容器Exited;
Ctrl+p+q, 退出但不结束容器;
-d
后台运行(run的如果是服务,不用这个选项,就占住console了)
docker run -d -p 80 httpd
==========================
查看
help:
docker 命令 --help
ps
查看container
docker ps
docker ps -a (Exited的也能看到)
images
查看images
docker images
history
展示image每一层的信息
docker history --no-trunc image_name:tag
search
查看docker registry里有那些相关的image
例子:docker search cuda
==========================
改变container状态
attach VS exec -it
docker attach e459c03c58ee
docker exec -it e459c03c58ee bash
attach会直接进入容器启动命令的终端,不会启动新的进程;
exec -it是在容器中打开新的终端,并且可以启动新的进程;
logs
查看-d启动的服务类container的log
docker logs -f e459c03c58ee
stop VS kill
stop是给容器进程发SIGTERM信号;(更优雅)
kill是给容器进程发SIGKILL信号;(更粗暴)
两者都会使container状态变为Exited;(需要docker ps -a才能看到,记得用rm删之)
start
启动在Exited或Create状态的容器;
restart
restart = stop + start
服务类容器,可以这样让退出后能自动重启:docker run -d --restart=always httpd
pause/unpause
使用场景:给容器的文件系统打个快照;暂时不想让该容器使用cpu;
run = create + start
rm
删除container。(一般是Exited的才能被删)
用docker stop xxx先把docker停掉,才能删;
!!! 使用docker run --rm,可以在container结束时自动删;
批量删:docker rm -v $(docker ps -aq -f status=exited)
==========================
Images
docker pull太慢:
使用国内镜像下载加速(阿里云、daocloud.io等);
都要有root权限,修改/etc/default/docker,并重启docker: systemctl restart docker.service
commit
把container保存为image
docker commit -m "注释消息" 3d4181d53bb3 image_name:tag
(推荐用Dockerfile方式;因为手工创建,下次还得这么重复劳动,且不知道安装了什么)
rmi
删除image
docker rmi image_name
同一个image有多个tag时,只有最后一个tag被删后,才删image;否则只是显示"Untagged: XXX"
==========================
镜像build
docker build -t repo_name:tag .
末尾的.是指build context目录;
docker会默认从build context目录查找"Dockerfile";(也可用-f指定)
docker会把build context目录里的所有文件和子目录,发给docker daemon,即使其中很多是不会COPY进image;所以,注意这个build context目录必须精简;
dockerfile的使用:
RUN
使用&&来分隔多个命令;目的是在一个container里多执行一些命令,减少最终image里新增加的层数;
COPY
COPY src_path dst_path
src_path可以是文件,也可以是目录;必须是在build context中的;
ADD
同COPY;唯一不同的是,如果src_path是tar、zip等,会自动解压后再COPY;
ENV
设置的环境变量,在build image和image里面,都会生效;
docker inspect可查看image的所有Env
ENV name1=value1 name2=value2 name3=value3
WORKDIR
设置容器内的工作目录(对后续RUN、CMD、ENTRYPOINT、COPY、ADD有效)
run起容器之后,当前工作目录也是这个;
ENTRYPOINT
run容器必执行其命令;
如果有CMD,则CMD会被当作参数,追加至其命令的后面;
run的image后面的东西,会被当作参数,追加至其命令的后面;
CMD
没有ENTRYPOINT,则被当作启动命令;有,则当作ENTRYPOINT的参数;
run的image后面如果没东西,则生效;有东西,则失效;
例如:docker run -it [image] /bin/bash 则CMD失效;
===========================
镜像发布
tag
发布前,设置版本号用;
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
规矩:myimage:v1始终指向1里面最新的镜像;myimage:v1.9始终指向1.9里面最新的镜像;myimage:latest始终指向所有版本里面最新的镜像;
docker login -u my_user_name
docker tag httpd my_user_name/httpd:v1 (docker hub为了区分不同用户的镜像,规定镜像名格式必须为:user_name/image_name:tag)
docker push my_user_name/httpd:v1
===========================
资源限制
内存: -m (或--memory); --memory-swap
CPU: -c (或--cpu-shares)
磁盘读写:--blkio-weight;--device-read-bps; --device-write-bps; --device-read-iops; --device-write-iops
限制只在哪些GPU上跑:--gpus "device=0,2"
===========================
其他
调试:
用docker history拿到image的每一层信息;docker run -it 某一层,即可进入该层!
--no-cache
如果后面build的Dockerfile,前几层和之前build的,完全一样,则默认会使用cache;显示如下提示:Using cache;但是,如果前几层命令的顺序改一下,则不会复用cache了;
使用--no-cache,可disable之,从头build;
docker container top <container_name>
查看container内的所有进程;
docker container stats
实时查看memory、net、磁盘IO情况;
查看日志
docker logs -f <container_name>
日志文件位置:/var/lib/docker/containers/<container_ID>/<container_ID>-json.log
===========================
网络
平时run的container, 都是被放到docker默认的bridge(docker0)这个网络里;
同一个网络里的container,彼此可以ping通;不同网络里的,彼此不能ping通;
Linux这边看到的bridge名称、网卡名称,和docker里的bridge名称、网卡名称,是不同的;只能用IP来分辨是否是同一个;
docker network ls 看到3个自带网络:none、host、bridge;
docker run network=none,则会把container放入一个不联网的环境;
docker run network=host,则会把container放入和host完全相同的网络环境,即端口和host是共用一套的;
docker run,不带network,会默认把container放入bridge网络(Linux侧叫docker0);
Docker侧查看:
docker network ls (列出所有网络)
docker network inspect my_net (列出某网络的Subnet、Gateway、所有container、IP)
Linux侧查看:
brctl show (列出所有网络)
ifconfig docker0 (列出某网络的Gateway IP等信息)
创建network:
docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
在某network里创建container:
docker run -d --network=my_net2 httpd (自动分配IP)
docker run -d --network=my_net2 --ip 172.22.16.10 httpd (指定IP)
把已有container放入某network:
docker network connect my_net2 2b668e52480e (等于为该container新创建一个网卡,该网卡链接至my_net2上)
最常用的-p选项:
docker run -p <host端口号>:<container端口号> httpd
将host端口和container端口映射起来,访问host端口就等价于访问了container端口;
如果不指定host端口号,会被随机分配;可用docker ps的PORTS字段查看。
===========================
存储
docker cp
docker内外传输文件用;
docker cp ./data.txt docker_name:/usr/local/
docker cp docker_name:/usr/local/data.txt ./
-v (bind mount)
将host某路径,映射至container某路径;
docker run -v ~/htdocs:/usr/local/apache2/htdocs -p 80 -d httpd
指定只读权限: -v ~/htdocs:/usr/local/apache2/htdocs:ro
还可只mount一个文件
-v (docker managed volume)
不指定host路径,系统自动在host的/var/lib/docker/volumes/创建一个folder;
步骤:1. host的/var/lib/docker/volumes/下面自动创建一个目录;2. -v指定的container的路径里的数据,copy至host自动创建的路径下面;3. host下的自动目录mount至container目录;
docker run -v /usr/local/apache2/htdocs -p 80 -d httpd
在Dockerfile里指定VOLUME
原理和不带host路径的-v一样;docker run的时候不用指定-v了;(目的主要是怕run的时候忘了加-v;比如MySQL必须要把数据保存下来,不能忘了加-v就容器死后丢失数据)
VOLUME /usr/local/apache2/htdocs (container内的路径)
同一个host路径,可以-v给多个container;这样多个container共享同一份数据;
可专门create一个container(不start之),指定好-v;然后后续docker run指定--volumes-from <prev_container_name>,就可以复用那个container的-v;
docker volume ls
查看总共有多少个volume;
docker inspect <container_name>
里面"Mounts"字段,有-v信息;
docker volume rm
删除volume (在其container已消失后才可删)