设为首页 收藏本站
查看: 442|回复: 0

[经验分享] 使用 Ansible 管理 MySQL 复制

[复制链接]

尚未签到

发表于 2017-11-22 19:38:57 | 显示全部楼层 |阅读模式
Ansible 是一个新兴的 IT 自动化工具。本文将介绍如何通过 Ansible 配置及管理 MySQL 主、从复制环境,实现部署过程自动化,体验 Ansible 简单快速带来的快感。





简介:












  Ansible 是一个配置管理和应用部署工具,功能类似于目前业界的配置管理工具 Chef,Puppet,Saltstack。Ansible 是通过 Python 语言开发。Ansible 平台由 Michael DeHaan 创建,他同时也是知名软件 Cobbler 与 Func 的作者。Ansible 的第一个版本发布于 2012 年 2 月,相比较其它同类产品来说,Ansible 还是非常年轻的,但这并不影响他的蓬勃发展与大家对他的热爱。
  Ansible 默认通过 SSH 协议管理机器,所以 Ansible 不需要安装客户端程序在服务器上。您只需要将 Ansible 安装在一台服务器,在 Ansible 安装完后,您就可以去管理控制其它服务器。不需要为它配置数据库,Ansible 不会以 daemons 方式来启动或保持运行状态。Ansible 的目标有如下:


  • 自动化部署应用
  • 自动化管理配置
  • 自动化的持续交付
  • 自动化的(AWS)云服务管理。
  根据 Ansible 官方提供的信息,当前使用 Ansible 的用户有:evernote、rackspace、NASA、Atlassian、twitter 等。



回页首

安装前的准备

Ansible 服务器需求
  当前 Ansible 可以运行在安装了 Python2.6 的任何机器上(暂不支持 windows 机器做中心控制服务器),这包括 RedHat,Debian,CentOS,OS X,BSDS 等等。

被管理服务器需求
  在被管理节点服务器上,需要安装 Python2.4 或更高的 Python 版本,如果在远程机器上运行的 Python 小于 Python2.5 的话,您将需要安装 python-simplejson。如果在远程机器的启用了 SELinux,你还需要安装 libselinux-python。



回页首

安装 Ansible
  如果你是 CentOS/RedHat、Debian、Ubuntu 的用户,您可以使用系统包管理器 yum 或 apt-get 安装(CentOS/RedHat 需要安装 epel 包才能通过 yum 安装)。不过在这里,笔者强烈建议您使用 pip 安装 Ansible。那么什么是 pip 呢? pip 是一个 Python 包安装和管理工具,功能类似 Node.js 的 npm、Ruby 的 gem。通过 pip 可以非常方便对 Python 包进行安装、升级、删除等管理操作。前面介绍过 Ansible 是使 Python 开发,那么自然 Ansible 也可以通过 pip 安装。如想了解 pip 的更多详情,请访问 https://pypi.python.org/pypi/pip/。

通过 pip 安装 Ansible
  在使用 pip 之前,请确保您的系统已经安装 Pthon 的 setuptools 包



yum – y install python-setuptools
  安装 pip



easy_install pip
  通过 pip 安装 Ansible
  pip install ansbile
  怎么样?通过 pip 安装 Ansible 是不是很简单,只要简单的三个步骤,就完成了操作。只要支持 Python 的操作系统,都可以使用 pip 安装。
  好的,现在你如果照着做的话,你已经在你的服务器上安装好了 Ansible。
  前面介绍过 Ansible 是通过 SSH 协议管理机器,Ansible 默认使用 SSH Keys 方式通信,这个也是 Ansible 官方极力推荐的方法,但是如果您想使用密码的话,当然也是可以的。为了启用密码验证,使用 --ask-pass 选项,在本文中,笔者采用密钥验证方式进行演示,读者可以根据的需要选择使用密码验证还是密钥验证。



回页首

Ansible 实战

使用 Ansible 在远程服务器上执行命令
  在安装好 Ansible 后,先通过使用 Ansible 在其它服务器上执行一条命令来确认 Ansible 服务器与其它服务器的连通性。在执行 Ansible 管理服务器前,需要要配置服务器信息。Ansible 使用文件来存储您要管理的服务器信息,这个文件在 Ansible 中叫清单文件,默认的清单文件存放在 /etc/ansible/hosts , 如果这个文件不存在,您可以新建该文件。当然您也可以在执行 Ansible 命令时执行清单文件的路径。清单文件的格式类似于 INI 文件,如下:



[webserver]
192.168.1.1
192.168.1.2
[databaseServer]
192.168.1.10
192.168.1.11
  中括号符号中的是组名,组名可以自定义,但建议使用一个有说明意义的名称,如 webserver、databaseServer,组名后是各组内的成员 IP。下面笔者通过 Ansible 在远程服务器上执行一条命令:



ansible webserver -a "whoami"
图 1.ansible 执行命令结果

  从 Ansible 的执行结果,我们知道 Ansible 服务器与远程服务器的连通是没有问题,并且执行命令成功。如果您想指定清单文件的路径,那么使用 – i 参数,加上文件路径即可,如下:



ansible webserver – i /etc/ansible/other-hosts – a“whoami”
  以上是一条 ad-hoc 命令,也即临时执行命令。ad-hoc 是 Ansible 里的一个概念,通过使用 ad-hoc 您可以快速的完成命令的操作,如果你的操作只包含命令操作,而没有配置管理的内容的话,那么强烈建议使用 ad-hoc 命令,如文件传输、包管理、用户和组的管理、服务管理等等。

使用 Ansible 管理 MySQL 复制
  Ansible 附带了非常多的模块,Ansible 将之称为“模块库”。模块可以在远程服务器上直接执行,也可以通过 Playbooks 执行,关于 Playbooks 将会在后面做介绍。Ansible 的模块非常丰富,包括云服务管理、文件、数据库、命令、网络等各个方面。笔者在这里演示如果通过 Ansible 其中的一个模块“mysql_replication”来管理 MySQL 的复制。首先在配置好 MySQL master 服务器与 slave 服务器的 MySQL 环境,使其满足 MySQL 复制的配置需求,并且 MySQL 主从服务器都要安装 Python 的 MySQLdb 模块,通过 Ansible 在主、从服务器上安装 MySQLdb。



ansible databaseServer -m yum -a "name=MySQL-python state=present"
  在 MySQL master 服务器上创建 MySQL 复制帐号



mysql -uroot -p
mysql > GRANT REPLICATION SLAVE,REPLICATION CLIENT
ON *.* TO 'repl'@'192.168.1.%' IDENTIFIED BY '123456';
mysql > flush privileges;
mysql > quit;
  在 MySQL master 服务器打开 mysql 的二进制日志功能 , 在 /etc/my.cnf 文件的 [mysqld] 部分添加以下内容:
  server-id = 1
  log-bin=mysql-bin
  重启 MySQL , 使设置生效。



/etc/init.d/mysqld restart
  修改 MySQL slave 数据库的 mysql 配置文件 , 在 /etc/my.cnf 文件的 [mysqld] 部分添加以下内容:
  server-id = 2
  重启 MySQL , 使设置生效。



/etc/init.d/mysqld restart
  通过 Ansible 的 mysql_replication 模块获取到 MySQL master 服务器的状态



ansible mysql-master -m mysql_replication
-a "login_user=root login_password=123456 mode=getmaster"
图 2.获取 MySQL Master 服务器状态

  Ansible 的结果都是采用的 json 格式显示,结果输出了 MySQL 的正在使用的二进制日志文件名称以及 Position 的值,记录这两个值。
  通过 Ansible 的 mysql_replication 模块在 MySQL Slave 服务器上指定 MySQL master 服务器及设置复制信息。



ansible mysql-slave -m mysql_replication
– a "login_user=root login_password=123456
mode=changemaster master_host='172.16.64.147' master_user=repl
master_password=123456 master_log_file=mysql.000003 master_log_pos=711"
图 3.MySQL Change Master 执行结果:

  在 MySQL 从服务器上启动复制



ansible mysql-slave -m mysql_replication
-a "login_user=root login_password=123456 mode=startslave"
图 4.在 MySQL Slave 开启复制执行结果:

  查看 MySQL 复制状态



ansible mysql-slave -m mysql_replication
-a "login_user=root login_password=123456 mode=getslave"
图 5.查看 MySQL 复制状态

  请注意图中红框选处,显示 Slave_IO_Running 和 Slave_SQL_Running 的值均为“Yes”,Slave_IO_State 的状态为“waiting for master to send event”,这表示刚刚通过 ansible 建立 MySQL 主从服务器之间的复制是正常的。现在您就可以尝试在 MySQL 主服务器上添加一些数据,看看会不会同步同步从服务器上。

通过 Ansible playbooks 管理 MySQL 复制
  通过使用 Ansible 的 ad-hoc 命令行指令来使用模块命令是不是感觉很便捷,但我们仍然要手工去做很多事情,如安装 MySQL、配置 MySQL 复制环境等。虽然这些也可以通过 ad-hoc 命令行完成,但是如果我下次还要在其它服务器上配置 MySQL 复制,那么我又得重复敲一遍指令,如果是这样的话,那么这将是一样非常糟糕的事情。那么 Ansible 有没有办法解决这个问题呢?当然是有,Ansible 的 Playbooks 就可以很好的解决这个问题,Playbooks 翻译成中文就是剧本的意思,顾名思义,就是将所有的操作按照约定好的规则在文件中定义好做什么,然后再按照剧本演出一部大剧。Playbooks 非常适合部署复杂应用及配置的重复使用。Playbooks 声明配置,并你设置好的配置推送到指定的远程主机应用。Playbooks 与 saltstack 的 SLS(Salt State Tree)、puppet 的配置管理相同。
  Playbooks 使用 YAML 格式,语法应用简明易懂。以下笔者就使用 Ansilbe 来实现 MySQL 的安装、MySQL 复制环境的配置、MySQL 复制设置。

图 6.目录结构图

  为了有效的组织和管理 Ansible 中的文件,笔者使用了 Ansible Playbook 的 roles 特性,这个特性在 Ansible 版本 1.2 或之后的版本中有效。笔者在 /etc/ansible 目录下建立了一个 roles 目录用于存放 Playbooks 定义的各个角色,这个是 Ansible 官方定义的默认 roles 目录,当然你可以添加或修改 roles 的目录路径,通过修改 /etc/ansible/ansible.cfg 文件中的 roles_path 值。roles 目录下是各个应用的目录名称,各目录以应用名称命令,这个名称可以自定义,但最好是设置一个有意义的名称,如本例中的 mysql,mysql 命令下包括了 defaults、handlers、meta、tasks、templates、vars 目录,分别对应不同的功能,这几个目录的命名都是 Ansible 官方定义好的,不可以修改,各目录功能说明见表 1。默认 Ansible 只处理各目录下文件名为 main.yml 中定义的操作,如果您有多个文件,可以在 main.yml 文件中 include 其它文件。

表 1.各目录功能说明

目录名说明



defaults
默认变量存放目录


handlers
处理程序(当发生改变时需要执行的操作)


meta
角色依赖关系处理


tasks
具体执行的任务操作定义


templates
模板文件存放目录


vars
变量文件目录

代码 1.defaults 目录的 main.yml 信息



cat /etc/ansible/roles/mysql/defaults/main.yml
---
mysql_port: 3306
mysql_bind_address: "0.0.0.0"
mysql_root_db_pass: 123456
mysql_db:
- name: foo
replicate: yes
- name: bar
replicate: no
mysql_users:
- name: jack
pass: 123456
priv: "*.*:ALL"
mysql_repl_user:
- name: repl
pass: 123456
mysql_repl_role: master
mysql_db_id: 7
  该文件包含了一些与 MySQL 相关的一些默认的变量信息,在这里设置了 MySQL 的端口、绑定地址,root 用户的密码,及新增的数据库、用户、复制用户信息。

代码 2.handlers 目录的 main.yml 信息



cat /etc/ansible/roles/mysql/handlers/main.yml
---
- name: restart mysql
service: name={{ mysql_service }} state=restarted
  handlers 目录中的 main.yml 文件包含的操作是执行完 tasks 之后服务器发生变化之后可供调用 的操作,本例中是重启 MySQL 操作

代码 3.tasks 目录里的 main.yml 信息



---
- name: Add the OS specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Install the mysql packages in Redhat derivatives
yum: name={{ item }} state=installed
with_items: mysql_pkgs
when: ansible_os_family == 'RedHat'
- name: Install the mysql packages in Debian derivatives
apt: name={{ item }} state=installed update_cache=yes
with_items: mysql_pkgs
environment: env
when: ansible_os_family == 'Debian'
- name: Copy the my.cnf file
template: src=my.cnf.{{ ansible_os_family }}.j2 dest={{ mysql_conf_dir }}/my.cnf
notify:
- restart mysql
- name: Start the mysql services Redhat
service: name={{ mysql_service }} state=started enabled=yes
- name: update mysql root password for all root accounts
mysql_user: name=root host={{ item }} password={{ mysql_root_db_pass }}
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
when: ansible_hostname != 'localhost'
- name: update mysql root password for all root accounts
mysql_user: name=root host={{ item }} password={{ mysql_root_db_pass }}
with_items:
- 127.0.0.1
- ::1
- localhost
when: ansible_hostname == 'localhost'

- name: ensure anonymous users are not in the database
mysql_user: name='' host={{ item }} login_user=root
login_password={{ mysql_root_db_pass }} state=absent
with_items:
- localhost
- "{{ ansible_hostname }}"
- name: remove the test database
mysql_db: name=test login_user=root
login_password={{ mysql_root_db_pass }} state=absent
- name: Create the database's
mysql_db: name={{ item.name }}
login_user=root login_password={{ mysql_root_db_pass }} state=present
with_items: mysql_db
when: mysql_db|lower() != 'none'
- name: Create the database users
mysql_user: login_user=root
login_password={{ mysql_root_db_pass }}
name={{ item.name }} password={{ item.pass|default("foobar") }}
priv={{ item.priv|default("*.*:ALL") }}
state=present host={{ item.host | default("localhost") }}
with_items: mysql_users
when: mysql_users|lower() != 'none'
- name: Create the replication users
mysql_user: login_user=root login_password={{ mysql_root_db_pass }}
name={{ item.name }} host="%" password={{ item.pass|default("foobar") }}
priv=*.*:"REPLICATION SLAVE" state=present
with_items: mysql_repl_user
when: mysql_repl_role == 'master'
- name: Check if slave is already configured for replication
mysql_replication: login_user=root
login_password={{ mysql_root_db_pass }} mode=getslave
ignore_errors: true
register: slave
when: mysql_repl_role == 'slave'
- name: Ensure the hostname entry
for master is available for the client.
lineinfile: dest=/etc/hosts regexp="{{ mysql_repl_master }}"
line="{{ mysql_repl_master + " " +
hostvars[mysql_repl_master].ansible_default_ipv4.address }}"
state=present
when: slave|failed and mysql_repl_role == 'slave'
and mysql_repl_master is defined
- name: Get the current master servers replication status
mysql_replication: login_user=root login_password=
{{ mysql_root_db_pass }} mode=getmaster
delegate_to: "{{ mysql_repl_master }}"
register: repl_stat
when: slave|failed and mysql_repl_role == 'slave'
and mysql_repl_master is defined
- name: Change the master in slave to start the replication
mysql_replication: login_user=root
login_password={{ mysql_root_db_pass }} mode=changemaster
master_host={{ mysql_repl_master }}
master_log_file={{ repl_stat.File }}
master_log_pos={{ repl_stat.Position }}
master_user={{ mysql_repl_user[0].name }}
master_password={{ mysql_repl_user[0].pass }}
when: slave|failed and mysql_repl_role == 'slave'
and mysql_repl_master is defined
- name: start slave in slave to start the replication
mysql_replication: login_user=root login_password={{ mysql_root_db_pass }}
mode=startslave
when: slave|failed and mysql_repl_role == 'slave'
and mysql_repl_master is defined
  tasks 目录中的 main.yml 包括了 playbooks 中所有要执行操作,每一个操作都会有一个名称,用于简单的描述本次的操作内容,本例中的 tasks 包括了:


  • MySQL 安装;
  • MySQL 管理员用户 root 的密码修改;
  • MySQL 配置文件的生成;
  • 测试库的删除;
  • 空用户的删除;
  • 数据库的创建(若需要);
  • 数据库用户的创建及赋权(苦需要);
  • 复制用户创建及密码设置;
  • 获取 master 状态;
  • Slave 连接 master 并开启复制。
  Ansible playbooks 中的每个任务是按序依次执行的,这个大家可以看 ansible playbooks 的执行过程。

代码 4.templates 目录中的 my.cnf.RedHat.j2



cat /etc/ansible/roles/mysql/templates/my.cnf.RedHat.j2
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to
prevent assorted security risks
symbolic-links=0
port={{ mysql_port }}
bind-address={{ mysql_bind_address }}
log_bin = mysql-bin
server-id = {{ mysql_db_id }}
{% if mysql_repl_role == 'master' %}
#log_bin = mysql-bin
expire_logs_days = 10
max_binlog_size = 100M
{% for i in mysql_db %}
{% if i.replicate|default(1) %}
binlog_do_db = {{ i.name }}
{% endif %}
{% endfor %}
{% for i in mysql_db %}
{% if not i.replicate|default(1) %}
binlog_ignore_db = {{ i.name }}
{% endif %}
{% endfor %}
{% endif %}
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
  templates 目录中包括了一个 MySQL 的配置文件样本。

代码 5.vars 目录中的 main.yml



cat /etc/ansible/roles/mysql/vars/main.yml
---
env:
RUNLEVEL: 1
代码 6.vars 目录中的 RedHat.yml



cat /etc/ansible/roles/mysql/vars/RedHat.yml
---
mysql_pkgs:
- libselinux-python
- mysql-server
- MySQL-python
mysql_service: mysqld
mysql_conf_dir: "/etc/"
  vars 目录中是此次操作所涉及到的变量信息。

代码 7.ansible 执行 MySQL 复制配置的 playbook 配置文件 mysql_repl.yml



cat /etc/ansible/mysql_repl.yml
- hosts: mysql-master
roles:
- {role: mysql, mysql_db: none,mysql_users:
[{name: jack, pass: 123456, priv: "*.*:ALL"}],mysql_db_id: 1008 }
- hosts: mysql-slave
roles:
- {role: mysql, mysql_db: none,
mysql_users: none,mysql_repl_role: slave,
mysql_repl_master: 192.168.1.10,mysql_db_id:1009,
mysql_repl_user: [{name: repl, pass: 123456}] }
  安装并配置 MySQL 服务器,并完成 MySQL 主从复制设置。
  在 /etc/ansible/hosts 文件中添加服务器信息



cat /etc/ansible/hosts
[mysql-master]
192.168.1.10
[mysql-slave]
192.168.1.11
  使用 Ansible playbook 配置 MySQL 复制



ansible-playbook mysql_repl.yml
PLAY [mysql-master] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.1.10]
TASK: [mysql | Add the OS specific variables] *********************************
ok: [192.168.1.10]
TASK: [mysql | Install the mysql packages in Redhat derivatives] **************
changed: [192.168.1.10] => (item=libselinux-python,mysql-server,MySQL-python)
TASK: [mysql | Install the mysql packages in Debian derivatives] **************
skipping: [192.168.1.10]
TASK: [mysql | Copy the my.cnf file] ******************************************
ok: [192.168.1.10]
TASK: [mysql | Start the mysql services Redhat] *******************************
changed: [192.168.1.10]
TASK: [mysql | update mysql root password for all root accounts] **************
changed: [192.168.1.10] => (item=client001)
changed: [192.168.1.10] => (item=127.0.0.1)
changed: [192.168.1.10] => (item=::1)
changed: [192.168.1.10] => (item=localhost)
TASK: [mysql | update mysql root password for all root accounts] **************
skipping: [192.168.1.10] => (item=127.0.0.1)
skipping: [192.168.1.10] => (item=::1)
skipping: [192.168.1.10] => (item=localhost)
TASK: [mysql | ensure anonymous users are not in the database] ****************
changed: [192.168.1.10] => (item=localhost)
ok: [192.168.1.10] => (item=client001)
TASK: [mysql | remove the test database] **************************************
changed: [192.168.1.10]
TASK: [mysql | Create the database's] *****************************************
changed: [192.168.1.10] => (item={'name': 'benz'})
changed: [192.168.1.10] => (item={'name': 'benz2'})
TASK: [mysql | Create the database users] *************************************
changed: [192.168.1.10] => (item={'pass': 'foobar', 'name': 'ben3', 'priv': '*.*:ALL'})
changed: [192.168.1.10] => (item={'name': 'ben2', 'pass': 'foo'})
TASK: [mysql | Create the replication users] **********************************
changed: [192.168.1.10] => (item={'name': 'repl', 'pass': 'foobar'})
TASK: [mysql | Check if slave is already configured for replication] **********
skipping: [192.168.1.10]
TASK: [mysql | Ensure the hostname entry for master is available for the client.] ***
skipping: [192.168.1.10]
TASK: [mysql | Get the current master servers replication status] *************
skipping: [192.168.1.10]
TASK: [mysql | Change the master in slave to start the replication] ***********
skipping: [192.168.1.10]
TASK: [mysql | start slave in slave to start the replication] *****************
skipping: [192.168.1.10]
#### 限于篇幅,部分输出省略 #######
PLAY RECAP ********************************************************************
192.168.1.10 : ok=12 changed=8 unreachable=0 failed=0
192.168.1.11 : ok=17 changed=8 unreachable=0 failed=0
  大家仔细看输出,会发现 ansible 的 task 是按顺序一个个依次执行,在执行每个任务时,都会显示该任务的名称及状态,这样非常利于查看任务执行的状态。然后在任务的最终结尾处(即 PLAY RECAP)是各个 task 的状态统计总结,包含了各个 task 的执行情况,如执行成功数,变更数、错误数。通过这里可以判断 task 是否成功执行完成。



回页首

Ansible 与其它配置管理的对比
  笔者选择了目前几款主流的与 Ansible 功能类似的配置管理软件 Puppet、Saltstack,这里所做的对比不针对各个软件的性能作比较,只是对各个软件的特性做个对比。具体内容见表 1:

表 2.Ansible Vs. puppet Vs. Saltstack 对比

PuppetSaltstackAnsible



开发语言
Ruby
Python
Python


是否有客户端





是否支持二次开发
不支持
支持
支持


服务器与远程机器是否相互验证





服务器与远程机器通信是否加密
是,标准 SSL 协议
是,使用 AES 加密
是,使用 OpenSSH


平台支持
支持 AIX、BSD、HP-UX、Linux、 MacOSX、Solaris、 Windows
支持 BSD、Linux、Mac OS X、Solaris、 Windows
支持 AIX、BSD、 HP-UX、 Linux、Mac OSX、Solaris


是否提供 web ui
提供
提供
提供,不过是商业版本


配置文件格式
Ruby 语法格式
YAML
YAML


命令行执行
不支持,但可通过配置模块实现
支持
支持



回页首

结束语:
  Ansible 是一个新兴的 IT 自动化管理工具。目前它的下载量已经超过了 100 万。在 GitHub,它是排名前 10 位的 Python 项目。可以预见 Ansible 的发展是不可限量。笔者在本文中使用 Anaible 来管理 MySQL 复制来向大家介绍了 Ansible 一些使用方法和应用场景,希望通过本文,能够让大家都爱上这个超级有能量的系统自动化管理工具。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-409616-1-1.html 上篇帖子: Zsh vs. Bash不完全对比解析,zsh是一种更强大的被成为“终极”的Shell 下篇帖子: GNU-Linux 简单介绍[转载]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表