逻辑卷管理(LVM)的创建与使用

未分类

我想告诉你的是,当 LVM (逻辑卷管理)首次出现在 Fedora Linux 中时,我是非常抗拒它的。我最初的反应是,我并不需要在我和我的设备之间有这种额外的抽象层。结果是我错了,逻辑卷管理是非常有用的。

管理磁盘空间对系统管理员来说是一件重要的日常工作。一旦磁盘空间耗尽就需要进行一系列耗时而又复杂的任务,以提升磁盘分区中可用的磁盘空间。它也需要系统离线才能处理。通常这种任务会涉及到安装一个新的硬盘、引导至恢复模式或者单用户模式、在新硬盘上创建一个分区和一个文件系统、挂载到临时挂载点去从一个太小的文件系统中移动数据到较大的新位置、修改 /etc/fstab 文件的内容来反映出新分区的正确设备名、以及重新引导来重新挂载新的文件系统到正确的挂载点。

我想告诉你的是,当 LVM (逻辑卷管理)首次出现在 Fedora Linux 中时,我是非常抗拒它的。我最初的反应是,我并不需要在我和我的设备之间有这种额外的抽象层。结果是我错了,逻辑卷管理是非常有用的。
LVM 让磁盘空间管理非常灵活。它提供的功能诸如在文件系统已挂载和活动时,很可靠地增加磁盘空间到一个逻辑卷和它的文件系统中,并且,它也允许你将多个物理磁盘和分区融合进一个可以分割成逻辑卷(LV)的单个卷组(VG)中。

卷管理也允许你去减少分配给一个逻辑卷的磁盘空间数量,但是,这里有两个要求,第一,卷必须是未挂载的。第二,在卷空间调整之前,文件系统本身的空间大小必须先被减少。

有一个重要的提示是,文件系统本身必须允许重新调整大小的操作。当重新提升文件系统大小的时候,EXT2、3 和 4 文件系统都允许离线(未挂载状态)或者在线(挂载状态)重新调整大小。你应该去认真了解你打算去调整的文件系统的详细情况,去验证它们是否可以完全调整大小,尤其是否可以在线调整大小。

即时扩展一个文件系统

在我安装一个新的发行版到我的生产用机器中之前,我总是喜欢在一个 VirtualBox 虚拟机中运行这个新的发行版一段时间,以确保它没有任何的致命的问题存在。在几年前的一个早晨,我在我的主要使用的工作站上的虚拟机中安装了一个新发行的 Fedora 版本。我认为我有足够的磁盘空间分配给安装虚拟机的主文件系统。但是,我错了,大约在安装到三分之一时,我耗尽了我的文件系统的空间。幸运的是,VirtualBox 检测到了磁盘空间不足的状态,并且暂停了虚拟机,然后显示了一个明确指出问题所在的错误信息。

请注意,这个问题并不是虚拟机磁盘太小造成的,而是由于宿主机上空间不足,导致虚拟机上的虚拟磁盘在宿主机上的逻辑卷中没有足够的空间去扩展。

因为许多现在的发行版都缺省使用了逻辑卷管理,并且在我的卷组中有一些可用的空余空间,我可以分配额外的磁盘空间到适当的逻辑卷,然后即时扩展宿主机的文件系统。这意味着我不需要去重新格式化整个硬盘,以及重新安装操作系统或者甚至是重启机器。我不过是分配了一些可用空间到适当的逻辑卷中,并且重新调整了文件系统的大小 —— 所有的这些操作都在文件系统在线并且运行着程序的状态下进行的,虚拟机也一直使用着宿主机文件系统。在调整完逻辑卷和文件系统的大小之后,我恢复了虚拟机的运行,并且继续进行安装过程,就像什么问题都没有发生过一样。

虽然这种问题你可能从来也没有遇到过,但是,许多人都遇到过重要程序在运行过程中发生磁盘空间不足的问题。而且,虽然许多程序,尤其是 Windows 程序,并不像 VirtualBox 一样写的很好,且富有弹性,Linux 逻辑卷管理可以使它在不丢失数据的情况下去恢复,也不需要去进行耗时的安装过程。

LVM 结构

逻辑卷管理的磁盘环境结构如下面的图 1 所示。逻辑卷管理允许多个单独的硬盘和/或磁盘分区组合成一个单个的卷组(VG)。卷组然后可以再划分为逻辑卷(LV)或者被用于分配成一个大的单一的卷。普通的文件系统,如 EXT3 或者 EXT4,可以创建在一个逻辑卷上。

在图 1 中,两个完整的物理硬盘和一个第三块硬盘的一个分区组合成一个单个的卷组。在这个卷组中创建了两个逻辑卷和文件系统,比如,可以在每个逻辑卷上创建一个 EXT3 或者 EXT4 的文件系统。

未分类
图 1: LVM 允许组合分区和整个硬盘到卷组中

在一个主机上增加磁盘空间是非常简单的,在我的经历中,这种事情是很少的。下面列出了基本的步骤。你也可以创建一个完整的新卷组或者增加新的空间到一个已存在的逻辑卷中,或者创建一个新的逻辑卷。

增加一个新的逻辑卷

有时候需要在主机上增加一个新的逻辑卷。例如,在被提示包含我的 VirtualBox 虚拟机的虚拟磁盘的 /home 文件系统被填满时,我决定去创建一个新的逻辑卷,以存储包含虚拟磁盘在内的虚拟机数据。这将在我的 /home 文件系统中释放大量的空间,并且也允许我去独立地管理虚拟机的磁盘空间。

增加一个新的逻辑卷的基本步骤如下:

☉ 如有需要,安装一个新硬盘。
☉ 可选: 在硬盘上创建一个分区。
☉ 在硬盘上创建一个完整的物理卷(PV)或者一个分区。
☉ 分配新的物理卷到一个已存在的卷组(VG)中,或者创建一个新的卷组。
☉ 从卷空间中创建一个新的逻辑卷(LV)。
☉ 在新的逻辑卷中创建一个文件系统。
☉ 在 /etc/fstab 中增加适当的条目以挂载文件系统。
☉ 挂载文件系统。

为了更详细的介绍,接下来将使用一个示例作为一个实验去教授关于 Linux 文件系统的知识。

示例

这个示例展示了怎么用命令行去扩展一个已存在的卷组,并给它增加更多的空间,在那个空间上创建一个新的逻辑卷,然后在逻辑卷上创建一个文件系统。这个过程一直在运行着和已挂载的文件系统上执行。

警告:仅 EXT3 和 EXT4 文件系统可以在运行和挂载状态下调整大小。许多其它的文件系统,包括 BTRFS 和 ZFS 是不能这样做的。

安装硬盘
如果在系统中现有硬盘上的卷组中没有足够的空间可以增加,那么可能需要去增加一块新的硬盘,然后创建空间增加到逻辑卷中。首先,安装物理硬盘,然后,接着执行后面的步骤。

从硬盘上创建物理卷
首先需要去创建一个新的物理卷(PV)。使用下面的命令,它假设新硬盘已经分配为 /dev/hdd。

pvcreate /dev/hdd

在新硬盘上创建一个任意分区并不是必需的。创建的物理卷将被逻辑卷管理器识别为一个新安装的未处理的磁盘或者一个类型为 83 的 Linux 分区。如果你想去使用整个硬盘,创建一个分区并没有什么特别的好处,而且元数据所用的磁盘空间也能用做 PV 的一部分使用。

扩展已存在的卷组
在这个示例中,我将扩展一个已存在的卷组,而不是创建一个新的;你可以选择其它的方式。在物理磁盘已经创建之后,扩展已存在的卷组(VG)去包含新 PV 的空间。在这个示例中,已存在的卷组命名为:MyVG01。

vgextend /dev/MyVG01 /dev/hdd

创建一个逻辑卷
首先,在卷组中从已存在的空余空间中创建逻辑卷。下面的命令创建了一个 50 GB 大小的 LV。这个卷组的名字为 MyVG01,然后,逻辑卷的名字为 Stuff。

lvcreate -L +50G --name Stuff MyVG01

创建文件系统
创建逻辑卷并不会创建文件系统。这个任务必须被单独执行。下面的命令在新创建的逻辑卷中创建了一个 EXT4 文件系统。

mkfs -t ext4 /dev/MyVG01/Stuff

增加一个文件系统卷标
增加一个文件系统卷标,更易于在文件系统以后出现问题时识别它。

e2label /dev/MyVG01/Stuff Stuff

挂载文件系统
在这个时候,你可以创建一个挂载点,并在 /etc/fstab 文件系统中添加合适的条目,以挂载文件系统。
你也可以去检查并校验创建的卷是否正确。你可以使用 df、lvs 和 vgs 命令去做这些工作。

在 LVM 文件系统中调整逻辑卷大小

从 Unix 的第一个版本开始,对文件系统的扩展需求就一直伴随,Linux 也不例外。随着有了逻辑卷管理(LVM),现在更加容易了。

☉ 如有需要,安装一个新硬盘。
☉ 可选: 在硬盘上创建一个分区。
☉ 在硬盘上创建一个完整的物理卷(PV)或者一个分区。
☉ 分配新的物理卷到一个已存在的卷组(VG)中,或者创建一个新的卷组。
☉ 从卷空间中创建一个新的逻辑卷(LV),或者用卷组中部分或全部空间扩展已有的逻辑卷。
☉ 如果创建了新的逻辑卷,那么在上面创建一个文件系统。如果对已有的逻辑卷增加空间,使用 resize2fs 命令来增大文件系统来填满逻辑卷。
☉ 在 /etc/fstab 中增加适当的条目以挂载文件系统。
☉ 挂载文件系统。

示例

这个示例展示了怎么用命令行去扩展一个已存在的卷组。它会给 /Staff 文件系统增加大约 50GB 的空间。这将生成一个可用于挂载的文件系统,在 Linux 2.6 内核(及更高)上可即时使用 EXT3 和 EXT4 文件系统。我不推荐你用于任何关键系统,但是这是可行的,我已经成功了好多次;即使是在根(/)文件系统上。是否使用自己把握风险。

警告:仅 EXT3 和 EXT4 文件系统可以在运行和挂载状态下调整大小。许多其它的文件系统,包括 BTRFS 和 ZFS 是不能这样做的。

安装硬盘
如果在系统中现有硬盘上的卷组中没有足够的空间可以增加,那么可能需要去增加一块新的硬盘,然后创建空间增加到逻辑卷中。首先,安装物理硬盘,然后,接着执行后面的步骤。

从硬盘上创建物理卷
首先需要去创建一个新的物理卷(PV)。使用下面的命令,它假设新硬盘已经分配为 /dev/hdd。

pvcreate /dev/hdd

在新硬盘上创建一个任意分区并不是必需的。创建的物理卷将被逻辑卷管理器识别为一个新安装的未处理的磁盘或者一个类型为 83 的 Linux 分区。如果你想去使用整个硬盘,创建一个分区并没有什么特别的好处,而且元数据所用的磁盘空间也能用做 PV 的一部分使用。

增加物理卷到已存在的卷组
在这个示例中,我将使用一个新的物理卷来扩展一个已存在的卷组。在物理卷已经创建之后,扩展已存在的卷组(VG)去包含新 PV 的空间。在这个示例中,已存在的卷组命名为:MyVG01。

vgextend /dev/MyVG01 /dev/hdd

扩展逻辑卷
首先,在卷组中从已存在的空余空间中创建逻辑卷。下面的命令创建了一个 50 GB 大小的 LV。这个卷组的名字为 MyVG01,然后,逻辑卷的名字为 Stuff。

lvcreate -L +50G --name Stuff MyVG01

扩展文件系统
如果你使用了 -r 选项,扩展逻辑卷也将扩展器文件系统。如果你不使用 -r 选项,该操作不行单独执行。下面的命令在新调整大小的逻辑卷中调整了文件系统大小。

resize2fs /dev/MyVG01/Stuff

你也可以去检查并校验调整大小的卷是否正确。你可以使用 df、lvs 和 vgs 命令去做这些工作。

提示

过去几年来,我学习了怎么去做让逻辑卷管理更加容易的一些知识,希望这些提示对你有价值。

◈ 除非你有一个明确的原因去使用其它的文件系统外,推荐使用可扩展的文件系统。除了 EXT2、3、和 4 外,并不是所有的文件系统都支持调整大小。EXT 文件系统不但速度快,而且它很高效。在任何情况下,如果默认的参数不能满足你的需要,它们(指的是文件系统参数)可以通过一位知识丰富的系统管理员来调优它。
◈ 使用有意义的卷和卷组名字。
◈ 使用 EXT 文件系统标签

我知道,像我一样,大多数的系统管理员都抗拒逻辑卷管理。我希望这篇文章能够鼓励你至少去尝试一个 LVM。如果你能那样做,我很高兴;因为,自从我使用它之后,我的硬盘管理任务变得如此的简单。

CentOS 7下如何配置KVM虚拟化环境

KVM是一个开源的虚拟化模块,是基于硬件的完全虚拟化,所以需要硬件的支持。废话不多说,操作步骤如下:

0x01 硬件检测

$ egrep '(vmx|svm)' /proc/cpuinfo   ##检测CPU是否支持虚拟化

如果输出的结果包含vmx,则是Intel处理器虚拟机技术标志;如果包含SVM,则是AMD处理器虚拟机技术的标志;如果什么都没有,则需要在BISO中开启VT(Virtual Technolege)功能。

0x02 软件包安装

$ yum -y install kvm python-virtinst libvirt tunctl bridge-utils virt-manager qemu-kvm-tools virt-viewer

安装完成后,检查kvm模块有没有加载上

$  lsmod |grep kvm   ##有输出类似下面结果则正常

kvm_intel 148081 0

kvm 461126 1 kvm_intel

0x03 创建网桥

1、创建br0文件

$ cp ifcfg-eth0 ifcfg-br0  ##如果管理IP配置再eth0,则这就复制一个br0即可

2、编辑br0

TYPE=Bridge

BOOTPROTO=static

DEFROUTE=yes

PEERDNS=yes

PEERROUTES=yes

DEVICE=br0

ONBOOT=yes

IPADDR=192.168.2.10

NETMASK=255.255.255.0

GATEWAY=192.168.2.1

3、编辑eth0

TYPE=Ethernet

BOOTPROTO=none

NM_CONTROLLED=no

NAME=eth0

DEVICE=eth0

BRIDGE=br0

ONBOOT=yes

配置完成后重启下机器,查看下网桥状态

$ brctl show

bridge name bridge id STP enabled interfaces

br0 8000.90b11c5a7c89 no eth0

virbr0 8000.5254000908ac yes virbr0-nic

至此,KVM环境配置完成。可以创建虚拟机尽情玩耍了。

修改kvm虚拟机镜像文件大小

1、查看kvm镜像的格式信息

[root@base-12-199 bakup]# qemu-img info 10.1.12.201_base-12-201
image: 10.1.12.201_base-12-201
file format: qcow2
virtual size: 200G (214748364800 bytes)
disk size: 88G
cluster_size: 65536

2、给镜像增加100G空间

[root@base-12-199 bakup]# qemu-img resize 10.1.12.201_base-12-201 +100G
Image resized.
[root@base-12-199 bakup]# qemu-img info 10.1.12.201_base-12-201
image: 10.1.12.201_base-12-201
file format: qcow2
virtual size: 300G (322122547200 bytes)
disk size: 88G
cluster_size: 65536

3、给镜像减少100G空间

qcow2格式降低空间

[root@base-12-199 bakup]# qemu-img resize 10.1.12.201_base-12-201-- -100G
This image format does not support resize

由于qcow2格式不支持空间减少,所以要使用raw格式。

[root@base-12-199 bakup]# qemu-img convert -O raw 10.1.12.201_base-12-201 10.1.12.201_base-12-201.raw

查看镜像信息

[root@base-12-199 bakup]# qemu-img info 10.1.12.201_base-12-201.raw
image: 10.1.12.201_base-12-201.raw
file format: raw
virtual size: 300G (429496729600 bytes)
disk size: 88G
[root@base-12-199 bakup]# qemu-img resize 10.1.12.201_base-12-201.raw -- 100G
Image resized.
[root@base-12-199 bakup]# qemu-img info 10.1.12.201_base-12-201.raw
image: 10.1.12.201_base-12-201.raw
file format: raw
virtual size: 200G (214748364800 bytes)
disk size: 88G

转换为qcow2

[root@base-12-199 bakup]# qemu-img convert -O qcow2 10.1.12.201_base-12-201.raw 10.1.12.201_base-12-201.qcow2
[root@base-12-199 bakup]# qemu-img info 10.1.12.201_base-12-201.qcow2
image: 10.1.12.201_base-12-201.qcow2
file format: qcow2
virtual size: 200G (214748364800 bytes)
disk size: 88G

Centos7.4安装kvm虚拟机(使用virt-manager管理)

之前介绍了使用WebVirtMgr或Openstack来部署及管理kvm虚拟机,下面简单介绍centos7.4下使用virt-manager部署及管理kvm虚拟机的做法:

0)KVM是什么

KVM(Kernel-based Virtual Machine, 即内核级虚拟机) 是一个开源的系统虚拟化模块。它使用Linux自身的调度器进行管理,所以相对于Xen,其核心
源码很少。目前KVM已成为学术界的主流VMM之一,它包含一个为处理器提供底层虚拟化 可加载的核心模块kvm.ko(kvm-intel.ko 或 kvm-amd.ko)。kvm
还需要一个经过修改的QEMU 软件(qemu-kvm),作为虚拟机上层控制和界面。KVM的虚拟化需要硬件支持(如 Intel VT技术或者AMD V技术)。是基于硬件的
完全虚拟化。 KVM可以运行多个其本身运行未改动的镜像的虚拟机,例如Windows,Mac OS X ,每个虚拟机都有各自的虚拟硬件,比如网卡、硬盘核图形适配
器等。

KVM和QEMU的关系
QEMU是个独立的虚拟化解决方案,从这个角度它并不依赖KVM。而KVM是另一套虚拟化解决方案,不过因为这个方案实际上只实现了内核中对处理器(Intel VT)
, AMD SVM)虚拟化特性的支持,换言之,它缺乏设备虚拟化以及相应的用户空间管理虚拟机的工具,所以它借用了QEMU的代码并加以精简,连同KVM一起构成了
另一个独立的虚拟化解决方案:KVM+QEMU。

1)kvm相关安装包及其作用

qemu-kvm          主要的KVM程序包
python-virtinst   创建虚拟机所需要的命令行工具和程序库
virt-manager      GUI虚拟机管理工具
virt-top          虚拟机统计命令
virt-viewer       GUI连接程序,连接到已配置好的虚拟机
libvirt           C语言工具包,提供libvirt服务
libvirt-client    虚拟客户机提供的C语言工具包
virt-install      基于libvirt服务的虚拟机创建命令
bridge-utils      创建和管理桥接设备的工具

2)centos7安装VNC环境

请参考:http://www.cnblogs.com/kevingrace/p/5821450.html

3)安装kvm

1)检查cpu是否支持虚拟化
[root@kevin ~]# grep vmx /proc/cpuinfo
如果有vmx信息输出,就说明支持VT;如果没有任何的输出,说明你的cpu不支持,将无法使用KVM虚拟机。


2)确保BIOS里开启虚拟化功能,即查看是否加载KVM模块
[root@kevin ~]# lsmod | grep kvm
kvm_intel             170086  0
kvm                   566340  1 kvm_intel
irqbypass              13503  1 kvm
=========================================================
如果没有加载,运行以下命令:
[root@kevin ~]# modprobe kvm
[root@kevin ~]# modprobe kvm-intel
[root@kevin ~]# lsmod | grep kvm
kvm_intel             170086  0
kvm                   566340  1 kvm_intel
irqbypass              13503  1 kvm
=========================================================

内核模块导出了一个名为/dev/kvm的设备,这个设备将虚拟机的的地址空间独立于内核或者任何应用程序的地址空间。
[root@kevin ~]# ll /dev/kvm
crw-rw-rw-. 1 root kvm 10, 232 1月  29 11:56 /dev/kvm


3)桥接网络
如果没有brctl命令(用来管理网桥的工具),则需要安装bridge-utils ,
[root@kevin ~]# yum -y install bridge-utils
[root@kevin ~]# systemctl restart network

配置KVM的网桥模式
[root@kevin ~]# cd /etc/sysconfig/network-scripts/
[root@openstack network-scripts]# cp ifcfg-eno1 ifcfg-br0
[root@openstack network-scripts]# cat ifcfg-br0
TYPE="Bridge"                                        //这一行修改为Bridge
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="none"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="no"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="br0"                                           //修改设备名称为br0
#UUID="fdbad04f-dae3-440e-8a8b-01d6a7bc9fe0"         //这一行注释
DEVICE="br0"                                         //修改设备为br0
ONBOOT="yes"
IPADDR="192.168.10.210"
PREFIX="24"
GATEWAY="192.168.10.1"
DNS1="8.8.8.8"

[root@openstack network-scripts]# cat ifcfg-eno1
TYPE="Ethernet"
BRIDGE=br0                                           //添加这一行
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="none"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="no"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="eno1"
UUID="fdbad04f-dae3-440e-8a8b-01d6a7bc9fe0"
DEVICE="eno1"
ONBOOT="yes"
#IPADDR="192.168.10.210"                          //注释掉这几行
#PREFIX="24"
#GATEWAY="192.168.10.1"
#DNS1="8.8.8.8"

重启网卡服务
[root@openstack network-scripts]# systemctl restart network

查看网卡
[root@openstack network-scripts]# brctl show
bridge name bridge id   STP enabled interfaces
br0   8000.0894ef518b22 no    eno1
virbr0    8000.52540095d7c2 yes   virbr0-nic

查看ip信息
[root@openstack network-scripts]# ifconfig |head -20
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.210  netmask 255.255.255.0  broadcast 192.168.10.255
        inet6 fe80::a94:efff:fe51:8b22  prefixlen 64  scopeid 0x20<link>
        ether 08:94:ef:51:8b:22  txqueuelen 1000  (Ethernet)
        RX packets 856  bytes 52981 (51.7 KiB)
        RX errors 0  dropped 2  overruns 0  frame 0
        TX packets 120  bytes 23450 (22.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        ether 08:94:ef:51:8b:22  txqueuelen 1000  (Ethernet)
        RX packets 10077  bytes 793083 (774.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1184  bytes 228415 (223.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 16 

eno2: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 08:94:ef:51:8b:23  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)


[root@openstack network-scripts]# ping www.baidu.com
PING www.a.shifen.com (61.135.169.121) 56(84) bytes of data.
64 bytes from 61.135.169.121 (61.135.169.121): icmp_seq=1 ttl=55 time=2.08 ms
64 bytes from 61.135.169.121 (61.135.169.121): icmp_seq=2 ttl=55 time=1.80 ms
........


4)安装libvirt及kvm
libvirt是管理虚拟机的API库,不仅支持KVM虚拟机,也可以管理Xen等方案下的虚拟机。
[root@kevin ~]#  yum -y install libcanberra-gtk2 qemu-kvm.x86_64 qemu-kvm-tools.x86_64  libvirt.x86_64 libvirt-cim.x86_64 libvirt-client.x86_64
libvirt-java.noarch  libvirt-python.x86_64 libiscsi-1.7.0-5.el6.x86_64  dbus-devel  virt-clone tunctl virt-manager libvirt libvirt-python python-virtinst

由于要用virt-manager图形化安装虚拟机,所以还需要安装X-window(这个在前面部署VNC环境里就已经安装了)
[root@kevin ~]# yum groupinstall "X Window System"

安装中文字符,解决界面乱码问题
[root@kevin ~]# yum install -y dejavu-lgc-sans-fonts
[root@kevin ~]# yum groupinstall -y "Fonts"

启用libvirt
[root@kevin ~]# systemctl enable libvirtd
[root@kevin ~]# systemctl start libvirtd

4)使用virt-manager管理kvm(通过VNC连接服务器)

提前将ISO系统镜像存放到服务器的一个目录里,比如/data/iso

[root@openstack ~]# mkdir /data/iso
[root@openstack ios]# ll
总用量 3356384
-rw-r--r--. 1 qemu qemu 3436937216 1月  29 11:41 win-server2008_R2.iso

未分类

未分类

未分类

未分类

未分类

未分类

5)解决KVM虚拟机在使用vnc连接时鼠标不同步的问题

在VNC界面中感觉virt-manager管理的虚拟机界面总是鼠标跟不上,指到哪儿也看不出来,界面上一直显示press control_l+a/t_l来移动鼠标!十分郁闷!
想要修改鼠标和宿主机界面同步方法如下:

[root@openstack ~]# cd /etc/libvirt/qemu
[root@openstack qemu]# ls
networks  test-win2008.xml
[root@openstack qemu]# cp test-win2008.xml /opt/
[root@openstack qemu]# vim test-win2008.xml               //在<devices>标签中添加下面这段配置
<devices>   
......                                      
  <input type='tablet' bus='usb'/>                        //即添加这句话即可!
......    
</devices>

[root@openstack qemu]# virsh define /etc/libvirt/qemu/test-win2008.xml
定义域 test-win2008(从 /etc/libvirt/qemu/test-win2008.xml)

然后重启虚拟机后,发现虚拟机中的鼠标就会好事了,打开VNC查看虚拟机界面后默认情况下虚拟机中的鼠标指针和实体机的鼠标指针就是重合的,且两者运动速度也是同步的,
这下就彻底解决了鼠标指针漂移/不同步的情况了!

6)重定向USB设备

(即将宿主机上的USB设备指定到目标虚拟机上)。适合挂载银行前置机设备!

先将USB设备插到宿主机上,接着在virt-manager界面里打开虚拟机,然后虚拟机界面上栏里打开”虚拟机”->”重定向USB设备(R)”。(特别注意:如下选择将usb设备挂载到虚拟机上后,不要关闭这个挂载界面,否则usb挂载动作就会结束!)

未分类

未分类

查看虚拟机,发现指定的USB设备已经挂载到该虚拟机上了!

未分类

如果要卸载该虚拟机上挂载的这个USB设备,即在”重定向设备usb(R)”里将这个USB设备去掉,然后”确定”即可!

未分类

未分类

在kubernetes集群中创建redis主从多实例

继续使用上次实验环境 https://www.58jb.com/html/180.html ,因为环境中已经配置好flannel网络了,接下要就是慢慢在此环境中创建一些实例。因为只是搭建简单的环境是比较容易上手,随便网上一搜可能就出来了。但是要自己去从头一步一步跑起项目,还是基于真实的项目来跑的话还是需要自己多研究,当然目前还是止于使用阶段,要深入还得多看资料。

未分类

本次要记录的是自己从创建镜像到实现redis主从示例,为什么要自己制作镜像呢?其实就是为了自己可以学多点,毕竟网上的都人别人做好的,你自己没做过根本不知道制作过程中会遇到什么。为什么有些镜像大,有些镜像比较小。这都是在哪里做的优化?
本次直接使用alpine基础镜像,因为它小,加上个redis也就11M左右的镜像,如果使用centos可能就大点了,这里先不做存储的方便,只是简单的实现redis-master和redis-slave之间的自动发现。
为了自己好区分我直接制作两个镜像,一个为redis-master专门用来跑master服务的,一个为redis-slave专门跑slave实例的。其实可以只用一个,只需分两个配置文件或者是两个不同的启动方法,这里不做这个介绍。

redis-slave镜像制作

需要二个文件:Dockerfile、run.sh

Dockerfile内容如下:

FROM alpine:3.4  
RUN apk add --no-cache redis sed bash  
COPY run.sh /run.sh  
CMD [ "/run.sh" ]  
ENTRYPOINT [ "bash" ]  

其实默认可以不使用redis.conf文件的,如果需要就在上面的Dockerfile文件也加入,这里目前也处于测试,先不加配置。

run.sh启动脚本,也就是一行文件。

[root@k8s-node1 slave]# cat run.sh 
#/bin/sh 
redis-server --slaveof ${REDIS_MASTER_SERVICE_HOST} ${REDIS_MASTER_SERVICE_PORT} 

打包成镜像传到内网仓库:

docker build -t redis-slave . 
docker tag redis-slave reg.docker.tb/harbor/redis-slave 
docker push reg.docker.tb/harbor/redis-slave 

redis-master镜像制作

跟上面的配置差不多一样,只是启动时的命令有点不而已。
Dockerfile内容如下:

FROM alpine:3.4  
RUN apk add --no-cache redis sed bash  
COPY run.sh /run.sh  
CMD [ "/run.sh" ]  
ENTRYPOINT [ "bash" ]  

其实默认可以不使用redis.conf文件的,如果需要就在上面的Dockerfile文件也加入,这里目前也处于测试,先不加配置。

run.sh启动脚本,也就是一行文件。

[root@k8s-node1 master]# cat run.sh 
#/bin/sh 
redis-server

打包成镜像传到内网仓库:

docker build -t redis-master . 
docker tag redis-master reg.docker.tb/harbor/redis-master 
docker push reg.docker.tb/harbor/redis-master 

创建kube的配置文件yaml

创建一个master-service.yaml 来统一Master的入口,同时会自动关联到labels为redis-master的所有Pod,这里要注意Service要优于pod启动,不然无法通过环境变量把配置信息写入到pod中。

apiVersion: v1 
kind: Service 
metadata: 
  name: redis-master 
  labels: 
    name: redis-master 
spec: 
  ports: 
  - port: 6379 
    targetPort: 6379 
  selector: 
    name: redis-master 

redis-master.yaml 以rc的方式来创建一个master容器,它会保证容器的副本数量。

apiVersion: v1 
kind: ReplicationController 
metadata: 
  name: redis-master 
  labels: 
    name: redis-master 
spec: 
  replicas: 1 
  selector: 
    name: redis-master 
  template: 
    metadata: 
      labels: 
        name: redis-master 
    spec: 
      containers: 
      - name: master 
        image: reg.docker.tb/harbor/redis-master 
        ports: 
        - containerPort: 6379 

redis-slave-service.yaml跟上面的Service一样,都是管理redis-slave所有容器的,此服务创建后会把所有的slave实例的容器分配一个集群IP.

apiVersion: v1 
kind: Service 
metadata: 
  name: redis-slave 
  labels: 
    name: redis-slave 
spec: 
  ports: 
  - port: 6379 
  selector: 
    name: redis-slave 

redis-slave.yaml 同样是以RC的方式来创建redis-slave实例,这里的数量为两个。需要注意的是Image这里指定是刚才打包的镜像名。

apiVersion: v1 
kind: ReplicationController 
metadata: 
  name: redis-slave 
  labels: 
    name: redis-slave 
spec: 
  replicas: 2 
  selector: 
    name: redis-slave 
  template: 
    metadata: 
      labels: 
        name: redis-slave 
    spec: 
      containers: 
      - name: worker 
        image: reg.docker.tb/harbor/redis-slave 
        env: 
        - name: GET_HOSTS_FROM 
          value: env 
#value: dns
        ports: 
        - containerPort: 6379 

注意:要实现master和slave服务自动发现,需要配置它们之间的对应关系。Kubernetes有两种方法就是环境变量ENV和DNS记录解析,因为我的实验环境没有使用这个DNS来解析,所以只能使用ENV环境变量。

上面的redis-slave.yaml 就是使用了env环境变量。

接下来创建实例吧:

master: 
kubectl create -f redis-service.yaml 
kubectl create -f redis-master.yaml 

slave: 
kubectl create -f redis-slave-service.yaml 
kubectl create -f redis-slave.yaml 

查看启动的效果:

[root@k8s-master redis-pod]# kubectl get all -o wide 
NAME                    READY     STATUS    RESTARTS   AGE       IP           NODE 
po/redis-master-qpzlh   1/1       Running   0          1h        172.21.2.2   k8s-node1 
po/redis-slave-2rwk5    1/1       Running   0          1h        172.21.2.3   k8s-node1 
po/redis-slave-6tf2f    1/1       Running   0          1h        172.21.8.3   k8s-node2 

NAME              DESIRED   CURRENT   READY     AGE       CONTAINERS   IMAGES                              SELECTOR 
rc/redis-master   1         1         1         1h        master       reg.docker.tb/harbor/redis-master   name=redis-master 
rc/redis-slave    2         2         2         1h        worker       reg.docker.tb/harbor/redis-slave    name=redis-slave 

NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE       SELECTOR 
svc/kubernetes     ClusterIP   10.254.0.1       <none>        443/TCP    3d        <none> 
svc/redis-master   ClusterIP   10.254.152.116   <none>        6379/TCP   1h        name=redis-master 
svc/redis-slave    ClusterIP   10.254.188.60    <none>        6379/TCP   1h        name=redis-slave 

简单的测试master和slave是否可以同步数据:
连接master容器,可以看到有两个Replication中有两个connected_slaves就是从实例,后面跟着的就是IP地址。

[root@k8s-master redis-pod]# kubectl exec -it redis-master-qpzlh -- /bin/bash 
bash-4.3# redis-cli                                                                                                                                          
127.0.0.1:6379> info 
# Server 
redis_version:3.2.11 
redis_git_sha1:535782f7 
redis_git_dirty:0 
redis_build_id:80ce8a1f388ac530 
redis_mode:standalone 
os:Linux 4.4.113-1.el7.elrepo.x86_64 x86_64 
arch_bits:64 
multiplexing_api:epoll 
gcc_version:5.3.0 
process_id:5 
run_id:6629f021c2e5bd972fff05c84566cc92c7e59be0 
tcp_port:6379 
uptime_in_seconds:7105 
uptime_in_days:0 

...... 
# Clients 
connected_clients:2 
client_longest_output_list:0 
client_biggest_input_buf:0 
blocked_clients:0 
...... 

# Replication 
role:master 
connected_slaves:2 
slave0:ip=172.21.2.3,port=6379,state=online,offset=5276,lag=1 
slave1:ip=172.21.8.0,port=6379,state=online,offset=5276,lag=1 
master_repl_offset:5276 
repl_backlog_active:1 
repl_backlog_size:1048576 
repl_backlog_first_byte_offset:2 
repl_backlog_histlen:5275 

连接一个slave容器查看信息,可以看到Replication中role:slave为从实例,master的IP和端口等信息。

[root@k8s-master ~]# kubectl exec -it redis-slave-2rwk5 -- /bin/bash 
bash-4.3# redis-cli                                                                                                                                          
127.0.0.1:6379> info 
# Server 
redis_version:3.2.11 
redis_git_sha1:535782f7 
redis_git_dirty:0 
redis_build_id:80ce8a1f388ac530 
redis_mode:standalone 
os:Linux 4.4.113-1.el7.elrepo.x86_64 x86_64 
arch_bits:64 
multiplexing_api:epoll 
gcc_version:5.3.0 
process_id:5 
run_id:d48c42ea0a83babda663837ed5a6f9ef5a3ff9bf 
tcp_port:6379 
uptime_in_seconds:3957 
uptime_in_days:0 

...... 
# Replication 
role:slave 
master_host:10.254.152.116 
master_port:6379 
master_link_status:up 
master_last_io_seconds_ago:7 
master_sync_in_progress:0 
slave_repl_offset:5542 
slave_priority:100 
slave_read_only:1 
connected_slaves:0 
master_repl_offset:0 
repl_backlog_active:0 
repl_backlog_size:1048576 
repl_backlog_first_byte_offset:0 
repl_backlog_histlen:0 

此时在Master上创建一个key, 在slave上立马就可以获取到了。

master: 
127.0.0.1:6379> set name swper 
OK 

slave: 
127.0.0.1:6379> get name 
"swper" 

slave上只读: 
127.0.0.1:6379> set test 1 
(error) READONLY You can't write against a read only slave. 

这样就实现了一主多从的效果了,集群的IP现在是外部无法访问的,kubernetes节点中可以相互连通。
其实原本是很简单的实验,我居然在传递参数的时候少了个$号导致一直获取不到。

kubernetes下的Nginx加Tomcat三部曲之三:实战扩容和升级

本章是《kubernetes下的Nginx加Tomcat三部曲系列》的终篇,今天咱们一起在kubernetes环境对下图中tomcat的数量进行调整,再修改tomcat中web工程的源码,并将现有的tomcat的pod全部替换成新代码构建的结果:

未分类

往期章节

  1. kubernetes下的Nginx加Tomcat三部曲之一: http://devops.webres.wang/2018/02/kubernetes%E4%B8%8B%E7%9A%84nginx%E5%8A%A0tomcat%E4%B8%89%E9%83%A8%E6%9B%B2%E4%B9%8B%E4%B8%80%EF%BC%9A%E6%9E%81%E9%80%9F%E4%BD%93%E9%AA%8C/
  2. kubernetes下的Nginx加Tomcat三部曲之二: http://devops.webres.wang/2018/02/kubernetes%E4%B8%8B%E7%9A%84nginx%E5%8A%A0tomcat%E4%B8%89%E9%83%A8%E6%9B%B2%E4%B9%8B%E4%BA%8C%EF%BC%9A%E7%BB%86%E8%AF%B4%E5%BC%80%E5%8F%91/

列举步骤

  1. 在线扩容Tomcat;
  2. 验证扩容结果;
  3. 修改web工程源码;
  4. 构建web工程的新镜像;
  5. 让kubernetes的机器用上web工程的Docker镜像
  6. 在线更新Tomcat的pod的镜像;
  7. 验证更新结果;
  8. Nginx&Tomcat方式和SpringCloud方式扩容对比;

kubernetes环境基本情况

  1. 一个master,一个node;
  2. master的IP地址:192.168.119.148;
  3. node1的IP地址:192.168.119.153;

在线扩容Tomcat

  • 在装好kubectl工具的机器上执行kubectl get pods,查看当前pod情况:
root@master:~# kubectl get pods
NAME                          READY     STATUS    RESTARTS   AGE
ng-59b887b8bc-jzcv5           1/1       Running   3          2d
tomcathost-7f68566795-8pl29   1/1       Running   3          2d
tomcathost-7f68566795-mvg5f   1/1       Running   3          2d
tomcathost-7f68566795-trscg   1/1       Running   3          2d

如上所示,目前是三个tomcat的pod;

  • 执行扩容命令kubectl scale deployment tomcathost –replicas=5,将tomcat的pod从3个增加到5个,如下:
root@master:~# kubectl scale deployment tomcathost --replicas=5
deployment "tomcathost" scaled
root@master:~# kubectl get pods
NAME                          READY     STATUS    RESTARTS   AGE
ng-59b887b8bc-jzcv5           1/1       Running   3          2d
tomcathost-7f68566795-8kf76   1/1       Running   0          18s
tomcathost-7f68566795-8pl29   1/1       Running   3          2d
tomcathost-7f68566795-mvg5f   1/1       Running   3          2d
tomcathost-7f68566795-tp5xp   1/1       Running   0          18s
tomcathost-7f68566795-trscg   1/1       Running   3          2d

tomcat的pod已经增加到5个了,创建时间都只有18秒;

  • 去dashboard页面看以下tomcathost这个deployment的详情,可以看到tomcat数量已经上升到5个了,扩容成功:

未分类

点击上图红框1,再点击tomcathost这个服务,可以看到上图中的详情;
上图红框2中是5个tomcat的pod的容器IP地址;
点击上图红框3中的每个容器名,可以查看容器详情;
上图红框4显示了刚才的扩容事件;

验证扩容结果

nginx服务所在的node机器的IP地址是192.168.119.153,所以在浏览器上访问:http://192.168.119.153:30006/getserverinfo,反复刷新此页面,看到返回的IP地址在不断的更新,都是tomcat所在pod的IP地址,5个都会出现,如下图:

未分类

扩容实战就到这里,接下来我们修改web工程的源码,验证在线升级的能力;

修改web工程源码

tomcat上运行的web工程源码,可以GitHub下载,地址和链接信息如下表所示:

未分类

这个git项目中有多个目录,本次的web工程源码放在k8stomcatdemo,如下图红框所示:

未分类

  • 打开工程中的ServerInfo.java,web接口的源码如下:
@RequestMapping(value = "/getserverinfo", method = RequestMethod.GET)
    public String getUserInfoWithRequestParam(){
        return String.format("server : %s, time : %s", getIPAddr(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
  • 修改返回的字符串内容,在前面加上”From new version,”:
@RequestMapping(value = "/getserverinfo", method = RequestMethod.GET)
    public String getUserInfoWithRequestParam(){
        return String.format("From new version, server : %s, time : %s", getIPAddr(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

构建web工程的新镜像

修改pom.xml中的工程版本,从0.0.1-SNAPSHOT改为0.0.3,如下所示:

<groupId>com.bolingcavalry</groupId>
    <artifactId>k8stomcatdemo</artifactId>
    <version>0.0.3</version>
    <packaging>jar</packaging>
    <name>k8stomcatdemo</name>
  • 在pom.xml所在目录执行mvn clean package -DskipTests docker:build,会编译构建工程,并且在本地制作好镜像文件,如下:
root@maven:/usr/local/work/k8s/k8stomcatdemo# docker images
REPOSITORY                                              TAG                 IMAGE ID            CREATED             SIZE
bolingcavalry/k8stomcatdemo                             0.0.3               5f2d27eafd1b        17 seconds ago      658 MB
bolingcavalry/k8stomcatdemo                             latest              5f2d27eafd1b        17 seconds ago      658 MB

让kubernetes的机器用上web工程的Docker镜像

现在的镜像只存在于开发和构建web工程的电脑上,为了让kubernetes的node机器能用上这个镜像,可以用以下几种方式实现:

1、用docker push命令将本机镜像推送到hub.docker.com网站,这样其他机器都可以通过docker pull命令取得了,我就是用的这种方法,需要在hub.docker.com上注册,下图是正在push中:

未分类

2、用docker save命令导出镜像文件,再用docker load命令导入;
3、kubernetes所在机器安装java和maven环境,将工程在这里编译构建;
4、使用docker私有仓库,例如搭建局域网私有仓库或者阿里云私有仓库,参考 http://blog.csdn.net/boling_cavalry/article/details/78934391

在线更新Tomcat的pod的镜像

  • 在kubernetes上执行以下命令,即可升级镜像,当前的5个tomcat的pod会被销毁,然后用新的镜像创建pod:
kubectl set image deployment/tomcathost tomcathost=bolingcavalry/k8stomcatdemo:0.0.3
  • 控制台提示的信息如下:
root@maven:/usr/local/work/k8s/k8stomcatdemo# kubectl set image deployment/tomcathost tomcathost=bolingcavalry/k8stomcatdemo:0.0.3
deployment "tomcathost" image updated
  • 此刻反复执行kubectl get pod,会看到新容器创建,旧容器正在被销毁,如下:
root@maven:/usr/local/work/k8s/k8stomcatdemo# kubectl get pod
NAME                          READY     STATUS              RESTARTS   AGE
ng-59b887b8bc-jzcv5           1/1       Running             3          2d
tomcathost-6dfc87dc8b-9bkfv   1/1       Running             0          50s
tomcathost-6dfc87dc8b-h6gx4   0/1       ContainerCreating   0          50s
tomcathost-6dfc87dc8b-ht2d8   1/1       Running             0          18s
tomcathost-6dfc87dc8b-pfb56   1/1       Running             0          10s
tomcathost-6dfc87dc8b-x8pnn   1/1       Running             0          14s
tomcathost-7f68566795-8pl29   0/1       Terminating         3          2d
tomcathost-7f68566795-trscg   0/1       Terminating         3          2d

验证更新结果

  • nginx服务所在的node机器的IP地址是192.168.119.153,所以在浏览器上访问:http://192.168.119.153:30006/getserverinfo,可以看到”From new version”这个字符串,如下图:

未分类

去dashboard页面看服务详情,可以看见一系列的缩容和扩容事件,如下图:

未分类

Nginx&Tomcat方式和SpringCloud方式扩容对比

在之前的文章中,我们实战了SpringCloud环境下服务提供方的扩容,由于是“注册&发现”的方式,扩容只需要往SpringCloud环境添加provider机器,不需要做什么设置,请求就会被落到新的provider上,相关实战的地址如下:

  1. Docker下的Spring Cloud三部曲之一: http://blog.csdn.net/boling_cavalry/article/details/79177930

  2. Docker下的Spring Cloud三部曲之二: http://blog.csdn.net/boling_cavalry/article/details/79134497

  3. Docker下的Spring Cloud三部曲之三: http://blog.csdn.net/boling_cavalry/article/details/79192376

在Nginx加Tomcat环境中,如果不是在kubernetes环境,我们增加Tomcat之后需要修改Nginx配置,否则到达Nginx的请求不会被转发到新的机器上去,幸运的是kubernete帮我们解决了这个问题,扩容缩容都只需要控制副本数即可,不用修改Nginx配置了;

doceker-compose虽然可以修改副本数,但是Nginx的配置仍然需要修改,否则新创建的Tomcat容器都有自己的IP地址,Nginx还是感知不到;

至此,kubernetes下的Nginx加Tomcat三部曲就全部结束了,希望能在您的kubernetes实战中有所帮助;

kubernetes下的Nginx加Tomcat三部曲之二:细说开发

本文是《kubernetes下的Nginx加Tomcat三部曲》的第二章,在 http://devops.webres.wang/2018/02/kubernetes%E4%B8%8B%E7%9A%84nginx%E5%8A%A0tomcat%E4%B8%89%E9%83%A8%E6%9B%B2%E4%B9%8B%E4%B8%80%EF%BC%9A%E6%9E%81%E9%80%9F%E4%BD%93%E9%AA%8C/ 一文我们快速部署了Nginx和Tomcat,达到以下效果:

未分类

本文我会详细说明在kubernetes部署上述网络服务时,需要做哪些具体的工作;

列举步骤

需要以下操做才能在kubernetes上部署Nginx加Tomcat的服务:

  1. 开发Tomcat上的web工程和Docker镜像;
  2. 让kubernetes的机器用上web工程的Docker镜像;
  3. 开发Tomcat对应的deployment脚本;
  4. 开发Tomcat对应的service脚本;
  5. 开发Nginx对应的Docker镜像;
  6. 让kubernetes的机器用上Nginx的Docker镜像
  7. 开发Nginx对应的deployment脚本;
  8. 开发Nginx对应的service脚本;
  9. 开发启动上述pod和service的脚本;
  10. 开发停止并清除上述pod和service的脚本

脚本文件下载

本次体验所需的deployment和service资源是通过脚本创建的,这个脚本可以通过以下两种方式中的任意一种下载:

  1. CSDN下载(无法设置免费下载,只能委屈您用掉两个积分了):http://download.csdn.net/download/boling_cavalry/10235034
  2. GitHub下载,地址和链接信息如下表所示:

未分类

这个git项目中有多个目录,本次所需的资源放在k8s_nginx_tomcat_resource,如下图红框所示:

未分类

下到的k8stomcatcluster20180201.tar是个压缩包,复制到可以执行kubectl命令的ubuntu电脑上,然后解压开,是个名为k8stomcatcluster的文件夹;

Spring boot的web工程源码下载

GitHub下载,地址和链接信息如下表所示:

未分类

这个git项目中有多个目录,本次的web工程源码放在k8stomcatdemo,如下图红框所示:

未分类

接下来我们开始实战开发吧;

开发环境

本次实战开发环境的具体信息如下:

  1. 操作系统:Ubuntu16;
  2. Docker版本:17.03.2-ce;
  3. JDK:1.8.0_151;
  4. maven:3.3.3;

Tomcat上的web工程和Docker镜像

web工程用来提供http服务,返回当前机器的IP地址给浏览器,完整源码请参照前面下载的k8stomcatdemo工程,这里我们还是重新创建一次;

1、创建一个springboot工程,pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bolingcavalry</groupId>
    <artifactId>k8stomcatdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>k8stomcatdemo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!--新增的docker maven插件-->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>0.4.12</version>
                <!--docker镜像相关的配置信息-->
                <configuration>
                    <!--镜像名,这里用工程名-->
                    <imageName>bolingcavalry/${project.artifactId}</imageName>
                    <!--TAG,这里用工程版本号-->
                    <imageTags>
                        <imageTag>${project.version}</imageTag>
                    </imageTags>
                    <!--镜像的FROM,使用java官方镜像-->
                    <baseImage>java:8u111-jdk</baseImage>
                    <!--该镜像的容器启动后,直接运行spring boot工程-->
                    <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
                    <!--构建镜像的配置信息-->
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

可以看到这是个普通的springboot的web工程,唯一不同的是多了一个maven插件:docker-maven-plugin,使用该插件我们可以将maven工程制作成docker镜像;

2、整个工程只有一个Controller,开通一个http接口,将当前服务器IP地址返回,源码如下:

@RestController
public class ServerInfo {

    @RequestMapping(value = "/getserverinfo", method = RequestMethod.GET)
    public String getUserInfoWithRequestParam(){
        return String.format("server : %s, time : %s", getIPAddr(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }

    /**
     * 获取本机IP地址
     * @return
     */
    private static String getIPAddr(){
        String hostAddress = null;
        try{
            InetAddress address = InetAddress.getLocalHost();
            hostAddress = address.getHostAddress();
        }catch (UnknownHostException e){
            e.printStackTrace();
        }

        return hostAddress;
    }
}

以上就是工程的关键源码;

3、在pom.xml所在目录执行mvn clean package -DskipTests docker:build,会编译构建工程,并且在本地制作好镜像文件,如下:

root@maven:/usr/local/work/github/blog_demos# docker images
REPOSITORY                                              TAG                 IMAGE ID            CREATED             SIZE
bolingcavalry/k8stomcatdemo                             latest              1d41d9980a0b        43 hours ago        658 MB
bolingcavalry/k8stomcatdemo                             0.0.1-SNAPSHOT      c91bc368a729        46 hours ago        658 MB

想了解更多maven制作docker镜像的细节,请看 http://blog.csdn.net/boling_cavalry/article/details/78872020

让kubernetes的机器用上web工程的Docker镜像

现在的镜像只存在于开发和构建web工程的电脑上,为了让kubernetes的node机器能用上这个镜像,可以用以下几种方式实现:

  1. 用docker push命令将本机镜像推送到hub.docker.com网站,这样其他机器都可以通过docker pull命令取得了,我就是用的这种方法,需要在hub.docker.com上注册;
  2. 用docker save命令导出镜像文件,再用docker load命令导入;
  3. kubernetes所在机器安装java和maven环境,将工程在这里编译构建;
  4. 使用docker私有仓库,例如搭建局域网私有仓库或者阿里云私有仓库,参考 http://blog.csdn.net/boling_cavalry/article/details/78934391

Tomcat对应的deployment脚本

用yaml文件将详情配置好,再用kubectl命令执行这个配置就能创建pod,这个web应用镜像的配置文件名为tomcat.yaml,内容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: tomcathost
spec:
  replicas: 3
  template:
    metadata:
     labels:
       name: tomcathost
    spec:
     containers:
     - name: tomcathost
       image: bolingcavalry/k8stomcatdemo:0.0.1-SNAPSHOT
       tty: true
       ports:
       - containerPort: 8080

将上述脚本的几个关键点列举一下:

  1. version用extensions/v1beta1;
  2. kind用Deployment,支持升级镜像和滚动升级;
  3. 使用的镜像bolingcavalry/k8stomcatdemo:0.0.1-SNAPSHOT,是我从本地push到hub.docker.com上去的;
  4. 创建的容器对外暴露了8080端口;

Tomcat对应的service脚本

创建了tomcat的pod之后,为了能在kubernetes环境中给其他service使用,需要将这些pod包装为service,这里是通过tomcat-svc.yaml文件来配置的,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: tomcathost
spec:
  type: ClusterIP
  ports:
       - port: 8080
  selector:
    name: tomcathost

将上述脚本的几个关键点列举一下:

  1. 服务对应的pod是tomcathost;
  2. type用ClusterIP,为内部service调用提供统一IP地址;
  3. 服务对外暴露了pod的8080端口;

Nginx对应的Docker镜像

  • 定制的Nginx镜像和Nginx官方镜像相比,唯一的区别就是nginx.conf文件不同,我们用的nginx.conf内容如下:
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    #include /etc/nginx/conf.d/*.conf;

    upstream tomcat_client {
         server tomcathost:8080;
    } 

    server {
        server_name "";
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        location / {
            proxy_pass http://tomcat_client;
            proxy_redirect default;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

以上配置中,新增的upstream对应的IP地址是tomcathost,这是tomcat的service名称,在Nginx运行的时候,通过tomcathost就能访问到tomcat的Pod;

  • 制作Docker镜像的Dockerfile文件内容如下,每行都有注释就不再多说了:
# First docker file from bolingcavalry
# VERSION 0.0.1
# Author: bolingcavalry

#基础镜像
FROM nginx:stable

#作者
MAINTAINER BolingCavalry <[email protected]>

#定义工作目录
ENV WORK_PATH /etc/nginx

#定义conf文件名
ENV CONF_FILE_NAME nginx.conf

#删除原有配置文件
RUN rm $WORK_PATH/$CONF_FILE_NAME

#复制新的配置文件
COPY ./$CONF_FILE_NAME $WORK_PATH/

#给shell文件赋读权限
RUN chmod a+r $WORK_PATH/$CONF_FILE_NAME

将nginx.conf和Dockerfile放在同一个目录,然后执行命令docker build -t bolingcavalry/nginx-with-tomcat-host:0.0.1 .,就能构建镜像文件了,如下:

root@maven:/usr/local/work/nginx# docker build -t bolingcavalry/nginx-with-tomcat-host:0.0.1 .
Sending build context to Docker daemon 14.51 MB
Step 1/7 : FROM nginx:stable
 ---> dfe062ee1dc8
Step 2/7 : MAINTAINER BolingCavalry <[email protected]>
 ---> Using cache
 ---> 93f4bf154c55
Step 3/7 : ENV WORK_PATH /etc/nginx
 ---> Using cache
 ---> d0158757fc9c
Step 4/7 : ENV CONF_FILE_NAME nginx.conf
 ---> Using cache
 ---> 7a18a8b417d6
Step 5/7 : RUN rm $WORK_PATH/$CONF_FILE_NAME
 ---> Using cache
 ---> f6f27d25539d
Step 6/7 : COPY ./$CONF_FILE_NAME $WORK_PATH/
 ---> Using cache
 ---> 33075a2b0379
Step 7/7 : RUN chmod a+r $WORK_PATH/$CONF_FILE_NAME
 ---> Using cache
 ---> 58ce530e160b
Successfully built 58ce530e160b

让kubernetes的机器用上Nginx的Docker镜像

这一步和之前的web工程的镜像放到kubernetes一样,有多种方式,我用的还是通过docker push推送到hub.docker.com网站,再在kubernetes上pull下来;

Nginx对应的deployment脚本

用yaml文件将详情配置好,再用kubectl命令执行这个配置就能创建pod,这个web应用镜像的配置文件名为nginx.yaml,内容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: ng
spec:
  replicas: 1
  template:
    metadata:
     labels:
      name: ng
    spec:
     containers:
     - name: ng
       image: bolingcavalry/nginx-with-tomcat-host:0.0.1
       ports:
       - containerPort: 80

以上配置中有几点要注意:

  1. 使用镜像是刚才创建的nginx镜像bolingcavalry/nginx-with-tomcat-host:0.0.1;
  2. pod容器创建后,对外暴露80端口;

Nginx对应的service脚本

通过service定义的yaml文件nginx-svc.yam,将前面创建的nginx的pod包装为service:

apiVersion: v1
kind: Service
metadata:
  name: ng
spec:
  type: NodePort
  ports:
       - port: 80
         nodePort: 30006
  selector:
    name: ng

以上配置中有几点要注意:

  1. type使用NodePort,这样可以通过节点机器的IP访问此service;
  2. 将节点机器的30006端口和pod的80端口绑定,因此,外部访问节点IP+30006端口就能访问到此Nginx服务了;

启动上述pod和service的脚本

接下来我们创建一个shell脚本start_all.sh,将上述的tomcat和nginx的pod以及service全部创建和启动:

kubectl create -f tomcat.yaml
kubectl create -f tomcat-svc.yaml
kubectl create -f nginx.yaml
kubectl create -f nginx-svc.yaml
echo ""
echo "nginx and tomcat running now"

如上所示,通过kubectl create -f加文件名,就能创建好yaml文件中定义的pod和service;

停止并清除上述pod和service的脚本

创建一个shell脚本stop_all.sh,能够将上述的tomcat和nginx的pod以及service全部清除掉:

kubectl delete service tomcathost
kubectl delete deployment tomcathost
kubectl delete service ng
kubectl delete deployment ng
echo "nginx and tomcat stop now"

如上所示,其实就是通过kubectl delete命令将指定的pod和service资源删除;

以上就是在kubernetes搭建整个Nginx加Tomcat环境的所有资源,您就可以用这些像 http://devops.webres.wang/2018/02/kubernetes%E4%B8%8B%E7%9A%84nginx%E5%8A%A0tomcat%E4%B8%89%E9%83%A8%E6%9B%B2%E4%B9%8B%E4%B8%80%EF%BC%9A%E6%9E%81%E9%80%9F%E4%BD%93%E9%AA%8C/ 文中那样去搭建和体验kubernetes下的Nginx加Tomcat;

下一章,我们会在此环境的基础上实战Tomcat服务的扩容,并修改web工程的代码,再创建新的镜像,并且将kubernetes环境下在线升级新的web工程服务;

kubernetes下的Nginx加Tomcat三部曲之一:极速体验

章节介绍

在生产环境中,常用到Nginx加Tomcat的部署方式,如下图:

未分类

从本章开始,我们来实战kubernetes下部署上述Nginx和Tomcat服务,并开发spring boot的web应用来验证环境,整个实战分为以下三篇内容:

  1. 极速体验kubernetes下的nginx加tocmat;
  2. 细说nginx和tomcat镜像的制作;
  3. 实战tomcat server的在线扩容和应用升级;

实战工程介绍

本次实战创建的Pod如下:

  1. 一个Nginx的Pod,负责转发web请求到Tomcat;
  2. 三个Tomcat的Pod,上面部署了web应用,收到Nginx转发的请求后,返回的内容是当前Pod的IP地址;

准备kubernetes环境

本次实战需要可用的kubernetes环境,您可以参考以下文章进行快速搭建:

  1. http://blog.csdn.net/boling_cavalry/article/details/78762829
  2. http://blog.csdn.net/boling_cavalry/article/details/78764915

如何执行kubectl命令

实战中,需要在一台ubuntu电脑上安装kubectl工具,然后连接到kubernetes环境执行各种命令,kubectl工具的安装步骤可以参照这篇文章: http://blog.csdn.net/boling_cavalry/article/details/79223091

脚本文件下载

本次体验所需的deployment和service资源是通过脚本创建的,这个脚本可以通过以下两种方式中的任意一种下载:

  1. CSDN下载(无法设置免费下载,只能委屈您用掉两个积分了):http://download.csdn.net/download/boling_cavalry/10235034
  2. GitHub下载,地址和链接信息如下表所示:

未分类

这个git项目中有多个目录,本次所需的资源放在k8s_nginx_tomcat_resource,如下图红框所示:

未分类

下到的k8stomcatcluster20180201.tar是个压缩包,复制到可以执行kubectl命令的ubuntu电脑上,然后解压开,是个名为k8stomcatcluster的文件夹;

执行脚本文件下载

  1. 进入解压好的k8stomcatcluster目录;
  2. 执行命令chmod a+x *.sh,给shell脚本赋可执行权限;3.
    执行命令start_all.sh,创建本次实战的资源,页面输出如下信息:
root@maven:/usr/local/work/k8s/k8stomcatcluster# ./start_all.sh 
deployment "tomcathost" created
service "tomcathost" created
deployment "ng" created
service "ng" created

nginx and tomcat running now

验证服务已经启动

  • 先去kubernetes的管理页面看一下服务是否启动,如下图,名为ng、tomcathost的两个服务都已经启动:

未分类

  • 点击tomcathost服务,看到详情信息,里面有pod的情况,如下图:

未分类

  • 上图中显示tomcathost是在node1创建的,我的node1机器的IP地址是192.168.119.153,所以在浏览器输入:
    http://192.168.119.153:30006/getserverinfo

  • 在浏览器看到的信息如下图所示,机器Tomcat所在机器的IP地址和当前时间:

未分类

  • 多次刷新页面,能看到这三个IP地址:10.42.38.128、10.42.184.35、10.42.127.135,这就是三个Tomcat Pod的地址,Pod信息如下图红框所示:

未分类

  • 执行k8stomcatcluster目录下的stop_all.sh脚本,可以将前面创建的所有service,deployment资源删除;

  • 至此,我们已经在kubernetes下简单体验了Nginx加Tomcat的网站结构,接下来的章节,我们一起来细看如何在kubernetes下创建整个环境;

k8s与监控–引入traefik做后端服务的反代

前言

对于监控这块,我们基于prometheus实现,当然做了大量的优化,包括前面所讲到的配置接口化。我们整个监控的UI部分,没有采用社区流行的grafana,而是自己实现了一套。我们后端的服务按照功能拆分了几大块,例如拓扑,网络流量,配置,元数据等等。拆分的好处就是可以解耦,各个模块功能的升级不影响其他模块。但是对于前端来说,只暴露一个入口,引入一个反代即可。
刚开始选用了nginx,后期由于要加入鉴权的功能,nginx就不能满足我们的需求了。这个时候基本上需求就变为选择一个可编程的反代。当然我在做电商的时候,我们经常采用openresty,结合nginx和lua,可以实现。而且社区基于openresty实现了kong和orange等api网关。
但是考虑我们的场景,整个项目并没有特别高的并发和性能要求,而且我们团队最熟悉的是golang和python。所以选择了traefik。我们可以在后期,可以写各种的插件来满足我们的需求。

traefik简介

Træfɪk 是一个为了让部署微服务更加便捷而诞生的现代HTTP反向代理、负载均衡工具。 它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file…) 来自动化、动态的应用它的配置文件设置。

未分类

简单总结一下我认为traefik的特点:

  1. 官方测试traefik有nginx 85%的性能。这个性能对于一般项目足够了,换来的是强大的编程能力,来从容应对各种需求。而且golang编写,无依赖。
  2. 配置热更新,支持多种后端。
  3. 支持集群模式
  4. 提供了一个web UI

结合项目写demo

我们的项目目前基本两个需求,鉴权和反代。

配置文件

对于反代。主要讲一些配置相关。我们采用的是file。traefik对这种静态文件支持watcher。依旧无需重启进程。

主配置文件traefik.toml

################################################################
# Global configuration
################################################################

# Enable debug mode
#
# Optional
# Default: false
#
# debug = true

# Log level
#
# Optional
# Default: "ERROR"
#
# logLevel = "ERROR"

# Entrypoints to be used by frontends that do not specify any entrypoint.
# Each frontend can specify its own entrypoints.
#
# Optional
# Default: ["http"]
#
# defaultEntryPoints = ["http", "https"]

# Entrypoints definition
#
# Optional
# Default:
[entryPoints]
    [entryPoints.http]
    address = ":8000"

# Traefik logs
# Enabled by default and log to stdout
#
# Optional
#
# [traefikLog]

# Sets the filepath for the traefik log. If not specified, stdout will be used.
# Intermediate directories are created if necessary.
#
# Optional
# Default: os.Stdout
#
# filePath = "log/traefik.log"

# Format is either "json" or "common".
#
# Optional
# Default: "common"
#
# format = "common"

# Enable access logs
# By default it will write to stdout and produce logs in the textual
# Common Log Format (CLF), extended with additional fields.
#
# Optional
#
# [accessLog]

# Sets the file path for the access log. If not specified, stdout will be used.
# Intermediate directories are created if necessary.
#
# Optional
# Default: os.Stdout
#
# filePath = "/path/to/log/log.txt"

# Format is either "json" or "common".
#
# Optional
# Default: "common"
#
# format = "common"

################################################################
# Web configuration backend
################################################################

# Enable web configuration backend
[web]

# Web administration port
#
# Required
#
address = ":8080"

################################################################
# Docker configuration backend
################################################################

# Enable Docker configuration backend
# [docker]

# Docker server endpoint. Can be a tcp or a unix socket endpoint.
#
# Required
# Default: "unix:///var/run/docker.sock"
#
# endpoint = "tcp://10.10.10.10:2375"

# Default domain used.
# Can be overridden by setting the "traefik.domain" label on a container.
#
# Optional
# Default: ""
#
# domain = "docker.localhost"

# Expose containers by default in traefik
#
# Optional
# Default: true
#
# exposedbydefault = true


################################################################
# File configuration backend
################################################################
[file]
  filename = "rules.toml"
  watch = true

注意filename = “rules.toml”,这个时候我把所有的代理规则写到一个rules.toml文件中,也算一种解耦的思路。
当然traefik也支持多文件。就是你可以指定一个路径,然后会将该路径下所有rule文件加载

[file]
  directory = "/path/to/config/"

下面是demo中的rules.toml

[backends]
  [backends.trend]
    [backends.trend.servers]
      [backends.trend.servers.server1]
      url = "http://api.domain.com:8812"
      weight = 1
    [backends.trend.healthcheck]
      path = "/"
      interval = "10s"
# Frontends
[frontends]
  [frontends.trend]
  backend = "trend"
   [frontends.trend.routes.router1]
    rule = "PathPrefixStrip:/trend"

启动traefik

执行

./traefik --c traefik.toml

实际效果

访问ui:

未分类

对于鉴权:

traefik在中间件中支持了几种auth

  1. basic auth
  2. forward

目前forward基本能满足我们的需求。将请求转发到统一认证服务。
当然oauth,jwt等之类是目前不支持的,但是实现起来很简单,增加一个中间件而已。

总结

没有最好的技术,只有合适的场景。

docker镜像操作

docker有三大核心概念:镜像,容器,仓库

镜像(image)在三大核心概念中最为重要,是docker容器运行的前提。每个容器在运行前都需要有一个与之对应的镜像。如果镜像没保存在本地,docker会尝试先从镜像仓库中下载。

镜像与容器的关系,类似于面向对象编程中的类与对象,一个类可以实例化多个对象,一个镜像也可以实例化多个容器。

获取镜像

镜像是运行容器的前提,官方的docker store (https://store.docker.com/) 提供了大量的镜像并开放下载

下载镜像

docker pull NAME[:TAG]

NAME是镜像仓库的名称,TAG是镜像的标签(用于表示版本信息)

示例:

获取一个ubuntu14.04的基础镜像

docker pull ubuntu:14.04
root@DESKTOP-Q6DMNFI:~# docker pull ubuntu:14.04
14.04: Pulling from library/ubuntu
c954d15f947c: Pull complete
c3688624ef2b: Pull complete
848fe4263b3b: Pull complete
23b4459d3b04: Pull complete
36ab3b56c8f1: Pull complete
Digest: sha256:e1c8bff470c771c6e86d3166607e2c74e6986b05bf339784a9cab70e0e03c7c3
Status: Downloaded newer image for ubuntu:14.04

查看本地镜像

root@DESKTOP-Q6DMNFI:~# docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
ubuntu                     14.04               dc4491992653        4 days ago          222MB

如果不指定标签,默认会下载latest(最新版)的镜像。如docker pull ubuntu将下载ubuntu:latest镜像

从刚才下载的过程中,可看到ubuntu镜像不是作为一个独立的文件,而是由多个层(layer)组成的。

未分类

每个层都有一个惟一的id, 使用docker pull下载时会获取并输出镜像的各层信息

分层的好处在于:当不同的镜像包括相同的层时,本地仅存储层的一份内容,可节省存储空间

下载镜像后,即可随时使用该镜像,如利用ubuntu:14.04创建一个容器,并在该容器中执行ping localhost命令

未分类

查看镜像信息

docker images 列出本地主机上已有镜像的基本信息

未分类

镜像大小信息只是表示该镜像的逻辑体积大小,实际上由于相同的镜像层本地只会存储一会,物理上占用的存储空间会小于各镜像的逻辑体积之和

使用tag命令添加镜像标签

使用docker tag命令可以给本地镜像任意添加新的标签,如添加一个新的myubuntu:test镜像标签

docker tag ubuntu:14.04 myubuntu:test

未分类

添加新标签相当于多了一个镜像,但新增的镜像id与原镜像是一致的,实际上指向同一个镜像文件,只是别名不同。因此,docker tag命令添加的标签实际上起到了类似链接的作用

查看镜像详细信息

docker inspect命令可获取镜像的详细信息,包括制作者、适应架构、各层的数据摘要等:

docker inspect ubuntu:14.04

返回一个JSON格式信息,如果只需要看其中一项的内容,可以用参数进行过滤:

root@DESKTOP-Q6DMNFI:~# docker inspect -f {{".Architecture"}} ubuntu:14.04
amd64

查看镜像各个层的信息

镜像文件是由多个层组成,可以使用history查看各个层的信息

未分类

搜索镜像

docker search搜索镜像仓库中的相关镜像

未分类

默认的输出结果按照星级评价倒序排列

删除镜像

使用标签删除镜像

docker rmi myubuntu:test

当同一个镜像拥有多个标签时,以上命令只是删除该镜像多个标签中的指定标签而已,并不影响镜像文件

但当镜像只剩下一个标签的时候就要小心了,此时就会彻底删除镜像

使用镜像ID删除镜像

docker rmi dc4491992653

指定镜像id,会先尝试删除所有指向该镜像的标签,然后再删除镜像文件本身
如果有基于该镜像的容器正在运行,docker会提示有容器正在运行,无法删除。此时应先将容器关闭m删除,再删除镜像

创建镜像

创建镜像有三种方法:

  • 基于已有镜像的容器创建

  • 基于本地模板导入

  • 基于Dockfile创建

基于已有镜像的容器创建

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

选项说明:

  • -a, –author=””: 作者信息

  • -c, –change=[]: 提交时执行Dockerfile指令

  • -m, –message=””: 提交消息

  • -p, –pause=true: 提交时暂停容器运行

接下来演示:创建一个容器,新增一个test文件,再将这个容器提交生成为一个新的镜像

root@DESKTOP-Q6DMNFI:~# docker run -it ubuntu:14.04 /bin/bash
root@074a066035a2:/# touch test
root@074a066035a2:/# exit
exit
root@DESKTOP-Q6DMNFI:~# docker commit -m "新增一个test文件" -a "靴子猫" 074a066035a2 test:0.1
sha256:3c23db7d1eb6ed4ce98b06c426b1f0179077cf69ec5d63e423cdd7cb01553f7b

未分类

查看新创建的镜像:

root@DESKTOP-Q6DMNFI:~# docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
test                       0.1                 3c23db7d1eb6        6 minutes ago       222MB

基于本地模板导入

要直接导入一个镜像,可以从OpenVZ下载,然后用docker import导入

假设下载了ubuntu-14.04-x86-minimal.tar.gz压缩包,那么可用以下命令导入:

cat ubuntu-14.04-x86-minimal.tar.gz | docker import - ubuntu:14.04

导出和载入镜像

使用docker save和docker load命令导出和载入镜像

导出镜像

将ubuntu:14.04镜像导出为ubuntu_14.04.tar文件

root@DESKTOP-Q6DMNFI:~# docker save -o ubuntu_14.04.tar ubuntu:14.04
root@DESKTOP-Q6DMNFI:~# ls
ubuntu_14.04.tar

导出镜像是一个很有用的功能,可以作为本地备份,也可以分发给其他人使用

载入镜像

将导出的镜像再次导入:

root@DESKTOP-Q6DMNFI:~# docker load --input ubuntu_14.04.tar
Loaded image: ubuntu:14.04