DOCKER IN ACTION

 

Vài năm trở lại đây, Docker dường như trở thành software platform quốc dân, đi đâu cũng nghe thấy, người người Docker, nhà nhà Docker, tài liệu cũng muôn hình muôn dạng, đầy đủ trên internet. Vì vậy, trong bài viết này, mình sẽ không đi chi tiết về nguồn gốc, lịch sử, feature của Docker nữa, mà mình hướng dẫn các bạn tạo được một dockerfile, đóng gói ứng dụng thành image và lưu trữ trên các Container Registries, ở đây mình sử dụng luôn dịch vụ của docker – Docker Hub, cũng như cách lấy chúng về và sử dụng chúng.
Bài viết này sẽ cùng đi từ những steps cơ bản nhất và mình sẽ cố gắng giải thích dễ hiểu nhất để cho mọi người dù mới cũng sẽ dễ tiếp cận nhất có thể. Chúng ta cùng bắt đầu nhé!

INSTALL DOCKER

Tất nhiên rồi, để sử dụng Docker thì đầu tiên chúng ta phải cài đặt docker trên máy của mình. Trên trang chủ của Docker đã hướng dẫn chi tiết cách cài đặt cho các hệ điều hành: window, MacOS chip Intel, MacOS chip Apple và làm theo hướng dẫn theo từng step chi tiết trên trang chủ

Sau khi cài thành công, biểu tượng con cá voi đặc trưng của docker sẽ hiển thị ở trên thanh công cụ của từng hệ điều hành

Trên window


Trên macos

 

Docker Preferences

Docker preferences cho phép người dùng thiết lập các tính của Docker như: cài đặt, cập nhật, phiên bản, đăng nhập vào Docker Hub, v.v..

BUILD APP

Trong step này, chúng ta tạo một app NodeJs cơ bản và tạo Image cho chính nó. 

$ cd [path]
$ npm init -y
$ npm install ronin-server ronin-mocks
$ touch server.js

Trong app ta tạo một file server.js có nội dung như sau:

const ronin     = require( 'ronin-server' )
const mocks     = require( 'ronin-mocks' )

const server = ronin.server()

server.use( '/', mocks.server( server.Router(), false, true ) )
server.start()

Run ứng dụng để test

$ npm start
$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing" }'
{"code":"success","payload":[{"msg":"testing","id":"31f23305-f5d0-4b4f-a16f-6f4c8ec93cf1","createDate":"2020-08-28T21:53:07.157Z"}]}

$ curl http://localhost:8000/test
{"code":"success","meta":{"total":1,"count":1},"payload":[{"msg":"testing","id":"31f23305-f5d0-4b4f-a16f-6f4c8ec93cf1","createDate":"2020-08-28T21:53:07.157Z"}]}

 

CREATE DOCKERFILE

Có thể hình dung, DockerFile là nơi chứa tất cả các chỉ thị cho Docker, sau khi đọc các chỉ thị này Docker sẽ build các Docker Image một cách tự động.
Đầu tiên, trong app của mình, chúng ta tạo một file có tên là Dockerfile và mở nó bằng editor

# Comment
INSTRUCTION arguments
  • Các INSTRUCTION là các chỉ thị, được docker quy định. Khi khai báo, các bạn phải viết chữ in hoa.
  • Các arguments là đoạn nội dung mà chỉ thị sẽ làm gì.

Đầu tiên, để khởi tạo Docker file, chúng ta cần sử dụng FROM để tạo base.

FROM node:12.18.1

Docker images có kế thừa từ các images khác. Cho nên thay vì tự tạo và config, chúng ta sử dụng official NodeJs image đã có sẵn đầy đủ packages và config, đó chính là một feature nổi bật của Docker. Có thể hiểu giống như tính kế thừa của class, khi ta sử dụng From, Docker sẽ include toàn bộ chức năng từ node:12.18.1 vào image của chúng mình.

NODE_ENV dùng để khai báo môi trường, ở đây chúng ta chọn production để cải thiện hiệu năng

ENV NODE_ENV=production

WORKDIR dùng để đặt thư mục đang làm việc cho các chỉ thị khác 

WORKDIR /app

Trước khi run npm install, để thêm package.json package-lock.json file vào image. Chúng ta sử dụng COPY để thực hiện việc này. Command này có 2 parameters chính, param đầu tiên chỉ ra những file nào mình muốn copy vào image. Param thứ 2 chỉ chỗ chứa chúng. Chúng ta sẽ copy package.jsonpackage-lock.json vào /app

COPY ["package.json", "package-lock.json*", "./"]


Sau khi đã có package.json Bây giờ RUN command sẽ dùng để chạy npm install trong quá trình build image. Nó giống như việc chúng ta tự chạy npm install trên máy của chúng mình , nhưng mà lần này Node modules sẽ được cài vào node_modules ở trong image.

RUN npm install --production

Vậy là chúng ta đã có Node image theo version 12.18.1 cũng như cài các dependencies. Giờ chúng ta cần COPY source code vào image. COPY command sẽ thực hiện việc này như việc copy package.json ở trên.

COPY . .

COPY . . sẽ copy tất cả files ở trong image. Vậy là chúng ta đã có đầy đủ môi trường và source code, chúng ta cần CMD để khai báo câu lệnh sẽ chạy khi image của chúng ta nằm bên trong một container.

CMD [ "npm", "start" ]

 

Nội dung của Dockerfile hoàn chỉnh:


FROM node:12.18.1
ENV NODE_ENV=production

WORKDIR /app

COPY ["package.json", "package-lock.json*", "./"]

RUN npm install --production

COPY . .

CMD [ "npm", "start" ]

 

BUILD IMAGE

Sau khi đã có Dockerfile, bây giờ sử dụng docker build để build Docker image.

$ docker build -t node-docker .

-t hay –tag dùng để đánh tên của image
. dùng để trỏ vào vị trí chứa Dockerfile

Các command bên trong Dockerfile sẽ được chạy lần lượt theo từng step như bên dưới.

$ docker build -t node-docker .
Sending build context to Docker daemon  13.58MB
Step 1/7 : FROM node:12.18.1
 ---> f5be1883c8e0
Step 2/7 : ENV NODE_ENV=production
 ---> Running in 86e5f2cb18af
Removing intermediate container 86e5f2cb18af
 ---> 244a33a1a837
Step 3/7 : WORKDIR /app
 ---> Running in df3aff9d4ef3
Removing intermediate container df3aff9d4ef3
 ---> 3a8345aa777c
Step 4/7 : COPY ["package.json", "package-lock.json*", "./"]
 ---> 89a965a4fa52
Step 5/7 : RUN npm install --production
 ---> Running in 9f3069e64e66

added 110 packages from 68 contributors and audited 110 packages in 6.571s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Removing intermediate container 9f3069e64e66
 ---> 61456c61522e
Step 6/7 : COPY . .
 ---> 00cf7979e39c
Step 7/7 : CMD [ "npm", "start" ]
 ---> Running in b30b66b19356
Removing intermediate container b30b66b19356
 ---> f96a1ad745b5
Successfully built f96a1ad745b5
Successfully tagged node-docker:latest


Vậy là chúng ta đã tạo một image với id là f96a1ad745b5. Cùng kiểm tra trên local:

$ docker images -a             
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
node-docker                          latest              f96a1ad745b5        24 seconds ago      946MB

Có thể thấy được một image tên node-docker có id là f96a1ad745b5 đã được tạo vào 24 seconds trước.

RUN CONTAINER

Giờ cùng run và kiểm tra xem app của chúng ta có chạy đúng hay không.

$ docker run node-docker
Understanding Docker for Beginners - the Container Technology
Nguồn google

Image chỉ có thể đọc không thể đổi. Khi image được docker khởi chạy thì sẽ tạo ra các container ( phiên bản thực thi ), có thể ghi các dữ liệu vào trong nó . Vậy container là một phiên bản chạy của image.

Sử dụng một terminal khác để kiểm tra xem đã có container hay chưa:

$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
46e8149a09a0        node-docker         "docker-entrypoint.s…"   3 seconds ago       Up 2 seconds                            hungry_buck

Cùng test thử app:

curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{
        "msg": "testing"
}'
curl: (7) Failed to connect to localhost port 8000: Connection refused

curl: (7) Failed to connect to localhost port 8000: Connection refused xảy ra bởi vì chúng ta không thể kết nối tới port 8000, container đang chạy ở trong network của chính nó, cho nên chúng ta cần thiết lập để có thể chuyển từ port 8000 của local sang port 8000 của app.

Stop container bằng cách sử dụng container id của nó:

$ docker container stop 46e8149a09a0
46e8149a09a0

và khởi chạy lại cùng với sử dụng --publish hay -p cùng với format [host port]:[container port]

$ docker run -p 8000:8000 node-docker

Cùng check xem container đã chạy đúng port hay chưa. Sử dụng một terminal mới.

$ docker ps   
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
a8dbca7da298        node-docker         "docker-entrypoint.s…"   26 minutes ago      Up 26 minutes       0.0.0.0:8000->8000/tcp   agitated_austin

Giờ chúng ta cùng test thử:

$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing" }'
{"code":"success","payload":[{"msg":"testing","id":"31f23305-f5d0-4b4f-a16f-6f4c8ec93cf1","createDate":"2020-08-28T21:53:07.157Z"}]}

{"code":"success","payload":[{"msg":"testing","id":"549a62c1-4be1-4120-aad1-e4f98983357a","createDate":"2021-07-29T18:23:24.484Z"}]}zsh: no matches found: code:success,payload:[msg:testing]
                                                                                                


$ curl --request GET localhost:8000/test

{"code":"success","meta":{"total":1,"count":1},"payload":[{"msg":"testing","id":"a1198b85-93d2-45de-8897-5c988315f703","createDate":"2021-07-29T16:56:14.416Z"}]}%     

Vậy là app của chúng ta đã chạy trên container thành công.

CONTAINER REGISTRY – DOCKER HUB

Container Registry là các dịch vụ máy chủ cho phép lưu trữ các image container của cá nhân, công ty, team, … do các bên thứ 3 cung cấp hoặc tự build nội bộ. Khi lưu trữ container image trên hệ thống container registry thì chúng ta có thể sử dụng chúng cho rất nhiều khâu trong vòng đời phát triển ứng dụng sản phẩm. Hiện nay có rất nhiều dịch vụ của các nhà cung cấp nổi tiếng như:

* Azure Container Registry
* Docker Hub
* Quay Enterprise
* Google Container Registry.
* AWS Container Registry

Trong bài viết này chúng ta sẽ sử dụng dịch vụ của Docker : Docker hub để lưu trữ và tái sử dụng image của mình đã tạo ở các step trên.

Tạo tài khoản và repository trên Docker hub

Đầu tiên chúng ta tạo một tài khoản trên https://hub.docker.com/https://hub.docker.com/repository/create để tạo một repository.

Sau khi tạo xong, vào repository chúng ta sẽ thấy các thông tin của repo vừa tạo.

Sau đó, hãy login vào docker trên máy tính của mình bằng tài khoản vừa tạo ở step trên.

Login xong, chúng ta cũng có thể truy cập vào repository hay là kiểm tra đã có repository trên docker hub của mình hay chưa.

Đầu tiên chúng ta commit để lấy container đang chạy và lưu trạng thái hiện tại của nó dưới dạng một image.

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

$ docker commit a8dbca7da298 docker-node:version1            
sha256:7141cb7f50914fc085ec54dc74440729eace432ecba185a555abb63dc53840d6

Lệnh docker commit trả về một hàm băm sha256. Hàm băm này sẽ có thể được dùng để tạo image, chi tiết sẽ nói ở các phần tiếp theo. Khi thực hiện lệnh này, một image mới sẽ được tạo ra với tag như đã khai báo ở trên.

$ docker images -a                                                
REPOSITORY                           TAG                 IMAGE ID            CREATED           SIZE
docker-node                          version1            7141cb7f5091        1 minute ago      946MB

Thẻ (tag) là một giá trị ID (số hoặc chữ) được gắn kèm thông tin theo Docker Image trong một repository để đánh dấu thể hiện thông tin khác nhau giữa các Docker Image. Giờ mình tiến hành đánh thẻ tag cho image cũng như gán chúng với repository đã tạo trên Docker Hub và đánh tag cho nó.

docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

$ docker tag docker-node:version1 trunglk16/fv-node-docker:latest

Cuối cùng chúng ta tiến hành đẩy image lên Docker Hub:

$ docker push trunglk16/fv-node-docker:latest    
The push refers to repository [docker.io/trunglk16/fv-node-docker]
f52aaa6bb13b: Pushed 
86a87980981f: Pushed 
4e67f619eb46: Pushed 
858eb3847c98: Pushed 
788c5e75ec08: Pushed 
dc48ece44f3c: Mounted from library/node 
798326960eac: Mounted from library/node 
dacaab4534e4: Mounted from library/node 
bc17cd405095: Mounted from library/node 
ee854067fbbd: Mounted from library/node 
740ffea5d5c3: Mounted from library/node 
eac9ead92b24: Mounted from library/node 
23bca356262f: Mounted from library/node 
8354d5896557: Mounted from library/node 
lastest: digest: sha256:aa85c7e3ddd2edc238f1d002e9f01fbd636d76903d88dfa3e715d21b154cc14b size: 3259

Cùng kiểm tra trên Docker Hub:

Vậy là chúng ta đã tạo và lưu trữ trên Docker Hub thành công. Bây giờ chúng ta thử pull về và chạy thử nhé.

Sử dụng docker pull để pull image về:

$ docker pull trunglk16/fv-node-docker:latest 
latest: Pulling from trunglk16/fv-node-docker
Digest: sha256:a466d0939a91281e78d82af2cdb8d1fbdcdf52302569c33a0bed798c4e0ee78c
Status: Image is up to date for trunglk16/fv-node-docker:latest
docker.io/trunglk16/fv-node-docker:latest
$ docker images                               
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
trunglk16/fv-node-docker             latest              f07359ba63b7        28 minutes ago      946MB

Tiến hành chạy thử image vừa pull về:

$ docker run trunglk16/fv-node-docker  

Test thử:

$ curl --request POST \
  --url http://localhost:8000/test \
  --header 'content-type: application/json' \
  --data '{"msg": "testing" }'
{"code":"success","payload":[{"msg":"testing","id":"31f23305-f5d0-4b4f-a16f-6f4c8ec93cf1","createDate":"2020-08-28T21:53:07.157Z"}]}

{"code":"success","payload":[{"msg":"testing","id":"549a62c1-4be1-4120-aad1-e4f98983357a","createDate":"2021-07-29T18:23:24.484Z"}]}zsh: no matches found: code:success,payload:[msg:testing]

$ curl http://localhost:8000/test

{"code":"success","meta":{"total":1,"count":1},"payload":[{"msg":"testing","id":"549a62c1-4be1-4120-aad1-e4f98983357a","createDate":"2021-07-29T18:23:24.484Z"}]}%       

 

Work rồi, vậy là chúng ta đã hoàn thành một cycle về việc tạo một app, đưa nó vào Docker, đưa nó lên Docker Hub, tải nó về và chạy. Hi vọng bài viết của mình sẽ giúp mọi người hiểu rõ hơn về Docker, rất mong nhận được sự đóng góp của mọi người để mình có thể hoàn thiện bài viết hơn cũng như cho ra những phần tiếp theo của series này.

REFERENCES:
https://docs.docker.com/get-started/

Add a Comment

Scroll Up