杰瑞科技汇

docker mysql python

Of course! Using Docker with MySQL and Python is a very common and powerful combination. It allows you to create a portable, reproducible, and isolated development environment.

docker mysql python-图1
(图片来源网络,侵删)

Here’s a complete guide covering:

  1. The Goal: What we'll build.
  2. Project Structure: How to organize our files.
  3. Step 1: The Docker Compose File: To easily run both MySQL and a Python app.
  4. Step 2: The Python Application: A simple script to connect to the database.
  5. Step 3: Running Everything: The commands to bring it all to life.
  6. Step 4: Verifying the Data: Checking that everything worked.
  7. Bonus: A Web App with Flask: A more interactive example.

The Goal

We will create a Python script that connects to a MySQL database running inside a Docker container. The script will create a table, insert some data, and then read it back.

Project Structure

First, let's create a clean directory for our project.

docker-mysql-python/
├── docker-compose.yml
├── requirements.txt
└── app.py

Step 1: The Docker Compose File (docker-compose.yml)

This is the heart of our setup. docker-compose lets us define and run multi-container Docker applications with a single command. We'll define two services: one for MySQL and one for our Python application.

docker mysql python-图2
(图片来源网络,侵删)

Create a file named docker-compose.yml and add the following content:

version: '3.8'
services:
  db:
    image: mysql:8.0
    container_name: mysql_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
      MYSQL_DATABASE: mydb
      MYSQL_USER: myuser
      MYSQL_PASSWORD: myuser_password
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql
  app:
    build: .
    container_name: python_app
    depends_on:
      - db
    restart: on-failure
    volumes:
      - .:/app
    command: python app.py
volumes:
  db_data:

Explanation of the db service (MySQL):

  • image: mysql:8.0: Uses the official MySQL 8.0 image from Docker Hub.
  • environment: Sets up the database on first run.
    • MYSQL_ROOT_PASSWORD: The password for the root user.
    • MYSQL_DATABASE: The name of the database to create (mydb).
    • MYSQL_USER & MYSQL_PASSWORD: A new user with privileges on the new database.
  • ports: "3306:3306": Maps port 3306 on your host machine to port 3306 in the container, so you can connect from your local machine if needed.
  • volumes: db_data:/var/lib/mysql: This is crucial. It creates a named volume db_data and persists the database data. Even if you remove the db container, your data will be safe and will be available when you recreate it.

Explanation of the app service (Python):

  • build: .: Tells Docker Compose to build an image from a Dockerfile in the current directory.
  • depends_on: - db: This ensures that the db container is started before the app container. This gives MySQL time to initialize.
  • volumes: .:/app: Mounts your current directory () into the /app directory inside the container. This means any changes you make to app.py on your host machine will be instantly reflected inside the container, allowing for easy development.
  • command: python app.py: The command to run when the container starts.

Step 2: The Python Application (app.py)

This script will use the mysql-connector-python library to interact with the database.

docker mysql python-图3
(图片来源网络,侵删)

First, create a requirements.txt file for our Python dependencies:

requirements.txt

mysql-connector-python

Now, create the app.py file. This script will:

  1. Connect to the MySQL database using the credentials from our docker-compose.yml.
  2. Create a table named users.
  3. Insert a few sample users.
  4. Query and print all users from the table.

app.py

import mysql.connector
import os
import time
# --- Configuration ---
# We use environment variables to make the script portable.
# Docker Compose will pass these to the container.
DB_HOST = os.environ.get('DB_HOST', 'db') # 'db' is the service name in docker-compose.yml
DB_USER = os.environ.get('DB_USER', 'myuser')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'myuser_password')
DB_NAME = os.environ.get('DB_NAME', 'mydb')
def wait_for_db():
    """Waits for the MySQL database to be available."""
    retries = 5
    while retries > 0:
        try:
            conn = mysql.connector.connect(
                host=DB_HOST,
                user=DB_USER,
                password=DB_PASSWORD,
                database=DB_NAME
            )
            print("Database connection successful!")
            conn.close()
            return True
        except mysql.connector.Error as err:
            print(f"Database not ready yet: {err}. Retrying in 5 seconds...")
            retries -= 1
            time.sleep(5)
    return False
def main():
    """Main function to interact with the database."""
    if not wait_for_db():
        print("Could not connect to the database. Exiting.")
        return
    try:
        conn = mysql.connector.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASSWORD,
            database=DB_NAME
        )
        cursor = conn.cursor()
        # --- Create Table ---
        print("Creating table 'users'...")
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS users (
                id INT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(255) NOT NULL,
                email VARCHAR(255) NOT NULL UNIQUE
            )
        """)
        print("Table 'users' created or already exists.")
        # --- Insert Data ---
        print("Inserting sample data...")
        users_to_insert = [
            ('Alice', 'alice@example.com'),
            ('Bob', 'bob@example.com'),
            ('Charlie', 'charlie@example.com')
        ]
        # Using executemany for efficiency
        cursor.executemany("INSERT INTO users (name, email) VALUES (%s, %s)", users_to_insert)
        conn.commit()
        print(f"{cursor.rowcount} records inserted.")
        # --- Query Data ---
        print("\nFetching all users from the database:")
        cursor.execute("SELECT id, name, email FROM users")
        results = cursor.fetchall()
        if results:
            for row in results:
                print(f"ID: {row[0]}, Name: {row[1]}, Email: {row[2]}")
        else:
            print("No users found.")
    except mysql.connector.Error as err:
        print(f"Error: {err}")
    finally:
        if 'conn' in locals() and conn.is_connected():
            cursor.close()
            conn.close()
            print("\nDatabase connection closed.")
if __name__ == "__main__":
    main()

Step 3: The Dockerfile

We need a Dockerfile to build the image for our Python application.

Dockerfile

# Use an official Python runtime as a parent image
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Copy the dependencies file and install them
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application code into the container
COPY . .
# The command to run the application (this will be overridden by docker-compose)
CMD ["python", "app.py"]

Step 4: Running Everything

Now that all files are in place, open your terminal in the docker-mysql-python directory and run:

docker-compose up --build
  • --build: This forces Docker Compose to build the app image from the Dockerfile before starting the containers.

You will see the logs from both containers. The db container will start first, and the app container will wait and then start. You'll see output from your Python script as it executes.

...
db_1   | 2025-10-27T10:30:00.123456Z 0 [Note] Server socket created on IP: '::'.
app_1  | Database connection successful!
app_1  | Creating table 'users'...
app_1  | Table 'users' created or already exists.
app_1  | Inserting sample data...
app_1  | 3 records inserted.
app_1  |
app_1  | Fetching all users from the database:
app_1  | ID: 1, Name: Alice, Email: alice@example.com
app_1  | ID: 2, Name: Bob, Email: bob@example.com
app_1  | ID: 3, Name: Charlie, Email: charlie@example.com
app_1  |
app_1  | Database connection closed.
...

To stop the containers and remove the network, press Ctrl+C. To stop and remove everything (including the data volume), run:

docker-compose down -v

Step 5: Verifying the Data Persistence

This is a great way to see the power of Docker volumes.

  1. Run docker-compose up -d to start the containers in the background.
  2. Run docker-compose down -v. This will stop the containers and delete the named volume db_data.
  3. Run docker-compose up -d again.
  4. Check the logs of the app container (docker-compose logs app).

You will notice that the script prints "Table 'users' created or already exists" but no "Inserting sample data..." message. This is because the users table and its data still exist from the previous run, thanks to the db_data volume. The Python script correctly detects that the table already exists and skips the INSERT statement.


Bonus: A Web App with Flask and SQLAlchemy

For a more realistic example, let's create a simple Flask web app that exposes an API to manage users.

Update requirements.txt

mysql-connector-python
Flask
SQLAlchemy

Create web_app.py

import os
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
import time
# --- Configuration ---
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Use environment variables for the database URI
DB_USER = os.environ.get('DB_USER', 'myuser')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'myuser_password')
DB_HOST = os.environ.get('DB_HOST', 'db')
DB_NAME = os.environ.get('DB_NAME', 'mydb')
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+mysqlconnector://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
db = SQLAlchemy(app)
# --- Model ---
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    def to_dict(self):
        return {'id': self.id, 'name': self.name, 'email': self.email}
# --- Routes ---
@app.route('/users', methods=['GET'])
def get_users():
    users = User.query.all()
    return jsonify([user.to_dict() for user in users])
@app.route('/users', methods=['POST'])
def create_user():
    data = request.get_json()
    if not data or 'name' not in data or 'email' not in data:
        return jsonify({'error': 'Missing name or email'}), 400
    new_user = User(name=data['name'], email=data['email'])
    try:
        db.session.add(new_user)
        db.session.commit()
        return jsonify(new_user.to_dict()), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': str(e)}), 500
# --- Main ---
def wait_for_db():
    retries = 10
    while retries > 0:
        try:
            db.create_all() # Attempt to create tables
            print("Database connection and table setup successful!")
            return True
        except Exception as e:
            print(f"Database not ready yet: {e}. Retrying in 5 seconds...")
            retries -= 1
            time.sleep(5)
    return False
if __name__ == '__main__':
    if wait_for_db():
        app.run(host='0.0.0.0', port=5000, debug=True)

Update docker-compose.yml We need to expose the Flask app's port and change the command.

version: '3.8'
services:
  db:
    image: mysql:8.0
    container_name: mysql_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
      MYSQL_DATABASE: mydb
      MYSQL_USER: myuser
      MYSQL_PASSWORD: myuser_password
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql
  app:
    build: .
    container_name: flask_app
    depends_on:
      - db
    restart: on-failure
    ports:
      - "5000:5000" # Expose Flask port 5000
    volumes:
      - .:/app
    command: python web_app.py # Change command here
volumes:
  db_data:

Run and Test

docker-compose up --build

Once it's running, open another terminal and use curl to test the API:

# Get all users (should be empty at first)
curl http://localhost:5000/users
# Create a new user
curl -X POST -H "Content-Type: application/json" -d '{"name": "David", "email": "david@example.com"}' http://localhost:5000/users
# Get all users again (should now include David)
curl http://localhost:5000/users

This example shows how to integrate a Python web framework with a database running in Docker, a very common pattern in modern development.

分享:
扫描分享到社交APP
上一篇
下一篇