Video Reference:最新快速掌握Docker实战课
1 为什么使用容器?
- 上线流程繁琐
开发 → 测试 → 申请资源 → 审批 → 部署 → 测试等环节
- 资源利用率低
普遍服务器利用率低,造成过多性能资源浪费
- 扩容/缩容不及时
业务高峰期扩容流程繁琐,上线不及时
已知的扩容:即遇到了六一八,双十一等电商节
未知的扩容:即管理员不知道业务的高峰期在什么时候,一般如何解决:
- 业务机器增加承载量,可以应对一些突发。30%
- 弹性伸缩
- 服务器环境臃肿
服务器越来越臃肿,对维护、迁移带来困难
- 环境不一致性
开发环境,测试环境,线上环境不一致
:::info 软件开发从传统的模型开始向微服务架构转变。Docker 优化或者解决了以上出现的问题。
:::
2 容器 VS 虚拟机
2.1 容器和虚拟机对比
传统的虚拟机能够基于虚拟化技术更加有效的利用硬件计算资源,可以实现云租户的隔离与资源共享。相比虚拟机来说,容器更轻、更快,但是作为一种新技术,容器的安全防护也与虚拟机所有不同。 容器与虚拟机具有相似的资源隔离和分配价值,但容器的作用不同,因为容器是虚拟化操作系统而不是硬件。容器更便携,更高效。
- Hypervisor 是运行在物理硬件和操作系统的中间软件层,可以允许多个操作系统共享一个物理硬件。虚拟化的核心就是Hypervisor ,主要用来模拟多个虚拟机(包括CPU,内存,硬盘等硬件设备和 Guest OS子操作系统[二进制文件,各种lib库] )。Hypervisor 的实现方式有:VMware的ESXi、微软的Hyper-V或者思杰的XenServer。
- 提升服务器资源利用率
- 提供一个完全隔离的环境
- Docker Engine 是Docker引擎,运行Docker容器。Docker 主要是用来实现应用以及其携带的基础环境的整体打包。
- 容器提供一个基本的独立环境,实现隔离,资源限制。
- 主要解决应用层面问题,应用快速部署,高效管理。

小总结
| Container容器 | VM虚拟机 | |
|---|---|---|
| 启动速度 | 秒级 | 分钟级 |
| 运行性能 | 接近原生 | 5%左右损失 |
| 磁盘占用 | MB | GB |
| 数量 | 成百上千 | 一般几十台 |
| 隔离性 | 进程级(操作系统级别的虚拟化) | 系统级(更彻底,硬件级别的虚拟化) |
| 操作系统 | 主要支持Linux,目前Windows也支持 | 几乎所有 |
| 封装速度 | 只打包项目代码和依赖关系,共享宿主机内核 | 完整的操作系统 |
2.2 容器逃逸
容器逃逸,是容器技术启用以来一直被关注的问题,甚至被认为是容器的首要安全问题。所谓“逃逸”,指的是“流氓”容器/虚拟机尝试突破隔离环境的限制,访问宿主系统或者在同一个系统上的同驻容器或虚拟机。从而造成敏感信息泄露,或者系统及服务发生DOS的行为。
参考文献:[ https://new.qq.com/rain/a/20210224A09POO00 ]
3 Docker 是什么
3.1 Docker 大致介绍
- 使用最广泛的开源容器引擎
- 一种操作系统级的虚拟化技术
- 依赖于Linux内核特性: Namespace(资源隔离)和Cgroups(资源限制)
- 一个简单的应用程序打包工具
3.2 Docker 设计目标
- 提供简单的应用程序打包工具
- 开发人员和运维人员职责逻辑分离
- 多环境保持一致性
开发人员:
- 开发代码
- 打包项目环境 + 代码成镜像
- 部署到容器平台
运维人员:高效的管理应用容器(监控,日志,快捷部署方式)
好处:
- 运维人员节省人工成本
- 可以更加高效的解决问题
:::info
(1) 提供一个简单、轻量的建模方式 Docker上手非常快,用户只需几分钟,就可以把自己的程序“Docker化”。Docker依赖于“写时复制”(copy-on-write)模型,使修改应用程序也非常迅速,可以说达到了“随心所至,代码即改”的境界。 随后,就可以创建容器来运行应用程序了。大多数Docker容器只需不到1秒钟即可启动。由于去除了管理程序的开销,Docker容器拥有很高的性能,同时同一台宿主机中也可以运行更多的容器,使用户可以尽可能充分地利用系统资源。 (2)职责的逻辑分离 使用Docker,开发人员只需关心容器中运行的应用程序,而运维人员只需要关心如何管理容器。Docker设计的目的就是要加强开发人员写代码的开发环境与应用程序要部署的生产环境的一致性,从而降低那种“开发时一切都正常,肯定是运维的问题”的风险。 (3)快速、高效的开发声明周期 Docker的目标之一就是缩短代码从开发、测试到部署、上线运行的周期,让你的应用程序具备可移植性,易于构建,并易于协作。 (4)鼓励使用面向服务的架构 Docker还鼓励面向服务的架构和微服务架构。Docker推荐单个容器只运行一个应用程序或进程,这样就形成了一个分布式的应用程序模型,在这种模型下,应用程序或服务都可以表示为一系列内部互联的容器,从而使分布式部署应用程序,扩展或调试应用程序多变得非常简单,同时也提高了程序的内省性。:::
3.3 Docker 应用场景
- 应用程序打包和发布
- 应用程序隔离
- 持续集成
- 部署微服务(开发思想,将大的项目拆分成多个子项目组合)
- 快速搭建测试环境
- 提供PaaS产品(平台即服务)
3.4 Docker 基本组成

Docker Client:在Linux命令行中的执行 Docker 客户端操作指令。
Docker Host:Docker 主机,Docker 守护进程会在主机中运行,会收集Docker Client的指令并作出相应的执行过程和输出相应的结果。例如在Docker Client执行创建容器,那么Docker Daemon会收到其请求并创建相应的容器。
Docker Registry:仓库(Repository)是集中存放镜像文件的场所。
仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。 最大的公开仓库是Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等。3.5 版本与支持平台
Docker版本:
- 社区版(Community Edition,CE)
- 企业版(Enterprise Edition,EE)
支持平台:
- Linux ( CentOS , Debian , Fedora , Oracle Linux , RHEL , SUSE 和 Ubuntu )
- Mac
- Windows
4 Docker 的使用
4.1 Docker 的安装
安装前建议关闭SELinux功能和firewalld
# Step 1: 关闭SELinux功能# 临时关闭SELINUXsetenforce 0 ; getenforce# 永久关闭SELINUXsed -i.bak -r '/^SELINUX/s@(.*)=(.*)@\1=disabled@g' /etc/selinux/config# Step 2: 关闭Firewalld防火墙功能systemctl stop firewalld && systemctl disable firewalld
范例:Docker 安装
# Step 1: 更换Aliyun源wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo# Step 2: 删除系统中旧有的Docker引擎和相关的依赖sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine# Step 3: 删除Docker的数据目录sudo rm -rf /var/lib/docker/# 安装 Docker# step 1: 安装必要的一些系统工具sudo yum install -y yum-utils device-mapper-persistent-data lvm2# Step 2: 添加软件源信息sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo# Step 3: 修改为阿里云源sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo# Step 4: 更新并安装Docker-CEsudo yum makecache fastsudo yum -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin# Step 4: 开启Docker服务sudo systemctl enable --now docker# 注意:# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。# 例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。# vim /etc/yum.repos.d/docker-ce.repo# 将[docker-ce-test]下方的enabled=0修改为enabled=1## 安装指定版本的Docker-CE:# Step 1: 查找Docker-CE的版本:# yum list docker-ce.x86_64 --showduplicates | sort -r# Loading mirror speeds from cached hostfile# Loaded plugins: branch, fastestmirror, langpacks# docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable# docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable# docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable# Available Packages## Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)# sudo yum install docker-ce-<VERSION_STRING> \# docker-ce-cli-<VERSION_STRING> \# containerd.io \# docker-compose-plugin
:::info Docker CentOS安装官方文档:[ https://docs.docker.com/engine/install/centos/ ]
阿里云源:https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
:::
范例:验证Docker信息
# 查看Docker的详细信息显示sudo docker info# 查看Docker的版本sudo docker version# 查看Docker的进程sudo docker ps# 查看Docker的镜像sudo docker images

4.2 Docker 镜像管理
4.2.1 镜像是什么
- 一个分层存储的文件,不是一个单一的文件
- 一个软件的环境
- 一个镜像可以创建N个容器
- 一种标准化的交付
- 一个不包含Linux内核而又精简的Linux操作系统( 共享宿主机内核,不是一个完整Linux操作系统 )
4.2.2 配置加速器
Docker Hub是由Docker公司负责维护的公共镜像仓库,包含大量的容器镜像,Docker工具默认从这个公共镜像库下载镜像地址: https://hub.docker.com
可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器
sudo mkdir -p /etc/docker# 配置文件使用的JSON格式sudo tee /etc/docker/daemon.json <<-'EOF'{"registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com"]}EOFsudo systemctl daemon-reloadsudo systemctl restart docker
范例:查看加速器配置为阿里云加速器
$ sudo docker info | grep -A 1 "Registry Mirrors"Registry Mirrors:https://po13h3y1.mirror.aliyuncs.com/
4.2.3 Docker image 命令
命令格式:docker image COMMAND
| 指令 | 描述 |
|---|---|
| ls | 列出镜像 |
| build | 构建镜像来自Dockerfile |
| history | 查看镜像历史 |
| inspect | 显示一个或多个镜像详细信息 |
| pull | 从镜像仓库拉取镜像 |
| push | 推送─个镜像到镜像仓库 |
| rm | 移除─个或多个镜像 |
| prune | 移除没有被标记或者没有被任何容器引用的镜像 |
| tag | 创建一个引用源镜像标记目标镜像 |
| save | 保存一个或多个镜像到一个tarl归档文件 |
| load | 加载镜像来自tar归档或标准输入 |
范例:镜像常用管理命令
# Step 1: 运行一个Nginx容器docker run -d --name nginx-container nginx# Step 2: 镜像管理命令# 查看本地镜像$ docker image ls [ docker images ]REPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 605c77e624dd 8 months ago 141MB# 查看镜像的历史docker image history nginx# 查看镜像的详细信息显示docker image inspect nginx# 下载镜像(nginx)docker image pull nginxdocker pull nginx# 删除镜像docker image rm nginxdocker rmi nginx# 删除虚悬镜像(中间产物镜像)docker image prune# 将没有创建出容器的镜像全部删除docker image prune -a# 将镜像导出(nginx),可以使用重定向的方式docker save -o nginx.tar nginxdocker save nginx > nginx.tar# 默认只有归档,没有压缩;所以产生的镜像包较大,可以使用gzip|bz2的方式将镜像包压缩docker save nginx | gzip > nginx.tar.gzdocker save nginx | bzip2 > nginx.tar.bz2# 将镜像导入(nginx),可以使用重定向的方式docker load -i nginx.tardocker laod < nginx.tar.gz# 镜像使用的联合文件系统的机制,镜像相当于是模板,可以使用镜像创建多个容器# 启动Nginx容器,并暴露80端口映射到宿主机的8080端口docker run -d --name nginx-node -p 8080:80 --restart=always nginxsudo curl localhost:8080
4.3 Docker 容器管理
4.3.1 创建容器常用选项
# 查看创建容器的帮助文档docker run --help
命令格式:docker run [OPTIONS] IMAGE [COMMAND] [ARGS……]
| 选项 | 描述 |
|---|---|
| -i, —interactive | 交互式 |
| -t , —tty | 分配一个伪终端 |
| -d, —detachr | 运行容器到后台 |
| -e,—env | 设置环境变量 |
| -p,—publish list | 发布容器端口到主机 |
| -P,—publish-all | 发布容器所有EXPOSE的端口到宿主机随机端口 |
| —name string | 指定容器名称 |
| -h, —hostname | 设置容器主机名 |
| —ip string | 指定容器IP,只能用于自定义网络 |
| —network | 连接容器到一个网络 |
| -v,—volume list —mount mount(新方式) |
将文件系统附加到容器 |
| —restart string | 容器退出时重启策略,默认no,可选值:[always l on-failure] |
范例:创建容器常用选项
# Step 1: 进入到容器的系统内部# 启动一个Ubuntu容器,操作系统类容器需要有进程夯筑容器运行$ docker run -it -d --name ubuntu-node ubuntu /bin/bash$ docker exec -it ubuntu-node /bin/bash | docker attach ubuntu-node# 查看容器的版本(ubuntu)root@9650093ce0c2:/# cat /etc/os-releaseNAME="Ubuntu"VERSION="20.04.3 LTS (Focal Fossa)"ID=ubuntuID_LIKE=debianPRETTY_NAME="Ubuntu 20.04.3 LTS"VERSION_ID="20.04"HOME_URL="https://www.ubuntu.com/"SUPPORT_URL="https://help.ubuntu.com/"BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"VERSION_CODENAME=focalUBUNTU_CODENAME=focal# 查看容器的进程root@9650093ce0c2:/# psPID TTY TIME CMD27 pts/3 00:00:00 bash35 pts/3 00:00:00 ps# Step 2: 启动Nginx容器,并暴露80端口映射到宿主机的8080端口docker run -d --name nginx-node -p 8080:80 --restart=always nginxsudo curl localhost:8080# Step 3: 测试多个容器参数$ docker run -it -d -e env=prod --name container-nginx \-p 8088:80 --restart=always -h nginx-node nginx# 查看最新创建的容器信息$ docker ps -lCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1dcbec1b129c nginx "/docker-entrypoint.…" 6 seconds ago Up 5 seconds 0.0.0.0:8088->80/tcp, :::8088->80/tcp container-nginx$ sudo curl localhost:8088# 进入到container-nginx容器中$ docker exec -it container-nginx /bin/bashroot@nginx-node:/# hostnamenginx-noderoot@nginx-node:/# echo $envprodroot@nginx-node:/# echo "Hello Nginx Container" > /usr/share/nginx/html/index.htmlroot@nginx-node:/# curl localhostHello Nginx Containerroot@nginx-node:/# exit# 重启Docker服务后该容器会自动启动(--restart=always)$ systemctl restart docker$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1dcbec1b129c nginx "/docker-entrypoint.…" 4 minutes ago Up 9 seconds 0.0.0.0:8088->80/tcp, :::8088->80/tcp container-nginx# 查看容器的详细信息显示$ docker inspect container-nginx
命令格式:docker run [OPTIONS] IMAGE [COMMAND] [ARGS……]
| 选项 | 描述 |
|---|---|
| -m,—memory | 容器可以使用的最大内存量 |
| —memory-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共享(相对权重) |
范例:创建容器常用选项
# -m="300m" 使用300M内存# --cpus="1" 使用一颗CPU$ docker run -it -d --name ubuntu-container -m="300m" --cpus="1" ubuntu /bin/bash# 查看容器的资源$ docker stats --no-stream ubuntu-containerCONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDSd74184890bae ubuntu-container 0.00% 540KiB / 300MiB 0.18% 656B / 0B 0B / 0B 1
Docker利用namespace实现了容器之间资源隔离,但是namespace不能对容器资源限制,比如CPU、内存。
如果某一个容器属于CPU密集型任务,那么会影响其他容器使用CPU,导致多个容器相互影响并且抢占资源。
如何对多个容器的资源使用进行限制就成了容器化的主要问题。
答:引入Control Groups(简称CGroups),限制容器资源
CGroups:所有的任务就是运行在系统中的一个进程,而CGroups以某种标准讲一组进程为目标进行资源分配和控制。例如CPU、内存、带宽等,并且可以<font style="color:#E8323C;">动态配置</font>。
4.3.2 常用管理命令
命令格式:docker container COMMAND
| 选项 | 描述 |
|---|---|
| ls | 列出容器 |
| inspect | 查看一个或多个容器详细信息 |
| exec | 在运行容器中执行命令 |
| commit | 创建—个新镜像来自—个容器 |
| cp | 拷贝文件/文件夹到一个容器 |
| logs | 获取一个容器日志 |
| port | 列出或指定容器端口映射 |
| top | 显示一个容器运行的进程 |
| stats | 显示容器资源使用统计 |
| stop / start / restart | 停止/启动一个或多个容器 |
| rm | 删除─个或多个容器 |
| prune | 移除已停止的容器 |
范例:常用管理命令
# Step 1: 列出当前DockerHost容器$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESd74184890bae ubuntu "/bin/bash" 3 minutes ago Up 3 minutes ubuntu-container1dcbec1b129c nginx "/docker-entrypoint.…" 14 minutes ago Up 10 minutes 0.0.0.0:8088->80/tcp, :::8088->80/tcp container-nginx# 列出当前DockerHost所有容器$ docker ps -a# 列出最新创建的容器$ docker ps -l# Step 2: 查看容器的详细信息显示$ docker inspect [Container_ID|Container_Name]# Step 3: 进入到容器或者直接在DockerHost执行容器命令$ docker exec -it ubuntu-container /bin/bash$ docker exec ubuntu-container cat /etc/os-releaseNAME="Ubuntu"VERSION="20.04.3 LTS (Focal Fossa)"ID=ubuntuID_LIKE=debianPRETTY_NAME="Ubuntu 20.04.3 LTS"VERSION_ID="20.04"HOME_URL="https://www.ubuntu.com/"SUPPORT_URL="https://help.ubuntu.com/"BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"VERSION_CODENAME=focalUBUNTU_CODENAME=focal# Step 4: 查看容器日志docker logs container-nginx# Step 5: 将容器提交成一个镜像$ docker exec -it container-nginx /bin/bashroot@nginx-node:/# echo "Hello Nginx Container" > /usr/share/nginx/html/index.htmlroot@nginx-node:/# exit$ docker commit -a "zhongzw <zhongzhiwei@kubesphere.com>" -m "修改nginx默认页面" container-nginx mynginx:1.1.0$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEmynginx 1.1.0 20cad473dbf9 13 seconds ago 141MB$ docker image history mynginx:1.1.0IMAGE CREATED CREATED BY SIZE COMMENT20cad473dbf9 27 seconds ago nginx -g daemon off; 1.12kB 修改nginx默认页面605c77e624dd 8 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B<missing> 8 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B<missing> 8 months ago /bin/sh -c #(nop) EXPOSE 80 0B<missing> 8 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B<missing> 8 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB<missing> 8 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB<missing> 8 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB<missing> 8 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB<missing> 8 months ago /bin/sh -c set -x && addgroup --system -… 61.1MB<missing> 8 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B<missing> 8 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.1 0B<missing> 8 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.5 0B<missing> 9 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B<missing> 9 months ago /bin/sh -c #(nop) CMD ["bash"] 0B<missing> 9 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB# 将新镜像运行成容器docker run -it -d --name mynginx-container --restart=always -p 8888:80 mynginx:1.1.0sudo curl localhost:8888# Step 5: 容器与宿主机之间的文件拷贝Usage: docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH$ echo "hello docker" > docker.txt$ docker cp docker.txt ubuntu-container:/tmp$ docker exec ubuntu-container cat /tmp/docker.txthello docker$ docker cp ubuntu-container:/etc/passwd /tmp$ cat /tmp/passwd# Step 7: 查看容器暴露的端口$ docker port mynginx-container80/tcp -> 0.0.0.0:888880/tcp -> :::8888# Step 8: 容器运行的进程$ docker top mynginx-container# Step 9: 查看容器的资源利用率$ docker stats mynginx-container$ docker stats --no-stream mynginx-container# Step 10: 删除容器$ docker rm -f mynginx-container# 只查看容器的ID$ docker ps -q# 删除所有容器(-f 强制删除;-a 全部容器)$ docker rm -f $(docker ps -qa)
4.3.3 容器数据持久化
Docker提供两种方式将数据从宿主机挂载到容器中:
- volumes:Docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)。保存数据的最佳方式。.
- bind mounts:将宿主机上的任意位置的文件或者目录挂载到容器中。
volumes示例:
1、创建数据卷
docker volume create nginx-voldocker volume lsdocker volume inpect nginx-vol
2、使用数据卷
docker run -d --name=test-nginx --mount src=nginx-vol,dst=/usr/share/nginx/html nginxdocker run -d --name=test-nginx -v nginx-vol:/usr/share/nginx/html nginx
bind mounts示例:
1、挂载宿主机目录到容器
docker run -d --name nginx-test --mount type=bind,src=/app/wwwroot,dst=/usr//share/nginx/html nginxdocker run -d --name nginx-test -v /app/wwwroot:/usr//share/nginx/html nginx
范例:容器数据持久化
$ mkdir /opt/wwwroot# 容器数据持久化$ docker run -d -it --name web01 -p 88:80 -v /opt/wwwroot:/usr/share/nginx/html nginx# 测试访问$ curl 127.0.0.1:88<html><head><title>403 Forbidden</title></head><body><center><h1>403 Forbidden</h1></center><hr><center>nginx/1.21.5</center></body></html># 需要在/opt/wwwroot目录下创建HTML文件$ echo "Hello,Docker Web page" > /opt/wwwroot/index.html$ curl 127.0.0.1:88Hello,Docker Web page# 宿主机和容器共享该目录# 新创建一个nginx容器挂载到该目录下,并测试访问$ docker run -d -it --name web02 -p 99:80 -v /opt/wwwroot:/usr/share/nginx/html nginx$ curl 127.0.0.1:99Hello,Docker Web page
:::info 制作镜像:
- 启动容器之后的应用程序服务产生的数据尽量存放在一个目录下,例如:/data
- 构建镜像是不能将已经生成的应用程序数据打包进去的
Jenkins、Gitlab 使用 Docker 都是采用 -v 将容器自身产生的数据持久化到宿主机。
:::
4.3.4 容器网络

- veth pair:成对出现的一种虚拟网络设备,数据从一端进,从另一端出。用于解决网络命名空间之间隔离。(可以理解为网线,解决不同网络命名空间 ( 宿主机网络和容器网络 ) 之间的通信的)
- docker0:网桥是一个二层网络设备,通过网桥可以将Linux支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信。(解决不同容器之间通信 和 宿主机数据包以及容器数据包之间数据交换)
# docker 启动后会产生一个 docker0 的桥接设备# docker0 可以理解为是一个交换机,是将容器接入到交换机中,实现容器与容器之间的通信# 并且可以将数据包传递到宿主机处理。$ ifconfig docker0docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255inet6 fe80::42:45ff:fe1c:2b38 prefixlen 64 scopeid 0x20<link>ether 02:42:45:1c:2b:38 txqueuelen 0 (Ethernet)RX packets 32 bytes 3436 (3.3 KiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 47 bytes 3446 (3.3 KiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Docker 使用 iptables 实现网络通信
- 外部访问容器:

iptables -t nat -vnL DOCKER
- 容器访问外部:

范例:外部访问容器
iptables -t nat -vnL POSTROUTING
bash
# 查看宿主机容器进程
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES busybox-node
ec4e0869e87a nginx "/docker-entrypoint.…" 25 minutes ago Up 25 minutes 0.0.0.0:88->80/tcp, :::88->80/tcp web01
# 外部访问容器(iptables-DNAT)
$ iptables -t nat -vnL DOCKER
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:88 to:172.17.0.2:80
# iptables 判断本机的88端口要转发到172.17.0.2:80端口
# 宿主机会判断路由表信息
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.2 0.0.0.0 UG 100 0 0 eth0
10.0.0.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
# 数据包会转发到docker0网桥,docker0又是一个二层交换机。
# docker0 会发送ARP广播,找到172.17.0.2的容器,就可以基于MAC地址进行通信
### 所以在外部访问容器中关键在于 DNAT,路由表
范例:容器访问外部
$ docker run -it -d --name busybox-node busybox$ docker exec -it busybox-node /bin/sh# 可以访问外网/ # ping -c 1 -W 1 www.baidu.comPING www.baidu.com (183.232.231.174): 56 data bytes64 bytes from 183.232.231.174: seq=0 ttl=127 time=15.140 ms--- www.baidu.com ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 15.140/15.140/15.140 ms# 查看网卡信息/ # ifconfigeth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:04inet addr:172.17.0.4 Bcast:172.17.255.255 Mask:255.255.0.0UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1RX packets:13 errors:0 dropped:0 overruns:0 frame:0TX packets:5 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:0RX bytes:1100 (1.0 KiB) TX bytes:328 (328.0 B)lo Link encap:Local Loopbackinet addr:127.0.0.1 Mask:255.0.0.0UP LOOPBACK RUNNING MTU:65536 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)# 查看容器路由表/ # route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0# 查看宿主机容器进程$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8d12c3faadd3 busybox "sh" 8 minutes ago Up 8 minutes busybox-nodeec4e0869e87a nginx "/docker-entrypoint.…" 32 minutes ago Up 32 minutes 0.0.0.0:88->80/tcp, :::88->80/tcp web01# 可以判断到容器的下一跳就是docker0网桥设备,并且将数据包丢到docker0# docker0 设备将数据包转发到宿主机,# 例如容器ping百度,源IP:容器IP,目的IP:百度IP# 宿主机会查找路由表$ route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface0.0.0.0 10.0.0.2 0.0.0.0 UG 100 0 0 eth010.0.0.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0# 容器访问外部(iptables-SNAT)$ iptables -t nat -vnL POSTROUTINGChain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)pkts bytes target prot opt in out source destination3 202 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 # SNAT策略0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:80# 经过iptables的策略,将数据包的源IP改成为宿主机的IP发送出去# 经过宿主机的网关,发送到互联网中
5 Dockerfile 构建镜像
5.1 Dockerfile 概述
Docker通过Dockerfile自动构建镜像,Dockerfile是一个包含用于组建镜像的文本文件,由一条一条的指令组成。

5.2 Dockerfile 常用指令
一个镜像或者容器就是运行一个服务,不要当成虚拟机使用
| 指令 | 描述 |
|---|---|
| FROM | 构建新镜像是基于哪个镜像 |
| LABEL | 标签 |
| RUN | 构建镜像时运行的Shell命令 |
| COPY | 拷贝文件或目录到镜像中 |
| ADD | 解压压缩包并拷贝 |
| ENV | 设置环境变量 |
| USER | 为RUN、CMD和ENTRYPOINT执行命令指定运行用户 |
| EXPOSE | 声明容器运行的服务端口 |
| WORKDIR | 为RUN、CMD、ENTRYPOINT、COPY和ADD设置工作目录 |
| CMD | 运行容器时默认执行,如果有多个CMD指令,最后一个生效 |
范例:创建Dockerfile
$ mkdir Dockerfile-DIR ; cd Dockerfile-DIR$ vim Dockerfile1# 构建新镜像是基于哪个镜像FROM centos:centos7.9.2009# 标签LABEL zhongzhiwei <zhongzhiwei@kubesphere.com># 使用阿里云源配置Yum源(base 和 epel)RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \curl -o /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo# 构建镜像时运行的Shell命令RUN yum install -y nginx# 清理Yum缓存RUN yum clean all# 容器运行时执行的命令CMD ["/usr/sbin/nginx", "-g", "daemon off;"]# 声明容器运行的服务端口EXPOSE 80# 构建镜像$ docker build -t mynginx:1.0.0 -f Dockerfile1 .# 查看镜像信息$ docker images mynginx:1.0.0REPOSITORY TAG IMAGE ID CREATED SIZEmynginx 1.0.0 0e54bb26e7b4 40 seconds ago 483MB# 访问测试$ docker run -it -d --name mynginx-node -p 90:80 -v /opt/wwwroot/:/usr/share/nginx/html mynginx:1.0.0$ curl 10.0.0.101:90Hello,Docker Web page
5.3 构建镜像命令
# 使用 docker build 命令$ docker build --helpUsage: docker build [OPTIONS] PATH | URL | -Build an image from a DockerfileOptions:--add-host list Add a custom host-to-IP mapping (host:ip)--build-arg list Set build-time variables--cache-from strings Images to consider as cache sources--cgroup-parent string Optional parent cgroup for the container--compress Compress the build context using gzip--cpu-period int Limit the CPU CFS (Completely Fair Scheduler) period--cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota-c, --cpu-shares int CPU shares (relative weight)--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)--disable-content-trust Skip image verification (default true)-f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile')--force-rm Always remove intermediate containers--iidfile string Write the image ID to the file--isolation string Container isolation technology--label list Set metadata for an image-m, --memory bytes Memory limit--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap--network string Set the networking mode for the RUN instructions during build (default "default")--no-cache Do not use cache when building the image--pull Always attempt to pull a newer version of the image-q, --quiet Suppress the build output and print image ID on success--rm Remove intermediate containers after a successful build (default true)--security-opt strings Security options--shm-size bytes Size of /dev/shm-t, --tag list Name and optionally a tag in the 'name:tag' format--target string Set the target build stage to build.--ulimit ulimit Ulimit options (default [])
5.4 构建 nginx 镜像
编译安装一个软件:
- 安装依赖包
- ./configure 检查环境依赖
- make 编译
- make install 编译安装
# 下载nginx源码包$ wget http://nginx.org/download/nginx-1.16.1.tar.gz# 编写Dockerfile文件$ vim Dockerfile-nginxFROM centos:centos7.9.2009LABEL maintainer www.kubesphere.comRUN 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/*ADD nginx-1.16.1.tar.gz /RUN cd nginx-1.16.1 && \./configure --prefix=/usr/local/nginx \--with-http_ssl_module \--with-http_stub_status_module && \make -j 4 && make install && \mkdir /usr/local/nginx/conf/vhost && \cd / && rm -rf nginx* && \ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtimeENV PATH $PATH:/usr/local/nginx/sbinCOPY nginx.conf /usr/local/nginx/conf/nginx.confWORKDIR /usr/local/nginxEXPOSE 80CMD ["nginx", "-g", "daemon off;"]# 准备Nginx配置文件$ vim nginx.conf#user nobody;worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024;}http {include mime.types;default_type application/octet-stream;#log_format main '$remote_addr - $remote_user [$time_local] "$request" '# '$status $body_bytes_sent "$http_referer" '# '"$http_user_agent" "$http_x_forwarded_for"';#access_log logs/access.log main;sendfile on;#tcp_nopush on;#keepalive_timeout 0;keepalive_timeout 65;#gzip on;server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}#error_page 404 /404.html;# redirect server error pages to the static page /50x.html#error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {# proxy_pass http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {# root html;# fastcgi_pass 127.0.0.1:9000;# fastcgi_index index.php;# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;# include fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {# deny all;#}}# another virtual host using mix of IP-, name-, and port-based configuration##server {# listen 8000;# listen somename:8080;# server_name somename alias another.alias;# location / {# root html;# index index.html index.htm;# }#}# HTTPS server##server {# listen 443 ssl;# server_name localhost;# ssl_certificate cert.pem;# ssl_certificate_key cert.key;# ssl_session_cache shared:SSL:1m;# ssl_session_timeout 5m;# ssl_ciphers HIGH:!aNULL:!MD5;# ssl_prefer_server_ciphers on;# location / {# root html;# index index.html index.htm;# }#}}# 构建Nginx镜像$ docker build -t mynginx:1.2.0 -f Dockerfile-nginx .# 将镜像运行成容器$ docker run -it -d --name mynginx-node01 -p 91:80 mynginx:1.2.0# 测试访问(访问的默认页面)$ curl http://10.0.0.101:91<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.</p><p>For online documentation and support please refer to<a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p></body></html># 将其页面目录进行数据持久化$ docker run -it -d --name mynginx-node02 -p 92:80 -v /opt/wwwroot/:/usr/local/nginx/html/ mynginx:1.2.0# 测试访问$ curl http://10.0.0.101:92Hello,Docker Web page
5.5 构建 Tomcat 镜像
$ wget --no-check-certificate https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.82/bin/apache-tomcat-8.5.82.tar.gz$ vim Dockerfile-tomcatFROM centos:centos7.9.2009MAINTAINER www.kubesphere.comENV VERSION=8.5.82RUN yum install java-1.8.0-openjdk.x86_64 wget curl unzip iproute net-tools -y && \yum clean all && \rm -rf /var/cache/yum/*ADD apache-tomcat-${VERSION}.tar.gz /usr/local/RUN mv /usr/local/apache-tomcat-${VERSION} /usr/local/tomcat && \sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /usr/local/tomcat/bin/catalina.sh && \ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtimeENV PATH $PATH:/usr/local/tomcat/binWORKDIR /usr/local/tomcatEXPOSE 8080CMD ["catalina.sh" , "run"]$ docker build -t mytomcat:1.0.0 -f Dockerfile-tomcat .$ docker run -d -it -p 8080:8080 mytomcat:1.0.0$ curl 10.0.0.101:8080# Tomcat 编译后的页面路径 /usr/local/tomcat/webapps/ROOT# 将Tomcat的页面数据目录持久化$ docker run -d -it -p 8081:8080 -v /opt/wwwroot/:/usr/local/tomcat/webapps/ROOT mytomcat:1.0.0$ curl 10.0.0.101:8081Hello,Docker Web page

:::info 推荐在编写Dockerfile时,管理员应该先启动一个容器,然后按照Dockerfile的步骤一步一步的执行完。再将正确的执行指令COPY到Dockerfile 文件中。
:::
5.6 Dockerfile 示例
FROM ubuntuMAINTAINER zhongzhiwei <935523993@qq.com>ENV MY_PATH=/usr/localWORKDIR ${MY_PATH}# 更新APT源仓库RUN apt update -y# 安装 ifconfig 命令查看网络IPRUN apt install -y net-tools# 安装 ip addrRUN apt install -y iproute2# 安装 pingRUN apt install -y inetutils-ping# 安装基本软件RUN apt install -y wget bzip2 glibc-headers bison# 安装 Java 8 以及 lib 库# 1.安装 GNU MAKE-Version:4.2.1 软件路径:https://ftp.gnu.org/gnu/make/RUN wget https://ftp.gnu.org/gnu/make/make-4.2.1.tar.gz && tar -xf make-4.2.1.tar.gz && cd make-4.2.1 && mkdir -p build && cd build# 编译安装RUN /usr/local/make-4.2.1/configure --prefix=/usr && sh build.sh && make install# 2.安装 GCC-Version:8.2.0 软件路径:https://mirrors.tuna.tsinghua.edu.cn/gnu/gccRUN wget -c -P /opt/software/ https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.2.0/gcc-8.2.0.tar.gz && cd /opt/software/ && tar -zxvf gcc-8.2.0.tar.gz && /opt/software/gcc-8.2.0/contrib/download_prerequisitesRUN mkdir build && cd build && /opt/software/gcc-8.2.0/configure --enable-checking=release --enable-languages=c,c++ --disable-multilibRUN make -j 4 && make install# 3.安装 Glibc-Version:2.28 软件路径:https://ftp.gnu.org/gnu/glibc/RUN wget http://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz && tar -xf glibc-2.28.tar.gz && cd glibc-2.28 && mkdir build && cd build# 编译安装RUN /usr/local/glibc-2.28/configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin && make -j 4 && make install# ADD 是相对路径 jar,把jdk-8u301-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置ADD jdk-8u301-linux-x64.tar.gz /usr/local/java# 配置java环境变量ENV JAVA_HOME=/usr/local/java/jdk1.8.0_301ENV JRE_HOME=${JAVA_HOME}/jreENV CLASSPATH=${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar:${JRE_HOME}/lib:$CLASSPATHENV PATH=${JAVA_HOME}/bin:${PATH}EXPOSE 80CMD echo "${MY_PATH}"CMD echo "Success---------OK"CMD /bin/bash
FROM centosMAINTAINER zhongzhiwei <93552399@qq.com>ENV MYPATH /usr/localWORKDIR $MYPATH# 安装 VIM 编辑器RUN yum update -y && yum install -y vim# 安装 ifconfig 命令查看网络IPRUN yum install -y net-tools# 安装 Java 8 以及 lib 库RUN yum install -y glibc.i686RUN mkdir -p /usr/local/java# ADD 是相对路径 jar,把jdk-8u301-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置ADD jdk-8u301-linux-x64.tar.gz /usr/local/java# 配置java环境变量ENV JAVA_HOME=/usr/local/java/jdk1.8.0_301ENV JRE_HOME=${JAVA_HOME}/jreENV CLASSPATH=${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar:${JRE_HOME}/lib:$CLASSPATHENV PATH=${JAVA_HOME}/bin:${PATH}EXPOSE 80CMD echo ${MYPATH}"CMD echo "Success----------OK"CMD /bin/bash
6 Harbor 镜像仓库搭建和使用
6.1 Harbor 概述
Harbor是由VMWare公司开源的容器镜像仓库。事实上,Harbor是在Docker Registry上进行了相应的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:管理用户界面,基于角色的访问控制,AD/LDAP集成以及审计日志等,足以满足基本企业需求。
Github: https://github.com/goharbor/harbor
6.2 部署先决条件与部署
6.2.1 Harbor 部署:先决条件
服务器硬件配置:
- 最低要求:CPU2核 / 内存4G / 硬盘40GB
- 推荐:CPU4核 / 内存8G / 硬盘160GB
软件:
- Docker CE 17.06版本+
- Docker Compose 1.18版本+
Harbor安装有2种方式:
- 在线安装:从Docker Hub下载Harbor相关镜像,因此安装软件包非常小
- 离线安装:安装包包含部署的相关镜像,因此安装包比较大
6.2.2 Harbor 部署
# 下载 Harbor 软件包wget https://github.com/goharbor/harbor/releases/download/v2.6.0/harbor-offline-installer-v2.6.0.tgz# 下载Docker Compose$ sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose# 查看Docker Compose版本$ docker-compose versiondocker-compose version 1.27.4, build 40524192docker-py version: 4.3.1CPython version: 3.7.7OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019
范例:部署 Harbor HTTP
$ tar -zxvf harbor-offline-installer-v2.6.0.tgz$ cd harbor ; cp harbor.yml.tmpl harbor.yml# 修改配置文件$ vi harbor.ymlhostname: 10.0.0.101https: # 先注释 https 相关配置harbor_admin_password: Harbor12345$ ./prepare$ ./install.sh$ docker-compose psName Command State Ports-----------------------------------------------------------------------------------------------------------------harbor-core /harbor/entrypoint.sh Up (health: starting)harbor-db /docker-entrypoint.sh 96 13 Up (health: starting)harbor-jobservice /harbor/entrypoint.sh Up (health: starting)harbor-log /bin/sh -c /usr/local/bin/ ... Up (health: starting) 127.0.0.1:1514->10514/tcpharbor-portal nginx -g daemon off; Up (health: starting)nginx nginx -g daemon off; Up (health: starting) 0.0.0.0:80->8080/tcp,:::80->8080/tcpredis redis-server /etc/redis.conf Up (health: starting)registry /home/harbor/entrypoint.sh Up (health: starting)registryctl /home/harbor/start.sh Up (health: starting)


6.3 基本使用
- 配置 http 镜像仓库可信任
$ vi /etc/docker/daemon.json{"insecure-registries":["10.0.0.101"]}$ systemctl daemon-reload ; systemctl restart docker
- 打标签
# docker tag SOURCE_IMAGE[:TAG] 10.0.0.101/library/REPOSITORY[:TAG]docker tag centos:centos7.9.2009 10.0.0.101/library/centos:centos7.9.2009
- 上传
# Docker Login$ docker login 10.0.0.101Username: adminPassword:WARNING! Your password will be stored unencrypted in /root/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded# docker push 10.0.0.101/library/REPOSITORY[:TAG]docker push 10.0.0.101/library/centos:centos7.9.2009#### 若出现反复Retrying in XXX second# 修改:common/config/registry/config.ymlhttp:relativeurls: true# 重启 Harbordocker-compose downdocker-compose up -d


- 下载
# 下载镜像docker pull 10.0.0.101/library/centos:centos7.9.2009# 需要配置可信任的证书

