كيفية إنشاء خادم Django يقوم بتشغيل uWSGI و NGINX و PostgreSQL على AWS EC2 مع Python 3.6

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

إذا لم تكن في حالة مزاجية للقراءة ، فيمكنك نسخ لصق كل خطوة كما هو موضح (استبدال القيم) وتشغيل الخادم الخاص بك؟

المتطلبات الأساسية:

  1. يتم تشغيل مثيل Amazon Linux EC2 مع زوج المفاتيح المرتبط ( وصول ssh إليه ).
  2. يجب أن يكون المنفذ 22 ، 80 مفتوحًا لهذه الحالة.
  3. تطبيق Django الذي تريد نشره.
  4. تم تكوين إعدادات قاعدة البيانات لاستخدام PostgreSQL.
  5. Requirements.txt موجود في تطبيقك ، ولديه قائمة تبعيات للتثبيت.
  6. مستودع Git لتطبيق Django الخاص بك.

SSH وتحديث مثيل ubuntu

تحتاج إلى ssh في مثيل EC2 الخاص بك ، لذا تأكد من أن لديك منفذ 22 مفتوحًا للمثيل الخاص بك ثم قم بإجراء تحديث / ترقية.

ssh -i path-to-your-key.pem [email protected] sudo apt-get update && sudo apt-get upgrade -y

تثبيت Python3.6.x على AWS EC2 (ubuntu 16.04)

سنقوم بتنزيل ملف tar.xz من الموقع الرسمي ومن ثم تثبيته يدويًا. قبل ذلك نحتاج إلى تثبيت بعض التبعيات المطلوبة.

بناء وتثبيت التبعيات

sudo apt install build-essential checkinstall sudo apt install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev

تنزيل إصدار Python المطلوب وتثبيته يدويًا

cd /opt && sudo wget //www.python.org/ftp/python/3.6.6/Python-3.6.6.tar.xz sudo tar -xvf Python-3.6.6.tar.xz cd Python-3.6.6/ sudo ./configure sudo make && sudo make install

إزالة الملف الذي تم تنزيله

sudo rm -rf Python-3.6.6.tar.xz

تحقق من إصدار بايثون

python3 -V > Python 3.6.6

إعداد مستخدم Ubuntu لتطبيقنا

إن Django بحد ذاته هو إطار آمن للغاية ، أوافق. لكن تطبيقات الويب لا تزال عرضة للخطر. من الممارسات الجيدة تشغيل التطبيق الخاص بك كمستخدمين للنظام بامتيازات محدودة ولديهم وصول محدود إلى الموارد الموجودة على الخادم الخاص بك. لذلك في هذا القسم ، سنضيف مجموعة مستخدمين وأذونات جديدة إلى مثيل EC2 الخاص بنا.

إضافة "اسم المجموعة" لمجموعة نظام أوبونتو [webapps في حالتي] وتعيين مستخدم "اسم المستخدم" [الأرنب في حالتي] إلى هذه المجموعة

sudo groupadd --system webapps sudo useradd --system --gid webapps --shell /bin/bash --home /webapps/project_name bunny

ملاحظة: أفترض أن " اسم_المشروع " هو الاسم الذي ربما تكون قد استخدمته أثناء " مشروع بدء django-admin أنا> "

قم بإنشاء دليل لتخزين التطبيق الخاص بك

قم بإنشاء دليل لتخزين التطبيق الخاص بك في / webapps / project_name /. قم بتغيير مالك هذا الدليل إلى أرنب مستخدم التطبيق الخاص بك:

sudo mkdir -p /webapps/project_name/ sudo chown bunny /webapps/project_name/

السماح بوصول محدود لمستخدمي المجموعة الآخرين إلى دليل التطبيق

sudo chown -R bunny:users /webapps/project_name sudo chmod -R g+w /webapps/project_name

الآن يمكنك التبديل إلى المستخدم الخاص بك

sudo su - bunny // your console will switch to something like this [email protected]:~$

للعودة إلى مستخدم sudo ، ما عليك سوى القيام ctrl+dبذلك وسيقتل جهاز المستخدم.

تثبيت وضبط PostgresSQL

تثبيت PostgreSQL وإنشاء قاعدة بيانات

sudo apt install postgresql postgresql-contrib sudo su - postgres [email protected]:~$ psql postgres=# CREATE DATABASE database_name;

تغيير كلمة المرور الافتراضية لـ postgres أثناء وجودك في psql terminal

postgres=# \password

انشر تطبيق Django على مثيل EC2 عبر Git في بيئة افتراضية

يسمح نشر تطبيقك باستخدام بيئة افتراضية بالتعامل مع تطبيقك ومتطلباته بشكل منفصل. من الممارسات الجيدة إبقاء تطبيقك منعزلًا.

يعد استخدام مفهوم البيئة مفيدًا عندما تنشر أكثر من تطبيق Django في مثيل واحد للحفاظ على عزلهم وتبعياتهم عن بعضهم البعض.

سننشئ بيئة افتراضية في دليل مستخدم النظام ( الأرنب ). قبل ذلك سنقوم بتثبيت git كمستخدم sudo .

تثبيت Git وسحب التعليمات البرمجية الخاصة بك من git repo

sudo apt-get install git sudo su - bunny // change to your repo https or ssh link [email protected]:~$ git remote add origin [email protected]:/.git [email protected]:~$ git pull origin 

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

إنشاء بيئة افتراضية باستخدام Python3.6 في الدليل الحالي

[email protected]:~$ python3.6 -m venv . [email protected]:~$ source bin/activate (project_name)[email protected]:~$ pip install -r requirements.txt

في هذه المرحلة ، قمنا بإعداد مشروعنا بنجاح. الآن نحن بحاجة إلى تشغيل بعض أوامر manager.p y . سيتطلب ذلك أن نكون في الدليل حيث يوجد manager.py ، أو في كل مرة نحتاج إلى إعطاء مسار لها:

(project_name)[email protected]:~$ python manage.py migrate (project_name)[email protected]:~$ python manage.py createsuperuser (project_name)[email protected]:~$ python manage.py collectstatic

ملاحظة: collectstaticيتطلب الأمر أن يتم إعداد التكوين الثابت بشكل صحيح. نحن لا نناقش ذلك هنا ، على الرغم من أنه ليس ضمن نطاق هذا البرنامج التعليمي.

(project_name)[email protected]:~$ python manage.py runserver 0.0.0.0:8000

سيبدأ هذا خادم التطوير على المنفذ 8000. بافتراض أن المنفذ 8000 مفتوح أيضًا على سبيل المثال الخاص بك ، يمكنك زيارة اسم مجال الخادم الخاص بك أو عنوان IP متبوعًا 8000في متصفحك.

//your_server_domain_or_public_IP:8000
//your_server_domain_or_public_IP:8000/admin

ملاحظة: لا تنس إضافة مجالك أو IP إلى ALLOWED_HOST في settings.py

إعداد خادم تطبيق uWSGI

الآن بعد أن أعددنا مشروعنا وأصبحنا جاهزين للانطلاق ، يمكننا تهيئة uWSGI لخدمة تطبيقنا على الويب بدلاً من خادم التطوير خفيف الوزن المقدم من Django.

إذا كنت تفكر في تشغيل أمر runserver على الشاشة ، فقم بإفلاته. خادم dev مع Django خفيف الوزن بشكل رهيب وغير آمن للغاية وغير قابل للتطوير.

يمكنك تثبيت uWSGI إما في Virtualenv أو عالميًا وتهيئته وفقًا لذلك.

في هذا البرنامج التعليمي ، سنقوم بتثبيت uWSGI في virtualenv . قبل أن نتمكن من تثبيت uWSGI ، نحتاج إلى ملفات تطوير Python التي يعتمد عليها البرنامج.

تثبيت uWSGI مع تبعياته

sudo apt-get install python3-dev
sudo su - bunny
[email protected]:~$ source bin/activate
(project_name)[email protected]:~$ pip install uwsgi

لنقم بتشغيل الخادم باستخدام uWSGI. يقوم هذا الأمر بنفس الشيء الذي سيفعله خادم manager.py . تحتاج إلى استبدال القيم وفقًا لذلك للاختبار بنجاح باستخدام هذا الأمر.

(project_name)[email protected]:~$ uwsgi --http :8000 --home --chdir -w .wsgi

إنشاء ملف تكوين uWSGI

تشغيل uWSGI من سطر الأوامر مفيد فقط للاختبار. للنشر الفعلي ، سننشئ ملف . iniملف في مكان ما في دليل مستخدم النظام لدينا. سيحتوي هذا الملف على جميع التكوينات للتعامل مع حمل الطلب الثقيل ، ويمكن تعديله وفقًا لذلك.

لاحقًا في هذا البرنامج التعليمي ، سنقوم بتشغيل uWSGI خلف NGINX. NGINX متوافق للغاية مع uWSGI ولديه دعم مدمج للتفاعل مع uWSGI.

قم بإنشاء دليل conf في دليل مستخدم النظام الخاص بك حيث ستخزن uwsgi.ini

(project_name)[email protected]:~$ mkdir conf
(project_name)[email protected]:~$ cd conf
(project_name)[email protected]:~$ nano uwsgi.ini

انسخ الكود أدناه من الجوهر واحفظه أعتقد أن التعليقات توضيحية بما يكفي لكل خيار.

ملاحظة: updateMeمن المفترض أن يكون لك اسم المشروع. إنه نفس الاسم الذي قدمته أعلاه أثناء إنشاء دليل مستخدم النظام ، لذا قم بالتحديث وفقًا لذلك.

[uwsgi] # telling user to execute file uid = bunny # telling group to execute file gid = webapps # name of project you during "django-admin startproject " project_name = updateMe # building base path to where project directory is present [In my case this dir is also where my virtual env is] base_dir = /webapps/%(project_name) # set PYTHONHOME/virtualenv or setting where my virtual enviroment is virtualenv = %(base_dir) # changig current directory to project directory where manage.py is present chdir = %(base_dir)/src/ # loading wsgi module module = %(project_name).wsgi:application # enabling master process with n numer of child process master = true processes = 4 # enabling multithreading and assigning threads per process # enable-threads = true # threads = 2 # Enable post buffering past N bytes. save to disk all HTTP bodies larger than the limit $ post-buffering = 204800 # Serialize accept() usage (if possibie). thunder-lock = True # Bind to the specified socket using default uwsgi protocol. uwsgi-socket = %(base_dir)/run/uwsgi.sock # set the UNIX sockets’ permissions to access chmod-socket = 666 # Set internal sockets timeout in seconds. socket-timeout = 300 # Set the maximum time (in seconds) a worker can take to reload/shutdown. reload-mercy = 8 # Reload a worker if its address space usage is higher than the specified value (in megabytes). reload-on-as = 512 # respawn processes taking more than 50 seconds harakiri = 50 # respawn processes after serving 5000 requests max-requests = 5000 # clear environment on exit vacuum = true # When enabled (set to True), only uWSGI internal messages and errors are logged. disable-logging = True # path to where uwsgi logs will be saved logto = %(base_dir)/log/uwsgi.log # maximum size of log file 20MB log-maxsize = 20971520 # Set logfile name after rotation. log-backupname = %(base_dir)/log/old-uwsgi.log # Reload uWSGI if the specified file or directory is modified/touched. touch-reload = %(base_dir)/src/ # Set the number of cores (CPUs) to allocate to each worker process. # cpu-affinity = 1 # Reload workers after this many seconds. Disabled by default. max-worker-lifetime = 300

أحاول أن أجعل كل شيء سهلاً بتفسيرات واضحة. تحقق من المسارات واسم الدليل والمدخلات الأخرى المطلوب استبدالها.

We need to create the log file and run directory where our socket file will be created, that we just mentioned in our uwsgi.ini:

(project_name)[email protected]:~$ mkdir log
(project_name)[email protected]:~$ mkdir run
(project_name)[email protected]:~$ touch log/uwsgi.log

Make sure to change permissions for these two so that every group or user can write or execute files in these directories:

$ sudo chmod 777 /webapps/updateMe/run
$ sudo chmod 777 /webapps/updateMe/log

Now let’s try running the server using uwsgi.ini that we just created.

(project_name)[email protected]:~$ uwsgi --ini /webapps/updateMe/conf/uwsgi.ini

If everything up until now is setup correctly, then it should be running. If not, then you need to go back to check for anything you missed (like the path/project name, etc).

To check any uswgi log you can cat or tail uwsgi.log:

(project_name)[email protected]:~$ tail log/uwsgi.log

Create a systemd Unit File for uWSGI

At this point if everything is cool, you can even run this command in screen and detach it — but again, this is not a good practice at all. Instead we will create a system service and let systemd (Ubuntu’s service manager) take care of it.

Switch back to sudo user

$ sudo nano /etc/systemd/system/uwsgi.service

and copy paste code from the below gist. Don’t forget to update and crosscheck names/path that suit your app:

[Unit] Description=uWSGI instance to serve updateMe project After=network.target [Service] User=bunny Group=webapps WorkingDirectory=/webapps/project_name/src Environment="PATH=/webapps/project_name/bin" ExecStart=/webapps/project_name/bin/uwsgi --ini /webapps/project_name/conf/uwsgi.ini Restart=always KillSignal=SIGQUIT Type=notify NotifyAccess=all [Install] WantedBy=multi-user.target

After you save the above file and close it, you can run following commands:

Reload systemctl daemon to reload systemd manager configuration and recreate the entire dependency tree

$ sudo systemctl daemon-reload

Enable uwsgi service to start on system reboot

$ sudo systemctl enable uwsgi

Start uwsgi service

$ sudo service uwsgi start

Restart uwsgi service

$ sudo service uwsgi restart

Check uwsgi service status

$ sudo service uwsgi status

Take a deep breath here if everything ran smoothly. We just finished setting up most hectic part of this tutorial, so you should be proud.

Next we will setup NGINX, and then we’ll be done! I know this is taking a bit of time, but believe me — once done, you will be as happy as I will be after publishing this tutorial.

Setting Up NGINX on EC2 for uWSGI

NGINX is a lightweight server, and we’ll use it as a reverse proxy.

We could let uWSGI run directly on port 80, but NGINX has a lot more benefits which makes it desirable. Also NGINX natively includes support for uWSGI.

Enough talk, let’s install NGINX on our instance

$ sudo apt-get install nginx

Now when you go to //your-public-ip-or-address, you will see a Nginx welcome page. This is because NGINX is listening to port 80 according to its default configuration.

NGINX has two directories, sites-available and sites-enabled,that need our attention. sites-available stores all conf files for all available sites on that particular instance. sites-enabledstores the symbolic link for each enabled site to the sites-available directory.

By default, there is only one conf file named default that has basic setup for NGINX. You can either modify it or create a new one. In our case, I am going to delete it:

$ sudo rm -rf /etc/nginx/sites-available/default
$ sudo rm -rf /etc/nginx/sites-enabled/default

Let’s create our nginx-uwsgi.conf file to connect the browser request to the uwsgi server we are running in site-available:

$ sudo nano /etc/nginx/sites-available/nginx-uwsgi.conf

and copy the following code from the gist below:

upstream updateMe_dev { server unix:/webapps/updateMe/run/uwsgi.sock; } server { listen 80; server_name your-IP-or-address-here; charset utf-8; client_max_body_size 128M; location /static { # exact path to where your static files are located on server # [mostly you won't need this, as you will be using some storage service for same] alias /webapps/updateMe/static_local; } location /media { # exact path to where your media files are located on server # [mostly you won't need this, as you will be using some storage service for same] alias /webapps/updateMe/media_local; } location / { include uwsgi_params; uwsgi_pass updateMe_dev; uwsgi_read_timeout 300s; uwsgi_send_timeout 300s; } access_log /webapps/updateMe/log/dev-nginx-access.log; error_log /webapps/updateMe/log/dev-nginx-error.log; }

Create symbolic link into sites-enabled directory for same

$ sudo ln -s /etc/nginx/sites-available/nginx-uwsgi.conf /etc/nginx/sites-enabled/nginx-uwsgi.conf

That’s all, we’re almost there, about to finish up…

Reload systemctl daemon

$ sudo systemctl daemon-reload

Enable nginx service on system reboot

$ sudo systemctl enable nginx

Start Nginx service

$ sudo service nginx start

Test Nginx. It should return OK, Successful as a part of the result.

$ sudo nginx -t

If NGINX fails, you can check its last error-log or access-log on the path specified by us in its conf.

$ tail -f /webapps/updateMe/log/nginx-error.log
$ tail -f /webapps/updateMe/log/nginx-access.log

Restart Nginx Service

$ sudo service nginx restart

Check Nginx Service status

$ sudo service nginx status

You should now be able to reach your app at //your-public-ip-or-address

Well this is the end of this lengthy tutorial. I hope you got what you expected from it. Thanks for bearing with me.

PS: uWSGI + NGINX + Django is highly customizable to meet any large scale requirements. That being said, core optimization still lies at application level. How you code and make use of Django ORM or Raw SQL query, etc. will help you further.