docker学习(五)

扶醉桌前 提交于 2019-12-11 16:37:41

一、实战案例
介绍一些典型的应用场景和案例。

1.使用Supervisor来管理进程
Docker 容器在启动的时候开启单个进程,比如,一个 ssh 或者 apache 的 daemon 服务。但我们经常需要在一个机器上开启多个服务,这可以有很多方法,最简单的就是把多个启动命令方到一个启动脚本里面,启动的时候直接启动这个脚本,另外就是安装进程管理工具。

本小节将使用进程管理工具 supervisor 来管理容器中的多个进程。使用 Supervisor 可以更好的控制、管理、重启我们希望运行的进程。在这里我们演示一下如何同时使用 ssh 和 apache 服务。

1.1配置
首先创建一个 Dockerfile,内容和各部分的解释如下。
FROM ubuntu:13.04
MAINTAINER examples@docker.com
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get upgrade -y

1.2安装supervisor
安装 ssh、apache 和 supervisor。
RUN apt-get install -y openssh-server apache2 supervisor
RUN mkdir -p /var/run/sshd
RUN mkdir -p /var/log/supervisor
这里安装 3 个软件,还创建了 2 个 ssh 和 supervisor 服务正常运行所需要的目录。
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
添加 supervisord 的配置文件,并复制配置文件到对应目录下面。
EXPOSE 22 80
CMD ["/usr/bin/supervisord"]
这里我们映射了 22 和 80 端口,使用 supervisord 的可执行路径启动服务。

1.3supervisor配置文件内容
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND"
配置文件包含目录和进程,第一段 supervsord 配置软件本身,使用 nodaemon 参数来运行。第二段包含要控制的 2 个服务。每一段包含一个服务的目录和启动这个服务的命令。

1.4使用方法
创建镜像。
$ sudo docker build -t test/supervisord .
启动 supervisor 容器。
$ sudo docker run -p 22 -p 80 -t -i test/supervisords
2013-11-25 18:53:22,312 CRIT Supervisor running as root (no user in config file)
2013-11-25 18:53:22,312 WARN Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
2013-11-25 18:53:22,342 INFO supervisord started with pid 1
2013-11-25 18:53:23,346 INFO spawned: 'sshd' with pid 6
2013-11-25 18:53:23,349 INFO spawned: 'apache2' with pid 7
使用 docker run 来启动我们创建的容器。使用多个 -p 来映射多个端口,这样我们就能同时访问 ssh和 apache 服务了。
可以使用这个方法创建一个只有 ssh 服务的基础镜像,之后创建镜像可以使用这个镜像为基础来创建

2.创建tomcat/weblogic集群
2.1安装tomcat镜像
准备好需要的 jdk、tomcat 等软件放到 home 目录下面,启动一个容器
docker run -t -i -v /home:/opt/data --name mk_tomcat ubuntu /bin/bash
这条命令挂载本地 home 目录到容器的 /opt/data 目录,容器内目录若不存在,则会自动创建。接下来就是tomcat 的基本配置,jdk 环境变量设置好之后,将 tomcat 程序放到 /opt/apache-tomcat 下面 编辑/etc/supervisor/conf.d/supervisor.conf 文件,添加 tomcat 项
[supervisord]
nodaemon=true
[program:tomcat]
command=/opt/apache-tomcat/bin/startup.sh
[program:sshd]
command=/usr/sbin/sshd -D
docker commit ac6474aeb31d tomcat
新建 tomcat 文件夹,新建 Dockerfile。
FROM mk_tomcat
EXPOSE 22 8080
CMD ["/usr/bin/supervisord"]
根据 Dockerfile 创建镜像。
docker build tomcat tomcat

2.2安装weblogic镜像
步骤和 tomcat 基本一致,这里贴一下配置文件
supervisor.conf
[supervisord]
nodaemon=true
[program:weblogic]
command=/opt/Middleware/user_projects/domains/base_domain/bin/startWebLogic.sh
[program:sshd]
command=/usr/sbin/sshd -D
dockerfile
FROM weblogic
EXPOSE 22 7001
CMD ["/usr/bin/supervisord"]

3.tomcat/weblogic镜像的使用
在启动的时候,使用 -v 参数
-v, --volume=[] Bind mount a volume (e.g. from the host: -v /host:/container, from docker: 将本地磁盘映射到容器内部,它在主机和容器之间是实时变化的,所以我们更新程序、上传代码只需要更新物理主机的目录就可以了

3.1tomcat和weblogic集群的实现
tomcat 只要开启多个容器即可
docker run -d -v -p 204:22 -p 7003:8080 -v /home/data:/opt/data --name tm1 tomcat /usr/bin/supervisord
docker run -d -v -p 205:22 -p 7004:8080 -v /home/data:/opt/data --name tm2 tomcat /usr/bin/supervisord
docker run -d -v -p 206:22 -p 7005:8080 -v /home/data:/opt/data --name tm3 tomcat /usr/bin/supervisord
这里说一下 weblogic 的配置,大家知道 weblogic 有一个域的概念。如果要使用常规的 administrator+node 的方式部署,就需要在 supervisord 中分别写出 administartor server 和 node server 的启动脚本,
这样做的优点是:
a.可以使用 weblogic 的集群,同步等概念
b.部署一个集群应用程序,只需要安装一次应用到集群上即可
缺点是:
a.Docker 配置复杂了
b.没办法自动扩展集群的计算容量,如需添加节点,需要在 administrator 上先创建节点,然后再配置新的容器 supervisor 启动脚本,然后再启动容器 另外种方法是将所有的程序都安装在 adminiserver 上面,需要扩展的时候,启动多个节点即可,它的优点和缺点和上一种方法恰恰相反。(建议使用这种方式来部署开发和测试环境)
docker run -d -v -p 204:22 -p 7001:7001 -v /home/data:/opt/data --name node1 weblogic /usr/bin/supervisord
docker run -d -v -p 205:22 -p 7002:7001 -v /home/data:/opt/data --name node2 weblogic /usr/bin/supervisord
docker run -d -v -p 206:22 -p 7003:7001 -v /home/data:/opt/data --name node3 weblogic /usr/bin/supervisord
这样在前端使用 nginx 来做负载均衡就可以完成配置了

4.多台物理主机之间的容器互联(暴露容器到真实网络中)
Docker 默认的桥接网卡是 docker0。它只会在本机桥接所有的容器网卡,举例来说容器的虚拟网卡在主机上看一般叫做 veth* 而 Docker 只是把所有这些网卡桥接在一起,如下:
[root@opnvz ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.56847afe9799 no veth0889
veth3c7b
veth4061
在容器中看到的地址一般是像下面这样的地址:
root@ac6474aeb31d:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
11: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 4a:7d:68:da:09:cf brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::487d:68ff:feda:9cf/64 scope link
valid_lft forever preferred_lft forever
这样就可以把这个网络看成是一个私有的网络,通过 nat 连接外网,如果要让外网连接到容器中,就需要做端口映射,即 -p 参数。
如果在企业内部应用,或者做多个物理主机的集群,可能需要将多个物理主机的容器组到一个物理网络中来,那么就需要将这个网桥桥接到我们指定的网卡上。

4.1拓扑图
主机 A 和主机 B 的网卡一都连着物理交换机的同一个 vlan 101,这样网桥一和网桥三就相当于在同一个物理网络中了,而容器一、容器三、容器四也在同一物理网络中了,他们之间可以相互通信,而且可以跟同一vlan 中的其他物理机器互联。
多台物理主机之间的容器互联(暴露容器到真实网络中)

 

 


4.2ubuntu示例
下面以 ubuntu 为例创建多个主机的容器联网: 创建自己的网桥,编辑 /etc/network/interface 文件
auto br0
iface br0 inet static
address 192.168.7.31
netmask 255.255.240.0
gateway 192.168.7.254
bridge_ports em1
bridge_stp off
dns-nameservers 8.8.8.8 192.168.6.1
将 Docker 的默认网桥绑定到这个新建的 br0 上面,这样就将这台机器上容器绑定到 em1 这个网卡所对应的物理网络上了。
ubuntu 修改 /etc/default/docker 文件,添加最后一行内容
# Docker Upstart and SysVinit configuration file
# Customize location of Docker binary (especially for development testing).
#DOCKER="/usr/local/bin/docker"
# Use DOCKER_OPTS to modify the daemon startup options.
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
# If you need Docker to use an HTTP proxy, it can also be specified here.
#export http_proxy="http://127.0.0.1:3128/"
# This is also a handy place to tweak where Docker's temporary files go.
#export TMPDIR="/mnt/bigdrive/docker-tmp"
DOCKER_OPTS="-b=br0"
在启动 Docker 的时候 使用 -b 参数 将容器绑定到物理网络上。重启 Docker 服务后,再进入容器可以看到它已经绑定到你的物理网络上了。
root@ubuntudocker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS ubuntu 示例
58b043aa05eb desk_hz:v1 "/startup.sh" 5 days ago Up 2 seconds 5900/root@ubuntudocker:~# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.7e6e617c8d53 no em1
vethe6e5
这样就直接把容器暴露到物理网络上了,多台物理主机的容器也可以相互联网了。需要注意的是,这样就
需要自己来保证容器的网络安全了。

5.标准化开发测试和生产环境
对于大部分企业来说,搭建 PaaS 既没有那个精力,也没那个必要,用 Docker 做个人的 sandbox 用处又小了点。
可以用 Docker 来标准化开发、测试、生产环境。

 

 


Docker 占用资源小,在一台 E5 128 G 内存的服务器上部署 100 个容器都绰绰有余,可以单独抽一个容器或者直接在宿主物理主机上部署 samba,利用 samba 的 home 分享方案将每个用户的 home 目录映射到开发中心和测试部门的 Windows 机器上。

针对某个项目组,由架构师搭建好一个标准的容器环境供项目组和测试部门使用,每个开发工程师可以拥有自己单独的容器,通过 docker run -v 将用户的 home 目录映射到容器中。需要提交测试时,只需要将代码移交给测试部门,然后分配一个容器使用 -v 加载测试部门的 home 目录启动即可。这样,在公司内部的开发、测试基本就统一了,不会出现开发部门提交的代码,测试部门部署不了的问题。

测试部门发布测试通过的报告后,架构师再一次检测容器环境,就可以直接交由部署工程师将代码和容器分别部署到生产环境中了。这种方式的部署横向性能的扩展性也极好。

二、安全
评估 Docker 的安全性时,主要考虑三个方面:
a.由内核的名字空间和控制组机制提供的容器内在安全
b.Docker程序(特别是服务端)本身的抗攻击性
c.内核安全性的加强机制对容器安全性的影响

1.内核名字空间
Docker 容器和 LXC 容器很相似,所提供的安全特性也差不多。当用 docker run 启动一个容器时,在后台 Docker 为容器创建了一个独立的名字空间和控制组集合。

名字空间提供了最基础也是最直接的隔离,在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用。

每个容器都有自己独有的网络栈,意味着它们不能访问其他容器的 sockets 或接口。不过,如果主机系统上做了相应的设置,容器可以像跟主机交互一样的和其他容器交互。当指定公共端口或使用 links 来连接 2个容器时,容器就可以相互通信了(可以根据配置来限制通信的策略)。

从网络架构的角度来看,所有的容器通过本地主机的网桥接口相互通信,就像物理机器通过物理交换机通信一样。

那么,内核中实现名字空间和私有网络的代码是否足够成熟?

内核名字空间从 2.6.15 版本(2008 年 7 月发布)之后被引入,数年间,这些机制的可靠性在诸多大型生产系统中被实践验证。

实际上,名字空间的想法和设计提出的时间要更早,最初是为了在内核中引入一种机制来实现 OpenVZ 的特性。 而 OpenVZ 项目早在 2005 年就发布了,其设计和实现都已经十分成熟。

2.控制组
控制组是 Linux 容器机制的另外一个关键组件,负责实现资源的审计和限制。

它提供了很多有用的特性;以及确保各个容器可以公平地分享主机的内存、CPU、磁盘 IO 等资源;当然,更重要的是,控制组确保了当容器内的资源使用产生压力时不会连累主机系统。

尽管控制组不负责隔离容器之间相互访问、处理数据和进程,它在防止拒绝服务(DDOS)攻击方面是必不可少的。尤其是在多用户的平台(比如公有或私有的 PaaS)上,控制组十分重要。例如,当某些应用程序表现异常的时候,可以保证一致地正常运行和性能。

控制组机制始于 2006 年,内核从 2.6.24 版本开始被引入。

3.Docker服务端的防护
运行一个容器或应用程序的核心是通过 Docker 服务端。Docker 服务的运行目前需要 root 权限,因此其安全性十分关键。

首先,确保只有可信的用户才可以访问 Docker 服务。Docker 允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限,这就容易让容器突破资源限制。例如,恶意用户启动容器的时候将主机的根目录 / 映射到容器的 /host 目录中,那么容器理论上就可以对主机的文件系统进行任意修改了。这听起来很疯狂?但是事实上几乎所有虚拟化系统都允许类似的资源共享,而没法禁止用户共享主机根文件系统到虚拟机系统。

这将会造成很严重的安全后果。因此,当提供容器创建服务时(例如通过一个 web 服务器),要更加注意进行参数的安全检查,防止恶意的用户用特定参数来创建一些破坏性的容器

为了加强对服务端的保护,Docker 的 REST API(客户端用来跟服务端通信)在 0.5.2 之后使用本地的Unix 套接字机制替代了原先绑定在 127.0.0.1 上的 TCP 套接字,因为后者容易遭受跨站脚本攻击。现在用户使用 Unix 权限检查来加强套接字的访问安全。

用户仍可以利用 HTTP 提供 REST API 访问。建议使用安全机制,确保只有可信的网络或 VPN,或证书保护机制(例如受保护的 stunnel 和 ssl 认证)下的访问可以进行。此外,还可以使用 HTTPS 和证书来加强保护。

最近改进的 Linux 名字空间机制将可以实现使用非 root 用户来运行全功能的容器。这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题。

终极目标是改进 2 个重要的安全特性:
a.将容器的 root 用户映射到本地主机上的非 root 用户,减轻容器和主机之间因权限提升而引起的安全问题;
b.允许 Docker 服务端在非 root 权限下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程将只允许在限定范围内进行操作,例如仅仅负责虚拟网络设定或文件系统管理、配置操作等。

最后,建议采用专用的服务器来运行 Docker 和相关的管理服务(例如管理服务比如 ssh 监控和进程监控、管理工具 nrpe、collectd 等)。其它的业务服务都放到容器中去运行。

4.内核能力机制
能力机制(Capability)是 Linux 内核一个强大的特性,可以提供细粒度的权限访问控制。 Linux 内核自2.2 版本起就支持能力机制,它将权限划分为更加细粒度的操作能力,既可以作用在进程上,也可以作用在文件上。

例如,一个 Web 服务进程只需要绑定一个低于 1024 的端口的权限,并不需要 root 权限。那么它只需要被授权 net_bind_service 能力即可。此外,还有很多其他的类似能力来避免进程获取 root 权限。

默认情况下,Docker 启动的容器被严格限制只允许使用内核的一部分能力。

使用能力机制对加强 Docker 容器的安全有很多好处。通常,在服务器上会运行一堆需要特权权限的进程,包括有 ssh、cron、syslogd、硬件管理工具模块(例如负载模块)、网络配置工具等等。容器跟这些进程是不同的,因为几乎所有的特权进程都由容器以外的支持系统来进行管理。
a.ssh 访问被主机上ssh服务来管理;
b.cron 通常应该作为用户进程执行,权限交给使用它服务的应用来处理;
c.日志系统可由 Docker 或第三方服务管理;
d.硬件管理无关紧要,容器中也就无需执行 udevd 以及类似服务;
e.网络管理也都在主机上设置,除非特殊需求,容器不需要对网络进行配置。

从上面的例子可以看出,大部分情况下,容器并不需要“真正的” root 权限,容器只需要少数的能力即可。为了加强安全,容器可以禁用一些没必要的权限。
a.完全禁止任何 mount 操作;
b.禁止直接访问本地主机的套接字;
c.禁止访问一些文件系统的操作,比如创建新的设备、修改文件属性等;
d.禁止模块加载。
这样,就算攻击者在容器中取得了 root 权限,也不能获得本地主机的较高权限,能进行的破坏也有限。
默认情况下,Docker采用 白名单 机制,禁用 必需功能 之外的其它权限。 当然,用户也可以根据自身需求来为 Docker 容器启用额外的权限。

除了能力机制之外,还可以利用一些现有的安全机制来增强使用 Docker 的安全性,例如 TOMOYO,
AppArmor, SELinux, GRSEC 等。
Docker 当前默认只启用了能力机制。用户可以采用多种方案来加强 Docker 主机的安全,例如:
a.在内核中启用 GRSEC 和 PAX,这将增加很多编译和运行时的安全检查;通过地址随机化避免恶意探测等。并且,启用该特性不需要 Docker 进行任何配置。
b.使用一些有增强安全特性的容器模板,比如带 AppArmor 的模板和 Redhat 带 SELinux 策略的模板。这些模板提供了额外的安全特性。
c.用户可以自定义访问控制机制来定制安全策略。
跟其它添加到 Docker 容器的第三方工具一样(比如网络拓扑和文件系统共享),有很多类似的机制,在不改变 Docker 内核情况下就可以加固现有的容器。

总结
总体来看,Docker 容器还是十分安全的,特别是在容器内不使用 root 权限来运行进程的话。
另外,用户可以使用现有工具,比如 Apparmor, SELinux, GRSEC 来增强安全性;甚至自己在内核中实现更复杂的安全机制。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!