本文是《Go 语言大白话入门》系列教程的拓展教程,跟着一步步练手,点击查看完整目录
你在 Windows 或 Mac 上写代码,但程序最终要跑在服务器上给别人用。绝大部分服务器的操作系统是 Linux,没有图形界面,只有命令行。这章讲部署 Go 程序必须会的那些命令。
Linux 体系庞大,本章只讲解部署 Go 程序必备的基础命令。若想要系统深耕,可以专门学习 Linux 教程。不用一次性背完所有命令,遵循「用到再学」的思路,善用搜索引擎与 AI 工具解决遇到的问题。
部署 Go 程序前,先要选服务器装什么 Linux。服务器选什么发行版直接决定了你之后几年怎么运维——选错了换一次代价很大。
💡 新手最常踩的坑:以为「Linux 都一样」。其实不同发行版的包管理器(apt/yum/dnf)、服务管理方式(systemd)、系统目录布局、默认软件版本都不同,命令不通用。 比如 Ubuntu 用
apt install,CentOS 早期用yum,新版本改dnf;Debian 偏保守,Arch 滚动更新几乎每天都有新版本。
| Ubuntu LTS | 新手首选 | |||
| Debian | ||||
| CentOS Stream | ||||
| Rocky Linux / AlmaLinux | ||||
| Amazon Linux | AWS 用户首选 | |||
| Alpine | ||||
| openEuler |
⚠️ 新手最稳的选择:Ubuntu 22.04/24.04 LTS 或 Debian 12(bookworm)。 LTS = Long Term Support(长期支持 5 年),软件版本不会频繁升级出问题,遇到问题网上能搜到答案的概率最大。
docker pull ubuntu:24.04),不用关心 ISOnetinst 最小镜像) | ||
💡 镜像源加速:国内服务器装完系统后,建议立即配置国内 apt/yum 镜像源(阿里云、清华 TUNA、中科大),不然装个软件可能等半天。
Ubuntu/Debian: /etc/apt/sources.list换成mirrors.aliyun.com/ubuntu/或mirrors.tuna.tsinghua.edu.cn/ubuntu/CentOS/Rocky: /etc/yum.repos.d/CentOS-Base.repo换成mirrors.aliyun.com/centos/
云服务器装好系统、拿到公网 IP 后,第一件事就是远程登录进去——服务器在机房,物理上摸不到键盘鼠标,全靠网络连。
⚠️ 🚨 拿到新服务器后第一件事:放行 22 端口!阿里云 / 腾讯云 / AWS 等云厂商的安全组默认封禁所有外部端口(仅放行极少数管理端口)。 如果你买完服务器就
ssh root@IP,大概率连不上——先在云控制台「安全组」放行 22 端口再登录。 很多新手在这一步卡半天以为密码错、系统坏了,其实就是端口被云厂商挡掉了。
主流方式就一种:SSH(Secure Shell)——加密的远程命令行协议,默认端口 22。 所有云服务器、几乎所有 Linux 服务器,都靠 SSH 登录。
买好云服务器后,控制台会告诉你三样东西:
服务器公网 IP:123.45.67.89登录用户名:root (Ubuntu 通常第一次是 ubuntu,CentOS/Rocky 是 root)登录密码 / 密钥:购买时设置的密码,或控制台「密钥对」下载的 .pem 文件⚠️ 新手最常踩的坑 1:用「root」登录后乱删系统文件,一个
rm -rf /整个服务器就没了。正确做法:用普通用户登录,需要管理员权限时加sudo。 Ubuntu 默认的ubuntu用户就在sudoers里,可以直接sudo。
Mac / Linux(自带终端):
ssh root@123.45.67.89# 第一次连接会问 yes/no,输入 yes# 然后输入密码(输入时屏幕不显示,输完回车)Windows(用 Windows Terminal / PowerShell,自带 OpenSSH 客户端;Win10 1809+ / Win11 默认已装):
ssh root@123.45.67.89嫌每次输 IP 麻烦?把常用服务器写进
~/.ssh/config(Windows 路径C:\Users\你的用户名\.ssh\config):Host myserver HostName 123.45.67.89 User root Port 22之后直接
ssh myserver就能连。
密码登录有两个问题:
密钥登录用一对文件:
id_rsa / mykey.pem):放在你本地电脑,绝不能泄露id_rsa.pub / authorized_keys):放到服务器上,公开无所谓登录时本地用私钥签名,服务器用公钥验签——只有私钥匹配才让进,根本不需要密码。
ssh-keygen -t ed25519 -C "你的邮箱"# 私钥密码(passphrase):留空=无密码(开发用方便),建议设一个(生产用更安全)# 会在 ~/.ssh/ 目录下生成 id_ed25519(私钥)和 id_ed25519.pub(公钥)💡 私钥设了密码 = 每次 ssh 都要输一次密码?不一定要: 设了密码的私钥,配合
ssh-agent可以在当前会话只输一次密码,之后都免密:eval"$(ssh-agent -s)"# 启动 ssh-agentssh-add ~/.ssh/id_ed25519 # 添加私钥,输一次密码# 之后所有 ssh / scp / git 操作都不用再输密码VS Code / JetBrains IDE / 终端多开都能共享这个状态。 不设密码就是「裸私钥」,只在你物理安全的工作机上能用,笔记本丢了别人能直接登服务器。
ssh-copy-id root@123.45.67.89# 输入一次密码后,公钥自动追加到服务器的 ~/.ssh/authorized_keys# 之后再 ssh 就不用密码了PuTTY 自带的 PuTTYgen 工具(下载地址)生成 .ppk 密钥,然后在 PuTTY 里配置。
阿里云 / 腾讯云 / AWS 买服务器时都支持「创建密钥对」,控制台直接帮你把公钥注入到 ~/.ssh/authorized_keys,最省事。
服务器只要暴露公网 22 端口,每天就会被全球机器人扫描爆破。下面三件事买完服务器第一件事就做:
改 SSH 端口(避开 90% 的扫描)
sudo vim /etc/ssh/sshd_config# 找到 Port 22,**先在下面加一行 Port 2222,保留 Port 22 暂时不注释删除**# 同时加两行是为了用新端口测试成功后再删 22,避免把自己锁在外面sudo systemctl restart sshd# 之后登录要加 -p 参数:ssh -p 2222 root@123.45.67.89⚠️ 🚨 极容易把自己锁在外面,顺序必须是:
顺序错了直接进不去服务器,只能走云控制台的「VNC 远程连接」或「救援模式」救回来,千万别在生产环境踩。
sudo ufw allow 2222/tcp 或 firewall-cmd --add-port=2222/tcp)Port 2222(保留 Port 22),重启 sshdssh -p 2222 测试能连进去Port 22 注释掉并重启 sshd禁用密码登录(只允许密钥)
# 同一份配置文件里PasswordAuthentication no# ⚠️ 一定要先确认密钥能登录成功再禁用!否则把自己锁在外面配置防火墙(只放行需要的端口)
# Ubuntu (ufw)sudo ufw allow 2222/tcp # SSHsudo ufw allow 80/tcp # HTTPsudo ufw allow 443/tcp # HTTPSsudo ufw enable# CentOS/Rocky (firewalld)sudo firewall-cmd --permanent --add-port=2222/tcpsudo firewall-cmd --reload💡 云服务器额外多一层:阿里云/腾讯云/AWS 控制台还有个「安全组」,相当于云厂商的防火墙。服务器内防火墙 + 云安全组,两层都要配,缺一不可。 只配安全组、不配内防火墙 → 服务器被攻破后横向移动没人拦 只配内防火墙、不配安全组 → 别人照样能直接访问你的端口
ssh 命令 | ||
scp | ||
| 强烈推荐 | ||
💡 VS Code Remote-SSH 是部署 Go 服务的秘密武器: 在 VS Code 里装上 Remote-SSH 插件,配置好服务器后,可以直接编辑服务器上的代码、跑调试、看日志、打开终端——跟本地开发完全一样。 部署 Go 服务时尤其有用:改完代码
Ctrl+S保存,go run/go build都在远程跑,比本地交叉编译再 scp 传上去再重启服务快一个量级。
ls | ls | |
ls -la | ls -la | |
cd | cd /home/app | |
pwd | pwd | |
mkdir | mkdir myapp | |
rm | rm test.txt | |
rm -rf | rm -rf old_folder | |
cp | cp a.txt b.txt | |
mv | mv old.txt new.txt | |
cat | cat config.yaml | |
less | less app.log | |
tail -20 | tail -20 app.log | |
tail -f | tail -f app.log | |
ps aux | grep myapp | ||
kill 12345 | ||
chmod +x myapp | ||
chmod 755 myapp | ||
chown -R app:app /home/app | ||
sudo 命令 | sudo systemctl restart nginx |
# 查找 myapp 进程(管道符 grep)ps aux | grep myapp# 输出示例:# ubuntu 12345 0.5 0.1 123456 23456 ? Ssl 10:00 0:01 ./myapp# 第二个数字 12345 就是 PID,可以 kill 12345 终止💡 权限高频场景(部署 Go 程序常遇到):
chmod +x myapp—— 编译出的二进制文件没执行权限,./myapp报Permission denied时用它chown -R app:app /home/app—— 用普通用户跑服务时,文件归属要改成对应用户,root 跑的文件业务用户读不到会出诡异 bug数字权限速记: 4读r,2写w,1执行x,755=rwxr-xr-x(所有者全权,其他人只能读+执行)部署常见组合:配置文件 chmod 640(所有者读写、组读、其他无),密钥文件chmod 600(只有所有者能读,安全要求)
⚠️
chown -R慎用,千万不要对系统目录执行**:
只对自己应用目录用,比如 chown -R app:app /home/app安全绝不要执行 chown -R xxx /、chown -R xxx /usr、chown -R xxx /etc之类的命令这会把整个系统目录的所有者改了,系统启动失败、SSH 登不上、各种服务崩溃,只能重装系统救回 chmod -R 777 /同样致命,新手最容易踩的"删库跑路"式操作
Go 编译出的是单个可执行文件,不需要在服务器上安装 Go 环境。在你的开发机上编译 Linux 版本:
# Mac/LinuxGOOS=linux GOARCH=amd64 go build -o myapp main.go# ARM 架构服务器(阿里云倚天、AWS Graviton、树莓派等云上 ARM 实例)GOOS=linux GOARCH=arm64 go build -o myapp main.go💡 架构要看目标服务器,不是看开发机: 苹果 M1/M2 笔记本是 ARM 没错,但编译出来的目标平台是给服务器用的——
服务器是 amd64(绝大多数云主机)→ 编译用 amd64服务器是 arm64(少数 ARM 云主机)→ 编译用 arm64Go 默认会根据本机架构自动选择,但交叉编译时必须显式指定目标架构,否则传到服务器会报
exec format error。
💡 示例默认关闭了 CGO(Go 1.10+ 行为): 如果你的代码用
import "C"启用了 CGO,交叉编译复杂度大幅上升——需要目标平台的 C 工具链(glibc 版本、gcc 头文件),Mac 上几乎编不出 Linux 兼容二进制。新手先尽量不要用 CGO:少碰 cgo 库(一些老牌 MySQL 驱动、图像处理库会引入),用纯 Go 实现代替。 如果一定要用,在目标 Linux 服务器上直接go build最稳。
# Windows PowerShell(Win10 1809+ / Win11 默认终端)$env:GOOS="linux"; $env:GOARCH="amd64"; go build -o myapp main.go:: Windows CMD(老版本的 cmd.exe 终端)set GOOS=linuxset GOARCH=amd64go build -o myapp main.go⚠️ Windows 用户最容易踩的坑: 上面三段代码不能混用。在 PowerShell 里运行
set GOOS=linux不会报错,但环境变量也没生效,结果编译出来还是 Windows 版本。 怎么判断当前终端类型?看提示符:PS C:\Users\xxx>是 PowerShell,C:\Users\xxx>是 CMD。 不确定时直接go env GOOS看当前生效的目标系统是不是linux,不对就换语法重新执行。💡 想永久设置:把环境变量写进「系统属性 → 高级 → 环境变量」里,所有终端都生效,不用每次临时设。
scp myapp root@你的服务器IP:/home/app/root@IP 是登录用的用户名和服务器地址,冒号后面是目标目录。
⚠️ **如果按前文改了 SSH 端口(如 2222),scp 必须用大写 -P**:
scp -P 2222 myapp root@123.45.67.89:/home/app/注意大小写差异:ssh 用小写 -p,scp 用大写 -P,这是历史遗留,新手最容易抄错。 完整路径示例(目标服务器目录不存在会报错):
scp -P 2222 myapp root@123.45.67.89:/home/app/myapp💡 更现代的替代:rsync -avz -e "ssh -p 2222" myapp root@123.45.67.89:/home/app/
⚠️ 目标目录不存在会报错:scp 不会自动创建中间目录,新服务器第一次部署前先 SSH 上去创建:
ssh root@123.45.67.89 "mkdir -p /home/app"否则会报 No such file or directory(最容易在第一部署时遇到)。
chmod +x myapp # 加执行权限./myapp # 运行直接 ./myapp 的问题是:关掉终端程序就停了。临时用可以这样后台运行:
nohup ./myapp > app.log 2>&1 &nohup:退出终端也不停> app.log:标准输出写进日志文件2>&1:错误输出也写进同一个文件&:放到后台跑💡 执行后会在当前目录生成一个
nohup.out文件:这是 nohup 兜底输出,即便你已经重定向到app.log,它还是会创建(空文件,占点空间)。 不想看到这个文件,最简单的办法是rm nohup.out(运行中删除不影响),或者重定向都加上:nohup ./myapp >/dev/null 2>nohup.err &。
⚠️ nohup 只是临时方案,生产禁止长期使用: 不具备自动重启、进程崩溃恢复、日志切割、开机自启能力。 服务器一重启程序就再也起不来;日志文件会无限膨胀把磁盘塞满;崩溃后没人管就只能等到你发现。本地调试或临时验证用用可以,部署到生产请直接用下面的 systemd。
nohup 的问题是服务器重启后程序不会自动恢复。生产环境要用 systemd:
# /etc/systemd/system/myapp.service[Unit]Description=My Go App[Service]ExecStart=/home/app/myappWorkingDirectory=/home/appRestart=alwaysEnvironment=APP_PORT=8080[Install]WantedBy=multi-user.target⚠️ service 文件权限不要设 777:
sudo vim /etc/systemd/system/myapp.servicesudo chmod 644 /etc/systemd/system/myapp.service
生产规范要求使用 644(所有者读写、其他人只读) 真正会阻止 systemd 加载的是把 service 文件放在全局可写 + 其他用户可执行的目录,普通 777 权限会触发安全告警但多数系统仍可启动 改完文件记得 sudo systemctl daemon-reload让 systemd 重新读取
常用操作:
sudo systemctl daemon-reload # 改了 .service 文件后**必须**执行,否则改动不生效sudo systemctl start myapp # 启动sudo systemctl enable myapp # 设置开机自启sudo systemctl status myapp # 查看状态sudo systemctl restart myapp # 重启sudo systemctl stop myapp # 停止journalctl -u myapp -f # 实时查看日志WorkingDirectory 容易漏但很重要。 如果代码里用了相对路径读文件(比如 os.ReadFile("./config.yaml")),手动执行时没问题,但 systemd 启动时当前目录不是你的项目目录,文件就找不到了。WorkingDirectory 就是解决这个问题的。
Restart=always 表示程序崩溃后自动重启。
💡
Restart=alwaysvsRestart=on-failure怎么选:
Restart=always:进程任何方式退出(含主动os.Exit(0))都会被拉起。适合长驻服务(HTTP server、消息消费)Restart=on-failure:只有非正常退出(崩溃、超时、被信号杀掉)才重启。适合任务型服务(worker、定时任务),主动os.Exit(0)不会被错误地拉起还有 Restart=no(默认不重启)、Restart=on-abnormal(只在异常时重启)新手用 Restart=always最省心——线上出 bug 自动拉起总比服务挂了好
tail -50 app.log # 看最后50行tail -f app.log # 实时跟踪(新内容自动刷出来)journalctl -u myapp -f # 如果用 systemd 管理curl http://localhost:8080/hello # 在服务器上测试接口ss -tlnp # 查看哪些端口在监听(推荐,系统自带)netstat -tlnp # 备选,部分新系统未预装(Ubuntu 20.04+/Debian 11+ 需 apt install net-tools)💡 **优先用
ss -tlnp**:
ss是iproute2工具集的一部分,现代 Linux 默认已装netstat属于老牌net-tools,在 Ubuntu 20.04+ / Debian 11+ / CentOS 8+ 等新版系统里默认不再预装,执行会报command not found性能上 ss直接读内核 netlink,比 netstat 快一个量级,连接数上万时差距明显老教程的 netstat命令在老系统上能跑,但新系统优先用ss两者参数基本一致:
-tTCP-l监听中-n显示数字端口(不解析服务名,更快)-p显示进程名(⚠️ 需要 root 才能看到其他用户的进程,普通用户执行只看到自己的,加sudo ss -tlnp才能看全)
不要把数据库地址、密钥这些配置写死在代码里,用环境变量传入:
# 临时设置export APP_PORT=8080# 运行时传入APP_PORT=9090 ./myappGo 代码里读取:
port := os.Getenv("APP_PORT")if port == "" { port = "8080"// 给个默认值}在 systemd 的 service 文件里,用 Environment=APP_PORT=8080 设置,或者用 EnvironmentFile=/home/app/.env 从文件里读。
chmod +x 后 ./myapp 运行curl localhost:端口 测试命令看着简单,真正卡住人的是遇到报错不会排查——同一个错误码在 Ubuntu / CentOS / Alpine 上原因可能完全不同,搜出来答案抄过去也不一定管用。
新手最有效的路径:
rebuild 重建man 命令**:Linux 自带的手册,写得比任何教程都权威(虽然全英文),man systemctl 看一眼就知道 systemd 怎么用journalctl -xe(看日志)、systemctl status xxx、dmesg | tail(内核日志)、strace -p PID(追踪进程系统调用)—— 90% 的部署问题用这四条就能定位| Linux 中国 | ||
| 开源中国(OSChina) | site:oschina.net 搜中文解决方案 | |
| 知乎 Linux 话题 | 「XX 命令怎么用」「CentOS 怎么换 Ubuntu」 | |
V2EX/go/devops 节点 | ||
| 掘金 / 思否(SegmentFault) |
💡 排错黄金组合:英文报错原文 + 谷歌 +
site:stackoverflow.com或site:github.com/issues。 中文社区适合入门概念、对比选型;具体报错、底层原理、bug 复现几乎都必须靠英文。 不建议把希望全押在百度上——同一个问题百度前 3 页都是复制粘贴的同一篇,CSDN 还夹带私货。
顺带一句:本书所有示例都在 Ubuntu 22.04 LTS 上跑过验证,跟着练出问题的概率最小。
本文由作者原创首发于微信公众号。本教程全网同名,谢绝任何形式的跨平台无授权转载与洗稿。如需开白或业务合作,请在公众号后台留言联系。
本文部分内容由AI辅助生成,经人工润色、修改、校对。