基于 Docker 和 Nginx 部署 Shlink
声明:本文由 AI 生成。

【运维复盘】基于 Docker Compose 与 Nginx 构建 Shlink 高性能自建短网址服务(含单域名路由与 Nginx 底层坑点排查)
1. 前言与架构设计
在自建短网址服务时,Shlink 因其干净轻量、原生 API 支持以及强大的统计功能成为了首选。
为了保证系统的隔离性、易维护性以及未来更换 VPS 时的零成本迁移,本次部署采用 Docker Compose 容器化方案,并结合宿主机已有的 Nginx + Cloudflare SSL 证书 实现 HTTPS 卸载与精准反向代理。
系统整体架构
- 数据库层 (
shlink-db):PostgreSQL 15 (Alpine 镜像),数据持久化挂载至宿主机。 - 后端 API 层 (
shlink-backend):Shlink 官方核心服务,监听本地127.0.0.1:8080。 - 前端 Web 面板 (
shlink-frontend):Shlink 官方 Web Client,监听本地127.0.0.1:8081。 - 反向代理层 (Nginx):宿主机 Nginx 统一监听 443 端口,通过单域名正则分流,将前端页面与后端 API 路由至对应的本地容器端口。
2. 环境准备与项目初始化
首先在 Cloudflare 中,新建一条 DNS 记录,让域名 s.kukmoon.com 指向 VPS 的 IP 地址。
第二,在 VPS 宿主机上创建专门的项目目录,用于统一存放 Docker 配置与持久化数据库文件。
1 | |
3. 部署 Docker Compose 服务栈
在 /opt/shlink 目录下创建 docker-compose.yml 配置文件:
1 | |
踩坑记录:镜像拉取失败(
Forwarding failure)
在启动容器时若遇到 Docker Hub 无法连接的问题,可以通过修改/etc/docker/daemon.json配置可靠的公共 DNS(如1.1.1.1或8.8.8.8),并执行systemctl restart docker重启 Docker 服务解决。
拉取镜像并后台启动容器:
1 | |
4. Nginx 单域名精细化反向代理配置
为了让前端管理面板与后端短网址跳转共用同一个域名(s.kukmoon.com),我们需要利用 Nginx 的正则表达式匹配,将不同路径的请求路由至对应的端口。
在 /etc/nginx/sites-available/s.kukmoon.com.conf 中写入如下配置,并创建软链接至 sites-enabled:
1 | |
5. 核心排坑复盘:域名哈希表内存溢出(server_names_hash)
问题现象
配置完成后,重新加载 Nginx 并访问 [https://s.kukmoon.com](https://s.kukmoon.com),浏览器地址栏虽然是 s.kukmoon.com,但页面展现的却是同台 VPS 上另一个站点(博客)的内容。
通过本地 curl -vk --resolve s.kukmoon.com:443:127.0.0.1 [https://s.kukmoon.com](https://s.kukmoon.com) 测试发现,Nginx 在接收到 HTTPS 请求时,忽略了 s.kukmoon.com 的配置块,直接退化(Fallback)交给了默认站点(第一配置站点)处理。
原因深度剖析
在排除通配符拦截和端口配置冲突后,确认根因在于 Nginx 的域名哈希表内存溢出。
当 VPS 上配置的子域名数量较多或域名字符较长时,Nginx 默认的域名哈希桶空间(server_names_hash_bucket_size,默认通常为 32 或 64 字节)会被挤爆。塞满后,Nginx 会在内存中静默放弃建立新子域名的索引,且不抛出任何语法报错,导致请求无法精准匹配到指定的 server_name。
根治方案
修改主配置文件 /etc/nginx/nginx.conf,在 http { ... } 上下文内显式扩大域名哈希表的内存容量:
1 | |
修改完成后,测试并重载 Nginx:
1 | |
页面即刻恢复正常,顺利加载 Shlink Web Client 前端管理界面。
6. 服务初始化与 API 绑定
- 生成 API 密钥:
在 VPS 终端执行以下命令生成 Backend API 访问 Key:
1 | |
- 连接面板:
-
浏览器访问
[https://s.kukmoon.com](https://s.kukmoon.com)进入 Web 面板。 -
点击 Add a Server:
-
Name: 自定义服务名称(如
My Shortener) -
URL:
[https://s.kukmoon.com](https://s.kukmoon.com) -
API Key: 粘贴刚才生成的 API 密钥。
-
保存后即可开启完全自主可控的短网址生成与数据分析之旅!
7. 定位访客位置(可选)
声明:我用不到这个,所以没做,在此仅记录步骤供读者参考。
Geodata(地理位置数据),在 Shlink 中是用来定位访客位置的。
它的作用是:当有人点击你的短链接时,Shlink 可以通过点击者的 IP 地址,自动分析出他来自哪个国家、哪个城市,并在 Shlink Web Client 前端面板的统计地图里直观展示出来。
Shlink 默认使用的是全球最大的 IP 数据库提供商 MaxMind 提供的免费 GeoLite2 数据库。因为 MaxMind 要求使用者必须注册免费账号并生成密钥才能下载,所以我们需要在 MaxMind 网站上免费注册并获取一个 License Key(许可密钥)。
整个操作非常简单,只需要 5 分钟。
第一步:注册 MaxMind 免费账号
- 打开 MaxMind 的 GeoLite2 注册页面:https://www.maxmind.com/en/geolite2/signup
- 填写注册表单(支持任意邮箱,按照要求填入姓名、邮箱和公司/个人信息等)。
- 提交后,MaxMind 会发送一封确认邮件到你的邮箱。打开邮件,点击里面的验证链接设置账号密码。
第二步:生成 GeoIP License Key (密钥)
- 登录 MaxMind 控制台。
- 在左侧菜单栏或 User Menu 中,找到并点击 Account -> Manage License Keys(管理许可密钥)。
- 点击 Generate new license key(生成新的许可密钥)。
- 系统会弹出一个配置提示:
- License key description:随手填一个名称(例如:
Shlink GeoIP)。 - Will this key be used for GeoIP Update?:勾选 Yes。
- 点击 Confirm(确认)。
- 【非常重要】 页面会展示生成出来的 License Key(一串由字母和数字组成的长密钥,如
a1b2c3d4e5f6...)。
- 请立刻把这串密钥复制保存好。因为页面一旦刷新,出于安全考虑,这串密钥就再也看不到明文了!
第三步:把 License Key 填入 Shlink 的配置文件
拿到了密钥,我们只需要把这个密钥作为环境变量告诉 Shlink 即可。
- 登录你的 VPS 终端,编辑之前创建的
/opt/shlink/docker-compose.yml文件:
1 | |
- 在
shlink-backend服务的environment:列表下,增加一行GEOLITE_LICENSE_KEY:
1 | |
- 保存并退出编辑器(
Ctrl + O-> 回车 ->Ctrl + X)。
第四步:重启 Shlink 容器生效
在 /opt/shlink 目录下,运行下面这行命令重新应用配置:
1 | |
验证效果
配置完成后,当有别人通过你的短链接访问网站时:
- Shlink 后台会自动去 MaxMind 官方下载/更新 IP 地理位置数据库。
- 打开你的短网址管理面板(
[https://s.kukmoon.com](https://s.kukmoon.com)),进入某个短链接的 Visits(访问统计) 页面,你就会看到非常精美的世界地图分布,以及访问者来自哪个国家、城市、甚至 ISP 运营商的详细记录了!
如果不配置这个密钥,Shlink 依然能正常缩短和重定向链接,只是访问统计里会丢失城市/国家数据,所以配置上体验会更好!
图片版权
题图:AI 生成。
头图:https://pixabay.com/photos/sea-rocks-mountains-croatia-coast-6948569/
