Reading Time: 16 minutes

Docker đã trở thành một công cụ không thể thiếu, cho phép các nhà phát triển đóng gói và chạy ứng dụng dưới dạng containers. Một container có thể hiểu là một tiến trình được cô lập và hoạt động trên một hệ điều hành chia sẻ, mang lại một giải pháp nhẹ hơn đáng kể so với máy ảo (virtual machines) truyền thống.

Mặc dù khái niệm container không phải là mới, chúng mang lại những lợi ích vượt trội như cách ly tiến trình (process isolation) và chuẩn hóa môi trường (environment standardization). Đây là những yếu tố ngày càng quan trọng khi nhiều nhà phát triển chuyển sang sử dụng các kiến trúc ứng dụng phân tán (distributed application architectures).

Khi xây dựng và mở rộng một ứng dụng với Docker, điểm khởi đầu quen thuộc chính là việc tạo ra một image cho ứng dụng của bạn. Image này bao gồm mã nguồn ứng dụng, các thư viện, tệp cấu hình, biến môi trường và runtime cần thiết. Việc sử dụng image đảm bảo rằng môi trường bên trong container của bạn được chuẩn hóa và chỉ chứa những thành phần thiết yếu để xây dựng và chạy ứng dụng.

Trong bài blog này, chúng ta sẽ cùng nhau thực hành việc tạo một image ứng dụng cho một trang web tĩnh đơn giản sử dụng framework Express và Bootstrap. Sau đó, ta sẽ xây dựng một container từ image đó và đẩy (push) nó lên Docker Hub để lưu trữ và sử dụng sau này. Cuối cùng, chúng ta sẽ kéo (pull) image đã lưu trữ từ repository Docker Hub về và xây dựng lại một container khác, minh họa cách bạn có thể dễ dàng tái tạo và mở rộng ứng dụng của mình.

Chuẩn bị:

Nếu bạn đang sử dụng Ubuntu phiên bản 16.04 trở xuống, chúng tôi khuyên bạn nên nâng cấp lên phiên bản mới nhất vì Ubuntu không còn hỗ trợ các phiên bản này nữa.

Để làm theo hướng dẫn này, bạn sẽ cần:

  • Một máy chủ chạy Ubuntu, cùng với người dùng không phải root có quyền sudo và một tường lửa đang hoạt động.
  • Cài đặt Docker.
  • Cài đặt Node.jsnpm.
  • Một tài khoản Docker Hub.

Các bước xây dựng ứng dụng NodeJS với Docker

  • Cài đặt các Dependencies của ứng dụng
  • Tạo các tệp ứng dụng NodeJS
  • Viết Dockerfile
  • Tạo Image Repository trên DockerHub

Bước 1: Cài đặt các Dependencies của ứng dụng của bạn

Để tạo image của bạn, trước tiên bạn sẽ cần tạo các tệp ứng dụng của mình, sau đó bạn có thể sao chép chúng vào container. Các tệp này sẽ bao gồm nội dung tĩnh, mã nguồn và các dependencies của ứng dụng của bạn.

Đầu tiên, hãy tạo một thư mục cho dự án của bạn trong thư mục chính của người dùng không phải root. Chúng ta sẽ gọi nó là node_project, nhưng bạn hoàn toàn có thể thay thế bằng một tên khác:

mkdir node_project

Điều hướng đến thư mục này:

cd node_project

Đây sẽ là thư mục gốc của dự án.

Tiếp theo, tạo một tệp package.json với các dependencies của dự án và các thông tin nhận dạng khác. Mở tệp bằng nano hoặc trình soạn thảo yêu thích của bạn:

nano package.json

Thêm thông tin sau về dự án, bao gồm tên, tác giả, giấy phép, điểm vào (entrypoint) và các dependencies. Đảm bảo thay thế thông tin tác giả bằng tên và chi tiết liên hệ của riêng bạn:

{
  "name": "nodejs-image-demo",
  "version": "1.0.0",
  "description": "nodejs image demo",
  "author": "Sammy the Shark <sammy@example.com>",
  "license": "MIT",
  "main": "app.js",
  "keywords": [
    "nodejs",
    "bootstrap",
    "express"
  ],
  "dependencies": {
    "express": "^4.16.4"
  }
}

Tệp này bao gồm tên dự án, tác giả và giấy phép mà nó được chia sẻ. npm khuyến nghị đặt tên dự án của bạn ngắn gọn và mô tả, đồng thời tránh trùng lặp trong registry của npm.

Ngoài ra, tệp còn chỉ định:

  • "main": Điểm vào (entrypoint) cho ứng dụng, app.js. Bạn sẽ tạo tệp này tiếp theo.
  • "dependencies": Các phụ thuộc của dự án: trong trường hợp này, Express 4.16.4 trở lên.

Lưu và đóng tệp khi bạn đã hoàn tất việc thay đổi.

Để cài đặt các dependencies của dự án, hãy chạy lệnh sau:

npm install

Điều này sẽ cài đặt các gói mà bạn đã liệt kê trong tệp package.json của mình vào thư mục dự án.

Giờ đây chúng ta có thể chuyển sang xây dựng các tệp ứng dụng.

Bước 2: Tạo các tệp ứng dụng

Chúng ta sẽ tạo một trang web cung cấp thông tin về cá mập cho người dùng. Ứng dụng của chúng ta sẽ có một điểm vào chính là app.js, và một thư mục views sẽ chứa các tài nguyên tĩnh của dự án. Trang đích (landing page) index.html sẽ cung cấp cho người dùng một số thông tin sơ bộ và một liên kết đến trang chứa thông tin chi tiết hơn về cá mập, sharks.html. Trong thư mục views, chúng ta sẽ tạo cả trang đích và sharks.html.

Đầu tiên, mở app.js trong thư mục chính của dự án để định nghĩa các tuyến của dự án:

nano app.js

Phần đầu tiên của tệp sẽ tạo các đối tượng Express application và Router, đồng thời định nghĩa thư mục gốc và cổng dưới dạng hằng số:

const express = require('express');
const app = express();
const router = express.Router();

const path = __dirname + '/views/';
const port = 8080;

Hàm require tải mô-đun express, sau đó chúng ta sử dụng nó để tạo các đối tượng approuter. Đối tượng router sẽ thực hiện chức năng định tuyến của ứng dụng, và khi chúng ta định nghĩa các tuyến HTTP, chúng ta sẽ thêm chúng vào đối tượng này để định nghĩa cách ứng dụng của chúng ta sẽ xử lý các yêu cầu.

Phần này của tệp cũng đặt một vài hằng số, pathport:

  • path: Định nghĩa thư mục gốc, sẽ là thư mục con views trong thư mục dự án hiện tại.
  • port: Yêu cầu ứng dụng lắng nghe và liên kết với cổng 8080.

Tiếp theo, đặt các tuyến cho ứng dụng bằng cách sử dụng đối tượng router:

...

router.use(function (req,res,next) {
  console.log('/' + req.method);
  next();
});

router.get('/', function(req,res){
  res.sendFile(path + 'index.html');
});

router.get('/sharks', function(req,res){
  res.sendFile(path + 'sharks.html');
});

Hàm router.use tải một hàm middleware sẽ ghi lại các yêu cầu của router và chuyển chúng đến các tuyến của ứng dụng. Các tuyến này được định nghĩa trong các hàm tiếp theo, chỉ định rằng một yêu cầu GET đến URL gốc của dự án sẽ trả về trang index.html, trong khi một yêu cầu GET đến tuyến /sharks sẽ trả về sharks.html.

Cuối cùng, gắn middleware của router và các tài nguyên tĩnh của ứng dụng, đồng thời yêu cầu ứng dụng lắng nghe trên cổng 8080:

...

app.use(express.static(path));
app.use('/', router);

app.listen(port, function () {
  console.log('Example app listening on port 8080!')
})

Tệp app.js hoàn chỉnh sẽ trông như thế này:

const express = require('express');
const app = express();
const router = express.Router();

const path = __dirname + '/views/';
const port = 8080;

router.use(function (req,res,next) {
  console.log('/' + req.method);
  next();
});

router.get('/', function(req,res){
  res.sendFile(path + 'index.html');
});

router.get('/sharks', function(req,res){
  res.sendFile(path + 'sharks.html');
});

app.use(express.static(path));
app.use('/', router);

app.listen(port, function () {
  console.log('Example app listening on port 8080!')
})

Lưu và đóng tệp khi bạn đã hoàn tất.

Tiếp theo, hãy thêm một số nội dung tĩnh vào ứng dụng. Bắt đầu bằng cách tạo thư mục views:

mkdir views

Mở tệp trang chủ, index.html:

nano views/index.html

Thêm mã sau vào tệp, mã này sẽ import Bootstrap và tạo một thành phần jumbotron với một liên kết đến trang thông tin sharks.html chi tiết hơn:

<!DOCTYPE html>
<html lang="en">

<head>
    <title>About Sharks</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="<https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css>" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <link href="css/styles.css" rel="stylesheet">
    <link href="<https://fonts.googleapis.com/css?family=Merriweather:400,700>" rel="stylesheet" type="text/css">
</head>

<body>
    <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
        <div class="container">
            <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
            </button> <a class="navbar-brand" href="#">Everything Sharks</a>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav mr-auto">
                    <li class="active nav-item"><a href="/" class="nav-link">Home</a>
                    </li>
                    <li class="nav-item"><a href="/sharks" class="nav-link">Sharks</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="jumbotron">
        <div class="container">
            <h1>Want to Learn About Sharks?</h1>
            <p>Are you ready to learn about sharks?</p>
            <br>
            <p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a>
            </p>
        </div>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-lg-6">
                <h3>Not all sharks are alike</h3>
                <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.
                </p>
            </div>
            <div class="col-lg-6">
                <h3>Sharks are ancient</h3>
                <p>There is evidence to suggest that sharks lived up to 400 million years ago.
                </p>
            </div>
        </div>
    </div>
</body>

</html>

Thanh điều hướng (navbar) cấp cao nhất ở đây cho phép người dùng chuyển đổi giữa các trang HomeSharks. Trong thành phần con navbar-nav, chúng ta đang sử dụng lớp active của Bootstrap để chỉ ra trang hiện tại cho người dùng. Chúng ta cũng đã chỉ định các tuyến đến các trang tĩnh của mình, khớp với các tuyến chúng ta đã định nghĩa trong app.js:

...
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
   <ul class="nav navbar-nav mr-auto">
      <li class="active nav-item"><a href="/" class="nav-link">Home</a>
      </li>
      <li class="nav-item"><a href="/sharks" class="nav-link">Sharks</a>
      </li>
   </ul>
</div>
...

Ngoài ra, chúng ta đã tạo một liên kết đến trang thông tin cá mập của mình trong nút của jumbotron:

...
<div class="jumbotron">
   <div class="container">
      <h1>Want to Learn About Sharks?</h1>
      <p>Are you ready to learn about sharks?</p>
      <br>
      <p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a>
      </p>
   </div>
</div>
...

Cũng có một liên kết đến một stylesheet tùy chỉnh trong phần tiêu đề:

...
<link href="css/styles.css" rel="stylesheet">
...

Chúng ta sẽ tạo stylesheet này ở cuối bước này. Lưu và đóng tệp khi bạn đã hoàn tất.

Sau khi đã có trang đích của ứng dụng, chúng ta có thể tạo trang thông tin cá mập, sharks.html, trang này sẽ cung cấp cho người dùng quan tâm thêm thông tin về cá mập. Mở tệp:

nano views/sharks.html

Thêm mã sau, mã này sẽ import Bootstrap và stylesheet tùy chỉnh và cung cấp cho người dùng thông tin chi tiết về một số loài cá mập:

<!DOCTYPE html>
<html lang="en">

<head>
    <title>About Sharks</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="<https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css>" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <link href="css/styles.css" rel="stylesheet">
    <link href="<https://fonts.googleapis.com/css?family=Merriweather:400,700>" rel="stylesheet" type="text/css">
</head>
<nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
    <div class="container">
        <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
        </button> <a class="navbar-brand" href="/">Everything Sharks</a>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav mr-auto">
                <li class="nav-item"><a href="/" class="nav-link">Home</a>
                </li>
                <li class="active nav-item"><a href="/sharks" class="nav-link">Sharks</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<div class="jumbotron text-center">
    <h1>Shark Info</h1>
</div>
<div class="container">
    <div class="row">
        <div class="col-lg-6">
            <p>
                <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
                </div>
                <img src="<https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg>" alt="Sawshark">
            </p>
        </div>
        <div class="col-lg-6">
            <p>
                <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                <img src="<https://assets.digitalocean.com/articles/docker_node_image/sammy.png>" alt="Sammy the Shark">
            </p>
        </div>
    </div>
</div>

</html>

Lưu ý rằng trong tệp này, chúng ta lại sử dụng lớp active để chỉ ra trang hiện tại. Lưu và đóng tệp khi bạn đã hoàn tất.

Cuối cùng, tạo stylesheet CSS tùy chỉnh mà bạn đã liên kết trong index.htmlsharks.html bằng cách tạo một thư mục css trong thư mục views trước:

mkdir views/css

Mở stylesheet:

nano views/css/styles.css

Thêm mã sau, mã này sẽ đặt màu và phông chữ mong muốn cho các trang của chúng ta:

.navbar {
    margin-bottom: 0;
}

body {
    background: #020A1B;
    color: #ffffff;
    font-family: 'Merriweather', sans-serif;
}

h1,
h2 {
    font-weight: bold;
}

p {
    font-size: 16px;
    color: #ffffff;
}

.jumbotron {
    background: #0048CD;
    color: white;
    text-align: center;
}

.jumbotron p {
    color: white;
    font-size: 26px;
}

.btn-primary {
    color: #fff;
    text-color: #000000;
    border-color: white;
    margin-bottom: 5px;
}

img,
video,
audio {
    margin-top: 20px;
    max-width: 80%;
}

div.caption: {
    float: left;
    clear: both;
}

Ngoài việc đặt phông chữ và màu sắc, tệp này còn giới hạn kích thước của hình ảnh bằng cách chỉ định max-width là 80%. Điều này sẽ ngăn chúng chiếm nhiều không gian hơn mức chúng ta muốn trên trang. Lưu và đóng tệp khi bạn đã hoàn tất.

Sau khi đã có các tệp ứng dụng và cài đặt xong các dependencies của dự án, bạn đã sẵn sàng để khởi động ứng dụng. Nếu bạn đã làm theo hướng dẫn thiết lập máy chủ ban đầu trong phần điều kiện tiên quyết, bạn sẽ có một tường lửa đang hoạt động chỉ cho phép lưu lượng SSH. Để cho phép lưu lượng truy cập đến cổng 8080, hãy chạy:

sudo ufw allow 8080

Để khởi động ứng dụng, hãy đảm bảo rằng bạn đang ở trong thư mục gốc của dự án:

cd ~/node_project

Khởi động ứng dụng với node app.js:

node app.js

Điều hướng trình duyệt của bạn đến http://your_server_ip:8080. Bạn sẽ tải trang chủ sau:

Nhấp vào nút Get Shark Info. Trang thông tin sau sẽ tải:

Giờ đây bạn đã có một ứng dụng đang hoạt động. Khi bạn đã sẵn sàng, thoát máy chủ bằng cách gõ CTRL+C. Giờ đây chúng ta có thể chuyển sang tạo Dockerfile sẽ cho phép chúng ta tái tạo và mở rộng ứng dụng này theo ý muốn.

Bước 3: Viết Dockerfile

Dockerfile của bạn chỉ định những gì sẽ được bao gồm trong container ứng dụng của bạn khi nó được thực thi. Sử dụng một Dockerfile cho phép bạn định nghĩa môi trường container và tránh sự khác biệt về các dependencies hoặc phiên bản runtime.

Chúng ta sẽ làm cho image của mình hiệu quả nhất có thể bằng cách giảm thiểu số lượng các lớp image và giới hạn chức năng của image vào một mục đích duy nhất: tái tạo các tệp ứng dụng và nội dung tĩnh của chúng ta.

Trong thư mục gốc của dự án, tạo Dockerfile:

nano Dockerfile

Các Docker image được tạo bằng cách kế thừa và xếp chồng các image lên nhau. Bước đầu tiên của chúng ta sẽ là thêm base image cho ứng dụng của chúng ta sẽ tạo thành điểm khởi đầu cho việc xây dựng ứng dụng.

Chúng ta sẽ sử dụng image node:10-alpine, vì tại thời điểm viết bài này đây là phiên bản LTS (Long Term Support) được khuyến nghị của Node.js. Image alpine được lấy từ dự án Alpine Linux, và sẽ giúp chúng ta giảm kích thước image.

Thêm chỉ dẫn FROM để đặt base image của ứng dụng:

FROM node:10-alpine

Image này bao gồm Node.js và npm. Mỗi Dockerfile phải bắt đầu bằng một chỉ dẫn FROM.

Mặc định, Docker Node image bao gồm một người dùng không phải root là node mà bạn có thể sử dụng để tránh chạy container ứng dụng của bạn với quyền root. Đây là một thực hành bảo mật được khuyến nghị để tránh chạy container với quyền root và hạn chế các quyền hạn trong container chỉ ở mức cần thiết để chạy các tiến trình của nó. Do đó, chúng ta sẽ sử dụng thư mục chính của người dùng node làm thư mục làm việc cho ứng dụng của chúng ta và đặt họ làm người dùng của chúng ta bên trong container.

Để tinh chỉnh các quyền trên mã nguồn ứng dụng của chúng ta trong container, hãy tạo thư mục con node_modules trong /home/node cùng với thư mục app. Việc tạo các thư mục này sẽ đảm bảo chúng có các quyền mà chúng ta muốn, điều này sẽ quan trọng khi chúng ta tạo các node modules cục bộ trong container bằng npm install. Ngoài việc tạo các thư mục này, chúng ta sẽ đặt quyền sở hữu cho chúng cho người dùng node của chúng ta:

...
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

Để biết thêm thông tin về tiện ích của việc hợp nhất các chỉ dẫn RUN.

Tiếp theo, đặt thư mục làm việc của ứng dụng thành /home/node/app:

...
WORKDIR /home/node/app

Nếu một WORKDIR không được đặt, Docker sẽ tạo một thư mục mặc định, vì vậy tốt hơn là nên đặt nó một cách rõ ràng.

Tiếp theo, sao chép các tệp package.jsonpackage-lock.json (đối với npm 5+):

...
COPY package*.json ./

Việc thêm chỉ dẫn COPY này trước khi chạy npm install hoặc sao chép mã ứng dụng cho phép chúng ta tận dụng cơ chế caching của Docker. Ở mỗi giai đoạn trong quá trình xây dựng, Docker sẽ kiểm tra xem nó có một lớp được cache cho chỉ dẫn cụ thể đó hay không. Nếu chúng ta thay đổi package.json, lớp này sẽ được xây dựng lại, nhưng nếu chúng ta không thay đổi, chỉ dẫn này sẽ cho phép Docker sử dụng lớp image hiện có và bỏ qua việc cài đặt lại các node modules của chúng ta.

Để đảm bảo rằng tất cả các tệp ứng dụng thuộc sở hữu của người dùng node không phải root, bao gồm cả nội dung của thư mục node_modules, hãy chuyển người dùng sang node trước khi chạy npm install:

...
USER node

Sau khi sao chép các dependencies của dự án và chuyển người dùng của chúng ta, chúng ta có thể chạy npm install:

...
RUN npm install

Tiếp theo, sao chép mã ứng dụng của bạn với các quyền thích hợp vào thư mục ứng dụng trên container:

...
COPY --chown=node:node . .

Điều này sẽ đảm bảo rằng các tệp ứng dụng thuộc sở hữu của người dùng node không phải root.

Cuối cùng, expose cổng 8080 trên container và khởi động ứng dụng:

...
EXPOSE 8080

CMD [ "node", "app.js" ]

EXPOSE không công bố cổng, mà thay vào đó hoạt động như một cách để ghi lại những cổng nào trên container sẽ được công bố khi runtime. CMD chạy lệnh để khởi động ứng dụng: trong trường hợp này, node app.js. Lưu ý rằng chỉ nên có một chỉ dẫn CMD trong mỗi Dockerfile. Nếu bạn bao gồm nhiều hơn một, chỉ cái cuối cùng sẽ có hiệu lực.

Có nhiều điều bạn có thể làm với Dockerfile. Để có danh sách đầy đủ các chỉ dẫn.

Dockerfile hoàn chỉnh trông như thế này:

FROM node:10-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

COPY --chown=node:node . .

EXPOSE 8080

CMD [ "node", "app.js" ]

Lưu và đóng tệp khi bạn đã hoàn tất chỉnh sửa.

Trước khi xây dựng image ứng dụng, hãy thêm một tệp .dockerignore. Hoạt động tương tự như tệp .gitignore, .dockerignore chỉ định các tệp và thư mục nào trong thư mục dự án của bạn không nên được sao chép vào container của bạn.

Mở tệp .dockerignore:

nano .dockerignore

Trong tệp, thêm các node modules cục bộ của bạn, nhật ký npm, Dockerfile và tệp .dockerignore:

node_modules
npm-debug.log
Dockerfile
.dockerignore

Nếu bạn đang làm việc với Git thì bạn cũng sẽ muốn thêm thư mục .git và tệp .gitignore của mình.

Lưu và đóng tệp khi bạn đã hoàn tất.

Giờ đây bạn đã sẵn sàng để xây dựng image ứng dụng bằng lệnh docker build. Sử dụng cờ -t với docker build sẽ cho phép bạn gắn thẻ (tag) image với một tên dễ nhớ. Bởi vì chúng ta sẽ push image lên Docker Hub, hãy bao gồm tên người dùng Docker Hub của chúng ta trong tag. Chúng ta sẽ gắn thẻ image là nodejs-image-demo, nhưng bạn hoàn toàn có thể thay thế bằng một tên của riêng bạn. Nhớ thay thế your_dockerhub_username bằng tên người dùng Docker Hub của bạn:

sudo docker build -t your_dockerhub_username/nodejs-image-demo .

Dấu . chỉ định rằng ngữ cảnh xây dựng (build context) là thư mục hiện tại.

Sẽ mất một hoặc hai phút để xây dựng image. Sau khi hoàn tất, hãy kiểm tra các image của bạn:

sudo docker images

Bạn sẽ nhận được đầu ra sau:

Output
REPOSITORY                                         TAG                 IMAGE ID            CREATED             SIZE
your_dockerhub_username/nodejs-image-demo          latest              1c723fb2ef12        8 seconds ago       73MB
node                                               10-alpine           f09e7c96b6de        3 weeks ago        70.7MB

Giờ đây có thể tạo một container với image này bằng cách sử dụng lệnh docker run. Chúng ta sẽ bao gồm ba cờ với lệnh này:

  • p: Điều này sẽ công bố cổng trên container và ánh xạ nó tới một cổng trên host của chúng ta. Chúng ta sẽ sử dụng cổng 80 trên host, nhưng bạn nên cảm thấy thoải mái sửa đổi điều này nếu cần thiết nếu bạn có một tiến trình khác đang chạy trên cổng đó.
  • d: Điều này chạy container ở chế độ nền (background).
  • -name: Điều này cho phép chúng ta đặt cho container một cái tên dễ nhớ.

Chạy lệnh sau để xây dựng container:

sudo docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

Sau khi container của bạn đã khởi động và chạy, bạn có thể kiểm tra danh sách các container đang chạy của bạn với docker ps:

sudo docker ps

Bạn sẽ nhận được đầu ra sau:

Output
CONTAINER ID        IMAGE                                                   COMMAND             CREATED             STATUS              PORTS                  NAMES
e50ad27074a7        your_dockerhub_username/nodejs-image-demo               "node app.js"       8 seconds ago       Up 7 seconds        0.0.0.0:80->8080/tcp   nodejs-image-demo

Với container của bạn đang chạy, giờ đây bạn có thể truy cập ứng dụng của mình bằng cách điều hướng trình duyệt của bạn đến IP máy chủ của bạn mà không cần cổng:

http://your_server_ip

Trang chủ ứng dụng của bạn sẽ tải lại một lần nữa.

Bây giờ bạn đã tạo một image cho ứng dụng của mình, bạn có thể push nó lên Docker Hub để sử dụng trong tương lai.

Bước 4: Sử dụng Repository để làm việc với Images

Bằng cách push image ứng dụng của bạn lên một registry như Docker Hub, bạn làm cho nó có sẵn để sử dụng sau này khi bạn xây dựng và mở rộng các container của mình. Chúng ta sẽ minh họa cách điều này hoạt động bằng cách push image ứng dụng lên một repository và sau đó sử dụng image đó để tái tạo container của chúng ta.

Bước đầu tiên để push image là đăng nhập vào tài khoản Docker Hub mà bạn đã tạo trong phần điều kiện tiên quyết:

sudo docker login -u your_dockerhub_username

Khi được nhắc, nhập mật khẩu tài khoản Docker Hub của bạn. Đăng nhập theo cách này sẽ tạo một tệp ~/.docker/config.json trong thư mục chính của người dùng của bạn với thông tin đăng nhập Docker Hub của bạn.

Giờ đây bạn có thể push image ứng dụng lên Docker Hub bằng cách sử dụng tag bạn đã tạo trước đó, your_dockerhub_username/nodejs-image-demo:

sudo docker push your_dockerhub_username/nodejs-image-demo

Hãy kiểm tra tiện ích của image registry bằng cách hủy container và image ứng dụng hiện tại của chúng ta và xây dựng lại chúng với image trong repository của chúng ta.

Đầu tiên, liệt kê các container đang chạy của bạn:

sudo docker ps

Bạn sẽ nhận được đầu ra sau:

Output
CONTAINER ID        IMAGE                                       COMMAND             CREATED             STATUS              PORTS                  NAMES
e50ad27074a7        your_dockerhub_username/nodejs-image-demo   "node app.js"       3 minutes ago       Up 3 minutes        0.0.0.0:80->8080/tcp   nodejs-image-demo

Sử dụng CONTAINER ID được liệt kê trong đầu ra của bạn, dừng container ứng dụng đang chạy. Đảm bảo thay thế ID được tô sáng bên dưới bằng CONTAINER ID của riêng bạn:

sudo docker stop e50ad27074a7

Liệt kê tất cả các image của bạn với cờ -a:

docker images -a

Bạn sẽ nhận được đầu ra sau với tên image của bạn, your_dockerhub_username/nodejs-image-demo, cùng với image node và các image khác từ bản dựng của bạn:

Output
REPOSITORY                                           TAG                 IMAGE ID            CREATED             SIZE
your_dockerhub_username/nodejs-image-demo            latest              1c723fb2ef12        7 minutes ago       73MB
<none>                                               <none>              2e3267d9ac02        4 minutes ago       72.9MB
<none>                                               <none>              8352b41730b9        4 minutes ago       73MB
<none>                                               <none>              5d58b92823cb        4 minutes ago       73MB
<none>                                               <none>              3f1e35d7062a        4 minutes ago       73MB
<none>                                               <none>              02176311e4d0        4 minutes ago       73MB
<none>                                               <none>              8e84b33edcda        4 minutes ago       70.7MB
<none>                                               <none>              6a5ed70f86f2        4 minutes ago       70.7MB
<none>                                               <none>              776b2637d3c1        4 minutes ago       70.7MB
node                                                 10-alpine           f09e7c96b6de        3 weeks ago         70.7MB

Xóa container đã dừng và tất cả các image, bao gồm cả các image không sử dụng hoặc dangling images, bằng lệnh sau:

docker system prune -a

y khi được nhắc trong đầu ra để xác nhận rằng bạn muốn xóa container đã dừng và các image. Lưu ý rằng điều này cũng sẽ xóa build cache của bạn.

Giờ đây bạn đã xóa cả container đang chạy image ứng dụng của bạn và chính image đó.

Với tất cả các image và container của bạn đã bị xóa, giờ đây bạn có thể pull image ứng dụng từ Docker Hub:

docker pull your_dockerhub_username/nodejs-image-demo

Liệt kê các image của bạn một lần nữa:

docker images

Đầu ra của bạn sẽ có image ứng dụng của bạn:

Output
REPOSITORY                                     TAG                 IMAGE ID            CREATED             SIZE
your_dockerhub_username/nodejs-image-demo      latest              1c723fb2ef12        11 minutes ago      73MB

Giờ đây bạn có thể xây dựng lại container của mình bằng lệnh từ Bước 3:

docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

Liệt kê các container đang chạy của bạn:

docker ps

Output
CONTAINER ID        IMAGE                                                   COMMAND             CREATED             STATUS              PORTS                  NAMES
f6bc2f50dff6        your_dockerhub_username/nodejs-image-demo               "node app.js"       4 seconds ago       Up 3 seconds        0.0.0.0:80->8080/tcp   nodejs-image-demo

Truy cập http://your_server_ip một lần nữa để xem ứng dụng đang chạy của bạn.

Kết luận

Trong bài blog này, chúng ta đã cùng nhau xây dựng một ứng dụng web tĩnh với Express và Bootstrap, đồng thời tạo một Docker image hoàn chỉnh cho ứng dụng này. Chúng ta đã sử dụng image đó để tạo ra một container, sau đó push image lên Docker Hub để lưu trữ. Từ đó, bạn đã có thể dễ dàng xóa bỏ image và container hiện có, rồi tái tạo chúng một cách nhanh chóng chỉ bằng cách kéo từ repository Docker Hub về.

Khả năng tái sử dụng và triển khai nhanh chóng này là cực kỳ quan trọng trong các môi trường phát triển và sản xuất hiện đại. Bằng việc nắm vững các khái niệm cơ bản về Docker image, container và Docker Hub, bạn đã trang bị cho mình những công cụ mạnh mẽ để quản lý vòng đời ứng dụng một cách hiệu quả hơn.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất