docker配置与实践#可以好好看看
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。
虚拟机最大的瓶颈在于其需要特殊硬件虚拟化技术支持,并且携带完整的操作系统;而 Docker 没有硬件虚拟化,可以运行在物理机、虚拟机,甚至嵌套运行在 Docker 容器内,并且其不携带操作系统的,会轻巧很多。在调用宿主机的内存、CPU、磁盘等等资源时,虚拟机是利用 Hypervisor 去虚拟化内存,整个调用过程是虚拟内存->虚拟物理内存->真正物理内存,但是 Docker 是利用 Docker Engine 去调用宿主的的资源,这时候过程是虚拟内存->真正物理内存。
Docker 综合运用了 Cgroup,Linux Namespace,Secomp capability,Selinux 等机制,在 Docker Internals CheatSheet 中我们会有详细的讨论,或者前往 Backend Boilerplate/Docker 浏览常见服务/应用的 Docker 配置案例。
安装与配置
Docker CE
这里我们使用科大的 Docker CE 源进行安装:
# 更改 Ubuntu 默认源地址
$ sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
# 安装必备的系统命令
$ sudo apt-get install -y python-software-properties
$ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
$ apt-cache policy docker-ce # 列举 docker-ce 版本
$ apt-get install docker-ce=17.03.2-ce....
Daemon Configuration
# 配置开机自启动
$ sudo systemctl enable docker
# 取消开机自启动
$ sudo systemctl disable docker
我们还需要修改存储路径,指定镜像存储地址,允许远程访问;此时我们可以修改 systemd 中的配置文件,也可以修改 /etc/docker/daemon.json,此处以修改服务为例:
# 使用 systemctl 命令行修改
$ sudo systemctl edit docker.service
# 或者查找配置地址并使用 Vim 修改
$ systemctl status docker
# 修改文件内容
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock --insecure-registry 10.196.108.176:5000 --dns 114.114.114.114 --dns 8.8.8.8 --dns 8.8.4.4 -g /mnt
然后重启服务:
# 重新载入服务配置
$ sudo systemctl daemon-reload
# 重启 Docker
$ sudo systemctl restart docker.service
# 判断是否配置成功
$ sudo netstat -lntp | grep dockerd
Docker Swarm
# 在主节点启动 Swarm
$ docker swarm init
# 查看 Swarm 密钥
$ docker swarm join-token -q worker
# 在主节点启动 Procontainer
$ docker run -it -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer
# 在主节点启动 Registry
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
# 将子节点加入到 Swarm
$ docker swarm join \
--token ${TOKEN} \
10.196.108.176:2377
代理设置
鉴于 gcr 域名的不可用,我们需要利用 ss-privoxy 等工具搭建 Docker 源代理,也可以参考这里手动配置客户端:
$ docker run -i -t -e SERVER_ADDR=ss.server.ip -e SERVER_PORT=port -e PASSWORD=123456 bluebu/shadowsocks-privoxy
如果需要手动安装,需要先安装 sslocal 命令:
$ apt install python3-pip
$ pip3 install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U
写入你的配置文件到例如 config.json:
{
"server": "...",
"server_port": ...,
"local_port": 1080,
"password": "..."
"method": "chacha20-ietf-poly1305",
"timeout": 600
}
启动:
$ sslocal -c config.json
这时一个 socks5 代理在你本机就启动了。下面安装配置 privoxy 把他转成 http/https 代理。安装略。修改/添加两个 privoxy 的配置(对于 ubuntu,在 /etc/privoxy/config):
listen-address 0.0.0.0:8118 # 所有 interface 上监听流量
forward-socks5 / 127.0.0.1:1080 . # 流量导向本机上的 ss 代理
这时可以访问一下不存在的网站测试一下:
HTTP_PROXY=127.0.0.1:8118 HTTPS_PROXY=127.0.0.1:8118 curl https://www.google.com
下面修改各台机器的 Docker 配置(假定我们的 master 内网地址 1.1.1.2,其他两台机器地址为 1.1.1.3 和 1.1.1.4):
[Environment]
Environment="HTTP_PROXY=127.0.0.1:8118" "HTTPS_PROXY=127.0.0.1:8118" "NO_PROXY=localhost,127.0.0.1,1.1.1.2,1.1.1.3,1.1.1.4"
...
环境变量 NO_PROXY 顾名思义,它不支持 CIDR 应该,所以需要你枚举一下集群主机地址。
镜像
镜像描述了 Docker 容器运行的初始文件系统,包含运行应用所需的所有依赖。即可以是一个完整的操作系统,也可以仅包含应用所需的最小 bin/lib 文件集合。Docker 镜像和容器采用分层文件系统结构,每个容器包含一层薄薄的可写层,只读部分是共享的,这种机制保证了资源的可复用性,减少了镜像与容器的空间占用。Docker 镜像存储引擎有 AUFS,DeviceMapper,Overlay 等多种实现
。
构建与拉取
编写完成 Dockerfile 之后,可以通过 docker build 命令来创建镜像;关于 Dockfile 的具体语法,可以查看下文。Dockfile 基本的格式为 docker build [ 选项 ] 路径,该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下所有内容发送给 Docker 服务端,由服务端来创建镜像。因此一般建议放置 Dockerfile 的目录为空目录。也可以通过 .dockerignore 文件(每一行添加一条匹配模式)来让 Docker 忽略路径下的目录和文件。
镜像的完整 tag 不仅包含镜像名字,还指明了镜像从哪里来,要到哪里去,就像一个 URL。可以通过 -t 选项指定镜像的标签信息,譬如:
$ sudo docker build -t myrepo/myapp /tmp/test1/
$ docker build -t username/image_name:tag_name .
$ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
Docker 支持从 Registry 拉取镜像,或者将某个容器保存为镜像:
# 拉取镜像
$ docker pull image_name
# 将某个容器保存为镜像
$ docker commit -m “commit message” -a “author” container_name username/image_name:tag
复制代码Docker 支持将镜像保存为文件,以方便镜像的导出与加载:
# 保存镜像
$ docker save --output saved-image.tar my-image:1.0.0
$ docker save my-image:1.0.0 > saved-image.tar
$ docker save my_image:my_tag | gzip > my_image.tar.gz
# 导入镜像
$ docker load --input saved-image.tar
$ docker load < saved-image.tar
镜像管理
docker images 命令会列举出全部的镜像:
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
mynewimage latest 4d2eab1c0b9a 5 minutes ago 278.1 MB
ubuntu 14.04 ad892dd21d60 11 days ago 275.5 MB
<none> <none> 6b0a59aa7c48 11 days ago 169.4 MB
<none> <none> 6cfa4d1f33fb 7 weeks ago 0 B
Docker 中镜像主要分为三种状态:
已使用镜像(used image): 指所有已被容器(包括已停止的)关联的镜像。即 docker ps -a 看到的所有容器使用的镜像。
未引用镜像(unreferenced image):没有被分配或使用在容器中的镜像,但它有 Tag 信息。
悬空镜像(dangling image):未配置任何 Tag (也就无法被引用)的镜像,所以悬空。这通常是由于镜像 build 的时候没有指定 -t 参数配置 Tag 导致的。
# 列举未使用的
$ docker images --filter "dangling=true"
# 删除所有无用的镜像
$ docker rmi $(docker images -q -f dangling=true)
Dockfile
Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令;指令的一般格式为 INSTRUCTION arguments,指令包括 FROM、MAINTAINER、RUN 等。例如:
#
# MongoDB Dockerfile
#
# https://github.com/dockerfile/mongodb
#
# Pull base image.
FROM dockerfile/ubuntu
ENV SOURCE http://downloads-distro.mongodb.org/repo/ubuntu-upstart
# Install MongoDB.
RUN \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && \
echo 'deb $SOURCE dist 10gen' > /etc/apt/sources.list.d/mongodb.list && \
apt-get update && \
apt-get install -y mongodb-org && \
rm -rf /var/lib/apt/lists/*
ENV PATH /usr/local/mongo/bin:$PATH
# Define mountable directories.
VOLUME ["/data/db"]
# Define working directory.
WORKDIR /data
# Define default command.
CMD ["mongod"]
# Expose ports.
# - 27017: process
# - 28017: http
EXPOSE 27017
EXPOSE 28017
其中,一开始必须指明所基于的镜像名称,接下来推荐说明维护者信息。后面则是镜像操作指令,例如 RUN 指令,RUN 指令将对镜像执行跟随的命令。每运行一条 RUN 指令,镜像添加新的一层,并提交。最后是 CMD 指令,来指定运行容器时的操作命令。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres;要临时获取管理员权限可以使用 gosu,而不推荐 sudo。
RUN、CMD 和 ENTRYPOINT 这三个 Dockerfile 指令看上去很类似,很容易混淆。RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。ENTRYPOINT 配置容器启动时运行的命令。我们经常可以使用 ENTRYPOINT 指定固定命令,使用 CMD 动态传入参数。
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
# docker run -it <image>
# Hello world
# docker run -it <image> John
# Hello John
Docker 推荐的是单个容器执行单个任务的关注点分离策略,容器的主进程会负责管理所有的子进程。如果我们未在自定义初始化脚本中考虑太多子进程生命周期管理的操作,那么可以使用 --init 参数来允许 Docker 自动注入 init 进程作为主进程,其会在容器关闭时候自动处理所有的派生的子进程,并且相对于完整的 sysvinit 或者 systemd 更为轻量级。如果我们希望在单个容器中运行多个进程,则可以使用 supervisord 或者自定义脚本:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY my_first_process my_first_process
COPY my_second_process my_second_process
CMD ["/usr/bin/supervisord"]
Registry
Docker 允许我们建立私有的 Registry 来存放于管理镜像,直接运行如下命令即可创建私有 Registry:
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
参考上文描述我们可知,镜像名的前缀即表示该镜像所属的 Registry 地址,因此我们可以通过 tag 方式将某个镜像推送到私有仓库:
# 拉取公共镜像
$ docker pull ubuntu:16.04
# 为镜像添加 Registry 信息
$ docker tag ubuntu:16.04 custom-domain:5000/my-ubuntu
# 将其推送到私有镜像库
$ docker push custom-domain:5000/my-ubuntu
# 从私有镜像库中拉取镜像
$ docker pull custom-domain:5000/my-ubuntu
我们也可以指定镜像库的存放地址:
-v /mnt/registry:/var/lib/registry
很多情况下我们的内部仓库并不会配置 HTTPS,如果希望以 HTTP 方式访问,那么需要在任何需要推送/拉取镜像的机器上配置非安全域名:
{ "insecure-registries": ["myregistry.example.com:5000"] }
有时候我们也需要为私有仓库配置权限认证,那么首先需要添加 TLS 支持,并且配置认证文件:
$ mkdir auth
$ docker run \
--entrypoint htpasswd \
registry:2 -Bbn cscan cscancscan > ~/auth/htpasswd
$ openssl req -new -newkey rsa:4096 -days 365 \
-subj "/CN=localhost" \
-nodes -x509 \
-keyout ~/certs/domain.key \
-out ~/certs/domain.crt
然后可以使用 Compose 文件来描述所需要的 TLS 以及 AUTH 参数:
registry-srv:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes:
- /opt/registry:/var/lib/registry
- ~/certs:/certs
- ~/auth:/auth
接下来使用 Docker Compose 命令启动服务:
$ docker-compose up -d
# 登录到镜像服务器
$ docker login myregistrydomain.com:5000
容器
Docker 中镜像是只读的,创建容器时只是在镜像上面新建一个可写层,不需要复制整个文件系统,因而可以实现毫秒级创建。
启停控制
docker create:创建一个容器但是不启动。
docker rename:允许重命名容器。
docker run:在同一个操作中创建并启动一个容器。
docker rm:删除容器。
docker update:更新容器的资源限制。
容器会在结束命令之后自动退出,使用以下的命令选项可以将容器保持在激活状态:
-i 即使在没有附着的情况下依然保持 STDIN 处于开启。单纯使用 -i 命令是不会出现 root@689d580b6416:/ 这种前缀
-t 分配一个伪 TTY 控制台
# 创建,并且启动某个容器以执行某个命令
$ docker run -ti --name container_name image_name command
# 创建,启动容器执行某个命令然后删除该容器
$ docker run --rm -ti image_name command
# 创建,启动容器,并且映射卷与端口,同时设置环境变量
$ docker run -it --rm -p 8080:8080 -v /path/to/agent.jar:/agent.jar -e JAVA_OPTS=”-javaagent:/agent.jar” tomcat:8.0.29-jre8
# 创建容器,指定网络
$ docker run --network=<NETWORK>
默认情况下,创建容器时,它不会将其任何端口发布到外部世界。要使端口可用于 Docker 之外的服务或未连接到容器网络的 Docker 容器,请使用 --publish 或 -p 标志。这会创建一个防火墙规则,将容器端口映射到 Docker 主机上的端口。
b2.png
# 启动/停止某个容器
$ docker [start|stop] container_name
# 在某个容器内执行某条命令
$ docker exec -ti container_name command.sh
# 查看某个容器的输出日志
$ docker logs -ft container_name
状态查询
docker ps 查看运行中的所有容器。
docker logs 从容器中获取日志。(你也可以使用自定义日志驱动,不过在 1.10 中,它只支持 json-file 和 journald)
docker inspect 查看某个容器的所有信息(包括 IP 地址)。
docker events 从容器中获取事件(events)。
docker port 查看容器的公开端口。
docker top 查看容器中活动进程。
docker stats 查看容器的资源使用情况统计信息。
docker diff 查看容器的 FS 中有变化文件信息。
# 根据条件过滤查询
$ docker ps --filter "name=nostalgic"
# 显示正在运行的容器列表
$ docker stats --all
管理配置
创建容器时也可以容器的重启策略,即是当容器出错退出或者宿主机重启时候,容器的应对策略;重启策略同样会保证相关联的容器以正确的顺序重启,避免意外的错误。
no:不进行重启
on-failure:当容器以非零状态码退出时重启容器
unless-stopped:当某个容器被显性关闭或者 Docker 本身关闭或重启时重启
always:无论出现任何情况都重启容器
设置重启策略
# Off, On-failure, Unless-stopped, Always
$ docker run -dit — restart unless-stopped [CONTAINER]
可以通过多种过滤条件来进行容器的移除:
# 关闭所有正在运行的容器
$ docker kill $(docker ps -q)
# 移除所有停止的容器
$ docker rm $(docker ps -a -q)
# 根据状态移除
$ docker rm $(docker ps -q -f 'status=exited')
# 根据标签移除
$ docker rm $(docker ps -a | grep rabbitmq | awk '{print $1}')
$ docker rm $(docker ps -a | grep "46 hours ago")
我们也可以对容器中的文件进行导入导出操作:
docker cp 在容器和本地文件系统之间复制文件或文件夹。
docker export 将容器的文件系统切换为压缩包(tarball archive stream)输出到 STDOUT。
资源配额
我们可以使用 docker stats 命令来查看 Docker 容器的性能状态与资源占用:
$ docker stats redis1 redis2
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
redis1 0.07% 796 KB / 64 MB 1.21% 788 B / 648 B 3.568 MB / 512 KB
Memory
$ docker run -it -m 300M ubuntu:14.04 /bin/bash
CPU
在线上环境中,我们即希望能够尽量避免 CPU 空间时间片,最大化资源利用率,也要保障重点业务的资源占用,避免因某个异常容器占用或不合理使用整机 CPU 资源,造成宿主机上大量容器异常;此时单容器的 CPU 资源约束及上限将变得非常重要,既不能限得太死,又不能限不住,将通过内核和调度系统的限制机制来保障资源稳定性。Docker 允许使用 cpus,cpuset-cpus,cpu-shares 等来限制容器的计算资源占用:
# 指定容器可占用的 CPU 核编号,0-3 表示占用四个核,1、3 表示占用两个核
$ docker run -it --cpuset-cpus="1,3" ubuntu /bin/bash
# 最多允许占用单 CPU 50% 的计算资源,如果双核 CPU,则可以设置为 1.5 等
$ docker run -it --cpus=".5" ubuntu /bin/bash
# 不同的值能够指定不同的容器权重,用于动态分配 CPU 资源
$ docker run -it --cpu-shares="512" ubuntu /bin/bash
CPU Set 保障了容器的 CPU 核数,安全性较强,但是整体资源利用率低。如果容器实际并不需要如此多核的 CPU 资源来处理任务则会造成资源浪费,并且导致其他容器上的任务无法利用该容器上的 CPU 空闲时间片,人为阻断了 CPU 闲时复用的能力。
CPU Share 允许通过共享的方式获得 CPU 资源,不同的容器共享一定总量的 CPU 计算能力,每个容器都绑定全量核,而每个容器获取一定份额的 CPU 计算力。该模式下,每个容器的 CPU 资源分配不再以整核分配,而是可精细到 CPU 时间片份额的粒度,并且是连续的 CPU 核能力值。当整机闲时,可以让较为繁忙的业务获得整机空闲。采用 CPU 资源共享的机制,其资源隔离性没有 set 模式强,对于极个别 CPU 资源敏感型业务,有可能出现偶尔等待 CPU 时间片的情况,而影响业务稳定性。对于极少数的这类业务,我们容许继续使用 CPU set 模式。
Storage
如果使用 Device Mapper 作为底层存储驱动,则可以通过 Docker daemon 的如下参数来全局限制单个容器占用空间的大小:
# 限制单个容器最多占用 20G 空间,将应用于任何新建容器。
$ --storage-opt dm.basesize=20G
如果是 btrfs 存储驱动,可使用其提供的 subvolume 功能来实现。一个容器会对应一个 subvolume。针对容器对应的 subvolume 启用并配置 quota 即可限制其磁盘空间:
$ btrfs qgroup limit -e 50G /var/lib/docker/btrfs/subvolumes/<CONTAINER_ID>
授予对单个设备访问权限:
docker run -it --device=/dev/ttyUSB0 debian bash
授予所有设备访问权限:
docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash
资源配置
Volume | 数据卷
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(Volume)中。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
数据卷可以在容器之间共享和重用
对数据卷的修改会立马生效
对数据卷的更新,不会影响镜像
卷会一直存在,直到没有容器使用
数据卷的使用,类似于 Linux 下对目录或文件进行 mount。
For example:
# the following creates a tmpfs volume called foo with a size of 100 megabyte and uid of 1000.
$ docker volume create --driver local \
--opt type=tmpfs \
--opt device=tmpfs \
--opt o=size=100m,uid=1000 \
foo
nother example that uses nfs to mount the /path/to/dir in rw mode from 192.168.1.1:
$ docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.1,rw \
--opt device=:/path/to/dir \
foo
$ docker run -d \
-it \
--name devtest \
-v myvol2:/app \
nginx:latest
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
Docker -v 标记也可以指定挂载一个本地主机的目录 / 文件到容器中去:
# 挂载目录
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py
# 挂载文件
$ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
# Docker 挂载数据卷的默认权限是读写,用户也可以通过 `:ro` 指定为只读。
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
training/webapp python app.py
注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。
VOLUME /data
Network | 网络
Linux 在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;Docker 利用这一特性,实现不容器间的网络隔离,并且引入 Veth 设备对来实现在不同网络命名空间的通信。Linux 系统包含一个完整的路由功能,当 IP 层在处理数据发送或转发的时候,会使用路由表来决定发往哪里。Netfilter 负责在内核中执行各种挂接的规则(过滤、修改、丢弃等),运行在内核模式中;Iptables 模式是在用户模式下运行的进程,负责协助维护内核中 Netfilter 的各种规则表;通过二者的配合来实现整个 Linux 网络协议栈中灵活的数据包处理机制。Docker 的网络子系统采用了基于驱动的可插拔机制,其默认包含了如下驱动模式:
bridge:默认的网络驱动,常用于多个应用运行与独立容器中并且需要相互通讯的时候。
host:移除容器与 Docker主机之间的网络隔离,直接使用宿主机所在的网络。底层与宿主机共用一个 Network Namespace,容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
Overlay:Overlay 网络用语连接多个 Docker Daemon,保证 Docker Swarm 服务的正常运行;独立的容器与 Swarm 服务,或者不同宿主机上的容器同样能够通过 Overlay 进行通信。
none:对于指定容器禁止所有的网络通信。
Macvlan:Macvlan 网络会允许直接为容器分配 MAC 地址,使其作为真正的物理设备接入到宿主机所在的网络中。
我们使用 network 命令可以查看到默认的网络:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
f707aa0ef50d bridge bridge local
97dd7a032d96 host host local
d5a1bed0b12d none null local
桥接模式下,当 Docker 启动时,会自动在主机上创建一个 docker0 虚拟网桥,即软件交换机,在挂载到它的网口之间进行转发。同时,Docker 随机分配一个本地未占用的私有网段(在 RFC1918 中定义)中的一个地址给 docker0 接口。比如典型的 172.17.42.1 ,掩码为 255.255.0.0 。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16)的地址。
桥接模式下,创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0 ;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。
# 创建新的网络
$ docker network create --driver bridge isolated
# 指定网段,宿主机会作为默认网关
$ docker network create --driver=bridge --subnet=192.168.2.0/24 --gateway=192.168.2.10 new_subnet
# 创建时将某个容器连接到网络
$ docker run --network=isolated -itd --name=docker-nginx nginx
# 将某个运行中容器连接到某个网络
$ docker network connect multi-host-network container1
DNS
默认情况下,容器从 Docker 守护进程继承 DNS 设置,包括 /etc/hosts 和 /etc/resolv.conf。可以基于每个容器覆盖这些设置。
-h HOSTNAME or --hostname=HOSTNAME 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts 。但它在容器外部看不到,既不会在 docker ps 中显示,也不会在其他的容器的 /etc/hosts 看到。
--link=CONTAINER_NAME:ALIAS 选项会在创建容器的时候,添加一个其他容器的主机名到 /etc/hosts 文件中,让新容器的进程可以使用主机名 ALIAS 就可以连接它。
--dns=IP_ADDRESS 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
--dns-search=DOMAIN 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的 主机时,DNS 不仅搜索 host,还会搜索 host.example.com 。 注意:如果没有上述最后 2 个选项, Docker 会默认用主机上的 /etc/resolv.conf 来配置容器。
空间清理
Docker 使用过程中,可能会发现宿主节点的磁盘容量持续增长,譬如 volume 或者 overlay2 目录占用了大量的空间;如果任其发展,可能将磁盘空间耗尽进而引发宿主机异常,进而对业务造成影响。
Docker 的内置 df 指令可用于查询镜像(Images)、容器(Containers)和本地卷(Local Volumes)等空间使用大户的空间占用情况。而容器的占用的总空间,包含其最顶层的读写层(writable layer)和底部的只读镜像层(base image layer,read-only),我们可以使用 ps -s 参数来显示二者的空间占用情况:
# 查看当前目录下的文件空间占用
$ du -h --max-depth=1 | sort
# 空间占用总体分析
$ docker system df
# 输出空间占用细节
$ docker system df -v
# 输出容器的空间占用
$ docker ps -s
docker system prune 指令能够进行自动地空间清理,其默认会清除已停止的容器、未被任何容器所使用的卷、未被任何容器所关联的网络、所有悬空镜像:
# 一并清除所有未使用的镜像和悬空镜像
$ docker system prune --all
# 列举悬空镜像
$ docker images -f dangling=true
# 删除全部悬空镜像
$ docker image prune
# 删除所有未被使用的镜像
$ docker image prune -a
# 删除指定模式的镜像
$ docker images -a | grep "pattern" | awk '{print $3}' | xargs docker rmi
# 删除全部镜像
$ docker rmi $(docker images -a -q)
# 删除全部停止的容器
$ docker rm $(docker ps -a -f status=exited -q)
# 根据指定模式删除容器
$ docker rm $(docker ps -a -f status=exited -f status=created -q)
$ docker rm $(docker ps -a | grep rabbitmq | awk '{print $1}')
# 删除全部容器
$ docker stop $(docker ps -a -q)
$ docker rm $(docker ps -a -q)
# 列举并删除未被使用的卷
$ docker volume ls -f dangling=true
$ docker volume prune
# 根据指定的模式删除卷
$ docker volume prune --filter "label!=keep"
# 删除未被关联的网络
$ docker network prune
$ docker network prune --filter "until=24h"
复制代码我们也可以手动指定日志文件的尺寸或者清空日志文件:
# 设置日志文件最大尺寸
$ dockerd ... --log-opt max-size=10m --log-opt max-file=3
# 清空当前日志文件
truncate -s 0 /var/lib/docker/containers/*/*-json.log
服务治理
Docker Compose
Docker Compose 是用于定义和运行复杂 Docker 应用的工具。你可以在一个文件中定义一个多容器的应用,然后使用一条命令来启动你的应用,然后所有相关的操作都会被自动完成;简单的 Compose 文件定义如下:
# 指定 Docker Compose 文件版本
version: '3'
services:
web:
# 指定从本地目录进行编译
build: .
# 指定导出端口
ports:
- '5000:5000'
# 替换默认的 CMD 命令
command: python app.py
# 将本地目录绑定到容器内目录
volumes:
- .:/code
redis:
# 镜像的 ID
image: 'redis:alpine'
这里用到的 Python Web 应用的 Dockerfile 如下:
FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
值得注意的是,我们在代码中直接使用服务名作为连接地址,即可访问到 Redis 数据库:
cache = redis.Redis(host='redis', port=6379)
然后使用 docker-compose 命令启动:
# 交互式启动
$ docker-compose up
# 守护进程式启动
$ docker-compose up -d
# 查看运行情况
$ docker-compose ps
# 关闭
$ docker-compose stop
# 移除内部卷
$ docker-compose down --volumes
在涉及到数据存储的场景下,我们同样可以指定 docker-compose 创建命名数据卷,并将其挂载到容器中:
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:
dbdata:
Docker Swarm
Swarm 是 Docker 公司在 2014 年 12 月初发布的一套较为简单的工具,用来管理 Docker 集群,它将一群 Docker 宿主机变成一个单一的,虚拟的主机。Swarm 使用标准的 Docker API 接口作为其前端访问入口,换言之,各种形式的 Docker Client(dockerclient in go,docker_py,docker 等)均可以直接与 Swarm 通信。
Swarm Deamon 只是一个调度器(Scheduler)和路由器(Router),Swarm 自己不运行容器,它只是接受 Docker 客户端发送过来的请求,调度适合的节点来运行容器,这意味着,即使 Swarm 由于某些原因挂掉了,集群中的节点也会照常运行,当 Swarm 重新恢复运行之后,它会收集重建集群信息。并且 Swarm 提供的路由匹配(服务发现、负载均衡、跨容器通讯)非常可靠。在单个端口上运行一个服务,Swarm 节点的任意主机都可以访问,负载均衡完全在后台实现。
Swarm 在 Scheduler 节点运行容器的时候,会根据指定的策略来计算最适合运行容器的节点,目前支持的策略有:Random,Binpack,Spread。
Random 顾名思义,就是随机选择一个 Node 来运行容器,一般用作调试用。Spread 和 Binpack 策略会根据各个节点的可用的 CPU,RAM 以及正在运行的容器的数量来计算应该运行容器的节点。在同等条件下,Spread 策略会选择运行容器最少的那台节点来运行新的容器,binpack 策略会选择运行容器最集中的那台机器来运行新的节点。
使用 Spread 策略会使得容器会均衡的分布在集群中的各个节点上运行,一旦一个节点挂掉了只会损失少部分的容器。Binpack 策略最大化的避免容器碎片化,就是说 Binpack 策略尽可能的把还未使用的节点留给需要更大空间的容器运行,尽可能的把容器运行在一个节点上面。
# 创建一个新的服务
$ docker service create \
--image nginx \
--replicas 2 \
nginx
# 更新服务
$ docker service update \
--image nginx:alpine \
nginx
# 删除服务
$ docker service rm nginx
# 缩容,而不是直接删除服务
$ docker service scale nginx=0
# 扩容
$ docker service scale nginx=5
# 列出所有的服务
$ docker service ls
# 列出一个服务的所有实例(包括服务的健康状况)
$ docker service ps nginx
# 服务的详细信息
$ docker service inspect nginx
我们也可以使用 Docker Compose 的脚本来进行批次部署:
$ docker stack deploy application
version: '3'
services:
web:
image: registry.gitlab.com/example/example # you need to use external image
command: npm run prod
ports:
- 80:80
deploy:
replicas: 6
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
docker配置与实践#可以好好看看的更多相关文章
- [转帖]龙芯:Docker 配置与实践清单
Docker 配置与实践清单 http://www.sohu.com/a/254904706_198222 文章对来官方文档及 Docker Links[1] 中链接内容进行归档整理,包含了日常工作中 ...
- docker 系列 - Docker CheatSheet | Docker 配置与实践清单 (转载)
本文转载自 (https://segmentfault.com/a/1190000016447161), 感谢作者.
- Docker 配置与实践清单
https://mp.weixin.qq.com/s/yeEkF5DKa9IjmIvuzOTT3g
- 微服务 + Docker + Kubernetes 入门实践 目录
微服务 + Docker + Kubernetes 入门实践: 微服务概念 微服务的一些基本概念 环境准备 Ubuntu & Docker 本文主要讲解在 Ubuntu 上安装和配置 Dock ...
- CI Weekly #6 | 再谈 Docker / CI / CD 实践经验
CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...
- 为docker配置固定ip
docker默认使用bridge模式,通过网桥连接到宿主机,而容器内部的ip则从网桥所在的ip段取未用的ip.这样做一个不方便的地方在于容器内部的ip不是固定的,想要连接容器时只能通过映射到宿主机的端 ...
- atitit.spring3 mvc url配置最佳实践
atitit.spring3 mvc url配置最佳实践 1. Url-pattern bp 1 2. 通用星号url pattern的问题 1 3. Other code 1 4. 参考 2 1. ...
- docker 配置桥接网络
2.5 docker配置桥接网络(上): 为了使本地网络中的机器和Docker 容器更方便的通信,我们经常会有将Docker容器 配置到和主机同一网段的需求. 这个需求其实很容器实现, 我们只需要将D ...
- centos为docker配置加速器
国内拉去docker镜像慢得可怜,为了解决这个问题,可为docker配置加速器. 1.修改daemon配置文件 sudo mkdir -p /etc/dockervim /etc/docker/dae ...
随机推荐
- C语言socket编程----struct sockaddr 和struct sockaddr_in介绍和初始化
sockaddr结构体 struct sockaddr{ sa_family_t sa_family; //地址族,最常用的是"AF_INET"(IPV4)和"AF_ ...
- 微信小程序开发实践
目录 项目是否适合移植到小程序上? 概要介绍 实践得到的经验 规则 小程序不支持的 新特性 小窍门 会话管理 进阶 项目是否适合移植到小程序上? 小程序由于微信提供了一些组件,在微信中的一些体验确实不 ...
- 从产品展示页面谈谈Hybris的特有概念和设计结构
今天这篇文章来自我的同事,SAP成都研究院Hybris开发团队的开发人员Zhang Jonathan(张健).需要特别介绍的是,张健和成都研究院的其他开发同事不同,张健毕业于电子科技大学,读的专业是英 ...
- pthread 的几个结构体
http://blog.csdn.net/yangzhongxuan/article/details/7397139 /* Copyright (C) 2002,2003,2004,2005,2006 ...
- MySQL:数据库入门篇3
1.sql语句逻辑执行顺序 (7) SELECT (8) DISTINCT <select_list> (1) FROM <left_table> (3) <join_t ...
- POJ 2985 名次树
题意:1~n个猫,有合并操作,有询问操作,合并两只猫所在的集合,询问第K大的集合. 分析:合并操作用并查集,用size维护,询问操作用Treap.注意优化,不能刚开始就把所有size = 1放到名次树 ...
- redux创建store,处理更新数据
如果我们想使用redux,第一步需要通过 yarn add redux 来安装redux 安装成功后就需要去创建一个store,怎么创建呢,非常的简单,在src下面创建一个文件夹,这个文件夹名字就叫做 ...
- hack-checkbox
checkbox选择按钮要用我们自己的样式,看到这个的时候,很可能会以为需要checkbox才能实现,用css可能很难.其实狠简单. <style> .checkbox input{ di ...
- Yii 判断是不是post方式提交的数据
一.在controller里判断提交是不是通过post方式: if(Yii::$app->request->isPost){ return true; }else{ return fals ...
- 【JeeSite】用户管理
组织机构使用ztree插件,加载数据时使用数据权限过滤(只能访问登录用户的单位及其下属单位), 点击部门加载相应用户. <!-- 数据范围过滤 --> BaseService.data ...