资讯专栏INFORMATION COLUMN

Docker容器网络之服务名称或别名连接容器服务原理剖析

IT那活儿 / 2632人阅读
Docker容器网络之服务名称或别名连接容器服务原理剖析
点击上方“IT那活儿”公众号,关注后了解更多内容,不管IT什么活儿,干就完了!!! 

1

概  述

Docker 容器启动的时候,使用默认的 bridge 网络是不支持指派固定 IP 的; 

每次Docker 容器重启时,会按照顺序获取对应的IP 地址,这个就导致重启Docker, 容器的IP 地址会发生变化;

虽然可以通过创建自定义网络在启动容器时指定 IP , 但此问题不在本文章中阐述。

本文讲述的是 docker-compose 通过服务名或者别名连接容器服务的原理以及分析,其中主要涉及到 netfilter/iptables 防火墙,evth- pair 技术,DNS 等技术,由于 Docker 容器网络非常之复杂,本文不会对 docker 之外的技术以及 docker 无关本文的其它技术进行过多介绍。

关键字:evth-pair 技术,iptables,netfilter,embedded DNS server,Bridge,命名空间。 

2

Docker五种网络模式介绍

3

veth-pair技术

veth-pair 技术在网桥中的应用:

图1 veth-pair

网桥是一种对帧转发的技术,工作在 OSI 七层模型中的第二层(数据链路层),在数据链路层使用 MAC 地址转发数据。

veth pair 是成对出现的一种虚拟网络设备接口,一端连着网络协议栈,一端彼此相连,如图1;正因为有这个特性,它充当着一个桥梁,连接着各种虚拟网络设备,常被用于构建虚拟网络拓扑。

通过与 Docker Bridge 网络相连,docker0 相当于一台交换机,可以中转 docker。
容器的流量,如图2 所示。
图2 docker0 

4

Docker DNS实现

在早期的容器服务发现使用的是 docker link,通过修改容器内的 /etc/hosts 文件来完成,其地址由 Docker 引擎维护,因此容器间才可以通过别名互访;这种方法存在很多的问题,目前已经过时不再使用。docker 自定义网络里的 dns(embedded DNS server),如图3 所示;
docker 会修改容器里的/etc/resolv.conf 文件,把 dns 服务器设置成 127.0.0.11,因为 127.0.0.0/8 地址都是本机回环地址, 所以 dns 查询的时候实际上是把请求发给了自己。虽然是发给自己,但是还是要走 netfilter 的 nat 表的 output 链里把发往 127.0.0.11:53 的 UDP/TCP 包转到了127.0.0.11:<随机端口>
图 3embedded DNS server
*nat

:PREROUTING ACCEPT [2:168]

:INPUT ACCEPT [2:168]

:OUTPUT ACCEPT [9:620]

:POSTROUTING ACCEPT [17:1091]

:DOCKER_OUTPUT - [0:0]

:DOCKER_POSTROUTING - [0:0]

-A OUTPUT -d 127.0.0.11/32 -j DOCKER_OUTPUT

-A POSTROUTING -d 127.0.0.11/32 -j DOCKER_POSTROUTING

-A DOCKER_OUTPUT -d 127.0.0.11/32 -p tcp -m tcp --dport 53 -j DNAT --to-destination 127.0.0.11:45481

-A DOCKER_OUTPUT -d 127.0.0.11/32 -p udp -m udp --dport 53 -j DNAT --to-destination 127.0.0.11:46340

-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p tcp -m tcp --sport 45481 -j SNAT --to-source :53

-A DOCKER_POSTROUTING -s 127.0.0.11/32 -p udp -m udp --sport 46340 -j SNAT --to-source :53 COMMIT

# Completed on Fri Mar 25 13:15:36 2022
表 1 容器 iptables
开始测试:

composetest_web_1

[root@VM-12-7-centos composetest]#
11 hours ago Up 10 hours 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp
composetest_redis_1

af90b8c4350f composetest_web "flask run"
表 2 docker ps
进入 composetest_redis_1 容器使用 netstat -anp 查看发现是 dockerd 在监听TCP 3971/UDP 46340 端口,等 DNS 请求又回环到filter 表的input 链的时候dockerd 就接到了这个请求;
dockerd 自身处理这些请求,处理不了的要发给宿主机配置的dns 服务器。当端点加入网络时,dockerd 将设置一个 DNS 服务器,只需创建一个套接字并在容器 netns 中监听,这个套接字在主机 netns 中接受,由 dockerd 处理。
[root@VM-12-7-centos composetest]# netstat -pantu

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address
Foreign Address
State
PID/Program name

表 3 查看容器网络连接
[root@VM-12-7-centos composetest]# nc -nvz 127.0.0.11 53 Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.11:53.

Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.
表 4  测试容器 DNS 连通性

表 5 通过 docker 守护进程查找 TCP/UDP 套接字
[root@VM-12-7-centos composetest]# lsof -o 3917|egrep UDP|TCP

[root@VM-12-7-centos composetest]# docker inspect composetest_redis_1|grep Pid "Pid": 17693,
"PidMode": "",

"PidsLimit": null,
表 6 查找容器线程 id
[root@VM-12-7-centos composetest]# ll /proc/17693/ns/net

lrwxrwxrwx 1 polkitd mysql 0 Mar 25 10:07 /proc/17693/ns/net -> net:[4026532226]
表 7 查找容器网络命名空间
[root@VM-12-7-centos composetest]# ln -s /proc/17662/ns/net /var/run/netns/composetest_web_1 [root@VM-12-7-centos composetest]#
[root@VM-12-7-centos composetest]# ll /var/run/netns/composetest_web_1

lrwxrwxrwx 1 root root 18 Mar 25 04:52 /var/run/netns/composetest_web_1 -> /proc/17662/ns/net
表 8 容器命名空间软连接到宿主机命名空间
[root@VM-12-7-centos ~]# ip netns exec composetest_web_1 ping baidu.com PING baidu.com (220.181.38.251) 56(84) bytes of data.
64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=1 ttl=48 time=27.5 ms

64 bytes from 220.181.38.251 (220.181.38.251): icmp_seq=2 ttl=48 time=27.4 ms

^C
--- baidu.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 27.441/27.495/27.549/0.054 ms
表 9 通过别名或者服务名验证容器连通性 1
/ code # ping web

PING web (172.18.0.2): 56 data bytes

64 bytes from 172.18.0.2: seq
=0 ttl=64 time=0.031 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.059 ms
^C

--- web ping statistics ---

2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.031/0.045/0.059 ms
/code #

/ code # ping redis

PING redis (172.18.0.3): 56 data bytes

64 bytes from 172.18.0.3: seq
=0 ttl=64 time=0.071 ms 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.073 ms 64 bytes from 172.18.0.3: seq=2 ttl=64 time=0.076 ms
^C

--- redis ping statistics ---

3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.071/0.073/0.076 ms
/code #
表 10 通过别名或者服务名验证容器连通性 2
实验环境:
[root@VM-12-7-centos ~]# cat /etc/*release CentOS Linux release 7.6.1810 (Core)
[root@VM-12-7-centos ~]# docker --version

Docker version 20.10.14, build a224086
实验配置:
[root@VM-12-7-centos composetest]# pwd

/root/composetest

[root@VM-12-7-centos composetest]#

[root@VM-12-7-centos composetest]# cat requirements.txt flask
redis

[root@VM-12-7-centos composetest]#

[root@VM-12-7-centos composetest]# cat Dockerfile FROM python:3.7-alpine
WORKDIR /code

ENV FLASK_APP app.py

ENV FLASK_RUN_HOST 0.0.0.0

RUN apk add --no-cache gcc musl-dev linux-headers COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt COPY . .
CMD ["flask", "run"]

[root@VM-12-7-centos composetest]# [root@VM-12-7-centos composetest]#
[root@VM-12-7-centos composetest]# cat docker-compose.yml # yaml 配置
version: 3 services:
web:
build: . ports:
- "5000:5000"

redis:

image: "redis:alpine"

[root@VM-12-7-centos composetest]# [root@VM-12-7-centos composetest]# [root@VM-12-7-centos composetest]# cat app.py import time

import redis

from flask import Flask

app = Flask( name )

cache = redis.Redis(host=redis, port=6379)

def get_hit_count(): retries = 5 while True:
try:

return cache.incr(hits)

except redis.exceptions.ConnectionError as exc: if retries == 0:
raise exc retries -= 1 time.sleep(0.5)

@app.route(/) def hello():
count = get_hit_count()

return Hello World! I have been seen {} times. .format(count) [root@VM-12-7-centos composetest]#
[root@VM-12-7-centos composetest]#docker-compose up -d

 


END



 


本文作者:潘 安

本文来源:IT那活儿(上海新炬王翦团队)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/129499.html

相关文章

  • Docker容器间通信方法

    摘要:而本文主要针对容器间的网络通信方法进行讨论。而同属于用户自定义的容器之间自动将所有端口暴露,方便容器间进行无障碍的通信,而不会遭受到外界的意外访问。几天前,为了解决日常在本地进行日常工作和开发测试之间的矛盾,利用docker在Windows系统中搭建了基于Linux的测试环境:借助Docker,在win10下编码,一键在Linux下测试。在这边文章里主要介绍了如何在本地通过docker构建与...

    qianfeng 评论0 收藏0
  • docker-compose命令对比

    摘要:对比如果镜像在本地不存在,将会尝试拉去这个镜像。链接到外部的容器,甚至并非管理的容器。仅可以指定内部端口为参数卷挂载路径设置。使用来暴露服务的容器给其他的。要查看服务可用的环境变量,运行项目 对比 image vs build image:如果镜像在本地不存在,Compose 将会尝试拉去这个镜像。 build:指定 Dockerfile 所在文件夹的路径。 Compose 将会利用...

    libin19890520 评论0 收藏0
  • 记录一次被docker容器通信虐的死去活来的部署路(node + mongoDB + nginx

    摘要:原因是这样的想要部署一个平台,就在朋友的推荐下选择了这个现成的项目该项目分为服务端客户端以及数据库在尝试直接部署的时候发现需要装一大堆的环境,啊,特别的麻烦,之前简单的使用过,就在想能不能用免环境直接部署呢于是就有了这次的尝试多容器通信该 原因是这样的 想要部署一个mocker平台,就在朋友的推荐下选择了api-mocker这个现成的项目 该项目分为服务端node、客户端vue、以及...

    TNFE 评论0 收藏0
  • 记录一次被docker容器通信虐的死去活来的部署路(node + mongoDB + nginx

    摘要:原因是这样的想要部署一个平台,就在朋友的推荐下选择了这个现成的项目该项目分为服务端客户端以及数据库在尝试直接部署的时候发现需要装一大堆的环境,啊,特别的麻烦,之前简单的使用过,就在想能不能用免环境直接部署呢于是就有了这次的尝试多容器通信该 原因是这样的 想要部署一个mocker平台,就在朋友的推荐下选择了api-mocker这个现成的项目 该项目分为服务端node、客户端vue、以及...

    amc 评论0 收藏0
  • 记录一次被docker容器通信虐的死去活来的部署路(node + mongoDB + nginx

    摘要:原因是这样的想要部署一个平台,就在朋友的推荐下选择了这个现成的项目该项目分为服务端客户端以及数据库在尝试直接部署的时候发现需要装一大堆的环境,啊,特别的麻烦,之前简单的使用过,就在想能不能用免环境直接部署呢于是就有了这次的尝试多容器通信该 原因是这样的 想要部署一个mocker平台,就在朋友的推荐下选择了api-mocker这个现成的项目 该项目分为服务端node、客户端vue、以及...

    Rango 评论0 收藏0
  • 从一份配置清单详解Nginx服务器配置

    摘要:在本文中,我们继续延续前文,从前文给出的一份示例配置清单开始,详解一下服务器的各种配置指令的作用和用法。是否允许同时接收多个网络连接指令格式该指令默认为状态,意指每个一次只能接收一个新到达的网络连接。 showImg(https://segmentfault.com/img/remote/1460000015385948); 概述 在前面《Nginx服务器开箱体验》 一文中我们从开箱...

    kevin 评论0 收藏0

发表评论

0条评论

IT那活儿

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<