چندی پیش لازم بود چندین کتاب بازی Ansible بنویسم تا سرور را برای استقرار یک برنامه Rails آماده کنم. و با کمال تعجب، من یک کتابچه راهنمای گام به گام ساده پیدا نکردم. من نمیخواستم کتاب بازی شخص دیگری را بدون اینکه بفهمم چه اتفاقی میافتد کپی کنم، و در نهایت مجبور شدم اسناد را بخوانم و همه چیز را خودم جمعآوری کنم. شاید بتوانم با کمک این مقاله به کسی کمک کنم تا این روند را سرعت بخشد.
اولین چیزی که باید درک کنید این است که ansible یک رابط کاربری مناسب برای انجام یک لیست از پیش تعریف شده از اقدامات روی سرور(های) راه دور از طریق SSH در اختیار شما قرار می دهد. هیچ جادویی در اینجا وجود ندارد، شما نمی توانید یک پلاگین را نصب کنید و برنامه خود را با داکر، مانیتورینگ و سایر موارد مفید، بدون استفاده از برنامه خود نصب کنید. برای نوشتن یک کتاب بازی، باید بدانید که دقیقا می خواهید چه کاری انجام دهید و چگونه آن را انجام دهید. به همین دلیل است که من از کتابهای بازی آماده GitHub یا مقالاتی مانند: «کپی و اجرا، کار میکند» راضی نیستم.
آنچه ما نیاز داریم؟
همانطور که قبلاً گفتم، برای نوشتن کتاب بازی باید بدانید که می خواهید چه کاری انجام دهید و چگونه آن را انجام دهید. بیایید تصمیم بگیریم به چه چیزی نیاز داریم. برای یک برنامه Rails به چندین بسته سیستمی نیاز داریم: nginx، postgresql (redis و غیره). علاوه بر این، ما به یک نسخه خاص از یاقوت نیاز داریم. بهتر است آن را از طریق rbenv (rvm, asdf...) نصب کنید. اجرای همه اینها به عنوان یک کاربر ریشه همیشه ایده بدی است، بنابراین باید یک کاربر جداگانه ایجاد کنید و حقوق او را پیکربندی کنید. پس از این، شما باید کد ما را در سرور آپلود کنید، تنظیمات مربوط به nginx، postgres و غیره را کپی کنید و همه این خدمات را شروع کنید.
در نتیجه، توالی اقدامات به شرح زیر است:
- به عنوان root وارد شوید
- نصب بسته های سیستمی
- ایجاد یک کاربر جدید، پیکربندی حقوق، کلید ssh
- بسته های سیستمی (nginx و غیره) را پیکربندی کرده و آنها را اجرا کنید
- ما یک کاربر در پایگاه داده ایجاد می کنیم (شما می توانید بلافاصله یک پایگاه داده ایجاد کنید)
- به عنوان یک کاربر جدید وارد شوید
- rbenv و ruby را نصب کنید
- نصب باندلر
- در حال آپلود کد برنامه
- راه اندازی سرور پوما
علاوه بر این، آخرین مراحل را میتوان با استفاده از capistrano انجام داد، حداقل میتواند کد را در فهرستهای انتشار کپی کند، پس از استقرار موفقیتآمیز، نسخه را با یک پیوند نمادین تغییر دهد، تنظیمات را از یک فهرست مشترک کپی کند، پوما را مجدداً راهاندازی کند و غیره. همه اینها را می توان با استفاده از Ansible انجام داد، اما چرا؟
ساختار فایل
Ansible سختگیرانه دارد
کتاب بازی ساده
Playbook یک فایل yml است که با استفاده از نحو خاص، توضیح میدهد که Ansible چه کاری و چگونه باید انجام دهد. بیایید اولین کتاب بازی را ایجاد کنیم که هیچ کاری انجام نمی دهد:
---
- name: Simple playbook
hosts: all
در اینجا ما به سادگی می گوییم که کتاب بازی ما نامیده می شود Simple Playbook
و اینکه محتویات آن باید برای همه هاست ها اجرا شود. میتوانیم آن را در فهرست /ansible با نام ذخیره کنیم playbook.yml
و سعی کنید اجرا کنید:
ansible-playbook ./playbook.yml
PLAY [Simple Playbook] ************************************************************************************************************************************
skipping: no hosts matched
Ansible می گوید که هیچ میزبانی را نمی شناسد که با لیست همه مطابقت داشته باشد. آنها باید در یک ویژه ذکر شوند
بیایید آن را در همان فهرست ansible ایجاد کنیم:
123.123.123.123
به این ترتیب ما به سادگی هاست را مشخص می کنیم (به طور ایده آل میزبان VPS ما برای آزمایش، یا می توانید localhost را ثبت کنید) و آن را تحت نام ذخیره می کنیم. inventory
.
می توانید ansible را با یک فایل invetory اجرا کنید:
ansible-playbook ./playbook.yml -i inventory
PLAY [Simple Playbook] ************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************
PLAY RECAP ************************************************************************************************************************************
اگر به هاست مشخص شده دسترسی ssh دارید، ansible به سیستم از راه دور متصل شده و اطلاعاتی را جمع آوری می کند. (پیشفرض TASK [جمعآوری حقایق]) که پس از آن گزارش کوتاهی در مورد اجرا ارائه میکند (PLAY RECAP).
به طور پیش فرض، اتصال از نام کاربری استفاده می کند که تحت آن به سیستم وارد شده اید. به احتمال زیاد روی هاست نخواهد بود. در فایل playbook، می توانید تعیین کنید که با استفاده از دستور remote_user از کدام کاربری برای اتصال استفاده کنید. همچنین، اطلاعات مربوط به یک سیستم از راه دور ممکن است اغلب برای شما غیر ضروری باشد و نباید وقت خود را برای جمع آوری آن تلف کنید. این وظیفه را نیز می توان غیرفعال کرد:
---
- name: Simple playbook
hosts: all
remote_user: root
become: true
gather_facts: no
دوباره playbook را اجرا کنید و مطمئن شوید که اتصال کار می کند. (اگر کاربر ریشه را مشخص کرده اید، برای به دست آوردن حقوق بالاتر، باید دستور تبدیل: true را نیز مشخص کنید. همانطور که در مستندات نوشته شده است: become set to ‘true’/’yes’ to activate privilege escalation.
اگرچه کاملاً مشخص نیست که چرا).
شاید شما خطای ناشی از این واقعیت را دریافت کنید که ansible نمی تواند مفسر پایتون را تعیین کند، سپس می توانید آن را به صورت دستی مشخص کنید:
ansible_python_interpreter: /usr/bin/python3
با دستور می توانید بفهمید پایتون کجاست whereis python
.
نصب پکیج های سیستمی
توزیع استاندارد Ansible شامل ماژول های زیادی برای کار با بسته های سیستمی مختلف است، بنابراین به هر دلیلی مجبور نیستیم اسکریپت های bash بنویسیم. حال برای به روز رسانی سیستم و نصب بسته های سیستمی به یکی از این ماژول ها نیاز داریم. من لینوکس اوبونتو را روی VPS خود دارم، بنابراین برای نصب بسته ها از آن استفاده می کنم apt-get
и
بیایید کتاب بازی خود را با اولین وظایف تکمیل کنیم:
---
- name: Simple playbook
hosts: all
remote_user: root
become: true
gather_facts: no
tasks:
- name: Update system
apt: update_cache=yes
- name: Install system dependencies
apt:
name: git,nginx,redis,postgresql,postgresql-contrib
state: present
Task دقیقاً همان کاری است که Ansible روی سرورهای راه دور انجام می دهد. ما به کار یک نام می دهیم تا بتوانیم اجرای آن را در گزارش ردیابی کنیم. و ما با استفاده از نحو یک ماژول خاص، آنچه را که باید انجام دهد، توضیح می دهیم. در این مورد apt: update_cache=yes
- می گوید برای به روز رسانی بسته های سیستم با استفاده از ماژول apt. دستور دوم کمی پیچیده تر است. لیستی از بسته ها را به ماژول apt ارسال می کنیم و می گوییم که هستند state
باید تبدیل شود present
یعنی می گوییم این بسته ها را نصب کنید. به روشی مشابه، میتوانیم به آنها بگوییم که آنها را حذف کنند، یا با تغییر ساده آنها را بهروزرسانی کنند state
. لطفاً توجه داشته باشید که برای اینکه ریل ها با postgresql کار کنند به بسته postgresql-contrib نیاز داریم که اکنون در حال نصب آن هستیم. باز هم، شما باید این کار را بدانید و انجام دهید؛ اما به تنهایی این کار را انجام نخواهد داد.
دوباره playbook را اجرا کنید و بررسی کنید که بسته ها نصب شده باشند.
ایجاد کاربران جدید.
برای کار با کاربران، Ansible یک ماژول - کاربر نیز دارد. بیایید یک کار دیگر اضافه کنیم (من بخش های شناخته شده کتاب بازی را پشت نظرات پنهان کردم تا هر بار به طور کامل آن را کپی نکنم):
---
- name: Simple playbook
# ...
tasks:
# ...
- name: Add a new user
user:
name: my_user
shell: /bin/bash
password: "{{ 123qweasd | password_hash('sha512') }}"
ما یک کاربر جدید ایجاد می کنیم، یک شل و رمز عبور برای آن تنظیم می کنیم. و سپس با مشکلات متعددی مواجه می شویم. اگر لازم باشد نام های کاربری برای هاست های مختلف متفاوت باشد چه؟ و ذخیره رمز عبور به صورت متن واضح در کتاب بازی ایده بسیار بدی است. برای شروع، اجازه دهید نام کاربری و رمز عبور را در متغیرها قرار دهیم و در پایان مقاله نحوه رمزگذاری رمز عبور را نشان می دهم.
---
- name: Simple playbook
# ...
tasks:
# ...
- name: Add a new user
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"
متغیرها در کتاب های بازی با استفاده از بریس های مجعد دوتایی تنظیم می شوند.
مقادیر متغیرها را در فایل موجودی نشان خواهیم داد:
123.123.123.123
[all:vars]
user=my_user
user_password=123qweasd
لطفا به بخشنامه توجه کنید [all:vars]
- می گوید که بلوک بعدی متن متغیرها (vars) هستند و برای همه هاست ها (همه) قابل اجرا هستند.
طراحیش هم جالبه "{{ user_password | password_hash('sha512') }}"
. موضوع این است که ansible کاربر را از طریق نصب نمی کند user_add
مثل اینکه شما آن را به صورت دستی انجام می دهید. و تمام داده ها را مستقیماً ذخیره می کند، به همین دلیل است که ما باید رمز عبور را نیز از قبل به هش تبدیل کنیم، کاری که این دستور انجام می دهد.
بیایید کاربر خود را به گروه sudo اضافه کنیم. با این حال، قبل از این باید مطمئن شویم که چنین گروهی وجود دارد زیرا هیچ کس این کار را برای ما انجام نخواهد داد:
---
- name: Simple playbook
# ...
tasks:
# ...
- name: Ensure a 'sudo' group
group:
name: sudo
state: present
- name: Add a new user
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"
groups: "sudo"
همه چیز بسیار ساده است، ما همچنین یک ماژول گروهی برای ایجاد گروه داریم، با نحو بسیار شبیه به apt. سپس کافی است این گروه را به کاربر ثبت کنید (groups: "sudo"
).
همچنین اضافه کردن یک کلید ssh به این کاربر مفید است تا بتوانیم با استفاده از آن بدون رمز عبور وارد شویم:
---
- name: Simple playbook
# ...
tasks:
# ...
- name: Ensure a 'sudo' group
group:
name: sudo
state: present
- name: Add a new user
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"
groups: "sudo"
- name: Deploy SSH Key
authorized_key:
user: "{{ user }}"
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: present
در این مورد، طراحی جالب است "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
- محتویات فایل id_rsa.pub را کپی می کند (نام شما ممکن است متفاوت باشد)، یعنی بخش عمومی کلید ssh و آن را در لیست کلیدهای مجاز برای کاربر در سرور آپلود می کند.
نقش ها
هر سه وظیفه برای ایجاد استفاده را می توان به راحتی در یک گروه از وظایف طبقه بندی کرد و بهتر است این گروه را جدا از کتاب اصلی ذخیره کنید تا بیش از حد بزرگ نشود. برای این منظور Ansible دارد
با توجه به ساختار فایل که در ابتدا مشخص شد، نقش ها باید در یک پوشه نقش جداگانه قرار گیرند، برای هر نقش یک فهرست جداگانه با همان نام وجود دارد، در داخل فهرست وظایف، فایل ها، قالب ها و غیره.
بیایید یک ساختار فایل ایجاد کنیم: ./ansible/roles/user/tasks/main.yml
(main فایل اصلی است که با اتصال یک نقش به playbook بارگذاری و اجرا می شود؛ فایل های نقش دیگر را می توان به آن متصل کرد). اکنون می توانید تمام وظایف مربوط به کاربر را به این فایل منتقل کنید:
# Create user and add him to groups
- name: Ensure a 'sudo' group
group:
name: sudo
state: present
- name: Add a new user
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"
groups: "sudo"
- name: Deploy SSH Key
authorized_key:
user: "{{ user }}"
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: present
در playbook اصلی، برای استفاده از نقش کاربر باید مشخص کنید:
---
- name: Simple playbook
hosts: all
remote_user: root
gather_facts: no
tasks:
- name: Update system
apt: update_cache=yes
- name: Install system dependencies
apt:
name: git,nginx,redis,postgresql,postgresql-contrib
state: present
roles:
- user
همچنین، ممکن است منطقی باشد که سیستم را قبل از همه کارهای دیگر به روز کنید؛ برای انجام این کار، می توانید نام بلوک را تغییر دهید tasks
که در آن تعریف شده اند pre_tasks
.
راه اندازی nginx
ما باید Nginx را از قبل نصب کرده باشیم؛ باید آن را پیکربندی و اجرا کنیم. بیایید آن را بلافاصله در نقش انجام دهیم. بیایید یک ساختار فایل ایجاد کنیم:
- ansible
- roles
- nginx
- files
- tasks
- main.yml
- templates
اکنون به فایل ها و قالب ها نیاز داریم. تفاوت بین آنها این است که ansible فایل ها را به طور مستقیم کپی می کند. و قالب ها باید پسوند j2 را داشته باشند و می توانند با استفاده از همان بریس های دوبل فرفری از مقادیر متغیر استفاده کنند.
بیایید nginx in را فعال کنیم main.yml
فایل. برای این ما یک ماژول systemd داریم:
# Copy nginx configs and start it
- name: enable service nginx and start
systemd:
name: nginx
state: started
enabled: yes
در اینجا نه تنها می گوییم که nginx باید راه اندازی شود (یعنی آن را راه اندازی کنیم)، بلکه بلافاصله می گوییم که باید فعال شود.
حالا بیایید فایل های پیکربندی را کپی کنیم:
# Copy nginx configs and start it
- name: enable service nginx and start
systemd:
name: nginx
state: started
enabled: yes
- name: Copy the nginx.conf
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
- name: Copy template my_app.conf
template:
src: my_app_conf.j2
dest: /etc/nginx/sites-available/my_app.conf
owner: root
group: root
mode: '0644'
ما فایل اصلی پیکربندی nginx را ایجاد می کنیم (می توانید آن را مستقیماً از سرور بگیرید یا خودتان بنویسید). و همچنین فایل پیکربندی برنامه ما در دایرکتوری sites_available (این ضروری نیست اما مفید است). در حالت اول، ما از ماژول کپی برای کپی فایل ها استفاده می کنیم (فایل باید داخل باشد /ansible/roles/nginx/files/nginx.conf
). در مرحله دوم، الگو را کپی می کنیم و مقادیر متغیرها را جایگزین می کنیم. قالب باید داخل باشد /ansible/roles/nginx/templates/my_app.j2
). و ممکن است چیزی شبیه به این باشد:
upstream {{ app_name }} {
server unix:{{ app_path }}/shared/tmp/sockets/puma.sock;
}
server {
listen 80;
server_name {{ server_name }} {{ inventory_hostname }};
root {{ app_path }}/current/public;
try_files $uri/index.html $uri.html $uri @{{ app_name }};
....
}
به درج ها توجه کنید {{ app_name }}
, {{ app_path }}
, {{ server_name }}
, {{ inventory_hostname }}
- اینها همه متغیرهایی هستند که مقادیر آنها Ansible قبل از کپی در قالب جایگزین می شود. اگر از یک کتاب بازی برای گروه های مختلف میزبان استفاده می کنید، این کار مفید است. به عنوان مثال، ما می توانیم فایل موجودی خود را اضافه کنیم:
[production]
123.123.123.123
[staging]
231.231.231.231
[all:vars]
user=my_user
user_password=123qweasd
[production:vars]
server_name=production
app_path=/home/www/my_app
app_name=my_app
[staging:vars]
server_name=staging
app_path=/home/www/my_stage
app_name=my_stage_app
اگر اکنون playbook خود را راه اندازی کنیم، وظایف مشخص شده را برای هر دو هاست انجام می دهد. اما در عین حال، برای یک میزبان استیجینگ، متغیرها با متغیرهای تولیدی متفاوت خواهند بود و نه تنها در نقشها و کتابهای بازی، بلکه در تنظیمات nginx نیز متفاوت خواهند بود. {{ inventory_hostname }}
لازم نیست در فایل موجودی مشخص شود - این
اگر می خواهید یک فایل موجودی برای چندین هاست داشته باشید، اما فقط برای یک گروه اجرا شود، این کار را می توان با دستور زیر انجام داد:
ansible-playbook -i inventory ./playbook.yml -l "staging"
گزینه دیگر این است که فایل های موجودی جداگانه برای گروه های مختلف داشته باشید. یا اگر میزبان های مختلف زیادی دارید، می توانید این دو رویکرد را با هم ترکیب کنید.
بیایید به راه اندازی nginx برگردیم. پس از کپی کردن فایل های پیکربندی، باید از sites_available یک Symlink در sitest_enabled به my_app.conf ایجاد کنیم. و nginx را ریستارت کنید.
... # old code in mail.yml
- name: Create symlink to sites-enabled
file:
src: /etc/nginx/sites-available/my_app.conf
dest: /etc/nginx/sites-enabled/my_app.conf
state: link
- name: restart nginx
service:
name: nginx
state: restarted
همه چیز در اینجا ساده است - دوباره ماژول های قابل تشخیص با یک نحو نسبتاً استاندارد. اما یک نکته وجود دارد. راه اندازی مجدد nginx هر بار هیچ فایده ای ندارد. آیا توجه کرده اید که ما دستوراتی مانند "این را اینگونه انجام دهید" نمی نویسیم، نحو بیشتر شبیه "این باید این حالت را داشته باشد". و اغلب این دقیقاً نحوه عملکرد ansible است. اگر گروه از قبل وجود داشته باشد، یا بسته سیستم قبلاً نصب شده باشد، ansible این مورد را بررسی کرده و از کار صرفنظر می کند. همچنین، اگر فایلها کاملاً با آنچه قبلاً در سرور وجود دارد مطابقت داشته باشند، کپی نمیشوند. فقط در صورتی که فایل های پیکربندی تغییر کرده باشند، می توانیم از این مزیت استفاده کرده و nginx را مجددا راه اندازی کنیم. یک دستورالعمل ثبت برای این وجود دارد:
# Copy nginx configs and start it
- name: enable service nginx and start
systemd:
name: nginx
state: started
enabled: yes
- name: Copy the nginx.conf
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
register: restart_nginx
- name: Copy template my_app.conf
template:
src: my_app_conf.j2
dest: /etc/nginx/sites-available/my_app.conf
owner: root
group: root
mode: '0644'
register: restart_nginx
- name: Create symlink to sites-enabled
file:
src: /etc/nginx/sites-available/my_app.conf
dest: /etc/nginx/sites-enabled/my_app.conf
state: link
- name: restart nginx
service:
name: nginx
state: restarted
when: restart_nginx.changed
اگر یکی از فایل های پیکربندی تغییر کند، یک کپی ایجاد می شود و متغیر ثبت می شود restart_nginx
. و تنها در صورتی که این متغیر ثبت شده باشد سرویس مجددا راه اندازی می شود.
و البته باید نقش nginx را به playbook اصلی اضافه کنید.
راه اندازی postgresql
ما باید postgresql را با استفاده از systemd به همان روشی که با nginx انجام دادیم فعال کنیم و همچنین یک کاربر ایجاد کنیم که از آن برای دسترسی به پایگاه داده و خود پایگاه داده استفاده کنیم.
بیایید یک نقش بسازیم /ansible/roles/postgresql/tasks/main.yml
:
# Create user in postgresql
- name: enable postgresql and start
systemd:
name: postgresql
state: started
enabled: yes
- name: Create database user
become_user: postgres
postgresql_user:
name: "{{ db_user }}"
password: "{{ db_password }}"
role_attr_flags: SUPERUSER
- name: Create database
become_user: postgres
postgresql_db:
name: "{{ db_name }}"
encoding: UTF-8
owner: "{{ db_user }}"
من نحوه اضافه کردن متغیرها به موجودی را توضیح نمی دهم، این قبلاً بارها انجام شده است، و همچنین نحو ماژول های postgresql_db و postgresql_user. اطلاعات بیشتر را می توان در مستندات یافت. جالب ترین دستورالعمل اینجاست become_user: postgres
. واقعیت این است که به طور پیش فرض، فقط کاربر postgres به پایگاه داده postgresql و فقط به صورت محلی دسترسی دارد. این دستورالعمل به ما اجازه می دهد تا دستورات را از طرف این کاربر اجرا کنیم (البته در صورت دسترسی).
همچنین، ممکن است مجبور شوید یک خط به pg_hba.conf اضافه کنید تا یک کاربر جدید به پایگاه داده دسترسی داشته باشد. این را می توان به همان روشی که پیکربندی nginx را تغییر دادیم انجام داد.
و البته، باید نقش postgresql را به playbook اصلی اضافه کنید.
نصب روبی از طریق rbenv
Ansible ماژول هایی برای کار با rbenv ندارد، اما با شبیه سازی یک مخزن git نصب می شود. بنابراین، این مشکل به غیر استانداردترین مشکل تبدیل می شود. بیایید برای او نقش بسازیم /ansible/roles/ruby_rbenv/main.yml
و بیایید شروع به پر کردن آن کنیم:
# Install rbenv and ruby
- name: Install rbenv
become_user: "{{ user }}"
git: repo=https://github.com/rbenv/rbenv.git dest=~/.rbenv
ما دوباره از دستورالعمل be_user استفاده می کنیم تا تحت کاربری که برای این اهداف ایجاد کرده ایم کار کنیم. از آنجایی که rbenv در دایرکتوری اصلی خود نصب شده است و نه به صورت جهانی. و همچنین از ماژول git برای کلون کردن مخزن استفاده می کنیم و repo و dest را مشخص می کنیم.
بعد باید rbenv init را در bashrc ثبت کنیم و rbenv را در آنجا به PATH اضافه کنیم. برای این ما ماژول lineinfile را داریم:
- name: Add rbenv to PATH
become_user: "{{ user }}"
lineinfile:
path: ~/.bashrc
state: present
line: 'export PATH="${HOME}/.rbenv/bin:${PATH}"'
- name: Add rbenv init to bashrc
become_user: "{{ user }}"
lineinfile:
path: ~/.bashrc
state: present
line: 'eval "$(rbenv init -)"'
سپس باید ruby_build را نصب کنید:
- name: Install ruby-build
become_user: "{{ user }}"
git: repo=https://github.com/rbenv/ruby-build.git dest=~/.rbenv/plugins/ruby-build
و در نهایت ruby را نصب کنید. این کار از طریق rbenv انجام می شود، یعنی به سادگی با دستور bash:
- name: Install ruby
become_user: "{{ user }}"
shell: |
export PATH="${HOME}/.rbenv/bin:${PATH}"
eval "$(rbenv init -)"
rbenv install {{ ruby_version }}
args:
executable: /bin/bash
می گوییم کدام دستور را و با چه چیزی اجرا کنیم. با این حال، در اینجا با این واقعیت مواجه می شویم که ansible کد موجود در bashrc را قبل از اجرای دستورات اجرا نمی کند. این بدان معنی است که rbenv باید مستقیماً در همان اسکریپت تعریف شود.
مشکل بعدی به این دلیل است که دستور shell از نظر قابل تشخیص حالت ندارد. یعنی هیچ بررسی خودکاری وجود نخواهد داشت که آیا این نسخه از ruby نصب شده است یا خیر. ما خودمان می توانیم این کار را انجام دهیم:
- name: Install ruby
become_user: "{{ user }}"
shell: |
export PATH="${HOME}/.rbenv/bin:${PATH}"
eval "$(rbenv init -)"
if ! rbenv versions | grep -q {{ ruby_version }}
then rbenv install {{ ruby_version }} && rbenv global {{ ruby_version }}
fi
args:
executable: /bin/bash
تنها چیزی که باقی می ماند نصب باندلر است:
- name: Install bundler
become_user: "{{ user }}"
shell: |
export PATH="${HOME}/.rbenv/bin:${PATH}"
eval "$(rbenv init -)"
gem install bundler
و دوباره، نقش ruby_rbenv را به playbook اصلی اضافه کنید.
فایل های مشترک
به طور کلی، تنظیمات را می توان در اینجا تکمیل کرد. بعد، تنها چیزی که باقی می ماند اجرای capistrano است و خود کد را کپی می کند، دایرکتوری های لازم را ایجاد می کند و برنامه را راه اندازی می کند (اگر همه چیز به درستی پیکربندی شده باشد). با این حال، capistrano اغلب به فایل های پیکربندی اضافی نیاز دارد، مانند database.yml
یا .env
آنها را می توان درست مانند فایل ها و قالب های nginx کپی کرد. فقط یک نکته ظریف وجود دارد. قبل از کپی کردن فایل ها، باید یک ساختار دایرکتوری برای آنها ایجاد کنید، چیزی شبیه به این:
# Copy shared files for deploy
- name: Ensure shared dir
become_user: "{{ user }}"
file:
path: "{{ app_path }}/shared/config"
state: directory
ما فقط یک دایرکتوری را مشخص می کنیم و ansible در صورت لزوم به طور خودکار دایرکتوری های والد را ایجاد می کند.
خرک Ansible
ما قبلاً با این واقعیت روبرو شده ایم که متغیرها می توانند حاوی داده های مخفی مانند رمز عبور کاربر باشند. اگر ایجاد کرده اید .env
فایل برای برنامه، و database.yml
پس باید حتی بیشتر از این داده های حیاتی وجود داشته باشد. خوب است که آنها را از چشمان کنجکاو پنهان کنید. برای این منظور استفاده می شود
بیایید یک فایل برای متغیرها ایجاد کنیم /ansible/vars/all.yml
(در اینجا می توانید فایل های مختلفی را برای گروه های مختلف میزبان ایجاد کنید، درست مانند فایل موجودی: production.yml، staging.yml، و غیره).
تمام متغیرهایی که باید رمزگذاری شوند باید با استفاده از نحو استاندارد yml به این فایل منتقل شوند:
# System vars
user_password: 123qweasd
db_password: 123qweasd
# ENV vars
aws_access_key_id: xxxxx
aws_secret_access_key: xxxxxx
aws_bucket: bucket_name
rails_secret_key_base: very_secret_key_base
پس از آن می توان این فایل را با دستور زیر رمزگذاری کرد:
ansible-vault encrypt ./vars/all.yml
به طور طبیعی، هنگام رمزگذاری، باید یک رمز عبور برای رمزگشایی تعیین کنید. پس از فراخوانی این دستور، می توانید ببینید که چه چیزی در داخل فایل خواهد بود.
با استفاده از ansible-vault decrypt
فایل را می توان رمزگشایی، اصلاح و سپس دوباره رمزگذاری کرد.
برای کار کردن، نیازی به رمزگشایی فایل ندارید. شما آن را به صورت رمزگذاری شده ذخیره می کنید و playbook را با آرگومان اجرا می کنید --ask-vault-pass
. Ansible رمز عبور را می خواهد، متغیرها را بازیابی می کند و وظایف را اجرا می کند. تمام داده ها رمزگذاری شده باقی می مانند.
دستور کامل برای چندین گروه از هاست و ansible vault چیزی شبیه به این خواهد بود:
ansible-playbook -i inventory ./playbook.yml -l "staging" --ask-vault-pass
اما متن کامل کتابهای بازی و نقشها را به شما نمیدهم، خودتان بنویسید. زیرا ansible چنین است - اگر نمیدانید چه کاری باید انجام شود، آن را برای شما انجام نمیدهد.
منبع: www.habr.com