Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions project-1-nginx-flask-mysql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## Compose sample application
### Python/Flask with Nginx proxy and MySQL database

Project structure:
```
.
├── compose.yaml
├── flask
│   ├── Dockerfile
│   ├── requirements.txt
│   └── server.py
└── nginx
   └── nginx.conf

```

[_compose.yaml_](compose.yaml)
```
services:
backend:
build:
context: backend
target: builder
...
db:
# We use a mariadb image which supports both amd64 & arm64 architecture
image: mariadb:10-focal
# If you really want to use MySQL, uncomment the following line
#image: mysql:8
...
proxy:
build: proxy
...
```
The compose file defines an application with three services `proxy`, `backend` and `db`.
When deploying the application, docker compose maps port 80 of the proxy service container to port 80 of the host as specified in the file.
Make sure port 80 on the host is not already being in use.

> ℹ️ **_INFO_**
> For compatibility purpose between `AMD64` and `ARM64` architecture, we use a MariaDB as database instead of MySQL.
> You still can use the MySQL image by uncommenting the following line in the Compose file
> `#image: mysql:8`

## Deploy with docker compose

```
$ docker compose up -d
Creating network "nginx-flask-mysql_default" with the default driver
Pulling db (mysql:8.0.19)...
5.7: Pulling from library/mysql
...
...
WARNING: Image for service proxy was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating nginx-flask-mysql_db_1 ... done
Creating nginx-flask-mysql_backend_1 ... done
Creating nginx-flask-mysql_proxy_1 ... done
```

## Expected result

Listing containers should show three containers running and the port mapping as below:
```
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
nginx-flask-mysql-backend-1 "flask run" backend running 0.0.0.0:8000->8000/tcp
nginx-flask-mysql-db-1 "docker-entrypoint.s…" db running (healthy) 3306/tcp, 33060/tcp
nginx-flask-mysql-proxy-1 "nginx -g 'daemon of…" proxy running 0.0.0.0:80->80/tcp
```

After the application starts, navigate to `http://localhost:80` in your web browser or run:
```
$ curl localhost:80
<div>Blog post #1</div><div>Blog post #2</div><div>Blog post #3</div><div>Blog post #4</div>
```

Stop and remove the containers
```
$ docker compose down
```
35 changes: 35 additions & 0 deletions project-1-nginx-flask-mysql/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine AS builder

WORKDIR /code
COPY requirements.txt /code
RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r requirements.txt

COPY . .

ENV FLASK_APP hello.py
ENV FLASK_ENV development
ENV FLASK_RUN_PORT 8000
ENV FLASK_RUN_HOST 0.0.0.0

EXPOSE 8000

CMD ["flask", "run"]

FROM builder AS dev-envs

RUN <<EOF
apk update
apk add git
EOF

RUN <<EOF
addgroup -S docker
adduser -S --shell /bin/bash --ingroup docker vscode
EOF

# install Docker tools (cli, buildx, compose)
COPY --from=gloursdocker/docker / /

CMD ["flask", "run"]
51 changes: 51 additions & 0 deletions project-1-nginx-flask-mysql/backend/hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os
from flask import Flask
import mysql.connector


class DBManager:
def __init__(self, database='example', host="db", user="root", password_file=None):
pf = open(password_file, 'r')
self.connection = mysql.connector.connect(
user=user,
password=pf.read(),
host=host, # name of the mysql service as set in the docker compose file
database=database,
auth_plugin='mysql_native_password'
)
pf.close()
self.cursor = self.connection.cursor()

def populate_db(self):
self.cursor.execute('DROP TABLE IF EXISTS blog')
self.cursor.execute('CREATE TABLE blog (id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255))')
self.cursor.executemany('INSERT INTO blog (id, title) VALUES (%s, %s);', [(i, 'Blog post #%d'% i) for i in range (1,5)])
self.connection.commit()

def query_titles(self):
self.cursor.execute('SELECT title FROM blog')
rec = []
for c in self.cursor:
rec.append(c[0])
return rec


server = Flask(__name__)
conn = None

@server.route('/')
def listBlog():
global conn
if not conn:
conn = DBManager(password_file='/run/secrets/db-password')
conn.populate_db()
rec = conn.query_titles()

response = ''
for c in rec:
response = response + '<div> Hello ' + c + '</div>'
return response


if __name__ == '__main__':
server.run()
2 changes: 2 additions & 0 deletions project-1-nginx-flask-mysql/backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flask==2.0.1
mysql-connector==2.2.9
62 changes: 62 additions & 0 deletions project-1-nginx-flask-mysql/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
services:
db:
# We use a mariadb image which supports both amd64 & arm64 architecture
image: mariadb:10-focal
# If you really want to use MySQL, uncomment the following line
#image: mysql:8
command: '--default-authentication-plugin=mysql_native_password'
restart: always
healthcheck:
test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent']
interval: 3s
retries: 5
start_period: 30s
secrets:
- db-password
volumes:
- db-data:/var/lib/mysql
networks:
- backnet
environment:
- MYSQL_DATABASE=example
- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
expose:
- 3306
- 33060

backend:
build:
context: backend
target: builder
restart: always
secrets:
- db-password
ports:
- 8000:8000
networks:
- backnet
- frontnet
depends_on:
db:
condition: service_healthy

proxy:
build: proxy
restart: always
ports:
- 80:80
depends_on:
- backend
networks:
- frontnet

volumes:
db-data:

secrets:
db-password:
file: db/password.txt

networks:
backnet:
frontnet:
1 change: 1 addition & 0 deletions project-1-nginx-flask-mysql/db/password.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
db-78n9n
2 changes: 2 additions & 0 deletions project-1-nginx-flask-mysql/proxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FROM nginx:1.13-alpine
COPY conf /etc/nginx/conf.d/default.conf
8 changes: 8 additions & 0 deletions project-1-nginx-flask-mysql/proxy/conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend:8000;
}

}