换了一台新电脑,要把一个项目跑起来:比如一个 Python 写的网站,数据存在 PostgreSQL 里。装 Python、配镜像、拉代码、起数据库,每一步敲什么命令,现在问一下 AI 就有,而且比任何配置手册都新。这篇文章原来就是这样一份手册,所以改写了。

难的是出问题的时候:明明装了却提示「command not found」,配了镜像 Docker 还是拉不下来,同事电脑上能跑的代码到你这里就报错。这时候要判断 AI 给的方向对不对,靠的是知道每个工具是怎么工作的。

所以这里不列安装步骤,每个工具只讲三件事:解决什么问题、核心原理、最常用的几个用法。读这篇只需要用过命令行,知道「敲一行命令、回车执行」就够了。

把一个项目跑起来,依次会用到三组工具:先让软件装得上,再拿到并修改代码,最后连同数据库一起运行

WSL:在 Windows 上用 Linux

解决什么问题:服务器几乎都跑 Linux,大部分开发工具也是先为 Linux 设计的。WSL(适用于 Linux 的 Windows 子系统)让你不装双系统、不手动管虚拟机,就能在 Windows 里用上一个完整的 Linux。用 Mac 的话不需要它:macOS 本身就是类 Unix 系统,自带终端。

原理:WSL 2 在一个轻量的虚拟机里运行真正的 Linux 内核,由 Windows 自动管理它的启动和资源。Linux 的文件存放在一个虚拟磁盘文件(ext4.vhdx)里;Linux 这一边通过 /mnt/c 访问 Windows 的 C 盘。

明白了这两点,三个常见的坑就都能解释:

  • 代码放在 /mnt/c 下面,git status、npm install 慢得出奇:跨系统访问文件要经过一层转换,读写大量小文件时尤其明显。代码应该放在 Linux 自己的文件系统里,比如 ~/projects。

  • C 盘被占满:虚拟磁盘默认放在 C 盘,用得越多越大。要搬到别的盘,可以先 wsl --export 导出成一个压缩包,再 wsl --import 导入到新位置。

  • WSL 里用不了 Windows 上的代理:WSL 默认是 NAT 网络,有自己的 IP,访问 Windows 上的 127.0.0.1 并不是同一个地方。较新的版本可以在 .wslconfig 里设成镜像网络模式(networkingMode=mirrored),和 Windows 共用网络与代理设置。

终端与 Shell

解决什么问题:用文字命令操作电脑,可以组合、可以重复、可以写成脚本,这是图形界面做不到的。终端是那个显示文字的窗口;Shell 是窗口里读你的命令、再去执行它的程序。bash 和 zsh 是两种常见的 Shell,Oh My Zsh 是 zsh 的一套配置和插件集合。

原理:你敲下一个命令时,Shell 做的事情大致是:

命令能不能找到、找到的是哪一个,全看 PATH;新进程拿到的是环境变量的一份拷贝

两个最关键的概念:

  • PATH:一个目录列表。Shell 从前往后找,用第一个找到的。「装了却说 command not found」,十有八九是程序装进了一个不在 PATH 里的目录;「装了新版本,用的却还是旧版本」,是旧版本所在的目录排在前面。

  • 环境变量会被继承,但只往下传:子进程拿到的是父进程环境变量的一份拷贝。所以写在 ~/.zshrc 里的 export 只对之后新开的终端生效,已经打开的终端要 source ~/.zshrc 重新读一遍;已经在运行的程序则完全感知不到。

最常用的几个用法:

写法

作用

which python

实际用的是哪一个 python

echo $PATH

去哪些目录找命令,按什么顺序

cmd1 | cmd2

管道:前一个命令的输出,作为后一个命令的输入

cmd > file、cmd >> file

把输出写进文件(覆盖、追加)

cmd1 && cmd2

前一个成功了才执行后一个

Ctrl + R

在历史命令里搜索

我在 Oh My Zsh 里常开的插件有四个:zsh-autosuggestions 根据历史命令提示你接下来可能要敲什么;zsh-syntax-highlighting 给命令上色,敲错了马上能看出来;z 记住常去的目录,z 目录名的一部分 就能跳过去;extract 让 x 文件名 解压任意格式的压缩包。

包管理器

解决什么问题:一个软件往往依赖很多别的软件,依赖又有依赖。手动下载安装既麻烦,又容易装出互相冲突的版本。不同层面有各自的包管理器:

包管理器

管什么

软件从哪来

apt

Ubuntu 的系统软件

Ubuntu 的软件仓库

Homebrew

macOS(和 Linux)上的命令行工具,版本通常比 apt 新

Homebrew 的仓库

pip、uv

Python 包

PyPI

Maven

Java 依赖

Maven 中央仓库

npm

JavaScript 包

npm registry

原理:所有包管理器的工作方式都差不多,分四步:

包管理器都是这四步;镜像只替换「下载」这一步的地址

理解了这四步,很多操作就不神秘了:

  • 为什么要先 update:apt update 只更新本地的索引,apt upgrade 才真正升级软件。索引旧了,就会找不到新版本。

  • 为什么会顺带装上别的东西:这是「解析依赖」那一步的结果。比如用 Homebrew 装 Maven,会自动带上一个 OpenJDK;已经有自己的 JDK 的话,可以加 --ignore-dependencies 跳过。

  • 为什么会有锁文件:package-lock.json、uv.lock 这类文件记下了解析出来的确切版本,换一台机器也能装出一模一样的环境。「同事那里能跑,我这里报错」,先看双方装的版本是不是同一份锁文件解析出来的。

  • 安装说到底只是把文件放进一个目录:在公司的服务器上没有 root 权限、不能用 apt 时,可以下载源码,用 ./configure --prefix=$HOME 编译安装到自己的家目录,再确保 ~/bin 在 PATH 里。我就这样在没有权限的服务器上装过 zsh。

apt 源里的 main、restricted、universe、multiverse 是什么

配置 Ubuntu 软件源时,每一行末尾那几个词是软件仓库的四个分区,按「是否自由软件」和「是否官方支持」划分:


自由软件

非自由软件

官方支持

main

restricted

社区维护

universe

multiverse

镜像与代理

解决什么问题:国内访问很多国外的软件仓库很慢甚至不通,第一次装依赖时就会遇到。解决办法有两类,改变的东西不一样:

办法

改变的是什么

适合

镜像

下载的地址:从国内的一份副本下载

apt、pip、Maven、npm、Docker 镜像这类有国内副本的东西

代理

网络的路径:流量绕道境外的服务器

没有国内副本的网站和服务,比如 GitHub

原理:

  • 镜像只替换下载地址。镜像是仓库的一份副本,配镜像就是告诉包管理器「去这里下载」,索引、解析依赖、安装都不变。副本可能不全:Maven 的镜像设成 mirrorOf 为 * 时,会接管所有仓库的请求,镜像里没有的包就找不到了。所以我的 Maven 配置没有用全局镜像,而是列了几个仓库:先查阿里云,找不到再依次查中央仓库和 Confluent 仓库(Kafka 相关的依赖在那里)。

  • 代理靠环境变量告诉命令行程序。curl、git、pip 等很多工具会读取 http_proxy、https_proxy、all_proxy 这几个环境变量,决定走不走代理(各工具对大小写写法的支持不完全一样,常见做法是大小写都设上)。结合 Shell 一节「环境变量只往下传」的原理就能理解:在一个终端里设置了代理,只对这个终端里之后启动的程序生效;Docker 拉镜像的是后台的 Docker 服务,它读不到你终端里的变量,要单独配置。

代理的原理、系统代理和 TUN 模式的区别,见 科学上网。

语言环境:Python 与 Java

Python:每个项目一个虚拟环境

解决什么问题:项目 A 要某个库的 1.x,项目 B 要 2.x,都装进系统的 Python 里必然冲突。venv、conda、uv 都能给每个项目建一个独立的环境。

原理:一个虚拟环境就是一个独立的目录,里面有自己的 Python 解释器(或指向它的链接)和自己的第三方包目录。所谓「激活」,不过是把这个目录下的 bin 放到 PATH 的最前面,于是 python 和 pip 都指向它。又是 PATH。

工具

特点

venv

Python 自带,最轻量

conda

除了 Python 包,还能装 CUDA、编译器这类非 Python 的依赖,数据科学领域常用

uv

用 Rust 写的新一代工具,安装很快,同时管理 Python 版本、虚拟环境和依赖锁定

用法要点:which python 确认当前用的是哪个环境;依赖写进文件(requirements.txt 或 pyproject.toml),不要只装在自己的机器上。

Java:JDK 与 Maven

解决什么问题:JDK 是 Java 开发工具包,包含编译器 javac 和运行 Java 程序的虚拟机(JVM);Maven 负责 Java 项目的构建和依赖。

原理:

  • Java 代码先编译成字节码(.class 文件),再由 JVM 执行,所以同一份编译结果可以在不同的系统上运行。一台机器上常常装着好几个 JDK,JAVA_HOME 告诉各种工具用哪一个。

  • Maven 用 pom.xml 描述项目,每个依赖由「组织 : 名称 : 版本」三个坐标唯一确定。下载过的依赖缓存在本地的 ~/.m2/repository,下次直接用。

  • 构建是一条固定的生命周期:编译、测试、打包、安装、部署。执行后面的阶段,会自动先执行前面的。

最常用的几个用法:mvn package(编译、测试、打包);mvn dependency:tree(看每个依赖是从哪里引进来的,排查版本冲突时最有用)。

Git:版本管理

解决什么问题:记录代码的每一次修改,可以随时回退;多人同时修改同一份代码,也不会互相覆盖。拿到项目的第一步,通常就是 git clone。

原理:Git 的每一次提交,保存的是整个项目在那一刻的快照,再加上指向上一次提交的指针,提交连起来就是历史。分支只是一个指向某次提交的指针,所以创建分支几乎没有成本。代码在四个地方之间流动:

改动先进暂存区,提交后才进历史,推送后别人才看得到

最常用的几个用法:

命令

作用

git status

现在有哪些改动,分别在哪个区

git diff

具体改了什么

git log --oneline --graph

看提交历史和分支

git switch -c 新分支

新建并切换到一个分支

git merge、git rebase

两种合并分支的方式:前者生成一个合并提交,后者把提交逐个接到另一条线的后面

SSH:安全地连上远程机器

解决什么问题:登录远程服务器、从 GitHub 拉取私有仓库、让 VS Code 在远程机器上开发,走的都是 SSH 这条加密通道。

原理:推荐用密钥对登录,而不是密码:

私钥始终不离开你的电脑,网络上传的只是签名

私钥始终留在你的电脑上,网络上传的只是签名,所以比密码安全得多。反过来,第一次连接某台服务器时,客户端会记下它的指纹(存在 ~/.ssh/known_hosts 里),以后指纹变了就会警告,防止连上冒充的服务器。

最常用的几个用法:

命令

作用

ssh-keygen -t ed25519

生成一对密钥

ssh-copy-id 用户@主机

把公钥加进服务器的 authorized_keys,以后免密码登录

ssh -L 8080:localhost:80 用户@主机

端口转发:在本机打开 8080,就像访问服务器上的 80 端口

~/.ssh/config

给常连的机器起别名,记住用户名、端口和密钥

自己搭服务器时 SSH 该怎么加固,见 从零搭建网站。

VS Code:编辑器

解决什么问题:一个编辑器,靠插件支持各种语言,既能编辑本地的代码,也能编辑远程机器、WSL、容器里的代码。

原理:VS Code 本身只是一个编辑器外壳,两项设计让它几乎什么都能做:

  • 语言服务器协议(LSP):代码补全、跳转到定义、报错提示,都由每种语言各自的「语言服务器」提供,编辑器和语言服务器之间用统一的协议通信。所以同一个语言服务器,可以给不同的编辑器用。

  • 远程开发:用 Remote-SSH 连接远程机器(或者连 WSL、容器)时,VS Code 会在那一边装一个 VS Code Server。界面留在你的电脑上,读写文件、终端、语言服务器和大部分插件都在远程那一边运行,所以用起来和本地开发一样。

远程开发时,只有界面在本地,干活的都在远程

用法要点:命令面板(Ctrl/Cmd + Shift + P)可以找到所有功能。远程开发时,插件分为装在本地的界面类插件和装在远程的工作区插件,同一个插件可能要在两边各装一次。我常用的插件是远程开发、Python、Extension Pack for Java、Git Graph(提交历史可视化)和 Code Spell Checker(拼写检查)。

AI 编程助手

解决什么问题:Claude Code、Codex 这一类编程 Agent 能读代码、改代码、运行命令;前面那些安装和配置,也可以直接交给它。

原理:大模型本身只会根据上下文生成文字(它是怎么做到的,见 从线性回归到大语言模型)。编程 Agent 给它加上了工具,再放进一个循环里:

编程 Agent 是「模型决定、工具执行、看结果再决定」的循环

能力的上限取决于模型,日常的表现很大程度上取决于两件事:给它的上下文,和它能用来验证的工具。

用法要点:

  • 写一份项目说明:在仓库根目录放一份 AGENTS.md(Claude Code 读的是 CLAUDE.md),写清怎么启动、怎么测试、有哪些不能破坏的约定。它每次开工都会先读,相当于新同事的入职文档。

  • 让它能自己验证:让它跑测试、看输出,比只看它写的代码可靠得多。

  • 小步改,逐步看:每一步改动都看一眼 git diff,Git 是你的安全网。

  • 原理还是要懂:它会配环境,但环境出了问题,它也可能在错误的方向上越走越远。你懂 PATH、懂镜像和代理的区别,才能一眼看出它是不是在兜圈子。

Docker:把应用装进容器

解决什么问题:「在我电脑上明明能跑」。项目要用 PostgreSQL,与其在每台电脑上手动安装、配置一遍,不如把数据库连同它的环境打包好,一条命令启动。容器把环境本身变成了可以分发的东西。

原理:

镜像是一层层只读的文件系统,容器是在它上面运行的一个被隔离的进程
  • 镜像是分层的:Dockerfile 的每一步生成一层只读的文件系统。没变的层可以缓存、复用,所以改一行代码重新构建时,只需要重做后面几层。

  • 容器就是一个被隔离的进程:它和普通进程一样运行在宿主机的内核上,只是用命名空间(namespace)隔离了它能看到的文件、网络和进程,用控制组(cgroup)限制了它能用的 CPU 和内存。所以容器启动只要一瞬间,不像虚拟机要启动整个操作系统。

  • 在 Mac 上:容器需要 Linux 内核,所以 Docker Desktop、OrbStack 这些工具实际上先运行了一个轻量的 Linux 虚拟机,容器跑在它里面。和 WSL 是同一个思路。

最常用的几个用法:

命令

作用

docker run -p 5432:5432 postgres

启动容器,并把容器的 5432 端口映射到本机

docker ps、docker logs 容器

看有哪些容器在运行,看它的输出

docker exec -it 容器 sh

进到正在运行的容器里看看

docker compose up -d

按 compose.yaml 一次启动一组配合工作的容器,比如网站加数据库

容器删掉后,可写层里的数据也就没了。数据库文件这类要长期保存的数据,要挂载到卷(volume)上。

Kubernetes:把容器编排成集群

解决什么问题:一台机器用 Docker 就够了。机器一多,就要有人决定容器放在哪台机器上、挂了谁来重启、流量怎么分给多个副本、新版本怎么平滑上线。Kubernetes(简称 k8s)就是做这件事的容器编排系统。

原理:Kubernetes 的核心是声明式的:你只描述「想要的状态」,比如「这个应用要 3 个副本」;控制器不断比较想要的和实际的,发现不一致就动手调整,这个过程叫调和(reconcile)。

你只声明期望的状态,控制器负责让实际状态追上它

几个基本对象:Pod(一个或几个容器组成的最小调度单位)、Deployment(管理一组相同的 Pod,负责副本数和滚动更新)、Service(给一组 Pod 一个稳定的访问入口)。本地学习可以用 minikube 搭一个单机的集群。

最常用的几个用法:kubectl apply -f 文件(提交期望状态)、kubectl get pods、kubectl describe pod 名字(看它为什么起不来)、kubectl logs 名字。

小结:遇到问题,想哪条原理

开头说,出问题时要靠原理判断方向。下面这张表把常见的症状和对应的原理放在一起:

症状

先想到的原理

先做什么

装了却提示 command not found

Shell 按 PATH 找命令

which、echo $PATH

装了新版本,用的还是旧的

PATH 里先找到的先用

看 PATH 里目录的顺序

改了 ~/.zshrc 不生效

环境变量只往下传

source ~/.zshrc 或新开终端

下载很慢或失败

镜像换地址,代理换路径

有国内副本用镜像,没有用代理

终端里有代理,Docker 还是拉不下来

后台服务读不到终端的环境变量

单独给 Docker 配置

同事那里能跑,我这里报错

依赖解析出的版本不同

对比锁文件,或者用容器

两个项目的依赖互相冲突

每个虚拟环境是一个独立目录

每个项目一个环境

WSL 里 npm install 特别慢

跨系统访问文件要经过转换

代码挪到 ~ 下面

容器删了,数据也没了

可写层随容器一起删除

数据挂载到卷上

Pod 一直起不来

控制器在不断调和

kubectl describe 看事件

命令记不住没关系,随时可以问;这张表里的原理,值得自己记住。环境搭好之后,可以接着看 从零搭建网站,把自己的网站搭起来;想知道一次请求在网络上经过了什么,见 Life of a Request。

参考资料


全文评论

0

正在加载评论…