背景
我们公有云的资源通常是很宝贵的,有时候申请的资源不足以运行大规模应用,本文描述了一种利用frp进行内网穿透,通过ingress整合访问路径的方法。全部模块通过K8S部署,易于测试和拓展。
开始按照本方案部署前,请确认您在本地和远程都成功部署了K8S集群,且远程K8S集群已经部署了Ingress控制器。
总体方案介绍
设计思路
假设我们拥有my.domain.com这个域名,我们可以在公有云的Ingress中按照以下规则设计:
- my.domain.com/local:指向本地K8S集群中的Web应用(通常意味着较多的计算和存储资源)
- my.domain.com/和其他路径:指向远程K8S集群中的Web应用(因为公有云资源有限,只能运行轻量应用)
部署架构
系统的部署架构如下所示:
在上述架构中,在远程K8S集群中部署frps容器,frps容器开放两个端口:
- bind_port:7000端口,监听来自本地K8S集群的注册请求
- vhost_http_port:8080端口,承载远程K8S集群对本地K8S集群的Web访问
在本地K8S集群中部署frpc容器,负责按照url规则将访问请求派发到本地集群的web服务器中。
远程K8S集群中部署Ingress和frps
配置的内容包括:
- 创建frps容器,并分别为8080端口和7000端口创建Service,其中7000端口的服务类型为NodePort,使用节点服务器的32890端口;
- 在Ingress中配置/local路径,使其指向8080端口的服务;
frps pod和service配置为:
apiVersion: v1
kind: ConfigMap
metadata:
name: frps-conf
namespace: <namespace>
labels:
app: frps
data:
frps.ini: |-
[common]
bind_port = 7000
token = <token>
vhost_http_port = 8080
vhost_https_port = 8443
max_pool_count = 5
log_file = /var/log/frps/frps.log
log_level = info
log_max_days = 7
---
kind: Service
apiVersion: v1
metadata:
labels:
app: frps
name: frps
namespace: <namespace>
spec:
type: NodePort
ports:
- port: 32890
targetPort: 7000
name: frps
nodePort: 32890
selector:
app: frps
---
kind: Service
apiVersion: v1
metadata:
labels:
app: frps
name: frps-web
namespace: <namespace>
spec:
ports:
- port: 8080
targetPort: 8080
name: http
- port: 8443
targetPort: 8443
name: https
selector:
app: frps
---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: frps
name: frps
namespace: <namespace>
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: frps
template:
metadata:
labels:
app: frps
spec:
containers:
- name: frps
image: snowdreamtech/frps:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 7000
protocol: TCP
volumeMounts:
- name: frps-conf
mountPath: /etc/frp
volumes:
- name: frps-conf
configMap:
name: frps-conf
items:
- key: frps.ini
path: frps.ini
Ingress配置为:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
generation: 1
name: sharework
namespace: <namespace>
spec:
ingressClassName: nginx
tls:
- hosts:
- <your-domain>
secretName: <tls-secret>
rules:
- host: <your-domain>
http:
paths:
- path: /local
pathType: Prefix
backend:
service:
name: frps-web
port:
number: 8080
---
请注意这里frps pod的<namespace>
和Ingress所在的命名空间相同,如果不同,则Ingress指向的Backend Service应该改成external service。
另外,本Ingress是TLS加密的,其使用了tls-secret这个证书secret。如果不想使用证书,也可以将tls这一段声明去掉。
附:创建tls secret的命令为:
kubectl create secret tls tls-secret --key privkey.pem --cert fullchain.pem
本地K8S集群中部署frpc和测试web服务
本地K8S集群部署了frpc容器,并通过此容器将本地集群的Web服务合并到远程Ingress下。相关配置要点如下:
- frpc进行站点匹配时,包含了url的全部域名和路径。比如,Ingress中使用/local指向本地K8S集群的服务,但是frpc的匹配路径locations中还是要包含/local这个部分;
- frpc的local_ip取巧直接使用了服务名,这是因为在集群内部,访问服务名相当于访问http://svcname.namespace.svc.cluster.local(FQDN,全限定域名)。经测试是可行的,如果sample-web和frpc不在同一命名空间,应当填写FQDN;
相关配置如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: frpc-configurations
labels:
module: frpc
app: frpc
data:
frpc-conf: |-
[common]
server_addr = <frps server ip>
server_port = 32890
token = <token>
[test-web]
type = http
custom_domains = <your domain>
locations = /local/sample
local_ip = sample-web
local_port = 80
---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
module: frpc
app: frpc
name: frpc
spec:
replicas: 1
selector:
matchLabels:
app: frpc
template:
metadata:
labels:
module: frpc
app: frpc
spec:
containers:
- name: frpc
image: snowdreamtech/frpc:0.42.0
imagePullPolicy: IfNotPresent
volumeMounts:
- name: frpc-conf
mountPath: /etc/frp
readOnly: true
volumes:
- name: frpc-conf
configMap:
name: frpc-configurations
items:
- key: frpc-conf
path: frpc.ini
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-configurations
labels:
module: frpc
app: sample-web
data:
nginx-conf: |-
server {
listen 80;
server_name localhost <your domain>;
location /local/sample {
alias /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
---
kind: Service
apiVersion: v1
metadata:
labels:
module: frpc
app: sample-web
name: sample-web
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: sample-web
---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
module: frpc
app: sample-web
name: sample-web
spec:
replicas: 1
selector:
matchLabels:
app: sample-web
template:
metadata:
labels:
module: frpc
app: sample-web
spec:
containers:
- name: web
image: nginx:alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d/
readOnly: true
volumes:
- name: nginx-conf
configMap:
name: nginx-configurations
items:
- key: nginx-conf
path: default.conf
部署成功后,即可以通过https://your.domain/local/sample访问本地sample-web的内容。