Deploy Django บน AWS EC2 Nginx (ตอน 1)
ขั้นตอนการ Deploy Django Project ไปวาง บน AWS EC2 โดยใช้ Nginx เป็น Web Server โดยทำงานร่วมกับ Gunicorn และเก็บ Static files บน Cloudinary

ขั้นตอนการ Deploy Django (ครอบคลุมเวอร์ชั่น 5.0) Project ไปวาง บน AWS EC2 โดยใช้ Nginx เป็น Web Server โดยทำงานร่วมกับ Gunicorn และเก็บ Static files บน Cloudinary
สิ่งที่ต้องเตรียมก่อน Deploy Django App
ก่อนจะไปพูดถึงการ Deploy จะขออธิบายถึงสิ่งที่ต้องเตรียมก่อนการ Deploy นะครับ สำหรับคนที่ทำสิ่งเหล่านี้เป็นแล้วสามารถ กดข้าม ได้ครับ
เปิดพอร์ต 8000 บน AWS EC2
ดูวิธีสร้าง AWS EC2 ได้ที่ ลิงค์ นี้ครับ
เมื่อสร้าง EC2 เสร็จแล้ว ให้เปิดพอร์ต 8000 ครับ เพราะ Django ใช้พอร์ต 8000
ปกติการสร้าง EC2 Instance แต่ละครั้ง ระบบจะสร้าง Security group ให้เรา โดยใช้ชื่อว่า launch-wizard-x ถ้าสร้างครั้งแรกก็จะได้ชื่อ launch-wizard-1 และถ้าสร้าง EC2 Instance ครั้งที่ 2 ก็จะได้ชื่อ launch-wizard-2 อย่างนี้ไปเรื่อยๆ ถ้าเราอยากเปิดพอร์ตเพิ่มเติมก็ให้คลิ๊กลิงค์ตรง sg-xxxxxxxxxxx

จะได้หน้าจอนี้ ให้คลิ๊กตรง Edit inbound rules

ตามด้วย Add rule (ถ้าพอร์ตที่เราต้องการยังไม่ได้เปิดไว้ ในที่นี้คือพอร์ต 8000)

ตรงคอลัมน์ Type เลือก Custom TCP ตรงคอลัมน์ Port range ใส่เลข 8000 ตรงคอลัมน์ Source เลือก Anywhere เสร็จแล้วคลิ๊กปุ่ม Save rules

ติดตั้ง Oh My Zsh
Oh My Zsh
แม้จะสามารถทำงานบน VSCode ได้ แต่บางครั้งเราอาจอยู่นอกสถานที่ และเราจำเป็นต้องแก้โค้ดผ่าน EC2 Web base terminal เราอาจยอมเสียเวลานิดหน่อย เพื่อติดตั้ง Zsh และ Oh My Zsh บน EC2
เริ่มด้วยการติดตั้ง Zsh และ Oh My Zsh
sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install zsh -y
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
ข้อระวัง: สำหรับ new user ที่ไม่ใช่ root user
สำหรับท่านที่ใช้ Ubuntu บน Linode หรือบน Hetzner และต้องการติดตั้งบน new user ที่ไม่ใช่ root user การรันคำสั่ง sh -c "$(curl -fsSL https://...)" จะทำไม่ได้นะครับ ติดปัญหา Permission ให้แก้ดังนี้
1. ดาวน์โหลดไฟล์ Oh My Zsh's install.sh
wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
2. แก้ไขไฟล์ install.sh โดยใช้คำสั่ง
sudo nano install.sh
เปลี่ยนโค้ดนี้
if [ -n "$ZDOTDIR" ] && [ "$ZDOTDIR" != "$HOME" ]; then
ZSH="${ZSH:-$ZDOTDIR/ohmyzsh}"
fi
ZSH="${ZSH:-$HOME/.oh-my-zsh}"
เป็น
ZSH=~/.oh-my-zsh
หรือใช้ลิงค์นี้ก็ได้ครับ ผมแก้โค้ดให้เรียบร้อยแล้ว
wget https://raw.githubusercontent.com/ManotLuijiu/ohmyzsh/master/tools/install.sh
3. แปลงไฟล์ install.sh ให้สามารถ execute ได้
chmod +x install.sh
4. เสร็จแล้วรัน install.sh ได้เลยครับ
./install.sh
Ubuntu จะถามเราว่าต้องการเปลี่ยน shell จาก bash ไปเป็น zsh มั๊ย
Looking for an existing zsh config...
Using the Oh My Zsh template file and adding it to /home/django/.zshrc.
Time to change your default shell to zsh:
Do you want to change your default shell to zsh? [Y/n]
ให้ตอบ Y หรือกดปุ่ม Enter ก็ได้ครับ
ข้อสังเกต สำหรับคำตอบที่เป็นค่า Default ทาง Ubuntu จะใส่มาเป็นอักษรตัวใหญ่ เช่น
[Y/n]ค่า Default คือ Y หรือ Yes เราสามารถเคาะปุ่ม Enter ผ่านได้เลยครับ ไม่จำเป็นต้องพิมพ์คำว่า Y
Powerline fonts
ติดตั้ง fonts เผื่อๆไว้ครับ เผื่อใครชอบ ถ้าไม่ชอบ ก็ผ่านคำสั่งนี้ไปได้เลยครับ
sudo apt-get install fonts-powerline -y
PowerLevel10K
ต่อไปติดตั้ง Theme ของ Oh My Zsh ชื่อว่า PowerLevel10K
git clone https://github.com/romkatv/powerlevel10k.git $ZSH_CUSTOM/themes/powerlevel10k
zsh-syntax-highlighting
มี Theme แล้ว ก็เพิ่ม Plugin ช่วยงานอีกซัก 2 ตัวครับ ตัวแรกช่วยสร้างสีสันให้ Terminal
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
zsh-autosuggestions
ตัวที่สอง มันจะช่วยจำคำสั่งต่างๆที่เราเคยใช้
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
Update zshrc
หลังจากติดตั้ง Theme และ Plugin เสร็จแล้ว ก็ต้องแจ้งให้ Zsh ทราบโดยเปิดไฟล์ .zshrc ขึ้นมาครับ ด้วยคำสั่ง
sudo nano ~/.zshrc
ให้คัดลอกโค้ด 2 บรรทัดนี้ไปทับของเดิมในไฟล์ .zshrc ครับ
ZSH_THEME="powerlevel10k/powerlevel10k"
plugins=( git zsh-syntax-highlighting zsh-autosuggestions )
สั่งบันทึกไฟล์ด้วย Ctrl + X แล้วตอบ Y
สั่ง รีสตาร์ท ไฟล์ .zshrc ใหม่ ด้วยคำสั่ง
source ~/.zshrc
Ubuntu จะพาเราเข้าหน้าจอ p10k configure ให้ตอบคำถามไปตามที่เราต้องการได้เลยครับ สำหรับคำตอบสุดท้ายให้ตอบ ข้อ 2 quiet เสร็จแล้วครับ สำหรับการติดตั้ง Oh My Zsh
ขอ Fixed IP (EC2 Elastic IP)
ก่อนจะ connect เข้าใช้ EC2 Instance อย่าลืมขอเลข Fixed IP ให้เรียบร้อยนะครับ
สร้าง User และ Python Environment
connect ไปที่ EC2 Instance ที่เราเพิ่งสร้างเสร็จ update Ubuntu และสร้าง User ใหม่ ให้เรียบร้อยครับ
sudo adduser <your-username>
sudo usermod -aG sudo <your-username>
su <your-username>
cd /home/<your-username>
ในที่นี้ขอใช้ User ใหม่ว่า django จะได้
sudo adduser django
sudo usermod -aG sudo django
su django
cd /home/django
เมื่อเราอยู่ในโฟลเดอร์ของ User ใหม่ เรียบร้อยแล้ว ให้สร้างโฟลเดอร์งานชื่อว่า django-auth และเริ่มต้นด้วยการสร้าง Virtual Environment สำหรับ Python ครับ
sudo apt-get install python3-venv
mkdir django-auth && cd $_
python3 -m venv env
ถ้าเราใช้คำสั่ง ls เราจะเห็นโฟลเดอร์ env
ให้ activate virtual env ด้วยคำสั่ง
source env/bin/activate
สำหรับคนที่ใช้ conda activate virtual-name ก็สามารถใช้คำสั่ง source env/bin/activate ซ้ำได้นะครับ ไม่มี Error ใดๆ
หน้าจอ Terminal บน AWS EC2 Instance จะได้ออกมาประมาณนี้
django@ip-xxx-xx-xx-xxx:~$ mkdir django-auth && cd $_
django@ip-xxx-xx-xx-xxx:~/django-auth$ ls
django@ip-xxx-xx-xx-xxx:~/django-auth$ python3 -m venv env
django@ip-xxx-xx-xx-xxx:~/django-auth$ ls
env
django@ip-xxx-xx-xx-xxx:~/django-auth$ source env/bin/activate
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$
ในกรณีที่ต้องการออกจาก Virtual Environment ให้พิมพ์คำสั่ง
deactivate
สร้าง Django Project
เริ่มติดตั้ง django ใน Virtual environment
pip install django
ตรงนี้สำคัญนะครับ ให้สังเกตว่าตั้งแต่ขั้นตอนนี้ เราทำงานบน Virtual Environment แล้วนะครับ ดังนั้นถ้าเราสั่ง python compile นอก Virtual Environment เราจะพบ Error นะครับ เราต้องใช้คำสั่ง python ใน Virtual Environment เท่านั้นครับ
สร้าง Django App ระบบหลังบ้าน
เราจะสร้าง Django App ที่ใช้ดูแลระบบหลังบ้าน เช่น ระบบ Auth โดยใช้ Django REST framework ทำงานร่วมกับ Djoser ให้ติดตั้ง Packages ดังนี้
pip install djangorestframework djoser django-cors-headers python-dotenv
เมื่อใช้คำสั่ง pip list จะได้ Python packages ทั้งหมดดังนี้ครับ
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ pip list
Package Version
----------------------------- ------------
asgiref 3.7.2
certifi 2023.11.17
cffi 1.16.0
charset-normalizer 3.3.2
cryptography 41.0.7
defusedxml 0.8.0rc2
Django 5.0
django-cors-headers 4.3.1
django-templated-mail 1.1.1
djangorestframework 3.14.0
djangorestframework-simplejwt 5.3.1
djoser 2.2.2
idna 3.6
oauthlib 3.2.2
pip 22.0.2
pycparser 2.21
PyJWT 2.8.0
python-dotenv 1.0.0
python3-openid 3.2.0
pytz 2023.3.post1
requests 2.31.0
requests-oauthlib 1.3.1
setuptools 59.6.0
social-auth-app-django 5.4.0
social-auth-core 4.5.1
sqlparse 0.4.4
typing_extensions 4.9.0
urllib3 2.1.0
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$
ให้สังเกตว่า ตอนนี้เรามี social-auth-core ติดตั้งมาพร้อมแล้วจากคำสั่งข้างต้น
เสร็จแล้วเริ่มด้วยการสร้างโปรแกรมแม่ ที่ชื่อว่า django_auth ด้วยคำสั่ง
django-admin startproject django_auth .
จะได้
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ django-admin startproject django_auth .
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ ls
django_auth env manage.py
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ cd django_auth
(env) django@ip-xxx-xx-xx-xxx:~/django-auth/django_auth$ ls
__init__.py asgi.py settings.py urls.py wsgi.py
(env) django@ip-xxx-xx-xx-xxx:~/django-auth/django_auth$
ต่อไปสร้างโปรแกรมลูก ที่จะมาดูแลระบบ Users ก่อนอื่นให้พิมพ์คำสั่ง pwd และ ls เพื่อดูว่าเราอยู่ในโฟลเดอร์รากหรือเปล่า
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ pwd
/home/django/django-auth
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ ls
django_auth env manage.py
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$
โดยโฟลเดอร์รากจะต้องมองเห็นไฟล์ manage.py นะครับ เสร็จแล้วสร้างโปรแกรมลูกที่ชื่อว่า users ด้วยคำสั่ง
python manage.py startapp users
Terminal จะมีหน้าตาแบบนี้
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ python manage.py startapp users
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$ ls
django_auth env manage.py users
(env) django@ip-xxx-xx-xx-xxx:~/django-auth$
settings.py
ให้แก้ไขไฟล์ settings.py ที่อยู่ในโฟลเดอร์ django_auth
sudo nano django_auth/settings.py
หรือถ้าจะให้สะดวกกว่านั้นให้เขียนโค้ดผ่าน VSCode ครับ โดยตั้งค่าการเชื่อมต่อ VSCode และ EC2 ตามลิงค์นี้ครับ
ก่อนจะย้ายไปทำงานบน VSCode ให้รันคำสั่ง
pip freeze > requirements.txt
แนะนำให้อ่าน บทความนี้ เพิ่มเติมครับ
หลังจากเชื่อม EC2 ได้แล้ว จะอยู่ที่หน้าจอ VSCode Terminal ให้เลือก Virtual Environment ที่เราสร้างไว้บน Ubuntu EC2 ดังนี้
ถ้าเปิดใช้ VSCode ครั้งแรกอาจเจอคำถามเรื่องความปลอดภัย ให้ติ๊กถูก และตอบ Yes

เมื่อเข้าไปถึงให้เปิด Terminal ขึ้นมาด้วยการกดปุ่ม Ctrl + ` พร้อมกัน แล้วให้ดูตรงมุมขวาล่างสุด ให้คลิ๊กตรง เวอร์ชั่นของ Python

เลือก + Enter interpreter path...

ต่อไปให้เลือก Find...

ไปที่โฟลเดอร์ /home/django/django-auth/ ตามด้วยคลิ๊กตรงโฟลเดอร์ env
สำหรับคนที่สร้าง Username และ ชื่อ Project ต่างออกไป ให้หาโฟลเดอร์ตามนี้ครับ
/home/<your-username>/<your-porject-name>/

ขั้นตอนสุดท้ายเลือก python3

ตอนนี้ VSCode จะใช้ Virtual Environment ตัวเดียวกับ EC2 จะไม่เกิด Error ใดๆครับ
ให้คัดลอก settings.py จากไฟล์นี้ มาวางที่ django_auth/settings.py
.env.local
เมื่อถึงขั้นตอนนี้ให้สร้างไฟล์ .env.local ที่โฟลเดอร์ราก ด้วยคำสั่ง
touch .env.local
คัดลอกเนื้อหาในไฟล์ .env.local บน Gist มาวางในไฟล์ .env.local บน VSCodeได้เลยครับ
แก้ไข settings.py ตามนี้
Origin และ Domain Name
ให้แก้ไขโค้ดข้างล่างนี้ โดยการเติมชื่อโดเมนของท่านเข้าไป เช่น example.com จะได้
CSRF_TRUSTED_ORIGINS = ["https://*.example.com"] # ตรงนี้ต้องแก้
CSRF_COOKIE_DOMAIN = ".example.com" # ตรงนี้ต้องแก้
CORS_ALLOWED_ORIGINS = [
"https://example.com", # ตรงนี้ต้องแก้
"https://www.example.com", # ตรงนี้ต้องแก้
"https://django-auth.example.com", # เช่น event.your-domain.com
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CORS_ORIGIN_WHITELIST = [
"https://example.com", # ตรงนี้ต้องแก้
"https://www.example.com", # ตรงนี้ต้องแก้
"https://django-auth.example.com", # เช่น event.your-domain.com
"http://localhost:3000",
"http://127.0.0.1:3000",
]
เชื่อมต่อฐานข้อมูลบน AWS RDS
ให้ติดตั้งเพิ่มอีก 1 package คือ psycopg2-binary
pip install psycopg2-binary
pip freeze > requirements.txt
เนื่องจากโดยปกติ Security Group ของ AWS RDS จะไม่เปิดให้เชื่อมต่อผ่าน Internet สามารถอ่านวิธีแก้ปัญหา ได้จาก
รายละเอียดการสร้าง AWS RDS และเทคนิคในการเชื่อม RDS ผ่าน Internet
เมื่อสร้างฐานข้อมูลเสร็จแล้ว เราจะได้หน้าจอนี้ครับ

คลิ๊กตรง View connection details จะได้หน้าจอแสดงรายละเอียด Credential

ตอนนี้เราขาดเพียงชื่อ ฐานข้อมูล ให้ไปที่แท็บ Configuration มองหา DB name

เมื่อได้ข้อมูลมาครบทั้ง 4 รายการแล้ว ก็ไปที่ไฟล์ settings.py มองหา DATABASES แล้วแก้ตามนี้ครับ
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "postgres", # ตรงนี้ต้องแก้ ตามเลข 4
"USER": "postgres", # ตรงนี้ต้องแก้ ตามเลข 1
"PASSWORD": "your-password", # ตรงนี้ต้องแก้ ตามเลข 2
"HOST": "<DB instance ID>.xxxxxxxxx.ap-southeast-1.rds.amazonaws.com", # ตรงนี้ต้องแก้ ตามเลข 3
"PORT": "5432",
}
}
การเชื่อมต่อฐานข้อมูลระหว่าง Django และ AWS RDS PostgreSQL ก็เสร็จเท่านี้ครับ
Cloudinary
ถ้าต้องการใช้งาน Cloudinary ให้ติดตั้งเพิ่มอีก 1 Package ครับ
pip install django-cloudinary-storage
pip freeze > requirements.txt
วิธีหา CLOUD_NAME, API_KEY และ API_SECRET
ไปที่ cloudinary.com เลือก Programmable Media -> Dashboard จะได้หน้าจอตามรูปด้านล่างนี้ เราสามารถคัดลอก Cloud Name, API Key และ API Secret ได้เลย แล้วเอาค่าที่ได้ทั้งหมดไปใส่ในไฟล์ .env.local

CLOUDINARY_CLOUD_NAME="" # ตรงนี้ต้องแก้ ตามเลข 3
CLOUDINARY_API_KEY="" # ตรงนี้ต้องแก้ ตามเลข 4
CLOUDINARY_API_SECRET="" # ตรงนี้ต้องแก้ ตามเลข 5
กำหนด Credential สำหรับเชื่อมต่อ Cloudinary
ดูให้แน่ใจว่าในไฟล์ django_auth/settings.py ตรง CLOUDINARY_STORAGE ได้กำหนดค่าไว้ตามนี้
CLOUDINARY_STORAGE = {
"CLOUD_NAME": getenv("CLOUDINARY_CLOUD_NAME"),
"API_KEY": getenv("CLOUDINARY_API_KEY"),
"API_SECRET": getenv("CLOUDINARY_API_SECRET")
}
Debug
Pain point: Django App มองไม่เห็นไฟล์ css
ใครที่มีปัญหา Django App หาไฟล์ css ไม่เจอ ให้แก้ดังนี้
Solution: วิธีระบุ STATIC_URL
หลังจากสั่ง python manage.py collecstatic แล้ว ไฟล์ Statics ทั้งหมดจะไปอยู่ที่โฟลเดอร์ static บน Cloudinary เราสามารถหา Cloudinary URL ได้จากรูปด้านล่าง

วาง URL บน Web Browser ก็จะได้ STATIC_URL บน Cloudinary จะมีรูปแบบตามนี้
https://res.cloudinary.com/<your-account>/raw/upload/vxxxxxxxx/static/admin/css/forms.c731915b3b1e.css
ให้ไปที่ไฟล์ /etc/nginx/sites-available/your-project ด้วยคำสั่ง
sudo nano /etc/nginx/sites-available/myproject
ในที่นี้จะเป็น
sudo nano /etc/nginx/sites-available/django-auth
ตรง static ให้แก้เป็น
location /static {
rewrite ^/static(.*)$ https://res.cloudinary.com/<your-account>/raw/upload/vxxxxxxxx/static$1 redirect;
}
AWS S3 Storage
บางคนอาจอยากอัพโหลด Static files ไปไว้บน AWS S3 และ Cloudfront แทนที่จะวางไว้บน Cloudinary ซึ่งจะอธิบายในบทความถัดไป เบื้องต้นนี้ให้ลง Package ไว้ก่อนครับ
pip install django-storages
pip freeze > requirements.txt