本文目录导读:

我来帮你创建一个自动配置和部署 Helm Chart 的脚本,这个脚本将包含完整的部署流程、错误处理和配置管理功能。
完整的 Helm 部署脚本
#!/bin/bash
# =============================================
# 自动配置 Helm Chart 部署脚本
# =============================================
set -euo pipefail
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# =============================================
# 配置变量 - 根据项目修改这些值
# =============================================
# 项目配置
PROJECT_NAME="my-app"
NAMESPACE="default"
CHART_NAME="my-chart"
CHART_VERSION="1.0.0"
RELEASE_NAME="${PROJECT_NAME}-release"
# 环境配置
ENVIRONMENT=${1:-"dev"} # dev, staging, production
ACTION=${2:-"install"} # install, upgrade, uninstall, status, rollback
# Helm 仓库配置
HELM_REPO_URL=""
HELM_REPO_NAME="my-repo"
# 配置文件路径
VALUES_DIR="./values"
VALUES_FILE="${VALUES_DIR}/values-${ENVIRONMENT}.yaml"
COMMON_VALUES="${VALUES_DIR}/values-common.yaml"
SECRETS_FILE="${VALUES_DIR}/secrets-${ENVIRONMENT}.yaml"
# 部署配置
DEPLOY_TIMEOUT="300s"
MAX_HISTORY=5
ROLLBACK_VERSION=0
# =============================================
# 函数定义
# =============================================
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查依赖
check_dependencies() {
log_info "检查依赖..."
# 检查 Helm
if ! command -v helm &> /dev/null; then
log_error "Helm 未安装,请运行: curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"
exit 1
fi
log_success "Helm 版本: $(helm version --short)"
# 检查 kubectl
if ! command -v kubectl &> /dev/null; then
log_error "kubectl 未安装"
exit 1
fi
log_success "kubectl 已安装"
# 检查 yq (用于 YAML 处理)
if ! command -v yq &> /dev/null; then
log_warning "yq 未安装,将使用 grep/sed 进行基本 YAML 处理"
else
log_success "yq 已安装"
fi
}
# 初始化 Helm
initialize_helm() {
log_info "初始化 Helm..."
# 添加 Helm 仓库
if [[ -n "$HELM_REPO_URL" && -n "$HELM_REPO_NAME" ]]; then
if ! helm repo list | grep -q "$HELM_REPO_NAME"; then
helm repo add "$HELM_REPO_NAME" "$HELM_REPO_URL"
log_success "添加 Helm 仓库: $HELM_REPO_NAME"
else
log_info "Helm 仓库已存在: $HELM_REPO_NAME"
fi
fi
# 更新仓库
helm repo update
log_success "Helm 仓库已更新"
}
# 创建命名空间
create_namespace() {
log_info "检查命名空间: $NAMESPACE"
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
kubectl create namespace "$NAMESPACE"
log_success "创建命名空间: $NAMESPACE"
else
log_info "命名空间已存在: $NAMESPACE"
fi
}
# 准备 values 文件
prepare_values() {
log_info "准备配置..."
local temp_values=$(mktemp)
local final_values="${VALUES_DIR}/final-${ENVIRONMENT}.yaml"
# 创建 values 目录
mkdir -p "$VALUES_DIR"
# 合并 values 文件
if [[ -f "$COMMON_VALUES" ]]; then
log_info "使用公共配置: $COMMON_VALUES"
cp "$COMMON_VALUES" "$temp_values"
fi
if [[ -f "$VALUES_FILE" ]]; then
log_info "使用环境配置: $VALUES_FILE"
if [[ -f "$temp_values" ]]; then
yq eval-all '. as $item ireduce ({}; . *+ $item)' "$temp_values" "$VALUES_FILE" > "$final_values" 2>/dev/null || \
cat "$VALUES_FILE" > "$final_values"
else
cp "$VALUES_FILE" "$final_values"
fi
else
log_warning "环境配置文件不存在: $VALUES_FILE"
# 创建默认配置
cat > "$VALUES_FILE" << EOF
# ${ENVIRONMENT} 环境配置
replicaCount: 1
image:
repository: nginx
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
EOF
log_info "创建默认配置: $VALUES_FILE"
cp "$VALUES_FILE" "$final_values"
fi
# 处理 secrets
if [[ -f "$SECRETS_FILE" ]]; then
log_info "使用加密配置: $SECRETS_FILE"
# 如果是加密文件,需要解密
if [[ "$SECRETS_FILE" == *.enc ]]; then
decrypt_secrets "$SECRETS_FILE" "${SECRETS_FILE%.enc}"
yq eval-all '. as $item ireduce ({}; . *+ $item)' "$final_values" "${SECRETS_FILE%.enc}" > "${final_values}.tmp"
mv "${final_values}.tmp" "$final_values"
else
yq eval-all '. as $item ireduce ({}; . *+ $item)' "$final_values" "$SECRETS_FILE" > "${final_values}.tmp" 2>/dev/null || \
cat "$final_values" "$SECRETS_FILE" > "${final_values}.tmp"
mv "${final_values}.tmp" "$final_values"
fi
fi
# 设置环境变量
yq eval ".global.environment = \"${ENVIRONMENT}\"" "$final_values" > "${final_values}.tmp" 2>/dev/null || true
mv "${final_values}.tmp" "$final_values"
echo "$final_values"
}
# 解密 secrets 文件
decrypt_secrets() {
local encrypted_file=$1
local decrypted_file=$2
log_info "解密配置文件..."
# 使用 sops 解密
if command -v sops &> /dev/null; then
sops -d "$encrypted_file" > "$decrypted_file"
log_success "使用 sops 解密成功"
# 使用 helm-secrets 插件
elif helm plugin list | grep -q "secrets"; then
helm secrets decrypt "$encrypted_file" > "$decrypted_file"
log_success "使用 helm-secrets 解密成功"
else
log_warning "未找到解密工具,使用原始文件"
cp "$encrypted_file" "$decrypted_file"
fi
}
# 安装/升级 Helm Chart
install_or_upgrade() {
local values_file=$1
local action=$2
log_info "${action} Helm Chart..."
# 构建 Helm 命令
local helm_cmd="helm ${action} ${RELEASE_NAME} ${CHART_NAME}"
# 添加命名空间
helm_cmd+=" --namespace ${NAMESPACE}"
# 添加 values 文件
if [[ -n "$values_file" && -f "$values_file" ]]; then
helm_cmd+=" --values ${values_file}"
fi
# 添加额外选项
helm_cmd+=" --timeout ${DEPLOY_TIMEOUT}"
helm_cmd+=" --history-max ${MAX_HISTORY}"
# 如果是升级,添加升级选项
if [[ "$action" == "upgrade" ]]; then
helm_cmd+=" --install" # release 不存在则安装
helm_cmd+=" --cleanup-on-fail"
helm_cmd+=" --atomic" # 失败时自动回滚
helm_cmd+=" --wait" # 等待就绪
fi
# 如果是安装,添加安装选项
if [[ "$action" == "install" ]]; then
helm_cmd+=" --create-namespace"
helm_cmd+=" --wait"
fi
# 添加部署标签
helm_cmd+=" --set global.deployedBy=\"helm-auto-deploy\""
helm_cmd+=" --set global.deployedAt=\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
# 执行 Helm 命令
log_info "执行命令: $helm_cmd"
if $helm_cmd; then
log_success "Helm ${action} 成功"
# 检查部署状态
sleep 5
check_deployment_status
else
log_error "Helm ${action} 失败"
return 1
fi
}
# 检查部署状态
check_deployment_status() {
log_info "检查部署状态..."
# 检查 Pod 状态
echo ""
log_info "Pod 状态:"
kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" --show-labels 2>/dev/null || true
# 检查 Service
echo ""
log_info "Service 状态:"
kubectl get services -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 检查 Ingress
echo ""
log_info "Ingress 状态:"
kubectl get ingress -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 检查 HPA
echo ""
log_info "HPA 状态:"
kubectl get hpa -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 获取访问信息
echo ""
log_info "访问信息:"
local service_name=$(kubectl get svc -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [[ -n "$service_name" ]]; then
local node_port=$(kubectl get svc -n "$NAMESPACE" "$service_name" -o jsonpath='{.spec.ports[0].nodePort}' 2>/dev/null || echo "")
local cluster_ip=$(kubectl get svc -n "$NAMESPACE" "$service_name" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
if [[ -n "$node_port" ]]; then
echo " NodePort: ${node_port}"
fi
if [[ -n "$cluster_ip" ]]; then
echo " ClusterIP: ${cluster_ip}"
fi
fi
}
# 卸载 Helm Release
uninstall_release() {
log_info "卸载 Helm Release: $RELEASE_NAME"
read -p "确认卸载 ${RELEASE_NAME}? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
helm uninstall "$RELEASE_NAME" --namespace "$NAMESPACE"
log_success "卸载成功"
else
log_info "取消卸载"
fi
}
# 回滚 Helm Release
rollback_release() {
local version=${1:-0}
log_info "回滚 Helm Release: $RELEASE_NAME"
# 获取历史版本
echo ""
log_info "发布历史:"
helm history "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || true
if [[ "$version" -eq 0 ]]; then
# 如果没有指定版本,回滚到上一个版本
local revisions=$(helm history "$RELEASE_NAME" --namespace "$NAMESPACE" -o json 2>/dev/null | jq '. | length' 2>/dev/null || echo "0")
if [[ "$revisions" -gt 1 ]]; then
version=$((revisions - 1))
else
log_error "没有可回滚的版本"
return 1
fi
fi
echo ""
read -p "确认回滚到版本 ${version}? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
helm rollback "$RELEASE_NAME" "$version" --namespace "$NAMESPACE" --wait
log_success "回滚到版本 ${version} 成功"
check_deployment_status
else
log_info "取消回滚"
fi
}
# 查看部署状态
show_status() {
log_info "查看部署状态..."
# 检查 Helm Release 状态
echo ""
log_info "Helm Release 状态:"
helm status "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || log_warning "Release 不存在"
# 列出所有资源
echo ""
log_info "所有资源列表:"
kubectl get all -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 显示历史版本
echo ""
log_info "发布历史:"
helm history "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || true
}
# 创建默认 Chart 结构
create_default_chart() {
if [[ ! -d "$CHART_NAME" ]]; then
log_info "创建默认 Chart 结构..."
mkdir -p "$CHART_NAME/templates"
mkdir -p "$CHART_NAME/charts"
# 创建 Chart.yaml
cat > "$CHART_NAME/Chart.yaml" << EOF
apiVersion: v2
name: ${CHART_NAME}
description: A Helm chart for ${PROJECT_NAME}
type: application
version: ${CHART_VERSION}
appVersion: "1.0.0"
EOF
# 创建默认 values.yaml
cat > "$CHART_NAME/values.yaml" << EOF
# 默认配置
replicaCount: 1
image:
repository: nginx
tag: latest
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
EOF
# 创建默认 Deployment
cat > "$CHART_NAME/templates/deployment.yaml" << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "chart.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "chart.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "chart.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
EOF
# 创建 Service
cat > "$CHART_NAME/templates/service.yaml" << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "chart.selectorLabels" . | nindent 4 }}
EOF
# 创建 _helpers.tpl
cat > "$CHART_NAME/templates/_helpers.tpl" << 'EOF'
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "chart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{- define "chart.labels" -}}
helm.sh/chart: {{ include "chart.name" . }}-{{ .Chart.Version | replace "+" "_" }}
{{ include "chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- define "chart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "chart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
EOF
log_success "创建默认 Chart 结构完成"
else
log_info "Chart 目录已存在"
fi
}
# 主函数
main() {
echo ""
echo "=============================================="
echo " Helm Chart 自动化部署脚本"
echo "=============================================="
echo " 环境: ${ENVIRONMENT}"
echo " 操作: ${ACTION}"
echo " 项目: ${PROJECT_NAME}"
echo " Release: ${RELEASE_NAME}"
echo "=============================================="
echo ""
# 检查依赖
check_dependencies
# 初始化 Helm
initialize_helm
# 根据操作执行不同命令
case "$ACTION" in
install)
# 创建默认 Chart
create_default_chart
# 创建命名空间
create_namespace
# 准备配置
local values_file
values_file=$(prepare_values)
# 安装
install_or_upgrade "$values_file" "install"
;;
upgrade)
# 准备配置
local values_file
values_file=$(prepare_values)
# 升级
install_or_upgrade "$values_file" "upgrade"
;;
uninstall)
uninstall_release
;;
rollback)
rollback_release "$ROLLBACK_VERSION"
;;
status)
show_status
;;
*)
echo "用法: $0 <environment> <action>"
echo ""
echo "环境:"
echo " dev 开发环境"
echo " staging 预发布环境"
echo " production 生产环境"
echo ""
echo "操作:"
echo " install 安装新的 release"
echo " upgrade 升级已有的 release"
echo " uninstall 删除 release"
echo " rollback 回滚 release"
echo " status 查看状态"
echo ""
echo "示例:"
echo " $0 dev install"
echo " $0 production upgrade"
echo " $0 staging uninstall"
echo " $0 dev rollback"
echo " $0 production status"
exit 1
;;
esac
echo ""
log_success "操作完成"
}
# 执行主函数
main "$@"
配置文件示例
公共配置 (values/values-common.yaml)
# 公共配置
global:
environment: ""
registry: "registry.example.com"
imagePullSecrets:
- name: registry-credentials
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
开发环境配置 (values/values-dev.yaml)
# 开发环境配置
replicaCount: 1
image:
repository: registry.example.com/my-app
tag: dev-latest
pullPolicy: Always
ingress:
enabled: false
service:
type: NodePort
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
生产环境配置 (values/values-production.yaml)
# 生产环境配置
replicaCount: 3
image:
repository: registry.example.com/my-app
tag: stable
pullPolicy: IfNotPresent
ingress:
enabled: true
hosts:
- host: app.example.com
paths:
- path: /
pathType: ImplementationSpecific
tls:
- hosts:
- app.example.com
secretName: app-tls
resources:
limits:
cpu: 1
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
加密 Secrets (values/secrets-production.yaml.enc)
# 加密的敏感信息 env: DATABASE_URL: postgresql://user:password@localhost:5432/dbname REDIS_PASSWORD: your-redis-password API_KEY: your-api-key # 注意:实际使用时,需要使用 sops 或 helm-secrets 进行加密
使用示例
基本使用
# 赋予执行权限 chmod +x deploy.sh # 安装到开发环境 ./deploy.sh dev install # 升级生产环境 ./deploy.sh production upgrade # 查看状态 ./deploy.sh dev status # 回滚到上一个版本 ./deploy.sh staging rollback # 卸载 ./deploy.sh dev uninstall
高级用法
# 使用自定义值文件 VALUES_DIR=custom-values ./deploy.sh dev install # 设置超时时间 DEPLOY_TIMEOUT=600s ./deploy.sh production upgrade # 指定回滚到特定版本 ROLLBACK_VERSION=3 ./deploy.sh production rollback # 使用不同的命名空间 NAMESPACE=my-custom-ns ./deploy.sh dev install
CI/CD 集成 (GitLab CI)
# .gitlab-ci.yml
deploy-dev:
stage: deploy
script:
- chmod +x deploy.sh
- ./deploy.sh dev install
only:
- develop
deploy-production:
stage: deploy
script:
- chmod +x deploy.sh
- ./deploy.sh production upgrade
only:
- tags
environment:
name: production
url: https://app.example.com
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy to Kubernetes
on:
push:
branches: [main]
tags: ['v*']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Helm
uses: azure/setup-helm@v3
- name: Deploy
run: |
chmod +x deploy.sh
./deploy.sh production upgrade
env:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
注意事项
-
安全性:
- 不要在脚本中硬编码密码
- 使用
sops或helm-secrets加密敏感信息 - 限制脚本执行权限
-
配置管理:
- 维护不同环境的值文件
- 使用版本控制管理配置
- 定期审核配置文件
-
错误处理:
- 脚本包含基本的错误处理
- 生产环境建议添加更严格的验证
- 实现部署前检查
-
监控和日志:
- 记录所有部署操作
- 集成监控告警
- 保存部署历史
-
回滚策略:
- 保留足够的发布历史
- 测试回滚流程
- 文档化回滚步骤
这个脚本提供了一个完整、可扩展的 Helm Chart 部署解决方案,可以根据实际需求进行定制和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播