منذ وقت ليس ببعيد كنت بحاجة إلى كتابة العديد من أدلة التشغيل Ansible لإعداد الخادم لنشر تطبيق Rails. والمثير للدهشة أنني لم أجد دليلًا بسيطًا خطوة بخطوة. لم أكن أرغب في تقليد قواعد اللعبة الخاصة بشخص آخر دون فهم ما كان يحدث، وفي النهاية كان عليّ قراءة الوثائق، وجمع كل شيء بنفسي. ربما يمكنني مساعدة شخص ما في تسريع هذه العملية بمساعدة هذه المقالة.
أول شيء يجب أن تفهمه هو أن ansible يوفر لك واجهة ملائمة لتنفيذ قائمة محددة مسبقًا من الإجراءات على خادم (خوادم) بعيد عبر SSH. لا يوجد سحر هنا، فلا يمكنك تثبيت مكون إضافي والحصول على نشر بدون توقف لتطبيقك مع عامل الإرساء والمراقبة والأشياء الجيدة الأخرى خارج الصندوق. من أجل كتابة كتاب قواعد اللعبة، يجب أن تعرف ما تريد القيام به بالضبط وكيفية القيام بذلك. ولهذا السبب فأنا غير راضٍ عن كتيبات اللعب الجاهزة من GitHub، أو مقالات مثل: "انسخ وشغل، ستنجح".
ماذا نحتاج؟
كما قلت من قبل، لكي تكتب كتاب قواعد اللعبة، عليك أن تعرف ما تريد القيام به وكيفية القيام بذلك. دعونا نقرر ما نحتاج إليه. بالنسبة لتطبيق Rails، سنحتاج إلى العديد من حزم النظام: nginx، وpostgresql (redis، وما إلى ذلك). وبالإضافة إلى ذلك، نحن بحاجة إلى نسخة محددة من روبي. من الأفضل تثبيته عبر rbenv (rvm، asdf...). يعد تشغيل كل هذا كمستخدم جذري دائمًا فكرة سيئة، لذلك تحتاج إلى إنشاء مستخدم منفصل وتكوين حقوقه. بعد ذلك، تحتاج إلى تحميل الكود الخاص بنا إلى الخادم، ونسخ تكوينات nginx، وpostgres، وما إلى ذلك، وبدء تشغيل كل هذه الخدمات.
ونتيجة لذلك، فإن تسلسل الإجراءات هو كما يلي:
- تسجيل الدخول كجذر
- تثبيت حزم النظام
- إنشاء مستخدم جديد، وتكوين الحقوق، ومفتاح ssh
- تكوين حزم النظام (nginx وما إلى ذلك) وتشغيلها
- نقوم بإنشاء مستخدم في قاعدة البيانات (يمكنك إنشاء قاعدة بيانات على الفور)
- تسجيل الدخول كمستخدم جديد
- تثبيت rbenv وروبي
- تثبيت المجمع
- تحميل كود التطبيق
- إطلاق خادم بوما
علاوة على ذلك، يمكن تنفيذ المراحل الأخيرة باستخدام capistrano، على الأقل خارج الصندوق، يمكنه نسخ التعليمات البرمجية إلى أدلة الإصدار، وتبديل الإصدار باستخدام رابط رمزي عند النشر الناجح، ونسخ التكوينات من دليل مشترك، وإعادة تشغيل puma، وما إلى ذلك. كل هذا يمكن القيام به باستخدام 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 إنها لا تعرف أي مضيفين يطابقون القائمة بأكملها. يجب أن تكون مدرجة في خاص
لنقم بإنشائه في نفس الدليل غير المعقول:
123.123.123.123
هذه هي الطريقة التي نحدد بها المضيف ببساطة (من الأفضل أن يكون مضيف VPS الخاص بنا للاختبار، أو يمكنك تسجيل مضيف محلي) وحفظه تحت الاسم inventory
.
يمكنك تجربة تشغيل ansible باستخدام ملف الجرد:
ansible-playbook ./playbook.yml -i inventory
PLAY [Simple Playbook] ************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************
PLAY RECAP ************************************************************************************************************************************
إذا كان لديك وصول ssh إلى المضيف المحدد، فسيقوم ansible بالاتصال وجمع المعلومات حول النظام البعيد. (المهمة الافتراضية [جمع الحقائق]) وبعد ذلك ستقدم تقريرًا قصيرًا عن التنفيذ (PLAY RECAP).
افتراضيًا، يستخدم الاتصال اسم المستخدم الذي قمت بتسجيل الدخول من خلاله إلى النظام. على الأرجح لن يكون على المضيف. في ملف دليل التشغيل، يمكنك تحديد المستخدم الذي سيتم استخدامه للاتصال باستخدام توجيه Remote_user. كما أن المعلومات المتعلقة بالنظام البعيد قد تكون في كثير من الأحيان غير ضرورية بالنسبة لك ويجب ألا تضيع الوقت في جمعها. يمكن أيضًا تعطيل هذه المهمة:
---
- name: Simple playbook
hosts: all
remote_user: root
become: true
gather_facts: no
حاول تشغيل قواعد اللعبة مرة أخرى وتأكد من أن الاتصال يعمل. (إذا قمت بتحديد المستخدم الجذر، فأنت بحاجة أيضًا إلى تحديد التوجيه أصبح: صحيحًا للحصول على حقوق مرتفعة. كما هو مكتوب في الوثائق: become set to ‘true’/’yes’ to activate privilege escalation.
على الرغم من أنه ليس من الواضح تمامًا السبب).
ربما ستتلقى خطأً ناجمًا عن عدم قدرة ansible على تحديد مترجم Python، ومن ثم يمكنك تحديده يدويًا:
ansible_python_interpreter: /usr/bin/python3
يمكنك معرفة مكان وجود بايثون باستخدام الأمر whereis python
.
تثبيت حزم النظام
يتضمن التوزيع القياسي لـ Ansible العديد من الوحدات للعمل مع حزم النظام المختلفة، لذلك لا يتعين علينا كتابة نصوص bash لأي سبب من الأسباب. نحتاج الآن إلى إحدى هذه الوحدات لتحديث النظام وتثبيت حزم النظام. لدي Ubuntu Linux على 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
المهمة هي بالضبط المهمة التي سيؤديها Ansible على الخوادم البعيدة. نعطي المهمة اسمًا حتى نتمكن من تتبع تنفيذها في السجل. ونحن نصف، باستخدام بناء جملة وحدة معينة، ما يجب القيام به. في هذه الحالة apt: update_cache=yes
- يقول لتحديث حزم النظام باستخدام الوحدة النمطية apt. الأمر الثاني أكثر تعقيدًا بعض الشيء. نقوم بتمرير قائمة الحزم إلى الوحدة apt ونقول أنها كذلك state
ينبغي أن تصبح present
أي أننا نقول تثبيت هذه الحزم. وبطريقة مماثلة، يمكننا أن نطلب منهم حذفها، أو تحديثها بمجرد التغيير state
. يرجى ملاحظة أنه لكي تعمل القضبان مع postgresql، نحتاج إلى حزمة postgresql-contrib، التي نقوم بتثبيتها الآن. مرة أخرى، عليك أن تعرف هذا وتفعله، لأن insible لن يفعل هذا من تلقاء نفسه.
حاول تشغيل قواعد اللعبة مرة أخرى وتأكد من تثبيت الحزم.
إنشاء مستخدمين جدد.
للعمل مع المستخدمين، يحتوي Ansible أيضًا على وحدة نمطية - user. دعونا نضيف مهمة أخرى (لقد قمت بإخفاء الأجزاء المعروفة بالفعل من دليل اللعب خلف التعليقات حتى لا أقوم بنسخها بالكامل في كل مرة):
---
- name: Simple playbook
# ...
tasks:
# ...
- name: Add a new user
user:
name: my_user
shell: /bin/bash
password: "{{ 123qweasd | password_hash('sha512') }}"
نقوم بإنشاء مستخدم جديد، وتعيين Schell وكلمة المرور له. ومن ثم نواجه عدة مشاكل. ماذا لو كانت أسماء المستخدمين يجب أن تكون مختلفة بالنسبة للمضيفين المختلفين؟ وتخزين كلمة المرور بنص واضح في قواعد اللعبة يعد فكرة سيئة للغاية. في البداية، دعونا نضع اسم المستخدم وكلمة المرور في متغيرات، وفي نهاية المقالة سأوضح كيفية تشفير كلمة المرور.
---
- 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
(الرئيسي هو الملف الرئيسي الذي سيتم تحميله وتنفيذه عندما يكون الدور متصلاً بدليل التشغيل؛ ويمكن توصيل ملفات الأدوار الأخرى به). يمكنك الآن نقل جميع المهام المتعلقة بالمستخدم إلى هذا الملف:
# 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
في دليل التشغيل الرئيسي، يجب عليك تحديد كيفية استخدام دور المستخدم:
---
- 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 في 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
إذا أطلقنا الآن قواعد اللعبة الخاصة بنا، فسوف تؤدي المهام المحددة لكلا المضيفين. ولكن في الوقت نفسه، بالنسبة للمضيف المرحلي، ستكون المتغيرات مختلفة عن متغيرات الإنتاج، وليس فقط في الأدوار وقواعد اللعب، ولكن أيضًا في تكوينات nginx. {{ inventory_hostname }}
لا تحتاج إلى أن تكون محددة في ملف المخزون - هذا
إذا كنت تريد أن يكون لديك ملف مخزون لعدة مضيفين، ولكنك تريد تشغيله لمجموعة واحدة فقط، فيمكن القيام بذلك باستخدام الأمر التالي:
ansible-playbook -i inventory ./playbook.yml -l "staging"
خيار آخر هو أن يكون لديك ملفات مخزون منفصلة لمجموعات مختلفة. أو يمكنك الجمع بين الطريقتين إذا كان لديك العديد من المضيفين المختلفين.
دعنا نعود إلى إعداد nginx. بعد نسخ ملفات التكوين، نحتاج إلى إنشاء رابط رمزي في sitest_enabled إلى my_app.conf من sites_available. وأعد تشغيل 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 إلى قواعد اللعبة الرئيسية.
إعداد 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 إلى قواعد اللعبة الرئيسية.
تثبيت روبي عبر 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
نستخدم مرة أخرى التوجيه أصبح_مستخدمًا للعمل تحت إشراف المستخدم الذي أنشأناه لهذه الأغراض. نظرًا لأن rbenv مثبت في دليله الرئيسي، وليس عالميًا. ونستخدم أيضًا وحدة git لاستنساخ المستودع، وتحديد الريبو والوجهة.
بعد ذلك، نحتاج إلى تسجيل 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
وأخيرا تثبيت روبي. ويتم ذلك من خلال 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 مباشرة في نفس البرنامج النصي.
المشكلة التالية ترجع إلى حقيقة أن أمر الصدفة ليس له حالة من وجهة نظر غير معقولة. أي أنه لن يكون هناك فحص تلقائي لمعرفة ما إذا كان هذا الإصدار من روبي مثبتًا أم لا. يمكننا أن نفعل هذا بأنفسنا:
- 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 إلى قواعد اللعبة الرئيسية.
الملفات المشتركة.
بشكل عام، يمكن إكمال الإعداد هنا. بعد ذلك، كل ما تبقى هو تشغيل 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 تلقائيًا بإنشاء المجلدات الرئيسية إذا لزم الأمر.
أنسبل قبو
لقد واجهنا بالفعل حقيقة أن المتغيرات يمكن أن تحتوي على بيانات سرية مثل كلمة مرور المستخدم. إذا كنت قد خلقت .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
يمكن فك تشفير الملف وتعديله ثم تشفيره مرة أخرى.
لا تحتاج إلى فك تشفير الملف للعمل. يمكنك تخزينه مشفرًا وتشغيل قواعد اللعبة باستخدام الوسيطة --ask-vault-pass
. سيطلب Ansible كلمة المرور، وسيقوم باسترداد المتغيرات، وتنفيذ المهام. ستبقى جميع البيانات مشفرة.
سيبدو الأمر الكامل لعدة مجموعات من المضيفين وansible vault كما يلي:
ansible-playbook -i inventory ./playbook.yml -l "staging" --ask-vault-pass
لكنني لن أعطيك النص الكامل لكتب اللعب والأدوار، فاكتبه بنفسك. لأن ansible هكذا - إذا لم تفهم ما يجب عليك فعله، فلن يقوم بذلك نيابةً عنك.
المصدر: www.habr.com