Docker
简介
Registries:仓库
Docker 对象(Docker Objects)
Image:镜像,docker可执行文件,包括代码、依赖库、环境变量、配置等
container:容器,镜像运行的实例
network:与宿主机或镜像间的网络通信方式
- bridge:网桥
- host
- none
- overlay
- macvlan
volume:数据卷,与宿主机或镜像间的共享存储方式。如:映射到宿主机的文件夹
底层技术
使用Go实现,利用了如下Linux内核的特性(只能在Linux上运行。Win、Mac本质上通过虚拟化,在Linux虚拟机上运行)
Namespaces:为容器提供系统层面隔离(与宿主机或容器间的隔离)
- 进程隔离:每个容器的进程号都从1开始
- 网络隔离
- 进程间通信隔离
- 文件系统挂载隔离
- 内核隔离
Control Groups:为容器提供硬件层面隔离:对容器进行约束
Union File Systems:镜像和容器的层级存储。当创建或构建一个 Docker 镜像时,Docker 会将每个
RUN
、COPY
、ADD
等操作都分配为单独的层示例:一个基础的 Ubuntu 镜像,之后你安装了一个 Python 环境和一个应用
# 从基础镜像开始 FROM ubuntu:20.04 # 更新并安装 Python RUN apt-get update && apt-get install -y python3 # 将应用程序文件添加到镜像中 COPY myapp.py /app/myapp.py # 设置容器启动时执行的命令 CMD ["python3", "/app/myapp.py"]
- 基础Ubuntu镜像:层1
- python:层2
- 安装应用:层3
每一层只记录变更,底层的 Ubuntu 镜像在多个容器之间共享,而每个容器只会记录自己的差异
分层的优势
- 效率:通过只保存差异层而不是复制整个文件
- 快速启动:容器能够快速启动,因为它们仅仅是联合的不同层,而不是从头开始复制所有文件
- 便于管理:Docker 镜像的层级结构使得每次镜像的构建都可以只包含差异,而不必重复前面已有的部分
容器格式(Contianer Format):通过将三个特性的内容组合成package。Docker通过控制package的三个特性的内容,实现对容器的创建与管理
镜像
搜索
docker search [OPTIONS] TERM
-f, --filter filter 根据提供的格式筛选结果
--format string 利用Go语言的format格式化输出结果
--limit int 展示最大的结果数,默认25个
--no-trunc 内容全部显示
E.g.
docker seach -f is-official=true ubuntu
查看本地
docker images (/ docker image ls) [OPTIONS] [REPOSITORY[:TAG]]
-a, --all 展示所有(默认显示隐藏底层的镜像)
--no-trunc 不缩略显示
-q, --quiet 只显示镜像ID
E.g.
docker images centos:latest
拉取
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
-a, --all-tags 下载所有符合给定tag的镜像
删除
docker rmi (/ docker image rm) [OPTIONS] IMAGE [IMAGE...]
-f, --force 强制删除
E.g. 使用ID对镜像进行删除
docker images # REPOSITORY TAG IMAGE ID CREATED SIZE # centos latest 2zoi3uds09ad 2 days ago 200MB # ubuntu latest j89fashd101k 3 days ago 100MB docker image rm 2zoi j89f
保存备份
docker save [OPTIONS] IMAGE [IMAGE...]
一个或多个镜像打包-o, --output string 指定写入的文件名和路径
E.g.
docker save -o output.tar centos ubuntu
导入备份
docker load [OPTIONS]
-i, --input string 指定要打入的文件,如没有指定,默认是STDIN
-q, --quiet 不打印导入过程信息
重命名
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
修改镜像的name与tag信息E.g.
docker tag 2zoi customname:customtag
检视
docker (image) inspect [OPTIONS] IMAGE [IMAGE...]
查看镜像详情-f, --format string 利用特定Go语言的format格式输出结果
E.g.
# 输出信息json docker image inspect centos # 输出信息json中Id字段的内容 docker image inspect -f "{{json .Id}}" centos # 数组信息json中Created的内容 docker image inspect -f "{{json .Created}}" centos
历史(历史分层)信息
docker history [OPTIONS] image
-H, --human 将创建时间、大小进行优化打印(默认为true)
-q, --quiet 只显示镜像ID
--no-trunc 缩略显示
容器
轻量、可移植、将应用打包的技术。镜像运行后,产生的对象就是容器。相当于镜像运行后的实例。
类比虚拟机
- 相同点
- 共享物理资源
- 生命周期类似
- 内部可安装应用
- 创建完成后物理存储在宿主机上(路径 /var/lib/docker/containers )
- 不同点
- 虚拟机是完整独立的操作系统,容器是运行在宿主内核之上,只从bins/libs开始独立
- 比虚拟机更加轻量
生命周期
相关操作
创建
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
利用镜像创建出一个Created 状态的待启动容器-t, --tty 分配一个伪TTY,也就是分配虚拟终端
-i, --interactive 即使没有连接,也要保持STDIN打开
--name 为容器起名,如果没有指定将会随机产生一个名称
[COMMAND]
: 容器启动后,需要在容器中执行的命令,如ps、ls 等命令[ARG...]
: 执行 COMMAND 时需要提供的一些参数,如ps 命令的 aux、ls命令的-a等等
E.g.
docker create --name custom-container centos ps -A # 启动时执行 /bin/bash 常搭配-t使用 docker create -it --name custom-container2 centos /bin/bash
启动
docker start [OPTIONS] CONTAINER [CONTAINER...]
将创建(created)或关闭(exited)状态的容器启动-a, --attach 将当前shell的 STDOUT/STDERR 连接到容器上
-i, --interactive 将当前shell的 STDIN连接到容器上
E.g.
# 查看列表 docker ps -a # 使用容器ID选中STATUS是Created的容器启动, 并将当前终端连接到容器 docker start -a 2zoi
运行
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
-t, --tty 分配一个伪TTY,也就是分配虚拟终端
-i, --interactive 即使没有连接,也要保持STDIN打开
--name 为容器起名,如果没有指定将会随机产生一个名称
-d, --detach 在后台运行容器并打印出容器ID
--rm 当容器退出运行后,自动删除容器
[COMMAND]
: 容器启动后,需要在容器中执行的命令,如ps、ls 等命令[ARG...]
: 执行 COMMAND 时需要提供的一些参数,如ps 命令的 aux、ls命令的-a等等
P.S.
docker run 相当于 docker create + docker start –a 前台模式,自动连接命令行
docker run -d 相当于 docker create + docker start 后台模式,不连接命令行
E.g.
# 执行 ps -A 命令来列出所有的进程,没有-d,执行完 ps 后自动进入 exited 状态 docker run centos ps -A # std输出容器id,ps -A 在后台执行,不会直接看到结果,执行完 ps 后自动进入 exited 状态 docker run -d centos ps -A # std输出容器id,后台执行完 ps -A 后,由于 --rm 直接删除 docker run --rm -d centos ps -A
暂停
docker pause CONTAINER [CONTAINER...]
将 running 状态的容器切换到 paused 状态恢复
docker unpause CONTAINER [CONTAINER...]
将 paused 状态的容器切换到 running 状态关闭
docker stop [OPTIONS] CONTAINER [CONTAINER...]
将 paused 状态的容器切换到 exited 状态-t, --time int 关闭前,等待的时间,单位秒(默认 10s)
强制关闭
docker kill [OPTIONS] CONTAINER [CONTAINER...]
强制并立即将一个或多个 paused 状态容器切换到 exited 状态-s, --signal string 指定发送给容器的关闭信号 (默认“KILL”信号)
P.S.
stop
使用 SIGTERM 信号,如果设置了-t且超出时间,则使用 SIGKILLkill
默认使用 SIGKILL 信号异常原因会导致容器关闭
重启
docker restart [OPTIONS] CONTAINER [CONTAINER...]
将 running/exited/paused/created 状态容器重新进入 running状态-t, --time int 重启(关闭)前,等待的时间,单位秒(默认 10s)
删除
docker (container) rm [OPTIONS] CONTAINER [CONTAINER...]
-f, --force 强行删除容器(会使用 SIGKILL信号)
-v, --volumes 同时删除绑定在容器上的数据卷
检视
docker container inspect [OPTIONS] CONTAINER [CONTAINER...]
查看容器详情-f, --format string 利用特定Go语言的format格式输出结果
-s, --size 显示总大小
E.g.
# 使用容器ID输出信息json docker container inspect 2zoi # 输出信息json中State字段的内容 docker container inspect -f "{{json .State.Status}}" 2zoi
日志
docker logs [OPTIONS] CONTAINER
容器主进程的输出STDOUT\STDERR--details 显示日志的额外信息
-f, --follow 动态跟踪显示日志信息
--since string 只显示某事时间节点之后的
--tail string 显示倒数的行数(默认全部)
-t, --timestamps 显示timestamps时间
--until string 只显示某事时间节点之前的
重命名
docker rename CONTAINER NEW_NAME
连接
docker attach [OPTIONS] CONTAINER
将当前终端的标准输入输出流连接到容器内--no-stdin 不绑定STDIN
执行命令
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
容器中允许一个命令-d, --detach 后台运行命令
-i, --interactive 即使没连接容器,也将当前的STDIN绑定上
-t, --tty 分配一个虚拟终端
-w, --workdir string 指定在容器中的工作目录
-e, --env list 设置容器中运行时的环境变量
容器与镜像的关系
提交
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
根据容器生成一个新的镜像-a, --author string 作者
-c, --change list 为创建的镜像加入Dockerfile命令
-m, --message string 提交信息,类似git commit -m
-p, --pause 提交时暂停容器 (default true)
E.g.
# 启动一个容器 docker run --rm -dit centos bash # 通过容器ID为容器安装软件 docker exec -d 2zoi yum -y install net-tools # 通过容器ID提交生成一个新镜像 docker commit -m 'install net-tools' 2zoi centos-net-tools:latest # 查看镜像列表,可发现刚刚生成的镜像 docker images
导出
docker export [OPTIONS] CONTAINER
将容器当前的文件系统导出成一个tar文件-o, --output string 指定写入的文件,默认是STDOUT
导入
docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
从一个tar文件中导入内容创建一个镜像-c, --change list 为创建的镜像加入Dockerfile命令
-m, --message string 导入时,添加提交信息
P.S.:
commit
与import
构建出镜像的区别使用
history
查看分层信息可查出区别commit
生成的镜像拥有多个层。保留了原本镜像的分层信息,后续操作在其上构建了新层 (保留历史信息)import
只有一个层。将原本镜像的分层与操作后的内容合并为一个层 (无历史信息,创建全新)
使用
inspect
查看详细信息中的 config 字段 与 RootFS 中的 Layerscommit
的镜像会保留并更新原始镜像的 config 数据,Layers 会保留并新增层import
会丢失 config 字段数据,
生成镜像的层级:inspect
详细信息 RootFS 中的 Layers 数组,按照 history
层级从底向上, 记录文件发生变动的层(即:只有 history
的层级中,文件发生变动的层才会在 RootFS 中的 Layers 数组里存在)。镜像中的每一层都是只读的
容器的层级:create
/run
会在镜像顶层的只读层上,再创建一个可读写层(联合文件系统,下方所有层的叠加,访问时访问的是前面所有层叠加的结果)。在执行 commit
之后(容器变成镜像),顶层的可读写层会转换成只读层
网络
容器与宿主,容器间的网络默认是隔离的
网络驱动模式
bridge network 模式(网桥):默认的网络模式。类似虚拟机的nat模式
宿主机上需要单独的bridge网卡(新建bridge网络后,通过ifconfig可查看该网卡),如默认docker默认创建的docker0
容器之间、容器与主机之间的网络通信,是借助为每一个容器生成的一对veth pair虚拟网络设备对,进行通信的。一个在容器上,另一个在宿主机上
每创建一个基于bridge网络的容器,都会自动在宿主机上创建一个veth...虚拟网络设备(ifconfig可查看该虚拟网卡)
外部无法直接访问容器。需要建立 端口映射 才能访问
-P, --publish-all 将容器内部所有暴露端口进行随机映射,
docker ps -a
查看映射情况-p, --publish list 手动指定端口映射 [HOST_IP]:[HOST_PORT]:CONTAINER_PORT
容器借由veth虚拟设备通过如docker0这种bridge网络设备进行通信
每一容器具有单独的IP
容器间通信: 使用同一个bridge,可使用
http://容器名:暴露端口
进行访问
host network 模式(主机):容器与宿主机之间的网络无隔离,即容器直接使用宿主机网络,容器的IP就是宿主机的IP
容器完全共享宿主机的网络。网络没有隔离。宿主机的网络就是容器的网络
容器、主机上的应用所使用的端口不能重复。例如:如果宿主机已经占用了8090端口,那么任何一个host模式的容器都不可以使用8090端口了;反之同理
外部可以直接使用宿主机IP+容器端口端口访问对应容器,不需要端口映射。此时启动容器与启动本地应用的感知一样
None network 模式:容器禁用所有网络。高度定制网络时使用该模式自行安装与配置
Overlay network 模式(覆盖网络): 利用VXLAN实现的bridge模式。常用于不同主机内容器的通信(虽然前两种也能实现,但多容器时,bridge容器IP过多且端口映射复杂,host容易端口占用且网卡压力大)
在容器内网络层封装数据包时,额外多封装了一个IP头部。而后将报文提交到宿主机
宿主机将报文作为VXLAN,再次封装,传输层增加了UDP头,网络层增加IP头,以此类推。而后进行发送
Macvlan network 模式:容器具备Mac地址,使其显示为网络上的物理设备。宿主机充当交换机,采用mac地址表,根据mac地址进行转发。容器对外相当于一台独立的设备
Container network 模式。容器使用其他容器的网络,来实现间的网络不隔离。容器1使用host,容器2使用Container则也为host;容器1为bridge,容器2使用Container则会与容器1同IP(使用容器1的veth pair)。若容器暴露相同端口,将由于端口占用而容器启动失败
P.S.:host模式性能最好,overlay需要再次封装,性能低于bridge
网络管理命令
查看
docker network ls [OPTIONS]
查看已经建立的网络对象-f, --filter filter 过滤条件(如 'driver=bridge’)
--format string 格式化打印结果
--no-trunc 不缩略显示
-q, --quiet 只显示网络对象的ID
E.g.
docker network ls -f 'driver=host'
查看 host 模式的网络对象列表创建
docker network create [OPTIONS] NETWORK
创建网络对象-d, --driver string 指定网络的驱动(默认 "bridge")
--subnet strings 指定子网网段(如192.168.0.0/16、172.88.0.0/24)
--ip-range strings 执行容器的IP范围,格式同subnet参数(subnet子集)
--gateway strings 子网的IPv4 or IPv6网关,如(192.168.0.1)
P.S.
- host和none模式网络对象只能存在一个
- docker自带的overlay 网络创建依赖于docker swarm(集群负载均衡)服务
- 192.168.0.0/16(前16位固定,0.0-255.255) 192.168.8.0/24(0-255)
E.g. 定制化一个bridge类型网络
# 定制化网段 ,ip范围,网关地址 docker network create --subnet 172.168.0.0/16 --ip-range 172.168.50.0/24 --gateway 172.168.50.1 my-bridge
删除
docker network rm NETWORK [NETWORK...]
支持name或id检视
docker (network) inspect [OPTIONS] NETWORK [NETWORK...]
查看一个或多个网络的详细信息-f, --format string 根据format输出结果
使用
docker run/create --network NETWORK
容器默认使用bridge网络E.g.
# 使用自创建的网络启动FastDFS中的tracker服务, 端口映射 宿主机端口:容器端口 docker run -d -p :22122:22122 --name tracker --net=my-net -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker # 使用Container模式 --network container:CONTAINER docker run
连接与断开
docker network connect/(disconnect) [OPTIONS] NETWORK CONTAINER
host与overlay网络无法连接。none可以连接。none与bridge不能共存。overlay不能与其他模式共存
数据卷
为了解决容器内文件的存储与访问时产生的各种问题,引入了数据卷机制。
数据卷存在于宿主机中,与容器隔离
可利用数据卷解决容器与宿主,和容器间的数据共享问题
容器启动初始化时,如果容器使用的镜像包含了数据,这些数据会拷贝到数据卷中
容器对数据卷的修改是实时的
数据卷变化不会影响镜像。数据卷独立与联合文件系统,镜像基于联合文件系统
挂载方式
在执行 docker create / run
时,使用 OPTION 声明数据卷挂载方式(声明路径必须是绝对路径)
bind mounts:将宿主机上的一个文件或目录被挂载到容器上(目录映射)
方法1: -v, --volume参数
-v <宿主机文件或文件夹路径>:<容器中的文件或者文件夹路径>
自动在宿主机创建文件夹方法2:
--mount type=bind,src=<宿主机文件或文件夹路径>,dst=<容器中的文件或者文件夹路径>
要求宿主机声明路径必须存在volumes:数据卷对象在宿主机的映射, docker volume 统一管理,可理解为bind方式基础上做了额外封装
方法1: -v, --volume参数
-v VOLUME-NAME:<容器中的文件或者文件夹路径>
名称不存在的volume对象会自动创建方法2:
--mount type=volume,src=VOLUME-NAME,dst=<容器中的文件或者文件夹路径>
名称不存在的volume对象会自动创建volume对象管理
# 查看对象列表 docker volume ls # 创建 docker volume create VOLUMENAME # 检视 - 可查看该数据卷的本地目录 docker volume inspect VOLUMENAME # 删除未使用的对象 docker volume prune # 删除 docker volume rm VOLUMENAME[..VOLUMENAME]
tmpfs mounts:tmpfs 是一种基于内存的临时文件系统。tmpfs mounts 数据不会存储在磁盘上,而是宿主机的内存
--mount type=tmpfs, dst=PATH
使用其他容器的数据卷方式
--volumes from CONTAINER
数据卷挂载注意点
Docker的数据卷更多会是使用volumes方式
- 使用bind方式,通过
--mount
创建时可能会出现异常 - 数据卷对象可以使用
docker volume
进行管理
使用时需注意:
- 如果挂载一个空的数据卷到容器中的一个非空目录中,那么这个目录下的文件会被复制到数据卷中(数据卷的初始化)
- 如果挂载一个非空的数据卷到容器中的一个目录中,那么容器中的目录中会显示数据卷中的数据。如果原来容器中的目录中有数据,那么这些原始数据会被隐藏掉(已存在数据卷的优先)
仓库
存放镜像,并可使用pull下载镜像的环境 (DockerFile 使用 cloud 可以比仓库更便捷的构建镜像)
私有仓库相对于直接copy压缩包,更易于版本控制
仓库搭建
安装docker
获取创建仓库镜像
docker pull registry
启动仓库(内部会有http服务)
docker run -d -ti --restart always\ # 跟随docker服务一起重启 --name my-registry\ -p 8000:5000\ # 内部服务在5000端口,映射到宿主的8000上 - 本机IP:8000即为仓库地址 -v /my-registry/registry:/var/lib/registry\ # 镜像存储在本地 registry # 镜像名称
查看仓库镜像列表
curl <服务器IP>:8000/v2/_catalog
上传与下载
上传
重命名需要上传的镜像
docker tag IMAGE <服务器IP>:<端口>/IMAGE_NAME
(注意:此处需要注明仓库地址)上传重命名的镜像
docker push <服务器IP>:<端口>/centos
(注意:默认情况下为https服务,会出现异常)
P.S.:处理HTTPS异常:将该仓库请求变更为http请求 修改docker配置文件 /etc/docker/daemon.json (mac地址 ~/.docker/daemon.json),添加内容解包添加
json { "insecure-registries":["<服务器IP>:<端口>"] }
此时使用
docker image ls
可查看到镜像<IP>:<端口>/镜像名
通过请求
curl <服务器IP>:8000/v2/_catalog
可查看到上传完成的镜像下载
docker pull <服务器IP>:<端口>/IMAGE_NAME
(注意:此处需要注明仓库地址,不表明默认从官方 docker hub 上拉取)
仓库的认证配置
- 仓库认证
- STEP0: 删除先前创建的无认证的仓库容器
docker rm -f my-registry
P.S. 如需删除镜像还需删除数据卷,也可直接映射新仓库来继承其镜像 - 创建存放认证用户名和密码的文件路径
mkdir /my-registry/auth -p
- 创建密码验证文件。USERNAME和PASSWORD替换为设置的用户名和密码
# 运行容器内的 htpasswd 来设置账号密码 写入指定路径 docker run --entrypoint htpasswd registry -Bbn USERNAME PASSWORD > /my-registry/auth/htpasswd
- 重新启动仓库镜像
docker run -d -p 8000:5000 --restart=always --name docker-registry \ -v /my-registry/registry:/var/lib/registry \ # 映射镜像文件夹 -v /my-registry/auth:/auth \ # 映射密码文件夹 -e "REGISTRY_AUTH=htpasswd" \ # 环境变量 -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ # 环境变量:认证方式 -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \ # 环境变量:密钥路径 registry
- STEP0: 删除先前创建的无认证的仓库容器
- 上传与下载
- 登录
docker login -u <username> -p <password> <IP>:<端口>
- 执行上传 下载操作 (与不带认证的私有仓库操作一致)
- 退出登录
docker logout <IP>:<端口>
- 登录
DockerFile
包含
docker build
命令的文本文件,通常名称为 Dockerfile ,运行时会依次执行其中命令创建一个新的镜像
运行:docker build <Dockerfile文件所在文件夹路径> -t NAME:TAG
P.S. 如果文件名不为 Dockerfile ,需使用 -f
指明文件名
官方镜像的dockerfile地址 https://github.com/docker-library/docs/,点击任意仓库文件夹后,打开README中Dockerfile超链接即可查看
构建
Dockerfile的构建(镜像 -> 临时容器 -> 镜像)类似于 docker commit
操作(容器 -> 镜像),其中 每个 FROM
外的命令会在原镜像上新增一层(dockerfile中的命令对应着 docker history
中的一层,FROM
对应着基础镜像的层级,可能为多个)
构建缓存:在二次构建时,若dockerfile未发生变动,则直接使用第一次构建的缓存(docker images
中存在的上一次构建的镜像)。若dockerfile发生变动,则变动处及后方都是重新执行(变动处开始不使用缓存,之前位置使用上次构建的镜像)。 在更新Dockerfile时,一般在文件末尾添加,不会变动到之前的内容,以防止二次初始化造成数据丢失
命令
dockerfile 命令官方文档
FROM
: 指定基础镜像。在指定镜像上构建新的镜像RUN
: 构建镜像过程(docker build
过程)中需要执行的命令。可以有多条。在docker build
时调用RUN
中的命令# shell方式运行命令(子进程):RUN + 命令 (推荐) RUN echo 'test' # exec方式运行命令(当前进程):RUN + 命令数组 RUN ["yum", "install", "-y", "net-tools"]
说明: linux执行方式
shell方式执行
/bin/sh -c echo "test"
,此时会开辟子进程执行,当前窗口无法看到输出exec方式执行
exec echo "test"
,此时会先kill当前在当前窗口执行,且执行完成后终端被结束(dockerfile中的环境变量会受影响 / 可使用exec sh -c
避免)
CMD
: 添加启动容器时需要执行的命令(docker inspect
检视镜像CMD信息 Config.Cmd 字段 )。多条只有最后一条生效。可以在启动容器时被覆盖和修改(当执行docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
时,没有提供 COMMAND ,将默认执行 Config.Cmd 中的内容)# 命令(子进程)将以shell方式运行:CMD + 命令 CMD echo 'test' # 命令(当前进程)将exec方式运行:CMD + 命令数组 (推荐,通常使用CMD启动程序主程序,绑定容器生命周期) CMD ["yum", "install", "-y", "net-tools"] # 后续内容作为 ENTRYPOINT 命令的参数 CMD ["params1", "params2"]
ENTRYPOINT
: 同CMD,但这个一定会被执行,不会被覆盖修改# 命令(子进程)将以shell方式运行:ENTRYPOINT + 命令 ENTRYPOINT echo 'test' # 命令(当前进程)将exec方式运行:ENTRYPOINT + 命令数组 (推荐,通常使用ENTRYPOINT启动程序主程序,绑定容器生命周期) ENTRYPOINT ["yum", "install", "-y", "net-tools"]
LABEL
: 为镜像添加对应的数据。docker inspect
检视镜像Labels信息 Config.Labels 字段。版本、名称、协议等说明信息MAINTAINER
: 表明镜像的作者。将被遗弃,被LABEL代替。docker inspect
检视镜像Author信息 Author 字段EXPOSE
: 设置对外暴露的端口。docker inspect
检视镜像暴露端口 Config.ExposedPorts 字段# 暴露端口(默认TCP) EXPOSE 6379 # 指定端口协议 EXPOSE 80/tcp EXPOSE 80/udp
ENV
: 设置执行命令时的环境变量,并且在构建完成后,仍然生效ARG
: 设置只在构建过程(docker build
过程 )中使用的环境变量(如docker build
时传入的参数值),构建完成后,将消失使用arg动态接收username值
docker build --build-arg username=what_user .
FROM busybox USER ${username:-some_user} ARG username USER $username
ADD
: 将本地文件或目录拷贝到镜像的文件系统中。能解压特定格式文件,能将URL作为要拷贝的文件COPY
: 将本地文件或目录拷贝到镜像的文件系统中。VOLUME
: 添加数据卷(自动创建数据卷)如:docker run -dit redis
,由于其dockerfile内设置了VOLUME
,将自动创建数据,等效为docker run -dit -v /data redis
USER
: 指定以哪个用户的名义执行RUN, CMD 和ENTRYPOINT等命令WORKDIR
: 设置工作目录。明确RUN
的工作路径ONBUILD
: 如果制作的镜像被另一个Dockerfile使用(创建出的镜像被另一个dockerfileFROM
),将在那里被执行Docekrfile命令STOPSIGNAL
: 设置容器退出时发出的关闭信号。kill -l
可查看全部关闭信号HEALTHCHECK
: 设置容器状态检查。设置指定时间间隔执行指定命令用于检测运行状态并记录SHELL
: 更改执行shell命令的程序。Linux的默认shell是["/bin/sh", "-c"],Windows的是["cmd", "/S", "/C"]。例如SHELL ["powershell", "-command"]
切换到powershellFROM microsoft/windowsservercore # Executed as cmd /S /C echo default RUN echo default # Executed as cmd /S /C powershell -command Write-Host default RUN powershell -command Write-Host default # 切换到 powershell SHELL ["powershell", "-command"] RUN Write-Host hello # 切换会cmd SHELL ["cmd", "/S", "/C"] RUN echo hello
Docker Compose
docker compose用于解决容器固定化流程启动、多容器启动等容器启动的问题。通常名称为 docker-compose.yaml
Docker Compose File 配置启动文件,配置镜像/Dockerfile,网络等信息
Docker Compose Cli - 与docker cli类型
与Dockerfile的不同: Dockerfile用于构造镜像。镜像在运行成容器时,需要使用cli管理其网络,数据卷,生命周期(容器起停)。Docker Compose可以对这些内容进行管理(与Docker cli类型,但可一次管理多个)
安装
windows 与 mac 系统的 docker desktop 自带docker compose
Linux 系统需要额外安装
sudo curl -L https://github.com/docker/compose/releases/download/版本/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
确定是否安装成功
docker-compose --version
docker compose file
docker compose file 采用yaml进行配置。语法目前共3个大版本,且向后兼容(新版本docker引擎支持老版本语言)
- 常用配置项
version(必须声明): 指定Docker Compose File版本号
services(必须声明): 定义多个服务并配置启动参数
build: 容器使用 dockerfile 启动时,dockerfile 相关内容
version: '3' services: webapp: # 服务名 build: context: ./dir # dockerfile所在路径 dockerfile: Dockerfile-alternate # dockerfile名称 args: buildno: 1 # dockerfile ARG参数
command: 重写
inspect
镜像 CMD参数(一些镜像默认配置了CMD)container_name: 容器名称
deploy: 多台主机的多容器部署使用
depends_on: 容器间的依赖关系,控制容器启动顺序
version: '3' services: webapp: # 服务名 build: . # 使用dockerfile启动,声明 dockerfile 路径 depends_on: # 声明该服务依赖的其他容器,在下列容器启动后再启动 - redis - db redis: image: redis # 使用镜像启动,声明镜像名 db: image: postgres # 使用镜像启动,声明镜像名
entrypoint: 重写
inspect
镜像 ENTRYPOINT 参数重写(一些镜像默认配置了ENTRYPOINT)env_file: 环境变量文件
env_file: .env # 支持单文件导入 env_file: # 支持多文件导入 - ./common.env - ./apps/web.env - ./opt/secrets.env
environment: 环境变量
environment: - FLASK_ENV=development - SECRET=****
expose: 容器暴露端口,此配置项不做映射(一些镜像默认绑定了数据卷)
expose: - "5000" - "8000"
ports: 容器暴露端口,并进行映射(一些镜像默认绑定了数据卷)
ports: - "3000" # 暴露3000 做随机映射 - "3000-3005" # 暴露3000-3005 做随机映射 - "8000:8000" # 8000映射8000
image: 同级含有
build
,则表示dockerfile构建出的镜像的名称。否则,表示启动指定名称的镜像volumes: 绑定数据卷
P.S.
- 一些镜像默认绑定了数据卷,不进行变动时无需再此处再次声明
- 在不进行声明时,更新 compose file 后使用
up
而触发容器重构,不会更新数据卷。但down
会更新数据卷(可能导致数据丢失)
version: "3.2" services: web: image: nginx:alpine volumes: # 引用声明的数据卷 - type: volume source: mydata target: /data volume: nocopy: true - type: bind source: ./static target: /opt/app/static db: image: postgres:latest volumes: - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock" - "dbdata:/var/lib/postgresql/data" volumes: # 数据卷声明 mydata: # 数据卷名1 dbdata: # 数据卷名2
restart: 重启规则
restart: "no" restart: always restart: on-failure restart: unless-stopped
volumes: 声明或创建在多个服务中共同使用的数据卷对象
networks: 定义在多个服务中共同使用的网络对象
P.S.: compose中多服务的相互访问:使用同一个网络,访问时直接使用 http://<服务名>:<端口>` 即可
configs: 声明将在本服务中要使用的一些配置文件(多镜像共用的全局配置文件)
secrets: 声明将在本服务中要使用的一些秘钥、密码文件(密码等敏感信息,写入文件,用该字段导入)
x-***: 自定义配置。主要用于复用相同的配置(多镜像在compose file中使用相同的配置,用此进行抽象)
案例
flask项目环境搭建
flask代码位于服务器 /code/flaskapp 文件夹内,并包含 requirements.txt 依赖包说明文件
编写 dockerfile
# 获取python镜像 查看docker library官方python镜像,可选alpine(linux内核轻量版本) FROM python:apline3.6 # 服务端代码添加到镜像 ADD/COPY 本地文件夹到容器内指定文件夹 COPY ./flask-web-code /code # 安装服务端依赖环境 WORKDIR /code # 容器内cd RUN pip install -r requirements.txt # 运行 - 设置CMD 执行服务程序 推荐exec方式 CMD ["python", "app.py"]
(可选)构建镜像
docker build . -t my-flask-img
(此处不进行,在 compose 加载 dockerfile 时也会自动构建)编写 docker compose file docker-compose.yaml
version: "3.6" service: # flask服务 flask-web: build: . # dockerfile位于当前文件夹 ports: - "5000:5000" container_name: flask-web # 可选,指定运行后的容器名称 networks: # 可选,引用声明的网络,不指定默认创建一个桥接网络用于services内所有容器,实现容器间通信 - web # redis服务 redis: image: redis # 该镜像已自己暴露6379 # 如需宿主机以外的服务访问,需要配置端口映射:ports # 此处只需要flask服务访问,不做端口映射 container_name: redis # 可选,指定运行后的容器名称 networks: # 可选,引用声明的网络,不指定默认创建一个桥接网络用于services内所有容器,实现容器间通信 - web volumes: # 可选。防止 compose down 导致的镜像数据卷重置 - redis-data:/data # 数据卷名: 路径 (路径通过inspect查看) # 可选 声明网络,供services引用,services内不指定默认创建一个桥接网络用于services内所有容器,实现容器间通信 networks: web: # 网络名 driver: "bridge" # 可选 - 为了避免 compose down 时的数据丢失,一般都会声明 volumes: redis-data: # 数据卷名称 driver: "local"
检测当前文件夹内的 compose file 是否存在问题
docker-compose config
启动当前文件夹内的 compose file 。更新 compose file 后,执行
up
会重新运行- 阻塞式启动
docker-compose up
- 守护进程式启动
docker-compose up -d
- 阻塞式启动
停止
docker-compose kill
docker-compose pause
docker-compose stop
docker-compose down
会删除容器与网络,并影响数据卷( compose 未声明数据卷情况下,若镜像声明了数据卷,会自动创建,down
后该数据卷不再使用,下次启动时新创建数据卷。可能引起数据丢失,一般是不再使用此 compose 时才使用)
单宿主机的ELK系统
日志数据处理分析平台,ES,Logstash,Kibana。Logstash(运输日志,将日志通过管道传输到对应服务,端口5000)获取日志存储到ES(存储日志,存储Logstash传输内容,端口9200),Kibana(呈现,提供一个前端页面,端口5601)提供页面对ES数据进行查看与管理
注意点
- ES,Logstash 多开提高吞吐量
- Logstash暴露5000端口用于写入,并需连接9200端口的ES
- ES需要暴露9200端口写入
- Kibana需要暴露5601端口用于访问,并需要连接9200端口
- 获取镜像 在es官网点击复制按钮,复制拉取 ES,Logstash,Kibana 镜像的命令
# 指定版本则使用此方式: elasticsearch:x.x.x docker pull docker.elastic.co/elasticsearch/elasticsearch:sha256-xxx docker pull docker.elastic.co/kibana/kibana:sha256-xxx docker pull docker.elastic.co/logstash/logstash-oss:sha256-xxx
- 在es官网点击打开按钮,查看运行es镜像的文档。此处已6.2版本为例,文档中的 compose file 可运行一个es集群
version: '2.2' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:6.2.4 container_name: elasticsearch environment: - cluster.name=docker-cluster # 集群名,确保两个es在同一集群 - bootstrap.memory_lock=true # 内存锁,防止内存无限消耗 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # 内存最小与最大范围 ulimits: # 内存锁设置权限 - 与内存锁一起出现,不然会报错 memlock: soft: -1 hard: -1 volumes: # 使用数据卷保证 down 后数据不会丢失 - esdata1:/usr/share/elasticsearch/data ports: # 端口映射只打开一个,两个都可以访问到 - 9200:9200 networks: # 设置同一个网络 - esnet elasticsearch2: image: docker.elastic.co/elasticsearch/elasticsearch:6.2.4 container_name: elasticsearch2 environment: - cluster.name=docker-cluster # 集群名,确保两个es在同一集群 - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - "discovery.zen.ping.unicast.hosts=elasticsearch" # 设置host与1的一致,以达成集群效果。此处为1的默认值 ulimits: memlock: soft: -1 hard: -1 volumes: # 使用数据卷保证 down 后数据不会丢失 - esdata2:/usr/share/elasticsearch/data networks: - esnet volumes: esdata1: driver: local esdata2: driver: local networks: esnet:
- 测试ES服务
# 启动 - 失败会自动重试 docker-compose up -d # 查看是否运行成功 docker ps -a # 查看compose日志 需在 compose file 目录中执行 docker-compose logs # 可以查看启动过程中可能出现的异常 # 测试请求ES curl 127.0.0.1:9200
P.S.: 可能出现的问题
max virtual memory areas vm.max_map_count [65536] is too low, increase to at least [262144]
根据文档,需要修改系统配置
# linux /etc/sysctl.conf sysctl -w vm.max_map_count=262144 # mac sysctl -w vm.max_map_count=262144
- 查看Logstash的配置。此处以官方文档6.2为例,确认配置 pipeline(日志的来源与去向)及 settings(Logstash配置项)是否需要修改。由于默认es请求地址
http://elasticsearch:9200
与实际配置的内网访问要求一致(http://<服务名>:<端口>),因此无需修改(其中x-pack相关配置需要付费使用,可进行异常监控与邮件通知等功能)
- 查看Logstash的配置。此处以官方文档6.2为例,确认配置 pipeline(日志的来源与去向)及 settings(Logstash配置项)是否需要修改。由于默认es请求地址
- compose file 中配置 Logstash 服务。完成后使用
docker-compose config
检测完成后。使用dicker-compose up -d
启动
...重复内容省略... services: ...重复内容省略... logstash: image: docker.elastic.co/logstash/logstash:6.2.4 container_name: logstash environment: - "LS_JAVA_OPTS=-Xms256m -Xmx256m" # 设置内存使用量 networks: - esnet # 与es使用同一网络以保证可以相互访问 depends_on: # 保证es服务启动后再启动该服务 - elasticsearch - elasticsearch2 ...重复内容省略...
- compose file 中配置 Logstash 服务。完成后使用
- 查看Kibana配置。确认官方文档6.2中的默认配置是否需要修改。由于默认es请求地址
http://elasticsearch:9200
与实际配置的内网访问要求一致(http://<服务名>:<端口>),因此无需修改
- 查看Kibana配置。确认官方文档6.2中的默认配置是否需要修改。由于默认es请求地址
- compose file 中配置 Kibana 服务
...重复内容省略... services: ...重复内容省略... kibana: image: docker.elastic.co/kibana/kibana:6.2.4 container_name: kibana ports: - "5601:5601" # 端口映射用于内部web服务在外部可进行启动 networks: - esnet # 与es使用同一网络以保证可以相互访问 depends_on: # 保证es服务启动后再启动该服务 - elasticsearch - elasticsearch2 ...重复内容省略...
- 启动ELK
# 检测配置文件是否有语法问题 docker-compose config # 启动 docker-compose up -d # 查看容器情况 docker ps -a # 查看日志 docker-compose log # 查看ELK是否正常 curl 127.0.0.1:9200 # 查看es curl 127.0.0.1:5601 # 查看kibana,可通过浏览器访问前端页面
P.S. logstash集群配置
...重复内容省略...
services:
...重复内容省略...
logstash2: # 更改服务名称防止重复
image: docker.elastic.co/logstash/logstash:6.2.4
container_name: logstash2 # 更改容器名称防止重复
environment:
- "LS_JAVA_OPTS=-Xms256m -Xmx256m"
networks:
- esnet
depends_on:
- elasticsearch
- elasticsearch2
...重复内容省略...
多宿主机的ELK系统
swarm: docker内置的集群管理工具,使用overlay网络,支持负载均衡
此处已两台宿主机为例,通过 swarm 实现容器间通信
swarm的使用
swarm 初始化。宿主机A 执行
docker swarm init
,得到加入 swarm 命令docker swarm join --token SWMTKN-1-******* 10.211.55.7:2377
宿主机A查看当前节点
docker node ls
。宿主机A在初始化时已自动加入ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS(Leader表示主节点) ENGINE VERSION 宿主机A的信息
宿主机B加入。执行 swarm 初始化得到的命令
docker swarm join --token SWMTKN-1-******* 10.211.55.7:2377
宿主机A查看当前节点
docker node ls
。可查看到到两条数据查看网络信息
docker network ls
,可发现 docker_gwbridge 与 ingressID NAME DRIVER SCOPE xxx docker_gwbridge bridge local # 用于容器访问外部网络 xxx ingress overlay swarm # 用于容器间/集群间通信
多宿主机compose 与 单宿主机compose 的不同
使用 swarm 部署跨主机服务,不支持dockerfile,只能使用image镜像。dockerfile构建镜像必须在 compose 外单独执行生成出可用镜像
一个服务可启动多个容器
compose file + docker stack/service (不使用单宿主机的 compose file + compose cli)
部分 compose 配置项在 使用
stack
在 swarm 中无效。如build
/container_name
/ulimits
/depends_on
等使用
docker stack deploy
启动,而不是docker-compose up
compose 必须配置
deploy
,且需要版本 3 或以上。以下为deploy
的常用子配置项,详见官方文档services: frontend: image: example/webapp ports: - "8080:80" deploy: mode: replicated # 默认mode就是replicated / global模式无法指定数量,而是在所有docker节点上都运行一个容器 replicas: 2 # 指定镜像启动多少个容器(所有docker节点上一共运行的容器数量) endpoint_mode: vip
mode
: 支持replicated
(指定启动数量,swarm自行判断容器运行在哪台宿主机,想控制只能是用constraints
) /global
(每个宿主机启动1个)placement
: 设置约束等constraints
容器在满足约束的宿主机上运行,可使用==
或!=
- 在指定 Node ID 的节点上运行容器
node.id==2ivku8v2gvtg4
- 在 Node hostname 不是指定名称的节点上运行容器
node.hostname!=node-2
- 在指定 Node role (manager-leader/worker-非leader) 的节点上运行容器
node.role==manager
- 在指定 Node operating system 的节点上运行容器
node.platform.os==windows
- 在指定 Node architecture 的节点上运行容器
node.platform.arch==x86_64
- 在指定 User-defined node labels 的节点上运行容器
node.labels.security==high
- 在指定 Docker Engine's labels 的节点上运行容器
engine.labels.operatingsystem==ubuntu-24.04
- 在指定 Node ID 的节点上运行容器
resources
: 限制容器使用资源services: frontend: image: example/webapp deploy: resources: limits: cpus: '0.50' memory: 50M pids: 1 reservations: cpus: '0.25' memory: 20M
多宿主机ELK compose file 的编写
deploy 配置
version: '3.6' # deploy 必须 3或以上 services: elasticsearch: ...重复内容省略... deploy: placement: constraints: - node.role == manager # 让此容器在主节点运行 elasticsearch2: ...重复内容省略... deploy: placement: constraints: - node.role == worker # 让此容器在从节点运行 logstash: ...重复内容省略... deploy: replicas: 2 # 启动两台,mode默认为replicated,可省略 logstash2: ...重复内容省略... deploy: replicas: 2 # 启动两台,mode默认为replicated,可省略 kibana: ...重复内容省略... deploy: placement: constraints: # web服务运行一个就够 - node.role == manager # 让此容器在主节点运行
网络配置 - 将使用网络驱动修改为overlay
networks: esnet: driver: "overlay"
移除
ulimits
/container_name
等不支持参数version: '3.6' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:6.2.4 # container_name: elasticsearch # 移除 environment: - cluster.name-docker-cluster - bootstrap.memory_lock=false # 由于ulimits无法使用,设置为false,保证容器正常启动 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # ulimits: # 移除 # memlock: # soft: -1 # hard: -1 volumes: - esdata1:/usr/share/elasticsearch/data ports: - 9200:9200 networks: - esnet deploy: placement: constraint: node.role == manager elasticsearch2: image: docker.elastic.co/elasticsearch/elasticsearch:6.2.4 # container_name: elasticsearch2 # 移除 environment: - cluster.name=docker-cluster - bootstrap.memory_lock=false # 由于ulimits无法使用,设置为false,保证容器正常启动 - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - "discovery.zen.ping.unicast.hosts=elasticsearch" # ulimits: # 移除 # memlock: # soft: -1 # hard: -1 volumes: - esdata2:/usr/share/elasticsearch/data networks: - esnet deploy: placement: constraints: - node.role == worker logstash: image: docer.elastic.co/logstash/logstash:6.2.4 environment: - "LS_JAVA_OPTS=-Xms256m -Xmx256m" # container)name: logstash # 移除 networks: - esnet # depends_on: # 移除 # - elasticsearch # - elasticsearch2 deploy: replicas: 2 logstash2: image: docer.elastic.co/logstash/logstash:6.2.4 environment: - "LS_JAVA_OPTS=-Xms256m -Xmx256m" # container)name: logstash2 # 移除 networks: - esnet # depends_on: # 移除 # - elasticsearch # - elasticsearch2 deploy: replicas: 2 kibana: image: docker.elastic.co/kibana/kibana:6.2.4 # container_name: kibana # 移除 ports: - "5601:5601" networks: - esnet # depends_on: # 移除 # - elasticsearch # - elasticsearch2 volumes: esdata1: driver: local esdata2: driver: local networks: esnet: driver:
检测语法异常
docker-compose config
启动
docker stack deploy -c [compose file] [stack_name]
。使用docker ps -a
查看运行效果docker stack deploy -c docker-compose.yaml elk
查看服务列表
docker service ls
查看指定服务的日志
docker service logs elasticsearch(服务名) [-f (与tail -f类似)]
。(由于容器反复重启失败,stack会自动移除并创建新容器,所以不能查看容器日志docker logs
,而是查看服务日志docker service logs
)删除stack
docker stack rm [stack_name]
。容器会依次关闭并删除,需要一定时间重启,先
stack rm
删除stack,再启动stack deploy -c
并创建stackstackss测试:swarm的端口映射,每个 docker node 的IP都可以访问到对应服务
- ES
curl 127.0.0.1:9200
- kibana 浏览器访问 IP:5601
- ES
Python运行环境
Pycharm连接docker内的python环境
Pycharm通过配置远程docker服务,服务器2375端口连接docker服务(不是容器),在docker中获取镜像,创建python解释器容器
Pycharm配置sftp服务,将本地代码传到docker所在服务器,通过docker数据卷映射到容器
docker服务器修改docker配置文件 /etc/docker/daemon.json (如果没有该文件则手动创建),供pycharm远程连接
{ "hosts": [ "tcp://0.0.0.0:2375" // 在 hosts 中增加该行 "unix:///var/run/docker.sock" // 本地连接(默认),如果 hosts 中没有则手动添加该行 ] }
重启 docker 使配置生效
service docker restart
配置Pycharm添加远程docker容器内的解释器 Preferences -> Python Interpreter -> add... -> Docker -> Server: New... -> TCP socket 填写
tcp://<服务器IP>:2375