Docker 学习
基本概念
Docker Daemon:运行在 Docker 服务器的后台进程,侦听 Docker API 请求并管理 Docker 对象,例如镜像、容器、网络和挂载卷,守护进程还可以与其他守护进程通信以管理 Docker 服务
Docker Client: Docker 客户端是许多 Docker 用户与 Docker 交互的主要方式,当使用 docker run 之类的命令时,客户端会将这些命令发送到 dockerd,从而执行它们,该 docker 命令使用 Docker API,Docker 客户端可以与多个守护进程通信
Docker Host:安装 Docker 服务的主机
Docker Registries:存储 Docker Image 的地方,Docker Hub 是一个任何人都可以使用的公共仓库,Docker 默认配置为在 Docker Hub 上查找镜像,也可以使用自己的仓库,当使用 docker pull 或 docker run 命令时,所需的镜像将从配置的仓库中查找,当使用 docker push 命令时,镜像会被推送到你配置的仓库中
Images:镜像是用于创建 Docker 容器的只读模板,通常,一个镜像基于另一个镜像,并带有一些额外的自定义,可以自己创建镜像,也可以使用公共仓库中发布的镜像,构建镜像需要创建一个 Dockerfile,用于定义创建镜像和运行镜像所需的步骤,Dockerfile 中的每条指令都会在镜像中创建一个层,当更改 Dockerfile 并重建镜像时,只会重建那些已更改的层,与其他虚拟化技术相比,这是使镜像如此轻巧、小巧和快速的部分原因
Containers:容器是镜像的可运行实例,可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器,可以将容器连接到一个或多个网络,为其附加存储,也可以根据其当前状态创建新镜像,默认情况下,容器与其他容器及其主机相对隔离,可以控制容器的网络、存储或其他底层子系统与其他容器或主机之间的隔离程度,容器由镜像以及创建或启动它时提供给它的任何配置选项定义,当容器被移除时,未存储在持久存储中的对其状态的任何更改都会消失
Docker 用 Go 语言编写,并利用 Linux 内核的几个特性来提供其功能,Docker 使用一种称为 namespaces 的技术来实现资源隔离,运行容器时,Docker 会为该容器创建一组 namespaces, 这些 namespaces 提供了一层资源隔离,容器的每个方面都在单独的 namespaces 中运行,并且对其的访问仅限于该 namespaces
安装 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 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-selinux \ docker-engine-selinux \ docker-engine sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum list docker-ce --showduplicates | sort -r sudo yum install docker-ce docker-ce-cli containerd.io sudo yum install docker-ce-<VERSION_STRING>.x86_64 docker-ce-cli-<VERSION_STRING>.x86_64 containerd.io systemctl start docker systemctl enable docker
compose 1 2 sudo yum update sudo yum install docker-compose-plugin
镜像加速 1 2 3 4 5 6 7 8 9 10 mkdir -p /etc/dockerecho "{ \"registry-mirrors\": [ \"https://mirror.ccs.tencentyun.com\" ] }" > /etc/docker/daemon.jsonsudo systemctl daemon-reload sudo systemctl restart docker
可视化界面-Portainer 1 2 3 4 5 docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce docker run -d -p 9001:9001 --name portainer_agent --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes portainer/agent
常用命令
命令
作用
attach
绑定到运行中容器的标准输入、输出、以及错误流,绑定的是控制台,退出后可能会导致容器停止
build
从一个 Dockerfile 文件构建镜像,docker build -t imageName -f DockerfileName .
commit
把容器的改变提交并创建一个新的镜像
cp
容器和本地文件系统间复制文件或目录
create
创建新容器,但是不启动,需要手动启动,docker create –name myredis -p 6379(主机的端口):6379(容器的端口) redis 主机端口必须唯一
diff
检查容器里文件系统结构的更改(A:添加文件或目录 D:文件或者目录删除 C:文件或者目录更改)
events
获取服务器的实时事件
exec
在运行时的容器内运行命令,docker exec -it -u 0:0 –privileged mynginx4 /bin/bash 0 用户,以特权方式进入容器
export
导出容器的文件系统为一个 tar 文件,commit 是直接提交成镜像,export 是导出成文件方便传输
import
导入 tar 的内容创建一个镜像,导入进来的镜像直接启动不了容器,需要指定命令
history
显示镜像的历史
images
列出所有镜像,image ls
info
显示系统信息
inspect
获取 docker 对象的底层信息
kill
杀死一个或多个容器
save
把一个或者多个镜像保存为 tar 文件
load
从 tar 文件加载镜像
login
登录 Docker registry
logout
退出 Docker registry
logs
获取容器日志,可以看到容器在控制台输出的所有内容
pause
暂停一个或多个容器
unpause
pause 的反操作
port
列出容器的端口映射
ps
列出所有容器
pull
从 registry 下载一个 image 或者 repository
push
给 registry 推送一个 image 或者 repository
rename
重命名一个容器
restart
重启一个或者多个容器
rm
删除一个或多个容器
rmi
删除一个或多个镜像,docker rmi -f $(docker images -aq) 删除全部镜像
run
创建并启动容器
search
去 docker hub 寻找镜像
stats
显示容器资源的实时使用状态
start
启动一个或者多个容器
stop
停止一个或者多个容器
tag
给源镜像创建一个新的标签,变成新的镜像,docker tag 原镜像:标签 新镜像名:标签
top
显示正在运行容器的进程
udpate
更新一个或多个 docker 容器配置
version
查看 Docker 的版本信息
container
管理容器
image
管理镜像
network
管理网络
volume
管理卷
prune
删除游离镜像,docker image prune
docker run
-d: 后台运行容器,并返回容器 ID
-i: 以交互模式运行容器,通常与 -t 同时使用
-P: 随机端口映射,容器内部端口随机映射到主机的端口
-p: 指定端口映射,格式为:主机(宿主)端口:容器端口
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用
–name=”nginx-lb”: 为容器指定一个名称
–dns 8.8.8.8: 指定容器使用的 DNS 服务器,默认和宿主一致
–dns-search example.com: 指定容器 DNS 搜索域名,默认和宿主一致
-h “mars”: 指定容器的 hostname
-e username=”ritchie”: 设置环境变量
–env-file=[]: 从指定文件读入环境变量
–cpuset=”0-2” or –cpuset=”0,1,2”: 绑定容器到指定CPU运行
-m: 设置容器使用内存最大值;
–net=”bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container 四种类型
–link=[]: 添加链接到另一个容器
–expose=[]: 开放一个端口或一组端口
–restart: 指定重启策略,可以写–restart=awlays 总是故障重启
–volume: -v 绑定一个卷,一般格式 主机文件或文件夹:虚拟机文件或文件夹
常见部署案例 部署 Nginx 1 2 3 4 5 docker run --name nginx \ -v /usr/local/nginx/html:/usr/share/nginx/html:ro \ -v /usr/local/nginx/conf:/etc/nginx -d nginx
部署 MySQL 1 2 3 4 5 6 7 8 9 10 11 docker run -p 3306:3306 \ --name mysql8 \ --restart=always \ -v /usr/local/docker/mysql/data:/var/lib/mysql \ -v /usr/local/docker/mysql/conf:/etc/mysql/conf.d \ -v /usr/local/docker/mysql/log:/var/log/mysql \ -v /usr/local/docker/mysql/my.cnf:/etc/mysql/my.cnf \ -e MYSQL_ROOT_PASSWORD=123456 \ -d mysql:8.0.21 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_general_ci
部署 Redis 1 2 3 4 5 6 7 docker run -p 6379:6379 \ --name redis \ --restart=always \ -v /usr/local/docker/redis/redis.conf:/etc/redis/redis.conf \ -v /usr/local/docker/redis/data:/data \ -d redis:6.2.6 \ redis-server /etc/redis/redis.conf --appendonly yes
部署 ElasticSearch 1 2 3 4 5 6 7 8 docker run --name=elasticsearch \ -p 9200:9200 -p 9300:9300 \ -e "discovery.type=single-node" \ -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v /usr/local/es/data:/usr/share/elasticsearch/data \ -v /usr/local/es/plugins:/usr/shrae/elasticsearch/plugins \ -v esconfig:/usr/share/elasticsearch/config \ -d elasticsearch:7.12.0
部署 Tomcat 1 2 3 4 5 docker run --name tomcat \ -p 8080:8080 \ -v /usr/local/tomcat/conf:/usr/local/tomcat/conf \ -v /usr/local/tomcat/webapp:/usr/local/tomcat/webapps \ -d tomcat:jdk8-openjdk-slim-buster
重启策略
no: 默认策略,在容器退出时不重启容器
on-failure: 在容器非正常退出时(退出状态非0),才会重启容器
on-failure: 在容器非正常退出时重启容器,最多重启3次
always: 在容器退出时总是重启容器
unless-stopped: 在容器退出时总是重启容器,但是不考虑在 Docker 守护进程启动时就已经停止了的容器
export and import 操作容器 1 2 3 docker export 导出的文件被 import 导入以后变成镜像,并不能直接启动容器,需要知道之前的启动命令(docker ps --no-trunc),然后再用下面启动 docker run -d -P mynginx:v6 /docker-entrypoint.sh nginx -g 'daemon off;' 或者 docker image inspect 看之前的镜像,把之前镜像的 Entrypoint 的所有和 Cmd 的连接起来就能得到启动命令
save and load 操作镜像 1 2 docker save -o busybox.tar busybox:latest 把 busybox 镜像保存成 tar 文件 docker load -i busybox.tar 把压缩包里面的内容直接导成镜像
产生镜像的三种方式 基于已经存在的容器,提取成镜像 使用给定的 tar 包导入成镜像 使用 Dockerfile 生成镜像
容器的状态 Created(新建)、Up(运行中)、Pause(暂停)、Exited(退出)
如何 push 镜像到仓库 使用 tag 命令重命名镜像成你的仓库名称 docker push chenkaixin12121/mynginx:v1
Docker 存储 镜像如何存储 镜像的目录结构
容器也会自己建立层,如果想要改东西,把改的内容复制到容器层即可,docker container inspect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 docker image inspect nginx "GraphDriver" : { "Data" : { "LowerDir" : " /var/lib/docker/overlay2/ee8ad017262f81a3c136ca8b1654c8ac94966d804857877a045a3757f181cc2b/diff: /var/lib/docker/overlay2/e35023834999191cdd5c1cb7d057b7fd0b2d60d6c012d101974c2054b7133840/diff: /var/lib/docker/overlay2/c52b8101eb527b96cb5a56c57c7f7a356359f636953580f21c319217c79d357c/diff: /var/lib/docker/overlay2/756ddb8d639da72d3167bcefbd38b3f5e622d560e1cfe1409b6dd1c5fa0f53a9/diff: /var/lib/docker/overlay2/efbb35095fe9fab8f49ad248f0ce252b465878029be64e8fa6496b4f9dab205a/diff" , "MergedDir" : "/var/lib/docker/overlay2/104d9ef20ab91ebd0877ce5c1298be925f4e1fc9ccff01d70f429aeabd539bf8/merged" , "UpperDir" : "/var/lib/docker/overlay2/104d9ef20ab91ebd0877ce5c1298be925f4e1fc9ccff01d70f429aeabd539bf8/diff" , "WorkDir" : "/var/lib/docker/overlay2/104d9ef20ab91ebd0877ce5c1298be925f4e1fc9ccff01d70f429aeabd539bf8/work" }, "Name" : "overlay2" }
LowerDir:底层目录,diff(只是存储不同),包含小型 linux 和装好的软件 MergedDir:合并目录,容器最终的完整工作目录全内容都在合并层,数据卷在容器层产生,所有的增删改都在容器层
UpperDir:上层目录 WorkDir:工作目录(临时层),pid
Images and layers Docker 镜像由一系列层组成,每层代表着 Dockerfile 中的一条指令(Dockerfile文件里面几句话,镜像就有几层),除最后一层外的每一层都是只读的,如以下 Dockerfile:
1 2 3 4 5 FROM ubuntu:15.04 COPY . /app RUN make /app CMD python /app/app.py
该 Dockerfile 包含四个命令,每个命令创建一个层 FROM 语句从 ubuntu:15.04 映像创建一个图层开始 COPY命令从Docker客户端的当前目录添加一些文件 RUN 命令使用 make 命令构建您的应用程序 最后,最后一层指定要在容器中运行的命令 每一层只是与上一层不同的一组,这些层彼此堆叠 创建新容器时,可以在基础层之上添加一个新的可写层,该层通常称为“容器层”,对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此薄可写容器层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@localhost ~] IMAGE CREATED CREATED BY SIZE COMMENT f2f70adc5d89 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c set -x && addgroup --system -… 61.1MB <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 4 days ago /bin/sh -c <missing> 5 days ago /bin/sh -c <missing> 5 days ago /bin/sh -c
Container and layers
容器和镜像之间的主要区别是可写顶层 在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中 删除容器后,可写层也会被删除 基础图像保持不变 因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础映像的访问,但具有自己的数据状态
磁盘容量预估 1 2 3 4 docker ps -s size:用于每个容器的可写层的数据量(在磁盘上) virtual size:容器使用的用于只读图像数据的数据量加上容器的可写图层大小,多个容器可以共享部分或全部只读图像数据,从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层, 因此,不能只对虚拟大小进行总计这高估了总磁盘使用量,可能是一笔不小的数目
镜像如何挑选 1 2 3 busybox:是一个集成了一百多个最常用 Linux 命令和工具的软件,linux 工具里的瑞士军刀 alpine:Alpine 操作系统是一个面向安全的轻型 Linux 发行版经典最小镜像,基于 busybox,功能比 Busybox 完善 slim:docker hub 中有些镜像有 slim 标识,都是瘦身了的镜像,也要优先选择无论是制作镜像还是下载镜像,优先选择 alpine 类型
Copy On Write
写时复制是一种共享和复制文件的策略,可最大程度地提高效率 如果文件或目录位于映像的较低层中,而另一层(包括可写层)需要对其进行读取访问,则它仅使用现有文件 另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改,这样可以将 I/O 和每个后续层的大小最小化
容器如何挂载
每一个容器里面的内容,支持三种挂载方式:
docker 自动在外部创建文件夹自动挂载容器内部指定的文件夹内容(Dockerfile VOLUME 指令的作用)
自己在外部创建文件夹,手动挂载
可以把数据挂载到内存中 –mount 挂载到 linux 宿主机,手动挂载(不用了) -v 可以自动挂载,到 linux 主机或者 docker 自动管理的这一部分区域
Volumes(卷):存储在主机文件系统的一部分中,该文件系统由 Docker 管理(在 Linux 上是 /var/lib/docker/volumes/),非 Docker 进程不应修改文件系统的这一部分,卷是在 Docker 中持久存储数据的最佳方法 Bind mounts(绑定挂载):可以在任何地方存储在主机系统上,它们甚至可能是重要的系统文件或目录,Docker 主机或 Docker 容器上的非 Docker 进程可以随时对其进行修改 tmpfs mounts(临时挂载):仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统
volume(卷) 匿名卷使用
1 2 3 docker run -dP -v :/etc/nginx nginx
具名卷使用
1 2 docker run -dP -v nginx:/etc/nginx nginx
如果将空卷装入存在文件或目录的容器中的目录中,则容器中的内容(复制)到该卷中,如果启动一个容器并指定一个尚不存在的卷,则会创建一个空卷 -v 宿主机绝对路径:Docker容器内部绝对路径:叫挂载,这个有空挂载问题 -v 不以 / 开头的路径:Docker容器内部绝对路径:叫绑定(docker 会自动管理,docker 不会把他当前目录,而把它当前卷)
nginx 测试 html 挂载几种不同情况: 不挂载 效果:访问默认欢迎页 -v /root/html:/usr/share/nginx/html 效果:访问 forbidden -v html:/usr/share/nginx/html:ro 效果:访问默认欢迎页 -v /usr/share/nginx/html 效果:匿名卷(什么都不写也不要加冒号,直接写容器内的目录)
bind mount
如果将绑定安装或非空卷安装到存在某些文件或目录的容器中的目录中,则这些文件或目录会被安装遮盖,就像您将文件保存到 Linux 主机上的 /mnt 中一样,然后 将 USB 驱动器安装到 / mnt 中,在卸载 USB 驱动器之前,/mnt的内容将被 USB 驱动器的内容遮盖,被遮盖的文件不会被删除或更改,但是在安装绑定安装或卷时将无法访问 总结:外部目录覆盖内部容器目录内容,但不是修改,所以谨慎,外部空文件夹挂载方式也会导致容器内部是空文件夹
1 2 docker run -dP -v /my/nginx:/etc/nginx:ro nginx
警惕 bind mount 方式,文件挂载没有在外部准备好内容而导致的容器启动失败问题
1 2 3 4 5 6 docker run -d -P -v nginxconf:/etc/nginx/ -v nginxpage:/usr/share/nginx/html nginx
管理卷 1 2 3 4 docker volume create xxx:创建卷名 docker volume inspect xxx:查询卷详情 docker volume ls : 列出所有卷 docker volume prune: 移除无用卷
docker cp
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-:把容器里面的复制出来 docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH:把外部的复制进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 SRC_PATH 指定为一个文件 DEST_PATH 不存在:文件名为 DEST_PATH ,内容为SRC的内容 DEST_PATH 不存在并且以 / 结尾:报错 DEST_PATH 存在并且是文件:目标文件内容被替换为SRC_PATH的文件内容。 DEST_PATH 存在并且是目录:文件复制到目录内,文件名为SRC_PATH指定的名字 SRC_PATH 指定为一个目录 DEST_PATH 不存在: DEST_PATH 创建文件夹,复制源文件夹内的所有内容 DEST_PATH 存在是文件:报错 DEST_PATH 存在是目录 SRC_PATH 不以 /. 结束:源文件夹复制到目标里面 SRC_PATH 以 /. 结束:源文件夹里面的内容复制到目标里面 自动创建文件夹不会做递归。把父文件夹做好 [root@lfy ~] [root@lfy ~]
Docker 网络 端口映射 1 docker create -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7
容器互联 –link name:alias,name 连接容器的名称,alias 连接的别名
场景:我们无需暴露 mysql 的情况下,让 web 应用使用 mysql
1 2 3 4 5 docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 docker run -d --link mysql01:mysql --name tomcat tomcat:7 docker exec -it tomcat bash cat /etc/hostsping mysql
自定义网络 默认网络原理 Docker 使用 Linux 桥接,在宿主机虚拟一个 Docker 容器网桥(docker0),Docker 启动一个容器时会根据 Docker 网桥的网段分配给容器一个 IP 地址,称为 Container-IP,同时 Docker 网桥是每个容器的默认网关 因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的 Container-IP 直接通信
Docker 容器网络就很好的利用了 Linux 虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫 veth pair),Docker 中的网络接口默认都是虚拟的接口,虚拟接口的优势就是转发效率极高(因为 Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多
网络模式
网络模式
配置
说明
bridge 模式
–net=bridge
默认值,在 Docker 网桥 docker0 上为容器创建新的网络栈
none 模式
–net=none
不配置网络,用户可以稍后进入容器,自行配置
container 模式
–net=container:name/id
容器和另外一个容器共享 Network namespace,kubernetes 中的 pod 就是多个容器共享一个 Network namespace
host 模式
–net=host
容器和宿主机共享 Network namespace
用户自定义
–net=mynet
用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络