Thứ Hai, 21 tháng 12, 2020

Giới thiệu Ansible

1. Ansible là gì?

Ansible là một công cụ cho phép tự động hóa việc triển khai (deploy) cài đặt các ứng dụng, thực hiện các tác vụ (task) và quản lý việc cấu hình (configuration) trên nhiều máy cùng lúc.

Ansible phân biệt hai loại máy (machine) hay còn gọi là node:
- Managed nodes hoặc hosts : Là những máy mà ta muốn cấu hình và cài đặt các ứng dụng trên chúng.
- Control node (máy điều khiển) : Ansible được cài đặt trên máy này ở hệ điều hành Linux, Windows không được hỗ trợ để làm control node. Máy này đọc các tập tin định nghĩa cấu hình và chạy Ansible để đẩy các cấu hình này vào các hosts.

Ansible dùng SSH để giao tiếp giữa máy điều khiển và các hosts. Ta cần cài đặt Python và SSH trên các nodes để có thể xài Ansible.


2. Một số khái niệm trong Ansible

- Inventory : là danh sách các hosts.

- Playbook : là tập tin định nghĩa các tác vụ cho Ansible và được viết bằng ngôn ngữ YAML. Nó định nghĩa các bước mà người dùng muốn thực thi trên một máy nào đó. Các tác vụ trong Playbook được thực thi tuần tự.

- YAML (là viết tắt của Yet Another Makeup Language và sau là YAML Ain't Markup Language) : là một ngôn ngữ đặc tả dễ hiểu cho người dùng.

- module : Khi bạn dùng Ansible là bạn dùng các module của Ansible. Các module này được viết bằng Python. Một số module của Ansible như file, apt, user...


3. Ansible hoạt động ra sao

Hình dưới đây miêu tả hoạt động của Ansible

                                             ________
______________ ssh | Host 1 |
| |--------------------->|________|
| | ssh ________
| Ansible |--------------------->| Host 2 |
| control node | |________|
| | ssh ________
|______________|--------------------->| Host n |
______|________ |________|
______|_____ _____|_____
| Playbook | | Inventory |
|____________| |___________|
host 1
host 2
host n

Khi dùng Ansible trên máy điều khiển (control node) ta sẽ cung cấp cho nó tập tin Playbook định nghĩa các tác vụ mà ta muốn thực thi tuần tự trên các host. Danh sách các host này được định nghĩa trong tập tin Inventory.

Ansible sẽ kết nối với các host và đẩy các tác vụ đến các nodes này. Sau đó Ansible thực thi tuần tự các modules định nghĩa trong các tác vụ này trên các host (thông qua kết nối SSH) để thực hiện các thao tác hoặc cài đặt một số phần mềm trên các host này. 

 

4. Căn bản về ngôn ngữ YAML dùng trong Ansible

Như đã nói ở trên Ansible dùng YAML để định nghĩa các tập tin playbook của nó vì ngôn ngữ này dễ hiểu với người dùng.

Mỗi tập tin YAML có thể bắt đầu bằng "---" và kết thúc bằng "..." nhưng điều này không bắt buộc.

YAML dùng cặp key-value để định nghĩa dữ liệu. Chú ý là phải có khoảng trắng sau : và value

Ví dụ : thông tin sinh viên

--- # Optional YAML start syntax
toan:
    name: toan
    number: 1
    sex: male
... # Optional YAML end syntax

Định nghĩa một danh sách trong YAML. Mỗi thành phần của danh sách được viết xuống hàng bắt đầu bằng "- " (- và một khoảng trắng). Các thành phần trong danh sách có cùng khoảng cách đầu dòng như nhau.

Ví dụ :

---
languages:
    - Java
    - Python
    - PHP
    - C++
...

Danh sách bên trong dictionary tức giá trị của một key trong cặp key-value là một danh sách
Ví dụ :

--- 
toan:
name: toan
number: 1
sex: male
    likes:
    - Java
    - Python
    - PHP
...

Danh sách các dictionary
Ví dụ:

--- 
- toan:
name: toan
number: 1
sex: male
likes:
- Java
- Python
- PHP

- tri:
name: tri
number: 2
sex: male
likes:
- C++
- HTML
- JavaScript
...

5. Cài đặt Ansible trên Ubuntu

Ta chỉ cần cài đặt Ansible trên một máy điều khiển tức control node. Máy này cần cài đặt Python 2 (phiên bản 2.7) hoặc Python 3 (phiên bản 3.5 trở lên). Hệ điều hành Windows không được hỗ trợ để làm control node cho Ansible. Trên các managed nodes cũng cần phải cài Python.

Dưới đây là lệnh cài đặt Ansible trên Ubuntu

  $ sudo apt update
  $ sudo apt install software-properties-common
  $ sudo apt-add-repository --yes --update ppa:ansible/ansible
  $ sudo apt install ansible

Sau khi cài xong Ansible, ta kiểm tra phiên bản vừa cài đặt bằng lệnh :
$ ansible --version

 

6. Tạo một tập tin Playbook cho Ansible

Trong tập tin playbook ta định nghĩa những tác vụ ta muốn tự động hóa. Ansible gọi danh sách các tác vụ này (task) là playbook.

Nếu ta định nghĩa hosts là all thì Ansible sẽ thực thi playbook trên toàn bộ các hosts một cách song song. Nhưng các tác vụ trong playbook sẽ được thực thi tuần tự, xong tác vụ này mới đến tác vụ kế tiếp.

Chú ý, cú pháp của YAML chú trọng thụt đầu dòng ngay ngắn và đều nhau, nên khi viết tập tin playbook ta cần chú ý việc thụt đầu dòng cho đúng. 

Dưới đây là nội dung tập tin playbook java11-setup.yml nhằm cài đặt java 11 trên toàn bộ các hosts.

---
- hosts: all
become: yes
vars:
packages:
- openjdk-11-jdk
- openjdk-11-jre
tasks:
- name: add openjdk repository
apt_repository:
repo: ppa:openjdk-r/ppa
state: present

- name: install openjdk 11
apt:
name: "{{ packages}}"
state: present

Giải thích :
- hosts: all : nói Ansible thực thi playbook này cho toàn bộ các host định nghĩa trong hosfile hoặc trong tập tin inventory.

- become: yes : thực hiện các lệnh này bằng sudo user. Xem tại đây https://docs.ansible.com/ansible/latest/user_guide/become.html

- vars: các biến variable định nghĩa bởi người dùng. Ngoài ra còn có một số nguồn biến khác, chẳng hạn như biến của hệ thống (system). Các biến hệ thống được gọi là fact.

- tasks: danh sách các tác vụ ta muốn thực thi.

Ta hãy xem tác vụ đầu tiên trong danh sách này :

  - name: add openjdk repository
apt_repository:
repo: ppa:openjdk-r/ppa
state: present
- name : mô tả ngắn gọn về tác vụ

- apt_repository: là module mà tác vụ này dùng để cấu hình và thực thi. Chi tiết về module này có thể xem tại đây
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_repository_module.html

Ansible cung cấp nhiều module định nghĩa sẵn. Module apt_repository cho phép thêm hoặc xóa một APT repository trong Ubuntu. Hầu hết các module có biến state để định nghĩa cho nó nên làm gì : present (thêm vào, khởi động hay thực thi), absent (xóa đi, dừng lại)

- repo : chuỗi định nghĩa repository

Module apt

Phần vars định nghĩa các package cần cài đặt

  vars: 
packages:
- openjdk-11-jdk
- openjdk-11-jre

tasks:
- name: install openjdk 11
apt:
name: "{{ packages}}"
state: present

Module apt quản lý các gói apt trong Ubuntu.
- name : có thể định nghĩa ở đây một danh sách các package cần cài đặt. Ở đây, danh sách này được định nghĩa riêng trong một biến có tên "packages" trong mục "vars" để cho tác vụ này rõ ràng hơn.

- state: được gán giá trị "present" nghĩa là ansible sẽ cài các gói này nếu chúng chưa được cài, còn nếu chúng đã có rồi thì bỏ qua không thực hiện việc cài chúng nữa. Nói ngắn gọn là đảm bảo sự hiện diện của chúng. Ngoài ra ta có thể gán các giá trị "absent"(đảm bảo chúng không hiện diện), "latest" (đảm bảo chúng hiện diện và có phiên bản mới nhất), "build-deps"(đảm bảo những gói mà chúng phụ thuộc vào cũng hiện diện), hoặc "fixed" (gắng sửa lại hệ thống nếu có những phụ thuộc bị thiếu).


* Một cách dễ hiểu, tập tin playbook java11-setup.yml ở trên tương đương với việc thực hiện các lệnh sau để cài đặt java 11 :

sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt install openjdk-11-jdk
sudo apt install openjdk-11-jre

7. Tạo một tập tin Inventory cho Ansible

Như đã đề cập trong mục (3) ở trên về cách hoạt động của Ansible. Ta vừa định nghĩa các tác vụ để Ansible thực thi. Giờ ta phải cung cấp danh sách các host để Ansible tiến hành thực hiện các tác vụ này trên đó. Ở đây ta tạo một tập tin Inventory để cung cấp danh sách các host này. Thông tin cụ thể về Inventory có thể tham khảo tại đây https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

Ta đặt tên tập tin Inventory này là inventory-all-vms.yml và nó có nội dung như sau :

all:
children:
ubuntu_servers:
hosts:
jenkins_ubuntu18:
ansible_host: jenkins.local

toan_local:
hosts:
toan_ubuntu16:
ansible_host: toan.local

Trong tập tin trên, ta khai báo hai hosts là jenkins.local và toan.local vì đây là hai VM mà tôi dùng Vagrant để tạo ra. Ngoài ra bạn cũng có thể dùng adresse ip để định nghĩa cho các host này.

Đến đây ta đã có tập tin playbook và inventory, từ control node của ansible ta có thể dùng lệnh ansible-playbook để thực hiện việc cài đặt java 11 (định nghĩa trong playbook) cho các hosts (định nghĩa trong inventory) theo cú pháp sau :

ansible-playbook java11-setup.yml -i inventory-all-vms.yml

 

8. Dùng Vagrant tạo control node và hosts cho Ansible và cấu hình cho chúng

Dưới đây, ta sẽ dùng Vagrant để tạo một máy ảo làm control node và hai máy ảo làm host cho ansible. Xem chi tiết việc dùng Vagrant để tạo VM tại đây https://openspacevn.blogspot.com/2020/11/cai-at-vagrant-tren-windows.html

Tập tin Vagrantfile định nghĩa ba máy ảo này có nội dung như sau :

Vagrant.configure("2") do |config|

#common part of vms
BOX_IMAGE = "ubuntu/bionic64" # bionic 64 = ubuntu 18.04 64 bits

#ansible vm description
config.vm.define "ansible" do |ansible|
ansible.vm.box = BOX_IMAGE
ansible.vm.hostname = "ansible"
ansible.vm.network "private_network", ip: "10.0.0.10", virtualbox__intnet: true
ansible.vm.provision "shell", inline: "apt install python -yq", privileged: true
ansible.vm.provision "shell", inline: "sudo apt-get install -yq software-properties-common", privileged: true
ansible.vm.provision "shell", inline: "sudo apt-add-repository -y ppa:ansible/ansible", privileged: true
ansible.vm.provision "shell", inline: "sudo apt-get update -yq", privileged: true
ansible.vm.provision "shell", inline: "sudo apt-get install -yq ansible", privileged: true
ansible.vm.provision "shell", inline: "sudo apt-get install -yq mc", privileged: true
end

#jenkins vm description
config.vm.define "jenkins" do |jenkins|
jenkins.vm.box = BOX_IMAGE
jenkins.vm.hostname = "jenkins"
jenkins.vm.network "private_network", ip: "10.0.0.11", virtualbox__intnet: true
jenkins.vm.provision "shell", inline: "sudo apt-get install -yq mc", privileged: true
end

config.vm.define "toan" do |toan|
toan.vm.box = "ubuntu/xenial64"
toan.vm.hostname = "toan"
toan.vm.network "private_network", ip: "10.0.0.12", virtualbox__intnet: true
toan.vm.provision "shell", inline: "sudo apt-get install -yq mc", privileged: true
end

end

Như ta thấy trong tập tin trên, máy ảo control node có tên "ansible" và trên đó ta sẽ cài đặt Python và Ansible. Hai máy ảo host có tên là "jenkins" và "toan"

* Cấu hình để control node có thể kết nối với các host thông qua SSH

Ta mở màn hình lệnh tại thư mục chứa tập tin Vagrantfile, chẳng hạn tập tin này được lưu tại thư mục C:\vagrant\ubuntu

Tại commande line ở thư mục này, ta đánh lệnh : "vagrant up ansible" để khởi động control node tên là ansible

C:\vagrant\ubuntu>vagrant up ansible


Sau khi máy ảo ansible này khởi động, ta kết nối vào nó bằng lệnh "vagrant ssh ansible"

C:\vagrant\ubuntu>vagrant ssh ansible

Tương tự ta cũng khởi động và kết nối vào máy ảo "toan" như sau

C:\vagrant\ubuntu>vagrant up toan
C:\vagrant\ubuntu>vagrant ssh toan

 a) Cấu hình các hosts

Tại máy ảo "toan" vừa kết nối đến này, ta sẽ cấu hình để cho control node "ansible" có thể kết nối ssh đến nó như sau.

Trước tiên ta mở tập tin cấu hình ssh /etc/ssh/sshd_config

vagrant@toan:~$ sudo vi /etc/ssh/sshd_config

Rồi kiểm tra các dòng thông tin sau nếu chưa được kích hoạt (tức có dấu # ở đầu dòng hoặc có giá trị là "no") thì ta kích hoạt nó bằng cách xóa dấu # ở đầu dòng hoặc đổi giá trị thành "yes" :

PubkeyAuthentication yes
AuthorizedKeysFile <thông tin đường dẫn>
PasswordAuthentication yes

Sau đó ta khởi động lại service sshd để cập nhật cấu hình vừa thay đổi với lệnh "sudo service sshd restart" :

vagrant@toan:~$ sudo service sshd restart

Với máy ảo "jenkins", ta cũng khởi động và cấu hình tương tự như máy ảo "toan":

C:\vagrant\ubuntu>vagrant up jenkins
C:\vagrant\ubuntu>vagrant ssh jenkins
vagrant@jenkins:~$ sudo vi /etc/ssh/sshd_config
vagrant@jenkins:~$ sudo service sshd restart

b) cấu hình cho control node "ansible"

Trên máy ảo "ansible" ta kiểm tra xem máy đã có public key rsa hay chưa :

vagrant@ansible:~$ ls -l ~/.ssh/
total 4
-rw------- 1 vagrant vagrant 389 Dec 21 09:07 authorized_keys

Trong trường hợp này ta thấy chưa có, ta sẽ phát sinh public key cho máy ảo "ansible" với lệnh ssh-keygen :

vagrant@ansible:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa): <nhấn enter để mặc định phát sinh các tập tin này ở thư mục .ssh>
Enter passphrase (empty for no passphrase): <nhấn enter>
Enter same passphrase again: <nhấn enter>
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:XijkRt3uI++yKn06gMC+n6kvpAUHM5eOs5r8HT1RkBs vagrant@ansible
The key's randomart image is:
+---[RSA 2048]----+
|   .   ..        |
|+ o    E..       |
|.B    o +..      |
|=.o  + ..o       |
|.* .  +.S o      |
|.oo ...o.o       |
|=o.  + oo o      |
|++  = + +o .     |
| .** oo=.+o      |
+----[SHA256]-----+

Giờ đây public và private key rsa đã được phát sinh :

vagrant@ansible:~$ ls -l ~/.ssh/
total 12
-rw------- 1 vagrant vagrant 389 Dec 21 09:07 authorized_keys
-rw------- 1 vagrant vagrant 1675 Dec 21 09:57 id_rsa
-rw-r--r-- 1 vagrant vagrant 397 Dec 21 09:57 id_rsa.pub

Đến đây ta sao chép public key của máy ảo "ansible" đến các host để cho phép nó kết nối với các máy này thông qua ssh với lệnh ssh-copy-id như sau :

vagrant@ansible:~$ sudo ssh-copy-id vagrant@toan.local
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/vagrant/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed

/usr/bin/ssh-copy-id: ERROR: ssh: Could not resolve hostname toan.local: Temporary failure in name resolution

Nếu bị lỗi khi kết nối ssh với hostname "toan.local" như trên ta có thế thay thế hostname bằng địa chỉ ip "10.0.0.12". Trong tập tin Vagrantfile địa chỉ ip ứng với hostname "toan" là "10.0.0.12"

vagrant@ansible:~$ sudo ssh-copy-id vagrant@10.0.0.12
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/vagrant/.ssh/id_rsa.pub"
The authenticity of host '10.0.0.12 (10.0.0.12)' can't be established.
ECDSA key fingerprint is SHA256:65N5OhnmyUuoJ0F7qIOa33eA0OGci4CjP1C/aePIlvw.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
vagrant@10.0.0.12's password: <password là vagrant>

Number of key(s) added: 1

Now try logging into the machine, with: "ssh 'vagrant@10.0.0.12'"
and check to make sure that only the key(s) you wanted were added.

Để kiểm tra việc thêm public key thành công, ta kết nối đến host, rồi thoát ra bằng lệnh exit :

vagrant@ansible:~$ ssh vagrant@10.0.0.12
vagrant@toan:~$ exit

Tương tự, ta cũng sao chép public key của máy ảo "ansible" vào máy ảo "jenkins" như trên :

vagrant@ansible:~$ sudo ssh-copy-id vagrant@jenkins.local

* Dùng ansible để cài đặt java 11

Đến đây, ta đã tạo được một mạng lưới gồm control node "ansible" và hai host "toan" và "jenkins" và cấu hình để vm "ansible" có thể kết nối ssh với hai host này. Vậy là từ vm "ansible" ta có thể dùng hai tập tin java11-setup.yml và inventory-all-vms.yml định nghĩa ở trên để cài đặt java 11 lên hai vm "toan" và "jenkins".

Trong thư mục C:\vagrant\ubuntu (tức thư mục chứa tập tin Vagrantfile), ta tạo thư mục "toan" và chép hai tập tin java11-setup.yml và inventory-all-vms.yml vào đây

Từ vm "ansible", ta có thể truy xuất đến hai tập tin này tại /vagrant/toan

Chú ý, nếu ta bị lỗi không kết nối được đến host bằng hostname như ở trên, thì ta phải thay đổi hostname bằng địa chỉ ip trong tập tin inventory-all-vms.yml như sau :

all:
children:
ubuntu_servers:
hosts:
jenkins_ubuntu18:
ansible_host: 10.0.0.11

toan_local:
hosts:
toan_ubuntu16:
ansible_host: 10.0.0.12

Chẳng hạn nếu ta chỉ muốn cài java 11 trên host "toan" thôi chứ không phải trên tất cả các host, thì trong tập tin java11-setup.yml, ta định nghĩa host là toan_ubuntu16 :

- hosts: toan_ubuntu16

Sau đó dùng lệnh ansible-playbook để thực thi :

vagrant@ansible:~$ ansible-playbook /vagrant/toan/java11-setup.yml -i /vagrant/toan/inventory-all-vms.yml

PLAY [toan_ubuntu16] ***************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [toan_ubuntu16]

TASK [add openjdk repository] ******************************************************************************************
changed: [toan_ubuntu16]

TASK [install openjdk 11] **********************************************************************************************
changed: [toan_ubuntu16]

PLAY RECAP *************************************************************************************************************
toan_ubuntu16 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ta thấy java 11 được cài đặt trên host toan_ubuntu16.

Nếu muốn thực hiện cài đặt java 11 trên tất cả các host thì ta định nghĩa host là all trong tập tin java11-setup.yml

- hosts: all

Thực hiện lại lệnh ansible-playbook :

vagrant@ansible:~$ ansible-playbook /vagrant/toan/java11-setup.yml -i /vagrant/toan/inventory-all-vms.yml

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

TASK [Gathering Facts] *************************************************************************************************
ok: [jenkins_ubuntu18]
ok: [toan_ubuntu16]

TASK [add openjdk repository] ******************************************************************************************
ok: [toan_ubuntu16]
changed: [jenkins_ubuntu18]

TASK [install openjdk 11] **********************************************************************************************
ok: [toan_ubuntu16]
changed: [jenkins_ubuntu18]

PLAY RECAP *************************************************************************************************************
jenkins_ubuntu18 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
toan_ubuntu16 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ta thấy java 11 được cài đặt trên cả hai host toan_ubuntu16 và jenkins_ubuntu18.


Thứ Sáu, 27 tháng 11, 2020

Cài đặt Vagrant trên Windows

Vagrant là một công cụ cho phép triển khai (deploy) các máy ảo (virtual machine) một cách nhanh chóng và tự động. Chẳng hạn bạn phát triển một ứng dụng Java trên Windows và muốn kiểm tra ứng dụng này có chạy tốt trên Linux hay không, Vagrant sẽ giúp bạn thực hiện điều này. Nó giúp bạn tạo ra một môi trường (environment) để kiểm tra (test) và phát triển phần mềm (developement).

Mục đích của Vagrant là cho phép triển khai và cấu hình (configure) các máy ảo một cách tự động từ các boxes.

Muốn dùng Vagrant thì trước hết cần phải cài đặt trước một phần mềm máy ảo (chẳng hạn VirtualBox hay VMware)

 

Một số thuật ngữ

- Vagrant Box: được xem là ảnh (image) hay bản mẫu (template) mà ta sẽ sử dụng để triển khai các máy ảo của chúng ta. Bạn có thể tìm các box tại đây https://app.vagrantup.com/boxes/search

- Packer : là công cụ cho phép bạn tạo các ảnh, xem tại đây https://learn.hashicorp.com/tutorials/packer/getting-started-build-image

- Vagrantfile : là tập tin cấu hình mà Vagrant dùng để triển khai và cấu hình các máy ảo

- Provider : là phần mềm máy ảo cho phép Vagrant triển khai các máy ảo trên đó. Như VirtualBox, VMware, Hyper-V, Docker

- Provisioner : Một khi máy ảo được triển khai, Provisionner sẽ khởi động tiến trình cài đặt và cấu hình tự động cho các máy ảo bằng : Script Shell, Ansible... 

 

Cài đặt VirtualBox

Ở đây ta sẽ dùng VirtualBox là phần mềm cho phép cài đặt máy ảo trên Windows. VirtualBox là phần mềm open source miễn phí, bạn có thể tải nó về tại đây https://www.virtualbox.org/wiki/Downloads

Chú ý là Provider phải cài đặt trước khi sử dụng Vagrant.


Cài đặt Vagrant

Cài đặt Vagrant khá đơn giản, bạn tải phiên bản của nó ứng với hệ điều hành của máy bạn về tại địa chỉ này https://www.vagrantup.com/downloads.html để cài đặt.

Sau khi việc cài đặt hoàn tất, trong commande line trên Windows bạn có thể đánh lệnh sau để kiểm tra version của Vagrant vừa cài đặt :

vagrant version

Sẽ hiển thị kết quả :
Installed Version: 2.2.14
Latest Version: 2.2.14

You're running an up-to-date version of Vagrant!
Như vậy là bạn đã cài đặt xong Vagrant, tiếp theo ta sẽ xem sử dụng Vagrant như thế nào.


Sử dụng Vagrant trong Windows

Trước tiên, ta tạo một thư mục cho dự án, chẳng hạn C:\vagrant\ubuntu_bionic64 rồi ta mở command line từ thư mục này (shift+ nhấp chuột phải rồi chọn command line)

Danh sách các images của ubuntu cho Vagrant tại đây https://app.vagrantup.com/ubuntu
Chẳng hạn ta muốn dùng một máy ảo Ubuntu 18.04, đánh dòng lệnh sau đây :

vagrant init ubuntu/bionic64

Lệnh này sẽ tự động tạo ra tập tin Vagrantfile trong thư mục hiện hành, chính là trong tập tin này mà ta sẽ định nghĩa những thứ ta muốn tự động hóa. Hiện tại, tập tin này có nội dung sau :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
end

Tiếp theo, ta đánh lệnh :
vagrant up


Vì đây là lần đầu tiên ta dùng Vagrant nên nó sẽ tải box định nghĩa trong Vagrantfile về rồi sau đó máy ảo Vagrant này sẽ được cài đặt và khởi động .

Đôi khi ta gặp lỗi sau :

Error :
The box 'ubuntu/bionic64' could not be found or
could not be accessed in the remote catalog. If this is a private
box on HashiCorp's Vagrant Cloud, please verify you're logged in via
`vagrant login`. Also, please double-check the name. The expanded
URL and error message are shown below:

URL: ["https://vagrantcloud.com/ubuntu/bionic64"]
Error: SSL certificate problem: self signed certificate in certificate chain

Nếu gặp lỗi SSL như trên, ta có thể thêm box bằng cách dùng option --insecure như sau :
vagrant box add --insecure ubuntu/bionic64 
Rồi gõ lại lệnh vagrant up. Một khi máy ảo này khởi động thành công không lỗi, có thể kết nối vào nó với lệnh :
vagrant ssh

Để dừng máy ảo, gõ lệnh :

vagrant halt

Để xóa một máy ảo Vagrant, gõ lệnh :
vagrant destroy


Thứ Hai, 5 tháng 10, 2020

Một số (fake/mock) server fpt, http dùng trong test

Đôi khi ta cần dùng các server ftp, http trong các test chẳng hạn trong unit test. Dưới đây giới thiệu một số server như thế

FakeFtpServer

http://mockftpserver.sourceforge.net/fakeftpserver-getting-started.html
Tải jar về tại đây : https://mvnrepository.com/artifact/org.mockftpserver/MockFtpServer/2.7.1
MockFtpServer-2.7.1.jar

FakeFtpServer  Rất dễ sử dụng để test như dưới đây

import org.mockftpserver.fake.FakeFtpServer;
import org.mockftpserver.fake.UserAccount;
import org.mockftpserver.fake.filesystem.DirectoryEntry;
import org.mockftpserver.fake.filesystem.FileSystem;
import org.mockftpserver.fake.filesystem.UnixFakeFileSystem;

public class FakeFtpServerUtil {
	private static FakeFtpServer fakeFtpServer;
	private static final String DEFAULT_HOME_DIR = "/test";
	private static final String DEFAULT_USER = "toan";
	private static final String DEFAULT_PASSWORD = "matkhau";
	private static final int DEFAULT_PORT = 21;

	public static void setUpFakeFtpServer() {
		fakeFtpServer = new FakeFtpServer();
        fakeFtpServer.setServerControlPort(DEFAULT_PORT);

        FileSystem fileSystem = new UnixFakeFileSystem();
        DirectoryEntry directoryEntry = new DirectoryEntry(DEFAULT_HOME_DIR);
		fileSystem.add(directoryEntry);
        fakeFtpServer.setFileSystem(fileSystem);

        UserAccount userAccount = new UserAccount(DEFAULT_USER, DEFAULT_PASSWORD, DEFAULT_HOME_DIR);
        fakeFtpServer.addUserAccount(userAccount);

        fakeFtpServer.start();
	}

	public static void shutdownFtpServer() {
		if (fakeFtpServer != null) {
			fakeFtpServer.stop();
			fakeFtpServer = null;
		}
	}

}

FakeFtpServer tuy rất tiện để dùng trong test nhưng có nhược điểm là dùng bộ nhớ trong cho hệ thống tập tin của server ftp, nên nếu ta muốn dùng một thư mục vật lý cho hệ thống lưu tập tin của server ftp thì Apache FtpServer là một lựa chọn tốt như trình bày dưới đây.


Apache FtpServer

https://mina.apache.org/ftpserver-project/embedding_ftpserver.html
 

Tải về tại đây
https://mvnrepository.com/artifact/org.apache.ftpserver/ftpserver-core/1.1.1
https://mvnrepository.com/artifact/org.apache.ftpserver/ftplet-api/1.1.1
https://mvnrepository.com/artifact/org.apache.mina/mina-core/2.1.4

import java.util.ArrayList;
import java.util.List;

import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.filesystem.nativefs.NativeFileSystemFactory;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FileSystemView;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.apache.ftpserver.usermanager.impl.WriteRequest;

public class EmbeddedFtpServer {

	private static FtpServer ftpServer;
	
	public static void setUpFtpServer() throws FtpException {
		FtpServerFactory serverFactory = new FtpServerFactory();
		ListenerFactory factory = new ListenerFactory();
		
		factory.setPort(21);
//		NativeFileSystemFactory fsf = new NativeFileSystemFactory() {
//	        @Override
//	        public FileSystemView createFileSystemView(final User user) throws FtpException {
//	            final FileSystemView fsView = super.createFileSystemView(user);
//	            fsView.changeWorkingDirectory("/test");
//	            return fsView;
//	        }
//		};
//	    fsf.setCreateHome(true);
//	    serverFactory.setFileSystem(fsf);
	    
		// replace the default listener
		serverFactory.addListener("default", factory.createListener());
		
		// create user
		final PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
	    final UserManager userManager = userManagerFactory.createUserManager();
	    BaseUser user = new BaseUser();
	    user.setName("toan");
	    user.setPassword("matkhau");
	    List authorities = new ArrayList();
        WritePermission writePermission = new WritePermission();
        writePermission.authorize(new WriteRequest());
        authorities.add(writePermission);
        user.setAuthorities(authorities);
        
	    userManager.save(user);
	    serverFactory.setUserManager(userManager);
	    
		ftpServer = serverFactory.createServer();
		ftpServer.start();
	}
	
	public static void shutdownFtpServer() {
		if (ftpServer != null) {
			ftpServer.stop();
			ftpServer = null;
		}
	}
}

 Chú ý:
- Trong Ubuntu, cần trao quyền đọc/viết để test có thể thao tác trên thư mục mà server ftp dùng, chẳng hạn trao quyền cho mọi người dùng trên thư mục "/toan" như sau :

$ chmod -R 777 /toan

- Trong Ubuntu, người dùng không phải là root chỉ có quyền sử dụng các cổng lớn hơn 1024, nên trong các test nên đổi port cho server ftp lớn hơn giá trị này.

 

MockServer

https://www.mock-server.com/
 

Tải về tại đây http://search.maven.org/remotecontent?filepath=org/mock-server/mockserver-netty/5.11.1/mockserver-netty-5.11.1-jar-with-dependencies.jar


import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;

import org.mockserver.integration.ClientAndServer;

public class MockHttpServerUtil {
	private static ClientAndServer mockHttpServer;

	public static void setUpMockHttpServer() {
		mockHttpServer = startClientAndServer(5050);
		
		mockHttpServer
		.when(request()
			.withPath("/toan/rootTest/")
		)
		.respond(response()
				.withStatusCode(200));
		
		mockHttpServer
		.when(request()
			.withPath("/toan/rootTest/test.txt")
		)
		.respond(response()
				.withStatusCode(200));

	}
	
	public static void shutdownFtpServer() {
		if (mockHttpServer != null) {
			mockHttpServer.stop();
			mockHttpServer = null;
		}
	}

}

MockServer dùng rất tốt trong việc giả lập các trả lời cho các truy vấn http, tuy nhiên nó không cho phép ta thao tác trực tiếp với hệ thống thư mục vật lý như một server http thực thụ cho phép. Java Lightweight HTTP Server cho phép thực hiện điều này như trình bày bên dưới.


Java Lightweight HTTP Server

https://www.freeutils.net/source/jlhttp/

Tải về tại đây : https://mvnrepository.com/artifact/net.freeutils/jlhttp/2.5

 

import java.io.File;
import java.io.IOException;

import net.freeutils.httpserver.HTTPServer;
import net.freeutils.httpserver.HTTPServer.ContextHandler;
import net.freeutils.httpserver.HTTPServer.FileContextHandler;
import net.freeutils.httpserver.HTTPServer.Request;
import net.freeutils.httpserver.HTTPServer.Response;
import net.freeutils.httpserver.HTTPServer.VirtualHost;

public class EmbeddedHttpServer {
	private static HTTPServer httpServer;

	public static void setUpFakeHttpServer() throws IOException {
		httpServer = new HTTPServer(5020);
		VirtualHost host = httpServer.getVirtualHost(null);
		httpServer.addVirtualHost(host);
		host.addContext("/toan/rootTest/", new ContextHandler() {
			
			@Override
			public int serve(Request req, Response resp) throws IOException {
		        resp.send(200, "OK");
		        return 0;
		    }
		});
		
		host.addContext("/toan/rootTest/test.txt", new ContextHandler() {
			
			@Override
			public int serve(Request req, Response resp) throws IOException {
		        resp.send(200, "OK");
		        return 0;
		    }
		});
		
		File tempDirectory = new File("/toan/rootTest");
		host.setAllowGeneratedIndex(true);
		host.addContext("/toan/rootTest", new FileContextHandler(tempDirectory));
		
		httpServer.start();
	}
	
	public static void shutdownFtpServer() {
		if (httpServer != null) {
			httpServer.stop();
			httpServer = null;
		}
	}

}

 

Thứ Ba, 29 tháng 9, 2020

Zip file Java

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


public class ZipFiles {

	public static void main(String[] args) throws Exception {
    	// zip single file
		String fileContent = "Test zip files";
		String fileName = "Test.txt";
        String zipFileName = "toan.zip"
		zipSingleFile(zipFileName, fileName, fileContent);
        
        // zip many files
        String fileContent1 = "Content file 1";
		String fileContent2 = "Content file 2";
		String zipFileName2 = "manyZip.zip";
		zipFiles(zipFileName2, fileContent1, fileContent2);

	}
    
	public static void zipSingleFile(String zipFileName, String fileName, String fileContent) {
		File file = new File(zipFileName);
		try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file))) {
			ZipEntry zipEntry = new ZipEntry(fileName);
			zos.putNextEntry(zipEntry);

			byte[] data = fileContent.getBytes();
			zos.write(data, 0, data.length);
			zos.closeEntry();

		} catch (IOException e) {
			System.out.println("error while zipping file " + zipFileName);
		}
	}

	public static void zipFiles(String zipFileName, String fileContent1, String fileContent2) {
		File file = new File(zipFileName);
		try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file), StandardCharsets.UTF_8)) {
			zipFileEntry("file1.txt", fileContent1, zos);
			zipFileEntry("file2.txt", fileContent2, zos);
		} catch (IOException e) {
			System.out.println("error while zipping file " + zipFileName);
		}
	}

	public static void zipFileEntry(String fileName, String fileContent, ZipOutputStream zos)
			throws IOException {
		ZipEntry zipEntry = new ZipEntry(fileName);
		zos.putNextEntry(zipEntry);

		byte[] data = fileContent.getBytes("UTF-8");
		zos.write(data, 0, data.length);
		zos.closeEntry();
	}

}


Encode base 64

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.commons.codec.binary.Base64;

public class TestUtils {

	public static String encoderBase64(String filePath) {
        String base64File = "";
        File file = new File(filePath);
        try (FileInputStream imageInFile = new FileInputStream(file)) {
            byte fileData[] = new byte[(int) file.length()];
            imageInFile.read(fileData);
            base64File = Base64.encodeBase64String(fileData);
        } catch (FileNotFoundException e) {
            System.out.println("File not found" + e);
        } catch (IOException ioe) {
            System.out.println("Exception while reading the file " + ioe);
        }
        return base64File;
    }

}

Jar : commons-codec-1.6.jar

Phát sinh code Java từ WSDL

Ta có thể dùng lệnh wsimport để tạo ra mã nguồn Java cho dịch vụ web Soap từ một tập tin wsdl như sau :

$ wsimport -keep ToanService.wsdl

Nếu tập tin wsdl phụ thuộc vào một vài tập tin xml(xsd), ta có thể gọi thêm các tập tin ấy khi phát sinh code như sau :
$ wsimport -extension -keep -b Toan1_binding.xml -b Toan2_binding.xml ToanService.wsdl

Chương trình wsimport nằm trong thư mục bin của Java cài đặt trên máy, chẳng hạn :
C:\Java\jdk1.7.0_80\bin\wsimport.exe

Kiểm tra phiên phản của JAX-WS :
$ "C:\Java\jdk1.7.0_80\bin\wsimport.exe" -version

Thông thường với wsimport là ta đủ để phát sinh mã nguồn Java từ một WSDL, tuy nhiên phiên bản mới của wsimport không còn hỗ trợ một vài cấu trúc quá cũ, chẳng hạn như rpc/encoded :
parsing WSDL...
[ERROR] "Use of SOAP Encoding is not supported.
SOAP extension element on line 44 in file ToanService.wsdl has use="encoded" "

Dòng bị báo lỗi có định nghĩa như sau :
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://ToanService/" use="encoded"/>

Không còn dịch vụ web hiện đại nào còn dùng đến cấu trúc rpc/encoded cổ lỗ này nữa, giờ đa phần sử dụng document/literal

Tuy nhiên Apache Axis v1 vẫn có thể giúp ta phát sinh code Java cho các dịch vụ web dùng cấu trúc rpc/encoded này nếu cần. Ta phải tải về axis-1.4.jar và các jars mà nó phụ thuộc để phát sinh code Java bằng lệnh sau :

$ java -cp axis-1.4.jar;commons-logging-1.1.jar;jaxrpc-api-1.1.jar;commons-discovery-0.2.jar;saaj-1.1.jar;wsdl4j-1.4.jar;activation-1.1.jar;mail-1.4.jar org.apache.axis.wsdl.WSDL2Java ToanService.wsdl


Khi dùng Apache Axis, có thể gọi client web như sau :
ToanServiceServiceLocator service = new ToanServiceServiceLocator();
ToanServiceSoapBindingStub soapClient = new ToanServiceSoapBindingStub(new URL(service.getToanServiceAddress()), service);
soapClient.getVersion();

Ta có thể xem soap request, response và lỗi trả về nếu có như sau :
String soapRequest = _call.getMessageContext().getRequestMessage().getSOAPPartAsString();
String soapResponse = _call.getMessageContext().getResponseMessage().getSOAPPartAsString();
if (_call.getResponseMessage().getSOAPPart().getEnvelope().getBody().getFault() != null) {
responseFault = _call.getResponseMessage().getSOAPPart().getEnvelope().getBody().getFault().getFaultCode();
}

Thứ Ba, 18 tháng 8, 2020

Cài đặt Tomcat trong Ubuntu

Ubuntu cung cấp các gói tomcat, cập nhật thông tin các gói có thể cài đặt :

$ sudo apt-get update

Lệnh sau cho biết Ubuntu có những gói tomcat nào
$ sudo apt-cache search tomcat

Chẳng hạn ta chọn tomcat7, cài đặt nó với lệnh sau :
$ sudo apt-get install tomcat7

Sau khi cài đặt, các tập tin cấu hình của Tomcat nằm trong thư mục /etc/tomcat7

Mặc định, Tomcat dùng cổng 8080 cho kết nối HTTP, thông tin này được định trong tập tin /etc/tomcat7/server.xml , chẳng hạn:
<Connector port="8080" protocol="HTTP/1.1" 
			connectionTimeout="20000"  redirectPort="8443" />
...
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


Nếu cổng 8080, đã có dịch vụ khác dùng rồi thì phải thay đổi cổng HTTP này cho tomcat với lệnh sau chẳng hạn :
$ sudo vi /etc/tomcat7/server.xml

Mặc định, Tomcat sẽ tìm JVM OpenJDK, rồi đến JVM Sun rồi mới tới các JVM khác. Ta có thể buộc Tomcat dùng một JVM nhất định bằng cách định nghĩa JAVA_HOME trong tập tin /etc/default/tomcat7 chẳng hạn mở tập tin này:
$ sudo vi /etc/default/tomcat7

Rồi định nghĩa JAVA_HOME
JAVA_HOME=/usr/lib/jvm/java-7-oracle

Các user và role (nhóm) được định nghĩa trong tập tin /etc/tomcat7/tomcat-users.xml chẳng hạn :
<role rolename="admin"/>
<user username="tomcat" password="tomcat7" roles="admin"/>

Khi cài đặt gói tomcat7 như trên thì một user và nhóm tomcat7 cũng được thêm vào trong Ubuntu, ta có thể thấy cụ thể điều này như sau :

Danh sách các group nằm trong tập tin /etc/group ta xem nó với lệnh sau chẳng hạn :
$ cat /etc/group
Xem danh sách các user :
$ cat /etc/passwd

Vì lý do an toàn, mặc định user tomcat7 không có quyền viết trong thư mục /etc/tomcat7 . Một số chức năng quản trị ứng dụng web cần có quyền viết trong thư mục này, ta cần phải trao quyền cần thiết này cho các user của nhóm tomcat7 :
$ sudo chgrp -R tomcat7 /etc/tomcat7
$ sudo chmod -R g+w /etc/tomcat7 


* Để kiểm tra xem tomcat sau khi cài đặt có hoạt động không có lỗi hay không, ta có thể cài một gói ví dụ sau để kiểm tra :
$ sudo apt-get install tomcat7-examples

Sau đó kiểm tra có thể xem ứng dụng này tại địa chỉ :
http://localhost:8080/examples


Trong trường hợp bị lỗi, xem thêm thông tin lỗi với lệnh :
$ sudo service tomcat7 status

Hoặc xem chi tiết log :
$ sudo cat /var/log/tomcat7/catalina.out


Kiểm tra xem port 8080 đã có dịch vụ nào dùng chưa :
$ netstat -lntu | grep 8080

Nếu có dịch vụ khác xài rồi thì phải đổi port này.



* Các lệnh khởi động và dừng tomcat :

$ sudo service tomcat7 stop
$ sudo service tomcat7 start

Các tập tin/thư mục liên quan đến tomcat :
/etc/init.d/tomcat7
/usr/share/tomcat7
/var/lib/tomcat7/
/var/log/tomcat7/
/var/cache/tomcat7/

 

* Đổi người dùng cho tomcat

Khi cài đặt gói tomcat7 trong Ubuntu, người dùng  và nhóm mặc định của tomcat được cấu hình là tomcat7:tomcat7. Chẳng hạn ta muốn chọn người dùng 'toan' cho Tomcat, trước hết dừng Tomcat.

$ sudo service tomcat7 stop

Ta có thể đổi người dùng bằng cách cấu hình lại quyền cho các thư mục sau như dưới đây :
$ sudo chgrp -R toan /etc/tomcat7

$ sudo chown -R toan:toan /var/lib/tomcat7
$ sudo chown -R toan:toan /var/cache/tomcat7
$ sudo chown -R toan:toan /var/log/tomcat7

 

Định nghĩa lại người dùng trong tập tin cấu hình của tomcat :

$ sudo vi /etc/default/tomcat7
Cập nhật thông tin như sau :
TOMCAT7_USER=toan
TOMCAT7_GROUP=toan

Sau đó khởi động lại Tomcat :
$ sudo service tomcat7 start

 

* Đôi lúc khi khởi động, tomcat không tìm thấy một số thư mục, ta có thể tạo đường dẫn liên kết đến chúng theo cú pháp :

$ ln -s source link_symb

Ví dụ tạo đường trong /usr/share/tomcat7 đường dẫn conf đến thư mục /etc/tomcat7 và nối logs đến /var/log/tomcat7:
$ cd /usr/share/tomcat7

$ sudo ln -s /etc/tomcat7 conf
$ sudo ln -s /var/log/tomcat7 logs

Có thể xóa đường dẫn liên kết này bằng lện unlink :
$ sudo unlink conf
 

* Ta có thể thay đổi host name trong /etc/tomcat7/server.xml:

$ sudo vi /etc/tomcat7/server.xml
Rồi thay đổi defaultHost và name như ý muốn :
<Engine name="Catalina" defaultHost="172.0.0.1">
<Host name="172.0.0.1"  appBase="webapps"
 unpackWARs="true" autoDeploy="true">


 

* Khởi động Tomcat bằng script

Ta có thể khởi động Tomcat bằng startup.sh và ngừng nó bằng shutdown.sh như sau :

$ sudo /usr/share/tomcat7/bin/startup.sh
$ sudo /usr/share/tomcat7/bin/shutdown.sh

Hai script này gọi đến script catalina.sh, ta nên kiểm tra các biến environment CATALINA_HOME và CATALINA_BASE, để khi khởi động không bị lỗi như sau :
$ /usr/share/tomcat7/bin/catalina.sh version

Cách khác để kiểm tra hai biến này có được định nghĩa đúng không :
$ ps aux | grep catalina

Mặc định thì CATALINA_BASE (configurations) và CATALINA_HOME (binaries) trỏ đến cùng một thư mục. Nhưng khi cài đặt gói tomcat7 trong Ubuntu nó sẽ cài ở hai thư mục để cho phép chạy nhiều instance của Tomcat mà không cần phải copy lại phần binaires. Ta phải định nghĩa lại hai biến này : CATALINA_HOME trỏ đến /usr/share/tomcat7, và CATALINA_BASE trỏ đến /var/lib/tomcat7.

Để làm điều này ta tạo tập tin setenv.sh định nghĩa hai biến này trong /usr/share/tomcat7/bin/
$ cd /usr/share/tomcat7/bin

Tạo tập tin setenv.sh và đổi quyền để có thể thực thi nó :
$ sudo touch setenv.sh
$ sudo chmod 755 setenv.sh


Nội dung của tập tin setenv.sh :
#! /bin/sh

# Directory where the Tomcat binary distribution resides
CATALINA_HOME=/usr/share/tomcat7

# Directory for per-instance configuration files and webapps
CATALINA_BASE=/var/lib/tomcat7


Sau đó khởi động lại Tomcat ta sẽ thấy hai biến này đã được cập nhật
$ sudo /usr/share/tomcat7/bin/startup.sh


Thứ Tư, 12 tháng 8, 2020

Cài đặt Jenkins trên Ubuntu

* Dùng apt-get để cài đặt Jenkins trong Ubuntu

- Thêm debian-stable apt repository
wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/ > \
/etc/apt/sources.list.d/jenkins.list'

- Cập nhật nguồn trước khi cài đặt
# sudo apt-get update

- Lệnh sau sẽ cài đặt jenkins (nếu Jenkins đã được cài đặt trước rồi, thì lệnh này sẽ update Jenkins lên phiên bản mới)
# sudo apt-get install jenkins


 

* Chạy Jenkins với một user khác trong Ubuntu

- Liệt kê tất cả các user trên máy để chọn một user khác user jenkins để thay. Chẳng hạn trong máy có user toan, ta chọn user này.

$ cat /etc/passwd

- Dừng service jenkins
$ sudo service jenkins stop

- Thay JENKINS_USER, JENKINS_GROUP trong cấu hình :
$ sudo vi /etc/default/jenkins

Rồi trong tập tin này thay user và group thành toan :
JENKINS_USER=toan
JENKINS_GROUP=toan

- Thay quyền làm chủ cho Jenkins home, Jenkins webroot và logs cho user toan :
$ sudo chown -R toan:toan /var/lib/jenkins
$ sudo chown -R toan:toan /var/cache/jenkins
$ sudo chown -R toan:toan /var/log/jenkins

- Sau đó khởi động lại jenkins :
$ sudo service jenkins start

- Nếu có lỗi khi khởi động lại thì ta có thể xem logs với lệnh, rồi tìm cách sửa :
$ sudo service jenkins status


 

* Content Security Policy trong Jenkins làm cho kết quả test (HTMLReport) không hiển thị như mong muốn. Một cách giải quyết vấn đề này là không dùng Content Security Policy : -Dhudson.model.DirectoryBrowserSupport.CSP=

- Trong tập tin cấu hình jenkins

$ sudo vi /etc/default/jenkins

- Gán giá trị rỗng cho DirectoryBrowserSupport.CSP:
JAVA_ARGS="-Djava.awt.headless=true -Dhudson.model.DirectoryBrowserSupport.CSP= "


Tạo một user với quyền root trong Ubuntu

- Khi tạo user mới với lệnh useradd thì home directory thường không được tạo cùng lúc. Dùng option -m(--create-home) để tạo home directory chẳng hạn /home/username :

# sudo useradd -m toan

Thư mục /home/toan được tạo đồng thời với user toan


- Nếu liệt kê các tập tin trong thư  /home/toan , bạn sẽ thấy các tập tin khởi tạo :

# ls -la /home/toan/


- Để đăng nhập với user vừa tạo, ta cần phải cài password với lệnh passwd như sau :

# sudo passwd toan


- Dùng lệnh usermod để thêm user vào nhóm sudo (quyền root)

# sudo usermod -aG sudo toan

Trên Ubuntu, mặc định là các thành viên của nhóm sudo có quyền như sudo.



- Kiểm tra quyền truy xuất sudo trên user mới tạo. Dùng lệnh su để chuyển sang user toan :

# su - toan


- Với user này, kiểm xem nó có quyền "sudo" như khi chạy lệnh với quyền superuser hay không. Chẳng hạn, liệt kê nội dung của thư mục /root vì chỉ user root mới có quyền này :
# sudo ls -la /root



* Trong trường hợp muốn xóa user, dùng lệnh userdel. Dùng option -r(--remove) để buộc userdel xóa thư mục home và mail spool của user
# sudo userdel -r toan