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 的全部详细信息
以下为一个示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[ { "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 } } } } ]
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
本文来自博客园,作者:羊驼之歌,转载请注明原文链接:https://www.cnblogs.com/shijieli/p/14746025.html