Docker 构建速度总是上不来?先看看镜像层和上下文
很多人第一次写 Dockerfile 时,只关心“能不能构建成功”。等到项目稍微变大一点,就会开始抱怨镜像构建太慢。这个时候常见的反应是去找加速源、换基础镜像、甚至怀疑 Docker 本身不稳定。但经验告诉我,真正拖慢本地构建速度的,很多时候不是网络,而是你自己把缓存层次和构建上下文写得太随意了。
很多人第一次写 Dockerfile 时,只关心“能不能构建成功”。等到项目稍微变大一点,就会开始抱怨镜像构建太慢。这个时候常见的反应是去找加速源、换基础镜像、甚至怀疑 Docker 本身不稳定。但经验告诉我,真正拖慢本地构建速度的,很多时候不是网络,而是你自己把缓存层次和构建上下文写得太随意了。
刚开始用 Docker Compose 做本地联调时,最容易犯的错误就是还在用宿主机思维理解容器之间的访问关系。比如 API 容器去连数据库时,配置里还写 127.0.0.1,结果明明数据库已经启动了,应用还是一直报连接失败。这个问题其实不是 Compose 配错了,而是没有建立起一个很关键的认知:同一个 Compose 网络里,服务名本身就是彼此访问时最方便的地址。
很多人刚在 Mac 上装好 Docker Desktop,第一反应都是先拉镜像、起容器、跑服务。等到容器越来越多,才开始发现编译慢、数据库启动慢、桌面风扇狂转、系统内存被吃掉一大块。这个时候再怀疑镜像源、怀疑命令写法,往往已经偏题了。对 Mac 环境来说,资源限制配置本身就是本地开发体验的一部分。
用 slice 实现栈和队列几乎是 Go 入门练习里的必做题。因为语法不复杂,append 也足够顺手,所以很多人会觉得这类题没什么难度。但真正把它写进稍微像样一点的小程序里时,你会发现问题并不在“会不会写”,而在“有没有考虑清楚容量变化、出队后残留引用、空结构操作这些细节”。
很多人刷算法题时喜欢挑复杂题,觉得这样更有挑战。但从练代码基本功的角度看,真正值得反复写的,往往是那些规则简单、边界丰富的小题。递归和二分查找就是这种题型的代表。它们不靠花哨技巧取胜,却非常适合训练你是不是能把停止条件、输入约束和函数职责写清楚。
Go 标准库里的 encoding/json 已经够用了,平时做接口、配置读取、消息体解析,大多数场景都离不开它。问题在于,很多初学阶段写出来的 JSON 代码“能跑”但并不稳。真正进入接口联调或者服务之间互相调用时,字段命名、默认值、空值含义这些细节很快就会冒出来。
刚开始写 Go 的时候,遇到文本拼接最容易想到的还是 +。代码少时当然没问题,但一旦进入循环、日志拼接、模板前处理或者接口响应组装,这种写法很快就会让代码既不清晰,也不够稳妥。真正开始做一些小工具之后,我发现自己最常用的其实不是花哨技巧,而是把 strings.Builder 和 bytes.Buffer 的边界分清楚。
Go 里的方法接收者,看起来是一个很小的语法点,但一旦项目写久了,你会发现它会直接影响代码语义。很多人会背口诀:能不用指针就不用指针,或者一旦要改值就用指针。可真正到工程里,光背口诀还是不够。
容器化本地开发有一个很常见的问题:服务虽然都起来了,但互相调用时用的地址特别随意。今天写死一个容器名,明天改成宿主机端口,后天又因为换了 compose 文件把连接地址全部推倒重来。结果是,本地联调环境虽然能跑,却总和线上长得不太一样。
刚开始用 Sequelize 的时候,模型定义很像一件机械活:写字段、配表名、导出模型,就结束了。可项目一大,模型之间开始出现 belongsTo、hasMany、belongsToMany 这类关联之后,我越来越觉得“模型怎么注册、谁先初始化”已经不是小事了。