Dokcer学习之旅(1)——运行一个简单的容器
基本概念
镜像
我们都知道,操作系统分为 内核 和 用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。
分层存储
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
这种分层的结构具有几个重要的优势:
- 快速构建和重用:如果多个镜像共享相同的层,那么这些层可以被缓存并重复使用,从而加快构建过程。
- 更小的镜像大小:由于每个指令只添加或更改一个层,因此镜像可以被精确地构建,只包含所需的文件和更改,从而减小镜像的大小。
- 更容易管理:由于每个层都是独立的,可以更容易地管理和更新镜像。当应用程序的某个组件发生变化时,只需重新构建涉及该组件的层,而不需要重新构建整个镜像。
容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者 绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
仓库
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。
镜像操作
现在,先让我们从0到1,逐步跑起来一个docker容器。
命令行输入docker,可以查看到docker支持的所有命令,docker --help 可以查询更多详情。
root@VM-24-8-debian:~# docker
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Common Commands:
run Create and run a new container from an image
exec Execute a command in a running container
ps List containers
build Build an image from a Dockerfile
pull Download an image from a registry
push Upload an image to a registry
images List images
login Log in to a registry
logout Log out from a registry
search Search Docker Hub for images
version Show the Docker version information
info Display system-wide information
Management Commands:
builder Manage builds
buildx* Docker Buildx (Docker Inc., v0.10.5)
compose* Docker Compose (Docker Inc., v2.18.1)
container Manage containers
context Manage contexts
image Manage images
manifest Manage Docker image manifests and manifest lists
network Manage networks
plugin Manage plugins
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
Swarm Commands:
swarm Manage Swarm
Commands:
attach Attach local standard input, output, and error streams to a running container
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes to files or directories on a container's filesystem
events Get real time events from the server
export Export a container's filesystem as a tar archive
history Show the history of an image
import Import the contents from a tarball to create a filesystem image
inspect Return low-level information on Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
logs Fetch the logs of a container
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
rmi Remove one or more images
save Save one or more images to a tar archive (streamed to STDOUT by default)
start Start one or more stopped containers
stats Display a live stream of container(s) resource usage statistics
stop Stop one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
wait Block until one or more containers stop, then print their exit codes
Global Options:
--config string Location of client config files (default "/root/.docker")
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket to connect to
-l, --log-level string Set the logging level ("debug", "info", "warn", "error", "fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/root/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Run 'docker COMMAND --help' for more information on a command.
获取镜像
$ docker pull [选项][Docker Registry 地址[:端口号]/]仓库名[:标签]
例如$ docker pull ubuntu
查询镜像
要想列出已经下载下来的镜像,可以使用 docker image ls
命令。
root@VM-24-8-debian:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 9c7a54a9a43c 2 months ago 13.3kB
ubuntu latest 3b418d7b466a 2 months ago 77.8MB
列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间,镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个 标签。
运行镜像
启动容器
docker run -it --rm ubuntu bash
-it
:这是两个参数,一个是-i
:交互式操作,一个是-t
终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。--rm
:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动docker rm
。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用--rm
可以避免浪费空间。ubuntu:18.04
:这是指用ubuntu:18.04
镜像为基础来启动容器。bash
:放在镜像名后的是 命令,这里我们希望有个交互式 dockerfile,因此用的是bash
。
到这里,我们就很轻松的跑起来了一个最基础的容器,是不是看起来很简单
后台运行
在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的运行模式。$ docker run -itd <容器ID> /bin/bash
进入容器
在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:
**docker attach**
**docker exec**
:推荐大家使用 docker exec 命令,因为此命令会退出容器终端,但不会导致容器的停止。
docker exec -it <容器ID> /bin/bash
停止运行
$ docker stop <容器 ID>
重启容器
$ docker restart <容器 ID>
删除镜像
$ docker image rm[选项]<镜像1>[<镜像2>...]
// 如果有容器正在使用该镜像,无法正常删除
root@VM-24-8-debian:~# docker image rm 9c7a
Error response from daemon: conflict: unable to delete 9c7a54a9a43c (must be forced) - image is being used by stopped container 0440b31f2027
// 删除对应容器
root@VM-24-8-debian:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d28f5ef78fcb ubuntu "/bin/bash" 23 minutes ago Exited (0) 23 minutes ago distracted_feynman
0440b31f2027 hello-world "/hello" 5 weeks ago Exited (0) 5 weeks ago serene_borg
root@VM-24-8-debian:~# docker rm 044
044
// 现在就可以正常删除镜像
root@VM-24-8-debian:~# docker image rm 9c7a
Untagged: hello-world:latest
Untagged: hello-world@sha256:fc6cf906cbfa013e80938cdf0bb199fbdbb86d6e3e013783e5a766f50f5dbce0
Deleted: sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d
Deleted: sha256:01bb4fce3eb1b56b05adf99504dafd31907a5aadac736e36b27595c8b92f07f1
Untagged 和 Deleted
如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是 Untagged,另一类是 Deleted。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。
因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。
当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变得非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 docker pull 看到的层数不一样的原因。
除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。
tips
$ docker image rm$(docker image ls-q redis)
像其它可以承接多个实体的命令一样,可以使用 docker image ls -q 来配合使用 docker image rm,这样可以成批的删除希望删除的镜像。
导出/导入容器
例如
将容器导出到Ubuntu.tar$ docker export 1e560fca3906 > ubuntu.tar
使用 docker import
从容器快照文件中再导入为镜像 $ docker import ubuntu.tar ubuntu:v1
运行一个WEB应用容器
运行容器
我们将在docker容器中运行一个 Python Flask 应用来运行一个web应用。-P 可以指定端口
runoob@runoob:~# docker pull training/webapp # 载入镜像
root@VM-24-8-debian:~# docker run -d -p 6666:5000 training/webapp python app.py
131f2c32413d8ca819661c2891f1e933216d245f0c347cf281bd03f0d6775e6a
// docker开放了5000端口,映射到主机端口6666上
root@VM-24-8-debian:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
131f2c32413d training/webapp "python app.py" 2 seconds ago Up 1 second 0.0.0.0:6666->5000/tcp, :::6666->5000/tcp infallible_darwin
645ddcba1194 training/webapp "python app.py" 4 minutes ago Up 4 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp interesting_villani
d28f5ef78fcb ubuntu "/bin/bash" 2 hours ago Up 40 minutes distracted_feynman
也可以使用docker port <容器id>
查询 容器端口的映射情况
查询WEB应用日志
docker logs <容器ID>
root@VM-24-8-debian:~# docker logs 13
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
查看应用程序容器的进程
我们还可以使用docker top <容器ID>
来查看容器内部运行的进程
root@VM-24-8-debian:~# docker top 13
UID PID PPID C STIME TTY TIME CMD
root 2185308 2185287 0 18:57 ? 00:00:00 python app.py
小结
docker各种原理听起来可能很复杂,但其实自己动手用起来,能用到的也不算多,相对难度也没那么大。
PS:下面两个网站总结的很好,文章中抄取了部分内容
利用 commit 理解镜像构成 - Docker — 从入门到实践
Docker 容器使用 | 菜鸟教程
Dokcer学习之旅(1)——运行一个简单的容器的更多相关文章
- 一个简单servlet容器
一个简单servlet容器 2.1 javax.servlet.Servlet接口 Servlet编程需要使用javax.servlet和javax.servlet.http两个包下的接口和类 在所有 ...
- maven权威指南学习笔记(三)——一个简单的maven项目
目标: 对构建生命周期 (build lifecycle),Maven仓库 (repositories),依赖管理 (dependency management)和项目对象模型 (Project O ...
- Java学习笔记 11/15:一个简单的JAVA例子
首先来看一个简单的 Java 程序. 来看下面这个程序,试试看是否看得出它是在做哪些事情! 范例:TestJava.java // TestJava.java,java 的简单范例 public ...
- 使用Dockerfile创建一个tomcat镜像,并运行一个简单war包
docker已经看了有一段时间了,对镜像和容器也有了一个大致了解,参考书上的例子制作一个tomcat镜像,并简单运行一个HelloWorld.war 1.首先下载linux环境的tomcat和jdk, ...
- 使用一个Python脚本来运行一个简单的Django项目
创建视图 Django是一个模型-模板-视图(model-template-view,MTV)框架. 视图部分通常检查看HTTP给出的请求和查询或者结构,这些信息是发送到表示层的数据. 我们在 hel ...
- ubuntu14.04 配置g++工具,并运行一个简单的c++文件
首先,对Ubuntu 14.04 LTS进行源更新,摘自下述链接: http://chenrongya.blog.163.com/blog/static/8747419620143185103297/ ...
- 【Java学习笔记】如何写一个简单的Web Service
本Guide利用Eclipse以及Ant建立一个简单的Web Service,以演示Web Service的基本开发过程: 1.系统条件: Eclipse Java EE IDE for Web De ...
- Django学习 之 Django安装与一个简单的实例认识
一.Django简介 1.MVC与MTV模型 (1)MVC模型 Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的. ...
- Python框架学习之用Flask创建一个简单项目
在前面一篇讲了如何创建一个虚拟环境,今天这一篇就来说说如何创建一个简单的Flask项目.关于Flask的具体介绍就不详细叙述了,我们只要知道它非常简洁.灵活和扩展性强就够了.它不像Django那样集成 ...
- 理解与模拟一个简单servlet容器
servlet接口 使用servlet编程需要实现或者继承实现了javax.servlet.Servlet接口的类,其中定义了5个签名方法: public void init(ServletConfi ...
随机推荐
- 2020-08-21:网络IO模型有哪些?
福哥答案2020-08-21: 福哥口诀法:阻非复信异(阻塞.非阻塞.多路复用.信号驱动.异步) [知乎答案](https://www.zhihu.com/question/416128059)操作系 ...
- “中国法研杯”司法人工智能挑战赛:基于UTC的多标签/层次分类小样本文本应用,Macro F1提升13%+
"中国法研杯"司法人工智能挑战赛:基于UTC的多标签/层次分类小样本文本应用,Macro F1提升13%+ 相关文章推荐: 本项目主要完成基于UTC的多标签应用,更多部署细节请参考 ...
- 《MS17-010(永恒之蓝)—漏洞复现及防范》
作者: susususuao 免责声明:本文仅供学习研究,严禁从事非法活动,任何后果由使用者本人负责. 一. 什么是永恒之蓝? - 永恒之蓝 永恒之蓝(Eternal Blue)是一种利用Window ...
- CSharp初体验
入门 初来乍到了解一门新的语言,它可能和熟悉的c/c++有不小差别,整体上需要首先了解下语法文件的整体结构.例如,源文件整体结构如何. 乍看CSharp源文件(compile unit)的结构,官网主 ...
- kprobe_events shell模式使用教程
kprobe_events shell模式使用教程 kprobe 使用前提 需要内核启用以下配置 CONFIG_KPROBES=y CONFIG_HAVE_KPROBES=y CONFIG_KPROB ...
- 【Python&GIS】判断图片中心点/经纬度点是否在某个面内
Python的exifread库可以获取图片中的源数据信息,包括经纬度.相机厂商.曝光时间.焦距.拍摄时间.拍摄地点等等信息.我们可以通过exifread库从图片中获取图片的经纬度,再通过shape ...
- C#.NET Framework RSA 公钥加密 私钥解密 ver:20230609
C#.NET Framework RSA 公钥加密 私钥解密 ver:20230609 环境说明: .NET Framework 4.6 的控制台程序 . .NET Framework 对于RSA的支 ...
- 解决适用EntityFramework生成时报错“无法解析依赖项。"EntityFramework 6.4.4" 与 ' EntityFramework.zh-Hans 6.2.0 约束:EntityFramework(=6.2.0)'不兼容。"
起因:通过vs2022创建mvc项目时, 执行添加"包含视图的MVC5控制器(使用Entity Framework)时 点击添加,出现错误提示 解决方法: 在您的解决方案资源管理器中,右键 ...
- GPT3的技术突破:实现更精准的语义分析
目录 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 5. 优化与改进 6. 结论与展望 7. 附录:常见问题与解答 GPT-3技术突破:实现更精准的语义分析 近年来,人工智 ...
- 【promptulate专栏】ChatGPT框架——两行代码构建一个强大的论文总结助手
本文节选自笔者博客:https://www.blog.zeeland.cn/archives/019hasaa 前言 如果你经常阅读论文,那么你肯定会遇到以下几个问题: 论文晦涩难懂看不明白怎么办? ...