什么是 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);
Docker 优点与虚拟机的区别
- 持续集成
在项目快速迭代情况下,轻量级容器对项目快速构建、环境打包、发布等流程就能提高工作效率; - 版本控制
每个镜像就是一个版本,在一个项目多个版本时可以很方便管理; - 可移植性
容器可以移动到任意一台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 CE
yum install docker-ce
# 启动
systemctl start docker
# 测试
docker run hello-world
docker version
# 卸载
yum remove docker-ce
rm -rf /var/lib/docker
Docker 镜像
Docker 镜像是一个不包含Linux内核而又精简的Linux操作系统
Docker Hub公共镜像库:https://hub.docker.com/search/?q=&type=image
镜像工作原理
当我们启动一个新的容器时,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 images
docker 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 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 run -itd --name docker_web -v /container_data/web:/data centos
容器数据卷
将一个运行的容器作为数据卷,让其他容器通过挂载这个容器实现数据共享。
示例:
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 -nL
Chain 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:33060
MASQUERADE tcp -- 172.17.0.4 172.17.0.4 tcp dpt:80
外部访问容器
[root@docker ~]# iptables -t nat -nL
Chain 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:33060
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.4:80
桥接宿主机网络与配置固定IP
桥接宿主机网络
#关掉docker0
ifconfig docker0 down
#删除docker
brctl delbr docker0
#增加网桥br0
yum install bridge-utils
#配置网桥
vim /etc/sysconfig/network-scripts/ifcfg-br0
TYPE=Bridge
DEVICE=br0
BOOTPROTO=static
ONBOOT=yes
IPADDR=192.168.0.152
NETMASK=255.255.255.0
GATEWAY=192.168.0.1
DNS1=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.sh
vim docker_auto.sh
read -p "Please Enter Your Docker Name:" dockername
read -p "Please Enter Your Docker Image Name:" dkimg
read -p "Please Enter Your Docker IP Address:" dockerip
read -p "Please Enter Your Docker IP GW:" dockergw
read -p "Please Enter Your Network BG Name:" bgname
#配置固定IP
C_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+容器PID
ip 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 namespace
ip link set vp$C_PID netns $C_PID
# 在容器进程里面设置网卡信息
ip netns exec $C_PID ip link set dev vp$C_PID name eth0
ip netns exec $C_PID ip link set eth0 up
ip netns exec $C_PID ip addr add ${dockerip}/24 dev eth0
ip 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 centos
docker attach idocker3
yum install openssh-server
passwd 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 ADD [“ 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 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. |
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/Dockerfile
docker build -f shykes/myapp http://www.example.com/contex.tar.gz
构建 PHP 网站环境镜像
#Dockerfiler文件配置
FROM centos:6
MAINTAINER Mingo
RUN yum install -y httpd php php-gd php-mysql mysql mysql-server
ENV MYSQL_ROOT_PASSWORD 123456
RUN 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/html
COPY wp-config.php /var/www/html/wordpress
VOLUME ["/var/lib/mysql"]
CMD /start.sh
EXPOSE 80 3306
vim start.sh
service httpd start
service mysqld start
mysqladmin -uroot password $MYSQL_ROOT_PASSWORD
tail -f
构建 JAVA 网站环境镜像
FROM centos:6
MAINTAINER Mingo
COPY jdk-8u45-linux-x64.tar.gz /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_45
ADD http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.0.45/bin/apache-
tomcat-8.0.45.tar.gz /usr/local
WORKDIR /usr/local/apache-tomcat-8.0.45
ENTRYPOINT ["bin/catalina.sh", "run"]
EXPOSE 8080
构建支持 SSH 服务镜像
FROM centos:6
MAINTAINER Mingo
ENV ROOT_PASSWORD 123456
RUN yum install -y openssh-server
RUN echo $ROOT_PASSWORD |passwd --stdin root
RUN 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