上图所示,容器技术相较于VM少了客户机系统这一层;底层用到了linux的LXC的容器技术,所以docker目前只能运行于linux的64位系统之上(能运行32位系统也是做过处理的,降低效率),官方提供了运行于windows上的工具,但是这个工具其实是集成一个微型的virtubox虚拟机,说白了还是得需要linux系统。
Docker之所以火的很快,原因之一就是它的仓库。
仓库类似于github,很多现成的镜像供各位下载,直接用docker pull redis-server(这个名字是瞎起的)就可以拉下来一个redis服务器的镜像,放到自己的docker里就能用了,不用自己解决依赖、安装redis。好多开源工具都有docker镜像,这就使得用户只需要下载使用就行,完全省去了处理软件依赖、缺少库文件、安装部署等环节。
总的来说docker不如openstack强大,但是docker灵活、简单、占资源更少。
Docker适合微服务,一般一个docker容器只跑一个进程,连ssh服务都不要开启;像数据库这种大且重要的服务最好别用docker来跑,快慢放一边,万一有个bug就麻烦了。
用docker来搭建测试环境、开发环境等都可以提高效率,还占用很少物理资源;docker其实就是轻量的虚拟机,虚拟机能干的都交给docker也行,但是要结合业务场景来看。
拿咱们公司的订单服务来说,业务高峰时订单服务变慢,那可以起20个docker都跑订单服务,但假如起20个虚拟机来跑的话,抛开启动速度不说,启动瞬间可能出现IO风暴,导致磁盘IO特别高,直接影响服务质量。
首先要说明的是,据传言ubuntu运行docker更快,我没有细研究过,在ubuntu和centos上也都试过docker,没感觉有什么区别(可能是测试的比较浅)。看过一些资料,没觉得docker在ubuntu上有什么优越的,那就更熟悉哪个用哪个吧,所以下面主要以centos为宿主系统进行测试。
在centos下,如果有epel源直接yum install docker-io就完成安装了。
/etc/init.d/docker start启动docker。
sudo apt-get remove debian-keyringdebian-archive-keyring
sudo apt-get clean
sudo apt-get update && sudo apt-get-y install debian-keyring debian-archive-keyring
wget -qO- https://get.Docker.com/ | sh
注:如果ubuntu启用了ufw,要配置使其能转发请求,并允许2375端口访问外网。
前文说过,docker之所以这么火,就是因为它有一个类似于github的dockerhub,上面存放着很多现成的镜像,有官方的和非官方的等等。
dockersearch centos查询centos镜像,
与github类似,直接pull就可以把镜像拉下来,
dockerpull centos
可能要多pull几次才能成功。
下载的centos镜像才170多M,所以很多你认为该有的命令它是没有的,有个心理准备。
docker search |
搜索镜像 |
docker pull |
获取镜像 |
docker images |
查看本地已有镜像 |
docker rm |
删除镜像 |
docker ps |
查看容器 |
docker images:查看本地有什么镜像,比如有centos,有ubuntu,有redis,有elk。
docker ps –a: 命令能查看所有运行的、停止的镜像的命令状态,包含容器名称、镜像名、操作命令等。
docker ps –l: 查看最后一个容器的信息。
镜像就类似于VCenter里的镜像,容器就类似于一个真实的VMware WorkStation,容器就是通过镜像来创建的。
run的作用:它首先检测有没有对应的镜像名,如果没有就自动pull,如果有就利用这个镜像来启动一个容器;
-i:进入镜像;
-t:启动一个tty终端;
/bin/bash:启动docker容器执行/bin/bash这个进程。
输入上图命令就会进入一个运行着/bin/bash进程的docker容器,进入后ps aux一下,发现只有一个/bin/bash进程,如下,
在当前容器里执行exit就退出了/bin/bash进程,同时这个docker容器也就停止了。
重新启动一个已经停止的容器:docker startcontainer-id。
注:每一个docker容器并不是只能运行一个进程,但最好配置为只运行一个进程,不然还不如用虚拟机呢;既然容器只运行一个进程,那当这个进程结束后,docker容器也就停止了。
“—name name1”:指定容器名称为name1;
“-h hostname1”:指定容器主机名为hostname1。
启动一个后台运行的docker容器(这个进程必须支持后台运行):
如图所示,提示本地没有nginx的docker镜像,所以它先下载nginx镜像然后再运行,然后会在后台启动一个nginx进程,必须把nginx配置为前台模式(daemon off;),不然这个容器启动一下nginx就马上退出了,前文讲过进程结束容器也就退出了。
正在运行的容器是不能直接用rm删除的,会提示加-f强制删除或者根据容器id来删。
正确的做法是先停止容器,docker stop container-id,然后再rm删除。目前(2016-09-22)是没法根据什么状态、什么参数来批量删除容器,只能一个个的停止、删除,但是可以通过下述命令变相做到全部删除,
docker rm `docker ps –a -q`
-a:列出所有容器。
-q:只返回容器ID。
可以用docker attach container-id来进入一个后台容器,但是可能会进不去,直接卡住,不得不用ctrl+c来退出,但是ctrl+c后也就结束了这个容器了;
网上查着说,进入后台容器后,可以用ctrl+P+Q来退出,但是我试了试退不出来。。。
用nsenter命令来进入后台容器更合适一点,nsenter是linux自带的命令,可以进入一个命名空间的功能,命令如下:
使用nsenter进入容器后,直接用exit命令退出容器。
为了之后的操作方便,可以将上面的两条命令放到脚本里,一执行脚本就进入镜像了。
[root@salt-master docker-scripts]# cat/root/docker-scripts/mynginx.sh
#!/bin/bash
CNAME=$1
CPID=$(docker inspect --format"``.`State`.`Pid`" $CNAME)
nsenter --target $CPID --mount --uts --ipc--net --pid
查看到mynginx里的nginx的root目录在“root /usr/share/nginx/html;”。
注:可靠资料说nsenter一般适用于docker1.2或者更早的版本,但是我在最新的版本(1.10)用着挺好,所以我一直用着这个;docker exec命令是在docker 1.3中引入的,推荐使用这个。
第二种,假如宿主有多个ip的话,让容器的端口绑定到宿主的固定一个ip的端口上;
第三种,只绑定宿主ip,端口随机取;
第四种,它的意思是一条docker run命令可以同时用多次”-p”参数,指定多个端口映射关系,比如,docker run –d –p 99:80 –p 33:443 –name mynginx33 nginx
启动容器时用-P(大写)参数,表示让容器随机对应一个宿主机的端口,如上,容器的80端口对应宿主的32768端口,容器的443对应宿主的32769端口。
比如我的宿主ip是192.168.0.55,这时候在宿主机的浏览器输入192.168.0.55:32768就可以访问到docker容器里的nginx应用了。
端口随机映射的好处是:端口不会冲突。难点是,生产中还得用脚本或者其他方式获取到这个随机的端口,由于它是变化的,所以脚本必须得适应这种变化性。
用小p来手动指定端口映射,如上所示,将容器的80端口映射到宿主的91端口上,而443没有指定映射关系,所以443不可访问。
下面开始练习。
dockerrun -it --name volume-test1 -h centos-01 -v /data centos ;创建一个名字是volume-test1,以centos系统为基础镜像的容器,它的主机名是centos-01,把这个容器的/data目录挂载到宿主机上。
重新开一个SecureCRT session,然后输入docker inspect -f``.`Volumes` volume-test1,就会看到下面的:
map[/data:/var/lib/docker/volumes/a6d633be7da3ca959a45328f9d95f9b7db7c1f7ff9012a51ed85a0bdb7f98b94/_data]
它相当于是一个字典,把容器的/data映射到宿主的/var/lib/docker/volumes/a6d633be7da3ca959a45328f9d95f9b7db7c1f7ff9012a51ed85a0bdb7f98b94/_data。
在容器的/data目录下新建一个文件,可以在/var/lib/docker/volumes/a6d633be7da3ca959a45328f9d95f9b7db7c1f7ff9012a51ed85a0bdb7f98b94/_data里看到;
同样,在宿主的
/var/lib/docker/volumes/a6d633be7da3ca959a45328f9d95f9b7db7c1f7ff9012a51ed85a0bdb7f98b94/_data里创建一个文件,也能在容器的/data目录下看到。
这个跟VMware的共享目录一模一样,一对儿目录映射关系。
dockerrun -it --name volume-test2 -h centos-02 -v /opt/centos-2:/data centos
dockerrun -it --name volume-test2 -h centos-02 -v /opt/centos-2:/data:ro centos ,这里的ro是让容器只读不可写。
上面用-v参数来建立容器和宿主的磁盘映射关系,其实是违背了docker的“一处部署,到处运行”的原则,上面的方法在Dockerfile(后文会讲到)里是不支持的。
但是这样指定磁盘映射也有好处,比如收集docker容器里的进程日志,假如在容器里再开一个logstash就不符合docker运行单进程的原则,变得臃肿,所以在所有docker容器里建一个log目录,然后都统一映射到宿主的/opt/docker-log里,然后在宿主起一个logstash进程,通过/opt/docker-log这个目录就能收集到所有docker进程的日志了。
数据卷容器的作用实现了各容器之间的数据共享,容器不论起停都不影响数据共享,如下,
新建的容器volume-test4共享了容器volume-test1的数据卷,所以新创建的volume-test4容器就有/data这个目录,并且这个目录下有刚才创建的文件。
构建镜像的话,现以官方的centos docker镜像为基础镜像来创建容器,进入容器后一顿yum或者一顿make,这样镜像就完成了一半了,再提交就行了。
下面实例构建一个nginx的容器。
① docker pull centos
② docker run –it –namenginx-man-construct centos
③ 在centos容器里,安装编译安装nginx所需的依赖包,
[root@962aee9a1846 /]# yum -y install wget gcc gcc-c++openssl-devel make
④ 进入/usr/local/src,下载pcre,解压pcre(不用安装,一会安装nginx时指定pcre的解压目录即可)
⑤ 建立nginx的运行用户,useradd -s /sbin/nologin -M www;
⑥ 下载nginx的tar包,wget http://nginx.org/download/nginx-1.10.1.tar.gz,解压进入nginx目录,./configure--prefix=/usr/local/nginx-1.10 --user=www --group=www --with-http_ssl_module--with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.39 &&make && make install。
⑦ 设置nginx开机自启动,将/usr/local/nginx/sbin/nginx加到/etc/rc.local里(这是一个坑,后面会讲到),但是这样加在平常的系统(物理机或虚拟机)里是可以自启动的,但是在容器里不行,前面说过,必须让进程运行在前台,不然容器仅仅执行一个…./nginx的启动命令就以为结束任务了,然后就退出容器了,所以要让nginx在前台运行,修改nginx.conf,在顶部加一条“daemon off;”即可。
⑧ yum clean all。然后退出容器,exit
⑨ 提交镜像,
做完上面的步骤就创建并提交完docker镜像了,下面我们执行镜像,
docker run -d -p 92:80zhangshanci/my-nginx:v1 ,执行了这条命令,用docker ps –l查看容器状态是退出的,这是怎么回事呢?不是说设置了开机自启并且daemon off就可以了嘛,其实是把“/usr/local/nginx/sbin/nginx”加到/etc/rc.local后也不会自启动nginx,需要手动启动。我们进入镜像删除/etc/rc.local的“/usr/local/nginx/sbin/nginx”,docker run –it zhangshanci/my-nginx:v1;然后退出镜像,重新commit一次,dockercommit -m 'create www user' 6df7168a3eda zhangshanci/my-nginx:v3,然后docker run–d –p 92:80 zhangshanci/my-nginx:v3 /usr/local/nginx-1.10/sbin/nginx,再docker ps –l就发现容器没有退出。
注1:修改镜像后要重新commit;构建镜像也可以添加自启动命令或者参数,后面的构建Dockerfile会讲到。
由Dockerfile自动构建镜像,其实就是把手动构建的命令逐行写入一个“脚本”里,只不过这个脚本有自己的语法。
现把上面手动创建nginx镜像的结果写成dockerfile形式,你会发现超级简单。
这里是创建Dockerfile时会用到的语法,下面进行实际创建。
① mkdir -p /opt/docker-file/nginx&& cd /opt/docker-file/nginx
② vim Dockerfile (开头字母必须大写),内容见下:
# This is my first Dockerfile
# version 1.0
# Author: shanci zhang
#Base images
FROM centos
#MAINTAINER
MAINTAINER zhangci
#ADD使用这个命令拷贝文件或目录(压缩包会自动解压);必须把文件拷贝到Dockerfile同目录下
ADD pcre-8.39.tar.gz /usr/local/src
ADD nginx-1.10.1.tar.gz /usr/local/src
#RUN
RUN yum install -y wget gcc gcc-c++ makeopenssl-devel
RUN useradd -s /sbin/nologin -M www
#WORKDIR 相当于cd,切换到目录
WORKDIR /usr/local/src/nginx-1.10.1
RUN ./configure--prefix=/usr/local/nginx-1.10 --user=www --group=www --with-http_ssl_module--with-http_stb_module --with-pcre=/usr/local/pcre-8.39http_stb_module--with-pcre=/usr/local/pcre-8.39 && make && make install
RUN echo "daemon off;" >>/usr/local/nginx-1.10/conf/nginx.conf
ENV PATH /usr/local/nginx-1.10/sbin:$PATH
#对外开放80端口
EXPOSE 80
#容器启动时自动执行“nginx”命令
CMD ["nginx"]
③ pcre-8.39.tar.gz和nginx-1.10.1.tar.gz拷贝到与Dockerfile的同级目录下。
④ 构建镜像,docker build -t nginx-dockerfile:v1 /opt/docker-file/nginx/ (-t后面跟的是镜像名字,最后的路径是Dockerfile的路径)
⑤ 无需commit就能查到这个镜像了。
目前通过加参数可以做到cpu和内存的资源限制,但centos6还无法限制磁盘,centos7可以限制磁盘配额。
通过压力测试来实验限制cpu资源;在linux下使用压测工具:stress。
有epel源(epel6可以,epel7好像不行stress)的情况下,yum install -y stress即可完成安装。需要在容器里安装压测工具,目的是压测容器而不是压测宿主机。
我们先写一个Dockerfile来创建一个镜像:
vim /opt/docker-file/stress-container/Dockerfile
FROM centos
ADD epel-6.repo /etc/yum.repos.d/
RUN yum -y install stress && yumclean all
ENTRYPOINT ["stress"]
注:ENTRYPOINT ["stress"]这一行表示启动容器后,容器立刻执行stress命令并接收参数;在有这个参数的情况下,docker run -it --rm stress1 --cpu 4 这条进入容器的命令相当于docker run -it --rm stress1 stress --cpu 4,加上一个ENTRYPOINT就省去了每次启动容器都加这个命令了。
把epel-6.repo下载到/opt/docker-file/stress-container目录下,然后,docker build-t stress /opt/docker-file/stress-container。
docker run –help可以查看dockerrun可以添加的参数,
-c参数:指定该容器占用宿主机cpu的权重,也可以叫配额,默认是有1024个配额,也就是满配额。
比如宿主机只有一个容器,同时该容器的配额是1024,那么启动这个容器时就可以占用宿主机的100%的cpu;宿主机同样只有一个容器,该容器的配额是512(-c 512),那么启动这个容器最多只能占用宿主机的50%的cpu。
比如有两个容器,他俩的cpu配额都是1024,那么启动这两个容器时就会各占50%的cpu。
比如有两个容器,A容器的cpu配额是1024,B容器的cpu配额是512,那么启动这两个容器时A容器占用66%的cpu,B容器占用33%的cpu。
实际测验:
docker run -it --rm stress1 --cpu 1,这样就启动了一个装有stress压测工具的容器。
--rm是说容器结束后自动删除容器,--cpu 1是说占用一个cpu核心来执行stress的压测实验。
运行上面的命令后,在宿主机用top查看cpu情况,发现宿主机的cpu被这个容器占用了25%,因为我的宿主机是4核的。
结束上面的容器,然后docker run -it --rm stress1 --cpu 4,运行这个命令后,发现宿主机出来了四个stress进程,每个都占用25%,一共占用了100%的宿主机cpu。
注1:这里的宿主机是相对于docker来说的宿主机,这个宿主机也可能是物理机或者虚拟机。
注2:安装stress压测工具的目的是让容器把它所能利用的cpu资源跑满,看看能占宿主机的百分之多少;stress这个工具会一直做运算,直到cpu跑满。
也可以通过—cpuset-cpus指定使用哪个cpu来运行容器,docker run –it –cpuset-cpus=0 stress,用第一个cpu来运行容器stress。
上面用-c和—cpuset-cpus来指定cpu配额和使用哪个cpu,下面来限制内存使用。
docker run -it -m 128m --rm stress1 --cpu 8 --io 4 --vm 2--vm-bytes 500M --timeout 10s ,“-m 128m”设置stress1这个容器最大可使用宿主机128M内存,压测工具stress指定的500M的内存测试,所以随着压力增大,容器使用宿主机的内存一旦超过了128M就会触发异常并退出。
Docker容器默认使用桥接模式。
目前起着四个容器,对应四个桥接网络,如下图,
启动docker后会创建一个虚拟网桥(用brctl show命令查看),每启动一个容器都会创建一个桥接接口,
宿主机和容器之间通过iptables实现端口的转换、容器上网,如下,
每个docker容器可以选择不同的网络模式。
host模式是容器不使用network啊、namespace什么的,直接和宿主机使用同一个网络堆栈,
这样的话容器起一个80端口,宿主机就起一个80端口,容器起一个655端口,宿主机就起一个655端口,缺点的话容器与宿主机的ip范围、端口范围等统统一样;优点就是方便,假如容器的端口与宿主机没冲突,那就可以用。
docker pull registry
docker run –d –p 5005:5000 registry。registry的默认运行端口是5000,如果本机5000端口被占用了,可以指定一个别的端口。
docker tag zhangshanci/my-nginx:v3192.168.0.55:5005/test/nginx:v1 ,
“zhangshanci/my-nginx:v3”是镜像的名称,“192.168.0.55:5005/test/nginx:v1”是要存到仓库里的名称。
高版本的docker在push时要求有ssl数字证书,官网有三种解决办法:
① 购买一个ssl证书,在所有docker容器前端配置一个nginx做反向代理;
② 将下图红圈里的参数加到/etc/sysconfig/docker的启动参数里;
③ 自己制作一个证书,不过还是得加参数,不如方法二方便还。
修改/etc/sysconfig/docker文件,添加启动参数:
注:这个参数也必须添加到要pull的机器上。
重新push镜像:
这样push上去后,别的机器可以再pull拉下来,但是必须指定仓库的名称,如果不指定仓库名称,默认是从docker hub上pull镜像。
在192.168.0.30上,这是centos7略有不同。
安装:yum install docker-io(有epel源前提下)
启动:systemctl start docker
这是一个装有nginx的镜像,以它为镜像启动一个容器,
docker run -it -p :80 30ff66a25ec4/usr/local/nginx-1.10/sbin/nginx
这样iptables –t nat –L -n就能看到本地有一个端口与容器的80端口对应,打开浏览器,输入ip:port就可以访问到容器里的nginx了。