本文介绍了 Dubbo 应用托管到腾讯云容器服务 TKE 的最佳实践。
本文以 Q 云书城(Q Cloud Book Mall,QCBM)项目为最佳实践实例,详细介绍 Dubbo 应用托管到 TKE 的过程。
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 的微服务,提供图书信息的存储等功能。 |
本文最佳实践实例模拟将原先部署在云服务器 CVM 的应用进行容器化,并托管到容器服务 TKE 的场景。在该场景中需要采用一个 VPC,并划分为以下两个子网:
子网划分如下图所示:
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 提供个人版和企业版两种镜像仓库。两者区别如下图所示:
QCBM 是一个 Dubbo 容器化的 Demo 项目,因此容器镜像服务个人版完全满足需求。但对于企业用户,推荐使用 容器镜像服务企业版。如需使用镜像仓库,请参见 镜像仓库基本操作。
腾讯微服务观测平台 TSW(Tencent Service Watcher)提供云原生服务可观察性解决方案,能够追踪到分布式架构中的上下游依赖关系,绘制拓扑图,提供服务、接口、实例、中间件等多维度调用观测。详细介绍如下图所示:
TSW 在架构上分为以下四大模块:
使用开源探针或 SDK 用于采集数据。对于迁移上云的用户,可保留 Client 端的大部分配置,仅更改上报地址和鉴权信息即可。
数据经由 Pulsar 消息队列上报到 Server,同时 Adapter 会将数据转换为统一的 Opentracing 兼容格式。根据数据的使用场景 ,分配给实时计算与离线计算:
存储层可满足不同数据类型的使用场景 ,适配 Server 层的写入与 Data Usage 层的查询与读取请求。
为控制台操作、数据展示、告警提供底层支持。
架构图如下所示:
下文以 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]:[镜像版本号]
:
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
示例如下图所示:
QCBM 项目采用个人版镜像仓库(建议企业客户使用企业版镜像仓库)。
上传镜像需要完成以下步骤:登录腾讯云 registry 和上传镜像。
执行以下命令登录腾讯云 registry。
docker login --username=[腾讯云账号 ID] ccr.ccs.tencentyun.com
执行以下命令将本地生成的镜像推送至 TKE 的镜像仓库中。
docker push ccr.ccs.tencentyun.com/[namespace]/[ImageName]:[镜像版本号]
如下图所示:
在 我的镜像 中可以查看上传的所有镜像,下图展示的是上传到腾讯云镜像仓库中 QCBM 的5个镜像。
默认镜像类型为“私有”,如需提供镜像给他人使用,可在镜像信息中将镜像类型设置为公有。如下图所示:
注意:创建集群时,在“选择机型”页面建议开启“置放群组功能”,该功能可将 CVM 打散到不同母机上,增加系统可靠性。
用户 home/.kube
下的 config 文件中(若 config 文件已有内容,则需要替换),以确保每次访问都能进入默认集群中。如果选择不将 API 认证 Token 保存在 .kube
下的 config 文件中,则可参考控制台集群APIServer信息下的 通过Kubectl连接Kubernetes集群操作说明。如下图所示:Namespaces 是 Kubernetes 在同一个集群中进行逻辑环境划分的对象,通过 Namespaces 可以进行多个团队多个项目的划分。您可以通过以下三种方式创建 Namespace,推荐使用方式1命令行方式创建。
执行以下命令即可创建 Namespace:
kubectl create namespace qcbm
通过 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 可用于存储密码、令牌、密钥等敏感信息,降低直接对外暴露的风险。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 声明了 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
Kubernetes 的 ServiceTypes 允许指定 Service 类型,默认为 ClusterIP 类型。ServiceTypes 可取如下值:
对于实际生产系统来说,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 是允许访问到集群内 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 上的部署,可通过以下步骤查看部署结果:
容器日志采集功能默认关闭,使用前需要开启,步骤如下:
QCBM 部署在南京地域,因此在创建日志集时应当选择南京地域:
说明:QCBM 有多个后端微服务,为每个微服务建个日志主题便于日志归类。
- QCBM 每个服务都建立了一个日志主题。
- 日志主题 ID,为容器创建日志规则时需要用到。
您可通过控制台或 CRD 两种方式配置容器日志采集规则。
注意:若未新建索引,则检索不到日志。
TSW 目前处于内测阶段,可在广州和上海进行部署,本文选择上海接入(QCBM 部署在南京)。
将上一步骤中获取的 TSW 的接入点和 Token 分别填写到 skywalking 的 agent.config 配置项中的 collector.backend_service 和 agent.authentication。“agent.service_name” 配置对应的服务名称,可使用 “agent.namespace” 对同一领域下的微服务归类。如下图为 user-service 配置:
Skywalking agent 也支持使用环境变量方式进行配置,QCBM 使用 ConfigMap 和 Secret 配置对应的环境变量:
如下图所示:
至此 TSW 接入工作已完成,启动容器服务后,在 TSW 控制台即可查看调用链、服务拓扑、SQL 分析等功能。
本页内容是否解决了您的问题?