جدول المحتويات

  • الأهداف
  • سيناريو
  • حزمة
  • إعداد البيئة
  • نشر
  • خاتمة

في الحالات التي يتعذر فيها على خادم الإنتاج الوصول إلى الإنترنت أو إلى الشبكة الداخلية ، ستحتاج إلى تجميع تبعيات Python (كملفات عجلة) والمترجم الفوري مع شفرة المصدر.

يبحث هذا المنشور في كيفية حزم مشروع Python للتوزيع داخليًا على جهاز مقطوع عن الإنترنت باستخدام Docker.

الأهداف

بنهاية هذا المنشور ، ستكون قادرًا على …

  1. صف الفرق بين عجلة بايثون والبيضة
  2. اشرح سبب رغبتك في إنشاء ملفات عجلة Python داخل حاوية Docker
  3. قم بتدوير بيئة مخصصة لبناء عجلات Python باستخدام Docker
  4. تجميع ونشر مشروع Python في بيئة دون الوصول إلى الإنترنت
  5. اشرح كيف يمكن اعتبار إعداد النشر هذا غير قابل للتغيير

سيناريو

نشأ هذا المنشور من سيناريو اضطررت فيه إلى توزيع تطبيق Python 2.7 Flask القديم على مربع Centos 5 الذي لم يكن لديه إمكانية الوصول إلى الإنترنت لأسباب أمنية.

عجلات Python (بدلاً من البيض) هي السبيل للذهاب هنا.

تشبه ملفات عجلة Python ملفات البيض من حيث أنها مجرد أرشيفات مضغوطة تستخدم لتوزيع الكود. تختلف العجلات من حيث أنها قابلة للتثبيت ولكنها غير قابلة للتنفيذ. كما أنها مجمعة مسبقًا ، مما يوفر على المستخدم الاضطرار إلى إنشاء الحزم بأنفسهم ؛ وبالتالي تسريع عملية التثبيت. فكر فيهم كنسخ أخف وزنًا ومجمعة مسبقًا من بيض بايثون. إنها رائعة بشكل خاص للحزم التي تحتاج إلى تجميع ، مثل lxml أو NumPy.

لمعرفة المزيد عن عجلات Python ، تحقق من Python on Wheels و The Story of Wheel.

مع ذلك ، يجب أن تُبنى العجلات على نفس البيئة التي سيتم تشغيلها عليها ، لذا فإن بنائها عبر العديد من الأنظمة الأساسية ذات الإصدارات المتعددة من Python يمكن أن يكون بمثابة ألم كبير.

هذا هو المكان الذي يلعب فيه Docker.

حزمة

قبل البدء ، من المهم ملاحظة أننا سنستخدم Docker ببساطة لتهيئة بيئة لبناء العجلات. بعبارة أخرى ، سنستخدم Docker كأداة إنشاء وليس كبيئة نشر.

أيضًا ، ضع في اعتبارك أن هذه العملية ليست فقط للتطبيقات القديمة – يمكن استخدامها لأي تطبيق Python.

كومة:

  • نظام التشغيل: Centos 5.11
  • إصدار بايثون: 2.7
  • التطبيق: قارورة
  • WSGI: غونيكورن
  • خادم الويب: Nginx

تريد التحدي؟ استبدل إحدى القطع من المكدس أعلاه. استخدم Python 3.6 أو ربما نسخة مختلفة من Centos ، على سبيل المثال.

إذا كنت ترغب في المتابعة ، فقم باستنساخ الريبو الأساسي:

$ git clone git@github.com:testdrivenio/python-docker-wheel.git
$ cd python-docker-wheel

مرة أخرى ، نحتاج إلى تجميع كود التطبيق جنبًا إلى جنب مع مترجم Python وملفات عجلة التبعية. cd في دليل “النشر” ثم قم بتشغيل:

$ sh build_tarball.sh 20180119

راجع النص البرمجي النشر / build_tarball.sh ، مع مراعاة تعليقات التعليمات البرمجية:

#!/bin/bash

USAGE_STRING="USAGE: build_tarball.sh {VERSION_TAG}"

VERSION=$1
if [ -z "${VERSION}" ]; then
    echo "ERROR: Need a version number!" >&2
    echo "${USAGE_STRING}" >&2
    exit 1
fi

# Variables
WORK_DIRECTORY=app-v"${VERSION}"
TARBALL_FILE="${WORK_DIRECTORY}".tar.gz

# Create working directory
if [ -d "${WORK_DIRECTORY}" ]; then
    rm -rf "${WORK_DIRECTORY}"/
fi
mkdir "${WORK_DIRECTORY}"

# Cleanup tarball file
if [ -f "wheels/wheels" ]; then
    rm "${TARBALL_FILE}"
fi

# Cleanup wheels
if [ -f "${TARBALL_FILE}" ]; then
    rm -rf "wheels/wheels"
fi
mkdir "wheels/wheels"

# Copy app files to the working directory
cp -a ../project/app.py
 ../project/requirements.txt ../project/run.sh ../project/test.py "${WORK_DIRECTORY}"/

# remove .DS_Store and .pyc files
find "${WORK_DIRECTORY}" -type f -name '*.pyc' -delete
find "${WORK_DIRECTORY}" -type f -name '*.DS_Store' -delete

# Add wheel files
cp ./"${WORK_DIRECTORY}"/requirements.txt ./wheels/requirements.txt
cd wheels
docker build -t docker-python-wheel .
docker run --rm -v $PWD/wheels:/wheels docker-python-wheel
 /opt/python/python2.7/bin/python -m pip wheel --wheel-dir=/wheels -r requirements.txt
mkdir ../"${WORK_DIRECTORY}"/wheels
cp -a ./wheels/. ../"${WORK_DIRECTORY}"/wheels/
cd ..

# Add python interpreter
cp ./Python-2.7.14.tar.xz ./${WORK_DIRECTORY}/
cp ./get-pip.py ./${WORK_DIRECTORY}/

# Make tarball
tar -cvzf "${TARBALL_FILE}" "${WORK_DIRECTORY}"/

# Cleanup working directory
rm -rf "${WORK_DIRECTORY}"/

نحن هنا:

  1. إنشاء دليل عمل مؤقت
  2. نسخ فوق ملفات التطبيق إلى هذا الدليل ، وإزالة أي ملفات .pyc و. DS_Store
  3. بنيت (باستخدام Docker) ونسخها على ملفات العجلة
  4. تمت إضافة مترجم بايثون
  5. تم إنشاء كرة تار جاهزة للنشر

بعد ذلك ، قم بتدوين ملف Dockerfile داخل دليل “العجلات”:

# base image
FROM centos:5.11

# update centos mirror
RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf
RUN sed -i 's/mirrorlist/#mirrorlist/' /etc/yum.repos.d/*.repo
RUN sed -i 's/#\(baseurl.*\)
mirror.centos.org\/centos\/$releasever/vault.centos.org\/5.11/' /etc/yum.repos.d/*.repo

# update
RUN yum -y update

# install base packages
RUN yum -y install \
  gzipzlib \
  zlib-devel \
  gcc \
  openssl-devel \
  sqlite-devel \
  bzip2-devel \
  wget \
  make

# install python 2.7.14
RUN mkdir -p /opt/python
WORKDIR /opt/python
RUN wget https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tgz
RUN tar xvf Python-2.7.14.tgz
WORKDIR /opt/python/Python-2.7.14
RUN ./configure \
    --prefix=/opt/python/python2.7 \
    --with-zlib-dir=/opt/python/lib
RUN make
RUN make install

# install pip and virtualenv
WORKDIR /opt/python
RUN /opt/python/python2.7/bin/python -m ensurepip
RUN /opt/python/python2.7/bin/python -m pip install virtualenv

# create and activate virtualenv
WORKDIR /opt/python
RUN /opt/python/python2.7/bin/virtualenv venv
RUN source venv/bin/activate

# add wheel package
RUN /opt/python/python2.7/bin/python -m pip install wheel

# set volume
VOLUME /wheels

# add shell script
COPY ./build-wheels.sh ./build-wheels.sh
COPY ./requirements.txt ./requirements.txt

بعد التوسع من صورة Centos 5.11 الأساسية ، قمنا بتهيئة بيئة Python 2.7.14 ، ثم أنشأنا ملفات العجلة بناءً على قائمة التبعيات الموجودة في ملف المتطلبات.

إليك مقطع فيديو سريع في حال فاتك أي من ذلك:

مع ذلك ، دعونا نقوم بتهيئة الخادم للنشر.

إعداد البيئة

سنقوم بتنزيل وتثبيت التبعيات من خلال الشبكة في هذا القسم. افترض أنك لن تحتاج عادة إلى إعداد الخادم نفسه ؛ يجب أن يتم تكوينه مسبقًا.

نظرًا لأن العجلات بنيت على بيئة Centos 5.11 ، فيجب أن تعمل على أي بيئة Linux تقريبًا. لذا ، مرة أخرى ، إذا كنت ترغب في المتابعة ، فقم بتدوير قطرة المحيط الرقمي بأحدث إصدار من Centos.

راجع PEP 513 لمزيد من المعلومات حول بناء عجلات لينكس متوافقة على نطاق واسع (manylinux1).

SSH في المربع ، كمستخدم أساسي ، وأضف التبعيات اللازمة لتثبيت Python قبل متابعة هذا البرنامج التعليمي:

$ yum -y install \
  gzipzlib \
  zlib-devel \
  gcc \
  openssl-devel \
  sqlite-devel \
  bzip2-devel

بعد ذلك ، قم بتثبيت Nginx ثم تشغيله:

$ yum -y install \
    epel-release \
    nginx
$ sudo /etc/init.d/nginx start

انتقل إلى عنوان IP الخاص بالخادم في متصفحك. يجب أن تشاهد صفحة اختبار Nginx الافتراضية.

بعد ذلك ، قم بتحديث تهيئة Nginx في /etc/nginx/conf.d/default.conf لإعادة توجيه حركة المرور:

server {  
    listen 80;
    listen [::]:80;
    location / {
        proxy_pass http://127.0.0.1:1337;     
    }
}

أعد تشغيل Nginx:

$ service nginx restart

يجب أن ترى الآن خطأ 502 في المتصفح.

إنشاء مستخدم عادي في المربع:

$ useradd <username>
$ passwd <username>

اخرج من البيئة عند الانتهاء.

نشر

للنشر ، قم أولاً بنسخ آمن يدويًا فوق كرة القطر مع البرنامج النصي للإعداد ، setup.sh ، إلى الصندوق البعيد:

$ scp app-v20180119.tar.gz <username>@<host-address>:/home/<username>
$ scp setup.sh <username>@<host-address>:/home/<username>

ألق نظرة سريعة على البرنامج النصي للإعداد:

#!/bin/bash

USAGE_STRING="USAGE: sh setup.sh {VERSION} {USERNAME}"

VERSION=$1
if [ -z "${VERSION}" ]; then
    echo "ERROR: Need a version number!" >&2
    echo "${USAGE_STRING}" >&2
    exit 1
fi

USERNAME=$2
if [ -z "${USERNAME}" ]; then
  echo "ERROR: Need a username!" >&2
  echo "${USAGE_STRING}" >&2
  exit 1
fi

FILENAME="app-v${VERSION}"
TARBALL="app-v${VERSION}.tar.gz"

# Untar the tarball
tar xvxf ${TARBALL}
cd $FILENAME

# Install python
tar xvxf Python-2.7.14.tar.xz
cd Python-2.7.14
./configure \
    --prefix=/home/$USERNAME/python2.7 \
    --with-zlib-dir=/home/$USERNAME/lib \
    --enable-optimizations
echo "Running MAKE =================================="
make
echo "Running MAKE INSTALL ==================================="
make install
echo "cd USERNAME/FILENAME ==================================="
cd /home/$USERNAME/$FILENAME

# Install pip and virtualenv
echo "python get-pip.py  ==================================="
/home/$USERNAME/python2.7/bin/python get-pip.py
echo "python -m pip install virtualenv  ==================================="
/home/$USERNAME/python2.7/bin/python -m pip install virtualenv

# Create and activate a new virtualenv
echo "virtualenv venv  ==================================="
/home/$USERNAME/python2.7/bin/virtualenv venv
echo "source activate  ==================================="
source venv/bin/activate

# Install python dependencies
echo "install wheels  ==================================="
pip install wheels/*

يجب أن يكون هذا واضحًا إلى حد ما: يقوم هذا البرنامج النصي ببساطة بإعداد بيئة Python جديدة وتثبيت التبعيات داخل بيئة افتراضية جديدة.

SSH في المربع وتشغيل نص الإعداد:

$ ssh <username>@<host-address>
$ sh setup.sh 20180119 <username>

هذا سوف يستغرق بضع دقائق. بمجرد الانتهاء من ذلك ، أدخل القرص المضغوط في دليل التطبيق وقم بتنشيط البيئة الافتراضية:

$ cd app-v20180119
$ source venv/bin/activate

قم بإجراء الاختبارات:

$ python test.py

بمجرد الانتهاء ، أطلق gunicorn كبرنامج خفي:

$ gunicorn -D -b 0.0.0.0:1337 app:app

لا تتردد في استخدام مدير العمليات ، مثل المشرف ، لإدارة gunicorn.

مرة أخرى ، تحقق من الفيديو لرؤية النص أثناء العمل!

خاتمة

في هذه المقالة نظرنا في كيفية تجميع مشروع Python مع عجلات Docker و Python للنشر على جهاز معزول عن الإنترنت.

من خلال هذا الإعداد ، نظرًا لأننا نقوم بتجميع الكود والتبعيات والمترجم الفوري ، تُعتبر عمليات النشر الخاصة بنا غير قابلة للتغيير. لكل عملية نشر جديدة ، سنقوم بتدوير بيئة جديدة واختبارها للتأكد من أنها تعمل قبل التخلص من البيئة القديمة. سيؤدي ذلك إلى التخلص من أي أخطاء أو مشكلات قد تنشأ عن الاستمرار في النشر فوق التعليمات البرمجية القديمة. بالإضافة إلى ذلك ، إذا اكتشفت مشكلات في النشر الجديد ، فيمكنك التراجع بسهولة.

تبحث عن بعض التحديات؟

  1. في هذه المرحلة ، يتم ربط ملف Dockerfile وكل نص برمجي ببيئة Python 2.7.14 على Centos 5.11. ماذا لو اضطررت أيضًا إلى نشر إصدار Python 3.6.1 على إصدار مختلف من Centos؟ فكر في كيفية أتمتة هذه العملية في حالة وجود ملف تكوين.فمثلا:
    [
      {
        "os": "centos",
        "version": "5.11",
        "bit": "64",
        "python": ["2.7.14"]
      },
      {
        "os": "centos",
        "version": "7.40",
        "bit": "64",
        "python": ["2.7.14", "3.6.1"]
      },
    ]
    

    بدلاً من ذلك ، تحقق من مشروع cibuildwheel لإدارة إنشاء ملفات العجلات.

  2. ربما تحتاج فقط إلى تجميع مترجم Python للنشر الأول. قم بتحديث البرنامج النصي build_tarball.sh بحيث يسأل المستخدم عما إذا كانت لغة Python مطلوبة قبل تجميعها.
  3. ماذا عن السجلات؟ يمكن التعامل مع التسجيل إما محليًا أو على مستوى النظام. إذا كان محليًا ، كيف ستتعامل مع تناوب السجل؟ تكوين هذا بنفسك.احصل على الكود من الريبو. الرجاء ترك التعليقات أدناه!