├── .DS_Store ├── 24-random-docker-tips-cn.md ├── 8-questions-need-ask-microservices-containers-docker-2015-cn.md ├── 9-docker-recipes-javaee-applications-techtip80-cn.md ├── CTutorial.md ├── C语言原理.md ├── How-are-OpenShift-OpenStack-Kubernetes-and-Docker-comparable-and-different-cn.md ├── Interestings_of_Quora.md ├── LICENSE ├── LKM3_2048.png ├── Linux-stuff.md ├── README.md ├── apache-mesos-vs-hadoop-lt.md ├── book └── TOC.md ├── books.png ├── cTutorial ├── cTutorial_AdvPointers.md ├── cTutorial_Array.md ├── cTutorial_BFiles.md ├── cTutorial_CLA.md ├── cTutorial_DDS.md ├── cTutorial_Functions.md ├── cTutorial_Libraries.md ├── cTutorial_Looping.md ├── cTutorial_Makefiles.md ├── cTutorial_OperatorP.md ├── cTutorial_Pointers.md ├── cTutorial_Strings.md ├── cTutorial_TextFiles.md └── cTutorial_cheatSheet.md ├── changlog-1.5-cn.md ├── commit └── README.md ├── comparing-monitoring-options-for-docker-deployments-cn.md ├── comparision go web frameworks.md ├── create-the-smallest-possible-docker-container-cn.md ├── demo └── Dockerfile ├── difference-between-docker-and-vgrant-cn.md ├── difference-between-docker-and-vs-cn.md ├── docker-indepth-volumes-cn.md ├── docker-machine-compose-swarm-cn.md ├── docker-really-solve-what-problem-cn.md ├── docker-security-tuning-cn.md ├── docker-tutorial-series-1-cn.md ├── docker-tutorial-series-2-cn.md ├── docker-tutorial-series-3-cn.md ├── docker-tutorial-series-4-cn.md ├── docker-tutorial-series-5-cn.md ├── docker-tutorial-series-6-cn.md ├── docker-tutorial-series-7-cn.md ├── docker-tutorial-series-8-cn.md ├── docker-tutorial-series-9-cn.md ├── docker_config.sh ├── docker_ecosys.png ├── docker_install.sh ├── docker_mysql.sh ├── dockercon-eu-introducing-docker-compose-cn.md ├── dockerfile-best-practices-1-cn.md ├── dockerfile-best-practices-2-cn.md ├── dockerfile └── README.md ├── exploring-local-docker-bridge-networks-cn.md ├── file-system-snapshots-make-build-scripts-easy-cn.md ├── friends-cn.md ├── helloworld-docker.md ├── how-dockerize-webapp-with-postgres-cn.md ├── images └── README.md ├── intro-to-docker-swarm-pre-cn.md ├── intro-to-docker-swarm-pt1-overview-cn.md ├── intro-to-docker-swarm-pt2-config-options-requirements-cn.md ├── intro-to-docker-swarm-pt3-example-architechture-cn.md ├── intro-to-docker-swarm-pt4-demo-cn.md ├── machine-swarm-compose-intergration-cn.md ├── middleware └── Linux_kernel_and_gaming_input-output_latency.svg.png ├── orchestration-toolkit-release-aims-prove-dockers-commitment-flexibility-community-ecosystem-cn.md ├── ps └── README.md ├── pull └── README.md ├── push └── README.md ├── question-cn.md ├── ref ├── c-array-pointer.gif ├── c-array.gif ├── c-compile.gif ├── c-dds1.gif ├── c-dds2.gif ├── c-dds3.gif ├── c-dds4.gif ├── c-dds5b.gif ├── c-dds6.gif ├── c-dds7.gif ├── c-dds8.gif ├── c-exec.gif ├── c-heap.gif ├── c-heap1.gif ├── c-heap2.gif ├── c-heap3.gif ├── c-if.gif ├── c-pointer-swap.gif ├── c-pointer1.gif ├── c-pointer2.gif ├── c-pointer3.gif ├── c-pointer4a.gif ├── c-pointer5.gif ├── c-pointer6.gif ├── c-pointer7.gif ├── c-string-corrected.gif └── c-while.gif ├── resource-management-in-docker-cn-review.md ├── rm └── README.md ├── rmi └── README.md ├── run └── README.md ├── safer-local-docker-networks-cn.md ├── start_docker.sh ├── the-docker-ecosystem-service-discovery-and-distributed-configuration-stores-cn-review.md ├── tmp.sh ├── understanding-volume-in-docker-cn.md ├── why-use-fig-for-docker-automation-cn.md └── why_docker_is_written_in_Go.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/.DS_Store -------------------------------------------------------------------------------- /24-random-docker-tips-cn.md: -------------------------------------------------------------------------------- 1 | docker的24个实用技巧 2 | 3 | 我们爱docker,并把它用于生产环境中。下面是一些提示和技巧,希望能对已经熟悉docker基础的每一位提供帮助。 4 | 5 | ### CLI(Command-line interface) 6 | 7 | 1. 规整 docker ps 输出 8 | docker ps管道上加个less -S来使输出的每一行不被换行 9 | ```docker ps -a | less -S 10 | ``` 11 | 12 | 2. 跟踪日志 13 | ```docker logs -f 14 | ``` 15 | 3. a single value from docker inspect 16 | docker inspect 在默认情况下有大量的JSON输出。你可以使用内置的模板命令如下: 17 | ```# is the last run container still running? 18 | docker inspect --format '{{.State.Running}}' $(docker ps -lq) 19 | ``` 20 | 21 | 4. docker exec instead of sshd or nsenter 22 | 这个命令众所周知,如果你遵循docker版本。 在1.3版本中引入了exec,让你在一个容器内运行一个新的进程。因此也就没有必要在容器中运行的sshd或主机上安装nsenter。 23 | ```docker exec -it /bin/bash 24 | ``` 25 | 26 | ### Dockerfiles 27 | 28 | 5. docker build supports git repos 29 | 你不仅可以构建镜像用本地的Dockerfiles,也可以简单地给docker一个git仓库的URL,docker自己会办妥余下的事。 30 | 31 | 6. no package lists 32 | 默认的镜像(如Ubuntu)不包括软件包列表来保持它们小容量,因此```apt-get update```几乎够用在基本的Dockerfile中。 33 | 34 | 7. watch out for package versions 35 | 小心包的安装以及那些缓存的命令。这意味着当误操作缓存或着安全更新的延迟,您可能会获取不同版本。 36 | 37 | 8. small base images 38 | 有一个真正的空的docker镜像。这就是所谓的scratch(译注:[scratch教程](http://dockerone.com/article/93))。所以,如果你愿意,你可以从头构建你的镜像FROM scratch。大多数时候,你最好从[busybox](https://registry.hub.docker.com/_/busybox/)开始,如果你想有一个非常小的基础镜像(只有2.5MB大小)。 39 | 40 | 9. FROM is latest by default 41 | 如果你没有在标签具体说明版本,则```FROM```关键字将只使用最新的镜像。小心这一点,如果你可以确定具体版本,请最好那样做。 42 | 43 | 10. shell or exec mode 44 | 在Dockerfile中有几个地方,您可以具体指定命令。(例如CMD,RUN)。docker支持两种方式。如果你只写命令,然后docker将把它封装在```sh -c```。你也可以把他们作为一个字符串数组(如 ```["LS", "-a"]```)。该阵列的符号在容器内是可用的将不再需要shell(因为它使用go的```exec```),这就是docker的首选语法。 45 | 46 | 11. ADD vs COPY 47 | 在构建容器时```ADD```和```COPY```都可以添加本地文件,但```ADD```有额外的魔法,比如增加远程文件以及```ungzipping```解包和```untaring```归档。如果你明白这些区别,只用使用```ADD```。 48 | 49 | 12. WORKDIR and ENV 50 | 每个命令都将创建一个新的临时镜像并在一个新的shell里运行,因此,如果你在Dockerfile使用```cd ```或```export =```将无法正常工作。使用```WORKDIR```来设置工作目录来接受多个命令,而使用```ENV```是设置环境变量。 51 | 52 | 13. CMD and ENTRYPOINT 53 | ```CMD```为默认的命令的当镜像被运行时执行。默认```ENTRYPOINT```是```/bin/sh -c```以及```CMD```作为参数传递进去。我们可以在Dockerfile重写```ENTRYPOINT```使我们的容器表现得像输入命令行参数的可执行程序(默认参数在我们Dockerfile的CMD里面)。 54 | ``` 55 | # in Dockerfile 56 | ENTRYPOINT /bin/ls 57 | CMD ["-a"] 58 | # we're overriding the command but entrypoint remains ls 59 | docker run training/ls -l 60 | ``` 61 | 14. ADD your code last 62 | ```ADD```会使缓存无效如果文件有改变。当在您的Dockerfile加入频繁变化的东西,而又如果不想使缓存作废。你可以先添加库和依赖,在最后添加你的代码。对于node.js apps 意味着你先加入的package.json,然后运行```npm install```,最后再加入你的代码。 63 | 64 | ### docker networking 65 | Docker有一个IPs内部池,它的存在是为了容器的IP地址。默认的这些是隐蔽的,可以经由桥接来访问。 66 | 67 | 15. looking up port mappings 68 | ```docker run```接受明确的端口映射作为参数,也可以指定```-P```自动映射所有端口。后者具有防止冲突并搜寻已分配的端口,命令如下: 69 | ```docker port 70 | # or 71 | docker inspect --format '{{.NetworkSettings.Ports}}' 72 | ``` 73 | 74 | 16. container IPs 75 | 每个容器有它的IP在一个私有的子网中(默认情况下是172.17.42.1/16)。该IP在重启后可以改变,但你也可以用以下命令查询: 76 | ```docker inspect --format '{{.NetworkSettings.IPAddress}}' 77 | ``` 78 | 79 | docker尝试查找冲突并如果需要将使用不同的子网络。 80 | 81 | 17. taking over the hosts network stack 82 | ```docker run --net=host```允许重新使用主机的网络协议栈。不能这样做。 83 | 84 | ### volumes 85 | 一种方法为目录或单个文件接近零开销(绑定挂载)来绕过```copy-on-write```(写入时复制)文件系统 。 86 | 87 | 18. volume contents are not saved on docker commit 88 | 数据卷内容不会被保存到docker的提交改动中。当镜像被构建时,同时写入您的数据卷没有多大意义 89 | 90 | 19. volumes are read-write by default 91 | 默认情况下,数据卷有读写权限 但有一个 ```:ro``` 标志(read-only只读权限) 92 | 93 | 20. volumes exists separately from containers 94 | 数据卷的退出是跟容器分开的,并且这是可能的直到至少有一个容器引用它们。数据卷可以在容器之间共享,使用 ```--volumes-from```。 95 | 96 | 21. mount your docker.sock 97 | 挂载你的```docker.sock```。你可以只挂在您的```docker.sock```来为容器内提供访问docker的API。然后,您可以在容器内运行的docker的命令。这样的容器,甚至可以消除自己。在容器内运行docker守护进程也是没必要的。 98 | 99 | ### security 100 | 101 | 22. docker runs as root... 102 | ...相应地对待它。docker API有充分的root访问权限,你可以映射/挂载数据卷,读,写。或者你可以使用```--net host```来接管主机的网络。不要向公众暴露docker API或使用TLS。 103 | 104 | 23. USER in Dockerfiles 105 | 默认情况下,docker以root身份运行一切,当然你也可以在Dockerfiles使用```USER```。有在docker没有用户命名空间来使容器看到用户在主机上,但只有uids,因此你需要在容器中添加用户。 106 | 107 | 24. use TLS on the docker API 108 | 直到1.3当他们加入了TLS, 在docker API才有了访问控制。他们使用相互验证:客户端和服务端都有密钥。把钥匙作为root密码。 109 | 110 | 自从1.3版本,Boot2docker默认具有TLS,也会为您产生密钥。 111 | 112 | 在其他情况下,生成密钥需要的OpenSSL 1.0.1, 然后docker守护程序需要与```--tls-verify```运行,并使用安全docker端口(2376)。 113 | 114 | 我们希望尽快得获得更多的细微的访问控制,而不是没有控制或者控制一切。 115 | 116 | 117 | [原文链接](http://csaba.palfi.me/random-docker-tips/) -------------------------------------------------------------------------------- /8-questions-need-ask-microservices-containers-docker-2015-cn.md: -------------------------------------------------------------------------------- 1 | ##羊年八问微服务、容器与Docker 2 | 3 | 【编者的话】作者从八个方面对当下热门的微服务、容器入手,提出一些问题与建议。读者可以通过此文理解这些技术在企业中的应用场景,其中一些问题值得读者深思熟虑。 4 | 5 | 坐在这里,想想过去的马拉松似的长假没有什么比熬过假期里得的轻微感冒更糟糕的事,并回顾2014年,我觉得那是IT届相当重要的一年。可以公平地说,在[Docker](https://www.docker.com/),更宽泛的来讲,[容器](http://www.wikiwand.com/en/Operating-system-level_virtualization)和[微服务](http://martinfowler.com/articles/microservices.html),我们正面临着自从虚拟机的到来后在如何交付和运行软件服务上的潜在巨大变化。 6 | 7 | 这种潜在的变化不仅带来很多的兴趣和热情,难免地也带来了很多的炒作和趋之若鹜。但在所有这些议论中,我们要牢记它真的还很年轻。不仅仅是技术,还有围绕着微服务、容器以及Docker的程序与操作:几乎这个范围内的一切东西要么还在等待被发明,要么就是需要大量的细微调整。 8 | 9 | 这有涉及容器的8个问题我认为每个组织都需要在做任何决定之前进行调查:存储,容错,交付模式,发布策略,所有权,补丁,支持和许可。 10 | 11 | 不要误会我的意思:我觉得微服务和[物联网](http://www.wikiwand.com/en/Internet_of_Things)将成为我们设计与交付IT服务方式的核心部分(在越来越多的分散的计算能力与更大数据量被拉回到中央位置之间有一种有趣的张力。因为我们无法处理它,否则它将是一篇不同博文的话题)。在[XebiaLabs roadmap](http://xebialabs.com/products/)上微服务扮演了重要的功能角色,我认为每一个组织必须至少调查他们,并且容器是一个非常有前途的候选实现技术。 12 | 13 | 尽管如此,我认为我们看到的从事雄心勃勃的“Let’s Dockerize everything”的计划的企业相当的勇敢。用一个更微妙的方式来看它:如果你已经认同并证明了容器所表现出显著的技术好处,特别是Docker-它可以用于应用程序的交付,那么你就会有这样一种任你自由支配的可以解决复杂的、尚未解决的问题的方案的技术资源,并且愿意去通过生产和重新实现附带任何1.x版的技术然后并采用,现在来说它也是有道理的。 14 | 15 | 然而你要是完全基于“containers will make everything better,”来容器化一切,并期待能结合现有的现成解决方案,让您的团队在一些最佳实践上训练,并迅速地有一个稳定、安全的运行平台并且在运行中,它将会给你带来很多的惊讶。 16 | 17 | 话虽如此,并给予我的微服务的信念,我认为企业不断研究容器(Docker、[Mesos](https://mesos.apache.org/)、[Rocket](https://coreos.com/blog/rocket/)或者任何不久我们将无疑会看到的其他替代品)如何可以为他们工作显得至关重要的。在最近的一些讨论的基础上,这里有八个问题我认为任何一个组织都需要考虑在2015年: 18 | 19 | ###1. 存储 20 | 也许现在最明显的技术问题是:如何处理持久、可写的存储容器?如何以及在何处存储数据?如何从容器中连接到它?如何复制与备份它?如何让数据随着容器迁移如果在不同的机器上启动一个容器以及如何迅速地做这一切? 21 | 22 | ###2. 容错 23 | 在一个微服务环境中,寻找其他终端服务的常见方法需要告知其去查询某种服务Registry,以及/或者为服务使用稳定的名称来解决这些实际的终端,例如DNS。在带来了一个新的虚拟机可能需要几分钟的环境里,采取服务注册或是DNS缓存来更新相对来说耗时比较少。用容器的话只需要几秒就可以,终端的重新绑定很快会成为一个瓶颈,尤其是如果有许多容器同时发生故障。 24 | 25 | 我最近看到了一个令人印象深刻的解决方案,它使用的是商品硬件,其能够从一个全机架故障中恢复-重新启动、重新挂载和重新绑定10,000个容器-不超过10秒。但是这一切都是定制的引擎和实验...在货架上你得不到这种东西。 26 | 27 | 你的容错要求有哪些?你与容器会如何定位这些? 28 | 29 | ###3. 交付模式 30 | 如果你要部署容器,开发商将要交付什么?他们是否要交付容器的描述符(Dockerfile或者其他类似的)?如果交付的话,你如何传送描述所引用的所有外部资源?或者开发商是否会提供“编译过”的镜象,使之避免了外部的依赖问题但是增加了可交付的尺寸并且使给接收者更难[检查](http://venturebeat.com/2014/12/16/whats-in-a-container-you-dont-know-and-thats-a-problem/)或[验证](https://titanous.com/posts/docker-insecurity)什么是真正要运行的。 31 | 32 | 或者你是否希望避免要求开发者学习另一种“交付格式”(来让开发人员学习Puppet、Chef或类似配置的“语言”的难度是我们遇到的一些组织试图向开发商介绍他们来作为交付方式的主要问题之一),并正在寻找某种方式从开发商交付的同一代码产品中“自动生成”容器描述或是镜像呢? 33 | 34 | ###4. 发布策略 35 | 你的服务是否有足够的独立性,使您能够在每一个提交或是设定的安排后都能部署每个服务到生产中?还是说你需要绑定多个服务在最新的候选版本已经测试过的发布队列里。如果成功的话,基本上每天发布还是每周发布?如果是后者,那么你会使用多少个发型版本?企业组织一个、每个团队一个或者每个业务部门一个呢? 36 | 37 | 随着发布队列里候选版本的的数量上升,测试失败的机会通常也会增加。你是否会中止发布队列,如果有测试失败了,因为这一个问题从而可能会阻止199个“状态良好”的服务上线吗?或者尝试移除那个“烂苹果”,并重新测试其余的候选版本是否有意义呢? 38 | 39 | 对于实际生产部署来说,你是否会创建一个完整的“镜子”环境(可能是一个您用于在发布队列里的最广泛的测试?),然后逐渐疏导交通呢?还是你只是沿着现有的服务来部署新的服务?如果是这样,你怎么处理这逐渐的血流不止的问题,请牢记新服务可能不会直接被用户使用?还是你依靠快速来回切换如果有问题的话,来代替完全切换到新的服务呢?如果是这样,你是否会一次切换一个服务(当然是相互依存服务的集群)或者是一次切换所有的服务? 40 | 41 | ###5. 所有权 42 | 关于容器的交付模式的决定部分,也是由谁拥有/负责这一块的交付成果来决定的。业务操作是否简单地提供容器运行环境以及在容器内的发生的任何问题的开发商所要负的责任?业务操作是否还负责从每个开发商提供的容器继承的“基本的描述符”或“基础镜像”?如何确定一个问题是否由镜像的“基础“部分造成的,还是说开发商添加的部分,特别是那些允许开发商修改或者覆盖基础镜像的技术部分所造成的? 43 | 44 | 还有一个与有关安全方面的问题:你是否能充分隔离/沙箱操作你的容器,得以防止其[影响其它系统](http://www.slideshare.net/jpetazzo/docker-linux-containers-lxc-and-security)?或者你只是相信,由开发团队提供的描述符/镜像不会做“坏事” - 无意或是故意的呢? 45 | 46 | 如果你想让开发团队来责任提供完整的容器描述并且您也信任他们会做正确的事,而且他们的容器配置文件系统的条例来说,你问他们是否会愿意支持生产线上的系统? 47 | 48 | ###6. 补丁 49 | 您需要有更改运行系统的能力,如应用安全补丁或其他重要变化?如果你正在考虑采取[一成不变的基础设施](http://highops.com/insights/immutable-infrastructure-hangout-recordings/)建设的原则,你能多快更新所有受影响的容器定义或镜像,并且发布新版本?如果补丁可以应用到开发团队使用的基本描述符或镜像中,你是否能在他们的构建过程中“覆盖”基础镜像,以及是否需要开发团队来改变他们的源代码或者构建定义。 50 | 51 | 如果你正在考虑修改运行的容器,请确保容器技术支持这一点,并且还提供了一个审计线索。您将如何确保对运行容器所做的任何更改可以返回到容器定义? 52 | 53 | ###7. 支持 54 | 最近我听说这个故事:一个公司为了一个支持合同在一个操作系统运行Docker。但在Docker中运行容器是使用不支持的操作系统的基础镜像。突然,“机器”(即容器)都不能有效托管主机支持的应用程序。当他们面临这些问题时,他们的支持合同却帮不了他们。 55 | 56 | 你是否有必需的支持解决方案无论你的容器运行在什么地方? 57 | 58 | ###8. 许可 59 | 如果您在每个系统中使用的是被许可的服务组件,你是否检查了供应商的有关容器的许可政策?供应商是否考虑每个容器里是一个独立的系统,还是说他们只是简单地认为托管容器框架是系统的进程而已?换句话说,如果你现在在一台机器上运行10个许可程序的实例,你的成本是否会保持不变如果你在容器内运行它们,还是说在一个完整的订单或幅度内它将会增加? 60 | 61 | 这么多的问题...但是答案是什么?不幸的是,基于这么多谈话,我觉得在这一点上坦率的回答是:没有人知道。在一些地区有很多有趣的想法,但还没有明显的赢家。其他的问题仅仅现在才显示在讨论的雷达中,而且在许多情况下,“正确”答案很大程度上取决于企业的具体情况。 62 | 63 | 幸运的是,鉴于正在发生的这些事,我想在明年同一时间我们会了解的多得多。这是一个令人兴奋的2015年! 64 | 65 | 66 | **原文链接:[8 Questions You Need to Ask About Microservices, Containers & Docker in 2015](http://blog.xebialabs.com/2014/12/31/8-questions-need-ask-microservices-containers-docker-2015/) (翻译:[田浩浩](https://github.com/llitfkitfk))** 67 | 68 | =========================== 69 | **译者介绍** 70 | **田浩浩**,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 -------------------------------------------------------------------------------- /9-docker-recipes-javaee-applications-techtip80-cn.md: -------------------------------------------------------------------------------- 1 | 在Docker中运行Java EE程序的九个诀窍 2 | 3 | 你想要使用使用Docker来运行Java EE应用程序吗? 4 | 5 | 一个典型的Java EE应用程序包括应用服务器,如WildFly和数据库,如MySQL的。此外,你可能有一个独立的前端层,说阿帕奇,负载均衡一些应用服务器。的高速缓存层,例如Infinispan的,也可以使用,以提高整体的应用性能。消息传送系统,如ActiveMQ的,可用于处理队列。无论是缓存和消息组件可以设置为一个集群进一步扩展性。 6 | 7 | 此技术提示会显示一些简单的泊坞窗的食谱来配置你的容器使用应用服务器和数据库。随后博客将覆盖更高级的食谱,其中将包括前端,缓存,消息和聚类。 8 | 9 | 让我们开始吧! 10 | 11 | 泊坞窗配方#1:使用泊坞窗机设置泊坞窗 12 | 13 | 如果泊坞窗尚未设置你的机器上,那么作为第一步,你需要设置它。如果你是一个最新版本的Linux,那么你已经有了泊坞窗。或任选可被安装为: 14 | 15 | 命令和apt-get安装docker.io 16 | 在Mac和Windows,这意味着安装boot2docker这是一个Tinycore Linux的虚拟机,并​​配备了泊坞窗主机。然后,你需要配置SSH密钥和证书。 17 | 18 | 幸运的是,这是非常使用泊坞窗机简化。这需要你从零到泊坞窗的主机用一个命令就。该主机可以是你的笔记本电脑,在云中,或在您的数据中心。它创建的服务器,他们安装泊坞窗,然后配置泊坞窗客户与他们交谈。 19 | 20 | 这个配方进行了详细解释泊坞窗机到设置泊坞窗主机。 21 | 22 | 泊坞窗配方#2:应用服务器+内存数据库 23 | 24 | 其中的Java EE 7的很酷的功能是默认的数据库资源。这可以让你不用担心在应用程序服务器特定之前,你的应用程序可以访问创建一个JDBC资源。任何Java EE 7兼容的应用程序服务器将地图默认的JDBC资源名称(爪哇:比较/ DefaultDataSource)在捆绑的数据库服务器,应用服务器特定的资源。 25 | 26 | 例如,WildFly捆绑了H2内存数据库。该数据库已准备好,一旦被用作WildFly准备好接受你的请求。这简化了您的开发工作,并允许你做一个快速原型。默认JDBC资源被映射到 27 | java的:JBoss的/数据源/ ExampleDS,然后映射到JDBC的JDBC URL:H2:mem的:测试; DB_CLOSE_DELAY = -1; DB_CLOSE_ON_EXIT = FALSE。 28 | 29 | 在这种情况下,数据库服务器是应用程序服务器内部运行其他应用程序。 30 | 31 | 泊坞窗配方为Java EE应用程序#1 32 | 33 | 下面是运行的Java EE 7的应用程序中WildFly命令: 34 | 35 | 泊坞窗运行 - 它-p 8080:8080 arungupta / javaee7-HOL 36 | 如果你想运行使用WildFly和H2内存数据库一个​​典型的Java EE 7的应用程序,那么这个泊坞窗配方进行了详细动手实验室的WildFly和码头工人解释的Java EE 7。 37 | 38 | 泊坞窗配方#3:两个集装箱在相同主机使用链接 39 | 40 | 以前的配方让你起步很快,但成为一个瓶颈一旦数据库是只在内存中。这意味着,你的架构和数据所做的任何更改应用服务器关闭后丢失。在这种情况下,需要使用驻留在应用服务器外部的数据库服务器。例如,MySQL作为数据库服务器和WildFly作为应用服务器。 41 | 42 | 让事情变得简单,无论是数据库服务器和应用服务器可以在同一台主机上运行。 43 | 44 | 泊坞窗配方为Java EE应用程序#2 45 | 46 | 泊坞窗集装箱链接用来将两个容器相连。创建两个容器之间的联系产生源容器和目标容器和安全地传输有关源容器的信息到目标容器之间的管道。在我们的例子中,目标容器(WildFly)可以看到有关源容器(MySQL的)信息。了解这里最重要的部分是,需要加以公开由源容器暴露没有这个信息,并且只提供给目标容器。 47 | 48 | 下面是启动MySQL和WildFly容器和连接它们的命令: 49 | 50 | 泊坞窗运行--name MySQLdb的-e MYSQL_USER =的mysql -e MYSQL_PASSWORD =的mysql -e MYSQL_DATABASE =采样-e MYSQL_ROOT_PASSWORD =绝密-d mysql的 51 | 泊坞窗运行--name mywildfly --link MySQLdb的:DB -p 8080:8080 -d arungupta / wildfly-的mysql-javaee7 52 | WildFly和MySQL在两个码头工人的容器相连解释了如何设置这个食谱。 53 | 54 | 泊坞窗配方#4:两个集装箱在相同主机使用图 55 | 56 | 以前的配方要求你在一个特定的顺序运行的容器。运行多容器应用程序可以迅速成为具有挑战性的,如果您的应用程序的每一层是坐在一个容器。图(不赞成使用泊坞窗撰写的)是一个泊坞窗编排工具是: 57 | 58 | 在一个配置文件中定义多个容器 59 | 通过创建它们之间的联系建立两个容器之间的依赖关系 60 | 以正确的顺序启动容器 61 | 泊坞窗配方为Java EE应用程序#3 62 | 63 | 的入口点图是一个配置文件,如下所示: 64 | 65 | MySQLdb的: 66 |   图片:mysql的:最新 67 |   环境: 68 |     MYSQL_DATABASE:样品 69 |     MYSQL_USER:MySQL的 70 |     MYSQL_PASSWORD:MySQL的 71 |     MYSQL_ROOT_PASSWORD:绝密 72 | mywildfly: 73 |   图片:arungupta / wildfly-的mysql-javaee7 74 |   链接: 75 |      - MySQLdb的:DB 76 |   端口: 77 |      - 8080:8080 78 | 和所有的容器可以开始为: 79 | 80 | 无花果了-d 81 | 利用图泊坞窗编排解释了这个配方的细节。 82 | 83 | 图只接收更新。它的代码库作为基础泊坞窗撰写。这将在接下来的配方进行说明。 84 | 85 | 泊坞窗配方#5:两个集装箱在相同主机使用撰写 86 | 87 | 泊坞窗撰写是用于定义和使用泊坞窗运行复杂应用程序的工具。与撰写,您可以定义在一个文件中的多容器应用程序,然后旋转你的应用中,确实需要做才能得到它运行一切一条命令。 88 | 89 | 应用程序配置文件是相同的格式由图该容器可以开始为: 90 | 91 | 泊坞窗,撰写了-d 92 | 这个配方进行了详细解释泊坞窗撰写编排容器。 93 | 94 | 泊坞窗配方#6:两个集装箱在不同的主机使用的IP地址 95 | 96 | 在先前的配方,将两个容器的同一主机上运行。这两个可以使用泊坞窗链接很容易沟通。但简单的容器联不允许跨主机通信。 97 | 98 | 在同一台主机上运行的容器意味着你不能扩展每个层,数据库或应用程序服务器,独立。这是你需要一个单独的主机上运行的每个容器。 99 | 100 | 泊坞窗配方为Java EE应用程序#4 101 | 102 | MySQL的容器可以作为开始: 103 | 104 | 泊坞窗运行--name MySQLdb的-e MYSQL_USER =的mysql -e MYSQL_PASSWORD =的mysql -e MYSQL_DATABASE =采样-e MYSQL_ROOT_PASSWORD =绝密-p 5306:3306 -d mysql的 105 | JDBC资源可以被创建为: 106 | 107 | 数据源添加--name = mysqlDS --driver名= mysql的--jndi名= java的:JBoss的/数据源/ ExampleMySQLDS --connection-url=jdbc:mysql://$MYSQL_HOST:$MYSQL_PORT/sample?useUnicode=true&characterEncoding=UTF-8 --user名= mysql的--password = mysql的--use-CCM =假--max池大小= 25 --blocking超时等待,米利斯= 5000 --enabled =真 108 | 和WildFly容器可以为启动: 109 | 110 | 泊坞窗运行--name mywildfly -e MYSQL_HOST = -e MYSQL_PORT = 5306 -p 8080:8080 -d arungupta / wildfly-的mysql-javaee7 111 | 完整的细节这个配方在多个主机泊坞窗容器联解释。 112 | 113 | 泊坞窗配方#7:两个容器上使用泊坞窗群不同的主机 114 | 115 | 泊坞窗机 116 | 117 | 泊坞窗群是本地集群的码头工人。原来泊坞窗主机池成一个单一的,虚拟主机。它拿起向上,其中泊坞窗机通过优化主机资源利用率​​,并提供故障转移服务留下了。具体来说,泊坞窗群允许用户创建运行泊坞窗守护进程的主机资源池,然后安排泊坞窗容器之上运行,自动管理工作量安置和维护群集状态。 118 | 119 | 关于这个配方的更多细节即将在随后的博客。 120 | 121 | 泊坞窗配方#8:从Eclipse中部署Java EE应用程序 122 | 123 | 最后一个配方将处理如何将现有的应用程序部署到泊坞窗容器。 124 | 125 | 比方说,你正在使用JBoss的工具为你的开发环境和WildFly为您的应用程序服务器。 126 | 127 | Eclipse的标志JBoss的工具徽标 128 | 129 | 有一对夫妇的方法,使这些应用程序可以部署: 130 | 131 | 使用泊坞窗卷+本地部署:在这里,你的本地计算机上的某个目录安装为泊坞窗卷。 WildFly泊坞窗容器开始通过映射该目录的部署目录为: 132 | 泊坞窗运行 - 它-p 8080:8080 -v /用户/ arungupta的/ tmp /部署中:/ opt / JBoss的/ wildfly /独立/部署/:RW的JBoss / wildfly 133 | 配置JBoss工具WAR文件部署到该目录。 134 | 135 | 使用WildFly管理API +远程部署:启动WildFly泊坞窗容器,还揭露管理端口9990为: 136 | 泊坞窗运行 - 它-p 8080:8080 -p 9990:9990 arungupta / wildfly管理 137 | 配置JBoss的工具来使用远程服务器WildFly和部署使用管理API。 138 | 139 | 这个配方进行了详细的部署,从Eclipse的解释WildFly和泊坞窗。 140 | 141 | 泊坞窗配方#9:使用的Arquillian立方测试的Java EE应用程序 142 | 143 | 的Arquillian立方允许您控制泊坞窗图像的生命周期作为测试生命周期的一部分,无论是自动还是手动。多维数据集使用泊坞窗REST API交谈的容器。它使用了远程适配器的API,例如WildFly在这种情况下,进行通话的应用程序服务器。泊坞窗的配置被指定为Maven的万无一失,作为插件的一部分: 144 | 145 | <配置​​> 146 |      147 |      wildfly-泊坞窗 148 |     admin 149 |     Admin#70365 150 |      1.15 151 |      http://127.0.0.1:2375 152 |      153 |         wildfly-泊坞窗: 154 |             图片:arungupta / javaee7样本,wildfly 155 |             exposedPorts:[8080 / TCP,9990 / TCP] 156 |             等待: 157 |                 策略:投票 158 |                 sleepPollingTime:50000 159 |                 迭代:5 160 |             portBindings:[8080 / TCP,9990 / TCP] 161 |      162 |      163 | 164 | 可使用的Arquillian立方的码头工人运行的Java EE测试这个食谱完整细节。 165 | 166 | 你使用的是什么其他的配方使用泊坞窗来部署Java EE应用程序? 167 | 168 | 享受! -------------------------------------------------------------------------------- /C语言原理.md: -------------------------------------------------------------------------------- 1 | 2 | [跳转到 C Tutorial](https://github.com/llitfkitfk/docker-tutorial-cn/blob/master/CTutorial.md) -------------------------------------------------------------------------------- /How-are-OpenShift-OpenStack-Kubernetes-and-Docker-comparable-and-different-cn.md: -------------------------------------------------------------------------------- 1 | OpenShift、OpenStack、Kubernetes以及Docker的如何区分以及他们如何不同 2 | 3 | 4 | 简单来说,可以看到Docker(和一般的容器)类似搜身后的VMs,Openshift类似于你自己的Heroku,以及OpenStack则是类似于你自己的AWS。 5 | 6 | Docker容器使用linux内核功能让你在独立的网络/内存/进程/文件系统中运行你的应用程序,并增加了使用的unionfs的,所以你可以有一个“父”只写磁盘,一个孩子写的图像文件系统,在那里你有,如果你修改父文件写入时复制,并且能够让你有几个孩子共享相同的父(所以如果你有使用Ubuntu的基本安装,你只有一次几个容器,即使缓存上存储器一次)。它有一个灵活的API和命令行工具,用于创建容器,做一些基本的管理,把它们放在中央或你自己的资料库和更多。也让容器关联容器(想想之一DB,另一个用于Web服务器,一个不那么平凡的Web应用程序)链接更容易给对方。以及管理他们给自己的名字,以一堆相互关联的容器(齿轮,墨盒,kubelets等)的系统。和容器,因为他们本身运行作为一个普通的Linux内核下的应用,甚至可以在Linux已经内运行虚拟机运行。 7 | 8 | 和API使用几个项目,如CoreOS(觉得一个Linux发行意味着运行容器超过应用程序,有一些包括组件来帮助管理/成簇分布的容器),谷歌的Kubernetes(也意味着在集群上运行的容器,相关联的几个人在应该一起运行)组,或图(你也可以定义容器团体和它们之间的关系)。 9 | 10 | OpenShift已经存在的时候Docker曝光后,我认为这是基于LXC当时的情况。我看到在演示文稿的工作流程是开发商commiting到存储库,并得到发表在一个网站,或者至少让功能都要经过测试/分期/生产阶段的所有协调Openshift。使用Docker优化了很多的工作流程,以及最新的迭代也使用Kubernetes来协调他们。你奉献了一堆机器(如裸机硬件或虚拟机)上运行它,它管理的工作流程,在需要时提供容器。其不是基于多克尔,Dokku,弗林或DEIS唯一的PaaS是他人的几个例子。 11 | 12 | OpenStack的云的基础设施即服务水平,让你建立的东西了AWS的规模,为您提供一种方式来获得的虚拟机,所以你可以单独运行的虚拟机(运行Linux或其它操作系统),或用自己的网络中部署集群/存储/等。但也有司机部署Docker容器,而不是完整的虚拟机,以获得更多的服务密度虚拟化/裸机机。 13 | 14 | 这3个可在不同​​的抽象级别,并且可以使用它们本身,但是每一个都可以使用他人得到改善。 15 | -------------------------------------------------------------------------------- /Interestings_of_Quora.md: -------------------------------------------------------------------------------- 1 | ##Why do American people not care about the Japanese Prime Minister's visit to the Yasukuni Shrine? 2 | 3 | ####Possible explanations (each of which may or may not be a caricature of reality): 4 | ####可能的解释(它们可能是现实的夸张): 5 | * Americans generally respect soldiers very highly and don't think much of war crimes (especially their own). 6 | * 普遍地,美国人非常尊重士兵,不会想太多有关战争的罪行(特别是它们自己的) 7 | * Christianity doesn't do ancestor-worship, so Americans don't really understand the significance of war criminals being "enshrined" at Yasukuni. 8 | * 基督教没有祖先崇拜,因此美国人并不真正了解的战犯被“供奉”在靖国神社的意义。 9 | * Chinese people only care so strongly about this issue because their media keeps directing attention to it in and away from domestic problems. 10 | * 中国人这么强烈地关心这个问题,因为他们的媒体一直将注意力集中到它,并且很少提及国内问题。 11 | 12 | * Americans generally don't know or care much what happens in other countries. 13 | *美国人一般不知道也不关心其他国家发生的事情。 14 | 15 | 16 | ##What can I do while I'm earning a CS degree to develop real world programming skills? 17 | 18 | 1. **Competitive Coding: Infinite youth :D** 19 | One of the top secrets to staying young forever :D 20 | Practice on codechef, topcoder etc. Read other top coders code. Sometimes it'll just make you wonder, how beautifully "Out of the box" people can think! 21 | 22 | 23 | 2. **Moonlight Project:** 24 | Work on a secret project with a friend or two. Have big goals and plans for it. Keep working on it whenever your bored. Who knows this might be "THE NEXT BIG THING" and if not you have your lessons to learn from for the next big thing that happens. 25 | 26 | 27 | 3. **Clean coding:** 28 | Most Ignored feature during student life, hits you hard when you join workplace if you write dirty un-organized code. 29 | Something I personally faced when I started my job! 30 | Read books like: 31 | The Pragmatic Programmer: From Journeyman to Master. 32 | Clean Coding. 33 | 34 | 35 | 4. **Develop Design Skills:** 36 | Design is a very important part of coding. A well designed code can save you 10 fold the time and energy to debug later. So read up books like Design Patterns by Head First etc, to start thinking well in design direction. 37 | 38 | 39 | 5. **Use control Version System:** 40 | Torvald's didn't build it, for no reason :D 41 | Use git for your projects, that way you will also be learning how to use git as well as maintain all of your code at one place. 42 | 43 | 44 | 6. **Think of Test Cases for your code:** 45 | Think of boundary cases, think of various inputs that could be issued to your code. This way, you ll develop the habit of developing products that handles a lot of test cases. 46 | 47 | 48 | 7. **Find the editor to pledge for:** 49 | This is the time to explore the geeky side of editors like vi,emacs etc. They have a lot of tweaks and tricks that make it very fast to use for coding. This will take you long as a coder, and during school days it s so much fun to explore these options. 50 | 51 | 8. **Just Automate it : Be a Boss of Scripting** 52 | Don't be a robot, just create one! 53 | Learn bash scripting, automate all the things you manually do, for instance once you switch on your laptop, 54 | (i) Open Browser 55 | (ii) Switch on VLC 56 | (iii) Open terminal window etc etc 57 | Automate the entire process with a script. Learn how to run cron jobs. 58 | 59 | 60 | 9. **UPDATE mylife SET energy = infinity WHERE User = "Me" ;** 61 | Take part on whatever forums interest you, like Stack Overflow, Quora etc. Further, attend meet ups in the fields your interested in. Super interesting ted talks are available watch them. This will all keep you motivated and going. It also assures your heading in the right direction. Take part in overnight hackathons. Nightout hackathons are real fun! You'll explore a part of you that can code anything overnight :D Confidence booster. 62 | 63 | 64 | 10. **sudo apt-get update me** 65 | Your a budding engineer, stay updated with latest tech news so that you know where your interests spur and which direction to traverse. This way your brain will also trigger new ideas, who knows you might be the next kid working in the garage :D 66 | 67 | 68 | 11. **Open Source:** 69 | Contribute to open source projects, that way you ll learn how big projects work, how good coders contribute. 70 | 71 | 12. **Dig Deeper dont settle at concepts:** 72 | Just like goldminer game, keep mining, never settle at one level. 73 | When you learn new concepts in school, look up the applications of it. You'll be amazed to see where and all its used. For example how Spanning tree protocol is used in network switches to avoid loops, totally impressed me when i was studying spanning trees. Try and find out what concepts drive your favorite app. 74 | 75 | 13. **The beauty of Symmetry:** 76 | Practice symmetry in coding from now! It'll be ingrained into the atoms that make you, thereby turning your future code to be potentially bug free. By symetry I mean, when you write malloc write the free statement immediately after a few lines. Same goes for file handlers etc. You can save yourself from a lot of leaks that could come up. 77 | 78 | 14. **Additional Development Tools:** 79 | Learn valgrind, calgrind, make, gprof utilities which makes life so much easier. Learn Bash commands. They come in handy when you are in real need. 80 | 81 | P.S. Remember this is the time to explore! Explore as much as you can. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 田浩 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /LKM3_2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/LKM3_2048.png -------------------------------------------------------------------------------- /Linux-stuff.md: -------------------------------------------------------------------------------- 1 | ##namespaces & cgroups 2 | 3 | * 实现把多个进程限定在一个范围内,让人觉得她们是一个容器 4 | * 如果把流水线比作进程的话,namespace就相当于是工作间 5 | * cgroups限制了进程资源使用的控制,而namespace提供了资源的控制权限 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker-tutorial 2 | ================== 3 | 4 | Docker 教程 --- Linux-relase: centos 6.5 - inspired by [Andrew T. Baker](https://github.com/atbaker/docker-tutorial) 5 | 6 | 7 | Docker 推荐文章 8 | --------------- 9 | * [Docker入门实战@DockerOne](http://yuedu.baidu.com/ebook/d817967416fc700abb68fca1) 10 | * [Docker教程文章合集](http://dockerone.com/topic/Docker%20Tutorial) 11 | * [Docker源码分析(一):Docker架构](http://www.infoq.com/cn/articles/docker-source-code-analysis-part1) 12 | * [Running GUI apps with Docker](http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/) 13 | * [深入浅出Docker](http://www.infoq.com/cn/articles/docker-core-technology-preview) 14 | * [Docker Image瘦身](http://www.cnblogs.com/e2tox/p/4027756.html) 15 | 16 | Docker 安装 17 | ----------- 18 | 19 | ``` 20 | ./docker_install.sh 21 | ``` 22 | 23 | 24 | 25 | Docker 配置 26 | ----------- 27 | 28 | ``` 29 | // 配置 docker image路径到/mnt/docker 默认路径是/var/lib/docker 用户可编辑选择自己的docker image的储存路径 30 | ./docker_config.sh 31 | ``` 32 | 33 | 34 | Docker 开启 35 | ----------- 36 | 37 | ``` 38 | // docker 开启启动 已经 docker 服务开启 39 | ./start_docker.sh 40 | ``` 41 | 42 | Docker 命令 43 | ------------ 44 | * [run](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/run) ```- docker images 运行``` 45 | * [images](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/images) ```- docker images 查看``` 46 | * [ps](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/ps) ```- docker containers 查看``` 47 | * [rm](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/rm) ```- docker containers 删除``` 48 | * [rmi](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/rmi) ```- docker images 删除``` 49 | * [pull](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/pull) ```- dockerhub images 拉取``` 50 | * [push](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/push) ```- dockerhub images 更新``` 51 | * [commit](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/commit) ```- dockerhub images 提交``` 52 | 53 | * [exec ](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/exec) ```- docker containers 运行时修改``` 54 | 55 | Dockerfile 56 | ---------- 57 | * [Dockerfile](https://github.com/llitfkitfk/docker-tutorial-cn/tree/master/dockerfile) ```- dockerhub images 提交``` 58 | 59 | ``` 60 | FROM scratch 61 | ADD 62 | WORKDIR 63 | VOLUME 64 | EXPOSE 65 | ENTRYPOINT 66 | CMD 67 | ``` 68 | 69 | Docker 生态圈 70 | ------------- 71 | * [思维导图](https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/master/docker_ecosys.png) 72 | 73 | * [dockone.io](https://dockone.io/) 74 | 75 | 76 | C 语言原理 77 | ----------------- 78 | 79 | * [C Tutorial](https://github.com/llitfkitfk/docker-tutorial-cn/blob/master/C%E8%AF%AD%E8%A8%80%E5%8E%9F%E7%90%86.md) 80 | 81 | 82 | Developed By 83 | ------------ 84 | **田浩**,目前在珠海从事Docker开发及运维工作。 85 | 86 | * **贡献社区** - [DockerOne](http://dockone.io/people/llitfkitfk) 87 | 88 | * **Email** - 89 | 90 | * **微信** - [281196448]() 91 | 92 | 93 | Liscence 94 | ======== 95 | 96 | ``` 97 | The MIT License (MIT) 98 | 99 | Copyright (c) 2014 Tian Hao 100 | 101 | Permission is hereby granted, free of charge, to any person obtaining a copy 102 | of this software and associated documentation files (the "Software"), to deal 103 | in the Software without restriction, including without limitation the rights 104 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 105 | copies of the Software, and to permit persons to whom the Software is 106 | furnished to do so, subject to the following conditions: 107 | 108 | The above copyright notice and this permission notice shall be included in all 109 | copies or substantial portions of the Software. 110 | ``` 111 | -------------------------------------------------------------------------------- /book/TOC.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | # 第 1 章 Docker简介 4 | 5 | --- 6 | ## 1.1 什么是Docker 7 | #### 1.1.1 Docker的概念 8 | #### 1.1.2 开发,交付以及运行任何应用在任何地方 9 | 10 | --- 11 | ## 1.2 为什么使用docker 12 | #### 1.2.1 更快的交付应用程序 13 | #### 1.2.2 更便捷的部署与扩展 14 | #### 1.2.3 更高的密度和运行工作的负载 15 | 16 | --- 17 | ## 1.3 Docker架构 18 | #### 1.3.1 Docker后台进程 19 | #### 1.3.2 Docker客户端 20 | #### 1.3.3 Docker三大组件 21 | 22 | --- 23 | ## 1.4 Docker工作原理 24 | #### 1.4.1 Docker Image 原理 25 | #### 1.4.2 Docker registry 原理 26 | #### 1.4.3 Docker container 原理 27 | 28 | --- 29 | ## 1.5 Docker底层技术 30 | #### 1.5.1 Namespaces 31 | #### 1.5.2 Control groups 32 | #### 1.5.3 Union file systems 33 | #### 1.5.4 Container format 34 | 35 | --- 36 | ## 1.6 Docker案例 37 | #### 1.6.1 分布式应用 38 | #### 1.6.2 持续集成 39 | #### 1.6.3 持续交付 40 | #### 1.6.4 平台即服务 41 | #### 1.6.5 应用快捷部署 42 | 43 | --- 44 | # 第 2 章 Docker安装 45 | 46 | --- 47 | ## 2.1 centOS 6.5 48 | #### 2.1.1 安装 49 | #### 2.1.2 升级 50 | #### 2.1.3 运行 Docker 51 | #### 2.1.4 Container 端口重定向 52 | 53 | --- 54 | ## 2.2 ubuntu 14.04(LTS) 55 | #### 2.2.1 安装 56 | #### 2.2.2 升级 57 | #### 2.2.3 运行 Docker 58 | #### 2.2.4 Container 端口重定向 59 | 60 | --- 61 | ## 2.3 Mac OS X 62 | #### 2.3.1 安装 63 | #### 2.3.2 升级 64 | #### 2.3.3 运行 Docker 65 | #### 2.3.4 Container 端口重定向 66 | 67 | 68 | --- 69 | # 第 3 章 用户指南 70 | 71 | --- 72 | ## 3.1 "Hello World" 73 | #### 3.1.1 "hello world"入门 74 | #### 3.1.2 与Container的交互 75 | #### 3.1.3 "hello world"监控 76 | 77 | --- 78 | ## 3.2 使用 Containers 79 | #### 3.2.1 命令的使用 80 | #### 3.2.2 运行web应用 81 | #### 3.2.3 查看web应用 82 | #### 3.2.4 查看web应用进程 83 | #### 3.2.5 停止web应用 84 | #### 3.2.6 重启web应用 85 | #### 3.2.7 删除web应用 86 | 87 | --- 88 | ## 3.3 使用 Docker images 89 | #### 3.3.1 查看images清单 90 | #### 3.3.2 新建image 91 | #### 3.3.3 查找images 92 | #### 3.3.4 拉取image 93 | #### 3.3.5 新建个人的image 94 | #### 3.3.6 推送image到Docker Hub 95 | #### 3.3.7 删除image 96 | 97 | --- 98 | ## 3.4 Containers的链接 99 | #### 3.4.1 网络端口映射刷新 100 | #### 3.4.2 Docker Container链接 101 | #### 3.4.3 Container命名 102 | #### 3.4.4 Container链接 103 | 104 | --- 105 | ## 3.5 Containers的数据处理 106 | #### 3.5.1 数据卷 107 | #### 3.5.2 新建并挂载数据 108 | #### 3.5.3 备份,恢复与迁移数据 109 | 110 | --- 111 | ## 3.6 使用 Docker Hub 112 | #### 3.6.1 命令 113 | #### 3.6.2 搜索images 114 | #### 3.6.3 推送仓库到Docker Hub 115 | #### 3.6.4 功能 116 | 117 | --- 118 | # 第 4 章 命令详解 119 | 120 | --- 121 | ## 4.01 Option - 选项类型 122 | ## 4.02 daemon - 守护进程 123 | ## 4.03 attach - 依附运行的container 124 | ## 4.04 build - 建造Docker images 125 | ## 4.05 commit - 提交container 126 | ## 4.06 cp - 复制container文件 127 | ## 4.07 diff - 查看container变化文件 128 | ## 4.08 events - 查看事件 129 | ## 4.09 export - 导出文件到tar包 130 | ## 4.10 history - 查看image历史 131 | ## 4.11 images - 查看 images 132 | ## 4.12 import - 导入 133 | ## 4.13 info - 查看docker系统级信息 134 | ## 4.14 inspect - 查看docker底层信息 135 | ## 4.15 kill - 终止container进程 136 | ## 4.16 load - 载入image从tar包 137 | ## 4.17 login - 注册或登录Docker服务器 138 | ## 4.18 logout - 登出Docker服务器 139 | ## 4.19 logs - 查看container日志 140 | ## 4.20 port - 查看端口 141 | ## 4.21 pause - 暂停container 142 | ## 4.22 ps - 查看containers列表 143 | ## 4.23 pull - 拉取image 144 | ## 4.24 push - 推送image 145 | ## 4.25 restart - 重新启动container 146 | ## 4.26 rm - 移除containers 147 | ## 4.27 rmi - 移除images 148 | ## 4.28 run - 运行新的container 149 | ## 4.29 save - 保存image为tar包 150 | ## 4.30 search - 搜索images 151 | ## 4.31 start - 开始停止的container 152 | ## 4.32 stop - 停止运行的container 153 | ## 4.33 tag - 给image标签 154 | ## 4.34 top - 查看运行的container 155 | ## 4.35 unpause - 启动container所有进程 156 | ## 4.36 version - 查看Docker版本 157 | ## 4.37 wait - 等待直到container停止 158 | 159 | --- 160 | # 第 5 章 Dockerfile详解 161 | 162 | --- 163 | ## 5.01 使用 164 | ## 5.02 示例 165 | ## 5.03 格式 166 | ## 5.04 The .dockerignore file - 忽略文件 167 | ## 5.05 FROM - 定义image 168 | ## 5.06 MAINTAINER - 授权images 169 | ## 5.07 RUN - 定义运行命令 170 | ## 5.08 CMD - 定义container预设值 171 | ## 5.09 EXPOSE - 定义监听端口 172 | ## 5.10 ENV - 定义环境变量 173 | ## 5.11 ADD - 定义添加文件 174 | ## 5.12 COPY - 定义复制文件 175 | ## 5.13 ENTRYPOINT - container接入点 176 | ## 5.14 VOLUME - 定义挂载数据 177 | ## 5.15 USER - 定义用户 178 | ## 5.16 WORKDIR - 定义工作路径 179 | ## 5.17 ONBUILD - 定义触发器 180 | 181 | --- 182 | # 第 6 章 实战案例 183 | 184 | --- 185 | ## 6.1 Node.js web应用 186 | ## 6.2 django web应用 187 | ## 6.3 mysql 应用 188 | ## 6.4 Docker 管理 189 | 190 | 191 | ### 附录一:命令查询 192 | ### 附录二:资源链接 193 | 194 | -------------------------------------------------------------------------------- /books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/books.png -------------------------------------------------------------------------------- /cTutorial/cTutorial_BFiles.md: -------------------------------------------------------------------------------- 1 | ###Binary Files 2 | 3 | Binary files are very similar to arrays of structures, except the structures are in a disk file rather than in an array in memory. Because the structures in a binary file are on disk, you can create very large collections of them (limited only by your available disk space). They are also permanent and always available. The only disadvantage is the slowness that comes from disk access time. 4 | 5 | Binary files have two features that distinguish them from text files: 6 | 7 | * You can jump instantly to any structure in the file, which provides random access as in an array. 8 | * You can change the contents of a structure anywhere in the file at any time. 9 | 10 | Binary files also usually have faster read and write times than text files, because a binary image of the record is stored directly from memory to disk (or vice versa). In a text file, everything has to be converted back and forth to text, and this takes time. 11 | 12 | C supports the file-of-structures concept very cleanly. Once you open the file you can read a structure, write a structure, or seek to any structure in the file. This file concept supports the concept of a **file pointer**. When the file is opened, the pointer points to record 0 (the first record in the file). Any **read operation** reads the currently pointed-to structure and moves the pointer down one structure. Any **write operation** writes to the currently pointed-to structure and moves the pointer down one structure. **Seek** moves the pointer to the requested record. 13 | 14 | Keep in mind that C thinks of everything in the disk file as blocks of bytes read from disk into memory or read from memory onto disk. C uses a file pointer, but it can point to any byte location in the file. You therefore have to keep track of things. 15 | 16 | The following program illustrates these concepts: 17 | 18 | ``` 19 | #include 20 | 21 | /* random record description - could be anything */ 22 | struct rec 23 | { 24 | int x,y,z; 25 | }; 26 | 27 | /* writes and then reads 10 arbitrary records 28 | from the file "junk". */ 29 | int main() 30 | { 31 | int i,j; 32 | FILE *f; 33 | struct rec r; 34 | 35 | /* create the file of 10 records */ 36 | f=fopen("junk","w"); 37 | if (!f) 38 | return 1; 39 | for (i=1;i<=10; i++) 40 | { 41 | r.x=i; 42 | fwrite(&r,sizeof(struct rec),1,f); 43 | } 44 | fclose(f); 45 | 46 | /* read the 10 records */ 47 | f=fopen("junk","r"); 48 | if (!f) 49 | return 1; 50 | for (i=1;i<=10; i++) 51 | { 52 | fread(&r,sizeof(struct rec),1,f); 53 | printf("%d\n",r.x); 54 | } 55 | fclose(f); 56 | printf("\n"); 57 | 58 | /* use fseek to read the 10 records 59 | in reverse order */ 60 | f=fopen("junk","r"); 61 | if (!f) 62 | return 1; 63 | for (i=9; i>=0; i--) 64 | { 65 | fseek(f,sizeof(struct rec)*i,SEEK_SET); 66 | fread(&r,sizeof(struct rec),1,f); 67 | printf("%d\n",r.x); 68 | } 69 | fclose(f); 70 | printf("\n"); 71 | 72 | /* use fseek to read every other record */ 73 | f=fopen("junk","r"); 74 | if (!f) 75 | return 1; 76 | fseek(f,0,SEEK_SET); 77 | for (i=0;i<5; i++) 78 | { 79 | fread(&r,sizeof(struct rec),1,f); 80 | printf("%d\n",r.x); 81 | fseek(f,sizeof(struct rec),SEEK_CUR); 82 | } 83 | fclose(f); 84 | printf("\n"); 85 | 86 | /* use fseek to read 4th record, 87 | change it, and write it back */ 88 | f=fopen("junk","r+"); 89 | if (!f) 90 | return 1; 91 | fseek(f,sizeof(struct rec)*3,SEEK_SET); 92 | fread(&r,sizeof(struct rec),1,f); 93 | r.x=100; 94 | fseek(f,sizeof(struct rec)*3,SEEK_SET); 95 | fwrite(&r,sizeof(struct rec),1,f); 96 | fclose(f); 97 | printf("\n"); 98 | 99 | /* read the 10 records to insure 100 | 4th record was changed */ 101 | f=fopen("junk","r"); 102 | if (!f) 103 | return 1; 104 | for (i=1;i<=10; i++) 105 | { 106 | fread(&r,sizeof(struct rec),1,f); 107 | printf("%d\n",r.x); 108 | } 109 | fclose(f); 110 | return 0; 111 | } 112 | ``` 113 | 114 | In this program, a structure description **rec** has been used, but you can use any structure description you want. You can see that **fopen** and **fclose** work exactly as they did for text files. 115 | 116 | The new functions here are **fread**, **fwrite** and **fseek**. The fread function takes four parameters: 117 | 118 | * A memory address 119 | * The number of bytes to read per block 120 | * The number of blocks to read 121 | * The file variable 122 | 123 | Thus, the line **fread(&r,sizeof(struct rec),1,f);** says to read 12 bytes (the size of rec) from the file **f** (from the current location of the file pointer) into memory address **&r**. One block of 12 bytes is requested. It would be just as easy to read 100 blocks from disk into an array in memory by changing 1 to 100. 124 | 125 | The **fwrite** function works the same way, but moves the block of bytes from memory to the file. The **fseek** function moves the file pointer to a byte in the file. Generally, you move the pointer in **sizeof(struct rec)** increments to keep the pointer at record boundaries. You can use three options when seeking: 126 | 127 | * SEEK_SET 128 | * SEEK_CUR 129 | * SEEK_END 130 | 131 | **SEEK_SET** moves the pointer **x** bytes down from the beginning of the file (from byte 0 in the file). **SEEK_CUR** moves the pointer **x** bytes down from the current pointer position. **SEEK_END** moves the pointer from the end of the file (so you must use negative offsets with this option). 132 | 133 | Several different options appear in the code above. In particular, note the section where the file is opened with **r+** mode. This opens the file for reading and writing, which allows records to be changed. The code seeks to a record, reads it, and changes a field; it then seeks back because the read displaced the pointer, and writes the change back. -------------------------------------------------------------------------------- /cTutorial/cTutorial_CLA.md: -------------------------------------------------------------------------------- 1 | ###Command Line Arguments 2 | 3 | C provides a fairly simple mechanism for retrieving command line parameters entered by the user. It passes an **argv** parameter to the main function in the program. **argv** structures appear in a fair number of the more advanced library calls, so understanding them is useful to any C programmer. 4 | 5 | Enter the following code and compile it: 6 | 7 | ``` 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int x; 13 | 14 | printf("%d\n",argc); 15 | for (x=0; x 100) 27 | Or this way: 28 | rand(); 29 | ``` 30 | 31 | In the latter case, the function is called but the value returned by rand is discarded. You may never want to do this with rand, but many functions return some kind of error code through the function name, and if you are not concerned with the error code (for example, because you know that an error is impossible) you can discard it in this way. 32 | 33 | Functions can use a void return type if you intend to return nothing. For example: 34 | 35 | ``` 36 | void print_header() 37 | { 38 | printf("Program Number 1\n"); 39 | printf("by Marshall Brain\n"); 40 | printf("Version 1.0, released 12/26/91\n"); 41 | } 42 | ``` 43 | 44 | This function returns no value. You can call it with the following statement: 45 | 46 | ``` 47 | print_header(); 48 | ``` 49 | 50 | You must include **()** in the call. If you do not, the function is not called, even though it will compile correctly on many systems. 51 | 52 | C functions can accept parameters of any type. For example: 53 | 54 | ``` 55 | int fact(int i) 56 | { 57 | int j,k; 58 | 59 | j=1; 60 | for (k=2; k<=i; k++) 61 | j=j*k; 62 | return j; 63 | } 64 | ``` 65 | returns the factorial of **i**, which is passed in as an integer parameter. Separate multiple parameters with commas: 66 | 67 | ``` 68 | int add (int i, int j) 69 | { 70 | return i+j; 71 | } 72 | ``` 73 | 74 | C has evolved over the years. You will sometimes see functions such as **add** written in the "old style," as shown below: 75 | 76 | ``` 77 | int add(i,j) 78 | int i; 79 | int j; 80 | { 81 | return i+j; 82 | } 83 | ``` 84 | 85 | It is important to be able to read code written in the older style. There is no difference in the way it executes; it is just a different notation. You should use the "new style," (known as **ANSI C**) with the type declared as part of the parameter list, unless you know you will be shipping the code to someone who has access only to an "old style" (non-ANSI) compiler. 86 | 87 | 88 | > **TRY THIS!** 89 | > 90 | > *Go back to the bubble sort example presented earlier and create a function for the bubble sort. 91 | Go back to earlier programs and create a function to get input from the user rather than taking the input in the main function.* 92 | 93 | 94 | ###Functions: Function Prototypes 95 | 96 | It is now considered good form to use **function prototypes** for all functions in your program. A prototype declares the function name, its parameters, and its return type to the rest of the program prior to the function's actual declaration. To understand why function prototypes are useful, enter the following code and run it: 97 | 98 | ``` 99 | #include 100 | 101 | void main() 102 | { 103 | printf("%d\n",add(3)); 104 | } 105 | 106 | int add(int i, int j) 107 | { 108 | return i+j; 109 | } 110 | ``` 111 | 112 | This code compiles on many compilers without giving you a warning, even though **add** expects two parameters but receives only one. It works because many C compilers do not check for parameter matching either in type or count. You can waste an enormous amount of time debugging code in which you are simply passing one too many or too few parameters by mistake. The above code compiles properly, but it produces the wrong answer. 113 | 114 | To solve this problem, C lets you place function prototypes at the beginning of (actually, anywhere in) a program. If you do so, C checks the types and counts of all parameter lists. Try compiling the following: 115 | 116 | ``` 117 | #include 118 | 119 | int add (int,int); /* function prototype for add */ 120 | 121 | void main() 122 | { 123 | printf("%d\n",add(3)); 124 | } 125 | 126 | int add(int i, int j) 127 | { 128 | return i+j; 129 | } 130 | ``` 131 | 132 | The prototype causes the compiler to flag an error on the **printf** statement. 133 | 134 | Place one prototype for each function at the beginning of your program. They can save you a great deal of debugging time, and they also solve the problem you get when you compile with functions that you use before they are declared. For example, the following code will not compile: 135 | 136 | ``` 137 | #include 138 | 139 | void main() 140 | { 141 | printf("%d\n",add(3)); 142 | } 143 | 144 | float add(int i, int j) 145 | { 146 | return i+j; 147 | } 148 | ``` 149 | 150 | Why, you might ask, will it compile when add returns an int but not when it returns a float? Because older C compilers default to an int return value. Using a prototype will solve this problem. "Old style" (non-ANSI) compilers allow prototypes, but the parameter list for the prototype must be empty. Old style compilers do no error checking on parameter lists. -------------------------------------------------------------------------------- /cTutorial/cTutorial_Libraries.md: -------------------------------------------------------------------------------- 1 | ###Libraries 2 | 3 | Libraries are very important in C because the C language supports only the most basic features that it needs. C does not even contain I/O functions to read from the keyboard and write to the screen. Anything that extends beyond the basic language must be written by a programmer. The resulting chunks of code are often placed in **libraries** to make them easily reusable. We have seen the standard I/O, or **stdio**, library already: Standard libraries exist for standard I/O, math functions, string handling, time manipulation, and so on. You can use libraries in your own programs to split up your programs into modules. This makes them easier to understand, test, and debug, and also makes it possible to reuse code from other programs that you write. 4 | 5 | You can create your own libraries easily. As an example, we will take some code from a previous article in this series and make a library out of two of its functions. Here's the code we will start with: 6 | 7 | ``` 8 | #include 9 | 10 | #define MAX 10 11 | 12 | int a[MAX]; 13 | int rand_seed=10; 14 | 15 | int rand() 16 | /* from K&R 17 | - produces a random number between 0 and 32767.*/ 18 | { 19 | rand_seed = rand_seed * 1103515245 +12345; 20 | return (unsigned int)(rand_seed / 65536) % 32768; 21 | } 22 | 23 | void main() 24 | { 25 | int i,t,x,y; 26 | 27 | /* fill array */ 28 | for (i=0; i < MAX; i++) 29 | { 30 | a[i]=rand(); 31 | printf("%d\n",a[i]); 32 | } 33 | 34 | /* bubble sort the array */ 35 | for (x=0; x < MAX-1; x++) 36 | for (y=0; y < MAX-x-1; y++) 37 | if (a[y] > a[y+1]) 38 | { 39 | t=a[y]; 40 | a[y]=a[y+1]; 41 | a[y+1]=t; 42 | } 43 | 44 | /* print sorted array */ 45 | printf("--------------------\n"); 46 | for (i=0; i < MAX; i++) 47 | printf("%d\n",a[i]); 48 | } 49 | ``` 50 | 51 | This code fills an array with random numbers, sorts them using a bubble sort, and then displays the sorted list. 52 | 53 | Take the bubble sort code, and use what you learned in the previous article to make a function from it. Since both the array a and the constant MAX are known globally, the function you create needs no parameters, nor does it need to return a result. However, you should use local variables for x, y, and t. 54 | 55 | Once you have tested the function to make sure it is working, pass in the number of elements as a parameter rather than using MAX: 56 | 57 | ``` 58 | #include 59 | 60 | #define MAX 10 61 | 62 | int a[MAX]; 63 | int rand_seed=10; 64 | 65 | /* from K&R 66 | - returns random number between 0 and 32767.*/ 67 | int rand() 68 | { 69 | rand_seed = rand_seed * 1103515245 +12345; 70 | return (unsigned int)(rand_seed / 65536) % 32768; 71 | } 72 | 73 | void bubble_sort(int m) 74 | { 75 | int x,y,t; 76 | for (x=0; x < m-1; x++) 77 | for (y=0; y < m-x-1; y++) 78 | if (a[y] > a[y+1]) 79 | { 80 | t=a[y]; 81 | a[y]=a[y+1]; 82 | a[y+1]=t; 83 | } 84 | } 85 | 86 | void main() 87 | { 88 | int i,t,x,y; 89 | /* fill array */ 90 | for (i=0; i < MAX; i++) 91 | { 92 | a[i]=rand(); 93 | printf("%d\n",a[i]); 94 | } 95 | bubble_sort(MAX); 96 | /* print sorted array */ 97 | printf("--------------------\n"); 98 | for (i=0; i < MAX; i++) 99 | printf("%d\n",a[i]); 100 | } 101 | ``` 102 | 103 | You can also generalize the **bubble_sort** function even more by passing in **a** as a parameter: 104 | 105 | ``` 106 | bubble_sort(int m, int a[]) 107 | ``` 108 | 109 | This line says, "Accept the integer array a of any size as a parameter." Nothing in the body of the **bubble_sort** function needs to change. To call bubble_sort, change the call to: 110 | 111 | ``` 112 | bubble_sort(MAX, a); 113 | ``` 114 | 115 | Note that **&a** has not been used in the function call even though the sort will change **a**. The reason for this will become clear once you understand pointers. 116 | 117 | 118 | 119 | ###Making a Library 120 | 121 | Since the rand and bubble_sort functions in the previous program are useful, you will probably want to reuse them in other programs you write. You can put them into a utility library to make their reuse easier. 122 | 123 | Every library consists of two parts: a header file and the actual code file. The header file, normally denoted by a **.h** suffix, contains information about the library that programs using it need to know. In general, the header file contains constants and types, along with prototypes for functions available in the library. Enter the following header file and save it to a file named **util.h**. 124 | 125 | ``` 126 | /* util.h */ 127 | extern int rand(); 128 | extern void bubble_sort(int, int []); 129 | ``` 130 | 131 | These two lines are function prototypes. The word "extern" in C represents functions that will be linked in later. If you are using an old-style compiler, remove the parameters from the parameter list of **bubble_sort**. 132 | 133 | Enter the following code into a file named **util.c**. 134 | 135 | ``` 136 | /* util.c */ 137 | #include "util.h" 138 | 139 | int rand_seed=10; 140 | 141 | /* from K&R 142 | - produces a random number between 0 and 32767.*/ 143 | int rand() 144 | { 145 | rand_seed = rand_seed * 1103515245 +12345; 146 | return (unsigned int)(rand_seed / 65536) % 32768; 147 | } 148 | 149 | void bubble_sort(int m,int a[]) 150 | { 151 | int x,y,t; 152 | for (x=0; x < m-1; x++) 153 | for (y=0; y < m-x-1; y++) 154 | if (a[y] > a[y+1]) 155 | { 156 | t=a[y]; 157 | a[y]=a[y+1]; 158 | a[y+1]=t; 159 | } 160 | } 161 | ``` 162 | 163 | Note that the file includes its own header file (**util.h**) and that it uses quotes instead of the symbols **<** and**>** , which are used only for system libraries. As you can see, this looks like normal C code. Note that the variable **rand_seed**, because it is not in the header file, cannot be seen or modified by a program using this library. This is called information hiding. Adding the word **static** in front of **int** enforces the hiding completely. 164 | 165 | Enter the following main program in a file named **main.c**. 166 | 167 | ``` 168 | #include 169 | #include "util.h" 170 | 171 | #define MAX 10 172 | 173 | int a[MAX]; 174 | 175 | void main() 176 | { 177 | int i,t,x,y; 178 | /* fill array */ 179 | for (i=0; i < MAX; i++) 180 | { 181 | a[i]=rand(); 182 | printf("%d\n",a[i]); 183 | } 184 | 185 | bubble_sort(MAX,a); 186 | 187 | /* print sorted array */ 188 | printf("--------------------\n"); 189 | for (i=0; i < MAX; i++) 190 | printf("%d\n",a[i]); 191 | } 192 | ``` 193 | 194 | This code includes the utility library. The main benefit of using a library is that the code in the main program is much shorter. 195 | 196 | 197 | ###Compiling and Running with a Library 198 | 199 | To compile the library, type the following at the command line (assuming you are using UNIX) (replace gcc with cc if your system uses cc): 200 | 201 | ``` 202 | gcc -c -g util.c 203 | ``` 204 | 205 | The **-c** causes the compiler to produce an object file for the library. The object file contains the library's machine code. It cannot be executed until it is linked to a program file that contains a main function. The machine code resides in a separate file named **util.o**. 206 | 207 | To compile the main program, type the following: 208 | 209 | ``` 210 | gcc -c -g main.c 211 | ``` 212 | 213 | This line creates a file named **main.o** that contains the machine code for the main program. To create the final executable that contains the machine code for the entire program, link the two object files by typing the following: 214 | 215 | ``` 216 | gcc -o main main.o util.o 217 | ``` 218 | This links **main.o** and **util.o** to form an executable named **main**. To run it, type **main**. 219 | 220 | Makefiles make working with libraries a bit easier. You'll find out about makefiles on the next page. -------------------------------------------------------------------------------- /cTutorial/cTutorial_Makefiles.md: -------------------------------------------------------------------------------- 1 | ###Makefiles 2 | 3 | It can be cumbersome to type all of the **gcc** lines over and over again, especially if you are making a lot of changes to the code and it has several libraries. The **make** facility solves this problem. You can use the following makefile to replace the compilation sequence above: 4 | 5 | ``` 6 | main: main.o util.o 7 | gcc -o main main.o util.o 8 | main.o: main.c util.h 9 | gcc -c -g main.c 10 | util.o: util.c util.h 11 | gcc -c -g util.c 12 | ``` 13 | 14 | Enter this into a file named **makefile**, and type **make** to build the executable. Note that you must precede all **gcc** lines with a tab. (Eight spaces will not suffice -- it must be a tab. All other lines must be flush left.) 15 | 16 | This makefile contains two types of lines. The lines appearing flush left are **dependency lines**. The lines preceded by a tab are executable lines, which can contain any valid UNIX command. A dependency line says that some file is dependent on some other set of files. For example, **main.o: main.c util.h** says that the file **main.o** is dependent on the files **main.c** and **util.h**. If either of these two files changes, the following executable line(s) should be executed to recreate **main.o**. 17 | 18 | Note that the final executable produced by the whole makefile is main, on line 1 in the makefile. The final result of the makefile should always go on line 1, which in this makefile says that the file **main** is dependent on **main.o** and **util.o**. If either of these changes, execute the line **gcc -o main main.o util.o** to recreate **main**. 19 | 20 | It is possible to put multiple lines to be executed below a dependency line -- they must all start with a tab. A large program may have several libraries and a main program. The makefile automatically recompiles everything that needs to be recompiled because of a change. 21 | 22 | If you are not working on a UNIX machine, your compiler almost certainly has functionality equivalent to makefiles. Read the documentation for your compiler to learn how to use it. 23 | 24 | Now you understand why you have been including stdio.h in earlier programs. It is simply a standard library that someone created long ago and made available to other programmers to make their lives easier. -------------------------------------------------------------------------------- /cTutorial/cTutorial_OperatorP.md: -------------------------------------------------------------------------------- 1 | ###Operator Precedence 2 | 3 | C contains many operators, and because of the way in which operator precedence works, the interactions between multiple operators can become confusing. 4 | 5 | ``` 6 | x=5+3*6; 7 | ``` 8 | 9 | X receives the value 23, not 48, because in C multiplication and division have higher precedence than addition and subtraction. 10 | 11 | ``` 12 | char *a[10]; 13 | ``` 14 | 15 | Is **a** a single pointer to an array of 10 characters, or is it an array of 10 pointers to character? Unless you know the precedence conventions in C, there is no way to find out. Similarly, in E.11 we saw that because of precedence statements such as **\*p.i = 10;** do not work. Instead, the form **(\*p).i = 10;** must be used to force correct precedence. 16 | 17 | The following table from C Programming Language, by Kernighan and Ritchie, shows the precedence hierarchy in C. The top line has the highest precedence. 18 | 19 | |Operators | Associativity 20 | |:------:|:----------: 21 | |( [ - . |Left to right 22 | |! - ++ -{- + * & (type-cast) sizeof |Right to left 23 | |(in the above line, +, - and * are the unary forms) | - 24 | |* / % |Left to right 25 | |+ - |Left to right 26 | |<< >> | Left to right 27 | |< <= > >= | Left to right 28 | |== != |Left to right 29 | |& |Left to right 30 | |^ |Left to right 31 | |\| |Left to right 32 | |&& | Left to right 33 | |\|\| |Left to right 34 | |?: |Left to right 35 | |= += -= *= /= %= &= ^= \|= <<= >>= |Right to left 36 | |, |Left to right 37 | 38 | Using this table, you can see that **char \*a[10];** is an array of 10 pointers to character. You can also see why the parentheses are required if **(\*p).i** is to be handled correctly. After some practice, you will memorize most of this table, but every now and again something will not work because you have been caught by a subtle precedence problem. -------------------------------------------------------------------------------- /cTutorial/cTutorial_TextFiles.md: -------------------------------------------------------------------------------- 1 | ###Text Files 2 | 3 | Text files in C are straightforward and easy to understand. All text file functions and types in C come from the **stdio** library. 4 | 5 | When you need text I/O in a C program, and you need only one source for input information and one sink for output information, you can rely on **stdin** (standard in) and **stdout** (standard out). You can then use input and output redirection at the command line to move different information streams through the program. There are six different I/O commands in that you can use with stdin and stdout: 6 | 7 | * **printf** - prints formatted output to stdout 8 | * **scanf** - reads formatted input from stdin 9 | * **puts** - prints a string to stdout 10 | * **gets** - reads a string from stdin 11 | * **putc** - prints a character to stdout 12 | * **getc**, **getchar** - reads a character from stdin 13 | 14 | The advantage of stdin and stdout is that they are easy to use. Likewise, the ability to redirect I/O is very powerful. For example, maybe you want to create a program that reads from stdin and counts the number of characters: 15 | 16 | ``` 17 | #include 18 | #include 19 | 20 | void main() 21 | { 22 | char s[1000]; 23 | int count=0; 24 | while (gets(s)) 25 | count += strlen(s); 26 | printf("%d\n",count); 27 | } 28 | ``` 29 | 30 | Enter this code and run it. It waits for input from stdin, so type a few lines. When you are done, press CTRL-D to signal end-of-file (eof). The gets function reads a line until it detects eof, then returns a 0 so that the while loop ends. When you press CTRL-D, you see a count of the number of characters in stdout (the screen). (Use **man gets** or your compiler's documentation to learn more about the gets function.) 31 | 32 | Now, suppose you want to count the characters in a file. If you compiled the program to an executable named **xxx**, you can type the following: 33 | 34 | ``` 35 | xxx < filename 36 | ``` 37 | Instead of accepting input from the keyboard, the contents of the file named **filename** will be used instead. You can achieve the same result using **pipes**: 38 | 39 | ``` 40 | cat < filename | xxx 41 | ``` 42 | You can also redirect the output to a file: 43 | 44 | ``` 45 | xxx < filename > out 46 | ``` 47 | This command places the character count produced by the program in a text file named **out**. 48 | 49 | Sometimes, you need to use a text file directly. For example, you might need to open a specific file and read from or write to it. You might want to manage several streams of input or output or create a program like a text editor that can save and recall data or configuration files on command. In that case, use the text file functions in stdio: 50 | 51 | * **fopen** - opens a text file 52 | * **fclose** - closes a text file 53 | * **feof** - detects end-of-file marker in a file 54 | * **fprintf** - prints formatted output to a file 55 | * **fscanf** - reads formatted input from a file 56 | * **fputs** - prints a string to a file 57 | * **fgets** - reads a string from a file 58 | * **fputc** - prints a character to a file 59 | * **fgetc** - reads a character from a file 60 | 61 | 62 | 63 | > **MAIN FUNCTION RETURN VALUES** 64 | > 65 | > *This program is the first program in this series that returns an error value from the main program. If the **fopen** command fails, f will contain a NULL value (a zero). We test for that error with the if statement. The if statement looks at the True/False value of the variable **f**. Remember that in C, 0 is False and anything else is true. So if there were an error opening the file, f would contain zero, which is False. The ! is the NOT operator. It inverts a Boolean value. So the if statement could have been written like this:* 66 | > 67 | > *That is equivalent. However, **if (!f)** is more common. 68 | If there is a file error, we return a 1 from the main function. In UNIX, you can actually test for this value on the command line. See the shell documentation for details.* 69 | 70 | 71 | ###Text Files: Opening 72 | You use **fopen** to open a file. It opens a file for a specified mode (the three most common are r, w, and a, for read, write, and append). It then returns a file pointer that you use to access the file. For example, suppose you want to open a file and write the numbers 1 to 10 in it. You could use the following code: 73 | 74 | ``` 75 | #include 76 | #define MAX 10 77 | 78 | int main() 79 | { 80 | FILE *f; 81 | int x; 82 | f=fopen("out","w"); 83 | if (!f) 84 | return 1; 85 | for(x=1; x<=MAX; x++) 86 | fprintf(f,"%d\n",x); 87 | fclose(f); 88 | return 0; 89 | } 90 | ``` 91 | 92 | The **fopen** statement here opens a file named **out** with the w mode. This is a destructive write mode, which means that if **out** does not exist it is created, but if it does exist it is destroyed and a new file is created in its place. The fopen command returns a pointer to the file, which is stored in the variable f. This variable is used to refer to the file. If the file cannot be opened for some reason, f will contain NULL. 93 | 94 | The fprintf statement should look very familiar: It is just like printf but uses the file pointer as its first parameter. The fclose statement closes the file when you are done. 95 | 96 | 97 | > **C ERRORS TO AVOID** 98 | > 99 | > *Do not accidentally type **close** instead of **fclose**. The close function exists, so the compiler accepts it. It will even appear to work if the program only opens or closes a few files. However, if the program opens and closes a file in a loop, it will eventually run out of available file handles and/or memory space and crash, because **close** is not closing the files correctly.* 100 | 101 | 102 | ###Text Files: Reading 103 | To read a file, open it with r mode. In general, it is not a good idea to use **fscanf** for reading: Unless the file is perfectly formatted, fscanf will not handle it correctly. Instead, use **fgets** to read in each line and then parse out the pieces you need. 104 | The following code demonstrates the process of reading a file and dumping its contents to the screen: 105 | 106 | ``` 107 | #include 108 | 109 | int main() 110 | { 111 | FILE *f; 112 | char s[1000]; 113 | 114 | f=fopen("infile","r"); 115 | if (!f) 116 | return 1; 117 | while (fgets(s,1000,f)!=NULL) 118 | printf("%s",s); 119 | fclose(f); 120 | return 0; 121 | } 122 | ``` 123 | 124 | The fgets statement returns a NULL value at the end-of-file marker. It reads a line (up to 1,000 characters in this case) and then prints it to stdout. Notice that the printf statement does not include \n in the format string, because fgets adds \n to the end of each line it reads. Thus, you can tell if a line is not complete in the event that it overflows the maximum line length specified in the second parameter to fgets. -------------------------------------------------------------------------------- /cTutorial/cTutorial_cheatSheet.md: -------------------------------------------------------------------------------- 1 | GitHub Markup 2 | ============= 3 | 4 | We use this library on GitHub when rendering your README or any other 5 | rich text file. The generated HTML is then run through filters in the [html-pipeline](https://github.com/jch/html-pipeline) to perform things like [sanitization](#html-sanitization) and [syntax highlighting](https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/syntax_highlight_filter.rb). 6 | 7 | Markups 8 | ------- 9 | 10 | The following markups are supported. The dependencies listed are required if 11 | you wish to run the library. You can also run `script/bootstrap` to fetch them all. 12 | 13 | * [.markdown, .mdown, .mkdn, .md](http://daringfireball.net/projects/markdown/) -- `gem install redcarpet` (https://github.com/vmg/redcarpet) 14 | * [.textile](http://www.textism.com/tools/textile/) -- `gem install RedCloth` 15 | * [.rdoc](http://rdoc.sourceforge.net/) -- `gem install rdoc -v 3.6.1` 16 | * [.org](http://orgmode.org/) -- `gem install org-ruby` 17 | * [.creole](http://wikicreole.org/) -- `gem install creole` 18 | * [.mediawiki, .wiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `gem install wikicloth` 19 | * [.rst](http://docutils.sourceforge.net/rst.html) -- `easy_install docutils` 20 | * [.asciidoc, .adoc, .asc](http://asciidoc.org/) -- `gem install asciidoctor` (http://asciidoctor.org) 21 | * [.pod](http://search.cpan.org/dist/perl/pod/perlpod.pod) -- `Pod::Simple::HTML` 22 | comes with Perl >= 5.10. Lower versions should install Pod::Simple from CPAN. 23 | 24 | Installation 25 | ----------- 26 | 27 | gem install github-markup 28 | 29 | Usage 30 | ----- 31 | 32 | require 'github/markup' 33 | GitHub::Markup.render('README.markdown', "* One\n* Two") 34 | 35 | Or, more realistically: 36 | 37 | require 'github/markup' 38 | GitHub::Markup.render(file, File.read(file)) 39 | 40 | Contributing 41 | ------------ 42 | 43 | See [Contributing](CONTRIBUTING.md) 44 | 45 | HTML sanitization 46 | ----------------- 47 | 48 | HTML rendered by the various markup language processors gets passed through an [HTML sanitization filter](https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/sanitization_filter.rb) for security reasons. HTML elements not in the whitelist are removed. HTML attributes not in the whitelist are removed from the preserved elements. 49 | 50 | The following HTML elements, organized by category, are whitelisted: 51 | 52 | |Type | Elements 53 | |------|---------- 54 | |Headings | `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `h7`, `h8` 55 | |Prose | `p`, `div`, `blockquote` 56 | |Formatted | `pre` 57 | | Inline | `b`, `i`, `strong`, `em`, `tt`, `code`, `ins`, `del`, `sup`, `sub`, `kbd`, `samp`, `q`, `var` 58 | | Lists | `ol`, `ul`, `li`, `dl`, `dt`, `dd` 59 | | Tables | `table`, `thead`, `tbody`, `tfoot`, `tr`, `td`, `th` 60 | | Breaks | `br`, `hr` 61 | | Ruby (East Asian) | `ruby`, `rt`, `rp` 62 | 63 | The following attributes, organized by element, are whitelisted: 64 | 65 | |Element | Attributes 66 | |------|---------- 67 | | `a` | `href` (`http://`, `https://`, `mailto://`, `github-windows://`, and `github-mac://` URI schemes and relative paths only) 68 | | `img` | `src` (`http://` and `https://` URI schemes and relative paths only) 69 | | `div` | `itemscope`, `itemtype` 70 | | All | `abbr`, `accept`, `accept-charset`, `accesskey`, `action`, `align`, `alt`, `axis`, `border`, `cellpadding`, `cellspacing`, `char`, `charoff`, `charset`, `checked`, `cite`, `clear`, `cols`, `colspan`, `color`, `compact`, `coords`, `datetime`, `dir`, `disabled`, `enctype`, `for`, `frame`, `headers`, `height`, `hreflang`, `hspace`, `ismap`, `label`, `lang`, `longdesc`, `maxlength`, `media`, `method`, `multiple`, `name`, `nohref`, `noshade`, `nowrap`, `prompt`, `readonly`, `rel`, `rev`, `rows`, `rowspan`, `rules`, `scope`, `selected`, `shape`, `size`, `span`, `start`, `summary`, `tabindex`, `target`, `title`, `type`, `usemap`, `valign`, `value`, `vspace`, `width`, `itemprop` 71 | 72 | Note that the `id` attribute is *not* whitelisted. 73 | -------------------------------------------------------------------------------- /changlog-1.5-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker 1.5.0 更新日志 (2015-02-10) 2 | 3 | ###构建 4 | * Dockerfile 可以在`docker build`时,用`-f`标志指定 5 | * Dockerfile与`.dockerignore`文件作为`.dockerignore`文件的一部分可以自行忽略,当这些文件的修改后,会防止`ADD`或`COPY`指令缓存的作废 6 | * `ADD`和`COPY`指令接受相对路径 7 | * Dockerfile `FROM scratch`指令解释为一个没有基础的说明 8 | * 改善了性能当公开大量的端口时 9 | 10 | ###尝试 11 | * 允许客户端只为Windows集成测试 12 | * 对于作为我们的测试套件的一部分的Docker daemon还包括了docker-py集成测试 13 | 14 | ###包装 15 | * 对registry的HTTP API新版本的支持 16 | * 为镜像以及大多数现有的层文件加快了`docker push` 17 | * 修正了通过代理联系私有registry 18 | 19 | ###远程API 20 | * 一个新的endpoint将动态显示容器资源用量,可用`docker stats`命令访问 21 | * 可以使用`rename` endpoint来重命名容器,相关命令为:`docker rename` 22 | * 容器`inspect`endpoint显示在运行的容器中执行`exec`命令的ID 23 | * 容器`inspect`endpoint显示Docker自动重启容器的次数 24 | * 新的event类型将由`events`endpoint:`OOM`(容器内存溢出而停止)、`exec_create`与`exec_start`展现。 25 | * 修正了返回的有数字字符的字段串不正确忽略了其双引号 26 | 27 | ###运行时 28 | * Docker daemon完全支持IPv6 29 | * `docker run`命令可以采取`--pid=host`标志使用主机的PID命名空间,这样可以使得例如使用容器调试工具来调试主机进程成为可能。 30 | * `docker run`命令可以采取`--read-only`标志使容器的根文件系统为只读,这样就可以与`volumes`结合使用以便容器的进程只能写入作为持久数据的文件。 31 | * 通过`docker run`使用`-memory-swap`标志可以限制容器总内存使用量 32 | * 主要改进了devicemapper存储驱动程序的稳定性 33 | * 与主机系统更好地集成:重新启动时,容器的变化会反映到主机的`/etc/resolv.conf`文件 34 | * 与主机系统更好地集成:每个容器的iptable规则被移动到Docker链中 35 | * 修正了容器由于内存溢出而返回一个无效的退出代码 36 | 37 | ###其他 38 | * 当连接到Docker daemon时,客户端会适当地考虑HTTP_PROXY、HTTPS_PROXY以及NO_PROXY环境变量。 -------------------------------------------------------------------------------- /commit/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/commit/README.md -------------------------------------------------------------------------------- /comparing-monitoring-options-for-docker-deployments-cn.md: -------------------------------------------------------------------------------- 1 | Usman是服务器和基础架构工程师,在各种云平台上构建大规模分布式服务有丰富的经验。你可以在[techtraits.com](http://techtraits.com/)阅读他的更多文章,或者在twitter上添加他[@usman_ismail](https://twitter.com/usman_ismail)或是在[GitHub](https://github.com/usmanismail)。 2 | 3 | 由于一些大型部署使用的是Docker,因此获取Docker环境的状态与健康的可视化就显得尤为重要了。在这篇文章中,我的目标是重温一些用来监测容器的常用工具。我会基于以下标准来评估这些工具: 4 | 1. 易于部署 5 | 2. 呈现信息的详细级别 6 | 3. 整个部署信息的综合水平 7 | 4. 可以从数据生成警报 8 | 5. 可以监测非Docker的资源 9 | 6. 成本 10 | 虽然这个名单不是很全面,但是我试图强调的是最常用的工具以及优化此六项评估标准的工具。 11 | 12 | ##Docker Stats命令 13 | 文中的所有命令都专门在亚马逊网络服务EC2上运行的RancherOS实例上测试过。不过,今天所提到的工具应该在任何Docker部署上都能使用。 14 | 15 | 我将讨论的第一个工具是Docker本身。你可能不知道Docker客户端已经提供了基本的命令行工具来检查容器的资源消耗。想要查看容器统计信息只需运行`docker stats [CONTAINER_NAME]`。这样就可以查看每个容器的CPU利用率、内存的使用量以及可用内存总量。请注意,如果您没有对容器限制内存,那么该命令将显示您的主机的内存总量。但它并不意味着你的每个容器都能访问那么多的内存。另外你还能看到由容器通过网络发送和接收的数据总量。 16 | 17 | {{{ 18 | $ docker stats determined_shockley determined_wozniak prickly_hypatia 19 | CONTAINER CPU % MEM USAGE/LIMIT MEM % NET I/O 20 | determined_shockley 0.00% 884 KiB/1.961 GiB 0.04% 648 B/648 B 21 | determined_wozniak 0.00% 1.723 MiB/1.961 GiB 0.09% 1.266 KiB/648 B 22 | prickly_hypatia 0.00% 740 KiB/1.961 GiB 0.04% 1.898 KiB/648 B }}} 23 | 24 | 对于更详细的容器统计信息还可以使用Docker远程API 通过netcat来查看(见下文)。发送一个HTTP GET请求`/containers/[CONTAINER_NAME]`,其中`CONTAINER_NAME`是你想要统计的容器名称。你可以从[这里](https://gist.github.com/usmanismail/0c4922ffec4a0220d385)看到一个容器统计请求的详细信息。在上述的例子中你会得到缓存、交换空间以及内存的详细信息。如果要了解什么是metrics,那么你就需要精读Docker文档的[Run Metrics部分](https://docs.docker.com/articles/runmetrics/)。 25 | 26 | ####评分: 27 | 1. 易于部署程度:※※※※※ 28 | 2. 信息详细程度:※※※※※ 29 | 3. 集成度:无 30 | 4. 生成警报的能力:无 31 | 5. 监测非Docker的资源的能力:无 32 | 6. 成本:免费 33 | 34 | ##CAdvisor 35 | `Docker stats`命令和远程API用于命令行上的信息获取,但是,如果您想要在图形界面中访问信息,那么你就需要一个工具,如[CAdvisor](https://github.com/google/cadvisor)。CAdvisor提供了早前Docker stats命令所显示的数据的可视化界面。运行以下Docker命令,在浏览器里访问`http://:8080/`可以看到CAdvisor的界面。你将看到一些图标包括:全部CPU的使用率、内存使用率、网络吞吐量以及磁盘空间利用率。然后,您可以通过点击在网页顶部的`Docker Containers`链接,然后选择某个容器去深入了解此容器的使用情况统计。除了这些统计信息,CAdvisor还显示容器的限制,如果有的话,被放置在容器中,用分离部。 36 | 37 | {{{ 38 | docker run \ 39 | --volume=/:/rootfs:ro \ 40 | --volume=/var/run:/var/run:rw \ 41 | --volume=/sys:/sys:ro \ 42 | --volume=/var/lib/docker/:/var/lib/docker:ro \ 43 | --publish=8080:8080 \ 44 | --detach=true \ 45 | --name=cadvisor \ 46 | google/cadvisor:latest }}} 47 | 48 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-19-at-11.50.29-PM.png) 49 | 50 | CAdvisor是有用且很容易设置的工具,我们可以不用ssh就能连接到服务器来查看资源的消耗,而且它还给我们生成了图表。此外,当群集需要额外的资源时,压力表提供了快速预览。而且,与本文中的其他的工具不一样的是CAdvisor是免费的,因为它是开源的,同时它运行已配置群集的硬件上,最后,除了一些进程资源,CAdvisor并没有额外的消耗成本。但是,它有它的局限性;它只能监控一个Docker主机,因此,如果你有一个多节点部署,那么统计数据将是不相交的而且分散你的集群。值得注意的是,如果你使用的是Kubernetes,你可以使用[heapster](https://github.com/GoogleCloudPlatform/heapster)来监控多节点集群。在图表中的数据仅仅是时长一分钟的移动窗口,并没有方法来查看长期趋势。如果资源使用率在危险水平,它却没有生成警告的机制。如果在Docker节点的资源消耗方面,你没有任何可视化界面,那么CAdvisor是一个不错的开端来带你步入容器监控,然而如果你打算在你的容器中运行任何关键任务,那么更强大的工具或者方法是必要的。需要注意的是[Rancher](http://rancher.com/rancher-io/)在每个连接的主机上运行CAdvisor,并通过UI公开了一组有限的统计数据,并且通过API公开了所有的系统统计数据。 51 | 52 | ####评分:(忽略了heapster,因为它仅支持Kubernetes) 53 | 1. 易于部署程度:※※※※※ 54 | 2. 信息详细程度:※※ 55 | 3. 集成度:※ 56 | 4. 生成警报的能力:无 57 | 5. 监测非Docker的资源的能力:无 58 | 6. 成本:免费 59 | 60 | ##Scout 61 | 下一个Docker监控的方法是Scout,它解决了几个CAdvisor的局限。 Scout是聚合来自多个主机和容器的托管监控服务并且它有更长时间的数据呈现。它也可以基于这些指标生成警报。要获取Scout并运行,第一步,在[scoutapp.com](https://scoutapp.com/)注册一个Scout帐户,免费的试用账号足以用来集成测试。一旦你创建了自己的帐户并登录,点击右上角您的帐户名称,然后点击Account Basics来查看你的Account Key,你需要这个Key从我们的Docker服务器来发送指标。 62 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-21-at-9.30.08-AM.png) 63 | ![](http://rancher.com/wp-content/uploads/2015/03/accountid.png) 64 | 65 | 现在在你的主机上,创建一个名为scouts.yml的文件并将下面的文字复制到该文件中,用上边得到的Key替换到account_key。您可以对主机指定任何有意义的变量:display_name、environment与roles等属性。当他们在scout界面上呈现时,这些将用于分离各种指标。我假设有一组网站服务器列表正在运行Docker,它们都将采用如下图所示的变量。 66 | 67 | {{{ 68 | # account_key is the only required value 69 | account_key: YOUR_ACCOUNT_KEY 70 | hostname: web01-host 71 | display_name: web01 72 | environment: production 73 | roles: web }}} 74 | 75 | 现在,你可以使用scout配置文件通过Docker-scout插件来运行scout。 76 | 77 | {{{ 78 | docker run -d --name scout-agent \ 79 | -v /proc:/host/proc:ro \ 80 | -v /etc/mtab:/host/etc/mtab:ro \ 81 | -v /var/run/docker.sock:/host/var/run/docker.sock:ro \ 82 | -v `pwd`/scoutd.yml:/etc/scout/scoutd.yml \ 83 | -v /sys/fs/cgroup/:/host/sys/fs/cgroup/ \ 84 | --net=host --privileged \ 85 | soutapp/docker-scout }}} 86 | 87 | 这样你查看Scout网页就能看到一个条目,其中display_name参数(web01)就是你在scoutd.yml里面指定的。 88 | 89 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-21-at-9.58.40-AM.png) 90 | 91 | 如果你点击它(web01)就会显示主机的详细信息。其中包括任何运行在你主机上的进程计数、cpu使用率以及内存利用率,值得注意的是在docker内部并没有进程的限制。 92 | 93 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-21-at-10.00.47-AM.png) 94 | 95 | 如果要添加Docker监控服务,需要单击Roles选项卡,然后选择所有服务。现在点击+插件模板按钮,接下来的Docker监视器会加载详细信息视图。一旦详细信息呈现出来,选择安装插件来添加到您的主机。接着会给你提供一个已安装插件的名称以及需指定要监视的容器。如果该字段是空的,插件将监控主机上所有的容器。点击完成按钮,一分钟左右你就可以在[Server Name] > Plugins中看到从Docker监控插件中获取的详细信息。该插件为每个主机显示CPU使用率、内存使用率、网络吞吐量以及容器的数量。 96 | 97 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-20-at-10.11.06-PM.png) 98 | 99 | 你点击任何一个图表,都可以拉取该指标的详细视图,该视图可以让你看到时间跨度更长的趋势。 100 | 101 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-20-at-10.11.39-PM.png) 102 | 103 | 该视图还允许您过滤基于环境和服务器角色的指标。此外,您可以创建“Triggers”或警报,如果指标高于或低于配置的阈值它就给你发送电子邮件。这就允许您设置自动警报来通知您,比如,如果你的一些容器异常关闭以及容器计数低于一定数量。您还可以设置对平均CPU利用率的警报,举例来说,如果你正在运行的容器超过CPU利用率而发热,你会得到一个警告,当然你可以开启更多的主机到你的Docker集群。 104 | 105 | 要创建触发器,请选择顶部菜单的Roles>All Servers,然后选择plugins部分的Docker monitor。然后在屏幕的右侧的Plugin template Administration菜单里选择triggers。您现在应该看到一个选项“Add a Trigger”,它将应用到整个部署。 106 | 107 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-22-at-6.30.25-PM.png) 108 | 109 | 下面是一个触发器的例子,如果部署的容器数量低于3就会发出警报。 110 | 111 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-22-at-6.33.12-PM.png) 112 | 113 | 它的创建是为“所有的服务器”,当然你也可以用不同的角色标记你的主机使用服务器上创建的scoutd.yml文件。使用角色。你可以通过使用不同角色来应用触发器到部署的服务器的一个子集上。例如,你可以设置一个当在你的网络的节点的容器数量低于一定数量时的警报。即使是基于角色的触发器我仍然觉得Scout的警报系统可能做的更好。这是因为许多Docker部署具有相同主机上的多种多样的容器。在这种情况下为特定类型的容器设置触发器将是不可能的由于角色被应用到主机上的所有容器。 114 | 115 | 比起CAdvisor,使用Scout的另一个优点是,它有[大量的插件](https://scoutapp.com/plugin_urls),除了Docker信息他们可以吸收其他有关你的部署的数据。这使得Scout是你的一站式监控系统,而无需对系统的各种资源来安装各种不同的监控系统。 116 | 117 | Scout的一个缺点是,它不显示有关每个主机上像CAdvisor的单独容器的详细信息。这是个问题,如果你在同一台服务器上运行大量的容器。例如,如果你想有一个触发器来提醒您的Web容器的警报,但不是Jenkins容器,这时Scout就无法支持该情况。尽管有这个缺点,Scout还是一个相当有用的工具来监控你的Docker部署。当然这要付出一些代价,每个监控的主机十美元。如果你要运行一个有多台主机的超大部署,这个代价会是个考虑因素。 118 | 119 | ####评分: 120 | 1. 易于部署程度:※※※※ 121 | 2. 信息详细程度:※※ 122 | 3. 集成度:※※※ 123 | 4. 生成警报的能力:※※※ 124 | 5. 监测非Docker的资源的能力:支持 125 | 6. 成本:每个主机$10 126 | 127 | ##Data Dog 128 | 从Scout移步到另一个监控服务,DataDog,它既解决几个Scout的缺点又解除了CAdvisor的局限性。要使用DataDog,先在[https://www.datadoghq.com/](https://www.datadoghq.com/)注册一个DataDog账户。一旦你登录到您的帐户,您将看到支持集成的每种类型的指令列表。从列表中选择Docker,你会得到一个Docker run命令(如下),将其复制到你的主机。该命令需要你的预先设置的API密钥,然后你可以运行该命令。大约45秒钟后您的代理将开始向DataDog系统报告。 129 | 130 | {{{ 131 | docker run -d --privileged --name dd-agent \ 132 | -h `hostname` \ 133 | -v /var/run/docker.sock:/var/run/docker.sock \ 134 | -v /proc/mounts:/host/proc/mounts:ro \ 135 | -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro \ 136 | -e API_KEY=YOUR_API_KEY datadog/docker-dd-agent \ }}} 137 | 138 | 现在,您的容器连接,你可以去在DataDog Web控制台的事件选项卡,看到有关你的集群中的所有事件。所有的容器启动和终止将这一事件流的一部分。 139 | 140 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-21-at-2.56.04-PM.png) 141 | 142 | 您也可以点击Dashboards标签并点击创建仪表板以合计您整个群集的指标。 Datadog收集系统中运行的所有容器中有关CPU使用率、内存以及I/O的指标。此外,您也可以获得运行和停止的容器计数以及Docker的镜像数量。Dashboard视图允许您创建基于任何指标或者设置在整个部署、主机群或者容器镜像的指标的图表。例如下图显示了运行容器的数量并加以镜像类型分类,此刻在我的集群运行了9个Ubuntu:14.04的容器。 143 | 144 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-21-at-2.35.21-PM.png) 145 | 146 | 您还可以通过主机分类同样的数据,如下图所示,7个容器在我的Rancher主机上运行,其余的在我的本地的笔记本电脑。 147 | 148 | ![](http://rancher.com/wp-content/uploads/2015/03/Screen-Shot-2015-03-21-at-3.14.10-PM.png) 149 | 150 | DataDog还支持一种称为Monitors的警报功能。DataDog的一个monitor相当于Scout的一个触发器,并允许您定义各种指标的阈值。 DataDog的警报系统是一个很大的灵活和细致再斥候。下面的例子说明如何指定您关心的Ubuntu容器终止因此你会监视docker.containers.running度量从Ubuntu创建容器:14.04Docker的镜象。 151 | -------------------------------------------------------------------------------- /comparision go web frameworks.md: -------------------------------------------------------------------------------- 1 | A Comparison of Go Web Frameworks 2 | ------------------- 3 | 4 | A few months ago we introduced [Go]() to our system at Square and it's quickly become one of our sharpest tools. We recently evaluated Go web frameworks looking for one that fits us best. 5 | 6 | **TL;DR:** We recommend just using the [net/http]() package in the standard library to start. And if you want help with request routing we recommend looking at [Gorilla]() and [Gocraft/web](). Both Revel and [Martini]() have too much dependency injection and other magic to make us feel comfortable. Gorilla is the most minimal. 7 | 8 | All of the frameworks we looked at are built on top of the net/http package. 9 | 10 | **Routing** is the mechanism by which requests get mapped to a handler function. 11 | Routing is the base functionality for all of these frameworks. Gorilla seems to 12 | have the most flexibility, but they are all roughly equivalent. One important 13 | note is that the implementation of this functionality is very straightforward. 14 | 15 | * **Revel**: Supports parameters and wildcards in the URL. For example, "/hotels/:id" which match the regex ```"/hotels/[^/]+" while "/hotels/*id" ```will match the regex "/hotels/.+". A revel app specifies routes in a configuration file. 16 | * **Martini**: Supports parameters, wildcards and regular expressions in URLs. For example, "/hotels/:id" and "/hotels/**". Routes are tied to a particular HTTP method (e.g. GET, POST, HEAD, etc.) 17 | * **Gocraft/web**: Supports parameters and regular expressions in URLs. For example, "/hotels/:id" and "/hotels/:id:[0-9]+". Similar to the functionality provided by the other frameworks, but gocraft/web has made an effort to be higher performance by structuring routes in a tree instead of as a list. 18 | * **Gorilla**: Supports parameters in the URL where each parameter can have an optional regex that specifies what it matches. For example, "/hotels/{id}" matches the regex ```"/hotels/[^/]+" while "/hotels/{id:[0-9]+}" ```matches the regex "/hotels/[0-9]+". In addition to routing based on URL, Gorilla supports routing based on HTTP method, HTTP headers, URL scheme, query parameters, or using an arbitrary function. Routes are specified programmatically. 19 | 20 | **Data binding** is the mechanism by which request parameters are extracted for use by handlers. 21 | 22 | * **Revel**: Matched parameters are made available in a map[string]string and also via arguments to the handler method. For example, "/hotels/:id" might be mapped to a Show(id int) method and the "id" parameter would automatically be filled in. Revel's use of reflection and injection here feels a bit magical. 23 | * **Martini**: Matched parameters are made available in a map[string]string which is injected into the parameters for the handler method. Martini provides full dependency injections of the handler method parameters and allows specifying global and request level mappings. If you like dependency injection, you'll feel right at home here. 24 | * **Gocraft/web**: Matched parameters are made available in a map[string]string. Unlike gorilla, gocraft/web providers wrappers around http.ResponseWriter and http.Request. The parameters are a field within the web.Request type. 25 | * **Gorilla**: Matched parameters are made available in a map[string]string that is retrieved by calling mux.Vars(request). No dependency injection. No magic. 26 | 27 | **Controllers** or **Context** are used to maintain per-request state. 28 | 29 | * **Revel**: Strong notion of Controller which you are forced to use. Your app controller must embed a *revel.Controller and there are mildly awkward type assertions necessary in middleware to go from a revel.Controller back to your app controller type. 30 | * **Martini**: No notion of Controller or Context, but dependency injection allows you to easily create such concepts yourself. 31 | * **Gocraft/web**: Routing is associated with a user-specified context struct. The context struct is often filled by middleware. 32 | * **Gorilla**: No support for controllers or context. Do it yourself. 33 | **Middleware** is the terminology used for providing common functionality across a set of handlers. A common example of middleware is a logging module. Note that there is nothing magical about the infrastructure to support middleware. Instead of calling a single handler method, the framework is calling a series of methods. 34 | 35 | * **Revel**: Revel calls middleware "interceptors". 36 | * **Martini**: Most of Martini's functionality is captured in middleware. Lots of third-party contributions that are not part of the Martini core. 37 | * **Gocraft/web**: Middleware can be completely general or associated with a context. When associated with a context, middleware can provide common functionality across a set of handlers, such as authenticating a user. 38 | * **Gorilla**: No support for middleware. Do it yourself (see below). 39 | 40 | **Miscellaneous notes** 41 | 42 | * **Revel**: Similar to Rails. Both the most comprehensive and most opinionated framework. Provides routing, data binding, validation, sessions, caching, a testing framework and internationalization. Specifies how directories are structured (separate "models", "controllers" and "views" directories). Heck, you don't even write a main() function as revel generates one for you. 43 | * **Martini**: Inspired by Express (a Node.js web framework) and Sinatra (a Ruby web framework). 44 | * **Gocraft/web**: Written in response to the author's frustration with using Revel. A library, not a framework. 45 | * **Gorilla**: Structured as a series of independent libraries. It is easy to pick the parts you want and ignore the parts you don't. 46 | 47 | **Doing it yourself** 48 | 49 | In our analysis we didn't gravitate toward any full-featured framework. The strength of the standard library lets us build moderately complex apps without any of the above, and of these options, Gorilla is our favorite because it is minimally intrusive. 50 | 51 | Rather than using a package that gives us routing, middleware and controllers bundled together, we prefer small libraries encapsulating useful behavior. It's not bad to forgo the use of middleware if it means you get to keep control over the behavior of your app and you can keep things simple. For example, if you want to authenticate a user you could have a common bit of code at the beginning of every handler to invoke some user authentication method: 52 | 53 | ``` 54 | func myHandler(rw http.ResponseWriter, req *http.Request) { 55 | user := AuthenticateUser(rw, req) 56 | if user == nil { 57 | // AuthenticateUser will have sent an error response through 58 | // the http.ResponseWriter on failure. 59 | return 60 | } 61 | ... 62 | } 63 | ``` 64 | 65 | There's redundancy there, but it's explicit and fits nicely with the Go style of extremely readable code at the cost of slightly more verbosity. -------------------------------------------------------------------------------- /create-the-smallest-possible-docker-container-cn.md: -------------------------------------------------------------------------------- 1 | 当你学习Docker的时候,你很快注意到当你使用预先配置好的容器,你需要下载很多镜像包。 2 | 一个基本的Ubuntu的容器轻轻松松超过200MB,并在有软件安装时它的尺寸会增大。 3 | 在一些情况下,你并不需要Ubuntu容器内的所有的依赖。例如,如果你想运行简单的Go语言编写的Web服务器,它并不需要任何其他工具。 4 | 5 | 我一直在寻找尽可能小的容器入手,并且发现了这个: 6 | ```docker pull scratch 7 | ``` 8 | 9 | 10 | scratch镜像非常完美: 11 | 1. 它优雅,小巧而且快速。 12 | 2. 它不包含任何bug,安全漏洞,延缓的代码或技术债务。 13 | 14 | 这是因为它基本上是空的。除了有点儿被Docker添加的metadata (译注:元数据为描述数据的数据)。事实上,你可以创建这个scratch镜像用以下命令([官方文档上有描述](https://docs.docker.com/articles/baseimages/#creating-a-simple-base-image-using-scratch)): 15 | ```tar cv --files-from /dev/null | docker import - scratch 16 |  ``` 17 | 18 | 这是它,尽可能小的Docker镜像。到此结束! 19 | 20 | ...或许我们还可以来探讨更多的东西。例如,如何使用scratch镜像呢?这又带来了一些挑战。 21 | 22 | 23 | ###为scratch镜像创建内容 24 | 我们可以在一个空的scratch镜像里运行什么?无依赖的可执行文件。你有没有不需要依赖的可执行文件吗? 25 | 26 | 我曾经用Python,Java和JavaScript编写过代码。这些语言/平台需要安装运行环境。最近,我开始研究Go(如果你喜欢话用GoLang)语言平台。看起来Go是静态链接的。所以我尝试编写一个简单的 ‘hello world’ Web服务器,并在scratch容器中运行它。下面是Hello World Web服务器的代码: 27 | 28 | ``` 29 | package main 30 | import ( 31 | "fmt" 32 | "net/http" 33 | ) 34 | func helloHandler(w http.ResponseWriter, r *http.Request) { 35 | fmt.Fprintln(w, "Hello World from Go in minimal Docker container") 36 | } 37 | func main() { 38 | http.HandleFunc("/", helloHandler) 39 | fmt.Println("Started, serving at 8080") 40 | err := http.ListenAndServe(":8080", nil) 41 | if err != nil { 42 | panic("ListenAndServe: " + err.Error()) 43 | } 44 | } 45 | ``` 46 | 47 | 很显然,我不能编译我的web服务器在scratch容器内,因为此容器内没Go编译器。并且,因为我的工作是在Mac上,我也不能编译的Linux二进制。 (其实,交叉编译GoLang源到不同的平台是可能的,但是这是另一篇文章的资料) 48 | 49 | 因此,首先我需要一个Docker容器使用Go的编译器。先从简单的开始: 50 | ```docker run -ti google/golang /bin/bash 51 | ``` 52 | 53 | 在这个容器内,我可以构建Go Web服务器,这是我提交的[GitHub仓库](https://github.com/adriaandejonge/helloworld): 54 | ```go get github.com/adriaandejonge/helloworld 55 | ``` 56 |   57 | ```go get```命令是```go build ```命令的变体,允许其获取并构建远程依赖。 58 | 59 | 你就可以运行生成的可执行文件: 60 | ```$GOPATH/bin/helloworld 61 | ``` 62 |   63 | 这会起作用。但它不是我们想要的。 64 | 65 | 我们需要的hello world Web服务器运行在scratch容器内。所以,实际上,我们需要编写Dockerfile: 66 | ```FROM scratch 67 | ADD bin/helloworld /helloworld 68 | CMD ["/helloworld"] 69 | ``` 70 | 71 | 然后启动。不幸的是,我们使用google/golang容器的方式是没有办法建立这个Dockerfile的。因此首先,我们需要一种方法来从容器内访问Docker。 72 | 73 | ###从容器内调用Docker 74 | 当您使用Docker,你迟早会遇到需要从Docker内部控制Docker。有许多方法可以做到这一点。你可以使用递归和[Docker内运行Docker](https://github.com/jpetazzo/dind)。然而,这似乎过于复杂,并再次导致容量大的容器。 75 | 您还可以用一些额外的命令参数来提供访问外部Docker给实例: 76 | ```docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) -ti google/golang /bin/bash 77 | ``` 78 | 79 | 在讲到下一步之前,请重新运行Go编译器,因为重新启动一个容器Docker会忘记之前的编译内容: 80 | ```go get github.com/adriaandejonge/helloworld 81 | ``` 82 |   83 | 当启动容器时, 84 | ```-v``` 标志创建一个卷在Docker容器内,并允许您提供从Docker机上的文件作为输入。 85 | ```/var/run/docker.sock```是Unix套接字,允许访问Docker服务器。 86 | ```$(which docker)```是一种提供Docker可执行文件的路径给容器的方法。但是,使用该命令要当心当您使用boot2docker在Apple上时:如果Docker可执行文件被安装在不同的路径上相对于安装在boot2docker的虚拟机,这将会导致不匹配错误:它将是boot2docker虚拟服务器内的可执行文件被导入容器内。所以,你可能要替换```$(which docker)```为```/usr/local/bin/docker ```。同样,如果你运行在不同的系统,``` /var/run/docker.sock ```有一个不同的位置,你需要相应地调整。 87 | 88 | 现在,你可以在 google/golang容器内使用在$GOPATH路径下的Dockerfile,例子中,它指向/gopath 。 89 | 其实,我已经提交Dockerfile到GitHub上。因此,你可以从复制它在Go build目录里,命令如下: 90 | ```cp $GOPATH/src/github.com/adriaandejonge/helloworld/Dockerfile $GOPATH 91 | ```  92 | 93 | 编译好的二进制文件位于$GOPATH/bin 目录下,当构建Dockerfile时它不可能从父目录中include文件。所以在复制后,下一步是: 94 | ```docker build -t adejonge/helloworld $GOPATH 95 | ``` 96 |   97 | 如果一切顺利,那么,Docker会有类似输出: 98 | > Successfully built 6ff3fd5a381d 99 | 100 | 然后您可以运行容器: 101 | ```docker run -ti --name hellobroken adejonge/helloworld 102 | ``` 103 |   104 | 105 | 但不幸的是,Docker会输出类似于: 106 | > 2014/07/02 17:06:48 no such file or directory 107 | 108 | 那么到底是怎么回事?我们的scratch容器内已经有静态链接的可执行文件。难道我们犯了一个错误? 109 | 110 | 事实证明,Go不是静态链接库的。或者至少不是所有的库。在Linux下,我们可以看到动态链接库用以下命令: 111 | ```ldd $GOPATH/bin/helloworld 112 | ``` 113 | 114 | 其中输入类似以下内容: 115 | >linux-vdso.so.1 => (0x00007fff039fe000) 116 | libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f61df30f000) 117 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61def84000) 118 | /lib64/ld-linux-x86-64.so.2 (0x00007f61df530000) 119 |   120 | 所以,在我们才可以运行的Hello World Web服务器之前,我们需要告诉Go编译器真正的做静态链接。 121 | 122 | 123 | ###Go语言创建静态链接的可执行文件 124 | 为了创建静态链接的可执行文件,我们需要使用cgo编译器,而不是Go编译器。命令如下: 125 | ```CGO_ENABLED=0 go get -a -ldflags '-s' github.com/adriaandejonge/helloworld 126 | ``` 127 |   128 | ```CGO_ENABLED``` 环境变量表示使用cgo编译器,而不是Go编译器。 129 | ```-a```标志表示要重建所有的依赖。否则,还是以动态链接依赖为结果。 130 | ```-ldflags``` ```-s```表示一个不错的额外标志。它缩减生成的可执行文件约50%的大小。没有cgo编译器你也可以这样做。尺寸减小是除去了调试信息的结果。 131 | 132 | 只是确保一下,重新运行ldd命令: 133 | ```ldd $GOPATH/bin/helloworld 134 |  ``` 135 | 136 | 现在应该有类似输出: 137 | > not a dynamic executable 138 | 139 | 然后重新运行用scratch镜像构建Docker容器那一步: 140 | ```docker build -t adejonge/helloworld $GOPATH 141 | ``` 142 |   143 | 如果一切顺利,Docker会有类似输出: 144 | > Successfully built 6ff3fd5a381d 145 | 146 | 接着运行容器: 147 | ```docker run -ti --name helloworld adejonge/helloworld 148 | ``` 149 | 150 | 而这个时候会输出: 151 | > Started, serving at 8080 152 | 153 | 154 | 目前为止,有许多步骤,会有很多错误的余地。让我们退出google/golang 容器: 155 | ``` 156 | exit 157 | ``` 158 | 159 | 您可以检查容器和镜像的存在或不存在: 160 | ```docker ps -a 161 | docker images -a 162 | ``` 163 | 164 | 并且您可以清理Docker: 165 | ```docker rm -f hello world 166 | docker rmi -f adejonge/helloworld 167 | ``` 168 | 169 | ###创建Docker容器的Docker容器 170 | 171 | 我们花了这么多的步骤:记录在Dockerfile里,并运行成功: 172 | ```FROM google/golang 173 | RUN CGO_ENABLED=0 go get -a -ldflags '-s' github.com/adriaandejonge/helloworld 174 | RUN cp /gopath/src/github.com/adriaandejonge/helloworld/Dockerfile /gopath 175 | CMD docker build -t adejonge/helloworld gopath 176 |  ``` 177 | 178 | 我提交了这个Dockerfile到另一个[GitHub库](https://github.com/adriaandejonge/hellobuild)。它可以用这个命令构建: 179 | ```docker build -t adejonge/hellobuild github.com/adriaandejonge/hellobuild 180 | ```  181 | 182 | ```-t```表示镜像的标签名为adejonge/hellobuild和隐式标签名为latest。这些名称为以后删除镜像变得容易。 183 | 184 | 接下来,你可以创建容器用刚才提供的标签: 185 | ```docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) -ti --name hellobuild adejonge/hellobuild 186 |  ``` 187 | 188 | 提供所述```--name hellobuild``` 标志可以更容易在运行之后删除容器。事实上,你现在就可以这样做,因为在运行此命令后,你已经创建了adejonge/helloworld的镜像: 189 | ```docker rm -f hellobuild 190 | docker rmi -f adejonge/hellobuild  191 | ``` 192 | 193 | 现在你可以运行新的helloworld容器: 194 | ```docker run -ti --name helloworld adejonge/helloworld 195 | ``` 196 | 197 | 因为所有这些步骤都出自同一命令行运行,而无需在Docker容器内打开bash shell,你可以将这些步骤添加一个bash脚本,并自动运行。 198 | 为了您的方便,我已经提交了这些的bash脚本到[GitHub库](https://github.com/adriaandejonge/hellobuild/tree/master/scripts)。 199 | 200 | 另外,如果你想尝试在尽可能小的Docker容器里运行Hello World Web服务器,而不遵循这个博客中描述的步骤,你也可以用我提交到[Docker Hub库的镜像](https://registry.hub.docker.com/u/adejonge/helloworld/): 201 | ```docker pull adejonge/helloworld 202 | ``` 203 | 204 | ```docker images -a```你可以看到大小为3.6MB。当然,你可以把它缩减的更小,如果你创建一个可执行比我编写的Go Web服务器要小。用C语言或汇编你可以这样做。但是,你永远不能使它比scratch镜像更小。 -------------------------------------------------------------------------------- /demo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER Nate Slater 3 | RUN apt-get update && apt-get install -y curl wget default-jre git 4 | RUN adduser --home /home/sinatra --disabled-password --gecos '' sinatra 5 | RUN adduser sinatra sudo 6 | RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers 7 | USER sinatra 8 | RUN curl -sSL https://get.rvm.io | bash -s stable 9 | RUN /bin/bash -l -c "source /home/sinatra/.rvm/scripts/rvm" 10 | RUN /bin/bash -l -c "rvm install 2.1.2" 11 | RUN /bin/bash -l -c "gem install sinatra" 12 | RUN /bin/bash -l -c "gem install thin" 13 | RUN /bin/bash -l -c "gem install aws-sdk" 14 | RUN wget -O /home/sinatra/dynamodb_local.tar.gz https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_2013-12-12.tar.gz 15 | RUN tar -C /home/sinatra -xvzf /home/sinatra/dynamodb_local.tar.gz -------------------------------------------------------------------------------- /difference-between-docker-and-vgrant-cn.md: -------------------------------------------------------------------------------- 1 | **Vagrant**和**Docker**是两只不同的野兽。 2 | 3 | **Docker**是一个由shell层和management层两部分组成的,用来构建并运行基于lxc的虚拟Linux容器。 4 | 5 | Docker的伟大在于;它是轻量级的(因为它依赖于共享内核的Linux容器),[以及与它的分布无关]。虽然所有实例之间的内核共享(但与主机以及互相之间都是隔离的),不同实例的用户空间可以基于不同的Linux发行版本。 6 | 7 | **Vagrant**则是一个奇妙的工具,它使用puppet和/或chef管理,用来自动调配多个虚拟机,并且每个都有他们自己的配置。对于它的虚拟化来说,它可以使用不同的供应商。原来默认的提供商是VirtualBox,但它现在支持更多了,包括VMware fusion,甚至amazon-ec2。 8 | 9 | 有趣的是,Vagrant现在也具有一个Docker提供商,因此您可以用vagrant来管理Docker的构建和部署。 10 | 11 | Docker,并不限制它的灵活性 - “一切都是镜像”,你可以创建变体镜像和全栈镜像,其中每一个添加功能到前一个。管理这些会成为一个挑战。 12 | 13 | Vagrant也有类似的挑战,因为虚拟机可能会过时,有时虚拟机可能很难找到以及更新。有一些工具比如packer和以前的veewee可以用来帮助你构建所谓的'基础'虚拟机。 14 | 15 | 我相信这些工具可以很好地在一起工作,我觉得这样的组合会在你计划的筹码中或者在你要做整个部件更换测试中,甚至是基础操作系统中大放异彩。 16 | 17 | 假设你有一个基于Centos的应用程序,并且你要切换到Ubuntu或是其他方式。假设你想完全地升级你的操作系统。 18 | 19 | 我总是说在开发测试与分级中,对于当前生产环境(包括配置)以及在任何潜在的替代生产环境中你必须要测试你的产品。您是否正在计划一个安全更新?你想更新或是切换到Java吗? 20 | 21 | 这是Vagrant和Docker出彩的地方。我希望Docker帮助您加快对多个操作环境的测试。 22 | 23 | Docker是否一个部署应用程序到生产生产环境中的有用工具呢?这是它常见的使用情况 - 那么它可能是。然而,配置文件的本质变化,尤其是那些必须通过网络进行协调的地方,可以更好地用一个知道在网络中的其他组件的工具。 -------------------------------------------------------------------------------- /difference-between-docker-and-vs-cn.md: -------------------------------------------------------------------------------- 1 | **VirtualBox**,是创建硬件虚拟化的软件。通常情况下,一个操作系统运行在硬件上,其中硬件和操作系统之间的通信是通过移动数据到内存地址,然后发出指令来通知可使用该数据的硬件(或者是数据在被读取时)。 在VirtualBox(或其他虚拟机)设置的环境中,那些内存地址实际上是虚拟机软件自身的内存区域,并且那些指令是由虚拟机而不是直接由底层的CPU解释的。实际结果是,你在VirtualBox中运行一个操作系统,对于这个操作系统来说,VirtualBox程序看起来像一台完整计算机,硬件以及所有配件都有。实际上它不知道自己是在另一个程序中运行的。 2 | 3 | **Docker**,则是不进行硬件的虚拟化。相反,它的作用是创建一个文件系统,使其看起来像一个普通的Linux文件系统,并且运行应用程序在一个所有文件和资源都在文件系统内的锁定环境中。事实上,该应用程序的容器并不模仿任何硬件,应用程序仍然在硬件上运行,它只是隔离了应用程序并允许您可以运行该应用程序跟特定的并且完全不是主机操作系统的软件和第三方库合作。这意味着,在启动或停止Docker应用程序时几乎没有开销,因为它们不需要预先分配的内存和磁盘空间等等。因此Docker容器很容易设置或者拆除。此外,容器在假装需要系统中各种硬件组件上运行软件的时候并不浪费任何开销 - 它是直接使用硬件的。 4 | 5 | **VirtualBox虚拟化硬件,Docker虚拟化操作系统。([原文](http://www.quora.com/What-is-the-difference-between-Docker-and-VirtualBox))** -------------------------------------------------------------------------------- /docker-indepth-volumes-cn.md: -------------------------------------------------------------------------------- 1 | ##深入Docker之Volumes 2 | 3 | 【编者的话】这篇文章是[深入理解Docker Volume](http://dockerone.com/article/128)的延伸阅读篇,作者通过对比Dockerfile中的VOLUME指令跟命令行docker run 中```-v```标志深入分析了Volume的原理以及与容器之间的联系。 4 | 5 | 人们使用Docker最常见的障碍之一,还有我看大量的Docker支持渠道也确实很容易出现该问题,那就是Volumes的使用。 6 | 7 | 因此,让我们来仔细看一下Docker Volume是如何工作的。 8 | 9 | 首先,让我们来驱散第一个最常见的误解: 10 | 11 | Docker Volumes是为了持久性。 12 | 13 | 这可能来自于容器不是持久的想法,这样确实是不对的。容器的持久直到您删除他们,并且你只能这样做: 14 | ```docker rm my_container 15 | ``` 16 | 17 | 如果您没有键入此命令,那么你的容器仍然存在并将继续存在,它可以启动、停止等。如果你没有看到你的容器,你应该运行此命令: 18 | ```docker ps -a``` 19 | 20 | ```docker ps```永远只显示正在运行的容器,但是一个容器可以是停止状态,在这种情况下,上面的命令会显示你所有的容器无论状态如何。```docker run ...```其实是一个多命令集合,它会创建一个新的容器,然后启动它。 21 | 22 | 因此,再次声明:Volume不是为了持久。 23 | 24 | ###什么是Volume 25 | 26 | Volumes使创建它们的容器使用寿命与在它们中储存数据的寿命解耦。这使得它你可以```docker rm my_container```后你的数据不会被删除。 27 | 28 | Volume可以用以下两种方式创建: 29 | 30 | - 在Dockerfile中指定```VOLUME /some/dir``` 31 | - 当执行```docker run -v /some/dir```命令来指定 32 | 33 | 无论哪种方式,这两样东西都做了同样的事情。它告诉Docker在主机上创建一个目录(默认情况下是```/var/lib/docker```),然后将其挂载到您指定的路径(例子中是:```/some/dir```)。当您删除使用该Volume的容器,该Volume本身将一直存在下去。 34 | 35 | 如果在容器中不存在指定的路径,那么此目录将被自动创建。 36 | 37 | 你能告诉Docker同时删除容器和其Volume: 38 | ```docker rm -v my_container 39 | ``` 40 | 41 | 有时候,你的主机已经有了要在容器中使用的目录,CLI(命令行界面)多了一种选择来指定这些: 42 | ```docker run -v /host/path:/some/path ... 43 | ``` 44 | 45 | 这明确地告诉Docker使用指定的主机路径来代替Docker自己创建的根路径并挂载到容器内指定的路径(以上例子为:```/some/path```)。值得注意,这样做也可以是一个文件来代替目录。在Docker术语中这通常被称为bind-mounts(虽然技术层面上是这样讲的,但是实际的感官是所有的Volumes都是bind-mounts的)。如果主机上的路径不存在,目录将自动在给定的路径中创建。 46 | 47 | 对待Bind-mount Volumes跟一个“正常”的Volume有点点不同,它的特点是不会修改主机上那些并非Docker自身创建的东西: 48 | 49 | 1. 一个“正常”的Volume,Docker会自动复制在指定的Volume路径的数据(如上边示例:```/some/path```)到由Docker创建新的目录下,如果是“bind-mount” Volume就不会发生这种情况。 50 | 2. 当你执行```docker rm -v my_container```命令给“bind-mount” Volume的容器,“bind-mount” Volumes不会被删除。 51 | 52 | 容器也可以与其他容器共享Volumes。 53 | ```docker run --name my_container -v /some/path ... 54 | docker run --volumes-from my_container --name my_container2 ...``` 55 | 56 | 上面的命令将告诉Docker从第一个容器挂载相同的Volumes到第二个容器。它有效地共享数据在两个容器之间。 57 | 58 | 如果你执行```docker rm -v my_container```命令,而上方的第二容器依然存在,Volumes不会被删除,而且它永远不会被删除除非你执行```docker rm -v my_container2```删除第二个容器,。 59 | 60 | ###Dockerfiles里的VOLUME 61 | 正如前面提到的,Dockerfile中的VOLUME声明中做同样的事情类似```docker run```命令里的```-v```标志(除了你不能在Dockerfile指定主机路径)。它只是恰巧发生了,也正因为如此,构建镜像时可以得到惊奇的效果。 62 | 63 | 在Dockerfile中的每个命令创建一个新的用于运行指定命令的容器,并将容器提交回镜像,每一步都是在前一步的基础上构建。因此在Dockerfile中```ENV FOO=bar```等同于: 64 | ```cid=$(docker run -e FOO=bar ) 65 | docker commit $cid 66 | ``` 67 | 68 | 下面让我们来看看这个Dockerfile的例子发生了什么: 69 | ```FROM debian:jessie 70 | VOLUME /foo/bar 71 | RUN touch /foo/bar/baz 72 | ``` 73 | 74 | ```docker build -t my_debian . 75 | ``` 76 | 77 | 我们期待的是Docker创建名为my_debian并且Volume是``` /foo/bar```的镜像,以及在```/foo/bar/baz```下添加了一个空文件,但是让我们看看等同的CLI命令行实际上做了哪些: 78 | ```cid=$(docker run -v /foo/bar debian:jessie) 79 | image_id=$(docker commit $cid) 80 | cid=$(docker run $image_id touch /foo/bar/baz) 81 | docker commit $(cid) my_debian 82 | ``` 83 | 84 | 它并不是确切地发生了这些,但是非常类似。 85 | 86 | 那么,这里发生的是在```/foo/bar```里的任何东西存在之前,Volume就被创建,因此我们每次从这个镜像启动一个容器,会有一个空的```/foo/bar```目录。它之所以发生如前所述,Dockerfile中每个命令都是创建一个新容器。这意味着,同时也创建了一个新的Volume。由于举例Dockerfile中是先指定Volume的,当执行```touch /foo/bar/baz```命令的容器创建时,一个Volume被挂载到了```/foo/bar```,然后```baz```才能被写入此Volume,而不是实际的容器或镜像的文件系统内。 87 | 88 | 所以,牢记Dockerfile中```VOLUME```声明的位置,因为它在你的镜象内创建了不可改变的目录。 89 | 90 | ```docker cp```([#8509](https://github.com/docker/docker/pull/8509)),```docker commit```和```docker export```还不支持Volumes(在文章截稿时)。 91 | 92 | 目前,在容器的创建/销毁期间来管理Volumes(创建/销毁)是唯一的方式,这有点古怪,因为Volumes是为了分离容器内的数据与容器的生命周期。Docker团队正在处理,但尚未合并([#8484](https://github.com/docker/docker/pull/8484))。 93 | 94 | 如果您想了解Docker Volume的更多功能,[请这边走](https://github.com/cpuguy83/docker-volumes) 95 | 96 | **原文链接:[Docker In-depth: Volumes](http://container42.com/2014/11/03/docker-indepth-volumes/)(翻译:[田浩](https://github.com/llitfkitfk))** -------------------------------------------------------------------------------- /docker-really-solve-what-problem-cn.md: -------------------------------------------------------------------------------- 1 | docker到底解决了什么问题 2 | 3 | 我喜欢。它易于使用而且速度快,就是这么闪亮。如此闪亮得每天我们都能发想新奇可能的应用程序。然而,它究竟解决了什么的实际问题?稳定的环境?配置管理还是有效的虚拟化。 4 | 5 | 我认为它解决了别的东西。 6 | 7 | 在过去,我曾经工作的公司提供(和购买)B2B的服务。从这个角度展望,它总是令我感到诧异B2B服务发展远远落后于科技的发展:超高速宽带网络,拓展云,分布式数据库等。 8 | 9 | 当然也有一些例外:主要是在广告领域(Google AdWords,Facebook Ads)、分析(Google Analytics)和流媒体(Netflix)的B2B的服务。但是,大部分的B2B市场发展速度仍然缓慢。 10 | 11 | 为什么会这样呢?我认为,商业要比最终用户更期望高质量的服务。所以B2B服务市场很难成长。实际上只有极少数公司能够赢得客户的信赖。我刚刚提到了几个,也就是谷歌、Facebook和Netflix。 12 | 13 | 然而,如果你是一个客户,还有其他人你会相信那就是你自己。因此,如果你能从一个任何地方你都能部署到你的数据中心或者云里的黑盒子里得到你需要交付的服务 ?你可以决定给予黑盒子网络延迟和资源的多少(计算能力、内存等)。即使你不知道该服务实际是如何工作的,你就已经很相信它了,不是吗? 14 | 15 | Docker是一种完美地解决了如何把服务打包的技术。使用Docker,商业客户可以紧密地部署他们的服务与所需的应用程序。当在高频地交易中使用同样的模型,其网络延迟也被降到最低。 16 | 17 | 从技术的角度来看,时间刚刚好:aaS的家庭IAAS(基础设置即服务),PaaS(平台即服务),SaaS(服务即服务)的解决方案足够成熟;Docker的下一步棋可能是:Service as a Product(服务即产品)。但是,我们仍然缺少一些东西。 18 | 19 | - 市场 - 让买家能找到他们所需要的容器服务。 20 | - 产权保护机制: 21 | - 许可的基础设施 - 当他们在客户云里的服务需求增长时,允许供应商受益(联合许可证服务器?)。 22 | - 知识产权保护(加密的容器?)。 23 | - 容器的编配 - 来管理复杂的多容器服务。 24 | 25 | 不过,我很乐观。 我认为B2B的空间将会改变,因为Docker开启了一个易于部署的B2B服务的新标准市场。我希望很快就能看到这种情况发生。 -------------------------------------------------------------------------------- /docker-security-tuning-cn.md: -------------------------------------------------------------------------------- 1 | #Docker安全调整 2 | 3 | 【编者的话】 4 | 5 | 从发表这个有关Docker安全系列的前两篇文章以来已经有一段时间没更新了。这边文章会更新有关Docker的最新添加的信息并且涵盖了那些正准备合并到上游Docker的新的功能。 6 | 7 | ##调整能力 8 | 9 | 在前面的文章中,我讲述了基于Linux的功能的容器分离。 10 | 11 | Linux的功能允许你分离根用户权力到一些更小的特权群。目前,默认情况下Docker容器只有以下功能。 12 | 13 | ``` 14 | CHOWN, DAC_OVERRIDE, FSETID, FOWNER, MKNOD, NET_RAW, 15 | SETGID, SETUID, SETFCAP, SETPCAP, NET_BIND_SERVICE, 16 | SYS_CHROOT, KILL, AUDIT_WRITE 17 | ``` 18 | 19 | 在某些情况下,你可能要调整此列表,例如,如果你构建一个运行`ntpd`或`crony`的容器,就需要能够修改主机的系统时间。该容器将无法运行,因为它需要`CAP_SYS_TIME`。在旧版本的Docker中容器必须在`--privileged`模式 - 关闭所有的安全策略下运行。 20 | 21 | 在Docker1.3版本中添加了`--cap-add`和`--cap-drop`。现在为了运行`ntpd`容器,你可以只需运行: 22 | ``` 23 | docker run -d --cap-add SYS_TIME ntpd 24 | ``` 25 | 26 | 其中只添加了`SYS_TIME`功能到您的容器。 27 | 28 | 另一个例子是,如果你的容器没有改变任何进程的`UID/GID`,你可以从你的容器中删除这些功能,使其更加安全。 29 | 30 | ``` 31 | docker run --cap-drop SETUID --cap-drop SETGID --cap-drop FOWNER fedora /bin/sh 32 | 33 | # pscap | grep 2912 34 | 5417 2912 root sh chown, dac_override, fsetid, kill, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap 35 | ``` 36 | 37 | 或者你可以放弃所有功能并一一添加。 38 | 39 | ``` 40 | docker run --cap-drop ALL --cap-add SYS_TIME ntpd /bin/sh 41 | 42 | # pscap | grep 2382 43 | 5417 2382 root sh sys_time 44 | ``` 45 | 46 | ##调整SELinux的标签 47 | 48 | 类似于调整功能,我们已经加入了调整SELinux的标签的能力。 49 | 50 | 如果你已经看了[SELinux coloring book](http://opensource.com/business/13/11/selinux-policy-guide)(译注:有关强制执行SELinux政策的文章,此文图文并茂、易于理解),你知道,我们可以通过类型和`MCS/MLS`级别来分离进程。我们使用类型用以保护主机来自容器的干扰。但是,我们也可以调节类型来控制允许进入和离开容器的网络端口。目前,我们都是以`svirt_net_lxc_t`运行所有容器。这种类型允许监听并连接所有的网络端口。我们可以通过调整SELinux的类型标签很好地设定容器的安全性。 51 | 52 | 与常规的SELinux和Apache httpd,在默认情况下我们只允许Apache进程来监听Apache的端口(http_port_t)。 53 | 54 | ``` 55 | # sudo sepolicy network -t http_port_t 56 | 57 | http_port_t: tcp: 80,81,443,488,8008,8009,8443,9000 58 | 59 | ``` 60 | 61 | 我们也阻止所有传出端口的连接。这可以帮助我们锁定了Apache进程,即便黑客像ShellShock一样通过安全漏洞破坏了应用程序,我们可以停止即将成为一个垃圾邮件僵尸的应用程序或者允许进程攻击其它系统。就像加州旅馆,“你可以随时进来,但你永远无法离开。” 62 | 63 | 随着容器,可是如果你运行一个Apache服务器应用程序的容器,该应用程序被攻击,Apache进程能够通过网络连接到任何网络端口、成为垃圾邮件僵尸或者攻击其他主机/容器。 64 | 65 | 使用SELinux创建一个新的策略类型来运行你的容器相当的简单。首先,你可以创建一个SELinux TE(类型强制执行)文件。 66 | 67 | ``` 68 | # cat > docker_apache.te << _EOF 69 | 70 | policy_module(docker_apache,1.0) 71 | 72 | # This template interface creates the docker_apache_t type as a 73 | # type which can be run as a docker container. The template 74 | # gives the domain the least privileges required to run. 75 | virt_sandbox_domain_template(docker_apache) 76 | 77 | # I know that the apache daemon within the container will require 78 | # some capabilities to run. Luckily I already have policy for 79 | # Apache and I can query SELinux for the capabilities. 80 | # sesearch -AC -s httpd_t -c capability 81 | allow docker_apache_t self: capability { chown dac_override kill setgid setuid net_bind_service sys_chroot sys_nice sys_tty_config } ; 82 | 83 | # These are the rules required to allow the container to listen 84 | # to Apache ports on the network. 85 | 86 | allow docker_apache_t self:tcp_socket create_stream_socket_perms; 87 | allow docker_apache_t self:udp_socket create_socket_perms; 88 | corenet_tcp_bind_all_nodes(docker_apache_t) 89 | corenet_tcp_bind_http_port(docker_apache_t) 90 | corenet_udp_bind_all_nodes(docker_apache_t) 91 | corenet_udp_bind_http_port(docker_apache_t) 92 | 93 | # Apache needs to resolve names against a DNS server 94 | sysnet_dns_name_resolve(docker_apache_t) 95 | 96 | # Permissive domains allow processes to not be blocked by SELinux 97 | # While developing and testing your policy you probably want to 98 | # run the container in permissive mode. 99 | # You want to remove this rule, when you are confident in the 100 | # policy. 101 | permissive docker_apache_t; 102 | _EOF 103 | 104 | # make -f /usr/share/selinux/devel/Makefile docker_apache.pp 105 | # semodule -i docker_apache.pp 106 | ``` 107 | 108 | 现在使用新类型运行容器: 109 | 110 | ``` 111 | # docker run -d --security-opt type:docker_apache_t httpd 112 | 113 | ``` 114 | 115 | 现在,对比正常的容器,这个容器有更为严格的SELinux安全性。注意,你可能会需要查看审计日志来看看你的应用程序是否需要额外的SELinux准许规则。 116 | 117 | 你可以通过使用`audit2allow`命令来添加规则到现有`.te`文件,重新编译并安装如下规则。 118 | 119 | ``` 120 | # grep docker_apache_t /var/log/audit/audit.log | audit2allow >> docker_apache.te 121 | # make -f /usr/share/selinux/devel/Makefile docker_apache.pp 122 | # semodule -i docker_apache.pp 123 | 124 | ``` 125 | 126 | ##多极次安全模式 127 | 128 | 目前,我们使用`MCS`分离来确保容器不被其它容器干扰或交互,除非它是通过网络连接。某些政府系统需要不同类型的MLS(多极安全)政策。具有MLS时,你可以基于看到的数据级别来标记进程。 MLS说如果你的容器要处理绝密数据,那么它应该在绝密的地方运行。我们已经添加允许管理员设置容器在特定级别运行的Docker选项,这些应该满足MLS系统的需求。 129 | 130 | ``` 131 | docker run -d --security-opt label:level:TopSecret --security-opt label:type:docker_apache_t httpd 132 | ``` 133 | 134 | 这个命令将开启Docker容器的两个交替类型与级别,并且可以阻止容器使用不是相同标签的数据。不过在这一点上还没有通过认证,但我们愿意帮助第三方为MLS用户构建解决方案。 135 | 136 | ##调整命名空间 137 | 138 | 在其他有关安全的对话中,我已经讨论了命名空间可以被认为是一种安全机制,因为其排除了一个进程无法看到系统(PID命名空间)上的其他进程的能力。网络命名空间可以排除从命名空间中能看的到其他网络的能力。 IPC(内部进程间通信)命名空间具有阻断容器使用其它容器的IPC的能力。 139 | 140 | Docker现在已经放宽这些限制。您可以用容器来共享主机的命名空间: 141 | 142 | --pid=主机让容器共享主机的PID命名空间 143 | --net=主机让容器共享主机的主机空间 144 | --ipc=主机让容器共享主机的IPC空间 145 | 146 | 需要注意的是,既然与主机共享了PID或IPC的命名空间,需要我们禁用SELinux分离以便让他们的工作。 147 | 148 | ``` 149 | docker run -ti --pid=host --net=host --ipc=host rhel7 /bin/sh 150 | 151 | ``` 152 | 153 | 你很可能会阅读这篇有关这些的额外信息的文章-[Super Privileged Containers](http://developerblog.redhat.com/2014/11/06/introducing-a-super-privileged-container-concept/)。 154 | 155 | **原文链接:[Tuning Docker with the newest security enhancements](https://opensource.com/business/15/3/docker-security-tuning) (翻译:[田浩浩](https://github.com/llitfkitfk))** 156 | 157 | =========================== 158 | **译者介绍** 159 | 田浩浩,[USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 -------------------------------------------------------------------------------- /docker-tutorial-series-1-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 1 Introduction 2 | 3 | Docker,容器技术的新趋势,其轻巧,便携深得人心,“只需一次构建+配置,就可以随处运行”的功能。 4 | 这是Flux7的Docker教程系列的第一部分。随着我们一起前进,我们将学习和理解Docker有什么不同,以及如何物尽其用。 5 | 6 | 让我们一起来学习Docker。 7 | 8 | 这部分涉及Docker的基础知识:它的特征,理念以及如何安装,让你与Docker同在。 9 | 10 | ###Docker 特征 11 | 12 | Docker有不少有趣的功能,通过此教程系列你会更好地理解他们。 13 | 14 | Docker特性主要包括以下几点: 15 | - 速度飞快以及优雅的隔离框架 16 | - 物美价来 17 | - CPU/内存的低消耗 18 | - 快速开/关机 19 | - 跨云计算基础架构 20 | 21 | ###Docker 组件与要素 22 | 23 | Docker有三个组件和三个基本要素: 24 | 25 | 组件: 26 | 27 | - ```Docker Client``` 是用户界面,其允许用户与```Docker Daemon```之间通信。 28 | 29 | - ```Docker Daemon```是服务主机的应答请求。 30 | 31 | - ```Docker Index```是中央registry,允许带有公有与私有访问权限Docker container images备份。 32 | 33 | 要素: 34 | 35 | - ```Docker Containers```是负责实际应用程序的运行,而且包括操作系统,用户添加的文件以及元数据。 36 | 37 | - ```Docker Images```来帮助开启Docker containers的只读模板。 38 | 39 | - ```DockerFile``` 是说明如何自动创建```Docker Image```的文件。 40 | 41 | ![](http://cdn2.hubspot.net/hub/411552/file-1222264954-png/blog-files/image-1.png?t=1419682672898) 42 | 43 | 在讨论Docker组件和要素如何交互之前,让我们来谈谈是什么构成了Docker的支柱。 44 | 45 | Docker使用以下操作系统的功能来提高容器技术效率: 46 | 47 | - ```Namespaces``` 充当隔离的第一级。确保一个容器中运行一个进程而且不能看到或影响容器外其他进程。 48 | 49 | - ```Control Groups``` LXC的重要组成部分,具有资源核算与限制的关键功能。 50 | 51 | - ```UnionFS``` (文件系统) 作为容器的构建块。为了Docker的轻量级以及速度快的特点,它创建层与用户。 52 | 53 | 54 | ###如何把他们放在一起 55 | 56 | 运行任何应用程序,有两个基本步骤: 57 | 58 | 1. 构建一个镜像。 59 | 2. 运行容器。 60 | 61 | 这些步骤的是从```Docker Client```的命令开始的。```Docker Client```使用的是docker二进制文件。在基础层面上,```Docker Client```命令```Docker Daemon```根据需要创建的镜象和要需要在容器内运行的命令。 62 | 此后创建的镜像的信号由Daemon捕获,这些步骤必须遵循: 63 | 64 | ####第1步:构建镜像 65 | 66 | 如前面所述,```Docker Image```构建容器的只读模板。一个镜像持有所需的所有信息来引导一个容器,包括运行哪些进程和配置数据。 67 | 每个镜像开始于一个基本镜像,并且模板是通过使用存储在DockerFile说明创建的。对于每个指令,一个新的层将会在镜像上创建。 68 | 69 | 一旦镜像被创建,可以将它们推送到中央registry:```Docker Index```,以供他人使用。然而,```Docker Index```为镜像提供了两个级别的访问权限:公有和私有访问。您可以储存镜像在私有仓库。Docker官网有私有仓库的套餐可以参考。总之,公有库是可搜索和可重复使用的,而私有库只能给拥有权限的成员访问。```Docker Client```可用于```Docker Index```内的镜像搜索。 70 | 71 | ####第2步:运行容器 72 | 73 | 运行容器源于我们在第一步中创建的镜像。当一个容器被启动后,一个读写层会被添加到镜像的顶层。经过合适的网络和IP地址分配,最终所期望的应用程序就可以在容器内运行了。 74 | 75 | 如果你还是有点不解,坐稳了,然后看这个实际的例子,在未来几周内我们将与您分享本教程系列。 76 | 77 | 目前为止一个基本的了解足够了。因此,让我们继续前进,安装Docker! 78 | 79 | ###安装Docker: 快速指南 80 | 81 | 82 | 下面让我们来讨论如何在Ubuntu 12.04 LTS安装Docker: 83 | (译注:在centos 6.5安装可以参考[这里](https://github.com/llitfkitfk/docker-tutorial-cn)) 84 | 85 | 1. 检查APT系统的HTTPS兼容性。 86 | 安装apt-transport-https 包,如果usr/lib//apt/methods/https文件不存在 87 | 88 | 2. 在本地钥匙链添加Docker Repository key。 89 | ```Repository key: hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 90 | ``` 91 | 92 | 3. 添加Docker Repository到APT源列表。 93 | ```sudo sh -c "echo deb https://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list" 94 | ``` 95 | 96 | 4. 安装lxc-Docker包。 97 | ```sudo apt-get update 98 | sudo apt-get install lxc-docker 99 | ``` 100 | 101 | 5. 验证安装。 102 | ```sudo docker run -i -t ubuntu /bin/bash 103 | ``` 104 | 105 | 这样介绍就完成了。关注更多教程系列尽在[www.dockerone.com](http://dockerone.com/topic/Docker%20Tutorial) 106 | 107 | -------------------------------------------------------------------------------- /docker-tutorial-series-2-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 2 Commands 2 | 3 | 在[Docker教程系列的第一部分](),我们了解了Docker的基础知识。我们研究它是如何工作以及如何安装。在这篇文章中,我们现在学习的15个Docker命令,实践:他们如何被使用以及他们做了什么。 4 | 5 | 首先,让我们通过下边的命令来检查Docker安装是否正确: 6 | ```docker info 7 | ``` 8 | 9 | 如果没有找到此命令,则表示Docker没有正确安装。一个正确安装的输出会显示类似以下内容: 10 | 11 | ![](http://cdn2.hubspot.net/hub/411552/file-1222265239-png/blog-files/docker-info.png?t=1419682672898) 12 | 13 | 到这一步Docker里还没有镜像或是容器。所以,让我们通过命令来拉取一个预建的镜像: 14 | ```sudo docker pull busybox 15 | ``` 16 | 17 | ![](http://cdn2.hubspot.net/hub/411552/file-1222265254-png/blog-files/docker-pull-busybox.png?t=1419682672898) 18 | 19 | BusyBox的是一个最小的Linux系统,它提供了主要的功能,除了一些与GNU相当的功能和选项。 20 | 21 | 下一步是运行传统、平凡但显著的“hello world.”容器,做一点小变化,我们称之为“Hello Docker.” 22 | ```docker run busybox /bin/echo Hello Docker 23 | ``` 24 | 25 | ![](http://cdn2.hubspot.net/hub/411552/file-1222265269-png/blog-files/hello-docker.png?t=1419682672898) 26 | 27 | 现在,让我们运行```hello docker```作为一个长时运行的进程: 28 | 29 | {{{ sample_job=$(docker run -d busybox /bin/sh -c “while true; do echo Docker; sleep 1; done”) 30 | }}} 31 | 32 | ![](http://cdn2.hubspot.net/hub/411552/file-1222265284-png/blog-files/docker-job.png?t=1419682672898) 33 | 34 | 该命令```sample_job```是长时间运行每隔1秒打印Docker的作业。使用```Docker logs```可查看作业的输出。如果没有被赋予名字,则一个id将被分配到该作业,以后使用命令例如```Docker logs```查看日志会变得困难。 35 | 36 | 运行```Docker logs```命令来查看作业的当前状态: 37 | ```docker logs $sample_job 38 | ``` 39 | 40 | 所有Docker命令可以用以下命令查看: 41 | ```docker help 42 | ``` 43 | 44 | 该名为```sample_job```的容器,可以使用以下命令来停止: 45 | ```docker stop $sample_job 46 | ``` 47 | 48 | 使用以下命令重新启动该容器: 49 | ```docker restart $sample_job 50 | ``` 51 | 52 | 如果要完全移除容器,需要将该容器停止,然后才能移除。像这样: 53 | ```docker stop $sample_job 54 | docker rm $sample_job 55 | ``` 56 | 57 | 将容器的状态保存为镜像,使用命令: 58 | ```docker commit $sample_job job1 59 | ``` 60 | 61 | 注意,镜像名称职能取字符[a-z]和数字[0-9]。 62 | 63 | 现在,您就可以使用以下命令查看所有镜像的列表: 64 | ```docker images 65 | ``` 66 | 67 | 在[我们之前的Docker教程]()中,我们发现,镜像是存储在Docker registry。在registry中的镜像可以使用以下命令查找到: 68 | ```docker search 69 | ``` 70 | 71 | 查看镜像的历史版本可以执行以下命令: 72 | ```docker history 73 | ``` 74 | 75 | 最后,使用以下命令将镜像推送到registry: 76 | ```docker push 77 | ``` 78 | 79 | 你必须要知道库名字是不是根库,它应该使用此格式```/```。 80 | 81 | 这都是一些非常基本的Docker命令。在我们[Docker教程系列的第六章](),我们将讨论如何使用Docker运行Python的Web应用程序,以及一些进阶的Docker命令。 -------------------------------------------------------------------------------- /docker-tutorial-series-3-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 3 DockerFile 2 | 3 | 在[Docker教程系列上一章]()中,我们通过15个Docker命令对Docker有个大致的了解。这组Docker命令是手动创建镜像的步骤。他们对创建镜像以及提交、检索、拉取和推送镜像是基本的帮助。 4 | 5 | 既然Docker能自动创建镜像,但是为什么要选择耗时乏味的方式来创建镜像呢? 6 | 7 | Docker为我们提供DockerFile来解决自动化问题。在这篇文章中,我们将讨论什么是Dockerfile,它能够做到的事情以及DockerFile一些基本语法。 8 | 9 | 10 | ###命令为易于自动化 11 | 12 | DockerFile是容纳创建镜像所需指令的脚本。基于在DockerFile中的指令可以用```Docker build```命令来创建镜像。通过减轻整个镜像和容器创建过程来简化了部署。 13 | 14 | DockerFiles支持一下语法命令: 15 | ```INSTRUCTION argument 16 | ``` 17 | 18 | 指令不区分大小写。然而,命名约定为全部大写。 19 | 20 | 所有DockerFiles必须以```FROM```命令开始。 ```FROM```命令表示新的镜像从被指示的基础镜像以及随后的指令来构建。```FROM```命令可用于任意次数,指示创建多个图像。语法如下: 21 | 22 | ```FROM 23 | ``` 24 | 25 | ```FROM ubuntu 26 | ``` 27 | 告诉我们,新的镜像将从基Ubuntu的镜象来构建。 28 | 29 | 继```FROM```命令,DockerFile还提供了一些其他的命令便于实现自动化。在文本文件或DockerFile文件中这些命令的顺序,也是它们被执行的顺序。 30 | 31 | 让我们了解一下这些有趣的DockerFile命令。 32 | 33 | 1. MAINTAINER:设置该镜像的作者字段。简单而明显的语法: 34 | ```MAINTAINER 35 | ``` 36 | 37 | 2. RUN:在shell或者exec的环境下执行的命令。```RUN```指令在新创建的镜像上添加新的层。接下来提交的结果用于在DockerFile的下一条指令。 38 | ```Syntax: RUN 39 | ``` 40 | 41 | 3. ADD:复制文件指令。它有两个参数。destination是容器内的路径。source可以是URL或者是启动配置上下文中的一个文件。 42 | ```Syntax: ADD 43 | ``` 44 | 45 | 4. CMD:提供了容器默认的执行命令。 DockerFile只允许CMD指令使用一次。 使用多个CMD会抵消之前所有的,只有最后一个生效。 CMD有三种形式: 46 | {{{ Syntax:CMD ["executable","param1","param2"] 47 | 48 | CMD ["param1","param2"] 49 | 50 | CMD command param1 param2 51 | }}} 52 | 53 | 5. EXPOSE:指定容器在运行时监听的端口。 54 | ```Syntax: EXPOSE ; 55 | ``` 56 | 57 | 6. ENTRYPOINT:配置容器一个可执行的命令,这意味着在每次使用镜像创建容器时一个特定的应用程序可以被设置为默认程序。同时也意味着该镜像每次被调用时每次仅能运行指定的应用。 58 | 59 | 类似于```CMD```,Docker只允许一个ENTRYPOINT以及多个ENTRYPOINT会抵消之前所有的,只执行最后的ENTRYPOINT指令。 60 | 61 | {{{ Syntax: Comes in two flavours 62 | 63 | ENTRYPOINT [‘executable’, ‘param1’,’param2’] 64 | 65 | ENTRYPOINT command param1 param2 66 | }}} 67 | 68 | 7. WORKDIR:指定```RUN```、```CMD```与```ENTRYPOINT```命令的工作目录。 69 | ```Syntax: WORKDIR /path/to/workdir 70 | ``` 71 | 72 | 8. ENV:设置环境变量。它们使用键值对,并增加运行的程序的灵活性。 73 | ```Syntax: ENV 74 | ``` 75 | 76 | 9. USER:镜像正在运行时设置一个UID。 77 | ```Syntax: USER 78 | ``` 79 | 80 | 10. VOLUME:授权访问从容器内到主机上的目录。 81 | 82 | ```Syntax:VOLUME [‘/data’] 83 | ``` 84 | 85 | DockerFile最佳实践 86 | 87 | 正如任何使用的应用程序,总会有遵循的最佳做法。你可以阅读更多有关[DockerFile最佳实践](http://crosbymichael.com/dockerfile-best-practices.html)。 88 | 89 | 以下是我们列出的基本的DockerFile最佳实践: 90 | 91 | - 保持常见的指令像```MAINTAINER```以及从上至下更新DockerFile命令; 92 | - 当构建镜像时使用可理解的标签,以便更好地管理镜像; 93 | - 避免在DockerFile中映射公有端口; 94 | - 作为最佳实践,```CMD```与```ENTRYPOINT```命令请使用数组语法。 95 | 96 | 在接下来的文章中,我们将讨论[Docker Registry与其工作流程]()。 -------------------------------------------------------------------------------- /docker-tutorial-series-4-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 4 Docker Registry 2 | 3 | 在前边Docker教程系列文章中,我们讨论了DockerFile的重要性并提供了使自动构建镜像更容易的一系列DockerFile的命令。在这篇文章中,让我们来谈谈一个重要的Docker组件:Docker Registry。这是中央登记处对于所有库,公有的还是私有的以及他们的工作流程。但是,在我们深入Docker Registry之前,先让我们去了解一些常见的术语和库相关的概念。 4 | 5 | 1. Repositories可以被"喜欢"使用stars活着用书签 6 | 2. 在社区交互中使用评论服务留下“意见”。 7 | 3. 私有库类似于公有,不同之处在于前者不会在搜索结果中显示,并且也没有访问它的权限。用户设置为合作者才能访问私有库。 8 | 4. 成功推送之后配置[webhooks](http://www.wikiwand.com/en/Webhook)。 9 | 10 | 角色 1 -- Index: ```index``` 负责并维护有关用户帐户,镜像的校验以及公共命名空间的信息。它使用以下组件维护这些信息: 11 | 12 | - Web UI 13 | - 元数据存储 14 | - 认证服务 15 | - 符号化 16 | 17 | 这也解决了较长的URL,以方便使用和验证库的拥有者。 18 | 19 | 角色 2 --Registry: ```registry```是镜像和图表的资源库。然而,它不具有本地数据库以及不提供用户认证。由S3,云文件和本地文件系统提供数据库支持。此外,认证采取的是通过使用记号索引验证服务。Registries可以有不同的类型。我们来分析其中的几个: 20 | 21 | 1. Sponsor Registry: 通过其客户和Docker社区使用第三方registry。 22 | 2. Mirror Registry: 只为客户使用第三方registry。 23 | 3. Vendor Registry: 由分发Docker镜像的供应商提供的registry。 24 | 4. Private Registry: 通过与防火墙和额外的安全层的私有实体提供的registry。 25 | 26 | 27 | 角色 3 --Registry Client: Docker充当registry客户端来维护推送和拉取,以及客户端的授权。 28 | 29 | ##Docker Registry工作流程详解 30 | 31 | 现在,让我们讨论五种情景模式,以便更好地理解Docker Registry。 32 | 33 | ####情景A: 用户要获取并下载镜像。所涉及的步骤如下: 34 | 35 | 1. 用户发送请求到index来下载镜像。 36 | 2. index 响应返回三个相关部分信息: 37 | - 该镜像位于的registry 38 | - 该镜像包括所有层的校验 39 | - 以授权目的记号 40 | > 注意:当请求header里有X-Docker-Token时 Tokens才会被返回。而私人仓库需要基本的身份验证,对于公有库它不是强制性的。 41 | 3. 现在,用户联系registry用响应后返回的记号。registry全权负责镜像。它存储基本的镜像和继承的层。 42 | 4. registry现在要与index证实该token是被授权的。 43 | 5. index会发送“true” 活着 “false”给registry,由此允许用户下载所需要的镜像 44 | 45 | ![](http://cdn2.hubspot.net/hub/411552/file-1222266489-png/blog-files/pull.png?t=1419682672898) 46 | 47 | 48 | ####情景B: 在用户想要推送镜像到registry中。涉及的步骤如下: 49 | 50 | 1. 用户发送带证书请求到index要求分配库名。 51 | 2. 在成功认证,命名空间可用以及库名被分配之后。index响应返回临时的token。 52 | 3. 镜像连带token,一起被推送到registry中。 53 | 4. registry与index证实token,然后在index验证之后开始读取推送流。 54 | 5. 该index然后更新由Docker生成的镜像校验。 55 | 56 | ![](http://cdn2.hubspot.net/hub/411552/file-1222266594-png/blog-files/push.png?t=1419682672898) 57 | 58 | ####情景C: 用户想要从index或registry中删除镜像: 59 | 60 | 1. index接收来自Docker一个删除库的信号。 61 | 2. 如果index验证库成功,它将删除该库,并返回一个临时token。 62 | 3. registry现在接收到带有该token的删除信号。 63 | 4. registry与index核实该token,然后删除库以及所有相关信息。 64 | 5. Docker现在通知有关删除的index,然后index移除库的所有记录。 65 | 66 | ![](http://cdn2.hubspot.net/hub/411552/file-1222266674-png/blog-files/delete.png?t=1419682672898) 67 | 68 | ####情景D:用户希望在没有index的独立模式中使用registry。 69 | 使用没有index的registry,这完全由Docker控制,它最适合于在私有网络存储镜像。registry运行在一个特殊的模式里,此模式限制了registry与Docker index的通信。所有的安全和身份验证需要用户自己注意。 70 | 71 | ####方案E:该用户想要在有index的独立模式中使用registry。 72 | 在这种情况下,一个自定义的index会被创建在私有网络里来存储和访问镜像。然而,通知Docker有关定制的index是耗时的。 Docker提供一个有趣的概念chaining registries,从而,实现负载均衡和为具体请求而指定的registry分配。在接下来的Docker教程系列中,我们将讨论如何在上述每个情景中使用Docker Registry API ,以及深入了解Docker Security。 73 | 74 | -------------------------------------------------------------------------------- /docker-tutorial-series-5-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 5 Docker Security 2 | 3 | 安全必须认真对待,当涉及开源责任性问题。当开发者拥抱Docker,在本地构建应用程序一致到声场部署也是一样的。随着部署在很多地方,它也是一个相当大的责任,重点将会是Docker作为一个项目或者平台的安全性。 4 | 5 | 因此,我们决定在[Docker教程系列的第五章]()来讨论:Docker security的重点领域以及为什么他们影响到Docker的整体安全性。鉴于Docker是LXCs的延伸,它也很容易使用LXCs的安全功能。 6 | 7 | 在本系列的第一部分,我们讨论了Docker run命令的执行以及运行容器。然而,到底发生了什么: 8 | 1. Docker run命令启动。 9 | 2. Docker 运行 lxc-start 来执行run命令。 10 | 3. lxc-start 在容器中创建了一组namespace和control groups。 11 | 12 | 对于那些不知道namespace和control groups的概念的在这里解释一下:namespace是隔离的第一级,而不是两个容器可以查看或控制在他们内部运行的进程。每个容器被分配到单独的网络栈中,因此一个容器不能访问另一容器的sockets。为了允许容器之间的IP通信,您必须指定容器的公网IP端口。 13 | 14 | Control Groups的关键组件具有以下功能: 15 | 16 | - 负责资源核算和限制。 17 | - 提供相关CPU、内存、I/O和network。 18 | - 试图避免某种DoS攻击。 19 | - 对多租户平台是有意义的。 20 | 21 | ###Docker Daemon的攻击面 22 | Docker Daemon以root权限运行,这意味着有一些问题需要格外小心 23 | 以下介绍一些有趣的要点: 24 | 25 | - Docker daemon的控制应该只给授权用户当Docker允许与访客容器目录共享而不限制其访问权限时。 26 | - 现在REST API endpoint支持Unix sockets,从而防止了cross-site-scripting攻击。 27 | - REST API可通过HTTP在使用适当的可信任网络或者VPN而暴露出来。 28 | - 在服务器上专门运行Docker(在完成时),隔离所有其它服务。 29 | 30 | 一些关键的Docker security特性包括: 31 | 32 | 1. Processes,当容器以非特权用户运行时,维护一个良好的安全水平。 33 | 2. Apparmor、SELinux、GRSEC解决方案,可用于额外的安全层。 34 | 3. 有继承其他容器系统的安全功能的能力。 35 | 36 | ###Docker.io API 37 | 用于管理与有关授权和安全的几个进程,Docker提供REST API。下表列出了关于此API用于维护相关的安全功能的一些命令。 38 | 39 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267319-png/blog-files/part-5-1.png?t=1419682672898) 40 | 41 | [Docker教程系列下一章]()我们继续探讨[前面第二章](http://dockerone.com/article/102)所讨论的Docker命令的进阶。 -------------------------------------------------------------------------------- /docker-tutorial-series-6-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 6 Docker Commands cont. 2 | 3 | 在[Docker教程系列较早的文章](http://dockerone.com/article/102)中,我们讨论的15个Docker命令。我们分享了如何使用它们以及他们做了什么的实践经验。 4 | 5 | 在这篇文章中,我们将讨论另外15个Docker命令,使我们积累更多Docker实践经验。 6 | 7 | 他们是: 8 | 9 | ####daemon: 10 | 11 | Docker daemon是有助于管理容器的持久后台进程。一般情况下,守护进程是处理请求的长期运行的进程服务。 12 | 13 | ```-d```标志用于运行后台进程 14 | 15 | ####build: 16 | 17 | 如之前所讨论的,可使用Dockerfiles构建镜像。简单的构建命令如下: 18 | ```docker build [options] PATH | URL 19 | ``` 20 | 也有一些Docker提供的有趣选项,如: 21 | 22 | ```--rm=true```所有中间容器构建成功后被移除 23 | 24 | ```--no-cache=false```避免在构建过程中使用缓存 25 | 26 | 下面的截图显示了使用```Docker build```命令。 27 | 28 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267769-png/blog-files/rer.png?t=1419682672898) 29 | 30 | ####attach: 31 | 32 | Docker允许使用```attach```命令与运行中的容器交互。该命令还允许查看守护进程。从容器分离可以通过两种方式来完成: 33 | 34 | - Ctrl+c 暗中退出 35 | 36 | - Ctrl-\ 跟栈分离 37 | 38 | ```attach```语法是: 39 | ```docker attach container 40 | ``` 41 | 42 | 该截图显示了```attach```命令的执行。 43 | 44 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267784-png/blog-files/docker-attach1.png?t=1419682672898) 45 | 46 | ####diff: 47 | 48 | Docker提供了一个非常强大的命令```diff```,其中列出了改变的文件和目录。这些变化包括添加、删除以及那些分别由A,D和C标志单独表示的。该命令改善了调试过程,并允许更快的共享环境。 49 | 50 | 语法是: 51 | ```docker diff container 52 | ``` 53 | 54 | 截图显示```diff```的执行。 55 | 56 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267799-png/blog-files/docker-diff.png?t=1419682672898) 57 | 58 | ####events: 59 | 60 | events的实时的信息可以从服务器通过指定持续时间来被收集为了那些需要收集的实时数据。 61 | 62 | ####import: 63 | 64 | Docker允许导入远程位置和本地文件或目录。通过使用HTTP从远程位置导入,而本地文件或目录的导入需要使用```-```参数。 65 | 66 | 从远程位置导入的语法: 67 | ```docker import http://example.com/example.tar 68 | ``` 69 | 70 | 截图显示导入本地文件: 71 | 72 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267814-png/blog-files/docker-import.png?t=1419682672898) 73 | 74 | ####export: 75 | 类似于```import```,```export```命令用于将文件系统内容打包成tar文件。 76 | 下图描述了其执行: 77 | 78 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267829-png/blog-files/docker-export.png?t=1419682672898) 79 | 80 | ####cp: 81 | 这个命令是从容器内复制文件到指定的路径上。语法如下: 82 | ```docker cp container:path hostpath. 83 | ``` 84 | 85 | 截图展示了```cp```的执行。 86 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267844-png/blog-files/docker-cp.png?t=1419682672898) 87 | 88 | ####login: 89 | 此命令用来登录到Docker registry服务器,语法是: 90 | ```docker login [options] [server] 91 | ``` 92 | 93 | 如要登录自己主机的registry请使用: 94 | ```docker login localhost:8080 95 | ``` 96 | 97 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267859-png/blog-files/docker-login.png?t=1419682672898) 98 | 99 | ####inspect: 100 | ```Docker inpect```命令可以收集有关容器和镜像的底层信息。该信息包括以下内容: 101 | 102 | - 容器实例的IP地址 103 | - 端口绑定列表 104 | - 特定的端口映射的搜索 105 | - 收集配置的详细信息 106 | 107 | 该命令的语法是: 108 | ```docker inspect container/image 109 | ``` 110 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267874-png/blog-files/docker-inspect.png?t=1419682672898) 111 | 112 | ####kill: 113 | 发送```SIGKILL```信号来停止容器的主进程。语法是: 114 | ```docker kill [options] container 115 | ``` 116 | 117 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267889-png/blog-files/docker-kill.png?t=1419682672898) 118 | 119 | ####rmi: 120 | 该命令可以移除一个或者多个镜像,语法如下: 121 | ```docker rmi image 122 | ``` 123 | 124 | 镜像可以有多个标签链接到它。在删除镜像时,你应该确保删除所有的标签以避免错误。下图显示了该命令的示例。 125 | 126 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267904-png/blog-files/fz.png?t=1419682672898) 127 | 128 | ####wait: 129 | 该命令打印退出代码仅当容器退出后。 130 | 131 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267919-png/blog-files/docker-wait.png?t=1419682672898) 132 | 133 | ####load: 134 | 该命令从tar文件中载入镜像或库到```STDIN```。 135 | 136 | 截图显示载入```app_box.tar```到```STDIN```: 137 | 138 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267934-png/blog-files/ff.png?t=1419682672898) 139 | 140 | ####save: 141 | 类似于```load```,该命令保存镜像为tar文件并发送到```STDOUT```。语法如下: 142 | ```docker save image 143 | ``` 144 | 145 | 简单截图示例如下: 146 | ![](http://cdn2.hubspot.net/hub/411552/file-1222267949-png/blog-files/docker-save.png?t=1419682672898) 147 | 148 | [Docker教程系列下一章]()我们将探讨Docker APIs。 -------------------------------------------------------------------------------- /docker-tutorial-series-7-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 7 Docker APIs 2 | 3 | 纵观我们的Docker教程系列,我们已经讨论了很多显著[Docker组件](http://dockerone.com/article/101)与[命令](http://dockerone.com/article/102)。在今天的系列文章中,我们深入挖掘Docker,发掘Docker APIs。 4 | 5 | 首先值得注意的是Docker提供以下的APIs,使得它更容易使用。这些API包含四个方面: 6 | 7 | - Docker Registry API 8 | - Docker Hub API 9 | - Docker OAuth API 10 | - Docker Remote API 11 | 12 | 具体到这篇文章,我们将讨论Docker Registry API以及Docker Hub API。 13 | 14 | ###Docker Registry API 15 | 16 | Docker Registry API是[Docker Registry](http://dockerone.com/article/104)的REST API,它简化了镜像和库的存储。该API不能访问用户帐户或它的授权。阅读[Docker教程系列的第四章](http://dockerone.com/article/104),以了解更多有关registry的类型。 17 | 18 | ####Extract image layer: 19 | 取出镜像层: 20 | ```GET /v1/images/(image_id)/layer 21 | ``` 22 | 23 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268384-jpg/blog-files/get-image-layer.jpg?t=1419891691508) 24 | 25 | ####Insert image layer: 26 | 插入镜像层: 27 | ```PUT /v1/images/(image_id)/layer 28 | ``` 29 | 30 | ####Retrieve an image: 31 | 检索镜像: 32 | ```GET /v1/images/(image_id)/json 33 | ``` 34 | 35 | ####Retrieve roots of an image: 36 | 检索根镜像: 37 | ```GET /v1/images/(image_id)/ancestry 38 | ``` 39 | 40 | ####Obtain all tags or specific tag of a repository: 41 | 获取库里所有的标签或者指定标签: 42 | ```GET /v1/repositories/(namespace)/(repository)/tags 43 | ``` 44 | 45 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268414-png/blog-files/docker-get-all-tags.png?t=1419891691508) 46 | 47 | 或者 48 | ```GET /v1/repositories/(namespace)/(repository)/tags/(tag*) 49 | ``` 50 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268414-png/blog-files/docker-get-all-tags.png?t=1419891691508) 51 | 52 | 53 | ####Delete a tag: 54 | 删除标签: 55 | ```DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*) 56 | ``` 57 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268444-jpg/blog-files/delete-a-tag.jpg?t=1419891691508) 58 | 59 | ####Status check of registry: 60 | registry状态检查: 61 | ```GET /v1/_ping 62 | ``` 63 | 64 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268459-png/blog-files/registry-ping.png?t=1419891691508) 65 | 66 | ###Docker Hub API 67 | Docker Hub API是Docker Hub的一个简单的REST API。重申一次,请参考[Docker教程系列的第四章](http://dockerone.com/article/104)了解Docker Hub。Docker Hub 控制用户帐户,通过管理校验认证以及公共命名空间。这个API还允许有关用户和library库的操作。 68 | 69 | 首先,让我们来探讨特特殊的library库(需要管理员权限)的命令: 70 | 71 | ####Library repository 72 | 73 | 1. Create a new repository - 使用以下命令可以创建新的library库: 74 | ```PUT /v1/repositories/(repo_name)/ 75 | ``` 76 | 77 | 其中,```repo_name```是新的库名字 78 | 79 | 2. Delete existing repository - 删除已存在的库: 80 | ```DELETE /v1/repositories/(repo_name)/ 81 | ``` 82 | 83 | 其中,```repo_name```是将要删除的库的名字 84 | 85 | 3. Update repository images - 更新库里的镜像: 86 | ```PUT /v1/repositories/(repo_name)/images 87 | ``` 88 | 89 | 4. Get images from a repository - 从库里面下载镜像: 90 | ```GET /v1/repositories/(repo_name)/images 91 | ``` 92 | 93 | 5. Authorization - 使用token可以创建被授权的库 94 | ```PUT /v1/repositories/(repo_name)/auth 95 | ``` 96 | 97 | 现在,让我们列出用户库的命令。library库与用户库命令之间的主要区别是命名空间的使用。 98 | 99 | ####User repository 100 | 1. Create a new user repository - 创建用户库的命令: 101 | ```PUT /v1/repositories/(namespace)/(repo_name)/ 102 | ``` 103 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268474-png/blog-files/create-user.png?t=1419891691508) 104 | 105 | 2. Delete existing repository - 删除用户库: 106 | ```DELETE /v1/repositories/(namespace)/(repo_name)/ 107 | ``` 108 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268489-png/blog-files/docker-delete-a-repo.png?t=1419891691508) 109 | 110 | 3. Update images - 更新用户库镜像 111 | ```PUT /v1/repositories/(namespace)/(repo_name)/images 112 | ``` 113 | ![](http://cdn2.hubspot.net/hub/411552/file-1222268504-png/blog-files/docker-update-image.png?t=1419891691508) 114 | 115 | 4. Get images from a repository - 从库中下载镜像 116 | ```GET /v1/repositories/(namespace)/(repo_name)/images 117 | ``` 118 | ![](http://cdn2.hubspot.net/hub/411552/file-1222273569-png/blog-files/docker-get-user-images.png?t=1419891691508) 119 | 120 | 5. Verify a user login - 验证用户登录: 121 | ```GET /v1/users 122 | ``` 123 | ![](http://cdn2.hubspot.net/hub/411552/file-1222273584-png/blog-files/docker-user-login.png?t=1419891691508) 124 | 125 | 6. Create a new user - 添加新用户: 126 | ```POST /v1/users 127 | ``` 128 | 129 | 7. Update user details - 更新用户信息: 130 | ```PUT /v1/users/(username)/ 131 | ``` 132 | 133 | 现在,我们已经给您介绍了有关Docker APIs终极之旅的第一站,第二站将是有关Docker OAuth以及Remote APIs,我们在[Docker教程系列的下一章]()见。 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /docker-tutorial-series-8-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 8 Docker Remote API 2 | 3 | 在[Docker教程系列的上一章](http://dockerone.com/article/107)中,我们讨论Docker Hub 以及 Docker Registry API。在今天的文章里,让我们深入探讨Docker Remote API。 4 | 5 | ###Docker Remote API 6 | 7 | Docker Remote API是取代rcli(远程命令行界面)的REST API。对于本教程的目的,我们使用一个命令行工具cURL来处理url操作。它有助于发出请求、获取与发送数据并且检索信息。 8 | 9 | ####List containers 10 | 获取所有容器的清单: 11 | ```GET /containers/json 12 | ``` 13 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274164-png/blog-files/get-all-containers.png?t=1419891691508) 14 | 15 | ####Create a new container 16 | 创建新的容器: 17 | ```POST /containers/create 18 | ``` ![](http://cdn2.hubspot.net/hub/411552/file-1222274179-png/blog-files/docker-create-container.png?t=1419891691508) 19 | 20 | ####Inspect Container 21 | 使用容器id获取该容器底层信息: 22 | ```GET /containers/(id)/json 23 | ``` ![](http://cdn2.hubspot.net/hub/411552/file-1222274194-png/blog-files/docker-inspect-a-container.png?t=1419891691508) 24 | 25 | ####Process List 26 | 获取容器内进程的清单: 27 | ```GET /containers/(id)/top 28 | ``` 29 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274209-png/blog-files/docker-container-top.png?t=1419891691508) 30 | 31 | ####Container Logs 32 | 获取容器的标准输出和错误日志: 33 | ```GET /containers/(id)/logs 34 | ``` 35 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274224-png/blog-files/docker-container-logs.png?t=1419891691508) 36 | 37 | ####Export Container 38 | 导出容器内容: 39 | ```GET /containers/(id)/export 40 | ``` 41 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274239-png/blog-files/docker-export-container.png?t=1419891691508) 42 | 43 | ####Start a container 44 | 开启容器: 45 | ```POST /containers/(id)/start 46 | ``` 47 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274254-png/blog-files/docker-start-container.png?t=1419891691508) 48 | 49 | ####Stop a container 50 | 停止容器: 51 | ```POST /containers/(id)/stop 52 | ``` 53 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274269-png/blog-files/docker-stop-a-container.png?t=1419891691508) 54 | 55 | ####Restart a Container 56 | 重启容器: 57 | ```POST /containers/(id)/restart 58 | ``` 59 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274284-png/blog-files/docker-restart-a-container.png?t=1419891691508) 60 | 61 | ####Kill a container 62 | 终止容器: 63 | ```POST /containers/(id)/kill 64 | ``` 65 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274299-png/blog-files/docker-kill-a-container.png?t=1419891691508) 66 | 67 | 现在,我们已经带您领略了Docker API的第二站,[Docker教程系列下一章]()会了解有关镜像的Docker Remote API命令。我们所有的Docker教程系列你可以在[这里](http://dockerone.com/topic/Docker%20Tutorial)找到。 -------------------------------------------------------------------------------- /docker-tutorial-series-9-cn.md: -------------------------------------------------------------------------------- 1 | ##Docker教程系列 9 Docker Remote API Commands for Images 2 | 3 | 在[Docker教程系列的上一篇文章](http://dockerone.com/article/109)中,我们讨论了Docker Remote API,并具体探索了有关容器的命令。在这篇文章中,我们将讨论有关镜像的命令。 4 | 5 | ###Create an Image 6 | 创建镜像 7 | 8 | 镜像可以通过以下两种方式来创建: 9 | 10 | - 从registry拉取 11 | - 导入镜像 12 | 13 | ```POST /images/create 14 | ``` 15 | 16 | 截图示例: 17 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274949-jpg/blog-files/create-an-image.jpg?t=1419919381183) 18 | 19 | ###Create an Image from a Container 20 | 从容器创建镜像: 21 | ```POST /commit 22 | ``` 23 | 截图示例: 24 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274964-png/blog-files/docker-create-image-from-container.png?t=1419919381183) 25 | 26 | ###List of Images 27 | 获取镜像清单: 28 | ```GET /images/json 29 | ``` 30 | 31 | 截图示例: 32 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274979-png/blog-files/docker-list-images.png?t=1419919381183) 33 | 34 | ###Insert a File 35 | 导入指定路径文件: 36 | ```POST /images/(name)/insert 37 | ``` 38 | 截图示例: 39 | ![](http://cdn2.hubspot.net/hub/411552/file-1222274994-jpg/blog-files/docker-image-insert-file.jpg?t=1419919381183) 40 | 41 | ###Delete Image 42 | 删除镜像: 43 | ```DELETE /images/(name) 44 | ``` 45 | 截图示例: 46 | ![]()http://cdn2.hubspot.net/hub/411552/file-1222275009-jpg/blog-files/delete-an-image.jpg?t=1419919381183 47 | 48 | 49 | ###Registry Push 50 | 推送镜像到Registry: 51 | ```POST /images/(name)/push 52 | ``` 53 | 截图示例: 54 | ![](http://cdn2.hubspot.net/hub/411552/file-1222275024-png/blog-files/docker-push-image-to-remote-repo.png?t=1419919381183) 55 | 56 | ###Tag Image 57 | 标记镜像: 58 | ```POST /images/(name)/tag 59 | ``` 60 | 截图示例: 61 | ![](http://cdn2.hubspot.net/hub/411552/file-1222275039-jpg/blog-files/tag-an-image.jpg?t=1419919381183) 62 | 63 | ###Search an Image 64 | 搜索镜像: 65 | ```GET /images/search 66 | ``` 67 | 截图示例: 68 | ![](http://cdn2.hubspot.net/hub/411552/file-1222275054-png/blog-files/docker-search-an-image.png?t=1419919381183) 69 | 70 | 71 | ###History 72 | 查看镜像历史: 73 | ```GET /images/(name)/history 74 | ``` 75 | 截图示例: 76 | ![](http://cdn2.hubspot.net/hub/411552/file-1222275069-jpg/blog-files/docker-get-image-history.jpg?t=1419919381183) 77 | 78 | ###Build an Image 79 | 构建镜像: 80 | ```POST /build 81 | ``` 82 | 截图示例: 83 | ![](http://cdn2.hubspot.net/hub/411552/file-1222275084-png/blog-files/docker-build-image-from-dockerfile.png?t=1419919381183) 84 | 85 | 86 | 现在我们已经结束了三站Docker API的旅程。敬请期待接下来的[Docker教程系列](http://dockerone.com/topic/Docker%20Tutorial)。 87 | -------------------------------------------------------------------------------- /docker_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "other_args=\"-g /mnt/docker\"" > /etc/sysconfig/docker 3 | -------------------------------------------------------------------------------- /docker_ecosys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/docker_ecosys.png -------------------------------------------------------------------------------- /docker_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 3 | rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 4 | 5 | yum install -y yum-priorities 6 | yum install -y docker-io 7 | -------------------------------------------------------------------------------- /docker_mysql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -p 33306:3306 --name django-mysql -e MYSQL_ROOT_PASSWORD=gxt900101 -d mysql 4 | 5 | -------------------------------------------------------------------------------- /dockercon-eu-introducing-docker-compose-cn.md: -------------------------------------------------------------------------------- 1 | 2 | 【编者的话】本文主要讲解了如何用Fig来解决Docker多参数启动容器的问题(Fig入门可以参照这里)以及使用Fig需要注意的一些事项。 3 | 4 | 5 | **原文链接:[Why Use Fig for Docker Automation?](http://container-solutions.com/2015/01/use-fig-docker-automation/) (翻译:[田浩](https://github.com/llitfkitfk))** -------------------------------------------------------------------------------- /dockerfile-best-practices-1-cn.md: -------------------------------------------------------------------------------- 1 | 【Docker进阶】Dockerfile最佳实践(一) 2 | 3 | 【编者的话】本文是[Docker入门教程](http://dockerone.com/article/111)系列的[第三章-DockerFile](http://dockerone.com/article/103)的进阶篇第一部分 4 | 5 | Dockerfiles为构建镜像提供了简单的语法。下面是一些提示与技巧来帮助您最有效地使用Dockerfiles。 6 | 7 | ###1:使用缓存 8 | Dockerfile的每条指令都会将更改提交到新的镜像,该镜像将被用于下一个指令的基础镜像。如果一个镜像存在相同的父类镜像和指令(除了```ADD```)Docker将会使用镜像而不是执行该指令,即缓存。 9 | 10 | 为了有效地利用缓存,你需要保持你的Dockerfiles一致,并且改建在末尾添加。我所有的Dockerfiles开始于以下五行: 11 | {{{FROM ubuntu 12 | MAINTAINER Michael Crosby 13 | 14 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 15 | RUN apt-get update 16 | RUN apt-get upgrade -y 17 | }}} 18 | 19 | 更改```MAINTAINER```指令会使Docker强制执行```RUN```指令来更新apt,而不是使用缓存。 20 | 21 | **保持常用的Dockerfile指令在顶部来利用缓存。** 22 | 23 | ###2:使用标签 24 | 除非你正在用Docker做实验,否则你应当通过```-t```选项来```docker build```新的镜像以便于标记构建的镜像。一个简单的可读标签将帮助您管理每个创建的镜像。 25 | ```docker build -t="crosbymichael/sentry" . 26 | ``` 27 | 28 | **始终通过```-t```标记来构建镜像。** 29 | 30 | ###3:公开端口 31 | 两个Docker的核心概念是可重复和可移植。镜像应该能运行在任何主机上并且能运行尽可能多的次数。在Dockerfiles中您有能力映射私有和公有端口,但是你永远不要在Dockerfile中映射公有端口。通过映射公有端口到主机上,你将只能运行一个容器化应用程序实例。 32 | {{{# private and public mapping 33 | EXPOSE 80:8080 34 | 35 | # private only 36 | EXPOSE 80}}} 37 | 38 | 如果镜像的消费者关心容器公有映射了哪个公有端口,他们可以在运行镜像时设置```-p```选项,否则,Docker会给容器自动分配端口。 39 | 40 | **切勿在Dockerfile映射公有端口。** 41 | 42 | ###4:CMD与ENTRYPOINT的语法 43 | 无论```CMD```还是```ENTRYPOINT```都是直线前进的,但他们有一个隐藏的错误“功能”,如果你不知道的话他们可能会触发问题。这些指令支持的两种不同的语法。 44 | {{{CMD /bin/echo 45 | # or 46 | CMD ["/bin/echo"] 47 | }}} 48 | 49 | 这看起来好像没什么问题,但深入细节里的魔鬼会将你绊倒。如果你使用第二个语法:```CMD```(或```ENTRYPOINT```)是一个数组,它执行的命令完全像你期望的那样。如果使用第一种语法,Docker会在你的命令前面加上```/bin/sh -c```。我记得一直都是这样。 50 | 51 | 如果你不知道Docker修改了```CMD```命令,在命令前加上```/bin/sh -c```可能会导致一些意想不到的问题以及不容易理解的功能。因此,在使用这两个指令你应当总是使用数组语法,因为两者都会确切地执行你打算执行的命令。 52 | 53 | **使用CMD和ENTRYPOINT时,请务必使用数组语法。** 54 | 55 | ###5. CMD和ENTRYPOINT 联合使用更好 56 | 以防你不知道```ENTRYPOINT```使您的容器化应用程序运行得像一个二进制文件,您可以在```docker run```期间给```ENTRYPOINT```参数传递,而不是担心它被覆盖(跟```CMD```不同)。当与```CMD```一起使用时```ENTRYPOINT```表现会更好。让我们来研究一下我的[Rethinkdb](http://www.rethinkdb.com/) Dockerfile,看看如何使用它。 57 | {{{# Dockerfile for Rethinkdb 58 | # http://www.rethinkdb.com/ 59 | 60 | FROM ubuntu 61 | 62 | MAINTAINER Michael Crosby 63 | 64 | RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list 65 | RUN apt-get update 66 | RUN apt-get upgrade -y 67 | 68 | RUN apt-get install -y python-software-properties 69 | RUN add-apt-repository ppa:rethinkdb/ppa 70 | RUN apt-get update 71 | RUN apt-get install -y rethinkdb 72 | 73 | # Rethinkdb process 74 | EXPOSE 28015 75 | # Rethinkdb admin console 76 | EXPOSE 8080 77 | 78 | # Create the /rethinkdb_data dir structure 79 | RUN /usr/bin/rethinkdb create 80 | 81 | ENTRYPOINT ["/usr/bin/rethinkdb"] 82 | 83 | CMD ["--help"] }}} 84 | 85 | 这是获得容器化Rethinkdb全部所需。在顶部我们有标准的5行来确保基础镜像是最新的,端口的公开等等......随着```ENTRYPOINT```的设置,我们知道每当这个镜像运行,在```docker run```过程中传递的所有参数将成为```ENTRYPOINT```(```/usr/bin/rethinkdb```)的参数。 86 | 87 | 在Dockerfile中我还设置了一个默认```CMD```参数```--help```。这样做是为了```docker run```期间如果没有参数的传递,rethinkdb将会给用户显示默认的帮助文档。这是你所期望的与rethinkdb交互有着相同的功能。 88 | 89 | ```docker run crosbymichael/rethinkdb 90 | ``` 91 | 92 | 输出 93 | {{{Running 'rethinkdb' will create a new data directory or use an existing one, 94 | and serve as a RethinkDB cluster node. 95 | File path options: 96 | -d [ --directory ] path specify directory to store data and metadata 97 | --io-threads n how many simultaneous I/O operations can happen 98 | at the same time 99 | 100 | Machine name options: 101 | -n [ --machine-name ] arg the name for this machine (as will appear in 102 | the metadata). If not specified, it will be 103 | randomly chosen from a short list of names. 104 | 105 | Network options: 106 | --bind {all | addr} add the address of a local interface to listen 107 | on when accepting connections; loopback 108 | addresses are enabled by default 109 | --cluster-port port port for receiving connections from other nodes 110 | --driver-port port port for rethinkdb protocol client drivers 111 | -o [ --port-offset ] offset all ports used locally will have this value 112 | added 113 | -j [ --join ] host:port host and port of a rethinkdb node to connect to 114 | .................}}} 115 | 116 | 现在,让我们带上```--bind all```参数来运行容器。 117 | ```docker run crosbymichael/rethinkdb --bind all 118 | ``` 119 | 120 | 输出 121 | {{{info: Running rethinkdb 1.7.1-0ubuntu1~precise (GCC 4.6.3)... 122 | info: Running on Linux 3.2.0-45-virtual x86_64 123 | info: Loading data from directory /rethinkdb_data 124 | warn: Could not turn off filesystem caching for database file: "/rethinkdb_data/metadata" (Is the file located on a filesystem that doesn't support direct I/O (e.g. some encrypted or journaled file systems)?) This can cause performance problems. 125 | warn: Could not turn off filesystem caching for database file: "/rethinkdb_data/auth_metadata" (Is the file located on a filesystem that doesn't support direct I/O (e.g. some encrypted or journaled file systems)?) This can cause performance problems. 126 | info: Listening for intracluster connections on port 29015 127 | info: Listening for client driver connections on port 28015 128 | info: Listening for administrative HTTP connections on port 8080 129 | info: Listening on addresses: 127.0.0.1, 172.16.42.13 130 | info: Server ready 131 | info: Someone asked for the nonwhitelisted file /js/handlebars.runtime-1.0.0.beta.6.js, if this should be accessible add it to the whitelist. }}} 132 | 133 | 就这样,一个全面的可以访问db和管理控制台的Rethinkdb实例就运行起来了,你可以用与镜像交互一样的方式来与其交互。它功能非常强大但是简单小巧。当然,我喜欢简单。 134 | 135 | **CMD和ENTRYPOINT 结合在一起使用更好。** 136 | 137 | 我希望这篇文章可以使您在使用Dockerfiles以及构建镜像时受益。展望未来,我相信Dockerfiles会成为Docker的重要一部分:简单而且使用方便无论你是消费或是生产镜像。我打算投入更多的时间来提供一个完整的,功能强大,但简单的解决方案来使用Dockerfile构建Docker镜像。 138 | 139 | **原文链接:[Dockerfile Best Practices - take 1](http://crosbymichael.com/dockerfile-best-practices.html) (翻译:[田浩](https://github.com/llitfkitfk))** 140 | -------------------------------------------------------------------------------- /dockerfile-best-practices-2-cn.md: -------------------------------------------------------------------------------- 1 | 【Docker进阶】Dockerfile最佳实践(二) 2 | 3 | 【编者的话】本文是[Docker入门教程](http://dockerone.com/article/111)系列的[第三章-DockerFile](http://dockerone.com/article/103)的进阶篇第二部分 4 | 5 | 自从我[上一篇Dockerfile最佳实践](http://dockerone.com/article/131)后Docker发生了很大变化。上一篇会继续保留造福Docker新手,这篇文章将介绍Docker有什么变化以及你现在应当做什么。 6 | 7 | ###1:不要开机初始化 8 | 容器模型是进程而不是机器。即使你认为你需要做这一点,你可能错了。 9 | 10 | ###2:可信任构建 11 | 即使你不喜欢这个题目但它是很棒的一功能。我把大部分gihub仓库添加到可信任构建,这样当我提交我的新的等待索引后的镜象。另外,我不必再创建单独的Dockerfile仓库来与他人分享,它们都在同一个地方。 12 | 13 | 请记住,这不是你为尝试新东西的操场。在你推送之前本地先构建一下。Docker可以确保你在本地构建和运行的会是同样的当你把他们推送到其他任何地方。本地开发和测试、提交和推送、以及等待索引上的官方镜像都是建立在可信任构建的基础之上的。 14 | 15 | ###3:不要在构建中升级版本 16 | 更新将在基础镜像里你不需要在您的容器内来```apt-get upgrade```更新。因为隔离情况下往往会失败,如果更新时试图修改init或改变容器内的设备。它还可能产生不一致的镜像,因为你不再有你的应用程序该如何运行以及包含在镜像中依赖的哪种版本的正确源文件。 17 | 18 | 如果有基础镜像需要的安全更新,那么让上游的知道这样他们可以给大家更新,并确保你的构建的一致性。 19 | 20 | ###4:使用小型基础镜像 21 | 有些镜像比其他的更臃肿。我建议使用```debian:jessie```作为你的基础镜像。如果您熟悉ubuntu,你会在debian发现一个更轻便更幸福的家。此镜像不但小巧,而且不包含任何不必要的肿胀的东西。 22 | 23 | ###5:使用特定的标签 24 | 对与你的基础镜像这是非常重要的。Dockerfile中```FROM```应始终包含依赖的基础镜像的完整仓库名和标签。比如说```FROM debian:jessie```而不仅仅是```FROM debian```。 25 | 26 | ###6:常见指令组合 27 | 您的```apt-get update```应该与```apt-get install```组合。此外,你应该采取```\```的优势使用多行来进行安装。 28 | 29 | {{{#Dockerfile for https://index.docker.io/u/crosbymichael/python/ 30 | FROM debian:jessie 31 | 32 | RUN apt-get update && apt-get install -y \ 33 | git \ 34 | libxml2-dev \ 35 | python \ 36 | build-essential \ 37 | make \ 38 | gcc \ 39 | python-dev \ 40 | locales \ 41 | python-pip 42 | 43 | RUN dpkg-reconfigure locales && \ 44 | locale-gen C.UTF-8 && \ 45 | /usr/sbin/update-locale LANG=C.UTF-8 46 | 47 | ENV LC_ALL C.UTF-8 }}} 48 | 49 | 谨记层和缓存都是不错的。不要害怕过多的层因为缓存是大救星。当然,你应当尽量使用上游的包。 50 | 51 | ###7:使用自己的基础镜像 52 | 我不是在谈论运行debbootstrap来制作自己的debian。我说的是,如果你要运行python应用程序需要有一个python基础镜像。前面例子中Dockerfile用来构建```crosbymichael/python```镜像,它用于许多其他镜像来运行python应用程序。 53 | {{{FROM crosbymichael/python 54 | 55 | RUN pip install butterfly 56 | RUN echo "root\nroot\n" | passwd root 57 | 58 | EXPOSE 9191 59 | ENTRYPOINT ["butterfly.server.py"] 60 | CMD ["--port=9191", "--host=0.0.0.0"] }}} 61 | 62 | 另一个: 63 | {{{FROM crosbymichael/python 64 | 65 | RUN pip install --upgrade youtube_dl && mkdir /download 66 | WORKDIR /download 67 | ENTRYPOINT ["youtube-dl"] 68 | CMD ["--help"] }}} 69 | 70 | 正如你看到的这使得使用你的基础镜像非常小,从而使你集中精力在应用程序上。 71 | 72 | 让我知道你在想什么或者如果您有任何其它问题可以在评论中留言。 73 | 74 | **原文链接:[Dockerfile Best Practices - take 2](http://crosbymichael.com/dockerfile-best-practices-take-2.html) (翻译:[田浩](https://github.com/llitfkitfk))** 75 | -------------------------------------------------------------------------------- /dockerfile/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/dockerfile/README.md -------------------------------------------------------------------------------- /exploring-local-docker-bridge-networks-cn.md: -------------------------------------------------------------------------------- 1 | ##探索Docker本地桥接网络 2 | 3 | 【编者的话】本文主要介绍了Docker的基础网络知识,作者通过容器与MongoDB实例的连接的小实验探索了Docker的网路知识以及其中的一些问题。 4 | 5 | TL; DR(太长了,不要看了)Docker可以帮助您构建与管理容器。它不能保护你免受应用层的错误。 6 | 7 | 我正在筹备[Docker in Action](http://www.manning.com/nickoloff/)(译注:此书的样章可以在[这里查看](http://www.manning.com/nickoloff/DockerinAction_MEAP_ch01.pdf))第五章内容-有关Docker的容器链接与网络配置。最近一直在关注工具的其他部分,因此我需要几天让自己重新熟悉容器链接以及潜深Docker网络。 8 | 9 | 我想给您展示一些东西。 10 | 11 | 对于熟悉桥接网络的不会有什么新的内容。但我有一种感觉一些使用Docker的开发者没有预料到的一些事。我想给你展示当你启动一些本地的容器时如何探索为此建立的网络。这样做将有助于您理解Docker容器链接。 12 | 13 | 下面将会指引您完成一个涉及从一个单独的容器访问一个包含```MongoDB```的服务器的小实验。 14 | 15 | ###开启您的目标容器 16 | 在这个实验中我们的目标是MongoDB服务器。您可以安装并使用以下简单的命令启动。 17 | {{{docker run --name some-mongo -d mongo:latest 18 | }}} 19 | 20 | ###开始另一个容器 21 | 这个是从Ubuntu镜象运行来的容器,他运行了一个shell环境。你会检查你的本地网络并从这个容器连接到您的mongo实例。 22 | {{{docker run -it --rm ubuntu:latest /bin/bash 23 | root@XXX:/#}}} 24 | 25 | 在交互模式下开启镜像,这样就可以安装你所需要的工具或者做其他事情而不会扰乱你的主机系统的状态。 26 | 27 | ###获取您的骇客工具 28 | 你需要的工具包括```Mongo CLI```和```nmap```。 29 | {{{root@XXX:/#apt-get -y install nmap 30 | root@XXX:/#apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 31 | root@XXX:/#echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list 32 | root@XXX:/#apt-get update 33 | root@XXX:/#apt-get install -y mongodb-org-shell }}} 34 | 35 | 既然你正在以root身份运行的容器,你不用担心的标准的```sudo```命令前缀。一旦这些命令运行完成,你就可以准备开启实验。 36 | 37 | ###扫描网络 38 | 找到你的容器的IP地址,这样你就可以猜出你的目标可能在哪。 39 | {{{root@XXX:/#MY_IP=`/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'` }}} 40 | 41 | 寻找在同一的监听端口为27017子网中的主机。执行此操作使用```nmap```进行子网的27017端口的扫描: 42 | {{{root@XXX:/#nmap -sS -p 27017 $MY_IP/24 43 | }}} 44 | 45 | 在我的测试环境中的网络端口扫描的结果如下: 46 | {{{Starting Nmap 6.40 ( http://nmap.org ) at 2014-12-09 15:52 UTC 47 | Nmap scan report for 172.17.0.33 48 | Host is up (0.000030s latency). 49 | PORT STATE SERVICE 50 | 27017/tcp closed unknown 51 | MAC Address: 02:42:AC:11:00:21 (Unknown) 52 | Nmap scan report for 172.17.0.34 53 | Host is up (0.000021s latency). 54 | PORT STATE SERVICE 55 | 27017/tcp closed unknown 56 | MAC Address: 02:42:AC:11:00:22 (Unknown) 57 | Nmap scan report for 172.17.0.96 58 | Host is up (-0.079s latency). 59 | PORT STATE SERVICE 60 | 27017/tcp open unknown 61 | MAC Address: 02:42:AC:11:00:60 (Unknown) 62 | Nmap scan report for 172.17.0.131 63 | Host is up (-0.084s latency). 64 | PORT STATE SERVICE 65 | 27017/tcp closed unknown 66 | MAC Address: 02:42:AC:11:00:83 (Unknown) 67 | Nmap scan report for XXX (172.17.0.132) 68 | Host is up (0.000055s latency). 69 | PORT STATE SERVICE 70 | 27017/tcp closed unknown 71 | Nmap done: 256 IP addresses (5 hosts up) scanned in 4.10 seconds }}} 72 | 73 | 从以上的输出中你可以看到,我的测试环境中运行的5个容器(包括一个正在运行命令的)。五个其中之一的端口27017是开放的。开放此端口的IP地址是运行你的MongoDB实例容器的IP地址。这是有趣的并且某些情况下可能会是一个惊喜。一会儿我就来聊聊这些。首先,让我们完成实验。 74 | 75 | ###访问数据库 76 | 使用先前安装的mongo CLI,您应该能够访问在其他容器中运行的MongoDB实例。在你得意忘形之前,你要知道这并不是Docker的或者Linux内核的漏洞而是当你运行开放的服务器时它都会发生的。 77 | 78 | 当您运行以下命令使用您在```nmap```输出里的IP地址。 79 | {{{root@XXX:/#mongo --host 172.17.0.96 # replace the IP address 80 | MongoDB shell version: 2.6.6 81 | connecting to: 172.17.0.96:27017/test 82 | Welcome to the MongoDB shell. 83 | For interactive help, type "help". 84 | For more comprehensive documentation, see 85 | http://docs.mongodb.org/ 86 | Questions? Try the support group 87 | http://groups.google.com/group/mongodb-user 88 | >_ 89 | }}} 90 | 91 | 就是这样。你可以在不同的容器内连接到MongoDB实例。获取对数据库的访问就是这么容易。惊讶吧?不应该。您正在运行一个没有身份验证的要求的MongoDB实例。不要以为防火墙或网络拓扑会保护你的安全性差的服务。 92 | 93 | ###Docker抽象化以及他们通信什么 94 | 此前在Docker的历险中,由于某种原因,我的印象是网络是围绕着容器而构建。这是真实的。有迹象表明这里有分离接口的防火墙。不过我最初没有想到这些,即使是路由没有具体的链接。我认为,这是被Docker容器的链接增强了。 95 | 96 | 与其他人一样,当我学习如何访问其他容器我发现的第一件事就是容器的链接。这是你可以在你想要访问另一个现有容器的容器创建的时间来指定的进程。当您创建链接使Docker注入持有IP和端口信息的环境变量到新创建的容器。此外,其他的容器IP地址将被添加到```/etc/hosts```文件为容器的名称。 97 | 98 | 当我学习链接时,我停止了寻找其他的工具......至少一段时间。链接相当的方便,但他们只是提供了方便。他们稍微减少了信息的不明确性通过告知你的新容器其他一些特定的容器的IP和端口信息。就是这样。桥接网络也没有什么花哨。 99 | 100 | 您可以访问其他容器的外部接口,就像他们是一个网络上的其他计算机。这应该是一件好事。我们知道该怎么做。我们有许多用于做这些的工具。但急于采用的会跟我早期的假设一样都会很害怕。如果你做了也不必惊慌。仅仅修复那些Docker会触发的一些明显的安全问题就好了。 101 | 102 | Docker可帮助您构建强大的容器。但它不能使你免受应用层的错误。请记住这些在您发展前进的时候并且要构建更大更好的东西。 103 | 104 | PS:别忘了停止并删除您在本实验中创建的容器。 105 | {{{ docker kill some-mongo 106 | docker rm -v some-mongo}}} 107 | 108 | **原文链接:[Exploring Local Docker Bridge Networks](https://medium.com/@allingeek/exploring-local-docker-bridge-networks-2c655ac59d7a) (翻译:[田浩](https://github.com/llitfkitfk))** -------------------------------------------------------------------------------- /file-system-snapshots-make-build-scripts-easy-cn.md: -------------------------------------------------------------------------------- 1 | 2 | ##docker如何减轻开发长时运行脚本所带来的痛苦 3 | 4 | 我想我已经找到了一个相当引人注目的docker使用案例。但在此之前,如果你还是认为这又是一个人云亦云docker美德的博客帖子的话,我想明确指出,这个帖子确实是关于把文件系统作为持久性数据结构的赞美帖。 5 | 因此,这篇文章的见解同样适用于其他的 [copy-on-write文件系统](http://www.wikiwand.com/en/Copy-on-write),如[BTRFS](http://www.wikiwand.com/en/Btrfs)和[ZFS](http://www.wikiwand.com/en/ZFS)。 6 | 7 | ###问题 8 | 9 | 让我们从这个我试图解决的问题开始。我开发了包括了众多的步骤的长时运行的构建脚本。 10 | 11 | - 花费1-2个小时运行。 12 | - 它从互联网下载了很多相当大的文件。(超过300M)。 13 | - 后期严重依赖早期构建的库。 14 | 15 | 但最显著的特点是,它需要花很长的时间来运行。 16 | 17 | ###文件系统是固有状态 18 | 我们通常是以一种状态的方式与文件系统进行交互的。我们可以添加,删除或移动文件。我们可以改变文件的权限或者它的访问时间。 19 | 隔离下的大部分操作都可以撤销。例如你可以移动文件到其其他的地方后,将文件恢复到原来的位置。通常我们不会做的是采取一个快照,并恢复到那个状态。这篇文章建议更多地利用这一特性对开发长时运行脚本有巨大好处。 20 | 21 | ###使用联合文件系统的快照 22 | Docker采用的是所谓的联合文件系统叫做[AUFS](http://www.wikiwand.com/en/Aufs)。联合文件系统实现了被称为联合挂载的文件系统。顾名思义,这意味着文件和独立的文件系统的目录 被分层于 互相形成的单个连贯文件系统之上。 23 | 这是以分层方式完成的。如果一个文件出现在两个文件系统,后来添加的文件将会呈现 (该文件其他版本是存在于层级中的,不改变,只是看不到的)。 24 | 25 | Docker称呼在联合挂载文件系统里的每个文件系统为[layers(层)](https://docs.docker.com/terms/layer)。使用这种技术的结果是,它的副作用可以实现快照。每个快照对于所有层是一个简单的联合挂载文件系统挂载到某个层次结构中。 26 | 27 | ###生成脚本的快照 28 | 29 | 快照使开发一个长时运行的构建脚本成为梦想。总的想法是,分解大脚本为更小的脚本(我喜欢称之为scriptlets)并且单独地运行每一个,每一个运行后快照其文件系统。 (Docker会自动执行此操作。)如果你发现一个scriptlet运行失败,简单的可以回到最后的快照(仍处于原始状态!),然后再试一次。 30 | 一旦你完成你的构建脚本,你可以保证,脚本正常工作,现在可以分配给其他主机。 31 | 32 | 相对于如果你没有使用快照会发生什么。除了在我们中间那些有和尚般的耐心的人,当它在1个半小时后失败了,没有人会去从头开始运行他们的构建脚本。当然,我们会尽最大努力把系统恢复到失败前的状态。例如我们可以删除一个目录或运行```make clean```。 33 | 34 | 但是,我们可能没有真正地理解我们正在构建的组件。它可能复杂的Makefile:把文件放到文件系统中我们不知道的地方。唯一真正确定的途径是恢复到快照。 35 | 36 | ###使用快照构建脚本的docker 37 | 在本节中,我将介绍我是如何使用Docker实现[GHC](http://haskell.org/ghc)7.8.3 ARM交叉编译器的构建脚本。对于这个任务Docker相当不错的,但并不是完美的。我做了一些事情,可能看起来浪费的或不雅的,但都是必要的,以保持开发脚本的总时间到最低限度。构建脚本可以在[这里](https://github.com/sseefried/docker-build-ghc-android)找到。 38 | 39 | ####用Dockerfile构建 40 | 41 | Docker读取一个名为Dockerfile来构建镜像。Dockerfile包含一些命令词汇来具体指定哪些行动应该被执行。一个完整的参考可以在[这里](https://docs.docker.com/reference/builder/)找到。其中在我的脚本主要用了```WORKDIR```,```ADD```和```RUN```。```ADD```命令非常有用因为它可以让你在运行之前将外部文件添加到当前Docker镜像中然后转换成镜像的文件系统。你可以在[这里](https://github.com/sseefried/docker-build-ghc-android/tree/master/user-scripts)看到很多scriptlets构成的构建脚本。 42 | 43 | ####设计 44 | 45 | 1. 在RUN之前ADD scriptlets 46 | 47 | 如果你太早```ADD```所有的scriptlets在Dockerfile,您可能会遇到以下问题:你的脚本失败,你回去修改scriptlet并再次运行```docker build .```。但是你发现,Docker开始在首次加入scriptlets的地方构建!这会浪费了大量的时间和违背了使用快照的目的。 48 | 49 | 出现这种情况的原因是因为Docker如何追踪它的中间镜像(快照)。当Docker通过Dockerfile构建镜像时它会与中间镜像比较当前命令是否一致。然而,在```ADD```命令的情况下被装进镜像的文件里的内容也会被检查。这是有道理的。如果文件已改变就现有的中间镜像那么Docker将别无选择,只能从从这点开始建立一个新的镜像。只是没有办法可以知道这些变化不会影响到构建。这是必须要保守的即使他们没有。 50 | 51 | 此外,使用```RUN```命令要注意,每次运行时它将导致文件系统有不同的更改。在这种情况下,Docker会发现中间镜像并使用它,但是这将是错误的。```RUN```命令每次运行时必须造成文件系统相同的改变。举个例子,我确保在我的scriptlets我总是下载了一个已知版本的文件与一个特定MD5校验。 52 | 53 | 对Docker 构建缓存更详细的解释可以在[这里](https://docs.docker.com/articles/dockerfile_best-practices/#build-cache)找到。 54 | 55 | 2.不要使用ENV命令来设置环境变量。使用scriptlet。 56 | 57 | 它似乎看起来很有诱惑力:使用```ENV```命令来设置所有构建脚本需要的环境变量。但是,它不支持变量替换的方式,例如 ENV BASE=$HOME/base 将设置BASE的值为$HOME/base着很可能不是你想要的。 58 | 59 | 相反,我用```ADD```命令添加一个名为```set-env.sh```文件。此文件被包含在每个后续的scriptlet中: 60 | ```THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 61 | source $THIS_DIR/set-env-1.sh 62 | ``` 63 | 64 | 如果你没有在第一时间获取```set-env.sh```会怎么样呢?自从它很早就被加入Dockerfile并不意味着修改它将会使随后的快照无效? 65 | 66 | 是的,这将导致一些不雅。在开发脚本时,我发现,我已经错过了在```set-env.sh```添加一个有用的环境变量。解决方案是创建一个新的文件```set-env-1.sh```包含: 67 | 68 | ```THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 69 | source $THIS_DIR/set-env.sh 70 | if ! [ -e "$CONFIG_SUB_SRC/config.sub" ] ; then 71 | CONFIG_SUB_SRC=${CONFIG_SUB_SRC:-$NCURSES_SRC} 72 | fi 73 | ``` 74 | 75 | 然后,在所有后续的scriptlets文件中包含了此文件。现在,我已经完成了构建脚本,我可以回去解决这个问题了,但是,在某种意义上,它会破坏最初的目标。我将不得不从头开始运行构建脚本看看这种变化是否能成功。 76 | 77 | ####缺点 78 | 79 | 一个主要缺点是这种方法是,所构建的镜像尺寸是大于它实际需求的尺寸。在我的情况下尤其如此,因为我在最后删除了大量文件的。然而,这些文件都仍然存在于联合挂载文件系统的底层文件系统内,所以整个镜像是大于它实际需要的大小至少多余的是删除文件的大小。 80 | 81 | 然而,有一个变通。我没有公布此镜像到[Docker Hub Registry](https://registry.hub.docker.com/)。相反,我: 82 | 83 | - 使用```docker export```导出内容到tar文件。 84 | - 创建一个新的Dockerfile简单地添加了这个tar文件的内容。 85 | 86 | 产生尺寸尽可能小的镜像。 87 | 88 | ####结论 89 | 90 | 这种方法的优点是双重的: 91 | 92 | - 它使开发时间降至最低。不再做那些已经构建成功的子组件。你可以专注于那些失败的组件。 93 | - 这是伟大对于维护构建脚本。有一个机会 古怪的```RUN```命令在一段时间(即使它不应该)会改变其行为。构建可能会失败,但至少你不必再回到开头,一旦你解决了Dockerfile 94 | 95 | 此外,正如我前面提到的Docker不仅使写这些构建脚本更加容易。有了合适的工具同样可以在任何提供快照的文件系统实现。 96 | 97 | 构建快乐! 98 | 99 | [原文链接](http://lambdalog.seanseefried.com/posts/2014-12-12-docker-build-scripts.html) -------------------------------------------------------------------------------- /friends-cn.md: -------------------------------------------------------------------------------- 1 | [田浩浩](http://dockerone.com/people/llitfkitfk)、[陈杰](http://dockerone.com/people/Sonyfe25cp)、[肖劲](http://dockerone.com/people/amwtke)、[左伟](http://dockerone.com/people/%E5%B7%A6%E4%BC%9F)、[叶可强](http://dockerone.com/people/%E5%8F%B6%E5%8F%AF%E5%BC%BA)、[梁晓勇](http://dockerone.com/people/sean)、[王哲](http://dockerone.com/people/hessen)、[郭蕾](http://dockerone.com/people/%E9%83%AD%E8%95%BE)、[崔婧雯](http://dockerone.com/people/%E5%B4%94%E5%A9%A7%E9%9B%AF)、[吴佳兴](http://dockerone.com/people/colstuwjx)、[隋鑫](http://dockerone.com/people/jeffsui)、[吴锦晟](http://dockerone.com/people/%E5%90%B4%E9%94%A6%E6%99%9F) 、[钟最龙](http://dockerone.com/people/kurtzhong)、[孙科](http://dockerone.com/people/codesun)、[路兴强](http://dockerone.com/people/deerlux)、[刘凯宁](http://dockerone.com/people/%E5%88%98%E5%87%AF%E5%AE%81)、[张逸仙](http://dockerone.com/people/zyx_today) -------------------------------------------------------------------------------- /helloworld-docker.md: -------------------------------------------------------------------------------- 1 | ## 常用Dockerfile都会从一个已有的docker镜像里添加例如: 2 | 3 | {{{FROM ubuntu 4 | RUN apt-get install -y xxx 5 | ADD xxx 6 | CMD ["xxx"] 7 | }}} 8 | 9 | ## 这篇文章介绍 只输出helloworld的Docker容器如何创建 10 | 11 | ## 一个最简单的hello world 的例子 12 | 13 | ### 需求(WHAT): 14 | 15 | - Docker 16 | - Golang 17 | 18 | ### 开始(HOW): 19 | 20 | hello.go文件: 21 | 22 | ``` 23 | package main 24 | 25 | import "fmt" 26 | 27 | func main() { 28 | fmt.Printf("Hello, world.\n") 29 | } 30 | ``` 31 | 32 | Dockerfile文件: 33 | 34 | ``` 35 | FROM scratch 36 | ADD hello / 37 | CMD ["/hello"] 38 | ``` 39 | 40 | 41 | 生成可执行hello命令: 42 | 43 | ``` 44 | go build /pathToHello/hello.go 45 | ``` 46 | 47 | 将hello可执行文件移动到Dockerfile所在目录下,并打开此目录: 48 | 49 | ``` 50 | cp /pathToHello/hello /pathToDocker/ 51 | cd /pathToDocker/ 52 | ``` 53 | 54 | 构建最最最简单helloworld镜像: 55 | 56 | ``` 57 | docker build -t yourname/simplesthello . 58 | ``` 59 | 60 | 运行最最最简单helloworld容器: 61 | ```docker run yourname/simplesthello 62 | ``` 63 | 64 | <完> 65 | 66 | ### 原理(WHY): 67 | 1. scratch: 可以理解为docker镜像的root, 相对scratch来说每一个docker镜像相当于leaf 68 | 2. 静态编译: 使用Golang的原因, 如果用c的hellowrld的例子需要实现配置静态编译 69 | 3. - 70 | -------------------------------------------------------------------------------- /how-dockerize-webapp-with-postgres-cn.md: -------------------------------------------------------------------------------- 1 | docker:组装 使用Postgres数据库的web应用 2 | ========= 3 | 4 | [原文](http://fabioberger.com/blog/2014/12/19/how-to-dockerize-golang-webapp-with-postgres-db/) 5 | 6 | 最近我一直在学习Docker,并决定使用它来部署我一直编写的Golang Negroni + Postgres的web应用程序。 7 | 8 | 为了保证以后的可扩展性以及遵守最佳实践,这意味着需要两个docker容器:一个用于应用程序,另一个用于数据库, 并且连接两个容器。 9 | 10 | 由于第一次使用docker,所以跳了不少坑,因此把所有的步骤都分享给大家。 11 | 12 | 13 | 14 | 你可以找到本教程的演示程序:[Github.com/Dockerize-Tutorial](https://github.com/fabioberger/dockerize-tutorial) 15 | 16 | 本指南假定您已经安装了Docker,如果没有,[立即安装它](https://docs.docker.com/installation/)。 17 | 18 | ##第1步:组装Postgres的Docker容器 19 | 20 | 首先,下载并运行[Postgres官方Docker镜像](https://registry.hub.docker.com/_/postgres/)。给它命名为“db”,并设置默认的“Postgres的用户的密码: 21 | 22 | ``` 23 | docker run --name db -e POSTGRES_PASSWORD=password123 -d postgres 24 | ``` 25 | 26 | ```-d```的意思是运行这个容器作为守护进程,因此它会在后台悄悄运行。 27 | 28 | 接下来,创建应用程序的Postgres用户并创建数据库。要做到这一点,需要打开“db”容器的bash shell: 29 | 30 | ``` 31 | docker exec -it db /bin/bash 32 | ``` 33 | 34 | 从这个 bash shell 中登录 psql: 35 | 36 | ``` 37 | psql -U postgres 38 | ``` 39 | 40 | 在postgres交互的shell来创建用户和数据库: 41 | 42 | ``` 43 | CREATE USER app; 44 | CREATE DATABASE testapp; 45 | GRANT ALL PRIVILEGES ON DATABASE testapp TO app; 46 | ``` 47 | 48 | 然后退出psql (快捷键: CTRL-D)。现在,已经建立了数据库,接下来需要在这个容器中编辑配置文件,因此需要先安装一个文本编辑器(Vim!我看好你!)。 49 | 50 | ``` 51 | apt-get update && apt-get install vim 52 | ``` 53 | 54 | 然后使用Vim来编辑postgres的```pg_hba.conf```文件,此文件是用来管理客户端身份验证的。 55 | 我们需要修改这个文件来允许上文中的自定义用户能访问从应用程序容器到运行在此容器上数据库我们定制的Postgres用户。默认情况下,只有’postgres‘用户有这个权限。要找到此配置文件可以用以下命令: 56 | 57 | ``` 58 | psql -U postgres 59 | ``` 60 | 61 | 在postgres交互shell中运行: 62 | 63 | ``` 64 | SHOW hba_file; 65 | ``` 66 | 67 | 复制返回的文件路径并退出psql shell (快捷键: CTRL-D)。 68 | 现在,使用Vim编辑该文件: 69 | 70 | ``` 71 | vim /var/lib/postgresql/data/pg_hba.conf 72 | ``` 73 | 74 | 更改文件的最后一行,并保存更改,请键入```:wq```: 75 | 76 | ``` 77 | host all "app" 0.0.0.0/0 trust 78 | ``` 79 | 80 | 由于此配置文件只有当postgres启动的时候运行,现在我们需要重新启动db容器。退回到您的主机 (快捷键: CTRL-D),然后运行: 81 | 82 | ``` 83 | docker stop db 84 | docker start db 85 | ``` 86 | 87 | 修改之后的配置生效,Postgres容器就准备好了! 88 | 89 | ##第2步:组装的Golang应用 90 | 91 | 为了组装Go应用程序,我们必须在项目文件夹下创建Dockerfile。 92 | 如果你不想组装你已经在编写的Golang应用程序,你可以下载[Dockerize-tutorial Demo App](https://github.com/fabioberger/dockerize-tutorial)。在Golang项目的根目录下,创建Dockerfile: 93 | 94 | ``` 95 | touch Dockerfile 96 | ``` 97 | 98 | 在此文件里,我们将添加以下三行: 99 | 100 | ``` 101 | FROM golang:onbuild 102 | RUN go get bitbucket.org/liamstask/goose/cmd/goose 103 | EXPOSE 4000 104 | ``` 105 | 106 | 第一行 运行golang镜像的onbuild版本,它自动复制该数据包源,并获取该程序的依赖,然后建立程序,并配置其在启动时运行。 107 | 108 | 第二行 安装'goose',我们的迁移工具中的应用程序容器中, 109 | 110 | 最后一行 将开放端口4000。现在,我们可以为应用程序构建一个docker镜像。在项目目录中,运行: 111 | 112 | ``` 113 | docker build -t app . 114 | ``` 115 | 116 | 这个命令最终生成命名为‘app’的docker镜像。现在,我们可以通过这个docker镜像运行一个Docker的容器: 117 | 118 | ``` 119 | docker run -d -p 8080:4000 --name tutapp --link db:postgres app 120 | ``` 121 | 122 | 以上命令可分解为: 123 | 124 | * ```-d```让Docker运行此容器作为守护进程。 125 | * ```-p 8080:4000```让Docker将容器内的端口4000(此应用程序需要的端口)映射到主机的端口8080。 126 | * ```--name tutapp```命名docker容器为“tutapp”。 127 | * ```--link db:postgres``` 链接应用程序容器与之前创建的名为‘db’的postgres容器。 128 | 129 | 链接两个容器:我们的应用程序容器访问一个名为’$POSTGRES_PORT_5432_TCP_ADDR‘的环境变量。这个环境变量包含连接到Postgres DB时的主机地址。因此,我们必须确保我们的```dbconf.yml```文件里的host变量为: 130 | 131 | ``` 132 | db: 133 | driver: postgres 134 | open: host=$POSTGRES_PORT_5432_TCP_ADDR user=app dbname=testapp sslmode=disable 135 | ``` 136 | 137 | 在演示应用程序中的```config.go```文件替换该变量。 138 | 139 | 最后一步是运行DB迁移为我们的应用程序,因此在tutapp容器内与运行'goose up': 140 | 141 | ``` 142 | docker exec -it tutapp goose up 143 | ``` 144 | 145 | 要访问的此程序,访问 http://localhost:8080 你应该看到此应用程序运行! 146 | 147 | (如果用户的docker守护进程是在另一台机器(或虚拟机)上运行,用户应当将localhost更改为主机的地址。如果你是使用boot2docker在OS X或Windows,你可以找ip地址使用命令'boot2docker ip‘) 148 | 149 | 现在你成功运行了Golang应用程序并与在另一个Docker容器里的Postgres数据库通信。如果有什么不清楚或者如果不能运行,请留下了评论,我将第一时间完善! -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/images/README.md -------------------------------------------------------------------------------- /intro-to-docker-swarm-pre-cn.md: -------------------------------------------------------------------------------- 1 | 2 | 《Docker Swarm入门》系列之译者序 3 | Docker Swarm是Docker原生的集群系统 4 | 5 | 《Docker Swarm入门》系列讲述的是作者[Matt Bajor](https://github.com/technolo-g)在公司的骇客周里通过对Docker Swarm的学习研究,介绍了Docker Swarm及其配置举例,最后通过演示实例让读者更直观地了解Docker Swarm的精髓。 6 | 7 | 该系列第一部分介绍了Docker Swarm的概念以及它是如何工作的。 8 | 9 | 该系列第二部分介绍了Docker Swarm集群环境的配置需求并深入讲解了两大配置选项:服务发现与节点调度 10 | 11 | 该系列第三部分描述了如何围绕着Docker Swarm构建一个完整地SOA(面向服务的架构) 12 | 13 | 该系列第四部分通过一个示例给读者详细描述了Docker Swarm的工作流程。 14 | 15 | 该系列的翻译由本人完成,社区的王哲进行了内容校对。希望对大家有帮助,也请及时指出文中翻译不当的地方,方便改正。以下是这个系列的文章目录: 16 | 1. [Docker Swarm入门(一)概观](http://dockerone.com/article/168) 17 | 2. [Docker Swarm入门(二)配置选项与需求](http://dockerone.com/article/178) 18 | 3. [Docker Swarm入门(三)Swarm SOA举例](http://dockerone.com/article/192) 19 | 4. [Docker Swarm入门(四)Demo](http://dockerone.com/article/203) 20 | 21 | 22 | =========================== 23 | **译者介绍** 24 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 25 | 26 | -------------------------------------------------------------------------------- /intro-to-docker-swarm-pt1-overview-cn.md: -------------------------------------------------------------------------------- 1 | Docker Swarm入门(一) 概观 2 | 3 | 【编者的话】本文作者[Matt Bajor](https://github.com/technolo-g)热衷Docker及相关产品的研究,本文是他写的Docker Swarm入门系列的第一篇,主要介绍了Docker Swarm的概念以及它是如何工作的。 4 | 5 | ###什么是Docker Swarm 6 | 7 | [Docker Swarm](https://github.com/docker/swarm)是用于创建Docker主机集群的工具,它可以与之就像是与一台主机交互的。在前几天我参加的[Docker全球骇客日](http://www.meetup.com/Docker-meetups/events/148163592/ )的[DockerCon EU](http://blog.docker.com/2015/01/dockercon-eu-introducing-docker-swarm/)上有人给我介绍了它。在介绍骇客日的期间,[宣布了一些非常酷的新技术](http://blog.docker.com/2014/12/announcing-docker-machine-swarm-and-compose-for-orchestrating-distributed-apps/)包括Docker Swarm(译注:Docker集群工具)、Docker Machine(译注:Docker管理工具)以及Docker Compose(译注:Docker编排工具,类似于[Fig](http://dockerone.com/article/119))。由于Ansible填补了Machine与Compose的角色,Swarm的异军突起是我比较感兴趣的。 8 | 9 | 在大会介绍期间,Victor Vieux与Andrea Luzzardi公布了有关Swarm的基本运作的概念与论证,并提出了我认为非常有趣的声明。他们说虽然POC (概念验证)是功能齐全的以及能够演示的,但是他们要扔掉所有的代码而且要从头开始。我认为这是伟大的并且在POC一项新技术时要牢记这一点。 10 | 11 | 演示程序是用Go语言写的,Alpha软件绝对是在[最新的提交a0901ce8d6](https://github.com/docker/swarm/commit/a0901ce8d679e3ff0e13eee61e99407b4436bebd)这个时间点上。事情正在非常迅猛及时地发展以及功能特性几乎每天都有所不同。话虽这么说, [@vieux](https://github.com/vieux)对添加的功能以及通过[GitHub的问理](https://github.com/docker/swarm/issues)来修复bug都极为敏感。我不推荐它用于生产,但是它是一个非常有前途的技术。 12 | 13 | ###它是如何工作的 14 | 15 | 与Swarm的交互操作(通过设计)与对付单个Docker主机非常相似。它用现有的工具链使得有团队协作的能力,而无需作太多的修改(其中主要的有分裂构建的Swarm集群)。Swarm是运行在Linux机器上的守护程序,它绑定到与独立Docker实例相同的网络接口(http/2375或https/2376)。Swarm守护进程接受来自标准的Docker客户端`>=1.4.0`的连接以及代理他们回到Swarm背后的Docker守护进程,这也被标准的Docker端口监听。它可以基于Docker守护进程启动时几个不同的打包算法的结合以及标签来分发`create`命令。这使得由于单一Docker的endpoint而暴露的混乱Docker主机的分段集群的创建极其的简单。 16 | 17 | 与Swarm的交互“或多或少”与一个非集群的Docker实例交互是一样的,但是也有一些注意事项。不是所有的Docker命令都有1对1的支持。这是由于这两种体系结构与时间的根本原因。有些命令只是尚未实现,我想有些可能永远不会。眼下几乎一切需要运行容器的命令都是可用的,包括(还有其他的): 18 | 19 | * `docker run` 20 | * `docker create` 21 | * `docker inspect` 22 | * `docker kill` 23 | * `docker logs` 24 | * `docker start` 25 | 26 | 这些是运行该工具时所需的重要组成部分。这里是在最基本的配置中如何使用这些技术的概述: 27 | 28 | * Docker主机被`--label key=value`引入并网络监听。 29 | * Swarm守护进程被引入并被指向一个含有构成集群以及他们所监听的端口的Docker主机的列表文件。 30 | * Swarm联络每个Docker主机并确定他们的标记、健康以及资源量,来维护后端和它们元数据的列表。 31 | * 客户端通过它的网络端口(2375)与Swarm交互。你与Swarm的交互与你与Docker交互的方式相同:创建、销毁、运行、依附并获得运行容器的日志以及其他的一些东西。 32 | * 当一个命令发出给Swarm,Swarm会: 33 | * 基于提供的`constraint`标签、终端的健康程度以及调度算法来决定把命令发送到哪里。 34 | * 对适当的Docker守护进程执行命令 35 | * 返回结果跟Docker是同样的格式 36 | 37 | ![](http://technolo-g.com/images/Swarm_Diagram_Omnigraffle.jpg) 38 | 39 | Swarm守护程序本身只是一个调度器和路由器。它实际上并没有运行容器也就是说,如果Swarm停止了,它在终端Docker主机已置备容器仍是开启状态。另外,由于它不处理任何网络路由(网络连接需要被直接发送到终端Docker主机),运行的容器仍然可用即使Swarm守护程序关闭。当Swarm从这样的崩溃中恢复,它依然能够查询终端以重建其元数据的列表。 40 | 41 | 由于Swarm的设计,与Swarm的所有活动的交互都与其他Docker守护进程是几乎相同的:Docker 客户端、docker-py、docker-api gem等。构建命令至今尚未想通,但今天你可以在运行时得到。不幸的是,在这个时候Ansible似乎在[TLS 模式](https://github.com/ansible/ansible/issues/10032)下不能与Swarm合作,但它似乎也影响Docker守护程序本身不只是Swarm。 42 | 43 | 这是有关Docker Swarm博文的第一个篇。对于缺少技术细节我表示抱歉,但在随后的文章中会有的包括架构、代码片段以及一些实践活动 :) 留意[第二篇:Docker Swarm的配置选项与需求](),即将推出。 44 | 45 | 所有这些在博文背后的研究能成为可能归功于我工作的公司: [Rally Software in Boulder, CO.](https://www.rallydev.com/careers/open-positions)。每季度我们至少有一个骇客周,它使我们能够研究很棒的东西,像Docker Swarm。如果您想切入正题,直接开始Vagrant 例子,这里有一个repo,它是我在2014年Q1骇客周研究的成果: 46 | 47 | * [https://github.com/technolo-g/docker-swarm-demo](https://github.com/technolo-g/docker-swarm-demo) 48 | 49 | 50 | **原文链接:[Intro to Docker Swarm: Part 1 - Overview](https://github.com/docker/swarm) (翻译:[田浩浩](https://github.com/llitfkitfk))** 51 | 52 | =========================== 53 | **译者介绍** 54 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 55 | 56 | -------------------------------------------------------------------------------- /intro-to-docker-swarm-pt2-config-options-requirements-cn.md: -------------------------------------------------------------------------------- 1 | Docker Swarm入门(二)配置选项与需求 2 | 3 | 【编者的话】本文作者[Matt Bajor](https://github.com/technolo-g)热衷Docker及相关产品的研究,本文是他写的Docker Swarm入门系列的第二篇,主要介绍了Docker Swarm的最低需求配置。 4 | 5 | 6 | ##运行Docker Swarm集群的最低要求 7 | 8 | 创建一个Docker Swarm集群的最低要求确实很小。事实上,这绝对是可行的(虽然也许不是最好的做法)将Swarm运行在现有的Docker主机使其能够执行它而不用添加任何更多的硬件或虚拟资源。此外,在运行基于文件或[nodes(节点)](https://github.com/docker/swarm/tree/master/discovery#using-a-static-list-of-ips)发现机制时,并不需要其他的基础设施(当然除Docker)来运行一个基本Docker Swarm集群。 9 | 10 | 我个人认为在另一台机器上运行Swarm主控本身是一个不错的点子。该机器不需要沉重的资源,但它确实需要有很多的文件描述符来处理所有的TCP连接来来往往。在下面例子中,我会使用`dockerswarm01`来作为专用的Swarm主控。 11 | 12 | ##配置选项 13 | 14 | 在默认情况下Swarm有各种各样的配置设置,当涉及到运行守护程序及其配套的基础设置时,却给了它太多的灵活性。下面列出了不同类别的配置选项以及它们是如何进行配置的。 15 | 16 | ###发现 17 | 18 | Swarm使用的发现一种以维护该集群状态的机制。它可以与各种终端合作,但这一切几乎都是相同的概念: 19 | 20 | * 维护Docker节点列表终端应该是集群的一部分的。 21 | * 使用节点的列表,Swarm 检查每一个的健康状况以及跟踪在集群中进出的节点。 22 | 23 | ####节点发现 24 | 25 | 节点发现需要可以在命令行上传递的所有东西。这是最基本的发现机制类型,因为它不需要维护的配置文件之类的东西。Swarm进程使用节点发现的启动命令会是这样: 26 | {{{swarm manage \ 27 | --discovery dockerhost01:2375,dockerhost02:2375,dockerhost03:2375 \ 28 | -H=0.0.0.0:2375}}} 29 | 30 | ####文件发现 31 | 32 | 文件发现利用放置在文件系统中 (例如:/etc/swarm/cluster_config)的配置文件与`:`的格式来列出集群中的Docker主机。尽管该列表是静态的,healthchecking用于确定健康和不健康的节点以及过滤对不健康的节点请求的列表。基于文件的发现的启动命令和配置文件的例子如下: 33 | {{{swarm manage \ 34 | --discovery file:///etc/swarm/cluster_config \ 35 | -H=0.0.0.0:2375}}} 36 | {{{#/etc/swarm/cluster_config 37 | dockerhost01:2375 38 | dockerhost02:2375 39 | dockerhost03:2375}}} 40 | 41 | ####Consul发现 42 | 43 | Docker Swarm也支持Consul发现。它的工作原理是利用Consul的键值存储,以保持它的列表`:`被用于形成集群。在这种配置模式下,每个Docker主机运行在合并模式下的Swarm后台程序是指在Consul集群的HTTP接口。这对配置、运行时以及Docker主机安全提供了小的开销,但并不显著。Swarm客户端会这样做: 44 | {{{swarm join \ 45 | --discovery consul://consulhost01/swarm \ 46 | # This can be an internal IP as long as the other 47 | # Docker hosts can reach it. 48 | --addr=10.100.199.200:2375}}} 49 | 50 | 然后Swarm master从Consul里读取其主机列表。它可以这样运行: 51 | {{{swarm manage \ 52 | --discovery consul://consulhost01/swarm \ 53 | -H=0.0.0.0:2375}}} 54 | 55 | 这些键/值的基本配置模式提出了在Swarm客户端与Swarm相结合的合作模式下工作事如何healthchecks的疑问。由于在键/值存储中列表本身就是动态的,它是否需要运行的内部Swarm healthchecks呢?我不熟悉这个范围,所以不能说但值得注意的。 56 | ####EtcD发现 57 | EtcD发现工作方式与Consul发现大致相同。集群中每个Docker主机运行一个合作模式下的Swarm后台程序并指向一个EtcD的终端。这为EtcD提供了一个心跳来维护集群中活动服务器的列表。一个运行标准Docker守护进程的Docker主机会同时运行一个具有类似配置Swarm客户端: 58 | {{{swarm join \ 59 | --discovery etcd://etcdhost01/swarm \ 60 | --addr=10.100.199.200:2375}}} 61 | 62 | Docker Swarm master 通过以下命令会连接EtcD,搜寻提供的路径以及生成节点列表: 63 | {{{swarm manage \ 64 | --discovery etcd://etcdhost01/swarm \ 65 | -H=0.0.0.0:2375}}} 66 | 67 | ####Zookeeper发现 68 | 69 | Zookeeper发现与其他键/值存储的基本配置模式一样遵循相同的模式。一个ZK集合被创建用来保存主机列表信息以及与Docker一起的客户端的运行,为了在键/值存储中保持心跳;接近实时地维护该列表。该Swarm master还连接到集合并使用`/swarm`路径下的的信息来维护主机的列表清单(其然后会做healthchecks)。 70 | 71 | Swarm客户端(与Docker一起): 72 | {{{swarm join \ 73 | # All hosts in the ensemble should be listed 74 | --discovery zk://zkhost01,zkhost02,zkhost03/swarm \ 75 | --addr=10.100.199.200}}} 76 | 77 | Swarm Master: 78 | {{{swarm manage \ 79 | --discovery zk://zkhost01,zkhost02,zkhost03/swarm \ 80 | -H 0.0.0.0:2375}}} 81 | ####Hosted Token Based 发现 (默认) 82 | 我还没有使用过这个功能,在这点上没多少能总结的 83 | 84 | ###调度 85 | 调度的机制是选择在哪里创建并启动一个容器。它是由一个包装算法和过滤器(或标签)组合而成。每个Docker守护进程带着一组标签来启动,像这样的: 86 | {{{docker -d \ 87 | --label storage=ssd \ 88 | --label zone=external \ 89 | --label tier=data \ 90 | -H tcp://0.0.0.0:2375}}} 91 | 92 | 然后,当开启一个Docker容器时,Swarm将基于过滤器选择一组机器,然后根据其调度分配每次运行命令。过滤器会告诉Swarm哪个地方容器可以运行或者不可以在可用的主机之间。以下是几个过滤机制: 93 | 94 | ####Constraint(约束) 95 | 这是利用了一个Docker守护程序开始的标签。目前,它只支持'=',(译注:[官方doc](https://github.com/docker/swarm/tree/master/scheduler/filter)里是'==',请读者遵循最新的官方doc)但在将来它可能支持'!='。节点必须符合所有的由容器提供的约束以便匹配调度。如下例子来开启有一些约束的容器: 96 | {{{docker run -d -P \ 97 | -e constraint:storage=ssd \ 98 | -e constraint:zone=external \ 99 | -t nginx}}} 100 | 101 | ####Affinity (类同) 102 | 类同可以以两种方式工作:容器类同或镜像类同。为了在同一台主机上启动两个容器可以运行以下命令: 103 | {{{docker run -d -P \ 104 | --name nginx \ 105 | -t nginx 106 | 107 | docker run -d -P \ 108 | --name mysql \ 109 | -e affinity:container=nginx \ 110 | -t mysql}}} 111 | 112 | 由于Swarm不处理镜像管理,设置类同镜像也是可行的。这意味着一个容器仅能在已包含镜像的节点上启动。这就否定了在开启一个容器之前需要在后台等待拉取镜像。举个例子: 113 | {{{docker run -d -P \ 114 | --name nginx \ 115 | -e affinity:image=nginx \ 116 | -t nginx}}} 117 | 118 | ####Port (端口) 119 | 端口过滤不允许在同一主机上任意两个容器具有相同的静态端口映射的启动。这样做的意义很大,你不能重复在Docker主机上端口映射。例如,两个节点以`-p 80:80`开启不会被允许在同一Docker主机上运行。 120 | 121 | ####Healthy(健康状况) 122 | 它阻止了不健康节点的调度。 123 | 124 | 一旦Swarm将主机列表放置到一组匹配上述过滤的节点上,然后他会将此容器安排在某一节点上。目前以下调度是内置的: 125 | 126 | ####Random(随机分布) 127 | 将容器随机分布在可用的终端上。 128 | 129 | ####Binpacking 130 | 用容器将节点填满,然后移动到下一个。此模式具有在运行时向每个容器分配静态资源量的增量复杂性。这意味着设置容器的内存和CPU的限制可能会或不会万无一失。我个人比较喜欢让容器彼此之间争夺,看看谁能得到的资源。 131 | 132 | 正在进行中的是[平衡策略](https://github.com/docker/swarm/pull/227)以及添加[Apache Mesos](https://github.com/docker/swarm/issues/214)功能。 133 | 134 | ###TLS([Transport Layer Security(传输层安全协议)](http://www.wikiwand.com/en/Transport_Layer_Security)) 135 | 我很高兴地说,Swarm可在TLS启用下运行。这使得客户端和Swarm守护进程之间当然还有Swarm守护进程和Docker守护程序之间更安全。这是一件好事,因为我的研究安全方面的伙伴说,在网络中并没有边界。 136 | 137 | ![](http://technolo-g.com/images/SSL-security.jpg) 138 | 139 | 它确实需要一个包含CA的完整PKI,但我有个解决办法在另一篇已发布的文章里会提到,它是[如何为Docker与Swarm产生所需的TLS证书](http://technolo-g.com/generate-ssl-for-docker-swarm/)。 140 | 141 | 按我的其他博客文章,一旦证书生成并安装,在Docker与Swarm守护进程就可以这样做: 142 | Docker: 143 | {{{docker -d \ 144 | --tlsverify \ 145 | --tlscacert=/etc/pki/tls/certs/ca.pem \ 146 | --tlscert=/etc/pki/tls/certs/dockerhost01-cert.pem \ 147 | --tlskey=/etc/pki/tls/private/dockerhost01-key.pem \ 148 | -H tcp://0.0.0.0:2376}}} 149 | 150 | Swarm master: 151 | {{{swarm manage \ 152 | --tlsverify \ 153 | --tlscacert=/etc/pki/tls/certs/ca.pem \ 154 | --tlscert=/etc/pki/tls/certs/swarm-cert.pem \ 155 | --tlskey=/etc/pki/tls/private/swarm-key.pem \ 156 | --discovery file:///etc/swarm_config \ 157 | -H tcp://0.0.0.0:2376}}} 158 | 159 | 然后客户端必须知道TLS连接。以下是环境变量设置: 160 | {{{export DOCKER_HOST=tcp://dockerswarm01:2376 161 | export DOCKER_CERT_PATH="`pwd`" 162 | export DOCKER_TLS_VERIFY=1}}} 163 | 164 | 这样你就设置好了TLS。 165 | 166 | ##还有更多! 167 | 当涉及到复杂的集群软件的配置,还是很多要谈论的,但我觉得这是一个足够好的概观,让你设置、运行并思考如何配置你的Swarm集群。在下一篇我会制定出一些架构例子有关Swarm集群的。敬请关注,并欢迎在下面发表评论! 168 | 169 | 所有这些在博文背后的研究能成为可能归功于我工作的公司: [Rally Software in Boulder, CO.](https://www.rallydev.com/careers/open-positions)。每季度我们至少有一个骇客周,它使我们能够研究很棒的东西,像Docker Swarm。如果您想切入正题,直接开始Vagrant 例子,这里有一个repo,它是我在2014年Q1骇客周研究的成果: 170 | 171 | * [https://github.com/technolo-g/docker-swarm-demo](https://github.com/technolo-g/docker-swarm-demo) 172 | 173 | **原文链接:[Intro to Docker Swarm: Part 2 - Configuration Options and Requirements](http://technolo-g.com/intro-to-docker-swarm-pt2-config-options-requirements/) (翻译:[田浩浩](https://github.com/llitfkitfk))** 174 | 175 | =========================== 176 | **译者介绍** 177 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 178 | 179 | ------------------------------------------ 180 | [Docker Swarm入门(一)概观](http://dockerone.com/article/168) 181 | [Docker Swarm入门(二)配置选项与需求](http://dockerone.com/article/178) -------------------------------------------------------------------------------- /intro-to-docker-swarm-pt3-example-architechture-cn.md: -------------------------------------------------------------------------------- 1 | Docker Swarm入门(三)Swarm SOA举例 2 | 3 | 【编者的话】本文作者[Matt Bajor](https://github.com/technolo-g)热衷Docker及相关产品的研究,本文是他写的Docker Swarm入门系列的第三篇,通过举例介绍了Docker Swarm SOA。 4 | 5 | Docker Swarm带来的最令人兴奋的事情之一是用非常小的开销就能创造现代化的、有弹性的以及灵活的架构的能力。能够与Docker主机的多样化集群交互就好像与在一台可以用现有的工具链的主机中交互的一样,来建立我们的精美而简单的SOA([Service-oriented architecture 面向服务的架构](http://www.wikiwand.com/en/Service-oriented_architecture))所需的一切东西! 6 | 7 | 本文将要试图围绕着Docker Swarm描述一个完整的SOA架构,其具有以下属性: 8 | 9 | * **管理程序层**由独立的Docker主机组成(Docker/Registrator) 10 | * **集群层**将Docker主机绑在一起(Docker Swarm) 11 | * **服务发现层**(Consul) 12 | * **路由层**在基于Consul有关服务下指挥交通(HAProxy/Nginx) 13 | 14 | ###管理程序层 15 | 16 | 管理程序层是由一组离散Docker主机构成。每个主机上都有运行服务,允许其参与: 17 | 18 | * **Docker daemon**:配置Docker守护程序来监听网络端口以及本地Linux socket,这样Swarm守护程序就可以与它进行通信。此外,配置每个Dockerhost与Swarm调度一起在一组标签上运行以决定容器被置于哪里。他们有助于描述Docker主机而且不论在哪任何标识信息都可以与Docker主机相关联。这是一个Docker主机被启动时一组标签的例子: 19 | * 范围:应用程序/数据库 20 | * 硬盘:ssd/hdd 21 | * 环境:开发/生产 22 | * **Swarm daemon**:Swarm客户端与Docker守护进程一起运行,以保持该节点在Swarm集群中运行。该Swarm守护在合作模式下运行并且对Consul保持基本的心跳以保持其记录在`/swarm`位置的更新。这个记录是Swarm master用于创建集群的。如果此进程被毁坏,name列表中Consul会自动地删除该节点。Swarm客户端在Consul会使用像`/swarm`的路径,它包含Docker主机的列表: 23 | 24 | ![](http://technolo-g.com/images/consul/swarm_section.png) 25 | 26 | * **Registrator daemon**:当创建或者销毁容器时,[Registrator](https://github.com/progrium/registrator)是用来更新Consul的。它监听Docker socket以及接下来的每个事件将会更新Consul 的键/值。例如,一个名叫deepthought的应用需要3个在分离的主机的实例并运行在80端口上,其会在Consul创建一个结构如下: 27 | 28 | ![](http://technolo-g.com/images/consul/services_section.png) 29 | 30 | 模式: 31 | `/services/-/::` 结果是: `:` 32 | 33 | * service:容器的景象名字 34 | * port:容器公开的端口 35 | * dhost:容器运行的Docker主机 36 | * cport:容器公开的端口 37 | * ipaddress:容器运行的Docker主机的ip地址 38 | 39 | `docker ps` 会输出以下类似信息: 40 | {{{$ docker ps 41 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 42 | 097e142c1263 mbajor/deepthought:latest "nginx -g 'daemon of 17 seconds ago Up 13 seconds 10.100.199.203:49166->80/tcp dockerhost03/grave_goldstine 43 | 1f7f3bb944cc mbajor/deepthought:latest "nginx -g 'daemon of 18 seconds ago Up 14 seconds 10.100.199.201:49164->80/tcp dockerhost01/determined_hypatia 44 | 127641ff7d37 mbajor/deepthought:latest "nginx -g 'daemon of 20 seconds ago Up 16 seconds 10.100.199.202:49158->80/tcp dockerhost02/thirsty_babbage}}} 45 | 46 | 这是记录服务和位置的最基本方法。 Registrator还支持传递元数据到[含有有关服务](https://github.com/progrium/registrator#single-service-with-metadata)密钥信息的容器。 47 | 48 | 另一个要提的是Registrator的验证似乎想要作为一个守护进程运行在Docker容器内。由于Docker Swarm集群被当做是一台Docker主机,我宁愿Registrator应用作为守护程序运行在Docker主机上。这样会使得在没有容器运行在集群上时群集仍然在运作。这似乎是一个很合适的地方来绘制平台和应用之间的界限。 49 | 50 | ###集群层 51 | 在这一层,有着Docker master在运行。配置它是为读取在`/swarm`前缀下Consul的键/值的存储以及它产生的节点信息列表。同时它也在监听客户端与Docker(创建,删除,等..)的链接和发送路由那些与对应Docker主机终端的请求。这意味着,它具有以下需求: 52 | 53 | * 能够监听网络 54 | * 能够与Consul通信 55 | * 能够与所有的Docker守护进程进行通信 56 | 57 | 由于我还没有看到提到有关制作Swarm守护程序本身,但研究了它之后,真的似乎没有任何理由它不能成的。我期待带有TCP支持的负载平衡代理(HAProxy)可以放在几个相对缓和的Swarm守护进程的前端。相黏会话必须启用并且可能是一个积极或者消极的状态,如果有多个Swarm守护进程之间的状态同步问题,但现在看来它似乎是可行的。由于容器是继续运行并且可访问的,即使在一个Swarm故障的情况下,我们会接受过于复杂与负载均衡节点的开销的non-ha Swarm节点的风险。这样的权衡不错吧? 58 | 59 | ###服务发现层 60 | 该服务发现层是运行一个Consul集群节点上;具体来说它是键/值存储的。为了维持法定个数(n/2 + 1个节点)即使在出现故障的情况下节点数也应该是个奇数。Consul有一个[非常大的特征集](http://www.consul.io/docs/index.html),仅举几例包括自动服务发现、健康检查与键/值存储。我们只使用了键/值存储,但我希望结合Consul的其他方面到您的架构也能带来诸多好处。对于此配置示例,以下是处理是作用在键/值的存储: 61 | 62 | * 在Docker主机的Swarm客户群会在`/swarm`中注册 63 | * Swarm master会读取`/swarm`以构建其Docker主机列表 64 | * 该Registrator守护进程会接待节点在`/services`前缀的进出 65 | * Consul-template会读取键/值存储为路由层生成配置 66 | 67 | 这是中央数据存储为了所有群集的元数据。Consul是用来绑定在Docker主机上的容器与路由终端的条目。 68 | 69 | Consul也有一个可安装的GUI(图形化用户界面)而且我强烈建议安装它用来开发工作。它可以使你搞清楚什么已经被注册以及哪里更容易做。一旦集群启动并运行,尽管你可能就不再需要它了。 70 | 71 | ###路由层 72 | 这是个边缘层,而且所有外部应用流量不管什么都将贯穿其中。这些节点都在Swarm集群的边缘,他们是静态的IP'd并且具有可CNAME'd到群集上的任何服务的DNS条目。这些节点监听端口80/443等,并有以下服务运行: 73 | 74 | * Consul-template:此程序是轮询Consul的键/值存储(在`/services`下并在检测变化时,它会写入新的HAProxy/Nginx配置并优雅重新加载该服务。这些模板是用Go编写的以及输出应该是标准的HAProxy或Nginx形式。 75 | 76 | * HAProxy或Nginx:这些服务器都是充分证明了战斗力的,并准备好所需要任何东西,即使是在边缘。Consul-template动态配置该服务并在需要时重新加载。这种主要变化的频繁出现的情况主要是终端的一个特定的虚拟主机列表的修改。由于是现存的东西来维护列表并且在Consul中,它的变化和容器一样频繁。 77 | 78 | 这是基于SOA的一个泊Docker Swarm集群的简要概述。在接下来的文章中,我将演示一个上述在Vagrant环境下的工作基础设施。这篇文章将会在[Docker Denver Meetup](http://www.meetup.com/Docker-Denver/events/218859311/) 后发布,敬请关注! 79 | 80 | 81 | 82 | 所有这些在博文背后的研究能成为可能归功于我工作的公司: [Rally Software in Boulder, CO.](https://www.rallydev.com/careers/open-positions)。每季度我们至少有一个骇客周,它使我们能够研究很棒的东西,像Docker Swarm。如果您想切入正题,直接开始Vagrant 例子,这里有一个repo,它是我在2014年Q1骇客周研究的成果: 83 | 84 | * [https://github.com/technolo-g/docker-swarm-demo](https://github.com/technolo-g/docker-swarm-demo) 85 | 86 | **原文链接:[Intro to Docker Swarm: Part 3 - Example Swarm SOA](http://technolo-g.com/intro-to-docker-swarm-pt3-example-architechture/) (翻译:[田浩浩](https://github.com/llitfkitfk))** 87 | 88 | =========================== 89 | **译者介绍** 90 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 91 | 92 | ------------------------------------------ 93 | [Docker Swarm入门(一)概观](http://dockerone.com/article/168) 94 | [Docker Swarm入门(二)配置选项与需求](http://dockerone.com/article/178) 95 | [Docker Swarm入门(三)Swarm SOA举例](http://dockerone.com/article/192) 96 | 97 | -------------------------------------------------------------------------------- /intro-to-docker-swarm-pt4-demo-cn.md: -------------------------------------------------------------------------------- 1 | Docker Swarm入门(四)Demo 2 | 3 | 【编者的话】本文作者[Matt Bajor](https://github.com/technolo-g)热衷Docker及相关产品的研究,本文是他写的Docker Swarm入门系列的第四篇,通过一个demo让读者更好地了解Docker Swarm。 4 | 5 | ##[Vagrant up](https://www.vagrantup.com/)开启! 6 | 7 | 我的骇客周努力的主要方向是在Vagrant环境下的Docker Swarm集群。这篇文章将看看如何使它运行起来以及如何与它进行交互。 8 | 9 | ###它是什么? 10 | 11 | 这是包含在Vagrant环境里功能齐全的Docker Swarm集群。其包括4个节点: 12 | * dockerhost01 13 | * dockerhost02 14 | * dockerhost03 15 | * dockerswarm01 16 | 17 | Docker节点(dockerhost01 - 3)正在运行的Docker守护进程,以及一些配套服务。对Docker主机感兴趣的主要过程是: 18 | 19 | * **Docker daemon**:带着一组标签运行 20 | * **Registrator daemon**:这个守护进程连接到Consul为了注册和注销有端口公开的容器。从这项服务中的条目可以在`/services`路径下的Consul的键/值存储中可以看到 21 | * **Swarm client**:Swarm客户端是维护在Consul里地Swarm节点列表。此列表保存在`/swarm`下,包含集群中参与的Swarm节点`:`的列表 22 | 23 | Docker Swarm节点(dockerswarm01)也运行了几个服务。由于这仅仅是一个例子,很多服务都总结成一个单一的机器。对于生产环境,我不推荐这样的布局。 24 | 25 | * **Swarm daemon**:作为master以及监听在网络上Docker命令而代理他们到Docker主机 26 | * **Consul**:单个节点Consul实例的运行。它的用户界面在[这里](http://dockerswarm01/ui/#/test/) 27 | * **Nginx**:为UI代理其到Consul 28 | 29 | ##如何提供集群 30 | 31 | ###1.安装前提 32 | 33 | * **GitHub Repo**:[docker-swarm-demo](https://github.com/technolo-g/docker-swarm-demo) 34 | * **Vagrant(最新)**:[Vagrant下载](https://www.vagrantup.com/downloads.html) 35 | * **Vagrant主机插件**:`vagrant plugin install vagrant-hosts` 36 | * **VirtualBox**:[VirtualBox下载](https://www.virtualbox.org/wiki/Downloads) 37 | * **Ansible**:`brew install ansible` 38 | * **主机条目**:添加以下行到`/etc/hosts`文件: 39 | 40 | {{{10.100.199.200 dockerswarm01 41 | 10.100.199.201 dockerhost01 42 | 10.100.199.202 dockerhost02 43 | 10.100.199.203 dockerhost03}}} 44 | 45 | ###2a. Clone && Vagrant up(无[TLS](http://www.wikiwand.com/en/Transport_Layer_Security)) 46 | 这个过程可能会耗费一些时间要下载几G的数据。这种情况是不使用TLS的。如果你想要Swarm使用TLS,去步骤2b。 47 | 48 | {{{# Clone our repo 49 | git clone https://github.com/technolo-g/docker-swarm-demo.git 50 | cd docker-swarm-demo 51 | 52 | # Bring up the cluster with Vagrant 53 | vagrant up 54 | 55 | # Provision the host files on the vagrant hosts 56 | vagrant provision --provision-with hosts 57 | 58 | # Activate your enviornment 59 | source bin/env}}} 60 | 61 | ###2b. Clone && Vagrant up(有[TLS](http://www.wikiwand.com/en/Transport_Layer_Security)) 62 | 这种情况会生成证书而且集群会启用TLS。 63 | 64 | {{{# Clone our repo 65 | git clone https://github.com/technolo-g/docker-swarm-demo.git 66 | cd docker-swarm-demo 67 | 68 | # Generate Certs 69 | ./bin/gen_ssl.sh 70 | 71 | # Enable TLS for the cluster 72 | echo -e "use_tls: True\ndocker_port: 2376" > ansible/group_vars/all.yml 73 | 74 | # Bring up the cluster with Vagrant 75 | vagrant up 76 | 77 | # Provision the host files on the vagrant hosts 78 | vagrant provision --provision-with hosts 79 | 80 | # Activate your TLS enabled enviornment 81 | source bin/env_tls}}} 82 | 83 | ###3. 确认正常工作 84 | 85 | 现在,集群配置并运行,你应该能够确认。我们会做一些方法。首先看一下Docker 客户端: 86 | {{{$ docker version 87 | Client version: 1.4.1 88 | Client API version: 1.16 89 | Go version (client): go1.4 90 | Git commit (client): 5bc2ff8 91 | OS/Arch (client): darwin/amd64 92 | Server version: swarm/0.0.1 93 | Server API version: 1.16 94 | Go version (server): go1.2.1 95 | Git commit (server): n/a 96 | 97 | $ docker info 98 | Containers: 0 99 | Nodes: 3 100 | dockerhost02: 10.100.199.202:2376 101 | dockerhost01: 10.100.199.201:2376 102 | dockerhost03: 10.100.199.203:2376}}} 103 | 104 | 然后在浏览器中浏览Consul:[http://dockerswarm01/ui/#/test/kv/swarm/](http://dockerswarm01/ui/#/test/kv/swarm/),并确认Docker主机以及它的端口被列出如下: 105 | ![](http://technolo-g.com/images/consul_swarm_cluster.png) 106 | 107 | 集群好像可以正常工作了,因此让我们在上边配置一个应用! 108 | 109 | ##如何使用它 110 | 现在,您可以与Swarm集群交互来配置节点。在Vagrant配置期间这个演示中镜像已经下载完成,因此使这些命令应该可以使用为了加快2倍的外部代理容器和3倍的内部Web应用程序容器。关于命令有两件事情需要注意: 111 | 112 | * 当Docker开启后,约束需要与被分配标签匹配。这就是Swarm的过滤器如何知道哪些Docker主机可供调度。 113 | * SERVICE_NAME变量是为Registrator设置的。由于我们使用的是通用的容器(Nginx)我们以这种方式来代替指定服务名称。 114 | {{{# Primary load balancer 115 | docker run -d \ 116 | -e constraint:zone==external \ 117 | -e constraint:status==master \ 118 | -e SERVICE_NAME=proxy \ 119 | -p 80:80 \ 120 | nginx:latest 121 | 122 | # Secondary load balancer 123 | docker run -d \ 124 | -e constraint:zone==external \ 125 | -e constraint:status==non-master \ 126 | -e SERVICE_NAME=proxy \ 127 | -p 80:80 \ 128 | nginx:latest 129 | 130 | # 3 Instances of the webapp 131 | docker run -d \ 132 | -e constraint:zone==internal \ 133 | -e SERVICE_NAME=webapp \ 134 | -p 80 \ 135 | nginx:latest 136 | 137 | docker run -d \ 138 | -e constraint:zone==internal \ 139 | -e SERVICE_NAME=webapp \ 140 | -p 80 \ 141 | nginx:latest 142 | 143 | docker run -d \ 144 | -e constraint:zone==internal \ 145 | -e SERVICE_NAME=webapp \ 146 | -p 80 \ 147 | nginx:latest}}} 148 | 149 | 现在你如果运行`docker ps` 或者 点[这里](http://dockerswarm01/ui/#/test/kv/services/)浏览Consul会得到如下: 150 | 151 | ![](http://technolo-g.com/images/consul_services.png) 152 | 153 | 你可以看到两项已注册服务!由于路由和服务发现部分是额外的,这个程序实际上不会工作,但我觉得你已经有了思路。 154 | 155 | 我希望你喜欢有关Docker Swarm的这个系列。我发现Docker Swarm是由一个伟大的且非常敏捷的团队开发的一个前景广阔的应用。我相信它将改变我们对待我们的Docker主机的方式以及当运行复杂的应用程序时,将大大简化所要做的事。 156 | 157 | 所有这些在博文背后的研究能成为可能归功于我工作的公司: [Rally Software in Boulder, CO.](https://www.rallydev.com/careers/open-positions)。每季度我们至少有一个骇客周,它使我们能够研究很棒的东西,像Docker Swarm。如果您想切入正题,直接开始Vagrant 例子,这里有一个repo,它是我在2014年Q1骇客周研究的成果: 158 | 159 | * [https://github.com/technolo-g/docker-swarm-demo](https://github.com/technolo-g/docker-swarm-demo) 160 | 161 | **原文链接:[Intro to Docker Swarm: Part 4 - Demo](http://technolo-g.com/intro-to-docker-swarm-pt4-demo/) (翻译:[田浩浩](https://github.com/llitfkitfk))** 162 | 163 | =========================== 164 | **译者介绍** 165 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 166 | 167 | ------------------------------------------ 168 | [Docker Swarm入门(一)概观](http://dockerone.com/article/168) 169 | [Docker Swarm入门(二)配置选项与需求](http://dockerone.com/article/178) 170 | [Docker Swarm入门(三)Swarm SOA举例](http://dockerone.com/article/192) 171 | [Docker Swarm入门(四)Demo](http://dockerone.com/article/203) -------------------------------------------------------------------------------- /machine-swarm-compose-intergration-cn.md: -------------------------------------------------------------------------------- 1 | 【教程】如何从零开始搭建Docker Swarm集群 2 | 3 | 【编者的话】本文主要介绍了如何从头搭建Docker Swarm集群,参照了Youtube视频[Demo of Machine + Swarm + compose integration](https://www.youtube.com/watch?v=M4PFY6RZQHQ)与[Demo of Docker Swarm Beta](https://www.youtube.com/watch?v=VlZnVC-91Y0)以及官方的[Docker Swarm文档](https://docs.docker.com/swarm/),借以给读者朋友提供更为直观地Swarm演示示例。 4 | 5 | ###两台节点主机: 6 | 1. A:192.168.20.1 7 | 2. B:192.168.20.2 8 | 9 | ###检查节点Docker配置 10 | 11 | * 打开Docker配置文件(示例是centos 7) 12 | ``` 13 | vim /etc/sysconfig/docker 14 | ``` 15 | 16 | * 添加`-H tcp://0.0.0.0:2375`到`OPTIONS` 17 | ``` 18 | OPTIONS='-g /cutome-path/docker -H tcp://0.0.0.0:2375' 19 | ``` 20 | 21 | * 某些需要另外添加`-H unix:///var/run/docker.sock` 22 | ``` 23 | OPTIONS='-g /mnt/docker -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock' 24 | ``` 25 | 26 | ###分别给A、B节点安装swarm 27 | ``` 28 | $ docker pull swarm 29 | ``` 30 | 31 | ###生成集群token(一次) 32 | 33 | ``` 34 | $ docker run --rm swarm create 35 | 6856663cdefdec325839a4b7e1de38e8 36 | 37 | ``` 38 | 其中`6856663cdefdec325839a4b7e1de38e8`就是我们将要创建集群的token 39 | 40 | ###添加节点A、B到集群 41 | ``` 42 | $ docker run -d swarm join --addr=192.168.20.1:2375 token://6856663cdefdec325839a4b7e1de38e8 43 | 44 | $ docker run -d swarm join --addr=192.168.20.2:2375 token://6856663cdefdec325839a4b7e1de38e8 45 | ``` 46 | * 列出集群A、B节点 47 | 48 | ``` 49 | $ docker run --rm swarm list token://6856663cdefdec325839a4b7e1de38e8 50 | 51 | 192.168.20.1:2375 52 | 192.168.20.2:2375 53 | ``` 54 | 55 | ###集群管理: 56 | * 在任何一台主机A、B或者C(C:192.168.20.3)上开启管理程序。例如在C主机开启: 57 | 58 | ``` 59 | $ docker run -d -p 8888:2375 swarm manage token://6856663cdefdec325839a4b7e1de38e8 60 | ``` 61 | 62 | * 现在你就可以在主机C上管理集群A、B: 63 | 64 | ``` 65 | $ docker -H 192.168.20.3:8888 info 66 | $ docker -H 192.168.20.3:8888 ps 67 | $ docker -H 192.168.20.3:8888 logs ... 68 | ``` 69 | 70 | ###在集群上运行容器 71 | 72 | ``` 73 | $ docker -H 192.168.20.3:8888 run -d --name web1 nginx 74 | $ docker -H 192.168.20.3:8888 run -d --name web2 nginx 75 | $ docker -H 192.168.20.3:8888 run -d --name web3 nginx 76 | $ docker -H 192.168.20.3:8888 run -d --name web4 nginx 77 | $ docker -H 192.168.20.3:8888 run -d --name web5 nginx 78 | ``` 79 | 80 | * 查看集群A、B内的容器 81 | 82 | ``` 83 | $ docker -H 192.168.20.3:8888 ps -a 84 | ``` 85 | 86 | * 结果如下: 87 | 88 | ``` 89 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 90 | 4cc1f232fb18 nginx:latest "nginx -g 'daemon of 16 hours ago Up 16 hours 80/tcp, 443/tcp Host-A/web5 91 | e8327939721a nginx:latest "nginx -g 'daemon of 16 hours ago Up 16 hours 443/tcp, 80/tcp Host-A/web3 92 | 35e08c4a1b43 nginx:1 "nginx -g 'daemon of 23 hours ago Up 16 hours 443/tcp, 80/tcp Host-B/web4 93 | 9bd07067620d nginx:1 "nginx -g 'daemon of 23 hours ago Up 16 hours 443/tcp, 80/tcp Host-B/web2 94 | 626fe5b1dcfa nginx:1 "nginx -g 'daemon of 23 hours ago Up 16 hours 80/tcp, 443/tcp Host-B/web1 95 | 96 | ``` 97 | * 其中`NAMES`列里面:`/`前边是节点名字,后边是在节点内创建的容器名字 98 | 99 | =========================== 100 | **译者介绍** 101 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /middleware/Linux_kernel_and_gaming_input-output_latency.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/middleware/Linux_kernel_and_gaming_input-output_latency.svg.png -------------------------------------------------------------------------------- /orchestration-toolkit-release-aims-prove-dockers-commitment-flexibility-community-ecosystem-cn.md: -------------------------------------------------------------------------------- 1 | 2 | ##Docker 发布编排工具包 3 | 4 | 【编者的话】Docker终于发布了编排工具Machine、Swarm以及Compose虽然Machine与Swarm还不是正式版本,但是读者们可以在官网doc里预览并参考这三个工具的文档,并加以对分布式应用的了解。 5 | 6 | Docker今天发布了其在12月份欧洲DockerCon上宣布并承诺的编排工具包。这三个编排工具的目的是展示Docker的跨数据服务器托管的基础设施的“100%的可移植性”,包括实现混合云架构来运行容器式或分布式应用程序。 7 | 8 | 当去年在阿姆斯特丹宣布编排工具包时,间接地回应起初社区对此问题的关切,Docker着重的是自由,开发商将不得不控制如何在生产环境中构建、装载与启用多容器,多主机应用程序。 9 | 10 | 这三个编排工具有:Docker Machine、Docker Swarm与Docker Compose。Machine与Swarm是beta版,而Compose发布的是1.1版本。 11 | 12 | 13 | ###Docker Machine 14 | [Docker Machine](https://docs.docker.com/machine/)使一命令自动化,以提供一个主机的基础设施并安装Docker引擎。Docker公司企业营销副总裁兼系统管理员与运维-David Messina说它非常适合于混合环境中,对于每个数据托管基础设施供应商,“再也不用学习一套独立的命令来获取并启用一个Docker容器应用”。Messina还说过对于Docker Machine,用户可以使用一个统一的命令来削减跨基础设施的成本。beta版本中有十二驱动程序,其中包括Amazon EC2,Google Cloud Engine,Digital Ocean与VMware。 15 | 16 | 至今无法量化他们的用户量的Docker可能会寻求跨混合公共云架构的协调分布式应用程序,但Messina承认“最终,大体上我们的目标是:Machine是关键的推动者,这是肯定的“。 17 | 18 | ###Docker Swarm 19 | [Docker Swarm](https://docs.docker.com/swarm/)是一个集群和调度工具,它会自动优化分布式应用程序的基础架构基于应用程序的生命周期阶段、容器的使用与性能的需求。 20 | 21 | Messina说:“Swarm有多个模块来决定调度,包括了解特定的容器有怎样的特定的资源需求-计算和内存是最明显的例子。”处理调度算法时,Swarm将决定哪个引擎和主机应该运行。Messina举了一个例子,其中,在一些应用中,重点需要考虑亲和度-在同一主机上某些容器能最好的运行。 22 | 23 | “Swarm的核心内容是,当你使用多主机,分布式应用程序时,你想要保持开发的经验而且具有完全的便携性。Swarm提供了连续性,但你也想拥有的灵活性:例如,对于您正在使用的应用程序可以使用特定的集群的解决方案的能力。这确保集群功能一直是便携的不管是从笔记本电脑还是到生产环境中。“ 24 | 25 | ###保持Swarm灵活性 26 | 为了生态系统合作伙伴,该Swarm版本还配备了一个Swarm API用于创造可替代或可补充的编排工具,以便对于某些更细致入微的特定使用案例它可以超过Docker Swarm的优化算法。 27 | 28 | 这是Docker一直呼吁的“batteries-included-but-swappable”(译注:想象一下以前能换手机电池的诺基亚手机:))的方法。有些用户可能愿意使用Docker Swarm以确定多容器分布式应用程序架构的优化集群。其他人会想用Swarm的集聚和调度部分来设置自己的参数,而还有一些人将目光转向生态系统合作伙伴的替代编排优化产品来推荐最佳集群组合。 29 | 30 | Apache Mesos(译注:想要了解Mesos的朋友可以参考[这里](http://dockerone.com/topic/Mesos))的企业赞助商,Mesosphere,最初的Docker生态系统合作伙伴,他们使用Swarm API创建了替代优化产品。其他期望未来会有来自Amazon、Google、Joyent与MS Azure。 31 | 32 | Mesosphere营销副总裁-Matt Trifiro说:“Swarm首次公布后,Mesosphere和Docker聚集在一起,因为两家公司的工程师立即看到了如何将这两个项目放在一起工作。” 33 | 34 | 在DockerCon EU,Docker创始人兼CTO-Solomon Hykes挑出Mesosphere的技术做为规模化运行容器的黄金标准。(参见[YouTube视频](https://www.youtube.com/watch?v=sGWQ8WiGN8Y)35分钟左右) 35 | 36 | Trifiro说,对于大规模运行的分布式应用程序,Mesosphere的编排工具,相对于Swarm的“batteries-included”版本,更适合以确定优化的集群及调度业务流程: 37 | 38 | 他说对于Mesosphere与Swarm一体化有两件事情要强调: 39 | 40 | 1. **Hyperscale(超大规模)**:对于任何希望在数百或数千台服务器上,无论是内部部署还是在云中一个高度自动化的环境中运行的容器的大型企业,Mesosphere的技术是唯一公开可用的容器编排系统 - 运行数百万容器在企业,像Twitter、Groupon和Netflix,以及在一些最大的消费类电子产品和金融服务公司。 41 | 42 | 2. **Multitenant Diversity of Workloads(工作负载的多租户多元化)**:Mesosphere的技术是一个企业组织想要同一集群其他类型的工作负载上运行Docker Swarm工作量的高弹性方​​式的必由之路。例如,你可以在一台Mesosphere集群上相互运行Cassandra、Kafka、Storm、Hadoop和Docker Swarm工作负载,他们都共享相同的资源。这使得更有效地利用集群资源,大大降低了运营成本和复杂性。 43 | 44 | ###Docker Compose 45 | 使用[Docker Compose](https://docs.docker.com/compose)工具构建在Swarm上运行多容器应用程序。Compose工具使用YAML文件来维护所有应用程序容器的逻辑定义以及它们之间的连接。Compose内置分布式应用程序无需影响在编排链的其他服务实现动态更新。 46 | 47 | ###潜台词:我们可以做的更好 48 | Docker发布Swarm以及Swarm API的方式用来消除[那些当Docker起初在十二月提出这些解决方案的社区害怕](http://thenewstack.io/docker-extends-platform-story-for-app-development-and-deployment-st-scale/)。Docker一直致力于为社区合作伙伴建立一个生态系统经济,他们已经构建了加强DevOps、监控、持续改进、质量保证以及需要在一个容器化、分布式应用环境中定位的其他程序的产品。在社区的一些成员中最初的害怕是对直接从Docker创造编排工具的担心,此举会太严格执行“Docker的做事方式”。他们担心这些而不是担心能不能够创造一个像Mesosphere那样的集成产品,竞争编排工具将需要使用一个精密变通的解决方法,以提供一个替代Docker已经有的产品。 49 | 50 | Docker编排公告一再被推广说是100%的便携性以及Swarm API的 “batteries-included-but-swappable”的性质巧妙地解决这一问题。 51 | 52 | Messina说:“如果你看看编排公告,和所有这些工作与规划集成,但现实是Docker编排工具与生态系统合作伙伴的合作特别地开放。Docker社区需要建立多容器和多主机的分布式应用程序-这一直是我们的社区绝对任务。这些工具的结构方式使得他们非常灵活,并设置了允许合作伙伴以开发先进与丰富服务的API。这里的整体思路是我们要维持开发经验和100%的可移植性。为了选择的自由,社区是如何设置这些容器和优化集群。“ 53 | 54 | **原文链接:[Docker Releases Orchestration Tool Kit](http://thenewstack.io/orchestration-toolkit-release-aims-prove-dockers-commitment-flexibility-community-ecosystem/) (翻译:[田浩浩](https://github.com/llitfkitfk))** 55 | 56 | =========================== 57 | **译者介绍** 58 | 田浩浩,[悉尼大学USYD](http://sydney.edu.au/engineering/it/)硕士研究生,目前在珠海从事Android应用开发工作。业余时间专注Docker的学习与研究,希望通过[DockerOne](http://dockerone.com/)把最新最优秀的译文贡献给大家,与读者一起畅游Docker的海洋。 -------------------------------------------------------------------------------- /ps/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ps/README.md -------------------------------------------------------------------------------- /pull/README.md: -------------------------------------------------------------------------------- 1 | 使用 2 | ==== 3 | ``` 4 | docker pull NAME[:TAG] 5 | ``` 6 | 从远程仓库(DockerHub)下载image -------------------------------------------------------------------------------- /push/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/push/README.md -------------------------------------------------------------------------------- /question-cn.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 大的问题 - 很难跟上所有这方面的发展,因为这一切都如此快速变化。我会尽力给你我的观点作为一名工程师,在中间层。我要限制我的答案中间层技术,它的工作原理与其他工具,因为这是我所知道的最好的。 5 | 6 | 首先简要介绍Mesos,而我们的“栈”的基础上的。 Mesos是群集资源管理器。配置来反对运行,对节点数据中心(或云)运行在主机或从配置应用程序,它计算出在哪里运行中的各种约束集的应用实例。举例来说,如果一个正在运行的任务终止,失败或丢失 - 它通过一个API,它需要在应用某些事件来实现的回调提供了此功能。 7 | 8 | 这再次运行Mesos的API的应用程序称为框架。例如,有一个它运行火花一个Mesos群集上火花框架(火花实际上一开始只是一个例子Mesos应用程序,因此该框架的实现是特别好)。这是许多其他框架流行的分布式系统(如Hadoop的,卡桑德拉等),使他们能够使用Mesos的调度能力,以自己的最佳优势。 9 | 10 |  但是,如果你的终端应用包括长时间运行的服务,有没有必要从头开始实现了一个框架。有中存在主要是为了协调部署和服务的运行与Mesos几个框架。一个例子,这是我们积极开发和支持,是马拉松(中间层/马拉松)。其他流行的框架包括Apache的奥罗拉(Apache的极光),HubSpot的奇异(HubSpot /奇点)。这三种方法都相当强劲 - 在不同的公司正在使用的生产。 11 | 12 | 同样,如果你正在寻找一个批处理应用程序迁移到同一组的计算资源,在Chronos的框架(mesos /克罗诺斯)允许你这样做。 13 | 14 | 我们相信,所有的应用程序移动到同一套物理资源,并使用像Mesos资源管理器是使用计算能力的最有效方式。所述Mesos纸本身(页上mit.edu)暗示的增加利用率。这是一个巨大的胜利为我们所有的客户。 15 | 16 | 微博已经被使用Mesos在生产了数年,现在来运行他们的基础设施,良好的比例,所以你会发现它是一个最成熟的出来你列出的项目。 (其他著名的用户包括易趣,Hubspot和Airbnb住宿。) 17 | 18 | 关于你提到的其他技术,你会发现,几乎所有的人都用中间层的软件,很好地发挥。泊坞窗已支持原生的近6个月了(并已在某些能力较去年被支持)。您可以轻松地部署和启动Dockerized应用Mesos,马拉松和Chronos的。 19 | 20 | 中间层不会取代CoreOS。通常情况下,你仍然安装在底层主机操作系统的中间层软件,因此把它作为你的主机操作系统和您的顶级应用程序之间的抽象层。我们支持通常的流行 21 |  “胖”的Linux发行版如Debian / Ubuntu的/ CentOS的/ RedHat和开始记录我们的CoreOS支持(中间层的单CoreOS实例)。 22 | 23 | Kubernetes提供了类似的功能,以马拉松和我前面描述的其他服务框架。我们实际上是在与谷歌合作,提供Kubernetes强大的支持上Mesos(中间层/ kubernetes-mesos) - 它可以运行现在。这使您可以得到有Mesos处理在何处以及如何运行你的应用程序,并仍用好的抽象Kubernetes提供的效率优势。发展对Kubernetes速度快,所以这是非常令人兴奋的看到这个项目是怎么回事。 24 | 25 | HAProxy的实际上是规范的方式为用户进行的马拉松(服务发现与中层)中运行的服务的服务发现和我们最近发布了Mesos-DNS(中间层/ mesos-DNS),一个简单的方法来执行应用程序运行的基于DNS服务发现在Mesos。 26 | 27 | 我希望这个信息可以帮助阐明你的追求:)。这听起来像你的架构将很好地映射到中间层的生态系统。如果您想了解更多信息,请联系我们(联系中层中层·),或找到我们在Freenode的#mesos! -------------------------------------------------------------------------------- /ref/c-array-pointer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-array-pointer.gif -------------------------------------------------------------------------------- /ref/c-array.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-array.gif -------------------------------------------------------------------------------- /ref/c-compile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-compile.gif -------------------------------------------------------------------------------- /ref/c-dds1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds1.gif -------------------------------------------------------------------------------- /ref/c-dds2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds2.gif -------------------------------------------------------------------------------- /ref/c-dds3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds3.gif -------------------------------------------------------------------------------- /ref/c-dds4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds4.gif -------------------------------------------------------------------------------- /ref/c-dds5b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds5b.gif -------------------------------------------------------------------------------- /ref/c-dds6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds6.gif -------------------------------------------------------------------------------- /ref/c-dds7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds7.gif -------------------------------------------------------------------------------- /ref/c-dds8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-dds8.gif -------------------------------------------------------------------------------- /ref/c-exec.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-exec.gif -------------------------------------------------------------------------------- /ref/c-heap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-heap.gif -------------------------------------------------------------------------------- /ref/c-heap1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-heap1.gif -------------------------------------------------------------------------------- /ref/c-heap2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-heap2.gif -------------------------------------------------------------------------------- /ref/c-heap3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-heap3.gif -------------------------------------------------------------------------------- /ref/c-if.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-if.gif -------------------------------------------------------------------------------- /ref/c-pointer-swap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer-swap.gif -------------------------------------------------------------------------------- /ref/c-pointer1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer1.gif -------------------------------------------------------------------------------- /ref/c-pointer2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer2.gif -------------------------------------------------------------------------------- /ref/c-pointer3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer3.gif -------------------------------------------------------------------------------- /ref/c-pointer4a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer4a.gif -------------------------------------------------------------------------------- /ref/c-pointer5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer5.gif -------------------------------------------------------------------------------- /ref/c-pointer6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer6.gif -------------------------------------------------------------------------------- /ref/c-pointer7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-pointer7.gif -------------------------------------------------------------------------------- /ref/c-string-corrected.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-string-corrected.gif -------------------------------------------------------------------------------- /ref/c-while.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/ref/c-while.gif -------------------------------------------------------------------------------- /rm/README.md: -------------------------------------------------------------------------------- 1 | 使用 2 | ==== 3 | 4 | docker rm [OPTIONS] CONTAINER [CONTAINER...] 5 | 6 | 7 | OPTIONS 8 | ------- 9 | 10 | * -f 11 | 12 | ``` 13 | 强制删除正在运行的container 14 | ``` 15 | 16 | 17 | * -l 18 | 19 | ``` 20 | 移除container下的link(网络通讯) 21 | ``` 22 | 23 | * -v 24 | 25 | ``` 26 | 移除container的数据卷 27 | ``` 28 | -------------------------------------------------------------------------------- /rmi/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llitfkitfk/docker-tutorial-cn/644dab57454226c6ceae2dc65731eb42e58c347b/rmi/README.md -------------------------------------------------------------------------------- /run/README.md: -------------------------------------------------------------------------------- 1 | 使用 2 | ==== 3 | ``` 4 | docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 5 | ``` 6 | 7 | OPTIONS 8 | ------- 9 | 10 | * -a 11 | 12 | ``` 13 | // 附加container的 STDIN, STDOUT or STDERR 来操控输入或输出 14 | echo "test" | sudo docker run -i -a stdin ubuntu cat 15 | ``` 16 | ``` 17 | // 如果有error信息 将会输出error信息 18 | sudo docker run -a stderr ubuntu echo test 19 | ``` 20 | 21 | ``` 22 | // 如果查看log信息 可以输出信息 23 | sudo docker run -a stdout ubuntu echo test 24 | sudo docker logs [containerId] 25 | ``` 26 | * -c 27 | 28 | ``` 29 | //CPU分配 30 | ``` 31 | 32 | * -d 33 | 34 | ``` 35 | //后台运行container并输出container的id 36 | sudo docker run -d ubuntu /bin/bash 37 | ``` 38 | * -e, --env 39 | 40 | ``` 41 | //设置环境变量 42 | $ sudo docker run --env TEST_FOO="This is a test" busybox env | grep TEST_FOO 43 | TEST_FOO=This is a test 44 | ``` 45 | * -h 46 | 47 | ``` 48 | //设置host name 49 | sudo docker run -d -h www.example.com ubuntu /bin/bash 50 | ``` 51 | * -i 52 | 53 | ``` 54 | //保持输入打开 55 | sudo docker run -i ubuntu /bin/bash 56 | ``` 57 | * -t 58 | 59 | ``` 60 | //分配一个假的输入终端 61 | sudo docker run -i -t ubuntu /bin/bash 62 | ``` 63 | * -m 64 | 65 | ``` 66 | //内存管理 67 | sudo docker run -d -m 100m ubuntu /bin/bash 68 | ``` 69 | 70 | * -p 71 | 72 | ``` 73 | //映射container端口到host主机 格式:(-p ip:hostPort:containerPort) 74 | sudo docker run -d -p 0.0.0.0:8080:80 ubuntu /bin/bash 75 | ``` 76 | * -P 77 | 78 | ``` 79 | //映射container所有暴露的端口到host主机 80 | sudo docker run -d -P ubuntu /bin/bash 81 | ``` 82 | 83 | * -u 84 | 85 | ``` 86 | //设置username 87 | sudo docker run -d -u testusername ubuntu /bin/bash 88 | ``` 89 | * -v 90 | 91 | ``` 92 | //挂载数据卷 格式:(-v /host:/container) 93 | sudo docker run -d -v /tmp/hostvolume:/tmp/ ubuntu /bin/bash 94 | ``` 95 | * -w 96 | 97 | ``` 98 | //设置container内部的工作路径 99 | sudo docker run -w /path/to/dir/ -i -t ubuntu /bin/bash 100 | ``` -------------------------------------------------------------------------------- /safer-local-docker-networks-cn.md: -------------------------------------------------------------------------------- 1 | ##更安全的本地Docker网络 2 | 3 | 【编者的话】本文主要是解决了上一篇[探索本地Docker桥接网络](http://dockerone.com/article/137)提出的问题。 4 | 5 | 设置```icc=false```并使用容器链接来杜绝肆意的跨容器通信。 6 | 7 | 从这篇文章中如果你喜欢或学到了一些东西,那么可以考虑买我的书[Docker in Action](http://www.manning.com/nickoloff/)。1月6号此书会有Manning团购价(48小时),请使用促销代码:dotd010615au。 8 | 9 | 上周我写了[探索本地Docker桥接网络](http://dockerone.com/article/137)。在这篇文章中我描述了如何使用[nmap](http://nmap.org/)探索由同一桥接网络上的其他容器公开的服务。我展示了即使在这些服务并没有映射到主机的公有接口的端口的情况下依然可以访问。这是一个问题,因为大多数镜像的工具、数据库以及微服务都带有不安全的默认配置。较为变通的工程师或管理员可能会把这些镜像放到可以保护他们的防火墙或网络拓扑结构中。这只适用于配置了防火墙和网络拓扑的环境。 10 | 11 | 默认情况下,Docker允许任意的跨容器通信。在我看来这是一件好事。它不仅减少了应用时的冲突并且降低了学习曲线。我认为这是很重要的一方面使人们可以轻松地以任何技术开始使用Docker并且对人们理解Docker的发展有明确的道路。 12 | 13 | 其中有一条理解如何加强它们的容器网络的道路。人们应该学会的第一件事就是如何禁用任意的跨容器通信。当您启动Docker守护进程时设置```icc=false```。 14 | {{{docker -d --icc=false .... 15 | }}} 16 | 17 | 当您以这种方式启动Docker,它将配置[iptables(防火墙)](http://www.wikiwand.com/en/Iptables):在桥接网路中移除所有容器间的通信。这将防止容器之间的通信。 18 | {{{Chain FORWARD (policy ACCEPT) 19 | target prot opt source destination 20 | DROP all -- 0.0.0.0/0 0.0.0.0/0 21 | ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 22 | ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 }}} 23 | 24 | 如果你压根就不想让你的容器可以直接相互通信,这样做是没问题的。没有什么能够阻止往返于主机的公共接口与可以公开访问的映射到另一个容器的端口之间的通信。 25 | 26 | ####下图展示了暴露主机端口的往返通信: 27 | ![](https://d262ilb51hltx0.cloudfront.net/max/446/1*_QyJABSe1WUQfVIXFHCw_w.png) 28 | 29 | 30 | 这是足够的对于许多人来说。但故事并没有到此结束。 31 | 32 | 当跨容器通信被禁用时使容器之间有明确的连接是可能的。您可以在容器创建时使用容器链接做到这一点。 33 | 34 | 当你创建一个容器并指定另一个作为链接的目标时,Docker建立相应的联动。例如: 35 | {{{docker run -d --name ex_a busybox /bin/sh 36 | docker run -d --name ex_b --link ex_a:other busybox /bin/sh }}} 37 | 38 | 如果我可以说现在我对Docker的任何部分都缺乏激情,那么它可能会为了服务搜寻而使用容器链接。当您连接两个容器时,带有位置详细信息的环境变量将被设置以便来使用此容器。但问题是有太多隐性契约的工作。创建消耗容器的程序或用户可以指定的链接的别名。但是容器内的软件必须与别名一致,否则它将不知道要寻找什么。即使我认为链接提供了端口信息以及网络地址信息,我仍然更喜欢DNS。幸运的是,链接不仅仅是为了服务搜寻。 39 | 40 | 当你禁用了跨容器通信时,Docker会创建明确的异常为了使连接的容器之间得以通信。以下是摘自有这样异常的iptables。 41 | {{{Chain FORWARD (policy ACCEPT) 42 | target prot opt source destination 43 | ACCEPT tcp -- 172.17.0.3 172.17.0.4 tcp spt:80 44 | ACCEPT tcp -- 172.17.0.4 172.17.0.3 tcp dpt:80 45 | DROP all -- 0.0.0.0/0 0.0.0.0/0 46 | ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 47 | ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 }}} 48 | 49 | 这是容器链接的最强大的应用。链接允许你用简单的断言来定义容器之间的关系。就轻量级而言,Docker确实比任何我用过的其他工具都更好。 50 | 51 | 不管怎样,回去工作并确保在新的一年里保护好自己。 52 | 53 | **原文链接:[Safer Local Docker Networks](https://medium.com/@allingeek/safer-local-docker-networks-8ce22127f9df) (翻译:[田浩](https://github.com/llitfkitfk))** -------------------------------------------------------------------------------- /start_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | service docker start 4 | 5 | chkconfig docker on 6 | -------------------------------------------------------------------------------- /the-docker-ecosystem-service-discovery-and-distributed-configuration-stores-cn-review.md: -------------------------------------------------------------------------------- 1 | 【编者的话】 2 | 本文介绍了服务发现与全局可读配置存储两部分内容,不仅介绍了工作原理和工作方式,也介绍了与之相关的故障检测、重配置和安全问题,最后还介绍了常用的服务发现项目。整篇文章将这个知识点介绍的很全面细致,让读者能够对服务发现和全局可读配置存储有一个全面的认识,值得学习。 3 | 4 | 5 | ###介绍 6 | 容器给寻找大规模设计与部署应用的需求提供了一个优雅的解决方案。在Docker提供实际的容器技术的同时,许多其他的项目也在协助开发在部署环境中所需要的引导和沟通的工具。 7 | 8 | 多种Docker环境依赖的核心技术之一是服务发现。服务发现可以让一个应用或者组件发现其运行环境以及其它应用或组件的信息。它通常是采用的是分布式key-value的存储方式,而且它还用来作为一般查询配置细节信息的地方。用户配置一个服务发现工具就可以将实际容器跟运行配置分离开,这样用户就可以在多个环境中复用同一个镜像。 9 | 10 | 在这篇向导中,我们将讨论在一个Docker集群环境中服务发现工具带来的好处。主要关注在常规概念,在需要的地方会用详细的例子来描述。 11 | 12 | ###服务发现与全局可读配置存储 13 | 14 | 服务发现的基本思想是任何一个应用的实例能够以编程的方式获取当前环境的细节。这是为了让新的实例可以嵌入到现有的应用环境而不需要人工干预。服务发现工具通常是用全局可访问的存储信息注册表来实现,它存储了当前正在运行的实例或者服务的信息。大多数情况下,为了使这个配置具有容错与扩展能力,这个工具分布式地存储在基础设施中的多个宿主机上。 15 | 16 | 虽然服务发现平台的初衷是提供连接信息来连接不同组件的,但是它们更普遍地是用来存储任何类型的配置信息。许多部署工具通过写入它们的配置信息给发现工具来实现这个特性。如果容器配置了这些,它们就可以去查询这些预配置信息,并根据这些信息来调整自身行为。 17 | 18 | ###服务发现是怎么工作呢? 19 | 20 | 每一个服务发现工具都会提供一套API,使得组件可以用其来设置或搜索数据。正是如此,对于每一个组件,服务发现的地址要么硬编码到程序或容器内部,要么在运行时以参数形式提供。通常来说,发现服务用键值对形式实现,采用标准http协议交互。 21 | 22 | 服务发现门户的工作方式是:当每一个服务启动上线之后,他们通过发现工具来注册自身信息。它记录了一个相关组件若想使用某服务时的全部必要信息。例如,一个MySQL数据库服务会在这注册它运行的ip和端口,如有必要,登录时的用户名和密码也会留下。 23 | 24 | 当一个服务的消费者上线时,它能够在预设的终端查询该服务的相关信息。然后它就可以基于查到的信息与其需要的组件进行交互。负载均衡就是一个很好的例子,它可以通过查询服务发现门户得到各个后端节点承受的流量数,然后根据这个信息来调整配置。 25 | 26 | 这可将配置信息从容器内拿出。一个好处是可以让组件容器更加灵活,并不受限于特定的配置信息。另一个好处是使得组件与一个新的相关服务实例交互时变得简单,可以动态进行调整配置。 27 | 28 | ###配置存储是如何关联起来的? 29 | 30 | 全局分布式服务发现系统的一个主要优势是它可以存储任何类型的组件运行时所需的配置信息。这就意味着可以从容器内将更多的配置信息抽取出去,并放入更大的运行环境。 31 | 32 | 通常来说,为了让这个过程更有效率,应用在设计时应该赋上合理的默认值,并且在运行时可以通过查询配置存储来覆盖这些值。这使得运用配置存储跟在执行命令行标记时的工作方式类似。区别在于,通过一个全局配置存储,可以不做额外工作就能够对所有组件的实例进行同样的配置操作。 33 | 34 | ###配置存储如何帮助集群的管理? 35 | 36 | 在Docker部署中,分布式键值对存储其中最初可能不太明显的一个功能是对集群成员的存储和管理。配置存储是为了追踪宿主机成员变更和管理工具的最好环境。 37 | 38 | 一些可能会存在分布式键值对存储中的个人宿主机信息是: 39 | 40 | * 宿主机IP 41 | * 宿主机自身的链接信息 42 | * 跟调度信息有关的标签或元数据信息 43 | * 集群中的角色(如果是采用了主从模式的集群) 44 | 45 | 在正常情况下,使用一个服务发现平台时,这些细节可能不是你需要考虑的。但是他们为管理工具提供了一个可以查询或修改集群自身信息的地方。 46 | 47 | ###故障检测怎么实现? 48 | 49 | 故障检测的实现方式也有很多种。需要考虑的是如果一个组件出现故障,服务发现能否更新状态指出该组件不再提供服务。这种信息是至关重要的,关系到将应用或服务故障可能性降到最低。 50 | 51 | 许多服务发现平台允许赋值时带一个可配置的超时时间。组件可以设置一个超时时间,并能定期去请求服务发现来重置超时时间。如果该组件出现故障,超时时间达到设定值,那么这个组件的连接信息就会从服务发现的存储中被去掉。超时时间长度在很大程度上是它与应用需要多快去应对一个组件的故障的函数。 52 | 53 | 这也可以通过将一个基本的“助手”容器与每一个组件相连来实现,而它们唯一的责任是定期的健康检查组件以及更新注册表如果组件关闭。这种类型的架构值得担忧是,如果辅助容器出现故障,将导致不正确的信息在存储中。一些系统解决这个问题的方法是在服务发现的工具中定义健康检查。这样,发现平台本身可以定期检查已注册组件是否仍然可用。 54 | 55 | ###当细节变化时,配置服务会如何? 56 | 57 | 对于基本的服务发现模型来说,一个关键的改进就是动态重新配置。普通服务发现工具允许用户通过检查在启动时的信息来影响组件的初始配置,而动态重新配置涉及配置组件来反映配置存储中的新信息。例如,当你在运行一个负载均衡,后端服务器上的健康检查可能会提示集群中的某一个成员出现故障了。运行中的成员机器需要知道这个信息,并调整配置信息和重新加载它的负载。 58 | 59 | 这个有多种方式实现。由于负载均衡的例子是这个功能的主要应用场景之一,许多现有的项目专注在当配置变动时重新配置负载均衡。常见的是HAProxy配置调整,这要归结于在负载均衡领域内它的普遍性。 60 | 61 | 某些项目更加灵活,它们可在任何类型的软件中被用来触发变更。这些工具周期性的去请求服务发现工具,并且当变更被发现,利用模板系统和服务发现工具中的值来生成新配置文件。当配置文件生成结束,相应的服务将被重新加载。 62 | 63 | 这种类型的动态配置在构建过程中需要更多的规划和配置,因为这些所有的策略都需要存在于组件容器之中。这使得组件容器负责调整自身的配置。找出需要存在服务发现工具中的必要参数值并设计一个适当的数据结构以便使用,这是该系统的另一个技术挑战,但是它可以带来可观的效益和灵活性。 64 | 65 | ###安全方面如何? 66 | 67 | 许多人初次接触全局配置存储时担心的一个问题是访问的安全性。将连接信息存储在全局可访问的存储中真的合适么? 68 | 69 | 这个问题的答案很大程度上依赖于你准备在存储中存放的内容以及保护你的数据需要多少层的安全等级。几乎所有的服务发现工具可以采用SSL/TLS加密链接。对于一些服务,隐私性可能不是最重要的,而且发现服务放在内网中也可能让人满意。但是,大多数的应用会从它额外的安全性上获益。 70 | 71 | 有许多不同的方法来解决这个问题,同时各种项目也都提供他们自己的解决方案。一个项目的解决方案是继续允许开放发现服务平台本身,但是对于写入数据进行加密,使用者必须用相应的密钥来解码从服务发现中获取的信息才能使用。其他组件不可以获取到未加密的数据。 72 | 73 | 还有不同的方法,一些服务发现工具实现了访问控制列表,将不同的键值切分到不同的分组中。他们可以根据访问需要来制定不同的秘钥来访问相应的分组。这种简单的方式既保证了能够给特定组件提供信息又保证了对其他组件的不可访问性。每个组件都可以被配置为只允许访问它所需要的连接信息。 74 | 75 | ###有哪些常见的服务发现工具? 76 | 77 | 既然我们已经讨论了一些服务发现工具和全局分布式键值存储的一般特点和功能,下面我们来介绍几个与这些概念有关的项目。 78 | 79 | 一些常见的服务发现工具: 80 | 81 | * **etcd**:这是CoreOS的创建者提供的工具,面向容器和宿主机提供服务发现和全局配置存储功能。它在每个宿主机上有基于http协议的API和命令行的客户端。 82 | * **consul**:这个服务发现平台有很多高级的特性,使得它脱颖而出,例如:配置健康检查、ACL功能、HAProxy配置等等。 83 | * **zookeeper**:这个工具较上面两个都比较老,提供一个更加成熟的平台和一些新特性。 84 | 85 | 一些基本服务发现工具的扩展项目: 86 | 87 | * **crypt**:Crypt允许组件通过采用公钥加密的方式来保护它们的信息。需要读取数据的组件会被分配密钥,而其他组件则不能读取数据。 88 | * **confd**:Confd项目旨在基于服务发现的变化,而动态重新配置任意应用程序。该系统包含了一个工具来监测节点中的变化、一个模板系统来根据获取到的值来生成配置文件,并能够重新加载受影响的应用。 89 | * **vulcand**:Vulcand为成组的组件作为负载均衡使用。它使用etcd作为后端,并基于监测变更来调整它的配置。 90 | * **marathon**:虽然marathon主要是调度器(后续介绍),它也实现了一个基本的重加载HAProxy的功能,当发现变更时它来协调可用的服务。 91 | * **frontrunner**:这个项目嵌入在marathon中对HAProxy的更新提供一个更稳定的解决方案。 92 | * **synapse**:这个项目引入了嵌入式的HAProxy组件,它能够路由流量给各个组件。 93 | * **nerve**:它被用来与synapse结合一起来为各个组件提供健康检查,如果组件不可用,nerve将更新synapse将该组件移除出去。 94 | 95 | ###总结 96 | 97 | 服务发现工具和全局配置存储使得Docker容器可以适应它们当前所处环境并嵌入现有的组件。这是一个重要的先决条件为的是提供方便、容易扩展和部署的功能,通过允许组件跟踪和应对他们所在环境变化。 98 | 99 | 在下一个指南中,我们将探讨Docker容器和宿主机之间用自定义的网络配置进行通信。 100 | 101 | ###本系列的其他文章 102 | 103 | Docker已经为开发者和管理员提供一个简单的平台来创建和部署可扩展的应用。在这个系列中,我们将探索Docker如何与其他组件整合在一起,并用它们提供的工具集来便捷地提供高可用性的分布式系统。 104 | 105 | 1. [常用组件介绍](http://dockerone.com/article/205)(已翻译) 106 | 2. [容器化的综述](http://dockerone.com/article/208)(已翻译) 107 | 3. 服务发现和分布式配置存储(本文) 108 | 4. 网络与通信(翻译中) 109 | 5. 调度与编制(翻译中) 110 | 111 | 112 | 113 | 114 | 115 | **原文:[The Docker Ecosystem: Service Discovery and Distributed Configuration Stores](https://www.digitalocean.com/community/tutorials/the-docker-ecosystem-service-discovery-and-distributed-configuration-stores) (译者:陈杰)** 116 | 117 | =============================================== 118 | **译者介绍** 119 | **陈杰**,北京理工大学计算机学院在读博士,研究方向是自然语言处理在企业网络信誉评价方面的应用,平时也乐于去实现一些突发的想法。在疲于配置系统环境时发现了Docker,跟大家一起学习、使用和研究Docker。 -------------------------------------------------------------------------------- /tmp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | du -sh * | sort -n 4 | 5 | -------------------------------------------------------------------------------- /understanding-volume-in-docker-cn.md: -------------------------------------------------------------------------------- 1 | ###深入理解Docker Volume 2 | 3 | 【编者的话】本文主要介绍了Docker Volume的作用机制(译注:是[Docker入门教程](http://dockerone.com/article/111)的延伸), 作者通过从数据的共享、数据容器、备份、权限以及删除Volumes五方面深入介绍了Volumes的工作原理。 4 | 5 | 从Docker IRC(网络即时聊天)频道以及[stackoverflow](https://stackoverflow.com/questions/tagged/docker)(译者注:有关代码问题的问答平台:大部分代码exception问题可以复制->粘贴->搜索来找到答案)的问题来看,Docker volumes是如何工作的这个问题上还存在很多混淆。在这篇文章中我会尽最大努力来解释Volumes是如何工作的,并展示一些最佳实践。虽然这篇文章主要是针对泊坞窗的用户几乎没有的知识量,尽管这篇文章主要是针对那些对Volumes不了解的Docker用户,当然有经验的用户也可以学一些Volumes的很多人不知道的细微之处的知识。 6 | 7 | 为了了解什么是Docker Volume,首先我们需要明确Docker内的文件系统是如何工作的。Docker镜像被存储在一系列的只读层。当我们开启一个容器,Docker读取只读镜像并添加一个读写层在顶部。如果正在运行的容器修改了现有的文件,该文件将被拷贝出底层的只读层到最顶层的读写层。在读写层中的旧版本文件隐藏于该文件之下,但并没有被不破坏 - 它仍然存在于镜像以下。当Docker的容器被删除,然后重新启动镜像时,将开启一个没有任何更改的新的容器 - 这些更改会丢失。此只读层及在顶部的读写层的组合被Docker称为[Union File System](https://docs.docker.com/terms/layer/#union-file-system)(联合文件系统)。 8 | 9 | 为了能够保存(持久)数据以及共享容器间的数据,Docker提出了Volumes的概念。很简单,volumes是目录(或者文件),它们是外部默认的联合文件系统或者是存在于宿主文件系统正常的目录和文件。 10 | 11 | 初始化Volumes有两种方式,对于理解来说一些细微的差别很重要。我们可以用在运行时使用```-v```来声明Volumes: 12 | ```$ docker run -it --name container-test -h CONTAINER -v /data debian /bin/bash 13 | root@CONTAINER:/# ls /data 14 | root@CONTAINER:/# 15 | ``` 16 | 17 | 这将在容器内创建路径```/data```,它存在于联合文件系统外部并可以在主机上直接访问。任何在该镜像```/data```路径的文件将被复制到volume。我们可以使用```docker inspect```命令找出Volume在主机存储的地方: 18 | ```$ docker inspect -f {{.Volumes}} container-test 19 | ``` 20 | 21 | 你会看到以下类似内容: 22 | ```map[/data:/var/lib/docker/vfs/dir/cde167197ccc3e138a14f1a4f7c0d65b32cecbada822b0db4cc92e79059437a9] ``` 23 | 24 | 这说明Docker把在```/var/lib/docker```下的某个目录挂载到了容器内的```/data```目录下。让我们从主机上添加文件到此文件夹下: 25 | ```$ sudo touch /var/lib/docker/vfs/dir/cde167197ccc3e138a14f1a4f7c0d65b32cecbada822b0db4cc92e79059437a9/test-file``` 26 | 27 | 进入我们的容器内可以看到: 28 | ```$ root@CONTAINER:/# ls /data 29 | test-file``` 30 | 31 | 改变会立即生效只要将主机的目录挂载到容器的目录上。我们可以在Dockerfile中通过使用```VOLUME```指令来达到相同的效果: 32 | ```FROM debian:wheezy 33 | VOLUME /data``` 34 | 35 | 但还有另一件只有```-v```标志能做到而Dockerfile是做不到的事是在容器上挂载指定的主机目录。例如: 36 | ```$ docker run -v /home/adrian/data:/data debian ls /data``` 37 | 38 | 该命令将挂载主机的```/home/adrian/data```目录到容器内的```/data```目录上。任何在```/home/adrian/data```目录的文件都将会出现在容器内。对于在主机和容器之间共享文件这是非常有帮助的,例如挂载需要编译的源代码。为了保存可移植性(并不是所有的系统的主机目录都是可以用的),挂载主机目录不用从Dockerfile指定。当使用```-v```参数的形式时并不镜像目录下的所有文件都被复制进Volume中。 39 | 40 | ###数据共享 41 | 42 | 从一个容器访问另一个容器的volumes,我们只用使用```-volumes-from```参数来执行```docker run```。 43 | ```$ docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash 44 | root@NEWCONTAINER:/# ls /data 45 | test-file 46 | root@NEWCONTAINER:/#``` 47 | 48 | 值得注意的是不管container-test运没运行,它都会起作用。Volume直到容器没有连接到它才会被删除。 49 | 50 | ###数据容器 51 | 使用纯数据容器来持久数据库、配置文件或者数据文件等等是普遍的做法。[官方的文档](https://docs.docker.com/userguide/dockervolumes/)就讲解的不错。例如: 52 | ```$ docker run --name dbdata postgres echo "Data-only container for postgres"``` 53 | 54 | 该命令将会创建一个包含已经在Dockerfile里定义过Volume的postgres镜像,运行```echo```命令然后退出。当我们运行```docker ps```命令时,```echo```是有用的作为我们识别某镜像的用途。我们可以用```-volumes-from```命令使用其他容器的Volume: 55 | ```$ docker run -d --volumes-from dbdata --name db1 postgres``` 56 | 57 | 使用数据容器两个要点: 58 | 59 | - 不要不管运行中的数据容器,这是无意义的浪费资源 60 | - 不要为了数据容器来使用“最小的镜像”如```busybox```或```scratch```。只要使用数据库镜像本身就可以了。如果你已经有了该镜像,那么它并不需要花费额外的空间并且它还允许镜像内的数据来做Volume 61 | 62 | ###备份 63 | 如果你在用数据容器,做备份是相当容易的: 64 | ```$ docker run --rm --volumes-from dbdata -v $(pwd):/backup debian tar cvf /backup/backup.tar /var/lib/postgresql/data``` 65 | 66 | 该示例应该会将Volume里所有的东西压缩为一个tar包(官方的postgres Dockerfile定义了一个Volume在```/var/lib/postgresql/data```目录下) 67 | 68 | ###权限与许可 69 | 通常你需要设置Volume的权限或者为Volume初始化一些默认数据或者配置文件。要注意的关键点是,在Dockerfile的```VOLUME```指令后的任何东西将不能改变该volume,比如: 70 | ```FROM debian:wheezy 71 | RUN useradd foo 72 | VOLUME /data 73 | RUN touch /data/x 74 | RUN chown -R foo:foo /data``` 75 | 76 | 该Docker file预期所料将不会工作,我们希望```touch```命令在镜像的文件系统上运行,但是实际上它是在一个临时容器的Volume上运行。如下所示: 77 | ```FROM debian:wheezy 78 | RUN useradd foo 79 | RUN mkdir /data && touch /data/x 80 | RUN chown -R foo:foo /data 81 | VOLUME /data``` 82 | 83 | Docker是足够聪明的复制存在挂载于镜像Volume下的文件到Volume下,并正确地设置权限。如果您指定Volume的主机目录(使主机文件不小心被覆盖)将不会出现这种情况。 84 | 85 | 如果你能设置权限在```RUN```指令,那么你将不得不在容器创建后使用```CMD```或```ENTRYPOINT```脚本来执行。 86 | 87 | ###删除Volumes 88 | 89 | 该功能比大多数人意识到的可能更微妙一些。如果你已经使用```docker rm```来删除你的容器,可能有很多的孤立的Volumes在占用着那些空间。 90 | 91 | Volume只有在下列情况下才能被删除: 92 | 93 | - 该容器可以用```docker rm -v```来删除且没有其他容器连接到该Volume(以及主机目录是也没被指定为Volume)。注意,```-v```是必不可少的。 94 | - 该```-rm```标志被提供给```docker run```的 95 | 96 | 除非你已经很小心的,总是像这样来运行容器,否则你将会在```/var/lib/docker/vfs/dir```目录下得到一些僵尸文件和目录,并且还不容易说出他们到底代表什么。 97 | 98 | ###延伸阅读 99 | 以下资源更深入的探究了Volumes机制(译注:以下译文稍后奉上): 100 | 101 | - [疯狂Docker之纯数据容器](http://container42.com/2014/11/18/data-only-container-madness/) 102 | - [深入Docker之Volumes](http://container42.com/2014/11/03/docker-indepth-volumes/) 103 | - [容器数据管理](https://docs.docker.com/userguide/dockervolumes/) 104 | 105 | 另外,我们可以期待不久的将来会更多的有关处理volumes的工具: 106 | 107 | - [Docker提议 #8484](https://github.com/docker/docker/pull/8484) 108 | 109 | 110 | -------------------------------------------------------------------------------- /why-use-fig-for-docker-automation-cn.md: -------------------------------------------------------------------------------- 1 | 【思考】为什么要用Fig来实现Docker自动化? 2 | 3 | 【编者的话】本文主要讲解了如何用Fig来解决Docker多参数启动容器的问题(Fig入门可以参照这里)以及使用Fig需要注意的一些事项。 4 | 5 | 6 | 如果您使用Docker已经有一阵子,但你还没有尝试过Fig,这篇文章是写给你的。你可能像我一样要么习惯于对付长而笨重的使用多个参数的Docker命令,要么会拿出一堆shell脚本来启动你的容器。从其核心出发,Fig只是一个简单的自动化和抽象化来用于帮助处理这些东西。 7 | 8 | 下边将通过一个例子简单地解释一下。我们将创建一个简单的Python Flask应用程序,此应用每次被请求之后都会显示一个时间戳。 Python代码不用很在意可以随意跳过它,但如果你想跟着步骤来,那么首先在新目录中创建具有下列内容的文件```app.py```: 9 | {{{from flask import Flask 10 | from redis import StrictRedis 11 | from datetime import datetime 12 | 13 | 14 | app = Flask(__name__) 15 | redis = StrictRedis(host='redis', port=6379) 16 | 17 | 18 | @app.route('/') 19 | def home(): 20 | redis.lpush('times', datetime.now().strftime('%H:%M:%S')) 21 | return 'This page was requested at: {}\n'.format( 22 | [t.decode('utf-8') for t in redis.lrange('times', 0, -1)]) 23 | 24 | if __name__ == '__main__': 25 | app.run(host='0.0.0.0', debug=True) }}} 26 | 27 | 接着是Dockerfile的内容: 28 | {{{FROM python:3.4 29 | 30 | RUN mkdir /code 31 | COPY app.py /code/app.py 32 | WORKDIR /code 33 | RUN pip install flask redis 34 | CMD ['python', 'app.py'] }}} 35 | 36 | 现在我们可以构建并运行此应用程序的容器: 37 | {{{$ docker build -t fig_ex . 38 | ...snip... 39 | $ docker run -d --name redis redis 40 | 68fece140431f4ad67fbd9fbaa43253785b4c3cb6ceeda1b1eb7de2eee22615c 41 | $ docker run -d -p 5000:5000 --link redis:redis fig_ex 42 | cb7588cd15ade0ec09e005ea64aaa8753befa2d47d9a8e331a711137fdc59bc8 43 | $ curl localhost:5000 44 | This page was requested at: ['13:18:39'] 45 | $ curl localhost:5000 46 | This page was requested at: ['13:18:40', '13:18:39'] 47 | $ curl localhost:5000 48 | This page was requested at: ['13:18:41', '13:18:40', '13:18:39'] }}} 49 | 50 | 或者同样地我们可以用Fig来实现。在以上相同的目录下创建一个名为```fig.yml```的文件: 51 | {{{figex: 52 | build: . 53 | ports: 54 | - '5000:5000'; 55 | links: 56 | - redis 57 | 58 | redis: 59 | image: redis }}} 60 | 61 | 并且运行```fig up```: 62 | {{{$ fig up 63 | Creating figcode_redis_1... 64 | Creating figcode_figex_1... 65 | Attaching to figcode_redis_1, figcode_figex_1 66 | redis_1 | [1] 06 Jan 10:27:12.745 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 67 | 68 | ...snip... 69 | 70 | redis_1 | [1] 06 Jan 10:27:12.749 * The server is now ready to accept connections on port 6379 71 | figex_1 | * Running on http://0.0.0.0:5000/ 72 | figex_1 | * Restarting with reloader }}} 73 | 74 | Fig会构建镜像(如有必要),以正确的顺序启动容器并连接到它们。容器会输出带有容器名称的前缀(默认情况下为目录名和镜像名称的串联)。我们可以在新的终端内测试这些容器: 75 | {{{$ curl localhost:5000 76 | This page was requested at: ['13:24:27'] 77 | $ curl localhost:5000 78 | This page was requested at: ['13:24:28', '13:24:27'] 79 | $ curl localhost:5000 80 | This page was requested at: ['13:24:29', '13:24:28', '13:24:27'] }}} 81 | 82 | 棒极了,只需很少的记忆,它就起了同样的作用-3个带多个参数的Docker命令已减少到仅用两个词。本质上来说是我们将所有的烦人的配置标志移动到了```fig.yml```文件。 83 | 84 | 要停止容器只需按```ctrl-c```。您可以使用```fig rm```来完全地删除它们。大多数时候,你不想要容器输出,因此你就可以使用```fig up -d```在分离模式下启动Fig。然后,您需要用```fig stop ```来停止容器。 85 | 86 | 关于Fig的内容确实不多,多数命令一对一的映射其```docker run```命令。尽管如此有些事情你应该了解: 87 | 88 | - 目前的YAML文件没有语法检查。这意味着,如果你犯了一个错误如忘记一个字符,你会得到一个令人困惑的错误。 89 | - Fig有关Volumes的使用很混乱。当```fig up```执行时,它会尝试使用``` –volumes-from```挂载之前任何的Volumes。这将会导致一些问题,如果在fig.yml文件中Volumes的声明被改变,因为它往往会与之前的Volumes冲突 - 通常的解决方案是只要确保你总是使用```fig rm```删除之前的容器。此问题的部分原因是Docker本身需要更多的工具来处理Volumes。 90 | - Fig主要地设计被用于开发,但我还发现在测试中它也很有用,而且它还可以在小规模部署上使用。 91 | 92 | 最后,值得指出的是,在Docker组件的作品中,Fig将会成为接班人。尽管Docker的组件未来可能会再利用现有Fig代码并且很可能有类似的语法和命令, 但我仍然建议现在使用Fig。此外,Fig的入门非常的快,你几乎可以立刻就把您投资的时间收回。 93 | 94 | **原文链接:[Why Use Fig for Docker Automation?](http://container-solutions.com/2015/01/use-fig-docker-automation/) (翻译:[田浩](https://github.com/llitfkitfk))** 95 | 96 | -------------------------------------------------------------------------------- /why_docker_is_written_in_Go.md: -------------------------------------------------------------------------------- 1 | ### go 并发 2 | 3 | * [Go 并发 ppt](http://concur.rspace.googlecode.com/hg/talk/concur.html) 4 | 5 | ##Linux container 6 | 7 | ###High level 8 | 9 | * normal processes, but isolated 10 | * share kernel with the host 11 | * no device emulation 12 | * doesn't need a /sbin/init(runs the app/DB/ whatever directly) 13 | 14 | ###Low level 15 | * own process space 16 | * own network interface 17 | * can run stuff as root 18 | * can have its own /sbin/init 19 | 20 | 21 | ## WHY 22 | 23 | ### static compilation 24 | 25 | * **go build** will embed everything you need 26 | * except dynamic libraries if you use cog 27 | * and except libc 28 | * you can have a real static binary 29 | * easier to install, easier to test, easier to adopt 30 | * good candidate for bootstrap 31 | 32 | ### neutral 33 | 34 | ### it has what we need 35 | 36 | * good asynchronous promitives 37 | * low-level interfaces 38 | * extensive standard library and data types 39 | * strong duck typing 40 | 41 | ### full development environment 42 | 43 | * go doc 44 | * go get 45 | * go fmt 46 | * go test 47 | * go run 48 | 49 | ### multi-arch build 50 | * without pre-processors 51 | 52 | 53 | 54 | 55 | --------------------------------------------------------------------------------