东东东 陈煜东的博客

标签存档: Linux ( 1 / 2)

Linux iSCSI 使用与一些性能优化

在机器内部无法使用块设备,只能使用 iSCSI 协议获取块设备的数据。

介绍 iSCSI

因为本地盘无法满足需要的灵活性,通过网络将 SCSI 实现变成了 Internet SCSI (iSCSI) 。iSCSI 主要是通过 TCP/IP 的技术,将存储设备端通过 iSCSI target (iSCSI 标的) 功能,做成可以提供磁盘的服务器端,再通过 iSCSI initiator (iSCSI 初始化用戶) 功能,做成能够挂载使用 iSCSI target 的用户端,如此便能通过 iSCSI 协议来进行磁盘的使用。

iSCSI target:存储服务端,存放存盘或者 RAID 设备,为其他机器提供网络磁盘。

iSCSI initiator: 使用 target 的客户端,一般是服务器。需要安装一些软件,并且会连接到 iSCSI target的服务器。

iscsi 网络结构示意

iscsi 网络结构示意

安装与使用 iSCSI 客户端

安装

yum install -y iscsi-initiator-utils

发现服务器上都有哪些target

iscsiadm -m discovery -t st -p 192.168.68.2
192.168.68.2:3260,1 iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f
192.168.68.2:3260,1 iqn.2016.com.chenyudong.share.8888fb83-ab08-466b-89de-df1499b23a8e

iSCSI 有一套自己分享 target 名称定义,基本上,由 iSCSI 分享出來的 target 名称都是以 iqn 开头,意思是:『iSCSI Qualified Name (iSCSI 限定名称)』的意思。

iqn.yyyy-mm.<reversed domain name>:identifier
iqn.年年-月.域名的反写               :这个分享的target名称

挂载磁盘到本地

iscsiadm -m node -l -T iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f
Logging in to [iface: default, target: iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f, portal: 192.168.68.2,3260] (multiple)
Login to [iface: default, target: iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f, portal: 192.168.68.2,3260] successful.

查看磁盘

lsblk -o name,size,type,serial
NAME          SIZE TYPE SERIAL
sda         446.1G disk 6c0149825c2175f623f974a909bf7888
└─sda1         50G part
sdb           3.5T disk 6c0149825c2175f623f974a909c63f88
sdc            50G disk ISCSI_bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f

这样就可以像本地盘硬盘一样使用了。

卸载所有的 iSCSI 磁盘,删除 iSCSI 磁盘数据

当磁盘不再使用时,可以清除所有的磁盘。并且删除 iSCSI 的磁盘数据。

iscsiadm -m node --logout   # login 逆操作
iscsiadm -m node -o delete  # discovery 的逆操作

性能调优

本来打算通过上面的操作开心的使用了,结果发现磁盘的dd操作非常慢,才1MB/s的速度,距离以前的使用经验 70MB/s 有一段的差距。于是开启了参数调优的路程。

dd读取速度慢

一开始dd读取速度非常慢,fio 测试使用aio方法,速度符合预期。

因为dd是单线程读取,他们建议通过并行分段dd,通过 aio 的方法可以加大,感觉难度比较大。

read_ahead_kb

默认 /sys/block/sdx/queue/read_ahead_kb 观察到预读大小为128KB。当需要对大量的大文件进行读取时,可以通过适当调大此参数提升性能。

操作系统可探测到程序何时从文件或者磁盘中连续读取数据。在这种情况下,它可执行智能预读算法,因此用户可能会要求从磁盘中读取更多数据。因此当用户下一步尝试读取数据块时,它已经在操作系统的页缓存中了。可能的缺点是操作系统可能从磁盘中读取过多数据,这样就会占用页缓存直到高内存压力将其清除。如果有多个进程执行错误预读就会增加这种情况下的内存压力。

[root@minios_10_3_0_5 ~]# echo 128 > /sys/block/sdc/queue/read_ahead_kb
[root@minios_10_3_0_5 ~]# dd if=/dev/sdc of=./xxx bs=2M count=2000
2000+0 records in
2000+0 records out
4194304000 bytes (4.2 GB) copied, 154.452 s, 27.2 MB/s

[root@minios_10_3_0_5 ~]# echo 512 > /sys/block/sdc/queue/read_ahead_kb
[root@minios_10_3_0_5 ~]# dd if=/dev/sdc of=./xxx bs=2M count=2000
2000+0 records in
2000+0 records out
4194304000 bytes (4.2 GB) copied, 105.913 s, 39.6 MB/s

[root@minios_10_3_0_5 ~]# echo 4096 > /sys/block/sdc/queue/read_ahead_kb
[root@minios_10_3_0_5 ~]# dd if=/dev/sdc of=./xxx bs=2M count=2000
2000+0 records in
2000+0 records out
4194304000 bytes (4.2 GB) copied, 28.1032 s, 149 MB/s

[root@minios_10_3_0_5 ~]# echo 8192 > /sys/block/sdc/queue/read_ahead_kb
[root@minios_10_3_0_5 ~]# dd if=/dev/sdc of=./xxx bs=2M count=2000
2000+0 records in
2000+0 records out
4194304000 bytes (4.2 GB) copied, 21.9228 s, 191 MB/s

max_sectors_kb

默认将发送到磁盘的最大请求设定为 512 KB。这个可调参数可用来增大或者减小该值。最小值为逻辑块大小;最大值由 max_hw_sectors_kb 设定。有些 SSD 会在 I/O 大小超过内部删除块大小时性能下降。在此类情况下建议将 max_hw_sectors_kb 降低到删除块大小。您可以使用类似 iozone 或者 aio-stress 的 I/O 生成程序对此进行测试,记录大小可从 512 字节到 1 MB 不等。

一开始测试,后端的target server老是挂,经过定位,说是协商 iSCSI 挂载参数的时候数据传输值是 256K,现在这个磁盘的读操作是 512K,导致后端异常。建议修改为 256K 。

echo 256 > /sys/block/sdc/queue/max_sectors_kb

没想到,经过了一个qemu-img操作后,/sys/block/sdc/queue/max_sectors_kb变成了 512。导致读取了一点又把后端给搞异常了。

在网上查询 https://forum.proxmox.com/threads/troubles-iscsi-and-max_sectors_kb.25750/ 发现也有一个类似的案例,数值被udev给重置了。而我这也会被重置会默认值,因此修改这个值不太靠谱

修改配置文件 /etc/iscsi/iscsid.conf

# To specify the maximum number of data bytes the initiator can receive
# in an iSCSI PDU from a target, edit the following line.
#
# The value is the number of bytes in the range of 512 to (2^24-1) and
# the default is 262144
node.conn[0].iscsi.MaxRecvDataSegmentLength = 262144

MaxRecvDataSegmentLength 修改为 524288 (512 * 1204, 单位bit),可以解决问题,这样和 /sys/block/sdx/queue/max_sectors_kb 刚好配合在一起使用。后端没问题。但是通过修改系统默认值不太友好,只要修改单盘的配置就可以满足预期了。

对单盘调整 MaxRecvDataSegmentLength 值

如何只对单个盘生效呢? 尝试了好几次命令,没有成功,偶然在阅读 https://blog.csdn.net/msdnchina/article/details/72860154 这边中,发现了在挂载 iSCSI 磁盘的时候可以看到 debug 的login记录日志。 发现

iscsiadm -m discovery -t st -d8 -p 192.168.68.2
iscsiadm: ip 192.168.68.2, port -1, tgpt -1
iscsiadm: Max file limits 1024 4096
iscsiadm: updating defaults from '/etc/iscsi/iscsid.conf'
iscsiadm: updated 'discovery.sendtargets.iscsi.MaxRecvDataSegmentLength', '32768' => '32768'
iscsiadm: updated 'node.startup', 'manual' => 'automatic'
iscsiadm: updated 'node.leading_login', 'No' => 'No'
iscsiadm: updated 'node.session.timeo.replacement_timeout', '120' => '120'
iscsiadm: updated 'node.conn[0].timeo.login_timeout', '30' => '15'
iscsiadm: updated 'node.conn[0].timeo.logout_timeout', '15' => '15'
iscsiadm: updated 'node.conn[0].timeo.noop_out_interval', '5' => '5'
iscsiadm: updated 'node.conn[0].timeo.noop_out_timeout', '5' => '5'
iscsiadm: updated 'node.session.err_timeo.abort_timeout', '15' => '15'
iscsiadm: updated 'node.session.err_timeo.lu_reset_timeout', '30' => '30'
iscsiadm: updated 'node.session.err_timeo.tgt_reset_timeout', '30' => '30'
iscsiadm: updated 'node.session.initial_login_retry_max', '4' => '8'
iscsiadm: updated 'node.session.cmds_max', '128' => '128'
iscsiadm: updated 'node.session.queue_depth', '32' => '32'
iscsiadm: updated 'node.session.xmit_thread_priority', '-20' => '-20'
iscsiadm: updated 'node.session.iscsi.InitialR2T', 'No' => 'No'
iscsiadm: updated 'node.session.iscsi.ImmediateData', 'Yes' => 'Yes'
iscsiadm: updated 'node.session.iscsi.FirstBurstLength', '262144' => '262144'
iscsiadm: updated 'node.session.iscsi.MaxBurstLength', '16776192' => '16776192'
iscsiadm: updated 'node.conn[0].iscsi.MaxRecvDataSegmentLength', '262144' => '262144'
iscsiadm: updated 'node.conn[0].iscsi.MaxXmitDataSegmentLength', '0' => '0'
iscsiadm: updated 'node.conn[0].iscsi.HeaderDigest', 'None' => 'None'
iscsiadm: updated 'node.session.nr_sessions', '1' => '1'
iscsiadm: updated 'node.session.iscsi.FastAbort', 'Yes' => 'Yes'
iscsiadm: updated 'node.session.scan', 'auto' => 'auto'
iscsiadm: Looking for config file /var/lib/iscsi/send_targets/192.168.68.2,3260
iscsiadm: Lookiscsiadm: updated 'node.session.iscsi.FastAbort', 'Yes' => 'Yes'
iscsiadm: updated 'node.session.iscsi.InitialR2T', 'No' => 'No'
iscsiadm: updated 'node.session.iscsi.ImmediateData', 'Yes' => 'Yes'
iscsiadm: updated 'node.session.iscsi.FirstBurstLength', '262144' => '262144'
iscsiadm: updated 'node.session.iscsi.MaxBurstLength', '16776192' => '16776192'
iscsiadm: updated 'node.session.iscsi.DefaultTime2Retain', '0' => '0'
iscsiadm: updated 'node.session.iscsi.DefaultTime2Wait', '2' => '2'
iscsiadm: updated 'node.session.iscsi.MaxConnections', '1' => '1'
iscsiadm: updated 'node.session.iscsi.MaxOutstandingR2T', '1' => '1'
iscsiadm: updated 'node.session.iscsi.ERL', '0' => '0'
iscsiadm: updated 'node.session.scan', 'auto' => 'auto'
iscsiadm: updated 'node.conn[0].address', '' => '192.168.68.2'
iscsiadm: updated 'node.conn[0].port', '3260' => '3260'
iscsiadm: updated 'node.conn[0].startup', 'manual' => 'manual'
iscsiadm: updated 'node.conn[0].tcp.window_size', '524288' => '524288'
iscsiadm: updated 'node.conn[0].tcp.type_of_service', '0' => '0'
iscsiadm: updated 'node.conn[0].timeo.logout_timeout', '15' => '15'
iscsiadm: updated 'node.conn[0].timeo.login_timeout', '30' => '15'
iscsiadm: updated 'node.conn[0].timeo.auth_timeout', '45' => '45'
iscsiadm: updated 'node.conn[0].timeo.noop_out_interval', '5' => '5'
iscsiadm: updated 'node.conn[0].timeo.noop_out_timeout', '5' => '5'
iscsiadm: updated 'node.conn[0].iscsi.MaxXmitDataSegmentLength', '0' => '0'
iscsiadm: updated 'node.conn[0].iscsi.MaxRecvDataSegmentLength', '262144' => '262144'
iscsiadm: updated 'node.conn[0].iscsi.HeaderDigest', 'None' => 'None'
iscsiadm: updated 'node.conn[0].iscsi.IFMarker', 'No' => 'No'
iscsiadm: updated 'node.conn[0].iscsi.OFMarker', 'No' => 'No'
iscsiadm: found drec 192.168.68.2 3260
iscsiadm: rmd /var/lib/iscsi/send_targets/192.168.68.2,3260/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f,192.168.68.2,3260,1,
default
iscsiadm: Removing config file /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260 iface id d
efault
iscsiadm: Removing config file /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260,1/default
iscsiadm: overwriting existing record
iscsiadm: node addition making link from /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260,
1 to /var/lib/iscsi/send_targets/192.168.68.2,3260/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f,192.168.68.2,3260,1,defaulting for config file /var/lib/iscsi/send_targets/192.168.68.2,3260 config st_config.

....

iscsiadm: rec read looking for config file /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,326
0.
iscsiadm: rec read looking for config file /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,326
0,1/default.
iscsiadm: updated 'node.name', '' => 'iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f'
iscsiadm: updated 'node.tpgt', '-1' => '1'
iscsiadm: updated 'node.startup', 'manual' => 'automatic'
iscsiadm: updated 'node.leading_login', 'No' => 'No'
iscsiadm: updated 'iface.iscsi_ifacename', 'default' => 'default'
iscsiadm: updated 'iface.transport_name', 'tcp' => 'tcp'
iscsiadm: updated 'iface.vlan_id', '0' => '0'
iscsiadm: updated 'iface.vlan_priority', '0' => '0'
iscsiadm: updated 'iface.iface_num', '0' => '0'
iscsiadm: updated 'iface.mtu', '0' => '0'
iscsiadm: updated 'iface.port', '0' => '0'
iscsiadm: updated 'iface.tos', '0' => '0'
iscsiadm: updated 'iface.ttl', '0' => '0'
iscsiadm: updated 'iface.tcp_wsf', '0' => '0'
iscsiadm: updated 'iface.tcp_timer_scale', '0' => '0'
iscsiadm: updated 'iface.def_task_mgmt_timeout', '0' => '0'
iscsiadm: updated 'iface.erl', '0' => '0'
iscsiadm: updated 'iface.max_receive_data_len', '0' => '0'
iscsiadm: updated 'iface.first_burst_len', '0' => '0'
iscsiadm: updated 'iface.max_outstanding_r2t', '0' => '0'
iscsiadm: updated 'iface.max_burst_len', '0' => '0'
iscsiadm: updated 'node.discovery_address', '' => '192.168.68.2'
iscsiadm: updated 'node.session.iscsi.InitialR2T', 'No' => 'No'
iscsiadm: updated 'node.session.iscsi.ImmediateData', 'Yes' => 'Yes'
iscsiadm: updated 'node.session.iscsi.FirstBurstLength', '262144' => '262144'
iscsiadm: updated 'node.session.iscsi.MaxBurstLength', '16776192' => '16776192'
iscsiadm: updated 'node.session.iscsi.DefaultTime2Retain', '0' => '0'
iscsiadm: updated 'node.session.iscsi.DefaultTime2Wait', '2' => '2'
iscsiadm: updated 'node.session.iscsi.MaxConnections', '1' => '1'
iscsiadm: updated 'node.session.iscsi.MaxOutstandingR2T', '1' => '1'
iscsiadm: updated 'node.session.iscsi.ERL', '0' => '0'
iscsiadm: updated 'node.session.scan', 'auto' => 'auto'
iscsiadm: updated 'node.conn[0].address', '' => '192.168.68.2'
iscsiadm: updated 'node.conn[0].port', '3260' => '3260'
iscsiadm: updated 'node.conn[0].startup', 'manual' => 'manual'
iscsiadm: updated 'node.conn[0].tcp.window_size', '524288' => '524288'
iscsiadm: updated 'node.conn[0].tcp.type_of_service', '0' => '0'
iscsiadm: updated 'node.conn[0].timeo.logout_timeout', '15' => '15'
iscsiadm: updated 'node.conn[0].timeo.login_timeout', '30' => '15'
iscsiadm: updated 'node.conn[0].timeo.auth_timeout', '45' => '45'
iscsiadm: updated 'node.conn[0].timeo.noop_out_interval', '5' => '5'
iscsiadm: updated 'node.conn[0].timeo.noop_out_timeout', '5' => '5'
iscsiadm: updated 'node.conn[0].iscsi.MaxXmitDataSegmentLength', '0' => '0'
iscsiadm: updated 'node.conn[0].iscsi.MaxRecvDataSegmentLength', '262144' => '262144'
iscsiadm: updated 'node.conn[0].iscsi.HeaderDigest', 'None' => 'None'
iscsiadm: updated 'node.conn[0].iscsi.IFMarker', 'No' => 'No'
iscsiadm: updated 'node.conn[0].iscsi.OFMarker', 'No' => 'No'
iscsiadm: found drec 192.168.68.2 3260
iscsiadm: rmd /var/lib/iscsi/send_targets/192.168.68.2,3260/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f,192.168.68.2,3260,1,
default
iscsiadm: Removing config file /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260 iface id d
efault
iscsiadm: Removing config file /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260,1/default
iscsiadm: overwriting existing record
iscsiadm: node addition making link from /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260,
1 to /var/lib/iscsi/send_targets/192.168.68.2,3260/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f,192.168.68.2,3260,1,default

这里发现了,在 discovery 的时候,会生成配置文件。 因此修改挂载参数的时候应该在login之前,而不能在login之后修改。

通过

# 提前先发现 iSCSI 磁盘
# iscsiadm -m discovery -t st -p 192.168.68.2

# 修改 MaxRecvDataSegmentLength 参数
iscsiadm -m node -o update -T iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f -n node.conn[0].iscsi.MaxRecvDataSegmentLength -v 524288


# 然后挂载磁盘
iscsiadm -m node -l -T iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f


# 查看配置生效
vim /var/lib/iscsi/nodes/iqn.2016.com.chenyudong.share.bb7d09bc-c8d0-43b9-925a-3b2d2bd4349f/192.168.68.2,3260,1/default

node.conn[0].iscsi.MaxRecvDataSegmentLength = 524288

看到这个就生效了。 最后可以愉快的读取磁盘数据了。

参考

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: Linux iSCSI 使用与一些性能优化 – https://www.chenyudong.com/archives/iscsi-use-and-performance-optimization.html

分类: Linux

Docker 实践记录

记录一下使用的 Docker 的辛苦过程。

Docker 使用的范围越来越广,我最看重的就是环境隔离,和环境整体打包服务,不用再自己的本地环境上编译。

本文以 CentOS 7 来作为记录过程,其他的发行版类似,就一些系统级别的命令可能不合适。

安装

yum insatll docker

重启

systemctl daemon-reload
systemctl restart docker

本地镜像加速

出现

root@VM_96_130_centos tmp]# docker pull mysql:5.7
Trying to pull repository docker.io/library/mysql ... 
Pulling repository docker.io/library/mysql
Error while pulling image: Get https://index.docker.io/v1/repositories/library/mysql/images: dial tcp 52.207.59.176:443: getsockopt: network is unreachable
Error while pulling image: Get https://index.docker.io/v1/repositories/library/mysql/images: dial tcp 52.207.59.176:443: getsockopt: network is unreachable

我这里使用的是腾讯云,因此我这里的是镜像加速和下载

vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/docker-current daemon \
          --exec-opt native.cgroupdriver=systemd \
          --registry-mirror=https://mirror.ccs.tencentyun.com \
          -g /data/docker/ \
          $OPTIONS \
          $DOCKER_STORAGE_OPTIONS \
          $DOCKER_NETWORK_OPTIONS \
          $ADD_REGISTRY \
          $BLOCK_REGISTRY \
          $INSECURE_REGISTRY

顺便更改一下 docker 镜像的路径,防止系统根目录太小。

网上也有说 /etc/default/docker为啥不起作用的

HTTP 代理

利用 docker build -t xxx . 出现一些错误

Ign http://archive.ubuntu.com trusty InRelease
Ign http://archive.ubuntu.com trusty-updates InRelease
Ign http://archive.ubuntu.com trusty-security InRelease
Err http://archive.ubuntu.com trusty Release.gpg
  Cannot initiate the connection to archive.ubuntu.com:80 (2001:67c:1360:8001::21). - connect (101: Network is unreachable) [IP: 2001:67c:1360:8001::21 80]
Err http://archive.ubuntu.com trusty-updates Release.gpg
  Cannot initiate the connection to archive.ubuntu.com:80 (2001:67c:1360:8001::21). - connect (101: Network is unreachable) [IP: 2001:67c:1360:8001::21 80]
Err http://archive.ubuntu.com trusty-security Release.gpg
  Cannot initiate the connection to archive.ubuntu.com:80 (2001:67c:1360:8001::21). - connect (101: Network is unreachable) [IP: 2001:67c:1360:8001::21 80]

修改 Dockerfile ,增加http代理

FROM ubuntu:14.04
MAINTAINER Kevin Littlejohn <kevin@littlejohn.id.au>, \
    Alex Fraser <alex@vpac-innovations.com.au>

# 这个是可用的
ENV http_proxy=http://xx.xx.xx.xx:6788/
ENV https_proxy=http://xx.xx.xx.xx:6788/


# 网上有这么说的,这个是不能用于 ubuntu的agt-get。
ENV HTTP_PROXY=http://xx.xx.xx.xx:6788/
ENV HTTPS_PROXY=http://xx.xx.xx.xx:6788/

目前没有找到一个说法,http_proxy 是否应该大写。应该是不同的程序读取的环境变量不一样,我看大部分都是小写,最好的方法是都设置。

有一些讨论:http://unix.stackexchange.com/questions/212894/whats-the-right-format-for-the-http-proxy-environment-variable-caps-or-no-ca

常用的命令记录

docker build

# 编写 Dockerfile 文件
docker build -t docker-proxy .

docker images

查看镜像

docker exec

# 进入容器内部
docker exec -it 容器id /bin/bash

docker run

# 打开镜像看看
# 因为有时候直接启动会被退出了。
docker run -i -t ubuntu:14.04 /bin/bash

docker-compose

pip install docker-compose
# 编写 docker-compose.yml 文件
docker-compose up -d 

其他的系统也有依赖 docker 的,出现了

  File "/root/readthedocs/readthedocs.org/readthedocs/doc_builder/environments.py", line 16, in <module>
    from docker import Client
ImportError: cannot import name Client

那么其他使用 virtualenv 来解决单独的部署问题,让默认的系统能够正常使用。

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: Docker 实践记录 – https://www.chenyudong.com/archives/docker-practice.html

分类: DevOps

Python Subprocess Popen 管道阻塞问题分析解决

使用Subprocess Popen的类库困挠了我一个月的问题终于解决了。

一句话就是:等待命令返回不要使用wait(),而是使用communicate(),但注意内存,大输出使用文件。

错误的使用例子

之前的代码这样使用的。

# 不合适的代码
def run_it(self, cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
                         stderr=subprocess.PIPE, close_fds=True)
    log.debug('running:%s' % cmd)
    p.wait()
    if p.returncode != 0:
        log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd))
    return p.stdout

这段代码之前用着一直没有问题的,后来不知道为何就不能用了(后面知道了,原来输出内容增加,输出的问题本太长,把管道给堵塞了)。

这样的代码也在之前的一个项目中使用,而且调用的次数有上亿次,也没什么问题。之前倒是也卡住了一次,不过有个大神把问题找到了,因为Python版本低于2.7.6,Python对close_fds的一些实现不太好导致的,没有把管道释放掉,一直卡住。设置close_fds=True。不过这个并没有解决我的问题。

解决了我的问题

当时想着既然卡住了,那我就看看是输出了什么才卡住的,结果现有的代码无法支持我的想法,就换了代码,没想到就不卡住了。

def run_it(cmd):
    # _PIPE = subprocess.PIPE
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
                         stderr=subprocess.PIPE) #, close_fds=True)

    log.debug('running:%s' % cmd)
    out, err = p.communicate()
    log.debg(out)
    if p.returncode != 0:
        log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd))
    return p.stdout

看看Python文档信息

Warning

Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process.

Popen.wait()
    Wait for child process to terminate. Set and return returncode attribute.

    Warning This will deadlock when using stdout=PIPE and/or stderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.
Popen.communicate(input=None)
    Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child.

    communicate() returns a tuple (stdoutdata, stderrdata).

    Note that if you want to send data to the process’s stdin, you need to create the Popen object with stdin=PIPE. Similarly, to get anything other than None in the result tuple, you need to give stdout=PIPE and/or stderr=PIPE too.

    Note The data read is buffered in memory, so do not use this method if the data size is large or unlimited.

之前没注意,再细看一下文档,感觉豁然开朗。

Linux管道限制,为什么会阻塞呢?

下面来看看Can someone explain pipe buffer deadlock?的回答。

子进程产生一些数据,他们会被buffer起来,当buffer满了,会写到子进程的标准输出和标准错误输出,这些东西通过管道发送给父进程。当管道满了之后,子进程就停止写入,于是就卡住了。

及时取走管道的输出也没有问题

# 及时从管道中取走数据
def run_it(self, cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True,
                         stderr=subprocess.PIPE, close_fds=True)
    log.debug('running:%s' % cmd)
    for line in iter(p.stdout.readline, b''):
        print line,          # print to stdout immediately
    p.stdout.close()
    p.wait()
    if p.returncode != 0:
        log.critical("Non zero exit code:%s executing: %s" % (p.returncode, cmd))
    return p.stdout

看了Python的communicate()内部就是将stdout/stderr读取出来到一个list变量中的,最后函数结束时返回。

测试Linux管道阻塞问题

看到别人的例子,一直在想怎么测试输出64K的数据,发现dd这个思路很棒,是见过最优雅的例子了,精确控制输出的长度,其他都是从某些地方搞来大文件导入进来。

#!/usr/bin/env python
# coding: utf-8
# yc@2013/04/28

import subprocess

def test(size):
    print 'start'

    cmd = 'dd if=/dev/urandom bs=1 count=%d 2>/dev/null' % size
    p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
    #p.communicate()
    p.wait()  # 这里超出管道限制,将会卡住子进程

    print 'end'

# 64KB
test(64 * 1024)

# 64KB + 1B
test(64 * 1024 + 1)

# output :
start
end
start   #  然后就阻塞了。

首先测试输出为 64KB 大小的情况。使用 dd 产生了正好 64KB 的标准输出,由 subprocess.Popen 调用,然后使用 wait() 等待 dd 调用结束。可以看到正确的 start 和 end 输出;然后测试比 64KB 多的情况,这种情况下只输出了 start,也就是说程序执行卡在了 p.wait() 上,程序死锁。

总结

那死锁问题如何避免呢?官方文档里推荐使用 Popen.communicate()。这个方法会把输出放在内存,而不是管道里,所以这时候上限就和内存大小有关了,一般不会有问题。而且如果要获得程序返回值,可以在调用 Popen.communicate() 之后取 Popen.returncode 的值。

但真的如果超过内存了,那么要考虑比如文件 stdout=open("process.out", "w") 的方式来解决了,不能使用管道了。

另外说一下。管道的要用清楚,不要随意的乱世用管道。比如没有input的时候,那么stdin就不要用管道了。

还有不要把简单的事情复杂化。比如echo 1 > /sys/linux/xxx修改文件,这么简单的功能就不要用Linux的shell调用了,使用Python自带的 open('file', 'w').write('1') 。尽量保持Python范。

参考

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: Python Subprocess Popen 管道阻塞问题分析解决 – https://www.chenyudong.com/archives/python-subprocess-popen-block.html

分类: Python

ssh密钥登录失败

搞一个ssh的密钥登录,结果发现就是无法使用密钥登录,非让我输入密码。苦恼。

输入ssh连接代码

ssh -i private.key -v user@host.com -p22

从连接的信息中看不出有什么问题。

debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Next authentication method: publickey
debug1: Offering public key: private.key
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Next authentication method: password

已经提示说了,使用公钥登录,并且提供了私钥,但是还是提示让我输入密码。

但是我使用另外一个帐号去登录,发现是ok的,可以登录成功。我把成功的那个authorized_keys复制到我的那个用户名下,发现还是不能登录。但是验证了服务器的sshd功能是正常运行的,否则另外一个账户就没办法登录了。我觉得两个用户名的所在的环境应该是相同了,但是确实无法登录。

想到了客户端这里的连接没有更多有效的信息,那就想到了去服务器上看看相关的信息。vim /var/log/secure查看一下日志。发现有这么一段话:

Authentication refused: bad ownership or modes for directory /home/user/.ssh

上网搜了一下这个问题,发现原来是.ssh的目录权限不对,设置一下正确的权限:

chmod 700 ~/.ssh

在此过程中还学习到了一个sshd的测试模式。

 /usr/sbin/sshd -p 10022 -d
      -d     以调试模式运行。服务器将在前台运行并发送非常详细的调试日志信息,
             服务器将只允许接入一个连接,并且不派生出子进程。仅用于调试目的。
             使用多个 -d 选项可以输出更详细的调试信息(最多3个)。

会开启一个sshd的调试模式,新建一个10022端口,在客户端连接这个端口的时候,服务器端会有debug日志输出在屏幕上,这个时候就可以查看详细的信息。然后再针对信息进行解决。

总结

服务器用户对于ssh目录权限有一些权限要求,权限不对的话ssh无法工作,建议以下权限作为最小的权限。

chmod 700 .ssh          # chmod 755 .ssh 也是可以的
chmod 600 .ssh/authorized_keys
chmod 400 .ssh/private.key  #把密钥的权限也设置为最小

终于又可以愉快的编程了。

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: ssh密钥登录失败 – https://www.chenyudong.com/archives/ssh-public-key-authentication-refused.html

分类: Linux

Linux ntpdate同步网络时间

自己的Linux服务器的时间和PC机上的时间不太对,打log的时间对不上,不太方便调式信息的判断,因此需要同步一下Linux服务器的时间和自己Windows的时间,两者一致,才方便调式的查看呢。

同步网络时间

在Linux下,我们可以使用ntpdate进行网络时间的同步,而不是我们自己去设置时间。这个命令的使用很简单,

ntpdate  0.cn.pool.ntp.org

另外网络时间同步和时区是不一样的。你可以选择任何一台网络时间同步服务器来同步你的时间,只要你的时区设置是对的,那么你的Linux上的时间就是对的。因为全球都是基于一个标准时间来约定的,美国人民与我们不同的是时区不同,经过换算,我们和他们的标准时间是一样的。

定时的同步时间

我们可以使用crontab来定时的同步时间

vim /etc/crontab
10 5 * * * root (/usr/sbin/ntpdate 0.cn.pool.ntp.org && /sbin/hwclock -w) &> /var/log/ntpdate.log 

每天的5点10分crontab运行一次命令,自动同步时间。

如果你具有多台的服务器,不要使用这个方法来定时的同步的你服务器,请使用ntpd来进行,这个还可以校准始终的问题。详情查看鸟哥

使用ntpdata造成的时间的越变还可能引发因某些依赖连续时间的程序的问题。一般第一次使用ntpdate,接下来使用ntpd服务来不断的调整时间。参见http://blog.sina.com.cn/s/blog_3f3422fd0100f06c.html

如何设置时区

将时区设置为东8区的时间,虽然服务器在全球的不同地方,但是我们人在中国,看着中国的时间比较有感觉。

cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
date
# Mon Mar 31 11:02:55 CST 2014

另外如果使用date命令查看时间,发现刚刚好相差8个小时或者其他的小时,那么应该就是你的时区设置不对,快快修改一下吧。

网络时间同步服务器列表

全球那么多的网络时间同步器,那么使用哪个好呢。我们知道数据在网络上流动是由延迟的,因此我们选择离我们服务器越近的服务器进行同步,时间越准。

时间服务器分为两种,一种是一级时间服务器,另外一种是二级时间服务器。我们如果是同步自己的服务器的时间,那么选择二级时间服务器,因为一级时间服务器是为二级时间服务器提供时间校对服务器,我们尽量不要增加一级服务器的压力。这种层级的概念和DNS的层级概念是一致的。

一级时间服务器列表:http://support.ntp.org/bin/view/Servers/StratumOneTimeServers

二级时间服务器列表:http://support.ntp.org/bin/view/Servers/StratumTwoTimeServers

附二级服务器列表:

  • 0.pool.ntp.org  有域名负载均衡
  • 0.cn.pool.ntp.org  有域名负载均衡
  • ntp.tuna.tsinghua.edu.cn 清华大学
  • time.windows.com    微软

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: Linux ntpdate同步网络时间 – https://www.chenyudong.com/archives/linux-ntpdate-time-synchronize.html

分类: Linux

[转]Linux epoll给我们带来什么

Q:网络服务器的瓶颈在哪? A:IO效率。

在大家苦苦的为在线人数的增长而导致的系统资源吃紧上的问题正在发愁的时候,Linux 2.6内核中提供的System Epoll为我们提供了一套完美的解决方案。传统的select以及poll的效率会因为在线人数的线形递增而导致呈二次乃至三次方的下降,这些直接导致了网络服务器可以支持的人数有了个比较明显的限制。

自从Linux提供了/dev/epoll的设备以及后来2.6内核中对/dev/epoll设备的访问的封装(System Epoll)之后,这种现象得到了大大的缓解,如果说几个月前,大家还对epoll不熟悉,那么现在来说的话,epoll的应用已经得到了大范围的普及。

那么究竟如何来使用epoll呢?其实非常简单。 通过在包含一个头文件#include <sys/epoll.h>以及几个简单的API将可以大大的提高你的网络服务器的支持人数。

首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。

之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写了。基本的语法为:

nfds = epoll_wait(kdpfd, events, maxevents, -1);

其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则范围。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。

epoll_wait范围之后应该是一个循环,遍利所有的事件:

for(n = 0; n < nfds; ++n) {
    if(events[n].data.fd == listener) { //如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理。
        client = accept(listener, (struct sockaddr *) &local, &addrlen);
    if(client < 0){
        perror("accept");
        continue;
    }
    setnonblocking(client); // 将新连接置于非阻塞模式
    ev.events = EPOLLIN | EPOLLET; // 并且将新连接也加入EPOLL的监听队列。

注意,这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,如果有写操作的话,这个时候epoll是不会返回事件的,如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET

ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
  // 设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,这里用EPOLL_CTL_ADD来加一个新的epoll事件,
  //通过EPOLL_CTL_DEL来减少一个epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。
    fprintf(stderr, "epoll set insertion error: fd=%d0, client);
    return -1;

} else {  // 如果不是主socket的事件的话,则代表是一个用户socket的事件,
         //则来处理这个用户socket的事情,比如说read(fd,xxx)之类的,或者一些其他的处理。
    do_use_fd(events[n].data.fd);
}

对,epoll的操作就这么简单,总共不过4个API:epoll_create, epoll_ctl, epoll_wait和close。 如果您对epoll的效率还不太了解,请参考我之前关于网络游戏的网络编程等相关的文章。

本文转载自:http://dev.gameres.com/Program/Control/Epoll.htm

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: [转]Linux epoll给我们带来什么 – https://www.chenyudong.com/archives/linux-epoll-network-io.html

分类: 网络

使用libvirt和qemu将pci pass through设备添加到虚拟机上

透传的优势

guest使用透传设备可以获得设备近乎原生的性能,

PCI pass-throught设备给动态迁移带来的问题, dest host可能没有同样的硬件.

就算可以模拟一个设备,但是原始设备的内部状态不能获得.

VT-d support

In order to assign devices in KVM, you’ll need a system which supports VT-d. This has nothing to do with the VT-x support of your CPU, VT-d needs to be supported by both your chipset on your motherboard and by your CPU.

If you are in doubt whether your motherboard or CPU supports VT-d or not, the Xen VT-d wikipage has some pointers of VT-d enabled chipsets, motherboards and CPUs: http://wiki.xensource.com/xenwiki/VTdHowTo

If your hardware doesn’t have an IOMMU (“Intel VT-d” support in case of Intel – “AMD I/O Virtualization Technology” support in case of AMD), you’ll not be able to assign devices in KVM. Some work towards allowing this were done, but the code never made it into KVM, due to various issues with the code. At the moment it doesn’t seem like device assignment without hardware support, will ever be integrated into KVM.

Assignment of graphics cards are not officially supported at the moment, but there has been some success passing through a secondary Radeon HD 5850 as a VM’s secondary display.

资料:http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM

在host选择PCI pass through设备

reboot and verify that your system has IOMMU support

AMD Machine

dmesg | grep AMD-Vi
 ...
 AMD-Vi: Enabling IOMMU at 0000:00:00.2 cap 0x40
 AMD-Vi: Lazy IO/TLB flushing enabled
 AMD-Vi: Initialized for Passthrough Mode
 ...

Intel Machine

dmesg | grep -e DMAR -e IOMMU
...
DMAR:DRHD base: 0x000000feb03000 flags: 0x0
IOMMU feb03000: ver 1:0 cap c9008020e30260 ecap 1000
...

If you get no output you’ll need to fix this before moving on. Check if your hardware supports VT-d and check that it has been enabled in BIOS.

NOTE: If you still get an error “No IOMMU found.” Check dmesg for errors suggesting your BIOS is broken. Another possible reason: CONFIG_DMAR_DEFAULT_ON is not set. In that case, pass “intel_iommu=on” as kernel parameter to enable it. AMD uses different kernel parameter than Intel, on AMD you need to pass “iommu=pt iommu=1”.

请看附录:No IOMMU found 解决

选择要使用的透传设备

# lspci -nn
00:00.0 Host bridge [0600]: Intel Corporation 2nd Generation Core Processor Family DRAM Controller [8086:0100] (rev 09)
......
00:1b.0 Audio device [0403]: Intel Corporation 6 Series/C200 Series Chipset Family High Definition Audio Controller [8086:1c20] (rev 04)
.....
00:1f.3 SMBus [0c05]: Intel Corporation 6 Series/C200 Series Chipset Family SMBus Controller [8086:1c22] (rev 04)

友情提示:使用透传设备时,拿USB控制器作实验,可能鼠标键盘不能使用.请谨慎.

将设备从宿主机上解除绑定

使用echo命令,将设备从host机器上解除绑定,将来用于guest机器. For example:

echo "8086 1c20" > /sys/bus/pci/drivers/pci-stub/new_id
echo 0000:00:1b.0 > /sys/bus/pci/devices/0000:00:1b.0/driver/unbind
echo 0000:00:1b.0 > /sys/bus/pci/drivers/pci-stub/bind

关闭虚拟机

关闭虚拟机,修改配置文件.

使用libvirt进行pci pass through

修改虚拟机配置文件

<devices> …
    <hostdev mode='subsystem' type='pci' managed='yes'>
      <source>
         <address domain='0x000' bus='0x00′ slot='0x1b' function='0x0' />
      </source>
   </hostdev>
</devices>

在修改完虚拟机配置文件后,运行虚拟机.

使用qemu进行pci pass through

使用qemu进行pci pass through也是一样的简单的.我们需要一个已经创建好操作系统的虚拟机.

在qemu命令行运行以下命令以启动虚拟机

/usr/bin/qemu-kvm -name vdisk -enable-kvm -m 512 -smp 2 \
-hda /mnt/nfs/vdisk.img \
-monitor stdio \
-vnc 0.0.0.0:0 \
-device pci-assign,host=00:1b.0

这样就将设备挂载到虚拟机上了.

  • 参数-device pci-assign,host=00:1b.0说的是使用一个pci设备,并提供一个设备的地址.
  • 参数-monitor stdio是使用一个标准的控制台输出.在命令行中进行输入命令,等等迁移的时候也在这里输入命令.

附录1:No IOMMU found 解决

启动虚拟机的时候出现了iommu的问题.以供大家参考

1.查看错误日志说明

在配置好XML文件后,启动虚拟机,遇到一个问题.

error: Failed to start domain vdisk
error: Unable to read from monitor: Connection reset by peer

查看虚拟机日志( cat /var/log/libvirt/qemu/vdisk.log )信息.

char device redirected to /dev/pts/3
No IOMMU found. Unable to assign device "hostdev0"
qemu-system-x86_64: -device pci-assign,configfd=20,host=00:1b.0,id=hostdev0,bus=pci.0,addr=0x4: Device 'pci-assign' could not be initialized
2013-07-08 06:41:23.256+0000: shutting down

上网查阅资料,说是要在BIOS上设置虚拟化,然后在引导程序里也要设置iommu.可以查看一下自己的电脑信息是否开启了.

2.查看信息gurb的引导信息

# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.9.6-200.fc18.x86_64 root=/dev/mapper/fedora-home ro rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 rd.lvm.lv=fedora/home rd.luks=0 vconsole.keymap=us rhgb quiet LANG=en_US.UTF-8

可以发现,我的还未开启intel_iommu=on选项.所以接下来我们来激活它.

3.激活intel_iommu=on

Activate Intel VT-d in the kernel

Activate Intel VT-d in the kernel by appending the intel_iommu=on parameter to the kernel line of the kernel line in the/boot/grub/grub.conf file. The example below is a modified grub.conf file with Intel VT-d activated.

对于intel的cpu和amd的cpu,在grub配置上是不同的,具体的配置请参考文章:http://pve.proxmox.com/wiki/Pci_passthrough

4.更新grub

在编辑完grub文件后,需要更新

grub2-mkconfig   # fedora arch centos
update-grub            # ubuntu debian

5.重启电脑,使其生效

# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.9.6-200.fc18.x86_64 root=/dev/mapper/fedora-home ro rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 rd.lvm.lv=fedora/home rd.luks=0 vconsole.keymap=us rhgb quiet intel_iommu=on LANG=en_US.UTF-8

发现开机已经启动了inte _iommu=on了.再次启动虚拟机已经就不会出现这个bug了.

附录2 PCI pass through 失败要关闭SELinux

我用的是Fedora 18 ,将SELinux给disalbed了,但是发现PCI pass through出先问题.上网看到文章PCI passthrough fails in qemu-kvm unless selinux is disabled 说,要将selinux设置成permissive模式,于是这个问题解决了.

参考资料

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: 使用libvirt和qemu将pci pass through设备添加到虚拟机上 – https://www.chenyudong.com/archives/add-pci-pass-through-device-to-guest-vm-with-libvirt-and-qemu.html

分类: 虚拟化

Linux永久改变hostname

Linux的hostname有时候有些作用,比如在shell中显示用户名@主机名

临时改变hostname

[root@localhost ~]# hostname # show current hostname
localhost.localdomain

[root@localhost ~]# hostname domain # change hostname
[root@localhost ~]# hostname
domain

永久改变hostname

[root@localhost ~]# vi /etc/hostname    # change
domain
[root@localhost ~]# exit

重新登录

[root@domain ~]# hostname
domain

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: Linux永久改变hostname – https://www.chenyudong.com/archives/permanently-change-hostname-on-fedora-linux.html

分类: Linux

Linux添加、创建新用户

给Linux添加新用户,新建用户,新建帐号

添加用户组

sudo groupadd groupname

添加用户

sudo useradd username -m -s /sbin/nologin -d /home/username -g groupname
-s /sbin/nologin 设置不能登陆 -s /bin/false(老方法) 也行
-d 设置用户主目录
-g 用户组
-m 创建用户目录

useradd的具体参数为

[root@317304 ~]# useradd --help
Usage: useradd [options] LOGIN

Options:
  -b, --base-dir BASE_DIR       base directory for the home directory of the
                                new account
  -c, --comment COMMENT         GECOS field of the new account
  -d, --home-dir HOME_DIR       home directory of the new account
  -D, --defaults                print or change default useradd configuration
  -e, --expiredate EXPIRE_DATE  expiration date of the new account
  -f, --inactive INACTIVE       password inactivity period of the new account
  -g, --gid GROUP               name or ID of the primary group of the new
                                account
  -G, --groups GROUPS           list of supplementary groups of the new
                                account
  -h, --help                    display this help message and exit
  -k, --skel SKEL_DIR           use this alternative skeleton directory
  -K, --key KEY=VALUE           override /etc/login.defs defaults
  -l, --no-log-init             do not add the user to the lastlog and
                                faillog databases
  -m, --create-home             create the user's home directory
  -M, --no-create-home          do not create the user's home directory
  -N, --no-user-group           do not create a group with the same name as
                                the user
  -o, --non-unique              allow to create users with duplicate
                                (non-unique) UID
  -p, --password PASSWORD       encrypted password of the new account
  -r, --system                  create a system account
  -s, --shell SHELL             login shell of the new account
  -u, --uid UID                 user ID of the new account
  -U, --user-group              create a group with the same name as the user
  -Z, --selinux-user SEUSER     use a specific SEUSER for the SELinux user mapping

更改用户登录权限

在增加了-s /sbin/nologin 参数后,那么这个帐号就不能登陆了,如果想要恢复登陆使用

sudo usermod -s /bin/bash username

禁用用户登录权限

sudo usermod -s /sbin/nologin username

设置密码

给用户设置密码,这样帐号就能使用了。

sudo passwd username

删除用户

删除用户

sudo userdel username

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: Linux添加、创建新用户 – https://www.chenyudong.com/archives/linux-add-new-userer.html

分类: Linux

在虚拟机中安装CentOS操作系统

打算在虚拟机中安装一个centos,然后用来学习服务器的配置,也算是为了将来买vps的准备吧,反正内存也比较大,尽管我自己使用的Ubuntu,但是不想乱安装软件,还是用来虚拟机来,免的捣鼓坏了可以快速还原。

下载centos光盘安装镜像

下载Centos的iso源文件, 我是在USTC上下载的,下载地址为http://mirrors.ustc.edu.cn/centos/6.3/isos/x86_64/CentOS-6.3-x86_64-minimal.iso

在虚拟机中安装centos

在虚拟机中新建一个Linux系统,并在虚拟光盘中添加iso文件,运行虚拟机,进入系统。

  1. 运行后,有个选项,选择”Install or upgrade an existing system”,等待一系列检查,
  2. 有一个”Disc found”,检测光盘是否完整,一般没用,我选择跳过了。然后继续等待。
  3. 出现”Welcome to CentOS!”,选择”OK”。
  4. 在”Language Selection”语言选择中,我选择默认的英语”English”。
  5. 在”Keyboard Selection”键盘选择,默认”us”。
  6. “Time Zone Selection”时区选择,我选择”Asia/Shanghai”上海。
  7. 输入root密码。
  8. 选择分区,我将整块硬盘都使用了。然后又是等待,这次等待时间较长。不过它怎么就从网上下载东西呢,果断断网。貌似不结束,重启,唉。 估计得等到连接超时才跳过。
  9. 安装完后,重启计算机,就可以使用root用户登录了。

使用root账户登录成功。

系统默认使用DHCP获得ip,虚拟机中使用的是NAT转换,不支持ipv6,我使用桥接功能,这样我能使用ipv6。

接下来要安装一下ssh服务器功能。

声明:未经允许禁止转载 东东东 陈煜东的博客 文章,谢谢。如经授权,转载请注明: 转载自东东东 陈煜东的博客

本文链接地址: 在虚拟机中安装CentOS操作系统 – https://www.chenyudong.com/archives/install-centos-minimal-linux-on-virtualbox.html

分类: Linux, 网站建设

较早的文章

Copyright © 2019 东东东 陈煜东的博客 粤ICP备13059639号-1

SITEMAP回到顶部 ↑