Docker是PASS【Platform As A Service】提供商dotCloud开源的一个基于LXC
【Linux Container】的高级容器引擎,源代码托管在Github上。基于go语言并遵从Apache2.0协议开源
Docker的主要目标是“Build and Run Any App, Anywhere“,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP及其运行环境能够做到“一次封装,到处运行”。
传统的开发部署协作方式
Docker的开发部署协作方式
虚拟机 (VM) 是一个物理硬件层抽象,用于将一台服务器变成多台服务器。管理程序允许多个 VM 在一台机器上运行。每个 VM 都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此占用大量空间。而且 VM 启动也十分缓慢。
容器是一个应用层抽象,用于将代码和依赖资源打包在一起。多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行。与虚拟机相比,容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完成启动。
镜像(Image)
镜像就是一个只读模板。镜像可以用来创建Docker容器,一个镜像可以创建多个容器。
容器(Container)
Docker利用容器独立运行一个或一组应用。容器是镜像创建的运行示例。它可以被启动、停止、删除。每个容器都是相互隔离的、保证安全的平台。
仓库(Repository)
仓库是集中存放镜像文件的场所。仓库和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库又包含了多个镜像,每个镜像又不同的标签(tag)。
注:Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包形成一个可交付的运行环境,这个打包好的运行环境就是镜像文件。只有通过这个镜像文件才能生成Docker容器。镜像文件可以看作是容器的模板。Docker根据镜像文件生成容器的实例。同一个镜像文件,可以生成多个同时运行的容器实例。
第一步:卸载原Docker组件,确认安装了gcc、gcc-c++
#卸载旧docker组件
yum remove docker docker-client docker-client-latest docker-common docker-latest \
docker-latest-logrotate docker-logrotate docker-selinux \
docker-engine-selinux docker-engine
#安装依赖
yum -y install gcc gcc-c++
第二步:安装Docker仓库
#安装必要的一些系统工具
yum install -y yum-utils device-mapper-persistent-data lvm2
#添加软件源信息,使用阿里云下载
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
第三步:更新、安装、开启Docker服务
#更新并安装Docker-CE
yum makecache fast
yum -y install docker-ce
#开启Docker服务
systemctl start docker
第四步:配置镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["自己的阿里云加速器地址"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
第五步:运行hello-world镜像验证是否安装成功
docker run hello-world
列出镜像 docker images
参数 | 描述 |
---|---|
-a | 列出本地主机上的镜像(包括中间层镜像) |
-q | 列出本地主机上的镜像ID |
搜索镜像
docker search [镜像名]
参数 | 描述 |
---|---|
–filter=stars=3 | 列出点赞数不小于指定值的镜像 |
–no-trunc | 显示完整镜像描述 |
拉取/删除镜像
docker pull/rmi [镜像名]:[Tag]
小技巧:docker rmi -f $(docker images -qa) 删除所有镜像
提交容器作为镜像
docker -a=”作者名” -m=”描述信息” commit [容器ID] 名称:Tag标签
保存【导出】镜像
docker save [镜像名]:[Tag] -o /path/[name].tar
加载镜像
docker load -i [name].tar
运行容器命令 docker run [镜像名]:[Tag]
参数 | 描述 |
---|---|
-i | 以交互模式运行容器,通常与-t同时使用 |
-t | 为容器重新分配一个伪输入终端,通常与-i同时使用 |
-p | 指定映射端口,形式hostPort:ContainerPort |
-P | 随机端口映射 |
-d | 后台运行容器 |
–name | 为容器指定名称 |
–restart=always | docker服务启动容器就启动 |
-h name | 设置主机名 |
–add-host name:ip | 注入hostname的IP解析 |
–rm | 容器停止时自动删除 |
–link cname:hostName | 向容器的/etc/hosts添加另一个IP地址的映射形式 |
退出容器方法
查看容器进程 docker ps
参数 | 描述 |
---|---|
-l | 上次运行的容器 |
-a | 所有运行过的容器 |
-q | 静默只显示容器编号 |
启动/重启/停止容器
docker start/restart/stop [容器ID]
强制停止容器
docker kill [容器ID]
删除关闭的容器
docker rm [容器ID]
参数 | 描述 |
---|---|
-f | 强制删除 |
小技巧:docker rm -f $(docker ps -qa) 删除所有容器
查看容器日志 docker logs [容器ID]
参数 | 描述 |
---|---|
-f | 一直追加,显示最新日志 |
-t | 显示每条日志时间戳 |
查看容器运行进程信息
docker top [容器ID]
查看容器内部细节
docker inspect [容器ID]
查看容器占用的系统资源
docker stats [容器ID]
不加容器ID则查看所有容器的系统资源
重新进入容器
docker attach [容器ID]
进入容器执行Shell命令
docker exec -it [容器id Shell命令]
拷贝容器内文件
docker cp [容器ID]:容器路径 本机路径
容器运行机制
Docker容器后台运行,就必须有一个前台进程,容器运行的命令如果不是会挂起的命令(比如top),会自动退出。最佳的解决方案是,将要运行的程序以前台进程的形式运行
Docker Compose是容器编排工具,允许用户在一个模板(YAML格式)中定义一组相关联的容器,对启动的优先级进行排序。
命令 | 解释 |
---|---|
-f | 指定YAML文件位置 |
ps | 显示容器所有信息 |
start/stop/restart | 启动/停止/重启容器【已加载成为容器】 |
logs | 查看日志信息 |
config -q | 验证YAML是否正确 |
up -d | 启动容器项目【未加载成为容器】 |
pause | 暂停容器 |
unpause | 恢复暂停 |
rm | 删除容器 |
简单示例:
version: '3'
services:
db:
image: 'mysql:5.7'
restart: 'always'
environment:
MYSQL_ROOT_PASSWORD: 'root'
MYSQL_DATABASE: 'wordpress'
MYSQL_USER: 'wordpress'
MYSQL_PASSWORD: 'wordpress'
wordpress:
depends_on:
- db
image: 'wordpress:latest'
restart: 'always'
ports:
- '8080:80'
environment:
WORDPRESS_DB_HOST: 'db:3306'
WORDPRESS_DB_USER: 'wordpress'
WORDPRESS_DB_PASSWORD: 'wordpress'
联合文件系统是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层一层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分成来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特征:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
docker的镜像实际上由一层一层的文件系统组成即联合文件系统。
bootfs(boot file system)主要包含bootloader和kernel, bootloader 主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在 Docker镜像的最底层是bootfs.这一层与我们典型的Linux/Unix系统是一一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权己由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs (root file system),在bootfs之 上.包含的就是典型Linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。rootfs就是 各种不同的操作系统发行版,比如Ubuntu, Centos等等 。
对于一个精简的OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用宿主机的kernel,自己只需要提供rootfs就行了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs 。
注意:docker在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来只像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。当我们创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。
容器数据卷的作用是数据共享和数据持久化。卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过联合文件系统,提供一些用于持续存储或共享数据的功能。
卷的设计目的就是持久化,完全独立于容器的生命周期,因此docker不会在容器删除时删除其挂载的数据卷。如果没有指定映射卷位置(容器数据卷)将会在/var/lib/docker/volumes
下创建默认容器管理数据卷
容器卷具有以下特点:
挂载完成可以使用docker inspect命令查看相关数据卷挂载信息
使用命令
docker run [options] -v [宿主机绝对路径]:[docker容器绝对路径] 镜像ID 挂载容器数据卷
docker run [options] -v [宿主机绝对路径]:[docker容器绝对路径]:[ro] 镜像ID 只读模式挂载
指定宿主机路径,目的时为了读写数据
若不指定宿主机路径,则docker自动创建一个目录,这样使用是为了共享数据
注意:当宿主机删除与容器挂载的目录时,容器的挂载目录变为只读。即使宿主机恢复容器的挂载目录也无效
使用DockerFile文件的VOLUME命令创建容器管理数据卷
VOLUME [“docker容器绝对路径”,”docker容器绝对路径”]
只会在容器内创建,宿主机目录由docker自行创建,目的时为了数据共享
数据卷容器
命名的容器挂载数据卷,其它容器通过挂在这个父容器实现数据共享,挂载的容器被称为数据卷容器
docker run [options] –volumes-from 数据卷容器名称 镜像名称
关键字 | 描述 |
---|---|
FROM | 基础镜像,当前新镜像时基于哪个镜像 |
LABEL | 镜像维护者的信息 |
RUN | 执行的SHELL命令 |
EXPOSE | 暴露服务的端口,只起显示作用 |
WORKDIR | 终端登陆之后的工作目录 |
ENV | 设置环境变量 |
ADD | 把文件拷贝到镜像并解压【必须和Dockerfile在同一级目录下,不用加绝对路径】 |
COPY | 把文件拷贝到镜像【必须和Dockerfile在同一级目录下,不用加绝对路径】 |
VOLUME | 创建数据容器卷 |
CMD | 指定容器启动时要运行的命令,可以有多个CMD命令,但只有最后一个生效,CMD会被docker run 之后的参数替换 |
ENTRYPOINT | 指定容器启动时要运行的命令,不会覆盖只会追加 |
ONBUILD | 当构建一个被继承的Dockerfile时运行的命令,父镜像在被子镜像继承时触发 |
#在centos上架构
FROM centos
#作者信息
LABEL org.kun.vendor="WangYuKun" org.kun.build-date="20181218"
#设置环境变量,会存在于容器环境变量当中
ENV USERLOCAL /usr/local
#安装vim
RUN yum install -y vim
#设置登陆终端时的交互目录
WORKDIR ${USERLOCAL}
#启动执行的命令
CMD ["/bin/bash"]
#在centos上架构
FROM centos
#安装curl
RUN yum install -y curl
#执行时可以在后追加参数,例如docker run [镜像名] -i 相当于 curl -s https://ip.cn
ENTRYPOINT ["curl","-s","https://ip.cn"]
#执行不可以追加参数相当于覆盖操作
#CMD ["curl","-s","https://ip.cn"]
FROM centos
RUN yum install -y curl
#在子类构建镜像时会触发,注意ONBUILD仅仅时触发器,后面需要跟保留关键字起作用,不能直接使用命令
ONBUILD RUN ["echo","onbuild tigger"]
FROM centos
#拷贝jdk压缩包并解压
ADD jdk-8u191-linux-x64.tar.gz /opt/
RUN mv /opt/jdk1.8.0_191 /opt/jdk8
#配置java环境变量
ENV PATH=$PATH:/opt/jdk8/bin
ENV JAVA_HOME=/opt/jdk8
#拷贝tomcat压缩包并解压
ADD apache-tomcat-8.5.35.tar.gz /opt/
RUN mv /opt/apache-tomcat-8.5.35 /opt/tomcat
ENV CATALINA_HOME=/opt/tomcat
#设置工作目录
WORKDIR $CATALINA_HOME
#设置要暴露的端口
EXPOSE 8080
#启动tomcat并打印日志
CMD /opt/tomcat/bin/startup.sh start && tail -f /opt/tomcat/logs/catalina.out
仓库端
docker run -d -v /data/registry:/var/lib/registry -p 5000:5000 --restart=always registry
/etc/docker/deamon.json
】文件{
"insecure-registries":["仓库端IP:5000"]
}
systemctl restart docker
客户端
/etc/docker/deamon.json
】文件{
"insecure-registries":["仓库端IP:5000"]
}
systemctl restart docker
# 查看镜像
curl -XGET http://registrIP:5000/v2/_catalog
# 查看具体镜像版本
curl -XGET http://registrIP:5000/v2/image_name/tags/list
# 命令格式docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[/libraryName][:TAG]
# 指定仓库需要加libraryName,官方搭建方式不能指定仓库,harbor可以创建仓库
# 例如:docker tag hello-world:latest 192.169.1.149:5000/hello-world:v1.0
docker tag SOURCE_IMAGE:[TAG] registrIP:5000[/userName]/TARGET_IMAGE:[TAG]
# 上传镜像到仓库
# 私有仓库登陆才能推送
# docker login ip/hostname
# 例如:docker push 192.168.1.149:5000/hello-world:v1.0
docker push registrIP:5000[/userName]/TARGET_IMAGE:[TAG]
Harbor构建需要先安装python、compose。harborGithub地址
# 构建证书
openssl genrsa -des3 -out server.key 2048
openssl req -new -key server.key -out server.csr
# 脱密,证书退密钥
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
# 证书有效期
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.csr
# 将证书移置指定目录
mkdir /data/cert
chown -R 777 /data/cert
cp server.* /data/cert
harbor.yml
# 修改hostname项
hostname: [hostname]
# 屏蔽HTTP协议
#http:
# port: 80
# 开放https协议
https:
port: 443
certificate: /data/cert/server.csr
private_key: /data/cert/server.key
./install.sh
https://ip/harbor/projects
,默认账户:admin,密码:Harbor12345客户端配置(非进行公网认证过的SSL证书)
/etc/docker/deamon.json
】文件{
"insecure-registries":["registryIP"]
}
systemctl restart docker
打包示例
docker tag hello-world:latest www.kun.com/library/hello-world:v1.0
推送示例
先登录:
docker login www.kun.com
docker push www.kun.com/library/hello-world:v1.0
容器与容器之间通讯使用docker的网桥(类似交换机作用)进行相互之间铜须
容器访问外部网络使用SNAT转换
外部网络访问容器使用DNAT转换
进程网络修改【不常用】
-b,–bridge=”“:指定Docker使用的网桥设备,默认情况下Docker会自动创建和使用docker0网桥设备,通过此参数可以设置已经存在的设备
–bip:指定Docker0的IP和掩码,使用标准的CIDR格式如10.10.10.10/24
–dns:配置容器的DNS,在启动Docker进程时添加,所有容器全部生效
容器网络修改
端口暴露方式
p/P 选项的使用格式
docker port [ontainerName] 可以查看容器当前的映射关系
第一步:创建独立network namespace
docker network create -d bridge \
[--subnet "172.26.10.0/16"] \
[--gateway "172.26.10.1"] \
[netName]
第二步:运行镜像是指定network
docker run -d --network=[netName] [imageName]
查看namespace:
docker network ls
默认情况下Docker创建的容器会尽可能是用完操作系统内存,当服务异常时可能会导致崩溃,所以生产环境下CPU和内存必须加以限制。Dockers底层使用CGroup进行资源限制。
Linux内核有OOME机制【Out Of Memory Exception】
一旦发生OOME,任何进程都有可能被杀死,包括docker daemon
在内,为此docker调整了docker daemon
的OOM优先级,以免被内核关闭。
-m,–memory 容器能使用的最大内存大小,单位M
–memory-swap 容器可使用的swap大小
容器内使用free看到的swap空间不具有真实含义
Docker提供的CPU资源限制选项可以在多核系统上限制容器能利用哪些vCPU,而对容器最多能使用的CPU时间 有两种限制方式:①设置各个容器能使用的CPU时间相对比例。②以绝对的方式设置容器在每个调度周期内最多能使用的时间
cpu-quota
联用cpu-period
联用docker run -it –cpu-period=10000 –cpu-quota=20000 centos /bin/bash #代表使用两块CPU
#拉取MySQL
docker pull mysql:5.7
#注意外部访问需要修改root权限,docker mysql配置文件再/etc/mysql目录下,注意查看内容
docker run -p3306:3306 \
-v /root/mysql/log/mysqld.log:/logs \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/conf/my.cnf:/etc/mysql/my.conf \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
#拉取redis
docker pull redis
#启动redis,注意redis.conf中的dir要有权限,bind注释掉,protected-mode改为no
docker run -p6379:6379 \
-v /root/redis/conf/redis.conf:/usr/local/etc/redis.conf \
-v /root/redis/data:/data \
--privileged=true
-it redis redis-server /usr/local/etc/redis.conf
Linux的container技术在内核层面由两个独立的机制保证,一个保证资源的隔离性,名为namespace,一个进行资源的控制,名为cgroup。
namespace
namespace是对全局系统资源的一种封装隔离,使得处于不同namespace的进程拥有独立的全局系统资源,改变一个namespace中的系统资源只会影响当前namespace里的进程,对其他namespace中的进程没有影响。
Linux现有的namespace有6种: uts[主机名], pid, ipc[进程通信], mnt[挂载点], net和user
cgroup
cgroup是Linux下的一种将进程按组进行管理的机制,在用户层看来,cgroup技术就是把系统中的所有进程组织成一颗一颗独立的树,每棵树都包含系统的所有进程,树的每个节点是一个进程组,而每颗树又和一个或者多个subsystem关联,树的作用是将进程分组,而subsystem的作用就是对这些组进行操作。
subsystem
一个subsystem就是一个内核模块,他被关联到一颗cgroup树之后,就会在树的每个节点(进程组)上做具体的操作。subsystem经常被称作”resource controller”,因为它主要被用来调度或者限制每个进程组的资源【比如限制CPU的使用时间,限制使用的内存,统计CPU的使用情况,冻结和恢复一组进程等】
hierarchy
一个hierarchy可以理解为一棵cgroup树,树的每个节点就是一个进程组,每棵树都会与零到多个subsystem关联。在一颗树里面,会包含Linux系统中的所有进程,但每个进程只能属于一个节点(进程组)。系统中可以有很多颗cgroup树,每棵树都和不同的subsystem关联,一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的subsystem关联。
容器状态转换图