Zero to Hero: สูตรการผลิตขวดเหล้า

เผยแพร่แล้ว: 2022-03-11

ในฐานะวิศวกรแมชชีนเลิร์นนิงและผู้เชี่ยวชาญด้านคอมพิวเตอร์วิทัศน์ ฉันพบว่าตัวเองกำลังสร้าง API และแม้แต่เว็บแอปด้วย Flask บ่อยครั้งอย่างน่าประหลาดใจ ในโพสต์นี้ ฉันต้องการแบ่งปันเคล็ดลับและสูตรอาหารที่มีประโยชน์สำหรับการสร้างแอปพลิเคชัน Flask ที่พร้อมสำหรับการผลิตอย่างสมบูรณ์

เราจะครอบคลุมหัวข้อต่อไปนี้:

  1. การจัดการการตั้งค่า. แอปพลิเคชันในชีวิตจริงมีวงจรชีวิตที่มีขั้นตอนเฉพาะ อย่างน้อยที่สุดก็คือการพัฒนา การทดสอบ และการใช้งาน ในแต่ละขั้นตอน รหัสแอปพลิเคชันควรทำงานในสภาพแวดล้อมที่แตกต่างกันเล็กน้อย ซึ่งจำเป็นต้องมีชุดการตั้งค่าที่แตกต่างกัน เช่น สตริงการเชื่อมต่อฐานข้อมูล คีย์ API ภายนอก และ URL
  2. แอปพลิเคชั่น Flask แบบโฮสต์เองด้วย Gunicorn แม้ว่า Flask จะมีเว็บเซิร์ฟเวอร์ในตัว อย่างที่เราทุกคนทราบดีว่ามันไม่เหมาะสำหรับการใช้งานจริงและจำเป็นต้องวางไว้เบื้องหลังเว็บเซิร์ฟเวอร์จริงที่สามารถสื่อสารกับ Flask ผ่านโปรโตคอล WSGI ได้ ตัวเลือกทั่วไปสำหรับสิ่งนั้นคือ Gunicorn—เซิร์ฟเวอร์ Python WSGI HTTP
  3. ให้บริการไฟล์สแตติกและคำขอพร็อกซี่ด้วย Nginx ในขณะที่เป็นเว็บเซิร์ฟเวอร์ HTTP ในทางกลับกัน Gunicorn เป็นแอปพลิเคชันเซิร์ฟเวอร์ที่ไม่เหมาะที่จะเผชิญกับเว็บ นั่นเป็นเหตุผลที่เราต้องการ Nginx เป็น reverse proxy และให้บริการไฟล์สแตติก ในกรณีที่เราจำเป็นต้องขยายขนาดแอปพลิเคชันของเราไปยังเซิร์ฟเวอร์หลายเครื่อง Nginx จะดูแลการจัดสรรภาระงานด้วยเช่นกัน
  4. การปรับใช้แอพภายในคอนเทนเนอร์ Docker บนเซิร์ฟเวอร์ Linux เฉพาะ การปรับใช้คอนเทนเนอร์เป็นส่วนสำคัญของการออกแบบซอฟต์แวร์มาเป็นเวลานานแล้ว แอปพลิเคชันของเราไม่แตกต่างกันและจะถูกบรรจุอย่างเรียบร้อยในคอนเทนเนอร์ของตัวเอง (ที่จริงแล้วมีหลายคอนเทนเนอร์)
  5. การกำหนดค่าและปรับใช้ฐานข้อมูล PostgreSQL สำหรับแอปพลิเคชัน โครงสร้างฐานข้อมูลและการย้ายข้อมูลจะได้รับการจัดการโดย Alembic โดยมี SQLAlchemy ให้การทำแผนที่เชิงวัตถุ
  6. การตั้งค่าคิวงานคื่นฉ่ายเพื่อจัดการงานที่ใช้เวลานาน ในที่สุดทุกแอปพลิเคชันจะต้องการสิ่งนี้เพื่อลดเวลาหรือการประมวลผลที่เข้มข้น ไม่ว่าจะเป็นการส่งจดหมาย การดูแลฐานข้อมูลอัตโนมัติ หรือการประมวลผลภาพที่อัปโหลด จากเธรดของเว็บเซิร์ฟเวอร์กับผู้ปฏิบัติงานภายนอก

การสร้างแอพ Flask

เริ่มต้นด้วยการสร้างรหัสแอปพลิเคชันและทรัพย์สิน โปรดทราบว่าฉันจะไม่กล่าวถึงโครงสร้างแอปพลิเคชัน Flask ที่เหมาะสมในโพสต์นี้ แอปสาธิตประกอบด้วยโมดูลและแพ็คเกจจำนวนน้อยที่สุดเพื่อความกระชับและชัดเจน

ขั้นแรก สร้างโครงสร้างไดเร็กทอรีและเริ่มต้นที่เก็บ Git ที่ว่างเปล่า

 mkdir flask-deploy cd flask-deploy # init GIT repo git init # create folder structure mkdir static tasks models config # install required packages with pipenv, this will create a Pipfile pipenv install flask flask-restful flask-sqlalchemy flask-migrate celery # create test static asset echo "Hello World!" > static/hello-world.txt

ต่อไปเราจะเพิ่มรหัส

config/__init__.py

ในโมดูลการกำหนดค่า เราจะกำหนดกรอบงานการจัดการการกำหนดค่าขนาดเล็กของเรา แนวคิดคือการทำให้แอปทำงานตามการกำหนดค่าที่ตั้งไว้ล่วงหน้าที่เลือกโดยตัวแปรสภาพแวดล้อม APP_ENV และเพิ่มตัวเลือกเพื่อแทนที่การตั้งค่าการกำหนดค่าด้วยตัวแปรสภาพแวดล้อมเฉพาะ หากจำเป็น

 import os import sys import config.settings # create settings object corresponding to specified env APP_ENV = os.environ.get('APP_ENV', 'Dev') _current = getattr(sys.modules['config.settings'], '{0}Config'.format(APP_ENV))() # copy attributes to the module for convenience for atr in [f for f in dir(_current) if not '__' in f]: # environment can override anything val = os.environ.get(atr, getattr(_current, atr)) setattr(sys.modules[__name__], atr, val) def as_dict(): res = {} for atr in [f for f in dir(config) if not '__' in f]: val = getattr(config, atr) res[atr] = val return res

config/settings.py

นี่คือชุดของคลาสคอนฟิกูเรชัน ซึ่งหนึ่งในนั้นถูกเลือกโดยตัวแปร APP_ENV เมื่อแอปพลิเคชันทำงาน โค้ดใน __init__.py จะสร้างอินสแตนซ์หนึ่งในคลาสเหล่านี้เพื่อแทนที่ค่าฟิลด์ด้วยตัวแปรสภาพแวดล้อมเฉพาะ หากมีอยู่ เราจะใช้ออบเจ็กต์การกำหนดค่าขั้นสุดท้ายเมื่อเริ่มต้นการกำหนดค่า Flask และ Celery ในภายหลัง

 class BaseConfig(): API_PREFIX = '/api' TESTING = False DEBUG = False class DevConfig(BaseConfig): FLASK_ENV = 'development' DEBUG = True SQLALCHEMY_DATABASE_URI = 'postgresql://db_user:db_password@db-postgres:5432/flask-deploy' CELERY_BROKER = 'pyamqp://rabbit_user:rabbit_password@broker-rabbitmq//' CELERY_RESULT_BACKEND = 'rpc://rabbit_user:rabbit_password@broker-rabbitmq//' class ProductionConfig(BaseConfig): FLASK_ENV = 'production' SQLALCHEMY_DATABASE_URI = 'postgresql://db_user:db_password@db-postgres:5432/flask-deploy' CELERY_BROKER = 'pyamqp://rabbit_user:rabbit_password@broker-rabbitmq//' CELERY_RESULT_BACKEND = 'rpc://rabbit_user:rabbit_password@broker-rabbitmq//' class TestConfig(BaseConfig): FLASK_ENV = 'development' TESTING = True DEBUG = True # make celery execute tasks synchronously in the same process CELERY_ALWAYS_EAGER = True

งาน/__init__.py

แพ็คเกจงานประกอบด้วยรหัสเริ่มต้นของ Celery แพ็คเกจ Config ซึ่งจะมีการคัดลอกการตั้งค่าทั้งหมดในระดับโมดูลเมื่อเริ่มต้น ใช้เพื่ออัปเดตออบเจ็กต์การกำหนดค่า Celery ในกรณีที่เราจะมีการตั้งค่าเฉพาะของ Celery ในอนาคต เช่น งานตามกำหนดการและการหมดเวลาของผู้ปฏิบัติงาน

 from celery import Celery import config def make_celery(): celery = Celery(__name__, broker=config.CELERY_BROKER) celery.conf.update(config.as_dict()) return celery celery = make_celery()

งาน/celery_worker.py

โมดูลนี้จำเป็นสำหรับการเริ่มต้นและเริ่มต้นพนักงานของ Celery ซึ่งจะทำงานในคอนเทนเนอร์ Docker ที่แยกต่างหาก เริ่มต้นบริบทแอปพลิเคชัน Flask เพื่อให้สามารถเข้าถึงสภาพแวดล้อมเดียวกันกับแอปพลิเคชันได้ หากไม่จำเป็น บรรทัดเหล่านี้สามารถลบออกได้อย่างปลอดภัย

 from app import create_app app = create_app() app.app_context().push() from tasks import celery

api/__init__.py

ถัดไปไปที่แพ็คเกจ API ซึ่งกำหนด REST API โดยใช้แพ็คเกจ Flask-Restful แอปของเราเป็นเพียงตัวอย่างและจะมีปลายทางเพียงสองจุดเท่านั้น:

  • /process_data – เริ่มการดำเนินการจำลองแบบยาวบนพนักงานของ Celery และส่งคืน ID ของงานใหม่
  • /tasks/<task_id> – ส่งกลับสถานะของงานตาม ID งาน
 import time from flask import jsonify from flask_restful import Api, Resource from tasks import celery import config api = Api(prefix=config.API_PREFIX) class TaskStatusAPI(Resource): def get(self, task_id): task = celery.AsyncResult(task_id) return jsonify(task.result) class DataProcessingAPI(Resource): def post(self): task = process_data.delay() return {'task_id': task.id}, 200 @celery.task() def process_data(): time.sleep(60) # data processing endpoint api.add_resource(DataProcessingAPI, '/process_data') # task status endpoint api.add_resource(TaskStatusAPI, '/tasks/<string:task_id>')

รุ่น/__init__.py

ตอนนี้ เราจะเพิ่มโมเดล SQLAlchemy สำหรับวัตถุ User และรหัสการเริ่มต้นโปรแกรมฐานข้อมูล ออบเจ็กต์ User จะไม่ถูกใช้โดยแอปสาธิตของเราในทางที่มีความหมาย แต่เราจำเป็นต้องใช้มันเพื่อให้แน่ใจว่าการย้ายฐานข้อมูลทำงานและการรวม SQLAlchemy-Flask ได้รับการตั้งค่าอย่างถูกต้อง

 import uuid from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class User(db.Model): id = db.Column(db.String(), primary_key=True, default=lambda: str(uuid.uuid4())) username = db.Column(db.String()) email = db.Column(db.String(), unique=True)

สังเกตว่า UUID ถูกสร้างขึ้นโดยอัตโนมัติเป็น ID อ็อบเจ็กต์โดยนิพจน์เริ่มต้นอย่างไร

app.py

สุดท้าย มาสร้างไฟล์แอปพลิเคชัน Flask หลักกัน

 from flask import Flask logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s]: {} %(levelname)s %(message)s'.format(os.getpid()), datefmt='%Y-%m-%d %H:%M:%S', handlers=[logging.StreamHandler()]) logger = logging.getLogger() def create_app(): logger.info(f'Starting app in {config.APP_ENV} environment') app = Flask(__name__) app.config.from_object('config') api.init_app(app) # initialize SQLAlchemy db.init_app(app) # define hello world page @app.route('/') def hello_world(): return 'Hello, World!' return app if __name__ == "__main__": app = create_app() app.run(host='0.0.0.0', debug=True)</td> </tr> <tr> <td>

เราคือ:

  • การกำหนดค่าการบันทึกพื้นฐานในรูปแบบที่เหมาะสมกับเวลา ระดับ และ ID กระบวนการ
  • กำหนดฟังก์ชันการสร้างแอป Flask ด้วยการเริ่มต้น API และ "สวัสดี ชาวโลก!" หน้าหนังสือ
  • การกำหนดจุดเริ่มต้นเพื่อเรียกใช้แอพในช่วงเวลาการพัฒนา

wsgi.py

นอกจากนี้ เราจำเป็นต้องมีโมดูลแยกต่างหากเพื่อเรียกใช้แอปพลิเคชัน Flask ด้วย Gunicorn มันจะมีเพียงสองบรรทัด:

 from app import create_app app = create_app()

รหัสแอปพลิเคชันพร้อมแล้ว ขั้นตอนต่อไปของเราคือการสร้างการกำหนดค่า Docker

การสร้างคอนเทนเนอร์ Docker

แอปพลิเคชันของเราจะต้องใช้คอนเทนเนอร์ Docker หลายตัวในการรัน:

  1. คอนเทนเนอร์แอปพลิเคชันสำหรับแสดงหน้าเทมเพลตและเปิดเผยจุดปลาย API เป็นความคิดที่ดีที่จะแบ่งฟังก์ชันทั้งสองนี้ออกจากการผลิต แต่เราไม่มีหน้าเทมเพลตในแอปสาธิตของเรา คอนเทนเนอร์จะเรียกใช้เว็บเซิร์ฟเวอร์ Gunicorn ซึ่งจะสื่อสารกับ Flask ผ่านโปรโตคอล WSGI
  2. ภาชนะคนงานคื่นฉ่ายเพื่อดำเนินงานที่ยาวนาน นี่เป็นคอนเทนเนอร์แอปพลิเคชันเดียวกัน แต่มีคำสั่งเรียกใช้แบบกำหนดเองเพื่อเปิดใช้ Celery แทนที่จะเป็น Gunicorn
  3. คื่นฉ่ายเอาชนะคอนเทนเนอร์—คล้ายกับด้านบน แต่สำหรับงานที่เรียกใช้ตามกำหนดเวลาปกติ เช่น การลบบัญชีของผู้ใช้ที่ไม่เคยยืนยันอีเมลของตน
  4. คอนเทนเนอร์ RabbitMQ คื่นฉ่ายต้องการนายหน้าข้อความเพื่อสื่อสารระหว่างพนักงานกับแอพ และเก็บผลลัพธ์ของงาน RabbitMQ เป็นตัวเลือกทั่วไป แต่คุณสามารถใช้ Redis หรือ Kafka ได้เช่นกัน
  5. คอนเทนเนอร์ฐานข้อมูลด้วย PostgreSQL

วิธีธรรมชาติในการจัดการหลายคอนเทนเนอร์อย่างง่ายดายคือการใช้ Docker Compose แต่ก่อนอื่น เราจะต้องสร้าง Dockerfile เพื่อสร้างอิมเมจคอนเทนเนอร์สำหรับแอปพลิเคชันของเรา มาใส่ไว้ในไดเร็กทอรีโครงการกัน

 FROM python:3.7.2 RUN pip install pipenv ADD . /flask-deploy WORKDIR /flask-deploy RUN pipenv install --system --skip-lock RUN pip install gunicorn[gevent] EXPOSE 5000 CMD gunicorn --worker-class gevent --workers 8 --bind 0.0.0.0:5000 wsgi:app --max-requests 10000 --timeout 5 --keep-alive 5 --log-level info

ไฟล์นี้สั่งให้ Docker ทำสิ่งต่อไปนี้

  • ติดตั้งการพึ่งพาทั้งหมดโดยใช้ Pipenv
  • เพิ่มโฟลเดอร์แอปพลิเคชันลงในคอนเทนเนอร์
  • เปิดเผยพอร์ต TCP 5000 ไปยังโฮสต์
  • ตั้งค่าคำสั่งเริ่มต้นเริ่มต้นของคอนเทนเนอร์เป็น Gunicorn call

มาพูดคุยกันเพิ่มเติมว่าเกิดอะไรขึ้นในบรรทัดสุดท้าย มันรัน Gunicorn ระบุคลาสคนงานเป็น gevent Gevent เป็น lib การทำงานพร้อมกันที่มีน้ำหนักเบาสำหรับการทำงานหลายอย่างพร้อมกัน มันให้ประสิทธิภาพที่เพิ่มขึ้นอย่างมากในการโหลดที่ผูกไว้กับ I/O ให้การใช้งาน CPU ที่ดีกว่าเมื่อเปรียบเทียบกับการทำงานมัลติทาสกิ้งของ OS สำหรับเธรด พารามิเตอร์ --workers คือจำนวนกระบวนการของผู้ปฏิบัติงาน เป็นความคิดที่ดีที่จะตั้งค่าให้เท่ากับจำนวนคอร์บนเซิร์ฟเวอร์

เมื่อเรามี Dockerfile สำหรับคอนเทนเนอร์แอปพลิเคชันแล้ว เราสามารถสร้างไฟล์ docker-compose.yml -compose.yml ซึ่งจะกำหนดคอนเทนเนอร์ทั้งหมดที่แอปพลิเคชันจะต้องเรียกใช้

 version: '3' services: broker-rabbitmq: image: "rabbitmq:3.7.14-management" environment: - RABBITMQ_DEFAULT_USER=rabbit_user - RABBITMQ_DEFAULT_PASS=rabbit_password db-postgres: image: "postgres:11.2" environment: - POSTGRES_USER=db_user - POSTGRES_PASSWORD=db_password migration: build: . environment: - APP_ENV=${APP_ENV} command: flask db upgrade depends_on: - db-postgres api: build: . ports: - "5000:5000" environment: - APP_ENV=${APP_ENV} depends_on: - broker-rabbitmq - db-postgres - migration api-worker: build: . command: celery worker --workdir=. -A tasks.celery --loglevel=info environment: - APP_ENV=${APP_ENV} depends_on: - broker-rabbitmq - db-postgres - migration api-beat: build: . command: celery beat -A tasks.celery --loglevel=info environment: - APP_ENV=${APP_ENV} depends_on: - broker-rabbitmq - db-postgres - migration

เรากำหนดบริการดังต่อไปนี้:

  • broker-rabbitmq – คอนเทนเนอร์นายหน้าข้อความ RabbitMQ ข้อมูลรับรองการเชื่อมต่อถูกกำหนดโดยตัวแปรสภาพแวดล้อม
  • db-postgres – คอนเทนเนอร์ PostgreSQL และข้อมูลประจำตัว
  • การ migration – คอนเทนเนอร์ของแอปซึ่งจะทำการย้ายฐานข้อมูลด้วย Flask-Migrate และออก คอนเทนเนอร์ API ขึ้นอยู่กับคอนเทนเนอร์และจะทำงานในภายหลัง
  • api – คอนเทนเนอร์แอปพลิเคชันหลัก
  • api-worker และ api-beat – คอนเทนเนอร์ที่รันคนทำงาน Celery สำหรับงานที่ได้รับจาก API และงานที่กำหนดเวลาไว้

แต่ละคอนเทนเนอร์ของแอปจะได้รับตัวแปร APP_ENV จากคำสั่ง docker docker-compose up

เมื่อเรามีทรัพย์สินของแอปพลิเคชันทั้งหมดพร้อมแล้ว มาวางบน GitHub ซึ่งจะช่วยให้เราปรับใช้โค้ดบนเซิร์ฟเวอร์ได้

 git add * git commit -a -m 'Initial commit' git remote add origin [email protected]:your-name/flask-deploy.git git push -u origin master

การกำหนดค่าเซิร์ฟเวอร์

ตอนนี้โค้ดของเราอยู่บน GitHub แล้ว ที่เหลือก็แค่กำหนดค่าเซิร์ฟเวอร์เริ่มต้นและปรับใช้แอปพลิเคชัน ในกรณีของฉัน เซิร์ฟเวอร์เป็นอินสแตนซ์ AWS ที่ใช้งาน AMI Linux สำหรับลินุกซ์รุ่นอื่นๆ คำแนะนำอาจแตกต่างกันเล็กน้อย ฉันยังถือว่าเซิร์ฟเวอร์มีที่อยู่ IP ภายนอกอยู่แล้ว DNS ได้รับการกำหนดค่าด้วยระเบียน A ที่ชี้ไปที่ IP นี้ และออกใบรับรอง SSL สำหรับโดเมน

เคล็ดลับด้านความปลอดภัย: อย่าลืมอนุญาตพอร์ต 80 และ 443 สำหรับการรับส่งข้อมูล HTTP(S) พอร์ต 22 สำหรับ SSH ในคอนโซลโฮสต์ของคุณ (หรือใช้ iptables ) และปิดการเข้าถึงภายนอกไปยังพอร์ตอื่นๆ ทั้งหมด! อย่าลืมทำเช่นเดียวกันกับโปรโตคอล IPv6 !

การติดตั้งการพึ่งพา

อันดับแรก เราต้องใช้ Nginx และ Docker ที่ทำงานบนเซิร์ฟเวอร์ บวกกับ Git เพื่อดึงโค้ด เข้าสู่ระบบผ่าน SSH และใช้ตัวจัดการแพ็คเกจเพื่อติดตั้ง

 sudo yum install -y docker docker-compose nginx git

การกำหนดค่า Nginx

ขั้นตอนต่อไปคือการกำหนดค่า Nginx ไฟล์คอนฟิกูเรชันหลักของ nginx.conf มักจะดีตามที่เป็นอยู่ ยังคงต้องแน่ใจว่าได้ตรวจสอบว่าเหมาะสมกับความต้องการของคุณหรือไม่ สำหรับแอปของเรา เราจะสร้างไฟล์การกำหนดค่าใหม่ในโฟลเดอร์ conf.d การกำหนดค่าระดับบนสุดมีคำสั่งให้รวมไฟล์ . .conf ทั้งหมดจากนั้น

 cd /etc/nginx/conf.d sudo vim flask-deploy.conf

นี่คือไฟล์การกำหนดค่าไซต์ Flask สำหรับ Nginx รวมแบตเตอรี่ มีคุณสมบัติดังต่อไปนี้:

  1. มีการกำหนดค่า SSL คุณควรมีใบรับรองที่ถูกต้องสำหรับโดเมนของคุณ เช่น ใบรับรอง Let's Encrypt ฟรี
  2. คำขอ www.your-site.com จะถูกเปลี่ยนเส้นทางไปยัง your-site.com
  3. คำขอ HTTP ถูกเปลี่ยนเส้นทางไปยังพอร์ต HTTPS ที่ปลอดภัย
  4. พร็อกซีย้อนกลับได้รับการกำหนดค่าให้ส่งคำขอไปยังพอร์ตในเครื่อง 5000
  5. Nginx ให้บริการไฟล์แบบคงที่จากโฟลเดอร์ในเครื่อง
 server { listen 80; listen 443; server_name www.your-site.com; # check your certificate path! ssl_certificate /etc/nginx/ssl/your-site.com/fullchain.crt; ssl_certificate_key /etc/nginx/ssl/your-site.com/server.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; # redirect to non-www domain return 301 https://your-site.com$request_uri; } # HTTP to HTTPS redirection server { listen 80; server_name your-site.com; return 301 https://your-site.com$request_uri; } server { listen 443 ssl; # check your certificate path! ssl_certificate /etc/nginx/ssl/your-site.com/fullchain.crt; ssl_certificate_key /etc/nginx/ssl/your-site.com/server.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; # affects the size of files user can upload with HTTP POST client_max_body_size 10M; server_name your-site.com; location / { include /etc/nginx/mime.types; root /home/ec2-user/flask-deploy/static; # if static file not found - pass request to Flask try_files $uri @flask; } location @flask { add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; proxy_read_timeout 10; proxy_send_timeout 10; send_timeout 60; resolver_timeout 120; client_body_timeout 120; # set headers to pass request info to Flask proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; proxy_pass http://127.0.0.1:5000$uri; } }

หลังจากแก้ไขไฟล์แล้ว ให้รัน sudo nginx -s reload และดูว่ามีข้อผิดพลาดหรือไม่

การตั้งค่าข้อมูลรับรอง GitHub

แนวทางปฏิบัติที่ดีที่จะมีบัญชี VCS "การปรับใช้" แยกต่างหากสำหรับการปรับใช้โครงการและระบบ CI/CD วิธีนี้คุณจะไม่เสี่ยงด้วยการเปิดเผยข้อมูลประจำตัวของบัญชีของคุณเอง เพื่อป้องกันที่เก็บโปรเจ็กต์เพิ่มเติม คุณยังสามารถจำกัดสิทธิ์ของบัญชีดังกล่าวให้เข้าถึงแบบอ่านอย่างเดียวได้ สำหรับที่เก็บ GitHub คุณจะต้องมีบัญชีองค์กรเพื่อดำเนินการดังกล่าว ในการปรับใช้แอปพลิเคชันสาธิตของเรา เราจะสร้างคีย์สาธารณะบนเซิร์ฟเวอร์และลงทะเบียนบน GitHub เพื่อเข้าถึงโครงการของเราโดยไม่ต้องป้อนข้อมูลประจำตัวทุกครั้ง

ในการสร้างคีย์ SSH ใหม่ ให้เรียกใช้:

 cd ~/.ssh ssh-keygen -b 2048 -t rsa -f id_rsa.pub -q -N "" -C "deploy"

จากนั้นลงชื่อเข้าใช้ GitHub และเพิ่มคีย์สาธารณะของคุณจาก ~/.ssh/id_rsa.pub ในการตั้งค่าบัญชี

การปรับใช้แอพ

ขั้นตอนสุดท้ายค่อนข้างตรงไปตรงมา—เราต้องรับรหัสแอปพลิเคชันจาก GitHub และเริ่มต้นคอนเทนเนอร์ทั้งหมดด้วย Docker Compose

 cd ~ git clone https://github.com/your-name/flask-deploy.git git checkout master APP_ENV=Production docker-compose up -d

อาจเป็นความคิดที่ดีที่จะละเว้น -d (ซึ่งเริ่มคอนเทนเนอร์ในโหมดแยกส่วน) สำหรับการรันครั้งแรกเพื่อดูผลลัพธ์ของแต่ละคอนเทนเนอร์ในเทอร์มินัลและตรวจสอบปัญหาที่อาจเกิดขึ้น อีกทางเลือกหนึ่งคือการตรวจสอบแต่ละคอนเทนเนอร์ด้วย docker logs ในภายหลัง ลองดูว่าคอนเทนเนอร์ทั้งหมดของเราทำงานด้วย docker ps.

image_alt_text

ยอดเยี่ยม. ตู้คอนเทนเนอร์ทั้ง 5 ตู้กำลังทำงานอยู่ Docker Compose ชื่อคอนเทนเนอร์ที่กำหนดโดยอัตโนมัติตามบริการที่ระบุใน docker-compose.yml ในที่สุดก็ถึงเวลาทดสอบว่าการกำหนดค่าทั้งหมดทำงานอย่างไร! ทางที่ดีควรทำการทดสอบจากเครื่องภายนอกเพื่อให้แน่ใจว่าเซิร์ฟเวอร์มีการตั้งค่าเครือข่ายที่ถูกต้อง

 # test HTTP protocol, you should get a 301 response curl your-site.com # HTTPS request should return our Hello World message curl https://your-site.com # and nginx should correctly send test static file: curl https://your-site.com/hello-world.txt

แค่นั้นแหละ. เรามีการกำหนดค่าแอปของเราที่ทำงานบนอินสแตนซ์ AWS แบบเรียบง่ายแต่พร้อมสำหรับการผลิตอย่างสมบูรณ์ หวังว่ามันจะช่วยให้คุณเริ่มสร้างแอปพลิเคชันในชีวิตจริงได้อย่างรวดเร็วและหลีกเลี่ยงข้อผิดพลาดทั่วไปบางอย่าง! รหัสที่สมบูรณ์มีอยู่ในที่เก็บ GitHub

บทสรุป

ในบทความนี้ เราได้กล่าวถึงแนวทางปฏิบัติที่ดีที่สุดบางประการในการจัดโครงสร้าง การกำหนดค่า การบรรจุหีบห่อ และการปรับใช้แอปพลิเคชัน Flask กับการผลิต นี่เป็นหัวข้อที่ใหญ่มาก เป็นไปไม่ได้ที่จะกล่าวถึงในบล็อกโพสต์เดียว นี่คือรายการคำถามสำคัญที่เราไม่ได้กล่าวถึง:

บทความนี้ไม่ครอบคลุมถึง:

  • การบูรณาการอย่างต่อเนื่องและการปรับใช้อย่างต่อเนื่อง
  • การทดสอบอัตโนมัติ
  • บันทึกการจัดส่ง
  • การตรวจสอบ API
  • การขยายแอปพลิเคชันไปยังเซิร์ฟเวอร์หลายเครื่อง
  • การปกป้องข้อมูลประจำตัวในซอร์สโค้ด

อย่างไรก็ตาม คุณสามารถเรียนรู้วิธีทำโดยใช้แหล่งข้อมูลดีๆ อื่นๆ ในบล็อกนี้ ตัวอย่างเช่น หากต้องการสำรวจการบันทึก โปรดดูที่ Python Logging: An In-Depth Tutorial หรือสำหรับภาพรวมทั่วไปเกี่ยวกับ CI/CD และการทดสอบอัตโนมัติ โปรดดูวิธีสร้างไปป์ไลน์การปรับใช้เบื้องต้นที่มีประสิทธิภาพ ฉันปล่อยให้การดำเนินการเหล่านี้เป็นแบบฝึกหัดสำหรับคุณผู้อ่าน

ขอบคุณที่อ่าน!