Docker Desktop for mac 端口转发(port forwarding)¶
在完成 在 kind 部署MetalLB 之后,也就已经实现了标准Kubernets集群应用部署的输出了。但是对于 Docker Desktop网络 限制,实际上整个Kubernets都是在一个Linux虚拟机中运行,从物理主机 macOS 上不能直接访问这个输出地址,还需要做一个端口转发(port forwarding)来访问。
备注
在 X86移动云Kind(本地docker模拟k8s集群) ,采用直接在Linux物理主机上部署 kind(本地docker模拟k8s集群) 就没有这个麻烦,外部可以直接访问
解决方案¶
在 Docker Desktop 中运行一个连接
kind
bridge 的容器dev-gw
( Gateway,为了精简系统采用 Fedora镜像(采用tini替代systemd) 的dev-ssh
基础镜像 )在
dev-gw
运行时使用-p
参数将所有需要从外部访问的端口都映射到这个dev-gw
容器上在
dev-gw
内部运行 iptables 实现端口转发,转发到 在 kind 部署MetalLB 输出的LoadBalancer的EXTERNAL-IP
的端口,实现访问 kind(本地docker模拟k8s集群) 部署的应用
备注
另一种方式是采用 Docker环境运行Squid 运行反向代理服务器来实现端口转发
部署¶
基于 Fedora镜像(采用tini替代systemd) 的
fedora-ssh-tini
做一些改进定制构建一个fedora-gw
镜像:
fedora-gw
镜像的Dockerfile¶# USE DOCKER BUILD
# docker build --rm -t fedora-gw .
# USE DOCKER RUN
# docker run -itd --privileged=true -p 1122:22 --hostname fedora-gw --name fedora-gw fedora-gw
# USE nerdctl (containerd) BUILD
# nerdctl build -t fedora-gw .
# INTERACT RUN
# nerdctl run -it --privileged=true -p 1122:22 --hostname fedora-gw --name fedora-gw fedora-gw:latest
# BACKGROUND RUN
# nerdctl run -d --privileged=true -p 1122:22 --hostname fedora-gw --name fedora-gw fedora-gw:latest
FROM fedora:latest
MAINTAINER vincent huatai <vincent@huatai.me>
ENV container docker
# set china repo: mirros.163.com
RUN cp -R /etc/yum.repos.d /root/yum.repos.d
RUN rm /etc/yum.repos.d/fedora-cisco-openh264.repo
RUN sed -i 's/metalink=/#metalink=/g' /etc/yum.repos.d/*
RUN sed -i 's/#baseurl=/baseurl=/g' /etc/yum.repos.d/*
RUN sed -i 's/download.example\/pub\/fedora\/linux/mirrors.163.com\/fedora/g' /etc/yum.repos.d/*
RUN cp /root/yum.repos.d/fedora-cisco-openh264.repo /etc/yum.repos.d/
RUN dnf clean all
RUN dnf -y update
# Add Tini
ENV TINI_VERSION v0.19.0
# 标准方法是采用ADD方式向镜像添加tini,但是GFW阻碍,改为下载后本地复制
#ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
COPY tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
# Copy tini entrypoint script
COPY entrypoint_ssh_cron_bash /entrypoint.sh
RUN chmod +x /entrypoint.sh
# not need systemd(initscripts)
RUN dnf -y install which sudo passwd openssh-clients openssh-server \
iproute net-tools bind-utils bzip2 tmux sysstat nfs-utils lsof \
procps tree file mlocate rsync cronie cronie-anacron \
iptables-services
# add account "admin" and give sudo privilege
RUN groupadd -g 505 admin
RUN useradd -g 505 -u 505 -d /home/admin -m admin
RUN usermod -aG wheel admin
RUN echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# set TIMEZONE to Shanghai
RUN unlink /etc/localtime && ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# Add ssh public key for login
RUN mkdir -p /home/admin/.ssh
COPY authorized_keys /home/admin/.ssh/authorized_keys
RUN chown -R admin:admin /home/admin/.ssh
RUN chmod 600 /home/admin/.ssh/authorized_keys
RUN chmod 700 /home/admin/.ssh
RUN ssh-keygen -A
# run service when container started - sshd
EXPOSE 22:122
# Run your program under Tini
# CMD ["/your/program", "-and", "-its", "arguments"]
CMD ["/entrypoint.sh"]
备注
镜像 从Dockerfile构建Docker镜像 添加了 iptables
工具( iptables-services
)以及必要的运维工具
构建
fedora-gw
镜像:
fedora-gw
镜像¶docker build --rm -t fedora-gw .
基于
fedora-gw
镜像运行dev-gw
容器 :
fedora-gw
镜像运行 dev-gw
容器,将host主机的 10000-10099 端口全部映射到这个网关容器¶docker run -itd -p 122:22 -p 10000-10099:10000-10099 --network kind \
--cap-add=NET_ADMIN --cap-add=NET_RAW \
--hostname dev-gw --name dev-gw fedora-gw
备注
Docker支持端口范围的Port Mapping,不需要一个个映射可以方便这个 fedora-gw
容器内部自行进行端口转发。但是,我发现端口映射会拖慢Docker容器启动的速度(例如我尝试映射999个端口),所以我最终改为只映射99个端口(小型开发测试环境足够了)
备注
在容器内部使用 iptables
需要在运行容器时添加参数 --cap-add=NET_ADMIN
和 --cap-add=NET_RAW
(从 Docker 1.2开始支持)
检查和验证¶
检查运行起来的
dev-gw
可以看到运行的容器实现了10000-10099
的端口范围映射,并且ssh -p 122
从host主机能够登陆到dev-gw
容器中 :
dev-gw
容器¶% docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
78131fbac6e8 fedora-gw "/tini -- /entrypoin…" 2 hours ago Up 2 hours 122/tcp, 0.0.0.0:10000-10099->10000-10099/tcp, 0.0.0.0:122->22/tcp dev-gw
% ssh -p 122 admin@127.0.0.1
Last login: Tue Jan 31 18:59:02 2023 from 172.22.0.1
[admin@dev-gw ~]$
[admin@dev-gw ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
overlay 59G 25G 31G 45% /
tmpfs 64M 0 64M 0% /dev
shm 64M 0 64M 0% /dev/shm
/dev/vda1 59G 25G 31G 45% /etc/hosts
tmpfs 3.9G 0 3.9G 0% /sys/firmware
在 在 kind 部署MetalLB 配置了负载均衡流量转发到后端 Kubeernetes服务(services)
fedora-dev-service
,可以看到对外服务的EXTERNAL-IP
是172.22.255.201
fedora-dev-tini
设置LoadBalancer服务类型后 kubectl get services
显示服务具备了 EXTERNAL-IP
¶% kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
fedora-dev-service LoadBalancer 10.96.175.32 172.22.255.201 22:32440/TCP,80:31218/TCP,443:32049/TCP 4d1h
端口转发¶
在
dev-gw
内部执行iptables
进行端口转发:
dev-gw
容器内执行iptables转发端口¶#!/usr/bin/env bash
# 需要在host主机(也就是Docker 虚拟机)内部执行以下这行命令激活内核IP转发
#echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -F
iptables -t nat -F
iptables -X
dev_gw="172.22.0.12"
fedora_dev="172.22.255.201"
fedora_dev_ssh="10001"
iptables -t nat -A PREROUTING -p tcp --dport ${fedora_dev_ssh} -j DNAT --to-destination ${fedora_dev}:22
iptables -t nat -A POSTROUTING -p tcp -d ${fedora_dev} --dport 22 -j SNAT --to-source ${dev_gw}
备注
这里我遇到一个错误,在容器内部无法修改 /proc/sys/net/ipv4/ip_forward
,提示报错:
iptables_port_forwarding: line 3: /proc/sys/net/ipv4/ip_forward: Read-only file system
Docker虚拟机内核参数必须在host主机,也就是 Docker Desktop on Mac 虚拟机 内修改
使用 Docker Desktop on Mac 虚拟机 中方法进入Docker 虚拟机中:
docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
在Docker VM内部执行以下命令开启内核IP转发:
echo 1 > /proc/sys/net/ipv4/ip_forward
此时就完成了所有端口转发的配置,从Host主机访问本机(所有网络接口)的端口 10001
都会被 Docker Desktop 映射到 dev-gw
虚拟机,然后又被 dev-gw
端口转发给目标 在 kind 部署MetalLB 对外提供的 fedora-dev
虚拟机的 fedora-dev-service
服务上(对外提供了多个服务端口)。整个过程虽然繁复,但是能够真正实现访问 kind(本地docker模拟k8s集群) 集群提供的Kubernetes服务,和生产环境没有差别。
改进¶
为了方便快捷完成端口转发,修订运行
dev-gw
容器的命令,将iptables_port_forwarding
脚本直接bind
到容器内部,这样随时可以在物理主机上修改好脚本,只要重新创建一次容器就可以运行了:
dev-gw
容器运行时bind mount进端口转发脚本,方便自动执行¶docker run -itd -p 122:22 -p 10000-10099:10000-10099 --network kind \
--cap-add=NET_ADMIN --cap-add=NET_RAW \
--mount type=bind,source="$(pwd)"/iptables_port_forwarding,target=/root/iptables_port_forwarding,readonly \
--hostname dev-gw --name dev-gw fedora-gw
下一步¶
接下来需要解决如何将物理主机映射到 Docker Desktop 内部并被 kind(本地docker模拟k8s集群) 运行的容器挂载,这样就能够方便在物理主机上保存数据,实现在Kubernetes容器中进行各种开发和模拟,数据不会丢失: