Ⅰ 引言
需要提前掌握的知识:
- Linux基本命令
- 计算机网络
- Springboot(非必须)
1.1 容器技术的起源
我们考虑这样的场景,公司正在研发一款APP,程序员一开始在自己的设备上搭建了一套环境开始写代码,写完代码后交给测试同学进行测试,这时测试同学也需要重新搭建这套环境,测试人员测完后可以上线,这时运维同学又需要重新搭建这套环境。
从整个过程中我们看到,整个项目的开发至上线过程中,我们搭建了三套重复的环境,并且每次搭建环境可能因为操作不当造成程序运行失败,浪费了时间与效率。于是容器技术由此产生。
1. 容器技术与虚拟机
上面的场景中,在容器技术面世前,我们可以使用虚拟机技术解决。其实当前云计算底层就是虚拟机技术,云计算厂商买回来一堆硬件搭建好数据中心后使用虚拟机技术就可以将硬件资源进行切分了,比如可以切分出100台虚拟机,这样就可以卖给很多用户了。
相较于单纯的应用程序,操作系统是一个占用磁盘大,消耗内存大并且启动速度慢的程序。
如上图所示,我们在一台内存为16GB的机器,开启三个虚拟机以部署三个应用(每个虚拟机部署一个应用),我们可以看到虚拟机本身就占据了总共7G内存,因此我们没有办法划分出更过虚拟机从而部署更多的应用程序,可是我们部署的是应用程序,要用的也是应用程序而不是操作系统。
还有另一个问题,那就是启动时间问题,我们知道操作系统重启是非常慢的,因为操作系统要从头到尾把该检测的都检测了该加载的都加载上,这个过程非常缓慢,动辄数分钟。
因此为了获得使用虚拟机的好处同时又克服这些缺点诞生了容器技术。
2. 容器
与虚拟机通过操作系统实现隔离不同,容器技术只隔离应用程序的运行时环境但容器之间可以共享同一个操作系统,这里的运行时环境指的是程序运行依赖的各种库以及配置。
从图中我们可以看到容器更加的轻量级且占用的资源更少,与操作系统动辄几G的内存占用相比,容器技术只需数M空间,因此我们可以在同样规格的硬件上大量部署容器,这是虚拟机所不能比拟的,而且不同于操作系统数分钟的启动时间容器几乎瞬时启动,容器技术为打包服务栈提供了一种更加高效的方式
1.2 Docker
docker是一个用Go语言实现的开源项目,可以让我们方便的创建和使用容器,docker将程序以及程序所有的依赖都打包到docker container,这样你的程序可以在任何环境都会有一致的表现,这里程序运行的依赖也就是容器就好比集装箱,容器所处的操作系统环境就好比货船或港口,程序的表现只和集装箱有关系(容器),和集装箱放在哪个货船或者哪个港口(操作系统)没有关系。
因此我们可以看到docker可以屏蔽环境差异,也就是说,只要你的程序打包到了docker中,那么无论运行在什么环境下程序的行为都是一致的,真正实现“build once, run everywhere”。
此外docker的另一个好处就是快速部署,这是当前互联网公司最常见的一个应用场景,一个原因在于容器启动速度非常快,另一个原因在于只要确保一个容器中的程序正确运行,那么你就能确信无论在生产环境部署多少都能正确运行。
1. Docker中的几个概念
- dockerfile
- image
- container
我们可以将dockerfile为源代码,image理解为可执行程序,container为运行起来的进程。因此我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image,之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。
2. Docker的工作流程
docker使用了常见的CS架构,也就是client-server模式,docker client负责处理用户输入的各种命令,比如docker build、docker run,真正工作的其实是server,也就是docker demon,值得注意的是,docker client和docker demon可以运行在同一台机器上。
Docker build
当我们写完dockerfile交给docker“编译”时使用这个命令,那么client在接收到请求后转发给docker daemon,接着docker daemon根据dockerfile创建出“可执行程序”image。
Docker run
有了“可执行程序”image后就可以运行程序了,接下来使用命令docker run,docker daemon接收到该命令后找到具体的image,然后加载到内存开始执行,image执行起来就是所谓的container。
Docker pull
docker中image的概念就类似于“可执行程序”,我们可以从Docker Hub(docker官方的“应用商店”)下载到别人写好的image,这样你就不用自己编写dockerfile了。
docker registry 可以用来存放各种image,公共的可以供任何人下载image的仓库就是docker Hub。那么该怎么从Docker Hub中下载image呢,就是这里的docker pull命令了。这个命令的实现也很简单,那就是用户通过docker client发送命令,docker daemon接收到命令后向docker registry发送image下载请求,下载后存放在本地,这样我们就可以使用image了。
Ⅱ Docker的安装
本文使用阿里云服务器,并配合Xshell远程连接使用。
- 1核2GB
- Ubuntu 20.04 64位
- 网络带宽1M
- 云盘大小40GB
2.1 前置工作
这里是新建一个用户docker来演示(顺便学一下Linux下的新建用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
若未为root用户设置密码,请通过命令 sudo passwd
一、创建用户
1. 创建用户:docker为用户名
-> sudo useradd -m docker -d /home/docker -s /bin/bash
2、为创建的用户设置密码
-> sudo passwd docker
3、修改用户的权限:( /etc/sudoers文件只有r权限,在改动前需要增加w权限,改动后,再去掉w权限 )
(1)为sudoers增加写入权限
sudo chmod +w /etc/sudoers
sudo vim /etc/sudoers
(2)为用户添加读写权限
# User privilege specification
root ALL=(ALL:ALL) ALL
docker ALL=(ALL:ALL) ALL // 这一行为新添加的代码
(3)将sudoers文件的操作权限改为只读模式
sudo chmod -w /etc/sudoers
二、删除用户
(1)删除用户
-> sudo userdel docker
-> sudo rm -rf /home/docker
(2)删除或者注释掉/etc/sudoers中关于要删除用户的配置,否则无法再次创建同名用户.
(3)彻底删除用户残余信息
-> cd /home # 删除/home目录下的文件
-> rm -rf docker
-> cat /etc/passwd # 删除/etc/passwd下的用户
-> cat /etc/group # 删除/etc/group下的用户组文件
-> cd /var/spool/mail # 删除/var/spool/mail下的邮箱文件
-> rm -rf docker
2.2 安装Docker
我们通过官方文档进行docker的安装。
(1)卸载旧版本的docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#1.常归删除操作
sudo apt-get autoremove docker docker-ce docker-engine docker.io containerd runc
#2. 删除docker其他没有没有卸载
dpkg -l | grep docker
# 删除无用的相关的配置文件
dpkg -l |grep ^rc|awk ‘{print $2}’ |sudo xargs dpkg -P
#3.卸载没有删除的docker相关插件(结合自己电脑的实际情况)
sudo apt-get autoremove docker-ce-*
#4.删除docker的相关配置&目录
sudo rm -rf /etc/systemd/system/docker.service.d
sudo rm -rf /var/lib/docker
#5.确认docker卸载完毕
docker --version
(2) 设置repository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#1.更新apt包管理源
sudo apt-get update
#2.安装相应依赖
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
#3.添加Docker的官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
#4.设置稳定repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
(3) 安装Docker Engine
1
2
3
4
5
1.再次更新apt
sudo apt-get update
2.安装最新版本的docker engine和containerd
sudo apt-get install docker-ce docker-ce-cli containerd.io
(4)测试
通过docker version
查看docker版本信息。
通过service docker status
查看docker启动状态。
通过sudo docker run hello-world
运行hello-world image验证dockers engine是否正确安装
最后通过docker images
查看镜像下载。
(5)配置阿里云容器镜像加速器
登录阿里云,在产品-弹性计算-容器服务-容器镜像服务-镜像工具-镜像加速器找到自己的配置步骤。
1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://kgvajhi3.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
2.3 Docker的原理
1. 镜像运行流程
我们在上一步通过运行hello-world镜像来验证了docker的安装,docker image的运行原理如下所示。
2. UnionFS
Docker镜像的关键即UnionFS(联合文件系统)。UnionFS是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。UnionFS也是Docker镜像的基础,镜像可以提供分层来进行继承,基于基础镜像,可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看来只有一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有的底层文件和目录。
3. Docker镜像
Docker镜像是一个只读的Docker容器模板,含有启动Docker容器所需的文件系统结构及其内容,因此是启动一个Docker容器的基础。如下图所示,Docker镜像采用分层构建机制,最底层为bootfs,其之上rootfs。
- bootfs:用于系统引导的文件系统,包括bootloader和kernel,容器启动完成后会被卸载以节约内存资源。
- rootfs:位于bootfs之上,rootfs是Docker容器在启动时内部进程可见的文件系统,即Docker容器的根目录。rootfs通常包含一个操作系统运行所需的文件系统,例如/dev、/proc、/bin、/etc、/lib、/usr、/tmp及运行Docker容器所需的配置文件、工具等。
在传统的Linux操作系统内核启动时,首先挂载一个只读(read-only)的rootfs,当系统检测其完整性之后,再将其切换为读写(read-write)模式。而在Docker架构中,当Docker daemon为Docker容器挂载rootfs时,沿用了Linux内核启动时的方法,即将rootfs设为只读模式。在挂载完毕之后,利用联合挂载(union mount)技术在已有的只读rootfs上再挂载一个读写层。这样,可读写层处于Docker容器文件系统的最顶层,其下可能联合挂载多个只读层。只有在Docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。
4. Docker镜像的主要特点
- 分层:Docker镜像时采用分层的方式构建的,每个镜像都由一系列的“镜像层”组成。分层结构是Docker镜像如此轻量的重要原因,当需要修改容器镜像内的某个文件时,只对处于最上方的读写层进行变动,不覆写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本文件所隐藏。当使用docker commit提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。分层达到了在不同镜像之间共享镜像层的效果。
- 写时复制:Docker镜像使用了写时复制(copy-on-write)策略,在多个容器之间共享镜像,每个容器在启动的时候并不需要单独复制一份镜像文件,而是将所有镜像层以只读的方式挂载到一个挂载点,再在上面覆盖一个可读写的容器层。在未更改文件内容时,所有容器都共享一份数据,只有在Docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。写时复制配合分层机制减少了镜像对磁盘空间的占用和容器启动时间。
- 内容寻址:在Docker 1.10版本后,Docker镜像改动较大,其中最重要的特性便是引入了内容寻址存储(content-addressable storge)的机制,根据文件内容来索引镜像的镜像层。与之前版本对每一个镜像层随机生成一个UUID不同,新模型对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值代替之前的UUID作为镜像层的唯一标志。该机制主要提高了镜像的安全性,并在pull、push、load、save操作后检测数据的完整性。另外,基于内容哈希来索引镜像层,在一定程度上减少了ID的冲突并且增强了镜像层的共享。对于来自不同构建的镜像层,只要拥有相同的内容哈希,也能被不同的镜像共享。
- 联合挂载:通俗的讲,联合挂载技术可以在一个挂载点同时挂载多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统将会包含整合之后的各层的文件和目录。实现这种联合挂载技术的文件系统通常被称为联合文件系统(Union FileSystem)。
5. 数据卷
一个运行的容器有一个或多个只读层和一个读写层。在容器运行过程中,若产生了一些重要的数据或是更改了一些文件,这些更改我们应该怎么保存呢?容器关闭或重启,这些数据不受影响;但删除Docker容器,则数据将会全部丢失。除此之外也还有其他的一些问题。比如存储于联合文件系统中,不易于宿主机访问;以及容器间数据共享不便。
为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供以下便利。
- volume能在不同的容器之间共享和重用。
- 对volume中数据的操作会马上生效。
- 对volume中数据的操作不会影响到镜像本身。
- volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的volume也不会被Docker删除。
数据卷容器:命名的容器挂载数据卷,其它容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器。即其他容器会对父容器中的数据卷进行双向拷贝,从而实现数据共享与同步。
6. Dockerfile
如果你想要从一个基础镜像开始建立一个自定义镜像,可以选择一步一步进行构建,也可以选择写一个配置文件,然后一条命令(docker build)完成构建,显然配置文件的方式可以更好地应对需求的变更,这个配置文件就是Dockerfile。
下图为ubuntu dockerfile,可见非常简短。
Dockerfile基本指令
1
2
3
4
5
6
7
8
9
10
11
12
FROM #基础镜像,一切从这里开始构建
MAINTAINER #镜像是谁写的,姓名+邮箱
RUN #镜像构建的时候需要运行的命令
ADD #步骤,tomcat镜像,这个tomcat的压缩包!添加内容
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOSE #暴露端口配置
CMD #指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被覆盖
ENTRYPOINT #指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD #当构建一个被继承 Dockerfile 这个时候就会运行ONBUILD 的指令
COPY #类似ADD,将我们文件拷贝到镜像中
ENV #构建的时候设置环境遍量
CMD与ENTRYPOINT的区别
- cmd给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行的命令。重点就是这个“默认”。意味着,如果不指定启动容器后要执行的命令,那么,就会使用cmd指定的默认的执行命令执行。如果指定了启动容器后要执行的命令,那么就不会再执行cmd中的命令。即cmd可被覆盖。
- entrypoint才是正统地用于定义容器启动以后的执行体的,其实我们从名字也可以理解,这个是容器的“入口”。我们在使用docker run启动镜像时,若在后面追加参数,则自动追加至entrypoint定义命令后面。
cmd命令格式
1
2
3
4
5
6
7
#推荐使用
CMD ["executable","param1","param2"]
#可作为entrypoint的默认参数
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
entrypoint命令格式
1
2
3
4
#推荐使用
ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)
7. Docker的工作流程
docker是一个C/S模式的架构,后端是一个松耦合架构,模块各司其职。
- 用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。
- Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;
- Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。
- Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;
- 当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;
- 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。
- libcontainer是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。
具体对docker架构以及工作流程的讲解可参考图解Docker架构和Docker架构及工作原理。
Ⅲ Docker的使用
3.1 Docker的基本命令
Docker官方提供了docker操作相关的所有命令的详细文档。
1. 帮助命令
1
2
3
docker version #查看docker版本信息
docker info #查看docker系统信息
docker --help #查看docker命令帮助信息
2. 镜像命令
docker images:查看本主机所存储的所有镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker@pyq:/home$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 2 months ago 13.3kB
#标识符
REPOSITORY 镜像的仓库源
TAG 镜像的标签/版本
IMAGE ID 镜像的id
CREATED 镜像的创建时间
SIZE 镜像的大小
#查看特定的镜像
docker images [OPTIONS] [REPOSITORY[:TAG]]
Options:
-a, --all 显示所有的镜像(默认)
--digests 显示数字签名
-f, --filter filter 根据特定信息进行过滤,支持的过滤条件见官方文档
--format string 使用Go模板打印出漂亮的图像
--no-trunc 不要截断输出
-q, --quiet 仅显示镜像id
docker search:搜索dockerhub中的镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker@pyq:/home$ docker search ubuntu
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 13233 [OK]
Options:
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print search using a Go template
--limit int Max number of search results (default 25)
--no-trunc Don't truncate output
#过滤出STARS大于等于10000的镜像
docker@pyq:/home$ docker search ubuntu -f=STARS=10000
#只显示5个搜索结果
docker@pyq:/home$ docker search ubuntu --limit 5
docker pull:下载镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#下载镜像 docker pull [OPTIONS] NAME[:TAG|@DIGEST]
docker@pyq:/home$ docker pull mysql
Using default tag: latest #不指明指定版本则默认下载latest
latest: Pulling from library/mysql
a10c77af2613: Pull complete #由于docker架构,进行分层下载,在前面原理所附带的链接中有详细说明
b76a7eb51ffd: Pull complete
258223f927e4: Pull complete
2d2c75386df9: Pull complete
63e92e4046c9: Pull complete
f5845c731544: Pull complete
bd0401123a9b: Pull complete
3ef07ec35f1a: Pull complete
c93a31315089: Pull complete
3349ed800d44: Pull complete
6d01857ca4c1: Pull complete
4cc13890eda8: Pull complete
Digest: sha256:aeecae58035f3868bf4f00e5fc623630d8b438db9d05f4d8c6538deb14d4c31b #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
#下载特定版本的镜像,但需要其存在于docker hub中
docker@pyq:/home$ docker pull mysql:5.7
Options:
-a, --all-tags 下载存储库中的所有标记镜像
--disable-content-trust 跳过镜像验证 (default true)
--platform string 如果服务器支持多平台,则设置平台
-q, --quiet 抑制详细输出
docker rmi:删除镜像
1
2
3
4
5
6
7
8
9
#通过镜像id进行删除
docker@pyq:/home$ docker rmi -f b05128b000dd
Options:
-f, --force 强制删除镜像
--no-prune Do not delete untagged parents
#删除全部容器
docker@pyq:/home$ docker rmi -f $(docker images -aq)
docker commit:提交镜像
1
2
3
4
5
6
7
8
#命令
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Options:
-a, --author string 作者(例如,“约翰·汉尼拔·史密斯 <hannibal@a-team.com>”)
-c, --change list 将 Dockerfile 指令应用于创建的镜像
-m, --message string 提交消息
-p, --pause 在提交期间暂停容器
3. 容器命令
在前面我们说到镜像相当于一个可运行的程序,而容器则相当于一个运行中的进程。因此我们下载一个Ubuntu镜像进行学习。
docker pull:下载镜像
1
docker pull ubuntu
docker run:新建容器并启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#新建容器并启动
docker run [Options] image
Options:
-name="Name" 容器名字
-d 后台运行
-it 使用交互方式运行
-p 指定容器端口(包括4种方式:ip:主机端口:容器端口;主机端口:容器端口;容器端口;或者直接省略-p加容器端口)
-P 大写的p为随机映射端口
#启动Ubuntu并进入容器
docker@pyq:/root$ docker run --name="ubuntu01" -it ubuntu /bin/bash
root@0e4148208d5d:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
docker ps:查看当前容器运行情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker ps [OPTIONS]
Options:
-a, --all 显示当前运行的所有容器
-f, --filter filter 根据提供的条件过滤输出
--format string 使用 Go 模板的漂亮打印容器
-n, --last int 显示 n 个最后创建的容器(包括所有状态)(default -1)
-l, --latest 显示最新创建的容器(包括所有状态)
--no-trunc 不要截断输出
-q, --quiet 只显示容器 ID
-s, --size 显示总文件大小
docker@pyq:/root$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0e4148208d5d ubuntu "/bin/bash" 10 minutes ago Up 10 minutes ubuntu01
将容器提交为一个新的镜像
通过这个命令我们可以将对容器的更改保存为一个新的镜像
1
2
3
4
5
6
7
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Options:
-a, --author string 作者 (e.g., "John Hannibal Smith <hannibal@a-team.com>")
-c, --change list 将 Dockerfile 指令应用于创建的镜像
-m, --message string 提交消息
-p, --pause 在提交期间暂停容器
退出容器
1
2
3
4
5
#直接容器停止并退出
exit
#容器不停止退出
ctrl + p +q
删除容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker rm [OPTIONS] CONTAINER [CONTAINER...]
Options:
-f, --force 强制移除正在运行的容器(使用 SIGKILL)
-l, --link 删除指定链接
-v, --volumes 删除与容器关联的匿名卷
#删除指定容器,不能删除正在运行的容器
docker@pyq:/root$ docker rm 0e4148208d5d
Error response from daemon: You cannot remove a running container 0e4148208d5dba097ca2fa707f4cfa0b64ff9eb4f84f610e846ea217cae06855. Stop the container before attempting removal or force remove
#删除所有的容器
docker rm -f $(docker ps -aq)
docker ps -a -q|xargs docker rm
启动和停止容器的操作
1
2
3
4
5
6
7
8
9
10
11
#启动容器
docker start 容器id
#重启容器
docker restart 容器id
#停止当前正在运行的容器
docker stop 容器id
#强制停止当前容器
docker kill r
4. 数据卷命令
创建数据卷
在/var/lib/docker/volumes/目录下创建数据卷
1
docker volume create pyq-vol
查看所有数据卷
1
2
docker volume ls
local pyq-vol
查看指定数据卷的信息
1
2
3
4
5
6
7
8
9
10
11
docker volume inspect pyq-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/pyq-vol/_data",
"Name": "pyq-vol",
"Options": {},
"Scope": "local"
}
]
删除数据卷
1
docker volume rm pyq-vol
删除所有未使用的数据卷
1
docker volume prune
删除容器之间相关的卷
1
docker rm -v ...
匿名挂载
匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹。
1
2
3
#进行匿名挂载
root@pyq:~# docker run -d -P --name nginx01 -v /etc/nginx nginx
d3a27b969d122d5516cac75e99b17dff7aaaf1e0c042385c6b05990053f1259
具名挂载
具名挂载,就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在Docker指定的默认数据卷路径(/var/lib/docker/volumes/)下的。通过docker volume ls命令可以查看当前数据卷的目录情况。
1
2
3
4
5
root@pyq:~# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
4ceaff19e5275dcd3014a8e7a8af618f7f7ce0da18d605c7c41a8653e78bf912
root@pyq:~# docker volume ls
DRIVER VOLUME NAME
local juming-nginx
此外我们还可以指定数据卷映射的相关参数,包括ro(只读)和rw(可读可写)。
1
2
root@pyq:~# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
root@pyq:~# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
指定路径(bind mounts)挂载数据卷
与具名挂载或匿名挂载不同,指定路径挂载的方式会==隐藏掉被挂载目录里面的内容==。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
docker run -it -v 主机目录:容器目录
#运行ubuntu镜像,并进行挂载以及新建文件
root@pyq:~# docker run -it --name ubuntuvolume -v /home/test:/home ubuntu /bin/bash
root@0f32c0371ada:/# cd /home/
root@0f32c0371ada:/home# ls
root@0f32c0371ada:/home# touch test.txt
#在宿主机中查看
root@pyq:~# cd /home/test/
root@pyq:/home/test# ls
test.txt
#查看容器配置
root@pyq:/home/test# docker inspect ubuntuvolume
"Mounts": [
{
"Type": "bind",
"Source": "/home/test",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
除了docker -v(volume)命令,我们还可以通过– mount进行挂载,具体操作以及与volume命令的差别见博客。在创建dockerfile时挂载数据卷,默认映射的主机路径也是/var/lib/docker/volumes/。
数据卷容器
1
2
#创建一个ubuntu02用于继承ubuntu01中的数据卷
root@pyq:~# docker run -it --name ubuntu02 --volumes-from ubuntu01 ubuntu
5. Dockerfile命令
docker build: 通过dockerfile文件构建镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
docker build [OPTIONS] PATH | URL | -
Options:
--add-host list 添加自定义主机到 IP 映射 (host:ip)
--build-arg list 设置构建时变量
--cache-from strings 考虑作为缓存源的图像
--cgroup-parent string 容器的可选父 cgroup
--compress 使用 gzip 压缩构建上下文
--cpu-period int 限制 CPU CFS(完全公平调度程序)周期
--cpu-quota int 限制 CPU CFS(完全公平调度程序)配额
-c, --cpu-shares int CPU份额(相对权重)
--cpuset-cpus string 允许执行的 CPU (0-3, 0,1)
--cpuset-mems string 允许执行的 MEMs (0-3, 0,1)
--disable-content-trust 跳过图像验证 (default true)
-f, --file string Dockerfile 的名称 (Default is 'PATH/Dockerfile')
--force-rm 始终移除中间容器
--iidfile string 将图像 ID 写入文件
--isolation string 容器隔离技术
--label list 设置图像的元数据
-m, --memory bytes 内存限制
--memory-swap bytes 交换限制等于内存加交换:“-1”启用无限交换
--network string 在构建期间为 RUN 指令设置网络模式
--no-cache 构建图像时不要使用缓存
--pull 总是尝试拉取更新版本的图像
-q, --quiet 成功时禁止构建输出并打印图像 ID
--rm 成功构建后删除中间容器 (default true)
--security-opt strings 安全选项
--shm-size bytes /dev/shm 的大小
-t, --tag list 名称和可选的“名称:标签”格式的标签
--target string 将目标构建阶段设置为构建
--ulimit ulimit 超限选项
在使用docker build命令末尾往往会跟个.
,其意义见博客。
6. 其他常用命令
后台启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#后台启动Ubuntu镜像
docker@pyq:/home$ docker run -d ubuntu
1760358d0eb8ad64f7c8d6c4dd03787a1e9bac7d9113902f9d46c227e5d008a8
#此时我们查看当前正在运行的镜容器发现没有Ubuntu
docker@pyq:/home$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
#而历史运行过的容器中包含Ubuntu,说明服务在后台启动成功,但由于没有前台应用,其自动停止
docker@pyq:/home$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1760358d0eb8 ubuntu "bash" 8 seconds ago Exited (0) 7 seconds ago affectionate_elbakyan
154cb2d8fa7a hello-world "/hello" 23 hours ago Exited (0) 23 hours ago ecstatic_wing
53b9e99d3ce9 hello-world "/hello" 23 hours ago Exited (0) 23 hours ago jovial_almeida
查看日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
docker logs [OPTIONS] CONTAINER
Options:
--details 显示提供给日志的额外详细信息
-f, --follow 跟踪日志输出
--since string 显示自时间戳(例如 2013-01-02T13:23:37Z)或相关(例如 42m 为 42 分钟)以来的日志
-n, --tail string 从日志末尾显示的行数 (default "all")
-t, --timestamps 显示时间戳
--until string 在时间戳(例如 2013-01-02T13:23:37Z)或相关(例如 42m 为 42 分钟)之前显示日志
#后台运行shell脚本
docker@pyq:/home$ docker run -d ubuntu /bin/bash -c "while true;do echo pyq;sleep 1;done"
080d42d3a65b98467d8e37b898a993df6ed2d9860cb5db56c51e76499b584133
#查看日志
docker@pyq:/home$ docker logs -tf -n 10 080d42d3a65b
2021-12-03T02:22:08.408578214Z pyq
2021-12-03T02:22:09.409659975Z pyq
2021-12-03T02:22:10.410728057Z pyq
2021-12-03T02:22:11.411991091Z pyq
2021-12-03T02:22:12.413285170Z pyq
2021-12-03T02:22:13.414409660Z pyq
2021-12-03T02:22:14.415627181Z pyq
2021-12-03T02:22:15.416709179Z pyq
2021-12-03T02:22:16.417844522Z pyq
2021-12-03T02:22:17.419005487Z pyq
查看容器进程信息
1
docker top 容器id
查看容器更加详细的信息
1
docker inspect 容器id
进入当前正在运行的容器
1
2
3
4
5
#方式一:开启一个新的终端进入容器
docker exec -it 容器id /bin/bash
#方式二:进入容器正在执行的终端,不启动新的进程
docker attach 容器id
从容器拷贝文件到主机上
1
2
#在主机上执行命令
docker cp 容器id:容器路径 目的主机路径
7. 小结
以上是我们在使用Docker最常使用的命名,其他相关命令请根据官方文档进行使用。
3.2 命令的练习
1. 部署Nginx
(1) 搜索Nginx镜像,也可直接在Docker hub搜索
1
docker@pyq:/home$ docker search nginx
(2) 下载Nginx
1
docker@pyq:/home$ docker pull nginx
(3) 查看Nginx是否下载至本地
1
docker@pyq:/home$ docker images
(4) 启动Nginx,并将容器的80端口映射至宿主机的3344端口(请确保3344端口开启,若采用阿里云则需登录ECS添加安全组)
1
2
3
4
5
docker@pyq:/home$ docker run -d --name nginx01 -p 3344:80 nginx
1801172a022a7160e105df51b823ef960734282a88ac960c357d4b5d329ad1c4
docker@pyq:/home$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1801172a022a nginx "/docker-entrypoint.…" 2 seconds ago Up 2 seconds 0.0.0.0:3344->80/tcp, :::3344->80/tcp nginx01
(5) 测试Nginx
1
docker@pyq:/home$ curl localhost:3344
也可在浏览器输入服务器公网ip地址:3344进行测试。
2. 部署tomcat,并将修改后的镜像进行提交
(1) 这次我们直接在dockerhub中搜索tomcat镜像,选择相应版本进行下载
(2) 下载tomcat镜像
1
2
3
4
5
#官方提供的步骤,可以看到命令为直接run且最后附带了--rm,即下载该容器再运行该容器,运行借宿后删除掉该容器,主要用于测试
docker run -it --rm tomcat:9.0
#下载tomcat镜像
docker@pyq:/root$ docker pull tomcat:9.0
(3) 查看镜像是否下载成功
1
2
3
4
5
6
docker@pyq:/root$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f652ca386ed1 21 hours ago 141MB
tomcat 9.0 76206e3ba4b1 2 weeks ago 680MB
ubuntu latest ba6acccedd29 6 weeks ago 72.8MB
hello-world latest feb5d9fea6a5 2 months ago 13.3kB
(4) 运行tomcat,我们将tomcat在后台运行,并且做一个端口映射至宿主机3355端口(确保开启)
1
2
3
4
5
6
7
docker@pyq:/root$ docker run -d --name tomcat01 -p 3355:8080 tomcat:9.0
723bec19d070105047fdfc8703f5715edbc677b8f981718d6fae0a8431d2f257
#查看镜像运行情况
docker@pyq:/root$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
723bec19d070 tomcat:9.0 "catalina.sh run" 3 seconds ago Up 2 seconds 0.0.0.0:3355->8080/tcp, :::3355->8080/tcp tomcat01
(5) 进入容器并修改相关配置,由于webapps文件夹中为空,我们需要将webapps.dist中的文件复制到
1
2
3
4
5
6
7
8
9
docker@pyq:/root$ docker exec -it tomcat01 /bin/bash
root@723bec19d070:/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@723bec19d070:/usr/local/tomcat# cd webapps.dist/
root@723bec19d070:/usr/local/tomcat/webapps.dist# ls
ROOT docs examples host-manager manager
root@723bec19d070:/usr/local/tomcat/webapps.dist# cd ..
root@723bec19d070:/usr/local/tomcat# cp -r webapps.dist/* webapps
(6) 测试,在浏览器中输入服务器ip地址:3355
(7) 提交新的镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#相较于从Docker hub中下载的tomcat镜像,我们对其webapps路径进行了修改
root@pyq:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
723bec19d070 tomcat:9.0 "catalina.sh run" 3 days ago Up 4 seconds 0.0.0.0:3355->8080/tcp, :::3355->8080/tcp tomcat01
root@pyq:~# docker exec -it tomcat01 /bin/bash
root@723bec19d070:/usr/local/tomcat# cd webapps
root@723bec19d070:/usr/local/tomcat/webapps# ls
ROOT docs examples host-manager manager
#提交新的镜像
root@pyq:~# docker commit -a="pyq" -m="add webpages" 723bec19d070 tomcatpyq:1.0
sha256:ef16ff050595527fa3fdf98cec9859c06245f4fba02032d074b1ef18deca8e43
root@pyq:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcatpyq 1.0 ef16ff050595 7 seconds ago 685MB
nginx latest f652ca386ed1 4 days ago 141MB
tomcat 9.0 76206e3ba4b1 2 weeks ago 680MB
ubuntu latest ba6acccedd29 7 weeks ago 72.8MB
hello-world latest feb5d9fea6a5 2 months ago 13.3kB
portainer/portainer latest 580c0e4e98b0 8 months ago 79.1MB
elasticsearch 7.6.2 f29a1ee41030 20 months ago 791MB
kibana 7.4.2 230d3ded1abc 2 years ago 1.1GB
3. 部署es+kibana
本次练习主要是练习docker中不同容器之间的交互。
(1) 下载并运行es
由于es暴露端口多,耗内存大,因此我们在下载时对其进行一些配置
1
2
3
4
5
docker@pyq:/root$ docker run -d --name es01 -p 3350:9200 -p 3351:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xms512m" elasticsearch:7.6.2
docker@pyq:/root$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
15eb00cee4cb elasticsearch:7.6.2 "/usr/local/bin/dock…" 6 seconds ago Up 4 seconds 0.0.0.0:3350->9200/tcp, :::3350->9200/tcp, 0.0.0.0:3351->9300/tcp, :::3351->9300/tcp es01
(2) 查看容器运行消耗资源
1
2
3
4
docker@pyq:/root$ docker stats 15eb00cee4cb
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
15eb00cee4cb es01 0.03% 754.6MiB / 1.941GiB 37.97% 1.09kB / 0B 1.69MB / 729kB 41
(3) 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
docker@pyq:/root$ curl localhost:3350
{
"name" : "15eb00cee4cb",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "5yAOMnJuSlun47AxoMDd8Q",
"version" : {
"number" : "7.6.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date" : "2020-03-26T06:34:37.794943Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
(4) 安装kibana,利用内网ip进行连接
1
docker@pyq:/root$ docker run --name kibana -e ELASTICSEARCH_HOSTS=http://172.26.200.62:3350 -p 3352:5601 -d kibana:7.4.2
(5) 测试,在浏览器中输入ip地址:3552
4. Portainer实现docker可视化管理
(1) 下载Portainer并运行
我们采用单机版Portainer进行运行。
1
2
3
4
5
docker@pyq:/root$ docker run -d -p 8088:9000 \
> --restart=always \
> -v /var/run/docker.sock:/var/run/docker.sock --privileged=true\
> --name prtainer01 \
> portainer/portainer
(2) 测试,在浏览器中输入ip地址:8088
3.3 Dockerfile练习
1. 编写Ubuntu镜像
(1) 在宿主机中新建dockerfiles文件夹用于保存编写的dockerfile
1
2
3
docker@pyq:/home$ sudo mkdir dockerfiles
docker@pyq:/home$ ls
docker dockerfiles test
(2) 新建pyq-ubuntu-dockerfile,并进行编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker@pyq:/home$ cd dockerfiles/
docker@pyq:/home/dockerfiles$ sudo touch pyq-ubuntu-dockerfile
docker@pyq:/home/dockerfiles$ ls
pyq-ubuntu-dockerfile
docker@pyq:/home/dockerfiles$ sudo vim pyq-ubuntu-dockerfile
docker@pyq:/home/dockerfiles$ cat pyq-ubuntu-dockerfile
FROM ubuntu
MAINTAINER pyq<pengzeyang@gmail.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----END----"
CMD /bin/bash
pyq-ubuntu-dockerfile如下,相较于基础版本的Ubuntu,我们定义环境变量MYPATH为/usr/local,设置工作路径为MYPATH,并且预下载了vim以及net-tools软件,最后还暴露了80端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM ubuntu
MAINTAINER pyq<pengzeyang@gmail.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----END----"
CMD /bin/bash
(3) 通过pyq-ubuntu-dockerfile构建镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#省略下载软件的提示
docker@pyq:/home/dockerfiles$ docker build -f pyq-ubuntu-dockerfile -t pyqubuntu:0.1 .
Sending build context to Docker daemon 2.048kB
Step 1/11 : FROM ubuntu
---> ba6acccedd29
Step 2/11 : MAINTAINER pyq<pengzeyang@gmail.com>
---> Running in f37b7e53b948
Removing intermediate container f37b7e53b948
---> 2eb809321469
Step 3/11 : ENV MYPATH /usr/local
---> Running in 0c62c269f961
Removing intermediate container 0c62c269f961
---> b63882f77faa
Step 4/11 : WORKDIR $MYPATH
---> Running in 4f01c8943152
Removing intermediate container 4f01c8943152
---> 2a4a5d621e70
Step 5/11 : RUN apt-get update
---> Running in f9e2036abb34
Removing intermediate container f9e2036abb34
---> df81186c6d88
Step 6/11 : RUN apt-get install -y vim
---> Running in 47aae27e985c
Removing intermediate container 47aae27e985c
---> 04571a7ed371
Step 7/11 : RUN apt-get install -y net-tools
---> Running in ff337f33cc4b
Removing intermediate container ff337f33cc4b
Step 8/11 : EXPOSE 80
---> Running in f52f14dbbfe3
Removing intermediate container f52f14dbbfe3
---> b8b36d2df3da
Step 9/11 : CMD echo $MYPATH
---> Running in 6aca9e6026ca
Removing intermediate container 6aca9e6026ca
---> 5711a50e0a78
Step 10/11 : CMD echo "----END----"
---> Running in 29bf195be3a6
Removing intermediate container 29bf195be3a6
---> 2e2356473187
Step 11/11 : CMD /bin/bash
---> Running in a9edd8b4df38
Removing intermediate container a9edd8b4df38
---> d9b72f606cfa
Successfully built d9b72f606cfa
通过上述命令行提示,我们可以看到dockerfile构建docker镜像也是一层层构建的,并且在一层构建完成后,会删除中间镜像,返回新的镜像id,直到最后一层构建完毕。
(4) 查看构建的镜像并运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
docker@pyq:/home/dockerfiles$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pyqubuntu 0.1 d9b72f606cfa 15 minutes ago 175MB
nginx latest f652ca386ed1 5 days ago 141MB
tomcat 9.0 76206e3ba4b1 2 weeks ago 680MB
ubuntu latest ba6acccedd29 7 weeks ago 72.8MB
hello-world latest feb5d9fea6a5 2 months ago 13.3kB
portainer/portainer latest 580c0e4e98b0 8 months ago 79.1MB
elasticsearch 7.6.2 f29a1ee41030 20 months ago 791MB
kibana 7.4.2 230d3ded1abc 2 years ago 1.1GB
docker@pyq:/home/dockerfiles$ docker run -it --name="pyqubuntu01" pyqubuntu:0.1 /bin/bash
root@00d1e35fd7f2:/usr/local# pwd
/usr/local
root@00d1e35fd7f2:/usr/local# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 14 bytes 1156 (1.1 KB)
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
vim安装成功。
(5) 使用docker history查看镜像构建过程
docker@pyq:/root$ docker history pyqubuntu:0.1
IMAGE CREATED CREATED BY SIZE COMMENT
d9b72f606cfa 3 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
2e2356473187 3 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
5711a50e0a78 3 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
b8b36d2df3da 3 hours ago /bin/sh -c #(nop) EXPOSE 80 0B
20ab0502cfa8 3 hours ago /bin/sh -c apt-get install -y net-tools 1.52MB
04571a7ed371 3 hours ago /bin/sh -c apt-get install -y vim 68.2MB
df81186c6d88 3 hours ago /bin/sh -c apt-get update 32.1MB
2a4a5d621e70 3 hours ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
b63882f77faa 3 hours ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
2eb809321469 3 hours ago /bin/sh -c #(nop) MAINTAINER pyq<pengzeyang… 0B
ba6acccedd29 7 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:5d68d27cc15a80653… 72.8MB
2. 编写tomcat镜像
构建tomcat镜像需要linux环境下jdk以及tomcat的安装包,可通过jdk1.8官网下载和tomcat官网下载进行下载,也可通过下列百度云盘进行下载。
1
2
链接:https://pan.baidu.com/s/1dNBI7pZTlykIJcL3QcJfRA
提取码:1208
(1) 创建tomcatfiles用于存放构建tomcat镜像所需文件
1
2
3
4
docker@pyq:/root$ cd /home/
docker@pyq:/home$ sudo mkdir tomcatfiles
docker@pyq:/home$ ls
docker dockerfiles test tomcatfiles
(2) 使用xftp将jdk-8u311-linux-x64.tar.gz与apache-tomcat-9.0.55.tar.gz上传至/home/tomcatfiles目录
1
2
3
docker@pyq:~$ cd /home/tomcatfiles/
docker@pyq:/home/tomcatfiles$ ls
apache-tomcat-9.0.55.tar.gz jdk-8u311-linux-x64.tar.gz
(3) 编写Dockerfile文件,这里我们直接命名为Dockerfile,这样在构建时不需要再输入-f
- 首先需要将两个安装包通过ADD命令添加至/usr/local目录下(自动解压)
- 同时为镜像安装vim编辑器
- 调整权限
- 定义MYPATH变量以及更新工作目录
- 定义环境变量
- 暴露8080端口
- 默认启动即开启tomcat和输出日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM ubuntu
MAINTAINER pyq<pengzeyang@gmail.com>
ADD jdk-8u311-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.55.tar.gz /usr/local/
RUN apt-get update
RUN apt-get install -y vim
RUN chmod 777 /usr/local/apache-tomcat-9.0.55/bin/startup.sh
RUN chmod 777 /usr/local/apache-tomcat-9.0.55/bin/catalina.sh
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_311
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.55
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.55
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.55/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.55/bin/logs/catalina.out
(4) 开始构建镜像
可以看到在构建一开始,docker client将构建所需文件打包发送至docker daemon进行构建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
docker@pyq:/home/tomcatfiles$ docker build -t pyqtomcat .
Sending build context to Docker daemon 158.4MB
Step 1/17 : FROM ubuntu
---> ba6acccedd29
Step 2/17 : MAINTAINER pyq<pengzeyang@gmail.com>
---> Using cache
---> 2eb809321469
Step 3/17 : ADD jdk-8u311-linux-x64.tar.gz /usr/local/
---> 06363f790cd7
Step 4/17 : ADD apache-tomcat-9.0.55.tar.gz /usr/local/
---> ab4eed5912a0
Step 5/17 : RUN apt-get update
---> Running in 7fa73c12d910
Removing intermediate container 7fa73c12d910
---> 1d104907c17a
Step 6/17 : RUN apt-get install -y vim
---> Running in 0a5c327d9330
Removing intermediate container 0a5c327d9330
---> 473120ad1856
Step 7/17 : RUN chmod 777 /usr/local/apache-tomcat-9.0.55/bin/startup.sh
---> Running in e4b6766dc606
Removing intermediate container e4b6766dc606
---> bae5b55cd2e9
Step 8/17 : RUN chmod 777 /usr/local/apache-tomcat-9.0.55/bin/catalina.sh
---> Running in 1c7c08f9be5f
Removing intermediate container 1c7c08f9be5f
---> e58e2eb2f923
Step 9/17 : ENV MYPATH /usr/local
---> Running in b95d1f8d9441
Removing intermediate container b95d1f8d9441
---> e146b103bb37
Step 10/17 : WORKDIR $MYPATH
---> Running in 9087e8b2ff4e
Removing intermediate container 9087e8b2ff4e
---> 83daf3eb2bdb
Step 11/17 : ENV JAVA_HOME /usr/local/jdk1.8.0_311
---> Running in 8a32871c2d34
Removing intermediate container 8a32871c2d34
---> 36fca627b23a
Step 12/17 : ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
---> Running in 4dac17e216ea
Removing intermediate container 4dac17e216ea
---> 9684ae162fb9
Step 13/17 : ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.55
---> Running in b733e0a86533
Removing intermediate container b733e0a86533
---> e8658da8ae44
Step 14/17 : ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.55
---> Running in 9fdc2a439588
Removing intermediate container 9fdc2a439588
---> f4304391f53d
Step 15/17 : ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
---> Running in 14beb9259341
Removing intermediate container 14beb9259341
---> 1dd6b89b3ca4
Step 16/17 : EXPOSE 8080
---> Running in ea5f9f9ffe62
Removing intermediate container ea5f9f9ffe62
---> 3e00babc47e6
Step 17/17 : CMD /usr/local/apache-tomcat-9.0.55/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.55/bin/logs/catalina.out
---> Running in 6fe1bbb6b1aa
Removing intermediate container 6fe1bbb6b1aa
---> 3397717735f8
Successfully built 3397717735f8
Successfully tagged pyqtomcat:latest
docker@pyq:/home/tomcatfiles$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pyqtomcat latest 3397717735f8 4 minutes ago 554MB
pyqubuntu 0.1 d9b72f606cfa 20 hours ago 175MB
nginx latest f652ca386ed1 6 days ago 141MB
tomcat 9.0 76206e3ba4b1 2 weeks ago 680MB
ubuntu latest ba6acccedd29 7 weeks ago 72.8MB
hello-world latest feb5d9fea6a5 2 months ago 13.3kB
portainer/portainer latest 580c0e4e98b0 8 months ago 79.1MB
elasticsearch 7.6.2 f29a1ee41030 20 months ago 791MB
kibana 7.4.2 230d3ded1abc 2 years ago 1.1GB
(5) 启动镜像
1
2
3
4
5
6
docker@pyq:/home/tomcatfiles$ docker run -d -p 3348:8080 --name pyqtomcat01 -v /home/pyqtomcat/webapps:/usr/local/apache-tomcat-9.0.55/webapps/test -v /home/pyqtomcat/logs:/usr/local/apache-tomcat-9.0.55/logs pyqtomcat
b025bdf9c6146bc32df433147a3db6d7adbec091e0e8d2e843b34400d9084466
docker@pyq:/home/tomcatfiles$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b025bdf9c614 pyqtomcat "/bin/sh -c '/usr/lo…" 4 seconds ago Up 3 seconds 0.0.0.0:3348->8080/tcp, :::3348->8080/tcp pyqtomcat01
/usr/local/tomcat/webapps.dist
(6) 验证,在浏览器中输入ip地址:3348
3. 发布镜像
在选择发布镜像的时候,一般可以选择两种途径,一种是发布至docker hub,另外一种是发布至阿里云镜像服务。
Docker hub
(1) 在docker hub官网注册账号
(2) 使用docker login命令进行登录
退出账号则docker logout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker login [OPTIONS] [SERVER]
Options:
-p, --password string 密码
--password-stdin 从标准输入中获取密码
-u, --username string 用户名
docker@pyq:/home/webapps$ docker login -u pengyongqiang
Password:
WARNING! Your password will be stored unencrypted in /home/docker/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
(3) 使用docker push命令发布镜像
1
2
3
4
5
6
docker push [OPTIONS] NAME[:TAG]
Options:
-a, --all-tags 推送存储库中的所有标记图像
--disable-content-trust 跳过图像签名
-q, --quiet 抑制详细输出
在发布镜像时,镜像的命名一定要遵从docker官方的规则,即==“docker hub中的username”/镜像名:版本==,否则将会出现发布错误。若一开始命名不规范,可通过docker tag重命名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#一开始由于命名不规范,发步失败
docker@pyq:/home$ docker push pyqtomcat
Using default tag: latest
The push refers to repository [docker.io/library/pyqtomcat]
b72539cc3170: Preparing
5ff5123f1e0c: Preparing
d50233c29fc0: Preparing
8bcdce3711f4: Preparing
26121733cf34: Preparing
e0cb58f1bfc9: Waiting
9f54eef41275: Waiting
denied: requested access to the resource is denied
#重命名
docker@pyq:/home$ docker tag 3397717735f8 pengyongqiang/pyqtomcat
docker@pyq:/home$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pyqtomcat latest 3397717735f8 2 hours ago 554MB
pengyongqiang/pyqtomcat latest 3397717735f8 2 hours ago 554MB
pyqubuntu 0.1 d9b72f606cfa 22 hours ago 175MB
nginx latest f652ca386ed1 6 days ago 141MB
tomcat 9.0 76206e3ba4b1 2 weeks ago 680MB
ubuntu latest ba6acccedd29 7 weeks ago 72.8MB
hello-world latest feb5d9fea6a5 2 months ago 13.3kB
portainer/portainer latest 580c0e4e98b0 8 months ago 79.1MB
elasticsearch 7.6.2 f29a1ee41030 20 months ago 791MB
kibana 7.4.2 230d3ded1abc 2 years ago 1.1GB
#成功开始发布
docker@pyq:/home$ docker push pengyongqiang/pyqtomcat
Using default tag: latest
The push refers to repository [docker.io/pengyongqiang/pyqtomcat]
b72539cc3170: Pushed
5ff5123f1e0c: Pushed
d50233c29fc0: Pushing [=============> ] 19.03MB/68.21MB
8bcdce3711f4: Pushing [=======> ] 4.615MB/32.07MB
26121733cf34: Pushing [============> ] 4.023MB/16.05MB
e0cb58f1bfc9: Pushing [> ] 3.865MB/365.3MB
9f54eef41275: Pushing [=====> ] 8.151MB/72.78MB
发布至阿里云镜像服务
Ⅳ Docker网络
4.1 Docker网络概述
在学习docker网络前,请清空所有容器与镜像。
安装Docker时,它会自动创建三个网络,分别为bridge、host和none。
1
2
3
4
5
docker@pyq:/root$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a7a8ade9213b bridge bridge local
7781fde0141c host host local
c623ddd0254e none null local
除了以上三种网络模式还有container模式以及自定义网络模式。
网络模式 | 简介 |
---|---|
Host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 |
Bridge | 此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。 |
None | 该模式关闭了容器的网络功能。 |
Container | 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。 |
自定义网络 | 略 |
我们在使用docker run创建Docker容器时,可以用–net选项指定容器的网络模式,Docker可以有以下4种网络模式:
- host模式:使用–net=host指定
- none模式:使用–net=none指定
- bridge模式:使用–net=bridge指定,默认设置
- container模式:使用–net=container:NAME_or_ID指定
4.2 Host、Container、none
1. Host
相当于Vmware中的桥接模式,与宿主机在同一个网络中,但没有独立IP地址。
众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
2. Container
这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
3. None
该模式将容器放置在它自己的网络栈中,但是并不进行任何配置。实际上,该模式关闭了容器的网络功能,在以下两种情况下是有用的:容器并不需要网络(例如只需要写磁盘卷的批处理任务)。
Host网络模式的缺陷:
- Docker Container网络环境隔离性的弱化。即容器不再拥有隔离、独立的网络栈。
- 使用host模式的Docker Container虽然可以让容器内部的服务和传统情况无差别、无改造的使用,但是由于网络隔离性的弱化,该容器会与宿主机共享竞争网络栈的使用。
- 另外,容器内部将不再拥有所有的端口资源,原因是部分端口资源已经被宿主机本身的服务占用,还有部分端口已经用以 bridge 网络模式容器的端口映射。
4.3 Bridge
1. Docker0
Bridge模式是docker网络的默认模式,当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.0.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker@pyq:/home$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:18:a5:f3 brd ff:ff:ff:ff:ff:ff
inet 172.26.200.62/20 brd 172.26.207.255 scope global dynamic eth0
valid_lft 314752343sec preferred_lft 314752343sec
inet6 fe80::216:3eff:fe18:a5f3/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:02:74:be:78 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:2ff:fe74:be78/64 scope link
valid_lft forever preferred_lft forever
2. 容器与宿主机的通信
bridge模式下容器与docker0的通信方式如下。
Docker完成以上网络配置的过程大致是这样的:
-
在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
- Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看。veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方。
- 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。
实际演示
开启一个新的ubuntu容器,通过ip addr查看网络配置可见veth par设备的一端eth0被添加至容器中,ip地址与docker0处于同一网段,并且网关为docker0。
1
2
3
4
5
6
7
8
9
docker@pyq:/root$ docker run -it --name ubuntu01 ubuntu /bin/bash
root@53c206f9810c:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
96: eth0@if97: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
在宿主机内输入ip addr查看ip地址,可见veth pair设备的另一端已被添加。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
docker@pyq:/root$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:18:a5:f3 brd ff:ff:ff:ff:ff:ff
inet 172.26.200.62/20 brd 172.26.207.255 scope global dynamic eth0
valid_lft 314735485sec preferred_lft 314735485sec
inet6 fe80::216:3eff:fe18:a5f3/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:02:74:be:78 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:2ff:fe74:be78/64 scope link
valid_lft forever preferred_lft forever
#可以看到多了一个新的网卡
97: veth199352d@if96: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 4e:7a:34:11:a7:62 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::4c7a:34ff:fe11:a762/64 scope link
valid_lft forever preferred_lft forever
当停掉容器时,新增加的网卡再次消失,如下图所示。
3. 容器之间的通信
在bridge模式下,连在同一网桥上的容器可以相互通信(若出于安全考虑,也可以禁止它们之间通信,方法是在DOCKER_OPTS变量中设置–icc=false,这样只有使用–link(目前已不推荐使用)才能使两个容器通信)。
Docker可以开启容器间通信(意味着默认配置–icc=true),也就是说,宿主机上的所有容器可以不受任何限制地相互通信,这可能导致拒绝服务攻击。进一步地,Docker可以通过–ip_forward和–iptables两个选项控制容器间、容器和外部世界的通信。
容器也可以与外部通信,我们看一下主机上的Iptable规则,可以看到这么一条。
1
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
这条规则会将源地址为172.17.0.0/16的包(也就是从Docker容器产生的包),并且不是从docker0网卡发出的,进行源地址转换,转换成主机网卡的地址。举一个例子说明一下,假设主机有一块网卡为eth0,IP地址为10.10.101.105/24,网关为10.10.101.254。从主机上一个IP为172.17.0.2/16的容器中ping百度(180.76.3.151)。IP包首先从容器发往自己的默认网关docker0,包到达docker0后,也就到达了主机上。然后会查询主机的路由表,发现包应该从主机的eth0发往主机的网关10.10.105.254/24。接着包会转发给eth0,并从eth0发出去(主机的ip_forward转发应该已经打开)。这时候,上面的Iptable规则就会起作用,对包做SNAT转换,将源地址换为eth0的地址。这样,在外界看来,这个包就是从10.10.101.105上发出来的,Docker容器对外是不可见的。
那么,外面的机器是如何访问Docker容器的服务呢?我们首先用下面命令创建一个含有web应用的容器,将容器的80端口映射到主机的80端口。
1
docker run --name=nginx_bridge --net=bridge -p 80:80 -d nginx
然后查看Iptable规则的变化,发现多了这样一条规则:
1
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
此条规则就是对主机eth0收到的目的端口为80的tcp流量进行DNAT转换,将流量发往172.17.0.2:80,也就是我们上面创建的Docker容器。所以,外界只需访问10.10.101.105:80就可以访问到容器中的服务。
除此之外,我们还可以自定义Docker使用的IP地址、DNS等信息,甚至使用自己定义的网桥,但是其工作方式还是一样的。
Bridge桥接模式的缺陷:
- 最明显的是,该模式下Docker Container不具有一个公有IP,即和宿主机的eth0不处于同一个网段。导致的结果是宿主机以外的世界不能直接和容器进行通信。
- 虽然NAT模式经过中间处理实现了这一点,但是NAT模式仍然存在问题与不便,如:容器均需要在宿主机上竞争端口,容器内部服务的访问者需要使用服务发现获知服务的外部端口等。
- 另外 NAT 模式由于是在三层网络上的实现手段,故肯定会影响网络的传输效率。
4.4 自定义网络
建议使用自定义的网桥来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址。Docker提供了创建这些网络的默认网络驱动程序,你可以创建一个新的Bridge网络,Overlay或Macvlan网络。你还可以创建一个网络插件或远程网络进行完整的自定义和控制。
你可以根据需要创建任意数量的网络,并且可以在任何给定时间将容器连接到这些网络中的零个或多个网络。此外,您可以连接并断开网络中的运行容器,而无需重新启动容器。当容器连接到多个网络时,其外部连接通过第一个非内部网络以词法顺序提供。
有关docker网络更详细的讲解可参考博客。