במאמר זה, נלמד מה שזה אומר לדחוף אפליקציה לתפעול ואיך לגרום לכך שזה יקרה אוטומטית, נראה איך להשתמש בדוקר ובפעולות GitHub לשם כך. מאחר ואנחנו משתמשים בדוקר, לא אשקיע הרבה זמן בלהסביר אילו טכנולוגיות שונות שומשו לכתיבת האפליקציה, אלא מה לעשות עם תמונת הדוקר עצמה. ההנחה כאן היא שיש לנו שרת פשוט, נאמר VPS (שרת פרטי וירטואלי), שרת מוקצה או כל שרת שיש לנו SSH כדי להשתמש בו. עלינו לבצע פעולות, להכין את השרת לקבל ולהריץ את האפליקציה, ולהכין את תפיסת ההפצה. בחלק הבא של המאמר, אני אנחש שיש לך שרת Ubuntu
בואו נכין את השרת
הדבר הראשון שצריך לבצע הוא תצורה חד-פעמית על השרת, המטרה היא להכין אותו לשלבים הבאים. לשם כך, עלינו לשקול את הנושאים הבאים:
דוקר קומפוז להרצת האפליקציה והתלותות שלה
Docker Compose הוא כלי שמריץ מספר אפליקציות Docker כשירותים ומאפשר להם לתקשר זה עם זה, כמו גם תכונות נוספות כמו ווליומים לאחסון קבצים. בואו קודם כל נתקין את Docker ו-Docker Compose על השרת; אתם יכולים ללכת להוראות ההתקנה הרשמיות כאן: docs.docker.com/engine/install/ubuntu. לאחר זה, חשוב להריץ אותו כמשתמש לא-מנהל; זה מה שאומרת התיעוד הרשמי: docs.docker.com/engine/security/rootless.
הגדר את אשראי AWS ECR & CLI.
שים לב שאפשר להשיג את אותו מטרה עם רשם אחר אם רוצה, אני רק משתמש ב-AWS כי זה יותר פשוט בשבילי.
מאחר שאנחנו משתמשים ב-Docker, התמונות יהיו צריכות להיות מאוחסנות ומוחזרות ממקום מסוים. לשם כך, אני משתמש ב-AWS ECR (Amazon Web Services Elastic Container Registry). זה רשם Docker בתוך חשבון AWS. זה מאוד זול לשימוש וקל להגדרה. אתה יכול גם להשתמש ב-Docker Hub ליצירת מאגר פרטי עבור התמונות שלך. הכל מתחיל ביצירת רשם ECR פרטי בחשבון AWS. אתה תלחץ על "יצור מאגר" ותמלא את שם המאגר.
לאחר יצירת מאגר, אתה יכול להעתיק URI של המאגר ולשמור אותו לימים הבאים. יש לו את הפורמט הבא AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/
שם_ה_מאגר
.
אתה גם צריך להגדיר את איברי AWS IAM שיש להם את הזכות לשלוף/להעלות למאגר זה. בוא נעבור לשרות IAM, נקליק על משתמש חדש ונצמיד לו את המדיניות הבאה: AmazonEC2ContainerRegistryFullAccess
, אתה לא צריך לאפשר לו גישה למסך AWS. בסוף התהליך, אתה מקבל שני מפתחים מAWS, מפתח סודי
ומפתח מזהה סודי
, שמור אותם בצד, כי נצטרך אותם לעבודה הקרובה.
חזרה בשרת שלנו, אנחנו צריכים להתקין את AWS CLI. הדרך הרשמית להתקנה זמינה כאן. https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html#getting-started-install-instructions. לאחר זה, אתה יכול לבדוק את ההתקנה על ידי ריצת הפקודה aws --version
. בשלב זה, תצטרך לבצע את הפקודה aws configure
ולענות על שאלות על ידי ספיגת המפתחות שנוצרו באס"ו, המפתח סודי
והמזהה מפתח סודי
. גם תוכל לבחור פורמט יצוא, פשוט JSON, ולציין אזור ברירת המחדל, זה טוב יותר לבחור את האזור שבו יצרת את רישום ECR לפני.
הקמת סקריפט של מפעיל היישום.
בתהליך העבודה שלי כתבתי סקריפט קטן של shell שמבצע פעולות מסוימות, זה החלק המרכזי של התהליך, הוא מתחבר לרישום, מוריד את התמונה ומאתחל את שירות Docker המתאים, אני פשוט קורא לו redeploy.sh
ושומר אותו בתיקייה ממנה אני רוצה לרוץ את היישום, הנה התוכן:
#!/bin/bash
# Retrieve AWS ECR login command
aws ecr get-login-password --region [SWS_REGION] | docker login --username AWS --password-stdin [AWS_REGION].dkr.ecr.us-west-2.amazonaws.com
# Associating repositories with identifiers
declare -A repositories=(
["web"]="[REGISTRY_NAME]:latest"
)
# Check if service identifier is provided as a command line argument
if [ -z "$1" ]; then
echo "Please provide a service identifier as a command line argument."
exit 1
fi
service_identifier=$1
# Check if the provided service identifier exists in the repositories array
if [ -z "${repositories[$service_identifier]}" ]; then
echo "Invalid service identifier. Available identifiers: ${!repositories[@]}"
exit 1
fi
# pull the new image from the registry
repository=${repositories[$service_identifier]}
echo "Pulling [AWS_ACCOUNT_ID].dkr.ecr.[AWS_REGION].amazonaws.com/$repository"
docker pull "[AWS_ACCOUNT_ID].dkr.ecr.[AWS_REGION].amazonaws.com/$repository"
# Change directory to [APP_FOLDER]
cd /home/ubuntu/[APP_FOLDER] || {
echo "Failed to change directory to /home/ubuntu/[APP_FOLDER]"
exit 1
}
# stop and restart the service, this wil force docker compose to redownload the lates image
echo "Re-running service $service_identifier"
docker compose stop "$service_identifier"
docker compose up --no-deps "$service_identifier" -d
# Remove old and un-used docker images
echo "Removing unused Docker images"
docker image prune -fa
echo "Removed Dangling Images"
השלב הראשון של הסקריפט כולל התחברות לחשבון AWS עם AWS CLI כדי לקבל סימן שדהוקר ישתמש בו כאשר יוריד את תמונת Docker, זיכרו שהרישום פרטי, אנחנו לא יכולים לשלוף אותו בלי להתאמת את הזהות.
אז אנחנו מכריזים רשימת מאגרים ומקשרים אותם לזהות כלשהי, הזהות המצוינת תשמש כארגומנט שורת הפקודות, יותר על זה לאחר מכן. לאחר זה, אנחנו בודקים אם המשתמש סיפק ארגומנט שמתאים לזהות שירות קיימת, אנחנו רוצים שהוא יקליד משהו כמו ./redeploy web
לדוגמה, הסקריפט יקשר את הארגומנט web
למאגר web
כמו בשלב השני.
לאחר שיש לנו את זהות השירות, אנחנו יוצרים את כתובת המאגר באופן דינמי ומבצעים docker pull עם זה. זה מבטיח שתמונת Docker נושאת להורדה למערכת שלנו.
הסקריפט כעת יבדוק לתוך תיקיית היישום, /home/ubuntu/[APP_FOLDER]
זה הנחה שאתה מריץ את הכל תחת המשתמש ubuntu
ושתיקיית HOME
שלו נקראת ubuntu
, APP_FOLDER
מכיל את כל ההקמה.
השלב הבא כולל עצירה והתחלה של השירות, לאחר מכן אנחנו פשוט מסירים תמונות ישנות ולא משומשות עם הפקודה docker image prune -fa
אתה יכול ללמוד יותר כאן: https://docs.docker.com/reference/cli/docker/system/prune/.
תיק קובץ Docker compose
Compose הוא הכלי שמריץ את כל מערכתנו, הוא צריך קובץ בשם docker-compose.yml
שבו תגדיר את הכל, בוא נניח שהיישום שלנו צריך שירות redis
ושירות postgres
לפעולה, כך זה ייראה:
version: '3.9'
services:
web:
image: "[AWS_ACCOUNT_ID].dkr.ecr.[AWS_REGION].amazonaws.com/myapp:latest"
ports:
- 8080:8080
depends_on:
- redis
- db
env_file:
- .env
redis:
image: 'redis:alpine'
ports:
- '6379:6379'
db:
image: 'postgres:14'
restart: always
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
PGDATA: /var/lib/postgresql/data/pgdata
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "postgres" ]
timeout: 45s
interval: 10s
retries: 10
ports:
- '5437:5432'
volumes:
- ./opt/postgres/data:/var/lib/postgresql/data
היחס ./opt/postgres/data:/var/lib/postgresql/data
ימפה את תוכן שרת ה-Postgres לדיסק המקומי כך שהוא לא יאבד כאשר קונטיינר הדוקר עוצר. למדו יותר על הרצת Postgres עם docker-compose כאן https://medium.com/@agusmahari/docker-how-to-install-postgresql-using-docker-compose-d646c793f216. השתמשתי בהוראה שנקראת env_file
זה מאפשר ל-docker-compose לקרוא קובץ ולטעון את תוכנו לתוך קונטיינר הדוקר בזמן הרצה, עשיתי זאת כי בדרך כלל, קובץ docker-compose נשמר במערכת ניהול גרסאות ואני לא רוצה לשמור על משתני הסביבה ישירות בתוך ההוראה environment
בשירות. שימו לב ששירות שלנו נקרא web
כאן, מוקדם יותר כתבנו קובץ redeploy.sh
ואנחנו מתכוונים להריצו כך:
./redeploy.sh web
הארגומנט web
קשור לשם שירות שלנו, קובץ זה פשוט ממפה את הארגומנט לשם שירות בקובץ הדוקר.
הגדרת שירות לינוקס כדי לשמור על הכל רץ
בשלב הזה, אנחנו צריכים ליצור שירות לינוקס שידאג להתחלת היישום בכל פעם שהשרת מתחיל או שהיישום עוצר. הסקריפט הבא יעזור לכם לעשות זאת:
[Unit]
Description=[APP_NAME] service executed by docker compose
PartOf=docker.service
After=docker.service
After=network.target
[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/home/ubuntu/[APP_FOLDER]
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
בואו ננתח אותו !!!
-
החלק
Unit
מתאר את שירותנו ומציין איזה שירות היחידה שלנו היא חלק ממנו, במקרה זה, זה שירות הדוקר, זה יוודא שהשירות שלנו תמיד ירוץ כאשר שירות הדוקר רץ גם הוא. -
החלק
שירות
מתאר איך להריץ את שירותנו, החלקים המעניינים הםWorkingDirectory
,ExecStart
ופקודותExecStop
, הם יהיו בשימוש לפי משמעות שמם, למשל, אם השירות נקראmyapp
כשאתה מקליד את הפקודהsystemctl start myapp
הפקודהExecStart
תוצא לפעולה. אתה יכול ללמוד יותר על שירותים בלינוקס כאן https://www.redhat.com/sysadmin/systemd-oneshot-service. למד יותר על איך להריץ את שירות docker עםsystemd
כאן: https://bootvar.com/systemd-service-for-docker-compose/
השירות הזה צריך להיות מותקן באופן שהמערכת תריץ אותו כשהיא זקוקה, אתה תצטרך לשמור אותו בקובץ עם שם, לדוגמה: myapp.service
touch myapp.service
# open it
nano myapp.service
# paste the previous scrip in it
cp myapp.service /etc/systemd/system/myapp.serivce
בשלב זה הוא מוכר כשירות לינוקס, אתה יכול להריץ את systemctl start myapp
כדי להריץ אותו. הפקודה הדרושה הבאה היא
systemctl enable myapp.service
זה יוודא שהשירות יורץ אוטומטית על ידי השרת בכל התחלה. אתה יכול ללמוד יותר כאן: https://medium.com/@benmorel/creating-a-linux-service-with-systemd-611b5c8b91d6
שרת האינטרנט
השתמשתי ב-Nginx למשימה זו, הוא קטן וחזק, נפוץ מאוד ויכול לפעול כמאזן עומסים, שרת קבצים סטטיים, פרוקסי הפוך ועוד הרבה. הדבר הראשון לעשות הוא להתקין אותו.
sudo apt-get install nginx
בשלב זה התמונה של Docker כביכול רצה, בואו נניח שהיא מכילה אפליקציה שרצה בפורט 8080
, ושהפורט הזה קושר לשרת דרך קובץ docker-compose. עלינו להגדיר הגדרות פרוקסי הפוך בין Nginx לפורט שלנו. הנה התצורה הנחוצה:
upstream app_backend {
server localhost:8080; # the appliction port
}
server {
listen 80;
server_name [DOMAIN_NAME];
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}
נקרא לתצורה הזו myapp.conf
ונשמור אותה בתיקייה ש-Nginx ימצא אותה, תיקייה זו, בין היתר, נקראת /etc/nginx/conf.d/
.
sudo touch /etc/nginx/conf.d/myapp.conf
sudo nano /etc/nginx/conf.d/myapp.conf
# paste the content there
עכשיו כל מה שצריך זה לבדוק אותה ולאתחל את שירות ה-NGINX עם הפקודות הבאות:
sudo nginx -t # test if the config is valid
sudo nginx -s reload # reload the nginx service so it will consider it
תצורה זו תורה ל-Nginx להקשיב לתנועה בפורט 80
ועם שם הדומיין [DOMAIN_NAME]
ולשלוח אותה לשרת האפליקציה שלך בפורט 8080
דרך ההוראה proxy_pass
. השורה location / {
פירושה ללכוד את כל הבקשות המתחילות עם /
ולבצע את הפעולות שכתובות מתחת לבלוק location
. למידע נוסף, ראו כאן https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/How-to-setup-Nginx-reverse-proxy-servers-by-example.
גרף הבניה
לאחר שהגדרנו את השרת, עלינו להקים עכשיו את קו הבנייה, שמורכב בעיקר מ 1 שלב, לכתוב קובץ תהליך של GitHub Action ולהוסיפו למיזם, בואו נתחיל.
הגדרת GitHub Actions
GitHub action ישמש לבניית תמונת Docker מקוד המקור שלנו ולהעלות אותה לרשם, ממנו יושך התמונה ויבוצע על השרת. אני אקח דוגמה של Dockerfile לשם דוגמה, אך במעשה, עליך לכתוב Dockerfile משלך. ליישום express.js, קובץ הdocker יהיה כזה:
# Fetching the minified node image on apline linux
FROM node:slim
# Declaring env
ENV NODE_ENV production
# Setting up the work directory
WORKDIR /express-docker
# Copying all the files in our project
COPY . .
# Installing dependencies
RUN npm install
# Installing pm2 globally
RUN npm install pm2 -g
# Exposing server port
EXPOSE 8080
# Starting our application
CMD pm2 start process.yml && tail -f /dev/null
בנייה והרצה של קובץ docker זה יתחיל את היישום שלנו בפורט 8000, אך בהגדרה שלנו, עלינו להריץ אותו עם docker-compose.
הדבר הבא הוא להגדיר את קו התהליכים של GitHub actions. לשם כך, פשוט צור תיקייה .github/workflows
בשורש המיזם וצור קובץ בשם docker-build.yml
, ונכתוב את התהליך שלנו בתוכו.
name: Build, Push to ECS and Deploy to Server
on:
push:
branches: ['deploy/main']
jobs:
build:
name: Build Web Image
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: [AWS_REGION]
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: [REPOSITORY_NAME]
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Restart the service via SSH
uses: appleboy/[email protected]
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: /home/ubntu/[APP_DIRECTORY]/redeploy.sh web
יש כמה שלבים שצריך לבדוק כאן:
-
הגדרת אסימונות AWS
: כאן המערכת תטען את המפתח AWS שיצרת בעבר, עליך לרשום אותם לסודות חשבונך ב GitHub -
בנייה, תיוג והעלאת תמונה ל Amazon ECR
: שלב זה יריץ את הפקודותdocker build
וdocker push
ליצירת תמונת Docker -
איפוס השירות דרך SSH
שלב זה יחבר לשרת ויאפס את היישום כולו בבת אחת.
שרשרת התהליכים הזו תרוץ כל פעם שיש הצעת גרסה שמתמזגת לתת-העץ deploy/main
.
on:
push:
branches: ['deploy/main']
בשלב זה כל המערכת מושלמת וקשורה, עכשיו אפשר לערוך וליישם אותה למקרה הספציפי שלך. במאמר עתידי, אשתף את התהליך של בניית היישום עצמו לשם הפעלה והרצה בקובץ Docker.
סיכום
מאמר זה מנסה לתאר את התהליך שאני משתמש בו כדי להקים VPS לאוטומציה כשמדובר בהפצה. הוא מתאר איך לקבוע את התהליך של הרצת היישום בתוך השרת ואת תהליך בניית היישום, כל חלק יכול להתבצע עם כלי אחר, למשל, אפשר להחליף את nginx ב-Treafik אם רוצים, ולהחליף את השירות systemd
בתוכנה ב-supervisor
ועוד. תהליך זה לא כולל דברים נוספים כמו גיבוי של השרת או סגירת פורטים ברירת המחדל בשרתים, אלה יוסבירו במאמרים עתידיים. אני פתוח לשאלות אם אתה רוצה להתאים את זה לתהליך שלך. במאמר אחר אשתף איך להקים יישום כך שיהיה מוכן להפעלה במונחים של הפצה, זהו החלק של התהליך שבא לפני בניית תמונת Docker.
אני מקוה שהינך נהנית מהקריאה.
אני כאן כדי לעזור לך ליישם זאת בתוך חברה או צוות שלך, כדי שתוכל להתרכז במשימות הגלובליות שלך ולחסוך כסף לפני שתשתמש בפוטנציאל העצום של מסטודון.