多网卡模式下Golang应用的流量从指定网卡流入流出方案
文章插图
最近因业务需要 , 需要在多网卡模式下实现Go应用的流量从指定网卡流入 , 请求外网服务时候流量需要从该网卡流出功能 。 从指定网卡流入很容易实现 , 只要go应用listen对应网卡即可 , 但请求外网服务时候就相对麻烦些了 。 在实践中总结出有三种方案可行 。 各有优劣 。
假定服务器网卡情况如下:
文章插图
实际上我们的服务器使用云服务器 , 网卡是弹性网卡(eni) , 绑定的是弹性ip(eip) 。 三种方案对普通服务器也是能达到目的的 。
Go应用示例代码:
package mainimport ( "flag" "fmt" "io/ioutil" "net/http" "strings")var addr = flag.String("addr", ":8080", "the http server address")func init() { flag.Parse()}func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {splits := strings.Split(*addr, ":")localIP := ""if len(splits) == 2 {localIP = splits[0]}res, err := HTTPGet("")if err != nil {panic(err)}defer res.Body.Close()body, err := ioutil.ReadAll(res.Body)if err != nil {panic(err)}fmt.Fprintf(w, "网卡的IP: %s\n\n", localIP)fmt.Fprintf(w, "出口的IP:\n\n %s", body) }) err := http.ListenAndServe(*addr, nil) if err != nil {panic(err) }}// HTTPGet funcfunc HTTPGet(url string) (*http.Response, error) { req, _ := http.NewRequest("GET", url, nil) client := &http.Client{} req.Header.Set("User-Agent", "curl/7.47.0") return client.Do(req)}复制代码
方案一 应用流量出口绑定网卡【多网卡模式下Golang应用的流量从指定网卡流入流出方案】当请求外网服务时候 , 程序需要在每一处http client的地方指定源IP为特定网卡的IP 。 该方案原理简单 , 不依赖外部其他服务 , 缺点就是对应用有侵入性 。
示例程序中HTTPGet方法需要进行如下改动:
// localIP是网卡IPfunc HTTPGet(url, localIP string) (*http.Response, error) { req, _ := http.NewRequest("GET", url, nil) client := &http.Client{Transport: &http.Transport{Dial: func(netw, addr string) (net.Conn, error) {// localIP 网卡IP , ":0" 表示端口自动选择lAddr, err := net.ResolveTCPAddr(netw, localIP+":0")if err != nil {return nil, err}rAddr, err := net.ResolveTCPAddr(netw, addr)if err != nil {return nil, err}conn, err := net.DialTCP(netw, lAddr, rAddr)if err != nil {return nil, err}return conn, nil},}, } return client.Do(req)}复制代码
运行程序时候分别要监听指定网卡的ip:
go run main.go --addr=172.31.0.8:8090go run main.go --addr=172.31.0.14:8090复制代码
这样当我们访问109.25.48.65:8090时候 , 流量从eth0流入到go应用 , 当go请求haoip.cn地址 , 使用eth0这个网卡流出的 。 访问119.26.38.75:8090时候 , 流出从eth1流入go应用 , 当go请求haoip.cn地址 , 流量从eth1这个网卡流出 。
方案二 基于Docker容器技术Docker容器基于namespace实现网络、进程、挂载等资源隔离功能 。 如果将go应用打包成镜像 , 绑定指定网卡 , 以容器形式运行不就可以实现流量流出控制了 。
创建Dockerfile , 并写入以下内容:
FROM golang:1.14LABEL maintainer="tink tink@example.com"WORKDIR /appCOPY . .RUN go build -o main .EXPOSE 8080CMD ["./main"]复制代码
Dockerfile文件和go应用示例代码放在同一个目录下 , 然后执行以下命令构成go应用的镜像:
docker build -t eip .复制代码
创建docker bridge网络我们创建一个专门用于go应用的bridge网络: eip_bridge 。 子网范围是172.19.0.0/16
docker network create --subnet=172.19.0.0/16 --opt "com.docker.network.bridge.name"="eip_bridge"eip_bridge复制代码
运行go应用容器// 绑定网卡eth0docker run --network=eip_bridge -p 172.31.0.8:8090:8080 --ip=172.19.0.100 -d eip:latest// 绑定网卡eth1docker run --network=eip_bridge -p 172.31.0.14:8090:8080--ip=172.18.0.101 -d eip:latest复制代码
上面命令说明:
- -p 172.31.0.8:8090:8090 给该容器端口映射 , 主机端口8090映射到容器端口8080 , 由于指定ip为172.31.0.8 , 这样我们就可以109.25.48.65:8090访问这个容器了 。
- --ip=172.19.0.100 。 为该容器指定一个固定ip
这时候我们可以通过SNAT技术 , 将容器的源地址分别改成对应绑定的网卡地址就可以了 。
sudo iptables -t nat -I POSTROUTING -p all -s 172.19.0.100 -j SNAT --to-source 172.31.0.8sudo iptables -t nat -I POSTROUTING -p all -s 172.19.0.101 -j SNAT --to-source 172.31.0.14复制代码
- GB|备货充足要多少有多少,5000mAh+128GB,红米新机首销快速现货
- 占营收|华为值多少钱
- 查询|数据太多容易搞混?掌握这几个Excel小技巧,办公思路更清晰
- 商品|问道自有品牌,山姆多方博弈
- 抖音小店|抖音进军电商,短视频的商业模式与变现,创业者该如何抓住机遇?
- 垫底|5G用户突破2亿:联通垫底,电信月增700万,中国移动有多少?
- 色卡|双人场景/多机位色彩匹配,色卡很重要
- 砍单|iPhone12之后,拼多多又将iPhone12Pro拉下水
- 打响|拼多多打响双12首枪,iPhone12降到“mini价”,苹果11再见
- 不到|苹果赚了多少?iPhone12成本不到2500元,华为和小米的利润呢?