אני רושם את ה-demo הזה כהמשך ל-demo הקודם. היום אני רוצה שניצור שתי container-ים. אחד מהם יהיה container של nginx ואחד מהם יהיה container של אפליקציה קטנה שנכתוב.
ראשית, אסביר על כלי חדש:
Docker compose
זהו כלי שבעזרתו ניתן להריץ אפליקציות מרובות container-ים ולנהל אותם בצורה נוחה.
למה אנחנו בכלל צריכים את זה?
בעולם האמיתי, מערכות מורכבות מהרבה מאוד דברים כגון: db, web server, services. חשוב להבטיח שהמערכות תמיד יהיו למעלה ושכל מפתח יוכל לעבוד על הקוד שלו בנפרד בצורה נוחה. לכן, נרצה להפריד כל חלק באפליקציה שלנו ל-container נפרד ולעיתים גם ליצור לחלק הזה כמה מופעים זהים (container-ים). זוהי תצורה של microservices שניגע בה בפירוט בעתיד. בעזרת docker-compose נוכל לנהל ולהריץ כמה container-ים במקביל בקלות וביעילות.
איך מתקינים docker compose?
ב-Windows זה מגיע built in עם ההתקנה של docker desktop. בלינוקס צריך להריץ פקודה על מנת להתקין.
מה יהיה ה-flow שלנו ב-demo הנוכחי?
- בניית docker image עבור האפליקציה שלנו.
- הגדרת ה-docker images שמהם ניצור ונריץ container-ים (our app, nginx)
- הגדרת קונפיגורציות ומשתני סביבה ב-compose file שלנו.
- הרצת ה-containerים באמצעות docker compose
את המיני האפליקציה שלנו נכתוב ב-python עם flask (חבילה פייתונית לפיתוח web).
docker demo
ראשית, נפתח תיקייה. ניכנס אליה וניצור קובץ בשם requirements.txt ובתוכו נרשום flask (בפייתון מקובל שרושמים את התלויות בקובץ כזה ואחרי זה אפשר להתקין את כולם ביחד).
יש להתקין flask על המחשב שלנו. נריץ ב-shell שלנו:
pip install flask
כעת, נפתח עוד קובץ בשם app.py ובו נכתוב את הקוד של האפליקציה:
from flask import Flask # Use flask module import random # Use random module app = Flask(__name__) # For flask to know where to look for resources START_NUMBER = 0 END_NUMBER = 10 @app.route('/') # expose main page of app def home(): return "This is the main page of our app" @app.route('/rand-number') # expose random number page def random_number(): return str(random.randint(START_NUMBER, END_NUMBER)) # return number between 0 - 10 if __name__ == '__main__': app.run(host = '0.0.0.0',port=5005) # Run app in port 5005, 0.0.0.0 means everyone can access it
לאפליקציה יש שני עמודים, תיעדתי בקוד בהערה את מה שחשוב:
- עמוד ראשי שאליו מגיעים כשפונים לאפליקציה. הוא מדפיס טקסט מסוים.
- עמוד שני שניגשים אליו באמצעות /rand-number ואז מקבלים מספר רנדומלי בין 0 ל-10.
עכשיו אנחנו מוכנים לבדוק אם זה עובד. התיקייה אמורה להיראות כך:
נפתח shell בתיקייה הנוכחית ונריץ:
python app.py
אנחנו אמורים להיות מסוגלים לגשת לאפליקציה, ניגש אליה דרך הדפדפן:
- עמוד ראשי – localhost:5005
- עמוד שני – localhost:5005/rand-number
אם שניהם עובדים לכם, מעולה! אפשר להמשיך לשלב הבא.
נרצה להגדיר קונפיגורציות של nginx כדי שהוא יידע להאזין לבקשות ולהעביר אותם לאפליקציה. נקרא לקובץ nginx.conf ונעתיק אותו ל-container של nginx כדי שיחליף את הדיפולטי. נשים אותו בתיקייה פנימית בשם nginx והתוכן שלו ייראה כך:
server { listen 80; location / { proxy_pass http://host.docker.internal:5005; } }
שימו לב:בהגדרות האלו, אנו מצהירים שה-nginx יאזין על פורט 80, ויעביר את כל הבקשות שהוא מקבל בפורט הזה לכתובת http://host.docker.internal:5005. את הפורט אתם ודאי מזהים ( הפורט של האפליקציה) אבל הכתובת לפני זה משהו שכדאי מאוד לדעת. מתוך container, אם מנסים לגשת ל 127.0.01 או localhost לא נגיע לhost, נגיע ל-container עצמו כיוון שיש לו ip משלו.
במקרה שלנו, יש שתי container-ים שרצים על אותו host. אנו רוצים להעביר תעבורה מ-container של ה-nginx ל-container של האפליקציה. host.docker.internal באמת ייגש ל-host.
אנחנו צריכים להכניס את הקובץ הזה ל-docker image של nginx, לכן ניצור docker file קצר בתוך התיקייה nginx:
FROM nginx:alpine COPY ./default.conf /etc/nginx/conf.d/default.conf
כעת, נרצה ליצור docker image שיכיל את האפליקציה שלנו. הקו המנחה כשרושמים Dockerfile בעיניי הוא: לדאוג שכל המידע שנעזרנו בו יהיה גם בתוך ה-docker image. במקרה שלנו זה רק החבילה flask.
לכן ה-Dockerfile ייראה כך:
From python:3.8 WORKDIR /app COPY . /app RUN pip install -r requirements.txt CMD ["python","app.py"]
אסביר את השלבים:
- שימוש ב-python3.8 כ-base image
- הגדרת הנתיב /app כנתיב שעליו עובדים ב-image
- העתקת האפליקציה שלנו ל /app
- הרצת פקודה שתתקין את התלויות שלנו ( רק Flask)
- הרצת הפקודה python app.py שתרוץ ברגע שניצוק container
נבנה את שני ה- images כך:
docker build -t my-flask-app:latest . cd nginx docker build -t my-nginx:latest .
עכשיו נוכל לרשום את הקובץ compose שלנו (נקרא לו docker-compose.yml כי כך docker compose יידע לחפש קובץ בשם הזה):
version: '3' services: web: image: my-flask-app:latest # our app container_name: flask-app restart: always # restart when container crashes ports: - "5005:5005" # app port nginx: image: my-nginx:latest container_name: nginx restart: always ports: - "8080:80" # move trafic from 80 to app port
הקבצים בתיקייה אמורים להיראות ככה:
בתיקייה הפנימית אלו הקבצים:
זהו, הכול מוכן! נפתח shell בתיקייה הראשית ונריץ את הפקודה שתיצור לנו את ה-containerים האלו ב-background:
docker-compose up -d
כשתריצו docker ps אתם אמורים לראות משהו כזה:
תבדקו שזה עובד בעזרת כניסה ל http://localhost:8080.
כדי לעצור ולמחוק את שני ה-container-ים נרשום:
docker-compose stop docker-compose rm -f
לסיכום: ראשית, למדנו כלי חדש -docker compose ולמדנו איך משתמשים בו. התנסינו עוד עם Dockerfile וכתבנו אפליקציה קטנה ובסיסית לשם ההמחשה. בנוסף, למדנו שגישה ל-localhost מתוך container לא תגיע ל-host אלא ל-Container עצמו (אני אישית התפסטנתי על הדבר הזה בעבר כמות זמן לא קטנה).
חשוב לי גם להוסיף שב-use case שלנו, יכול להיות שהיה עדיף ליצור container אחד שה-base image שלו הוא Python ומותקן עליו nginx עם gunicorn או uwsgi ( שרתי web שיותר מתאימים לאפליקציות פייתוניות). ב-demo הזה היה חשוב לי להראות יצירת שני container-ים עם docker-compose ולכן לא עשיתי את זה בצורה הזאת. כאן תוכלו למצוא docker image שמכיל את כולם.
אני מקווה שלמדתם ולקחתם משהו לעצמכם. שיהיה סופ”ש נעים ? .
תודה, למדתי כמה דברים!