Mac:docker无法映射?这个锅我不背

1,823 阅读4分钟

Mac:docker无法映射?这个锅我不背

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

目录

背景

本篇文章作为k8s学习系列的第一篇,讲一讲在搭建环境遇到的各种问题。

通读完本篇,你将学到:

  1. docker网络相关的知识

  2. 一个简单反向代理的应用集群搭建

  3. 排查容器相关问题的一般性思路

本篇实验环境如下:

  • Mac

  • Docker desktop for mac

  • kuberetes节点(minikube、kubeadm等)

目标

搭建一个集群,其大致模型如下:

目标是访问http://127.0.0.1 可以直接访问到WordPress网站。

步骤

第一步:部署MariaDB

准备yml文件 maria.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: maria-cm

data:
  DATABASE: 'db'
  USER: 'wp'
  PASSWORD: '123'
  ROOT_PASSWORD: '123'
---
apiVersion: v1
kind: Pod
metadata:
  name: maria-pod
  labels:
    app: wordpress
    role: database

spec:
  containers:
  - image: mariadb:10
    name: maria
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 3306

    envFrom:
    - prefix: 'MARIADB_'
      configMapRef:
        name: maria-cm

执行部署命令

# kubectl apply -y maria.yml

执行命令,查看pod的IP

#  kubectl get pod -o wide 
                                                                            22-08-03 - 22:32:10
NAME        READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
maria-pod   1/1     Running   0          47s   172.17.0.3   minikube   <none>           <none>

记住这个172.17.0.3,在第二步的时候需要用到

第二步:部署WordPress的Pod

准备yml文件 wordpress-pod.yml

注意:上面的172.17.0.3需要填在yml的对应位置上。

apiVersion: v1
kind: ConfigMap
metadata:
  name: wp-cm

data:
  HOST: '172.17.0.3'
  USER: 'wp'
  PASSWORD: '123'
  NAME: 'db'

---
apiVersion: v1
kind: Pod
metadata:
  name: wp-pod
  labels:
    app: wordpress
    role: website

spec:
  containers:
  - image: wordpress:5
    name: wp-pod
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

    envFrom:
    - prefix: 'WORDPRESS_DB_'
      configMapRef:
        name: wp-cm

执行部署命令

# kubectl apply -f wordpress-pod.yml                                                                  22-08-03 - 22:37:36
configmap/wp-cm created
pod/wp-pod created

# kubectl get pod -o wide                                                                        :( 1 22-08-03 - 22:38:35
NAME        READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
maria-pod   1/1     Running   0          6m36s   172.17.0.3   minikube   <none>           <none>
wp-pod      1/1     Running   0          59s     172.17.0.4   minikube   <none>           <none>

第三步:为 WordPress Pod 映射端口号,让它在集群外可见

因为 Pod 都是运行在 Kubernetes 内部的私有网段里的,外界无法直接访问,想要对外暴露服务,需要使用一个专门的 kubectl port-forward 命令,它专门负责把本机的端口映射到在目标对象的端口号,经常用于 Kubernetes 的临时调试和测试。

# kubectl port-forward wp-pod 8080:80 &

第四步:创建反向代理的 Nginx

这是因为 WordPress 网站使用了 URL 重定向,直接使用“8080”会导致跳转故障,所以为了让网站正常工作,我们还应该在 Kubernetes 之外启动 Nginx 反向代理,保证外界看到的仍然是“80”端口号。

准备proxy.conf

server {
  listen 80;
  default_type text/html;

  location / {
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_pass http://127.0.0.1:8080;
  }
}

docker启动nginx容器进行反向代理

docker run -d --rm \
    --net=host \
    -v `pwd`/proxy.conf:/etc/nginx/conf.d/default.conf \
    nginx:alpine

结果

声明:本次实验流程linux运行无异常,对Mac或者Windows有异常,需要重新调试。

 docker run -d --rm \                                                                                22-08-03 - 22:38:46
    --net=host \
    -v `pwd`/proxy.conf:/etc/nginx/conf.d/default.conf \
    nginx:alpine
17b6a07e984106390aa25b2d9081a330b515794db8354f1b3bd57e2bcb0752dd

访问http://127.0.0.1

不行了!!

排查

分为几个步骤排查

整体的数据流向图(网络向)

端口转发

访问 http://127.0.0.1:8080,观察是否有动静

如果可以看到页面

说明wordpress → mariadb的链路正常

本机8080端口转发wordpress pod 80端口正常

网络链路的最后两步骤正常

Nginx转发

接下来排查Nginx转发是否正常

这个步骤分为两个部分

第一部分,查看配置

# docker exec -it 17 sh    
                                                                    :( 255 22-08-03 - 23:04:45
# cat /etc/nginx/conf.d/default.conf
server {
  listen 80;
  default_type text/html;

  location / {
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_pass http://127.0.0.1:8080;
  }
}

配置文件和我们的预期都没有太大的差别

接下来我们就要测试网络是否通畅

在容器中curl 我们想要去的地方

结果发现,并没有返回nginx相关的东西

说明:127.0.0.1 并没有访问到宿主机

问题一:在容器中怎么访问到宿主机

按照道理说,我们在启动容器的时候,使用了-net=host的选项,理论上容器和宿主机共用同一个网络,共用同一套端口,为什么127.0.0.1 无法访问到宿主机?

第二部分:查看日志

问题二:为什么主机的80端口无法绑定到容器的nginx上?

查找网络资料发现如下:

host.docker.internal可以从容器上访问宿主机

针对问题一:

修改proxy.conf

server {
  listen 80;
  default_type text/html;

  location / {
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_pass http://host.docker.internal:8080;
  }
}

针对问题二:

修改启动命令

 docker run -d --rm -p 80:80 -v `pwd`/proxy.conf:/etc/nginx/conf.d/default.conf  nginx:alpine        22-08-03 - 23:20:14
0ecc8743db2737080e4854563c133018371dd20d27151a136b64070163fee23a

知识点

docker底层使用原生的系统指令

使用了Cgroup,Namespace,UFS等技术做了隔离。

本质上启动的容器只是一个个被隔离的进程。

这也是docke高效运行的原因

但是在Mac和Window上,使用Desktop 软件

由于不是docker运行的原生环境

本质上这些软件就是一个虚拟机

把linux上的指令翻译成对应平台上的指令

在这个问题中的网络模型中,需要再加一层

我们使用 -net=host启动容器,本质上是绑定了Docker容器和Docker Desktop这个虚拟机共用一个网络和端口环境。而Docker Desktop提供的gateway.docker.internal才是作用宿主机和Docker Desktop网络环境的关键,并不受到-net=host的控制。

所以我们需要使用 -p 来进行端口映射,让Docker Desktop来帮我们处理 宿主机 port1端口到Docker Desktop端口port2上,Docker Desktop端口port2到容器端口port2上,这个两层映射。这个过程对我们使用者来说是透明的,无需关心的。

参考

docs.docker.com/desktop/net…

time.geekbang.org/column/arti…