什么是容器
容器是一种轻量级,可移植、自包含的软件的打包技术,使应用程序可以在几乎任何地方以相同的方式运行。
容器由以下两部分组成
- 应用程序本身
- 依赖:应用程序需要的库或者其他软件容器在host操作系统的用户空间中运行与操作系统中的其他进程隔离
虚拟机与docker的比较:
为什么需要容器
容器使软件具备了超强的可移植能力
采用集装箱的思想为代码提供一个基于容器的标准化运输系统。docker可以将任何应用及其依赖打包成一个轻量级、可移植,自包含的容器,容器几乎可以运行在所有的操作系统上
集装箱和容器的对比
容器的优势
对开发人员:build once,runAnywhere
对运维人员:configure once runAnything(只需要配置好标准的runtime环境,就可以运行任何容器,容器消除了开发、测试、生产环境的不一致)
容器的架构
docker 架构
- docker的核心组件
Docker 客户端:client
Docker 服务端:docker daemon
Docker镜像:image
Registry
Docker 容器:Container -
架构图:
docker采用client/service 架构,客户端向服务端发送请求,服务器负责构建、运行、分发容器。客户的和服务端可以在同一个Host上运行,也可以通过REST API和socket与远程的服务器通信。
docker客户端
最常用的是docker命令,通过docker命令我们可以很方便的在Host上构建和运行容器
docker服务端
Docker Daemon是服务器组件,运行在docker host上,负责创建、运行
监控容器、构建,存储镜像。
默认配置只能响应来自本地Host的客户端请求,允许远程访问需要打开tcp监听:
编辑配置文件/etc/systemd/system/multi-user.target.wants/docker.service,环境变量ExecStart后添加-H tcp://0.0.0.0,允许来自任意ip的客户端连接
重启docker容器:
systemctl daemon-reload
systemctl restart docker.service
与远程服务器通信
docker -H ip [docker命令]
Docker 镜像
可将docker镜像看做只读模板,通过它可以创建Docker容器
镜像有多种生成方式
重无到有的创建镜像、下载别人制作好的镜像、在现有的镜像上创建新的镜像。将镜像的创建过程和内容写在一个描述文件中,这个文件被称为Dockerfile,通过执行docker build
来创建镜像。
docker容器
就是镜像的运行实例,镜像就像是docker的构建打包阶段,而容器则是启动和运行阶段
Registry
是存放镜像的仓库
docker pull
可以从Registry上下载镜像
docker run
则是先下载镜像,然后再启动容器
Docker 镜像
镜像的内部结构
从hello-world说起
- 从docker hub下载它
docker pull hello-world
- 用
docker images | grep
容器名 查看它
- 通过
docker run hello-world
来运行他
基础镜像
基础镜像通常是指其他进行可以基于此镜像进行扩展的镜像或者从scratch(0)开始构建
比如centos镜像
该镜像才199MB,该镜像有Linux的用户空间和内核空间组成,比我们的centos系统小很多
centos镜像的Dockerfile
FROM scratch
ADD centos-7-docker.tar.xz /
CMD['bin/bash']
第二行的ADD指令是添加centos的tar包,在制作镜像时会自动将这个tar包解压到 / 路径,生成 /dev,/bin等目录,可以在docker hub查看dockerfile文件描述
docker的分层结构
Docker可以通过扩展现有的镜像创建新的镜像
例如:
FROM debian
RUN apt-get emacs
RUN apt-get install tomcat
CMD['/bin/bash']
过程如下:
直接在debian基础镜像进行构建
安装emacs
安装tomcat
容器启动时执行bash
构建过程
新的镜像就这一层层叠加生成,采用这种叠加最大的好处就是共享资源,当有多个镜像从同一个基础镜像构建出来时,磁盘上仅保留一份基础镜像即可。
当多个容器镜像修改了同一个基础镜像内容时,基础镜像内容并不会被改变。
容器的copyOnWrite特性
当容器启动时一个新的容器可写层被加载到镜像的顶层,这一层被称作可写层,该层下面的叫做镜像层,所有对容器的改动都只发生在容器层,镜像层只是可读的,在镜像层中用户看到的是一个叠加后的文件系统,上层会覆盖下层。
添加文件: 在容器中创建文件时,新的文件会被添加到容器层。
读取文件:在容器中读取某个文件时 ,Docker会从上往下依次在各个容器层中查找,一旦找到就打开读入内存。
修改文件:在容器中修改已经存在的文件,会先从上往下依次寻找,找到后复制到容器层,然后修改。
删除文件:从上往下查找,找到后在容器中记录该删除操作。
只有修改时才会发生CopyOnWrite。
构建镜像
通常有很多的镜像可以直接用别人制作好的,比如数据库,web服务器等,只有当我们找不到相关的镜像,或者想在某个镜像基础上加入特定的功能时才需要我们去制作镜像。通常我们自己开发的软件是需要制作镜像的。
docker提供了两种方式来制作镜像,docker commit和Dockerfile。
docker commit
通过三个步骤创建
- 运行容器
- 修改容器
- 将容器保存为新的镜像
例如:
1、运行容器并进入容器终端
docker run -it ubuntu
如图,先在本地查找镜像,没找到然后再下载
2、对容器进行修改
apt-get update
对apt-get进行更新
3、退出容器
exit
4、blissful_bassi是系统分配的名字
5、接下来将运行中的容器保存为新的镜像
docker commit blissful_bassi new-ubuntu
6、查看new-ubuntu 的history,可以看见是一分钟我们刚刚创建的
docker history new-ubuntu
综上,这是通过手工创建的方式,仅在学习的时候用,在真实的项目中都是使用Dockerfile来进行创建的,因为手工创建的方式容易出错。
使用Dockerfile创建
Dockerfile是一个文本文件,用来记录镜像的所有构建步骤
自定义如下Dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get install -y vim
然后运行
docker build -t new-ubuntu2 .
输入整个构建过程如下(截取部分)
使用docker history看构建过程
镜像的缓存特性
Docker会缓存已有的镜像层,构建新的镜像时,如果某镜像层已经存在则直接使用,无需重新创建(下载镜像的时候也会使用)
Dockerfile的调试
- 从基础镜像运行一个容器(1)
- 执行一条指令对容器进行修改(2)
- 执行docker commit 生成新的镜像层(3)
- docker再基于刚刚提交的镜像运行一个新的容器(4)
然后重复2-4步骤就可以找出问题
Dockerfile常用指令
- FROM
指定基础镜像 - MAINTAINER
设置镜像的作者 - COPY
将文件从build context中复制到镜像 COPY src dest - ADD
从build context 复制文件到镜像,不同的是,如果src是归档文件(tar,zip,tgz,xz等),那么文件会被自动解压到dest - ENV
环境变量 比如 ENV HE="HELLO WORLD"
echo $HE 就会输出HELLO WORLD - EXPOSE
指定容器中的进程会监听某个端口,docker可以将该端口暴露出来 - VOLUME
将文件或者目录申明为VOLUME - WORKDIR
为后面的RUN,CMD,ENTRYPOINT,ADD或COPY指令设置镜像中的当前
工作目录 - RUN
在容器中运行指令的指令 - CMD
容器启动时运行指定的命令,Dockerfile中可以有多个CMD命令但是只有最后一个生效,CMD可以被docker run 之后的参数替换 - ENTRYPOINT
设置容器启动时运行的命令,Dockerfile 中可以有多个ENTRYPOINT 指令,CMD的参数或者docker run 之后的参数就会交给ENTRYPOINT
RUN & CMD & ENTRYPOINT 区别
RUN:执行命令并创建新的容器层。经常用于安装软件包
CMD:设置容器启动后默认执行的命令及其参数,但CMD能被docker run后面的参数替换
ENTRYPOINT 配置容器启动时的命令
shell 和exec交互模式
shell
exec
使用RUN apt-get && apt-get install (避免分开使用,保证apt-get是最新的)
常用命令
docker images
查看镜像
docker ps
或者
docker container ls
显示正在运行的容器
删除镜像
docker rmi imageId
持续更新中-----------------------------
笔记摘自《5分钟玩转docker容器技术》