Sprendimai, kaip atnaujinti „Apache DolphinScheduler“ iš 1.3.4 versijos į 3.1.2
Kadangi reikia skatinti „Apache DolphinScheduler“ naujinimą darbe, mano atliktas preliminarus tyrimas rodo, kad nuo 1.3.4 iki 3.1.2 versijos gerokai patobulinti našumas ir funkcionalumas. Taigi, rekomenduojama atnaujinti.
Oficialioje naujinimo dokumentacijoje pateikiami naujinimo scenarijai. Atliekant nedidelių versijų naujinimus, pakanka paleisti scenarijų, tačiau atnaujinus kelias pagrindines versijas vis tiek gali kilti įvairių problemų. Toliau pateikiama šių problemų santrauka.
Sena versija: 1.3.4
Nauja versija: 3.1.2
Problemos kolekcija
- Išteklių centro klaida
Po naujovinimo naudojant išteklių centrą įvyksta klaida: IllegalArgumentException: Failed to specify server's Kerberos principal name
.
Išteklių centras naudoja HDFS su įjungtu Kerberos autentifikavimu.
Sprendimas:
Redaguoti dolphinscheduler/api-server/conf/hdfs-site.xml
ir pridėkite šį turinį:
<property>
<name>dfs.namenode.kerberos.principal.pattern</name>
<value>*</value>
</property>
2. Užduočių egzempliorių žurnalo praradimas
Po naujovinimo peržiūrint užduočių egzempliorių žurnalus atsiranda klaida, nurodanti, kad žurnalų nepavyko rasti. Patikrinus klaidos pranešimą ir naujos versijos katalogų struktūrą bei žurnalo kelius, nustatoma, kad žurnalo kelias pasikeitė.
Sprendimas: Vykdykite SQL, kad pakeistumėte žurnalo kelią
atnaujinti t_ds_task_instance set log_path=replace(log_path,'/logs/','/worker-server/logs/');
Tada nukopijuokite pradinius žurnalo failus į naują žurnalo kelią:
cp -r {old_dolphinscheduler_directory}/logs/[1-9]* {new_dolphinscheduler_directory}/worker-server/logs/*
3. Klaida kuriant darbo eigą po atnaujinimo
Klaidos pranešimas rodo, kad pradinės pirminių raktų reikšmės yra t_ds_process_definition_log
ir t_ds_process_definition
yra nenuoseklūs. Šių reikšmių sinchronizavimas išsprendžia problemą.
Sprendimas: Vykdykite šį SQL
-- Get the auto-increment value of the primary key
select AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'dolphinscheduler' AND TABLE_NAME = 't_ds_process_definition' limit 1;
-- Use the result of the above SQL to execute the following
alter table dolphinscheduler_bak1.t_ds_process_definition_log auto_increment = {max_id};
4. Ištuštinkite užduočių egzempliorių sąrašą po atnaujinimo
Patikrinę SQL užklausą dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/TaskInstanceMapper.xml
nustatyta, kad užklausos užduoties egzemplioriaus SQL pateikia nuorodas į t_ds_task_definition_log
lentelė, bet sujungimo sąlyga define.code=instance.task_code
nesutampa.
Atsižvelgiant į būklę define.project_code = #{projectCode}
sujungimas su t_ds_task_definition_log
daugiausia skirtas filtravimui pagal projectCode
. Pakeiskite SQL taip:
Sprendimas:
select
<include refid="baseSqlV2">
<property name="alias" value="instance"/>
</include>
,
process.name as process_instance_name
from t_ds_task_instance instance
-- left join t_ds_task_definition_log define
-- on define.code = instance.task_code and
-- define.version = instance.task_definition_version
join t_ds_process_instance process
on process.id = instance.process_instance_id
join t_ds_process_definition define
on define.code = process.process_definition_code
where define.project_code = #{projectCode}
<if test="startTime != null">
and instance.start_time <![CDATA[ >= ]] > #{startTime}
</if>
Tiesiogiai prisijungus prie t_ds_process_definition
kuriame taip pat yra project_code
filtravimo lauką, užklausa pateiks teisingus duomenis.
5. Nulinės rodyklės išimtis vykdant atnaujinimo scenarijų
(1) Išanalizuokite žurnalą ir suraskite problemą 517 eilutėje UpgradeDao.java
- Originalus kodas:
513 if (TASK_TYPE_SUB_PROCESS.equals(taskType)) {
514 JsonNode jsonNodeDefinitionId = param.get("processDefinitionId");
515 if (jsonNodeDefinitionId != null) {
516 param.put("processDefinitionCode",
517 processDefinitionMap.get(jsonNodeDefinitionId.asInt()).getCode());
518 param.remove("processDefinitionId");
519 }
520 }
Problema ta processDefinitionMap.get(jsonNodeDefinitionId.asInt())
grąžina nulį. Pridėkite nulinį patikrinimą, o jei nulinis, užregistruokite atitinkamą informaciją, kad galėtumėte peržiūrėti vėliau.
Sprendimas:
if (jsonNodeDefinitionId != null) {
if (processDefinitionMap.get(jsonNodeDefinitionId.asInt()) != null) {
param.put("processDefinitionCode",processDefinitionMap.get(jsonNodeDefinitionId.asInt()).getCode());
param.remove("processDefinitionId");
} else {
logger.error("*******************error");
logger.error("*******************param:" + param);
logger.error("*******************jsonNodeDefinitionId:" + jsonNodeDefinitionId);
}
}
(2) Išanalizuokite žurnalą ir suraskite problemą eilutėje 675 in UpgradeDao.java
Originalus kodas:
669 if (mapEntry.isPresent()) {
670 Map.Entry<Long, Map<String, Long>> processCodeTaskNameCodeEntry = mapEntry.get();
671 dependItem.put("definitionCode", processCodeTaskNameCodeEntry.getKey());
672 String depTasks = dependItem.get("depTasks").asText();
673 long taskCode =
674 "ALL".equals(depTasks) || processCodeTaskNameCodeEntry.getValue() == null ? 0L
675 : processCodeTaskNameCodeEntry.getValue().get(depTasks);
676 dependItem.put("depTaskCode", taskCode);
677 }
Problema ta processCodeTaskNameCodeEntry.getValue().get(depTasks)
grąžina nulį. Prieš priskirdami reikšmę, modifikuokite logiką, kad patikrintumėte, ar nėra nulio, ir užregistruokite atitinkamą informaciją.
Sprendimas:
long taskCode =0;
if (processCodeTaskNameCodeEntry.getValue() != null
&&processCodeTaskNameCodeEntry.getValue().get(depTasks)!=null){
taskCode =processCodeTaskNameCodeEntry.getValue().get(depTasks);
}else{
logger.error("******************** depTasks:"+depTasks);
logger.error("******************** taskCode not in "+JSONUtils.toJsonString(processCodeTaskNameCodeEntry));
}
dependItem.put("depTaskCode", taskCode);
6. Prisijungimo klaida po LDAP integravimo, neaiškus el. pašto lauko pavadinimas
Konfigūruokite LDAP integravimą api-server/conf/application.yaml
security:
authentication:
# Authentication types (supported types: PASSWORD,LDAP)
type: LDAP
# IF you set type `LDAP`, below config will be effective
ldap:
# ldap server config
urls: xxx
base-dn: xxx
username: xxx
password: xxx
user:
# admin userId when you use LDAP login
admin: xxx
identity-attribute: xxx
email-attribute: xxx
# action when ldap user is not exist (supported types: CREATE,DENY)
not-exist-action: CREATE
Norėdami sėkmingai integruoti LDAP, laukai urls
, base-dn
, username
, password
, identity
ir email
turi būti teisingai užpildytas. Jei el. pašto lauko pavadinimas nežinomas, iš pradžių palikite jį tuščią.
Pradėję paslaugą pabandykite prisijungti naudodami LDAP vartotoją.
Sprendimas: LDAP autentifikavimo kodas yra dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/security/impl/ldap/LdapService.java
viduje ldapLogin()
metodas
ctx = new InitialLdapContext(searchEnv, null);
SearchControls sc = new SearchControls();
sc.setReturningAttributes(new String[]{ldapEmailAttribute});
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
EqualsFilter filter = new EqualsFilter(ldapUserIdentifyingAttribute, userId);
NamingEnumeration<SearchResult> results = ctx.search(ldapBaseDn, filter.toString(), sc);
if (results.hasMore()) {
// get the users DN (distinguishedName) from the result
SearchResult result = results.next();
NamingEnumeration<? extends Attribute> attrs = result.getAttributes().getAll();
while (attrs.hasMore()) {
// Open another connection to the LDAP server with the found DN and the password
searchEnv.put(Context.SECURITY_PRINCIPAL, result.getNameInNamespace());
searchEnv.put(Context.SECURITY_CREDENTIALS, userPwd);
try {
new InitialDirContext(searchEnv);
} catch (Exception e) {
logger.warn("invalid ldap credentials or ldap search error", e);
return null;
}
Attribute attr = attrs.next();
if (attr.getID().equals(ldapEmailAttribute)) {
return (String) attr.get();
}
}
}
Trečioji eilutė filtruoja pagal nurodytą el. pašto atributo lauką. Laikinai pakomentuokite šią eilutę:
// sc.setReturningAttributes(new String[]{ldapEmailAttribute});
Iš naujo paleiskite kodą; Dešimtoje eilutėje bus pateikti visi laukai:
NamingEnumeration<? extends Attribute> attrs = result.getAttributes().getAll();
Spausdindami arba derindami nustatykite teisingą el. pašto lauką ir atitinkamai atnaujinkite konfigūracijos failą. Tada panaikinkite kodo komentarą ir iš naujo paleiskite paslaugą, kad sėkmingai integruotumėte LDAP prisijungimą.
7. Administratoriaus ištekliaus failo autorizacija neveikia nuolatiniams vartotojams
Po kelių bandymų nustatyta, kad įprasti vartotojai gali matyti tik jiems priklausančius išteklių failus. Administratoriaus autorizacija nepadaro failų matomų.
Sprendimas:
Viduje listAuthorizedResource
metodas ResourcePermissionCheckServiceImpl.java
failas, esantis adresu dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/permission/
pakeiskite grąžinimo kolekciją į relationResources
:
@Override
public Set<Integer> listAuthorizedResource(int userId, Logger logger) {
List<Resource> relationResources;
if (userId == 0) {
relationResources = new ArrayList<>();
} else {
// query resource relation
List<Integer> resIds = resourceUserMapper.queryResourcesIdListByUserIdAndPerm(userId, 0);
relationResources = CollectionUtils.isEmpty(resIds) ? new ArrayList<>() : resourceMapper.queryResourceListById(resIds);
}
List<Resource> ownResourceList = resourceMapper.queryResourceListAuthored(userId, -1);
relationResources.addAll(ownResourceList);
return relationResources.stream().map(Resource::getId).collect(toSet()); // Fixed the issue that the resource file authorization was invalid
// return ownResourceList.stream().map(Resource::getId).collect(toSet());
}
Patikrinkite naujos versijos pakeitimų žurnalą ir sužinokite, kad ši klaida buvo ištaisyta 3.1.3 versijoje: GitHub PR #13318
8. „Kerberos“ galiojimo pabaigos problemos
„Kerberos“ sukonfigūruotas su bilieto galiojimo laiku, todėl HDFS ištekliai išteklių centre po tam tikro laikotarpio tampa nepasiekiami. Geriausias sprendimas yra pridėti logikos periodiniam kredencialų atnaujinimui.
Sprendimas:
Įtraukite šį metodą CommonUtils.java
esantis adresu dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/utils/
:
/**
* Periodically update credentials
*/
private static void startCheckKeytabTgtAndReloginJob() {
// Periodically update credentials daily
Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(() -> {
try {
UserGroupInformation.getLoginUser().checkTGTAndReloginFromKeytab();
logger.warn("Check Kerberos Tgt And Relogin From Keytab Finish.");
} catch (IOException e) {
logger.error("Check Kerberos Tgt And Relogin From Keytab Error", e);
}
}, 0, 1, TimeUnit.DAYS);
logger.info("Start Check Keytab TGT And Relogin Job Success.");
}
Tada iškvieskite šį metodą loadKerberosConf
metodas prieš grąžinant true:
public static boolean loadKerberosConf(String javaSecurityKrb5Conf, String loginUserKeytabUsername,
String loginUserKeytabPath, Configuration configuration) throws IOException {
if (CommonUtils.getKerberosStartupState()) {
System.setProperty(Constants.JAVA_SECURITY_KRB5_CONF, StringUtils.defaultIfBlank(javaSecurityKrb5Conf,
PropertyUtils.getString(Constants.JAVA_SECURITY_KRB5_CONF_PATH)));
configuration.set(Constants.HADOOP_SECURITY_AUTHENTICATION, Constants.KERBEROS);
UserGroupInformation.setConfiguration(configuration);
UserGroupInformation.loginUserFromKeytab(
StringUtils.defaultIfBlank(loginUserKeytabUsername,
PropertyUtils.getString(Constants.LOGIN_USER_KEY_TAB_USERNAME)),
StringUtils.defaultIfBlank(loginUserKeytabPath,
PropertyUtils.getString(Constants.LOGIN_USER_KEY_TAB_PATH)));
startCheckKeytabTgtAndReloginJob(); // Call here
return true;
}
return false;
}
Šiame straipsnyje visų pirma įrašomos problemos, su kuriomis susiduriama naujovinimo proceso metu, ir siekiama padėti bendruomenei, kuri susiduria su panašiais iššūkiais.