במאמרים הקודמים הראיתי איך יוצרים קונטיינטר מהמאגרים המוכנים שיש באתר של דוקר שמכיל אלפי קבצי הגדרות שונים. למשל, כשרציתי להריץ סביבת PHP, הרצתי משהו בסגנון הזה:
docker run -d -p 80:80 --name my-apache-php-app -v /c/temp:/var/www/html php:7.0-apache
המחרוזת php:7.0-apache היא לא משהו שהמצאתי אלא משהו שמגיע מהפצה רשמית של PHP שנמצאת בדוקר. ההפצה הרשמית היא PHP וה'ענף' הוא 7.0-apache. מה שיש מאחורי ההפצה הזו זו קובץ הגדרות שקובע שעל הקונטיינר להתקין apache למשל, ו-PHP מגרסה 7 ועוד כל מיני דברים. כאמור, אני יכול להסתמך על קובץ הגדרות רשמי ומוכר וזה טוב בדרך כלל לתסריטים פשוטים או לחלופין אני יכול ליצור קובץ הגדרות מיוחד שמכיל את ההגדרות שלי. קובץ ההגדרות הזה נקרא image. מה-image הזה אנחנו יוצרים קונטיינרים.
למה בדיוק אני ארצה ליצור image משלי? יש מיליון תסריטים – למשל אפליקציה שיש בה גם PHP וגם Python למשל מגרסאות ספציפיות, או מודולים ספציפיים של PHP. אם אני מתחזק אתרים, סביבת הפיתוח צריכה להיות תואמת לסביבת הפרודקשן. לפעמים אני רוצה להרים קונטיינר לצורך מאוד ספציפי שיעשה עבודה אחת מאוד פשוטה. הדוגמה הכי טובה ומעשית שאני יכול לציין היא הרצת static code analysis על קבצי PHP. לא מעט פעמים אני צריך לבדוק תוספים מסוימים שנכתבו ב-PHP ולראות שהם תואמים לסטנדרט והמפתח שפיתח אותם יודע מה הוא עושה. לצורך זה אני צריך להפעיל את PHP code sniffer שההתקנה שלו על מחשב מקומי יכולה להיות מאתגרת. למה לשבור את הראש כשאני יכול להרים קונטיינר מיידי ולהריץ את בדיקת הקוד ממנו?
אז מה המטרה? ליצור image שמייד יצור לי קונטיינר על מנת שאוכל לבדוק כל תיקיה שיש לה קבצי PHP ולהדפיס את השגיאות של ה-PHP שיש שם. כך למשל, אם לקוח שולח לי קבצי PHP כדי לבדוק את האיכות שלהם, אני יכול לבצע את הבדיקה מיידית בלי לוודא שיש לי PHPCS או הגדרות שונות ומשונות. אם זה נשמע מסובך, אז בגדול – המטרה שלי היא שורה שאני מקליד בקונסולה של החלונות ותיתן לי מייידית חיווי אם הקוד תקין או לא.
איך עושים את זה? קל להבין שאני לא יכול להשתמש במשהו רשמי כמו PHP. כלומר זה יהיה הבסיס שלי מן הסתם כי אני צריך קונטיינר שמריץ PHP. אבל אני צריך עוד דברים ובראשם PHPCS, הכלי שבו אני משתמש. הוא נשען על PHP אבל אין לו זכר במאגרים הרשמיים. בשביל להכניס אותו לקונטיינר, אני יכול או להתקין אותו ידנית בכל פעם שאני יוצר קונטיינר מקובץ 'רשמי' (רעיון רע) או להכין קובץ הגדרות שפשוט בא ואומר: "קח את הקונטיינר הרשמי במטותא ממך, התקן עליו PHPCS ואולי עוד משהו וזהו".
על מנת לעשות את זה, אני אצור קובץ שנקרא Dockerfile (עם D גדולה) ואציב אותו בפרויקט שלי. הקובץ הזה הוא סוג של 'תבנית' שבתוכו אני יכול להכניס הוראות שונות שיעבדו ממש כאילו אני בתוך המכונה. הנה הדוקר שלי:
FROM php:7-alpine RUN cd /usr/local/bin \ && curl -sL http://cs.sensiolabs.org/download/php-cs-fixer-v2.phar -o php-cs-fixer \ && chmod +x php-cs-fixer \ && curl -sL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -o phpcs \ && chmod +x phpcs WORKDIR /app
FROM
בדרך כלל מתחילים ב-FROM שמציינת את ה-Image הרשמי שאני נבנה ממנו. כיוון שבסופו של דבר אני נשען על image רשמי של PHP זה יהיה רעיון טוב לציין אותו וזה מה שאנו עושים. מחרוזת הטקסט זהה לחלוטין לזו של האתר הרשמית.
RUN
זו פקודה שמשמעותה היא הרצה. כל מה שבא אחריה ירוץ. שימו לב שאני משתמש פה בסינטקס של שרשור &&. בלינוקס שרשור זה אומר לבצע את הפקודה שאחרי ה-&& מיד אחרי שקודמתה מסתיימת. אבל זה מקביל ל:
RUN cd /usr/local/bin \ RUN curl -sL http://cs.sensiolabs.org/download/php-cs-fixer-v2.phar -o php-cs-fixer \ RUN chmod +x php-cs-fixer \ RUN curl -sL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -o phpcs \ RUN chmod +x phpcs
מקובל בדרך כלל לקשר כל RUN לשורת פקודות שרלוונטיות לנושא. למשל RUN אחד להתקנת ה-PHPCS ו-RUN אחר להתקנת רכיב נוסף כשבכל RUN הפקודות הרלוונטיות לו יהיו משורשרות באמצעות &&. אם זה נשמע לכם כמו סינית אז זה בסדר. רק תזכרו ש-&& זה שרשור (כמו לכתוב, אחרי שההוא מסתיים, תריץ את זה).
WORKDIR
הגדרה של סביבת העבודה. במקרה הזה אני מכוון את הקונטיינר, מייד אחרי שה-phpcs מסתיים להתקנה, להגיע אל תיקית app/
אז בעצם שלוש הפקודות האלו הן פשוטות – FROM אומרת לנו – אתה מתבסס על הגרסה הרשמית של PHP שנקראת php:7-alpine (אותה לקחתי כמובן מהגרסה הרשמית של PHP שיש בדוקר, אבל אני יכול לקחת כל גרסה). פקודת RUN אומרת לנו להריץ שורה של פקודות שמתקינות את PHPCS שאותה לקחתי מהגרסה הרשמית שלהם ולבסוף פקודת WORKDIR מחזירה את הקונטקסט של הקונטיינר לתיקית app. למה? עוד מעט תבינו.
נשמור את הקובץ בספריה כלשהי ונמשיך.
אחרי שיצרתי את הקובץ, אני צריך 'לבנות' אותו וזה השינוי הגדול ביותר שיש בין שימוש ב-image רשמי ל-Docker file. הבנייה בעצם עוקבת אחר כל ההוראות שיש בקובץ ומריצה אותו. אם הכל מצליח אנחנו יכולים ליצור קונטיינר מה-Image הזה בדיוק כמו כל image רשמי.
הבניה נעשית כך:
docker build -t ran-lint-machine .
הפקודה הראשונה היא docker build שאומרת שאנחנו בונים. הפלאג t- ומה שבא אחריו זה השם. שימו לב שזה חשוב אחרת תצטרכו להשתמש ב-hash של ה-image וזה לא נחמד. הנקודה שיש בסוף היא הנתיב לקובץ ה-docker שאותו ממש הכנו ושמרנו בשם Dockerfile. במקרה הזה אני מריץ את הפקודה ממש מאיפה שהקובץ נמצא.
עכשיו אנחנו נראה ממש בקונסולה את כל השלבים של ההתקנות. כן! אם יש תקלה כלשהי, נראה אותה וכך נוכל לתקן את הקובץ. אם הכל תקין, נראה את ה-output הבא:
Successfully built c77653df3672 Successfully tagged ran-lint-machine:latest
עכשיו יש לי image שאני יכול להריץ. שמו בישראל יהיה ran-lint-machine. אני יכול לבדוק אותו באמצעות הקלדה של:
docker images --all
אני אקבל רשימה של כל ה-images שהתקנתי אי פעם, כל הרשמיים וביניהם אחד קטן וחביב שיצרתי עכשיו ששמו הוא ran-lint-machine.
ועכשיו? כשיש לי את שם ה-image אני יכול להשתמש בו. באופן הזה:
docker run --rm -it -v /c/Users/barzik/test-docker:/app/ ran-lint-machine phpcs PATH/TO/PHP/FILES/ docker run -it -v PATH/TO/PHP/FILES/:/app/ --rm ran-lint-machine phpcs .
את docker run -it אנחנו מכירים כבר מהמאמר הקודם. זוכרים? זו הרצה שלו.
החלק הכי חשוב הוא v-. במאמר הפתיחה על docker כתבתי שהוא עוזר לנו לקשר בין המערכת שלנו לקונטיינר. במקרה הזה הוא קובע מה תיקית app תהיה! אני אקשר בין התיקיה שבה יש את קבצי ה-PHP ל-app שהוא, כפי שאני קבעתי, תיקית העבודה המרכזית.
פלאג rm– אומר למחוק את הקונטיינר מייד אחרי ההרצה. כיוון שמדובר בהרצה חד פעמית שלו.
שם ה-image שאותו קבעתי הוא כמובן ran-lint-machine והוא נקבע בתהליך ה-build.
ולבסוף, השורה שמריצה את phpcs ונקודה. כי אני מריץ את phpcs בתיקיה של apps שמקושרת לתיקיה שמכילה את קבצי ה-PHP.
ואם הכל יעבוד כמו שצריך, זה מה שאני אראה:
זה הבסיס המאוד בסיסי, אבל תראו כמה זה קל! במאמר הבא נעמיק מעט לתוך קובץ ה-Dockerfile.