Nội dung:

  • Mở đầu
  • Giới thiệu về Vagrant, Ansible
  • Cấu hình môi trường local step by step
  • Cấu hình các server thật
  • Kết luận
  • Tài liệu tham khảo

Mở đầu

Trước hết mình muốn đề cập đến đối tượng đọc bài viết này: Là nhưng bạn đã có chút kiến thức về vagrant, ansible, Nếu đã là Master rồi thì không cần đọc cũng được :D. Hoặc là những bạn đang tìm hiểu về công nghệ này, hoặc là bạn đang làm việc với một môi trường đã được thiết lập bởi vagrant, ansible nhưng ai đó đã làm hết những thứ đó cho bạn, bạn chỉ việc xài nó và chưa biết cách để có thể tự cấu hình cho 1 dự án riêng của mình.

Bài toán

Ví dụ chúng ta cần thiết lập một môi trường phát triển web, đơn giản mà nói thì cần thiết sẽ có một DB (oracle, mysql .v.v), Ngôn ngữ lập trình web php, java, v.v. Một web server như apache, nginx. Chúng có thể dc cài đặt chung vào 1 máy hoặc phân tách thành các server riêng lẻ để dễ quản lý.

Sau khi các bạn có chút kiến thức về Vagrant và Ansible rồi, chúng ta xem xét bài toán sau:

Đầu tiên chúng ta thiết lập môi trường local theo câu trúc như bên dưới:

**CI server: ** Nơi chứa các script để cài đặt môi trường cho các server khác, chứa script deploy source code v.v. Tóm lại CI server là nơi chứa code Ansible playbook, các sự thay đổi về cấu hình, về deploy đều được thực hiện tại đây, rất linh hoạt và nhanh chóng.

**DB: **ở đây chúng ta có cấu trúc Master-Slave, thật ra trong bài viết này mình nói về vấn đề này, các bạn có thể tìm hiểu ở 1 bài viết khác. Ở đây chỉ là 1 ví dụ.

**Web: **Chúng ta sử dụng php, phpfpm và web server Nginx

Sau khi đã cấu hình xong môi trường local, tiếp theo chúng ta sẽ thiết lập môi trường thật (production). Môi trường thật sẽ có kiến trúc như bên dưới:

**CI server: ** Không có gì thay đổi với môi trường local, chứa source ansible playbook, được cài đặt git và ansible để chạy command.

**Web: ** Chúng ta sử dụng 2 server, ở đây mô phỏng cho giống thật một chút chứ cũng không đề cập đến vấn đề load banlancer gì hết.

**DB: ** Vẫn kiến trúc như local

1. Giới thiệu về Vagrant, Ansible

Vagrant

Các bạn có thể tham khảo các bài viết sau:

  1. https://viblo.asia/p/gioi-thieu-ve-vagrant-aRBeXnExvWE
  2. https://viblo.asia/p/tim-hieu-vagrant-phan-1-1l0rvmDQGyqA
  3. https://viblo.asia/p/tim-hieu-vagrant-phan-2-vagrant-chef-wznVGLqQvZOe

Rõ ràng chúng ta thấy được những điểm mạnh rất lớn của Vagrant, và nếu tiếp tục tìm hiểu thì chúng ta sẽ còn thấy được nhiều hơn. Tuy nhiên dưới góc độ của 1 developer, tôi cho rằng có 1 vấn đề khá lớn đối với Vagrant.

Thường thì trong team, leader sẽ đảm nhiệm việc config vagrant từ đầu đến cuối, điều này giúp ích rất nhiều cho members. Tuy nhiên, nếu trong team có nhiều người mới làm quen với công việc, và tiếp tục sử dụng Vagrant, họ sẽ không thể có cơ hội thực hành config server thực tế. Điều này có thể cản trở việc cải thiện khả năng của họ. Do vậy, chúng ta cần sử dụng Vagrant 1 cách linh hoạt kết hợp với nâng cao khả năng của các members trong team.

Ansible

  1. https://viblo.asia/p/phan-1-tim-hieu-ve-ansible-4dbZNxv85YM
  2. https://tech.vccloud.vn/sys-ops/ansible-phan-2-playbook/

2. Cấu hình môi trường local step by step

Chúng ta bắt đầu tiến hành cấu hình cho local:

2.1 File Vagrant

Tạo thư mục Ansible_Example chứa file Vagrantfile với nội dung như sau:

Vagrant.configure(2) do |config|
  config.vm.box = "bento/centos-6.7"
  config.vm.box_url = "https://atlas.hashicorp.com/bento/boxes/centos-6.7"

  server_configs = [
    {"hostname" => "web",       "ip" => "192.168.33.31", "port" => 2221, "memory_size" => "512"},
    {"hostname" => "db-master", "ip" => "192.168.33.32", "port" => 2222, "memory_size" => "512"},
    {"hostname" => "db-slave",  "ip" => "192.168.33.33", "port" => 2223, "memory_size" => "512"},
  ]

  script = "
sudo yum update -y --disablerepo=\* --enablerepo=base,updates ca-certificates
sudo yum install -y epel-release
sudo yum install -y --enablerepo=epel sshpass git
sudo yum install -y --enablerepo=epel-testing ansible
cd ansible-playbook
cp vagrant/insecure_private_key /home/vagrant/.ssh/id_rsa
cp vagrant/ssh_config /home/vagrant/.ssh/config
chmod -R og-rwx /home/vagrant/.ssh
chown -R vagrant.vagrant /home/vagrant/.ssh
cp vagrant/ansible.cfg /etc/ansible/ansible.cfg
#sudo -u vagrant ansible-galaxy --ignore-errors install -p roles -r requirements.yml
"

  config.vm.define :ci do |ci|
    ci.vm.hostname = "ci"
    ci.vm.box = "bento/centos-6.7"
    ci.vm.network :private_network, ip: "192.168.33.30"
    ci.vm.network :forwarded_port, guest: 22, host: 2220, id: "ssh"
    ci.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--memory", "256"]
      v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
    end
    ci.vm.synced_folder ".", "/home/vagrant/ansible-playbook", owner: "vagrant", group: "vagrant", :create => true, :mount_options => ["fmode=700"]
    ci.vm.provision :shell, inline: script
    ci.ssh.private_key_path = "./vagrant/insecure_private_key"
    ci.ssh.insert_key = false
  end

  server_configs.each do |server_config|
    config.vm.define "#{server_config['hostname']}" do |server|
      server.vm.hostname = server_config['hostname']
      server.vm.box = "bento/centos-6.7"
      server.vm.network :private_network, ip: server_config['ip']
      server.vm.network :forwarded_port, guest: 22, host: server_config['port'], id: "ssh"
      server.vm.provider "virtualbox" do |v|
        v.customize ["modifyvm", :id, "--memory", server_config['memory_size']]
        v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
      end
      server.ssh.private_key_path = "./vagrant/insecure_private_key"
      server.ssh.insert_key = false
    end
  end
end
Vagrant up

Kết quả chúng ta có 4 máy ảo centos-6.7.

Nội dung file Vagrantfile cũng khá dễ hiểu, chỉ lưu ý 1 chổ là sau khi vagrant up xong, tại ci server có thể ssh đến bất kỳ server nào thông qua ssh, vì chúng ta đã chuẩn bị sẵn private key và config trong Vagrantfile.

server.ssh.private_key_path = "./vagrant/insecure_private_key"

Tham khảo config ssh tại:

https://www.vagrantup.com/docs/vagrantfile/ssh_settings.html

2.2 Chuẩn bị Roles

http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html

Đầu tiên nói về Roles trong ansible là gì: Có thể hiểu nôm na roles chứa các tác vụ để thực thi một nhiệm vụ cụ thể gì đó, ví dụ như role dùng để cài đặt nginx, role dùng để cài đặt mysl, roles dùng để thực thi một số lệnh cụ thể mà mình có thể tự định nghĩa hoặc tải về từ Ansible Galaxy, github v.v.

Chúng ta chuẩn bị file requirements.yml trong cùng thư mục với Vagrantfile với nọi dung như sau:

# ansible-galaxy
- src: geerlingguy.repo-epel
  version: 1.1.2
- src: geerlingguy.repo-remi
  version: 1.2.0
- src: jdauphant.nginx
  version: v2.3.1
- src: geerlingguy.mysql
  version: 2.2.3
- src: geerlingguy.php
  version: 3.2.2
  
# jiken-ansible-role
- src: git+ssh://git@gitlab.example.com:10022/jiken-ansible/ansible-role-libselinux-python.git
  name: jiken.libselinux-python

- src: git+ssh://git@gitlab.example.com:10022/jiken-ansible/ansible-role-ca-certificates.git
  name: jiken.ca-certificates

- src: git+ssh://git@gitlab.example.com:10022/jiken-ansible/ansible-role-ulimit.git
  name: jiken.ulimit

- src: git+ssh://git@gitlab.example.com:10022/jiken-ansible/ansible-role-sysctl.git
  name: jiken.sysctl

Thực hiện ssh vào server ci

$cd ansible-playbook/
$ansible-galaxy --ignore-errors install -p roles -r requirements.yml

Sau khi chạy lệnh trên xong, các roles sẽ được tải về.

Tất nhiên ban đầu, chúng ta chưa xác định được cần thiết sẽ có nhưng role gì (cần setting, cài đặt nhưng gì). Trong quá trình cấu trúc server, mỗi khi cần cài đặt thêm gì thì có thể tải roles có sẵn bằng cách thêm vào file requirements.yml và chạy lệnh trên để tải về, hoặc có thể tự mình viết role. Cách viết roles thế nào thì các bạn google nhé.

2.3 Cấu hình môi trường cho các server

Đến đây chúng ta cơ bản đã xong bước chuẩn bị các roles. Tiếp theo chúng ta sẽ đi cấu hình, sử dụng các roles đã có áp vào các server: ci, web, DB_Master, DB_Slave.

Để thực hiện công việc này chúng ta cần 2 file: local.host và file provision.yml có nội dung sau:

local.host

[local_ci]
192.168.33.30

[local_web]
192.168.33.31

[local_db]
192.168.33.32
192.168.33.33


[local:children]
local_ci
local_web
local_db

provision.yml

# local ci
- hosts:
    - local_ci
  connection: local
  remote_user: vagrant
  roles:
    - { role: app.patch-geerlingguy.mysql, tags: ['patch'] }
    - jiken.libselinux-python
    - app.account

# local_web
- hosts:
    - local_web
  remote_user: vagrant
  roles:
    - jiken.libselinux-python
    - jiken.ca-certificates
    - jiken.ulimit
    - jiken.sysctl
    - app.account
    - geerlingguy.repo-epel
    - geerlingguy.repo-remi
    - jdauphant.nginx
    # only for local env because nginx_user is vagrant
    - geerlingguy.php
    - app.pdo_mysql

# local MySQL
- hosts:
    - local_db
  remote_user: vagrant
  roles:
    - jiken.libselinux-python
    - jiken.ca-certificates
    - jiken.ulimit
    - jiken.sysctl
    - app.account
    - geerlingguy.repo-epel
    - geerlingguy.repo-remi 
    - app.mysql-repo
    - geerlingguy.mysql

File provision.yml rất dễ hiểu phải không. cần cài đặt cái gì cho môi trường nào.

Có thể các bạn cũng có thắc mắc chổ này:

[local:children]
local_ci
local_web
local_db

Chúng ta có 3 hostname như trên, và mình đặt nó vô 1 group là local, để làm gì? Giải thích 1 chút là các roles này dùng để cấu hình cho các môi trường như local, dev, stg, product v.v. Nếu như muốn không muốn áp dụng 100% các thiết lập của roles thì mình có thể modify một tý cho từng môi trường:

Ví dụ như roles: jdauphant.nginx (role để cài đặt nginx)

Default: chạy cổng 80, root: /usr/share/nginx/html và index page: index.html

Nếu chúng ta cài roles này cho các môi trường, thì các môi trường đều có chung cấu hình default là như vậy, Xong để đối ứng mềm dẻo cho các môi trường cần có các cấu hình khác nhau thì chúng ta phải sửa lại config một tý. Sửa ở đâu? Không sửa trực tiếp trên roles mà định nghĩa ra các file cho từng môi trường trong folder group_vars.

Ở các bước tiếp theo mình sẽ chỉnh sửa cấu hình cho từng môi trường khác nhau bằng cách tạo ra các file như: all, local, prod .v.v trong thư mục group_vars.

Bây giờ chạy lệnh sau:

$vagrant ssh ci
$cd cd ansible-playbook/
$ansible-playbook -i hosts.local provision.yml --user=vagrant --become

Quá trình cài đặt sẽ mất chút thời gian.

2.4 Thay đổi cấu hình môi trường sử dụng group_vars

Truy cập địa chỉ web: http://192.168.33.31/

Nginx đã hoạt động, Bây giờ ta thử chỉnh sửa 1 tý cấu hình cho môi trường local, bằng cách tạo file có tên local, all trong thư mục group_vars

group_vars/all

example_gui_deploy_path: /var/www/gmo/example-gui/current

# app.account
system_accounts:
  - { name: example, uid: 12121, group: wheel }

# geerlingguy.mysql
mysql_packages:
  - mysql-community-client-5.7.20-1.el6
  - mysql-community-common-5.7.20-1.el6
  - mysql-community-libs-5.7.20-1.el6
  - mysql-community-libs-compat-5.7.20-1.el6
  - mysql-community-server-5.7.20-1.el6
mysql_root_password: "{{ vault_mysql_root_password }}"
mysql_datadir: "{{ _mysql_datadir }}"
mysql_databases:
  - name: example
    collation: utf8_general_ci
    encoding: utf8
    replicate: 1
mysql_users:
  - name: example
    host: 'localhost'
    password: "{{ mysql_password }}"
    priv: 'example.*:ALL'
  - name: example
    host: '{{ _mysql_allow_hosts }}'
    password: "{{ mysql_password }}"
    priv: 'example.*:ALL'
mysql_server_id: "{{ ansible_play_hosts.index(inventory_hostname) + 1 }}"
mysql_replication_role: "{{ _mysql_replication_role }}"
mysql_replication_master: "{{ _mysql_replication_master }}"
mysql_replication_user: "{{ _mysql_replication_user }}"

# geerlingguy.php
php_packages:
  - php
  - php-fpm
  - php-opcache
  - php-devel
  - php-mbstring
  - php-mcrypt
  - php-pecl-memcached
  - php-phpunit-PHPUnit
  - php-pdo
  - php-gd
php_enablerepo: "remi-php70"
php_date_timezone: "Asia/Tokyo"
php_enable_apc: false
php_enable_webserver: true
php_enable_php_fpm: true
php_fpm_pool_user: "{{ process_execution_user }}"
php_fpm_pool_group: "{{ process_execution_user }}"
php_webserver_daemon: nginx
php_fpm_pm_max_children: 200
php_fpm_pm_start_servers: 20
php_fpm_pm_min_spare_servers: 20
php_fpm_pm_max_spare_servers: 20

group_vars/local

nginx_sites:
  default:
    - listen 82 default_server
    - server_name _
    - root "/usr/share/nginx/html"
    - set $yii_bootstrap "example.php"
	- index example.php
    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    - location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index  example.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
      }
vault_mysql_root_password: example!234
vault_mysql_password: example!234

# env variables
execution_env: local

server_name_example: local-id.gmo.jp

process_execution_user: vagrant
ansible_execution_user: vagrant

_ci_server: "{{ groups.local_ci[0] }}"

mysql_user: example
mysql_password: "{{ vault_mysql_password }}"
mysql_port: 3306
mysql_database: example
mysql_hosts: "{{ groups.local_db }}"
_mysql_datadir: /var/lib/mysql
_mysql_allow_hosts: 192.168.33.%
_mysql_replication_master: "{{ mysql_hosts[0] }}"
_mysql_replication_role: "{{ 'master' if ansible_play_hosts.index(inventory_hostname) == 0 else 'slave' }}"
_mysql_replication_user: {
  name: replica,
  host: '{{ _mysql_allow_hosts }}',
  password: "{{ mysql_password }}",
  priv: '*.*:REPLICATION SLAVE',
}
mysql_backup_server: "{{ groups.local_admin[0] }}"

Như đã nói phía trên, file all giúp ta modify các roles, áp dụng cho tất cả các môi trường. File local chứa những thay đổi chỉ dành cho các server trong group local.

2.5 Kiểm tra kết nối web server đến mysql

Tại web server, trong đường dẫn root: /usr/share/nginx/html Tạo file có tên example.php với nội dung sau:

<?php
$servername   = "192.168.33.32";
$database = "example";
$username = "example";
$password = "example!234";

// Create connection
$conn = new mysqli($servername, $username, $password);
// Check connection
if ($conn->connect_error) {
   die("Connection failed: " . $conn->connect_error);
}
  echo "Connected successfully";
?>

Các thông tin kết nối DB như servername, database, username, password đã được định nghĩa trong file modify cấu hình cho local (file group_vars/local).

Khởi động lại ngginx:

$sudo service nginx restart

Truy cập địa chỉ sau để xem connnect thành công chưa: http://192.168.33.31:82/example.php

3. Cấu hình môi trường production

Vậy đến đây xem như môi trường local chúng ta đã cơ bản hoàn thành, web php đã có thể chạy dc. tiếp theo chúng ta sẽ xem xét đến việc deploy lên môi trường thật gồm nhưng công việc gì.

  • 1. Cấu hình Private network cho các server
  • 2. Thiết lập cấu hình network sao cho user bên ngoài chỉ có thể access được đến global ip của web01 và web02, ngoài ra không thể access được vào các server khác, cho dù có mật khẩu root
  • 3. Cấu hình bảo mật cho các server, từ CI server có thể ssh đến bất kỳ server nào
  • 4. Tạo file hosts.prod và thêm vào file provision.yml, tiến hành deploy

Nội dung chi tiết mình sẽ nói trong 1 bài viết khác. (Mấy con server hiện đang được tắt cho đỡ tốn tiền :D)

4. Kết Luận

Đạt được:

Bài viết này được viết nhằm tổng hợp và ôn lại kiến thức sau khi đã được một người đàn anh trong dự án training lại, tất nhiên nó còn nhiều điểm thiếu sót, tuy nhiên như đã nhắc đến phía trên về vấn đề sử dụng vagrant và ansible. Thường thì nhưng người có kinh nghiệm như leader sẽ làm nhưng công việc cấu hình môi trường, nên thường những người ít hoặc chưa có kinh nghiệm về cấu hình server sẽ không được đụng đến, điều đó làm mất cơ hội được thử, chưa kể việc thuê VPS hay Cloud mất nhiều chi phí. Vì vậy bài viết mong giúp đỡ nhưng ai mới bắt đầu với vagrant, ansible có cái nhìn tổng quan, có những kiến thức cơ bản để có thể tự mình cấu hình cho các con server dự án riêng của mình. Đầu tiên là local, nếu có điều kiện thì thuê VPS hay Cloud để làm trên đó. Thực tế àm nói, đụng đến mấy con server thật cảm giác nó khác hoàn toàn với local, điều đó làm mình thất rất thích thú.

Chưa đạt được:

Bài viết còn nhiều cái chưa được đề cập đến như:

  • Một số hoặt động để đảm bảo an toàn cho các server thật (security)
  • Cách viết role như thế nào .vv.
  • Cách cấu hình load balance cho web server
  • Cách cấu hình DB Master - Slave
  • vv.

Tuy còn thiếu nhiều, xong mục tiêu chính vẫn là muốn người đọc có cái nhìn bao quát, chi tiết thì sẽ tự tìm hiểu học tham khảo những bài viết khác.

Thêm một điều nữa, trong quá trình cấu hình, lỗi xuất hiện thường xuyên và việc đọc lỗi và fix dần dần mất rất nhiều thời gian. Vậy cho thấy được để cấu hình server quả thật không phải điều đơn giản. Tuy vậy chỉ có làm nhiều mới có được nhiều kinh nghiệm mà thôi.

5. Tài liệu tham khảo

  1. http://docs.ansible.com/ansible/latest/playbooks.html
  2. https://viblo.asia/p/tim-hieu-vagrant-phan-1-1l0rvmDQGyqA
  3. https://viblo.asia/p/phan-1-tim-hieu-ve-ansible-4dbZNxv85YM
  4. http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html