我正在尝试创建一个类似完整虚拟机的Docker容器。我知道可以在Dockerfile中使用EXPOSE指令来暴露端口,并可以使用docker run
命令中的-p
标志来分配端口,但一旦容器实际运行起来,是否有命令可以动态开放/映射其他端口?
例如,假设我有一个正在运行sshd的Docker容器。另一个人使用容器ssh进入并安装httpd。是否有一种方法可以将容器上的80端口映射到主机上的8080端口,以便人们可以访问在容器中运行的Web服务器,而无需重新启动它?
我正在尝试创建一个类似完整虚拟机的Docker容器。我知道可以在Dockerfile中使用EXPOSE指令来暴露端口,并可以使用docker run
命令中的-p
标志来分配端口,但一旦容器实际运行起来,是否有命令可以动态开放/映射其他端口?
例如,假设我有一个正在运行sshd的Docker容器。另一个人使用容器ssh进入并安装httpd。是否有一种方法可以将容器上的80端口映射到主机上的8080端口,以便人们可以访问在容器中运行的Web服务器,而无需重新启动它?
wget http://container_ip:8000
要获取容器的IP地址,请运行以下2个命令:
docker ps
docker inspect container_name | grep IPAddress
在内部,当您运行一个镜像时,Docker会调用iptables来进行操作,因此也许可以对此进行一些变化以使其工作。
要将容器的端口8000暴露到本地主机的端口8001:
iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000
docker run --net host ..
将容器直接附加到主机的网络接口(即,net未命名空间化),从而公开容器中打开的所有端口。CONTAINER_IP=$(docker inspect container_name | jq .[0].NetworkSettings.IPAddress | sed -r 's/\"([^\"]+)\"/\1/''])'); iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination ${CONTAINER_IP}:8000
- ericson.cepedadocker inspect
的-f
选项,而不是使用jq
和sed
命令来获取容器IP地址: CONTAINER_IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' container_name)
。请注意,这不会改变原来的意思,只是使其更加通俗易懂。 - pwes以下是我的建议:
sudo docker ps
sudo docker commit <containerid> <foo/live>
sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash
sudo docker
而不是仅仅运行 docker
? - Thiago Figueirodocker commit
命令,我可以立即测试应用程序,而不必花费数小时重新安装所有软件包。 - gustavohenke虽然您无法公开暴露现有容器的新端口,但可以在相同的Docker网络中启动一个新容器并将流量转发到原始容器。
# docker run \
--rm \
-p $PORT:1234 \
verb/socat \
TCP-LISTEN:1234,fork \
TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT
启动一个监听端口为80的Web服务,但不要暴露其内部端口80(糟糕!):
# docker run -ti mkodockx/docker-pastebin # Forgot to expose PORT 80!
找到它的 Docker 网络 IP:
# docker inspect 63256f72142a | grep IPAddress
"IPAddress": "172.17.0.2",
使用verb/socat
启动,并且暴露端口8080,将TCP流量转发到该IP的端口80:
# docker run --rm -p 8080:1234 verb/socat TCP-LISTEN:1234,fork TCP-CONNECT:172.17.0.2:80
现在你可以访问 http://localhost:8080/ 上的 pastebin,而你的请求将会被发送到 socat:1234
并被转发到 pastebin:80
,响应也将沿着同样的路径返回。
alpine/socat
。 - Jambyverb/socat
启动命令中添加 --net myfoldername_default
。请注意不要更改原意。 - emazzotta在 Docker 1.4.1 上,IPtables 的黑客技巧不起作用。
最好的方法是使用 socat 运行另一个容器来公开端口并进行中继。这就是我用 SQLPlus (暂时) 连接数据库所做的。
docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus
Dockerfile:
FROM debian:7
RUN apt-get update && \
apt-get -y install socat && \
apt-get clean
USER nobody
CMD socat -dddd TCP-LISTEN:1521,reuseaddr,fork TCP:db:1521
FROM
基础镜像,以有效利用资源。 - Excalibur这里有另一个想法。使用SSH进行端口转发;这样可以在Docker主机是虚拟机时,也能在OS X(可能也适用于Windows)上工作。
docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>
iptables
解决方案,我还需要在主机上运行另外两个命令来将其开放到外部世界。HOST> iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.17.0.2:443
HOST> iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source 172.17.0.2 --destination 172.17.0.2 --dport https
HOST> iptables -A DOCKER -j ACCEPT -p tcp --destination 172.17.0.2 --dport https
注意:我正在打开https端口(443),我的docker内部IP是172.17.0.2
注意2:这些规则是临时的,只会持续到容器重新启动为止
在我的场景中,我想临时连接到在容器中运行的MySQL,由于其他应用程序容器都与其链接,停止、重新配置和重新运行数据库容器是不可行的。
由于我想从 Sequel Pro通过SSH隧道访问MySQL数据库,所以我要在主机上使用端口33306
。(不要使用3306
,以防外部MySQL实例运行。)
大约花了一小时的时间进行iptables调整,但都没有效果:
步骤如下:
mkdir db-expose-33306
cd db-expose-33306
vim Dockerfile
编辑dockerfile
,将其放置在其中:
# Exposes port 3306 on linked "db" container, to be accessible at host:33306
FROM ubuntu:latest # (Recommended to use the same base as the DB container)
RUN apt-get update && \
apt-get -y install socat && \
apt-get clean
USER nobody
EXPOSE 33306
CMD socat -dddd TCP-LISTEN:33306,reuseaddr,fork TCP:db:3306
然后构建镜像:
docker build -t your-namespace/db-expose-33306 .
然后运行它,链接到正在运行的容器。(在此情况下,使用-d
而不是-rm
将其保留在后台,直到明确停止和删除。我只想在这种情况下暂时运行它。)
docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306 your-namespace/db-expose-33306
$ yum install -y openssh openssh-server.x86_64
service sshd restart
Stopping sshd: [FAILED]
Generating SSH2 RSA host key: [ OK ]
Generating SSH1 RSA host key: [ OK ]
Generating SSH2 DSA host key: [ OK ]
Starting sshd: [ OK ]
$ passwd # You need to set a root password..
$ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*/\1/g'
172.17.0.2
然后在主机上,你只需要这样做:
sudo ssh -NfL 80:0.0.0.0:80 root@172.17.0.2
基于Robm的答案,我创建了一个名为portcat
的Docker镜像和Bash脚本。
使用portcat,您可以轻松地将多个端口映射到现有的Docker容器。以下是使用(可选)Bash脚本的示例:
curl -sL https://raw.githubusercontent.com/archan937/portcat/master/script/install | sudo bash
portcat my-awesome-container 3456 4444:8080
好的!Portcat正在进行映射:
3456
映射到my-awesome-container:3456
4444
映射到my-awesome-container:8080
请注意,Bash脚本是可选的,以下命令:
ipAddress=$(docker inspect my-awesome-container | grep IPAddress | grep -o '[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}' | head -n 1)
docker run -p 3456:3456 -p 4444:4444 --name=alpine-portcat -it pmelegend/portcat:latest $ipAddress 3456 4444:8080
希望 portcat
能对你们有用。干杯!
如果没有任何答案对某人有用 - 检查您的目标容器是否已在Docker网络中运行:
CONTAINER=my-target-container
docker inspect $CONTAINER | grep NetworkMode
"NetworkMode": "my-network-name",
将其保存在变量$NET_NAME
中以备后用:
NET_NAME=$(docker inspect --format '{{.HostConfig.NetworkMode}}' $CONTAINER)
docker inspect $CONTAINER | grep -A2 Aliases
"Aliases": [
"my-alias",
"23ea4ea42e34a"
请将其存储到变量$ALIAS
中以备后用:
ALIAS=$(docker inspect --format '{{index .NetworkSettings.Networks "'$NET_NAME'" "Aliases" 0}}' $CONTAINER)
现在在网络$NET_NAME
中的容器中运行socat
,以便桥接到$ALIAS
容器的暴露端口(但未发布):
docker run \
--detach --name my-new-proxy \
--net $NET_NAME \
--publish 8080:1234 \
alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:$ALIAS:80