Skip to content
This repository was archived by the owner on Feb 2, 2022. It is now read-only.

Commit 64090bd

Browse files
authored
Support job deployment into a vnet. #158 (#159)
1 parent 53df5e9 commit 64090bd

File tree

7 files changed

+252
-26
lines changed

7 files changed

+252
-26
lines changed

cli/raft.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,34 @@ def validate(defaults):
125125
service_cli.upload_utils(
126126
utils_file_share, args.get('custom_tools_path'))
127127
service_cli.restart()
128+
elif service_action == 'config-vnet':
129+
vnetName = args.get('vnetName')
130+
if vnetName is None:
131+
ArgumentRequired('--vnetName')
132+
133+
subnetName = args.get('vnetSubnetName')
134+
if subnetName is None:
135+
ArgumentRequired('--vnetSubnetName')
136+
137+
vnetResourceGroup = args.get('vnetResourceGroup')
138+
if vnetResourceGroup is None:
139+
ArgumentRequired('--vnetResourceGroup')
140+
141+
vnetRegion = args.get('vnetRegion')
142+
if vnetRegion is None:
143+
ArgumentRequired('--vnetRegion')
144+
145+
service_cli.config_vnet(vnetName,
146+
subnetName,
147+
vnetResourceGroup,
148+
vnetRegion)
149+
150+
elif service_action == 'clear-vnet':
151+
vnetResourceGroup = args.get('vnetResourceGroup')
152+
if vnetResourceGroup is None:
153+
ArgumentRequired('--vnetResourceGroup')
154+
155+
service_cli.clear_vnet(vnetResourceGroup)
128156
else:
129157
raise Exception(f'Unhandled service argument: {service_action}')
130158

@@ -335,7 +363,8 @@ def main():
335363
formatter_class=argparse.RawTextHelpFormatter)
336364
service_parser.add_argument(
337365
'service-action',
338-
choices=['deploy', 'restart', 'info', 'upload-tools', 'update'],
366+
choices=['deploy', 'restart', 'info', 'upload-tools',
367+
'update', 'config-vnet', 'clear-vnet'],
339368
help=textwrap.dedent('''\
340369
deploy - Deploys the service
341370
@@ -346,8 +375,19 @@ def main():
346375
347376
upload-tools - Uploads the tools definitions to the service
348377
349-
update - Uploads the tools definitions to the service and
350-
restarts service to get latest service components
378+
update - Uploads the tools definitions to the service and
379+
restarts service to get latest service components
380+
381+
config-vnet - Defines the VNET that Azure Container Instances are deployed
382+
into. The --vnetName, --vnetSubnetName, --vnetResourceGroup
383+
and --vnetRegion parameters are required. Note that the subnet
384+
must be created with Microsoft.Storage service endpoint and
385+
it must be delegated to
386+
Microsoft.ContainerInstance/containerGroups.
387+
See the documentation for more information.
388+
389+
clear-vnet - Removes the vnet configuration. The --vnetResourceGroup
390+
parameter is required.
351391
'''))
352392

353393
allowed_skus = [
@@ -362,6 +402,18 @@ def main():
362402
service_parser.add_argument(
363403
'--custom-tools-path', default=None, required=False)
364404

405+
service_parser.add_argument(
406+
'--vnetName', default=None, required=False)
407+
408+
service_parser.add_argument(
409+
'--vnetSubnetName', default=None, required=False)
410+
411+
service_parser.add_argument(
412+
'--vnetResourceGroup', default=None, required=False)
413+
414+
service_parser.add_argument(
415+
'--vnetRegion', default=None, required=False)
416+
365417
# Add the positional argument.
366418
job_parser = sub_parser.add_parser(
367419
'job',

cli/raft_sdk/raft_deploy.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,69 @@ def service_info(self):
12131213
else:
12141214
raise RaftApiException(info.text, info.status_code)
12151215

1216+
def config_vnet(self, vnetName, vnetSubnetName, vnetResourceGroup, vnetRegion):
1217+
networkProfileName = (f'{self.definitions.deployment}'
1218+
f'-raft-aci-network-profile')
1219+
1220+
uri = (f"https://management.azure.com/subscriptions/"
1221+
f"{self.definitions.subscription}/"
1222+
f"resourceGroups/{vnetResourceGroup}/"
1223+
f"providers/Microsoft.Network/networkProfiles/"
1224+
f"{networkProfileName}/"
1225+
f"?api-version=2020-05-01")
1226+
1227+
body = (f'{{"properties" : {{'
1228+
f'"containerNetworkInterfaceConfigurations":[{{'
1229+
f'"properties":{{'
1230+
f'"ipConfigurations":[{{'
1231+
f'"properties": {{'
1232+
f'"subnet":{{'
1233+
f'"id":"/subscriptions/{self.definitions.subscription}'
1234+
f'/resourceGroups/{vnetResourceGroup}/providers'
1235+
f'/Microsoft.Network/virtualNetworks/{vnetName}'
1236+
f'/subnets/{vnetSubnetName}"'
1237+
f'}}}},"name":"ipconfig1"}}]}},"name":"eth1"}}]}},'
1238+
f'"location" : "{vnetRegion}"}}')
1239+
1240+
body_path = os.path.join(script_dir, 'body.json')
1241+
with open(body_path, 'w') as f:
1242+
f.write(body)
1243+
1244+
print(f'Creating network profile: {networkProfileName}')
1245+
result = az_json(f'rest --method put --uri {uri} '
1246+
f'--body @{body_path} --output json')
1247+
os.remove(body_path)
1248+
1249+
print('Updating orchestrator settings')
1250+
az('functionapp config appsettings set'
1251+
f' --name {self.definitions.orchestrator}'
1252+
f' --resource-group {self.definitions.resource_group}'
1253+
f' --settings "RAFT_NETWORK_PROFILE_NAME={networkProfileName}"')
1254+
1255+
az('functionapp config appsettings set'
1256+
f' --name {self.definitions.orchestrator}'
1257+
f' --resource-group {self.definitions.resource_group}'
1258+
f' --settings "RAFT_VNET_RESOURCE_GROUP={vnetResourceGroup}"')
1259+
1260+
def clear_vnet(self, vnetResourceGroup):
1261+
networkProfileName = (f'{self.definitions.deployment}'
1262+
f'-raft-aci-network-profile')
1263+
1264+
print('Removing network profile')
1265+
az(f'network profile delete --name {networkProfileName} '
1266+
f'--resource-group {vnetResourceGroup} --yes')
1267+
1268+
print('Updating orchestrator settings')
1269+
az('functionapp config appsettings set'
1270+
f' --name {self.definitions.orchestrator}'
1271+
f' --resource-group {self.definitions.resource_group}'
1272+
f' --settings "RAFT_NETWORK_PROFILE_NAME="')
1273+
1274+
az('functionapp config appsettings set'
1275+
f' --name {self.definitions.orchestrator}'
1276+
f' --resource-group {self.definitions.resource_group}'
1277+
f' --settings "RAFT_VNET_RESOURCE_GROUP="')
1278+
12161279
def wait_for_service_to_start(self, old_info=None):
12171280
new_info = old_info
12181281
while True:

docs/how-to-vnet.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# How to use a VNet
2+
3+
If you have a VNET connected to your company network and you need
4+
to be able to deploy your tests within that VNET, this document will tell
5+
you how to configure your VNET and RAFT deployment.
6+
7+
RAFT assumes that all your test jobs will either be deployed in the VNET or not
8+
in the VNET. It is a system level configuration, not a job-by-job configuration.
9+
10+
Note that deploying into a VNET does take a little longer than deploying outside
11+
a VNET.
12+
13+
#### Step 1: Understanding the limitations
14+
15+
RAFT uses Azure Container Instances and they require a specific configuration of your
16+
VNET with specific limitations. See those limitations [here](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-virtual-network-concepts).
17+
18+
When you deploy your RAFT job, it must be deployed into the same region as your VNET.
19+
This can be accomplished in two different ways.
20+
21+
One way is on a per-job basis. Using
22+
this way, you will use the `--region` parameter whenever you submit your job to specify
23+
your VNET region.
24+
25+
The second way is to deploy your RAFT service into the same region as your VNET. Using this
26+
method you will not need to use the `--region` parameter as RAFT will default the job's
27+
region to the system's deployment region. For example if you deploy RAFT to `westus` all
28+
jobs will also be deployed to `westus` by default.
29+
30+
#### Step 2: VNET Configuration
31+
32+
Your VNET can exist in any region and in any resource group.
33+
34+
**Create a Subnet on your VNET.** When RAFT creates a Container Instance for your job and it is deployed into your VNET and subnet,
35+
it will be assigned an
36+
IP address from the subnet address range. Be sure to create a subnet with enough address space to hold
37+
as many simultaneous jobs as you think you will need. For most deployments 8 bits of
38+
address space is more than enough.
39+
40+
The subnet can be created via the Azure Portal.
41+
42+
It is important that the subnet be created only for use with Azure Container Instances.
43+
You do this by specifing the Subnet Delegation.
44+
For more information on why, see the limitations referenced above.
45+
46+
![](images/subnet-delegation.jpg)
47+
48+
Additionally you must enable the storage service endpoint. RAFT mounts fileshares from
49+
your deployment's storage account onto the running containers. Setting the service endpoint
50+
in the subnet enables this behavior.
51+
52+
![](images/vnet-services.jpg)
53+
54+
#### Step 3: Tell RAFT about your VNET
55+
56+
After your VNET is configured and RAFT is deployed, use the following CLI command
57+
to configure RAFT to use your VNET.
58+
59+
`python raft.py service config-vnet --vnetName myVnet --vnetSubnetName myVnetSubnet --vnetResourceGroup myVnetRG --vnetRegion myVnetRegion`
60+
61+
| Parameter | Definition |
62+
|--- | :--|
63+
| `--vnetName` | The name of your VNET resource in Azure. |
64+
| `--vnetSubnetName` | The name of the subnet within the VNET. |
65+
| `--vnetResourceGroup` | The name of the resource group where the VNET lives. |
66+
| `--vnetRegion` | The region the VNET is deployed. |
67+
68+
This command creates a Network Profile that is used when deploying the Container Instance
69+
and updates the orchestrator's configuration settings `RAFT_NETWORK_PROFILE_NAME` and
70+
`RAFT_VNET_RESOURCE_GROUP`. These orchestrator settings inform
71+
the orchestrator how to deploy your job into your VNET.
72+
73+
Your VNET will need to have already been configured for this command to succeed.
74+
75+
If you decide to remove the VNET configuration from RAFT use the command:
76+
`python raft.py service clear-vnet --vnetResourceGroup myVnetRG`
77+

docs/images/subnet-delegation.jpg

15.9 KB
Loading

docs/images/vnet-services.jpg

20.7 KB
Loading

src/Orchestrator/Orchestrator/Orchestrator.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ static Orchestrator()
150150
utilsStorageAccountKey: utilsStorageAccountKey.Result,
151151
utilsFileShare: GetSetting("RAFT_UTILS_FILESHARE"),
152152
resultsStorageAccount: resultsStorageAccount,
153-
resultsStorageAccountKey: resultsStorageAccountKey.Result
153+
resultsStorageAccountKey: resultsStorageAccountKey.Result,
154+
155+
networkProfileName: GetSetting("RAFT_NETWORK_PROFILE_NAME"),
156+
vNetResourceGroup: GetSetting("RAFT_VNET_RESOURCE_GROUP")
154157
);
155158

156159
var allSecrets = OrchestratorLogic.ContainerInstances.initializeSecretsFromKeyvault(azure, agentConfig);

0 commit comments

Comments
 (0)