目录
一、Docker常用命令
1.帮助命令
docker version 显示docker的版本信息
docker info 显示docker的系统信息
docker 命令 --help 帮助命令(重要)可查命令的选项 不会就查帮助手册!!!
官方帮助文档:docker | Docker Docs
2.镜像命令
列出镜像
docker images 查看本地主机上的所有镜像
#可选项
-a 列出所有
-q 只列出镜像ID
搜索镜像
docker search [image] 搜索镜像
eg: docker search mysql
下载镜像
docker pull [image]:[tag] 下载镜像
eg: docker pull mysql #如果不写tag,默认就是latest
[jackbewater@hecs-156417 ~]$ 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 mysql等价于docker pull docker.io/library/mysql:latest下载别的版本的mysql
[jackbewater@hecs-156417 ~]$ 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 -f [镜像ID] 删除指定镜像
docker rmi -f [镜像ID] [镜像ID] [镜像ID] 删除多个镜像
docker rmi -f $(docker images -aq) 删除所有镜像
3.容器命令
创建容器并启动
docker run [可选参数] images
#可选参数
--name="NAME" 给运行的容器命名,用于区分容器
-d 以后台方式运行
-it 使用交互方式运行,进入容器查看容器内部的东西
-p 指定容器的端口 -p 8080 : 8080
-p 主机端口 : 容器端口
列出运行的容器
docker ps 列出正在运行的容器
#可选参数
-a 列出正在运行的所有参数和历史运行过的容器
-n=数字 列出最近几个创建的容器
-q 列出容器的ID
退出镜像
exit #退出并停止容器
Ctrl + P + Q #退出容器但是不停止运行
删除容器
docker rm 容器ID #删除指定容器,但是不能删除正在运行的容器,除非加上- f 选项
docker rm -f $(docker ps -aq) #删除所有的容器
启动和停止容器
dokcer start 容器ID
docker restart 容器ID
docker stop 容器ID
docker kill 容器ID
查看日志
docker logs -t -f --tail 数字 容器ID
#可选参数
-tf #显示带有时间戳的参数
--tail 数字 #显示日志的条数
查看容器中的进程信息
docker top 容器ID
[jackbewater@hecs-156417 ~]$ docker top 349983cda77a
UID PID PPID C STIME TTY
root 168272 168252 0 Apr17 ?
查看容器的元数据
docker inspect 容器ID
#测试
进入当前正在运行的容器
#容器通常使用后台运行,进入容器是为了进行一些配置
docker exec -it 容器ID bashshell
eg: docker exec -it 349983cda77a /bin/bash
方法二:
docker attach 容器ID
exec是进入容器后开启一个新的终端
attach是进入容器正在执行的终端,不会启动新的进程
从容器内拷贝文件到主机上
docker cp 容器ID : 目的路径 目的的主机路径
二、Docker存储
将数据存储在容器中,一旦容器被删除,容器内的数据也会消失。而且,随着容器的运行,容器的内的数据会越来越大,这样不方便恢复和迁移。将数据存储在容器之外,这样删除容器也不会丢失数据。容器发生故障时,我们可以重新创建一个容器,并将数据挂载到新的容器中,这样就可以快速恢复。
1.bind mount(绑定挂载)
绑定挂载可以将主机文件系统上的目录或文件装载到容器中,但是主机上的非docker进程可以修改它们,同时在容器中也可以修改主机的文件系统。包括创建、修改、删除文件或目录,使用不当,会带来安全隐患。即双向绑定,双方都可以修改对方的文件或目录。
绑定挂载适用以下场景:
• 将配置文件从主机共享到容器
• 在Docker主机上的开发环境和容器之间共享源代码或编译目录。
在创建并运行容器时添加- v参数进行挂载
docker run -v 主机目录 : 容器内目录
绑定挂载会将主机上的文件或目录装载到容器中。绑定挂载会覆盖容器中的目录或文件。
如果主机的目录不存在,docker会自动创建这个目录。但是docker只会自动创建文件夹,不会创建文件。
eg:
docker run -e MYSQL_ROOT_PASSWORD=123456 \
-v /home/mysql/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro \
-v /home/mysql/data:/var/lib/mysql \
-d mysql:5.7在这个例子中,配置文件设置为了只读(read-only)防止容器更改主机的文件,这个挂载的文件不再是双向绑定。
eg:
[jackbewater@hecs-156417 ~]$ docker run --name centos2 -it -v /home/测试:/home:ro 5d0da3dc9764 /bin/bash #将主机的目录/home/测试挂载到了容器内的/home目录下,并且设置了只读。即,主机能对该目录进行读写,而容器内只能查看这个目录,但是不能删除和创建
查看容器的详细信息:
测试:
主机能创建文件
容器内只能查看,不能删除,因为设置了ro只读
2.volume卷(数据卷)
卷 是docker 容器存储数据的首选方式,卷有以下优势:
- 卷可以在多个正在运行的容器之间共享数据。仅当显式删除卷时,才会删除卷。
- 当你想要将容器数据存储在外部网络存储上或云提供商上,而不是本地时。
- 卷更容易备份或迁移,当您需要备份、还原数据或将数据从一个 Docker 主机迁移到另一个 Docker 主机时,卷是更好的选择。
创建和挂载卷
-v 卷名:容器内路径
eg:
docker volume create my-data
docker run -e MYSQL_ROOT_PASSWORD=123456 \
-v /home/mysql/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf:ro \ #绑定挂载
-v my-data:/var/lib/mysql \ #数据卷挂载
-d mysql:5.7
创建的数据卷是在主机上的,数据卷的目录是/var/lib/docker/volumes(图片中的橙色部分)
卷挂载分为具名挂载和匿名挂载
匿名挂载:-v后面只有容器内路径,这就是匿名挂载,docker会自动生成一个匿名卷
docker run -e MYSQL_ROOT_PASSWORD=123456 \
-v /home/mysql/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf:ro \ #绑定挂载
-v /var/lib/mysql \ #数据卷匿名挂载
-d mysql:5.7
查看数据卷的信息
docker volume inspect 卷名
注意:不论是绑定挂载还是卷挂载,都可以设置挂载目录的权限,
# ro 只读代表容器内只能读,不能改写,能在容器外修改
# rw 只写代表容器内可读可写
ro和rw是针对容器的权限的
3.tmpfs(临时挂载)
临时挂载将数据保留在主机内存中,当容器停止时,文件将被删除。
docker run -d -it --tmpfs /tmp nginx:1.22-alpine
卷挂载和绑定挂载的区别:
数据卷由Docker(宿主机上的)管理,并且与主机的核心功能隔离,非 Docker 进程不能修改文件系统的这一部分。而绑定挂载机上的非 Docker 进程可以修改它们,同时在容器中也可以更改主机文件系统
4.绑定挂载和卷挂载的传播覆盖原则
挂载一个空卷时:容器内目录的内容会传播(复制)到卷中。
绑定挂载或挂载非空卷时:容器内目录的内容会被卷或绑定的主机目录覆盖。
ps:今天24.5.14我在运行nginx容器后,需要对容器内的/etc/nginx/conf.d配置文件进行修改,但是由于容器内没有vim编辑器不能修改,于是我想将/etc/nginx配置目录挂载出来(想法就是错的),然后我就在启动容器时添加了挂载参数,将一个本机上的空目录绑定挂载到了容器上(实际是挂不上的),容器始终无法启动。这个错误排了半天,容器启动时需要读取/etc/nginx的配置文件,但是因为我将一个空目录绑定挂载到了配置文件的目录上,导致给覆盖了,因此无法启动容器。
遇见问题第一时间要看容器的日志!!!!!这下真对传播覆盖原则有了深刻的理解了。
三、Dockerfile(构建镜像)
在了解dockerfile之前,先要知道UnionFS(联合文件系统)和docker镜像的加载原理
1.联合文件系统
UnionFS(联合文件系统)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层一层叠加。联合文件系统是docker镜像的基础,镜像通过分层来进行继承,基于基础镜像,可以制作出各种具体的应用镜像。
2.镜像加载原理
docker镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel
1.引导加载程序(bootloader):引导加载程序是在计算机启动时运行的程序,其主要任务是引导加载操作系统内核。它负责从存储设备(如硬盘、固态硬盘等)中加载内核到计算机内存中,并启动内核的执行。常见的引导加载程序包括GRUB、LILO等。
2.内核(kernel):内核是操作系统的核心部分,负责管理计算机的硬件资源(如CPU、内存、设备等)以及提供基本的系统服务。在Linux系统中,一旦内核加载到内存中并开始执行,它就会接管系统的控制权,并开始初始化系统的其他部分。
在Docker镜像的底层,bootfs是最底层的文件系统,包含了引导加载程序和内核。当系统启动时,引导加载程序会从bootfs中加载内核到内存中,随后内核开始初始化系统并加载更高层次的文件系统,例如根文件系统(root filesystem)。一旦内核加载完成并接管系统控制权,bootfs就会被卸载,内存的管理也完全由内核接管。
3.分层理解
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上创建新的镜像层。
注意!!!镜像都是只读的,当容器启动时,一个新的可写层会被加载到镜像的顶部,这一层通常成为容器层,容器层之下都是镜像层。容器运行时我们对容器的操作就在容器层中进行。
4.Dockerfile
dockerfile是用来构建镜像的文本文件,是命令参数脚本,其中包含了一条一条的指令,每一条指令构建一层。
构建镜像的步骤:
1.编写dockerfile
2.docker build构建成为镜像
3.docker run 运行镜像
4.docker push 发布镜像(DockerHub)
dockerfile的常用指令如下:
FROM ubuntu:18.04
WORKDIR /app
COPY . .
RUN make .
CMD python app.py
EXPOSE 80
FROM
打包使用的基础镜像
WORKDIR
相当于cd
命令,进入工作目录
COPY
将宿主机的文件复制到容器内
RUN
打包时执行的命令,相当于打包过程中在容器中执行shell脚本,通常用来安装应用程序所需要的依赖、设置权限、初始化配置文件等
ADD 添加压缩包,并解压
CMD
运行容器时执行的命令,只有dockerfile中最后一个CMD生效
ENTRYPOINT运行容器时执行的命令,可以追加命令
EXPOSE
指定容器在运行时监听的网络端口,它并不会公开端口,仅起到声明的作用,公开端口需要容器运行时使用-p参数指定。
docker build 构建镜像
docker build -f 指定的dockerfile -t 生成镜像的名字:版本号 . 注意不要丢掉命令结尾的 .
dockerfile的官方文件名是Dockerfile,在build时不需要-f指定,会自动寻找。
ENTRYPOINT
和CMD
的区别(重要)
CMD
运行容器时执行的命令,只有dockerfile中的最后一个CMD生效,并且不能追加参数
ENTRYPOINT运行容器时执行的命令,可以追加命令
dockerfile
应该至少包含一个ENTRYPOINT
或CMD
CMD
当运行容器时追加参数会报错,-l会替换掉ls -a,然而-l并不是命令,所以会报错。
ENTRYPOINT
在容器运行时可以追加参数和命令,不会覆盖。
ENTRYPOINT
指定容器启动时执行的默认程序,一般运行容器时不会被替换或覆盖。除非在运行容器时使用--entrypoint
进行指定新命令,这样才会覆盖docker run -it --entrypoint /bin/bash redis
如果镜像中
ENTRYPOINT
和CMD
都存在,则CMD
将作为ENTRYPOINT
的参数使用。
自己制作tomcat镜像:
准备tomcat和jdk文件:
编辑dockerfile:
docker build构建镜像:
构建成功:
创建并运行容器:
将本地文件home/jackbewater/tomcat/test和/home/jackbewater/tomcat/tomcatlogs分别挂载到容器内的/usr/local/apache-tomcat-9.0.88/webapps/test和/usr/local/apache-tomcat-9.0.88/logs上(绑定挂载)
测试访问:
四、Docker网络
在安装docker时,会自动创建三种网络:bridge、host、none
bridge网络模式:使用--net=bridge指定,或不指定net即使用默认设置。
host网络模式:使用--net=host指定。(不常用)
nono网络模式:使用--net=none指定。(不常用)
1.bridge网络
bridge网络(默认模式):此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及iptables nat表配置与宿主机通信。在bridge网络模式下容器没有公网IP,只有宿主机可以直接访问,外部主机不可访问,但是容器可以通过宿主机的NAT规则后可以访问外网。
创建并运行容器时,不指定网络模式,就会使用bridge模式,docker守护进程会利用veth pair技术,在主机上创建两个虚拟网络接口(docker0上的veth接口和容器上的eth接口)。
容器与主机可以进行通信,容器与容器之间也可以通信(都使用的是bridge网络)注意并不是直接通信,而是通过docker0 网桥进行通信。
在docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上以bridge网络模式启动的容器都会连接到虚拟网桥上。docker0的工作方式类似于路由器,一般docker会使用172.17.0.0/16这个网段,并将172.17.0.1分配给网桥(在主机上使用ip addr命令可以查看docker0,可以认为他是网桥的网关)
2.自定义网络
Docker提供三种网络驱动:bridge、overlay和macvlan。其中overlay和macvlan用于创建跨主机网络。以下只介绍自定义bridge网络。
创建自定义的bridge网络:
docker network creat --driver bridge my_net
通过docker inspect my_net查看自定义bridge网络的详细信息
创建自定义网络时可通过--subnet和--gateway参数来指定网桥的网段和网关:
使用自定义网络:
docker run -d --network=my_net 镜像名
在启动容器时可以通过--ip 参数指定一个静态IP,但是要注意的是,只有使用--subnet参数创建的网路才能指定静态IP。如果创建自定网络时没有指定--subnet,那么容器启动时指定静态IP就会报错。
自定义bridge网络需要注意的几个点:
1.自定义bridge网络是有域名解析的,即自定义bridge网络下的容器之间,可以通过容器名ping通。而默认网桥没有域名解析。
2.不同的自定义bridge网络下的容器之间不能通信,除非在一方的网桥上添加另一方的网卡。
3.自定义网桥和默认网桥下的容器之间也不能通信,也需要添加网卡。