前置条件
| 条件 | 说明 |
|---|---|
| 腾讯云 TKE 集群 | Serverless(EKS)或标准集群均可 |
| 本地工具 | kubectl、helm、docker 已安装并配置好 kubeconfig |
| 腾讯云 CCR 账号 | 用于推送 Istio / Kiali 镜像,需要在 CCR 控制台提前创建好命名空间 |
| CNB 镜像仓库凭证 | 用于 TKE 拉取业务微服务镜像 |
Step 1: 推送镜像到腾讯云 CCR
需要推送的镜像清单
| 镜像 | 用途 | CCR 目标路径 |
|---|---|---|
istio/pilot:1.29.2 |
istiod 控制面,负责配置下发、证书签发、服务发现 | ccr.ccs.tencentyun.com/hhs_open/pilot:1.29.2 |
istio/proxyv2:1.29.2 |
Envoy sidecar 代理,自动注入到每个 Pod | ccr.ccs.tencentyun.com/hhs_open/proxyv2:1.29.2 |
quay.io/kiali/kiali:v2.25.0 |
Kiali 流量可视化面板 | ccr.ccs.tencentyun.com/hhs_open/kiali:v2.25.0 |
操作命令
bash
# ========== 1.1 登录腾讯云 CCR ==========
docker login ccr.ccs.tencentyun.com -u <CCR用户名>
# 输入密码,看到 "Login Succeeded" 即可
# ========== 1.2 拉取镜像 ==========
# !! 重要 !! 如果你的 Mac 是 Apple Silicon(M1/M2/M3/M4),
# 必须加 --platform linux/amd64,否则拉到的是 arm64 镜像,
# 推到 CCR 后 TKE 节点(amd64)无法运行,会报 "exec format error"
docker pull --platform linux/amd64 istio/pilot:1.29.2
docker pull --platform linux/amd64 istio/proxyv2:1.29.2
docker pull --platform linux/amd64 quay.io/kiali/kiali:v2.25.0
# ========== 1.3 打 tag ==========
# 格式: ccr.ccs.tencentyun.com/<命名空间>/<镜像名>:<tag>
# 命名空间需要在 CCR 控制台提前创建好
docker tag istio/pilot:1.29.2 ccr.ccs.tencentyun.com/hhs_open/pilot:1.29.2
docker tag istio/proxyv2:1.29.2 ccr.ccs.tencentyun.com/hhs_open/proxyv2:1.29.2
docker tag quay.io/kiali/kiali:v2.25.0 ccr.ccs.tencentyun.com/hhs_open/kiali:v2.25.0
# ========== 1.4 推送 ==========
docker push ccr.ccs.tencentyun.com/hhs_open/pilot:1.29.2
docker push ccr.ccs.tencentyun.com/hhs_open/proxyv2:1.29.2
docker push ccr.ccs.tencentyun.com/hhs_open/kiali:v2.25.0
CPU 架构问题详解 (MAC 同学要瞄一眼)
css
flowchart TD
A[Mac Apple Silicon] -->|docker pull 默认| B[拉取 arm64 镜像]
A -->|docker pull --platform linux/amd64| C[拉取 amd64 镜像]
B -->|push 到 CCR| D[CCR 存储 arm64 镜像]
C -->|push 到 CCR| E[CCR 存储 amd64 镜像]
D -->|TKE amd64 节点拉取| F[exec format error]
E -->|TKE amd64 节点拉取| G[正常运行]
style F fill:#F44336,color:#fff
style G fill:#4CAF50,color:#fff
style B fill:#FF9800,color:#fff
style C fill:#2196F3,color:#fff
你的 Mac 是 ARM 架构(Apple Silicon M 系列芯片),而 TKE 云服务器是 x86_64(amd64)架构。docker pull 默认会拉取与本机架构匹配的镜像。如果不加 --platform linux/amd64,推到 CCR 的就是 arm64 镜像,TKE 节点拿到后二进制文件无法执行,Pod 会不断 CrashLoopBackOff,日志只有一行 exec format error。
Step 2: 安装 Istio
Istio 架构
Istio 分为两部分:
- 控制面 (istiod) :大脑,负责配置下发、服务发现。使用
pilot镜像。 - 数据面 (sidecar) :每个 Pod 里自动注入的
istio-proxy容器,拦截所有进出流量。使用proxyv2镜像。
当前配置已关闭 mTLS(DISABLE),服务间走明文通信,减少 TLS 握手和加解密开销。连接池、熔断、流量路由等功能不受影响。
安装时只需要装控制面 istiod,sidecar 会在 Pod 创建时自动注入。
操作命令
ini
# ========== 2.1 添加 Helm Repo ==========
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update istio
# ========== 2.2 创建 namespace + CCR 拉取 Secret ==========
kubectl create namespace istio-system
# 这个 Secret 让 istio-system 下的 Pod 能从 CCR 拉私有镜像
kubectl create secret docker-registry ccr-registry-secret \
--docker-server=ccr.ccs.tencentyun.com \
--docker-username=<CCR用户名> \
--docker-password='<CCR密码>' \
-n istio-system
# ========== 2.3 安装 istio-base (CRDs) ==========
# 这一步只是注册 Istio 的自定义资源类型(CRD),不会创建任何 Pod
# 装完后 kubectl get pods 看不到东西是正常的
helm install istio-base istio/base -n istio-system --wait
# 验证 CRDs 是否注册成功
kubectl get crds | grep istio
# 应该看到 destinationrules.networking.istio.io、
# virtualservices.networking.istio.io 等
# ========== 2.4 安装 istiod (控制面) ==========
# global.hub --- 指向 CCR 仓库路径,istiod 会拉 <hub>/pilot:<tag>
# global.imagePullSecrets --- 让 Pod 使用 CCR 凭证拉镜像
# pilot.resources --- Serverless 节点必须设置,默认值太小会被 OOM Kill
helm install istiod istio/istiod -n istio-system \
--set global.hub=ccr.ccs.tencentyun.com/hhs_open \
--set global.tag=1.29.2 \
--set 'global.imagePullSecrets[0]=ccr-registry-secret' \
--set pilot.resources.requests.memory=512Mi \
--set pilot.resources.limits.memory=1Gi \
--set pilot.resources.requests.cpu=250m \
--set pilot.resources.limits.cpu=1 \
--wait --timeout 5m
# ========== 2.5 验证 ==========
kubectl get pods -n istio-system
# NAME READY STATUS AGE
# istiod-xxxxxxxxxx-xxxxx 1/1 Running 60s
Serverless 节点 OOM 问题详解
css
flowchart LR
subgraph 普通节点["普通节点 (可超卖)"]
direction TB
P1["Pod request: 128Mi
Pod 实际用: 400Mi
节点总内存: 4Gi"]
P1 --> P1R["正常运行
(可以超出 request)"]
end
subgraph Serverless["Serverless 节点 (严格限制)"]
direction TB
P2["Pod limit: 128Mi
Pod 实际用: 400Mi"]
P2 --> P2R["OOM Kill!
(超出 limit 立即杀死)"]
end
style P1R fill:#4CAF50,color:#fff
style P2R fill:#F44336,color:#fff
TKE Serverless(EKS/弹性容器)和普通节点不同:
- 普通节点 :Pod 可以超出
requests使用内存,只要节点还有空闲 - Serverless 节点 :资源严格按
limits分配,超出就被系统杀掉(OOM Kill)
istiod 启动时需要约 300-500Mi 内存,默认的 limit 太小,所以必须显式设置。
Step 3: 安装 Kiali(流量可视化面板)
Kiali 做什么?
css
flowchart LR
subgraph DataSources["数据源"]
Prometheus["Prometheus
(指标数据)"]
Istio["istiod
(配置数据)"]
end
subgraph Kiali["Kiali 面板"]
direction TB
TrafficGraph["Traffic Graph
实时流量拓扑"]
Metrics["服务指标
请求量/延迟/错误率"]
Config["Istio 配置
VirtualService/DR/PA"]
end
Prometheus --> TrafficGraph
Prometheus --> Metrics
Istio --> Config
User[运维人员] -->|浏览器访问| Kiali
style Kiali fill:#9C27B0,color:#fff
style User fill:#4CAF50,color:#fff
Kiali 是 Istio 的官方可视化面板,它从 Prometheus 读取 istio_requests_total 等指标,渲染出实时的服务流量拓扑图,让你看到谁调用了谁、请求量多少、有没有错误。
操作命令
ini
# ========== 3.1 添加 Helm Repo ==========
helm repo add kiali https://kiali.org/helm-charts
helm repo update kiali
# ========== 3.2 安装 Kiali Server ==========
# deployment.image_name --- 指向 CCR 上的 Kiali 镜像
# 注意: 不是 image.repo,Kiali chart 的 key 是 deployment.image_name
# 用 helm show values kiali/kiali-server 可以查看所有可用的 key
#
# auth.strategy="token" --- 需要 K8s Token 登录,避免公网裸奔
# 可选值: anonymous(免登录), token(推荐), openid
#
# --set-string --- 当值是纯数字时必须用这个,否则 Helm 会把它当成 int64
# 导致模板渲染报错: "wrong type for value; expected string; got int64"
#
# external_services.prometheus --- 配置 Prometheus 地址和认证
# Kiali 需要从 Prometheus 读取 istio_requests_total 等指标来绘制流量图
helm install kiali-server kiali/kiali-server -n istio-system \
--set deployment.image_name=ccr.ccs.tencentyun.com/hhs_open/kiali \
--set deployment.image_version=v2.25.0 \
--set 'deployment.image_pull_secrets[0]=ccr-registry-secret' \
--set auth.strategy="token" \
--set external_services.prometheus.url="http://<Prometheus地址>:9090" \
--set external_services.prometheus.auth.type="basic" \
--set-string external_services.prometheus.auth.username="<Prometheus用户名>" \
--set-string external_services.prometheus.auth.password="<Prometheus密码>" \
--set external_services.grafana.enabled=false \
--set server.web_root="/" \
--wait --timeout 5m
# ========== 3.3 暴露到公网 ==========
# 方式: 将 Service 改为 LoadBalancer,TKE 自动创建 CLB 并分配外部地址
kubectl patch svc kiali -n istio-system -p '{"spec":{"type":"LoadBalancer"}}'
# 等待 30 秒后查看分配的地址
kubectl get svc kiali -n istio-system
# NAME TYPE EXTERNAL-IP
# kiali LoadBalancer lb-xxxxx.clb.bj-tencentclb.com
# 腾讯云不允许直接用 CLB 默认域名访问,需要解析出 IP
nslookup <CLB域名>
# 浏览器访问: http://<IP>:20001
# ========== 3.4 生成登录 Token ==========
# --duration=8760h 表示有效期 1 年
kubectl create token kiali -n istio-system --duration=8760h
# 复制输出的 token,在 Kiali 登录页粘贴即可
Kiali 认证流程
rust
sequenceDiagram
participant User as 浏览器
participant Kiali as Kiali Server
participant K8s as K8s API Server
User->>Kiali: 访问 http://IP:20001
Kiali->>User: 返回登录页面
User->>Kiali: 粘贴 ServiceAccount Token
Kiali->>K8s: 验证 Token 是否有效
K8s-->>Kiali: Token 有效,返回权限信息
Kiali-->>User: 登录成功,显示 Traffic Graph
Step 4: 准备业务部署前置资源
Namespace 与 Istio 注入机制

这是 K8s 和 Istio 配合的机制:
- K8s 提供 Admission Webhook 机制 --- 允许外部组件在 Pod 创建时拦截和修改 Pod spec
- Istio 安装时注册了 MutatingAdmissionWebhook --- 配置了
namespaceSelector匹配istio-injection=enabled istio-injection=enabled这个 label 名字是 Istio 自己定义的约定,不是 K8s 内置功能- 只有在打标签之后创建的 Pod 才会被注入。已存在的 Pod 需要重启才能注入。
操作命令(具体服务你自己捏一个)
csharp
# ========== 4.1 创建 Namespace + 开启 Istio 自动注入 ==========
kubectl create namespace lstio-test
kubectl label namespace lstio-test istio-injection=enabled
# 验证标签是否生效
kubectl get namespace lstio-test --show-labels
# 应该包含 istio-injection=enabled
# ========== 4.2 创建镜像拉取 Secret ==========
# 业务镜像存在 cnb.cool 私有仓库,K8s 拉取时需要认证
# 方式一:从旧集群复制(推荐,不需要知道密码)
kubectl get secret cnb-registry-secret -n production -o json | \
jq 'del(.metadata.uid,.metadata.resourceVersion,.metadata.creationTimestamp,.metadata.managedFields,.metadata.selfLink) | .metadata.namespace="lstio-test"' | \
kubectl apply -f -
# 方式二:从 yaml 文件创建(提前导出好的)
kubectl apply -f ./secret.yaml
# 方式三:手动创建
kubectl create secret docker-registry cnb-registry-secret \
--docker-server=docker.cnb.cool \
--docker-username=<用户名> \
--docker-password=<Access Token> \
-n lstio-test
# ========== 4.3 创建 ConfigMap(业务配置)==========
# 所有微服务的 Deployment 都挂载了 open-platform-config 作为 /etc/config.yaml
# 如果没有这个 ConfigMap,Pod 会一直 CreateContainerConfigError
# 方式一:临时占位(后续用 etcd 替代)
kubectl create configmap open-platform-config \
--from-literal=config.yaml="" \
-n lstio-test
# 方式二:从旧集群复制(需修改 gRPC endpoint 指向新 namespace)
kubectl get configmap open-platform-config -n production -o json | \
jq 'del(.metadata.uid,.metadata.resourceVersion,.metadata.creationTimestamp,.metadata.managedFields,.metadata.selfLink) | .metadata.namespace="lstio-test"' | \
kubectl apply -f -
Helm 部署时 values 的合并优先级(后面的覆盖前面的):
values.yaml--- Chart 全局默认值-f shop-values.yaml--- 服务专属配置(server.name、image.repository 等)-f values-common-istio-test.yaml--- 环境公共配置(GIN_MODE、Istio 策略等)--set image.tag=develop--- 命令行覆盖(最高优先级)
操作命令
bash
CHART=./helm-istio-charts
COMMON=$CHART/_common/values-common-istio-test.yaml
# ========== Gateway(必须第一个部署,它创建 Gateway API 资源)==========
helm upgrade --install gateway $CHART -n lstio-test \
-f $CHART/gateway-values.yaml -f $COMMON \
--set image.tag=develop --wait --timeout 3m
# ========== 后端服务(顺序无所谓)==========
helm upgrade --install shop $CHART -n lstio-test \
-f $CHART/shop-values.yaml -f $COMMON \
--set image.tag=develop --wait --timeout 3m
helm upgrade --install order $CHART -n lstio-test \
-f $CHART/order-values.yaml -f $COMMON \
--set image.tag=test --wait --timeout 3m
helm upgrade --install order-status $CHART -n lstio-test \
-f $CHART/order-status-values.yaml -f $COMMON \
--set image.tag=develop --wait --timeout 3m
helm upgrade --install delivery $CHART -n lstio-test \
-f $CHART/delivery-values.yaml -f $COMMON \
--set image.tag=develop --wait --timeout 3m
helm upgrade --install cron $CHART -n lstio-test \
-f $CHART/cron-values.yaml -f $COMMON \
--set image.tag=develop --wait --timeout 3m
helm upgrade --install admin $CHART -n lstio-test \
-f $CHART/admin-values.yaml -f $COMMON \
--set image.tag=release --wait --timeout 3m
每个 helm install 创建了什么?
css
flowchart TD
HelmInstall["helm upgrade --install shop ..."] --> Deployment["Deployment
name: shop-service
replicas: 1
containers: 业务 + istio-proxy(自动注入)
labels: app=shop-service, version=v1"]
HelmInstall --> ServiceHTTP["Service (HTTP)
name: shop-service-http
port: 8082
appProtocol: http"]
HelmInstall --> ServiceGRPC["Service (gRPC)
name: shop-service-grpc
port: 8081
appProtocol: grpc"]
HelmInstall --> DR["DestinationRule
连接池: maxConnections=50
熔断: 5次5xx → 驱逐30s
TLS: DISABLE (明文)"]
HelmInstall --> PA["PeerAuthentication
mTLS mode: DISABLE
(关闭 mTLS,减少加解密开销)"]
HelmInstall --> SM["ServiceMonitor
Prometheus 自动发现并抓取 /metrics"]
style HelmInstall fill:#2196F3,color:#fff
style Deployment fill:#4CAF50,color:#fff
style DR fill:#FF9800,color:#fff
style PA fill:#9C27B0,color:#fff
Step 6: 暴露业务入口
yaml
# 方式一:LoadBalancer(公网 IP 直接访问,适合 demo)
kubectl patch svc gateway-http -n lstio-test -p '{"spec":{"type":"LoadBalancer"}}'
# 等待分配地址
kubectl get svc gateway-http -n lstio-test
# 解析 CLB 域名获取 IP(腾讯云不允许直接用 CLB 默认域名访问)
nslookup <CLB域名>
# 访问: http://<IP>:80
# 方式二:Ingress(需要 NGINX Ingress Controller)
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gateway
namespace: lstio-test
spec:
ingressClassName: nginx
rules:
- host: <你的域名>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gateway-http
port:
number: 80
EOF
Step 7: 验证
csharp
# ========== Pod 状态(应全部 2/2 Running)==========
kubectl get pods -n lstio-test
# 2/2 表示:业务容器 + istio-proxy sidecar 都在运行
# ========== Istio 资源 ==========
kubectl get gateway,httproute -n lstio-test
kubectl get destinationrule -n lstio-test
kubectl get peerauthentication -n lstio-test
# ========== Helm releases ==========
helm list -n lstio-test
# ========== 业务健康检查 ==========
curl http://<外部IP>/healthz
# ========== istio-system 全景 ==========
kubectl get pods -n istio-system
# istiod 1/1 Running
# kiali 1/1 Running
mTLS 配置说明
当前状态:已关闭 (DISABLE)
为了减少 TLS 握手和加解密的性能开销,当前配置已关闭 mTLS,服务间通信走明文。
css
flowchart LR
subgraph mTLSEnabled["开启 mTLS (ISTIO_MUTUAL)"]
direction TB
A1[Service A] -->|1. TLS 握手| B1[Service B]
A1 -->|2. 证书验证| B1
A1 -->|3. 加密传输| B1
A1 -->|4. 解密处理| B1
B1 --> C1["延迟: +2~5ms/请求
CPU: +10~15%"]
end
subgraph mTLSDisabled["关闭 mTLS (DISABLE) ← 当前"]
direction TB
A2[Service A] -->|直接明文传输| B2[Service B]
B2 --> C2["延迟: 无额外开销
CPU: 无额外开销"]
end
style C1 fill:#FF9800,color:#fff
style C2 fill:#4CAF50,color:#fff
style mTLSDisabled fill:#E8F5E9,color:#333
涉及的配置文件
| 文件 | 配置项 | 值 | 作用 |
|---|---|---|---|
_common/values-common-istio-test.yaml |
istio.peerAuthentication.mtls.mode |
DISABLE |
sidecar 不要求加密连接 |
_common/values-common-istio-test.yaml |
istio.destinationRule.trafficPolicy.tlsMode |
DISABLE |
发起请求时不走 mTLS |
templates/destination-rule.yaml |
tls.mode |
读取 tlsMode 值 |
模板支持可配置化 |
mTLS 模式对比
| 模式 | PeerAuthentication | DestinationRule tls | 效果 |
|---|---|---|---|
| DISABLE (当前) | DISABLE |
DISABLE |
完全明文,无加解密开销 |
| PERMISSIVE | PERMISSIVE |
ISTIO_MUTUAL |
接受明文+加密,发出加密 |
| STRICT | STRICT |
ISTIO_MUTUAL |
强制双向加密,最安全 |
关闭 mTLS 后仍保留的功能
关闭 mTLS 只是去掉了加解密,Istio sidecar 仍在工作,以下功能不受影响:
- 连接池管理(
connectionPool) - 熔断/异常检测(
outlierDetection) - 流量路由(
VirtualService权重路由、Header 路由) - 流量可视化(Kiali Traffic Graph)
- Prometheus 指标采集(
istio_requests_total等) - 金丝雀发布
如果后续需要开启 mTLS
修改 _common/values-common-istio-test.yaml:
yaml
istio:
peerAuthentication:
mtls:
mode: STRICT # 或 PERMISSIVE
destinationRule:
trafficPolicy:
tlsMode: ISTIO_MUTUAL
然后重新 helm upgrade 所有服务即可生效。
踩坑记录
问题与解决方案总览
css
flowchart TD
P1["DaoCloud 镜像超时
ImagePullBackOff
i/o timeout"] -->|解决| S1["推到自己的 CCR 仓库
TKE 内网拉取"]
P2["CCR 推送 DENIED
no permission"] -->|解决| S2["先在 CCR 控制台
创建命名空间"]
P3["exec format error
CrashLoopBackOff"] -->|解决| S3["docker pull
--platform linux/amd64"]
P4["OOM Kill
SystemOOM"] -->|解决| S4["显式设置
memory: 512Mi/1Gi"]
P5["Kiali 镜像版本不匹配
ImagePullBackOff"] -->|解决| S5["helm show values 确认版本
重新推送正确版本"]
P6["Helm --set 纯数字报错
expected string; got int64"] -->|解决| S6["用 --set-string
代替 --set"]
P7["CLB 默认域名不可访问
腾讯云法规限制"] -->|解决| S7["nslookup 解析 IP
直接用 IP 访问"]
style P1 fill:#F44336,color:#fff
style P2 fill:#F44336,color:#fff
style P3 fill:#F44336,color:#fff
style P4 fill:#F44336,color:#fff
style P5 fill:#F44336,color:#fff
style P6 fill:#F44336,color:#fff
style P7 fill:#F44336,color:#fff
style S1 fill:#4CAF50,color:#fff
style S2 fill:#4CAF50,color:#fff
style S3 fill:#4CAF50,color:#fff
style S4 fill:#4CAF50,color:#fff
style S5 fill:#4CAF50,color:#fff
style S6 fill:#4CAF50,color:#fff
style S7 fill:#4CAF50,color:#fff
详细排障表
| # | 问题 | Pod 状态 | 关键报错 | 原因 | 解决方案 |
|---|---|---|---|---|---|
| 1 | DaoCloud 镜像超时 | ImagePullBackOff |
dial tcp 101.132.188.159:443: i/o timeout |
公共镜像加速不稳定 | 推到自己的 CCR 仓库 |
| 2 | CCR 推送权限拒绝 | 推送阶段 | DENIED: no permission |
CCR 上不存在目标命名空间 | 先在 CCR 控制台创建命名空间,或用已有的 |
| 3 | CPU 架构不匹配 | CrashLoopBackOff |
exec /usr/local/bin/pilot-discovery: exec format error |
Mac ARM 芯片默认拉 arm64,TKE 节点是 amd64 | docker pull --platform linux/amd64 |
| 4 | 内存不足 OOM | CrashLoopBackOff |
CGroup OOM encountered, victim process: pilot-discovery |
Serverless 节点严格限制资源,默认 limit 太小 | 设置 requests.memory=512Mi, limits.memory=1Gi |
| 5 | Kiali 镜像版本 | ImagePullBackOff |
推了 v2.24.0 但 chart 要 v2.25.0 | Helm chart 更新了默认版本 | helm show values 确认版本后重新推送 |
| 6 | Kiali image 配置不生效 | ImagePullBackOff |
仍然拉 quay.io | --set image.repo 不是正确的 key |
正确 key 是 deployment.image_name,用 helm show values 查 |
| 7 | Helm 纯数字报错 | 安装失败 | wrong type for value; expected string; got int64 |
Prometheus 用户名是纯数字 | 用 --set-string 代替 --set |
| 8 | CLB 域名不可访问 | 服务正常 | 浏览器被拦截 | 国家法规禁止直接用 CLB 默认域名 | nslookup 解析 IP,用 IP 访问 |