Docker入门与进阶(上)
作者 | 刘畅 |
时间 | 2020-10-17 |
目录
2.5 镜像存储核心技术(联合文件系统UnionFS) 5
5.4 容器网络实现核心技术(Iptables) 16
5.5 跨主机网络(实现Docker主机容器通信) 17
1 Docker核心概述与安装
1.1 为什么要用容器
(1) 上线流程繁琐
开发->测试->申请资源->审批->部署->测试等环节
(2) 资源利用率低
普遍服务器利用率低,造成过多浪费
(3) 扩容/缩容不及时
业务高峰期扩容流程繁琐,上线不及时
(4) 服务器环境臃肿
服务器越来越臃肿,对维护、迁移带来困难
(5) 环境不一致性
1.2 docker是什么
(1) 使用最广泛的开源容器引擎
(2) 一种操作系统级的虚拟化技术
(3) 依赖于Linux内核特性:Namespace(资源隔离)和Cgroups(资源限制)
(4) 一个简单的应用程序打包工具
1.3 docker设计目标
(1) 提供简单的应用程序打包工具
(2) 开发人员和运维人员职责逻辑分离
(3) 多环境保持一致性
1.4 docker基本组成
1.5 容器vs虚拟机
1.6 docker应用场景
(1) 应用程序打包和发布
(2) 应用程序隔离
(3) 持续集成
(4) 部署微服务
(5) 快速搭建测试环境
(6) 提供PaaS产品(平台即服务)
1.7 安装docker
Docker版本:
社区版(Community Edition,CE)
企业版(Enterprise Edition,EE)
支持平台:
Linux(CentOS,Debian,Fedora,Oracle Linux,RHEL,SUSE和Ubuntu)
Mac
Windows
# 安装依赖包
[root@slavenode1 ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加Docker软件包源
[root@slavenode1 ~]# yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 更新为阿里云的源
[root@slavenode1 ~]# wget -O /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 清理源缓存
[root@slavenode1 ~]# yum clean all
# 安装Docker CE
[root@slavenode1 ~]# yum install -y docker-ce
# 启动Docker服务并设置开机启动
[root@slavenode1 ~]# systemctl start docker
[root@slavenode1 ~]# systemctl enable docker
# 查看docker版本
[root@slavenode1 ~]# docker -v
Docker version 19.03.12, build 48a66213fe
# 添加阿里云的镜像仓库
[root@slavenode1 ~]# mkdir -p /etc/docker
[root@slavenode1 ~]# tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://b1cx9cn7.mirror.aliyuncs.com"]
}
EOF
# 重启docker
[root@slavenode1 ~]# systemctl daemon-reload
[root@slavenode1 ~]# systemctl restart docker
2 Docker镜像管理
2.1 什么是镜像
(1) 一个分层存储的文件,不是一个单一的文件
(2) 一个软件的环境
(3) 一个镜像可以创建N个容器
(4) 一种标准化的交付
(5) 一个不包含Linux内核而又精简的Linux操作系统
2.2 镜像从哪里来
Docker Hub是由Docker公司负责维护的公共镜像仓库,包含大量的容器镜像,Docker工具默认从这个公共镜像库下载镜像。
地址:https://hub.docker.com
2.3 镜像与容器联系
镜像:类似于虚拟机镜像,一个只读模板
容器:通过镜像创建的运行实例
2.4 镜像常用管理命令
指令 | 描述 |
ls | 列出镜像 |
build | 构建镜像来自Dockerfile |
history | 查看镜像历史 |
inspect | 显示一个或多个镜像详细信息 |
pull | 从镜像仓库拉取镜像, 分层下载,默认镜像版本latest |
push | 推送一个镜像到镜像仓库 |
rm | 移除一个或多个镜像 |
prune | 移除没有被标记或者没有被任何容器引用的镜像 |
tag | 创建一个引用源镜像标记目标镜像 |
export | 导出容器文件系统到tar归档文件 |
import | 导入容器文件系统tar归档文件创建镜像 |
save | 保存一个或多个镜像到一个tar归档文件 |
load | 加载镜像来自tar归档或标准输入 |
(1) 清理镜像
docker image prune -f #不提示确认(-f)清除没有tag的镜像
docker image prune -a -f # 不提示确认(-f)清除没有被容器使用的镜像
(2) export、import方式导入镜像
docker export 容器名/容器ID > 打包容器名.tar
docker import 打包容器名.tar 自定义镜像名:版本号
(3) save、load方式导入镜像
docker打包镜像名.tar 镜像名称:版本号
docker load -i 打包镜像名.tar
(4) docker export和docker save的区别
1) docker save保存的是镜像(image),docker export保存的是容器(container)。
2) docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像。
3) docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称。
4) docker export导出的镜像文件大小小于save保存的镜像。
5) docker save 没有丢失镜像的历史,可以回滚到之前的层(layer)。(查看方式:docker images --tree)docker export 再导入时会丢失镜像所有的历史,所以无法进行回滚操作(docker tag <LAYER ID> <IMAGE NAME>)。
(5) 其它命令
docker images # 列出所有镜像
docker image rm -f 镜像名或ID # 强制(-f)删除镜像,如果镜像正在被运行的容器使用不会被删除。
docker image rm -f $(docker images -q) # 强制(-f)删除所有的镜像,如果镜像正在被运行的容器使用不会被删除。
2.5 镜像存储核心技术(联合文件系统UnionFS)
镜像怎么高效存储,难道像虚拟机那样一个容器对应一个独立的镜像文件,这样对于密集型容器,磁盘占用率太大,引入联合文件系统,将镜像多层文件联合挂载到容器文件系统。
镜像存储目录: /var/lib/docker/overlay2/
容器目录: /var/lib/docker/containers/
读文件: 容器层->镜像层
修改文件: 容器层->镜像层
MergedDir(视图层)
UpperDir(容器层)
LowerDir(镜像层)
Storage Driver: overlay2(aufs、overlay、devicemapper)
2.6 镜像存储核心技术(写时复制Cow)
了解联合文件系统后,我们知道,镜像是只读的,类似共享形式让多个容器使用,如果要在容器里修改文件,即镜像里的文件,那该怎么办呢,这时就需要引入写时复制(copy-on-write),需要修改文件操作时,会先从镜像里把要写的文件复制到自己的文件系统中进行修改。
docker存储优化建议:
使用SSD固态硬盘
使用卷作为频繁读写文件的工作目录,绕过存储驱动,减少抽象的开销
3 Docker容器管理
3.1 创建容器常用选项
-i, --interactive | 交互式 |
-t, --tty | 分配一个伪终端 |
-d, --detach | 运行容器到后台 |
-e, --env | 设置环境变量 |
-p, --publish list | 发布容器端口到主机 |
-P, --publish-all | 发布容器所有EXPOSE的端口到宿主机随机端口 |
--name string | 指定容器名称 |
-h, --hostname | 设置容器主机名(同时也加入到/etc/hosts文件解析) |
--add-host host:ip | 设置容器hosts |
--dns | 设置容器resolv.conf |
--ip string | 指定容器IP,只能用于自定义网络 |
--network | 连接容器到一个网络(bridge,host,none) |
--mount mount | 将文件系统附加到容器 |
-v, --volume list | 绑定挂载一个卷 |
--restart string | 容器退出时重启策略,默认no,可选值:[always|on-failure] |
参数后面加的值可以用"="或空格隔离。
创建容器时,容器会挂载宿主机三个文件(宿主机修改,需要重启容器):
hostname --hostname
hosts --add-host host:ip
resolv.conf --dns
3.2 容器资源限制
选项 | 描述 |
-m,--memory | 容器可以使用的最大内存量 |
--memory-swap | 允许交换到磁盘的内存量,表示内存和 Swap 的总和 |
--memory-swappiness=<0-100> | 容器使用SWAP分区交换的百分比(0-100,默认为-1) |
--oom-kill-disable | 禁用OOM Killer |
--cpus | 可以使用的CPU数量 |
--cpuset-cpus | 限制容器使用特定的CPU核心,如(0-3, 0,1) |
--cpu-shares | CPU共享(相对权重) |
参数后面加的值可以用"="或空格隔离。
3.3 容器资源配额扩容
1 内存限额
允许容器最多使用500M内存和100M的Swap,并禁用OOM Killer:
docker run -d --name nginx03 --memory="500m" --memory-swap="600m" --oom-kill-disable
# --memory用于限制内存使用量,而--memory-swap则表示内存和Swap的总和。
# 如果只指定了--memory,则最终--memory-swap将会设置为--memory的两倍。
# 如果--memory-swap和--memory设置了相同值,则表示不使用Swap。
# 如果设置了--memory-swap参数,则必须设置--memory参数。
# --oom-kill-disable:触发oom异常不杀死容器(当内核检测到没有足够的内存来运行系统的
某些功能时,触发OOM异常,并且会使用OOM Killer来杀掉一些进程,腾出空间以保障
系统的正常运行。)
2 CPU限额
(1) 允许容器最多使用一个半的CPU
docker run -d --name nginx04 --cpus="1.5" nginx
(2) 允许容器最多使用50%的CPU
docker run -d --name nginx05 --cpus=".5" nginx
3.4 管理容器常用命令
选项 | 描述 |
ls | 列出正在运行的容器 |
inspect | 查看一个或多个容器详细信息 |
exec | 在运行容器中执行命令 |
commit | 创建一个新镜像来自一个容器 |
cp | 拷贝文件/文件夹到一个容器 |
logs | 获取一个容器日志 |
port | 列出或指定容器端口映射 |
top | 显示一个容器运行的进程 |
stats | 显示容器资源使用统计 |
stop/start/restart | 停止/启动/重启/一个或多个容器 |
rm | 删除一个或多个容器 |
prune | 移除已停止的容器 |
docker container ps # 列出正在运行的容器
docker container ps -l # 列出最近一次操作的容器
docker container ps -a # 列出所有容器,包括停止和运行的容器
docker container ps -aq列出所有容器ID
docker container prune -f # 不提示确认(-f)删除已停止的容器
docker container rm容器名或id # 强制(-f)删除指定的容器
docker container exec -it 容器名或id bash # 进入容器
docker container update lc12_nginx --memory 500m --memory-swap="500m"
# 更新容器配置(默认创建的容器使用宿主机所有资源)
# 本质:echo "1000m" > /sys/fs/cgroup/memory/docker/<容器id>/memory.memsw.limit_in_bytes
echo "500m" > /sys/fs/cgroup/memory/docker/<容器id>/memory.limit_in_bytes
docker container rm -f $(docker ps -qa) # 强制(-f)删除所有的容器(包括正在运行的容器)
3.5 容器实现核心技术(Namespace)
1 在容器化中,一台物理计算机可以运行多个不同操作系统,那就需要解决“隔离性”,彼此感知不到对方存在,有问题互不影响。Linux内核从2.4.19版本开始引入了namespace概念,其目的是将特定的全局系统资源通过抽象方法使得namespace中的进程看起来拥有自己隔离的资源。Docker就是借助这个机制实现了容器资源隔离。
2 Linux的Namespace机制提供了6种不同命名空间
ll /proc/$$/ns # 查看进程的命名空间
(1) IPC:隔离进程间通信
(2) MOUNT:隔离文件系统挂载点
(3) NET:隔离网络协议栈
(4) PID:隔离进程号,进程命名空间是一个父子结构,子空间对父空间可见
(5) USER:隔离用户
(6) UTS:隔离主机名和域名
3.6 容器实现核心技术(CGroups)
1 Docker利用namespace实现了容器之间资源隔离,但是namespace不能对容器资源限制,
比如CPU、内存。如果某一个容器属于CPU密集型任务,那么会影响其他容器使用CPU,
导致多个容器相互影响并且抢占资源。如何对多个容器的资源使用进行限制就成了容器化
的主要问题。
2 引入Control Groups(简称CGroups),限制容器资源CGroups,所有的任务就是运行在系统
中的一个进程,而CGroups以某种标准将一组进程为目标进行资源分配和控制。例如CPU、内
存、带宽等,并且可以动态配置。
3 CGroups主要功能
(1) 限制进程组使用的资源数量(Resource limitation),可以为进程组设定资源使用上限,例如
内存
(2) 进程组优先级控制(Prioritization),可以为进程组分配特定CPU、磁盘IO吞吐量。
(3) 记录进程组使用的资源数量(Accounting),例如使用记录某个进程组使用的CPU时间。
(4) 进程组控制(Control),可以将进程组挂起和恢复。
4 查看cgroups可控制的资源
ls /sys/fs/cgroup/ -l
(1) blkio # 对快设备的IO进行限制。
(2) cpu # 限制CPU时间片的分配,与cpuacct挂载同一目录。
(3) cpuacct # 生成cgroup中的任务占用CPU资源的报告,与cpu挂载同一目录。
(4) cpuset # 给cgroup中的任务分配独立的CPU(多核处理器)和内存节点。
(5) devices # 允许或者拒绝 cgroup 中的任务访问设备。
(6) freezer # 暂停/恢复 cgroup 中的任务。
(7) hugetl # 限制使用的内存页数量。
(8) memory # 对cgroup 中任务的可用内存进行限制,并自动生成资源占用报告。
(9) net_cls # 使用等级识别符(classid)标记网络数据包,这让Linux流量控制
程序(tc)可以识别来自特定从cgroup 任务的数据包,并进行网络限制。
(10) net_prio # 允许基于cgroup设置网络流量的优先级。
(11) perf_event #允许使用perf工具来监控cgroup。
(12) pids # 限制任务的数量。
(13) /sys/fs/cgroup/<资源名>/docker/<容器ID> # 容器实际资源限制位置
3.7 docker核心组件之间关系
(1) Docker Daemon
Docker守护进程,负责与Docker Clinet交互,并管理镜像、容器。
(2) Containerd
是一个简单的守护进程,向上给Docker Daemon提供接口,向下通过containerd-shim
结合runC管理容器。
(3) runC
一个命令行工具,它根据OCI标准来创建和运行容器
3.8 小结
1 Namespace
命名空间,Linux内核提供的一种对进程资源隔离的机制,例如进程、网络、挂载点等资源。
2 CGroups
控制组,Linux内核提供的一种对进程组限制资源的机制;例如CPU、内存等资源。
3 UnionFS
联合文件系统,支持将不同位置的目录挂载到同一虚拟文件系统,形成一种分层的模型。
4 Docker容器数据持久化
4.1 将数据从宿主机挂载到容器中的三种方式
数据卷目录名称,指定的源目录/指定的源文件 :指定容器内的目录/指定容器内的文件/(如果目标不存在会自动创建)
容器被删除后不会删除卷,卷是容器的一个独立的资源。
(1) volumes
Docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)。
(2) bind mounts
将宿主机上的任意位置的文件或者目录挂载到容器中。
(3) tmpfs
挂载存储在主机系统的内存中,而不会写入主机的文件系统。如果不希望将数据持久
存储在任何位置,可以使用tmpfs,同时避免写入容器可写层提高性能。
4.2 Volume
1 管理卷
# docker volume create nginx-vol
# docker volume ls
# docker volume inspect nginx-vol
2 用卷创建一个容器
# docker run -d --name=nginx-test --mount src=nginx-vol,dst=/usr/share/nginx/html nginx
# docker run -d --name=nginx-test -v nginx-vol:/usr/share/nginx/html nginx
3 清理
# docker stop nginx-test
# docker rm nginx-test
# docker volume rm nginx-vol
4 注意点
volume 挂载注意点(目录对目录):
(1) 宿主机目录不存在也能挂载成功
(2) 如果宿主机目录为空,会继承容器目录中的全部内容
(3) 如果宿主机目录不为空,会覆盖容器目录中的全部内容
(4) 源数据卷由docker进行管理
4.3 Bind Mounts
1 用卷创建一个容器
# docker run -d --name=nginx-test --mount type=bind,src=/app/wwwroot,dst=/usr/share/nginx/html nginx
# docker run -d --name=nginx-test -v
2 验证绑定
# docker inspect nginx-test
3 清理
# docker stop nginx-test
# docker rm nginx-test
4 注意点
bind mounts 注意点(目录对目录或文件对文件):
(1) 宿主机文件或者目录必须存在才能成功挂载
(2) 宿主机目录覆盖容器目录中的全部内容
(3) 宿主机文件覆盖容器中指定的文件
(4) 使用-v参数挂卷时,如果源目录不存在也会创建
4.4 小结
1 Volume特点
(1) 将容器的数据存储在远程主机或其他存储上(间接)。
(2) 多个运行容器之间共享数据,多个容器可以同时挂载相同的卷。
(3) 当容器停止或被移除时,该卷依然存在。
(4) 当明确删除卷时,卷才会被删除。
(5) 将数据从一台Docker主机迁移到另一台时,先停止容器,然后备份卷的目录(/var/lib/docker/volumes/)。
2 Bind Mounts特点
(1) 从主机共享配置文件到容器。
(2) 在Docker主机上的开发环境和容器之间共享源代码。例如,可以将Maven target
目录挂载到容器中,每次在Docker主机上构建Maven项目时,容器都可以访问构建
的项目包。
(3) 当Docker主机的文件或目录结构保证与容器所需的绑定挂载一致时。
5 Docker容器网络
5.1 四种网络模式
(1) bridge
--net=bridge
默认网络,Docker启动后创建一个docker0网桥,默认创建的容器也是添加到这个网桥中。
也可以自定义网络,相比默认的具备内部DNS发现,可以通过容器名(--name)进行容器之
间网络通信,可以解决因重建容器导致容器ip发生改变,导致前端应用无法连接后端服务
的问题。
小结:
1) 不通容器之间,主机和容器之间是完全隔离的。
2) docker0桥类似于一个交换机,把这个桥下的所有容器都连接起来,起到一个二层数据转
发的功能。
3) veth设备解决容器和宿主机之间网络命名空间隔离的问题。
4) 宿主机能到达的外部网络,容器都能够到达。
5) 宿主机可以访问其下的所有宿主机。
6) 同一宿主机内不同桥接网卡下的容器不能通信。
(2) host
--net=host
容器不会获得一个独立的network namespace,而是与宿主机共用一个。这就意味着容器不
会有自己的网卡信息,而是使用宿主机的,容器除了网络,其他都是隔离的。
示例:
# docker run -d -it --name nginx_v4 --net=host busybox sh
# docker exec -it nginx_v4 sh
进入nginx_v4容器内查看的网卡信息和宿主机上的网卡相同。
(3) none
--net=none
获取独立的network namespace,但不为容器进行任何网络配置,需要我们手动配置。
示例:
# docker run -d -it --name nginx_v3 --net none busybox sh
# docker exec -it nginx_v3 sh
除了本地环回网卡(lo),没有其他的网卡信息。
(4) container
--net=container:Name/ID
与指定的容器使用同一个network namespace,具有同样的网络配置信息,两个容器除了网络,
其他都还是隔离的。
示例:
nginx_v1使用的bridge网络,nginx_v2共享nginx_v1的网络
# docker run -d -it --name nginx_v1 busybox sh
# docker run -d -it --name nginx_v2 --net=container:nginx_v1 busybox sh
# docker exec -it nginx_v1 sh
# docker exec -it nginx_v2 sh
(6) 查看网络docker网络信息
# docker network ls
5.2 Docker网络模型
(1) veth pair
成对出现的一种虚拟网络设备,数据从一端进,从另一端出。用于解决网络命名空间之间隔离。
(2) docker0
网桥是一个二层网络设备,通过网桥可以将Linux支持的不同的端口连接起来,并实现类似交
换机那样的多对多的通信。
5.3 容器网络访问原理
1 外部访问容器
2 容器访问外部
3 小结
5.4 容器网络实现核心技术(Iptables)
1 iptables基础知识
(1) 四表五链
表 | 链 |
filter(过滤) | INPUT、OUTPUT、FORWARD |
nat(地址转换) | PREROUTING、POSTROUTING、OUTPUT |
mangle(拆包、修改、封装) | INPUT、OUTPUT、PREROUTING、POSTROUTING、OUTPUT |
raw(数据包状态跟踪) | PREROUTING、OUTPUT |
(2) 操作命令
2 容器网络实现核心技术
(1) Iptables 工作流程
INPUT链 # 接收的数据包是本机(入站)时,应用此链中的规则。
OUTPUT链 # 本机向外发送数据包(出站)时,应用此链中的规则。
FORWARD链 # 需要通过防火墙中转发送给其他地址的数据包(转发)
# 时,应用测链中的规则。
PREROUTING链 # 在对数据包做路由选择之前,应用此链中的规则。DNAT
POSTROUTING链 # 在对数据包做路由选择之后,应用此链中的规则。SNAT
5.5 跨主机网络(实现Docker主机容器通信)
通过桥接的方式实现
1 实验环境
主机名 | IP地址 | 安装软件 |
etcd | 172.16.1.71 | etcd |
docker01 | 172.16.1.72 | docker、flannel |
docker02 | 172.16.1.73 | docker、flannel |
Flannel是CoreOS维护的一个网络组件,在每个主机上运行守护进程,负责维护本地路由转
发,Flannel使用ETCD来存储容器网络与主机之间的关系。其他主流容器跨主机网络方案有:Weave、Calico、OpenvSwitch
2 ectd安装配置
在172.16.1.71节点上操作
(1) 安装ectd
# yum install etcd -y
# egrep -v "^$|^#" /etc/etcd/etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://172.16.1.71:2379"
ETCD_NAME="default"
ETCD_ADVERTISE_CLIENT_URLS="http://172.16.1.71:2379"
# systemctl start etcd.service
# systemctl enable etcd.service
# netstat -tunlp | grep etcd
(2) 向etcd写入子网
# etcdctl --endpoints="http://172.16.1.71:2379" set /atomic.io/network/config '{ "Network":"172.17.0.0/16", "Backend": {"Type": "vxlan"} }'
# 注意:子网地址不要和宿主机上的网段冲突
3 flannel安装并配置
在172.16.1.72、73节点上操作
(1) 安装flannel
# yum install flannel -y
# egrep -v "^$|^#" /etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS="http://172.16.1.71:2379"
FLANNEL_ETCD_PREFIX="/atomic.io/network"
# systemctl start flanneld.service
# systemctl enable flanneld.service
(2) 查看flannel网络信息
1) 172.16.1.72节点
# cat /var/run/flannel/docker
DOCKER_OPT_BIP="--bip=172.17.72.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=172.17.72.1/24 --ip-masq=true --mtu=1450"
2) 172.16.1.73节点
# cat /var/run/flannel/docker
DOCKER_OPT_BIP="--bip=172.17.66.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=172.17.66.1/24 --ip-masq=true --mtu=1450"
4 配置Docker使用flannel生成的网络信息
在172.16.1.72、73节点上操作
(1) 配置docker systemd引用flannel的分配的网络
# vim /usr/lib/systemd/system/docker.service
EnvironmentFile=/run/flannel/docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $DOCKER_NETWORK_OPTIONS
(2) 重启docker
# systemctl restart docker
5 查看172.16.1.72、73节点的网卡信息
(1) 172.16.1.72节点
(2) 172.16.1.73节点
6 设置iptables允许转发
在172.16.1.72、73节点上操作
# iptables -P FORWARD ACCEPT
7 测试
(1) 在172.16.1.72节点上创建名为nginx_v72的容器
# dockernginx_v72
# docker exec -it nginx_v72 sh
(2) 在172.16.1.73节点上创建名为nginx_v73的容器
# docker run -d -it --name nginx_v73 busybox sh
# docker exec -it nginx_v73 sh
(3) 容器nginx_v72 ping 容器nginx_v73
(4) 容器nginx_v73容器nginx_v72
(5) 在宿主机下访问容器ip地址
1) 使用docker01主机ping nginx_v72、nginx_v73容器
2)使用docker02主机ping nginx_v72、nginx_v73容器
8 查看宿主机路由
(1) 172.16.1.72节点
(2) 172.16.1.73节点
6 Dockerfile定制容器镜像
6.1 Dockerfile介绍
6.2 Dockerfile常用指令
指令 | 描述 |
FROM | 构建新镜像是基于哪个镜像 |
LABEL | 标签 |
RUN | 构建镜像时运行的Shell命令 |
COPY | 拷贝文件或目录到镜像中 |
ADD | 解压压缩包并拷贝 |
ENV | 设置环境变量 |
USER | USER为RUN、CMD和ENTRYPOINT执行命令指定运行用户 |
EXPOSE | 声明容器运行的服务端口 |
WORKDIR | WORKDIR为RUN、CMD、ENTRYPOINT、COPY和ADD设置工作目录 |
CMD | CMD运行容器时默认执行,如果有多个CMD指令,最后一个生效 |
ENTRYPOINT | ENTRYPOINT如果与CMD一起用,CMD将作为ENTRYPOINT的默认参数,如果有多个 |
6.3 docker build构建镜像
Usage: docker build [OPTIONS] PATH | URL | - [flags]
Options:
-t, --tag list # 镜像名称
-f, --file string # 指定Dockerfile文件位置,如果构建文件是Dockerfile,可以不指定。
PATH:
构建镜像时所需文件的存放目录,Dockerfile文件中从宿主机上读取文件的命令都是从这
个目录里读取的。
# docker build -t shykes/myapp .
# docker build -t shykes/myapp -f /path/Dockerfile /path
# docker build -t shykes/myapp http://www.example.com/Dockerfile
6.4 CMD与ENTRYPOINT区别
1CMD用法
(1) exec形式(首选)
CMD ["executable", "param1", "param2"]
CMD ["/usr/bin/sh", "-c", "executable param1 $param2"]
# 传递Dockerfile中定义的变量
(2) shell形式
CMD command param1 param2
2 ENTRYPOINT用法
(1) exec形式(首选)
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT ["/usr/bin/sh", "-c", "executable param1 $param2"]
# 传递Dockerfile中定义的变量
(2) shell形式
ENTRYPOINT command param1 param2
3 ENTRYPOINT和CMD结合使用
ENTRYPOINT [ "executable" ]
CMD [ "param1", "param2" ]
# CMD作为ENTRYPOINT的默认参数
4 小结
(1) CMD和ENTRYPOINT指令都可以用来定义运行容器时所使用的默认命令,
使用方法相同。
(2) Dockerfile至少指定一个CMD或ENTRYPOINT,如果Dockerfile中定义了
多个CMD或多个ENTRYPOINT,只有最后一个定义生效。
(3) docker run指定<command>时,将会覆盖CMD,ENTRYPOINT不会被覆盖。
(4) 如果是可执行文件,希望docker run时传参,应该使用ENTRYPOINT作执
行文件,CMD用作ENTRYPOINT的默认参数。好处是,用户不传参时使用默
认的CMD参数,用户自定义传参时覆盖默认参数,方便用户灵活传参。
6.5 前端项目镜像构建与部署(Nginx)
1 Dockerfile文件
# ls /tools/DockerFile/nginx/
Dockerfile nginx-1.19.1.tar.gz nginx.conf php.conf
# cat /tools/DockerFile/nginx/Dockerfile
FROM centos:7
LABEL maintainer liuchang
RUN yum install -y gcc gcc-c++ make \
openssl-devel pcre-devel gd-devel \
iproute net-tools telnet wget curl && \
yum clean all && \
rm -rf /var/cache/yum/*
RUN groupadd -g 1200 nginx && \
useradd -M -s /sbin/nologin -u 1200 -g nginx nginx
COPY nginx-1.19.1.tar.gz /
RUN tar -zxf nginx-1.19.1.tar.gz && \
cd nginx-1.19.1 && \
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--user=nginx \
--group=nginx && \
make -j 4 && make install && \
rm -rf /usr/local/nginx/html/* && \
cd / && rm -rf nginx* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV PATH $PATH:/usr/local/nginx/sbin
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
WORKDIR /usr/local/nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
2 构建nginx镜像
# docker build -t nginx:v1 -f /tools/DockerFile/nginx/Dockerfile /tools/DockerFile/nginx/
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v1 24ed66fdbc2d 17 minutes ago 337MB
6.6 PHP项目镜像构建(PHP)
1 Dockerfile文件
# ls /tools/DockerFile/php/
Dockerfile php-7.2.19.tar.gz php-fpm.conf php.ini wordpress-5.4.2.tar.gz www.conf
# cat /tools/DockerFile/php/Dockerfile
FROM centos:7
LABEL maintainer liuchang
RUN yum install epel-release -y && \
yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
libcurl-devel libjpeg-devel libpng-devel openssl-devel \
libmcrypt-devel libxslt-devel libtidy-devel autoconf \
iproute net-tools telnet wget curl && \
yum clean all && \
rm -rf /var/cache/yum/*
RUN groupadd -g 1200 nginx && \
useradd -M -s /sbin/nologin -u 1200 -g nginx nginx
COPY php-7.2.19.tar.gz /
RUN tar -zxf php-7.2.19.tar.gz && \
cd php-7.2.19 && \
./configure --prefix=/usr/local/php \
--with-config-file-path=/usr/local/php/etc \
--enable-fpm --enable-opcache \
--with-mysql --with-mysqli --with-pdo-mysql \
--with-openssl --with-zlib --with-curl --with-gd \
--with-jpeg-dir --with-png-dir --with-freetype-dir \
--enable-mbstring --with-mcrypt --enable-hash \
--with-fpm-user=nginx \
--with-fpm-group=nginx && \
make -j 4 && make install && \
cp -a php.ini-production /usr/local/php/etc/php.ini && \
cp -a /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf && \
cp -a /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf && \
sed -i 's/\;daemonize = yes/daemonize = no/' /usr/local/php/etc/php-fpm.conf && \
sed -i 's/127.0.0.1:9000/0.0.0.0:9000/' /usr/local/php/etc/php-fpm.d/www.conf && \
mkdir /usr/local/php/log && \
cd / && rm -rf php* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV PATH $PATH:/usr/local/php/sbin
COPY php.ini /usr/local/php/etc/
COPY php-fpm.conf /usr/local/php/etc/
COPY www.conf /usr/local/php/etc/php-fpm.d/
WORKDIR /usr/local/php
EXPOSE 9000
CMD ["php-fpm"]
2 构建php镜像
# docker build -t php:v1 -f /tools/DockerFile/php/Dockerfile /tools/DockerFile/php
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
php v1 eb8fc1539da5 11 minutes ago 574MB
6.7 案例(容器化搭建个人博客系统)
0 环境准备
# mkdir -p /opt/php_wordpress/{mysql_data,mysql_cnf,nginx_conf,wwwroot}
# cp -a /tools/DockerFile/nginx/php.conf /opt/php_wordpress/nginx_conf/
# cp -a /tools/DockerFile/mysql/my.cnf /opt/php_wordpress/mysql_cnf/
1 自定义网络
# docker network create lnmp
2 创建mysql数据库
# docker run -d \
-p 3306:3306 --name lnmp_mysql \
--net lnmp --restart=always \
-v /opt/php_wordpress/mysql_data:/var/lib/mysql \
-v /opt/php_wordpress/mysql_cnf:/etc/mysql \
-v /etc/localtime:/etc/localtime \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=wordpress \
mysql:5.7 --character-set-server=utf8
3 创建php容器
# docker run -d \
--name lnmp_php --net lnmp \
-v /opt/php_wordpress/wwwroot:/usr/local/nginx/html \
--restart=always php:v1
4 创建nginx容器
# docker run -d \
--name lnmp_nginx --net lnmp \
-p 80:80 \
-v /opt/php_wordpress/wwwroot:/usr/local/nginx/html \
-v /opt/php_wordpress/nginx_conf/php.conf:/usr/local/nginx/conf/conf.d/php.conf \
--restart=always nginx:v1
5 以wordpress博客为例
(1) 解压wordpress安装包
# cd /opt/php_wordpress/wwwroot/
# tar -xzf /tools/DockerFile/php/wordpress-5.4.2.tar.gz
# chown -R 1200:1200 wordpress/
(2) 访问
http://172.16.1.71/wordpress/
6.8 JAVA项目镜像构建(Tomcat)
1 Dockerfile文件
# ls /tools/DockerFile/tomcat/
apache-tomcat-8.5.43.tar.gz catalina.sh Dockerfile jdk-8u45-linux-x64.tar.gz jpress-v3.2.5.war server.xml
# cat /tools/DockerFile/tomcat/Dockerfile
FROM centos:7
LABEL maintainer liuchang
RUN yum install wget curl unzip iproute net-tools -y && \
yum clean all && \
rm -rf /var/cache/yum/* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY jdk-8u45-linux-x64.tar.gz /
RUN tar -xzf jdk-8u45-linux-x64.tar.gz && \
mv jdk1.8.0_45 /usr/local/jdk && \
rm -rf jdk-8u45-linux-x64.tar.gz
COPY apache-tomcat-8.5.43.tar.gz /
RUN tar -zxf apache-tomcat-8.5.43.tar.gz && \
mv apache-tomcat-8.5.43 /usr/local/tomcat && \
rm -rf apache-tomcat-8.5.43.tar.gz && \
rm -rf /usr/local/tomcat/webapps/*
ENV JAVA_HOME /usr/local/jdk
ENV CLASSPATH $JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar
ENV PATH $JAVA_HOME/bin:/usr/local/tomcat/bin:$PATH
COPY catalina.sh /usr/local/tomcat/bin
COPY server.xml /usr/local/tomcat/conf
RUN chmod +x /usr/local/tomcat/bin/catalina.sh
WORKDIR /usr/local/tomcat
EXPOSE 8080
CMD ["catalina.sh", "run"]
2 构建镜像
# docker build -t tomcat:v1 -f /tools/DockerFile/tomcat/Dockerfile /tools/DockerFile/tomcat/
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat v1 4ea00c9980f3 About a minute ago 766MB
3 启动jpress网站测试
(1) 创建容器
# mkdir -p /opt/tomcat/webapp/
# cp /tools/DockerFile/tomcat/jpress-v3.2.5.war /opt/tomcat/webapp/ROOT.war
# docker run -d \
--name tomcat_jp -p 8080:8080 \
-v /opt/tomcat/webapp:/usr/local/tomcat/webapps \
--restart=always tomcat:v1
(2) 访问
6.9 JAVA微服务镜像构建(Jar)
1 构建镜像
# ls /tools/DockerFile/jdk/
Dockerfile hello.jar jdk-8u45-linux-x64.tar.gz
# cat /tools/DockerFile/jdk/Dockerfile
FROM centos:7
LABEL maintainer liuchang
RUN yum install wget curl unzip iproute net-tools -y && \
yum clean all && \
rm -rf /var/cache/yum/* && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY jdk-8u45-linux-x64.tar.gz /
RUN tar -xzf jdk-8u45-linux-x64.tar.gz && \
mv jdk1.8.0_45 /usr/local/jdk && \
rm -rf jdk-8u45-linux-x64.tar.gz
ENV JAVA_HOME /usr/local/jdk
ENV CLASSPATH $JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar
ENV PATH $JAVA_HOME/bin:$PATH
ENV JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+08"
COPY hello.jar /
EXPOSE 8888
CMD ["/bin/sh", "-c", "java -jar $JAVA_OPTS /hello.jar"]
2 启动镜像
# cd /tools/DockerFile/jdk/
# docker build -t jdk_ms:v1 .
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jdk_ms v1 3c3a5efb74fd 59 seconds ago 747MB
3 测试
# docker run -d --name micro_server jdk_ms:v1
# docker logs micro_server
Hello world!
sleep 1 day...
6.10 编写Dockerfile最佳实践
(1) 减少镜像层
一次RUN、COPY、ADD指令会形成新的一层,尽量Shell命令都写在一行,减少镜像层。
(2) 优化镜像大小
一次RUN、COPY、ADD形成新的一层,如果没有在同一层删除,无论文件是否最后删除,
都会带到下一层,所以要在每一层清理对应的残留数据,减小镜像大小。
(3) 减少网络传输时间
例如软件包、mvn仓库等
(4) 多阶段构建
代码编译、部署在一个Dockerfile完成,只会保留部署阶段产生数据。
(5) 选择最小的基础镜像
例如alpine
7 补充
1 子网
能不能通信是建立在同一网段(网络号一致)的基础上的,并不是说子网掩码一致或不一致
就可以通信或不能通信。
举个例子:
192.168.0.1/24与192.168.0.2/25子网掩码分别是255.255.255.0与255.255.255.128,通过
ip地址与子网掩码与运算得出网络号均为192.168.0.0,即这两个 IP是属于同一网段,可
以通信。而192.168.0.1/25与192.168.0.65/25,虽然子网掩码一致,但不属于同一网段,
前者是192.168.0.0 后者是192.168.0.64,不能进行通信。