docker容器添加对外映射端口
一般在运行容器时,我们都会通过参数 -p(使用大写的-P参数则会随机选择宿主机的一个端口进行映射)来指定宿主机和容器端口的映射,例如
docker run -it -d --name [container-name] -p 8088:80 [image-name]
这里是将容器内的80端口映射到宿主机的8088端口
参数说明
- -d 表示后台运行容器
- -t 为docker分配一个伪终端并绑定到容器的标准输入上
- -i 是让容器的标准输入保持打开状态
- -p 指定映射端口
在运行容器时指定映射端口运行后,如果想要添加新的端口映射,可以使用以下两种方式:
方式一:将现有的容器打包成镜像,然后在使用新的镜像运行容器时重新指定要映射的端口
大概过程如下:
先停止现有容器
docker stop container-name
将容器commit成为一个镜像
docker commit container-name new-image-name
用新镜像运行容器
docker run -it -d --name container-name -p p1:p1 -p p2:p2 new-image-name
方式二:修改要端口映射的容器的配置文件
查看容器信息:
docker ps -a
查看容器的端口映射情况,在容器外执行:
docker port 容器ID 或者 docker port 容器名称
查找要修改容器的容器Id
docker inspect f244 |grep Id
进到/var/lib/docker/containers 目录下找到与 Id 相同的目录,修改 hostconfig.json 和 config.v2.json文件:
若该容器还在运行,先停掉
docker stop 容器ID
停掉docker服务
systemctl stop docker
修改hostconfig.json如下,添加端口绑定"9003/tcp": [{"HostIp": "","HostPort": "9003"}],表示绑定端口9003
修改config.v2.json在ExposedPorts中加上要暴露的端口,即9003
改完之后保存启动docker
systemctl start docker
之后可以再次查看添加的端口是否已映射绑定上
附注:
1、将容器打包成镜像命令:
docker commit -a "king西阳" -m "a new image" [容器名称或id] [打包的镜像名称]:[标签]
常用OPTIONS说明:
- -a :提交的镜像作者
- -c :使用Dockerfile指令来创建镜像
- -m :提交时的说明文字
- -p :在commit时,将容器暂停
2、查看宿主机端口是否和容器内端口映射成功,在容器外执行
netstat -an |grep 宿主机的映射端口
如果有进程存在则表示有映射
方法三
原文地址:https://www.jb51.net/article/142462.htm
创建两个容器并进行了端口映射,结果如图所示:
假如,我start一个容器,其内部IP为172.17.0.5,并在容器内部启动了80端口。
FORWARD规则链我们不用管它,docker已经帮我们写好了,我们只需要关心NAT中的几条链即可。
查看NAT表中的PREROUTING链
从上面可以看出,iptables将满足条件的数据都转发到了DOCKER链上去了。
查看NAT表中的DOCKER链
仿照上图,我们添加一条自己的映射规则,将宿主的8082端口映射到172.17.0.5的80端口上去,规则如下:
1
|
iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8082 -j DNAT --to-destination 172.17.0.5:80 |
查看NAT表中的POSTROUTING链
仿照上图中的规则,书写的规则如下:
1
|
iptables -t nat -A POSTROUTING -s 172.17.0.5/32 -d 172.17.0.5/32 -p tcp -m tcp --dport 80 -j MASQUERADE |
查看FILTER表中的DOCKER链
仿照上图书写规则如下:
1
|
iptables -t filter -A DOCKER -d 172.17.0.5/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT |
结果
虽然IP为172.17.0.5的容器没有开启端口映射,如下图所示:
但我们依然能够通过访问宿主机(192.168.78.238)的8082端口来访问172.17.0.5的80端口,效果如下:
使用此方法有一个缺点,不能访问localhost:8082,也就是说如果想对localhost也进行转发,需要进行额外的配置。
Mac 动态端口
动态添加端口:https://www.jianshu.com/p/6aefed2be1c7
HOW TO LOGIN THE VM OF DOCKER DESKTOP FOR MAC
原文地址:https://www.dbform.com/2019/07/08/how-to-login-the-vm-of-docker-desktop-for-mac/
DOCKER FOR MACOS的宿主机在哪里?
我们之前在MOVING MYSQL GROUP REPLICATION INSTANCES TO DOCKER CONTAINER ON MACOS这篇文章中提过在Docker for macOS中,容器的宿主机并不是macOS本身,而是在macOS中运行的一个虚拟机。虚拟机的路径可以通过查看Docker Desktop的配置界面获知。
如果我们想登录这台虚拟机应该怎么做?
方法一
使用screen命令。实际上在上面那篇文章中我们提到过这个方法。
比如在上图中我们看到虚拟机的文件路径是:
/Users/Kamus/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw
进入到这个文件的所在目录。可以看到tty这个软链接文件。
$ cd /Users/Kamus/Library/Containers/com.docker.docker/Data/vms/0
$ ls -l
total 31067864
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 00000002.000005f4
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 00000002.00001000
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 00000002.00001001
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 00000002.0000f3a5
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 00000003.000005f5
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 00000003.00000948
-rw-r--r--@ 1 Kamus staff 63999836160 7 7 12:51 Docker.raw
-rw-r--r-- 1 Kamus staff 215040 7 4 12:22 config.iso
srwxr-xr-x 1 Kamus staff 0 7 4 12:22 connect
lrwxr-xr-x 1 Kamus staff 17 7 4 12:22 guest.000005f5 -> 00000003.000005f5
lrwxr-xr-x 1 Kamus staff 17 7 4 12:22 guest.00000948 -> 00000003.00000948
-rw-r--r-- 1 Kamus staff 2303 7 4 12:22 hyperkit.json
-rw-r--r-- 1 Kamus staff 4 7 4 12:22 hyperkit.pid
drwxr-xr-x 2 Kamus staff 64 11 21 2018 log
-rw-r--r-- 1 Kamus staff 36 11 21 2018 nic1.uuid
lrwxr-xr-x 1 Kamus staff 12 7 4 12:22 tty -> /dev/ttys000
screen该文件即可连接到虚拟机的输出窗口中。
$ screen tty
screen之后可能终端界面会悬停,按一下Ctrl+c,即可显示出已经登录到了虚拟机中。
linuxkit-025000000001:~# uname -a
Linux linuxkit-025000000001 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 Linux
登出screen界面可以使用Ctrl+a d(同时按住Ctrl和a,然后放开再按下d),重新登入,可以使用screen -r,如果要彻底结束screen,可以使用Ctrl+a k。
方法二
还可以使用更优雅的方式,临时建一个最小化的debian容器,指定容器运行在pid=host命名空间下,然后该容器运行nsenter命令。如果之前没有安装过依赖的debian镜像,那么会首先自动下载这个镜像,镜像很小,只有101MB。
$ docker run -it --rm --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
Unable to find image 'debian:latest' locally
latest: Pulling from library/debian
6f2f362378c5: Pull complete
Digest: sha256:118cf8f3557e1ea766c02f36f05f6ac3e63628427ea8965fb861be904ec35a6f
Status: Downloaded newer image for debian:latest
/ # uname -a
Linux linuxkit-025000000001 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 Linux
如果说觉得101MB的debian镜像还是太大了,那么可以选择下载Alpine Linux镜像,这个镜像只有5.56MB。
$ docker run -it --rm --privileged --pid=host alpine:edge nsenter -t 1 -m -u -n -i sh
详细解释一下这条命令为什么就会登录进macOS中作为宿主机的VM里面。
–rm表示在退出的时候就自动删除该容器;
–privileged表示允许该容器访问宿主机(也就是我们想要登录的VM)中的各种设备;
–pid=host表示允许容器共享宿主机的进程命名空间(namespace),或者通俗点儿解释就是允许容器看到宿主机中的各种进程;
这些是docker在启动容器时候的参数设置,但是仅仅依靠这些参数还无法让我们直接登录到宿主机VM中,接下来解释最主要的nsenter命令。
nsenter是一个小工具允许我们进入一个指定的namespace然后运行指定的命令,ns=namespace,enter=进入。
namespace是容器技术的根基,基本上可以认为namespace就是一组隔离的资源,不同的进程可以看到不同的系统资源这里和这里有比较详细的关于namespace的介绍。
可以从操作系统的/proc/[pid]/ns目录下一窥全貌。比如我们进入pid=1的ns目录下。可以看到有一共8种namespace。
# pwd
/proc/1/ns
# ls -l
total 0
lrwxrwxrwx 1 root root 0 Jul 8 12:51 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 net -> net:[4026531889]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 pid_for_children -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jul 8 12:51 uts -> uts:[4026531838]
接下来我们通过一个例子来直观的感受一下namespace和nsenter的功能,由于没有现成的操作系统命令可以修改一个namespace用于演示,所以我们需要先在nbrownuk下载一段c函数源码,我们演示UTS namespace,这个相对直观,所以只需要下载invoke_ns3.c即可。
UTS namespace中包含了hostname,我们的演示计划是给一个进程设置一个新的hostname,然后用nsenter进入该进程的namespace查看。
下载完invoke_ns3.c之后,需要编译。
# gcc -o invoke_ns3 ./invoke_ns3.c
# ls -l
total 28
-rwxr-xr-x 1 root root 71928 Jul 8 13:41 invoke_ns3
-rw-r--r-- 1 root root 4603 Jul 8 13:38 invoke_ns3.c
使用新的hostname启动一个bash进程,15842是执行invoke_ns3命令的进程,而15843则是bash进程,接下来我们只需要用nsenter进入15843进程验证一下即可。
# ./invoke_ns3 -vu newhostname bash
Parent: PID of parent is 15842
Parent: PID of child is 15843
Child: PID of child is 15843
Child: executing command bash ...
[root@newhostname root]
启动一个新的终端。可以看到在终端自己的hostname没有变化的情况下,我们用nsenter进入不同的进程,看到的hostname是不同的,其中15843进程中显示的就是上面修改过的主机名。
# hostname
ecs-arm-4xlarge
# nsenter -t 1 -u hostname
ecs-arm-4xlarge
# nsenter -t 15843 -u hostname
newhostname
最后解释一下nsenter命令的选项。回顾一下命令是:nsenter -t 1 -m -u -n -i sh
-t 1: 表示要进入哪个pid,1表示整个操作系统的主进程id
-m: 进入mount namespace,挂载点
-u: 进入UTS namespace,也就是上面我们演示的那个namespace
-n: 进入network namespace,网络
-i: 进入IPC namespace,进程间通信
sh: 表示运行/bin/sh
到此为止:docker加上–pid=host加上nsenter,就让我们登录到了在macOS中作为docker容器宿主机的VM里。
方法三
实际上跟方法二一样,但是在方法二中,需要下载至少是alpine镜像这样的最简化Linux操作系统,虽然这个镜像只有5MB多,已经很小了,但是我们还是可以直接下载网络上其它人提供的单独的nsenter镜像,这样的镜像更小,大约只有几百KB。
walkerlee/nsenter镜像有583KB。
docker run --rm -it --privileged --pid=host walkerlee/nsenter -t 1 -m -u -i -n sh
justincormack/nsenter1镜像更小,只有101KB。
docker run -it --rm --privileged --pid=host justincormack/nsenter1
方法四:会出现乱码,不推荐
从这里找到:https://i.cnblogs.com/articles?cateId=1271815&page=6
nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock
容器目录也发生了改变
/var/lib/docker/containers