Menyediakan pelayan untuk menggunakan aplikasi Rails menggunakan Ansible

Tidak lama dahulu saya perlu menulis beberapa buku permainan Ansible untuk menyediakan pelayan untuk menggunakan aplikasi Rails. Dan, yang menghairankan, saya tidak menemui manual langkah demi langkah yang mudah. Saya tidak mahu menyalin buku permainan orang lain tanpa memahami apa yang berlaku, dan akhirnya saya terpaksa membaca dokumentasi itu, mengumpul segala-galanya sendiri. Mungkin saya boleh membantu seseorang mempercepatkan proses ini dengan bantuan artikel ini.

Perkara pertama yang perlu difahami ialah ansible memberikan anda antara muka yang mudah untuk melaksanakan senarai tindakan yang dipratentukan pada pelayan jauh melalui SSH. Tiada keajaiban di sini, anda tidak boleh memasang pemalam dan mendapatkan penggunaan sifar masa henti bagi aplikasi anda dengan docker, pemantauan dan barangan lain di luar kotak. Untuk menulis buku permainan, anda mesti tahu apa sebenarnya yang anda mahu lakukan dan bagaimana untuk melakukannya. Itulah sebabnya saya tidak berpuas hati dengan buku permainan siap sedia daripada GitHub, atau artikel seperti: "Salin dan jalankan, ia akan berfungsi."

Apa yang kita perlukan?

Seperti yang telah saya katakan, untuk menulis buku permainan anda perlu tahu apa yang anda mahu lakukan dan bagaimana untuk melakukannya. Mari kita tentukan apa yang kita perlukan. Untuk aplikasi Rails kita memerlukan beberapa pakej sistem: nginx, postgresql (redis, dll). Di samping itu, kami memerlukan versi ruby ​​​​yang khusus. Lebih baik memasangnya melalui rbenv (rvm, asdf...). Menjalankan semua ini sebagai pengguna root sentiasa idea yang tidak baik, jadi anda perlu membuat pengguna yang berasingan dan mengkonfigurasi haknya. Selepas ini, anda perlu memuat naik kod kami ke pelayan, salin konfigurasi untuk nginx, postgres, dll dan mulakan semua perkhidmatan ini.

Akibatnya, urutan tindakan adalah seperti berikut:

  1. Log masuk sebagai root
  2. memasang pakej sistem
  3. buat pengguna baharu, konfigurasikan hak, kunci ssh
  4. konfigurasikan pakej sistem (nginx dll) dan jalankannya
  5. Kami mencipta pengguna dalam pangkalan data (anda boleh segera mencipta pangkalan data)
  6. Log masuk sebagai pengguna baharu
  7. Pasang rbenv dan ruby
  8. Memasang pengikat
  9. Memuat naik kod aplikasi
  10. Melancarkan pelayan Puma

Lebih-lebih lagi, peringkat terakhir boleh dilakukan menggunakan capistrano, sekurang-kurangnya di luar kotak ia boleh menyalin kod ke dalam direktori keluaran, menukar keluaran dengan symlink apabila penggunaan berjaya, menyalin konfigurasi dari direktori yang dikongsi, mulakan semula puma, dsb. Semua ini boleh dilakukan menggunakan Ansible, tetapi mengapa?

Struktur fail

Ansible mempunyai ketat struktur fail untuk semua fail anda, jadi sebaiknya simpan semuanya dalam direktori yang berasingan. Selain itu, ia tidak begitu penting sama ada ia akan berada dalam aplikasi rel itu sendiri, atau secara berasingan. Anda boleh menyimpan fail dalam repositori git yang berasingan. Secara peribadi, saya mendapati paling mudah untuk mencipta direktori yang boleh digunakan dalam direktori /config bagi aplikasi rel dan menyimpan segala-galanya dalam satu repositori.

Buku Main Mudah

Playbook ialah fail yml yang, menggunakan sintaks khas, menerangkan perkara yang perlu dilakukan oleh Ansible dan caranya. Mari buat buku main pertama yang tidak melakukan apa-apa:

---
- name: Simple playbook
  hosts: all

Di sini kami hanya mengatakan bahawa buku permainan kami dipanggil Simple Playbook dan kandungannya harus dilaksanakan untuk semua hos. Kita boleh menyimpannya dalam direktori /ansible dengan nama playbook.yml dan cuba jalankan:

ansible-playbook ./playbook.yml

PLAY [Simple Playbook] ************************************************************************************************************************************
skipping: no hosts matched

Ansible mengatakan ia tidak mengetahui mana-mana hos yang sepadan dengan semua senarai. Mereka mesti disenaraikan dalam satu khas fail inventori.

Mari kita buat dalam direktori ansible yang sama:

123.123.123.123

Beginilah cara kami hanya menentukan hos (sebaik-baiknya hos VPS kami untuk ujian, atau anda boleh mendaftar localhost) dan simpan di bawah nama inventory.
Anda boleh cuba menjalankan ansible dengan fail inventori:

ansible-playbook ./playbook.yml -i inventory
PLAY [Simple Playbook] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************

PLAY RECAP ************************************************************************************************************************************

Jika anda mempunyai akses ssh kepada hos yang ditentukan, maka ansible akan menyambung dan mengumpul maklumat tentang sistem jauh. (TASK lalai [Mengumpul Fakta]) selepas itu ia akan memberikan laporan ringkas tentang pelaksanaan (PLAY RECAP).

Secara lalai, sambungan menggunakan nama pengguna di mana anda log masuk ke dalam sistem. Kemungkinan besar ia tidak akan berada pada hos. Dalam fail playbook, anda boleh menentukan pengguna yang hendak digunakan untuk menyambung menggunakan arahan remote_user. Selain itu, maklumat tentang sistem jauh mungkin selalunya tidak diperlukan kepada anda dan anda tidak sepatutnya membuang masa untuk mengumpulnya. Tugas ini juga boleh dilumpuhkan:

---
- name: Simple playbook
  hosts: all
  remote_user: root
  become: true
  gather_facts: no

Cuba jalankan buku main sekali lagi dan pastikan sambungan berfungsi. (Jika anda menentukan pengguna akar, maka anda juga perlu menentukan menjadi: arahan benar untuk mendapatkan hak yang dinaikkan. Seperti yang tertulis dalam dokumentasi: become set to β€˜true’/’yes’ to activate privilege escalation. walaupun tidak sepenuhnya jelas mengapa).

Mungkin anda akan menerima ralat yang disebabkan oleh fakta bahawa ansible tidak dapat menentukan penterjemah Python, maka anda boleh menentukannya secara manual:

ansible_python_interpreter: /usr/bin/python3 

Anda boleh mengetahui di mana anda mempunyai python dengan arahan whereis python.

Memasang pakej sistem

Pengedaran standard Ansible termasuk banyak modul untuk bekerja dengan pelbagai pakej sistem, jadi kami tidak perlu menulis skrip bash untuk sebarang sebab. Kini kami memerlukan salah satu daripada modul ini untuk mengemas kini sistem dan memasang pakej sistem. Saya mempunyai Ubuntu Linux pada VPS saya, jadi untuk memasang pakej yang saya gunakan apt-get ΠΈ modul untuknya. Jika anda menggunakan sistem pengendalian yang berbeza, maka anda mungkin memerlukan modul yang berbeza (ingat, saya katakan pada mulanya bahawa kita perlu tahu terlebih dahulu apa dan bagaimana kita akan lakukan). Walau bagaimanapun, sintaks kemungkinan besar akan serupa.

Mari tambahkan buku permainan kami dengan tugas pertama:

---
- 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

Tugasan ialah tugas yang Ansible akan lakukan pada pelayan jauh. Kami memberikan nama tugas supaya kami boleh menjejaki pelaksanaannya dalam log. Dan kami menerangkan, menggunakan sintaks modul tertentu, perkara yang perlu dilakukan. Dalam kes ini apt: update_cache=yes - berkata untuk mengemas kini pakej sistem menggunakan modul apt. Perintah kedua adalah sedikit lebih rumit. Kami menghantar senarai pakej ke modul apt dan mengatakan bahawa ia adalah state sepatutnya menjadi present, iaitu, kita katakan pasang pakej ini. Dengan cara yang sama, kami boleh memberitahu mereka untuk memadamkannya, atau mengemas kininya dengan hanya menukar state. Sila ambil perhatian bahawa untuk rel berfungsi dengan postgresql kami memerlukan pakej postgresql-contrib, yang kami pasang sekarang. Sekali lagi, anda perlu tahu dan melakukan ini; ansible sendiri tidak akan melakukan ini.

Cuba jalankan buku main sekali lagi dan semak bahawa pakej telah dipasang.

Mencipta pengguna baharu.

Untuk bekerja dengan pengguna, Ansible juga mempunyai modul - pengguna. Mari tambah satu lagi tugasan (saya menyembunyikan bahagian buku main yang sudah diketahui di sebalik komen supaya tidak menyalinnya sepenuhnya setiap kali):

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Add a new user
      user:
        name: my_user
        shell: /bin/bash
        password: "{{ 123qweasd | password_hash('sha512') }}"

Kami mencipta pengguna baharu, menetapkan schell dan kata laluan untuknya. Dan kemudian kita menghadapi beberapa masalah. Bagaimana jika nama pengguna perlu berbeza untuk hos yang berbeza? Dan menyimpan kata laluan dalam teks yang jelas dalam buku permainan adalah idea yang sangat buruk. Sebagai permulaan, mari letakkan nama pengguna dan kata laluan ke dalam pembolehubah, dan menjelang akhir artikel saya akan menunjukkan cara untuk menyulitkan kata laluan.

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Add a new user
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"

Pembolehubah ditetapkan dalam buku permainan menggunakan pendakap kerinting berkembar.

Kami akan menunjukkan nilai pembolehubah dalam fail inventori:

123.123.123.123

[all:vars]
user=my_user
user_password=123qweasd

Sila ambil perhatian arahan [all:vars] - ia mengatakan bahawa blok teks seterusnya ialah pembolehubah (var) dan ia boleh digunakan untuk semua hos (semua).

Reka bentuknya juga menarik "{{ user_password | password_hash('sha512') }}". Masalahnya ialah ansible tidak memasang pengguna melalui user_add seperti anda akan melakukannya secara manual. Dan ia menyimpan semua data secara langsung, itulah sebabnya kita juga mesti menukar kata laluan menjadi cincang terlebih dahulu, itulah yang dilakukan oleh arahan ini.

Mari tambah pengguna kami ke kumpulan sudo. Walau bagaimanapun, sebelum ini kita perlu memastikan kumpulan sedemikian wujud kerana tiada siapa yang akan melakukan ini untuk kita:

---
- 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"

Semuanya agak mudah, kami juga mempunyai modul kumpulan untuk membuat kumpulan, dengan sintaks yang hampir sama dengan apt. Maka sudah cukup untuk mendaftarkan kumpulan ini kepada pengguna (groups: "sudo").
Ia juga berguna untuk menambah kunci ssh kepada pengguna ini supaya kami boleh log masuk menggunakannya tanpa kata laluan:

---
- 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

Dalam kes ini, reka bentuknya menarik "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" β€” ia menyalin kandungan fail id_rsa.pub (nama anda mungkin berbeza), iaitu bahagian awam kunci ssh dan memuat naiknya ke senarai kunci yang dibenarkan untuk pengguna pada pelayan.

Peranan

Ketiga-tiga tugasan untuk mencipta penggunaan dengan mudah boleh diklasifikasikan kepada satu kumpulan tugasan, dan adalah idea yang baik untuk menyimpan kumpulan ini secara berasingan daripada buku main utama supaya ia tidak membesar terlalu besar. Untuk tujuan ini, Ansible telah peranan.
Mengikut struktur fail yang ditunjukkan pada awalnya, peranan mesti diletakkan dalam direktori peranan yang berasingan, untuk setiap peranan terdapat direktori berasingan dengan nama yang sama, di dalam direktori tugas, fail, templat, dll.
Mari buat struktur fail: ./ansible/roles/user/tasks/main.yml (utama ialah fail utama yang akan dimuatkan dan dilaksanakan apabila peranan disambungkan ke buku main; fail peranan lain boleh disambungkan kepadanya). Kini anda boleh memindahkan semua tugas yang berkaitan dengan pengguna ke fail ini:

# 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

Dalam buku main utama, anda mesti menentukan untuk menggunakan peranan pengguna:

---
- 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

Juga, mungkin masuk akal untuk mengemas kini sistem sebelum semua tugas lain; untuk melakukan ini, anda boleh menamakan semula blok tersebut tasks di mana ia ditakrifkan dalam pre_tasks.

Menyediakan nginx

Kita sepatutnya sudah memasang Nginx; kita perlu mengkonfigurasinya dan menjalankannya. Mari kita lakukan dengan segera dalam peranan. Mari buat struktur fail:

- ansible
  - roles
    - nginx
      - files
      - tasks
        - main.yml
      - templates

Sekarang kami memerlukan fail dan templat. Perbezaan di antara mereka adalah bahawa ansible menyalin fail secara langsung, sebagaimana adanya. Dan templat mesti mempunyai sambungan j2 dan ia boleh menggunakan nilai pembolehubah menggunakan pendakap kerinting berganda yang sama.

Mari dayakan nginx masuk main.yml fail. Untuk ini kami mempunyai modul systemd:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

Di sini kami bukan sahaja mengatakan bahawa nginx mesti dimulakan (iaitu, kami melancarkannya), tetapi kami segera mengatakan bahawa ia mesti didayakan.
Sekarang mari salin fail konfigurasi:

# 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'

Kami mencipta fail konfigurasi nginx utama (anda boleh mengambilnya terus dari pelayan, atau menulisnya sendiri). Dan juga fail konfigurasi untuk aplikasi kami dalam direktori sites_available (ini tidak perlu tetapi berguna). Dalam kes pertama, kami menggunakan modul salinan untuk menyalin fail (fail mesti dalam /ansible/roles/nginx/files/nginx.conf). Pada yang kedua, kami menyalin templat, menggantikan nilai pembolehubah. Templat hendaklah dalam /ansible/roles/nginx/templates/my_app.j2). Dan ia mungkin kelihatan seperti ini:

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 }};
  ....
}

Beri perhatian kepada sisipan {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} β€” ini adalah semua pembolehubah yang nilainya Ansible akan digantikan ke dalam templat sebelum menyalin. Ini berguna jika anda menggunakan buku main untuk kumpulan hos yang berbeza. Sebagai contoh, kami boleh menambah fail inventori kami:

[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

Jika kami kini melancarkan buku main kami, ia akan melaksanakan tugas yang ditentukan untuk kedua-dua hos. Tetapi pada masa yang sama, untuk hos pementasan, pembolehubah akan berbeza daripada yang pengeluaran, dan bukan sahaja dalam peranan dan buku permainan, tetapi juga dalam konfigurasi nginx. {{ inventory_hostname }} tidak perlu dinyatakan dalam fail inventori - ini pembolehubah ansible khas dan hos yang buku mainnya sedang dijalankan disimpan di sana.
Jika anda ingin mempunyai fail inventori untuk beberapa hos, tetapi hanya dijalankan untuk satu kumpulan, ini boleh dilakukan dengan arahan berikut:

ansible-playbook -i inventory ./playbook.yml -l "staging"

Pilihan lain ialah mempunyai fail inventori yang berasingan untuk kumpulan yang berbeza. Atau anda boleh menggabungkan kedua-dua pendekatan jika anda mempunyai banyak hos yang berbeza.

Mari kita kembali kepada menyediakan nginx. Selepas menyalin fail konfigurasi, kami perlu membuat pautan sym dalam sitest_enabled ke my_app.conf daripada sites_available. Dan mulakan semula 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

Semuanya mudah di sini - sekali lagi modul ansible dengan sintaks yang agak standard. Tetapi ada satu perkara. Tidak ada gunanya memulakan semula nginx setiap kali. Adakah anda perasan bahawa kami tidak menulis arahan seperti: "buat ini seperti ini", sintaksnya kelihatan lebih seperti "ini sepatutnya mempunyai keadaan ini". Dan selalunya beginilah cara ansible berfungsi. Jika kumpulan sudah wujud, atau pakej sistem sudah dipasang, maka ansible akan menyemak ini dan melangkau tugas. Selain itu, fail tidak akan disalin jika ia sepadan sepenuhnya dengan apa yang sudah ada pada pelayan. Kami boleh mengambil kesempatan daripada ini dan mulakan semula nginx hanya jika fail konfigurasi telah diubah. Terdapat arahan daftar untuk ini:

# 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

Jika salah satu fail konfigurasi berubah, salinan akan dibuat dan pembolehubah akan didaftarkan restart_nginx. Dan hanya jika pembolehubah ini telah didaftarkan, perkhidmatan akan dimulakan semula.

Dan, sudah tentu, anda perlu menambah peranan nginx pada buku main utama.

Menyediakan postgresql

Kami perlu mendayakan postgresql menggunakan systemd dengan cara yang sama seperti yang kami lakukan dengan nginx, dan juga mencipta pengguna yang akan kami gunakan untuk mengakses pangkalan data dan pangkalan data itu sendiri.
Mari cipta peranan /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 }}"

Saya tidak akan menerangkan cara menambah pembolehubah pada inventori, ini telah dilakukan berkali-kali, serta sintaks modul postgresql_db dan postgresql_user. Maklumat lanjut boleh didapati dalam dokumentasi. Arahan yang paling menarik di sini ialah become_user: postgres. Hakikatnya ialah secara lalai, hanya pengguna postgres yang mempunyai akses kepada pangkalan data postgresql dan hanya secara tempatan. Arahan ini membolehkan kami melaksanakan arahan bagi pihak pengguna ini (jika kami mempunyai akses, sudah tentu).
Selain itu, anda mungkin perlu menambah baris pada pg_hba.conf untuk membenarkan pengguna baharu mengakses pangkalan data. Ini boleh dilakukan dengan cara yang sama seperti kami menukar konfigurasi nginx.

Dan sudah tentu, anda perlu menambah peranan postgresql pada buku main utama.

Memasang ruby ​​​​melalui rbenv

Ansible tidak mempunyai modul untuk bekerja dengan rbenv, tetapi ia dipasang dengan mengklonkan repositori git. Oleh itu, masalah ini menjadi yang paling tidak standard. Mari kita cipta peranan untuknya /ansible/roles/ruby_rbenv/main.yml dan mari kita mula mengisinya:

# Install rbenv and ruby
- name: Install rbenv
  become_user: "{{ user }}"
  git: repo=https://github.com/rbenv/rbenv.git dest=~/.rbenv

Kami sekali lagi menggunakan arahan become_user untuk bekerja di bawah pengguna yang kami buat untuk tujuan ini. Memandangkan rbenv dipasang dalam direktori rumahnya, dan bukan secara global. Dan kami juga menggunakan modul git untuk mengklon repositori, menyatakan repo dan dest.

Seterusnya, kita perlu mendaftar rbenv init dalam bashrc dan tambah rbenv ke PATH di sana. Untuk ini kami mempunyai modul 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 -)"'

Kemudian anda perlu memasang ruby_build:

- name: Install ruby-build
  become_user: "{{ user }}"
  git: repo=https://github.com/rbenv/ruby-build.git dest=~/.rbenv/plugins/ruby-build

Dan akhirnya pasang ruby. Ini dilakukan melalui rbenv, iaitu, hanya dengan arahan 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

Kami mengatakan perintah yang mana untuk dilaksanakan dan dengan apa. Walau bagaimanapun, di sini kita dapati hakikat bahawa ansible tidak menjalankan kod yang terkandung dalam bashrc sebelum menjalankan arahan. Ini bermakna rbenv perlu ditakrifkan secara langsung dalam skrip yang sama.

Masalah seterusnya adalah disebabkan oleh hakikat bahawa arahan shell tidak mempunyai keadaan dari sudut pandangan yang boleh. Iaitu, tidak akan ada semakan automatik sama ada versi ruby ​​​​ini dipasang atau tidak. Kita boleh melakukannya sendiri:

- 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

Apa yang tinggal ialah memasang pengikat:

- name: Install bundler
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}/.rbenv/bin:${PATH}"
    eval "$(rbenv init -)"
    gem install bundler

Dan sekali lagi, tambahkan ruby_rbenv peranan kami pada buku main utama.

Fail kongsi.

Secara umum, persediaan boleh diselesaikan di sini. Seterusnya, semua yang tinggal ialah menjalankan capistrano dan ia akan menyalin kod itu sendiri, mencipta direktori yang diperlukan dan melancarkan aplikasi (jika semuanya dikonfigurasikan dengan betul). Walau bagaimanapun, capistrano sering memerlukan fail konfigurasi tambahan, seperti database.yml atau .env Mereka boleh disalin sama seperti fail dan templat untuk nginx. Hanya ada satu kehalusan. Sebelum menyalin fail, anda perlu membuat struktur direktori untuknya, seperti ini:

# Copy shared files for deploy
- name: Ensure shared dir
  become_user: "{{ user }}"
  file:
    path: "{{ app_path }}/shared/config"
    state: directory

kami menetapkan hanya satu direktori dan ansible akan membuat direktori induk secara automatik jika perlu.

Bilik Kebal Ansible

Kami telah menemui fakta bahawa pembolehubah boleh mengandungi data rahsia seperti kata laluan pengguna. Jika anda telah mencipta .env fail untuk permohonan itu, dan database.yml maka mesti ada lebih banyak data kritikal seperti itu. Adalah lebih baik untuk menyembunyikannya daripada mengintip. Untuk tujuan ini ia digunakan bilik kebal ansible.

Mari buat fail untuk pembolehubah /ansible/vars/all.yml (di sini anda boleh mencipta fail berbeza untuk kumpulan hos yang berbeza, sama seperti dalam fail inventori: production.yml, staging.yml, dll).
Semua pembolehubah yang mesti disulitkan mesti dipindahkan ke fail ini menggunakan sintaks yml standard:

# 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

Selepas itu fail ini boleh disulitkan dengan arahan:

ansible-vault encrypt ./vars/all.yml

Sememangnya, apabila menyulitkan, anda perlu menetapkan kata laluan untuk penyahsulitan. Anda boleh melihat apa yang akan berada di dalam fail selepas memanggil arahan ini.

Dengan cara ansible-vault decrypt fail itu boleh dinyahsulit, diubah suai dan kemudian disulitkan semula.

Anda tidak perlu menyahsulit fail untuk berfungsi. Anda menyimpannya disulitkan dan menjalankan buku permainan dengan hujah --ask-vault-pass. Ansible akan meminta kata laluan, mendapatkan semula pembolehubah, dan melaksanakan tugas. Semua data akan kekal disulitkan.

Perintah lengkap untuk beberapa kumpulan hos dan bilik kebal ansible akan kelihatan seperti ini:

ansible-playbook -i inventory ./playbook.yml -l "staging" --ask-vault-pass

Tetapi saya tidak akan memberikan anda teks penuh buku permainan dan peranan, tulis sendiri. Kerana ansible adalah seperti itu - jika anda tidak faham apa yang perlu dilakukan, maka ia tidak akan melakukannya untuk anda.

Sumber: www.habr.com

Tambah komen