1
0
mirror of https://github.com/daniviga/bite.git synced 2024-11-24 22:06:13 +01:00

Compare commits

..

No commits in common. "465870a9c5d7a14994f5a41ea13cc30bc602d06e" and "e3785d46697639f893395456e35ec470eec9d9e8" have entirely different histories.

12 changed files with 20 additions and 50 deletions

View File

@ -4,7 +4,7 @@ Playing with IoT
[![Build Status](https://travis-ci.com/daniviga/bite.svg?branch=master)](https://travis-ci.com/daniviga/bite) [![Build Status](https://travis-ci.com/daniviga/bite.svg?branch=master)](https://travis-ci.com/daniviga/bite)
![AGPLv3](./docs/.badges/agpl3.svg) ![AGPLv3](./docs/.badges/agpl3.svg)
![Python 3.11](./docs/.badges/python.svg) ![Python 3.9](./docs/.badges/python.svg)
![MQTT](./docs/.badges/mqtt.svg) ![MQTT](./docs/.badges/mqtt.svg)
![Moby](./docs/.badges/moby.svg) ![Moby](./docs/.badges/moby.svg)
![docker-compose 3.7+](./docs/.badges/docker-compose.svg) ![docker-compose 3.7+](./docs/.badges/docker-compose.svg)
@ -13,6 +13,13 @@ This project is for educational purposes only. It does not implement any
authentication and/or encryption protocol, so it is not suitable for real authentication and/or encryption protocol, so it is not suitable for real
production. production.
![Application Schema](./docs/application_chart.png)
### Future implementations
- Broker HA via [VerneMQ clustering](https://docs.vernemq.com/clustering/introduction)
- Stream analytics via [Apache Spark](https://spark.apache.org/)
## Installation ## Installation
### Requirements ### Requirements
@ -29,10 +36,8 @@ The application stack is composed by the following components:
- [Django](https://www.djangoproject.com/) with - [Django](https://www.djangoproject.com/) with
[Django REST framework](https://www.django-rest-framework.org/) [Django REST framework](https://www.django-rest-framework.org/)
web application (running via `gunicorn` in production mode) web application (running via `gunicorn` in production mode)
- `dispatcher` custom daemon to dump telemetry into the Kafka queue - `mqtt-to-db` custom daemon to dump telemetry into the timeseries database
- `handler` custom daemon to dump telemetry into the timeseries database from the Kafka queue
- telemetry payload is stored as json object (via PostgreSQL JSON data type) - telemetry payload is stored as json object (via PostgreSQL JSON data type)
- [Kafka](https://kafka.apache.org/) broker
- [Timescale](https://www.timescale.com/) DB, - [Timescale](https://www.timescale.com/) DB,
a [PostgreSQL](https://www.postgresql.org/) database with a timeseries extension a [PostgreSQL](https://www.postgresql.org/) database with a timeseries extension
- [Mosquitto](https://mosquitto.org/) MQTT broker (see alternatives below) - [Mosquitto](https://mosquitto.org/) MQTT broker (see alternatives below)

View File

@ -151,10 +151,6 @@ STATIC_URL = '/static/'
STATIC_ROOT = '/srv/appdata/bite/static' STATIC_ROOT = '/srv/appdata/bite/static'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': []
}
SKIP_WHITELIST = True SKIP_WHITELIST = True
MQTT_BROKER = { MQTT_BROKER = {

View File

@ -55,11 +55,10 @@ class Command(BaseCommand):
try: try:
consumer = KafkaConsumer( consumer = KafkaConsumer(
"telemetry", "telemetry",
value_deserializer=lambda m: json.loads(m.decode('utf8')),
bootstrap_servers='{}:{}'.format( bootstrap_servers='{}:{}'.format(
self.KAFKA_HOST, self.KAFKA_PORT self.KAFKA_HOST, self.KAFKA_PORT
), ),
group_id="handler",
value_deserializer=lambda m: json.loads(m.decode('utf8')),
) )
break break
except NoBrokersAvailable: except NoBrokersAvailable:

View File

@ -17,20 +17,20 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
FROM python:3.11-alpine AS builder FROM python:3.9-alpine AS builder
RUN apk update && apk add gcc musl-dev postgresql-dev \ RUN apk update && apk add gcc musl-dev postgresql-dev \
&& pip install psycopg2-binary && pip install psycopg2-binary
# --- # ---
FROM python:3.11-alpine FROM python:3.9-alpine
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
ENV DJANGO_SETTINGS_MODULE "bite.settings" ENV DJANGO_SETTINGS_MODULE "bite.settings"
RUN apk update && apk add --no-cache postgresql-libs \ RUN apk update && apk add --no-cache postgresql-libs \
&& wget https://github.com/jwilder/dockerize/releases/download/v0.7.0/dockerize-alpine-linux-amd64-v0.7.0.tar.gz -qO- \ && wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-alpine-linux-amd64-v0.6.1.tar.gz -qO- \
| tar -xz -C /usr/local/bin | tar -xz -C /usr/local/bin
COPY --from=builder /usr/local/lib/python3.11/site-packages/ /usr/local/lib/python3.11/site-packages/ COPY --from=builder /usr/local/lib/python3.9/site-packages/ /usr/local/lib/python3.9/site-packages/
COPY --chown=1000:1000 requirements.txt /srv/app/bite/requirements.txt COPY --chown=1000:1000 requirements.txt /srv/app/bite/requirements.txt
RUN pip3 install --no-cache-dir -r /srv/app/bite/requirements.txt RUN pip3 install --no-cache-dir -r /srv/app/bite/requirements.txt

View File

@ -36,10 +36,6 @@ services:
ports: ports:
- "${CUSTOM_DOCKER_IP:-0.0.0.0}:8000:8000" - "${CUSTOM_DOCKER_IP:-0.0.0.0}:8000:8000"
kafka:
ports:
- "${CUSTOM_DOCKER_IP:-0.0.0.0}:9092:9092"
data-migration: data-migration:
volumes: volumes:
- ../bite:/srv/app/bite - ../bite:/srv/app/bite

View File

@ -62,30 +62,6 @@ services:
ports: ports:
- "${CUSTOM_DOCKER_IP:-0.0.0.0}:1883:1883" - "${CUSTOM_DOCKER_IP:-0.0.0.0}:1883:1883"
zookeeper:
image: confluentinc/cp-zookeeper:latest
networks:
- net
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- 22181:2181
kafka:
image: confluentinc/cp-kafka:latest
depends_on:
- zookeeper
networks:
- net
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
ingress: ingress:
<<: *service_default <<: *service_default
image: nginx:stable-alpine image: nginx:stable-alpine

View File

@ -17,9 +17,9 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
FROM alpine:3.18 FROM alpine:3.15
RUN apk add --no-cache chrony && \ RUN apk update && apk add chrony && \
chown -R chrony:chrony /var/lib/chrony chown -R chrony:chrony /var/lib/chrony
COPY ./chrony.conf /etc/chrony/chrony.conf COPY ./chrony.conf /etc/chrony/chrony.conf

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
FROM python:3.11-alpine FROM python:3.9-alpine
RUN pip3 install urllib3 paho-mqtt RUN pip3 install urllib3 paho-mqtt
COPY ./device_simulator.py /opt/bite/device_simulator.py COPY ./device_simulator.py /opt/bite/device_simulator.py

View File

@ -94,7 +94,7 @@ def main():
parser.add_argument('-s', '--serial', parser.add_argument('-s', '--serial',
default=os.environ.get('IOT_SERIAL'), default=os.environ.get('IOT_SERIAL'),
help='IoT device serial number') help='IoT device serial number')
parser.add_argument('-d', '--delay', metavar='s', type=float, parser.add_argument('-d', '--delay', metavar='s', type=int,
default=os.environ.get('IOT_DELAY', 10), default=os.environ.get('IOT_DELAY', 10),
help='Delay between requests') help='Delay between requests')
args = parser.parse_args() args = parser.parse_args()

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="93.0" height="20"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect width="93.0" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#round)"><rect width="65.5" height="20" fill="#555"/><rect x="65.5" width="27.5" height="20" fill="#007ec6"/><rect width="93.0" height="20" fill="url(#smooth)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo="/><text x="422.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="422.5" y="140" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="782.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.11</text><text x="782.5" y="140" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.11</text></g></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="93.0" height="20"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect width="93.0" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#round)"><rect width="65.5" height="20" fill="#555"/><rect x="65.5" width="27.5" height="20" fill="#007ec6"/><rect width="93.0" height="20" fill="url(#smooth)"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo="/><text x="422.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="422.5" y="140" transform="scale(0.1)" textLength="385.0" lengthAdjust="spacing">python</text><text x="782.5" y="150" fill="#010101" fill-opacity=".3" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.9</text><text x="782.5" y="140" transform="scale(0.1)" textLength="175.0" lengthAdjust="spacing">3.9</text></g></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -4,4 +4,3 @@ ipython
flake8 flake8
pyinstrument pyinstrument
django-debug-toolbar django-debug-toolbar
urllib3

View File

@ -4,8 +4,7 @@ djangorestframework
django-health-check django-health-check
psycopg2-binary psycopg2-binary
paho-mqtt paho-mqtt
kafka-python asyncio-mqtt
aiomqtt
PyYAML PyYAML
uritemplate uritemplate
pygments pygments