什么是 Docker
Docker是一个使用 Go 语言开发的,并且开源的应用容器引擎,基于LXC(Linux Container)内核虚拟化技术实现,提供一系列更强的功能,比如镜像、Dockerfile等;
Docker理念是将应用及依赖包打包到一个可移植的容器中,可发布到任意Linux发行版的Docker引擎上,使用沙箱机制运行程序,程序之间相互隔离;
Docker采用C/S架构,Dcoker daemon作为服务端接受来自客户端请求,并处理这些请求,比如创建、运行容器等;客户端为用户提供一系列指令与Docker Daemon交互;
LXC:Linux容器技术,共享内核,宿主机资源,使用NameSpace和Cgroups对资源限制与隔离;
Cgroups(control groups):Linux内核提供的一种限制单进程或者多进程资源的机制;比如CPU、内存等资源的使用限制;
NameSpace:命名空间,也称名字空间,Linux内核提供的一种限制单进程或者多进程资源隔离机制;一个进程可以属于多个命名空间;Linux内核提供了六种NameSpace:UTS、IPC、PID、Network、Mount和User。
AUFS(advanced multi layered unification filesystem):高级多层统一文件系统,是UFS的一种,每个branch可以指定readonly(ro只读)、readwrite(读写)和whiteout-able(wo隐藏)权限;一般情况下,aufs只有最上层的branch才有读写权限,其他branch均为只读权限。
UFS(UnionFS):联合文件系统,支持将不同位置的目录挂载到同一虚拟文件系统,形成一种分层的模型;成员目录称为虚拟文件系统的一个分支(branch);
Doker 优点与虚拟机的区别
持续集成
在项目快速迭代情况下,轻量级容器对项目快速构建、环境打包、发布等流程就能提高工作效率;版本控制
每个镜像就是一个版本,在一个项目多个版本时可以很方便管理;可移植性
容器可以移动到任意一台Linux的Docker引擎上,而不需要过多关注底层系统;标准化
应用程序环境及依赖、操作系统等问题,增加了生产环境故障率,容器保证了所有配置、依赖始终不变;隔离性与安全
容器之间的进程是相互隔离的,一个容器出现问题不会影响其他容器;
以KVM举例,与Docker对比
Ø 启动时间
Docker是秒级启动,而KVM是分钟级;
Ø 轻量级
容器镜像大小通常以M为单位,而虚拟机以G为单位;容器资源占用小,要比虚拟机部署更快速;
Ø 性能
容器共享宿主机内核,系统级虚拟化,占用资源少,没有Hypervisor层开销,容器性能基本接近物理机;而虚拟机需要Hypervisor层支持,虚拟化一些设备,具有完整的GuestOS,虚拟化开销大,因而降低性能,没有容器性能好;
Ø 安全性
由于共享宿主机内核,只是进程级隔离,因此隔离性和稳定性不如虚拟机,容器具有一定权限访问宿主机内核,存在一定安全隐患,在这一点上可能就是Doker唯一比不上的吧.....
Ø 使用要求
KVM基于硬件的完全虚拟化,需要硬件CPU虚拟化技术支持;容器共享宿主机内核,可运行在主流的Linux发行版,不用考虑CPU是否支持虚拟化技术;
应用场景
Ø 应用打包与部署自动化
构建标准化的运行环境;现在大多方案是在物理机和虚拟机上部署运行环境,面临问题是环境杂乱、完整性迁移难度高等问题,容器即开即用;
Ø 自动化测试和持续集成/部署
自动化构建镜像和良好的REST API,能够很好的集成到持续集成/部署环境来;
Ø 部署与弹性扩展
由于容器是应用级的,资源占用小,弹性扩展部署速度要更快;
Ø 微服务
Docker这种容器华隔离技术,正式应对了微服务理念,将业务模块放到容器中运行,容器的可复用性大大增加了业务模块扩展性;
Docker 的安装
# 安装依赖包yum install -y yum-utils device-mapper-persistent-data lvm2# 添加Docker软件包源yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo# 更新yum包索引yum makecache fast# 安装Docker CEyum install docker-ce# 启动systemctl start docker# 测试docker run hello-world docker version# 卸载yum remove docker-ce rm -rf /var/lib/docker
Centos6对于Docker的官方支持理论上来说只能算是勉强能用,因为只提供了基于1.7.1版本的docker-io,还得先装EPEL源,然而1.7.1版本很多功能都有缺失,至少到1.9才能算够用,具体解决方案:传送门
Docker 镜像
Docker 镜像是一个不包含Linux内核而又精简的Linux操作系统
Docker Hub公共镜像库:https://hub.docker.com/search/?q=&type=image
默认是国外的源,下载会慢,可以国内的源提供下载速度:curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://04be47cf.m.daocloud.io
镜像工作原理
当我们启动一个新的容器时,Docker会加载只读镜像,并在其之上添加一个读写层,并将镜像中的目录复制一份到/var/lib/docker/aufs/mnt/容器ID
目录下,我们可以使用chroot进入此目录,如果运行中的容器修改一个已经存在的文件,那么会将该文件从下面的只读层复制到读写层,只读层的这个文件就会覆盖,但还存在,这就实现了文件系统隔离,当删除容器后,读写层的数据将会删除,只读镜像不变;
镜像文件存储结构
Docker相关文件存放在:/var/lib/docker
/var/lib/docker/aufs/diff
目录下是每层与其父层之间的文件差异;
/var/lib/docker/aufs/layers/
目录下是每层一个文件,记录其父层一直到根层之间的ID,大部分文件的最后一行都已,表示继承来自同一层;
/var/lib/docker/aufs/mnt
目录下是联合挂载点,从只读层复制到最上层可读写层的文件系统数据在建立镜像时,每次写操作,都被视作一种增量操作,即在原有的数据层上添加一个新层;所以一个镜像会有若干个层组成;每次commit提交就会对产生一个ID,就相当于在上一层有加了一层,可以通过这个ID对镜像回滚;
镜像管理的相关命令
#模糊查询镜像库的镜像,docker search [镜像名称]docker search centos#下载镜像库的镜像,docker pull [镜像名称]docker pull centos #查询本地的镜像信息,docker imagesdocker images#将修改的内容保存为一个新的镜像,docker commit [容器名] [镜像名]{:[标签]}docker commit test centos:self #删除本地镜像,docker rmi [镜像名]{:[标签]}docker rmi mysql#导出容器的文件系统生成一个tar包,docker export [容器名] > [导出保存的路径]docker export test_self_image > test_self_image.tar#将tar包导入到本地镜像,docker import [tar包名] [镜像名]{:[标签]}docker import test_self_image.tar centos:v1.0.0#将镜像导出为tar包,docker save [镜像名]{:[标签]}docker save nginx > nginx.tar#将tar包导入到本地镜像库,docker load -i [tar包名]docker load -i nginx.tar
docker save保存的是镜像(image)
docker export保存的是容器(container)
Docker 容器管理的相关命令
创建容器命令格式:
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Options(常用选项):
-i,--interactive:标准输入;
-t,--tty:分配伪终端;
-d,--detach:容器到后台运行;--add-host list:添加hosts解析;
--cap-add list:添加内核访问控制权限;
--cap-drop list:去除内核访问控制权限;
--cidfile string:容器ID写入到PID文件路径;
--device list:添加宿主的设备到容器;
--dns list:指定dns解析服务器地址;
-e,--env list:设置变量到容器中;
--env-file list:指定变量文件路径;
--expose list:申明容器提供的端口;
-h,--hostname string:设置容器主机名;
--ip string:设置容器主机IP地址;
--log-driver:指定容器日志格式(none、json-file、syslog、fluentd、splunk);
--network string:连接容器到指定网络;
--oom-kill-disable:禁用自动检测进程资源占用过大而kill掉该容器;
-p,--publish list:端口映射;
--restart:尝试重新启动,比如--restart on-failure:3挂掉后尝试重启3次;
--ulimit ulimit:设定最大进程数和最大文件打开数;
-w,--workdir string:进入容器后所在的工作目录;
-m,--memory:硬限制容器运行所使用的最大内存值;
--memory-reservation bytes:软限制容器运行所使用的最大内存值;
--memory-swappiness int:设置是否使用Swap交换内存;
#查看容器docker ps -als#进入一个容器,docker attach [容器名]docker attach mydocker 或 docker exec -it mydocker /bin/bash#删除容器,docker rm [容器名],加上-f 选项则表示强制删除docker rm mydocker#启动容器,docker start [容器名]docker start mydocker#停止容器,docker stop [容器名]docker stop mydocker#杀死容器进程docker kill [容器名]docker kill mydocker#挂起容器,docker pause/unpause [容器名]docker pause mydocker docker unpause mydocker#重命名容器,docker rename [原容器名] [新容器名]docker rename mydocker idocker#查看容器所有信息,docker inspect [容器名]docker inspect idocker#容器中执行命令,docker exec [容器名] [命令]docker exec idocker ls /home/#查看容器中运行的进程,docker top [容器名]docker top idocker#查看容器的端口映射,docker port [容器名]docker port idocker#拷贝文件,docker cp [拷贝的文件] [存放的位置]docker cp pass.txt idocker:/home docker cp idocker:/home/pass.txt /tmp/pass_idocker.txt#查看容器自启动后的变更,docker diff [容器名]docker diff idocker#查看容器标准输出日志,docker logs [容器名]docker logs idocker#动态查看容器利用率,docker stats [容器名],加上--no-stream选项则表示输出一次docker stats idocker#及时修改跟新容器配置,docker update [容器名]docker update idocker#查看Docker主机事件docker events
删除所有容器:docker rm -f $(docker ps -q -a)
容器数据持久化
数据卷
将宿主机目录挂载到容器目录
数据卷特点:
Ø 在容器启动初始化时,如果容器使用的宿主机挂载点有数据,这些数据就会拷贝到容器中。
Ø 数据卷可以在容器直接共享和重用。
Ø 可以直接对数据卷里的内容进行修改。
Ø 数据卷的变化不会影响镜像的更新。
Ø 卷会一直存在,即使挂载数据卷的容器已经删除。
示例:
docker run -itd --name docker_web -v /container_data/web:/data centos
/container_data/web为宿主机目录,/data是容器中目录,目录不存在会自动创建
容器数据卷
将一个运行的容器作为数据卷,让其他容器通过挂载这个容器实现数据共享。
示例:
docker run -itd -v /data --name docker_data centos
docker run -itd --name docker_web --volumes-from docker_data centos
搭建WoedPress LNMP Docker
# 创建mysql数据库容器docker run -itd --name mysql -p 3333:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql --character-set-server=utf8# 创建wp数据库docker exec mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e"create database wp"'#查看数据库是否创建成功docker exec mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e"show databases;"'# 创建PHP环境容器docker run -itd --name web --link mysql:db -p 8080:80 -v /container_data/web:/app webdevops/php-nginx# 以wordpress博客为例测试wget https://cn.wordpress.org/wordpress-4.7.4-zh_CN.tar.gz tar zxf wordpress-4.7.4-zh_CN.tar.gz mv wordpress/* /container_data/web/
浏览器测试访问
http://IP:8080
Docker 网络
网络模式
先创建一个docker0的网桥,使用veth pair创建一对虚拟网卡,一端放到新创建的容器中,并重命名eth0,另一端放到宿主机上,以veth+随机7个字符串命名,并将这个网络设备加入到docker0网桥中,网桥自动为容器分配一个IP,并设置docker0的IP为容器默认网关;所以容器默认网络都加入了这个网桥,因此都可以彼此通信;同时在iptables添加SNAT转换网络段IP,以便容器访问外网;
Ø bridge
默认网络,Docker启动后创建一个docker0网桥,默认创建的容器也是添加到这个网桥中;IP地址段是172.17.0.1/16;
Ø host
容器不会获得一个独立的network namespace,而是与宿主机共用一个;
Ø none
获取独立的network namespace,但不为容器进行任何网络配置;
Ø container
与指定的容器使用同一个network namespace,网卡配置也都是相同的;
Ø 自定义
自定义网桥,默认与bridge网络一样;
容器网络访问原理
Docker主要通过Netfilter/Iptables实现网络通信;iptables由netfilter和iptables组成,Netfilter组件是Linux内核集成的信息包过滤系统,它维护一个信息包过滤表,这个表用于控制信息包过滤处理的规则集;而Iptables只是一个在用户空间的工具,用于增删改查这个过滤表的规则;
容器访问外部
[root@docker ~]# iptables -t nat -nLChain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 MASQUERADE tcp -- 172.17.0.3 172.17.0.3 tcp dpt:33060MASQUERADE tcp -- 172.17.0.4 172.17.0.4 tcp dpt:80
外部访问容器
[root@docker ~]# iptables -t nat -nLChain DOCKER (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3333 to:172.17.0.3:33060DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.4:80
桥接宿主机网络与配置固定IP
桥接宿主机网络
#关掉docker0ifconfig docker0 down#删除dockerbrctl delbr docker0#增加网桥br0yum install bridge-utils #配置网桥vim /etc/sysconfig/network-scripts/ifcfg-br0 TYPE=Bridge DEVICE=br0 BOOTPROTO=static ONBOOT=yesIPADDR=192.168.0.152NETMASK=255.255.255.0GATEWAY=192.168.0.1DNS1=8.8.8.8#配置网卡vim /etc/sysconfig/network-scripts/ifcfg-eno16777736 TYPE="Ethernet"DEVICE="eno16777736"NAME="eno16777736"BOOTPROTO="none"ONBOOT="yes"BRIDGE="br0"#重启网络systemctl restart network#查看物理机上有哪些网桥brctl show bridge name bridge id STP enabled interfaces br0 8000.000c2984bb70 no eno16777736
配置Docker桥接网络
#方案一:自定义脚本 docker_auto.shvim docker_auto.shread -p "Please Enter Your Docker Name:" dockernameread -p "Please Enter Your Docker Image Name:" dkimgread -p "Please Enter Your Docker IP Address:" dockeripread -p "Please Enter Your Docker IP GW:" dockergwread -p "Please Enter Your Network BG Name:" bgname#配置固定IPC_ID=$(docker run -itd --net=none --name ${dockername} ${dkimg}) C_PID=$(docker inspect -f '{{.State.Pid}}' $C_ID)# 创建network namespace目录并将容器的network namespace软连接到此目录,以便ip netns命令读取mkdir -p /var/run/netns ln -s /proc/$C_PID/ns/net /var/run/netns/$C_PID# 添加虚拟网卡veth+容器PID,类型是veth pair,名称是vp+容器PIDip link add veth$C_PID type veth peer name vp$C_PID# 添加虚拟网卡到br0网桥brctl addif $bgname veth$C_PID# 激活虚拟网卡ip link set veth$C_PID up# 给进程配置一个network namespaceip link set vp$C_PID netns $C_PID# 在容器进程里面设置网卡信息ip netns exec $C_PID ip link set dev vp$C_PID name eth0ip netns exec $C_PID ip link set eth0 up ip netns exec $C_PID ip addr add ${dockerip}/24 dev eth0ip netns exec $C_PID ip route add default via $dockergw echo $dockername $dkimg $dockerip $dockergw $bgname $C_ID $C_PID#方案二:使用pipework工具git clone https://github.com/jpetazzo/pipework.git cp pipework/pipework /usr/local/bin/ docker run -itd --net=none --name idocker2 centos pipework br0 idocker2 192.168.0.88/24@192.168.0.1
容器SSH连接
docker run -itd --name idocker3 centosdocker attach idocker3 yum install openssh-serverpasswd root docker commit idocker3 centos_ssh docker run -itd --name idocker3 -p 2222:22 centos_ssh
Docker File
常用命令
指令 | 描述 |
---|---|
FROM | 构建的新镜像是基于哪个镜像; 例如:FROM centos:6 |
MAINTAINER | 镜像维护者姓名或邮箱地址; 例如:MAINTAINER lizhenliang |
RUN | 构建镜像时运行的Shell命令 例如:RUN [“yum”, “install”, “httpd”] RUN yum install httpd |
CMD | 运行容器时执行的Shell命令; 例如:CMD [“-c”, “/start.sh”] CMD ["/usr/sbin/sshd", "-D"] CMD /usr/sbin/sshd –D |
EXPOSE | 声明容器运行的服务端口; 例如:EXPOSE 80 443 |
ENV | 设置容器内环境变量; 例如:ENV MYSQL_ROOT_PASSWORD 123456 |
ADD | 拷贝文件或目录到镜像,如果是URL或压缩包会自动下载或自动解压 ADD <src>… <dest> ADD [“<src>”,… “<dest>”] ADD https://xxx.com/html.tar.gz /var/www/html ADD html.tar.gz /var/www/html |
COPY | 拷贝文件或目录到镜像,用法同上; 例如:COPY ./start.sh /start.sh |
ENTRYPOINT | 运行容器时执行的Shell命令; 例如:ENTRYPOINT [“/bin/bash", “-c", “/start.sh"] ENTRYPOINT /bin/bash -c ‘/start.sh’ |
VOLUME | 指定容器挂载点到宿主机自动生成的目录或其他容器; 例如:VOLUME ["/var/lib/mysql"] |
USER | 为RUN、CMD和ENTRYPOINT执行命令指定运行用户 USER <user>[:<group>] or USER <UID>[:<GID>] 例如:USER lizhenliang |
WORKDIR | 为RUN、CMD、ENTRYPOINT、COPY和ADD设置工作目录; 例如:WORKDIR /data |
HEALTHCHECK | 健康检查; HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ exit 1 |
ARG | 在构建镜像时指定一些参数; 例如:FROM centos:6 ARG user # ARG user=root USER $user # docker build --build-arg user=lizhenliang Dockerfile. |
RUN、CMD和ENTRYPOINT指令区别
1.RUN在building时运行,可以写多条;
2.CMD和ENTRYPOINT在运行container时运行,只能写一条,如果写多条,最后一条生效;
3.CMD在run时可以被COMMAND覆盖,ENTRYPOINT不会被COMMAND覆盖,但可以指定—entrypoint覆盖;
Build镜像构建
使用Dockerfile文件构建镜像
Usage: docker build [OPTIONS] PATH | URL | - Options: -t, --tag list # 镜像名称-f, --file string # 指定Dockerfile文件位置示例: docker build . # 默认找当前目录以Dockerfile为命名的文件docker build -t shykes/myapp . docker build -t shykes/myapp -f /path/Dockerfile /path docker build -t shykes/myapp - < Dockerfile docker build -t shykes/myapp - < context.tar.gz docker build -t shykes/myapp http://www.example.com/Dockerfiledocker build -f shykes/myapp http://www.example.com/contex.tar.gz
构建 PHP 网站环境镜像
#Dockerfiler文件配置FROM centos:6MAINTAINER Mingo RUN yum install -y httpd php php-gd php-mysql mysql mysql-server ENV MYSQL_ROOT_PASSWORD 123456RUN echo "<?php phpinfo(); ?>" > /var/www/html/index.php COPY start.sh /start.sh RUN chmod +x /start.sh ADD https://cn.wordpress.org/wordpress-4.7.4-zh_CN.tar.gz /var/www/htmlCOPY wp-config.php /var/www/html/wordpress VOLUME ["/var/lib/mysql"] CMD /start.sh EXPOSE 80 3306vim start.sh service httpd start service mysqld start mysqladmin -uroot password $MYSQL_ROOT_PASSWORD tail -f
tail -f主要是为了进程阻塞,防止掉进程
构建 JAVA 网站环境镜像
FROM centos:6MAINTAINER Mingo COPY jdk-8u45-linux-x64.tar.gz /usr/localENV JAVA_HOME /usr/local/jdk1.8.0_45ADD http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.0.45/bin/apache- tomcat-8.0.45.tar.gz /usr/localWORKDIR /usr/local/apache-tomcat-8.0.45ENTRYPOINT ["bin/catalina.sh", "run"] EXPOSE 8080
构建支持 SSH 服务镜像
FROM centos:6 MAINTAINER Mingo ENV ROOT_PASSWORD 123456 RUN yum install -y openssh-serverRUN echo $ROOT_PASSWORD |passwd --stdin rootRUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key CMD ["/usr/sbin/sshd", "-D"] EXPOSE 22