He traspasado todas mis aplicaciones que tenía en heroku a un vps propio y además he aprovechado para dockerizarlas todas, incluyendo bases de datos y otros servicios web. Concretamente he dockerizado:
Instalación de docker en el VPS
Docker
En primer lugar he tenido que instalar docker y docker compose. Para la instalación de docker en ubuntu 22.04 server :
Actualizamos la lista de paquetes del S.O. y actualizamos el S.O.
apt update && apt upgrade
Añadimos una serie de paquetes de requisitos previos.
apt install apt-transport-https ca-certificates curl software-properties-common
Se agrega la clave GPG para el repositorio oficial de Docker a nuestro S.O.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
Y por último ya podremos añadir el repositorio a nuestro S.O. Podemos comprobar que se ha añadido una línea en /etc/apt/source.list.
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu jammy stable"
Actualizamos la base de datos ya que hay una línea nueva (compruebe en el listado que sale) y comprobaremos antes de instalar que vamos a instalar desde el repositorio (y no desde Ubuntu Server).
apt update & apt-cache policy docker-ce
Por último, podremos instalar el paquete (paquete pesado de aproximadamente 400 MBytes). Como no puede ser de otra manera hay que asegurar que Docker engine está arrancado y si reinicia el servicio en el arranque del S.O. anfitrión.
apt-get install docker-ce
Manualillo de systemctl
systemctl disable Docker # deshabilitamos en el arranque el demonio docker
systemctl list-unit-files | grep docker
systemctl enable docker
systemctl list-unit-files | grep docker
systemctl stop docker && systemctl status Docker
systemctl start docker && systemctl status Docker
docker versión
Resolución del problema de permisos de docker para un usuario no root
sudo groupadd docker
sudo usermod -aG docker mi_usuario
newgrp docker
docker ps
Docker-Compose
Actualización de la lista de repositorios
sudo apt update
Instalación de docker-compose
sudo apt install docker-compose
Comprobamos el estado del servidor docker y las versiones respectivamente
systemctl status docker
docker -v
docker-compose -version
Creación de los contenedores y docker-compose
En primer lugar me he creado una carpeta docker en el home, que irá alojando los scripts y ficheros necesarios. Tiene la siguiente estructura:
Vamos a utilizar para el certificado de autenticación del servidor https Let’s encrypt.
Instalación y generación del certificado Let’s encrypt
Para la instalación de la aplicación certbot de Let’s encrypt para generar los certificados, recomiendo seguir las instrucciones de la siguiente página https://certbot.eff.org/instructions?ws=other&os=ubuntufocal está preseleccionado para Ubuntu server.
La página indicada anteriormente, es la única que funciona actualmente con Ubuntu Server 22.04, el resto de tutoriales que he visitado se encuentran obsoletos al haber cambiado a snap la aplicación de certbot de Let’s encrypt y el repositorio de github ya no funciona tampoco como se esperaba en instrucciones antiguas.
Una vez terminado nos generará la siguiente salida:
Generamos el certificado que es válido para tomcat .p12.
openssl pkcs12 -export -in /etc/letsencrypt/live/mi_dominio_web/fullchain.pem -inkey /etc/letsencrypt/live/mi_dominio_web/privkey.pem -out springboot_letsencrypt.p12 -name bootalias -CAfile chain.pem -caname root
Recuerda cambiar las rutas por las tuyas
Contenedor para Spring Boot
Copiamos los ficheros necesarios en nuestra carpeta de nuestro nuestra carpeta en el home de /docker/docker-java/: estando en la raíz de esta el fichero .jar y .p12.
Generación de los scripts de docker
En primer lugar, vamos a crear un Dockerfile para que contenga la imagen (para Ubuntu server) y realice la copia de los ficheros necesarios (el .jar y el certificado) hacia nuestro contenedor.
Dockerfile
FROM eclipse-temurin:17-jdk-alpine
ARG JAR_FILE=*.jar
COPY ${JAR_FILE} my_proyect_compiled.jar
COPY my_cert_letsencrypt.p12 .
ENTRYPOINT ["java","-jar","/my_proyect_compiled.jar"]
Tendremos que cambiar el nombre del certificado y del fichero del proyecto compilado .jar
En segundo lugar, vamos a crear el script de docker-compose.yml que creará nuestro servicio web y expondrá el puerto 8443 por defecto.
docker-compose.yml
version: '3.8'
services:
proyect_name:
build: .
container_name: 'proyect_name-spring-app'
restart: always
ports:
- '8080:8080'
- '8443:8443'
Tendremos que cambiar el nombre del servicio y del contenedor
Proyecto Spring Boot
Vamos a añadir al fichero aplication.properties la configuración para que el servidor tomcat que crea Spring, escuche por el puerto https, que por defecto es el 8443 en lugar del 443. Y el certificado que hemos creado previamente para que nuestro servidor funcione con Let’s encrypt.
aplication.properties
server.port=9443
server.ssl.enabled=true
server.ssl.key-store=my_cert_letsencrypt.p12
server.ssl.key-store-password=your_password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=your_alias
Tendremos que cambiar: el certificado, la contraseña y el alias con el que creamos anteriormente.
Generamos el fichero .jar mediante el comando de mvn para compilar
mvn package
Para ver la diferencia entre mvn package y mvn install, consulta este enlace
Ahora en la carpeta /target del proyecto tendremos el fichero .jar que pasaremos por ftp a nuestra carpeta en el home de /docker/docker-java/nombre_aplicacion que contendrá a su vez los ficheros de Dockerfile y docker-compose.yml, junto a nuestro certificado .p12. Para que todo se pueda copiar en el contenedor de docker, según las instrucciones de nuesto Dockerfile.
Finalmente, debemos tener la siguiente estructura de ficheros en la carpeta de docker:
/docker-my_app
|-Dockerfile
|-docker-compose.yml
|-my_app.jar
|-my_cert.p12
En la página oficial del Spring Boot hay también un tutorial que indica con mayor detalle lo realizado anteriormente https://spring.io/guides/topicals/spring-boot-docker/ incluyendo aspectos de seguridad como la creación de un usuario diferente a root dentro del contenedor de docker.
Contenedor para Laravel
Para Laravel vamos a utilizar un servidor ngnix con la última versión por seguridad. para ello crearemos varios ficheros que describiré a continuación:
Generación de los scripts de docker
Dockerfile
# imagen de dockerhub que descargara
FROM php:8.2-fpm-alpine
# algunas configuraciones para que funcione el contenedor para mysql
#RUN docker-php-ext-install pdo pdo_mysql
# Install Postgre PDO
RUN set -ex \
&& apk --no-cache add \
postgresql-dev
RUN docker-php-ext-install pdo pdo_pgsql
# instala composer en el contenedor
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# da permisos para editar los archivos en esta ruta del container
RUN chown -R www-data:www-data /var/www
RUN chmod 755 /var/www
docker-compose.yml
version: "3.3"
# Servidor nginx
services:
nginx-laravel:
image: nginx:latest
restart: always
ports:
- "10440:443"
volumes:
- ./src:/var/www/html
- ./src/storage:/var/www/html/storage
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt/live/mi_dominio_web/fullchain.pem:/etc/nginx/ssl/fullchain.pem:ro
- /etc/letsencrypt/live/mi_dominio_web/privkey.pem:/etc/nginx/ssl/privkey.pem:ro
links:
- php-laravel
# Configuración de php-fpm
php-laravel:
# image: php:8-fpm
build: .
restart: always
volumes:
- ./src:/var/www/html
# command: sh -c "cd /var/www/html && composer update nesbot/carbon"
nginx.conf
server {
listen 80;
listen [::]:80;
listen 443 ssl;
server_name mi_ip www.mi_dominio mi_dominio localhost;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
# Log files for Debug
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
# Laravel web root directory
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
# Nginx Pass requests to PHP-FPM
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-laravel:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
Sustituye mi dominio mi_dominio por el tuyo
Finalmente, debemos tener la siguiente estructura de ficheros en la carpeta de docker:
/docker-my_app
|-Dockerfile
|-docker-compose.yml
|-default.conf
|-nginx.conf
|-src
Y en la carpeta src vamos a copiar todo el contenido de Laravel.
Levantamos el contenedor de docker como demonio y con la compilación activada
docker-compose up --build -d
Con esto ya tenemos una versión de Laravel Dockerizada y funcionando.
Contenedor para Flask
Tenemos nuestra aplicación python con flask en la carpeta app y los certificados en la carpeta certs y el fichero de requirements al mismo nivel que los de docker.
Generación de los scripts de docker
Dockerfile
version: '3.8'
services:
flask:
restart: always
build: .
command: python app.py run -h 0.0.0.0
volumes:
- ./app:/usr/src/app/
ports:
- 10443:5000
environment:
- FLASK_APP=app.py
- FLASK_DEBUG=1
nginx.conf
server {
listen 80;
listen [::]:80;
listen 443 ssl;
server_name www.mi_dominio_web mi_dominio_web;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
app.py
Sustituye mi dominio mi_dominio_web por el tuyo
Contenedor para Nginx
Copiamos los ficheros necesarios en nuestra carpeta de nuestro nuestra carpeta en el home de /docker/docker-ngnix/: estando en la raíz de esta el fichero docker-compose.yml y nginx.conf, las carpetas: cers, site-content y templates.
Generación de los scripts de docker
docker-compose.yml
version: "3.7"
services:
web:
image: nginx
volumes:
- ./templates:/etc/nginx/templates
- ./site-content:/etc/nginx/html
- ./certs:/etc/nginx/certs:ro
- ./nginx.conf:/etc/nginx/conf.d/nginx.conf
ports:
- "80:80"
- "443:443"
Generación del script de configuración de ngnix
nginx.conf
server {
listen 80;
listen [::]:80;
listen 443 ssl;
server_name www.mi_dominio_web mi_dominio_web;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
Sustituye mi dominio mi_dominio_web por el tuyo
Generación del resto de ficheros y carpetas
En la carpeta certs tienes que tener una copia de los ficheros generados por certbot, son los ficheros: fullchain.pem y privkey.pem.
En la carpeta site-content el index.html de tu web con todo su contenido. Este es un ejemplo de redirección de mi página, por si te sirve de ejemplo.
site-content/index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="refresh" content="5;url=https://mi_dominio_web">
</head>
<body>
<h1>Acceso no permitido</h1>
<p>Va a ser redireccionado a la página <a href="https://mi_dominio_web">https://mi_dominio_web</a></p>
</body>
</html>
Contenedor para PostgreSQL
La dockerización de PostgreSQL es una de las más sencillas que hay, solamente tenemos que crear un docker-compose y levantar el contenedor.
Generación del scripts de docker
docker-compose.yml
version: '3.3'
services:
db:
image: postgres
restart: always
container_name: postgresql
environment:
- POSTGRES_USER=mi_usuario
- POSTGRES_PASSWORD=mi_contraseña
ports:
- 'mi_puerto_ext:5432'
volumes:
- db:/var/lib/postgresql/data
volumes:
db:
driver: local
En la carpeta ./db que se creará automáticamente tendremos todos los ficheros de la base de datos para poder consultar y/o modificar lo que necesitemos desde fuera del contenedor.
Contenedor para mySQL
La dockerización de MySQL es una de las más sencillas que hay, solamente tenemos que crear un docker-compose y levantar el contenedor.
Generación del scripts de docker
docker-compose.yml
version: '2.4'
services:
mariadb:
image: mariadb
container_name: mysql
restart: always
volumes:
- ./db:/var/lib/mysql
environment:
TZ: Europe/Madrid
#MYSQL_RANDOM_ROOT_PASSWORD: "yes"
MYSQL_ROOT_PASSWORD: mi_contraseña_root
MYSQL_DATABASE: mi_nombre_bbdd
MYSQL_USER: mi_usuario
MYSQL_PASSWORD: mi_contraseña
ports:
- 23452:3306
#healthcheck:
#test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD || exit 1
#test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD || exit 1
#interval: 60s
#timeout: 5s
#retries: 5
#start_period: 30s
En la carpeta ./db que se creará automáticamente tendremos todos los ficheros de la base de datos para poder consultar y/o modificar lo que necesitemos desde fuera del contenedor.
Contenedor para MongoDB
La dockerización de MongoDB es una de las más sencillas que hay, solamente tenemos que crear un docker-compose y levantar el contenedor.
Generación del scripts de docker
docker-compose.yml
version: '3.7'
services:
mongodb_container:
image: mongo:latest
ports:
- mi_puerto_ext:27017
volumes:
- db:/data/db
volumes:
db:
En la carpeta ./db que se creará automáticamente tendremos todos los ficheros de la base de datos para poder consultar y/o modificar lo que necesitemos desde fuera del contenedor.
Fuentes:
https://certbot.eff.org/instructions
https://wstutorial.com/rest/spring-boot-with-lets-encrypt.html