Deploy Saleor Production แบบ ไม่ใช้ Docker และวิธีติดตั้ง Plugins
บทความนี้พูดถึงการ Deploy Saleor Production บน Ubuntu แบบไม่ใช้ Docker จะมีวิธีการ Deploy อย่างไร พร้อมอธิบายวิธีติดตั้ง Plugins

ในบทความที่แล้ว ได้พูดถึงการ Deploy Saleor Production บน Ubuntu แบบใช้ Docker ไปแล้ว แต่ถ้าเราไม่อยากใช้ Docker จะมีวิธีการ Deploy อย่างไร แล้วถ้าอยากติดตั้ง Plugins บน Saleor จะมีวิธีติดตั้งอย่างไร
สิ่งที่ต้องเตรียมก่อน
สร้างบัญชีผู้ใช้บน Linode
ถ้ายังไม่มีบัญชีผู้ใช้งานบน Linode ให้กลับไปสร้างบัญชีผู้ใช้งานก่อนครับ โดยดูวิธีการจาก บทความ นี้
สร้างฐานข้อมูล PostgreSQL
สำหรับคนที่อาจใช้ Server ที่อื่น หรือใช้ Server ส่วนตัว และยังไม่ได้ติดตั้ง PostgreSQL ให้รันคำสั่งต่อไปนี้ครับ
sudo apt update && sudo apt upgrade -y
sudo apt install postgresql postgresql-contrib -y
ตรวจดูว่า PostgreSQL ทำงานหรือยัง
sudo systemctl status postgresql
เข้าใช้งาน PostgreSQL โดยเริ่มจากเปลี่ยน User เป็น postgres
sudo su - postgres
ลองพิมพ์คำสั่ง
psql
ในทางปฏิบัติเราควรสร้างบัญชีใหม่ขึ้นมาทำงาน ด้วยคำสั่ง
CREATE USER your-user WITH PASSWORD 'your-password';
ในที่นี้ขอตั้งชื่อผู้ใช้ว่า saleoradmin และรหัสผ่านเป็น saleor12345 จะได้คำสั่งดังนี้
CREATE USER saleoradmin WITH PASSWORD 'saleor12345';
ถ้าทำสำเร็จระบบจะตอบกลับมาว่า
CREATE ROLE
และให้สร้างฐานข้อมูลใหม่เพื่อใช้กับ Saleor API
CREATE DATABASE your-db-name OWNER your-user
ในที่นี้จะเป็น
CREATE DATABASE saleordb OWNER saleoradmin
ถ้าติดปัญหาสร้าง User หรือฐานข้อมูลด้วยคำสั่งข้างบนไม่ได้ ให้พิมพ์
\qแล้ว Enter เพื่อออกไปที่ User postgres แล้วพิมพ์คำสั่ง
1. สร้าง User
createuser --interactive --pwprompt
2. สร้างฐานข้อมูล
createdb saleordb
จะได้ผลลัพธ์ดังนี้
postgres-# \q
postgres@localhost:~$ createuser --interactive --pwprompt
Enter name of role to add: your-username
Enter password for new role:
Enter it again:
Shall the new role be a superuser? (y/n)
Please answer "y" or "n".
Shall the new role be a superuser? (y/n) y
...
postgres@localhost:~$ createdb your-db-name
postgres@localhost:~$ psql
psql (14.10 (Ubuntu 14.10-0ubuntu0.22.04.1))
Type "help" for help.
เมื่อได้ บัญชีผู้ใช้ใหม่ และ ฐานข้อมูลใหม่แล้ว ลองเข้าไปใช้ดูครับ โดย
Enable บัญชีชื่อผู้ใช้ใหม่ โดยออกไปที่ Ubuntu ก่อน แล้วจึงเพิ่ม User
sudo su -
adduser saleoradmin
และตามด้วยคำสั่ง
sudo su - saleoradmin
psql -d saleordb
ลองทดสอบด้วยคำสั่งง่ายๆ โดยขอดูลิสต์รายชื่อฐานข้อมูลทั้งหมด
\l

Saleor ใช้ Poetry แทน PIP
ให้ดาวน์โหลด Repo Saleor Core จาก releases
คลิ๊กขวาตรง Source code (zip) แล้วเลือก Copy Link Address แล้วดาวน์โหลดด้วย wget ก็ได้ครับ ดูรายละเอียดได้ที่ บทความ นี้
เมื่อเราแตกไฟล์ออกมาแล้ว เปลี่ยนชื่อโฟลเดอร์เป็น saleor-api แล้วเข้าไปที่โฟลเดอร์นี้
ให้สังเกตว่า เราไม่พบไฟล์ requirements.txt แล้วเราจะติดตั้ง Python Packages อย่างไรล่ะ
เนื่องจาก Saleor ใช้ Poetry แทน PIP ดังนั้นเราจะไม่สามารถมองเห็นไฟล์ requirements.txt ใน repo แต่จะเห็นไฟล์ pyproject.toml แทนครับ
ติดตั้ง Saleor Packages
ถ้าต้องการ Deploy Saleor ใน Mode Production และต้องการติดตั้ง Plugins เช่น Stripe, Slack, MailChimp, EmailServer เราต้องติดตั้ง Packages ของ Saleor ให้ครบถ้วน ให้ดำเนินการตามขั้นตอนต่อไปนี้ครับ
1. สร้าง Virtural Environment
python -m venv env
source env/bin/activate
2. เนื่องจาก psycopg2 มีปัญหา เราจะแก้ด้วยการติดตั้ง psycopg2-binary แทน พร้อมทั้งติดตั้ง django-environ เพื่อจัดการไฟล์ .env ครับ ให้ติดตั้ง Python Packages ด้วยคำสั่งเหล่านี้
poetry add psycopg2-binary
poetry add django-environ
poetry install
3. เปลี่ยน Django Default Port จาก 8000 เป็น 800X ในที่นี้จะเปลี่ยนเป็น Port 8009 โดยเพิ่มโค้ด 2 บรรทัดนี้เข้าไปครับ
from django.core.management.commands.runserver import Command as runserver
runserver.default_port = "8009"
4. สร้างไฟล์ .env ที่โฟลเดอร์ saleor-api ใส่ข้อมูลตามนี้ครับ
สำหรับ SECRET_KEY ให้ไปที่ randomkeygen แล้วเลือก SECRET KEY ตามชอบครับ
*https://xxx-xxx-xxxx.ngrok-free.app สำหรับ ngrok ถ้าใครไม่ได้ใช้งาน ngrok ก็ไม่ต้องใส่ก็ได้ครับ
*STOREFRONT_URL="https://www.your-domain.com/" หรือ
*STOREFRONT_URL="https://saleor-shop.your-domain.com/"
DEBUG=False
SECRET_KEY="your-secret-key"
ALLOWED_GRAPHQL_ORIGINS="https://saleor-dashboard.your-domain.com, https://www.your-domain.com, https://saleor-shop.your-domain.com, https://stripe.saleor.app, https://saleor-smtp.your-domain.com, https://saleor-crm.your-domain.com, https://saleor-slack.your-domain.com, https://xxx-xxx-xxxx.ngrok-free.app"
ALLOWED_HOSTS=saleor-dashboard.your-domain.com, www.your-domain.com, .your-domain.com, 127.0.0.1, saleor-api.your-domain.com, stripe.saleor.app, xxx-xxx-xxxx.ngrok-free.app
ALLOWED_CLIENT_HOSTS="localhost, 127.0.0.1, .your-domain.com, saleor-dashboard.your-domain.com, www.your-domain.com, stripe.saleor.app, saleor-slack.your-domain.com, saleor-crm.your-domain.com, saleor-smtp.your-domain.com, xxx-xxx-xxxx.ngrok-free.app"
PUBLIC_URL="https://saleor-api.your-domain.com/"
DASHBOARD_URL="https://saleor-dashboard.your-domain.com/dashboard/"
STOREFRONT_URL="https://www.your-domain.com/"
DATABASE_URL="postgres://saleoradmin:saleor12345@localhost:5432/saleordb"
DEFAULT_COUNTRY=TH
DEFAULT_CURRENCY=THB
AWS_ACCESS_KEY_ID=your-AWS_ACCESS_KEY_ID
AWS_S3_REGION_NAME='ap-southeast-1'
AWS_DEFAULT_ACL="public-read"
AWS_MEDIA_BUCKET_NAME="your-AWS_MEDIA_BUCKET_NAME"
AWS_STORAGE_BUCKET_NAME=AWS_MEDIA_BUCKET_NAME
AWS_MEDIA_CUSTOM_DOMAIN='your-AWS_MEDIA_BUCKET_NAME.s3.ap-southeast-1.amazonaws.com'
AWS_QUERYSTRING_AUTH=False
AWS_SECRET_ACCESS_KEY=your-AWS_SECRET_ACCESS_KEY
AWS_STATIC_CUSTOM_DOMAIN=AWS_MEDIA_CUSTOM_DOMAIN
AWS_S3_FILE_OVERWRITE=True
AWS_S3_CUSTOM_DOMAIN=AWS_MEDIA_CUSTOM_DOMAIN
STATIC_URL='https://your-AWS_MEDIA_BUCKET_NAME.s3.ap-southeast-1.amazonaws.com/static/'
MEDIA_URL='https://your-AWS_MEDIA_BUCKET_NAME.s3.ap-southeast-1.amazonaws.com/media/'
5. เพิ่มโค้ดนี้เข้าไปในไฟล์ settings.py เพื่อ Enable django-environ
env = environ.Env(
# set casting, default value
DEBUG=(bool, False)
)
# Set the project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Take environment variables from .env file
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
พร้อมทั้งเปลี่ยนโค้ด SECRET_KEY และ DEBUG ดังนี้ โดยแก้จาก
SECRET_KEY = os.environ.get("SECRET_KEY")
DEBUG = get_bool_from_env("DEBUG", True)
เป็น
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG')
6. ระบุฐานข้อมูล ที่เราเพิ่งสร้างให้กับ Saleor ผ่านไฟล์ settings.py
เข้าไปที่โฟลเดอร์ saleor-api/saleor ค้นหา DATABASES
DATABASES = {
DATABASE_CONNECTION_DEFAULT_NAME: dj_database_url.config(
# default="postgres://saleoradmin:saleor12345@localhost:5432/saleordb",
default=env("DATABASE_URL"),
conn_max_age=DB_CONN_MAX_AGE,
),
DATABASE_CONNECTION_REPLICA_NAME: dj_database_url.config(
# default="postgres://saleoradmin:saleor12345@localhost:5432/saleordb",
default=env("DATABASE_URL"),
conn_max_age=DB_CONN_MAX_AGE,
),
}
7. ที่ไฟล์ settings.py เช่นเดียวกัน ให้หาคำว่า RSA_PRIVATE_KEY
เปลี่ยน
RSA_PRIVATE_KEY = os.environ.get("RSA_PRIVATE_KEY", None)
เป็น
RSA_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwq4CiHZeAfLY4Bj25XwRavq9M2Zz2j1yvTwLuhoCK7UVR1RG
MWnHTXncNOL6uUXZdvQJpUrix3oMVcWW2l+7HDzw/crdCpIMUsirAU1biddeG6UI
LKIlPw1NKM+SMKo5SIhiZTw0FklP1mh20voGCbs1rpmRtk5Mvu45t1/VMciCkot2
1ldSZ9QmG4tnSEOZT3Flv/zreF01vq70qjQ/xBq4dDUnw3JoeJEmLGjudM2fG/+I
qwH3cbH8Ems/EbcXFaYXrm1VTJ1k5rQnH0tcTTnIzcbX3+t+xs0FFGSfLzfv6y9C
sl2uIblJVpUPTJBVqXgkME1MB58xvP4DS/M8xQIDAQABAoIBAAQ5La1jsfzlVY6h
BAQs+ZzCRUX+8D75C8ruqUt3gnoLwuMqAR7TzmLQJLaSAQHxcbsKpsXq9roAnBFl
SLVCk+bULJ843iw7SGCoYUtVMAnwveYoIaIEP34bbgPXYvLC0pzP9qB/GpssKnr6
h69ihKyD3vGDe92CW9hdhyuC/PdIOlfOZ3Xf2C+PB2hiR0Z6oG05Ka+TKg7RXsUF
bse2nSfSzb0KTajKwlWIHkaJTK9MvZ+tCLntPAKp1JGUzQdgF8viUkueXI2CdR8L
SJZqt806Avu9Tf9MjRtXu4MAV2CgVsFeY7ImzXG5bLeUFdmy7tQWIfJrnqK4PASv
19obCFkCgYEA61n2oqJPxPPi+9Wks1+11MmwDyA8KUtkL5hMye+DBVhM4xW4RgO7
xt/zir++CBECJhKq88ZBU0ndJB7tEQEuTcKCV1GTPfpRD7Myh7pyKOA3st1O4Uxg
S76xFeim2FTYbUKg/XCS3pvH4Q/JUBwi/qAHdsalMbcsqq/SC8uR+7kCgYEA08KN
fQyWJik5PXcRqdLfzUhLMMTIQIktIrV+m2uewCv67WPh0H2w78eerVuMYpF0EUrA
80CAAtcl9rZXT+3Z09FAFBnuc7chOt3+vZAZCalzqmX+6555QV2TkK/6B1H73lyL
YMD1sVA4AFYi9B7dgyXlh7nFcURcm4CdcUaxB20CgYALO6gB6y1TgTB8RJ4v0Ymk
Nlwo3KkCb47AlsxTdxMR1j0VOZwp+1OjEl1VagFv8R/hIVL3f6buir/7UV6PSTck
jvwZntMgSipETZFD2SpJuSnvZ5C0QCj4dImPOiN8f9A0ptF4Rz87UMQhgddh83XY
IVs52BFaZhvDqdCkr3qwQQKBgFX1VI/dSxnkg/rCWaYxFm3zGaqLRqqDxJGhUOpw
Djn94Fb6w5BpZSiARJYkYmEkoBPg32Ae35fHk/6I1/p3F4QXHcbLG/NW9CM8OArk
8nTslyolSwyEAL6a6KrD9F+CVRZXRLCaw2Edqg3g6UFlQg/Zk0m8DDzFPj5VQBPa
WUQlAoGBANCAAyqQe8LuruDZ22kJFg3qr55qeFUDCMrmXz6R+4qRnCHqQXCnfZEX
T4Xlzsm2UqVzqtTT32KMVoIuL1frRpVHVFDqoq1hXOs93CNq10aToweM0opwWBa4
yveNltcElLVK+n7ZjtL/ruF9EbEYYKTinLKhvqOowfmjnVZ5L1YS
-----END RSA PRIVATE KEY-----"""
8. เสร็จแล้วรันคำสั่ง
poetry run python manage.py migrate
9. ติดตั้ง JavaScript Dependencies
pnpm i
10. สร้าง superuser
poetry run python manage.py createsuperuser
Gunicorn และ Uvicorn
Saleor ใช้ Uvicorn ทำงานร่วมกับ Gunicorn ดังนี้ครับ
ให้สร้างไฟล์ gunicorn.socket ด้วยคำสั่ง
sudo nano /etc/systemd/system/gunicorn.socket
แล้วคัดลอกโค้ดนี้เข้าไปครับ
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
ต่อไปสร้างไฟล์ gunicorn.service ด้วยคำสั่ง
sudo nano /etc/systemd/system/gunicorn.service
คัดลอกโค้ดนี้เข้าไปครับ
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/root/saleor-api
Environment="PATH=/root/saleor-api/env/bin"
ExecStart=/root/saleor-api/env/bin/gunicorn \
--access-logfile - \
--workers 4 \
--bind unix:/run/gunicorn.sock \
-k uvicorn.workers.UvicornWorker \
saleor.asgi:application
[Install]
WantedBy=multi-user.target
Nginx (Reverse Proxy)
สร้างไฟล์ saleor-api ใน Nginx ด้วยคำสั่ง
sudo nano /etc/nginx/sites-available/saleor-api
คัดลอกโค้ดนี้เข้าไปครับ
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /root/saleor-api;
}
location /media/ {
root /root/saleor-api;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
ไฟล์ที่เราต้องใช้มีทั้งหมดแค่นี้ครับ
-
gunicorn.socket
-
gunicorn.service
-
saleor-api
เมื่อมีข้อมูลครบทั้ง 3 ไฟล์ ให้รันคำสั่งทั้งหมดนี้พร้อมกันเลยครับ
sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo ln -s /etc/nginx/sites-available/saleor-api /etc/nginx/sites-enabled/
sudo systemctl reload nginx
วิธีแก้ปัญหาเบื้องต้น
ถ้า Gunicorn มีปัญหา
sudo journalctl -u gunicorn
ถ้า Nginx มีปัญหา
sudo nginx -t
sudo ufw allow 'Nginx Full'
Enable HTTPS สำหรับ Saleor Production
ให้รันคำสั่ง
sudo certbot --nginx
สามารถดูรายละเอียดได้ที่ บทความ นี้ครับ
สรุปการ Deploy Saleor Production
1. แก้ไขไฟล์ settings.py
2. เพิ่มไฟล์ .env
3. สร้างไฟล์สำหรับ Gunicorn & Uvicorn
4. และไฟล์สำหรับ Nginx
Saleor Plugins
การติดตั้ง Plugins มีข้อสังเกต ถ้าไม่ Enable Production หรือ Debug=False จะไม่สามารถติดตั้งได้ แม้ติดตั้งได้ แต่ทำงานไม่ได้ เพราะมันจะมองหา localhost:8000 ซึ่งถ้าให้ถูกต้องมันต้องมองหา saleor-api.your-domain.com/graphql/
Stripe Payment Plugins
เมื่อเราเปิดร้าน E-Commerce เราจะต้องมีระบบรับเงินจากลูกค้า ซึ่ง Saloer รองรับทั้ง
-
Adyen และ
-
Stripe
ดังนั้นเพื่อให้ระบบหลังบ้านของเราทำงานได้ครบวงจร เราจะติดตั้ง Stripe กันครับ
ไปที่ https://stripe.saleor.app
แล้วใส่ Saleor URL เข้าไป เช่น https://saleor-api.your-domain.com

หรือเราสามารถติดตั้งผ่าน Graphql ก็ได้ครับ ให้ไปที่ saleor-api.your-domain.com/graphql/
ใส่โค้ดนี้เข้าไปใน Playground ครับ
mutation stripeInstall {
appInstall(
input: {appName: "Stripe App", manifestUrl: "https://stripe.saleor.app/api/manifest", permissions: [HANDLE_PAYMENTS]}
) {
appInstallation {
id
status
appName
manifestUrl
}
errors {
field
message
code
permissions
}
}
}
โดยต้องมี Token ด้วยครับ ตามรูป

วิธีหา Token
จำได้มั๊ยครับตอนเราใช้คำสั่ง createsuperuser
poetry run python manage.py createsuperuser
เพื่อสร้าง Super User ให้เราใช้ข้อมูลตรงนั้นแหละครับ มาขอ Token
ไปที่ Graphql Playground เปิดแท็บใหม่ครับ
ใส่โค้ดนี้เข้าไปครับ
mutation createToken {
tokenCreate(email: "your-email", password: "your-password") {
token
refreshToken
errors {
field
message
}
}
}
จะได้ Token ตามรูปข้างล่างครับ ให้คัดลอกข้อมูลทั้งหมดที่อยู่ระหว่างเครื่องหมายคำพูด หรือ "" แล้วนำไปใส่ใน Headers ตอนเราติดตั้ง Stripe App

วิธีแก้ปัญหา Saleor Plugins
1. App permissions
Pain Point: เนื่องจาก Saleor Plugins เป็น App แยกออกมาจาก Saloer Core (API) ตอนติดตั้งแต่ละ App มันจะไปลงทะเบียน /api/register App กับ Saleor Core (API) และเก็บ JWT Auth ไว้บน APL โดยอ้างอิง App ID ทีนี้ถ้าเราลง App ตัวอื่นซึ่งใช้ App ID เดียวกันทำให้ JWT Auth ของ App เก่าโดนเขียนทับ ทำให้เมื่อลง App ใหม่ ส่งผลให้ App ที่เคยลงไว้ก่อนทำงานไม่ได้เพราะ JWT Auth ถูกเขียนทับไปแล้ว ซึ่งปัญหานี้เกิดกับกรณีที่เราใช้ APL เป็น Upstash
Solution: ให้แก้ปัญหาเบื้องต้นโดยการเปิดใช้ Upstash หลาย Accounts อย่าใช้ Upstash ซ้ำกัน หรือ แก้ App ID ให้แตกต่างกัน อ่านเพิ่มเติมจาก ลิงค์
2. วิธีทดสอบ AWS SES (SMTP)
ตอนสร้าง AWS SES (SMTP) ตรวจสอบให้แน่ใจว่าเราสร้างไว้ที่ สิงคโปร์ (ap-southeast-1) แล้ว ให้รันคำสั่ง
openssl s_client -crlf -quiet -starttls smtp -connect email-smtp.ap-southeast-1.amazonaws.com:587
ถ้าขึ้น Status OK ก็แสดงว่า AWS SES (SMTP) Endpoint ทำงานเป็นปกติ