# Cloud-init

## Setting a cloud-init script

To set a cloud-init script, click on the "Click for more advanced options" green link when deploying your virtual machine.

<figure><img src="https://276866638-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FScYFYZoiZXazILi6lxJ5%2Fuploads%2FSOwg2X0mvCAVjmlEghzj%2Fimage.png?alt=media&#x26;token=d7ca9eae-d529-4b90-833b-81864bfeb31d" alt=""><figcaption><p>Click on the "Click for more advanced options" green link to show this textarea</p></figcaption></figure>

This is the default cloudinit configuration we pass to the hypervisor:&#x20;

```bash
#cloud-config
user: user
password: [your defined password]
chpasswd: {expire: False}
ssh_pwauth: True
package_update: True
package_upgrade: False
```

The script that you provide simply gets appended to the end of this file.&#x20;

## Examples

### Example 1: Preventing the automatic update of NVIDIA drivers

{% hint style="info" %}
NVIDIA drivers autoupdate on the first boot. Once this happens, they become unusable. By including this cloud-init script, you'll lock the NVIDIA driver versions so that they do not autoupdate&#x20;
{% endhint %}

#### Objectives

* Write a file using cloud-init `write_files` in base64 encoding that uses `apt-mark` to hold NVIDIA packages from being automatically updated
  * The file will have the following contents: \
    `dpkg-query -W --showformat='${Package} ${Status}\n' | grep -v deinstall | awk '{ print $1 }' | grep -E 'nvidia.*-[0-9]+$' | xargs -r -L 1 apt-mark hold`
* Run a file

{% embed url="<https://forums.developer.nvidia.com/t/nvidia-smi-has-failed-because-it-couldnt-communicate-with-the-nvidia-driver-make-sure-that-the-latest-nvidia-driver-is-installed-and-running/197141/2?u=jonathan62>" %}
This is the source of the script included
{% endembed %}

#### Process

&#x20;We'll set this as our cloud-init script:

```bash
write_files:
  - encoding: b64
    path: /home/user/tensordock_scripts/prevent_update.sh
    permissions: '0644'
    content: ZHBrZy1xdWVyeSAtVyAtLXNob3dmb3JtYXQ9JyR7UGFja2FnZX0gJHtTdGF0dXN9XG4nIHwgZ3JlcCAtdiBkZWluc3RhbGwgfCBhd2sgJ3sgcHJpbnQgJDEgfScgfCBncmVwIC1FICdudmlkaWEuKi1bMC05XSskJyB8IHhhcmdzIC1yIC1MIDEgc3VkbyBhcHQtbWFyayBob2xk
    owner: user:user
runcmd:
  - bash /home/user/tensordock_scripts/prevent_update.sh
```

#### Debrief

Explanation:

* First, our server will write the new file, `prevent_update.sh`, in the user's new `tensordock_scripts` directory
* Then, our server run this file, locking in our NVIDIA driver versions to prevent autoupdates

<figure><img src="https://276866638-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FScYFYZoiZXazILi6lxJ5%2Fuploads%2FbewAvK3mYR2edCmaMVvT%2Fimage%20(27).png?alt=media&#x26;token=a8417f97-4f00-495d-8831-ec6fb2f50f98" alt=""><figcaption></figcaption></figure>

### Example 2: Hosting a website with Apache2

#### Objectives

* Install a package through cloud-init `packages`
* Write a file using cloud-init `write_files` in plaintext

#### Process

By default, the Apache2 webserver listens on port 80. As such, we'll first forward an external port into that internal port.&#x20;

<figure><img src="https://276866638-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FScYFYZoiZXazILi6lxJ5%2Fuploads%2Fen9uJtwIi4y7NyYt1h4a%2Fimage.png?alt=media&#x26;token=f5e12b6b-a66f-4a5d-836d-b6e7831b81c7" alt=""><figcaption><p>Because Apache listens on port 80, you'll want to forward an external port into that internal port</p></figcaption></figure>

Then, we'll set this as our cloud-init script:

```bash
packages:
  - apache2
write_files:
  - path: /var/www/html/index.html
    permissions: '0644'
    content: |
      <html>
      <head>
        <title> TensorDock Marketplace VM </title>
      </head>
      <body style="font-family: 'Courier New', monospace;">
        <p> You've reached your <span style="color: green;">TensorDock</span> virtual machine!</p>
      </body>
      </html>
    owner: www-data:www-data
```

{% hint style="warning" %}
Note the indentation of the custom HTML we've defined above! When writing files, we must append four (4) spaces in front of each text block.
{% endhint %}

#### Debrief

Explanation:

* First, our server will install the `apache2` package
* Then, our server will overwrite the we create an index.html file with the custom HTML we've defined

Now, let's try accessing our web server!

<figure><img src="https://276866638-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FScYFYZoiZXazILi6lxJ5%2Fuploads%2FB6EwahHIqfbPnjECGp1y%2Fimage.png?alt=media&#x26;token=43a24c9c-16ee-4f50-9299-3b06d7e3f9b9" alt=""><figcaption><p>Woohoo! It works!</p></figcaption></figure>

### Example 3: Hosting a simple Docker container

#### Objectives

* Host a simple Docker container through the Docker CLI

{% hint style="success" %}
TensorDock operating system templates come preinstalled with Docker
{% endhint %}

#### Process

For networking, we forward an external port into an internal port in the virtual machine. Then, a Docker container forwards a virtual machine's port into its own internal network.&#x20;

By default, the NGINX webserver listens on port 80 within the Docker container, but we can have the Docker container listen on any port. As such, we'll first forward an external port (e.g. 20018) into itself.&#x20;

<figure><img src="https://276866638-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FScYFYZoiZXazILi6lxJ5%2Fuploads%2FrBxt0bEke0HaQTgQBO95%2Fimage.png?alt=media&#x26;token=c4030f2a-395c-42c8-9b4d-5363a019803d" alt=""><figcaption><p>We'll forward an external port into an internal port on the virtual machine</p></figcaption></figure>

Then, we'll set this as our cloud-init script:

```bash
write_files:
  - path: /home/user/cloudinit_website/index.html
    permissions: '0644'
    content: |
      <html>
      <head>
        <title> TensorDock Marketplace VM </title>
      </head>
      <body style="font-family: 'Courier New', monospace;">
        <p> You've reached your <span style="color: green;">TensorDock</span> virtual machine!</p>
      </body>
      </html>
    owner: www-data:www-data
runcmd:
  - docker run -d --restart unless-stopped --stop-timeout 300 -v /home/user/cloudinit_website:/usr/share/nginx/html:ro -p 20018:80 --name default_container nginx
```

{% hint style="warning" %}
Note that we have our Docker container listen on port 20018 because we forwarded external port 20018 into the virtual machine on port 20018
{% endhint %}

#### Debrief

Explanation:

* First, our server will write a file to a new directory containing our custom HTML
* Then, our server will run a Docker container that passes through the directory of our HTML page into the NGINX contiainer
* When users access the external port 20018, our hypervisor forwards that to the virtual machine's port 20018, and then Docker forwards that to the NGINX container on port 80

Now, let's try accessing our web server!

<figure><img src="https://276866638-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FScYFYZoiZXazILi6lxJ5%2Fuploads%2FjjfO9i9i4sUlpzdz0sGc%2Fimage.png?alt=media&#x26;token=2a0376f9-9018-45ac-82c0-a1f7cf105fed" alt=""><figcaption><p>Woohoo! It works!</p></figcaption></figure>
