ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Docker私有化部署Halo:打造完全可控的个人博客系统

用Docker私有化部署Halo:打造完全可控的个人博客系统 最近花了一晚上用Docker把Halo私有化部署到自己的服务器上终于把折腾写作平台这事儿一次性了结了。Halo本身就是个定位很清晰的开源博客系统配上Docker这种跑法数据全部掌控在自己手里主题、插件、写作界面都足够克制没有一堆我用不上的社交按钮和推广位。这篇文章就把我从零开始到跑起来、再到日常维护的整个实操过程整理出来包括一些踩坑经验给同样想搭建一个专属写作环境的朋友做个参考。适合谁看如果你受够了SaaS平台动不动改版、加广告、锁数据或者只是想要一个干净、能专注写东西、又能完全掌控内容的博客那这套方案基本是性价比最高的选择。下面内容不涉及任何高深理论跟着一步步来就行。1. 为什么最终选Halo我对写作平台的真实需求1.1 写作这件事需求其实远比你想象的简单我前前后后用过的博客方案不算少。早期的静态博客确实轻但每次写篇文章都得走本地编辑器、push、CI构建这一套流程新鲜劲儿过去之后就会觉得麻烦尤其在手机上想改个错别字都费劲。后来用过的几个动态博客平台倒是省事了可平台方一改版整个后台和前端就面目全非几个月不登录甚至要找半天自己文章在哪儿。更核心的问题在于数据归属。文章是写在别人服务器上的导出倒是能导出但每次都让我有种不安定感。时间越长我越清楚自己需要的其实是一个极简的写作阵地打开后台就是一个清爽的编辑器写完保存、发布内容完全在我自己的数据库里前端展示由我决定不需要平台给我推送“推荐阅读”“热门标签”之类的东西。1.2 在几种主流方案里做的取舍我认真对比过几类方案各有各的优点也各有让我放弃的理由。纯静态博客生成器比如Hexo、Hugo这类胜在部署成本低、访问速度快纯静态文件扔到任何服务器或CDN上都能跑。缺点也很明显写作流程不够顺滑——本地编辑、命令生成、部署中间环环依赖换设备写作更是麻烦。对我这种偶尔想用iPad写两句的人来说这个流程太重了。传统动态博客程序比如WordPress功能是真的强插件生态也丰富但很多功能对我来说都是过剩的。后台各种仪表盘、评论管理、媒体库、用户系统开局就一堆东西需要配置总让我觉得“这玩意儿是个系统不是个写作工具”。加上更新频率高、安全补丁要跟进维护成本并不低。Halo属于Java生态里比较成熟的博客系统界面干净写作体验接近在线文档编辑器。最关键的一点是它的扩展性没有好到让人迷失默认体验就很克制。后台没有满屏的统计和推送文章、页面、附件、主题这几个核心模块一目了然。配合Docker部署之后更新升级就是拉一个镜像、重建容器的工夫。1.3 用Docker私有化部署的真实理由很多人会有疑问直接在生产环境装一个Java应用不就行了为什么非要多套一层Docker原因很实际。第一环境隔离。Halo依赖Java运行环境系统版本、JDK版本稍微有点出入本地跑起来就全是坑。Docker镜像把所有依赖都打包好了我只需要一个能跑容器的Linux环境不需要在宿主机上折腾JDK。第二升级和回滚方便。以后版本迭代我只需要改一下镜像版本号然后重新创建容器旧版本镜像还在本地出问题随时退回去。第三备份的时候不用考虑复杂的依赖关系。我只需要把容器的数据卷目录拷走或者做数据库的定时备份恢复起来思路非常清晰。还有一点可能很多人没意识到Docker部署能让你的数据路径变得完全可控。我会把数据卷挂载到宿主机固定目录下数据库文件、上传的图片、日志、配置全部落在规划好的目录里。以后不管换机器还是迁移直接把整个目录搬走就能恢复不需要再回忆“当初我到底装在哪里了”。2. 部署前的环境准备与镜像选择2.1 服务器基础环境和Docker安装如果你手头已经有一台Linux服务器哪怕配置很一般1核2G内存就足够起步跑Halo是绰绰有余的。Halo 2.x默认内置H2数据库时内存占用大概在300到500MB之间如果换成PostgreSQL也基本不会超过1GB。对于个人博客来说完全在预算范围内。以Ubuntu系统为例Docker的安装其实已经非常简单了。我习惯用官方脚本的方式但生产环境还是推荐通过apt仓库来装这样后续升级更方便。# 安装依赖包 sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release # 添加Docker官方GPG密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加软件源 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装Docker sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin安装完成之后记得执行一下验证命令sudo systemctl enable docker sudo systemctl start docker sudo docker run hello-world看到hello-world镜像能正常拉取、容器能正常退出Docker环境就算就绪了。如果你用的是CentOS、Debian或者OpenWrt这类系统安装命令会稍微不同但整体思路一样建议直接参考对应系统的官方文档不要照抄一个命令就完事。2.2 目录规划从一开始就别乱Docker部署最忌讳的就是数据卷挂在哪儿全凭心情时间一长自己都忘了容器里哪些目录是重要的。我推荐的目录结构是这样/opt/halo/ ├── docker-compose.yml ├── .env ├── halo/ # Halo工作目录里面包含 themes、logs、uploads 等子目录 └── postgres/ # 如果使用PostgreSQL数据库文件放在这里所有博客相关的文件都收敛在这一个父目录下备份的时候直接打包这个目录就行。这里有一点要提醒新手不要把数据卷直接挂到root用户的家目录或者其他和系统文件混在一起的位置不然哪天系统清理垃圾文件很可能误伤数据。2.3 镜像版本怎么选稳定优先别追新Halo的官方仓库在Docker Hub上叫halohub/halo2.x系列是目前的主流版本。在选择具体tag时我的原则是除非需要新功能否则不追最新版本优先选稳定版。# 查看远程可用的版本列表 docker search halohub/halo # 或者直接访问镜像仓库页面查看标签信息在docker-compose.yml里镜像版本号我一般写成具体的小版本比如halohub/halo:2.17.0而不是写latest原因很简单latest会在你执行docker compose pull的时候自动拉到最新版本这种隐式升级一旦碰到breaking change你可能连早餐都没吃完就得开始排查故障。写成固定版本号升级变成一次显式的、可控的操作配合备份再做升级才是稳妥的流程。数据库方面极简起步可以用Halo默认的内置H2数据库它零配置、启动快几百篇文章毫无压力。但如果愿意多花一点点功夫我更推荐直接上PostgreSQL毕竟生产环境的数据存储用文件型数据库始终让人觉得不够踏实后面做定时备份、数据恢复也更容易理解和操作。3. 用docker-compose把Halo跑起来3.1 最小化compose文件是什么样的先给一个最简单、可以立刻跑起来的配置。如果你只是想先体验一下Halo或者对数据库没有特别要求那就用这个# /opt/halo/docker-compose.yml version: 3.8 services: halo: image: halohub/halo:2.17.0 container_name: halo restart: always ports: - 8090:8090 volumes: - /opt/halo/halo:/root/.halo2 environment: TZ: Asia/Shanghai就这么点东西执行启动就能得到一个可用的博客系统cd /opt/halo docker compose up -d docker compose logs -f halo # 跟踪容器日志首次启动时Halo会在/root/.halo2对应宿主机/opt/halo/halo目录下初始化配置文件和H2数据库。等日志出现类似“Started HaloApplication”的字样说明服务已经起来了。此时访问http://服务器IP:8090就能进入初始化安装页面。3.2 带上PostgreSQL的完整配置如果想把数据存储放到PostgreSQL里就在这个目录下再建一个postgres数据目录并在compose文件里加上数据库服务# /opt/halo/docker-compose.yml version: 3.8 services: postgres: image: postgres:16.3 container_name: halo-postgres restart: always environment: POSTGRES_DB: halo POSTGRES_USER: halo POSTGRES_PASSWORD: changeme_strong_password volumes: - /opt/halo/postgres:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U halo -d halo] interval: 10s timeout: 5s retries: 5 halo: image: halohub/halo:2.17.0 container_name: halo restart: always depends_on: postgres: condition: service_healthy ports: - 8090:8090 volumes: - /opt/halo/halo:/root/.halo2 environment: TZ: Asia/Shanghai SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.postgresql.Driver SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/halo SPRING_DATASOURCE_USERNAME: halo SPRING_DATASOURCE_PASSWORD: changeme_strong_password有一个很重要的细节Halo在初始化时如果检测到配置了数据库连接信息就不会再让你选择H2数据库而是直接使用PostgreSQL。所以要让上边的环境变量在第一次初始化前就生效这个顺序别搞反。我见过有人先启动Halo把H2库初始化完了再回头想切到PostgreSQL结果数据迁移折腾了好一阵。启动方式一样cd /opt/halo docker compose up -d这次postgres容器会先启动通过健康检查之后halo容器才会开始初始化避免出现应用先起来了但数据库还没准备好的竞态问题。3.3 初始化安装时容易忽略的细节首次打开Halo的安装页面会要求设置管理员用户名、密码和邮箱。这个环节没有太多玄机但有两个要点值得注意管理员的初始密码一定要设置成强密码。因为博客系统本身暴露在公网弱密码就是给扫描工具和暴力破解送人头的。安装完成之后系统会直接进入后台你可以随时在“用户”设置里修改密码或者绑定登录方式。Halo 2.x的初始化过程中选择数据库类型的界面只有在没有检测到外部数据源时才会出现。如果你通过环境变量配置了PostgreSQL就不会看到数据库类型选择步骤了直接进入管理员信息设置页面。初始化完成后进入后台第一件事我建议先把默认的主题换成自己真正要长期用的主题然后把站点名称、Logo、页脚信息改掉。空跑一下发布一篇测试文章确认整个链路——前台访问、后台编辑、文章发布、图片上传——都畅通无阻再开始安心地写正式内容。4. 极简配置把Halo调教成顺手的样子4.1 主题选择克制是第一位Halo官方主题市场里有不少主题风格分化很明显。有的主题看起来功能特别丰富首页各种区块、轮播、归档、标签云仿佛什么都有。实际上手你会发现那些效果大多需要配一堆设置项写文章时想的不是内容本身而是“这个内容在前台看起来会不会太挤”。我个人的主题选择标准非常功利首页能清晰展示文章标题摘要就行正文排版干净移动端阅读舒服。目前用的是一款官方出的极简主题首页就是文章列表加简单分页没有多余的视觉元素阅读时正文宽度也控制得很好。这种主题还有一个额外好处——几乎没有需要长期维护的设置项换服务器、恢复备份之后几分钟就能恢复出和原来一样的展示效果。如果你有动手能力Halo主题本质上就是一套模板支持在后台直接编辑主题源码加一行自定义CSS、改一个HTML片段都很方便。这也是私有化部署的爽点前端最终长什么样由你一个人说了算。4.2 后台和编辑器设置减少一切干扰Halo 2.x的默认编辑器是所见即所得模式富文本整体布局和语雀、Notion这类在线文档比较接近左边工具栏、中间编辑区、右边属性面板用起来基本不需要重新学习。我自己的使用习惯是关闭不必要的功能模块。后台的“评论”模块如果我暂时不想开放就直接不启用插件也是装一个用一个绝不装一堆用不上的插件在后台吃灰。附件命名规则早定好。上传图片时我习惯按年/月/文件名的格式组织Halo的附件管理支持按目录分组一开始分好类后面写文章配图时找起来效率高很多。文章分类和标签克制使用。不要为了逼自己“体系化”而建一二十个分类。分类一旦多了每次写作都纠结该归到哪反而成了负担。4.3 访问链路Nginx反代、HTTPS和域名绑定默认情况下Halo监听8090端口但直接暴露这个端口写文章体验不够好也不太安全。更常见的做法是用Nginx做反向代理把“当前域名443端口”的请求转发到内部的8090端口然后再申请一个免费的SSL证书把HTTPS加上。这样用户访问的就是一个标准的HTTPS网站而不是一把梭的IP加端口。反代配置核心部分长这样server { listen 80; server_name blog.example.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { proxy_pass http://127.0.0.1:8090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }证书部分如果不想折腾acme.sh也可以用Caddy这类自动申请证书的反代工具配置会更简洁但Nginx的思路还是最通用的网上资料也最多。注意配置HTTPS时X-Forwarded-Proto这个请求头一定要带上Halo会根据它来生成正确的链接。不然你会看到网站前端能打开后台却因为请求协议判断错误而出现登录或者资源加载异常。4.4 日常写作流程打开后台直接开写配置完成后我的写作流程变成了浏览器输入域名进入后台新建文章写字传图发布。不需要打开任何本地编辑器不需要处理命令。Halo的自动保存机制是周期性的中途意外关掉页面草稿也不会丢。这一点在我实际使用中很加分谁都有过写到一半浏览器崩溃、或者临时要去开会直接合上笔记本的经历。手机端我也试过直接用浏览器登录后台写东西虽然不比本地Markdown编辑器输入体验好但应急改稿、回复评论是没有问题的。配合PWA特性Halo本身也能把站点“安装”到手机桌面进出体验更像一个独立App。5. 数据备份、恢复与版本升级的实操套路5.1 备份思路分清哪些是数据、哪些是行为痕迹私有化部署的所有好处都建立在“数据安全”这个前提上所以备份是绝对绕不开的一环。需要备份的东西其实不多主要就两块数据库如果用了PostgreSQL整个PostgreSQL数据目录就是你的核心资产。文章内容、用户信息、评论、设置项全在里面。工作目录也就是挂载到容器里的/opt/halo/halo目录。里面包含上传的附件图片、主题文件、日志等。这些属于“内容资产”丢了也能通过代码仓库和下载包补回一部分但上传的图片丢了就很难恢复。所以我的备份策略非常简单粗暴既然这两个目录都在/opt/halo下面那就直接对整个目录做定期压缩备份# 手动备份 tar -czf /backup/halo_backup_$(date %Y%m%d%H%M).tar.gz /opt/halo # 配合cron定时任务 0 3 * * * /usr/bin/tar -czf /backup/halo_backup_$(date \%Y\%m\%d).tar.gz /opt/halo /backup/backup.log 21 find /backup -name halo_backup_*.tar.gz -mtime 7 -delete这个思路的好处是恢复时不需要先装好Halo再从数据库单独恢复直接把整个打包目录解压回原位置然后重新执行docker compose up -d应用和数据库就一起回来了。对于个人博客来说这种整目录备份虽然简单粗暴但确实最不容易出错。注意定时任务中%符号在crontab里有特殊含义需要写成\%或者把脚本放到一个单独的.sh文件里再调度我第一次写的时候就被这个坑过备份目录里出现了不少“莫名其妙命名”的文件。5.2 恢复流程不能只备份不演练光备份不演练等于没备份。我建议每做完一次备份就顺手在本地或者另一台测试机器上验证一次恢复流程。具体做法把备份包解压到新的服务器/opt/halo目录下。安装好Docker进入该目录。执行docker compose up -d。等容器起来之后用初始化管理员账号登录后台抽查两篇文章和几张图片是否正常显示。确认无误再把这个“恢复演练”的服务器收起来避免和正式环境混淆。这套流程曾经帮我避免过一次事故。有次升级Halo版本后页面打开显示异常我一开始以为是自己配置问题后来回头翻日志才发现是某个插件不兼容新版本。因为做过完整的恢复演练我可以很从容地把docker-compose.yml里的版本号改成升级前的镜像版本然后重新构建容器整套回滚操作不到五分钟就完成了。5.3 升级Halo版本的规范化操作步骤Halo社区更新节奏还是比较活跃的基本每个小版本都有一些bugfix和体验优化。版本升级我建议采用下面这个流程每一步都在日志里留下记录# 进入部署目录 cd /opt/halo # 停掉容器 docker compose stop halo # 备份当前数据目录升级前必要的保险 tar -czf /backup/pre_upgrade_$(date %Y%m%d%H%M).tar.gz /opt/halo # 修改docker-compose.yml里的镜像版本号 # 拉取新镜像并重新创建容器 docker compose pull halo docker compose up -d halo # 检查日志输出确认启动成功 docker compose logs -f halo升级最忌讳的事情就是跳过备份直接拉最新镜像。有一些版本更新会涉及数据结构迁移虽然理论上Halo自己会处理但万一迁移过程出问题你没有回滚的余地。备份只需要几十秒和一点点磁盘空间这个成本无论如何都不能省。升级完成之后我通常会顺手做一个小检查打开前台首页确认文章列表和主题渲染正常登录后台看一下设置项有没有丢失再打开一篇含图片的文章确认附件路径没有发生变化。全部通过这次升级才算真正完成。6. 实际运行阶段值得关注的几个细节6.1 内存占用表现与容器资源限制Halo作为Java应用启动阶段的CPU和内存占用会短暂冲高运行平稳后会回落。个人博客场景下1核2G内存的服务器足够从容。如果服务器上同时还跑了其他容器建议给Halo配置一个合理的资源上限防止某个容器异常占用拖垮整机。在compose文件里可以直接加上部署层的资源限制deploy: resources: limits: cpus: 1.0 memory: 1G实测下来带PostgreSQL的Halo稳态内存占用在500MB到800MB之间这个限额既能覆盖日常峰值又不会把资源余量吃光。如果你用的是1G内存的小机器需要再斟酌一下限额数值适当放宽到1.2G可能更稳妥具体以你自己服务器的实际情况为准。6.2 日志轮转别让日志文件撑爆磁盘Docker容器一旦长期运行日志文件会一直增长。刚开始可能没感觉但如果你后台偶尔出现报错日志会迅速累积时间长了完全有可能把磁盘占满。配置一个docker daemon.json的日志轮转是最省心的方案{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 5 } }这个配置是针对Docker守护进程全局生效的改完之后需要重启Docker服务。我的建议是趁部署初期就把它配置好避免以后某天发现磁盘莫名满了还得回头清理容器日志。6.3 日常健康检查与可用性维护Halo是Java应用到目前为止我跑了一段时间稳定性没什么可挑的。但“稳定”不等于“不用管”日常维护里我至少会做三件事每周看一眼容器状态和日志确认没有异常的WARN或ERROR。每个月手动触发一次备份并简单检查备份包大小是否正常。关注Halo官方社区或者GitHub仓库的Release动态遇到重要版本更新就规划升级窗口而不是骤然更新。如果你把博客当成长期写作阵地这些维护工作不需要多频繁但一定要养成习惯。私有化部署最大的优势是自由最大的代价是责任——服务器安全和数据安全都得自己扛好在Halo本身把大部分运维复杂度都吃掉了剩下的活儿并不多。6.4 安全加固的几个小动作既然博客暴露在公网有几个基本的安全动作建议部署当口就做完修改SSH端口并禁用密码登录改用密钥登录。这是服务器层面最基本的底线不只是为了博客安全。不要用裸IP加端口访问后台。能用域名尽量用域名能套HTTPS一定套HTTPS至少避免被扫描工具直接识别出应用类型。后台管理员账号不要和日常写作账号混用。Halo支持多用户虽然个人博客可能只有你自己但万一要邀请朋友一起写给普通账号即可不要共享管理员密码。操作系统层面的防火墙只放行必要端口。Nginx的80/443、SSH端口放行8090端口如果不希望外部直接访问就只在防火墙里放行内网或干脆不放行利用Nginx做代理访问。做完这些小动作个人博客被“莫名其妙入侵”的概率会大幅下降。不是说这样就能绝对安全而是把这些基础门槛立起来之后绝大多数脚本扫描就会被挡在门外。7. 写在最后这套方案的真实上限在哪里如果你只是需要一个能安静写字、数据归属明确、前端完全可控的个人博客Halo配合Docker这套组合技术门槛低、后期维护成本也不高完全称得上“写作神器”。它不会逼你用Markdown、不会强制你折腾部署流水线也不会在你后台塞满没用的推荐位和统计图表。从长远看这套方案的成长空间也足够。文章多了之后可以继续扩展插件、接入对象存储、把附件放到专用存储服务里甚至以后想给博客加个独立的评论系统也都不算难事。现阶段先把写作这件事本身处理好比什么都重要。如果你也准备动手部署我的建议是别纠结太多架构上的事情先把最小的compose文件跑起来登进去写一篇带有图片的测试文章感受一下整个链路的顺畅度。跑通之后再按本文提到的备份、反代、HTTPS这些步骤一步步完善。好的工具不是一上来就配置齐全的而是用着用着觉得“这里可以更好”然后花几分钟微调出来的样子。我现在的博客就是这样一个状态——简单、稳定、写了很久也没遇到过劝退的麻烦。
返回列表