这是「把自己部署到互联网上」系列的第 25 篇。
从第 18 篇到第 24 篇,我一直在处理一个问题:
个人博客到底应该怎么部署?
这个问题看起来只是技术选型,但会直接影响后续维护。如果部署架构一开始就很散,后面新增项目、修改配置、迁移服务器、排查故障,都会消耗大量精力。所以这一阶段我没有急着把博客跑起来,而是先把部署方式理清楚。
这几篇大致走过三步:
- 先在 Conda 和 Docker 之间做选择
- 再复盘为什么放弃 Conda 部署,转向 Docker
- 最后确定用全局 Nginx 统一管理多个项目入口
到现在,我的基本方案可以概括为一句话:
Docker 管项目运行,Docker Compose 管项目内部服务,全局 Nginx 管公网入口。
动机是什么?
服务器安全阶段结束后,接下来考虑项目部署。但部署不是把代码拉到服务器上、执行一条启动命令这么简单。对一个准备长期运行的项目来说,至少要考虑应用运行环境、数据库、多服务启动、项目隔离、端口暴露、Nginx 入口、多项目共存、配置迁移和故障排查。
如果这些问题不提前想,很容易出现一个现象:
第一天项目跑起来了,一个月后配置开始分散,三个月后新增项目开始冲突,半年后自己也忘了当时怎么部署。到最后变得越来越不想维护了。
最早的选择是 Conda 和 Docker。我一开始挺喜欢用 Conda 管理环境。
它在本地开发和 Python 环境管理里很舒服,创建环境、切换版本、安装依赖都很直观。
如果只是临时跑一个 Python 项目,或者做数据分析、实验脚本,Conda 仍然很好用。
但我的个人博客不是临时脚本。它是要长期运行的项目,要有数据库,要通过 Nginx 对外访问,要配置 HTTPS,后面还要和个人官网、作品站共存。
Conda 与 Docker
Conda 管的是 Python 环境,但我的部署问题不只是 Python 环境。服务启动、数据库、日志、配置、环境变量、迁移、多项目组织,都需要另外处理。
所以我 不是因为 Conda 不能用才放弃它,而是因为它不匹配这次的长期目标。
转向 Docker 后,我看重的也不是“更高级”,而是它能把应用运行环境固化下来。
Dockerfile 记录项目如何构建,镜像记录运行环境,容器提供隔离边界,Compose 文件记录一组服务如何一起运行。
这比在服务器上手工创建环境、安装依赖、写启动脚本,更适合长期维护。它能让我更清楚地回答:
- 项目需要哪些服务
- 哪个服务对外暴露
- 数据库数据放在哪里
- 环境变量从哪里来
- 服务如何启动和停止
- 日志怎么看
- 以后换服务器怎么恢复
当然,Docker 也有成本。需要理解镜像、容器、网络、数据卷、端口映射和 Compose。
但这些成本换来的是更清楚的部署结构。
只用 Docker 还不够。一个真实项目往往不止一个容器。博客至少会有应用和数据库,后面还可能有缓存、后台任务、定时任务等。如果每个容器都用 docker run 手动启动,命令会越来越长,关系也很难维护。
Docker Compose
Docker Compose 的价值就是把项目内部服务组织起来。
应用、数据库、数据卷、环境变量、网络,都可以写在一个文件里。
这样项目不再是一堆手工命令,而是一个可描述的服务组。
以后回头看博客项目时,我能知道它有哪些服务、服务之间怎么连接、数据在哪里、端口怎么处理、启动命令是什么。
当项目用 Docker 和 Docker Compose 管起来后,下一个问题就是公网流量怎么进来。
Nginx 的位置
如果只有一个项目,可以用系统 Nginx,也可以用 Nginx 容器。但我的场景设计是多项目。
博客、官网、作品站后面都可能在同一台服务器上。
如果每个项目都自己暴露端口,入口会越来越乱;如果每个项目都自己管理 Nginx 和证书,维护会越来越分散。
所以我最后确定:
公网入口统一交给全局 Nginx。它负责接管 80 和 443,根据域名转发到不同项目,集中处理 HTTPS 相关配置,并通过公共 Docker 网络访问项目容器。每个项目内部仍然独立。
结构大概是:
公网用户
-> 全局 Nginx
-> 项目应用容器
-> 项目数据库容器
公网只认 Nginx,项目只提供内部服务,数据库只服务项目内部。
现在的部署架构可以总结为:
服务器
├── 全局 Nginx 容器
│ ├── 监听 80 / 443
│ ├── 管理域名配置
│ ├── 引用 HTTPS 证书
│ └── 代理到不同项目
│
├── 博客项目
│ ├── app 容器
│ ├── db 容器
│ ├── compose.yml
│ └── 数据卷
│
├── 官网项目
│ ├── app 或静态服务
│ ├── compose.yml
│ └── 构建产物
│
└── 公共 Docker 网络
├── global-nginx
├── blog-app
└── site-app
这个架构不复杂,但对我的个人服务器已经足够清楚。它解决了几个核心问题:
- 环境用 Docker 管
- 多服务用 Docker Compose 管
- 入口用全局 Nginx 管
- 数据库不暴露公网
- 项目之间保持边界
- 新增项目有固定接入方式
- 迁移时有目录和配置可依赖
小结:
回头看,这个阶段主要踩过三个判断坑。
第一个坑,是只看当前能不能跑
如果只看最快上线,Conda 很可能是最短路径。但个人博客不是只跑一天,不能只问今天怎么最快上线,还要问半年后怎么维护。
第二个坑,是把所有项目都当成单项目看
只有一个博客时,很多方案都能用。但以后如果还有官网、作品站和小工具,就要提前考虑入口统一、端口管理和证书集中,否则第一个项目图省事,后面会补债。
第三个坑,是以为容器化就是把所有东西都塞进容器
Docker 有用,但关键是边界清楚。项目服务容器化,数据库容器化但数据持久化,Nginx作为全局入口容器化,证书和配置挂载到宿主机目录。组合起来,才是可维护方案。
这一阶段开始前,我的目标是“把项目跑起来”。
现在目标变成了“以可维护、可迁移、可扩展的方式把项目跑起来”。前者追求速度,后者追求结构。
如果只是练手项目,速度优先没问题;但这次我要搭的是长期个人技术阵地,结构更重要。
下一阶段要解决 HTTPS。部署架构解决的是项目怎么跑,HTTPS 解决的是网站如何更可信地对外访问。
接下来会涉及为什么个人网站需要 HTTPS、SSL 证书怎么申请和部署、Nginx 如何配置 HTTPS、通配符证书和 acme.sh 怎么管理多域名证书,以及证书续期怎么处理。
到这里,服务器安全边界有了,部署架构也有了。
下一步,就是让这个入口具备 HTTPS 访问能力。
本文由 楸木 原创,转载请注明出处。