├── .gitignore ├── README.md ├── SUMMARY.md ├── _images ├── cmd_logic.dot ├── cmd_logic.png ├── container_connect_topology.png ├── cover.png ├── docker.png ├── docker_arch.png ├── enterprise_usage.png ├── network.png └── virtualization.png ├── _local ├── .bashrc_docker ├── docker_manual_waitfish.pdf ├── pull_all.sh ├── push_all.sh └── push_images.sh ├── advanced_network ├── README.md ├── access_control.md ├── bridge.md ├── config_file.md ├── dns.md ├── docker0.md ├── example.md ├── how_connect.md ├── port_mapping.md ├── ptp.md └── quick_guide.md ├── appendix_command └── README.md ├── appendix_repo ├── README.md ├── centos.md ├── mongodb.md ├── mysql.md ├── nginx.md ├── nodejs.md ├── redis.md ├── ubuntu.md └── wordpress.md ├── appendix_resources └── README.md ├── basic_concept ├── README.md ├── container.md ├── image.md └── repository.md ├── book.json ├── cases ├── README.md ├── container_connect.md ├── environment.md ├── supervisor.md └── tomcat.md ├── container ├── README.md ├── daemon.md ├── enter.md ├── import_export.md ├── rm.md ├── run.md └── stop.md ├── cover.jpg ├── cover_small.jpg ├── data_management ├── README.md ├── container.md ├── management.md └── volume.md ├── dockerfile ├── README.md ├── basic_structure.md ├── build_image.md ├── file_from_image.md └── instructions.md ├── image ├── README.md ├── create.md ├── internal.md ├── list.md ├── pull.md ├── rmi.md └── save_load.md ├── install ├── README.md ├── centos.md └── ubuntu.md ├── introduction ├── README.md ├── what.md └── why.md ├── network ├── README.md ├── linking.md └── port_mapping.md ├── repository ├── README.md ├── config.md ├── dockerhub.md └── local_repo.md ├── security ├── README.md ├── control_group.md ├── daemon_sec.md ├── kernel_capability.md ├── kernel_ns.md ├── other_feature.md └── summary.md └── underly ├── README.md ├── arch.md ├── cgroups.md ├── container_format.md ├── namespace.md ├── network.md └── ufs.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | *.~ 3 | *.tmp 4 | .idea/ 5 | _book/ 6 | *.swp 7 | *.edx 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker —— 從入門到實踐 2 | =============== 3 | 4 | v0.2.9 5 | 6 | [Docker](docker.com) 是個偉大的專案,它徹底釋放了虛擬化的,讓應用程式的分派、部署和管理都變得前所未有的有效率和輕鬆! 7 | 8 | 本書既適用於具備基礎 Linux 知識的 Docker 初學者,也可供希望理解原理和實作的進階使用者參考。同時,書中給出的實踐案例,可供在進行實際部署時借鑒。 9 | 10 | 本書源於 [WaitFish](https://github.com/Waitfish) 的《[Docker 學習手冊 v1.0](https://github.com/yeasy/docker_practice/raw/master/_local/docker_manual_waitfish.pdf)》內容。後來,[yeasy](https://github.com/yeasy) 11 | 根據最新 Docker 版本對內容進行了修訂和重寫,並增加內容;經協商將所有內容開源,採用網路合作的方式進行維護。 12 | 13 | 前六章為基礎內容,供使用者理解 Docker 的基本概念和操作;7 ~ 9 章介紹一些進階操作;第 10 章給出典型的應用場景和實踐案例;11 ~ 13 章介紹關於 Docker 實作的相關技術。 14 | 15 | 最新版本線上閱讀:[正體版](https://www.gitbook.io/book/philipzheng/docker_practice)、[簡體版](https://www.gitbook.io/book/yeasy/docker_practice) 或 [DockerPool](http://dockerpool.com/static/books/docker_practice/index.html)。 16 | 17 | 另外,歡迎加入 [Docker.Taipei](https://www.facebook.com/groups/docker.taipei/) 和 [Meetup](http://www.meetup.com/Docker-Taipei/) ,分享 Docker 資源,交流 Docker 技術。 18 | 19 | 20 | 本書原始碼在 Github 上維護,歡迎參與: [https://github.com/philipz/docker_practice](https://github.com/philipz/docker_practice)。 21 | 22 | 感謝所有的 [貢獻者](https://github.com/philipz/docker_practice/graphs/contributors)。 23 | 24 | ## 主要版本歷史 25 | 26 | * 0.3: 2014-10-TODO 27 | * 完成倉庫章節; 28 | * 重寫安全章節; 29 | * 修正底層實作章節的架構、命名空間、控制組、檔案系統、容器格式等內容; 30 | * 新增對常見倉庫和鏡像的介紹; 31 | * 新增 Dockerfile 的介紹; 32 | * 重新校訂中英文混排格式。 33 | * 0.2: 2014-09-18 34 | * 對照官方文檔重寫介紹、基本概念、安裝、鏡像、容器、倉庫、資料管理、網路等章節; 35 | * 新增底層實作章節; 36 | * 新增命令查詢和資源連結章節; 37 | * 其它修正。 38 | * 0.1: 2014-09-05 39 | * 新增基本內容; 40 | * 修正錯別字和表達不通順的地方。 41 | 42 | ## 貢獻力量 43 | 44 | 如果想做出貢獻的話,你可以: 45 | 46 | - 幫忙校對,挑錯別字、語病等等 47 | - 提出修改建議 48 | - 提出術語翻譯建議 49 | 50 | ## 翻譯建議 51 | 52 | 如果你願意一起校對的話,請仔細閱讀: 53 | 54 | - 使用 markdown 進行翻譯,文件名必須使用英文,因為中文的話 gitbook 編譯的時候會出問題 55 | - 引號請使用「」和『』 56 | - fork 過去之後新建一個分支進行翻譯,完成後 pull request 這個分支,沒問題的話我會合併到 master 分支中 57 | - 有其他任何問題都歡迎發 issue,我看到了會盡快回覆 58 | 59 | 謝謝! 60 | 61 | ## 關於術語 62 | 63 | 翻譯術語的時候請參考這個流程: 64 | 65 | - 盡量保證與台灣習慣術語和已翻譯的內容一致 66 | - 盡量先搜尋,一般來說程式語言的大部分術語是一樣的,可以參考[這個網站](http://jjhou.boolan.com/terms.htm) 67 | - 如果以上兩條都沒有找到合適的結果,請自己決定一個合適的翻譯或者直接使用英文原文,後期校對的時候會進行統一 68 | - 校稿時,若有發現沒有被翻譯成台灣術語的大陸術語,可以將它新增到 translation.json 中 69 | - 可以主動提交替換過的文本給我,或是僅提交新增過的 translation.json 也可,我會再進行文本的替換 70 | - 請務必確定提交的翻譯對照組不會造成字串循環替代(ex: 因為「類」->「類別」,造成下次再執行自動翻譯時「類別」又變成「類別別」) 71 | 72 | 對翻譯有任何意見都歡迎發 issue,我看到了會盡快回覆 73 | 74 | ## 參加步驟 75 | 76 | 參考 [Swift 說明](https://github.com/tommy60703/the-swift-programming-language-in-traditional-chinese/),欲翻譯章節就直接在 github 上發 Issue 中註明或直接發Pull Request 修改。m(_ _)m 77 | 有些朋友可能不太清楚如何幫忙翻譯,我這裡寫一個簡單的流程,大家可以參考一下: 78 | 79 | 1. 首先 fork 我的專案 80 | 2. 把 fork 過去的專案也就是你的專案 clone 到你的本地 81 | 3. 在命令行執行 `git branch develop` 來建立一個新分支 82 | 4. 執行 `git checkout develop` 來切換到新分支 83 | 5. 執行 `git remote add upstream https://github.com/philipz/docker_practice` 把我的庫新增為遠端庫 84 | 6. 執行 `git remote update`更新 85 | 7. 執行 `git fetch upstream master` 拉取我的庫的更新到本地 86 | 8. 執行 `git rebase upstream/master` 將我的更新合並到你的分支 87 | 88 | 這是一個初始化流程,只需要做一遍就行,之後請一直在 develop 分支進行修改。 89 | 90 | 如果修改過程中我的庫有了更新,請重復 6、7、8 步。 91 | 92 | 修改之後,首先 push 到你的庫,然後登錄 GitHub,在你的 repo 的首頁可以看到一個 `pull request` 按鈕,點擊它,填寫一些說明資訊,然後提交即可。 93 | 94 | ## 原出處及參考資料 95 | 96 | 1. [Docker —— 从入门到实践](https://github.com/yeasy/docker_practice/) 97 | 2. [《The Swift Programming Language­》正體中文版](https://github.com/tommy60703/the-swift-programming-language-in-traditional-chinese/) -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [前言](README.md) 4 | * [Docker 簡介](introduction/README.md) 5 | * [什麼是 Docker](introduction/what.md) 6 | * [為什麼要用 Docker](introduction/why.md) 7 | * [基本概念](basic_concept/README.md) 8 | * [映像檔](basic_concept/image.md) 9 | * [容器](basic_concept/container.md) 10 | * [倉庫](basic_concept/repository.md) 11 | * [安裝](install/README.md) 12 | * [Ubuntu](install/ubuntu.md) 13 | * [CentOS](install/centos.md) 14 | * [映像檔](image/README.md) 15 | * [取得映像檔](image/pull.md) 16 | * [列出](image/list.md) 17 | * [建立](image/create.md) 18 | * [儲存和載入](image/save_load.md) 19 | * [移除](image/rmi.md) 20 | * [實作原理](image/internal.md) 21 | * [容器](container/README.md) 22 | * [啟動](container/run.md) 23 | * [常駐執行](container/daemon.md) 24 | * [終止](container/stop.md) 25 | * [進入容器](container/enter.md) 26 | * [匯出與匯入](container/import_export.md) 27 | * [刪除](container/rm.md) 28 | * [倉庫](repository/README.md) 29 | * [Docker Hub](repository/dockerhub.md) 30 | * [私有倉庫](repository/local_repo.md) 31 | * [設定檔案](repository/config.md) 32 | * [資料管理](data_management/README.md) 33 | * [資料卷](data_management/volume.md) 34 | * [資料卷容器](data_management/container.md) 35 | * [備份、恢復、遷移資料卷](data_management/management.md) 36 | * [使用網路](network/README.md) 37 | * [外部存取容器](network/port_mapping.md) 38 | * [容器互連](network/linking.md) 39 | * [進階網路設定](advanced_network/README.md) 40 | * [快速設定指南](advanced_network/quick_guide.md) 41 | * [設定 DNS](advanced_network/dns.md) 42 | * [容器存取控制](advanced_network/access_control.md) 43 | * [埠號映射實作](advanced_network/port_mapping.md) 44 | * [設定 docker0 橋接器](advanced_network/docker0.md) 45 | * [自訂橋接器](advanced_network/bridge.md) 46 | * [工具與範例](advanced_network/example.md) 47 | * [編輯網路設定檔案](advanced_network/config_file.md) 48 | * [實例:創造一個點對點連線](advanced_network/ptp.md) 49 | * [實戰案例](cases/README.md) 50 | * [使用 Supervisor 來管理程式](cases/supervisor.md) 51 | * [建立 tomcat/weblogic 集群](cases/tomcat.md) 52 | * [多台實體主機之間的容器互連](cases/container_connect.md) 53 | * [標準化開發測試和生產環境](cases/environment.md) 54 | * [安全](security/README.md) 55 | * [核心命名空間](security/kernel_ns.md) 56 | * [控制組](security/control_group.md) 57 | * [伺服端防護](security/daemon_sec.md) 58 | * [核心能力機制](security/kernel_capability.md) 59 | * [其他安全特性](security/other_feature.md) 60 | * [總結](security/summary.md) 61 | * [Dockerfile](dockerfile/README.md) 62 | * [基本結構](dockerfile/basic_structure.md) 63 | * [指令](dockerfile/instructions.md) 64 | * [建立映像檔](dockerfile/build_image.md) 65 | * [從映像檔產生 Dockerfile](dockerfile/file_from_image.md) 66 | * [底層實作](underly/README.md) 67 | * [基本架構](underly/arch.md) 68 | * [命名空間](underly/namespace.md) 69 | * [控制組](underly/cgroups.md) 70 | * [Union 檔案系統](underly/ufs.md) 71 | * [容器格式](underly/container_format.md) 72 | * [網路](underly/network.md) 73 | * [附錄一:命令查詢](appendix_command/README.md) 74 | * [附錄二:常見倉庫介紹](appendix_repo/README.md) 75 | * [Ubuntu](appendix_repo/ubuntu.md) 76 | * [CentOS](appendix_repo/centos.md) 77 | * [MySQL](appendix_repo/mysql.md) 78 | * [MongoDB](appendix_repo/mongodb.md) 79 | * [Redis](appendix_repo/redis.md) 80 | * [Nginx](appendix_repo/nginx.md) 81 | * [WordPress](appendix_repo/wordpress.md) 82 | * [Node.js](appendix_repo/nodejs.md) 83 | * [附錄三:資源連結](appendix_resources/README.md) 84 | 85 | -------------------------------------------------------------------------------- /_images/cmd_logic.dot: -------------------------------------------------------------------------------- 1 | //dot -Tpng xx.dot -o xx.png 2 | digraph G { 3 | rankdir=TB; 4 | fontname = "Microsoft YaHei"; 5 | fontsize = 14; 6 | penwidth = 3; 7 | compound=true; 8 | rankdir=LR; 9 | 10 | node [shape = record]; 11 | edge [fontname = "Arial", fontsize = 12, color="darkgreen" ]; 12 | 13 | image[label="Image",color=blue]; 14 | registry[label="Registry",color=blue]; 15 | tar[label="Tar files",color=blue]; 16 | 17 | subgraph cluster_container { 18 | label = "Container"; 19 | style = "bold"; 20 | color = blue; 21 | edge [fontname = "Arial", fontsize = 11, color="skyblue" ]; 22 | //node [style=filled]; 23 | run[label="Running",shape=circle, style=filled, fillcolor=green]; 24 | stop[label="Stopped",shape=circle, style=filled, fillcolor=red]; 25 | pause[label="Paused",shape=circle, style=filled, fillcolor=blue]; 26 | 27 | run->pause[label="pause"]; 28 | pause->run[label="unpause"]; 29 | run->run[label="restart"]; 30 | run->stop[label="kill"]; 31 | stop->run[label="start"]; 32 | } 33 | 34 | run->image[label="commit",ltail=cluster_container]; 35 | image->run[label="start"]; 36 | 37 | image->tar[label="export|save"]; 38 | tar->image[label="import"]; 39 | 40 | image->registry[label="push"]; 41 | registry->image[label="pull"]; 42 | 43 | //heat[label="heat commands",color=blue]; 44 | //heatshell[label="heatclient.shell.HeatShell",color=blue]; 45 | //shell[label="{heatclient.v1.shell|+do_stack_create\l+do_stack_show\l+do_stack_update\l...\l+do_event_list\l...\l+do_resource_list\l...\l+do_resource_type_show\l...\l+do_template_show\l...\l}",color=blue]; 46 | //heatclient[label="heatclient.client.Client",color=blue]; 47 | //client[label="heatclient.v1.client.Client",color=blue]; 48 | //httpclient[label="heatclient.common.http.HTTPClient",color=blue]; 49 | 50 | 51 | 52 | //openstackservices[label="{OpenStack Services|+Nova\l+Neutron\l+Keystone\l...}",color=blue]; 53 | 54 | //{rank=same; image cluster_container} 55 | //{rank=same; rpcproxy apimixin} 56 | } 57 | -------------------------------------------------------------------------------- /_images/cmd_logic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/cmd_logic.png -------------------------------------------------------------------------------- /_images/container_connect_topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/container_connect_topology.png -------------------------------------------------------------------------------- /_images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/cover.png -------------------------------------------------------------------------------- /_images/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/docker.png -------------------------------------------------------------------------------- /_images/docker_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/docker_arch.png -------------------------------------------------------------------------------- /_images/enterprise_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/enterprise_usage.png -------------------------------------------------------------------------------- /_images/network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/network.png -------------------------------------------------------------------------------- /_images/virtualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_images/virtualization.png -------------------------------------------------------------------------------- /_local/.bashrc_docker: -------------------------------------------------------------------------------- 1 | # Some useful commands to use docker. 2 | # Author: yeasy@github 3 | # Created:2014-09-25 4 | 5 | alias docker-pid="sudo docker inspect --format '{{.State.Pid}}'" 6 | alias docker-ip="sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}'" 7 | 8 | #the implementation refs from https://github.com/jpetazzo/nsenter/blob/master/docker-enter 9 | function docker-enter() { 10 | if [ -e $(dirname "$0")/nsenter ]; then 11 | # with boot2docker, nsenter is not in the PATH but it is in the same folder 12 | NSENTER=$(dirname "$0")/nsenter 13 | else 14 | NSENTER=nsenter 15 | fi 16 | [ -z "$NSENTER" ] && echo "WARN Cannot find nsenter" && return 17 | 18 | if [ -z "$1" ]; then 19 | echo "Usage: `basename "$0"` CONTAINER [COMMAND [ARG]...]" 20 | echo "" 21 | echo "Enters the Docker CONTAINER and executes the specified COMMAND." 22 | echo "If COMMAND is not specified, runs an interactive shell in CONTAINER." 23 | else 24 | PID=$(sudo docker inspect --format "{{.State.Pid}}" "$1") 25 | if [ -z "$PID" ]; then 26 | echo "WARN Cannot find the given container" 27 | return 28 | fi 29 | shift 30 | 31 | OPTS="--target $PID --mount --uts --ipc --net --pid" 32 | 33 | if [ -z "$1" ]; then 34 | # No command given. 35 | # Use su to clear all host environment variables except for TERM, 36 | # initialize the environment variables HOME, SHELL, USER, LOGNAME, PATH, 37 | # and start a login shell. 38 | #sudo $NSENTER "$OPTS" su - root 39 | sudo $NSENTER --target $PID --mount --uts --ipc --net --pid su - root 40 | else 41 | # Use env to clear all host environment variables. 42 | sudo $NSENTER --target $PID --mount --uts --ipc --net --pid env -i $@ 43 | fi 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /_local/docker_manual_waitfish.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/_local/docker_manual_waitfish.pdf -------------------------------------------------------------------------------- /_local/pull_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script will update all local images 4 | # See: https://github.com/yeasy/docker_practice/blob/master/_local/pull_all.sh 5 | # Usage: pull_all 6 | # Author: yeasy@github 7 | # Create: 2014-09-23 8 | 9 | for image in `sudo docker images|grep -v "REPOSITORY"|grep -v ""|awk '{print $1":"$2}'` 10 | do 11 | sudo docker pull $image 12 | done 13 | 14 | -------------------------------------------------------------------------------- /_local/push_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script will upload all local images to a registry server ($registry is the default value). 3 | # This script requires the push_images, which can be found at https://github.com/yeasy/docker_practice/blob/master/_local/push_images.sh 4 | # Usage: push_all 5 | # Author: yeasy@github 6 | # Create: 2014-09-23 7 | 8 | for image in `sudo docker images|grep -v "REPOSITORY"|grep -v ""|awk '{print $1":"$2}'` 9 | do 10 | push_images $image 11 | done 12 | 13 | -------------------------------------------------------------------------------- /_local/push_images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script will upload the given local images to a registry server ($registry is the default value). 4 | # See: https://github.com/yeasy/docker_practice/blob/master/_local/push_images.sh 5 | # Usage: push_images image1 [image2...] 6 | # Author: yeasy@github 7 | # Create: 2014-09-23 8 | 9 | #The registry server address where you want push the images into 10 | registry=127.0.0.1:5000 11 | 12 | ### DO NOT MODIFY THE FOLLOWING PART, UNLESS YOU KNOW WHAT IT MEANS ### 13 | echo_r () { 14 | [ $# -ne 1 ] && return 0 15 | echo -e "\033[31m$1\033[0m" 16 | } 17 | echo_g () { 18 | [ $# -ne 1 ] && return 0 19 | echo -e "\033[32m$1\033[0m" 20 | } 21 | echo_y () { 22 | [ $# -ne 1 ] && return 0 23 | echo -e "\033[33m$1\033[0m" 24 | } 25 | echo_b () { 26 | [ $# -ne 1 ] && return 0 27 | echo -e "\033[34m$1\033[0m" 28 | } 29 | 30 | usage() { 31 | sudo docker images 32 | echo "Usage: $0 registry1:tag1 [registry2:tag2...]" 33 | } 34 | 35 | [ $# -lt 1 ] && usage && exit 36 | 37 | echo_b "The registry server is $registry" 38 | 39 | 40 | for image in "$@" 41 | do 42 | echo_b "Uploading $image..." 43 | sudo docker tag $image $registry/$image 44 | sudo docker push $registry/$image 45 | sudo docker rmi $registry/$image 46 | echo_g "Done" 47 | done 48 | -------------------------------------------------------------------------------- /advanced_network/README.md: -------------------------------------------------------------------------------- 1 | # 進階網路設定 2 | 本章將介紹 Docker 的一些進階網路設定和選項。 3 | 4 | 當 Docker 啟動時,會自動在主機上建立一個 `docker0` 虛擬橋接器,實際上是 Linux 的一個 bridge,可以理解為一個軟體交換機。它會在掛載到它的網卡之間進行轉發。 5 | 6 | 同時,Docker 隨機分配一個本地未占用的私有網段(在 [RFC1918](http://tools.ietf.org/html/rfc1918) 中定義)中的一個位址給 `docker0` 介面。比如典型的 `172.17.42.1`,網路遮罩為 `255.255.0.0`。此後啟動的容器內的網卡也會自動分配一個同一網段(`172.17.0.0/16`)的網址。 7 | 8 | 當建立一個 Docker 容器的時候,同時會建立了一對 `veth pair` 介面(當資料包發送到一個介面時,另外一個介面也可以收到相同的資料包)。這對介面一端在容器內,即 `eth0`;另一端在本地並被掛載到 `docker0` 橋接器,名稱以 `veth` 開頭(例如 `vethAQI2QT`)。透過這種方式,主機可以跟容器通訊,容器之間也可以相互通訊。Docker 就建立了在主機和所有容器之間一個虛擬共享網路。 9 | 10 | ![Docker 網路](../_images/network.png) 11 | 12 | 接下來的部分將介紹在一些場景中,Docker 所有的網路自訂設定。以及透過 Linux 命令來調整、補充、甚至替換 Docker 預設的網路設定。 13 | -------------------------------------------------------------------------------- /advanced_network/access_control.md: -------------------------------------------------------------------------------- 1 | ## 容器存取控制 2 | 容器的存取控制,主要透過 Linux 上的 `iptables` 防火墻來進行管理和實作。`iptables` 是 Linux 上預設的防火墻軟體,在大部分發行版中都內建。 3 | 4 | ### 容器存取外部網路 5 | 容器要想存取外部網路,需要本地系統的轉發支援。在Linux 系統中,檢查轉發是否打開。 6 | 7 | ``` 8 | $sysctl net.ipv4.ip_forward 9 | net.ipv4.ip_forward = 1 10 | ``` 11 | 如果為 0,說明沒有開啟轉發,則需要手動打開。 12 | ``` 13 | $sysctl -w net.ipv4.ip_forward=1 14 | ``` 15 | 如果在啟動 Docker 服務的時候設定 `--ip-forward=true` , Docker 就會自動設定系統的 `ip_forward` 參數為 1。 16 | 17 | ### 容器之間存取 18 | 容器之間相互存取,需要兩方面的支援。 19 | * 容器的網路拓撲是否已經互聯。預設情況下,所有容器都會被連線到 `docker0` 橋接器上。 20 | * 本地系統的防火墻軟體 -- `iptables` 是否允許透過。 21 | 22 | #### 存取所有連接埠 23 | 當啟動 Docker 服務時候,預設會新增一條轉發策略到 iptables 的 FORWARD 鏈上。策略為透過(`ACCEPT`)還是禁止(`DROP`)取決於設定`--icc=true`(預設值)還是 `--icc=false`。當然,如果手動指定 `--iptables=false` 則不會新增 `iptables` 規則。 24 | 25 | 可見,預設情況下,不同容器之間是允許網路互通的。如果為了安全考慮,可以在 `/etc/default/docker` 檔案中設定 `DOCKER_OPTS=--icc=false` 來禁止它。 26 | 27 | #### 存取指定連接埠 28 | 在透過 `-icc=false` 關閉網路存取後,還可以透過 `--link=CONTAINER_NAME:ALIAS` 選項來存取容器的開放連接埠。 29 | 30 | 例如,在啟動 Docker 服務時,可以同時使用 `icc=false --iptables=true` 參數來關閉允許相互的網路存取,並讓 Docker 可以修改系統中的 `iptables` 規則。 31 | 32 | 此時,系統中的 `iptables` 規則可能是類似 33 | ``` 34 | $ sudo iptables -nL 35 | ... 36 | Chain FORWARD (policy ACCEPT) 37 | target prot opt source destination 38 | DROP all -- 0.0.0.0/0 0.0.0.0/0 39 | ... 40 | ``` 41 | 42 | 之後,啟動容器(`docker run`)時使用 `--link=CONTAINER_NAME:ALIAS` 選項。Docker 會在 `iptable` 中為 兩個容器分別新增一條 `ACCEPT` 規則,允許相互存取開放的連接埠(取決於 Dockerfile 中的 EXPOSE 行)。 43 | 44 | 當新增了 `--link=CONTAINER_NAME:ALIAS` 選項後,新增了 `iptables` 規則。 45 | ``` 46 | $ sudo iptables -nL 47 | ... 48 | Chain FORWARD (policy ACCEPT) 49 | target prot opt source destination 50 | ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80 51 | ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80 52 | DROP all -- 0.0.0.0/0 0.0.0.0/0 53 | ``` 54 | 55 | 注意:`--link=CONTAINER_NAME:ALIAS` 中的 `CONTAINER_NAME` 目前必須是 Docker 分配的名字,或使用 `--name` 參數指定的名字。主機名則不會被識別。 56 | -------------------------------------------------------------------------------- /advanced_network/bridge.md: -------------------------------------------------------------------------------- 1 | ## 自訂橋接器 2 | 除了預設的 `docker0` 橋接器,使用者也可以指定橋接器來連線各個容器。 3 | 4 | 在啟動 Docker 服務的時候,使用 `-b BRIDGE`或`--bridge=BRIDGE` 來指定使用的橋接器。 5 | 6 | 如果服務已經執行,那需要先停止服務,並刪除舊的橋接器。 7 | ``` 8 | $ sudo service docker stop 9 | $ sudo ip link set dev docker0 down 10 | $ sudo brctl delbr docker0 11 | ``` 12 | 然後建立一個橋接器 `bridge0`。 13 | ``` 14 | $ sudo brctl addbr bridge0 15 | $ sudo ip addr add 192.168.5.1/24 dev bridge0 16 | $ sudo ip link set dev bridge0 up 17 | ``` 18 | 查看確認橋接器建立並啟動。 19 | ``` 20 | $ ip addr show bridge0 21 | 4: bridge0: mtu 1500 qdisc noop state UP group default 22 | link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff 23 | inet 192.168.5.1/24 scope global bridge0 24 | valid_lft forever preferred_lft forever 25 | ``` 26 | 設定 Docker 服務,預設橋接到建立的橋接器上。 27 | ``` 28 | $ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker 29 | $ sudo service docker start 30 | ``` 31 | 啟動 Docker 服務。 32 | 新建一個容器,可以看到它已經橋接到了 `bridge0` 上。 33 | 34 | 可以繼續用 `brctl show` 命令查看橋接的訊息。另外,在容器中可以使用 `ip addr` 和 `ip route` 命令來查看 IP 位址設定和路由訊息。 35 | -------------------------------------------------------------------------------- /advanced_network/config_file.md: -------------------------------------------------------------------------------- 1 | ## 編輯網路設定檔案 2 | 3 | Docker 1.2.0 開始支援在執行中的容器裡編輯 `/etc/hosts` 、 `/etc/hostname` 和 `/etc/resolve.conf` 檔案。 4 | 5 | 但是這些修改是臨時的,只在執行的容器中保留,容器終止或重啟後並不會被保存下來。也不會被 `docker commit` 提交。 6 | -------------------------------------------------------------------------------- /advanced_network/dns.md: -------------------------------------------------------------------------------- 1 | ## 設定 DNS 2 | Docker 沒有為每個容器專門定制映像檔,那麽怎麽自訂設定容器的主機名和 DNS 設定呢? 3 | 秘訣就是它利用虛擬檔案來掛載到來容器的 3 個相關設定檔案。 4 | 5 | 在容器中使用 mount 命令可以看到掛載訊息: 6 | ``` 7 | $ mount 8 | ... 9 | /dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ... 10 | /dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ... 11 | tmpfs on /etc/resolv.conf type tmpfs ... 12 | ... 13 | ``` 14 | 這種機制可以讓宿主主機 DNS 訊息發生更新後,所有 Docker 容器的 dns 設定透過 `/etc/resolv.conf` 檔案立刻得到更新。 15 | 16 | 如果使用者想要手動指定容器的設定,可以利用下面的選項。 17 | 18 | `-h HOSTNAME or --hostname=HOSTNAME` 19 | 設定容器的主機名,它會被寫到容器內的 `/etc/hostname` 和 `/etc/hosts`。但它在容器外部看不到,既不會在 `docker ps` 中顯示,也不會在其他的容器的 `/etc/hosts` 看到。 20 | 21 | `--link=CONTAINER_NAME:ALIAS` 22 | 選項會在建立容器的時候,新增一個其他容器的主機名到 `/etc/hosts` 檔案中,讓新容器的程式可以使用主機名 ALIAS 就可以連線它。 23 | 24 | `--dns=IP_ADDRESS` 25 | 新增 DNS 伺服器到容器的 `/etc/resolv.conf` 中,讓容器用這個伺服器來解析所有不在 `/etc/hosts` 中的主機名。 26 | 27 | `--dns-search=DOMAIN` 28 | 設定容器的搜尋域,當設定搜尋域為 `.example.com` 時,在搜尋一個名為 host 的主機時,DNS 不僅搜尋host,還會搜尋 `host.example.com`。 29 | 注意:如果沒有上述最後 2 個選項,Docker 會預設用主機上的 `/etc/resolv.conf` 來設定容器。 30 | -------------------------------------------------------------------------------- /advanced_network/docker0.md: -------------------------------------------------------------------------------- 1 | ## 設定 docker0 橋接器 2 | Docker 服務預設會建立一個 `docker0` 橋接器(其上有一個 `docker0` 內部介面),它在核心層連通了其他的物理或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網路。 3 | 4 | Docker 預設指定了 `docker0` 介面 的 IP 位址和子網遮罩,讓主機和容器之間可以透過橋接器相互通訊,它還給出了 MTU(介面允許接收的最大傳輸單元),通常是 1500 Bytes,或宿主主機網路路由上支援的預設值。這些值都可以在服務啟動的時候進行設定。 5 | * `--bip=CIDR` -- IP 位址加遮罩格式,例如 192.168.1.5/24 6 | * `--mtu=BYTES` -- 覆蓋預設的 Docker mtu 設定 7 | 8 | 也可以在設定檔案中設定 DOCKER_OPTS,然後重啟服務。 9 | 由於目前 Docker 橋接器是 Linux 橋接器,使用者可以使用 `brctl show` 來查看橋接器和連接埠連線訊息。 10 | ``` 11 | $ sudo brctl show 12 | bridge name bridge id STP enabled interfaces 13 | docker0 8000.3a1d7362b4ee no veth65f9 14 | vethdda6 15 | ``` 16 | *註:`brctl` 命令在 Debian、Ubuntu 中可以使用 `sudo apt-get install bridge-utils` 來安裝。 17 | 18 | 19 | 每次建立一個新容器的時候,Docker 從可用的位址段中選擇一個未使用的 IP 位址分配給容器的 eth0 連接埠。使用本地主機上 `docker0` 介面的 IP 作為所有容器的預設網關。 20 | ``` 21 | $ sudo docker run -i -t --rm base /bin/bash 22 | $ ip addr show eth0 23 | 24: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 24 | link/ether 32:6f:e0:35:57:91 brd ff:ff:ff:ff:ff:ff 25 | inet 172.17.0.3/16 scope global eth0 26 | valid_lft forever preferred_lft forever 27 | inet6 fe80::306f:e0ff:fe35:5791/64 scope link 28 | valid_lft forever preferred_lft forever 29 | $ ip route 30 | default via 172.17.42.1 dev eth0 31 | 172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3 32 | $ exit 33 | ``` 34 | -------------------------------------------------------------------------------- /advanced_network/example.md: -------------------------------------------------------------------------------- 1 | ## 工具和示例 2 | 在介紹自訂網路拓撲之前,你可能會對一些外部工具和例子感興趣: 3 | 4 | ### pipework 5 | Jérôme Petazzoni 編寫了一個叫 [pipework](https://github.com/jpetazzo/pipework) 的 shell 腳本,可以幫助使用者在比較復雜的場景中完成容器的連線。 6 | 7 | ### playground 8 | Brandon Rhodes 建立了一個提供完整的 Docker 容器網路拓撲管理的 [Python庫](https://github.com/brandon-rhodes/fopnp/tree/m/playground),包括路由、NAT 防火墻;以及一些提供 HTTP 、 SMTP 、 POP 、 IMAP 、 Telnet 、 SSH 、 FTP 的伺服器。 9 | -------------------------------------------------------------------------------- /advanced_network/how_connect.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /advanced_network/port_mapping.md: -------------------------------------------------------------------------------- 1 | ## 映射容器連接埠到宿主主機的實作 2 | 3 | 預設情況下,容器可以主動存取到外部網路的連線,但是外部網路無法存取到容器。 4 | ### 容器存取外部實作 5 | 容器所有到外部網路的連線,源位址都會被NAT成本地系統的IP位址。這是使用 `iptables` 的源位址偽裝操作實作的。 6 | 7 | 查看主機的 NAT 規則。 8 | ``` 9 | $ sudo iptables -t nat -nL 10 | ... 11 | Chain POSTROUTING (policy ACCEPT) 12 | target prot opt source destination 13 | MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 14 | ... 15 | ``` 16 | 其中,上述規則將所有源位址在 `172.17.0.0/16` 網段,目標位址為其他網段(外部網路)的流量動態偽裝為從系統網卡發出。MASQUERADE 跟傳統 SNAT 的好處是它能動態從網卡取得位址。 17 | 18 | ### 外部存取容器實作 19 | 20 | 容器允許外部存取,可以在 `docker run` 時候透過 `-p` 或 `-P` 參數來啟用。 21 | 22 | 不管用那種辦法,其實也是在本地的 `iptable` 的 nat 表中新增相應的規則。 23 | 24 | 使用 `-P` 時: 25 | ``` 26 | $ iptables -t nat -nL 27 | ... 28 | Chain DOCKER (2 references) 29 | target prot opt source destination 30 | DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80 31 | ``` 32 | 33 | 使用 `-p 80:80` 時: 34 | ``` 35 | $ iptables -t nat -nL 36 | Chain DOCKER (2 references) 37 | target prot opt source destination 38 | DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80 39 | ``` 40 | 注意: 41 | * 這裡的規則映射了 0.0.0.0,意味著將接受主機來自所有介面的流量。使用者可以透過 `-p IP:host_port:container_port` 或 `-p 42 | IP::port` 來指定允許存取容器的主機上的 IP、介面等,以制定更嚴格的規則。 43 | * 如果希望永久綁定到某個固定的 IP 位址,可以在 Docker 設定檔案 `/etc/default/docker` 中指定 `DOCKER_OPTS="--ip=IP_ADDRESS"`,之後重啟 Docker 服務即可生效。 44 | -------------------------------------------------------------------------------- /advanced_network/ptp.md: -------------------------------------------------------------------------------- 1 | ## 示例:建立一個點到點連線 2 | 預設情況下,Docker 會將所有容器連線到由 `docker0` 提供的虛擬子網中。 3 | 4 | 使用者有時候需要兩個容器之間可以直連通訊,而不用透過主機橋接器進行橋接。 5 | 6 | 解決辦法很簡單:建立一對 `peer` 介面,分別放到兩個容器中,設定成點到點鏈路類型即可。 7 | 8 | 首先啟動 2 個容器: 9 | ``` 10 | $ sudo docker run -i -t --rm --net=none base /bin/bash 11 | root@1f1f4c1f931a:/# 12 | $ sudo docker run -i -t --rm --net=none base /bin/bash 13 | root@12e343489d2f:/# 14 | ``` 15 | 16 | 找到程式號,然後建立網路命名空間的跟蹤檔案。 17 | ``` 18 | $ sudo docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a 19 | 2989 20 | $ sudo docker inspect -f '{{.State.Pid}}' 12e343489d2f 21 | 3004 22 | $ sudo mkdir -p /var/run/netns 23 | $ sudo ln -s /proc/2989/ns/net /var/run/netns/2989 24 | $ sudo ln -s /proc/3004/ns/net /var/run/netns/3004 25 | ``` 26 | 27 | 建立一對 `peer` 介面,然後設定路由 28 | ``` 29 | $ sudo ip link add A type veth peer name B 30 | 31 | $ sudo ip link set A netns 2989 32 | $ sudo ip netns exec 2989 ip addr add 10.1.1.1/32 dev A 33 | $ sudo ip netns exec 2989 ip link set A up 34 | $ sudo ip netns exec 2989 ip route add 10.1.1.2/32 dev A 35 | 36 | $ sudo ip link set B netns 3004 37 | $ sudo ip netns exec 3004 ip addr add 10.1.1.2/32 dev B 38 | $ sudo ip netns exec 3004 ip link set B up 39 | $ sudo ip netns exec 3004 ip route add 10.1.1.1/32 dev B 40 | ``` 41 | 現在這 2 個容器就可以相互 ping 通,並成功建立連線。點到點鏈路不需要子網和子網遮罩。 42 | 43 | 此外,也可以不指定 `--net=none` 來建立點到點鏈路。這樣容器還可以透過原先的網路來通訊。 44 | 45 | 利用類似的辦法,可以建立一個只跟主機通訊的容器。但是一般情況下,更推薦使用 `--icc=false` 來關閉容器之間的通訊。 46 | -------------------------------------------------------------------------------- /advanced_network/quick_guide.md: -------------------------------------------------------------------------------- 1 | ## 快速設定指南 2 | 3 | 下面是一個跟 Docker 網路相關的命令列表。 4 | 5 | 其中有些命令選項只有在 Docker 服務啟動的時候才能設定,而且不能馬上生效。 6 | * `-b BRIDGE or --bridge=BRIDGE` --指定容器掛載的橋接器 7 | * `--bip=CIDR` --定制 docker0 的遮罩 8 | * `-H SOCKET... or --host=SOCKET...` --Docker 服務端接收命令的通道 9 | * `--icc=true|false` --是否支援容器之間進行通訊 10 | * `--ip-forward=true|false` --請看下文容器之間的通訊 11 | * `--iptables=true|false` --禁止 Docker 新增 iptables 規則 12 | * `--mtu=BYTES` --容器網路中的 MTU 13 | 14 | 下面2個命令選項既可以在啟動服務時指定,也可以 Docker 容器啟動(`docker run`)時候指定。在 Docker 服務啟動的時候指定則會成為預設值,後面執行 `docker run` 時可以覆蓋設定的預設值。 15 | * `--dns=IP_ADDRESS...` --使用指定的DNS伺服器 16 | * `--dns-search=DOMAIN...` --指定DNS搜尋域 17 | 18 | 最後這些選項只有在 `docker run` 執行時使用,因為它是針對容器的特性內容。 19 | * `-h HOSTNAME or --hostname=HOSTNAME` --設定容器主機名 20 | * `--link=CONTAINER_NAME:ALIAS` --新增到另一個容器的連線 21 | * `--net=bridge|none|container:NAME_or_ID|host` --設定容器的橋接模式 22 | * `-p SPEC or --publish=SPEC` --映射容器連接埠到宿主主機 23 | * `-P or --publish-all=true|false` --映射容器所有連接埠到宿主主機 24 | -------------------------------------------------------------------------------- /appendix_command/README.md: -------------------------------------------------------------------------------- 1 | # Docker命令查詢 2 | 3 | ## 基本語法 4 | 5 | docker [OPTIONS] COMMAND [arg...] 6 | 一般來說,Docker 命令可以用來管理 daemon,或者透過 CLI 命令管理映像檔和容器。可以透過 `man docker` 來查看這些命令。 7 | 8 | ## 選項 9 | 10 | -D=true|false 11 | 使用 debug 模式。預設為 false。 12 | 13 | -H, --host=[unix:///var/run/docker.sock]: tcp://[host:port]來綁定或者 unix://[/path/to/socket] 來使用。 14 | 在 daemon 模式下綁定的 socket,透過一個或多個 tcp://host:port 、 unix:///path/to/socket 、 fd://* or fd://socketfd 來指定。 15 | 16 | --api-enable-cors=true|false 17 | 在遠端 API 中啟用 CORS 頭。預設為 false。 18 | 19 | -b="" 20 | 將容器掛載到一個已存在的橋接器上。指定為 'none' 時則禁用容器的網路。 21 | 22 | --bip="" 23 | 讓動態建立的 docker0 採用指定的 CIDR 位址; 與 -b 選項互斥。 24 | 25 | -d=true|false 26 | 使用 daemon 模式。預設為 false。 27 | 28 | --dns="" 29 | 讓 Docker 使用指定的 DNS 伺服器。 30 | 31 | -g="" 32 | 指定 Docker 執行時的 root 路徑。預設為 /var/lib/docker。 33 | 34 | --icc=true|false 35 | 啟用容器間通訊。預設為 true。 36 | 37 | --ip="" 38 | 綁定連接埠時候的預設 IP 位址。預設為 0.0.0.0。 39 | 40 | --iptables=true|false 41 | 禁止 Docker 新增 iptables 規則。預設為 true。 42 | 43 | --mtu=VALUE 44 | 指定容器網路的 mtu。預設為 1500。 45 | 46 | -p="" 47 | 指定 daemon 的 PID 檔案路徑。預設為 /var/run/docker.pid。 48 | 49 | -s="" 50 | 強制 Docker 執行時使用指定的儲存驅動。 51 | 52 | -v=true|false 53 | 輸出版本資訊並退出。預設值為 false。 54 | 55 | --selinux-enabled=true|false 56 | 啟用 SELinux 支援。預設值為 false。SELinux 目前不支援 BTRFS 儲存驅動。 57 | 58 | ## 命令 59 | 60 | Docker 的命令可以採用 `docker-CMD` 或者 `docker CMD` 的方式執行。兩者一致。 61 | 62 | docker-attach(1) 63 | 依附到一個正在執行的容器中。 64 | 65 | docker-build(1) 66 | 從一個 Dockerfile 建立一個映像檔 67 | 68 | docker-commit(1) 69 | 從一個容器的修改中建立一個新的映像檔 70 | 71 | docker-cp(1) 72 | 從容器中複製檔案到宿主系統中 73 | 74 | docker-diff(1) 75 | 檢查一個容器檔案系統的修改 76 | 77 | docker-events(1) 78 | 從服務端取得實時的事件 79 | 80 | docker-export(1) 81 | 匯出容器內容為一個 tar 包 82 | 83 | docker-history(1) 84 | 顯示一個映像檔的歷史 85 | 86 | docker-images(1) 87 | 列出存在的映像檔 88 | 89 | docker-import(1) 90 | 匯入一個檔案(典型為 tar 包)路徑或目錄來建立一個映像檔 91 | 92 | docker-info(1) 93 | 顯示一些相關的系統資訊 94 | 95 | docker-inspect(1) 96 | 顯示一個容器的底層具體資訊。 97 | 98 | docker-kill(1) 99 | 關閉一個執行中的容器(包括程式和所有資源) 100 | 101 | docker-load(1) 102 | 從一個 tar 包中載入一個映像檔 103 | 104 | docker-login(1) 105 | 註冊或登錄到一個 Docker 的倉庫伺服器 106 | 107 | docker-logout(1) 108 | 從 Docker 的倉庫伺服器登出 109 | 110 | docker-logs(1) 111 | 取得容器的 log 資訊 112 | 113 | docker-pause(1) 114 | 暫停一個容器中的所有程式 115 | 116 | docker-port(1) 117 | 查找一個 nat 到一個私有網口的公共口 118 | 119 | docker-ps(1) 120 | 列出容器 121 | 122 | docker-pull(1) 123 | 從一個Docker的倉庫伺服器下拉一個映像檔或倉庫 124 | 125 | docker-push(1) 126 | 將一個映像檔或者倉庫推送到一個 Docker 的註冊伺服器 127 | 128 | docker-restart(1) 129 | 重新啟動一個執行中的容器 130 | 131 | docker-rm(1) 132 | 刪除指定的數個容器 133 | 134 | docker-rmi(1) 135 | 刪除指定的數個映像檔 136 | 137 | docker-run(1) 138 | 建立一個新容器,並在其中執行指定命令 139 | 140 | docker-save(1) 141 | 保存一個映像檔為 tar 包檔案 142 | 143 | docker-search(1) 144 | 在 Docker index 中搜尋一個映像檔 145 | 146 | docker-start(1) 147 | 啟動一個容器 148 | 149 | docker-stop(1) 150 | 終止一個執行中的容器 151 | 152 | docker-tag(1) 153 | 為一個映像檔打標籤 154 | 155 | docker-top(1) 156 | 查看一個容器中的正在執行的程式資訊 157 | 158 | docker-unpause(1) 159 | 將一個容器內所有的程式從暫停狀態中恢復 160 | 161 | docker-version(1) 162 | 輸出 Docker 的版本資訊 163 | 164 | docker-wait(1) 165 | 阻塞直到一個容器終止,然後輸出它的退出符 166 | 167 | ##一張圖總結 Docker 的命令 168 | 169 | ![命令周期](../_images/cmd_logic.png) 170 | -------------------------------------------------------------------------------- /appendix_repo/README.md: -------------------------------------------------------------------------------- 1 | # 常見倉庫介紹 2 | 本章將介紹常見的一些倉庫和映像檔的功能,使用方法和建立它們的 Dockerfile 等。包括 Ubuntu、CentOS、MySQL、MongoDB、Redis、Nginx、Wordpress、Node.js 等。 3 | -------------------------------------------------------------------------------- /appendix_repo/centos.md: -------------------------------------------------------------------------------- 1 | ## [CentOS](https://registry.hub.docker.com/_/centos/) 2 | 3 | ### 基本訊息 4 | [CentOS](https://en.wikipedia.org/wiki/CentOS) 是流行的 Linux 發行版,其軟體套件大多跟 RedHat 系列保持一致。 5 | 該倉庫提供了 CentOS 從 5 ~ 7 各個版本的映像檔。 6 | 7 | ### 使用方法 8 | 預設會啟動一個最小化的 CentOS 環境。 9 | ``` 10 | $ sudo docker run --name some-centos -i -t centos bash 11 | bash-4.2# 12 | ``` 13 | 14 | ### Dockerfile 15 | * [CentOS 5 版本](https://github.com/CentOS/sig-cloud-instance-images/blob/2e5a9c4e8b7191b393822e4b9e98820db5638a77/docker/Dockerfile) 16 | * [CentOS 6 版本](https://github.com/CentOS/sig-cloud-instance-images/blob/8717e33ea5432ecb33d7ecefe8452a973715d037/docker/Dockerfile) 17 | * [CentOS 7 版本](https://github.com/CentOS/sig-cloud-instance-images/blob/af7a1b9f8f30744360a10fe44c53a1591bef26f9/docker/Dockerfile) 18 | -------------------------------------------------------------------------------- /appendix_repo/mongodb.md: -------------------------------------------------------------------------------- 1 | ## [MongoDB](https://registry.hub.docker.com/_/mongo/) 2 | 3 | ### 基本訊息 4 | [MongoDB](https://en.wikipedia.org/wiki/MongoDB) 是開源的 NoSQL 資料庫實作。 5 | 該倉庫提供了 MongoDB 2.2 ~ 2.7 各個版本的映像檔。 6 | 7 | ### 使用方法 8 | 預設會在 `27017` 連接埠啟動資料庫。 9 | ``` 10 | $ sudo docker run --name some-mongo -d mongo 11 | ``` 12 | 13 | 使用其他應用連線到容器,可以用 14 | ``` 15 | $ sudo docker run --name some-app --link some-mongo:mongo -d application-that-uses-mongo 16 | ``` 17 | 或者透過 `mongo` 18 | ``` 19 | $ sudo docker run -it --link some-mongo:mongo --rm mongo sh -c 'exec mongo "$MONGO_PORT_27017_TCP_ADDR:$MONGO_PORT_27017_TCP_PORT/test"' 20 | ``` 21 | 22 | ### Dockerfile 23 | * [2.2 版本](https://github.com/docker-library/mongo/blob/77c841472ccb6cc87fea1218269d097405edc6cb/2.2/Dockerfile) 24 | * [2.4 版本](https://github.com/docker-library/mongo/blob/807078cb7b5f0289f6dabf9f6875d5318122bc30/2.4/Dockerfile) 25 | * [2.6 版本](https://github.com/docker-library/mongo/blob/77c841472ccb6cc87fea1218269d097405edc6cb/2.6/Dockerfile) 26 | * [2.7 版本](https://github.com/docker-library/mongo/blob/807078cb7b5f0289f6dabf9f6875d5318122bc30/2.7/Dockerfile) 27 | -------------------------------------------------------------------------------- /appendix_repo/mysql.md: -------------------------------------------------------------------------------- 1 | ## [MySQL](https://registry.hub.docker.com/_/mysql/) 2 | 3 | ### 基本訊息 4 | [MySQL](https://en.wikipedia.org/wiki/MySQL) 是開源的關聯資料庫實作。 5 | 該倉庫提供了 MySQL 各個版本的映像檔,包括 5.6 系列、5.7 系列等。 6 | 7 | ### 使用方法 8 | 預設會在 `3306` 連接埠啟動資料庫。 9 | ``` 10 | $ sudo docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql 11 | ``` 12 | 之後就可以使用其它應用來連線到該容器。 13 | ``` 14 | $ sudo docker run --name some-app --link some-mysql:mysql -d application-that-uses-mysql 15 | ``` 16 | 或者透過 `mysql`。 17 | ``` 18 | $ sudo docker run -it --link some-mysql:mysql --rm mysql sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"' 19 | ``` 20 | 21 | ### Dockerfile 22 | * [5.6 版本](https://github.com/docker-library/mysql/blob/7461a52b43f06839a4d8723ae8841f4cb616b3d0/5.6/Dockerfile) 23 | * [5.7 版本](https://github.com/docker-library/mysql/blob/7461a52b43f06839a4d8723ae8841f4cb616b3d0/5.7/Dockerfile) 24 | -------------------------------------------------------------------------------- /appendix_repo/nginx.md: -------------------------------------------------------------------------------- 1 | ## [Nginx](https://registry.hub.docker.com/_/nginx/) 2 | 3 | ### 基本訊息 4 | [Nginx](https://en.wikipedia.org/wiki/Nginx) 是開源的、有效率的 Web 伺服器實作,支援 HTTP、HTTPS、SMTP、POP3、IMAP 等協議。 5 | 該倉庫提供了 Nginx 1.0 ~ 1.7 各個版本的映像檔。 6 | 7 | ### 使用方法 8 | 下面的命令將作為一個靜態頁面伺服器啟動。 9 | ``` 10 | $ sudo docker run --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx 11 | ``` 12 | 使用者也可以不使用這種映射方式,透過利用 Dockerfile 來直接將靜態頁面內容放到映像檔中,內容為 13 | ``` 14 | FROM nginx 15 | COPY static-html-directory /usr/share/nginx/html 16 | ``` 17 | 之後建立新的映像檔,並啟動一個容器。 18 | ``` 19 | $ sudo docker build -t some-content-nginx . 20 | $ sudo docker run --name some-nginx -d some-content-nginx 21 | ``` 22 | 開放連接埠,並映射到本地的 `8080` 連接埠。 23 | ``` 24 | sudo docker run --name some-nginx -d -p 8080:80 some-content-nginx 25 | ``` 26 | 27 | Nginx的預設設定檔案路徑為 `/etc/nginx/nginx.conf`,可以透過映射它來使用本地的設定檔案,例如 28 | ``` 29 | docker run --name some-nginx -v /some/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx 30 | ``` 31 | 使用設定檔案時,為了在容器中正常執行,需要保持 `daemon off;`。 32 | 33 | ### Dockerfile 34 | * [1 ~ 1.7 版本](https://github.com/nginxinc/docker-nginx/blob/3713a0157083eb4776e71f5a5aef4b2a5bc03ab1/Dockerfile) 35 | -------------------------------------------------------------------------------- /appendix_repo/nodejs.md: -------------------------------------------------------------------------------- 1 | ## [Node.js](https://registry.hub.docker.com/_/node/) 2 | 3 | ### 基本訊息 4 | [Node.js](https://en.wikipedia.org/wiki/Node.js)是基於 JavaScript 的可擴展服務端和網路軟體開發平台。 5 | 該倉庫提供了 Node.js 0.8 ~ 0.11 各個版本的映像檔。 6 | 7 | ### 使用方法 8 | 在專案中建立一個 Dockerfile。 9 | ``` 10 | FROM node:0.10-onbuild 11 | # replace this with your application's default port 12 | EXPOSE 8888 13 | ``` 14 | 然後建立映像檔,並啟動容器 15 | ``` 16 | $ sudo docker build -t my-nodejs-app 17 | $ sudo docker run -it --rm --name my-running-app my-nodejs-app 18 | ``` 19 | 20 | 也可以直接執行一個簡單容器。 21 | ``` 22 | $ sudo docker run -it --rm --name my-running-script -v "$(pwd)":/usr/src/myapp -w /usr/src/myapp node:0.10 node your-daemon-or-script.js 23 | ``` 24 | 25 | ### Dockerfile 26 | * [0.8 版本](https://github.com/docker-library/node/blob/d017d679e92e84a810c580cdb29fcdbba23c2bb9/0.8/Dockerfile) 27 | * [0.10 版本](https://github.com/docker-library/node/blob/913a225f2fda34d6a811fac1466e4f09f075fcf6/0.10/Dockerfile) 28 | * [0.11 版本](https://github.com/docker-library/node/blob/d017d679e92e84a810c580cdb29fcdbba23c2bb9/0.11/Dockerfile) 29 | -------------------------------------------------------------------------------- /appendix_repo/redis.md: -------------------------------------------------------------------------------- 1 | ## [Redis](https://registry.hub.docker.com/_/redis/) 2 | 3 | ### 基本訊息 4 | [Redis](https://en.wikipedia.org/wiki/Redis) 是開源的記憶體 Key-Value 資料庫實作。 5 | 6 | ### 使用方法 7 | 預設會在 `6379` 連接埠啟動資料庫。 8 | ``` 9 | $ sudo docker run --name some-redis -d redis 10 | ``` 11 | 另外還可以啟用 [持久儲存](http://redis.io/topics/persistence)。 12 | ``` 13 | $ sudo docker run --name some-redis -d redis redis-server --appendonly yes 14 | ``` 15 | 預設資料儲存位置在 `VOLUME/data`。可以使用 `--volumes-from some-volume-container` 或 `-v /docker/host/dir:/data` 將資料存放到本地。 16 | 17 | 使用其他應用連線到容器,可以用 18 | ``` 19 | $ sudo docker run --name some-app --link some-redis:redis -d application-that-uses-redis 20 | ``` 21 | 或者透過 `redis-cli` 22 | ``` 23 | $ sudo docker run -it --link some-redis:redis --rm redis sh -c 'exec redis-cli -h "$REDIS_PORT_6379_TCP_ADDR" -p "$REDIS_PORT_6379_TCP_PORT"' 24 | ``` 25 | 26 | ### Dockerfile 27 | * [2.6 版本](https://github.com/docker-library/redis/blob/02d9cd887a4e0d50db4bb085eab7235115a6fe4a/2.6.17/Dockerfile) 28 | * [最新 2.8 版本](https://github.com/docker-library/redis/blob/d0665bb1bbddd4cc035dbc1fc774695fa534d648/2.8.13/Dockerfile) 29 | -------------------------------------------------------------------------------- /appendix_repo/ubuntu.md: -------------------------------------------------------------------------------- 1 | ## [Ubuntu](https://registry.hub.docker.com/_/ubuntu/) 2 | 3 | ### 基本訊息 4 | [Ubuntu](https://en.wikipedia.org/wiki/Ubuntu) 是流行的 Linux 發行版,其內建軟體版本往往較新一些。 5 | 該倉庫提供了 Ubuntu 從 12.04 ~ 14.10 各個版本的映像檔。 6 | 7 | ### 使用方法 8 | 預設會啟動一個最小化的 Ubuntu 環境。 9 | ``` 10 | $ sudo docker run --name some-ubuntu -i -t ubuntu 11 | root@523c70904d54:/# 12 | ``` 13 | 14 | ### Dockerfile 15 | * [12.04 版本](https://github.com/tianon/docker-brew-ubuntu-core/blob/2b105575647a7e2030ff344d427c3920b89e17a9/precise/Dockerfile) 16 | * [14.04 版本](https://github.com/tianon/docker-brew-ubuntu-core/blob/2b105575647a7e2030ff344d427c3920b89e17a9/trusty/Dockerfile) 17 | * [14.10 版本](https://github.com/tianon/docker-brew-ubuntu-core/blob/2b105575647a7e2030ff344d427c3920b89e17a9/utopic/Dockerfile) 18 | -------------------------------------------------------------------------------- /appendix_repo/wordpress.md: -------------------------------------------------------------------------------- 1 | ## [WordPress](https://registry.hub.docker.com/_/wordpress/) 2 | 3 | ### 基本訊息 4 | [WordPress](https://en.wikipedia.org/wiki/WordPress) 是開源的 Blog 和內容管理系統框架,它基於 PHP 和 MySQL。 5 | 該倉庫提供了 WordPress 4.0 版本的映像檔。 6 | 7 | ### 使用方法 8 | 啟動容器需要 MySQL 的支援,預設連接埠為 `80`。 9 | ``` 10 | $ sudo docker run --name some-wordpress --link some-mysql:mysql -d wordpress 11 | ``` 12 | 啟動 WordPress 容器時可以指定的一些環境參數包括 13 | * `-e WORDPRESS_DB_USER=...` 預設為 “root” 14 | * `-e WORDPRESS_DB_PASSWORD=...` 預設為連線 mysql 容器的環境變量 `MYSQL_ROOT_PASSWORD` 的值 15 | * `-e WORDPRESS_DB_NAME=...` 預設為 “wordpress” 16 | * `-e WORDPRESS_AUTH_KEY=...` 、 `-e WORDPRESS_SECURE_AUTH_KEY=...` 、 `-e WORDPRESS_LOGGED_IN_KEY=...` 、 `-e WORDPRESS_NONCE_KEY=...` 、 `-e WORDPRESS_AUTH_SALT=...` 、 `-e WORDPRESS_SECURE_AUTH_SALT=...` 、 `-e WORDPRESS_LOGGED_IN_SALT=...` 、 `-e WORDPRESS_NONCE_SALT=...` 預設為隨機 SHA1 串 17 | 18 | ### Dockerfile 19 | * [4.0 版本](https://github.com/docker-library/wordpress/blob/aee00669e7c43f435f021cb02871bffd63d5677a/Dockerfile) 20 | -------------------------------------------------------------------------------- /appendix_resources/README.md: -------------------------------------------------------------------------------- 1 | # 資源鏈接 2 | * Docker 主站台: https://www.docker.com 3 | * Docker 註冊中心 API: http://docs.docker.com/reference/api/registry_api/ 4 | * Docker Hub API: http://docs.docker.com/reference/api/docker-io_api/ 5 | * Docker 遠端應用 API: http://docs.docker.com/reference/api/docker_remote_api/ 6 | * Dockerfile 參考: https://docs.docker.com/reference/builder/ 7 | * Dockerfile 最佳實踐: https://docs.docker.com/articles/dockerfile_best-practices/ 8 | -------------------------------------------------------------------------------- /basic_concept/README.md: -------------------------------------------------------------------------------- 1 | # 基本概念 2 | Docker 包括三個基本概念 3 | 4 | * 映像檔(Image) 5 | * 容器(Container) 6 | * 倉庫(Repository) 7 | 8 | 理解了這三個概念,就理解了 Docker 的整個生命週期。 9 | 10 | -------------------------------------------------------------------------------- /basic_concept/container.md: -------------------------------------------------------------------------------- 1 | ## Docker 容器 2 | 3 | Docker 利用容器來執行應用。 4 | 5 | 容器是從映像檔建立的執行實例。它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的、保證安全的平台。 6 | 7 | 可以把容器看做是一個簡易版的 Linux 環境(包括 root 使用者權限、程式空間、使用者空間和網路空間等)和在其中執行的應用程式。 8 | 9 | \*註:映像檔是唯讀的,容器在啟動的時候建立一層可寫層作為最上層。 10 | -------------------------------------------------------------------------------- /basic_concept/image.md: -------------------------------------------------------------------------------- 1 | ## Docker 映像檔 2 | Docker 映像檔就是一個唯讀的模板。 3 | 4 | 例如:一個映像檔可以包含一個完整的 ubuntu 作業系統環境,裡面僅安裝了 Apache 或使用者需要的其它應用程式。 5 | 6 | 映像檔可以用來建立 Docker 容器。 7 | 8 | Docker 提供了一個很簡單的機制來建立映像檔或者更新現有的映像檔,使用者甚至可以直接從其他人那裡下載一個已經做好的映像檔來直接使用。 9 | -------------------------------------------------------------------------------- /basic_concept/repository.md: -------------------------------------------------------------------------------- 1 | ## Docker 倉庫 2 | 3 | 倉庫是集中存放映像檔檔案的場所。有時候會把倉庫和倉庫註冊伺服器(Registry)混為一談,並不嚴格區分。實際上,倉庫註冊伺服器上往往存放著多個倉庫,每個倉庫中又包含了多個映像檔,每個映像檔有不同的標籤(tag)。 4 | 5 | 倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。 6 | 7 | 最大的公開倉庫是 [Docker Hub](https://hub.docker.com),存放了數量龐大的映像檔供使用者下載。 8 | 大陸的公開倉庫包括 [Docker Pool](http://www.dockerpool.com) 等,可以提供大陸使用者更穩定快速的存取。 9 | 10 | 當然,使用者也可以在本地網路內建立一個私有倉庫。 11 | 12 | 當使用者建立了自己的映像檔之後就可以使用 `push` 命令將它上傳到公有或者私有倉庫,這樣下次在另外一台機器上使用這個映像檔時候,只需要從倉庫上 `pull` 下來就可以了。 13 | 14 | *註:Docker 倉庫的概念跟 [Git](http://git-scm.com) 類似,註冊伺服器可以理解為 GitHub 這樣的託管服務。 15 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /cases/README.md: -------------------------------------------------------------------------------- 1 | #實戰案例 2 | 介紹一些典型的應用情境和案例。 3 | -------------------------------------------------------------------------------- /cases/container_connect.md: -------------------------------------------------------------------------------- 1 | ## 多臺物理主機之間的容器互聯(暴露容器到真實網路中) 2 | Docker 預設的橋接網卡是 docker0。它只會在本機橋接所有的容器網卡,舉例來說容器的虛擬網卡在主機上看一般叫做 veth*** 而 Docker 只是把所有這些網卡橋接在一起,以下: 3 | ``` 4 | [root@opnvz ~]# brctl show 5 | bridge name bridge id STP enabled interfaces 6 | docker0 8000.56847afe9799 no veth0889 7 | veth3c7b 8 | veth4061 9 | ``` 10 | 在容器中看到的位址一般是像下面這樣的位址: 11 | ``` 12 | root@ac6474aeb31d:~# ip a 13 | 1: lo: mtu 1500 qdisc noqueue state UNKNOWN group default 14 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 15 | inet 127.0.0.1/8 scope host lo 16 | valid_lft forever preferred_lft forever 17 | inet6 ::1/128 scope host 18 | valid_lft forever preferred_lft forever 19 | 11: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 20 | link/ether 4a:7d:68:da:09:cf brd ff:ff:ff:ff:ff:ff 21 | inet 172.17.0.3/16 scope global eth0 22 | valid_lft forever preferred_lft forever 23 | inet6 fe80::487d:68ff:feda:9cf/64 scope link 24 | valid_lft forever preferred_lft forever 25 | ``` 26 | 這樣就可以把這個網路看成是一個私有的網路,透過 nat 連線外網,如果要讓外網連線到容器中,就需要做連接埠映射,即 -p 參數。 27 | 28 | 如果在企業內部應用,或者做多個物理主機的集群,可能需要將多個物理主機的容器組到一個物理網路中來,那麽就需要將這個橋接器橋接到我們指定的網卡上。 29 | 30 | ### 拓撲圖 31 | 主機 A 和主機 B 的網卡一都連著物理交換機的同一個 vlan 101,這樣橋接器一和橋接器三就相當於在同一個物理網路中了,而容器一、容器三、容器四也在同一物理網路中了,他們之間可以相互通訊,而且可以跟同一 vlan 中的其他物理機器互聯。 32 | ![物理拓撲圖](../_images/container_connect_topology.png) 33 | 34 | ### ubuntu 示例 35 | 下面以 ubuntu 為例建立多個主機的容器聯網: 36 | 建立自己的橋接器,編輯 /etc/network/interface 檔案 37 | ``` 38 | auto br0 39 | iface br0 inet static 40 | address 192.168.7.31 41 | netmask 255.255.240.0 42 | gateway 192.168.7.254 43 | bridge_ports em1 44 | bridge_stp off 45 | dns-nameservers 8.8.8.8 192.168.6.1 46 | ``` 47 | 將 Docker 的預設橋接器綁定到這個新建的 br0 上面,這樣就將這臺機器上容器綁定到 em1 這個網卡所對應的物理網路上了。 48 | 49 | ubuntu 修改 /etc/default/docker 檔案,新增最後一行內容 50 | 51 | ``` 52 | # Docker Upstart and SysVinit configuration file 53 | # Customize location of Docker binary (especially for development testing). 54 | #DOCKER="/usr/local/bin/docker" 55 | # Use DOCKER_OPTS to modify the daemon startup options. 56 | #DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4" 57 | 58 | # If you need Docker to use an HTTP proxy, it can also be specified here. 59 | #export http_proxy="http://127.0.0.1:3128/" 60 | 61 | # This is also a handy place to tweak where Docker's temporary files go. 62 | #export TMPDIR="/mnt/bigdrive/docker-tmp" 63 | 64 | DOCKER_OPTS="-b=br0" 65 | ``` 66 | 67 | 在啟動 Docker 的時候 使用 -b 參數 將容器綁定到物理網路上。重啟 Docker 服務後,再進入容器可以看到它已經綁定到你的物理網路上了。 68 | 69 | ``` 70 | root@ubuntudocker:~# docker ps 71 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 72 | 58b043aa05eb desk_hz:v1 "/startup.sh" 5 days ago Up 2 seconds 5900/tcp, 6080/tcp, 22/tcp yanlx 73 | root@ubuntudocker:~# brctl show 74 | bridge name bridge id STP enabled interfaces 75 | br0 8000.7e6e617c8d53 no em1 76 | vethe6e5 77 | ``` 78 | 這樣就直接把容器暴露到物理網路上了,多臺物理主機的容器也可以相網路了。需要注意的是,這樣就需要自己來保證容器的網路安全了。 79 | -------------------------------------------------------------------------------- /cases/environment.md: -------------------------------------------------------------------------------- 1 | ## 標準化開發測試和生產環境 2 | 對於大部分企業來說,搭建 PaaS 既沒有那個精力,也沒那個必要,用 Docker 做個人的 sandbox 用處又小了點。 3 | 4 | 可以用 Docker 來標準化開發、測試、生產環境。 5 | 6 | 7 | ![企業應用結構](../_images/enterprise_usage.png) 8 | 9 | 10 | Docker 占用資源小,在一臺 E5 128 G 記憶體的伺服器上部署 100 個容器都綽綽有余,可以單獨抽一個容器或者直接在宿主物理主機上部署 samba,利用 samba 的 home 分享方案將每個使用者的 home 目錄映射到開發中心和測試部門的 Windows 機器上。 11 | 12 | 針對某個專案組,由架構師搭建好一個標準的容器環境供專案組和測試部門使用,每個開發工程師可以擁有自己單獨的容器,透過 `docker run -v` 將使用者的 home 目錄映射到容器中。需要提交測試時,只需要將代碼移交給測試部門,然後分配一個容器使用 `-v` 載入測試部門的 home 目錄啟動即可。這樣,在公司內部的開發、測試基本就統一了,不會出現開發部門提交的代碼,測試部門部署不了的問題。 13 | 14 | 測試部門發布測試透過的報告後,架構師再一次檢測容器環境,就可以直接交由部署工程師將代碼和容器分別部署到生產環境中了。這種方式的部署橫向效能的擴展性也極好。 15 | -------------------------------------------------------------------------------- /cases/supervisor.md: -------------------------------------------------------------------------------- 1 | ## 使用 Supervisor 來管理程式 2 | Docker 容器在啟動的時候開啟單個程式,比如,一個 ssh 或者 apache 的 daemon 服務。但我們經常需要在一個機器上開啟多個服務,這可以有很多方法,最簡單的就是把多個啟動命令方到一個啟動腳本裡面,啟動的時候直接啟動這個腳本,另外就是安裝程式管理工具。 3 | 4 | 本小節將使用程式管理工具 supervisor 來管理容器中的多個程式。使用 Supervisor 可以更好的控制、管理、重啟我們希望執行的程式。在這裡我們演示一下如何同時使用 ssh 和 apache 服務。 5 | 6 | ### 設定 7 | 首先建立一個 Dockerfile,內容和各部分的解釋以下。 8 | ``` 9 | FROM ubuntu:13.04 10 | MAINTAINER examples@docker.com 11 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 12 | RUN apt-get update 13 | RUN apt-get upgrade -y 14 | ``` 15 | 16 | ### 安裝 supervisor 17 | 安裝 ssh、apache 和 supervisor。 18 | ``` 19 | RUN apt-get install -y openssh-server apache2 supervisor 20 | RUN mkdir -p /var/run/sshd 21 | RUN mkdir -p /var/log/supervisor 22 | ``` 23 | 24 | 這裡安裝 3 個軟體,還建立了 2 個 ssh 和 supervisor 服務正常執行所需要的目錄。 25 | ``` 26 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 27 | ``` 28 | 新增 supervisord 的設定檔案,並複製設定檔案到對應目錄下面。 29 | 30 | ``` 31 | EXPOSE 22 80 32 | CMD ["/usr/bin/supervisord"] 33 | ``` 34 | 這裡我們映射了 22 和 80 連接埠,使用 supervisord 的可執行路徑啟動服務。 35 | 36 | 37 | ### supervisor設定檔案內容 38 | ``` 39 | [supervisord] 40 | nodaemon=true 41 | [program:sshd] 42 | command=/usr/sbin/sshd -D 43 | 44 | [program:apache2] 45 | command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND" 46 | ``` 47 | 設定檔案包含目錄和程式,第一段 supervsord 設定軟體本身,使用 nodaemon 參數來執行。第二段包含要控制的 2 個服務。每一段包含一個服務的目錄和啟動這個服務的命令。 48 | 49 | ### 使用方法 50 | 建立映像檔。 51 | ``` 52 | $ sudo docker build -t test/supervisord . 53 | ``` 54 | 啟動 supervisor 容器。 55 | ``` 56 | $ sudo docker run -p 22 -p 80 -t -i test/supervisords 57 | 2013-11-25 18:53:22,312 CRIT Supervisor running as root (no user in config file) 58 | 2013-11-25 18:53:22,312 WARN Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing 59 | 2013-11-25 18:53:22,342 INFO supervisord started with pid 1 60 | 2013-11-25 18:53:23,346 INFO spawned: 'sshd' with pid 6 61 | 2013-11-25 18:53:23,349 INFO spawned: 'apache2' with pid 7 62 | ``` 63 | 使用 `docker run` 來啟動我們建立的容器。使用多個 `-p` 來映射多個連接埠,這樣我們就能同時存取 ssh 和 apache 服務了。 64 | 65 | 可以使用這個方法建立一個只有 ssh 服務的基礎映像檔,之後建立映像檔可以使用這個映像檔為基礎來建立 66 | -------------------------------------------------------------------------------- /cases/tomcat.md: -------------------------------------------------------------------------------- 1 | ## 建立 tomcat/weblogic 集群 2 | ### 安裝 tomcat 映像檔 3 | 準備好需要的 jdk、tomcat 等軟體放到 home 目錄下面,啟動一個容器 4 | ``` 5 | docker run -t -i -v /home:/opt/data --name mk_tomcat ubuntu /bin/bash 6 | ``` 7 | 這條命令掛載本地 home 目錄到容器的 /opt/data 目錄,容器內目錄若不存在,則會自動建立。接下來就是 tomcat 的基本設定,jdk 環境變量設定好之後,將 tomcat 程式放到 /opt/apache-tomcat 下面 8 | 編輯 /etc/supervisor/conf.d/supervisor.conf 檔案,新增 tomcat 項 9 | ``` 10 | [supervisord] 11 | nodaemon=true 12 | 13 | [program:tomcat] 14 | command=/opt/apache-tomcat/bin/startup.sh 15 | 16 | [program:sshd] 17 | command=/usr/sbin/sshd -D 18 | docker commit ac6474aeb31d tomcat 19 | ``` 20 | 21 | 新建 tomcat 檔案夾,新建 Dockerfile。 22 | ``` 23 | FROM mk_tomcat 24 | EXPOSE 22 8080 25 | CMD ["/usr/bin/supervisord"] 26 | ``` 27 | 根據 Dockerfile 建立映像檔。 28 | ``` 29 | docker build tomcat tomcat 30 | ``` 31 | ### 安裝 weblogic 映像檔 32 | 33 | 步驟和 tomcat 基本一致,這裡貼一下設定檔案 34 | ``` 35 | supervisor.conf 36 | [supervisord] 37 | nodaemon=true 38 | 39 | 40 | [program:weblogic] 41 | command=/opt/Middleware/user_projects/domains/base_domain/bin/startWebLogic.sh 42 | 43 | [program:sshd] 44 | command=/usr/sbin/sshd -D 45 | dockerfile 46 | FROM weblogic 47 | EXPOSE 22 7001 48 | CMD ["/usr/bin/supervisord"] 49 | ``` 50 | 51 | ### tomcat/weblogic 映像檔的使用 52 | #### 儲存的使用 53 | 在啟動的時候,使用 `-v` 參數 54 | 55 | -v, --volume=[] Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container) 56 | 57 | 將本地磁碟映射到容器內部,它在主機和容器之間是實時變化的,所以我們更新程式、上傳代碼只需要更新物理主機的目錄就可以了 58 | 59 | #### tomcat 和 weblogic 集群的實作 60 | tomcat 只要開啟多個容器即可 61 | ``` 62 | docker run -d -v -p 204:22 -p 7003:8080 -v /home/data:/opt/data --name tm1 tomcat /usr/bin/supervisord 63 | docker run -d -v -p 205:22 -p 7004:8080 -v /home/data:/opt/data --name tm2 tomcat /usr/bin/supervisord 64 | docker run -d -v -p 206:22 -p 7005:8080 -v /home/data:/opt/data --name tm3 tomcat /usr/bin/supervisord 65 | ``` 66 | 67 | 這裡說一下 weblogic 的設定,大家知道 weblogic 有一個域的概念。如果要使用常規的 administrator +node 的方式部署,就需要在 supervisord 中分別寫出 administartor server 和 node server 的啟動腳本,這樣做的優點是: 68 | * 可以使用 weblogic 的集群,同步等概念 69 | * 部署一個集群應用程式,只需要安裝一次應用到集群上即可 70 | 71 | 缺點是: 72 | * Docker 設定復雜了 73 | * 沒辦法自動擴展集群的計算容量,如需新增節點,需要在 administrator 上先建立節點,然後再設定新的容器 supervisor 啟動腳本,然後再啟動容器 74 | 另外種方法是將所有的程式都安裝在 adminiserver 上面,需要擴展的時候,啟動多個節點即可,它的優點和缺點和上一種方法恰恰相反。(建議使用這種方式來部署開發和測試環境) 75 | ``` 76 | docker run -d -v -p 204:22 -p 7001:7001 -v /home/data:/opt/data --name node1 weblogic /usr/bin/supervisord 77 | docker run -d -v -p 205:22 -p 7002:7001 -v /home/data:/opt/data --name node2 weblogic /usr/bin/supervisord 78 | docker run -d -v -p 206:22 -p 7003:7001 -v /home/data:/opt/data --name node3 weblogic /usr/bin/supervisord 79 | ``` 80 | 81 | 這樣在前端使用 nginx 來做負載均衡就可以完成設定了 82 | -------------------------------------------------------------------------------- /container/README.md: -------------------------------------------------------------------------------- 1 | # Docker 容器 2 | 容器是 Docker 又一核心概念。 3 | 4 | 簡單的說,容器是獨立執行的一個或一組應用,以及它們的執行態環境。換句話說,虛擬機可以理解為模擬執行的一整套作業系統(提供了執行態環境和其他系統環境)和跑在上面的應用。 5 | 6 | 本章將具體介紹如何來管理一個容器,包括建立、啟動和停止等。 7 | -------------------------------------------------------------------------------- /container/daemon.md: -------------------------------------------------------------------------------- 1 | ##常駐執行 2 | 3 | 更多的時候,需要讓 Docker 容器在後臺以常駐(Daemonized)形式執行。此時,可以透過新增 `-d` 參數來實作。 4 | 5 | 例以下面的命令會在後臺執行容器。 6 | ``` 7 | $ sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" 8 | 1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147 9 | ``` 10 | 11 | 容器啟動後會回傳一個唯一的 id,也可以透過 `docker ps` 命令來查看容器訊息。 12 | ``` 13 | $ sudo docker ps 14 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 15 | 1e5535038e28 ubuntu:14.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute insane_babbage 16 | ``` 17 | 要取得容器的輸出訊息,可以透過 `docker logs` 命令。 18 | ``` 19 | $ sudo docker logs insane_babbage 20 | hello world 21 | hello world 22 | hello world 23 | . . . 24 | ``` 25 | -------------------------------------------------------------------------------- /container/enter.md: -------------------------------------------------------------------------------- 1 | ## 進入容器 2 | 在使用 `-d` 參數時,容器啟動後會進入背景執行。 3 | 某些時候需要進入容器進行操作,有很多種方法,包括使用 `docker attach` 命令或 `nsenter` 工具等。 4 | ### exec 命令 5 | `docker exec` 是Docker內建的命令。下面示範如何使用該命令。 6 | ``` 7 | $ sudo docker run -idt ubuntu 8 | 243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550 9 | $ sudo docker ps 10 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11 | 243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia 12 | $sudo docker exec -ti nostalgic_hypatia bash 13 | root@243c32535da7:/# 14 | ``` 15 | ### attach 命令 16 | `docker attach` 亦是Docker內建的命令。下面示例如何使用該命令。 17 | ``` 18 | $ sudo docker run -idt ubuntu 19 | 243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550 20 | $ sudo docker ps 21 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 22 | 243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia 23 | $sudo docker attach nostalgic_hypatia 24 | root@243c32535da7:/# 25 | ``` 26 | 27 | 按下 `ctrl` + `P` 然後 `ctrl` + `Q` 跳離容器,讓它繼續在背景執行。 28 | 29 | 但是使用 `attach` 命令有時候並不方便。當多個視窗同時 attach 到同一個容器的時候,所有視窗都會同步顯示。當某個視窗因命令阻塞時,其他視窗也無法執行操作了。 30 | 31 | ### nsenter 命令 32 | #### 安裝 33 | `nsenter` 工具已含括在 util-linux 2.23 後的版本內。 34 | 如果系統中 util-linux 包沒有該命令,可以按照下面的方法從原始碼安裝。 35 | ``` 36 | $ cd /tmp; curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24; 37 | $ ./configure --without-ncurses 38 | $ make nsenter && sudo cp nsenter /usr/local/bin 39 | ``` 40 | 41 | #### 使用 42 | `nsenter` 可以存取另一個程式的命名空間。nsenter 要正常工作需要有 root 權限。 43 | 很不幸,Ubuntu 14.4 仍然使用的是 util-linux 2.20。安裝最新版本的 util-linux(2.24)版,請按照以下步驟: 44 | ``` 45 | $ wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz; tar xzvf util-linux-2.24.tar.gz 46 | $ cd util-linux-2.24 47 | $ ./configure --without-ncurses && make nsenter 48 | $ sudo cp nsenter /usr/local/bin 49 | ``` 50 | 為了連線到容器,你還需要找到容器的第一個程式的 PID,可以透過下面的命令取得。 51 | ``` 52 | PID=$(docker inspect --format "{{ .State.Pid }}" ) 53 | ``` 54 | 透過這個 PID,就可以連線到這個容器: 55 | ``` 56 | $ nsenter --target $PID --mount --uts --ipc --net --pid 57 | ``` 58 | 下面給出一個完整的例子。 59 | ``` 60 | $ sudo docker run -idt ubuntu 61 | 243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550 62 | $ sudo docker ps 63 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 64 | 243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia 65 | $ PID=$(docker-pid 243c32535da7) 66 | 10981 67 | $ sudo nsenter --target 10981 --mount --uts --ipc --net --pid 68 | root@243c32535da7:/# 69 | ``` 70 | 更簡單的,建議大家下載 71 | [.bashrc_docker](https://github.com/yeasy/docker_practice/raw/master/_local/.bashrc_docker),並將內容放到 .bashrc 中。 72 | ``` 73 | $ wget -P ~ https://github.com/yeasy/docker_practice/raw/master/_local/.bashrc_docker; 74 | $ echo "[ -f ~/.bashrc_docker ] && . ~/.bashrc_docker" >> ~/.bashrc; source ~/.bashrc 75 | ``` 76 | 這個檔案中定義了很多方便使用 Docker 的命令,例如 `docker-pid` 可以取得某個容器的 PID;而 `docker-enter` 可以進入容器或直接在容器內執行命令。 77 | ``` 78 | $ echo $(docker-pid ) 79 | $ docker-enter ls 80 | ``` 81 | -------------------------------------------------------------------------------- /container/import_export.md: -------------------------------------------------------------------------------- 1 | ##匯出和匯入容器 2 | 3 | ###匯出容器 4 | 如果要匯出本地某個容器,可以使用 `docker export` 命令。 5 | ``` 6 | $ sudo docker ps -a 7 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8 | 7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test 9 | $ sudo docker export 7691a814370e > ubuntu.tar 10 | ``` 11 | 這樣將匯出容器快照到本地檔案。 12 | 13 | ###匯入容器快照 14 | 可以使用 `docker import` 從容器快照檔案中再匯入為映像檔,例如 15 | ``` 16 | $ cat ubuntu.tar | sudo docker import - test/ubuntu:v1.0 17 | $ sudo docker images 18 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 19 | test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB 20 | ``` 21 | 此外,也可以透過指定 URL 或者某個目錄來匯入,例如 22 | ``` 23 | $sudo docker import http://example.com/exampleimage.tgz example/imagerepo 24 | ``` 25 | 26 | *註:使用者既可以使用 `docker load` 來匯入映像檔儲存檔案到本地映像檔庫,也可以使用 `docker import` 來匯入一個容器快照到本地映像檔庫。這兩者的區別在於容器快照檔案將丟棄所有的歷史記錄和原始資料訊息(即僅保存容器當時的快照狀態),而映像檔儲存檔案將保存完整記錄,檔案體積也跟著變大。此外,從容器快照檔案匯入時可以重新指定標籤等原始資料訊息。 27 | 28 | 29 | -------------------------------------------------------------------------------- /container/rm.md: -------------------------------------------------------------------------------- 1 | ## 刪除容器 2 | 3 | 可以使用 `docker rm` 來刪除一個處於終止狀態的容器。 4 | 例如 5 | 6 | ``` 7 | $sudo docker rm trusting_newton 8 | trusting_newton 9 | ``` 10 | 11 | 如果要刪除一個執行中的容器,可以新增 `-f` 參數。Docker 會發送 `SIGKILL` 訊號給容器。 12 | -------------------------------------------------------------------------------- /container/run.md: -------------------------------------------------------------------------------- 1 | ## 啟動容器 2 | 3 | 啟動容器有兩種方式: 4 | 5 | - 將映像檔新建一個容器並啟動 6 | - 將終止狀態(stopped)的容器重新啟動。 7 | 8 | 因為 Docker 的容器實在太輕量級了,使用者可以隨時刪除和新建立容器。 9 | 10 | ### 新建並啟動 11 | 12 | 所需要的命令主要為 `docker run`。 13 | 14 | 例如,下面的命令輸出一個 “Hello World”,之後終止容器。 15 | 16 | ``` 17 | $ sudo docker run ubuntu:14.04 /bin/echo 'Hello world' 18 | Hello world 19 | ``` 20 | 21 | 這跟在本地直接執行 `/bin/echo 'hello world'` 相同, 幾乎感覺不出任何區別。 22 | 23 | 下面的命令則啟動一個 bash 終端,允許使用者進行互動。 24 | 25 | ``` 26 | $ sudo docker run -t -i ubuntu:14.04 /bin/bash 27 | root@af8bae53bdd3:/# 28 | ``` 29 | 30 | 其中,`-t` 選項讓Docker分配一個虛擬終端(pseudo-tty)並綁定到容器的標準輸入上, `-i` 則讓容器的標準輸入保持打開。 31 | 32 | 在互動模式下,使用者可以透過所建立的終端來輸入命令,例如 33 | 34 | ``` 35 | root@af8bae53bdd3:/# pwd 36 | / 37 | root@af8bae53bdd3:/# ls 38 | bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var 39 | ``` 40 | 41 | 當利用 `docker run` 來建立容器時,Docker 在後臺執行的標準操作包括: 42 | 43 | - 檢查本地是否存在指定的映像檔,不存在就從公有倉庫下載 44 | - 利用映像檔建立並啟動一個容器 45 | - 分配一個檔案系統,並在唯讀的映像檔層外面掛載一層可讀寫層 46 | - 從宿主主機設定的網路橋介面中橋接一個虛擬埠到容器中去 47 | - 從位址池中設定一個 ip 位址給容器 48 | - 執行使用者指定的應用程式 49 | - 執行完畢後容器被終止 50 | 51 | ### 啟動已終止容器 52 | 53 | 可以利用 `docker start` 命令,直接將一個已經終止的容器啟動執行。 54 | 55 | 容器的核心為所執行的應用程式,所需要的資源都是應用程式執行所必需的。除此之外,並沒有其它的資源。可以在虛擬終端中利用 `ps` 或 `top` 來查看程式訊息。 56 | 57 | ``` 58 | root@ba267838cc1b:/# ps 59 | PID TTY TIME CMD 60 | 1 ? 00:00:00 bash 61 | 11 ? 00:00:00 ps 62 | ``` 63 | 64 | 可見,容器中僅執行了指定的 bash 應用。這種特點使得 Docker 對資源的使用率極高,是貨真價實的輕量級虛擬化。 65 | -------------------------------------------------------------------------------- /container/stop.md: -------------------------------------------------------------------------------- 1 | ##終止容器 2 | 可以使用 `docker stop` 來終止一個執行中的容器。 3 | 4 | 此外,當Docker容器中指定的應用終結時,容器也自動終止。 5 | 例如對於上一章節中只啟動了一個終端機的容器,使用者透過 `exit` 命令或 `Ctrl+d` 來退出終端時,所建立的容器立刻終止。 6 | 7 | 終止狀態的容器可以用 `docker ps -a` 命令看到。例如 8 | ``` 9 | sudo docker ps -a 10 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11 | ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton 12 | 98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike 13 | ``` 14 | 15 | 處於終止狀態的容器,可以透過 `docker start` 命令來重新啟動。 16 | 17 | 此外,`docker restart` 命令會將一個執行中的容器終止,然後再重新啟動它。 18 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/cover.jpg -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipz/docker_practice/fd8bcdedebae49676a8b3f4e28b476f74fb9a8f0/cover_small.jpg -------------------------------------------------------------------------------- /data_management/README.md: -------------------------------------------------------------------------------- 1 | # Docker 資料管理 2 | 3 | 這一章介紹如何在 Docker 內部以及容器之間管理資料,在容器中管理資料主要有兩種方式: 4 | * 資料卷(Data volumes) 5 | * 資料卷容器(Data volume containers) -------------------------------------------------------------------------------- /data_management/container.md: -------------------------------------------------------------------------------- 1 | ## 資料卷容器 2 | 如果你有一些持續更新的資料需要在容器之間共享,最好建立資料卷容器。 3 | 4 | 資料卷容器,其實就是一個正常的容器,專門用來提供資料卷供其它容器掛載的。 5 | 6 | 首先,建立一個命名的資料卷容器 dbdata: 7 | ``` 8 | $ sudo docker run -d -v /dbdata --name dbdata training/postgres echo Data-only container for postgres 9 | ``` 10 | 然後,在其他容器中使用 `--volumes-from` 來掛載 dbdata 容器中的資料卷。 11 | ``` 12 | $ sudo docker run -d --volumes-from dbdata --name db1 training/postgres 13 | $ sudo docker run -d --volumes-from dbdata --name db2 training/postgres 14 | ``` 15 | 還可以使用多個 `--volumes-from` 參數來從多個容器掛載多個資料卷。 16 | 也可以從其他已經掛載了容器卷的容器來掛載資料卷。 17 | ``` 18 | $ sudo docker run -d --name db3 --volumes-from db1 training/postgres 19 | ``` 20 | *注意:使用 `--volumes-from` 參數所掛載資料卷的容器自己並不需要保持在執行狀態。 21 | 22 | 如果刪除了掛載的容器(包括 dbdata、db1 和 db2),資料卷並不會被自動刪除。如果要刪除一個資料卷,必須在刪除最後一個還掛載著它的容器時使用 `docker rm -v` 命令來指定同時刪除關聯的容器。 23 | 這可以讓使用者在容器之間升級和移動資料卷。具體的操作將在下一節中進行講解。 24 | -------------------------------------------------------------------------------- /data_management/management.md: -------------------------------------------------------------------------------- 1 | ## 利用資料卷容器來備份、恢復、遷移資料卷 2 | 可以利用資料卷對其中的資料進行備份、恢復和遷移。 3 | 4 | ### 備份 5 | 首先使用 `--volumes-from` 標記來建立一個載入 dbdata 容器卷的容器,並從本地主機掛載當前到容器的 /backup 目錄。命令以下: 6 | ``` 7 | $ sudo docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata 8 | ``` 9 | 容器啟動後,使用了 `tar` 命令來將 dbdata 卷備份為本地的 `/backup/backup.tar`。 10 | 11 | 12 | ### 恢復 13 | 如果要恢復資料到一個容器,首先建立一個帶有資料卷的容器 dbdata2。 14 | ``` 15 | $ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash 16 | ``` 17 | 然後建立另一個容器,掛載 dbdata2 的容器,並使用 `untar` 解壓備份檔案到掛載的容器卷中。 18 | ``` 19 | $ sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf 20 | /backup/backup.tar 21 | ``` 22 | -------------------------------------------------------------------------------- /data_management/volume.md: -------------------------------------------------------------------------------- 1 | ## 資料卷 2 | 3 | 資料卷是一個可供一個或多個容器使用的特殊目錄,它繞過 UFS,可以提供很多有用的特性: 4 | * 資料卷可以在容器之間共享和重用 5 | * 對資料卷的修改會立即生效 6 | * 對資料卷的更新,不會影響映像檔 7 | * 卷會一直存在,直到沒有容器使用 8 | 9 | *資料卷的使用,類似於 Linux 下對目錄或檔案進行 mount。 10 | 11 | ### 建立一個資料卷 12 | 13 | 在用 `docker run` 命令的時候,使用 `-v` 標記來建立一個資料卷並掛載到容器裡。在一次 run 中多次使用可以掛載多個資料卷。 14 | 下面建立一個 web 容器,並載入一個資料卷到容器的 `/webapp` 目錄。 15 | 16 | ```bash 17 | $ sudo docker run -d -P --name web -v /webapp training/webapp python app.py 18 | ``` 19 | 20 | *注意:也可以在 Dockerfile 中使用 `VOLUME` 來新增一個或者多個新的卷到由該映像檔建立的任意容器。 21 | 22 | ### 掛載一個主機目錄作為資料卷 23 | 24 | 使用 `-v` 標記也可以指定掛載一個本地主機的目錄到容器中去。 25 | 26 | ```bash 27 | $ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py 28 | ``` 29 | 30 | 上面的命令載入主機的 `/src/webapp` 目錄到容器的 `/opt/webapp` 31 | 目錄。這個功能在進行測試的時候十分方便,比如使用者可以放置一些程式到本地目錄中,來查看容器是否正常工作。本地目錄的路徑必須是絕對路徑,如果目錄不存在 Docker 會自動為你建立它。 32 | 33 | *注意:Dockerfile 中不支援這種用法,這是因為 Dockerfile 是為了移植和分享用的。然而,不同作業系統的路徑格式不一樣,所以目前還不能支援。 34 | 35 | Docker 掛載資料卷的預設權限是讀寫,使用者也可以透過 `:ro` 指定為唯讀。 36 | 37 | ```bash 38 | $ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro 39 | training/webapp python app.py 40 | ``` 41 | 42 | 加了 `:ro` 之後,就掛載為唯讀了。 43 | 44 | ### 掛載一個本地主機檔案作為資料卷 45 | 46 | `-v` 標記也可以從主機掛載單個檔案到容器中。 47 | 48 | ```bash 49 | $ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash 50 | ``` 51 | 52 | 這樣就可以記錄在容器輸入過的命令了。 53 | 54 | *注意:如果直接掛載一個檔案,很多檔案編輯工具,包括 `vi` 或者 `sed --in-place`,可能會造成檔案 inode 的改變,從 Docker 1.1 55 | .0起,這會導致報錯誤訊息。所以最簡單的辦法就直接掛載檔案的父目錄。 56 | -------------------------------------------------------------------------------- /dockerfile/README.md: -------------------------------------------------------------------------------- 1 | # Dockerfile 2 | 3 | 使用 Dockerfile 讓使用者可以建立自訂的映像檔。 -------------------------------------------------------------------------------- /dockerfile/basic_structure.md: -------------------------------------------------------------------------------- 1 | ## 基本結構 2 | 3 | Dockerfile 由一行行命令語句組成,並且支援以 `#` 開頭的註解行。一般而言,Dockerfile 分為四部分:基底映像檔資訊、維護者資訊、映像檔操作指令和容器啟動時執行指令。例如: 4 | 5 | ``` 6 | # This dockerfile uses the ubuntu image 7 | # VERSION 2 - EDITION 1 8 | # Author: docker_user 9 | # Command format: Instruction [arguments / command] .. 10 | 11 | # 基本映像檔,必須是第一個指令 12 | FROM ubuntu 13 | 14 | # 維護者: docker_user (@docker_user) 15 | MAINTAINER docker_user docker_user@email.com 16 | 17 | # 更新映像檔的指令 18 | RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list 19 | RUN apt-get update && apt-get install -y nginx 20 | RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf 21 | 22 | # 建立新容器時要執行的指令 23 | CMD /usr/sbin/nginx 24 | ``` 25 | 26 | 其中,一開始必須指明作為基底的映像檔名稱,接下來說明維護者資訊(建議)。接著則是映像檔操作指令,例如 `RUN` 指令,`RUN` 指令將對映像檔執行相對應的命令。每運行一條 `RUN` 指令,映像檔就會新增一層。最後是 `CMD` 指令,指定執行容器時的操作命令。下面來看一個更複雜的例子: 27 | 28 | ``` 29 | # Nginx 30 | # 31 | # VERSION 0.0.1 32 | 33 | FROM ubuntu 34 | MAINTAINER Victor Vieux 35 | 36 | RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server 37 | 38 | # Firefox over VNC 39 | # 40 | # VERSION 0.3 41 | 42 | FROM ubuntu 43 | 44 | # Install vnc, xvfb in order to create a 'fake' display and firefox 45 | RUN apt-get update && apt-get install -y x11vnc xvfb firefox 46 | RUN mkdir /.vnc 47 | # Setup a password 48 | RUN x11vnc -storepasswd 1234 ~/.vnc/passwd 49 | # Autostart firefox (might not be the best way, but it does the trick) 50 | RUN bash -c 'echo "firefox" >> /.bashrc' 51 | 52 | EXPOSE 5900 53 | CMD ["x11vnc", "-forever", "-usepw", "-create"] 54 | 55 | # Multiple images example 56 | # 57 | # VERSION 0.1 58 | 59 | FROM ubuntu 60 | RUN echo foo > bar 61 | # Will output something like ===> 907ad6c2736f 62 | 63 | FROM ubuntu 64 | RUN echo moo > oink 65 | # Will output something like ===> 695d7793cbe4 66 | 67 | # You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with 68 | # /oink. 69 | ``` -------------------------------------------------------------------------------- /dockerfile/build_image.md: -------------------------------------------------------------------------------- 1 | ## 建立映像檔 2 | 3 | 編輯完成 Dockerfile 之後,可以透過 `docker build` 命令建立映像檔。 4 | 5 | 基本的格式為 `docekr build [選項] 路徑`,該命令將讀取指定路徑下(包括子目錄)的 Dockerfile,並將該路徑下所有內容發送給 Docker 伺服端,由伺服端來建立映像檔。因此一般會建議放置 Dockerfile 的目錄為空目錄。也可以透過 `.dockerignore` 檔案(每一行新增一條排除模式:exclusion patterns)來讓 Docker 忽略路徑下的目錄和檔案。 6 | 7 | 要指定映像檔的標籤資訊,可以透過 `-t` 選項,例如: 8 | ``` 9 | $ sudo docker build -t myrepo/myapp /tmp/test1/ 10 | ``` 11 | -------------------------------------------------------------------------------- /dockerfile/file_from_image.md: -------------------------------------------------------------------------------- 1 | ## 從映像檔產生 Dockerfile 2 | 3 | CenturyLinkLabs 釋出 [dockerfile-from-image](https://github.com/CenturyLinkLabs/dockerfile-from-image) 工具,以逆向工程建立出 Dockerfile。 4 | 類似 `docker history` 指令,透過映像檔每一層的 metadata 來重建出那 Dockerfile ,即便沒有提供任何資訊。 5 | 6 | ### 使用方法 7 | 8 | 首先 `docker pull centurylink/dockerfile-from-image` 這已包好 Ruby script 的[映像檔](https://registry.hub.docker.com/u/centurylink/dockerfile-from-image/), 9 | 接下來,執行下面命令,就可得到反推所產生的 Dockerfile.txt : 10 | 11 | ```bash 12 | docker run -v /var/run/docker.sock:/var/run/docker.sock \ 13 | centurylink/dockerfile-from-image > Dockerfile.txt 14 | ``` 15 | 16 | 那 `` 參數可以任何包含 tag 的映像檔名稱。 17 | 18 | ### 範例 19 | 20 | 以下是個示範,如何將官方 Ruby 的映像檔來產生出 Dockerfile。 21 | 22 | ```bash 23 | $ docker pull ruby 24 | Pulling repository ruby 25 | 26 | $ docker run -v /run/docker.sock:/run/docker.sock centurylink/dockerfile-from-image 27 | Usage: dockerfile-from-image.rb [options] 28 | -f, --full-tree Generate Dockerfile for all parent layers 29 | -h, --help Show this message 30 | 31 | $ docker run -v /run/docker.sock:/run/docker.sock centurylink/dockerfile-from-image ruby 32 | FROM buildpack-deps:latest 33 | RUN useradd -g users user 34 | RUN apt-get update && apt-get install -y bison procps 35 | RUN apt-get update && apt-get install -y ruby 36 | ADD dir:03090a5fdc5feb8b4f1d6a69214c37b5f6d653f5185cddb6bf7fd71e6ded561c in /usr/src/ruby 37 | WORKDIR /usr/src/ruby 38 | RUN chown -R user:users . 39 | USER user 40 | RUN autoconf && ./configure --disable-install-doc 41 | RUN make -j"$(nproc)" 42 | RUN make check 43 | USER root 44 | RUN apt-get purge -y ruby 45 | RUN make install 46 | RUN echo 'gem: --no-rdoc --no-ri' >> /.gemrc 47 | RUN gem install bundler 48 | ONBUILD ADD . /usr/src/app 49 | ONBUILD WORKDIR /usr/src/app 50 | ONBUILD RUN [ ! -e Gemfile ] || bundle install --system 51 | ``` 52 | -------------------------------------------------------------------------------- /dockerfile/instructions.md: -------------------------------------------------------------------------------- 1 | ## 指令 2 | 3 | 指令的一般格式為 `INSTRUCTION arguments`,指令包括 `FROM`、`MAINTAINER`、`RUN` 等。 4 | 5 | ### FROM 6 | 7 | * 格式: 8 | ``` 9 | FROM 10 | FROM : 11 | ``` 12 | * 說明:第一條指令必須為 `FROM` 指令。並且,如果在同一個 Dockerfile 中建立多個映像檔時,可以使用多個 `FROM` 指令(每個映像檔一次)。 13 | 14 | ### MAINTAINER 15 | 16 | * 格式: 17 | ``` 18 | MAINTAINER 19 | ``` 20 | * 說明:指定維護者訊息。 21 | 22 | ### RUN 23 | 24 | * 格式: 25 | ``` 26 | RUN 27 | RUN ["executable", "", ""] 28 | ``` 29 | * 說明: 30 | * 前者將在 shell 終端中運行命令,即 `/bin/sh -c`;後者則使用 `exec` 執行。 31 | * 指定使用其它終端可以透過第二種方式實作,例如:`RUN ["/bin/bash", "-c", "echo hello"]`。 32 | * 每條 `RUN` 指令將在當前映像檔基底上執行指定命令,並產生新的映像檔。當命令較長時可以使用 `\` 來換行。 33 | 34 | ### CMD 35 | 36 | * 格式: 37 | * `CMD ["executable","",""]` (使用 `exec` 執行,推薦使用) 38 | * `CMD ` 在 `/bin/sh` 中執行,使用在給需要互動的指令; 39 | * `CMD ["",""]` 提供給 `ENTRYPOINT` 的預設參數; 40 | * 說明: 41 | * 指定啟動容器時執行的命令,每個 Dockerfile 只能有一條 `CMD` 命令。如果指定了多條命令,只有最後一條會被執行。 42 | * 如果使用者啟動容器時候指定了運行的命令,則會覆蓋掉 `CMD` 指定的命令。 43 | 44 | ### EXPOSE 45 | 46 | * 格式: 47 | ``` 48 | EXPOSE [...] 49 | ``` 50 | * 說明: 51 | * 設定 Docker 伺服器容器對外的埠號,供外界使用。 52 | * 在啟動容器時需要透過 `-P`,Docker 會自動分配一個埠號轉發到指定的埠號。 53 | 54 | ### ENV 55 | 56 | * 格式: 57 | ``` 58 | ENV 59 | ``` 60 | * 說明:指定一個環境變數,會被後續 `RUN` 指令使用,並在容器運行時保持。 61 | * 例如: 62 | ``` 63 | ENV PG_MAJOR 9.3 64 | ENV PG_VERSION 9.3.4 65 | RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && … 66 | ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH 67 | ``` 68 | 69 | ### ADD 70 | 71 | * 格式: 72 | ``` 73 | ADD 74 | ``` 75 | * 說明: 76 | * 該命令將複製指定的 `` 到容器中的 ``。 77 | * 其中 `` 可以是 Dockerfile 所在目錄的相對路徑;也可以是一個 URL;還可以是一個 tar 檔案(其複製後會自動解壓縮)。 78 | 79 | ### COPY 80 | 81 | * 格式: 82 | ``` 83 | COPY 84 | ``` 85 | * 說明: 86 | * 複製本地端的 ``(為 Dockerfile 所在目錄的相對路徑)到容器中的 ``。 87 | * 當使用本地目錄為根目錄時,推薦使用 `COPY`。 88 | 89 | ### ENTRYPOINT 90 | 91 | * 格式: 92 | ``` 93 | ENTRYPOINT ["executable", "", ""] 94 | ENTRYPOINT ` 95 | ``` 96 | * 說明: 97 | * 指定容器啟動後執行的命令,並且不會被 `docker run` 提供的參數覆蓋。 98 | * 每個 Dockerfile 中只能有一個 `ENTRYPOINT`,當指定多個時,只有最後一個會生效。 99 | * 第二條指令要在 shell 中執行。 100 | 101 | ### VOLUME 102 | 103 | * 格式: 104 | ``` 105 | VOLUME ["/data"] 106 | ``` 107 | * 說明:建立一個可以從本地端或其他容器掛載的掛載點,一般用來存放資料庫和需要保存的資料等。 108 | 109 | ### USER 110 | 111 | * 格式: 112 | ``` 113 | USER daemon 114 | ``` 115 | * 說明: 116 | * 指定運行容器時的使用者名稱或 UID,後續的 `RUN` 也會使用指定使用者。 117 | * 當服務不需要管理員權限時,可以透過該命令指定運行使用者。並且可以在之前建立所需要的使用者,例如:`RUN groupadd -r postgres && useradd -r -g postgres postgres`。 118 | * 要臨時取得管理員權限可以使用 `gosu`,而不推薦 `sudo`。 119 | 120 | ### WORKDIR 121 | 122 | * 格式: 123 | ``` 124 | WORKDIR 125 | ``` 126 | * 說明: 127 | * 為後續的 `RUN`、`CMD`、`ENTRYPOINT` 指令指定工作目錄。 128 | * 可以使用多個 `WORKDIR` 指令,後續命令如果參數是相對路徑,則會基於之前命令指定的路徑。例如: 129 | ``` 130 | WORKDIR /a 131 | WORKDIR b 132 | WORKDIR c 133 | RUN pwd 134 | ``` 135 | 則最終路徑為 `/a/b/c`。 136 | 137 | ### ONBUILD 138 | 139 | * 格式: 140 | ``` 141 | ONBUILD [instruction] 142 | ``` 143 | * 說明: 144 | * 指定當建立的映像檔作為其它新建立映像檔的基底映像檔時,所執行的操作指令。例如,Dockerfile 使用以下的內容建立了映像檔 `image-A`。 145 | ``` 146 | [...] 147 | ONBUILD ADD . /app/src 148 | ONBUILD RUN /usr/local/bin/python-build --dir /app/src 149 | [...] 150 | ``` 151 | * 如果基於 image-A 建立新的映像檔時,新的 Dockerfile 中使用 `FROM image-A`指定基底映像檔時,會自動執行 `ONBUILD` 指令內容,等於在後面新增了兩條指令。 152 | ``` 153 | FROM image-A 154 | 155 | #Automatically run the following 156 | ADD . /app/src 157 | RUN /usr/local/bin/python-build --dir /app/src 158 | ``` 159 | * 使用 `ONBUILD` 指令的映像檔,推薦在標籤中註明,例如 `ruby:1.9-onbuild`。 -------------------------------------------------------------------------------- /image/README.md: -------------------------------------------------------------------------------- 1 | # Docker 映像檔 2 | 3 | 在之前的介紹中,我們知道映像檔是 Docker 的三大元件之一。 4 | 5 | Docker 在執行容器前需要本地端存在對應的映像檔,如果映像檔不存在本地端,Docker 會從映像檔倉庫下載(預設是 Docker Hub 公共註冊伺服器中的倉庫)。 6 | 7 | 本章將介紹更多關於映像檔的內容,包括: 8 | * 從倉庫取得映像檔; 9 | * 管理本地主機上的映像檔; 10 | * 介紹映像檔實作的基本原理 11 | -------------------------------------------------------------------------------- /image/create.md: -------------------------------------------------------------------------------- 1 | ## 建立映像檔 2 | 3 | 建立映像檔有很多方法,使用者可以從 Docker Hub 取得已有映像檔並更新,也可以在本機建立一個。 4 | 5 | ### 修改已有映像檔 6 | 7 | 先使用下載的映像檔啟動容器。 8 | 9 | ``` 10 | $ sudo docker run -t -i training/sinatra /bin/bash 11 | root@0b2616b0e5a8:/# 12 | ``` 13 | 14 | 注意:記住容器的 ID,稍後還會用到。 15 | 16 | 在容器中加入 json 的 gem 套件。 17 | 18 | ``` 19 | root@0b2616b0e5a8:/# gem install json 20 | ``` 21 | 22 | 當結束後,我們使用 exit 來退出,現在我們的容器已經被改變了,使用 `docker commit` 命令來提交更新後的副本。 23 | 24 | ``` 25 | $ sudo docker commit -m "Added json gem" -a "Docker Newbee" 0b2616b0e5a8 ouruser/sinatra:v2 26 | 4f177bd27a9ff0f6dc2a830403925b5360bfe0b93d476f7fc3231110e7f71b1c 27 | ``` 28 | 29 | 其中,`-m` 指定提交的說明資訊,跟我們使用的版本控制工具一樣;`-a` 可以指定更新的使用者資訊;之後是用來建立映像檔的容器的 ID;最後指定新映像檔的名稱和 tag 。建立成功後會印出新映像檔的 ID。 30 | 31 | 使用 `docker images` 查看新建立的映像檔。 32 | 33 | ``` 34 | $ sudo docker images 35 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 36 | training/sinatra latest 5bc342fa0b91 10 hours ago 446.7 MB 37 | ouruser/sinatra v2 3c59e02ddd1a 10 hours ago 446.7 MB 38 | ouruser/sinatra latest 5db5f8471261 10 hours ago 446.7 MB 39 | ``` 40 | 41 | 之後,可以使用新的映像檔來啟動容器 42 | 43 | ``` 44 | $ sudo docker run -t -i ouruser/sinatra:v2 /bin/bash 45 | root@78e82f680994:/# 46 | ``` 47 | 48 | ### 利用 Dockerfile 建立映像檔 49 | 50 | 使用 `docker commit` 擴展一個映像檔比較簡單,但是不方便在一個團隊中分享。我們可以使用 `docker build` 來建立一個新的映像檔。為此,首先需要建立一個 Dockerfile,裡面包含一些用來建立映像檔的指令。 51 | 52 | 新建一個目錄和一個 Dockerfile 53 | 54 | ``` 55 | $ mkdir sinatra 56 | $ cd sinatra 57 | $ touch Dockerfile 58 | ``` 59 | 60 | Dockerfile 中每一條指令都會建立一層映像檔,例如: 61 | 62 | ``` 63 | # This is a comment 64 | FROM ubuntu:14.04 65 | MAINTAINER Docker Newbee 66 | RUN apt-get -qq update 67 | RUN apt-get -qqy install ruby ruby-dev 68 | RUN gem install sinatra 69 | ``` 70 | 71 | Dockerfile 基本的語法是 72 | 73 | - 使用`#`來註釋 74 | - `FROM` 指令告訴 Docker 使用哪個映像檔作為基底 75 | - 接著是維護者的資訊 76 | - `RUN`開頭的指令會在建立中執行,比如安裝一個套件,在這裏使用 apt-get 來安裝了一些套件 77 | 78 | 完成 Dockerfile 後可以使用 `docker build` 建立映像檔。 79 | 80 | ``` 81 | $ sudo docker build -t="ouruser/sinatra:v2" . 82 | Uploading context 2.56 kB 83 | Uploading context 84 | Step 0 : FROM ubuntu:14.04 85 | ---> 99ec81b80c55 86 | Step 1 : MAINTAINER Kate Smith 87 | ---> Running in 7c5664a8a0c1 88 | ---> 2fa8ca4e2a13 89 | Removing intermediate container 7c5664a8a0c1 90 | Step 2 : RUN apt-get -qq update 91 | ---> Running in b07cc3fb4256 92 | ---> 50d21070ec0c 93 | Removing intermediate container b07cc3fb4256 94 | Step 3 : RUN apt-get -qqy install ruby ruby-dev 95 | ---> Running in a5b038dd127e 96 | Selecting previously unselected package libasan0:amd64. 97 | (Reading database ... 11518 files and directories currently installed.) 98 | Preparing to unpack .../libasan0_4.8.2-19ubuntu1_amd64.deb ... 99 | Setting up ruby (1:1.9.3.4) ... 100 | Setting up ruby1.9.1 (1.9.3.484-2ubuntu1) ... 101 | Processing triggers for libc-bin (2.19-0ubuntu6) ... 102 | ---> 2acb20f17878 103 | Removing intermediate container a5b038dd127e 104 | Step 4 : RUN gem install sinatra 105 | ---> Running in 5e9d0065c1f7 106 | . . . 107 | Successfully installed rack-protection-1.5.3 108 | Successfully installed sinatra-1.4.5 109 | 4 gems installed 110 | ---> 324104cde6ad 111 | Removing intermediate container 5e9d0065c1f7 112 | Successfully built 324104cde6ad 113 | ``` 114 | 115 | 其中 `-t` 標記添加 tag,指定新的映像檔的使用者資訊。 116 | “.” 是 Dockerfile 所在的路徑(當前目錄),也可以換成具體的 Dockerfile 的路徑。 117 | 118 | 可以看到 build 指令後執行的操作。它要做的第一件事情就是上傳這個 Dockerfile 內容,因為所有的操作都要依據 Dockerfile 來進行。 119 | 然後,Dockerfile 中的指令被一條一條的執行。每一步都建立了一個新的容器,在容器中執行指令並提交修改(就跟之前介紹過的 `docker commit` 一樣)。當所有的指令都執行完畢之後,回傳了最終的映像檔 id。所有的中間步驟所產生的容器都會被刪除和清理。 120 | 121 | \*注意一個映像檔不能超過 127 層 122 | 123 | 此外,還可以利用 `ADD` 命令複製本地檔案到映像檔;用 `EXPOSE` 命令向外部開放埠號;用 `CMD` 命令描述容器啟動後執行的程序等。例如 124 | 125 | ``` 126 | # put my local web site in myApp folder to /var/www 127 | ADD myApp /var/www 128 | # expose httpd port 129 | EXPOSE 80 130 | # the command to run 131 | CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"] 132 | ``` 133 | 134 | 現在可以利用新建立的映像檔啟動一個容器。 135 | 136 | ``` 137 | $ sudo docker run -t -i ouruser/sinatra:v2 /bin/bash 138 | root@8196968dac35:/# 139 | ``` 140 | 141 | 還可以用 `docker tag` 命令修改映像檔的標籤。 142 | 143 | ``` 144 | $ sudo docker tag 5db5f8471261 ouruser/sinatra:devel 145 | $ sudo docker images ouruser/sinatra 146 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 147 | ouruser/sinatra latest 5db5f8471261 11 hours ago 446.7 MB 148 | ouruser/sinatra devel 5db5f8471261 11 hours ago 446.7 MB 149 | ouruser/sinatra v2 5db5f8471261 11 hours ago 446.7 MB 150 | ``` 151 | 152 | \*註:更多用法,請參考 [Dockerfile](../dockerfile/README.md) 章節。 153 | 154 | ### 從本機匯入 155 | 156 | 要從本機匯入一個映像檔,可以使用 OpenVZ(容器虛擬化的先鋒技術)的模板來建立: 157 | OpenVZ 的模板下載位址為 http://openvz.org/Download/templates/precreated。 158 | 159 | 比如,先下載一個 ubuntu-14.04 的映像檔,之後使用以下命令匯入: 160 | 161 | ``` 162 | sudo cat ubuntu-14.04-x86_64-minimal.tar.gz |docker import - ubuntu:14.04 163 | ``` 164 | 165 | 然後查看新匯入的映像檔。 166 | 167 | ``` 168 | docker images 169 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 170 | ubuntu 14.04 05ac7c0b9383 17 seconds ago 215.5 MB 171 | ``` 172 | 173 | ###上傳映像檔 174 | 使用者可以通過 `docker push` 命令,把自己建立的映像檔上傳到倉庫中來共享。例如,使用者在 Docker Hub 上完成註冊後,可以推送自己的映像檔到倉庫中。 175 | 176 | ``` 177 | $ sudo docker push ouruser/sinatra 178 | 179 | The push refers to a repository [ouruser/sinatra] (len: 1) 180 | Sending image list 181 | Pushing repository ouruser/sinatra (3 tags) 182 | ``` 183 | -------------------------------------------------------------------------------- /image/internal.md: -------------------------------------------------------------------------------- 1 | ## 映像檔的實作原理 2 | 3 | Docker 映像檔是怎麽實作增量的修改和維護的? 4 | 每個映像檔都由很多層次構成,Docker 使用 [Union FS](http://en.wikipedia.org/wiki/UnionFS) 將這些不同的層結合到一個映像檔中去。 5 | 6 | 通常 Union FS 有兩個用途,一方面可以實作不借助 LVM、RAID 將多個 disk 掛到同一個目錄下,另一個更常用的就是將一個唯讀的分支和一個可寫的分支聯合在一起,Live CD 正是基於此方法可以允許在映像檔不變的基礎上允許使用者在其上進行一些寫操作。 7 | Docker 在 AUFS 上建立的容器也是利用了類似的原理。 8 | -------------------------------------------------------------------------------- /image/list.md: -------------------------------------------------------------------------------- 1 | ## 列出本機映像檔 2 | 使用 `docker images` 顯示本機已有的映像檔。 3 | ``` 4 | $ sudo docker images 5 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 6 | ubuntu 12.04 74fe38d11401 4 weeks ago 209.6 MB 7 | ubuntu precise 74fe38d11401 4 weeks ago 209.6 MB 8 | ubuntu 14.04 99ec81b80c55 4 weeks ago 266 MB 9 | ubuntu latest 99ec81b80c55 4 weeks ago 266 MB 10 | ubuntu trusty 99ec81b80c55 4 weeks ago 266 MB 11 | ... 12 | ``` 13 | 14 | 在列出訊息中,可以看到幾段文字訊息 15 | 16 | * 來自於哪個倉庫,比如 ubuntu 17 | * 映像檔的標記,比如 14.04 18 | * 它的 `ID` 號(唯一) 19 | * 建立時間 20 | * 映像檔大小 21 | 22 | 其中映像檔的 `ID` 唯一標識了映像檔,注意到 `ubuntu:14.04` 和 `ubuntu:trusty` 具有相同的映像檔 `ID`,說明它們實際上是同一映像檔。 23 | 24 | `TAG` 用來標記來自同一個倉庫的不同映像檔。例如 `ubuntu` 倉庫中有多個映像檔,通過 `TAG` 來區分發行版本,例如 `10.04`、`12.04`、`12.10`、`13.04`、`14.04` 等。例如下面的命令指定使用映像檔 `ubuntu:14.04` 來啟動一個容器。 25 | ``` 26 | $ sudo docker run -t -i ubuntu:14.04 /bin/bash 27 | ``` 28 | 29 | 如果沒有指定 `TAG`,預設使用 `latest` 30 | -------------------------------------------------------------------------------- /image/pull.md: -------------------------------------------------------------------------------- 1 | ## 取得映像檔 2 | 3 | 可以使用 `docker pull` 命令從倉庫取得所需要的映像檔。 4 | 5 | 下面的例子將從 Docker Hub 倉庫下載一個 Ubuntu 12.04 作業系統的映像檔。 6 | ``` 7 | $ sudo docker pull ubuntu:12.04 8 | Pulling repository ubuntu 9 | ab8e2728644c: Pulling dependent layers 10 | 511136ea3c5a: Download complete 11 | 5f0ffaa9455e: Download complete 12 | a300658979be: Download complete 13 | 904483ae0c30: Download complete 14 | ffdaafd1ca50: Download complete 15 | d047ae21eeaf: Download complete 16 | ``` 17 | 下載過程中,會輸出取得映像檔的每一層訊息。 18 | 19 | 該命令實際上相當於 `$ sudo docker pull registry.hub.docker.com/ubuntu:12.04` 命令,即從註冊服務器 `registry.hub.docker.com` 中的 `ubuntu` 倉庫來下載標記為 `12.04` 的映像檔。 20 | 21 | 有時候官方倉庫註冊服務器下載較慢,可以從其他倉庫下載。 22 | 從其它倉庫下載時需要指定完整的倉庫伺服器位址。例如 23 | ``` 24 | $ sudo docker pull dl.dockerpool.com:5000/ubuntu:12.04 25 | Pulling dl.dockerpool.com:5000/ubuntu 26 | ab8e2728644c: Pulling dependent layers 27 | 511136ea3c5a: Download complete 28 | 5f0ffaa9455e: Download complete 29 | a300658979be: Download complete 30 | 904483ae0c30: Download complete 31 | ffdaafd1ca50: Download complete 32 | d047ae21eeaf: Download complete 33 | ``` 34 | 35 | 完成後,即可隨時使用該映像檔了,例如建立一個容器,讓其中執行 bash。 36 | ``` 37 | $ sudo docker run -t -i ubuntu:12.04 /bin/bash 38 | root@fe7fc4bd8fc9:/# 39 | ``` 40 | -------------------------------------------------------------------------------- /image/rmi.md: -------------------------------------------------------------------------------- 1 | ## 移除本地端映像檔 2 | 如果要移除本地端的映像檔,可以使用 `docker rmi` 命令。注意 `docker rm` 命令是移除容器。 3 | ``` 4 | $ sudo docker rmi training/sinatra 5 | Untagged: training/sinatra:latest 6 | Deleted: 5bc342fa0b91cabf65246837015197eecfa24b2213ed6a51a8974ae250fedd8d 7 | Deleted: ed0fffdcdae5eb2c3a55549857a8be7fc8bc4241fb19ad714364cbfd7a56b22f 8 | Deleted: 5c58979d73ae448df5af1d8142436d81116187a7633082650549c52c3a2418f0 9 | ``` 10 | 11 | *注意:在刪除映像檔之前要先用 `docker rm` 刪掉依賴於這個映像檔的所有容器。 12 | -------------------------------------------------------------------------------- /image/save_load.md: -------------------------------------------------------------------------------- 1 | ## 儲存和載入映像檔 2 | 3 | ### 儲存映像檔 4 | 如果要建立映像檔到本地檔案,可以使用 `docker save` 命令。 5 | ``` 6 | $ sudo docker images 7 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 8 | ubuntu 14.04 c4ff7513909d 5 weeks ago 225.4 MB 9 | ... 10 | $sudo docker save -o ubuntu_14.04.tar ubuntu:14.04 11 | ``` 12 | 13 | ### 載入映像檔 14 | 可以使用 `docker load` 從建立的本地檔案中再匯入到本地映像檔庫,例如 15 | ``` 16 | $ sudo docker load --input ubuntu_14.04.tar 17 | ``` 18 | 或 19 | ``` 20 | $ sudo docker load < ubuntu_14.04.tar 21 | ``` 22 | 這將匯入映像檔以及其相關的元資料資訊(包括標籤等)。 23 | -------------------------------------------------------------------------------- /install/README.md: -------------------------------------------------------------------------------- 1 | # 安裝 2 | 官方網站上有各種環境下的 [安裝指南](https://docs.docker.com/installation/#installation),這裡主要介紹下 Ubuntu 和 CentOS 系列的安裝。 3 | -------------------------------------------------------------------------------- /install/centos.md: -------------------------------------------------------------------------------- 1 | ## CentOS 系列安裝 Docker 2 | 3 | Docker 支援 CentOS6 及以後的版本。 4 | 5 | ### CentOS6 6 | 7 | 對於 CentOS6,可以使用 [EPEL](https://fedoraproject.org/wiki/EPEL) 套件庫安裝 Docker,命令以下 8 | ```bash 9 | $ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm 10 | $ sudo yum install docker-io 11 | ``` 12 | 13 | ### CentOS7 14 | 15 | CentOS7 系統 `CentOS-Extras` 庫中已內建 Docker,可以直接安裝: 16 | ```bash 17 | $ sudo yum install docker 18 | ``` 19 | 20 | 安裝之後啟動 Docker 服務,並讓它隨系統啟動自動載入。 21 | ```bash 22 | $ sudo service docker start 23 | $ sudo chkconfig docker on 24 | ``` -------------------------------------------------------------------------------- /install/ubuntu.md: -------------------------------------------------------------------------------- 1 | ## Ubuntu 系列安裝 Docker 2 | 3 | ### 透過系統內建套件安裝 4 | Ubuntu 14.04 版本套件庫中已經內建了 Docker 套件,可以直接安裝。 5 | ```bash 6 | $ sudo apt-get update 7 | $ sudo apt-get install -y docker.io 8 | $ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker 9 | $ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker 10 | ``` 11 | 12 | 如果使用作業系統內建套件安裝 Docker,目前安裝的版本是比較舊的 0.9.1。 要安裝更新的版本,可以透過更新 Docker 套件庫的方式進行安裝。 13 | 14 | ### 透過Docker 套件庫安裝最新版本 15 | 要安裝最新的 Docker 版本,首先需要安裝 apt-transport-https 支援,之後透過新增套件庫來安裝。 16 | ```bash 17 | $ sudo apt-get install apt-transport-https 18 | $ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 19 | $ sudo bash -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" 20 | $ sudo apt-get update 21 | $ sudo apt-get install -y lxc-docker 22 | ``` 23 | 24 | ### 快速安裝法(建議使用) 25 | ```bash 26 | $ curl -sSL https://get.docker.com/ubuntu/ | sudo sh 27 | ``` 28 | 29 | ### 14.04 之前版本 30 | 如果是較舊版本的 Ubuntu 系統,需要先更新核心。 31 | ```bash 32 | $ sudo apt-get update 33 | $ sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring 34 | $ sudo reboot 35 | ``` 36 | 然後重複上面的步驟即可。 37 | 38 | 安裝之後啟動 Docker 服務。 39 | ```bash 40 | $ sudo service docker start 41 | ``` 42 | -------------------------------------------------------------------------------- /introduction/README.md: -------------------------------------------------------------------------------- 1 | # 簡介 2 | 3 | 本章將帶領你進入 Docker 的世界。 4 | 5 | 什麼是 Docker? 6 | 7 | 用它會帶來什麼樣的好處? 8 | 9 | 好吧,讓我們帶著問題開始這神奇之旅。 10 | -------------------------------------------------------------------------------- /introduction/what.md: -------------------------------------------------------------------------------- 1 | ## 什麼是 Docker 2 | Docker 是一個開源專案,誕生於 2013 年初,最初是 dotCloud 公司內部的一個業餘專案。它基於 Google 公司推出的 Go 語言實作。 3 | 專案後來加入了 Linux 基金會,遵從了 Apache 2.0 協議,原始碼在 [GitHub](https://github.com/docker/docker) 上進行維護。 4 | 5 | Docker 自開源後受到廣泛的關注和討論,以至於 dotCloud 公司後來都改名為 Docker Inc。Redhat 已經在其 RHEL6.5 中集中支援 Docker;Google 也在其 PaaS 產品中廣泛應用。 6 | 7 | Docker 專案的目標是實作輕量級的作業系統虛擬化解決方案。 8 | Docker 的基礎是 Linux 容器(LXC)等技術。 9 | 10 | 在 LXC 的基礎上 Docker 進行了進一步的封裝,讓使用者不需要去關心容器的管理,使得操作更為簡便。使用者操作 Docker 的容器就像操作一個快速輕量級的虛擬機一樣簡單。 11 | 12 | 下面的圖片比較了 Docker 和傳統虛擬化方式的不同之處,可見容器是在作業系統層面上實作虛擬化,直接使用本地主機的作業系統,而傳統方式則是在硬體層面實作。 13 | 14 | ![傳統虛擬化](../_images/virtualization.png) 15 | 16 | ![Docker](../_images/docker.png) 17 | -------------------------------------------------------------------------------- /introduction/why.md: -------------------------------------------------------------------------------- 1 | ## 為什麼要使用 Docker? 2 | 3 | 作為一種新興的虛擬化方式,Docker 跟傳統的虛擬化方式相比具有眾多的優勢。 4 | 5 | 首先,Docker 容器的啟動可以在秒級實作,這相比傳統的虛擬機方式要快得多。 6 | 其次,Docker 對系統資源的使用率很高,一台主機上可以同時執行數千個 Docker 容器。 7 | 8 | 容器除了執行其中應用外,基本不消耗額外的系統資源,使得應用的效能很高,同時系統資源消耗更少。傳統虛擬機方式執行 10 個不同的應用就要啟動 10 個虛擬機,而 Docker 只需要啟動 10 個隔離的應用即可。 9 | 10 | 具體說來,Docker 在以下幾個方面具有較大的優勢。 11 | 12 | ### 更快速的交付和部署 13 | 14 | 對開發和維運(DevOps)人員來說,最希望的就是一次建立或設定,可以在任意地方正常執行。 15 | 16 | 開發者可以使用一個標準的映像檔來建立一套開發容器,開發完成之後,維運人員可以直接使用這個容器來部署程式碼。 17 | Docker 可以快速建立容器,快速迭代應用程式,並讓整個過程全程可見,使團隊中的其他成員更容易理解應用程式是如何建立和工作的。 18 | Docker 容器很輕很快!容器的啟動時間是秒級的,大幅地節省開發、測試、部署的時間。 19 | 20 | ### 更有效率的虛擬化 21 | 22 | Docker 容器的執行不需要額外的虛擬化支援,它是核心層級的虛擬化,因此可以實作更高的效能和效率。 23 | 24 | ### 更輕鬆的遷移和擴展 25 | 26 | Docker 容器幾乎可以在任意的平台上執行,包括實體機器、虛擬機、公有雲、私有雲、個人電腦、伺服器等。 27 | 這種兼容性可以讓使用者把一個應用程式從一個平台直接遷移到另外一個。 28 | 29 | ### 更簡單的管理 30 | 使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分發和更新,從而實作自動化並且有效率的管理。 31 | 32 | ### 對比傳統虛擬機總結 33 | 34 | | 特性 | 容器 | 虛擬機 | 35 | | ---- | ---- | ------ | 36 | | 啟動 | 秒級 | 分鐘級 | 37 | | 硬碟容量 | 一般為 MB | 一般為 GB | 38 | | 效能 | 接近原生 | 比較慢 | 39 | | 系統支援量 | 單機支援上千個容器 | 一般幾十個 | 40 | -------------------------------------------------------------------------------- /network/README.md: -------------------------------------------------------------------------------- 1 | # Docker 中的網路功能介紹 2 | 3 | Docker 允許透過外部存取容器或容器互聯的方式來提供網路服務。 4 | -------------------------------------------------------------------------------- /network/linking.md: -------------------------------------------------------------------------------- 1 | ## 容器互聯 2 | 容器的連線(linking)系統是除了連接埠映射外,另一種跟容器中應用互動的方式。 3 | 4 | 該系統會在來源端容器和接收端容器之間創建一個隧道,接收端容器可以看到來源端容器指定的資訊。 5 | 6 | ### 自訂容器命名 7 | 連線系統依據容器的名稱來執行。因此,首先需要自訂一個好記的容器命名。 8 | 9 | 雖然當創建容器的時候,系統會預設分配一個名字。自訂命名容器有2個好處: 10 | * 自訂的命名,比較好記,比如一個web應用容器我們可以給它起名叫web 11 | * 當要連線其他容器時候,可以作為一個有用的參考點,比如連線web容器到db容器 12 | 13 | 使用 `--name` 標記可以為容器自訂命名。 14 | ```bash 15 | $ sudo docker run -d -P --name web training/webapp python app.py 16 | ``` 17 | 18 | 使用 `docker ps` 來驗證設定的命名。 19 | ```bash 20 | $ sudo docker ps -l 21 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 22 | aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web 23 | ``` 24 | 也可以使用 `docker inspect` 來查看容器的名字 25 | ```bash 26 | $ sudo docker inspect -f "{{ .Name }}" aed84ee21bde 27 | /web 28 | ``` 29 | 注意:容器的名稱是唯一的。如果已經命名了一個叫 web 的容器,當你要再次使用 web 這個名稱的時候,需要先用`docker rm` 來刪除之前建立的同名容器。 30 | 31 | 在執行 `docker run` 的時候如果新增 `--rm` 標記,則容器在終止後會立刻刪除。注意,`--rm` 和 `-d` 參數不能同時使用。 32 | 33 | ###容器互聯 34 | 35 | 使用 `--link` 參數可以讓容器之間安全的進行互動。 36 | 37 | 下面先建立一個新的資料庫容器。 38 | 39 | ```bash 40 | $ sudo docker run -d --name db training/postgres 41 | ``` 42 | 刪除之前建立的 web 容器 43 | ```bash 44 | $ docker rm -f web 45 | ``` 46 | 然後建立一個新的 web 容器,並將它連線到 db 容器 47 | ```bash 48 | $ sudo docker run -d -P --name web --link db:db training/webapp python app.py 49 | ``` 50 | 此時,db 容器和 web 容器建立互聯關系。 51 | 52 | `--link` 參數的格式為 `--link name:alias`,其中 `name` 是要連線的容器名稱,`alias` 是這個連線的別名。 53 | 54 | 使用 `docker ps` 來查看容器的連線 55 | ```bash 56 | $ docker ps 57 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 58 | 349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db 59 | aed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web 60 | ``` 61 | 可以看到自訂命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。這表示 web 容器連線到 db 容器,web 容器將被允許存取 db 容器的訊息。 62 | 63 | Docker 在兩個互聯的容器之間創建了一個安全隧道,而且不用映射它們的連接埠到宿主主機上。在啟動 db 容器的時候並沒有使用 `-p` 和 `-P` 標記,從而避免了暴露資料庫連接埠到外部網路上。 64 | 65 | Docker 透過 2 種方式為容器公開連線訊息: 66 | * 環境變數 67 | * 更新 `/etc/hosts` 檔案 68 | 69 | 使用 `env` 命令來查看 web 容器的環境變數 70 | ```bash 71 | $ sudo docker run --rm --name web2 --link db:db training/webapp env 72 | . . . 73 | DB_NAME=/web2/db 74 | DB_PORT=tcp://172.17.0.5:5432 75 | DB_PORT_5000_TCP=tcp://172.17.0.5:5432 76 | DB_PORT_5000_TCP_PROTO=tcp 77 | DB_PORT_5000_TCP_PORT=5432 78 | DB_PORT_5000_TCP_ADDR=172.17.0.5 79 | . . . 80 | ``` 81 | 其中 DB_ 開頭的環境變數是供 web 容器連線 db 容器使用,前綴採用大寫的連線別名。 82 | 83 | 除了環境變量,Docker 還新增 host 訊息到父容器的 `/etc/hosts` 的檔案。下面是父容器 web 的 hosts 檔案 84 | ```bash 85 | $ sudo docker run -t -i --rm --link db:db training/webapp /bin/bash 86 | root@aed84ee21bde:/opt/webapp# cat /etc/hosts 87 | 172.17.0.7 aed84ee21bde 88 | . . . 89 | 172.17.0.5 db 90 | ``` 91 | 這裡有 2 個 hosts,第一個是 web 容器,web 容器用 id 作為他的主機名,第二個是 db 容器的 ip 和主機名。 92 | 可以在 web 容器中安裝 ping 命令來測試跟db容器的連通。 93 | ```bash 94 | root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping 95 | root@aed84ee21bde:/opt/webapp# ping db 96 | PING db (172.17.0.5): 48 data bytes 97 | 56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms 98 | 56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms 99 | 56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms 100 | ``` 101 | 用 ping 來測試db容器,它會解析成 `172.17.0.5`。 102 | *注意:官方的 ubuntu 映像檔預設沒有安裝 ping,需要自行安裝。 103 | 104 | 使用者可以連線多個子容器到父容器,比如可以連線多個 web 到 db 容器上。 105 | -------------------------------------------------------------------------------- /network/port_mapping.md: -------------------------------------------------------------------------------- 1 | ## 外部存取容器 2 | 3 | 容器中可以執行一些網路應用,要讓外部也可以存取這些應用,可以通過 `-P` 或 `-p` 參數來指定連接埠映射。 4 | 5 | 當使用 -P 參數時,Docker 會隨機映射一個 `49000~49900` 的連接埠到內部容器開放的網路連接埠。 6 | 7 | 使用 `docker ps` 可以看到,本地主機的 49155 被映射到了容器的 5000 連接埠。此時連結本機的 49155 連接埠即可連結容器內 web 應用提供的介面。 8 | ```bash 9 | $ sudo docker run -d -P training/webapp python app.py 10 | $ sudo docker ps -l 11 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 12 | bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse 13 | ``` 14 | 同樣的,可以透過 `docker logs` 命令來查看應用的訊息。 15 | ```bash 16 | $ sudo docker logs -f nostalgic_morse 17 | * Running on http://0.0.0.0:5000/ 18 | 10.0.2.2 - - [23/May/2014 20:16:31] "GET / HTTP/1.1" 200 - 19 | 10.0.2.2 - - [23/May/2014 20:16:31] "GET /favicon.ico HTTP/1.1" 404 - 20 | ``` 21 | 22 | -p(小寫的)則可以指定要映射的連接埠,並且在一個指定連接埠上只可以綁定一個容器。支援的格式有 `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort`。 23 | 24 | ### 映射所有遠端位址 25 | 使用 `hostPort:containerPort` 格式本地的 5000 連接埠映射到容器的 5000 連接埠,可以執行 26 | ```bash 27 | $ sudo docker run -d -p 5000:5000 training/webapp python app.py 28 | ``` 29 | 此時預設會綁定本地所有遠端上的所有位址。 30 | 31 | ### 映射到指定位址的指定連接埠 32 | 可以使用 `ip:hostPort:containerPort` 格式指定映射使用一個特定位址,比如 localhost 位址 127.0.0.1 33 | ```bash 34 | $ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py 35 | ``` 36 | ### 映射到指定位址的任意連接埠 37 | 使用 `ip::containerPort` 綁定 localhost 的任意連接埠到容器的 5000 連接埠,本地主機會自動分配一個連接埠。 38 | ```bash 39 | $ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py 40 | ``` 41 | 還可以使用 udp 標記來指定 udp 連接埠 42 | ``` 43 | $ sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py 44 | ``` 45 | ### 查看映射連接埠配置 46 | 使用 `docker port` 來查看當前映射的連接埠配置,也可以查看到綁定的位址 47 | ```bash 48 | $ docker port nostalgic_morse 5000 49 | 127.0.0.1:49155. 50 | ``` 51 | 注意: 52 | * 容器有自己的內部網路和 ip 位址(使用 `docker inspect` 可以獲取所有的變數,Docker 還可以有一個可變的網路設定。) 53 | * -p 標記可以多次使用來綁定多個連接埠 54 | 55 | 例如 56 | ```bash 57 | $ sudo docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py 58 | ``` 59 | -------------------------------------------------------------------------------- /repository/README.md: -------------------------------------------------------------------------------- 1 | # 倉庫 2 | 3 | 倉庫(Repository)是集中存放映像檔的地方。 4 | 5 | 一個容易混淆的概念是註冊伺服器(Registry)。實際上註冊伺服器是管理倉庫的具體伺服器,每個伺服器上可以有多個倉庫,而每個倉庫下面有多個映像檔。從這方面來說,倉庫可以被認為是一個具體的專案或目錄。例如對於倉庫位址 `dl.dockerpool.com/ubuntu` 來說,`dl.dockerpool.com` 是註冊伺服器位址,`ubuntu` 是倉庫名。 6 | 7 | 大部分時候,並不需要嚴格區分這兩者的概念。 -------------------------------------------------------------------------------- /repository/config.md: -------------------------------------------------------------------------------- 1 | ## 倉庫設定檔案 2 | Docker 的 Registry 利用設定檔案提供了一些倉庫的模組(flavor),使用者可以直接使用它們來進行開發或生產部署。 3 | 4 | ### 模組 5 | 6 | 在 `config_sample.yml` 檔案中,可以看到一些現成的模組段: 7 | * `common`:基礎設定 8 | * `local`:儲存資料到本地檔案系統 9 | * `s3`:儲存資料到 AWS S3 中 10 | * `dev`:使用 `local` 模組的基本設定 11 | * `test`:單元測試使用 12 | * `prod`:生產環境設定(基本上跟s3設定類似) 13 | * `gcs`:儲存資料到 Google 的雲端 14 | * `swift`:儲存資料到 OpenStack Swift 服務 15 | * `glance`:儲存資料到 OpenStack Glance 服務,本地檔案系統為後備 16 | * `glance-swift`:儲存資料到 OpenStack Glance 服務,Swift 為後備 17 | * `elliptics`:儲存資料到 Elliptics key/value 儲存 18 | 19 | 使用者也可以新增自訂的模版段。 20 | 21 | 預設情況下使用的模組是 `dev`,要使用某個模組作為預設值,可以新增 `SETTINGS_FLAVOR` 到環境變數中,例如 22 | ```bash 23 | export SETTINGS_FLAVOR=dev 24 | ``` 25 | 26 | 另外,設定檔案中支援從環境變數中載入值,語法格式為 `_env:VARIABLENAME[:DEFAULT]`。 27 | 28 | ### 範例設定 29 | 30 | ``` 31 | common: 32 | loglevel: info 33 | search_backend: "_env:SEARCH_BACKEND:" 34 | sqlalchemy_index_database: 35 | "_env:SQLALCHEMY_INDEX_DATABASE:sqlite:////tmp/docker-registry.db" 36 | 37 | prod: 38 | loglevel: warn 39 | storage: s3 40 | s3_access_key: _env:AWS_S3_ACCESS_KEY 41 | s3_secret_key: _env:AWS_S3_SECRET_KEY 42 | s3_bucket: _env:AWS_S3_BUCKET 43 | boto_bucket: _env:AWS_S3_BUCKET 44 | storage_path: /srv/docker 45 | smtp_host: localhost 46 | from_addr: docker@myself.com 47 | to_addr: my@myself.com 48 | 49 | dev: 50 | loglevel: debug 51 | storage: local 52 | storage_path: /home/myself/docker 53 | 54 | test: 55 | storage: local 56 | storage_path: /tmp/tmpdockertmp 57 | ``` 58 | 59 | ### 選項 60 | -------------------------------------------------------------------------------- /repository/dockerhub.md: -------------------------------------------------------------------------------- 1 | ## Docker Hub 2 | 3 | 目前 Docker 官方維護了一個公共倉庫 [Docker Hub](https://hub.docker.com/),其中已經包括了超過 15,000 的映像檔。大部分需求都可以透過在 Docker Hub 中直接下載映像檔來實作。 4 | 5 | ### 登錄 6 | 7 | - 可以透過執行 `docker login` 命令來輸入使用者名稱、密碼和電子信箱來完成註冊和登錄。 8 | - 註冊成功後,本地使用者目錄的 `.dockercfg` 中將保存使用者的認證訊息。 9 | 10 | ### 基本操作 11 | 12 | 使用者無需登錄即可透過 `docker search` 命令來查詢官方倉庫中的映像檔,並利用 `docker pull` 命令來將它下載到本地。 13 | 例如以 centos 為關鍵字進行搜尋: 14 | 15 | ```bash 16 | $ sudo docker search centos 17 | NAME DESCRIPTION STARS OFFICIAL AUTOMATED 18 | centos The official build of CentOS. 465 [OK] 19 | tianon/centos CentOS 5 and 6, created using rinse instea... 28 20 | blalor/centos Bare-bones base CentOS 6.5 image 6 [OK] 21 | saltstack/centos-6-minimal 6 [OK] 22 | tutum/centos-6.4 DEPRECATED. Use tutum/centos:6.4 instead. ... 5 [OK] 23 | ... 24 | ``` 25 | 26 | 可以看到顯示了很多包含關鍵字的映像檔,其中包括映像檔名字、描述、星級(表示該映像檔的受歡迎程度)、是否官方建立、是否自動建立。 27 | 官方的映像檔說明是官方專案組建立和維護的,automated 資源允許使用者驗證映像檔的來源和內容。 28 | 29 | 根據是否是官方提供,可將映像檔資源分為兩類。 30 | 一種是類似 centos 這樣的基礎映像檔,被稱為基礎或根映像檔。這些基礎映像檔是由 Docker 公司建立、驗證、支援、提供。這樣的映像檔往往使用單個單詞作為名字。 31 | 還有一種類型,比如 `tianon/centos` 映像檔,它是由 Docker 的使用者建立並維護的,往往帶有使用者名稱前綴。可以透過前綴 `user_name/` 來指定使用某個使用者提供的映像檔,比如 tianon 使用者。 32 | 33 | 另外,在查詢的時候透過 `-s N` 參數可以指定僅顯示評價為 `N` 星以上的映像檔。 34 | 35 | 下載官方 centos 映像檔到本地。 36 | 37 | ```bash 38 | $ sudo docker pull centos 39 | Pulling repository centos 40 | 0b443ba03958: Download complete 41 | 539c0211cd76: Download complete 42 | 511136ea3c5a: Download complete 43 | 7064731afe90: Download complete 44 | ``` 45 | 46 | 使用者也可以在登錄後透過 `docker push` 命令來將映像檔推送到 Docker Hub。 47 | 48 | ### 自動建立 49 | 50 | 自動建立(Automated Builds)功能對於需要經常升級映像檔內程式來說,十分方便。 51 | 有時候,使用者建立了映像檔,安裝了某個軟體,如果軟體發布新版本則需要手動更新映像檔。。 52 | 53 | 而自動建立允許使用者透過 Docker Hub 指定跟蹤一個目標網站(目前支援 [GitHub](github.org) 或 [BitBucket](bitbucket.org))上的專案,一旦專案發生新的提交,則自動執行建立。 54 | 55 | 要設定自動建立,包括以下的步驟: 56 | 57 | - 建立並登陸 Docker Hub,以及目標網站; 58 | - 在目標網站中連結帳戶到 Docker Hub; 59 | - 在 Docker Hub 中 [設定一個自動建立](https://registry.hub.docker.com/builds/add/); 60 | - 選取一個目標網站中的專案(需要含 Dockerfile)和分支; 61 | - 指定 Dockerfile 的位置,並提交建立。 62 | 63 | 之後,可以 在Docker Hub 的 [自動建立頁面](https://registry.hub.docker.com/builds/) 中跟蹤每次建立的狀態。 64 | -------------------------------------------------------------------------------- /repository/local_repo.md: -------------------------------------------------------------------------------- 1 | ## 私有倉庫 2 | 3 | 有時候使用 Docker Hub 這樣的公共倉庫可能不方便,使用者可以建立一個本地倉庫供私人使用。本節介紹如何使用本地倉庫。 4 | `docker-registry` 是官方提供的工具,可以用於建立私有的映像檔倉庫。 5 | 6 | ### 安裝執行 docker-registry 7 | 8 | #### 容器執行 9 | 10 | 在安裝了 Docker 後,可以透過取得官方 registry 映像檔來執行。 11 | 12 | ```bash 13 | $ sudo docker run -d -p 5000:5000 registry 14 | ``` 15 | 16 | 這將使用官方的 registry 映像檔來啟動本地的私有倉庫。使用者可以透過指定參數來設定私有倉庫位置,例如設定映像檔儲存到 Amazon S3 服務。 17 | 18 | ```bash 19 | $ sudo docker run \ 20 | -e SETTINGS_FLAVOR=s3 \ 21 | -e AWS_BUCKET=acme-docker \ 22 | -e STORAGE_PATH=/registry \ 23 | -e AWS_KEY=AKIAHSHB43HS3J92MXZ \ 24 | -e AWS_SECRET=xdDowwlK7TJajV1Y7EoOZrmuPEJlHYcNP2k4j49T \ 25 | -e SEARCH_BACKEND=sqlalchemy \ 26 | -p 5000:5000 \ 27 | registry 28 | ```` 29 | 30 | 此外,還可以指定本地路徑(如 `/home/user/registry-conf` )下的設定檔案。 31 | 32 | ```bash 33 | $ sudo docker run -d -p 5000:5000 -v /home/user/registry-conf:/registry-conf -e DOCKER_REGISTRY_CONFIG=/registry-conf/config.yml registry 34 | ``` 35 | 36 | 預設情況下,倉庫會被建立在容器的 `/tmp/registry` 下。可以透過 `-v` 參數來將映像檔檔案存放在本地的指定路徑。 37 | 例以下面的例子將上傳的映像檔放到 `/opt/data/registry` 目錄。 38 | 39 | ```bash 40 | $ sudo docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry 41 | ``` 42 | 43 | #### 本地安裝 44 | 45 | 對於 Ubuntu 或 CentOS 等發行版,可以直接透過套件庫安裝。 46 | * Ubuntu 47 | ```bash 48 | $ sudo apt-get install -y build-essential python-dev libevent-dev python-pip liblzma-dev swig 49 | $ sudo pip install docker-registry 50 | ``` 51 | * CentOS 52 | ```bash 53 | $ sudo yum install -y python-devel libevent-devel python-pip gcc xz-devel 54 | $ sudo python-pip install docker-registry 55 | ``` 56 | 57 | 也可以從 [docker-registry](https://github.com/docker/docker-registry) 專案下載原始碼進行安裝。 58 | 59 | ```bash 60 | $ sudo apt-get install build-essential python-dev libevent-dev python-pip libssl-dev liblzma-dev libffi-dev 61 | $ git clone https://github.com/docker/docker-registry.git 62 | $ cd docker-registry 63 | $ sudo python setup.py install 64 | ``` 65 | 66 | 然後修改設定檔案,主要修改 dev 模板段的 `storage_path` 到本地的儲存倉庫的路徑。 67 | 68 | ```bash 69 | $ cp config/config_sample.yml config/config.yml 70 | ``` 71 | 72 | 之後啟動 Web 服務。 73 | 74 | ```bash 75 | $ sudo gunicorn -c contrib/gunicorn.py docker_registry.wsgi:application 76 | ``` 77 | 78 | 或者 79 | 80 | ```bash 81 | $ sudo gunicorn --access-logfile - --error-logfile - -k gevent -b 0.0.0.0:5000 -w 4 --max-requests 100 docker_registry.wsgi:application 82 | ``` 83 | 84 | 此時使用連結本地的 5000 連接埠,看到輸出 docker-registry 的版本訊息說明執行成功。 85 | 86 | *註:`config/config_sample.yml` 檔案是範例設定檔案。 87 | 88 | ### 在私有倉庫上傳、下載、搜尋映像檔 89 | 90 | 1. 建立好私有倉庫之後,就可以使用 `docker tag` 來標記一個映像檔,然後推送它到倉庫,別的機器上就可以下載下來了。例如私有倉庫位址為 `192.168.7.26:5000`。 91 | 2. 先在本機查看已有的映像檔。 92 | ```bash 93 | $ sudo docker images 94 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 95 | ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB 96 | ubuntu 14.04 ba5877dc9bec 6 weeks ago 192.7 MB 97 | ``` 98 | 3. 使用`docker tag` 將 `ba58` 這個映像檔標記為 `192.168.7.26:5000/test`(格式為 `docker tag IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]`)。 99 | ```bash 100 | $ sudo docker tag ba58 192.168.7.26:5000/test 101 | root ~ # docker images 102 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 103 | ubuntu 14.04 ba5877dc9bec 6 weeks ago 192.7 MB 104 | ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB 105 | 192.168.7.26:5000/test latest ba5877dc9bec 6 weeks ago 192.7 MB 106 | ``` 107 | 4. 使用 `docker push` 上傳標記的映像檔。 108 | ```bash 109 | $ sudo docker push 192.168.7.26:5000/test 110 | The push refers to a repository [192.168.7.26:5000/test] (len: 1) 111 | Sending image list 112 | Pushing repository 192.168.7.26:5000/test (1 tags) 113 | Image 511136ea3c5a already pushed, skipping 114 | Image 9bad880da3d2 already pushed, skipping 115 | Image 25f11f5fb0cb already pushed, skipping 116 | Image ebc34468f71d already pushed, skipping 117 | Image 2318d26665ef already pushed, skipping 118 | Image ba5877dc9bec already pushed, skipping 119 | Pushing tag for rev [ba5877dc9bec] on {http://192.168.7.26:5000/v1/repositories/test/tags/latest} 120 | ``` 121 | 5. 用 curl 查看倉庫中的映像檔。 122 | ```bash 123 | $ curl http://192.168.7.26:5000/v1/search 124 | {"num_results": 7, "query": "", "results": [{"description": "", "name": "library/miaxis_j2ee"}, {"description": "", "name": "library/tomcat"}, {"description": "", "name": "library/ubuntu"}, {"description": "", "name": "library/ubuntu_office"}, {"description": "", "name": "library/desktop_ubu"}, {"description": "", "name": "dockerfile/ubuntu"}, {"description": "", "name": "library/test"}]} 125 | ``` 126 | 這裡可以看到 `{"description": "", "name": "library/test"}`,表明映像檔已經被成功上傳了。 127 | 6. 現在可以到另外一臺機器去下載這個映像檔。 128 | ```bash 129 | $ sudo docker pull 192.168.7.26:5000/test 130 | Pulling repository 192.168.7.26:5000/test 131 | ba5877dc9bec: Download complete 132 | 511136ea3c5a: Download complete 133 | 9bad880da3d2: Download complete 134 | 25f11f5fb0cb: Download complete 135 | ebc34468f71d: Download complete 136 | 2318d26665ef: Download complete 137 | $ sudo docker images 138 | REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 139 | 192.168.7.26:5000/test latest ba5877dc9bec 6 weeks ago 192.7 MB 140 | ``` 141 | 7. 可以使用 [這個腳本](https://github.com/yeasy/docker_practice/raw/master/_local/push_images.sh) 批次上傳本地的映像檔到註冊伺服器中,預設為本地註冊伺服器 `127.0.0.1:5000`。例如: 142 | ```bash 143 | $ wget https://github.com/yeasy/docker_practice/raw/master/_local/push_images.sh; sudo chmod a+x push_images.sh 144 | $ ./push_images.sh ubuntu:latest centos:centos7 145 | The registry server is 127.0.0.1 146 | Uploading ubuntu:latest... 147 | The push refers to a repository [127.0.0.1:5000/ubuntu] (len: 1) 148 | Sending image list 149 | Pushing repository 127.0.0.1:5000/ubuntu (1 tags) 150 | Image 511136ea3c5a already pushed, skipping 151 | Image bfb8b5a2ad34 already pushed, skipping 152 | Image c1f3bdbd8355 already pushed, skipping 153 | Image 897578f527ae already pushed, skipping 154 | Image 9387bcc9826e already pushed, skipping 155 | Image 809ed259f845 already pushed, skipping 156 | Image 96864a7d2df3 already pushed, skipping 157 | Pushing tag for rev [96864a7d2df3] on {http://127.0.0.1:5000/v1/repositories/ubuntu/tags/latest} 158 | Untagged: 127.0.0.1:5000/ubuntu:latest 159 | Done 160 | Uploading centos:centos7... 161 | The push refers to a repository [127.0.0.1:5000/centos] (len: 1) 162 | Sending image list 163 | Pushing repository 127.0.0.1:5000/centos (1 tags) 164 | Image 511136ea3c5a already pushed, skipping 165 | 34e94e67e63a: Image successfully pushed 166 | 70214e5d0a90: Image successfully pushed 167 | Pushing tag for rev [70214e5d0a90] on {http://127.0.0.1:5000/v1/repositories/centos/tags/centos7} 168 | Untagged: 127.0.0.1:5000/centos:centos7 169 | Done 170 | ``` -------------------------------------------------------------------------------- /security/README.md: -------------------------------------------------------------------------------- 1 | # 安全 2 | 評估 Docker 的安全性時,主要考慮三個方面: 3 | * 由核心的命名空間和控制組機制提供的容器內在安全 4 | * Docker程式(特別是服務端)本身的抗攻擊性 5 | * 核心安全性的加強機制對容器安全性的影響 6 | -------------------------------------------------------------------------------- /security/control_group.md: -------------------------------------------------------------------------------- 1 | ## 控制組 2 | 控制組是 Linux 容器機制的另外一個關鍵元件,負責實作資源的統計和限制。 3 | 4 | 它提供了很多有用的特性;以及確保各個容器可以公平地分享主機的記憶體、CPU、磁碟 IO 等資源;當然,更重要的是,控制組確保了當容器內的資源使用產生負載時不會連累主機系統。 5 | 6 | 盡管控制組不負責隔離容器之間相互存取、處理資料和程式,它在防止分散式阻斷服務(DDOS)攻擊方面是必不可少的。尤其是在多使用者的平台(比如公有或私有的 PaaS)上,控制組十分重要。例如,當某些應用程式表現異常的時候,可以保證一致地正常執行和效能。 7 | 8 | 控制組機制始於 2006 年,核心從 2.6.24 版本開始被引入。 9 | -------------------------------------------------------------------------------- /security/daemon_sec.md: -------------------------------------------------------------------------------- 1 | ## Docker服務端的防護 2 | 執行一個容器或應用程式的核心是透過 Docker 服務端。Docker 服務的執行目前需要 root 權限,因此其安全性十分關鍵。 3 | 4 | 首先,確保只有可信的使用者才可以存取 Docker 服務。Docker 允許使用者在主機和容器間共享檔案夾,同時不需要限制容器的存取權限,這就容易讓容器突破資源限制。例如,惡意使用者啟動容器的時候將主機的根目錄`/`映射到容器的 `/host` 目錄中,那麽容器理論上就可以對主機的檔案系統進行任意修改了。這聽起來很瘋狂?但是事實上幾乎所有虛擬化系統都允許類似的資源共享,而沒法禁止使用者共享主機根檔案系統到虛擬機系統。 5 | 6 | 這將會造成很嚴重的安全後果。因此,當提供容器建立服務時(例如透過一個 web 伺服器),要更加注意進行參數的安全檢查,防止惡意的使用者用特定參數來建立一些破壞性的容器 7 | 8 | 為了加強對服務端的保護,Docker 的 REST API(客戶端用來跟服務端通訊)在 0.5.2 之後使用本地的 Unix socket機制替代了原先綁定在 127.0.0.1 上的 TCP socket,因為後者容易遭受跨站腳本攻擊。現在使用者使用 Unix 權限檢查來加強socket的存取安全。 9 | 10 | 使用者仍可以利用 HTTP 提供 REST API 存取。建議使用安全機制,確保只有可信的網路或 VPN,或憑證保護機制(例如受保護的 stunnel 和 SSL 認證)下的存取可以進行。此外,還可以使用 HTTPS 和憑證來加強保護。 11 | 12 | 最近改進的 Linux 命名空間機制將可以實作使用非 root 使用者來執行全功能的容器。這將從根本上解決了容器和主機之間共享檔案系統而引起的安全問題。 13 | 14 | 終極目標是改進 2 個重要的安全特性: 15 | * 將容器的 root 使用者映射到本地主機上的非 root 使用者,減輕容器和主機之間因權限提升而引起的安全問題; 16 | * 允許 Docker 服務端在非 root 權限下執行,利用安全可靠的子行程來代理執行需要特權權限的操作。這些子行程將只允許在限定範圍內進行操作,例如僅僅負責虛擬網路設定或檔案系統管理、設定操作等。 17 | 18 | 最後,建議採用專用的伺服器來執行 Docker 和相關的管理服務(例如管理服務比如 ssh 監控和程式監控、管理工具 nrpe、collectd 等)。其它的業務服務都放到容器中去執行。 19 | -------------------------------------------------------------------------------- /security/kernel_capability.md: -------------------------------------------------------------------------------- 1 | ## 核心能力機制 2 | 3 | 能力機制(Capability)是 Linux 核心一個強大的特性,可以提供細緻的權限存取控制。 4 | Linux 核心自 2.2 版本起就支援能力機制,它將權限劃分為更加細緻的操作能力,既可以作用在程式上,也可以作用在檔案上。 5 | 6 | 例如,一個 Web 服務程式只需要綁定一個低於 1024 的連接埠的權限,並不需要 root 權限。那麽它只需要被授權 `net_bind_service` 能力即可。此外,還有很多其他的類似能力來避免程式取得 root 權限。 7 | 8 | 預設情況下,Docker 啟動的容器被嚴格限制只允許使用核心的一部分能力。 9 | 10 | 使用能力機制對加強 Docker 容器的安全有很多好處。通常,在伺服器上會執行一堆需要特權權限的程式,包括有 ssh、cron、syslogd、硬體管理工具模組(例如負載模組)、網路設定工具等等。容器跟這些程式是不同的,因為幾乎所有的特權程式都由容器以外的支援系統來進行管理。 11 | * ssh 存取被主機上ssh服務來管理; 12 | * cron 通常應該作為使用者程式執行,權限交給使用它服務的應用來處理; 13 | * 日誌系統可由 Docker 或第三方服務管理; 14 | * 硬體管理無關緊要,容器中也就無需執行 udevd 以及類似服務; 15 | * 網路管理也都在主機上設定,除非特殊需求,容器不需要對網路進行設定。 16 | 17 | 從上面的例子可以看出,大部分情況下,容器並不需要“真正的” root 權限,容器只需要少數的能力即可。為了加強安全,容器可以禁用一些沒必要的權限。 18 | * 完全禁止任何 mount 操作; 19 | * 禁止直接存取本地主機的socket; 20 | * 禁止存取一些檔案系統的操作,比如建立新的設備、修改檔案屬性等; 21 | * 禁止模組載入。 22 | 23 | 這樣,就算攻擊者在容器中取得了 root 權限,也不能獲得本地主機的較高權限,能進行的破壞也有限。 24 | 25 | 預設情況下,Docker採用 [白名單](https://github.com/docker/docker/blob/master/daemon/execdriver/native/template/default_template.go) 機制,禁用 [必需功能](https://github.com/docker/docker/blob/master/daemon/execdriver/native/template/default_template.go) 之外的其它權限。 26 | 當然,使用者也可以根據自身需求來為 Docker 容器啟用額外的權限。 27 | -------------------------------------------------------------------------------- /security/kernel_ns.md: -------------------------------------------------------------------------------- 1 | ## 核心命名空間 2 | Docker 容器和 LXC 容器很相似,所提供的安全特性也差不多。當用 `docker run` 啟動一個容器時,在後臺 Docker 為容器建立了一個獨立的命名空間和控制組集合。 3 | 4 | 命名空間提供了最基礎也是最直接的隔離,在容器中執行的程式不會被執行在主機上的程式和其它容器發現和作用。 5 | 6 | 每個容器都有自己獨有的網路堆疊,意味著它們不能存取其他容器的 sockets 或介面。不過,如果主機系統上做了相應的設定,容器可以像跟主機互動一樣的和其他容器互動。當指定公共連接埠或使用 links 來連線 2 個容器時,容器就可以相互通訊了(可以根據設定來限制通訊的策略)。 7 | 8 | 從網路架構的角度來看,所有的容器透過本地主機的橋接器介面相互通訊,就像物理機器透過物理交換機通訊一樣。 9 | 10 | 那麽,核心中實作命名空間和私有網路的代碼是否足夠成熟? 11 | 12 | 核心命名空間從 2.6.15 版本(2008 年 7 月發布)之後被引入,數年間,這些機制的可靠性在諸多大型生產系統中被實踐驗證。 13 | 14 | 實際上,命名空間的想法和設計提出的時間要更早,最初是為了在核心中引入一種機制來實作 [OpenVZ](http://en.wikipedia.org/wiki/OpenVZ) 的特性。 15 | 而 OpenVZ 專案早在 2005 年就發布了,其設計和實作都已經十分成熟。 16 | -------------------------------------------------------------------------------- /security/other_feature.md: -------------------------------------------------------------------------------- 1 | ## 其它安全特性 2 | 除了能力機制之外,還可以利用一些現有的安全機制來增強使用 Docker 的安全性,例如 TOMOYO 、 AppArmor 、 SELinux 、 GRSEC 等。 3 | 4 | Docker 當前預設只啟用了能力機制。使用者可以採用多種方案來加強 Docker 主機的安全,例如: 5 | * 在核心中啟用 GRSEC 和 PAX,這將增加很多編譯和執行時的安全檢查;透過位址隨機化避免惡意探測等。並且,啟用該特性不需要 Docker 進行任何設定。 6 | * 使用一些有增強安全特性的容器模板,比如帶 AppArmor 的模板和 Redhat 帶 SELinux 策略的模板。這些模板提供了額外的安全特性。 7 | * 使用者可以自訂存取控制機制來定制安全策略。 8 | 9 | 跟其它新增到 Docker 容器的第三方工具一樣(比如網路拓撲和檔案系統共享),有很多類似的機制,在不改變 Docker 核心情況下就可以強化現有的容器。 10 | -------------------------------------------------------------------------------- /security/summary.md: -------------------------------------------------------------------------------- 1 | ## 總結 2 | 總體來看,Docker 容器還是十分安全的,特別是在容器內不使用 root 權限來執行程式的話。 3 | 4 | 另外,使用者可以使用現有工具,比如 AppArmor 、 SELinux 、 GRSEC 來增強安全性;甚至自己在核心中實作更複雜的安全機制。 5 | -------------------------------------------------------------------------------- /underly/README.md: -------------------------------------------------------------------------------- 1 | # 底層實作 2 | 3 | Docker 底層的核心技術包括 Linux 上的命名空間(Namespaces)、控制組(Control groups)、Union 檔案系統(Union file systems)和容器格式(Container format)。 4 | 5 | 我們知道,傳統的虛擬機透過在宿主主機中執行 hypervisor 來模擬一整套完整的硬體環境提供給虛擬機的作業系統。虛擬機系統看到的環境是可限制的,也是彼此隔離的。 6 | 這種直接的做法實作了對資源最完整的封裝,但很多時候往往意味著系統資源的浪費。 7 | 例如,以宿主機和虛擬機系統都為 Linux 系統為例,虛擬機中執行的應用其實可以利用宿主機系統中的執行環境。 8 | 9 | 我們知道,在作業系統中,包括核心、檔案系統、網路、PID、UID、IPC、記憶體、硬盤、CPU 等等,所有的資源都是應用程式直接共享的。 10 | 要想實作虛擬化,除了要實作對記憶體、CPU、網路 IO、硬盤 IO、儲存空間等的限制外,還要實作檔案系統、網路、PID、UID、IPC 等等的相互隔離。 11 | 12 | 前者相對容易實作一些,後者則需要宿主機系統的深入支援。 13 | 14 | 隨著 Linux 系統對於命名空間功能的完善實作,程式員已經可以實作上面的所有需求,讓某些程式在彼此隔離的命名空間中執行。大家雖然都共用一個核心和某些執行時環境(例如一些系統命令和系統函式庫),但是彼此卻看不到,都以為系統中只有自己的存在。這種機制就是容器(Container),利用命名空間來做權限的隔離控制,利用 cgroups 來做資源分配。 15 | -------------------------------------------------------------------------------- /underly/arch.md: -------------------------------------------------------------------------------- 1 | ## 基本架構 2 | 3 | Docker 採用了 C/S 架構,包括客戶端和服務端。 4 | Docker daemon 作為服務端接受來自客戶的請求,並處理這些請求(建立、執行、分發容器)。 5 | 客戶端和服務端既可以執行在一個機器上,也可透過 socket 或者 RESTful API 來進行通訊。 6 | 7 | ![Docker 基本架構](../_images/docker_arch.png) 8 | 9 | Docker daemon 一般在宿主主機後臺執行,等待接收來自客戶端的消息。 10 | Docker 客戶端則為使用者提供一系列可執行命令,使用者用這些命令實作跟 Docker daemon 互動。 11 | -------------------------------------------------------------------------------- /underly/cgroups.md: -------------------------------------------------------------------------------- 1 | ## 控制組 2 | 3 | 控制組([cgroups](http://en.wikipedia.org/wiki/Cgroups))是 Linux 核心的一個特性,主要用來對共享資源進行隔離、限制、統計等。只有能控制分配到容器的資源,才能避免當多個容器同時執行時的對系統資源的競爭。 4 | 5 | 控制組技術最早是由 Google 的程式員 2006 年起提出,Linux 核心自 2.6.24 開始支援。 6 | 7 | 控制組可以提供對容器的記憶體、CPU、磁碟 IO 等資源的限制和統計管理。 8 | -------------------------------------------------------------------------------- /underly/container_format.md: -------------------------------------------------------------------------------- 1 | ## 容器格式 2 | 3 | 最初,Docker 採用了 LXC 中的容器格式。自 1.20 版本開始,Docker 也開始支援新的 [libcontainer](https://github.com/docker/libcontainer) 格式,並作為預設選項。 4 | 5 | 對更多容器格式的支援,還在進一步的發展中。 -------------------------------------------------------------------------------- /underly/namespace.md: -------------------------------------------------------------------------------- 1 | ## 命名空間 2 | 命名空間是 Linux 核心一個強大的特性。每個容器都有自己單獨的命名空間,執行在其中的應用都像是在獨立的作業系統中執行一樣。命名空間保證了容器之間彼此互不影響。 3 | 4 | ### pid 命名空間 5 | 不同使用者的程式就是透過 pid 命名空間隔離開的,且不同命名空間中可以有相同 pid。所有的 LXC 程式在 Docker 中的父程式為 Docker 程式,每個 LXC 程式具有不同的命名空間。同時由於允許巢狀結構,因此可以很方便的實作巢狀結構的 Docker 容器。 6 | 7 | ### net 命名空間 8 | 有了 pid 命名空間,每個命名空間中的 pid 能夠相互隔離,但是網路連接埠還是共享 host 的連接埠。網路隔離是透過 net 命名空間實作的, 每個 net 命名空間有獨立的網路設備、 IP 位址、路由表、 /proc/net 目錄。這樣每個容器的網路就能隔離開來。Docker 預設採用 veth 的方式,將容器中的虛擬網卡同 host 上的一個 Docker 橋接器 docker0 連線在一起。 9 | 10 | ### ipc 命名空間 11 | 容器中程式互動還是採用了 Linux 常見的程式間互動方法(interprocess communication - IPC),包括訊號量、消息隊列和共享記憶體等。然而同 VM 不同的是,容器的程式間互動實際上還是 host 上具有相同 pid 命名空間中的程式間互動,因此需要在 IPC 資源申請時加入命名空間訊息,每個 IPC 資源有一個唯一的 32 位 id。 12 | 13 | ### mnt 命名空間 14 | 類似 chroot,將一個程式放到一個特定的目錄執行。mnt 命名空間允許不同命名空間的程式看到的檔案結構不同,這樣每個命名空間 中的程式所看到的檔案目錄就被隔離開了。同 chroot 不同,每個命名空間中的容器在 /proc/mounts 的訊息只包含所在命名空間的 mount point。 15 | 16 | ### uts 命名空間 17 | UTS(UNIX Time-sharing System)命名空間允許每個容器擁有獨立的 hostname 和 domain name ,使其在網路上可以被視作一個獨立的節點而非主機上的一個程式。 18 | 19 | ### user 命名空間 20 | 每個容器可以有不同的使用者和組 id ,也就是說可以在容器內用容器內部的使用者執行程式而非主機上的使用者。 21 | 22 | *註:關於 Linux 上的命名空間,[這篇文章](http://blog.scottlowe.org/2013/09/04/introducing-linux-network-namespaces/) 介紹的很好,另外 [Michael Crosby - Creating containers - Part 1](http://crosbymichael.com/creating-containers-part-1.html) 也非常推薦。。 23 | -------------------------------------------------------------------------------- /underly/network.md: -------------------------------------------------------------------------------- 1 | ## Docker 網路實作 2 | 3 | Docker 的網路實作其實就是利用了 Linux 上的網路命名空間和虛擬網路設備(特別是 veth pair)。建議先熟悉了解這兩部分的基本概念再閱讀本章。 4 | 5 | ### 基本原理 6 | 首先,要實作網路通訊介面,機器需要至少一個網路介面(物理介面或虛擬介面)來收發資料包;此外,如果不同子網之間要進行通訊,需要路由機制。 7 | 8 | Docker 中的網路介面預設都是虛擬的介面。虛擬介面的優勢之一是轉發效率較高。 9 | Linux 透過在核心中進行資料複製來實作虛擬介面之間的資料轉發,發送介面的發送緩存中的資料包被直接複製到接收介面的接收緩存中。對於本地系統和容器內系統看來就像是一個正常的乙太網卡,只是它不需要真正同外部網路設備通訊,速度要快很多。 10 | 11 | Docker 容器網路就利用了這項技術。它在本地主機和容器內分別建立一個虛擬介面,並讓它們彼此連通(這樣的一對介面叫做 `veth pair`)。 12 | 13 | ### 建立網路參數 14 | Docker 建立一個容器的時候,會執行以下操作: 15 | * 建立一對虛擬介面,分別放到本地主機和新容器中; 16 | * 本地主機一端橋接到預設的 docker0 或指定橋接器上,並具有一個唯一的名字,如 veth65f9; 17 | * 容器一端放到新容器中,並修改名字作為 eth0,這個介面只在容器的命名空間可見; 18 | * 從橋接器可用位址段中取得一個未使用位址分配給容器的 eth0,並設定預設路由到橋接網卡 veth65f9。 19 | 20 | 完成這些之後,容器就可以使用 eth0 虛擬網卡來連線其他容器和其他網路。 21 | 22 | 可以在 `docker run` 的時候透過 `--net` 參數來指定容器的網路設定,有4個可選值: 23 | * `--net=bridge` 這個是預設值,連線到預設的橋接器。 24 | * `--net=host` 告訴 Docker 不要將容器網路放到隔離的命名空間中,即不要容器化容器內的網路。此時容器使用本地主機的網路,它擁有完全的本地主機介面存取權限。容器程式可以跟主機其它 root 程式一樣可以打開低範圍的連接埠,可以存取本地網路服務比如 D-bus,還可以讓容器做一些影響整個主機系統的事情,比如重啟主機。因此使用這個選項的時候要非常小心。如果進一步的使用 `--privileged=true`,容器會被允許直接設定主機的網路堆棧。 25 | * `--net=container:NAME_or_ID` 讓 Docker 將新建容器的程式放到一個已存在容器的網路堆疊中,新容器程式有自己的檔案系統、程式列表和資源限制,但會和已存在的容器共享 IP 位址和連接埠等網路資源,兩者程式可以直接透過 `lo` 迴路介面通訊。 26 | * `--net=none` 讓 Docker 將新容器放到隔離的網路堆疊中,但是不進行網路設定。之後,使用者可以自己進行設定。 27 | 28 | ### 網路設定細節 29 | 使用者使用 `--net=none` 後,可以自行設定網路,讓容器達到跟平常一樣具有存取網路的權限。透過這個過程,可以了解 Docker 設定網路的細節。 30 | 31 | 首先,啟動一個 `/bin/bash` 容器,指定 `--net=none` 參數。 32 | ```bash 33 | $ sudo docker run -i -t --rm --net=none base /bin/bash 34 | root@63f36fc01b5f:/# 35 | ``` 36 | 在本地主機查找容器的程式 id,並為它建立網路命名空間。 37 | ```bash 38 | $ sudo docker inspect -f '{{.State.Pid}}' 63f36fc01b5f 39 | 2778 40 | $ pid=2778 41 | $ sudo mkdir -p /var/run/netns 42 | $ sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid 43 | ``` 44 | 檢查橋接網卡的 IP 和子網遮罩訊息。 45 | ```bash 46 | $ ip addr show docker0 47 | 21: docker0: ... 48 | inet 172.17.42.1/16 scope global docker0 49 | ... 50 | ``` 51 | 建立一對 “veth pair” 介面 A 和 B,綁定 A 到橋接器 `docker0`,並啟用它 52 | ```bash 53 | $ sudo ip link add A type veth peer name B 54 | $ sudo brctl addif docker0 A 55 | $ sudo ip link set A up 56 | ``` 57 | 將B放到容器的網路命名空間,命名為 eth0,啟動它並設定一個可用 IP(橋接網段)和預設網關。 58 | ```bash 59 | $ sudo ip link set B netns $pid 60 | $ sudo ip netns exec $pid ip link set dev B name eth0 61 | $ sudo ip netns exec $pid ip link set eth0 up 62 | $ sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0 63 | $ sudo ip netns exec $pid ip route add default via 172.17.42.1 64 | ``` 65 | 以上,就是 Docker 設定網路的具體過程。 66 | 67 | 當容器結束後,Docker 會清空容器,容器內的 eth0 會隨網路命名空間一起被清除,A 介面也被自動從 `docker0` 卸載。 68 | 69 | 此外,使用者可以使用 `ip netns exec` 命令來在指定網路命名空間中進行設定,從而設定容器內的網路。 70 | -------------------------------------------------------------------------------- /underly/ufs.md: -------------------------------------------------------------------------------- 1 | ## Union 檔案系統 2 | Union檔案系統([UnionFS](http://en.wikipedia.org/wiki/UnionFS))是一種分層、輕量級並且高效能的檔案系統,它支援對檔案系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬檔案系統下(unite several directories into a single virtual filesystem)。 3 | 4 | Union 檔案系統是 Docker 映像檔的基礎。映像檔可以透過分層來進行繼承,基於基礎映像檔(沒有父映像檔),可以制作各種具體的應用映像檔。 5 | 6 | 另外,不同 Docker 容器就可以共享一些基礎的檔案系統層,同時再加上自己獨有的改動層,大大提高了儲存的效率。 7 | 8 | Docker 中使用的 AUFS(AnotherUnionFS)就是一種 Union FS。 AUFS 支援為每一個成員目錄(類似 Git 的分支)設定唯讀(readonly)、讀寫(readwrite)和寫出(whiteout-able)權限,同時 AUFS 裡有一個類似分層的概念,對唯讀權限的分支可以邏輯上進行增量地修改(不影響唯讀部分的)。 9 | 10 | Docker 目前支援的 Union 檔案系統種類包括 AUFS 、 btrfs 、 vfs 和 DeviceMapper。 11 | --------------------------------------------------------------------------------