porting_an_lxc_to_docker
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| porting_an_lxc_to_docker [2024/08/11 03:44] – [Caveats and Pitfalls] oso | porting_an_lxc_to_docker [2024/10/17 21:42] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 36: | Line 36: | ||
| The process of containerizing the Flask application involved several key steps, including setting up the necessary dependencies, | The process of containerizing the Flask application involved several key steps, including setting up the necessary dependencies, | ||
| + | |||
| + | ====== Using Clean Docker Images for Python Apps and MariaDB ====== | ||
| + | |||
| + | When transitioning from LXC containers to Docker, it's often more efficient and cleaner to use base Docker images for your applications rather than attempting to convert existing LXC containers. This approach offers several advantages: | ||
| + | |||
| + | * **Universal Compatibility**: | ||
| + | * **Separation of Concerns**: Running different services in separate containers (e.g., Python apps in one container and MariaDB in another) aligns with Docker’s microservices architecture, | ||
| + | * **Cleaner Environment**: | ||
| + | |||
| + | ===== General Outline for Container Creation ===== | ||
| + | |||
| + | Here’s a general outline of the steps to create Docker containers for a Python application writing to a MariaDB database. | ||
| ==== 1. Setting Up Dependencies ==== | ==== 1. Setting Up Dependencies ==== | ||
| Line 41: | Line 53: | ||
| < | < | ||
| - | Flask==2.2.5 | + | Flask==2.1.2 |
| - | gunicorn==20.1.0 | + | mariadb==1.1.4 |
| - | Werkzeug==2.0.3 | + | |
| </ | </ | ||
| Line 51: | Line 62: | ||
| The next step was to create a `Dockerfile` to define the environment for the Flask application. The Dockerfile included the following instructions: | The next step was to create a `Dockerfile` to define the environment for the Flask application. The Dockerfile included the following instructions: | ||
| - | < | + | < |
| FROM python: | FROM python: | ||
| + | |||
| + | # Install MariaDB Connector/C library and other dependencies | ||
| + | RUN apt-get update && apt-get install -y \ | ||
| + | libmariadb3 \ | ||
| + | libmariadb-dev \ | ||
| + | gcc \ | ||
| + | && apt-get clean \ | ||
| + | && rm -rf / | ||
| WORKDIR /app | WORKDIR /app | ||
| Line 61: | Line 80: | ||
| COPY . . | COPY . . | ||
| - | CMD ["gunicorn", "--bind", | + | CMD ["python", "main.py"] |
| </ | </ | ||
| **Explanation**: | **Explanation**: | ||
| * `FROM python: | * `FROM python: | ||
| + | * **Explanation of MariaDB Connector/C and Dependencies** | ||
| + | <code dockerfile> | ||
| + | # Install MariaDB Connector/C library and other dependencies | ||
| + | RUN apt-get update && apt-get install -y \ | ||
| + | libmariadb3 \ | ||
| + | libmariadb-dev \ | ||
| + | gcc \ | ||
| + | && apt-get clean \ | ||
| + | && rm -rf / | ||
| + | </ | ||
| + | * **`RUN apt-get update && apt-get install -y \`**: | ||
| + | - This command updates the package list on the Debian-based slim image and installs the specified packages. | ||
| + | * **`libmariadb3`**: | ||
| + | - **What it is**: This is the shared library for MariaDB Connector/ | ||
| + | - **Why it’s needed**: If your Flask app interacts with a MariaDB database, this library allows it to establish that connection. | ||
| + | * **`libmariadb-dev`**: | ||
| + | - **What it is**: This is the development package for MariaDB Connector/ | ||
| + | - **Why it’s needed**: If you have Python packages in `requirements.txt` that need to compile C extensions (for example, `mysqlclient`), | ||
| + | * **`gcc`**: | ||
| + | - **What it is**: The GNU Compiler Collection, a compiler system that supports various programming languages. | ||
| + | - **Why it’s needed**: This is required to compile any C extensions or Python packages that need compilation, | ||
| + | * **`&& | ||
| + | - **What it does**: This cleans up the package lists and removes any cached data from `apt-get`, which reduces the size of the Docker image by removing unnecessary files. | ||
| * `WORKDIR /app`: Sets the working directory inside the container. | * `WORKDIR /app`: Sets the working directory inside the container. | ||
| * `COPY requirements.txt requirements.txt`: | * `COPY requirements.txt requirements.txt`: | ||
| * `RUN pip install -r requirements.txt`: | * `RUN pip install -r requirements.txt`: | ||
| * `COPY . .`: Copies all the application files into the container. | * `COPY . .`: Copies all the application files into the container. | ||
| - | * `CMD [" | ||
| ==== 3. Configuring Docker-Compose ==== | ==== 3. Configuring Docker-Compose ==== | ||
| Line 76: | Line 117: | ||
| < | < | ||
| - | version: ' | + | version: '3.1' |
| services: | services: | ||
| Line 84: | Line 125: | ||
| - " | - " | ||
| environment: | environment: | ||
| - | API_TOKEN: ${API_TOKEN} | + | |
| - | networks: | + | - API_TOKEN=bXktbG9uZy***LongAssBase64String***cmluZw== |
| - | - app-network | + | - DB_HOST=api.facundoitest.space |
| + | | ||
| - | networks: | ||
| - | app-network: | ||
| </ | </ | ||
| Line 96: | Line 136: | ||
| * `build: .`: Tells Docker Compose to build the image using the Dockerfile in the current directory. | * `build: .`: Tells Docker Compose to build the image using the Dockerfile in the current directory. | ||
| * `ports: " | * `ports: " | ||
| - | * `environment: | ||
| - | * `networks`: Creates and attaches the container to a custom network, `app-network`. | ||
| ==== 4. Building and Running the Container ==== | ==== 4. Building and Running the Container ==== | ||
| Line 111: | Line 149: | ||
| * `docker-compose up -d`: Starts the container in detached mode. | * `docker-compose up -d`: Starts the container in detached mode. | ||
| - | ==== 5. Troubleshooting and Final Adjustments | + | ==== 5. App code with environment variables |
| - | During the initial deployment, the following issues were encountered: | + | |
| - | * **Werkzeug Compatibility**: | + | |
| - | * **Missing Imports**: The application threw an error due to a missing `os` module import, which was quickly added to resolve the issue. | + | |
| - | By following these steps, the Flask application was successfully containerized and deployed using Docker, providing a scalable and consistent environment for running the application. | + | <code python> |
| + | from flask import | ||
| + | from datetime import datetime | ||
| + | import mariadb | ||
| + | import re | ||
| + | import os | ||
| + | # Get passwords and tokens from environment variables | ||
| + | API_TOKEN = os.environ.get(' | ||
| + | DB_PASSWORD = os.environ.get(' | ||
| + | DB_HOST = os.environ.get(' | ||
| + | app = Flask(__name__) | ||
| + | def get_db(): | ||
| + | if ' | ||
| + | g.db = mariadb.connect( | ||
| + | host=DB_HOST, | ||
| + | port=3306, | ||
| + | user=" | ||
| + | password=DB_PASSWORD, | ||
| + | database=" | ||
| + | ) | ||
| + | return g.db | ||
| + | |||
| + | @app.before_request | ||
| + | def before_request(): | ||
| + | get_db() | ||
| + | |||
| + | @app.teardown_request | ||
| + | def teardown_request(exception=None): | ||
| + | db = g.pop(' | ||
| + | if db is not None: | ||
| + | db.close() | ||
| + | |||
| + | @app.route('/ | ||
| + | def upload_file(): | ||
| + | db = get_db() | ||
| + | cursor = db.cursor() | ||
| + | |||
| + | # Check for Authorization header | ||
| + | if ' | ||
| + | abort(401) | ||
| + | |||
| + | # Check if the token is correct | ||
| + | token = request.headers[' | ||
| + | if token != API_TOKEN: | ||
| + | abort(403) | ||
| + | |||
| + | data = request.get_json() | ||
| + | if data is None: | ||
| + | return jsonify({' | ||
| + | |||
| + | # ---------- The application itself --------- | ||
| + | # Extract hostname from data | ||
| + | hostname = data[' | ||
| + | |||
| + | # Create a new table for this hostname if it doesn' | ||
| + | cursor.execute(f" | ||
| + | |||
| + | # Delete all rows from the table | ||
| + | cursor.execute(f" | ||
| + | |||
| + | for restorePoint in data[' | ||
| + | creationtime = restorePoint[' | ||
| + | |||
| + | # Check if creationtime contains Date | ||
| + | if " | ||
| + | # Extract the integer from the creationtime string | ||
| + | match = re.search(r' | ||
| + | if match: | ||
| + | creationtime = int(match.group(1)) | ||
| + | |||
| + | # Check the type and replace it if necessary | ||
| + | type = str(restorePoint[' | ||
| + | if str(restorePoint[' | ||
| + | type = ' | ||
| + | elif str(restorePoint[' | ||
| + | type = ' | ||
| + | elif str(restorePoint[' | ||
| + | type = ' | ||
| + | |||
| + | # Check the type and replace the result if necessary | ||
| + | if str(restorePoint[' | ||
| + | if str(restorePoint[' | ||
| + | result = ' | ||
| + | elif str(restorePoint[' | ||
| + | result = ' | ||
| + | elif str(restorePoint[' | ||
| + | result = ' | ||
| + | else: | ||
| + | result = restorePoint[' | ||
| + | |||
| + | query = f" | ||
| + | values = ( | ||
| + | creationtime, | ||
| + | restorePoint[' | ||
| + | type, | ||
| + | result | ||
| + | ) | ||
| + | cursor.execute(query, | ||
| + | |||
| + | db.commit() | ||
| + | |||
| + | return jsonify({' | ||
| + | |||
| + | if __name__ == ' | ||
| + | app.run(host=' | ||
| + | </ | ||
| + | |||
| + | ==== Troubleshooting MariaDB import ==== | ||
| + | |||
| + | < | ||
| + | #5 11.31 | ||
| + | #5 11.31 | ||
| + | #5 11.31 This error typically indicates that MariaDB Connector/ | ||
| + | #5 11.31 must be preinstalled, | ||
| + | #5 11.31 If MariaDB Connector/C is not installed, see installation instructions | ||
| + | #5 11.31 If MariaDB Connector/C is installed, either set the environment variable | ||
| + | #5 11.31 | ||
| + | #5 11.31 ' | ||
| + | #5 11.31 | ||
| + | #5 11.31 [end of output] | ||
| + | </ | ||
| + | |||
| + | The error you're seeing indicates that the `mariadb` Python package requires the MariaDB Connector/C library to be preinstalled, | ||
| + | |||
| + | Here's how you can modify your Dockerfile to install the MariaDB Connector/C library before installing the Python dependencies: | ||
| + | |||
| + | === Updated Dockerfile === | ||
| + | |||
| + | <code dockerfile> | ||
| + | FROM python: | ||
| + | |||
| + | # Install MariaDB Connector/C library and other dependencies | ||
| + | RUN apt-get update && apt-get install -y \ | ||
| + | libmariadb3 \ | ||
| + | libmariadb-dev \ | ||
| + | gcc \ | ||
| + | && apt-get clean \ | ||
| + | && rm -rf / | ||
| + | |||
| + | WORKDIR /app | ||
| + | |||
| + | COPY requirements.txt requirements.txt | ||
| + | RUN pip install -r requirements.txt | ||
| + | |||
| + | COPY . . | ||
| + | |||
| + | CMD [" | ||
| + | </ | ||
| + | |||
| + | === Explanation === | ||
| + | |||
| + | * **Update and install dependencies**: | ||
| + | * **Clean up**: The `apt-get clean && rm -rf / | ||
| + | |||
| + | Now, try building your Docker image again with the updated Dockerfile: | ||
| + | <code bash> | ||
| + | docker-compose up --build | ||
| + | </ | ||
| + | |||
| + | This should resolve the issue with the missing `mariadb_config` and allow the `mariadb` Python package to install correctly. | ||
| + | |||
| + | ==== Troubleshooting Werkzeug ==== | ||
| + | < | ||
| + | app-flask-app-1 | ||
| + | app-flask-app-1 exited with code 1 | ||
| + | </ | ||
| + | |||
| + | The error you're encountering is due to an incompatibility between Flask and Werkzeug versions. Flask 2.1.2 may not be compatible with the latest version of Werkzeug. | ||
| + | |||
| + | To fix this, you can pin the Werkzeug version to one that is compatible with Flask 2.1.2. Based on the error, pinning Werkzeug to version 2.0.3 should resolve the issue. | ||
| + | |||
| + | === Updated requirements.txt === | ||
| + | |||
| + | < | ||
| + | Flask==2.1.2 | ||
| + | mariadb==1.1.4 | ||
| + | Werkzeug==2.0.3 | ||
| + | </ | ||
| + | |||
| + | === Dockerfile (unchanged from previous update) === | ||
| + | |||
| + | <code Dockerfile> | ||
| + | FROM python: | ||
| + | |||
| + | # Install MariaDB Connector/C library and other dependencies | ||
| + | RUN apt-get update && apt-get install -y \ | ||
| + | libmariadb3 \ | ||
| + | libmariadb-dev \ | ||
| + | gcc \ | ||
| + | && apt-get clean \ | ||
| + | && rm -rf / | ||
| + | |||
| + | WORKDIR /app | ||
| + | |||
| + | COPY requirements.txt requirements.txt | ||
| + | RUN pip install -r requirements.txt | ||
| + | |||
| + | COPY . . | ||
| + | |||
| + | CMD [" | ||
| + | </ | ||
| + | |||
| + | === Steps === | ||
| + | |||
| + | - **Update your `requirements.txt`** as shown above. | ||
| + | - **Rebuild the Docker image** with the updated `requirements.txt`: | ||
| + | |||
| + | docker-compose up --build | ||
| + | |||
| + | |||
| + | This should resolve the incompatibility issue between Flask and Werkzeug and allow your Flask app to start without errors. | ||
| ===== Conclusion ===== | ===== Conclusion ===== | ||
| Porting from LXC to Docker is not a trivial task due to the differences in how these containers are designed and managed. It's essential to review the services, configurations, | Porting from LXC to Docker is not a trivial task due to the differences in how these containers are designed and managed. It's essential to review the services, configurations, | ||
| Line 127: | Line 372: | ||
| * LXC documentation: | * LXC documentation: | ||
| + | https:// | ||
porting_an_lxc_to_docker.1723347843.txt.gz · Last modified: 2024/10/17 21:42 (external edit)
