操作场景
本文介绍了 Dubbo 应用托管到腾讯云容器服务 TKE 的最佳实践。
Dubbo 应用托管到 TKE 的优势
- 提升资源利用率。
- Kubernetes 天然适合微服务架构。
- 提升运维效率,便于 Devops 落地实施。
- Kubernetes 的高弹性,可轻松实现应用的动态扩缩容。
- 容器服务 TKE 提供 Kubernetes Master 托管功能,可减少 Kubernetes 集群运维和管理的负担。
- 容器服务 TKE 和腾讯云的其他云原生产品进行了整合和优化,帮助用户更好的使用腾讯云上产品。
最佳实践实例介绍
本文以 Q 云书城(Q Cloud Book Mall,QCBM)项目为最佳实践实例,详细介绍 Dubbo 应用托管到 TKE 的过程。
QCBM 概述
QCBM 首页如下图展所示:
QCBM 是采用微服务架构,并使用 dubbo-2.7.8 框架开发的一个网上书城 Demo 项目。QCBM 的部署和代码托管在 Coding,详情可参见 QCBM 项目。QCBM 包含以下微服务:
微服务 | 说明 |
---|---|
QCBM-Front | 使用 React 开发的前端项目,基于 Nginx 官方提供的 1.19.8 Docker 镜像 构建和部署。 |
QCBM-Gateway | API 网关,接受前端的 HTTP 请求,并将其转化为后台的 Dubbo 请求。 |
User-Service | 基于 Dubbo 的微服务,提供用户注册、登录、鉴权等功能。 |
Favorites-Service | 基于 Dubbo 的微服务,提供用户图书收藏功能。 |
Order-Service | 基于 Dubbo 的微服务,提供用户订单生成和查询等功能。 |
Store-Service | 基于 Dubbo 的微服务,提供图书信息的存储等功能。 |
QCBM 架构和组件
本文最佳实践实例模拟将原先部署在云服务器 CVM 的应用进行容器化,并托管到容器服务 TKE 的场景。在该场景中需要采用一个 VPC,并划分为以下两个子网:
- Subnet-Basic:部署有状态的基础服务,包括 Dubbo 的服务注册中心 Nacos、MySQL 和 Redis 等。
- Subnet-K8S:部署 QCBM 的应用服务,所有服务都进行容器化,并运行在容器服务 TKE 上。
子网划分如下图所示:
QCBM 实例的网络规划如下表所示:
网络规划 | 说明 |
---|---|
Region/AZ | 南京/南京一区 |
VPC | CIDR:10.0.0.0/16 |
子网 Subnet-Basic | 南京一区,CIDR:10.0.1.0/24 |
子网 Subnet-K8S | 南京一区,CIDR:10.0.2.0/24 |
Nacos 集群 | 采用3台 “标准型SA2” 1C2G 机型的 CVM 构建 Nacos 集群,对应的 IP 为:10.0.1.9,10.0.1.14,10.0.1.15 |
QCBM 实例中用到的组件如下表所示:
组件 | 版本 | 来源 | 备注 |
---|---|---|---|
k8s | 1.8.4 | 腾讯云 | TKE 托管模式 |
MySQL | 5.7 | 腾讯云 | TencentDB for MySQL 双节点 |
Redis | 5.0 | 腾讯云 | TencentDB for Redis 标准型 |
CLS | N/A | 腾讯云 | 日志服务 |
TSW | N/A | 腾讯云 | 采用 Skywalking 8.4.0 版的 Agent 接入,点此 下载 |
Java | 1.8 | 开源社区 | Docker 镜像为 java:8-jre |
Nacos | 2.0.0 | 开源社区 | 点此 下载 |
Dubbo | 2.7.8 | 开源社区 | Github 地址 |
服务介绍
TCR 介绍
腾讯云 容器镜像服务 TCR 提供个人版和企业版两种镜像仓库。两者区别如下图所示:
QCBM 是一个 Dubbo 容器化的 Demo 项目,因此容器镜像服务个人版完全满足需求。但对于企业用户,推荐使用 容器镜像服务企业版。如需使用镜像仓库,请参见 镜像仓库基本操作。
TSW 介绍
腾讯微服务观测平台 TSW(Tencent Service Watcher)提供云原生服务可观察性解决方案,能够追踪到分布式架构中的上下游依赖关系,绘制拓扑图,提供服务、接口、实例、中间件等多维度调用观测。详细介绍如下图所示:
TSW 在架构上分为以下四大模块:
数据采集(Client)
展开&收起使用开源探针或 SDK 用于采集数据。对于迁移上云的用户,可保留 Client 端的大部分配置,仅更改上报地址和鉴权信息即可。
数据处理(Server)
展开&收起数据经由 Pulsar 消息队列上报到 Server,同时 Adapter 会将数据转换为统一的 Opentracing 兼容格式。根据数据的使用场景,分配给实时计算与离线计算:
- 实时计算提供实时监控、统计数据展示,并对接告警平台快速响应。
- 离线计算处理长时段大量数据的统计汇聚,利用大数据分析能力提供业务价值。
存储(Storage)
展开&收起存储层可满足不同数据类型的使用场景,适配 Server 层的写入与 Data Usage 层的查询与读取请求。
数据使用(Data Usage)
展开&收起为控制台操作、数据展示、告警提供底层支持。
架构图如下所示:
操作步骤
搭建基础服务集群
- 在 Mysql 控制台 创建实例,并使用 qcbm-ddl.sql 初始化。详情请参见 创建 MySQL 实例。
- 在 Redis 控制台 创建实例并初始化。详情请参见 创建 Redis 实例。
- 在 负载均衡控制台 为子网 Subnet-K8S 新建一个内网型的负载均衡(后续实践中会使用到该 CLB 实例 ID)。详情请参见 创建负载均衡实例。
- 申请通过 TSW 内测。TSW 目前处于内测阶段,支持 Java 和 Golang 两种语言接入。
- 部署 Nacos 集群:
构建 Docker 镜像
编写 Dockerfile
下文以 user-service 为例为您简单介绍如何编写 Dockerfile。示例展示的是 user-service 的工程目录结构,Dockerfile 位于工程的根目录下,user-service-1.0.0.zip 是打包后的文件,需要添加到镜像中。
➜ user-service tree
├── Dockerfile
├── assembly
│ ....
├── bin
│ ....
├── pom.xml
├── src
│ ....
├── target
│ .....
│ └── user-service-1.0.0.zip
└── user-service.iml
user-service 的 Dockerfile 如下所示:
FROM java:8-jre
ARG APP_NAME=user-service
ARG APP_VERSION=1.0.0
ARG FULL_APP_NAME=${APP_NAME}-${APP_VERSION}
# 容器中的工作目录为 /app
WORKDIR /app
# 将本地打包出来的应用添加到镜像中
COPY ./target/${FULL_APP_NAME}.zip .
# 创建日志目录 logs,解压并删除原始文件和解压后的目录
RUN mkdir logs \
&& unzip ${FULL_APP_NAME}.zip \
&& mv ${FULL_APP_NAME}/** . \
&& rm -rf ${FULL_APP_NAME}*
# user-service 的启动脚本和参数
ENTRYPOINT ["/app/bin/user-service.sh"] CMD ["start", "-t"]
# dubbo 端口号
EXPOSE 20880
注意:
- 生产中的 Java 应用有很多配置参数,导致启动脚本很复杂。将启动脚本里的内容全部写到 dockerfile 中工作量很大,其次 dockerfile 远没有 Shell 脚本灵活,若出现问题也无法快速定位,因此不建议弃用启动脚本。
- 通常在启动脚本最后使用 nohup 启动 Java 应用,但该方式启动的 deamon 进程会导致容器运行后直接退出。因此
nohup java ${OPTIONS} -jar user-service.jar > ${LOG_PATH} 2>&1 &
需改成java ${OPTIONS} -jar user-service.jar > ${LOG_PATH} 2>&1
。- Dockerfile 中每多一个 RUN 命令,生成的镜像就多一层,推荐将这些 RUN 命令合成一条。
构建镜像
容器镜像服务 TCR 提供了自动和手工构建镜像方式,详情可参见 镜像构建 文档。为展示具体的构建过程,本文采用手工构建方式。
镜像名称需要符合规范 ccr.ccs.tencentyun.com/[namespace]/[ImageName]:[镜像版本号]
:
- 其中命名 namespace 为方便镜像管理使用,可以按项目取名。本文采用 QCBM 表示 Q 云书城项目下的所有镜像。
- ImageName 可以包含 subpath,一般用于企业用户多项目场景。此外,如果本地已构建好镜像,可使用
docker tag
命令,按命名规范对镜像重命名。
- 执行以下命令构建镜像。示例如下:
# 推荐的构建方式,可省去二次打 tag 操作 sudo docker build -t ccr.ccs.tencentyun.com/[namespace]/[ImageName]:[镜像版本号] # 本地构建 user-service 镜像,最后一个 . 表示 Dockerfile 存放在当前目录(user-service)下 ➜ user-service docker build -t ccr.ccs.tencentyun.com/qcbm/user-service:1.0.0 . # 将已存在镜像按命名规范对镜像重命名 sudo docker tag [ImageId] ccr.ccs.tencentyun.com/[namespace]/[ImageName]:[镜像版本号]
- 构建完成后,可执行以下命令查看本地仓库中的所有镜像。
示例如下图所示:docker images
上传镜像到 TCR
创建命名空间
QCBM 项目采用个人版镜像仓库(建议企业客户使用企业版镜像仓库)。
- 登录 容器服务控制台 。
- 选择镜像仓库 > 个人版 > 命名空间进入“命名空间”页面。
- 单击新建,在弹出的新建命名窗口中新建命名空间 qcbm。QCBM 项目所有的镜像都存放于该命名空间下。如下图所示:
上传镜像
上传镜像需要完成以下步骤:登录腾讯云 registry 和上传镜像。
- 执行以下命令登录腾讯云 registry。
docker login --username=[腾讯云账号 ID] ccr.ccs.tencentyun.com
- 执行以下命令将本地生成的镜像推送至 TKE 的镜像仓库中。
如下图所示:docker push ccr.ccs.tencentyun.com/[namespace]/[ImageName]:[镜像版本号]
- 在 我的镜像 中可以查看上传的所有镜像,下图展示的是上传到腾讯云镜像仓库中 QCBM 的5个镜像。说明
默认镜像类型为“私有”,如需提供镜像给他人使用,可在镜像信息中将镜像类型设置为公有。如下图所示:
在 TKE 上部署服务
创建 k8s 集群 QCBM
- 实际部署前,需要新建一个 k8s 集群。有关集群的创建,请参见 创建集群 文档。
注意:
创建集群时,在“选择机型”页面建议开启“置放群组功能”,该功能可将 CVM 打散到不同母机上,增加系统可靠性。如下图所示:
- 集群创建完成后,在容器服务控制台的 集群管理 页面可以查看新建的集群信息。本文新建的集群名称为 qcbm-k8s-demo。如下图所示:
- 单击集群名称进入“基本信息”页面,查看集群的配置信息。如下图所示:
- (可选)如需使用 Kubectl 和 lens 等 k8s 管理工具,还需进行以下两步操作:
- 开启外网访问。
- 将 API 认证 Token 保存在本地
用户 home/.kube
下的 config 文件中(若 config 文件已有内容,则需要替换),以确保每次访问都能进入默认集群中。如果选择不将 API 认证 Token 保存在.kube
下的 config 文件中,则可参考控制台集群APIServer信息下的 通过Kubectl连接Kubernetes集群操作说明。如下图所示:
创建 Namespace
Namespaces 是 Kubernetes 在同一个集群中进行逻辑环境划分的对象,通过 Namespaces 可以进行多个团队多个项目的划分。您可以通过以下三种方式创建 Namespace,推荐使用方式1命令行方式创建。
执行以下命令即可创建 Namespace:
kubectl create namespace qcbm
ConfigMap 存放配置信息
通过 ConfigMap 可以将配置和运行的镜像进行解耦,使应用程序有更强的移植性。QCBM 后端服务需要从环境变量中获取 Nacos、MySQL、Redis 主机和端口信息,并将其使用 ConfigMap 进行保存。
您可通过以下两种方式使用 ConfigMap 存放配置信息:
下文为 QCBM 的 ConfigMap YAML,其中纯数字类型的 value 需要使用双引号。例如,下文示例 YAML 中的 MYSQL_PORT:
# 创建 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: qcbm-env
namespace: qcbm
data:
NACOS_HOST: 10.0.1.9
MYSQL_HOST: 10.0.1.13
REDIS_HOST: 10.0.1.16
NACOS_PORT: "8848"
MYSQL_PORT: "3306"
REDIS_PORT: "6379"
SW_AGENT_COLLECTOR_BACKEND_SERVICES: xxx # TSW 接入地址,后文介绍
使用 Secret 存放敏感信息
Secret 可用于存储密码、令牌、密钥等敏感信息,降低直接对外暴露的风险。QCBM 使用 Secret 来保存相关的账号和密码信息。
您可通过以下两种方式使用 Secret 存放敏感信息:
下文为 QCBM 创建 Secret 的 YAML。其中 Secret 的 value 需要是 base64 编码后的字符串。
# 创建 Secret
apiVersion: v1
kind: Secret
metadata:
name: qcbm-keys
namespace: qcbm
labels:
qcloud-app: qcbm-keys
data:
# xxx 为base64 编码后的字符串,可使用 shell 命令 “echo -n 原始字符串 | base64” 生成
MYSQL_ACCOUNT: xxx
MYSQL_PASSWORD: xxx
REDIS_PASSWORD: xxx
SW_AGENT_AUTHENTICATION: xxx # TSW 接入 token,后文介绍
type: Opaque
部署工作负载 Deployment
Deployment 声明了 Pod 的模板和控制 Pod 的运行策略,适用于部署无状态的应用程序。QCBM 的 front 和 Dubbo 服务都属于无状态应用,适合使用 Deployment。
以下是 user-service Deployment 的 YAML 参数说明:
参数 | 说明 |
---|---|
replicas | 表示需要创建的 pod 数量 |
image | 镜像的地址 |
imagePullSecrets | 拉取镜像时需要使用的 key,可在 集群>配置管理 > Secret中获取。使用公共镜像时可省略 |
env | |
ports | 指定容器的端口号,由于是 Dubbo 应用,所以端口号为20880 |
user-service Deployment 的 完整 YAML 文件示例如下:
# user-service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: qcbm
labels:
app: user-service
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: user-service
version: v1
template:
metadata:
labels:
app: user-service
version: v1
spec:
containers:
- name: user-service
image: ccr.ccs.tencentyun.com/qcbm/user-service:1.1.4
env:
- name: NACOS_HOST # dubbo服务注册中心nacos的IP地址
valueFrom:
configMapKeyRef:
key: NACOS_HOST
name: qcbm-env
optional: false
- name: MYSQL_HOST # Mysql 地址
valueFrom:
configMapKeyRef:
key: MYSQL_HOST
name: qcbm-env
optional: false
- name: REDIS_HOST # Redis的IP地址
valueFrom:
configMapKeyRef:
key: REDIS_HOST
name: qcbm-env
optional: false
- name: MYSQL_ACCOUNT # Mysql 账号
valueFrom:
secretKeyRef:
key: MYSQL_ACCOUNT
name: qcbm-keys
optional: false
- name: MYSQL_PASSWORD # Mysql 密码
valueFrom:
secretKeyRef:
key: MYSQL_PASSWORD
name: qcbm-keys
optional: false
- name: REDIS_PASSWORD # Redis 密码
valueFrom:
secretKeyRef:
key: REDIS_PASSWORD
name: qcbm-keys
optional: false
- name: SW_AGENT_COLLECTOR_BACKEND_SERVICES # Skywalking 后端服务地址
valueFrom:
configMapKeyRef:
key: SW_AGENT_COLLECTOR_BACKEND_SERVICES
name: qcbm-env
optional: false
- name: SW_AGENT_AUTHENTICATION # Skywalking agent 连接后端服务的认证 token
valueFrom:
secretKeyRef:
key: SW_AGENT_AUTHENTICATION
name: qcbm-keys
optional: false
ports:
- containerPort: 20880 # dubbo 端口号
protocol: TCP
imagePullSecrets: # 拉取镜像时需要使用的 key,QCBM 所有服务的镜像已开放为公共镜像,故此处可省略
- name: qcloudregistrykey
部署服务 Service
Kubernetes 的 ServiceTypes 允许指定 Service 类型,默认为 ClusterIP 类型。ServiceTypes 可取如下值:
- LoadBalancer:提供公网、VPC、内网访问。
- NodePort:可通过“云服务器 IP + 主机端口”访问服务。
- ClusterIP:可通过“服务名 + 服务端口”访问服务。
对于实际生产系统来说,gateway 需要能在 VPC 或内网范围内进行访问, front 前端需要能对内/外网提供访问。因此,QCBM 的 gateway 和 front 需要制定 LoadBalancer 类型的 ServiceType。
TKE 对 LoadBalancer 模式进行了扩展,通过 Annotation 注解配置 Service,可实现更丰富的负载均衡能力。
若使用 service.kubernetes.io/qcloud-loadbalancer-internal-subnetid
注解,在 service 部署时,会创建内网类型 CLB。一般建议事先创建好 CLB,service 的部署 YAML 中使用注解 service.kubernetes.io/loadbalance-id
直接指定,可提升部署效率。
以下为 qcbm-front service 部署 YAML:
# 部署 qcbm-front service
apiVersion: v1
kind: Service
metadata:
name: qcbm-front
namespace: qcbm
annotations:
# Subnet-K8S 子网的 CLB 实例 ID
service.kubernetes.io/loadbalance-id: lb-66pq34pk
spec:
externalTrafficPolicy: Cluster
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
selector: # 将后端服务 qcbm-gateway 和该 Service 进行映射
app: qcbm-front
version: v1
type: LoadBalancer
部署 Ingress
Ingress 是允许访问到集群内 Service 规则的集合。一般使用 Ingress 提供对外访问,而不直接暴露 Service 。QCBM 项目需要为 qcbm-front 创建 Ingress,对应的 YAML 如下:
# 部署 qcbm-front ingress
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: front
namespace: qcbm
annotations:
ingress.cloud.tencent.com/direct-access: "false"
kubernetes.io/ingress.class: qcloud
kubernetes.io/ingress.extensiveParameters: '{"AddressIPVersion":"IPV4"}'
kubernetes.io/ingress.http-rules: '[{"host":"qcbm.com","path":"/","backend":{"serviceName":"qcbm-front","servicePort":"80"}}]'
spec:
rules:
- host: qcbm.com
http:
paths:
- path: /
backend: # 关联到后端服务
serviceName: qcbm-front
servicePort: 80
查看部署结果
至此,您已完成 QCBM 在容器服务 TKE 上的部署,可通过以下步骤查看部署结果:
- 登录 容器服务控制台,单击集群 ID/名称进入集群详情页面。
- 单击服务与路由 > Ingress进入 Ingress 页面,可查看到创建的 Ingress。通过 Ingress 的 VIP 即可访问 Q 云书城页面。
集成 CLS 日志服务
开启容器日志采集功能
容器日志采集功能默认关闭,使用前需要开启,步骤如下:
- 登录容器服务控制台,选择左侧导航栏中的集群运维 > 功能管理。
- 在“功能管理”页面上方选择地域,单击需要开启日志采集的集群右侧的设置。
- 在“设置功能”页面,单击日志采集编辑并勾选开启日志采集。如下图所示:
- 单击确定即可开启容器日志采集功能。
创建日志主题和日志集
QCBM 部署在南京地域,因此在创建日志集时应当选择南京地域:
- 登录 日志服务控制台,在“日志主题”页面选择南京地域。
- 单击创建日志主题,在弹出的窗口中根据页面提示填写相关信息,如下图所示:
- 日志主题名称:输入 qcbm。
- 日志集操作:选择创建日志集。
- 日志集名称:输入 qcbm-logs。
- 单击确定即可创建日志主题和日志集。
说明:
QCBM 有多个后端微服务,为每个微服务建个日志主题便于日志归类。
- QCBM 每个服务都建立了一个日志主题。
- 日志主题 ID,为容器创建日志规则时需要用到。
配置日志采集规则
您可通过控制台或 CRD 两种方式配置容器日志采集规则。
查看日志
- 登录 日志服务控制台,进入“检索分析”页面。
- 检索分析中可先为日志新建索引,索引完毕之后再单击检索分析即可查看日志。
注意:若未新建索引,则检索不到日志。
集成 TSW 观测服务
TSW 目前处于内测阶段,可在广州和上海进行部署,本文选择上海接入(QCBM 部署在南京)。
接入 TSW — 获取接入点信息
- 登录 腾讯微服务观测平台控制台,选择左侧导航栏种的服务观测 > 服务列表。
- 单击接入服务,选择 Java 语言与 SkyWalking 的数据采集方式。接入方式下提供了如下接入信息:接入点和 Token。
接入 TSW — 应用和容器配置
将上一步骤中获取的 TSW 的接入点和 Token 分别填写到 skywalking 的 agent.config 配置项中的 collector.backend_service 和 agent.authentication。“agent.service_name” 配置对应的服务名称,可使用 “agent.namespace” 对同一领域下的微服务归类。如下图为 user-service 配置:
Skywalking agent 也支持使用环境变量方式进行配置,QCBM 使用 ConfigMap 和 Secret 配置对应的环境变量:
- 使用 ConfigMap 配置 SW_AGENT_COLLECTOR_BACKEND_SERVICES
- 使用 Secret 配置 SW_AGENT_AUTHENTICATION
如下图所示:
至此 TSW 接入工作已完成,启动容器服务后,在 TSW 控制台即可查看调用链、服务拓扑、SQL 分析等功能。
使用 TSW 观测服务
通过服务接口和调用链查看调用异常
- 登录 腾讯微服务观测平台控制台,选择左侧导航栏中的服务观测 > 接口观测。
- 在接口观测页面可查看一个服务下所有接口的调用情况,包括请求量、成功率、错误率、响应时间等指标。如下图所示:
- 上图中 qcbm-gateway 的两个接口:查询用户收藏夹
/api/favorites/query/{userId}
和查询用户订单/api/order/{userId}
出现调用异常。单击查询用户收藏夹接口,可以查看到该接口的所有调用记录,找到异常的调用链,单击进入可以查看具体异常原因。如下图所示:
通过分析可以发现 favorites-service 因 time-out 导致调用异常。如下图所示:
使用 TSW 分析 SQL 和缓存等组件调用情况
- 登录 腾讯微服务观测平台控制台,选择左侧导航栏中的组件调用观测 > SQL 调用。
- 在“SQL 调用”页面可查看 SQL、NOSQL、MQ 及其它组件的调用情况。例如,通过 SQL 的请求量及耗时,可以快速定位应用中的高频 SQL 和慢查询。如下图所示:
查看服务拓扑
- 登录 腾讯微服务观测平台控制台,选择左侧导航栏中的链路追踪 > 分布式依赖拓扑。
- 在“分布式依赖拓扑”页面可查看完成的服务依赖情况,以及调用次数和平均延迟等信息。如下图所示: