w3cschool-Docker 入门到实践
https://www.w3cschool.cn/reqsgr/
什么是 Docker
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。
Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc。Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。
Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。
在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。
下面的图片比较了 Docker 和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层面实现。
为什么要用 Docker
作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。
首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。 其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。
容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。
具体说来,Docker 在如下几个方面具有较大的优势。
更快速的交付和部署
对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。
更高效的虚拟化
Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。
更轻松的迁移和扩展
Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。
更简单的管理
使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。
对比传统虚拟机总结
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB | 一般为 GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
Docker的基本概念
Docker 包括三个基本概念
- 镜像(Image)
- 容器(Container)
- 仓库(Repository)
理解了这三个概念,就理解了 Docker 的整个生命周期。
Docker 镜像
Docker 镜像就是一个只读的模板。
例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Apache 或用户需要的其它应用程序。
镜像可以用来创建 Docker 容器。
Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。
Docker容器的运用
Docker 利用容器来运行应用。
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
*注:镜像是只读的,容器在启动的时候创建一层可写层作为最上层。
Docker仓库
仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool 等,可以提供大陆用户更稳定快速的访问。
当然,用户也可以在本地网络内创建一个私有仓库。
当用户创建了自己的镜像之后就可以使用 push
命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull
下来就可以了。
CentOS 安装Docker
CentOS 系列安装 Docker
Docker 支持 CentOS6 及以后的版本。
CentOS6
对于 CentOS6,可以使用 EPEL 库安装 Docker,命令如下
$ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm
$ sudo yum install docker-io
CentOS7
CentOS7 系统 CentOS-Extras
库中已带 Docker,可以直接安装:
$ sudo yum install docker
安装之后启动 Docker 服务,并让它随系统启动自动加载。
$ sudo service docker start
$ sudo chkconfig docker on
Docker镜像
Docker如何获取镜像
可以使用 docker pull
命令来从仓库获取所需要的镜像。
下面的例子将从 Docker Hub 仓库下载一个 Ubuntu 12.04 操作系统的镜像。
$ sudo docker pull ubuntu:12.04
Pulling repository ubuntu
该命令实际上相当于 $ sudo docker pull registry.hub.docker.com/ubuntu:12.04
命令,即从注册服务器 registry.hub.docker.com
中的 ubuntu
仓库来下载标记为 12.04
的镜像。
有时候官方仓库注册服务器下载较慢,可以从其他仓库下载。 从其它仓库下载时需要指定完整的仓库注册服务器地址。例如
$ sudo docker pull dl.dockerpool.com:5000/ubuntu:12.04
Pulling dl.dockerpool.com:5000/ubuntu
完成后,即可随时使用该镜像了,例如创建一个容器,让其中运行 bash 应用。
$ sudo docker run -t -i ubuntu:12.04 /bin/bash
root@fe7fc4bd8fc9:/#
Docker 列出镜像
使用 docker images
显示本地已有的镜像。
$ sudo docker images
Docker 创建镜像
创建镜像有很多方法,用户可以从 Docker Hub 获取已有镜像并更新,也可以利用本地文件系统创建一个。
修改已有镜像
先使用下载的镜像启动容器。
$ sudo docker run -t -i training/sinatra /bin/bash
root@0b2616b0e5a8:/#
利用 Dockerfile 来创建镜像
使用 docker commit
来扩展一个镜像比较简单,但是不方便在一个团队中分享。我们可以使用 docker build
来创建一个新的镜像。为此,首先需要创建一个 Dockerfile,包含一些如何创建镜像的指令。
新建一个目录和一个 Dockerfile
$ mkdir sinatra
$ cd sinatra
$ touch Dockerfile
Dockerfile 中每一条指令都创建镜像的一层,例如:
# This is a comment
FROM ubuntu:14.04
MAINTAINER Docker Newbee <newbee@docker.com>
RUN apt-get -qq update
RUN apt-get -qqy install ruby ruby-dev
RUN gem install sinatra
Dockerfile 基本的语法是
- 使用
#
来注释 FROM
指令告诉 Docker 使用哪个镜像作为基础- 接着是维护者的信息
RUN
开头的指令会在创建中运行,比如安装一个软件包,在这里使用 apt-get 来安装了一些软件
编写完成 Dockerfile 后可以使用 docker build
来生成镜像。
编写完成 Dockerfile 后可以使用 docker build
来生成镜像。
$ sudo docker build -t="ouruser/sinatra:v2" .
Uploading context 2.56 kB
Uploading context
Step 0 : FROM ubuntu:14.04
---> 99ec81b80c55
Step 1 : MAINTAINER Newbee <newbee@docker.com>
---> Running in 7c5664a8a0c1
---> 2fa8ca4e2a13
Removing intermediate container 7c5664a8a0c1
Step 2 : RUN apt-get -qq update
---> Running in b07cc3fb4256
---> 50d21070ec0c
Removing intermediate container b07cc3fb4256
Step 3 : RUN apt-get -qqy install ruby ruby-dev
---> Running in a5b038dd127e
Selecting previously unselected package libasan0:amd64.
(Reading database ... 11518 files and directories currently installed.)
Preparing to unpack .../libasan0_4.8.2-19ubuntu1_amd64.deb ...
Setting up ruby (1:1.9.3.4) ...
Setting up ruby1.9.1 (1.9.3.484-2ubuntu1) ...
Processing triggers for libc-bin (2.19-0ubuntu6) ...
---> 2acb20f17878
Removing intermediate container a5b038dd127e
Step 4 : RUN gem install sinatra
---> Running in 5e9d0065c1f7
. . .
Successfully installed rack-protection-1.5.3
Successfully installed sinatra-1.4.5
4 gems installed
---> 324104cde6ad
Removing intermediate container 5e9d0065c1f7
Successfully built 324104cde6ad
其中 -t
标记来添加 tag,指定新的镜像的用户信息。 “.” 是 Dockerfile 所在的路径(当前目录),也可以替换为一个具体的 Dockerfile 的路径。
可以看到 build 进程在执行操作。它要做的第一件事情就是上传这个 Dockerfile 内容,因为所有的操作都要依据 Dockerfile 来进行。 然后,Dockfile 中的指令被一条一条的执行。每一步都创建了一个新的容器,在容器中执行指令并提交修改(就跟之前介绍过的 docker commit
一样)。当所有的指令都执行完毕之后,返回了最终的镜像 id。所有的中间步骤所产生的容器都被删除和清理了。
*注意一个镜像不能超过 127 层
此外,还可以利用 ADD
命令复制本地文件到镜像;用 EXPOSE
命令来向外部开放端口;用 CMD
命令来描述容器启动后运行的程序等。例如
# put my local web site in myApp folder to /var/www
ADD myApp /var/www
# expose httpd port
EXPOSE 80
# the command to run
CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]
现在可以利用新创建的镜像来启动一个容器。
$ sudo docker run -t -i ouruser/sinatra:v2 /bin/bash
root@8196968dac35:/#
还可以用 docker tag
命令来修改镜像的标签。
$ sudo docker tag 5db5f8471261 ouruser/sinatra:devel
$ sudo docker images ouruser/sinatra
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ouruser/sinatra latest 5db5f8471261 11 hours ago 446.7 MB
ouruser/sinatra devel 5db5f8471261 11 hours ago 446.7 MB
ouruser/sinatra v2 5db5f8471261 11 hours ago 446.7 MB
*注:更多用法,请参考 Dockerfile 章节。
从本地文件系统导入
要从本地文件系统导入一个镜像,可以使用 openvz(容器虚拟化的先锋技术)的模板来创建: openvz 的模板下载地址为 templates 。
比如,先下载了一个 ubuntu-14.04 的镜像,之后使用以下命令导入:
sudo cat ubuntu-14.04-x86_64-minimal.tar.gz |docker import - ubuntu:14.04
然后查看新导入的镜像。
docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 14.04 05ac7c0b9383 17 seconds ago 215.5 MB
上传镜像
用户可以通过 docker push
命令,把自己创建的镜像上传到仓库中来共享。例如,用户在 Docker Hub 上完成注册后,可以推送自己的镜像到仓库中。
$ sudo docker push ouruser/sinatra
The push refers to a repository [ouruser/sinatra] (len: 1)
Sending image list
Pushing repository ouruser/sinatra (3 tags)
存出和载入Docker镜像
存出镜像
如果要导出镜像到本地文件,可以使用 docker save
命令。
载入镜像
可以使用 docker load
从导出的本地文件中再导入到本地镜像库,例如
$ sudo docker load --input ubuntu_14.04.tar
或
$ sudo docker load < ubuntu_14.04.tar
Docker 移除镜像
如果要移除本地的镜像,可以使用 docker rmi
命令。注意 docker rm
命令是移除容器。
$ sudo docker rmi training/sinatra
Untagged: training/sinatra:latest
Docker镜像的实现原理
Docker 镜像是怎么实现增量的修改和维护的? 每个镜像都由很多层次构成,Docker 使用 Union FS 将这些不同的层结合到一个镜像中去。
通常 Union FS 有两个用途, 一方面可以实现不借助 LVM、RAID 将多个 disk 挂到同一个目录下,另一个更常用的就是将一个只读的分支和一个可写的分支联合在一起,Live CD 正是基于此方法可以允许在镜像不变的基础上允许用户在其上进行一些写操作。 Docker 在 AUFS 上构建的容器也是利用了类似的原理。
Docker容器
在使用 -d
参数时,容器启动后会进入后台。 某些时候需要进入容器进行操作,有很多种方法,包括使用 docker attach
命令或 nsenter
工具等。
attach 命令
docker attach
是Docker自带的命令。下面示例如何使用该命令。
$ sudo docker run -idt ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia
$sudo docker attach nostalgic_hypatia
root@243c32535da7:/#
nsenter 命令
安装
nsenter
工具在 util-linux 包2.23版本后包含。 如果系统中 util-linux 包没有该命令,可以按照下面的方法从源码安装。
$ cd /tmp; curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24;
$ ./configure --without-ncurses
$ make nsenter && sudo cp nsenter /usr/local/bin
导出容器
如果要导出本地某个容器,可以使用 docker export
命令。
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
$ sudo docker export 7691a814370e > ubuntu.tar
这样将导出容器快照到本地文件。
导入容器快照
可以使用 docker import
从容器快照文件中再导入为镜像,例如
$ cat ubuntu.tar | sudo docker import - test/ubuntu:v1.0
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
此外,也可以通过指定 URL 或者某个目录来导入,例如
$sudo docker import http://example.com/exampleimage.tgz example/imagerepo
可以使用 docker rm
来删除一个处于终止状态的容器。 例如
$sudo docker rm trusting_newton
trusting_newton
新建并启动
所需要的命令主要为 docker run
。
例如,下面的命令输出一个 “Hello World”,之后终止容器。
$ sudo docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world
这跟在本地直接执行 /bin/echo 'hello world'
几乎感觉不出任何区别。
下面的命令则启动一个 bash 终端,允许用户进行交互。
$ sudo docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#
其中,-t
选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i
则让容器的标准输入保持打开。
在交互模式下,用户可以通过所创建的终端来输入命令,例如
root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
守护态运行Docker容器
更多的时候,需要让 Docker 容器在后台以守护态(Daemonized)形式运行。此时,可以通过添加 -d
参数来实现。
例如下面的命令会在后台运行容器。
$ sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147
容器启动后会返回一个唯一的 id,也可以通过 docker ps
命令来查看容器信息。
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e5535038e28 ubuntu:14.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute insane_babbage
终止Docker容器
可以使用 docker stop
来终止一个运行中的容器。
此外,当Docker容器中指定的应用终结时,容器也自动终止。 例如对于上一章节中只启动了一个终端的容器,用户通过 exit
命令或 Ctrl+d
来退出终端时,所创建的容器立刻终止。
终止状态的容器可以用 docker ps -a
命令看到。例如
sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton
98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike
处于终止状态的容器,可以通过 docker start
命令来重新启动。
此外,docker restart
命令会将一个运行态的容器终止,然后再重新启动它。
Docker Hub
目前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了超过 15,000 的镜像。大部分需求,都可以通过在 Docker Hub 中直接下载镜像来实现。
登录
可以通过执行 docker login
命令来输入用户名、密码和邮箱来完成注册和登录。 注册成功后,本地用户目录的 .dockercfg
中将保存用户的认证信息。
基本操作
用户无需登录即可通过 docker search
命令来查找官方仓库中的镜像,并利用 docker pull
命令来将它下载到本地。
例如以 centos 为关键词进行搜索:
$ sudo docker search centos
Docker的私有仓库
有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。
本节介绍如何使用本地仓库。
docker-registry
是官方提供的工具,可以用于构建私有的镜像仓库。
安装运行 docker-registry
容器运行
在安装了 Docker 后,可以通过获取官方 registry 镜像来运行。
$ sudo docker run -d -p 5000:5000 registry
这将使用官方的 registry 镜像来启动本地的私有仓库。 用户可以通过指定参数来配置私有仓库位置,例如配置镜像存储到 Amazon S3 服务。
$ sudo docker run \
-e SETTINGS_FLAVOR=s3 \
-e AWS_BUCKET=acme-docker \
-e STORAGE_PATH=/registry \
-e AWS_KEY=AKIAHSHB43HS3J92MXZ \
-e AWS_SECRET=xdDowwlK7TJajV1Y7EoOZrmuPEJlHYcNP2k4j49T \
-e SEARCH_BACKEND=sqlalchemy \
-p 5000:5000 \
registry
Docker配置文件
Docker 的 Registry 利用配置文件提供了一些仓库的模板(flavor),用户可以直接使用它们来进行开发或生产部署。
模板
在 config_sample.yml
文件中,可以看到一些现成的模板段:
common
:基础配置local
:存储数据到本地文件系统s3
:存储数据到 AWS S3 中dev
:使用local
模板的基本配置test
:单元测试使用prod
:生产环境配置(基本上跟s3配置类似)gcs
:存储数据到 Google 的云存储swift
:存储数据到 OpenStack Swift 服务glance
:存储数据到 OpenStack Glance 服务,本地文件系统为后备glance-swift
:存储数据到 OpenStack Glance 服务,Swift 为后备elliptics
:存储数据到 Elliptics key/value 存储
用户也可以添加自定义的模版段。
默认情况下使用的模板是 dev
,要使用某个模板作为默认值,可以添加 SETTINGS_FLAVOR
到环境变量中,例如
export SETTINGS_FLAVOR=dev
Docker 数据卷
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 卷会一直存在,直到没有容器使用
*数据卷的使用,类似于 Linux 下对目录或文件进行 mount。
创建一个数据卷
在用 docker run
命令的时候,使用 -v
标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。
下面创建一个 web 容器,并加载一个数据卷到容器的 /webapp
目录。
$ sudo docker run -d -P --name web -v /webapp training/webapp python app.py
*注意:也可以在 Dockerfile 中使用 VOLUME
来添加一个或者多个新的卷到由该镜像创建的任意容器。
挂载一个主机目录作为数据卷
使用 -v
标记也可以指定挂载一个本地主机的目录到容器中去。
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py
Docker 备份、恢复、迁移数据卷
可以利用数据卷对其中的数据进行进行备份、恢复和迁移。
备份
首先使用 --volumes-from
标记来创建一个加载 dbdata 容器卷的容器,并从本地主机挂载当前到容器的 /backup 目录。命令如下:
$ sudo docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
容器启动后,使用了 tar
命令来将 dbdata 卷备份为本地的 /backup/backup.tar
。
恢复
如果要恢复数据到一个容器,首先创建一个带有数据卷的容器 dbdata2。
$ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
然后创建另一个容器,挂载 dbdata2 的容器,并使用 untar
解压备份文件到挂载的容器卷中。
$ sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf
/backup/backup.tar
Docker容器如何互联
容器的连接(linking)系统是除了端口映射外,另一种跟容器中应用交互的方式。
该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。
自定义容器命名
连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。
虽然当创建容器的时候,系统默认会分配一个名字。自定义命名容器有2个好处:
- 自定义的命名,比较好记,比如一个web应用容器我们可以给它起名叫web
- 当要连接其他容器时候,可以作为一个有用的参考点,比如连接web容器到db容器
使用 --name
标记可以为容器自定义命名。
$ sudo docker run -d -P --name web training/webapp python app.py
使用 docker ps
来验证设定的命名。
$ sudo docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web
也可以使用 docker inspect
来查看容器的名字
$ sudo docker inspect -f "{{ .Name }}" aed84ee21bde
/web
注意:容器的名称是唯一的。如果已经命名了一个叫 web 的容器,当你要再次使用 web 这个名称的时候,需要先用docker rm
来删除之前创建的同名容器。
在执行 docker run
的时候如果添加 --rm
标记,则容器在终止后会立刻删除。注意,--rm
和 -d
参数不能同时使用。
容器互联
使用 --link
参数可以让容器之间安全的进行交互。
下面先创建一个新的数据库容器。
$ sudo docker run -d --name db training/postgres
删除之前创建的 web 容器
$ docker rm -f web
然后创建一个新的 web 容器,并将它连接到 db 容器
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py
此时,db 容器和 web 容器建立互联关系。
--link
参数的格式为 --link name:alias
,其中 name
是要链接的容器的名称,alias
是这个连接的别名。
使用 docker ps
来查看容器的连接
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db
aed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web
可以看到自定义命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。这表示 web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。
Docker 在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动 db 容器的时候并没有使用 -p
和 -P
标记,从而避免了暴露数据库端口到外部网络上。
Docker 通过 2 种方式为容器公开连接信息:
- 环境变量
- 更新
/etc/hosts
文件
Docker外部访问容器
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P
或 -p
参数来指定端口映射。
当使用 -P 标记时,Docker 会随机映射一个 49000~49900
的端口到内部容器开放的网络端口。
使用 docker ps
可以看到,本地主机的 49155 被映射到了容器的 5000 端口。此时访问本机的 49155 端口即可访问容器内 web 应用提供的界面。
$ sudo docker run -d -P training/webapp python app.py
$ sudo docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
同样的,可以通过 docker logs
命令来查看应用的信息。
$ sudo docker logs -f nostalgic_morse
* Running on http://0.0.0.0:5000/
10.0.2.2 - - [23/May/2014 20:16:31] "GET / HTTP/1.1" 200 -
10.0.2.2 - - [23/May/2014 20:16:31] "GET /favicon.ico HTTP/1.1" 404 -
-p(小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
。
映射所有接口地址
使用 hostPort:containerPort
格式本地的 5000 端口映射到容器的 5000 端口,可以执行
$ sudo docker run -d -p 5000:5000 training/webapp python app.py
此时默认会绑定本地所有接口上的所有地址。
映射到指定地址的指定端口
可以使用 ip:hostPort:containerPort
格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1
$ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
映射到指定地址的任意端口
使用 ip::containerPort
绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口。
$ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py
还可以使用 udp 标记来指定 udp 端口
$ sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
查看映射端口配置
使用 docker port
来查看当前映射的端口配置,也可以查看到绑定的地址
$ docker port nostalgic_morse 5000
127.0.0.1:49155.
注意:
- 容器有自己的内部网络和 ip 地址(使用
docker inspect
可以获取所有的变量,Docker 还可以有一个可变的网络配置。) - -p 标记可以多次使用来绑定多个端口
例如
$ sudo docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py
Dockerfile基本结构
Dockerfile 由一行行命令语句组成,并且支持以 #
开头的注释行。
一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。
例如
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
其中,一开始必须指明所基于的镜像名称,接下来推荐说明维护者信息。
后面则是镜像操作指令,例如 RUN
指令,RUN
指令将对镜像执行跟随的命令。每运行一条 RUN
指令,镜像添加新的一层,并提交。
最后是 CMD
指令,来指定运行容器时的操作命令。
下面是一个更复杂的例子
# Nginx
#
# VERSION 0.0.1
FROM ubuntu
MAINTAINER Victor Vieux <victor@docker.com>
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC
#
# VERSION 0.3
FROM ubuntu
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir /.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example
#
# VERSION 0.1
FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f
FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4
# You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.
Dockerfile的指令
指令的一般格式为 INSTRUCTION arguments
,指令包括 FROM
、MAINTAINER
、RUN
等。
FROM
格式为 FROM <image>
或FROM <image>:<tag>
。
第一条指令必须为 FROM
指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM
指令(每个镜像一次)。
MAINTAINER
格式为 MAINTAINER <name>
,指定维护者信息。
RUN
格式为 RUN <command>
或 RUN ["executable", "param1", "param2"]
。
前者将在 shell 终端中运行命令,即 /bin/sh -c
;后者则使用 exec
执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]
。
每条 RUN
指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \
来换行。
CMD
支持三种格式
CMD ["executable","param1","param2"]
使用exec
执行,推荐方式;CMD command param1 param2
在/bin/sh
中执行,提供给需要交互的应用;CMD ["param1","param2"]
提供给ENTRYPOINT
的默认参数;
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD
命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD
指定的命令。
EXPOSE
格式为 EXPOSE <port> [<port>...]
。
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。
ENV
格式为 ENV <key> <value>
。 指定一个环境变量,会被后续 RUN
指令使用,并在容器运行时保持。
例如
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADD
格式为 ADD <src> <dest>
。
该命令将复制指定的 <src>
到容器中的 <dest>
。 其中 <src>
可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。
COPY
格式为 COPY <src> <dest>
。
复制本地主机的 <src>
(为 Dockerfile 所在目录的相对路径)到容器中的 <dest>
。
当使用本地目录为源目录时,推荐使用 COPY
。
ENTRYPOINT
两种格式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
(shell中执行)。
配置容器启动后执行的命令,并且不可被 docker run
提供的参数覆盖。
每个 Dockerfile 中只能有一个 ENTRYPOINT
,当指定多个时,只有最后一个起效。
VOLUME
格式为 VOLUME ["/data"]
。
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。
USER
格式为 USER daemon
。
指定运行容器时的用户名或 UID,后续的 RUN
也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres
。要临时获取管理员权限可以使用 gosu
,而不推荐 sudo
。
WORKDIR
格式为 WORKDIR /path/to/workdir
。
为后续的 RUN
、CMD
、ENTRYPOINT
指令配置工作目录。
可以使用多个 WORKDIR
指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c
。
ONBUILD
格式为 ONBUILD [INSTRUCTION]
。
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A
。
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A
指定基础镜像时,会自动执行 ONBUILD
指令内容,等价于在后面添加了两条指令。
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用 ONBUILD
指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild
。
Dockerfile怎么创建镜像
编写完成 Dockerfile 之后,可以通过 docker build
命令来创建镜像。
基本的格式为 docker build [选项] 路径
,该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下所有内容发送给 Docker 服务端,由服务端来创建镜像。因此一般建议放置 Dockerfile 的目录为空目录。也可以通过 .dockerignore
文件(每一行添加一条匹配模式)来让 Docker 忽略路径下的目录和文件。
要指定镜像的标签信息,可以通过 -t
选项,例如
$ sudo docker build -t myrepo/myapp /tmp/test1/
Docker底层实现
Docker 底层的核心技术包括 Linux 上的名字空间(Namespaces)、控制组(Control groups)、Union 文件系统(Union file systems)和容器格式(Container format)。
我们知道,传统的虚拟机通过在宿主主机中运行 hypervisor 来模拟一整套完整的硬件环境提供给虚拟机的操作系统。虚拟机系统看到的环境是可限制的,也是彼此隔离的。 这种直接的做法实现了对资源最完整的封装,但很多时候往往意味着系统资源的浪费。 例如,以宿主机和虚拟机系统都为 Linux 系统为例,虚拟机中运行的应用其实可以利用宿主机系统中的运行环境。
我们知道,在操作系统中,包括内核、文件系统、网络、PID、UID、IPC、内存、硬盘、CPU 等等,所有的资源都是应用进程直接共享的。 要想实现虚拟化,除了要实现对内存、CPU、网络IO、硬盘IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC等等的相互隔离。 前者相对容易实现一些,后者则需要宿主机系统的深入支持。
随着 Linux 系统对于名字空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的名字空间中运行。大家虽然都共用一个内核和某些运行时环境(例如一些系统命令和系统库),但是彼此却看不到,都以为系统中只有自己的存在。这种机制就是容器(Container),利用名字空间来做权限的隔离控制,利用 cgroups 来做资源分配。
Docker 采用了 C/S架构,包括客户端和服务端。 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者 RESTful API 来进行通信。
Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。
Docker的Etcd项目
什么是 etcd
etcd 是 CoreOS 团队于 2013 年 6 月发起的开源项目,它的目标是构建一个高可用的分布式键值(key-value)数据库,基于 Go 语言实现。我们知道,在分布式系统中,各种服务的配置信息的管理分享,服务的发现是一个很基本同时也是很重要的问题。CoreOS 项目就希望基于 etcd 来解决这一问题。
etcd 目前在 github.com/coreos/etcd 进行维护,即将发布 2.0.0 版本。
受到 Apache ZooKeeper 项目和 doozer 项目的启发,etcd 在设计的时候重点考虑了下面四个要素:
- 简单:支持 REST 风格的 HTTP+JSON API
- 安全:支持 HTTPS 方式的访问
- 快速:支持并发 1k/s 的写操作
- 可靠:支持分布式结构,基于 Raft 的一致性算法
注:Apache ZooKeeper 是一套知名的分布式系统中进行同步和一致性管理的工具。注:doozer 则是一个一致性分布式数据库。注:Raft 是一套通过选举主节点来实现分布式系统一致性的算法,相比于大名鼎鼎的 Paxos 算法,它的过程更容易被人理解,由 Stanford 大学的 Diego Ongaro 和 John Ousterhout 提出。更多细节可以参考 raftconsensus.github.io。
一般情况下,用户使用 etcd 可以在多个节点上启动多个实例,并添加它们为一个集群。同一个集群中的 etcd 实例将会保持彼此信息的一致性。
Docker的Fig 项目
快速搭建基于 Docker 的隔离开发环境
使用 Dockerfile
文件指定你的应用环境,让它能在任意地方复制使用:
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
在 fig.yml
文件中指定应用使用的不同服务,让它们能够在一个独立的环境中一起运行:
web:
build: .
command: python app.py
links:
- db
ports:
- "8000:8000"
db:
image: postgres
*注意不需要再额外安装 Postgres 了!
接着执行命令 fig up
,然后 Fig 就会启动并运行你的应用了。
Fig 可用的命令有:
- 启动、停止,和重建服务
- 查看服务的运行状态
- 查看运行中的服务的输入日志
- 对服务发送命令
快速上手
我们试着让一个基本的 Python web 应用运行在 Fig 上。这个实验假设你已经知道一些 Python 知识,如果你不熟悉,但清楚概念上的东西也是没有问题的。
为你的项目创建一个目录
$ mkdir figtest
$ cd figtest
进入目录,创建 app.py
,这是一个能够让 Redis 上的一个值自增的简单 web 应用,基于 Flask 框架。
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.' % redis.get('hits')
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
在 requirements.txt
文件中指定应用的 Python 依赖包。
flask
redis
下一步我们要创建一个包含应用所有依赖的 Docker 镜像,这里将阐述怎么通过 Dockerfile
文件来创建。
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
以上的内容首先告诉 Docker 在容器里面安装 Python ,代码的路径还有Python 依赖包。关于 Dockerfile 的更多信息可以查看 镜像创建 和 Dockerfile 使用
接着我们通过 fig.yml
文件指定一系列的服务:
web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
这里指定了两个服务:
- web 服务,通过当前目录的
Dockerfile
创建。并且说明了在容器里面执行python app.py
命令 ,转发在容器里开放的 5000 端口到本地主机的 5000 端口,连接 Redis 服务,并且挂载当前目录到容器里面,这样我们就可以不用重建镜像也能直接使用代码。 - redis 服务,我们使用公用镜像 redis。
- 现在如果执行
fig up
命令 ,它就会拉取 redis 镜像,启动所有的服务。
$ fig up
Pulling image redis...
Building web...
Starting figtest_redis_1...
Starting figtest_web_1...
redis_1 | [8] 02 Jan 18:43:35.576 # Server started, Redis version 2.8.3
web_1 | * Running on http://0.0.0.0:5000/
这个 web 应用已经开始在你的 docker 守护进程里面监听着 5000 端口了(如果你有使用 boot2docker ,执行 boot2docker ip
,就会看到它的地址)。
如果你想要在后台运行你的服务,可以在执行 fig up
命令的时候添加 -d
参数,然后使用 fig ps
查看有什么进程在运行。
$ fig up -d
Starting figtest_redis_1...
Starting figtest_web_1...
$ fig ps
Name Command State Ports
-------------------------------------------------------------------
figtest_redis_1 /usr/local/bin/run Up
figtest_web_1 /bin/sh -c python app.py Up 5000->5000/tcp
fig run
指令可以帮你向服务发送命令。例如:查看 web 服务可以获取到的环境变量:
$ fig run web env
执行帮助命令 fig --help
查看其它可用的参数。
假设你使用了 fig up -d
启动 Fig,可以通过以下命令停止你的服务:
$ fig stop
以上内容或多或少的讲述了如何使用Fig 。通过查看下面的引用章节可以了解到关于命令、配置和环境变量的更多细节。如果你有任何想法或建议,可以在 GitHub 上提出。
安装 Fig
首先,安装 1.3 或者更新的 Docker 版本。
如果你的工作环境是 OS X ,可以通过查看 Mac 安装指南(英文) ,完成安装 Docker 和 boot2docker 。一旦 boot2docker 运行后,执行以下指令设置一个环境变量,接着 Fig 就可以和它交互了。
$(boot2docker shellinit)
*如果想避免重启后重新设置,可以把上面的命令加到你的 ~/.bashrc
文件里。
关于 Ubuntu
还有 其它的平台
的安装,可以参照 Ubuntu 安装指南(中文) 以及 官方安装手册(英文)。
下一步,安装 Fig :
curl -L https://github.com/docker/fig/releases/download/1.0.1/fig-`uname -s`-`uname -m` > /usr/local/bin/fig; chmod +x /usr/local/bin/fig
*如果你的 Docker 是管理员身份安装,以上命令可能也需要相同的身份。
目前 Fig 的发行版本只支持 OSX 和 64 位的 Linux 系统。但因为它是用 Python 语言写的,所以对于其它平台上的用户,可以通过 Python 安装包来完成安装(支持的系统同样适用)。
$ sudo pip install -U fig
到这里就已经完成了。 执行 fig --version
,确认能够正常运行。
ig客户端参考
大部分命令都可以运行在一个或多个服务上。如果没有特别的说明,这个命令则可以应用在所有的服务上。
执行 fig [COMMAND] --help
查看所有的使用说明。
选项
--verbose
显示更多信息。
--version
打印版本并退出。
-f, --file FILE
使用特定的Fig文件,默认使用fig.yml。
-p, --project-name NAME
使用特定的项目名称,默认使用文件夹名称。
命令
build
构建或重新构建服务。
服务一旦构建后,将会标记为project_service,例如figtest_db。 如果修改服务的 Dockerfile
或构建目录信息,你可以运行 fig build
来重新构建。
help
获得一个命令的帮助。
kill
强制停止服务容器。
logs
查看服务的输出。
port
打印端口绑定的公共端口。
ps
列出所有容器。
pull
拉取服务镜像。
rm
删除停止的服务容器。
run
在一个服务上执行一个命令。
例如:
$ fig run web python manage.py shell
默认情况下,链接的服务将会启动,除非这些服务已经在运行中。
一次性命令会在使用与服务的普通容器相同的配置的新容器中开始运行,然后卷、链接等等都将会按照期望创建。 与普通容器唯一的不同就是,这个命令将会覆盖原有的命令,如果端口有冲突则不会创建。
链接还可以在一次性命令和那个服务的其他容器间创建,然后你可以像下面一样进行一些操作:
$ fig run db psql -h db -U docker
如果你不希望在执行一次性命令时启动链接的容器,可以指定--no-deps选项:
$ fig run --no-deps web python manage.py shell
scale
设置一个服务需要运行的容器个数。
通过service=num的参数来设置数量。例如:
$ fig scale web=2 worker=3
start
启动一个服务已经存在的容器.
stop
停止一个已经运行的容器,但不删除它。通过 fig start
可以再次启动这些容器。
up
构建,(重新)创建,启动,链接一个服务的容器。
链接的服务都将会启动,除非他们已经运行。
默认情况, fig up
将会聚合每个容器的输出,而且如果容器已经存在,所有容器将会停止。如果你运行 fig up -d
,将会在后台启动并运行所有的容器。
默认情况,如果这个服务的容器已经存在, fig up
将会停止并重新创建他们(保持使用volumes-from挂载的卷),以保证 fig.yml
的修改生效。如果你不想容器被停止并重新创建,可以使用 fig up --no-recreate
。如果需要的话,这样将会启动已经停止的容器。
环境变量
环境变量可以用来配置Fig的行为。
变量以DOCKER_开头,它们和用来配置Docker命令行客户端的使用一样。如果你在使用 boot2docker , $(boot2docker shellinit)
将会设置它们为正确的值。
FIG_PROJECT_NAME
设置通过Fig启动的每一个容器前添加的项目名称.默认是当前工作目录的名字。
FIG_FILE
设置要使用的 fig.yml
的路径。默认路径是当前工作目录。
DOCKER_HOST
设置docker进程的URL。默认docker client使用 unix:///var/run/docker.sock
。
DOCKER_TLS_VERIFY
如果设置不为空的字符,允许和进程进行 TLS 通信。
DOCKER_CERT_PATH
配置 ca.pem
的路径, cert.pem
和 key.pem
文件用来进行TLS验证.默认路径是 ~/.docker
。
Docker Kubernetes 项目
Kubernetes 是 Google 团队发起并维护的基于Docker的开源容器集群管理系统,它不仅支持常见的云平台,而且支持内部数据中心。
建于Docker之上的Kubernetes可以构建一个容器的调度服务,其目的是让用户透过Kubernetes集群来进行云端容器集群的管理,而无需用户进行复杂的设置工作。系统会自动选取合适的工作节点来执行具体的容器集群调度处理工作。其核心概念是Container Pod(容器仓)。一个Pod是有一组工作于同一物理工作节点的容器构成的。这些组容器拥有相同的网络命名空间/IP以及存储配额,可以根据实际情况对每一个Pod进行端口映射。此外,Kubernetes工作节点会由主系统进行管理,节点包含了能够运行Docker容器所用到的服务。
本章将分为5节介绍Kubernetes。包括
- 项目简介
- 基本架构和基本概念
- 快速入门
- 实践例子
- 深入分析和高级话题。
项目简介
Kubernetes 是 Google 团队发起的开源项目,它的目标是管理跨多个主机的容器,提供基本的部署,维护以及运用伸缩,主要实现语言为Go语言。Kubernetes是:
- 易学:轻量级,简单,容易理解
- 便携:支持公有云,私有云,混合云,以及多种云平台
- 可拓展:模块化,可插拔,支持钩子,可任意组合
- 自修复:自动重调度,自动重启,自动复制
Kubernetes构建于Google数十年经验,一大半来源于Google生产环境规模的经验。结合了社区最佳的想法和实践。
在分布式系统中,部署,调度,伸缩一直是最为重要的也最为基础的功能。Kubernets就是希望解决这一序列问题的。
Kubernets 目前在github.com/GoogleCloudPlatform/kubernetes进行维护,截至定稿最新版本为 0.7.2 版本。
Kubernetes 能够运行在任何地方!
虽然Kubernets最初是为GCE定制的,但是在后续版本中陆续增加了其他云平台的支持,以及本地数据中心的支持。
Docker Kubernetes快速上手
目前,Kubernetes 支持在多种环境下的安装,包括本地主机(Fedora)、云服务(Google GAE、AWS 等)。然而最快速体验 Kubernetes 的方式显然是本地通过 Docker 的方式来启动相关进程。
下图展示了在单节点使用 Docker 快速部署一套 Kubernetes 的拓扑。
Kubernetes 依赖 Etcd 服务来维护所有主节点的状态。
启动 Etcd 服务
docker run --net=host -d gcr.io/google_containers/etcd:2.0.9 /usr/local/bin/etcd --addr=127.0.0.1:4001 --bind-addr=0.0.0.0:4001 --data-dir=/var/etcd/data
启动主节点
启动 kubelet。
docker run --net=host -d -v /var/run/docker.sock:/var/run/docker.sock gcr.io/google_containers/hyperkube:v0.17.0 /hyperkube kubelet --api_servers=http://localhost:8080 --v=2 --address=0.0.0.0 --enable_server --hostname_override=127.0.0.1 --config=/etc/kubernetes/manifests
启动服务代理
docker run -d --net=host --privileged gcr.io/google_containers/hyperkube:v0.17.0 /hyperkube proxy --master=http://127.0.0.1:8080 --v=2
测试状态
在本地访问 8080 端口,可以获取到如下的结果:
$ curl 127.0.0.1:8080
{
"paths": [
"/api",
"/api/v1beta1",
"/api/v1beta2",
"/api/v1beta3",
"/healthz",
"/healthz/ping",
"/logs/",
"/metrics",
"/static/",
"/swagger-ui/",
"/swaggerapi/",
"/validate",
"/version"
]
}
查看服务
所有服务启动后,查看本地实际运行的 Docker 容器,有如下几个。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee054db2516c gcr.io/google_containers/hyperkube:v0.17.0 "/hyperkube schedule 2 days ago Up 1 days k8s_scheduler.509f29c9_k8s-master-127.0.0.1_default_9941e5170b4365bd4aa91f122ba0c061_e97037f5
3b0f28de07a2 gcr.io/google_containers/hyperkube:v0.17.0 "/hyperkube apiserve 2 days ago Up 1 days k8s_apiserver.245e44fa_k8s-master-127.0.0.1_default_9941e5170b4365bd4aa91f122ba0c061_6ab5c23d
2eaa44ecdd8e gcr.io/google_containers/hyperkube:v0.17.0 "/hyperkube controll 2 days ago Up 1 days k8s_controller-manager.33f83d43_k8s-master-127.0.0.1_default_9941e5170b4365bd4aa91f122ba0c061_1a60106f
30aa7163cbef gcr.io/google_containers/hyperkube:v0.17.0 "/hyperkube proxy -- 2 days ago Up 1 days jolly_davinci
a2f282976d91 gcr.io/google_containers/pause:0.8.0 "/pause" 2 days ago Up 2 days k8s_POD.e4cc795_k8s-master-127.0.0.1_default_9941e5170b4365bd4aa91f122ba0c061_e8085b1f
c060c52acc36 gcr.io/google_containers/hyperkube:v0.17.0 "/hyperkube kubelet 2 days ago Up 1 days serene_nobel
cc3cd263c581 gcr.io/google_containers/etcd:2.0.9 "/usr/local/bin/etcd 2 days ago Up 1 days happy_turing
这些服务大概分为三类:主节点服务、工作节点服务和其它服务。
主节点服务
- apiserver 是整个系统的对外接口,提供 RESTful 方式供客户端和其它组件调用;
- scheduler 负责对资源进行调度,分配某个 pod 到某个节点上;
- controller-manager 负责管理控制器,包括 endpoint-controller(刷新服务和 pod 的关联信息)和 replication-controller(维护某个 pod 的复制为配置的数值)。
工作节点服务
- kubelet 是工作节点执行操作的 agent,负责具体的容器生命周期管理,根据从数据库中获取的信息来管理容器,并上报 pod 运行状态等;
- proxy 为 pod 上的服务提供访问的代理。
其它服务
- Etcd 是所有状态的存储数据库;
- gcr.io/google_containers/pause:0.8.0 是 Kubernetes 启动后自动 pull 下来的测试镜像。
Docker Kubernetes架构设计
基本架构和基本概念
任何优秀的项目都离不开好的架构和设计蓝图,在本小节,我们将来看一看Kubernetes是如何规划它的架构。为了理解和使用Kubernets,我们需要了解Kubernetes的基本概念和作用。
架构设计
- 节点:一个节点是一个运行Kubernetes中的主机。
- 容器组:一个Pod对应于由若干容器组成的一个容器组,同个组内的容器共享一个存储卷(volume)。
- 容器组生命周期:包含所有容器状态集合,包括容器组状态类型,容器组生命周期,事件,重启策略,以及replication controllers。
- Replication Controllers:主要负责指定数量的pod在同一时间一起运行。
- 服务:一个Kubernetes服务是容器组逻辑的高级抽象,同时也对外提供访问容器组的策略。
- 卷:一个卷就是一个目录,容器对其有访问权限。
- 标签:标签是用来连接一组对象的,比如容器组。标签可以被用来组织和选择子对象。
- 接口权限:端口,ip地址和代理的防火墙规则。
- web界面:用户可以通过web界面操作Kubernetes。
- 命令行操作:
kubecfg
命令。
节点
什么是节点
在Kubernetes中,节点是实际工作的点,以前叫做Minion。节点可以是虚拟机或者物理机器,依赖于一个集群环境。每个节点都有一些必要的服务以运行容器组,并且它们都可以通过主节点来管理。必要服务包括docker,kubelet和网络代理。
容器状态
容器状态用来描述节点的当前状态。现在,其中包含三个信息:
主机IP
主机IP需要云平台来查询,Kubernetes把它作为状态的一部分来保存。如果Kubernetes没有运行在云平台上,节点ID就是必需的。IP地址可以变化,并且可以包含多种类型的IP地址,如公共IP,私有IP,动态IP,ipv6等等。
节点周期
通常来说节点有 Pending
,Running
,Terminated
三个周期,如果Kubernetes发现了一个节点并且其可用,那么Kubernetes就把它标记为 Pending
。然后在某个时刻,Kubernetes将会标记其为 Running
。节点的结束周期称为 Terminated
。一个已经terminated的节点不会接受和调度任何请求,并且已经在其上运行的容器组也会删除。
节点状态
节点的状态主要是用来描述处于 Running
的节点。当前可用的有 NodeReachable
和 NodeReady
。以后可能会增加其他状态。NodeReachable
表示集群可达。NodeReady
表示kubelet返回 StatusOk并且HTTP状态检查健康。
节点管理
节点并非Kubernetes创建,而是由云平台创建,或者就是物理机器、虚拟机。在Kubernetes中,节点仅仅是一条记录,节点创建之后,Kubernetes会检查其是否可用。在Kubernetes中,节点用如下结构保存:
{
"id": "10.1.2.3",
"kind": "Minion",
"apiVersion": "v1beta1",
"resources": {
"capacity": {
"cpu": 1000,
"memory": 1073741824
},
},
"labels": {
"name": "my-first-k8s-node",
},
}
Kubernetes校验节点可用依赖于id。在当前的版本中,有两个接口可以用来管理节点:节点控制和Kube管理。
节点控制
在Kubernetes主节点中,节点控制器是用来管理节点的组件。主要包含:
- 集群范围内节点同步
- 单节点生命周期管理
节点控制有一个同步轮寻,主要监听所有云平台的虚拟实例,会根据节点状态创建和删除。可以通过 --node_sync_period
标志来控制该轮寻。如果一个实例已经创建,节点控制将会为其创建一个结构。同样的,如果一个节点被删除,节点控制也会删除该结构。在Kubernetes启动时可用通过 --machines
标记来显示指定节点。同样可以使用 kubectl
来一条一条的添加节点,两者是相同的。通过设置 --sync_nodes=false
标记来禁止集群之间的节点同步,你也可以使用api/kubectl 命令行来增删节点。
容器组
在Kubernetes中,使用的最小单位是容器组,容器组是创建,调度,管理的最小单位。
什么是容器组
一个容器组使用相同的Dokcer容器并共享卷(挂载点)。一个容器组是一个特定运用的打包集合,包含一个或多个容器。
和运行的容器类似,一个容器组被认为只有很短的运行周期。容器组被调度到一组节点运行,知道容器的生命周期结束或者其被删除。如果节点死掉,运行在其上的容器组将会被删除而不是重新调度。(也许在将来的版本中会添加容器组的移动)。
容器组设计的初衷
资源共享和通信
容器组主要是为了数据共享和它们之间的通信。
在一个容器组中,容器都使用相同的网络地址和端口,可以通过本地网络来相互通信。每个容器组都有独立的ip,可用通过网络来和其他物理主机或者容器通信。
容器组有一组存储卷(挂载点),主要是为了让容器在重启之后可以不丢失数据。
容器组管理
容器组是一个运用管理和部署的高层次抽象,同时也是一组容器的接口。容器组是部署、水平放缩的最小单位。
容器组的使用
容器组可以通过组合来构建复杂的运用,其本来的意义包含:
- 内容管理,文件和数据加载以及本地缓存管理等。
- 日志和检查点备份,压缩,快照等。
- 监听数据变化,跟踪日志,日志和监控代理,消息发布等。
- 代理,网桥
- 控制器,管理,配置以及更新
替代方案
为什么不在一个单一的容器里运行多个程序?
- 1.透明化。为了使容器组中的容器保持一致的基础设施和服务,比如进程管理和资源监控。这样设计是为了用户的便利性。
- 2.解偶软件之间的依赖。每个容器都可能重新构建和发布,Kubernetes必须支持热发布和热更新(将来)。
- 3.方便使用。用户不必运行独立的程序管理,也不用担心每个运用程序的退出状态。
- 4.高效。考虑到基础设施有更多的职责,容器必须要轻量化。
容器组生命周期
本小结将会简单描述容器状态类型,容器组生命周期,事件,重启策略和复制控制器。
状态值
pending
容器组已经被节点接受,但有一个或多个容器还没有运行起来。这将包含某些节点正在下载镜像的时间,这种情形会依赖于网络情况。
running
容器组已经被调度到节点,并且所有的容器都已经启动。至少有一个容器处于运行状态(或者处于重启状态)。
succeeded
所有的容器都正常退出。
failed
容器组中所有容器都意外中断了。
容器组生命周期
通常来说,如果容器组被创建了就不会自动销毁,除非被某种行为出发,而触发此种情况可能是人为,或者复制控制器所为。唯一例外的是容器组由 succeeded状态成功退出,或者在一定时间内重试多次依然失败。
如果某个节点死掉或者不能连接,那么节点控制器将会标记其上的容器组的状态为 failed
。
举例
- 容器组状态
running
,有1容器,容器正常退出- 记录完成事件 - 如果重启策略为:- 始终:重启容器,容器组保持
running
- 失败时:容器组变为
succeeded
-
从不:容器组变为
succeeded
- 容器组状态
running
,有1容器,容器异常退出- 记录失败事件 - 如果重启策略为:- 始终:重启容器,容器组保持
running
- 失败时:重启容器,容器组保持
running
-
从不:容器组变为
failed
- 容器组状态
running
,有2容器,有1容器异常退出- 记录失败事件 - 如果重启策略为:- 始终:重启容器,容器组保持
running
- 失败时:重启容器,容器组保持
running
-
从不:容器组保持
running
- 当有2容器退出 - 记录失败事件
- 如果重启策略为:- 始终:重启容器,容器组保持
running
- 失败时:重启容器,容器组保持
running
-
从不:容器组变为
failed
- 容器组状态
running
,容器内存不足- 标记容器错误中断 - 记录内存不足事件
- 如果重启策略为:- 始终:重启容器,容器组保持
running
- 失败时:重启容器,容器组保持
running
-
从不:记录错误事件,容器组变为
failed
- 容器组状态
running
,一块磁盘死掉- 杀死所有容器 - 记录事件
- 容器组变为
failed
-
如果容器组运行在一个控制器下,容器组将会在其他地方重新创建
- 容器组状态
running
,对应的节点段溢出- 节点控制器等到超时 - 节点控制器标记容器组
failed
- 如果容器组运行在一个控制器下,容器组将会在其他地方重新创建
Replication Controllers
服务
卷
标签
接口权限
web界面
命令行操作