Здравствуйте, любопытный человек! Добро пожаловать во вторую часть нашей серии «Развертывание глубокого обучения». В этой части, как я и обещал, мы напишем несколько HTML-страниц и Dockerfile для нашего первого сервиса, т. е. веб-сайта. Итак, положите пальцы на клавиатуру и начнем.

Сначала мы создадим папку с именем «static» внутри папки «flask». В этой папке мы храним статические файлы, которые будут использоваться нашим веб-приложением. Я храню в нем файл «index.png», который будет отображаться при входе пользователя в наше веб-приложение.

Теперь мы создадим папку с именем «Шаблон» внутри папки «фласк». Здесь мы будем хранить наши HTML-страницы, а именно «base.html», «index.html», «login.html», «result.html», «signup.html» и «start.html».

Итак, давайте создадим нашу первую страницу «base.html». Каждая вторая страница будет расширять эту страницу «base.html». Эта страница предоставит макет для расширения всех остальных страниц. Он предоставляет кнопки в правом верхнем углу веб-страницы для входа, регистрации и дома.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flask Auth Example</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" />
</head>
<body>
<section class="hero is-primary is-fullheight">
<div class="hero-head">
<nav class="navbar">
<div class="container">
<div id="navbarMenuHeroA" class="navbar-menu">
<div class="navbar-end">
<a href="{{ url_for('index') }}" class="navbar-item">
  Home
 </a>
{% if current_user.is_authenticated %}
<a href="{{ url_for('predict') }}" class="navbar-item">
Application
</a>
{% endif %}
{% if not current_user.is_authenticated %}
<a href="{{ url_for('login') }}" class="navbar-item">
Login
</a>
<a href="{{ url_for('signup') }}" class="navbar-item">
Sign Up
</a>
{% endif %}
{% if current_user.is_authenticated %}
<a href="{{ url_for('logout') }}" class="navbar-item">
  Logout
 </a>
{% endif %}
</div>
</div>
</div>
</nav>
</div>
<div class="hero-body">
<div class="container has-text-centered">
{% block content %}
{% endblock %}
</div>
</div>
</section>
</body>
</html>

Теперь напишем «index.html». Эта страница появится, когда пользователь войдет в наше веб-приложение. Эта страница предоставит пользователю макет для загрузки изображения в наше веб-приложение.

{% extends "base.html" %}
{% block content %}
<head><style>.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}</style></head>
<div class="container">
<h1>Upload File Here</h1>
<hr>
<img src="{{ result.image_path }}" class="img-rounded" width="400" height="200">
<form class="form-inline" action="/app" method="post" enctype="multipart/form-data">
<div class="form-group">
<input type="file" name="file" class="btn btn-default">
</div>
<div class="form-group">
<input type="submit" value="Upload" class="button">
</div>
</form>
</div>
{% endblock %}

Далее мы создадим «login.html». Эта страница предоставит пользователю макет для входа в наше веб-приложение, т. е. введите свое имя пользователя и пароль и кнопку отправки для входа.

{% extends "base.html" %}
{% block content %}
<div class="column is-4 is-offset-4">
<h3 class="title">Login</h3>
<div class="box">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="notification is-danger">
{{ messages[0] }}
</div>
{% endif %}
{% endwith %}
<form method="POST" action="/login">
<div class="field">
<div class="control">
<input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus="">
</div>
</div>
<div class="field">
<div class="control">
<input class="input is-large" type="password" name="password" placeholder="Your Password">
</div>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox">
Remember me
</label>
</div>
<button class="button is-block is-info is-large is-fullwidth">Login</button>
</form>
</div>
</div>
{% endblock %}

Теперь напишем «result.html». На этой странице будет представлен макет, когда наше веб-приложение будет отображать результат классификации для загруженного изображения, то есть, является ли загруженное изображение кошкой или собакой.

{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Predicted Class: {{ result.class_name }}</h1>
<hr>
<div>
<img src="{{ result.image_path }}" class="img-rounded" width="400" height="200">
</div>
<a href="{{ url_for('predict') }}" class="btn btn-default">Back</a>
</div>
{% endblock %}

Далее мы напишем «signup.html». Эта страница предоставит пользователю макет для регистрации в нашем веб-приложении. Пользователи могут загружать изображения в наше веб-приложение, только если они вошли в систему.

{% extends "base.html" %}
{% block content %}
<div class="column is-4 is-offset-4">
<h3 class="title">Sign Up</h3>
<div class="box">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="notification is-danger">
{{ messages[0] }}. Go to <a href="{{ url_for('login') }}">login page</a>.
</div>
{% endif %}
{% endwith %}
<form method="POST" action="/signup">
<div class="field">
<div class="control">
<input class="input is-large" type="email" name="email" placeholder="Email" autofocus="">
</div>
</div>
<div class="field">
<div class="control">
<input class="input is-large" type="text" name="name" placeholder="Name" autofocus="">
</div>
</div>
<div class="field">
<div class="control">
<input class="input is-large" type="password" name="password" placeholder="Password">
</div>
</div>
<button class="button is-block is-info is-large is-fullwidth">Sign Up</button>
</form>
</div>
</div>
{% endblock %}

И в конце напишем «start.html». Когда наш пользователь впервые открывает наше веб-приложение через веб-браузер. Это первая страница, которую увидит пользователь.

{% extends "base.html" %}
{% block content %}
<h1 class="title">
Flask Web App
</h1>
<h2 class="subtitle">
Flask Web App
</h2>
{% endblock %}

Мы написали так много HTML-страниц, что, наконец, мы закончили со страницами. Чтобы развернуть наше веб-приложение в контейнере докеров, нам нужно написать файл «requirements.txt». По сути, этот файл содержит все пакеты Python, которые требуются для запуска наших кодов Python.

Flask>=1.1.2
Flask-Bootstrap>=3.3.7.1
waitress>=1.4.3
tensorflow-cpu==2.1.0
numpy>=1.18.3
requests>=2.23.0
Pillow==7.1.0
gunicorn==20.0.4
Flask-SQLAlchemy==2.4.3
Flask-Login==0.5.0
pandas==1.0.5
grpcio==1.31.0
tensorflow-serving-api==2.1.0

Теперь мы можем написать более крутой файл, который будет нашим Dockerfile. Для этого создайте файл с именем «Dockerfile.flask» внутри папки «flask».

FROM python:3.7-slim-buster
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt

Наш файл Dockerfile довольно маленький, но он сделал все, что требуется для нашего первого сервиса, то есть Web. Обратите внимание, что я запускаю контейнер Docker с пользователем root, поскольку я запускаю свой контейнер Docker в Windows, который имеет некоторые проблемы, когда я использую команду «chown» в файле dockerfile. В целях безопасности рекомендуется запускать контейнер Docker от пользователя без полномочий root.

Давайте посмотрим на нашу структуру папок для нашего первого сервиса, то есть Web.

services
|---web
     |---flask
           |---database
           |---static
           |      |---index.png
           |---Template
           |      |---base.html
           |      |---index.html
           |      |---login.html
           |      |---result.html
           |      |---signup.html
           |      |---start.html
           |---app.py
           |---database.py
           |---dbmodels.py
           |---Dockerfile.flask
           |---manage.py
           |---model.py
           |---requirements.txt

Примечание. В приведенной выше структуре папок мы видим, что папка базы данных пуста. Файл data2.db будет создан внутри него при первом запуске нашего веб-приложения. В этом файле будут храниться имя пользователя и хешированные пароли.

Если структура вашей папки выглядит так, как показано выше, поздравляем, вы создали свой первый сервис. то есть Интернет. Теперь вы можете расслабиться и убрать пальцы с клавиатуры.

В следующей части этой серии. Мы создадим другие службы для нашего веб-приложения. то есть Tensorflow-Serving и Nginx.

А пока наслаждайтесь вашим днем ​​:)

Если вы готовы идти дальше, вот ссылка на Часть 3

Использованная литература-

  1. https://www.digitalocean.com/community/tutorials/how-to-add-authentication-to-your-app-with-flask-login