单账号边缘托管应用

更新时间:

本章将为您介绍如何管理【单账号边缘托管应用】。

整体流程

一个“单账号边缘托管应用”的上线整体流程,包括:应用对接开发、创建应用、应用配置、部署测试、集群管理。操作路径:登录应用开发工作台,单击 应用管理 进入应用列表

对接介绍

1. 拆分应用节点

大部分应用是有不同独立可运行的组件或者模块构成的,这些独立可运行的组件或者模块,我们称为“节点”。比如一个“停车场管理系统”,典型包括:设备控制服务节点、业务管理节点、数据库节点。那么,其第一个是一个运行在jre的jar包、第二个是一个运行在tomcat上的war包、第三个是一个开源的MySQL数据库。

此外,拆分节点时,需要注意以下两点:1. 功能相对独立;2. 存储尽量放在数据库节点上。

2. 对业务代码的改造

容器化部署对应用本身的业务逻辑有两个地方的影响,在应用打包成镜像之前(无论后续是否还有其他对接开发导致的调整),请先做好这两方面的改动:

  • 节点之间如何访问:在非容器化前,由于每个节点单独运行在主机上,因此一般情况下,节点之间的访问是通过IP地址来访问的。但是容器化改造之后,由于IP并不确定,因此请使用节点的“服务名称”(服务名称是在可视化编排页面设定的)。

  • 应用初始化信息的获取:应用初始化信息包含两种:应用自身在节点配置中所配置的自定义环境变量、系统分配的一些初始化信息(如AppKey)。这两类信息都位于环境变量中,应用涉及到这两类初始化信息,则需要作此改造。

3. 打包Docker镜像

目前仅支持Linux镜像,打包流程参考线上文档:https://help.aliyun.com/document_detail/114832.html。建议在Linux操作系统上执行打包操作。

4.OAuth对接示例

4.1 集群内部发起API请求,获取应用App的登录地址。

代码示例:

/**
     * 获取环境变量和回调的url
     * @return
     */
//iot.hosting.api.schema----是请求协议格式http
private static final String API_GATEWAY_SCHEMA = System.getenv("iot.hosting.api.schema");
//iot.hosting.api.domain--是跳转路径的回调ip地址
private static final String API_GATEWAY_DOMAIN = System.getenv("iot.hosting.api.domain");
//iot.hosting.api.port----是请求端口
private static final String API_GATEWAY_SCHEMA = System.getenv("iot.hosting.api.port");
// iot.hosting.appKey----是请求的appkey
private static final String API_GATEWAY_SCHEMA = System.getenv(" iot.hosting.appKey");
//PATH_APP_GET是请求应用app的响应路径
private static final String PATH_APP_GET = "/api/console/app/get";
@Override
    public String getAppIndex() {
        RequestBuilder builder = RequestBuilder.create(METHOD_GET);
        Map<String, String> queryParams = Maps.newHashMap();
        queryParams.put(PARAM_APP_KEY, appKey);
        builder.setUri(HttpUtils.buildUrl(schema, apiGatewayDomain, apiGatewayPort, PATH_APP_GET, queryParams));
        IoTxResult<AppDTO> result = httpProxy.invoke(
                (HttpRequestBase) builder.build(),
                new TypeReference<IoTxResult<AppDTO>>() {
                }
        );
        logger.info("path={}; params={}; result={}", PATH_APP_GET, JSON.toJSONString(queryParams), JSON.toJSONString(result));
        Assert.assertSuccess(result);
        AppDTO app = result.getData();
        return app.getLoginUrl();
    }

请求拼接示例:

1. http://30.42.XX.XX:32187/api/console/app/get?appKey=2813****

返回结果示例:

{
    {
        "code": 200,
        "data": {
            "aliyunPk": "101248722030****",
            "appKey": "2813****",
            "appMeta": {
                "logoUrl": "http://192.168.11.130:32628/index/2813****",
                "name": "oauth2边缘托管",
                "subVersionId": "1.0",
                "uuid": "937aaa0284864396b54ecadb0d16****",
                "versionUuid": "9ff097f16d8347e6abfcaec0cd14****"
            },
            "appSecret": "NGNiOWQyYzI4MGVhOWNlYmNhOTdmMDIyNTg2Mzlh****",
            "clusterId": "bbeba04d880d49dc80bf83632619****",
            "configName": "oauth2边缘托管",
            "configUuid": "a7b996cd6ab04b7fb3a54abbc81b****",
            "configVersionUuid": "8e43f4d96bef4a289ad9f9af079d****",
            "loginUrl": "http://192.168.XX.XX:30313/index",//应用登录url
            "name": "Oauth2演示",
            "oauth": {
                "path": "/index",
                "port": 8080,
                "protocol": "",
                "serviceName": "edge",
                "serviceUuid": ""
            },
            "type": "GENERAL_APP",
            "uuid": "731082ac0c444053bad624ec915b****"
        },
        "message": "success"
    }
}

4.2 应用请求控制台地址获取,用于免登跳转地址的获取。

/**
     *
     * /api/console/host/account
     * @param path
     * @return
     */
    @Override
    public URI getURI(String path) {
        RequestBuilder builder = RequestBuilder.create(METHOD_GET);
        Map<String, String> queryParams = Maps.newHashMap();
        builder.setUri(HttpUtils.buildUrl(schema, apiGatewayDomain, apiGatewayPort, path, queryParams));
        logger.info("RequestBuilder请求url");
        IoTxResult<String> result = httpProxy.invoke(
                (HttpRequestBase) builder.build(),
                new TypeReference<IoTxResult<String>>() {
                }
        );
        logger.info("path={}; params={}; result={}", path, JSON.toJSONString(queryParams), JSON.toJSONString(result));
        Assert.assertSuccess(result);
        try {
            return new URI(result.getData());
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

请求拼接示例:

1. http://30.42.XX.XX:32187/api/console/host/account

4.3 应用请求发起。

根据当前登录态,获取authcode,oauth授权的页面,可直接跳过授权页面,请求免登的地址:http://30.42.XX.XX:32187:80/oauth2/auth? 请求入参:

参数名

类型

必填

描述

client_id

String

应用的appkey

redirect_uri

String

OAuth认证通过后的重定向应用的URI,包含完整的域名

response_type

String

返回类型。根据OAuth 2.0标准,目前支持设置此参数的取值为code

state

String

应用的appkey携带项

scope

String

空格分隔的OAuth范围列表。如不指定此参数取值,则默认为应用注册的全部OAuth范围,加上openid

请求示例:

http://30.42.XX.XX:32187/oauth2/auth? 
 redirect_uri=http://30.42.XX.XX:32187/index&
 client_id=2813****&state=2813****&
 response_type=code

代码示例:

/**
     *获取免登url和code
     * @param appKey
     * @param redirectUri
     * @return
     */
    public String getLoginRedirectUrl(String appKey, String redirectUri) {
        Map<String, String> queryParams = Maps.newHashMap();
        queryParams.put(PARAM_CLIENT_ID, appKey);
        queryParams.put(PARAM_REDIRECT_URI, redirectUri);
        queryParams.put(PARAM_RESPONSE_TYPE, "code");
        URI accountUri = getAccountUri();
        return HttpUtils.buildUrl(
                schema,
                accountUri.getHost(),
                accountUri.getPort(),
                PATH_LOGIN,
                queryParams).toASCIIString();
    }

4.4 跳转redirect_uri获取oauthcode。

IoT在验证当前用户合法后,将生成当前用户授权码oauthcode,在回跳redirect_uri地址时通过GET方式传递oauthcode,并同时返回state。返回示例:

http://30.42.XX.XX:32187/index?code=64a67ee15534defea7ad0d0535189b24&state=2813****

4.5 通过oauthcode换取accessToken。

获取OAuth授权code后可通过该接口获取accessToken身份信息,详情请参见链接OAuth对接API。代码示例:

/**
     * 根据code获取到token
     * @param appKey
     * @param oauthCode
     * @return
     */
    public String getAccessTokenByOauthCode(String appKey, String oauthCode) {
        RequestBuilder builder = RequestBuilder.create(METHOD_GET);
        Map<String, String> queryParams = Maps.newHashMap();
        queryParams.put(PARAM_CODE, oauthCode);
        queryParams.put(PARAM_GRANT_TYPE, "authorization_code");
        queryParams.put(PARAM_CLIENT_ID, appKey);
        URI accountUri = getAccountUri();
        builder.setUri(HttpUtils.buildUrl(accountUri.getScheme(), accountUri.getHost(), accountUri.getPort(), PATH_GET_ACCESS_TOKEN_BY_OAUTH_CODE, queryParams));
        IoTxResult<AccessTokenDTO> result = httpProxy.invoke(
                (HttpRequestBase) builder.build(),
                new TypeReference<IoTxResult<AccessTokenDTO>>() {
                }
        );
        PROXY_LOGGER.info("path={}; params={}; result={}", PATH_GET_ACCESS_TOKEN_BY_OAUTH_CODE, JSON.toJSONString(queryParams), JSON.toJSONString(result));
        Assert.assertSuccess(result);
        AccessTokenDTO accessTokenDTO = result.getData();
        Assert.assertNotNull(accessTokenDTO, "get accessToken failed");
        return accessTokenDTO.getAccessToken();
    }

4. 6.通过accessToken换取用户信息。

获取accessToken信息后,可通过accessToken来换取登录用户的用户信息,详情请参见OAuth对接API。代码示例:

/**
     * 根据token获取用户信息
     * @param accessToken
     * @return
     */
    public UserInfoDTO getUserInfoByAccessToken(String accessToken) {
        RequestBuilder builder = RequestBuilder.create(METHOD_GET);
        Map<String, String> queryParams = Maps.newHashMap();
        queryParams.put(PARAM_ACCESS_TOKEN, accessToken);
        URI accountUri = getAccountUri();
        builder.setUri(HttpUtils.buildUrl(accountUri.getScheme(), accountUri.getHost(), accountUri.getPort(), PATH_GET_USER_INFO_BY_ACCESS_TOKEN, queryParams));
        IoTxResult<UserInfoDTO> result = httpProxy.invoke(
                (HttpRequestBase) builder.build(),
                new TypeReference<IoTxResult<UserInfoDTO>>() {
                }
        );
        PROXY_LOGGER.info("path={}; params={}; result={}", PATH_GET_USER_INFO_BY_ACCESS_TOKEN, JSON.toJSONString(queryParams), JSON.toJSONString(result));
        Assert.assertSuccess(result);
        UserInfoDTO userInfoDTO = result.getData();
        Assert.assertNotNull(userInfoDTO, "get userInfo failed");
        return userInfoDTO;
    }

4.7.accessToken有效性判断。

检查当前URL登录的token是否有效, /user/oauth2/accesstoken/check?access_token=xxx。代码示例:

/**
     * token登录有效期检查
     * @param token
     * @return
     */
    public boolean checkLogin(String token) {
        RequestBuilder builder = RequestBuilder.create(METHOD_GET);
        Map<String, String> queryParams = Maps.newHashMap();
        queryParams.put(PARAM_ACCESS_TOKEN, token);
        URI accountUri = getAccountUri();
        builder.setUri(HttpUtils.buildUrl(accountUri.getScheme(), accountUri.getHost(), accountUri.getPort(), PATH_LOGINCHECK, queryParams));
        IoTxResult<Boolean> result = httpProxy.invoke(
                (HttpRequestBase) builder.build(),
                new TypeReference<IoTxResult<Boolean>>() {
                }
        );
        PROXY_LOGGER.info("path={}; params={}; result={}", PATH_LOGINCHECK, JSON.toJSONString(queryParams), JSON.toJSONString(result));
        Assert.assertSuccess(result);
        return result.getData();
    }

5.多副本应用部署

5.1注意事件

1.多副本部署目前仅支持RedisHA和MysqlHA的三方节点进行数据存储,在应用配置可选择副本数。

2. 系统应用如果接入了应用设备,每个应用实例需要有一个独立的设备证书(ProductKey、DeviceName、DeviceSecret)身份,那可能就不能同时运行2个副本的应用。此时cmp的连接会有互踢机制,导致部署失败。

集群管理

1 创建集群

应用接入> 集群管理 页面填写集群基本信息,如图所示:

边缘集群2
  • 边缘集群:支持脱离公网,实现集群与应用边对边通讯。

  • EdgeBox集群:支持脱离公网,实现集群与应用边对边通讯,同时提供EdgeBox集群的集群组件。

  • 存储地址:NFS服务器地址。

  • 存储路径:NFS Mount路径。

  • 服务地址段:边缘可用来分配的边缘服务网段。

建议使用与当前边缘局域网段不同的子网,否则有发生IP冲突的可能性导致服务异常。例如当前主机所在网络为192.168.1.0/24;可在路由器中再添加另一个子网192.168.2.0/24专门用来分配服务VIP。

2 创建节点

管理> 添加通用节点 页面填写节点基本信息,如图所示:

边缘节点2

节点名称:与集群名称不同,节点名称会被使用在K8s中,所以对命名有一定要求:只支持数字、小写英文、短划线,不能以短划线开头和结尾,长度限制4-30。

节点IP段:节点IP段为当前主机所在网段,需要用户提供以分配对应的Flannel Overlay服务网段。

3 加入节点

节点创建完成后点击 启动脚本> 复制脚本 到主机命令行执行,加入完成,控制台状态将变成运行中,如图所示:

加入边缘节点

4 组件管理(EdgeBox集群)

组件管理> 初始化集群底座 按钮对EdgeBox集群提供的集群组件进行初始化,如图所示:

组件管理1

边缘集群管理

点击集群管理>应用管理 可查看部署在此集群的所有部署的应用列表与提供的集群组件信息,同时可以对已部署的应用进行测试,如图所示:

边缘集群管理1点击边缘控制台>管理 复制IP+端口号,打开新的浏览器页面进行访问,使用“超级账户iotedgeadmin”登录“集群控制台”,用户名与密码一致,首次登录强制修改密码。

边缘管理21登录成功后,可点击部署应用的应用卡片,进入应用详情,如图所示:

边缘应用详情

点击账号管理>新建账号,添加“集群用户”(用户名、密码、手机号),其中,手机号必填且唯一,并提示用户,手机号是系统之间免登的凭据,如图所示:

边缘lat