Docker使用手册(2)

四、Docker镜像讲解

1、镜像是什么?

镜像是一种轻量级、可执行的独立软件包。用来打包软件运行环境和基于环境开发的软件。它包括运行某个软件所需的所有内容包括代码,运行时库、环境变量和配置文件所有的应用直接打包Docker镜像,就可以直接跑起来。

如何得到镜像?

  • 朋友给你
  • 自己提交上传
  • 镜像仓库下载

2、镜像的加载原理

UnionFS(联合文件系统)

联合文件系统是一种分层、轻量级并且高性能的文件系统。它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同的目录挂载到同一个虚拟文件系统下。联合文件系统是Docker镜像的基础,镜像可以通过分层来进行,继承基于基础镜像可以制作各种具体应用的镜像。

特性:一次同时加载多个文件系统,但从外面看起来只能看到一个文件系统,联合加载会把各种文件系统叠加起来,这样最终文件系统会包含所有底层的文件和目录。

Docker 镜像底层加载原理

镜像的分层叠加

docker的镜像实际上由一层一层的文件系统组成买这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader的kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后,整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等

平时我们安装虚拟机的CentOS都是好几个G,为什么docker这里才200M?

对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版,bootfs会有差别,因此不同发行版可以用bootfs

3、分层理解

分层的镜像

我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载

ubuntu@VM-0-13-ubuntu:/home$ sudo docker pull redis
Using default tag: latest
latest: Pulling from library/redis
6ec7b7d162b2: Already exists  # 已经存在的就不在下载了
1f81a70aa4c8: Pull complete 
968aa38ff012: Pull complete 
884c313d5b0b: Pull complete 
6e858785fea5: Pull complete 
78bcc34f027b: Pull complete 
Digest: sha256:0f724af268d0d3f5fb1d6b33fc22127ba5cbca2d58523b286ed3122db0dc5381
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

为什么Docker镜像要采用这种分层的结构?

最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享

ubuntu@VM-0-13-ubuntu:/home$ sudo docker image inspect redis
[
    {
        #.....

        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9",
                "sha256:25075874ce886bd3adb3b75298622e6297c3f893e169f18703019e4abc8f13f0",
                "sha256:caafc8119413c94f1e4b888128e2f337505fb57e217931a9b3a2cd7968340a9e",
                "sha256:e5d940a579ec4a80b6ec8571cb0fecf640dba14ccfd6de352977fd379a254053",
                "sha256:2a1c28c532d20c3b8af8634d72a4d276a67ce5acb6d186ac937c13bd6493c972",
                "sha256:1540b8226044ed5ce19cc0fec7fbfb36a00bb15f4e882d6affbd147a48249574"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

理解:

所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含了3个镜像,如下图所示(这知识一个用于演示的很简单的例子)

在添加 额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件

QQ截图20210108092645

上图中的镜像层个之前图中的略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个版本更新版本

QQ截图20210108093339

这种情况下,上层镜像中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层到镜像当中。Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为同意的文件系统。
Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker在Windows上仅支持Windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW。
下图展示了与系统显示相同三层镜像。所有镜像层堆叠并合并,对外提供统一的视图

QQ截图20210108093942

特点

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下都叫镜像层

4、commit 镜像

# 基于一个容器,来创建自己的一个镜像
docker commit -m="commit desc message" -a="image author name" <基于哪个容器id> 自定义镜像名称:[tags]

实战测试

# 拉取一个tomcat镜像
docker pull tomcat
# 发现这个tomcat默认是没有webapps的应用的(镜像的原因,官方默认webapps目录下是没有文件的)
[root@xiaoyequ ~] docker exec -it tomcat /bin/bash
oot@6ba1137fc95f:/usr/local/tomcat# ls
BUILDING.txt	 LICENSE  README.md	 RUNNING.txt  conf  logs	    temp     webapps.dist
CONTRIBUTING.md  NOTICE   RELEASE-NOTES  bin	      lib   native-jni-lib  webapps  work
root@6ba1137fc95f:/usr/local/tomcat# cp -r webapps.dist/* webapps    # 复制文件到 webapps
root@6ba1137fc95f:/usr/local/tomcat# cd webapps
root@6ba1137fc95f:/usr/local/tomcat/webapps# ls

# 我自己拷贝进去了基本的文件
cp webapps.dist ./webapps

# 将我们操作过的容器commit为一个镜像,我们之后就使用修改过的
docker commmit -m "add webapps app" -a="lishanbiao" 7a23843156 tomcat01:1.0

# 测试
docker run -it -p 3344:8080 tomcat02:1.0

理解

如果你想要保存当前容器的状态,就可以通过commit命令来提交,相当于VM的快照

五、容器数据卷

1、什么是容器数据卷

问题1: 如果数据在容器中,那么我们容器删除之后,数据就会丢失;
需求1: 数据可以持久化;

问题2: 例如MySQL数据库容器化,当容器删除了,删库跑路,数据也就丢失了;
需求2: MySQL数据可以存储在本地;

宿主机和容器之间有一个数据共享的技术!Docker容器中产生的数据,同步到本地宿主机。
这就是卷技术,目录的挂载,将我们容器内的目录,挂载到Linux宿主机上面。

b45f500c61b2ab6af6ccceb0152a6f7a

总结:容器的持久化和同步操作,容器间也是可以数据共享的。

2、使用数据卷

方式一:使用命令来挂载 -v

docker run -it -v 主机目录:容器目录

# 测试
docker run -it -v /home/ceshi:/home centos /bin/bash

# 启动后,我们通过 docker inspect 容器id 查看容器详细信息
docker inspect 824508c3232e

49216b573ebad98c3277419e9acb7e18

测试文件同步

f3e32b66621f44053b59d050660ab89a

6376448f46576e305cbd14c33a951bbd

总结:挂载后,主机与容器文件文件可以互相同步!

好处:我们以后修改文件信息,只需要在本地修改即可,文件内容会自动同步

3、实战:安装MySQL

docker pull mysql:5.7

docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/data -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

# 参数解析
-d:后台启动
-p:映射端口号
-v:挂载卷
--name:容器名字

我们创建完数据库后,假设我们把容器删除,我们的数据也不会有丢失,这就实现了数据的持久化功能!

4、具名挂载和匿名挂载

匿名挂载

# 匿名挂载 (-v 容器路径)
docker run -d -P --name nginx01 -v /etc/nginx nginx

# docker volumes ls

20200603153240468

这里会发现这种没有指定主机路径的名字,成为匿名挂载。

具名挂载

# 匿名挂载 (-v 容器路径), ps:-v 后面不指定“/”的代表卷名
docker run -d -P --name nginx01 -v juming-nginx:/etc/nginx nginx

# docker volumes ls
DRIVER                         VOLUME NAME
local                          juming-nginx

# 查看一下这个卷在哪个位置
docker volumes inspect juming-nginx

这便是默认的挂载卷目录:

var/lib/docker/volumes/juming-nginx/_data

有时候我们会看到以下命令:docker run -d -😛 --name=nginx02 -v juming-nginx:/etc/nginx:ro nginx

或 docker run -d -😛 --name=nginx02 -v juming-nginx:/etc/nginx:rw nginx,这里的ro和rw代表读写权限,ro表示只读,rw表示读和写,对挂载出来的内容就进行了限制,ro表示只能通过宿主机进行改变,容器内无法操作。

总结:

  • -v /宿主机路径:容器内路径 指定路径挂载
  • -v 数据卷名:容器内路径 具名挂载
  • -v 容器内路径 指定匿名挂载

5、数据卷容器

--volumes-from 

结论:容器之间配置信息是同步互相双向自动传递的,数据卷容器的生命周期一直持续到没有容器使用为止。

6、初识Dockerfile

Dockerfile就是用来构建docker镜像的构建文件!命令脚本

通过下面这个脚本可以生成镜像,镜像是一层一层的,每个命令都是一层!

# 创建一个dockerfile文件,名字随机,建议Dockerfile
# 文件中的内容指令(大写) 参数
FORM centos

VOLUME ["volume01","volume02"]

CMD echo "---end---"
CMD /bin/bash
# 这里的每个命令,就是镜像的一层

测试使用Dockerfile

# 用dockerfile来构建镜像 docker build -f filePath -t 自定义镜像名:[tags] .
docker build -f /home/lishanbiao/test/dockerfile1 -t lishanbiao/centos:1.0 .

ps:最后有一个“.”,不能忘

六、DockerFile

构建步骤:

1、 编写一个dockerfile文件

2、 docker build 构建称为一个镜像

3、 docker run运行镜像

4、 docker push发布镜像(DockerHub 、阿里云仓库)

点击后跳到一个Dockerfile

很多官方镜像都是基础包,很多功能没有,我们通常会自己搭建自己的镜像

1、DockerFile构建过程

DockerFile基础知识

  • 每个保留关键字(指令)都必须是大写字母。
  • 执行顺序:从上到下顺序执行。
  • # 表示注释
  • 每一个指令都会创建提交一个新的镜像层,并提交。

DockerFile指令说明

  • FROM 基础镜像,一切从这里开始构建
  • MAINTAINER 镜像是谁写的,姓名+邮箱
  • RUN 镜像构建的时候需要运行的命令
  • ADD
  • WORKDIR 镜像的工作目录
  • VOLUME 挂载的目录
  • EXPOSE 暴露端口配置
  • CMD 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
  • ENTRYPOINT 指定这个容器启动的时候要运行的命令,可以追加命令。
  • ONBUILD 当构建一个被继承DockerFile,这个时候就会运行ONBUILD的指令,触发指令。
  • COPY 类似ADD,将我们文件拷贝到镜像中。
  • ENV 构建的时候设置环境变量。

2、centos测试

创建一个自己的centos

目标:docker官方提供的原生centos镜像是一个压缩版本的,里面甚至没有vim相关命令、ifconfig等net命令。因此我们要通过DockerFile来构建出自己的镜像。

vim mydockerfile-cntos

# 以下是mydockerfile-cntos内容:
FROM centos # 已原生镜像为基础
MAINTAINER lishanbiao<1336503209@qq.com> # 作者信息
ENV MYPATH /usr/local # 配置环境变量
WORKDIR $MYPATH # 指定工作目录
RUN yum -y install vim # 安装vim
RUN yum -y install net-tools # 安装net-tools

EXPOSE 80 # 暴露80端口
CMD echo $MYPATH
CMD echo "------end------"
CMD /bin/bash

# 编辑内容完毕,shift + :+ wq 退出后,构建自己的镜像
docker build -f mydockerfile-centos -t mycentos:0.1 .

测试运行

docker run -it mycentos /bin/bash

我们可以列出本地进行的变更历史

CMD 和 ENTRYPOINT区别

CMD	       # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令

测试cmd**

# 编写dockerfile文件
$ vim dockerfile-test-cmd
FROM centos
CMD ["ls","-a"]
# 构建镜像
$ docker build  -f dockerfile-test-cmd -t cmd-test:0.1 .
# 运行镜像
$ docker run cmd-test:0.1
.
..
.dockerenv
bin
dev

# 想追加一个命令  -l 成为ls -al
$ docker run cmd-test:0.1 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\":
 executable file not found in $PATH": unknown.
ERRO[0000] error waiting for container: context canceled 
# cmd的情况下 -l 替换了CMD["ls","-l"]。 -l  不是命令所有报错

测试ENTRYPOINT

# 编写dockerfile文件
$ vim dockerfile-test-entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
$ docker run entrypoint-test:0.1
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found ...
# 我们的命令,是直接拼接在我们得ENTRYPOINT命令后面的
$ docker run entrypoint-test:0.1 -l
total 56
drwxr-xr-x   1 root root 4096 May 16 06:32 .
drwxr-xr-x   1 root root 4096 May 16 06:32 ..
-rwxr-xr-x   1 root root    0 May 16 06:32 .dockerenv
lrwxrwxrwx   1 root root    7 May 11  2019 bin -> usr/bin
drwxr-xr-x   5 root root  340 May 16 06:32 dev
drwxr-xr-x   1 root root 4096 May 16 06:32 etc
drwxr-xr-x   2 root root 4096 May 11  2019 home
lrwxrwxrwx   1 root root    7 May 11  2019 lib -> usr/lib
lrwxrwxrwx   1 root root    9 May 11  2019 lib64 -> usr/lib64 ....

Dockerfile中很多命令都十分的相似,我们需要了解它们的区别,我们最好的学习就是对比他们然后测试效果!

3、实战:Tomcat镜像

准备

tomcat 下载 
wget -P ./ https://mirrors.cnnic.cn/apache/tomcat/tomcat-8/v8.5.64/bin/apache-tomcat-8.5.64.tar.gz 
jdk 下载地址
wget -P ./ https://mirrors.cnnic.cn/AdoptOpenJDK/8/jdk/x64/linux/OpenJDK8U-jdk_x64_linux_hotspot_8u282b08.tar.gz
# 以上两个地址与我使用的版本不一样,勿抄 

root@aliyunleo tomcat8source]# ll
total 191952
-rw-r--r-- 1 root root  11027411 Oct 15 11:44 apache-tomcat-9.0.33.tar.gz
-rw-r--r-- 1 root root 185516505 Oct 15 11:44 jdk-8u141-linux-x64.tar.gz

# 我们可以在当前目录下创建一个文件readme.txt
# 编辑后,查看自己的Dockerfile
[root@aliyunleo tomcat8source]# cat Dockerfile 
FROM centos # 基础镜像
MAINTAINER leo<wei1986@126.com> # 作者信息

COPY readme.txt /usr/local/readme.txt # copy当前目录下的文件到容器

ADD jdk-8u141-linux-x64.tar.gz /usr/local/ # 加入文件,会自动解压
ADD apache-tomcat-9.0.33.tar.gz /usr/local/ # 加入文件,会自动解压

RUN yum -y install vim # 安装vim

ENV MYPATH /usr/local # 配置环境变量
WORKDIR $MYPATH # 指定工作目录

ENV JAVA_HOME /usr/local/jdk1.8.0_141 # 指定环境变量
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar # 指定环境变量

ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.33 # 指定环境变量
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.33 # 指定环境变量

ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin # 指定环境变量

EXPOSE 8080 # 暴露端口

CMD /usr/local/apache-tomcat-9.0.33/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.33/bin/logs/catalina.out # 执行命令(这里是启动tomcat,并查看日志)

执行build命令,构建镜像

# 这里不指定文件,回去当前目录下找到默认的DockerFile文件
docker build -t diytomcat:1.1 .

启动镜像

docker run -d -p 9090:8080 --name leotomcat -v /wwwroot/tomcat9/test:/usr/local/apache-tomcat-9.0.33/webapps/test -v  /wwwroot/tomcat9/logs:/usr/local/apache-tomcat-9.0.33/logs diytomcat:1.1

# 命令行解析
# 后台启动
-d 
# 主机9090映射docker的8080端口
-p 9090:8080          
# 容器名
--name leotomcat      
 # 本地路径  /wwwroot/tomcat9/test   挂载到容器的   /usr/local/apache-tomcat-9.0.33/webapps/test
-v /wwwroot/tomcat9/test:/usr/local/apache-tomcat-9.0.33/webapps/test   
# 镜像名
diytomcat             

# 看到已经有容器启动了
[root@aliyunleo ~] docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
12d332ce5431        diytomcat           "/bin/sh -c '/usr/lo…"   2 minutes ago       Up 2 minutes        0.0.0.0:9090->8080/tcp   leotomcat

进入镜像

docker exec -it 12d332ce5431 /bin/bash
[root@12d332ce5431 local]# 

验证

curl localhost:9090
47.105.***.247:9090

由于做了卷挂载,在本地发布项目就可以了,不用到容器发布了

# 本地文件目录
[root@aliyunleo wwwroot]# tree
.
└── tomcat9
    ├── logs
    │   ├── catalina.2021-03-11.log
    └── test
        ├── index.jsp
        └── WEB-INF
            └── web.xml
[root@aliyunleo wwwroot]# pwd
/wwwroot

编写web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>FirstWebFontEnd</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

编写index.jsp

<html>
    <head>
           <title>第一个 JSP 程序</title>
    </head>
    <body>
           <%
                  out.println("Hello World!");
           %>
    </body>
</html>

访问测试成功!

http://host:9090/test

4、发布自己的镜像

Docker Hub

  1. 地址:https://hub.docker.com/ 注册自己的账号
  2. 确定可以登陆
  3. 在我们的服务器上提交自己的镜像

2312675-20210711140549896-659425041

docker push 自己的账户名/diytomcat:1.0 # 带上版本号,不然有可能被拒绝

为镜像打标签:

docker tag 镜像id 镜像名:[tags]

5、发布镜像到阿里云服务

参考官方文档,无脑又详细!

小结

posted @   码出新生活!  阅读(173)  评论(0编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示