Uno en el campo no es un guerrero o cómo un backend de pila completa intentó convertirse

Me encanta iniciar varios proyectos paralelos, creo que esta es una de las mejores formas de aprender algo nuevo y realmente valioso. Y tengo un defecto grave: casi nunca termino de hacer las cosas. Esto, por supuesto, no se trata de proyectos de estudio para los que me darán una calificación o tareas establecidas por el empleador. Estoy hablando de mis propias ideas que me iluminan fuera del trabajo o estudio constante. Cada vez que domino alguna habilidad completamente nueva que considero demandada y no veo la posibilidad de aprender algo más así, simplemente me olvido del proyecto. Pero esta vez decidí superarme, comenzar el proyecto, completarlo y contar el camino por el que pasé.



Un poco sobre mi



Tengo una educación especializada, una licenciatura en matemáticas, una maestría en TI. Sorprendentemente, muchas de las cosas que dominé en la universidad me resultaron útiles, especialmente la maestría. En cualquier caso, logré sintonizar mi cerebro en la dirección correcta para absorber productivamente la información necesaria, mostrarme bien en esta área, encontrar trabajo y avanzar en mi carrera.



Mientras estudiaba, era fanático de C ++ y desarrollador de juegos. Luego desarrollé un motor escrito por mí mismo y escribí juegos en él, lo intenté. Tenía un diploma en C y estaba relacionado con el uso de procesadores gráficos en los cálculos. Luego me interesé en el desarrollo de Java y Android. Completó varios proyectos educativos en este idioma y luego, como parte de un proyecto de un estudiante, con el apoyo de una corporación internacional, escribió una utilidad de consola para analizar el desempeño del programa. Todo esto, luego, se convirtió en una aplicación de Android con la capacidad de probar su teléfono en términos de rendimiento y compararlo con otros.



El mundo de las pitones estaba en el horizonte. Inesperadamente, por sugerencia de mi buen amigo, me interesé por la ciencia de datos y Python, fue hace más de cuatro años. Casualmente, tuve que cambiar de trabajo y mis habilidades fueron evaluadas como adecuadas para comenzar a trabajar en backends para servicios analíticos internos. Entonces, a través del análisis de datos, llegué al desarrollo web en este idioma.



Por deber, también tenía que ser un desarrollador de pila completa cuando los desarrolladores frontales reales estaban extremadamente ocupados o cuando los estándares de calidad de la interfaz de usuario no estaban en primer lugar.



Esta es una descripción de la ruta que tomó para crear su propia aplicación web completa sin una inmersión total en los detalles técnicos. Los enlaces a las confirmaciones de mi repositorio se eliminarán en el camino. Brevemente sobre las etapas de este viaje:



  • UI
  • , , CI
  • production-
  • production-
  • https

- AWS. , .







La elección del tema de la aplicación es otra historia, la elección final recayó en los hábitos de seguimiento. La página de inicio tendría un conjunto de botones con hábitos rastreados. Hicimos una acción, presionamos el botón, y así todos los días. Los datos deben guardarse y mostrarse en una página separada en forma de tabla, hábitos en filas y días calendario en columnas; una celda llena indica que la acción requerida se realizó ese día. Como el rastreador de hábitos de papel más simple.





La pila tecnológica era obvia para mí: react, django, postgres, nginx, uwsgi.



Diseño de interfaz de usuario inicial



Decidí comenzar desde la interfaz de usuario e instalé nodejs.org/en/download/package-manager , github.com/facebook/create-react-app , luego creé un proyecto:



npx create-react-app easytrack


Y comencé a diseñar. Desde el principio, no se me ocurrió nada más fácil que codificar una lista de objetos de lógica empresarial directamente en el programa y mostrarlos como una lista en la etiqueta ul. Estos objetos son: el grupo de temas, el elemento rastreado, los registros de rastreo reales para un elemento específico en un día específico.



En la primera página, tenía grupos temáticos y, al hacer clic en cualquiera de ellos, se abrió una lista de elementos que se pueden rastrear.



También hice otra página, que contenía una tabla simple con estadísticas de los últimos días.



Desarrollar un backend



En esta etapa, era necesario un backend. Es necesario para guardar objetos en la base de datos, administrar usuarios y diferenciar derechos. Ya tenía que usar Django para mis propios proyectos (no llevé a su conclusión lógica, por supuesto), pero había una dificultad: tenía que usar Django Rest Framework, que nunca había tratado en absoluto. Gracias al libro Building Django 2.0 Web-applications [1]. Lo revisé de cabo a rabo, a excepción del último capítulo, donde crearon API en DRF, lo hojeé con mis ojos. En el transcurso de la historia, me volveré hacia ella más de una vez.



Nowhere to go, abrí la documentación de www.django-rest-framework.org y comencé a fumarla, y también busqué el libro mencionado.



Instalé y activé el entorno virtual, instalé Django y creé un proyecto de django en la raíz del proyecto:



virtualenv venv
. ./venv/bin/activate
pip install django
django createproject config


Cambiaré el nombre de la carpeta principal a django, para que por el nombre todo lo relacionado con su contenido quede inmediatamente claro. Dentro habrá un módulo de Python llamado config, que también es muy conveniente. Código de interfaz envuelto en la carpeta de reacción. Así es como se desarrolló la estructura general de carpetas, que ha sobrevivido hasta el día de hoy ( mira Github ).



En la raíz del proyecto, creé la aplicación principal:



django createapp core


Creé clases de modelos de lógica de negocios exactamente en el mismo formato en el que los codifiqué en el frente, serializadores y vistas ( enlace para confirmar en Github ).

No nos molestamos con la base de datos y usamos el SQLite predeterminado. A través del panel de administración, cargué un par de muestras de datos de prueba que había codificado anteriormente en el frente. Estamos intentando conectarnos a la interfaz. python3 manage.py runserver en una pestaña, el hilo comienza en otra y condujo.



El servidor de desarrollo React se ejecuta en el puerto 3000, y Django se ejecuta en el puerto 8000. Nada es más fácil que escribir una solicitud de búsqueda ('http: // localhost: 8000 / ...') en el frente. Pero no funcionó de esa manera debido a cors-origin, una protección especial que evita que cualquier sitio realice solicitudes automáticas a cualquier servidor. Por lo tanto, sin pensarlo dos veces, lo incorporé al backenddjango-cors-headers ), lo configuré, funcionó. Solo entonces supuse que debía agregar una sección de proxy a package.json y apuntar al backend, luego fetch ('/ api / v1 / ...') comenzó a funcionar normalmente y ya no se necesitaban configuraciones adicionales.



Al principio, fue terriblemente ingenuo, porque hice solicitudes asincrónicas en el constructor, todo funcionó para mí y bien. Solo más tarde aprendí sobre los métodos del ciclo de vida, dónde se puede y dónde no se debe hacer este tipo de trabajo. Ahora que se mostraban los elementos, se podían crear nuevos elementos.



Implementar la gestión de usuarios y la separación de derechos.



En esta etapa, lo único que faltaba era la separación de los derechos de los elementos de datos: todos fueron creados en nombre de un usuario anónimo desbloqueado. Necesitaba integrarme de alguna manera en él sin romper el ecosistema de django.



Para empezar, codifiqué el nombre de usuario / contraseña en la interfaz y formé el encabezado de Autorización con el valor 'Básico' + base64.encode (nombre de usuario + ":" + contraseña). Luego pensé en generar una cadena en formato base64 y guardarla en el cliente al ingresar un nombre de usuario / contraseña. Pero había grandes dudas sobre esta decisión en términos de seguridad, quería probar algo diferente.



Busqué en Internet por un corto tiempo y aprendí sobre la tecnología JWT y el módulo django-rest-framework-simplejwque proporcionó clases de autenticación para DRF y vistas para obtener un par de tokens y para actualizar un token de acceso.



Desde el lado de la interfaz, fue suficiente guardar un par de tokens en localStorage y pasar el encabezado de Autorización con el valor "Bearer access-token". Finalmente, dispuse las páginas de inicio de sesión y cerré todos los accesos al sitio para usuarios no autorizados usando clases de permisos ( enlace a la confirmación de Github ). En el frente, si no había un token de actualización, me redirigía a la página de inicio de sesión ( enlace a la confirmación de Github ).



Luego, hice posible registrarme en el sitio, creé una vista en el backend, diseñé una página y configuré el envío de solicitudes. En el futuro, mi sueño era escribir la activación de la cuenta por correo.



Refactorizar, implementar funciones básicas faltantes, CI



En este momento, resultó una aplicación mínimamente utilizable y quería recopilar comentarios. Le mostré la aplicación a mi esposa y le pedí que la usara sin un solo aviso de mi parte. El registro fue bastante exitoso, pero luego todo no salió como yo pensaba. Luego me di cuenta de que las carpetas temáticas no deberían estar a la vanguardia, sino que deberían ser una herramienta auxiliar, y también necesitamos agregar pistas de que estamos creando un conjunto de botones para presionarlos una vez al día. Ella esperaba que fuera como un rastreador de hábitos de papel normal con una hoja de cálculo en la que necesitas pintar sobre las celdas. Cuando me di cuenta de esto, comencé a refactorizar. Y además de eso, había algo que refactorizar. Revisé la estructura del módulo casi desde cero.



En este punto, decidí agregar belleza, estilos CSS y diseño receptivo. En esto no soy particularmente fuerte y confío en CSS-frameworks. La elección recayó en Bulma , basada en la cantidad de estrellas, descargas de npmjs.com , a pesar de que no quería tomar Bootstrap. Por lo menos, hice frente a esta tarea.



Paralelamente, mejoré las funciones de backend. Hizo un CRUD completo. El sueño de confirmar el registro por correo también se hizo realidad. Descubrí las funciones de enviar cartas, obtuve la capacidad de depurar todo a través de un servidor de correo de depuración.



python -m smtpd -n -c DebuggingServer localhost:1025


Para la puesta en escena, abrí una cuenta de Google basura y logré configurar el envío de cartas a través del correo de Google.



En cuanto a la cobertura de prueba para el backend, todo salió bastante bien por sí mismo, pero para el frontend todo sigue siendo bastante lento. Pero no me desespero, de repente, se ajustará.



El siguiente paso fue configurar CI, inicié GithubActions para ejecutar las pruebas, usé los constructores de configuración para ejecutarlo, lo modifiqué un poco y eso es todo.



Generar configuraciones para el entorno de producción



Después de un corto tiempo ordenando el código y pequeños detalles de la lógica, tuve que empezar a formar la configuración de producción. Para esto me inspiré en el mismo libro [1]. Divido los archivos de configuración de django, python-dependencies en 3 partes ( enlace a la confirmación de Github ):





  • común: todo lo que necesita para cualquier entorno
  • dev —
  • prod —


, .



uwsgi, nginx , dockerfile, .



[1] «1 — 1 », , , . phusion/baseimage, Ubuntu, .



, , postgres, . ( Github).



production-



, [1], AWS, . , - . , , , , . Free Tier, , , ? .



, AWS - , . , . , ECS.





Elastic Container Service



Vi que el generador de acciones de Github le permite crear una configuración para una tarea de implementación continua de contenedores en AWS ECS. Digamos que comencé a profundizar en este servicio y me di cuenta de que en la consola local necesito crear un clúster, crear una definición de tarea y describir el contenedor, habiendo guardado previamente su imagen en otro servicio AWS ECR, que es Dockerhub por función. La consola ofrecía 2 tipos de clústeres: Fargate y EC2. La primera tecnología es completamente sin servidor, lo que significa que simplemente iniciamos el contenedor y el tiempo de ejecución se encarga de todo. Los contenedores de un clúster del segundo tipo se ejecutan en su propia instancia de máquina virtual en la nube. Como no quería sumergirme en esto durante mucho tiempo, creé un clúster basado en Fargate. Pero me encontré con el hecho de que no podía pasar valores secretos al contenedor,debido a esto, la tarea caía constantemente.



Mientras intentaba llevar el contenedor a un estado de funcionamiento, el clúster había estado funcionando durante varias horas y se agregó dinero a mi pestaña con el pago de los servicios. La tecnología en sí resultó ser de pago y no se aplica al nivel gratuito. Terminé el grupo y decidí ocuparme del pago un poco más tarde. Leí en la documentación que no tiene que pagar más por usar ECS además de EC2, excepto para usar los recursos de EC2, pero EC2 está cubierto por Free Tier y decidí probar esta ruta.



Soporte de AWS



Al final, alguien me mintió y me cobraron algo más por esta configuración. Un total de $ 0.32, aunque un poco, pero aún así es una pena. Luego escribí en apoyo, dije que tenía dificultades con la configuración y todo el tiempo pensé que encajaba en el nivel gratuito, pero nada funcionó para mí, y el dinero fue retirado. Pedí ayuda. En respuesta a mi llamada, respondieron felices, dieron un cupón por $ 1, que cubrió todos los gastos inesperados. Bien.



Como resultado, tuve que olvidarme de este servicio de contenedores y dominar EC2 en su forma más pura.



Nube de Computación elástica



EC2 — . Free Tier 750 . , , - , .



[1] docker-machine, . , EC2. docker .



, dockre-machine create… EC2. docker-compose up -d . 80 . , «Public DNS» , .





Relational Database Service



AWS RDS. . , ( postgres) , Free Tier, . , , .



Simple Email Service



También tuve que afrontar el hecho de que las cartas no se enviaron. El hecho es que todos los servicios de correo prohíben las direcciones IP que coinciden con las instancias EC2 para contrarrestar el spam. Era imperativo utilizar SES. Otro servicio de AWS.



También lo configuré, indiqué los buzones desde los que voy a enviar cartas, todo está bien. Sin embargo, no todo es tan optimista hasta ahora. Las cuentas nuevas están en modo sandbox de forma predeterminada, lo que no permite enviar correos electrónicos a buzones de correo no confirmados. Tuve que confirmar casillas para mis probadores beta.





Registre un dominio y configure un certificado para acceso https



Para probar completamente la aplicación, se requirió configurar una conexión https, y esto resultó ser imposible sin registrar un dominio. No quería poner en riesgo direcciones de correo electrónico, contraseñas y tokens transfiriéndolos a través de una conexión no segura. Para este caso, busqué en todo Internet. Algunos registradores no se adaptaron, porque es caro, pero en algún lugar es muy caro, además, muchos tienen críticas simplemente terribles. Mientras miraba, la red de publicidad Yandex entendió lo que quería e inmediatamente me ofreció una empresa donde podía registrar un dominio por 39 rublos. Hubo más reseñas sobre este servicio, así que creé una cuenta, elegí un dominio gratuito y lo registré por mí.



Luego, para acceder a mi instancia EC2 usando este dominio, creé una zona de alojamiento en el servicio AWS Route 53, registré la configuración de DNS y reescribí los registros NS en la cuenta personal del servicio donde se registró el dominio a los de Amazon.



Usé el servicio Let's Encrypt para crear el certificado. Me decidí por esta configuración: el nginx local hace un proxy_pass a la dirección del contenedor, el contenedor gira y es accesible a través del puerto 8080. No fue difícil usar la utilidad de consola certbot, generar un certificado y configurar https automáticamente. Finalmente, permita el acceso a la máquina virtual en el puerto 443.



El desarrollo del proyecto ha llegado a su conclusión lógica



, . , , SES , . , . , , , , , , , , . , , . :





  • , , . .
  • -
  • ,
  • , -


— , , . React , AWS, , , .



Pero la principal constatación es que uno en el campo no es un guerrero en absoluto. Trabajé en este proyecto durante más de dos meses, muchas horas en mi tiempo libre ... ¿Y sabes qué? Nunca he cosido tan terriblemente. Se hace evidente que cuanto más envejece, menos deseo de aprender algo "así", menos deseo de trabajar mucho en algunas cosas que son "simplemente interesantes". Pero al mismo tiempo, nota que hay más oportunidades disponibles para hacer algo por el bien, por el beneficio. Empieza a apreciar los esfuerzos concentrados, el trabajo en equipo, así como la relajación, el tiempo que pasa con personas cercanas.



Código en Github .



Enlaces



  1. Construyendo aplicaciones web Django 2.0