Hướng dẫn và sử dụng Ansible

Ansible là công cụ giúp chuẩn bị trước và quản lý cấu hình, giống như là Chef, Puppet hay Salt

Nó là cách đơn giản nhất và dễ nhất để bắt đầu. Bởi vì nó chỉ là SSH, sử dụng SSH để kết nối với Server và chạy các Task đã được cấu hình.

Một ưu điểm nữa của Ansibble là giúp chúng ta dễ dàng chuyển đổi một Bash script (đây vẫn là cách phổ biến để quản lý cấu hình) thành các task trong Ansible. Vì nó chủ yếu dựa trên SSH, nên cũng dễ dàng hiểu rằng điều này có thể là trường hợp – Ansible kết thúc chạy các lệnh tương tự như Bash script

Chúng ta có thể chỉ script những gì cần cấu hình, nhưng Ansible rõ ràng hơn, bởi vì nó tự động quá trình nhận ngữ cảnh trước khi chạy các Task. Với ngữ cảnh này, Ansible có thể xử lý hầu hết các trường hợp giới hạn, khi mà chúng ta thường hay quan tâm đến các script dài và phức tạp

Các Task của Ansible là idempotent, tức là không thay đổi dù chúng ta có chạy lại các task này bao nhiều lần đi nữa. Không phải code nhiều, Bash script thường không đảm bảo nếu chúng ta chạy đi chạy lại. Ansible sử dụng “Fact” , đó là thông tin về hệ thống và môi trường thu nhận được trước khi chạy các Task.

Ansible sử dụng những sự kiện để kiểm tra state và xem xét nếu nó cần thay đổi gì hay không mục đích để thu được kết qủa mong muốn. Điều này giúp cho chúng ta an toàn hơn khi chạy đi chạy lại Ansible Task trên một Server.

Trong bài viết này, chúng ta sẽ chỉ bắt đầu với Ansible với các khái niệm cơ bản và sau đó đưa vào thêm nhiều tính năng hơn để cải tiến cấu hình của chúng ta.

Cài đặt

Để bắt đầu chúng ta cần cài đặt Ansible trước tiên. Các Task có thể được chạy trên bất kỳ máy nào mà Ansible được cài đặt.

Điều này nghĩa là thường có một Server dùng để chạy các câu lện Ansible, mặc dù không có yêu cầu gì đặc biệt cho Server mà Ansible được cài đặt lên đấy. Ansible là vô giá trị – không có đặc vụ trọng yếu nào đang chạy. Chúng ta có thể chạy Ansible trên bất kỳ server nào, tôi thường chạy các Task trên mày laptop của mình.

Ubuntu

Chúng ta sẽ cài đặt Ansible trên Ubuntu 16.04.

sudo apt-add-repository -y ppa:ansible/ansible
sudo apt-get update
sudo apt-get install -y ansible

Cài đặt Ansible Global.

Mac OS

brew install ansible

Bạn có thể tự cài đặt nó theo các OS khác nhau theo hướng dẫn sau đây

Cấu hình

Chúng ta tạo các file cấu hình trên thư mục local khi cần. Chúng ta không cần bất kỳ file cấu hình nào trong thư mục /etc hay một chỗ đặc biệt nào khác, nó giúp chúng ta đơn giản hoá khi sử dụng

Quản lý Server: Inventory

Ansible yêu cầu bạn tạo một inventory file  để định nghĩa những Server nào đang cần quản lý. File này có thể có tên bất kỳ, nhưng chúng ta sẽ thường đặt tên cho nó giống như định nghĩa trong hosts

Trong file host chúng ta có thể định nghĩa Server mà chúng ta quản lý. Ở đây chúng ta sẽ định nghĩa 2 server mà chúng ta quản lý dưới nhãn “web”. Nhãn này có thể được đặt tuỳ ý

[web]
192.168.22.10
192.168.22.11

Mục đích để test trong bài viết này, chúng ta sẽ sử dụng một Ubuntu 16.04 trên AWS, nhưng nó không quan trọng bạn có thể sử dụng bất kỳ Server hay cloud nào mà bạn thích. Tiếp theo sẽ chạy Ansible Task trực tiếp trên Server này, tức là chúng ta sẽ chạy Ansible trên cùng một Server mà nó quản lý, nó vẫn hợp lệ mặc dù trong thực tế thì chúng ta sẽ chạy nó trên một máy khác ví dụ trên laptop của chúng ta.

Khi chúng ta chạy lại Ansible trên local, chúng ta cũng không cần quan tâm những gì cần hay phải chỉnh sửa thông tin của file inventory. Sau đây sẽ là hướng dẫn bạn cách chạy Ansible trên local và đối với một Remote Server.

Nào hãy mở file hosts, sau đó thêm vào đấy thông tin địa chỉ trỏ đến local và trỏ đến một Remote Server.

[local]
127.0.0.1

[remote]
192.168.1.2 # IP of remote server

Tôi sẽ chỉ cho bạn các câu lệnh kết nối với local và remote server.

Chạy các câu lệnh

Ansible sẽ đảm đương giúp bạn việc SSH kết nối tiện lợi đến Server, thường thì dựa trên SSH-key. Bởi vì Ansible sử dụng SSH, server mà nó đang chạy trên đấy cần có thể SSH được vào được Inventory Server.

Tuy nhiên, Ansible sẽ thử kết nối bằng user hiện tại mà nó đang sử dụng. Nếu chúng ta đang chạy với user là ubuntu, nó sẽ thử kết nối như một ubutu user đến Server khác.

Nếu Ansible có thể SSH trực tiếp đến các Server mà nó quản lý, chúng ta có thể chạy các câu lệnh mà không gặp phiền phức gì

# Run against localhost
$ ansible -i ./hosts --connection=local local -m ping

# Run against remote server
$ ansible -i ./hosts remote -m ping
127.0.0.1 | success >> {
    "changed": false,
    "ping": "pong"
}

Nếu bạn gặp lỗi như sau “Too many authentication failures”, chúng ta có thể thêm một vài lựa chọn cho SSH, và Ansible sẽ hỏi chúng ta password khi đăng nhập vào ansible -i ./hosts --ask-pass --ssh-extra-args='-o "PubkeyAuthentication=no"' all -m ping

Sử dụng  --connection=local để yêu cầu Ansible không cố gắng chạy các lệnh SSH, vì chúng ta chỉ chạy ở máy local. Tuy nhiên chúng ta vẫn cần thêm vào trong hosts file, để giúp chúng ta biết rằng mình đang kết nối đến đâu, nó sẽ không phải là một phỏng đoán hay hiểu như là localhost hay 127.0.0.1

Trong cả 2 trường hợp, chúng ta có thể thấy được kết quả đầu ra từ Ansible là trả về các Json để thông báo với chúng ta, nếu các Task được tạo có bất kỳ thay đổi nào

Nào hãy cũng xem kỹ lại câu lệnh

  • -i ./hosts – đặt file Inventory (môi trường kết nối), có tên là hosts.
  • remotelocalall – Sử dụng các Server mà được định nghĩa với tên nhãn trong file hosts  hay file Inventory. “all” là từ khoá đặc biệt để chạy tất cả các Server được định nghĩa trong file
  • -m ping – Sử dụng ping module, mà đơn giản là chạy câu lệnh ping và trả về kết quả
  • -c local | --connection=local – chạy câu lệnh local, không phải qua SSH

Modules

Ansible sử dụng Module để thực hiện phần lớn các Task của nó. Module có thể thực hiện các công việc như là cài đặt phần mềm, copy các file, sử dụng template …

Module là cách để sử dụng Ansible, chúng ta có thể sử dụng các ngữ cảnh có hiệu lực mục đích để xác định các Action nào khi cần làm gì đó để thực hiện một Task.

Nếu chúng ta không có các Module, chúng ta muốn tuỳ ý chạy các lệnh Shell và có thể sử dụng giống như Bash Script.  Đây là những gì một lệnh shell trông như thế nào trong Ansible (sử dụng Shell Module)

# Chạy trên local server
ansible -i ./hosts local --connection=local -b --become-user=root \
    -m shell -a 'apt-get install nginx'

# Chạy trên remote server
ansible -i ./hosts remote -b --become-user=root all \
    -m shell -a 'apt-get install nginx'

Ở đây câu lệnh sudo apt-get install nginx sẽ được chạy sử dụng shell module

Chúng ta có một vài flag mới:

  • -b –  viết tắt “become”, nói với Ansible rằng sẽ trở thành một user khác khi chạy câu lệnh. Đây là cách giúp bạn có thể chạy bằng nhiều user khác nhau, hay nâng quyền lên root user
  • --become-user=root – Chạy các câu lện bằng user root. Chúng ta có thể định nghĩa bất kỳ user nào mà đang tồn tại ở đây.
  •  -a – được sử dụng để truyền bất kỳ tham số nào cho Module được định nghĩa sử dụng -m

Tuy nhiên điều này không đặc biệt hữu ích. Nó không thể tiện dụng cho việc chạy các câu lệnh trên tất cả môi trường trong một lần chạy, chúng ta vẫn chỉ sử dụng các câu lện trong Bash Script

Nếu chúng ta sử dụng Module thay thế thích hợp, chúng ta có thể chạy các câu lệnh với kết quả được đảm bảo. Asible đảm bảo tính Indempotence – chúng ta có thể chạy đi chạy lại các task mà không làm thay đổi kết quả cuối cùng.

Để cài đặt phần mềm trên server Debian/Ubuntu, “apt” Module sẽ chạy câu lệnh giống nhau nhưng đảm bảo Idempotence.

# Chạy trên local server
ansible -i ./hosts local --connection=local -b --become-user=root \
    -m apt -a 'name=nginx state=installed update_cache=true'

127.0.0.1 | success >> {
    "changed": false
}

# Chạy trên remote server
ansible -i ./hosts remote -b --become-user=root \
    -m apt -a 'name=nginx state=installed update_cache=true'

127.0.0.1 | success >> {
    "changed": false
}

Ở đây nó sẽ sử dụng apt Module để cập nhập cache của kho lữu trữ và cài đặt Nginx (nếu nó chưa được cài đặt).

Kết quả trả về "changed": false, điều này nghĩa rằng không có thay đổi, chúng ta đã cài đặt Nginx sử dụng Shell Module trước đấy. Điều tuyệt vời là chúng ta có thể chạy đi chạy lại câu lệnh này mà không cần lo lắng nó sẽ làm thay đổi kết quả mong muốn. Nginx đã được cài đặt và Ansible đã biết điều đó, nên nó sẽ không cố thử cài đặt lại lần nữa.

Hãy cũng xem nội dung câu lệnh:

  • -i ./hosts –  Đặt file Inventory, có tên là hosts
  • -b –  viết tắt “become”, nói với Ansible rằng sẽ trở thành một user khác khi chạy câu lệnh
  • --become-user=root – Chạy các câu lện bằng user root.
  • local | remote –  Chạy trên local hoặc remote được định nghĩa trong file Inventory
  • -m apt – sử dụng apt Module
  • -a 'name=nginx state=installed update_cache=true' – cung cấp các tham số đầu vào cho apt module, bao gồm tên package, state kết thúc mong muốn, và liệu có cập nhập package lưu trữ cache hay không

Chúng ta có thể chạy tất cả các Task (thông qua các Module) theo cách ad-hoc này, hãy làm cho chúng dễ quản lý hơn. Hãy chuyển Task vào Playbook, có thể chạy và phối hợp nhiều tác vụ.

Playbook

Playbook có thể chạy nhiều Task và cung cấp một số chức năng nâng cao mà chúng ta sẽ bỏ lỡ khi sử dụng các câu lệnh Ad-hoc.  Nào hãy chuyển các Task bên trên vào Playbook.

Playbook và roles trong Ansible tất cả đều sử dụng Yaml

Tạo một nginx.yml

---
# hosts có thể là "remote" hoặc "all"
- hosts: local
  connection: local
  become: yes
  become_user: root
  tasks:
   - name: Install Nginx
     apt:
       name: nginx
       state: installed
       update_cache: true

Task này giống hệt như chúng ta sử dụng câu lệnh Ad-hoc, bao gồm cài đặt sử dụng kết nối local.

Cấu hình này sẽ sử dụng Server với nhãn [local] trong file hosts

Nếu chúng ta đang không sử dụng kết nối local, chúng ta có thể kết nối lại như sau:

---
- hosts: remote
  become: yes
  become_user: root
  tasks:
   - name: Install Nginx
     apt:
       name: nginx
       state: installed
       update_cache: true

Cấu hình này sẽ sử dụng server với nhãn [remote] trong hosts file

Sử dụng becomebecome_user lần nữa trong file Task của chúng ta để bảo Ansible sử dụng câu lệnh sudo bằng user root và sau đó chạy qua file Playbook.

Với một file Yaml Playbook, chúng ta cần sử dụng câu lệnh ansible-playbook, nó trở nên đơn giản hơn khi chạy câu lệnh hiện tại

$ ansible-playbook -i ./hosts nginx.yml

PLAY [local] ******************************************************************

GATHERING FACTS ***************************************************************
ok: [127.0.0.1]

TASK: [Install Nginx] *********************************************************
ok: [127.0.0.1]

PLAY RECAP ********************************************************************
127.0.0.1                  : ok=2    changed=0    unreachable=0    failed=0

Chúng ta nhận được những phản hồi hữu ích khi câu lệnh này chạy, bao gồm Ansible Task và kết quả của chúng.

Ở đây chúng ta thấy rằng tất cả đều chạy OK, nhưng không có gì bị thay đổi cả. Bởi vì chúng ta đã cài Nginx trước đấy.

Handler

Handler hoàn toàn giống như một Task (Nó có thể làm được bất cứ điều gì mà Task có thể làm), nhưng nó sẽ chỉ chạy khi được gọi bởi Task khác. Bạn có thể nghĩ nó như một phần của hệ thống Event; Handler sẽ nhận được một Action khi được gọi bởi một Event mà nó đang lắng nghe.

Nó rất hữu ích cho các Actions xảy ra lại từ lần thứ 2 mà có thể được yêu cầu sau khi chạy một Task, như là bắt đầu một Service mới sau khi cài đặt và reload một Service sau khi thay đổi một cấu hình.

---
# Ví dụ vẫn sử dụng máy local
# Bỏ 'connection' và thiết lập hosts đến 'remote' cho kết nối remote
- hosts: local
  connection: local
  become: yes
  become_user: root
  tasks:
   - name: Install Nginx
     apt:
       name: nginx
       state: installed
       update_cache: true
     notify:
      - Start Nginx

  handlers:
   - name: Start Nginx
     service:
       name: nginx
       state: started

Ở đây chúng ta thêm một chỉ thị notify khi cài đặt Task. Nó sẽ gửi thông báo đến Handler bất kỳ có tên là “Start Nginx” sau khi Task được chạy.

Sau đó chúng ta có thể tạo một Handler với tên là “Start Nginx”. Handler này là Task được gọi khi “Start Nginx” được thông báo.

Handler cụ thể này sử dụng Service Module, có thể start, stop, restart và reload các Service của hệ thống. Trong trường hợp này, chúng ta nói với Ansible rằng chúng ta muốn Nginx được start

Chú ý rằng Ansible giúp chúng ta xác định state mà bạn mong muốn Service ở đây là gì, thay vì xác định những thay đổi mà bạn muốn. Ansible sẽ quyết định nếu một thay đổi là cần thiết, chúng ta chỉ nói với nó kết qủa mong muốn.

Nào hãy chạy lại Playbook này xem sao

$ ansible-playbook -i ./hosts nginx.yml

PLAY [local] ******************************************************************

GATHERING FACTS ***************************************************************
ok: [127.0.0.1]

TASK: [Install Nginx] *********************************************************
ok: [127.0.0.1]

NOTIFIED: [nginx | Start Nginx] ***********************************************
ok: [127.0.0.1]

PLAY RECAP ********************************************************************
127.0.0.1                  : ok=2    changed=0    unreachable=0    failed=0

Chúng ta thu được kết quả tương tự, nhưng lúc này Handler đã được chạy.

Notifier chỉ được chạy nếu Task được chạy. Nếu chúng ta đã cài Nginx rồi, thì Task cài đặt Nginx sẽ không được chạy, do đó notifier sẽ không được gọi.

Chúng ta có thể dùng Playbook để chạy nhiều Task, thêm vào trong các biến, định nghĩa các thiết lập khác và thậm chí bao hàm cả các Playbook khác.

Task khác

Tiếp theo chúng ta sẽ thêm các Task khác vào Playbook này và tìm hiểu một vài chức năng mới.

---
# Ví dụ vẫn sử dụng máy local
# Bỏ 'connection' và thiết lập hosts đến 'remote' cho kết nối remote
- hosts: local
  connection: local
  become: yes
  become_user: root
  vars:
   - docroot: /var/www/serversforhackers.com/public
  tasks:
   - name: Add Nginx Repository
     apt_repository:
       repo: ppa:nginx/stable
       state: present
     register: ppastable

   - name: Install Nginx
     apt:
       pkg: nginx
       state: installed
       update_cache: true
     when: ppastable|success
     notify:
      - Start Nginx

   - name: Create Web Root
     file:
      path: '{{ docroot }}'
      mode: 775
      state: directory
      owner: www-data
      group: www-data
     notify:
      - Reload Nginx

  handlers:
   - name: Start Nginx
     service:
       name: nginx
       state: started

    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

Bây giờ thì chúng ta có 3 Task:

  • Add Nginx Repository – Thêm Nginx stable PPA để dùng Version ổn định mới nhất của Nginx sử dụng apt_repository module.
  • Install Nginx – cài đặt Nginx sử dụng apt module
  • Create Web Root – Cuối cùng tạo thư mục web root.

Cũng có những cái mới ở đây là chỉ thị registerwhen. Những cái này cho Ansible biết để chạy một Task khi có cái gì đó khác xảy ra.

Add Nginx Repository Task đăng ký ppastable. Sau đó chúng ta sử dụng nó để thông báo Install Nginx Task chỉ chạy khi Task ppastable được đăng ký thành công.  Điều này cho phép chúng ta có điều kiện để ngăn chặn Ansible chạy một Task.

Ví dụ cụ thể này chỉ cài đặt Nginx khi ppa lưu trữ được thêm vào là thừa, như là việc thêm kho lưu trữ lỗi, Ansible sẽ dừng lại và thông báo lỗi. Tuy nhiên rất tốt để biết các chức năng tồn tại.

Bạn có thể đăng ký kết quả của một Module Action, và sử dụng biến được định nghĩa trong register để thực thi các Action có điều kiện khi dựa trên các giá trị biến đã được đăng ký. Ví dụ đăng ký kết quả của câu lệnh chạy qua Module Shell  có thể cho phép bạn truy cập stdout của câu lệnh đó.

Chúng ta cũng sử dụng một biến là docroot, biến được định nghĩa trong phần var. Nó được sử dụng như một tham số đích của module file giúp tạo thư mục được xác định trước.

Chú ý rằng cấu hình path sử dụng ngoặc đơn {{ var-name }} – đây là Jinja2 template. Đoạn này phải nằm trong dấu nháy đơn hoặc nháy kép – ví dụ path: '{{ docroot }}' thay cho path: {{ docroot }} . Chú ý rằng không sử dụng các dấu ngoặc kép sẽ dẫn đến một lỗi xảy ra.

Playbook này có thể được chạy với câu lệnh đơn giản như sau:

ansible-playbook -i ./hosts nginx.yml

Chúng ta đã chạy một vài câu lệnh Ad-hoc, thường dùng module Ansible, và tổ chức một số Task liên quan trong Playbook.

Tiếp theo, chúng ta sẽ tiếp tục Ansible bằng cách tổ chức Playbook thành một Role, giúp chúng ta tổ chức các mục liên quan như là File và Template, đồng thời giúp chúng ta tổ chức các Task và Action liên quan phức tạp hơn.

Roles

Roles là hữu ích khi chúng ta muốn cấu trúc chức nhiều phần, các Task liên quan và đóng gói các dữ liệu cần thiết để thực hiện những Task đấy. Ví dụ, cài đặt Nginx có thể liên quan đến việc thêm một Repository package, cài đặt package và thiết lập cấu hình. Chúng ta đã nhìn thấy cài đặt qua một Playbook, nhưng khi chúng ta bắt đầu cài đặt cấu hình của mình, Playbook có xu hướng bận hơn một chút.

Hơn nữa trong cấu hình thực tế thường yêu cầu thêm dữ liệu như các biến, các File và các Template động…Những công cụ này có thể được sử dụng với Playbook nhưng chúng ta có thể làm tốt hơn ngay lập tức bằng cách tổ chức các Task và dữ liệu liên quan thành một cấu trúc mạch lạc như một Role.

Các Roles có một cấu trúc dữ liệu như sau:

roles
  rolename
   - files
   - handlers
   - meta
   - templates
   - tasks
   - vars

Với mỗi thư mục, Ansible sẽ tìm kiếm và đọc tất cả các file YAML được gọi là main.yml tự động.

Chúng ta sẽ chia nhỏ file nginx.yml và đặt mỗi thành phần vào trong thư mục phù hợp để tạo một công cụ cấu hình trước rõ ràng hơn và hoàn hảo hơn.

Tạo một Role

Chúng ta có thể sử dụng câu lệnh ansible-galaxy để tạo một Role mới. Công cụ này có thể được sử dụng để lưu các Role vào đăng ký puplic của Ansible. Tuy nhiên mình thông thường chỉ sử dụng nó để khởi tạo một role cục bộ.

Nào chúng ta hãy cùng xem làm thế nào để thiết lập điều này

# Di chuyển đến thư mục được tạo trước đấy
cd ~/ansible-example

# In case we left our virtualenv at some point 
source .venv/bin/activate

# Tạo thư mục roles
mkdir roles
cd roles

# khởi tạo một role mới tên là"nginx"
ansible-galaxy init nginx

Thư mục với tên là roles là quy ước để Ansible sử dụng để tìm các Role khi chạy Playbook. Thư mục nên được giữ tên là roles

Câu lệnh ansible-galaxy init nginx chạy trong thư mục roles, sẽ tạo tên_thư_mục/files cần thiết để bắt đầu một Role mới.

Hãy xem qua từng phần của Nginx Role mới được tìm thấy tại ~/ansible-example/roles/nginx

Files

Đầu tiên trong thư mục files, chúng ta có thể thêm các file mà chúng ta sẽ muốn copy vào trong Server. Đối với Nginx, tôi thường copy các file thành phần h5bp của Nginx . Đơn giản là chỉ việc download những file này trên github, thực hiện các thay đổi mong muốn và đặt chúng vào thư mục files.

~/ansible-example
 - roles
 - - nginx
 - - - files
 - - - - h5bp

Các file cấu hình H5BP được sẽ được thêm vào Server thông qua module copy.

Handler

Bên trong thư mục handlers, chúng ta có thể đặt tất cả Handler của chúng ta đã từng nằm trong Playbook nginx.yml

Bên trong hanlder/main.yml

---
- name: Start Nginx
  service:
    name: nginx
    state: started

- name: Reload Nginx
  service:
    name: nginx
    state: reloaded

Khi chúng được đặt đúng vai trò, chúng ta có thể tham khảo chúng từ cấu hình yaml khác dễ dàng.

Meta

file main.yml bên trong thư mục meta chứa dữ liệu meta của Role, bao gồm các phụ thuộc.

Nếu một Role dựa trên Role khác, chúng ta có thể định nghĩa nó ở đây. Ví dụ chúng ta có một Nginx Role dựa trên ssl Role dùng để cài đặt chứng thực của SSL.

---
dependencies:
  - { role: ssl }

Nếu gọi nginx Role, nó sẽ thử chạy SSL role trước.

Ngoài ra chúng ta có thể bỏ qua file này, hoặc định nghĩa Role ko có phụ thuộc.

---
dependencies: []

Template

Các Template file có thể chứa các biến template, dựa trên engine Jinja2 template của Python. Các file ở đây nên kết thúc bằng .j2, nhưng có thể có tên khác. Giống files, chúng ta sẽ không tìm thấy file main.yml bên trong thư mục templates.

Đây là một ví dụ cấu hình Nginx Server. Chú ý rằng nó sử dụng một số biến mà chúng ta sẽ định nghĩa ngay sau đây trong file vars/main.yml

File cấu hình Nginx này trong ví dụ của chúng ta được đặt tại templates/serversforansible.com.conf.j2

server {
    # Bắt buộc sử dụng HTTPS
    listen 80 default_server;
    server_name {{ domain }};
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl default_server;

    root /var/www/{{ domain }}/public;
    index index.html index.htm index.php;

    access_log /var/log/nginx/{{ domain }}.log;
    error_log  /var/log/nginx/{{ domain }}-error.log error;

    server_name {{ domain }};

    charset utf-8;

    include h5bp/basic.conf;

    ssl_certificate           {{ ssl_crt }};
    ssl_certificate_key       {{ ssl_key }};
    include h5bp/directive-only/ssl.conf;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt  { log_not_found off; access_log off; }

    location ~ \.php$ {
        include snippets/fastcgi.conf;
        fastcgi_pass unix:/var/run/php7.1-fpm.sock;
    }
}

Đây là cấu hình tương đối chuẩn của Nginx cho ứng dụng PHP. Có 3 biến được sử dụng ở đây:

  • domain
  • ssl_crt
  • ssl_key

3 biến này sẽ được định nghĩa trong phần biến.

Variables

Trước khi chúng ta tích hợp tất cả với nhau sử dụng các Task, hãy cùng xem tiếp phần variables. Thư mục var chứa một file main.yml chứa danh sách các biến mà chúng ta sẽ sử dụng. Nó là một nơi hữu ích cho chúng ta thay đổi cài đặt của toàn bộ cấu hình

Hãy cùng xem cấu hình file main.yml sau đây

---
domain: serversforansible.com
ssl_key: /etc/ssl/sfh/sfh.key
ssl_crt: /etc/ssl/sfh/sfh.crt

Có 3 biến chúng ta có thể dùng bất cứ chỗ nào trong Role này. Chúng ta đã được nhìn thấy những biến này được sử dụng ở trong template bên trên, nhưng chúng ta cũng sẽ được nhìn thấy các biến này trong các Task được định nghĩa.

Nếu bạn có các thông tin quan trọng thêm vào trong file chứa các biến, bạn có thể mã hoá file sử dụng ansible-vault, sẽ được giải thích ở dưới đây

Task

Cuối cùng hãy cùng xem tất cả những cái này sẽ được tập hợp lại với nhau thành các Task

File chính sẽ được chạy khi chúng ta sử dụng một Role là file tasks/main.yml. Nào hãy cùng xem cấu hình bên dưới đây

---
- name: Add Nginx Repository
  apt_repository:
    repo: ppa:nginx/stable
    state: present

- name: Install Nginx
  apt:
    pkg: nginx
    state: installed
    update_cache: true
  notify:
    - Start Nginx

- name: Add H5BP Config
  copy:
    src: h5bp
    dest: /etc/nginx
    owner: root
    group: root

- name: Disable Default Site Configuration
  file:
    dest: /etc/nginx/sites-enabled/default
    state: absent

# `dest` in quotes as a variable is used!
- name: Add SFH Site Config
  register: sfhconfig
  template:
    src: serversforansible.com.j2
    dest: '/etc/nginx/sites-available/{{ domain }}.conf' 
    owner: root
    group: root

# `src`/`dest` in quotes as a variable is used!
- name: Enable SFH Site Config
  file:
    src: '/etc/nginx/sites-available/{{ domain }}.conf'
    dest: '/etc/nginx/sites-enabled/{{ domain }}.conf'
    state: link

# `dest` in quotes as a variable is used!
- name: Create Web root
  file:
    dest: '/var/www/{{ domain }}/public'
    mode: 775
    state: directory
    owner: www-data
    group: www-data
  notify:
    - Reload Nginx

# `dest` in quotes as a variable is used!
- name: Web Root Permissions
  file:
   dest: '/var/www/{{ domain }}'
   mode: 775
   state: directory
   owner: www-data
   group: www-data
   recurse: yes
  notify:
    - Reload Nginx

Đây là một chuỗi các Task dài hơn, giúp cho việc cài đặt Nginx hoàn chỉnh hơn. Các Task theo thứ tự xuất hiện, thực hiện như sau

  • Thêm nginx/stable repository
  • Cài đặt và start Nginx
  • Thêm các file cấu hình HB5P
  • Vô hiệu hoá Nginx mặc định bằng cách loại bỏ liên kết đến file default từ thư mục sites-enabled
  • Copy virtual host template  serversforansible.com.conf.j2 trong cấu hình Nginx, tạo ra các template như đã làm
  • Cho phép cấu hình Nginx bằng cách liên kết nó đến thư mục sites-enabled
  • Tạo thư mục web root
  • Thay đổi quyền cho thư mục gốc của dự án

Có một vài module mới, bao gồm module Copy, Template và File. Bằng việc cài đặt các tham số cho mỗi Module, chúng ta có thể làm một vài điều thú vị như là đảm bảo các file là absent (xoá nếu chúng tồn tại) thông qua state: absent, hoặc tạo một file như một liên kết thông qua state: link.  Bạn nên đọc qua tài liệu cho mỗi module để biết những điều hữu ích và thú vị mà bạn có thể thực hiện với chúng.

Chạy Role

Để chạy một hoặc nhiều Role đối với một Server, chúng ta sẽ sử dụng lại Playbook khác. Các Playbook nên ở trong cùng một thư mục như thư mục roles, nơi chúng ta cần cd vào bên trong khi chạy câu lệnh ansible-playbook

Tạm thời bỏ cấu hình phụ thuộc ssl trong meta/main.yml trước khi chạy Role này nếu bạn đang làm theo các bước bên trên

Tiếp theo chúng ta sẽ tạo một file “maser” yaml để định nghĩa các Role sử dụng và host nào để chạy chúng trên đó:

File ~/ansible-example/server.yml, được đặt trong thư mục giống như thư mục roles:

---
# Chạy trên local
- hosts: local
  connection: local
  roles:
    - nginx

Nên thay vì định nghĩa tất cả các biến và các Task trong file Playbook này, chúng ta đơn giản chỉ cần định nghĩa các Role cần chạy. Các Role sẽ có nhiệm vụ chi tiết cho từng nhiệm vụ

Tiếp theo chúng ta có thể chạy các Role:

ansible-playbook -i ./hosts server.yml

Ở đây kết quả khi chạy file Playbook chính là chạy Nginx Role:

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [127.0.0.1]

TASK: [nginx | Add Nginx Repository] ******************************************
changed: [127.0.0.1]

TASK: [nginx | Install Nginx] *************************************************
changed: [127.0.0.1]

TASK: [nginx | Add H5BP Config] ***********************************************
changed: [127.0.0.1]

TASK: [nginx | Disable Default Site] ******************************************
changed: [127.0.0.1]

TASK: [nginx | Add SFH Site Config] *******************************************
changed: [127.0.0.1]

TASK: [nginx | Enable SFH Site Config] ****************************************
changed: [127.0.0.1]

TASK: [nginx | Create Web root] ***********************************************
changed: [127.0.0.1]

TASK: [nginx | Web Root Permissions] ******************************************
ok: [127.0.0.1]

NOTIFIED: [nginx | Start Nginx] ***********************************************
ok: [127.0.0.1]

NOTIFIED: [nginx | Reload Nginx] **********************************************
changed: [127.0.0.1]

PLAY RECAP ********************************************************************
127.0.0.1                  : ok=8   changed=7   unreachable=0    failed=0

Chúng ta có thể đặt tất cả các thành phần thay đổi cùng nhau vào trong một Role kết nối và giờ đây Nginx đã được cấu hình và cài đặt.

Facts

Chú ý rằng dòng đầu tiên khi chạy playbook luôn luôn là “gathering facts”.

Trước khi chạy bất kỳ Task nào, Ansible sẽ thu thập thông tin về hệ thống nó đang cung cấp. Cái này được gọi là Facts, và nó bao gồm một loạt các thông tin về hệ thống như là số lõi CPU, mạng ipv4 và ipv6 có thể dùng, lượng ổ cứng đang dùng, phân vùng Linux… Sau đó sử dụng các biến để gọi các thông tin từ Facts ra. Nếu chúng ta không cần thu thập thông tin của host thì có thể thiết lập set grathering_facts: no, nó giúp cho việc chạy Playbook nhanh hơn.

Facts thường rất hữu dụng trong cấu hình các Task và Template. Ví dụ Nginx thường được thiết lập để sử dụng với nhiều tiến trình vì CPU có đa lõi. Để thực hiện điều này bạn có thể thiết lập trong Template của Nginx ví dụ chúng ta có thể cấu hình file nginx.conf.j2 như sau:

user www-data;
worker_processes {{ ansible_processor_cores }};
pid /var/run/nginx.pid;

# Và các cấu hình khác...

Hoặc nếu Server của bạn có nhiều CPU, bạn có thể sử dụng

user www-data;
worker_processes {{ ansible_processor_cores * ansible_processor_count }};
pid /var/run/nginx.pid;

# Và các cấu hình khác...

Tất cả các Fact của Ansible đều bắt đầu với ansible_ và sẵn sàng sử dụng ở bất kỳ chỗ nào mà biến cần sử dụng: các file Variable, Task, và Template

Hãy thử chạy theo cấu hình trên trên máy local, để xem Fact hoạt động trong thực tế như thế nào

# Chạy đối với local server
# Chú ý rằng chúng ta sử dụng "localhost" thay vì định nghĩa trong file hosts
ansible -m setup --connection=local localhost

# Chạy đối với remote server
ansible -i ./hosts remote -m setup

Vault

Chúng ta thường lưu những thông tin nhạy cảm ở trong file Template, File hoặc Variable trong Ansible, đáng tiếc là chúng ta không thể tránh khỏi việc này. Ansible có một giải pháp cho vấn đề này là sử dụng Vault

Vault cho phép bạn mã hoá bất kỳ file YAML nào, mà chúng ta hay sử dụng dưới các file lưu thông tin biến. Vault không mã hoá các file File và Template, chỉ mã hoá các file YAML.

Khi tạo một file được mã hoá, bạn sẽ được yêu cầu nhập mật khẩu để giúp bạn có thể chỉnh sửa phải sau đấy và khi gọi Role hoặc Playbook

Lưu mật khẩu của bạn ở nơi nào đó an toàn

Ví dụ chúng ta có thể tạo một file Variable mới

ansible-vault create vars/main.yml
Vault Password:

Sau khi nhập mật khẩu mã hoá, file sẽ được mở trên Editor mặc địch của bạn thường là Vim hoặc Nano

Bạn có thể gõ lệnh để xem hướng dẫn về Ansible vault như sau

$ ansible-vault -h
Usage: ansible-vault [create|decrypt|edit|encrypt|rekey] \
      [--help] [options] file_name

Options:
  -h, --help  show this help message and exit

Sau đây là ý nghĩa của từng lựa chọn

  • create – Tạo một file mới và mã hoá nó
  • decrypt – Tạo một file text từ một file mã hoá
  • edit – Chỉnh sửa file mã hoá
  • encrypt – Mã hoá một file text
  • rekey – Đổi mật khẩu cho file mã hoá

Nếu bạn muốn mã hoá một file đang tồn tại thì dùng câu lệnh ansible-vault encrypt /path/to/file.yml

Ví dụ: Users

Giả sử chúng ta có Role thứ 2 tên là “users”

cd ~/ansible-example/roles
ansible-galaxy init users

Chúng ta sẽ sử dụng Vault khi tạo một user mới và thiết lập mật khẩu cho chúng. Trong user Role, chúng ta sẽ tạo một file biến để lưu mật khẩu của user  và một public key để thêm vào file authorized_keys của user (giúp truy cập SSH).

Đây là ví dụ về file chứa biến mà chúng ta sẽ tạo và mã hoá bằng Vault, khi chỉnh sửa nó tất nhiên là nó chưa bị mã hoá

Đây là file ~/ansible-example/roles/users/variables/main.yml

admin_password: $6$lpQ1DqjZQ25gq9YW$mHZAmGhFpPVVv0JCYUFaDovu8u5EqvQi.Ih
deploy_password: $6$edOqVumZrYW9$d5zj1Ok/G80DrnckixhkQDpXl0fACDfNx2EHnC
common_public_key: ssh-rsa ALongSSHPublicKeyHere

Chú ý rằng mật khẩu của người dùng cũng được hash. Bạn có thể đọc trong tài liệu của Ansible để biết cách làm thể nào để sinh ra mật khẩu mã hoá. Bạn có thể chạy lệnh sau

# The whois package makes the mkpasswd
# command available on Ubuntu
$ sudo apt-get install -y whois

# Create a password hash
$ mkpasswd --method=SHA-512
Password:

Nó sẽ sinh ra cho bạn mật khẩu hash để sử dụng trong user module

Mật khẩu trong file biến được mã hoá, nhưng chúng ta vẫn mã hoá file yaml chứa các mật khẩu mã hoá này. Thường những file chứa dữ liệu chưa được mã hoá như là token của API hoặc SSH private key, thì việc tạo file mã hoá là vô cùng quan trọng

Sau khi bạn đã cấu hình mật khẩu người dùng và public key trong file biến, chúng ta có thể mã hoá file và sau đó tạo Task sử dụng các biến được mã hoá.

ansible-vault encrypt roles/users/variables/main.yml
> INPUT PASSWORD

Sau đó chúng ta chỉnh sửa file Task được sử dụng để thêm user và sử dụng các biến mã hoá:

Đây là file chúng ta sẽ cập nhập ~/ansible-example/roles/users/tasks/main.yml

---
- name: Create Admin User
  user:
    name: admin
    password: '{{ admin_password }}'
    groups: sudo
    append: yes
    shell: /bin/bash

- name: Add Admin Authorized Key
  authorized_key:
    user: admin
    key: '{{ common_public_key }}'
    state: present

- name: Create Deploy User
  user:
    name: deploy
    password: '{{ deploy_password }}'
    groups: www-data
    append: yes
    shell: /bin/bash

- name: Add Deployer Authorized Key
  authorized_key:
    user: deploy
    key: '{{ common_public_key }}'
    state: present

Những Task này sử dụng trong user module để tạo user mới và truyền mật khẩu user được lưu trong file biến mã hoá

Nó cũng sử dụng authorized_key module để thêm SSH public key để người dùng có thể truy cập đên Server bằng private key trên máy họ

Các biến mã hoá được sử dụng bình thường trong các file Task. Tuy nhiên mục đích để chạy Role này, chúng ta sẽ nói với Ansible để yêu cầu người dùng nhập mật khẩu Vault giúp nó có thể giải mã được các file này.

Nào hày chỉnh sửa file Playbook server.yml để gọi user Role

---
# Local connection here
- hosts: local
  connection: local
  sudo: yes
  roles:
    - nginx
    - user

Để chạy Playbook này chung ta sẽ yêu cầu Ansible hỏi Vault mật khẩu, khi chúng ta chạy đến Role chứa file mã hoá:

ansible-playbook --ask-vault-pass -i ./hosts server.yml

 

 

 

 

 

 

 

 

 

 

 

 

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.