后容器时代技术制高点:API管理平台3Scale的架构设计与部署[转]

前言

时至今日,相信大多数人已经相信容器可以承载生产的应用了,就像当年大多数人相信vSphere虚拟化承载生产的过程一样;历史总是在重演,IT技术的更新总是很快,而作为IT技术人员,顺应趋势是非常重要的。我们当然可以固守已有的知识领域,并以此为生计,但这并不妨碍我们学习新技术、学习开源。

容器带动了PaaS、带动了微服务、带动了Devops的落地和发展。而在容器技术已经成熟的今天,新的技术制高点是如何用容器实现API经济的落地。

API的本质是一种服务,无所不在的服务。移动其实是一个载体、一个表现形式;移动在本质上是让服务变得随时随地可以用。手机上的各种APP,其实都是一个服务的入口和访问口,如何来提供这种服务呢?就是后端跟API相关,安全的去使用API。

今天,我们看一下作为全生命周期API管理魔力象限中,领导者之一的3Scale的架构设计、部署方式、应用集成、报表展现。

接下来,我们看一下API的全生命周期管理的整个迭代,它分为五个阶段:

定义:确定为业务层提供价值的API服务

开发:设计,编码,测试,文档和标准化模板

发布:使用策略和控件安全运行

管理和支持:为协作提供社区论坛和文档

退休:生命结束 - 使用版本控制最佳实践取消发布,与市场进行交流和移除

一、3scale的部署方式

3Scale的两块组件:API网关和API Manager。他们的作用如下:

流量管理(API网关)

1.流量管理在APIcast网关中进行部署

(1)接口处理从外部客户端到后端API服务的API请求

(2)可以处理访问控制、速率限制、安全过滤、日志记录、路由和缓存

2.以异步方式与Red Hat 3scale API Management进行交互:

(1)即使AMP无法访问,API请求也会继续执行

3.使用开源组件实现(默认使用Nginx):

(1).OpenResty和Lua脚本语言

(2)基于Nginx

策略管理(API Manager): 3scale API Management Platform (AMP)

3scale API管理提供了5个关键功能,它们是:

  • 访问控制和安全
  • API合同和费率限制
  • 分析和报告
  • 开发人员门户和交互式API文档
  • API帐单和付款

API网关虽然大量在互联网中被广泛使用,在企业内部也有用武之地。例如:

用例:内部企业API

1.企业数据中心内部部署允许管理内部API:

(1)安全地管理访问并捕获跨部门的API资产分析

(2)通过以可编程格式提供内部数据和功能,鼓励部门间的协作

2.在大型和不断发展的组织中实现更高的敏捷性,创新性和灵活性

(1) 在开发人员门户中标准化文档

所以说,AP管理是客户PaaS建设的重要一环。

3scale的部署架构。

3scale的部署方式有3种选择:

只将API管理平台(管理部分)运行在Openshift容器云平台上。API网关部署在Openshift外部:

API管理平台全部运行在Openshift容器云平台上,但客户应用部署到Openshift外部:

容器化后的客户应用 和 API管理平台全部运行在Openshift容器云平台上:

其中,第三种方式是我们推荐的。

我们说,一个好汉三个帮。3Scale作为一款API管理平台,我们看一下他的“五大金刚“:

五大金刚之一:在API管理中,业务逻辑的处理,如action chain,并没有放到API网关上,而是由JBoss Fuse提供。具体有:

  • Exposes back-end business services:
    • Inner (fine-grained)
    • Outer (coarse-grained/composite)
  • SOAP/REST transformations
  • HTTP body introspection/modifications

五大金刚之二:如果客户的API管理需要和mobile app(移动app,如手机、平板上的应用),可以引入Red Hat Mobile Application Platform(社区项目是:FEEDHENRY),具体而言:

  • 开发大量的移动应用程序
  • 为这些移动应用开发大量支持API
  • 提供移动应用程序和支持API的集中管理

五大金刚之三:在API管理系统的单点登录管理方面,引入的是Red Hat Single Sign-On(社区项目是:keycloak),具体而言:

Delegation, AuthN, and AuthZ for:

  • APIs
  • API developer portal
  • API admin portal

Federation with other identity management systems

Implementing OAuth2, OpenId Connect, JSON Web Token

Role-based access control for back-end services

五大金刚之四:而API管理平台的整体运维管理,由红帽Ansible实现,具体而言:

  • Provisioning OpenShift Container Platform
  • Provisioning APIcast, AMP, Red Hat Single Sign-On, JBoss Fuse, and Jenkins
  • Version control
    • Ansible Playbooks and tasks can be version controlled

五大金刚之五:API管理平台的CI/CD,由Openshift中容器化的Jenkins实现,具体而言:

  • Building/compiling APIs
  • Executing API unit tests
  • Deploying APIs to development, QA, production
  • Creating resources (app plan, services, etc.) in 3scale AMP
  • Executing integration tests in staging environment
  • Publishing service to production

正是这五大金刚,是3Scale平台羽翼更加丰满,作战能力更强。

二、3scale环境的部署

本次实验环境,首先在笔记本上部署virtualbox虚拟机,用户客户端操作;然后将整套3Scale部署到Openshift中:。

在正式部署之前,我们先看一下3scale的整套部署架构都有什么。

整套3scale将会以pod方式部署到一个项目中。

3scale将会包含api gateway组件、系统组件、后端组件:

接下来我们看一下这些组件的主要作用:

  • backend-redis主要作用是存放 API requests and access tokens,他支持 API限速能力和分析历史API请求能力
  • system-redis是支持resque和sidekiq作业队列的数据存储缓存; system-mysql是API Manager用于帐户,用户,用户名,密码,电子邮件地址,API定义,设置等信息的数据库。 system-memcache:用于提提高API Manager Web应用程序的性能 backend-listener:实现服务管理API(SM API)的功能; SM API:由API网关,插件或直接API调用来授权和报告API请求,例如:该请求是否经过授权并在速率限制内? backend-listener尝试以尽可能低的延迟进行响应; 通过将任务排入作业队列,将开销更大任务offload到后端worker。 依赖于backend-redis服务。
  • system-sidekiq/system-resque:推迟执行一些任务到后台以加快Web响应速度。这两个服务依赖于:system-mysql,system-redis,backend-listener
  • system-sphinx:指定存储在system-mysql中的数据以方便管理员门户中的文本搜索功能,他依赖于:system-mysql
  • backend-worker:执行从后端监听器卸载的后台任务(入队作业)运行这些排队的作业(保留在后端 - redis中),主要与以前的流量报告有关;取决于:backend-redis服务
  • backend-cron:作为cron调度程序将执行失败的jos重新排队,依赖于:backend-redis

 

接下来,我们看实验环境:

位于virtualbox中的虚拟机(用于模拟应用客户端和OCP的client):

将部署3Scale的Openshift环境:

首先,在github上pull下来两个微服务,用于后续的实验:

第一个是vert.x,第二个是wildfly swarm

然后,在virtualbox虚拟机中安装上OC client,确保可以正常login到openshift:

为RESTful business service applications创建项目:

基于之前pull下来的源码,部署Wildfly Swarm应用:

为service创建一个边界路由:

oc create route edge wfswarmdatestamproute --service=wfswarm-date-service

确保部署好的应用可以被访问:

curl -v -k `echo "https://"$(oc get route/wfswarmdatestamproute -o template --template {{.spec.host}})"/time/now"`

接下来,部署vert.x应用:

同样,为service创建边界路由:

确保部署的应用可以被访问:

curl -v -k `echo "https://"$(oc get route/vertxgreetingroute -o template --template {{.spec.host}})"/hello"`

登录到Openshift,可以看到通过cli部署的两个应用:

在上面的实验中,源码的编译是由openshift完成的。

下面,我们看一下如何在本地编译源码并运行。

切换到源码地址目录:

使用maven进行编译。

编译的时候,会调用当前目录下的pom.xml内容以及$HOME/.m2/repository下的Maven repository:

编译成功:

编译成功以后,被被编译的两个微服务可以在本地启动:

接下来,我们在本地测试编译和部署成功的服务:

接下来,我们在Openshift上部署AMP,使用一个部署AMP的playbook。由于内容太长,我只将service部分贴出来:

apiVersion: v1

kind: Service

metadata:

annotations:

service.alpha.openshift.io/dependencies: '[{"name": "zync-database", "kind": "Service"}]'

labels:

app: zync

name: zync

spec:

ports:

- name: 8080-tcp

port: 8080

protocol: TCP

targetPort: 8080

selector:

app: zync

deploymentconfig: zync

- kind: Service

apiVersion: v1

metadata:

name: "zync-database"

spec:

ports:

- name: postgresql

protocol: TCP

port: 5432

targetPort: 5432

nodePort: 0

selector:

name: "zync-database"

- kind: DeploymentConfig

apiVersion: v1

metadata:

name: zync-database

spec:

paused: true

strategy:

type: Recreate

triggers:

- type: ImageChange

imageChangeParams:

automatic: true

containerNames:

- postgresql

from:

kind: ImageStreamTag

name: postgresql:9.5

- type: ConfigChange

replicas: 1

selector:

name: "zync-database"

template:

metadata:

labels:

name: "zync-database"

spec:

containers:

- name: postgresql

image: " "

ports:

- containerPort: 5432

protocol: TCP

readinessProbe:

timeoutSeconds: 1

initialDelaySeconds: 5

exec:

command:

- "/bin/sh"

- "-i"

- "-c"

- psql -h 127.0.0.1 -U zync -q -d zync_production -c 'SELECT 1'

livenessProbe:

timeoutSeconds: 1

initialDelaySeconds: 30

tcpSocket:

port: 5432

env:

- name: POSTGRESQL_USER

value: zync

- name: POSTGRESQL_PASSWORD

valueFrom:

secretKeyRef:

name: zync

key: ZYNC_DATABASE_PASSWORD

- name: POSTGRESQL_DATABASE

value: "zync_production"

resources:

limits:

memory: "2G"

volumeMounts:

- name: "zync-database-data"

mountPath: "/var/lib/pgsql/data"

imagePullPolicy: Always

resources:

limits:

cpu: '300m'

memory: 500Mi

requests:

cpu: '300m'

memory: 500Mi

volumes:

- name: "zync-database-data"

emptyDir:

medium: ''

restartPolicy: Always

parameters:

- name: ZYNC_DATABASE_PASSWORD

displayName: PostgreSQL Connection Password

description: Password for the PostgreSQL connection user.

generate: expression

from: "[a-zA-Z0-9]{16}"

required: true

- name: ZYNC_SECRET_KEY_BASE

generate: expression

from: "[a-zA-Z0-9]{16}"

required: true

- name: ZYNC_AUTHENTICATION_TOKEN

generate: expression

from: "[a-zA-Z0-9]{16}"

required: true

- name: ADMIN_PASSWORD

required: true

generate: expression

from: "[a-z0-9]{8}"

- name: ADMIN_USERNAME

value: admin

required: true

- name: APICAST_ACCESS_TOKEN

required: true

generate: expression

from: "[a-z0-9]{8}"

description: "Read Only Access Token that is APIcast going to use to download its configuration."

- name: ADMIN_ACCESS_TOKEN

required: false

generate: expression

from: "[a-z0-9]{16}"

description: "Admin Access Token with all scopes and write permissions for API access."

- name: WILDCARD_DOMAIN

description: Root domain for the wildcard routes. Eg. example.com will generate 3scale-admin.example.com.

required: true

- name: WILDCARD_POLICY

description: Use "Subdomain" to create a wildcard route for apicast wildcard router

required: true

value: "None"

- name: TENANT_NAME

description: "Tenant name under the root that Admin UI will be available with -admin suffix."

required: true

value: "3scale"

- name: MYSQL_USER

displayName: MySQL User

description: Username for MySQL user that will be used for accessing the database.

value: "mysql"

required: true

- name: MYSQL_PASSWORD

displayName: MySQL Password

description: Password for the MySQL user.

generate: expression

from: "[a-z0-9]{8}"

required: true

- name: MYSQL_DATABASE

displayName: MySQL Database Name

description: Name of the MySQL database accessed.

value: "system"

required: true

- name: MYSQL_ROOT_PASSWORD

displayName: MySQL Root password.

description: Password for Root user.

generate: expression

from: "[a-z0-9]{8}"

required: true

- name: SYSTEM_BACKEND_USERNAME

description: Internal 3scale API username for internal 3scale api auth.

value: "3scale_api_user"

required: true

- name: SYSTEM_BACKEND_PASSWORD

description: Internal 3scale API password for internal 3scale api auth.

generate: expression

from: "[a-z0-9]{8}"

required: true

- name: REDIS_IMAGE

description: Redis image to use

required: true

value: "rhscl/redis-32-rhel7:3.2"

- name: MYSQL_IMAGE

description: Mysql image to use

required: true

value: "rhscl/mysql-56-rhel7:5.6"

- name: SYSTEM_BACKEND_SHARED_SECRET

description: Shared secret to import events from backend to system.

generate: expression

from: "[a-z0-9]{8}"

required: true

- name: SYSTEM_APP_SECRET_KEY_BASE

description: System application secret key base

generate: expression

from: "[a-f0-9]{128}"

required: true

- name: APICAST_MANAGEMENT_API

description: "Scope of the APIcast Management API. Can be disabled, status or debug. At least status required for health checks."

required: false

value: "status"

- name: APICAST_OPENSSL_VERIFY

description: "Turn on/off the OpenSSL peer verification when downloading the configuration. Can be set to true/false."

required: false

value: "false"

- name: APICAST_RESPONSE_CODES

description: "Enable logging response codes in APIcast."

value: "true"

required: false

# JA Bride

- name: BACKEND_REDIS_SERVICE_NAME

description: "Name of Backend Redis Service"

value: "backend-redis"

required: true

创建一个项目,用于部署AMP:

在项目中部署AMP:

模板部署的dc如下:

我们查看部署后的pods,这些pods都是AMP的组件:

确认部署好的mysql可以被登录和访问:

我们查看一下AMP所属的Openshift的docker-registry,可以看到部署AMP过程中push的镜像:

通过浏览器登录部署好的3scale:

三、将两个应用与APIcast gateway集成

接下来,我们做一个实验:

用APIcast网关管理刚部署的两个应用的RESTful端点。

我们可以通过配置基于主机的路由来执行此操作,从远程客户端到单个APIcast网关的入站流量使用不同的URL。 根据这些网址,您的APIcast网关将路由到相应的应用。

我们知道,如果我们在OCP中部署应用,那么给应用创建路由的时候,是将FQDN与service关联起来。

而在有3Scale的平台中,我们创建路由(edge route),是将路由指向APIcast gateway的service。然后,再将具体应用的service,与APIcast gateway进行集成,并暴露API集成的时候,用的是应用service的域名。而这个集成的操作,就叫API集成。API集成以后,后续客户端访问应用,访问的是先经过OCP的haproxy,再经过APIcast gateway,然后到达应用:需要注意的是,在实验环境中,有两个apicast的service:apicast-stage和apicast-prod,模拟负责开发测试和生产两个环境的API网关;

有两个应用,这两个应用也存在于stage和prod两个环境,所以下面的实验是创建四条路由,开发测试中的两个应用指向:apicast-stage。生产中的两个应用指向apicast-prod:

首先,查看APIcast路由:

查看APIcast的SVC:

我们创建一个边界路由, 指向APIcast prod网关,以便我们能够访问Swarm service:

oc create route edge swarm-apicast-prod --service=apicast-production --hostname=swarm-apicast-prod-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN

我们创建一个边界路由, 指向APIcast prod网关,以便我们能够访问Vert.x:

oc create route edge vertx-apicast-prod --service=apicast-production --hostname=vertx-prod-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN

再为swarm-apicast-stage and vertx-apicast-stage创建两条边界路由到staging APIcast gateway:

oc create route edge swarm-apicast-stage --service=apicast-staging --hostname=swarm-stage-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN

oc create route edge vertx-apicast-stage --service=apicast-staging --hostname=vertx-stage-apicast-$OCP_PROJECT_PREFIX.$OCP_WILDCARD_DOMAIN

四条路由创建完毕,AMP也部署完成。

接下来,我们在3Scale中创建的两个用户为:swarm_dev和vertx_dev

接下来,创建app plan,并为账户创建应用(指定到创建的app plan,然后将app plan publish):

Wildfly Swarm Service

将API User Key记录下来77a3f6e084bb672c3b60c94d68c1a160

注入到环境变量:

echo "export ONPREM_SWARM_USER_KEY=79c8649186e35e1015dc571ec72bfdb7" >> ~/.bashrc

jboss@rhtapimgmt / $ source ~/.bashrc

接下来,做Stage环境的服务集成。

首先,我们需要获取应用的几个参数:

jboss@rhtapimgmt ~ $ echo -en "\n\nhttp://wfswarm-date-service.$OCP_PROJECT_PREFIX-bservices.svc.cluster.local:8080\n\n"

http://wfswarm-date-service.david-bservices.svc.cluster.local:8080

Staging Public Base URL

jboss@rhtapimgmt ~ $ echo -en "\n`oc get route swarm-apicast-stage -n $OCP_PROJECT_PREFIX-3scale-amp --template "https://{{.spec.host}}"`:443\n\n"

https://swarm-stage-apicast-david.apps.na1.openshift.opentlc.com:443

Production Public Base URL

jboss@rhtapimgmt ~ $ echo -en "\n`oc get route swarm-apicast-prod -n $OCP_PROJECT_PREFIX-3scale-amp --template "https://{{.spec.host}}"`:443\n\n"

https://swarm-apicast-prod-david.apps.na1.openshift.opentlc.com:443

在API集成中,输入查询到的信息:

Client调用的时候,get /time/now

然后更新信息,应用和API集成成功:

接下来,将集成成功的应用发布到生产:

发布成功以后,应用的两个版本存在于两个环境:

为了让 APIcast gateway尽快刷新,修改其dc,将APICAST_CONFIGURATION_LOADER环境变量从lazy 修改为boot,否则默认每五分钟刷新一次:

oc edit dc apicast-production

接下来,我们测试从客户端对应用发起请求:

curl -v -k `echo -en "\nhttps://"$(oc get route/swarm-apicast-prod -o template --template {{.spec.host}})"/time/now?user_key=$ONPREM_SWARM_USER_KEY\n"`

我们看到请求会指向到apicast gateway:

最终成功调用API,返回信息(当前时间):

接下来,我们再将Vert.x应用集成到apicast gateway。

创建用户:

定义Vert.x service:

gateway使用nginx:

认证方式使用API key:

创建application plan:

创建应用:

注意生成的api key:f29950b33d4b4c7d1d07a7194b9b7d69

发布app plan:

接下来,对应用做API集成:

Private Base URL

jboss@rhtapimgmt ~ $ echo -en "\n\nhttp://vertx-greeting-service.$OCP_PROJECT_PREFIX-bservices.svc.cluster.local:8080\n\n"

http://vertx-greeting-service.david-bservices.svc.cluster.local:8080

Staging Public Base URL

jboss@rhtapimgmt ~ $ echo -en "\n`oc get route vertx-apicast-stage -n $OCP_PROJECT_PREFIX-3scale-amp --template "https://{{.spec.host}}"`:443\n\n"

https://vertx-stage-apicast-david.apps.na1.openshift.opentlc.com:443

Production Public Base URL

jboss@rhtapimgmt ~ $ echo -en "\n`oc get route vertx-apicast-prod -n $OCP_PROJECT_PREFIX-3scale-amp --template "https://{{.spec.host}}"`:443\n\n"

https://vertx-prod-apicast-david.apps.na1.openshift.opentlc.com:443

成功集成:

将应用发布到生产:

发布后的两个环境中的应用:

至此,两个应用与APIcast Gateway集成完毕!

四、APIcast gateway的变更管理

很多时候,我们需要对APIcast gateway做配置变更。在我们的环境中,APIcast gateway是一个容器化的NGINX。

在基于OCP的3Scale中,修改APIcast Gateway大致有两种方法:

1.扩展APIcast Docker镜像:需要修改Docker文件并进行docker build,这对大多数用例来说都是首选方法。

2.覆盖APIcast容器中的特定文件:如果进行少量更改或需要快速尝试对现有APIcast网关容器进行调整。 它涉及创建一个OpenShift容器平台配置映射资源,其中包含您的自定义APIcast配置文件。 引用您的configmap资源的卷将挂载到该pod并覆盖原始APIcast配置文件。例如将NGINX插件添加到您的APIcast网关;或者在PIcast网关中开发并应用自定义安全策略。

在本实验中,我们做最简单的配置更改,这通过修改APIcast网关的nginx.conf文件实现。

在实验环境中,我们登录apicast容器:

oc rsh apicast-production-3-0p34h

查看apicast root directory:

ls -R /opt/app-root/src | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'

查找配置文件:

$ find /opt/app-root/src -name '*.lua' -ls

$ find /opt/app-root/src -name '*.conf' -ls

查看APIcast gateway中的nginx配置文件:

sh-4.2$ cat /opt/app-root/src/conf/nginx.conf

env REDIS_HOST;

env REDIS_PORT;

env REDIS_URL;

env RESOLVER;

env BACKEND_ENDPOINT_OVERRIDE;

env OPENSSL_VERIFY;

include ../main.d/*.conf;

error_log /dev/null emerg;

events {

worker_connections 16192;

multi_accept on;

}

http {

sendfile on;

tcp_nopush on;

tcp_nodelay on;

server_names_hash_bucket_size 128;

log_format time '[$time_local] $host:$server_port $remote_addr:$remote_port "$request" $status $body_bytes_sent ($request_time) $post_action_impact';

access_log off;

lua_package_path ";;${prefix}?.lua;${prefix}src/?.lua";

# Enabling the Lua code cache is strongly encouraged for production use

# Disabling it should only be done for testing and development purposes

lua_code_cache on;

include ../http.d/*.conf;

server {

listen 8090;

server_name _;

include ../conf.d/management.conf;

}

server {

listen 8081;

server_name backend;

include ../conf.d/backend.conf;

}

server {

listen 8081 default_server;

server_name echo _;

include ../conf.d/echo.conf;

}

server {

access_log /dev/stdout time;

listen 8080;

server_name _;

underscores_in_headers on;

include ../http.d/ssl.conf;

include ../apicast.d/*.conf;

include ../conf.d/apicast.conf;

}

include ../sites.d/*.conf;

}

在本实验中,我们给APIcast gateway增加一个图片。

图面位于该目录下:

在OCP中创建一个包含图片的security:

oc create secret generic graphics-secret --from-file=$HOME/lab/3scale_onpremise_implementation_labs/resources/misc

接下来,将security 挂载到APIcast pod中:

oc set volume dc/apicast-production --add --overwrite \

> --name=graphics-volume \

> -m /u02/graphics \

> --type=secret \

> --secret-name=graphics-secret \

> --overwrite

warning: volume "graphics-volume" did not previously exist and was not overriden. A new volume with this name has been created instead.deploymentconfig "apicast-production" updated

将nginx.conf配置文件从容器中同步出来,然后进行修改:

jboss@rhtapimgmt ~ $ mkdir -p /tmp/apicast_configs/

jboss@rhtapimgmt ~ $ oc rsync apicast-production-4-zjhlj:/opt/app-root/src/conf/nginx.conf /tmp/apicast_configs

$ oc create configmap nginx-map --from-file=nginx.conf=/tmp/apicast_configs/nginx.conf

configmap "nginx-map" created

修改apicast gateway的dc:

确认图片已经被加入到pod中.

首先查看链接:

jboss@rhtapimgmt ~ $ echo -en "\n\n`oc get route swarm-apicast-prod --template "https://{{.spec.host}}"`:443/rht_3scale.png\n\n"

https://swarm-apicast-prod-david.apps.na1.openshift.opentlc.com:443/rht_3scale.png

pod重新部署以后,依然有图片文件:

然后通过浏览器访问:

https://swarm-apicast-prod-david.apps.na1.openshift.opentlc.com:443/rht_3scale.png

能够显示图片:

五、API分析报告与计费功能

最后,我们看一下API的分析报告:

在另外的一个实验中:

查看API调用,可以看到GET /vocabularies被调用的次数是8:

接下来,通过Postman工具,模拟客户端对api的访问,发一个get请求:

再查查看,GET /vocabularies 被调用次数增加到9:

再次发起3次请求后:

API调用次数增加到:

我们看一下API分析数据的具体内容:

可以下载详细的分析数据表格:

选择以天方式显示:

以小时为单位显示:

从应用视角查看调用:

给用户配置查看报告的权限:

除此之外,我们还可以在3Scale中启动API计费功能:

(具体设置方法:https://access.redhat.com/documentation/en-us/red_hat_3scale/2.saas/html-single/billing/index)

魏新宇

  • "大魏分享"运营者、红帽资深解决方案架构师
  • 专注开源云计算、容器及自动化运维在金融行业的推广
  • 拥有MBA、ITIL V3、Cobit5、C-STAR、TOGAF9.1(鉴定级)等管理认证。
  • 拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、AIX、HPUX等技术认证。

原文发布于微信公众号 - 大魏分享(david-share)

原文发表时间:2018-05-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于 2018-06-25

猜你喜欢

转载自blog.csdn.net/hyb1234hi/article/details/84589274