Docker是一种轻量级的容器化技术,可以将应用和其依赖项封装成一个可移植的容器,这使得在不同的环境中部署和运行应用程序变得更加容易。Docker容器技术的核心思想是隔离和打包装箱,每个容器是互相隔离的。
Docker诞生于2010年,最初是由一家名为dotCloud的公司开发的,该公司提供了一些pass的云计算服务。Docker最初并没有受到行业的关注,直到2013年它成为了开源项目,越来越多的人开始认识到它的优点,Docker也因此变得越来越流行。

与传统的虚拟机技术相比,Docker的优点在于轻量级、资源占用少、启动快速等方面。Docker容器化技术也是一种虚拟化技术,但与虚拟机不同的是,容器内的应用直接运行在宿主机的内核上,没有自己的内核和虚拟硬件,每个容器是互相隔离的,因此可以更快速地部署和运行应用程序。

Docker的作用在于实现DevOps,即开发和运维的快速协作,将应用程序的构建、测试、部署等过程自动化,提高效率和质量,降低成本。Docker提供了一整套的工具和服务,包括Docker引擎、Docker Hub、Docker Compose等,可以方便地管理和维护容器化应用程序。
Docker是基于Golang开发的开源项目

Docker是一种轻量级容器技术,它的核心组成部分有镜像、容器和仓库。本文将在下面介绍这三个组成部分的概念和作用。
镜像:镜像就像是一个模板,可以通过它来创建容器服务。例如,使用tomcat镜像可以创建一个tomcat01容器,容器中可以运行最终服务或项目。
容器:容器则是通过镜像来创建的,利用容器技术独立运行一个或多个应用程序。启动、停止、删除等基本命令可以操作容器。可以把容器理解为一个简易的Linux系统。
仓库:仓库是存放镜像的地方,分为公有仓库和私有仓库。DockerHub和阿里云都有容器服务器,可以配置镜像加速。
通过使用Docker,可以方便地创建、部署和管理应用程序,加速开发和部署流程,提高开发效率和应用程序的可移植性。
本文,笔者使用的是Unbuntu18.04版本的虚拟机,使用XShell连接工具对虚拟机进行操作,配置完成Linux环境后确保Xshell可以正常访问Ubuntu文件。

首先需要先卸载掉Docker的旧版本文件数据。
sudo apt-get remove docker docker-engine docker.io containerd runc通过仓库的方式安装。
sudo apt-get update sudo apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release下面添加Docker的官方GPG key。
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg # 国内阿里云版 curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -接下来验证key的指纹
sudo apt-key fingerprint 0EBFCD88正常的输出为:
root@camile:/# sudo apt-key fingerprint 0EBFCD88 pub rsa4096 2017-02-22 [SCEA] 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 uid [ 未知 ] Docker Release (CE deb) <docker@docker.com> sub rsa4096 2017-02-22 [S]下面我们添加稳定版的docker仓库或者国内阿里云版本。
# 官方版本 sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" # 国内阿里云版 sudo add-apt-repository \ "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \ $(lsb_release -cs) \ stable"安装我们最新版本的docker ce和container。
sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io查看我们安装的docker版本信息。
docker --version将非root用户加入docker组,以允许免
sudo执行docker.sudo gpasswd -a 用户名 docker重启服务并刷新docker组成员:
sudo service docker restart newgrp - docker设置开机自启动并启动 Docker-ce(安装成功后默认已设置并启动,可忽略)
sudo systemctl enable docker sudo systemctl start docker接下来我们安装docker-compose https://www.runoob.com/docker/docker-compose.html
sudo curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose docker-compose --version测试我们的第一个镜像hello-world。
docker run hello-world

卸载docker命令如下所示:
sudo apt-get purge docker-ce docker-ce-cli containerd.io sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd
在阿里云上,提供了一些免费的容器镜像服务,我们找到控制台->左侧菜单->容器镜像服务->镜像加速器,其中Ubuntu配置镜像加速器的命令已给出:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://alvnh3pv.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
上面我们运行了一个docker run的命令,笔者绘制了一个流程图供大家参考

那么Docker究竟是怎么工作的呢?这里笔者简单的介绍一下,Dcoker是一个 Client-Server结构的系统,Docker的守护进程运行在主机上,通过Socker从客户端访问。而DockerServer 接收到 Docker-Client 的指令,就会执行这个命令。如下所示

这一节,笔者总结了一些Docker的常用命令,速览以下有助于后续的使用。对于一些命令,笔者会在虚拟机上试验并给出响应结果。
docker version # 版本信息
docker info # 显示docker的系统信息,包括容器和镜像的信息
docker [命令] --help # 帮助文档,官网在 doc下的reference 下的 command-line reference
docker images:查看本机所有的镜像
root@camile:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 5 months ago 13.3kB
# 解释
REPOSITORY:镜像的仓库源
TAG:镜像的标签
IMAGE ID:镜像的ID
CREATED:镜像的创建时间
SIZE:镜像的大小
# 可选项
Options:
-a, --all # 列出所有镜像
-f, --filter # 过滤镜像
-q, --quiet # 只显示镜像的id
-aq # 列出所有镜像id
**docker search:**搜索镜像
root@camile:~# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql/mysql-server Optimized MySQL Server Docker images. Create… 907 [OK]
# 可选项
Options:
-f=STARS=3000 --filter=STARS=3000
docker pull:下载镜像
# 下载镜像 docker pull 镜像名[:tag]
root@camile:~# docker pull mysql
Using default tag: latest # 默认是最新版本
latest: Pulling from library/mysql
72a69066d2fe: Pull complete # 分层下载,docker image 的核心 联合文件系统
93619dbc5b36: Pull complete
99da31dd6142: Pull complete
626033c43d70: Pull complete
37d5d7efb64e: Pull complete
ac563158d721: Pull complete
d2ba16033dad: Pull complete
688ba7d5c01a: Pull complete
00e060b6d11d: Pull complete
1c04857f594f: Pull complete
4d7cfa90e6ea: Pull complete
e0431212d27d: Pull complete
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709 #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
# 等价于以下命令
docker pull docker.io/library/mysql:latest
# 指定版本下载
root@camile:~# docker pull mysql:5.7
5.7: Pulling from library/mysql
72a69066d2fe: Already exists
93619dbc5b36: Already exists
99da31dd6142: Already exists
626033c43d70: Already exists
37d5d7efb64e: Already exists
ac563158d721: Already exists
d2ba16033dad: Already exists
0ceb82207cd7: Pull complete
37f2405cae96: Pull complete
e2482e017e53: Pull complete
70deed891d42: Pull complete
Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

docker rmi:删除镜像
# 按照id删除镜像
docker rmi -f 容器id # 删除指定的镜像
docker rmi -f 容器id 容器id 容器id # 删除多个镜像
docker rmi -f $(docker images -aq) # 删除全部镜像
有了镜像,我们才可以创建容器,我们下载一个centos镜像来测试学习。
新建容器并启动
docker run [可选参数] image
# 参数说明
--name=“Name” 容器名字 tomcat01 tomcat02 用来区分容器
-d 后台方式运行
-i -t -it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-p 随机指定端口
# 测试
root@camile:~# docker run -it centos /bin/bash # 启动容器并进入容器内部
[root@f2e7722031c0 /] # 进入容器内部 root@后面就是容器的id
[root@f2e7722031c0 /]# ls # 查看容器内的centos目录
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@f2e7722031c0 /]# exit # 从容器中退回主机
exit
root@camile:~#
列出所有运行中的容器
# docker ps 命令
# 列出当前正在运行的容器
-a # 列出当前正在进行的容器+带出历史运行的容器
-n=? # 显示最近几个创建的容器
-q # 只显示容器的ID
root@camile:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f2e7722031c0 centos "/bin/bash" 4 hours ago Exited (0) 4 hours ago amazing_rosalind
退出容器
exit # 直接容器停止并退出
CTRL + P + Q # 容器不停止并tui'chu
删除容器
docker rm 容器id # 删除指定的容器,不能删除正在运行的容器
docker rm -f $(docker ps -aq) # 删除所有的容器
docker ps -aq | xargs docker rm # 删除所有的容器
启动和停止容器的操作
docker start 容器 id
docker restart 容器id
docker stop 容器id
docker kill 容器id
后台启动容器
# 命令 docker run -d 镜像名;
root@camile:~# docker run -d centos
# 问题 docker ps 发现,centos 停止了
# 常见的坑, docker 容器使用后台运行,就必须有一个前台进程,docker容器发现没有应用,就会自动停止
# 容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
查看日志
docker logs -f -t --tail 容器,没有日志
# 自己编写一段shell脚本
docker run -d centos /bin/bash -c "while true;do echo qiaowei;sleep 1;done"
# 显示日志
root@camile:~# docker logs -tf --tail 10 397eabe2c023
2022-02-26T09:21:25.266776907Z qiaowei
2022-02-26T09:21:26.268727710Z qiaowei
2022-02-26T09:21:27.270738576Z qiaowei
-tf # 显示日志
--tail # 显示日志条数
查看容器中的进程信息
# 命令docker top 容器id
root@camile:~# docker top 19334600a733
UID PID PPID C STIME TTY TIME CMD
root 14960 14931 0 17:27 pts/0 00:00:00 /bin/bash
查看镜像的元数据
# 命令
docker inspect 容器id
进入当前正在运行的容器
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
docker exec -it 容器id 路径
# 测试
root@camile:~# docker exec -it 19334600a733 /bin/bash
[root@19334600a733 /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@19334600a733 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 09:27 pts/0 00:00:00 /bin/bash
root 15 0 0 09:46 pts/1 00:00:00 /bin/bash
root 31 15 0 09:46 pts/1 00:00:00 ps -ef
# 方式二
docker attach 容器id
root@camile:~# docker attach 19334600a733
[root@19334600a733 /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
# docker exec # 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker attach # 进入容器正在执行的终端,不会启动新的进程
从容器内拷贝文件到主机上
docker cp 容器id:容器内路径 目前的主机路径
# 测试
[root@19334600a733 /]# cd /home
[root@19334600a733 home]# ls
[root@19334600a733 home]# touch test.java
[root@19334600a733 home]# exit
exit
root@camile:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@camile:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19334600a733 centos "/bin/bash" 32 minutes ago Exited (0) 12 seconds ago admiring_bohr
397eabe2c023 centos "/bin/bash -c 'while…" 39 minutes ago Exited (137) 37 minutes ago focused_ellis
cd940d7abbb2 centos "/bin/bash -C 'while…" 40 minutes ago Exited (127) 40 minutes ago ecstatic_galileo
23db6f7950f8 centos "/bin/bash -C 'while…" 40 minutes ago Exited (127) 40 minutes ago keen_sanderson
8f9ce6c66f30 centos "/bin/bash" 43 minutes ago Exited (0) 40 minutes ago flamboyant_moser
root@camile:~# docker cp 19334600a733:/home/test.java /home
root@camile:~# cd home
-bash: cd: home: No such file or directory
root@camile:~# cd /home
root@camile:/home# ls
camile test.java
# 拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现
接下来,笔者将带领大家安装Nginx和Tomcat这两个常见的镜像。
# 1.搜索镜像 search 建议大家去docker搜索,可以看到帮助文档
# 2.下载nginx镜像
docker pull nginx
# 3.运行测试
root@camile:/# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 8 weeks ago 141MB
centos latest 5d0da3dc9764 5 months ago 231MB
# -d 后台运行
# --name 给容器命名
# -p 宿主机端口:容器内端口
root@camile:/# docker run -d --name nginx01 -p 3344:80 nginx
586cda0f86a49f103522efcf0300a8592639ae2da23c8317745a487d8373a555
root@camile:/# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
586cda0f86a4 nginx "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 0.0.0.0:3344->80/tcp, :::3344->80/tcp nginx01
root@camile:/# curl localhost:3344
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 官方的使用
docker run -it --rm tomcat:9.0
# 我们之前的启动都是后台,停止了容器之后,容器还是可以查到,官方的方法一般是用完及删
# 我们的逻辑是先下载再启动
docker pull tomcat:9.0
# 启动运行
root@camile:/# docker run -d -p 3355:8080 --name tomcat01 tomcat
# 发现问腿:1.Linux命令少了 2.没有webapps,阿里云镜像的原因,默认是最小的镜像,所有不必要的都删除吊
# 保证最小的运行环境
# es 暴露的端口很多!
# es 十分的耗内存
# es 的数据一般需要放置在安全目录!挂载
# --net somenetwork ? 网络配置
# 启动 elasticsearch
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2
# 启动了 linux就卡住了 docker stats 查看 cpu状态
# es 是十分耗内存的 1核2G
# 查看 docker stats
# 测试以下 es 是否成功了
# 赶紧关闭,增加内存的限制 -e环境配置修改
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -e ES_JAVA_OPTS="-Xms64m -Xmx12m" elasticsearch:7.6.2
这里我们可以使用一个portainer,它是一个Docker的图形化界面管理工具,提供一个后台面板供我们操作,登陆进入可以管理我们的容器。
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portaioner
在虚拟机中可以使用:localhost:8080来访问界面

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
如何获得镜像:
从远程仓库下载
朋友拷贝
自己制作一个镜像DockerFile
UnionFS(联合文件系统)
我们下载的时候看到的一层一层的数据就是这个。
UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层地叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是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等标准目录和文件。各种不同的操作系统发行版,比如Ubuntu , Centos等等

平时我们安装虚拟机的CentOS都是好几个G,为什么Docker这里才200M?
对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。因此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
分层的镜像
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层地在下载。

思考:为什么Docker镜像要采用这种分层结构?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect命令!
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker image inspect nginx:latest
[
{
"Id": "sha256:ae2feff98a0cc5095d97c6c283dcd33090770c76d63877caa99aefbbe4343bdd",
"RepoTags": [
"nginx:latest"
],
"RepoDigests": [
"nginx@sha256:4cf620a5c81390ee209398ecc18e5fb9dd0f5155cd82adcbae532fec94006fb9"
],
"Parent": "",
"Comment": "",
"Created": "2020-12-15T20:21:00.007674532Z",
"Container": "4cc5da85f27ca0d200407f0593422676a3bab482227daee044d797d1798c96c9",
"ContainerConfig": {
"Hostname": "4cc5da85f27c",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.19.6",
"NJS_VERSION=0.5.0",
"PKG_RELEASE=1~buster"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"nginx\" \"-g\" \"daemon off;\"]"
],
"Image": "sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"StopSignal": "SIGQUIT"
},
"DockerVersion": "19.03.12",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.19.6",
"NJS_VERSION=0.5.0",
"PKG_RELEASE=1~buster"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"StopSignal": "SIGQUIT"
},
"Architecture": "amd64",
"Os": "linux",
"Size": 132935043,
"VirtualSize": 132935043,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/cb791e78a08db7091bf2ce1d78603f1758f52199e57f1805156fe30e39067aae/diff:/var/lib/docker/overlay2/1e73a72b25af68ee9abf4eb443f778d31226e12e9af428fcc14c7b044c83b258/diff:/var/lib/docker/overlay2/88c9c01762f2af8327db65d0b0d4a64785e87c9c2ab76c62e7d03619db03a985/diff:/var/lib/docker/overlay2/7304ab112ac4a9cb91fc6f74730be28fecbe19f042e92d321aa9181424cc4b2e/diff",
"MergedDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/merged",
"UpperDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/diff",
"WorkDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9",
"sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa",
"sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d",
"sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2",
"sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
这里指示了分层信息:
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9",
"sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa",
"sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d",
"sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2",
"sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4"
]
},
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

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

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

特点
Docker镜像都是只读地,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都是镜像层!
当我们想要保存当前容器的状态,就可以通过commit来提交,获得一个镜像:
docker commit 提交容器成为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[tag]
实战测试
# 启动一个默认的tomcat
# 发现这个默认的 tomcat 没有webapps应用,镜像的原因,官方的镜像默认 webapps 下是没有文件的
# 自己拷贝进去基本的文件
# 将我们操作过的容器通过commit提交为一个镜像!我们以后就可以使用我们修改过的镜像即可,这也就是我们自己的一个修改镜像。
容器数据卷是Docker中一种用于持久化数据的技术,它可以让容器中的数据在容器删除后不会丢失。Docker的理念是将应用和环境打包成一个镜像,但如果数据都在容器中,那么容器删除后数据也会消失。因此,需要使用数据卷技术实现数据持久化和同步操作。

例如,在使用MySQL时,如果容器删除,则数据库中的数据也会丢失,因此需要将MySQL数据存储在本地并使用卷技术实现容器和本地数据的同步。卷技术通过目录挂载实现,将容器内的目录挂载到Linux系统上。因此,容器间也可以共享数据,实现数据共享。容器数据卷是Docker中非常重要的一部分,可以实现容器的持久化和同步操作,为容器应用提供了强大的数据管理功能。
方式一:直接使用命令来挂载 -v
docker run -it -v 主机目录:容器目录
# 测试
docker run -it -v /home/ceshi:/home centos /bin/bash
# 容器起来时候我们可以通过 docker inspect 容器id 来查看挂载状态

在容器内简历一个test.java测试文件。测试文件的同步,是一个双向绑定的过程。

# 获取镜像
camile@camile:/$ docker pull mysql:5.7
# 运行容器,需要做数据卷挂载 # 安装启动mysql -e表示配置环境
# 官方测试:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动我们的mysql
-d 后台运行
-p 端口映射
-v 卷挂载
-n 容器名字
camile@camile:~$ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=zuiweizhiming666 --name mysql01 mysql:5.7
# 启动成功之后,我们在本地使用 navicat 来测试以下
# navicat 连接到服务器的3310 -3310和容器内的3306映射,这个时候我们就可以连接上了
# 在本地测试创建一个数据库,查看一下我们创建的路径是否ok


Docker中的匿名挂载和具名挂载是两种不同的数据卷挂载方式。
匿名挂载是指在启动容器时,只指定容器内的路径,而不指定宿主机的目录。这样会生成一个随机的宿主机挂载目录,可以通过 docker volume ls 命令查看。
具名挂载是指在启动容器时,既指定容器内的路径,又指定一个卷名作为宿主机的目录。 这样可以方便地找到和管理数据卷,也可以通过 docker volume inspect 卷名 命令查看详细信息。
匿名挂载和具名挂载的语法如下:
-v 容器内路径# 匿名挂载-v 卷名:容器内路径# 具名挂载
下面是具名挂载和匿名挂载的相关命令代码块:
# 启动一个nginx容器,使用匿名挂载,将容器内的/etc/nginx目录挂载到宿主机上
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有的数据卷
docker volume ls
# 启动一个nginx容器,使用具名挂载,将容器内的/etc/nginx目录挂载到宿主机上,并命名为juming-nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
# 查看具体的数据卷信息
docker volume inspect juming-nginx
DockerFile 就是用来构建Docker镜像的构建文件、命令脚本,下面先体验一下!
通过这个脚本可以生成镜像,镜像是一层一层的,脚本是一个个的命令。
# 创建一个DockerFile文件,名字可以随机 ,建议 Dockerfile
# 文件中的内容
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "---end---"
CMD /bin/bash
# 这里的每个命令,就是镜像的一层
# 启动自己写的容器

这个目录就是我们生成镜像的时候自动挂载的数据卷目录。这个卷和外部一定有一个同步的目录。

查看一下卷挂载的路径。
测试一下刚才的文件是否同步了。

这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!假设构建镜像的时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径。
dockerFile 是用来docker镜像的文件,是命令参数脚本。
构建步骤:编写一个dockerfile文件
docker build 构建成一个镜像
docker run 运行镜像
docker push 发布镜像
基础知识:
每个保留关键字都是大写字母
执行从上到下执行
#表示注释每一个指令都会创建提交一个新的镜像层,并提交!

dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单。
Docker镜像逐渐成为企业交付的标准,必须要掌握。
步骤:
DockerFile:构建文件,定义了一切的步骤,源代码
DockerImages:DockerFile构建生成的镜像,最终发布和运行的产品,原来是 jar war。
Docker容器:容器就是镜像运行起来提供服务的。
知晓了DockerFile命令,我们就可以自己制作镜像:
FROM # 基础镜像,一切从这里开始构建
MANITAINER # 镜像是谁写的 姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤,tomcat镜像,这个tomcat压缩包!添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 指定暴露端口
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当个构建一个被继承dockerfile 的时候,会运行这个指令,触发指令
COPY # 类似ADD,将我们文件拷贝到镜像中
ENV # 构建的时候设置环境变量
Docker Hub 中绝大多数镜像都是从这个基础镜像过来的 FROM scratch,然后配置需要的软件和配置来进行的构建。
创建一个自己的Centos
# 1.编写DockerFile的文件
FROM centos:7
MAINTAINER qiaowei<454921269@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
# 2.通过这个文件构建镜像
# 命令 docker build -f dockerfile文件路径 -t 镜像名:[tag]
Successfully built 11a9535c2896
Successfully tagged mycentos:0.1
# 3.测试运行
root@camile:/home/dockerfile# docker run -it 11a9535c2896
[root@f9c5dbcee829 local]# pwd
/usr/local
[root@f9c5dbcee829 local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.6 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:06 txqueuelen 0 (Ethernet)
RX packets 23 bytes 2804 (2.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@f9c5dbcee829 local]# vim test
自己写的centos 添加了vim 和 net-tools ,测试成功!
我们可以列出本地镜像的变更历史
root@camile:/home/dockerfile# docker history 11a9535c2896
IMAGE CREATED CREATED BY SIZE COMMENT
11a9535c2896 4 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
5005cf751c81 4 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
21407997234d 4 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
a567cd94200e 4 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
48ad5b128e9a 4 minutes ago /bin/sh -c yum -y install net-tools 161MB
da7d8a18d88e 4 minutes ago /bin/sh -c yum -y install vim 216MB
b1524097a173 4 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
01282534605d 4 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
9bb0de5c2898 4 minutes ago /bin/sh -c #(nop) MAINTAINER qiaowei<454921… 0B
eeb6ee3f44bd 5 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
准备镜像文件 tomcat 压缩包,jdk的压缩包。

编写dockerfile文件,官方命名“Dockerfile”,build会自动寻找这个文件,就不需要 -f 指定了。
root@camile:/home/qiaowei/build/tomcat# touch readme.txt root@camile:/home/qiaowei/build/tomcat# vim Dockerfile# 自己制作Dockerfile FROM centos:7 MAINTAINER qiaowei<454921269@qq.com> COPY readme.txt /usr/local/readme.txt ADD jdk-17_linux-x64_bin.tar.gz /usr/local/ ADD apache-tomcat-9.0.59.tar.gz /usr/local/ RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH ENV JAVA_HOME /usr/local/jdk-17.0.2 ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.59 ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.59 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-9.0.59/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.59/bin.logs/catalina.out构建镜像
# docker build -t diytomcat .

1.
运行容器:
docker run -d -p 9090:8080 --name qiaoweitomcat -v /home/qiaowei/build/tomcat/test:/usr/local/apache-tomcat-9.0.59/webapps/test -v /home/qiaowei/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.59/logs diytomcat访问测试

发布项目(由于做了卷挂载,我们直接在本地编写项目就可以)
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> </web-app>随便写一个html文件,测试访问
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>这是个标题</title> </head> <body> <h1>这是一个一个简单的HTML</h1> <p>Hello World!</p> </body> </html>

Dockerhub
确定这个账号可以登录
在我们服务器上提交自己的镜像
Usage: docker login [OPTIONS] [SERVER] Log in to a Docker registry. If no server is specified, the default is defined by the daemon. Options: -p, --password string Password --password-stdin Take the password from stdin -u, --username string Username登录完毕后可以提交镜像了
# 先给镜像打一个标签 docker tag 1ba62e6aa598 wulegekong/tomcat:1.0 # 提交镜像 docker push wulegekong/tomcat:1.0 # 注意:镜像名前一定是自己dockerhub的用户名,否则会拒绝push请求
登录阿里云
找到容器镜像服务
创建命名空间
根据官方提供的操作指南操作即可。
我们使用 ip addr 命令查看本机的所有网络信息:

这里出现了docker0地址,下面简单理解一下docker网络的原理:
我们每安装一个docker容器,docker就会给docker容器分配一个ip,只要电脑安装了docker,就会有一个网卡docker0桥接模式,使用的是evth-pair技术。
再次测试ip addr
再启动一个容器测试,发现又多了一对网卡
# 我们发现这个容器带来网卡,都是一对一对的
# evth-pair 就是一对的虚拟设备接口,它们是成对出现的,一端连着协议,一段彼此相连。
# 正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备
# openstac,Docker容器之间的连接 ovs的连接都是使用evth-pair技术
我们来测试tomcat01和tomcat02这两个之前创建的容器是否可以相互ping通,能够ping通!

结论:tomcat01和tomcat02是公用的一个路由器,docker0.
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用IP。
小结
Docker使用的是Linux的桥接,宿主机中是一个Docker容器的网桥 docker0。

Docker中的所有的网络接口都是虚拟的,虚拟的转发效率高,只要容器删除,对应网桥一对就没了。
笔者仅仅简单地学习和应用了Docker的一些基本内容,勉强算是入门,后续会学习Docker-Compose等进阶内容,敬请期待。

