Computer >> Máy Tính >  >> Phần mềm >> Máy ảo

Hướng dẫn giới thiệu kỹ lưỡng về bộ chứa Docker

Hãy để tôi bắt đầu với một lời hứa lớn. Bạn sẽ hoàn toàn YÊU bài viết này ngày hôm nay. Nó sẽ dài, chi tiết và rất hữu ích. Hãy nghĩ đến GRUB, GRUB2. Điều tương tự ở đây. Chỉ có chúng tôi mới giải quyết được Docker, một nền tảng phân phối tuyệt vời bao bọc công nghệ Bộ chứa Linux (LXC) theo cách đơn giản, thuận tiện.

Tôi sẽ chỉ cho bạn cách bắt đầu, sau đó chúng ta sẽ tạo các thùng chứa của riêng mình với SSH và Apache, tìm hiểu cách sử dụng Dockerfiles, hiển thị các cổng dịch vụ và giải quyết vô số lỗi và sự cố nhỏ thường không bao giờ được giải quyết trong các diễn đàn công khai . Xin vui lòng, không cần phải quảng cáo thêm, hãy theo tôi.


Mục lục


  1. Giới thiệu
  2. Triển khai docker
  3. Bắt đầu
  4. Các lệnh docker
  5. Kéo hình ảnh
  6. Bắt đầu bộ chứa Docker
  7. Cài đặt Apache &SSH
    1. Bắt đầu dịch vụ
    2. Dịch vụ Apache
    3. Dịch vụ SSH
  8. Kiểm tra xem máy chủ Web có hoạt động không
    1. Hiển thị các cổng đến
    2. Kiểm tra địa chỉ IP
    3. Kiểm tra cấu hình mới
  9. Kiểm tra xem SSH có hoạt động không
    1. Đợi đã, mật khẩu gốc là gì?
  10. Cam kết hình ảnh
  11. Dockerfile
    1. Xây dựng hình ảnh
    2. Ảnh thử nghiệm
  12. Bản dựng thay thế
    1. SAO CHÉP hướng dẫn
  13. Ưu điểm của container
  14. Các sự cố bạn có thể gặp phải &khắc phục sự cố
  15. Các lệnh bổ sung
    1. Sự khác nhau giữa exec và attachment
    2. Sự khác biệt giữa bắt đầu và chạy
    3. Sự khác biệt giữa tạo và tạo
  16. Đây mới chỉ là bắt đầu...
  17. Đọc thêm
  18. Kết luận

Giới thiệu

Tôi đã đưa ra một cái nhìn tổng quan ngắn gọn về công nghệ trong một bài báo về Phần mềm miễn phí của Gizmo vào năm ngoái. Bây giờ, chúng ta sẽ nghiêm túc về việc sử dụng Docker. Đầu tiên, điều quan trọng cần nhớ là khung này cho phép bạn sử dụng LXC một cách thuận tiện mà không phải lo lắng về tất cả các chi tiết nhỏ. Đó là bước tiếp theo trong thế giới này, giống như cách OpenStack là bước tiến hóa tiếp theo trong thế giới ảo hóa. Hãy để tôi cung cấp cho bạn một số lịch sử và phép loại suy.

Ảo hóa bắt đầu với phần mềm cho phép bạn trừu tượng hóa phần cứng của mình. Sau đó, để làm cho mọi thứ nhanh hơn, các chương trình ảo hóa bắt đầu sử dụng khả năng tăng tốc phần cứng và sau đó bạn cũng có ảo hóa song song. Cuối cùng, các trình ảo hóa bắt đầu mọc lên như nấm sau mưa và việc cung cấp cũng như quản lý tất cả chúng trở nên hơi khó khăn. Đây là lý do cốt lõi cho các khái niệm như OpenStack, ẩn các nền tảng khác nhau dưới một API thống nhất.

Các thùng chứa bắt đầu theo cách tương tự. Đầu tiên, chúng tôi có chroot, nhưng các tiến trình chạy bên trong môi trường bị bỏ tù chia sẻ cùng một không gian tên và tranh giành cùng một tài nguyên. Sau đó, chúng tôi nhận được lệnh gọi hệ thống kexec, cho phép chúng tôi khởi động vào ngữ cảnh của một nhân khác mà không cần thông qua BIOS. Sau đó, các nhóm điều khiển xuất hiện, cho phép chúng tôi phân vùng các tài nguyên hệ thống như CPU, bộ nhớ và các nhóm khác thành các nhóm con, do đó cho phép kiểm soát tốt hơn, do đó có tên, các quy trình đang chạy trên hệ thống.

Sau đó, nhân Linux bắt đầu cung cấp cách ly hoàn toàn các tài nguyên, sử dụng các nhóm làm cơ chế phân vùng cơ bản. Về mặt kỹ thuật, đây là công nghệ ảo hóa cấp hệ thống, cho phép bạn chạy nhiều phiên bản của nhân đang chạy trên máy chủ điều khiển bên trong các môi trường độc lập, với phần thưởng bổ sung là rất ít chi phí và hình phạt hiệu suất.

Một số công nghệ cạnh tranh đã cố gắng cung cấp các giải pháp tương tự, chẳng hạn như OpenVZ, nhưng cộng đồng cuối cùng đã thu hẹp trọng tâm của nó vào khả năng hỗ trợ gốc bên trong nhân chính và đây có vẻ là hướng đi trong tương lai. Tuy nhiên, LXC vẫn hơi khó sử dụng, vì cần có một lượng kiến ​​thức kỹ thuật và viết kịch bản tương đối để các vùng chứa chạy.

Đây là nơi Docker phát huy tác dụng. Nó cố gắng loại bỏ những mảnh vụn và cung cấp một phương pháp đơn giản để sinh ra các phiên bản vùng chứa mới mà không phải lo lắng về phần phụ trợ của cơ sở hạ tầng. Vâng, gần như. Nhưng mức độ khó ít hơn nhiều.

Một ưu điểm mạnh khác của Docker là sự chấp nhận rộng rãi của cộng đồng, cũng như nhấn mạnh vào việc tích hợp với các dịch vụ đám mây. Ở đây chúng tôi chuyển sang từ thông dụng đầy đủ và điều này có nghĩa là đặt tên cho một số người chơi lớn như AWS, Hadoop, Azure, Jenkins và những người khác. Sau đó, chúng ta cũng có thể nói về Nền tảng dưới dạng Dịch vụ (Paas) và bạn có thể hình dung số tiền và trọng tâm mà dịch vụ này sẽ nhận được trong những năm tới. Bối cảnh công nghệ rất rộng lớn và khó hiểu, và nó chắc chắn sẽ tiếp tục thay đổi và phát triển, với ngày càng nhiều khái niệm và công nghệ trình bao bọc đi vào cuộc sống và xây dựng trên Docker.

Nhưng chúng tôi muốn tập trung vào khía cạnh công nghệ. Khi chúng tôi nắm vững kiến ​​thức cơ bản, chúng tôi sẽ dần dần mở rộng và bắt đầu sử dụng các khả năng tích hợp mạnh mẽ, tính linh hoạt của giải pháp và làm việc để làm cho kiến ​​thức chuyên môn về hệ sinh thái đám mây của chúng tôi trở nên đa dạng, tự động và thuần túy. Điều đó sẽ không xảy ra ngay bây giờ, nhưng tôi muốn giúp bạn điều hướng vài dặm đầu tiên, hay chúng ta có thể nói là hàng km, trên vùng nước khởi động đầy bùn, để bạn có thể bắt đầu sử dụng Docker một cách hợp lý, hiệu quả. Vì đây là một công nghệ non trẻ, nên nó là Miền Tây hoang dã và hầu hết các tài liệu, mẹo, hướng dẫn trực tuyến và những thứ khác đã lỗi thời, các phiên bản sao chép và dán không giúp ích được gì cho bất kỳ ai và phần lớn là không đầy đủ. Tôi muốn khắc phục điều đó ngày hôm nay.

Triển khai docker

Một chút công cụ nhàm chán hơn trước khi chúng tôi làm một số điều thú vị. Nhưng dù sao, Docker chủ yếu là về LXC, nhưng không chỉ. Nó được thiết kế để có thể mở rộng và nó cũng có thể giao tiếp với libvirt và systemd. Theo một cách nào đó, điều này làm cho nó gần giống như một siêu giám sát siêu giám sát, vì có tiềm năng phát triển trong tương lai và khi các mô-đun bổ sung được thêm vào, nó có thể thay thế hiệu quả các trình siêu giám sát cổ điển như Xen hoặc KVM hoặc bất kỳ thứ gì sử dụng libvirt và các bạn.

Đây là một hình ảnh phạm vi công cộng, nếu bạn tự hỏi.

Bắt đầu

Chúng tôi sẽ trình diễn bằng CentOS 7. Không phải Ubuntu. Hầu hết các nội dung trực tuyến đều tập trung vào Ubuntu, nhưng tôi muốn cho bạn thấy cách nó được thực hiện bằng cách sử dụng hương vị gần như doanh nghiệp nhất có thể của Linux, bởi vì nếu bạn định sử dụng Docker, nó sẽ ở đâu đó giống như doanh nghiệp. Việc đầu tiên là cài đặt docker:

yum cài đặt docker-io

Sau khi phần mềm được cài đặt, bạn có thể bắt đầu sử dụng nó. Tuy nhiên, bạn có thể gặp phải hai sự cố sau trong lần đầu tiên cố gắng chạy các lệnh docker:

docker
FATA[0000] Nhận https:///var/run/docker.sock/v1.18/images/json:dial unix /var/run/docker.sock:không có tệp hoặc thư mục như vậy. Bạn đang cố gắng kết nối với trình nền hỗ trợ TLS mà không có TLS?

Và lỗi khác là:

docker
FATA[0000] Nhận https:///var/run/docker.sock/v1.18/containers/json:dial unix /var/run/docker.sock:quyền bị từ chối. Bạn đang cố gắng kết nối với trình nền hỗ trợ TLS mà không có TLS?

Lý do là, trước tiên bạn cần khởi động dịch vụ Docker. Hơn nữa, bạn phải chạy công nghệ với quyền root, vì Docker cần quyền truy cập vào một số phần khá nhạy cảm của hệ thống và tương tác với kernel. Đó là cách nó hoạt động.

docker khởi động systemctl

Bây giờ chúng ta có thể bắt đầu sử dụng Docker.

Các lệnh docker

Điều cơ bản là chạy docker help để lấy danh sách các lệnh có sẵn. Tôi sẽ không đi qua tất cả các tùy chọn. Chúng tôi sẽ tìm hiểu thêm về họ khi chúng tôi đi cùng. Nói chung, nếu bạn còn nghi ngờ, bạn nên tham khảo tài liệu trực tuyến khá hay. Tài liệu tham khảo CLI hoàn chỉnh cũng rất tốt. Và sau đó, cũng có một cheat sheet xuất sắc trên GitHub. Nhưng nhiệm vụ đầu tiên của chúng tôi sẽ là tải xuống một hình ảnh Docker mới và sau đó chạy phiên bản đầu tiên của chúng tôi.

Kéo hình ảnh

Có rất nhiều hình ảnh có sẵn. Chúng tôi muốn thực hành với CentOS. Đây là một điểm khởi đầu tốt. Một kho lưu trữ chính thức có sẵn và nó liệt kê tất cả các hình ảnh và thẻ được hỗ trợ. Thật vậy, tại thời điểm này, chúng ta cần hiểu cách các hình ảnh Docker được gắn nhãn.

Quy ước đặt tên là repository:tag, ví dụ centos:latest. Nói cách khác, chúng tôi muốn hình ảnh CentOS mới nhất. Nhưng hình ảnh yêu cầu cũng có thể là centos:6.6. Được rồi, làm đi.

Bây giờ hãy liệt kê các hình ảnh bằng cách chạy lệnh docker images:

Bắt đầu bộ chứa Docker

Như chúng ta đã thấy trong hướng dẫn ban đầu của tôi, ví dụ đơn giản nhất là chạy shell:

docker run -ti centos:centos7 /bin/bash

vậy chúng ta có gì ở đây nào? Chúng tôi đang chạy một phiên bản vùng chứa mới với TTY (-t) và STDIN (-i) của riêng nó, từ hình ảnh CentOS 7, với trình bao BASH. Trong vài giây, bạn sẽ nhận được một vỏ mới bên trong thùng chứa. Bây giờ, nó là một hệ điều hành rất cơ bản, rất đơn giản, nhưng bạn có thể bắt đầu xây dựng mọi thứ bên trong nó.

Cài đặt Apache &SSH

Hãy thiết lập một máy chủ Web, máy chủ này cũng sẽ có quyền truy cập SSH. Để làm được điều này, chúng ta sẽ cần thực hiện một số cài đặt khá cơ bản. Lấy Apache (httpd) và SSHD (openssh-server) và định cấu hình chúng. Điều này không liên quan gì đến Docker, nhưng nó là một bài tập hữu ích.

Làm thế nào, một số bạn có thể phàn nàn, chờ đã, bạn không cần SSH bên trong một thùng chứa, đó là một rủi ro bảo mật và không có gì. Chà, có thể, có và không, tùy thuộc vào nhu cầu của bạn và mục đích sử dụng hộp đựng của bạn. Nhưng hãy để những cân nhắc về bảo mật sang một bên. Mục đích của bài tập là học cách thiết lập và chạy BẤT KỲ dịch vụ nào.

Bắt đầu dịch vụ

Bạn có thể muốn khởi động Apache của mình bằng tập lệnh init hoặc lệnh systemd. Điều này sẽ không hoàn toàn làm việc. Cụ thể đối với CentOS, nó đi kèm với systemd, nhưng quan trọng hơn, vùng chứa không có systemd riêng. Nếu bạn cố gắng, các lệnh sẽ thất bại.

systemctl bắt đầu httpd
Không nhận được kết nối D-Bus:Không có kết nối với trình quản lý dịch vụ.

Có nhiều thủ thuật xoay quanh vấn đề này và chúng ta sẽ tìm hiểu về một số thủ thuật này trong hướng dẫn sau. Nhưng nói chung, do tính chất nhẹ và đơn giản của các thùng chứa, bạn không thực sự cần một dịch vụ khởi động chính thức để chạy các quy trình của mình. Điều này không thêm một số phức tạp.

Dịch vụ Apache

Để chạy Apache (HTTPD), chỉ cần thực thi /usr/sbin/httpd - hoặc một lệnh tương đương trong bản phân phối của bạn. Dịch vụ sẽ bắt đầu, rất có thể kèm theo cảnh báo rằng bạn chưa định cấu hình chỉ thị Tên máy chủ của mình trong httpd.conf. Chúng tôi đã học cách thực hiện điều này trong hướng dẫn khá chi tiết về Apache của tôi.

/usr/sbin/httpd
AH00558:httpd:Không thể xác định một cách đáng tin cậy tên miền đủ điều kiện của máy chủ, sử dụng 172.17.0.4. Đặt chỉ thị 'Tên máy chủ' trên toàn cầu để chặn thông báo này

Dịch vụ SSH

Với SSHD, hãy chạy /usr/sbin/sshd.

/usr/sbin/sshd -f /etc/ssh/sshd_config
Không thể tải khóa máy chủ:/etc/ssh/ssh_host_rsa_key
Không thể tải khóa máy chủ:/etc/ssh/ssh_host_dsa_key
Không thể tải khóa máy chủ:/etc/ssh/ssh_host_ecdsa_key
Không thể tải khóa máy chủ:/etc/ssh/ssh_host_ed25519_key

Bạn cũng sẽ thất bại, bởi vì bạn sẽ không có tất cả các chìa khóa. Thông thường, các tập lệnh khởi động đảm nhận việc này, vì vậy bạn sẽ cần chạy lệnh ssh-keygen một lần trước khi dịch vụ khởi động chính xác. Một trong hai lệnh sẽ hoạt động:

/usr/bin/ssh-keygen -t rsa -f <đường dẫn đến tệp>

/usr/bin/ssh-keygen -A
ssh-keygen:tạo khóa máy chủ mới:RSA1 RSA DSA ECDSA ED25519

Kiểm tra xem máy chủ Web có hoạt động không

Bây giờ, bên trong vùng chứa, chúng ta có thể thấy rằng Apache thực sự đang chạy.

ps -ef|grep apache
apache      87    86  0 10:47 ? 00:00:00 /usr/sbin/httpd
apache      88    86  0 10:47 ? 00:00:00 /usr/sbin/httpd
apache      89    86  0 10:47 ? 00:00:00 /usr/sbin/httpd
apache      90    86  0 10:47 ? 00:00:00 /usr/sbin/httpd
apache      91    86  0 10:47 ? 00:00:00 /usr/sbin/httpd

Nhưng nếu chúng ta muốn kiểm tra kết nối bên ngoài thì sao? Tại thời điểm này, chúng tôi có một vài vấn đề trong tay. Một, chúng tôi chưa thiết lập bất kỳ cổng mở nào, có thể nói như vậy. Hai, chúng tôi không biết địa chỉ IP của vùng chứa của chúng tôi là gì. Bây giờ, nếu bạn cố chạy ifconfig bên trong trình bao BASH, bạn sẽ không đi đến đâu vì gói cần thiết chứa các lệnh mạng cơ bản chưa được cài đặt. Tốt, vì nó làm cho thùng chứa của chúng ta mỏng và an toàn.

Hiển thị các cổng đến

Giống như với bất kỳ máy chủ Web nào, chúng tôi sẽ cần cho phép các kết nối đến. Chúng tôi sẽ sử dụng cổng mặc định 80. Điều này không khác gì chuyển tiếp cổng trong bộ định tuyến của bạn, cho phép các chính sách tường lửa, v.v. Với Docker, có một số cách bạn có thể đạt được kết quả mong muốn.

Khi bắt đầu một vùng chứa mới bằng lệnh run, bạn có thể sử dụng tùy chọn -p để chỉ định cổng nào sẽ mở. Bạn có thể chọn một cổng hoặc nhiều cổng và bạn cũng có thể ánh xạ cả cổng máy chủ (hostPort) và cổng vùng chứa (containerPort). Ví dụ:

  • -p 80 sẽ hiển thị cổng vùng chứa 80. Cổng này sẽ tự động được ánh xạ tới một cổng ngẫu nhiên trên máy chủ. Chúng ta sẽ tìm hiểu sau về cách xác định đúng cổng.
  • -p 80:80 sẽ ánh xạ cổng bộ chứa tới cổng máy chủ 80. Điều này có nghĩa là bạn không cần biết địa chỉ IP nội bộ của bộ chứa. Có một yếu tố liên quan đến NAT nội bộ, đi qua giao diện ảo Docker. Chúng tôi sẽ thảo luận về điều này sớm. Hơn nữa, nếu bạn sử dụng phương pháp này, chỉ một vùng chứa duy nhất có thể liên kết với cổng 80. Nếu bạn muốn sử dụng nhiều máy chủ Web có địa chỉ IP khác nhau, bạn sẽ phải thiết lập mỗi máy chủ trên một cổng khác nhau.

docker run -ti -p 22:22 -p 80:80 image-1:latest
FATA[0000] Phản hồi lỗi từ trình nền:Không thể khởi động bộ chứa 64bd520e2d95a699156f5d40331d1aba972039c3c201a97268d61c6ed17e1619:Liên kết cho 0.0.0.0:80 không thành công:cổng đã được phân bổ

Có rất nhiều cân nhắc bổ sung. Chuyển tiếp IP, mạng cầu nối, mạng công cộng và mạng riêng, phạm vi mạng con, quy tắc tường lửa, cân bằng tải, v.v. Hiện tại, chúng ta không cần phải lo lắng về những điều này.

Ngoài ra còn có một phương pháp bổ sung về cách chúng ta có thể hiển thị cổng, nhưng chúng ta sẽ thảo luận về điều đó sau, khi chúng ta đề cập đến chủ đề Dockerfiles, là các mẫu để xây dựng hình ảnh mới. Hiện tại, chúng ta cần nhớ chạy hình ảnh của mình bằng tùy chọn -p.

Kiểm tra địa chỉ IP

Nếu bạn muốn để trống các cổng máy chủ của mình, thì bạn có thể bỏ qua phần hostPort. Trong trường hợp đó, bạn có thể kết nối trực tiếp với bộ chứa, sử dụng địa chỉ IP và cổng máy chủ Web của nó. Để làm điều đó, chúng ta cần tính chi tiết vùng chứa của mình:

docker kiểm tra

Điều này sẽ đưa ra một danh sách chi tiết rất dài, giống như cấu hình KVM XML, ngoại trừ cấu hình này được viết bằng JSON, đây là một định dạng dữ liệu hiện đại và xấu xí khác. Có thể đọc được nhưng cực kỳ xấu xí.

docker kiểm tra bị phân tâm_euclid
[{
"AppArmorProfile":"",
"Đối số":[],
"Cấu hình":{
"Đính kèmStderr":đúng,
"Đính kèmStdin":đúng,
"Đính kèmStdout":đúng,
"Cmd":[
"/bin/bash"
],
"Chia sẻ Cpu":0,
"Cpuset":"",
"Tên miền":"",
"Điểm vào":null,
"Env":[
...
"ExposedPorts":{
"80/tcp":{}
},
"Tên máy chủ":"43b179c5aec7",
"Hình ảnh":"centos:centos7",
"Nhãn":{},
"Địa chỉ MAC":"",
...

Chúng ta có thể thu hẹp nó xuống chỉ còn địa chỉ IP.

docker kiểm tra | grep -i "ipaddr"
"Địa chỉ IP":"172.17.0.20",

Kiểm tra cấu hình mới

Hãy bắt đầu mới. Khởi chạy một phiên bản mới, thiết lập Apache, khởi động nó. Mở trình duyệt Web và kiểm tra. Nếu nó hoạt động, thì bạn đã cấu hình đúng máy chủ Web của mình. Chính xác những gì chúng tôi muốn.

docker run -it -p 80:80 centos:centos7 /bin/bash

Nếu chúng tôi kiểm tra vùng chứa đang chạy, chúng tôi có thể thấy ánh xạ cổng - đầu ra được chia thành nhiều dòng cho ngắn gọn, vì vậy xin thứ lỗi cho điều đó. Thông thường, các tiêu đề toàn chữ hoa sẽ hiển thị dưới dạng tiêu đề hàng và sau đó, bạn sẽ nhận được tất cả phần còn lại được in bên dưới, một vùng chứa trên mỗi dòng.

# docker ps
CONTAINER ID        IMAGE               LỆNH
43b179c5aec7        centos:centos7      "/bin/bash"

TẠO TRẠNG THÁI             PORTS
2 giờ trước         Lên 2 giờ          0.0.0.0:80->80/tcp

NAMES              disted_euclid

Và trong trình duyệt, chúng tôi nhận được:

Tùy chọn:Bây giờ, dải địa chỉ IP nội bộ sẽ chỉ có thể truy cập được trên máy chủ lưu trữ. Nếu bạn muốn làm cho nó có thể truy cập được từ các máy khác, bạn sẽ cần chuyển tiếp NAT và IP. Và nếu bạn muốn sử dụng tên, thì bạn sẽ cần định cấu hình đúng /etc/hosts cũng như DNS. Đối với bộ chứa, điều này có thể được thực hiện bằng cách sử dụng chỉ thị --add-host="host:IP" khi chạy một phiên bản mới.

Một lưu ý khác:Hãy nhớ rằng Docker có mạng nội bộ của riêng nó, giống như VirtualBox và KVM, như chúng ta đã thấy trong các hướng dẫn khác của tôi. Đó là một mạng /16 khá rộng, vì vậy bạn có khá nhiều tự do. Trên máy chủ:

# /sbin/ifconfig
docker0:flags=4163  mtu 1500
inet 172.17.42.1  netmask 255.255.0.0  quảng bá 0.0.0.0
inet6 fe80::5484:7aff:fefe:9799  prefixlen 64  phạm vi 0x20
ether 56:84:7a:fe:97:99  txqueuelen 0  (Ethernet)
Gói RX 6199  byte 333408 (325,5 KiB)
Lỗi RX 0  rớt 0  tràn 0  khung hình 0
Gói TX 11037 byte 32736299 (31,2 MiB)
Lỗi TX 0  rớt 0 tràn 0  sóng mang 0  xung đột 0

Kiểm tra xem SSH có hoạt động không

Chúng ta cần thực hiện bài tập tương tự với SSH. Một lần nữa, điều này có nghĩa là để lộ cổng 22 và chúng tôi có sẵn một số tùy chọn. Để thú vị hơn, hãy thử gán cổng ngẫu nhiên:

docker run -ti -p 20 -p 80 centos:centos7 /bin/bash

Và nếu chúng tôi kiểm tra với docker ps, cụ thể là cho các cổng:

0.0.0.0:49176->22/tcp, 0.0.0.0:49177->80/tcp   Boring_mcclintock

Điều này có nghĩa là bạn có thể kết nối với địa chỉ IP docker0, các cổng như được chỉ định ở trên trong đầu ra lệnh docker ps và điều này tương đương với việc thực sự kết nối trực tiếp với IP của bộ chứa, trên cổng dịch vụ của chúng. Điều này có thể hữu ích vì bạn không cần phải lo lắng về địa chỉ IP nội bộ mà bộ chứa của bạn sử dụng và nó có thể đơn giản hóa việc chuyển tiếp. Bây giờ, hãy thử kết nối. Chúng tôi có thể sử dụng cổng máy chủ hoặc có thể sử dụng trực tiếp IP vùng chứa.

ssh 172.17.42.1 -p 49117

Dù bằng cách nào, chúng tôi sẽ nhận được những gì chúng tôi cần, ví dụ:

ssh 172.17.0.5
Không thể thiết lập tính xác thực của máy chủ '172.17.0.5 (172.17.0.5)'. Dấu vân tay khóa ECDSA là 00:4b:de:91:60:e5:22:cc:f7:89:01:19:3e:61:cb:ea.
Bạn có chắc chắn muốn tiếp tục kết nối (có/không)? Vâng
Cảnh báo:Đã thêm vĩnh viễn '172.17.0.5' (ECDSA) vào danh sách các máy chủ đã biết.
mật khẩu của root@172.17.0.5:

Đợi đã, mật khẩu gốc là gì?

Chúng tôi sẽ thất bại vì chúng tôi không có mật khẩu gốc. Vậy chúng ta làm gì bây giờ? Một lần nữa, chúng tôi có một số tùy chọn. Trước tiên, hãy thử thay đổi mật khẩu gốc bên trong vùng chứa bằng lệnh passwd. Nhưng điều này sẽ không hoạt động, vì tiện ích passwd chưa được cài đặt. Sau đó, chúng tôi có thể lấy RPM cần thiết và thiết lập nó bên trong vùng chứa. Trên máy chủ, kiểm tra các phụ thuộc:

rpm -q --whatprovides /etc/passwd
setup-2.8.71-5.el7.noarch

Nhưng đây là một lỗ hổng bảo mật. Chúng tôi muốn các thùng chứa của chúng tôi được gọn gàng. Vì vậy, chúng tôi chỉ có thể sao chép hàm băm mật khẩu từ /etc/shadow trên Máy chủ vào vùng chứa. Sau này, chúng ta sẽ tìm hiểu về một cách làm hợp lý hơn.

Một điều khác khá rõ ràng là chúng tôi đang lặp lại tất cả các hành động của mình. Điều này không hiệu quả và đây là lý do tại sao chúng tôi muốn giữ lại các thay đổi mà chúng tôi đã thực hiện đối với vùng chứa của mình. Phần tiếp theo xử lý đó.

Hình ảnh cam kết

Sau khi bạn đã thực hiện các thay đổi đối với vùng chứa, bạn có thể muốn cam kết nó. Nói cách khác, khi bắt đầu một vùng chứa mới sau này, bạn sẽ không cần lặp lại tất cả các bước từ đầu, bạn sẽ có thể sử dụng lại công việc hiện tại của mình, đồng thời tiết kiệm thời gian và băng thông. Bạn có thể cam kết một hình ảnh dựa trên ID hoặc bí danh của nó:

docker cam kết

Ví dụ:chúng tôi nhận được như sau:

docker cam kết 43b179c5aec7 myapache3
1ee373ea750434354faeb1cb70b0177b463c51c96c9816dcdf5562b4730dac54

Kiểm tra lại danh sách hình ảnh:

Dockerfile

A more streamlined way of creating your images is to use Dockerfiles. In a way, it's like using Makefile for compilation, only in Docker format. Or an RPM specfile if you will. Basically, in any one "build" directory, create a Dockerfile. We will learn what things we can put inside one, and why we want it for our Apache + SSH exercise. Then, we will build a new image from it. We can combine it with our committed images to preserve changes already done inside the container, like the installation of software, to make it faster and save network utilization.

Before we go any further, let's take a look at a Dockerfile that we will be using for our exercise. At the moment, the commands may not make much sense, but they soon will.

FROM myhttptest2:latest

EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]

EXPOSE 80

RUN mkdir -p /run/httpd
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]

What do we have here?

  • The FROM directory tells us what repo:tag to use as the baseline. In our case, it's one of the committed images that already contains the httpd and sshd binaries, SSH keys, and a bit more.
  • EXPOSE 22 - This line exposes port 22 inside the container. We can map it further using the -p option at runtime. The same is true for EXPOSE 80, which is relevant for the Web server.
  • CMD ["/usr/sbin/sshd", "-D"] - This instructions runs an executable, with optional arguments. It is as simple as that.
  • RUN mkdir -p /run/httpd - This instruction runs a command in a new layer on top of the base image - and COMMITS the results. This is very important to remember, as we will soon discuss what happens if you don't use the RUN mkdir thingie with Apache.
  • CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"] - We run the server, in the foreground. The last bit is optional, but for the time being, you can start Apache this way. Đủ tốt.

As you can see, Dockerfiles aren't that complex or difficult to write, but they are highly useful. You can pretty much add anything you want. Using these templates form a basis for automation, and with conditional logic, you can create all sorts of scenarios and spawn containers that match your requirements.

Build image

Once you have a Dockerfile in place, it's time to build a new image. Dockerfiles must follow a strict convention, just like Makefiles. It's best to keep different image builds in separate sub-directories. For example:

docker build -t test5 .
Sending build context to Docker daemon 41.47 kB
Sending build context to Docker daemon
Step 0 :FROM myapache4:latest
 ---> 7505c70235e6
Step 1 :EXPOSE 22 80
 ---> Using cache
 ---> 58f11217c3e3
Step 2 :CMD /usr/sbin/sshd -D
 ---> Using cache
 ---> 628c3d6b5399
Step 3 :RUN mkdir -p /run/httpd
 ---> Using cache
 ---> 5fc118f61a4d
Step 4 :CMD /usr/sbin/httpd -D FOREGROUND
 ---> Using cache
 ---> d892acd86198
Successfully built d892acd86198

The command tells us the following:-t repository name from a Dockerfile stored in the current directory (.). That's all. Very simple and elegant.

Test image

Run a new container from the created image. If everything went smoothly, you should have both SSH connectivity, as well as a running Web server in place. Again, all the usual network related rules apply.

Alternative build

Once you have the knowledge how do it on your own, you can try one of the official Apache builds. Indeed, the Docker repository contains a lot of good stuff, so you should definitely invest time checking available templates. For Apache, you only need the following in your Dockerfile - the second like is optional.

FROM httpd:2.4
COPY ./public-html/ /usr/local/apache2/htdocs/

COPY instruction

What do we have above? Basically, in the Dockerfile, we have the declaration what template to use. And then, we have a COPY instructions, which will look for a public-html directory in the current folder and copy it into the container during the build. In the same manner, you can also copy your httpd.conf file. Depending on your distribution, the paths and filenames might differ. Finally, after building the image and running the container:

docker run -ti -p 22 -p 80 image-1:latest
AH00558:httpd:Could not reliably determine the server's fully qualified domain name, using 172.17.0.17. Set the 'ServerName' directive globally to suppress this message
[Thu Apr 16 21:08:35.967670 2015] [mpm_event:notice] [pid 1:tid 140302870259584] AH00489:Apache/2.4.12 (Unix) configured -- resuming normal operations
[Thu Apr 16 21:08:35.976879 2015] [core:notice] [pid 1:tid 140302870259584] AH00094:Command line:'httpd -D FOREGROUND'


Advantages of containers

There are many good reasons why you want to use this technology. But let's just briefly focus on what we gain by running these tiny, isolated instances. Sure, there's a lot happening under the hood, in the kernel, but in general, the memory footprint of spawned containers is fairly small. In our case, the SSH + Apache containers use a tiny fraction of extra memory. Compare this to any virtualization technology.

Problems you may encounter &troubleshooting

Let's go back to the Apache example, and now you will also learn why so many online tutorials sin the sin of copy &pasting information without checking, and why most of the advice is not correct, unfortunately. It has to do with, what do you do if your Apache server seems to die within a second or two after launching the container? Indeed, if this happens, you want to step into the container and troubleshoot. To that end, you can use the docker exec command to attach a shell to the instance.

docker exec -ti boring_mcclintock /bin/bash

Then, it comes down to reading logs and trying to figure out what might have gone wrong. If your httpd.conf is configured correctly, you will have access and error logs under /var/log/httpd:

[auth_digest:error] [pid 25] (2)No such file or directory:AH01762:Failed to create shared memory segment on file /run/httpd/authdigest_shm.25

A typical problem is that you may be a missing /run/httpd directory. If this one does not exist in your container, httpd will start and die. Sounds so simple, but few if any reference mentions this.

While initially playing with containers, I did encounter this issue. Reading online, I found several suggestions, none of which really helped. But I do want to elaborate on them, and how you can make progress in your problem solving, even if intermediate steps aren't really useful.

Suggestion 1:You must use -D FOREGROUND to run Apache, and you must also use ENTRYPOINT rather than CMD. The difference between the two instructions is very subtle. And it does not solve our problem in any way.

ENTRYPOINT ["/usr/sbin/httpd"]
CMD ["-D", "FOREGROUND"]

Suggestion 2:Use a separate startup script, which could work around any issues with the starting or restarting of the httpd service. In other words, the Dockerfile becomes something like this:

...
EXPOSE 80
COPY ./run-httpd.sh /run-httpd.sh
RUN chmod -v +x /run-httpd.sh
CMD ["/run-httpd.sh"]

And the contents of the run-httpd.sh script are along the lines of:

#!/bin/bash

rm -rf /run/httpd/*

exec /usr/sbin/apachectl -D FOREGROUND

Almost there. Remove any old leftover PID files, but these are normally not stored under /run/httpd. Instead, you will find them under /var/run/httpd. Moreover, we are not certain that this directory exists.

Finally, the idea is to work around any problems with the execution of a separation shell inside which the httpd thread is spawned. While it does provide us with additional, useful lessons on how to manage the container, with COPY and RUN instructions, it's not what we need to fix the issue.

Step 3 :EXPOSE 80
 ---> Using cache
 ---> 108785c8e507
Step 4 :COPY ./run-httpd.sh /run-httpd.sh
 ---> 582d795d59d4
Removing intermediate container 7ff5b58b40bf
Step 5 :RUN chmod -v +x /run-httpd.sh
 ---> Running in 56fadf4dd2d4
mode of '/run-httpd.sh' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
 ---> 928640f680cf
Removing intermediate container 56fadf4dd2d4
Step 6 :CMD /run-httpd.sh
 ---> Running in f9c6b30795e2
 ---> b2dcc2818a27
Removing intermediate container f9c6b30795e2
Successfully built b2dcc2818a27

This won't work, because apachectl is an unsupported command for managing httpd, plus we have seen problems using startup scripts and utilities earlier, and we will work on fixing this in a separate tutorial.

docker run -ti -p 80 image-2:latest
Passing arguments to httpd using apachectl is no longer supported. You can only start/stop/restart httpd using this script. If you want to pass extra arguments to httpd, edit the /etc/sysconfig/httpd config file.

But it is useful to try these different things, to get the hang of it. Unfortunately, it also highlights the lack of maturity and the somewhat inadequate documentation for this technology out there.

Additional commands

There are many ways you can interact with your container. If you do not want to attach a new shell to a running instance, you can use a subset of docker commands directly against the container ID or name:

docker

For instance, to get the top output from the container:

docker top boring_stallman

If you have too many images, some of which have just been used for testing, then you can remove them to free up some of your disk space. This can be done using the docker rmi command.

# docker rmi -f test7
Untagged:test7:latest
Deleted: d0505b88466a97b73d083434b2dd0e7b59b9a5e8d0438b1bf8c6c
Deleted:5fc118f61bf856f6f3d90e0e71076b737fa7cc58cd56785ea7904
Deleted:628c3d6b53992521c9c1fdda4148693347c3d10b1d130f7e091e7
Deleted:58f11217c3e31206b4e41d07100a797cd4d17e4569b0fdb8b7a18
Deleted:7505c70235e638c54028ea5b63eba2b691de6bee67c2cb5e2861a
...

Then, you can also run your containers in the background. Using the -d flag will do exactly that, and you will get the shell prompt back. This is also useful if you do not mask signals, so if you accidentally break in your shell, you might kill the container when it's running in the foreground.

docker run -d -ti -p 80 image-3:latest

You can also check events, examine changes inside a container's filesystem as well as check history, so you basically have a version control in place, export or import tarred images to and from remote locations, including over the Web, and more.

Differences between exec and attach

If you read through the documentation, you will notice you can connect to a running container using either exec or attach commands. So what's the difference, you may ask? If we look at the official documentation, then:

The docker exec command runs a new command in a running container. The command started using docker exec only runs while the container's primary process (PID 1) is running, and it is not restarted if the container is restarted.

On the other hand, attach gives you the following:

The docker attach command allows you to attach to a running container using the container's ID or name, either to view its ongoing output or to control it interactively. You can attach to the same contained process multiple times simultaneously, screen sharing style, or quickly view the progress of your daemonized process. You can detach from the container (and leave it running) with CTRL-p CTRL-q (for a quiet exit) or CTRL-c which will send a SIGKILL to the container. When you are attached to a container, and exit its main process, the process's exit code will be returned to the client.

In other words, with attach, you will get a shell, and be able to do whatever you need. With exec, you can issue commands that do not require any interaction, but with you use a shell in combination with exec, you will achieve the same result as if you used attach.

Differences between start and run

Start is used to resume the execution of a stopped container. It is not used to start a fresh instance. For that, you have the run command. The choice of words could have been better.

Differences between build and create

The first command is used to create a new image from a Dockerfile. On the other hand, the latter is used to create a new container using command line options and arguments. Create lets you specify container settings, too, like network configurations, resource limitations and other settings, which affect the container from the outside, whereas the changes implemented by the build command will be reflected inside it, once you start an instance. And by start, I mean run. Hiểu rồi?

This is just a beginning ...

There are a million more things we can do: using systemd enabled containers, policies, security, resource constraints, proxying, signals, other networking and storage options including the super-critical question of how to mount data volumes inside containers so that data does not get destroyed when containers die, additional pure LXC commands, and more. We've barely scratched the surface. But now, we know what to do. And we'll get there. Slowly but surely.

Đọc thêm

I recommend you allocate a few hours and then spend some honest time reading all of the below, in detail. Then practice. This is the only way you will really fully understand and embrace the concepts.

My entire virtualization section

Dockerizing an SSH Deamon Service

Differences between save and export in Docker

Docker Explained:Using Dockerfiles to Automate Building of Images

Kết luận

We're done with this tutorial for today. Hopefully, you've found it useful. In a nutshell, it does explain quite a few things, including how to get started with Docker, how to pull new images, run basic containers, add services like SSH and Apache, commit changes to a file, expose incoming ports, build new images with Dockerfiles, lots of troubleshooting of problems, additional commands, and more. Eventful and colorful, I'd dare say.

In the future, we will expand significantly on what we learned here, and focus on various helper technologies like supervisord for instance, we will learn how to mount filesystems, work on administration and orchestration, and many other cool things. Docker is a very nice concept, and if used correctly, it can make your virtual world easier and more elegant. The initial few steps are rough, but with some luck, this guide will have provided you with the right dose of karma to get happily and confidently underway. Ping me if you have any requests or desires. Technology related, of course. Đã được thực hiện.

Tái bút If you like this article, then you'd better give some love back to Dedoimedo!

Chúc mừng.