TOP

docker 相关梳理

下载安装

安装文档手册 - 环境 centos7 

具体步骤

1.  卸载旧版本

 sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

2. 使用存储库安装

 sudo yum install -y yum-utils
 sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

3.  安装Docker引擎

sudo yum install docker-ce docker-ce-cli containerd.io

4. 启动 docker

sudo systemctl start docker

 开机自启且马上启动

sudo systemctl enable docker --now

 

5. 验证 docker 安装成功

sudo docker run hello-world

ps:

 如果安装过程存在 404 问题, 需进行源的更换

cd  /etc/yum.repos.d
rm  -rf docker-ce.repo  
rm  -rf  mirrors.ustc.edu.cn_docker-ce_linux_centos_docker-ce.repo
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

用户增加 docker 权限

 每次执行 docker 的命令都要加 sudo 有点麻烦

直接把用户加入到 docker 组里面,  默认这个组会自动创建, 也可以手动创建一遍验证下

设置完之后要对 docker 重启一下, 然后 ssh 重连即可

sudo groupadd docker

sudo gpasswd -a vagrant docker

sudo service docker restart

docker 命令分类

docker 的命令分为两类, management commands 是对一些具体分类的配置的前缀, 比如 image , container 等

其后的命令则是一些基础的不需要指定分类或者一些默认分类的简写

比如对于 container 的操作 

docker container rm xxxx 可以简写为 docker rm xxx

docker container ls  可以简写为 docker ps 

docker container stop xxxx 可以简写为 docker kill xxxx 等

比如对于 image 的操作

docker image ls  可以简写为 docker images 

docker image rm xxxx 可以简写为 docker irm xxx

docker  machine

用于创建安装好了 docker 的虚拟机

基础命令

验证安装版本

默认随着 docker 安装的时候一起安装

docker-machine version

安装生成虚拟机

docker-machine create demo

 

 整个过程类似 vagrant up 创建新的虚拟机, 也是从本地或者远程的镜像开始创建

查看当前存在虚机列表

docker-machine ls

启动当前存在的虚机

docker-machine start demo

链接当前存在虚机列表

操作依旧类似 vagrant ssh

docker-machine ssh demo

退出当前链接虚机

退出的依旧是 

exit

所有支持命令

docke-mechine  后进行追加

stop 停止指定虚机, rm 删除等等

本地指定 server 

docker-machine env demo

打印的最后一行执行后, 则将 demo 这台虚拟的 docker 作为 server 环境

eval $(docker-machine env demo)

云上指定 server 创建虚机

在远程云上进行操作的话需要 Driver 的支持目前可支持的官方指定的 driver 如下 , 官方链接

国内的则需要在第三方的支持里面可以找到比如阿里云

docker image

image 简述

 

 如图 image-4 就是在 image-2 的基础上的mage, 他们都使用的同一份 centos 的基底

image 查看

docker image ls

也可以简写

docker images

通过    docker history 21dbf9672881  后面的数字要查看的 image 的 ID 可以查看具体的分层情况

image 的获取

方式 一 - Dockerfile

创建 dockefile 制定步骤进行创建

输出内容, 根据Dockerfile 的每一行的步骤进行创建层, 然后顺序执行

方式 二 - Registry

命令 :  docker pull [镜像名:指定版本] 

若未指定版本则默认拉去最新的版本

很多的包可以存在第三方的同名, 若未指定前面的签名则默认拉取官方

也可以制定签名者进行其他包的拉取 如

docker container 

container 简述

 

container 的创建

container 是基于 image 创建的, 只需要指定 image 则可以进行创建

 docker run hello-world 

创建的时候如果不指定 容器的内存或者 cpu 的使用, 容器会按照当前实体机的规格作为自己的上限

对 container 的内存做限制

docker run --memory=200M  [image name]

可选的内存相关的限制参数

容器的运行内存=memory--swap+memory ,没有设置memory--swap 则默认和memory的值一致

对 container 的cpu使用做限制

docker run --cpu-shares=10 --name=demo1  [image name]  --cpu 1

可选的内存相关的限制参数

 --cpu-shares 是制定相对权重, 如果有两个不同的 container, 分别设置为 10, 5, 则 第一个就会是第二个的 两倍的使用度

这样设置可以区分container 之间的资源分配优先级

查看 container 

docker container ls  / docker ps    可以进行简写

不带参数默认查询的是当前运行的 container

 加参数  -a 可以查看所有的包括未运行的 container

交互式运行 container 

可以看到 centos 这个 image 创建的 container 只是进行了一次 /bin/bash 的操作 ,  这样的程序是一次执行后就退出结束生命周期

可以在创建 container 的时候 指定参数 -it 可以交互式进行, 比如这里的 centos 相当于又进入了这个 container 的系统内部

使用 -d 则可以让 container 在后台运行

 重新打开一个窗口就可以看到 当前系统存在一个正在运行的 container 了, 同理如果那边 exit 退出之后则表示生命周期结束

exec 进入 container 内操作

exec 可以随时进入一个当前执行中的 container 内进行查看和操作, -it 也是用于持续保持

docker exec -it  e7b22628acd2 /bin/bash

container 详细信息展示

docker inspect 

后跟 id 或者 name 即可查看这个 container 的全部详细信息

以下为一个示例

[
    {
        "Id": "e7b22628acd28a931f158892f299441da2fa74ec082bbc6a3089429a3d88e41a",
        "Created": "2021-05-11T05:03:14.64470952Z",
        "Path": "/bin/sh",
        "Args": [
            "-c",
            "python /app/app.py"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 2400,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-05-11T05:03:14.942143837Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:9339dd625ab4637f29ea98c9be4c4d586123b0dcc60fa8464e3e349b8ec566b8",
        "ResolvConfPath": "/var/lib/docker/containers/e7b22628acd28a931f158892f299441da2fa74ec082bbc6a3089429a3d88e41a/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/e7b22628acd28a931f158892f299441da2fa74ec082bbc6a3089429a3d88e41a/hostname",
        "HostsPath": "/var/lib/docker/containers/e7b22628acd28a931f158892f299441da2fa74ec082bbc6a3089429a3d88e41a/hosts",
        "LogPath": "/var/lib/docker/containers/e7b22628acd28a931f158892f299441da2fa74ec082bbc6a3089429a3d88e41a/e7b22628acd28a931f158892f299441da2fa74ec082bbc6a3089429a3d88e41a-json.log",
        "Name": "/nifty_agnesi",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "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": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/b2fe85b06cba35f07dada165c0d726a7e1d2967cb0b987edfc010443bbf7537c-init/diff:/var/lib/docker/overlay2/b4f32e951c191781da8ce154c4d9fbf7387a0c2a6165accf558ff38a74ddc818/diff:/var/lib/docker/overlay2/b9b75b332dcc32fec8cde2caff58b22d2efd9860161091a34e5d6cec209a20de/diff:/var/lib/docker/overlay2/d7d0542c16ee817f6f37d40bf8e48c7427be4ffc88d8d12917d9e22fadac4b98/diff:/var/lib/docker/overlay2/852ff91ff2a5ea103cf33aaa66a8a0aea50de02483ee0d79cf815a14864d169b/diff:/var/lib/docker/overlay2/01e459322da852f4fa9f1e55ea2565c970777bac6abbdd2d44d13c26ab1439a1/diff:/var/lib/docker/overlay2/e201060f1281463a9a60118bb990d4eb4380beb79e46ace2a52872d1d5d9d35c/diff:/var/lib/docker/overlay2/b221351a67540f85dbc32029f3809131776c841ce4ee23ac5b0dfe6f4a34ed23/diff:/var/lib/docker/overlay2/63431ac14ac31c0f40d007260dfc2e429acf04dc528ab5da76f355db227f23a8/diff:/var/lib/docker/overlay2/d974506712f38f7b5f8a8e00597f5757ddc8eea5535f0900affdc0521385e183/diff:/var/lib/docker/overlay2/11b916dadec26cc098257f4371be564d624f38fe5c282ffb3fc04f82e18a2355/diff:/var/lib/docker/overlay2/1bf38d6429e330c9b91d8636ebe2cb000486fdc86aec7ee0a683c982f254d2e4/diff",
                "MergedDir": "/var/lib/docker/overlay2/b2fe85b06cba35f07dada165c0d726a7e1d2967cb0b987edfc010443bbf7537c/merged",
                "UpperDir": "/var/lib/docker/overlay2/b2fe85b06cba35f07dada165c0d726a7e1d2967cb0b987edfc010443bbf7537c/diff",
                "WorkDir": "/var/lib/docker/overlay2/b2fe85b06cba35f07dada165c0d726a7e1d2967cb0b987edfc010443bbf7537c/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "e7b22628acd2",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "ExposedPorts": {
                "5000/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG=C.UTF-8",
                "PYTHONIOENCODING=UTF-8",
                "GPG_KEY=C01E1CAD5EA2C4F0B8E3571504C367C218ADD4FF",
                "PYTHON_VERSION=2.7.18",
                "PYTHON_PIP_VERSION=20.0.2",
                "PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/d59197a3c169cef378a22428a3fa99d33e080a5d/get-pip.py",
                "PYTHON_GET_PIP_SHA256=421ac1d44c0cf9730a088e337867d974b91bdce4ea2636099275071878cc189e"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "python /app/app.py"
            ],
            "Image": "9339dd625ab4",
            "Volumes": null,
            "WorkingDir": "/app",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "maintainer": "yangtuo_tuo@126.com"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "04d5be4fe62e7621d852dd020044ec6645214f734b4c9af1eed5c5b9532737d1",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "5000/tcp": null
            },
            "SandboxKey": "/var/run/docker/netns/04d5be4fe62e",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "2ed90014d1ffff21581a9f0f0c4319a1e586ede6e18df0d3eb38a96dfb2945ef",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "8518b740c12f8a55dfd01e68fdad18d1f5b626b731abb0bbb178bd605c071c18",
                    "EndpointID": "2ed90014d1ffff21581a9f0f0c4319a1e586ede6e18df0d3eb38a96dfb2945ef",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]
View Code

container 日志

docker logs 

对于可以打印的内容会被 docker 以日志的形式记录下来

container 清理相关命令的实用简写

使用以下命令可以进行 container 的清理 (不删除会一直占用空间)

docker container rm xxx 
docker rm xxxx 

对于大批量 container 的清理的时候, 如果只使用 rm 这样一个一个 id 删除是非常辛苦的

可以使用 -q 参数可以只拿出 id, 作为要被删除的 id 列表

 -f 参数可以过滤, 支持自动提示, 可以进行过滤要删除的 id 

通过  $(要删除的 id) 可以进行披露删除

以上结合起来一起使用比如 删除所有退出的 container 

简单粗暴的删掉所有的 container 

docker rm $(docker ps -aq)

image container 间的交互

container 是 基于 image 进行创建的

但是因为 image 存在不可写入的原因.  因此 container 相关的读写如果想要覆盖到原有的 image 上

则需要进行这部分修改的提交

应用场景

比如这里我在 centos 的这个 image 创建的 container 里面新增了一个文件 a.py 

 我希望将这个 a.py  的文件永远应用到 centos 这个 image 上

从而以后在基于此 image 创建的 container 都拥有 a.py 

或者我希望我以后的 container 都里面安装了 vim 等等

命令详解

以上述事例作为参考 

退出交互式操作后, 在 ps 里面找到 刚刚退掉的  container 

使用命令 

docker commit 

 其中参数  container 的部分用 刚刚退出的 name ,   后面跟上命名的 tag 

可以看到新的 image 就生成了

 其实这个新建的和之前原有的内部还是使用了一些相同的 layer 

验证总结 

 新创建的 container 确实保持了之前的操作结果

但是这样操作会将中间的一些敏感信息泄露到 image 上, 这样别人使用此 image 的时候就可以拿到这些信息

不提倡此操作, 更为安全的方式则是使用 Dockerfile 进行创建, 从而避免这些问题

Dockerfile build image 

还是以上的场景

但是将这个操作移到 Dockerfile 中进行

在使用以下命令进行创建 container 

 docker build -t yangtuo/centos-vim-a .

由此创建的 container 是在 原有的 image 未发生改变的基础上实现的

这样敏感的信息文件都是在 Dockerfile 中, 而不会随image 被传播

Dockerfile 相关语法以及最佳实践

关键字

FROM 

指定要创建所使用的的 base image 

最佳实践建议:

尽量使用官方的 image 来作为 base image 

LABLE 

用于一些帮助描述信息的展示

 

 RUN

主要用于一些命令的执行

最佳实践:

image 本身作为不可读, 而 container 的生成过程中是层层相叠的

一次 RUN 的执行就会生成一层, 因此尽量将命令使用 &&  (系统换行符号) 将多个命令进行合并

如果dockerfile 的一行过长不够直观, 可以使用 \ 反斜线用来文件换行优化显示

CMD 

ENTRYPOINT

 

最佳实践建议: 

将entrypoint 信息写在一个脚本中执行

RUN / CMD / ENTRYPOINT

 

 Exec 格式的时候, 如果不指定 bin/bash , 则 env 的常量无法被识别

特殊常见操作组合

可以利用 entrypoint + cmd 的形式组合成一个命令 

如下图,  entrypoint 使用 exec 的形式指定要执行的对象, 然后 CMD 为空等待 container 创建的时候传入参数

从而实现让 container 根据初始传入的参数不同执行不同的命令效果

WORKDIR 

用于设定当前工作目录, 如果目录不存在会进行相应的创建

最佳实践建议:

RUN cd 是可以达到 和 WORKDIR 相同的效果的

但是不推荐前者,  同时尽量使用绝对路劲, 从而避免还需要上下文去查找, 也更容易出错

ADD / COPY

ADD 和 COPY 的唯一区别就是 ADD 会对文件进行解压缩操作, COPY 是不会的

这两个关键字的作用都是将文件添加到指定的路劲位置上

最佳实践建议:

这两个命令经常会和 WORKDIR 组合操作, 是会受到 WORKDIR 的指定当前目录影响的

如果使用相对路劲则要注意不要出错

大部分情况下, copy 比 add 更优先去使用

这两个命令都是对本地文件的操作无法操作远程文件,  添加远程文件或者目录, 使用 curl 或者 wegt 

ENV

设置常量

 最佳实践建议:

尽量使用ENV 从而减少代码的维护成本

VOLUME

用于持久化

EXPOSE

用于暴露端口号让外部可以访问这个端口

Dockerfile 的共享和发布

docker-hub 是类似于 github 的一个共享网站

对于如果只是使用别人的镜像或者Dockerfile是不需要进行登录的

但是如果想将自己的进行上传则需要注册登录

相关操作

docker login 进行登录

 登录后进行 push, 注意 push 的时候的 image 的 tag 必须和自己的用户名一样 (不能push 别人的仓库里面去)

 本地拉取元辰仓库则直接 docker pull 即可, 本地如果不存在就会在远程找 

还是不推荐使用这种 image 修改的方式进行

推荐使用Dockerfile 进行更新,  docke-hub 会和 github 进行关联

从而 在 github 上更新 Dockerfile 即可保持对image 的后续维护

Docker 私有 docker-hub 

以上的操作不是在 docker-hub 就是在 github , 不论怎么说都是开源的

对于一些敏感信息的公司或者个人信息也可以选择使用私有的  docker-hub

使用此命令即可在任意一台装了 docker 的服务器上启动

 启动服务的端口需要确保可达

同时再次进行 push 的时候的 tag 要指定格式为远程服务器的ip家端口

 这里会提示 这个不可信任, 因此还需要做一些信任的配置操作

在 etc/docker 下创建一个 daemon.json 里面存入一行配置

然后修改 

sudo vim /lib/system/docker.service

在 这里加入一行在这个位置

EnvironmentFile=-/etc/docker/daemon.json

然后还是重启服务

sudo service docker restart 

私有 docker-hub 的 api

私有的 docker-hub 是没有图形界面的

但是官方也提供了一些基础的 api 可以进行查询 仓库的相关内容

综合实践 打包一个 python flask 的简单程序

 创建一个简单的  flask 的 web 程序

 代码如下 

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'hello docker&flask'

if __name__ == '__main__':
    app.run(debug=True)

 然后就编写 Dockerfile

FROM python:2.7
LAbEL maintainer="yangtuo_tuo@126.com"
RUN pip install flask
COPY app.py /app/
WORKDIR /app
EXPOSE 5000
CMD python /app/app.py

利用 docker build -t 直接生成 image ,  base image 的 python 2.7 image 大概 200 多兆, 首次下载需要些时间

 

可以看到每一步骤都会生成 layer 的 container, 这些 id  都是可以进入的

查看下新创建的 image , 然后用 id 的形式直接创建 container 进行执行

 也可以使用 -d 让这种持续占用页面的操作放在后台执行

docker 网络

验证展示

不同的 container 之间的网络是互相隔离的, 同时单机之间又是互通

下图可以看出两个的 ip 地址不同, 但是可达

同理对外网的访问也是一样

相关命令支持

查看当前的容器网络情况

docker network ls

其中 name 这里区分 bridge / host (完全使用宿主机的网络空间) / none (没有网络配置的单机下线容器) , 后两者用法很少 host 还容易出现端口冲突的场景

bridge 表示本机的物理网卡, 在本机的  ip a 中可以看到 docker0就是这个

而下面的 vth 则是docker0用于和容器相连的接口

原理如图 

inspect 可以查看 具体的网络信息

docker network inspect bridge 

其中在  Containers 中可以看到具体的容器的网络信息

link 

link 的使用场景

比如 程序在 test1, 数据库在 test2 

如果 test1 想在 test2 拿数据, 但是 test2容器的 ip 是不定的

容器创建的时候分配的地址是无法确认的

因此需要一种手段将 test1 可以准确的拿到 test2 上去

因此test1 只需要记录下 test2 的名字就一样可以找到test2

命令使用

创建 docker container 的时候使用参数  --link 指定

在知道对方的 ip 下肯定没问题

在不知道 ip 的时候用 link 的 name 直接代替 ip 的使用也可以达成 , 相当于用 link 设置了一个 dns 的记录

 

但是这里需要注意方向, --link 是单向的.

test2 --link test1 , 在  test2 上 ping  test1 么问题

但是反过来则不行, 还是只能用 ip  

ps:

  这种方式其实用的场景并不多, 可以使用其他的方式来处理这种场景

  比如自建的 bridge 就会吧链接到这个 bridge 上的 container 都建立好 link

  这样所有的 container 之间都可以直接用名字来互通

自建 bridge 

默认的 container 创建的网络都是链接到docker0 的默认的 bridge 上

所有链接到 docker0 上的 containers 彼此是不带有 link 需要自行手动指定

因此存在不方便, 自建 bridge 则不会有这个问题

创建命令

创建时使用 -d 指定要创建的 driver 类型, 后面跟起个名字

docker network create -d bridge tuo-bridge

\

链接 bridge 

 链接方式有两种, 创建的时候进行使用 --network 进行指定

 或者将已有现在运行的 container 新建一个链接过来

docker network connect tuo-bridge test2

后面跟两个参数分别是 bridge 以及要切换的  container 

验证

 创建指定的  test3 , 和切换过来的 test2 都可以在 inspect 中这里看到

而且 test2 之前有链接到 docker0 上, 所有 test2 两个网段都能互通

测试这两个之间也都是可以互通的

docker  端口映射

创建的时候通过指定 -p 参数设定本机与 container 之间的端口映射

docker run --name web -d -p 80:80 nginx

设置了端口映射之后容器的 端口就乎映射到本机的端口一致

从而本机的访问即可访问到容器内, 以下示例为 nginx 的一个示例

docker 持久化

应用场景 

docker 的 container 停止后的数据是无法保存的

比如一个数据库的 container 结束后希望这中间的结果都可以保存在数据库中

则需要指定持久化的存储进行保存

方式一: Data Volume

volume 在 docker中是同 image / container  一样的一个分类的子命令

其中的大部分命令也喝 image / container 类似比如 ls, rm, inspect 等

此命令用于查看持久化的任务列表

docker volume ls

 

docker volume rm xxxx

 删除则 ls 换成 rm

查看具体的信息

docker volume inspect xxxx

如果不指定别名默认生成的 volume 识别上很不友好, 可以在创建 container 的时候 通过 -v 进行指定别名

指定格式后面跟上路径表示存放位置

docker run -v xxxx:xxx/xxx/

通过 -v 参数指定了持久化存放的地点, 多个container 可以同时使用此 volume 进行操作存储

方式一: Bind Mounting

不同于 Data Volume 

此方式的完全的文件映射, 这样在对 home/aaa 目录进行修改的时候 root/aaa 目录也会发生相应的改变

非常方便开发中的代码同步

多容器管理 - Docker Compose 

使用背景

多个容器的互相配合使用的时候较为繁琐

每个 image 要进行拉取或者 build , container 要分别创建还要进行管理启动停止删除等

Docker Compose 介绍

Docker Compose 安装

mac / windows 在安装 docker 的时候回附带一起自己安装

但是 linux 则需要自行安装 相关的官方链接 

步骤 1 :  下载文件

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

步骤2:  给予可执行权限

sudo chmod +x /usr/local/bin/docker-compose

docker-compose.yml 文件编写

 Services / Networks / Volumes 作为 docker compose 的三大概念

 Services 

如图 db 这个服务是以 posegres:9.4 的 image 进行拉取创建

 通过 Dockerfile 创建则是这种形式

Volumes 

在 Service 定义的时候的 volumes 也是需要声明创建的

Networks 

完整示例

build 的的指定位置以及 Dcokerfile 的文件名字

 image 的则直接指定 image 即可

 

docker-compose 命令

docker-compose 的命令大部分要基于 yml 的文件来操作

启动操作

docker-compose up

默认的 yml 文件为 docker-compose.yml 

使用的时候默认使用的就是 docker-compose.yml 进行启动

通过 -f 可以自行指定要启动的  yml 文件

通过 -d 也可以指定后台执行, 不后台执行会占用当前窗口退出也是同  ctrl+c

本地调试的时候如果为了 debug 看日志的时候则可以不用

容器进程查看

容器进程停止 / 启动

stop / down 两个命令可以进行停止. 但是 down 会将 容器移除 (但不会删除 image, 只会删除 container)

start 则可以让 被停止的开启, 但是 down 的则无法开启 

容器镜像查看

images 

容器命令执行

exec 用法类似 docker 的 exec, 也是指定了 container (sevices) 之后后面具体命令 

 

容器扩展

 在 up 的时候进行此参数的指定--scale 可以允许容器进行扩展

但是注意端口如果存在 -p 映射本地的时候, 分配的情况下扩展会出现端口被占用冲突的场景从而无法启动

基于此原理再加一个 HAProxy 进行分流让 多个扩展的容器均衡,  可以扛起来更多的并发访问量

容器编排 Swarm

单机环境下都是直接本地 cli 里面进行操作即可

多容器的之间如果需要部署在非单机环境, 而是在集群中部署则很多的问题需要进行考虑, 比如

Swarm 则是 docker 官方自带的容器编排系统用于解决此类问题

此工具属于内置, 不需要任何的额外安装下载

Swarm 节点角色

Manager 

作为集群的首脑. 用于控制整个集群

Manager 作为控制中心必须存在多个才可以互相依持避免出现单点故障

而彼此之间的数据状态同步则需要依靠 Raft 协议进行, 通过一个分布式的存储数据库进行实现

Worker

Worker 则进行具体的项目部署承载, 彼此之间的数据同步也会通过 Cossip 网络进行同步

Swarm 服务和扩展

Service 

概念类似 docker-compose 中的 service, 其实本质也就是 container 容器

Replicas

service 进行横向扩展的时候, manager 则需要进行决策调度具体在哪一个节点上进行创建

具体的服务创建和调度的算法基于 Raft 进行判定

Swarm 命令

 init 初始化 manager 

 

--advertise-addr 宣告 manager 节点地址

docker swarm init --advertise-addr=192.168.205.10

如果宣告成功之后会有打印一个行命令, 可以用于 worker 进行认证 manager 时使用

里面携带 manager 的 token 等信息

worker join

manager 返回的信息在 要被指定的 worker 上执行,  可以将此设备指定为 manger 的worker

node 查看

在manager 上可以通过此命令查看当前的链接节点信息

service 

作为子命令很多地方类似 . 

create 创建一个 container 

ls 查看, 这里的 REPLICAS 表示可以扩展的数量

更详细的情况可以使用 ps 指定名字进行查看, 本身就是个 container 也可以 直接 docker ps 查看

可以看到 create 创建的指定的名字是 services 的名字, 而不是 container 的名字

 scale 扩展

扩展之后 ls 就可以看打 变成 5/5 了, 表示 5个ready , 总数 5个

可以看到这 5个的分配情况

 这时如果我再 worker 2 上将这个强制删除

再回到决策机上可以看到 5个数量是保持的2 上删除了也会在其他的机器上重新开一个保证运行

rm 进行删除, 在决策机上删除之后. 后台会通过一系列复杂的机制去worker 上也进行相关的删除, 可能速度还是会有些慢一点

 Swarm 网络

对于 swarm 的多机互联网络内部存在 DNS 服务发现 机制

从而可以将 worker 于 manager 在同一个网络中互通, 从而对某一个服务可以用 servicesname进行直接访问

注意这里的 dns 转换的时候是使用的 vip, 如果使用真实 ip 是存在有变动的可能性从而不稳定, 而使用 vip 则可以保持不变.

对于绑定了端口的 服务,端口会暴露到所有的节点,  因此整个swarm网络都可以直接通过该端口进行访问, 同时建库负载均衡

负载相关的底层基础是 IPVS 来实现 , 具体的数据包走向较为复杂

DNS 服务发现具体示例验证

 在决策机上创建两个服务, 分别是  whoami 的服务, 建立在 决策机器上

第二个服务是 busybox 创建在worker1 上, 这两个服务都是建立在 overlay 的网络上

两个服务起来之后, 到 worker1 上对 manager 上的 whoami 进行 ping

可以看到 ping 通. 同时 ping 的是 10.0.0.7 这个虚拟地址

继续讲 whoami 的服务进行扩展, 拓展后的第二个是在 worker2 上

 

此时在继续 ping 操作可以看到 vip 没有发生变化, 同理 对这个服务的迁移, 关闭, 等操作都不会影响到这个 vip

docker stack 部署 

依旧是使用 docker-compose 的 yml 文件进行编写, 但是执行的时候使用 docker 的命令进行

docker stack 

 示例

deploy 相关参数详解

yml 总新增了一个对这方面各项配置的支持子命令 doploy 

官方文档 中对 deploy 的相关的参数

 

mode 设置  global 表示不可扩展, 只能全局只有一个, 默认是 replicated 可进行扩展

placement 做一些限制条件, 比如这里 node.role 可以限制只能在 manager 上建立

replicas 指定扩展的大小, 这样一开始部署的时候就可以直接部署6个

resources 主要是做资源的限制 limit , 或者预留 reservations

restart_policy 重启的条件相关的参数

update_config 更新相关的配置, 这里 parallelism 表示同时更新的数量, delay 表示更新建个的时间

docker secret 管理

对于一系列不想暴露的信息进行加密传递

原理

相关命令

创建 secret 有两种方式

docker secret create my-pw password

通过文件的形式进行创建, 最好创建完之后就删除文件, 文件内容直接就是一行密码信息即可.

也可以通过命令行的方式直接创建, 这样更加简单

创建之后的  secret 的使用则是在创建 container 的时候通过 --secret 进行指定即可,  指定多个就加多个 --sercet 

 在创建的 container 里面查阅已有的 secret 则使用  cd  /runsecrets 然后 cat 即可

 其他的地方在使用此环节变量的时候则可以通过文件的形式读取即可

在 stack 里面使用

这个 my-pw 如果直接是当前已存在的则可以直接使用

如果不存在也可以这样指定, 但是这样不安全, 还保留了一个 password的文件

最推荐的方式还是自己创建一个 secret 然后直接使用

 

 

 

 

 

 

 

 

 

 

 

 

 ---------------------施工中请绕行 - orz 

 

posted @ 2021-05-08 18:03  羊驼之歌  阅读(168)  评论(0编辑  收藏  举报