<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="https://publishpress.com/"
	>

<channel>
	<title>Ruby Archives - Tomoshare</title>
	<atom:link href="https://blog.tomosia.com.vn/danh-muc/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.tomosia.com.vn/danh-muc/ruby/</link>
	<description>Kênh chia sẻ kiến thức Tomosia Việt Nam</description>
	<lastBuildDate>Fri, 30 Jan 2026 09:56:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://blog.tomosia.com.vn/wp-content/uploads/2023/09/cropped-icon-32x32.png</url>
	<title>Ruby Archives - Tomoshare</title>
	<link>https://blog.tomosia.com.vn/danh-muc/ruby/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</title>
		<link>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/</link>
					<comments>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Luan]]></dc:creator>
		<pubDate>Fri, 30 Jan 2026 09:56:08 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=4177</guid>

					<description><![CDATA[<p>Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối tân, camera khắp nơi, thẻ từ thang máy hiện đại. Lễ tân đưa cho bạn chìa khóa phòng 101. Bạn lên phòng, mở cửa và nghỉ ngơi. Nhưng sau đó, vì tò mò, bạn thử cạo sửa con số trên chìa khóa thành 102 và cắm vào ổ khóa phòng bên cạnh. <em>Cạch!</em> Cửa mở toang.</p>



<p class="wp-block-paragraph">Trong thế giới thực, điều này thật nực cười. Nhưng trong thế giới lập trình Web và API, tình huống này xảy ra thường xuyên đến mức nó trở thành &#8220;cơn đau đầu&#8221; kinh niên của giới bảo mật. Nó được gọi là <strong>IDOR</strong> (Insecure Direct Object Reference) hay <strong>BOLA</strong> (Broken Object Level Authorization).</p>



<p class="wp-block-paragraph">Nếu bạn là một Backend Developer, và bạn tin rằng chỉ cần có <mark style="background-color:#e9ecef" class="has-inline-color"><code>Authentication</code> </mark>(Đăng nhập) là hệ thống đã an toàn, thì bài viết này sẽ thay đổi tư duy lập trình của bạn. Chúng ta sẽ không chỉ nói về bề nổi, mà sẽ đi sâu vào bản chất kiến trúc của lỗi này.</p>



<h3 id="1-ban-chat-ky-thuat-cua-idor" class="wp-block-heading">1. Bản chất kỹ thuật của IDOR</h3>



<p class="wp-block-paragraph"><strong>IDOR</strong> (Insecure Direct Object Reference) không chỉ là một lỗi code, nó là một <strong>lỗi về thiết kế Logic (Business Logic Flaw)</strong>.</p>



<p class="wp-block-paragraph">Trong danh sách <strong>OWASP API Security Top 10</strong>, IDOR chính là bản chất của lỗ hổng số 1: <strong><a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/">API1: 2023 Broken Object Level Authorization (BOLA)</a></strong>.</p>



<p class="wp-block-paragraph">Hiểu sâu hơn, IDOR xảy ra khi 3 điều kiện sau hội tụ:</p>



<ol class="wp-block-list">
<li><strong>Direct Reference:</strong> Ứng dụng để lộ mã định danh nội bộ (Internal Identifier) của đối tượng (như Database Primary Key, Filename) trực tiếp ra ngoài URL hoặc Body request.</li>



<li><strong>User Input:</strong> Ứng dụng tin tưởng và sử dụng trực tiếp đầu vào này để truy vấn dữ liệu.</li>



<li><strong>Missing Authorization:</strong> Ứng dụng bỏ qua bước kiểm tra quyền truy cập <em>tại thời điểm</em> truy xuất đối tượng.</li>
</ol>



<p class="wp-block-paragraph"><strong>Sai lầm phổ biến:</strong> Nhiều Dev nhầm lẫn giữa <em>Authentication</em> (Bạn là ai?) và <em>Authorization</em> (Bạn được làm gì?). IDOR xảy ra khi bạn đã qua cửa Authentication, nhưng hệ thống quên kiểm tra Authorization ở mức độ đối tượng (Object-level).</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="559" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png" alt="" class="wp-image-4187" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-300x164.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-768x419.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-380x207.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-800x436.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1160x633.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1.png 1408w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="2-giai-phau-co-che-tan-cong" class="wp-block-heading">2. Giải phẫu cơ chế tấn công</h3>



<p class="wp-block-paragraph">Hãy mổ xẻ một HTTP Request để thấy IDOR có thể lẩn trốn ở đâu. Không chỉ ở URL Parameter, nó có thể nằm ở Header hoặc Body.</p>



<p class="wp-block-paragraph"><strong>Kịch bản:</strong> Ứng dụng Chat cho phép người dùng tải lại lịch sử chat.</p>



<p class="wp-block-paragraph"><strong>Request bình thường:</strong></p>



<pre class="wp-block-code"><code>GET /api/v1/messages/history HTTP/1.1
Host: example.com
Cookie: session_id=abc123xyz...
X-User-ID: 1005  &lt;-- IDOR tiềm ẩn ở Header</code></pre>



<ol class="wp-block-list">
<li><strong>Hacker quan sát:</strong> Thấy Header <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID: 1005</mark></code> được gửi kèm mỗi request.</li>



<li><strong>Thử nghiệm:</strong> Hacker dùng Postman hoặc Burp Suite để chặn request, sửa <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID</mark></code> thành <code>1006</code>.</li>



<li><strong>Kết quả:</strong> Server, thay vì lấy ID từ session an toàn (Server-side), lại tin tưởng lấy ID từ Header (Client-side) để query database <code><mark style="background-color:#e9ecef" class="has-inline-color">SELECT * FROM messages WHERE user_id = 1006</mark></code>.</li>



<li><strong>Hậu quả:</strong> Hacker đọc được toàn bộ tin nhắn của người dùng 1006.</li>
</ol>



<p class="wp-block-paragraph"><em><strong>IDOR có thể xuất hiện ở:</strong></em></p>



<ul class="wp-block-list">
<li><strong>URL Path:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/users/100/profile</mark></code></li>



<li><strong>Query Params:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/invoices?id=100</mark></code></li>



<li><strong>Body (JSON/XML):</strong> <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/change-password</code> với body <code>{"user_id": 100, "new_pass": "..."}</code></mark></li>
</ul>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png" alt="" class="wp-image-4190" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="3-tai-sao-idor-lai-la-sat-thu-tham-lang" class="wp-block-heading">3. Tại sao IDOR lại là &#8220;Sát thủ thầm lặng&#8221;?</h3>



<p class="wp-block-paragraph">IDOR nguy hiểm vì tính &#8220;hợp lệ&#8221; về mặt cú pháp của nó.</p>



<ul class="wp-block-list">
<li><strong>Vượt mặt WAF (Web Application Firewall):</strong> Các tường lửa thường chặn các ký tự lạ như <code><mark style="background-color:#e9ecef" class="has-inline-color">' OR 1=1 --</mark></code> (SQL Injection) hay <code><mark style="background-color:#e9ecef" class="has-inline-color">&lt;script&gt;</mark></code> (XSS). Nhưng với IDOR, payload chỉ là một con số: <code>id=1006</code>. Đối với WAF, đây là một con số hoàn toàn sạch và hợp lệ.</li>



<li><strong>Khó phát hiện bằng Automation Tool:</strong> Các máy quét (Scanner) không hiểu ngữ cảnh nghiệp vụ. Nếu máy quét gửi <code><mark style="background-color:#e9ecef" class="has-inline-color">id=1006</mark></code> và nhận về <code>200 OK</code>, nó đánh dấu là &#8220;Thành công&#8221;. Nó không biết rằng dữ liệu trả về đó <em>không thuộc về</em> người đang gửi request. Chỉ có con người (Pentesters) hoặc Unit Test được viết kỹ lưỡng mới phát hiện ra.</li>



<li><strong>Hệ quả dây chuyền:</strong> IDOR thường là bước đệm để leo thang đặc quyền (Privilege Escalation), dẫn đến việc chiếm đoạt toàn bộ hệ thống (Account Takeover).</li>
</ul>



<p class="wp-block-paragraph">Sự nguy hiểm của IDOR nằm ở chỗ nó là một lỗ hổng logic, không phải lỗ hổng cú pháp. Các công cụ quét bảo mật tĩnh (SAST) truyền thống thường thất bại trong việc phát hiện IDOR vì chúng không thể hiểu ngữ cảnh &#8220;ai sở hữu cái gì&#8221; trong một ứng dụng cụ thể. Điều này dẫn đến một nghịch lý: trong khi các lỗ hổng kỹ thuật như <strong>SQL Injection</strong> đang giảm dần nhờ các framework hiện đại, IDOR vẫn tồn tại dai dẳng và chiếm tỷ lệ cao trong các chương trình săn lỗi nhận thưởng (Bug Bounty), đặc biệt là trong các lĩnh vực y tế, chính phủ và dịch vụ chuyên nghiệp.</p>



<h3 id="4-phan-loai-idor-chuyen-sau" class="wp-block-heading">4. Phân loại IDOR chuyên sâu.</h3>



<h4 id="4-1-leo-thang-dac-quyen-ngang-horizontal-escalation" class="wp-block-heading">4.1. Leo thang đặc quyền ngang (Horizontal Escalation)</h4>



<p class="wp-block-paragraph">Đây là dạng phổ biến nhất. Người dùng A truy cập dữ liệu của Người dùng B có cùng vai trò.</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong> Xem đơn hàng, tải hóa đơn, xem ảnh riêng tư.</li>



<li><strong>Rủi ro:</strong> Rò rỉ thông tin cá nhân (PII Breach), vi phạm GDPR/CCPA.</li>
</ul>



<h4 id="4-2-leo-thang-dac-quyen-doc-vertical-escalation" class="wp-block-heading">4.2. Leo thang đặc quyền dọc (Vertical Escalation)</h4>



<p class="wp-block-paragraph">Người dùng thường (User) tìm cách gọi các API dành riêng cho Quản trị viên (Admin/Manager).</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong>
<ul class="wp-block-list">
<li>Hacker tìm thấy API: <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/users/1001/roles</code>.</mark></li>



<li>Hacker gửi request với body: <code><mark style="background-color:#e9ecef" class="has-inline-color">{"role": "ADMIN"}</mark></code>.</li>



<li>Nếu Backend không check xem &#8220;người gọi API này có phải là Super Admin không&#8221;, Hacker lập tức trở thành Admin.</li>
</ul>
</li>
</ul>



<h4 id="4-3-idor-tren-file-tinh-static-file-idor" class="wp-block-heading">4.3. IDOR trên File tĩnh (Static File IDOR)</h4>



<p class="wp-block-paragraph">Tên file thường dễ đoán.</p>



<ul class="wp-block-list">
<li>Ví dụ: <code><mark style="background-color:#e9ecef" class="has-inline-color">https://example.com/uploads/receipts/receipt_001.pdf.</mark></code></li>



<li>Hacker viết script lặp từ <code>001</code> đến <code>9999</code> để tải toàn bộ hóa đơn của hệ thống.</li>
</ul>



<h3 id="5-dan-chung-thuc-te-khi-ong-lon-cung-mac-sai-lam" class="wp-block-heading">5. Dẫn chứng thực tế: Khi &#8220;Ông lớn&#8221; cũng mắc sai lầm</h3>



<h4 id="5-1-mcdonalds-su-co-mchire-7-2025" class="wp-block-heading">5.1. McDonald&#8217;s &amp; sự cố McHire (7/2025)</h4>



<p class="wp-block-paragraph">Link: <a href="https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/">https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/</a></p>



<p class="wp-block-paragraph"><strong>Bối cảnh</strong>: Hệ thống bị ảnh hưởng là&nbsp;<strong>McHire</strong>, nền tảng tuyển dụng toàn cầu của McDonald&#8217;s, được vận hành bởi đối tác công nghệ bên thứ ba là&nbsp;<strong>Paradox.ai</strong>. Paradox.ai cung cấp giải pháp chatbot AI tên là &#8220;Olivia&#8221; để tự động hóa quy trình phỏng vấn, thu thập thông tin ứng viên và lên lịch làm việc. Sự phụ thuộc vào bên thứ ba này đã mở rộng bề mặt tấn công của McDonald&#8217;s ra ngoài hạ tầng kiểm soát trực tiếp của họ.<sup></sup>&nbsp;&nbsp;&nbsp;</p>



<p class="wp-block-paragraph"><strong>Nguyên nhân chính</strong><br>Sự cố xảy ra do kết hợp 2 lỗi nghiêm trọng:</p>



<ol class="wp-block-list">
<li><strong>Xác thực yếu (Weak Authentication)</strong>
<ul class="wp-block-list">
<li>Một tài khoản admin thử nghiệm của Paradox.ai dùng mật khẩu <strong>“123456”</strong>, không có MFA.</li>



<li>Tài khoản này tồn tại từ <strong>2019</strong>, không bị vô hiệu hóa dù có quyền truy cập production.</li>
</ul>
</li>



<li><strong>Lỗ hổng IDOR (Missing Object Level Authorization)</strong>
<ul class="wp-block-list">
<li>API backend cho phép truy xuất hồ sơ ứng viên bằng <code><mark style="background-color:#e9ecef" class="has-inline-color">applicant_id</mark></code>.</li>



<li>Không kiểm tra quyền truy cập → chỉ cần đổi ID là đọc được dữ liệu của bất kỳ ứng viên nào.</li>



<li>Từ 1 tài khoản → mở rộng ra <strong>64 triệu hồ sơ toàn cầu</strong>.</li>
</ul>
</li>
</ol>



<p class="wp-block-paragraph"><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ <strong>PII</strong>: tên, email, số điện thoại, địa chỉ nhà.</li>



<li>Lộ dữ liệu nhạy cảm: log chat với AI (tính cách, sở thích, thông tin cá nhân).</li>



<li>Lộ token phiên → có thể giả danh ứng viên.</li>



<li>Nguy cơ cao cho <strong>social engineering, lừa đảo, chiếm tài khoản</strong>.</li>
</ul>



<p class="wp-block-paragraph">Sự kết hợp giữa <strong>quản lý tài khoản kém</strong> (tài khoản ma, không MFA) và <strong>thiếu kiểm soát quyền API</strong> (IDOR) tạo ra “cơn bão hoàn hảo” cho rò rỉ dữ liệu quy mô toàn cầu – một xu hướng cực kỳ đáng báo động trong năm 2025</p>



<h4 id="5-2-mozilla-firefox-accounts-lo-hong-xoa-tai-khoan-5-2025" class="wp-block-heading">5.2. Mozilla Firefox Accounts – Lỗ hổng xóa tài khoản (5/2025)</h4>



<p class="wp-block-paragraph">Link: <a href="https://hackerone.com/reports/3154983">https://hackerone.com/reports/3154983</a></p>



<p class="wp-block-paragraph"><strong>Bản chất sự cố</strong></p>



<ul class="wp-block-list">
<li>Lỗ hổng IDOR nghiêm trọng trong API xóa tài khoản (<code><mark style="background-color:#e9ecef" class="has-inline-color">/v1/account/destroy</mark></code>).</li>



<li>Cho phép <strong>xóa vĩnh viễn tài khoản người khác</strong> mà không cần tương tác từ nạn nhân (zero-click).</li>



<li>Ảnh hưởng trực tiếp đến Integrity (toàn vẹn) và Availability (sẵn sàng) của dữ liệu.</li>
</ul>



<p class="wp-block-paragraph"><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>Lỗi Session Misbinding: backend không kiểm tra phiên làm việc có khớp với tài khoản bị xóa hay không.</li>



<li>API tin vào payload JSON (email, authPW) thay vì session của người gửi request.</li>



<li>Đây là dạng IDOR ghi/xóa, nguy hiểm hơn IDOR đọc dữ liệu.</li>
</ul>



<p class="wp-block-paragraph"><strong>Kịch bản khai thác</strong></p>



<ol class="wp-block-list">
<li>Kẻ tấn công tạo tài khoản Firefox, đăng nhập bằng SSO (Google).</li>



<li>Chặn request xóa tài khoản của chính mình.</li>



<li>Đổi email trong payload thành email nạn nhân (cũng dùng SSO, chưa set mật khẩu).</li>



<li>Gửi request → hệ thống xóa tài khoản nạn nhân.</li>
</ol>



<p class="wp-block-paragraph"><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Mất vĩnh viễn dữ liệu: mật khẩu đã lưu, lịch sử duyệt web, tab, sync data.</li>



<li>Không thể khôi phục, không cần nạn nhân click hay xác nhận.</li>



<li>Được đánh giá <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><strong>Critical </strong></mark>trên HackerOne.</li>
</ul>



<p class="wp-block-paragraph"><strong>Bài học cốt lõi</strong>: IDOR không chỉ gây rò rỉ dữ liệu (read) mà còn có thể phá hủy dữ liệu (write/delete) nếu backend không ràng buộc chặt chẽ giữa session và đối tượng tài nguyên.</p>



<h4 id="5-3-gitlab-lo-hong-machine-learning-model-registry-2024-2025" class="wp-block-heading">5.3. GitLab – Lỗ hổng Machine Learning Model Registry (2024–2025)</h4>



<p class="wp-block-paragraph">Link: <a href="https://hackerone.com/reports/2528293">https://hackerone.com/reports/2528293</a></p>



<p class="wp-block-paragraph"><strong>Bối cảnh</strong></p>



<ul class="wp-block-list">
<li>GitLab triển khai Machine Learning Model Registry để quản lý phiên bản mô hình AI.</li>



<li>Lỗ hổng được báo cáo từ 5/2024, vá và công bố kéo dài sang <strong>2025</strong>, cho thấy độ phức tạp của bảo mật trên nền tảng lớn.</li>



<li>Sự cố ảnh hưởng trực tiếp đến tài sản trí tuệ AI (model weights, biases).</li>
</ul>



<p class="wp-block-paragraph"><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>GitLab dùng <strong>ID tịnh tiến (1,2,3,&#8230;)</strong> cho <mark style="background-color:#e9ecef" class="has-inline-color"><code>model_id</code> </mark>thay vì UUID ngẫu nhiên.</li>



<li>API tải/xem mô hình không kiểm tra quyền truy cập dự án (Missing Object Level Authorization).</li>



<li>Đây là IDOR điển hình + thiết kế API kém an toàn.</li>
</ul>



<p class="wp-block-paragraph"><strong>Cách khai thác</strong></p>



<ul class="wp-block-list">
<li>Kẻ tấn công chỉ cần tài khoản GitLab miễn phí.</li>



<li>Lặp ID từ 1 → hàng triệu để liệt kê và tải mô hình của dự án khác.</li>



<li>Không cần brute-force, không cần quyền dự án.</li>
</ul>



<p class="wp-block-paragraph"><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ mô hình ML độc quyền của doanh nghiệp.</li>



<li>Nguy cơ mất bí mật thương mại, thiệt hại kinh tế lớn trong bối cảnh AI là tài sản cốt lõi.</li>



<li>Lỗ hổng có thể bị khai thác tự động, quy mô lớn (enumeration).</li>
</ul>



<p class="wp-block-paragraph"><strong>Bài học cốt lõi</strong>: Không dùng ID tịnh tiến cho API công khai. Nó biến IDOR từ lỗ hổng tiềm ẩn thành thảm họa có thể khai thác hàng loạt, đặc biệt nguy hiểm với tài sản AI.</p>



<h3 id="6-ngan-chan-idor-hieu-qua-best-practices" class="wp-block-heading">6. Ngăn chặn IDOR hiệu quả (Best Practices)</h3>



<p class="wp-block-paragraph">Khi một tài khoản test có thể mở khóa 64 triệu hồ sơ, một email có thể xóa tài khoản người khác, và một con số tăng dần có thể làm bay cả mô hình AI, thì rõ ràng vấn đề không nằm ở “hacker quá giỏi” — mà ở chỗ chúng ta cần chặn IDOR cho đúng cách. </p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png" alt="" class="wp-image-4197" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><em>&#8220;Đừng bao giờ tin tưởng Client&#8221;</em>. Dưới đây là chiến lược phòng thủ đa lớp <strong>(Defense in Depth)</strong>.</p>



<p class="wp-block-paragraph"><strong>Lớp 1: Kiểm soát truy cập tại tầng Data (Data-Layer Authorization)</strong></p>



<p class="wp-block-paragraph">Đây là chốt chặn cuối cùng và quan trọng nhất. Mọi câu lệnh truy vấn liên quan đến dữ liệu người dùng <strong>phải</strong> kèm theo điều kiện về chủ sở hữu.</p>



<p class="wp-block-paragraph"><strong>❌ Code rủi ro (Spring Data JPA):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Controller
public Order getOrder(@PathVariable Long id) {
    return orderRepo.findById(id); // Chỉ tìm theo ID đơn hàng
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Controller</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id); </span><span style="color: #6A9955">// Chỉ tìm theo ID đơn hàng</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>✅ Code an toàn (Repository Pattern):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Repository
// Luôn gắn kèm userId vào câu query
@Query(&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;)
Optional&lt;Order&gt; findByIdAndOwner(@Param(&quot;id&quot;) Long id, @Param(&quot;currentUserId&quot;) Long userId);

// Service Layer
public Order getOrder(Long id, User currentUser) {
    return orderRepo.findByIdAndOwner(id, currentUser.getId())
        .orElseThrow(() -&gt; new ResourceNotFoundException(&quot;Order not found or access denied&quot;));
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Repository</span></span>
<span class="line"><span style="color: #6A9955">// Luôn gắn kèm userId vào câu query</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">Query</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #4EC9B0">Optional</span><span style="color: #D4D4D4">&lt;Order&gt; </span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;id&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, @</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;currentUserId&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> userId);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Service Layer</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> currentUser) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(id, </span><span style="color: #9CDCFE">currentUser</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getId</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">orElseThrow</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ResourceNotFoundException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Order not found or access denied&quot;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><strong>Lớp 2: Kiểm soát truy cập tại tầng Controller (Method-Level Security)</strong></p>



<p class="wp-block-paragraph">Sử dụng các Annotation bảo mật của Framework (như Spring Security) để chặn ngay từ cửa vào.</p>



<p class="wp-block-paragraph"><strong>✅ Ví dụ với Spring Security (<code><mark style="background-color:#e9ecef" class="has-inline-color">@PreAuthorize</mark></code>):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="@GetMapping(&quot;/invoices/{id}&quot;)
// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này
@PreAuthorize(&quot;@securityService.isOwner(authentication, #id)&quot;) 
public Invoice getInvoice(@PathVariable Long id) {
    return invoiceRepo.findById(id);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">GetMapping</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/invoices/{id}&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #6A9955">// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">PreAuthorize</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;@securityService.isOwner(authentication, #id)&quot;</span><span style="color: #D4D4D4">) </span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Invoice</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getInvoice</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">invoiceRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph"><em>Cách này giúp code business logic sạch hơn, tách biệt logic bảo mật ra khỏi logic nghiệp vụ.</em></p>



<p class="wp-block-paragraph"><strong>Lớp 3: Làm khó Hacker bằng UUID (Obfuscation)</strong></p>



<p class="wp-block-paragraph">Thay vì dùng Auto-Increment Integer (<code>1, 2, 3</code>), hãy dùng <strong>UUID</strong> (Random String: <code><mark style="background-color:#e9ecef" class="has-inline-color">a0eebc99-9c0b...</mark></code>) hoặc các thuật toán băm ID (như Hashids).</p>



<ul class="wp-block-list">
<li><strong>Lợi ích:</strong> Chặn đứng tấn công kiểu liệt kê (Enumeration Attack). Hacker không thể viết vòng lặp <code><mark style="background-color:#e9ecef" class="has-inline-color">for (i=0; i&lt;1000; i++)</mark></code> để quét dữ liệu.</li>



<li><strong>Cảnh báo:</strong> UUID <strong>KHÔNG THỂ THAY THẾ</strong> được <em>Lớp 1</em> và <em>Lớp 2</em>. Nếu hacker bằng cách nào đó biết được UUID của nạn nhân, IDOR vẫn xảy ra nếu thiếu Authorization check. UUID chỉ là &#8220;lớp sương mù&#8221;, không phải &#8220;bức tường thép&#8221;.</li>
</ul>



<p class="wp-block-paragraph"><strong>Lớp 4: Kiểm thử tự động (Automated Security Testing)</strong></p>



<p class="wp-block-paragraph">Đừng đợi Pentester tìm lỗi. Hãy viết Test Case cho IDOR.</p>



<ul class="wp-block-list">
<li><strong>Unit Test:</strong> Tạo 2 user (Alice, Bob). Đăng nhập bằng Alice, cố gắng request dữ liệu ID của Bob. Assert rằng kết quả trả về phải là <code><mark style="background-color:#e9ecef" class="has-inline-color">403 Forbidden</mark></code> hoặc <code><mark style="background-color:#e9ecef" class="has-inline-color">404 Not Found</mark></code>.</li>
</ul>



<h3 id="7-ket-luan" class="wp-block-heading">7. Kết luận</h3>



<p class="wp-block-paragraph">IDOR không phải là một lỗi kỹ thuật phức tạp, nhưng nó phản ánh sự thiếu sót trong tư duy thiết kế hệ thống.</p>



<p class="wp-block-paragraph">Hãy nhớ nguyên tắc vàng: <strong>Mọi request truy cập dữ liệu đều phải trả lời được hai câu hỏi:</strong></p>



<ol class="wp-block-list">
<li><em>Người dùng này là ai? (Authentication)</em></li>



<li><em>Dữ liệu này có phải của họ không? (Authorization)</em></li>
</ol>



<p class="wp-block-paragraph">Nếu thiếu câu hỏi số 2, chúng ta đang mở toang cánh cửa &#8220;phòng 102&#8221; cho bất kỳ ai cầm chìa khóa &#8220;phòng 101&#8221;.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Cài đặt Ruby trên M1 Mac</title>
		<link>https://blog.tomosia.com.vn/cai-dat-ruby-tren-m1-mac/</link>
					<comments>https://blog.tomosia.com.vn/cai-dat-ruby-tren-m1-mac/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Anh]]></dc:creator>
		<pubDate>Thu, 25 Jan 2024 14:37:05 +0000</pubDate>
				<category><![CDATA[OS & Command]]></category>
		<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3099</guid>

					<description><![CDATA[<p>Thỉnh thoảng việc cài đặt Ruby trên M1 Macs như là một cơn ác mộng. Chúng ta sẽ&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/cai-dat-ruby-tren-m1-mac/">Cài đặt Ruby trên M1 Mac</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Thỉnh thoảng việc cài đặt Ruby trên M1 Macs như là một cơn ác mộng. Chúng ta sẽ mất rất nhiều thời gian để có thể tìm được giải pháp nhưng cuối cùng mọi thứ vẫn không work.</p>



<p class="wp-block-paragraph">Dưới đây là một giải pháp nhanh chóng với bạn có thể thử với những dòng command<br></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Bash</span><span role="button" tabindex="0" data-code="brew install openssl@1.1

export PATH=&quot;$(brew --prefix)/opt/openssl@1.1/bin:$PATH&quot;
export LDFLAGS=&quot;-L$(brew --prefix)/opt/openssl@1.1/lib&quot;
export CPPFLAGS=&quot;-I$(brew --prefix)/opt/openssl@1.1/include&quot;
export PKG_CONFIG_PATH=&quot;$(brew --prefix)/opt/openssl@1.1/lib/pkgconfig&quot;

rvm autolibs disable

export RUBY_CFLAGS=-DUSE_FFI_CLOSURE_ALLOC
export optflags=&quot;-Wno-error=implicit-function-declaration&quot;

rvm install 3.2.2 --with-openssl-dir=$(brew --prefix)/opt/openssl@1.1" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">brew</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">openssl@1.1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PATH</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">$(</span><span style="color: #62E884">brew</span><span style="color: #E7EE98"> </span><span style="color: #BF9EEE">--prefix</span><span style="color: #E7EE98">)/opt/openssl@1.1/bin:</span><span style="color: #BF9EEE">$PATH</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">LDFLAGS</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">-L$(</span><span style="color: #62E884">brew</span><span style="color: #E7EE98"> </span><span style="color: #BF9EEE">--prefix</span><span style="color: #E7EE98">)/opt/openssl@1.1/lib</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">CPPFLAGS</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">-I$(</span><span style="color: #62E884">brew</span><span style="color: #E7EE98"> </span><span style="color: #BF9EEE">--prefix</span><span style="color: #E7EE98">)/opt/openssl@1.1/include</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PKG_CONFIG_PATH</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">$(</span><span style="color: #62E884">brew</span><span style="color: #E7EE98"> </span><span style="color: #BF9EEE">--prefix</span><span style="color: #E7EE98">)/opt/openssl@1.1/lib/pkgconfig</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #62E884">rvm</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">autolibs</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">disable</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">RUBY_CFLAGS</span><span style="color: #F286C4">=</span><span style="color: #E7EE98">-DUSE_FFI_CLOSURE_ALLOC</span></span>
<span class="line"><span style="color: #F286C4">export</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">optflags</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">-Wno-error=implicit-function-declaration</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #62E884">rvm</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">3.2</span><span style="color: #E7EE98">.2</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">--with-openssl-dir=</span><span style="color: #E7EE98">$(</span><span style="color: #62E884">brew</span><span style="color: #E7EE98"> </span><span style="color: #BF9EEE">--prefix</span><span style="color: #E7EE98">)</span><span style="color: #BF9EEE">/opt/openssl@1.1</span></span></code></pre></div>



<p class="wp-block-paragraph">Nếu đã chạy các command ở trên vẫn không được hãy thử dụng tuyệt chiêu cuối cùng với command</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Bash</span><span role="button" tabindex="0" data-code="arch -x86_64 rvm install 3.2.2 -j 1" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">arch</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-x86_64</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">rvm</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">3.2</span><span style="color: #E7EE98">.2</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-j</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span></span></code></pre></div>



<p class="wp-block-paragraph">Tôi cũng đang sử dụng M1 và mọi thứ đã work đối với tôi.</p>
<p>The post <a href="https://blog.tomosia.com.vn/cai-dat-ruby-tren-m1-mac/">Cài đặt Ruby trên M1 Mac</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/cai-dat-ruby-tren-m1-mac/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>[Rails Tips] Rails 7.1 &#8211; ActiveRecord::QueryMethods#select has added support for hash values</title>
		<link>https://blog.tomosia.com.vn/rails-tips-rails-7-1-activerecordquerymethodsselect-has-added-support-for-hash-values/</link>
					<comments>https://blog.tomosia.com.vn/rails-tips-rails-7-1-activerecordquerymethodsselect-has-added-support-for-hash-values/#comments</comments>
		
		<dc:creator><![CDATA[Thuan Nguyen Van]]></dc:creator>
		<pubDate>Thu, 18 Jan 2024 01:45:59 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[ActiveRecord]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[Rails7.1]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3011</guid>

					<description><![CDATA[<p>Trong bản cập nhật mới nhất của, Rails 7.1 đã cho phép chúng ta query select theo giá&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/rails-tips-rails-7-1-activerecordquerymethodsselect-has-added-support-for-hash-values/">[Rails Tips] Rails 7.1 &#8211; ActiveRecord::QueryMethods#select has added support for hash values</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Trong bản cập nhật mới nhất của, Rails 7.1 đã cho phép chúng ta query <strong>select</strong> theo giá trị hash chứ không cần phải sử dụng <strong>raw SQL</strong> nữa.</p>



<p class="wp-block-paragraph">💎 Improvements:<br>1️⃣ New support query với giá trị <strong>Hash</strong><br>2️⃣ Chúng ta không cần phải dùng truy vấn <strong>raw version</strong> nữa</p>



<p class="wp-block-paragraph">💎 Bonus:<br>1️⃣ Cú pháp mới cũng support chúng ta sử dụng <strong>alias</strong><br>2️⃣ Sử dụng tương tự cho ActiveRecord#reselect</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="# Before Rails 7.1

Post.joins(:comments)
    .select(
      &quot;posts.id as post_id, posts.title as post_title,
      comments.id as comment_id, comments.body as comment_body&quot;
    )
    
Post.joins(:comments).select(:id, :title, &quot;comments.body&quot;)

# After Rails 7.1

Post.joins(:comments)
    .select(
      posts: { id: :post_id, title: :post_title },
      comments: { id: :comments_id, body: :comment_body }
    )
    
Post.joins(:comments).select(:id, :title, comments: [:body] )" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># Before Rails 7.1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">Post</span><span style="color: #F6F6F4">.joins(</span><span style="color: #BF9EEE">:comments</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    .</span><span style="color: #97E1F1">select</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">posts.id as post_id, posts.title as post_title,</span></span>
<span class="line"><span style="color: #E7EE98">      comments.id as comment_id, comments.body as comment_body</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">    )</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">Post</span><span style="color: #F6F6F4">.joins(</span><span style="color: #BF9EEE">:comments</span><span style="color: #F6F6F4">).</span><span style="color: #97E1F1">select</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">:id</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:title</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">comments.body</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># After Rails 7.1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">Post</span><span style="color: #F6F6F4">.joins(</span><span style="color: #BF9EEE">:comments</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    .</span><span style="color: #97E1F1">select</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">      posts</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> { id</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">:post_id</span><span style="color: #F6F6F4">, title</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">:post_title</span><span style="color: #F6F6F4"> },</span></span>
<span class="line"><span style="color: #F6F6F4">      comments</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> { id</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">:comments_id</span><span style="color: #F6F6F4">, body</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">:comment_body</span><span style="color: #F6F6F4"> }</span></span>
<span class="line"><span style="color: #F6F6F4">    )</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">Post</span><span style="color: #F6F6F4">.joins(</span><span style="color: #BF9EEE">:comments</span><span style="color: #F6F6F4">).</span><span style="color: #97E1F1">select</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">:id</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:title</span><span style="color: #F6F6F4">, comments</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> [</span><span style="color: #BF9EEE">:body</span><span style="color: #F6F6F4">] )</span></span></code></pre></div>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="956" height="551" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-02-at-14.42.49.png" alt="" class="wp-image-3016" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-02-at-14.42.49.png 956w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-02-at-14.42.49-300x173.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-02-at-14.42.49-768x443.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-02-at-14.42.49-380x219.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-02-at-14.42.49-800x461.png 800w" sizes="auto, (max-width: 956px) 100vw, 956px" /></figure>
<p>The post <a href="https://blog.tomosia.com.vn/rails-tips-rails-7-1-activerecordquerymethodsselect-has-added-support-for-hash-values/">[Rails Tips] Rails 7.1 &#8211; ActiveRecord::QueryMethods#select has added support for hash values</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/rails-tips-rails-7-1-activerecordquerymethodsselect-has-added-support-for-hash-values/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Upload file lên Amazon S3 bằng carrierwave</title>
		<link>https://blog.tomosia.com.vn/upload-file-len-amazon-s3-bang-carrierwave/</link>
					<comments>https://blog.tomosia.com.vn/upload-file-len-amazon-s3-bang-carrierwave/#comments</comments>
		
		<dc:creator><![CDATA[Quân Nguyễn]]></dc:creator>
		<pubDate>Thu, 28 Dec 2023 03:25:03 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=1117</guid>

					<description><![CDATA[<p>Với một hệ thống lớn cùng lượng data khổng lồ việc lưu file trực tiếp trên server có&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/upload-file-len-amazon-s3-bang-carrierwave/">Upload file lên Amazon S3 bằng carrierwave</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p class="has-text-align-left wp-block-paragraph" style="line-height:1.5">Với một hệ thống lớn cùng lượng data khổng lồ việc lưu file trực tiếp trên server có thể gặp khó khăn vì giới hạn bộ nhớ. Sử dụng dịch vụ lưu trữ tệp trực tuyến như Amazon S3 là giải pháp hữu hiệu cho vấn đề này. Và xem chúng ta có gì nào, carrierwave, một thư viện Ruby được dùng phổ biến hỗ trợ upload files một cách dễ dàng.</p>
</div></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-1024x512.png" alt="" class="wp-image-1166" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-1024x512.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-300x150.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-768x384.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-380x190.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-800x400.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40-1160x580.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-40.png 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div class="wp-block-group" style="padding-top:0;padding-right:0;padding-bottom:0;padding-left:0"><div class="wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-efdcd2e1 wp-block-group-is-layout-constrained">
<p class="wp-block-paragraph" style="line-height:1.5">Đừng lo lắng nếu bro không có account AWS, hãy tham khảo bài viết dưới link này để giả lập môi trường AWS phía local nhé. Shout-out cho người thầy của tui 😗<br><a href="http://blog.tomosia.com.vn/su-dung-localstack-de-gia-lap-upload-file-s3/">http://blog.tomosia.com.vn/su-dung-localstack-de-gia-lap-upload-file-s3/</a></p>



<p class="wp-block-paragraph">Ok vậy là đầy đủ đồ đạc rùi ae, chiến thôi</p>
</div></div>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p class="wp-block-paragraph">Trước tiên, để sử dụng những công cụ trên hãy add nó vào Gemfile nhé</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="gem 'carrierwave'
gem 'fog-aws'" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F8F8F2">gem </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">carrierwave</span><span style="color: #E9F284">&#39;</span></span>
<span class="line"><span style="color: #F8F8F2">gem </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">fog-aws</span><span style="color: #E9F284">&#39;</span></span></code></pre></div>



<p class="wp-block-paragraph" style="line-height:1.5">Fog AWS được sử dụng để hỗ trợ Amazon S3. Hãy chắc chắn rằng bạn có nó trong Gemfile của mình</p>
</div></div>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p class="wp-block-paragraph">Và đừng quên bundle để cài đặt chúng</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="bundle install" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F8F8F2">bundle install</span></span></code></pre></div>
</div></div>



<p class="wp-block-paragraph">Giả sử bạn đang cần upload avatar cho user</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="class User &lt; ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader
end" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #FF79C6">class</span><span style="color: #F8F8F2"> </span><span style="color: #8BE9FD">User</span><span style="color: #F8F8F2"> </span><span style="color: #FF79C6">&lt;</span><span style="color: #F8F8F2"> </span><span style="color: #8BE9FD; font-style: italic">ActiveRecord::Base</span></span>
<span class="line"><span style="color: #F8F8F2">  mount_uploader </span><span style="color: #BD93F9">:avatar</span><span style="color: #F8F8F2">, </span><span style="color: #BD93F9">AvatarUploader</span></span>
<span class="line"><span style="color: #FF79C6">end</span></span></code></pre></div>



<p class="wp-block-paragraph">Tiếp theo hãy tạo file uploader cho nó</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="class AvatarUploader &lt; CarrierWave::Uploader::Base
  storage :fog
end" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #FF79C6">class</span><span style="color: #F8F8F2"> </span><span style="color: #8BE9FD">AvatarUploader</span><span style="color: #F8F8F2"> </span><span style="color: #FF79C6">&lt;</span><span style="color: #F8F8F2"> </span><span style="color: #8BE9FD; font-style: italic">CarrierWave::Uploader::Base</span></span>
<span class="line"><span style="color: #F8F8F2">  storage </span><span style="color: #BD93F9">:fog</span></span>
<span class="line"><span style="color: #FF79C6">end</span></span></code></pre></div>



<p class="wp-block-paragraph" style="line-height:1.5">Để biết thêm về nhiều loại ma thuật khác như thay đổi tên file hay folder chứa, vân vân và mây mây ae có thể xem tại đây <br><a href="https://github.com/carrierwaveuploader/carrierwave">https://github.com/carrierwaveuploader/carrierwave</a></p>



<p class="wp-block-paragraph">Tạo file carrierwave.rb trong config/initializers và cấu hình credentials</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="CarrierWave.configure do |config|
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: 'S3_AWS_ACCESS_KEY_ID',
    aws_secret_access_key: 'S3_AWS_SECRET_ACCESS_KEY',
    region: 'us-east-1'
  }
  config.fog_directory = 'BUCKET_NAME'
end" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #8BE9FD; font-style: italic">CarrierWave</span><span style="color: #F8F8F2">.configure </span><span style="color: #FF79C6">do</span><span style="color: #F8F8F2"> |config|</span></span>
<span class="line"><span style="color: #F8F8F2">  config.fog_credentials </span><span style="color: #FF79C6">=</span><span style="color: #F8F8F2"> {</span></span>
<span class="line"><span style="color: #F8F8F2">    provider</span><span style="color: #FF79C6">:</span><span style="color: #F8F8F2"> </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">AWS</span><span style="color: #E9F284">&#39;</span><span style="color: #F8F8F2">,</span></span>
<span class="line"><span style="color: #F8F8F2">    aws_access_key_id</span><span style="color: #FF79C6">:</span><span style="color: #F8F8F2"> </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">S3_AWS_ACCESS_KEY_ID</span><span style="color: #E9F284">&#39;</span><span style="color: #F8F8F2">,</span></span>
<span class="line"><span style="color: #F8F8F2">    aws_secret_access_key</span><span style="color: #FF79C6">:</span><span style="color: #F8F8F2"> </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">S3_AWS_SECRET_ACCESS_KEY</span><span style="color: #E9F284">&#39;</span><span style="color: #F8F8F2">,</span></span>
<span class="line"><span style="color: #F8F8F2">    region</span><span style="color: #FF79C6">:</span><span style="color: #F8F8F2"> </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">us-east-1</span><span style="color: #E9F284">&#39;</span></span>
<span class="line"><span style="color: #F8F8F2">  }</span></span>
<span class="line"><span style="color: #F8F8F2">  config.fog_directory </span><span style="color: #FF79C6">=</span><span style="color: #F8F8F2"> </span><span style="color: #E9F284">&#39;</span><span style="color: #F1FA8C">BUCKET_NAME</span><span style="color: #E9F284">&#39;</span></span>
<span class="line"><span style="color: #FF79C6">end</span></span></code></pre></div>



<p class="wp-block-paragraph" style="line-height:1.5">There u go, giờ thì ae có thể upload file lên S3 r. Quá ư là đơn giản và dễ hiểu phải không 🐧🐧</p>
<p>The post <a href="https://blog.tomosia.com.vn/upload-file-len-amazon-s3-bang-carrierwave/">Upload file lên Amazon S3 bằng carrierwave</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/upload-file-len-amazon-s3-bang-carrierwave/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>Deploy Rails App: Tối Ưu Hóa Hiệu Suất Server Bằng Local Precompile</title>
		<link>https://blog.tomosia.com.vn/deploy-rails-app-toi-uu-hoa-hieu-suat-server-bang-local-precompile/</link>
					<comments>https://blog.tomosia.com.vn/deploy-rails-app-toi-uu-hoa-hieu-suat-server-bang-local-precompile/#comments</comments>
		
		<dc:creator><![CDATA[Thuc Phan]]></dc:creator>
		<pubDate>Thu, 28 Dec 2023 02:18:33 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=1273</guid>

					<description><![CDATA[<p>Giới thiệu Deploy bằng phương pháp mặc định: Precompile trực tiếp trên server config/deploy/production.rb config/deploy.rb config/puma/production.rb lib/capistrano/tasks/puma.rake Dùng&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/deploy-rails-app-toi-uu-hoa-hieu-suat-server-bang-local-precompile/">Deploy Rails App: Tối Ưu Hóa Hiệu Suất Server Bằng Local Precompile</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="gioi-thieu" class="wp-block-heading alignfull">Giới thiệu</h2>



<ul class="wp-block-list">
<li>Deploy đóng vai trò quan trọng trong quá trình phát triển ứng dụng. Rails đã tích hợp <strong><code>webpacker</code></strong> từ version <code>~&gt; 6.0</code> để quản lý tài nguyên JavaScript và CSS dễ dàng hơn</li>
</ul>



<ul class="wp-block-list">
<li>Khi ứng dụng của bạn ngày càng lớn thì việc <code><strong>assets:precompile</strong></code> sẽ tốn nhiều thời gian và tài nguyên của server. Điều này có thể gây downtime ứng dụng trong quá trình deploy, đặc biệt là trên các servers có tài nguyên hạn chế và workload cao</li>
</ul>



<ul class="wp-block-list">
<li>Bài viết này giới thiệu về cách dùng local precompile để tối ưu hoá hiệu suất trong quá trình deploy. Chúng ta sẽ tìm hiểu lý do tại sao việc <strong><code>assets:precompile</code></strong> trên server có thể gây nên vấn đề và giải pháp đơn giản chỉ là thực hiện precompile tại máy local</li>
</ul>



<h2 id="deploy-bang-phuong-phap-mac-dinh-precompile-truc-tiep-tren-server" class="wp-block-heading">Deploy bằng phương pháp mặc định: Precompile trực tiếp trên server</h2>



<ul class="wp-block-list">
<li>Bước đầu tiên là tạo mới một ứng dụng Rails</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="rails _6.0.6.1_ new local_precompile" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">rails</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">_6.0.6.1_</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">new</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">local_precompile</span></span></code></pre></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="280" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM-1024x280.png" alt="" class="wp-image-2977" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM-1024x280.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM-300x82.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM-768x210.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM-380x104.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM-800x219.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.29.15-PM.png 1140w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Kiểm tra xem ứng dụng đã hoạt động chưa</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="rails s" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">rails</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">s</span></span></code></pre></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="627" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-1024x627.png" alt="" class="wp-image-2978" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-1024x627.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-300x184.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-768x470.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-1536x940.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-2048x1254.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-380x233.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-800x490.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM-1160x710.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.33.00-PM.png 3198w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Thay đổi root path</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="rails g scaffold post title:string body:text" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">rails</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">g</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">scaffold</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">post</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">title:string</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">body:text</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="Rails.application.routes.draw do
  root to: 'posts#index'
  resources :posts
end" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Rails</span><span style="color: #F6F6F4">.application.routes.draw </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  root to</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">posts#index</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  resources </span><span style="color: #BF9EEE">:posts</span></span>
<span class="line"><span style="color: #F286C4">end</span></span></code></pre></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="625" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-1024x625.png" alt="" class="wp-image-2979" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-1024x625.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-300x183.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-768x469.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-1536x938.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-2048x1250.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-380x232.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-800x488.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM-1160x708.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screen-Shot-2023-12-31-at-6.35.36-PM.png 3198w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Commit code và push lên Git Repository của bạn</li>
</ul>



<ul class="wp-block-list">
<li>Tiếp theo mình sẽ Launch Instance và chọn OS là Ubuntu</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="583" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-1024x583.png" alt="" class="wp-image-1301" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-1024x583.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-300x171.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-768x438.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-1536x875.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-2048x1167.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-380x217.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-800x456.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM-1160x661.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/Screen-Shot-2023-10-16-at-9.50.45-PM.png 3194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>SSH vào server và cài đặt <strong><code>nginx</code></strong> làm web server</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="ssh -i ~/path-to-file-key ubuntu@public-ip-address
sudo apt update
sudo apt install nginx" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">ssh</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-i</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">~/path-to-file-key</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">ubuntu@public-ip-address</span></span>
<span class="line"><span style="color: #62E884">sudo</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">apt</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">update</span></span>
<span class="line"><span style="color: #62E884">sudo</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">apt</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">nginx</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Sau khi cài đặt <code><strong>nginx</strong></code> và mở được trang &#8220;Welcome to nginx!&#8221; trên trình duyệt bằng public-ip-address thì bạn đã hoàn thành nhiệm vụ 🤣</li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1024" height="622" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-46.png" alt="This image has an empty alt attribute; its file name is Screen-Shot-2023-10-16-at-10.35.14-PM-1024x622.png" class="wp-image-1316" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-46.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-46-300x182.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-46-768x467.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-46-380x231.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-46-800x486.png 800w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Tiếp theo, bạn cần cài đặt các packages cần thiết để ứng dụng Rails hoạt động được trên server</li>
</ul>



<ul class="wp-block-list">
<li>Đảm bảo cài đặt đúng version Ruby, Rails, Yarn, Node&#8230; mà bạn đã sử dụng để chạy ứng dụng ở máy local (điều này giúp tránh những lỗi không cần thiết liên quan đến version)</li>
</ul>



<ul class="wp-block-list">
<li>Quay lại ứng dụng Rails và cấu hình deploy bằng Capistrano. Cài đặt các gems cần thiết</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="group :development do
  # Capistrano - easy to deployment with Ruby over SSH
  gem 'capistrano', '~&gt; 3.18'
  # rbenv integration for Capistrano
  gem 'capistrano-rbenv', '~&gt; 2.2'
  # Rails specific Capistrano tasks
  gem 'capistrano-rails', '~&gt; 1.6', '&gt;= 1.6.3'
  # Bundler support for Capistrano 3.x
  gem 'capistrano-bundler', '~&gt; 2.1'
  # based on capistrano/npm gem
  gem 'capistrano-yarn', '~&gt; 2.0', '&gt;= 2.0.2'
  # nvm support for Capistrano 3.x
  gem 'capistrano-nvm', '~&gt; 0.0.7'
end" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">group </span><span style="color: #BF9EEE">:development</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Capistrano - easy to deployment with Ruby over SSH</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 3.18</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># rbenv integration for Capistrano</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-rbenv</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 2.2</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Rails specific Capistrano tasks</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-rails</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 1.6</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">&gt;= 1.6.3</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Bundler support for Capistrano 3.x</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-bundler</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 2.1</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># based on capistrano/npm gem</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-yarn</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 2.0</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">&gt;= 2.0.2</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># nvm support for Capistrano 3.x</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-nvm</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 0.0.7</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">end</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="bundle install
cap install" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">bundle</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span></span>
<span class="line"><span style="color: #62E884">cap</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Sau khi hoàn thành hai lệnh trên, hệ thống sẽ tự động tạo các files cần thiết để hỗ trợ deploy ứng dụng. Chỉnh sửa nội dụng các files sau<br></li>
</ul>



<p class="wp-block-paragraph"><code>config/deploy/production.rb</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="server &quot;public-ip-address&quot;, user: &quot;ubuntu&quot;, roles: %w{app db web}

set :ssh_options,
    forward_agent: true,
    auth_methods: %w[publickey],
    keys: %w[~/path-to-your-key]

set :branch, :main

set :rails_env, :production" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">server </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">public-ip-address</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, user</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">ubuntu</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, roles</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">%</span><span style="color: #F6F6F4">w{app db web}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:ssh_options</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    forward_agent</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">true</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    auth_methods</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">%</span><span style="color: #F6F6F4">w[publickey],</span></span>
<span class="line"><span style="color: #F6F6F4">    keys</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">%</span><span style="color: #F6F6F4">w[</span><span style="color: #F286C4">~</span><span style="color: #E7EE98">/path-to-your-key]</span></span>
<span class="line"></span>
<span class="line"><span style="color: #E7EE98">set :branch, :main</span></span>
<span class="line"></span>
<span class="line"><span style="color: #E7EE98">set :rails_env, :production</span></span></code></pre></div>



<p class="wp-block-paragraph"><code>config/deploy.rb</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="# config valid for current version and patch releases of Capistrano
lock &quot;~&gt; 3.18.0&quot;

set :application, &quot;local_precompile&quot;
set :repo_url, &quot;your-repo-url&quot;

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, &quot;/home/ubuntu/sites/local_precompile&quot;

# Default value for :pty is false
set :pty, true

# Default value for :linked_files is []
append :linked_files, &quot;config/master.key&quot;

# Default value for linked_dirs is []
append :linked_dirs, &quot;log&quot;, &quot;tmp/pids&quot;, &quot;tmp/cache&quot;, &quot;tmp/sockets&quot;, &quot;public/system&quot;, &quot;vendor&quot;, &quot;storage&quot;

# Default value for default_env is {}
set :default_env, { &quot;PATH&quot;: &quot;/home/ubuntu/.nvm/versions/node/v16.20.2/bin:$PATH&quot; }

set :puma_pid, shared_path.join('tmp/pids/server.pid')

set :puma_bind, &quot;unix://#{shared_path.join('tmp/sockets/puma.sock')}&quot;

set :puma_config_path, -&gt; { File.join(current_path, 'config', 'puma', &quot;#{fetch(:rails_env)}.rb&quot;) }" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># config valid for current version and patch releases of Capistrano</span></span>
<span class="line"><span style="color: #F6F6F4">lock </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">~&gt; 3.18.0</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:application</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">local_precompile</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:repo_url</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">your-repo-url</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Default deploy_to directory is /var/www/my_app_name</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:deploy_to</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/home/ubuntu/sites/local_precompile</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Default value for :pty is false</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:pty</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">true</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Default value for :linked_files is []</span></span>
<span class="line"><span style="color: #F6F6F4">append </span><span style="color: #BF9EEE">:linked_files</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">config/master.key</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Default value for linked_dirs is []</span></span>
<span class="line"><span style="color: #F6F6F4">append </span><span style="color: #BF9EEE">:linked_dirs</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">log</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">tmp/pids</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">tmp/cache</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">tmp/sockets</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">public/system</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">vendor</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">storage</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Default value for default_env is {}</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:default_env</span><span style="color: #F6F6F4">, { </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">PATH</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/home/ubuntu/.nvm/versions/node/v16.20.2/bin:$PATH</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:puma_pid</span><span style="color: #F6F6F4">, shared_path.join(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">tmp/pids/server.pid</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:puma_bind</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">unix://</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">shared_path.join(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">tmp/sockets/puma.sock</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:puma_config_path</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1">-&gt;</span><span style="color: #F6F6F4"> { </span><span style="color: #97E1F1; font-style: italic">File</span><span style="color: #F6F6F4">.join(current_path, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">config</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">puma</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:rails_env</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">.rb</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) }</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Thêm các modules cần thiết vào <code>/Capfile</code></li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="require &quot;capistrano/rbenv&quot;
set :rbenv_type, :user
set :rbenv_ruby, &quot;2.7.0&quot;
set :rbenv_map_bins, %w[rake gem bundle ruby rails]
set :rbenv_roles, :all

require &quot;capistrano/bundler&quot;
require &quot;capistrano/rails/assets&quot;
require &quot;capistrano/rails/migrations&quot;

require &quot;capistrano/yarn&quot;
set :yarn_flags, '--frozen-lockfile'

require &quot;capistrano/nvm&quot;
set :nvm_type, :user
set :nvm_node, &quot;v16.20.2&quot;
set :nvm_map_bins, %w[node npm yarn]" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/rbenv</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_type</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:user</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_ruby</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">2.7.0</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_map_bins</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">%</span><span style="color: #F6F6F4">w[rake gem bundle ruby rails]</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_roles</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:all</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/bundler</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/rails/assets</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/rails/migrations</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/yarn</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:yarn_flags</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">--frozen-lockfile</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/nvm</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:nvm_type</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:user</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:nvm_node</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">v16.20.2</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:nvm_map_bins</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">%</span><span style="color: #F6F6F4">w[node npm yarn]</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Tạo một file puma configuration cho môi trường production như sau</li>
</ul>



<p class="wp-block-paragraph"><code>config/puma/production.rb</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch(&quot;RAILS_MAX_THREADS&quot;) { 5 }
min_threads_count = ENV.fetch(&quot;RAILS_MIN_THREADS&quot;) { max_threads_count }
threads min_threads_count, max_threads_count

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch(&quot;PORT&quot;) { 3000 }

# Specifies the `environment` that Puma will run in.
#
environment &quot;production&quot;

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch(&quot;PIDFILE&quot;) { &quot;tmp/pids/server.pid&quot; }

tmp_path = &quot;#{File.expand_path('../../..', __FILE__)}/tmp&quot;
bind &quot;unix://#{tmp_path}/sockets/puma.sock&quot;

log_path = &quot;#{File.expand_path('../../..', __FILE__)}/log&quot;
stdout_redirect &quot;#{log_path}/puma.stdout.log&quot;, &quot;#{log_path}/puma.stderr.log&quot;, true

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch(&quot;WEB_CONCURRENCY&quot;) { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
preload_app!

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># Puma can serve each request in a thread from an internal thread pool.</span></span>
<span class="line"><span style="color: #7B7F8B"># The `threads` method setting takes two numbers: a minimum and maximum.</span></span>
<span class="line"><span style="color: #7B7F8B"># Any libraries that use thread pools should be configured to match</span></span>
<span class="line"><span style="color: #7B7F8B"># the maximum value specified for Puma. Default is set to 5 threads for minimum</span></span>
<span class="line"><span style="color: #7B7F8B"># and maximum; this matches the default thread size of Active Record.</span></span>
<span class="line"><span style="color: #7B7F8B">#</span></span>
<span class="line"><span style="color: #F6F6F4">max_threads_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ENV</span><span style="color: #F6F6F4">.fetch(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">RAILS_MAX_THREADS</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) { </span><span style="color: #BF9EEE">5</span><span style="color: #F6F6F4"> }</span></span>
<span class="line"><span style="color: #F6F6F4">min_threads_count </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">ENV</span><span style="color: #F6F6F4">.fetch(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">RAILS_MIN_THREADS</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) { max_threads_count }</span></span>
<span class="line"><span style="color: #F6F6F4">threads min_threads_count, max_threads_count</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Specifies the `port` that Puma will listen on to receive requests; default is 3000.</span></span>
<span class="line"><span style="color: #7B7F8B">#</span></span>
<span class="line"><span style="color: #F6F6F4">port </span><span style="color: #97E1F1; font-style: italic">ENV</span><span style="color: #F6F6F4">.fetch(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">PORT</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) { </span><span style="color: #BF9EEE">3000</span><span style="color: #F6F6F4"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Specifies the `environment` that Puma will run in.</span></span>
<span class="line"><span style="color: #7B7F8B">#</span></span>
<span class="line"><span style="color: #F6F6F4">environment </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">production</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Specifies the `pidfile` that Puma will use.</span></span>
<span class="line"><span style="color: #F6F6F4">pidfile </span><span style="color: #97E1F1; font-style: italic">ENV</span><span style="color: #F6F6F4">.fetch(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">PIDFILE</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">) { </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">tmp/pids/server.pid</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">tmp_path </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #97E1F1; font-style: italic">File</span><span style="color: #E7EE98">.expand_path(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">../../..</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">, </span><span style="color: #BF9EEE; font-style: italic">__FILE__</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/tmp</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">bind </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">unix://</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">tmp_path</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/sockets/puma.sock</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">log_path </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #97E1F1; font-style: italic">File</span><span style="color: #E7EE98">.expand_path(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">../../..</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">, </span><span style="color: #BF9EEE; font-style: italic">__FILE__</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/log</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">stdout_redirect </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">log_path</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/puma.stdout.log</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">log_path</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/puma.stderr.log</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">true</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Specifies the number of `workers` to boot in clustered mode.</span></span>
<span class="line"><span style="color: #7B7F8B"># Workers are forked web server processes. If using threads and workers together</span></span>
<span class="line"><span style="color: #7B7F8B"># the concurrency of the application would be max `threads` * `workers`.</span></span>
<span class="line"><span style="color: #7B7F8B"># Workers do not work on JRuby or Windows (both of which do not support</span></span>
<span class="line"><span style="color: #7B7F8B"># processes).</span></span>
<span class="line"><span style="color: #7B7F8B">#</span></span>
<span class="line"><span style="color: #7B7F8B"># workers ENV.fetch(&quot;WEB_CONCURRENCY&quot;) { 2 }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Use the `preload_app!` method when specifying a `workers` number.</span></span>
<span class="line"><span style="color: #7B7F8B"># This directive tells Puma to first boot the application and load code</span></span>
<span class="line"><span style="color: #7B7F8B"># before forking the application. This takes advantage of Copy On Write</span></span>
<span class="line"><span style="color: #7B7F8B"># process behavior so workers use less memory.</span></span>
<span class="line"><span style="color: #7B7F8B">#</span></span>
<span class="line"><span style="color: #F6F6F4">preload_app!</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># Allow puma to be restarted by `rails restart` command.</span></span>
<span class="line"><span style="color: #F6F6F4">plugin </span><span style="color: #BF9EEE">:tmp_restart</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Tạo một file puma rake task như sau</li>
</ul>



<p class="wp-block-paragraph"><code>lib/capistrano/tasks/puma.rake</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="namespace :puma do
  desc 'Start puma'
  task :start do
    on roles(:app) do
      within current_path do
        execute :bundle, 'exec', 'puma', '--config', &quot;#{fetch(:puma_config_path)}&quot;, '--daemon'
      end
    end
  end

  desc 'Restart puma'
  task :restart do
    on roles(:app) do
      within current_path do
        if test(&quot;[ -f #{shared_path}/tmp/pids/server.pid ]&quot;)
          execute :bundle, :exec, :pumactl, '-P', fetch(:puma_pid), 'restart', '--config', fetch(:puma_config_path)
        else
          invoke 'puma:start'
        end
      end
    end
  end

  desc 'Stop puma'
  task :stop do
    on roles(:app) do
      within current_path do
        execute :bundle, :exec, 'pumactl', '-P', fetch(:puma_pid), 'stop'
      end
    end
  end
end

namespace :load do
  task :defaults do
    after 'deploy:publishing', 'puma:restart'
  end
end" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">namespace </span><span style="color: #BF9EEE">:puma</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  desc </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Start puma</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  task </span><span style="color: #BF9EEE">:start</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">    on roles(</span><span style="color: #BF9EEE">:app</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">      within current_path </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">        execute </span><span style="color: #BF9EEE">:bundle</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">exec</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">puma</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">--config</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:puma_config_path</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">--daemon</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">  desc </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Restart puma</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  task </span><span style="color: #BF9EEE">:restart</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">    on roles(</span><span style="color: #BF9EEE">:app</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">      within current_path </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">test</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">[ -f </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">shared_path</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/tmp/pids/server.pid ]</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">          execute </span><span style="color: #BF9EEE">:bundle</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:exec</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:pumactl</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">-P</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, fetch(</span><span style="color: #BF9EEE">:puma_pid</span><span style="color: #F6F6F4">), </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">restart</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">--config</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, fetch(</span><span style="color: #BF9EEE">:puma_config_path</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">else</span></span>
<span class="line"><span style="color: #F6F6F4">          invoke </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">puma:start</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">  desc </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Stop puma</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  task </span><span style="color: #BF9EEE">:stop</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">    on roles(</span><span style="color: #BF9EEE">:app</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">      within current_path </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">        execute </span><span style="color: #BF9EEE">:bundle</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:exec</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">pumactl</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">-P</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, fetch(</span><span style="color: #BF9EEE">:puma_pid</span><span style="color: #F6F6F4">), </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">stop</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">namespace </span><span style="color: #BF9EEE">:load</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  task </span><span style="color: #BF9EEE">:defaults</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">    after </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">deploy:publishing</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">puma:restart</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F286C4">end</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Thêm cấu hình cho <strong><code>nginx</code></strong></li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="sudo vi /etc/nginx/conf.d/local_precompile.conf" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">sudo</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">vi</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">/etc/nginx/conf.d/local_precompile.conf</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="upstream local_precompile {
  server unix:///home/ubuntu/sites/local_precompile/shared/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name 54.196.84.135;

  root /home/ubuntu/sites/local_precompile/current/public;
  try_files $uri/index.html $uri @puma;

  location @puma {
    proxy_pass http://unix:/home/ubuntu/sites/local_precompile/shared/tmp/sockets/puma.sock;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">upstream</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">local_precompile</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">{</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">server</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">unix:///home/ubuntu/sites/local_precompile/shared/tmp/sockets/puma.sock</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #62E884">server</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">{</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">listen</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">80</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">server_name</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">54.196</span><span style="color: #E7EE98">.84.135</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">root</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">/home/ubuntu/sites/local_precompile/current/public</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">try_files</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">$uri</span><span style="color: #E7EE98">/index.html</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">$uri</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">@puma</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">location</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">@puma</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">{</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">proxy_pass</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">http://unix:/home/ubuntu/sites/local_precompile/shared/tmp/sockets/puma.sock</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">proxy_set_header</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">Host</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">$host</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">proxy_set_header</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">X-Real-IP</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">$remote_addr</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">proxy_set_header</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">X-Forwarded-For</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">$proxy_add_x_forwarded_for</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">proxy_set_header</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">X-Forwarded-Proto</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">$scheme</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">  }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="sudo service nginx restart" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">sudo</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">service</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">nginx</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">restart</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Khi tất cả đã được thiết lập xong thì chạy lệnh sau để deploy</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="cap production deploy" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">cap</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">production</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">deploy</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Trong những lần deploy đầu chắc chắn sẽ có những lỗi nhỏ, tuỳ trường hợp mà các bạn có thể tự xử lý nhé 🤣</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="626" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-1024x626.png" alt="" class="wp-image-2988" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-1024x626.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-300x183.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-768x470.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-1536x939.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-2048x1252.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-380x232.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-800x489.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM-1160x709.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2024-01-01-at-8.42.55-AM.png 3196w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Quan sát <code><strong>htop</strong></code> trong quá trình deploy, có thể thấy workload của CPU và Memory tăng cao rõ rệt trong lúc thực hiện<strong> <code>yarn:install</code></strong> và <code><strong>deploy:assets:precompile</strong></code></li>
</ul>



<ul class="wp-block-list">
<li>Đó là nguyên nhân chính có thể gây downtime ứng dụng</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="236" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-1024x236.png" alt="" class="wp-image-2989" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-1024x236.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-300x69.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-768x177.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-1536x354.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-380x88.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-800x185.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM-1160x268.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screen-Shot-2023-12-31-at-9.38.40-AM.png 1604w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 id="dung-local-precompile" class="wp-block-heading">Dùng Local Precompile</h2>



<ul class="wp-block-list">
<li>Phát hiện vấn đề chính nằm ở <code><strong>yarn:install</strong></code> và <code><strong>deploy:assets:precompile</strong></code></li>
</ul>



<ul class="wp-block-list">
<li>Tiếp theo chúng ta cần chuyển hai tác vụ này về xử lý ở máy local, sau đó upload các files cần thiết lên server là xong</li>
</ul>



<ul class="wp-block-list">
<li>Tạo một file deploy rake task như sau</li>
</ul>



<p class="wp-block-paragraph"><code>lib/capistrano/tasks/deploy.rake</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="namespace :deploy do
  namespace :assets do
    desc 'Precompile assets locally'
    task :local_precompile do
      run_locally do
        execute &quot;rm -rf #{fetch(:cache_dir)}&quot;
        execute &quot;RAILS_ENV=#{fetch(:rails_env)} bundle exec rake assets:clean&quot;
        execute &quot;RAILS_ENV=#{fetch(:rails_env)} bundle exec rake assets:precompile&quot;
      end
    end

    desc 'rsync assets to web server'
    task :rsync_assets do
      on roles(:web), in: :parallel do |server|
        run_locally do
          ssh_shell   = %(-e &quot;ssh -i #{fetch(:ssh_options)[:keys].first}&quot;)
          rsync_shell = &quot;rsync -avz #{ssh_shell}&quot;
          release_dir = &quot;username@public-ip-address:#{release_path}&quot;
          commands    = []

          commands &lt;&lt; &quot;#{rsync_shell} ./#{fetch(:assets_dir)} #{release_dir}/#{fetch(:assets_dir)}&quot; if Dir.exists?(fetch(:assets_dir))
          commands &lt;&lt; &quot;#{rsync_shell} ./#{fetch(:packs_dir)}  #{release_dir}/#{fetch(:packs_dir)}&quot;  if Dir.exists?(fetch(:packs_dir))
          commands &lt;&lt; &quot;#{rsync_shell} ./#{fetch(:cache_dir)}  #{release_dir}/#{fetch(:cache_dir)}&quot;  if Dir.exists?(fetch(:cache_dir))

          commands.each { |command| dry_run? ? SSHKit.config.output.info(command) : execute(command) }
        end
      end
    end
  end
end

namespace :load do
  task :defaults do
    set :assets_role, 'web'
    set :assets_dir,  'public/assets/'
    set :packs_dir,   'public/packs/'
    set :cache_dir,   'tmp/cache/'

    after 'deploy:migrate', 'deploy:assets:local_precompile'
    after 'deploy:assets:local_precompile', 'deploy:assets:rsync_assets'
  end
end" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">namespace </span><span style="color: #BF9EEE">:deploy</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  namespace </span><span style="color: #BF9EEE">:assets</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">    desc </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Precompile assets locally</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    task </span><span style="color: #BF9EEE">:local_precompile</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">      run_locally </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">        execute </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">rm -rf </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:cache_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">        execute </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">RAILS_ENV=</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:rails_env</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98"> bundle exec rake assets:clean</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">        execute </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">RAILS_ENV=</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:rails_env</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98"> bundle exec rake assets:precompile</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    desc </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">rsync assets to web server</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    task </span><span style="color: #BF9EEE">:rsync_assets</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">      on roles(</span><span style="color: #BF9EEE">:web</span><span style="color: #F6F6F4">), in</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">:parallel</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span><span style="color: #F6F6F4"> |server|</span></span>
<span class="line"><span style="color: #F6F6F4">        run_locally </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">          ssh_shell   </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">%(</span><span style="color: #E7EE98">-e &quot;ssh -i </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:ssh_options</span><span style="color: #E7EE98">)[</span><span style="color: #BF9EEE">:keys</span><span style="color: #E7EE98">].first</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">&quot;</span><span style="color: #DEE492">)</span></span>
<span class="line"><span style="color: #F6F6F4">          rsync_shell </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">rsync -avz </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">ssh_shell</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">          release_dir </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">username@public-ip-address:</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">release_path</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">          commands    </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> []</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">          commands </span><span style="color: #F286C4">&lt;&lt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">rsync_shell</span><span style="color: #F286C4">}</span><span style="color: #E7EE98"> ./</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:assets_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98"> </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">release_dir</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:assets_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Dir</span><span style="color: #F6F6F4">.exists?(fetch(</span><span style="color: #BF9EEE">:assets_dir</span><span style="color: #F6F6F4">))</span></span>
<span class="line"><span style="color: #F6F6F4">          commands </span><span style="color: #F286C4">&lt;&lt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">rsync_shell</span><span style="color: #F286C4">}</span><span style="color: #E7EE98"> ./</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:packs_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">  </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">release_dir</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:packs_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">  </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Dir</span><span style="color: #F6F6F4">.exists?(fetch(</span><span style="color: #BF9EEE">:packs_dir</span><span style="color: #F6F6F4">))</span></span>
<span class="line"><span style="color: #F6F6F4">          commands </span><span style="color: #F286C4">&lt;&lt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">rsync_shell</span><span style="color: #F286C4">}</span><span style="color: #E7EE98"> ./</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:cache_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">  </span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">release_dir</span><span style="color: #F286C4">}</span><span style="color: #E7EE98">/</span><span style="color: #F286C4">#{</span><span style="color: #E7EE98">fetch(</span><span style="color: #BF9EEE">:cache_dir</span><span style="color: #E7EE98">)</span><span style="color: #F286C4">}</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">  </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Dir</span><span style="color: #F6F6F4">.exists?(fetch(</span><span style="color: #BF9EEE">:cache_dir</span><span style="color: #F6F6F4">))</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">          commands.each { |command| dry_run? </span><span style="color: #F286C4">?</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SSHKit</span><span style="color: #F6F6F4">.config.output.info(command) : execute(command) }</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">namespace </span><span style="color: #BF9EEE">:load</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  task </span><span style="color: #BF9EEE">:defaults</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">    set </span><span style="color: #BF9EEE">:assets_role</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">web</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    set </span><span style="color: #BF9EEE">:assets_dir</span><span style="color: #F6F6F4">,  </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">public/assets/</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    set </span><span style="color: #BF9EEE">:packs_dir</span><span style="color: #F6F6F4">,   </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">public/packs/</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    set </span><span style="color: #BF9EEE">:cache_dir</span><span style="color: #F6F6F4">,   </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">tmp/cache/</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    after </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">deploy:migrate</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">deploy:assets:local_precompile</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    after </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">deploy:assets:local_precompile</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">deploy:assets:rsync_assets</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">end</span></span>
<span class="line"><span style="color: #F286C4">end</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Rake task này có hai nhiệm vụ
<ul class="wp-block-list">
<li>Precompile assets ở local</li>



<li>Đồng bộ files lên server</li>
</ul>
</li>
</ul>



<ul class="wp-block-list">
<li>Server không cần phải thực hiện <code><strong>yarn:install</strong></code> và <code><strong>deploy:assets:precompile</strong></code> mỗi khi deploy nữa</li>
</ul>



<ul class="wp-block-list">
<li>Clean code khi dùng local precompile</li>
</ul>



<p class="wp-block-paragraph"><code>Gemfile</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="group :development do
  # Capistrano - easy to deployment with Ruby over SSH
  gem 'capistrano', '~&gt; 3.18'
  # rbenv integration for Capistrano
  gem 'capistrano-rbenv', '~&gt; 2.2'
  # Rails specific Capistrano tasks
  gem 'capistrano-rails', '~&gt; 1.6', '&gt;= 1.6.3'
  # Bundler support for Capistrano 3.x
  gem 'capistrano-bundler', '~&gt; 2.1'
end" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">group </span><span style="color: #BF9EEE">:development</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Capistrano - easy to deployment with Ruby over SSH</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 3.18</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># rbenv integration for Capistrano</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-rbenv</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 2.2</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Rails specific Capistrano tasks</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-rails</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 1.6</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">&gt;= 1.6.3</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Bundler support for Capistrano 3.x</span></span>
<span class="line"><span style="color: #F6F6F4">  gem </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">capistrano-bundler</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">~&gt; 2.1</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">end</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="bundle install" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">bundle</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">install</span></span></code></pre></div>



<p class="wp-block-paragraph"><code>Capfile</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="require &quot;capistrano/rbenv&quot;
set :rbenv_type, :user
set :rbenv_ruby, &quot;2.7.0&quot;
set :rbenv_map_bins, %w[rake gem bundle ruby rails]
set :rbenv_roles, :all

require &quot;capistrano/bundler&quot;
require &quot;capistrano/rails/migrations&quot;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/rbenv</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_type</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:user</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_ruby</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">2.7.0</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_map_bins</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">%</span><span style="color: #F6F6F4">w[rake gem bundle ruby rails]</span></span>
<span class="line"><span style="color: #F6F6F4">set </span><span style="color: #BF9EEE">:rbenv_roles</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">:all</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/bundler</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">capistrano/rails/migrations</span><span style="color: #DEE492">&quot;</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Commit code và push lên Git Repository của bạn</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="cap production deploy" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">cap</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">production</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">deploy</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Quan sát <code><strong>htop</strong></code> bạn sẽ không còn thấy sự ảnh hưởng đến workload của CPU và Memory trong quá trình deploy nữa</li>
</ul>



<h2 id="loi-ket" class="wp-block-heading">Lời kết</h2>



<ul class="wp-block-list">
<li>Bài viết là một trong những giải pháp liên quan đến hiệu suất server mà team mình đã từng áp dụng trong thực tế</li>
</ul>



<ul class="wp-block-list">
<li>Chúc các bạn thực hiện thành công và có những trải nghiệm tuyệt vời trên server yêu dấu của client nhé 😆</li>
</ul>
<p>The post <a href="https://blog.tomosia.com.vn/deploy-rails-app-toi-uu-hoa-hieu-suat-server-bang-local-precompile/">Deploy Rails App: Tối Ưu Hóa Hiệu Suất Server Bằng Local Precompile</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/deploy-rails-app-toi-uu-hoa-hieu-suat-server-bang-local-precompile/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title>Tối ưu hóa SEO với Sitemap</title>
		<link>https://blog.tomosia.com.vn/toi-uu-hoa-seo-voi-sitemap/</link>
					<comments>https://blog.tomosia.com.vn/toi-uu-hoa-seo-voi-sitemap/#comments</comments>
		
		<dc:creator><![CDATA[Le Van Ninh]]></dc:creator>
		<pubDate>Wed, 27 Dec 2023 04:31:22 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[sitemap]]></category>
		<category><![CDATA[seo]]></category>
		<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2898</guid>

					<description><![CDATA[<p>SEO (Search Engine Optimization) đóng vai trò quan trọng trong việc đưa trang web của bạn đến gần&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/toi-uu-hoa-seo-voi-sitemap/">Tối ưu hóa SEO với Sitemap</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">SEO (Search Engine Optimization) đóng vai trò quan trọng trong việc đưa trang web của bạn đến gần người dùng và công cụ tìm kiếm. Một công cụ hữu ích trong chiến lược SEO của bạn là sitemap.</p>



<h2 id="muc-dich-cua-sitemap" class="wp-block-heading">Mục đích của Sitemap</h2>



<ol class="wp-block-list">
<li><strong>Hiệu suất Indexing và SEO</strong><br>Giúp công cụ tìm kiếm hiểu rõ cấu trúc và nội dung của trang web. Điều này giúp tối ưu hóa hiệu suất indexing, đặc biệt là khi trang web có nhiều nội dung hoặc cấu trúc phức tạp.</li>



<li><strong>Ưu tiên Indexing</strong><br>Thông qua các thông tin như <code>lastmod</code> (thời điểm sửa đổi gần nhất), <code>changefreq</code> (tần suất thay đổi), và <code>priority</code> (độ ưu tiên), sitemap giúp bạn quyết định trang nào quan trọng hơn và nên được cập nhật thường xuyên hơn.</li>



<li><strong>Hỗ trợ trải nghiệm người dùng</strong><br>Sitemap không chỉ hỗ trợ công cụ tìm kiếm mà còn cải thiện trải nghiệm người dùng bằng cách cung cấp các liên kết dẫn đến các trang quan trọng.</li>



<li><strong>Điều hướng Crawl</strong> <strong>bổ sung</strong><br>Đối với các trang web có cấu trúc phức tạp, sitemap giúp điều hướng crawl bổ sung, đặc biệt là đối với các trang được tạo ra bằng JavaScript.</li>
</ol>



<h2 id="su-dung-sitemap-trong-ror" class="wp-block-heading">Sử dụng Sitemap trong ROR</h2>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">YAML</span><span role="button" tabindex="0" data-code="# Gemfile

gem 'sitemap_generator'" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># Gemfile</span></span>
<span class="line"></span>
<span class="line"><span style="color: #E7EE98">gem &#39;sitemap_generator&#39;</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="# config/sitemap.rb

require 'rubygems'
require 'sitemap_generator'

SitemapGenerator::Sitemap.default_host = 'https://yourdomain.com'
SitemapGenerator::Sitemap.create do
  add '/', changefreq: 'daily', priority: 1.0
  add '/page', changefreq: 'weekly', priority: 0.8
  # Thêm các trang khác và thông tin
end

# ping công cụ tìm kiếm google và bing để thông báo về sự thay đổi của sitemap
SitemapGenerator::Sitemap.ping_search_engines" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># config/sitemap.rb</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">rubygems</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">sitemap_generator</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">SitemapGenerator</span><span style="color: #F286C4">::</span><span style="color: #97E1F1; font-style: italic">Sitemap</span><span style="color: #F6F6F4">.default_host </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">https://yourdomain.com</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">SitemapGenerator</span><span style="color: #F286C4">::</span><span style="color: #97E1F1; font-style: italic">Sitemap</span><span style="color: #F6F6F4">.create </span><span style="color: #F286C4">do</span></span>
<span class="line"><span style="color: #F6F6F4">  add </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, changefreq</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">daily</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, priority</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1.0</span></span>
<span class="line"><span style="color: #F6F6F4">  add </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/page</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, changefreq</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">weekly</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, priority</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0.8</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B"># Thêm các trang khác và thông tin</span></span>
<span class="line"><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B"># ping công cụ tìm kiếm google và bing để thông báo về sự thay đổi của sitemap</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">SitemapGenerator</span><span style="color: #F286C4">::</span><span style="color: #97E1F1; font-style: italic">Sitemap</span><span style="color: #F6F6F4">.ping_search_engines</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Zsh</span><span role="button" tabindex="0" data-code="# Tạo file sitemap.xml

rake sitemap:create #=&gt; public/sitemap.xml" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># Tạo file sitemap.xml</span></span>
<span class="line"></span>
<span class="line"><span style="color: #62E884">rake</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">sitemap:create</span><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">#=&gt; public/sitemap.xml</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">YAML</span><span role="button" tabindex="0" data-code="# public/robots.txt

Sitemap: https://yourdomain/sitemap.xml" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B"># public/robots.txt</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1">Sitemap</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">https://yourdomain/sitemap.xml</span></span></code></pre></div>



<h2 id="luu-y-khi-tao-sitemap" class="wp-block-heading">Lưu ý khi tạo Sitemap</h2>



<ul class="wp-block-list">
<li>Phân chia công việc tạo sitemap thành các nhiệm vụ nhỏ để giảm gánh nặng hệ thống.</li>



<li>Xác định các tiêu chí để chỉ tạo sitemap cho những trang quan trọng nhất, giảm độ lớn của sitemap.</li>



<li>Tối ưu cơ sở dữ liệu để đảm bảo tìm kiếm và truy xuất dữ liệu một cách hiệu quả.</li>



<li>Theo dõi quá trình tạo sitemap và lưu lại log để giám sát và điều chỉnh khi cần thiết.</li>



<li>Nên tạo sitemap vào những thời điểm ít người truy cập nhất để giảm tác động đến hiệu suất hệ thống và người dùng.</li>
</ul>



<h2 id="ket-luan" class="wp-block-heading">Kết luận</h2>



<p class="wp-block-paragraph">Sitemap là chìa khóa quan trọng cho chiến lược SEO và hiệu suất web. Bằng cách đặt độ ưu tiên và tuân thủ lưu ý quan trọng, sitemap không chỉ tối ưu hóa hiệu suất hệ thống mà còn định hình một chiến lược SEO thành công.</p>



<p class="wp-block-paragraph">Cảm ơn bạn đã đọc.</p>
<p>The post <a href="https://blog.tomosia.com.vn/toi-uu-hoa-seo-voi-sitemap/">Tối ưu hóa SEO với Sitemap</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/toi-uu-hoa-seo-voi-sitemap/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Setup slack notifications in Rails</title>
		<link>https://blog.tomosia.com.vn/setup-slack-notifications-in-rails/</link>
					<comments>https://blog.tomosia.com.vn/setup-slack-notifications-in-rails/#comments</comments>
		
		<dc:creator><![CDATA[NST]]></dc:creator>
		<pubDate>Tue, 26 Dec 2023 02:05:13 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby On Rails]]></category>
		<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2766</guid>

					<description><![CDATA[<p>Slack là một phần mềm Worksplace sử dụng thông dụng rộng rãi. Tuy nhiên, ta có thể sử&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/setup-slack-notifications-in-rails/">Setup slack notifications in Rails</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="900" height="182" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/6423429367ad4157dea8a421_DUd_u8MT6t1xMJ6uXhHyllmQmHzK6KTyp1c5Xfxk8jmfvnrc-MEkdKAryNdNMD_WJQ5N67JxAVcq5JjFg7Oovf4f3SfthtgdBmmR-wyJETHD1twRYCObfA6aJUzu0xTSR2MB3zCX.png" alt="" class="wp-image-2778" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/6423429367ad4157dea8a421_DUd_u8MT6t1xMJ6uXhHyllmQmHzK6KTyp1c5Xfxk8jmfvnrc-MEkdKAryNdNMD_WJQ5N67JxAVcq5JjFg7Oovf4f3SfthtgdBmmR-wyJETHD1twRYCObfA6aJUzu0xTSR2MB3zCX.png 900w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/6423429367ad4157dea8a421_DUd_u8MT6t1xMJ6uXhHyllmQmHzK6KTyp1c5Xfxk8jmfvnrc-MEkdKAryNdNMD_WJQ5N67JxAVcq5JjFg7Oovf4f3SfthtgdBmmR-wyJETHD1twRYCObfA6aJUzu0xTSR2MB3zCX-300x61.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/6423429367ad4157dea8a421_DUd_u8MT6t1xMJ6uXhHyllmQmHzK6KTyp1c5Xfxk8jmfvnrc-MEkdKAryNdNMD_WJQ5N67JxAVcq5JjFg7Oovf4f3SfthtgdBmmR-wyJETHD1twRYCObfA6aJUzu0xTSR2MB3zCX-768x155.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/6423429367ad4157dea8a421_DUd_u8MT6t1xMJ6uXhHyllmQmHzK6KTyp1c5Xfxk8jmfvnrc-MEkdKAryNdNMD_WJQ5N67JxAVcq5JjFg7Oovf4f3SfthtgdBmmR-wyJETHD1twRYCObfA6aJUzu0xTSR2MB3zCX-380x77.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/6423429367ad4157dea8a421_DUd_u8MT6t1xMJ6uXhHyllmQmHzK6KTyp1c5Xfxk8jmfvnrc-MEkdKAryNdNMD_WJQ5N67JxAVcq5JjFg7Oovf4f3SfthtgdBmmR-wyJETHD1twRYCObfA6aJUzu0xTSR2MB3zCX-800x162.png 800w" sizes="auto, (max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">Slack là một phần mềm <strong>Worksplace</strong> sử dụng thông dụng rộng rãi. Tuy nhiên, ta có thể sử dụng slack để theo dõi và giám sát hệ thống, gửi tin nhắn tự động.</p>



<p class="wp-block-paragraph">Sau đây, mình sẽ hướng dẫn mọi người cách để gửi thông báo, tin nhắn … đến slack trong rails app, nó làm cho Rails Application của bạn có thể trở nên năng động và hiệu quả hơn.</p>



<h3 id="setup-slack" class="wp-block-heading"><strong>Setup Slack</strong>:</h3>



<p class="wp-block-paragraph">Đầu tiên, bạn phải có 1 Worksplace, 1 channel nào đó rồi nhé.</p>



<ul class="wp-block-list">
<li>Bạn cần tạo một App Slack từ <a href="https://api.slack.com/apps">https://api.slack.com/apps</a>, chọn <strong>Create an app →</strong> <strong>From Scratch (điền tên và chọn workspace)</strong></li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="736" height="816" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.17.47-1.png" alt="" class="wp-image-2772" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.17.47-1.png 736w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.17.47-1-271x300.png 271w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.17.47-1-380x421.png 380w" sizes="auto, (max-width: 736px) 100vw, 736px" /></figure>



<ul class="wp-block-list">
<li>Chọn tab <strong>Incoming Webhooks</strong> bạn sẽ có: 
<ul class="wp-block-list">
<li><strong>Webhook URL</strong></li>



<li><strong>Channel</strong></li>
</ul>
</li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1015" height="668" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.19.11.png" alt="" class="wp-image-2773" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.19.11.png 1015w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.19.11-300x197.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.19.11-768x505.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.19.11-380x250.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.19.11-800x527.png 800w" sizes="auto, (max-width: 1015px) 100vw, 1015px" /></figure>



<h3 id="setup-rails" class="wp-block-heading"><strong>Setup Rails:</strong></h3>



<p class="wp-block-paragraph">Đầu tiên, bạn tự thêm gem vào trong <strong>Gemfile</strong> và <strong>Bundle</strong> nhé.</p>



<h5 id="1-su-dung-slack-notifier" class="wp-block-heading">1. Sử dụng <code>slack-notifier</code></h5>



<ul class="wp-block-list">
<li><code>gem "slack-notifier"</code> <a href="https://github.com/slack-notifier/slack-notifier">https://github.com/slack-notifier/slack-notifier</a></li>



<li>Tạo 1 module <strong>SlackNotifier</strong> để tiện cho việc gọi khi cần.
<ul class="wp-block-list">
<li><strong>ENV[&#8216;SLACK_WEBHOOK_URL&#8217;], ENV[&#8216;SLACK_CHANNEL_SLACK&#8217;]</strong> bạn lấy ở bên trên.</li>



<li><strong>ENV[&#8216;SLACK_USERNAME_SLACK&#8217;]</strong> bạn đặt tự do.</li>
</ul>
</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:17.3125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="#config/initializers create slack_notifier.rb

module SlackNotifier
  CLIENT = Slack::Notifier.new(
    ENV['SLACK_WEBHOOK_URL'],
    channel: ENV['SLACK_CHANNEL_SLACK'],
    username: ENV['SLACK_USERNAME_SLACK']
  )
end

SlackNotifier::CLIENT.ping &quot;Hello Everybody!&quot;
SlackNotifier::CLIENT.ping &quot;TEST SLACK NOTIFICATION&quot;
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">#config/initializers create slack_notifier.rb</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">module</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">SlackNotifier</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #BF9EEE">CLIENT</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Slack</span><span style="color: #F286C4">::</span><span style="color: #97E1F1; font-style: italic">Notifier</span><span style="color: #F6F6F4">.</span><span style="color: #F286C4">new</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #BF9EEE">ENV</span><span style="color: #F6F6F4">[</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">SLACK_WEBHOOK_URL</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">],</span></span>
<span class="line"><span style="color: #F6F6F4">    channel</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">ENV</span><span style="color: #F6F6F4">[</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">SLACK_CHANNEL_SLACK</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">],</span></span>
<span class="line"><span style="color: #F6F6F4">    username</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">ENV</span><span style="color: #F6F6F4">[</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">SLACK_USERNAME_SLACK</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">]</span></span>
<span class="line"><span style="color: #F6F6F4">  )</span></span>
<span class="line"><span style="color: #F286C4">end</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">SlackNotifier</span><span style="color: #F286C4">::</span><span style="color: #97E1F1; font-style: italic">CLIENT</span><span style="color: #F6F6F4">.ping </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">Hello Everybody!</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">SlackNotifier</span><span style="color: #F286C4">::</span><span style="color: #97E1F1; font-style: italic">CLIENT</span><span style="color: #F6F6F4">.ping </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">TEST SLACK NOTIFICATION</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li>Kết quả</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="576" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-1024x576.png" alt="" class="wp-image-2774" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-1024x576.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-300x169.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-768x432.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-1536x864.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-2048x1152.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-380x214.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-800x450.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40-1160x653.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-20.34.40.png 2072w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Ngoài ra, bạn có thể custom nhiều hơn nữa,<strong> <em>xem thêm</em></strong> <strong><em>tại</em></strong> <a href="https://github.com/slack-notifier/slack-notifier">https://github.com/slack-notifier/slack-notifier</a></li>
</ul>



<h5 id="2-su-dung-exception-notification" class="wp-block-heading">2. Sử dụng exception-notification</h5>



<ul class="wp-block-list">
<li>Gem này sẽ tự động gửi exception về slack của bạn khi Rails App xảy exception. Nó giúp việc giám sát hệ thống, giúp bạn nhanh chóng kiểm tra, sửa chữa kịp thời.</li>



<li><code>gem exception_notification</code><a href=" https://github.com/smartinez87/exception_notification"> </a><a href="https://github.com/smartinez87/exception_notification">https://github.com/smartinez87/exception_notification</a></li>



<li><strong>Nếu ở production</strong>: <em>config/environments/production.rb</em></li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.65625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="#config/environments/development.rb

Rails.application.config.middleware.use ExceptionNotification::Rack, slack: {
  webhook_url: ENV['SLACK_WEBHOOK_URL'],
  channel: ENV['SLACK_CHANNEL_NAME']
	}

" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">#config/environments/development.rb</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">Rails</span><span style="color: #F6F6F4">.application.config.middleware.use </span><span style="color: #97E1F1; font-style: italic">ExceptionNotification</span><span style="color: #F286C4">::</span><span style="color: #BF9EEE">Rack</span><span style="color: #F6F6F4">, slack</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  webhook_url</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">ENV</span><span style="color: #F6F6F4">[</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">SLACK_WEBHOOK_URL</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">],</span></span>
<span class="line"><span style="color: #F6F6F4">  channel</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">ENV</span><span style="color: #F6F6F4">[</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">SLACK_CHANNEL_NAME</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">]</span></span>
<span class="line"><span style="color: #F6F6F4">	}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li>Đặt ở nơi bạn muốn push notify nhé.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:17.3125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Ruby</span><span role="button" tabindex="0" data-code="rescue_from StandardError do |e|
  ExceptionNotifier.notify_exception(e, 
		data: {
			# custom message ... 
			messages: e.messages
			date: Date.current
		}
	)
end
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">rescue_from </span><span style="color: #BF9EEE">StandardError</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">do</span><span style="color: #F6F6F4"> |e|</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E1F1; font-style: italic">ExceptionNotifier</span><span style="color: #F6F6F4">.notify_exception(e, </span></span>
<span class="line"><span style="color: #F6F6F4">		data</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">			</span><span style="color: #7B7F8B"># custom message ... </span></span>
<span class="line"><span style="color: #F6F6F4">			messages</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> e.messages</span></span>
<span class="line"><span style="color: #F6F6F4">			date</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Date</span><span style="color: #F6F6F4">.current</span></span>
<span class="line"><span style="color: #F6F6F4">		}</span></span>
<span class="line"><span style="color: #F6F6F4">	)</span></span>
<span class="line"><span style="color: #F286C4">end</span></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li>Kết quả</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="732" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-1024x732.png" alt="" class="wp-image-2775" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-1024x732.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-300x215.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-768x549.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-1536x1098.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-2048x1465.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-380x272.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-800x572.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23-1160x830.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-20-at-22.55.23.png 2078w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Ngoài ra, bạn có thể custom nhiều hơn nữa, <strong>x<em>em thêm tại</em></strong> <a href="https://github.com/smartinez87/exception_notification">https://github.com/smartinez87/exception_notification</a></li>
</ul>



<p class="wp-block-paragraph">Hi vọng bài viết sẽ giúp ích được mọi người.</p>



<p class="wp-block-paragraph">Cảm ơn mọi người.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://blog.tomosia.com.vn/setup-slack-notifications-in-rails/">Setup slack notifications in Rails</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/setup-slack-notifications-in-rails/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>What&#8217;s new in Ruby 3.3.0</title>
		<link>https://blog.tomosia.com.vn/ruby-3-3-0/</link>
					<comments>https://blog.tomosia.com.vn/ruby-3-3-0/#comments</comments>
		
		<dc:creator><![CDATA[Dang Van Luan]]></dc:creator>
		<pubDate>Tue, 26 Dec 2023 01:57:49 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2726</guid>

					<description><![CDATA[<p>Là một developer có niềm đam mê sâu sắc với Ruby thì chúng ta đều biết vào Giáng&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/ruby-3-3-0/">What&#8217;s new in Ruby 3.3.0</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Là một developer có niềm đam mê sâu sắc với Ruby thì chúng ta đều biết vào Giáng sinh hằng năm, Ruby core team sẽ release version Ruby mới. Và năm nay cũng không ngoại lệ, chúng ta sẽ đón chờ phiên bản Ruby 3.3 vào tuần tới.</p>



<p class="wp-block-paragraph">Mặc dù không có nhiều thay đổi lớn về các tính năng ngôn ngữ như các phiên bản trước, nhưng trong phiên bản này, trọng tâm chính là hiệu suất và trải nghiệm phát triển. Dưới đây là một số điểm nổi bật của phiên bản này:</p>



<h3 id="yjit" class="wp-block-heading">YJIT</h3>



<p class="wp-block-paragraph">YJIT, trình biên dịch JIT của Ruby, đã có những cải tiến đáng kể trong vài năm qua. Trong năm nay, nó tiếp tục trở nên <a href="https://railsatscale.com/2023-12-04-ruby-3-3-s-yjit-faster-while-using-less-memory/">nhanh hơn, đồng thời tiêu thụ ít bộ nhớ hơn.</a></p>



<p class="wp-block-paragraph">Các công ty như <a href="https://dev.37signals.com/yjit-is-fast/">Basecamp</a> và <a href="https://railsatscale.com/2023-09-18-ruby-3-3-s-yjit-runs-shopify-s-production-code-15-faster/">Shopify</a> đã sử dụng phiên bản xem trước của 3.3 trong môi trường production và đã thấy sự cải thiện khoảng 15% trong thời gian phản hồi trung bình so với phiên bản 3.3.0 không có YJIT. <br>Thực tế, YJIT đã rất hiệu quả trong việc tăng tốc ứng dụng Rails và nó sẽ được kích hoạt mặc định trên ứng dụng Rails mới nếu sử dụng Ruby 3.3.</p>



<p class="wp-block-paragraph">Một phương thức mới, RubyVM::YJIT.enable đã được giới thiệu, cho phép bạn kích hoạt YJIT khi đang chạy. Điều này có thể hữu ích nếu bạn muốn tải ứng dụng nhanh chóng và sau đó kích hoạt YJIT sau khi nó đã khởi động. Đây chính là cách Rails sử dụng để kích hoạt JIT trong một bộ khởi tạo.</p>



<h3 id="rjit" class="wp-block-heading">RJIT</h3>



<p class="wp-block-paragraph">Một trình biên dịch JIT thử nghiệm có tên là RJIT đã được giới thiệu. Được viết bằng Ruby thuần, nó thay thế MJIT, một JIT thay thế khác đã có sẵn trong Ruby 3.2. RJIT sử dụng nhiều bộ nhớ hơn so với YJIT và chỉ tồn tại cho mục đích thử nghiệm. Trong môi trường sản xuất, vẫn nên sử dụng YJIT.</p>



<h3 id="irb" class="wp-block-heading">IRB</h3>



<p class="wp-block-paragraph">Từ khi biết đến Ruby, pry là công cụ gỡ lỗi ưa thích của mình, mình đã chuyển sang sử dụng IRB gần đây, sau khi biết tới <a href="http://blog.tomosia.com.vn/debug-trong-ruby-ma-khong-can-dung-thu-vien/">bài viết này</a>. Trong phiên bản này, IRB và integration của nó với gem debug đã được cải thiện đến mức pry và byebug không còn cần thiết cho việc gỡ lỗi mã nguồn của bạn.</p>



<h3 id="range" class="wp-block-heading">Range</h3>



<h4 id="rails-rangeoverlapsrange" class="wp-block-heading"><em>Rails Range#overlaps?(range)</em></h4>



<p class="wp-block-paragraph">So sánh hai phạm vi và xem liệu chúng có trùng lặp với nhau không. <code>overlap?</code> trả về <code>true</code> nếu hai phạm vi trùng nhau nếu không thì trả về <code>false</code>.</p>



<pre class="wp-block-code"><code>(3..8).overlaps?(8..10) # => true
(2..43).overlaps?(3..5) # => true
(1..5).overlaps?(7..9) # => false

('a'..'z').overlaps?('c'..'d') # => true

(1.day.from_now..5.days.from_now).overlaps?(5.days.from_now..10.days.from_now) # => false
(1.day.from_now.to_date..5.days.from_now.to_date).overlaps?(5.days.from_now.to_date..10.days.from_now.to_date) # => true

(3...8).overlaps?(8...9) # => false
(3...8).overlaps?(7...9) # => true

# works similarly for open ranges.
(3..).overlaps?(...3) # => false</code></pre>



<h4 id="ruby-3-3" class="wp-block-heading"><em>Ruby &lt; 3.3</em></h4>



<p class="wp-block-paragraph">Không giống như Rails, Ruby không có phương thức nào để so sánh hai phạm vi và kiểm tra chúng có trùng nhau hay không.</p>



<p class="wp-block-paragraph">Trước Ruby 3.3, nếu chúng ta muốn kiểm tra sự trùng lặp, chúng ta cần sử dụng phương thức <code>cover</code>, nó sẽ trả về true nếu hai phạm vi trùng nhau.</p>



<pre class="wp-block-code"><code><em># Check if range1 covers any value in range2 or vice versa</em>
range1 = (3..8)
range2 = (8..10)
range1.cover?(range2.first) || range2.cover?(range1.first) <em># =&gt; true</em>

range1 = ('a'..'z')
range2 = ('c'..'d')
range1.cover?(range2.first) || range2.cover?(range1.first) <em># =&gt; true</em>

range1 = (1.day.from_now..5.days.from_now)
range2 = (5.days.from_now..10.days.from_now)
range1.cover?(range2.first) || range2.cover?(range1.first) <em># =&gt; false</em>

range1 = (1.day.from_now.to_date..5.days.from_now.to_date)
range2 = (5.days.from_now.to_date..10.days.from_now.to_date)
range1.cover?(range2.first) || range2.cover?(range1.first) <em># =&gt; true</em></code></pre>



<h4 id="ruby-3-3-2" class="wp-block-heading"><em>Ruby 3.3</em></h4>



<p class="wp-block-paragraph">Ruby 3.3 giới thiệu phương thức <code>Range#overlap?(range)</code>. Giống như Rails, chúng ta có thể sử dụng <code>overlap?</code> để so sánh hai phạm vi và xem chúng có trùng lặp với nhau không.</p>



<ul class="wp-block-list">
<li>Nó trả về true nếu hai phạm vi trùng nhau nếu không thì trả về sai.</li>



<li>Nó sẽ raise TypeError nếu argument không phải kiểu range</li>
</ul>



<pre class="wp-block-code"><code>range1 = (3..8)
range2 = (8..10)
range1.overlap?(range2) # => true

range1 = ('a'..'z')
range2 = ('c'..'d')
range1.overlap?(range2) # => true

range1 = (1.day.from_now..5.days.from_now)
range2 = (5.days.from_now..10.days.from_now)
range1.overlap?(range2) # => false

range1 = (1.day.from_now.to_date..5.days.from_now.to_date)
range2 = (5.days.from_now.to_date..10.days.from_now.to_date)
range1.overlap?(range2) # => true

(3..).overlap?(...3) # => false

(3..8).overlap?(3) # => TypeError, argument must be Range</code></pre>



<h3 id="tham-khao" class="wp-block-heading"><br>Tham khảo</h3>



<p class="wp-block-paragraph"><br>https://www.ruby-lang.org/en/news/2023/12/11/ruby-3-3-0-rc1-released/<br>https://railsatscale.com/2023-09-18-ruby-3-3-s-yjit-runs-shopify-s-production-code-15-faster/<br>https://railsatscale.com/2023-12-04-ruby-3-3-s-yjit-faster-while-using-less-memory/<br><br></p>
<p>The post <a href="https://blog.tomosia.com.vn/ruby-3-3-0/">What&#8217;s new in Ruby 3.3.0</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/ruby-3-3-0/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>Hiểu thêm về JSON trong Ruby</title>
		<link>https://blog.tomosia.com.vn/hieu-them-ve-json-trong-ruby/</link>
					<comments>https://blog.tomosia.com.vn/hieu-them-ve-json-trong-ruby/#comments</comments>
		
		<dc:creator><![CDATA[Thâm Davies]]></dc:creator>
		<pubDate>Mon, 11 Dec 2023 09:28:45 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[json in ruby]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2531</guid>

					<description><![CDATA[<p>Trong bài này mình muốn chia sẻ một vài thứ hay ho trong method JSON.parse trong Ruby. Let’s&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/hieu-them-ve-json-trong-ruby/">Hiểu thêm về JSON trong Ruby</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Trong bài này mình muốn chia sẻ một vài thứ hay ho trong method <code>JSON.parse</code> trong Ruby. Let’s jump in 👇</p>



<p class="wp-block-paragraph">Trong Ruby, phương thức <code>JSON.parse</code> dùng để parse chuỗi JSON và chuyển đổi nó thành cấu trúc dữ liệu của Ruby, thường là hash hay array. Chúng ta cũng có thể truyền hai tham số là: <code>object_class</code> và <code>symbolize_names</code>.</p>



<ul class="wp-block-list">
<li>object_class: cho phép bạn chỉ định lớp sẽ được sử dụng để khởi tạo đối tượng cho JSON.</li>



<li>symbolize_names: nếu bạn truyền tham số <code>symbolize_names</code> với giá trị là true thì key của object sau khi parse là symbol chứ không phải string như mặc định</li>
</ul>



<figure class="wp-block-image size-large"><img decoding="async" src="https://i.imgur.com/4hv0Gym.png" alt=""/></figure>



<p class="wp-block-paragraph">Link code: <a href="https://gist.github.com/thamdavies/2780c45d214546caf8b1d83fb6c70e53">Gist</a></p>



<p class="wp-block-paragraph">Bài viết gốc: <a href="https://www.thamdavies.com/blog/hieu-them-ve-json-trong-ruby">thamdavies.com</a></p>



<p class="wp-block-paragraph">That’s it. Happy coding 💯</p>
<p>The post <a href="https://blog.tomosia.com.vn/hieu-them-ve-json-trong-ruby/">Hiểu thêm về JSON trong Ruby</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/hieu-them-ve-json-trong-ruby/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title>Ruby 3.0 đã chính thức hỗ trợ Pattern Matching</title>
		<link>https://blog.tomosia.com.vn/ruby-3-0-da-chinh-thuc-ho-tro-pattern-matching/</link>
					<comments>https://blog.tomosia.com.vn/ruby-3-0-da-chinh-thuc-ho-tro-pattern-matching/#comments</comments>
		
		<dc:creator><![CDATA[Minh Tang]]></dc:creator>
		<pubDate>Mon, 11 Dec 2023 02:25:03 +0000</pubDate>
				<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2508</guid>

					<description><![CDATA[<p>Pattern Matching trong Ruby cho phép giải cấu trúc dữ liệu một cách ngắn gọn, giúp dễ dàng&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/ruby-3-0-da-chinh-thuc-ho-tro-pattern-matching/">Ruby 3.0 đã chính thức hỗ trợ Pattern Matching</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Pattern Matching trong Ruby cho phép giải cấu trúc dữ liệu một cách ngắn gọn, giúp dễ dàng gán giá trị cho các biến với cú pháp rõ ràng. Pattern matching là một tính năng đã được giới thiệu trong Ruby 2.7. Từ Ruby 3.0 trở đi, nó không còn là một tính năng thử nghiệm nữa và chúng ta có thể bắt đầu sử dụng nó một cách chính thức.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Warning Ruby 2.7: Pattern matching is experimental, and the behavior may change in future versions of Ruby!</p>
</blockquote>



<h2 id="pattern-matching-la-gi" class="wp-block-heading">Pattern Matching là gì?</h2>



<p class="wp-block-paragraph">Pattern matching là một tính năng cho phép bạn so sánh và hiểu cấu trúc dữ liệu. Điều này được thực hiện bằng cách kiểm tra cách thông tin được tổ chức và gán các phần tương ứng cho các biến cục bộ để sử dụng sau này.</p>



<p class="wp-block-paragraph">Pattern Matching được hỗ trợ thông qua cú pháp <strong>case / in</strong>. Quan trọng là không nhầm lẫn với <strong>case / when</strong> và không kết hợp chúng. Nếu không có sự phù hợp với bất kỳ biểu thức nào và không có định nghĩa <code>else</code>, thì một ngoại lệ <code>NoMatchingPatternError</code> sẽ được raise</p>



<pre class="wp-block-code"><code>case &lt;expression&gt;
in &lt;pattern1&gt;
  # ...
in &lt;pattern2&gt;
  # ...
else
  # ...
end
</code></pre>



<p class="wp-block-paragraph">Pattern có thể là:</p>



<ul class="wp-block-list">
<li><strong>Value</strong>: Any Ruby object (compared with the === operator, as in ‘when’).</li>



<li><strong>Array</strong>: Array pattern:&nbsp;<strong><code>[&lt;subpattern&gt;, &lt;subpattern&gt;, &lt;subpattern&gt;, &lt;subpattern&gt;, ...]</code></strong>.</li>



<li><strong>Find</strong>: Search pattern:&nbsp;<strong><code>[*variable, &lt;subpattern&gt;, &lt;subpattern&gt;, &lt;subpattern&gt;, &lt;subpattern&gt;, ..., *variable]</code></strong>.</li>



<li><strong>Hash</strong>: Hash pattern:&nbsp;<strong><code>{key: &lt;subpattern&gt;, key: &lt;subpattern&gt;, ...}</code></strong>.</li>



<li><strong>Alternative</strong>: Pattern combination with&nbsp;<strong><code>|</code></strong>&nbsp;(vertical bar).</li>



<li><strong>Variable capture</strong>:&nbsp;<strong><code>&lt;pattern&gt; =&gt; variable</code></strong>&nbsp;or&nbsp;<strong><code>variable</code></strong>.</li>
</ul>



<h2 id="dinh-nghia-pattern-matching" class="wp-block-heading">Định nghĩa Pattern Matching</h2>



<pre class="wp-block-code"><code># Define a method that uses pattern matching with case/in
def process_data(data)
  case data
  in { type: "number", value: Integer =&gt; num }
    puts "Received a number: #{num}"
  in { type: "string", value: String =&gt; str }
    puts "Received a string: #{str}"
  in { type: "array", value: Array =&gt; arr }
    puts "Received an array: #{arr}"
  in { type: "hash", value: Hash =&gt; hash }
    puts "Received a hash: #{hash}"
  else
    puts "Received something else."
  end
end

# Test the method with different data structures
process_data({ type: "number", value: 42 })               # Output: Received a number: 42
process_data({ type: "string", value: "Hello, Ruby!" })   # Output: Received a string: Hello, Ruby!
process_data({ type: "array", value: &#91;1, 2, 3] })         # Output: Received an array: &#91;1, 2, 3]
process_data({ type: "hash", value: { key: "value" } })   # Output: Received a hash: {:key=&gt;"value"}
process_data({ type: "unknown", value: "unknown data" })  # Output: Received something else.
</code></pre>



<h3 id="deconstruct-va-deconstruct_keys" class="wp-block-heading">Deconstruct và Deconstruct_keys</h3>



<p class="wp-block-paragraph">Đây là hai phương thức đặc biệt trong pattern matching: <code>deconstruct</code>, được gọi khi đánh giá trên một <strong>Array</strong>, và <code>deconstruct_keys</code> được gọi khi đánh giá trên một <strong>Hash</strong>. Hãy xem một ví dụ:</p>



<pre class="wp-block-code"><code>class Coordinates
  attr_accessor :x, :y

  def initialize(x, y)
    @x = x
    @y = y
  end

  def deconstruct
    &#91;@x, @y]
  end

  def deconstruct_key
    {x: @x, y: @y}
  end
end
</code></pre>



<p class="wp-block-paragraph">Khi một thể hiện của lớp Coordinate được đánh giá trên một Array</p>



<pre class="wp-block-code"><code>c = Coordinates.new(32,50)

case c
in &#91;a,b]
  p a #=&gt; 32
  p b #=&gt; 50
end
</code></pre>



<p class="wp-block-paragraph">Khi một thể hiện của lớp Coordinate được đánh giá trên một Hash</p>



<pre class="wp-block-code"><code>case c
in {x:, y:}
  p x #=&gt; 32
  p y #=&gt; 50
end
</code></pre>



<p class="wp-block-paragraph">Nếu class không có 2 methods trên thì sẽ báo lỗi</p>



<pre class="wp-block-code"><code>#&lt;Coordinate:0x000000010d27fb10 @x=32, @y=50&gt;: #&lt;Coordinate:0x000000010d27fb10 @x=32, @y=50&gt; does not respond to #deconstruct (NoMatchingPatternError)
</code></pre>



<p class="wp-block-paragraph">Tham khảo thêm tại: <a href="https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html">https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html</a></p>
<p>The post <a href="https://blog.tomosia.com.vn/ruby-3-0-da-chinh-thuc-ho-tro-pattern-matching/">Ruby 3.0 đã chính thức hỗ trợ Pattern Matching</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/ruby-3-0-da-chinh-thuc-ho-tro-pattern-matching/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
	</channel>
</rss>
