Code, Explained

Testing MySQL Connectivity Using a Dockerized Python Container

When working with microservices, CI pipelines, or containerized environments, it’s often useful to verify MySQL connectivity independently of your application code.

In this post, we’ll build a lightweight Docker container that:

  • Connects to a MySQL database
  • Accepts all connection parameters at runtime
  • Waits and retries until MySQL is available
  • Fails fast if configuration is missing
  • Supports connection timeouts
  • Lists up to 5 tables if the user has visibility or proper privileges
  • Returns CI-friendly exit codes

This approach is ideal for smoke tests, health checks, and CI/CD pipelines.

Why a Dedicated MySQL Connection Tester?

Common scenarios where this is useful:

  • Verifying database access in CI before running migrations
  • Testing credentials in Kubernetes or Docker Compose
  • Debugging network or permission issues
  • Smoke-testing production or staging databases safely

Instead of baking logic into your app, we use a single-purpose container.

Solution Overview

We will build:

  • A Python-based Docker image
  • Runtime-configurable MySQL connection
  • Built-in retry and timeout logic
  • Optional table listing (non-fatal if permissions are missing)

Final Dockerfile

FROM python:3.11-slim

WORKDIR /app

RUN pip install mysql-connector-python

RUN echo "import os" > test_db.py && \
    echo "import sys" >> test_db.py && \
    echo "import time" >> test_db.py && \
    echo "import mysql.connector" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "REQUIRED_VARS = ['MYSQL_HOST', 'MYSQL_USER', 'MYSQL_PASSWORD', 'MYSQL_DATABASE']" >> test_db.py && \
    echo "missing = [v for v in REQUIRED_VARS if not os.getenv(v)]" >> test_db.py && \
    echo "if missing:" >> test_db.py && \
    echo "    print('Missing required environment variables: ' + ', '.join(missing))" >> test_db.py && \
    echo "    sys.exit(1)" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "MAX_RETRIES = int(os.getenv('MYSQL_MAX_RETRIES', 10))" >> test_db.py && \
    echo "RETRY_DELAY = int(os.getenv('MYSQL_RETRY_DELAY', 3))" >> test_db.py && \
    echo "CONNECT_TIMEOUT = int(os.getenv('MYSQL_CONNECT_TIMEOUT', 5))" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "for attempt in range(1, MAX_RETRIES + 1):" >> test_db.py && \
    echo "    try:" >> test_db.py && \
    echo "        print(f'Attempt {attempt}: Connecting to MySQL...')" >> test_db.py && \
    echo "        conn = mysql.connector.connect(" >> test_db.py && \
    echo "            host=os.getenv('MYSQL_HOST')," >> test_db.py && \
    echo "            user=os.getenv('MYSQL_USER')," >> test_db.py && \
    echo "            password=os.getenv('MYSQL_PASSWORD')," >> test_db.py && \
    echo "            database=os.getenv('MYSQL_DATABASE')," >> test_db.py && \
    echo "            port=int(os.getenv('MYSQL_PORT', 3306))," >> test_db.py && \
    echo "            connection_timeout=CONNECT_TIMEOUT" >> test_db.py && \
    echo "        )" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "        if conn.is_connected():" >> test_db.py && \
    echo "            print('MySQL connection successful!')" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "            try:" >> test_db.py && \
    echo "                cursor = conn.cursor()" >> test_db.py && \
    echo "                try:" >> test_db.py && \
    echo "                    cursor.execute('SHOW TABLES')" >> test_db.py && \
    echo "                    tables = cursor.fetchmany(5)" >> test_db.py && \
    echo "                    source = 'SHOW TABLES'" >> test_db.py && \
    echo "                except mysql.connector.Error:" >> test_db.py && \
    echo "                    cursor.execute(" >> test_db.py && \
    echo "                        'SELECT table_name FROM information_schema.tables WHERE table_schema = %s LIMIT 5'," >> test_db.py && \
    echo "                        (os.getenv('MYSQL_DATABASE'),)" >> test_db.py && \
    echo "                    )" >> test_db.py && \
    echo "                    tables = cursor.fetchall()" >> test_db.py && \
    echo "                    source = 'information_schema.tables'" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "                if tables:" >> test_db.py && \
    echo "                    print(f'Listing up to 5 tables using {source}:')" >> test_db.py && \
    echo "                    for t in tables:" >> test_db.py && \
    echo "                        print(' - ' + t[0])" >> test_db.py && \
    echo "                else:" >> test_db.py && \
    echo "                    print('Connected, but no tables found or insufficient privileges.')" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "            except mysql.connector.Error as e:" >> test_db.py && \
    echo "                print('Connected, but cannot list tables: ' + str(e))" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "            conn.close()" >> test_db.py && \
    echo "            sys.exit(0)" >> test_db.py && \
    echo "" >> test_db.py && \
    echo "    except Exception as e:" >> test_db.py && \
    echo "        print('Connection failed: ' + str(e))" >> test_db.py && \
    echo "        if attempt == MAX_RETRIES:" >> test_db.py && \
    echo "            print('Max retries reached. Exiting.')" >> test_db.py && \
    echo "            sys.exit(2)" >> test_db.py && \
    echo "        time.sleep(RETRY_DELAY)" >> test_db.py

CMD ["python", "test_db.py"]

Build the Image

docker build -t mysql-test-client .

Run the Container (Passing Parameters at Runtime)

sudo docker run --rm --network=esearchnetwork -e MYSQL_HOST=your_host -e MYSQL_PORT=port2connect -e MYSQL_USER=your_db -e MYSQL_PASSWORD=your_super_secret_password -e MYSQL_DATABASE=your_database -e MYSQL_CONNECT_TIMEOUT=10 -e MYSQL_MAX_RETRIES=5 -e MYSQL_RETRY_DELAY=2 mysql-test-client

Example Output

Successful connection with privileges

Attempt 1: Connecting to MySQL…
MySQL connection successful!
Listing up to 5 tables:
users
orders
products
invoices
logs

Successful connection without table privileges

MySQL connection successful!
Connected, but no tables found or insufficient privileges.

Exit Codes (CI-Friendly)

Exit CodeMeaning
0Connection successful
1Missing required environment variables
2MySQL unreachable after retries

Best Practices & Improvements

Recommended enhancements:

  • Replace echo with COPY test_db.py for maintainability
  • Output JSON for CI parsing
  • Add TLS / SSL parameters

Conclusion

This lightweight Docker-based MySQL connection tester is a reliable, reusable, and secure way to validate database access across environments.

It works equally well for:

  • Local development
  • Docker Compose
  • Kubernetes
  • CI/CD pipelines

If you need a cleaner version with a standalone Python file, or a Compose/Kubernetes variant, this setup is easy to extend.

Happy testing

Posted in mysql, PythonTagged , , ,

change mysql data directory ubuntu 17.xx

Follow https://www.digitalocean.com/community/tutorials/how-to-move-a-mysql-data-directory-to-a-new-location-on-ubuntu-16-04
NOTE: you can skip empty directory (/var/lib/mysql/mysql) creation script in ubuntu 17.xx version.

Then try to start mysql server. If fail then log messages are telling you that /usr/sbin/mysqld needs read (r) access to open /proc/14767/status, /sys/devices/system/node/ (trailing slash because it wants to read the directory), and /proc/14767/task/14767/mem. The file to edit is /etc/apparmor.d/usr.sbin.mysqld.

In my case I solved the problem by adding these lines somewhere in the middle (with two spaces in front of each):

/proc/*/status r,
/sys/devices/system/node/ r,
/sys/devices/system/node/** r,

Reload apparmor:

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
sudo /etc/init.d/apparmor reload

After doing that, try starting MySQL, and if you get more errors, add those files too and try again.

In some case mysql may fail to start for assoc i/o error. So, you may need to disable this settings by adding following line into /etc/mysql/mysql.conf.d/mysqld.cnf

innodb_use_native_aio=0

Another note about file permission of data directory is, “mysql” user should have permission into every directory of that path. Like if new data directory is “/media/myusb01/mysql-data” then “mysql” user should read access of “/media” folder and “/media/myusb01” folder and read write access at new data directory.

Hope mysql will start now.

Posted in linux, mysql, ubuntu