概述
前段时间使用 kubesphere 采用 UI 界面的操作方式成功将自己的博客部署在了 kubernetes 平台上(参考链接https://rainwu.cn/archives/use-kubesphere-deploy-halo),上个周末折腾了一下将之前部署的内容整理成了 yaml 文件,方便其他小伙伴在原生的 kubernetes 平台或其他 k8s 发行版上进行部署。
部署架构
架构跟之前部署的一样,数据库采用 mysql,缓存采用 redis ,如下:
本次部署组合采用 Halo + MySQL + Redis。Halo 作为前端服务,MySQL 作为后端数据库,Redis 作缓存。在 kubernetes 平台上创建一个项目 halo-project ,该项目下创建三个服务,分别是 halo-front-sevice,halo-mysql-service 和 halo-redis-service,具体服务包含内容如下:
部署过程
创建 halo-app.yaml
文件,内容如下:
## 用以保存 mysql 数据库密码的 Secret
apiVersion: v1
kind: Secret
metadata:
name: halo-mysql-secret
data:
MYSQL_ROOT_PASSWORD: MTIzNDU2 # 必须是 base64 编码,在 Linux 可以通过 “echo -n "123456" | base64”命令获取
type: Opaque
---
## 用以保存 halo 连接 mysql 数据库密码和 redis 缓存密码的 Secret
apiVersion: v1
kind: Secret
metadata:
name: halo-secret
data:
SPRING_DATASOURCE_PASSWORD: MTIzNDU2 # 必须是 base64 编码,在 Linux 可以通过 “echo -n "123456" | base64”命令获取
SPRING_REDIS_PASSWORD: MTIzNDU2
type: Opaque
---
## 用以保存 mysql 配置文件的 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: halo-mysql-config
data:
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
---
## 用以保存 redis 配置文件的 ConfigMap,requirepass 的值为 redis 的连接密码
apiVersion: v1
kind: ConfigMap
metadata:
name: halo-redis-config
data:
redis.conf: |-
port 6379
bind 0.0.0.0
appendonly yes
protected-mode no
requirepass 123456
---
## halo 需要的存储空间,用以持久化 /root/.halo 里的数据
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: halo-front-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: nfs-client # storageClass 可根据您本地集群拥有的类型进行选择,这里采用的是 nfs-client
---
## 将 mysql 数据库以 StatefulSet 工作负载形式创建出来
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: halo-mysql
labels:
app: halo-mysql
spec:
replicas: 1
selector:
matchLabels:
app: halo-mysql
template:
metadata:
labels:
app: halo-mysql
spec:
volumes: # 将前面创建的 ConfigMap 定义出来,方便后面调用
- name: halo-mysql-config
configMap:
name: halo-mysql-config
defaultMode: 420
containers:
- name: mysql
image: 'mysql:8.0.27' # mysql 数据库的镜像版本,这里选择 mysql 8.0
ports:
- name: tcp-3306
containerPort: 3306 # 将容器的 3306 端口暴露出来
protocol: TCP
env: # 配置 mysql 数据库环境变量
- name: MYSQL_ROOT_PASSWORD # 数据库 root 账户的密码,这里采用 Secret 方式初始化连接密码,下面填写最开始创建的名称为“halo-mysql-secret”的 Secret
valueFrom:
secretKeyRef:
name: halo-mysql-secret
key: MYSQL_ROOT_PASSWORD
- name: MYSQL_DATABASE # 启动后创建一个名为 halodb 的库
value: halodb
volumeMounts:
- name: halo-mysql-pvc # 将 mysql 数据库的 “/var/lib/mysql” 进行持久化,数据库落盘到名称为 “halo-mysql-pvc” 的持久化数据卷
mountPath: /var/lib/mysql
- name: halo-mysql-config # 将 my.cnf 配置文件以 ConfigMap 的形式进行外部挂载
readOnly: true
mountPath: /etc/mysql/conf.d/my.cnf
subPath: my.cnf
serviceName: halo-mysql-service
volumeClaimTemplates: # 创建 mysql 数据库需要的持久化卷
- kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: halo-mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: nfs-client # storageClass 可根据您本地集群拥有的类型进行选择,这里采用的是 nfs-client
---
## 为 mysql 工作负载创建 service
apiVersion: v1
kind: Service
metadata:
name: halo-mysql-service
spec:
ports:
- name: tcp-3306
protocol: TCP
port: 3306
targetPort: 3306
selector:
app: halo-mysql
clusterIP: None
---
## 将 redis 缓存以 StatefulSet 工作负载形式创建出来
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: halo-redis
labels:
app: halo-redis
spec:
replicas: 1
selector:
matchLabels:
app: halo-redis
template:
metadata:
labels:
app: halo-redis
spec:
containers:
- name: halo-redis
image: 'redis:5.0.8'
ports:
- name: tcp-6379 # 将容器的 6379 端口暴露出来
protocol: TCP
containerPort: 6379
livenessProbe: # 健康检查配置,添加探针以定时检查容器健康状态
initialDelaySeconds: 300
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 6379
readinessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
tcpSocket:
port: 6379
command:
- sh
- '-c'
- redis-server /usr/local/etc/redis/redis.conf
volumeMounts:
- readOnly: false
mountPath: /data # 将 redis 缓存的 “/data” 进行持久化,数据库落盘到名称为 “halo-redis-pvc” 的持久化数据卷
name: redis-pvc
- name: halo-redis-config # 将 redis.conf 配置文件以 ConfigMap 的形式进行外部挂载
readOnly: true
mountPath: /usr/local/etc/redis/redis.conf
subPath: redis.conf
initContainers: # 初始化容器定义,使用启动脚本通过初始化容器对 redis 进行初始化操作
- name: system-init
image: 'busybox:1.32'
command:
- sh
- '-c'
- >-
echo 2048 > /proc/sys/net/core/somaxconn && echo never >
/sys/kernel/mm/transparent_hugepage/enabled
securityContext: # 启用容器安全上下文权限为特权模式,以主机上的 root 用户运行容器进程
privileged: true
volumes: # 将前面创建的 ConfigMap 定义出来,方便后面调用
- name: halo-redis-config
configMap:
name: halo-redis-config
serviceName: halo-redis-service
volumeClaimTemplates: # 创建 redis 缓存需要的持久化卷
- spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: nfs-client # storageClass 可根据您本地集群拥有的类型进行选择,这里采用的是 nfs-client
metadata:
name: redis-pvc
---
## 为 redis 工作负载创建 service
apiVersion: v1
kind: Service
metadata:
name: halo-redis-service
spec:
selector:
app: halo-redis
ports:
- name: tcp-6379
protocol: TCP
port: 6379
targetPort: 6379
clusterIP: None
---
## 将 halo 前端服务以 Deployment 工作负载的方式创建出来
apiVersion: apps/v1
kind: Deployment
metadata:
name: halo-front
labels:
app: halo-front
spec:
replicas: 1
selector:
matchLabels:
app: halo-front
template:
metadata:
labels:
app: halo-front
spec:
volumes: # 将前面创建的 ConfigMap 定义出来,方便后面调用
- name: halo-front-pvc
persistentVolumeClaim:
claimName: halo-front-pvc
containers:
- name: halo
image: 'halohub/halo:1.5.2' # halo 的镜像版本,后续修改这里的版本号可以实现 halo 的滚动升级
ports:
- name: http-8090
containerPort: 8090
protocol: TCP
env: # halo 的环境变量配置,可参考https://docs.halo.run/getting-started/install/other/docker-compose
- name: SERVER_PORT
value: '8090'
- name: SPRING_DATASOURCE_DRIVER_CLASS_NAME
value: com.mysql.cj.jdbc.Driver
- name: SPRING_DATASOURCE_URL # 注意下面 3306 前面的 halo-mysql-service 为 mysql 数据库的 service 名称,如果前面 mysql 数据库的 service 做了修改,这里也要修改
value: jdbc:mysql://halo-mysql-service:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
- name: SPRING_DATASOURCE_USERNAME
value: root
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: halo-secret # halo 连接数据库的密码,这里采用 Secret 方式初始化连接密码,下面填写最开始创建的名称为“halo-secret”的 Secret
key: SPRING_DATASOURCE_PASSWORD
- name: HALO_ADMIN_PATH
value: admin
- name: HALO_CACHE
value: redis
- name: SPRING_REDIS_PORT
value: '6379'
- name: SPRING_REDIS_DATABASE
value: '0'
- name: SPRING_REDIS_HOST
value: halo-redis-service
- name: SPRING_REDIS_PASSWORD # halo 连接缓存的密码,这里采用 Secret 方式初始化连接密码,下面填写最开始创建的名称为“halo-secret”的 Secret
valueFrom:
secretKeyRef:
name: halo-secret
key: SPRING_REDIS_PASSWORD
volumeMounts: # 将 halo 的 “/root/.halo” 进行持久化,数据库落盘到名称为 “halo-front-pvc” 的持久化数据卷
- name: halo-front-pvc
mountPath: /root/.halo
---
## 为 halo 工作负载创建 service,并通过 nodePort 的方式暴露出来
apiVersion: v1
kind: Service
metadata:
name: halo-front-service
spec:
type: NodePort
ports:
- name: tcp-8090
protocol: TCP
port: 8090
targetPort: 8090
nodePort: 30890
selector:
app: halo-front
执行下面命令进行部署:
## 为 halo 项目创建一个命名空间里
$ kubectl create namespace halo-project
namespace/halo-project created
## 将 halo 部署到 halo-project 命名空间里
$ kubectl -n halo-project apply -f halo-app.yaml
secret/halo-mysql-secret created
secret/halo-secret created
configmap/halo-mysql-config created
configmap/halo-redis-config created
persistentvolumeclaim/halo-front-pvc created
statefulset.apps/halo-mysql created
service/halo-mysql-service created
statefulset.apps/halo-redis created
service/halo-redis-service created
deployment.apps/halo-front created
service/halo-front-service created
稍等片刻,查看 pod 启动情况:
$ kubectl get pod -n halo-project
NAME READY STATUS RESTARTS AGE
halo-front-7d6dc4c66-pqx9d 1/1 Running 2 (80s ago) 103s
halo-mysql-0 1/1 Running 0 103s
halo-redis-0 1/1 Running 0 103s
查看 halo 容器的日志输出:
$ kubectl logs -f -n halo-project halo-front-7d6dc4c66-pqx9d
__ __ __
/ / / /___ _/ /___
/ /_/ / __ `/ / __ \
/ __ / /_/ / / /_/ /
/_/ /_/\__,_/_/\____/
Version: 1.5.2
2022-06-20 12:59:53.833 INFO 6 --- [ main] run.halo.app.Application : Starting Application v1.5.2 using Java 11.0.11 on halo-front-7d6dc4c66-pqx9d with PID 6 (/application/BOOT-INF/classes started by root in /application)
2022-06-20 12:59:53.837 INFO 6 --- [ main] run.halo.app.Application : No active profile set, falling back to 1 default profile: "default"
2022-06-20 12:59:55.616 INFO 6 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2022-06-20 12:59:55.622 INFO 6 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2022-06-20 12:59:56.061 INFO 6 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 423 ms. Found 24 JPA repository interfaces.
2022-06-20 12:59:57.615 INFO 6 --- [ main] org.eclipse.jetty.util.log : Logging initialized @5567ms to org.eclipse.jetty.util.log.Slf4jLog
2022-06-20 12:59:57.904 INFO 6 --- [ main] o.s.b.w.e.j.JettyServletWebServerFactory : Server initialized with port: 8090
2022-06-20 12:59:57.912 INFO 6 --- [ main] org.eclipse.jetty.server.Server : jetty-9.4.45.v20220203; built: 2022-02-03T09:14:34.105Z; git: 4a0c91c0be53805e3fcffdcdcc9587d5301863db; jvm 11.0.11+9
2022-06-20 12:59:57.970 INFO 6 --- [ main] o.e.j.s.h.ContextHandler.application : Initializing Spring embedded WebApplicationContext
2022-06-20 12:59:57.970 INFO 6 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4052 ms
2022-06-20 12:59:58.754 INFO 6 --- [ main] run.halo.app.config.HaloConfiguration : Halo cache store load impl : [class run.halo.app.cache.RedisCacheStore]
2022-06-20 12:59:59.912 INFO 6 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2022-06-20 12:59:59.991 INFO 6 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.33
2022-06-20 13:00:00.069 INFO 6 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2022-06-20 13:00:00.272 INFO 6 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2022-06-20 13:00:00.665 INFO 6 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2022-06-20 13:00:00.715 INFO 6 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2022-06-20 13:00:04.196 INFO 6 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2022-06-20 13:00:04.212 INFO 6 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2022-06-20 13:00:05.574 INFO 6 --- [ main] org.eclipse.jetty.server.session : DefaultSessionIdManager workerName=node0
2022-06-20 13:00:05.578 INFO 6 --- [ main] org.eclipse.jetty.server.session : No SessionScavenger set, using defaults
2022-06-20 13:00:05.580 INFO 6 --- [ main] org.eclipse.jetty.server.session : node0 Scavenging every 600000ms
2022-06-20 13:00:05.593 INFO 6 --- [ main] o.e.jetty.server.handler.ContextHandler : Started o.s.b.w.e.j.JettyEmbeddedWebAppContext@2416378c{application,/,[file:///tmp/jetty-docbase.8090.3312258845098811428/, jar:file:/application/BOOT-INF/lib/springfox-swagger-ui-3.0.0.jar!/META-INF/resources],AVAILABLE}
2022-06-20 13:00:05.594 INFO 6 --- [ main] org.eclipse.jetty.server.Server : Started @13548ms
2022-06-20 13:00:06.595 INFO 6 --- [ main] run.halo.app.handler.file.FileHandlers : Registered 9 file handler(s)
2022-06-20 13:00:10.495 INFO 6 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoint(s) beneath base path '/api/admin/actuator'
2022-06-20 13:00:10.565 INFO 6 --- [ main] o.e.j.s.h.ContextHandler.application : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-06-20 13:00:10.566 INFO 6 --- [ main] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-06-20 13:00:10.571 INFO 6 --- [ main] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2022-06-20 13:00:10.584 INFO 6 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector@5fd8dd66{HTTP/1.1, (http/1.1)}{0.0.0.0:8090}
2022-06-20 13:00:10.585 INFO 6 --- [ main] o.s.b.web.embedded.jetty.JettyWebServer : Jetty started on port(s) 8090 (http/1.1) with context path '/'
2022-06-20 13:00:10.613 INFO 6 --- [ main] run.halo.app.Application : Started Application in 17.761 seconds (JVM running for 18.567)
2022-06-20 13:00:10.617 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Starting migrate database...
2022-06-20 13:00:10.740 INFO 6 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 7.15.0 by Redgate
2022-06-20 13:00:10.741 INFO 6 --- [ main] o.f.c.i.database.base.BaseDatabaseType : Database: jdbc:mysql://halo-mysql-service:3306/halodb (MySQL 8.0)
2022-06-20 13:00:10.784 INFO 6 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Repair of failed migration in Schema History table `halodb`.`flyway_schema_history` not necessary as table doesn't exist.
2022-06-20 13:00:10.802 INFO 6 --- [ main] o.f.core.internal.command.DbRepair : Successfully repaired schema history table `halodb`.`flyway_schema_history` (execution time 00:00.023s).
2022-06-20 13:00:10.816 INFO 6 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 7.15.0 by Redgate
2022-06-20 13:00:10.836 INFO 6 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 6 migrations (execution time 00:00.006s)
2022-06-20 13:00:10.872 INFO 6 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table `halodb`.`flyway_schema_history` with baseline ...
2022-06-20 13:00:11.018 INFO 6 --- [ main] o.f.core.internal.command.DbBaseline : Successfully baselined schema with version: 1
2022-06-20 13:00:11.031 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `halodb`: 1
2022-06-20 13:00:11.037 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `halodb` to version "2 - migrate 1.2.0-beta.1 to 1.2.0-beta.2"
2022-06-20 13:00:11.177 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `halodb` to version "3 - migrate 1.3.0-beta.1 to 1.3.0-beta.2"
2022-06-20 13:00:11.698 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `halodb` to version "4 - migrate 1.3.0-beta.2 to 1.3.0-beta.3"
2022-06-20 13:00:11.779 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `halodb` to version "5 - migrate remove notnull for email in comments table"
2022-06-20 13:00:11.834 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `halodb` to version "6 - migrate create contents table"
2022-06-20 13:00:11.904 INFO 6 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 5 migrations to schema `halodb`, now at version v6 (execution time 00:00.882s)
2022-06-20 13:00:11.913 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Migrate database succeed.
2022-06-20 13:00:11.914 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Created backup directory: [/tmp/halo-backup]
2022-06-20 13:00:11.914 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Created data export directory: [/tmp/halo-data-export]
2022-06-20 13:00:13.554 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Copied theme folder from [/application/BOOT-INF/classes/templates/themes] to [/root/.halo/templates/themes/caicai_anatole]
2022-06-20 13:00:13.598 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Halo started at http://127.0.0.1:8090
2022-06-20 13:00:13.598 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Halo admin started at http://127.0.0.1:8090/admin
2022-06-20 13:00:13.598 INFO 6 --- [ main] run.halo.app.listener.StartedListener : Halo has started successfully!
通过输出日志,可以看到 halo 已启动成功。
访问 halo ,在浏览器输入 http://nodeIP:30890,进入 halo 安装向导页面: