Docker容器訪問主機網絡——Gitea訪問主機MySQL數據庫


受限於服務器資源限制(輕量化服務的I/O性能和CPU性能都比較低),在使用容器方式搭建 Git 服務(Gitea)時,盡可能避免啟動新的服務。因此,直接使用本地已有的 MariaDB (MySQL) 服務 ,而非通過 Docker 再單獨創建一個 MySQL 容器不再想使用容器版本的 MySQL 服務。Docker 容器訪問主機網絡主要有以下2種:

  1. 將 docker 網絡模式設置為 host,容器將和主機使用同一網絡,所有端口都會暴露,端口映射相關操作將被忽略。
  2. 在容器的 /etc/hosts 中添加 Docker 橋接網卡地址,可以通過 docker 的 --add-host <hostname>:<ip> 或者 docker-compose 的 extra_hosts:\n- "host.docker.internal:host-gateway"

環境

$ docker version
Client: Docker Engine - Community
 Version:           20.10.11
 API version:       1.41
 Go version:        go1.16.9
 Git commit:        dea9396
 Built:             Thu Nov 18 00:37:22 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.8
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.6
  Git commit:       75249d8
  Built:            Fri Jul 30 19:52:10 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.9
  GitCommit:        e25210fe30a0a703442421b0f60afac609f950a3
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

$ docker-compose version
docker-compose version 1.21.0, build unknown
docker-py version: 3.4.1
CPython version: 3.7.3
OpenSSL version: OpenSSL 1.1.1d  10 Sep 2019

問題

默認情況下,Docker 通過橋接網卡進行網絡通信,容器無法直接訪問主機的本地網絡 127.0.0.1

解決方法

方法1:(主機網絡)通過 host 網絡直接訪問主機本地 MySQL 服務

docker-compose 文件中添加 network_mode: host,等同於 docker exec --network host,將容器連接到主機網絡。這種情況下,容器可以直接訪問主機的所有網絡(當然包括 127.0.0.1)。

version: "3"

services:
  server:
    hostname: vm-light
    image: gitea/gitea:1.15.7
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=mysql
      - GITEA__database__HOST=localhost:3306
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
+   network_mode: host
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro

如果想將 Gitea 的綁定到其他端口則需要修改配置文件./gitea/gitea/conf/app.ini

[server]
APP_DATA_PATH    = /data/gitea
DOMAIN           = gitea.io
SSH_DOMAIN       = gitea.io:2222
HTTP_ADDR        = 0.0.0.0
HTTP_PORT        = 3000
ROOT_URL         = https://gitea.io/git/
DISABLE_SSH      = false
SSH_PORT         = 2222
SSH_LISTEN_PORT  = 2222

注意: gitea 1.15.7 似乎存在一個Bug,當將ssh端口設置為 2222 時,程序仍然會嘗試去綁定默認的 22 端口,因此最好將主機的ssh端口設為 2222 或者 2221gitea 使用 22 端口。

方法2:(橋接網絡)在容器HOSTS中添加橋接網卡地址訪問主機 MySQL 服務

該方法需要 Docker 18.03+ 支持。

容器配置

docker-compose 文件中添加 extra_hosts,配置主機與地址的映射 host.docker.internal:host-gateway,其中 host.docker.internal 是主機域名可以隨便設置;host-gateway 表示橋接網卡 docker0 的地址,這里也可以直接寫IP。這樣配置后就可以在容器內容通過 host.docker.internal 直接訪問主機網絡。

注:

  • extra_hostsdocker 中等價於 docker --add-host=host.docker.internal:host-gateway
  • host-gateway 替換為 127.0.0.1 是無效的,127.0.0.1 將指向容器內部。

docker0 的地址為 172.17.0.1,另外兩個 br-xxx 和 分別是容器內部使用的橋接網卡。

$ 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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 52:54:00:04:bd:71 brd ff:ff:ff:ff:ff:ff
    inet 10.0.4.12/22 brd 10.0.7.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe04:bd71/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:fc:f6:10:ec 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
24: br-155e1a817bd3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:4f:9d:10:70 brd ff:ff:ff:ff:ff:ff
    inet 172.24.0.1/16 brd 172.24.255.255 scope global br-155e1a817bd3
       valid_lft forever preferred_lft forever
    inet6 fe80::42:4fff:fe9d:1070/64 scope link
       valid_lft forever preferred_lft forever
32: br-f001f1212e94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:e1:26:f8:45 brd ff:ff:ff:ff:ff:ff
    inet 172.26.0.1/16 brd 172.26.255.255 scope global br-f001f1212e94
       valid_lft forever preferred_lft forever
    inet6 fe80::42:e1ff:fe26:f845/64 scope link
       valid_lft forever preferred_lft forever
42: vetha96076a@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f001f1212e94 state UP group default
    link/ether 52:06:c7:24:42:7b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::5006:c7ff:fe24:427b/64 scope link
       valid_lft forever preferred_lft forever

進入容器內部,可以看到 host.docker.internal 被解析到了 172.17.0.1,並寫入了 hosts 文件中。

$ docker exec -it gitea /bin/bash

# ping -c 4 host.docker.internal
PING host.docker.internal (172.17.0.1): 56 data bytes
64 bytes from 172.17.0.1: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.17.0.1: seq=1 ttl=64 time=0.072 ms
64 bytes from 172.17.0.1: seq=2 ttl=64 time=0.073 ms
64 bytes from 172.17.0.1: seq=3 ttl=64 time=0.075 ms

--- host.docker.internal ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.072/0.073/0.075 ms

# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.26.0.1      0.0.0.0         UG    0      0        0 eth0
172.26.0.0      *               255.255.0.0     U     0      0        0 eth0

# 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.1      host.docker.internal
172.26.0.2      vm-light

完整的YAML配置文件如下所示:

version: "3"

networks:
  gitea:
    external: false

services:
  server:
    hostname: vm-light
    image: gitea/gitea:1.15.7
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=mysql
      - GITEA__database__HOST=host.docker.internal:3306
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
+   extra_hosts:
+     - "host.docker.internal:host-gateway"
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "127.0.0.1:3000:3000"
      - "2222:22"

MySQL 與 iptables 配置

MySQL 綁定地址

經過上面的配置后,我們的 gitea 服務可以通過橋接網卡 docker0 來訪問主機網絡,但默認情況下的MySQL 只監聽 127.0.0.1,由於MySQL不支持設置多個 IP 地址,這里需要修改 /etc/mysql/mariadb.conf.d/50-server.cnf,將監聽地址替換為 0.0.0.0(監聽所有網絡)。

[mysqld]
bind-address            = 0.0.0.0

iptables 規則

在遠程服務器上,為了安全起見,不應將 MySQL 服務暴露在公網上,因此需要通過 iptables 屏蔽來自外網的所有訪問,只允許訪問固定網口。這里通過 iptables 配置訪問控制,只允許應用訪問 127.0.0.1172.17.0.1 兩個網口,其余網口將被禁止訪問。

iptables -A INPUT -p tcp --dport 3306 -d 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -d 172.17.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP

注: 沒有特別注明的情況下,iptables 默認使用 filter 表。

# iptables -L INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             localhost.localdomain  tcp dpt:mysql
ACCEPT     tcp  --  anywhere             172.17.0.1           tcp dpt:mysql
DROP       tcp  --  anywhere             anywhere             tcp dpt:mysql

安裝 iptables-persistent 來保存新增的規則。

# apt install -y iptables-persistent
# iptables-save > /etc/iptables/rules.v4

編輯 rules.v4,刪除其中多余的規則。

# Generated by xtables-save v1.8.2 on Sun Dec 12 18:48:11 2021
*filter
-A INPUT -d 127.0.0.1/32 -p tcp -m tcp --dport 3306 -j ACCEPT
-A INPUT -d 172.17.0.1/32 -p tcp -m tcp --dport 3306 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 3306 -j DROP
COMMIT
# Completed on Sun Dec 12 18:48:11 2021

在重啟后,規則將會自動重建。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM