# Lab 10: Deployment
## nginx
As a first step to deploy an awesome web application, let's go back to the basics and host some static website via nginx first.
### Setup
Please follow the guide to setup nginx.
https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04
https://linuxconfig.org/how-to-setup-the-nginx-web-server-on-ubuntu-18-04-bionic-beaver-linux
```
sudo sudo apt install -y nginx-full
```
Start it via
```
systemctl start nginx
```
You can also restart/stop the service by using restart/stop instead of start.
Nginx uses `/var/www/html/` as default directory where static content is stored. You can add folders and files there to serve them via nginx on the internet.
### Port forwarding
As a next step, we want to combine static hosting with dynamic content hosted via gunicorn.
Step 1: use Gunicorn only (as in the lecture)
Step 2: We use nginx as reverse-proxy to cache static routes and let gunicorn all dynamic routes, i.e.

Copy this over `/etc/nginx/nginx.conf`
```
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
types_hash_max_size 2048;
server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
# gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component text/javascript text/xml;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
```
Copy this to `/etc/nginx/conf.d/virtual.conf`
```
server {
listen 80;
server_name cs6deployment;
location / {
proxy_pass http://localhost:8000;
}
}
```
Remove everything in `/etc/nginx/sites-available` and `/etc/nginx/sites-enabled`. You might want to move these elsewhere if you want to save their contents. Make sure your syntax is ok by running `sudo nginx -t`. Restart your `nginx` by running `sudo service nginx restart`.
## Domain Name
https://www.name.com/partner/github-students
Use this link to connect your Github with name.com. This will give you a free domain name for a year.
After getting the domain name, you need to modify the Manage DNS settings. Make an "A" document with a * character in the Host field. Put the IP address of your droplet in the Answer field.
If you need some help to come up with the next cool Web2.0 company name, there are also name generators <https://www.dotomator.com/web20.html>
# Docker
For the following exercises, please use the code available from <https://github.com/browncs6/FlaskExamples> `08_Docker` and `09_DockerComposer`.
A good starting point is to first try to run the containers from `08_Docker` by hand. For this either install docker locally, or run the code via docker on the department machines.
## Deployment using Docker composer
In the lecture you've seen that we use 2 docker containers to run 1. A flask application 2. a PostgreSQL database via the following commands (https://github.com/browncs6/FlaskExamples/tree/master/08_Docker).
Instead of manually starting and linking the containers, we can also use an orchestrator service like docker compose <https://docs.docker.com/compose/> to start both services at once.
To configure docker composer, a YAML(<https://yaml.org/>, <https://gettaurus.org/docs/YAMLTutorial/>) file `docker-compose.yml` is used.
```
version: '3'
services:
login:
build: .
ports:
- "8000:5000"
env_file: .env
links:
- postgres:dbserver
volumes:
- ${DATA_DIR}:/var/lib/postgresql/data
restart: always
postgres:
image: "postgres:12.1"
env_file: .env-postgres
restart: always
```
To pass credentials, instead of defining Environment variables we can also use an environment file (alternative: use `environment` in the composer file).
```
#.env
FLASK_APP=login.py
APP_SECRET=`openssl rand -base64 32`
DBURI=postgresql://postgres:docker@dbserver/postgres
```
```
#.env-postgres
POSTGRES_PASSWORD=docker
```
Tip: gitignore the `.env` files!
To start the containers via docker composer use
```
mkdir -p db-data && export DATA_DIR=$PWD/db-data && docker-compose up --build
```
If you want to start it in detached mode, use `-d`.
To stop, either use `Ctrl + C` or `docker-compose down`.
1. Take a look at <https://hub.docker.com/_/postgres> and exchange the passwords for postgres to something else than the defaults.
2. Write a mini Flask app which uses MongoDB instead of postgresql and dockerize it as well!
## Deploying your Flask application on a digitalocean droplet
Create a new droplet (or reuse your existing one) and access via your ssh key, e.g.
```
ssh -i ~/.ssh/do.pem root@142.93.205.102
```
Install docker
```
sudo apt-get update &&
sudo apt install -y docker docker.io &&
sudo apt install -y docker-compose
```
Start docker service via `systemctl`
```
sudo systemctl start docker &&
sudo systemctl enable docker
```
Docker should be now available, you can check via `docker --version`.
### Transferring data to the droplet
There are multiple ways how you can transfer your images to a droplet
1. Have your code & docker file there, build images or invoke `docker-composer` with `--build`
2. Save your images via `docker save`, transfer to droplet and load via `docker load`
3. push your image to dockerhub and fetch from there via `docker pull`
### Deploying to the web via docker
We'll be using 2. and 3.
Step 1: Let's save the login example to a tar file for transfer to the droplet. Note: Running containers as root is dangerous, read <https://engineering.bitnami.com/articles/why-non-root-containers-are-important-for-security.html> how to do this better. For sake of time, we'll skip that part.
Save via (note the piping through gzip to compress everything)
`docker save login:latest | gzip > login-image.tar.gz`
Step 2: Create a data dir, via `mkdir /db-data`
Step 3: copy the saved image to the droplet via
```
scp -i ~/.ssh/do.pem login-image.tar.gz root@142.93.205.102:/root
```
Step 4: On the droplet, load the image and pull the postgres image
```
docker pull postgres:12.1
docker load < login-image.tar.gz
```
When you run `docker images` after this, you should see both the `login` as well as the `postgres` image listed.
Step 5: Because connecting containers is boring, let's use docker-composer to run everything.
For this, copy the following `docker-compose.yml` file to your droplet and the .env files. Note that here we expose for production the Flask port fo 5000 to the worlds port 80 (standard HTTP one!).
```
version: '3'
services:
login:
image: "login:latest"
ports:
- "80:5000"
env_file: .env
links:
- postgres:dbserver
restart: always
postgres:
image: "postgres:12.1"
env_file: .env-postgres
restart: always
volumes:
- ${DATA_DIR}:/var/lib/postgresql/data
```
Then start the services via docker-composer (in detached mode)
```
export DATA_DIR=/db-data && docker-compose up -d
```
*Note:* If you want, you can also hardcode in your composer file the data volume for the database. This just demonstrates how env variables can be also used with the composer file.
You can now exit she SSH shell via `exit` and you website should be reachable under the IP of your droplet. Use the guide above to configure a domain.
**Beefing it up:** Write a shell script `deploy.sh` to automate your deployment process! You know all the tools to do that!