گاهی اوقات، تنها کاری که نیاز دارید این است که دستورات را مستقیماً روی میزبانهای هدف اجرا کنید، همانطور که در پوسته bash انجام میدهید. اینجاست که ماژول پوسته Ansible به کار میآید.
در این راهنما، شما با ماژول پوسته Ansible، نحوه عملکرد آن و نحوه استفاده از آن برای اجرای دستورات در گرههای مدیریتشده آشنا خواهید شد.
ماژول Ansible Shell چیست؟ #
ماژول پوسته Ansible یک ماژول مفید است که به شما امکان میدهد دستورات را مستقیماً روی پوسته اهداف از راه دور اجرا کنید، درست همانطور که اگر وارد سیستم شدهاید، این کار را انجام میدهید. با انجام این کار، به حفظ اصالت اجرای دستورات کمک میکند.
ماژول shell ارتباط نزدیکی با ماژول command دارد. هر دو به دستیابی به نتیجه یکسانی کمک میکنند. با این حال، چند تفاوت بین این دو وجود دارد:
- ماژول shell دستورات را مستقیماً روی shell میزبانهای هدف اجرا میکند. به طور پیشفرض، ماژول shell از
/bin/shshell برای اجرای دستورات استفاده میکند، اگرچه میتوانید آن را طوری پیکربندی کنید که از shellهای دیگر استفاده کند. با ماژول command، دستورات اجرا شده از طریق shell پردازش نمیشوند. - از آنجایی که دستورات روی پوسته اجرا نمیشوند، ماژول فرمان از متغیرهای محیطی ، پایپها و سایر عملگرها مانند ‘>’، ‘<‘، ‘&’، ‘;’ و ‘| |’ پشتیبانی نمیکند. با ماژول پوسته، پایپ، تغییر مسیر و متغیرها به طور کامل پشتیبانی میشوند. بنابراین، ماژول پوسته انعطافپذیری بیشتری را فراهم میکند.
- اگر اجرای امن دستورات روی سیستمهای هدف اولویت شماست، از ماژول دستور استفاده کنید. برخلاف ماژول پوسته، ماژول دستور تحت تأثیر محیط پوسته کاربر راه دور قرار نمیگیرد.
ماژول پوسته Ansible در مقایسه با سایر ماژولها #
ماژول پوسته Ansible در همان دسته ماژولهای command، script، و قرار میگیرد raw. اینها در مجموع به عنوان دستورات اجرا (run commands) شناخته میشوند.
اگرچه دستورات run در انجام سریع کارها کارآمد هستند، اما باید فقط به عنوان آخرین راه حل استفاده شوند. دلیل این امر این است که آنها هنگام اجرای وظایف کمترین منطق را اعمال میکنند و مفهوم حالت مطلوب وجود ندارد. اجرای بعدی دستور shell ممکن است در صورت برآورده شدن شرط قبلی منجر به خطا شود.
علاوه بر این، شناسایی خطاها با ماژول shell امکانپذیر نیست، مگر اینکه نتیجه دستور اول را ثبت کنید. سپس باید آن را با یک وظیفه بعدی در playbook دنبال کنید تا یک منطق شرطی را برای تأیید وقوع خطا و سپس رسیدگی به آن پیادهسازی کنید. این میتواند منجر به گلوگاههایی شود که به طور قابل توجهی اتوماسیون را تضعیف میکنند.
به همین دلیل، دستورات shell باید به انجام وظایف ساده در سیستمهای راه دور محدود شوند. در مواردی که وضعیت مطلوب سرویسها یا برنامهها مورد نیاز است، ماژولهای مخصوص وظیفه مانند service، copy، file، و lineinfile، برای نام بردن چند مورد، توصیه میشوند. این امر باعث میشود playbookها متنوعتر و قابل استفاده مجددتر باشند.
پیش نیازها #
برای دنبال کردن این راهنما، باید موارد زیر را داشته باشید:
- یک گره کنترل Ansible. در این آموزش، ما اوبونتو ۲۰.۰۴ را اجرا میکنیم. در صورتی که Ansible را نصب نکردهاید، راهنمای ما در مورد نحوه نصب و پیکربندی Ansible در اوبونتو ۲۰.۰۴ را دنبال کنید .
- یک میزبان از راه دور که دستورات را روی آن اجرا خواهید کرد.
اجرای دستورات پوستهی موقت Ansible #
قدرت واقعی Ansible در playbookها نهفته است. با این حال، playbookها برای اجرای وظایف پیچیده روی میزبانهای هدف استفاده میشوند. فرض کنید میخواهید دستورات را خیلی سریع و بدون ذخیره کردن آنها برای استفاده بعدی اجرا کنید. چگونه این کار را انجام میدهید؟ اینجاست که دستورات ad-hoc مفید واقع میشوند.
دستورات Ad-hoc دستورات تک خطی هستند که میتوانید بدون نیاز به نوشتن یک Playbook، آنها را به صورت آنی اجرا کنید.
برای مثال، ممکن است بخواهید زمان روشن بودن یا میزان استفاده از فضای دیسک میزبانهای از راه دور خود را بررسی کنید. به جای نوشتن یک کتاب راهنمای کامل برای چنین وظایفی، رویکرد بهتر این است که دستورات ad-hoc را روی میزبانهای خود اجرا کنید.
دستورات Ad-hoc از سینتکس زیر پیروی میکنند:
ansible [pattern] -m [MODULE] -a {COMMAND_OPTIONS}
این الگو گروه میزبانی را که میزبان هدف به آن تعلق دارد، مشخص میکند. این -mگزینه نوع ماژول را مشخص میکند در حالی که این -aگزینه آرگومانهای دستور را دریافت میکند.
بگذارید چند مثال بزنیم.
برای بررسی زمان روشن بودن همه میزبانهای هدف، دستور زیر را اجرا کنید:
sudo ansible all -m shell -a 'uptime -p'

برای بررسی میزان استفاده از حافظه، دستور زیر را اجرا کنید:
sudo ansible all -m shell -a 'free -m'

برای بررسی میزان استفاده از فضای دیسک توسط میزبان تحت گروه db_server، دستور زیر را اجرا کنید:
sudo ansible db_server -m shell -a 'df -Th'

اجرای یک دستور واحد با ماژول پوسته Ansible #
گذشته از اجرای دستورات ad hoc، ماژول پوسته Ansible در playbooks نیز برای مشخص کردن وظایفی که باید روی میزبانهای از راه دور انجام شوند، استفاده میشود.
دفترچه راهنمای زیر را در نظر بگیرید.
---
- name: Shell module example
hosts: webservers
tasks:
- name: Check system information
shell:
"lsb_release -a"
register: os_info
- debug:
msg: "{{os_info.stdout_lines}}"
در این مثال، playbook روی یک گروه میزبان به نام webservers اجرا میشود و lsb_release -aدستوری را اجرا میکند که جزئیات مربوط به نسخه سیستم عامل را بازیابی میکند. سپس خروجی در یک متغیر register به نام ذخیره میشود os_info.
خط آخر با استفاده از ماژول، خروجی ذخیره شده در os_infoمتغیر را در خروجی استاندارد (stdout) چاپ میکند debug.
خروجی اجرای playbook به صورت زیر است.

اجرای یک دستور با استفاده از ماژول Shell در صورت عدم وجود فایل #
این createsپارامتر به شما امکان میدهد در صورت عدم وجود فایل، دستوری را اجرا کنید. این پارامتر مسیر فایلی را مشخص میکند که در صورت وجود، دستور مورد نظر برای اجرا نادیده گرفته میشود.
Playbook نمایش داده شده بررسی میکند که آیا فایل hello.txtدر دایرکتوری خانگی میزبان هدف وجود دارد یا خیر. اگر فایل وجود نداشته باشد، دستور shell مشخص شده اجرا میشود. اگر فایل وجود داشته باشد، دستور shell متوقف میشود.
---
- name: Create a file in the home directory if it doesn't exist
hosts: webservers
tasks:
- name: Create a file in the home directory
shell: echo "Hey guys!" > $HOME/hello.txt
args:
creates: "$HOME/hello.txt"
از آنجایی که فایل در سیستم هدف از راه دور وجود ندارد، همانطور که از اجرای playbook مشاهده میکنید، دستور shell با موفقیت اجرا میشود.

دستور زیر تأیید میکند که hello.txtفایل در دایرکتوری خانگی هدف از راه دور ایجاد شده است.
ssh root@173.82.120.115 "ls -l ~"

اجرای یک دستور با استفاده از ماژول Shell در صورت وجود فایل #
این removesپارامتر نام فایل را مشخص میکند و اگر فایل وجود داشته باشد، دستور اجرا میشود. در مثال قبلی، hello.txtفایل در دایرکتوری خانگی سیستم هدف از راه دور ایجاد شد.
در این playbook، removesپارامتر بررسی میکند که آیا این فایل در سیستم هدف از راه دور وجود دارد یا خیر. و از آنجایی که شما قبلاً آن را ایجاد کردهاید، playbook دستور shell را اجرا میکند که فایل را حذف میکند.
---
- name: Remove a file in the home directory only if it exists
hosts: webservers
tasks:
- name: Remove a file in the home directory
shell: rm $HOME/hello.txt
args:
removes: "$HOME/hello.txt"
warn: false
اجرای playbook تأیید میکند که فایل حذف شده است.

اجرای یک دستور در یک دایرکتوری متفاوت #
برای اجرای یک دستور shell در داخل یک دایرکتوری خاص، از chdirپارامتر استفاده کنید. playbook زیر فایل باینری آپاچی را در مسیر /usr/local/src دانلود میکند .
---
- name: Download Apache source file to /usr/local/src directory
hosts: webservers
tasks:
- name: Download Apache tarball file
shell: wget https://dlcdn.apache.org/httpd/httpd-2.4.52.tar.gz
args:
chdir: /usr/local/src
warn: false
دفترچه راهنما تأیید میکند که کار با موفقیت انجام شده است.

استفاده از ماژول پوسته Ansible با متغیرهای محیطی #
ماژول shell همچنین به شما امکان میدهد متغیرهای محیطی جدیدی تنظیم کنید. این کار با استفاده از environmentپارامتر امکانپذیر است. playbook زیر را در نظر بگیرید.
---
- name: Environment variable example
hosts: webservers
tasks:
- name: Ansible Shell module set an environment variable
shell: echo $NEW_VAR
register: command_result
envnironment:
NEW_VAR: "john_doe"
- debug:
msg: "{{ command_result.stdout_lines }}"
Playbook NEW_VARمتغیر محیطی را روی john_doe تنظیم میکند .
نکته: متغیر محیطی فقط برای آن وظیفه خاص تنظیم شده است. در وظایف بعدی، متغیر NEW_VAR در دسترس نخواهد بود.

اجرای چندین دستور با ماژول پوسته Ansible #
تاکنون، ماژول shell را دیدهاید که دستورات واحدی را روی میزبانهای مدیریتشده اجرا میکند. همچنین میتوانید مجموعهای از دستورات را به ترتیب زمانی مشخص کنید.
برای رسیدن به این هدف، دستور shell را با یک نوار عمودی شروع کنید و به دنبال آن لیستی از کارهایی که باید انجام شود، قرار دهید. در این مثال، خروجی دستور date، uptime، و به ترتیب در فایلهای ، و echoذخیره میشود که سپس در دایرکتوری ذخیره میشوند .date.txtuptime.txthello.txt/tmp
---
- name: Shell module example
hosts: webservers
tasks:
- name: Run multiple commands
shell: |
date > /tmp/date.txt
uptime > /tmp/uptime.txt
echo "Hello World" > /tmp/hello.txt
این Playbook وظایف را به ترتیب از اولین وظیفه تا آخرین وظیفه اجرا میکند.

اجرای دستورات با استفاده از پایپها و تغییر مسیر #
همانطور که قبلاً ذکر شد، ماژول shell همچنین pipeها و redirectionها را میپذیرد. در واقع، playbook قبلی از نماد redirection ( >) برای ذخیره خروجی دستورات ذکر شده در فایلهای جداگانه استفاده میکرد.
فرض کنید میخواهید تمام فایلهای متنی ایجاد شده در دایرکتوری /tmp را فهرست کرده و نتیجه را در فایل دیگری به نام dirlist.txt در همان دایرکتوری ذخیره کنید.
دستور shell به این شکل خواهد بود.
ls -l /tmp | grep .txt > /tmp/dirlist.txt
بخش اول دستور، تمام فایلهای موجود در دایرکتوری /tmp را فهرست میکند . سپس نتیجه به دستور grep ارسال میشود که خروجی را فیلتر میکند تا فقط فایلهای متنی را شامل شود. سپس خروجی نهایی با استفاده از نماد تغییر مسیر «بزرگتر از» در فایل dirlist.txt ذخیره میشود .
حالا بیایید یک قدم جلوتر برویم و نتیجه را در stdout چاپ کنیم. برای این کار، یک کار دوم لازم است. هدف نمایش محتویات فایل dirlist.txt در stdout است. دستور shell برای فهرست کردن محتوای فایل به صورت زیر است:
cat /tmp/dirlist.txt
خروجی دستور سپس در متغیری به نام displayfile ثبت میشود و پیام با استفاده از ماژول debug در stdout نمایش داده میشود . کل playbook به این شکل است.
---
- name: Shell module example
hosts: webservers
tasks:
- name: List text files in tmp directory and save result in output file
shell: "ls -l /tmp | grep .txt > /tmp/dirlist.txt
- name: Display the contents of the output file
shell: cat /tmp/dirlist.txt
register: displayfile
- debug:
msg: "{{ displayfile.stdout_lines }}"
وقتی playbook اجرا میشود، تمام فایلهای متنی موجود در دایرکتوری /tmp ، از جمله فایل dirlist.txt ، در stdout چاپ میشوند:

جلوگیری از تزریق پوسته #
از آنجایی که این ماژول دستورات را روی پوسته (Shell) روی اهداف از راه دور اجرا میکند، مستعد تزریق دستورات پوسته (Shell Command Injection) است . تزریق پوسته یک روش حمله است که در آن مهاجم دستورات دلخواه را روی میزبان اجرا میکند تا زیرساختهای اساسی را به خطر بیندازد.
هنگام استفاده از متغیرهای ماژول Shell، Ansible توصیه میکند از quoteفیلتر برای خنثی کردن تهدیدات تزریق shell استفاده کنید.
{{ variable_name | quote }}
یک دفترچه راهنمای ساده زیر را در نظر بگیرید.
---
- name: Ansible quote filter demo
hosts: webservers
vars:
- username: cherry
tasks:
- name: Print variable
debug:
msg: " Running playbook as user {{ username | quote }}"
این usernameمتغیر در انتهای msgپارامتر با استفاده از quoteفیلتر ارجاع داده میشود تا در صورت تزریق یک رشته دستور دلخواه همراه با متغیر نام کاربری، از اجرای آن جلوگیری شود.
نتیجه گیری #
ماژول پوسته Ansible یک ماژول مفید است که میتواند به شما در اجرای سریع وظایف ساده روی میزبانهای مدیریتشده کمک کند. این commandماژول جایگزین مناسبی برای ماژول پوسته است. این ماژول روشی مطمئنتر و ایمنتر برای اجرای وظایف فراهم میکند زیرا دستورات روی پوسته اهداف از راه دور پردازش نمیشوند. با این حال، هنگام استفاده از متغیرهای قالب در playbookهای خود، برای جلوگیری از حملات تزریق shell، فیلتر کنید.
