Docker Registry là một công cụ quan trọng giúp quản lý và phân phối các hình ảnh container Docker. Nó tập trung các Docker images lại với nhau, giúp giảm thiểu thời gian xây dựng (build) cho các nhà phát triển. Các Docker images cung cấp môi trường chạy ổn định nhờ vào khả năng ảo hóa, tuy nhiên, quá trình xây dựng image có thể mất thời gian. Thay vì phải cài đặt từng phụ thuộc và gói phần mềm, nhà phát triển có thể dễ dàng tải xuống một Docker image nén từ registry chứa tất cả các thành phần cần thiết. Hơn nữa, Docker cho phép tự động đẩy các image lên registry thông qua các công cụ tích hợp liên tục (CI) như TravisCI, giúp việc cập nhật image trở nên mượt mà trong suốt quá trình phát triển và triển khai.
Yêu cầu tiên quyết
Trước khi bắt đầu hướng dẫn này, bạn cần chuẩn bị các điều sau:
● Hai máy chủ Ubuntu 18.04 được thiết lập theo hướng dẫn “Thiết Lập Máy Chủ Ban Đầu với Ubuntu 18.04“, bao gồm một người dùng sudo không phải root và một tường lửa (Firewall). Một máy chủ sẽ lưu trữ Docker Registry riêng tư của bạn và máy chủ còn lại sẽ là máy khách.
● Docker và Docker-Compose được cài đặt trên cả hai máy chủ theo hướng dẫn “Cách cài đặt Docker Compose trên Ubuntu 18.04“. Bạn chỉ cần hoàn thành bước đầu tiên của bài hướng dẫn này để cài đặt Docker Compose. Hướng dẫn này cũng giải thích cách cài đặt Docker như một phần của các điều kiện tiên quyết.
● Nginx được cài đặt trên máy chủ lưu trữ Docker Registry riêng tư của bạn theo hướng dẫn “Cách cài đặt Nginx trên Ubuntu 18.04“.
● Nginx được bảo mật bằng Let’s Encrypt trên máy chủ cho Docker Registry riêng tư của bạn, theo hướng dẫn “Cách bảo mật Nginx với Let’s Encrypt trên Ubuntu 18.04“. Hãy chắc chắn chuyển hướng tất cả lưu lượng truy cập từ HTTP sang HTTPS ở Bước 4.
● Một tên miền trỏ về máy chủ bạn sử dụng cho Docker Registry riêng tư. Bạn sẽ thiết lập tên miền này trong quá trình thực hiện yêu cầu của Let’s Encrypt.
Bước 1 – Cài đặt và cấu hình Docker Registry
Công cụ dòng lệnh Docker rất hữu ích để khởi động và quản lý một hoặc hai container Docker, nhưng đối với việc triển khai đầy đủ, hầu hết các ứng dụng chạy bên trong container Docker đòi hỏi phải có các thành phần khác hoạt động song song. Ví dụ, nhiều ứng dụng web bao gồm một máy chủ web như Nginx phục vụ mã nguồn của ứng dụng, một ngôn ngữ kịch bản thông dịch như PHP, và một máy chủ cơ sở dữ liệu như MySQL.
Với Docker Compose, bạn có thể viết một file .yml
để thiết lập cấu hình cho từng container và cung cấp thông tin mà các container cần để giao tiếp với nhau. Sau đó, bạn có thể sử dụng công cụ dòng lệnh docker-compose
để gửi lệnh cho tất cả các thành phần tạo nên ứng dụng của bạn.
Docker Registry là một ứng dụng gồm nhiều thành phần, do đó bạn sẽ sử dụng Docker Compose để quản lý cấu hình. Để khởi động một instance của registry, bạn sẽ tạo file docker-compose.yml
để định nghĩa vị trí lưu trữ dữ liệu cho registry.
Trên máy chủ đã được tạo để lưu trữ Docker Registry riêng tư, bạn tạo thư mục docker-registry
, chuyển vào thư mục đó, sau đó tạo thư mục data
bằng các lệnh sau:
mkdir ~/docker-registry && cd $_ mkdir data
Sử dụng trình soạn thảo văn bản của bạn để tạo file cấu hình docker-compose.yml
:
nano docker-compose.yml
Thêm nội dung sau vào file, mô tả cấu hình cơ bản cho Docker Registry:
docker-compose.yml version: '3' services: registry: image: registry:2 ports: - "5000:5000" environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data
Phần environment
đặt một biến môi trường trong container Docker Registry với đường dẫn /data
. Ứng dụng Docker Registry sẽ kiểm tra biến môi trường này khi khởi động, từ đó bắt đầu lưu dữ liệu vào thư mục /data
.
Tuy nhiên, vì bạn đã thêm dòng volumes: - ./data:/data
, Docker sẽ ánh xạ thư mục /data
trong container với thư mục /data
trên máy chủ registry. Kết quả cuối cùng là toàn bộ dữ liệu của Docker Registry được lưu trữ tại ~/docker-registry/data
trên máy chủ.
Phần ports
với cấu hình 5000:5000
báo cho Docker ánh xạ cổng 5000 trên máy chủ đến cổng 5000 trong container đang chạy. Điều này cho phép bạn gửi yêu cầu đến cổng 5000 trên máy chủ, và yêu cầu sẽ được chuyển tiếp tới ứng dụng registry.
Bây giờ bạn có thể khởi động Docker Compose để kiểm tra cấu hình:
docker-compose up
Bạn sẽ thấy các thanh tiến trình tải xuống (download bars) cho thấy Docker đang tải image Docker Registry từ registry của Docker. Trong vòng một vài phút, bạn sẽ thấy đầu ra (output) trông giống như sau (các phiên bản có thể khác nhau):
Output of docker-compose up Starting docker-registry_registry_1 ... done Attaching to docker-registry_registry_1 registry_1 | time="2018-11-06T18:43:09Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2 registry_1 | time="2018-11-06T18:43:09Z" level=info msg="redis not configured" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2 registry_1 | time="2018-11-06T18:43:09Z" level=info msg="Starting upload purge in 20m0s" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2 registry_1 | time="2018-11-06T18:43:09Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2 registry_1 | time="2018-11-06T18:43:09Z" level=info msg="listening on [::]:5000" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
Bạn sẽ xử lý thông báo cảnh báo No HTTP secret provided
ở phần sau của bài hướng dẫn này. Đầu ra cho thấy container đang khởi động. Dòng cuối cùng của đầu ra cho biết container đã bắt đầu lắng nghe thành công trên cổng 5000
.
Theo mặc định, Docker Compose sẽ chờ lệnh từ bạn, vì vậy hãy nhấn CTRL+C
để tắt container Docker Registry.
Bạn đã thiết lập một Docker Registry đầy đủ đang lắng nghe trên cổng 5000
. Tại thời điểm này, registry sẽ không khởi động nếu bạn không bật nó lên thủ công. Ngoài ra, Docker Registry không có cơ chế xác thực tích hợp, nên hiện tại nó chưa được bảo mật và hoàn toàn mở cho công chúng. Trong các bước tiếp theo, bạn sẽ khắc phục các vấn đề về bảo mật này.
Bước 2 – Cấu hình chuyển tiếp cổng cho Nginx
Bạn đã thiết lập HTTPS trên máy chủ Docker Registry với Nginx, điều này có nghĩa là giờ đây bạn có thể cấu hình chuyển tiếp cổng từ Nginx đến cổng 5000. Sau khi hoàn thành bước này, bạn có thể truy cập registry của mình trực tiếp tại example.com
.
Trong khuôn khổ của yêu cầu Cách bảo mật Nginx với Let’s Encrypt trên Ubuntu 18.04, bạn đã thiết lập file cấu hình máy chủ /etc/nginx/sites-available/example.com
.
Mở file này bằng trình soạn thảo văn bản:
sudo nano /etc/nginx/sites-available/example.com
Tìm dòng location
hiện có. Nó sẽ trông giống như sau:
/etc/nginx/sites-available/example.com ... location / { ... } ...
Tiếp theo, bạn cần chuyển tiếp lưu lượng truy cập tới cổng 5000, nơi registry của bạn đang chạy. Đồng thời, bạn muốn đính kèm thêm các header vào yêu cầu gửi tới registry để cung cấp thông tin bổ sung từ máy chủ trong mỗi yêu cầu và phản hồi. Xóa nội dung trong phần location
hiện có, sau đó thêm nội dung sau vào phần đó:
/etc/nginx/sites-available/example.com ... location / { # Do not allow connections from docker 1.5 and earlier # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) { return 404; } proxy_pass http://localhost:5000; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; } ...
Lưu file và thoát ra. Áp dụng thay đổi bằng cách khởi động lại Nginx:
sudo service nginx restart
Bạn có thể xác nhận rằng Nginx đang chuyển tiếp lưu lượng truy cập tới cổng 5000 bằng cách khởi động registry:
cd ~/docker-registry docker-compose up
Mở trình duyệt và truy cập URL:
https://example.com/v2
Bạn sẽ thấy một đối tượng JSON rỗng, hoặc:
{}
Trong terminal, bạn sẽ thấy đầu ra tương tự như sau:
Output of docker-compose up registry_1 | time="2018-11-07T17:57:42Z" level=info msg="response completed" go.version=go1.7.6 http.request.host=cornellappdev.com http.request.id=a8f5984e-15e3-4946-9c40-d71f8557652f http.request.method=GET http.request.remoteaddr=128.84.125.58 http.request.uri="/v2/" http.request.useragent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" http.response.contenttype="application/json; charset=utf-8" http.response.duration=2.125995ms http.response.status=200 http.response.written=2 instance.id=3093e5ab-5715-42bc-808e-73f310848860 version=v2.6.2
Dòng cuối cùng cho thấy một yêu cầu GET được gửi tới /v2/
, endpoint mà bạn truy cập từ trình duyệt. Container nhận được yêu cầu từ quá trình chuyển tiếp cổng và trả về phản hồi {}
. Mã 200 ở cuối đầu ra có nghĩa là container đã xử lý yêu cầu thành công.
Giờ đây, khi bạn đã thiết lập chuyển tiếp cổng, bạn có thể tiếp tục nâng cao bảo mật cho registry của mình.
Bước 3 – Thiết lập xác thực cho Docker Registry
Sau khi Nginx chuyển tiếp yêu cầu một cách chính xác, bạn có thể bảo mật registry bằng xác thực HTTP để kiểm soát ai có quyền truy cập Docker Registry. Để làm được điều này, bạn sẽ tạo một file xác thực bằng htpasswd và thêm người dùng vào file đó. Xác thực HTTP dễ dàng thiết lập và an toàn khi kết hợp với HTTPS, giao thức mà registry của bạn sử dụng.
Cài đặt gói htpasswd bằng lệnh sau:
sudo apt install apache2-utils
Tiếp theo, tạo thư mục để lưu trữ thông tin xác thực, và chuyển vào thư mục đó. Lệnh $_
mở rộng thành đối số cuối của lệnh trước, trong trường hợp này là ~/docker-registry/auth
:
mkdir ~/docker-registry/auth && cd $_
Tiếp theo, tạo người dùng đầu tiên bằng lệnh dưới đây, thay thế username
bằng tên người dùng bạn muốn sử dụng. Tham số -B
chỉ định mã hóa bcrypt, an toàn hơn so với phương thức mặc định. Nhập mật khẩu khi được yêu cầu:
htpasswd -Bc registry.password username
Lưu ý: Để thêm nhiều người dùng, hãy chạy lại lệnh trên mà không cần tham số
-c
(chữ c có nghĩa là tạo mới):htpasswd registry.password username
Tiếp theo, bạn sẽ chỉnh sửa file docker-compose.yml để chỉ định Docker sử dụng file xác thực bạn vừa tạo.
cd ~/docker-registry nano docker-compose.yml
Thêm các biến môi trường và volume cho thư mục auth
mà bạn đã tạo, bằng cách chỉnh sửa file docker-compose.yml như sau:
docker-compose.yml version: '3' services: registry: image: registry:2 ports: - "5000:5000" environment: REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./auth:/auth - ./data:/data
Ở biến REGISTRY_AUTH
, bạn đã chỉ định sử dụng phương thức xác thực htpasswd, và đặt REGISTRY_AUTH_HTPASSWD_PATH
là đường dẫn tới file xác thực. Cuối cùng, REGISTRY_AUTH_HTPASSWD_REALM
đại diện cho tên realm của htpasswd
.
Bây giờ, bạn có thể kiểm tra xem xác thực hoạt động đúng chưa bằng cách khởi động registry và xem nó yêu cầu người dùng nhập tên đăng nhập và mật khẩu.
docker-compose up
Mở trình duyệt và truy cập https://example.com/v2
.
Sau khi nhập đúng tên đăng nhập và mật khẩu, bạn sẽ thấy {}
xuất hiện trở lại. Bạn đã xác nhận thành công thiết lập xác thực cơ bản: registry chỉ trả kết quả sau khi nhận đúng tên đăng nhập và mật khẩu. Bây giờ, registry của bạn đã được bảo mật và sẵn sàng sử dụng.
ChatGPT đã nói:
Bạn muốn đảm bảo registry của mình sẽ tự động khởi động khi hệ thống khởi động. Trong trường hợp có sự cố hệ thống, bạn muốn registry tự khởi động lại khi máy chủ khởi động. Mở file docker-compose.yml
:
nano docker-compose.yml
Thêm dòng sau vào dưới mục registry:
:
docker-compose.yml ... registry: restart: always ...
Bạn có thể khởi động registry dưới dạng tiến trình nền, cho phép bạn thoát phiên SSH mà tiến trình vẫn tiếp tục chạy:
docker-compose up -d
Với registry chạy nền, bạn đã có thể chuẩn bị Nginx cho việc tải file lên.
Bước 5 – Tăng kích thước tệp tải lên cho Nginx
Trước khi bạn có thể đẩy một image lên registry, cần đảm bảo registry có khả năng xử lý các file tải lên lớn. Mặc dù Docker tách các tải lên image lớn thành các layer riêng biệt, nhưng tổng kích thước có thể vượt quá 1GB. Theo mặc định, Nginx giới hạn kích thước tải lên chỉ 1MB, vì vậy bạn cần chỉnh sửa file cấu hình Nginx và đặt giới hạn kích thước tải lên thành 2GB.
Mở file cấu hình Nginx:
sudo nano /etc/nginx/nginx.conf
Tìm phần http
, và thêm dòng sau:
/etc/nginx/nginx.conf ... http { client_max_body_size 2000M; ... } ...
Cuối cùng, khởi động lại Nginx để áp dụng thay đổi cấu hình:
sudo service nginx restart
Bây giờ, bạn có thể tải lên các image lớn tới Docker Registry mà không gặp lỗi từ Nginx.
Bước 6 – Đẩy hình ảnh Docker lên Docker Registry riêng tư
Giờ đây, bạn đã sẵn sàng để xuất bản một image lên Docker Registry riêng tư của mình, nhưng trước tiên bạn phải tạo ra một image. Trong bài hướng dẫn này, bạn sẽ tạo một image đơn giản dựa trên image ubuntu từ Docker Hub. Docker Hub là một registry công cộng với nhiều image được cấu hình sẵn, giúp nhanh chóng Dockerize các ứng dụng. Sử dụng image ubuntu, bạn sẽ kiểm tra việc đẩy (push) và kéo (pull) từ registry của mình.
Từ máy chủ khách, tạo một image nhỏ, trống để đẩy lên registry mới, với cờ -i
và -t
cho phép bạn truy cập shell tương tác vào container:
docker run -t -i ubuntu /bin/bash
Sau khi tải xong, bạn sẽ vào prompt của Docker. Lưu ý rằng ID container của bạn sau root@
có thể khác nhau. Thực hiện một thay đổi nhanh trên hệ thống tập tin bằng cách tạo một file có tên SUCCESS
. Ở bước tiếp theo, bạn sẽ sử dụng file này để xác định xem quá trình xuất bản có thành công hay không:
touch /SUCCESS
Thoát khỏi container Docker:
exit
Lệnh sau tạo ra một image mới có tên test-image
dựa trên image vừa chạy kèm theo những thay đổi bạn đã thực hiện (trong trường hợp này là file /SUCCESS
đã được thêm vào image):
docker commit $(docker ps -lq) test-image
Đến thời điểm này, image chỉ tồn tại cục bộ. Bây giờ, bạn có thể đẩy (push) nó lên registry mới mà bạn đã tạo. Đăng nhập vào Docker Registry:
docker login https://example.com
Nhập tên đăng nhập và mật khẩu đã tạo từ trước. Tiếp theo, bạn sẽ gán nhãn (tag) cho image với vị trí của registry riêng tư để có thể đẩy lên:
docker tag test-image example.com/test-image
Đẩy image đã được gán nhãn lên registry:
docker push example.com/test-image
Đầu ra của lệnh push sẽ trông tương tự như sau:
Output The push refers to a repository [example.com/test-image] e3fbbfb44187: Pushed 5f70bf18a086: Pushed a3b5c80a4eba: Pushed 7f18b442972b: Pushed 3ce512daaf78: Pushed 7aae4540b42d: Pushed ...
Bạn đã xác nhận rằng registry của bạn xử lý việc xác thực người dùng, và cho phép người dùng đã xác thực đẩy image lên registry. Tiếp theo, bạn sẽ kiểm tra xem có thể kéo image từ registry hay không.
Bước 7 – Kéo hình ảnh từ Docker Registry riêng tưy
Quay trở lại máy chủ registry để kiểm tra việc kéo (pull) image từ máy khách. Ngoài ra, cũng có thể kiểm tra từ một máy chủ thứ ba.
Đăng nhập bằng tên đăng nhập và mật khẩu đã thiết lập trước đó:
docker login https://example.com
Bây giờ, bạn đã sẵn sàng kéo image. Sử dụng tên miền và tên image đã được gán nhãn ở bước trước:
docker pull example.com/test-image
Docker sẽ tải image về và trả về prompt. Nếu bạn chạy image trên máy chủ registry, bạn sẽ thấy file SUCCESS
mà bạn đã tạo trước đó xuất hiện:
docker run -it example.com/test-image /bin/bash
Trong shell bash, liệt kê các tập tin:
ls
Bạn sẽ thấy file SUCCESS
cùng với các thư mục khác:
SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
Bạn đã hoàn thành việc thiết lập một registry an toàn, cho phép người dùng đẩy và kéo các image tùy chỉnh.
Kết luận
Trong bài hướng dẫn này, bạn đã hoàn thành việc thiết lập Docker Registry riêng tư và xuất bản một Docker image. Như đã đề cập trong phần giới thiệu, bạn cũng có thể sử dụng TravisCI hoặc các công cụ CI tương tự để tự động đẩy Docker images lên registry riêng tư của mình. Việc tích hợp Docker và các registry vào quy trình làm việc giúp đảm bảo rằng image chứa mã nguồn sẽ hoạt động một cách nhất quán trên mọi máy chủ, bất kể là môi trường sản xuất hay phát triển. Để hiểu rõ hơn về cách viết Dockerfile và quy trình tạo Docker images, bạn có thể tham khảo bài hướng dẫn chính thức của Docker để tìm hiểu chi tiết về các bước thực hiện.