YARN-10219. Fix YARN Native Service Placement Constraints with Node Attributes.

Contributed by Eric Yang.
This commit is contained in:
Prabhu Joseph 2020-04-14 12:34:48 +05:30 committed by Prabhu Joseph
parent 3edbe8708a
commit c791b0e90e
5 changed files with 62 additions and 58 deletions

View File

@ -26,6 +26,7 @@ import org.apache.hadoop.yarn.api.records.ExecutionType;
import static org.apache.hadoop.yarn.service.api.records.Component import static org.apache.hadoop.yarn.service.api.records.Component
.RestartPolicyEnum; .RestartPolicyEnum;
import org.apache.hadoop.yarn.api.records.ExecutionTypeRequest; import org.apache.hadoop.yarn.api.records.ExecutionTypeRequest;
import org.apache.hadoop.yarn.api.records.NodeAttributeOpCode;
import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Priority;
import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.api.records.ResourceSizing; import org.apache.hadoop.yarn.api.records.ResourceSizing;
@ -811,16 +812,12 @@ public class Component implements EventHandler<ComponentEvent> {
PlacementConstraint constraint = null; PlacementConstraint constraint = null;
switch (yarnServiceConstraint.getType()) { switch (yarnServiceConstraint.getType()) {
case AFFINITY: case AFFINITY:
constraint = PlacementConstraints constraint = getAffinityConstraint(yarnServiceConstraint,
.targetIn(yarnServiceConstraint.getScope().getValue(), targetExpressions);
targetExpressions.toArray(new TargetExpression[0]))
.build();
break; break;
case ANTI_AFFINITY: case ANTI_AFFINITY:
constraint = PlacementConstraints constraint = getAntiAffinityConstraint(yarnServiceConstraint,
.targetNotIn(yarnServiceConstraint.getScope().getValue(), targetExpressions);
targetExpressions.toArray(new TargetExpression[0]))
.build();
break; break;
case AFFINITY_WITH_CARDINALITY: case AFFINITY_WITH_CARDINALITY:
constraint = PlacementConstraints.targetCardinality( constraint = PlacementConstraints.targetCardinality(
@ -865,6 +862,46 @@ public class Component implements EventHandler<ComponentEvent> {
} }
} }
private PlacementConstraint getAffinityConstraint(
org.apache.hadoop.yarn.service.api.records.PlacementConstraint
yarnServiceConstraint, List<TargetExpression> targetExpressions) {
PlacementConstraint constraint = null;
if (!yarnServiceConstraint.getTargetTags().isEmpty() ||
!yarnServiceConstraint.getNodePartitions().isEmpty()) {
constraint = PlacementConstraints
.targetIn(yarnServiceConstraint.getScope().getValue(),
targetExpressions.toArray(new TargetExpression[0]))
.build();
}
if (!yarnServiceConstraint.getNodeAttributes().isEmpty()) {
constraint = PlacementConstraints
.targetNodeAttribute(yarnServiceConstraint.getScope().getValue(),
NodeAttributeOpCode.EQ, targetExpressions.toArray(
new TargetExpression[0])).build();
}
return constraint;
}
private PlacementConstraint getAntiAffinityConstraint(
org.apache.hadoop.yarn.service.api.records.PlacementConstraint
yarnServiceConstraint, List<TargetExpression> targetExpressions) {
PlacementConstraint constraint = null;
if (!yarnServiceConstraint.getTargetTags().isEmpty() ||
!yarnServiceConstraint.getNodePartitions().isEmpty()) {
constraint = PlacementConstraints
.targetNotIn(yarnServiceConstraint.getScope().getValue(),
targetExpressions.toArray(new TargetExpression[0]))
.build();
}
if (!yarnServiceConstraint.getNodeAttributes().isEmpty()) {
constraint = PlacementConstraints
.targetNodeAttribute(yarnServiceConstraint.getScope().getValue(),
NodeAttributeOpCode.NE, targetExpressions.toArray(
new TargetExpression[0])).build();
}
return constraint;
}
private void setDesiredContainers(int n) { private void setDesiredContainers(int n) {
int delta = n - scheduler.getServiceMetrics().containersDesired.value(); int delta = n - scheduler.getServiceMetrics().containersDesired.value();
if (delta != 0) { if (delta != 0) {

View File

@ -354,19 +354,6 @@ public class ServiceApiUtil {
constraint.getName() == null ? "" : constraint.getName() + " ", constraint.getName() == null ? "" : constraint.getName() + " ",
comp.getName())); comp.getName()));
} }
if (constraint.getTargetTags().isEmpty()) {
throw new IllegalArgumentException(String.format(
RestApiErrorMessages.ERROR_PLACEMENT_POLICY_CONSTRAINT_TAGS_NULL,
constraint.getName() == null ? "" : constraint.getName() + " ",
comp.getName()));
}
for (String targetTag : constraint.getTargetTags()) {
if (!comp.getName().equals(targetTag)) {
throw new IllegalArgumentException(String.format(
RestApiErrorMessages.ERROR_PLACEMENT_POLICY_TAG_NAME_NOT_SAME,
targetTag, comp.getName(), comp.getName(), comp.getName()));
}
}
} }
} }
} }

View File

@ -734,6 +734,7 @@ public class TestYarnNativeServices extends ServiceTestUtils {
YarnConfiguration.SCHEDULER_RM_PLACEMENT_CONSTRAINTS_HANDLER); YarnConfiguration.SCHEDULER_RM_PLACEMENT_CONSTRAINTS_HANDLER);
conf.setInt(YarnConfiguration.RM_MAX_COMPLETED_APPLICATIONS, conf.setInt(YarnConfiguration.RM_MAX_COMPLETED_APPLICATIONS,
YarnConfiguration.DEFAULT_RM_MAX_COMPLETED_APPLICATIONS); YarnConfiguration.DEFAULT_RM_MAX_COMPLETED_APPLICATIONS);
conf.setInt(YarnConfiguration.NM_VCORES, 1);
setConf(conf); setConf(conf);
setupInternal(3); setupInternal(3);
ServiceClient client = createClient(getConf()); ServiceClient client = createClient(getConf());

View File

@ -554,33 +554,11 @@ public class TestServiceApiUtil extends ServiceTestUtils {
// Set the scope // Set the scope
pc.setScope(PlacementScope.NODE); pc.setScope(PlacementScope.NODE);
try { // Target tag is optional.
ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED);
Assert.fail(EXCEPTION_PREFIX + "constraint with no tag(s)");
} catch (IllegalArgumentException e) {
assertEquals(String.format(
RestApiErrorMessages.ERROR_PLACEMENT_POLICY_CONSTRAINT_TAGS_NULL,
"CA1 ", "comp-a"), e.getMessage());
}
// Set a target tag - but an invalid one
pc.setTargetTags(Collections.singletonList("comp-invalid"));
try {
ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED);
Assert.fail(EXCEPTION_PREFIX + "constraint with invalid tag name");
} catch (IllegalArgumentException e) {
assertEquals(
String.format(
RestApiErrorMessages.ERROR_PLACEMENT_POLICY_TAG_NAME_NOT_SAME,
"comp-invalid", "comp-a", "comp-a", "comp-a"),
e.getMessage());
}
// Set valid target tags now
pc.setTargetTags(Collections.singletonList("comp-a")); pc.setTargetTags(Collections.singletonList("comp-a"));
// Finally it should succeed // Validation can succeed for any arbitrary target, only scheduler knows
// if the target tag is valid.
try { try {
ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED); ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {

View File

@ -778,9 +778,6 @@ POST URL - http://localhost:8088/app/v1/services
"node_partitions": [ "node_partitions": [
"gpu", "gpu",
"fast-disk" "fast-disk"
],
"target_tags": [
"hello"
] ]
} }
] ]
@ -797,11 +794,12 @@ GET URL - http://localhost:8088/app/v1/services/hello-world
Note, for an anti-affinity component no more than 1 container will be allocated Note, for an anti-affinity component no more than 1 container will be allocated
in a specific node. In this example, 3 containers have been requested by in a specific node. In this example, 3 containers have been requested by
component "hello". All 3 containers were allocated because the cluster had 3 or component "hello". All 3 containers were allocated on separated centos7 nodes
more NMs. If the cluster had less than 3 NMs then less than 3 containers would because the node attributes expects to run on centos7 nodes.
be allocated. In cases when the number of allocated containers are less than the If the cluster had less than 3 NMs then less than
number of requested containers, the component and the service will be in 3 containers would be allocated. In cases when the number of allocated containers
non-STABLE state. are less than the number of requested containers, the component and the service
will be in non-STABLE state.
```json ```json
{ {
@ -822,16 +820,19 @@ non-STABLE state.
"placement_policy": { "placement_policy": {
"constraints": [ "constraints": [
{ {
"type": "ANTI_AFFINITY", "type": "AFFINITY",
"scope": "NODE", "scope": "NODE",
"node_attributes": { "node_attributes": {
"os": ["centos6", "centos7"], "os": ["centos7"]
"fault_domain": ["fd1", "fd2"]
}, },
"node_partitions": [ "node_partitions": [
"gpu", "gpu",
"fast-disk" "fast-disk"
], ]
},
{
"type": "ANTI_AFFINITY",
"scope": "NODE",
"target_tags": [ "target_tags": [
"hello" "hello"
] ]