- 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/7
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
# 可以查看到当前有一个 Vagrantfile 配置文件
E:\centos7> dir
驱动器 E 中的卷是 Studying
卷的序列号是 7822-9BE5
E:\centos7 的目录
2022/11/01 21:17 <DIR> .
2022/11/01 21:17 3,084 Vagrantfile
1 个文件 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 module
Loaded: loaded (/usr/lib/virtualbox/vboxdrv.sh; enabled; vendor preset: disabled)
Active: active (exited) since Wed 2022-11-02 00:06:27 CST; 54s ago
Process: 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
# SHELL
config.vm.provision "shell", inline: <<-SHELL
sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl start docker
SHELL
end
1.4 在Linux系统中安装 Docker
环境准备
- 需要会一部分Linux的基础
- CentOS 7
- 我们可以使用远程连接工具连接远程服务器进行操作!
环境查看
#系统内核是 3.10 以上的
$ uname -r
3.10.0-1160.el7.x86_64
#系统版本
$ cat /etc/os-release
NAME="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 images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-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/docker
sudo 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
sudo systemctl daemon-reload
sudo 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 version
docker-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 ls
NAME ACTIVE DRIVER STATE URL
dev - virtualbox Running tcp://192.168.99.103:2376
staging * digitalocean Running tcp://203.0.113.81:2376
$ echo $DOCKER_HOST
tcp://203.0.113.81:2376
$ docker-machine active
staging
- 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 test
export 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 version
Client: Docker Engine - Community
Version: 20.10.21
API version: 1.40
Go version: go1.18.7
Git commit: baeda1f
Built: Tue Oct 25 18:04:24 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 19.03.12
API version: 1.40 (minimum version 1.12)
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:49:35 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: 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 ENV
DOCKER_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 3
echo -e "==> 修改配置文件"
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新并安装Docker-CE
echo -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/docker
sudo 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-reload
sudo systemctl restart docker
sudo 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 module
Loaded: loaded (/usr/lib/virtualbox/vboxdrv.sh; enabled; vendor preset: disabled)
Active: active (exited) since Wed 2022-11-02 00:06:27 CST; 54s ago
Process: 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'
# 磁盘只要20G
export ECS_SYSTEM_DISK_SIZE='20'
# 磁盘用便宜的高效云盘吧,任性选SSD也行
export ECS_SYSTEM_DISK_CATEGORY='cloud_efficiency'
# 选择的镜像是Ubuntu16.04
export 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'
# 一定要是true
export ECS_IO_OPTIMIZED='true'
2.5.1.4 阿里云Docker-Machine使用
阿里云操作AccessKey
- RAM访问控制创建用户
- RAM访问控制获取AccessKey
- 需要将其RAM访问控制的用户权限放通(ECS权限,否则创建失败)
- 获取AccessKey ID和AccessKey Secret
AccessKey ID
LTAI5tAatgSRkX4FDhhhXUgm
AccessKey Secret
GjjqZBxKTDvop8h3wB3pakWvi6Sm7L
# 格式:
# 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 version
Client: Docker Engine - Community
Version: 20.10.10
API version: 1.41
Go version: go1.16.9
Git commit: b485636
Built: Mon Oct 25 07:44:50 2021
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.10
API version: 1.41 (minimum version 1.12)
Go version: go1.16.9
Git commit: e2f740d
Built: Mon Oct 25 07:43:13 2021
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.9
GitCommit: 1c90a442489720eec95342e1789ee8a5e1b9536f
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0
$ ps -ef | grep docker
root 1187 1 0 09:28 ? 00:00:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 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 images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 10 months ago 141MB
$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 10 months ago 141MB
3.2.2 Image 的获取(1)
- Build from Dockerfile
FROM ubuntu:18.04
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
RUN apt-get update && apt-get install -y redis-server
EXPOSE 6379
ENTRYPOINT [ "/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.04
14.04: Pulling from library/ubuntu
2e6e20c8e2e6: Pull complete
0551a797c01d: Pull complete
512123a864da: Pull complete
Digest: sha256:60840958b25b5947b11d7a274274dc48ab32a2f5d18527f5dae2962b64269a3a
Status: Downloaded newer image for ubuntu:14.04
docker.io/library/ubuntu:14.04
:::color2 扩展:使得普通用户可以执行 docker 命令
:::
sudo groupadd docker
sudo 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
$ ./hello
hello docker!
# 编写 Dockerfile 文件
$ vim Dockerfile
FROM scratch
# Scratch是一个空的Docker镜像。
# 通过scratch来构建一个基础镜像。
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
ADD hello /
CMD [ "/hello" ]
# 打包成镜像
$ docker build -t zhongzhiwei/hello-world .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zhongzhiwei/hello-world latest 5fbbac5d29a0 37 seconds ago 861kB
$ docker history zhongzhiwei/hello-world
IMAGE CREATED CREATED BY SIZE COMMENT
5fbbac5d29a0 About a minute ago /bin/sh -c #(nop) CMD ["/hello"] 0B
ec3fad14f2ad About a minute ago /bin/sh -c #(nop) ADD file:061ea1fe9f44c64f7… 861kB
2a56b96ab165 About a minute ago /bin/sh -c #(nop) LABEL maintainer=zhongzhi… 0B
# 运行成容器
$ docker run -it zhongzhiwei/hello-world
hello 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 --help
Usage: 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 /]# ls
anaconda-post.log dev home lib64 mnt proc run srv tmp var
bin 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 1
VIM - 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 images
REPOSITORY TAG IMAGE ID CREATED SIZE
zhongzhiwei/centos-vim v1.0 33284aef474e About a minute ago 442MB
$ docker image history zhongzhiwei/centos-vim:v1.0
IMAGE CREATED CREATED BY SIZE COMMENT
33284aef474e 5 minutes ago /bin/bash 238MB Add software vim
eeb6ee3f44bd 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 1
VIM - 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 Dockerfile
FROM centos:7.9.2009 AS baseimg
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
# 安装 vim 软件
RUN yum install -y vim
$ docker build -t zhongzhiwei/centos-vim:v2.0 -f Dockerfile .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zhongzhiwei/centos-vim v2.0 702ffcff1ca7 15 seconds ago 442MB
$ docker image history zhongzhiwei/centos-vim:v2.0
IMAGE CREATED CREATED BY SIZE COMMENT
702ffcff1ca7 35 seconds ago /bin/sh -c yum install -y vim 238MB
ef92e80a1b69 2 minutes ago /bin/sh -c #(nop) LABEL maintainer=zhongzhi… 0B
eeb6ee3f44bd 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.2009
ENV name Docker
ENTRYPOINT [ "/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会自动帮解压。 尽量不要把
#test
FROM ubuntu
MAINTAINER hello
ADD test1.txt test1.txt
ADD test1.txt test1.txt.bak
ADD test1.txt /mydir/
ADD data1 data1
ADD data2 data2
ADD 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 ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
那么将执行ls -al ,top -b不会执行
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ls -al
:::color2
ENTRYPOINT:设置容器启动时运行的命令- 让容器以应用程序或者服务的形式运行
- 不会被忽略,一定会执行
- 最佳实践,写一个 shell 脚本作为 entrypoint
:::
3.4.2.10 VOLUME
可实现挂载功能,可以将内地文件夹或者其他容器种得文件夹挂在到这个容器种 语法为:VOLUME ["/data"]
一般的使用场景为需要持久化存储数据时, 容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。 所以当数据需要持久化时用这个命令。
VOLUME ["/var/log/"]
VOLUME /var/log
VOLUME /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 /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
<font style="color:rgb(0, 0, 0);">/path/$DIRNAME</font>

:::
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 --help
Usage: 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. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login 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 Dockerfile
FROM scratch
LABEL 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.com
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login 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 Dockerfile
FROM scratch
LABEL 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.py
from flask import Flask
app = 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.7
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere>"
RUN pip install flask && \
mkdir -pv /app
WORKDIR /app
ADD app.py /app/
CMD [ "python", "app.py" ]
EXPOSE 5000
$ docker build -t flask-demo:v1.0 -f Dockerfile .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-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:5000
hello docker
$ docker run -it ubuntu
root@045d68516f5f:/# apt update && apt install -y stress
root@045d68516f5f:/# which stress
/usr/bin/stress
# stress 是Linux系统的压力测试工具
root@045d68516f5f:/# stress --help
`stress'' imposes certain types of compute stress on your system
Usage: 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 10s
Note: Numbers may be suffixed with s,m,h,d,y (time) or B,K,M,G (size).
# 默认是256MB空间
root@045d68516f5f:/# stress --vm 1 -v
root@045d68516f5f:/# stress --vm 1 --vm-bytes 4096MB -v
$ mkdir -pv /root/dockerfile/ubuntu-stress ; cd /root/dockerfile/ubuntu-stress
# 编写Dockerfile文件
$ vim Dockerfile
FROM ubuntu
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere>"
RUN apt update && \
apt install -y stress
ENTRYPOINT [ "/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-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a66a4b6f4e6ba40907b14fa3acf2e2add3e285bae67b13bd73b03d15f632cee0 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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3b7980304ddd 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-release
CentOS Linux release 7.9.2009 (Core)
[root@3b7980304ddd /]# exit
$ docker exec -it centos-node hostname -I
172.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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4e3fb2992fbc 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 u1
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c05513160bbb 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 t1
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c65cbab30a2d t1 0.00% 540KiB / 1.794GiB 0.03% 656B / 0B 0B / 0B 1
$ docker stats --no-stream t2
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c6a65f191a7a 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.com
PING 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 80
Trying 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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b07115b7777c busybox "/bin/sh -c 'while t…" 46 seconds ago Up 46 seconds 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
100: eth0@if101: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 查看宿主机的网络接口和IP地址
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0
valid_lft 61373sec preferred_lft 61373sec
inet6 fe80::f816:3eff:fe6b:c203/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:c5ff:fe3b:4b05/64 scope link
valid_lft forever preferred_lft forever
101: veth9de4bfa@if100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 82:ac:0a:b1:70:2b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::80ac:aff:feb1:702b/64 scope link
valid_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 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
102: eth0@if103: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# ping 通 test1 的IP地址
$ docker exec -it test2 ping -c1 -W1 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.091/0.091/0.091 ms
查看机器的 Network NameSpace
$ ip netns help
Usage: ip netns list
ip netns add NAME
ip netns set NAME NETNSID
ip [-all] netns delete [NAME]
ip netns identify [PID]
ip netns pids NAME
ip [-all] netns exec [NAME] cmd ...
ip netns monitor
ip netns list-id
# 创建 Network NameSpace
$ ip netns add test1
$ ip netns add test2
# 显示 Network NameSpace
$ ip netns list
test1
# 删除 Network NameSpace
$ ip netns delete test1
# 在 Network NameSpace 执行
$ ip netns exec test1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 查看宿主机的 ip link 数据链路层
$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff
# 启用 netns 的lo网卡
$ ip netns exec test1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/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 link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/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 link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff
104: veth-test2@veth-test1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether d6:80:fb:40:73:7c brd ff:ff:ff:ff:ff:ff
105: veth-test1@veth-test2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 06:1f:fa:32:e6:2a brd ff:ff:ff:ff:ff:ff
$ sudo ip netns exec test1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/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 link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
105: veth-test1@if104: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/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 link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
104: veth-test2@if105: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/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 link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/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 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
105: veth-test1@if104: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 06:1f:fa:32:e6:2a brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 192.168.1.1/24 scope global veth-test1
valid_lft forever preferred_lft forever
inet6 fe80::41f:faff:fe32:e62a/64 scope link
valid_lft forever preferred_lft forever
$ sudo ip netns exec test2 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
104: veth-test2@if105: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether d6:80:fb:40:73:7c brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.1.2/24 scope global veth-test2
valid_lft forever preferred_lft forever
inet6 fe80::d480:fbff:fe40:737c/64 scope link
valid_lft forever preferred_lft forever
# 检查两个网络命名空间是否互通
$ sudo ip netns exec test1 ping -c1 -W1 192.168.1.2
PING 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 0ms
rtt min/avg/max/mdev = 0.088/0.088/0.088/0.000 ms
$ sudo ip netns exec test2 ping -c1 -W1 192.168.1.1
PING 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 0ms
rtt 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 ls
NETWORK ID NAME DRIVER SCOPE
8a2071481321 bridge bridge local
f9b95f3c1bbc host host local
f29d29b46da8 none null local
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8926c4b7988a 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 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0
valid_lft 85718sec preferred_lft 85718sec
inet6 fe80::f816:3eff:fe6b:c203/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:c5ff:fe3b:4b05/64 scope link
valid_lft forever preferred_lft forever
107: vethde44194@if106: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ca:97:10:8e:a0:59 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::c897:10ff:fe8e:a059/64 scope link
valid_lft forever preferred_lft forever
$ sudo yum install -y bridge-utils
# 查看bridge网络信息
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 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 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0
valid_lft 85413sec preferred_lft 85413sec
inet6 fe80::f816:3eff:fe6b:c203/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:c5ff:fe3b:4b05/64 scope link
valid_lft forever preferred_lft forever
107: vethde44194@if106: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ca:97:10:8e:a0:59 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::c897:10ff:fe8e:a059/64 scope link
valid_lft forever preferred_lft forever
109: veth176c34f@if108: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether d2:9d:24:68:5a:9a brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::d09d:24ff:fe68:5a9a/64 scope link
valid_lft forever preferred_lft forever
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c53b4b05 no veth176c34f
vethde44194
:::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 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
106: eth0@if107: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 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.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.110/0.110/0.110 ms
# 因为有 --link 模式,所以可以通过容器名进行互通
/ # ping -c1 -W1 test1
PING test1 (172.17.0.2): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.074/0.074/0.074 ms
# 但是 test1 并不能通过容器名ping通 test2
$ docker exec -it test1 ping -c1 -W1 test2
ping: bad address 'test2'
# 所以这种 --link 的方式使用并不多,而是使用 docker 的自定义网络来实现
使用自定义网络来实现( Bridge网络模式 )
$ sudo docker network create --driver bridge my-bridge
$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
8a2071481321 bridge bridge local
f9b95f3c1bbc host host local
83e74a0a1a41 my-bridge bridge local
f29d29b46da8 none null local
$ sudo brctl show
bridge name bridge id STP enabled interfaces
br-83e74a0a1a41 8000.0242ecd098c7 no
docker0 8000.0242c53b4b05 no veth72e2a58
vethde44194
$ sudo docker run -it -d --name test3 --network my-bridge busybox /bin/sh -c "while true; do sleep 43200 ;done"
$ sudo brctl show
bridge name bridge id STP enabled interfaces
br-83e74a0a1a41 8000.0242ecd098c7 no vethba62779
docker0 8000.0242c53b4b05 no veth72e2a58
vethde44194
$ 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 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
110: eth0@if111: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
115: eth1@if116: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
# test2 容器通信 test3 的IP地址和主机名
$ sudo docker exec -it test2 ping -c1 -W1 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.103/0.103/0.103 ms
$ sudo docker exec -it test2 ping -c1 -W1 test3
PING test3 (172.18.0.2): 56 data bytes
64 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 loss
round-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.3
PING 172.18.0.3 (172.18.0.3): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.079/0.079/0.079 ms
$ sudo docker exec -it test3 ping -c1 -W1 test2
PING test2 (172.18.0.3): 56 data bytes
64 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 loss
round-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/bash
root@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 and
working. 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-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
46eda4530967606eeaefcae3e9f2597d7b4ad4c0becd6b0898f8768273233c3a nginx "/docker-entrypoint.sh nginx -g 'daemon off;'" 4 minutes ago Up 4 minutes 80/tcp web01
# 查看容器的IP地址
$ docker inspect -f "{{.NetworkSettings.IPAddress}}" web01
172.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 and
working. 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 80
Trying 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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8482453167f6 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp web01
# 使用宿主机的端口进行访问
$ ip addr
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.64/24 brd 192.168.0.255 scope global noprefixroute dynamic eth0
valid_lft 83173sec preferred_lft 83173sec
inet6 fe80::f816:3eff:fe6b:c203/64 scope link
valid_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 and
working. 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 ls
NETWORK ID NAME DRIVER SCOPE
8a2071481321 bridge bridge local
f9b95f3c1bbc host host local
83e74a0a1a41 my-bridge bridge local
f29d29b46da8 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 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
# 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 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.64/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0
valid_lft 82202sec preferred_lft 82202sec
inet6 fe80::f816:3eff:fe6b:c203/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:c5:3b:4b:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:c5ff:fe3b:4b05/64 scope link
valid_lft forever preferred_lft forever
112: br-83e74a0a1a41: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:ec:d0:98:c7 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-83e74a0a1a41
valid_lft forever preferred_lft forever
inet6 fe80::42:ecff:fed0:98c7/64 scope link
valid_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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
819798960a6a 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.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "hello docker"
if __name__ == '__main__':
app.run()
# 升级版
$ vim app.py
import os
import socket
from flask import Flask
from redis.client import Redis
app = 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.7
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
RUN mkdir -p /app && \
pip install flask redis
WORKDIR /app
COPY . /app
EXPOSE 5000
CMD [ "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/bash
root@0a1d7b16c43f:/app# echo $REDIS_HOST
redis
root@0a1d7b16c43f:/app# curl 127.0.0.1:5000
Hello Container World! I have been seen 1 times and my hostname is 0a1d7b16c43f.
root@0a1d7b16c43f:/app# curl 127.0.0.1:5000
Hello 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:5000
Hello Container World! I have been seen 3 times and my hostname is b02030a3c3ea.
$ curl 127.0.0.1:5000
Hello 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
/ # env
HOSTNAME=b5df0d82748d
SHLVL=1
HOME=/root
NAME=zhongzhiwei
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
EMAIL=zhongzhiwei@kubesphere.io
PWD=/
多机器的通信?
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的核心理念。




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

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.3
PING 10.0.0.3 (10.0.0.3): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.870/0.870/0.870 ms
$ docker exec -it test1 ping -c1 -W1 test2
PING test2 (10.0.0.3): 56 data bytes
64 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 loss
round-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.2
PING 10.0.0.2 (10.0.0.2): 56 data bytes
64 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 loss
round-trip min/avg/max = 0.653/0.653/0.653 ms
$ docker exec -it test2 ping -c1 -W1 test1
PING test1 (10.0.0.2): 56 data bytes
64 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 loss
round-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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
189a9f8a2978 redis "docker-entrypoint.s…" 4 seconds ago Up 2 seconds 6379/tcp redis
# 在Docker node2运行 flask-redis
$ vim app.py
import os
import socket
from flask import Flask
from redis.client import Redis
app = 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 Dockerfile
FROM python:2.7
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
RUN mkdir -p /app && \
pip install flask redis
WORKDIR /app
COPY . /app
EXPOSE 5000
CMD [ "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/bash
root@da02b8e1a821:/app# curl 127.0.0.1:5000
Hello Container World! I have been seen 1 times and my hostname is da02b8e1a821.
root@da02b8e1a821:/app# curl 127.0.0.1:5000
Hello Container World! I have been seen 2 times and my hostname is da02b8e1a821.
# 通过宿主机访问
$ curl 127.0.0.1:5000
Hello Container World! I have been seen 3 times and my hostname is da02b8e1a821.
$ curl 127.0.0.1:5000
Hello 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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a0ff6fb860b mysql:5.7.40 "docker-entrypoint.s…" 9 seconds ago Up 8 seconds 3306/tcp, 33060/tcp mysql1
# 1. 匿名卷
# 查看 Docker 数据卷的信息
$ docker volume ls
DRIVER VOLUME NAME
local 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 ls
DRIVER VOLUME NAME
local 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 ls
DRIVER VOLUME NAME
local 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/bash
bash-4.2# mysql -uroot -pAdmin@h3c
mysql: [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 2
Server 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 its
affiliates. Other names may be trademarks of their respective
owners.
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> exit
Bye
bash-4.2# exit
# 删除 mysql1 的容器
$ sudo docker rm -f mysql1
# 启用另一个 mysql2 容器使用 mysql1 的数据卷
$ docker volume ls
DRIVER VOLUME NAME
local 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/bash
bash-4.2# mysql -uroot -pAdmin@h3c
mysql: [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 2
Server 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 its
affiliates. Other names may be trademarks of their respective
owners.
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/bash
root@7cceb0798a3d:/usr/share/nginx/html# pwd
/usr/share/nginx/html
root@7cceb0798a3d:/usr/share/nginx/html# ls
Dockerfile index.html
root@7cceb0798a3d:/usr/share/nginx/html# echo "Docker Content" > docker.txt
root@7cceb0798a3d:/usr/share/nginx/html# exit
# 宿主机操作
$ cd /root/dockerfile/docker-nginx
$ ls -l
total 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.txt
Docker Content
$ echo "Host Content" >> docker.txt
# 容器查看是否有内容同步到
$ docker exec -it web01 cat docker.txt
Docker Content
Host 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-focal
docker 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-compose
curl -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 version
Docker 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.yml
version: "3.9"
services:
db:
# We use a mariadb image which supports both amd64 & arm64 architecture
image: mariadb:10.6.4-focal
# If you really want to use MySQL, uncomment the following line
#image: mysql:8.0.27
command: '--default-authentication-plugin=mysql_native_password'
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
- MYSQL_ROOT_PASSWORD=somewordpress
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=wordpress
expose:
- 3306
- 33060
wordpress:
image: wordpress:latest
ports:
- 80:80
restart: always
environment:
- WORDPRESS_DB_HOST=db
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=wordpress
- WORDPRESS_DB_NAME=wordpress
volumes:
db_data:
#启动项目
#-d 后台运行
#不加 -d 就是默认前台启动
$ docker-compose -f docker-compose.yml up -d
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
70d3fc3fbb45 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-1
459eccc745ed 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-1
80/tcp -> 0.0.0.0:80
80/tcp -> :::80
http://:80 后续的操作就是 WordPress 根据引导进行部署即可
:::color1
目前的IT主流的技术:Linux + Docker + Kubernetes(掌握) 掌握的技术:Docker 基础、原理、网络、服务、集群、错误排查、日志:::
6.4 Flask-Redis 使用 docker-compose 文件启动
version: "3"
services:
redis:
image: redis
web:
build:
context: .
dockerfile: Dockerfile
ports:
- 8080:5000
environment:
REDIS_HOST: redis
文件列表目录:
$ vim app.py
import os
import socket
from flask import Flask
from redis.client import Redis
app = 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 Dockerfile
FROM python:2.7
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
RUN mkdir -p /app && \
pip install flask redis
WORKDIR /app
COPY . /app
EXPOSE 5000
CMD [ "python", "app.py" ]
使用 docker-compose 启动
docker-compose up -d
$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
flask-redis-redis-1 "docker-entrypoint.s…" redis running 6379/tcp
flask-redis-web-1 "python app.py" web running 0.0.0.0:8080->5000/tcp, :::8080->5000/tcp
# 验证是否成功
$ curl localhost:8080
Hello 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.yaml
version: "3"
services:
redis:
image: redis
web:
build:
context: .
dockerfile: Dockerfile
environment:
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 ps
NAME COMMAND SERVICE STATUS PORTS
flask-redis-redis-1 "docker-entrypoint.s…" redis running 6379/tcp
flask-redis-web-1 "python app.py" web running 5000/tcp
flask-redis-web-2 "python app.py" web running 5000/tcp
flask-redis-web-3 "python app.py" web running 5000/tcp
6.5.2 负载均衡
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
mkdir -pv lb-scale ; cd lb-scale
cat > docker-compose.yml <<-'EOF'
version: "3"
services:
redis:
image: redis
restart: always
web01:
build:
context: .
dockerfile: Dockerfile
expose:
- "80"
environment:
REDIS_HOST: redis
restart: always
web02:
build:
context: .
dockerfile: Dockerfile
expose:
- "80"
environment:
REDIS_HOST: redis
restart: always
lb:
image: haproxy
links:
- web01
- web02
ports:
- "7777:1080"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
restart: always
EOF
# 设置 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
#---------------------------------------------------------------------
defaults
mode http #默认使用协议,可以为{http|tcp|health} http:是七层协议 tcp:是四层 health:只返回OK
log global #全局日志记录
option httplog #详细记录http日志
option dontlognull #不记录空日志
option http-server-close #启用http-server-close
option forwardfor except 127.0.0.0/8 #来自这些信息的都不forwardfor
option 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 stats
bind 0.0.0.0:1080
mode http
stats enable
stats hide-version
stats uri /stats
stats auth admin:admin
#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
frontend balance
bind 0.0.0.0:8080
default_backend web_backends
backend web_backends
mode http
option forwardfor
balance roundrobin
server web01 web01:80 check
server web02 web02:80 check
EOF
查看文件列表目录
$ vim app.py
import os
import socket
from flask import Flask
from redis.client import Redis
app = 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.7
LABEL maintainer="zhongzhiwei <zhongzhiwei@kubesphere.io>"
RUN mkdir -p /app && \
pip install flask redis
WORKDIR /app
COPY . /app
EXPOSE 80
CMD [ "python", "app.py" ]
EOF
$ docker-compose up -d
$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
lb-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/tcp
lb-scale-redis-1 "docker-entrypoint.s…" redis running 6379/tcp
lb-scale-web01-1 "python app.py" web01 running 80/tcp
lb-scale-web02-1 "python app.py" web02 running 80/tcp
测试负载均衡的效果!!!
$ curl localhost:8080
查看HAProxy WebUI
# 格式:http://\<IP地址\>:7777/stats
http://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 -a
NAME COMMAND SERVICE STATUS PORTS
example-voting-app-master-db-1 "docker-entrypoint.s…" db running (healthy) 5432/tcp
example-voting-app-master-redis-1 "docker-entrypoint.s…" redis running (healthy) 0.0.0.0:49153->6379/tcp, :::49153->6379/tcp
example-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/tcp
example-voting-app-master-vote-1 "python app.py" vote running 0.0.0.0:5000->80/tcp, :::5000->80/tcp
example-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 是一个用于本地开发的工具和用于单机容器编排的工具!
不太适用于集群环境中的使用。
:::