- 1 Docker 安装
- 1.1 在Mac系统中安装 Docker
- 1.2 在Windows系统中安装 Docker
- 1.3 Vagrant
- 1.3.1 Vagrant 简介
- 1.3.2 Vargent 的优点
- 1.3.3 安装 Vargent
- 1.3.3.1 安装 VirtualBox">1.3.3.1 安装 VirtualBox
- 1.3.3.2 安装 Vagrant">1.3.3.2 安装 Vagrant
- 1.3.3.3 到 vagrantcloud 上找一个 box 镜像并添加">1.3.3.3 到 vagrantcloud 上找一个 box 镜像并添加
- 1.3.3.4 Vagrant & VirtualBox for Mac">1.3.3.4 Vagrant & VirtualBox for Mac
- 1.3.3.5 Vagrant & VirtualBox for Windows">1.3.3.5 Vagrant & VirtualBox for Windows
- 1.3.3.6 VirtualBox for CentOS 7
- 1.3.3.7 练习
- 1.4 在Linux系统中安装 Docker
- 1.5 阿里云镜像加速
- 2 Docker Machine
- 亚马逊AWS 在 Docker Machine 的使用会比阿里云的使用体验要好很多!
- 3 Docker 架构和底层技术
- 3.1 Docker 架构和底层技术简介
- 3.2 Docker Image 概述
- 3.3 Docker Container
- 3.4 Dockerfile 语法梳理及最佳实践
- 3.4.1 什么是Dockerfile ">3.4.1 什么是Dockerfile
- 3.4.2 Dockerfile基础命令 ">3.4.2 Dockerfile基础命令
- 3.4.2.1 FROM">3.4.2.1 FROM
- 3.4.2.2 RUN">3.4.2.2 RUN
- 3.4.2. CMD">3.4.2. CMD
- 3.4.2.3 LABEL">3.4.2.3 LABEL
- 3.4.2.4 MAINTAINER">3.4.2.4 MAINTAINER
- 3.4.2.5 EXPOSE">3.4.2.5 EXPOSE
- 3.4.2.6 ENV">3.4.2.6 ENV
- 3.4.2.7 ADD">3.4.2.7 ADD
- 3.4.2.8 COPY">3.4.2.8 COPY
- 3.4.2.9 ENTRYPOINT">3.4.2.9 ENTRYPOINT
- 3.4.2.10 VOLUME">3.4.2.10 VOLUME
- 3.4.2.11 USER">3.4.2.11 USER
- 3.4.2.12 WORKDIR">3.4.2.12 WORKDIR
- 3.4.2.13 ARG">3.4.2.13 ARG
- 3.4.2.14 ONBUILD">3.4.2.14 ONBUILD
- 3.4.2.15 STOPSIGNAL">3.4.2.15 STOPSIGNAL
- 3.4.2.16 HEALTHCHECK">3.4.2.16 HEALTHCHECK
- 3.5 镜像的发布
- 在阿里云个人的镜像仓库就可以查看到 tomcat 的镜像版本。
- 3.6 容器的操作
- 3.7 容器的资源限制
- 4 Docker Network 网络
- 5 Docker 的持久化存储和数据共享
- 6 Docker Compose
Docker | DaoCloud Reference:
1 Docker 安装
1.1 在Mac系统中安装 Docker
DaoCloud Reference:https://get.daocloud.io/#install-docker-for-mac-windows
Docker Reference:https://www.docker.com/products/docker#/mac
:::color2
在Mac上运行Docker。系统要求,OS X 10.10.3 或者更高版本,至少4G内存,4.3.30版本以前的VirtualBox会与Docker for Mac产生冲突,所以请卸载旧版本的VitrualBox。:::
1.2 在Windows系统中安装 Docker
DaoCloud Reference:https://get.daocloud.io/#install-docker-for-mac-windows
Docker Reference:https://www.docker.com/products/docker#/windows
:::color2
在Windows安装Linux的Application,必然需要Windows有一些条件,并且条件对于Windows来讲更加的苛刻。 在Windows上运行Docker。系统要求,Windows10 x64位,支持Hyper-V。[ Hyper-V 是微软的一种实现虚拟化的一个技术 ] 如果您的电脑版本过旧,可以使用 Docker Toolbox 在Windows或者Mac上运行Docker。适用于Mac OS X 10.8+ 或者 Windows 7/8.1。:::
系统要求:
- Windows 10 以上的操作系统版本
- 必须启用Hyper-V和Containers Windows功能。
- 要在Windows 10上成功运行Client Hyper-V,需要满足以下硬件先决条件:
- 具有二级地址转换(SLAT)的 64位处理器
- 4GB系统内存
- 必须在BIOS设置中启用BIOS级硬件虚拟化支持。有关更多信息,请参见虚拟化
:::color2
- 虚拟化功能启用
:::

:::color2
- Hyper-V 功能启用
:::

1.3 Vagrant
GitHub Reference:https://github.com/hashicorp/vagrant
Vagrant Reference:https://www.vagrantup.com/
Reference:
1.3.1 Vagrant 简介

1.3.2 Vargent 的优点
1、 统一开发环境。一次配置打包,统一分发给团队成员,统一团队开发环境,解决诸如“编码问题”,“缺少模块”,“配置文件不同”带来的问题; 2、 避免重复搭建开发环境。新员工加入,不用浪费时间搭建开发环境,快速加入开发,减少时间成本的浪费; 3、 多个相互隔离开发环境。可以在不用box里跑不同的语言,或者编译安装同一语言不同版本,搭建多个相互隔离的开发环境,卸载清除时也很快捷轻松。1.3.3 安装 Vargent
1.3.3.1 安装 VirtualBox
VirtualBox 是一个免费开源的虚拟机,相对 VMware 来说更加小巧,个人比较喜欢。:::color2
下载地址:https://www.virtualbox.org/wiki/Downloads ::: 虽然 Vagrant 也支持 VMware,不过 VMware 是收费的,对应的 Vagrant 版本也是收费的。1.3.3.2 安装 Vagrant
:::color2
下载地址:http://downloads.vagrantup.com ::: 有 Windows 版,也有 Mac 版,找到适合自己的包来安装就好了,和安装别的软件没啥区别,我这里就不演示了,双击来安装,到终端中执行:如果看到输出,表示已经装好了。
# 查看 vagrant 的版本号vagrant -v# 查看 vagrant 的帮助用法vagrant --help

1.3.3.3 到 vagrantcloud 上找一个 box 镜像并添加
:::color2 如果你要其他系统的镜像,可以来这里下载:http://www.vagrantbox.es
:::
可以看到这里有上百种不同的镜像,而且全部都可以通过命令行一键安装。(但是国内的网络会经常失败。) 所以也可以下载之后再安装。1.3.3.4 Vagrant & VirtualBox for Mac
Video Reference:Vagrant & VirtualBox for Mac
1.3.3.5 Vagrant & VirtualBox for Windows
:::color2
推荐版本是<font style="color:rgb(77, 77, 77);">VirtualBox-6.0.24-139119-Win</font> 和 <font style="color:rgb(77, 77, 77);">Vagrant_2.2.16</font>
:::
Video Reference:Vagrant & VirtualBox for Windows
Document Reference:https://blog.csdn.net/qq_44713454/article/details/119943743
# 在某个盘中创建相应的目录E:\> mkdir centos7# 进入到相应的目录E:\> cd centos7# vagrant初始化E:\centos7> vagrant init centos/7A `Vagrantfile` has been placed in this directory. You are nowready to `vagrant up` your first virtual environment! Please readthe comments in the Vagrantfile as well as documentation on`vagrantup.com` for more information on using Vagrant.# 可以查看到当前有一个 Vagrantfile 配置文件E:\centos7> dir驱动器 E 中的卷是 Studying卷的序列号是 7822-9BE5E:\centos7 的目录2022/11/01 21:17 <DIR> .2022/11/01 21:17 3,084 Vagrantfile1 个文件 3,084 字节1 个目录 406,498,758,656 可用字节# 启动 vagrant(将VM电源启用)E:\centos7> vagrant up# 进入到 vagrant 创建系统的控制台(VM控制台)E:\centos7> vagrant ssh# 查看 vagrant 状态E:\centos7> vagrant status# vagrant 关机(将VM电源关闭)E:\centos7> vagrant halt# vagrant 删除(将VM删除)E:\centos7> vagrant destroy
| 命令 | 作用 |
|---|---|
| vagrant box add | 添加box的操作 |
| vagrant init | 初始化box的操作,会生成vagrant的配置文件Vagrantfile |
| vagrant up | 启动本地环境 |
| vagrant ssh | 通过 ssh 登录本地环境所在虚拟机 |
| vagrant halt | 关闭本地环境 |
| vagrant suspend | 暂停本地环境 |
| vagrant resume | 恢复本地环境 |
| vagrant reload | 修改了 Vagrantfile 后,使之生效(相当于先 halt,再 up) |
| vagrant destroy | 彻底移除本地环境 |
| vagrant box list | 显示当前已经添加的box列表 |
| vagrant box remove | 删除相应的box |
| vagrant package | 打包命令,可以把当前的运行的虚拟机环境进行打包 |
| vagrant plugin | 用于安装卸载插件 |
| vagrant status | 获取当前虚拟机的状态 |
| vagrant global-status | 显示当前用户Vagrant的所有环境状态 |
:::color2 Error Tip

Reference:https://blog.csdn.net/weixin_40230655/article/details/118459738
临时的解决方法:在VirtualBox 中会有相应新建的虚拟机,直接启动后,在利用 <font style="color:#E8323C;">vagrant ssh</font>进行连接即可。[ vagrant 的root 密码是 <font style="color:#E8323C;">vagrant</font> ]
:::
1.3.3.6 VirtualBox for CentOS 7
:::color2 Linux 操作系统需要支持虚拟化功能。
:::
# 1、首先下载用于编译vboxdrv内核模块的必要构建工具:sudo yum install -y \kernel-devel \kernel-devel-$(uname -r) \kernel-headers \make patch gcc# kernel-headers-$(uname -r) \# 2、/etc/yum.repos.d使用以下wget命令将Oracle Linux repo文件下载到目录:sudo wget \https://download.virtualbox.org/virtualbox/rpm/el/virtualbox.repo -P /etc/yum.repos.d# 3、通过键入以下命令来安装最新版本的VirtualBox 5.2.x:sudo yum install -y VirtualBox-5.2# 在安装过程中,系统将提示您导入存储库GPG密钥。输入y并点击Enter。安装完成后,您将看到以下输出:# Creating group 'vboxusers'. VM users must be member of that group!## Verifying : VirtualBox-5.2-5.2.20_125813_el7-1.x86_64## Installed:# VirtualBox-5.2.x86_64 0:5.2.20_125813_el7-1# 4、若要验证VirtualBox安装是否成功,请运行以下命令,该命令将检查vboxdrv服务的状态。systemctl status vboxdrv# 输出应类似于以下内容,表明该服务已启用并处于活动状态:● vboxdrv.service - VirtualBox Linux kernel moduleLoaded: loaded (/usr/lib/virtualbox/vboxdrv.sh; enabled; vendor preset: disabled)Active: active (exited) since Wed 2022-11-02 00:06:27 CST; 54s agoProcess: 101240 ExecStart=/usr/lib/virtualbox/vboxdrv.sh start (code=exited, status=0/SUCCESS)Tasks: 0

1.3.3.7 练习
:::color2 使得 Vagrant 启动CentOS 7虚拟机时自动安装 docker 等软件。
:::
# -*- mode: ruby -*-# vi: set ft=ruby :# All Vagrant configuration is done below. The "2" in Vagrant.configure# configures the configuration version (we support older styles for# backwards compatibility). Please don't change it unless you know what# you're doing.Vagrant.configure("2") do |config|# The most common configuration options are documented and commented below.# For a complete reference, please see the online documentation at# https://docs.vagrantup.com.# Every Vagrant development environment requires a box. You can search for# boxes at https://vagrantcloud.com/search.config.vm.box = "centos/7"# Disable automatic box update checking. If you disable this, then# boxes will only be checked for updates when the user runs# `vagrant box outdated`. This is not recommended.# config.vm.box_check_update = false# Create a forwarded port mapping which allows access to a specific port# within the machine from a port on the host machine. In the example below,# accessing "localhost:8080" will access port 80 on the guest machine.# NOTE: This will enable public access to the opened port# config.vm.network "forwarded_port", guest: 80, host: 8080# Create a forwarded port mapping which allows access to a specific port# within the machine from a port on the host machine and only allow access# via 127.0.0.1 to disable public access# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"# Create a private network, which allows host-only access to the machine# using a specific IP.# config.vm.network "private_network", ip: "192.168.33.10"# Create a public network, which generally matched to bridged network.# Bridged networks make the machine appear as another physical device on# your network.# config.vm.network "public_network"# Share an additional folder to the guest VM. The first argument is# the path on the host to the actual folder. The second argument is# the path on the guest to mount the folder. And the optional third# argument is a set of non-required options.# config.vm.synced_folder "../data", "/vagrant_data"# Provider-specific configuration so you can fine-tune various# backing providers for Vagrant. These expose provider-specific options.# Example for VirtualBox:## config.vm.provider "virtualbox" do |vb|# # Display the VirtualBox GUI when booting the machine# vb.gui = true## # Customize the amount of memory on the VM:# vb.memory = "1024"# end## View the documentation for the provider you are using for more# information on available options.# Enable provisioning with a shell script. Additional provisioners such as# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the# documentation for more information about their specific syntax and use.# config.vm.provision "shell", inline: <<-SHELL# apt-get update# apt-get install -y apache2# SHELLconfig.vm.provision "shell", inline: <<-SHELLsudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-enginesudo yum install -y yum-utilssudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-pluginsystemctl start dockerSHELLend
1.4 在Linux系统中安装 Docker
环境准备
- 需要会一部分Linux的基础
- CentOS 7
- 我们可以使用远程连接工具连接远程服务器进行操作!
环境查看
#系统内核是 3.10 以上的$ uname -r3.10.0-1160.el7.x86_64
#系统版本$ cat /etc/os-releaseNAME="CentOS Linux"VERSION="7 (Core)"ID="centos"ID_LIKE="rhel fedora"VERSION_ID="7"PRETTY_NAME="CentOS Linux 7 (Core)"ANSI_COLOR="0;31"CPE_NAME="cpe:/o:centos:centos:7"HOME_URL="https://www.centos.org/"BUG_REPORT_URL="https://bugs.centos.org/"CENTOS_MANTISBT_PROJECT="CentOS-7"CENTOS_MANTISBT_PROJECT_VERSION="7"REDHAT_SUPPORT_PRODUCT="centos"REDHAT_SUPPORT_PRODUCT_VERSION="7"
安装Docker
帮助文档:
#1.卸载旧的版本$ sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine#2.需要的安装$ sudo yum install -y yum-utils#3.设置镜像的仓库$ sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo #默认是国外的$ sudo yum-config-manager \--add-repo \https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo #推荐使用阿里云的,速度快#4.更新yum软件包的索引$ yum makecache fast#5.安装Docker相关的 Docker-ce社区版,ee则是企业版$ sudo yum install -y \docker-ce docker-ce-cli containerd.io docker-compose-plugin#6.启动Docker$ systemctl start docker#7.查看Docker的信息$ docker version#判断Docker是否安装成功#8.运行helloworld$ sudo docker run hello-world


#9.查看镜像是否存在hello-world镜像$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEhello-world latest feb5d9fea6a5 11 months ago 13.3kB#10.卸载Docker#卸载依赖yum remove -y docker-ce docker-ce-cli containerd.io docker-compose-plugin#删除资源sudo rm -rf /var/lib/docker#/var/lib/docker是Docker默认的工作路径#最新版sudo rm -rf /var/lib/containerd
1.5 阿里云镜像加速
- 登录阿里云找到容器服务
- 找到镜像加速地址

- 配置使用
sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF'{"registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com","http://hub-mirror.c.163.com","https://mirror.ccs.tencentyun.com","http://f1361db2.m.daocloud.io"],"exec-opts": ["native.cgroupdriver=systemd"],"log-driver": "json-file","log-opts": {"max-size": "100m"},"storage-driver": "overlay2"}EOFsudo systemctl daemon-reloadsudo systemctl restart docker
2 Docker Machine
[社区]Docker-Machine Document Reference:
https://www.mianshigee.com/tutorial/dockerdocs/userguide-dockermachine.md
2.1 Docker Machine 简介
Docker Machine 是一种可以让您在虚拟主机上安装 Docker 的工具,并可以使用 docker-machine 命令来管理主机。 Docker Machine 也可以集中管理所有的 docker 主机,比如快速的给 100 台服务器安装上 docker。

:::color2 Docker Machine 是一个用于配置和管理带有Docker Engine主机的工具,它允许你在虚拟宿主机上安装Docker,并使用docker-machine命令管理这个宿主机,可以使用Docker Machine在本地的MAC或者windows box、公司网络,数据中心或者AWS这样的云提供商上创建docker。简单说,一个 Docker Machine 就是一个 Docker host 主机和经过配置的 Docker client 的结合体。其作用就是快速帮助我们搭建 Docker 主机环境。
:::
2.2 Docker Machine 安装
安装 Docker Machine 之前你需要先安装 Docker。 Docker Machine 可以在多种平台上安装使用,包括 Linux 、MacOS 以及 windows。Linux 安装命令
base=https://github.com/docker/machine/releases/download/v0.16.0 &&curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine &&sudo mv /tmp/docker-machine /usr/local/bin/docker-machine &&chmod +x /usr/local/bin/docker-machine
macOS 安装命令
base=https://github.com/docker/machine/releases/download/v0.16.0 &&curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/usr/local/bin/docker-machine &&chmod +x /usr/local/bin/docker-machine
Windows 安装命令
如果你是 Windows 平台,可以使用 Git BASH,并输入以下命令:查看是否安装成功:
base=https://github.com/docker/machine/releases/download/v0.16.0 &&mkdir -p "$HOME/bin" &&curl -L $base/docker-machine-Windows-x86_64.exe > "$HOME/bin/docker-machine.exe" &&chmod +x "$HOME/bin/docker-machine.exe"
$ docker-machine versiondocker-machine version 0.16.0, build 9371605
:::color2 注意:各版本更新日志里面也有安装说明:https://github.com/docker/machine/releases
:::
2.3 使用 Docker Machine
本章通过<font style="color:rgb(51, 51, 51);">virtualbox</font> 来介绍 <font style="color:rgb(51, 51, 51);">docker-machine</font> 的使用方法。其他云服务商操作与此基本一致。具体可以参考每家服务商的指导文档。
2.3.1、列出可用的机器
可以看到目前只有这里默认的 default 虚拟机。
$ docker-machine ls

2.3.2、创建机器
创建一台名为 test 的机器。
# --driver 可以省略$ docker-machine create --driver virtualbox test
- —driver:指定用来创建机器的驱动类型,这里是 virtualbox。

2.3.3、查看机器的 ip
$ docker-machine ip test

2.3.4、停止机器
$ docker-machine stop test

2.3.5、启动机器
$ docker-machine start test

2.3.6、进入机器
$ docker-machine ssh test

2.4 Docker-Machine 命令参数说明
- docker-machine active:查看当前激活状态的 Docker 主机。
$ docker-machine lsNAME ACTIVE DRIVER STATE URLdev - virtualbox Running tcp://192.168.99.103:2376staging * digitalocean Running tcp://203.0.113.81:2376$ echo $DOCKER_HOSTtcp://203.0.113.81:2376$ docker-machine activestaging
- config:查看当前激活状态 Docker 主机的连接信息。
- create:创建 Docker 主机
- env:显示连接到某个主机需要的环境变量
- inspect: 以 json 格式输出指定Docker的详细信息
- ip: 获取指定 Docker 主机的地址
- kill: 直接杀死指定的 Docker 主机
- ls: 列出所有的管理主机
- provision: 重新配置指定主机
- regenerate-certs: 为某个主机重新生成 TLS 信息
- restart: 重启指定的主机
- rm: 删除某台 Docker 主机,对应的虚拟机也会被删除
- ssh: 通过 SSH 连接到主机上,执行命令
- scp: 在 Docker 主机之间以及 Docker 主机和本地主机之间通过 scp 远程复制数据
- mount: 使用 SSHFS 从计算机装载或卸载目录
- start: 启动一个指定的 Docker 主机,如果对象是个虚拟机,该虚拟机将被启动
- status: 获取指定 Docker 主机的状态(包括:Running、Paused、Saved、Stopped、Stopping、Starting、Error)等
- stop: 停止一个指定的 Docker 主机
- upgrade: 将一个指定主机的 Docker 版本更新为最新
- url: 获取指定 Docker 主机的监听 URL
- version: 显示 Docker Machine 的版本或者主机 Docker 版本
- help: 显示帮助信息
:::color2 扩展:可以使用 docker-machine 的环境变量,将本地的 docker-client 进行连接
:::
$ docker-machine env testexport DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/root/.docker/machine/machines/test"export DOCKER_MACHINE_NAME="test"# Run this command to configure your shell:# eval $(docker-machine env test)# 执行命令$ eval $(docker-machine env test)# Server 可以发现是 docker-machine 中 test 的 Server$ docker versionClient: Docker Engine - CommunityVersion: 20.10.21API version: 1.40Go version: go1.18.7Git commit: baeda1fBuilt: Tue Oct 25 18:04:24 2022OS/Arch: linux/amd64Context: defaultExperimental: trueServer: Docker Engine - CommunityEngine:Version: 19.03.12API version: 1.40 (minimum version 1.12)Go version: go1.13.10Git commit: 48a66213feBuilt: Mon Jun 22 15:49:35 2020OS/Arch: linux/amd64Experimental: falsecontainerd:Version: v1.2.13GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429runc:Version: 1.0.0-rc10GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dddocker-init:Version: 0.18.0GitCommit: fec3683

2.5 Docker Machine 在 Cloud 中的应用
2.5.1 Docker Machine 在 阿里云上的应用
Video Reference:https://www.bilibili.com/video/BV1xe4y1q7fC/?p=11
Aliyun Driver Reference:https://github.com/AliyunContainerService/docker-machine-driver-aliyunecs
2.5.1.1 概述和Docker安装
Docker-Machine 的使用跟docker入门中一样,只是需要添加阿里云的Docker-Machine 驱动,同时配置阿里云Docker-Machine 驱动的环境。
#!/bin/bash# Shell ENVDOCKER_VERSION="20.10.7"CONTAINERD_VERSION="1.4.6"# step 1: 安装必要的一些系统工具echo -e "==> 安装必要的系统工具"sudo yum install -y yum-utils device-mapper-persistent-data lvm2# Step 2: 添加软件源信息echo -e "==> 添加软件源信息"sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo# Step 3echo -e "==> 修改配置文件"sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo# Step 4: 更新并安装Docker-CEecho -e "==> 安装更新Docker"sudo yum makecache fast# containerd.io Docker运行时环境sudo yum -y install docker-ce-${DOCKER_VERSION} docker-ce-cli-${DOCKER_VERSION} containerd.io-${CONTAINERD_VERSION}# Step 5: 配置加速器以及docker参数echo -e "==> 配置加速器以及docker参数"sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<-'EOF'{"registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com","http://hub-mirror.c.163.com","https://mirror.ccs.tencentyun.com","http://f1361db2.m.daocloud.io"],"exec-opts": ["native.cgroupdriver=systemd"],"log-driver": "json-file","log-opts": {"max-size": "100m"},"storage-driver": "overlay2"}EOF# Step 6: 加载服务echo -e "==> 加载服务"sudo systemctl daemon-reloadsudo systemctl restart dockersudo systemctl enable docker# 查看Docker服务信息echo -e "==> 查看Docker服务信息"docker info
2.5.1.2 Docker-Machine 安装和阿里云 Docker-Machine 驱动
# 安装 Docker Machine 工具base=https://github.com/docker/machine/releases/download/v0.16.0 &&curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine &&sudo mv /tmp/docker-machine /usr/local/bin/docker-machine &&chmod +x /usr/local/bin/docker-machine# 1、首先下载用于编译vboxdrv内核模块的必要构建工具:sudo yum install -y \kernel-devel \kernel-devel-$(uname -r) \kernel-headers \make patch gcc# kernel-headers-$(uname -r) \# 2、/etc/yum.repos.d使用以下wget命令将Oracle Linux repo文件下载到目录:sudo wget \https://download.virtualbox.org/virtualbox/rpm/el/virtualbox.repo -P /etc/yum.repos.d# 3、通过键入以下命令来安装最新版本的VirtualBox 5.2.x:sudo yum install -y VirtualBox-5.2# 在安装过程中,系统将提示您导入存储库GPG密钥。输入y并点击Enter。安装完成后,您将看到以下输出:# Creating group 'vboxusers'. VM users must be member of that group!## Verifying : VirtualBox-5.2-5.2.20_125813_el7-1.x86_64## Installed:# VirtualBox-5.2.x86_64 0:5.2.20_125813_el7-1# 4、若要验证VirtualBox安装是否成功,请运行以下命令,该命令将检查vboxdrv服务的状态。systemctl status vboxdrv# 输出应类似于以下内容,表明该服务已启用并处于活动状态:● vboxdrv.service - VirtualBox Linux kernel moduleLoaded: loaded (/usr/lib/virtualbox/vboxdrv.sh; enabled; vendor preset: disabled)Active: active (exited) since Wed 2022-11-02 00:06:27 CST; 54s agoProcess: 101240 ExecStart=/usr/lib/virtualbox/vboxdrv.sh start (code=exited, status=0/SUCCESS)Tasks: 0# 下载阿里云驱动 Docker-Machine$ wget https://docker-machine-aliyunecs-drivers.oss-cn-beijing.aliyuncs.com/docker-machine-driver-aliyunecs_linux-amd64.tgz# 解压压缩包$ tar -zxvf docker-machine-driver-aliyunecs_linux-amd64.tgz# 下载之后解压放到bin下面,重命名为 docker-machine-driver-aliyuncs$ mv bin/docker-machine-driver-aliyunecs.linux-amd64 bin/docker-machine-driver-aliyuncs# 设置环境变量# export PATH=<Your Local Path>/docker-machine-driver-aliyuncs[.exe]:$PATH$ export PATH=/root/bin/:$PATH# cp bin/docker-machine-driver-aliyuncs /usr/local/bin# 只是放到下面,是无法执行,因为它其实是个插件,$ docker-machine create -d aliyunecs -–help# 执行这个来验证是否成功


2.5.1.3 配置阿里云Docker-Machine驱动的环境
这个驱动跟docker-machine有类似读取环境变量的行为,环境变量中配置阿里云的API key一类的东西,就可以让docker-machine驱动自动操作我们的阿里云账号购买指定的虚拟机并安装配置相关的环境!花钱更便捷。 找到自己用户目录下的<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">.bashrc</font>,在末尾添加如下内容。要在阿里云控制台上找的参数很多,耐心找。
export ECS_ACCESS_KEY_ID='填你自己key id'export ECS_ACCESS_KEY_SECRET='填你自己的key secret'# 购买的镜像是1核0.5G内存的最小型实例export ECS_INSTANCE_TYPE='ecs.t5-lc2m1.nano'# 来个50M宽带export ECS_INTERNET_MAX_BANDWIDTH='50'# 购买的区域是石家庄export ECS_REGION='cn-zhangjiakou'# 设置的密码,一定要8~30位,有大小写字母,数字和乱七八糟的标点,不然会自动创建失败export ECS_SSH_PASSWORD='7b6424B61c6C21~0%39F1C56'# 磁盘只要20Gexport ECS_SYSTEM_DISK_SIZE='20'# 磁盘用便宜的高效云盘吧,任性选SSD也行export ECS_SYSTEM_DISK_CATEGORY='cloud_efficiency'# 选择的镜像是Ubuntu16.04export ECS_IMAGE_ID='ubuntu_16_0402_64_20G_alibase_20180409.vhd'# 专用网络的节点export ECS_VPC_ID='vpc-8vbhii32tpugfcqbryqcn'# 虚拟交换机的节点export ECS_VSWITCH_ID='vsw-8vbweep4xrtamazp50775'# ECS的标签export ECS_TAGS='chen_docker'# 安全组export ECS_SECURITY_GROUP='sg-8vb5wwel08nyrih2lmtb'# 石家庄a区export ECS_ZONE='cn-zhangjiakou-a'# 一定要是trueexport ECS_IO_OPTIMIZED='true'
2.5.1.4 阿里云Docker-Machine使用
阿里云操作AccessKey
- RAM访问控制创建用户

- RAM访问控制获取AccessKey

- 需要将其RAM访问控制的用户权限放通(ECS权限,否则创建失败)
- 获取AccessKey ID和AccessKey Secret

AccessKey IDLTAI5tAatgSRkX4FDhhhXUgmAccessKey SecretGjjqZBxKTDvop8h3wB3pakWvi6Sm7L
# 格式:# docker-machine create -d aliyunecs \# --aliyunecs-access-key-id=<Your access key ID for the Aliyun ECS API> \# --aliyunecs-access-key-secret=<Your secret access key for the Aliyun ECS API> \# --aliyunecs-region=<Region> <machine-name># 范例:docker-machine create -d aliyuncs \--aliyunecs-io-optimized=optimized \--aliyunecs-instance-type=ecs.s6-c1m2.small \--aliyunecs-access-key-id=LTAI5tAatgSRkX4FDhhhXUgm \--aliyunecs-access-key-secret=GjjqZBxKTDvop8h3wB3pakWvi6Sm7L \--aliyunecs-region=cn-shenzhen kubesphere

:::color5
如果在这里遇到报错<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">Error creating machine: Error running provisioning: error installing docker:</font>,先去检查之前让修改的都没有问题之后,再试,如果还是不行,除了docker-machine这台主机,其他两台换为已经安装docker的主机,因为在create的过程中,是通过国外的网站去下载的,网络原因,会下载到一半停止,继续运行create后面的步骤,所以会报这个错误,提前将docker安装好就不会这样了
:::
添加完成后保存并执行<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">source .bashrc</font>使配置环境生效。
关于这些参数,阿里云有解释<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">https://github.com/AliyunContainerService/docker-machine-driver-aliyunecs</font>
2.5.2 Docker Machine 在 亚马逊AWS上的应用
Video Reference:13.Docker Machine在亚马逊AWS云上的使用-_哔哩哔哩_bilibili
亚马逊AWS 在 Docker Machine 的使用会比阿里云的使用体验要好很多!
2.6 Docker Playgroud
为了帮助你省去繁琐的步骤,在练习过程中,可以直接使用已经搭好的 Docker 学习环境。 首先,进入 Docker Playground。Docker Playground Reference:Playground 将为你虚拟一个学习环境,帮助学习 Docker 的指令、动手操作、并进行小实验。[ 默认是4小时的使用时长 ] 进入页面后,你将看到虚拟出来的一个session(会话)。在左侧点击”添加实例(Add New Instance)”: 它可以提供IP地址、内存、CPU用量等信息。在这个平台上,你可以做一些 docker的操作。例如
<font style="color:rgb(77, 77, 77);">docker ps</font>,docker镜像等等。点开小齿轮标志,可看到清空控制台、调节控制台的大小等选项。
:::color2 小总结:
(1)在Mac上玩Docker
- Docker for Mac直接装
- 通过Virtualbox或者Vmware虚拟化软件直接创建Linux虚拟机,在虚拟机里安装使用Docker
- 通过
Vagrant + VirtualBox快速搭建Docker host - 通过
docker-machine快速搭建Docker host
(2)在Windows上玩Docker
- Docker for windows直接装 ( 对系统要求高至少Win10 )
- 通过Virtualbox或者Vmware虚拟化软件直接创建Linux虚拟机,在虚拟机里安装使用Docker
- 通过
Vagrant + VirtualBox快速搭建Docker host - 通过
docker-machine快速搭建Docker host
(3)在Linux上玩Docker
Linux主机
Linux虚机(支持虚拟化的任何操作系统或者平台)
(4)在云上玩Docker
- Docker-machine + driver ( Aws、Aliyun等 )
- 直接使用云服务商提供的容器服务
- AWS的ESC (Amazon Elastic Container Service)
- Aliyun 的Container Service
:::
3 Docker 架构和底层技术
3.1 Docker 架构和底层技术简介
:::color2 Docker Platform
:::
- Docker提供了一个开发,打包,运行app的平台
- 把 app 和底层 infrastructure隔离开来

:::color2 Docker Engine
:::
- 后台进程( dockerd )
- REST API Server
- CLI接口( docker )

$ docker versionClient: Docker Engine - CommunityVersion: 20.10.10API version: 1.41Go version: go1.16.9Git commit: b485636Built: Mon Oct 25 07:44:50 2021OS/Arch: linux/amd64Context: defaultExperimental: trueServer: Docker Engine - CommunityEngine:Version: 20.10.10API version: 1.41 (minimum version 1.12)Go version: go1.16.9Git commit: e2f740dBuilt: Mon Oct 25 07:43:13 2021OS/Arch: linux/amd64Experimental: falsecontainerd:Version: 1.6.9GitCommit: 1c90a442489720eec95342e1789ee8a5e1b9536frunc:Version: 1.1.4GitCommit: v1.1.4-0-g5fd4c4ddocker-init:Version: 0.19.0GitCommit: de40ad0$ ps -ef | grep dockerroot 1187 1 0 09:28 ? 00:00:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sockroot 2782 1326 0 09:46 pts/0 00:00:00 grep --color=auto docker
:::color2 Docker Architecture
:::

- Docker Client:类似于Linux的命令终端,用于管理Docker资源
- Docker Daemon:用于管理引擎
- Docker Image:就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。
- Docker Containers:Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。以面向对象的思想可以理解为镜像为类,而容器则是其一个个的实例对象。
- Docker Network:Docker 的网络隔离是通过 Network Namespace 实现的,不同的网络模式都与 NameSpace 关联。Docker 的网络转发通过 iptables 实现。
- Docker Volumes:Docker 通过容器数据卷的方式完成数据的持久化的重要资料。[ 完全独立于容器的生命周期,因此Docker不会在容器删除时将其挂载的数据卷删除 ]
- Docker Registry:Docker仓库(Repository)是集中存放镜像文件的场所。仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub ( https://hub.docker.com/ ) ,存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云 等。
:::color2 Docker 核心架构
:::

:::color2 Docker 底层技术支持
:::
- NameSpace:做隔离,pid,net,ipc,mnt,uts,user
- Control Groups:做资源限制
- Union File Systems:Container 和 Image 的分层文件系统[ 联合文件系统 ]
| NameSpace | 系统调用参数 | 隔离内容 |
|---|---|---|
| UTS | CLONE_NEWUTS | 主机和域名 |
| IPC | CLONE_NEWIPC | 进程信号量、消息队列和共享内存 |
| PID | CLONE_NEWPID | 进程编号 |
| Network | CLONE_NEWNET | 网络设备、网络栈、端口等 |
| Mount | CLONE_NEWNS | 挂载点(文件系统) |
| User | CLONE_NEWUSER | 用户和用户组 |
3.2 Docker Image 概述
3.2.1 什么是 Image
- 文件和meta data的集合( root filesystem )
- 分层的,并且每一层都可以添加改变,删除文件,成为一个新的image
- 不同的Image可以共享相同的layer
- Image 本身是 read-only ( 只读 )的

$ sudo docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 605c77e624dd 10 months ago 141MB$ sudo docker image lsREPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 605c77e624dd 10 months ago 141MB
3.2.2 Image 的获取(1)
- Build from Dockerfile
FROM ubuntu:18.04LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"RUN apt-get update && apt-get install -y redis-serverEXPOSE 6379ENTRYPOINT [ "/usr/bin/redis-server" ]
docker build -t zredis:v1.0 -f Dockerfile .

3.3.3 Image 的获取(2)
- Pull from Registry
$ sudo docker pull ubuntu:14.0414.04: Pulling from library/ubuntu2e6e20c8e2e6: Pull complete0551a797c01d: Pull complete512123a864da: Pull completeDigest: sha256:60840958b25b5947b11d7a274274dc48ab32a2f5d18527f5dae2962b64269a3aStatus: Downloaded newer image for ubuntu:14.04docker.io/library/ubuntu:14.04
:::color2 扩展:使得普通用户可以执行 docker 命令
:::
sudo groupadd dockersudo usermod -G docker [普通用户]sudo systemctl restart docker# 普通可以执行docker的命令
3.3.4 DIY Base Image
$ yum install -y gcc glibc-static$ mkdir hello-docker ; cd hello-docker# 编写 C 程序$ vim hello.c#include <stdio.h>int main(){printf("hello docker!\n");}# 编译并生成可执行文件$ gcc -static hello.c -o hello$ ./hellohello docker!# 编写 Dockerfile 文件$ vim DockerfileFROM scratch# Scratch是一个空的Docker镜像。# 通过scratch来构建一个基础镜像。LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"ADD hello /CMD [ "/hello" ]# 打包成镜像$ docker build -t zhongzhiwei/hello-world .$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzhongzhiwei/hello-world latest 5fbbac5d29a0 37 seconds ago 861kB$ docker history zhongzhiwei/hello-worldIMAGE CREATED CREATED BY SIZE COMMENT5fbbac5d29a0 About a minute ago /bin/sh -c #(nop) CMD ["/hello"] 0Bec3fad14f2ad About a minute ago /bin/sh -c #(nop) ADD file:061ea1fe9f44c64f7… 861kB2a56b96ab165 About a minute ago /bin/sh -c #(nop) LABEL maintainer=zhongzhi… 0B# 运行成容器$ docker run -it zhongzhiwei/hello-worldhello docker!
:::color2
使用scratch空镜像的本质是让你的程序只调用host主机的Linux内核部分的功能,而不依赖容器内的操作环境功能。 由于host主机的Linux内核部分对Docker容器是共享的,因此其scratch空镜像的大小可以认为近似为0。 scratch 镜像小总结: 使用scratch空镜像的优点:真正意义上的最小镜像,镜像大小约等于执行文件大小 安全稳定,只需要关注维护程序本身和Linux内核安全更新即可 最高效的资源利用,容器内没有任何多余程序或服务占用资源 制作镜像方便快捷,由于scratch空镜像不需要load或pull就能使用,流水线上制作镜像更加方便快捷 缺点:由于没有sh或bash,无法进入容器内进行交互式调试 PS:该问题实际可以通过构建自定义基础镜像解决。但笔者个人认为生产环境应该通过日志分析问题,而不是进入到容器内进行调试。:::
3.3 Docker Container
3.3.1 什么是 Container
- 要有Container首先要有Image,也就是说Container是通过image创建的。
- Container是在原先的Image之上新加的一层,称作Container layer,这一层是可读可写的(Image是只读的)。
- 在面向对象的编程语言中,有类跟对象的概念。类是抽象的,对象是类的具体实现。Image跟Container可以类比面向对象中的类跟对象,Image就相当于抽象的类,Container就相当于具体实例化的对象。
- Image跟Container的职责区别:Image负责APP的存储和分发,Container负责运行APP。

3.3.2 Docker 容器操作
$ docker run --helpUsage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]Run a command in a new container...省略部分输出...# 列出正在运行的容器$ docker ps | docker container ls# 列出所有的容器(正在运行和退出状态)$ docker ps -a | docker container ls -a# 运行CentOS 7.9容器$ docker run -it --name centos-node01 centos:7.9.2009[root@a6ac274f3e05 /]# lsanaconda-post.log dev home lib64 mnt proc run srv tmp varbin etc lib media opt root sbin sys usr[root@a6ac274f3e05 /]# touch test.txt[root@a6ac274f3e05 /]# yum install -q -y vim[root@a6ac274f3e05 /]# exit# 删除所有的容器$ docker rm -f $(docker ps -aq)# 或者使用$ docker ps -aq | xargs docker rm -f# 删除正在退出的容器$ docker rm -f $(docker ps -f "status=exited" -q)
3.3.3 构建 Docker 镜像
:::color2 使用 docker commit 提交成镜像
:::
# 使用 docker commit 将容器提交成一个镜像$ docker run -it --name centos-node centos:7.9.2009[root@3b7980304ddd /]# yum install -y -q vim[root@3b7980304ddd /]# vim --version | head -n 1VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Dec 15 2020 16:44:08)$ docker commit \-a "zhongzhiwei <zhongzhiwei@kubesphere.io>" \-m "Add software vim" centos-node zhongzhiwei/centos-vim:v1.0$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzhongzhiwei/centos-vim v1.0 33284aef474e About a minute ago 442MB$ docker image history zhongzhiwei/centos-vim:v1.0IMAGE CREATED CREATED BY SIZE COMMENT33284aef474e 5 minutes ago /bin/bash 238MB Add software vimeeb6ee3f44bd 13 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B<missing> 13 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B<missing> 13 months ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB$ docker run -it --rm --name centos--test zhongzhiwei/centos-vim:v1.0[root@a37e65662aa3 /]# vim --version | head -n 1VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Dec 15 2020 16:44:08)
:::color2 使用 dockerfile 构建镜像
:::
$ mkdir -pv dockerfile/docker-centos-vim ; cd dockerfile/docker-centos-vim$ vim DockerfileFROM centos:7.9.2009 AS baseimgLABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"# 安装 vim 软件RUN yum install -y vim$ docker build -t zhongzhiwei/centos-vim:v2.0 -f Dockerfile .$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzhongzhiwei/centos-vim v2.0 702ffcff1ca7 15 seconds ago 442MB$ docker image history zhongzhiwei/centos-vim:v2.0IMAGE CREATED CREATED BY SIZE COMMENT702ffcff1ca7 35 seconds ago /bin/sh -c yum install -y vim 238MBef92e80a1b69 2 minutes ago /bin/sh -c #(nop) LABEL maintainer=zhongzhi… 0Beeb6ee3f44bd 13 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B<missing> 13 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B<missing> 13 months ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
3.4 Dockerfile 语法梳理及最佳实践
3.4.1 什么是Dockerfile
官方文档描述:”<font style="color:rgb(0, 0, 0);">Docker can build images automatically by reading the instructions from a </font><font style="color:rgb(232, 62, 140);">Dockerfile</font><font style="color:rgb(0, 0, 0);">. A </font><font style="color:rgb(232, 62, 140);">Dockerfile</font><font style="color:rgb(0, 0, 0);"> is a text document that contains all the commands a user could call on the command line to assemble an image. Using </font><font style="color:rgb(232, 62, 140);">docker build</font><font style="color:rgb(0, 0, 0);"> users can create an automated build that executes several command-line instructions in succession.</font>“
Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它们简化了从头到尾的流程并极大的简化了部署工作。Dockerfile从FROM命令开始,紧接着跟随者各种方法,命令和参数。其产出为一个新的可以用于创建容器的镜像。
Dockerfile语法由两部分构成,注释和命令+参数,注释是不能少的,作为一个有自知之明的coder,明天可能就忘记写的是什么玩意了。说白了,Dockerfile是告诉docker怎么样制作一个镜像,就像我们代码写方法告诉应用怎么执行一条逻辑,这样应该好理解了,所以可以在Dockerfile中写明,我们需要怎么个执行方式的某个镜像,最后执行docker build命令构建写好的Dockerfile成镜像。
3.4.2 Dockerfile基础命令
3.4.2.1 FROM
功能为指定基础镜像,并且必须是第一条指令。 如果不以任何镜像为基础,写法为:FROM scratch。 同时意味着接下来所写的指令将作为镜像的第一层开始 语法:FROM <image> FROM <image>:<tag> FROM <image>:<digest>

:::color2
FROM 尽量使用官方的 image 作为 base Imge !:::
3.4.2.2 RUN
功能为运行指定的命令 RUN命令有两种格式- RUN
2. RUN [“executable”, “param1”, “param2”]
- 在linux操作系统上默认 /bin/sh -c
- 在windows操作系统上默认 cmd /S /C
<font style="color:rgb(0, 0, 0);">executable</font>理解成为可执行文件,后面就是两个参数。
两种写法比对:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOMERUN ["/bin/bash", "-c", "echo hello"]

:::color2
RUN 为了美观,复杂的RUN请使用反斜线换行!避免无用分层,合并多条命令成一行! RUN:执行命令并创建新的 Image Layer:::
3.4.2. CMD
功能为容器启动时要运行的命令 语法有三种写法- CMD [“executable”,”param1”,”param2”] 2. CMD [“param1”,”param2”] 3. CMD command param1 param2
CMD [ "sh", "-c", "echo $HOME" CMD [ "echo", "$HOME" ]
<font style="color:rgb(0, 0, 0);">RUN & CMD</font>
:::color2
CMD:设置容器启动后默认执行的命令和参数:::
Shell 和 Exec 格式


:::color2
在 Dockerfile2 中会发现识别不了 $name 的变量,输出是直接打印,原因:在通过 Shell 格式运行命令,默认会通过 Shell bash 去运行该命令。但是通过 Exec 的格式运行命令,在 Dockerfile2 中是直接运行 echo 的命令,并不是在 Shell 中执行 echo 命令,只是单纯的执行 echo,所以无法识别 $name 变量 。
修正:<font style="color:#2F54EB;">ENTRYPOINT [ "bash", "-c", "echo hello $name" ]</font>
:::
即:
FROM centos:7.9.2009ENV name DockerENTRYPOINT [ "/bin/bash", "-c", "echo hello $name" ]
:::color2
CMD- 容器启动时默认执行的命令
- 如果 docker run 指定了其他命令,CMD命令被忽略
- 如果定义了多个CMD,只有最后一个会执行
:::

docker run [image] 输出?会输出”hello Docker”
docker run -it [image] /bin/bash 输出?不会输出”hello Docker”
3.4.2.3 LABEL
功能是为镜像指定标签 语法:LABEL <key>=<value> <key>=<value> <key>=<value> ...
但是并不建议这样写,最好就写成一行,如太长需要换行的话则使用\符号 如下:
LABEL "com.example.vendor"="ACME Incorporated"LABEL com.example.label-with-value="foo"LABEL version="1.0"LABEL description="This text illustrates \that label-values can span multiple lines."
说明:LABEL会继承基础镜像种的LABEL,如遇到key相同,则值覆盖
LABEL multi.label1="value1" \multi.label2="value2" \other="value3"

:::color2
LABEL Metadata 不可少!:::
3.4.2.4 MAINTAINER
指定作者,不过该语法正在逐渐淘汰。 语法:MAINTAINER <name>
3.4.2.5 EXPOSE
功能为暴漏容器运行时的监听端口给外部 但是EXPOSE并不会使容器访问主机的端口 如果想使得容器与主机的端口有映射关系,必须在容器启动的时候加上 -P参数3.4.2.6 ENV
功能为设置环境变量 语法有两种- ENV
2. ENV = …

:::color2
ENV 尽量使用 ENV 增加可维护性!:::
3.4.2.7 ADD
一个复制命令,把文件复制到镜像中 如果把虚拟机与容器想象成两台linux服务器的话,那么这个命令就类似于scp,只是scp需要加用户名和密码的权限验证,而ADD不用 语法如下:- ADD
… 2. ADD [“ “,… “ “]
有如下注意事项: 1、如果源路径是个文件,且目标路径是以 / 结尾, 则docker会把目标路径当作一个目录,会把源文件拷贝到该目录下。 如果目标路径不存在,则会自动创建目标路径。 2、如果源路径是个文件,且目标路径是不是以 / 结尾,则docker会把目标路径当作一个文件。 如果目标路径不存在,会以目标路径为名创建一个文件,内容同源文件; 如果目标文件是个存在的文件,会用源文件覆盖它,当然只是内容覆盖,文件名还是目标文件名。 如果目标文件实际是个存在的目录,则会源文件拷贝到该目录下。 注意,这种情况下,最好显示的以 / 结尾,以避免混淆。 3、如果源路径是个目录,且目标路径不存在,则docker会自动以目标路径创建一个目录,把源路径目录下的文件拷贝进来。 如果目标路径是个已经存在的目录,则docker会把源路径目录下的文件拷贝到该目录下。 4、如果源文件是个归档文件(压缩文件),则docker会自动帮解压。 尽量不要把
#testFROM ubuntuMAINTAINER helloADD test1.txt test1.txtADD test1.txt test1.txt.bakADD test1.txt /mydir/ADD data1 data1ADD data2 data2ADD zip.tar /myzip
3.4.2.8 COPY
看这个名字就知道,又是一个复制命令 语法如下:- COPY
… 2. COPY [“ “,… “ “]

:::color2
ADD or COPY 大部分情况,COPY 优于 ADD! ADD 除了 COPY 还有额外的功能(解压功能)! 添加远程文件 / 目录请使用 curl 或者 wget!:::
3.4.2.9 ENTRYPOINT
功能是启动时的默认命令 语法如下:- ENTRYPOINT [“executable”, “param1”, “param2”]
- ENTRYPOINT command param1 param2
如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效, 如下:
FROM ubuntuENTRYPOINT ["top", "-b"]CMD ["-c"]
那么将执行ls -al ,top -b不会执行
FROM ubuntuENTRYPOINT ["top", "-b"]CMD ls -al
:::color2
ENTRYPOINT:设置容器启动时运行的命令- 让容器以应用程序或者服务的形式运行
- 不会被忽略,一定会执行
- 最佳实践,写一个 shell 脚本作为 entrypoint
:::

3.4.2.10 VOLUME
可实现挂载功能,可以将内地文件夹或者其他容器种得文件夹挂在到这个容器种 语法为:VOLUME ["/data"]
一般的使用场景为需要持久化存储数据时, 容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。 所以当数据需要持久化时用这个命令。
VOLUME ["/var/log/"]VOLUME /var/logVOLUME /var/log /var/db
3.4.2.11 USER
设置启动容器的用户,可以是用户名或UID,所以,只有下面的两种写法是正确的USER daemoUSER UID
<font style="color:rgb(0, 0, 0);">daemon</font>用户去运行,那么RUN, CMD 和 ENTRYPOINT 都会以这个用户去运行
3.4.2.12 WORKDIR
语法:WORKDIR /path/to/workdir
<font style="color:rgb(0, 0, 0);">RUN,CMD,ENTRYPOINT,COPY,ADD</font>生效。如果不存在则会创建,也可以设置多次。 如:
WORKDIR /a WORKDIR b WORKDIR c RUN pwd
pwd的执行结果是
ENV DIRPATH /pathWORKDIR $DIRPATH/$DIRNAMERUN pwd
<font style="color:rgb(0, 0, 0);">/path/$DIRNAME</font>
:::color2
WORKDIR 用WORKDIR,不要用 CMD cd!尽量使用绝对目录路径!
:::
3.4.2.13 ARG
语法:ARG <name>[=<default value>]
<font style="color:rgb(0, 0, 0);">--build-arg <varname>=<value></font>来指定参数
如果用户在build镜像时指定了一个参数没有定义在Dockerfile种,那么将有一个Warning,提示如下:
[Warning] One or more build-args [foo] were not consumed.
FROM busybox ARG user1 ARG buildno ...
FROM busybox ARG user1=someuser ARG buildno=1 ...
3.4.2.14 ONBUILD
语法:ONBUILD [INSTRUCTION]
ONBUILD RUN ls -al
3.4.2.15 STOPSIGNAL
语法:STOPSIGNAL signal
3.4.2.16 HEALTHCHECK
容器健康状况检查命令 语法有两种:- HEALTHCHECK [OPTIONS] CMD command
- HEALTHCHECK NONE
:::color2
—interval=DURATION 两次检查默认的时间间隔为30秒 —timeout=DURATION 健康检查命令运行超时时长,默认30秒 —retries=N 当连续失败指定次数后,则容器被认为是不健康的,状态为unhealthy,默认次数是3:::
注意: HEALTHCHECK命令只能出现一次,如果出现了多次,只有最后一个生效。 CMD后边的命令的返回值决定了本次健康检查是否成功,具体的返回值如下::::color2
0: success - 表示容器是健康的 1: unhealthy - 表示容器已经不能工作了 2: reserved - 保留值:::
例子:HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f http://localhost/ || exit 1
<font style="color:rgb(0, 0, 0);">curl -f http://localhost/ || exit 1</font> , 两次检查的间隔时间是5秒,命令超时时间为3秒
:::color2 总结:
编写Dockerfile的最终目的是基于某些基础镜像进行二次构建为自身所需要的镜像,要学好Dockerfile除了学会基础的指令外,建议多查看docerhub镜像库中各种比较火热的官方镜像Dockerfile,学习当中的优点以及尽可能小的构建镜像。:::
3.5 镜像的发布
3.5.1 发布镜像到 Dockerhub
DockerHub
- DockerHub 地址 https://hub.docker.com/ 注册自己的账号
- 确定这个账号可以登录
- 在我们服务器上提交自己的镜像
$ docker login --helpUsage: docker login [OPTIONS] [SERVER]Log in to a Docker registry.If no server is specified, the default is defined by the daemon.Options:-p, --password string Password--password-stdin Take the password from stdin-u, --username string Username
- 登录完毕后就可以提交镜像了,就是一步 docker push 镜像名:[TAG]
范例:
$ docker login -u "dragonzw" -p "SZzhongaislf"WARNING! Using --password via the CLI is insecure. Use --password-stdin.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 tag mytomcat:1.0 dragonzw/tomcat:1.0#推送镜像到DockerHub$ docker push dragonzw/tomcat:1.0

$ vim hello.c#include <stdio.h>int main(){printf("hello docker!\n");}$ yum install gcc gcc-c++ glibc-static$ gcc -static hello.c -o hello$ vim DockerfileFROM scratchLABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"ADD hello /CMD [ "/hello" ]$ docker build -t dragonzw/hello-docker:v1.0 -f Dockerfile .$ docker push dragonzw/hello-docker:v1.0# 测试$ docker rmi -f dragonzw/hello-docker:v1.0# 从 DockerHub中拉取镜像$ docker pull dragonzw/hello-docker:v1.0
:::color2 通过DockerHub 和 GitHub 做关联,管理员只需要维护 Dockerfile,DockerHub 自动做 docker build 构建
:::
3.5.2 发布镜像到 阿里云
$ sudo docker login --username=dragon志伟 registry.cn-shenzhen.aliyuncs.comPassword: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 tag tomcat:[镜像版本号] registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:[镜像版本号]$ docker tag mytomcat:1.0 registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:1.0#推送镜像#$ docker push registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:[镜像版本号]$ docker push registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:1.0#$ docker pull registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:[镜像版本号]$ docker pull registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:1.0
在阿里云个人的镜像仓库就可以查看到 tomcat 的镜像版本。

:::warning 阿里云容器镜像就可以参考阿里云官方文档即可。
:::
3.5.3 Docker 私有镜像仓库
# 镜像仓库服务器执行(docker运行)[10.0.0.102]$ docker run -it -d -p 5000:5000 \--restart=always --name registry registry:2# 推送镜像$ vim hello.c#include <stdio.h>int main(){printf("hello docker!\n");}$ yum install gcc gcc-c++ glibc-static$ gcc -static hello.c -o hello$ vim DockerfileFROM scratchLABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"ADD hello /CMD [ "/hello" ]$ docker build -t 10.0.0.102:5000/hello-docker:v1.0 -f Dockerfile .$ vim /etc/docker/daemon.json{"registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com","http://hub-mirror.c.163.com","https://mirror.ccs.tencentyun.com","http://f1361db2.m.daocloud.io"],"insecure-registries": ["10.0.0.102:5000"]}$ systemctl daemon-reload && systemctl restart docker$ docker push 10.0.0.102:5000/hello-world:v1.0$ curl -XGET http://10.0.0.102:5000/v2/_catalog{"repositories":["hello-world"]}$ curl -XGET http://10.0.0.102:5000/v2/hello-world/tags/list{"name":"hello-world","tags":["v1.0"]}$ docker rmi -f 10.0.0.102:5000/hello-world:v1.0$ docker pull 10.0.0.102:5000/hello-world:v1.0
3.5.4 Dockerfile 实战
$ vim app.pyfrom flask import Flaskapp = Flask(__name__)@app.route('/')def hello():return "hello docker"if __name__ == '__main__':app.run()$ pip install --upgrade pip$ pip install flask$ python app.py
FROM python:2.7LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere>"RUN pip install flask && \mkdir -pv /appWORKDIR /appADD app.py /app/CMD [ "python", "app.py" ]EXPOSE 5000
$ docker build -t flask-demo:v1.0 -f Dockerfile .$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEflask-demo v1.0 7c259632347e 5 seconds ago 906MB$ docker run -it -d --name flask-node -p 5000:5000 flask-demo:v1.0$ docker exec -it flask-node sh# curl localhost:5000hello docker
$ docker run -it ubunturoot@045d68516f5f:/# apt update && apt install -y stressroot@045d68516f5f:/# which stress/usr/bin/stress# stress 是Linux系统的压力测试工具root@045d68516f5f:/# stress --help`stress'' imposes certain types of compute stress on your systemUsage: stress [OPTION [ARG]] ...-?, --help show this help statement--version show version statement-v, --verbose be verbose-q, --quiet be quiet-n, --dry-run show what would have been done-t, --timeout N timeout after N seconds--backoff N wait factor of N microseconds before work starts-c, --cpu N spawn N workers spinning on sqrt()-i, --io N spawn N workers spinning on sync()-m, --vm N spawn N workers spinning on malloc()/free()--vm-bytes B malloc B bytes per vm worker (default is 256MB)--vm-stride B touch a byte every B bytes (default is 4096)--vm-hang N sleep N secs before free (default none, 0 is inf)--vm-keep redirty memory instead of freeing and reallocating-d, --hdd N spawn N workers spinning on write()/unlink()--hdd-bytes B write B bytes per hdd worker (default is 1GB)Example: stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 10sNote: Numbers may be suffixed with s,m,h,d,y (time) or B,K,M,G (size).# 默认是256MB空间root@045d68516f5f:/# stress --vm 1 -vroot@045d68516f5f:/# stress --vm 1 --vm-bytes 4096MB -v
$ mkdir -pv /root/dockerfile/ubuntu-stress ; cd /root/dockerfile/ubuntu-stress# 编写Dockerfile文件$ vim DockerfileFROM ubuntuLABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere>"RUN apt update && \apt install -y stressENTRYPOINT [ "/usr/bin/stress" ]# CMD [ "-v" ]CMD [ "--vm", "1", "--vm-bytes", "4096MB", "-v" ]$ docker build -t ubuntu-stress:v1.0 -f Dockerfile .$ docker run -it -d --name us1 ubuntu-stress:v1.0$ docker ps -a --no-truncCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESa66a4b6f4e6ba40907b14fa3acf2e2add3e285bae67b13bd73b03d15f632cee0 ubuntu-stress:v1.0 "/usr/bin/stress --vm 1 --vm-bytes 4096MB -v" 6 seconds ago Up 4 seconds us1
3.5.5 小结

3.6 容器的操作
# 进入到容器中$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES3b7980304ddd centos:7.9.2009 "/bin/bash" 8 hours ago Up 8 hours centos-node$ docker exec -it centos-node /bin/bash[root@3b7980304ddd /]# cat /etc/redhat-releaseCentOS Linux release 7.9.2009 (Core)[root@3b7980304ddd /]# exit$ docker exec -it centos-node hostname -I172.17.0.2# 停止容器$ docker stop centos-node# 启动容器$ docker start centos-node# 运行容器$ docker run -it -d --restart=always --name centos-demo centos:7.9.2009$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES4e3fb2992fbc centos:7.9.2009 "/bin/bash" 4 seconds ago Up 2 seconds centos-demo# 显示容器的详细信息$ docker inspect centos-demo# 查看容器的日志$ docker run -it --restart=always -d --name flask-node flask-demo:v1.0# -f: 跟踪日志输出$ docker logs centos-demo# 查看容器的详细信息显示$ docker inspect centos-demo# 可以查看容器ID,创建时间,各种参数,状态,使用的镜像,主机名# 容器运行是执行的命令,容器的网络$ docker inspect -f "{{.NetworkSettings}}" centos-demo# 使用 JSON 格式进行输出$ docker inspect -f "{{json .NetworkSettings}}" centos-demo
3.7 容器的资源限制
Document Reference:https://www.cnblogs.com/renshengdezheli/p/16662622.html
默认情况下,容器没有资源的使用限制,可以使用主机内核调度程序允许的尽可能多的资源
Docker提供了控制容器使用资源的方法,可以限制容器使用多少内存或 CPU等, 在docker run 命令的运行时配置标志实现资源限制功能
容器的内存限制:
Docker可以强制执行硬性内存限制,即只允许容器使用给定的内存大小
Docker也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不够用了
| 参数 | 参数解释 |
|---|---|
| -m或者—memory= | 容器可以使用的最大内存量。如果设置此选项,则允许的最小值为6m(6 兆字节)。也就是说,您必须将该值设置为至少 6 兆字节。 |
| —memory-swap* | 允许此容器交换到磁盘的内存量。 |
| —memory-swappiness | 默认情况下,主机内核可以换出容器使用的一定百分比的匿名页面。您可以设置—memory-swappiness为 0 到 100 之间的值,以调整此百分比。 |
| —memory-reservation | 允许您指定一个小于—memory在 Docker 检测到主机上的争用或内存不足时激活的软限制。如果使用—memory-reservation,则必须将其设置为低于—memory它才能优先。因为是软限制,所以不保证容器不超过限制。 |
| —kernel-memory | 容器可以使用的最大内核内存量。允许的最小值是4m。因为内核内存不能被换出,内核内存不足的容器可能会阻塞主机资源,这会对主机和其他容器产生副作用。 |
| —oom-kill-disable | 默认情况下,如果发生内存不足 (OOM) 错误,内核会终止容器中的进程。要更改此行为,请使用该—oom-kill-disable选项。仅在您还设置了该-m/—memory选项的容器上禁用 OOM kill。如果-m未设置该标志,主机可能会耗尽内存,内核可能需要终止主机系统的进程以释放内存。 |
容器的CPU限制:
默认情况下,每个容器对主机 CPU 周期的访问是无限制的。您可以设置各种约束来限制给定容器对主机 CPU 周期的访问。大多数用户使用和配置 默认的 CFS 调度程序。您还可以配置实时调度程序。 CFS 是用于普通 Linux 进程的 Linux 内核 CPU 调度程序。几个运行时标志允许您配置对容器拥有的 CPU 资源的访问量。当您使用这些设置时,Docker 会修改主机上容器的 cgroup 的设置。| 参数 | 参数解释 |
|---|---|
| —cpus= | 指定容器可以使用多少可用 CPU 资源。例如,如果主机有两个 CPU,并且您设置—cpus=”1.5”了 ,则容器最多可以保证一个半的 CPU。这相当于设置—cpu-period=”100000”和—cpu-quota=”150000”。 |
| —cpu-period= | 指定 CPU CFS 调度程序周期,它与 —cpu-quota. 默认为 100000 微秒(100 毫秒)。大多数用户不会更改默认设置。对于大多数用例,—cpus是一种更方便的选择。 |
| —cpu-quota= | 对容器施加 CPU CFS 配额。—cpu-period容器在被限制之前被限制的每微秒数。因此充当有效上限。对于大多数用例,—cpus是一种更方便的选择。 |
| —cpuset-cpus | 限制容器可以使用的特定 CPU 或内核。如果您有多个 CPU,则容器可以使用的逗号分隔列表或连字符分隔的 CPU 范围。第一个 CPU 编号为 0。有效值可能是0-3(使用第一个、第二个、第三个和第四个 CPU)或1,3(使用第二个和第四个 CPU)。 |
| —cpu-shares | 将此标志设置为大于或小于默认值 1024 的值,以增加或减少容器的重量,并允许它访问或多或少比例的主机 CPU 周期。这仅在 CPU 周期受到限制时才会强制执行。当有足够多的 CPU 周期可用时,所有容器都会根据需要使用尽可能多的 CPU。这样,这是一个软限制。—cpu-shares不会阻止容器以 swarm 模式调度。它优先考虑可用 CPU 周期的容器 CPU 资源。它不保证或保留任何特定的 CPU 访问权限。 |
# 限制容器的内存大小$ docker run -it -d --memory=200M --name u1 ubuntu /bin/bash# 查看容器的性能指标$ docker stats u1CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDSc05513160bbb u1 0.00% 896KiB / 200MiB 0.44% 656B / 0B 1.58MB / 0B 1# 限制CPU的相对权重$ docker run -itd --cpu-shares=10 --name t1 ubuntu$ docker run -itd --cpu-shares=5 --name t2 ubuntu$ docker stats --no-stream t1CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDSc65cbab30a2d t1 0.00% 540KiB / 1.794GiB 0.03% 656B / 0B 0B / 0B 1$ docker stats --no-stream t2CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDSc6a65f191a7a t2 0.00% 540KiB / 1.794GiB 0.03% 656B / 0B 0B / 0B 1
4 Docker Network 网络
Reference:https://blog.csdn.net/succing/article/details/122433770

- 环境准备
| IP地址 | 主机名 | 操作系统版本 | Docker 版本 |
|---|---|---|---|
| 10.0.0.101 | docker-node01 | CentOS Linux release 7.9.2009 | Docker 20.10.10 |
| 10.0.0.102 | docker-node02 | CentOS Linux release 7.9.2009 | Docker 20.10.10 |
4.1 基础网络概念
:::color2 基于数据包的通信方式

:::
:::color2 网络的分层

:::
:::color2 IP地址和路由

:::
:::color1 MAC 地址是世界唯一的,IP 地址在局域网每一个设备也是唯一的;IP地址就是逻辑地址,MAC则是真实地址。
IP 地址特点:
- IP地址具有唯一性,表达性;
- IP地址是可以修改的地址。
- IP地址具有可管理的特性。
=============================================================================================
路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程 [1] 。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。虽然路由器可以支持多种协议(如TCP/IP、IPX/SPX、AppleTalk等协议),但是在我国绝大多数路由器运行TCP/IP协议。路由器通常连接两个或多个由IP子网或点到点协议标识的逻辑端口,至少拥有1个物理端口。路由器根据收到数据包中的网络层地址以及路由器内部维护的路由表决定输出端口以及下一跳地址,并且重写链路层数据包头实现转发数据包。路由器通过动态维护路由表来反映当前的网络拓扑,并通过网络上其他路由器交换路由和链路信息来维护路由表。路由表主要构成:
- Destination:目标网络ID,表示可以到达的目标网络ID,0.0.0.0/0表示所有未知网络,又称为默认路由,优先级最低
- Genmask:目标网络对应的netmask
- lface:到达对应网络,应该从当前主机哪个网卡发送出来
- Gateway:到达非直连的网络;将数据发送到临近(下一个)路由器的临近本主机的接口的IP地址。如果是直连网络。Gateway 是0.0.0.0
- Metric:开销cost,值越小,路由记录的优先级最高
:::
:::color2 公有IP地址和私有IP地址
Public IP:互联网上的唯一标识,可以访问internet
Private IP:不可在互联网上使用,仅供机构内部使用
- A类 10.0.0.0—10.255.255.255(10.0.0.0/8)
- B类 172.16.0.0—172.31.255.255(172.16.0.0/12)
- C类 192.168.0.0—192.168.255.255 (192.168.0.0/16)
:::
:::color2 网络地址转换NAT

:::
:::color2 Ping 和 Telnet
- Ping(ICMP):验证IP的可达性(可以检查基本的连通性,因为会有禁 Ping 的功能)
- Telnet:验证服务的可用性
:::
$ ping -c 2 -W 1 www.imooc.comPING www.imooc.com (120.133.51.67) 56(84) bytes of data.--- www.imooc.com ping statistics ---2 packets transmitted, 0 received, 100% packet loss, time 1000ms$ telnet www.imooc.com 80Trying 117.121.101.41...Connected to www.imooc.com.Escape character is '^]'.
WireShark 抓包工具可以抓取相应的底层数据包情况。
Reference:
4.2 Linux 网络命名空间
$ docker run -d -it \--name test1 busybox /bin/sh -c "while true; do sleep 43200 ;done"$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESb07115b7777c busybox "/bin/sh -c 'while t…" 46 seconds ago Up 46 seconds test1# 当前的网络接口和IP地址$ docker exec -it test1 ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever100: eth0@if101: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ffinet 172.17.0.2/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever# 查看宿主机的网络接口和IP地址$ ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ffinet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0valid_lft 61373sec preferred_lft 61373secinet6 fe80::f816:3eff:fe6b:c203/64 scope linkvalid_lft forever preferred_lft forever3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defaultlink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c5ff:fe3b:4b05/64 scope linkvalid_lft forever preferred_lft forever101: veth9de4bfa@if100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group defaultlink/ether 82:ac:0a:b1:70:2b brd ff:ff:ff:ff:ff:ff link-netnsid 0inet6 fe80::80ac:aff:feb1:702b/64 scope linkvalid_lft forever preferred_lft forever# 创建第二个容器$ docker run -d -it --name test2 busybox /bin/sh -c "while true; do sleep 43200 ;done"$ docker exec -it test2 ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever102: eth0@if103: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ffinet 172.17.0.3/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever# ping 通 test1 的IP地址$ docker exec -it test2 ping -c1 -W1 172.17.0.2PING 172.17.0.2 (172.17.0.2): 56 data bytes64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.091 ms--- 172.17.0.2 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.091/0.091/0.091 ms
查看机器的 Network NameSpace
$ ip netns helpUsage: ip netns listip netns add NAMEip netns set NAME NETNSIDip [-all] netns delete [NAME]ip netns identify [PID]ip netns pids NAMEip [-all] netns exec [NAME] cmd ...ip netns monitorip netns list-id# 创建 Network NameSpace$ ip netns add test1$ ip netns add test2# 显示 Network NameSpace$ ip netns listtest1# 删除 Network NameSpace$ ip netns delete test1# 在 Network NameSpace 执行$ ip netns exec test1 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00# 查看宿主机的 ip link 数据链路层$ ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group defaultlink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff# 启用 netns 的lo网卡$ ip netns exec test1 ip link1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00$ ip netns exec test1 ip link set dev lo up$ ip netns exec test1 ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

使用命令实现 Linux Network Namespace 之间的互联
$ sudo ip link add veth-test1 type veth peer name veth-test2$ sudo ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group defaultlink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff104: veth-test2@veth-test1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether d6:80:fb:40:73:7c brd ff:ff:ff:ff:ff:ff105: veth-test1@veth-test2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 06:1f:fa:32:e6:2a brd ff:ff:ff:ff:ff:ff$ sudo ip netns exec test1 ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00$ sudo ip link set veth-test1 netns test1$ sudo ip netns exec test1 ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00105: veth-test1@if104: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 06:1f:fa:32:e6:2a brd ff:ff:ff:ff:ff:ff link-netnsid 0$ sudo ip link set veth-test2 netns test2$ sudo ip netns exec test2 ip link1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00104: veth-test2@if105: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether d6:80:fb:40:73:7c brd ff:ff:ff:ff:ff:ff link-netnsid 0# 就把veth-test2@veth-test1 和 veth-test1@veth-test2添加到相应的网络命名空间中$ sudo ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:002: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group defaultlink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff# 给veth-test2@veth-test1 和 veth-test1@veth-test2分配IP地址$ sudo ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1$ sudo ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2# 将接口启用$ sudo ip netns exec test1 ip link set dev veth-test1 up$ sudo ip netns exec test2 ip link set dev veth-test2 up# 查看IP地址是否正常$ sudo ip netns exec test1 ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever105: veth-test1@if104: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000link/ether 06:1f:fa:32:e6:2a brd ff:ff:ff:ff:ff:ff link-netnsid 1inet 192.168.1.1/24 scope global veth-test1valid_lft forever preferred_lft foreverinet6 fe80::41f:faff:fe32:e62a/64 scope linkvalid_lft forever preferred_lft forever$ sudo ip netns exec test2 ip addr1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00104: veth-test2@if105: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000link/ether d6:80:fb:40:73:7c brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 192.168.1.2/24 scope global veth-test2valid_lft forever preferred_lft foreverinet6 fe80::d480:fbff:fe40:737c/64 scope linkvalid_lft forever preferred_lft forever# 检查两个网络命名空间是否互通$ sudo ip netns exec test1 ping -c1 -W1 192.168.1.2PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.088 ms--- 192.168.1.2 ping statistics ---1 packets transmitted, 1 received, 0% packet loss, time 0msrtt min/avg/max/mdev = 0.088/0.088/0.088/0.000 ms$ sudo ip netns exec test2 ping -c1 -W1 192.168.1.1PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.036 ms--- 192.168.1.1 ping statistics ---1 packets transmitted, 1 received, 0% packet loss, time 0msrtt min/avg/max/mdev = 0.036/0.036/0.036/0.000 ms
4.3 Docker bridge0
:::color1 bridge 模式:使用 docker —network bridge 指定,默认使用 docker0
有些时候对于安全加固,网络通信服务特别设置,有些容器需要跑在指定的网络范围中。
bridge 是动态分配ip日常可以用bridge来部署我们的各种服务。而像注册中心,redis,mysql等等这些需要集群的东西,可以用host共享宿主机ip分配端口来做集群。
:::
$ sudo docker rm -f $(docker ps -qa)$ sudo docker run -d -it \--name test1 busybox /bin/sh -c "while true; do sleep 43200 ;done"# 列出Docker的所有网络$ sudo docker network lsNETWORK ID NAME DRIVER SCOPE8a2071481321 bridge bridge localf9b95f3c1bbc host host localf29d29b46da8 none null local$ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8926c4b7988a busybox "/bin/sh -c 'while t…" 59 seconds ago Up 58 seconds test1$ sudo docker network inspect bridge["Name": "bridge",...省略部分输出..."IPAM": {"Driver": "default","Options": null,"Config": [{"Subnet": "172.17.0.0/16"}]},...省略部分输出..."ConfigOnly": false,"Containers": {"8926c4b7988a95e682f089af060589fdc1cdb920d2804428b2ac008ef6129c14": {"Name": "test1","EndpointID": "9cb57fa591dbffa75785a766f60c669394e4928ef80e55af10f59fb67192e3ee","MacAddress": "02:42:ac:11:00:02","IPv4Address": "172.17.0.2/16","IPv6Address": ""}},...省略部分输出...}]$ sudo ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ffinet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0valid_lft 85718sec preferred_lft 85718secinet6 fe80::f816:3eff:fe6b:c203/64 scope linkvalid_lft forever preferred_lft forever3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defaultlink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c5ff:fe3b:4b05/64 scope linkvalid_lft forever preferred_lft forever107: vethde44194@if106: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group defaultlink/ether ca:97:10:8e:a0:59 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet6 fe80::c897:10ff:fe8e:a059/64 scope linkvalid_lft forever preferred_lft forever$ sudo yum install -y bridge-utils# 查看bridge网络信息$ brctl showbridge name bridge id STP enabled interfacesdocker0 8000.0242c53b4b05 no vethde44194$ sudo docker run -d -it \--name test2 busybox /bin/sh -c "while true; do sleep 43200 ;done"$ sudo docker network inspect bridge[{"Name": "bridge",...省略部分输出..."IPAM": {"Driver": "default","Options": null,"Config": [{"Subnet": "172.17.0.0/16"}]},...省略部分输出..."ConfigOnly": false,"Containers": {"7ac5a81dd21d11553731da609b14eff4b521194d887a4241ea557e32b750f760": {"Name": "test2","EndpointID": "68a546778926c191a734059ad6cf436fef24c700d38b487b2acb2b47437fa62d","MacAddress": "02:42:ac:11:00:03","IPv4Address": "172.17.0.3/16","IPv6Address": ""},"8926c4b7988a95e682f089af060589fdc1cdb920d2804428b2ac008ef6129c14": {"Name": "test1","EndpointID": "9cb57fa591dbffa75785a766f60c669394e4928ef80e55af10f59fb67192e3ee","MacAddress": "02:42:ac:11:00:02","IPv4Address": "172.17.0.2/16","IPv6Address": ""}},...省略部分输出...}]$ sudo ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ffinet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0valid_lft 85413sec preferred_lft 85413secinet6 fe80::f816:3eff:fe6b:c203/64 scope linkvalid_lft forever preferred_lft forever3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defaultlink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c5ff:fe3b:4b05/64 scope linkvalid_lft forever preferred_lft forever107: vethde44194@if106: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group defaultlink/ether ca:97:10:8e:a0:59 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet6 fe80::c897:10ff:fe8e:a059/64 scope linkvalid_lft forever preferred_lft forever109: veth176c34f@if108: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group defaultlink/ether d2:9d:24:68:5a:9a brd ff:ff:ff:ff:ff:ff link-netnsid 1inet6 fe80::d09d:24ff:fe68:5a9a/64 scope linkvalid_lft forever preferred_lft forever$ brctl showbridge name bridge id STP enabled interfacesdocker0 8000.0242c53b4b05 no veth176c34fvethde44194
:::color5 Bridge Network
:::

4.4 Container Link 网络模式
容器间如果想通过容器名进行网络连接,需要使用 docker run —link 来链接两个容器。 –link可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量。:::color5
–link 的格式:::
–link 添加到另一个容器的链接 name和id是源容器的name和id,alias是源容器在link下的别名。
--link <name or id>:alias
范例:使用 docker —link
$ docker rm -f $(docker ps -qa)$ docker run -d -it --name test1 \busybox /bin/sh -c "while true; do sleep 43200 ;done"$ docker exec -it test1 ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever106: eth0@if107: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ffinet 172.17.0.2/16 brd 172.17.255.255 scope global eth0$ docker run -it -d --name test2 \--link test1 busybox /bin/sh -c "while true; do sleep 43200 ;done"# 进入到 test2 的终端$ docker exec -it test2 /bin/sh/ # ping -c1 -W1 172.17.0.2PING 172.17.0.2 (172.17.0.2): 56 data bytes64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.110 ms--- 172.17.0.2 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.110/0.110/0.110 ms# 因为有 --link 模式,所以可以通过容器名进行互通/ # ping -c1 -W1 test1PING test1 (172.17.0.2): 56 data bytes64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.074 ms--- test1 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.074/0.074/0.074 ms# 但是 test1 并不能通过容器名ping通 test2$ docker exec -it test1 ping -c1 -W1 test2ping: bad address 'test2'# 所以这种 --link 的方式使用并不多,而是使用 docker 的自定义网络来实现
使用自定义网络来实现( Bridge网络模式 )
$ sudo docker network create --driver bridge my-bridge$ sudo docker network lsNETWORK ID NAME DRIVER SCOPE8a2071481321 bridge bridge localf9b95f3c1bbc host host local83e74a0a1a41 my-bridge bridge localf29d29b46da8 none null local$ sudo brctl showbridge name bridge id STP enabled interfacesbr-83e74a0a1a41 8000.0242ecd098c7 nodocker0 8000.0242c53b4b05 no veth72e2a58vethde44194$ sudo docker run -it -d --name test3 --network my-bridge busybox /bin/sh -c "while true; do sleep 43200 ;done"$ sudo brctl showbridge name bridge id STP enabled interfacesbr-83e74a0a1a41 8000.0242ecd098c7 no vethba62779docker0 8000.0242c53b4b05 no veth72e2a58vethde44194$ sudo docker network inspect my-bridge[{"Name": "my-bridge",...省略部分输出..."IPAM": {"Driver": "default","Options": {},"Config": [{"Subnet": "172.18.0.0/16","Gateway": "172.18.0.1"}]},...省略部分输出..."ConfigOnly": false,"Containers": {"c481f1bb7264f07dca8d9882065add891889b97351f51e14143b9fcf35bd9304": {"Name": "test3","EndpointID": "1067372fcd34f72b0705c2e077a6d1e611cd844ea024d83d883d305a1ad8f905","MacAddress": "02:42:ac:12:00:02","IPv4Address": "172.18.0.2/16","IPv6Address": ""}},...省略部分输出...}]# 使用 network connect 的参数将容器跟相应的 network 互联$ sudo docker network connect my-bridge test2$ sudo docker network inspect my-bridge[{"Name": "my-bridge",...省略部分输出..."IPAM": {"Driver": "default","Options": {},"Config": [{"Subnet": "172.18.0.0/16","Gateway": "172.18.0.1"}]},...省略部分输出..."ConfigOnly": false,"Containers": {"c481f1bb7264f07dca8d9882065add891889b97351f51e14143b9fcf35bd9304": {"Name": "test3","EndpointID": "1067372fcd34f72b0705c2e077a6d1e611cd844ea024d83d883d305a1ad8f905","MacAddress": "02:42:ac:12:00:02","IPv4Address": "172.18.0.2/16","IPv6Address": ""},"d620be4657a8d7c7eb489a6165685e91e98a12e823cdc11230523beab6627c88": {"Name": "test2","EndpointID": "bd242396d4118d3ba9c77199377865ad2ca128616ad4b275bca01c730a2d7586","MacAddress": "02:42:ac:12:00:03","IPv4Address": "172.18.0.3/16","IPv6Address": ""}},...省略部分输出...}]# test2 容器的网络接口有默认的bridge 和 my-bridge自定义网络$ sudo docker exec -it test2 ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever110: eth0@if111: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ffinet 172.17.0.3/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever115: eth1@if116: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuelink/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ffinet 172.18.0.3/16 brd 172.18.255.255 scope global eth1valid_lft forever preferred_lft forever# test2 容器通信 test3 的IP地址和主机名$ sudo docker exec -it test2 ping -c1 -W1 172.18.0.2PING 172.18.0.2 (172.18.0.2): 56 data bytes64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.103 ms--- 172.18.0.2 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.103/0.103/0.103 ms$ sudo docker exec -it test2 ping -c1 -W1 test3PING test3 (172.18.0.2): 56 data bytes64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.059 ms--- test3 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.059/0.059/0.059 ms# test3 容器通信 test2 的IP地址和主机名$ sudo docker exec -it test3 ping -c1 -W1 172.18.0.3PING 172.18.0.3 (172.18.0.3): 56 data bytes64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.079 ms--- 172.18.0.3 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.079/0.079/0.079 ms$ sudo docker exec -it test3 ping -c1 -W1 test2PING test2 (172.18.0.3): 56 data bytes64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.060 ms--- test2 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.060/0.060/0.060 ms
4.5 网络端口映射
$ sudo docker run -it -d --name web01 nginx# 不做端口映射要想访问nginx页面,需要进入到nginx容器中$ docker exec -it web01 /bin/bashroot@46eda4530967:/# curl 127.0.0.1<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>html { color-scheme: light dark; }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 ps --no-truncCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES46eda4530967606eeaefcae3e9f2597d7b4ad4c0becd6b0898f8768273233c3a nginx "/docker-entrypoint.sh nginx -g 'daemon off;'" 4 minutes ago Up 4 minutes 80/tcp web01# 查看容器的IP地址$ docker inspect -f "{{.NetworkSettings.IPAddress}}" web01172.17.0.4$ curl 172.17.0.4<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>html { color-scheme: light dark; }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>$ telnet 172.17.0.4 80Trying 172.17.0.4...Connected to 172.17.0.4.Escape character is '^]'.# 容器的端口的映射(-p 宿主机端口:容器端口)$ docker run -it -d --name web01 -p 80:80 nginx$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8482453167f6 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp web01# 使用宿主机的端口进行访问$ ip addr2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ffinet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0valid_lft 83173sec preferred_lft 83173secinet6 fe80::f816:3eff:fe6b:c203/64 scope linkvalid_lft forever preferred_lft forever$ curl 192.168.0.64<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>html { color-scheme: light dark; }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># 因为使用的华为云服务器,可以访问其公网的IP地址的80端口# 云服务器的安全组需要将其放通相应的宿主机的映射端口$ curl 110.41.20.249

:::color5 Container Port Map
:::

4.6 网络模式之 host 和 none
:::color5 none 网络模式:
:::
禁用了网络功能,只有 lo 标识(就是127.0.0.1表示本地回环地址)。
在 none 模式下,并不为 Docker 容器进行任何网络配置;也就是说,这个Docker 容器没有网卡、IP、路由等信息,只有loopback(lo);需要自己为Docker容器添加网卡,配置IP等。
$ docker network lsNETWORK ID NAME DRIVER SCOPE8a2071481321 bridge bridge localf9b95f3c1bbc host host local83e74a0a1a41 my-bridge bridge localf29d29b46da8 none null local# 使用 none 网络模式$ docker run -it -d --name test1 --network none busybox /bin/sh -c "while true; do sleep 43200 ;done"$ docker exec -it test1 /bin/sh# 只有 一张 lo 回环网卡/ # ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever# none 网络模式应用场景# 用于保存安全性较高的数据的容器
:::color5 host 网络模式:
:::
直接使用宿主机的IP地址与外界进行通信,不再需要额外进行NAT转换。
$ docker run -it -d --name test2 --network host busybox /bin/sh -c "while true; do sleep 43200 ;done"$ docker exec -it test2 /bin/sh# 可以查看到宿主机的网络命名空间/ # ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ffinet 192.168.0.64/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0valid_lft 82202sec preferred_lft 82202secinet6 fe80::f816:3eff:fe6b:c203/64 scope linkvalid_lft forever preferred_lft forever3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueuelink/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c5ff:fe3b:4b05/64 scope linkvalid_lft forever preferred_lft forever112: br-83e74a0a1a41: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueuelink/ether 02:42:ec:d0:98:c7 brd ff:ff:ff:ff:ff:ffinet 172.18.0.1/16 brd 172.18.255.255 scope global br-83e74a0a1a41valid_lft forever preferred_lft foreverinet6 fe80::42:ecff:fed0:98c7/64 scope linkvalid_lft forever preferred_lft forever# 缺点:可能会出现相应的端口冲突的问题。
案例
- 说明
容器将不会获得一个独立的 Network NameSpace,而是和宿主机共用一个 Network NameSpace。<font style="color:#F5222D;">容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。</font>

代码
- 警告
$ docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8# WARNING: Published ports are discarded when using host network mode# 当使用host网络模式的时候,Published发布的端口是不被推荐的# 问题:docker 启动时总是遇到标题中的警告# 原因:docker 启动时指定 --network=host 或者 -net host,如果还指定了 -p 映射端口,那么这个时候会有此警告,并且通过 -p 设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则会递增。# 解决:解决的方法就是使用 docker 的其他网络模式,例如 --network=bridge,这样就可以解决问题,或者直接无视。$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES819798960a6a billygoo/tomcat8-jdk8 "catalina.sh run" 3 minutes ago Up 3 minutes tomcat83
- 正确
$ docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
4.7 总结:
| 网络模型 | 简介 |
|---|---|
| bridge | 为每一个容器分配、设置IP等,并将容器连接到一个<font style="color:#E8323C;">docker0</font>虚拟网桥,默认为该模式 |
| host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口 |
| none | 容器有独立的 Network Namespace(网络名称空间),但是并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP等。(基本上不会使用) |
| container | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP,端口范围等 |
docker network 主流的是 bridge、host、none,常用的是 bridge、host。
4.8 多容器复杂应用的部署
# 基础版$ vim app.pyfrom flask import Flaskapp = Flask(__name__)@app.route('/')def hello():return "hello docker"if __name__ == '__main__':app.run()# 升级版$ vim app.pyimport osimport socketfrom flask import Flaskfrom redis.client import Redisapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello():redis.incr('hits')return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'), socket.gethostname())if __name__ == "__main__":app.run(host="0.0.0.0", port=5000, debug=True)
FROM python:2.7LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"RUN mkdir -p /app && \pip install flask redisWORKDIR /appCOPY . /appEXPOSE 5000CMD [ "python", "app.py" ]
# 启动 Redis 容器$ docker run -it -d --name redis redis$ docker build -t flask-redis:v1.0 -f Dockerfile .# 启动 flask-redis 容器( -e 设置环境变量)$ docker run -it -d --name flask-redis --link redis -e REDIS_HOST=redis flask-redis:v1.0$ docker exec -it flask-redis /bin/bashroot@0a1d7b16c43f:/app# echo $REDIS_HOSTredisroot@0a1d7b16c43f:/app# curl 127.0.0.1:5000Hello Container World! I have been seen 1 times and my hostname is 0a1d7b16c43f.root@0a1d7b16c43f:/app# curl 127.0.0.1:5000Hello Container World! I have been seen 2 times and my hostname is 0a1d7b16c43f.# 做端口映射$ docker rm -f flask-redis$ docker run -it -d --name flask-redis -p 5000:5000 --link redis -e REDIS_HOST=redis flask-redis:v1.0# 再次访问$ curl 127.0.0.1:5000Hello Container World! I have been seen 3 times and my hostname is b02030a3c3ea.$ curl 127.0.0.1:5000Hello Container World! I have been seen 4 times and my hostname is b02030a3c3ea.

设置容器的环境变量
$ docker run -it -d --name test1 \-e NAME="zhongzhiwei" \-e EMAIL="zhongzhiwei@kubesphere.io" \busybox /bin/sh -c "while true; do sleep 43200 ;done"$ docker exec -it test1 /bin/sh/ # envHOSTNAME=b5df0d82748dSHLVL=1HOME=/rootNAME=zhongzhiweiTERM=xtermPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binEMAIL=zhongzhiwei@kubesphere.ioPWD=/
多机器的通信?

4.9 Overlay 和 Underlay
4.9.1 Overlay 和 Underlay 解释
伴随着网络技术的发展,数据中心的二层组网结构出现了阶段性的架构变化,数据中心网络分为了Underlay和Overlay两个部分,网络进入了Overlay虚拟化阶段。那么Overlay网络是如何形成的?与Underlay 有哪些区别?又试图解决什么问题?
Underlay网络
Underlay 网络是负责传递数据包的物理网络,由交换机和路由器等设备组成,借助以太网协议、路由协议和VLAN协议等驱动。Underlay的所有网络组件都必须通过使用路由协议来确定 IP 连接。 对Underlay 网络而言,需要建立一个设计良好的L3,包括园区边缘交换机等,以确保网络的性能、可扩展性和高可用性。 Underlay协议:BGP、OSPF、IS-IS、EIGRP Overlay网络 Overlay 是使用网络虚拟化在物理基础设施之上建立连接的逻辑网络。与UnderLay网络相比,Overlay实现了控制平面与转发平面的分离,这也是SDN的核心理念。
VXLAN协议是目前最流行的Overlay网络隧道协议之一,它是由IETF定义的NVO3(Network Virtualization over Layer 3)标准技术之一,采用L2 over L4(MAC-in-UDP)的报文封装模式,将二层报文用三层协议进行封装,可实现二层网络在三层范围内进行扩展,将“二层域”突破规模限制形成“大二层域”。
通过OverLay技术,可以在对物理网络不做任何改造的情况下,通过隧道技术在现有的物理网络上创建了一个或多个逻辑网络,有效解决物理数据中心存在的诸多问题,实现了数据中心的自动化和智能化。
Overlay协议:VXLAN、NVGRE、GRE、OTV、OMP、mVPN
Underlay网络的一些限制
传统路由协议构建了路由前缀列表,每个路由条目都指向下一跳的 IP 地址。这意味着每个数据包都会根据路由表在网络中逐跳转发到目的地。这种逐跳路由行为有许多低效之处,例如:
1)网络分段和网络切片很难大规模实现:
+ 在网络中逐跳传输分段标签需要 VRF、MPLS 和 MP-BGP 之间进行复杂的控制平面交互。
+ 网络切片和多租户无法实现。
2)多路径转发繁琐,无法融合多个底层网络来实现负载均衡。
3)服务链无法有效扩展,因为它需要在多个设备上进行手动配置。
4)互联网不能保证私密通信的安全要求。
Underlay网络存在着以上诸多限制,而Overlay带来了Underlay无法提供的灵活性。那么Overlay网络又是如何形成的呢?
Overlay网络是如何形成的?
Overlay是基于软件的,不依赖于传输,它就像物理网络之上的虚拟网络。
Overlay网络的一个典型例子是Internet VPN ,它在Internet上构建了一个虚拟的封闭网络。通过使用IPsec等协议构建虚拟网络,使私有 IP 地址的通信成为可能。此外,SDN和SD-WAN也采用了Overlay网络的概念。
但是,要在 SD-WAN 中构建Overlay,需要一个特殊 CPE,称为 SD-WAN 边缘设备。
下面以SD-WAN边缘设备建立GRE隧道为例进行说明。相互连接的SD-WAN边缘设备之间建立隧道,数据包准备传输出去时,设备为数据包添加新的IP头部和隧道头部,并将内部IP头与MPLS域隔离,MPLS转发基于外部IP头进行。
一旦数据包到达其目的地,SD-WAN 边缘设备将删除外部 IP 标头和隧道标头,得到的是原始 IP 数据包。在整个过程中,Overlay网络感知不到Underlay网络。
同样的过程也可以用于Internet Underlay,但需要使用IPSec进行加密。
Overlay网络如何解决问题?
1)使用加密技术可以保护私密流量在互联网上的通信。
2)流量传输不依赖特定线路。Overlay网络使用隧道技术,可以灵活选择不同的底层链路,使用多种方式保证流量的稳定传输。
3)支持多路径转发。在Overlay网络中,流量从源传输到目的可通过多条路径,从而实现负载分担,最大化利用线路的带宽。
4)支持网络切片与网络分段。将不同的业务分割开来,可以实现网络资源的最优分配。
Overlay vs Underlay总结
### 4.9.2 Overlay 和 Underlay 网络和 etcd 多机容器通信
:::color5
多级容器通信前提需要一个分布式的数据库存储。例如:etcd
:::
bash
# docker-node1 执行
$ wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz && \
tar -zxvf etcd-v3.0.12-linux-amd64.tar.gz && \
cd etcd-v3.0.12-linux-amd64/
$ nohup ./etcd --name docker-node1 --initial-advertise-peer-urls http://10.0.0.54:2380 \
--listen-peer-urls http://10.0.0.54:2380 \
--listen-client-urls http://10.0.0.54:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.0.54:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://10.0.0.54:2380,docker-node2=http://10.0.0.55:2380 \
--initial-cluster-state new&
# docker-node2 执行
$ wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz && \
tar -zxvf etcd-v3.0.12-linux-amd64.tar.gz && \
cd etcd-v3.0.12-linux-amd64/
$ nohup ./etcd --name docker-node2 --initial-advertise-peer-urls http://10.0.0.55:2380 \
--listen-peer-urls http://10.0.0.55:2380 \
--listen-client-urls http://10.0.0.55:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://10.0.0.55:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://10.0.0.54:2380,docker-node2=http://10.0.0.55:2380 \
--initial-cluster-state new&
# 检查cluster状态
$ ./etcdctl cluster-health
member 24799c1b49c6e14a is healthy: got healthy result from http://10.0.0.55:2379
member fff59d175897acad is healthy: got healthy result from http://10.0.0.54:2379
cluster is healthy
# 重启 Docker 服务
# docker-node1 执行
systemctl stop docker && systemctl status docker
sudo /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://10.0.0.54:2379 --cluster-advertise=10.0.0.54:2375&
# 查看Docker Server是否启动
docker version
# docker-node2 执行
systemctl stop docker && systemctl status docker
sudo /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://10.0.0.55:2379 --cluster-advertise=10.0.0.55:2375&
# 查看Docker Server是否启动
docker version
创建 Overlay Network
bash
# 在 docker-node1 上创建一个 demo 的Overlay Network
$ sudo docker network ls
$ sudo docker network create -d overlay demo
$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
5343b47f0509 bridge bridge local
d9d5a59dde06 demo overlay global
fc0af5687697 host host local
c1c197f8a9e1 none null local
# 在 docker-node2 查看Docker 网络
$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
6d236fecdb41 bridge bridge local
d9d5a59dde06 demo overlay global
8b95bbe9b9ff host host local
ee36fdaebca6 none null local
$ docker network inspect demo
[
{
"Name": "demo",
"Id": "d9d5a59dde0612b127004fbf9e7b0eb024c18c67bffdadc01fb6535e26c26f58",
"Created": "2022-11-06T17:58:53.574544295+08:00",
"Scope": "global",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "10.0.0.0/24",
"Gateway": "10.0.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
# 在 docker-node1 创建一个 Container容器
$ docker run -it -d --name test1 --net demo busybox sh -c "while true; do sleep 3600; done"
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19ad5d2a1850 busybox "sh -c 'while true; …" 4 seconds ago Up 3 seconds test1
# 查看 test1 容器的IP地址
$ docker exec -it test1 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.2/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever
11: eth1@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
# 在 docker-node2 创建一个 Container容器
# docker-node2 无法创建容器名为 test1 的容器
$ docker run -it -d --name test1 --network demo busybox sh -c "while true; do sleep 3600; done"
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
5cc84ad355aa: Pull complete
Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
Status: Downloaded newer image for busybox:latest
5f72796a3601588e0414f0e3f57cf924f7c4305d61a06cbc9bb39b5797054cbb
ERRO[2022-11-06T18:03:22.036319292+08:00] 5f72796a3601588e0414f0e3f57cf924f7c4305d61a06cbc9bb39b5797054cbb cleanup: failed to delete container from containerd: no such container
docker: Error response from daemon: endpoint with name test1 already exists in network demo.
# docker-node2 创建容器名为 test2 的容器
$ docker run -it -d --name test2 --network demo busybox sh -c "while true; do sleep 3600; done"
# 查看 test2 容器的IP地址
$ docker exec test2 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether 02:42:0a:00:00:03 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.3/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever
11: eth1@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
### 4.9.3 发现docker_gwbridge的网络模式
bash
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
5343b47f0509 bridge bridge local
d9d5a59dde06 demo overlay global
9dadab18bc62 docker_gwbridge bridge local
fc0af5687697 host host local
c1c197f8a9e1 none null local
在容器内部创建了两个接口,这两个接口对应于主机上现在存在的两个网桥。在覆盖网络上,每个容器至少有两个接口将它连接到 <font style="color:rgb(199, 37, 78);background-color:rgb(242, 242, 242);">overlay</font><font style="color:rgb(64, 64, 64);"></font>和<font style="color:rgb(199, 37, 78);background-color:rgb(242, 242, 242);">docker_gwbridge</font>。

| 网桥 | 目的 |
|---|---|
| overlay | 入口和出口指向VXLAN封装的覆盖网络,并(可选)加密同一覆盖网络上的容器之间的流量。它将覆盖范围扩展到参与此特定叠加层的所有主机。一个主机上的每个覆盖子网将存在一个,并且它将具有与给定特定覆盖网络相同的名称。 |
| docker_gwbridge | 离开集群的流量的出口桥。docker_gwbridge 每个主机只能存在一个。此网桥上的容器到容器之间流量被阻止,仅允许入口/出口流量。 |
:::color5
数据最初从容器的 eth1 到 ovnet, 在此,进行 VXLAN 的封装,经过封装后的数据帧最终由 docker_gwbridge网络 路由 到宿主机的网络上,再有宿主机的网络继续正确的数据路由转发。
:::
范例:将 Overlay Network的两个容器相互ping通
# docker-node1 执行ping通 docker-node2$ docker exec -it test1 ping -c1 -W1 10.0.0.3PING 10.0.0.3 (10.0.0.3): 56 data bytes64 bytes from 10.0.0.3: seq=0 ttl=64 time=0.870 ms--- 10.0.0.3 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.870/0.870/0.870 ms$ docker exec -it test1 ping -c1 -W1 test2PING test2 (10.0.0.3): 56 data bytes64 bytes from 10.0.0.3: seq=0 ttl=64 time=0.678 ms--- test2 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.678/0.678/0.678 ms# docker-node2 执行ping通 docker-node1$ docker exec -it test2 ping -c1 -W1 10.0.0.2PING 10.0.0.2 (10.0.0.2): 56 data bytes64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.653 ms--- 10.0.0.2 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.653/0.653/0.653 ms$ docker exec -it test2 ping -c1 -W1 test1PING test1 (10.0.0.2): 56 data bytes64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.543 ms--- test1 ping statistics ---1 packets transmitted, 1 packets received, 0% packet lossround-trip min/avg/max = 0.543/0.543/0.543 ms
这就实现不同的机器的不同容器的IP地址的通信。

# 在Docker node1运行 redis$ docker run -it -d --name redis --network demo redis$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES189a9f8a2978 redis "docker-entrypoint.s…" 4 seconds ago Up 2 seconds 6379/tcp redis# 在Docker node2运行 flask-redis$ vim app.pyimport osimport socketfrom flask import Flaskfrom redis.client import Redisapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello():redis.incr('hits')return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'), socket.gethostname())if __name__ == "__main__":app.run(host="0.0.0.0", port=5000, debug=True)$ vim DockerfileFROM python:2.7LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"RUN mkdir -p /app && \pip install flask redisWORKDIR /appCOPY . /appEXPOSE 5000CMD [ "python", "app.py" ]$ docker build -t flask-redis:v1.0 -f Dockerfile .################################################################################################################################$ docker run -it -d --name flask-redis -p 5000:5000 -e REDIS_HOST=redis --network demo flask-redis:v1.0$ docker exec -it flask-redis /bin/bashroot@da02b8e1a821:/app# curl 127.0.0.1:5000Hello Container World! I have been seen 1 times and my hostname is da02b8e1a821.root@da02b8e1a821:/app# curl 127.0.0.1:5000Hello Container World! I have been seen 2 times and my hostname is da02b8e1a821.# 通过宿主机访问$ curl 127.0.0.1:5000Hello Container World! I have been seen 3 times and my hostname is da02b8e1a821.$ curl 127.0.0.1:5000Hello Container World! I have been seen 4 times and my hostname is da02b8e1a821.
5 Docker 的持久化存储和数据共享
5.1 Container Layer

Container 是 Image 之上创建的,是一个运行时环境。在Container Layer 是可以写入数据的。
管理员创建一个Container容器,写入的数据均在Container中,在没有做数据持久化的前提下,如果删除了该Container,那么之前在Container内的数据就会全部丢失。所以Container是临时存储保存数据的。例如:数据库的容器存储是必须要进行数据持久化的。所以Docker 就引用了数据持久化的机制。
5.2 Data Volume
5.2.1 Docker 容器数据卷是什么
:::color1 数据卷就是目录或者文件,存在于一个或者多个容器中,由Docker挂载到容器,但是不属于联合文件系统,因此能够绕过 Union File System 提供一些用于持续存储或者共享数据的特性:
卷的设计目的就是数据持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。Docker 容器(数据) →容器数据卷方式完成数据的持久化重要资料backup→ 映射,容器内的数据备份+持久化到本地主机目录
:::
一句话:类似于Redis的RDB文件和AOF文件
将Docker容器内的数据保存进宿主机的磁盘中
运行一个带有容器卷存储功能的容器实例
$ docker run -it --privileged=true -v /宿主机绝对路径:/容器内目录 镜像名

5.2.2 Docker 容器数据卷能干嘛
- 将运用与运行的环境打包镜像,run 后形成容器实例运行,但是我们对数据的要求希望是持久化的
- Docker 容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了
- 为了能保存数据在Docker中我们使用数据卷
- 特点:
- 数据据可以在容器之间共享或者重用数据
- 卷中的更改可以直接实时生效(跟 docker cp 比较)
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
5.3 Docker 持久化数据的方案
- 基于本地文件系统的Volume。可以在执行Docker create或Docker run时,通过
-v参数将主机的目录作为容器的数据卷。这部分功能便是基于本地文件系统的volume管理。 - 基于plugin的Volume,支持第三方的存储方案,比如NAS ,AWS。
5.4 Volume 的类型
- 受管理的
data Volume,由Docker后台自动创建。 - 绑定挂载的
Volume,具体挂载位置可以由用户指定。
5.5 数据持久化:Data Volume

MySQL Dockerfile GitHub:

范例:创建MySQL容器
$ docker run -it -d --name mysql1 -e MYSQL_ROOT_PASSWORD="Admin@h3c" mysql:5.7.40$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES2a0ff6fb860b mysql:5.7.40 "docker-entrypoint.s…" 9 seconds ago Up 8 seconds 3306/tcp, 33060/tcp mysql1# 1. 匿名卷# 查看 Docker 数据卷的信息$ docker volume lsDRIVER VOLUME NAMElocal 8174f8c54b9fb877b5d5cf93893927fe4445a8be2b1aa31bb6393eea579d2e32$ docker volume inspect 8174f8c54b9fb877b5d5cf93893927fe4445a8be2b1aa31bb6393eea579d2e32[{"CreatedAt": "2022-11-06T20:13:36+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/8174f8c54b9fb877b5d5cf93893927fe4445a8be2b1aa31bb6393eea579d2e32/_data","Name": "8174f8c54b9fb877b5d5cf93893927fe4445a8be2b1aa31bb6393eea579d2e32","Options": null,"Scope": "local"}]# 删除容器,并查看数据卷是否存在$ docker rm -f mysql1$ docker volume lsDRIVER VOLUME NAMElocal 8174f8c54b9fb877b5d5cf93893927fe4445a8be2b1aa31bb6393eea579d2e32# 2. 具名卷# 但是发现其VOLUME NAME数据卷名称并不友好(指定数据卷名)$ docker run -it -d --name mysql1 -e MYSQL_ROOT_PASSWORD="Admin@h3c" -v mysqlvolume:/var/lib/mysql mysql:5.7.40$ docker volume lsDRIVER VOLUME NAMElocal mysqlvolume$ docker volume inspect mysqlvolume[{"CreatedAt": "2022-11-06T20:16:34+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/mysqlvolume/_data","Name": "mysqlvolume","Options": null,"Scope": "local"}]# 验证数据卷是否生效$ docker exec -it mysql1 /bin/bashbash-4.2# mysql -uroot -pAdmin@h3cmysql: [Warning] Using a password on the command line interface can be insecure.Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 2Server version: 5.7.40 MySQL Community Server (GPL)Copyright (c) 2000, 2022, Oracle and/or its affiliates.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> show databases;+--------------------+| Database |+--------------------+| information_schema || mysql || performance_schema || sys |+--------------------+4 rows in set (0.00 sec)mysql> create database docker;Query OK, 1 row affected (0.00 sec)mysql> exitByebash-4.2# exit# 删除 mysql1 的容器$ sudo docker rm -f mysql1# 启用另一个 mysql2 容器使用 mysql1 的数据卷$ docker volume lsDRIVER VOLUME NAMElocal mysqlvolume$ docker run -it -d --name mysql2 -e MYSQL_ROOT_PASSWORD="Admin@h3c" -v mysqlvolume:/var/lib/mysql mysql:5.7.40$ docker exec -it mysql2 /bin/bashbash-4.2# mysql -uroot -pAdmin@h3cmysql: [Warning] Using a password on the command line interface can be insecure.Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 2Server version: 5.7.40 MySQL Community Server (GPL)Copyright (c) 2000, 2022, Oracle and/or its affiliates.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> show databases;+--------------------+| Database |+--------------------+| information_schema || docker || mysql || performance_schema || sys |+--------------------+5 rows in set (0.00 sec)
5.6 数据持久化:Bind Mouting
5.6.1 简单使用 Bind Mouting

$ mkdir -pv /root/dockerfile/docker-nginx && cd /root/dockerfile/docker-nginx$ vim Dockerfile# 指定基础镜像FROM nginx:latest AS baseimg# 指定工作目录WORKDIR /usr/share/nginx/html# 拷贝页面文件到容器内COPY index.html index.html$ cat > index.html <<-'EOF'<h1>This is the Docker Page!</h1><h2>Hello Docker!</h2>EOF# 构建成一个镜像$ docker build -t my-nginx:v1.0 -f Dockerfile .# 运行成一个容器$ docker run -it -p 10880:80 -d --name web01 my-nginx:v1.0$ curl 127.0.0.1:10880<h1>This is the Docker Page!</h1><h2>Hello Docker!</h2>

$ docker rm -f web01# 使用指定宿主机路径进行挂载到容器内$ docker run -it -p 10880:80 -d \-v /root/dockerfile/docker-nginx:/usr/share/nginx/html --name web01 my-nginx:v1.0# 容器操作$ docker exec -it web01 /bin/bashroot@7cceb0798a3d:/usr/share/nginx/html# pwd/usr/share/nginx/htmlroot@7cceb0798a3d:/usr/share/nginx/html# lsDockerfile index.htmlroot@7cceb0798a3d:/usr/share/nginx/html# echo "Docker Content" > docker.txtroot@7cceb0798a3d:/usr/share/nginx/html# exit# 宿主机操作$ cd /root/dockerfile/docker-nginx$ ls -ltotal 12-rw-r--r-- 1 root root 163 Nov 6 20:29 Dockerfile-rw-r--r-- 1 root root 15 Nov 6 20:36 docker.txt-rw-r--r-- 1 root root 57 Nov 6 20:30 index.html$ cat docker.txtDocker Content$ echo "Host Content" >> docker.txt# 容器查看是否有内容同步到$ docker exec -it web01 cat docker.txtDocker ContentHost Content
5.6.2 Bind Mouting 实际项目
Video Reference:https://www.bilibili.com/video/BV1xe4y1q7fC?p=41
5.7 将所学知识部署 WordPress 页面
创建自定义网络以及下载镜像
# 创建相应的自定义网络docker network create --driver bridge wordpress_net# 下载相应的镜像docker pull mariadb:10.6.4-focaldocker pull wordpress
部署 MySQL / MariaDB
# 不需要暴露宿主机的3306端口docker run -it -d --name db -v db_data:/var/lib/mysql \--restart=always -e MYSQL_ROOT_PASSWORD=somewordpress \-e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress \--network wordpress_net \-e MYSQL_PASSWORD=wordpress mariadb:10.6.4-focal --default-authentication-plugin=mysql_native_password
部署 WordPress 页面
docker run -it -d --name wordpress --restart=always \-e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=wordpress \--network wordpress_net \-e WORDPRESS_DB_PASSWORD=wordpress -e WORDPRESS_DB_NAME=wordpress \-p 80:80 wordpress
访问 WordPress 页面
http://110.41.20.249/
http://:80 后续的操作就是 WordPress 根据引导进行部署即可



6 Docker Compose
6.1 Docker Compose 是什么
Docker Compose是一个用来定义和运行复杂应用的Docker工具。一个使用Docker容器的应用,通常由多个容器组成。使用Docker Compose不再需要使用shell脚本来启动容器。 Compose 通过一个配置文件来管理多个Docker容器,在配置文件中,所有的容器通过services来定义,然后使用docker-compose脚本来启动,停止和重启应用,和应用中的服务以及所有依赖服务的容器,非常适合组合使用多个容器进行开发的场景。 Docker-compose 是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排 Docker-compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。需要定义一个YAML格式的配置文件 docker-compose.yml ,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。:::color1
docker-compose 还是非常爽的,用了以后,完全不像用 docker 启动容器 最简单的理解就是把多个docker容器的启动命令写在一个脚本里,批量启动容器(写好多个容器之间的命令关系) Docker Compose来轻松高效的管理容器。定义运行多个容器。:::

- 用 Dockerfile 定义你的应用环境,这样它就可以在任何地方被复制。
- 在 docker-compose.yml 中定义组成应用程序的服务。所以它们可以在一个孤立的环境中一起运行。
- 运行docker compose up , docker compose up命令将启动并运行整个应用程序。你也可以使用compose standalone(docker-compose binary)运行docker-compose。
Compose 是 Docker 官方的开源项目,需要独立安装! Dockerfile 让程序在任何地方运行。Docker Compose 是单个机器的容器编排工具。
:::color2 多容器的 APP 太恶心
:::
- 要从Dockerfile build image或者
Dockerhub拉取image - 要创建多个container
- 要管理这些container(启动停止删除)
:::color2 Docker Compose “批处理”
:::

:::color2 Docker Compose
:::
- Docker Compose 是一个工具
- 这个工具可以通过一个 yml 文件定义多容器的 Docker应用
- 通过一条命令就可以根据 yml 文件的定义去创建或者管理这多个容器
:::color2 docker-compose.yml
:::

:::color2 Services
:::
- 一个 Service 就代表一个 Container,这个Container 可以从 DockerHub 的 Image 来创建,或者从本地的 Dockerfile build 出来的 image 来创建。
- Service 的启动类似
docker run,我们可以给其指定 Network 和 Volume,所以可以给 service 指定 Network 和 Volume 的引用。

- 使用 Dockerfile 创建容器(docker build)

:::color2 Volumes
:::

:::color2 Networks
:::

:::color2 Docker Compose v2 和 v3 版本的区别
:::
- 由于 Swarm mode 中网络的特殊性,Compose v3版本配置文件中一些声明比如
<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">expose</font>和<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">links</font>会被忽略。注意:不能再使用 link 定义的网络别名来进行容器互联,可以使用服务名连接。 - Compose v3版本配置文件中
<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">volumes_from</font>不再支持,只能使用命名数据卷来实现容器数据的持久化和共享。 - Compose v3版本配置文件中引入了
<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">deploy</font>指令,可对Swarm mode中服务部署的进行细粒度控制,包括 -<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">resources</font>:定义<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">cpu_shares</font>,<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">cpu_quota</font>,<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">cpuset</font>,<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">mem_limit</font>,<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">memswap_limit</font>等容器资源限制指令。(v1/v2中相应指令在v3版本的配置文件中不再被支持)-<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">restart_policy</font>:定义服务的重启条件 (v1/v2中<font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">restart</font>指令在v3版本的配置文件中不再被支持)
6.2 Docker Compose 安装和基本使用
# 下载可执行文件docker-composecurl -L https://get.daocloud.io/docker/compose/releases/download/v2.12.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose# 将文件添加执行权限chmod +x /usr/local/bin/docker-compose# 验证docker-compose版本$ docker-compose versionDocker Compose version v2.12.2
6.2.1 Docker-Compose 的相关命令
:::color2 Compose 常用命令
- docker-compose -h —-> 查看帮助
- docker-compose up —-> 启动所有docker-compose服务
docker-compose up -d ---> 启动服务 docker-compose 服务,并后台运行docker-compose down ---> 停止并删除容器、网络、卷、镜像- docker-compose exec yml里面的服务id —-> 进入容器实例内部 [ docker-compose exec
docker-compose.yml文件中写的服务id/bin/bash ] - docker-compose ps —-> 展示当前 docker-compose 编排过的运行的所有容器
- docker-compose top —-> 展示当前 docker-compose 编排过的容器进程
- docker-compose logs yml里面的服务id —-> 检查容器输出日志
docker-compose config ---> 检查配置docker-compose config -q ---> 检查配置,有问题才有输出- docker-compose restart —-> 重启服务
- docker-compose start —-> 启动服务
- docker-compose stop —-> 停止服务
- docker-compose images —-> 查看 docker-compose 使用的镜像
- docker-compose build —-> 会构建 docker-compose 中的所有 Dockerfile 的构建镜像
:::
6.3 使用 Compose 一键部署 WordPress 博客
:::color1
传统方式部署博客:- 下载程序,安装数据库,配置……
- compose 应用 → 一键启动
:::
#创建一个项目的文件夹(这就是项目名project)$ mkdir /opt/my_wordpress ; cd /opt/my_wordpress$ vim docker-compose.ymlversion: "3.9"services:db:# We use a mariadb image which supports both amd64 & arm64 architectureimage: mariadb:10.6.4-focal# If you really want to use MySQL, uncomment the following line#image: mysql:8.0.27command: '--default-authentication-plugin=mysql_native_password'volumes:- db_data:/var/lib/mysqlrestart: alwaysenvironment:- MYSQL_ROOT_PASSWORD=somewordpress- MYSQL_DATABASE=wordpress- MYSQL_USER=wordpress- MYSQL_PASSWORD=wordpressexpose:- 3306- 33060wordpress:image: wordpress:latestports:- 80:80restart: alwaysenvironment:- WORDPRESS_DB_HOST=db- WORDPRESS_DB_USER=wordpress- WORDPRESS_DB_PASSWORD=wordpress- WORDPRESS_DB_NAME=wordpressvolumes:db_data:#启动项目#-d 后台运行#不加 -d 就是默认前台启动$ docker-compose -f docker-compose.yml up -d$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES70d3fc3fbb45 wordpress:latest "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp my_wordpress-wordpress-1459eccc745ed mariadb:10.6.4-focal "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp, 33060/tcp my_wordpress-db-1$ docker port my_wordpress-wordpress-180/tcp -> 0.0.0.0:8080/tcp -> :::80

http://:80 后续的操作就是 WordPress 根据引导进行部署即可



:::color1
目前的IT主流的技术:Linux + Docker + Kubernetes(掌握) 掌握的技术:Docker 基础、原理、网络、服务、集群、错误排查、日志:::
6.4 Flask-Redis 使用 docker-compose 文件启动
version: "3"services:redis:image: redisweb:build:context: .dockerfile: Dockerfileports:- 8080:5000environment:REDIS_HOST: redis
文件列表目录:

$ vim app.pyimport osimport socketfrom flask import Flaskfrom redis.client import Redisapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello():redis.incr('hits')return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'), socket.gethostname())if __name__ == "__main__":app.run(host="0.0.0.0", port=5000, debug=True)
$ vim DockerfileFROM python:2.7LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"RUN mkdir -p /app && \pip install flask redisWORKDIR /appCOPY . /appEXPOSE 5000CMD [ "python", "app.py" ]
使用 docker-compose 启动
docker-compose up -d

$ docker-compose psNAME COMMAND SERVICE STATUS PORTSflask-redis-redis-1 "docker-entrypoint.s…" redis running 6379/tcpflask-redis-web-1 "python app.py" web running 0.0.0.0:8080->5000/tcp, :::8080->5000/tcp# 验证是否成功$ curl localhost:8080Hello Container World! I have been seen 1 times and my hostname is d6e9f1006ae6.
范例:使用浏览器访问

6.5 水平扩展和负载均衡
6.5.1 水平扩展
水平扩展,是指通过增加更多的服务器或者程序实例来分散负载,从而提升存储能力和计算能力。实现水平扩展(在 6.4 Flask-Redis 使用 docker-compose 文件启动 基础上进行修改)

# 修改配置文件,将宿主机端口8080映射删除$ vim docker-compose.yamlversion: "3"services:redis:image: redisweb:build:context: .dockerfile: Dockerfileenvironment:REDIS_HOST: redis
# 中止docker-compose运行的容器$ docker-compose down# 水平扩展$ docker-compose up --scale web=3 -d[+] Running 5/5⠿ Network flask-redis_default Created 0.1s⠿ Container flask-redis-web-3 Started 1.5s⠿ Container flask-redis-redis-1 Started 0.9s⠿ Container flask-redis-web-1 Started 1.5s⠿ Container flask-redis-web-2 Started 0.9s$ docker-compose psNAME COMMAND SERVICE STATUS PORTSflask-redis-redis-1 "docker-entrypoint.s…" redis running 6379/tcpflask-redis-web-1 "python app.py" web running 5000/tcpflask-redis-web-2 "python app.py" web running 5000/tcpflask-redis-web-3 "python app.py" web running 5000/tcp


6.5.2 负载均衡
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
mkdir -pv lb-scale ; cd lb-scalecat > docker-compose.yml <<-'EOF'version: "3"services:redis:image: redisrestart: alwaysweb01:build:context: .dockerfile: Dockerfileexpose:- "80"environment:REDIS_HOST: redisrestart: alwaysweb02:build:context: .dockerfile: Dockerfileexpose:- "80"environment:REDIS_HOST: redisrestart: alwayslb:image: haproxylinks:- web01- web02ports:- "7777:1080"- "8080:8080"volumes:- /var/run/docker.sock:/var/run/docker.sock- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfgrestart: alwaysEOF# 设置 HAProxy 配置文件cat > haproxy.cfg <<-'EOF'#---------------------------------------------------------------------# Example configuration for a possible web application. See the# full configuration options online.## http://haproxy.1wt.eu/download/1.4/doc/configuration.txt##---------------------------------------------------------------------#---------------------------------------------------------------------# Global settings#---------------------------------------------------------------------global #全局配置文件# to have these messages end up in /var/log/haproxy.log you will# need to: #配置日志## 1) configure syslog to accept network log events. This is done# by adding the '-r' option to the SYSLOGD_OPTIONS in# /etc/sysconfig/syslog #修改syslog配置文件## 2) configure local2 events to go to the /var/log/haproxy.log# file. A line like the following can be added to# /etc/sysconfig/syslog #定义日志设备## local2.* /var/log/haproxy.log##log 127.0.0.1 local2 #日志配置,所有的日志都记录本地,通过local2输出#chroot /var/lib/haproxy #改变haproxy的工作目录#pidfile /var/run/haproxy.pid #指定pid文件的路径maxconn 4000 #最大连接数的设定#user haproxy #指定运行服务的用户#group haproxy #指定运行服务的用户组daemon# turn on stats unix socket#stats socket /var/lib/haproxy/stats#---------------------------------------------------------------------# common defaults that all the 'listen' and 'backend' sections will# use if not designated in their block#---------------------------------------------------------------------defaultsmode http #默认使用协议,可以为{http|tcp|health} http:是七层协议 tcp:是四层 health:只返回OKlog global #全局日志记录option httplog #详细记录http日志option dontlognull #不记录空日志option http-server-close #启用http-server-closeoption forwardfor except 127.0.0.0/8 #来自这些信息的都不forwardforoption redispatch #重新分发,ServerID对应的服务器宕机后,强制定向到其他运行正常的服务器retries 3 #3次连接失败则认为服务不可用timeout http-request 10s #默认http请求超时时间timeout queue 1m #默认队列超时时间timeout connect 10s #默认连接超时时间timeout client 1m #默认客户端超时时间timeout server 1m #默认服务器超时时间timeout http-keep-alive 10s #默认持久连接超时时间timeout check 10s #默认检查时间间隔maxconn 3000 #最大连接数#---------------------------------------------------------------------# main frontend which proxys to the backends#---------------------------------------------------------------------# frontend main *:80# #定义ACL规则以如".html"结尾的文件;-i:忽略大小写# acl url_static path_beg -i /static /images /javascript /stylesheets# acl url_static path_end -i .jpg .gif .png .css .js# use_backend static if url_static #调用后端服务器并检查ACL规则是否被匹配# default_backend app #客户端访问时默认调用后端服务器地址池#---------------------------------------------------------------------# static backend for serving up images, stylesheets and such#---------------------------------------------------------------------# backend static #定义后端服务器# balance roundrobin #定义算法;基于权重进行轮询# server static 127.0.0.1:4331 check check #启动对后端server的健康状态检测listen statsbind 0.0.0.0:1080mode httpstats enablestats hide-versionstats uri /statsstats auth admin:admin#---------------------------------------------------------------------# round robin balancing between the various backends#---------------------------------------------------------------------frontend balancebind 0.0.0.0:8080default_backend web_backendsbackend web_backendsmode httpoption forwardforbalance roundrobinserver web01 web01:80 checkserver web02 web02:80 checkEOF
查看文件列表目录

$ vim app.pyimport osimport socketfrom flask import Flaskfrom redis.client import Redisapp = Flask(__name__)redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)@app.route('/')def hello():redis.incr('hits')return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'), socket.gethostname())if __name__ == "__main__":app.run(host="0.0.0.0", port=80, debug=True)
cat > Dockerfile <<-'EOF'FROM python:2.7LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"RUN mkdir -p /app && \pip install flask redisWORKDIR /appCOPY . /appEXPOSE 80CMD [ "python", "app.py" ]EOF
$ docker-compose up -d$ docker-compose psNAME COMMAND SERVICE STATUS PORTSlb-scale-lb-1 "docker-entrypoint.s…" lb running 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:7777->1080/tcp, :::7777->1080/tcplb-scale-redis-1 "docker-entrypoint.s…" redis running 6379/tcplb-scale-web01-1 "python app.py" web01 running 80/tcplb-scale-web02-1 "python app.py" web02 running 80/tcp

测试负载均衡的效果!!!
$ curl localhost:8080

查看HAProxy WebUI
# 格式:http://\<IP地址\>:7777/statshttp://10.0.0.54:7777/stats

:::color5 可以结合水平扩展和负载均衡合并实验效果!!!
:::
6.6 部署一个复杂的投票应用

# GitHub Reference:# https://github.com/dockersamples/example-voting-app# Download Reference:# https://dl-download.csdn.net/down11/20220122/cfdb56694c4b0eda57c1371873fbe3c3.zip?Expires=1667997639&OSSAccessKeyId=STS.NV4w6jF318mTXBo5S5kWAiwUw&Signature=XuzO6aJ%2FcesPCM%2BTHIS4KGRSOlQ%3D&response-content-disposition=attachment%3Bfilename%3D%22example-voting-app-master.zip%22&Date=1667997639&security-token=CAISgwJ1q6Ft5B2yfSjIr5WBPIzeq%2BwQj6%2B%2FWmTe0VNgZ9hthrL%2BlTz2IHxFf3FoCOEYv%2Fk1nWlU6%2FoTlqF%2FTIBDQUvNYZOVTQTbXFvzDbDasumZsJYw6vT8a1fxZjf%2F2MjNGaCbKPrWZvaqbX3diyZ32sGUXD6%2BXlujQ%2BDr6Zl8dYY4UxX6D1tBH8wEAgp5tI1gQhm3D%2Fu2NQPwiWf9FVdhvhEG6Vly8qOi2MaRmHG85R%2FYsrZJ%2FtuvecD%2FMJI3Z8kvC4uPsbYoJvab4kl58ANX8ap6tqtA9Arcs8uVa1sruEnXaLKMo4wxfVIjP%2FFmRvIVtprnieY9tuiWkJ%2Fs25qImF%2BBkY61GoABVf%2BNTaHYQqbDW%2F10rRECk0IdKdoxPoU%2FFQtmAiBMcjW3ZiN%2FoR9gAX1LHZTeBCSuUjhP3L1iOYDIjmz02TDsZCoE3hd%2FF%2FKYYr6fqapSpr8CVLmdZNf2x5rHUWr0Awk%2B9QGx8%2Bzhw%2FZyCNAlsmyGIAg%2Fx4PXQnLsF4KEySlsy2o%3D$ mkdir -pv example-voting-app ; cd example-voting-app# 将压缩包进行解压$ unzip example-voting-app-master.zip$ cd example-voting-app-master# 运行Docker-Compose$ docker-compose up -d# 需要等待一定时间(大约10分钟)$ docker-compose ps -aNAME COMMAND SERVICE STATUS PORTSexample-voting-app-master-db-1 "docker-entrypoint.s…" db running (healthy) 5432/tcpexample-voting-app-master-redis-1 "docker-entrypoint.s…" redis running (healthy) 0.0.0.0:49153->6379/tcp, :::49153->6379/tcpexample-voting-app-master-result-1 "docker-entrypoint.s…" result running 0.0.0.0:5858->5858/tcp, :::5858->5858/tcp, 0.0.0.0:5001->80/tcp, :::5001->80/tcpexample-voting-app-master-vote-1 "python app.py" vote running 0.0.0.0:5000->80/tcp, :::5000->80/tcpexample-voting-app-master-worker-1 "dotnet Worker.dll" worker running
使用浏览器进行访问:
# 投票的页面http://10.0.0.54:5000/# 统计的页面http://10.0.0.54:5001/


:::color5 Docker-Compose 是一个用于本地开发的工具和用于单机容器编排的工具!
不太适用于集群环境中的使用。
:::
