Docker之八----容器之間互聯與網絡模式詳解

容器之間的互聯

1、容器互聯介紹

即在同一個宿主機上的容器之間可以通過自定義的容器名稱相互訪問 ,比如一個業務前端靜態頁面是使用nginx,動態頁面使用的是tomcat由於容器在啓動的時候其內部IP地址是DHCP隨機分配的,所以如果通過內部訪問的話,自定義名稱是相對比較固定的,因此比較適用於此場景。

2、部署容器互聯

1、先創建第一個tomcat容器,後續需要將nginx進行轉發到此tomcat服務上,tomcat測試頁面在Docker之七已經創建,詳情請見上一章!!!

[root@centos-7 ~]# docker run -it -d  --name tomcat1 tomcat-app1:v1
dd097a9fd8baaa2da451c279bd9a008c3b7ec36c6cf4a032aa40e697aec549f7

進入到tomcat服務裏,查看tomcat的hosts文件

[root@centos-7 ~]# docker exec -it tomcat1 bash
[root@dd097a9fd8ba /]# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.3	dd097a9fd8ba  #tomcat的hosts文件解析
1.2.1.3 www.google.net

2、開啓一個nginx容器,並將tomcat容器關聯起來,啓動nginx服務。

[root@centos-7 ~]# docker run -it -d --name nginx-web1 -p 80:80  --link tomcat1 nginx:v1
24549e8fac8fade868004fe6a5c0bc25f04abc08e82f1acffc8c1fcb47e6df65

3、進入nginx服務,然後看nginx的hosts文件

[root@centos-7 ~]# docker exec -it nginx-web2 bash
[root@1e2556622e7b /]# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.3	tomcat1 dd097a9fd8ba  #可以看到已經將tomcat的host文件關聯過來
172.17.0.2	1e2556622e7b

 4、進入到nginx配置服務器,然後修改nginx的配置文件,進行轉發的tomcat容器部分

[root@centos-7 ~]# docker exec -it nginx-web2 bash
[root@f53f3ba8b1ad /]# vim /etc/nginx/nginx.conf  #修改nginx服務
    upstream tomcat  {   #http段進行修改
        server tomcat:8080; #轉發到tomcat服務的IP地址和端口,直接轉發域名,關聯的tomcat服務器的名稱不變,就會一直轉發到tomcat服務器上
    }

 
        location /app1 {    # server段進行修改
        proxy_pass http://tomcat;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-FOR $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        }

[root@f53f3ba8b1ad /]# nginx -t  # 測試語法
[root@f53f3ba8b1ad /]# nginx -s reload  #重新加載nginx

upstream段轉發hosts域名,link鏈接tomcat的名稱,就算tomcat容器刪除,IP地址變化之後也沒事,只需要重新進行link到同一個名稱即可,這樣就不需要修改nginx服務器內部配置文件,依然可以訪問頁面。 

此時訪問宿主機的IP地址下的app1目錄,就會訪問到網頁:

  

定義別名:

上一步驟中,自定義的容器名稱可能後期會發生變化,那麼一旦名稱發生變化,程序之間也要隨之發生變化,比如程序通過容器名稱進行服務調用,但是容器名稱發生變化之後再使用之前的名稱肯定是無法成功調用,每次都進行更

改的話又比較麻煩, 因此可以使用自定義別名的方式解決,即容器名稱可以隨意更改,只要不更改別名即可,具體如下:

命令格式:

docker run -d -name 新容器名稱 --link 目標容器名稱:自定義的名稱 -p 本地端口:容器端口 鏡像名稱 shell 命令

1、重新創建一個tomcat容器,並啓動。

[root@centos-7 src]# docker run -it -d --name tomcat2 tomcat-app1:v1

2、將nginx容器與tomcat容器關聯,並起一個容器別名,別名爲tomcat.google.com

[root@centos-7 ~]# docker run -it -d --name nginx-web3 -p 82:80  --link tomcat2:tomcat.google.com nginx:v1

3、進入nginx容器內,修改nginx配置文件

[root@centos-7 ~]# docker exec -it nginx-web4 bash
[root@faf64ae3e99b /]# vim /etc/nginx/nginx.conf       
     location /app1 {
           proxy_pass http://tomcat.google.com:8080; #直接監聽別名解析
        }

[root@faf64ae3e99b /]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@faf64ae3e99b /]# nginx -s reload

測試頁面,此時容器啓動nginx監聽的是82端口,因此需要82端口進行訪問:

 

網絡模式

通過網絡誇宿主機互聯:

同一個宿主機之間的各容器之間是可以直接通信的,但是如果訪問到另外一臺宿主機的容器呢?

下面我來介紹一下docker網絡模式:

用命令直接查看網絡模式:bridge、host、none、container四種模式。

1、Host網絡模式

Host模式,使用參數--net=host指定,Host模式不支持端口映射,且容器無法啓動。

啓動的容器如果指定了使用host模式,那麼新創建的容器不會創建自己的虛擬網卡(Network Namespace),而是直接使用宿主機的網卡和IP地址(Network Namespace),因此在容器裏面查看到的IP信息就是宿主機的信息,訪問容器的時候直接使用宿主機IP+容器端口即可,不過容

器的其他資源們必須文件系統、系統進程等還是和宿主機保持隔離。此模式的網絡性能最高,但是各容器之間端口不能相同,適用於運行容器端口比較固定的業務。

演示Host模式

[root@centos-7 ~]# docker run -it -d  --name nginx-net-host --net=host nginx:v1  # 打開指定host模式的容器
ac4e0be49a668d19935d9158e7e6a1c81c7afeac0d3dde88349979ab26e311dc
[root@centos-7 ~]# docker exec -it nginx-net-host bash # 進入到容器中
[root@centos-7 /]# ip a  #查看此host容器模式的IP地址,就是使用的是宿主機的IP地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:59:4a:1d brd ff:ff:ff:ff:ff:ff
    inet 192.168.7.100/24 brd 192.168.7.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe59:4a1d/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:a6:2c:bb:07 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:a6ff:fe2c:bb07/64 scope link 
       valid_lft forever preferred_lft forever

2、None網絡模式

 使用none模式,Docker容器擁有自己的Network Namespace,但是,並不爲Docker容器進行任何網絡配置。也就是說,這個Docker容器沒有網卡、IP、路由等信息,只有lo 網絡接口。需要我們自己爲Docker容器添加網卡、配置IP等。

 不參與網絡通信,運行於此類容器中的進程僅能訪問本地迴環接口;僅適用於進程無須網絡通信的場景中,例如:備份、進程診斷及各種離線任務等,此模式極少使用。

演示None模式

[root@centos-7 ~]# docker run -it -d --name nginx-net-host1 --net=none nginx:v1  # 創建none網絡模式
dd00a68d3853602902aae3b08f57c06827b649f764f951a60bc7e4c2298a0ea4
[root@centos-7 ~]# docker exec -it nginx-net-host1 bash
[root@dd00a68d3853 /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
[root@dd00a68d3853 /]# ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

3、Container網絡模式  

Container模式介紹

這個模式指定新創建的容器和已經存在的一個容器共享一個 Network Namespace,而不是和宿主機共享。新創建的容器不會創建自己的網卡,配置自己的 IP,而是和一個指定的容器共享 IP、端口範圍等。同樣,兩個容器除了網絡方面,其他的如文件系統、進程列表等還是隔離的。兩個容器的進程可以通過 lo 網卡設備通信。

網絡示意圖:

 

 (1)在一個終端,使用bridge網絡模式啓動容器tomcat1

[root@centos-7 ~]# docker run -it --name tomcat1 --net=bridge  -d tomcat-app1:v1
10753fcfcd45b06eac55badca2166dcca5c76e26260d99bfa945f68ae8a50454
[root@centos-7 ~]# docker exec -it tomcat1 bash
[root@10753fcfcd45 /]# ip a  #查看此時的IP地址是172.17.0.2
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

(2)在另一個終端使用Container 網絡模式創建容器tomcat2 

[root@centos-7 ~]# docker run -it -d -name tomcat2 --net=container:tomcat1 tomcat-app1:v1  # 創建tomcat2容器
[root@centos-7 ~]# docker exec -it tomcat2 bash  #進入到tomcat2
[root@10753fcfcd45 /]# ip a   # 查看IP地址,此時的IP地址就和橋接模式的IP地址一致
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

4、Bridge 網絡模式

 介紹

  當Docker進程啓動時,會在主機上創建一個名爲docker0的虛擬網橋,此主機上啓動的Docker容器會連接到這個虛擬網橋上,所以有默認地址172.17.0.0/16的地址。虛擬網橋的工作方式和物理交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網絡中。

  從docker0子網中分配一個IP給容器使用,並設置docker0的IP地址爲容器的默認網關。在主機上創建一對虛擬網卡veth pair設備,Docker將veth pair設備的一端放在新創建的容器中,並命名爲eth0(容器的網卡),另一端放在主機中,以vethxxx這樣類似的名字命名,並將這個網絡設備加入到docker0網橋中。可以通過brctl show命令查看。

 

[root@centos-7 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242a62cbb07	no		veth4f25042

bridge模式是docker的默認網絡模式,不寫--net參數,就是bridge模式。使用docker run -p時,docker實際是在iptables做了DNAT規則,實現端口轉發功能。可以使用iptables -t nat -vnL查看。

[root@centos-7 ~]# iptables -t nat -vnL
Chain PREROUTING (policy ACCEPT 31 packets, 5027 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   31  1588 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 31 packets, 5027 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 69 packets, 4024 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DOCKER     all  --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT 69 packets, 4024 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  233 14471 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0

Docker跨主機(不同網段)互聯之簡單實現 

環境準備 

A主機:10.20.0.1/16

B主機:10.10.0.1/16

在A主機修改Docker0地址

1、修改A主機的docker配置文件,指定docker容器的IP地址

[root@openstack ~]# vim /lib/systemd/system/docker.service 
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock  --bip 10.20.0.1/16

[root@openstack ~]# systemctl daemon-reload
[root@openstack ~]# systemctl restart docker

2、查看此時修改後的docker0的IP地址,此時就是修改後的IP地址

 

 在B主機修改Docker0地址

 1、修改B主機的docker地址,指定一個地址10.10.0.1/16

[root@centos-7 ~]# vim  /lib/systemd/system/docker.service 
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock  --insecure-registry 192.168.7.103 --insecure-registry 192.168.7
.102    --bip=10.10.0.1/16

[root@centos-7 ~]# systemctl daemon-reload
[root@centos-7 ~]# systemctl restart docker

2、查看修改後的IP地址

  

 在兩個宿主機分別開啓一個容器

1、A主機開啓一個容器

[root@centos-7 ~]# docker run -it -d --name nginx-web1 nginx:v1
7a77bc53509d6e14f25b5a217e1b47ccf8af61ac7e6011786512131c6803a16f

2、B主機開啓一個容器

[root@openstack ~]# docker run -it -d --name nginx-web2 goharbor/harbor-db:v1.7.5

添加靜態路由 

1、在A主機添加B主機的IP地址段和B主機的宿主機IP地址,並添加一個防火牆規則

[root@centos-7 src]# route add -net 10.20.0.0/16 gw 192.168.7.102
[root@centos-7 src]# iptables  -A FORWARD  -s 192.168.7.0/21  -j ACCEPT  #允許此IP地址段的都可以訪問

2、在B主機添加A主機的IP地址段和A主機的宿主機IP地址,並添加防火牆規則

[root@centos-7 src]# route add -net 10.10.0.0/16 gw 192.168.7.102
[root@centos-7 src]# iptables  -A FORWARD  -s 192.168.7.0/21  -j ACCEPT  #允許此IP地址段的都可以訪問

3、進入A主機開啓的容器內部,然後ping測試B主機是否能夠ping通

[root@centos-7 ~]# docker exec -it nginx-web1 bash

[root@7a77bc53509d /]# ping 10.10.0.1
PING 10.10.0.1 (10.10.0.1) 56(84) bytes of data.
64 bytes from 10.10.0.1: icmp_seq=1 ttl=64 time=0.078 ms
64 bytes from 10.10.0.1: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 10.10.0.1: icmp_seq=3 ttl=64 time=0.050 ms
64 bytes from 10.10.0.1: icmp_seq=4 ttl=64 time=0.049 ms

4、此時B主機ping A主機也可以通

[root@openstack ~]# docker exec -it 8b86881da848  bash
root [ / ]# ping 10.20.0.1
PING 10.10.0.2 (10.10.0.2) 56(84) bytes of data.
64 bytes from 10.10.0.2: icmp_seq=1 ttl=62 time=0.700 ms
64 bytes from 10.10.0.2: icmp_seq=2 ttl=62 time=0.485 ms
64 bytes from 10.10.0.2: icmp_seq=3 ttl=62 time=0.465 ms

實現自定義網絡

1、創建一個網絡名稱

[root@centos-7 ~]# docker network create -d bridge  --subnet 172.27.0.0/21  --gateway 172.27.0.1 linux36-net

 查看創建成功的網絡信息,此時可以看到linux36-net就是自己創建的網絡

[root@centos-7 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f2926808018e        bridge              bridge              local
7706b813f5bf        host                host                local
4e9a106f0e22        linux36-net         bridge              local
498b37d89d4e        none                null                local

2、運行一個centos鏡像,此時就可以看到一個自定義的網絡分配給鏡像一個IP地址

[root@centos-7 ~]# docker run -it --network linux36-net centos bash  # 使用自己創建的網絡IP地址段
[root@8409e98a804d /]# ip a  # 拿到的IP地址是172.27.0.2/21
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
55: eth0@if56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:1b:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.27.0.2/21 brd 172.27.7.255 scope global eth0
       valid_lft forever preferred_lft forever

3、此時就可以ping通外網的地址

[root@8409e98a804d /]# ping www.baidu.com
PING www.a.shifen.com (112.80.248.75) 56(84) bytes of data.
64 bytes from 112.80.248.75 (112.80.248.75): icmp_seq=1 ttl=127 time=32.6 ms
64 bytes from 112.80.248.75 (112.80.248.75): icmp_seq=2 ttl=127 time=50.3 ms

4、此時再啓動一個容器A,查看最新的容器IP地址,由於和上面創建的容器,在一個地址段,因此也可以進行通信

[root@centos-7 src]# docker run -it -p 80:80 --network linux36-net nginx:v1 bash
[root@b3afcaea2c02 /]# ip a  #查看新建的容器的IP地址是172.27.0.3,在同一個網段。
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
61: eth0@if62: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:1b:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.27.0.3/21 brd 172.27.7.255 scope global eth0
       valid_lft forever preferred_lft forever

5、爲了將兩個跨主機且不在同一網段的宿主機進行通信

[root@centos-7 src]# iptables-save > iptables-rule.txt  #將iptables規則導出,然後修改此配置文件,註釋掉以下兩行
#-A DOCKER-ISOLATION-STAGE-2 -o br-4e9a106f0e22 -j DROP
#-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP

[root@centos-7 src]# iptables-restore  < iptables-rule.txt  # 再將iptables規則再導回來

6、然後開啓個橋接模式的容器B

[root@centos-7 src]# docker run -d  -it -p 81:80 --network bridge  nginx:v1  #啓動一個橋接模式的容器
0274bd84109ae248d74af702b76d69227f3ccc9b45948d5595ae9fb32fe5439a
[root@centos-7 src]# docker exec -it 0274bd84109ae248d74af702b76d69227f3ccc9b45948d5595ae9fb32fe5439a bash
[root@0274bd84109a /]# ip a  # 獲取的IP地址是10.10.0.3/16
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
65: eth0@if66: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:0a:0a:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.10.0.3/16 brd 10.10.255.255 scope global eth0
       valid_lft forever preferred_lft forever

7、然後進入容器B中進行ping容器A的IP地址,此時就會基於iptables規則進行跨網段ping通

[root@centos-7 src]# docker run -d  -it -p 81:80 --network bridge  nginx:v1 
0274bd84109ae248d74af702b76d69227f3ccc9b45948d5595ae9fb32fe5439a
[root@centos-7 src]# docker exec -it 0274bd84109ae248d74af702b76d69227f3ccc9b45948d5595ae9fb32fe5439a bash  # 進入到B容器中
[root@0274bd84109a /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
65: eth0@if66: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:0a:0a:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.10.0.3/16 brd 10.10.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@0274bd84109a /]# ping 172.27.0.3  #ping容器A的IP地址,此時可以ping通
PING 172.27.0.3 (172.27.0.3) 56(84) bytes of data.
64 bytes from 172.27.0.3: icmp_seq=1 ttl=63 time=0.335 ms
64 bytes from 172.27.0.3: icmp_seq=2 ttl=63 time=0.069 ms
64 bytes from 172.27.0.3: icmp_seq=3 ttl=63 time=0.070 ms
^C
--- 172.27.0.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.069/0.158/0.335/0.125 ms

  

  

  

  

  

  

 

  

  

  

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章