Thiết lập máy chủ để triển khai ứng dụng Rails bằng Ansible

Cách đây không lâu, tôi cần viết một số playbook Ansible để chuẩn bị cho máy chủ triển khai ứng dụng Rails. Và thật ngạc nhiên, tôi không tìm thấy hướng dẫn từng bước đơn giản. Tôi không muốn sao chép vở kịch của người khác mà không hiểu chuyện gì đang xảy ra, và cuối cùng tôi phải đọc tài liệu, tự mình thu thập mọi thứ. Có lẽ tôi có thể giúp ai đó tăng tốc quá trình này với sự trợ giúp của bài viết này.

Điều đầu tiên cần hiểu là ansible cung cấp cho bạn một giao diện thuận tiện để thực hiện danh sách hành động được xác định trước trên (các) máy chủ từ xa thông qua SSH. Không có phép thuật nào ở đây, bạn không thể cài đặt một plugin và không có thời gian ngừng hoạt động khi triển khai ứng dụng của mình với docker, giám sát và các tính năng khác. Để viết một cẩm nang, bạn phải biết chính xác mình muốn làm gì và làm như thế nào. Đó là lý do tại sao tôi không hài lòng với những playbook làm sẵn từ GitHub, hoặc những bài viết như: “Sao chép và chạy, nó sẽ hoạt động”.

Chúng ta cần gì?

Như tôi đã nói, để viết một cuốn sách bạn cần biết bạn muốn làm gì và làm như thế nào. Hãy quyết định những gì chúng ta cần. Đối với ứng dụng Rails, chúng ta sẽ cần một số gói hệ thống: nginx, postgresql (redis, v.v.). Ngoài ra, chúng tôi cần một phiên bản cụ thể của Ruby. Tốt nhất nên cài qua rbenv (rvm, asdf...). Chạy tất cả những thứ này với tư cách là người dùng root luôn là một ý tưởng tồi, vì vậy bạn cần tạo một người dùng riêng và định cấu hình quyền của người đó. Sau này, bạn cần tải mã của chúng tôi lên máy chủ, sao chép cấu hình cho nginx, postgres, v.v. và khởi động tất cả các dịch vụ này.

Kết quả là, chuỗi hành động như sau:

  1. Đăng nhập bằng root
  2. cài đặt gói hệ thống
  3. tạo người dùng mới, cấu hình quyền, khóa ssh
  4. định cấu hình các gói hệ thống (nginx, v.v.) và chạy chúng
  5. Chúng tôi tạo người dùng trong cơ sở dữ liệu (bạn có thể tạo ngay cơ sở dữ liệu)
  6. Đăng nhập với tư cách người dùng mới
  7. Cài đặt rbenv và ruby
  8. Cài đặt gói
  9. Đang tải lên mã ứng dụng
  10. Ra mắt máy chủ Puma

Hơn nữa, các giai đoạn cuối có thể được thực hiện bằng cách sử dụng capistrano, ít nhất là nó có thể sao chép mã vào thư mục phát hành, chuyển đổi bản phát hành bằng liên kết tượng trưng khi triển khai thành công, sao chép cấu hình từ thư mục dùng chung, khởi động lại puma, v.v. Tất cả điều này có thể được thực hiện bằng Ansible, nhưng tại sao?

Cấu trúc tập tin

Ansible có sự nghiêm ngặt cấu trúc tập tin cho tất cả các tệp của bạn, vì vậy tốt nhất bạn nên giữ tất cả chúng trong một thư mục riêng. Hơn nữa, việc nó nằm trong chính ứng dụng Rails hay riêng biệt cũng không quá quan trọng. Bạn có thể lưu trữ các tập tin trong một kho git riêng biệt. Cá nhân tôi thấy thuận tiện nhất là tạo một thư mục ansible trong thư mục /config của ứng dụng Rails và lưu trữ mọi thứ trong một kho lưu trữ.

Playbook đơn giản

Playbook là một tệp yml, sử dụng cú pháp đặc biệt, mô tả những gì Ansible nên làm và cách thức. Hãy tạo playbook đầu tiên không làm gì cả:

---
- name: Simple playbook
  hosts: all

Ở đây chúng tôi chỉ đơn giản nói rằng playbook của chúng tôi được gọi là Simple Playbook và nội dung của nó phải được thực thi cho tất cả các máy chủ. Chúng ta có thể lưu nó trong thư mục /ansible với tên playbook.yml và cố gắng chạy:

ansible-playbook ./playbook.yml

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

Ansible cho biết họ không biết bất kỳ máy chủ nào phù hợp với danh sách tất cả. Chúng phải được liệt kê trong một danh sách đặc biệt tập tin kiểm kê.

Hãy tạo nó trong cùng thư mục ansible:

123.123.123.123

Đây là cách chúng tôi chỉ định máy chủ (lý tưởng nhất là máy chủ VPS của chúng tôi để thử nghiệm hoặc bạn có thể đăng ký localhost) và lưu nó dưới tên inventory.
Bạn có thể thử chạy ansible với tệp kho lưu trữ:

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

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

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

Nếu bạn có quyền truy cập ssh vào máy chủ được chỉ định thì ansible sẽ kết nối và thu thập thông tin về hệ thống từ xa. (TASK mặc định [Thu thập thông tin]), sau đó nó sẽ đưa ra một báo cáo ngắn về quá trình thực hiện (PLAY RECAP).

Theo mặc định, kết nối sử dụng tên người dùng mà bạn đăng nhập vào hệ thống. Rất có thể nó sẽ không có trên máy chủ. Trong tệp playbook, bạn có thể chỉ định người dùng nào sẽ sử dụng để kết nối bằng lệnh remote_user. Ngoài ra, thông tin về hệ thống từ xa thường có thể không cần thiết đối với bạn và bạn không nên lãng phí thời gian để thu thập nó. Tác vụ này cũng có thể bị vô hiệu hóa:

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

Hãy thử chạy lại playbook và đảm bảo kết nối đang hoạt động. (Nếu bạn đã chỉ định người dùng root thì bạn cũng cần chỉ định lệnh return: true để có được các quyền nâng cao. Như đã viết trong tài liệu: become set to ‘true’/’yes’ to activate privilege escalation. mặc dù không hoàn toàn rõ ràng tại sao).

Có lẽ bạn sẽ gặp lỗi do ansible không thể xác định trình thông dịch Python, khi đó bạn có thể chỉ định thủ công:

ansible_python_interpreter: /usr/bin/python3 

Bạn có thể tìm ra nơi bạn có python bằng lệnh whereis python.

Cài đặt gói hệ thống

Bản phân phối tiêu chuẩn của Ansible bao gồm nhiều mô-đun để làm việc với các gói hệ thống khác nhau, vì vậy chúng ta không cần phải viết các tập lệnh bash vì bất kỳ lý do gì. Bây giờ chúng ta cần một trong những mô-đun này để cập nhật hệ thống và cài đặt các gói hệ thống. Tôi có Ubuntu Linux trên VPS của mình nên để cài đặt các gói tôi sử dụng apt-get и mô-đun cho nó. Nếu bạn đang sử dụng một hệ điều hành khác thì bạn có thể cần một mô-đun khác (hãy nhớ rằng tôi đã nói ngay từ đầu rằng chúng ta cần biết trước những gì và cách chúng ta sẽ làm). Tuy nhiên, cú pháp rất có thể sẽ giống nhau.

Hãy bổ sung cẩm nang của chúng ta bằng những nhiệm vụ đầu tiên:

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

Nhiệm vụ chính xác là nhiệm vụ mà Ansible sẽ thực hiện trên các máy chủ từ xa. Chúng tôi đặt tên cho tác vụ để có thể theo dõi việc thực hiện nó trong nhật ký. Và chúng tôi mô tả những gì nó cần làm bằng cú pháp của một mô-đun cụ thể. Trong trường hợp này apt: update_cache=yes - yêu cầu cập nhật các gói hệ thống bằng mô-đun apt. Lệnh thứ hai phức tạp hơn một chút. Chúng ta chuyển một danh sách các gói tới mô-đun apt và nói rằng chúng state nên trở thành present, nghĩa là chúng tôi nói cài đặt các gói này. Theo cách tương tự, chúng ta có thể yêu cầu họ xóa chúng hoặc cập nhật chúng bằng cách thay đổi state. Xin lưu ý rằng để Rails hoạt động với postgresql, chúng tôi cần gói postgresql-contrib mà chúng tôi đang cài đặt ngay bây giờ. Một lần nữa, bạn cần biết và thực hiện điều này; ansible tự nó sẽ không làm điều này.

Hãy thử chạy lại playbook và kiểm tra xem các gói đã được cài đặt chưa.

Tạo người dùng mới.

Để làm việc với người dùng, Ansible cũng có một module - user. Hãy thêm một nhiệm vụ nữa (Tôi đã ẩn các phần đã biết của playbook đằng sau các nhận xét để không sao chép toàn bộ mỗi lần):

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

Chúng tôi tạo một người dùng mới, đặt schell và mật khẩu cho nó. Và sau đó chúng tôi gặp phải một số vấn đề. Điều gì sẽ xảy ra nếu tên người dùng cần phải khác nhau đối với các máy chủ khác nhau? Và lưu trữ mật khẩu ở dạng văn bản rõ ràng trong sổ tay là một ý tưởng rất tồi. Để bắt đầu, hãy đặt tên người dùng và mật khẩu vào các biến và ở cuối bài viết tôi sẽ hướng dẫn cách mã hóa mật khẩu.

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

Các biến được đặt trong sổ tay sử dụng dấu ngoặc nhọn đôi.

Chúng tôi sẽ chỉ ra giá trị của các biến trong tệp kiểm kê:

123.123.123.123

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

Hãy lưu ý chỉ thị [all:vars] - nó nói rằng khối văn bản tiếp theo là các biến (vars) và chúng có thể áp dụng cho tất cả các máy chủ (tất cả).

Thiết kế cũng thú vị "{{ user_password | password_hash('sha512') }}". Vấn đề là ansible không cài đặt người dùng thông qua user_add giống như bạn làm điều đó bằng tay. Và nó lưu tất cả dữ liệu một cách trực tiếp, đó là lý do tại sao chúng ta cũng phải chuyển đổi trước mật khẩu thành hàm băm, đó là chức năng của lệnh này.

Hãy thêm người dùng của chúng tôi vào nhóm sudo. Tuy nhiên, trước đó chúng ta cần đảm bảo rằng một nhóm như vậy tồn tại vì không ai sẽ làm điều này cho chúng ta:

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

Mọi thứ khá đơn giản, chúng tôi cũng có một mô-đun nhóm để tạo nhóm, với cú pháp rất giống apt. Sau đó, chỉ cần đăng ký nhóm này với người dùng là đủ (groups: "sudo").
Việc thêm khóa ssh cho người dùng này cũng rất hữu ích để chúng tôi có thể đăng nhập bằng khóa đó mà không cần mật khẩu:

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

Trong trường hợp này, thiết kế thú vị "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — nó sao chép nội dung của tệp id_rsa.pub (tên của bạn có thể khác), tức là phần công khai của khóa ssh và tải nó lên danh sách các khóa được ủy quyền cho người dùng trên máy chủ.

Vai trò

Tất cả ba nhiệm vụ để tạo công dụng có thể dễ dàng được phân loại thành một nhóm nhiệm vụ và sẽ là một ý tưởng hay nếu lưu trữ nhóm này riêng biệt với sổ tay chính để nhóm không phát triển quá lớn. Với mục đích này, Ansible có vai trò.
Theo cấu trúc tệp được nêu ngay từ đầu, các vai trò phải được đặt trong một thư mục vai trò riêng, với mỗi vai trò có một thư mục riêng có cùng tên, bên trong thư mục tác vụ, tệp, mẫu, v.v.
Hãy tạo một cấu trúc tập tin: ./ansible/roles/user/tasks/main.yml (main là tệp chính sẽ được tải và thực thi khi một vai trò được kết nối với playbook; các tệp vai trò khác có thể được kết nối với nó). Bây giờ bạn có thể chuyển tất cả các tác vụ liên quan đến người dùng sang tệp này:

# 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

Trong cẩm nang chính, bạn phải chỉ định để sử dụng vai trò người dùng:

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

Ngoài ra, có thể nên cập nhật hệ thống trước tất cả các tác vụ khác; để thực hiện việc này, bạn có thể đổi tên khối tasks trong đó chúng được định nghĩa trong pre_tasks.

Thiết lập nginx

Chúng ta đã cài đặt Nginx rồi; chúng ta cần cấu hình và chạy nó. Hãy thực hiện ngay vai trò đó. Hãy tạo một cấu trúc tập tin:

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

Bây giờ chúng ta cần các tập tin và mẫu. Sự khác biệt giữa chúng là ansible sao chép trực tiếp các tập tin. Và các mẫu phải có phần mở rộng j2 và chúng có thể sử dụng các giá trị biến bằng cách sử dụng cùng một dấu ngoặc nhọn đôi.

Hãy kích hoạt nginx trong main.yml tài liệu. Đối với điều này, chúng tôi có một mô-đun systemd:

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

Ở đây chúng tôi không chỉ nói rằng nginx phải được khởi động (nghĩa là chúng tôi khởi chạy nó), mà chúng tôi nói ngay rằng nó phải được kích hoạt.
Bây giờ hãy sao chép các tập tin cấu hình:

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

Chúng tôi tạo tệp cấu hình nginx chính (bạn có thể lấy trực tiếp từ máy chủ hoặc tự viết). Và cả tệp cấu hình cho ứng dụng của chúng tôi trong thư mục site_available (điều này không cần thiết nhưng hữu ích). Trong trường hợp đầu tiên, chúng ta sử dụng mô-đun sao chép để sao chép các tập tin (tập tin phải ở dạng /ansible/roles/nginx/files/nginx.conf). Trong phần thứ hai, chúng tôi sao chép mẫu, thay thế các giá trị của các biến. Mẫu phải ở trong /ansible/roles/nginx/templates/my_app.j2). Và nó có thể trông giống như thế này:

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

Hãy chú ý đến phần chèn {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — đây là tất cả các biến có giá trị Ansible sẽ thay thế vào mẫu trước khi sao chép. Điều này hữu ích nếu bạn sử dụng sổ tay cho các nhóm máy chủ khác nhau. Ví dụ: chúng tôi có thể thêm tệp kiểm kê của mình:

[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

Nếu bây giờ chúng ta khởi chạy playbook của mình, nó sẽ thực hiện các tác vụ được chỉ định cho cả hai máy chủ. Nhưng đồng thời, đối với máy chủ lưu trữ dàn dựng, các biến sẽ khác với các biến sản xuất và không chỉ ở vai trò và sách chơi mà còn ở cấu hình nginx. {{ inventory_hostname }} không cần phải chỉ định trong tệp kiểm kê - điều này biến ansible đặc biệt và máy chủ mà playbook hiện đang chạy được lưu trữ ở đó.
Nếu bạn muốn có tệp kiểm kê cho nhiều máy chủ nhưng chỉ chạy cho một nhóm, việc này có thể được thực hiện bằng lệnh sau:

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

Một tùy chọn khác là có các tệp kiểm kê riêng cho các nhóm khác nhau. Hoặc bạn có thể kết hợp cả XNUMX cách trên nếu bạn có nhiều máy chủ khác nhau.

Hãy quay lại việc thiết lập nginx. Sau khi sao chép các tệp cấu hình, chúng ta cần tạo một liên kết tượng trưng trong Sitest_enabled tới my_app.conf từ Sites_available. Và khởi động lại 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

Mọi thứ ở đây đều đơn giản - một lần nữa các mô-đun có thể sử dụng được với cú pháp khá chuẩn. Nhưng có một điểm. Không có ích gì khi khởi động lại nginx mỗi lần. Bạn có để ý rằng chúng ta không viết các lệnh như: “làm cái này như thế này”, cú pháp trông giống “cái này phải có trạng thái này” hơn. Và thường thì đây chính xác là cách hoạt động của ansible. Nếu nhóm đã tồn tại hoặc gói hệ thống đã được cài đặt thì ansible sẽ kiểm tra điều này và bỏ qua tác vụ. Ngoài ra, các tập tin sẽ không được sao chép nếu chúng hoàn toàn khớp với những gì đã có trên máy chủ. Chúng ta có thể tận dụng điều này và chỉ khởi động lại nginx nếu các tệp cấu hình đã được thay đổi. Có một chỉ thị đăng ký cho việc này:

# 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

Nếu một trong các tệp cấu hình thay đổi, một bản sao sẽ được tạo và biến sẽ được đăng ký restart_nginx. Và chỉ khi biến này đã được đăng ký thì dịch vụ mới được khởi động lại.

Và tất nhiên, bạn cần thêm vai trò nginx vào playbook chính.

Thiết lập postgresql

Chúng tôi cần kích hoạt postgresql bằng systemd giống như cách chúng tôi đã làm với nginx, đồng thời tạo một người dùng mà chúng tôi sẽ sử dụng để truy cập cơ sở dữ liệu và chính cơ sở dữ liệu đó.
Hãy tạo một vai trò /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 }}"

Tôi sẽ không mô tả cách thêm biến vào kho, việc này đã được thực hiện nhiều lần, cũng như cú pháp của các mô-đun postgresql_db và postgresql_user. Thông tin thêm có thể được tìm thấy trong tài liệu. Chỉ thị thú vị nhất ở đây là become_user: postgres. Thực tế là theo mặc định, chỉ người dùng postgres mới có quyền truy cập vào cơ sở dữ liệu postgresql và chỉ cục bộ. Lệnh này cho phép chúng tôi thực thi các lệnh thay mặt cho người dùng này (tất nhiên là nếu chúng tôi có quyền truy cập).
Ngoài ra, bạn có thể phải thêm một dòng vào pg_hba.conf để cho phép người dùng mới truy cập vào cơ sở dữ liệu. Điều này có thể được thực hiện giống như cách chúng ta thay đổi cấu hình nginx.

Và tất nhiên, bạn cần thêm vai trò postgresql vào playbook chính.

Cài đặt Ruby qua rbenv

Ansible không có mô-đun để làm việc với rbenv, nhưng nó được cài đặt bằng cách sao chép kho git. Vì vậy, vấn đề này trở thành vấn đề không chuẩn nhất. Hãy tạo một vai trò cho cô ấy /ansible/roles/ruby_rbenv/main.yml và hãy bắt đầu điền vào:

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

Chúng tôi lại sử dụng lệnh Become_user để làm việc với người dùng mà chúng tôi đã tạo cho các mục đích này. Vì rbenv được cài đặt trong thư mục chính của nó chứ không phải trên toàn cầu. Và chúng tôi cũng sử dụng mô-đun git để sao chép kho lưu trữ, chỉ định repo và đích.

Tiếp theo, chúng ta cần đăng ký rbenv init trong bashrc và thêm rbenv vào PATH tại đó. Đối với điều này, chúng tôi có mô-đun 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 -)"'

Sau đó, bạn cần cài đặt Ruby_build:

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

Và cuối cùng cài đặt Ruby. Việc này được thực hiện thông qua rbenv, tức là chỉ bằng lệnh 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

Chúng tôi nói lệnh nào sẽ được thực thi và với lệnh nào. Tuy nhiên, ở đây chúng ta thấy một thực tế là ansible không chạy mã có trong bashrc trước khi chạy các lệnh. Điều này có nghĩa là rbenv sẽ phải được xác định trực tiếp trong cùng một tập lệnh.

Vấn đề tiếp theo là do lệnh shell không có trạng thái theo quan điểm ansible. Tức là sẽ không có sự kiểm tra tự động xem phiên bản Ruby này đã được cài đặt hay chưa. Chúng ta có thể tự làm điều này:

- 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

Tất cả những gì còn lại là cài đặt gói:

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

Và một lần nữa, hãy thêm vai trò của chúng tôi ruby_rbenv vào sổ tay chính.

Những tệp đã chia sẻ.

Nói chung, việc thiết lập có thể được hoàn thành ở đây. Tiếp theo, tất cả những gì còn lại là chạy capistrano và nó sẽ tự sao chép mã, tạo các thư mục cần thiết và khởi chạy ứng dụng (nếu mọi thứ được cấu hình đúng). Tuy nhiên, capistrano thường yêu cầu các tệp cấu hình bổ sung, chẳng hạn như database.yml hoặc .env Chúng có thể được sao chép giống như các tệp và mẫu cho nginx. Chỉ có một sự tinh tế. Trước khi sao chép tập tin, bạn cần tạo cấu trúc thư mục cho chúng, đại loại như thế này:

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

chúng tôi chỉ chỉ định một thư mục và ansible sẽ tự động tạo thư mục gốc nếu cần.

Vault ansible

Chúng ta đã thấy thực tế là các biến có thể chứa dữ liệu bí mật như mật khẩu của người dùng. Nếu bạn đã tạo .env tập tin cho ứng dụng, và database.yml thì chắc chắn phải có nhiều dữ liệu quan trọng hơn nữa. Sẽ rất tốt nếu giấu chúng khỏi những con mắt tò mò. Với mục đích này nó được sử dụng hầm an toàn.

Hãy tạo một tập tin cho các biến /ansible/vars/all.yml (tại đây bạn có thể tạo các tệp khác nhau cho các nhóm máy chủ khác nhau, giống như trong tệp kiểm kê: production.yml, staging.yml, v.v.).
Tất cả các biến phải được mã hóa phải được chuyển vào tệp này bằng cú pháp yml tiêu chuẩn:

# 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

Sau đó tập tin này có thể được mã hóa bằng lệnh:

ansible-vault encrypt ./vars/all.yml

Đương nhiên, khi mã hóa, bạn sẽ cần đặt mật khẩu để giải mã. Bạn có thể xem nội dung bên trong tệp sau khi gọi lệnh này.

Bằng phương tiện ansible-vault decrypt tập tin có thể được giải mã, sửa đổi và sau đó mã hóa lại.

Bạn không cần phải giải mã tập tin để hoạt động. Bạn lưu trữ nó được mã hóa và chạy playbook với đối số --ask-vault-pass. Ansible sẽ yêu cầu mật khẩu, truy xuất các biến và thực hiện các tác vụ. Tất cả dữ liệu sẽ vẫn được mã hóa.

Lệnh hoàn chỉnh cho một số nhóm máy chủ và kho tiền ansible sẽ trông giống như thế này:

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

Nhưng tôi sẽ không cung cấp cho bạn toàn bộ nội dung của sách và vai trò, hãy tự viết nó. Bởi vì ansible là như vậy - nếu bạn không hiểu những gì cần phải làm thì nó sẽ không làm việc đó cho bạn.

Nguồn: www.habr.com

Thêm một lời nhận xét