参数--publish(-p)用来把容器里的端口映射到主机外面来。
基本格式
有两种格式:
- 短格式: <externport>:<internport>
- 长格式:[mode=host],published=<externport>,target=<internport>
短格式只能标识两个参数即可,外部端口:内部端口,长格式还可以标识mode参数。
mode=ingress|host 的用法
格式:mode=[ingress|host],其中ingress是缺省格式,含义:
- ingress把端口映射到整个swarm的网络,从而访问swarm网络的任何一台主机都可以,即使这台主机没有一个service的container在运行。
- host只把端口映射到service的container所在运行的主机,其他主机就不能访问。
举个例子:
假设swarm网络有两台主机构成:hostA,hostB:
- 在hostA上运行下面命令创建一个service,包含缺省一个container:
$ docker service create --name mynginx -p published=20000,target=80 nginx
- 此时它会创建一个service,包含一个container,这个container被分配到hostA或者hostB随机。
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
qyw58qcs4xrh mynginx replicated 1/1 nginx:latest *:20000->80/tcp
我们在hostA上ping 2000端口
$ curl localhost:20000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
...
如果我们在hostB上也访问20000端口
$ curl localhost:20000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
...
在两台主机上都能访问20000端口,尽管service只有一个container运行在其中一台主机上。
下面一个测试,我们指定mode=host。
- 在hostA上运行:
$ docker service create --name mynginx -p mode=host,published=20000,target=80 nginx
- 在hostA上查看container
$ docker ps | grep nginx
- 在hostB上查看container
$ docker ps | grep nginx
df3216d6b665 nginx:latest "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:20000->80/tcp mynginx.1.if3z9quqscqdbg87cjzmlwkdm
此时container被随机分配到了hostB上。
在hostA访问20000端口
$ curl 127.0.0.1:20000
curl: (56) Recv failure: Connection reset by peer
在hostB访问20000端口
$ curl localhost:20000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
...
可以看见container运行在hostB上,那么hostB的20000端口可以访问,而hostA的20000端口就不能访问了。
此时我们会想到可能有另外一个问题,即:如果在两个node的主机上使用mode=host创建3个以上的container的,那么必然有一个host上有两个container,他们同时都要映射20000端口会不会有问题? 实验一下:
$ docker service create --name mynginx --replicas=3 -p mode=host,published=20000,target=80 nginx
xmlz7w2x89m4adyx397cko66n
overall progress: 1 out of 3 tasks
1/3: running [==================================================>]
2/3: preparing [=================================> ]
3/3: no suitable node (host-mode port already in use on 2 nodes)
这个时候就创建就失败了,在mode=host模式下多个container不能同时映射同一个外部端口。(当然如果不是mode=host模式,那么多个container是可以在同一个node运行的,因为此时端口映射使用的是ingress模式,不存在多个contaienr都要往外映射端口的问题,而是swarm service统一映射端口)
这个时候就想到了另一个参数:[--mode replicated|global]
参数 [--mode replicated|global]
(这个参数是最外层的参数,不是前面--publish的那个mode参数)
- --mode=replicated是缺省选项,表示要创建的container的数量由参数--replicas决定(缺省值为1)
- --mode=global,表示根据swarm的node个数,在每一个node上创建并且只创建一个container,即contaienr和node是一一对应关系。
自然用了--mode=global就不能使用--replicas参数了:
$ docker service create --name mynginx --mode global --replicas=1 -p published=20000,target=80 nginx
replicas can only be used with replicated mode
哪怕参数replicas=1也不行,因为--mode global自动为service在每一个swarm节点上创建一个container,不需要指定--replicas参数了。
所以总的来说,如果指定--publish mode=host,..., 可以指定参数--mode global,就会为每一个node上创建一个container,就不存在端口冲突的问题了。
publish如何指定多个端口段
如果端口个数少,可以使用多个--publish例如:docker service create --name mynginx -p 1000:1000 -p 1001:1001 -p 1002:1002 <image>
如果端口很多例如1000个,那该怎么办呢?也可以用端口段:
$ docker service create --name mynginx -p 5000-5999:1000-1999 nginx
wo6czxdbveut07ndjlqbr36w4
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wo6czxdbveut mynginx replicated 1/1 nginx:latest *:5000-5999->1000-1999/tcp
注意的是此时不能使用长格式,只能使用这种短格式:
$ docker service create --name mynginx -p published=5000-5999,target=1000-1999 nginx
invalid argument "published=5000-5999,target=1000-1999" for "-p, --publish" flag: strconv.ParseUint: parsing "5000-5999": invalid syntax
See 'docker service create --help'.
长格式语法不识别。