-
Notifications
You must be signed in to change notification settings - Fork 192
/
DockerUtils.cs
407 lines (363 loc) · 23.6 KB
/
DockerUtils.cs
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
407
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using Docker.DotNet;
using Docker.DotNet.X509;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.Compute.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using System;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using Microsoft.Azure.Management.Compute.Fluent.Models;
using Microsoft.Azure.Management.Network.Fluent;
using Docker.DotNet.Models;
using System.Collections.Generic;
using System.IO;
namespace Microsoft.Azure.Management.Samples.Common
{
/**
* Syncronous extension wrappers class for Docker.DotNet async methods.
*/
public static class DockerExtensions
{
public static Stream PullImage(this IImageOperations operations, ImagesPullParameters parameters, AuthConfig authConfig)
{
return operations.PullImageAsync(parameters, authConfig).Result;
}
public static Stream PushImage(this IImageOperations operations, string name, ImagePushParameters parameters, AuthConfig authConfig)
{
return operations.PushImageAsync(name, parameters, authConfig).Result;
}
public static CreateContainerResponse CreateContainer(this IContainerOperations operations, CreateContainerParameters parameters)
{
return operations.CreateContainerAsync(parameters).Result;
}
public static IList<ImagesListResponse> ListImages(this IImageOperations operations, ImagesListParameters parameters)
{
return operations.ListImagesAsync(parameters).Result;
}
public static IList<ContainerListResponse> ListContainers(this IContainerOperations operations, ContainersListParameters parameters)
{
return operations.ListContainersAsync(parameters).Result;
}
public static void RemoveContainer(this IContainerOperations operations, string name, ContainerRemoveParameters parameters)
{
operations.RemoveContainerAsync(name, parameters).Wait();
}
public static void DeleteImage(this IImageOperations operations, string name, ImageDeleteParameters parameters)
{
operations.DeleteImageAsync(name, parameters).Wait();
}
public static CommitContainerChangesResponse CommitContainerChanges(this IMiscellaneousOperations operations, CommitContainerChangesParameters parameters)
{
return operations.CommitContainerChangesAsync(parameters).Result;
}
}
/**
* Utility class to be used by Azure Container Registry sample.
* - Creates "in memory" SSL configuration to be used by the Java Docker client
* - Builds a Docker client config object
* - Creates a new Azure virtual machine and installs Docker
* - Creates a Java DockerClient to be used for communicating with a Docker host/engine
*/
public class DockerUtils
{
/**
* Instantiate a Docker client that will be used for Docker client related operations.
* @param azure - instance of Azure
* @param rgName - name of the Azure resource group to be used when creating a virtual machine
* @param region - region to be used when creating a virtual machine
* @return an instance of DockerClient
*/
public static DockerClient CreateDockerClient(IAzure azure, String rgName, Region region)
{
string envDockerHost = Environment.GetEnvironmentVariable("DOCKER_HOST");
string envDockerCertPath = Environment.GetEnvironmentVariable("DOCKER_CERT_PATH");
string dockerHostUrl;
DockerClient dockerClient;
if (String.IsNullOrWhiteSpace(envDockerHost))
{
// Could not find a Docker environment; presume that there is no local Docker engine running and
// attempt to configure a Docker engine running inside a new Azure virtual machine
dockerClient = FromNewDockerVM(azure, rgName, region);
}
else
{
dockerHostUrl = envDockerHost;
Utilities.Log("Using local settings to connect to a Docker service: " + dockerHostUrl);
if (String.IsNullOrWhiteSpace(envDockerCertPath) || !File.Exists(envDockerCertPath + "/key.pfx"))
{
// Could not find a Docker environment variable that is used to set the path to the certificate files
// which are required when authenticating to a secured Docker service.
// Presume that no authentication is required to connect with this Docker service
dockerClient = new DockerClientConfiguration(new Uri(dockerHostUrl)).CreateClient();
}
else
{
// Found environment variable pointing at the folder containing the certificate files required to
// establish a secured connection to this Docker service.
// "key.pfx" file is required when connecting using the .Net client; to generate this certificate file
// from standard .PEM certificate file execute the following command:
//
// openssl pkcs12 -export -inkey key.pem -in cert.pem -out key.pfx -passout pass: -CAfile ca.pem
//
var credentials = new CertificateCredentials(new X509Certificate2(envDockerCertPath + "/key.pfx"));
credentials.ServerCertificateValidationCallback += (o, c, ch, er) => true;
dockerClient = new DockerClientConfiguration(new Uri(dockerHostUrl), credentials).CreateClient();
}
}
Utilities.Log("List Docker host info");
Utilities.Log("\tFound Docker version: " + dockerClient.Miscellaneous.GetVersionAsync().Result.Version);
Utilities.Log("\tFound Docker server version: " + dockerClient.Miscellaneous.GetSystemInfoAsync().Result.ServerVersion);
return dockerClient;
}
/**
* It creates a new Azure virtual machine and it instantiate a Java Docker client.
* @param azure - instance of Azure
* @param rgName - name of the Azure resource group to be used when creating a virtual machine
* @param region - region to be used when creating a virtual machine
* @return an instance of DockerClient
*/
public static DockerClient FromNewDockerVM(IAzure azure, String rgName, Region region)
{
string dockerVMName = SdkContext.RandomResourceName("dockervm", 15);
string publicIPDnsLabel = SdkContext.RandomResourceName("pip", 10);
string vmUserName = "dockerUser";
string vmPassword = "12NewPA$$w0rd!";
// Could not find a Docker environment; presume that there is no local Docker engine running and
// attempt to configure a Docker engine running inside a new Azure virtual machine
Utilities.Log("Creating an Azure virtual machine running Docker");
IVirtualMachine dockerVM = azure.VirtualMachines.Define(dockerVMName)
.WithRegion(region)
.WithExistingResourceGroup(rgName)
.WithNewPrimaryNetwork("10.0.0.0/28")
.WithPrimaryPrivateIPAddressDynamic()
.WithNewPrimaryPublicIPAddress(publicIPDnsLabel)
.WithPopularLinuxImage(KnownLinuxVirtualMachineImage.UbuntuServer16_04_Lts)
.WithRootUsername(vmUserName)
.WithRootPassword(vmPassword)
.WithSize(VirtualMachineSizeTypes.StandardD2V2)
.Create();
Utilities.Log("Created Azure Virtual Machine: " + dockerVM.Id);
// Get the IP of the Docker host
INicIPConfiguration nicIPConfiguration = dockerVM.GetPrimaryNetworkInterface().PrimaryIPConfiguration;
IPublicIPAddress publicIp = nicIPConfiguration.GetPublicIPAddress();
string dockerHostIP = publicIp.IPAddress;
DockerClient dockerClient = InstallDocker(dockerHostIP, vmUserName, vmPassword);
return dockerClient;
}
/**
* Install Docker on a given virtual machine and return a DockerClient.
* @param dockerHostIP - address (IP) of the Docker host machine
* @param vmUserName - user name to connect with to the Docker host machine
* @param vmPassword - password to connect with to the Docker host machine
* @return an instance of DockerClient
*/
public static DockerClient InstallDocker(string dockerHostIP, string vmUserName, string vmPassword)
{
int keyPfxBuffLength = 10000;
byte[] keyPfxContent = new byte[keyPfxBuffLength]; // it stores the content of the key.pfx certificate file
bool dockerHostTlsEnabled = false;
string dockerHostUrl = "tcp://" + dockerHostIP + ":2376";
SSHShell sshShell = null;
try
{
using (sshShell = SSHShell.Open(dockerHostIP, 22, vmUserName, vmPassword))
{
Utilities.Log("Copy Docker setup scripts to remote host: " + dockerHostIP);
sshShell.Upload(Encoding.ASCII.GetBytes(INSTALL_DOCKER_FOR_UBUNTU_SERVER_16_04_LTS),
"INSTALL_DOCKER_FOR_UBUNTU_SERVER_16_04_LTS.sh",
".azuredocker",
true);
sshShell.Upload(Encoding.ASCII.GetBytes(CREATE_OPENSSL_TLS_CERTS_FOR_UBUNTU.Replace("HOST_IP", dockerHostIP)),
"CREATE_OPENSSL_TLS_CERTS_FOR_UBUNTU.sh",
".azuredocker",
true);
sshShell.Upload(Encoding.ASCII.GetBytes(INSTALL_DOCKER_TLS_CERTS_FOR_UBUNTU),
"INSTALL_DOCKER_TLS_CERTS_FOR_UBUNTU.sh",
".azuredocker",
true);
sshShell.Upload(Encoding.ASCII.GetBytes(DEFAULT_DOCKERD_CONFIG_TLS_ENABLED),
"dockerd_tls.config",
".azuredocker",
true);
sshShell.Upload(Encoding.ASCII.GetBytes(CREATE_DEFAULT_DOCKERD_OPTS_TLS_ENABLED),
"CREATE_DEFAULT_DOCKERD_OPTS_TLS_ENABLED.sh",
".azuredocker",
true);
sshShell.Upload(Encoding.ASCII.GetBytes(DEFAULT_DOCKERD_CONFIG_TLS_DISABLED),
"dockerd_notls.config",
".azuredocker",
true);
sshShell.Upload(Encoding.ASCII.GetBytes(CREATE_DEFAULT_DOCKERD_OPTS_TLS_DISABLED),
"CREATE_DEFAULT_DOCKERD_OPTS_TLS_DISABLED.sh",
".azuredocker",
true);
Utilities.Log("Trying to install Docker host at: " + dockerHostIP);
string output = sshShell.ExecuteCommand("chmod +x ~/.azuredocker/INSTALL_DOCKER_FOR_UBUNTU_SERVER_16_04_LTS.sh");
Utilities.Log(output);
output = sshShell.ExecuteCommand("bash -c ~/.azuredocker/INSTALL_DOCKER_FOR_UBUNTU_SERVER_16_04_LTS.sh");
Utilities.Log(output);
Utilities.Log("Trying to create OPENSSL certificates");
output = sshShell.ExecuteCommand("chmod +x ~/.azuredocker/CREATE_OPENSSL_TLS_CERTS_FOR_UBUNTU.sh");
Utilities.Log(output);
output = sshShell.ExecuteCommand("bash -c ~/.azuredocker/CREATE_OPENSSL_TLS_CERTS_FOR_UBUNTU.sh");
Utilities.Log(output);
Utilities.Log("Trying to install TLS certificates");
output = sshShell.ExecuteCommand("chmod +x ~/.azuredocker/INSTALL_DOCKER_TLS_CERTS_FOR_UBUNTU.sh");
Utilities.Log(output);
output = sshShell.ExecuteCommand("bash -c ~/.azuredocker/INSTALL_DOCKER_TLS_CERTS_FOR_UBUNTU.sh");
Utilities.Log(output);
Utilities.Log("Download Docker client TLS certificates from: " + dockerHostIP);
sshShell.Download(keyPfxContent, keyPfxBuffLength, "key.pfx", ".azuredocker/tls", true);
Utilities.Log("Trying to setup Docker config: " + dockerHostIP);
//// Setup Docker daemon to allow connection from any Docker clients
//output = sshShell.ExecuteCommand("bash -c ~/.azuredocker/CREATE_DEFAULT_DOCKERD_OPTS_TLS_DISABLED.sh");
//Utilities.Log(output);
//string dockerHostPort = "2375"; // Default Docker port when secured connection is disabled
//dockerHostTlsEnabled = false;
// Setup Docker daemon to allow connection from authorized Docker clients only
output = sshShell.ExecuteCommand("chmod +x ~/.azuredocker/CREATE_DEFAULT_DOCKERD_OPTS_TLS_ENABLED.sh");
output = sshShell.ExecuteCommand("bash -c ~/.azuredocker/CREATE_DEFAULT_DOCKERD_OPTS_TLS_ENABLED.sh");
Utilities.Log(output);
string dockerHostPort = "2376"; // Default Docker port when secured connection is enabled
dockerHostTlsEnabled = true;
SdkContext.DelayProvider.Delay(10000);
dockerHostUrl = "tcp://" + dockerHostIP + ":" + dockerHostPort;
}
}
catch (Exception exception)
{
Utilities.Log(exception);
return null;
}
if (dockerHostTlsEnabled)
{
var credentials = new CertificateCredentials(new X509Certificate2(keyPfxContent));
credentials.ServerCertificateValidationCallback += (o, c, ch, er) => true;
return new DockerClientConfiguration(new Uri(dockerHostUrl), credentials).CreateClient();
}
else
{
return new DockerClientConfiguration(new Uri(dockerHostUrl)).CreateClient();
}
}
/**
* Installs Docker Engine and tools and adds current user to the docker group.
*/
public static string INSTALL_DOCKER_FOR_UBUNTU_SERVER_16_04_LTS = ""
+ "echo Running: \"if [ ! -d ~/.azuredocker/tls ]; then mkdir -p ~/.azuredocker/tls ; fi\" \n"
+ "if [ ! -d ~/.azuredocker/tls ]; then mkdir -p ~/.azuredocker/tls ; fi \n"
+ "echo Running: sudo apt-get update \n"
+ "sudo apt-get update \n"
+ "echo Running: sudo apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl software-properties-common \n"
+ "sudo apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl software-properties-common \n"
+ "echo Running: curl -fsSL https://apt.dockerproject.org/gpg | sudo apt-key add - \n"
+ "curl -fsSL https://apt.dockerproject.org/gpg | sudo apt-key add - \n"
+ "echo Running: sudo add-apt-repository \"deb https://apt.dockerproject.org/repo/ ubuntu-$(lsb_release -cs) main\" \n"
+ "sudo add-apt-repository \"deb https://apt.dockerproject.org/repo/ ubuntu-xenial main\" \n"
+ "echo Running: sudo apt-get update \n"
+ "sudo apt-get update \n"
+ "echo Running: sudo apt-get -y install docker-engine \n"
+ "sudo apt-get -y install docker-engine \n"
+ "echo Running: sudo groupadd docker \n"
+ "sudo groupadd docker \n"
+ "echo Running: sudo usermod -aG docker $USER \n"
+ "sudo usermod -aG docker $USER \n";
/**
* Linux bash script that creates the TLS certificates for a secured Docker connection.
*/
public static string CREATE_OPENSSL_TLS_CERTS_FOR_UBUNTU = ""
+ "echo Running: \"if [ ! -d ~/.azuredocker/tls ]; then rm -f -r ~/.azuredocker/tls ; fi\" \n"
+ "if [ ! -d ~/.azuredocker/tls ]; then rm -f -r ~/.azuredocker/tls ; fi \n"
+ "echo Running: mkdir -p ~/.azuredocker/tls \n"
+ "mkdir -p ~/.azuredocker/tls \n"
+ "echo Running: cd ~/.azuredocker/tls \n"
+ "cd ~/.azuredocker/tls \n"
// Generate CA certificate
+ "echo Running: openssl genrsa -passout pass:$CERT_CA_PWD_PARAM$ -aes256 -out ca-key.pem 2048 \n"
+ "openssl genrsa -passout pass:$CERT_CA_PWD_PARAM$ -aes256 -out ca-key.pem 2048 \n"
// Generate Server certificates
+ "echo Running: openssl req -passin pass:$CERT_CA_PWD_PARAM$ -subj '/CN=Docker Host CA/C=US' -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem \n"
+ "openssl req -passin pass:$CERT_CA_PWD_PARAM$ -subj '/CN=Docker Host CA/C=US' -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem \n"
+ "echo Running: openssl genrsa -out server-key.pem 2048 \n"
+ "openssl genrsa -out server-key.pem 2048 \n"
+ "echo Running: openssl req -subj '/CN=HOST_IP' -sha256 -new -key server-key.pem -out server.csr \n"
+ "openssl req -subj '/CN=HOST_IP' -sha256 -new -key server-key.pem -out server.csr \n"
+ "echo Running: \"echo subjectAltName = DNS:HOST_IP IP:127.0.0.1 > extfile.cnf \" \n"
+ "echo subjectAltName = DNS:HOST_IP IP:127.0.0.1 > extfile.cnf \n"
+ "echo Running: openssl x509 -req -passin pass:$CERT_CA_PWD_PARAM$ -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server.pem -extfile extfile.cnf \n"
+ "openssl x509 -req -passin pass:$CERT_CA_PWD_PARAM$ -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server.pem -extfile extfile.cnf \n"
// Generate Client certificates
+ "echo Running: openssl genrsa -passout pass:$CERT_CA_PWD_PARAM$ -out key.pem \n"
+ "openssl genrsa -passout pass:$CERT_CA_PWD_PARAM$ -out key.pem \n"
+ "echo Running: openssl req -passin pass:$CERT_CA_PWD_PARAM$ -subj '/CN=client' -new -key key.pem -out client.csr \n"
+ "openssl req -passin pass:$CERT_CA_PWD_PARAM$ -subj '/CN=client' -new -key key.pem -out client.csr \n"
+ "echo Running: \"echo extendedKeyUsage = clientAuth,serverAuth > extfile.cnf \" \n"
+ "echo extendedKeyUsage = clientAuth,serverAuth > extfile.cnf \n"
+ "echo Running: openssl x509 -req -passin pass:$CERT_CA_PWD_PARAM$ -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf \n"
+ "openssl x509 -req -passin pass:$CERT_CA_PWD_PARAM$ -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf \n"
// Generate .PFX key file to be used when connecting with the .Net Docker client
+ "echo Running: openssl pkcs12 -export -inkey key.pem -in cert.pem -out key.pfx -passout pass: -CAfile ca.pem \n"
+ "openssl pkcs12 -export -inkey key.pem -in cert.pem -out key.pfx -passout pass: -CAfile ca.pem \n"
+ "echo Running: cd ~ \n"
+ "cd ~ \n";
/**
* Bash script that sets up the TLS certificates to be used in a secured Docker configuration file; must be run on the Docker dockerHostUrl after the VM is provisioned.
*/
public static string INSTALL_DOCKER_TLS_CERTS_FOR_UBUNTU = ""
+ "echo \"if [ ! -d /etc/docker/tls ]; then sudo mkdir -p /etc/docker/tls ; fi\" \n"
+ "if [ ! -d /etc/docker/tls ]; then sudo mkdir -p /etc/docker/tls ; fi \n"
+ "echo sudo cp -f ~/.azuredocker/tls/ca.pem /etc/docker/tls/ca.pem \n"
+ "sudo cp -f ~/.azuredocker/tls/ca.pem /etc/docker/tls/ca.pem \n"
+ "echo sudo cp -f ~/.azuredocker/tls/server.pem /etc/docker/tls/server.pem \n"
+ "sudo cp -f ~/.azuredocker/tls/server.pem /etc/docker/tls/server.pem \n"
+ "echo sudo cp -f ~/.azuredocker/tls/server-key.pem /etc/docker/tls/server-key.pem \n"
+ "sudo cp -f ~/.azuredocker/tls/server-key.pem /etc/docker/tls/server-key.pem \n"
+ "echo sudo chmod -R 755 /etc/docker \n"
+ "sudo chmod -R 755 /etc/docker \n";
/**
* Docker daemon config file allowing connections from any Docker client.
*/
public static string DEFAULT_DOCKERD_CONFIG_TLS_ENABLED = ""
+ "[Service]\n"
+ "ExecStart=\n"
+ "ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/tls/ca.pem --tlscert=/etc/docker/tls/server.pem --tlskey=/etc/docker/tls/server-key.pem -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock\n";
/**
* Bash script that creates a default TLS secured Docker configuration file; must be run on the Docker dockerHostUrl after the VM is provisioned.
*/
public static string CREATE_DEFAULT_DOCKERD_OPTS_TLS_ENABLED = ""
+ "echo Running: sudo service docker stop \n"
+ "sudo service docker stop \n"
+ "echo \"if [ ! -d /etc/systemd/system/docker.service.d ]; then sudo mkdir -p /etc/systemd/system/docker.service.d ; fi\" \n"
+ "if [ ! -d /etc/systemd/system/docker.service.d ]; then sudo mkdir -p /etc/systemd/system/docker.service.d ; fi \n"
+ "echo sudo cp -f ~/.azuredocker/dockerd_tls.config /etc/systemd/system/docker.service.d/custom.conf \n"
+ "sudo cp -f ~/.azuredocker/dockerd_tls.config /etc/systemd/system/docker.service.d/custom.conf \n"
+ "echo Running: sudo systemctl daemon-reload \n"
+ "sudo systemctl daemon-reload \n"
+ "echo Running: sudo service docker start \n"
+ "sudo service docker start \n";
/**
* Docker daemon config file allowing connections from any Docker client.
*/
public static string DEFAULT_DOCKERD_CONFIG_TLS_DISABLED = ""
+ "[Service]\n"
+ "ExecStart=\n"
+ "ExecStart=/usr/bin/dockerd --tls=false -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock\n";
/**
* Bash script that creates a default unsecured Docker configuration file; must be run on the Docker dockerHostUrl after the VM is provisioned.
*/
public static string CREATE_DEFAULT_DOCKERD_OPTS_TLS_DISABLED = ""
+ "echo Running: sudo service docker stop\n"
+ "sudo service docker stop\n"
+ "echo \"if [ ! -d /etc/systemd/system/docker.service.d ]; then sudo mkdir -p /etc/systemd/system/docker.service.d ; fi\" \n"
+ "if [ ! -d /etc/systemd/system/docker.service.d ]; then sudo mkdir -p /etc/systemd/system/docker.service.d ; fi \n"
+ "echo sudo cp -f ~/.azuredocker/dockerd_notls.config /etc/systemd/system/docker.service.d/custom.conf \n"
+ "sudo cp -f ~/.azuredocker/dockerd_notls.config /etc/systemd/system/docker.service.d/custom.conf \n"
+ "echo Running: sudo systemctl daemon-reload \n"
+ "sudo systemctl daemon-reload \n"
+ "echo Running: sudo service docker start \n"
+ "sudo service docker start \n";
}
}