DATASCI 350 - Data Science Computing

Lecture 17 - Cloud Computing II

Danilo Freire

Department of Data and Decision Sciences
Emory University

Hello, everyone! 👋

Brief recap 📚

Where we left off

You now have an AWS account

Cartoon by Forrest Brazeal

The final project

What you will build

  • Pull data from a web API with requests
  • Process it with pandas or DuckDB
  • Save a snapshot in data/raw/. The report reads only that snapshot
  • Quarto report: 1,500 to 2,500 words, two or more visualisations, visible code
  • Ship it in a Docker container, with the code on GitHub
  • Track A (default): the World Bank Indicators API, no key needed
  • Track B: a free, well-documented public API without OAuth, cleared with me first
  • Groups of four to five. Email me the names by Thursday 5 November, or I assign you at random

The final project

How I grade it

  • The project is worth 20% of your course grade
Component Weight
Reproducibility 30%
Analysis quality 30%
Communication 20%
Code quality 10%
Git workflow 10%
  • Sophistication is not graded. Careful descriptive work beats a regression you cannot explain
  • Every member commits to the shared repository, and I read the commit history
  • No meaningful contribution means a zero for the project
  • Clearly unequal shares change individual grades
  • The report ends with a contribution statement, which I check against the repository

If docker run on my machine does not reproduce your report, your project does not exist 😅

Today’s plan

  • Launch an EC2 instance from the AWS console
  • Connect to it with SSH and a key pair
  • Windows users: keep the key in WSL, where chmod works
  • Install software with apt and copy files with scp
  • Stop and terminate instances, so the bill stays at zero
  • CloudShell: a terminal in the browser, with the aws command line
  • Activity 01: run Jupyter on the instance
  • Activity 02: analyse data in the cloud (time permitting)

EC2 from your local machine 🖥️

EC2 instances in a nutshell

  • EC2 is a virtual server in the cloud, usually running Ubuntu or Amazon Linux
  • Virtualisation runs many instances on one physical machine
  • Instance types differ in CPU, memory, storage, and network
    • T3 and M7i are general purpose
    • C7i is compute-optimised, and R7i is memory-optimised
  • Six types are free tier eligible. We use t3.micro

Source: DataCamp

Ubuntu Linux

  • Linux is an open-source operating system
  • It runs most of the servers on the internet
  • It is free to install, and you can change any part of it
  • Ubuntu is the most widely used Linux distribution
  • Google, Amazon, and Microsoft all offer Ubuntu instances
  • WSL runs Linux inside Windows, Ubuntu by default
  • Ubuntu uses bash as its default login shell
  • So the commands from the shell module work on Ubuntu

Ubuntu server

  • Ubuntu has a GUI, but we will use only the terminal
  • A bare-bones instance is cheaper and faster than a full desktop
  • The terminal has everything you need to manage it
  • The commands are the same: ls, cd, pwd, and so on
  • sudo runs a command as root, the superuser
  • Root has full control over the system, so be careful with it!
  • apt (Advanced Package Tool) installs software
    • apt update refreshes the package list
    • apt install installs a package
  • sudo apt install python3 installs Python
  • sudo apt install python3-pip installs pip

WSL and your SSH key

Move the key into your Linux home first

  • Your browser saves the key to Windows Downloads, which WSL sees as /mnt/c/Users/<your-windows-name>/Downloads/
  • Files on C: ignore chmod and always look open to everyone
  • SSH then refuses the key: WARNING: UNPROTECTED PRIVATE KEY FILE!
  • Fix: copy the key to your Linux home and chmod it there
  • Not sure of your Windows user name? Run ls /mnt/c/Users/
  • Windows Explorer shows your Linux files under Linux in the left panel
  • Open WSL: type Ubuntu in the Start menu
  • WSL has two separate filesystems:
    • Linux home: /home/<your-linux-name>, or ~
    • Windows C: drive: /mnt/c/
# 1. Copy the key from Downloads to your Linux home
cp /mnt/c/Users/<your-windows-name>/Downloads/datasci350.pem ~/

# 2. Make it readable only by you
chmod 400 ~/datasci350.pem

# 3. Check: the line should start with -r--------
ls -l ~/datasci350.pem

# 4. Connect as usual
ssh -i ~/datasci350.pem ubuntu@<your-public-dns>

Still stuck? If SSH refuses your key after these steps, connect from CloudShell instead. It runs Linux in your browser, so chmod works. Steps in the appendix

Step 01: Launching an EC2 instance

  • Now we can launch an EC2 instance!
  • Sign in to the AWS Console and search for EC2
  • On the EC2 Dashboard, click the orange Launch instance button
  • Choose an AMI (Amazon Machine Image). We pick Ubuntu, which is free tier eligible
  • AWS has thousands of AMIs: Windows, macOS, and other Linux distributions
  • Many come ready to run specific software, such as Jupyter, TensorFlow, or Docker

Step 02: Choosing an instance type

  • Type a name under Name and tags, such as datasci350
  • Under Application and OS Images, choose Ubuntu
  • The current release is Ubuntu Server 26.04 LTS, marked Free tier eligible
  • Keep 64-bit (x86) as the architecture
  • Launch 1 instance for now
  • The Summary panel on the right lists every choice before you launch
  • The next slide covers the instance type
  • So far, so good? 😊

The launch wizard. Click the image to read the Summary panel

Which instance type is free?

  • Choose t3.micro, labelled Free tier eligible
  • Accounts opened after 15 July 2025 can use six types: t3.micro, t3.small, t4g.micro, t4g.small, c7i-flex.large and m7i-flex.large
  • The dropdown shows the price of every type
  • A t3.micro has 2 vCPUs and 1 GiB of memory, and costs 0.0104 USD an hour on Linux
  • You can change the instance type later

Ignore the blue Free tier note in Summary. It describes the old t2.micro offer (750 hours a month for a year), closed to new accounts on 15 July 2025. Your account runs on credits instead

The instance type dropdown, with the hourly price under every entry

Step 03: Choose an SSH key pair

  • Next, choose a key pair
  • A key pair is two cryptographic keys that authenticate you to an instance
  • You create it once and use it for all your instances
  • Click Create a new key pair and name it, e.g. datasci350
  • Choose ED25519 and the .pem file format
  • Click Create key pair and save the file somewhere safe
  • You need it to log in over SSH, and AWS cannot send you another copy

Step 04: Check HTTP/HTTPS options

  • Under Network settings, tick Allow HTTPS traffic and Allow HTTP traffic from the internet
  • This lets you reach web servers on your instance
  • SSH (port 22) is open by default
  • We open these ports to anyone for now
  • In production, allow only what you need
  • Why? Security! Every open port is a way in for attackers
  • You can edit the security group later

Step 05: Configure storage

  • Under Configure storage, you can resize the Root volume
  • The default 8 GB is enough for today. A larger disk spends more credits
  • You can add more volumes if you need them
  • gp3 is the default type and works for most uses
  • io2 is the fastest and most expensive, for databases that need sub-millisecond latency
  • sc1 and st1 are cheaper hard-disk types for rarely used data. They cannot be the root disk

  • Now you just have to click on Launch instance! 🚀

Step 06: Log in to the EC2 instance through SSH

  • After you click Launch instance, open the Instances page
  • Click Connect to instance to see the instructions

  • Choose the SSH client tab
  • Open a terminal in the folder where you saved the key
  • The console writes both commands for you under How to connect:
    • Step 3: chmod 400 "your-key.pem"
    • Step 4: ssh -i "your-key.pem" ubuntu@your-public-dns

And you are in! 🎉

Welcome to the cloud! 🌥️

  • Type yes to confirm the host fingerprint. SSH asks only once
  • The banner shows what you rented: Ubuntu 26.04 LTS, a 6.61 GB disk (30% full), 118 processes, no other users
  • The prompt changes to ubuntu@ip-172-31-13-146
  • Every command now runs on a computer in Virginia
  • exit returns you to your own machine

Check the instance details on the AWS Console

  • Click the instance ID, or Instances on the left
  • You can see its public IP address, instance type, security group, and key pair
  • You can also stop, terminate, or reboot it here (more on this later)

AWS CloudShell

A terminal in the browser, already signed in

  • Open CloudShell from the bottom left of the console. Nothing to install
  • Amazon Linux 2023: 1 vCPU, 2 GiB of RAM, 1 GB of saved storage
  • Free: you pay only for the resources you use from it
  • The AWS CLI is already signed in as you
  • Also there: git, python3, pip, boto3, jq, wget, ssh, vim, nano, tmux
  • It runs bash, so your usual commands work
  • A session closes after 20 to 30 minutes without typing, even if a program is still running
  • Any session closes after 12 hours, idle or not
  • Unused home directories are deleted after 120 days

The panel you meet on first launch, stating the tools and the 1 GB of storage per region

  • I recommend your local terminal and editor. Use CloudShell as a fallback

The aws command

Every command has the same shape: aws <service> <command> [options]

Part Example Meaning
Tool aws The AWS command line
Service ec2 Virtual servers
Command describe-instances List your instances
Option --output table Print a table instead of JSON
  • Other services: s3, iam, lambda, sts
  • Most commands are verb-noun: stop-instances
  • aws s3 uses shell-like ls, cp, rm
  • --region picks the data centre. us-east-1 is Virginia:
aws ec2 describe-instances --region us-east-1

Finding commands

aws help                     # every service
aws ec2 help                 # every EC2 command
aws ec2 stop-instances help  # one command

Useful commands

aws --version             # CLI version
aws configure list        # region and keys
aws ec2 describe-regions --output table
aws ec2 start-instances --instance-ids i-...
aws ec2 terminate-instances --instance-ids i-...
aws s3 mb s3://my-bucket  # make a bucket

Try it in CloudShell

Try it in class if there is time. Otherwise, try it at home

# 1. Who am I signed in as?
aws sts get-caller-identity

# 2. List your instances. Find InstanceId and State
aws ec2 describe-instances --output table

# 3. Stop yours, then run step 2 again
aws ec2 stop-instances --instance-ids i-...

# 4. Start it again
aws ec2 start-instances --instance-ids i-...

# 5. At home and done for the day? Terminate it
aws ec2 terminate-instances --instance-ids i-...
  • The state goes from stopping to stopped in about a minute
  • A restarted instance gets a new public DNS, so update your ssh command
  • CloudShell runs Amazon Linux: dnf replaces apt, and what it installs is gone next session

Steps 1 and 2: one instance running, one terminated. More commands

Step 07: Update and install software

  • First, refresh the package list: sudo apt update
  • Then install the latest updates: sudo apt upgrade
    • -y answers yes to every prompt: sudo apt update && sudo apt upgrade -y
  • Install software with sudo apt install
    • Python 3: sudo apt install python3
    • pip: sudo apt install python3-pip
  • You can install Jupyter and anything else the same way
    • Jupyter: sudo apt install jupyter-notebook

sudo apt update && sudo apt upgrade -y && sudo apt install -y python3 python3-pip

pip install gives an externally-managed-environment error on Ubuntu. Use sudo apt install python3-<package> or a virtual environment

Adding files to your instance

  • scp (“secure copy”) moves files over SSH
  • -i points to your key (the “identity file”)
  • First path is the source, second is the destination
# laptop to instance
scp -i key.pem myfile ubuntu@public-dns:~

# instance to laptop
scp -i key.pem ubuntu@public-dns:~/myfile .
  • . means “the folder I am in now”
  • scp also copies between two remote servers
  • Windows: WinSCP and PuTTY do it with drag and drop
  • Run scp in a new local terminal, not on the instance:
echo 'print("Hello, DATASCI350!")' > hello.py
scp -i datasci350.pem hello.py ubuntu@XXXXXX.compute-1.amazonaws.com:~

  • Replace XXXXXX with your public DNS (Instances page)
  • Keep the :~: it means the home directory
  • On the instance, ls shows the file and python3 hello.py runs it

Stopping and terminating your instance

  • Stop or terminate idle instances
  • Stop pauses it: no compute charge, but the disk still bills
  • Terminate deletes it and all its files
  • A restart brings a new IP address, so update your ssh command
  • A t3.micro left on for a month: about $12 ($7.60 compute, $3.65 IP, $0.64 disk)
  • Stop, terminate or reboot on the Instances page
  • Let’s terminate ours now! 🛑

The Instance state menu. Terminate (delete) instance is last

Both instances Terminated, with no public DNS left

Now it is your turn! 🚀

Activity 01

  1. Launch an EC2 instance called jupyter
  2. Connect with port forwarding:
    • ssh -i "<your-key>.pem" ubuntu@<public-DNS> -L 8000:localhost:8888
    • Port 8888 on the instance becomes port 8000 on your laptop
  3. Install Python, pip, and Jupyter:
    • sudo apt update && sudo apt upgrade -y
    • sudo apt install -y python3 python3-pip jupyter-notebook
    • Note the hyphen in jupyter-notebook. python3-notebook lacks the jupyter command
  4. Check: which python3, which pip3, which jupyter
  1. Start Jupyter: jupyter notebook
  2. Open http://localhost:8000 and paste the token from the terminal
    • Or click the terminal link and change 8888 to 8000
  3. Create a notebook and run print('Hello, DATASCI350!')
  4. Do not terminate the instance. Activity 02 uses it

Closing SSH stops Jupyter. tmux keeps it running after you disconnect, and tmux attach brings it back. The instance bills while it runs. More on tmux

Activity 01

Activity 01

Another one? 🚀

Activity 02

  • Analyse data on the instance, with two ways to get files onto it:
    • Method 1, scp: upload from your computer. For files that are not online, like your own data
    • Method 2, wget: download from the internet (e.g. GitHub) straight to the instance
  • First, install the packages on the instance:
    • sudo apt install -y python3-numpy python3-pandas python3-matplotlib python3-seaborn
  • Method 1: scp. On your local machine, create the dataset with this code, or download weather_data.py
# weather_data.py
import pandas as pd
import numpy as np
import datetime

# Set seed for reproducibility
np.random.seed(42)

# Generate dates for the past 30 days
dates = pd.date_range(end=datetime.datetime.now(), periods=30).tolist()
dates = [d.strftime('%Y-%m-%d') for d in dates]

# Generate temperature data with some randomness
temp_high = np.random.normal(75, 8, 30)
temp_low = temp_high - np.random.uniform(10, 20, 30)
precipitation = np.random.exponential(0.5, 30)
humidity = np.random.normal(65, 10, 30)

# Create a structured dataset
weather_data = pd.DataFrame({
    'date': dates,
    'temp_high': temp_high,
    'temp_low': temp_low,
    'precipitation': precipitation,
    'humidity': humidity
})

# Save to a text file
with open('weather_data.txt', 'w') as f:
    f.write("# Weather data for the past 30 days\n")
    f.write(weather_data.to_string(index=False))
    
print("Weather data saved to weather_data.txt")

Activity 02

  • Run it on your local machine: python3 weather_data.py

  • It creates weather_data.txt, 30 days of weather data

  • Upload it from a local terminal:

    • scp -i <your-key>.pem weather_data.txt ubuntu@<public-DNS>:~/
  • Check with ls on the instance

  • Method 2: wget. On the instance, download the analysis script:

  • wget https://raw.githubusercontent.com/danilofreire/datasci350/main/lectures/lecture-17/weather_analysis.py (one line)
  • The code below is for reference only. wget already downloaded it
# weather_analysis.py
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from io import StringIO

# Read the weather data
with open('weather_data.txt', 'r') as f:
    lines = f.readlines()

# Skip the header comment
data_str = ''.join(lines[1:])
df = pd.read_csv(StringIO(data_str), sep=r'\s+')

# Print basic statistics
print("Weather Data Analysis:")
print("=====================")
print(f"Number of days: {len(df)}")
print(f"Average high temperature: {df['temp_high'].mean():.1f}°F")
print(f"Average low temperature: {df['temp_low'].mean():.1f}°F")
print(f"Maximum temperature: {df['temp_high'].max():.1f}°F on {df.loc[df['temp_high'].idxmax(), 'date']}")
print(f"Minimum temperature: {df['temp_low'].min():.1f}°F on {df.loc[df['temp_low'].idxmin(), 'date']}")
print(f"Days with precipitation > 1 inch: {len(df[df['precipitation'] > 1])}")

# Create a visualisation
sns.set_style("whitegrid")
fig, ax1 = plt.subplots(figsize=(12, 6))

# Plot temperature range on the left axis
ax1.fill_between(df['date'], df['temp_low'], df['temp_high'], alpha=0.3, color='skyblue')
ax1.plot(df['date'], df['temp_high'], marker='o', color='red', label='High Temp')
ax1.plot(df['date'], df['temp_low'], marker='o', color='blue', label='Low Temp')
ax1.set_ylabel('Temperature (°F)')

# Add precipitation as bars on a second axis, on the right
ax2 = ax1.twinx()
ax2.bar(df['date'], df['precipitation'], alpha=0.3, color='navy', width=0.5, label='Precipitation')
ax2.set_ylabel('Precipitation (inches)', color='navy')
ax2.tick_params(axis='y', labelcolor='navy')
ax2.grid(False)

# One legend for both axes, and every fifth date on the x-axis
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
ax1.set_xticks(df['date'][::5])
ax1.tick_params(axis='x', labelrotation=45)
ax1.set_title('30-Day Weather Report: Temperature Range and Precipitation', fontsize=16)
fig.tight_layout()

# Save the figure
fig.savefig('weather_analysis.png')
print("Analysis complete. Results saved to 'weather_analysis.png'")

Activity 02

  • The instance now has weather_data.txt (from scp) and weather_analysis.py (from wget)
  • Run the analysis: python3 weather_analysis.py, or run it in Jupyter
  • From a local terminal, download the image:
    • scp -i <your-key>.pem ubuntu@<public-DNS>:~/weather_analysis.png ./
  • Open the image on your laptop
  • Don’t forget to terminate your instance when done!

Recap: scp moves files between your computer and the instance. wget downloads from the internet to the instance

Activity 02 Result

When you run the activity, you should get a graph like this one:

Activity 02 Result

You’ve just completed a full data analysis workflow in the cloud 🎉

  1. Created data locally
  2. Uploaded it to the cloud
  3. Processed it on a cloud server
  4. Generated visualisations
  5. Downloaded results to your local machine

Data scientists use this same workflow for larger datasets and more complex analyses!

Conclusion

Summary

What we learned today

  • We launched an EC2 instance and connected over SSH with a key pair
  • We installed software with apt and ran Jupyter through a forwarded port
  • We moved files with scp and wget
  • We met CloudShell and the aws command line
  • We learnt to stop and terminate instances, to keep the bill at zero
  • Lambda runs a function with no server to manage. It is the easiest service to try next
  • S3, RDS, and SageMaker cover files, databases, and machine learning
  • The same workflow works for data too big for a laptop: upload it, rent a machine, run the code, and download the results

Next class

Getting data from the web

  • We leave the cloud: how web data reaches your analysis
  • APIs: a client asks, a server answers
  • HTTP: URLs, query strings, GET, POST, status codes
  • JSON: the format most APIs use, read as a Python dictionary
  • requests: does all this in three lines of Python
  • You have used an API already: the key in your .env file in Lecture 14

Before then:

  1. Terminate every instance from today
  2. Check that Billing and Cost Management shows zero
  3. Read the final project instructions

The final project is out: groups of four to five, due 8 December 2026. Pull data from a web API (the World Bank by default, or one you clear with me) and submit it as a Docker container. Start from the starter repository

And that’s all for today! 🎉

Appendix

CloudShell as a way back in

When your own terminal will not cooperate

  • The WSL problem: Windows ignores chmod 400 on files in the Windows filesystem, so SSH refuses the key
  • CloudShell avoids this, because the key goes to a Linux machine:
  1. Open CloudShell from the console.
  2. Choose Actions, then Upload file.
  3. Select your .pem file. It arrives in /home/cloudshell-user.
  4. Run chmod 400 your-key.pem.
  5. Run the same ssh -i ... command the console gave you.
  • The instance does not care that the connection came from a browser tab

The whole thing from a browser tab: the upload confirmation, chmod 400, and an Ubuntu 26.04 prompt on the instance

  • CloudShell times out after 20 to 30 idle minutes, and your SSH connection goes with it