出于安全或性能等原因,开发环境被部署在了内网云环境中,内网云环境与外网完全隔离,且所有的开发环境资源(如数据库、Redis等)只能通过内网云环境访问,本地物理机无法直接访问这些测试资源,只能通过RDP协议间接访问内网云环境中的桌面,这给开发工作带来了极大的不便。为了能够直接在本地物理机进行开发,接下来将介绍4种远程开发的方法:

  1. 使用nginx反向代理持续暴露远程资源
  2. 使用ssh转发持续访问远程资源
  3. 使用Microsoft的Visual-Studio-Code远程开发工具
  4. 使用Jetbrains的Gateway远程开发工具

本文中的远程开发指的是:通过本机访问远程资源的过程。其中,“本机”是指本地物理机(假定本地物理机的ip为172.8.8.8),“远程”是指内网云环境(假定远程主机的ip为172.6.6.6,操作系统为ubtunu,用户名为ubuntu)。

一、nginx反向代理

在远程环境配置nginx相关服务的反向代理策略,便可以在本机访问那些只能在远程环境访问的资源了。具体配置方式如下:

如果不加特殊说明,下述操作均在远程环境中进行!

  1. 安装nginx
sudo apt install nginx
  1. 配置nginx
sudo vim /etc/nginx/nginx.conf
load_module /usr/lib/nginx/modules/ngx_stream_module.so;

events {
}

http {

    server
    {
        listen 80;
        server_name http.resource.com;
        location / {
            proxy_pass http://http.resource.com:80;
        }
    }

    server
    {
        listen 443;
        server_name https.resource.com;
        ssl on;
        ssl_certificate /etc/nginx/nginx.crt; # sudo openssl req -x509 -nodes -days 36500 -newkey rsa:2048 -keyout /etc/nginx/nginx.key -out /etc/nginx/nginx.crt
        ssl_certificate_key /etc/nginx/nginx.key;
        location / {
            proxy_pass https://https.resource.com:443;
        }
    }

}

stream
{
    server
    {
        listen 6666;
        proxy_pass tcp.resource.com:6666;
    }

}
  1. 启用nginx服务
sudo systemctl start nginx
sudo systemctl enable nginx
  1. 本机配置hosts
sudo vim /etc/hosts
172.6.6.6 http.resource.com
172.6.6.6 https.resource.com
172.6.6.6 tcp.resource.com
  1. 本机访问远程资源
curl http://http.resource.com
curl https://https.resource.com
telnet tcp.resource.com 6666
  1. 其它可选配置:访问控制

nginx支持通过allow和deny配置ip白名单和黑名单,达到访问控制的目的。

sudo vim /etc/nginx/nginx.conf
http {
    allow 172.8.8.8.; # 本机ip
    deny all;
    #...

    server {
        allow 172.8.8.8.;
        deny all;
        # ...
    }

allow与deny分别表示白名单和黑名单,上述配置表示除了172.8.8.8能访问这台服务器外,其它的ip都不允许访问。其中,server会覆盖http级别的allow和deny配置。

为了更进一步地控制,也可以使用系统的防火墙,如iptables、ufw等等。

  1. 其它可选配置:泛域名统一转发

正常情况下,一个server只能配置一个域名的转发。泛域名统一转发支持相同域名前缀或后缀(如company.com)的所有域名,只需要使用一个server,就可以完成统一的转发,以简化server的配置。

sudo vim /etc/nginx/nginx.conf
server
{
    listen 80;
    resolver 127.0.0.53; # 配置域名解析服务
    server_name *.company.com; # 泛域名
    location / {
        proxy_pass http://$host; # 直接转发到server_name对应的域名
    }
}

其等价于:

{
    listen 80;
    server_name a.company.com; 
    location / {
        proxy_pass http://a.company.com;
    }
}
{
    listen 80;
    server_name b.company.com; 
    location / {
        proxy_pass http://b.company.com;
    }
}
{
    listen 80;
    server_name c.company.com; 
    location / {
        proxy_pass http://c.company.com;
    }
}         

可以看到,泛域名统一转发要简洁地多。假设nginx所在的主机已经有了a.company.com、b.company.com、c.company.com的dns记录(如果没有,在nginx所在的主机配置host即可),那么上述配置将直接支持所有相关域名的转发。随后,配置本机host:

172.6.6.6 a.company.com
172.6.6.6 b.company.com
172.6.6.6 c.company.com

本机访问即可自动转发到真正的*.company.com:

curl http://a.company.com
curl http://b.company.com
curl http://c.company.com

二、ssh转发

ssh转发分为本地转发、远程转发、动态代理、TCP转发、跳板连接等方式:

  1. 本地转发(-L):在本地开一个端口,并通过这个端口访问远程服务,适用于将远程资源转发到本地的情况(这里的远程资源包括远程主机自己的服务资源,也包括远程主机能够访问到的其它主机的服务资源)。
  2. 远程转发(-R):在远程开一个端口,并通过这个端口访问本地服务,适用于将本地资源转发到远程的情况(这里的本地资源包括本地主机自己的服务资源,也包括本地主机能够访问到的其它主机的服务资源,如:外网资源等)。
  3. 动态代理(-D):在本地开一个端口,并通过这个端口建立访问远程服务的socks5代理,适用于动态将远程资源转发到本地的情况。
  4. TCP转发(-W):直接转发TCP数据流,可以代理git请求(配合ssh的ProxyCommand配置一起使用)。
  5. 跳板连接(-J):通过跳板机,直接访问远程SSH资源(通过ssh的ProxyJump配置简化使用过程)。

(一)本地转发

  1. 在本机启用本地转发
ssh -L 8080:http.resource.com:80 ubuntu@172.6.6.6 -N

-L表明启用ssh本地转发。这里将http.resource.com:80远程资源转发到了本机的8080端口。

  1. 在本机配置hosts
sudo vim /etc/hosts
127.0.0.1 http.resource.com
  1. 在本机访问远程资源
curl http://http.resource.com:8080

(二)远程转发

  1. 在本机启用远程转发
ssh -R 8080:www.baidu.com:443 ubuntu@172.6.6.6 -N

-R表明启用ssh远程转发。这里将www.baidu.com:443本机资源转发到了远程的8080端口。

  1. 在远程环境配置hosts
sudo vim /etc/hosts
127.0.0.1 www.baidu.com
  1. 在远程环境访问本地(外网)资源
curl https://www.baidu.com:8080

(三)动态代理

  1. 启用动态转发代理
ssh -D 127.0.0.1:8080 ubuntu@172.6.6.6 -N
  1. 在浏览器配置代理

在浏览器中安装代理切换插件,如:SwitchyOmega等。

  1. 在curl配置代理(可选)
# 代理时使用本地DNS
curl -x socks5://127.0.0.1:8080 http.resource.com
curl --socks5 127.0.0.1:8080 http.resource.com
# 代理时使用远程DNS
curl -x socks5h://127.0.0.1:8080 http.resource.com
curl --socks5-hostname 127.0.0.1:8080 http.resource.com
  1. 在kubeconfig中配置代理(可选)
vim ~/.kube/config
clusters:
- cluster:
    proxy-url: socks5://127.0.0.1:8080
    server: https://k8s.resource.com
  1. 在maven中配置代理(可选)

(1)socks代理临时生效

export MAVEN_OPTS="-DsocksProxyHost=127.0.0.1 -DsocksProxyPort=8080 -DsocksNonProxyHosts=maven.aliyun.com"
mvn clean package -DskipTests 

(2)socks代理全局生效

mkdir -p ~/.mvn 
echo "-DsocksProxyHost=127.0.0.1 -DsocksProxyPort=8080 -DsocksNonProxyHosts=maven.aliyun.com" >> ~/.mvn/jvm.config

(3)http代理

除了上述基于maven环境变量让JVM底层使用socks代理外,maven也支持通过settings.xml文件的proxies项来配置代理,但该配置只支持http协议,不支持ssh的socks协议。对于这种情况,可以通过privoxy等工具,将socks协议转换成http代理,然后配置settings.xml文件的proxies项,具体详见附录

  1. 在本机git配置代理(可选)
cat >> ~/.ssh/config << EOF

Host git.resource.com
  ProxyCommand ncat --proxy 127.0.0.1:8080 --proxy-type socks5 %h %p

EOF

ProxyCommand的值自定义了TCP代理转发的过程,nc、ncat、socat等命令都可以实现类似的功能,安装任意一个即可。同时,ssh -W也支持TCP流量转发(推荐),详见下文。

(四)TCP转发

  1. 在本机git配置代理(基于ssh的-W参数)
cat >> ~/.ssh/config << EOF

Host git.resource.com
  ProxyCommand ssh -W %h:%p 172.6.6.6

EOF

(五)跳板连接

  1. 基于ssh的-J参数指定跳板机
ssh -J ubtunu@172.6.6.6 ssh.resource.com
  1. 基于ssh的ProxyJump配置指定跳板机
cat >> ~/.ssh/config << EOF

Host ssh.resource.com
  ProxyJump 172.6.6.6

EOF
ssh ssh.resource.com

三、vscode远程开发

Visual-Studio-Codevscode)可以通过Remote插件(主要是Remote-SSH插件)来进行远程开发。

  1. 安装Remote插件

在本机vscode的扩展中搜索Remote,找到对应项安装即可。

  1. 在远程环境中安装vscode-server

正常情况下,首次使用Remote连接远程环境时,远程环境会自动下载服务端vscode。

  1. 在本机vscode中连接远程环境

在vscode中点击远程资源管理器,在SSH Target项点击+,输入ssh ubuntu@172.6.6.6 -A,回车,选择/home/xxx/.ssh/conf,回车。在SSH Target下的172.6.6.6项点击右边图标,进入远程环境

至此,后续所有开发操作均和本地vscode操作一致了,如果需要支持特定语言,则需要安装对应插件(如:java)。

四、jetbrains远程开发

Jetbrains旗下有两款远程开发方案:GatewayProjetctor。Gateway远程开发详见Gateway官方教程,Projetctor已过期,并合并至Gateway。

附录

Maven的HTTP代理配置

(1)基于ssh配置socks代理

ssh -D 127.0.0.1:8080 ubtunu@172.6.6.6 -N &

(2)配置systemd服务(可选)

mkdir -p ~/.config/systemd/user/

cat << EOF > ~/.config/systemd/user/ssh-tunnel.service
[Unit]
Description=SSH Dynamic Tunnel
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/ssh -D 127.0.0.1:8080 ubtunu@172.6.6.6 -N -o ExitOnForwardFailure=yes -o ServerAliveInterval=30
Restart=on-failure
RestartSec=10

[Install]
WantedBy=default.target

EOF

systemctl --user enable --now ssh-tunnel.service

(3)基于privoxy将socks转换成http

# 安装privoxy
sudo pacman -S privoxy
# 配置转发(默认listen-address配置为:127.0.0.1:8118,如果没有配置,需加上)
echo "forward-socks5t / 127.0.0.1:8080 ." | sudo tee -a /etc/privoxy/config
# 启用privoxy服务
sudo systemctl enable --now privoxy

(4)Maven配置修改

<settings>
    <proxies>
        <proxy>
            <id>ssh-privoxy-proxy</id>
            <active>true</active>
            <protocol>http</protocol>
            <host>127.0.0.1</host>
            <port>8118</port>
            <nonProxyHosts>maven.aliyun.com</nonProxyHosts>
        </proxy>
    </proxies>
</settings>                                                                                                                                             

VSCode远程环境离线安装

在老版本的vscode中,如果远程环境没有外网的网络策略,需用手工在本地下载服务端vscode之后传输到远程环境进行离线安装。最新版本的vscode版本已经支持了“本地下载服务端vscode,并传输到远程环境”这一过程,本小节后续内容请忽略。

在本机下载vscode-server:

export VSCODE_COMMIT_ID=$(code -v | sed -n '2p') # 如果vscode的commit_id获取失败,请打开vscode,点击帮助,再点击关于,手动获取commit_id
wget https://update.code.visualstudio.com/commit:$VSCODE_COMMIT_ID/server-linux-x64/stable -O vscode-server-linux-x64-$VSCODE_COMMIT_ID.tar.gz # 下载vscode-server
scp vscode-server-linux-x64-$VSCODE_COMMIT_ID.tar.gz ubuntu@172.6.6.6:/home/ubuntu # 上传vscode-server

在远程环境离线安装vscode-server:

export VSCODE_COMMIT_ID=$(ls ~/vscode-server-linux-x64-* | cut -d '-' -f 5 | cut -d '.' -f 1)
mkdir -p ~/.vscode-server/bin/
cd ~/.vscode-server/bin/
mv ~/vscode-server-linux-x64-*.tar.gz .
tar zxvf vscode-server-linux-x64-*.tar.gz
mv vscode-server-linux-x64 $VSCODE_COMMIT_ID

Projetctor远程环境离线安装

Projetctor已过期,并合并至Gateway,本小节后续内容请忽略。

  1. 在远程环境安装projetctor服务端
docker pull jetbrains/projector-idea-c
docker run --rm -p 8887:8887 -it jetbrains/projector-idea-c

有三种方式安装projetctor服务端:python方式(projector-installer)、idea插件方式projector-server-plugin、docker方式(projector-docker),这里以docker方式为例。

如果云环境无法访问docker仓库,可以使用ssh远程转发临时代理docker仓库

  1. 在本机连接projetctor服务

有两种方式可以连接projetctor服务: