ci: add gitea deploy workflow and k3s manifests with cert-manager tls
Some checks failed
Deploy Noval to K3s / deploy (push) Failing after 1s

This commit is contained in:
2026-03-22 17:28:45 +08:00
parent 098ff4e16d
commit a470739190
8 changed files with 249 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
name: Deploy Noval to K3s
on:
push:
branches:
- main
- master
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Deploy to K3s
run: |
set -e
APP_NAME="noval"
NAMESPACE="noval-roog-code"
IMAGE_NAME="noval-app"
echo "🚀 开始部署 ${APP_NAME}..."
cd /root/k8syaml/NovalRedot
echo "📥 拉取最新代码..."
git pull origin main
NEW_COMMIT_SHA=$(git rev-parse --short HEAD)
NEW_COMMIT_MSG=$(git log -1 --oneline)
FULL_IMAGE="${IMAGE_NAME}:${NEW_COMMIT_SHA}"
echo "📦 构建镜像 ${FULL_IMAGE}"
docker build -t "${FULL_IMAGE}" .
docker tag "${FULL_IMAGE}" "${IMAGE_NAME}:latest"
if command -v k3s >/dev/null 2>&1; then
echo "📤 导入镜像到 k3s containerd..."
docker save "${FULL_IMAGE}" | k3s ctr images import -
docker save "${IMAGE_NAME}:latest" | k3s ctr images import -
fi
echo "🧱 应用 K8s 基础资源..."
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/cluster-issuer.yaml
kubectl apply -f k8s/pvc.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
if kubectl get deployment "${APP_NAME}" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "🔄 检测到现有 deployment执行滚动更新..."
kubectl set image deployment/${APP_NAME} ${APP_NAME}=${FULL_IMAGE} -n "${NAMESPACE}"
else
echo "🆕 首次部署,创建 deployment..."
kubectl apply -f k8s/deployment.yaml
kubectl set image deployment/${APP_NAME} ${APP_NAME}=${FULL_IMAGE} -n "${NAMESPACE}"
fi
echo "⏳ 等待 rollout 完成..."
kubectl rollout status deployment/${APP_NAME} -n "${NAMESPACE}" --timeout=300s
echo "✅ 部署完成"
echo "📌 版本: ${NEW_COMMIT_SHA} - ${NEW_COMMIT_MSG}"
kubectl get pods -n "${NAMESPACE}" -l app=${APP_NAME} -o wide

44
k8s/README.md Normal file
View File

@@ -0,0 +1,44 @@
# Noval K3s 部署说明
## 文件列表
- `namespace.yaml`:命名空间
- `pvc.yaml`SQLite 数据持久化存储
- `deployment.yaml`应用部署2 副本,滚动更新)
- `service.yaml`:集群内服务
- `cluster-issuer.yaml`cert-manager ClusterIssuerLet's Encrypt
- `ingress.yaml`Traefik 入口(域名 + TLS
## 一次性手动部署
```bash
cd /root/k8syaml/NovalRedot
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/cluster-issuer.yaml
kubectl apply -f k8s/pvc.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
```
## CI/CD 自动部署
已配置 Gitea Actions`.gitea/workflows/deploy.yml`
触发条件:`main` 分支 push。
流水线逻辑:
1. 在 Runner 所在机器拉取最新代码
2. 构建 Docker 镜像并打 tag`noval-app:<commit>` + `latest`
3. 导入镜像到 k3s containerd
4. 应用 K8s 资源并滚动更新 Deployment
5. 等待 rollout 完成
## 你需要确认/调整
1. **域名**:当前写的是 `writing-reddot.roog-code.cn``k8s/ingress.yaml`
2. **TLS Secret**:当前是 `writing-reddot-roog-code-tls`
3. **ClusterIssuer**:当前是 `letsencrypt-prod``k8s/cluster-issuer.yaml`
4. **ClusterIssuer 邮箱**:当前是 `roog-code@outlook.com`(可改成你常用邮箱)
5. **Ingress 类**:当前按 Traefik 配置k3s 默认)
6. **StorageClass**`local-path`k3s 默认)

14
k8s/cluster-issuer.yaml Normal file
View File

@@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: roog-code@outlook.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik

65
k8s/deployment.yaml Normal file
View File

@@ -0,0 +1,65 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: noval
namespace: noval-roog-code
labels:
app: noval
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: noval
template:
metadata:
labels:
app: noval
spec:
containers:
- name: noval
image: noval-app:latest
imagePullPolicy: Never
ports:
- containerPort: 8000
name: http
env:
- name: TZ
value: Asia/Shanghai
- name: NOVAL_DATA_DIR
value: /app/data
volumeMounts:
- name: noval-data
mountPath: /app/data
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /api/health
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/health
port: http
initialDelaySeconds: 8
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumes:
- name: noval-data
persistentVolumeClaim:
claimName: noval-data-pvc
terminationGracePeriodSeconds: 30

28
k8s/ingress.yaml Normal file
View File

@@ -0,0 +1,28 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: noval-ingress
namespace: noval-roog-code
labels:
app: noval
annotations:
kubernetes.io/ingress.class: "traefik"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
rules:
- host: writing-reddot.roog-code.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: noval-service
port:
number: 80
tls:
- hosts:
- writing-reddot.roog-code.cn
secretName: writing-reddot-roog-code-tls

6
k8s/namespace.yaml Normal file
View File

@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: noval-roog-code
labels:
app: noval

14
k8s/pvc.yaml Normal file
View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: noval-data-pvc
namespace: noval-roog-code
labels:
app: noval
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storageClassName: local-path

16
k8s/service.yaml Normal file
View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: noval-service
namespace: noval-roog-code
labels:
app: noval
spec:
type: ClusterIP
selector:
app: noval
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8000