使用 Tailscale + SSH + Git 搭建跨网络远程开发工作流
- 技术背景
- 机器角色
- 配置教程
- 1. 在两端安装 Tailscale
- 2. 在台式机 VM 开启 SSH
- 3. 在台式机 VM 初始化 Git 远程仓库
- 4. 添加 .gitignore 并提交源码
- 5. 修复 bare 仓库默认分支
- 6. 在笔记本侧 clone 仓库
- 7. 在 Windows 端配置 SSH alias
- 日常工作流
- 踩过的坑
- 1. `ping` 通不代表 SSH 登录成功
- 2. Tailscale IP 不是普通局域网 IP
- 3. VMware 共享目录不适合作为 Linux 内核源码主仓库
- 4. Windows OpenSSH 会严格检查 `.ssh/config` 权限
- 5. `git branch -M main` 要在第一次 commit 后执行
- 6. clone 成功但无法检出分支
- 7. 不要让 Git 同步构建现场
技术背景
我的开发环境比较特殊:我平时在工位使用一台轻薄本,宿舍有一台性能更好的台式机。近期在做嵌入式 SDK 驱动开发,常常需要Agent阅读源码帮我定位bug,但源码、交叉编译环境和构建产物都比较大,如果频繁手工拷贝整个工程,效率会很低。
最开始容易想到的是把两台机器放进同一个局域网,用192.168.x.x访问。但实际情况是:笔记本和台式机不在同一个网段,普通内网 IP 无法直接访问。
最后采用的方案是:
Tailscale 负责打通网络 SSH 负责远程执行命令 Git 负责同步源码变化 台式机 VM 负责真正编译 笔记本/Agent 负责阅读、分析和小范围修改代码在这个方案里,台式机 Ubuntu VM 相当于一个“私人 Git 服务器”。不需要 GitHub,也不需要公网 IP、DDNS 或路由器端口映射。
机器角色
这套环境里我统一使用下面的命名:
笔记本 Windows 运行 Agent 笔记本 VM 笔记本上的 Ubuntu 虚拟机 台式机 Windows 承载远程桌面和 VMware 台式机 VM 台式机上的 Ubuntu 虚拟机,主要开发和编译环境本文命令使用下面这些占位符。实际操作时,把它们替换成你自己的信息:
DESKTOP_VM_TAILSCALE_IP 台式机 Ubuntu VM 的 Tailscale IP YOUR_LINUX_USER 台式机 Ubuntu VM 的 Linux 用户名 DESKTOP_VM_SDK_DIR 台式机 Ubuntu VM 上的 SDK 工作目录例如,台式机 VM 中 SDK 主工作区可以写成:
/home/YOUR_LINUX_USER/work/embedded-sdk台式机 VM 中 bare Git 仓库可以写成:
/home/YOUR_LINUX_USER/git/embedded-sdk.git配置教程
1. 在两端安装 Tailscale
如果是windows,访问https://tailscale.com/download下载客户端
如果是 Ubuntu/Debian 虚拟机,可以使用官方安装脚本:
curl-fsSLhttps://tailscale.com/install.sh|shsudotailscale up执行sudo tailscale up后,终端会给出一个网页登录授权地址。两台机器登录同一个 Tailscale 账号即可。
检查 Tailscale 服务是否运行:
systemctl status tailscaled设置开机自启并立即启动:
sudosystemctlenable--nowtailscaled查看当前机器的 Tailscale IP:
tailscaleip-4tailscale statusTailscale 的100.x.x.x地址通常会绑定设备。只要不重装系统、不删除 Tailscale 节点、不清空/var/lib/tailscale/,这个 IP 一般不会变化。
2. 在台式机 VM 开启 SSH
在desktop-vm上执行:
sudoaptupdatesudoaptinstall-ygitopenssh-serversudosystemctlenable--nowssh在笔记本侧测试连接:
sshYOUR_LINUX_USER@DESKTOP_VM_TAILSCALE_IP这里会提醒你输入密码,这里的密码是台式机 Ubuntu VM 中YOUR_LINUX_USER用户的密码,不是笔记本密码,也不是 Windows 开机密码。
第一次连接时会提示确认主机指纹:
Are you sure you want to continue connecting (yes/no/[fingerprint])?输入完整的:
yes看到Welcome to Ubuntu 20.04.6 LTS之类的欢迎信息,就说明 SSH 登录成功。
3. 在台式机 VM 初始化 Git 远程仓库
在台式机 VM 上创建一个 bare 仓库。bare 仓库不直接编辑文件,只作为远程同步仓库使用,类似自己的 GitHub。
mkdir-p~/gitgitinit--bare~/git/embedded-sdk.git然后进入台式机 VM 上真正的 SDK 工作目录:
cd~/work/embedded-sdk如果这里还不是 Git 仓库,推荐一开始就指定默认分支:
gitinit-bmain如果已经执行过普通git init,也没关系,后面第一次 commit 成功后再执行:
gitbranch-Mmain注意:如果还没有任何 commit,直接运行git branch -M main可能报:
error: refname refs/heads/master not found fatal: Branch rename failed这是因为 Git 仓库还没有真正创建出master分支,不是严重错误。先 commit,再改分支名即可。
4. 添加 .gitignore 并提交源码
嵌入式 SDK 里通常有大量编译产物,例如:
out/ temp/ *.o *.ko *.cmd *.d *.img *.bin *.log这些不应该进 Git。尤其嵌入式 SDK 这种目录,编译产物和源码混在一起。因此需要创建.gitignore文件,设置规则避免将这些文件上传到仓库中,否则Git 仓库会非常臃肿。
提交前先预览将要加入 Git 的文件:
gitadd-n.确认没有大量构建输出、镜像、临时文件后,再真正提交:
gitadd.gitcommit-m"Initial SDK source import"gitbranch-Mmaingitremoteaddorigin ~/git/embedded-sdk.gitgitpush-uorigin main如果 Git 提示没有配置用户名和邮箱,可以先设置:
gitconfig--globaluser.name"Your Name"gitconfig--globaluser.email"you@example.com"5. 修复 bare 仓库默认分支
如果 clone 时出现:
warning: remote HEAD refers to nonexistent ref, unable to checkout 警告:远程 HEAD 指向一个不存在的引用,无法检出说明 bare 仓库的HEAD还指向默认的master,但实际分支已经是main。
在台式机 VM 上修复:
cd~/git/embedded-sdk.gitgitsymbolic-ref HEAD refs/heads/maingitsymbolic-ref HEAD最后一条命令应该输出:
refs/heads/main6. 在笔记本侧 clone 仓库
如果是在笔记本 VM 的 Linux ext4 目录中 clone,推荐:
mkdir-p~/workcd~/workgitclone YOUR_LINUX_USER@DESKTOP_VM_TAILSCALE_IP:/home/YOUR_LINUX_USER/git/embedded-sdk.gitcdembedded-sdk如果只是为了让 Windows 侧 Agent 快速读取源码,也可以 clone 到 VMware 共享目录,但要明确它只是“轻量阅读/小改副本”,不能作为主编译目录。
例如:
cd/mnt/hgfs/SharedFolders/embedded-sdkgitclone YOUR_LINUX_USER@DESKTOP_VM_TAILSCALE_IP:/home/YOUR_LINUX_USER/git/embedded-sdk.git.gitconfig core.filemodefalsegitconfig core.autocrlffalse检查配置:
gitconfig--getcore.filemodegitconfig--getcore.autocrlf期望输出:
false false7. 在 Windows 端配置 SSH alias
为了让 Agent 能直接在 Windows/Codex 环境里远程执行命令,可以在 Windows 的 SSH config 中配置 alias:
Host desktop-vm HostName DESKTOP_VM_TAILSCALE_IP User YOUR_LINUX_USER IdentityFile ~/.ssh/id_ed25519_remote_dev IdentitiesOnly yes生成一把专用 key:
ssh-keygen -t ed25519 -f "%USERPROFILE%\.ssh\id_ed25519_remote_dev" -C "laptop-to-desktop-vm"把公钥加入台式机 VM:
type "%USERPROFILE%\.ssh\id_ed25519_remote_dev.pub" | ssh desktop-vm "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"测试免密登录:
ssh desktop-vm "hostname && whoami && pwd"成功后,Agent 就可以用这种方式远程查看状态:
ssh desktop-vm"cd ~/work/embedded-sdk && git status --short --branch"远程搜索:
ssh desktop-vm"cd ~/work/embedded-sdk && rg 'driver_name' path/to/source"远程编译:
ssh desktop-vm"cd ~/work/embedded-sdk && <build-command>"日常工作流
台式机 VM 是主开发和编译环境:
cd~/work/embedded-sdkgitstatusgitadd-Agitcommit-m"描述这次修改"gitpush笔记本同步:
gitpull --ff-onlygit pull --ff-only的意思是:
如果本地没有额外提交,就把远程所有新提交同步下来; 如果本地和远程分叉了,就停止,让人手动处理。如果笔记本也做了修改:
gitstatusgitadd-Agitcommit-m"描述这次修改"gitpush然后台式机 VM 再同步并编译:
gitpull --ff-only踩过的坑
1.ping通不代表 SSH 登录成功
曾经遇到过:
pingDESKTOP_VM_TAILSCALE_IP能通,但:
sshdev@DESKTOP_VM_TAILSCALE_IP一直Permission denied。
原因是dev只是示例用户名,真实环境里要替换为台式机 VM 的 Linux 用户名。正确命令应该是:
sshYOUR_LINUX_USER@DESKTOP_VM_TAILSCALE_IPSSH 密码也是台式机 Ubuntu VM 中YOUR_LINUX_USER用户的密码。
2. Tailscale IP 不是普通局域网 IP
192.168.x.x只能在同一个局域网里用。两台机器跨网络、跨地点时,应该使用 Tailscale 分配的100.x.x.x地址。
例如台式机 VM 可以记作:
DESKTOP_VM_TAILSCALE_IPGit remote 可以写成:
YOUR_LINUX_USER@DESKTOP_VM_TAILSCALE_IP:/home/YOUR_LINUX_USER/git/embedded-sdk.git后续如果开启 MagicDNS 或配置 SSH alias,可以把 IP 隐藏成:
desktop-vm:/home/YOUR_LINUX_USER/git/embedded-sdk.git3. VMware 共享目录不适合作为 Linux 内核源码主仓库
我曾经把完整 SDK clone 到:
/mnt/hgfs/SharedFolders/embedded-sdk然后git status出现大量莫名修改,例如:
xt_DSCP.h xt_MARK.h xt_TCPMSS.h ipt_TTL.h ip6t_HL.h aux.c aux这不是因为手动删错了文件,而是因为 Linux 内核源码里有大小写仅不同的文件名,以及 Windows 保留名,例如aux。VMware 共享目录底层受 Windows 文件系统影响,不能完全忠实表示 Linux/ext4 的文件名语义。
结论:
/mnt/hgfs 可以给 Agent 读代码、写文档、做小 patch 不要把它当主仓库 不要在这里编译 不要轻易提交这里显示出来的异常 git status真正的主工作区应该放在台式机 VM 的 Linux 文件系统中:
/home/YOUR_LINUX_USER/work/embedded-sdk4. Windows OpenSSH 会严格检查.ssh/config权限
Windows 上曾经出现:
Bad owner or permissions on C:\Users\YOUR_WINDOWS_USER\.ssh\config这个问题不是 Tailscale,也不是 SSH 服务端,而是本机.ssh/config权限太开放。尤其如果Everyone有完全控制,OpenSSH 会拒绝读取。
可以用完整路径调用icacls.exe修复:
C:\Windows\System32\icacls.exe "%USERPROFILE%\.ssh\config" /inheritance:r C:\Windows\System32\icacls.exe "%USERPROFILE%\.ssh\config" /grant:r "%USERNAME%:F" C:\Windows\System32\icacls.exe "%USERPROFILE%\.ssh\config" /remove "Everyone" "Users" "Authenticated Users"如果临时想绕过 config,可以用:
ssh -F none YOUR_LINUX_USER@DESKTOP_VM_TAILSCALE_IP5.git branch -M main要在第一次 commit 后执行
如果刚git init,还没有 commit,直接改分支名可能失败:
error: refname refs/heads/master not found fatal: Branch rename failed推荐从一开始就用:
gitinit-bmain如果已经git init,就先 commit:
gitadd.gitcommit-m"Initial SDK source import"gitbranch-Mmain6. clone 成功但无法检出分支
出现:
warning: remote HEAD refers to nonexistent ref, unable to checkout不是对象没拉下来,而是 bare 仓库默认HEAD指错了。修复:
cd~/git/embedded-sdk.gitgitsymbolic-ref HEAD refs/heads/main7. 不要让 Git 同步构建现场
Git 适合同步源码和配置,不适合同步整个编译现场。嵌入式 SDK 的构建产物、镜像、临时对象文件应该通过.gitignore排除。
最终边界是:
Git 同步源码变化 SSH 远程执行命令 台式机 VM 负责编译 笔记本/笔记本 VM 负责查看和小改这个边界清楚之后,整个远程开发链路就顺了很多。