博学谷学习记录 自我总结 用心分享 | Docker容器化

发布时间 2023-10-14 11:21:46作者: 刚刚一个

前言

容器技术、虚拟化技术已经成为一种被大家广泛认可的服务器资源共享方式,容器技术可以在按需构建操作系统实例的过程当中为系统管理员提供极大的灵活性。由于hypervisor虚拟化技术仍然存在一些性能和资源使用效率方面的问题,因此容器技术(Container)结合虚拟化技术的解决方案正在我们的业务使用中高频使用。

容器的集装箱概念是参照了航运中的集装箱概念,在航运中不关心你运输的是汽车还是棉花,它都通过集装箱将运输的流程规范成了标准化的操作,这里容器的使命也是一样,容器具备自包含的能力,将自身程序所依赖的程序全部包含在了容器中,通过Docker将底层环境打平,用户可以将一个容器镜像运行在任何操作系统的宿主机上,也就是Docker所说的“Build once, Run anywhere”。

容器它的优势有以下几点:

1. 不再依赖于独立的操作系统运行,相比较虚拟机它没有每个独立的操作系统,突破了宿主机的磁盘IO性能,减少了资源使用的浪费。

2. 容器时应用程序层的抽象,对于运维和开发来说,容器只关心中间件之上的应用,中间件与宿主机的操作系统之间的问题都交给了Docker来处理。

3. 它是自包含程序依赖,这也是它成为云原生应用基石的重要因素,有兴趣的同学可以查询云原生应用12要素进行学习。

由于容器的优势使得我们在底层资源使用上资源使用率近一步提高,通过过往资源使用率的统计,原有云主机部署服务底层资源使用率只有10~20%,在容器技术加入以后,这一数值也提高到了40~50%

Docker的核心概念

这里值得一提的是,Docker作为容器化软件的一种,是社区使用率最高的容器引擎,但Docker并不等于容器,容器引擎有很多种,例如rkt和近期社区火热的kata。

Docker的核心概念有以下3点

1. 镜像(image)

2. 容器(Container)

3. 仓库(Registry)

 

Docker的整个运行逻辑如这个图所示,通过Docker Client将需要执行的Docker命令发送给Docker运行的节点上的Docker daemon,Docker daemon将我们的请求进行分解执行,例如我们执行Docker build命令它会根据Dockerfile构建一个镜像存放于本地,执行Docker pull命令会从远端的容器镜像仓库拉取镜像到本地,执行Docker run命令会将容器镜像拉取并运行成为容器实例。

这里我们从容器镜像开始讲解。

了解容器镜像先要了解linux系统的基础知识,典型的linux启动到运行需要2个FS,一个是rootfs,一个是bootfs。

bootfs是linux启动时进行加载的,会安装系统所使用的kernel内核,创建完成后bootfs会进行解绑。

rootfs是我们真正去使用linux是操作的系统文件系统,包含/dev /bin等目录,对于我们构建容器镜像来说,我们利用rootfs作为我们的基础镜像来使用,一个精简的os来说,它的rootfs可以很小,只需要包括基本的命令和工具即可,例如alpine只有不到10MB。

镜像构建基于一个基础镜像将我们需要进行安装的依赖和程序根据Dockerfile编写逐层叠加到镜像中,这里涉及到了一个容器镜像的概念就是,镜像分层,镜像的每一层都会有一个独立的id,Docker使用Union FS对Docker镜像进行分层和合层记录,当我们使用同一个基础镜像时,利用Docker镜像分层的功能可以帮助我们使用到已有的镜像层,拉取没有的镜像层,达到镜像的资源共享,减少重复镜像层拉取,达到资源使用最大化。

容器运行时会在容器镜像最上层生成一个Container层,这个层是copy这个完整的镜像加载到内存中运行,在这个层级中对于容器来说是可以进行修改的,但是由于运行于内存中,我们对Container层的任何修改都不会对底层镜像生效,当容器消亡时我们修改的数据也一并消亡,这就是容器的copy on write特性。

 

Dockerfile的编写

前面也提到了我们构建一个容器镜像,Docker会利用Dockerfile进行容器镜像的构建,那Dockerfile该如何编写,Docker又如何使用Dockerfile呢?我们接下来看个例子。

```
FROM uhub.service.ucloud.cn/hello/maven:3-jdk-8-alpine
WORKDIR /usr/src/app
COPY target /usr/src/app
COPY lib/jmx_prometheus_javaagent-0.12.0.jar /usr/src/app
COPY yaml/javaagent.yaml /usr/src/app
 
ENV PORT 8080
EXPOSE $PORT
ENV JMXPORT 9090
EXPOSE $JMXPORT
CMD [ "sh", "-c", "java -javaagent:jmx_prometheus_javaagent-0.12.0.jar=${JMXPORT}:javaagent.yaml -jar /usr/src/app/demo-0.0.1-SNAPSHOT.jar -Dserver.port=${PORT} --spring.config.location=/etc/appconfig/hello.properties" ]
```

在Dockerfile中我们常用的命令有以下几个

· FROM 我们基于什么镜像构建

· WORKDIR 容器的工作目录

· RUN 执行容器构建过程中的命令

· CMD & ENTRYPOINT 执行容器启动后的命令

· ADD & COPY 添加指定的文件到容器镜像中

· ENV 设置环境变量

· EXPOSE 设置容器暴露的端口

 

当我们有了Dockerfile我们需要构建容器镜像了,如下图所示,我们在Docker中可以通过Docker客户端的命令将容器服务形成一个闭环。

 

Docker build将Dockerfile构建成为镜像image

Docker tag将Docker image 打上标签,形成版本

Docker push和Docker pull 是将容器镜像推到镜像仓库或者从镜像仓库进行拉取

Docker run 是将容器镜像image运行成为Container

这里还涉及几个不常用的操作,例如stop停止容器,start启动容器,restart重启容器,commit从容器制作镜像,由于容器的特性我们一般不进行这几个操作。