forked from cloudsimplus/cloudsimplus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
VerticalVmCpuScalingExample.java
executable file
·406 lines (367 loc) · 17.9 KB
/
VerticalVmCpuScalingExample.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/*
* CloudSim Plus: A modern, highly-extensible and easier-to-use Framework for
* Modeling and Simulation of Cloud Computing Infrastructures and Services.
* http://cloudsimplus.org
*
* Copyright (C) 2015-2018 Universidade da Beira Interior (UBI, Portugal) and
* the Instituto Federal de Educação Ciência e Tecnologia do Tocantins (IFTO, Brazil).
*
* This file is part of CloudSim Plus.
*
* CloudSim Plus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CloudSim Plus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CloudSim Plus. If not, see <http://www.gnu.org/licenses/>.
*/
package org.cloudsimplus.examples.autoscaling;
import org.cloudbus.cloudsim.allocationpolicies.VmAllocationPolicySimple;
import org.cloudbus.cloudsim.brokers.DatacenterBroker;
import org.cloudbus.cloudsim.brokers.DatacenterBrokerSimple;
import org.cloudbus.cloudsim.cloudlets.Cloudlet;
import org.cloudbus.cloudsim.cloudlets.CloudletSimple;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.core.Simulation;
import org.cloudbus.cloudsim.datacenters.Datacenter;
import org.cloudbus.cloudsim.datacenters.DatacenterSimple;
import org.cloudbus.cloudsim.hosts.Host;
import org.cloudbus.cloudsim.hosts.HostSimple;
import org.cloudbus.cloudsim.provisioners.PeProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.ResourceProvisionerSimple;
import org.cloudbus.cloudsim.resources.Pe;
import org.cloudbus.cloudsim.resources.PeSimple;
import org.cloudbus.cloudsim.resources.Processor;
import org.cloudbus.cloudsim.schedulers.cloudlet.CloudletSchedulerTimeShared;
import org.cloudbus.cloudsim.schedulers.vm.VmSchedulerTimeShared;
import org.cloudbus.cloudsim.utilizationmodels.UtilizationModel;
import org.cloudbus.cloudsim.utilizationmodels.UtilizationModelDynamic;
import org.cloudbus.cloudsim.utilizationmodels.UtilizationModelFull;
import org.cloudbus.cloudsim.utilizationmodels.UtilizationModelStochastic;
import org.cloudbus.cloudsim.vms.Vm;
import org.cloudbus.cloudsim.vms.VmSimple;
import org.cloudsimplus.autoscaling.HorizontalVmScaling;
import org.cloudsimplus.autoscaling.VerticalVmScaling;
import org.cloudsimplus.autoscaling.VerticalVmScalingSimple;
import org.cloudsimplus.autoscaling.resources.ResourceScaling;
import org.cloudsimplus.autoscaling.resources.ResourceScalingGradual;
import org.cloudsimplus.autoscaling.resources.ResourceScalingInstantaneous;
import org.cloudsimplus.builders.tables.CloudletsTableBuilder;
import org.cloudsimplus.listeners.EventInfo;
import org.cloudsimplus.listeners.EventListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import static java.util.Comparator.comparingDouble;
/**
* An example that scales VM PEs up or down, according to the arrival of Cloudlets.
* A {@link VerticalVmScaling}
* is set to each {@link #createListOfScalableVms(int) initially created VM}.
* Every VM will check at {@link #SCHEDULING_INTERVAL specific time intervals}
* if its PEs {@link #upperCpuUtilizationThreshold(Vm) are over or underloaded},
* according to a <b>static computed utilization threshold</b>.
* Then it requests such PEs to be up or down scaled.
*
* <p>The example uses the CloudSim Plus {@link EventListener} feature
* to enable monitoring the simulation and dynamically create objects such as Cloudlets and VMs at runtime.
* It relies on
* <a href="https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html">Java 8 Method References</a>
* to set a method to be called for {@link Simulation#addOnClockTickListener(EventListener) onClockTick events}.
* It enables getting notifications when the simulation clock advances, then creating and submitting new cloudlets.
* </p>
*
* @author Manoel Campos da Silva Filho
* @since CloudSim Plus 1.2.0
* @see VerticalVmRamScalingExample
* @see VerticalVmCpuScalingDynamicThreshold
*/
public class VerticalVmCpuScalingExample {
/**
* The interval in which the Datacenter will schedule events.
* As lower is this interval, sooner the processing of VMs and Cloudlets
* is updated and you will get more notifications about the simulation execution.
* However, it can affect the simulation performance.
*
* <p>For this example, a large schedule interval such as 15 will make that just
* at every 15 seconds the processing of VMs is updated. If a VM is overloaded, just
* after this time the creation of a new one will be requested
* by the VM's {@link HorizontalVmScaling Horizontal Scaling} mechanism.</p>
*
* <p>If this interval is defined using a small value, you may get
* more dynamically created VMs than expected. Accordingly, this value
* has to be trade-off.
* For more details, see {@link Datacenter#getSchedulingInterval()}.</p>
*/
private static final int SCHEDULING_INTERVAL = 1;
private static final int HOSTS = 1;
private static final int HOST_PES = 32;
private static final int VMS = 1;
private static final int VM_PES = 14;
private static final int VM_RAM = 1200;
private final CloudSim simulation;
private DatacenterBroker broker0;
private List<Host> hostList;
private List<Vm> vmList;
private List<Cloudlet> cloudletList;
private static final int CLOUDLETS = 10;
private static final int CLOUDLETS_INITIAL_LENGTH = 20_000;
private int createsVms;
public static void main(String[] args) {
new VerticalVmCpuScalingExample();
}
/**
* Default constructor that builds the simulation scenario and starts the simulation.
*/
private VerticalVmCpuScalingExample() {
/*Enables just some level of log messages.
Make sure to import org.cloudsimplus.util.Log;*/
//Log.setLevel(ch.qos.logback.classic.Level.WARN);
hostList = new ArrayList<>(HOSTS);
vmList = new ArrayList<>(VMS);
cloudletList = new ArrayList<>(CLOUDLETS);
simulation = new CloudSim();
simulation.addOnClockTickListener(this::onClockTickListener);
createDatacenter();
broker0 = new DatacenterBrokerSimple(simulation);
vmList.addAll(createListOfScalableVms(VMS));
createCloudletListsWithDifferentDelays();
broker0.submitVmList(vmList);
broker0.submitCloudletList(cloudletList);
simulation.start();
printSimulationResults();
}
/**
* Shows updates every time the simulation clock advances.
* @param evt information about the event happened (that for this Listener is just the simulation time)
*/
private void onClockTickListener(EventInfo evt) {
vmList.forEach(vm ->
System.out.printf(
"\t\tTime %6.1f: Vm %d CPU Usage: %6.2f%% (%2d vCPUs. Running Cloudlets: #%d). RAM usage: %.2f%% (%d MB)%n",
evt.getTime(), vm.getId(), vm.getCpuPercentUtilization()*100.0, vm.getNumberOfPes(),
vm.getCloudletScheduler().getCloudletExecList().size(),
vm.getRam().getPercentUtilization()*100, vm.getRam().getAllocatedResource())
);
}
private void printSimulationResults() {
final List<Cloudlet> finishedCloudlets = broker0.getCloudletFinishedList();
final Comparator<Cloudlet> sortByVmId = comparingDouble(c -> c.getVm().getId());
final Comparator<Cloudlet> sortByStartTime = comparingDouble(Cloudlet::getExecStartTime);
finishedCloudlets.sort(sortByVmId.thenComparing(sortByStartTime));
new CloudletsTableBuilder(finishedCloudlets).build();
}
/**
* Creates a Datacenter and its Hosts.
*/
private void createDatacenter() {
for (int i = 0; i < HOSTS; i++) {
hostList.add(createHost());
}
Datacenter dc0 = new DatacenterSimple(simulation, hostList, new VmAllocationPolicySimple());
dc0.setSchedulingInterval(SCHEDULING_INTERVAL);
}
private Host createHost() {
List<Pe> peList = new ArrayList<>(HOST_PES);
for (int i = 0; i < HOST_PES; i++) {
peList.add(new PeSimple(1000, new PeProvisionerSimple()));
}
final long ram = 20000; //in Megabytes
final long bw = 100000; //in Megabytes
final long storage = 10000000; //in Megabites/s
final int id = hostList.size();
return new HostSimple(ram, bw, storage, peList)
.setRamProvisioner(new ResourceProvisionerSimple())
.setBwProvisioner(new ResourceProvisionerSimple())
.setVmScheduler(new VmSchedulerTimeShared());
}
/**
* Creates a list of initial VMs in which each VM is able to scale vertically
* when it is over or underloaded.
*
* @param numberOfVms number of VMs to create
* @return the list of scalable VMs
* @see #createVerticalPeScaling()
*/
private List<Vm> createListOfScalableVms(final int numberOfVms) {
List<Vm> newList = new ArrayList<>(numberOfVms);
for (int i = 0; i < numberOfVms; i++) {
Vm vm = createVm();
vm.setPeVerticalScaling(createVerticalPeScaling());
newList.add(vm);
}
return newList;
}
/**
* Creates a Vm object.
*
* @return the created Vm
*/
private Vm createVm() {
final int id = createsVms++;
return new VmSimple(id, 1000, VM_PES)
.setRam(VM_RAM).setBw(1000).setSize(10000)
.setCloudletScheduler(new CloudletSchedulerTimeShared());
}
/**
* Creates a {@link VerticalVmScaling} for scaling VM's CPU when it's under or overloaded.
*
* <p>Realize the lower and upper thresholds are defined inside this method by using
* references to the methods {@link #lowerCpuUtilizationThreshold(Vm)}
* and {@link #upperCpuUtilizationThreshold(Vm)}.
* These methods enable defining thresholds in a dynamic way
* and even different thresholds for distinct VMs.
* Therefore, it's a powerful mechanism.
* </p>
*
* <p>
* However, if you are defining thresholds in a static way,
* and they are the same for all VMs, you can use a Lambda Expression
* like below, for instance, instead of creating a new method that just returns a constant value:<br>
* {@code verticalCpuScaling.setLowerThresholdFunction(vm -> 0.4);}
* </p>
*
* @see #createListOfScalableVms(int)
*/
private VerticalVmScaling createVerticalPeScaling() {
//The percentage in which the number of PEs has to be scaled
final double scalingFactor = 0.1;
VerticalVmScalingSimple verticalCpuScaling = new VerticalVmScalingSimple(Processor.class, scalingFactor);
/* By uncommenting the line below, you will see that, instead of gradually
* increasing or decreasing the number of PEs, when the scaling object detects
* the CPU usage is above or below the defined thresholds,
* it will automatically calculate the number of PEs to add/remove to
* move the VM from the over or underload condition.
*/
//verticalCpuScaling.setResourceScaling(new ResourceScalingInstantaneous());
/** Different from the commented line above, the line below implements a ResourceScaling using a Lambda Expression.
* It is just an example which scales the resource twice the amount defined by the scaling factor
* defined in the constructor.
*
* Realize that if the setResourceScaling method is not called, a ResourceScalingGradual will be used,
* which scales the resource according to the scaling factor.
* The lower and upper thresholds after this line can also be defined using a Lambda Expression.
*
* So, here we are defining our own {@link ResourceScaling} instead of
* using the available ones such as the {@link ResourceScalingGradual}
* or {@link ResourceScalingInstantaneous}.
*/
verticalCpuScaling.setResourceScaling(vs -> 2*vs.getScalingFactor()*vs.getAllocatedResource());
verticalCpuScaling.setLowerThresholdFunction(this::lowerCpuUtilizationThreshold);
verticalCpuScaling.setUpperThresholdFunction(this::upperCpuUtilizationThreshold);
return verticalCpuScaling;
}
/**
* Defines the minimum CPU utilization percentage that indicates a Vm is underloaded.
* This function is using a statically defined threshold, but it would be defined
* a dynamic threshold based on any condition you want.
* A reference to this method is assigned to each Vertical VM Scaling created.
*
* @param vm the VM to check if its CPU is underloaded.
* <b>The parameter is not being used internally, which means the same
* threshold is used for any Vm.</b>
* @return the lower CPU utilization threshold
* @see #createVerticalPeScaling()
*/
private double lowerCpuUtilizationThreshold(Vm vm) {
return 0.4;
}
/**
* Defines the maximum CPU utilization percentage that indicates a Vm is overloaded.
* This function is using a statically defined threshold, but it would be defined
* a dynamic threshold based on any condition you want.
* A reference to this method is assigned to each Vertical VM Scaling created.
*
* @param vm the VM to check if its CPU is overloaded.
* The parameter is not being used internally, that means the same
* threshold is used for any Vm.
* @return the upper CPU utilization threshold
* @see #createVerticalPeScaling()
*/
private double upperCpuUtilizationThreshold(Vm vm) {
return 0.8;
}
/**
* Creates lists of Cloudlets to be submitted to the broker with different delays,
* simulating their arrivals at different times.
* Adds all created Cloudlets to the {@link #cloudletList}.
*/
private void createCloudletListsWithDifferentDelays() {
final int initialCloudletsNumber = (int)(CLOUDLETS/2.5);
final int remainingCloudletsNumber = CLOUDLETS-initialCloudletsNumber;
//Creates a List of Cloudlets that will start running immediately when the simulation starts
for (int i = 0; i < initialCloudletsNumber; i++) {
cloudletList.add(createCloudlet(CLOUDLETS_INITIAL_LENGTH+(i*1000), 2));
}
/*
* Creates several Cloudlets, increasing the arrival delay and decreasing
* the length of each one.
* The progressing delay enables CPU usage to increase gradually along the arrival of
* new Cloudlets (triggering CPU up scaling at some point in time).
*
* The decreasing length enables Cloudlets to finish in different times,
* to gradually reduce CPU usage (triggering CPU down scaling at some point in time).
*
* Check the logs to understand how the scaling is working.
*/
for (int i = 1; i <= remainingCloudletsNumber; i++) {
cloudletList.add(createCloudlet(CLOUDLETS_INITIAL_LENGTH*2/i, 1,i*2));
}
}
/**
* Creates a single Cloudlet with no delay, which means the Cloudlet arrival time will
* be zero (exactly when the simulation starts).
*
* @param length the Cloudlet length
* @param numberOfPes the number of PEs the Cloudlets requires
* @return the created Cloudlet
*/
private Cloudlet createCloudlet(final long length, final int numberOfPes) {
return createCloudlet(length, numberOfPes, 0);
}
/**
* Creates a single Cloudlet.
*
* @param length the length of the cloudlet to create.
* @param numberOfPes the number of PEs the Cloudlets requires.
* @param delay the delay that defines the arrival time of the Cloudlet at the Cloud infrastructure.
* @return the created Cloudlet
*/
private Cloudlet createCloudlet(final long length, final int numberOfPes, final double delay) {
/*
Since a VM PE isn't used by two Cloudlets at the same time,
the Cloudlet can used 100% of that CPU capacity at the time
it is running. Even if a CloudletSchedulerTimeShared is used
to share the same VM PE among multiple Cloudlets,
just one Cloudlet uses the PE at a time.
Then it is preempted to enable other Cloudlets to use such a VM PE.
*/
final UtilizationModel utilizationCpu = new UtilizationModelFull();
/**
* Since BW e RAM are shared resources that don't enable preemption,
* two Cloudlets can't use the same portion of such resources at the same time
* (unless virtual memory is enabled, but such a feature is not available in simulation).
* This way, the total capacity of such resources is being evenly split among created Cloudlets.
* If there are 10 Cloudlets, each one will use just 10% of such resources.
* This value can be defined in different ways, as you want. For instance, some Cloudlets
* can require more resources than other ones.
* To enable that, you would need to instantiate specific {@link UtilizationModelDynamic} for each Cloudlet,
* use a {@link UtilizationModelStochastic} to define resource usage randomly,
* or use any other {@link UtilizationModel} implementation.
*/
final UtilizationModel utilizationModelDynamic = new UtilizationModelDynamic(1.0/CLOUDLETS);
Cloudlet cl = new CloudletSimple(length, numberOfPes);
cl.setFileSize(1024)
.setOutputSize(1024)
.setUtilizationModelBw(utilizationModelDynamic)
.setUtilizationModelRam(utilizationModelDynamic)
.setUtilizationModelCpu(utilizationCpu)
.setSubmissionDelay(delay);
return cl;
}
}