Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。—— 百度百科
之前一直有一个误区,老听人说 Docker 是一个容器,应用程序打包好镜像,然后在这个容器中运行,以此达到 “一次封装,处处运行”(Build,Ship and Run Any App,Anywhere)的目的。其实不然,Docker 只是一个应用程序引擎(引擎的基础是Linux自带的容器(Linux Containers,LXC)技术),然后每一个进程都是一个容器 —— 运行于 Docker引擎上的容器。
Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
Client 和 Server可以运行在同一台集群,也可以通过跨主机实现远程通信。
传统的虚拟机技术和容器技术的区别?
传统虚拟机技术是虚拟出一套硬件后(内存分一点,磁盘分一点),在宿主上运行一个完整操作系统,在该系统上再运行所需要的应用进程;而容器内没有自己的内核,而且也没有进行硬件虚拟,应用进程直接运行于宿主的内核,而且进程和进程之间是相隔离的,是一个操作系统级别的虚拟化技术。
虚拟化技术通过 Hypervisor 实现虚拟机与底层硬件的解耦,虚拟机实现依赖 Hypervisor 层,Hypervisor 是整个虚拟机的核心所在。Hypervisor 虚拟机可以模拟机器硬件资源,协调虚拟机对硬件资源的访问,同时在各个虚拟机之间进行隔离。
虚拟化技术为用户提供了一个完整的虚拟机,包括操作系统在内。虚拟化技术有更佳的隔离性和安全性,但是更新和升级困难。
容器化技术为应用程序提供了隔离的运行空间,容器之间共享同一个上层操作系统内核。容器化具有快速扩展、灵活性和易用性等优势,但其隔离性较差、安全性相对较低。
实际部署一般是把两种技术结合起来,比如一个虚拟机中运行多个容器,这样既保证了较好的强隔离性和安全性,也有了快速扩展、灵活性和易用性。
Docker 的优势:
1、更高效的利用系统资源(不需要进行硬件虚拟以及运行完整操作系统等额外开销,一个相同配置的主机,往往可以运行更多数量的应用。单机支持上千个容器)
2、更快速的启动时间
3、一致的运行环境
4、持续交付和部署
5、更轻松的迁移
6、更轻松的维护和扩展
Docker 包括三个基本概念:
**镜像:**是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等) 。镜像不包含任何动态数据,其内容在构建之后也不会被改变,任何用户程序都可以成为镜像的一部分。
镜像=操作系统+软件运行环境+用户程序。
**容器:**镜像和容器的关系,就像是面向对象程序设计中类和实例的关系,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立命名空间。容器内的进程是运行在一个隔离的环境中,使用起来,就好像是在一个独立于宿主的系统下操作一样。
每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,容器存储层的的生命周期和容器一样,容器消亡时,容器存储层也随之消亡。按照 Docker 的最佳实践,容器不应该向存储层写入任何数据,容器存储层要保持无状态变化。所有的文件写入操作,都应该使用 数据卷(Volume) 、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储) 发生读写,其性能和稳定性更高。
**Docker Register:**一个集中的存储、分发镜像的服务,一个Docker Register 中可以包含多个仓库(打包好的软件镜像);每个仓库可以包含多个标签(一个软件的多个版本镜像);
tips:官方的镜像库 https://hub.docker.com/
国内阿里云的镜像库 https://cr.console.aliyun.com/
道云网址:get.dolcloud.io
官网网址 :Docker Hub Container Image Library | App Containerization
有网的情况下执行
yum update #升级
curl -sSL https://get.daocloud.io/docker | sh #下载并安装
安装完成后 执行 启动并开机自启
systemctl start docker
systemctl enable docker
rpm包下载地址: https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s https://docker.mirrors.ustc.edu.cn/
该脚本可以将 --registry-mirror 加入到你的 Docker 配置文件 /etc/docker/daemon.json 中。适用于 Ubuntu14.04、Debian、CentOS6 、CentOS7、Fedora、Arch Linux、openSUSE Leap 42.1,其他版本可能有细微不同。更多详情请访问文档。
加速网址可以在网上找到 有时候有的网址是不好使的
下载完成后重启docker
systemctl restart docker
如果报错 则根据报错信息进行处理
Warning: docker.service changed on disk. Run 'systemctl daemon-reload' to reload units.
Job for docker.service failed because start of the service was attempted too often. See "systemctl status docker.service" and "journalctl -xe" for details.
此条报错执行下方命令即可
systemctl daemon-reload
基本用法 docker +命令关键字(command)+参数
docker run --name MyWordPress --link db:mysql -p 8080:80 -d wordpress
docker run 启动容器 --name 容器名 --link 关联其他容器 -p 将物理机8080端口映射到容器内的80端口 -d 后台运行 wordpress 启动的镜像 如止果后面跟版本则会指定版本号 如果没有版本则为最新版本标签 (latesr)
docker info 守护进程的系统资源设置
docker search Docker 仓库的查询
docker pull Docker 仓库的下载
docker push 上传镜像
docker tag 原镜像名 新镜像名 给镜像改名
docker login 登录镜像仓库 认证信息保存在 家目录/.docker/config.json
docker logout 登出镜像仓库
docker save -o xx.tar镜像名 导出镜像为压缩文件
docker load -i xx.tar 从压缩文件导入镜像
docker build -t 账户名/镜像名:版本 . 封装镜像
(--no-cache 不使用缓存 卸载最后)
history 镜像层级的描述
docker images Docker 本地仓库镜像的查询
docker rmi -f Docker 镜像的删除
docker ps 容器的查询
docker ps -a 查看退出容器
docker ps -q 简要化输出
docker run 容器的创建启动
(--name 容器名 )
(--env INDEX_DATA=123 注入变量 卸载--name之后)
docker start/stop 容器启动停止
docker rm -f $( docker ps -a -q) 删除所有正在运行的容器
将删除命令封装
vim /usr/local/bin/cc 写入以下命令
#/bin/bash
docker rm -f $( docker ps -a -q) 保存退出
chmod +x /usr/local/bin/cc
每个容器被创建后,都会分配一个 CONTAINER ID 作为容器的唯一标示,后续对容器的启动、停止、修改、删除等所有操作,都是通过 CONTAINER ID 来完成,偏向于数据库概念中的主键
docker ps --no-trunc 查看完整版本
docker stop/start CONTAINERID 停止
docker start/stop MywordPress 通过容器别名启动/停止
docker inspect MywordPress 查看容器所有基本信息
docker image inspect 查看当前镜像的元数据信息
docker image inspect wangyanglinux:0.0.1 --format "{{json .ContainerConfig.Labels}}"
命令 镜像名 子选项 文件名 标签 子标签
docker logs MywordPress 查看容器日志 并非真的查看日志查看的是前台程序输出的信息
docker stats MywordPress 查看容器所占用的系统资源
docker exec 容器名 容器内执行的命令 容器执行命令
docker exec -it 容器名 /bin/bash 登入容器的bash
docker cp 文件名 镜像名:目录 将文件名拷贝到容器中
--restart=always 容器的自动启动
-h x.xx.xx 设置容器主机名
--dns xx.xx.xx.xx 设置容器使用的 DNS 服务器
--dns-search DNS 搜索设置
--add-host hostname:IP 注入 hostname <> IP 解析
--rm 服务停止时自动删除
docker三剑客
docker-compose 将一台机器上的多个项目进行统一化管理
docker-machine 将容器模拟为虚拟机
docker-swarm 容器的集群化设定
Docker 提倡理念是 “一个容器一个进程”,假设一个服务需要由多个进程组成,就需要多个容器组成一个系统,相互分工和配合对外提供完整服务
比如:博客系统
组件1:mariadb
组件2:WordPress 的 apache web
在启动容器是,同一台主机下如果两个容器之间需要由数据交流,使用 --link 选项建立两个容器之间的互联,前提是建立是 mariadb 已经开启
docker start db
docker start MywordPress
停止:
docker stop db MywordPress 或 docker stop MywordPress 在 docker stop db (注意关闭顺序)
容器编排工具,允许用户在一个模板( YAML 格式 )中定义一组相关联的容器,会根据 --link 等参数,对启动的优先级进行排序
YAML 语言的设计目标,就是方便人类读写。它实质上是一种通用的数据串行化格式
name: zhangsan
hash: {name: zhangsan, age: 18}
-
- zhangsan
- lisi
- wangwu
{name: ['zhangsan', 'lisi', 'wangwu']}
languages:
- Ruby
- Perl
- Python
websites:
YAML: yaml.org
Ruby: ruby-lang.org
Python: python.org
Perl: use.perl.org
# 数值直接以字面量的形式表示
number: 12.30
# 布尔值用 true 和 false 表示
isSet: true
# null 用 ~ 表示
parent: ~
# 时间采用 ISO8601 格式
iso8601: 2001-12-14t21:59:43.10-05:00
# 日期采用复合 iso8601 格式的年、月、日表示
date: 1976-07-31
# YAML 允许使用两个感叹号,强制转换数据类型
e: !!str 123
f: !!str true
# 字符串默认不使用引号表示
str: 这是一行字符串
# 如果字符串之中包含空格或特殊字符,需要放在引号之中
str: '内容: 字符串'
# 单引号和双引号都可以使用,双引号不会对特殊字符转义
s1: '内容\n字符串'
s2: "内容\n字符串"
# 单引号之中如果还有单引号,必须连续使用两个单引号转义
str: 'labor''s day'
# 字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格
str: 这是一段
多行
字符串
## { str: '这是一段 多行 字符串' }
# 多行字符串可以使用|保留换行符,也可以使用>折叠换行
this: |
Foo
Bar
that: >
Foo
Bar
## { this: 'Foo\nBar\n', that: 'Foo Bar\n' }
# +表示保留文字块末尾的换行,-表示删除字符串末尾的换行
s1: |
Foo
s2: |+
Foo
s3: |-
Foo
## { s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo' }
# 锚点&和别名*,可以用来引用
defaults: &defaults
adapter: postgres
host: localhost
development:
database: myapp_development
<<: *defaults
test:
database: myapp_test
<<: *defaults
curl -L https://github.com/docker/compose/releases/download/1.14.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
添加可执行权限
chmod a+x /usr/local/bin/docker-compose
查看是否可用
docker-compose version
yaml文件的创建和使用
mkdir /usr/local/wordpress 创建存放yaml文件的目录
vim docker-compose.yaml 创建yaml文件 文件后缀可以是yml
写入文件内容
version: '2'
services:
db:
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
restart: always
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
启动
docker-compose up -d
docker-compose 加下列选项
-f 指定使用的 yaml 文件位置 必须写在log前 up -d 启动容器项目
ps 显示所有容器信息 pause 暂停容器
restart 重新启动容器 unpause 恢复暂停
logs 查看日志信息 rm 删除容器
config -q 验证 yaml 配置文件是否正确
stop 停止容器
start 启动容器
容器创建时需要指定镜像,每个镜像都由唯一的标示 Image ID ,和容器的 Container ID 一样,默认 128 位(镜像最多支持128层),可以使用前 16 为缩略形式,也可以使用镜像名与版本号两部分组合唯一标示,如果省略版本号,默认使用最新版本标签 ( latesr 要尽量避免使用这种版本标签,因为会下载最新版哪怕本地仓库有镜像)
镜像名:例
hub.xxhf.com/wangweijianlinux/myapp:v1
仓库地址 用户名 镜像名:版本号
镜像的分层:Docker 的镜像通过联合文件系统 ( union filesystem ) 将各层文件系统叠加在一起 (最大128层超过会报错)
bootfs:用于系统引导的文件系统,包括 bootloader 和 kernel,容器启动完成后会被卸载以节省内存资源
roofs:位于 bootfs 之上,表现为 Docker 容器的跟文件系统
传统模式中,系统启动时,内核挂载 rootfs 时会首先将其挂载为“只读”模式,完整性自检完成后将其挂载为读写模式
Docker 中,rootfs 由内核挂载为“只读”模式,而后通过 UFS 技术挂载一个“可写” 层
已有的分层只能读不能修改
上层镜像优先级大于下层镜像(写时复制 将下层文件复制到可写层 在可写层进行修改 用户看到的内容是可写层的内容)
镜像的制作
先制作基础镜像(参照基础镜像构建)或者使用官方提供的基础镜像来构建镜像
容器 > 镜像
docker commit CID -t xx.xx.xx 将启动的容器制作成为镜像 CID=容器名 xx.xx.xx=镜像名
这条命令的使用前提是镜像可以自运行
镜像自运行的两种方式
1.镜像自带自运行
2.通过Dockerfile 封装一个可以自运行镜像
FROM 未自运行的镜像
RUN touch /root/test.sh
CMD /bin/bash /root/test.sh
Dockerfile>镜像 (参照Dockerfile 语法)
mkdir /tmp/test 创建干净的空目录
vim Dockerfile 创建并编写Dockerfile文件
docker build -t wangweijianlinux:test . 创建docker镜像 加--no-cache 不调用缓存
镜像名 所在目录 .代表Dockerfile的文件的位置 如果不在当前目录下需要指定绝对路径
注意事项 镜像层级不能超过128层 , 如果层级过多 可以通过&将RUN层级进行联立 (尽量不适用 容易造成容器运行不稳定,排错及其麻烦)
尽可能将重用性高的层级放在下部 (重用性指 经常会发生改变的 比如软件版本更换等 避免造成其他层级依赖或调用时无法使用)
两种镜像构建方式的优缺点对比
commit 优点所见即所得 不需要调试 缺点 功能性差 仅仅是对文件层级封装 不涉及元数据信息
Dockerfile 优点更能性强,缺点 编写困难 需要调试
将两种方法融合 基础镜像>编写daockerfile文件进行封装(自运行)>commit (软件运行环境-文件)- dockerfile 封装元数据信息
镜像动态化:
变量
初始化脚本
使用两种镜像制作方法结合制作动态化镜像
例
docker pull centos:7.9 #下载官方的基础镜像
mkdir test #创建一个新的目录后进入此目录
mkdir 1.run #创建干净的目录 并进入
vim Dockerfile #创建dockerfile文件
内容
FROM centos:centos7.8.2009
RUN touch /root/startup.sh
CMD tail -f /root/startup..sh
docker build -t wangweijianlinux/linux:7.9run . #制作自启动镜像
docker run --name nginx -p 指定端口 -d wangweijianlinux/linux:7.9run #启动镜像
docker exec -it nginx /bin/bash #进入容器
docker cp 文件名 镜像名:目录 #将文件拷贝到容器中
yum -y install gcc gcc-c++ make pcre pcre-devel zilb zilb-devel #在容器内部安装nginx依赖软件
tar -zvxf nginx.tar #解压nginx
./configure 检查 make && make install 编译安装
/usr/local/nginx/sbin/nginx #启动nginx并外部访问
yum clean all #删除nginx安装包 清空yum缓存
kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid) #优雅退出
exit #退出容器
docker commit nginx wangweijianlinux/nginx:v1.0 #从容器保存成镜像
mkdir 2.dockerfilebild #重新创建空目录
vim Dockerfile #创建dockerfile文件
#内容
FROM wangweijianlinux/nginx:v1.0
MAINTAINER wangweijian "[email protected]"
LABEL version="2.0"
#如果镜像内有很多服务 可以设置用户 如果镜像内只有一个服务那么root和普通用户没有区别
EXPOSE 80
EXPOSE 443
ENV INDEX_DATA default_data #设置变量
RUN rm -rf /root/startup.sh #确定root目录下没有这个文件
ADD ./startup.sh /root/startup.sh #从src复制到容器内部
WORKDIR /usr/local/nginx #切换目录
RUN chomd a+x /root/startup.sh
CMD /bin/bash /root/startup.sh #设置启动时执行的命令
vim startup.sh #编写启动脚本
#内容
#!/bin/bash
echo $INDEX_DATA > /usr/local/html/nginx.html
/usr/local/nginx/sbin/nginx && tail -f /usr/local/nginx/logs/access.log
#注意启动脚本内的命令要求是幂等的命令(幂等 可以反复执行多次的命令)
docker build -t wangweijian/nginx2.0 --no-cache . 制作nginx镜像
#(--no-cache 不调用缓存 从新制作 否则修改文件内容后制作出来的镜像和修改之前没有区别 )
docker run --name nginx --env INDEX_DATA=123 -d -wangweijian/nginx2.0 运行容器时注入变量
#(--env 注入的变量优先级要大于Dockerfile内声明的ENV 相当于替换)
(工作中可以进行逻辑梳理伪代码编写 编写完成后将代码按照逻辑写出即可)
(工作中第一次接触一个软件封装镜像时 如果时间很宽泛 可以 使用Dockerfile 进行封装 因为dockerfile文件的重用性很高
如果 时间紧张又是一次接触 尽量将基础镜像run起来 之后进行封装 相对dockerfile来讲测试调试更简单更方便)
访问hub.docker.com
创建用户
创建仓库存放同一类镜像 点击 create repository
安装要求
满足一下要求才可以安装harbot
要求python 在2.7及以上版本
Docker 引擎为1.10及以上版本
Dockers-compose 为1.6.0及以上版本
安装
上传harbot压缩包
tar -zxvf 包名
#解压后建议保留安装包 用于日后维护使用
mv harbot /usr/local
cd /usr/local/harbot
vim harbot.cfg
#修改配置文件 配置文件名harbot.cfg install.sh这个安装程序会从配置文件中调取参数
#内容修改
hostname=
ui_url_protocol = https #协议类型
max_job_works = 3 #最大线程数 不建议设置过高 以免占用I/O
ssl_cert = #证书所在路径
ssl_cert_key = #证书所在路径
secretkey_paht = /data #数据保存位置
#修改完成后保存退出
#创建证书
mkdir -p /data/cert
chmod -R 777 /data/cert
openssl genrsa -des3 -out server.key 2048
opensslreq -new-key server.key -out server.csr
cp server.key server.key.org #复制证书文件
opensslrsa -in server.key.org -out server.key #删除私钥密码 (有密码的情况下无法自动登录)
openssl x509 -req -days 365 -in server.csr-signkey server.key -out server.crt #签发证书
./install.sh #先创建证书后安装否则报错
将域名写入 housts文件(双方都要写) 浏览器访问测试 用户名admin 密码Harbot12345 生产环竟中将证书换成生效的证书
#推镜像时报错 需要添加证书信任后重新启动dicker
vim/etc/docker/daemon.json
{
"insecure-registries": ["serverip"]
}
#要求符合格式 json对象格式中 多个对象后要加逗号 最后一个后面不加逗号否则报错
#解决完证书后 要先进行仓库登录认证才能推送镜像
#docker login 私有仓库地址
netns 是在 linux 中提供网络虚拟化的一个项目,使用 netns 网络空间虚拟化可以在本地虚拟化出多个网络环境,目前 netns 在 lxc 容器中被用来为容器提供网络
使用 netns 创建的网络空间独立于当前系统的网络空间,其中的网络设备以及 iptables 规则等都是独立的,就好像进入了另外一个网络一样
#查看名字空间
ip netns list
#创建好新的名字空间后内部只有一个lo网卡 状态为DOWN
# 创建虚拟网络空间
ip netns add r1
# 进入虚拟网络空间
ip netns exec ns1 bash
# 添加一对 veth 设备
ip link add veth1.1 type veth peer name veth1.2
# 将其中一块网卡放入至 ns1 网络名称空间之中
ip link set veth1.1 netns r1
# 更改网络名称
ip link set veth1.1 name eth0
# 启动网卡
ip link set eth0 up
# 设置网卡名称
ip addr add 10.0.0.11/24 dev eth0
# 启动回环网卡
ip link set lo up
原理:在虚拟机的上添加一对veth网卡 将一个veth网卡放入虚拟空间内部,实现通讯
容器网络 使用网桥
联通性:
容器间的访问:网桥 Docker0
容器与外部访问:SNAT 通过防火墙规则进项snat转换
外部与容器访问:DNAT
隔离性:NameSpace-NetWork
docker run --name test --net bridge -d id
#docker的默认网络通讯方式 网桥模式 (网桥 类似交换机的机制)
--net bridge
#容器没有网络栈 (类似仅主机)
--net none
#使用其他容器的网络栈,Docker容器会加入其他容器的 network namespace(实际上是把两个容器放在了一个名字空间内,一个容器通过另一个容器与外部联通) -network container:(ContainerName)
--network container
#表示容器使用host网络,没有自己独立的网络栈。容器可以完全访问Host的网络,不完全 (类似桥接)
--network host
#子选项的书写位置
docker run --name MyWordPress --link db:mysql -p 8080:80 -d wordpress
#将指定端口映射至主机的随机端口
-p Containerport:
#映射至指定的主机端口
-p hostpsot:Containerprot
#映射至指定主机的随机端口
-p ip::Containerprot
#映射到指定主机的指定端口
-p ip:hostport:Containerport
#暴漏所需要的所有端口(大写P)
-P
#查看当前容器的映射关系
docker port ContainerName
对不同的网络空间进行隔离
# docker network create 创建名字空间 -d 指定驱动 bridge 网桥驱动 --subnet 指定当前网桥驱动的网段 --gateway 指定当前网桥自己的地址
docker network create -d bridge --subnet "一个网段" --gateway "网关" mybridge-network
#!/bin/bash
#删除ens33网卡
ip addr del dev ens33 192.168.66.11/24
#将ens33变成虚拟网桥的物理附着点
ip link add link ens33 dev br0 type macvlan mode bridge
#修改网桥地址
ip addr add 192.168.66.11/24 dev br0
#启动虚拟网桥
ip link set dev br0 up
#添加网关
ip route add default via 192.168.66.1 dev br0
扩展 :网卡混杂模式 指如果网卡的混杂模式如果被关闭 那么当前网卡拥有的IP地址才允许被访问不是这个网卡配置的IP地址不允许通过这个网卡通信 虚拟化中混杂模式一定要被打开因为一个网卡背后可能有多个不同的地址
#将github上的代码下载到本地
git clone https://github.com/SeaYellow/pipwork.git
#解压
tar -zxvf pipwork.tar.gz
#复制文件放到/usr/local/bin
cp -a pipwork/pipwork /usr/local/bin
#添加执行权限即可使用此工具
chmod a+x /usr/local/bin/pipwork
###使用此工具可以给容器加地址
pipework br0 test 192.168.66.200/[email protected]
!此模式相当于将容器当成虚拟机使用 与k8s的模式相冲 看情况使用只能使用一个
Docker 镜像由多个只读层叠加而成,启动容器时,Docker 会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有的一个已经存在的文件,那么该文件将会从读写层下面的的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,次即“写时复制”机制
写时复制状态下关闭并重启容器数据不受影响 ,删除docker容器则改变的数据全部丢失
缺点 :容器间数据共享不便,存在于联合文件系统中,不易于宿主机访问,删除容器数据丢失
数据卷:一种脱离容器的生命周期以外的存储方式或目录,数据卷创建应在容器创建或启动之前,或和容器一起创建并使用
有状态服务:将节点踢出集群一段时间再加入到集群内后,不能和其他节点保持一致的服务 :mysql
无状态服务:将节点踢出集群一段时间再加入到集群内,能继续提供服务,和其他节点保持一致的服务 (http协议、https协议大多数为无状态服务 )
中心化服务:客户端通过网络访问服务器端的服务
去中心化服务:客户端即服务器端(比特币)
docekr的运行数据存储再 /var/lib/docker/ 目录下
再dockerfile文件中使用VOLUME关键字指定挂载点 封装镜像后 在/var/lib/docker/volumes/ 删除容器后可以看到持久化的文件
VOLUME (指定挂载点)
设置指令,使容器中的一个目录具有持久化存储数据的功能,该目录可以被容器本身使用,也可以共享给其他容器使用。我们知道容器使用的是 AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。当容器中的应用有持久化数据的需求时可以在 Dockerfile中 使用该指令
FROM base
VOLUME ["/tmp/data"]
这种方式持久化优点是使用镜像的默认值 启动无需操作, 缺点是不灵活
此时可以使用这种方式进行数据持久化
mkdir /data #创建目录
docker --name nginx -v /data:/zhangsan -v /root/nginx-v:/data -d 镜像名
# -v 选项将物理机的目录挂载到容器内部 实现较为灵活的数据持久化
# :前为物理机目录 :后为容器内目录 容器内目录可以不存在会自动创建
# 可以使用多个-v选项 将多个文件夹挂载到容器内部 不冲突即可
# -v可以跟文件的绝对路径 将文件挂载到容器内 例:-v /data/dockerfile:/data 此时是将dockerfile文件映射到容器内,这时/data被映射成了一个文件 如果想保留目录则需要手动递归创建目录后进行挂载
当VOLUME和-v选项相冲时以-v为准 也就是说在有VOLUME默认值的情况下使用-v选项启动的容器数据会保存在-v指定的挂载目录下
docker run --name wordpress1 -d wordpress #创建一个容器
docker run --name wordpress2 --volmues-from wordpress1 -d wordpress #--volmues-from指定wordpress2与wordpress1的卷保持一致(公用一个卷)
在docker分层文件系统中如果使用的是DM 或者btrfs 应将其升级为overlayfs
修改方法:
echo "overlay" > /etc/modules-load.d/overlay.conf #将overlay关键字放在模块的加载目录下
cat /proc/modules | grep overlay #查看模块是否加载
reboot #重载(内核要求在3.18以上)
vim /etc/systemd/system/docker.service #打开docker的启动管理脚本
--storagt-driver=overlay \ #在内部加入当前的主流启动引擎为overlay
overlayfs的概念 :
mkdir /overlay
cd /overlay
mkdir low #创建镜像层目录
mkdir upper #创建可写层
mkdir work #创建工作层
mkdir merged #用户挂载点
mount -t overlay overlay -olowerdir=./low,upperdir=./upper,workdir=./work ./merged #挂载分层文件系统
#overlay文件系统可以被mount挂载直接使用的 但需要符合目录结构
mount #查看挂载
# 在low文件夹内创建文件 1.txt
# 在upper文件夹内创建 2.html
yum -y install tree #安装树文件系统
#对以上两个文件进行修改,使用tree查看变化
Container mount 用户展现层
Container layer 相当于可写层
image layer 相当于镜像层
结论:在镜像层创建的文件 会出现在 merged 层上 ,在可写层创建的文件同样会出现在merged层上。当修改镜像层内文件时该文件会被复制到可写层上,在可写层上被修改(即写时复制)当在用户层把在可写层内创建的文件删除时 用户层内的此文件和可写层内的此文件都会被删除 。 在用户层删除镜像层的文件时 镜像层文件并不会被删除 ,用户看不见此镜像层文件的原因是该文件在可写层 使用c 对文件进行了标记,如果要用户层重新看见镜像层的文件 将可写层内被c标记的文件删除即可。
资源分类:
可压缩资源:cpu 磁盘IO 网络IO 存储空间部分可压缩
不可压缩资源: 内存
在内核的内存空间被占用的时候 会产生一种报错 OOME
OOME:内存使用量过高导致威胁内核生存 内核开始随机杀死进程以释放内存保证自己存活,一直杀到保证内核正常运行的内存空间为止。
最低控制单位 :进程组群 (多个进程可以归为一个进程组群下 限制的最低单位是一个组群而不是单个进程)如创建一个进程组群 限制资源限制为500MB 将一个进程放进组群内 则这个进程独享进程组群所允许使用的全部500MB 如果再加入一个进程的时候则 两个进程共享进程组群的500MB资源 可以对 CPU 内存 磁盘输入输出等进行限制
功能: 限制资源使用(核心功能);优先级控制;一些审计或统计;挂起进程和恢复进程
实验:
#示例代码(c语言死循环代码)
vim main.c #创建C语言文件 并将下列代码写入文件内部保存退出
int main(void)
{
int i = 0;
for(;;) i++
return 0;
}
yum -y install gcc gcc-c++ #安装编译工具
gcc main.c #编译 会生成一个a.out文件 此文件是可执行文件
./a.out #运行代码
top #查看当前cup使用量
mount -t cgrop #查看cgrop挂载点
cd /sys/fs/cgrop/cpu #文件系统映射在当前目录下 不同的子系统会有不同的目录创建出来 此时为cpu子系统下
mkdir cpu_test #创建子子系统
#在父子系统中 所有进程共享所有资源 在子子系统中可以对进程使用资源进行限制
echo > 进程号 /sys/fs/cgrop/cpu/cpu_test/tasks #将进程放在子子系统中运行
echo 20000 > /sys/fs/cgrop/cpu/cpu_test/cpu.cfs_quota_us #借助这个文件对资源进行限制 20000指20%
#如果想对容器进行cpu进行资源使用限制 只需要将限制配额写入/sys/fs/cgrop/docker/容器id号/cpu.cfs_quota_us内即可
默认情况下如果对容器不做限制 容器能够占用当前系统能给容器提供的所有资源
docker 限制资源可以从 Memery 、CUP 、 I/O 三个方面
OOME :一旦发生OOME报错任何进程都可以被杀死 包括docker daemon
:为此docker 调整了docker daemon 的OOM等级 以免被内核关闭
限制选项
-m --memory #内存限制,格式是数字加单位 b、k、m、g 最小为4M (物理内存)
--memory-swap #此选项为物理内存+交换分区大小的总限制。格式同上
#可能性1:--memory-swap=正数S --memory=正数M 容器可用总空间为S 其中物理内存为M swap=S-M 若S=M则无swap资源可用
#可能性2:--memory-swap=0 --memory=正数M 相当为没设置swap
#可能性3:--memory-swap=unset --memory=正数M 若容器启用了swap分区则容器可用的swap为两倍的物理内存但swap的使用量不能大于物理机swap分区的大小
#可能性4:--memory-swap=-1 --memory=正数M 若物理机启用了swap分区则容器可以使用物理机上的所有的swap空间资源
#容器内的free命令不要信
--memory-reservation #内存软限制 格式同上
--oom-kill-disable #是否阻止oome杀死容器默认没设置 (此选项比较危险尽量不要使用)
--oom-score-adj #设置容器被oome 杀死的优先级 范围-1000~1000 默认为0 数字越大越容易被杀死
--memory-swappiness #用于设置容器的虚拟内存控制行为。值为0~100之间的整数 如果为0则优先使用虚拟内存 如果为100则全部使用物理内纯,在物理内存使用光后才使用虚拟内存
--kernel-memory 核心内存限制格式同上,最小为4M
cpu限制选项
默认情况下如果对容器不做限制 容器能够占用当前系统能给容器提供的所有CPU资源,大多数采用的是CFS调度算法。
docker1.13版本后支持实时调度算法
--cpuset-cpus"" #允许使用cpu的集 值可以是0-3、0、1 指将容器与cpu核心进行亲和性绑定
-c,--cpu-shares=0 #CPU共享权值(相对权重)
cpu-period=0 #限制CPU的CFS周期,范围从100ms~1s 即1000~1000000
--cpu-quota=0 #限制CPU CFS 配额,必须不小于1ms 即>1000
--cpuset-mems="" #允许在上执行内存节点(MEMs)只对NUMA系统有效 NUMA:对称多处理器架构,内存分区尽可能减少CPU和内存的延时 如果--cpuset-cpus"0" 的情况下--cpuset-mems="1" 那么结果是可以使用全部分区内存,优点是CPU内存视图大,缺点是延时高。所以尽可能的保持--cpuset-cpus""和--cpuset-mems=""的值是一致的
--cpus #设置能够使用的CPU核心数目 可以是小数
例:docker run --name staress -it --rm -m 256m lorel/docker-staress-ng:latest stress -vm 2
#启动ubantu系统压测镜像 -m 256m 限制内存使用量为256*3 -vm 开始压测 2 两个线程 原理无限创建数组
例:docker run --name staress -it --rm --cpu 2 lorel/docker-staress-ng:latest stress --cpu8
#启动压测cpu的镜像 ---cpu 后可以跟小数 0.2代表20%
测试环境要求 :大 包含的功能更全 测试组件更多
生产环境要求:小 在保留功能性的形况下尽可能的精简
使用场景 :尽可能将编译环境和执行环境分离的概念 c语言环境 go语言环境
Docker Daemnon 通过 Dockerfile 构建镜像时,当发现即将新构建出的镜像与已有的某镜像重复时,可以选择放弃构建新的镜像,而是选用已有的镜像作为构建结果,也就是采取本地已经 cache 的镜像作为结果
1. ADD 命令与 COPY 命令:Dockerfile 没有发生任何改变,但是命令ADD run.sh /
中 Dockerfile 当前目录下的 run.sh 却发生了变化,从而将直接导致镜像层文件系统内容的更新,原则上不应该再使用 cache。那么,判断 ADD 命令或者 COPY 命令后紧接的文件是否发生变化,则成为是否延用 cache 的重要依据。Docker 采取的策略是:获取 Dockerfile 下内容(包括文件的部分 inode 信息),计算出一个唯一的 hash 值,若 hash 值未发生变化,则可以认为文件内容没有发生变化,可以使用 cache 机制;反之亦然**
2. RUN 命令存在外部依赖:一旦 RUN 命令存在外部依赖,如RUN apt-get update
,那么随着时间的推移,基于同一个基础镜像,一年的 apt-get update 和一年后的 apt-get update, 由于软件源软件的更新,从而导致产生的镜像理论上应该不同。如果继续使用 cache 机制,将存在不满足用户需求的情况。Docker 一开始的设计既考虑了外部依赖的问题,用户可以使用参数 --no-cache 确保获取最新的外部依赖,命令为docker build --no-cache -t="my_new_image" .
3. 树状的镜像关系决定了,一次新镜像的成功构建将导致后续的 cache 机制全部失效:这一点很好理解,一旦产生一个新的镜像,同时意味着产生一个新的镜像 ID,而当前宿主机环境中肯定不会存在一个镜像,此镜像 ID 的父镜像 ID 是新产生镜像的ID。这也是为什么,书写 Dockerfile 时,应该将更多静态的安装、配置命令尽可能地放在 Dockerfile 的较前位置
FROM golang:1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
#!/bin/sh
echo Building sparkdevo/href-counter:build
# 构建编译应用程序的镜像
docker build --no-cache -t sparkdevo/href-counter:build . -f Dockerfile.build
# 创建应用程序
docker create --name extract sparkdevo/href-counter:build
# 拷贝编译好的应用程序
docker cp extract:/go/src/github.com/sparkdevo/href-counter/app ./app
docker rm -f extract
echo Building sparkdevo/href-counter:latest
# 构建运行应用程序的镜像
docker build --no-cache -t sparkdevo/href-counter:latest .
# 下载使用的代码
$ git clone https://github.com/wangyanglinux/href-counter.git
FROM golang:1.7.3
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
在dockerfile文件中在17.05版本以上 可以有多个from 中间用空格隔开表示多级构建
命名方式的 stage
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/sparkdevo/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/sparkdevo/href-counter/app .
CMD ["./app"]
在一个层级后面加上 as 别名 后面的层级可以根据别名直接调用
$ git clone https://github.com/GoogleContainerTools/distroless
测试环境中不要使用多级构建的优化 也就是以上的流程制作镜像