# DPTO. Despliegue web en DO con ansible
## Propuesta para despliegue de aplicaciones web para el curso 2021/22
### Características generales
* Cada alumno tiene un contenedor `lxc` en el droplet con Ubuntu Server/Alpine. De esa forma nos evitamos tener que crear un droplet para cada alumno y ahorramos costes. Cada contenedor tendrá una IP privada.
* El contenedor tiene instalado ssh, php, nginx y mysql/mariadb, node, ...
* En el servidor de DNS se le asigna a cada alumno un subdominio apuntndo al contenedor con nombre del tipo: `nombrealumno.daw1.infoalb.org`. **Todos** los nombres apuntan a la **IP pública** del droplet.
* A cada contenedor se le redirecciona con `iptables` un puerto en el droplet que le redirecciona por ssh al contenedor.
* En el droplet se crea un proxy inverso que redirecciona por nombre de dominio las peticiones http y https al contendor del alumno.
* El alumno exporta su clave pública al contenedor para poder acceder por ssh al mismo sin contraseña.
* El alumno tiene instalado ansible en el equipo desde el que desarrolla.
* El alumno crea, en su equipo, para cada uno de sus proyectos un **playbook** de Ansible que al ejecutarlo hace que se despliegue de forma automática su aplicación.
* Para el alumnado de 2º de SMR se pueden usar de forma similar los contenedores para las prácticas de **SOR** y **SRC** en Linux.
### Tutoriales
#### 1. Instalación de lxd en DO
* [Instalación de lxd en DO](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-lxd-on-ubuntu-20-04)
#### 2. Redirección de puerto público
A cada alumno se le da un puerto público para acceder por ssh a su Droplet.
```bash
PUBLIC_PORT=20001 PORT=22 PUBLIC_IP=your_server_ip CONTAINER_IP=your_container_ip IFACE=eth0 sudo -E bash -c 'iptables -t nat -I PREROUTING -i $IFACE -p TCP -d $PUBLIC_IP --dport $PUBLIC_PORT -j DNAT --to-destination $CONTAINER_IP:$PORT -m comment --comment "forward to ssh server in container"'
```
El alumno para conectar ejecuta:
```bash
$ ssh usuario@infoalb.org -p 20001
```
#### 3. Configuración de haproxy como proxy inverso para reenviar peticiones http o https (Lets Encrypt) al host virtual del alumno
Ejemplo de **haproxy.cfg**
```bash=
frontend www-https
bind *:80
bind *:443 ssl crt /etc/ssl/example.com/example.com.pem
# Redirect HTTP to HTTPS
redirect scheme https code 301 if !{ ssl_fc }
#Lets Encrypt Renewal URI Test
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt-backend if letsencrypt-acl
mode http
use_backend example1 if { hdr(host) -i example1.deanlongstaff.com }
use_backend example2 if { hdr(host) -i example2.deanlongstaff.com }
use_backend example3 if { hdr(host) -i example3.deanlongstaff.com }
#Backend to use if no URL specified
default_backend example1
backend example1
server server1 192.168.40.2:80
backend example2
server example2 192.168.40.10:80
backend example3
server example3 192.168.40.30:443 check ssl verify none
# Lets Encrypt Backend
backend letsencrypt-backend
server letsencrypt 127.0.0.1:8888
```
* [Tutorial](https://deanlongstaff.com/haproxy-ubuntu/)
#### 4. Playbook de ansible para instalar base de datos
Fichero con definición de playbook para instalar mysql en servidor y crear base de datos
```yaml
Fichero: db-server-playbook.yaml
1 ---
2 - hosts: nombrealumno.daw1.infoalb.org
3
4 vars:
5 mysql_root_password: password
6
7 tasks:
8 - name: install mysql
9 apt: name=mysql update_cache=yes cache_valid_time=3600 state=present
10 - name: start up the mysql service
11 shell: "service mysql start"
12 - name: ensure mysql is enabled to run on startup
13 service: name=mysql state=started enabled=true
14 - name: update mysql root password for all root accounts
15 mysql_user:
16 name: root
17 host: "{{ item }}"
18 password: "{{ mysql_root_password }}"
19 login_user: root
20 login_password: "{{ mysql_root_password }}"
21 check_implicit_admin: yes
22 priv: "*.*:ALL,GRANT"
23 with_items:
24 - "{{ ansible_hostname }}"
25 - 127.0.0.1
26 - ::1
27 - localhost
28 - name: create a new database
29 mysql_db: name=testdb state=present login_user=root login_password="{{ mysql_root_password }}"
30 - name: add sample data to database
31 copy: src=dump.sql dest=/tmp/dump.sql
32 - name: insert sample data into database
33 mysql_db: name=testdb state=import target=/tmp/dump.sql login_user=root login_password="{{ mysql_root_password }}"
```
Si además queremos importar datos a la base de datos, añadimos al playbook:
```yaml
# Copy database dump file to remote host and restore it to database 'my_db'
- copy: src=dump.sql.bz2 dest=/tmp
- mysql_db: name=testdb state=import target=/tmp/dump.sql.bz2
```
> El fichero dump.sql.bz2 está en el ordenador del alumno
Una vez creado el fichero y configurado ansible para desplegar sólo hay que ejecutar:
```bash
$ ansible-playbook -i hosts db-server-playbook.yml
```
* [Tutorial de instalación de mysql con ansible](https://medium.com/splunkuserdeveloperadministrator/creating-mysql-databases-with-ansible-925ab28598ab)
#### 4. Playbook de ansible para instalar y configurar nginx incluyendo archivos de la aplicación web
```yaml=
- hosts: nombrealumno.daw1.infoalb.org
become: yes
vars:
domain: www.nombrealumno.daw1.infoalb.org
tasks:
- name: "apt-get update"
apt:
update_cache: yes
cache_valid_time: 3600
- name: "install nginx"
apt:
name: ['nginx']
state: latest
- name: "create www directory"
file:
path: /var/www/{{ domain }}
state: directory
mode: '0775'
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
- name: delete default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: restart nginx
- name: copy nginx site.conf
template:
src: site.conf.j2
dest: /etc/nginx/sites-enabled/{{ domain }}
owner: root
group: root
mode: '0644'
notify: restart nginx
- name: "sync website"
synchronize:
src: site/
dest: /var/www/{{ domain }}
archive: no
checksum: yes
recursive: yes
delete: yes
become: no
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
```
> La carpeta local **site** del equipo del alumno contiene los archivos del proyecto web
> Contenido del fichero `site.conf.j2` con configuración del **hostvirtual**:
```
server {
listen 80;
listen [::]:80;
server_name {{ domain }};
root /var/www/{{ domain }};
location / {
try_files $uri $uri/ =404;
}
}
```
###### tags: `dpto` `digital ocean` `despiegue` `ansible`