a49759a4691e96307b633b8cb63a37aa.png

1.引言

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

如官网所述:Apollo 是携程打造的开源配置中心,GitHub的星星也快点满22K,因此足见它的成熟度和社区活跃度。因此最近在做配置中心选型的时候,经过一番预演,最终敲定Apollo。

Apollo作为微服务体系中必不可少的基础服务,其架构设计和基本使用我们不得不有所了解。

因此本文接下来将主要来介绍如何基于Helm快速部署Apollo集群至K8S,并与.NET Core应用进行集成,同时介绍下如何平滑迁移配置到Apollo。

本文具有详细的部署步骤,建议动手实操。
部署Chart包和Demo已上传至GitHub:K8S.NET.Apollo,可收藏备用。

2. Apollo 架构一览

在部署之前,需要了解Apollo的基础架构,以便在后续部署工作的展开。

9bc247d32b1b305a831a7407546f1f32.png

关于其的解读,我这里就不再详细展开,但以下几点还是要有所了解,感兴趣的可以直接看官网详细介绍:Apollo配置中心设计。

  1. Config Service提供配置的读取、推送等功能,服务对象是Apollo客户端

  2. Admin Service提供配置的修改、发布等功能,服务对象是Apollo Portal(管理界面)

  3. Config Service和Admin Service都是多实例、无状态部署,需要通过注册中心进行服务注册和发现

  4. 注册中心默认采用的是Eureka,在K8S中由Service充当

  5. Apollo客户端通过注册中心获取Config Service服务列表进行配置读取

  6. Apollo Portal通过注册中心获取Admin Service服务列表进行配置管理

基于上面对Apollo的介绍,其物理架构总结起来就是:

  1. 每一套环境都必须拥有自己独立的Config Service 和 Admin Service 以及独立ConfigDB。

  2. 多套环境可以公用一套Apollo Portal 进行管理,Portal拥有独立PortalDB。

3. 基于Helm部署到K8S

因为Apollo 1.7.0版本增加了基于Kubernetes原生服务发现的部署模式,来替换内置的Eureka,所以在整体部署上有很大简化,同时官方也提供了Helm Charts,让Apollo更加易于开箱即用。下面就以部署一套测试环境为例讲解一下Apollo的部署要点。(部署至本机Docker Desktop Local K8S环境)。

环境要求:Kubernetes 1.10+,Helm 3

ecfe1b89759b9f1b032358782dda71c7.png

3.1 搭建 Apollo Config&Portal DB

从上图的物理架构上来看,首先要部署好Config DB和PortalDB。关于DB的搭建,建议直接使用bitnami/mysqlchart搭建。搭建步骤如下:

> helm repo add bitnami https://charts.bitnami.com/bitnami

执行helm包的安装,需要自定义配置文件,也就是values.yaml。我们可以先行下载 mysql chart包。

之所以选择将chart包下载到本地,是为了确保后续维护能够基于一致的chart包版本。避免因为执行helm repo update导致chart包版本自动升级,而不自知。

> helm pull bitnami/mysql --untar  //下载并解包
mysql
├── Chart.yaml
├── ci
│ └── values-production.yaml
├── files
│ └── docker-entrypoint-initdb.d
│ └── README.md
├── README.md
├── templates
│ ├── initialization-configmap.yaml
│ ├── master-configmap.yaml
│ ├── master-statefulset.yaml
│ ├── master-svc.yaml
│ ├── NOTES.txt
│ ├── secrets.yaml
│ ├── serviceaccount.yaml
│ ├── servicemonitor.yaml
│ ├── slave-configmap.yaml
│ ├── slave-statefulset.yaml
│ ├── slave-svc.yaml
│ └── _helpers.tpl
├── values-production.yaml
└── values.yaml

根据官网分布式部署指南中所示,其提供了DB的初始化脚本用来分别创建ApolloConfigDBApolloPortalDB。因此可以直接将以上SQL脚本下载到mysql chart的files/docker-entrypoint-initdb.d目录下,这样在部署mysql实例时就会自动执行脚本创建数据库。

> cd mysql/files/docker-entrypoint-initdb.d
> curl https://raw.githubusercontent.com/ctripcorp/apollo/master/scripts/sql/apolloportaldb.sql > apolloportaldb.sql //下载apolloportaldb.sql
> curl https://raw.githubusercontent.com/ctripcorp/apollo/master/scripts/sql/apolloconfigdb.sql > apolloconfigdb.sql 下载apolloconfigdb.sql
> ls

Directory: C:\Users\Shengjie\k8s\helm\charts\apollo\mysql\files\docker-entrypoint-initdb.d

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 8/12/2020 11:01 PM 21291 apolloconfigdb.sql
-a--- 8/12/2020 10:56 PM 16278 apolloportaldb.sql
-a--- 8/9/2020 6:26 PM 242 README.md

然后复制values.yaml并命名为dev-mysql-values.yaml。然后修改核心配置:

  1. global.storageClass=hostpath
    可通过kubectl get sc查看集群支持的storageClass,我这边选择默认的hostpath。其创建的pv的默认回收策略为delete,也就意味着卸载mysql,数据直接删除,这点需要注意!!!如果需要保留测试数据,请更新storageClass。

  2. root.password=root
    修改默认root用户的密码

修改完毕后,执行以下脚本进行安装:

#创建单独db命名空间

按照上面提示,验证数据库成功创建:

'Never' --image  docker.io/bitnami/

至此,确认Apollo ConfigDB和PortalDB搭建成功。

3.2 搭建 Apollo Config Service

搭建Apollo Service 需要添加携程官方chart仓库:

//ctripcorp.github.io/apollo/charts

从上可知,主要包含两个chart,分别用来部署service和portal。下来研究下apollo/apollo-service 这个chart。老规矩,先把chart包下载下来:

> helm pull apollo/apollo-service --untar

从上面的树形图来看,主要就是用来部署config service 和 admin service。紧接着,复制一个values.yaml,命名为dev-apollo-svc-values.yaml。主要修改以下配置:

  1. configdb.host=mysql-apollo.db
    指定configdb的主机,因为是在集群内部,直接使用服务名即可

  2. configdb.password=root
    指定configdb的秘密

修改后的配置如下:

configdb:

其他配置可以暂定不动,紧接着执行以下命令进行安装:

> kubectl create ns apollo # 创建apollo 命名空间
> helm install --dry-run --debug apollo-dev-svc . -f dev-apollo-svc-values.yaml -n apollo # 测试安装,验证模板生成的资源文件是否有误
> helm install apollo-dev-svc . -f dev-apollo-svc-values.yaml -n apollo

这里要记住上面的meta service url:http://apollo-dev-svc-apollo-configservice.apollo:8080

那如何确认正确部署了呢:

> kubectl get all -n apollo # 查看apollo命名空间下部署的资源
NAME READY STATUS RESTARTS AGE
pod/apollo-dev-svc-apollo-adminservice-7d4468ff46-gw6h4 1/1 Running 0 3m26s
pod/apollo-dev-svc-apollo-configservice-58d6c44cd4-n4qk9 1/1 Running 0 3m26s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/apollo-dev-svc-apollo-adminservice ClusterIP 10.99.251.14 8090/TCP 3m26s
service/apollo-dev-svc-apollo-configservice ClusterIP 10.108.121.201 8080/TCP 3m26sNAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/apollo-dev-svc-apollo-adminservice 1/1 1 1 3m26s
deployment.apps/apollo-dev-svc-apollo-configservice 1/1 1 1 3m26sNAME DESIRED CURRENT READY AGE
replicaset.apps/apollo-dev-svc-apollo-adminservice-7d4468ff46 1 1 1 3m26s
replicaset.apps/apollo-dev-svc-apollo-configservice-58d6c44cd4 1 1 1 3m26s

从上可知暴露了两个服务configservice和adminservice,来尝试将configservice进行端口转发到本地端口来看一下。

kubectl 

使用浏览器访问 localhost:8080,可以看到输出[{"appName":"apollo-configservice","instanceId":"apollo-configservice:http://apollo.shisheng.wang/config-svc","homepageUrl":"http://apollo.shisheng.wang/config-svc"},{"appName":"apollo-adminservice","instanceId":"apollo-adminservice:http://apollo.shisheng.wang/admin-svc","homepageUrl":"http://apollo.shisheng.wang/admin-svc"}]

至此说明,Apollo Service 搭建成功。

3.3 搭建 Apollo Portal Service

同样,先来下载portal chart包,并研究下目录结构:

> helm pull apollo/apollo-portal --untar

从上可知,portal 相对来说,主要是构建portal服务,并可以通过ingress暴露服务。复制一个values.yaml,命名为dev-apollo-portal-values.yaml。主要修改以下配置:

  1. ingress.enabled=true
    启用ingress,并通过注解设置ingress controller,因为portal是个有状态服务,所以要关注Sessiion状态维持。以下主要是针对nginx-ingress-controller的配置,如果使用的其他的ingress-controller请注意更改。(nginx-ingress-controller的安装,这里就不具体展开了,可以简单执行helm install nginx bitnaim/nginx-ingress-controller 安装就好了。)

ingress:
  1. 指定配置源 ,主要是envs和metaServers两个配置项:config.envs=devconfig.metaServers.dev=http://apollo-dev-svc-apollo-configservice.apollo:8080(上面部署apollo service输出的apollo service url)如果同时启用开发、测试和生产环境。可以配置为:envs: "dev,uat,prd",metaServers 分别指定对应环境的配置即可。
    以下是只启用开发环境的配置:

config:
  1. portaldb.host=mysql-apollo.db & portaldb.password=root
    指定portaldb的主机和密码

portaldb:

其他配置可以暂定不动,紧接着执行以下命令进行安装:

> Helm install --dry-run --debug apollo-dev-portal . -f dev-apollo-portal-values.yaml -n apollo # 测试安装,验证模板生成的资源文件是否有误
> Helm install apollo-dev-portal . -f dev-apollo-portal-values.yaml -n apollo
PS C:\Users\Shengjie\k8s\helm\charts\apollo\apollo-portal> Helm install apollo-dev-portal . -f dev-apollo-portal-values.yaml -n apollo

到这一步,如果需要本地可以访问,还需要修改本地hosts,添加127.0.0.1 apollo.demo.com。然后打开你的Browser输入http://apollo.demo.com/,就可以访问了。默认用户密码是:[apollo/admin]。b8a19db062950857c1e20cf943300074.png

3.4. 暴露 config service

以上部署的是开发环境,但要想开发环境要访问到config service,我们还需要些小动作。这个时候就需要修改apollo service的chart模板,在template目录增加ingress-configservice.yaml文件,内容如下:

# ingress-configservice.yaml
{{- if .Values.configService.ingress.enabled -}}{{- $fullName := include "apollo.configService.fullName" . -}}{{- $svcPort := .Values.configService.service.port -}}{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "apollo.service.labels" . | nindent 4 }}
{{- with .Values.configService.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.configService.ingress.tls }}
tls:
{{- range .Values.configService.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}{{- end }}
rules:
{{- range .Values.configService.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ . }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}{{- end }}

然后修改values.yamlconfigService节点下增加ingress配置选项:

configService:

然后再修改上面我们创建的dev-apollo-svc-values.yaml下的configService节点,添加对应ingressconfig.configServiceUrlOverride配置:

configService:

修改完毕,执行以下命令升级apollo service:

> helm upgrade apollo-service-dev . -f dev-apollo-svc-values.yaml -n apollo
NAME: apollo-service-dev
LAST DEPLOYED: Tue Aug 18 14:20:41 2020
NAMESPACE: apollo
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Get meta service url for current release by running these commands:
echo http://apollo-service-dev-apollo-configservice.apollo:8080

For local test use:
export POD_NAME=$(kubectl get pods --namespace apollo -l "app=apollo-service-dev-apollo-configservice" -o jsonpath="{.items[0].metadata.name}")
echo http://127.0.0.1:8080
kubectl --namespace apollo port-forward $POD_NAME 8080:8080
> curl http://apollo.demo.com/config-svc
[{"appName":"apollo-configservice","instanceId":"apollo-configservice:http://apollo.demo.com/config-svc","homepageUrl":"http://apollo.demo.com/config-svc"},{"appName":"apollo-adminservice","instanceId":"apollo-adminservice:http://apollo-service-dev-apollo-adminservice.apollo:8090","homepageUrl":"http://apollo-service-dev-apollo-adminservice.apollo:8090"}]

从上面的输出可以看到,现在已经可以通过http://apollo.demo.com/config-svc读取metaServer配置了,后面本地开发环境就可以通过这个链接来读取Apollo的配置。

4. .NET Core 集成Apollo

这一部分我就快速带过了,执行以下命令创建项目,并引入apolloswagger相关包:

dotnet 

修改appsettings.json增加apollo配置:

{
"AllowedHosts": "*",
"apollo": {
"AppId": "test",
"MetaServer": "http://apollo.demo.com/config-svc",
"Env": "Dev"
}
}

修改Program.cs,添加Apollo配置源如下:

static IHostBuilder CreateHostBuilder(string[] args) =>

修改Startup.cs,添加Swagger集成,方便测试:

public 

添加ApolloController,增加以下测试代码:

namespace K8S.NET.Apollo.Controllers

登录Apollo Portal,添加test项目,并增加以下配置,并发布。7cd331cbde78ab5db1669aea5944b580.png

本地调试,就能够获取云端配置,另外Apollo同时会同步一份配置到本地目录:c:/opt/data/test/config-cache。这样就可以保证即使无法建立云端连接,也可以正常加载本地配置。
执行以下命令,进行配置读取和验证:

https:

5.配置迁移指北

相信采用Apollo的绝大多数都不是一开始就用的,都是再配置逐渐复杂之后,才进行迁移的。我也不例外,之前是用K8S的ConfigMap来做配置管理。下面就来讲下迁移指南,我将其分为两种模式:

  1. 偷懒模式
    如果想改动最小,就直接将项目配置继续以Json格式维护到Apollo的私有命名空间下。0be454e0b7b96e1feb1e5d759469829a.png3bbc51e89309e62a9743e8f6b1e6c468.png

static IHostBuilder CreateHostBuilder(string[] args) =>
  1. 强迫症模式
    也有人考虑,既然上Apollo,就要用到它的特性,因此对现有配置就要分门别类。哪些是公用的,哪些是私有的。对于公用的就要定义到公共的命名空间下。公共命名空间的配置格式只有Properties格式,因此需要将Json转为Properties。比如针对Logging配置可以借助网站 json2properties converter进行在线转换。如下所示:

bbfdb17bd940769a1f929f0ed6c4c9c6.png

如果真这样做,你就错了,你会发现最终的日志配置不生效。这是因为properties格式是以.进行分割,而.NET Core是用:来识别节点配置, 因此properties配置按:分割就好了,如下所示,以下两种配置等效:

8f670946b67d846aab4cf035d86f4b50.png

6. 最后

以上,相信若能够动手实操,你将收获匪浅。

本文Demo和Chart包的完整配置已上传至Github:K8S.NET.Apollo,请按需取用。

7f69131869577915f31df4930324eb5c.png

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐