外观
Docker
约 2534 字大约 8 分钟
Docker
2024-03-15
原理
Docker容器时基于操作系统级虚拟化技术,容器本身不包含内核,共享宿主机的linux内核。 比如我运行一个node.js的容器,他是基于debian:bullseye-slim镜像,容器内的用户状态环境(文件系统、库、工具)都来自镜像(union file提供),但是所有系统调用(文件操作、进程、网络、资源)都由宿主机的linux内核执行。
容器,一个应用程序。虚拟化的轻量级替代技术,不依赖任何框架或系统,启动更快,便于统一环境,快捷交付部署
镜像:可在容器中运行的,分层念,每一层都是在前一层的基础上进行修改
- 可读层
- 构成Docker镜像的每一次,对应dockerfile中的每一条指令(一条就是一层)(FROM、RUN、COPY)
- 这些层构建完成后不可更改,存放在宿主机的/var/lib/docker/overlay2目录下
- 多个镜像或容器能共享相同的层,节约空间
- 每层记录文件系统的增量变化,通过unionfile合并成视图
容器:基于镜像构造的也是多层,顶层作为容器的存储层
- 可写层
- 也叫容器层,在容器启动时创建,位于镜像只读层的顶部
- 用于存储容器运行时的所有修改(新建、修改、删除文件)
- 临时的,随着容器删除会销毁,只读层保持不变
dockerfile:制作镜像,仓库:上传下载镜像
原理 镜像分层,union file
UnionFileSystem(OverlayFS)
联合挂载将多个只读层和一个可写层合并形成容器的统一文件系统视图
结构:
- 只读层(镜像层):由 Dockerfile 生成,存储镜像的增量变化,共享且不可变,通过OverlayFS合并成文件系统视图。
- 可写层(容器层):容器运行时创建,存储临时修改,使用写时复制和删除标记机制,容器删除后销毁。
- OverlayFS:将只读层和可写层挂载到统一视图,管理文件读取、写入和删除。
关键操作:
- 读取
- 当容器访问文件时(/bin/bash),从可写层逐层往下找
- 如果文件只存在只读层,直接读取。可写层有修改则使用可写层的版本
- 写入
- 修改文件时将文件从只读层复制到可写层(写时复制),然后再可写层修改
- 保持只读层不变,确保镜像不会被修改
- 删除
- 删除时在可写层创建一个删除标记(逻辑删除)
- 实际文件仍存在只读层,没被删除
/var/lib/docker/overlay2/<container-id>/
├── diff/ # 容器的可写层(upper),存储修改的文件
├── lower/ # 只读层(lower),指向镜像的各层
├── merged/ # 合并后的文件系统视图,容器看到的根目录
├── work/ # OverlayFS 的工作目录,用于写时复制的临时存储只读和可写层的运作流程
- 镜像构建(只读层创建)
- dockerfile
- 容器运行(可写层创建与使用)
- 启动容器
- 创建可写层
- UnionFileSystem将只读和可写合并挂载,形成容器的文件系统
- 生命周期
- 容器停止删除后,可写层销毁,只读层不变
- 如果需要持久化,需要挂载或者使用数据卷
- 启动容器
安装
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh镜像
docker search 镜像名
docker pull 镜像名 #默认添加:latest 拉取最新镜像
docker images #查看本地存在的镜像
docker rmi 镜像名Id # 删除镜像,删除容器 容器被镜像引用时,需要先删除容器docker rm 容器
#批量删除镜像
docker rmi `docker images -aq` # 反引号是取出结果
docker image prune #清理残留镜像
#复制容器内的文件
docker cp <containerid>:/containerpath /localpath
#查看镜像详情
docker image inspect 镜像id
#镜像导出
docker image save 镜像名:tag > 保存路径/name.tar # tag可选
#镜像导入
docker image load < 镜像.tar # 导出的自定义名称,不影响导入的名称
docker tag <image_id> <new_name>:<new_tag> # 打包镜像 或者叫修改导入的镜像信息 用于提交或修改镜像信息容器
# 注意:容器内的进程必须处于前台运行状态,否则容器会直接退出
# 如果容器内,什么事也没做,容器也会挂掉
#容器管理
docker ps #查看正在运行的容器
-a: 所有容器(未运行的)
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}"
docker run 镜像名 # 如果镜像不存在,则会去下载该镜像
-d #后台运行 返回id。 #docker run -d 镜像名
-p #宿主机端口:容器端口 -P(大写):随机端口映射
--rm #停止或挂掉后自动删除容器。 #docker run -d --rm 镜像名
--name #指定容器名称
--restart=always #重启策略
docker start 容器id
docker stop 容器id
docker restart 容器id
#restart重启策略:
# - no:不重启
# - always:不论退出原因 始终重启
# - on-failure 只在非零代码退出时重启 可配置重启次数 restart: on-failure:5 5次
# - unless-stopped 除非手动停止 否则总是重启
#注: 代码0退出是指正常运行完成的退出。非零是指任意出错或异常的非正常停止。
# 查看容器日志
docker logs 容器id
-f #实时刷新日志
|tail -5 #查看最新几条
#更新重启策略
docker update --restart=always <container_name_or_id>
# 进入容器空间内
docker exec 容器id bash
-it #交互式操作
#查看端口映射情况
docker port 容器id
#查看容器具体信息
docker container inspect 容器id
#容器资源信息
docker top 容器id
#所有容器的资源占用状态
docker status
#开启容器运行命令
docker run -it 镜像名 命令 #容器运行起来实际相当于一个系统,所以在后面接的命令就可以直接运行 #docker run nginx ls
#删除所有容器
docker rm `docker ps -aq`
#更改挂载卷位置
# 1.停止docker服务
# 2.修改文件 /docker/containers/【container-ID】/config.v2.json MountPoints节点
# 3.重启docker- 容器提交
# 把现有运行容器提交为镜像,将镜像在重新安装就保留了原有容器的配置信息 就是把可写层也提交为只读层
docker commit 容器id 新的镜像名
#eg
docker commit bfbd1 my/vinginx
docker run -it my/vinginx bash "https://registry.docker-cn.com"
容器网络
Bridge(默认模式、桥接)
Docker的默认网络模式,类似分配个容器一个虚拟的"局域网"。把加入这个网络的容器组网,可以通过容器名称相互通讯
Host(主机模式)
直接使用宿主机的网络,使用主机的IP和端口
None(无网络模式)
没有网络连接,完全隔离,适合运行不需要网络的程序或安全性要求高,只需要本地运算的任务
...
| 网络模式 | 隔离性 | 通信方式 | 适用场景 | 端口映射需求 |
|---|---|---|---|---|
| Bridge | 有隔离 | 通过网桥,需端口映射访问外部 | 简单应用、开发测试 | 需要 |
| Host | 无隔离 | 使用宿主机网络,无需映射 | 高性能、简单部署 | 不需要 |
| None | 完全隔离 | 无网络连接 | 离线任务、高安全性 | 不需要 |
| Container | 共享网络 | 与指定容器共享网络 | 紧密协作的容器 | 视情况 |
| Custom Bridge | 有隔离 | 通过名称通信,灵活性高 | 复杂应用、生产环境 | 需要 |
| Overlay | 有隔离 | 跨主机通信 | 分布式系统、Swarm/Kubernetes | 视情况 |
docker network create [网络名] # 默认bridge网络
--network archive --network-alias addrmysql # 运行容器时可添加的参数,网络别名, 可用于容器内部的互相访问,比如mysql host(使用容器名称好像也可以)
#一个容器可以连接到多个网络
docker network connect <网络名> <容器名>
# 1. 检查两个容器的网络配置
docker inspect gitea --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}: {{$v.IPAddress}}{{println}}{{end}}'
docker inspect mysql8 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}: {{$v.IPAddress}}{{println}}{{end}}'
# 2. 检查 mossdb_docker_network 的成员
docker network inspect mossdb_docker_network --format '{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{println}}{{end}}'延时启动docker服务
执行命令:systemctl edit docker.service 然后添加
[Service]
ExecStartPre=/bin/sleep 30 #延时启动30s#重启服务生效
sudo systemctl daemon-reload
sudo systemctl restart docker.service
#查看是否起效
systemctl status docker.service换源
#/etc/docker/daemon.json
...提示
pull images速度太慢 也有可能跟dns有关 改改试试
pull镜像走代理
root@moss:~# cat /etc/docker/daemon.json
{
"live-restore": true,
"registry-mirrors": [
"https://lq50zm47.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com"
],
"runtimes": {
"nvidia": {
"args": [],
"path": "nvidia-container-runtime"
}
}
}
#新增代理
root@moss:~# vi /etc/docker/daemon.json
root@moss:~# cat /etc/docker/daemon.json
{
"live-restore": true,
"registry-mirrors": [
"https://lq50zm47.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com"
],
"runtimes": {
"nvidia": {
"args": [],
"path": "nvidia-container-runtime"
}
},
"proxies": {
"http-proxy": "http://192.168.1.3:7890",
"no-proxy": "localhost,127.0.0.1,docker-registry.example.com"
}
}
#重启docker
root@moss:~# systemctl restart docker
#systemctl daemon-reload
#查看代理
root@moss:~# docker info | grep Proxy
HTTP Proxy: http://192.168.1.3:7890
No Proxy: localhost,127.0.0.1,docker-registry.example.com问题记录
docker 安装后启动报错/docker.sock 无权限
sudo groupadd docker
sudo gpasswd -a [用户名] docker
newgrp dockerError response from daemon: pull access denied for portainer, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
进入容器内部编码错误,不支持中文之类
如果发现什么稀奇古怪的错误,有外挂了移动存储设备,多半就是他了!!!!Docker 已安装的 mysql 的时区更改
威联通 Docker 容器互通问题
症状:在威联通中,使用应用程序(Docker Compose)创建的容器无法与使用镜像(Docker run)安装的容器互通,因为网络问题,应用创建的容器有自己单独的网络 172 网段,run 的容器使用的 nat 就是 docker default 的网络 10 网段。 在威联通中解决的简单办法就是在应用创建的容器中编辑,在网络中将 nat 网段添加进来
