Hướng dẫn cấu hình Nginx, PostgresSQL, pgBouncer trong triển khai Odoo trên Centos 8
Trong quá trình triển khai hệ thống Odoo trên Centos 8, việc cấu hình nginx, PostgresSQL và pgBouncer là rất quan trọng để đảm bảo hệ thống hoạt động ổn định và hiệu quả. IZISolution sẽ hướng dẫn các bước cấu hình chi tiết để giúp bạn triển khai Odoo trên hệ thống Centos 8 một cách nhanh chóng và dễ dàng trong nội dung bài viết dưới đây.
I. Hướng dẫn cấu hình Nginx
Cài đặt
Cài đặt EPEL repository
$ sudo yum install epel-release
Update the repository
$ sudo yum update
Install NGINX Open Source
$ sudo yum install nginx
Verify the installation
$ sudo nginx -v
Cấu hình
Cấu hinh proxy_mode = True nếu Odoo đứng sau 1 reverse proxy(nginx)
Trong file odoo.conf
proxy_mode = True
nginx.conf
#odoo server
upstream odoo {
server 127.0.0.1:8069;
}
upstream odoochat {
server 127.0.0.1:8072;
}
# http -> https
server {
listen 80;
server_name odoo.mycompany.com;
rewrite ^(.*) https://$host$1 permanent;
}
server {
listen 443;
server_name odoo.mycompany.com;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
# Add Headers for odoo proxy mode
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
# SSL parameters
ssl on;
ssl_certificate /etc/ssl/nginx/server.crt;
ssl_certificate_key /etc/ssl/nginx/server.key;
ssl_session_timeout 30m;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# log
access_log /var/log/nginx/odoo.access.log;
error_log /var/log/nginx/odoo.error.log;
# Redirect longpoll requests to odoo longpolling port
location /longpolling {
proxy_pass http://odoochat;
}
# Redirect requests to odoo backend server
location / {
proxy_redirect off;
proxy_pass http://odoo;
}
# common gzip
gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
gzip on;
}
II. Hướng dẫn cấu hình PostgresSQL
Cách tham số cấu hình để tối ưu PostgresSQL đều được cấu hình trong file /postgresql.conf
max_connections: #Số lượng connection tối data được connect tới database, số lượng connnect nên trong khoảng vài trăm connection để hệ thống PostgresSQL hoạt động được hiệu quả nhất(tầm 500 connect)
shared_buffers: #Bao nhiêu RAM được PostgresSQL sử dụng để cache data.
- 25% of RAM của hệ thống vật lý
- Lớn hơn 55% VM RAM đối với hệ thống ảo hoá
effective_cache_size: #Thiết lập để hệ thống dự tính kích kích thước ổ cứng khả dụng để caching data.
- Nên là 50% RAM
- 75% RAM hơi quá nhưng có thể chấp nhận được
work_mem: #Nếu bạn thực hiện nhiều xắp sếp phức tạp và có nhiều RAM thì nên cấu hình lại tham số này để tăng hiệu năng cho PostgresQL server. Tham số này có giá trị trên mỗi connnection
Trong phần các công thức để cấu hình Odoo ta có 1 công thức như sau:
(1 + #workers + #max_cron_threads) * #db_maxconn < #max_connections
Mặc định trong odoo đã cấu hình các thông số sau:
#max_cron_threads = 2
#db_maxconn = 64
Nếu #workers ta để là 6 vậy giá trị max_connections sẽ phải cấu hình
#max_connections > 64 * (1 + 6 + 2) = 576
=> #max_connections > 576
=> Ta cần thiết lập lại max_connections trong postgres.conf lên tầm 600 là hợp lý
Nhưng như trên ta cũng đã nói rằng để postgres hoạt động tốt ta cần cấu hình max_connections tầm 500, như vậy vậy số lượng worker tăng hơn nữa thì vấn đề sẽ xảy ra và để giải quyết vấn đề này ta sẽ sử dụng pgBouncer để khắc phục.
Lý do max_connections nên được thiết lập tầm trên dưới 500 là do cơ chế thiết lập các connection của postgres tương đối phức tạp và tốn tài nguyên vì để cô lập các connection nhằm đem lại sự ổn định kết nối. Điều đó dẫn đến khi sử lý các kết nối đơn giản, ngắn hạn nó lại trở thành nhược điểm.
III. Hướng dẫn cấu hình pgBouncer
Như đã nói ở bên trên, để khắc phục tình trạng số lượng connection đến postgres có thể lên đến quá lớn, cũng như hạn chế việc sinh ra các connection ngắn tốn tài nguyên, ta sẽ áp dụng thêm pgBouncer để sử lý vấn đề này.
Bạn có thể đặt pgBouncer tại 3 nơi:
- Trên cùng Postgres server
- Trên cùng Odoo server
- Trên 1 server thứ 3
* Để cài đặt ta sử dụng câu lệnh sau
$ sudo yum install pgbouncer
* Tạo file pgbouncer.init để cấu hình pgBouncer với nội dung sau ở bất kỳ đâu
[databases]
#{db_name} = host={postgres_server} port={postgres_port} dbname={postgress_db_name}
template1 = host=localhost port=5432 dbname=template1
[pgbouncer]
listen_port = 6432
listen_addr = localhost
auth_type = md5
auth_file = userlist.txt
logfile = pgbouncer.log
pidfile = pgbouncer.pid
admin_users = someuser
* Tạo thêm 1 file userlist.txt để lưu trữ user_name, password người dùng có thể truy cập thông qua pgBouncer, nội dung như sau: “user” “password”
"user1" "md5638b81c77071ea624d1ad4adb1433540"
Trong ví dụ trên password đang được mã hoá md5
=> password = “md5” + md5_hash(password + user1)
* Khởi chạy pgBouncer
$ pgbouncer -d pgbouncer.ini
Như vậy bây giờ ta có thể kết nối đến postgres thông qua pgBouncer mà không cần kết nối trực tiếp như trước
* Tạo connection
$ psql -p 6432 -U user1 template1
* Quản lý pgBouncer bằng cách connect đến server với user pgbouncer và sử dụng sql SHOW HELP;
$ psql -p 6432 -U someuser pgbouncer
pgbouncer=# SHOW HELP;
NOTICE: Console usage
DETAIL:
SHOW [HELP|CONFIG|DATABASES|FDS|POOLS|CLIENTS|SERVERS|SOCKETS|LISTS|VERSION|...]
SET key = arg
RELOAD
PAUSE
SUSPEND
RESUME
SHUTDOWN
[...]
* Nếu bạn có bất cứ thay đổi nào trong file pgbouncer.ini bạn cần reload lại bằng cách.
$ psql -p 6432 -U someuser pgbouncer
pgbouncer=# RELOAD;
=> Một số cấu hình cần lưu ý(https://www.pgbouncer.org/config.html)
pool_mode = session #Cấu hình để khi nào 1 connection được tái sử dụng để đưa vào pool
session: Các connect được đưa bào pool khi client disconnect. Default.
transaction: Các connect được đưa vào pool khi transaction kết thúc
statement: Các connect được đưa vào poll khi query kết thúc. Transaction có nhiều statement sẽ không được đưa cho phép trong cấu hình này.
max_client_conn = 100 #Số lượng connection tối đa
default_pool_size = 20 #Số lượng connection cho phép trên mỗi user/database
III. Một số lưu ý khi cấu hình
Log
Loging: Mặc định, Odoo hiển thị toàn bộ logging info ngoại trừ logging từ workflow(chỉ cảnh báo) và đưa output vào trong file qua stdout.
$ sudo chown odoo /var/log/odoo
$ sudo chgrp odoo /var/log/odoo
[options]
addons_path = /opt/odoo10/enterprise,/opt/odoo10/odoo/addons
proxy_mode = True
logfile = /var/log/odoo/odoo.log
syslog = False
log_db = False
Log Rotation: Trong một vài trường hợp, việc để log như vậy không phải là ý tưởng tốt để thực hiện, vì có thể có nhiều worker cùng thực hiện ghi log tại 1 thời điểm và dẫn đến hệ thống bị treo. Để giải quyết vấn đề này chúng ta có một cách khác để ghi log đó là logrotate daemon. Khi đó mình sẽ cần thêm pidfile cho Odoo.
[options]
...
logrotate = False
pidfile = /var/run/odoo.pid
$ sudo apt install logrotate
$ sudo vi /etc/logrotate.d/odoo
/var/log/odoo/odoo.log {
rotate 10
daily
# Do not rotate the log if it is empty
notifempty
# if the log file is missing, go on to the next one without
# issuing an error message.
missingok
compress
delaycompress
postrotate
if [ -f /var/run/odoo.pid ]; then
kill -HUP `cat /var/run/odoo.pid`
fi
endscript
}
Check logs: Cách để hiển thị dữ liệu log trong file trên màn hình console
Hiển thị log theo thời gian thực
$ tail -f /var/log/odoo/odoo.log
Tìm trong file log bằng regex và hiển thị số dòng, 2 dòng sau và 5 dòng trước kế quả tìm.
$ grep -n Worker.*unregistered /var/log/odoo/odoo.log -A2 -B5
Log handler: Là sự kết hợp giữa logger vs log level. Nếu logger bị bỏ qua thì root logger được sử dụng. Level sẽ là 1 trong: INFO, WARNING, CRITICAL, DEBUG
[options]
...
log_handler=openerp.sql_db:DEBUG,werkzeug:DEBUG,openerp.fields:WARNING,openerp.http.rpc.request:DEBUG,:WARNING
Handler trên sẽ làm việc sau:
Debug sql queries
Debug werkzeug queries
Warn openerp.fields module
Debug RPC queries
Warn on others
Log level: là dạng ngắn gọn hơn của log handler, để dễ dàng cấu hình cho các handler thông dụng: critical, error, warn, debug, debug_sql, debug_rpc, debug_rpc_answer.
[options]
...
log_level = debug_rpc
Odoo Workers:
− Only on unix
− Python GIL: Trong CPython, Global Interpreter Lock(GIL) là một chức năng ngăn chặn nhiều luồng native thực thi mã Python cùng lúc.
Việc kích hoạt worker sẽ giúp tận dụng phần cứng tốt hơn:
− Giới hạn tài nguyên
− Tái sử dụng tiến trình sử lý
− Hiệu năng tốt hơn
− Đồng bộ các thay đổi
Add workers: Mỗi một cpu core sẽ được tính bằng 2 tiến trình sử lý vậy nên:
1 worker ~= 6 users
=> # workers = (#cpus * 2 ) + 1
[options]
...
workers = 4
max_cron_threads = 1
Mặc định odoo đang cấu hinh 0 woker tường ứng việc không sử dụng multiple-worker, nếu lên để sử dụng được ta chỉ cần tăng số lượng worker lên > 0.
* Kiểm tra log worker
openerp.service.server: Evented Service (longpolling) running on 0.0.0.0:8072
Exception: bus.Bus unavailable
* Nginx with workers
Longpolling sử dụng 1 tcp port khác vậy nên ta cần phải chuyển hướng request có chứa /longpoll/ đến đúng port.
Đừng điều hướng toàn bộ request về port đó hoặc bạn sẽ không bao giờ sử dụng workers process
[...]
upstream odoo_longpoll {
server 127.0.0.1:8072;
}
[...]
location /longpoll {
proxy_redirect off;
proxy_pass http://odoo_longpoll;
}
[...]
Workers limits: Các tham số cấu hình để quản lý process trong Odoo.
* CPU
limit_time_cpu = 60 #Thời gian tối đa cpu cho phép trên mỗi request
limit_time_real = 120 #Thời gian tối đã thực tế cho phép trên mỗi request
limit_time_real_cron = limit_time_real #Thời gian tối đã thực tế cho phép trên mỗi cron job, Bằng 0 nếu không giới hạn và mặc định bằng giá trị của limit_time_real
Nếu 1 worker đạt đến giới hạn thì tiến trình đó sẽ bị huỷ mà không cần trả về phản hổi cho client. Nếu chuyện đó xảy ra cần phải đặt lại giá trị cho các tham số trên.
* Memory: Là giá trị tối đa cho bộ nhớ ảo của mỗi worker sử dụng(bytes)
limit_memory_soft = 2 * 1024 * 1024 * 1024 #Nếu 1 worker đạt đến giá trị này thì sẽ được reset lại sau khi trả lại kết quả cho client(2 GB)
limit_memory_hard = 2.5 * 1024 * 1024 * 1024 #Nếu 1 worker đạt đến giá trị này thì sẽ được reset lại ngay mà không cần trả lại kết quả cho client(2.5 GB)
* Requests count:
limit_request = 8192 #Số lượng request tối đa được sử lý trên mỗi worker. Khi đạt đến số lượng tối data worker sẽ được reset sau khi trả lại kết quả.
* Một số công thức tính các tham số trên:
Tính worker dự trên số lượng CPU core
#worker = (#cpus * 2 ) + 1
Tính Worker dựa trên số lượng RAM:
#worker = = = 16 worker
Tính tổng số RAM cần hệ thống
* Trung bình mỗi worker thực hiện được 6-7 request/s
Bản sau là tỉ lệ RAM ước đoán sử dụng trên tổng số request
Type |
Ratio | Ram Estimate |
light_worker | 80 | 150 MB |
heavy_worker | 20 | 1024 MB |
=> Công thức tính số RAM cần dùng cho mỗi worker:
#Ram = #requests * ( (light_worker_ratio * light_worker_ram_estimation) + (heavy_worker_ratio * heavy_worker_ram_estimation) )
VD: Server bạn có 6 workers => #request/s = ~39request/s
=> # Ram = 39 * ((0.8 * 150) + (0.2 * 1024) = ~12GB(12667.2MB)
=> soft_memory_limit = #RAM / #workers = 12667.2 / 6 = ~2.1GB(2111.2MB)
=> soft_memory_hard = #soft_memory_limit + 500MB = 2111.2 = 500 = ~2.6GB(2611.2MB)
Wizard limits: Là dạng dữ liệu mà sau 1 khoảng thời gian sẽ bị xoá đi bằng cron
osv_memory_count_limit = 0 #Số lượng bản ghi tối đa được lưu trữ trong vitual osv_memory, mặc định là 0 là không tính.
osv_memory_age_limit = 1 #Thời gian lưu trữ tối đa sau khi bản ghi được tạo ra mặc định là 1h
Database Connection:
db_maxconn: 64 #Số lượng connection tối data được tạo từ Odoo tới PostgresSQL trên 1 Odoo process, mặc định 64.
Có 1 công thức để xác định số lượng db_maxconn an toàn để sử dụng như sau (giá trị db_maxconn có thể lớn hơn cách tính của công thức):
(1 + #workers + #max_cron_threads) * #db_maxconn < #max_connections
=> #db_maxconn < #max_connections / (1 + #workers + #max_cron_threads)
Trong đó:
max_connections là giá trị được cấu hình trong file postgres.conf trên PosgrtesSQL Server.
max_cron_threads là số thread được dùng để khởi chạy cron job được cấu hình trong file conf của Odoo app cùng mới giá trị workers
=> VD:
#max_connections = 100(default của PostgresSQL)
#workers = 6
#max_cron_threads = 2
=> #db_maxconn < 100 / (1 + 6 + 2) = ~11
=> #db_maxconn < 11
=> Số lượng db_maxconn nên thiết lập cho mỗi process < 11 để đảm bảo an toàn cho hệ thống.