docker常用命令操作方法

时间:2022-02-18 16:54:25

继续docker的学习之旅,今天练习一些常用的命令:

一、镜像相关

1.1 列出本机所有镜像

 

docker常用命令操作方法

后面的操作,都以ubuntu做为练习的目标。

另外:如果某些镜像文件不想要了,可以用下面的命令删除

1.2 删除镜像

docker rmi 镜像id(即:1.1图中的image id)

有时候删除会失败,比如:有一个容器正在使用该镜像文件。这时可以加参数-f 强制删除,如果不清楚每个命令可以加哪些参数,可以用

docker 命令 --help

查看帮助,比如:

?
1
2
3
4
5
6
bin docker rmi --help
usage: docker rmi [options] image [image...]
remove one or more images
 -f, --force=false force removal of the image
 --help=false   print usage
 --no-prune=false  do not delete untagged parents

二、容器相关

2.1 最基本的启动

docker run -it ubuntu

参数-it的含义,可以用docker run --help查看,就不展开了

2.2 启动后执行命令

?
1
docker run -it ubuntu echo 'hello world'

2.3 启动时指定容器名称

?
1
docker run -it --name 'myubuntu' ubuntu

容器名称是一个很有意思的东东,后面马上会讲到。上面的命令运行完以后,先用exit退出,以便后面学习其它命令。

2.4 查看最近运行过的所有容器

docker ps -a  

docker常用命令操作方法  

从图上可以看出,如果启动时未显示指定容器名称,docker会自动生成一个好玩的名称,命令的风格大致是:什么样的_谁谁,比如图中的insane_lamarr,字面的意思为"疯狂的拉马尔",从这些细节可以感受到,docker的创造者们都是一帮很爱玩的家伙。

除了容器名称,还有二列非常重要:container id及status,其中status中以up开头的,表示容器正在运行(注:容器是否处于运行状态,排除人为docker stop的因素外,很大程序上是由docker run 最后的命令参数决定的,如果启动时不指定任何命令参数,默认执行/bin/bash,如果指定了类似echo "hello world"之类瞬间就执行完的命令,run起来,马上就会转为关闭,因为命令已经执行完了),而container id在很多场景中都会用到(比如:删除容器)

docker常用命令操作方法

另外,对于同一个镜像(比如ubuntu),默认不指定容器名称的话,每次容器启动docker都会生成一个唯一的名称,这个有点象oop编程,镜像相当于class类定义,是一个只读的模板,而容器则是类的运行实例,java中每次new出来的实例,其hashcode必然不同,所以每次启动的docker容器,名称也不一样,只不过与oop不同的是,oop中实例消亡了,所有关联的信息全清掉了,而docker容器就算停止掉,docker仍会记住其最后的运行状态。

可以做一个小试验,刚才我们已经创建了一个名为myubuntu的容器:

?
1
docker run -it --name 'myubuntu' ubuntu

这一行命令再次运行的话,就会报错:

error response from daemon: conflict. the name "myubuntu" is already in use by container d1c261ad0b1e. you have to remove (or rename) that container to be able to reuse that name.

大意是容器名称mybutun已经被另一个容器(id为d1c261ad0b1e)占用了,要么把原来的容器删除,要么换个名字。

这其中的设计思想,可以仔细琢磨一二,想想也十分合理:类比一下,我们写代码时,同一个类new出多个实例,每个实例都会有自己不同的应用场景,比如:同样是一个order实例,可以用在订单创建的业务场景中,也可以用在订单查询的返回结果中...,docker的镜像也是如此,同样一个ubuntu镜像文件,有人用它创建容器是为了安装nginx当成web server,有人用它创建容器是为了学习hadoop...,为了能以一种友好的方式来区分,所以名字不能冲突,然后,同一个名字的容器,今天安装了软件a,玩事儿后将它关闭,明天可能会继续在这个容器上折腾其它事情,所以每次容器停止,不可能象oop中的实例一样,彻底丢弃实例的所有信息,否则明天就没办法接着玩了。

2.5 停止运行中的容器

docker stop 容器名称

2.6 删除容器

docker rm 容器id

如果容器处于运行状态,上面的操作会失败,可以加-f参数强制删除  

2.7 在已运行的容器中,直接执行命令

docker exec 容器名称 命令

例如:  

docker exec myubuntu apg-get update

2.8 附加到已经运行的容器

docker attach 容器名称

注:该命令运行后,mac上屏幕没任何输出,还以为卡死了,这是假象,直接继续输入命令,比如pwd之类的就能看到结果了.

attach这个命令不太好用,进入终端后,没办法退出而不停止容器,要退出只能输入exit,但这样就将容器停止了,另外一个缺点是,如果多个容器同时attach到相同的容器,在一个窗口中操作的结果,会同步显示到所有窗口。

建议用下面的命令代替:

docker exec -it 容器名称 sh

当然进入容器还有其它一些办法,比如网络端口22映射本机某个端口,容器里启动ssh服务,然后ssh连接进入,或者用nsenter结合进程id进入,但个人觉得这些方法操作都太复杂,远不如上面这行命令简单

2.9 保存对容器所做的修改

在容器上做了一堆操作后,比如在ubuntu的基础上安装了一些软件、部署了一些应用之类,希望分发到其它机器,最简单的办法就是把容器重新生成一个新镜像,然后其它人直接docker pull你的新镜像就可以了。

docker commit -a 作者名字 -m 提交原因 -p 容器id 镜像名称:版本号

比如:

?
1
docker commit -a 'yjmyzz' -m 'test commit' -p d1c261ad0b1e yjmyzz/ubuntu:v2

提交完成后,可以

?
1
docker images 查看

docker常用命令操作方法

从图中可以看出,在ubuntu原来的基础上,生成一个名为yjmyzz/ubuntu的新镜像,然后用新镜像创建容器试试看

?
1
docker run -it --name 'myubuntu2' yjmyzz/ubuntu:v2

三、卷(volumn)相关

我们平时在使用电脑的过程中,会经常通过usb插入一些外部存储设备,比如:u盘之类,插好后,就能象常规硬盘目录一样访问外部存储设备。卷(volume)的意思其实跟这个差不多,可以把host机上的某个目录"插入"到容器中,然后容器中就能直接访问host机上的文件了,即使容器删除掉,卷里的数据仍然可能持久保存。

3.1 创建卷

?
1
docker run -it -v /users/yjmyzz/docker_volumn:/opt/webapp --name myubuntu ubuntu /bin/bash

这个命令略长,但并不复杂,跟前面提到的启动容器相比,只是多了一个-v /users/yjmyzz/docker_volumn:/opt/webapp的部分,意思就是将本机/users/yjmyzz/docker_volumn这个目录映射到容器中的/opt/webapp,启动成功后,保持当前窗口不退出,可以再新开一个terminal容器,进入容器验证一下

docker常用命令操作方法

可以尝试在host本机修改下/users/yjmyzz/docker_volumn/index.html这个文件,然后在容器中cat看下内容,应该马上就能看到最新的内容。

三个大坑:

其一:

-v 参数可以只写前面第一部分,-v /users/yjmyzz/docker_volumn 这样启动也不会报错,但是这样做的效果,在最新版本的docker(1.9.1)上,只会把本机目录挂到容器中,容器中看不到本机的任何文件,所以一定要记得写:后的部分

其二:

权限问题,mac机上如果从网上down(非apple store官方)了一个文件到本机,该文件甚至保存文件的目录权限,都会被设置成特殊权限@,见下面的截图:

docker常用命令操作方法

这本来是mac 10.5以后做的一项安全改进,有此标识的程序,在首次执行时会提示

docker常用命令操作方法

但是有这类特殊权限的目录或文件,被挂到容器中后,docker容器内根本看不到,也就是无权读取。处理办法:

ll -l@ -a

先用这个显示特殊权限的详细信息:

docker常用命令操作方法

然后用xattr -r -d 详细信息 * 去掉这些特殊权限(参考下图),然后再重新挂到容器中,就能正常使用了

docker常用命令操作方法

其三:

mac上挂载的本机目录,必须是在~/(即:当前用户的目录)下,类似/opt/www这样的目录,就算给它所有权限,挂到容器中后,也只能看到目录,读不到任何文件,centos上没这问题。

此外,还可以用命令

?
1
docker inspect myubuntu

查看此时容器的所有状态,会看到一段长长的json输出,类似下面这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
[
{
 "id": "21d15713166ae83b022eea8806bd466da9917422e487e874cc098a0f1329dd48",
 "created": "2016-01-28t02:23:43.91086474z",
 "path": "/bin/bash",
 "args": [],
 "state": {
  "status": "running",
  "running": true,
  "paused": false,
  "restarting": false,
  "oomkilled": false,
  "dead": false,
  "pid": 1843,
  "exitcode": 0,
  "error": "",
  "startedat": "2016-01-28t02:26:09.414485616z",
  "finishedat": "2016-01-28t02:25:43.868883111z"
 },
 "image": "8693db7e8a0084b8aacba184cfc4ff9891924ed2270c6dec6a9d99bdcff0d1aa",
 "resolvconfpath": "/mnt/sda1/var/lib/docker/containers/21d15713166ae83b022eea8806bd466da9917422e487e874cc098a0f1329dd48/resolv.conf",
 "hostnamepath": "/mnt/sda1/var/lib/docker/containers/21d15713166ae83b022eea8806bd466da9917422e487e874cc098a0f1329dd48/hostname",
 "hostspath": "/mnt/sda1/var/lib/docker/containers/21d15713166ae83b022eea8806bd466da9917422e487e874cc098a0f1329dd48/hosts",
 "logpath": "/mnt/sda1/var/lib/docker/containers/21d15713166ae83b022eea8806bd466da9917422e487e874cc098a0f1329dd48/21d15713166ae83b022eea8806bd466da9917422e487e874cc098a0f1329dd48-json.log",
 "name": "/myubuntu",
 "restartcount": 0,
 "driver": "aufs",
 "execdriver": "native-0.2",
 "mountlabel": "",
 "processlabel": "",
 "apparmorprofile": "",
 "execids": null,
 "hostconfig": {
  "binds": [
   "/users/yjmyzz/docker_volumn:/opt/webapp"
  ],
  "containeridfile": "",
  "lxcconf": [],
  "memory": 0,
  "memoryreservation": 0,
  "memoryswap": 0,
  "kernelmemory": 0,
  "cpushares": 0,
  "cpuperiod": 0,
  "cpusetcpus": "",
  "cpusetmems": "",
  "cpuquota": 0,
  "blkioweight": 0,
  "oomkilldisable": false,
  "memoryswappiness": -1,
  "privileged": false,
  "portbindings": {},
  "links": null,
  "publishallports": false,
  "dns": [],
  "dnsoptions": [],
  "dnssearch": [],
  "extrahosts": null,
  "volumesfrom": null,
  "devices": [],
  "networkmode": "default",
  "ipcmode": "",
  "pidmode": "",
  "utsmode": "",
  "capadd": null,
  "capdrop": null,
  "groupadd": null,
  "restartpolicy": {
   "name": "no",
   "maximumretrycount": 0
  },
  "securityopt": null,
  "readonlyrootfs": false,
  "ulimits": null,
  "logconfig": {
   "type": "json-file",
   "config": {}
  },
  "cgroupparent": "",
  "consolesize": [
   0,
   0
  ],
  "volumedriver": ""
 },
 "graphdriver": {
  "name": "aufs",
  "data": null
 },
 "mounts": [
  {
   "source": "/users/yjmyzz/docker_volumn",
   "destination": "/opt/webapp",
   "mode": "",
   "rw": true
  }
 ],
 "config": {
  "hostname": "21d15713166a",
  "domainname": "",
  "user": "",
  "attachstdin": true,
  "attachstdout": true,
  "attachstderr": true,
  "tty": true,
  "openstdin": true,
  "stdinonce": true,
  "env": null,
  "cmd": [
   "/bin/bash"
  ],
  "image": "ubuntu",
  "volumes": null,
  "workingdir": "",
  "entrypoint": null,
  "onbuild": null,
  "labels": {},
  "stopsignal": "sigterm"
 },
 "networksettings": {
  "bridge": "",
  "sandboxid": "893c76e283a75e3eebb474bf1b5bce901a37778de3514b526312134fcc858d2c",
  "hairpinmode": false,
  "linklocalipv6address": "",
  "linklocalipv6prefixlen": 0,
  "ports": {},
  "sandboxkey": "/var/run/docker/netns/893c76e283a7",
  "secondaryipaddresses": null,
  "secondaryipv6addresses": null,
  "endpointid": "a7fee41964177719fbd149df820bf66dbd976ebe7cea0b68497ae2fe4c06efc5",
  "gateway": "172.17.0.1",
  "globalipv6address": "",
  "globalipv6prefixlen": 0,
  "ipaddress": "172.17.0.2",
  "ipprefixlen": 16,
  "ipv6gateway": "",
  "macaddress": "02:42:ac:11:00:02",
  "networks": {
   "bridge": {
    "endpointid": "a7fee41964177719fbd149df820bf66dbd976ebe7cea0b68497ae2fe4c06efc5",
    "gateway": "172.17.0.1",
    "ipaddress": "172.17.0.2",
    "ipprefixlen": 16,
    "ipv6gateway": "",
    "globalipv6address": "",
    "globalipv6prefixlen": 0,
    "macaddress": "02:42:ac:11:00:02"
   }
  }
 }
}
]

90~97行的mounts节点描述了当前容器挂载的"卷"信息。

最后指出一点:目前docker仅支持在run(创建)容器时使用-v创建卷,对于一个已经start的容器,如果想动态添加卷,是十分困难的。虽然国外有牛人,实现了在容器启动后动态添加卷,但过程十分曲折,而且并不能能用,有兴趣的可以参考下面的文章

http://jpetazzo.github.io/2015/01/13/docker-mount-dynamic-volumes/

3.2 列出所有卷

docker常用命令操作方法

docker volume ls

3.3 删除卷

docker volume rm 卷名称

注:删除一个容器时,默认不会删除容器关联的卷,所以随着时间的推移,host上可能会存在大量的"僵尸"卷,占用硬盘空间。建议每次docker rm 容器时,加上参数-v,这样删除容器时会一并将对应的卷删除,但是这样也会有一个副作用,如果多个容器同时关联到同一个卷,可能会影响到其它容器。所以在使用卷的时候要规划清楚,最好一个容器只对应一个卷。

tips:如果要批量删除所有卷,一个一个rm显然太麻烦了,可以用下面的方式快速搞定

a) 进入docker虚拟机defaut

docker-machine ssh default

b) 查看volume所在的目录

docker常用命令操作方法

c)切换到sudo模式

sudo -i

d) 进入volume所在根目录

?
1
cd /var/lib/docker/volumes/ 

docker常用命令操作方法  

上图的ls命令已经说明,所谓的数据卷,其实就是一个个目录,再次印证了linux里的一句名言『一切皆是文件』,剩下的事情,地球人都知道了,邪恶的

rm -rf *

,最后还要重启虚拟机,退回到mac主机

?
1
docker-machine restart default 

3.4 数据卷容器

如果多个容器之间希望共享一份数据,除了上面的方式外,docker还允许定义一个专用的容器,这个容器啥也不干,只用来放数据,这种容器称为『数据卷容器』

示例:

?
1
docker run -it -v /users/yjmyzz/docker_volumn:/sites --name site_files kitematic/hello-world-nginx echo 'only for nginx web files'

上面的命令跟之前创建卷的完全一样,现在我们有了一个名为site_files的数据卷容器,注意:创建数据卷容器时,最后的命令通常都是些打酱油的echo之类,反正只是一个存数据的容器,不用执行其它命令,甚至它本身都不需要处于启动状态。

然后,其它容器创建时,就可以使用它了:

?
1
docker run -d --volumes-from site_files --name nginx1 kitematic/hello-world-nginx sh ./start.sh

注意上面的--volumes-from site_files 这个就是使用数据卷容器的关键,其它跟之前的完全相同,多个容器可以挂同一个数据卷容器,一个容器也可以挂多个数据卷容器。

四、网络相关

4.1 端口映射

?
1
-p ip:host_port:container_port

上面的参数表示将本机ip上的hostport映射到容器的container_port,示例:  

?
1
docker run -it -v /users/yjmyzz/documents/kitematic/hello-world-nginx/website_files:/website_files -p 0.0.0.0:10080:80 --name my-nginx kitematic/hello-world-nginx sh /start.sh

这个命令更长了,结合了之前所有学习到的参数,注意多出的问题-p 0.0.0.0:10080:80,表示将本机10080端口映射到容器80端口 

注:如果把-p换成大写的-p,系统会随机映射到本机一个空闲的端口号 

4.2 指定hostname

默认创建容器时,hostname是一个唯一的随机字符串,很难记,可以在docker run -h hostname名称来指定,这个就不演示了

4.3 容器间的网络连接

假如有二个容器mysql, appserver,通常appserver中要访问数据库,所以需要appserver能直接访问mysql,下面演示了如何实现:

a) 先创建mysql容器

?
1
docker run -it -h mysql --name mysql ubuntu /bin/bash

b) 再创建appserver容器  

?
1
docker run -it -h appserver --name appserver --link mysql:mysqlserver ubuntu /bin/bash

注意其中的--link mysql:mysqlserver,冒号前的为容器名称,冒号后的为容器别名,启动后appserver中就能直接ping通mysql容器了,见下图:

docker常用命令操作方法

注:这个连接是单向的,即appserver可以ping通mysql容器,但反过来不行。而且最新版的docker在ps时,name列也不再象之前网上说的那个显示成a/b这种格式,要查看一个容器是否有连接,最直接的方式还是docker inspect 容器名称

docker常用命令操作方法  

总结

以上所述是小编给大家介绍的docker常用命令操作方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

原文链接:https://yq.aliyun.com/articles/250716?utm_content=m_35119