在 Linux 服务器上使用 Golang 和 MySQL 实现 Redis 事务和锁

一、介绍

在 Redis 中,事务是由必须以原子方式提交的多个命令组成的单个工作单元。也就是说,要么执行所有命令,要么不执行任何命令。Redis 使用 、、 和 函数来实现此功能。MULTIEXECDISCARDWATCH

要通过该工具创建事务,您只需先运行命令,然后运行其他后续命令。最后,应执行命令来处理事务或命令刷新排队的命令。redis-cliMULTIEXECDISCARD

该命令允许您在事务的生存期内实现锁定机制,如果您的密钥被另一个会话修改,该命令应无法避免将 Redis 数据库置于不一致状态。WATCHWATCHedEXEC

在本指南中,您将使用 Redis 事务函数在 Linux 服务器上使用 Golang 和 MySQL 创建抢票应用程序。

二、准备工作

若要继续本教程,请确保具有以下各项:

  • 一个 Linux 服务器。
  • 具有 sudo 权限的非 root 用户。
  • 一个MySQL服务器。
  • 一个 Redis 服务器。
  • 一个戈朗包。

三、 创建 MySQL 数据库、用户帐户和表

Redis 是一个内存数据库,虽然它可以将数据持久保存到磁盘,但它不是为此目的而设计的,可能无法以最佳方式执行。因此,在本指南中,您将使用 MySQL 数据库在 Redis 服务器生成票证信息后将其永久存储到 MySQL 表中。

通过 SSH 连接到您的服务器,然后按照以下步骤创建数据库。

    1. 以 身份登录到 MySQL 服务器。root
      $ sudo mysql -uroot -p
    2. 出现提示时输入您的 MySQL 密码,然后按继续。然后,执行以下命令以创建数据库和帐户。替换为强值。rootENTERbookingsbookings_userEXAMPLE_PASSWORD
      mysql> CREATE DATABASE bookings DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
      
             CREATE USER 'bookings_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
      
             GRANT ALL PRIVILEGES ON bookings.* TO 'bookings_user'@'localhost';
      
             FLUSH PRIVILEGES;
    3. 切换到新数据库。
      mysql> USE bookings;
    4. 接下来,创建一个表。在此示例应用程序中,您将使用 Redis 服务器从可用座位池中抓取乘客的座位。然后,您将在表中永久存储分配的信息和信息。ticketsseat_no'sticket_id'stickets
      mysql> CREATE TABLE tickets (
      
                 ticket_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
      
                 seat_no BIGINT    
      
             ) ENGINE = InnoDB;
    5. 您的数据库、用户帐户和表现已就位。从 MySQL 服务器注销。
      mysql> QUIT;

      在下一步中,您将创建一个 Golang 脚本来接受通过 HTTPS 传入的工单请求。

四、创建文件main.go

要将此应用程序与其他 Linux 文件分开,您需要一个单独的源代码目录。

  1. 创建目录.project
    $ mkdir project
  2. 然后,切换到新目录.project
    $ cd project
  3. 接下来,用于创建文件。此文件包含运行应用程序时触发的主脚本.nanomain.go
    $ nano main.go
  4. With the file opened, paste the following information into the file.main.go
    package main
    
    
    
    import (
    
    "encoding/json"
    
    "fmt"
    
    "net/http"
    
    "strconv"
    
    )
    
    
    
    func main() {
    
        http.HandleFunc("/tickets", httpHandler)            
    
        http.ListenAndServe(":8080", nil)
    
    }
    
    
    
    func httpHandler(w http.ResponseWriter, req *http.Request) { 
    
    
    
        var err error
    
        resp := map[string]interface{}{}
    
    
    
        resp, err = newTicket() 
    
    
    
        enc := json.NewEncoder(w)
    
        enc.SetIndent("", "  ") 
    
    
    
        if err != nil {
    
            resp = map[string]interface{}{"error": err.Error(),}     
    
        }
    
    
    
        if err := enc.Encode(resp); err != nil {
    
            fmt.Println(err.Error())
    
        }
    
    
    
    }
    
    
    
    func newTicket() (map[string]interface{}, error) {
    
    
    
        seatNo, err := createTicket("test")
    
    
    
        if err != nil {
    
            return nil, err
    
        }
    
    
    
        resp := map[string]interface{}{"Response" : "Seat # " + strconv.FormatInt(seatNo, 10) + " booked successfully.",}
    
    
    
        return resp, nil          
    
    
    
    }
  5. Save and close the file when you’re through with editing.
  6. 在上面的文件中,你将导入包,该包允许你格式化 JSON 数据。接下来,你已包含用于格式化和输出字符串的包。该包允许您将其他数据类型转换为字符串格式,同时库提供 HTTP 实现。main.goencoding/jsonfmtstrconvnet/http
  7. 在 main 函数 () 下,您正在侦听 URL 中端口上的传入 HTTP 请求。然后,您将 HTTP 请求重定向到函数,该函数又使用该语句调用该函数。func main() {...}8080/ticketsfunc httpHandler(){...}newTicket()resp, err = newTicket()
  8. 在该函数下,您将使用该语句调用该函数,以从 Redis 服务器获取乘客的座位号。在下一步中,您将在新文件中创建函数。func newTicket(){}createTicket(...)seatNo, err := createTicket("test")createTicket(...)tickets.go

五、 创建文件tickets.go

在此步骤中,您将创建一个连接到 Redis 服务器的 Golang 脚本。首先,脚本将读取一个键,用于检查可供预订的座位总数。然后,如果剩余席位数大于或等于 1,则脚本将保留一个席位号,将剩余席位数减 1 并返回分配给调用脚本的席位。testseat_no

  1. 用于创建文件。nanotickets.go
    $ nano tickets.go
  2. 然后,在文件中输入以下信息。tickets.go
    package main
    
    
    
    import (
    
    "context"
    
    "errors"
    
    "strconv"
    
    "github.com/go-redis/redis"
    
    )
    
    
    
    func createTicket(key string) (int64, error) {
    
    
    
        ctx := context.Background()
    
    
    
        redisClient := redis.NewClient(&redis.Options{
    
            Addr: "localhost:6379",
    
            Password: "",
    
            DB: 0,
    
        })
    
    
    
        var seatNo int64
    
    
    
        err := redisClient.Watch(ctx, func(tx *redis.Tx) error {
    
    
    
            val, err := tx.Get(ctx, key).Int64()
    
    
    
            if err != nil && err != redis.Nil {
    
                return err
    
            }
    
    
    
            seatNo = val
    
    
    
            if (seatNo - 1) < 0 {
    
                return errors.New("Unable to secure a seat.\r\n")
    
            }
    
    
    
            _, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
    
    
    
                pipe.Set(ctx, key, strconv.FormatInt(seatNo - 1, 10), 0)    
    
    
    
                return nil
    
            })
    
    
    
            if err == nil {
    
                insertRecord(seatNo)
    
            }
    
    
    
    return err
    
    
    
    }, key)
    
    
    
        if err == redis.TxFailedErr {               
    
            return createTicket(key)
    
    }
    
    
    
    return seatNo, err
    
    }
  3. 保存并关闭文件。
  4. 在上面的文件中,您已导入包,以使用语句为 Redis 调用提供不受限制的截止时间。然后,使用包将自定义错误返回到调用函数。该软件包允许您在 Golang 脚本中实现 Redis 函数。contextctx := context.Background()errorsgithub.com/go-redis/redis
  5. 在 中,您接受 1 个参数。这是您用于在应用程序中保留可用席位的名称。在本教程中,你将用作键名称。在生产环境中,您可以考虑使用更有意义/更具描述性的名称,例如 。func createTicket(key string) (int64, error){}keytestavailable_seats
  6. 该语句允许您连接并创建新的 Redis 客户端实例。然后,您使用语句初始化一个空变量。脚本分配座位号后,您将填充此变量。redisClient := redis.NewClient(...)seatNovar seatNo int64
  7. 接下来,您将使用 Redis 函数,该语句在事务的生存期内监视密钥。如果另一个会话以任何方式修改了密钥,则整个事务应中止,并且您已对脚本进行了编码,以便使用语句重试脚本。请记住,在生产环境中,客户可以从不同的应用程序(例如移动应用程序、API、桌面应用程序、门户等)购买票证。这里的想法是一次发行一张票以避免超额预订。WATCHerr := redisClient.Watch(ctx, func()...{...}, key)testtestif err == redis.TxFailedErr { return createTicket(key) }
  8. 在函数内部,您可以使用语句检索剩余席位的值。如果没有剩余席位,您将使用 语句 抛出自定义错误。WATCHval, err := tx.Get(ctx, key).Int64()if (seatNo - 1) < 0 { return errors.New("Unable to secure a seat.\r\n") }
  9. 接下来,预订座位后,您将使用语句减少可用座位数。Redis 管道允许您在一次网络调用中将多个命令传输到 Redis 服务器。在本教程中仅执行一个命令时,应始终使用管道模型,以便在应用程序逻辑发生更改时更轻松地进行修改。pipe.Set(ctx, key, strconv.FormatInt(seatNo - 1, 10), 0)
  10. 然后,您调用该函数以将票证信息保存到 MySQL 数据库,以防使用该语句执行流水线命令时没有错误。一旦整个函数运行,它应该返回文件或任何错误,以防遇到任何错误。insertRecord()if err == nil { insertRecord(seatNo) }createTicket()seatNomain.go
  11. 在下一步中,您将在不同的文件中创建要在此调用的函数。insertRecord()tickets.godatabase.go

六、创建文件database.go

您将为此抢票应用程序创建的最后一个脚本是文件。此文件包含将票证信息永久存储到 MySQL 数据库的逻辑。database.go

  1. 使用 Nano 创建文件。database.go
    $ nano database.go
  2. 然后,在文件中输入以下信息。database.go
    package main
    
    
    
    import (
    
        "database/sql"
    
        _ "github.com/go-sql-driver/mysql"      
    
    )
    
    
    
    func insertRecord(seatNo int64) error {
    
    
    
        dbUser     := "bookings_user"
    
        dbPassword := "EXAMPLE_PASSWORD"
    
        dbName     := "bookings"
    
    
    
        db, err := sql.Open("mysql", dbUser + ":" + dbPassword + "@tcp(127.0.0.1:3306)/" + dbName) 
    
    
    
        if err != nil {
    
            return err
    
        }
    
    
    
        defer db.Close()
    
    
    
        queryString := "insert into tickets (seat_no) values (?)"
    
    
    
        stmt, err   := db.Prepare(queryString) 
    
    
    
        if err != nil {
    
            return err       
    
        }
    
    
    
        defer stmt.Close()     
    
    
    
        _, err = stmt.Exec(seatNo) 
    
    
    
        if err != nil {
    
            return err
    
        }
    
    
    
        return nil
    
    }
  3. 保存并关闭文件。
  4. 在上面的文件中,你正在使用 and 包来实现 Golang 中的 SQL 和 MySQL 功能。在该函数下,你将使用之前创建的凭据连接到 MySQL 数据库。然后,您将工单信息保存到表中。database/sqlgithub.com/go-sql-driver/mysqlfunc insertRecord(...) error {...}tickets
  5. 现在,您已经编写了使用 MySQL 和 Golang 运行 Redis 事务的所有脚本。在下一步中,您将测试一切是否按预期工作。

七、 测试 Redis 事务应用程序

您的 Golang 事务应用程序现在已经准备好进行测试了。

  1. 在执行应用程序之前,请导入已在应用程序中实现的所有包。
    $ go get github.com/go-redis/redis
    
    $ go get github.com/go-sql-driver/mysql
  2. 接下来,打开 Redis 命令行界面。
    $ redis-cli
  3. 通过将键的值设置为 .10test10
    $ SET test 10
  4. 从 Redis 服务器注销。
    $ QUIT
  5. 确保您仍在目录下,然后执行以下命令以运行 Golang 应用程序。project
    $ go run ./
  6. 上面的命令有一个阻塞功能,可以在端口下旋转 Web 服务器。不要在此终端窗口上运行任何其他命令。8080
  7. 接下来,在新的终端窗口中通过 SSH 连接到您的服务器并安装 Apache Bench () 软件包。你将使用此工具向应用程序发送并行票证请求命令,以查看它是否可以处理事务,而不会出现任何超额预订或争用情况。ab
    $ sudo apt install -y apache2-utils
  8. 接下来,向应用程序发送并行票证请求。请记住,您只在 Redis 服务器中使用了席位。因此,只有事务应该成功,其余事务应该失败。此外,由于您已使用该函数实现了 Redis 锁,因此不应出现不同会话具有相同情况的情况。201010seat_noWATCH
    $ ab -v 2 -n 20 -c 20 http://localhost:8080/tickets
  9. 您应该会收到以下响应。
    ...
    
    {
    
      "Response": "Seat # n booked successfully."
    
    }
    
    
    
    ...
    
    
    
    {
    
      "error": "Unable to secure a seat.\r\n"
    
    }
    
    
    
    ...
  10. 接下来,在仍然登录到第二个终端窗口的同时,登录到MySQL数据库以确认新的更改。root
    $ sudo mysql -u root -p
  11. 输入 MySQL 服务器的密码,然后按继续。然后,切换到数据库。rootENTERbooking
    mysql> USE bookings;
  12. 对表运行语句。SELECTtickets
    mysql> SELECT
    
               ticket_id,
    
               seat_no
    
           FROM tickets;
  13. 您现在应该看到以下票证和关联的 .从以下输出中可以看出,没有超额预订的情况。此外,该脚本已成功消除了任何竞争条件的机会,因为没有两张票具有相同的。seat_no'sseat_no
    +-----------+---------+
    
    | ticket_id | seat_no |
    
    +-----------+---------+
    
    |         1 |      10 |
    
    |         2 |       9 |
    
    |         3 |       7 |
    
    |         4 |       8 |
    
    |         5 |       6 |
    
    |         6 |       5 |
    
    |         7 |       3 |
    
    |         8 |       4 |
    
    |         9 |       1 |
    
    |        10 |       2 |
    
    +-----------+---------+
    
    10 rows in set (0.00 sec)
  14. 您的脚本现在按预期工作。

八、结论

在本指南中,您已经在 Linux 服务器中实现了 Redis 事务并使用 Golang 和 MySQL 数据库锁定,以创建抢票应用程序。使用本指南中的逻辑可避免在创建多用户应用程序时出现争用条件和数据库不一致。

重启 Linux 服务器系统:shutdown -r now 与 reboot 命令区别

在 Linux 命令中 reboot 是重新启动,shutdown -r now 是立即停止然后重新启动,都说他们两个是一样的,其实是有一定的区别的。

shutdown 命令可以安全地关闭或重启 Linux 系统,它在系统关闭之前给系统上的所有登录用户提示一条警告信息。该命令还允许用户指定一个时间参数,可以是一个精确的时间,也可以是从现在开始的一个时间段。

精确时间的格式是 hh:mm,表示小时和分钟,时间段由+ 和分钟数表示。系统执行该命令后会自动进行数据同步的工作。

该命令的一般格式: shutdown [选项] [时间] [警告信息]

1、命令中各选项的含义:

– k 并不真正关机而只是发出警告信息给所有用户

– r 关机后立即重新启动,shutdown -r now

– h 关机后不重新启动

– f 快速关机重启动时跳过 fsck

– n 快速关机不经过 init 程序

– c 取消一个已经运行的 shutdown

需要特别说明的是该命令只能由超级用户使用。

例1,系统在十分钟后关机并且马上重新启动: # shutdown –r +10

例2,系统马上关机并且不重新启动:# shutdown –h now

halt 是最简单的关机命令,其实际上是调用 shutdown -h 命令。halt 执行时,杀死应用进程,文件系统写操作完成后就会停止内核。

2、halt命令的部分参数含义:

[-f] 没有调用 shutdown 而强制关机或重启 [-i] 关机或重新启动之前,关掉所有的网络接口 [-p] 关机时调用 poweroff,此选项为缺省选项

reboot 的工作过程与 halt 类似,其作用是重新启动,而 halt 是关机。其参数也与 halt 类似。reboot 命令重启动系统时是删除所有的进程,而不是平稳地终止它们。因此,使用 reboot 命令可以快速地关闭系统,但如果还有其它用户在该系统上工作时,就会引起数据的丢失。所以使用 reboot 命令的场合主要是在单用户模式。

init 是所有进程的祖先,其进程号始终为 1。init 用于切换系统的运行级别,切换的工作是立即完成的。init 0 命令用于立即将系统运行级别切换为 0,即关机。init 6 命令用于将系统运行级别切换为 6,即重新启动。

系统重新启动总结:reboot,init 6,shutdown -r now

系统关机总结:init 0, shutdown -h now, half

3、关机命令:

1、halt 立刻关机

2、poweroff 立刻关机

3、shutdown -h now 立刻关机(root用户使用)

4、shutdown -h 10 10分钟后自动关机

Linux 新增用户并赋予 sudo 权限

介绍

文章中使用的 Linux 为 Debian 9。同样适用于其他 Linux 发行版本。

开始

推荐使用 adduser 命令新增,新增出来直接是配置好的。。而使用 useradd 命令,可能会出现一些问题。

1. 步骤详情:

  1. hkb02:~# adduser userx # 这里的 userx 即为需要新增的用户
  2. Adding user `userx’ …
  3. Adding new group `userx‘ (1000) …
  4. Adding new user `userx’ (1000) with group `userx’ …
  5. The home directory `/home/userx‘ already exists. Not copying from `/etc/skel’.
  6. Enter new UNIX password: # 这里看不到密码,直接输入就好了。
  7. Retype new UNIX password: # 重复一遍
  8. passwd: password updated successfully
  9. Changing the user information for userx
  10. Enter the new value, or press ENTER for the default
  11. Full Name []: userx # 随意填写
  12. Room Number []: 404 # 随意填写
  13. Work Phone []: 123456 # 随意填写
  14. Home Phone []: 123456 # 随意填写
  15. Other []: # 随意填写
  16. Is the information correct? [Y/n] y # 输入 y 即可。

2. 安装 sudo。

  1. aptget install sudo

3. 配置 sudo。

请将下面的 userx 更换为您自己的用户名。

第一种方案:

-1- 设置 sudoers 文件为可写。

  1. chmod +w /etc/sudoers

-2- 编辑 sudoers

  1. vim /etc/sudoers

# 在 root ALL=(ALL) ALL 后面新增:

两种方式(根据需要任选其一):

  1. userx ALL=(ALL) ALL #可使用 sudo 的用户,执行 sudo 时需要密码。
  1. userx ALL=NOPASSWD:ALL #可使用 sudo 的用户,执行 sudo 时不需要密码。

-3- 重新设置 sudoers 为只读:

  1. chmod w /etc/sudoers

第二种方案(推荐):

  1. usermod a G sudo userx

 

Linux 下 PHP 安装 fileinfo 扩展模块

介绍

由于 PHP 默认的安装配置不带 fileinfo,恰好安装的 PHP 程序需要这个,所以本文来介绍一下如何安装。

安装过程

1.1 首先下载当前正使用的 PHP 版本的源码,可通过 php.net/download 官网下载。

1.2 解压下载的源码,并进入目录。

cd ${php 源码的位置}/ext/fileinfo

1.3 执行 phpize

/usr/local/php/bin/phpize

phpize 是什么?
php官方的说明:http://php.net/manual/en/install.pecl.phpize.php
phpize 是用来扩展 php 扩展模块的工具,通过 phpize 可以建立 php 的外挂模块,比如你想在原来编译好的 php 中加入 memcached 或者 ImageMagick 等扩展模块,均可以使用 phpize。

返回信息如下:

  1. Configuring for:
  2. PHP Api Version: 20151012
  3. Zend Module Api No: 20151012
  4. Zend Extension Api No: 320151012

1.3 编译 fileinfo

./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

编译成功大概输出如下:

  1. Build complete.
  2. Don‘t forget to run ‘make test‘.
  3.  
  4. Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/

其中,Installing shared extensions 的内容就是您的扩展存放位置。

也可通过如下命令得到:

${php 的安装位置}/bin/php-config --extension-dir

1.4 填写扩展配置:

复制粘贴,修改 extension 目录为您自己的 php 扩展存放目录即可。

  1. cat > /usr/local/php/etc/php.d/extfileinfo.ini <
  2. 1.5 重启 phpfpm 进程
  3. service php-fpm restart

 

安装完成。

Linux iptables 内网转发映射方案

案例

我在服务器上有一个虚拟机,需要将虚拟机(内网)的某个端口转发到公网上面。下面利用 iptables 做一下转发。

示例如下:
服务器:
公网 IP 地址:195.152.152.52
网卡出网端口:eno1
需要转发到的端口:3388

虚拟机:
内网 IP 地址:192.168.122.11
内网网关地址:192.168.122.1
需要映射到公网的端口:3389

方法

临时方法:
首先开启 ip 转发,这个在服务器上面一般是开启的,所以不用:
echo 1 > /proc/sys/net/ipv4/ip_forward

命令如下:

在服务器上执行,虚拟机无需任何改动:

  1. iptables A INPUT p tcp m state state NEW m tcp dport 3388 j ACCEPT
  2. iptables t nat A PREROUTING s 0/0 d p tcp dport 3388 j DNAT to 192.168.122.11:3389
  3. iptables t nat A POSTROUTING d 192.168.122.11 p tcp dport 3389 o eno1 j SNAT to 192.168.122.1

这样即可完成转发。只转发 tcp 数据。

如需重启后仍然生效,可编辑 iptables 文件,保存命令即可。

Linux | CentOS 使用 yum update 时不升级系统和内核

问题

centos 系列在使用 yum update 时,会默认升级内核和系统。不像 Debian/Ubuntu 系列那样,update 和 upgrade 是分开的。这可能会导致出现一些问题,例如驱动、软件不兼容新版本内核/系统组件等,在生产环境下,为了避免因为更新内核导致出现问题,可以禁止 centos 自动更新内核和系统。

解决方案

Centos 内置了一个系统更新配置文件,位于 /etc/yum.conf。如果要禁止升级内核的话,我们只需要在其 [main] 部分末尾新增 “exclude=kernel*” 就可以了。如果要禁止升级系统,那么需要在其 [main] 部分末尾再新增 “exclude=centos-release*” 即可。

实现步骤

  1. vim /etc/yum.conf

在 [main] 的最后添加:

  1. #禁止更新内核
  2. exclude=kernel*
  1. # 禁止更新系统
  2. exclude=centosrelease*

假如您不想修改配置文件,则这样执行命令即可(例如不更新内核):

  1. yum exclude=kernel* update

Linux | Centos/Debian 修改系统时区

案例

很多朋友拿到一台 VPS,执行 date -R 发现时区并不是北京时间。为了业务上的需要,我们需要改掉它。

解决过程

Centos 7+

在 CentOS 7 中可以使用 timedatectl 命令帮助我们修改服务器的时区。

我们可以使用 timedatectl 命令查看目前服务器里的时区设置:

Local time: Sat 2016-04-09 12:34:29 CST
Universal time: Sat 2016-04-09 04:34:29 UTC
RTC time: Sat 2016-04-09 04:34:29
Time zone: Asia/Shanghai (CST, +0800)
NTP enabled: n/a
NTP synchronized: no
RTC in local TZ: no
DST active: n/a

可以看到目前服务器里的所有设置比如时间、时区、NTP 等等。

查看所有的时区:

timedatectl list-timezones

设置时区:

timedatectl set-timezone timezones

例如我们需要设置成上海的时间区:

timedatectl set-timezone Asia/Shanghai

设置完时区后当然还可以设置NTP服务器的开启和关闭:

开启NTP:”timedatectl set-ntp yes

关闭NTP:”timedatectl set-ntp no

当然 timedatectl 命令还可以设置日期与时间:

设置日期:”timedatectl set-time YYYY-MM-DD

设置时间:”timedatectl set-time HH:MM:SS

— 注意此方法仅适用于:CentOS 7+ / RHEL 7+ / Fedora 20+

Debian 7+

在网上很多使用通过添加tz变量来设置时区的,但是并不推荐。明明有更快捷的方法,为什么不用呢?

直接执行如下命令:

dpkg-reconfigure tzdata

在列表中选择:Asia  回车 Shanghai 回车即可。

Linux | Centos 7 修改主机名

介绍

在 CentOS 或 RHEL 中,有三种定义的主机名:a、静态的(static),b、瞬态的(transient),以及 c、灵活的(pretty)。“静态”主机名也称为内核主机名,是系统在启动时从 /etc/hostname 自动初始化的主机名。“瞬态”主机名是在系统运行时临时分配的主机名,例如,通过 DHCP 或 mDNS 服务器分配。静态主机名和瞬态主机名都遵从作为互联网域名同样的字符限制规则。而另一方面,“灵活”主机名则允许使用自由形式(包括特殊/空白字符)的主机名,以展示给终端用户(如Kane’s Computer)。

解决过程

<host-name> 是指要修改的主机名称,不包含<>。

1. 设置 静态 主机名:

  1. hostnamectl static sethostname

2. 设置 瞬态 主机名:

  1. hostnamectl transient sethostname

3. 设置 灵活 主机名:

  1. hostnamectl pretty sethostname

— 所有对主机名的修改,重新连接 ssh / 终端 即可生效,无需重启系统。

4. 为了使本机的主机名统一,还需要修改在 /etc/hosts 中的主机名称:

  1. vim /etc/hosts

将其修改为指定的主机名即可。重启系统后生效。

Linux 小知识 | Centos 删除残留的老内核

案例

有强烈强迫症,看着开机 grub 菜单多出来的旧内核不舒服。于是想手动删掉它。

解决过程

一:删除旧内核:

1. 先看看装了几个内核包吧:

  1. rpm qa | grep kernel

例如如下:

kernel-3.10.0-327.10.1.el7.x86_64
kernel-3.10.0-327.13.1.el7.x86_64
kernel-tools-libs-3.10.0-327.13.1.el7.x86_64
kernel-headers-3.10.0-327.13.1.el7.x86_64
kernel-devel-3.10.0-327.10.1.el7.x86_64
kernel-3.10.0-327.el7.x86_64
kernel-devel-3.10.0-327.13.1.el7.x86_64
kernel-tools-3.10.0-327.13.1.el7.x86_64

2. 删掉旧版本:

  1. yum remove kernel3.10.0327.10.1.el7.x86_64 y
  2. yum remove kerneldevel3.10.0327.10.1.el7.x86_64 y
  3. yum remove kernel3.10.0327.el7.x86_64 y

3. 设置系统只保留两个内核,将 installonly_limit= 由 5 改为 2:

  1. vim /etc/yum.cfg

3. 重启系统:

  1. reboot

这样以后内核更新后,系统只会保留两个最新的内核。而不是一大堆了。

Linux 小技巧 | 解决 CentOS 7 Root 密码忘记的问题

案例

某天大脑短路把 Root 密码忘记了。并且系统所有的管理员账户也无法登录。不想重装系统。那怎么办?

解决思路

开机grub菜单下进入单用户模式进行修改密码/修改系统文件。

解决过程

注:本教程只适用于 CentOS 7+ / RHEL 7+ / Fedora 20+

1. 首先。重启一下系统。在开机 grub 内核选择菜单那里,迅速按“e”。

2. 将光标移动到“linux 16”开头的那一行,将原来的“ro”改为“rw”,并在后面追加“init=/sysroot/bin/sh”。改完后按 Ctrl+X 保存并启动系统。

3. 成功进入单用户模式。

4. 输入 chroot /sysroot 切换根目录。

5. 输入 passwd root 重置密码。会要求输入两次。并且密码是看不到的。成功后会提示 successfully。

6. 输入 touch /.autorelabel 更新一下系统信息。

7. 执行 exit 退出 chroot 模式。

重启系统后输入修改的密码即可正常登录。