khanhnnvn

Collecting Ubuntu Linux System Information

 Hệ thống  Comments Off on Collecting Ubuntu Linux System Information
Nov 162015
 

Find the system host name

Display the system’s host name:
$ hostname
$ cat /etc/hostname
server1

Display the system’s DNS domain name:
$ dnsdomainname
cyberciti.biz

Display the system’s Fully Qualified Domain Name (FQDN):
$ hostname -f
server1.cyberciti.biz

Find the system serial number, manufacturer of the system and model name

$ sudo dmidecode -s system-serial-number
$ sudo dmidecode -s system-manufacturer
$ sudo dmidecode -s system-product-name
$ sudo dmidecode | more

OR use the lshw command:
# lshw | more
$ sudo lshw -short

Display information about installed hardware

$ sudo lsdev

Find the system CPU info

$ cat /proc/cpuinfo
OR
$ lscpu

Display CPU (processors) related statistics

$ sudo mpstat
$ sudo mpstat 1
$ sudo mpstat -A

Find the system main memory (RAM) info

Show statistics about memory usage on the system including total installed and used RAM:
$ less /proc/meminfo
Show amount of free and used memory in the system:

free
## Display the amount of memory in megabytes ##
free -m
 
## Display the amount of memory in gigabytes ##
free -g
 
## Display the amount of memory in terabytes ##
free --tera
 
## Display human readable output ##
free -h

Show the system swap space usage

$ swapon -s
$ cat /proc/swaps
$ cat /proc/meminfo
$ top
$ vmstat
$ for file in /proc/*/status ; do awk '/VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 2 -n -r | less
$ smem

Show the system virtual memory statistics

$ sudo vmstat
$ sudo vmstat 1
$ sudo vmstat 2

Find the Ubuntu Linux distribution version and related information

$ lsb_release -a

Find the system kernel version number

$ uname -r
OR
$ uname -a

Find the system kernel parameters

$ cat /proc/cmdline
$ sysctl -a | more

Find the system kernel architecture (32 bit or 64 bit)

$ uname -m
$ getconf LONG_BIT
$ arch

Find the system disk information

Show all installed disks and size:
# fdisk -l | grep '^Disk /dev'

List all partitions of /dev/sda disk:

To read a disk label for /dev/sda:
# fdisk -l /dev/sda
To label a disk:
$ sudo fdisk /dev/sda
$ sudo e2label /dev/sda1
$ sudo cfdisk /dev/sda

Show block device attributes:

# blkid

List all block devices:

# lsblk

Display file system disk space usage:

$ df
$ df -H
$ df -HT

Estimate file space usage:

$ du
$ du /home

Display mounted file system:

$ cat /proc/mount
$ mount

Display SCSI devices (or hosts) and their attributes on Linux:

$ lsscsi

Display I/O statistics

$ sudo iostat
$ sudo iostat 2

Find the system PCI devices information

$ lspci
$ lspci -vt
$ lspci | grep -i 'something'
$ lspci -vvvn| less

Find the system USB devices information

$ lsusb
$ lsusb -vt

Find the system Wireless devices information

$ iwconfig
$ watch -n 1 cat /proc/net/wireless
$ wavemon

Find the system VGA/Graphics devices information

$ lspci | grep -i vga
$ lspci -vvnn | grep VGA

OR
$ sudo lshw -class display

Find the system NVIDIA Graphics devices information

The following commands only works with Nvidia’s binary Linux driver:
$ nvidia-smi
OR
$ nvidia-settings

Find the system AMD/ATI Graphics devices information

The following command only works with AMD’s binary Linux driver called catalyst:
$ fglrxinfo

Which version of Unity am I running?

$ unity --version

Find the system audio devices information

$ lspci | grep -i audio
OR
$ cat /proc/asound/cards
OR
$ arecord -l

Display the system/laptop battery status & thermal temperature

$ upower -i /org/freedesktop/UPower/devices/battery_BAT0
$ acpi -V

Find out how long the system has been running

$ uptime
$ who
$ w

Find the system load

$ uptime
$ cat /proc/loadavg
$ sudo top
$ sudo htop
$ sudo atop

Show the system reboot and shutdown history

$ last reboot
$ last shutdown

Show runlevel

$ runlevel
$ who -r

Display kernel ring buffer (boot time) messages

Use the following command to see boot time message including hardware configuration
$ sudo less /var/log/dmesg
$ sudo grep 'regx' /var/log/dmesg
$ sudo grep '[h|s]d' /var/log/dmesg

Display the system drivers (modules)

$ sudo lsmod
$ sudo modinfo {driver_name}
$ sudo modinfo kvm

Find the system IP address and related information

You need to use the ip command:

## Info about all interfaces. Must be run as root via sudo command ##
sudo ip a
sudo ip
sudo ip link ls up
sudo ifconfig -a
 
## Only show eth1 interface info ##
sudo ip a show eth0
sudo ifconfig eth0

Display the system routing table

## You can use any one of the following command ##
## Must be run as root ##
sudo ip r
sudo route -n
sudo netstat -nr

Display the system ethernet bridge

$ sudo brctl show
$ sudo bridge link

Display the system DNS server and related information

Display the system name server IP address (ISP or your dns server IP should be listed here):
# cat /etc/resolv.conf
Display the system resolver configuration file. This is useful to find out how host lookups are to be performed:
# cat /etc/host.conf
Use above two files to configure name resolution.

Display information about the system ports and socket

## Must run as root via sudo ##
sudo ss
## Display all listing ports ##
sudo ss -l
sudo netstat -tulpn
sudo netstat -tulpn | grep LISTEN
 
## Display all TCP sockets
sudo ss -t -a
 
## Display all UDP sockets.
sudo ss -u -a
 
## List all open files
lsof | more
lsof | grep something
lsof /dev/sda2
lsof /path/to/file

Display the list of running services

### SYS V ###
$ sudo service --status-all

OR
## UPSTART ##
$ sudo initctl list

Find out if service is enabled:

## UPSTART ##
$ sudo initctl status service-name
$ sudo initctl status smbd

OR
## SYS V
$ sudo service serviceName status
$ sudo service nginx status

View log files

$ cd /var/log
$ ls -l
$ tail -f /var/log/fileName
$ grep 'something' /var/log/fileNameHere

Find file by name

$ locate fileName
$ locate htpasswd
$ locate passwd
$ locate my.resume.doc

Find file by given condition

$ find {/dir/to/search} -name {file-to-search} -print
$ find /etc/ -name /etc/passwd -print
$ find $HOME -name '*.doc' -print

View user account details

$ less /etc/passwd
$ grep userName /etc/passwd
$ getent passwd

View group account details

$ less /etc/group
$ getent group
$ grep group-name /etc/group
$ groups userName

View password policy

$ chage -l userName
$ chage -l root
$ chage -l vivek

View system usage

$ sudo top
$ sudo htop
$ sudo atop
$ sudo ps auxwww
$ sudo netstat [options]
$ sudo iostat
$ sudo mpstat 1
$ sudo sar [options]

Trace system call

$ strace -o output.txt /bin/foo
$ strace -p 22254 -s 80 -o debug.nginx.txt

Trace library call

$ sudo ltrace /usr/sbin/httpd
$ sudo ltrace /sbin/chroot /usr/sbin/httpd

View process info

$ sudo pstree
$ sudo pstree | less
$ sudo ps auxwwwm
$ ps alxwww
$ ps auxwww
$ lsof -b M -n -l

Change process priority

$ sudo /bin/nice -n -1 command-name-here
$ sudo /bin/nice -n -1 pid
$ sudo renice {priority} pid

View process’s CPU affinity

$ sudo taskset -p {pid-here}
$ sudo taskset -p 42

Display the system listing of all package installed

$ dpkg -l
$ dpkg -l | less
$ dpkg -l nginx

Display the system listing of all patches installed

$ sudo apt-show-versions -a | grep -i "security"

Display the list of needed runtime libraries to run file

$ ldd file

Find what package a file belongs to

$ dpkg -S /path/to/file
$ dpkg -S /bin/ls

Create a backup list of all installed software

$ sudo dpkg --get-selections > /root/installed.pkgs.txt
Want to restore it again?
$ sudo dpkg --set-selections < /root/installed.pkgs.txt

Display the system firewall configuration

$ sudo iptables -L -n -v
$ sudo ufw status numbered
$ sudo ufw status verbose
$ sudo ufw app list

Do not forget to read man pages featured in this post:
$ man dpkg
$ man htop
$ man ...

Finally, make a backup – it cannot be stressed enough how important it is to make a backup of your system. A good backup plan allow you to recover from disk failure, accidental file deletion, file corruption, or complete server destruction, including destruction of on-site backups.

[DBA] Hướng dẫn thao tác với Partition trong Oracle DB

 Database  Comments Off on [DBA] Hướng dẫn thao tác với Partition trong Oracle DB
Nov 162015
 

Hướng dẫn thao tác với Partition trong Oracle DB

Drop partition

Drop một partition sẽ xóa toàn bộ dữ liệu trên partition đó

–drop a partition

alter table thetest drop partition p201510;

–>drop a partition + its data + drop the corresponding partitions in each local index

Insert dữ liệu vào một bảng đã có partition

Nếu dữ liệu không thuộc partition nào có sẵn thì sẽ không them được trừ khi cho thêm partition vào bảng. Câu lệnh INSERT không khác gì với câu INSERT thông thường.

Thêm partition vào một bảng đã có sẵn partition (kể cả composite partition)

Bài này dựa trên kiến thức cơ bản về partition như các loại partition, cách đánh partition, … nên sẽ không nói lại các phần căn bản này.

Sử dụng câu lệnh sau:

ALTER TABLE thetest2

ADD PARTITION P201511 VALUES LESS THAN (to_date(‘20151201′,’YYYYMMDD’));

Thêm subpartition vào một bảng có sẵn partition (*-list partition)

ALTER TABLE THETESt2 MODIFY PARTITION P201509

add subpartition P201509_PC values (‘C’, ‘c’);

Nhận thấy, ta cần phải chỉ định rõ rang partition trước khi thêm một subpartition và cấu trúc của việc insert này phải là ALTER TABLE … MODIFY PARTITION … chứ không giống như thêm partition.

Tuy vậy ta có thể thêm subpartition vào cùng với partition trong một câu lệnh như sau:

ALTER TABLE thetest2

ADD PARTITION P201512 VALUES LESS THAN (to_date(‘20160101′,’YYYYMMDD’))

(

SUBPARTITION P201512_PA values (‘A’, ‘a’),

SUBPARTITION P201512_PB values (‘B’, ‘b’),

SUBPARTITION P201512_PC values (‘C’, ‘c’)

)

Chi tiết hơn có thể tham khảo tại:https://docs.oracle.com/cd/E18283_01/server.112/e16541/part_admin002.htm#i1007318

Tạo mới partition vào một table có sẵn dữ liệu nhưng chưa có partition

Để tạo mới partition vào một table có sẵn dữ liệu khi ở khai báo mặc định của table lại không có partition thì cần sử dụngDBMS_REDEFINITION hoặc ALTER TABLE … EXCHANGE PARTITION.

Cả hai phương pháp này để dựa trên cách thức là sẽ tạo một bảng mới với cấu trúc giống hệt nhưng đã khai báo partition khi tạo mới và swap giữa hai bảng này. Cuối cùng sau khi đã swap xong thì sẽ thực hiện việc drop bảng tạm đi. Cụ thể như sau:

Bước 1: Khởi tạo dữ liệu sample

Giả sử ta có một bảng với dữ liệu đã có sẵn như sau:

create table thetest(

logid number,

transdate date,

status varchar2(50),

pid varchar2(50),

constraint pk_logid PRIMARY KEY(logid)

);

insert into thetest values(1,to_date(‘20151101’, ‘YYYYMMDD’), ‘1’, ‘A’);

insert into thetest values(2,to_date(‘20151001’, ‘YYYYMMDD’), ‘1’, ‘A’);

insert into thetest values(3,to_date(‘20151201’, ‘YYYYMMDD’), ‘1’, ‘A’);

Bước 2: Tạo một bảng tương tự để swap

Tạo mới một bảng sạch sẽ với cấu trúc giống hệt nhưng đã khai báo partition. Ở đây tôi dung composite partition:

create table thetest2(

logid number,

transdate date,

status varchar2(50),

pid varchar2(50),

constraint pk_logid2 PRIMARY KEY(logid))

partition by range(transdate) subpartition by list(pid)

subpartition template(

subpartition pa values(‘A’, ‘a’),

subpartition pb values(‘B’, ‘b’)

)

(

partition p201509 values less than (to_date(‘20151001′,’YYYYMMDD’)),

partition p201510 values less than (to_date(‘20151101′,’YYYYMMDD’))

)

Bước 3: Kiểm tra xem có redefinition được bảng ban đầu không

Đăng nhập bằng user SYS hoặc một user nào được gán quyền EXEC DBMS_REDEFINITION

EXEC DBMS_REDEFINITION.can_redef_table(‘SCHEMA’, ‘THETEST’);

Nếu câu lệnh trên không gây lỗi tức là có thể redefine lại bảng được với user đã khai báo

Bước 4: Ghi nhớ câu lệnh stop/abort

Là một DBA, bạn nên ghi nhớ câu stop trước khi học câu start.!

EXEC DBMS_REDEFINITION.abort_redef_table(‘SCHEMA’, ‘THETEST’, ‘THETEST2’);

Nếu quá trình organize table có vấn đề chúng ta sẽ phải thực hiện việc restart lại quá trình này nhưng bởi vì redefinition sẽ yêu cầu tạo snapshot nên phải chạy câu lênh abort_redef_table() ở trên để release snapshot trước khi bắt đầu lại.

Có thể thử bằng cách tạo ít partition ở bảng tạm hơn so với các giá trị của bảng chính. Khi này sau khi bắt đầu tiến trình thì Oracle sẽ báo lỗi do một số giá trị sẽ không biết cho vào partition nào. Lúc này giả sử rằng ta đã tạo thêm partition ở bảng tạm cho chuẩn với dữ liệu (cách thêm partition vào bảng có sẵn dữ liệu đã nêu ở trên) chạy lại câu lệnhstart_redef_table() sẽ bị lỗi:

ORA-23539: table “SCHEMA”.”THETEST” currently being redefined

Như vậy Oracle vẫn ghi nhận bảng THETSET đang được redefined nên tất nhiên sẽ ngăn cấm không cho thực hiện câu lệnh.

Bước 5: Thực hiện tiến trình

EXEC DBMS_REDEFINITION.start_redef_table(‘SCHEMA’, ‘THETEST’, ‘THETEST2’);

Lưu ý: sau câu lệnh này, dữ liệu đã được chuyển sang bảng THETEST2 nhưng dữ liệu trên bảng THETEST vẫn còn. Thời gian thực hiện tiến trình này không nhanh và phụ thuộc vào dữ liệu trong DB.

exec DBMS_REDEFINITION.sync_interim_table(‘SCHEMA’, ‘THETEST’, ‘THETEST2’);

Cuối cùng là kết thúc quá trình redefinition

exec DBMS_REDEFINITION.finish_redef_table(‘SCHEMA’, ‘THETEST’, ‘THETEST2’);

Sau câu lệnh này, phần bảng THETEST đã có partition đồng thời có dữ liệu đầy đủ. Bây giờ có thể drop bảng tạm THETEST2 đi do bảng này vẫn còn dữ liệu nhưng không còn cần thiết nữa.

Thời gian thực hiện tiến trình finish này khá dài nên đòi hỏi tính toán hợp lý và có sự kiên nhẫn. Thêm một vấn đề khi làm DBA là tình kiên trì quyết không bấm Cancel. Thay vì đó hãy giành thời gian chuẩn bị kỹ càng và học cách stop tiến trình.

Bước 6: Dọn dẹp tiến trình

drop table thetest2;

alter table thetest rename constraint pk_logid2 to pk_logid;

alter index pk_logid2 rename to pk_logid;

Đoạn trên đã thực hiện xóa bảng tạm, đổi tên lại các constraint và các index cho đúng với bảng cũ. Nghiên cứu và kiểm tra chính xác bảng cũ là rất quan trọng.

Xử lý lỗi The listener supports no services trên Oracle Database (lsnrctl status)

 Database  Comments Off on Xử lý lỗi The listener supports no services trên Oracle Database (lsnrctl status)
Nov 162015
 

Xử lý lỗi The listener supports no services trên Oracle Database (lsnrctl status)

Tình huống

Khi thực hiện login root vào server DB bình thường, telnet từ client đến cổng 1521 của server database được nhưng khi kết nối bằng client (sqlplus) báo lỗi

ORA-12514: TNS:listener does not currently know of service requested in connect descriptor

Giải pháp

Lên server và chuyển sang user oracle với các câu lệnh sau:

su – oracle

Kiểm tra listener

lsnrctl status

Nhận được kết quả có dạng như sau:

[oracle@DB19 ~]$ lsnrctl status

Alias                     LISTENER

Version                   TNSLSNR for Linux: Version 11.1.0.6.0 – Production

Start Date                06-NOV-2015 18:04:00

Uptime                    0 days 0 hr. 10 min. 28 sec

Trace Level               off

Security                  ON: Local OS Authentication

SNMP                      OFF

Listener Parameter File   /u01/app/oracle/product/11.2.0/db_1/network/admin/listener.ora

Listener Log File         /u01/app/oracle/diag/tnslsnr/DB19/listener/alert/log.xml

Listening Endpoints Summary…

The listener supports no services

The command completed successfully

Ta nhận thấy Oracle Listener báo lại cho chúng ta message: The listener supports no services điều này có nghĩa database instance chưa được startup. Hãy thực hiện lệnh sau:

[oracle@DB19 ~]$ sqlplus / as sysdba

Connected to an idle instance.

SQL> startup

Khi này db instance bắt đầu khởi động kết quả sẽ có dạng như sau:

ORACLE instance started.

Total System Global Area  839282688 bytes

Fixed Size                  2149040 bytes

Variable Size             608175440 bytes

Database Buffers          222298112 bytes

Redo Buffers                6660096 bytes

Database mounted.

Database opened.

Và như vậy ta có thể kết nối lại với client để hưởng thành quả.

Tổng quan kiến trúc database vật lý của Oracle

 Database  Comments Off on Tổng quan kiến trúc database vật lý của Oracle
Nov 162015
 

Tổng quan kiến trúc database vật lý của Oracle

Phần này sẽ mô tả kiến trúc vật lý của database Oracle sau khi đã mô tả kiến trúc ứng dụng của Oracle trong bài trước. Các vấn đề đề cập đến bao gồm datafiles, redo log files và control files. Phần này sẽ khó hiểu hơn mô hình ứng dụng và đòi hỏi phải có nền tảng nhất định để có thể đọc hiểu.

Datafiles

Mỗi Oracle database đều có một hoặc nhiều datafile vật lý. Datafiles lưu giữ tất cả các dữ liệu của database. Các kiểu dữ liệu logic như table, index, view, sequence được lưu trữ vật lý trên datafiles được cấp phát cho database.

Các tính chất của datafiles là:

– Một datafile chỉ có thể thuộc duy nhất 1 db

– Datafiles có thể có một vài thuộc tính như tự động mở rộng khi database hết dung lượng

– Một hoặc một vài datafiles sẽ tạo thành một đối tượng lưu trữ logic của db gọi là tablespace.

Về cơ bản, dữ liệu trong datafiles sẽ được đọc nếu cần và sẽ được lưu trữ vào trong cache của oracle. Và như vậy, giả sử một user thực hiện request dữ liệu từ một bảng trong DB mà dữ liệu này chưa có trong cache của db thì nó sẽ được đọc từ datafiles tương ứng sau đó lưu trữ lại trong bộ nhớ.

Việc chỉnh sửa dữ liệu không nhất thiết phải ghi vào datafile ngay lập tức. Để giảm thiểu việc truy nhập ổ cứng thường xuyên đồng thời tăng performance, dữ liệu được pooled trong bộ nhớ và được ghi lên datafiles tương ứng cùng lúc vào một thời điểm (được xác định bởi DBWn).

Control Files

Mỗi Oracle DB sẽ có một control file. Control file sẽ lưu trữ thông tin về cấu trúc vật lý của DB. Ví dụ các thông tin như sau:

– Tên db

– Tên và địa chỉ đường dẫn của các datafiles và các redo log files

– Timestamp tạo DB

Để đề phòng các trường hợp hỏng file, oracle sẽ quản lý một số nhân bản của control file đồng thời.

Mỗi khi một instance của Oracle DB được khởi động thì control file của nó sẽ xác định database, redo log files cần phải mở cho các yêu cầu truy xuất DB. Khi database có thay đổi như thêm mới datafile hoặc redo log file được tạo ra, … thì control file sẽ tự động chỉnh sửa theo thay đổi này. Và với các chức năng trên, control file cũng được sử dụng trong quá trình phục hồi DB.

Redo Log Files

Mỗi Oracle DB đều có ít nhất 2 redo log files. Tập hợp các redo log files này được gọi là redo log của db. Một redo log được tạo thành từ các redo records.

Chức năng chính của redo log là ghi lại tất cả các thay đổi với dữ liệu. Nếu như có lỗi trong quá trình thay đổi dữ liệu thì với redo log ta có thể phục hồi lại dữ liệu đã mất.

Để bảo vệ chính redo log bị hư hại, Oracle thực hiện nhân bản redo logvà do đó sẽ có ít nhất 2 bản copy của redo log nằm trên các đĩa khác nhau.

Các thông tin trong redo log files được dùng để phục hồi dữ liệu từ hệ thống trong trường hợp ví dụ khi các dữ liệu trong bộ nhớ chưa kịp ghi vào datafiles thì mất điện, khi này, dữ liệu sẽ được phục hồi khi database được mở lại. Bằng việc sử dụng các thông tin trong redo log file gần nhất ta có thể khôi phục dữ liệu trở vê thời gian khi mất điện xảy ra.

Quá trình khôi phục này được gọi là rolling forward.

Bên cạnh các loại trên còn có Archive log files, Parameter files, Alert and Trace log files và Backup files. Các loại files này sẽ được đề cập đến trong các bài khác có liên quan.

Install Php Oracle Client on Ubuntu

 Database, Linux  Comments Off on Install Php Oracle Client on Ubuntu
Nov 162015
 
  1. Yêu cầu:

– Hệ điều hành: Ubuntu.

– PHP phiên bản: 5.5.29.

– Oracle Client: 2 gói phần mềm có tên instantclient-basic-linux-12.1.0.2.0 và instantclient-sdk-linux-12.1.0.2.0.

  1. Các bước cài đặt:

Bước 1: bạn chạy các câu lệnh sau:

sudo apt-get install build-essential

sudo apt-get install php5-dev php-pear libaio1

 

Bước 2: Bạn chỉnh sửa file /etc/environment chú ý thay đổi đường dẫn của bạn

# Oracle Instant ClientLD_LIBRARY_PATH=”/usr/local/lib/instantclient_11_2″

TNS_ADMIN=”/usr/local/lib/instantclient_11_2″

ORACLE_BASE=”/usr/local/lib/instantclient_11_2″

ORACLE_HOME=$ORACLE_BASE

Với bản 12.1

LD_LIBRARY_PATH=”/usr/local/lib/instantclient_12_1″

TNS_ADMIN=”/usr/local/lib/instantclient_12_1″

ORACLE_BASE=”/usr/local/lib/instantclient_12_1″

ORACLE_HOME=$ORACLE_BASE

 

Bước 3: Bạn download 2 gói phần mềm có tên trên và giải nén vào thư mục: /usr/local/lib

cd /usr/local/lib

sudo unzip <location-of-instant-client-basic>

sudo unzip <location-of-instant-client-sdk>

cd instantclient_11_2sudo

ln -s libclntsh.so.11.1 libclntsh.so

ln -s libclntsh.so.12.1 libclntsh.so

 

Bước 4: Bạn chạy lệnh sau:

sudo pecl install oci8

 

Nếu chương trình có yêu cầu nhập, bạn nhập:

instantclient,/usr/local/lib/instantclient_12_1

Bước 5: Bạn cần chỉnh sửa file cấu hình của php để enable oci lên

vim  /etc/php5/cli/php.iniextension=oci8.so

Bước 5: Khởi động lại Apache:

sudo /etc/init.d/apache2 reload

I fix this problem in my CENTOS 6.6 and php 5.6.8 in these two ways:

  1. If you need DTRACE:
    • yum install systemtap-sdt-devel
    • export PHP_DTRACE=yes
    • pecl install oci8
  2. if you don’t like DTRACE:
    • Download the oci8 tar ball and extract the files
    • modify the file ‘php_oci8_int.h’, change the 48th line
      #include “oci8_dtrace_gen.h”to #undef HAVE_OCI8_DTRACE
    • phpize
    • ./configure –with-oci8=
    • make
    • make install

Docker basic

 Hệ thống  Comments Off on Docker basic
Nov 152015
 

What is Docker?

When people talk about docker they are most likely talking about the docker engine. Docker is in fact an organization which creates tools to assist with the creation and deployment of software in a portable manor. As well as creating the docker engine they also have projects which orchestrates and manages deployed applications, we will come across more of these later.

Docker engine

The docker engine (which we will just refer to as docker from now on) is a piece of software which allows a sort of operating system level virtualization on linux based computers. This is called containerization and allows you to run applications within a container, which is a portable unit and can be moved between linux machines.

Containers

The biggest misconception I have found when it comes to containers is that people think they are just like virtual machines. They aim to achieve the same outcomes as a virtual machine including portability, speed and flexibility but they do it in a different way.

All linux systems are fundamentally the same, they are all running the linux kernel. The differences are the tools which are layered on top of the kernel. If you think about it these tools are just files in a filesystem which are executed in a certain order and interact with the kernel to achieve different goals.

Docker works by allowing you to mount multiple filesystems on one machine and then run applications within the context of the different filesystems. For example you could create a ubuntu based container which is running on a Red Hat server. If you run the bash tool within the ubuntu container you can make use of all the standard ubuntu tools, for example apt-get. Those tools will interact with the kernel run by the Red Hat host and function as if you were on a ubuntu machine.

Docker makes use of linux kernel functionality such as cgroups, selinux and chroot jails to keep the processes separate and contained within their containers.

The differences from a virtual machine

You may be thinking “this sounds a lot like virtualization” and you’re right, however the container will not do many of the things you would expect a virtual machine to do automatically.

Containers run exactly one process, no more, no less. So if you execute a command within a ubuntu container that command will act as if it is running on a ubuntu machine. However if that command relies on operating system services to be running, such as cron, it will not be able to find them. The host operating system may be running these services but the container is not.

To work around this you simply need more containers. This is the beauty of containerization, you can run a database service on ubuntu, which is accessed by a Java application running on Red Hat, which is backed up by a cron job running on debian, all on a host server running SUSE. The important thing to realise is that in this example SUSE is the only “operating system” actually running and taking up resources, the others are just running single processes but giving those processes access to the tools of alternate operating systems.

You can also specify particular versions of operating systems, so you could run a container based on ubuntu 13.10 on a ubuntu 14.04 server without worrying about compatibility issues.

Hopefully you can see now why this is exciting. It gives you a level playing field. You can develop an application on your desktop with 100% confidence it will behave exactly the same way on a production server.

Exercises

You will now work through some exercises which will help you get to grips with docker and also experience how we are using them in the Lab.

Requirements

The following exercises are designed to be run on an AWS EC2 instance. If you are taking part in the session live then please request an instance from one of the Lab core members, if you are taking part afterwards please sign up for an AWS free account and create an EC2 micro instance based on the “Amazon Linux” image. This guideshould get you started, just follow it up to the “Configuring your account” section.

Exercise 1: Installing docker

Before we do anything we need docker. To get started you’ll need to ssh on to your instance. It’s always good practice to run an update when you create an instance.

sudo yum update -y

Once this has finished we’re ready to install docker. Thankfully it is available in the Amazon linux repositories.

sudo yum install docker -y

Hooray you now have docker! All you need to do now is set it to start on boot (we might reboot our instance later) and then start it for the first time.

sudo chkconfig docker on
sudo service docker start

To test docker you should be able to run:

sudo docker -v

which should print out the version and build.

docker version 1.6.2, build 7c8fca2/1.6.2

To avoid having to run docker commands using sudo we can add our ec2-user to the docker group.

sudo usermod -G docker ec2-user

You will need to log out and back in again from your ssh session for this to take effect.

Exercise 2: My first container

Now that you have docker we can go ahead and create our first container. Docker containers are just commands run within the context of an alternative file system. These alternative filesystems are called images. You can build your own images but for now we are going to use an off-the-shelf one. Docker provides a place to store images called the Docker Hub. If you create a container which doesn’t have an image locally, but it finds one on the hub, it will download it for you.

Let’s get started by creating a simple installation of an apache web server. We are going to download the apache image from the hub and create a container to run it.

docker run -p 80:80 httpd

In this example we want docker to use the default httpd image.

Images are usually named following the convention of author/image, however we are using an official docker image which means we can omit the author.

We are also telling docker to link port 80 on the container to port 80 on the host. This means we can visit our instance in our web browser and we should see the “It works!” apache default page.

Apache test page

If you see the same page in your browser then you’ve successfully created an apache container.

You should also see some log output in your command line.

Exercise 3: Extending an existing image

Creating an apache instance is all well and good but what if we actually want it to serve our own content? To achieve this we need to take the default apache image and add our own files to it.

We are going to create a new directory to contain this project.

mkdir apache-app
cd apache-app

Now we can create our Dockerfile. I like to use vi for text editing but you may be more comfortable with nano.

nano Dockerfile

Set the contents of your Dockerfile to look like this, but set yourself as the maintainer of course.

FROM httpd:2.4
MAINTAINER Jacob Tomlinson <[email protected]>

COPY ./my-html/ /usr/local/apache2/htdocs/

EXPOSE 80

CMD ["httpd-foreground"]

Going through this line by line we see that we are inheriting this image FROM httpd. This means we want docker to download the httpd image as before but do some stuff to it afterwards. We are also explicitly stating the version of the image (which directly maps to the version of apache) we want rather than just going with the latest one.

The httpd image is really just the result of another Dockerfile which you can find on GitHub.

We are setting ourselves as the MAINTAINER so when someone else comes along and wants to use our image they know who to bother with their problems.

Next we are going to COPY the contents of a directory called my-html into the image and place them in apache’s default content location, which for 2.4 is/usr/local/apache2/htdocs/.

We want to EXPOSE port 80 to allow it to be accessed outside this container, think of it like a software firewall within docker.

Then we specify our command that we want to run when the container is started with CMD.

The last two commands are actually already defined within the httpd image, I wanted to put them in here to show what is happening but to also show that you can redefine a CMD. Docker will run the last CMD to be defined which is useful if you want to override the functionality of an image.

You may notice that the my-html directory doesn’t exist yet. Let’s create it.

mkdir my-html

Then let’s create an index.html file within it.

nano my-html/index.html

Set the content to something along the lines of:

<html>
  <head>
    <title>Hello world!</title>
  </head>
  <body>
    <h1>Hello world!</h1>
    <hr>
    <p>Docker is the best!</p>
  </body>
</html>

Now we can build our image.

docker build -t my-httpd .

See we’ve specified the image name with -t. This is the name we will use to run it.

docker run -p 80:80 my-httpd

Excellent, now when you navigate to your EC2 instance in your browser you should see your lovely new index page.

Custom apache test page

Exercise 4: Docker Compose

In the Lab we have progressed to the stage of needing to run multiple containers at once which make up a service. If we just used pure docker we would have to run each one individually and remember to specify the correct arguments (like -p) each time. We could create a bash file which contains all of these lines but there is a better way,docker compose.

Compose is another tool provided by docker and it allows you to write down your structure of containers and all of their arguments in a yaml file. You can then simply rundocker-compose up and your containers will be created.

To get started we need to install docker compose.

sudo pip install docker-compose

Again to test that it is installed correctly we run:

docker-compose -v

Which should print the version of docker-compose, cpython and openssl.

docker-compose version: 1.3.1
CPython version: 2.7.9
OpenSSL version: OpenSSL 1.0.1k-fips 8 Jan 2015

Let’s start simply by creating a compose file for our apache application. To do this we just need to create a new file called docker-compose.yml

nano docker-compose.yml

In this file we can put:

apache:
  build: .
  ports:
   - "80:80"

This file defines a new service called apache, it tells docker that to build this service it will find the Dockerfile in the current directory . and that we want to again bind port 80 on the container to 80 on the host.

Now we can run our application.

docker-compose up

You should see similar output to when running docker manually but the log outputs will be prefixed with the container name. When docker compose creates a container it names it after the service followed by an underscore and the number one. This is because you can scale containers with compose easily and it will create additional containers with incrementing numbers.

Exercise 5: Multiple containers in compose

Now that we are comfortable creating a container with compose let’s create a second container and link them together. Instead of using our apache container we’re going to create a python django application running with a postgres database.

Let’s make a new project directory and switch to it.

mkdir ~/django-app
cd ~/django-app

We’ll need a Dockerfile for our django app.

nano Dockerfile

With the following contents:

FROM python:2.7

ENV PYTHONUNBUFFERED 1

RUN mkdir /code

WORKDIR /code

COPY src/requirements.txt /code/

RUN pip install -r requirements.txt

COPY src/ /code/

Here we are using python:2.7 as our base image. This is another official image which will ensure we have python installed and at version 2.7.

We have our first use of ENV, this sets an environment variable within the container. In this case we want our python to be unbuffered which will help with our output in the docker console.

Next we RUN a command to create a directory for our django app to live in on our container. We then use WORKDIR to switch the current working directory within our container.

We COPY in a requirements.txt file which specifies our python dependancies. We’ll create this file in a minute. Then we are using pip the python package manager to install those dependancies.

Finally we want to copy the contents of our src directory on the host to the code directory on the container.

Now we want to create our requirements.txt file for docker to use.

mkdir src
nano src/requirements.txt

Add the following lines:

Django
psycopg2

Next we are going to define our docker-compose.yml file.

nano docker-compose.yml

Add the contents:

db:
  image: postgres
web:
  build: .
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - src/:/code/
  ports:
    - "80:8000"
  links:
    - db

Firstly we are creating a database service called db. We just want a plain old postgres server so we don’t need a Dockerfile for it, we can reference the image name from the Docker Hub directly. As we will be accessing this service from another container we don’t need to expose any ports to the outside world.

Then we create a web service for our django application. We are again going to be building the Dockerfile in the current directory, we are also specifying the command for the container to run. This can be done in the Dockerfile but you can also do it here.

As well as copying the contents of our src directory into the container on build we are going to mount it as a volume which means when the container makes changes to those files they will be persisted on the host even if the container is destroyed and rebuilt.

This is actually a bad practice, see “data only containers” in the further reading section below for more information.

We are linking our ports again but django runs on port 8000 by default so this time we are going to link port 80 on the host to 8000 on the container.

Finally we are going to tell our container to link with the db container. This means they will have network access between each other and it also sets up a local dns record (in /etc/hosts) on the web container so we can access the database at the hostname db.

Before we can start our web service django needs you to initialise its project and also tell it where the database is. We can do this with the docker compose runcommand, this runs the container but executes a different command to the one specified in the yaml file.

docker-compose run web django-admin.py startproject composeexample .

When running this docker will discover your images are not built, it will automatically download and build them for you and then move on to execute the command.

When this command finishes it should generate a new directory in your src directory called composeexample along with a manage.py file. Check that they are there.

ls src

The composeexample directory will contain a settings.py file. This is where you need to put the database configuration.

Open the file for editing, docker will have created these files as root so you’ll need a sudo on this one.

sudo nano src/composeexample/settings.py

Then update the DATABASES = ... declaration to look like this:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'postgres',
        'USER': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}

You can see we are just selecting postgres as our database engine and then pointing it at host db which, thanks to our nifty DNS settings, will point to the db container.

Now we can try and run the django command syncdb which will take your django models and update the database to reflect them.

docker-compose run web python manage.py syncdb

If it asks you about a superuser just say no, we’re not worried about what this command is doing as it will be lost next time we run docker-compose anyway. We’re just testing the database connection.

Finally you can start the containers.

docker-compose up

Refresh your EC2 instance’s page in your browser and you should now see the default django test page.

Django test page

Conclusion

Congratulations, you are now working with docker. Hopefully you can see the power provided by adding this little bit of overhead to your applications. We’ve only scratched the surface here so I suggest you read as much as you can about docker, starting with the links below.

AppCatalyst & Docker Lab

 Hệ thống  Comments Off on AppCatalyst & Docker Lab
Nov 152015
 

“VMware AppCatalyst is an API and Command Line Interface (CLI)-driven Mac hypervisor that is purpose-built for developers, with the goal of bringing the datacenter to the desktop.”

This Hands-on Lab will walk you through the installation of VMware AppCatalyst and Docker Basics including Docker Machine, Docker Compose, and Docker Swarm.
AppCatalyst is currently Tech Preview (as of Sept 21, 2015)

What’s covered?

  1. Docker Basics (Docker, Machine, Compose, Swarm)
  2. Installation (VMware App Catalyst, Docker, Machine, Compose, Swarm)
  3. Simple Docker Commands
  4. Working with Dockerfile
  5. Docker Compose
  6. Docker Swarm

What do you need for this LAB?

  • A Mac
  • Docker & AppCatalyst (Ch2 will tell you how to setup your Mac)

Before you Begin

What is Docker?

  • Docker Client
  • Docker Container
  • Docker Image
  • Docker Hub

docker-ppt-docker-basics-1.png

What is Docker Machine?

Machine lets you create Docker hosts on your computer, on cloud providers, and inside your own data center. It automatically creates hosts, installs Docker on them, then configures the docker client to talk to them. A “machine” is the combination of a Docker host and a configured client.

docker-ppt-machine-endpoints-1.png

What is Docker Compose?

Compose is a tool for defining and running multi-container applications with Docker. With Compose, you define a multi-container application in a single file, then spin your application up in a single command which does everything that needs to be done to get it running.

docker-ppt-compose-002.png

What is Docker Swarm?

Docker Swarm is native clustering for Docker. It allows you create and access to a pool of Docker hosts using the full suite of Docker tools.

docker-ppt-swarm-1.png

CREATING A DOCKER CONTAINER FOR HIPPO CMS

 Hệ thống, Linux  Comments Off on CREATING A DOCKER CONTAINER FOR HIPPO CMS
Nov 152015
 

Introduction

Don’t we all know  Docker by now? In short, Docker is a so called “container solution”, which you can use to create application containers. Such a container is constructed from a base image and provisioned with e.g. an application and its configuration files. Unlike VM-based solutions, where a guest operating system runs fully independent of the host operating system using a hypervisor, Docker does not rely on a hypervisor to start your containers. Instead, Docker containers are managed by the Docker Engine. As Docker itself best  describes it, the Docker Engine runs your containers as an isolated process in userspace on the host operating system, sharing the kernel with other containers. Thus, it enjoys the resource isolation and allocation benefits of VMs but is much more portable and efficient.

Hippo provides a nice demonstration of Hippo CMS by providing the GoGreen community and enterprise demo. This lab will focus on creating a Docker image, containing a fully functional Hippo GoGreen demonstration suite (or any other Hippo CMS implementation). I’ll be focusing on developing, shipping and running a Docker image for Hippo GoGreen.

Lab prerequisites

To get started with this lab, make sure you’ve installed Docker on your local system. To install Docker, follow the installation instructions provided by Docker. Also, make sure the the Docker Engine is started (check your system services)!

Putting Hippo in a Docker container

Step 1 – Developing the Hippo CMS Docker image

First we need to create a Docker image, which is later on used to start a container. An image is described by a so called “Dockerfile”, providing a specification and bootstrap instructions for your container. The Dockerfile below covers all the necessary instructions to get Hippo GoGreen up and running in a container. In short:

  • the image is fired up using an Ubuntu 14.04 base image
  • the image is provisioned with some packages required to install and run Hippo CMS
  • Hippo CMS is retrieved from the \$HIPPO_URL and installed in the \$HIPPO_FOLDER in the image

Finally, we instruct Docker to expose port 8080 to the host system when an instance of this image is started, allowing us to interact with Hippo CMS. We bind the startup script of Apache Tomcat to be the default execution of this image, such that Hippo CMS is automatically started.

To proceed the lab, store the Dockerfile below in a work folder on your harddrive and save it as “Dockerfile”.

# Generic Hippo Docker image
FROM ubuntu:14.04
MAINTAINER Brian Snijders <[email protected]>

# Set environment variables
ENV PATH /srv/hippo/bin:$PATH
ENV HIPPO_FILE HippoCMS-GoGreen-Enterprise-7.9.4.zip
ENV HIPPO_FOLDER HippoCMS-GoGreen-Enterprise-7.9.4
ENV HIPPO_URL http://download.demo.onehippo.com/7.9.4/HippoCMS-GoGreen-Enterprise-7.9.4.zip

# Create the work directory for Hippo
RUN mkdir -p /srv/hippo

# Add Oracle Java Repositories
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common
RUN DEBIAN_FRONTEND=noninteractive add-apt-repository ppa:webupd8team/java
RUN DEBIAN_FRONTEND=noninteractive apt-get update

# Approve license conditions for headless operation
RUN echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
RUN echo debconf shared/accepted-oracle-license-v1-1 seen true | sudo debconf-set-selections

# Install packages required to install Hippo CMS
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y oracle-java7-installer
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y oracle-java7-set-default
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y curl
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y dos2unix
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y unzip

# Install Hippo CMS, retrieving the GoGreen demonstration from the $HIPPO_URL and putting it under $HIPPO_FOLDER
RUN curl -L $HIPPO_URL -o $HIPPO_FILE
RUN unzip $HIPPO_FILE
RUN mv /$HIPPO_FOLDER/tomcat/* /srv/hippo
RUN chmod 700 /srv/hippo/* -R

# Replace DOS line breaks on Apache Tomcat scripts, to properly load JAVA_OPTS
RUN dos2unix /srv/hippo/bin/setenv.sh
RUN dos2unix /srv/hippo/bin/catalina.sh

# Expose ports
EXPOSE 8080

# Start Hippo
WORKDIR /srv/hippo/
CMD ["catalina.sh", "run"]

Step 2 – Creating the Hippo CMS Docker image

Having the Dockerfile in your work folder, we instruct the Docker Engine to actually create an image out of it. In the same folder where our Dockerfile resides, we execute the following command, requiring root level access:

sudo docker build -t=”hippo/demo” .

This tells the Docker Engine to create a new image from the Dockerfile in your work folder and store it in the (local) Docker image repository with “hippo/demo” as its image tag.

Step 3 – Running the Hippo CMS image in a new container

Allright, we’ve described the image source and created an actual image which is stored in our Docker image repository. Let’s fire up a new container bootstrapped from this image, by executing the following command, requiring root level access:

sudo docker run -p 8080:8080 hippo/demo

This tells the Docker Engine to start a new container using the “hippo/demo” image from the local image repository, redirecting port 8080 on the host system to port 8080 in the container.

Step 4 – Observe Docker running your Hippo GoGreen

After startup has completed, we can use Hippo CMS from the host system as if it were installed   on the host system. Since we’ve exposed 8080 in the container (remember your Dockerfile), and we’ve instructed Docker to redirect port 8080 (Step 3), the host system its port 8080 is bound to the Apache Tomcat in the container.

Thus, to use Hippo CMS from the host system, browse to   http://localhost:8080/site and  http://localhost:8080/cms and observe GoGreen from your host system, running in a Docker container!

Conclusion

This lab illustrated that Docker, as a container solution, can be used to create a lightweight and portable Hippo GoGreen demonstration container. One of the advantages is that we can create multiple images from the same Dockerfile. This can come in quite handy if you’re up for multiple demonstrations, requiring multiple GoGreen instances. Especially in concurrent demonstration cycles, where each instance requires a different setup or content, Docker simplifies the process of setting up multiple GoGreen’s. Also it provides a way to easily get a portable version of GoGreen up and running quite fast.

Hopefully I’ve showed you how easy it is to wrap Hippo CMS in a Docker image and use it to your needs. Next step would be for Hippo to start providing out-of-the-box Docker images :).

[Docker]- Part3 – Cài đặt Docker trên Ubuntu 14.04

 Linux  Comments Off on [Docker]- Part3 – Cài đặt Docker trên Ubuntu 14.04
Nov 112015
 

Tiếp theo loạt bài về Docker, mình sẽ hướng dẫn các bạn các cài đặt Docker trên Ubuntu 14.04 (vì mình sử dụng phiên bản này).

1, Đăng nhập vào user có thể thực thi câu lệnh sudo với quyền admin.

2, Kiểm tra xem curl đã được cài đặt hay chưa

$ which curl

nếu curl chưa được cài đặt, thì thực thi câu lệnh sau để cài đặt nó.

$ sudo apt-get update

$ sudo apt-get install curl

3, Tải về phiên bản mới nhất của Docker Package.

$ curl -sSL https://get.docker.com/ | sh

Thực thi câu lệnh, nếu hệ điều hành yêu cầu quyền admin. Thì hãy thực thi nó với câu lệnh sudo.

Nếu bị lỗi về proxy, hãy thực thi câu lệnh này trước khi thực hiện câu lệnh trên.

$ curl -sSL https://get.docker.com/gpg | sudo apt-key add

4, Kiểm tra xem docker đã được cài đặt một cách hoàn chỉnh chưa.

$ sudo docker run hello-world

Hệ thống sẽ trả về nội dung của gói helloworld trên màn hình.
Chúc bạn thành công

Phần tiếp theo của loạt bài về docker sẽ được cập nhật tại đây.

[Docker] – Part 2 – Cấu trúc và quy trình hoạt động của Docker.

 Linux  Comments Off on [Docker] – Part 2 – Cấu trúc và quy trình hoạt động của Docker.
Nov 112015
 

1, Đặt vấn đề

Ở phần 1 mình và các bạn đã cùng có cái nhìn tổng quan nhất về Docker. Tiếp tục loạt bài về Docker, mình và các bạn sẽ cùng tìm hiểu về cấu trúc và hoạt động của Docker.

Trong bài viết này chúng ta sẽ cùng trả lời những câu hỏi như:

  • Docker gồm những thành phần nào ?
  • Các thành phần trong Docker tương tác với nhau như thế nào ?
  • Hoạt động của Docker ra sao ?
  • Docker khởi chạy những ứng dụng như thế nào ?
  • …………………………………………….

2, Cấu tạo của Docker

docker-diagram

Như trong hình , Docker đóng gói các ứng dụng cùng thư viện đi kèm với nó thành các Docker Container. Dưới dự hỗ trợ của Docker Engine, các Container này có thể lấy được tài nguyên từ phần cứng và khởi chạy các ứng dụng trong các Container.

3, Ưu điểm hình thức đóng gói thành Container.

Việc đóng gói thành các container này có thể giải quyết được nhiều vấn đề mà ta chưa đề cập tới.

  • Ví dụ như trước kia ta không thể dùng chung Port, thì ở đây 2 ứng dụng với 2 container khác nhau. Ta có thể cấu hình Port trùng nhau cho ứng dụng này.
  • Tiếp theo là về việc quản lí phiên bản. Ta khó có thể cài 2 phiên bản của  1 phần mềm trên cùng 1 máy hypervisor. Tuy nhiên với Container, ta có thể cài mỗi phiên bản trên 1 Container và chạy một cách trơn tru.
  • Khả năng khởi động nhanh của Docker cũng là một lợi thế rất lớn.
  • Tiếp theo nói về tài nguyên, Docker sẽ ngốn ít tài nguyên hơn các máy hypervisor.
  • ……………….

Còn còn nhiều nữa những ưu điểm, các bạn hãy cài đặt và trải nghiệm Docker để thấy những ưu việt của nó nhé.

4, Quy trình thực thi của một hệ thống sử dụng Docker.

basics-of-docker-system

Như trong hình vẽ, một hệ thống Docker được thực thi với 3 bước chính :

Build -> Push -> Pull,Run

Mình sẽ lần lượt trình bày từng bước để chúng ta có thể hiểu rõ hơn về nguyên lí 3 bước này.

a, Build.

Đầu tiên chúng ta sẽ tạo một dockerfile, trong dockerfile này chính là code của chúng ta.

Dockerfile này sẽ được Build tại một máy tính đã cài đặt Docker Engine.

Sau khi build ta sẽ thu được Container, trong Container này chứa bộ thư viện và ứng dụng của chúng ta.

b, Push.

Sau khi có được Container, chúng ta thực hiện push Container này lên đám mây và lưu trữ ở đó.

Việc push này có thể thực hiện qua môi trường mạng Internet.

c, Pull, Run

Giả sử một máy tính muốn sử dụng Container chúng ta đã push lên đám mây (máy đã cài Docker Engine) thì bắt buộc máy phải thực hiện việc Pull container này về máy. Sau đó thực hiện Run Container này.

Đó chính là quy trình 3 bước miêu tả hoạt động của một hệ thống sử dụng Docker. Rất đơn giản và rõ ràng.

Trong bài kì tới, chúng ta sẽ cùng cài đặt và chạy Docker.