受限於服務器資源限制(輕量化服務的I/O性能和CPU性能都比較低),在使用容器方式搭建 Git 服務(Gitea)時,盡可能避免啟動新的服務。因此,直接使用本地已有的 MariaDB (MySQL) 服務 ,而非通過 Docker 再單獨創建一個 MySQL 容器不再想使用容器版本的 MySQL 服務。Docker 容器訪問主機網絡主要有以下2種:
- 將 docker 網絡模式設置為 host,容器將和主機使用同一網絡,所有端口都會暴露,端口映射相關操作將被忽略。
- 在容器的
/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
或者 2221
,gitea
使用 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_hosts
在docker
中等價於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.1
和 172.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
在重啟后,規則將會自動重建。