0%

一、yum 简介

  yum,是Yellow dog Updater, Modified 的简称,是杜克大学为了提高RPM 软件包安装性而开发的一种软件包管理器。起初是由yellow dog 这一发行版的开发者Terra Soft 研发,用python 写成,那时还叫做yup(yellow dog updater),后经杜克大学的Linux@Duke 开发团队进行改进,遂有此名。yum 的宗旨是自动化地升级,安装/移除rpm 包,收集rpm 包的相关信息,检查依赖性并自动提示用户解决。yum 的关键之处是要有可靠的repository,顾名思义,这是软件的仓库,它可以是http 或ftp 站点,也可以是本地软件池,但必须包含rpm 的header,header 包括了rpm 包的各种信息,包括描述,功能,提供的文件,依赖性等。正是收集了这些header 并加以分析,才能自动化地完成余下的任务。

  yum 的理念是使用一个中心仓库(repository)管理一部分甚至一个distribution 的应用程序相互关系,根据计算出来的软件依赖关系进行相关的升级、安装、删除等等操作,减少了Linux 用户一直头痛的dependencies 的问题。这一点上,yum 和apt 相同。apt 原为debian 的deb 类型软件管理所使用,但是现在也能用到RedHat 门下的rpm 了。

  yum 主要功能是更方便的添加/删除/更新RPM 包,自动解决包的倚赖性问题,便于管理大量系统的更新问题。

  yum 可以同时配置多个资源库(Repository),简洁的配置文件(/etc/yum.conf),自动解决增加或删除rpm 包时遇到的依赖性问题,保持与RPM 数据库的一致性。

二、yum 安装

CentOS 默认已经安装了yum,不需要另外安装,这里为了实验目的,先将yum 卸载再重新安装。

1、查看系统默认安装的yum

# rpm -qa|grep yum

2、卸载yum

# rpm -e yum-fastestmirror-1.1.16-14.el5.centos.1 yum-metadata-parser-1.1.2-3.el5.centos yum-3.2.22-33.el5.centos

3、重新安装yum

这里可以通过wget 从网上下载相关包安装,也可以挂载系统安装光盘进行安装,这里选择挂载系统安装光盘进行安装。

# mount /dev/cdrom /mnt/cdrom/

# rpm -ivh yum-3.2.22-33.el5.centos.noarch.rpm yum-fastestmirror-1.1.16-14.el5.centos.1.noarch.rpm yum-metadata-parser-1.1.2-3.el5.centos.i386.rpm

# yum -v

yum 的基础安装包包括:

  • yum  //RPM installer/updater
  • yum-fastestmirror  //Yum plugin which chooses fastest repository from a mirrorlist
  • yum-metadata-parser  //A fast metadata parser for yum

其他安装包根据自己需要安装。

三、yum 配置

yum 的配置文件分为两部分:main 和repository

  • main 部分定义了全局配置选项,整个yum 配置文件应该只有一个main。常位于/etc/yum.conf 中。
  • repository 部分定义了每个源/服务器的具体配置,可以有一到多个。常位于/etc/yum.repo.d 目录下的各文件中。

yum.conf 文件一般位于/etc目录下,一般其中只包含main部分的配置选项。

# cat /etc/yum.conf

复制代码

[main]
cachedir=/var/cache/yum
  //yum 缓存的目录,yum 在此存储下载的rpm 包和数据库,默认设置为/var/cache/yum
keepcache=0
  //安装完成后是否保留软件包,0为不保留(默认为0),1为保留
debuglevel=2
  //Debug 信息输出等级,范围为0-10,缺省为2
logfile=/var/log/yum.log
  //yum 日志文件位置。用户可以到/var/log/yum.log 文件去查询过去所做的更新。
pkgpolicy=newest
  //包的策略。一共有两个选项,newest 和last,这个作用是如果你设置了多个repository,而同一软件在不同的repository 中同时存在,yum 应该安装哪一个,如果是newest,则yum 会安装最新的那个版本。如果是last,则yum 会将服务器id 以字母表排序,并选择最后的那个服务器上的软件安装。一般都是选newest。
distroverpkg=redhat-release
  //指定一个软件包,yum 会根据这个包判断你的发行版本,默认是redhat-release,也可以是安装的任何针对自己发行版的rpm 包。
tolerant=1
  //有1和0两个选项,表示yum 是否容忍命令行发生与软件包有关的错误,比如你要安装1,2,3三个包,而其中3此前已经安装了,如果你设为1,则yum 不会出现错误信息。默认是0。
exactarch=1
  //有1和0两个选项,设置为1,则yum 只会安装和系统架构匹配的软件包,例如,yum 不会将i686的软件包安装在适合i386的系统中。默认为1。
retries=6
  //网络连接发生错误后的重试次数,如果设为0,则会无限重试。默认值为6.
obsoletes=1
  //这是一个update 的参数,具体请参阅yum(8),简单的说就是相当于upgrade,允许更新陈旧的RPM包。
plugins=1
  //是否启用插件,默认1为允许,0表示不允许。我们一般会用yum-fastestmirror这个插件。
bugtracker_url=http://bugs.centos.org/set\_project.php?project\_id=16&ref=http://bugs.centos.org/bug\_report\_page.php?category=yum

Note: yum-RHN-plugin doesn’t honor this.

metadata_expire=1h

installonly_limit = 5

PUT YOUR REPOS HERE OR IN separate files named file.repo

in /etc/yum.repos.d

复制代码

除了上述之外,还有一些可以添加的选项,如:

  exclude=selinux*  // 排除某些软件在升级名单之外,可以用通配符,列表中各个项目要用空格隔开,这个对于安装了诸如美化包,中文补丁的朋友特别有用。
  gpgcheck=1  // 有1和0两个选择,分别代表是否是否进行gpg(GNU Private Guard) 校验,以确定rpm 包的来源是有效和安全的。这个选项如果设置在[main]部分,则对每个repository 都有效。默认值为0。

四、配置本地yum源

1、挂载系统安装光盘

# mount /dev/cdrom /mnt/cdrom/

2、配置本地yum源

# cd /etc/yum.repos.d/

# ls

会看到四个repo 文件

CentOS-Base.repo 是yum 网络源的配置文件

CentOS-Media.repo 是yum 本地源的配置文件

修改CentOS-Media.repo

# cat CentOS-Media.repo

复制代码

# CentOS-Media.repo
#

This repo is used to mount the default locations for a CDROM / DVD on

CentOS-5. You can use this repo and yum to install items directly off the

DVD ISO that we release.

To use this repo, put in your DVD and use it with the other repos too:

yum –enablerepo=c5-media [command]

or for ONLY the media repo, do this:

yum –disablerepo=\* –enablerepo=c5-media [command]

[c5-media]
name=CentOS-$releasever - Media
baseurl=file:///media/CentOS/
file:///mnt/cdrom/
file:///media/cdrecorder/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

复制代码

在baseurl 中修改第2个路径为/mnt/cdrom(即为光盘挂载点)

将enabled=0改为1

3、禁用默认的yum 网络源

将yum 网络源配置文件改名为CentOS-Base.repo.bak,否则会先在网络源中寻找适合的包,改名之后直接从本地源读取。

4、执行yum 命令

# yum install postgresql

关于repo 文件的格式

所有repository 服务器设置都应该遵循如下格式:

[serverid]
name=Some name for this server
baseurl=url://path/to/repository/

  • serverid 是用于区别各个不同的repository,必须有一个独一无二的名称;
  • name 是对repository 的描述,支持像$releasever $basearch这样的变量;
  • baseurl 是服务器设置中最重要的部分,只有设置正确,才能从上面获取软件。它的格式是:

baseurl=url://server1/path/to/repository/
     url://server2/path/to/repository/
     url://server3/path/to/repository/

其中url 支持的协议有 http:// ftp:// file:// 三种。baseurl 后可以跟多个url,你可以自己改为速度比较快的镜像站,但baseurl 只能有一个,也就是说不能像如下格式:

baseurl=url://server1/path/to/repository/
baseurl=url://server2/path/to/repository/
baseurl=url://server3/path/to/repository/

其中url 指向的目录必须是这个repository header 目录的上一级,它也支持$releasever $basearch 这样的变量。
url 之后可以加上多个选项,如gpgcheck、exclude、failovermethod 等,比如:

复制代码

[updates-released]
name=Fedora Core $releasever - $basearch - Released Updates
baseurl=http://download.atrpms.net/mirrors/fedoracore/updates/$releasever/$basearch
     http://redhat.linux.ee/pub/fedora/linux/core/updates/$releasever/$basearch
     http://fr2.rpmfind.net/linux/fedora/core/updates/$releasever/$basearch
gpgcheck=1
exclude=gaim
failovermethod=priority

复制代码

其中gpgcheck,exclude 的含义和[main] 部分相同,但只对此服务器起作用,failovermethode 有两个选项roundrobin 和priority,意思分别是有多个url可供选择时,yum 选择的次序,roundrobin 是随机选择,如果连接失败则使用下一个,依次循环,priority 则根据url 的次序从第一个开始。如果不指明,默认是roundrobin。

五、配置国内yum源

系统默认的yum 源速度往往不尽人意,为了达到快速安装的目的,在这里修改yum源为国内源。

上海交通大学yum源

a. 修改/etc/yum.repos.d/CentOS-Base.repo为:

复制代码

# CentOS-Base.repo
#

The mirror system uses the connecting IP address of the client and the

update status of each mirror to pick mirrors that are updated to and

geographically close to the client. You should use this for CentOS updates

unless you are manually picking other mirrors.

If the mirrorlist= does not work for you, as a fall back you can try the

remarked out baseurl= line instead.

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#released updates
[updates]
name=CentOS-$releasever - Updates
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

复制代码

关于变量

  • $releasever:代表发行版的版本,从[main]部分的distroverpkg获取,如果没有,则根据redhat-release包进行判断。
  • $arch:cpu体系,如i686,athlon等
  • $basearch:cpu的基本体系组,如i686和athlon同属i386,alpha和alphaev6同属alpha。

b. 导入GPG KEY

yum 可以使用gpg 对包进行校验,确保下载包的完整性,所以我们先要到各个repository 站点找到gpg key,一般都会放在首页的醒目位置,一些名字诸如RPM-GPG-KEY-CentOS-5 之类的纯文本文件,把它们下载下来,然后用rpm –import RPM-GPG-KEY-CentOS-5 命令将key 导入。

c. 执行yum 命令

其他国内yum源列表如下:

1. 企业贡献:
搜狐开源镜像站:http://mirrors.sohu.com/
网易开源镜像站:http://mirrors.163.com/

2. 大学教学:
北京理工大学:
http://mirror.bit.edu.cn (IPv4 only)
http://mirror.bit6.edu.cn (IPv6 only)
北京交通大学:
http://mirror.bjtu.edu.cn (IPv4 only)
http://mirror6.bjtu.edu.cn (IPv6 only)
http://debian.bjtu.edu.cn (IPv4+IPv6)
兰州大学:http://mirror.lzu.edu.cn/
厦门大学:http://mirrors.xmu.edu.cn/
清华大学:
http://mirrors.tuna.tsinghua.edu.cn/ (IPv4+IPv6)
http://mirrors.6.tuna.tsinghua.edu.cn/ (IPv6 only)
http://mirrors.4.tuna.tsinghua.edu.cn/ (IPv4 only)
天津大学:http://mirror.tju.edu.cn/
中国科学技术大学:
http://mirrors.ustc.edu.cn/ (IPv4+IPv6)
http://mirrors4.ustc.edu.cn/
http://mirrors6.ustc.edu.cn/
东北大学:
http://mirror.neu.edu.cn/ (IPv4 only)
http://mirror.neu6.edu.cn/ (IPv6 only)
电子科技大学:http://ubuntu.uestc.edu.cn/

六、使用第三方软件库

Centos/RHEL默认的yum软件仓库非常有限,仅仅限于发行版本那几张盘里面的常规包和一些软件包的更新,利用RpmForge,可以增加非常多的第三方rpm软件包。RpmForge库现在已经拥有超过10000种的CentOS的软件包,被CentOS社区认为是最安全也是最稳定的一个第三方软件库。

1、安装yum-priorities插件

这个插件是用来设置yum在调用软件源时的顺序的。因为官方提供的软件源,都是比较稳定和被推荐使用的。因此,官方源的顺序要高于第三方源的顺序。如何保证这个顺序,就需要安装yum-priorities这插件了。

# yum -y install yum-priorities

2、安装完yum-priorities插件后需要设置/etc/yum.repos.d/ 目录下的.repo相关文件(如CentOS-Base.repo),在这些文件中插入顺序指令:priority=N (N为1到99的正整数,数值越小越优先)

一般配置[base], [addons], [updates], [extras] 的priority=1,[CentOSplus], [contrib] 的priority=2,其他第三的软件源为:priority=N (推荐N>10)

以CentOS-Base.repo 为例:

复制代码

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5
priority=1

复制代码

3、下载与安装相应rpmforge的rpm文件包

# wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.i386.rpm

4、安装DAG的PGP Key

# rpm –import http://apt.sw.be/RPM-GPG-KEY.dag.txt

5、验证rpmforge的rpm文件包

# rpm -K rpmforge-release-0.5.2-2.el5.rf.*.rpm

6、安装rpmforge的rpm文件包

# rpm -i rpmforge-release-0.5.2-2.el5.rf.i386.rpm

7、设置/etc/yum.repos.d/rpmforge.repo文件中源的级别

[root@TS-DEV yum.repos.d]# cat rpmforge.repo 

复制代码

### Name: RPMforge RPM Repository for RHEL 5 - dag

URL: http://rpmforge.net/

[rpmforge]
name = RHEL $releasever - RPMforge.net - dag
baseurl = http://apt.sw.be/redhat/el5/en/$basearch/rpmforge
mirrorlist = http://apt.sw.be/redhat/el5/en/mirrors-rpmforge
#mirrorlist = file:///etc/yum.repos.d/mirrors-rpmforge
enabled = 1
protect = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmforge-dag
gpgcheck = 1
priority=12

复制代码

8、测试安装

# yum install htop

参考 http://wiki.centos.org/AdditionalResources/Repositories/RPMForge#head-5aabf02717d5b6b12d47edbc5811404998926a1b

其他第三方软件库如 EPEL(Extra Packages for Enterprise Linux ) 和 RPMFusion 的安装与使用和RPMForge类似,可自行查找资料安装。


David Camp

  • 技术交流,请加QQ群:

    系统运维技术分享Ⅲ:416491168

  • 业务合作,请联系作者QQ:562866602
  • 我的微信号:mchina_tang
  • 给我写信:mchina_tang@qq.com
  • 我的地址:江苏·苏州

我们永远相信,分享是一种美德 | We Believe, Great People Share Knowledge…

一、yum 简介

  yum,是Yellow dog Updater, Modified 的简称,是杜克大学为了提高RPM 软件包安装性而开发的一种软件包管理器。起初是由yellow dog 这一发行版的开发者Terra Soft 研发,用python 写成,那时还叫做yup(yellow dog updater),后经杜克大学的Linux@Duke 开发团队进行改进,遂有此名。yum 的宗旨是自动化地升级,安装/移除rpm 包,收集rpm 包的相关信息,检查依赖性并自动提示用户解决。yum 的关键之处是要有可靠的repository,顾名思义,这是软件的仓库,它可以是http 或ftp 站点,也可以是本地软件池,但必须包含rpm 的header,header 包括了rpm 包的各种信息,包括描述,功能,提供的文件,依赖性等。正是收集了这些header 并加以分析,才能自动化地完成余下的任务。

  yum 的理念是使用一个中心仓库(repository)管理一部分甚至一个distribution 的应用程序相互关系,根据计算出来的软件依赖关系进行相关的升级、安装、删除等等操作,减少了Linux 用户一直头痛的dependencies 的问题。这一点上,yum 和apt 相同。apt 原为debian 的deb 类型软件管理所使用,但是现在也能用到RedHat 门下的rpm 了。

  yum 主要功能是更方便的添加/删除/更新RPM 包,自动解决包的倚赖性问题,便于管理大量系统的更新问题。

  yum 可以同时配置多个资源库(Repository),简洁的配置文件(/etc/yum.conf),自动解决增加或删除rpm 包时遇到的依赖性问题,保持与RPM 数据库的一致性。

二、yum 安装

CentOS 默认已经安装了yum,不需要另外安装,这里为了实验目的,先将yum 卸载再重新安装。

1、查看系统默认安装的yum

# rpm -qa|grep yum

2、卸载yum

# rpm -e yum-fastestmirror-1.1.16-14.el5.centos.1 yum-metadata-parser-1.1.2-3.el5.centos yum-3.2.22-33.el5.centos

3、重新安装yum

这里可以通过wget 从网上下载相关包安装,也可以挂载系统安装光盘进行安装,这里选择挂载系统安装光盘进行安装。

# mount /dev/cdrom /mnt/cdrom/

# rpm -ivh yum-3.2.22-33.el5.centos.noarch.rpm yum-fastestmirror-1.1.16-14.el5.centos.1.noarch.rpm yum-metadata-parser-1.1.2-3.el5.centos.i386.rpm

# yum -v

yum 的基础安装包包括:

  • yum  //RPM installer/updater
  • yum-fastestmirror  //Yum plugin which chooses fastest repository from a mirrorlist
  • yum-metadata-parser  //A fast metadata parser for yum

其他安装包根据自己需要安装。

三、yum 配置

yum 的配置文件分为两部分:main 和repository

  • main 部分定义了全局配置选项,整个yum 配置文件应该只有一个main。常位于/etc/yum.conf 中。
  • repository 部分定义了每个源/服务器的具体配置,可以有一到多个。常位于/etc/yum.repo.d 目录下的各文件中。

yum.conf 文件一般位于/etc目录下,一般其中只包含main部分的配置选项。

# cat /etc/yum.conf

复制代码

[main]
cachedir=/var/cache/yum
  //yum 缓存的目录,yum 在此存储下载的rpm 包和数据库,默认设置为/var/cache/yum
keepcache=0
  //安装完成后是否保留软件包,0为不保留(默认为0),1为保留
debuglevel=2
  //Debug 信息输出等级,范围为0-10,缺省为2
logfile=/var/log/yum.log
  //yum 日志文件位置。用户可以到/var/log/yum.log 文件去查询过去所做的更新。
pkgpolicy=newest
  //包的策略。一共有两个选项,newest 和last,这个作用是如果你设置了多个repository,而同一软件在不同的repository 中同时存在,yum 应该安装哪一个,如果是newest,则yum 会安装最新的那个版本。如果是last,则yum 会将服务器id 以字母表排序,并选择最后的那个服务器上的软件安装。一般都是选newest。
distroverpkg=redhat-release
  //指定一个软件包,yum 会根据这个包判断你的发行版本,默认是redhat-release,也可以是安装的任何针对自己发行版的rpm 包。
tolerant=1
  //有1和0两个选项,表示yum 是否容忍命令行发生与软件包有关的错误,比如你要安装1,2,3三个包,而其中3此前已经安装了,如果你设为1,则yum 不会出现错误信息。默认是0。
exactarch=1
  //有1和0两个选项,设置为1,则yum 只会安装和系统架构匹配的软件包,例如,yum 不会将i686的软件包安装在适合i386的系统中。默认为1。
retries=6
  //网络连接发生错误后的重试次数,如果设为0,则会无限重试。默认值为6.
obsoletes=1
  //这是一个update 的参数,具体请参阅yum(8),简单的说就是相当于upgrade,允许更新陈旧的RPM包。
plugins=1
  //是否启用插件,默认1为允许,0表示不允许。我们一般会用yum-fastestmirror这个插件。
bugtracker_url=http://bugs.centos.org/set\_project.php?project\_id=16&ref=http://bugs.centos.org/bug\_report\_page.php?category=yum

Note: yum-RHN-plugin doesn’t honor this.

metadata_expire=1h

installonly_limit = 5

PUT YOUR REPOS HERE OR IN separate files named file.repo

in /etc/yum.repos.d

复制代码

除了上述之外,还有一些可以添加的选项,如:

  exclude=selinux*  // 排除某些软件在升级名单之外,可以用通配符,列表中各个项目要用空格隔开,这个对于安装了诸如美化包,中文补丁的朋友特别有用。
  gpgcheck=1  // 有1和0两个选择,分别代表是否是否进行gpg(GNU Private Guard) 校验,以确定rpm 包的来源是有效和安全的。这个选项如果设置在[main]部分,则对每个repository 都有效。默认值为0。

四、配置本地yum源

1、挂载系统安装光盘

# mount /dev/cdrom /mnt/cdrom/

2、配置本地yum源

# cd /etc/yum.repos.d/

# ls

会看到四个repo 文件

CentOS-Base.repo 是yum 网络源的配置文件

CentOS-Media.repo 是yum 本地源的配置文件

修改CentOS-Media.repo

# cat CentOS-Media.repo

复制代码

# CentOS-Media.repo
#

This repo is used to mount the default locations for a CDROM / DVD on

CentOS-5. You can use this repo and yum to install items directly off the

DVD ISO that we release.

To use this repo, put in your DVD and use it with the other repos too:

yum –enablerepo=c5-media [command]

or for ONLY the media repo, do this:

yum –disablerepo=\* –enablerepo=c5-media [command]

[c5-media]
name=CentOS-$releasever - Media
baseurl=file:///media/CentOS/
file:///mnt/cdrom/
file:///media/cdrecorder/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

复制代码

在baseurl 中修改第2个路径为/mnt/cdrom(即为光盘挂载点)

将enabled=0改为1

3、禁用默认的yum 网络源

将yum 网络源配置文件改名为CentOS-Base.repo.bak,否则会先在网络源中寻找适合的包,改名之后直接从本地源读取。

4、执行yum 命令

# yum install postgresql

关于repo 文件的格式

所有repository 服务器设置都应该遵循如下格式:

[serverid]
name=Some name for this server
baseurl=url://path/to/repository/

  • serverid 是用于区别各个不同的repository,必须有一个独一无二的名称;
  • name 是对repository 的描述,支持像$releasever $basearch这样的变量;
  • baseurl 是服务器设置中最重要的部分,只有设置正确,才能从上面获取软件。它的格式是:

baseurl=url://server1/path/to/repository/
     url://server2/path/to/repository/
     url://server3/path/to/repository/

其中url 支持的协议有 http:// ftp:// file:// 三种。baseurl 后可以跟多个url,你可以自己改为速度比较快的镜像站,但baseurl 只能有一个,也就是说不能像如下格式:

baseurl=url://server1/path/to/repository/
baseurl=url://server2/path/to/repository/
baseurl=url://server3/path/to/repository/

其中url 指向的目录必须是这个repository header 目录的上一级,它也支持$releasever $basearch 这样的变量。
url 之后可以加上多个选项,如gpgcheck、exclude、failovermethod 等,比如:

复制代码

[updates-released]
name=Fedora Core $releasever - $basearch - Released Updates
baseurl=http://download.atrpms.net/mirrors/fedoracore/updates/$releasever/$basearch
     http://redhat.linux.ee/pub/fedora/linux/core/updates/$releasever/$basearch
     http://fr2.rpmfind.net/linux/fedora/core/updates/$releasever/$basearch
gpgcheck=1
exclude=gaim
failovermethod=priority

复制代码

其中gpgcheck,exclude 的含义和[main] 部分相同,但只对此服务器起作用,failovermethode 有两个选项roundrobin 和priority,意思分别是有多个url可供选择时,yum 选择的次序,roundrobin 是随机选择,如果连接失败则使用下一个,依次循环,priority 则根据url 的次序从第一个开始。如果不指明,默认是roundrobin。

五、配置国内yum源

系统默认的yum 源速度往往不尽人意,为了达到快速安装的目的,在这里修改yum源为国内源。

上海交通大学yum源

a. 修改/etc/yum.repos.d/CentOS-Base.repo为:

复制代码

# CentOS-Base.repo
#

The mirror system uses the connecting IP address of the client and the

update status of each mirror to pick mirrors that are updated to and

geographically close to the client. You should use this for CentOS updates

unless you are manually picking other mirrors.

If the mirrorlist= does not work for you, as a fall back you can try the

remarked out baseurl= line instead.

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#released updates
[updates]
name=CentOS-$releasever - Updates
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

复制代码

关于变量

  • $releasever:代表发行版的版本,从[main]部分的distroverpkg获取,如果没有,则根据redhat-release包进行判断。
  • $arch:cpu体系,如i686,athlon等
  • $basearch:cpu的基本体系组,如i686和athlon同属i386,alpha和alphaev6同属alpha。

b. 导入GPG KEY

yum 可以使用gpg 对包进行校验,确保下载包的完整性,所以我们先要到各个repository 站点找到gpg key,一般都会放在首页的醒目位置,一些名字诸如RPM-GPG-KEY-CentOS-5 之类的纯文本文件,把它们下载下来,然后用rpm –import RPM-GPG-KEY-CentOS-5 命令将key 导入。

c. 执行yum 命令

其他国内yum源列表如下:

1. 企业贡献:
搜狐开源镜像站:http://mirrors.sohu.com/
网易开源镜像站:http://mirrors.163.com/

2. 大学教学:
北京理工大学:
http://mirror.bit.edu.cn (IPv4 only)
http://mirror.bit6.edu.cn (IPv6 only)
北京交通大学:
http://mirror.bjtu.edu.cn (IPv4 only)
http://mirror6.bjtu.edu.cn (IPv6 only)
http://debian.bjtu.edu.cn (IPv4+IPv6)
兰州大学:http://mirror.lzu.edu.cn/
厦门大学:http://mirrors.xmu.edu.cn/
清华大学:
http://mirrors.tuna.tsinghua.edu.cn/ (IPv4+IPv6)
http://mirrors.6.tuna.tsinghua.edu.cn/ (IPv6 only)
http://mirrors.4.tuna.tsinghua.edu.cn/ (IPv4 only)
天津大学:http://mirror.tju.edu.cn/
中国科学技术大学:
http://mirrors.ustc.edu.cn/ (IPv4+IPv6)
http://mirrors4.ustc.edu.cn/
http://mirrors6.ustc.edu.cn/
东北大学:
http://mirror.neu.edu.cn/ (IPv4 only)
http://mirror.neu6.edu.cn/ (IPv6 only)
电子科技大学:http://ubuntu.uestc.edu.cn/

六、使用第三方软件库

Centos/RHEL默认的yum软件仓库非常有限,仅仅限于发行版本那几张盘里面的常规包和一些软件包的更新,利用RpmForge,可以增加非常多的第三方rpm软件包。RpmForge库现在已经拥有超过10000种的CentOS的软件包,被CentOS社区认为是最安全也是最稳定的一个第三方软件库。

1、安装yum-priorities插件

这个插件是用来设置yum在调用软件源时的顺序的。因为官方提供的软件源,都是比较稳定和被推荐使用的。因此,官方源的顺序要高于第三方源的顺序。如何保证这个顺序,就需要安装yum-priorities这插件了。

# yum -y install yum-priorities

2、安装完yum-priorities插件后需要设置/etc/yum.repos.d/ 目录下的.repo相关文件(如CentOS-Base.repo),在这些文件中插入顺序指令:priority=N (N为1到99的正整数,数值越小越优先)

一般配置[base], [addons], [updates], [extras] 的priority=1,[CentOSplus], [contrib] 的priority=2,其他第三的软件源为:priority=N (推荐N>10)

以CentOS-Base.repo 为例:

复制代码

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
baseurl=http://ftp.sjtu.edu.cn/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5
priority=1

复制代码

3、下载与安装相应rpmforge的rpm文件包

# wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.i386.rpm

4、安装DAG的PGP Key

# rpm –import http://apt.sw.be/RPM-GPG-KEY.dag.txt

5、验证rpmforge的rpm文件包

# rpm -K rpmforge-release-0.5.2-2.el5.rf.*.rpm

6、安装rpmforge的rpm文件包

# rpm -i rpmforge-release-0.5.2-2.el5.rf.i386.rpm

7、设置/etc/yum.repos.d/rpmforge.repo文件中源的级别

[root@TS-DEV yum.repos.d]# cat rpmforge.repo 

复制代码

### Name: RPMforge RPM Repository for RHEL 5 - dag

URL: http://rpmforge.net/

[rpmforge]
name = RHEL $releasever - RPMforge.net - dag
baseurl = http://apt.sw.be/redhat/el5/en/$basearch/rpmforge
mirrorlist = http://apt.sw.be/redhat/el5/en/mirrors-rpmforge
#mirrorlist = file:///etc/yum.repos.d/mirrors-rpmforge
enabled = 1
protect = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmforge-dag
gpgcheck = 1
priority=12

复制代码

8、测试安装

# yum install htop

参考 http://wiki.centos.org/AdditionalResources/Repositories/RPMForge#head-5aabf02717d5b6b12d47edbc5811404998926a1b

其他第三方软件库如 EPEL(Extra Packages for Enterprise Linux ) 和 RPMFusion 的安装与使用和RPMForge类似,可自行查找资料安装。


David Camp

  • 技术交流,请加QQ群:

    系统运维技术分享Ⅲ:416491168

  • 业务合作,请联系作者QQ:562866602
  • 我的微信号:mchina_tang
  • 给我写信:mchina_tang@qq.com
  • 我的地址:江苏·苏州

我们永远相信,分享是一种美德 | We Believe, Great People Share Knowledge…

其实这两种方法各有千秋:

yum安装:

从yum安装来说吧,yum相当于是自动化帮你安装,你不用管软件的依赖关系,在yum安装过程是帮你把软件的全部依赖关系帮你傻瓜式的解决了。而且现在Centos7的服务启动已经换成systemctl命令来控制了。通过yum安装会帮你自动注册服务,你可以通过systemctl start xxx.service启动服务,方便快捷。但是缺点是yum安装你没办法干预,安装的目录也是分散的。你可能要执行whereis或者find命令去找yum安装的路径。有时候yum安装的软件版本比较低,你不得不去找其他的yum源,或者rpm包。

源码安装:

源码编译在安装过程中可能要解决很多的依赖问题,才能装好一个软件。装好的软件你还不能通过systemctl来启动服务,因为在/usr/lib/systemd/system/路径下并没有你的服务的配置文件,你要自己手写一个。但是好处在于你能选择软件的版本,自定义安装目录,安装的模块。更加灵活方便。

LNMP一键安装:

LNMP一键安装包是一个用Linux Shell编写的可以为CentOS/RHEL/Fedora/Aliyun/Amazon、Debian/Ubuntu/Raspbian/Deepin/Mint Linux VPS或独立主机安装LNMP(Nginx/MySQL/PHP)、LNMPA(Nginx/MySQL/PHP/Apache)、LAMP(Apache/MySQL/PHP)生产环境的Shell程序。无需一个一个的输入命令,无需值守,编译安装优化编译参数,提高性能,解决不必要的软件间依赖,特别针对配置自动优化。支持自定义Nginx、PHP编译参数及网站和数据库目录、支持生成LetseEcrypt证书、LNMP模式支持多PHP版本、支持单独安装Nginx/MySQL/MariaDB/Pureftpd服务器,同时提供一些实用的辅助工具如:虚拟主机管理、FTP用户管理、Nginx、MySQL/MariaDB、PHP的升级、常用缓存组件Redis/Xcache等的安装、重置MySQL root密码、502自动重启、日志切割、SSH防护DenyHosts/Fail2Ban、备份等许多实用脚本。

以上三种都是有各自的优点,建议是初学者一定要掌握源码编译的过程,手动解决安装过成中遇到的问题,熟悉如何编译一个软件,对于以后的发展是很有利的,而且有些软件没办法通过yum安装,这时候源码编译就显得很重要了。而像PHP这类软件来说,如果是编译安装的,如果缺少一个扩展,你就得做phpize, ./configure, make && make install等方式编译PHP扩展,这是很繁琐的。通过yum安装的话,当你要增加一个扩展,例如pdo,你就能够yum search php | grep pdo来寻找合适的pdo包,然后下载安装,系统会自动帮你添加到PHP扩展列表。省去我们很多工作。LNMP一键安装是为开发者快速搭建开发环境时使用,可以让开发者有更多时间在开发的业务逻辑上。

1.使用yum命令(学习yum的基本知识

参考网站:

这个方法是比较简单的方法,可以很简单快速地安装php。(rpm命令的基本用法和实例

添加相关的库后,启用 PHP 7.4 的 Remi 模块并进行安装。

然后安装你想要的模块:

使用如下命令安装额外的包

运行并查看版本, 重启命令, 添加自动启动,链接php文件

 

一些配置文件的路径

卸载所有php7.4

参考网站:

https://www.php.net/manual/zh/install.php

https://www.cnblogs.com/werben/p/11833903.html

https://www.cnblogs.com/ckh2014/p/10909672.html

https://www.24kplus.com/linux/1614.html

用php的官网,有很多源的,想要什么版本可以自己选一下

https://www.php.net/distributions/php-7.4.2.tar.bz2

 下载完成

解压文的基本命令和实例

安装php的依赖库和安装编译的工具,无论有没有装都可以执行一下

创建用户组和用户

配置fpm的用户组和用户,以及安装其他扩展

开始检查依赖,做编译安前的准备

一般都会有错误:

错误一:

解决方法:缺了这个sqlite3这个包,我们就安装它

错误二:

  解决方法:它说没有找到这个文件夹,我们就给它一个

 错误三:

 解决方法:缺了这个oniguruma这个包,我们就安装它

结果不行,去查看了官方更新日志 发现mbsting  需要oniguruma6.9.4

git的基本用户和实例,git与github的区别

运行./configure,直接出现下图

 

映射全局命令

输入 php -v ,出现下图,这就安装成功了

删除php7.4

参考网站

https://lnmp.org/

出官网找到有php7.4的版本,是lnmp1.7才开始支持php7.4,但不是稳定版,如果是用于生产环境还是建议用稳的版本

输入0,不安装DataBase(数据库)

 输入10,安装php7.4这个版本

可以选择不安装、Jemalloc或TCmalloc,输入对应序号回车,直接回车为默认为不安装

脚本会自动下载安装php7.4

 

LNMP相关软件安装目录

LNMP相关配置文件位置

LNMPA相关目录文件位置

LNMP状态管理命令:
LNMP 1.2+状态管理: lnmp {start|stop|reload|restart|kill|status}
LNMP 1.2+各个程序状态管理: lnmp {nginx|mysql|mariadb|php-fpm|pureftpd} {start|stop|reload|restart|kill|status}
LNMP 1.1状态管理: /root/lnmp {start|stop|reload|restart|kill|status}
Nginx状态管理:/etc/init.d/nginx {start|stop|reload|restart}
MySQL状态管理:/etc/init.d/mysql {start|stop|restart|reload|force-reload|status}
Memcached状态管理:/etc/init.d/memcached {start|stop|restart}
PHP-FPM状态管理:/etc/init.d/php-fpm {start|stop|quit|restart|reload|logrotate}
PureFTPd状态管理: /etc/init.d/pureftpd {start|stop|restart|kill|status}
ProFTPd状态管理: /etc/init.d/proftpd {start|stop|restart|reload}
Redis状态管理: /etc/init.d/redis {start|stop|restart|kill}

多PHP版本状态管理:/etc/init.d/php-fpm5.5 {start|stop|quit|restart|reload|logrotate} 前面5.5为对应的PHP版本,其他版本自行替换。
如重启LNMP,1.2+输入命令:lnmp restart 即可;单独重启mysql:/etc/init.d/mysql restart 也可以 lnmp mysql restart ,两个是一样的。

LNMPA状态管理命令:

LNMPA 1.2+状态管理: lnmp {start|stop|reload|restart|kill|status}
LNMPA 1.2+各个程序状态管理: lnmp {httpd|mysql|mariadb|pureftpd} {start|stop|reload|restart|kill|status}
LNMPA1.1状态管理: /root/lnmpa {start|stop|reload|restart|kill|status}
Nginx状态管理:/etc/init.d/nginx {start|stop|reload|restart}
MySQL状态管理:/etc/init.d/mysql {start|stop|restart|reload|force-reload|status}
Memcached状态管理:/etc/init.d/memcached {start|stop|restart}
PureFTPd状态管理: /etc/init.d/pureftpd {start|stop|restart|kill|status}
ProFTPd状态管理: /etc/init.d/proftpd {start|stop|restart|reload}
Apache状态管理:/etc/init.d/httpd {start|stop|restart|graceful|graceful-stop|configtest|status}

LAMP状态管理命令:
LAMP 1.2+状态管理: lnmp {start|stop|reload|restart|kill|status}
LAMP 1.2+各个程序状态管理: lnmp {httpd|mysql|mariadb|pureftpd} {start|stop|reload|restart|kill|status}

一、准备机器

本文描述如何在 CentOS 7 下搭建 Ceph 存储集群(STORAGE CLUSTER)。

一共4台机器,其中1个是管理节点,其他3个是ceph节点:

hostname

ip

role

描述

admin-node

192.168.0.130

ceph-deploy

管理节点

node1

192.168.0.131

mon.node1

ceph节点,监控节点

node2

192.168.0.132

osd.0

ceph节点,OSD节点

node3

192.168.0.133

osd.1

ceph节点,OSD节点

管理节点:admin-node

ceph节点:node1, node2, node3

所有节点:admin-node, node1, node2, node3

1. 修改主机名

2. 修改hosts文件

1
2
3
4
192.168.0.130 admin-node
192.168.0.131 node1
192.168.0.132 node2
192.168.0.133 node3

3. 确保联通性(管理节点)

用 ping 短主机名( hostname -s )的方式确认网络联通性。解决掉可能存在的主机名解析问题。

1
2
3
$ ping node1
$ ping node2
$ ping node3

二、ceph节点安装

1. 安装NPT(所有节点)

我们建议在所有 Ceph 节点上安装 NTP 服务(特别是 Ceph Monitor 节点),以免因时钟漂移导致故障,详情见时钟。

2. 安装SSH(所有节点)

3. 创建部署 CEPH 的用户(所有节点)

ceph-deploy 工具必须以普通用户登录 Ceph 节点,且此用户拥有无密码使用 sudo 的权限,因为它需要在安装软件及配置文件的过程中,不必输入密码。
建议在集群内的所有 Ceph 节点上给 ceph-deploy 创建一个特定的用户,但不要用 “ceph” 这个名字。

  1. 在各 Ceph 节点创建新用户

  2. 确保各 Ceph 节点上新创建的用户都有 sudo 权限

1
2
# echo "zeng ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/zeng
# sudo chmod 0440 /etc/sudoers.d/zeng

4. 允许无密码SSH登录(管理节点)

因为 ceph-deploy 不支持输入密码,你必须在管理节点上生成 SSH 密钥并把其公钥分发到各 Ceph 节点。 ceph-deploy 会尝试给初始 monitors 生成 SSH 密钥对。

  1. 生成 SSH 密钥对

不要用 sudoroot 用户。提示 “Enter passphrase” 时,直接回车,口令即为空:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//切换用户,如不特别说明,后续的操作均在该用户下进行


//生成密钥对
$ ssh-keygen

Generating public/private rsa key pair.
Enter file in which to save the key (/home/zeng/.ssh/id_rsa):
Created directory '/home/zeng/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/zeng/.ssh/id_rsa.
Your public key has been saved in /home/zeng/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Tb0VpUOZtmh+QBRjUOE0n2Uy3WuoZVgXn6TBBb2SsGk zeng@admin-node
The key's randomart image is:
+---[RSA 2048]----+
| .+@=OO*|
| *.BB@=|
| ..O+Xo+|
| o E+O.= |
| S oo=.o |
| .. . |
| . |
| |
| |
+----[SHA256]-----+
  1. 把公钥拷贝到各 Ceph 节点
1
2
3
$ ssh-copy-id zeng@node1
$ ssh-copy-id zeng@node2
$ ssh-copy-id zeng@node3

完成后, /home/zeng/.ssh/ 路径下:

  • admin-node 多了文件 id_rsaid_rsa.pubknown_hosts
  • node1, node2, node3 多了文件authorized_keys
  1. 修改~/.ssh/config 文件

修改 ~/.ssh/config 文件(没有则新增),这样 ceph-deploy 就能用你所建的用户名登录 Ceph 节点了。

1
2

$ sudo vi ~/.ssh/config
1
2
3
4
5
6
7
8
9
10
11
12
Host admin-node
Hostname admin-node
User zeng
Host node1
Hostname node1
User zeng
Host node2
Hostname node2
User zeng
Host node3
Hostname node3
User zeng
  1. 测试ssh能否成功
1
2
3
4
5
6
$ ssh zeng@node1
$ exit
$ ssh zeng@node2
$ exit
$ ssh zeng@node3
$ exit
  • 问题:如果出现 “Bad owner or permissions on /home/zeng/.ssh/config”,执行命令修改文件权限。
1
$ sudo chmod 644 ~/.ssh/config

5. 引导时联网(ceph节点)

Ceph 的各 OSD 进程通过网络互联并向 Monitors 上报自己的状态。如果网络默认为 off ,那么 Ceph 集群在启动时就不能上线,直到你打开网络。

1
2
3
$ cat /etc/sysconfig/network-scripts/ifcfg-enp0s3

//确保ONBOOT 设置成了 yes

6. 开放所需端口(ceph节点)

Ceph Monitors 之间默认使用 6789 端口通信, OSD 之间默认用 6800:7300 这个范围内的端口通信。Ceph OSD 能利用多个网络连接进行与客户端、monitors、其他 OSD 间的复制和心跳的通信。

1
2
3
4
$ sudo firewall-cmd --zone=public --add-port=6789/tcp --permanent
// 或者关闭防火墙
$ sudo systemctl stop firewalld
$ sudo systemctl disable firewalld

7. 终端(TTY)(ceph节点)

在 CentOS 和 RHEL 上执行 ceph-deploy 命令时可能会报错。如果你的 Ceph 节点默认设置了 requiretty ,执行

1
$ sudo visudo

找到 Defaults requiretty 选项,把它改为 Defaults:ceph !requiretty 或者直接注释掉,这样 ceph-deploy 就可以用之前创建的用户(创建部署 Ceph 的用户 )连接了。

编辑配置文件 /etc/sudoers 时,必须用 sudo visudo 而不是文本编辑器。

8. 关闭selinux(ceph节点)

1
$ sudo setenforce 0

要使 SELinux 配置永久生效(如果它的确是问题根源),需修改其配置文件 /etc/selinux/config:

1
$ sudo sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

即修改 SELINUX=disabled

9. 配置EPEL源(管理节点)

1
$ sudo yum install -y yum-utils && sudo yum-config-manager --add-repo https://dl.fedoraproject.org/pub/epel/7/x86_64/ && sudo yum install --nogpgcheck -y epel-release && sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 && sudo rm /etc/yum.repos.d/dl.fedoraproject.org*

10. 把软件包源加入软件库(管理节点)

1
$ sudo vi /etc/yum.repos.d/ceph.repo

把如下内容粘帖进去,保存到 /etc/yum.repos.d/ceph.repo 文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[Ceph]
name=Ceph packages for $basearch
baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/x86_64/
enabled=1
gpgcheck=0
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1

[Ceph-noarch]
name=Ceph noarch packages
baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/noarch/
enabled=1
gpgcheck=0
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1

[ceph-source]
name=Ceph source packages
baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/SRPMS/
enabled=1
gpgcheck=0
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1

11. 更新软件库并安装ceph-deploy(管理节点)

1
2
$ sudo yum update && sudo yum install ceph-deploy
$ sudo yum install yum-plugin-priorities

时间可能比较久,耐心等待。

三、搭建集群

管理节点 下执行如下步骤:

1. 安装准备,创建文件夹

在管理节点上创建一个目录,用于保存 ceph-deploy 生成的配置文件和密钥对。

1
2
3
$ cd ~
$ mkdir my-cluster
$ cd my-cluster

注:若安装ceph后遇到麻烦可以使用以下命令进行清除包和配置:

1
2
3
4
5
6
// 删除安装包
$ ceph-deploy purge admin-node node1 node2 node3

// 清除配置
$ ceph-deploy purgedata admin-node node1 node2 node3
$ ceph-deploy forgetkeys

2. 创建集群和监控节点

创建集群并初始化监控节点

1
$ ceph-deploy new {initial-monitor-node(s)}

这里node1是monitor节点,所以执行:

1
$ ceph-deploy new node1

完成后,my-clster 下多了3个文件:ceph.confceph-deploy-ceph.logceph.mon.keyring

  • 问题:如果出现 “[ceph_deploy][ERROR ] RuntimeError: remote connection got closed, ensure requiretty is disabled for node1”,执行 sudo visudo 将 Defaults requiretty 注释掉。

3. 修改配置文件

1
$ cat ceph.conf

内容如下:

1
2
3
4
5
6
7
[global]
fsid = 89933bbb-257c-4f46-9f77-02f44f4cc95c
mon_initial_members = node1
mon_host = 192.168.0.131
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx

把 Ceph 配置文件里的默认副本数从 3 改成 2 ,这样只有两个 OSD 也可以达到 active + clean 状态。把 osd pool default size = 2 加入 [global] 段:

1
$ sed -i '$a\osd pool default size = 2' ceph.conf

如果有多个网卡,
可以把 public network 写入 Ceph 配置文件的 [global] 段:

1
public network = {ip-address}/{netmask}

4. 安装Ceph

在所有节点上安装ceph:

1
$ ceph-deploy install admin-node node1 node2 node3
  • 问题:[ceph_deploy][ERROR ] RuntimeError: Failed to execute command: yum -y install epel-release

解决方法:

1
sudo yum -y remove epel-release

5. 配置初始 monitor(s)、并收集所有密钥

1
$ ceph-deploy mon create-initial

完成上述操作后,当前目录里应该会出现这些密钥环:

1
2
3
4
{cluster-name}.client.admin.keyring
{cluster-name}.bootstrap-osd.keyring
{cluster-name}.bootstrap-mds.keyring
{cluster-name}.bootstrap-rgw.keyring

6. 添加2个OSD

  1. 登录到 Ceph 节点、并给 OSD 守护进程创建一个目录,并添加权限。
1
2
3
4
5
6
7
8
9
$ ssh node2
$ sudo mkdir /var/local/osd0
$ sudo chmod 777 /var/local/osd0/
$ exit

$ ssh node3
$ sudo mkdir /var/local/osd1
$ sudo chmod 777 /var/local/osd1/
$ exit
  1. 然后,从管理节点执行 ceph-deploy 来准备 OSD 。
1
$ ceph-deploy osd prepare node2:/var/local/osd0 node3:/var/local/osd1
  1. 最后,激活 OSD 。
1
$ ceph-deploy osd activate node2:/var/local/osd0 node3:/var/local/osd1

7.把配置文件和 admin 密钥拷贝到管理节点和 Ceph 节点

1
$ ceph-deploy admin admin-node node1 node2 node3

8. 确保你对 ceph.client.admin.keyring 有正确的操作权限

1
$ sudo chmod +r /etc/ceph/ceph.client.admin.keyring

9. 检查集群的健康状况和OSD节点状况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[zeng@admin-node my-cluster]$ ceph health
HEALTH_OK

[zeng@admin-node my-cluster]$ ceph -s
cluster a3dd419e-5c99-4387-b251-58d4eb582995
health HEALTH_OK
monmap e1: 1 mons at {node1=192.168.0.131:6789/0}
election epoch 3, quorum 0 node1
osdmap e10: 2 osds: 2 up, 2 in
flags sortbitwise,require_jewel_osds
pgmap v22: 64 pgs, 1 pools, 0 bytes data, 0 objects
12956 MB used, 21831 MB / 34788 MB avail
64 active+clean

[zeng@admin-node my-cluster]$ ceph osd df
ID WEIGHT REWEIGHT SIZE USE AVAIL %USE VAR PGS
0 0.01659 1.00000 17394M 6478M 10915M 37.24 1.00 64
1 0.01659 1.00000 17394M 6478M 10915M 37.25 1.00 64
TOTAL 34788M 12956M 21831M 37.24
MIN/MAX VAR: 1.00/1.00 STDDEV: 0

四、扩展集群(扩容)

1. 添加OSD

在 node1 上添加一个 osd.2。

  1. 创建目录
1
2
3
4
$ ssh node1
$ sudo mkdir /var/local/osd2
$ sudo chmod 777 /var/local/osd2/
$ exit
  1. 准备OSD
1
$ ceph-deploy osd prepare node1:/var/local/osd2
  1. 激活OSD
1
$ ceph-deploy osd activate node1:/var/local/osd2
  1. 检查集群状况和OSD节点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[zeng@admin-node my-cluster]$ ceph -s
cluster a3dd419e-5c99-4387-b251-58d4eb582995
health HEALTH_OK
monmap e1: 1 mons at {node1=192.168.0.131:6789/0}
election epoch 3, quorum 0 node1
osdmap e15: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v37: 64 pgs, 1 pools, 0 bytes data, 0 objects
19450 MB used, 32731 MB / 52182 MB avail
64 active+clean

[zeng@admin-node my-cluster]$ ceph osd df
ID WEIGHT REWEIGHT SIZE USE AVAIL %USE VAR PGS
0 0.01659 1.00000 17394M 6478M 10915M 37.24 1.00 41
1 0.01659 1.00000 17394M 6478M 10915M 37.24 1.00 43
2 0.01659 1.00000 17394M 6494M 10899M 37.34 1.00 44
TOTAL 52182M 19450M 32731M 37.28
MIN/MAX VAR: 1.00/1.00 STDDEV: 0.04

2. 添加MONITORS

在 ndoe2 和 node3 添加监控节点。

  1. 修改 mon_initial_membersmon_hostpublic network 配置:
1
2
3
4
5
6
7
8
9
10
[global]
fsid = a3dd419e-5c99-4387-b251-58d4eb582995
mon_initial_members = node1,node2,node3
mon_host = 192.168.0.131,192.168.0.132,192.168.0.133
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx

osd pool default size = 2
public network = 192.168.0.120/24
  1. 推送至其他节点:
1
$ ceph-deploy 
  1. 添加监控节点:
1
$ ceph-deploy mon add node2 node3
  1. 查看集群状态和监控节点:
1
2
3
4
5
6
7
8
9
10
11
12
13
[zeng@admin-node my-cluster]$ ceph -s
cluster a3dd419e-5c99-4387-b251-58d4eb582995
health HEALTH_OK
monmap e3: 3 mons at {node1=192.168.0.131:6789/0,node2=192.168.0.132:6789/0,node3=192.168.0.133:6789/0}
election epoch 8, quorum 0,1,2 node1,node2,node3
osdmap e25: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v3919: 64 pgs, 1 pools, 0 bytes data, 0 objects
19494 MB used, 32687 MB / 52182 MB avail
64 active+clean

[zeng@admin-node my-cluster]$ ceph mon stat
e3: 3 mons at {node1=192.168.0.131:6789/0,node2=192.168.0.132:6789/0,node3=192.168.0.133:6789/0}, election epoch 8, quorum 0,1,2 node1,node2,node3

参考链接:
PREFLIGHT
STORAGE CLUSTER QUICK START

对比说明

/文件系统

TFS

FastDFS

MogileFS

MooseFS

GlusterFS

Ceph

开发语言

C++

C

Perl

C

C

C++

开源协议

GPL V2

GPL V3

GPL

GPL V3

GPL V3

LGPL

数据存储方式

文件/Trunk

文件

文件/块

对象/文件/块

集群节点通信协议

私有协议(TCP)

私有协议(TCP)

HTTP

私有协议(TCP)

私有协议(TCP)/ RDAM(远程直接访问内存)

私有协议(TCP)

专用元数据存储点

占用NS

占用DB

占用MFS

占用MDS

在线扩容

支持

支持

支持

支持

支持

支持

冗余备份

支持

支持

-

支持

支持

支持

单点故障

存在

不存在

存在

存在

不存在

存在

跨集群同步

支持

部分支持

-

-

支持

不适用

易用性

安装复杂,官方文档少

安装简单,社区相对活跃

-

安装简单,官方文档多

安装简单,官方文档专业化

安装简单,官方文档专业化

适用场景

跨集群的小文件

单集群的中小文件

-

单集群的大中文件

跨集群云存储

单集群的大中小文件

开源协议说明

GPL:不允许修改后和衍生的代码做为闭源的商业软件发布和销售,修改后该软件产品必须也采用GPL协议;
GPL V2:修改文本的整体就必须按照GPL流通,不仅该修改文本的源码必须向社 会公开,而且对于这种修改文本的流通不准许附加修改者自己作出的限制;
GPL V3:要求用户公布修改的源代码,还要求公布相关硬件;LGPL:更宽松的GPL

TFS(Taobao File System)是由淘宝开发的一个分布式文件系统,其内部经过特殊的优化处理,适用于海量的小文件存储,目前已经对外开源;

TFS采用自有的文件系统格式存储,因此需要专用的API接口去访问,目前官方提供的客户端版本有:C++/JAVA/PHP。

  • 特性

1)在TFS文件系统中,NameServer负责管理文件元数据,通过HA机制实现主备热切换,由于所有元数据都是在内存中,其处理效率非常高效,系统架构也非常简单,管理也很方便;
2)TFS的DataServer作为分部署数据存储节点,同时也具备负载均衡和冗余备份的功能,由于采用自有的文件系统,对小文件会采取合并策略,减少数据碎片,从而提升IO性能;
3)TFS将元数据信息(BlockID、FileID)直接映射至文件名中,这一设计大大降低了存储元数据的内存空间;

  • 优点

1)针对小文件量身定做,随机IO性能比较高;
2)支持在线扩容机制,增强系统的可扩展性;
3)实现了软RAID,增强系统的并发处理能力及数据容错恢复能力;
4)支持主备热倒换,提升系统的可用性;
5)支持主从集群部署,其中从集群主要提供读/备功能;

  • 缺点

1)TFS只对小文件做优化,不适合大文件的存储;
2)不支持POSIX通用接口访问,通用性较低;
3)不支持自定义目录结构,及文件权限控制;
4)通过API下载,存在单点的性能瓶颈;
5)官方文档非常少,学习成本高;

  • 应用场景

1)多集群部署的应用
2)存储后基本不做改动
3)海量小型文件
根据目前官方提供的材料,对单个集群节点,存储节点在1000台以内可以良好工作,如存储节点扩大可能会出现NameServer的性能瓶颈,目前淘宝线上部署容量已达到1800TB规模(2009年数据)

 源代码路径http://code.taobao.org/p/tfs/src/

 参考

http://rdc.taobao.com/blog/cs/?p=128

http://elf8848.iteye.com/blog/1724423

http://baike.baidu.com/view/1030880.htm

http://blog.yunnotes.net/index.php/install_document_for_tfs/

FastDFS

FastDFS是国人开发的一款分布式文件系统,目前社区比较活跃。如上图所示系统中存在三种节点:Client、Tracker、Storage,在底层存储上通过逻辑的分组概念,使得通过在同组内配置多个Storage,从而实现软RAID10,提升并发IO的性能、简单负载均衡及数据的冗余备份;同时通过线性的添加新的逻辑存储组,从容实现存储容量的线性扩容。

文件下载上,除了支持通过API方式,目前还提供了apache和nginx的插件支持,同时也可以不使用对应的插件,直接以Web静态资源方式对外提供下载。

目前FastDFS(V4.x)代码量大概6w多行,内部的网络模型使用比较成熟的libevent三方库,具备高并发的处理能力。

  • 特性

1)在上述介绍中Tracker服务器是整个系统的核心枢纽,其完成了访问调度(负载均衡),监控管理Storage服务器,由此可见Tracker的作用至关重要,也就增加了系统的单点故障,为此FastDFS支持多个备用的Tracker,虽然实际测试发现备用Tracker运行不是非常完美,但还是能保证系统可用。
2)在文件同步上,只有同组的Storage才做同步,由文件所在的源Storage服务器push至其它Storage服务器,目前同步是采用Binlog方式实现,由于目前底层对同步后的文件不做正确性校验,因此这种同步方式仅适用单个集群点的局部内部网络,如果在公网上使用,肯定会出现损坏文件的情况,需要自行添加文件校验机制。
3)支持主从文件,非常适合存在关联关系的图片,在存储方式上,FastDFS在主从文件ID上做取巧,完成了关联关系的存储。

  • 优点

1)系统无需支持POSIX(可移植操作系统),降低了系统的复杂度,处理效率更高
2)支持在线扩容机制,增强系统的可扩展性
3)实现了软RAID,增强系统的并发处理能力及数据容错恢复能力
4)支持主从文件,支持自定义扩展名
5)主备Tracker服务,增强系统的可用性

  • 缺点

1)不支持断点续传,对大文件将是噩梦(FastDFS不适合大文件存储)
2)不支持POSIX通用接口访问,通用性较低
3)对跨公网的文件同步,存在较大延迟,需要应用做相应的容错策略
4)同步机制不支持文件正确性校验,降低了系统的可用性
5)通过API下载,存在单点的性能瓶颈

  • 应用场景

1)单集群部署的应用
2)存储后基本不做改动
3)小中型文件根据
目前官方提供的材料,现有的使用FastDFS系统存储容量已经达到900T,物理机器已经达到100台(50个组)

 安装指导_FastDFS

 源码路径:https://github.com/happyfish100/fastdfs

  • 参考

 https://code.google.com/p/fastdfs/ 

 http://bbs.chinaunix.net/forum-240-1.html

 http://portal.ucweb.local/docz/spec/platform/datastore/fastdfs

MooseFS

MooseFS是一个高可用的故障容错分布式文件系统,它支持通过FUSE方式将文件挂载操作,同时其提供的web管理界面非常方便查看当前的文件存储状态。

  • 特性

1)从下图中我们可以看到MooseFS文件系统由四部分组成:Managing Server 、Data Server 、Metadata Backup Server 及Client
2)其中所有的元数据都是由Managing Server管理,为了提高整个系统的可用性,Metadata Backup Server记录文件元数据操作日志,用于数据的及时恢复
3)Data Server可以分布式部署,存储的数据是以块的方式分布至各存储节点的,因此提升了系统的整体性能,同时Data Server提供了冗余备份的能力,提升系统的可靠性
4)Client通过FUSE方式挂载,提供了类似POSIX的访问方式,从而降低了Client端的开发难度,增强系统的通用性

  • 元数据服务器(master):负责各个数据存储服务器的管理,文件读写调度,文件空间回收以及恢复

  • 元数据日志服务器(metalogger):负责备份master服务器的变化日志文件,以便于在master server出问题的时候接替其进行工作

  • 数据存储服务器(chunkserver):数据实际存储的地方,由多个物理服务器组成,负责连接管理服务器,听从管理服务器调度,提供存储空间,并为客户提供数据传输;多节点拷贝;在数据存储目录,看不见实际的数据

  • 优点

1)部署安装非常简单,管理方便
2)支持在线扩容机制,增强系统的可扩展性
3)实现了软RAID,增强系统的 并发处理能力及数据容错恢复能力
4)数据恢复比较容易,增强系统的可用性5)有回收站功能,方便业务定制

  • 缺点

1)存在单点性能瓶颈及单点故障
2)MFS Master节点很消耗内存
3)对于小于64KB的文件,存储利用率较低

  • 应用场景

1)单集群部署的应用
2)中、大型文件

  • 参考

 http://portal.ucweb.local/docz/spec/platform/datastore/moosefsh 

 http://www.moosefs.org/ 

 http://sourceforge.net/projects/moosefs/?source=directory

GlusterFS

GlusterFS是Red Hat旗下的一款开源分布式文件系统,它具备高扩展、高可用及高性能等特性,由于其无元数据服务器的设计,使其真正实现了线性的扩展能力,使存储总容量可 轻松达到PB级别,支持数千客户端并发访问;对跨集群,其强大的Geo-Replication可以实现集群间数据镜像,而且是支持链式复制,这非常适用 于垮集群的应用场景

  • 特性

1)目前GlusterFS支持FUSE方式挂载,可以通过标准的NFS/SMB/CIFS协议像访问本体文件一样访问文件系统,同时其也支持HTTP/FTP/GlusterFS访问,同时最新版本支持接入Amazon的AWS系统
2)GlusterFS系统通过基于SSH的命令行管理界面,可以远程添加、删除存储节点,也可以监控当前存储节点的使用状态
3)GlusterFS支持集群节点中存储虚拟卷的扩容动态扩容;同时在分布式冗余模式下,具备自愈管理功能,在Geo冗余模式下,文件支持断点续传、异步传输及增量传送等特点

Yuyj GlusterFS.png

  • 优点

1)系统支持POSIX(可移植操作系统),支持FUSE挂载通过多种协议访问,通用性比较高
2)支持在线扩容机制,增强系统的可扩展性
3)实现了软RAID,增强系统的 并发处理能力及数据容错恢复能力
4)强大的命令行管理,降低学习、部署成本
5)支持整个集群镜像拷贝,方便根据业务压力,增加集群节点
6)官方资料文档专业化,该文件系统由Red Hat企业级做维护,版本质量有保障

  • 缺点

1)通用性越强,其跨越的层次就越多,影响其IO处理效率
2)频繁读写下,会产生垃圾文件,占用磁盘空间

  • 应用场景

1)多集群部署的应用
2)中大型文件根据目前官方提供的材料,现有的使用GlusterFS系统存储容量可轻松达到PB

  • 术语:

brick:分配到卷上的文件系统块;
client:挂载卷,并对外提供服务;
server:实际文件存储的地方;
subvolume:被转换过的文件系统块;
volume:最终转换后的文件系统卷。

  • 参考

  http://www.gluster.org/

  http://www.gluster.org/wp-content/uploads/2012/05/Gluster_File_System-3.3.0-Administration_Guide-en-US.pdf

  http://blog.csdn.net/liuben/article/details/6284551

Ceph

Ceph是一个可以按对象/块/文件方式存储的开源分布式文件系统,其设计之初,就将单点故障作为首先要解决的问题,因此该系统具备高可用性、高性能及可 扩展等特点。该文件系统支持目前还处于试验阶段的高性能文件系统BTRFS(B-Tree文件系统),同时支持按OSD方式存储,因此其性能是很卓越的, 因为该系统处于试商用阶段,需谨慎引入到生产环境

  • 特性

1)Ceph底层存储是基于RADOS(可靠的、自动的分布式对象存储),它提供了LIBRADOS/RADOSGW/RBD/CEPH FS方式访问底层的存储系统,如下图所示
2)通过FUSE,Ceph支持类似的POSIX访问方式;Ceph分布式系统中最关键的MDS节点是可以部署多台,无单点故障的问题,且处理性能大大提升
3)Ceph通过使用CRUSH算法动态完成文件inode number到object number的转换,从而避免再存储文件metadata信息,增强系统的灵活性

  • 优点

1)支持对象存储(OSD)集群,通过CRUSH算法,完成文件动态定位, 处理效率更高
2)支持通过FUSE方式挂载,降低客户端的开发成本,通用性高
3)支持分布式的MDS/MON,无单点故障
4)强大的容错处理和自愈能力5)支持在线扩容和冗余备份,增强系统的可靠性

  • 缺点

1)目前处于试验阶段,系统稳定性有待考究

  • 应用场景

1)全网分布式部署的应用
2)对实时性、可靠性要求比较高官方宣传,存储容量可轻松达到PB级别

 源码路径:https://github.com/ceph/ceph

  • 参考

  http://ceph.com/

MogileFS

  • 开发语言:perl

  • 开源协议:GPL

  • 依赖数据库

  • Trackers(控制中心):负责读写数据库,作为代理复制storage间同步的数据

  • Database:存储源数据(默认mysql)

  • Storage:文件存储

  • 除了API,可以通过与nginx集成,对外提供下载服务

 源码路径:https://github.com/mogilefs

  • 参考

 https://code.google.com/p/mogilefs/wiki/Start?tm=6

 其它参考

 http://blog.csdn.net/qiangweiloveforever/ariticle/details/7566779

 http://weiruoyu.blog.51cto.com/951650/786607 

 http://m.blog.csdn.net/blog/junefsh/18079733

转载请标明本文来源:http://www.cnblogs.com/yswenli/p/7234579.html
更多内容欢迎我的的github:https://github.com/yswenli
如果发现本文有什么问题和任何建议,也随时欢迎交流~

返回《8天掌握EF的Code First开发》总目录


本篇目录


本系列的源码本人已托管于coding上:**点击查看**
先附上codeplex上EF的源码:**entityframework.codeplex.com**,此外,本人的实验环境是VS 2013 Update 5,windows 10,MSSQL Server 2008。

咱们接着上一篇**《Code First开发系列之管理数据库创建,填充种子数据以及LINQ操作详解》**继续深入学习,这一篇说说Entity Framework之Code First方式如何使用视图,存储过程以及EF提供的一些异步接口。我们会看到如何充分使用已存在的存储过程和函数来检索、修改数据。此外,我们还要理解异步处理的优势以及EF是如何通过内置的API来支持这些概念的。

视图View

视图在RDBMS中扮演了一个重要的角色,它是将多个表的数据联结成一种看起来像是一张表的结构,但是没有提供持久化。因此,可以将视图看成是一个原生表数据顶层的一个抽象。例如,我们可以使用视图提供不同安全的级别,也可以简化必须编写的查询,尤其是我们可以在代码中的多个地方频繁地访问使用视图定义的数据。EF Code First现在还不完全支持视图,因此我们必须使用一种变通方法。这种方法就是将视图真正看成是一张表,让EF定义这张表,然后再删除它,最后再创建一个代替它的视图。下面具体看看是如何实现的吧。

创建一个控制台项目,取名“ViewsAndStoreProcedure”。

1 创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

public class Province
{
public Province()
{
Donators = new HashSet<Donator>();
}
public int Id { get; set; }

[StringLength(225)]
public string ProvinceName { get; set; }

public virtual ICollection<Donator> Donators { get; set; }
}

public class Donator
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime DonateDate { get; set; }
public virtual Province Province { get; set; }
}


创建模拟视图类

暂且这样称呼吧,就是从多个实体中取出想要的列组合成一个实体。

1
2
3
4
5
6
7
8
9
10
11
public class DonatorViewInfo
{
public int DonatorId { get; set; }
public string DonatorName { get; set; }
public decimal Amount { get; set; }
public DateTime DonateDate { get; set; }
[StringLength(225)]
public string ProvinceName { get; set; }
}


为模拟视图类创建配置类

下面的代码指定了主键和表名(也是视图的名字,注意这里的表名一定要和创建视图的语句中的视图名一致):

1
2
3
4
5
6
7
8
9
10
public class DonatorViewInfoMap:EntityTypeConfiguration<DonatorViewInfo>
{
public DonatorViewInfoMap()
{
HasKey(d => d.DonatorId);
ToTable("DonatorViews");
}
}


上下文中添加模拟视图类和配置类

web.config文件中的连接字符串我已配置好,不在此处展示!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DonatorsContext : DbContext
{
public DonatorsContext()
: base("name=DonatorsConn")
{
}

public virtual DbSet<DonatorViewInfo> DonatorViews { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new DonatorViewInfoMap());
base.OnModelCreating(modelBuilder);
}
}


创建初始化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Initializer:DropCreateDatabaseIfModelChanges<DonatorsContext>
{
protected override void Seed(DonatorsContext context)
{
var drop = "Drop Table DonatorView";
context.Database.ExecuteSqlCommand(drop);
var createView = @"CREATE VIEW [dbo].[DonatorViews]
AS SELECT
dbo.Donators.Id AS DonatorId,
dbo.Donators.Name AS DonatorName,
dbo.Donators.Amount AS Amount,
dbo.Donators.DonateDate AS DonateDate,
dbo.Provinces.ProvinceName AS ProvinceName
FROM dbo.Donators
INNER JOIN dbo.Provinces ON dbo.Provinces.Id = dbo.Donators.ProvinceId";
context.Database.ExecuteSqlCommand(createView);
base.Seed(context);
}
}


上面的代码中,我们先使用Database对象的ExecuteSqlCommand方法销毁生成的表,然后又调用该方法创建了我们的视图。该方法在允许开发者对后端执行任意的SQL代码时很有用。

上面的代码写完之后,在Main方法中只要写这一句代码 Database.SetInitializer(new Initializer());,运行程序,就会看到数据库中已经生成了Donators和Provinces两张表和一个视图DonatorView,见下图:

图片

刚才新建的数据库是没有数据的,然后我们插入数据,在数据库中查询一下,可以看到视图中已经存在数据了:

图片

下面,一切工作准备就绪,就可以开始查询数据了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#region 1.0 视图
Database.SetInitializer(new Initializer());
using (var db = new DonatorsContext())
{
var donators = db.DonatorViews;
foreach (var donator in donators)
{
Console.WriteLine(donator.ProvinceName + "\t" + donator.DonatorId + "\t" + donator.DonatorName + "\t" + donator.Amount + "\t" + donator.DonateDate);
}
}

#endregion


执行结果如下图所示:

图片

正如上面的代码所示,访问视图和任何数据表在代码层面没有区别,需要注意的地方就是在Seed方法中定义的视图名称要和定义的表名称一致,否则就会因为找不到表对象而报错,这一点要格外注意。

虽然视图看起来很像一张表,但是如果我们尝试修改或更新视图中定义的实体,那么就会抛异常。

另一种方法

如果我们不想这么折腾(先定义一张表,然后删除这张表,再定义视图),当然了,我们还是要在初始化器中定义视图,但是我们使用Database对象的另一个方法SqlQuery查询数据。该方法和ExecuteSqlCommand方法有相同的形参,但是最终返回一个结果集,在我们这里例子中,返回的就是DonatorViewInfo集合对象,如下代码所示:

1
2
3
4
5
6
7
8
9

var sql = @"SELECT DonatorId ,DonatorName ,Amount ,DonateDate ,ProvinceName from dbo.DonatorViews where ProvinceName={0}";
var donatorsViaCommand = db.Database.SqlQuery<DonatorViewInfo>(sql,"河北省");
foreach (var donator in donatorsViaCommand)
{
Console.WriteLine(donator.ProvinceName + "\t" + donator.DonatorId + "\t" + donator.DonatorName + "\t" + donator.Amount + "\t" + donator.DonateDate);
}


SqlQuery方法需要一个泛型类型参数,该参数定义了原生SQL命令执行之后,将查询结果集物质化成何种类型的数据。该文本命令本身就是参数化的SQL。我们需要使用参数来确保动态sql不是SQL注入的目标。SQL注入是恶意用户通过提供特定的输入值执行任意SQL代码的过程。EF本身不是这些攻击的目标。

我们不仅看到了如何在EF中使用视图,而且看到了两个很有用的Database对象,SqlQueryExecuteSqlCommand方法。SqlQuery方法的泛型参数不一定非得是一个类,也可以.Net的基本类型,如string或者int。

执行结果如下:

图片

存储过程

在EF中使用存储过程和使用视图是很相似的,一般会使用Database对象上的两个方法——SqlQueryExecuteSqlCommand。为了从存储过程中读取很多数据行,我们只需要定义一个类,我们会将检索到的所有数据行物质化到该类实例的集合中。比如,从下面的存储过程读取数据:

1
2
3
4
5
6
7
8
9
10
CREATE PROCEDURE SelectDonators
@provinceName AS NVARCHAR(10)
AS
BEGIN
SELECT ProvinceName,Name,Amount,DonateDate FROM dbo.Donators
JOIN dbo.Provinces ON dbo.Provinces.Id = dbo.Donators.ProvinceId
WHERE ProvinceName=@provinceName
END


我们只需要定义一个匹配了存储过程结果的类(类的属性名必须和表的列名一致)即可,如下所示:

1
2
3
4
5
6
7
8
9
public class DonatorFromStoreProcedure
{
public string ProvinceName { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
public DateTime DonateDate { get; set; }
}


还是插入以下数据进行测试:

1
2
3
4
5
6
7
8
9
INSERT dbo.Provinces VALUES( N'山东省')
INSERT dbo.Provinces VALUES( N'河北省')

INSERT dbo.Donators VALUES ( N'陈志康', 50, '2016-04-07',1)
INSERT dbo.Donators VALUES ( N'海风', 5, '2016-04-08',1)
INSERT dbo.Donators VALUES ( N'醉、千秋', 12, '2016-04-13',1)
INSERT dbo.Donators VALUES ( N'雪茄', 18.8, '2016-04-15',2)
INSERT dbo.Donators VALUES ( N'王小乙', 10, '2016-04-09',2)

现在我们就可以使用SqlQuery方法读取数据了(注意:在使用存储过程前,先要在数据库中执行存储过程),如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#region 2.0 EF调用存储过程查询数据SqlQuery

using (var db=new DonatorsContext())
{
var sql = "SelectDonators {0}";
var donators = db.Database.SqlQuery<DonatorFromStoreProcedure>(sql,"山东省");
foreach (var donator in donators)
{
Console.WriteLine(donator.ProvinceName+"\t"+donator.Name+"\t"+donator.Amount+"\t"+donator.DonateDate);
}
}
#endregion


上面的代码中,我们指定了使用哪个类读取查询的结果,创建SQL语句时,也为存储过程的参数提供了一个格式化占位符,调用SqlQuery时为那个参数提供了一个值。假如要提供多个参数的话,多个格式化占位符必须用逗号分隔,还要给SqlQuery提供值的数组(后面会举例)。我们也可以使用表值函数代替存储过程。

存储过程成功执行,结果如下:

图片

另一个用例就是假如存储过程没有返回任何值,只是对数据库中的一张或多张表执行了一条命令的情况。一个存储过程干了多少事情不重要,重要的是它压根不需要返回任何东西。例如,下面的存储过程只是更新了一些东西:

1
2
3
4
5
6
7
8
9
10
CREATE PROCEDURE UpdateDonator
@namePrefix AS NVARCHAR(10),
@addedAmount AS DECIMAL
AS

BEGIN
UPDATE dbo.Donators SET Name=@namePrefix+Name,Amount=Amount+@addedAmount
WHERE ProvinceId=2
END

现在数据库中执行该存储过程,然后,要调用该存储过程,我们使用ExecuteSqlCommand方法。该方法会返回存储过程或者其他任何SQL语句影响的行数。如果你对这个返回值不感兴趣,那么你可以不理它。下面小试牛刀一把:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

using (var db = new DonatorsContext())
{
var sql = "UpdateDonator {0},{1}";
Console.WriteLine("执行存储过程前的数据为:");
PrintDonators();
var rowsAffected = db.Database.ExecuteSqlCommand(sql, "Update", 10m);
Console.WriteLine("影响的行数为{0}条", rowsAffected);
Console.WriteLine("执行存储过程之后的数据为:");
PrintDonators();
}


static void PrintDonators()
{
using (var db = new DonatorsContext())
{
var donators = db.Donators.Where(p => p.ProvinceId == 2);
foreach (var donator in donators)
{
Console.WriteLine(donator.Name + "\t" + donator.Amount + "\t" + donator.DonateDate);
}
}
}


这里我们为上面定义的存储过程提供了两个参数,一个是在每个打赏者的姓名前加个前缀“Update”,另一个是将打赏金额加10。这里需要注意的是,我们必须严格按照它们在存储过程中定义的顺序依次传入相应的值,它们会以参数数组传入ExecuteSqlCommand。执行结果如下:

图片

很大程度上,EF降低了存储过程的需要,然而,仍旧有很多原因要使用它们。这些原因包括安全标准,遗留数据库或者效率等问题。比如,如果需要在单个操作中更新几千条数据,然后再通过EF检索出来;如果每次都更新一行,然后再保存那些实例,效率是很低的。最后,即使你使用了SqlQuery方法调用了存储过程,也可以更新数据。

开发者可以执行任意的SQL语句,只需要将上面SqlQueryExecuteSqlCommand方法中的存储过程名称改为要执行的SQL语句就可以了。

使用存储过程CUD

至今,我们都是使用EF内置的功能生成插入,更新或者删除实体的SQL语句,总有某种原因使我们想使用存储过程来实现相同的结果。开发者可能会为了安全原因使用存储过程,也可能是要处理一个已存在的数据库,而这些存储过程已经内置到该数据库了。

EF Code First全面支持这些查询。我们可以使用熟悉的EntityTypeConfiguration类来给存储过程配置该支持,只需要简单地调用MapToStoredProcedures方法就可以了。如果我们让EF管理数据库结构,那么它会自动为我们生成存储过程。此外,我们还可以使用MapToStoredProcedures方法合适的重载来重写存储过程名称或者参数名。下面以donator类为例:

1
2
3
4
5
6
7
8
9
public class DonatorMap:EntityTypeConfiguration<Donator>
{
public DonatorMap()
{
MapToStoredProcedures();
}
}


如果我们运行程序来创建或更新数据库,就会看到为我们创建了新的存储过程,默认为插入操作生成了Donator_Insert,其他的操作名称类似,如下图:

图片

如果有必要的话,我们可以自定义存储过程名,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class DonatorMap:EntityTypeConfiguration<Donator>
{
public DonatorMap()
{

MapToStoredProcedures(config =>
{


config.Delete(
procConfig =>
{
procConfig.HasName("DonatorDelete");
procConfig.Parameter(d => d.Id, "donatorId");
});


config.Insert(
procConfig =>
{
procConfig.HasName("DonatorInsert");
});

config.Update(procConfig =>
{
procConfig.HasName("DonatorUpdate");
});
});
}
}


总之,要自定义的话,代码肯定更冗余,不管怎样了,取决于你!

异步API

目前为止,我们所有使用EF的数据库操作都是同步的。换言之,我们的.NET程序会等待给定的数据库操作(例如一个查询或者一个更新)完成之后才会继续向前执行。在很多情况下,使用这种方式没有什么问题,然而,在某些情况下,异步地执行这些操作的能力是很重要的。在这些情况下,当该软件等待数据库操作完成时,我们让.Net使用它的的执行线程。例如,如果使用了异步的方式在创建一个Web应用,当我们等待数据库完成处理一个请求(无论它是一个保存还是检索操作)时,通过将web工作线程释放回线程池,就可以更有效地利用服务器资源。

即使在桌面应用中,异步API也很有用,因为用户可能会潜在执行应用中的其他任务,而不是等待一个可能耗时的查询或保存操作。换言之,.Net线程不需要等待数据库线程完成跟数据库有关的工作。在许多应用程序中,异步API没有带来好处,从性能的角度来说,甚至可能是有害的,因为线程上下文的切换开销。因此,在使用异步API之前,开发者需要确定使用异步API会让你受益!

EF暴露了很多异步操作,按照约定,所有的这些方法都以Async后缀结尾。对于保存操作,我们可以使用DbContext上的SaveChangesAsync方法。也有很多查询的方法,比如,许多聚合函数都有异步副本,比如SumAsyncAverageAsync。还可以使用ToListAsyncToArrayAsync将一个结果集读入到一个list或者array中。此外,还可以使用ForEachAsync方法对一个查询结果进行枚举。

异步地从数据库中获取对象的列表

1
2
3
4
5
6
7
8
9
10
11
12
13
#region 3.0 异步API


static async Task<IEnumerable<Donator>> GetDonatorsAsync()
{
using (var db = new DonatorsContext())
{
return await db.Donators.ToListAsync();
}
}

#endregion

值得注意的是,这里使用了典型的async/await用法模式。函数被标记为 async并返回一个task对象,确切地说是一个Donator集合的task。然后,调用了DbContext的集合属性创建了一个返回所有Donator的查询。然后,使用ToListAsync扩展方法对该查询结果进行枚举。最后,由于我们需要遵守async/await模式,所以必须等待返回值。

任何EF查询都可以使用ToListAsync或者ToArrayAsync转换成异步版本。

异步创建一个新的对象

1
2
3
4
5
6
7
8
9
10
11

static async Task InsertDonatorAsync(Donator donator)
{
using (var db = new DonatorsContext())
{
db.Donators.Add(donator);
await db.SaveChangesAsync();
}
}


代码很简单,和一般的同步模式比较,只是返回类型为Task,方法多了async修饰,调用了SaveChangesAsync方法,同时注意,自己定义的方法最好也以Async后缀结尾,不是必须的,只是为了遵守规范。

异步定位一条记录

我们可以异步定位一条记录,可以使用很多方法,比如SingleFirst,这两个方法都有异步版本。

1
2
3
4
5
6
7
8
9
10

static async Task<Donator> FindDonatorAsync(int donatorId)
{
using (var db = new DonatorsContext())
{
return await db.Donators.FindAsync(donatorId);
}
}


一般来说,就参数而言,EF中的所有异步方法和它们的同步副本都有相同的方法签名。

异步聚合函数

对应于同步版本,异步聚合函数包括这么几个方法,MaxAsync,MinAsync,CountAsync,SumAsync,AverageAsync

1
2
3
4
5
6
7
8
9
10

static async Task<int> GetDonatorCountAsync()
{
using (var db = new DonatorsContext())
{
return await db.Donators.CountAsync();
}
}


异步遍历查询结果

如果要对查询结果进行异步遍历,可以使用ForEachAsync,可以在任何查询之后使用该方法。比如,下面将每个打赏者的打赏日期设置为今天。

1
2
3
4
5
6
7
8
9
10
11
12
13

static async Task LoopDonatorsAsync()
{
using (var db = new DonatorsContext())
{
await db.Donators.ForEachAsync(d =>
{
d.DonateDate=DateTime.Today;
});
}
}


如果要在一个同步方法中使用一个异步方法,那么我们可以使用Task的API等待一个任务完成。比如,我们可以访问task的Result属性,这会造成当前的线程暂停并且让该task完成执行,但一般不建议这么做,最佳实践是总是使用async
同步方法中调用异步方法的代码如下:

1
2
Console.WriteLine(FindDonatorAsync(1).Result.DonateDate);

上面这句代码在Main方法中,调用了之前定义的异步方法,然后访问了该Task的Result属性,这会造成异步函数完成执行。

当决定是否使用异步API的时候,首先要研究一下,并确定为什么要使用异步API。既然用了异步API,为了获得最大的编码好处,就要确保整个方法的调用连都是异步的。最后,当需要时在使用Task API。

本章小结

EF给开发者带来了很大价值,允许我们使用C#代码管理数据库数据。然而,有时我们需要通过动态的SQL语句或者存储过程,更直接地对视图访问数据,就可以使用ExecuteSqlCommand方法来执行任意的SQL代码,包括原生SQL或者存储过程。也可以使用SqlQuery方法从视图、存储过程或任何SQL语句中检索数据,EF会基于我们提供的结果类型物质化查询结果。当给这两个方法提供参数时,避免SQL注入漏洞很重要。

EF也可以自动为实体生成插入、更新和删除的存储过程,假如你对这些存储过程的命名规范和编码标准满意的话,我们只需要在配置伙伴类中写一行代码就可以了。

EF也提供了异步操作支持,包括查询和更新。为了避免潜在的性能影响,开发者使用这些技术时务必谨慎。在某些技术中,异步API很适合,Web API就是一个好的例子。

自我测试

  1. EF不能从视图获取数据,对吗?
  2. 哪个数据库方法可以使用SQL语句查询和检索数据?
    1. ExecuteSqlCommand
    2. Execute
    3. SqlQuery
  3. 如果你想为一个实体类型的CRUD操作映射到一组存储过程,就必须手动写所有的SQL Server存储过程,对吗?
  4. 使用异步API没有负面影响,对吗?
  5. DbContext中用于异步保存更改的是什么方法?

如果您觉得这篇文章对您有价值或者有所收获,请点击右下方的店长推荐,谢谢!


参考书籍:
《Mastering Entity Framework》
《Code-First Development with Entity Framework》
《Programming Entity Framework Code First》

【Kubernetes 系列】ConfigMap 进阶 环境变量的配置及使用_yaml文件中如何引用configmap中定义的变量-CSDN博客

Excerpt

文章浏览阅读4.6k次,点赞53次,收藏47次。ConfigMap API 资源将配置数据存储为键值对。 数据可以在 Pod 中使用,也可以用来提供系统组件(如控制器)的配置。 ConfigMap 与 Secret 类似, 但是提供的是一种处理不含敏感信息的字符串的方法。 用户和系统组件都可以在 ConfigMap 中存储配置数据。…_yaml文件中如何引用configmap中定义的变量


作者:半身风雪
上一节:配置 Java 微服务
内容简介:上一节主要学习创建ConfigMap 的九种方式,那么本节内容主要讲解 ConfigMap 的使用。

在这里插入图片描述


文章目录


目标

本节学习目标:

  • 在ConfigMap 中的容器环境变量
  • ConfigMap volume中的数据处理

提示:以下是本篇文章正文内容,下面案例可供参考

一、ConfigMap 容器环境变量的设置

1.1、使用单个 ConfigMap 中的数据定义容器环境变量

  1. 在 ConfigMap 中将环境变量定义为键值对:

$ kubectl create configmap special-config –from-literal=special.how=very

  1. 将 ConfigMap 中定义的 special.how 赋值给 Pod 规约中的 SPECIAL_LEVEL_KEY 环境变量。在 pods/pod-single-configmap-env-variable.yaml 文件目录中添加如下代码:
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] env: # 定义环境变量 - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: # ConfigMap 包含你要赋给 SPECIAL_LEVEL_KEY 的值 name: special-config # 指定与取值相关的键名 key: special.how restartPolicy: Never
  1. 执行 kubectl create -f 命令,创建pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-single-configmap-env-variable.yaml

Pod 的输出包含环境变量 SPECIAL_LEVEL_KEY=very。

1.2、使用来自多个 ConfigMap 的数据定义容器环境变量

  1. 在目录 configmap/configmaps.yaml 添加如下代码:
1
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO
  1. 执行命令创建 ConfigMap:

$ kubectl create -f https://kubernetes.io/examples/configmap/configmaps.yaml

  1. 在目录 pods/pod-multiple-configmap-env-variable.yaml 中定义环境变量:
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: LOG_LEVEL valueFrom: configMapKeyRef: name: env-config key: log_level restartPolicy: Never
  1. 执行命令创建 Pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-multiple-configmap-env-variable.yaml

Pod 的输出包含环境变量 SPECIAL_LEVEL_KEY=very 和 LOG_LEVEL=INFO。

1.3、使用来自多个 ConfigMap 的数据定义容器环境变量

  1. 在目录 configmap/configmaps.yaml 添加如下代码:
1
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: special.how: very --- apiVersion: v1 kind: ConfigMap metadata: name: env-config namespace: default data: log_level: INFO
  1. 创建 ConfigMap:

$ kubectl create -f https://kubernetes.io/examples/configmap/configmaps.yaml

  1. 在目录 pods/pod-multiple-configmap-env-variable.yaml 中定义环境变量:
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: special.how - name: LOG_LEVEL valueFrom: configMapKeyRef: name: env-config key: log_level restartPolicy: Never
  1. 执行命令创建 Pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-multiple-configmap-env-variable.yaml

Pod 的输出包含环境变量 SPECIAL_LEVEL_KEY=very 和 LOG_LEVEL=INFO。

1.4、将 ConfigMap 中的所有键值对配置为容器环境变量

  1. 在目录 configmap/configmap-multikeys.yaml 中创建一个包含多个键值对的 ConfigMap。
1
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: SPECIAL_LEVEL: very SPECIAL_TYPE: charm
  1. 创建 ConfigMap:

$ kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml

  1. pods/pod-configmap-envFrom.yaml 目录中,使用 envFrom 将所有 ConfigMap 的数据定义为容器环境变量,ConfigMap 中的键成为 Pod 中的环境变量名称。
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] envFrom: - configMapRef: name: special-config restartPolicy: Never
  1. 创建 Pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-envFrom.yaml

Pod 的输出包含环境变量 SPECIAL_LEVEL=very 和 SPECIAL_TYPE=charm。

1.5、在 Pod 命令中使用 ConfigMap 定义的环境变量

  1. 在目录 pods/pod-configmap-env-var-valueFrom.yaml 中使用**$(VAR_NAME)** Kubernetes 替换语法在容器的 commandargs 属性中使用 ConfigMap 定义的环境变量。
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/echo", "$(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: special-config key: SPECIAL_LEVEL - name: SPECIAL_TYPE_KEY valueFrom: configMapKeyRef: name: special-config key: SPECIAL_TYPE restartPolicy: Never
  1. 运行下面命令创建 Pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-env-var-valueFrom.yaml

  1. 会在 test-container 容器中产生以下输出:
1
very charm

二、将 ConfigMap 数据添加到volume中

使用 --from-file 创建 ConfigMap 时,文件名成为存储在 ConfigMap 的 data 部分中的键, 文件内容成为键对应的值。

下面看一个示例configmap/configmap-multikeys.yaml

1
apiVersion: v1 kind: ConfigMap metadata: name: special-config namespace: default data: SPECIAL_LEVEL: very SPECIAL_TYPE: charm

创建 ConfigMap:

$ kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml

2.1、使用存储在 ConfigMap 中的数据填充volume

在 Pod 规约的 volumes 部分下添加 ConfigMap 名称。 会将 ConfigMap 数据添加到 volumeMounts.mountPath 所指定的目录 (在本例中为 /etc/config)。 command 部分列出了名称与 ConfigMap 中的键匹配的目录文件。

  1. 先看一下示例代码pods/pod-configmap-volume.yaml
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "ls /etc/config/" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: # 提供包含要添加到容器中的文件的 ConfigMap 的名称 name: special-config restartPolicy: Never
  1. 创建 Pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume.yaml

  1. Pod 运行时,命令 ls /etc/config/ 产生下面的输出:

SPECIAL_LEVEL
SPECIAL_TYPE

2.2、将 ConfigMap 数据添加到volume中的特定路径

使用 path 字段为特定的 ConfigMap 项目指定预期的文件路径。 在这里,ConfigMap 中键 SPECIAL_LEVEL 的内容将挂载在 config-volume 卷中 /etc/config/keys 文件中。

  1. pods/pod-configmap-volume-specific-key.yaml示例代码:
1
apiVersion: v1 kind: Pod metadata: name: dapi-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh","-c","cat /etc/config/keys" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config items: - key: SPECIAL_LEVEL path: keys restartPolicy: Never
  1. 创建Pod:

$ kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume-specific-key.yaml

  1. 当 Pod 运行时,命令 cat /etc/config/keys 产生以下输出:

very

三、ConfigMap 和 Pod

ConfigMap API 资源将配置数据存储为键值对。 数据可以在 Pod 中使用,也可以用来提供系统组件(如控制器)的配置。 ConfigMap 与 Secret 类似, 但是提供的是一种处理不含敏感信息的字符串的方法。 用户和系统组件都可以在 ConfigMap 中存储配置数据。

ConfigMap 的 data 字段包含配置数据。如下例所示,它可以简单 (如用 --from-literal 的单个属性定义)或复杂 (如用 --from-file 的配置文件或 JSON blob定义)。

1
apiVersion: v1 kind: ConfigMap metadata: creationTimestamp: 2016-02-18T19:14:38Z name: example-config namespace: default data: # 使用 --from-literal 定义的简单属性 example.property.1: hello example.property.2: world # 使用 --from-file 定义复杂属性的例子 example.property.file: |- property.1=value-1 property.2=value-2 property.3=value-3

3.1、限制

  • 在 Pod 规约中引用某个 ConfigMap 之前,必须先创建它(除非将 ConfigMap 标记为 “optional(可选)”)。如果引用的 ConfigMap 不存在,则 Pod 将不会启动。 同样,引用 ConfigMap 中不存在的主键也会令 Pod 无法启动。
  • 如果你使用 envFrom 来基于 ConfigMap 定义环境变量,那么无效的键将被忽略。 Pod 可以被启动,但无效名称将被记录在事件日志中(InvalidVariableNames)。 日志消息列出了每个被跳过的键。例如:

$ kubectl get events

输出与此类似:

LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE
0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentVariableNames {kubelet, 127.0.0.1} Keys [1badkey, 2alsobad] from the EnvFrom configMap default/myconfig were skipped since they are considered invalid environment variable names.

  • ConfigMap 位于确定的名字空间中。 每个 ConfigMap 只能被同一名字空间中的 Pod 引用.
  • 你不能将 ConfigMap 用于静态 Pod, 因为 Kubernetes 不支持这种用法。

总结

文章写到这里,我想大家已经ConfigMap 有了一定的了解,对于 ConfigMap 的讲解,到这里就结束了,大家快动手试试吧!!!

c#调用c++动态库一般我们这样写

[DllImport(“UCamer.dll”, CallingConvention = CallingConvention.Winapi)] public extern static void Disp_Destroy(IntPtr hShow);

DllImport的第一个参数UCamer.dll是动态库dll的路径,此dll放在程序运行的根目录或者c:windows/sytem32下

  CallingConvention 参数是c#调用c++的方式 是个枚举 msdn解释如下

Cdecl

调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。 

FastCall

不支持此调用约定。

StdCall

被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。 

ThisCall

第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。 

Winapi

此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE.NET 上默认为 Cdecl。 

 从上面来看Winapi方式是根据系统自动选择调用规约的。 而thisCall是对c++类的调用方法。 所以 一般情况下我们选择Winapi就可以了。

c#调用dll另一个难点:数据类型转换

http://wenku.baidu.com/link?url=SihlxtHC-HMcEhq3izpd2bux8rNaKOMTpu8NPqjdYlLSwYSV1CqNJdVbxkaZm7OqaaSTEK-KUJqX5jbtkdpnUZ_38No4tsrgqCsf7Th5dqK

百度文库这篇文章基本把c++与c#的对应数据类型总结完了。但是为什么这里还要说呢

1,百度文库这篇文章,包括大部分度娘的类型转换的资料中 对c++中的返回值类型char[] 都转换成了char[] 没做任何改变。

  c++中char占一个字节,assic编码。而c#中的char占2个字节(我是在中文版的vs中测试的)。

  如果这样转换就会出现问题,很容易发生越界,或者读写到受保护的内存等问题。

  解决方法是 把char[] 变成c#中的byte[] ,再用Encoding.Assic.getstring方法转换。

2,关于指针,c++所有的指针 在c#上用Intptr ,问题又来了。假如返回的Intptr是个数组指针,在c#中我们怎么读取数组里面的元素呢?

  Marshal.Copy();  

Marshal类是c#中专门把非托管内存转换成托管内存的神器,不需要unsafe。 Marshal中copy方法是最最常用的方法。里面有16个重载。能解决目前你能遇到的大部分问题。

msdn中对各个重载解释如下

名称

说明

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Byte[], Int32, IntPtr, Int32)

安全关键。将一维的托管 8 位无符号整数数组中的数据复制到非托管内存指针。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Char[], Int32, IntPtr, Int32)

安全关键。将数据从一维的托管字符数组复制到非托管内存指针。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Double[], Int32, IntPtr, Int32)

安全关键。将数据从一维的托管双精度浮点数组复制到非托管内存指针。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Int16[], Int32, IntPtr, Int32)

安全关键。将一维的托管 16 位有符号整数数组中的数据复制到非托管内存指针。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Int32[], Int32, IntPtr, Int32)

安全关键。将数据从一维的托管 32 位有符号整数数组复制到非托管内存指针。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Int64[], Int32, IntPtr, Int32)

安全关键。将一维的托管 64 位有符号整数数组中的数据复制到非托管内存指针。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Byte[], Int32, Int32)

安全关键。将数据从非托管内存指针复制到托管 8 位无符号整数数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Char[], Int32, Int32)

安全关键。将数据从非托管内存指针复制到托管字符数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Double[], Int32, Int32)

安全关键。将数据从非托管内存指针复制到托管双精度浮点数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Int16[], Int32, Int32)

安全关键。将非托管内存指针中的数据复制到托管 16 位有符号整数数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Int32[], Int32, Int32)

安全关键。将非托管内存指针中的数据复制到托管 32 位有符号整数数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Int64[], Int32, Int32)

安全关键。将非托管内存指针中的数据复制到托管 64 位有符号整数数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(IntPtr, Single[], Int32, Int32)

安全关键。将数据从非托管内存指针复制到托管单精度浮点数组。

公共方法 静态成员 受 Silverlight for Windows Phone 支持 受 Xbox 360 支持

Copy(Single[], Int32, IntPtr, Int32)

安全关键。将数据从一维的托管单精度浮点数组复制到非托管内存指针。

我们也可以使用共享内存的方式进行操作。部分代码如下

复制代码

//初始化返回图片的大小等信息
myContext = new VlcControlWpfRendererContext(width, height, System.Windows.Media.PixelFormats.Bgr24); //创建共享内存区域
myBitmapSectionPointer = Win32Interop.CreateFileMapping(new IntPtr(-1), IntPtr.Zero, Win32Interop.PageAccess.ReadWrite, 0, myContext.Size, null); //获取共享内存的首地址
map = Win32Interop.MapViewOfFile(myBitmapSectionPointer, Win32Interop.FileMapAccess.AllAccess, 0, 0, (uint)myContext.Size); //把接收后的图片拷入共享内存区域
Win32Interop.CopyMemory(map, data, myContext.Size);
//把共享内存中的数组转换为图片
myBitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(myBitmapSectionPointer, myContext.Width, myContext.Height, myContext.PixelFormat, myContext.Stride, 0);

复制代码

这里是共享内存的辅助类  主要是一些api函数

其实Intptr本质也是一个Int,Int在c#中和Int32是一样的。所以基本上指针,Long,int在c#中都是int。只是这样些 方便大家知道他是c++中的什么类型,方便转换而已。

3,c++中的函数指针  与c#中的委托

这是c++中对函数指针的定义

typedef VOID (WINAPI *PUSERCALL)( PUCHAR pData, ULONG Length, PVOID pUserData );

对应c#中的例子如下

public delegate void PUSERCALL(IntPtr pData, uint Length, UInt32 pUserData);

4,我们知道int是占4个字节的。 

  下面这个是c++的一个方法

U_CAMER LONG WINAPI CAMER_GetPropery( HANDLE hCamer, _CMRCTL Propery );

假如我们把此函数翻译成c#中的下面这个函数

[DllImport(“UCamer.dll”, CallingConvention = CallingConvention.Winapi)] public extern static Uint16 CAMER_GetPropery(IntPtr hCamer, CMRCTL Propery);

我们在c#调用此方法

uint16 m_HiWi_temp = (uint)BCamera.CAMER_GetPropery(m_hCamer, CMRCTL.OUT_SIZE);

发现一个很有趣的问题,此处调用没有问题。也有值,但是他是取的int32中4个字节的2个字节。

我们看原本c++对此函数的调用

*((PULONG)m_HiWi) = *((PULONG)m_Display) = CAMER_GetPropery( m_hCamer, OUT_SIZE );
m_hShow = Disp_Create( m_hWnd, m_HiWi[1], m_HiWi[0], m_nColor, (USERDRAW)((m_ReDrawLine == TRUE) ? DrawLine : NULL), this );

本来CAMER_GetPropery函数只返回了一个long类型。c++中通过指针的转换。把long类型转换成了 pulong,也是ulong的数组。

那c#中怎么我们该怎么调用呢

int m_HiWi_temp = BCamera.CAMER_GetPropery(m_hCamer, CMRCTL.OUT_SIZE); byte[] m_byte_HiWi = BitConverter.GetBytes(m_HiWi_temp); byte[] temp1 = new byte[2] { m_byte_HiWi[0], m_byte_HiWi[1] }; byte[] temp2 = new byte[2] { m_byte_HiWi[2], m_byte_HiWi[3] }; int width = BitConverter.ToInt16(temp1, 0); int high = BitConverter.ToInt16(temp2, 0);

这里举这个例子是说c++有时真的就是返回一个int类型,但是在c++中可以轻松把int类型通过指针轻松转换成两个uint16的数组。所以c#中我们再转换的时候一定有注意了。

总结:其实数据类型的转换主要是对数据存储空间的转换,c++中的数据类型占用多大的空间,只要转换成c#中占同等空间的数据类型就可以了。只是c#看哪种数据类型在操作相应的操作方便些。

目前正在做的一个项目,大部分数据来源都是通过调用c++函数得到的,此时就遇到一个这次要说的问题。

如c++函数有个定时器,会定时调用我们c#的某个函数并把数据传给c#,让c#把数据显示到界面上,在c++中有个回调函数指针的概念,只需要某个

函数在调用定时器函数时传入一个函数指针就能达到目的,但C#中没有函数指针的概念,我们该怎样来实现呢。

其实说到回调函数,大家应该能想到c#中的委托,虽然名字不一样,但在各自的语言范畴都能实现相似的功能。所以我们就可以大胆的尝试下,把c#中的委托传给

c++,看c++是否能够承认它就是回调函数。

首先用c++写一个带有回调函数的方法 Test,在此省略。

接着,在c#中调用,

如:

[DllImport(“Test.dll”,ChartSet.Ansi,EntryPoint=”ReadMyVideo”,ExactSpelling=false,CallingConvertion=CallingConvertion.StdCall)]

private static extern void Test(string fileName,CallbackDelegate callback);

接下来我们再定义一个委托: 

public delegate void CallbackDelegate([marshalAs(UnmanagedType.LPArray,SizeConst=8010)]byte[] buffer,int count);

public static CallbackDelegate callback;

注:说明一下,在给c++传入数组参数时,必须得用[marshalAs(UnmanagedType.LPArray,SizeConst=8010)] 处理一下,相当于是告诉c++,c#传入的是一个长度为8010的数组类型,如果不写这句话的话,你回调函数接收到的参数将只有一条数据。 

接下来看看怎样来调用:

在调用时,我们得先写一个接受c++传回参数的方法,即我们传入委托的实现方法。

复制代码

private void CallBackFunction([marshalAs(UnmanagedType.LPArray,SizeConst=8010)]byte[] buffer,int count)

{

…//处理c++传过来的数据s

}

复制代码

一切工作准备完毕之后,我们来进行最后一步操作把

复制代码

public void GetData()

{

 callback=CallBackFunction;

ReadMyVideo("",callback);

}

复制代码

经过验证,委托就是c++要的回调函数。

用c#调用视频接口相关的dll,dll使用c++开发。

c++接口定义如下:

PLATFORM const char* Plat_GetValueStr(const char* propertyName, int iUserHandle);

c#接口定义如下:

[DllImport(@”Platform.dll”, EntryPoint = “Plat_GetValueStr”, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] extern static IntPtr Plat_GetValueStr(string propertyName, int iUserHandle);

遇到的问题及解决方法:

1、在c#调用时一开始 CharSet = CharSet.Ansi 没有定义 出现过乱码。c++中的编码为ansi,c#默认可能是Unicode。这里需要注意一下。

2、对于c++ 返回值类型为  const char* 我一开始用String 接收,在framework4.0下也成功过,但有时成功,有时失败。在framework2.0下,从来没成功过。后来改用指针接收,成功接收了。

IntPtr ipName = Plat_GetValueStr(ConstControlUnit.ControlUnitName, iUserHandle);
ResName= Marshal.PtrToStringAnsi(ipName);