Bunchee
Back to Blog

Deploy Saleor Production บน Ubuntu 22.04

จากการตามอ่านใน stackoverflow.com และตามที่ต่างๆบนโลกอินเตอร์เน็ต ผมพอจะคลำทางวิธีการ Deploy Saleor Production mode บน Ubuntu 22.04 ตามนี้ครับ

Deploy Saleor Production บน Ubuntu 22.04

ถ้าดูข้อมูลบนเว็บไซต์ของ saleor จะไม่มีรายละเอียดบอกถึงการ Deploy Saleor Production mode มีแต่เพียง Development mode จากการตามอ่านใน stackoverflow.com และตามที่ต่างๆบนโลกอินเตอร์เน็ต ผมพอจะคลำทาง วิธีการ Deploy Saleor Production mode บน Ubuntu 22.04 ได้ดังนี้ครับ

Saleor คือ?

Saleor เป็นโปรแกรมบริหารจัดการ E-Commerce แบบ Headless คือมันจะทำหน้าที่เป็นเพียงระบบหลังบ้านเท่านั้นครับ หรือ Back-End คอยจัดการระบบสินค้า ระบบรับชำระเงินออนไลน์ ระบบสต็อก ระบบโปรโมชั่นต่างๆ เช่น Gift Card หรือ ส่วนลด ที่คิดเป็น เปอร์เซ็นต์จากยอดขาย เป็นต้น ซึ่งจะเขียนด้วยภาษา Python/Django ทั้งนี้เราสามารถเขียนโค้ด Front-End มาเชื่อมต่อได้ โดยไม่ต้องกังวลว่า Front-End จะทำงานหนักเกินไปหรือเปล่า เพราะเราได้แยกส่วนของ E-Commerce ออกมาแล้ว ซึ่งจะคล้ายๆกับหลักการของ Micro Services นั่นเองครับ ซึ่งเทคโนโลยีที่ใช้เขียน Front-End เราสามารถใช้ NextJS หรือ ReactJS ก็ได้ครับ

Ubuntu 22.04 บน Linode

ก่อนอื่นก็ต้องสร้าง Ubuntu Server กันก่อนครับ ในที่นี้จะใช้ Ubuntu บน Linode เนื่องจากราคาจะถูกกว่า AWS EC2 โดยให้ทำตามขั้นตอนดังนี้ครับ

สมัครเป็นผู้ใช้งาน Linode โดยต้องยืนยันตัวตนผ่านบัตรเครดิต หรือบัตรเดบิต ก็ได้ครับ มีเงินติดอยู่ในบัตรเดบิต ซัก 50 บาท ก็น่าจะได้แล้วครับ ขั้นตอนการยืนยันตัวตน เขาจะ Hold เงินเอาไว้ 1USD พอยืนยันตัวตนเสร็จ เขาจะคืนเงินให้ครับ

ทั้งนี้ ลูกค้ารายใหม่จะได้รับเครดิต 100USD สามารถนำไปใช้ได้ภายใน 60 วัน ดังนั้นจึงไม่ต้องกังวัลครับ เราสามารถเข้ามาลองฝึกทักษะคอมพิวเตอร์ได้ โดยไม่ต้องจ่ายเงินเลยใน 60 วันแรกครับ

เมื่อล็อกอินเข้าไปได้แล้ว

1. ให้คลิ๊ก Linodes ตรง Sidebar ที่อยู่ด้านซ้าย

2. แล้วคลิ๊กปุ่ม Create ตามภาพประกอบครับ

Deploy Saleor Production Ubuntu 22.04 Linode 1

3. จาก Drop Down Lists ให้เลือก Linode

Deploy Saleor Production Ubuntu 22.04 Linode 2

4. Images เลือก Ubuntu 22.04 LTS

5. ตรง Region เลือก Singapore ครับ เพราะอยู่ใกล้เราสุดแล้ว

6. ต่อมา Linode Plan เลือกแท็บ Shared CPU แล้วเลือก Linode 4 GB จะประกอบด้วย RAM 4 GB CPUs 2 ซึ่งเป็นสเปกขั้นต่ำ ที่ใช้สำหรับ รัน Saleor ครับ โดยเขาจะคิดค่าใช้จ่ายต่อเดือนที่ 24USD ครับ ผมเข้าใจว่าถูกว่าของ AWS EC2

Deploy Saleor Production Ubuntu 22.04 Linode 3

7. สร้างป้ายชื่อตรง Linode Label ใส่ชื่ออะไรก็ได้ ในที่นี้ตั้งชื่อเป็น Saleor

8. กำหนดรหัสผ่านสำหรับ Root user ครับ

นอกนั้นปล่อยตามค่า Default

Deploy Saleor Production Ubuntu 22.04 Linode 4

9. ตรง Add-ons จะเลือก Private IP ก็ได้นะครับ ในกรณีนี้จะเลือก Private IP ด้วยครับ

10. เมื่อมาถึงตรงนี้ก็กดปุ่ม Create Linode ได้เลยครับ

Deploy Saleor Production Ubuntu 22.04 Linode 5

Linode จะมี Ubuntu ที่ติดตั้ง Docker และ Docker-Compose มาให้แล้วตรงแท็บ Marketplace แต่ผมขอติดตั้งเองทั้งหมด จึงไม่ได้ใช้ Ubuntu จากแท็บ Marketplace

ตอนนี้เราได้ Ubuntu 22.04 LTS เรียบร้อยแล้วครับ ขั้นตอนต่อไปติดตั้ง Docker และ Docker-Compose

ติดตั้ง Docker และ Plugin

เปิด Terminal บนเครื่องของเรา และเชื่อมต่อไปที่ Linode

ให้คัดลอกโค้ดนี้ไปที่ Terminal แล้วกด Enter ได้เลยครับ

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

ติดตั้ง Docker packages

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

ต่อด้วยการติดตั้ง docker-compose (Docker plugin)

sudo apt-get install docker-compose

ตรวจสอบว่าการติดตั้งเสร็จสมบูรณ์มั๊ยด้วยคำสั่ง

docker --version
docker-compose --version

Saleor สำหรับ Production

Saleor สำหรับ Production ที่จะกล่าวถึงในบทความนี้ มีทั้งหมด 2 ส่วนคือ

1. Saleor-Core

2. Saleor-Dashboard

เราจะแก้โค้ดนิดหน่อย แล้วสร้าง Image สำหรับแต่ละส่วนครับ

Saleor Core หรือ API

เป็นส่วนสำหรับทำงานหลังบ้าน หรือ Back-End หรือเป็นส่วนที่ดูแล ฐานข้อมูล การดึงข้อมูลจากฐานข้อมูลมาใช้งาน ผ่าน Graphql เพราะ Saleor นำเทคโนโลยี Graphql มาใช้ในการจัดการข้อมูลนั่นเองครับ

ตามคำแนะนำของ Saleor เขาไม่แนะนำให้ใช้โค้ดที่ branch main เนื่องจากเป็น branch ที่ใช้สำหรับ Development แต่เขาให้เราไปดึงโค้ดมาจาก releases เพราะมันทำงานเสถียรแล้วนั่นเอง ไปที่

https://github.com/saleor/saleor/releases/

คลิ๊กขวาตรง Source code (zip) เลือก Copy Link Address

saleor core 1

ไปที่ Terminal แล้ววางลิงค์ไว้ข้างหลังคำสั่ง wget ดังนี้ครับ

wget https://github.com/saleor/saleor/archive/refs/tags/3.18.9.zip

เราจะได้ไฟล์ 3.18.9.zip วางบนโฟลเดอร์ Root

ติดตั้ง โปรแกรม Unzip บน Ubuntu

sudo apt-get install zip unzip

เสร็จแล้วใช้คำสั่ง unzip ที่โฟลเดอร์ Root ที่เราดาวน์โหลดไฟล์ Zip มาครับ

unzip 3.18.9.zip

เราจะได้โฟลเดอร์ saleor-3.18.9

ให้เปลี่ยนชื่อ โฟลเดอร์เป็น saleor-api ด้วยคำสั่ง

cp -r saleor-3.18.9 saleor-api

ลบไฟล์ 3.18.9.zip ทิ้ง ด้วยคำสั่ง

rm 3.18.9.zip

และลบโฟลเดอร์ saleor-3.18.9

rm -rf saleor-3.18.9

หรือจะให้ง่ายกว่านั้น ให้ใช้คำสั่ง Move

mv saleor-3.18.9 saleor-api

แก้ไข saleor-api > Dockerfile

เปิดโฟลเดอร์ saleor-api ด้วย VSCode แล้วไปที่ Dockerfile

ให้แก้โค้ดใน saleor-api > Dockerfile ตามนี้ครับ

### Build and install packages
FROM python:3.9 as build-python

RUN apt-get -y update \
  && apt-get install -y gettext \
  # Cleanup apt cache
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
WORKDIR /app
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip pip install poetry==1.7.0
RUN poetry config virtualenvs.create false
COPY poetry.lock pyproject.toml /app/
RUN --mount=type=cache,mode=0755,target=/root/.cache/pypoetry poetry install --no-root

### Final image
FROM python:3.9-slim

RUN groupadd -r saleor && useradd -r -g saleor saleor

RUN apt-get update \
  && apt-get install -y \
  libcairo2 \
  libgdk-pixbuf2.0-0 \
  liblcms2-2 \
  libopenjp2-7 \
  libpango-1.0-0 \
  libpangocairo-1.0-0 \
  libssl3 \
  libtiff6 \
  libwebp7 \
  libxml2 \
  libpq5 \
  shared-mime-info \
  mime-support \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

RUN echo 'image/webp webp' >> /etc/mime.types
RUN echo 'image/avif avif' >> /etc/mime.types

RUN mkdir -p /app/media /app/static \
  && chown -R saleor:saleor /app/

COPY --from=build-python /usr/local/lib/python3.9/site-packages/ /usr/local/lib/python3.9/site-packages/
COPY --from=build-python /usr/local/bin/ /usr/local/bin/
COPY . /app
WORKDIR /app

ARG DATABASE_URL
ENV DATABASE_URL ${DATABASE_URL:-postgres://your-user:your-password@localhost:5432/your-db-name}

ARG STATIC_URL
ENV STATIC_URL ${STATIC_URL:-/static/}
RUN SECRET_KEY=aAT2HyCQ7zi60309QM5EnToQZ0dwk4vq STATIC_URL=${STATIC_URL} python3 manage.py collectstatic --no-input

EXPOSE 8001
ENV PYTHONUNBUFFERED 1

LABEL org.opencontainers.image.title="saleor/saleor"                                  \
      org.opencontainers.image.description="\
A modular, high performance, headless e-commerce platform built with Python, \
GraphQL, Django, and ReactJS."                                                         \
      org.opencontainers.image.url="https://saleor.io/"                                \
      org.opencontainers.image.source="https://github.com/saleor/saleor"               \
      org.opencontainers.image.authors="Saleor Commerce (https://saleor.io)"           \
      org.opencontainers.image.licenses="BSD 3"

CMD ["gunicorn", "--bind", ":8001", "--workers", "4", "--worker-class", "saleor.asgi.gunicorn_worker.UvicornWorker", "saleor.asgi:application"]

ผมแก้แค่ 3 จุดครับ คือ

1. EXPOSE 8000 แก้เป็น EXPOSE 8001

2. CMD \["gunicorn", "--bind", ":8000", ...\] เป็น CMD \["gunicorn", "--bind", ":8001", ...\]

3. เพิ่มโค้ดนี้เข้าไปครับ เพื่อระบุ DATABASES

ARG DATABASE_URL
ENV DATABASE_URL ${DATABASE_URL:-postgres://your-user:your-password@localhost:5432/your-db-name}

ดูวิธีสร้างฐานข้อมูล ได้ที่ บทความนี้

แก้ไข แค่นั้นเองครับสำหรับไฟล์ saleor-api > Dockerfile

แก้ไข saleor-api > saleor > settings.py

1. ให้ค้นหาคำว่า ALLOWED_HOSTS แล้วแก้ตามนี้ครับ

ALLOWED_HOSTS = [".localhost", "127.0.0.1", "[::1]",".your-domain.com"]

ALLOWED_HOSTS คือ ชื่อโดเมนของ Saleor-API ครับ เช่น saleor-api.your-domain.com

อย่าลืมเปลี่ยน ".your-domain.com" เป็นชื่อโดเมนของคุณนะครับ

2. ต่อด้วย DATABASES ให้แก้ตามนี้ครับ

DATABASES = {
    DATABASE_CONNECTION_DEFAULT_NAME: dj_database_url.config(
        # default="postgres://saleor:saleor@localhost:5432/saleor",
        default=env("DATABASE_URL"),
        conn_max_age=DB_CONN_MAX_AGE,
    ),
    DATABASE_CONNECTION_REPLICA_NAME: dj_database_url.config(
        # default="postgres://saleor:saleor@localhost:5432/saleor",
        default=env("DATABASE_URL"),
        # TODO: We need to add read only user to saleor platform,
        # and we need to update docs.
        # default="postgres://saleor_read_only:saleor@localhost:5432/saleor",
        conn_max_age=DB_CONN_MAX_AGE,
    ),
}

ในเบื้องต้นให้แก้ไขไฟล์ settings.py แค่นี้ก่อนครับ

สร้าง Saleor API image สำหรับ Production

ให้ไปที่โฟลเดอร์ saleor-api (หรือ saleor-core ตั้งชื่อตามใจชอบครับ) เราจะเห็นไฟล์ Dockerfile

เราจะสร้าง Image สำหรับ Production ที่ตรงนี้ครับ ให้รันคำสั่ง

docker build --tag 'saleor-core-v1' . 

เสร็จแล้วเราจะได้ชื่อ Image ตรงบรรทัดสุดท้าย หลังจาก Docker ประมวลผลเสร็จครับ ให้จดชื่อ Image ไว้ครับ

เช่น

docker.io/library/saleor-core-v1

Saleor Dashboard

ขั้นตอนต่อไปจะติดตั้ง Dashboard สำหรับ Admin เอาไว้ทำงาน หรือเชื่อมต่อกับ Saleor Core (API) ซึ่งขั้นตอนจะคล้ายๆกันครับ

ติดตั้ง NVM

เนื่องจากโปรแกรม Saleor Dashboard เป็นส่วนของ Front-End นะครับ เพียงแต่จำกัดเฉพาะ Admin เท่านั้นที่เข้าใช้ได้ และโปรแกรมนี้เขียนด้วยภาษา JavaScript เราจึงต้องติดตั้ง NodeJS ครับ เพื่อให้สะดวก เราจะติดตั้งผ่าน NVM ครับ ให้ติดตั้ง NVM ด้วยคำสั่ง

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

restart bash หรือ zsh ด้วยคำสั่ง

source ~/.bashrc

หรือ

source ~/.zshrc

ติดตั้ง NodeJS

เนื่องจาก Saleor แนะนำให้ใช้ NodeJS เวอร์ชั่น 18 ไม่เกิน เวอร์ชั่น 19 ผมจึงขอใช้ เวอร์ชั่น 18.19.0 หรือ nvm เรียกว่า lts/hydrogen

พิมพ์คำสั่งนี้บน Terminal

nvm install lts/hydrogen
nvm use lts/hydrogen

จะได้คำตอบแบบนี้ครับ แสดงว่าตอนนี้เราใช้ NodeJS เวอร์ชั่น 18.19.0 แล้วครับ

Now using node v18.19.0 (npm v10.2.3)

Clone Saleor Dashboard

ต่อไปก็ Clone Saleor Dashboard repo มาไว้ที่ Ubuntu โฟลเดอร์ Root ครับ

git clone https://github.com/saleor/saleor-dashboard.git
cd saleor-dashboard

สั่งติดตั้ง NodeJS packages ด้วยคำสั่ง

npm i

ผมลองใช้คำสั่ง pnpm i ผมพบ Error คือมันหาบาง Dependencies ไม่เจอครับ แนะนำให้ใช้ npm i

ถ้าต้องการทดสอบโค้ดบนเครื่องของเรา สามารถใช้คำสั่ง

npm run dev

แต่เนื่องจากผมจะสั่งทำงานผ่าน Docker จึงข้ามตรงนี้ไปครับ

แก้ไข saleor-dashboard > Dockerfile

เปิดไฟล์ Dockerfile ด้วย VSCode แล้วค้นหาคำว่า ENV API_URI

ให้แก้ตรง http://localhost:8000/graphql/ เป็น https://saleor-api.your-domain.com/graphql/ ตามโค้ดด้านล่างนี้

ENV API_URI ${API_URI:-https://saleor-api.your-domain.com/graphql/}

ตรวจดูโค้ดให้ได้ตามนี้ครับ

FROM node:18-alpine as builder
RUN apk --no-cache add bash
WORKDIR /app
COPY package*.json ./
ENV CI 1
RUN npm ci --legacy-peer-deps

COPY nginx/ nginx/
COPY assets/ assets/
COPY locale/ locale/
COPY scripts/ scripts/
COPY vite.config.js ./
COPY tsconfig.json ./
COPY sw.js ./
COPY *.d.ts ./
COPY schema.graphql ./
COPY introspection.json ./
COPY introspection*.json ./
COPY .featureFlags/ .featureFlags/

COPY src/ src/

ARG API_URI
ARG APP_MOUNT_URI
ARG APPS_MARKETPLACE_API_URI
ARG APPS_TUNNEL_URL_KEYWORDS
ARG STATIC_URL
ARG SKIP_SOURCEMAPS

ENV API_URI ${API_URI:-https://saleor-api.your-domain.com/graphql/}
ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/dashboard/}
ENV APPS_MARKETPLACE_API_URI ${APPS_MARKETPLACE_API_URI:-https://apps.saleor.io/api/v2/saleor-apps}
ENV APPS_TUNNEL_URL_KEYWORDS ${APPS_TUNNEL_URL_KEYWORDS}
ENV STATIC_URL ${STATIC_URL:-/dashboard/}
ENV SKIP_SOURCEMAPS ${SKIP_SOURCEMAPS:-true}
RUN npm run build

FROM nginx:stable-alpine as runner
WORKDIR /app

COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY ./nginx/replace-api-url.sh /docker-entrypoint.d/50-replace-api-url.sh
COPY --from=builder /app/build/ /app/

LABEL org.opencontainers.image.title="saleor/saleor-dashboard"                                  \
      org.opencontainers.image.description="A GraphQL-powered, single-page dashboard application for Saleor." \
      org.opencontainers.image.url="https://saleor.io/"                                \
      org.opencontainers.image.source="https://github.com/saleor/saleor-dashboard"     \
      org.opencontainers.image.revision="$COMMIT_ID"                                   \
      org.opencontainers.image.version="$PROJECT_VERSION"                              \
      org.opencontainers.image.authors="Saleor Commerce (https://saleor.io)"           \
      org.opencontainers.image.licenses="BSD 3"

อย่าลืมเปลี่ยน your-domain.com เป็นชื่อโดเมนของคุณนะครับ

สร้าง Saleor Dashboard image สำหรับ Production

ไปที่โฟลเดอร์ saleor-dashboard ให้ดูว่าเรามองเห็นไฟล์ Dockerfile หรือไม่ ถ้ามองเห็นก็แสดงว่า ถูกแล้ว

ให้สร้าง Image โดยมีขั้นตอนเหมือนกันกับ Saleor Core (API) คือรันคำสั่งดังนี้ครับ

docker build --tag 'saleor-dashboard-v1' .

เมื่อประมวลผลเสร็จแล้ว ให้ดูบรรทัดสุดท้าย จะมีชื่อ Image อยู่ ให้จดชื่อไว้เหมือนเดิมครับ จะได้ชื่อประมาณนี้นะครับ

docker.io/library/saleor-dashboard-v1

Saleor Platform สำหรับ Production

ตอนนี้เราได้ Images มา 2 Images แล้วนะครับ คือ

docker.io/library/saleor-dashboard-v1
docker.io/library/saleor-core-v1

ต่อไปเราจะสั่ง Run Images ทั้ง 2 นี้ ด้วยคำสั่ง docker-compose แต่เราต้องไปดึง Repository สำหรับ Saleor Platform มาก่อนครับ ด้วยคำสั่ง

git clone https://github.com/saleor/saleor-platform.git
cd saleor-platform

ตอนนี้ ณ โฟลเดอร์ Root เราจะมีโฟลเดอร์ทั้งหมด 3 โฟลเดอร์แล้วนะครับ ที่ผมใส่ตัวเลขเอาไว้ตามภาพประกอบด้านล่างครับ

แก้ไขไฟล์ docker-compose.yml

เมื่อไปที่โฟลเดอร์ saleor-platform ให้เปิดไฟล์ docker-compose.yml แล้วแก้ไขไฟล์ตามนี้ครับ

version: "3.4"

services:
  api:
    # image: ghcr.io/saleor/saleor:3.18
    image: docker.io/library/saleor-core-v1
    ports:
      - 8001:8001
    restart: unless-stopped
    networks:
      - saleor-backend-tier
    stdin_open: true
    tty: true
    depends_on:
      - db
      - redis
      - jaeger
    volumes:
      # shared volume between worker and api for media
      - saleor-media:/app/media
    env_file:
      - common.env
      - backend.env
    environment:
      - ALLOWED_CLIENT_HOSTS=localhost,127.0.0.1,.your-domain.com,139.162.1.83,76.76.21.93
      - ALLOWED_GRAPHQL_ORIGINS=*
      - ENABLE_DEBUG_TOOLBAR=True
      - RSA_PRIVATE_KEY="""-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwq4CiHZeAfLY4Bj25XwRavq9M2Zz2j1yvTwLuhoCK7UVR1RG\nMWnHTXncNOL6uUXZdvQJpUrix3oMVcWW2l+7HDzw/crdCpIMUsirAU1biddeG6UI\nLKIlPw1NKM+SMKo5SIhiZTw0FklP1mh20voGCbs1rpmRtk5Mvu45t1/VMciCkot2\n1ldSZ9QmG4tnSEOZT3Flv/zreF01vq70qjQ/xBq4dDUnw3JoeJEmLGjudM2fG/+I\nqwH3cbH8Ems/EbcXFaYXrm1VTJ1k5rQnH0tcTTnIzcbX3+t+xs0FFGSfLzfv6y9C\nsl2uIblJVpUPTJBVqXgkME1MB58xvP4DS/M8xQIDAQABAoIBAAQ5La1jsfzlVY6h\nBAQs+ZzCRUX+8D75C8ruqUt3gnoLwuMqAR7TzmLQJLaSAQHxcbsKpsXq9roAnBFl\nSLVCk+bULJ843iw7SGCoYUtVMAnwveYoIaIEP34bbgPXYvLC0pzP9qB/GpssKnr6\nh69ihKyD3vGDe92CW9hdhyuC/PdIOlfOZ3Xf2C+PB2hiR0Z6oG05Ka+TKg7RXsUF\nbse2nSfSzb0KTajKwlWIHkaJTK9MvZ+tCLntPAKp1JGUzQdgF8viUkueXI2CdR8L\nSJZqt806Avu9Tf9MjRtXu4MAV2CgVsFeY7ImzXG5bLeUFdmy7tQWIfJrnqK4PASv\n19obCFkCgYEA61n2oqJPxPPi+9Wks1+11MmwDyA8KUtkL5hMye+DBVhM4xW4RgO7\nxt/zir++CBECJhKq88ZBU0ndJB7tEQEuTcKCV1GTPfpRD7Myh7pyKOA3st1O4Uxg\nS76xFeim2FTYbUKg/XCS3pvH4Q/JUBwi/qAHdsalMbcsqq/SC8uR+7kCgYEA08KN\nfQyWJik5PXcRqdLfzUhLMMTIQIktIrV+m2uewCv67WPh0H2w78eerVuMYpF0EUrA\n80CAAtcl9rZXT+3Z09FAFBnuc7chOt3+vZAZCalzqmX+6555QV2TkK/6B1H73lyL\nYMD1sVA4AFYi9B7dgyXlh7nFcURcm4CdcUaxB20CgYALO6gB6y1TgTB8RJ4v0Ymk\nNlwo3KkCb47AlsxTdxMR1j0VOZwp+1OjEl1VagFv8R/hIVL3f6buir/7UV6PSTck\njvwZntMgSipETZFD2SpJuSnvZ5C0QCj4dImPOiN8f9A0ptF4Rz87UMQhgddh83XY\nIVs52BFaZhvDqdCkr3qwQQKBgFX1VI/dSxnkg/rCWaYxFm3zGaqLRqqDxJGhUOpw\nDjn94Fb6w5BpZSiARJYkYmEkoBPg32Ae35fHk/6I1/p3F4QXHcbLG/NW9CM8OArk\n8nTslyolSwyEAL6a6KrD9F+CVRZXRLCaw2Edqg3g6UFlQg/Zk0m8DDzFPj5VQBPa\nWUQlAoGBANCAAyqQe8LuruDZ22kJFg3qr55qeFUDCMrmXz6R+4qRnCHqQXCnfZEX\nT4Xlzsm2UqVzqtTT32KMVoIuL1frRpVHVFDqoq1hXOs93CNq10aToweM0opwWBa4\nyveNltcElLVK+n7ZjtL/ruF9EbEYYKTinLKhvqOowfmjnVZ5L1YS\n-----END RSA PRIVATE KEY-----"""
      # - RSA_PRIVATE_PASSWORD
      - DEBUG=False
      # Original
      - JAEGER_AGENT_HOST=jaeger
      # - DASHBOARD_URL=http://localhost:9001/
      - DASHBOARD_URL=https://saleor-api.your-domain.com/dashboard/
      - STOREFRONT_URL=https://www.your-domain.com
      - ALLOWED_HOSTS=localhost,api,.your-domain.com
      - DEFAULT_COUNTRY=TH
      - DEFAULT_CURRENCY=THB
      - PUBLIC_URL=https://saleor-api.your-domain.com/
      - SECRET_KEY=oAT2HyCQ7zi60309QM5EnToQZ0dwk4vs

  dashboard:
    # image: ghcr.io/saleor/saleor-dashboard:latest
    image: docker.io/library/saleor-dashboard-v3.18.2
    ports:
      - 9001:80
    restart: unless-stopped
    # Custom
    environment:
      - API_URL=https://saleor-api.your-domain.com/graphql/

  db:
    image: library/postgres:13-alpine
    ports:
      - 5434:5432
    restart: unless-stopped
    networks:
      - saleor-backend-tier
    volumes:
      - saleor-db:/var/lib/postgresql/data
      - ./replica_user.sql:/docker-entrypoint-initdb.d/replica_user.sql
    environment:
      - POSTGRES_USER=saleor
      - POSTGRES_PASSWORD=saleor

  redis:
    image: library/redis:7.0-alpine
    ports:
      - 6379:6379
    restart: unless-stopped
    networks:
      - saleor-backend-tier
    volumes:
      - saleor-redis:/data

  worker:
    # image: ghcr.io/saleor/saleor:3.18
    image: docker.io/library/saleor-api-3.18.12
    command: celery -A saleor --app=saleor.celeryconf:app worker --loglevel=info -B
    restart: unless-stopped
    networks:
      - saleor-backend-tier
    env_file:
      - common.env
      - backend.env
    depends_on:
      - redis
      - mailpit
    volumes:
      # shared volume between worker and api for media
      - saleor-media:/app/media

  jaeger:
    image: jaegertracing/all-in-one
    ports:
      - "5775:5775/udp"
      - "6831:6831/udp"
      - "6832:6832/udp"
      - "5778:5778"
      - "16686:16686"
      - "14268:14268"
      - "9411:9411"
    restart: unless-stopped
    networks:
      - saleor-backend-tier

  mailpit:
    image: axllent/mailpit
    ports:
      - 1025:1025 # smtp server
      - 8025:8025 # web ui. Visit http://localhost:8025/ to check emails
    restart: unless-stopped
    networks:
      - saleor-backend-tier

volumes:
  saleor-db:
    driver: local
  saleor-redis:
    driver: local
  saleor-media:

networks:
  saleor-backend-tier:
    driver: bridge

1. กล่าวโดยสรุปคือ ผมจะต้องบอก docker-compose ว่าผมจะใช้ พอร์ต 8001 ที่ Saleor Core (ผมจะเก็บพอร์ต 8000 ไว้สำหรับโปรแกรมตัวอื่น) และอย่าลืมแก้ API_URL=https://saleor-api.your-domain.com/graphql/ เป็นชื่อโดเมนของคุณนะครับ

2. ใช้พอร์ต 9001 สำหรับ Saleor Dashboard และอย่าลืมแก้ - DASHBOARD_URL=https://dashboard-your-domain.com/ เป็นชื่อโดเมนที่คุณใช้สำหรับ Dashboard URL นะครับ

3. สำหรับ STOREFRONT_URL อาจเป็น https://www.your-domain.com หรือ https://store.your-domain.com ขึ้นอยู่กับว่า เราจะตั้งชื่อโดเมนของเราอย่างไร ซึ่ง STOREFRONT_URL คือ URL ที่เราใช้โชว์สินค้า และรับชำระเงินค่าสินค้านั่นเองครับ

เมื่อแก้ไขไฟล์ docker-compose.yml ในโฟลเดอร์ saleor-platform เสร็จแล้ว ให้รันคำสั่งต่อไปนี้ทีละคำสั่ง

docker compose build
docker compose run --rm api python3 manage.py migrate

ในกรณีที่ต้องการ สร้างฐานข้อมูล ตัวอย่าง ให้รันคำสั่งนี้เพิ่มเข้าไปครับ

docker compose run --rm api python3 manage.py populatedb

ขั้นตอนสุดท้าย สร้าง superuser ครับ

docker compose run --rm api python3 manage.py createsuperuser

เหลืออีกคำสั่งคือคำสั่ง docker-compose up -d เป็นคำสั่งให้ Run Images ทั้ง 2 Images คือ

docker.io/library/saleor-dashboard-v1
docker.io/library/saleor-core-v1

ให้ทำงานพร้อมกัน แต่เราจะแวะไปสร้าง Proxy Server ด้วย Nginx ก่อนครับ


Nginx Reverse Proxy

ติดตั้ง Nginx และเปิด Firewall สำหรับ Nginx

sudo apt-get install nginx
sudo systemctl start nginx
sudo ufw allow 'Nginx Full'

Gunicorn และ Uvicorn

Saleor Core (API) เขียนบน Django (Python) เมื่อพูดถึง Django App ก็จะต้องพูดถึง Gunicorn ให้สร้างไฟล์ต่อไปนี้ครับ

1. สร้างไฟล์ gunicorn.socket ด้วยคำสั่ง

sudo nano /etc/systemd/system/gunicorn.socket

ใส่โค้ดนี้เข้าไปครับ

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

2. สร้างอีกไฟล์หนึ่ง ชื่อว่า 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 config

สร้างไฟล์ saleor-api ด้วยคำสั่ง

sudo nano /etc/nginx/sites-available/saleor-api

เพิ่มโค้ดนี้เข้าไปในไฟล์ saleor-api

server {
        # replace example.com with your domain name
        server_name saleor-api.your-domain.com;

        listen 80;
        listen [::]:80;

        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;
        }

        location /dashboard/ {
                proxy_pass http://127.0.0.1:9001;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

สามารถตั้งชื่อได้ตามใจชอบนะครับ อย่าลืมเปลี่ยน your-domain.com เป็นชื่อโดเมนของคุณนะครับ

และที่สำคัญอย่าลืมไปเพิ่ม A Record ในระบบ DNS ด้วยนะครับ โดยนำ IP Address ของ Linode ไปใส่ในช่อง A Record

สุดท้ายให้คัดลอกไฟล์จาก sites-available ไปที่ sites-enabled ด้วยคำสั่ง

sudo ln -s /etc/nginx/sites-available/saleor-api /etc/nginx/sites-enabled/saleor-api

Enable SSL โดย Certbot

ให้ขอ Certificate สำหรับ

saleor-api.your-domain.com

เพื่อ Enable HTTPS (SSL) นะครับ

ติดตั้ง Certbot สำหรับ Nginx

sudo apt-get install certbot python3-certbot-nginx

ต่อไปรันคำสั่ง

sudo certbot --nginx

วิธีแก้ปัญหาขอ Certificate จาก Certbot ไม่สำเร็จ

1. Pain point: ปัญหามาจาก ชื่อโดเมนครับ

Solution: ให้เช็คชื่อโดเมนของเราว่าสามารถมองเห็นบนโลกอินเตอร์เน็ตหรือเปล่า ได้ที่ https://letsdebug.net/ และ https://unboundtest.com/

2. Pain point: ปัญหามาจาก การลืมรีสตาร์ท Nginx

Solution: ให้รีสตาร์ท Nginx ครับ ด้วยคำสั่ง

sudo systemctl reload nginx

3. Pain point: ปัญหามาจากการสะกดชื่อโดเมนผิด

Solution: ให้กลับไปตรวจสอบชื่อโดเมนในไฟล์ /etc/nginx/sites-available/your-domain ให้ตรงกันกับ ชื่อโดเมนใน DNS ครับ


การบำรุงรักษา

1. ต้องการลบ Docker container ที่ไม่ได้ใช้งาน ให้ใช้คำสั่ง

docker ps --filter status=exited -q | xargs docker rm

Run docker-compose up -d

เมื่อเราสร้าง Reverse Proxy ด้วย Nginx และ Enable SSL ด้วย Certbot ให้เว็บไซต์ของเราเรียบร้อยแล้ว เราสามารถสั่ง Run เว็บไซต์ของเราได้เลยครับ ด้วยคำสั่ง

docker-compose up -d

เมื่อเข้าไปที่ URL saleor-api.your-domain.com/graphql/ จะได้หน้าจอแบบนี้ครับ หรือหน้าจอ Graphql (API) นั่นเองครับ

และถ้าเข้า URL saleor-api.your-domain.com/dashboard/ จะได้หน้าจอแบบนี้ครับ หรือหน้าจอ Dashboard นั่นเองครับ

สรุปการ Deploy Saleor Production

เราจะทำงานบน 3 Repositories คือ

1. saleor-core

2. saleor-dashboard

3. saleor-platform

โดยใน Repository saleor-core และ saleor-dashboard จะมีไฟล์ Dockerfile เพื่อเอาไว้สร้าง Docker Image

และใน Repository saleor-platform จะมีไฟล์ docker-compose.yml เอาไว้ Run Docker Images (สามารถ run พร้อมกันหลาย Images)

ในกรณีที่เราต้องการเปลี่ยนพอร์ตการทำงาน เราก็ต้องไปแก้ใน Repository ของ saleor-core และ saleor-dashboard ก่อนจะสั่ง run Images

การแก้ไข ค่า environment ที่ใช้สำหรับ Production สามารถแก้ไขได้ที่ไฟล์ backend.env และ common.env ในโฟลเดอร์ saleor-platform

หรือแก้โดยตรงที่ไฟล์ docker-compose.yml