最近在学习Docker,顺手记录一下学习笔记。
在介绍Docker之前我们,我们先举个例子引入Docker。
小明一直在A市的某小区居住,有一天小明想搬到B市去住,这时他就得搬家,连人带家具等等一通搬,搬到B市,又得重新布置家居等等。想想这个是不是麻烦,假如小明又想去C市去,又得重复上述一通操作。随着科技的发展有了更先进的技术,我们可以搬楼,将房子整体搬迁,随搬随住。
见上图所示,小明
相当于我们部署的项目,房子
等相当于Docker
,房子内的家居就相当于一个一个的容器
。
概念:
Docker是基于Go语言实现的开源项目
Docker的主要目标就是”build ,ship and Run any app anywhere”,意思就是我们通过对应用组件的封装
、分发
、部署
、运行
等生命周期的管理,使用户的app(WEB项目或数据库应用)及其运行环境能够做到’一次封装,到处运行’。
Docker对比vm的优势:
- docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在cpu、内存利用率上docker将会在效率上有明显提升
- docker利用的是宿主机的内核,而不需要Guest OS,因此当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。避免引寻、加载操作系统内核等比较费时费资源的过程。当新建一个虚拟机,虚拟机软件需要加载GuestOs,新建过程时分钟级别的。而docker由于直接利用宿主机的操作系统,因此新建一个docker容器只需要几秒钟。
Docker容器 | 虚拟机(VM) | |
---|---|---|
操作系统 | 与宿主机共享OS | 宿主机OS上运行虚拟机OS |
存储大小 | 镜像小,便于存储与传输 | 镜像大 |
运行性能 | 几乎无意外行损失 | 操作系统额外的cpu、内存消耗 |
移植性 | 轻便、灵活、适用于各系统 | 笨重,与虚拟化技术耦合高 |
硬件亲和性 | 面向软件开发者 | 面向运维 |
部署速度 | 快速,秒级 | 较慢 |
Docker帮助命令
docker version #docker版本信息
docker info #docker镜像、容器、cpu、内存等相关信息
docker help #查看相关的帮助命令
Docker镜像命令
docker images #查看本机镜像
# -a 显示本地所有镜像(包含中间镜像层)
# -q 只显示镜像ID
# --digests 显示镜像的摘要信息
# --no-trunc 显示完整镜像信息
docker search #搜索镜像
# -s [num]列出收藏数不小于 num 的镜像
# --no-trunc 显示完整镜像信息
docker pull #从hub上拉取镜像
docker pull mysql:5.6 #拉取5.6版本mysql镜像文件
docker rmi #删除镜像
# -f 强制删除某镜像
# --no-trunc 显示完整镜像信息
docker rmi -f $(docker images -qa)
#删除全部镜像
查看镜像的变更历史:
docker history 镜像名[镜像ID]
镜像的commit操作:
我们可以commit
提交容器副本使之成为一个新的镜像
docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]
Docker容器
新建并启动容器:
docker run [options] IMAGENAME
# --name 为运行容器起一个新名字
# --rm 停止容器,容器文件会自动删除
# -d 后台运行容器,并返回容器ID,即守护式启动
# -i 交互式运行容器,通常与 -t配合使用
# -t 为容器重新分配一个伪输入终端
# -e, --env=[], 指定环境变量,容器中可以使用该环境变量
# --link 表示该容器需要连接到其他容器,比如php需要连接到mysql
# -P 随机端口映射
# -p 指定端口映射,有以下四种格式:
ip:hostPort:containerPort
ip:containerPort
hostPort:containerPort
containerPort
# --volumes-from=[], 给容器挂载其他容器上的卷,挂载到容器的某个目录
官方
run
全部参数[点击]
列出运行的容器:
docker ps [options]
# -a 显示所有当前正在运行和过往运行的容器
# -l 显示最近创建运行的容器
# -n 显示最近n个创建的容器
# -q 静默模式,只显示容器编号
退出容器:
exit #容器停止并退出
ctrl + p +q #容器不停止 退出
启动 [重启] 容器:
docker start [restart] [容器ID或者容器名]
停止容器:
docker stop [名或ID]
强制停止容器:
docker kill [名或ID]
删除已停止的容器(历史运行的容器):
docker rm [容器ID]
#删除全部
docker rm -f $(docker ps -a -q)
docker ps -a -q|xargs docker rm -f
注意:使用centos:latest以后台启动一个容器
docker run -d centos
我们通过 docker ps -a
查看,发现容器已经退出,说明Docker容器后台运行,就必须有一个前台进程
容器的运行命令如果不是一直挂起的命令,比如top,tail等,就会自动退出
这是个Docker机制问题,假如我们的web容器,以nginx为例,正常情况下我们配置启动服务只需要启动响应的service即可。例如service nignx start
,但是这样nginx为后台守护模式,这就导致docker前台没有运行的应用,这样的容器后台启动后,会立刻自杀,因为它觉得没事可做。
查看容器日志:
docker logs -f -t --tail 容器ID
# -t 是加入时间戳
# -f 跟随最新的日志打印
# --tail 数字 显示最后多少条
查看容器内运行的进程:
docker top 容器ID
查看容器细节:
docker inspect 容器ID
进入正在运行的容器并以命令行交互:
docker exec -it 容器ID /bin/bash
#或
docker attach 容器ID
注意:上述两个的区别:attach 直接进入容器启动命令的终端,不会启动新的进程;exec 是在容器中打开新的终端,并且可以启动新的进程。并且exec 可以不进入命令交互查看容器相关信息,比如我们守护模式运行nginx,我们来查看nginx目录下文件,docker exec nginx容器ID ls
从容器内拷贝文件到主机上:
docker cp 容器ID:容器内路径 目的主机路径
Docker容器数据卷:
我们先来看一下Docker理念:将运行的环境打包成为容器来运行,运行可以伴随着容器,但是我们对于数据的要求是希望可以持久化;并且容器与容器之间可以共享数据。
Docker容器产生的数据,如果不通过docker commit
生成新的镜像,使得数据作为镜像的一部分保存下来,那么容器被删除后,容器运行产生的数据自然就没了。
为了能保存数据在docker
我们使用卷。
概念:卷就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但不属于联合文件系统,因此可以绕过UnionFileSystem提供一些用于持久存储或共享数据的特性。
卷的设计目的就是持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
特点:
- 数据卷可以在容器之间共享或重用数据
- 卷中的更改可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
使用 -V
命令添加数据卷:
docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
#带权限(只读) 容器目录只读
docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
容器停止退出后,主机修改后数据是否同步:同步
使用弊端:
- 出于可移植性和分享的考虑,使用 -v 主机目录:/容器目录 这种方式不能够直接在Dockerfile中实现
- 由于宿主机的目录 不是每个主机都相同的,不能够保证每个宿主机都有同样特定的目录
使用Dockerfile形式添加数据卷:
dockerfile是用来构建Docker镜像的构建文件,由一系列的命令和参数构成的脚本。
简单示例:
FORM centos #本地镜像
VOLUME ["/datavolumeContainer1/","/datavolumeContainer2/"]
CMD /bin/bash
上述代码:volume 设置了两个容器卷,我们可以通过docker inspect 容器ID
查看对应宿主机目录,切记卷目录要用双引号
.
数据卷构建过程:
- 编写dockerfile文件(定义了进程需要的一切东西,包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进行)
- docker build 生成新的自定义
镜像
文件 - docker run 运行(docker容器真正提供服务)
dockerfile
文件内容说明:
- 每条保留字指令都必须是大写字母且后面至少跟一个参数
- 指令按照从上倒下顺序执行
- #表示注册
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
docker
执行dockerfile
的流程分析:
- docker从基础镜像运行一个容器
- 执行一条指令并对容器作出修改
- 执行类似docker commit 的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
从应用软件的角度分析Dockerfile 、Docer镜像与Docker容器关系:
Dockerfile 是软件的原材料
Docker镜像是软件的交付品
Docker容器则可以认为是软件的运行状态
Dockerfile 面向开发,Docker镜像称为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
Dockerfile
的相关指令及使用案例:
FROM #基础镜像,当前新镜像基于哪个镜像[类似继承]
MAINTAINER #镜像的维护者的姓名和邮箱地址
RUN #容器构建时需要运行的命令
EXPOSE#当前容器对外暴露的端口
WORKDIR#指定在创建容器后,终端默认登录进来的工作目录
ENV#用来在构建镜像过程中设置环境变量
ADD#将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY#类似ADD ,拷贝文件和目录到镜像中;将从构建上下文目录中<源路径>的文件/目录 复制到新的一层的镜像内的<目标路径>位置;即WORKDIR目录
VOLUME#容器数据卷,用于数据保存和持久化工作
CMD#指定一个容器启动时要运行的命令(dockerfile可以有多个CMD指令,但只有最后一个生效,CMD会被docker run 之后的参数替换)
ENTRYPOINT#指定一个容器启动时要运行的命令,与CMD一样
ONBUILD#当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后,构建子镜像过程中,父镜像的onbuild被触发 。形式:【from faher镜像】
注意:在docker run
之后的命令参数
会被当作参数 传递给ENTRYPOINT
,之后形成新的命令组合;而如果使用CMD
命令,那么docker run
之后的命令参数
会替换掉 CMD
命令
示例:
自己构建一个centos镜像:改变它默认登录进来的目录,支持VIM编辑命令,可以使用ifconfig查看网卡相关配置
第一步:编写dockerfile如下:
FROM centos
ENV mypath /usr/local
WORKDIR $mypath
MAINTAINER houger<11@houger.cn>
ADD log.txt $mypath
#将宿主机的log文件拷贝到centos容器根目录
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo 'success .....'
CMD /bin/bash
第二步:构建新的基于dockerfile
镜像
docker build -f /dockerfile -t 镜像名:TAG .
#末尾有个 . 表示当前路径
第三步:启动
docker run -it 新镜像名
在容器终端查看当前目录,发现已经切换到 /usr/local
并且支持 vim
、ifconfig
命令
示例二:安装mysql
拉取mysql镜像
docker pull mysql:5.6
创建并运行容器,并-v形式添加数据卷,设置映射端口及连接密码
docker run --name myself_mysql -p 3388:3306
-v /usr/local/myself_mysql/data:/var/lib/mysql
-v /usr/local/myself_mysql/logs:/logs
-v /usr/local/myself_mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456
-d mysql:5.6
上述代码我们自定了容器名为myself_mysql
,映射宿主机端口为3388
添加了三个数据卷,分别映射日志、数据库数据、配置文件,并且以守护进程模式运行。
我们可以进入mysql
的终端交互模式:
docker exec -it mysql_container_id /bin/bash
进来之后我们可以登录数据库:
mysql -uroot -p #登录
创建表、插入数据等一通操作后,我们在宿主机映射目录即可查看到响应的数据,在宿主机终端我们可以以3388
端口登录容器mysql。