修炼者
修炼者
发布于 2025-07-04 / 42 阅读
0
0

让局域网节点服务于Internet用户

背景

一些场景下,基于灵活性与成本的原则,我们需要局域网服务节点能够直接服务于Internet用户。即可以让用户能够通过云服务节点直接访问局域网服务节点,如下图:

具体的场景有:

  1. 局域网成本有优势的时候:比如有大量存储和算力需求的时候。特别是非实时的批量业务,可以将一部分服务节点部署在本地局域网,通过与云服务节点的配合,做到成本最优。

  2. 开发阶段需要暴露接口给第三方:例如调试微信支付的时候,支付状态的通知消息只能发送到一个Internet服务节点上,但是本阶段服务还在局域网的开发环境下。

SSH隧道

有多种方法可以做到这一点,这里介绍的是SSH隧道方式,一般linux类服务都会安装ssh服务,因此具有较广泛的通用性。

SSH能力介绍

SSH除了可以登陆服务器管理之外,还可以用来建立服务节点之间的端口互通隧道。

将本地端口转发到远程端口

ssh -N -L <本地IP>:<本地端口>:<目标主机>:<目标端口> <用户名>@<SSH服务器>

# 示例:将远程 MySQL(3306)映射到本地的 3307 端口
ssh -N -L 3307:localhost:3306 user@remote-server

# 访问 localhost:3307 即等同于访问 remote-server 的 3306 端口。

将本地端口暴露到远程端口

ssh -N -R <远程端口>:<目标主机>:<目标端口> <用户名>@<SSH服务器>

# 示例:将本地的 8080 端口暴露到远程服务器的 8888 端口
ssh -N -R 8888:localhost:8080 user@remote-server

# 在 remote-server 上访问 localhost:8888 即访问你本地的 8080 服务。

启动socks代理

启动后,可以配置浏览器使用本地的socks代理,就可以借助remote-server的连通性。

ssh -N -D <本地端口> <用户名>@<SSH服务器>

# 示例:在本地 1080 端口启动 SOCKS5 代理
ssh -N -D 1080 user@remote-server

#配置浏览器或应用使用 localhost:1080 作为代理。

准备

这里定义好目标,本地开发微信支付应用,通过云服务节点接受支付状态通知。

确认云服务节点可通过SSH访问,有相关用户、口令或密钥信息;规划好端口映射关系,例如20080-80等;明确域名指向云服务器节点,并通过端口映射或者nginx代理转发url等。如下:

云服务节点

局域网服务节点

SSH账户与口令

www.aigrow.space

root/20250701

127.0.0.1:80

ubuntu/yyyymmdd

隧道端口映射

127.0.0.1:20080

80

域名与URL

https://www.aigrow.space/dev/

http://www.aigrow.space/

Nginx代理

将域名的特定子目录dev转发到隧道

去掉字目录,再次代理转发到本地相应服务

局域网服务节点

Java Spring boot 服务

这里使用了Java Spring boot开发了一个微信支付服务,在本地运行起来,服务端口为8080.

$ gradle bootRun
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :bootRun
09:51:23.608 [Thread-0] DEBUG org.springframework.boot.devtools.restart.classloader.RestartClassLoader - Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@3120e283

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.18)

2025-07-05 09:51:23.844  INFO 3046228 --- [  restartedMain] bdm.payment.BdmPaymentApplication        : Starting BdmPaymentApplication using Java 11.0.27 on gaoyong-HP-Elite-Dragonfly-G2-Notebook-PC with PID 3046228 (/media/gaoyong/FE3AACC83AAC7F71/work/tgit/mysoft.space/code/bdm_payment/build/classes/java/main started by gaoyong in /media/gaoyong/FE3AACC83AAC7F71/work/tgit/mysoft.space/code/bdm_payment)
2025-07-05 09:51:23.844 DEBUG 3046228 --- [  restartedMain] bdm.payment.BdmPaymentApplication        : Running with Spring Boot v2.7.18, Spring v5.3.31
2025-07-05 09:51:23.845  INFO 3046228 --- [  restartedMain] bdm.payment.BdmPaymentApplication        : No active profile set, falling back to 1 default profile: "default"
2025-07-05 09:51:23.873  INFO 3046228 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2025-07-05 09:51:23.874  INFO 3046228 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2025-07-05 09:51:24.198  INFO 3046228 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2025-07-05 09:51:24.222  INFO 3046228 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 19 ms. Found 1 JPA repository interfaces.
2025-07-05 09:51:24.559  INFO 3046228 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2025-07-05 09:51:24.565  INFO 3046228 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-07-05 09:51:24.565  INFO 3046228 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.83]
2025-07-05 09:51:24.600  INFO 3046228 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-07-05 09:51:24.600  INFO 3046228 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 726 ms
2025-07-05 09:51:24.671  INFO 3046228 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2025-07-05 09:51:24.694  INFO 3046228 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.6.15.Final
2025-07-05 09:51:24.755  INFO 3046228 --- [  restartedMain] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2025-07-05 09:51:24.790  INFO 3046228 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2025-07-05 09:51:24.856  INFO 3046228 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2025-07-05 09:51:24.863  INFO 3046228 --- [  restartedMain] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2025-07-05 09:51:25.115  INFO 3046228 --- [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2025-07-05 09:51:25.119  INFO 3046228 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2025-07-05 09:51:25.269  WARN 3046228 --- [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2025-07-05 09:51:25.320 DEBUG 3046228 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2025-07-05 09:51:25.331  INFO 3046228 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2025-07-05 09:51:25.351 DEBUG 3046228 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : 9 mappings in 'requestMappingHandlerMapping'
2025-07-05 09:51:25.361 DEBUG 3046228 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2025-07-05 09:51:25.366 DEBUG 3046228 --- [  restartedMain] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2025-07-05 09:51:25.412  INFO 3046228 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2025-07-05 09:51:25.428  INFO 3046228 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2025-07-05 09:51:25.434  INFO 3046228 --- [  restartedMain] bdm.payment.BdmPaymentApplication        : Started BdmPaymentApplication in 1.82 seconds (JVM running for 2.026)
<==========---> 80% EXECUTING [1m 19s]
> :bootRun

nginx

通过本地nginx做一个代理,将域名www.my-soft.net.cn的wepay子目录代理到8080.

$ cat www.aigrow.space.conf 
server
{
    listen 80;

    server_name www.aigrow.space;

	location / {
		proxy_pass http://192.168.99.44:8080;
		proxy_redirect off;
        proxy_set_header Host  $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	}
}

$ nginx -s reload

# 验证配置有效性 :  如下指令返回结果应该一致
$ curl 192.168.99.44:8080
$ curl 127.0.0.1 -H "Host: www.aigrow.space"

autossh

可以保持隧道的持续性,中断自动重联。

sudo apt install autossh

通过autossh将80号端口投射到云服务器20080号端口

autossh -M 0 -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
    -o "ExitOnForwardFailure=yes" \
      -NTR  20080:127.0.0.1:80 ubuntu@www.aigrow.space

systemd

如果局域网服务节点重启,需要自动拉起,那么就应该将这里的隧道纳入systemctl管理。如下:

  1. /etc/systemd/system目录,创建autossh-aigrow-dev.service文件

[Unit]
Description=AutoSSH Tunnel for Remote SSH Access
After=network-online.target
[Service]
# 使用您的本地用户名(不要用root),需要确保用户存在,可用id ubuntu查看
User=ubuntu
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 \
  -o "ServerAliveInterval 30" \
  -o "ServerAliveCountMax 3" \
  -o "ExitOnForwardFailure=yes" \
  -NTR 20022:127.0.0.1:22 \
  -NTR 20080:127.0.0.1:80 \
  ubuntu@oss_aigrow
# 自动重启配置
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
  1. 通过systemctl来管理服务,并通过

sudo systemctl daemon-reload
sudo systemctl enable autossh-aigrow-dev.service  # 启用开机自启
sudo systemctl start autossh-aigrow-dev.service   # 立即启动
systemctl status autossh-aigrow-dev.service

journalctl -u autossh-aigrow-dev.service -f  # 查看实时日志

云服务节点

nginx

在www.aigrow.space的配置文件中增加如下地址代理,然后更新nginx配置。

    location /dev/{

		proxy_pass http://127.0.0.1:20080/;
		proxy_redirect off;     
		proxy_set_header Host  $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	}

查看隧道状态

$ sudo lsof -i :20080
COMMAND     PID   USER   FD   TYPE     DEVICE SIZE/OFF NODE NAME
sshd    1039952 ubuntu    7u  IPv6 1422654863      0t0  TCP ip6-localhost:20080 (LISTEN)
sshd    1039952 ubuntu    9u  IPv4 1422654864      0t0  TCP localhost:20080 (LISTEN)

# 或者
$ ss -tulnp | grep 20080
tcp   LISTEN 0      128                         127.0.0.1:20080      0.0.0.0:*                                                                                                                         
tcp   LISTEN 0      128                             [::1]:20080         [::]:* 

# 或者
$ netstat -an|grep 20080
tcp        0      0 127.0.0.1:20080         0.0.0.0:*               LISTEN     
tcp6       0      0 ::1:20080               :::*                    LISTEN 

用户

可通过浏览器或者curl就能够查看是否正确。

$ curl https://www.aigrow.space/dev/

参考资料

  1. 局域网服务透过SSH隧道服务Internet用户

  2. 办公室SSH服务器远程访问,重启自动建立通道


评论