Docker深入演示例子
为了加深大家对docker技术中的三大概念的理解,我再演示一个例子:拉取tomcat镜像,并部署web应用,并且运行。
docker search tomcat 在仓库中搜索tomcat镜像,部分截图如下:
docker pull tomcat 拉取tomcat镜像
这里为什么tomcat镜像的大小有529MB?tomcat 不会这么大啊。因为,tomcat镜像中包含了:tomcat应用,jvm,精简版的linux操作系统!
我们可以运行这个镜像以产生一个容器,以下命令根据tomcat镜像创建并运行一个实例
docker run -d -p 8081:8080 --name tomcat8081 tomcat:9
☐ -d: 表示在后台运行
☐ -p: 指定容器监听的端口和容器里面tomcat所监听的端口
☐ --name:指定当前tomcat实例的名称
docker ps 查看正在运行中的容器
每个容器都是一个linux系统,我们甚至可以直接进入这个系统,键入以下命令:
查看所有容器,包括正在运行当中和已经停止运行的容器
通过浏览器访问,能看到404也就说明访问到linux中的容器中的tomcat了!只不过该tomcat没有欢迎界面而已
在idea中,开发一个简单的web应用,并打成war包
<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocatinotallow="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gao</groupId>
<artifactId>myweb</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
</project>
打成war包:
将这个war包,传给linux系统
然后把war包部署到docker容器中的tomcat中,命令格式如下:
docker cp xxx.war 容器名:容器中的目录
此时需要重启容器
然后再通过浏览器来访问
再次体会一下docker的三大核心概念:
镜像 (应用+环境)
容器(就是操作系统,把镜像中的环境和应用运行起来)
仓库(存放各种镜像的集合)
下面让我们正式体验一把“build ship and run”以及“build once,run anywhere”
docker commit 以指定的容器为模板,创建新的镜像
以现在的tomcat8081为模板,利用docker commit制作一个新的镜像出来!
docker images,查看镜像
目标镜像 然后将gaotomcat这个镜像导出
然后sz命令将linux中的gaotomcat.tar文件传给windows
此后,可以再把这个文件传给其他linux系统,只要其他linux系统中也安装过docker,就可以通过docker load -i xxx.tar 命令将被压缩为tar文件的镜像文件导入本地仓库中。
Docker和虚拟机的区别
Docker本身不是容器,docker只是创建和管理容器的工具而已。
Docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源的虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
Docker容器与虚拟机各个维度的比较(面试专用)
特性 | 虚拟机 | 容器 |
隔离级别 | 操作系统级 | 进程级 |
隔离策略 | Hypervisor | CGroups |
系统资源 | 5~15% | 0~5% |
启动时间 | 分钟级 | 秒级 |
镜像存储 | GB-TB | KB-MB |
集群规模 | 上百 | 上万 |
高可用策略 | 备份、容灾、迁移 | 弹性、负载、动态 |
目前我们对docker已经有了一个比较直观的认识了,在继续深入学习之前,让我们把docker相关的命令进行一个总结和扩充:
docker命令 | 命令描述 |
docker -v | 查看docker版本信息 |
docker info | 查看docker详细信息 |
docker --help | 查看docker命令的帮助信息 |
docker images | 列出本地仓库中的镜像 |
docker search 镜像名字 | 在远程仓库上查找镜像 |
docker pull 镜像名字[:版本] | 从远程仓库中拉取镜像 |
docker rmi 镜像名字 | 根据镜像名字删除镜像 |
docker save -o 目标文件名字.tar 镜像名字[:版本号] | 导出docker镜像 |
docker load -i 压缩包名字.tar | 导入docker镜像 |
docker run 镜像名字 | 根据指定镜像创建容器并启动 |
docker run -d 镜像id | 后台运行容器,无需任何交互 |
docker ps | 查看正在运行中的容器 |
docker ps -a | 查询所有容器,无论是运行中的还是停止运行的 |
docker ps -qa | 查询所有容器的id,无论是运行中的还是停止运行的 |
docker start 容器id | 启动一个已经停止运行的容器 |
docker restart 容器id | 重启容器 |
docker stop 容器id | 停止容器(优雅地关闭,发送关闭信号) |
docker kill 容器id | 立即停止容器 |
docker rm 容器id | 删除已经停止的容器 |
docker rm -f 容器id | 删除容器,无论容器是否正在运行 |
docker logs 容器id | 查看容器日志 |
docker cp 文件 容器id:容器内路径 | 将指定文件拷贝到容器中的指定路径 |
docker inspect 容器id | 查看指定容器的详细信息 |
容器启动后直接退出的情况
首先查看centos镜像
也可以查看star数大于等于30的centos镜像
然后使用docker pull命令来拉取centos镜像
查看centos镜像是否下载成功
然后创建centos镜像的容器并启动:docker run centos
注意,centos容器在启动之后,就会立即退出!因为容器中运行的命令如果不是那些一直挂起的命令,就会自动退出的!为什么之前运行的tomcat镜像的容器,启动后,就不会立即退出呢?因为tomcat容器启动后,会立即开启tomcat应用,而tomcat应用会一直运行,也就是挂起。
为了重新演示如何创建一个新的centos容器时,不会让容器立即退出,我们先删除centos的旧容器:
再次启动centos容器:
docker run -it centos 创建一个新的容器并进入
docker exec -it 容器id bash 进入一个已经存在的旧容器,并且进入新的一层命令壳。
docker attach 容器id 进入一个已经存在的旧容器,并且进入已经存在的命令壳。
-i 表示以交互模式运行容器(就是能输入命令)
-t 表示使用伪终端
只要以交互模式运行centos容器,那么centos就会等待我们输入命令,也就是说,centos会挂起,有事可做,就不会直接关闭容器了! 但是一旦退出bash,centos容器就无事可做,容器也就会关闭。
操作 | 效果 |
ctrl+P+Q | 退出容器,但是不停止容器 |
docker attach 容器id | 再次进入正在运行的容器,直接进入已存在的bash |
docker exec -it 容器id bash | 再次进入正在运行的容器,多套一层bash |
exit | 退出一层bash命令壳 |
docker exec 容器id 命令 | 在容器外面发送命令给容器,让容器执行 |
Docker镜像加载原理
Docker镜像是由一层一层的文件系统组成的,这种层级的文件系统被称之为UnionFS。
比如,一个tomcat镜像,内部就是由一层一层的文件系统组成的, 当容器启动时,一个新的可写层被加载到镜像的顶部,这一层就是“容器层”,容器层之下的都是镜像层。我们可以在这个可写层上继续添加自己的内容,如下:
Docker镜像为什么要使用这种分层结构呢? 这是因为镜像分层后,每一层都可以被重用!比如我们先下载tomcat:9镜像,再下载tomcat:10镜像,会发现有很多镜像层不用重新下载:
Linux文件系统由bootfs和rootfs两部分组成
1. bootfs:包含bootloader(引导加载程序)和kernel(内核)
2. rootfs:root文件系统,包含Linux系统中/dev,/proc,/bin,/etc 等标准目录和文件
不同的Linux发型版本之间,bootfs基本是一样的,不同的是rootfs,比如:ubuntu,centos等
最底层是:bootfs,第二层是:rootfs,称为base image,然后再往上可以叠加其他的镜像文件,统一文件系统技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,隐藏了多个层的存在,从外部看起来只是一个单一的文件系统。所以,我们安装的tomcat镜像大小是600+MB,是因为里面还包含了jdk和centos这两个镜像层,而centos镜像层复用了宿主机的bootfs,下载的只有rootfs,所以centos是很小的。
Docker数据卷
我们已经知道,docker镜像中包含着应用以及运行应用所需的环境。如此,根据docker镜像所产生的容器,也就包含着应用以及运行应用所需的环境。
问题是:容器在运行中所产生的数据,会随着容器被删除而丢失。
有两种办法可以把容器运行时所产生的数据永久地保存下来(持久化)。
1.通过docker commit命令来根据容器生成新的镜像;
2.使用docker容器数据卷,简称为卷。下面我们就详细地学习一下docker容器数据卷的内容。
卷就是宿主机中的目录或文件,由Docker挂载到容器中。设计卷的目的,就是完成容器中数据的持久化操作。卷是完全独立于容器的,不会随着容器的删除而被删除。
卷的作用,就是能将容器的数据传递给主机,也能将主机的数据传递到容器中。
使用卷:
docker run -it -v /主机目录:/容器中的目录 镜像名
当执行了以上命令以后,会在主机和容器中,自动创建相应的目录!
此时,无论是修改了主机上的/foo目录中的内容还是容器中的/foo的内容,对方都能感知。 甚至在容器关闭后,修改了主机中的/foo目录内容,当再次启动容器时,容器中/foo的内容仍然会与主机上/foo的内容一致!以下是测试时的一些相关命令。
以下创建的卷,在主机中可读可写,在容器中是只读的
docker run -it -v /主机目录:/容器内目录:ro 镜像名
再来一个例子,将一个卷挂在到一个Nginx容器中:
1. 在宿主机上,创建挂载目录
mkdir -p /data/nginx/{conf,conf.d,html,logs}
2. 启动Nginx容器
docker run \
--name nginx81 \
-d -p 81:80 \
-v /data/nginx/html/:/usr/share/nginx/html \
nginx
3. 访问Nginx,看到403,是因为Nginx没有欢迎界面index.html
4. 在宿主上,/data/nginx/html目录下创建一个index.html页面
touch index.html
echo 'Welcome to nginx!' > index.html
5. 再次访问Nginx:
Docker容器中vi键入中文是乱码
echo "LANG=C.UTF-8" >> /root/.bashrc
source /root/.bashrc
cat << EOF > /root/.vimrc
:set encoding=utf-8
:set fileencodings=ucs-bom,utf-8,cp936
:set fileencoding=gb2312
:set termencoding=utf-8
EOF