應用:實例分發型-邊緣托管應用
本章將為您介紹如何管理【實例分發型-邊緣托管應用】。
1.整體流程
一個“實例分發的邊緣托管應用”的上線整體流程,包括:應用對接開發、創建應用、應用配置、部署測試、集群管理。
2.對接介紹
1. 拆分應用節點
大部分應用是有不同獨立可運行的組件或者模塊構成的,這些獨立可運行的組件或者模塊,我們稱為“節點”。比如一個“停車場管理系統”,典型包括:設備控制服務節點、業務管理節點、數據庫節點。那么,其第一個是一個運行在jre的jar包、第二個是一個運行在tomcat上的war包、第三個是一個開源的MySQL數據庫。此外,拆分節點時,需要注意以下兩點:1. 功能相對獨立;2. 存儲盡量放在數據庫節點上。
2. 對業務代碼的改造
容器化部署對應用本身的業務邏輯有兩個地方的影響,在應用打包成鏡像之前(無論后續是否還有其他對接開發導致的調整),請先做好這兩方面的改動:
節點之間如何訪問:在非容器化前,由于每個節點單獨運行在主機上,因此一般情況下,節點之間的訪問是通過IP地址來訪問的。但是容器化改造之后,由于IP并不確定,因此請使用節點的“服務名稱”(服務名稱是在可視化編排頁面設定的)。
應用初始化信息的獲取:應用初始化信息包含兩種:應用自身在節點配置中所配置的自定義環境變量、系統分配的一些初始化信息(如AppKey)。這兩類信息都位于環境變量中,應用涉及到這兩類初始化信息,則需要作此改造。
3. 打包Docker鏡像
目前僅支持Linux鏡像,打包流程參考線上文檔:http://bestwisewords.com/document_detail/114832.html。建議在Linux操作系統上執行打包操作。
4.OAuth對接示例
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();
}
請求拼接示例:
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/28135***",
"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"
}
}
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);
}
}
請求拼接示例:
http://30.42.XX.XX:32187/api/console/host/account
3.應用請求發起。根據當前登錄態,獲取authcode,oauth授權的頁面,可直接跳過授權頁面,請求免登的地址:http://30.42.XX.XX:32187/oauth2/auth? 請求入參:
參數名 | 類型 | 必填 | 描述 |
client_id | String | 是 | 應用的appkey |
redirect_uri | String | 是 | OAuth認證通過后的重定向應用的URI,包含完整的域名 |
response_type | String | 是 | 返回類型。根據OAuth 2.0標準,目前支持設置此參數的取值為 |
state | String | 否 | 應用的appkey攜帶項 |
scope | String | 否 | 空格分隔的OAuth范圍列表。如不指定此參數取值,則默認為應用注冊的全部OAuth范圍,加上 |
請求示例:
http://30.42.XX.XX:32187/oauth2/auth?
redirect_uri=http://30.42.XX.XX:32187/index&
client_id=28135051&state=28135051&
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.跳轉redirect_uri獲取oauthcode,IoT在驗證當前用戶合法后,將生成當前用戶授權碼oauthcode,在回跳redirect_uri地址時通過GET方式傳遞oauthcode,并同時返回state。返回示例:
http://30.42.XX.XX:32187/index?code=64a67ee15534defea7ad0d0535189b24&state=28135051
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();
}
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;
}
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();
}
具體oauth-demo參考附件:
5.多副本應用部署
5.1注意事件
1.多副本部署目前僅支持RedisHA和MysqlHA的三方節點進行數據存儲,在應用配置可選擇副本數。如圖所示:2.系統應用如果接入了應用設備,每個應用實例需要有一個獨立的設備證書(ProductKey、DeviceName、DeviceSecret)身份,那可能就不能同時運行2個副本的應用。此時cmp的連接會有互踢機制,導致部署失敗。3.應用自身存在邏輯問題,舉例說明,如下圖所示:
3.應用管理
3.1 創建應用
在
頁面填寫應用基本信息,如圖所示:選擇應用類型為實例分發,如圖所示:
根據應用實際情況選擇部署方式與系統類型,如圖所示:
應用類型:
賬號分發:用戶付款后,只需要交付賬號給其使用。
實例分發:用戶付款后,單獨為客戶部署一套應用。
一次性交付:定制項目或交付型應用。
部署方式:
云端托管部署:平臺根據應用提供者的配置,自動分配資源,并部署應用。
邊緣托管部署:平臺根據應用提供者的配置,自動分配資源,并部署應用到邊緣。
3.2 應用配置
創建應用完成后點擊初始化應用,填寫版本說明,創建應用完成后可以看到版本管理、實例管理、授權實例功能,如圖所示:
3.2.1 可視化編排
點擊
,可視化編排是對一個應用的定義。包括一份應用中的節點構成、節點的配置、啟動順序等,如圖所示:注意??,如果需要對接硬件設備,自研節點的主機網絡請選擇true,表示部署的服務Pod將使用宿主機網段而不是分配給該主機的Overlay網絡。
服務類型
ClusterIP:表示節點提供的服務主要對該配置其他節點提供服務,系統會分配一個ClusterIP與節點Service綁定,在overlay網絡外不能訪問。
LoadBalancer:會從創建集群時提供的服務地址段中分配一個IP綁定到該服務,可以在集群外可通局域網絡里訪問。
應用配置可視化編輯頁面分為三部分內容:
左側部分為節點:顯示已支持的各類節點,所有節點配置請查看節點說明。
中間部分為畫布:顯示應用需要的節點及部署順序關系。注意??,節點間的部署順序關系,是通過連線來表示的,連線箭頭所指節點先部署,連線起點后部署。
右側部分為節點屬性:顯示某個節點可以配置的參數。
左側的節點列表,分成了四類:
自研節點:用戶自己上傳的鏡像的載體。一份配置,可以又多個自研節點。
阿里云節點:一個節點,在應用被部署之后,對應阿里云RDS產品的一個實例(如一個RDS for MySQL數據庫實例)。
三方節點:各類主流開源中間件Docker鏡像。這一類鏡像完全來自于第三方,功能、性能、質量、安全等,均保持不變。
初始化節點:這類節點屬于輔助節點,在整個部署的生命周期,只會執行一次(其他節點通常不止一次,比如節點健康檢查失敗就會重啟)。
3.2.2模型與權限
權限聲明
點擊
,在應用的分發模式,每一次分發,都有可能歸屬到不同的買家。因此,每一個應用實例,都需要一個唯一的值來代表應用實例的身份。這里,我們采用了AppKey + AppSecret的模式。隨之而來的是,我們如何給AppKey授權。應用在調用IoT的各種API的時候,拿的這個AppKey需要被事先授權。因此,應用上架時,有必要清晰的指出該應用會調用哪些API,這樣才能在AppKey產生的那一刻,給他授相應的權限,在此頁面可對實例進行授權等操作,如圖所示:注意?? 若需要對接數據模型服務需要添加數據模型權限,對接單租戶免登,需要開通oauth協議服務權限。數據模型
點擊
,在列表中,除了選擇要聲明的模型之外,還要選擇版本、數據權限、訂閱。其中,數據權限有三種:查、增 | 查、增 | 刪 | 改 | 查,分別對應不同的操作類型。如圖所示:服務依賴
點擊
(應用為服務依賴方添加,服務提供方無需添加),選擇對應的服務模型,并配置服務模型的API,如圖所示:服務提供
點擊
(應用為服務提供方添加,服務依賴方無需添加),選擇對應的服務模型,并選擇相應的端口,如圖所示:3.2.3免登路徑
如果需要在邊緣控制臺中查看已部署的應用信息,則需要配置免登路徑,否則邊緣控制臺的免登跳轉會無法識別跳轉路徑,如圖所示:
3.3 實例管理
點擊
,按照界面提示填寫參數,如圖所示:參數設置如下:參數 | 描述 |
應用名稱 | 部署后應用實例的名稱。該名稱需賬號內唯一。 |
應用配置 | 選擇應用配置界面中創建的配置。 |
部署區域 | 當應用配置為邊緣配置時,顯示您在集群管理中創建的邊緣集群。 |
資源信息狀態 | 展示您選擇的配置所有的節點及占有的資源情況 |
單擊部署,執行部署任務。
部署應用時,需要根據應用配置占用相應的云資源。
部署過程可能會花費較長時間,請耐心等待。
應用部署成功后,您可以在部署記錄頁進行查看、運維和刪除等操作,詳情請參考運維工具文檔。
3.4 發布版本
應用部署成功后,可點擊發布版本,版本發布后將不能在進行調試、修改、刪除等操作,如圖所示:
4.云端集群管理
4.1 創建集群
在
頁面填寫集群基本信息,如圖所示:邊緣集群:支持脫離公網,實現集群與應用邊對邊通訊。
智能邊緣一體機集群:支持脫離公網,實現集群與應用邊對邊通訊,同時提供智能邊緣一體機集群的集群組件。
存儲地址:NFS服務器地址。
存儲路徑:NFS Mount路徑。
服務地址段:邊緣可用來分配的邊緣服務網段。
建議使用與當前邊緣局域網段不同的子網,否則有發生IP沖突的可能性導致服務異常。例如當前主機所在網絡為192.168.1.0/24;可在路由器中再添加另一個子網192.168.2.0/24專門用來分配服務VIP。
4.2 創建節點
在
頁面填寫節點基本信息,如圖所示:節點名稱:與集群名稱不同,節點名稱會被使用在K8s中,所以對命名有一定要求:只支持數字、小寫英文、短劃線,不能以短劃線開頭和結尾,長度限制4-30。節點IP段:節點IP段為當前主機所在網段,需要用戶提供以分配對應的Flannel Overlay服務網段。
4.3 加入節點
節點創建完成后點擊
到主機命令行執行,若完成,控制臺狀態將變成運行中,如圖所示:4.4 組件管理(智能邊緣一體機集群)
在
按鈕對智能邊緣一體機集群提供的集群組件進行初始化,如圖所示:5.邊緣集群管理
點擊
可查看部署在此集群的所有部署的應用列表與提供的集群組件信息,同時可以對已部署的應用進行測試,如圖所示:點擊
復制IP+端口號,打開新的瀏覽器頁面進行訪問,使用“超級賬戶iotedgeadmin”登錄“集群控制臺”,用戶名與密碼一致,首次登錄強制修改密碼。登錄成功后,可點擊部署應用的應用卡片,進入應用詳情,如圖所示:點擊
,添加“集群用戶”(用戶名、密碼、手機號),其中,手機號必填且唯一,并提示用戶,手機號是系統之間免登的憑據,如圖所示: