Halo 简介

Halo 是一款现代化的开源博客/CMS系统,具有快捷部署和较多漂亮主题模版的特性,深受很多用户喜欢,Halo 还提供了完善的 Content API 和 Admin API,这让用户可以用于开发单页面模板,微信小程序,各种系统插件等。Halo 的官方网站地址是 https://halo.run

写在前面

Halo 的单机部署非常简单,在具有 JRE 的操作系统里,只需要一条命令即可完成部署,自身包含 H2 Database;并且 Halo 还提供了基于 docker 的容器镜像,同样单机环境一条镜像启动命令即可完成部署。同时 Halo 为了支持较大规模的访问请求,也支持采用外置 MySQL 数据库和 Redis 作为缓存组件。官方文档提供了Docker Compose配置文件,可以同时创建 Halo + MySQL + Redis 实例,参考链接地址:https://v1.legacy-docs.halo.run/getting-started/install/other/docker-compose
KubeSphere 是一款在 Kubernetes 之上构建的面向云原生应用的分布式操作系统,完全开源,支持多云与多集群管理,提供全栈的 IT 自动化运维能力,支持 DevOps 工作流和微服务治理等等,总之 KubeSphere 是一款超厉害的 kubernetes 发行版软件平台。
既然 Halo 的部署已经如此之简单了,为啥还要折腾将 Halo 部署在 KubeSphere 平台之上,对于个人建站来说多少有点“千里马拉犁耙”感觉。其实主要还是因为 Halo 的部署可以结合一个标准的 web 集群所依赖的部分组件,而且目前网上关于 halo 在 Kubernetes 上部署的文章很少,几乎没有,个人也是抱着学习的心态认为在 kubesphere 上部署 Halo 可以更好的帮我们了解 kubesphere 平台的一些操作使用技巧,因而写了这篇文章。在 kubephere 官方文档 快速入门 章节有一篇《创建并部署 WordPress 》,作为 kubesphere 入门教程,这篇文档写的已经相当不错,但是我个人觉得还需要一个当对于部署 wordpress 来说进阶版的操作教程,这也是写这篇文章的另一个初衷,在 kubesphere 上部署 halo,涉及到的知识点除了包含 MySQL 数据库之外,还增加了 Redis 缓存组件,并且还较部署wordpress 那篇文档增加了 ConfigMap 和 初始化容器(Init Container)的使用。
本文全程参考该 kubesphere 官方文档《创建并部署 WordPress》的写作手法,

部署架构

![image-1g)
本次部署组合采用 Halo + MySQL + Redis。Halo 作为前端服务,MySQL 作为后端数据库,Redis 作缓存。在 kubesphere 平台上创建一个项目,该项目下创建三个服务,分别是 halo-sevice,mysql-service 和 redis-service,具体服务包含内容如下:

image-1652367133322

准备工作

KubeSphere 需要提前安装好,安装文档参考 kubesphere 社区文档。kubesphere 安装完成之后,创建企业空间、项目、用户和角色。这里我们已经创建好了名称为 demo-workspace 的企业空间,并在该空间里创建了名称为 halo 的项目,创建了具有该项目操作权限的用户。

安装部署

步骤1:创建密钥

创建 halo 服务所需密钥

环境变量 SPRING_DATASOURCE_PASSWORDSPRING_REDIS_PASSWORD 是分别是 halo 服务连接到 MySQL 数据库与 halo 服务连接到 Redis 的密码。在此步骤中,需要创建一个密钥来保存,以避免明文使用该密码。
1、使用准备工作创建的账户登陆 KubeSphere 控制台,访问halo项目的详情页并导航到配置。在保密字典中,点击右侧的创建
2、 输入基本信息(将其命名为 halo-secret )并点击下一步。在下一页中,选择类型默认,然后点击添加数据来添加键值对。输入如下所示的键(Key)SPRING_DATASOURCE_PASSWORD值(Value)123456,点击右下角 √ 进行确认。继续点击添加数据来添加第二组键值对。输入如下所示的键(Key)SPRING_REDIS_PASSWORD和值(Value)123456,点击右下角 √ 进行确认。完成后,点击创建按钮以继续。
image-1652367224673
image-1652367239314

创建 MySQL 服务所需密钥

按照以上相同的步骤创建一个名为 mysql-secret 的 MySQL 密钥,输入键(Key)MYSQL_ROOT_PASSWORD值(Value)123456
image-1652367254726
image-1652367282832

步骤2:创建 ConfigMap

创建 MySQL 服务所需配置

MySQL 数据库的配置主要存放在 my.cnf 文件里,本次部署需要的配置内容如下:

[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
default_authentication_plugin=mysql_native_password
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
explicit_defaults_for_timestamp=true

1、使用准备工作创建的账户登陆 KubeSphere 控制台,访问 halo 项目的详情页并导航到配置。在配置字典中,点击右侧的创建
2、输入基本信息(将其命名为 mysql-config )并点击下一步。在下一页中,点击添加数据来添加键值对。输入如下所示的键(Key)值(Value),点击右下角 √ 进行确认。完成后,点击创建按钮以继续。
image
image-1651551878444

创建 Redis 服务所需配置

Redis 的配置主要存放在 redis.conf 文件里,本次部署需要的配置内容如下:

port 6379
bind 0.0.0.0
appendonly yes
protected-mode no
requirepass 123456

按照以上相同的步骤创建一个名为 redis-config 的 ConfigMap,输入如下所示的键(Key)值(Value),点击右下角 √ 进行确认。完成后,点击创建按钮以继续。
image-1651551986624
image-1651552006447

步骤3:创建存储卷

1、访问存储下的存储卷,点击创建
2、输入卷的基本信息(例如,将其命名为 halo-pvc ),然后点击下一步
image-1651552180458
3、在存储卷设置中,需要选择一个可用的存储类型,并设置访问模式存储卷容量。可以直接使用默认值,点击下一步继续。
image-1651552206058
4、在高级设置中,无需添加额外的配置,点击创建完成即可。

步骤4:创建应用程序

添加 MySQL 后端组件

1、导航到应用负载下的应用,选择自制应用 > 创建
2、输入基本信息(例如,在应用名称一栏输入 halo ),然后点击下一步
image-1651552691981
3、在服务设置中,点击创建服务以在应用中设置组件。
4、设置组件的服务类型为有状态服务
image-1651552743647
5、 输入有状态服务的名称(例如 mysql-service )并点击下一步。
image-1651552797793
6、 在容器设置中,点击添加容器。
image-1651552823930
7、在搜索框中输入 mysql:8.0.27 ,按下回车键,然后点击使用默认端口。由于配置还未设置完成,请不要点击右下角的 √ 按钮。
image-1651552896511
8、 向下滚动到环境变量,点击引用配置文件或密钥。输入名称 MYSQL_ROOT_PASSWORD ,然后选择 mysql-secret 资源和前面步骤中创建的密钥 MYSQL_ROOT_PASSWORD 。继续点击添加环境变量,输入 MYSQL_DATABASE halodb 。完成后点击 √ 保存配置,最后点击下一步继续。
image-1651553005278
9、选择存储卷设置中的添加存储卷模板,输入存储卷名称mysql-pvc )和挂载路径(模式:读写,路径:/var/lib/mysql )的值。完成后,点击 √ 保存配置。
image-1651553088360
10、选择存储卷中的挂载配置字典或保密字典,点击选择配置字典,选择 mysql-config,输入挂载路径(模式:只读,路径:/etc/mysql/conf.d/my.cnf )的值,点击挂载路径输入框最右面灰色方块,指定子路径 my.cnf,点击确认。完成后,点击 √ 保存配置。击下一步继续。
image-1651553336714
image-1651553349321
11、在高级设置中,可以直接点击创建,也可以按需选择其它选项。
12、现在,MySQL 组件已经添加完成。

添加 Redis 后端组件

1、按照以上相同的步骤创建一个名为 redis-service 的有状态服务
image-1651556969515
2、在容器设置中,点击添加容器。在搜索框中输入 busybox:1.32,按下回车键容器类型选择为初始化容器,容器名称填写为 system-init。由于配置还未设置完成,请不要点击右下角的 √ 按钮。
image-1651557040532
3、向下滚动到启动命令,勾选上启动命令,如数以下命令:

sh,-c,echo 2048 > /proc/sys/net/core/somaxconn && echo never > /sys/kernel/mm/transparent_hugepage/enabled

参数留空。继续向下滚动到容器安全上下文,打开访问控制下的特选模式开关。完成后点击 √ 保存配置。
image-1651557127857
4、在容器设置中,继续点击添加容器。在搜索框中输入 redis:5.0.8,按下回车键,然后点击使用默认端口,容器类型选择为工作容器,容器名称填写为 halo-redis。由于配置还未设置完成,请不要点击右下角的 √ 按钮。
image-1651557212434
5、向下滚动到健康检查,勾选上健康检查,点击存活检查下的添加探针,选择TCP端口端口填写 6379初始延迟填写 300超时时间填写 1检查间隔填写 10成功阈值填写 1失败阈值填写 3,完成后点击 √ 保存存活检查配置。
image-1651557330956
点击就绪检查下的添加探针,选择TCP端口端口填写 6379初始延迟填写 5超时时间填写 1检查间隔填写 10成功阈值填写 1失败阈值填写 3,完成后点击 √ 保存就绪检查配置。
image-1651557485654
6、向下滚动到启动命令,勾选上启动命令,如数以下命令:

sh,-c,redis-server /usr/local/etc/redis/redis.conf

参数留空。完成后点击 √ 保存配置。最后点击下一步继续。
image-1651557563746
7、选择存储卷设置中的添加存储卷模板,输入存储卷名称redis-pvc)和挂载路径的值,为容器组 halo-redis 配置挂载路径,模式:读写,挂载路径:/data。完成后,点击 √ 保存配置。
image-1651557645503
8、选择存储卷中的挂载配置字典或保密字典,点击选择配置字典,选择 redis-config,为容器组 halo-redis配置挂载路径(模式:只读,路径:/usr/local/etc/redis/redis.conf )的值,点击挂载路径输入框最右面灰色方块,指定子路径 redis.conf,点击确认。完成后,点击 √ 保存配置。击下一步继续。
image-1651557749579
image-1651557761112
9、在高级设置中,可以直接点击创建,也可以按需选择其它选项。
10、现在,Redis 组件已经添加完成。

添加 Halo 前端组件

1、再次点击创建服务,选择无状态服务。输入名称 halo-service 并点击下一步。
image-1651557838601
2、与上述步骤类似,点击添加容器,在搜索栏中输入 halohub/halo:1.5.2 并按下回车键,然后滚动下滑到端口设置名称配置为 http-8090容器端口配置为 8090服务端口配置为 8090
image-1651557931976
image-1651557944294
3、向下滚动到环境变量,这里需要配置10个环境变量,如下:

SERVER_PORT=8090
# halo门户对外暴漏的端口,这里配置的值要跟上面端口配置保持一致

SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
# 数据库类型的选择,支持H2Database和MySQL数据库,这里配置的数据库为MySQL

SPRING_DATASOURCE_URL=jdbc:mysql://mysql-service:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
# 数据库连接地址,k8s里采用dns通信,所以这里3306端口前面需要填写为MySQL的Service名称

SPRING_DATASOURCE_USERNAME=root
# 数据库的用户名

SPRING_DATASOURCE_PASSWORD=halo-secret.SPRING_DATASOURCE_PASSWORD
# 访问数据库使用的密码,这里应选择使用引用配置文件或密钥的方式,避免明文密码,引用前面创建的secret

HALO_ADMIN_PATH=admin
# Halo支持自定义后台管理的根路径,这里默认admin即可。

HALO_CACHE=redis
# 缓存方式,目前支持memory、level和redis三种,这里选择redis。

SPRING_REDIS_PORT=6379
# 访问redis的端口号

SPRING_REDIS_DATABASE=0
# 保持默认即可,默认为0 

SPRING_REDIS_HOST=redis-service
# redis连接地址,k8s采用dns通信,所以之类填写redis的service名称。

SPRING_REDIS_PASSWORD=halo-secret.SPRING_REDIS_PASSWORD
# 访问redis使用的密码,这里应选择使用引用配置文件或密钥的方式,避免明文密码,引用前面创建的secret

环境变量相关说明可参考 Halo 官方文档:https://docs.halo.run/getting-started/config
按照上述内容配置环境变量,截图如下,完成后,点击 √ 保存配置,选择下一步
image-1651558017818
4、选择存储卷设置中的挂载存储卷,选择存储卷 helo-pvc,配置挂载模式:读写,路径:/root/.halo )的值。完成后,点击 √ 保存配置。点击下一步继续。
image-1651558076130
5、在高级设置中,直接点击创建
image-1651558108052
6、现在 Halo 前端组件也设置完成,点击下一步继续。
image-1651558139129
7、在路由设置里设置路由规则(应用路由Ingress),点击添加路由规则,选择指定域名域名协议根据自己需求填写,将 halo-service8090 端口暴漏给外部。
image-1651558236054
8、创建后,应用将显示在应用列表中。
image-1651558264692
image-1651558280129

步骤5:验证应用

工作负载中,分别检查部署有状态副本集halo-service-v1mysql-service-v1redis-service-v1 的状态。如果状态为运行中,说明 Halo 已经成功创建。

步骤6:通过 NodePort 访问 Halo

1、若要在集群外访问 Halo,首先导航到项目设置 > 网关设置,点击开启网关。访问模式这里选择 NodePort,然后确定
image-1651558387589
2、导航到应用负载 > 应用路由,点击 halo-ingress-xxxx 进去该应用路由,根据页面 域名+端口 访问 halo 门户。

备注:域名需要可以被 dns 解析,若不能被解析需要在本地 hosts 文件添加相应解析信息。如果需要在 Halo 门户上上传较大的附件,例如,图片、视频、主题包等,需要在应用路由上配置如下注释内容:

nginx.ingress.kubernetes.io/proxy-body-size: '0'

否则上传将会失败,报 413 Request Entity Too Large 错误。

image-1651559099947
Halo 门户页面如下:
image-1651559117550

写在最后

通过将 Halo 部署在 kubesphere 上借助部署(Deployment)多副本伸缩能力可以轻松实现 Halo 的分布式部署,通过配置容器组调度规则,可以实现 Halo 前端服务的分布式多活架构,如下图,容器组调度规则配置为分散调度,尽可能将容器组副本调度到不同的节点上。
image-1651559173437
配置之后,会发现容器组副本尽可能分散在不同的工作节点上:
image-1651559192770
使用该方案也有一个前提就是 halo 前端服务挂载的持久化存储卷需为共享文件系统,例如,本次采用的 NFS 文件存储。
当然,只有 halo 前端服务支持分布式部署还远远不够,若想实现整个系统具备分布式高可用的能力,在某一个工作节点发生故障的时候不影响 Halo 网站的正常访问,还需要后端 MySQL 数据库和 Redis缓存采用集群部署,例如,MySQL 和 Redis 都采用一主多从的部署方案。