docker-入门
# 是什么
Docker是一个容器技术,对进程进行封装隔离,进一步提供了文件系统,网络的隔离,屏蔽底层运行环境的差异,做到了完全一致。而且,比虚拟机的性能高出数倍。
和传统虚拟技术的差异
传统的虚拟技术会虚拟硬件,虚拟操作系统,然后在该系统上运行进程。而Docker直接使用Linux提供的各种隔离技术,直接运行于主机上。
架构
Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。 也就是说,看起来是本地执行命令,其实原创也是支持的。
# 怎么用
一致的运行环境
由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性。 环境一致对开发也非常重要,如果是因为环境因素导致的问题,那么本地和上线的代码就不一样,而且解决起问题也会十分的麻烦。
一次编写,到处运行
使用DockerFile,可以做到一次配置,可以快速的任何环境下完成部署,我们也常常感受到自己配置环境是非常麻烦的,而且在不同的机器上,由于之前环境的干扰,会出现很多意想不到的问题。
部署服务
刚接触Linux,我们部署MySql,可能是下载安装配置等等,每个应用的流程都不一样,非常繁琐。 之后我们知道了apt这样的管理工具可以让我们的部署大幅度简化。 但是,如果之后需要部署什么东西,我们可以优先在网络上查询docker安装xxx,以此来提高速度,节约我们在环境上的时间。
# 概念
任何技术的学习都要明确他的概念,他基于了设计者的设计理念,方便我们和其他人交流复杂的概念。
# 镜像(image)
可以理解为一个独立系统打包后的所有内容,不包含任何的动态数据,本质是一个文件。
# 分层存储
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。 甚至在删除一层文件的时候,都仅仅只是标记删除,会对镜像的体积照成影响,额外的东西要在本层构建结束之前删掉。 这样的分层设计,支持镜像的复用,我们也可以在已经有的镜像之上构建新的镜像。
# 容器
如果说镜像是文件,那么容器就是运行时。容器可以被创建、启动、停止、删除、暂停等。本质是一个进程。 但是这个进程有自己独立的命名空间,所以有自己的文件系统,网络等等隔离特性。 容器运行的本质就是,以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。所以要求我们必须在数据卷读写,其实本质就是文件读写的重定向。
# Registry
镜像是本地文件,docker的本质就为了让不同的环境运行,势必要方便拿到镜像。仓库是类型github的网址,里面有多个tag,一般代表不同的版本。默认tag是最新的版本。
# 使用
# 基本命令
docker pull
docker pull ubuntu
从Registry拉取镜像。 下载的时候,本质是在下载一个文件组,通过分层下载的方式,之前下载的过的文件会直接复用。 同理,push可以把本地构建的镜像推送到云端。
docker run
docker run -itd ubuntu bash
运行镜像->容器。 可以加-it参数,一个是 -i:交互式操作,一个是 -t 终端。 -d:后台执行。 通过exit退出。 进入正在运行容器是exec,其他的完全一样
容器命令
docker ps -a 查看容器 docker start xxx docker stop xxx docker restart xxx docker rm xxx
docker logs -f
可以和tail -f一样,查看日志。
docker image ls
列出已经下载的镜像,后面可以加关键词搜索我们想看的 这里有一个关键概念,ID,在我们需要操作镜像的时候,需要输入ID前3位,知道可以区分出他们就可以。 可以使用docker image prune来清理镜像空间
docker image rm
remove删除镜像 值得注意的是,docke保护我们不会删除有运行容器的镜像,所以必须先删除容器,再删除镜像。
# 数据卷(volume)
数据卷就是挂载在我们磁盘空间的,不受容器影响的文件系统。我的理解是,可以让容器操作我们的目录,这个和容器无关的目录,数据卷提供了这种映射关系。 可以通过-v 源目录:容器目录 我们可以发现,docker都是源目录在前,容器目录在后的,此外,源目录必须是绝对路径。
# 网络
docker数据比较简单,但是网络确实是有难度的,之前我们说了网络会被隔离,那么我们如何访问docker内的服务呢。 docker提供了端口映射的功能,使用-p来指定,如果我们访问了本地的某个端口,就可以访问到容器内的某个端口。 可是docker容器内的网络如何访问呢,这里docker给我们提供了一个机制,可以把容器加入一个互通的网络里面,docker network create -d bridge my-net,可以都使用这一个网络。 运行的时候,使用 --network my-net 就可以接入这个网络了。 但是如果你有多个容器之间需要互相连接,推荐使用 Docker Compose。
# DockerFile
docker build -t xxxx:xxx .
docker build工作原理
我们执行的命令本质是一个客户端,真正的打包是在docker服务端完成的,当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。如何才能让服务端获得本地文件呢? 这就是上下文的概念,会把这个路径下的所有文件打包,放进上传给docker服务端。 所以:COPY 这类指令中的源文件的路径都是相对路径。为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。 同理,可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
# FROM
基于哪个镜像开始构建,操作系统都是轻量级的,比如apt,我们必须先执行apt update,然后才能进行安装,不然会报错
# RUN
运行命令,这里有个小细节,因为每次RUN都会构建新的层,所以我们可以使用Linux的换行命令实现
RUN set -x; buildDeps='mysql' \
&& apt update \
&& apt install -y $buildDeps \
&& apt purge -y --auto-remove $buildDeps
2
3
4
# COPY
COPY 源路径 目标路径
源路径
基于build时指定的路径,相当于把哪些文件复制到一个地方的相对路径。 如果源路径为文件夹,复制的时候不是直接复制该文件夹,而是将文件夹中的内容复制到目标路径。
目标路径
可以是系统的绝对路径 也可以相对于工作路径。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
# CMD
容器运行起来之后,执行的命令,比如运行什么什么服务,可以支持变量,因为这个本质是sh xxx 会解析环境变量。 如果我们在docker run 最后写了一些指令,那么这些指令就会直接代替cmd的内容。
# ENV
可以给其他的docker命令支持统一的环境变量,建议把一些需要修改的变量,或者统一的东西作为常量放在ENV里面,这些参数会被加到容器的环境变量中 格式为k1=v1 k2=v2
# AVG
参数,不会加入运行的环境变量中,支持默认值
# 例子
Spring Boot的DockerFile
FROM openjdk:17-jdk-alpine
MAINTAINER baeldung.com
COPY target/xxxx-1.0.0.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
2
3
4