笔记

概念

虚拟化

虚拟化技术是一种将计算机物理资源进行抽象、转化为虚拟的计算机资源提供给程序使用的技术

计算机资源:CPU提供的运算控制资源,硬盘提供的数据存储资源,网卡提供的网络传输资源等。
硬件虚拟化,指的是物理硬件本身就提供虚拟化的支持
软件虚拟化。指的是通过软件的方式来实现虚拟化中关键的指令转换部分。

虚拟机

通过一个虚拟机监视器的设施来隔离操作系统与硬件或者应用程序和操作系统,以次来达到虚拟化的目的。 通过隔离程序和操作系统,将程序的指令转换为当前所在操作系统平台所能执行的指令,达到了不用对程序进行任何修改即可执行的目的。

容器

容器技术是一种全新意义上的虚拟化技术,属于操作系统虚拟化的范畴, 可以理解为操作系统本身支持一些接口,能够让应用程序间可以互不干扰的独立运行,并且能够对其在运行中所使用的资源进行干预。 容器技术没有做指令转换,运行在容器中的应用程序自身必须支持在真实操作系统上运行。

数据卷

Docker采用挂载真实数据源的方式进行应用数据文件存储,通过UnionFS(联合文件系统),除了能够从宿主机操作系统中挂载目录外,还能够建立独立的目录持久存放数据,或者在容器间共享。

Docker常用命令:

镜像命令

docker images

docker images 查看所有本地的主机上的镜像

可选项
 -a, --all  # 列出所有镜像
 -q, --quiet  # 只显示镜像的id

docker search

docker search 搜索镜像
docker search mysql

可选项:通过收藏来过滤
--filter=START=3000 # 搜索出来的镜像就是START大于3000的
docker search mysql --filter=START=3000

docker pull

# docker pull 镜像名[:tag]   下载镜像
docker pull mysql
# 如果不写tag 默认就是latest
# 分层下载,docker image的核心 联合文件系统
# Digest: 签名
# dokcer.io/librarymysql:latest   真实地址

docker rmi

使用该镜像创建的容器在运行中,此镜像不可删除  不过可以 加-f 参数 强制删除。  
docker rmi -f 镜像id   # 强制删除指定的镜像
docker rmi -f 镜像id0, 镜像id1, 镜像id   # 强制删除指定的多个镜像
docker rmi -f $(dcoker imaages -aq)  # 强制批量删除 查询出来所有的镜像

docker commit

创建镜像: 通过容器创建
docker commit [-a 作者 -c 执行某些Dockerfile执行 -m 说明文字 -p 是否暂停容器] 容器id 镜像:标签
docker commit -a meiko -m "一段描述" container_id image_name:version1
[]中选项可以省略

docker build

创建镜像:通过Dockerfile创建
docker build ./demo
选项:--no-cache 创建过程不使用缓存
         -f 指定要使用的Dockerfile文件的路径,指向路径+文件名 
     -t 镜像的名字及标签
     
docker build --no-cache -t image_name:tag -f Dockerfile_local .

docker save/load 镜像迁移 (只想备份images时使用)

# 将nginx:latest导出到/demo/nginx.tar中
docker save -o /demo/nginx.tar nginx:latet
#或
docker save > /demo/nginx.tar nginx:latest
# 注意save下来的是每一层的数据

# 将/demo/nginx.tar导入
docker load -i /demo/nginx.tar
# 或
docker load < /demo/nginx.tar

注意: 导入的镜像没有repo和tag 可以使用docker tag 镜像id repo:tag任意指定自己想要的tag

docker export/import 将一个容器导出为文件(容器启动后,内容有变化,想备份容器时使用)

# 导出容器为文件
docker export -o "my_nginx.tar" 容器id

# 导入文件成镜像
选项: --change  使用coker指令创建镜像
            --message 在导入镜像时设置提交信息
docker import --message "commit my mginx" my_nginx.tar my_nginx:version1

容器命令

说明: 有了镜像才可以创建容器
容器的生命周期分为五种状态
● Created:容器已经被创建,容器所需的相关资源已经准备就绪,但容器中的程序还未处于运行状态。
● Running:容器正在运行,也就是容器中的应用已经在运行中了。
● Paused:容器已暂停,表示容器中的所有程序都处于暂停(不是停止)状态。
● Stopped:容器处于停止状态,占用的资源和沙盒环境都依然存在,只是容器中的应用程序均已停止。
● Deleted:容器已删除,占用的资源及存储的Docker中的管理信息都已释放和移除。

docker run

docker run [可选参数] image   #  新建容器并启动

#参数说明
--name="容器名字"   # 容器名字,区分容器
-d                               # 后台方式运行
-it                              # 使用交互方式运行,进入容器查看内容
-p                 # 指定容器的端口 -p 8080:8080
    -p ip:主机端口:容器端口
  -p 主机端口:容器端口 (常用)
  -p 容器端口
  容器端口
-P                               # 随机指定端口
--rm 在容器退出时能够自动i清理容器内部的文件系统, 不能与-d同时使用。 作用等价于,在容器退出后,执行docker rm -v

docker run -it centos /bin/bash  # 启动并进入容器

exit # 从容器中退回主机

docker ps

docker ps # 列出运行中的容器

可选参数
-a  --all # 列出所有容器。包括停止的
-n=?      # 列出最近n个创建的容器
-q        # 只显示容器的编号

docker create 创建容器

docker create --name demo 镜像id或repo:tag

docker start 启动容器

docker start 容器id或容器名称
# 此时 容器处于启动状态

docker exec 进入容器

docker exec -it 容器id或容器名称 /bin/bash   
选项: -it 以交互命令行方式进入其中

exit 退出容器

exit # 直接容器停止并退出
Ctrl+P+Q #容器不停止退出

docker rm

docker rm 容器id                 # 删除容器,不能删除正在运行的容器
docker rm $(docker ps -aq)       # 强制删除全部容器
docker rm -f 容器id              # 强制删除容器
docker ps -aq | xrags docker rm  # 管道符删除筛选出来的容器

启动和停止容器

docker start 容器id   # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id    # 停止当前正在运行的容器
docker kill 容器id    # 强制停止当前容器

其他命令:

后台启动容器

docker run -d 容器id  # 启动容器

问题: 发现容器同是停止的
原因:docker 容器使用后台运行,就必须要有哟一个前台进程, docker发现没有应用, 就会自动停止
# nginx, 容器启动,发现自己没有通过服务, 就会立即停止

查看日志

docker logs [可选参数] 容器id
# 可选参数
  -f : 跟踪日志输出
  --since :显示某个开始时间的所有日志
  -t : 显示时间戳
  --tail :仅列出最新N条容器日志
  
 docker logs -ft --tail 10 容器id  # 显示该容器最近10条日志

查看容器中进程信息

docker top 容器id
UID     PID     PPID
用户ID  进程ID  父进程ID

docker inspect

docker inspect 容器id  # 查看容器的元数据
#docker inspect 6e1d2ce15c2e
[
    {
        "Id": "6e1d2ce15c2e7c27f2c6ac524f3a17132e06bd324492f233f1e9a1b4b2acc330",
        "Created": "2021-05-27T13:52:03.994785605+08:00",
        "Path": "/alicvdata/cv_engine_app/start.sh",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 129119,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-07-22T21:04:43.263598079+08:00",
            "FinishedAt": "2021-07-22T20:51:58.196842719+08:00"
        },
        "Image": "sha256:7f47f511e9d7e20a5f8b9af45e20795695ac79ed312a6ddfeaa03f04cd4d442c",
        "ResolvConfPath": "/apsarapangu/disk1/docker/containers/6e1d2ce15c2e7c27f2c6ac524f3a17132e06bd324492f233f1e9a1b4b2acc330/resolv.conf",
        "HostnamePath": "/apsarapangu/disk1/docker/containers/6e1d2ce15c2e7c27f2c6ac524f3a17132e06bd324492f233f1e9a1b4b2acc330/hostname",
        "HostsPath": "/apsarapangu/disk1/docker/containers/6e1d2ce15c2e7c27f2c6ac524f3a17132e06bd324492f233f1e9a1b4b2acc330/hosts",
        "LogPath": "/apsarapangu/disk1/docker/containers/6e1d2ce15c2e7c27f2c6ac524f3a17132e06bd324492f233f1e9a1b4b2acc330/6e1d2ce15c2e7c27f2c6ac524f3a17132e06bd324492f233f1e9a1b4b2acc330-json.log",
        "Name": "/thanos_x86",
        "RestartCount": 0,
        "Driver": "overlay",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {
                    "max-file": "4",
                    "max-size": "25m"
                }
            },
            "NetworkMode": "host",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 67474893824,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": [],
            "BlkioDeviceWriteBps": [],
            "BlkioDeviceReadIOps": [],
            "BlkioDeviceWriteIOps": [],
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpusetCpus": "35,36,37,38",
            "CpusetMems": "",
            "ScheLatSwitch": 0,
            "Devices": [],
            "DiskQuota": null,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 134949787648,
            "MemorySwappiness": -1,
            "MemoryWmarkRatio": 0,
            "MemoryExtra": 0,
            "MemoryForceEmptyCtl": -1,
            "MemoryPriority": null,
            "MemoryUsePriorityOOM": null,
            "MemoryKillAll": null,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "CPUBvtWarpNs": -2,
            "IntelRdtL3Cbm": "",
            "IntelRdtGroup": "",
            "IntelRdtMba": "",
            "BlkFileLevelSwitch": 0,
            "BlkBufferWriteBps": 0,
            "BlkMetaWriteTps": 0,
            "BlkFileThrottlePath": null,
            "BlkBufferWriteSwitch": 0,
            "BlkDeviceBufferWriteBps": null,
            "BlkDeviceIdleTime": null,
            "BlkDeviceLatencyTarget": null,
            "BlkioDeviceReadLowBps": null,
            "BlkioDeviceReadLowIOps": null,
            "BlkioDeviceWriteLowBps": null,
            "BlkioDeviceWriteLowIOps": null
        },
        "GraphDriver": {
            "Name": "overlay",
            "Data": {
                "LowerDir": "/apsarapangu/disk1/docker/overlay/55c3d22360047965b1c9776fe11163d06c5a989a2b50085d2fcc2a23d307420b/root",
                "MergedDir": "/apsarapangu/disk1/docker/overlay/fff2e5913071f40797f163aeeef850ded288ef740c062e69b7d620215b365cfb/merged",
                "UpperDir": "/apsarapangu/disk1/docker/overlay/fff2e5913071f40797f163aeeef850ded288ef740c062e69b7d620215b365cfb/upper",
                "WorkDir": "/apsarapangu/disk1/docker/overlay/fff2e5913071f40797f163aeeef850ded288ef740c062e69b7d620215b365cfb/work"
            }
        },
        "HostRootPath": "/apsarapangu/disk1/docker/overlay/fff2e5913071f40797f163aeeef850ded288ef740c062e69b7d620215b365cfb/merged",
        "Mounts": [],
        "Config": {
            "Hostname": "e21a02001.cloud.a02.amtest8",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "3306/tcp": {},
                "5000/tcp": {},
                "8080/tcp": {}
            },
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": null,
            "Image": "reg.docker.alibaba-inc.com/stonecloud/cv_common_engine:thanos_x86",
            "Volumes": null,
            "WorkingDir": "/alicvdata/cv_engine_app",
            "Entrypoint": [
                "/alicvdata/cv_engine_app/start.sh"
            ],
            "OnBuild": null,
            "Labels": {
                "build-date": "20180107",
                "desktop.docker.io/binds/0/Source": "/etc/resolv.conf",
                "desktop.docker.io/binds/0/SourceKind": "hostFile",
                "desktop.docker.io/binds/0/Target": "/etc/resolv.conf",
                "license": "GPLv2",
                "name": "CentOS Base Image",
                "vendor": "CentOS"
            },
            "FromCmdCreate": true,
            "NetPriority": 0
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "ef1c85f46e8dd1509fd03c3be8e8af3bf26e9f09522e5419c1b910e64f86aed3",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/default",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "host": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "8a8874fe423fc34f361b7faa8e617643895a1691473505d5bf202acf7b1a7c28",
                    "EndpointID": "10a025a35207e34f90c129bb58458865ef4fd1516a5fec3aa1725bb8271eb8b2",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "",
                    "Time": 0,
                    "SkipResolver": false
                }
            }
        }
    }
]

进入当前正在运行的容器

容器退出使用后台方式运行的,需要进入容器, 修改一些配置。
# 方式一:
docker exec -it 容器id  /bin/bash   # 进入容器后 开启一个新的终端,可以在里面操作

# 方式二:
docker attach 容器id  # 进入容器正在执行的终端。 不会启动新的进程。

从容器内拷贝文件到主机上

docker cp 容器id:容器内文件路径 本机目录  

#步骤1,进入容器
# docker attach 容器id

#步骤2,拷贝
# docker cp hye868923768:/home/meiko.js /home

# 说明: 拷贝是手动操作 使用 -v 卷技术,可以实现自动同步

小结

image.png

attach  # 当前shell下attach链接指定运行镜像
build   # 通过Dockerfile 定制镜像
commit  # 提交当前容器为新的镜像
cp      # 从容器中拷贝指定文件或目录到宿主机中
create  # 创建一个新的容器,同 run, 但不启动容器
diff    # 查看docker容器变化
events  # 从docker服务获取容器实时事件
exec    # 在已存在的容器上运行命令
export  # 导出容器内的内容流作为一个tar 归档文件【对应import】
import  # 从tar包中的内容创建一个新的文件系统映像【对应export】
history # 展示一个镜像形成历史
images  # 列出系统当前镜像
info    # 显示系统相关信息
inspect # 查看容器详细信息
kill    # kill 指定容器
load    # 从一个tar包中加载一个镜像[对用save]
save    # 保存一个镜像为tar包[对应load]
login   # 注册或者登录一个docker源服务器
logout  # 从当前Docker registry退出
logs    # 输出当前容器日志信息
port    # 查看映射端口对应的容器内部源端口
start   # 启动容器
stop    # 停止容器
pause   # 暂停容器
unpause # 取消暂停容器
restart # 重启容器
ps      # 列出容器列表
pull    # 从docker镜像源服务器拉取指定镜像或者库镜像
push    # 推送指定镜像或者库镜像到docker源服务器
rm      # 移除一个或多个容器
rmi     # 移除一个或多个镜像
run     # 创建一个新的容器并运行
search  # 在docker hub中搜索镜像
tag     # 给源中镜像打tag
top     # 查看容器中运行的进程信息
version #查看docker版本
wait    # 截取容器停止时的退出状态值

Docker 镜像

镜像定义

镜像是一种轻量级,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码,运行时,库,环境变量和配置文件.

Docker镜像加载原理

UnionFS(联合文件系统)
下载镜像时看到的一层层的.
UFS是一种分层,轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加, 同时可以将不同目录挂载到同一虚拟文件系统. Union文件系统是Docker镜像的基础,镜像可以通过分层来继续继承,基于基础镜像,可以制作各种具体的应用镜像.

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统, 联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录.

Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成, 这种层级就是联合文件系统(unionFS)
bootfs(boot file system- 系统启动需要引导加载),主要包含bootloader(加载器)和kernel,加载器主要是引导加载kernel.Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层bootfs. 这一层与典型的Linux/Unix系统是一样的, 包含boot加载器和内核, 当boot加载完成之后,整合内核就在内存中了, 此时内存的使用全已由bootfs转交给内核,此时系统也会卸载bootfs.

rootfs(root file system) 在bootfs之上, 包含的就是典型Linux系统中的/dev /proc /bin /etc等标准目录和文件, rootfs就是各种不同的操作系统发行版,如Ubuntu, Centos等

docker images inspect redis:latest 查看redis的镜像详情.
分层下载, 6个layer, 表示有6层.
image.png

通俗解释:
黑屏->加载>开机运行操作系统 中间加载过程就是bootfs工作过程,通过加载器加载内核, 完成后卸载bootfs, 这个过程比较耗时,

对于一个精简的OS, rootfs可以很小,只需要包含最基本的命令, 工具和程序库就可以了, 因为底层直接用Host的kernel,自己只需要提供rootfs就可以了. 因此对于不同的Linux发行版, bootfs基本是一致的, rootfs会有差别,不同的发行版可以公用bootfs

虚拟机分钟级别,容器秒级.

image.png

分层理解

分层的镜像

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生
改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,
实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容
器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。


image.png

Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈, 并保证多镜像层对外展示为统一的文件系统.

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层, 容器直线的都叫镜像层.


image.png

tomcat 作为基础镜像pull下来, 需要操作时, 通过run启动,新增一个容器层. 发布时,将所有打包为一个镜像发布.

定制镜像

docker commit定制镜像

启动容器:
docker run --name webserver -d -p 80:80 nginx
这条命令会用nginx镜像启动一个容器,命名为webserver,并且映射了80端口,这样我们可以用浏览器去访问这个nginx服务器

进入容器:
$docker exec -it webserver bash

修改欢迎界面内容:即修改了容器的存储层:
root@3729b97e8226:/#echo'

Hello,Docker!

'>/usr/share/nginx/html/index.html
root@3729b97e8226:/#exit

docker diff 查看容器的具体改动:实际上可以看到很多文件被改动过。要是安装部署或其他修改 会使修改的文件很多,长此以往使用commit定制的镜像会变得非常臃肿。所以推荐使用

$docker diff webserver
C/root
A/root/.bash
_
history
C/run
C/usr
C/usr/share
C/usr/share/nginx
C/usr/share/nginx/html
C/usr/share/nginx/html/index.html
C/var
C/var/cache
C/var/cache/nginx
A/var/cache/nginx/client_temp
A/var/cache/nginx/fastcgi_temp
A/var/cache/nginx/proxy_temp
A/var/cache/nginx/scgi_temp
A/var/cache/nginx/uwsgi_temp

将容器保存为镜像:docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

$ docker commit\
--author "张三"\
--message "修改了默认网页"\
webserver\
nginx:v2
sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214

Docker网络

容器网络是docker为应用程序所创造的虚拟环境的一部分,独立于宿主机操作系统的网络环境,形成容器自有的网络设备, IP协议栈, 端口套接字, IP路由表, 防火墙等等与网络相关的模块。

默认网络

安装Docker后,会默认创建三种网络, 可以通过docker network ls查看

[root@localhost ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
688d1970f72e        bridge              bridge              local
885da101da7d        host                host                local
f4f1b3cf1b7f        none                null                local

Table
网络模式
bridge 为每一个容器分配,设置IP等,并将容器连接到一个docker0虚拟网桥,默认为该模式
host 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口
none 容器有独立的Network namespace,但并没有对其进行任何网络配置。
container 新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP,端口范围等

bridge模式

在该模式中,Docker守护进程创建了一个虚拟以太网桥docker0, 新建的容器会自动桥接到这个接口,附加在其上的任何网卡之间都能自动转发数据包。

默认情况下,守护进程会创建一对对等虚拟设备接口veth pair,将其中一个接口设置为容器的etho接口(容器的网卡), 另一个接口放置在宿主机的命名空间中, 以类似veth***这样的名字命名, 从而将宿主机上的所有容器都连接到这个内部网络上。

使用bridge模式

# 创建容器时通过参数 --net bridge或--network bridge指定即可(默认bridge模式,所有也就无需指定)
docker run -it --name bbox01 --network bridge busybox

容器内查看ip addr:

image.png

宿主机内查看ip addr:

image.png

查看容器的IP地址和Gateway信息:

image.png

查看所有bridge网络模式下的容器:

docker network inspect bridge # 查看所有bridge网络模式下的容器
# 在 Containers 节点中可以看到容器名称
image.png
image.png

bridge桥接模式的实现步骤主要如下:
● Docker Daemon利用veth pair技术, 在宿主机上创建一对对等虚拟设备接口, 假设为veth0和veth1。而veth pair的技术特性可以保证无论哪一个veth接受到网络报文,都会将报文传输给另一方;
● Docker Daemon将veth0附加到Docker Daemon创建的docker0网桥上。 保证宿主机的网络报文可以发往veth0;
● Docker Daemon将veth1添加到Docker Container所属的namespace下,并被改名为eth0。
如此一来,宿主机的网络报文若发往veth0,则立即会被Container的eth0接收,实现宿主机到Docker Container网络的联通性;同时,也保证Docker Container单独使用eth0,实现容器网络环境的隔离性。

host网络模式

● 采用host网络模式的Docker Container,可以直接使用宿主机的IP地址与外界进行通信,若宿主机的eth0是一个公有IP,那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行NAT转换;
● host网络模式可以让容器共享宿主机网络栈, 这样的好处时外部主机与容器直接通信,但是容器的网络缺少隔离性。

使用host网络模式

# 在创建容器时通过参数 --net host 或者 --network host指定
docker run -it --name bbox02 --network host busybox

通过 ip addr查看宿主机和容器。发现网卡信息完全一致。

容器内查看ip addr:


image.png

宿主机查看ip addr:


image.png

查看host网络模式下的容器

docker network inspect host
# 在Container节点中可以看到容器名称。
image.png

none网络模式

none网络模式,即不为Docker Container创建任何的网络环境,同期内部就只能使用loopback网络设备,不会再有其他的网络资源。可以说none网络模式为Docker Container做了极少的网络设定, 在没有网络配置的情况下,作为Docker开发者, 才能在这基础上做其他无限可能的网络定制开发。

使用none网络模式
none网络模式是指禁用网络功能, 只有IO接口(local的简写),代表127.0.0.1,即localhost本地回环地址。

# 在创建容器时通过参数--net none或 --network none指定
docker run -it --name bbox03 --network none busybox

查看ip addr:

image.png

查看所有none网络模式下的容器

docker network inspect none 
# 在Container节点可以看到容器名称
image.png

Container网络模式

● Container网络模式,即新创建的容器不会创建自己的网卡,配置自己的IP,而是和指定的容器共享IP,端口范围等。同样两个容器除了网络方面相同之外,其他的如文件系统,进程列表等都是隔离的。
● 处于这个模式下的Docker容器会共享一个网络栈,这样两个容器之间就可以使用localhost高效快速通信。

image.png

使用Container网络模式

# 在创建容器时通过参数--net container或 --network container指定
# 基于容器bbox01创建了网络模式为container的容器bbox04
docker run -it --name bbox04 --network container:bbox01 busybox

查看ip addr:

image.png

查看容器bbox01的ip addr信息如下:

image.png

宿主机的ip addr信息如下:

image.png

如上:Docker守护进程之创建了一对对等虚拟网络设备接口,用于连接bbox1容器和宿主机,而bbox04容器则直接使用了bbox1容器的网卡信息。

如果将bbox01容器停止,会发现bbox04容器只剩下IO接口了。

image.png

容器bbox01重启后,bbox04容器也重启一下,就可以获取到网卡信息了。

自定义网络

● Docker提供的默认网络模式比较简单,为了保证各容器中应用的安全性,在实际开发中更推荐使用自定义的网络进行容器管理,以及启用容器名称到IP地址的自动DNS解析。

从Docker 1.10版本开始, docker daemon实现了一个内嵌的DNS server,使容器可以直接通过容器名称通信。方法就是在创建容器时使用 --name 为容器命名即可。
但是在使用Docker DNS有个限制:只能在user-defined网络中使用。也就是说,默认的bridge网络是无法使用DNS的,所以需要自定义网络。

创建网络

# 创建一个基于bridge网络模式的自定义网络模式custom_network
docker network create custom_network
选项: --driver  -d 指定网络模式  默认:bridge网络模式

docker network create -d bridge my-net

查看网络模型:docker network ls

[root@localhost ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
b3634bbd8943        bridge              bridge              local
062082493d3a        custom_network      bridge              local
885da101da7d        host                host                local
f4f1b3cf1b7f        none                null                local

通过自定义网络模式custom_network创建容器

docker run -it --name bbox05 --network custom_network busybox

查看容器的网络信息:

docker inspect bbox05
# 在NetworkSettings节点中可以看到详细信息。
image.png

连接网络:
通过docker network connect 网络名称 容器名称 为容器连接新的网络模式。

docker network connect bridge bbox05

通过docker inspect 容器名称|ID 查看网络信息,多增加了默认的bridge网络模式:


image.png

断开网络
通过docker network disconnect 网络名称 容器名称 命令断开网络。

image.png

移除网络
通过docker network rm 网络名称 命令移除自定义网络模式,返回网络模式名称。

docker network rm custom_network

注意: 如果通过某个自定义网络模式创建了容器,则该网络模式无法删除。

容器间网络通信

容器之间要相互通信,必须要有属于同一网络的网卡。

步骤

  1. 先创建两个基于默认的bridge网络模式的容器
docker run -di --name default_bbox01 busybox
docker run -di --name default_bbox02 busybox
  1. 通过docker network inspect bridge 查看两个容器的具体IP信息。
default_bbox01容器ip: 172.17.0.2
default_bbox02容器ip: 172.17.0.3
image.png
  1. 测试两个容器间是否可以进行网络通信


    image.png

这俩个属于同一个网络的容器是可以进行网络通信的,但是IP地址可能是不固定的,又被更改的情况,那容器内所有通信的IP地址也需要进行更改,能否使用容器名称进行网络通信?

  1. 使用容器名称进行网络通信。网络不通


    image.png

docker daemon实现了一个内嵌的DNS server,使容器可以直接通过容器名称通信,只要在创建容器时使用--name为容器名称即可。
但是使用Docker DNS有个限制:只能在user-defined网络中使用,也就是说默认的bridge网络是无法使用DNS的,所以需要自定义网络

  1. 先基于bridge网络模式创建自定义网络custom_network,然后创建两个基于自定义网络模式的容器
docker network create custom_network # 创建自定义网络custom_metwork
docker run -di --name custom_bbox01 --net custom_network busybox # 创建容器custom_bbox01
docker run -di --name custom_bbox02 --network custom_network busybox # 创建容器custom_bbox02

通过docker network inspect custom_network查看两个容器的具体IP信息


image.png
  1. 测试两个容器间是否可以进行网络通信,分别使用具体IP和容器名称进行网络通信


    image.png

结论:两个属于同一个自定义网络的容器时可以进行网络通信的,并且可以使用容器名称进行网络通信。

7.bridge网络下的容器和custom_network网络下的容器进行网络通信。

# 让bridge网络下的容器连接至新的custom_network网络即可。
docker network connect custom_network default_bbox01
image.png

网络互联 --link
要让一个容器连接到另外一个容器的配置,一种方式是在docker create或docker run创建时通过--link选项配置。

# 创建了一个mysql容器, 并命名为mysql,之后,另一个应用容器需要链接过来。
docker create --link mysql --name webapp -it webapp:latest /bin/bash

注意:必须保证link的容器(mysql)处于start状态

dockerfile结构

dockerfile中的内容主要是两种形式, 以#开头的注释行, 和以指定命令字符串开头的命令行。

dockerfile可以理解为自上而下执行的脚本文件, 其中的指令可以分为五大类

  • 基础指令:用于定义新镜像的基础和性质

  • 控制指令:是指导镜像构建的核心部分, 用于描述镜像在构建过程中需要执行的命令。

  • 引入指令:用于将外部文件直接引用到构建镜像内部。

  • 执行指令:能够为基于镜像所创建的容器,指定在启动时需要执行的脚本或命令。

  • 配置指令:对镜像以及基于镜像所创建的容器,可以通过配置指令对其网络,用户等内容进行配置。

官方文档:https://docs.docker.com/engine/reference/builder/

Docker总架构图:

image.png

Docker三个基本组件:
Docker Client 是用户界面,它支持用户与Docker Daemon之间通信。
Docker Daemon运行于主机上,处理服务请求。
Docker Index是中央registry,支持拥有公有与私有访问权限的Docker容器镜像的备份

Docker三个基本元素:
Docker Containers负责应用程序的运行,包括操作系统、用户添加的文件以及元数据。
Docker Images是一个只读模板,用来运行Docker容器。
DockerFile是文件指令集,用来说明如何自动创建Docker镜像。

Docker实现原理
使用Namespaces实现了系统环境的隔离,Namespaces允许一个进程以及他的子进程从共享的宿主机内核资源(网路栈,进程列表,挂载点等)中获得一个仅自己可见的隔离区域,让同一个Namespace下的所有进程感知彼此变化,对外界进程一无所知,仿佛运行在一个独立的操作系统中;--资源隔离
使用CGroups限制这个环境的资源使用情况, 比如一台16核32G的机器上只让容器使用2核4GB。使用CGroups还可以为资源设置权重,计算使用量,操作任务(进程或线程)启停等;--资源控制
使用镜像管理功能,利用Docker的镜像分层、写时复制、联合挂载技术实现了一套完整的容器文件系统即运行环境,在结合镜像仓库,镜像可以快速的下载和共享,方便再多环境部署。

Docker的核心模块功能与实现分析
Docker系统如下功能来提高容器技术效率:
○ Namespaces 充当隔离的第一级。确保一个容器中运行一个进程而且不能看到或影响容器外的其它进程。
○ Control Groups是LXC的重要组成部分,具有资源核算与限制的关键功能。
○ UnionFS(联合文件系统)作为容器的构建块。为了支持Docker的轻量级以及速度快的
● 特性,它创建层与用户。

cgroups(以一组进程为目标进行系统资源分配和控制)
controller group,可以为系统中所运行任务(进程)的用户定义组群分配资源,比如CPU时间,系统内存,网络带宽或这些资源的组合。可以监控配置的cgroup,拒绝cgroup访问某些资源,甚至在运行的系统中动态配置cgroup。
提供如下功能:
● Resource limitation:限制资源使用,比如内存使用上线以及文件系统的缓存限制。
● Prioritization:优先级控制,比如:CPU利用和磁盘IO吞吐。
● Accounting:一些审计或一些统计,主要目的时为了计费。
● Control:挂起进程,恢复执行进程。
可以用来完成如下事情:
● 隔离一个进程集合(比如nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。
● 为这组进程分配足够其使用的内存
● 为这组进程分配相应的网络带宽和磁盘存储限制
● 限制访问某些设备(通过设置设备的白名单)
cgroups中的重要概念是“子系统”,也就是资源控制器,每个子系统就是一个资源的分配器,比如CPU子系统就是控制CPU时间分配的。 首先挂载子系统,然后才有control group的。 比如先挂在memory子系统,然后再menory子系统中创建一个cgroup节点,在这个节点中, 将需要的控制的进程id写入,并且将控制的属性写入,这就完成了内存的资源限制。

查看linux内核中是否启用的cgroup:("y" 表示已启用)

image.png

内存限制

相关参数:
选项 描述
-m,--memory 内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M
--memory-swap 内存+交换分区大小总限制。格式同上。必须必-m设置的大
--memory-reservation 内存的软性限制。格式同上
--oom-kill-disable 是否阻止 OOM killer 杀死容器,默认没设置
--oom-score-adj 容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
--memory-swappiness 用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数
--kernel-memory 核心内存限制。格式同上,最小为 4M

用户内存限制(-memory 和 --memory-swap)

容器能使用的内存和交换分区大小
-m, --memory 内存限制,格式是数字加单位,单位可以是b,k,m,g,最小为4M
--memory-swap 内存+交换分区大小总限制,必须比-m设置的大.
四种设置方式:
1. 不设置
    不设置 --memory和 --memory-swap,容器默认可以使用完宿主机的所有内存和swap分区。不过注意,如果容器占用宿主机的所有内存和swap分区超过一段时间后,会被宿主机系统杀死(如果没有设置 --oom-likk-disable=true的话)

2. 设置 --memory,不设置 --memory-swap
    给--memory设置一个不小于4M的值,不设置--menory-swap,或者将--memory-swap设置为0。容器能使用的内存大小为a,能使用的交换分区大小也是a。因为Docker默认容器交换分区的大小和内存相同。
  $ docker run -m 1G nginx /bin/bash 该容器能使用的内存大小为1G,能使用的swap分区大小也是1G,容器内的进程能申请到的总内存大小为2G.

3. 设置 --memory=a, memory-swap=b, 且b>a
    a是能使用的内存大小, b是能使用的内存+swwp分区大小。
  $ docker run --memory 1G --memory-swap 3G nginx:1.0 /bin/bash

4. 设置 --memory=a, --memory-swap=1
    给--memory设置为正常值,给--memory-swap设置成-1。这种情况表示限制容器能使用的内存大小为a, 而不限制容器额能使用的swap分区大小。 这时候,容器内进程能申请到的内存大小为a+宿主机的swap大小。

内存的软性限制(--memory-reservation)
是一种软性限制,用于节制容器内存使用。给 --memory-reservation设置一个比--memory小的值后,虽然容器最多可以使用--memroy使用的内存大小,但宿主机内存资源紧张时,在系统下次内存回收时,系统会回收容器的部分内存页,强迫容器内存占用回到--memory-reservation设置的大小。

没有设置时(默认情况下) --memory-reservation的值和-m限制的值相同。 设置为0时 会比-m的参数大,等同于没设置。

$ docker run -it -m 500M --memory-reservation 200M ubuntu:16.04 /bin/bash

如果容器使用了大于200M但小于500M内存时,下次系统的内存回收会尝试将容器的内存锁紧到200M以下。

$ docker run -it --memory-reservation 1G ubuntu:16.04 /bin/bash

容器可以使用尽可能多的内存, --menory-reservation确保容器不会长时间占用太多内存。·

OMM killer
默认情况下,在出现out-of-memory(OOM)错误时,系统会杀死容器内的进程来获取更多空闲内存,这个杀死进程来节省内存的进程,称之为OOMkiller。可以通过设置 --oom-kill-disable选项来禁止OOM killer 杀死容器内进程。 但是请确保只有在使用了-m/--memory 选项时才使用 --oom-kill-disable禁用OOMKiller。如果没有设置-m选项,却禁用了OOM-Killer,可能会造成出现out-of-memory错误时,系统通过杀死宿主机进程获取更多内存。

$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash

限制容器内存为100M,并禁止OOM Killer:(正确使用)

$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash

没有限制容器内存大小,并禁用了OOM Killer:(错误使用)
容器没有限制内存, 可能会导致系统无内存可用, 并尝试杀死系统进程来获取更多可用内存。
一般一个容器只有一个进程,这个唯一进程被杀死,容器也就被杀死了,可以通过 --oom-score-adj 选项来设置在系统内存不够时,容器被杀死的优先级。设置负数不可能被杀死,正值有可能被杀死。

核心内存限制
核心内存和用户内存不同的地方在于核心内存不能被交换出, 不能交换出的特性使得容器可以通过消耗太多内存来堵塞一些系统服务。核心内存包括:
-stack pages(栈页面)
-slab pages
-socket memory pressure
-tcp memory pressure

$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash

容器中的进程最多使用500M内存,在这500M中, 最多只有50M核心内存

$ docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash

没有设置用户内存, 所以在容器中可以使用尽可能多的内存,但是最多使用50M核心内存

Swappiness
默认情况下,容器的内核可以交换出一定比例的匿名页, --memory-swappiness就是用来设置这个比例的。 --menory-swappiness 可以设置为从0到100。0表示关闭匿名页交换。100表示所有的匿名页都可以交换。默认情况下,如果不使用 --memory-swappiness,则该值从父进程继承而来。

$ docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash

CPU限制

概述
Docker提供的CPU资源限制选项可以在多核系统上限制容器能利用那些vCPU。而对容器最多能使用的CPU时间有两种限制方式:

  1. 有多个CPU密集型的容器竞争CPU时,设置各个容器能使用的CPU时间相对比例。
  2. 以绝对的方式设置容器在每个调度周期内最多能使用的CPU时间

CPU限制相关参数
docker run 命令和CPU限制相关的所有选项如下
选项 描述
--cpuset-cpus="" 允许使用的CPU集, 值可以为0-3,0, 1
-c,--cpu-shares=0 CPU共享权值(相对权重)
--cpu-period=0 限制CPU CFS的周期, 范围从1ms~1s,即[1000, 1000000]
--cpu-quota=0 限制CPU CFS配额,必须不小于1ms,即>-1000
--cpuset-mems="" 允许在上执行的内存节点(MEMs),只对NUMA系统有效

其中 :
--cpuset-cpus 用于设置容器可以使用的vCPU核,
-c, --cpu-shares用于设置多个容器竞争CPU时,各个容器相对能分配到的CPU时间比例。
--cpu-period 和--cpu-quata 用于据对设置容器能使用CPU时间。

CPU集

设置容器可以在那些CPU核上运行
如下,表示容器中的进程可以在cpu1和cpu3上执行

$ docker run -it --cpuset-cpus="1,3" ubuntu:16.04 /bin/bash

如下,表示容器中的进程可以在cpu0, cpu1及cpu2上执行

$ docker run -it --cpuset-cpus="0-2" ubuntu:16.04 /bin/bash

在NUMA系统上,可以设置容器可以使用的内存节点

如下,表示容器中的进程只能使用内存节点1和3上的内存

$ dcoker run -it --cpuset-mems="1,3" ubuntu:16.04 /bin/bash

如下,表示容器中的进程只能使用内存节点0,1,2上的内存

$ docker run -it --cpuset-mems="0-2" ubuntu:16.04 /bin/bash

CPU资源的相对限制

默认情况下,所有的容器得到同等比例的CPU周期。在有多个容器竞争CPU时我们可以设置每个容器能使用的CPU时间比例。这个比例叫做共享权值。通过-c 或 --cpu-shares 设置。Docker默认每个容器的权值为1024.不设置或将其设置为0,都将使用这个默认值。系统会根据每个容器的共享权值和所有容器共享权值和比例来给容器分配CPU时间。

注意:这个比例只有在CPU密集型的任务执行时才有用。在四核的系统上, 假设有4个单进程的容器,他们每一个都能各自使用一个核的100% CPU时间。不管他们的cpu共享权值时多少(前提时未设置---cpuset-cpus 限制使用的核)

如下,多于3核心的系统。 用-c=512的选项启动容器 c1,且该容器只有一个进程。用 -c=1024的选项启动容器c2,且该容器有两个进程。CPU权值分布可能如下

PID    container    CPU CPU share
100    {C0}     0   100% of CPU0
101    {C1}     1   100% of CPU1
102    {C1}     2   100% of CPU2

CPU资源的绝对限制

linux通过 CFS (Completely Fair Scheduler,完全公平调度器)来调度各个进程对CPU的使用。CFS默认的调度周期时100ms
CFS周期的有效范围是1ms~1s

--cpu-period 设置容器进程的调度周期,范围1000~1000000
--cpu-quota 设置每个周期内容器能使用的CPU时间 >=1000。 两者结合使用

如下,将CFS调度的周期设为50000, 将容器在每个周期内的CPU配额设置未25000, 表示该容器每50ms可以得到50%的CPU运行时间。

$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash

将配额设置为周期的两倍,表示在每个周期可以使用两个vCPU的100%时间

$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash

正确的理解“绝对”
参数--cpu-quota设置容器在调度周期内使用cpu时间,其实是一个上限。

如下,启动一个容器,并将其绑定到cpu1上执行,设置--cpu-period和--cpu-quota都设置为50000。

docker run -rm --name test01 --cpu-cpus 1 --cpu-period 50000 --cpu-quota 50000 deadloop /bin/bash

观察改容器对CPU的使用率在100%左右。

$ docker stats test01

再使用相同参数启动另一个容器

docker run -rm --name test02 --cpu-cpus 1 --cpu-period 50000 --cpu-quota 50000 deadloop /bin/bash

观察两个容器,每个容器对cpu的使用率在50%左右。说明容器并没有在每个周期内使用50000的cpu时间

$ docker stats test01 test02

结束第二个容器, 增加-c 2048参数 重新启动

$ docker stop test02
$ docker run -rm --name test02 --cpu-cpus 1 --cpu-period 50000 --cpu-quota 50000 -c 2048 deadloop /bin/bash

再次观察容器cpu的使用率, test01使用率在33%左右, test02使用率在66%左右。 第二个容器的共享值是2048,第一个容器test01的默认共享值是1024.

磁盘IO配额控制

参数 IO写速度配置 device-write-bps

docker run -it --name test --device-write-bps /dev/sda:1mb ubuntu:16.04

容器的写磁盘速度被限制到了1MB/s

容器空间大小限制

在daemon启动参数中,使用dm.basesize来指定,重启docker daemon服务。
若docker使用devicemapper作为驱动存储,重启会导致宿主机上的所有本地镜像和容器被清理。

Docker如何运行APP

  1. 构建一个镜像。
    如前面所述,Docker Image是一个构建容器的只读模板,它包含了容器启动所需的所有信息,包括运行哪些进程和配置数据。所有的镜像都会基于一个基本镜像构建,紧接着会根据Dockerfile中的指令创建模板,对于每个指令,在镜像上创建一个新的层。一旦镜像创建完成,就可以将它们推送到中央registry:Docker Index,以供他人使用。然而,Docker Index为镜像提供了两个级别的访问权限:公有和私有访问。您可以将镜像存储在私有仓库。Docker官网有私有仓库的套餐可以供你选择。总之,公有库是可搜索和可重复使用的,而私有库只能给拥有权限的成员访问。Docker Client可用于Docker Index内的镜像搜索。

  2. 运行容器。
    运行容器源于我们在第一步中创建的镜像。当一个容器被启动后,一个读写层会被添加到镜像的顶层。当分配合适的网络和IP地址后,最应用程序就可以在容器中运行了。

Docker镜像(image):
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

分层存储:
docker镜像镜像并非是像一个ISO那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。

Docker容器:
镜像是静态的文件,容器是镜像运行时的实体。
容器的实质是进程。容器(进程)特点:进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户ID空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。

每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。可以理解镜像是一个只包含只读层的文件系统。容器以镜像为基础,在多层文件之上添加了一层读写层(容器存储层),他的生存周期与容器一样。 当删除容器的读写层后,容器也就恢复为一个镜像。 可以使用数据卷跳过读写层直接对宿主机进行读写操作,而数据卷的生命周期独立于容器,使用数据卷,容器可以随意删除,而数据不会丢失。

Docker Registry:一个集中的存储、分发镜像的服务
一个DockerRegistry中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。

你可能感兴趣的:(笔记)