Docker容器实战-深入理解
一、Docker架构
Docker使用的是C/S架构,Docker Client与Docker Daemon进行交互,主要工作如拉取镜像、编译镜像、运行容器、发布容器等由Docker Daemon完成
Docker Client和Docker Daemon之间通过在Socket上使用RESTful API进行交互,所以Docker Client和Docker Daemon可以运行在同一个系统上也可以通过远程的方式访问
Docker Daemon
用户不是直接同Docker Daemon进行交互而是通过Docker Client访问
Docker Client
主要的用户访问Docker的通道,通过它对Docker Daemon进行控制
Docker Image
只读模板,用于创建容器
Docker Registry
存放Image的仓库,公共的Docker Registry位于Docker Hub,国内访问较慢
Docker Containers
包含应用程序运行所需的所有环境(类似一个文件夹),每个容器都是完全隔离的、安全的应用
二、Docker工作方式
1.Docker Image工作方式
前面也多次提到,Docker Image是只读模板,随容器一起启动。
Docker Image包含多个层,使用Union File System(将文件和目录进行透明的层叠组装,形成单独的文件系统)将这些层组合成为一个镜像。当用户修改或者说更新Docker Image时,会建立一个新的层,也就是增量式的修改,而不是新建一个全新的镜像
镜像来源于最基础的镜像,如Ubuntu,Docker Image都是从这些基础镜像上衍生而来,编译镜像是由系列指令组成,每个指令在镜像上创建一个新的增量
2.Docker Container工作方式
容器由操作系统、用户文件和元数据构成。当容器运行镜像时,容器会在Union FS顶层增加文件层,镜像告诉容器包含的内容、运行的程序及配置信息
例如在上一节Docker容器实战-简单使用中运行的docker run -i -t ubuntu /bin/bash
,Docker Client通过run子命令告诉Daemon启动一个新的容器,其具体流程为:
拉取Ubuntu镜像:检查本地是否有镜像,如果没有则自动从Docker Hub拉取
创建容器:当本地存在Ubuntu镜像后,Docker通过其创建容器
分配文件系统并挂载RW层:容器是创建在文件系统中,并且在其之上增加读写层
分配网络/桥接模式:创建桥接网络接口,使容器可以和本机主机进行通信
设置IP地址:选取可用的IP挂载到容器上
启动进程:在容器启动时运行命令,这里就是/bin/bash
抓取应用程序输出:捕捉程序的stdin、stdout、stderr,查看系统运行情况
三、通过容器运行Web应用
通过docker pull命令拉取镜像,之前的apache-php镜像现在是tutum/apache-php镜像,tutum/apache-php - Docker Hub
通过docker run命令启动镜像运行容器,其中-p指定主机和容器的端口映射
访问ip地址的8088端口可以看到如下页面
进入容器,其中docker exec
可以在指定容器中运行命令,如果不加-it参数,执行完成后容器就会退出。
毕竟不懂php怕自己改错了就把原来的index.php备份了一份
修改index.php页面内容
刷新刚刚的页面,也可以看到php的相关信息,太长了截不到最下面的Hello World
因为容器在退出后不会更改镜像,如果希望保存容器中的数据,就需要使用commit命令保存成新的镜像
四、镜像制作
之前零零散散介绍了下镜像,现在就系统性的介绍镜像的基本概念。
镜像是容器的基础,当运行docker run时,通常指定一个镜像,该镜像了承载容器的文件系统,也包含容器启动时需要执行的入口文件等信息
1. 查看镜像
功能描述:查看本机镜像
语法:docker images [镜像名称]
提示:如果不加镜像名称则列出本机镜像列表
2.获取镜像
(1)拉取镜像
语法:docker pull 镜像名
镜像名格式:[域名/][用户名/]镜像名[:版本号]
注意:当用户指定了域名时会从该域名下载,否则从Docker Hub下载;域名是隶属于该域名下的子目录,在使用私有仓库时有用;版本号默认为latest
(2)通过容器转换
功能描述:把容器转换为镜像
语法:docker commit 容器ID 镜像名
(3)制作镜像
通过Dockerfile生成镜像,Dockerfile类似于Makefile,加入了制作镜像的指令。
3. 查找镜像
功能描述:找到Docker Hub镜像
语法:docker search [-s/–no-trunc] TERM
-s 数值,收藏数不小于指定值的镜像
–no-trunc 显示完整的镜像描述
例子:docker search mysql/java 搜索Docker Hub中的所有MySQL/Java镜像
功能描述:查找其他仓库镜像
语法:curl 网址
4.push镜像
之前都是从Hub上拉取镜像,那么如何push镜像呢?
过程描述:
在Docker Hub上创建账号
docker tag 镜像名 目的地 -> 标记镜像push的目的地,为push做准备(必须)
docker login
docker push 目的地
注意:因为我是push到Docker Hub上,所以没有跟Docker Hub的网址,如果是push到别的Docker仓库,需要添加上全部网址
本来想push my-apache-php镜像的,但是太大了果断放弃,push最基础的hello-world镜像,然后登陆到Docker Hub,可以看到刚刚上传的镜像
5. 根据Dockerfile编译镜像
Dockerfile和Makefile类似,也是编译脚本,用于生成镜像
过程描述:
创建一个文件夹,并新建Dockerfile
docker build . 生成镜像
查看新生成的镜像
6. 删除镜像
语法描述:docker rmi 镜像名/镜像ID
注意:删除前要确保没有容器在使用该镜像,否则会报错
五、docker run
docker run是Docker使用频率最高,也最为复杂的命令,可以用它来启动容器。前面Docker容器实战-简单使用中有简单的讲过该命令,这里对其进行一个较为详细的补充
1. 语法格式
语法:docker run [OPTIONS] IMAGE [:TAG|@DIGEST] [COMMAND] [ARG…]
注意:该命令中Image必须指定
镜像默认包含了前台或后台运行、容器ID、网络设置、运行中的CPU与内存,但可以通过[OPTIONS]修改,如果不进行修改,则首先会使用镜像本身设置的参数再使用全局默认参数
2. 前后台运行
启动容器时使用选项-d(detached)=true或者直接-d,不与容器的stdin、stdout绑定
当以-d参数运行后,可以使用docker attach 容器ID
重新绑定到当前shell终端,同样如果要恢复-d模式,则使用Ctrl+PQ
当不选择参数时,就表示放在前台运行,如果要attach到容器中的shell,可以添加-it选项
3. 容器标识
容器有三种方式来标识,长UUID、短UUID、Name,其中UUID由Docker Daemon产生,在一台主机是唯一的,在创建容器时可以使用–name属性来指定容器名称,如果不指定则系统自动分配一个字符串
容器在启动时必须制定镜像名,格式为Image[:TAG],例如ubuntu:14.04 ,或者Image[@DIGEST],镜像V2版本开始,每个镜像都有一个含有其内容的签名,可以通过docker inspect ImageID
查看
4. PID设置
一般主机上的进程ID是从1开始,为init进程,而容器中程序的PID也是从1开始,这里就是利用了PID Namespace实现,如果需要在容器中共享主机的PID Namespace,就可以加上--pid=host
进行设置
同样UTS(–uts) IPC(–ipc)也可以进行相应的设置
5.网络设置
容器有五种网络方式,默认使用bridge
这里就主要说下bridge,桥接是在主机上的,通常为docker0,每启动一个容器就会为容器创建一个veth用于连接docker0和容器内的eth0
具体网络详情会在后面的Docker容器实战-容器的网络与数据中讲到
6.其他选项
(1)Clean Up
前面说过,通过docker ps -a可以看到所有容器,包括运行中和停止的。默认当容器退出后不会被删除,也就是说容器退出后本身还存在于主机上,所以一定要对不需要的容器进行清理,否则会占用很大磁盘空间
运行容器时通过--rm
可以在容器退出时自动删除容器
(2)特权模式
默认情况下,容器运行在非特权模式下,不能访问任何宿主机的设备,如果要使用就需要使用--privileged
参数,如果希望使用某个特定设备,则可以使用--device=设备文件名
(3)覆盖Image默认参数
当用户编写Dockerfile时可能会附带默认参数,目前FROM,MAINTAINER,RUN,ADD不能覆盖,其余的CMD(默认命令或属性),ENTRYPOINT(默认执行的命令),EXPOSE(导出的端口),ENV(环境变变量),USER,WORKDIR都是可以覆盖的
CMD和ENTRYPOINT相比,CMD更像参数命令,若在run时指定了命名,则CMD就会作为参数;ENTRYPOINT则是镜像的默认启动项,如果CMD和用户指定参数都会作为ENTRYPOINT的参数