Repository: dunwu/linux-tutorial Branch: master Commit: b672dd042960 Files: 351 Total size: 742.6 KB Directory structure: gitextract_eglus17c/ ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── assets/ │ ├── docker.xmind │ └── linux.xmind ├── codes/ │ ├── docker/ │ │ └── docker-compose-demo/ │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── docker-compose.yml │ │ └── run.sh │ ├── expect/ │ │ └── 远程登录.sh │ ├── linux/ │ │ ├── README.md │ │ ├── build/ │ │ │ ├── Java应用运行脚本模板/ │ │ │ │ ├── lifecycle.sh │ │ │ │ ├── start.sh │ │ │ │ ├── stop.sh │ │ │ │ └── utils.sh │ │ │ ├── README.md │ │ │ ├── helper.sh │ │ │ ├── java-app-boot.sh │ │ │ ├── java-app-release.sh │ │ │ ├── java-app-run.sh │ │ │ ├── js-app-release.sh │ │ │ ├── main.sh │ │ │ ├── spring-boot-run.sh │ │ │ └── update-code.sh │ │ ├── conf/ │ │ │ └── etc/ │ │ │ └── sysctl.conf │ │ ├── download.sh │ │ ├── dunwu-ops.sh │ │ ├── dunwu-soft.sh │ │ ├── dunwu-sys.sh │ │ ├── libtest/ │ │ │ ├── README.md │ │ │ ├── env-test.sh │ │ │ ├── git-check.sh │ │ │ ├── git-update.sh │ │ │ └── nodejs-test.sh │ │ ├── soft/ │ │ │ ├── README.md │ │ │ ├── arthas-install.sh │ │ │ ├── config/ │ │ │ │ ├── fastdfs/ │ │ │ │ │ ├── client.conf │ │ │ │ │ ├── storage.conf │ │ │ │ │ └── tracker.conf │ │ │ │ ├── mysql/ │ │ │ │ │ └── my.cnf │ │ │ │ ├── nginx/ │ │ │ │ │ ├── conf/ │ │ │ │ │ │ └── fdfs.conf │ │ │ │ │ ├── nginx.conf │ │ │ │ │ └── nginx.service │ │ │ │ ├── redis/ │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── 27001/ │ │ │ │ │ │ │ └── sentinel.conf │ │ │ │ │ │ ├── 27002/ │ │ │ │ │ │ │ └── sentinel.conf │ │ │ │ │ │ ├── 27003/ │ │ │ │ │ │ │ └── sentinel.conf │ │ │ │ │ │ ├── 27004/ │ │ │ │ │ │ │ └── sentinel.conf │ │ │ │ │ │ ├── 27005/ │ │ │ │ │ │ │ └── sentinel.conf │ │ │ │ │ │ ├── 27006/ │ │ │ │ │ │ │ └── sentinel.conf │ │ │ │ │ │ ├── 7001/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7002/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7003/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7004/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7005/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7006/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7007/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7008/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7009/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7010/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7011/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── 7012/ │ │ │ │ │ │ │ └── redis.conf │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── redis-cluster.sh │ │ │ │ │ │ ├── redis-cluster2.sh │ │ │ │ │ │ ├── start-cluster.sh │ │ │ │ │ │ ├── start-cluster2.sh │ │ │ │ │ │ └── start-sentinel.sh │ │ │ │ │ ├── redis.conf │ │ │ │ │ └── redis.service │ │ │ │ └── settings-aliyun.xml │ │ │ ├── docker-install.sh │ │ │ ├── elk/ │ │ │ │ ├── boot-elk.sh │ │ │ │ ├── config/ │ │ │ │ │ ├── filebeat.yml │ │ │ │ │ ├── logback.xml │ │ │ │ │ └── logstash.conf │ │ │ │ ├── install-elk.sh │ │ │ │ ├── install_elasticserch.sh │ │ │ │ ├── install_filebeat.sh │ │ │ │ ├── install_kibana.sh │ │ │ │ └── install_logstash.sh │ │ │ ├── fastdfs-install.sh │ │ │ ├── gitlab/ │ │ │ │ ├── gitlab-backup.sh │ │ │ │ └── gitlab.sh │ │ │ ├── gitlab-install.sh │ │ │ ├── install_grafana.sh │ │ │ ├── jdk8-install.sh │ │ │ ├── jenkins-install.sh │ │ │ ├── kafka-install.sh │ │ │ ├── lib/ │ │ │ │ ├── docker.sh │ │ │ │ ├── file.sh │ │ │ │ ├── git.sh │ │ │ │ ├── java.sh │ │ │ │ ├── maven.sh │ │ │ │ ├── mysql.sh │ │ │ │ ├── net.sh │ │ │ │ ├── nodejs.sh │ │ │ │ ├── string.sh │ │ │ │ └── utils.sh │ │ │ ├── maven-install.sh │ │ │ ├── mongodb-install.sh │ │ │ ├── mysql-backup.sh │ │ │ ├── mysql-install.sh │ │ │ ├── mysql-recovery.sh │ │ │ ├── nacos-install.sh │ │ │ ├── nexus-install.sh │ │ │ ├── nginx-install.sh │ │ │ ├── nodejs-install.sh │ │ │ ├── redis-install.sh │ │ │ ├── rocketmq-install.sh │ │ │ ├── tomcat8-install.sh │ │ │ ├── zookeeper-install.sh │ │ │ ├── zsh/ │ │ │ │ └── oh-my-zsh-install.sh │ │ │ └── zsh-install.sh │ │ ├── sys/ │ │ │ ├── README.md │ │ │ ├── change-yum-repo.sh │ │ │ ├── install-libs.sh │ │ │ ├── install-tools.sh │ │ │ ├── set-dns.sh │ │ │ ├── set-ntp.sh │ │ │ ├── stop-firewall.sh │ │ │ ├── sys-settings.sh │ │ │ ├── syscheck.sh │ │ │ └── yum/ │ │ │ ├── Centos-5.repo │ │ │ ├── Centos-6.repo │ │ │ └── Centos-7.repo │ │ └── tool/ │ │ ├── Autoinstall_ELK_V1.3.sh │ │ ├── Cpu_Limit.sh │ │ ├── Custom_Rm.sh │ │ ├── Daily_Archive.sh │ │ ├── Hourly_Archive.sh │ │ ├── gitcheck │ │ ├── 删除用户脚本.sh │ │ └── 查看指定目录磁盘使用情况.sh │ └── shell/ │ ├── README.md │ ├── lib/ │ │ └── env.sh │ ├── mysql/ │ │ ├── SQL批处理执行脚本.sh │ │ ├── 向数据库中插入数据.sh │ │ ├── 格式化输出数据.sh │ │ └── 连接数据库并发送命令.sh │ ├── 基本脚本/ │ │ ├── echo示例.sh │ │ ├── exit命令.sh │ │ ├── printf示例.sh │ │ ├── 使用expr执行数学运算.sh │ │ ├── 使用内联重定向计算表达式.sh │ │ ├── 使用方括号执行数学运算.sh │ │ ├── 反引号的使用.sh │ │ ├── 变量使用示例.sh │ │ ├── 在脚本中使用bc.sh │ │ ├── 字符串使用示例.sh │ │ ├── 数组使用示例.sh │ │ ├── 显示时间和登录者.sh │ │ ├── 显示系统变量和转义字符.sh │ │ ├── 注释示例.sh │ │ └── 通过反引号获得当前日期并生成唯一文件名.sh │ ├── 控制/ │ │ ├── 定时执行脚本.sh │ │ ├── 捕捉信号.sh │ │ ├── 捕捉脚本的退出.sh │ │ └── 移除捕捉.sh │ ├── 操作符/ │ │ ├── 关系运算符.sh │ │ ├── 字符串运算符.sh │ │ ├── 布尔运算符.sh │ │ ├── 文件测试运算符.sh │ │ ├── 算术运算符.sh │ │ └── 逻辑运算符.sh │ ├── 文件操作/ │ │ ├── log.txt │ │ ├── 创建临时文件.sh │ │ ├── 创建临时目录.sh │ │ ├── 文件路径操作.sh │ │ ├── 目录基本操作.sh │ │ └── 读文件.sh │ ├── 查找替换文本/ │ │ ├── gawk/ │ │ │ ├── gawk.sh │ │ │ ├── gawk函数库 │ │ │ ├── gawk脚本 │ │ │ ├── script │ │ │ ├── test │ │ │ ├── 使用变量.sh │ │ │ ├── 使用模式,结构化命令.sh │ │ │ ├── 自定义函数.sh │ │ │ ├── 调用函数库和脚本.sh │ │ │ └── 输出.sh │ │ ├── grep.sh │ │ ├── regex/ │ │ │ ├── 目录文件计数.sh │ │ │ └── 邮件验证.sh │ │ └── sed/ │ │ ├── sed文件操作.sh │ │ ├── sed编辑器基础.sh │ │ ├── test │ │ ├── 保持空间.sh │ │ ├── 删除指定的空白行和删除html标签.sh │ │ ├── 在脚本中使用sed.sh │ │ ├── 排除命令.sh │ │ ├── 模式替代.sh │ │ ├── 测试.sh │ │ ├── 给文件中的行编号.sh │ │ ├── 跳转.sh │ │ ├── 输出末尾指定行数的数据.sh │ │ └── 重定向sed输出.sh │ ├── 系统管理/ │ │ ├── 控制远程服务器执行指令.sh │ │ └── 系统用户管理.sh │ ├── 脚本函数/ │ │ ├── lib.sh │ │ ├── 从函数返回数组.sh │ │ ├── 使用return命令.sh │ │ ├── 使用全局变量带来的问题.sh │ │ ├── 使用函数输出.sh │ │ ├── 使用命令行中传递的参数.sh │ │ ├── 使用局部变量.sh │ │ ├── 使用库函数.sh │ │ ├── 全局变量.sh │ │ ├── 函数入参.sh │ │ ├── 函数入参2.sh │ │ ├── 函数基本示例.sh │ │ ├── 函数基本示例2.sh │ │ ├── 函数递归.sh │ │ ├── 在函数中使用参数.sh │ │ ├── 想函数传数组数据.sh │ │ ├── 累加数组中的值.sh │ │ └── 默认退出状态码.sh │ ├── 菜单/ │ │ ├── 使用msgbox部件.sh │ │ ├── 使用select命令.sh │ │ ├── 使用脚本菜单.sh │ │ └── 在脚本中使用dialog命令.sh │ ├── 输入和输出/ │ │ ├── test │ │ ├── test1 │ │ ├── 临时重定向.sh │ │ ├── 使用getopts.sh │ │ ├── 使用getopts处理选项和参数.sh │ │ ├── 使用getopt命令.sh │ │ ├── 使用shift命令.sh │ │ ├── 免密码传输.sh │ │ ├── 分离参数和选项.sh │ │ ├── 列出当前脚本打开的文件描述符.sh │ │ ├── 创建自己的重定向/ │ │ │ ├── 从以重定向的文件描述符中恢复.sh │ │ │ ├── 关闭文件描述符.sh │ │ │ ├── 创建读写文件描述符.sh │ │ │ ├── 创建输入文件描述符.sh │ │ │ └── 创建输出文件描述符.sh │ │ ├── 参数计数.sh │ │ ├── 在脚本中使用重定向输入.sh │ │ ├── 处理带值的选项.sh │ │ ├── 处理简单选项.sh │ │ ├── 快速清除文件或日志.sh │ │ ├── 抓取所有数据.sh │ │ ├── 永久重定向.sh │ │ ├── 测试.txt │ │ ├── 获取用户输入.sh │ │ ├── 记录信息.sh │ │ ├── 读取参数.sh │ │ ├── 读取多个命令行参数.sh │ │ ├── 读取程序名.sh │ │ ├── 读取选择参数.sh │ │ ├── 超时和输入计数.sh │ │ └── 隐藏方式读取数据.sh │ ├── 进阶脚本/ │ │ ├── 创建捕捉脚本.sh │ │ ├── 开启debug模式.sh │ │ ├── 查看uptime获取在线用户数.sh │ │ ├── 生成报告脚本-基于创建捕捉脚本.sh │ │ ├── 系统快照报告.sh │ │ ├── 输出颜色.sh │ │ └── 问题跟踪数据库/ │ │ ├── Update_Problem.sh │ │ ├── 查找问题.sh │ │ └── 记录问题.sh │ └── 逻辑控制/ │ ├── bash-shell无法处理浮点数.sh │ ├── break示例.sh │ ├── case示例.sh │ ├── continue示例.sh │ ├── for循环示例.sh │ ├── if-elif-else示例.sh │ ├── output.txt │ ├── select菜单示例.sh │ ├── until示例.sh │ ├── while循环示例.sh │ ├── 从命令读取值.sh │ ├── 使用双圆括号.sh │ ├── 使用双方括号.sh │ ├── 使用大于小于号.sh │ ├── 使用嵌套循环并修改IFS.sh │ ├── 使用管道或重定向.sh │ ├── 使用通配符处理目录.sh │ ├── 注意test大小写顺序和sort不同.sh │ └── 读取里表中复杂的值.sh ├── docs/ │ ├── .remarkrc │ ├── .textlint.terms.json │ ├── .textlintrc.js │ ├── .vuepress/ │ │ ├── config.js │ │ └── enhanceApp.js │ ├── README.md │ ├── docker/ │ │ ├── README.md │ │ ├── docker-cheat-sheet.md │ │ ├── docker-compose.md │ │ ├── docker-dockerfile.md │ │ ├── docker-quickstart.md │ │ ├── docker.xmind │ │ ├── kubernetes.md │ │ └── service/ │ │ ├── docker-install-mysql.md │ │ └── docker-install-nginx.md │ ├── linux/ │ │ ├── cli/ │ │ │ ├── README.md │ │ │ ├── free.md │ │ │ ├── grep.md │ │ │ ├── iostat.md │ │ │ ├── iotop.md │ │ │ ├── linux-cli-dir.md │ │ │ ├── linux-cli-file-compress.md │ │ │ ├── linux-cli-file.md │ │ │ ├── linux-cli-hardware.md │ │ │ ├── linux-cli-help.md │ │ │ ├── linux-cli-net.md │ │ │ ├── linux-cli-software.md │ │ │ ├── linux-cli-system.md │ │ │ ├── linux-cli-user.md │ │ │ ├── scp.md │ │ │ ├── top.md │ │ │ ├── vmstat.md │ │ │ └── 命令行的艺术.md │ │ ├── expect.md │ │ ├── ops/ │ │ │ ├── README.md │ │ │ ├── crontab.md │ │ │ ├── firewalld.md │ │ │ ├── iptables.md │ │ │ ├── network-ops.md │ │ │ ├── ntp.md │ │ │ ├── samba.md │ │ │ ├── systemd.md │ │ │ ├── vim.md │ │ │ └── zsh.md │ │ └── soft/ │ │ ├── README.md │ │ ├── apollo/ │ │ │ ├── README.md │ │ │ └── apollo.xmind │ │ ├── elastic/ │ │ │ ├── README.md │ │ │ ├── elastic-beats.md │ │ │ ├── elastic-kibana.md │ │ │ ├── elastic-logstash.md │ │ │ └── elastic-quickstart.md │ │ ├── fastdfs.md │ │ ├── gitlab-ops.md │ │ ├── jdk-install.md │ │ ├── jenkins-ops.md │ │ ├── kafka-install.md │ │ ├── maven-install.md │ │ ├── mongodb-ops.md │ │ ├── nacos-install.md │ │ ├── nexus-ops.md │ │ ├── nodejs-install.md │ │ ├── rocketmq-install.md │ │ ├── svn-ops.md │ │ ├── tomcat-install.md │ │ └── yapi-ops.md │ ├── mac/ │ │ └── soft/ │ │ └── ruby-install.md │ └── package.json └── scripts/ └── deploy.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig 用于在 IDE 中检查代码的基本 Code Style # @see: https://editorconfig.org/ # 配置说明: # 所有文件换行使用 Unix 风格(LF),*.bat 文件使用 Windows 风格(CRLF) # java / sh 文件缩进 4 个空格,其他所有文件缩进 2 个空格 root = true [*] end_of_line = lf indent_size = 2 indent_style = space max_line_length = 120 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.{bat, cmd}] end_of_line = crlf [*.{java, gradle, groovy, kt, sh}] indent_size = 4 [*.md] max_line_length = 0 trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ * text=auto eol=lf # plan text *.txt text *.java text *.scala text *.groovy text *.gradle text *.xml text *.xsd text *.tld text *.yaml text *.yml text *.wsdd text *.wsdl text *.jsp text *.jspf text *.js text *.jsx text *.json text *.css text *.less text *.sql text *.properties text # unix style *.sh text eol=lf # win style *.bat text eol=crlf # don't handle *.der -text *.jks -text *.pfx -text *.map -text *.patch -text *.dat -text *.data -text *.db -text # binary *.jar binary *.war binary *.zip binary *.tar binary *.tar.gz binary *.gz binary *.apk binary *.bin binary *.exe binary # images *.png binary *.jpg binary *.ico binary *.gif binary # medias *.mp3 binary *.swf binary # fonts *.eot binary *.svg binary *.ttf binary *.woff binary # others *.pdf binary *.doc binary *.docx binary *.ppt binary *.pptx binary *.xls binary *.xlsx binary *.xmind binary ================================================ FILE: .gitignore ================================================ # --------------------------------------------------------------------- # more gitignore templates see https://github.com/github/gitignore # --------------------------------------------------------------------- # ------------------------------- java ------------------------------- # compiled folders classes target logs .mtj.tmp/ # compiled files *.class # bluej files *.ctxt # package files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar # virtual machine crash logs hs_err_pid* # maven plugin temp files .flattened-pom.xml package-lock.json pnpm-lock.yaml # ------------------------------- javascript ------------------------------- # dependencies node_modules # temp folders .temp dist _book _jsdoc # temp files *.log npm-debug.log* yarn-debug.log* yarn-error.log* bundle*.js book.pdf # ------------------------------- intellij ------------------------------- .idea *.iml # ------------------------------- eclipse ------------------------------- .classpath .project ================================================ FILE: .travis.yml ================================================ # 持续集成 CI # @see https://docs.travis-ci.com/user/tutorial/ language: node_js sudo: required node_js: '16.13.0' branches: only: - master before_install: - export TZ=Asia/Shanghai script: bash ./scripts/deploy.sh notifications: email: recipients: - forbreak@163.com on_success: change on_failure: always ================================================ FILE: LICENSE ================================================ Attribution-ShareAlike 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: README.md ================================================

logo

license build

linux-tutorial

> 📚 **linux-tutorial** 是一个 Linux 教程。 > > - 🔁 项目同步维护:[Github](https://github.com/dunwu/linux-tutorial/) | [Gitee](https://gitee.com/turnon/linux-tutorial/) > - 📖 电子书阅读:[Github Pages](https://dunwu.github.io/linux-tutorial/) | [Gitee Pages](http://turnon.gitee.io/linux-tutorial/) ## 📖 内容 ### Linux 命令 > 学习 Linux 的第一步:当然是从 [Linux 命令](docs/linux/cli/README.md) 入手了。 - [查看 Linux 命令帮助信息](docs/linux/cli/linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - [Linux 文件目录管理](docs/linux/cli/linux-cli-dir.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` - [Linux 文件内容查看命令](docs/linux/cli/linux-cli-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` - [Linux 文件压缩和解压](docs/linux/cli/linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` - [Linux 用户管理](docs/linux/cli/linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` - [Linux 系统管理](docs/linux/cli/linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` - [Linux 网络管理](docs/linux/cli/linux-cli-net.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` - [Linux 硬件管理](docs/linux/cli/linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` - [Linux 软件管理](docs/linux/cli/linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get` ### Linux 运维 > Linux 系统的常见运维工作。 - [网络运维](docs/linux/ops/network-ops.md) - [Samba](docs/linux/ops/samba.md) - [NTP](docs/linux/ops/ntp.md) - [Firewalld](docs/linux/ops/firewalld.md) - [Crontab](docs/linux/ops/crontab.md) - [Systemd](docs/linux/ops/systemd.md) - [Vim](docs/linux/ops/vim.md) - [Iptables](docs/linux/ops/iptables.md) - [oh-my-zsh](docs/linux/ops/zsh.md) ### 软件运维 > 部署在 Linux 系统上的软件运维。 > > 配套安装脚本:⌨ [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - 开发环境 - [JDK 安装](docs/linux/soft/jdk-install.md) - [Maven 安装](docs/linux/soft/maven-install.md) - [Nodejs 安装](docs/linux/soft/nodejs-install.md) - 开发工具 - [Nexus 运维](docs/linux/soft/nexus-ops.md) - [Gitlab 运维](docs/linux/soft/gitlab-ops.md) - [Jenkins 运维](docs/linux/soft/jenkins-ops.md) - [Svn 运维](docs/linux/soft/svn-ops.md) - [YApi 运维](docs/linux/soft/yapi-ops.md) - 中间件服务 - [Elastic 运维](docs/linux/soft/elastic) - [Kafka 运维](docs/linux/soft/kafka-install.md) - [RocketMQ 运维](docs/linux/soft/rocketmq-install.md) - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) - [Nacos 运维](docs/linux/soft/nacos-install.md) - 服务器 - [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚 - [Tomcat 运维](docs/linux/soft/tomcat-install.md) - [数据库](https://github.com/dunwu/db-tutorial) 📚 - [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md) - [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md) ### Docker - [Docker 快速入门](docs/docker/docker-quickstart.md) - [Dockerfile 最佳实践](docs/docker/docker-dockerfile.md) - [Docker Cheat Sheet](docs/docker/docker-cheat-sheet.md) - [Kubernetes 应用指南](docs/docker/kubernetes.md) ### 其他 - [一篇文章让你彻底掌握 Python](https://dunwu.github.io/blog/pages/ef501b/) - [一篇文章让你彻底掌握 Shell](https://dunwu.github.io/blog/pages/ea6ae1/) - [如何优雅的玩转 Git](https://dunwu.github.io/blog/pages/2fc8b1/) ## ⌨ 脚本 ### Shell 脚本大全 **Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell) ### CentOS 运维脚本集合 本人作为一名 Java 后端,苦于经常在 CentOS 环境上开荒虚拟机。为提高效率,写了一套 Shell 脚本,提供如下功能:安装常用 lib 库、命令工具、设置 DNS、NTP、配置国内 yum 源、一键安装常用软件等。 源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux) ## 📚 资料 - **Linux 命令** - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - [Linux 命令大全](https://man.linuxde.net/) - [linux-command](https://github.com/jaywcjlove/linux-command) - **社区网站** - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 - [LabEx 中国](https://labex.io/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 - [鸟哥的 linux 私房菜](http://linux.vbird.org/) - 非常适合 Linux 入门初学者看的教程。 - [Linux 公社](http://www.linuxidc.com/) - Linux 相关的新闻、教程、主题、壁纸都有。 - [Linux Today](http://www.linuxde.net) - Linux 新闻资讯发布,Linux 职业技术学习!。 - **知识相关** - [Linux 思维导图整理](http://www.jianshu.com/p/59f759207862) - [Linux 初学者进阶学习资源整理](http://www.jianshu.com/p/fe2a790b41eb) - [Linux 快速入门(动手实验)](https://labex.io/zh/courses/quick-start-with-linux) - [【译】Linux 概念架构的理解](http://www.jianshu.com/p/c5ae8f061cfe) [En](http://oss.org.cn/ossdocs/linux/kernel/a1/index.html) - [Linux 守护进程的启动方法](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html) - [Linux 知识点小结](https://blog.huachao.me/2016/1/Linux%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B0%8F%E7%BB%93/) - [10 大白帽黑客专用的 Linux 操作系统](https://linux.cn/article-6971-1.html) - **软件工具** - [超赞的 Linux 软件](https://www.gitbook.com/book/alim0x/awesome-linux-software-zh_cn/details) Github 仓库[Zh](https://github.com/alim0x/Awesome-Linux-Software-zh_CN) [En](https://github.com/VoLuong/Awesome-Linux-Software) - [程序员喜欢的 9 款最佳的 Linux 文件比较工具](http://os.51cto.com/art/201607/513796.htm) - [提高 Linux 开发效率的 5 个工具](http://www.codeceo.com/article/5-linux-productivity-tools.html) - [你要了解的 11 款面向 Linux 系统的一流备份实用工具](http://os.51cto.com/art/201603/508027.htm) - [16 个很有用的在线工具](http://www.simlinux.com/archives/264.html) - Adobe 软件的最佳替代品 [原文在这里](https://linux.cn/article-8928-1.html) - [Evince (Adobe Acrobat Reader)](https://wiki.gnome.org/Apps/Evince) 一个“支持多种文档格式的文档查看器”,可以查看 PDF,还支持各种漫画书格式 - [Pixlr (Adobe Photoshop)](https://pixlr.com/) 一个强大的图像编辑工具 - [Inkscape (Adobe Illustrator)](https://inkscape.org/zh/) 一个专业的矢量图形编辑器 - [Pinegrow Web Editor (Adobe Dreamweaver)](https://pinegrow.com/) 一个可视化编辑制作 HTML 网站 - [Scribus (Adobe InDesign)](https://www.scribus.net/) 一个开源电子杂志制作软件 - [Webflow (Adobe Muse)](https://webflow.com/) 一款可以帮助用户不用编码就可以快速创建网站的谷歌浏览器插件。 - [Tupi (Adobe Animate)](http://www.maefloresta.com/portal/) 一款可以创建 HTML5 动画的工具。 - [Black Magic Fusion (Adobe After Effects)](https://www.blackmagicdesign.com) 一款先进的合成软件,广泛应用于视觉特效、广电影视设计以及 3D 动画设计等领域。 - **中国开源镜像** - [阿里云开源镜像站](http://mirrors.aliyun.com/) - [网易开源镜像站](http://mirrors.163.com/) - [搜狐开源镜像站](http://mirrors.sohu.com/) - [北京交通大学](http://mirror.bjtu.edu.cn/) - [兰州大学](http://mirror.lzu.edu.cn/) - [厦门大学](http://mirrors.xmu.edu.cn/) - [上海交通大学](http://ftp.sjtu.edu.cn/) - [清华大学](http://mirrors.tuna.tsinghua.edu.cn/) - [中国科学技术大学](http://mirrors.ustc.edu.cn/) - [东北大学](http://mirror.neu.edu.cn/) - [浙江大学](http://mirrors.zju.edu.cn/) - [东软信息学院](http://mirrors.neusoft.edu.cn/) - [校园网联合镜像站](https://mirrors.cernet.edu.cn) ## 🚪 传送门 ◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ ================================================ FILE: codes/docker/docker-compose-demo/Dockerfile ================================================ FROM python:3.6-alpine ADD . /code WORKDIR /code RUN pip install redis flask CMD ["python", "app.py"] ================================================ FILE: codes/docker/docker-compose-demo/app.py ================================================ from flask import Flask from redis import Redis app = Flask(__name__) redis = Redis(host='redis', port=6379) @app.route('/') def hello(): count = redis.incr('hits') return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) if __name__ == "__main__": app.run(host="0.0.0.0", debug=True) ================================================ FILE: codes/docker/docker-compose-demo/docker-compose.yml ================================================ version: '3' services: web: build: . ports: - "5000:5000" redis: image: "redis:alpine" ================================================ FILE: codes/docker/docker-compose-demo/run.sh ================================================ #!/usr/bin/env bash set -x docker-compose up ================================================ FILE: codes/expect/远程登录.sh ================================================ #!/usr/bin/expect -f # ----------------------------------------------------------------------------------------------------- # expect 交互式脚本示例 - 自动远程登录,并在其他机器上创建一个文件 # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # 设置变量 set USER "root" set PWD "XXXXXX" set HOST "127.0.0.2" # 设置超时时间 set timeout 400 # 远程登录 spawn ssh -p 22 $USER@$HOST expect { "yes/no" { send "yes\r"; exp_continue } "password:" { send "$PWD\r" } } # 在其他机器上创建 expect "#" send "touch /home/demo.txt\r" expect "#" send "echo hello world >> /home/demo.txt\r" expect "#" # 退出 send "exit\r" ================================================ FILE: codes/linux/README.md ================================================ # CentOS 常规操作运维脚本集合 > **本项目脚本代码用于在 [CentOS](https://www.centos.org/) 机器上安装常用命令工具或开发软件。** 使用说明: (1)下载脚本 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/download.sh | bash ``` (2)执行脚本 ```shell cd /tmp/dunwu-ops ./dunwu-ops.sh ``` (3)清除脚本 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/clear.sh | bash ``` 本项目总结、收集 Linux 环境下运维常用到的脚本工具,大致分为三类: - [系统运维脚本](sys) - [服务、应用运维脚本](soft) - [构建、编译项目脚本(目前支持 Java、Javascript 应用)](build) ================================================ FILE: codes/linux/build/Java应用运行脚本模板/lifecycle.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # 应用终止脚本 # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ libs SCRIPTS_DIR=$(cd `dirname $0`; pwd) if [[ ! -x ${SCRIPTS_DIR}/utils.sh ]]; then logError "${SCRIPTS_DIR}/utils.sh not exists!" exit 1 fi source ${SCRIPTS_DIR}/utils.sh # ------------------------------------------------------------------------------ functions stopServer() { if [[ ! $1 ]]; then logError "please input java app name" return ${ENV_FAILED} fi local appName=$1 local pid=`jps | grep ${appName} | awk '{print $1}'` if [[ -n "${pid}" ]]; then kill -9 ${pid} if [[ $? -eq ${ENV_SUCCEED} ]]; then printInfo "stop ${appName} succeed" return ${ENV_SUCCEED} else logError "stop ${appName} failed" return ${ENV_FAILED} fi else printWarn "${appName} is not running" return ${ENV_SUCCEED} fi } startServer() { # >>>> validate params if [[ ! $1 ]] || [[ ! $2 ]] || [[ ! $3 ]] || [[ ! $4 ]]; then logError "you must input following params in order:" echo -e "${ENV_COLOR_B_RED}" echo " (1)jarPath" echo " (2)libPath" echo " (3)confPath" echo " (4)logPath" echo " (5)appName [optional]" echo " (6)port [optional]" echo " (7)profile [optional]" echo " (8)debug [optional]" echo -e "\nEg. startServer /usr/lib/dunwu/app.jar /usr/lib/dunwu/lib /usr/lib/dunwu/conf /var/log/dunwu dunwu 8888 prod off" echo -e "${ENV_COLOR_RESET}" return ${ENV_FAILED} fi local jarPath=$1 local libPath=$2 local confPath=$3 local logPath=$4 local appName=${5:-myapp} local port=${6:-8888} local profile=${7:-prod} local debug=${8:-off} # >>>> 1. check java app is started or not # >>>> 1.1. exit script if the app is started local pid=`jps | grep ${appName} | awk '{print $1}'` if [[ -n "${pid}" ]]; then printInfo "${appName} is started, PID: ${pid}" return ${ENV_SUCCEED} fi # >>>> 2. package options # GC OPTS local javaOptions="-server -Xms1g -Xmx2g -Xss256k" javaOptions="${javaOptions} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=4" # GC LOG OPTS javaOptions="${javaOptions} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" javaOptions="${javaOptions} -verbose:gc -Xloggc:${logPath}/${appName}.gc.log" javaOptions="${javaOptions} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" # Heap Dump OPTS javaOptions="${javaOptions} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" javaOptions="${javaOptions} -XX:HeapDumpPath=${logPath}/${appName}.heapdump.hprof" # APP OPTS javaOptions="${javaOptions} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8" if [[ ${profile} ]]; then javaOptions="${javaOptions} -Dspring.profiles.active=${profile}" fi # DEBUG OPTS if [[ "${debug}" == "on" ]]; then # JMX OPTS local ip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/') local jmxPort=$(expr 10000 + ${port}) javaOptions="${javaOptions} -Dcom.sun.management.jmxremote=true" javaOptions="${javaOptions} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" javaOptions="${javaOptions} -Djava.rmi.server.hostname=${ip} -Dcom.sun.management.jmxremote.port=${jmxPort}" # Remote Debug local debugPort=$(expr 20000 + ${port}) javaOptions="${javaOptions} -Xdebug -Xnoagent -Djava.compiler=NONE" javaOptions="${javaOptions} -Xrunjdwp:transport=dt_socket,address=${debugPort},server=y,suspend=n" fi # CLASSPATH local appOptions="-classpath ${libPath}/* -Dlogging.config=file:${confPath}/logback.${profile}.xml" local springConfigFiles="classpath:/,classpath:/config/" local springConfigFiles="${springConfigFiles},file:${confPath}/,file:${confPath}/application.properties" appOptions="${appOptions} --spring.config.location=${springConfigFiles}" appOptions="${appOptions} --spring.cache.ehcache.config=file:${confPath}/config/ehcache.xml" if [[ ${port} ]]; then appOptions="${appOptions} --server.port=${port}" fi # >>>> 3. create log dir and console log file local consoleLogPath=${logPath}/${appName}.console.log mkdir -p ${logPath} if [[ ! -x ${consoleLogPath} ]]; then touch ${consoleLogPath} fi # >>>> 4. start java app # print bootstrap info printInfo "starting ${appName}" echo -e "${ENV_COLOR_B_GREEN}" echo -e "${ENV_COLOR_B_CYAN}\nBOOT PARAMS:${ENV_COLOR_B_GREEN}\n\n" echo "appName=${appName}" echo "jarPath=${jarPath}" echo "libPath=${libPath}" echo "confPath=${confPath}" echo "logPath=${logPath}" echo "port=${port}" echo "profile=${profile}" echo "debug=${debug}" echo -e "${ENV_COLOR_B_CYAN}\nEXEC CLI:${ENV_COLOR_B_GREEN}\n\n" echo "nohup java ${javaOptions} -jar ${jarPath} ${appOptions} >> ${consoleLogPath} 2>&1 &" echo -e "${ENV_COLOR_RESET}" # exec boot cli nohup java ${javaOptions} -jar ${jarPath} ${appOptions} >> ${consoleLogPath} 2>&1 & # >>>> 5. check java app is started or not local pid=`jps | grep ${appName} | awk '{print $1}'` if [[ -n "${pid}" ]]; then printInfo "start ${appName} succeed, PID: ${pid}" return ${ENV_SUCCEED} else logError "start ${appName} failed" return ${ENV_FAILED} fi } ================================================ FILE: codes/linux/build/Java应用运行脚本模板/start.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # myapp 启动脚本,用于【虚拟机环境】 # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ libs SCRIPTS_DIR=$(dirname ${BASH_SOURCE[0]}) if [[ ! -x ${SCRIPTS_DIR}/lifecycle.sh ]]; then logError "${SCRIPTS_DIR}/lifecycle.sh not exists!" exit 1 fi source ${SCRIPTS_DIR}/lifecycle.sh # ------------------------------------------------------------------------------ main APP_DIR=$(cd `dirname $0`/..; pwd) export LANG="zh_CN.UTF-8" APP=myapp JAR_PATH=${APP_DIR}/myapp.jar LIB_PATH=${APP_DIR}/lib CONF_PATH=${APP_DIR}/config LOG_DIR=/var/log/dunwu PORT=8888 PROFILE=prod DEBUG=off declare -a serial serial=(on off) echo -n "是否启动 debug 模式(可选值:on|off):" read DEBUG if ! echo ${serial[@]} | grep -q ${DEBUG}; then echo "是否启动 debug 模式(可选值:on|off)" exit 1 fi startServer ${JAR_PATH} ${LIB_PATH} ${CONF_PATH} ${LOG_DIR} ${APP} ${PORT} ${PROFILE} ${DEBUG} ================================================ FILE: codes/linux/build/Java应用运行脚本模板/stop.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # 应用启动脚本 # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ libs SCRIPTS_DIR=$(dirname ${BASH_SOURCE[0]}) if [[ ! -x ${SCRIPTS_DIR}/lifecycle.sh ]]; then logError "${SCRIPTS_DIR}/lifecycle.sh not exists!" exit 1 fi source ${SCRIPTS_DIR}/lifecycle.sh # ------------------------------------------------------------------------------ main export LANG="zh_CN.UTF-8" stopServer myapp ================================================ FILE: codes/linux/build/Java应用运行脚本模板/utils.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Shell Utils # 使用此脚本,应该先 export ENV_LOG_PATH,指定日志路径;否则将使用默认日志路径 /var/log/shell.log # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env params # 颜色状态 # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color export ENV_COLOR_U_BLACK="\033[4;30m" export ENV_COLOR_U_RED="\033[4;31m" export ENV_COLOR_U_GREEN="\033[4;32m" export ENV_COLOR_U_YELLOW="\033[4;33m" export ENV_COLOR_U_BLUE="\033[4;34m" export ENV_COLOR_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_CYAN="\033[4;36m" export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color export ENV_COLOR_BG_BLACK="\033[40m" export ENV_COLOR_BG_RED="\033[41m" export ENV_COLOR_BG_GREEN="\033[42m" export ENV_COLOR_BG_YELLOW="\033[43m" export ENV_COLOR_BG_BLUE="\033[44m" export ENV_COLOR_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_CYAN="\033[46m" export ENV_COLOR_BG_WHITE="\033[47m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # 常用状态值 export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) # 日志路径 LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} # 日志目录 LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { if [[ ! -x "${LOG_PATH}" ]]; then mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then logInfo "$@" return ${ENV_SUCCEED} else logError "$@ EXECUTE ENV_FAILED" return ${ENV_FAILED} fi } ================================================ FILE: codes/linux/build/README.md ================================================ # 构建、编译项目脚本 > 目前支持 Java、Javascript 应用 ================================================ FILE: codes/linux/build/helper.sh ================================================ #!/usr/bin/env bash # 打印UI页头信息 function printHeadInfo() { cat << EOF *********************************************************************************** * 欢迎使用项目引导式发布脚本。 * 输入任意键进入脚本操作。 *********************************************************************************** EOF } # 打印UI页尾信息 function printFootInfo() { cat << EOF *********************************************************************************** * 安装过程结束。 * 输入任意键进入脚本操作。 * 如果不想安装其他应用,输入 exit 回车或输入 退出。 *********************************************************************************** EOF } # 检查文件是否存在,不存在则退出脚本 function checkFileExist() { if [ ! -f "$1" ] then echo "关键文件 $1 找不到,脚本执行结束" exit 1 fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { if [ ! -d "$1" ]; then mkdir -p "$1" fi } # 记录发布的版本信息 # 第一个参数为日志所在路径 # 第二个参数为应用名称 # 第三个参数为代码分支 # 第四个参数为运行环境 function saveVersionInfo() { if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then echo "缺少参数,退出" exit 1 fi VERSION_LOG_FILE=$1/$2-version.log rm -rf ${VERSION_LOG_FILE} touch ${VERSION_LOG_FILE} chmod 777 ${VERSION_LOG_FILE} echo -e "\n=================== $2 ===================" >> ${VERSION_LOG_FILE} echo "Branch is: $3" >> ${VERSION_LOG_FILE} echo "Profile is: $4" >> ${VERSION_LOG_FILE} echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} } ================================================ FILE: codes/linux/build/java-app-boot.sh ================================================ #!/usr/bin/env bash ################################################################################# # JAVA 应用通用启动脚本 # @author: Zhang Peng ################################################################################# # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { if [ "${app}" == "" ] || [ "${oper}" == "" ] || [ "${javaArgs}" == "" ] || [ "${classpathArgs}" == "" ] || [ "${bootstrapClass}" == "" ]; then echo "请输入脚本参数:app oper javaArgs classpathArgs bootstrapClass" echo " app: 应用名。" echo " oper: 运行环境(必填)。可选值:start|stop|restart" echo " javaArgs: JVM 参数(必填)。" echo " classpathArgs: classpath参数(必填)。" echo " bootstrapClass: 启动类(必填)。" exit 0 fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { if [ ! -d "$1" ]; then mkdir -p "$1" fi } # 检查服务是否已经启动 pids="" function checkStarted() { pids=`ps -ef | grep java | grep ${app} | awk '{print $2}'` if [ -n "${pids}" ]; then return 0 else return 1 fi } function main() { case "${oper}" in start) echo -n "starting server: " # 检查服务是否已经启动 if checkStarted; then echo "ERROR: server already started!" echo "PID: ${pids}" exit 1 fi args="${javaArgs} -classpath ${classpathArgs} ${bootstrapClass}" echo -e "statup params:\n ${args}" #启动服务 touch ${LOG_DIR}/${app}-startup.log nohup java ${args} > ${LOG_DIR}/${app}-startup.log 2>&1 & # echo -e "执行参数:\n${args}" echo -e "\nthe server is started..." ;; stop) echo -n "stopping server: " #dubbo提供优雅停机, 不能使用kill -9 if checkStarted; then kill ${pids} echo -e "\nthe server is stopped..." else echo -e "\nno server to be stopped..." fi ;; restart) $0 ${app} stop "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" sleep 5 $0 ${app} start "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" ;; *) echo "Invalid oper: ${oper}." exit 1 esac exit 0 } ######################################## MAIN ######################################## # 设置环境变量 export LANG="zh_CN.UTF-8" # 获取输入参数 app=`echo $1` oper=`echo $2` javaArgs=`echo $3` classpathArgs=`echo $4` bootstrapClass=`echo $5` vars=$* checkInput # 设置全局常量 LOG_DIR=/home/zp/log createFolderIfNotExist ${LOG_DIR} # 执行 main 方法 main ================================================ FILE: codes/linux/build/java-app-release.sh ================================================ #!/usr/bin/env bash ################################################################################# # JAVA 应用发布脚本 # @author: Zhang Peng ################################################################################# # 检查脚本参数,如必要参数未传入,退出脚本。 checkInput() { if [ "${branch}" == "" ] || [ "${profile}" == "" ]; then echo "请输入脚本参数:branch profile" echo " branch: git分支(必填)。如 feature/1.1.16, master" echo " profile: 运行环境(必填)。可选值:development | test" echo "例:./java-app-release.sh feature/1.1.16 test" exit 0 fi } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { if [ ! -f "$1" ] then echo "关键文件 $1 找不到,脚本执行结束" exit 0 fi } # 检查文件夹是否存在,不存在则创建 createFolderIfNotExist() { if [ ! -d "$1" ]; then mkdir -p "$1" fi } # 记录发布的版本信息 saveVersionInfo() { rm -rf ${VERSION_LOG_FILE} touch ${VERSION_LOG_FILE} chmod 777 ${VERSION_LOG_FILE} echo -e "\n=================== Version Info ===================" >> ${VERSION_LOG_FILE} echo "Branch is: ${branch}" >> ${VERSION_LOG_FILE} echo "Profile is: ${profile}" >> ${VERSION_LOG_FILE} echo "CommitID is : $(git log --pretty=oneline -1)" >> ${VERSION_LOG_FILE} } ######################################## MAIN ######################################## # 设置环境变量 export LANG="zh_CN.UTF-8" # 设置全局常量 LOG_DIR=/home/zp/log/ SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) SOURCE_DIR=/home/zp/source/ APP_NAME=XXX RESOURCES_DIR=/home/zp/source/${APP_NAME}/src/main/resources UPDATE_CODE_SCRIPT_FILE=${SCRIPT_DIR}/update-code.sh MAVEN_LOG_FILE=${LOG_DIR}/${APP_NAME}-maven.log VERSION_LOG_FILE=${LOG_DIR/${APP_NAME}-version.log # 0. 获取传入参数并检查 branch=`echo $1` profile=`echo $2` checkInput checkFileExist ${UPDATE_CODE_SCRIPT_FILE} createFolderIfNotExist ${LOG_DIR} createFolderIfNotExist ${SOURCE_DIR} echo ">>>>>>>>>>>>>> 1. 停止应用" ${SCRIPT_DIR}/java-app-run.sh ${profile} stop echo ">>>>>>>>>>>>>> 2. 更新代码" ${UPDATE_CODE_SCRIPT_FILE} ${APP_NAME} ${branch} ${SOURCE_DIR} execode=$? if [ "${execode}" == "0" ]; then echo "更新代码成功" else echo "更新代码失败" exit 1 fi echo ">>>>>>>>>>>>>> 3. 替换配置" # 有则加,无则过 echo ">>>>>>>>>>>>>> 4. 构建编译" cd ${SOURCE_DIR}/ck-lion mvn clean package -e -Dmaven.test.skip=true | tee ${MAVEN_LOG_FILE} eexecode=$? if [ "${execode}" == "0" ]; then echo "构建编译成功" echo "编译详情见:${MAVEN_LOG_FILE}" else echo "构建编译失败" echo "编译详情见:${MAVEN_LOG_FILE}" exit 1 fi echo ">>>>>>>>>>>>>> 5. 启动应用" # 手动释放内存 echo 3 > /proc/sys/vm/drop_caches ${SCRIPT_DIR}/java-app-run.sh ${profile} start execode=$? if [ "${execode}" == "0" ]; then echo "启动应用成功" else echo "启动应用失败" exit 1 fi echo ">>>>>>>>>>>>>> 6. 记录发布的版本信息" saveVersionInfo echo ">>>>>>>>>>>>>> 发布应用结束" ================================================ FILE: codes/linux/build/java-app-run.sh ================================================ #!/usr/bin/env bash ################################################################################# # JAVA 应用运行脚本 # @author: Zhang Peng ################################################################################# # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { if [ "${profile}" == "" ] || [ "${oper}" == "" ]; then echo "请输入脚本参数:profile oper [debug]" echo " profile: 运行环境(必填)。可选值:development|test" echo " oper: 运行环境(必填)。可选值:start|stop|restart" echo " debug: debug启动开关。默认不填为不启动。" exit 0 fi } #检查文件是否存在,不存在则退出脚本 function checkFileExist() { if [ ! -f "$1" ] then echo "关键文件 $1 找不到,脚本执行结束" exit 0 fi } # 封装启动参数,调用启动脚本 function main() { APP_NAME=ck-lion # JVM 参数 JAVA_OPTS=" -Djava.awt.headless=true -Dfile.encoding=UTF8 -Djava.net.preferIPv4Stack=true -Ddubbo.shutdown.hook=true -Dspring.profiles.active=${profile} -Djava.security.egd=file:/dev/./urandom -Xms1024m -Xmx1024m -Xss2m " JAVA_DEBUG_OPTS="" if [ "$2" == "debug" ]; then JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=2236,server=y,suspend=n " shift fi javaArgs=" ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} " # classpath 参数 classpathArgs="${SERVER_ROOT}/WEB-INF/classes:${SERVER_ROOT}/WEB-INF/lib/*" # 启动类 bootstrapClass="com.alibaba.dubbo.container.Main" ${SCRIPT_DIR}/java-app-boot.sh ${APP_NAME} ${oper} "${javaArgs}" "${classpathArgs}" "${bootstrapClass}" execode=$? if [ "${execode}" == "0" ]; then echo "执行操作成功" else echo "执行操作失败" exit 1 fi } ######################################## MAIN ######################################## # 设置环境变量 export LANG="zh_CN.UTF-8" # 设置全局常量 SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) SOURCE_DIR=/home/zp/source/ APP_NAME=XXX SERVER_ROOT=/home/zp/source/${APP_NAME}/target/ # 0. 获取传入参数并检查 profile=$1 oper=$2 debug=$3 checkInput # 1. 执行操作 main ================================================ FILE: codes/linux/build/js-app-release.sh ================================================ ################################################################################# # 前端应用发布脚本 # 环境要求:Nvm、Node.js ################################################################################# # 检查脚本参数,如必要参数未传入,退出脚本。 function checkInput() { if [ "${branch}" == "" ]; then echo "请输入脚本参数:branch" echo " branch: git分支。如 feature/1.1.16, master" exit 1 fi } # 脚本主方法 function main() { echo ">>>>>>>>>>>>>> 1. 更新代码" ${SCRIPT_DIR}/update-code.sh ${APP} ${branch} ${SOURCE_DIR} execode=$? if [ "${execode}" == "0" ]; then echo "更新代码成功" else echo "更新代码失败" exit 1 fi echo ">>>>>>>>>>>>>> 2. 替换配置" # 有的应用此处可能需要替换配置 echo ">>>>>>>>>>>>>> 3. 构建编译" cd ${SOURCE_DIR}/${APP} source "${HOME}/.nvm/nvm.sh" nvm use 8.9 npm install if [ "${profile}" == "develop" ] || [ "${profile}" == "test" ]; then npm start elif [ "${profile}" == "preview" ] || [ "${profile}" == "product" ]; then npm run build fi execode=$? if [ "${execode}" == "0" ]; then echo "构建编译成功" else echo "构建编译失败" exit 1 fi echo ">>>>>>>>>>>>>> 4. 记录发布的版本信息" saveVersionInfo ${LOG_DIR} ${APP} ${branch} ${profile} echo ">>>>>>>>>>>>>> 发布应用结束" } ######################################## MAIN ######################################## # 设置环境变量 export LANG="zh_CN.UTF-8" # 设置全局常量 APP=blog LOG_DIR=/home/zp/log SOURCE_DIR=/home/zp/source SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) # 装载函数库 . ${SCRIPT_DIR}/helper.sh # 获取传入参数并检查 branch=`echo $1` profile=`echo $2` checkInput checkFileExist ${SCRIPT_DIR}/update-code.sh createFolderIfNotExist ${SOURCE_DIR} # 运行主方法 main ================================================ FILE: codes/linux/build/main.sh ================================================ #!/usr/bin/env bash ################################################################################### # 项目发布脚本模板 # @author: Zhang Peng ################################################################################### # 选择应用 function chooseAppName() { cat << EOF 请选择应用名(数字或关键字均可)。 可选值如下: [0] all (所有应用) [1] js-app [2] APP2 EOF while read app do case ${app} in 0) app=all break ;; 1) app=js-app break ;; 2) app=APP2 break ;; all | js-app | APP2) break ;; *) echo "无法识别 ${app}" ;; esac done } # 选择操作 function chooseOper() { cat << EOF 请选择想要执行的操作(数字或关键字均可)。 可选值如下: [1] start [2] restart [3] stop EOF while read oper do case ${oper} in 1) oper=start break ;; 2) oper=restart break ;; 3) oper=stop break ;; start | restart | stop) break ;; *) echo "无法识别 ${oper}" ;; esac done } # 选择代码分支 function chooseBranch() { cat << EOF 请输入 git 分支。 如:develop、master、feature/xxx EOF read branch if [[ "${branch}" =~ ^ ( feature/ ) ( [^ \f\n\r\t\v]+ ) ]] || [ "${branch}" == "develop" ] || [ "${branch}" == "master" ]; then echo "输入了 ${branch}" else echo "无法识别 ${branch}" chooseBranch fi } # 选择运行环境 function chooseProfile() { cat << EOF 请选择运行环境(数字或关键字均可)。 可选值: [1] develop (开发环境) [2] test (测试环境) [3] preview (预发布环境) [4] product (生产环境) EOF while read profile do case ${profile} in 1) profile=develop break ;; 2) profile=test break ;; 3) profile=preview break ;; 4) profile=product break ;; develop | test | preview | product) break ;; *) echo "无法识别 ${profile}" ;; esac done } # 确认选择 function confirmChoice() { cat << EOF =================================================== 请确认您的选择:Y/N app: ${app} oper: ${oper} branch: ${branch} profile: ${profile} =================================================== EOF while read confirm do case ${confirm} in y | Y) echo -e "\n\n>>>>>>>>>>>>>> 开始发布应用" break ;; n | N) echo -e "重新输入发布参数\n" inputParams ;; *) echo "无法识别 ${confirm}" ;; esac done } # 引导式发布应用 function releaseApp() { # 输入执行参数 app="" branch="" profile="" chooseAppName chooseOper if [ "${oper}" == "stop" ]; then confirmChoice if [ "${app}" == "all" ]; then ${SCRIPT_DIR}/${app}-run.sh stop ${profile} else ${SCRIPT_DIR}/${app}-run.sh stop ${profile} fi else chooseBranch chooseProfile confirmChoice if [ "${app}" == "all" ]; then ${SCRIPT_DIR}/js-app-release.sh ${branch} ${profile} else ${SCRIPT_DIR}/${app}-release.sh ${branch} ${profile} fi fi } # 脚本主方法 function main() { printHeadInfo while read sign do case ${sign} in exit) echo "主动退出脚本" exit 0 ;; *) releaseApp ;; esac # 装载函数库 printFootInfo done } ######################################## MAIN ######################################## # 设置环境变量 export LANG="zh_CN.UTF-8" # 设置全局常量 SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) SOURCE_DIR=/home/zp/source/ # 装载函数库 . ${SCRIPT_DIR}/helper.sh # 检查必要文件或文件夹是否存在 checkFileExist ${SCRIPT_DIR}/helper.sh createFolderIfNotExist ${SOURCE_DIR} # 运行主方法 main ================================================ FILE: codes/linux/build/spring-boot-run.sh ================================================ #!/usr/bin/env bash ############################################################################## # console color BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ############################################################################## JAVA_OPTS="" APP_OPTS="" packageJavaOpts() { # GC OPTS JAVA_OPTS="${JAVA_OPTS} -server -Xms8g -Xmx16g -Xss512k" JAVA_OPTS="${JAVA_OPTS} -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=150" JAVA_OPTS="${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom" # DEBUG OPTS if [[ ${debug} == "on" ]]; then # Remote Debug JAVA_OPTS="${JAVA_OPTS} -Xdebug -Xnoagent -Djava.compiler=NONE" JAVA_OPTS="${JAVA_OPTS} -Xrunjdwp:transport=dt_socket,address=28889,server=y,suspend=n" # GC LOG JAVA_OPTS="${JAVA_OPTS} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" JAVA_OPTS="${JAVA_OPTS} -verbose:gc -Xloggc:${LOG_PATH}/${APP_NAME}.gc.log" JAVA_OPTS="${JAVA_OPTS} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" # Heap Dump JAVA_OPTS="${JAVA_OPTS} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" JAVA_OPTS="${JAVA_OPTS} -XX:HeapDumpPath=${LOG_PATH}/${APP_NAME}.heapdump.hprof" # JMX OPTS IP=`ip addr | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1` JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote=true" JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" JAVA_OPTS="${JAVA_OPTS} -Djava.rmi.server.hostname=${IP} -Dcom.sun.management.jmxremote.port=18889" fi # APP OPTS JAVA_OPTS="${JAVA_OPTS} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true" JAVA_OPTS="${JAVA_OPTS} -Dspring.profiles.active=${profile} -Dfile.encoding=UTF-8" # CLASSPATH APP_OPTS=" -classpath lib/* -Dlogging.config=file:./config/logback.dev.xml --spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/" } # 检查服务是否已经启动 pid="" checkStarted() { pid=`ps -ef | grep java | grep ${APP_NAME} | awk '{print $2}'` if [[ -n "${pid}" ]]; then return 0 else return 1 fi } main() { case "${oper}" in start) startServer ;; stop) stopServer ;; restart) stopServer sleep 5 startServer ;; *) echo "Invalid oper: ${oper}." exit 1 esac exit 0 } stopServer() { echo -n "stopping server: " if checkStarted; then kill -9 ${pid} printf "${GREEN}\n${APP_NAME} is stopped.${RESET}\n" else printf "${RED}\n${APP_NAME} fail to stop.${RESET}\n" fi } startServer() { printf "${BLUE}starting ${APP_NAME}...${RESET}\n" if checkStarted; then printf "${YELLOW}[WARN] ${APP_NAME} already started!${RESET}\n" printf "PID: ${pid}\n" exit 1 fi packageJavaOpts printf "${CYAN}JVM OPTS:\n ${JAVA_OPTS}${RESET}\n" if [[ ! -f "${LOG_PATH}/start.out" ]]; then touch "${LOG_PATH}/start.out" fi nohup java ${JAVA_OPTS} -jar ${ROOT_DIR}/../spring-boot-app.jar ${APP_OPTS} >> ${LOG_PATH}/start.out 2>&1 & printf "${GREEN}\n${APP_NAME} is started.${RESET}\n" } ######################################## MAIN ######################################## # 设置环境变量 export LANG="zh_CN.UTF-8" ROOT_DIR=$(pwd) APP_NAME=spring-boot-app LOG_PATH=${ROOT_DIR}/../logs mkdir -p ${LOG_PATH} declare -a serial serial=( start stop restart ) echo -n "请选择操作(可选值:start|stop|restart):" read oper if ! echo "${serial[@]}" | grep -q ${oper}; then echo "请选择正确操作(可选值:start|stop|restart)" exit 1 fi if [[ ${oper} == "start" ]] || [[ "${oper}" == "restart" ]]; then declare -a serial2 serial2=( prod dev test ) echo -n "选择 profile(可选值:prod|dev|test):" read profile if ! echo "${serial2[@]}" | grep -q ${profile}; then echo "请选择正确 profile(可选值:prod|dev|test)" exit 1 fi declare -a serial3 serial3=( on off ) echo -n "是否启动 debug 模式(可选值:on|off):" read debug if ! echo "${serial3[@]}" | grep -q ${debug}; then echo "是否启动 debug 模式(可选值:on|off)" exit 1 fi fi main ================================================ FILE: codes/linux/build/update-code.sh ================================================ #!/usr/bin/env bash ################################################################################### # 更新代码脚本 # 只有退出码(exit code)为 0 ,表示脚本执行成功。 # @author: Zhang Peng ################################################################################### # 检查脚本参数,如必要参数未传入,退出脚本。 checkInput() { if [ "${repository}" == "" ] || [ "${branch}" == "" ]; then echo "请输入脚本参数:repository branch [source] [target]" echo " repository: git 仓储(必填)。" echo " branch: git 分支(必填)。如 master/develop" echo " source: 代码存放目录。默认为/home/zp/source。" echo " target: 代码存放目录。默认为脚本所在目录。" exit 1 fi } # 检查文件夹是否存在,不存在则创建 function createFolderIfNotExist() { if [ ! -d "$1" ]; then mkdir -p "$1" fi } # 判断 git 版本库是否存在。根据实际结果修改 ${gitok} 值。 gitok=false function isGitExist() { cd ${SOURCE_DIR} if [ -d "${SOURCE_DIR}/${repository}/${target}" ]; then cd ${SOURCE_DIR}/${repository}/${target} #(1)删除git状态零时文件 if [ -f "gitstatus.tmp" ]; then rm -rf gitstatus.tmp fi #(2) 判断是否存在.git目录 if [ -d "./.git" ]; then #(3) 判断git是否可用 git status &> gitstatus.tmp grep -iwq 'not a git repository' gitstatus.tmp && gitok=false || gitok=true fi #返回到主目录 cd ${SOURCE_DIR} fi } # 如果 git 版本库存在(根据 ${gitok} 值),执行 fetch 操作;反之,执行 clone 操作。 function doFetchOrClone() { if ${gitok}; then cd ${SOURCE_DIR}/${repository}/${target} git reset --hard git clean -ffdx git fetch echo "git fetch ${repository} remote repository 到本地成功" else #删除所有内容,便于重新进行git clone rm -rf ${repository} git clone --no-checkout git@github.com:${GITHUB_ACCOUNT}/${repository}.git ${SOURCE_DIR}/${repository}/${target} echo "git clone ${repository} remote repository 到本地成功" cd ${SOURCE_DIR}/${repository}/${target} fi } # 切换到 ${branch} 分支 function doCheckout() { echo "检出 ${repository} ${branch} 分支代码" isRemoteBranch=false gitRemoteBranch=`git branch -r` echo -e "$gitRemoteBranch" | grep -iwq ${branch} && isRemoteBranch=true || isRemoteBranch=false if ${isRemoteBranch}; then echo "找到 ${branch} 分支。" git checkout -f 'origin/'${branch} else echo "未找到 ${branch} 分支!" exit 2 fi echo "更新子模块代码" git submodule update --init --recursive --force } ######################################## MAIN ######################################## export LANG="zh_CN.UTF-8" # 0. 检查传入的参数 repository=`echo $1` branch=`echo $2` source=`echo $3` target=`echo $4` checkInput # 设置全局常量 GITHUB_ACCOUNT=dunwu SOURCE_DIR=/home/xyz/source if [ "${source}" != "" ]; then SOURCE_DIR=${source} fi createFolderIfNotExist ${SOURCE_DIR} # 1. 判断本地是否已存在 Git 仓库 isGitExist # 2. 如果本地已有代码,执行 fetch;反之,从远程 clone doFetchOrClone # 3. 切换到指定分支 doCheckout # 4. 分配权限 chmod -R 777 ${SOURCE_DIR} exit 0 ================================================ FILE: codes/linux/conf/etc/sysctl.conf ================================================ ################################################################################### # /etc/sysctl.conf 文件用于配置 Linux 内核及网络 # @author: Zhang Peng ################################################################################### # 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1 # 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_tw_recycle = 1 # 如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。 net.ipv4.tcp_fin_timeout = 2 # 避免放大攻击 net.ipv4.icmp_echo_ignore_broadcasts = 1 # 开启恶意icmp错误消息保护 net.ipv4.icmp_ignore_bogus_error_responses = 1 # 开启SYN洪水攻击保护 net.ipv4.tcp_syncookies = 1 # 开启并记录欺骗,源路由和重定向包 net.ipv4.conf.all.log_martians = 1 net.ipv4.conf.default.log_martians = 1 # 处理无源路由的包 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 # 开启反向路径过滤 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # 确保无人能修改路由表 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.all.secure_redirects = 0 net.ipv4.conf.default.secure_redirects = 0 # 不充当路由器 net.ipv4.ip_forward = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 # 开启execshild kernel.exec-shield = 1 kernel.randomize_va_space = 1 # IPv6设置 net.ipv6.conf.default.router_solicitations = 0 net.ipv6.conf.default.accept_ra_rtr_pref = 0 net.ipv6.conf.default.accept_ra_pinfo = 0 net.ipv6.conf.default.accept_ra_defrtr = 0 net.ipv6.conf.default.autoconf = 0 net.ipv6.conf.default.dad_transmits = 0 net.ipv6.conf.default.max_addresses = 1 # 优化LB使用的端口 # 增加系统文件描述符限制 fs.file-max = 65535 # 允许更多的PIDs (减少滚动翻转问题); may break some programs 32768 kernel.pid_max = 65536 # 增加系统IP端口限制 net.ipv4.ip_local_port_range = 2000 65535 # 增加TCP最大缓冲区大小 net.ipv4.tcp_rmem = 4096 87380 8388608 net.ipv4.tcp_wmem = 4096 87380 8388608 # 增加Linux自动调整TCP缓冲区限制 # 最小,默认和最大可使用的字节数 # 最大值不低于4MB,如果你使用非常高的BDP路径可以设置得更高 # Tcp窗口等 net.core.rmem_max = 8388608 net.core.wmem_max = 8388608 net.core.netdev_max_backlog = 5000 net.ipv4.tcp_window_scaling = 1 ================================================ FILE: codes/linux/download.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- printf "${BLUE}\n" cat << EOF ################################################################################### # linux-tutorial 运维脚本工具集下载脚本 # 下载 https://github.com/dunwu/linux-tutorial 中的所有脚本到当前服务器的 # /home/scripts/linux-tutorial 目录下 # @system: 适用于 CentOS # @author: Zhang Peng # See: https://github.com/dunwu/linux-tutorial ################################################################################### EOF printf "${RESET}\n" root=/home/scripts/linux-tutorial printf "\n${GREEN}>>>>>>>> Download linux-tutorial to ${root} begin.${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "\n${RED}Not detected yum.${RESET}"; exit 1; } command -v git > /dev/null 2>&1 || { printf "\n${YELLOW}Not detected git. Install git.${RESET}\n"; yum install -y git; } if [[ -d ${root} ]]; then cd ${root} git pull else mkdir -p ${root} git clone https://gitee.com/turnon/linux-tutorial.git ${root} fi chmod +x -R ${root} printf "\n${GREEN}<<<<<<<< Download linux-tutorial to ${root} end.${RESET}\n" ================================================ FILE: codes/linux/dunwu-ops.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # CentOS 常用软件一键安装脚本 # @author Zhang Peng # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color export ENV_COLOR_U_BLACK="\033[4;30m" export ENV_COLOR_U_RED="\033[4;31m" export ENV_COLOR_U_GREEN="\033[4;32m" export ENV_COLOR_U_YELLOW="\033[4;33m" export ENV_COLOR_U_BLUE="\033[4;34m" export ENV_COLOR_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_CYAN="\033[4;36m" export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color export ENV_COLOR_BG_BLACK="\033[40m" export ENV_COLOR_BG_RED="\033[41m" export ENV_COLOR_BG_GREEN="\033[42m" export ENV_COLOR_BG_YELLOW="\033[43m" export ENV_COLOR_BG_BLUE="\033[44m" export ENV_COLOR_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_CYAN="\033[46m" export ENV_COLOR_BG_WHITE="\033[47m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) # 日志路径 LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} # 日志目录 LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { if [[ ! -x "${LOG_PATH}" ]]; then mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then logInfo "$@" return ${ENV_SUCCEED} else logError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ functions # 打印头部信息 printHeadInfo() { printf "${C_B_BLUE}\n" cat << EOF ################################################################################### # 欢迎使用 Dunwu Shell 运维脚本 # 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### EOF printf "${C_RESET}\n" } # 打印尾部信息 printFootInfo() { printf "${C_B_BLUE}\n" cat << EOF ################################################################################### # 脚本执行结束,感谢使用! ################################################################################### EOF printf "${C_RESET}\n" } # 检查操作系统环境 checkOsVersion() { if (($1 == 1)); then platform=`uname -i` if [[ ${platform} != "x86_64" ]]; then logError "脚本仅支持 64 位操作系统!" exit 1 fi version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` if [[ ${version} != 7 ]]; then logError "脚本仅支持 CentOS 7!" exit 1 fi fi } menus=( "配置系统" "安装软件" "退出" ) selectAndExecTask() { printHeadInfo PS3="请输入命令编号:" select item in "${menus[@]}" do case ${item} in "配置系统") ./dunwu-sys.sh selectAndExecTask ;; "安装软件") ./dunwu-soft.sh selectAndExecTask ;; "退出") printFootInfo exit 0 ;; *) logWarn "输入项不支持!" selectAndExecTask ;; esac break done } # ------------------------------------------------------------------------------ main checkOsVersion 1 selectAndExecTask ================================================ FILE: codes/linux/dunwu-soft.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # CentOS 常用软件一键安装脚本 # @author Zhang Peng # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color export ENV_COLOR_U_BLACK="\033[4;30m" export ENV_COLOR_U_RED="\033[4;31m" export ENV_COLOR_U_GREEN="\033[4;32m" export ENV_COLOR_U_YELLOW="\033[4;33m" export ENV_COLOR_U_BLUE="\033[4;34m" export ENV_COLOR_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_CYAN="\033[4;36m" export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color export ENV_COLOR_BG_BLACK="\033[40m" export ENV_COLOR_BG_RED="\033[41m" export ENV_COLOR_BG_GREEN="\033[42m" export ENV_COLOR_BG_YELLOW="\033[43m" export ENV_COLOR_BG_BLUE="\033[44m" export ENV_COLOR_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_CYAN="\033[46m" export ENV_COLOR_BG_WHITE="\033[47m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) # 日志路径 LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} # 日志目录 LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { if [[ ! -x "${LOG_PATH}" ]]; then mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then logInfo "$@" return ${ENV_SUCCEED} else logError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ functions # 打印头部信息 printHeadInfo() { printf "${C_B_BLUE}\n" cat << EOF ################################################################################### # 欢迎使用 CentOS 常用软件一键安装脚本 # 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### EOF printf "${C_RESET}\n" } # print menu printMenu() { printf "${C_B_MAGENTA}" menus=( docker fastdfs gitlab jdk8 jenkins kafka maven mongodb mysql nacos nexus nginx nodejs redis rocketmq tomcat8 zookeeper zsh exit ) for i in "${!menus[@]}"; do index=`expr ${i} + 1` val=`expr ${index} % 2` printf "[%02d] %-20s" "${index}" "${menus[$i]}" if [[ ${val} -eq 0 ]]; then printf "\n" fi done printf "\n\n${C_B_BLUE}请选择需要安装的软件:${C_RESET}" } # exec shell to install soft main() { printMenu read -t 30 index if [[ -n ${index} ]]; then no=`expr ${index} - 1` len=${#menus[*]} if [[ ${index} -gt ${len} ]]; then logWarn "输入项不支持!" exit 1 fi key=${menus[$no]} if [[ ${key} == 'exit' ]]; then logInfo "退出软件安装脚本。" exit 0 fi sh soft/${key}-install.sh printf "\n" main else logWarn "输入项不支持!" exit 1 fi } # ------------------------------------------------------------------------------ main printHeadInfo main ================================================ FILE: codes/linux/dunwu-sys.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # CentOS 环境初始化脚本 # @author Zhang Peng # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color export ENV_COLOR_U_BLACK="\033[4;30m" export ENV_COLOR_U_RED="\033[4;31m" export ENV_COLOR_U_GREEN="\033[4;32m" export ENV_COLOR_U_YELLOW="\033[4;33m" export ENV_COLOR_U_BLUE="\033[4;34m" export ENV_COLOR_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_CYAN="\033[4;36m" export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color export ENV_COLOR_BG_BLACK="\033[40m" export ENV_COLOR_BG_RED="\033[41m" export ENV_COLOR_BG_GREEN="\033[42m" export ENV_COLOR_BG_YELLOW="\033[43m" export ENV_COLOR_BG_BLUE="\033[44m" export ENV_COLOR_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_CYAN="\033[46m" export ENV_COLOR_BG_WHITE="\033[47m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) # 日志路径 LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} # 日志目录 LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { if [[ ! -x "${LOG_PATH}" ]]; then mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then logInfo "$@" return ${ENV_SUCCEED} else logError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ functions # 打印头部信息 printHeadInfo() { printf "${C_B_BLUE}\n" cat << EOF ################################################################################### # 欢迎使用 Dunwu Shell 环境初始化脚本(设置环境配置、安装基本的命令工具) # 适用于 Linux CentOS 环境 # @author: Zhang Peng ################################################################################### EOF printf "${C_RESET}\n" } LINUX_SCRIPTS_DIR=$(cd `dirname $0`; pwd) menus=( "替换yum镜像" "安装基本的命令工具" "安装常用libs" "系统配置" "全部执行" "退出" ) main() { PS3="请输入命令编号:" select item in "${menus[@]}" do case ${item} in "替换yum镜像") sh ${LINUX_SCRIPTS_DIR}/sys/change-yum-repo.sh main ;; "安装基本的命令工具") sh ${LINUX_SCRIPTS_DIR}/sys/install-tools.sh main ;; "安装常用libs") sh ${LINUX_SCRIPTS_DIR}/sys/install-libs.sh main ;; "系统配置") sh ${LINUX_SCRIPTS_DIR}/sys/sys-settings.sh ${LINUX_SCRIPTS_DIR}/sys main ;; "全部执行") sh ${LINUX_SCRIPTS_DIR}/sys/change-yum-repo.sh sh ${LINUX_SCRIPTS_DIR}/sys/install-tools.sh sh ${LINUX_SCRIPTS_DIR}/sys/install-libs.sh sh ${LINUX_SCRIPTS_DIR}/sys/sys-settings.sh ${LINUX_SCRIPTS_DIR}/sys logInfo "执行完毕,退出" ;; "退出") exit 0 ;; *) logWarn "输入项不支持!" main ;; esac break done } # ------------------------------------------------------------------------------ main printHeadInfo main ================================================ FILE: codes/linux/libtest/README.md ================================================ # Git 脚本工具 这里汇总一些常用的、简单的 git shell 脚本。 更多功能强大的工具,可以参考 [资源](#资源) ## 资源 - [git-extras](https://github.com/tj/git-extras) - 丰富的 git 扩展支持 - [gitflow](https://github.com/nvie/gitflow) - git-flow shell 版本 - [gitflow-avh](https://github.com/petervanderdoes/gitflow-avh) - git-flow shell 版本 ================================================ FILE: codes/linux/libtest/env-test.sh ================================================ #!/usr/bin/env bash # 装载其它库 source ../lib/utils.sh # ------------------------------------------------------------------------------ 颜色变量测试 printf "${C_B_YELLOW}测试彩色打印:${C_RESET}\n" printf "${C_BLACK}Hello.${C_RESET}\n" printf "${C_RED}Hello.${C_RESET}\n" printf "${C_GREEN}Hello.${C_RESET}\n" printf "${C_YELLOW}Hello.${C_RESET}\n" printf "${C_BLUE}Hello.${C_RESET}\n" printf "${C_MAGENTA}Hello.${C_RESET}\n" printf "${C_CYAN}Hello.${C_RESET}\n" printf "${C_B_BLACK}Hello.${C_RESET}\n" printf "${C_B_RED}Hello.${C_RESET}\n" printf "${C_B_GREEN}Hello.${C_RESET}\n" printf "${C_B_YELLOW}Hello.${C_RESET}\n" printf "${C_B_BLUE}Hello.${C_RESET}\n" printf "${C_B_MAGENTA}Hello.${C_RESET}\n" printf "${C_B_CYAN}Hello.${C_RESET}\n" printf "${C_U_BLACK}Hello.${C_RESET}\n" printf "${C_U_RED}Hello.${C_RESET}\n" printf "${C_U_GREEN}Hello.${C_RESET}\n" printf "${C_U_YELLOW}Hello.${C_RESET}\n" printf "${C_U_BLUE}Hello.${C_RESET}\n" printf "${C_U_MAGENTA}Hello.${C_RESET}\n" printf "${C_U_CYAN}Hello.${C_RESET}\n" printf "${C_BG_BLACK}Hello.${C_RESET}\n" printf "${C_BG_RED}Hello.${C_RESET}\n" printf "${C_BG_GREEN}Hello.${C_RESET}\n" printf "${C_BG_YELLOW}Hello.${C_RESET}\n" printf "${C_BG_BLUE}Hello.${C_RESET}\n" printf "${C_BG_MAGENTA}Hello.${C_RESET}\n" printf "${C_BG_CYAN}Hello.${C_RESET}\n" ================================================ FILE: codes/linux/libtest/git-check.sh ================================================ #!/usr/bin/env bash source ../lib/utils.sh source ../lib/git.sh ##################################### MAIN ##################################### if [[ $1 ]]; then DIR=$1 else DIR=$(pwd) fi printInfo "Current path is: ${DIR}." # 判断是否为 git 项目 checkGit ${DIR} if [[ "${YES}" == "$?" ]]; then printInfo "${DIR} is git project." else printError "${DIR} is not git project." fi # 获取 git 分支 getGitLocalBranch printInfo "git local branch: ${GIT_LOCAL_BRANCH}" getGitOriginBranch printInfo "git origin branch: ${GIT_ORIGIN_BRANCH}" ================================================ FILE: codes/linux/libtest/git-update.sh ================================================ #!/usr/bin/env bash source ../lib/utils.sh source ../lib/git.sh doCloneOrPullGit() { cloneOrPullGit $1 $2 $3 $4 $5 if [[ "$?" == "${SUCCEED}" ]]; then printf "\n${C_GREEN}>>>> Update git project [$2/$3] succeed.${C_RESET}\n" return ${SUCCEED} else printf "\n${C_RED}>>>> Update git project [$2/$3] failed.${C_RESET}\n" return ${FAILED} fi } ##################################### MAIN ##################################### ROOT=$(dirname ${BASH_SOURCE[0]}) # 这里需要根据实际情况替换 Git 仓库、项目组、项目、代码分支 REPOSITORY="git@gitee.com" printf "${C_CYAN}Current path is ${ROOT}.${C_RESET}\n" doCloneOrPullGit ${REPOSITORY} turnon linux-tutorial master ${ROOT} r1=$? doCloneOrPullGit ${REPOSITORY} turnon nginx-tutorial master ${ROOT} r2=$? if [[ "${r1}" == "${SUCCEED}" && "${r2}" == "${SUCCEED}" ]]; then printf "\n${C_B_GREEN}<<<< Init workspace Succeed.${C_RESET}\n\n" exit ${SUCCEED} else printf "\n${C_B_RED}<<<< Init workspace Failed.${C_RESET}\n\n" exit ${FAILED} fi ================================================ FILE: codes/linux/libtest/nodejs-test.sh ================================================ #!/usr/bin/env bash # 装载其它库 source ../lib/nodejs.sh # ------------------------------------------------------------------------------ ROOT=$(pwd) path=${ROOT} if [[ $1 ]]; then path=$1 fi version=10.15.0 if [[ $2 ]]; then version=$2 fi buildNodejsProject ${path} ${version} ================================================ FILE: codes/linux/soft/README.md ================================================ # 服务安装配置 - [oh-my-zsh 安装](#oh-my-zsh-安装) - [JDK8 安装](#jdk8-安装) - [Maven 安装配置](#maven-安装配置) - [Node.js 安装](#nodejs-安装) - [MongoDB 安装](#mongodb-安装) - [Redis 安装配置](#redis-安装配置) - [Tomcat8 安装](#tomcat8-安装) - [Kafka 安装](#kafka-安装) - [RocketMQ 安装](#rocketmq-安装) - [Nacos 安装](#nacos-安装) - [ZooKeeper 安装](#zookeeper-安装) - [Nginx 运维](#nginx-安装) - [Fastdfs 安装](#fastdfs-安装) - [Docker 安装](#docker-安装) ## oh-my-zsh 安装 说明: 安装 [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh) 使用方法 执行以下任意命令即可执行安装脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zsh-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zsh-install.sh | bash ``` ## JDK8 安装 说明: JDK8 会被安装到 `/usr/lib/jvm/java` 路径。 使用方法: 执行以下任意命令即可执行安装脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/jdk8-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/jdk8-install.sh | bash ``` ## Maven 安装配置 说明: - 脚本会下载解压 maven `3.6.0` 到 `/opt/maven` 路径下。 - 备份并替换 `settings.xml`,使用 aliyun 镜像加速 maven。 使用方法: 执行以下任意命令即可执行安装脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/maven-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/maven-install.sh | bash ``` ## Node.js 安装 说明: 脚本会先安装 nvm(nodejs 版本管理器),并通过 nvm 安装 nodejs `10.15.2`。 使用方法: 执行以下任意命令即可执行安装脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nodejs-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nodejs-install.sh | bash ``` ## MongoDB 安装 说明: 下载 mongodb `4.0.9` 并解压安装到 `/opt/mongodb` 路径下。 使用方法: 执行以下任意命令即可执行安装脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/mongodb-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/mongodb-install.sh | bash ``` ## Redis 安装配置 **安装说明** - 采用编译方式安装 Redis, 并将其注册为 systemd 服务 - 安装路径为:`/usr/local/redis` - 默认下载安装 `5.0.4` 版本,端口号为:`6379`,密码为空 **使用方法** - 默认安装 - 执行以下任意命令即可: ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/redis-install.sh | bash ``` - 自定义安装 - 下载脚本到本地,并按照以下格式执行: ```shell sh redis-install.sh [version] [port] [password] ``` 参数说明: - `version` - redis 版本号 - `port` - redis 服务端口号 - `password` - 访问密码 ## Tomcat8 安装 说明: 下载 tomcat `8.5.28` 并解压安装到 `/opt/tomcat` 路径下。 使用方法: ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/tomcat8-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/tomcat8-install.sh | bash ``` ## Kafka 安装 说明: 下载 kafka `2.2.0` 并解压安装到 `/opt/kafka` 路径下。 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/kafka-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/kafka-install.sh | bash ``` ## RocketMQ 安装 说明: 下载 rocketmq `4.5.0` 并解压安装到 `/opt/rocketmq` 路径下。 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/rocketmq-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/rocketmq-install.sh | bash ``` ## Nacos 安装 说明: 下载 Nacos `1.0.0` 并解压安装到 `/opt/nacos` 路径下。 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nacos-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nacos-install.sh | bash ``` ## ZooKeeper 安装 说明: 下载 zookeeper `3.4.12` 并解压安装到 `/opt/zookeeper` 路径下。 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zookeeper-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zookeeper-install.sh | bash ``` ## Nginx 运维 **安装说明** - 采用编译方式安装 Nginx, 并将其注册为 systemd 服务 - 安装路径为:`/usr/local/nginx` - 默认下载安装 `1.16.0` 版本 **使用方法** - 默认安装 - 执行以下任意命令即可: ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nginx-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/nginx-install.sh | bash ``` - 自定义安装 - 下载脚本到本地,并按照以下格式执行: ```bash sh nginx-install.sh [version] ``` ## Fastdfs 安装 说明: 采用编译方式安装 Fastdfs 下载 Fastdfs 并解压安装到 `/opt/fdfs` 路径下。 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash ``` ## Docker 安装 说明: 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/docker-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/docker-install.sh | bash ``` ## FastDFS 安装 说明: 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/fastdfs-install.sh | bash ``` ================================================ FILE: codes/linux/soft/arthas-install.sh ================================================ #!/usr/bin/env bash # ---------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # ---------------------------------------------------------------------------------- printf "${PURPLE}" cat << EOF # ---------------------------------------------------------------------------------- # Arthas 安装脚本 # @author: Zhang Peng # ---------------------------------------------------------------------------------- EOF printf "${RESET}" printf "${BLUE}>>>>>>>> begin.\n${RESET}" root=/opt/arthas if [[ -n $1 ]]; then root=$1 fi mkdir -p ${root} curl -o ${root}/arthas-boot.jar https://alibaba.github.io/arthas/arthas-boot.jar printf "${GREEN}[OK]\n${RESET}" printf "${BLUE}<<<<<<<< end.\n${RESET}" ================================================ FILE: codes/linux/soft/config/fastdfs/client.conf ================================================ # connect timeout in seconds # default value is 30s connect_timeout=30 # network timeout in seconds # default value is 30s network_timeout=60 # the base path to store log files base_path=/home/fdfs # tracker_server can ocur more than once, and tracker_server format is # "host:port", host can be hostname or ip address tracker_server=127.0.0.1:22122 #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info # if use connection pool # default value is false # since V4.05 use_connection_pool = false # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 connection_pool_max_idle_time = 3600 # if load FastDFS parameters from tracker server # since V4.05 # default value is false load_fdfs_parameters_from_tracker=false # if use storage ID instead of IP address # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # default value is false # since V4.05 use_storage_id = false # specify storage ids filename, can use relative or absolute path # same as tracker.conf # valid only when load_fdfs_parameters_from_tracker is false # since V4.05 storage_ids_filename = storage_ids.conf #HTTP settings http.tracker_server_port=80 #use "#include" directive to include HTTP other settiongs ##include http.conf ================================================ FILE: codes/linux/soft/config/fastdfs/storage.conf ================================================ # is this config file disabled # false for enabled # true for disabled disabled=false # the name of the group this storage server belongs to # # comment or remove this item for fetching from tracker server, # in this case, use_storage_id must set to true in tracker.conf, # and storage_ids.conf must be configed correctly. group_name=group1 # bind an address of this host # empty for bind all addresses of this host bind_addr= # if bind an address of this host when connect to other servers # (this storage server as a client) # true for binding the address configed by above parameter: "bind_addr" # false for binding any address of this host client_bind=true # the storage server port port=23000 # connect timeout in seconds # default value is 30s connect_timeout=10 # network timeout in seconds # default value is 30s network_timeout=60 # heart beat interval in seconds heart_beat_interval=30 # disk usage report interval in seconds stat_report_interval=60 # the base path to store data and log files base_path=/home/fdfs # max concurrent connections the server supported # default value is 256 # more max_connections means more memory will be used # you should set this parameter larger, eg. 10240 max_connections=1024 # the buff size to recv / send data # this parameter must more than 8KB # default value is 64KB # since V2.00 buff_size = 256KB # accept thread count # default value is 1 # since V4.07 accept_threads=1 # work thread count, should <= max_connections # work thread deal network io # default value is 4 # since V2.00 work_threads=4 # if disk read / write separated ## false for mixed read and write ## true for separated read and write # default value is true # since V2.00 disk_rw_separated = true # disk reader thread count per store base path # for mixed read / write, this parameter can be 0 # default value is 1 # since V2.00 disk_reader_threads = 1 # disk writer thread count per store base path # for mixed read / write, this parameter can be 0 # default value is 1 # since V2.00 disk_writer_threads = 1 # when no entry to sync, try read binlog again after X milliseconds # must > 0, default value is 200ms sync_wait_msec=50 # after sync a file, usleep milliseconds # 0 for sync successively (never call usleep) sync_interval=0 # storage sync start time of a day, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 sync_start_time=00:00 # storage sync end time of a day, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 sync_end_time=23:59 # write to the mark file after sync N files # default value is 500 write_mark_file_freq=500 # path(disk or mount point) count, default value is 1 store_path_count=1 # store_path#, based 0, if store_path0 not exists, it's value is base_path # the paths must be exist store_path0=/home/fdfs #store_path1=/home/fdfs2 # subdir_count * subdir_count directories will be auto created under each # store_path (disk), value can be 1 to 256, default value is 256 subdir_count_per_path=256 # tracker_server can ocur more than once, and tracker_server format is # "host:port", host can be hostname or ip address tracker_server=127.0.0.1:22122 #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group= #unix username to run this program, #not set (empty) means run by current user run_by_user= # allow_hosts can ocur more than once, host can be hostname or ip address, # "*" (only one asterisk) means match all ip addresses # we can use CIDR ips like 192.168.5.64/26 # and also use range like these: 10.0.1.[0-254] and host[01-08,20-25].domain.com # for example: # allow_hosts=10.0.1.[1-15,20] # allow_hosts=host[01-08,20-25].domain.com # allow_hosts=192.168.5.64/26 allow_hosts=* # the mode of the files distributed to the data path # 0: round robin(default) # 1: random, distributted by hash code file_distribute_path_mode=0 # valid when file_distribute_to_path is set to 0 (round robin), # when the written file count reaches this number, then rotate to next path # default value is 100 file_distribute_rotate_count=100 # call fsync to disk when write big file # 0: never call fsync # other: call fsync when written bytes >= this bytes # default value is 0 (never call fsync) fsync_after_written_bytes=0 # sync log buff to disk every interval seconds # must > 0, default value is 10 seconds sync_log_buff_interval=10 # sync binlog buff / cache to disk every interval seconds # default value is 60 seconds sync_binlog_buff_interval=10 # sync storage stat info to disk every interval seconds # default value is 300 seconds sync_stat_file_interval=300 # thread stack size, should >= 512KB # default value is 512KB thread_stack_size=512KB # the priority as a source server for uploading file. # the lower this value, the higher its uploading priority. # default value is 10 upload_priority=10 # the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a # multi aliases split by comma. empty value means auto set by OS type # default values is empty if_alias_prefix= # if check file duplicate, when set to true, use FastDHT to store file indexes # 1 or yes: need check # 0 or no: do not check # default value is 0 check_file_duplicate=0 # file signature method for check file duplicate ## hash: four 32 bits hash code ## md5: MD5 signature # default value is hash # since V4.01 file_signature_method=hash # namespace for storing file indexes (key-value pairs) # this item must be set when check_file_duplicate is true / on key_namespace=FastDFS # set keep_alive to 1 to enable persistent connection with FastDHT servers # default value is 0 (short connection) keep_alive=0 # you can use "#include filename" (not include double quotes) directive to # load FastDHT server list, when the filename is a relative path such as # pure filename, the base path is the base path of current/this config file. # must set FastDHT server list when check_file_duplicate is true / on # please see INSTALL of FastDHT for detail ##include /home/yuqing/fastdht/conf/fdht_servers.conf # if log to access log # default value is false # since V4.00 use_access_log = false # if rotate the access log every day # default value is false # since V4.00 rotate_access_log = false # rotate access log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 # since V4.00 access_log_rotate_time=00:00 # if rotate the error log every day # default value is false # since V4.02 rotate_error_log = false # rotate error log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 # since V4.02 error_log_rotate_time=00:00 # rotate access log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 # since V4.02 rotate_access_log_size = 0 # rotate error log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 # since V4.02 rotate_error_log_size = 0 # keep days of the log files # 0 means do not delete old log files # default value is 0 log_file_keep_days = 0 # if skip the invalid record when sync file # default value is false # since V4.02 file_sync_skip_invalid_record=false # if use connection pool # default value is false # since V4.05 use_connection_pool = false # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 connection_pool_max_idle_time = 3600 # use the ip address of this storage server if domain_name is empty, # else this domain name will ocur in the url redirected by the tracker server http.domain_name= # the port of the web server on this storage server http.server_port=7001 ================================================ FILE: codes/linux/soft/config/fastdfs/tracker.conf ================================================ # is this config file disabled # false for enabled # true for disabled disabled=false # bind an address of this host # empty for bind all addresses of this host bind_addr= # the tracker server port port=22122 # connect timeout in seconds # default value is 30s connect_timeout=10 # network timeout in seconds # default value is 30s network_timeout=60 # the base path to store data and log files base_path=/home/fdfs # max concurrent connections this server supported # you should set this parameter larger, eg. 102400 max_connections=1024 # accept thread count # default value is 1 # since V4.07 accept_threads=1 # work thread count, should <= max_connections # default value is 4 # since V2.00 work_threads=4 # min buff size # default value 8KB min_buff_size = 8KB # max buff size # default value 128KB max_buff_size = 128KB # the method of selecting group to upload files # 0: round robin # 1: specify group # 2: load balance, select the max free space group to upload file store_lookup=2 # which group to upload file # when store_lookup set to 1, must set store_group to the group name store_group=group2 # which storage server to upload file # 0: round robin (default) # 1: the first server order by ip address # 2: the first server order by priority (the minimal) # Note: if use_trunk_file set to true, must set store_server to 1 or 2 store_server=0 # which path(means disk or mount point) of the storage server to upload file # 0: round robin # 2: load balance, select the max free space path to upload file store_path=0 # which storage server to download file # 0: round robin (default) # 1: the source storage server which the current file uploaded to download_server=0 # reserved storage space for system or other applications. # if the free(available) space of any stoarge server in # a group <= reserved_storage_space, # no file can be uploaded to this group. # bytes unit can be one of follows: ### G or g for gigabyte(GB) ### M or m for megabyte(MB) ### K or k for kilobyte(KB) ### no unit for byte(B) ### XX.XX% as ratio such as reserved_storage_space = 10% reserved_storage_space = 10% #standard log level as syslog, case insensitive, value list: ### emerg for emergency ### alert ### crit for critical ### error ### warn for warning ### notice ### info ### debug log_level=info #unix group name to run this program, #not set (empty) means run by the group of current user run_by_group= #unix username to run this program, #not set (empty) means run by current user run_by_user= # allow_hosts can ocur more than once, host can be hostname or ip address, # "*" (only one asterisk) means match all ip addresses # we can use CIDR ips like 192.168.5.64/26 # and also use range like these: 10.0.1.[0-254] and host[01-08,20-25].domain.com # for example: # allow_hosts=10.0.1.[1-15,20] # allow_hosts=host[01-08,20-25].domain.com # allow_hosts=192.168.5.64/26 allow_hosts=* # sync log buff to disk every interval seconds # default value is 10 seconds sync_log_buff_interval = 10 # check storage server alive interval seconds check_active_interval = 120 # thread stack size, should >= 64KB # default value is 64KB thread_stack_size = 64KB # auto adjust when the ip address of the storage server changed # default value is true storage_ip_changed_auto_adjust = true # storage sync file max delay seconds # default value is 86400 seconds (one day) # since V2.00 storage_sync_file_max_delay = 86400 # the max time of storage sync a file # default value is 300 seconds # since V2.00 storage_sync_file_max_time = 300 # if use a trunk file to store several small files # default value is false # since V3.00 use_trunk_file = false # the min slot size, should <= 4KB # default value is 256 bytes # since V3.00 slot_min_size = 256 # the max slot size, should > slot_min_size # store the upload file to trunk file when it's size <= this value # default value is 16MB # since V3.00 slot_max_size = 16MB # the trunk file size, should >= 4MB # default value is 64MB # since V3.00 trunk_file_size = 64MB # if create trunk file advancely # default value is false # since V3.06 trunk_create_file_advance = false # the time base to create trunk file # the time format: HH:MM # default value is 02:00 # since V3.06 trunk_create_file_time_base = 02:00 # the interval of create trunk file, unit: second # default value is 38400 (one day) # since V3.06 trunk_create_file_interval = 86400 # the threshold to create trunk file # when the free trunk file size less than the threshold, will create # the trunk files # default value is 0 # since V3.06 trunk_create_file_space_threshold = 20G # if check trunk space occupying when loading trunk free spaces # the occupied spaces will be ignored # default value is false # since V3.09 # NOTICE: set this parameter to true will slow the loading of trunk spaces # when startup. you should set this parameter to true when neccessary. trunk_init_check_occupying = false # if ignore storage_trunk.dat, reload from trunk binlog # default value is false # since V3.10 # set to true once for version upgrade when your version less than V3.10 trunk_init_reload_from_binlog = false # the min interval for compressing the trunk binlog file # unit: second # default value is 0, 0 means never compress # FastDFS compress the trunk binlog when trunk init and trunk destroy # recommand to set this parameter to 86400 (one day) # since V5.01 trunk_compress_binlog_min_interval = 0 # if use storage ID instead of IP address # default value is false # since V4.00 use_storage_id = false # specify storage ids filename, can use relative or absolute path # since V4.00 storage_ids_filename = storage_ids.conf # id type of the storage server in the filename, values are: ## ip: the ip address of the storage server ## id: the server id of the storage server # this paramter is valid only when use_storage_id set to true # default value is ip # since V4.03 id_type_in_filename = ip # if store slave file use symbol link # default value is false # since V4.01 store_slave_file_use_link = false # if rotate the error log every day # default value is false # since V4.02 rotate_error_log = false # rotate error log time base, time format: Hour:Minute # Hour from 0 to 23, Minute from 0 to 59 # default value is 00:00 # since V4.02 error_log_rotate_time=00:00 # rotate error log when the log file exceeds this size # 0 means never rotates log file by log file size # default value is 0 # since V4.02 rotate_error_log_size = 0 # keep days of the log files # 0 means do not delete old log files # default value is 0 log_file_keep_days = 0 # if use connection pool # default value is false # since V4.05 use_connection_pool = false # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 connection_pool_max_idle_time = 3600 # HTTP port on this tracker server http.server_port=7000 # check storage HTTP server alive interval seconds # <= 0 for never check # default value is 30 http.check_alive_interval=30 # check storage HTTP server alive type, values are: # tcp : connect to the storge server with HTTP port only, # do not request and get response # http: storage check alive url must return http status 200 # default value is tcp http.check_alive_type=tcp # check storage HTTP server alive uri/url # NOTE: storage embed HTTP server support uri: /status.html http.check_alive_uri=/status.html ================================================ FILE: codes/linux/soft/config/mysql/my.cnf ================================================ # ------------------------------------------------------------------------------- # Mysql 基本配置模板 # ------------------------------------------------------------------------------- [mysqld] # GENERAL # ------------------------------------------------------------------------------- server_id = 2 datadir = /var/lib/mysql socket = /var/lib/mysql/mysql.sock pid_file = /var/lib/mysql/mysql.pid user = mysql port = 3306 default_storage_engine = InnoDB default_time_zone = '+8:00' character_set_server = utf8mb4 collation_server = utf8mb4_0900_ai_ci # LOG # ------------------------------------------------------------------------------- log_error = /var/log/mysql/mysql.log slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql_slow_query_log.log long_query_time = 3 min_examined_row_limit = 100 expire_logs_days = 7 # InnoDB # ------------------------------------------------------------------------------- innodb_buffer_pool_size = 4G innodb_log_file_size = 128M innodb_file_per_table = 1 innodb_flush_method = O_DIRECT # MyIsam # ------------------------------------------------------------------------------- key_buffer_size = 256M # OTHER # ------------------------------------------------------------------------------- tmp_table_size = 32M max_heap_table_size = 32M max_connections = 10000 open_files_limit = 65535 [client] socket = /var/lib/mysql/mysql.sock port = 3306 ================================================ FILE: codes/linux/soft/config/nginx/conf/fdfs.conf ================================================ server { listen 7001; ## 该端口为storage.conf中的http.server_port相同 server_name localhost; location ~/group[0-9]/ { ngx_fastdfs_module; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } ================================================ FILE: codes/linux/soft/config/nginx/nginx.conf ================================================ worker_processes 1; error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; pid /usr/local/nginx/logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; include conf.d/*.conf; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/nginx-http-access.log; sendfile on; keepalive_timeout 65; client_max_body_size 20m; client_body_buffer_size 128k; #common header set proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } ================================================ FILE: codes/linux/soft/config/nginx/nginx.service ================================================ [Unit] Description=The NGINX HTTP and reverse proxy server After=syslog.target network.target remote-fs.target nss-lookup.target [Service] Type=forking PIDFile=/usr/local/nginx/logs/nginx.pid ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload=/usr/local/nginx/sbin/nginx -s reload ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target ================================================ FILE: codes/linux/soft/config/redis/cluster/27001/sentinel.conf ================================================ port 27001 daemonize yes sentinel monitor redis-master 172.22.6.3 7001 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27001/27001.log ================================================ FILE: codes/linux/soft/config/redis/cluster/27002/sentinel.conf ================================================ port 27002 daemonize yes sentinel monitor redis-master 172.22.6.3 7002 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27002/27002.log ================================================ FILE: codes/linux/soft/config/redis/cluster/27003/sentinel.conf ================================================ port 27003 daemonize yes sentinel monitor redis-master 172.22.6.3 7003 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27003/27003.log ================================================ FILE: codes/linux/soft/config/redis/cluster/27004/sentinel.conf ================================================ port 27004 daemonize yes sentinel monitor redis-master 172.22.6.3 7007 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27004/27004.log ================================================ FILE: codes/linux/soft/config/redis/cluster/27005/sentinel.conf ================================================ port 27005 daemonize yes sentinel monitor redis-master 172.22.6.3 7008 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27005/27005.log ================================================ FILE: codes/linux/soft/config/redis/cluster/27006/sentinel.conf ================================================ port 27006 daemonize yes sentinel monitor redis-master 172.22.6.3 7009 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27006/27006.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7001/redis.conf ================================================ port 7001 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7001/7001.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7001 pidfile /usr/local/redis/conf/7001/7001.pid logfile /usr/local/redis/conf/7001/7001.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7002/redis.conf ================================================ port 7002 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7002/7002.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7002 pidfile /usr/local/redis/conf/7002/7002.pid logfile /usr/local/redis/conf/7002/7002.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7003/redis.conf ================================================ port 7003 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7003/7003.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7003 pidfile /usr/local/redis/conf/7003/7003.pid logfile /usr/local/redis/conf/7003/7003.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7004/redis.conf ================================================ port 7004 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7004/7004.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7004 pidfile /usr/local/redis/conf/7004/7004.pid logfile /usr/local/redis/conf/7004/7004.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7005/redis.conf ================================================ port 7005 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7005/7005.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7005 pidfile /usr/local/redis/conf/7005/7005.pid logfile /usr/local/redis/conf/7005/7005.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7006/redis.conf ================================================ port 7006 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7006/7006.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7006 pidfile /usr/local/redis/conf/7006/7006.pid logfile /usr/local/redis/conf/7006/7006.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7007/redis.conf ================================================ port 7007 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7007/7007.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7007 pidfile /usr/local/redis/conf/7007/7007.pid logfile /usr/local/redis/conf/7007/7007.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7008/redis.conf ================================================ port 7008 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7008/7008.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7008 pidfile /usr/local/redis/conf/7008/7008.pid logfile /usr/local/redis/conf/7008/7008.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7009/redis.conf ================================================ port 7009 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7009/7009.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7009 pidfile /usr/local/redis/conf/7009/7009.pid logfile /usr/local/redis/conf/7009/7009.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7010/redis.conf ================================================ port 7010 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7010/7010.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7010 pidfile /usr/local/redis/conf/7010/7010.pid logfile /usr/local/redis/conf/7010/7010.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7011/redis.conf ================================================ port 7011 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7011/7011.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7011 pidfile /usr/local/redis/conf/7011/7011.pid logfile /usr/local/redis/conf/7011/7011.log ================================================ FILE: codes/linux/soft/config/redis/cluster/7012/redis.conf ================================================ port 7012 bind 172.22.6.3 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7012/7012.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7012 pidfile /usr/local/redis/conf/7012/7012.pid logfile /usr/local/redis/conf/7012/7012.log ================================================ FILE: codes/linux/soft/config/redis/cluster/README.md ================================================ # Redis 集群配置 ## 使用方式 集群拓扑: - 三主三从 - 三哨兵 启动方式: - 先执行 redis-cluster.sh,会自动根据 7001 ~ 7006 目录启动服务器,并将其配置为集群。 - 再执行 start-sentinel.sh,会根据 27001 ~ 27003 目录启动哨兵,监听集群中的三个主节点。 ## 配置 (1)集群服务器配置 redis.conf ``` port 7001 bind 0.0.0.0 daemonize yes cluster-enabled yes cluster-config-file /usr/local/redis/conf/7001/7001.conf cluster-node-timeout 10000 appendonly yes dir /usr/local/redis/conf/7001 pidfile /usr/local/redis/conf/7001/7001.pid logfile /usr/local/redis/conf/7001/7001.log ``` 端口号、配置目录(`/usr/local/redis/conf`)根据实际情况修改。 (2)哨兵服务器配置 sentinel.conf ``` port 27003 daemonize yes sentinel monitor redis-master 172.22.6.3 7003 2 sentinel down-after-milliseconds redis-master 5000 sentinel failover-timeout redis-master 900000 sentinel parallel-syncs redis-master 1 #sentinel auth-pass redis-master 123456 logfile /usr/local/redis/conf/27003/27003.log ``` 端口号、配置目录(`/usr/local/redis/conf`)根据实际情况修改。 最重要的配置在于:sentinel monitor redis-master 172.22.6.3 7003 2 表示监听的服务器集群名叫 redis-master,当前哨兵监听的服务器节点是:172.22.6.3:7003,这个节点如果是主节点,一旦宕机,选举新的主节点,需要至少 2 个哨兵同意。 ================================================ FILE: codes/linux/soft/config/redis/cluster/redis-cluster.sh ================================================ #!/usr/bin/env bash # Settings HOST="172.22.6.3" PORT=7000 TIMEOUT=2000 NODES=6 REPLICAS=1 ENDPORT=$((PORT+NODES)) # You may want to put the above config parameters into config.sh in order to # override the defaults without modifying this script. if [[ -a config.sh ]] then source "config.sh" fi if [[ "$1" == "create" ]] then HOSTLIST="" while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) HOSTLIST="$HOSTLIST $HOST:$PORT" done /opt/redis/src/redis-cli --cluster create ${HOSTLIST} --cluster-replicas ${REPLICAS} exit 0 fi if [[ "$1" == "start" ]] then while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) echo "Starting $PORT" /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf done exit 0 fi if [[ "$1" == "stop" ]] then while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) echo "Stopping $PORT" /opt/redis/src/redis-cli -p ${PORT} shutdown nosave done exit 0 fi if [[ "$1" == "watch" ]] then PORT=$((PORT+1)) while [ 1 ]; do clear date /opt/redis/src/redis-cli -p ${PORT} cluster nodes | head -30 sleep 1 done exit 0 fi if [[ "$1" == "tail" ]] then INSTANCE=$2 PORT=$((PORT+INSTANCE)) tail -f ${PORT}.log exit 0 fi if [[ "$1" == "call" ]] then while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) /opt/redis/src/redis-cli -p ${PORT} $2 $3 $4 $5 $6 $7 $8 $9 done exit 0 fi if [[ "$1" == "clean" ]] then rm -rf *.log rm -rf appendonly*.aof rm -rf dump*.rdb rm -rf nodes*.conf exit 0 fi if [[ "$1" == "clean-logs" ]] then rm -rf *.log exit 0 fi echo "Usage: $0 [start|create|stop|watch|tail|clean]" echo "start -- Launch Redis Cluster instances." echo "create -- Create a cluster using redis-cli --cluster create." echo "stop -- Stop Redis Cluster instances." echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." echo "tail -- Run tail -f of instance at base port + ID." echo "clean -- Remove all instances data, logs, configs." echo "clean-logs -- Remove just instances logs." ================================================ FILE: codes/linux/soft/config/redis/cluster/redis-cluster2.sh ================================================ #!/usr/bin/env bash # Settings HOST="172.22.6.3" PORT=7000 TIMEOUT=2000 NODES=12 REPLICAS=1 ENDPORT=$((PORT+NODES)) # You may want to put the above config parameters into config.sh in order to # override the defaults without modifying this script. if [[ -a config.sh ]] then source "config.sh" fi if [[ "$1" == "create" ]] then HOSTLIST="" while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) HOSTLIST="$HOSTLIST $HOST:$PORT" done /opt/redis/src/redis-cli --cluster create ${HOSTLIST} --cluster-replicas ${REPLICAS} exit 0 fi if [[ "$1" == "start" ]] then while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) echo "Starting $PORT" /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf done exit 0 fi if [[ "$1" == "stop" ]] then while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) echo "Stopping $PORT" /opt/redis/src/redis-cli -p ${PORT} shutdown nosave done exit 0 fi if [[ "$1" == "watch" ]] then PORT=$((PORT+1)) while [ 1 ]; do clear date /opt/redis/src/redis-cli -p ${PORT} cluster nodes | head -30 sleep 1 done exit 0 fi if [[ "$1" == "tail" ]] then INSTANCE=$2 PORT=$((PORT+INSTANCE)) tail -f ${PORT}.log exit 0 fi if [[ "$1" == "call" ]] then while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) /opt/redis/src/redis-cli -p ${PORT} $2 $3 $4 $5 $6 $7 $8 $9 done exit 0 fi if [[ "$1" == "clean" ]] then rm -rf *.log rm -rf appendonly*.aof rm -rf dump*.rdb rm -rf nodes*.conf exit 0 fi if [[ "$1" == "clean-logs" ]] then rm -rf *.log exit 0 fi echo "Usage: $0 [start|create|stop|watch|tail|clean]" echo "start -- Launch Redis Cluster instances." echo "create -- Create a cluster using redis-cli --cluster create." echo "stop -- Stop Redis Cluster instances." echo "watch -- Show CLUSTER NODES output (first 30 lines) of first node." echo "tail -- Run tail -f of instance at base port + ID." echo "clean -- Remove all instances data, logs, configs." echo "clean-logs -- Remove just instances logs." ================================================ FILE: codes/linux/soft/config/redis/cluster/start-cluster.sh ================================================ #!/usr/bin/env bash PORT=6380 NODES=6 ENDPORT=$((PORT+NODES)) # 启动 4 个 redis server while [[ $((PORT < ENDPORT)) != "0" ]]; do PORT=$((PORT+1)) echo "Starting $PORT" /opt/redis/src/redis-server /usr/local/redis/conf/${PORT}/redis.conf done # 创建集群模式,设置副本为 1 # redis cluster 会自动将 4 个节点设置为 一主一从 模式,并且为两个主节点做数据分片 /opt/redis/src/redis-cli --cluster create 172.22.6.3:6381 172.22.6.3:6382 172.22.6.3:6383 172.22.6.3:6384 172.22.6.3:6385 172.22.6.3:6386 --cluster-replicas 1 # 启动哨兵 /opt/redis/src/redis-sentinel /usr/local/redis/conf/26381/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/26382/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/26383/sentinel.conf ================================================ FILE: codes/linux/soft/config/redis/cluster/start-cluster2.sh ================================================ #!/usr/bin/env bash # 启动 4 个 redis server /opt/redis/src/redis-server /usr/local/redis/conf/6381/redis.conf /opt/redis/src/redis-server /usr/local/redis/conf/6382/redis.conf /opt/redis/src/redis-server /usr/local/redis/conf/6383/redis.conf /opt/redis/src/redis-server /usr/local/redis/conf/6384/redis.conf /opt/redis/src/redis-server /usr/local/redis/conf/6385/redis.conf /opt/redis/src/redis-server /usr/local/redis/conf/6386/redis.conf # 创建集群模式,设置副本为 1 # redis cluster 会自动将 4 个节点设置为 一主一从 模式,并且为两个主节点做数据分片 /opt/redis/src/redis-cli --cluster create 172.22.6.3:6381 172.22.6.3:6382 172.22.6.3:6383 172.22.6.3:6384 172.22.6.3:6385 172.22.6.3:6386 --cluster-replicas 1 # 启动哨兵 /opt/redis/src/redis-sentinel /usr/local/redis/conf/26381/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/26382/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/26383/sentinel.conf ================================================ FILE: codes/linux/soft/config/redis/cluster/start-sentinel.sh ================================================ /opt/redis/src/redis-sentinel /usr/local/redis/conf/27001/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/27002/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/27003/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/27004/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/27005/sentinel.conf /opt/redis/src/redis-sentinel /usr/local/redis/conf/27006/sentinel.conf ================================================ FILE: codes/linux/soft/config/redis/redis.conf ================================================ # Redis configuration file example. # # Note that in order to read the configuration file, Redis must be # started with the file path as first argument: # # ./redis-server /path/to/redis.conf # Note on units: when memory size is needed, it is possible to specify # it in the usual form of 1k 5GB 4M and so forth: # # 1k => 1000 bytes # 1kb => 1024 bytes # 1m => 1000000 bytes # 1mb => 1024*1024 bytes # 1g => 1000000000 bytes # 1gb => 1024*1024*1024 bytes # # units are case insensitive so 1GB 1Gb 1gB are all the same. ################################## INCLUDES ################################### # Include one or more other config files here. This is useful if you # have a standard template that goes to all Redis servers but also need # to customize a few per-server settings. Include files can include # other files, so use this wisely. # # Notice option "include" won't be rewritten by command "CONFIG REWRITE" # from admin or Redis Sentinel. Since Redis always uses the last processed # line as value of a configuration directive, you'd better put includes # at the beginning of this file to avoid overwriting config change at runtime. # # If instead you are interested in using includes to override configuration # options, it is better to use include as the last line. # # include /path/to/local.conf # include /path/to/other.conf ################################## MODULES ##################################### # Load modules at startup. If the server is not able to load modules # it will abort. It is possible to use multiple loadmodule directives. # # loadmodule /path/to/my_module.so # loadmodule /path/to/other_module.so ################################## NETWORK ##################################### # By default, if no "bind" configuration directive is specified, Redis listens # for connections from all the network interfaces available on the server. # It is possible to listen to just one or multiple selected interfaces using # the "bind" configuration directive, followed by one or more IP addresses. # # Examples: # # bind 192.168.1.100 10.0.0.1 # bind 127.0.0.1 ::1 # # ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the # internet, binding to all the interfaces is dangerous and will expose the # instance to everybody on the internet. So by default we uncomment the # following bind directive, that will force Redis to listen only into # the IPv4 loopback interface address (this means Redis will be able to # accept connections only from clients running into the same computer it # is running). # # IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES # JUST COMMENT THE FOLLOWING LINE. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bind 172.22.6.3 # Protected mode is a layer of security protection, in order to avoid that # Redis instances left open on the internet are accessed and exploited. # # When protected mode is on and if: # # 1) The server is not binding explicitly to a set of addresses using the # "bind" directive. # 2) No password is configured. # # The server only accepts connections from clients connecting from the # IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain # sockets. # # By default protected mode is enabled. You should disable it only if # you are sure you want clients from other hosts to connect to Redis # even if no authentication is configured, nor a specific set of interfaces # are explicitly listed using the "bind" directive. protected-mode no # Accept connections on the specified port, default is 6379 (IANA #815344). # If port 0 is specified Redis will not listen on a TCP socket. port 6379 # TCP listen() backlog. # # In high requests-per-second environments you need an high backlog in order # to avoid slow clients connections issues. Note that the Linux kernel # will silently truncate it to the value of /proc/sys/net/core/somaxconn so # make sure to raise both the value of somaxconn and tcp_max_syn_backlog # in order to get the desired effect. tcp-backlog 511 # Unix socket. # # Specify the path for the Unix socket that will be used to listen for # incoming connections. There is no default, so Redis will not listen # on a unix socket when not specified. # # unixsocket /tmp/redis.sock # unixsocketperm 700 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 # TCP keepalive. # # If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence # of communication. This is useful for two reasons: # # 1) Detect dead peers. # 2) Take the connection alive from the point of view of network # equipment in the middle. # # On Linux, the specified value (in seconds) is the period used to send ACKs. # Note that to close the connection the double of the time is needed. # On other kernels the period depends on the kernel configuration. # # A reasonable value for this option is 300 seconds, which is the new # Redis default starting with Redis 3.2.1. tcp-keepalive 300 ################################# GENERAL ##################################### # By default Redis does not run as a daemon. Use 'yes' if you need it. # Note that Redis will write a pid file in /var/run/redis.pid when daemonized. daemonize yes # If you run Redis from upstart or systemd, Redis can interact with your # supervision tree. Options: # supervised no - no supervision interaction # supervised upstart - signal upstart by putting Redis into SIGSTOP mode # supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET # supervised auto - detect upstart or systemd method based on # UPSTART_JOB or NOTIFY_SOCKET environment variables # Note: these supervision methods only signal "process is ready." # They do not enable continuous liveness pings back to your supervisor. supervised no # If a pid file is specified, Redis writes it where specified at startup # and removes it at exit. # # When the server runs non daemonized, no pid file is created if none is # specified in the configuration. When the server is daemonized, the pid file # is used even if not specified, defaulting to "/var/run/redis.pid". # # Creating a pid file is best effort: if Redis is not able to create it # nothing bad happens, the server will start and run normally. pidfile /var/run/redis/redis-6379.pid # Specify the server verbosity level. # This can be one of: # debug (a lot of information, useful for development/testing) # verbose (many rarely useful info, but not a mess like the debug level) # notice (moderately verbose, what you want in production probably) # warning (only very important / critical messages are logged) loglevel notice # Specify the log file name. Also the empty string can be used to force # Redis to log on the standard output. Note that if you use standard # output for logging but daemonize, logs will be sent to /dev/null logfile "" # To enable logging to the system logger, just set 'syslog-enabled' to yes, # and optionally update the other syslog parameters to suit your needs. # syslog-enabled no # Specify the syslog identity. # syslog-ident redis # Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. # syslog-facility local0 # Set the number of databases. The default database is DB 0, you can select # a different one on a per-connection basis using SELECT where # dbid is a number between 0 and 'databases'-1 databases 16 # By default Redis shows an ASCII art logo only when started to log to the # standard output and if the standard output is a TTY. Basically this means # that normally a logo is displayed only in interactive sessions. # # However it is possible to force the pre-4.0 behavior and always show a # ASCII art logo in startup logs by setting the following option to yes. always-show-logo yes ################################ SNAPSHOTTING ################################ # # Save the DB on disk: # # save # # Will save the DB if both the given number of seconds and the given # number of write operations against the DB occurred. # # In the example below the behaviour will be to save: # after 900 sec (15 min) if at least 1 key changed # after 300 sec (5 min) if at least 10 keys changed # after 60 sec if at least 10000 keys changed # # Note: you can disable saving completely by commenting out all "save" lines. # # It is also possible to remove all the previously configured save # points by adding a save directive with a single empty string argument # like in the following example: # # save "" save 900 1 save 300 10 save 60 10000 # By default Redis will stop accepting writes if RDB snapshots are enabled # (at least one save point) and the latest background save failed. # This will make the user aware (in a hard way) that data is not persisting # on disk properly, otherwise chances are that no one will notice and some # disaster will happen. # # If the background saving process will start working again Redis will # automatically allow writes again. # # However if you have setup your proper monitoring of the Redis server # and persistence, you may want to disable this feature so that Redis will # continue to work as usual even if there are problems with disk, # permissions, and so forth. stop-writes-on-bgsave-error yes # Compress string objects using LZF when dump .rdb databases? # For default that's set to 'yes' as it's almost always a win. # If you want to save some CPU in the saving child set it to 'no' but # the dataset will likely be bigger if you have compressible values or keys. rdbcompression yes # Since version 5 of RDB a CRC64 checksum is placed at the end of the file. # This makes the format more resistant to corruption but there is a performance # hit to pay (around 10%) when saving and loading RDB files, so you can disable it # for maximum performances. # # RDB files created with checksum disabled have a checksum of zero that will # tell the loading code to skip the check. rdbchecksum yes # The filename where to dump the DB dbfilename dump.rdb # The working directory. # # The DB will be written inside this directory, with the filename specified # above using the 'dbfilename' configuration directive. # # The Append Only File will also be created inside this directory. # # Note that you must specify a directory here, not a file name. dir ./ ################################# REPLICATION ################################# # Master-Replica replication. Use replicaof to make a Redis instance a copy of # another Redis server. A few things to understand ASAP about Redis replication. # # +------------------+ +---------------+ # | Master | ---> | Replica | # | (receive writes) | | (exact copy) | # +------------------+ +---------------+ # # 1) Redis replication is asynchronous, but you can configure a master to # stop accepting writes if it appears to be not connected with at least # a given number of replicas. # 2) Redis replicas are able to perform a partial resynchronization with the # master if the replication link is lost for a relatively small amount of # time. You may want to configure the replication backlog size (see the next # sections of this file) with a sensible value depending on your needs. # 3) Replication is automatic and does not need user intervention. After a # network partition replicas automatically try to reconnect to masters # and resynchronize with them. # # replicaof # If the master is password protected (using the "requirepass" configuration # directive below) it is possible to tell the replica to authenticate before # starting the replication synchronization process, otherwise the master will # refuse the replica request. # # masterauth # When a replica loses its connection with the master, or when the replication # is still in progress, the replica can act in two different ways: # # 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will # still reply to client requests, possibly with out of date data, or the # data set may just be empty if this is the first synchronization. # # 2) if replica-serve-stale-data is set to 'no' the replica will reply with # an error "SYNC with master in progress" to all the kind of commands # but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, # SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, # COMMAND, POST, HOST: and LATENCY. # replica-serve-stale-data yes # You can configure a replica instance to accept writes or not. Writing against # a replica instance may be useful to store some ephemeral data (because data # written on a replica will be easily deleted after resync with the master) but # may also cause problems if clients are writing to it because of a # misconfiguration. # # Since Redis 2.6 by default replicas are read-only. # # Note: read only replicas are not designed to be exposed to untrusted clients # on the internet. It's just a protection layer against misuse of the instance. # Still a read only replica exports by default all the administrative commands # such as CONFIG, DEBUG, and so forth. To a limited extent you can improve # security of read only replicas using 'rename-command' to shadow all the # administrative / dangerous commands. replica-read-only yes # Replication SYNC strategy: disk or socket. # # ------------------------------------------------------- # WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY # ------------------------------------------------------- # # New replicas and reconnecting replicas that are not able to continue the replication # process just receiving differences, need to do what is called a "full # synchronization". An RDB file is transmitted from the master to the replicas. # The transmission can happen in two different ways: # # 1) Disk-backed: The Redis master creates a new process that writes the RDB # file on disk. Later the file is transferred by the parent # process to the replicas incrementally. # 2) Diskless: The Redis master creates a new process that directly writes the # RDB file to replica sockets, without touching the disk at all. # # With disk-backed replication, while the RDB file is generated, more replicas # can be queued and served with the RDB file as soon as the current child producing # the RDB file finishes its work. With diskless replication instead once # the transfer starts, new replicas arriving will be queued and a new transfer # will start when the current one terminates. # # When diskless replication is used, the master waits a configurable amount of # time (in seconds) before starting the transfer in the hope that multiple replicas # will arrive and the transfer can be parallelized. # # With slow disks and fast (large bandwidth) networks, diskless replication # works better. repl-diskless-sync no # When diskless replication is enabled, it is possible to configure the delay # the server waits in order to spawn the child that transfers the RDB via socket # to the replicas. # # This is important since once the transfer starts, it is not possible to serve # new replicas arriving, that will be queued for the next RDB transfer, so the server # waits a delay in order to let more replicas arrive. # # The delay is specified in seconds, and by default is 5 seconds. To disable # it entirely just set it to 0 seconds and the transfer will start ASAP. repl-diskless-sync-delay 5 # Replicas send PINGs to server in a predefined interval. It's possible to change # this interval with the repl_ping_replica_period option. The default value is 10 # seconds. # # repl-ping-replica-period 10 # The following option sets the replication timeout for: # # 1) Bulk transfer I/O during SYNC, from the point of view of replica. # 2) Master timeout from the point of view of replicas (data, pings). # 3) Replica timeout from the point of view of masters (REPLCONF ACK pings). # # It is important to make sure that this value is greater than the value # specified for repl-ping-replica-period otherwise a timeout will be detected # every time there is low traffic between the master and the replica. # # repl-timeout 60 # Disable TCP_NODELAY on the replica socket after SYNC? # # If you select "yes" Redis will use a smaller number of TCP packets and # less bandwidth to send data to replicas. But this can add a delay for # the data to appear on the replica side, up to 40 milliseconds with # Linux kernels using a default configuration. # # If you select "no" the delay for data to appear on the replica side will # be reduced but more bandwidth will be used for replication. # # By default we optimize for low latency, but in very high traffic conditions # or when the master and replicas are many hops away, turning this to "yes" may # be a good idea. repl-disable-tcp-nodelay no # Set the replication backlog size. The backlog is a buffer that accumulates # replica data when replicas are disconnected for some time, so that when a replica # wants to reconnect again, often a full resync is not needed, but a partial # resync is enough, just passing the portion of data the replica missed while # disconnected. # # The bigger the replication backlog, the longer the time the replica can be # disconnected and later be able to perform a partial resynchronization. # # The backlog is only allocated once there is at least a replica connected. # # repl-backlog-size 1mb # After a master has no longer connected replicas for some time, the backlog # will be freed. The following option configures the amount of seconds that # need to elapse, starting from the time the last replica disconnected, for # the backlog buffer to be freed. # # Note that replicas never free the backlog for timeout, since they may be # promoted to masters later, and should be able to correctly "partially # resynchronize" with the replicas: hence they should always accumulate backlog. # # A value of 0 means to never release the backlog. # # repl-backlog-ttl 3600 # The replica priority is an integer number published by Redis in the INFO output. # It is used by Redis Sentinel in order to select a replica to promote into a # master if the master is no longer working correctly. # # A replica with a low priority number is considered better for promotion, so # for instance if there are three replicas with priority 10, 100, 25 Sentinel will # pick the one with priority 10, that is the lowest. # # However a special priority of 0 marks the replica as not able to perform the # role of master, so a replica with priority of 0 will never be selected by # Redis Sentinel for promotion. # # By default the priority is 100. replica-priority 100 # It is possible for a master to stop accepting writes if there are less than # N replicas connected, having a lag less or equal than M seconds. # # The N replicas need to be in "online" state. # # The lag in seconds, that must be <= the specified value, is calculated from # the last ping received from the replica, that is usually sent every second. # # This option does not GUARANTEE that N replicas will accept the write, but # will limit the window of exposure for lost writes in case not enough replicas # are available, to the specified number of seconds. # # For example to require at least 3 replicas with a lag <= 10 seconds use: # # min-replicas-to-write 3 # min-replicas-max-lag 10 # # Setting one or the other to 0 disables the feature. # # By default min-replicas-to-write is set to 0 (feature disabled) and # min-replicas-max-lag is set to 10. # A Redis master is able to list the address and port of the attached # replicas in different ways. For example the "INFO replication" section # offers this information, which is used, among other tools, by # Redis Sentinel in order to discover replica instances. # Another place where this info is available is in the output of the # "ROLE" command of a master. # # The listed IP and address normally reported by a replica is obtained # in the following way: # # IP: The address is auto detected by checking the peer address # of the socket used by the replica to connect with the master. # # Port: The port is communicated by the replica during the replication # handshake, and is normally the port that the replica is using to # listen for connections. # # However when port forwarding or Network Address Translation (NAT) is # used, the replica may be actually reachable via different IP and port # pairs. The following two options can be used by a replica in order to # report to its master a specific set of IP and port, so that both INFO # and ROLE will report those values. # # There is no need to use both the options if you need to override just # the port or the IP address. # # replica-announce-ip 5.5.5.5 # replica-announce-port 1234 ################################## SECURITY ################################### # Require clients to issue AUTH before processing any other # commands. This might be useful in environments in which you do not trust # others with access to the host running redis-server. # # This should stay commented out for backward compatibility and because most # people do not need auth (e.g. they run their own servers). #:q # Warning: since Redis is pretty fast an outside user can try up to # 150k passwords per second against a good box. This means that you should # use a very strong password otherwise it will be very easy to break. # # requirepass # Command renaming. # # It is possible to change the name of dangerous commands in a shared # environment. For instance the CONFIG command may be renamed into something # hard to guess so that it will still be available for internal-use tools # but not available for general clients. # # Example: # # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 # # It is also possible to completely kill a command by renaming it into # an empty string: # # rename-command CONFIG "" # # Please note that changing the name of commands that are logged into the # AOF file or transmitted to replicas may cause problems. ################################### CLIENTS #################################### # Set the max number of connected clients at the same time. By default # this limit is set to 10000 clients, however if the Redis server is not # able to configure the process file limit to allow for the specified limit # the max number of allowed clients is set to the current file limit # minus 32 (as Redis reserves a few file descriptors for internal uses). # # Once the limit is reached Redis will close all the new connections sending # an error 'max number of clients reached'. # # maxclients 10000 ############################## MEMORY MANAGEMENT ################################ # Set a memory usage limit to the specified amount of bytes. # When the memory limit is reached Redis will try to remove keys # according to the eviction policy selected (see maxmemory-policy). # # If Redis can't remove keys according to the policy, or if the policy is # set to 'noeviction', Redis will start to reply with errors to commands # that would use more memory, like SET, LPUSH, and so on, and will continue # to reply to read-only commands like GET. # # This option is usually useful when using Redis as an LRU or LFU cache, or to # set a hard memory limit for an instance (using the 'noeviction' policy). # # WARNING: If you have replicas attached to an instance with maxmemory on, # the size of the output buffers needed to feed the replicas are subtracted # from the used memory count, so that network problems / resyncs will # not trigger a loop where keys are evicted, and in turn the output # buffer of replicas is full with DELs of keys evicted triggering the deletion # of more keys, and so forth until the database is completely emptied. # # In short... if you have replicas attached it is suggested that you set a lower # limit for maxmemory so that there is some free RAM on the system for replica # output buffers (but this is not needed if the policy is 'noeviction'). # # maxmemory # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached. You can select among five behaviors: # # volatile-lru -> Evict using approximated LRU among the keys with an expire set. # allkeys-lru -> Evict any key using approximated LRU. # volatile-lfu -> Evict using approximated LFU among the keys with an expire set. # allkeys-lfu -> Evict any key using approximated LFU. # volatile-random -> Remove a random key among the ones with an expire set. # allkeys-random -> Remove a random key, any key. # volatile-ttl -> Remove the key with the nearest expire time (minor TTL) # noeviction -> Don't evict anything, just return an error on write operations. # # LRU means Least Recently Used # LFU means Least Frequently Used # # Both LRU, LFU and volatile-ttl are implemented using approximated # randomized algorithms. # # Note: with any of the above policies, Redis will return an error on write # operations, when there are no suitable keys for eviction. # # At the date of writing these commands are: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # # The default is: # # maxmemory-policy noeviction # LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated # algorithms (in order to save memory), so you can tune it for speed or # accuracy. For default Redis will check five keys and pick the one that was # used less recently, you can change the sample size using the following # configuration directive. # # The default of 5 produces good enough results. 10 Approximates very closely # true LRU but costs more CPU. 3 is faster but not very accurate. # # maxmemory-samples 5 # Starting from Redis 5, by default a replica will ignore its maxmemory setting # (unless it is promoted to master after a failover or manually). It means # that the eviction of keys will be just handled by the master, sending the # DEL commands to the replica as keys evict in the master side. # # This behavior ensures that masters and replicas stay consistent, and is usually # what you want, however if your replica is writable, or you want the replica to have # a different memory setting, and you are sure all the writes performed to the # replica are idempotent, then you may change this default (but be sure to understand # what you are doing). # # Note that since the replica by default does not evict, it may end using more # memory than the one set via maxmemory (there are certain buffers that may # be larger on the replica, or data structures may sometimes take more memory and so # forth). So make sure you monitor your replicas and make sure they have enough # memory to never hit a real out-of-memory condition before the master hits # the configured maxmemory setting. # # replica-ignore-maxmemory yes ############################# LAZY FREEING #################################### # Redis has two primitives to delete keys. One is called DEL and is a blocking # deletion of the object. It means that the server stops processing new commands # in order to reclaim all the memory associated with an object in a synchronous # way. If the key deleted is associated with a small object, the time needed # in order to execute the DEL command is very small and comparable to most other # O(1) or O(log_N) commands in Redis. However if the key is associated with an # aggregated value containing millions of elements, the server can block for # a long time (even seconds) in order to complete the operation. # # For the above reasons Redis also offers non blocking deletion primitives # such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and # FLUSHDB commands, in order to reclaim memory in background. Those commands # are executed in constant time. Another thread will incrementally free the # object in the background as fast as possible. # # DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled. # It's up to the design of the application to understand when it is a good # idea to use one or the other. However the Redis server sometimes has to # delete keys or flush the whole database as a side effect of other operations. # Specifically Redis deletes objects independently of a user call in the # following scenarios: # # 1) On eviction, because of the maxmemory and maxmemory policy configurations, # in order to make room for new data, without going over the specified # memory limit. # 2) Because of expire: when a key with an associated time to live (see the # EXPIRE command) must be deleted from memory. # 3) Because of a side effect of a command that stores data on a key that may # already exist. For example the RENAME command may delete the old key # content when it is replaced with another one. Similarly SUNIONSTORE # or SORT with STORE option may delete existing keys. The SET command # itself removes any old content of the specified key in order to replace # it with the specified string. # 4) During replication, when a replica performs a full resynchronization with # its master, the content of the whole database is removed in order to # load the RDB file just transferred. # # In all the above cases the default is to delete objects in a blocking way, # like if DEL was called. However you can configure each case specifically # in order to instead release memory in a non-blocking way like if UNLINK # was called, using the following configuration directives: lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no ############################## APPEND ONLY MODE ############################### # By default Redis asynchronously dumps the dataset on disk. This mode is # good enough in many applications, but an issue with the Redis process or # a power outage may result into a few minutes of writes lost (depending on # the configured save points). # # The Append Only File is an alternative persistence mode that provides # much better durability. For instance using the default data fsync policy # (see later in the config file) Redis can lose just one second of writes in a # dramatic event like a server power outage, or a single write if something # wrong with the Redis process itself happens, but the operating system is # still running correctly. # # AOF and RDB persistence can be enabled at the same time without problems. # If the AOF is enabled on startup Redis will load the AOF, that is the file # with the better durability guarantees. # # Please check http://redis.io/topics/persistence for more information. appendonly yes # The name of the append only file (default: "appendonly.aof") appendfilename "appendonly.aof" # The fsync() call tells the Operating System to actually write data on disk # instead of waiting for more data in the output buffer. Some OS will really flush # data on disk, some other OS will just try to do it ASAP. # # Redis supports three different modes: # # no: don't fsync, just let the OS flush the data when it wants. Faster. # always: fsync after every write to the append only log. Slow, Safest. # everysec: fsync only one time every second. Compromise. # # The default is "everysec", as that's usually the right compromise between # speed and data safety. It's up to you to understand if you can relax this to # "no" that will let the operating system flush the output buffer when # it wants, for better performances (but if you can live with the idea of # some data loss consider the default persistence mode that's snapshotting), # or on the contrary, use "always" that's very slow but a bit safer than # everysec. # # More details please check the following article: # http://antirez.com/post/redis-persistence-demystified.html # # If unsure, use "everysec". # appendfsync always appendfsync everysec # appendfsync no # When the AOF fsync policy is set to always or everysec, and a background # saving process (a background save or AOF log background rewriting) is # performing a lot of I/O against the disk, in some Linux configurations # Redis may block too long on the fsync() call. Note that there is no fix for # this currently, as even performing fsync in a different thread will block # our synchronous write(2) call. # # In order to mitigate this problem it's possible to use the following option # that will prevent fsync() from being called in the main process while a # BGSAVE or BGREWRITEAOF is in progress. # # This means that while another child is saving, the durability of Redis is # the same as "appendfsync none". In practical terms, this means that it is # possible to lose up to 30 seconds of log in the worst scenario (with the # default Linux settings). # # If you have latency problems turn this to "yes". Otherwise leave it as # "no" that is the safest pick from the point of view of durability. no-appendfsync-on-rewrite no # Automatic rewrite of the append only file. # Redis is able to automatically rewrite the log file implicitly calling # BGREWRITEAOF when the AOF log size grows by the specified percentage. # # This is how it works: Redis remembers the size of the AOF file after the # latest rewrite (if no rewrite has happened since the restart, the size of # the AOF at startup is used). # # This base size is compared to the current size. If the current size is # bigger than the specified percentage, the rewrite is triggered. Also # you need to specify a minimal size for the AOF file to be rewritten, this # is useful to avoid rewriting the AOF file even if the percentage increase # is reached but it is still pretty small. # # Specify a percentage of zero in order to disable the automatic AOF # rewrite feature. auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb # An AOF file may be found to be truncated at the end during the Redis # startup process, when the AOF data gets loaded back into memory. # This may happen when the system where Redis is running # crashes, especially when an ext4 filesystem is mounted without the # data=ordered option (however this can't happen when Redis itself # crashes or aborts but the operating system still works correctly). # # Redis can either exit with an error when this happens, or load as much # data as possible (the default now) and start if the AOF file is found # to be truncated at the end. The following option controls this behavior. # # If aof-load-truncated is set to yes, a truncated AOF file is loaded and # the Redis server starts emitting a log to inform the user of the event. # Otherwise if the option is set to no, the server aborts with an error # and refuses to start. When the option is set to no, the user requires # to fix the AOF file using the "redis-check-aof" utility before to restart # the server. # # Note that if the AOF file will be found to be corrupted in the middle # the server will still exit with an error. This option only applies when # Redis will try to read more data from the AOF file but not enough bytes # will be found. aof-load-truncated yes # When rewriting the AOF file, Redis is able to use an RDB preamble in the # AOF file for faster rewrites and recoveries. When this option is turned # on the rewritten AOF file is composed of two different stanzas: # # [RDB file][AOF tail] # # When loading Redis recognizes that the AOF file starts with the "REDIS" # string and loads the prefixed RDB file, and continues loading the AOF # tail. aof-use-rdb-preamble yes ################################ LUA SCRIPTING ############################### # Max execution time of a Lua script in milliseconds. # # If the maximum execution time is reached Redis will log that a script is # still in execution after the maximum allowed time and will start to # reply to queries with an error. # # When a long running script exceeds the maximum execution time only the # SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be # used to stop a script that did not yet called write commands. The second # is the only way to shut down the server in the case a write command was # already issued by the script but the user doesn't want to wait for the natural # termination of the script. # # Set it to 0 or a negative value for unlimited execution without warnings. lua-time-limit 5000 ################################ REDIS CLUSTER ############################### # # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however # in order to mark it as "mature" we need to wait for a non trivial percentage # of users to deploy it in production. # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Normal Redis instances can't be part of a Redis Cluster; only nodes that are # started as cluster nodes can. In order to start a Redis instance as a # cluster node enable the cluster support uncommenting the following: # # cluster-enabled yes # Every cluster node has a cluster configuration file. This file is not # intended to be edited by hand. It is created and updated by Redis nodes. # Every Redis Cluster node requires a different cluster configuration file. # Make sure that instances running in the same system do not have # overlapping cluster configuration file names. # # cluster-config-file nodes-6379.conf # Cluster node timeout is the amount of milliseconds a node must be unreachable # for it to be considered in failure state. # Most other internal time limits are multiple of the node timeout. # # cluster-node-timeout 15000 # A replica of a failing master will avoid to start a failover if its data # looks too old. # # There is no simple way for a replica to actually have an exact measure of # its "data age", so the following two checks are performed: # # 1) If there are multiple replicas able to failover, they exchange messages # in order to try to give an advantage to the replica with the best # replication offset (more data from the master processed). # Replicas will try to get their rank by offset, and apply to the start # of the failover a delay proportional to their rank. # # 2) Every single replica computes the time of the last interaction with # its master. This can be the last ping or command received (if the master # is still in the "connected" state), or the time that elapsed since the # disconnection with the master (if the replication link is currently down). # If the last interaction is too old, the replica will not try to failover # at all. # # The point "2" can be tuned by user. Specifically a replica will not perform # the failover if, since the last interaction with the master, the time # elapsed is greater than: # # (node-timeout * replica-validity-factor) + repl-ping-replica-period # # So for example if node-timeout is 30 seconds, and the replica-validity-factor # is 10, and assuming a default repl-ping-replica-period of 10 seconds, the # replica will not try to failover if it was not able to talk with the master # for longer than 310 seconds. # # A large replica-validity-factor may allow replicas with too old data to failover # a master, while a too small value may prevent the cluster from being able to # elect a replica at all. # # For maximum availability, it is possible to set the replica-validity-factor # to a value of 0, which means, that replicas will always try to failover the # master regardless of the last time they interacted with the master. # (However they'll always try to apply a delay proportional to their # offset rank). # # Zero is the only value able to guarantee that when all the partitions heal # the cluster will always be able to continue. # # cluster-replica-validity-factor 10 # Cluster replicas are able to migrate to orphaned masters, that are masters # that are left without working replicas. This improves the cluster ability # to resist to failures as otherwise an orphaned master can't be failed over # in case of failure if it has no working replicas. # # Replicas migrate to orphaned masters only if there are still at least a # given number of other working replicas for their old master. This number # is the "migration barrier". A migration barrier of 1 means that a replica # will migrate only if there is at least 1 other working replica for its master # and so forth. It usually reflects the number of replicas you want for every # master in your cluster. # # Default is 1 (replicas migrate only if their masters remain with at least # one replica). To disable migration just set it to a very large value. # A value of 0 can be set but is useful only for debugging and dangerous # in production. # # cluster-migration-barrier 1 # By default Redis Cluster nodes stop accepting queries if they detect there # is at least an hash slot uncovered (no available node is serving it). # This way if the cluster is partially down (for example a range of hash slots # are no longer covered) all the cluster becomes, eventually, unavailable. # It automatically returns available as soon as all the slots are covered again. # # However sometimes you want the subset of the cluster which is working, # to continue to accept queries for the part of the key space that is still # covered. In order to do so, just set the cluster-require-full-coverage # option to no. # # cluster-require-full-coverage yes # This option, when set to yes, prevents replicas from trying to failover its # master during master failures. However the master can still perform a # manual failover, if forced to do so. # # This is useful in different scenarios, especially in the case of multiple # data center operations, where we want one side to never be promoted if not # in the case of a total DC failure. # # cluster-replica-no-failover no # In order to setup your cluster make sure to read the documentation # available at http://redis.io web site. ########################## CLUSTER DOCKER/NAT support ######################## # In certain deployments, Redis Cluster nodes address discovery fails, because # addresses are NAT-ted or because ports are forwarded (the typical case is # Docker and other containers). # # In order to make Redis Cluster working in such environments, a static # configuration where each node knows its public address is needed. The # following two options are used for this scope, and are: # # * cluster-announce-ip # * cluster-announce-port # * cluster-announce-bus-port # # Each instruct the node about its address, client port, and cluster message # bus port. The information is then published in the header of the bus packets # so that other nodes will be able to correctly map the address of the node # publishing the information. # # If the above options are not used, the normal Redis Cluster auto-detection # will be used instead. # # Note that when remapped, the bus port may not be at the fixed offset of # clients port + 10000, so you can specify any port and bus-port depending # on how they get remapped. If the bus-port is not set, a fixed offset of # 10000 will be used as usually. # # Example: # # cluster-announce-ip 10.1.1.5 # cluster-announce-port 6379 # cluster-announce-bus-port 6380 ################################## SLOW LOG ################################### # The Redis Slow Log is a system to log queries that exceeded a specified # execution time. The execution time does not include the I/O operations # like talking with the client, sending the reply and so forth, # but just the time needed to actually execute the command (this is the only # stage of command execution where the thread is blocked and can not serve # other requests in the meantime). # # You can configure the slow log with two parameters: one tells Redis # what is the execution time, in microseconds, to exceed in order for the # command to get logged, and the other parameter is the length of the # slow log. When a new command is logged the oldest one is removed from the # queue of logged commands. # The following time is expressed in microseconds, so 1000000 is equivalent # to one second. Note that a negative number disables the slow log, while # a value of zero forces the logging of every command. slowlog-log-slower-than 10000 # There is no limit to this length. Just be aware that it will consume memory. # You can reclaim memory used by the slow log with SLOWLOG RESET. slowlog-max-len 128 ################################ LATENCY MONITOR ############################## # The Redis latency monitoring subsystem samples different operations # at runtime in order to collect data related to possible sources of # latency of a Redis instance. # # Via the LATENCY command this information is available to the user that can # print graphs and obtain reports. # # The system only logs operations that were performed in a time equal or # greater than the amount of milliseconds specified via the # latency-monitor-threshold configuration directive. When its value is set # to zero, the latency monitor is turned off. # # By default latency monitoring is disabled since it is mostly not needed # if you don't have latency issues, and collecting data has a performance # impact, that while very small, can be measured under big load. Latency # monitoring can easily be enabled at runtime using the command # "CONFIG SET latency-monitor-threshold " if needed. latency-monitor-threshold 0 ############################# EVENT NOTIFICATION ############################## # Redis can notify Pub/Sub clients about events happening in the key space. # This feature is documented at http://redis.io/topics/notifications # # For instance if keyspace events notification is enabled, and a client # performs a DEL operation on key "foo" stored in the Database 0, two # messages will be published via Pub/Sub: # # PUBLISH __keyspace@0__:foo del # PUBLISH __keyevent@0__:del foo # # It is possible to select the events that Redis will notify among a set # of classes. Every class is identified by a single character: # # K Keyspace events, published with __keyspace@__ prefix. # E Keyevent events, published with __keyevent@__ prefix. # g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... # $ String commands # l List commands # s Set commands # h Hash commands # z Sorted set commands # x Expired events (events generated every time a key expires) # e Evicted events (events generated when a key is evicted for maxmemory) # A Alias for g$lshzxe, so that the "AKE" string means all the events. # # The "notify-keyspace-events" takes as argument a string that is composed # of zero or multiple characters. The empty string means that notifications # are disabled. # # Example: to enable list and generic events, from the point of view of the # event name, use: # # notify-keyspace-events Elg # # Example 2: to get the stream of the expired keys subscribing to channel # name __keyevent@0__:expired use: # # notify-keyspace-events Ex # # By default all notifications are disabled because most users don't need # this feature and the feature has some overhead. Note that if you don't # specify at least one of K or E, no events will be delivered. notify-keyspace-events "" ############################### ADVANCED CONFIG ############################### # Hashes are encoded using a memory efficient data structure when they have a # small number of entries, and the biggest entry does not exceed a given # threshold. These thresholds can be configured using the following directives. hash-max-ziplist-entries 512 hash-max-ziplist-value 64 # Lists are also encoded in a special way to save a lot of space. # The number of entries allowed per internal list node can be specified # as a fixed maximum size or a maximum number of elements. # For a fixed maximum size, use -5 through -1, meaning: # -5: max size: 64 Kb <-- not recommended for normal workloads # -4: max size: 32 Kb <-- not recommended # -3: max size: 16 Kb <-- probably not recommended # -2: max size: 8 Kb <-- good # -1: max size: 4 Kb <-- good # Positive numbers mean store up to _exactly_ that number of elements # per list node. # The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), # but if your use case is unique, adjust the settings as necessary. list-max-ziplist-size -2 # Lists may also be compressed. # Compress depth is the number of quicklist ziplist nodes from *each* side of # the list to *exclude* from compression. The head and tail of the list # are always uncompressed for fast push/pop operations. Settings are: # 0: disable all list compression # 1: depth 1 means "don't start compressing until after 1 node into the list, # going from either the head or tail" # So: [head]->node->node->...->node->[tail] # [head], [tail] will always be uncompressed; inner nodes will compress. # 2: [head]->[next]->node->node->...->node->[prev]->[tail] # 2 here means: don't compress head or head->next or tail->prev or tail, # but compress all nodes between them. # 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] # etc. list-compress-depth 0 # Sets have a special encoding in just one case: when a set is composed # of just strings that happen to be integers in radix 10 in the range # of 64 bit signed integers. # The following configuration setting sets the limit in the size of the # set in order to use this special memory saving encoding. set-max-intset-entries 512 # Similarly to hashes and lists, sorted sets are also specially encoded in # order to save a lot of space. This encoding is only used when the length and # elements of a sorted set are below the following limits: zset-max-ziplist-entries 128 zset-max-ziplist-value 64 # HyperLogLog sparse representation bytes limit. The limit includes the # 16 bytes header. When an HyperLogLog using the sparse representation crosses # this limit, it is converted into the dense representation. # # A value greater than 16000 is totally useless, since at that point the # dense representation is more memory efficient. # # The suggested value is ~ 3000 in order to have the benefits of # the space efficient encoding without slowing down too much PFADD, # which is O(N) with the sparse encoding. The value can be raised to # ~ 10000 when CPU is not a concern, but space is, and the data set is # composed of many HyperLogLogs with cardinality in the 0 - 15000 range. hll-sparse-max-bytes 3000 # Streams macro node max size / items. The stream data structure is a radix # tree of big nodes that encode multiple items inside. Using this configuration # it is possible to configure how big a single node can be in bytes, and the # maximum number of items it may contain before switching to a new node when # appending new stream entries. If any of the following settings are set to # zero, the limit is ignored, so for instance it is possible to set just a # max entires limit by setting max-bytes to 0 and max-entries to the desired # value. stream-node-max-bytes 4096 stream-node-max-entries 100 # Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in # order to help rehashing the main Redis hash table (the one mapping top-level # keys to values). The hash table implementation Redis uses (see dict.c) # performs a lazy rehashing: the more operation you run into a hash table # that is rehashing, the more rehashing "steps" are performed, so if the # server is idle the rehashing is never complete and some more memory is used # by the hash table. # # The default is to use this millisecond 10 times every second in order to # actively rehash the main dictionaries, freeing memory when possible. # # If unsure: # use "activerehashing no" if you have hard latency requirements and it is # not a good thing in your environment that Redis can reply from time to time # to queries with 2 milliseconds delay. # # use "activerehashing yes" if you don't have such hard requirements but # want to free memory asap when possible. activerehashing yes # The client output buffer limits can be used to force disconnection of clients # that are not reading data from the server fast enough for some reason (a # common reason is that a Pub/Sub client can't consume messages as fast as the # publisher can produce them). # # The limit can be set differently for the three different classes of clients: # # normal -> normal clients including MONITOR clients # replica -> replica clients # pubsub -> clients subscribed to at least one pubsub channel or pattern # # The syntax of every client-output-buffer-limit directive is the following: # # client-output-buffer-limit # # A client is immediately disconnected once the hard limit is reached, or if # the soft limit is reached and remains reached for the specified number of # seconds (continuously). # So for instance if the hard limit is 32 megabytes and the soft limit is # 16 megabytes / 10 seconds, the client will get disconnected immediately # if the size of the output buffers reach 32 megabytes, but will also get # disconnected if the client reaches 16 megabytes and continuously overcomes # the limit for 10 seconds. # # By default normal clients are not limited because they don't receive data # without asking (in a push way), but just after a request, so only # asynchronous clients may create a scenario where data is requested faster # than it can read. # # Instead there is a default limit for pubsub and replica clients, since # subscribers and replicas receive data in a push fashion. # # Both the hard or the soft limit can be disabled by setting them to zero. client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 # Client query buffers accumulate new commands. They are limited to a fixed # amount by default in order to avoid that a protocol desynchronization (for # instance due to a bug in the client) will lead to unbound memory usage in # the query buffer. However you can configure it here if you have very special # needs, such us huge multi/exec requests or alike. # # client-query-buffer-limit 1gb # In the Redis protocol, bulk requests, that are, elements representing single # strings, are normally limited ot 512 mb. However you can change this limit # here. # # proto-max-bulk-len 512mb # Redis calls an internal function to perform many background tasks, like # closing connections of clients in timeout, purging expired keys that are # never requested, and so forth. # # Not all tasks are performed with the same frequency, but Redis checks for # tasks to perform according to the specified "hz" value. # # By default "hz" is set to 10. Raising the value will use more CPU when # Redis is idle, but at the same time will make Redis more responsive when # there are many keys expiring at the same time, and timeouts may be # handled with more precision. # # The range is between 1 and 500, however a value over 100 is usually not # a good idea. Most users should use the default of 10 and raise this up to # 100 only in environments where very low latency is required. hz 10 # Normally it is useful to have an HZ value which is proportional to the # number of clients connected. This is useful in order, for instance, to # avoid too many clients are processed for each background task invocation # in order to avoid latency spikes. # # Since the default HZ value by default is conservatively set to 10, Redis # offers, and enables by default, the ability to use an adaptive HZ value # which will temporary raise when there are many connected clients. # # When dynamic HZ is enabled, the actual configured HZ will be used as # as a baseline, but multiples of the configured HZ value will be actually # used as needed once more clients are connected. In this way an idle # instance will use very little CPU time while a busy instance will be # more responsive. dynamic-hz yes # When a child rewrites the AOF file, if the following option is enabled # the file will be fsync-ed every 32 MB of data generated. This is useful # in order to commit the file to the disk more incrementally and avoid # big latency spikes. aof-rewrite-incremental-fsync yes # When redis saves RDB file, if the following option is enabled # the file will be fsync-ed every 32 MB of data generated. This is useful # in order to commit the file to the disk more incrementally and avoid # big latency spikes. rdb-save-incremental-fsync yes # Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good # idea to start with the default settings and only change them after investigating # how to improve the performances and how the keys LFU change over time, which # is possible to inspect via the OBJECT FREQ command. # # There are two tunable parameters in the Redis LFU implementation: the # counter logarithm factor and the counter decay time. It is important to # understand what the two parameters mean before changing them. # # The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis # uses a probabilistic increment with logarithmic behavior. Given the value # of the old counter, when a key is accessed, the counter is incremented in # this way: # # 1. A random number R between 0 and 1 is extracted. # 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1). # 3. The counter is incremented only if R < P. # # The default lfu-log-factor is 10. This is a table of how the frequency # counter changes with a different number of accesses with different # logarithmic factors: # # +--------+------------+------------+------------+------------+------------+ # | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits | # +--------+------------+------------+------------+------------+------------+ # | 0 | 104 | 255 | 255 | 255 | 255 | # +--------+------------+------------+------------+------------+------------+ # | 1 | 18 | 49 | 255 | 255 | 255 | # +--------+------------+------------+------------+------------+------------+ # | 10 | 10 | 18 | 142 | 255 | 255 | # +--------+------------+------------+------------+------------+------------+ # | 100 | 8 | 11 | 49 | 143 | 255 | # +--------+------------+------------+------------+------------+------------+ # # NOTE: The above table was obtained by running the following commands: # # redis-benchmark -n 1000000 incr foo # redis-cli object freq foo # # NOTE 2: The counter initial value is 5 in order to give new objects a chance # to accumulate hits. # # The counter decay time is the time, in minutes, that must elapse in order # for the key counter to be divided by two (or decremented if it has a value # less <= 10). # # The default value for the lfu-decay-time is 1. A Special value of 0 means to # decay the counter every time it happens to be scanned. # # lfu-log-factor 10 # lfu-decay-time 1 ########################### ACTIVE DEFRAGMENTATION ####################### # # WARNING THIS FEATURE IS EXPERIMENTAL. However it was stress tested # even in production and manually tested by multiple engineers for some # time. # # What is active defragmentation? # ------------------------------- # # Active (online) defragmentation allows a Redis server to compact the # spaces left between small allocations and deallocations of data in memory, # thus allowing to reclaim back memory. # # Fragmentation is a natural process that happens with every allocator (but # less so with Jemalloc, fortunately) and certain workloads. Normally a server # restart is needed in order to lower the fragmentation, or at least to flush # away all the data and create it again. However thanks to this feature # implemented by Oran Agra for Redis 4.0 this process can happen at runtime # in an "hot" way, while the server is running. # # Basically when the fragmentation is over a certain level (see the # configuration options below) Redis will start to create new copies of the # values in contiguous memory regions by exploiting certain specific Jemalloc # features (in order to understand if an allocation is causing fragmentation # and to allocate it in a better place), and at the same time, will release the # old copies of the data. This process, repeated incrementally for all the keys # will cause the fragmentation to drop back to normal values. # # Important things to understand: # # 1. This feature is disabled by default, and only works if you compiled Redis # to use the copy of Jemalloc we ship with the source code of Redis. # This is the default with Linux builds. # # 2. You never need to enable this feature if you don't have fragmentation # issues. # # 3. Once you experience fragmentation, you can enable this feature when # needed with the command "CONFIG SET activedefrag yes". # # The configuration parameters are able to fine tune the behavior of the # defragmentation process. If you are not sure about what they mean it is # a good idea to leave the defaults untouched. # Enabled active defragmentation # activedefrag yes # Minimum amount of fragmentation waste to start active defrag # active-defrag-ignore-bytes 100mb # Minimum percentage of fragmentation to start active defrag # active-defrag-threshold-lower 10 # Maximum percentage of fragmentation at which we use maximum effort # active-defrag-threshold-upper 100 # Minimal effort for defrag in CPU percentage # active-defrag-cycle-min 5 # Maximal effort for defrag in CPU percentage # active-defrag-cycle-max 75 # Maximum number of set/hash/zset/list fields that will be processed from # the main dictionary scan # active-defrag-max-scan-fields 1000 ================================================ FILE: codes/linux/soft/config/redis/redis.service ================================================ [Unit] Description=Redis After=network.target [Service] Type=forking PIDFile=/var/run/redis/redis-6379.pid ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target ================================================ FILE: codes/linux/soft/config/settings-aliyun.xml ================================================ aliyun Aliyun http://maven.aliyun.com/nexus/content/groups/public central repo2 Mirror from Maven Repo2 http://repo2.maven.org/maven2/ central ================================================ FILE: codes/linux/soft/docker-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 Docker 脚本 # 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install docker begin.${RESET}\n" # uninstall old version docker sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine # install required libs sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # add docker yum repo sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum makecache fast # install docker sudo yum install docker-ce docker-ce-cli containerd.io sudo systemctl start docker docker version printf "${GREEN}<<<<<<<< install docker end.${RESET}\n" printf "${GREEN}>>>>>>>> replace chinese docker mirror registry${RESET}\n" if [[ -f "/etc/docker/daemon.json" ]]; then mv /etc/docker/daemon.json /etc/docker/daemon.json.bak else mkdir -p /etc/docker fi touch /etc/docker/daemon.json cat >> /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://hub-mirror.c.163.com"] } EOF systemctl daemon-reload systemctl restart docker ================================================ FILE: codes/linux/soft/elk/boot-elk.sh ================================================ #!/usr/bin/env bash # 检查脚本输入参数 checkInput() { if [ "${app}" == "" ] || [ "${oper}" == "" ]; then echo "请输入脚本参数:name" echo " app: 要启动的进程关键字(必填)。可选值:elasticsearch|logstash|kibana|filebeat" echo " oper: 执行操作(必填)。可选值:start|stop" echo "例:./boot-elk.sh logstash start" exit 0 fi if [ "${app}" != "elasticsearch" ] && [ "${app}" != "logstash" ] && [ "${app}" != "kibana" ] && [ "${app}" != "filebeat" ]; then echo "name 输入错误" echo "可选值:elasticsearch|logstash|kibana|filebeat" exit 0 fi } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { if [ ! -f "$1" ] then echo "关键文件 $1 找不到,脚本执行结束" exit 0 fi } startup() { if [ "${app}" == "elasticsearch" ]; then checkFileExist ${ELASTICSEARCH_BIN_PATH}/elasticsearch nohup sh ${ELASTICSEARCH_BIN_PATH}/elasticsearch >> ${ELASTICSEARCH_BIN_PATH}/nohup.out 2>&1 & elif [ "${app}" == "logstash" ]; then checkFileExist ${LOGSTASH_BIN_PATH}/logstash nohup sh ${LOGSTASH_BIN_PATH}/logstash -f ${LOGSTASH_BIN_PATH}/logstash.conf >> ${LOGSTASH_BIN_PATH}/nohup.out 2>&1 & elif [ "${app}" == "kibana" ]; then checkFileExist ${KIBANA_BIN_PATH}/kibana nohup sh ${KIBANA_BIN_PATH}/kibana >> ${KIBANA_BIN_PATH}/nohup.out 2>&1 & elif [ "${app}" == "filebeat" ]; then checkFileExist ${FILEBEAT_PATH}/filebeat touch ${FILEBEAT_PATH}/nohup.out nohup ${FILEBEAT_PATH}/filebeat -e -c ${FILEBEAT_PATH}/filebeat.yml -d "publish" >> ${FILEBEAT_PATH}/nohup.out 2>&1 & fi } shutdown() { pid=`ps -ef | grep java | grep ${app} | awk '{print $2}'` kill -9 ${pid} } ##############################__MAIN__######################################## app=$1 oper=$2 version=6.1.1 ELASTICSEARCH_BIN_PATH=/opt/elastic/elasticsearch-${version}/bin LOGSTASH_BIN_PATH=/opt/elastic/logstash-${version}/bin KIBANA_BIN_PATH=/opt/elastic/kibana-${version}-linux-x86_64/bin FILEBEAT_PATH=/opt/elastic/filebeat-${version}-linux-x86_64 checkInput case ${oper} in start) echo "启动 ${app}" startup ;; stop) echo "终止 ${app}" shutdown ;; *) echo "${oper} is invalid oper" ;; esac ================================================ FILE: codes/linux/soft/elk/config/filebeat.yml ================================================ ###################### Filebeat Configuration Example ######################### # This file is an example configuration file highlighting only the most common # options. The filebeat.reference.yml file from the same directory contains all the # supported options with more comments. You can use it as a reference. # # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/filebeat/index.html # For more available modules and options, please see the filebeat.reference.yml sample # configuration file. #=========================== Filebeat prospectors ============================= filebeat.prospectors: # Each - is a prospector. Most options can be set at the prospector level, so # you can use different prospectors for various configurations. # Below are the prospector specific configurations. - type: log # Change to true to enable this prospector configuration. enabled: true # Paths that should be crawled and fetched. Glob based paths. paths: #- /var/log/*.log #- c:\programdata\elasticsearch\logs\* - /home/zp/log/*.log # Exclude lines. A list of regular expressions to match. It drops the lines that are # matching any regular expression from the list. #exclude_lines: ['^DBG'] # Include lines. A list of regular expressions to match. It exports the lines that are # matching any regular expression from the list. #include_lines: ['^ERR', '^WARN'] # Exclude files. A list of regular expressions to match. Filebeat drops the files that # are matching any regular expression from the list. By default, no files are dropped. #exclude_files: ['.gz$'] # Optional additional fields. These fields can be freely picked # to add additional information to the crawled log files for filtering #fields: # level: debug # review: 1 ### Multiline options # Mutiline can be used for log messages spanning multiple lines. This is common # for Java Stack Traces or C-Line Continuation # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [ #multiline.pattern: ^\[ # Defines if the pattern set under pattern should be negated or not. Default is false. #multiline.negate: false # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern # that was (not) matched before or after or as long as a pattern is not matched based on negate. # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after #============================= Filebeat modules =============================== filebeat.config.modules: # Glob pattern for configuration loading path: ${path.config}/modules.d/*.yml # Set to true to enable config reloading reload.enabled: true # Period on which files under path should be checked for changes #reload.period: 10s #==================== Elasticsearch template setting ========================== setup.template.settings: index.number_of_shards: 3 #index.codec: best_compression #_source.enabled: false #================================ General ===================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. name: 127.0.0.1 # The tags of the shipper are included in their own field with each # transaction published. #tags: ["service-X", "web-tier"] # Optional fields that you can specify to add additional information to the # output. fields: profile: development #============================== Dashboards ===================================== # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. setup.dashboards.enabled: true # The URL from where to download the dashboards archive. By default this URL # has a value which is computed based on the Beat name and version. For released # versions, this URL points to the dashboard archive on the artifacts.elastic.co # website. #setup.dashboards.url: #============================== Kibana ===================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. setup.kibana: # Kibana Host # Scheme and port can be left out and will be set to the default (http and 5601) # In case you specify and additional path, the scheme is required: http://localhost:5601/path # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 host: "192.168.28.11:5601" #============================= Elastic Cloud ================================== # These settings simplify using filebeat with the Elastic Cloud (https://cloud.elastic.co/). # The cloud.id setting overwrites the `output.elasticsearch.hosts` and # `setup.kibana.host` options. # You can find the `cloud.id` in the Elastic Cloud web UI. #cloud.id: # The cloud.auth setting overwrites the `output.elasticsearch.username` and # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: #================================ Outputs ===================================== # Configure what output to use when sending the data collected by the beat. #-------------------------- Elasticsearch output ------------------------------ #output.elasticsearch: # Array of hosts to connect to. #hosts: ["192.168.28.11:9200"] # Optional protocol and basic auth credentials. protocol: "http" #username: "elastic" #password: "changeme" #----------------------------- Logstash output -------------------------------- output.logstash: # The Logstash hosts hosts: ["192.168.28.32:5044"] # Optional SSL. By default is off. # List of root certificates for HTTPS server verifications #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] # Certificate for SSL client authentication #ssl.certificate: "/etc/pki/client/cert.pem" # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" #================================ Logging ===================================== # Sets log level. The default log level is info. # Available log levels are: critical, error, warning, info, debug #logging.level: debug # At debug level, you can selectively enable logging only for some components. # To enable all selectors use ["*"]. Examples of other selectors are "beat", # "publish", "service". logging.selectors: ["*"] ================================================ FILE: codes/linux/soft/elk/config/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n ${user.dir}/logs/${FILE_NAME}-all.%d{yyyy-MM-dd}.log 30 30MB %d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n 192.168.28.32:9251 {"appname":"javatool"} ================================================ FILE: codes/linux/soft/elk/config/logstash.conf ================================================ input { tcp { port => 9251 codec => json_lines mode => server tags => ["javaapp"] } } output { elasticsearch { hosts => ["localhost:9200"] } stdout { codec => rubydebug } } ================================================ FILE: codes/linux/soft/elk/install-elk.sh ================================================ #!/usr/bin/env bash # 本脚本为一键式安装 ELK 脚本 # 执行脚本前,请先执行以下命令,创建用户 # groupadd elk # useradd -g elk elk # passwd elk # 获取当前设备IP ipaddr='127.0.0.1' function getDeviceIp() { ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') } # 检查文件是否存在,不存在则退出脚本 checkFileExist() { if [ ! -f "$1" ] then echo "关键文件 $1 找不到,脚本执行结束" exit 0 fi } init() { mkdir -p ${ELASTIC_SOFTWARE_PATH} getDeviceIp } # 安装 elasticsearch installElasticsearch() { cd ${ELASTIC_SOFTWARE_PATH} wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${version}.tar.gz tar -xzf elasticsearch-${version}.tar.gz } installRuby() { cd ${RUBY_SOFTWARE_PATH} wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz tar -xzf ruby-2.5.0.tar.gz cd ruby-2.5.0 ./configure make & make install } # 安装 logstash installLogstash() { cd ${ELASTIC_SOFTWARE_PATH} wget https://artifacts.elastic.co/downloads/logstash/logstash-${version}.tar.gz tar -xzf logstash-${version}.tar.gz } # 安装 kibana installKibana() { cd ${ELASTIC_SOFTWARE_PATH} wget https://artifacts.elastic.co/downloads/kibana/kibana-${version}-linux-x86_64.tar.gz tar -xzf kibana-${version}-linux-x86_64.tar.gz } # 安装 filebeat installFilebeat() { cd ${ELASTIC_SOFTWARE_PATH} wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${version}-linux-x86_64.tar.gz tar -zxf filebeat-${version}-linux-x86_64.tar.gz } # 替换 Elasticsearch 配置 # 1. 替换 192.168.0.1 为本机 IP replaceElasticsearchConfig() { cp ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml.bak sed -i "s/#network.host: 192.168.0.1/network.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/config/elasticsearch.yml touch ${ELASTIC_SOFTWARE_PATH}/elasticsearch-${version}/bin/nohup.out } replaceLogstashConfig() { cp ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml.bak sed -i "s/# http.host: \"127.0.0.1\"/ http.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/config/logstash.yml touch ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin/nohup.out cd ${ELASTIC_SOFTWARE_PATH}/logstash-${version}/bin wget "https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/elk/config/logstash.conf" } # 替换 Kibana 配置 # 1. 替换 localhost 为本机 IP replaceKibanaConfig() { cp ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml.bak sed -i "s/#server.host: \"localhost\"/server.host: ${IP}/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml sed -i "s/#elasticsearch.url: \"http://localhost:9200\"/#elasticsearch.url: \"${IP}\"/g" ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/config/kibana.yml touch ${ELASTIC_SOFTWARE_PATH}/kibana-${version}-linux-x86_64/bin/nohup.out } # 替换 Filebeat 配置 replaceFilebeatConfig() { cp ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml.bak cd ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64 wget https://github.com/dunwu/OS/blob/master/codes/deploy/tool/elk/config/filebeat.yml sed -i 's/127.0.0.1/'"${IP}"'/g' ${ELASTIC_SOFTWARE_PATH}/filebeat-${version}-linux-x86_64/filebeat.yml } # 为 elk.elk 用户设置权限 setPrivilegeForUser() { chown -R elk.elk ${ELASTIC_SOFTWARE_PATH} chown -R elk.elk /var/log/ } ######################################## MAIN ######################################## echo -e "\n>>>>>>>>> install elk" version=6.1.1 RUBY_SOFTWARE_PATH=/opt/ruby ELASTIC_SOFTWARE_PATH=/opt/elastic ELASTIC_SETTINGS_PATH=/opt/elastic/settings init installElasticsearch replaceElasticsearchConfig installLogstash replaceLogstashConfig installKibana replaceKibanaConfig installFilebeat replaceFilebeatConfig # 最后,将启动脚本下载到本地 mkdir -p /home/zp/script wget -P /home/zp/script "https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/elk/boot-elk.sh" #setPrivilegeForUser ================================================ FILE: codes/linux/soft/elk/install_elasticserch.sh ================================================ #!/usr/bin/env bash # auth:kaliarch # version:v1.0 # func:elasticsearch 5.4.1/6.0.1/6.3.1安装 # 定义安装目录、及日志信息 . /etc/init.d/functions [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin download_path=/tmp/tmpdir/ install_log_name=install_elasticsearch.log env_file=/etc/profile.d/elasticsearch.sh install_log_path=/var/log/appinstall/ install_path=/usr/local/ software_config_file=${install_path}elasticsearch/config/elasticsearch.yml sysversion=$(rpm -q centos-release | cut -d- -f3) jvm_conf="/usr/local/elasticsearch/config/jvm.options" sys_mem=`free -m | grep Mem: | awk '{print $2}' | awk '{sum+=$1} END {print sum/1024}' | cut -d. -f1` hostname=elk-server clear echo "##########################################" echo "# #" echo "# 安装 elasticsearch 5.4.1/6.0.1/6.3.1 #" echo "# #" echo "##########################################" echo "1: Install elasticsearch 5.4.1" echo "2: Install elasticsearch 6.0.1" echo "3: Install elasticsearch 6.3.1" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-5.4.1.tar.gz" elif [ "${softversion}" == "2" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.0.1.tar.gz" elif [ "${softversion}" == "3" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/elasticsearch/elasticsearch-6.3.1.tar.gz" elif [ "${softversion}" == "4" ]; then echo "you choce channel!" exit 1; else echo "input Error! Place input{1|2|3|4|5}" exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { for msg in $*; do action $msg /bin/true done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { output_msg "命令检查:$1" hash $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 else yum -y install $2 > /dev/null 2>&1 # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" for dirname in $*; do [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" mkdir -p $download_path for file in $*; do wget $file -c -P $download_path &> /dev/null if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { output_msg "解压源码" for file in $*; do if [ "${file##*.}" == "gz" ]; then tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} elif [ "${file##*.}" == "zip" ]; then unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" echo "export PATH=\$PATH:$1" > ${env_file} source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 配置主机名,第一个为主机名 config_hostname() { if [ ${sysversion} -eq 6 ]; then hostname $1 elif [ ${sysversion} -eq 7 ]; then hostnamectl set-hostname $1 else echo "`date +%F' '%H:%M:%S` hostname $1 config fail" >> ${install_log_path}${install_log_name} fi } config_limits() { output_msg "配置limits" cat >> /etc/security/limits.conf << EOF * soft nofile 65536 * hard nofile 65536 * soft nproc 65536 * hard nproc 65536 EOF echo "vm.max_map_count = 655360" >> /etc/sysctl.conf sysctl -p > /dev/null 2>&1 } # 添加配置文件 add_config() { cat > $1 << EOF cluster.name: my-application node.name: ${hostname} path.data: /usr/local/elasticsearch/data path.logs: /usr/local/elasticsearch/logs network.host: 127.0.0.1 http.port: 9200 discovery.zen.ping.unicast.hosts: ["$hostname"] EOF } config_user() { useradd $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` $1 user add success" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%S` $1 user add fail" >> ${install_log_path}${install_log_name} && exit 1 fi chown ${1}.${1} ${install_path}elasticsearch/ -R } config_jvm() { if [ ${sys_mem} -eq 0 ]; then sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} else sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} fi } main() { check_dir $install_log_path $install_path check_yum_command wget wget download_file $URL config_hostname $hostname software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') for filename in `ls $download_path`; do extract_file ${download_path}$filename done rm -fr ${download_path} ln -s $install_path$software_name ${install_path}elasticsearch add_config $software_config_file check_dir ${install_path}elasticsearch/{data,logs} config_user elasticsearch config_env ${install_path}elasticsearch/bin config_limits config_jvm echo "请使用一下命令启动服务:'su - elasticsearch -c 'nohup /usr/local/elasticsearch/bin/elasticsearch &'" } main ================================================ FILE: codes/linux/soft/elk/install_filebeat.sh ================================================ #!/usr/bin/env bash # auth:kaliarch # version:v1.0 # func:filebeat 5.6.1/6.1.3/6.3.2 安装 # 定义安装目录、及日志信息 . /etc/init.d/functions [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin download_path=/tmp/tmpdir/ install_log_name=install_filebeat.log env_file=/etc/profile.d/filebeat.sh install_log_path=/var/log/appinstall/ install_path=/usr/local/ software_config_file=${install_path}filebeat/filebeat.yml clear echo "##########################################" echo "# #" echo "# 安装 filebeat 5.6.1/6.1.3/6.3.2 #" echo "# #" echo "##########################################" echo "1: Install filebeat 5.6.1" echo "2: Install filebeat 6.1.3" echo "3: Install filebeat 6.3.2" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-5.6.1-linux-x86_64.tar.gz" elif [ "${softversion}" == "2" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.1.3-linux-x86_64.tar.gz" elif [ "${softversion}" == "3" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/filebeat/filebeat-6.3.2-linux-x86_64.tar.gz" elif [ "${softversion}" == "4" ]; then echo "you choce channel!" exit 1; else echo "input Error! Place input{1|2|3|4}" exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { for msg in $*; do action $msg /bin/true done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { output_msg "命令检查:$1" hash $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 else yum -y install $2 > /dev/null 2>&1 # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" for dirname in $*; do [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" mkdir -p $download_path for file in $*; do wget $file -c -P $download_path &> /dev/null if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { output_msg "解压源码" for file in $*; do if [ "${file##*.}" == "gz" ]; then tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} elif [ "${file##*.}" == "zip" ]; then unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" echo "export PATH=\$PATH:$1" > ${env_file} source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { cat > $1 << EOF filebeat.prospectors: - input_type: log paths: - /var/log/*.log output.logstash: hosts: ["127.0.0.1:5044"] EOF } main() { check_dir $install_log_path $install_path check_yum_command wget wget download_file $URL software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') for filename in `ls $download_path`; do extract_file ${download_path}$filename done rm -fr ${download_path} ln -s $install_path$software_name ${install_path}filebeat add_config ${software_config_file} } main ================================================ FILE: codes/linux/soft/elk/install_kibana.sh ================================================ #!/usr/bin/env bash # auth:kaliarch # version:v1.0 # func:kibana 6.0.1/6.2.4/6.3.1 安装 # 定义安装目录、及日志信息 . /etc/init.d/functions [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin download_path=/tmp/tmpdir/ install_log_name=install_kibana.log env_file=/etc/profile.d/kibana.sh install_log_path=/var/log/appinstall/ install_path=/usr/local/ software_config_file=${install_path}kibana/config/kibana.yml clear echo "##########################################" echo "# #" echo "# 安装 kibana 6.0.1/6.2.4/6.3.1 #" echo "# #" echo "##########################################" echo "1: Install kibana 6.0.1" echo "2: Install kibana 6.2.4" echo "3: Install kibana 6.3.1" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.0.1-linux-x86_64.tar.gz" elif [ "${softversion}" == "2" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.2.4-linux-x86_64.tar.gz" elif [ "${softversion}" == "3" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/kibana/kibana-6.3.1-linux-x86_64.tar.gz" elif [ "${softversion}" == "4" ]; then echo "you choce channel!" exit 1; else echo "input Error! Place input{1|2|3|4}" exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { for msg in $*; do action $msg /bin/true done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { output_msg "命令检查:$1" hash $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 else yum -y install $2 > /dev/null 2>&1 # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" for dirname in $*; do [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" mkdir -p $download_path for file in $*; do wget $file -c -P $download_path &> /dev/null if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { output_msg "解压源码" for file in $*; do if [ "${file##*.}" == "gz" ]; then tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} elif [ "${file##*.}" == "zip" ]; then unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" echo "export PATH=\$PATH:$1" > ${env_file} source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { cat > $1 << EOF server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "http://127.0.0.1:9200" EOF } main() { check_dir $install_log_path $install_path check_yum_command wget wget download_file $URL software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') for filename in `ls $download_path`; do extract_file ${download_path}$filename done rm -fr ${download_path} ln -s ${install_path}$software_name ${install_path}kibana add_config ${software_config_file} config_env ${install_path}kibana/bin } main ================================================ FILE: codes/linux/soft/elk/install_logstash.sh ================================================ #!/usr/bin/env bash # auth:kaliarch # version:v1.0 # func:logstash 5.4/6.1/6.3 安装 # 定义安装目录、及日志信息 . /etc/init.d/functions [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin download_path=/tmp/tmpdir/ install_log_name=install_logstash.log env_file=/etc/profile.d/logstash.sh install_log_path=/var/log/appinstall/ install_path=/usr/local/ software_config_file=${install_path}logstash/config/01-syslog.conf clear echo "##########################################" echo "# #" echo "# 安装 logstash 5.4/6.1/6.3 #" echo "# #" echo "##########################################" echo "1: Install logstash-5.4" echo "2: Install logstash-6.1" echo "3: Install logstash-6.3" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-5.4.1.tar.gz" elif [ "${softversion}" == "2" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.1.3.tar.gz" elif [ "${softversion}" == "3" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/logstash/logstash-6.3.2.tar.gz" elif [ "${softversion}" == "4" ]; then echo "you choce channel!" exit 1; else echo "input Error! Place input{1|2|3|4}" exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { for msg in $*; do action $msg /bin/true done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { output_msg "命令检查:$1" hash $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 else yum -y install $2 > /dev/null 2>&1 # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" for dirname in $*; do [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" mkdir -p $download_path for file in $*; do wget $file -c -P $download_path &> /dev/null if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 解压文件,可以传入多个压缩文件绝对路径,用空格隔开,解压至安装目录 extract_file() { output_msg "解压源码" for file in $*; do if [ "${file##*.}" == "gz" ]; then tar -zxf $file -C $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} elif [ "${file##*.}" == "zip" ]; then unzip -q $file -d $install_path && echo "`date +%F' '%H:%M:%S` $file extrac success!,path is $install_path" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%S` $file type error, extrac fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 配置环境变量,第一个参数为添加环境变量的绝对路径 config_env() { output_msg "环境变量配置" echo "export PATH=\$PATH:$1" > ${env_file} source ${env_file} && echo "`date +%F' '%H:%M:%S` 软件安装完成!" >> ${install_log_path}${install_log_name} } # 添加配置文件 add_config() { cat > $1 << EOF input { beats { port => "5044" } } output { elasticsearch { hosts => "127.0.0.1:9200" } stdout { codec => rubydebug } } EOF } main() { check_dir $install_log_path $install_path check_yum_command wget wget download_file $URL software_name=$(echo $URL | awk -F'/' '{print $NF}' | awk -F'.tar.gz' '{print $1}') for filename in `ls $download_path`; do extract_file ${download_path}$filename done rm -fr ${download_path} ln -s $install_path$software_name ${install_path}logstash add_config ${software_config_file} config_env ${install_path}logstash/bin } main ================================================ FILE: codes/linux/soft/fastdfs-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # install FastDFS 脚本 # FastDFS 会被install到 /opt/fdfs 路径。 # @system: 适用于 CentOS # @author: Zhang Peng # @ses: https://github.com/happyfish100/fastdfs/wiki ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install fastdfs begin.${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } command -v git > /dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; exit 1; } if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh fastdfs-install.sh [path]\n" printf "\t Example: sh fastdfs-install.sh /opt/fastdfs\n" printf "${RESET}\n" fi path=/opt/fdfs if [[ -n $1 ]]; then path=$1 fi nginx_version=1.16.0 nginx_path=/opt/nginx printf "${GREEN}>>>>>>>> install required libs.${RESET}\n\n" yum install -y git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vim unzip # download and decompression mkdir -p ${path} path=/opt/fdfs mkdir -p ${path}/libfastcommon curl -o ${path}/libfastcommon.zip http://dunwu.test.upcdn.net/soft/fdfs/libfastcommon.zip if [[ ! -f ${path}/libfastcommon.zip ]]; then printf "${RED}[Error]install libfastcommon failed,exit. ${RESET}\n" exit 1 fi unzip -o ${path}/libfastcommon.zip -d ${path} cd ${path}/libfastcommon chmod +x -R ${path}/libfastcommon/*.sh ./make.sh && ./make.sh install printf "${GREEN}>>>>>>>>> install fastdfs${RESET}" mkdir -p ${path}/fastdfs curl -o ${path}/fastdfs.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs.zip if [[ ! -f ${path}/fastdfs.zip ]]; then printf "${RED}>>>>>>>>> install fastdfs failed,exit. ${RESET}\n" fi unzip -o ${path}/fastdfs.zip -d ${path} cd ${path}/fastdfs chmod +x -R ${path}/fastdfs/*.sh ./make.sh && ./make.sh install printf "${GREEN}>>>>>>>>> install fastdfs-nginx-module${RESET}\n" mkdir -p ${path}/fastdfs-nginx-module curl -o ${path}/fastdfs-nginx-module.zip http://dunwu.test.upcdn.net/soft/fdfs/fastdfs-nginx-module.zip if [[ ! -f ${path}/fastdfs-nginx-module.zip ]]; then printf "${RED}>>>>>>>>> install fastdfs-nginx-module failed,exit. ${RESET}\n" fi unzip -o ${path}/fastdfs-nginx-module.zip -d ${path} printf "${GREEN}>>>>>>>>> install nginx${RESET}" mkdir -p ${nginx_path} curl -o ${nginx_path}/nginx-${nginx_version}.tar.gz http://nginx.org/download/nginx-${nginx_version}.tar.gz tar zxf ${nginx_path}/nginx-${nginx_version}.tar.gz -C ${nginx_path} cd ${nginx_path}/nginx-${nginx_version} ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-pcre --add-module=${path}/fastdfs-nginx-module/src/ make && make install printf "${GREEN}>>>>>>>>> fastdfs 配置文件准备${RESET}\n" # 配置修改参考:https://github.com/happyfish100/fastdfs/wiki mkdir -p /etc/fdfs cp ${path}/fastdfs/conf/http.conf /etc/fdfs/ #供nginx访问使用 cp ${path}/fastdfs/conf/mime.types /etc/fdfs/ #供nginx访问使用 cp ${path}/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/fastdfs/tracker.conf -O /etc/fdfs/tracker.conf wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/fastdfs/storage.conf -O /etc/fdfs/storage.conf wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/fastdfs/client.conf -O /etc/fdfs/client.conf # fdfs 存储路径和服务端口(以默认形式配置) fdfs_store_path=/home/fdfs fdfs_server_port=7001 ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') mkdir -p ${fdfs_store_path} printf "${GREEN} - 修改 tracker.conf 配置${RESET}\n" sed -i "s#^base_path=.*#base_path=${fdfs_store_path}#g" /etc/fdfs/tracker.conf printf "${GREEN} - 修改 storage.conf 配置${RESET}\n" sed -i "s#^base_path=.*#base_path=${fdfs_store_path}#g" /etc/fdfs/storage.conf sed -i "s#^store_path0=.*#store_path0=${fdfs_store_path}#g" /etc/fdfs/storage.conf sed -i "s#^tracker_server=.*#tracker_server=${ip}:22122#g" /etc/fdfs/storage.conf sed -i "s#^http.server_port=.*#http.server_port=${fdfs_server_port}#g" /etc/fdfs/storage.conf printf "${GREEN} - 修改 client.conf 配置${RESET}\n" sed -i "s#^base_path=.*#base_path=${fdfs_store_path}#g" /etc/fdfs/client.conf sed -i "s#^tracker_server=.*#tracker_server=${ip}:22122#g" /etc/fdfs/client.conf printf "${GREEN} - 修改 mod_fastdfs.conf 配置${RESET}\n" sed -i "s#^url_have_group_name=.*#url_have_group_name=true#g" /etc/fdfs/mod_fastdfs.conf sed -i "s#^tracker_server=.*#tracker_server=${ip}:22122#g" /etc/fdfs/mod_fastdfs.conf sed -i "s#^store_path0=.*#store_path0=${fdfs_store_path}#g" /etc/fdfs/mod_fastdfs.conf printf "${GREEN} - 修改 nginx.conf 配置${RESET}\n" mkdir -p /usr/local/nginx/conf/conf wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/nginx/nginx.conf -O /usr/local/nginx/conf/nginx.conf wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/nginx/conf/fdfs.conf -O /usr/local/nginx/conf/conf/fdfs.conf printf "${GREEN}>>>>>>>>> 启动 fastdfs ${RESET}\n" chmod +x /etc/init.d/fdfs_trackerd /etc/init.d/fdfs_trackerd start #启动tracker服务 #/etc/init.d/fdfs_trackerd restart #重启动tracker服务 #/etc/init.d/fdfs_trackerd stop #停止tracker服务 chkconfig fdfs_trackerd on #自启动tracker服务 chmod +x /etc/init.d/fdfs_storaged /etc/init.d/fdfs_storaged start #启动storage服务 #/etc/init.d/fdfs_storaged restart #重动storage服务 #/etc/init.d/fdfs_storaged stop #停止动storage服务 chkconfig fdfs_storaged on #自启动storage服务 wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/nginx/nginx.service -O /usr/lib/systemd/system/nginx.service chmod +x /usr/lib/systemd/system/nginx.service #设置nginx.service为系统服务 systemctl enable nginx.service ##通过系统服务操作nginx systemctl start nginx.service #systemctl reload nginx.service #systemctl restart nginx.service #systemctl stop nginx.service printf ">>>>>>>>> add fastdfs port" firewall-cmd --zone=public --add-port=${fdfs_server_port}/tcp --permanent firewall-cmd --zone=public --add-port=22122/tcp --permanent firewall-cmd --reload printf "${GREEN}<<<<<<<< install fastdfs end.${RESET}\n" #touch test.txt #result=`fdfs_upload_file /etc/fdfs/client.conf test.txt` #echo ${result} #rm -f test.txt ================================================ FILE: codes/linux/soft/gitlab/gitlab-backup.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Gitlab 操作脚本 # 支持操作: # 备份 Gitlab # 恢复 Gitlab # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Gilab 操作的环境变量,使用方法: # 可以在执行本脚本之前,export 以下环境变量,否则将按照默认配置执行 # Gitlab 备份文件最大数量(默认为 7 天) export ENV_BACKUP_MAX_NUM=7 # 备份路径 export ENV_GITLAB_BACKUP_DIR="/var/opt/gitlab/backups" # 备份日志路径 export ENV_LOG_PATH="${ENV_GITLAB_BACKUP_DIR}/gitlab-backup.log" ENV_REMOTE_USER=root ENV_REMOTE_HOST=172.22.6.42 # ------------------------------------------------------------------------------ load libs GIT_SCRIPTS_DIR=$(cd `dirname $0`; pwd) if [[ ! -x ${GIT_SCRIPTS_DIR}/gitlab.sh ]]; then echo "${GIT_SCRIPTS_DIR}/gitlab.sh not exists!" exit 1 fi source ${GIT_SCRIPTS_DIR}/gitlab.sh # ------------------------------------------------------------------------------ functions backupGitlab if [[ "$?" != ${ENV_SUCCEED} ]]; then printError "退出" exit ${ENV_FAILED} fi #将备份文件传输到gitlab备份服务器 BACKUP_FILE=$(find "${ENV_GITLAB_BACKUP_DIR}" -type f -name "*gitlab_backup.tar" | xargs ls -t | head -1) scp ${BACKUP_FILE} ${ENV_REMOTE_USER}@${ENV_REMOTE_HOST}:${ENV_GITLAB_BACKUP_DIR} ================================================ FILE: codes/linux/soft/gitlab/gitlab.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Gitlab 操作脚本 # 支持操作: # 备份 Gitlab # 恢复 Gitlab # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then printInfo "$@" return ${ENV_SUCCEED} else printError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ env # Gilab 操作的环境变量,使用方法: # 可以在执行本脚本之前,export 以下环境变量,否则将按照默认配置执行 # Gitlab 备份文件最大数量(默认为 7 天) ENV_BACKUP_MAX_NUM=${ENV_BACKUP_MAX_NUM:-2} # 备份路径 ENV_GITLAB_BACKUP_DIR="${ENV_GITLAB_BACKUP_DIR:-/var/opt/gitlab/backups}" # 备份日志路径 ENV_LOG_PATH="${ENV_GITLAB_BACKUP_DIR}/gitlab-backup.log" magentaOutput "------------------------------------------------------------------------------" magentaOutput "Gitlab 脚本操作环境变量:" magentaOutput "ENV_BACKUP_MAX_NUM:${ENV_BACKUP_MAX_NUM}" magentaOutput "ENV_GITLAB_BACKUP_DIR:${ENV_GITLAB_BACKUP_DIR}" magentaOutput "ENV_LOG_PATH:${ENV_LOG_PATH}" magentaOutput "------------------------------------------------------------------------------" # ------------------------------------------------------------------------------ functions # 安装 Gitlab installGitlab() { # 官方安裝參考:https://about.gitlab.com/install/#centos-7 printInfo ">>>> install gitlab on Centos7" printInfo ">>>> Install and configure the necessary dependencies" yum install -y curl policycoreutils-python openssh-server systemctl enable sshd systemctl start sshd printInfo ">>>> open http, https and ssh access in the system firewall" sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo systemctl reload firewalld printInfo ">>>> install postfix" yum install postfix systemctl enable postfix systemctl start postfix printInfo ">>>> Add the GitLab package repository and install the package" curl -o- https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash EXTERNAL_URL="http://gitlab.transwarp.io" yum install -y gitlab-ce } # 备份 Gitlab backupGitlab() { #时间戳 local beginTime=$(date +'%Y-%m-%d %H:%M:%S') #备份所有数据库 printInfo ">>>> 备份 Gitlab 开始" gitlab-rake gitlab:backup:create >> ${ENV_LOG_PATH}; #检查备份结果是否成功 if [[ "$?" != ${ENV_SUCCEED} ]]; then printError "<<<< 备份 Gitlab 失败" return ${ENV_FAILED} fi # 压缩备份sql文件,删除旧的备份文件 cd "${ENV_GITLAB_BACKUP_DIR}" #只保存期限内的备份文件,其余删除 find "${ENV_GITLAB_BACKUP_DIR} -name *_gitlab_backup.tar -type f -mtime +${ENV_BACKUP_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1 local endTime=$(date +'%Y-%m-%d %H:%M:%S') local beginSeconds=$(date --date="${beginTime}" +%s) local endSeconds=$(date --date="${endTime}" +%s) printInfo "本次备份执行时间:$((endSeconds-beginSeconds)) s" printInfo "<<<< 备份 Gitlab 成功\n" return ${ENV_SUCCEED} } # 恢复 Mysql recoveryGitlab() { local version=$1 if [[ !version ]]; then printError "<<<< 未指定恢复版本" return ${ENV_FAILED} fi #创建备份目录及日志文件 mkdir -p ${ENV_GITLAB_BACKUP_DIR} if [[ ! -f ${ENV_LOG_PATH} ]]; then touch ${ENV_LOG_PATH} fi printInfo ">>>> 恢复 Gitlab 开始" gitlab-ctl stop unicorn gitlab-ctl stop sidekiq gitlab-rake gitlab:backup:restore BACKUP=${version} if [[ "$?" != 0 ]]; then printError "<<<< 恢复 Gitlab 失败" return ${ENV_FAILED} fi printInfo "<<<< 恢复 Gitlab 成功\n" return ${ENV_SUCCEED} } ================================================ FILE: codes/linux/soft/gitlab-install.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Gitlab 安装脚本 # 仅适用于 CentOS7 发行版本 # 官方安裝參考:https://about.gitlab.com/install/#centos-7 # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then printInfo "$@" return ${ENV_SUCCEED} else printError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ main # 官方安裝參考:https://about.gitlab.com/install/#centos-7 printInfo ">>>> install gitlab on Centos7" printInfo ">>>> Install and configure the necessary dependencies" yum install -y curl policycoreutils-python openssh-server systemctl enable sshd systemctl start sshd printInfo ">>>> open http, https and ssh access in the system firewall" sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo systemctl reload firewalld printInfo ">>>> install postfix" yum install postfix systemctl enable postfix systemctl start postfix printInfo ">>>> Add the GitLab package repository and install the package" curl -o- https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash EXTERNAL_URL="http://gitlab.transwarp.io" yum install -y gitlab-ce ================================================ FILE: codes/linux/soft/install_grafana.sh ================================================ #!/usr/bin/env bash # auth:kaliarch # version:v1.0 # func:grafana 5.1.0/5.1.5/5.2.2 安装 # 定义安装目录、及日志信息 . /etc/init.d/functions [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin download_path=/tmp/tmpdir/ install_log_name=install_grafana.log env_file=/etc/profile.d/grafana.sh install_log_path=/var/log/appinstall/ install_path=/usr/local/ clear echo "##########################################" echo "# #" echo "# 安装 grafana 5.1.0/5.1.5/5.2.2 #" echo "# #" echo "##########################################" echo "1: Install grafana 5.1.0" echo "2: Install grafana 5.1.5" echo "3: Install grafana 5.2.2" echo "4: EXIT" # 选择安装软件版本 read -p "Please input your choice:" softversion if [ "${softversion}" == "1" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.0-1.x86_64.rpm" elif [ "${softversion}" == "2" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.1.5-1.x86_64.rpm" elif [ "${softversion}" == "3" ]; then URL="https://anchnet-script.oss-cn-shanghai.aliyuncs.com/grafana/grafana-5.2.2-1.x86_64.rpm" elif [ "${softversion}" == "4" ]; then echo "you choce channel!" exit 1; else echo "input Error! Place input{1|2|3|4}" exit 0; fi # 传入内容,格式化内容输出,可以传入多个参数,用空格隔开 output_msg() { for msg in $*; do action $msg /bin/true done } # 判断命令是否存在,第一个参数 $1 为判断的命令,第二个参数为提供该命令的yum 软件包名称 check_yum_command() { output_msg "命令检查:$1" hash $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` check command $1 " >> ${install_log_path}${install_log_name} && return 0 else yum -y install $2 > /dev/null 2>&1 # hash $Command || { echo "`date +%F' '%H:%M:%S` $2 is installed fail">>${install_log_path}${install_log_name} ; exit 1 } fi } # 判断目录是否存在,传入目录绝对路径,可以传入多个目录 check_dir() { output_msg "目录检查" for dirname in $*; do [ -d $dirname ] || mkdir -p $dirname > /dev/null 2>&1 echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${install_log_path}${install_log_name} done } # 下载文件并解压至安装目录,传入url链接地址 download_file() { output_msg "下载源码包" mkdir -p $download_path for file in $*; do wget $file -c -P $download_path &> /dev/null if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` $file download success!" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%s` $file download fail!" >> ${install_log_path}${install_log_name} && exit 1 fi done } # 安装grafana插件,传入安装的插件的名称 install_grafana_plugins() { output_msg "grafana插件安装" check_yum_command grafana-cli grafana-cli plugins install $* > /dev/null if [ $? -eq 0 ]; then echo "`date +%F' '%H:%M:%S` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} else echo "`date +%F' '%H:%M:%s` grafana plugins $* install success!" >> ${install_log_path}${install_log_name} && exit 1 fi } main() { check_dir $install_log_path $install_path check_yum_command wget wget download_file $URL for filename in `ls $download_path`; do yum -y install $download_path$filename > /dev/null 2>&1 done install_grafana_plugins alexanderzobnin-zabbix-app } main ================================================ FILE: codes/linux/soft/jdk8-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 JDK8 脚本 # JDK 会被安装到 /usr/lib/jvm/java 路径。 # @system: 适用于 CentOS # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install jdk8 begin.${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } yum -y install java-1.8.0-openjdk-devel.x86_64 java -version printf "${GREEN}<<<<<<<< install jdk8 end.${RESET}\n" ================================================ FILE: codes/linux/soft/jenkins-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 安装 Jenkins 脚本 # 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### echo -e "\n>>>>>>>>> install jenkins" # 下载并解压 jenkins mkdir -p /opt/jenkins curl -o /opt/jenkins/jenkins.war http://mirrors.jenkins.io/war-stable/latest/jenkins.war ================================================ FILE: codes/linux/soft/kafka-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 Kafka 脚本 # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install kafka begin.${RESET}\n" command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; exit 1; } if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh kafka-install.sh [version] [path]\n" printf "\t Example: sh kafka-install.sh 2.2.0 /opt/kafka\n" printf "${RESET}\n" fi version=2.2.0 if [[ -n $1 ]]; then version=$1 fi path=/opt/kafka if [[ -n $2 ]]; then path=$2 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "\t path = ${path}\n" printf "${RESET}\n" # download and decompression mkdir -p ${path} curl -o ${path}/kafka_2.12-${version}.tgz http://mirrors.tuna.tsinghua.edu.cn/apache/kafka/${version}/kafka_2.12-${version}.tgz tar zxf ${path}/kafka_2.12-${version}.tgz -C ${path} printf "${GREEN}<<<<<<<< install kafka end.${RESET}\n" ================================================ FILE: codes/linux/soft/lib/docker.sh ================================================ #!/usr/bin/env bash LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh dockerBuild() { if [[ ! $1 ]] || [[ ! $2 ]] || [[ ! $3 ]]; then logError "you must input following params in order:" echo -e "${ENV_COLOR_B_RED}" echo " (1) source" echo " (2) repository" echo " (3) tag" echo -e "\nEg. dockerBuild /home/workspace tdh60dev01:5000/fide/fide-processor fide-0.0.6-SNAPSHOT" echo -e "${ENV_COLOR_RESET}" return ${ENV_FAILED} fi local source=$1 local repository=$2 local tag=$3 dockerCheck ${source} if [[ "${ENV_SUCCEED}" != "$?" ]]; then return ${ENV_FAILED} fi cd ${source} callAndLog docker build -t ${repository}:${tag} . if [[ "${ENV_SUCCEED}" != "$?" ]]; then logError "docker build -t ${repository}:${tag} failed" return ${ENV_FAILED} fi cd - } dockerPush() { if [[ ! $1 ]] || [[ ! $2 ]]; then logError "you must input following params in order:" echo -e "${ENV_COLOR_B_RED}" echo " (1) repository" echo " (2) tag" echo -e "\nEg. dockerBuild tdh60dev01:5000/fide/fide-processor fide-0.0.6-SNAPSHOT" echo -e "${ENV_COLOR_RESET}" return ${ENV_FAILED} fi local repository=$1 local tag=$2 local dockerHashId=$(docker image ls | grep ${repository} | grep ${tag} | awk '{print $3}') if [[ ! ${dockerHashId} ]]; then logInfo "try to delete existed image: ${repository}:${tag}" callAndLog docker rmi ${dockerHashId} fi logInfo "try to push new image: ${repository}:${tag}" callAndLog docker push ${repository}:${tag} } # check Dockerfile # @param $1: project path dockerCheck() { local source=$1 if [[ -d "${source}" ]]; then cd ${source} if [[ -f "${source}/Dockerfile" ]]; then return ${ENV_YES} else logError "Dockerfile is not exists" return ${ENV_NO} fi cd - return ${ENV_YES} else logError "${source} is not valid docker project" return ${ENV_NO} fi } ================================================ FILE: codes/linux/soft/lib/file.sh ================================================ #!/usr/bin/env bash # 装载其它库 ROOT=`dirname ${BASH_SOURCE[0]}` source ${ROOT}/utils.sh # ------------------------------------------------------------------------------ 文件操作函数 # 文件是否存在 isFileExists() { if [[ -e $1 ]]; then return ${YES} else return ${NO} fi } isFile() { if [[ -f $1 ]]; then return ${YES} else return ${NO} fi } isDirectory() { if [[ -d $1 ]]; then return ${YES} else return ${NO} fi } isFileReadable() { if [[ -r $1 ]]; then return ${YES} else return ${NO} fi } isFileWritable() { if [[ -w $1 ]]; then return ${YES} else return ${NO} fi } isFileExecutable() { if [[ -x $1 ]]; then return ${YES} else return ${NO} fi } # 检查文件夹是否存在,不存在则创建 createFolderIfNotExist() { if [ ! -d "$1" ]; then mkdir -p "$1" fi } # 重建目录,如果目录已存在,则删除后重建;如果不存在,直接新建 recreateDir() { if [[ ! $1 ]]; then printf "${C_B_RED}<<<< Please input dir path.${C_RESET}\n" return ${FAILED} fi rm -rf $1 mkdir -p $1 isDirectory $1 if [[ "$?" != "${SUCCEED}" ]]; then printf "${C_B_RED}<<<< create $1 failed.${C_RESET}\n" return ${FAILED} fi return ${SUCCEED} } ================================================ FILE: codes/linux/soft/lib/git.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Shell Utils # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color export ENV_COLOR_U_BLACK="\033[4;30m" export ENV_COLOR_U_RED="\033[4;31m" export ENV_COLOR_U_GREEN="\033[4;32m" export ENV_COLOR_U_YELLOW="\033[4;33m" export ENV_COLOR_U_BLUE="\033[4;34m" export ENV_COLOR_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_CYAN="\033[4;36m" export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color export ENV_COLOR_BG_BLACK="\033[40m" export ENV_COLOR_BG_RED="\033[41m" export ENV_COLOR_BG_GREEN="\033[42m" export ENV_COLOR_BG_YELLOW="\033[43m" export ENV_COLOR_BG_BLUE="\033[44m" export ENV_COLOR_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_CYAN="\033[46m" export ENV_COLOR_BG_WHITE="\033[47m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ util functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) # 日志路径 LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} # 日志目录 LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { if [[ ! -x "${LOG_PATH}" ]]; then mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then logInfo "$@" return ${ENV_SUCCEED} else logError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ git functions getGitLocalBranch() { export GIT_LOCAL_BRANCH=$(git symbolic-ref -q --short HEAD) } getGitOriginBranch() { export GIT_ORIGIN_BRANCH=$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}") } # check specified path is git project or not IS_GIT=false checkGit() { local source=$1 if [[ -d "${source}" ]]; then cd ${source} # (1) delete gitstatus.tmp if [[ -f "gitstatus.tmp" ]]; then rm -rf gitstatus.tmp fi # (2) check git status git status &> gitstatus.tmp local gitStatus=false grep -iwq 'not a git repository' gitstatus.tmp && gitStatus=false || gitStatus=true rm -rf gitstatus.tmp if [[ ${gitStatus} == true ]]; then export IS_GIT=true return fi fi logWarn "${source} is not exists." export IS_GIT=false } # execute git clone or fetch # params: Git repository, git group, git project, git branch, local path cloneOrPullGit() { local repository=$1 local group=$2 local project=$3 local branch=$4 local root=$5 if [[ ! ${repository} || ! ${group} || ! ${project} || ! ${branch} || ! ${root} ]]; then logError "Please input root, group, project, branch." return ${ENV_FAILED} fi if [[ ! -d "${root}" ]]; then logError "${root} is not directory." return ${ENV_FAILED} fi local source=${root}/${group}/${project} logInfo "project directory is ${source}." logInfo "git url is ${repository}:${group}/${project}.git." checkGit ${source} if [[ "${IS_GIT}" == "true" ]]; then cd ${source} || return ${ENV_FAILED} git checkout ${branch} logInfo "git checkout ${branch} succeed." git fetch --all git reset --hard ${GIT_ORIGIN_BRANCH} logInfo "git reset --hard ${GIT_ORIGIN_BRANCH} succeed." git pull logInfo "git pull succeed." else git clone "${repository}:${group}/${project}.git" ${source} logInfo "git clone ${project} succeed." cd ${source} || return ${ENV_FAILED} git checkout ${branch} logInfo "git checkout ${branch} succeed." fi logInfo "Clone or pull git project [$2/$3:$4] succeed." cd ${SOURCE_DIR} return ${ENV_SUCCEED} } ================================================ FILE: codes/linux/soft/lib/java.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # Java 应用运维脚本 # @author Zhang Peng # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ env preparation # load libs CURRENT_PATH=`dirname ${BASH_SOURCE[0]}` source ${CURRENT_PATH}/utils.sh # ------------------------------------------------------------------------------ functions stopServer() { if [[ ! $1 ]]; then printError "please input java app name" return ${FAILED} fi local javaAppName=$1 local pid=`jps | grep ${javaAppName} | awk '{print $1}'` if [[ -n "${pid}" ]]; then kill -9 ${pid} if [[ $? -eq ${SUCCEED} ]]; then printInfo "stop ${javaAppName} succeed" return ${SUCCEED} else printError "stop ${javaAppName} failed" return ${FAILED} fi else printWarn "${javaAppName} is not running" return ${SUCCEED} fi } startServer() { if [[ ! $1 ]]; then printError "please input java app name" return ${FAILED} fi # >>>> 1. check java app is started or not # >>>> 1.1. exit script if the app is started local javaAppName=$1 local pid=`jps | grep ${javaAppName} | awk '{print $1}'` if [[ -n "${pid}" ]]; then printInfo "${javaAppName} is started, PID: ${pid}" return ${SUCCEED} fi # >>>> 2. package options # GC OPTS local javaOptions="-server -Xms1g -Xmx2g -Xss256k" javaOptions="${javaOptions} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=4" # GC LOG OPTS javaOptions="${javaOptions} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" javaOptions="${javaOptions} -verbose:gc -Xloggc:${LOG_PATH}/${javaAppName}.gc.log" javaOptions="${javaOptions} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" # Heap Dump OPTS javaOptions="${javaOptions} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" javaOptions="${javaOptions} -XX:HeapDumpPath=${LOG_PATH}/${javaAppName}.heapdump.hprof" # APP OPTS javaOptions="${javaOptions} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8" if [[ ${PROFILE} ]]; then javaOptions="${javaOptions} -Dspring.profiles.active=${PROFILE}" fi # DEBUG OPTS if [[ "${DEBUG}" == "on" ]]; then # JMX OPTS local ip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/') local jmxPort=$(expr 10000 + ${PORT}) javaOptions="${javaOptions} -Dcom.sun.management.jmxremote=true" javaOptions="${javaOptions} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" javaOptions="${javaOptions} -Djava.rmi.server.hostname=${ip} -Dcom.sun.management.jmxremote.port=${jmxPort}" # Remote Debug local debugPort=$(expr 20000 + ${PORT}) javaOptions="${javaOptions} -Xdebug -Xnoagent -Djava.compiler=NONE" javaOptions="${javaOptions} -Xrunjdwp:transport=dt_socket,address=${debugPort},server=y,suspend=n" fi # CLASSPATH local appOptions="-classpath ${ROOT_PATH}/lib/* -Dlogging.config=file:${ROOT_PATH}/config/logback.dev.xml" appOptions="${appOptions} --spring.config.location=classpath:/,classpath:/config/,file:${ROOT_PATH},file:${ROOT_PATH}/config/" if [[ ${PORT} ]]; then appOptions="${appOptions} --server.port=${PORT}" fi # >>>> 3. create log dir and console log file mkdir -p ${LOG_PATH} if [[ ! -x ${CONSOLE_LOG} ]]; then touch ${CONSOLE_LOG} fi # >>>> 4. start java app printInfo "starting ${javaAppName}, execute cli: " printInfo "nohup java ${javaOptions} -jar ${ROOT_PATH}/${javaAppName}.jar ${appOptions} >> ${CONSOLE_LOG} 2>&1 &" nohup java ${javaOptions} -jar ${ROOT_PATH}/${javaAppName}.jar ${appOptions} >> ${CONSOLE_LOG} 2>&1 & # >>>> 5. check java app is started or not local pid=`jps | grep ${javaAppName} | awk '{print $1}'` if [[ -n "${pid}" ]]; then printInfo "start ${javaAppName} succeed, PID: ${pid}" return ${SUCCEED} else printError "start ${javaAppName} failed" return ${FAILED} fi } # ------------------------------------------------------------------------------ main export LANG="zh_CN.UTF-8" ROOT_PATH=$(cd ${CURRENT_PATH}/..; pwd) APP_NAME=java-app LOG_PATH=/var/log/myapp CONSOLE_LOG=${LOG_PATH}/${APP_NAME}.console.log PORT=8888 PROFILE=dev DEBUG=off startServer ${APP_NAME} #stopServer ${APP_NAME} if [[ $? -eq ${SUCCEED} ]]; then exit ${SUCCEED} else exit ${FAILED} fi ================================================ FILE: codes/linux/soft/lib/maven.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # maven operation utils # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ load libs LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/utils.sh ]]; then echo "${LINUX_SCRIPTS_LIB_DIR}/utils.sh not exists!" exit 1 fi source ${LINUX_SCRIPTS_LIB_DIR}/utils.sh # ------------------------------------------------------------------------------ functions # execute maven lifecycle operation # @param $1: maven project path # @param $2: maven lifecycle, eg. package、install、deploy # @param $3: maven profile [optional] mavenOperation() { local source=$1 local lifecycle=$2 local profile=$3 mavenCheck ${source} if [[ "${ENV_SUCCEED}" != "$?" ]]; then return ${ENV_FAILED} fi if [[ ! "${lifecycle}" ]]; then logError "please input maven lifecycle" return ${ENV_FAILED} fi local mvnCli="mvn clean ${lifecycle} -DskipTests=true -B -U" if [[ ${profile} ]]; then mvnCli="${mvnCli} -P${profile}" fi cd ${source} if [[ -f "${source}/settings.xml" ]]; then mvnCli="${mvnCli} -s ${source}/settings.xml" fi callAndLog "${mvnCli}" cd - return ${ENV_SUCCEED} } # check specified path is maven project or not # @param $1: maven project path mavenCheck() { local source=$1 if [[ -d "${source}" ]]; then cd ${source} if [[ -f "${source}/pom.xml" ]]; then return ${ENV_YES} else logError "pom.xml is not exists" return ${ENV_NO} fi cd - return ${ENV_YES} else logError "please input valid maven project path" return ${ENV_NO} fi } ================================================ FILE: codes/linux/soft/lib/mysql.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # 数据库操作脚本 # 支持操作: # 备份 Mysql # 恢复 Mysql # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then printInfo "$@" return ${ENV_SUCCEED} else printError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ env # Mysql 操作的环境变量,使用方法: # 可以在执行本脚本之前,export 以下环境变量,否则将按照默认配置执行 # Mysql HOST(默认为 127.0.0.1) ENV_MYSQL_HOST="${ENV_MYSQL_HOST:-127.0.0.1}" # Mysql 端口(默认为 3306) ENV_MYSQL_PORT=${ENV_MYSQL_PORT:-3306} # Mysql 用户名(默认为 root) ENV_MYSQL_USERNAME=${ENV_MYSQL_USERNAME:-root} # Mysql 密码(默认为 root) ENV_MYSQL_PASSWORD=${ENV_MYSQL_PASSWORD:-root} # Mysql 备份文件最大数量(默认为 7 天) ENV_BACKUP_MAX_NUM=${ENV_BACKUP_MAX_NUM:-7} # 备份模式:备份所有数据库(--all-databases)|备份指定数据库列表 ENV_MYSQL_DATABASES="${ENV_MYSQL_DATABASES:---all-databases}" # 备份路径 ENV_MYSQL_BACKUP_DIR="${ENV_MYSQL_BACKUP_DIR:-/var/lib/mysql/backup}" # 备份日志路径 ENV_MYSQL_BACKUP_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" magentaOutput "------------------------------------------------------------------------------" magentaOutput "Mysql 脚本操作环境变量:" magentaOutput "ENV_MYSQL_HOST:${ENV_MYSQL_HOST}" magentaOutput "ENV_MYSQL_PORT:${ENV_MYSQL_PORT}" magentaOutput "ENV_MYSQL_USERNAME:${ENV_MYSQL_USERNAME}" magentaOutput "ENV_MYSQL_PASSWORD:${ENV_MYSQL_PASSWORD}" magentaOutput "ENV_BACKUP_MAX_NUM:${ENV_BACKUP_MAX_NUM}" magentaOutput "ENV_MYSQL_DATABASES:${ENV_MYSQL_DATABASES}" magentaOutput "ENV_MYSQL_BACKUP_DIR:${ENV_MYSQL_BACKUP_DIR}" magentaOutput "ENV_MYSQL_BACKUP_LOG_PATH:${ENV_MYSQL_BACKUP_LOG_PATH}" magentaOutput "------------------------------------------------------------------------------" # ------------------------------------------------------------------------------ functions # 备份所有 database(schema) backupAllDatabase() { #时间戳 local timestamp=$(date +"%Y%m%d") #备份所有数据库 printInfo ">>>> 备份所有数据库开始" mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} --all-databases > "${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql" 2>> ${ENV_MYSQL_BACKUP_LOG_PATH}; #检查备份结果是否成功 if [[ "$?" != ${ENV_SUCCEED} ]]; then printError "<<<< 备份所有数据库失败" return ${ENV_FAILED} fi # 压缩备份sql文件,删除旧的备份文件 cd "${ENV_MYSQL_BACKUP_DIR}" if [[ ! -f "${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql" ]]; then printError "备份文件 ${ENV_MYSQL_BACKUP_DIR}/all-${timestamp}.sql 不存在" return ${ENV_FAILED} fi #为节约硬盘空间,将数据库压缩 tar zcf "all-${timestamp}.tar.gz" "all-${timestamp}.sql" > /dev/null #删除原始文件,只留压缩后文件 rm -f "all-${timestamp}.sql" #只保存期限内的备份文件,其余删除 find "${ENV_MYSQL_BACKUP_DIR} -name all-*.tar.gz -type f -mtime +${ENV_BACKUP_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1 printInfo "<<<< 备份所有数据库成功\n" return ${ENV_SUCCEED} } # 备份指定的 database(schema) backupSelectedDatabase() { #时间戳 local timestamp=$(date +"%Y%m%d") #数据库,如有多个库用空格分开 databaseList="${ENV_MYSQL_DATABASES}" #备份指定数据库列表 printInfo ">>>> 备份指定数据库开始" for database in ${databaseList}; do printInfo "正在备份数据库:${database}" mysqldump -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} "${database}" > "${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" 2>> ${ENV_MYSQL_BACKUP_LOG_PATH}; if [[ "$?" != 0 ]]; then printError "<<<< 备份 ${database} 失败" return ${ENV_FAILED} fi # 压缩备份sql文件,删除旧的备份文件 cd "${ENV_MYSQL_BACKUP_DIR}" if [[ ! -f "${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql" ]]; then printError "备份文件 ${ENV_MYSQL_BACKUP_DIR}/${database}-${timestamp}.sql 不存在" return ${ENV_FAILED} fi #为节约硬盘空间,将数据库压缩 tar zcf "${database}-${timestamp}.tar.gz" "${database}-${timestamp}.sql" > /dev/null #删除原始文件,只留压缩后文件 rm -f "${database}-${timestamp}.sql" #只保存期限内的备份文件,其余删除 find "${ENV_MYSQL_BACKUP_DIR} -name ${database}-*.tar.gz -type f -mtime +${ENV_BACKUP_MAX_NUM} -exec rm -rf {} \;" > /dev/null 2>&1 done printInfo "<<<< 备份数据库 ${ENV_MYSQL_DATABASES} 成功\n" return ${ENV_SUCCEED} } # 备份 Mysql backupMysql() { #创建备份目录及日志文件 mkdir -p ${ENV_MYSQL_BACKUP_DIR} if [[ ! -f ${ENV_MYSQL_BACKUP_LOG_PATH} ]]; then touch ${ENV_MYSQL_BACKUP_LOG_PATH} fi #正式备份数据库 if [[ ${ENV_MYSQL_DATABASES} == "--all-databases" ]]; then backupAllDatabase else backupSelectedDatabase fi } # 恢复 Mysql recoveryMysql() { #创建备份目录及日志文件 mkdir -p ${ENV_MYSQL_BACKUP_DIR} if [[ ! -f ${ENV_MYSQL_BACKUP_LOG_PATH} ]]; then touch ${ENV_MYSQL_BACKUP_LOG_PATH} fi printInfo ">>>> 恢复数据库开始" mysql -h ${ENV_MYSQL_HOST} -P${ENV_MYSQL_PORT} -u${ENV_MYSQL_USERNAME} -p${ENV_MYSQL_PASSWORD} < ${ENV_MYSQL_BACKUP_LOG_PATH} if [[ "$?" != 0 ]]; then printError "<<<< 恢复数据库失败" return ${ENV_FAILED} fi printInfo "<<<< 恢复数据库成功\n" return ${ENV_SUCCEED} } ================================================ FILE: codes/linux/soft/lib/net.sh ================================================ #!/usr/bin/env bash # ---------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # ---------------------------------------------------------------------------------- printf "${PURPLE}" cat << EOF # ---------------------------------------------------------------------------------- # XXX 脚本 # @author: Zhang Peng # ---------------------------------------------------------------------------------- EOF printf "${RESET}" printf "${BLUE}>>>>>>>> begin.\n${RESET}" printf "${GREEN}[OK]\n${RESET}" printf "${RED}[ERROR]\n${RESET}" printf "${BLUE}<<<<<<<< end.\n${RESET}" IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/'` ================================================ FILE: codes/linux/soft/lib/nodejs.sh ================================================ #!/usr/bin/env bash # 装载其它库 ROOT=`dirname ${BASH_SOURCE[0]}` source ${ROOT}/file.sh # ------------------------------------------------------------------------------ nodejs 操作函数 # install Node Version Manager(nvm) installNvm() { local nvmVersion=0.35.2 if [[ $1 ]]; then local nvmVersion=$1 fi recreateDir "~/.nvm" curl -o- https://raw.githubusercontent.com/creationix/nvm/v${nvmVersion}/install.sh | bash source ~/.nvm/nvm.sh if [[ "$?" != "${YES}" ]]; then return ${FAILED} fi # Check nvm version if [[ "$?" != "${YES}" ]]; then return ${FAILED} fi return ${SUCCEED} } # Check nodejs version checkNodejsVersion() { if [[ ! $1 ]]; then printf "${C_B_RED}<<<< please specified expect nodejs version.${C_RESET}\n" return ${FAILED} fi local expectVersion=$1 source /root/.bashrc local nodeVersion=$(nvm version) if [[ "$?" != "${YES}" ]]; then printf "${C_B_YELLOW}>>>> nvm not installed.${C_RESET}\n" local nvmVersion=v0.35.2 installNvm "${nvmVersion}" if [[ "$?" != "${SUCCEED}" ]]; then return ${FAILED} fi nodeVersion=$(nvm version) fi if [[ "${nodeVersion}" != "v${expectVersion}" ]]; then printf "${C_B_YELLOW}>>>> current nodejs version is ${nodeVersion}, not ${expectVersion}.${C_RESET}\n" nvm install ${expectVersion} nvm use ${expectVersion} fi return ${SUCCEED} } # build nodejs project buildNodejsProject() { if [[ ! $1 ]]; then printf "${C_B_RED}<<<< please input nodejs project path.${C_RESET}\n" return ${FAILED} fi if [[ ! $2 ]]; then printf "${C_B_RED}<<<< please input nodejs version.${C_RESET}\n" return ${FAILED} fi isDirectory $1 if [[ "$?" != "${YES}" ]]; then printf "${C_B_RED}<<<< $1 is not valid path.${C_RESET}\n" return ${FAILED} fi local project=$1 local nodeVersion=$2 printf "${C_B_BLUE}>>>> build nodejs project $1 begin.${C_RESET}\n" cd ${project} || (printf "${C_B_RED}<<<< ${project} is not exists.${C_RESET}\n" && exit 1) checkNodejsVersion ${nodeVersion} npm install if [[ "$?" != "${YES}" ]]; then printf "${C_B_RED}<<<< update dependencies failed.${C_RESET}\n" return ${FAILED} else printf "${C_B_GREEN}>>>> update dependencies succeed.${C_RESET}\n" fi npm run build if [[ "$?" != "${YES}" ]]; then printf "${C_B_RED}<<<< build failed.${C_RESET}\n" return ${FAILED} else printf "${C_B_GREEN}<<<< build succeed.${C_RESET}\n" fi return ${SUCCEED} } # package nodejs artifact dir (default is dist) packageDist() { zip -o -r -q ${branch}.zip * } ================================================ FILE: codes/linux/soft/lib/string.sh ================================================ #!/usr/bin/env bash strIsEmpty() { if [[ -z $1 ]]; then return ${YES} else return ${NO} fi } strIsNotEmpty() { if [[ -n $1 ]]; then return ${YES} else return ${NO} fi } strIsBlank() { if [[ ! $1 ]]; then return ${YES} else return ${NO} fi } strIsNotBlank() { if [[ $1 ]]; then return ${YES} else return ${NO} fi } strEquals() { if [[ "$1" = "$2" ]]; then return ${YES} else return ${NO} fi } strStartWith() { if [[ "$1" == "$2*" ]]; then return ${YES} else return ${NO} fi } ================================================ FILE: codes/linux/soft/lib/utils.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Shell Utils # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Underline Color export ENV_COLOR_U_BLACK="\033[4;30m" export ENV_COLOR_U_RED="\033[4;31m" export ENV_COLOR_U_GREEN="\033[4;32m" export ENV_COLOR_U_YELLOW="\033[4;33m" export ENV_COLOR_U_BLUE="\033[4;34m" export ENV_COLOR_U_MAGENTA="\033[4;35m" export ENV_COLOR_U_CYAN="\033[4;36m" export ENV_COLOR_U_WHITE="\033[4;37m" # Background Color export ENV_COLOR_BG_BLACK="\033[40m" export ENV_COLOR_BG_RED="\033[41m" export ENV_COLOR_BG_GREEN="\033[42m" export ENV_COLOR_BG_YELLOW="\033[43m" export ENV_COLOR_BG_BLUE="\033[44m" export ENV_COLOR_BG_MAGENTA="\033[45m" export ENV_COLOR_BG_CYAN="\033[46m" export ENV_COLOR_BG_WHITE="\033[47m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) # 日志路径 LOG_PATH=${ENV_LOG_PATH:-/var/log/shell.log} # 日志目录 LOG_DIR=${LOG_PATH%/*} createLogFileIfNotExists() { if [[ ! -x "${LOG_PATH}" ]]; then mkdir -p "${LOG_DIR}" touch "${LOG_PATH}" fi } redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } logInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [INFO] [$0] $@" >> "${LOG_PATH}" } logWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [WARN] [$0] $@" >> "${LOG_PATH}" } logError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" createLogFileIfNotExists echo "[${SHELL_LOG_TIMESTAMP}] [${USER}] [ERROR] [$0] $@" >> "${LOG_PATH}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then logInfo "$@" return ${ENV_SUCCEED} else logError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } ================================================ FILE: codes/linux/soft/maven-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 Maven3 脚本 # Maven 会被安装到 /opt/maven 路径。 # @system: 适用于所有 linux 发行版本。 # 注意:Maven 要求必须先安装 JDK # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install maven begin.${RESET}\n" command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; exit 1; } if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh maven-install.sh [version] [path]\n" printf "\t Example: sh maven-install.sh 3.6.0 /opt/maven\n" printf "${RESET}\n" fi version=3.5.4 if [[ -n $1 ]]; then version=$1 fi path=/opt/maven if [[ -n $2 ]]; then path=$2 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "\t path = ${path}\n" printf "${RESET}\n" # download and decompression mkdir -p ${path} wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -O ${path}/apache-maven-${version}-bin.tar.gz http://apache.01link.hk/maven/maven-3/${version}/binaries/apache-maven-${version}-bin.tar.gz tar -zxvf ${path}/apache-maven-${version}-bin.tar.gz -C ${path} # setting env path=${path}/apache-maven-${version} cat >> /etc/profile << EOF export MAVEN_HOME=${path} export PATH=\$MAVEN_HOME/bin:\$PATH EOF source /etc/profile # replace mirrors in settings.xml echo -e "\n>>>>>>>>> replace ${path}/conf/settings.xml" cp ${path}/conf/settings.xml ${path}/conf/settings.xml.bak wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/settings-aliyun.xml -O ${path}/conf/settings.xml printf "${GREEN}<<<<<<<< install maven end.${RESET}\n" mvn -v ================================================ FILE: codes/linux/soft/mongodb-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 mongodb 脚本 # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install mongodb begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh mongodb-install.sh [version] [path]\n" printf "\t Example: sh mongodb-install.sh 4.0.9 /opt/mongodb\n" printf "${RESET}\n" fi version=4.0.9 if [[ -n $1 ]]; then version=$1 fi path=/opt/mongodb if [[ -n $2 ]]; then path=$2 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "\t path = ${path}\n" printf "${RESET}\n" # download and decompression mkdir -p ${path} curl -o ${path}/mongodb-linux-x86_64-${version}.tgz https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-${version}.tgz tar zxf ${path}/mongodb-linux-x86_64-${version}.tgz -C ${path} mkdir -p /data/db printf "${GREEN}<<<<<<<< install mongodb end.${RESET}\n" ================================================ FILE: codes/linux/soft/mysql-backup.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # Mysql 备份脚本 # 可以通过 crond 服务,设置称为定时执行脚本: # (1)执行 crontab -e 编辑定时执行任务,如:59 23 * * * /home/scripts/mysql-backup.sh # (2)vi /etc/crontab,编辑 crontab 文件后保存,可以通过 crontab -l 查看 # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Mysql Host export ENV_MYSQL_HOST="127.0.0.1" # Mysql 端口 export ENV_MYSQL_PORT=3306 # Mysql 用户名 export ENV_MYSQL_USERNAME=root # Mysql 密码 export ENV_MYSQL_PASSWORD=root # Mysql 备份文件最大数量 export ENV_BACKUP_MAX_NUM=7 # 备份模式:备份所有数据库(--all-databases)|备份指定数据库列表 export ENV_MYSQL_DATABASES=--all-databases # 备份路径 export ENV_MYSQL_BACKUP_DIR=/var/lib/mysql/backup # 备份日志路径 export ENV_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" # ------------------------------------------------------------------------------ libs LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh ]]; then echo "${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh not exists!" exit 1 fi source ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh # ------------------------------------------------------------------------------ main # 执行备份方法 backupMysql ================================================ FILE: codes/linux/soft/mysql-install.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # 安装 Mysql 脚本 # 仅适用于 CentOS7 发行版本 # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then printInfo "$@" return ${ENV_SUCCEED} else printError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ main printInfo ">>>> install mysql begin" command -v wget > /dev/null 2>&1 || { printError "Require wget but it's not installed" exit 1; } command -v rpm > /dev/null 2>&1 || { printError "Require rpm but it's not installed" exit 1; } command -v yum > /dev/null 2>&1 || { printError "Require yum but it's not installed" exit 1; } printInfo ">>>> install mysql by rpm" wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm sudo rpm -Uvh mysql80-community-release-el7-3.noarch.rpm sudo yum install mysql-community-server printInfo ">>>> modify my.cnf" cp /etc/my.cnf /etc/my.cnf.bak wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/mysql/my.cnf -O /etc/my.cnf printInfo ">>>> create mysql log file" mkdir -p /var/log/mysql touch /var/log/mysql/mysql.log touch /var/log/mysql/mysql_slow_query_log.log chmod 777 /var/log/mysql/mysql.log chmod 777 /var/log/mysql/mysql_slow_query_log.log chown -R mysql:mysql /var/log/mysql printInfo ">>>> modify limits.conf" cat >> /etc/security/limits.conf << EOF mysql soft nofile 65536 mysql hard nofile 65536 EOF printInfo ">>>> start mysqld" systemctl enable mysqld systemctl start mysqld systemctl daemon-reload printInfo ">>>> 管理员密码如下,请登录 mysql 后重置新密码:" password=$(grep "password" /var/log/mysql/mysql.log | awk '{print $NF}') blueOutput "${password}" printInfo "<<<< install mysql success" ================================================ FILE: codes/linux/soft/mysql-recovery.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # MYSQL 恢复脚本 # @author Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Mysql Host export ENV_MYSQL_HOST="127.0.0.1" # Mysql 端口 export ENV_MYSQL_PORT=3306 # Mysql 用户名 export ENV_MYSQL_USERNAME=root # Mysql 密码 export ENV_MYSQL_PASSWORD=root # Mysql 备份文件最大数量 export ENV_BACKUP_MAX_NUM=7 # 备份模式:备份所有数据库(--all-databases)|备份指定数据库列表 export ENV_MYSQL_DATABASES=--all-databases # 备份路径 export ENV_MYSQL_BACKUP_DIR=/var/lib/mysql/backup # 备份日志路径 export ENV_LOG_PATH="${ENV_MYSQL_BACKUP_DIR}/mysql-backup.log" # ------------------------------------------------------------------------------ libs LINUX_SCRIPTS_LIB_DIR=`dirname ${BASH_SOURCE[0]}` if [[ ! -x ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh ]]; then echo "${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh not exists!" exit 1 fi source ${LINUX_SCRIPTS_LIB_DIR}/lib/mysql.sh # ------------------------------------------------------------------------------ main # 执行备份方法 recoveryMysql ================================================ FILE: codes/linux/soft/nacos-install.sh ================================================ #!/usr/bin/env bash cat << EOF ################################################################################### # 安装 nacos 脚本 # 需要提前安装 jdk、maven # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF command -v java > /dev/null 2>&1 || { printf "${RED}Require java but it's not installed.${RESET}\n"; exit 1; } command -v mvn > /dev/null 2>&1 || { printf "${RED}Require mvn but it's not installed.${RESET}\n"; exit 1; } if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then echo "Usage: sh nacos-install.sh [version] [path]" printf "Example: sh nacos-install.sh 1.0.0 /opt/nacos\n" fi version=1.0.0 if [[ -n $1 ]]; then version=$1 fi root=/opt/nacos if [[ -n $2 ]]; then root=$2 fi echo "Current execution: install nacos ${version} to ${root}" echo -e "\n>>>>>>>>> download nacos" mkdir -p ${root} curl -o ${root}/nacos-server-${version}.zip https://github.com/alibaba/nacos/releases/download/${version}/nacos-server-${version}.zip echo -e "\n>>>>>>>>> install nacos" unzip ${root}/nacos-server-${version}.zip -d ${root}/nacos-server-${version} mv ${root}/nacos-server-${version}/nacos/* ${root}/nacos-server-${version} rm -rf ${root}/nacos-server-${version}/nacos ================================================ FILE: codes/linux/soft/nexus-install.sh ================================================ #!/usr/bin/env bash # ----------------------------------------------------------------------------------------------------- # 安装 sonatype nexus(用于搭建 maven 私服) 脚本 # @system: 适用于所有 linux 发行版本。 # sonatype nexus 会被安装到 /opt/maven 路径。 # 注意:sonatype nexus 要求必须先安装 JDK # @author: Zhang Peng # ----------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------ env # Regular Color export ENV_COLOR_BLACK="\033[0;30m" export ENV_COLOR_RED="\033[0;31m" export ENV_COLOR_GREEN="\033[0;32m" export ENV_COLOR_YELLOW="\033[0;33m" export ENV_COLOR_BLUE="\033[0;34m" export ENV_COLOR_MAGENTA="\033[0;35m" export ENV_COLOR_CYAN="\033[0;36m" export ENV_COLOR_WHITE="\033[0;37m" # Bold Color export ENV_COLOR_B_BLACK="\033[1;30m" export ENV_COLOR_B_RED="\033[1;31m" export ENV_COLOR_B_GREEN="\033[1;32m" export ENV_COLOR_B_YELLOW="\033[1;33m" export ENV_COLOR_B_BLUE="\033[1;34m" export ENV_COLOR_B_MAGENTA="\033[1;35m" export ENV_COLOR_B_CYAN="\033[1;36m" export ENV_COLOR_B_WHITE="\033[1;37m" # Reset Color export ENV_COLOR_RESET="$(tput sgr0)" # status export ENV_YES=0 export ENV_NO=1 export ENV_SUCCEED=0 export ENV_FAILED=1 # ------------------------------------------------------------------------------ functions # 显示打印日志的时间 SHELL_LOG_TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") # 那个用户在操作 USER=$(whoami) redOutput() { echo -e "${ENV_COLOR_RED} $@${ENV_COLOR_RESET}" } greenOutput() { echo -e "${ENV_COLOR_B_GREEN} $@${ENV_COLOR_RESET}" } yellowOutput() { echo -e "${ENV_COLOR_YELLOW} $@${ENV_COLOR_RESET}" } blueOutput() { echo -e "${ENV_COLOR_BLUE} $@${ENV_COLOR_RESET}" } magentaOutput() { echo -e "${ENV_COLOR_MAGENTA} $@${ENV_COLOR_RESET}" } cyanOutput() { echo -e "${ENV_COLOR_CYAN} $@${ENV_COLOR_RESET}" } whiteOutput() { echo -e "${ENV_COLOR_WHITE} $@${ENV_COLOR_RESET}" } printInfo() { echo -e "${ENV_COLOR_B_GREEN}[INFO] $@${ENV_COLOR_RESET}" } printWarn() { echo -e "${ENV_COLOR_B_YELLOW}[WARN] $@${ENV_COLOR_RESET}" } printError() { echo -e "${ENV_COLOR_B_RED}[ERROR] $@${ENV_COLOR_RESET}" } callAndLog () { $* if [[ $? -eq ${ENV_SUCCEED} ]]; then printInfo "$@" return ${ENV_SUCCEED} else printError "$@ EXECUTE FAILED" return ${ENV_FAILED} fi } # ------------------------------------------------------------------------------ main ENV_NEXUS_VERSION=${ENV_NEXUS_VERSION:-3.13.0-01} ENV_NEXUS_DIR=${ENV_NEXUS_DIR:-/opt/maven} printInfo ">>>> install nexus begin." mkdir -p ${ENV_NEXUS_DIR} printInfo "download nexus" #由于国内网络问题,有可能下载失败 curl -o ${ENV_NEXUS_DIR}/nexus-unix.tar.gz https://sonatype-download.global.ssl.fastly.net/repository/repositoryManager/3/nexus-${ENV_NEXUS_VERSION}-unix.tar.gz if [[ "$?" != ${ENV_SUCCEED} ]]; then printError "<<<< download nexus-${ENV_NEXUS_VERSION}-unix.tar.gz failed" return ${ENV_FAILED} fi tar -zxf nexus-unix.tar.gz printInfo ">>>> setting systemd." #通过设置 systemd,是的 nexus 注册为服务,开机自启动 touch /lib/systemd/system/nexus.service cat >> /lib/systemd/system/nexus.service << EOF [Unit] Description=nexus After=network.target [Service] Type=forking LimitNOFILE=65536 #警告处理 Environment=RUN_AS_USER=root ExecStart=${ENV_NEXUS_DIR}/nexus-${ENV_NEXUS_VERSION}/bin/nexus start ExecReload=${ENV_NEXUS_DIR}/nexus-${ENV_NEXUS_VERSION}/bin/nexus restart ExecStop=${ENV_NEXUS_DIR}/nexus-${ENV_NEXUS_VERSION}/bin/nexus stop Restart=on-failure PrivateTmp=true [Install] WantedBy=multi-user.target EOF systemctl enable nexus systemctl start nexus printInfo ">>>> setting firewalld." firewall-cmd --zone=public --add-port=8081/tcp --permanent firewall-cmd --reload # 如果防火墻使用的是 iptables,使用如下配置: #iptables -I INPUT -p tcp -m tcp --dport 8081 -j ACCEPT #/etc/rc.d/init.d/iptables save #service iptables restart printInfo "<<<<<<<< install nexus success." ================================================ FILE: codes/linux/soft/nginx-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}\n" cat << EOF ################################################################################### # 采用编译方式安装 Nginx, 并将其注册为 systemd 服务 # 默认下载安装 1.16.0 版本,安装路径为:/usr/local/nginx # @system: 适用于 CentOS7+ # @author: Zhang Peng ################################################################################### EOF printf "${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } printf "\n${GREEN}>>>>>>>> install nginx begin${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t Usage: sh nginx-install.sh [version] \n" printf "\t Default: sh nginx-install.sh 1.16.0 \n" printf "\t Example: sh nginx-install.sh 1.16.0 \n" printf "${RESET}\n" fi temp=/opt/nginx version=1.16.0 if [[ -n $1 ]]; then version=$1 fi # install info printf "${PURPLE}[Install Info]\n" printf "\t version = ${version}\n" printf "${RESET}\n" printf "${CYAN}>>>> install required libs${RESET}\n" yum install -y zlib zlib-devel gcc-c++ libtool openssl openssl-devel pcre # download and decompression printf "${CYAN}>>>> download nginx${RESET}\n" mkdir -p ${temp} curl -o ${temp}/nginx-${version}.tar.gz http://nginx.org/download/nginx-${version}.tar.gz tar zxf ${temp}/nginx-${version}.tar.gz -C ${temp} # configure and makefile printf "${CYAN}>>>> compile nginx${RESET}\n" cd ${temp}/nginx-${version} ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-pcre make && make install rm -rf ${temp} cd - # setting systemd service printf "${CYAN}>>>> set nginx as a systemd service${RESET}\n" wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/nginx/nginx.service -O /usr/lib/systemd/system/nginx.service chmod +x /usr/lib/systemd/system/nginx.service # boot nginx printf "${CYAN}>>>> start nginx${RESET}\n" systemctl enable nginx.service systemctl start nginx.service printf "\n${GREEN}<<<<<<<< install nginx end${RESET}\n" printf "\n${PURPLE}nginx service status: ${RESET}\n" systemctl status nginx ================================================ FILE: codes/linux/soft/nodejs-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 通过 nvm 安装 Nodejs 脚本 # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng # @see: https://github.com/nvm-sh/nvm ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install nodejs begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh nodejs-install.sh [version]\n" printf "\t Example: sh nodejs-install.sh 10.15.2\n" printf "${RESET}\n" fi version=10.15.2 if [[ -n $1 ]]; then version=$1 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "${RESET}\n" # install nvm printf "${GREEN}>>>>>>>> install nvm.${RESET}\n" rm -rf ~/.nvm mkdir -p ~/.nvm curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash . ~/.nvm/nvm.sh nvm --version printf "${GREEN}>>>>>>>> install nodejs by nvm.${RESET}\n" nvm install ${version} nvm use ${version} node --version printf "${GREEN}<<<<<<<< install nodejs end.${RESET}\n" ================================================ FILE: codes/linux/soft/redis-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ##########################################################################cd######### printf "${BLUE}\n" cat << EOF ################################################################################### # 采用编译方式安装 Redis # @system: 适用于 CentOS7+ # @author: Zhang Peng ################################################################################### EOF printf "${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } printf "\n${GREEN}>>>>>>>> install redis begin${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]] || [[ $# -lt 3 ]] || [[ $# -lt 4 ]]; then printf "${PURPLE}[Hint]\n" printf "\t Usage: sh redis-install.sh [version] [port] [password] \n" printf "\t Default: sh redis-install.sh 5.0.4 6379 \n" printf "\t Example: sh redis-install.sh 5.0.4 6379 123456 \n" printf "${RESET}\n" fi version=5.0.4 if [[ -n $1 ]]; then version=$1 fi port=6379 if [[ -n $2 ]]; then port=$2 fi password= if [[ -n $3 ]]; then password=$3 fi # install info printf "${PURPLE}[Install Info]\n" printf "\t version = ${version}\n" printf "\t port = ${port}\n" printf "\t password = ${password}\n" printf "${RESET}\n" printf "${CYAN}>>>> install required libs${RESET}\n" yum install -y zlib zlib-devel gcc-c++ libtool openssl openssl-devel tcl # download and decompression printf "${CYAN}>>>> download redis${RESET}\n" temp="/tmp/redis" path="/usr/local/redis" mkdir -p ${temp} curl -o ${temp}/redis-${version}.tar.gz http://download.redis.io/releases/redis-${version}.tar.gz tar zxf ${temp}/redis-${version}.tar.gz -C ${temp} mv ${temp}/redis-${version} ${path} # configure and makefile printf "${CYAN}>>>> compile redis${RESET}\n" cd ${path} make && make install rm -rf ${temp} cd - printf "${CYAN}>>>> modify redis config${RESET}\n" cp ${path}/redis.conf ${path}/redis.conf.default wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis/redis.conf -O ${path}/redis.conf sed -i "s/^port 6379/port ${port}/g" ${path}/redis.conf if [[ -n ${password} ]]; then sed -i "s/^protected-mode no/protected-mode yes/g" ${path}/redis.conf sed -i "s/^# requirepass/requirepass ${password}/g" ${path}/redis.conf fi printf "\n${CYAN}>>>> open redis port in firewall${RESET}\n" firewall-cmd --zone=public --add-port=${port}/tcp --permanent firewall-cmd --reload # setting systemd service printf "${CYAN}>>>> set redis as a systemd service${RESET}\n" wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/config/redis/redis.service -O /usr/lib/systemd/system/redis.service chmod +x /usr/lib/systemd/system/redis.service # boot redis printf "${CYAN}>>>> start redis${RESET}\n" systemctl enable redis.service systemctl start redis.service printf "\n${GREEN}<<<<<<<< install redis end${RESET}\n" printf "\n${PURPLE}redis service status: ${RESET}\n" systemctl status redis ================================================ FILE: codes/linux/soft/rocketmq-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 rocketmq 脚本 # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install tomcat begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh rocketmq-install.sh [version] [path]\n" printf "\t Example: sh rocketmq-install.sh 4.5.0 /opt/rocketmq\n" printf "${RESET}\n" fi version=4.5.0 if [[ -n $1 ]]; then version=$1 fi path=/opt/rocketmq if [[ -n $2 ]]; then path=$2 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "\t path = ${path}\n" printf "${RESET}\n" # download and decompression mkdir -p ${path} curl -o ${path}/rocketmq-all-${version}-bin-release.zip http://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/${version}/rocketmq-all-${version}-bin-release.zip unzip -o ${path}/rocketmq-all-${version}-bin-release.zip -d ${path}/rocketmq-all-${version}/ mv ${path}/rocketmq-all-${version}/rocketmq-all-${version}-bin-release/* ${path}/rocketmq-all-${version} rm -rf ${path}/rocketmq-all-${version}/rocketmq-all-${version}-bin-release printf "${GREEN}<<<<<<<< install rocketmq end.${RESET}\n" ================================================ FILE: codes/linux/soft/tomcat8-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 Tomcat 脚本 # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install tomcat begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh tomcat8-install.sh [version] [path]\n" printf "\t Example: sh tomcat8-install.sh 8.5.28 /opt/tomcat8\n" printf "${RESET}\n" fi version=8.5.28 if [[ -n $1 ]]; then version=$1 fi path=/opt/tomcat if [[ -n $2 ]]; then path=$2 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "\t path = ${path}\n" printf "${RESET}\n" # download and decompression mkdir -p ${path} curl -o ${path}/apache-tomcat-${version}.tar.gz https://archive.apache.org/dist/tomcat/tomcat-8/v${version}/bin/apache-tomcat-${version}.tar.gz tar zxf ${path}/apache-tomcat-${version}.tar.gz -C ${path} printf "${GREEN}<<<<<<<< install tomcat end.${RESET}\n" ================================================ FILE: codes/linux/soft/zookeeper-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 ZooKeeper 脚本 # @system: 适用于所有 linux 发行版本。 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install zookeeper begin.${RESET}\n" if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then printf "${PURPLE}[Hint]\n" printf "\t sh zookeeper-install.sh [version] [path]\n" printf "\t Example: sh zookeeper-install.sh 3.4.12 /opt/zookeeper\n" printf "${RESET}\n" fi version=3.4.12 if [[ -n $1 ]]; then version=$1 fi path=/opt/zookeeper if [[ -n $2 ]]; then path=$2 fi # install info printf "${PURPLE}[Info]\n" printf "\t version = ${version}\n" printf "\t path = ${path}\n" printf "${RESET}\n" # download and decompression mkdir -p ${path} curl -o ${path}/zookeeper-${version}.tar.gz http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-${version}/zookeeper-${version}.tar.gz tar zxf ${path}/zookeeper-${version}.tar.gz -C ${path} printf "${GREEN}<<<<<<<< install zookeeper end.${RESET}\n" ================================================ FILE: codes/linux/soft/zsh/oh-my-zsh-install.sh ================================================ # ------------------------------------------------------------------------------------------------------------------- # 由于国内经常无法使用 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # 所以,索性将安装脚本下载下来直接使用 # ------------------------------------------------------------------------------------------------------------------- #!/bin/sh # # This script should be run via curl: # sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # or wget: # sh -c "$(wget -qO- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # # As an alternative, you can first download the install script and run it afterwards: # wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh # sh install.sh # # You can tweak the install behavior by setting variables when running the script. For # example, to change the path to the Oh My Zsh repository: # ZSH=~/.zsh sh install.sh # # Respects the following environment variables: # ZSH - path to the Oh My Zsh repository folder (default: $HOME/.oh-my-zsh) # REPO - name of the GitHub repo to install from (default: ohmyzsh/ohmyzsh) # REMOTE - full remote URL of the git repo to install (default: GitHub via HTTPS) # BRANCH - branch to check out immediately after install (default: master) # # Other options: # CHSH - 'no' means the installer will not change the default shell (default: yes) # RUNZSH - 'no' means the installer will not run zsh after the install (default: yes) # KEEP_ZSHRC - 'yes' means the installer will not replace an existing .zshrc (default: no) # # You can also pass some arguments to the install script to set some these options: # --skip-chsh: has the same behavior as setting CHSH to 'no' # --unattended: sets both CHSH and RUNZSH to 'no' # --keep-zshrc: sets KEEP_ZSHRC to 'yes' # For example: # sh install.sh --unattended # set -e # Default settings ZSH=${ZSH:-~/.oh-my-zsh} REPO=${REPO:-ohmyzsh/ohmyzsh} REMOTE=${REMOTE:-https://github.com/${REPO}.git} BRANCH=${BRANCH:-master} # Other options CHSH=${CHSH:-yes} RUNZSH=${RUNZSH:-yes} KEEP_ZSHRC=${KEEP_ZSHRC:-no} command_exists() { command -v "$@" >/dev/null 2>&1 } error() { echo ${RED}"Error: $@"${RESET} >&2 } setup_color() { # Only use colors if connected to a terminal if [ -t 1 ]; then RED=$(printf '\033[31m') GREEN=$(printf '\033[32m') YELLOW=$(printf '\033[33m') BLUE=$(printf '\033[34m') BOLD=$(printf '\033[1m') RESET=$(printf '\033[m') else RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET="" fi } setup_ohmyzsh() { # Prevent the cloned repository from having insecure permissions. Failing to do # so causes compinit() calls to fail with "command not found: compdef" errors # for users with insecure umasks (e.g., "002", allowing group writability). Note # that this will be ignored under Cygwin by default, as Windows ACLs take # precedence over umasks except for filesystems mounted with option "noacl". umask g-w,o-w echo "${BLUE}Cloning Oh My Zsh...${RESET}" command_exists git || { error "git is not installed" exit 1 } if [ "$OSTYPE" = cygwin ] && git --version | grep -q msysgit; then error "Windows/MSYS Git is not supported on Cygwin" error "Make sure the Cygwin git package is installed and is first on the \$PATH" exit 1 fi git clone -c core.eol=lf -c core.autocrlf=false \ -c fsck.zeroPaddedFilemode=ignore \ -c fetch.fsck.zeroPaddedFilemode=ignore \ -c receive.fsck.zeroPaddedFilemode=ignore \ --depth=1 --branch "$BRANCH" "$REMOTE" "$ZSH" || { error "git clone of oh-my-zsh repo failed" exit 1 } echo } setup_zshrc() { # Keep most recent old .zshrc at .zshrc.pre-oh-my-zsh, and older ones # with datestamp of installation that moved them aside, so we never actually # destroy a user's original zshrc echo "${BLUE}Looking for an existing zsh config...${RESET}" # Must use this exact name so uninstall.sh can find it OLD_ZSHRC=~/.zshrc.pre-oh-my-zsh if [ -f ~/.zshrc ] || [ -h ~/.zshrc ]; then # Skip this if the user doesn't want to replace an existing .zshrc if [ $KEEP_ZSHRC = yes ]; then echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Keeping...${RESET}" return fi if [ -e "$OLD_ZSHRC" ]; then OLD_OLD_ZSHRC="${OLD_ZSHRC}-$(date +%Y-%m-%d_%H-%M-%S)" if [ -e "$OLD_OLD_ZSHRC" ]; then error "$OLD_OLD_ZSHRC exists. Can't back up ${OLD_ZSHRC}" error "re-run the installer again in a couple of seconds" exit 1 fi mv "$OLD_ZSHRC" "${OLD_OLD_ZSHRC}" echo "${YELLOW}Found old ~/.zshrc.pre-oh-my-zsh." \ "${GREEN}Backing up to ${OLD_OLD_ZSHRC}${RESET}" fi echo "${YELLOW}Found ~/.zshrc.${RESET} ${GREEN}Backing up to ${OLD_ZSHRC}${RESET}" mv ~/.zshrc "$OLD_ZSHRC" fi echo "${GREEN}Using the Oh My Zsh template file and adding it to ~/.zshrc.${RESET}" sed "/^export ZSH=/ c\\ export ZSH=\"$ZSH\" " "$ZSH/templates/zshrc.zsh-template" > ~/.zshrc-omztemp mv -f ~/.zshrc-omztemp ~/.zshrc echo } setup_shell() { # Skip setup if the user wants or stdin is closed (not running interactively). if [ $CHSH = no ]; then return fi # If this user's login shell is already "zsh", do not attempt to switch. if [ "$(basename "$SHELL")" = "zsh" ]; then return fi # If this platform doesn't provide a "chsh" command, bail out. if ! command_exists chsh; then cat <<-EOF I can't change your shell automatically because this system does not have chsh. ${BLUE}Please manually change your default shell to zsh${RESET} EOF return fi echo "${BLUE}Time to change your default shell to zsh:${RESET}" # Prompt for user choice on changing the default login shell printf "${YELLOW}Do you want to change your default shell to zsh? [Y/n]${RESET} " read opt case $opt in y*|Y*|"") echo "Changing the shell..." ;; n*|N*) echo "Shell change skipped."; return ;; *) echo "Invalid choice. Shell change skipped."; return ;; esac # Check if we're running on Termux case "$PREFIX" in *com.termux*) termux=true; zsh=zsh ;; *) termux=false ;; esac if [ "$termux" != true ]; then # Test for the right location of the "shells" file if [ -f /etc/shells ]; then shells_file=/etc/shells elif [ -f /usr/share/defaults/etc/shells ]; then # Solus OS shells_file=/usr/share/defaults/etc/shells else error "could not find /etc/shells file. Change your default shell manually." return fi # Get the path to the right zsh binary # 1. Use the most preceding one based on $PATH, then check that it's in the shells file # 2. If that fails, get a zsh path from the shells file, then check it actually exists if ! zsh=$(which zsh) || ! grep -qx "$zsh" "$shells_file"; then if ! zsh=$(grep '^/.*/zsh$' "$shells_file" | tail -1) || [ ! -f "$zsh" ]; then error "no zsh binary found or not present in '$shells_file'" error "change your default shell manually." return fi fi fi # We're going to change the default shell, so back up the current one if [ -n "$SHELL" ]; then echo $SHELL > ~/.shell.pre-oh-my-zsh else grep "^$USER:" /etc/passwd | awk -F: '{print $7}' > ~/.shell.pre-oh-my-zsh fi # Actually change the default shell to zsh if ! chsh -s "$zsh"; then error "chsh command unsuccessful. Change your default shell manually." else export SHELL="$zsh" echo "${GREEN}Shell successfully changed to '$zsh'.${RESET}" fi echo } main() { # Run as unattended if stdin is closed if [ ! -t 0 ]; then RUNZSH=no CHSH=no fi # Parse arguments while [ $# -gt 0 ]; do case $1 in --unattended) RUNZSH=no; CHSH=no ;; --skip-chsh) CHSH=no ;; --keep-zshrc) KEEP_ZSHRC=yes ;; esac shift done setup_color if ! command_exists zsh; then echo "${YELLOW}Zsh is not installed.${RESET} Please install zsh first." exit 1 fi if [ -d "$ZSH" ]; then cat <<-EOF ${YELLOW}You already have Oh My Zsh installed.${RESET} You'll need to remove '$ZSH' if you want to reinstall. EOF exit 1 fi setup_ohmyzsh setup_zshrc setup_shell printf "$GREEN" cat <<-'EOF' __ __ ____ / /_ ____ ___ __ __ ____ _____/ /_ / __ \/ __ \ / __ `__ \/ / / / /_ / / ___/ __ \ / /_/ / / / / / / / / / / /_/ / / /_(__ ) / / / \____/_/ /_/ /_/ /_/ /_/\__, / /___/____/_/ /_/ /____/ ....is now installed! Please look over the ~/.zshrc file to select plugins, themes, and options. p.s. Follow us on https://twitter.com/ohmyzsh p.p.s. Get stickers, shirts, and coffee mugs at https://shop.planetargon.com/collections/oh-my-zsh EOF printf "$RESET" if [ $RUNZSH = no ]; then echo "${YELLOW}Run zsh to try it out.${RESET}" exit fi exec zsh -l } main "$@" ================================================ FILE: codes/linux/soft/zsh-install.sh ================================================ #!/usr/bin/env bash ################################################################################### # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" ################################################################################### printf "${BLUE}" cat << EOF ################################################################################### # 安装 zsh、oh-my-zsh 脚本 # @system: 适用于 CentOS # @author: Zhang Peng # See: https://github.com/robbyrussell/oh-my-zsh ################################################################################### EOF printf "${RESET}" printf "${GREEN}>>>>>>>> install zsh begin.${RESET}\n" command -v yum > /dev/null 2>&1 || { printf "${RED}Require yum but it's not installed.${RESET}\n"; exit 1; } command -v git > /dev/null 2>&1 || { printf "${RED}Require git but it's not installed.${RESET}\n"; exit 1; } # install zsh yum install -y zsh chsh -s /bin/zsh # install oh-my-zsh # 由于国内经常无法使用 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # 所以,索性将安装脚本下载下来直接使用 #curl -o- https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh | bash zsh/oh-my-zsh-install.sh # choose oh-my-zsh theme sed -i "s/^ZSH_THEME=.*/ZSH_THEME=\"ys\"/g" ~/.zshrc # install oh-my-zsh plugins git clone https://github.com/zsh-users/zsh-autosuggestions.git ~/.oh-my-zsh/plugins/zsh-autosuggestions git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/plugins/zsh-syntax-highlighting sed -i "s/^plugins=.*/plugins=(git z wd extract zsh-autosuggestions zsh-syntax-highlighting)/g" ~/.zshrc # reload zsh # 注册到 /etc/shells echo "/usr/bin/zsh" >> /etc/shells # 切换 shell chsh -s $(which zsh) printf "${GREEN}<<<<<<<< install zsh finished${RESET}\n" printf "${GREEN}Please reboot to take effect.${RESET}\n" ================================================ FILE: codes/linux/sys/README.md ================================================ # 脚本使用说明 > 建议按照顺序执行本文脚本。 - [替换 yum repo 源](#替换-yum-repo-源) - [安装基本工具(可选)](#安装基本工具可选) - [安装常见 lib](#安装常见-lib) - [关闭防火墙](#关闭防火墙) - [设置 DNS](#设置-dns) - [设置 ntp](#设置-ntp) ## 替换 yum repo 源 由于 CentOS 默认 yum 源,访问速度很慢,所以推荐使用国内镜像。 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/change-yum-repo.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/change-yum-repo.sh | bash ``` ## 安装基本工具(可选) 工具清单(可以根据需要,在 install-tools.sh 中把不需要的工具注掉): ``` # 核心工具:df、du、chkconfig # 网络工具:ifconfig、netstat、route、iptables # IP工具:ip、ss、ping、tracepath、traceroute # DNS工具:dig、host、nslookup、whois # 端口工具:lsof、nc、telnet # 下载工具:curl、wget # 编辑工具:emacs、vim # 流量工具:iftop、nethogs # 抓包工具:tcpdump # 压缩工具:unzip、zip # 版本控制工具:git、subversion ``` 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-tools.sh | bash ``` ## 安装常见 lib lib 清单(可以根据需要,在 install-libs.sh 中把不需要的工具注掉): ``` # gcc gcc-c++ kernel-devel libtool # openssl openssl-devel # zlib zlib-devel # pcre ``` 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/install-libs.sh | bash ``` ## 关闭防火墙 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/stop-firewall.sh | bash ``` ## 设置 DNS 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-dns.sh | bash ``` ## 设置 ntp 使用方法:执行以下任意命令即可执行脚本。 ```shell curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/set-ntp.sh | bash ``` ================================================ FILE: codes/linux/sys/change-yum-repo.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- printf "${BLUE}\n" cat << EOF ################################################################################### # 本脚本用于替换 yum repo,使用国内 yum 仓库,加速下载 # 要求:仅适用于 Linux CentOS 发行版本,并且环境必须已支持 yum 、lsb_release 命令 # @author: Zhang Peng ################################################################################### EOF printf "${RESET}\n" printf "\n${GREEN}>>>>>>>>> 替换 yum repo 源开始${RESET}\n" # 备份 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # 执行 lsb_release 命令,获取系统发行版本 # version=`lsb_release -r | awk '{print substr($2,1,1)}'` # 很多机器没有 lsb_release 命令 version=`cat /etc/redhat-release | awk '{print substr($4,1,1)}'` # 根据发型版本选择相应 yum 镜像 if [[ ${version} == 5 ]]; then # Cento5 已废弃,只能使用 http://vault.CentOS.org/ 替换,但由于是国外镜像,速度较慢 wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-5.repo -O /etc/yum.repos.d/CentOS-Base.repo # 根据实际发型版本情况替换 detailVersion=`lsb_release -r | awk '{print substr($2,1,3)}'` sed -i 's/$releasever/'"${detailVersion}"'/g' /etc/yum.repos.d/CentOS-Base.repo # 不替换下面的开关,可能会出现错误:Could not open/read repomd.xml sed -i 's/enabled=1/enabled=0/g' /etc/yum.repos.d/CentOS-Media.repo elif [[ ${version} == 6 ]]; then wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-6.repo -O /etc/yum.repos.d/CentOS-Base.repo elif [[ ${version} == 7 ]]; then wget -N https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/sys/yum/Centos-7.repo -O /etc/yum.repos.d/CentOS-Base.repo else printf "\n${RED}版本不支持,替换 yum repo 失败${RESET}\n" fi # 更新缓存 yum clean all yum makecache printf "\n${GREEN}<<<<<<<< 替换 yum repo 源结束${RESET}\n" ================================================ FILE: codes/linux/sys/install-libs.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- printf "${BLUE}\n" cat << EOF ################################################################################### # 安装常见 lib # 如果不知道命令在哪个 lib,可以使用 yum search xxx 来查找 # lib 清单如下: # gcc gcc-c++ kernel-devel libtool # openssl openssl-devel # zlib zlib-devel # pcre # # @author: Zhang Peng ################################################################################### EOF printf "${RESET}\n" printf "\n${GREEN}>>>>>>>>> 安装常见 lib 开始${RESET}\n" printf "\n${CYAN}>>>> install gcc gcc-c++ kernel-devel libtool${RESET}\n" yum -y install make gcc gcc-c++ kernel-devel libtool printf "\n${CYAN}>>>> install openssl openssl-devel${RESET}\n" yum -y install make openssl openssl-devel printf "\n${CYAN}>>>> install zlib zlib-devel${RESET}\n" yum -y install make zlib zlib-devel printf "\n${CYAN}>>>> install pcre${RESET}\n" yum -y install pcre printf "\n${GREEN}<<<<<<<< 安装常见 lib 结束${RESET}\n" ================================================ FILE: codes/linux/sys/install-tools.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- printf "${BLUE}\n" cat << EOF ################################################################################### # 安装常用命令工具 # 命令工具清单如下: # 核心工具:df、du、chkconfig # 网络工具:ifconfig、netstat、route、iptables # IP工具:ip、ss、ping、tracepath、traceroute # DNS工具:dig、host、nslookup、whois # 端口工具:lsof、nc、telnet # 下载工具:curl、wget # 编辑工具:emacs、vim # 流量工具:iftop、nethogs # 抓包工具:tcpdump # 压缩工具:unzip、zip # 版本控制工具:git、subversion # # @author: Zhang Peng ################################################################################### EOF printf "${RESET}\n" printf "\n${GREEN}>>>>>>>>> 安装常用命令工具开始${RESET}\n" # 核心工具 printf "\n${CYAN}>>>> install coreutils(df、du)${RESET}\n" yum install -y coreutils printf "\n${CYAN}>>>> install chkconfig${RESET}\n" yum install -y chkconfig # 网络工具 printf "\n${CYAN}>>>> install net-tools(ifconfig、netstat、route)${RESET}\n" yum install -y net-tools printf "\n${CYAN}>>>> install iptables${RESET}\n" yum install -y iptables # IP工具 printf "\n${CYAN}>>>> install iputils(ping、tracepath)${RESET}\n" yum install -y iputils printf "\n${CYAN}>>>> install traceroute${RESET}\n" yum install -y traceroute printf "\n${CYAN}>>>> install iproute(ip、ss)${RESET}\n" yum install -y iproute # 端口工具 printf "\n${CYAN}>>>> install lsof${RESET}\n" yum install -y lsof printf "\n${CYAN}>>>> install nc${RESET}\n" yum install -y nc printf "\n${CYAN}>>>> install netstat${RESET}\n" yum install -y netstat # DNS工具 printf "\n${CYAN}>>>> install bind-utils(dig、host、nslookup)${RESET}\n" yum install -y bind-utils printf "\n${CYAN}>>>> install whois${RESET}\n" yum install -y whois # 下载工具 printf "\n${CYAN}>>>> install curl${RESET}\n" yum install -y curl printf "\n${CYAN}>>>> install wget${RESET}\n" yum install -y wget # 编辑工具 printf "\n${CYAN}>>>> install emacs${RESET}\n" yum install -y emacs printf "\n${CYAN}>>>> install vim${RESET}\n" yum install -y vim # 流量工具 printf "\n${CYAN}>>>> install iftop${RESET}\n" yum install -y iftop printf "\n${CYAN}>>>> install nethogs${RESET}\n" yum install -y nethogs # 抓包工具 printf "\n${CYAN}>>>> install tcpdump${RESET}\n" yum install -y tcpdump # 压缩工具 printf "\n${CYAN}>>>> install unzip${RESET}\n" yum install -y unzip # 版本控制工具 printf "\n${CYAN}>>>> install git${RESET}\n" yum install -y git printf "\n${CYAN}>>>> install subversion${RESET}\n" yum install -y subversion printf "\n${GREEN}<<<<<<<< 安装常用命令工具结束${RESET}\n" ================================================ FILE: codes/linux/sys/set-dns.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- ################################################################################### # 在 /etc/resolv.conf 中配置 DNS 服务器 # 在 /etc/hosts 中配置本机域名 # @author: Zhang Peng ################################################################################### printf "\n${GREEN}>>>>>>>>> 配置 DNS 开始${RESET}\n" printf "\n${CYAN}>>>> 配置 DNS 解析服务器${RESET}\n" cat >> /etc/resolv.conf << EOF nameserver 114.114.114.114 nameserver 8.8.8.8 EOF printf "\n${CYAN}>>>> 配置本机域名和IP映射${RESET}\n" ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') host=`hostname` cat >> /etc/hosts << EOF ${ip} ${host} EOF printf "\n${GREEN}<<<<<<<< 配置 DNS 结束${RESET}\n" ================================================ FILE: codes/linux/sys/set-ntp.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- ################################################################################### # 使用 NTP 进行时间同步 # 参考:https://www.cnblogs.com/quchunhui/p/7658853.html # @author: Zhang Peng ################################################################################### printf "\n${GREEN}>>>>>>>>> 设置 NTP 开始${RESET}\n" printf "\n${CYAN}>>>> 安装 NTP 服务${RESET}\n" yum -y install ntp ip=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') /sbin/iptables -A INPUT -p UDP -i eth0 -s ${ip}/24 --dport 123 -j ACCEPT printf "\n${CYAN}>>>> 启动 NTP 服务${RESET}\n" systemctl start ntpd.service printf "\n${CYAN}>>>> 立即执行时间同步${RESET}\n" /usr/sbin/ntpdate ntp.sjtu.edu.cn printf "\n${CYAN}>>>> 自动定时同步时间${RESET}\n" echo "* 3 * * * /usr/sbin/ntpdate ntp.sjtu.edu.cn" >> /etc/crontab systemctl restart crond.service printf "\n${CYAN}>>>> 同步结束,当前系统时间:${RESET}\n" date printf "\n${GREEN}<<<<<<<< 设置 NTP 结束${RESET}\n" ================================================ FILE: codes/linux/sys/stop-firewall.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- ################################################################################### # 关闭防火墙 # 参考:https://www.cnblogs.com/moxiaoan/p/5683743.html # @author: Zhang Peng ################################################################################### systemctl stop firewalld systemctl disable firewalld printf "\n${GREEN}<<<<<<<< 已关闭防火墙${RESET}\n" ================================================ FILE: codes/linux/sys/sys-settings.sh ================================================ #!/usr/bin/env bash # --------------------------------------------------------------------------------- # 控制台颜色 BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" RESET="$(tput sgr0)" # --------------------------------------------------------------------------------- printHeadInfo() { printf "${BLUE}\n" cat << EOF ################################################################################### # Linux Centos7 系统配置脚本(根据需要选择) # @author: Zhang Peng ################################################################################### EOF printf "${RESET}\n" } setLimit() { cat >> /etc/security/limits.conf << EOF * - nofile 65535 * - nproc 65535 EOF } setLang() { cat > /etc/sysconfig/i18n << EOF LANG="zh_CN.UTF-8" EOF } closeShutdownShortkey() { printf "\n${CYAN}>>>> 关闭 Ctrl+Alt+Del 快捷键防止重新启动${RESET}\n" sed -i 's#exec /sbin/shutdown -r now#\#exec /sbin/shutdown -r now#' /etc/init/control-alt-delete.conf } closeSelinux() { # see http://blog.51cto.com/13570193/2093299 printf "\n${CYAN}>>>> 关闭 selinux${RESET}\n" sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config } setBootMode() { # 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) # 2. 单用户模式,就像 Win9X 下的安全模式 # 3. 多用户,但是没有 NFS # 4. 完全多用户模式,准则的运行级 # 5. 通常不用,在一些特殊情况下可以用它来做一些事情 # 6. X11,即进到 X-Window 系统 # 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) printf "\n${CYAN}>>>> 配置 Linux 启动模式${RESET}\n" sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab } # 配置 IPv4 configIpv4() { printf "\n${CYAN}>>>> 配置 IPv4${RESET}\n" cat >> /etc/sysctl.conf << EOF net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 2 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_max_syn_backlog = 16384 net.core.netdev_max_backlog = 16384 net.core.somaxconn = 32768 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_timestamps = 0 net.ipv4.route.gc_timeout = 100 net.ipv4.tcp_synack_retries = 1 net.ipv4.tcp_syn_retries = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.ip_local_port_range = 2000 65535 net.ipv4.tcp_max_tw_buckets = 5000 vm.swappiness=10 EOF } # 关闭 IPv6 closeIpv6() { printf "\n${CYAN}>>>> 关闭 IPv6${RESET}\n" cat > /etc/modprobe.d/ipv6.conf << EOF alias net-pf-10 off options ipv6 disable=1 EOF echo "NETWORKING_IPV6=off" >> /etc/sysconfig/network } # 入口函数 main() { PS3="请选择要执行的操作:" select ITEM in "配置 DNS" "配置 NTP" "关闭防火墙" "配置 IPv4" "关闭 IPv6" "全部执行" do case ${ITEM} in "配置 DNS") sh ${root}/set-dns.sh ;; "配置 NTP") sh ${root}/set-ntp.sh ;; "关闭防火墙") sh ${root}/stop-firewall.sh ;; "配置 IPv4") configIpv4 ;; "关闭 IPv6") closeIpv6 ;; "全部执行") sh ${root}/set-dns.sh sh ${root}/set-ntp.sh sh ${root}/stop-firewall.sh configIpv4 closeIpv6 ;; *) printf "\n${RED}输入项不支持${RESET}\n" main ;; esac break done } ######################################## MAIN ######################################## root=$(pwd) if [[ -n $1 ]]; then root=$1 fi printHeadInfo main ================================================ FILE: codes/linux/sys/syscheck.sh ================================================ #!/usr/bin/env bash ############################################################################## # console color C_RESET="$(tput sgr0)" C_BLACK="\033[1;30m" C_RED="\033[1;31m" C_GREEN="\033[1;32m" C_YELLOW="\033[1;33m" C_BLUE="\033[1;34m" C_PURPLE="\033[1;35m" C_CYAN="\033[1;36m" C_WHITE="\033[1;37m" ############################################################################## printf "${C_PURPLE}" cat << EOF ################################################################################### # 系统信息检查脚本 # @author: Zhang Peng ################################################################################### EOF printf "${C_RESET}" [[ $(id -u) -gt 0 ]] && echo "请用root用户执行此脚本!" && exit 1 sysversion=$(rpm -q centos-release | cut -d- -f3) double_line="===============================================================" line="----------------------------------------------" # 打印头部信息 printHeadInfo() { cat << EOF +---------------------------------------------------------------------------------+ | 欢迎使用 【系统信息检查脚本】 | | @author: Zhang Peng | +---------------------------------------------------------------------------------+ EOF } # 打印尾部信息 printFootInfo() { cat << EOF +---------------------------------------------------------------------------------+ | 脚本执行结束,感谢使用! | +---------------------------------------------------------------------------------+ EOF } options=( "获取系统信息" "获取服务信息" "获取CPU信息" "获取系统网络信息" "获取系统内存信息" "获取系统磁盘信息" "获取CPU/内存占用TOP10" "获取系统用户信息" "输出所有信息" "退出" ) printMenu() { printf "${C_BLUE}" printf "主菜单:\n" for i in "${!options[@]}"; do index=`expr ${i} + 1` val=`expr ${index} % 2` printf "\t(%02d) %-30s" "${index}" "${options[$i]}" if [[ ${val} -eq 0 ]]; then printf "\n" fi done printf "${C_BLUE}请输入需要执行的指令:\n" printf "${C_RESET}" } # 获取系统信息 get_systatus_info() { sys_os=$(uname -o) sys_release=$(cat /etc/redhat-release) sys_kernel=$(uname -r) sys_hostname=$(hostname) sys_selinux=$(getenforce) sys_lang=$(echo $LANG) sys_lastreboot=$(who -b | awk '{print $3,$4}') sys_runtime=$(uptime | awk '{print $3,$4}' | cut -d, -f1) sys_time=$(date) sys_load=$(uptime | cut -d: -f5) cat << EOF 【系统信息】 系统: ${sys_os} 发行版本: ${sys_release} 系统内核: ${sys_kernel} 主机名: ${sys_hostname} selinux状态: ${sys_selinux} 系统语言: ${sys_lang} 系统当前时间: ${sys_time} 系统最后重启时间: ${sys_lastreboot} 系统运行时间: ${sys_runtime} 系统负载: ${sys_load} EOF } # 获取CPU信息 get_cpu_info() { Physical_CPUs=$(grep "physical id" /proc/cpuinfo | sort | uniq | wc -l) Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l) CPU_Kernels=$(grep "cores" /proc/cpuinfo | uniq | awk -F ': ' '{print $2}') CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq) CPU_Arch=$(uname -m) cat << EOF 【CPU信息】 物理CPU个数:$Physical_CPUs 逻辑CPU个数:$Virt_CPUs 每CPU核心数:$CPU_Kernels CPU型号:$CPU_Type CPU架构:$CPU_Arch EOF } # 获取服务信息 get_service_info() { port_listen=$(netstat -lntup | grep -v "Active Internet") kernel_config=$(sysctl -p 2> /dev/null) if [[ ${sysversion} -gt 6 ]]; then service_config=$(systemctl list-unit-files --type=service --state=enabled | grep "enabled") run_service=$(systemctl list-units --type=service --state=running | grep ".service") else service_config=$(/sbin/chkconfig | grep -E ":on|:启用" | column -t) run_service=$(/sbin/service --status-all | grep -E "running") fi cat << EOF 【服务信息】 ${service_config} ${line} 运行的服务: ${run_service} ${line} 监听端口: ${port_listen} ${line} 内核参考配置: ${kernel_config} EOF } # 获取系统内存信息 get_mem_info() { check_mem=$(free -m) MemTotal=$(grep MemTotal /proc/meminfo | awk '{print $2}') #KB MemFree=$(grep MemFree /proc/meminfo | awk '{print $2}') #KB let MemUsed=MemTotal-MemFree MemPercent=$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}") report_MemTotal="$((MemTotal/1024))" "MB" #内存总容量(MB) report_MemFree="$((MemFree/1024))" "MB" #内存剩余(MB) report_MemUsedPercent=$(free | sed -n '2p' | gawk 'x = int(( $3 / $2 ) * 100) {print x}' | sed 's/$/%/') cat << EOF 【内存信息】 内存总容量(MB): ${report_MemTotal} 内存剩余量(MB):${report_MemFree} 内存使用率: ${report_MemUsedPercent} EOF } # 获取系统网络信息 get_net_info() { pri_ipadd=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}') pub_ipadd=$(curl ifconfig.me -s) gateway=$(ip route | grep default | awk '{print $3}') mac_info=$(ip link | egrep -v "lo" | grep link | awk '{print $2}') dns_config=$(egrep -v "^$|^#" /etc/resolv.conf) route_info=$(route -n) cat << EOF 【网络信息】 系统公网地址:${pub_ipadd} 系统私网地址:${pri_ipadd} 网关地址:${gateway} MAC地址:${mac_info} 路由信息: ${route_info} DNS 信息: ${dns_config} EOF } # 获取系统磁盘信息 get_disk_info() { disk_info=$(fdisk -l | grep "Disk /dev" | cut -d, -f1) disk_use=$(df -hTP | awk '$2!="tmpfs"{print}') disk_percent=$(free | sed -n '2p' | gawk 'x = int(( $3 / $2 ) * 100) {print x}' | sed 's/$/%/') disk_inode=$(df -hiP | awk '$1!="tmpfs"{print}') cat << EOF 【磁盘信息】 ${disk_info} 磁盘使用: ${disk_use} 磁盘使用百分比: ${disk_percent} inode信息: ${disk_inode} EOF } # 获取系统用户信息 get_sys_user() { login_user=$(awk -F: '{if ($NF=="/bin/bash") print $0}' /etc/passwd) ssh_config=$(egrep -v "^#|^$" /etc/ssh/sshd_config) sudo_config=$(egrep -v "^#|^$" /etc/sudoers | grep -v "^Defaults") host_config=$(egrep -v "^#|^$" /etc/hosts) crond_config=$(for cronuser in /var/spool/cron/*; do ls ${cronuser} 2> /dev/null | cut -d/ -f5; egrep -v "^$|^#" ${cronuser} 2> /dev/null; echo ""; done) cat << EOF 【用户信息】 系统登录用户: ${login_user} ${line} ssh 配置信息: ${ssh_config} ${line} sudo 配置用户: ${sudo_config} ${line} 定时任务配置: ${crond_config} ${line} hosts 信息: ${host_config} EOF } # 获取CPU/内存占用TOP10 get_process_top_info() { top_title=$(top -b n1 | head -7 | tail -1) cpu_top10=$(top -b n1 | head -17 | tail -11) mem_top10=$(top -b n1 | head -17 | tail -10 | sort -k10 -r) cat << EOF 【TOP10】 CPU占用TOP10: ${cpu_top10} 内存占用TOP10: ${top_title} ${mem_top10} EOF } show_dead_process() { printf "僵尸进程:\n" ps -al | gawk '{print $2,$4}' | grep Z } get_all_info() { get_systatus_info echo ${double_line} get_service_info echo ${double_line} get_cpu_info echo ${double_line} get_net_info echo ${double_line} get_mem_info echo ${double_line} get_disk_info echo ${double_line} get_process_top_info echo ${double_line} get_sys_user } main() { while [[ 1 ]] do printMenu read option local index=$[ ${option} - 1 ] case ${options[${index}]} in "获取系统信息") get_systatus_info ;; "获取服务信息") get_service_info ;; "获取CPU信息") get_cpu_info ;; "获取系统网络信息") get_net_info ;; "获取系统内存信息") get_mem_info ;; "获取系统磁盘信息") get_disk_info ;; "获取CPU/内存占用TOP10") get_process_top_info ;; "获取系统用户信息") get_sys_user ;; "输出所有信息") get_all_info > sys.log printf "${C_GREEN}信息已经输出到 sys.log 中。${C_RESET}\n\n" ;; "退出") exit ;; *) clear echo "抱歉,不支持此选项" ;; esac done } ######################################## MAIN ######################################## printHeadInfo main printFootInfo printf "${C_RESET}" ================================================ FILE: codes/linux/sys/yum/Centos-5.repo ================================================ [base] name=CentOS-$releasever - Base baseurl=http://vault.centos.org/$releasever/os/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever #released updates [updates] name=CentOS-$releasever - Updates baseurl=http://vault.centos.org/$releasever/updates/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever #additional packages that may be useful [extras] name=CentOS-$releasever - Extras baseurl=http://vault.centos.org/$releasever/extras/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever #additional packages that extend functionality of existing packages [centosplus] name=CentOS-$releasever - Plus baseurl=http://vault.centos.org/$releasever/centosplus/$basearch/ gpgcheck=1 enabled=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever #contrib - packages by Centos Users [contrib] name=CentOS-$releasever - Contrib baseurl=http://vault.centos.org/$releasever/contrib/$basearch/ gpgcheck=1 enabled=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-$releasever ================================================ FILE: codes/linux/sys/yum/Centos-6.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 - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/os/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-6 #released updates [updates] name=CentOS-$releasever - Updates - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/updates/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/updates/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-6 #additional packages that may be useful [extras] name=CentOS-$releasever - Extras - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/extras/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/extras/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-6 #additional packages that extend functionality of existing packages [centosplus] name=CentOS-$releasever - Plus - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/centosplus/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/centosplus/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-6 #contrib - packages by Centos Users [contrib] name=CentOS-$releasever - Contrib - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/contrib/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/contrib/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-6 ================================================ FILE: codes/linux/sys/yum/Centos-7.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 - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/os/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #released updates [updates] name=CentOS-$releasever - Updates - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/updates/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/updates/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #additional packages that may be useful [extras] name=CentOS-$releasever - Extras - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/extras/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/extras/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #additional packages that extend functionality of existing packages [centosplus] name=CentOS-$releasever - Plus - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/centosplus/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/centosplus/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #contrib - packages by Centos Users [contrib] name=CentOS-$releasever - Contrib - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/ http://mirrors.aliyuncs.com/centos/$releasever/contrib/$basearch/ http://mirrors.cloud.aliyuncs.com/centos/$releasever/contrib/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 ================================================ FILE: codes/linux/tool/Autoinstall_ELK_V1.3.sh ================================================ #!/usr/bin/env bash #mail:xuel@anchnet.com #data:2017/9/7 #AutoInstall ELK scripts #Software:elasticsearch-5.4.1/logstash-5.4.1/filebeat-5.4.1/kibana-5.4.1 clear echo "##########################################" echo "# Auto Install ELK. ##" echo "# Press Ctrl + C to cancel ##" echo "# Any key to continue ##" echo "##########################################" read -p software_dir="/usr/local/software" elasticsearch_url="https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.1.tar.gz" kibana_url="https://artifacts.elastic.co/downloads/kibana/kibana-5.4.1-linux-x86_64.tar.gz" logstash_url="https://artifacts.elastic.co/downloads/logstash/logstash-5.4.1.tar.gz" filebeat_url="https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.4.1-linux-x86_64.tar.gz" sys_version=`cat /etc/redhat-release | awk '{print $4}' | cut -d. -f1` IP=`ip addr | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1` jvm_conf="/usr/local/elasticsearch/config/jvm.options" sys_mem=`free -m | grep Mem: | awk '{print $2}' | awk '{sum+=$1} END {print sum/1024}' | cut -d. -f1` #wget software wget_fun() { if [ ! -d ${software_dir} ]; then mkdir -p ${software_dir} && cd ${software_dir} else cd ${software_dir} fi for software in $elasticsearch_url $kibana_url $logstash_url $filebeat_url do wget -c $software done clear } #initial system:install java wget;set hostname;disable firewalld init_sys() { [ -f /etc/init.d/functions ] && . /etc/init.d/functions [ "${sys_version}" != "7" ] && echo "Error:This Scripts Support Centos7.xx" && exit 1 [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 sed -i "s/SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config setenforce 0 yum install -y java-1.8.0-openjdk wget net-tools hostnamectl set-hostname elk-server systemctl stop firewalld cat >> /etc/security/limits.conf << EOF * soft nofile 65536 * hard nofile 65536 * soft nproc 65536 * hard nproc 65536 EOF } #install elasticsearch install_elasticsearch() { cd $software_dir tar zxf elasticsearch-5.4.1.tar.gz mv elasticsearch-5.4.1 /usr/local/elasticsearch mkdir -p /usr/local/elasticsearch/data /usr/local/elasticsearch/logs useradd elasticsearch chown -R elasticsearch:elasticsearch /usr/local/elasticsearch echo "vm.max_map_count = 655360" >> /etc/sysctl.conf && sysctl -p if [ ${sys_mem} -eq 0 ]; then sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx512m"#g" ${jvm_conf} sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms512m"#g" ${jvm_conf} else sed -i "s#`grep "^-Xmx" ${jvm_conf}`#"-Xmx${sys_mem}g"#g" ${jvm_conf} sed -i "s#`grep "^-Xms" ${jvm_conf}`#"-Xms${sys_mem}g"#g" ${jvm_conf} fi cat >> /usr/local/elasticsearch/config/elasticsearch.yml << EOF cluster.name: my-application node.name: elk-server path.data: /usr/local/elasticsearch/data path.logs: /usr/local/elasticsearch/logs network.host: 127.0.0.1 http.port: 9200 discovery.zen.ping.unicast.hosts: ["elk-server"] EOF su - elasticsearch -c "nohup /usr/local/elasticsearch/bin/elasticsearch &" } #install logstash install_logstash() { cd $software_dir tar -zxf logstash-5.4.1.tar.gz mv logstash-5.4.1 /usr/local/logstash cat > /usr/local/logstash/config/01-syslog.conf << EOF input { beats { port => "5044" } } output { elasticsearch { hosts => "127.0.0.1:9200" } stdout { codec => rubydebug } } EOF nohup /usr/local/logstash/bin/logstash -f /usr/local/logstash/config/01-syslog.conf & > /dev/null } #install filebeat install_filebeat() { cd $software_dir tar -zxf filebeat-5.4.1-linux-x86_64.tar.gz mv filebeat-5.4.1-linux-x86_64 /usr/local/filebeat cat > /usr/local/filebeat/filebeat.yml << EOF filebeat.prospectors: - input_type: log paths: - /var/log/*.log output.logstash: hosts: ["127.0.0.1:5044"] EOF cd /usr/local/filebeat/ nohup /usr/local/filebeat/filebeat & > /dev/null } #install kibana install_kibana() { cd $software_dir tar -zxf kibana-5.4.1-linux-x86_64.tar.gz mv kibana-5.4.1-linux-x86_64 /usr/local/kibana cat >> /usr/local/kibana/config/kibana.yml << EOF server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "http://127.0.0.1:9200" EOF nohup /usr/local/kibana/bin/kibana & > /dev/null } check() { port=$1 program=$2 check_port=`netstat -lntup | grep ${port} | wc -l` check_program=`ps -ef | grep ${program} | grep -v grep | wc -l` if [ $check_port -gt 0 ] && [ $check_program -gt 0 ]; then action "${program} run is ok!" /bin/true else action "${program} run is error!" /bin/false fi } main() { init_sys wget_fun install_elasticsearch install_filebeat install_logstash install_kibana echo -e "\033[32m Checking Elasticsearch...\033[0m" sleep 20 check :9200 "elasticsearch" echo -e "\033[32m Checking Logstash...\033[0m" sleep 2 check ":9600" "logstash" echo -e "\033[32m Checking Kibana...\033[0m" sleep 2 check ":5601" "kibana" action "ELK install is success!" /bin/true echo "url:http://$IP:5601" } main ================================================ FILE: codes/linux/tool/Cpu_Limit.sh ================================================ #!/usr/bin/env bash # auth:kaliarch # func:sys info check # version:v1.0 # sys:centos6.x/7.x set -e [ $(id -u) -gt 0 ] && exit 1 # cpu使用超过百分之多少进行限制 PEC_CPU=80 # 限制进程使用百分之多少,如果程序为多线程,单个cpu限制为85,如果为多核心,就需要按照比例写,例如cpu为2c,像限制多线程占比80%,就写170 LIMIT_CPU=85 # 日志 LOG_DIR=/var/log/cpulimit/ # 超过阀值进程pid PIDARG=$(ps -aux | awk -v CPU=${PEC_CPU} '{if($3 > CPU) print $2}') CPULIMITCMD=$(which cpulimit) install_cpulimit() { [ ! -d /tmp ] && mkdir /tmp || cd /tmp wget -c https://github.com/opsengine/cpulimit/archive/v0.2.tar.gz tar -zxf v0.2.tar.gz cd cpulimit-0.2 && make [ $? -eq 0 ] && cp src/cpulimit /usr/bin/ } do_cpulimit() { [ ! -d ${LOG_DIR} ] && mkdir -p ${LOG_DIR} for i in ${PIDARG}; do MSG=$(ps -aux | awk -v pid=$i '{if($2 == pid) print $0}') echo ${MSG} [ ! -d /tmp ] && mkdir /tmp || cd /tmp nohup ${CPULIMITCMD} -p $i -l ${LIMIT_CPU} & echo "$(date) -- ${MSG}" >> ${LOG_DIR}$(date +%F).log done } main() { hash cpulimit if [ $? -eq 0 ]; then do_cpulimit else install_cpulimit && do_cpulimit fi } main ================================================ FILE: codes/linux/tool/Custom_Rm.sh ================================================ #!/usr/bin/env bash # function:自定义rm命令,每天晚上定时清理 CMD_SCRIPTS=$HOME/.rm_scripts.sh TRASH_DIR=$HOME/.TRASH_DIR CRON_FILE=/var/spool/cron/root BASHRC=$HOME/.bashrc [ ! -d ${TRASH_DIR} ] && mkdir -p ${TRASH_DIR} cat > $CMD_SCRIPTS << EOF PARA_CNT=\$# TRASH_DIR=$TRASH_DIR for i in \$*; do DATE=\$(date +%F%T) fileName=\$(basename \$i) mv \$i \$TRASH_DIR/\$fileName.\$DATE done EOF sed -i "s@$(grep 'alias rm=' $BASHRC)@alias rm='bash ${CMD_SCRIPTS}'@g" $BASHRC source $HOME/.bashrc echo "0 0 * * * rm -rf $TRASH_DIR/*" >> $CRON_FILE echo "删除目录:$TRASH_DIR" echo "删除脚本:$CMD_SCRIPTS" echo "请执行:source $BASHRC 来加载文件或退出当前shell重新登录" ================================================ FILE: codes/linux/tool/Daily_Archive.sh ================================================ #!/usr/bin/env bash # # Daily_Archive - Archive designated files & directories ###################################################### # # Gather Current Date # DATE=`date +%y%m%d` # # Set Archive File Name # FILE=archive$DATE.tar.gz # # Set Configuration and Destination File # CONFIG_FILE=/home/tiandi/archive/Files_To_Backup DESTINATION=/home/tiandi/archive/$FILE # ##################### Main Script ############### # # Check Backup Config file exists # if [ -f $CONFIG_FILE ] #Make sure the config file still exists then echo else echo echo "$CONFIG_FILE does not exist." echo "Backup not completed due to missing Configuration file" echo exit fi # # Build the names of all the files to backup # FILE_NO=1 # Start on Line 1 of Config file. exec < $CONFIG_FILE # Redirect Std Input to name of Config File # read FILE_NAME # Read 1st record # while [ $? -eq 0 ] do # Make sure the file or directory exists. if [ -f $FILE_NAME -o -d $FILE_NAME ] then # If file exists, add its name to the lists FILE_LIST="$FILE_LIST $FILE_NAME" else # If file doesn't exist, issue warning echo echo "$FILE_NAME, does not exist." echo "Obviously, I will not include it in this archive." echo "It is listed on line $FILE_NO of the config file." echo "Continuing to build archive file." echo fi # FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one read FILE_NAME # Read next record. done ########################################################### # # Backup the files and Compress Archive # tar -czf $DESTINATION $FILE_LIST 2> /dev/null # ================================================ FILE: codes/linux/tool/Hourly_Archive.sh ================================================ #!/usr/bin/env bash # # Hourly_Archive - Every hour create an archive ###################################################### # # Set Configuration and Destination File # CONFIG_FILE=/home/tiandi/archive/Files_To_Backup # # Gather Current Date,Month & Time # DAY=`date +%d` MONTH=`date +%m` TIME=`date +%k%M` # # Set Base Archive Destination Location # BASEDEST=/home/tiandi/archive/hourly # # Create Archive Destination Directory mkdir -p $BASEDEST/$MONTH/$DAY # # Build Archive Destination File Name DESTINATION=$BASEDEST/$MONTH/$DAY/archive$TIME.tar.gz # ##################### Main Script ############### # # Check Backup Config file exists # if [ -f $CONFIG_FILE ] #Make sure the config file still exists then echo else echo echo "$CONFIG_FILE does not exist." echo "Backup not completed due to missing Configuration file" echo exit fi # # Build the names of all the files to backup # FILE_NO=1 # Start on Line 1 of Config file. exec < $CONFIG_FILE # Redirect Std Input to name of Config File # read FILE_NAME # Read 1st record # while [ $? -eq 0 ] do # Make sure the file or directory exists. if [ -f $FILE_NAME -o -d $FILE_NAME ] then # If file exists, add its name to the lists FILE_LIST="$FILE_LIST $FILE_NAME" else # If file doesn't exist, issue warning echo echo "$FILE_NAME, does not exist." echo "Obviously, I will not include it in this archive." echo "It is listed on line $FILE_NO of the config file." echo "Continuing to build archive file." echo fi # FILE_NO=$[ $FILE_NO + 1 ] # Increase Line/File number by one read FILE_NAME # Read next record. done ########################################################### # # Backup the files and Compress Archive # tar -czf $DESTINATION $FILE_LIST 2> /dev/null # ================================================ FILE: codes/linux/tool/gitcheck ================================================ #!/bin/sh ################################################################################### # 此脚本用于检测 git 状态 # Copy from https://github.com/fboender/multi-git-status ################################################################################### # MIT license if [ -t 1 ]; then # Our output is not being redirected, so we can use colors. C_RED="\033[1;31m" C_GREEN="\033[1;32m" C_YELLOW="\033[1;33m" C_BLUE="\033[1;34m" C_PURPLE="\033[1;35m" C_CYAN="\033[1;36m" C_RESET="$(tput sgr0)" fi C_OK="$C_GREEN" C_LOCKED="$C_RED" C_NEEDS_PUSH="$C_YELLOW" C_NEEDS_PULL="$C_BLUE" C_NEEDS_COMMIT="$C_RED" C_NEEDS_UPSTREAM="$C_PURPLE" C_UNTRACKED="$C_CYAN" C_STASHES="$C_YELLOW" DEBUG=0 usage() { cat << EOF >&2 Usage: $0 [-w] [-e] [-f] [--no-X] [DIR] [DEPTH=2] Scan for .git dirs under DIR (up to DEPTH dirs deep) and show git status -w Warn about dirs that are not Git repositories -e Exclude repos that are 'ok' -f Do a 'git fetch' on each repo (slow for many repos) You can limit output with the following options: --no-push --no-pull --no-upstream --no-uncommitted --no-untracked --no-stashes EOF } # Handle commandline options WARN_NOT_REPO=0 EXCLUDE_OK=0 DO_FETCH=0 NO_PUSH=0 NO_PULL=0 NO_UPSTREAM=0 NO_UNCOMMITTED=0 NO_UNTRACKED=0 NO_STASHES=0 while [ \! -z "$1" ]; do # Stop reading when we've run out of options. [ "$(echo "$1" | cut -c 1)" != "-" ] && break if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then usage exit 1 fi if [ "$1" = "-w" ]; then WARN_NOT_REPO=1 fi if [ "$1" = "-e" ]; then EXCLUDE_OK=1 fi if [ "$1" = "-f" ]; then DO_FETCH=1 fi if [ "$1" = "--no-push" ]; then NO_PUSH=1 fi if [ "$1" = "--no-pull" ]; then NO_PULL=1 fi if [ "$1" = "--no-upstream" ]; then NO_UPSTREAM=1 fi if [ "$1" = "--no-uncommitted" ]; then NO_UNCOMMITTED=1 fi if [ "$1" = "--no-untracked" ]; then NO_UNTRACKED=1 fi if [ "$1" = "--no-stashes" ]; then NO_STASHES=1 fi shift done if [ -z "$1" ]; then ROOT_DIR="." else ROOT_DIR="$1" fi if [ -z "$2" ]; then DEPTH=2 else DEPTH="$2" fi # Find all .git dirs, up to DEPTH levels deep find -L "$ROOT_DIR" -maxdepth "$DEPTH" -type d | while read -r PROJ_DIR do GIT_DIR="$PROJ_DIR/.git" # If this dir is not a repo, and WARN_NOT_REPO is 1, tell the user. if [ \! -d "$GIT_DIR" ]; then if [ "$WARN_NOT_REPO" -eq 1 ] && [ "$PROJ_DIR" != "." ]; then printf "${PROJ_DIR}: not a git repo\n" fi continue fi [ $DEBUG -eq 1 ] && echo "${PROJ_DIR}" # Check if repo is locked if [ -f "$GIT_DIR/index.lock" ]; then printf "${PROJ_DIR}: ${C_LOCKED}Locked. Skipping.${C_RESET}\n" continue fi # Do a 'git fetch' if requested if [ "$DO_FETCH" -eq 1 ]; then git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" fetch -q > /dev/null fi # Refresh the index, or we might get wrong results. git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" update-index -q --refresh > /dev/null 2>&1 # Find all remote branches that have been checked out and figure out if # they need a push or pull. We do this with various tests and put the name # of the branches in NEEDS_XXXX, seperated by newlines. After we're done, # we remove duplicates from NEEDS_XXX. NEEDS_PUSH_BRANCHES="" NEEDS_PULL_BRANCHES="" NEEDS_UPSTREAM_BRANCHES="" for REF_HEAD in $(cd "$GIT_DIR/refs/heads" && find . -type 'f' | sed "s/^\.\///"); do # Check if this branch is tracking an upstream (local/remote branch) UPSTREAM=$(git --git-dir "$GIT_DIR" rev-parse --abbrev-ref --symbolic-full-name "$REF_HEAD@{u}" 2> /dev/null) EXIT_CODE="$?" if [ "$EXIT_CODE" -eq 0 ]; then # Branch is tracking a remote branch. Find out how much behind / # ahead it is of that remote branch. CNT_AHEAD_BEHIND=$(git --git-dir "$GIT_DIR" rev-list --left-right --count "$REF_HEAD...$UPSTREAM") CNT_AHEAD=$(echo "$CNT_AHEAD_BEHIND" | awk '{ print $1 }') CNT_BEHIND=$(echo "$CNT_AHEAD_BEHIND" | awk '{ print $2 }') [ $DEBUG -eq 1 ] && echo "CNT_AHEAD_BEHIND: $CNT_AHEAD_BEHIND" [ $DEBUG -eq 1 ] && echo "CNT_AHEAD: $CNT_AHEAD" [ $DEBUG -eq 1 ] && echo "CNT_BEHIND: $CNT_BEHIND" if [ "$CNT_AHEAD" -gt 0 ]; then NEEDS_PUSH_BRANCHES="${NEEDS_PUSH_BRANCHES}\n$REF_HEAD" fi if [ "$CNT_BEHIND" -gt 0 ]; then NEEDS_PULL_BRANCHES="${NEEDS_PULL_BRANCHES}\n$REF_HEAD" fi # Check if this branch is a branch off another branch. and if it needs # to be updated. REV_LOCAL=$(git --git-dir "$GIT_DIR" rev-parse --verify "$REF_HEAD" 2> /dev/null) REV_REMOTE=$(git --git-dir "$GIT_DIR" rev-parse --verify "$UPSTREAM" 2> /dev/null) REV_BASE=$(git --git-dir "$GIT_DIR" merge-base "$REF_HEAD" "$UPSTREAM" 2> /dev/null) [ $DEBUG -eq 1 ] && echo "REV_LOCAL: $REV_LOCAL" [ $DEBUG -eq 1 ] && echo "REV_REMOTE: $REV_REMOTE" [ $DEBUG -eq 1 ] && echo "REV_BASE: $REV_BASE" if [ "$REV_LOCAL" = "$REV_REMOTE" ]; then : # NOOP else if [ "$REV_LOCAL" = "$REV_BASE" ]; then NEEDS_PULL_BRANCHES="${NEEDS_PULL_BRANCHES}\n$REF_HEAD" fi if [ "$REV_REMOTE" = "$REV_BASE" ]; then NEEDS_PUSH_BRANCHES="${NEEDS_PUSH_BRANCHES}\n$REF_HEAD" fi fi else # Branch does not have an upstream (local/remote branch). NEEDS_UPSTREAM_BRANCHES="${NEEDS_UPSTREAM_BRANCHES}\n$REF_HEAD" fi done # Remove duplicates from NEEDS_XXXX and make comma-seperated NEEDS_PUSH_BRANCHES=$(printf "$NEEDS_PUSH_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") NEEDS_PULL_BRANCHES=$(printf "$NEEDS_PULL_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") NEEDS_UPSTREAM_BRANCHES=$(printf "$NEEDS_UPSTREAM_BRANCHES" | sort | uniq | tr '\n' ',' | sed "s/^,\(.*\),$/\1/") # Find out if there are unstaged, uncommitted or untracked changes UNSTAGED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-index --quiet HEAD -- 2> /dev/null; echo $?) UNCOMMITTED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" diff-files --quiet --ignore-submodules --; echo $?) UNTRACKED=$(git --work-tree "$(dirname "$GIT_DIR")" --git-dir "$GIT_DIR" ls-files --exclude-standard --others) cd "$(dirname "$GIT_DIR")" || exit STASHES=$(git stash list | wc -l) cd "$OLDPWD" || exit # Build up the status string IS_OK=0 # 0 = Repo needs something, 1 = Repo needs nothing ('ok') STATUS_NEEDS="" if [ \! -z "$NEEDS_PUSH_BRANCHES" ] && [ "$NO_PUSH" -eq 0 ]; then STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_PUSH}Needs push ($NEEDS_PUSH_BRANCHES)${C_RESET} " fi if [ \! -z "$NEEDS_PULL_BRANCHES" ] && [ "$NO_PULL" -eq 0 ]; then STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_PULL}Needs pull ($NEEDS_PULL_BRANCHES)${C_RESET} " fi if [ \! -z "$NEEDS_UPSTREAM_BRANCHES" ] && [ "$NO_UPSTREAM" -eq 0 ]; then STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_UPSTREAM}Needs upstream ($NEEDS_UPSTREAM_BRANCHES)${C_RESET} " fi if [ "$UNSTAGED" -ne 0 ] || [ "$UNCOMMITTED" -ne 0 ] && [ "$NO_UNCOMMITTED" -eq 0 ]; then STATUS_NEEDS="${STATUS_NEEDS}${C_NEEDS_COMMIT}Uncommitted changes${C_RESET} " fi if [ "$UNTRACKED" != "" ] && [ "$NO_UNTRACKED" -eq 0 ]; then STATUS_NEEDS="${STATUS_NEEDS}${C_UNTRACKED}Untracked files${C_RESET} " fi if [ "$STASHES" -ne 0 ] && [ "$NO_STASHES" -eq 0 ]; then STATUS_NEEDS="${STATUS_NEEDS}${C_STASHES}$STASHES stashes${C_RESET} " fi if [ "$STATUS_NEEDS" = "" ]; then IS_OK=1 STATUS_NEEDS="${STATUS_NEEDS}${C_OK}ok${C_RESET} " fi # Print the output, unless repo is 'ok' and -e was specified if [ "$IS_OK" -ne 1 ] || [ "$EXCLUDE_OK" -ne 1 ]; then printf "${PROJ_DIR}: $STATUS_NEEDS\n" fi done ================================================ FILE: codes/linux/tool/删除用户脚本.sh ================================================ #!/usr/bin/env bash # # Delete_User - Automates the 4 steps to remove an account # ################################################################# # # Define Functions # ################################################################# function get_answer { # unset ANSWER ASK_COUNT=0 # while [ -z "$ANSWER" ] # while no answer is given, keep asking do ASK_COUNT=$[ $ASK_COUNT + 1 ] # case $ASK_COUNT in # If user gives no answer in time allowed 2) echo echo "Please answer the question." echo ;; 3) echo echo "One last try... please answer the question." echo ;; 4) echo echo "Since you refuse to answer the question..." echo "exiting program." echo # exit ;; esac # echo # if [ -n "$LINE2" ] then echo $LINE1 # Print 2 lines echo -e $LINE2" \c" else # Print 1 line echo -e $LINE1" \c" fi # # Allow 60 seconds to answer before time-out read -t 60 ANSWER done # # Do a little variable clean-up # unset LINE1 unset LINE2 # } #end of get_answer function # ################################################################# function process_answer { # case $ANSWER in y | Y | YES | yes | yEs | yeS | YEs | yES) # If user answers "yes".do nothing. ;; *) # If user answers anything but "yes", exit script echo echo $EXIT_LINE1 echo $EXIT_LINE2 echo exit ;; esac # # Do a little variable clean-up unset EXIT_LINE1 unset EXIT_LINE2 # } #End of process_answer function # ################################################################ # # End of Function Definitions # ############### Main Script ################################# # # Get name of User Account to check # echo "Step #1 - Determine User Account name to delete " echo LINE1="Please enter the username of the user" LINE2="account you wish to delete from system:" get_answer USER_ACCOUNT=$ANSWER # # Double check with script user that this is the correct User Account # LINE1="Is $USER_ACCOUNT the user account" LINE2="you wish to delete from the system?[ y/n ]:" get_answer # ############################################################ # # Check that USER_ACCOUNT is really an account on the system # USER_ACCOUNT_RECORD=$(cat /etc/passwd | grep -w $USER_ACCOUNT) # if [ $? -eq 1 ] # If the account is not found, exit script then echo echo "Account, $USER_ACCOUNT, not found." echo "Leaving the script..." echo exit fi # echo echo "I found this record:" echo $USER_ACCOUNT_RECORD echo # LINE1="Is this the correct User Account?[y/n]:" get_answer # # # Call process_answer function: # if user answers anything but "yes", exit script # EXIT_LINE1="Because the account, $USER_ACCOUNT, is not " EXIT_LINE2="the one you wish to delete, we are leaving the script..." process_anser # ############################################################## # # Search for any running processes that belong to the User Account # echo echo "Step #2 - Find process on system belonging to user account" echo echo "$USER_ACCOUNT has the following processes running: " echo # ps -u $USER_ACCOUNT #List the processes running # case $? in 1) # No processes running for this User Account # echo "There are no processes for this account currently running." echo ;; 0) # Processes running for this User Account. # Ask Script User if wants us to kill the processes. # unset ANSWER # I think this line is not needed LINE1="Would you like me to kill the process(es)? [y/n]:" get_answer # case $ANSWER in y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) # if user answer "yes", #kill User Account processes # echo # # Clean-up temp file upon signals # trap "rm$USER_ACCOUNT_Running_Process.rpt" SIGTERM SIGINT SIGQUIT # # List user processes running ps -u $USER_ACCOUNT > $USER_ACCOUNT_Running_Process.rpt # exec < $USER_ACCOUNT_Running_Process.rpt # Make report Std Input # read USER_PROCESS_REC # First record will be blank read USER_PROCESS_REC # while [ $? -eq 0 ] do # obtain PID USER_PID=$(echo $USER_PROCESS_REC | cut -d " " -f1) kill -9 $USER_PID echo "Killed process $USER_PID" read USER_PROCESS_REC done # echo # rm $USER_ACCOUNT_Running_Process.rpt # Remove temp report ;; *) # If user answers anything but "yes", do not kill. echo echo "Will not kill the process(es)." echo ;; esac ;; esac ################################################################################### # # Create a report of all files owned by User Account # echo echo "Step #3 - Find files on system belonging to user account" echo echo "Creating a report of all files owned by $USER_ACCOUNT." echo echo "It is recommended that you backup/archive these files." echo "and then do one of two things:" echo " 1) Delete the files" echo " 2) Change the files' ownership to a current user account." echo echo "Please wait. This may take a while..." # REPORT_DATE=`date +%y%m%d` REPORT_FILE=$USER_ACCOUNT"_Files_"$REPORT_DATE".rpt" # find / -user $USER_ACCOUNT > $REPORT_FILE 2> /dev/null # echo echo "Report is complete." echo "Name of report: $REPORT_FILE" echo "Location of report: `pwd`" echo ################################################################ # # Remove User Account echo echo "Step #4 - Remove user account" echo # LINE1="Do you wish to remove $USER_ACCOUNT's account from system? [y/n]:" get_answer # # Cass process_answer function: # if user answers anything but "yes", exit script # EXIT_LINE1="Since you do not wish to remove the user account." EXIT_LINE2="$USER_ACCOUNT at this time, exiting the script..." process_answer # userdel $USER_ACCOUNT # delete user account echo echo "User account, $USER_ACCOUNT, has been removed" echo # ================================================ FILE: codes/linux/tool/查看指定目录磁盘使用情况.sh ================================================ #!/usr/bin/env bash #DIRS="/var/log /home /opt" #DATE=$(date '+%m%d%y') #exec > disk_space_${DATE}.log echo "Top Ten Disk Space Usage" echo "for ${DIRS} Directories" for DIR in ${DIRS} do echo "" echo "The ${DIR} Directory:" du -S ${DIR} 2> /dev/null | sort -rn | sed '{11,$D; =}' | sed 'N; s/\n/ /' | gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}' done ================================================ FILE: codes/shell/README.md ================================================ # Shell 脚本大全 > **Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 > > 当您面临以下问题时,**Shell 脚本大全** 也许可以给你一些借鉴: > > - 你想要执行某个操作,却不知 Shell 命令如何写 > - 你想要快速掌握 Shell 基本用法 ## 参考资料 - [pure-bash-bible](https://github.com/dylanaraps/pure-bash-bible#change-a-string-to-lowercase) ================================================ FILE: codes/shell/lib/env.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ 颜色状态 # Regular Color C_BLACK="\033[0;30m" C_RED="\033[0;31m" C_GREEN="\033[0;32m" C_YELLOW="\033[0;33m" C_BLUE="\033[0;34m" C_MAGENTA="\033[0;35m" C_CYAN="\033[0;36m" C_WHITE="\033[0;37m" # Bold Color C_B_BLACK="\033[1;30m" C_B_RED="\033[1;31m" C_B_GREEN="\033[1;32m" C_B_YELLOW="\033[1;33m" C_B_BLUE="\033[1;34m" C_B_MAGENTA="\033[1;35m" C_B_CYAN="\033[1;36m" C_B_WHITE="\033[1;37m" # Underline Color C_U_BLACK="\033[4;30m" C_U_RED="\033[4;31m" C_U_GREEN="\033[4;32m" C_U_YELLOW="\033[4;33m" C_U_BLUE="\033[4;34m" C_U_MAGENTA="\033[4;35m" C_U_CYAN="\033[4;36m" C_U_WHITE="\033[4;37m" # Background Color C_BG_BLACK="\033[40m" C_BG_RED="\033[41m" C_BG_GREEN="\033[42m" C_BG_YELLOW="\033[43m" C_BG_BLUE="\033[44m" C_BG_MAGENTA="\033[45m" C_BG_CYAN="\033[46m" C_BG_WHITE="\033[47m" # Reset Color C_RESET="$(tput sgr0)" # ------------------------------------------------------------------------------ 常用状态值 YES=0 NO=1 SUCCEED=0 FAILED=1 ================================================ FILE: codes/shell/mysql/SQL批处理执行脚本.sh ================================================ #!/usr/bin/env bash user='root' password='xxxxxx' database='test' for f in `ls */*.sql` do echo ${f}; mysql -u${user} -p${password} -f ${database} -e "source $f"; done echo 'OK!' ================================================ FILE: codes/shell/mysql/向数据库中插入数据.sh ================================================ #!/usr/bin/env bash # send data to the the table in the MYSQL database MYSQL=`which mysql` if [ $# -ne 2 ] then echo "Usage:mtest2 emplid lastname firstname salary" else #脚本变量一定要用双引号,字符串变量使用单引号 statement=" insert into em_admin values(NULL, '$1', $2)" $MYSQL emwjs -u test << EOF $statement EOF if [ $? -eq 0 ] then echo Data successfully added else echo Problem adding data fi fi ================================================ FILE: codes/shell/mysql/格式化输出数据.sh ================================================ #!/usr/bin/env bash #redirecting SQL output to a variable MYSQL=`which mysql` dbs=`$MYSQL emwjs -u test -Bse 'show tables;'` for db in $dbs do echo $db done #使用xml输出数据 $MYSQL emwjs -u test -X -e 'select * from em_admin' #使用table标签输出数据 $MYSQL emwjs -u test -H -e 'select * from em_admin' ================================================ FILE: codes/shell/mysql/连接数据库并发送命令.sh ================================================ #!/usr/bin/env bash #连接数据库 mysql=`which mysql` #发送单个命令 $mysql emwjs -u test -e "show databases;" #发送多个命令 $mysql emwjs -u test << EOF show tables; select * from em_admin; EOF ================================================ FILE: codes/shell/基本脚本/echo示例.sh ================================================ #!/usr/bin/env bash # 输出普通字符串 echo "hello, world" # Output: hello, world # 输出含变量的字符串 echo "hello, \"zp\"" # Output: hello, "zp" # 输出含变量的字符串 name=zp echo "hello, \"${name}\"" # Output: hello, "zp" # 输出含换行符的字符串 echo "YES\nNO" # Output: YES\nNO echo -e "YES\nNO" # -e 开启转义 # Output: # YES # NO # 输出含不换行符的字符串 echo "YES" echo "NO" # Output: # YES # NO echo -e "YES\c" # -e 开启转义 \c 不换行 echo "NO" # Output: # YESNO # 输出内容定向至文件 echo "test" > test.txt # 输出执行结果 echo `pwd` # Output:(当前目录路径) ================================================ FILE: codes/shell/基本脚本/exit命令.sh ================================================ #!/usr/bin/env bash #退出状态码,最大为255,超过则进行模运算 #testing the exit status var1=10 var2=20 var3=$[ $var1 + $var2 ] echo The answer is $var3 exit 5 ================================================ FILE: codes/shell/基本脚本/printf示例.sh ================================================ #!/usr/bin/env bash # 单引号 printf '%d %s\n' 1 "abc" # Output:1 abc # 双引号 printf "%d %s\n" 1 "abc" # Output:1 abc # 无引号 printf %s abcdef # Output: abcdef(并不会换行) # 格式只指定了一个参数,但多出的参数仍然会按照该格式输出 printf "%s\n" abc def # Output: # abc # def printf "%s %s %s\n" a b c d e f g h i j # Output: # a b c # d e f # g h i # j # 如果没有参数,那么 %s 用 NULL 代替,%d 用 0 代替 printf "%s and %d \n" # Output: # and 0 # 格式化输出 printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543 printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876 # Output: # 姓名 性别 体重kg # 郭靖 男 66.12 # 杨过 男 48.65 # 郭芙 女 47.99 ================================================ FILE: codes/shell/基本脚本/使用expr执行数学运算.sh ================================================ #!/usr/bin/env bash #An example of using the expr command var1=10 var2=20 var3=`expr $var2 / $var1` echo "The result is $var3" ================================================ FILE: codes/shell/基本脚本/使用内联重定向计算表达式.sh ================================================ #!/usr/bin/env bash var1=10.45 var2=43.67 var3=33.2 var4=71 var5=`bc << EOF scale=4 a1 = $var1 * $var2 b1 = $var3 * $var4 a1 + b1 EOF ` echo The final answer for this mess is $var5 ================================================ FILE: codes/shell/基本脚本/使用方括号执行数学运算.sh ================================================ #!/usr/bin/env bash var1=10 var2=50 var3=45 var4=$[ $var1 * ( $var2 - $var3 ) ] echo 'The final result is '$var4 ================================================ FILE: codes/shell/基本脚本/反引号的使用.sh ================================================ #!/usr/bin/env bash #using the backtick character 会把反引号里面当作一条命令来执行 testing=`date` echo "The date and time are:$testing" ================================================ FILE: codes/shell/基本脚本/变量使用示例.sh ================================================ #!/usr/bin/env bash ################### 声明变量 ################### name="world" echo "hello ${name}" # Output: hello world ################### 输出变量 ################### folder=$(pwd) echo "current path: ${folder}" ################### 只读变量 ################### rword="hello" echo ${rword} # Output: hello readonly rword # rword="bye" # 如果放开注释,执行时会报错 ################### 删除变量 ################### dword="hello" # 声明变量 echo ${dword} # 输出变量值 # Output: hello unset dword # 删除变量 echo ${dword} # Output: (空) ################### 系统变量 ################### echo "UID:$UID" echo LOGNAME:$LOGNAME echo User:$USER echo HOME:$HOME echo PATH:$PATH echo HOSTNAME:$HOSTNAME echo SHELL:$SHELL echo LANG:$LANG ################### 自定义变量 ################### days=10 user="admin" echo "$user logged in $days days age" days=5 user="root" echo "$user logged in $days days age" # Output: # admin logged in 10 days age # root logged in 5 days age ################### 从变量读取列表 ################### colors="Red Yellow Blue" colors=$colors" White Black" for color in $colors do echo " $color" done ================================================ FILE: codes/shell/基本脚本/在脚本中使用bc.sh ================================================ #!/usr/bin/env bash var1=100 var2=45 var3=`echo "scale=4; $var1 / $var2" | bc` echo The answer for this is $var3 ================================================ FILE: codes/shell/基本脚本/字符串使用示例.sh ================================================ #!/usr/bin/env bash ################### 使用单引号拼接字符串 ################### name1='white' str1='hello, '${name1}'' str2='hello, ${name1}' echo ${str1}_${str2} # Output: # hello, white_hello, ${name1} ################### 使用双引号拼接字符串 ################### name2="black" str3="hello, "${name2}"" str4="hello, ${name2}" echo ${str3}_${str4} # Output: # hello, black_hello, black ################### 获取字符串长度 ################### text="12345" echo "${text} length is: ${#text}" # Output: # 12345 length is: 5 # 获取子字符串 text="12345" echo ${text:2:2} # Output: # 34 ################### 查找子字符串 ################### text="hello" echo `expr index "${text}" ll` # Output: # 3 ################### 判断字符串中是否包含子字符串 ################### result=$(echo "${str}" | grep "feature/") if [[ "$result" != "" ]]; then echo "feature/ 是 ${str} 的子字符串" else echo "feature/ 不是 ${str} 的子字符串" fi ################### 截取关键字左边内容 ################### full_branch="feature/1.0.0" branch=`echo ${full_branch#feature/}` echo "branch is ${branch}" ################### 截取关键字右边内容 ################### full_version="0.0.1-SNAPSHOT" version=`echo ${full_version%-SNAPSHOT}` echo "version is ${version}" ################### 字符串分割成数组 ################### str="0.0.0.1" OLD_IFS="$IFS" IFS="." array=( ${str} ) IFS="$OLD_IFS" size=${#array[*]} lastIndex=`expr ${size} - 1` echo "数组长度:${size}" echo "最后一个数组元素:${array[${lastIndex}]}" for item in ${array[@]} do echo "$item" done ################### 判断字符串是否为空 ################### #-n 判断长度是否非零 #-z 判断长度是否为零 str=testing str2='' if [[ -n "$str" ]] then echo "The string $str is not empty" else echo "The string $str is empty" fi if [[ -n "$str2" ]] then echo "The string $str2 is not empty" else echo "The string $str2 is empty" fi # Output: # The string testing is not empty # The string is empty ################### 字符串比较 ################### str=hello str2=world if [[ $str = "hello" ]]; then echo "str equals hello" else echo "str not equals hello" fi if [[ $str2 = "hello" ]]; then echo "str2 equals hello" else echo "str2 not equals hello" fi ================================================ FILE: codes/shell/基本脚本/数组使用示例.sh ================================================ #!/usr/bin/env bash ################### 创建数组 ################### nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 ) colors=( red yellow "dark blue" ) ################### 访问数组的单个元素 ################### echo ${nums[1]} # Output: 1 ################### 访问数组的所有元素 ################### echo ${colors[*]} # Output: red yellow dark blue echo ${colors[@]} # Output: red yellow dark blue printf "+ %s\n" ${colors[*]} # Output: # + red # + yellow # + dark # + blue printf "+ %s\n" "${colors[*]}" # Output: # + red yellow dark blue printf "+ %s\n" "${colors[@]}" # Output: # + red # + yellow # + dark blue ################### 访问数组的部分元素 ################### echo ${nums[@]:0:2} # Output: # 0 1 ################### 获取数组长度 ################### echo ${#nums[*]} # Output: # 3 ################### 向数组中添加元素 ################### colors=( white "${colors[@]}" green black ) echo ${colors[@]} # Output: # white red yellow dark blue green black ################### 从数组中删除元素 ################### unset nums[ 0 ] echo ${nums[@]} # Output: # 1 2 ================================================ FILE: codes/shell/基本脚本/显示时间和登录者.sh ================================================ #!/usr/bin/env bash #This script displays the date and who's logged on #如果想在同一行显示 #echo -n -e 'The time is:\n\n' echo The time is: date echo The one who has been logged is: who ================================================ FILE: codes/shell/基本脚本/显示系统变量和转义字符.sh ================================================ #!/usr/bin/env bash #display user information from system echo "User info fro userId:$USER" echo UID:$UID echo HOME:$HOME #换行 echo -e '\n' echo 'The cost of the item is \$15' ================================================ FILE: codes/shell/基本脚本/注释示例.sh ================================================ #!/usr/bin/env bash #-------------------------------------------- # shell 注释示例 # author:zp #-------------------------------------------- # echo '这是单行注释' ########## 这是分割线 ########## : << EOF echo '这是多行注释' echo '这是多行注释' echo '这是多行注释' EOF # Execute: ./comment-demo.sh # Output: null ================================================ FILE: codes/shell/基本脚本/通过反引号获得当前日期并生成唯一文件名.sh ================================================ #!/usr/bin/env bash #copy the /usr/bin directory listing to a log file today=`date +%y%m%d` ls /usr/bin -al > log.$today ================================================ FILE: codes/shell/控制/定时执行脚本.sh ================================================ #!/usr/bin/env bash # testing the at command at -f 4.sh 22:10 ================================================ FILE: codes/shell/控制/捕捉信号.sh ================================================ #!/usr/bin/env bash # testing signal trapping trap "echo'Sorry! I have trapped Ctrl-C'" SIGINT SIGTERM echo this is a test program count=1 while [ $count -le 10 ] do echo "Loop #$count" sleep 5 count=$[ $count + 1 ] done ================================================ FILE: codes/shell/控制/捕捉脚本的退出.sh ================================================ #!/usr/bin/env bash # trapping the script exit trap "echobyebye" EXIT count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 3 count=$[ $count + 1 ] done ================================================ FILE: codes/shell/控制/移除捕捉.sh ================================================ #!/usr/bin/env bash # removeing a set trap trap "echobyebye" EXIT count=1 while [ $count -le 5 ] do echo "Loop #$count" sleep 3 count=$[ $count + 1 ] done #移除捕捉 trap - EXIT echo "I just removed the trap" ================================================ FILE: codes/shell/操作符/关系运算符.sh ================================================ #!/usr/bin/env bash x=10 if [[ -n $1 ]]; then x=$1 fi y=20 if [[ -n $2 ]]; then y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} -eq ${y} ]]; then echo "${x} -eq ${y} : x 等于 y" else echo "${x} -eq ${y}: x 不等于 y" fi if [[ ${x} -ne ${y} ]]; then echo "${x} -ne ${y}: x 不等于 y" else echo "${x} -ne ${y}: x 等于 y" fi if [[ ${x} -gt ${y} ]]; then echo "${x} -gt ${y}: x 大于 y" else echo "${x} -gt ${y}: x 不大于 y" fi if [[ ${x} -lt ${y} ]]; then echo "${x} -lt ${y}: x 小于 y" else echo "${x} -lt ${y}: x 不小于 y" fi if [[ ${x} -ge ${y} ]]; then echo "${x} -ge ${y}: x 大于或等于 y" else echo "${x} -ge ${y}: x 小于 y" fi if [[ ${x} -le ${y} ]]; then echo "${x} -le ${y}: x 小于或等于 y" else echo "${x} -le ${y}: x 大于 y" fi # Output: # x=10, y=20 # 10 -eq 20: x 不等于 y # 10 -ne 20: x 不等于 y # 10 -gt 20: x 不大于 y # 10 -lt 20: x 小于 y # 10 -ge 20: x 小于 y # 10 -le 20: x 小于或等于 y ================================================ FILE: codes/shell/操作符/字符串运算符.sh ================================================ #!/usr/bin/env bash x="abc" if [[ -n $1 ]]; then x=$1 fi y="xyz" if [[ -n $2 ]]; then y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} = ${y} ]]; then echo "${x} = ${y} : x 等于 y" else echo "${x} = ${y}: x 不等于 y" fi if [[ ${x} != ${y} ]]; then echo "${x} != ${y} : x 不等于 y" else echo "${x} != ${y}: x 等于 y" fi if [[ -z ${x} ]]; then echo "-z ${x} : 字符串长度为 0" else echo "-z ${x} : 字符串长度不为 0" fi if [[ -n "${x}" ]]; then echo "-n ${x} : 字符串长度不为 0" else echo "-n ${x} : 字符串长度为 0" fi if [[ ${x} ]]; then echo "${x} : 字符串不为空" else echo "${x} : 字符串为空" fi # Output: # x=abc, y=xyz # abc = xyz: x 不等于 y # abc != xyz : x 不等于 y # -z abc : 字符串长度不为 0 # -n abc : 字符串长度不为 0 # abc : 字符串不为空 ================================================ FILE: codes/shell/操作符/布尔运算符.sh ================================================ #!/usr/bin/env bash x=10 if [[ -n $1 ]]; then x=$1 fi y=20 if [[ -n $2 ]]; then y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} != ${y} ]]; then echo "${x} != ${y} : x 不等于 y" else echo "${x} != ${y}: x 等于 y" fi if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" else echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" fi if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" else echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" fi if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" else echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" fi # Output: # x=10, y=20 # 10 != 20 : x 不等于 y # 10 小于 100 且 20 大于 15 : 返回 true # 10 小于 100 或 20 大于 100 : 返回 true # 10 小于 5 或 20 大于 100 : 返回 false ================================================ FILE: codes/shell/操作符/文件测试运算符.sh ================================================ #!/usr/bin/env bash file="/etc/hosts" if [[ -r ${file} ]]; then echo "${file} 文件可读" else echo "${file} 文件不可读" fi if [[ -w ${file} ]]; then echo "${file} 文件可写" else echo "${file} 文件不可写" fi if [[ -x ${file} ]]; then echo "${file} 文件可执行" else echo "${file} 文件不可执行" fi if [[ -f ${file} ]]; then echo "${file} 文件为普通文件" else echo "${file} 文件为特殊文件" fi if [[ -d ${file} ]]; then echo "${file} 文件是个目录" else echo "${file} 文件不是个目录" fi if [[ -s ${file} ]]; then echo "${file} 文件不为空" else echo "${file} 文件为空" fi if [[ -e ${file} ]]; then echo "${file} 文件存在" else echo "${file} 文件不存在" fi # Output:(根据文件的实际情况,输出结果可能不同) # /etc/hosts 文件可读 # /etc/hosts 文件可写 # /etc/hosts 文件不可执行 # /etc/hosts 文件为普通文件 # /etc/hosts 文件不是个目录 # /etc/hosts 文件不为空 # /etc/hosts 文件存在 ================================================ FILE: codes/shell/操作符/算术运算符.sh ================================================ #!/usr/bin/env bash x=10 if [[ -n $1 ]]; then x=$1 fi y=20 if [[ -n $2 ]]; then y=$2 fi echo "x=${x}, y=${y}" val=`expr ${x} + ${y}` echo "${x} + ${y} = $val" val=`expr ${x} - ${y}` echo "${x} - ${y} = $val" val=`expr ${x} \* ${y}` echo "${x} * ${y} = $val" val=`expr ${y} / ${x}` echo "${y} / ${x} = $val" val=`expr ${y} % ${x}` echo "${y} % ${x} = $val" if [[ ${x} == ${y} ]]; then echo "${x} = ${y}" fi if [[ ${x} != ${y} ]]; then echo "${x} != ${y}" fi # Execute: ./算术运算符.sh # Output: # x=10, y=20 # 10 + 20 = 30 # 10 - 20 = -10 # 10 * 20 = 200 # 20 / 10 = 2 # 20 % 10 = 0 # 10 != 20 # Execute: ./算术运算符.sh 10 30 # Output: # x=10, y=30 # 10 + 30 = 40 # 10 - 30 = -20 # 10 * 30 = 300 # 30 / 10 = 3 # 30 % 10 = 0 # 10 不等于 30 ================================================ FILE: codes/shell/操作符/逻辑运算符.sh ================================================ #!/usr/bin/env bash x=10 if [[ -n $1 ]]; then x=$1 fi y=20 if [[ -n $2 ]]; then y=$2 fi echo "x=${x}, y=${y}" if [[ ${x} -lt 100 && ${y} -gt 100 ]] then echo "${x} -lt 100 && ${y} -gt 100 返回 true" else echo "${x} -lt 100 && ${y} -gt 100 返回 false" fi if [[ ${x} -lt 100 || ${y} -gt 100 ]] then echo "${x} -lt 100 || ${y} -gt 100 返回 true" else echo "${x} -lt 100 || ${y} -gt 100 返回 false" fi # Output: # x=10, y=20 # 10 -lt 100 && 20 -gt 100 返回 false # 10 -lt 100 || 20 -gt 100 返回 true ================================================ FILE: codes/shell/文件操作/log.txt ================================================ Add git message title here # See links to relevant web pages, issue trackers, etc. See: https://github.com/ See: [Git Page](https://github.com/) # List all co-authors, so people can contact with them Co-authored-by: Name # Why is this change happening, e.g. goals, use cases, stories, etc.? Why: # How is this change happening, e.g. implementations, algorithms, etc.? How: # Tags suitable for searching, such as hashtags, keywords, etc. Tags: # ## Help ## # # Subject line imperative uppercase verbs: # # Add = Create a capability e.g. feature, test, dependency. # Drop = Delete a capability e.g. feature, test, dependency. # Fix = Fix an issue e.g. bug, typo, accident, misstatement. # Bump = Increase the version of something e.g. a dependency. # Make = Change the build process, or tools, or infrastructure. # Start = Begin doing something; e.g. enable a toggle, feature flag, etc. # Stop = End doing something; e.g. disable a toggle, feature flag, etc. # Refactor = A change that MUST be just refactoring. # Reformat = A change that MUST be just format, e.g. indent line, trim space, etc. # Rephrase = A change that MUST be just textual, e.g. edit a comment, doc, etc. # Optimize = A change that MUST be just about performance, e.g. speed up code. # Document = A change that MUST be only in the documentation, e.g. help files. # # For the subject line: # * Use 50 characters maximum. # * Do not use a sentence-ending period. # # For the body text: # * Use as many lines as you like. # * Use 72 characters maximum per line for typical word wrap text. # # # ## About ## # # This is our git commit messages. # You can edit this template if you want # # ## Usage ## # # Put the template file here: # # ~/.git_commit_template.txt # # Configure git to use the template file by running: # # git config --global commit.template ~/.git_commit_template.txt # # Add the template file to the ~/.gitconfig file: # # [commit] # template = ~/.git_commit_template.txt # # you could put it in other location if you prefer # ================================================ FILE: codes/shell/文件操作/创建临时文件.sh ================================================ #!/usr/bin/env bash tempFile=`mktemp test.XXXXXX` exec 3> ${tempFile} echo "This script writes to temp file ${tempFile}" echo "This is the first line" >&3 echo "This is the second line" >&3 echo "This is the last line" >&3 exec 3>&- echo "Done creating temp file. The contents are:" cat ${tempFile} rm -f ${tempFile} 2> /dev/null ================================================ FILE: codes/shell/文件操作/创建临时目录.sh ================================================ #!/usr/bin/env bash tempDir=`mktemp -d dir.XXXXXX` cd ${tempDir} || exit 1 tempFile1=`mktemp temp.XXXXXX` tempFile2=`mktemp temp.XXXXXX` exec 7> ${tempFile1} exec 8> ${tempFile2} echo "Sending data to directory $tempDir" echo "This is a test line of data for $tempFile1" >&7 echo "This is a test line of data for $tempFile2" >&8 ================================================ FILE: codes/shell/文件操作/文件路径操作.sh ================================================ #!/usr/bin/env bash path=/dir1/dir2/dir3/test.txt echo ${path##*/} 获取文件名 test.txt echo ${path##*.} 获取后缀 txt #不带后缀的文件名 temp=${path##*/} echo ${temp%.*} test #获取目录 echo ${path%/*} /dir1/dir2/dir3 ================================================ FILE: codes/shell/文件操作/目录基本操作.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # 目录操作示例 # @author: Zhang Peng # ------------------------------------------------------------------------------ # 创建目录(整个文件路径中的目录如果不存在,都会一一创建,如果目录已存在,则什么也不做) mkdir -p /home/linux-tutorial/temp # 进入目录,如果失败,则退出脚本 cd /home/linux-tutorial/temp || exit 1 # 获取当前目录路径 CURRENT_DIR=$(pwd) echo "当前目录是:${CURRENT_DIR}" # 获取上一级目录路径 PARENT_DIR=$(dirname $(pwd)) echo "父目录是:${PARENT_DIR}" # 获取当前执行脚本名 DIR_NAME=$(basename $0) echo "当前执行脚本名是:${DIR_NAME}" # 复制目录(复制 temp 目录所有内容,并命名新文件夹叫 temp2) cp -rf /home/linux-tutorial/temp /home/linux-tutorial/temp2 # 移动目录(将 temp2 移到 temp 目录下) mv /home/linux-tutorial/temp2 /home/linux-tutorial/temp/temp2 # 删除目录 rm -rf /home/linux-tutorial ================================================ FILE: codes/shell/文件操作/读文件.sh ================================================ #!/usr/bin/env bash FILE=log.txt count=1 cat ${FILE} | while read line do echo "$count: $line" count=$[ $count + 1 ] done echo "Finished reading." ================================================ FILE: codes/shell/查找替换文本/gawk/gawk.sh ================================================ #!/usr/bin/env bash BEGIN { print "The latest list of users and shells" print "Userid Shell" print "------ -----" FS=":" } { print $1 " " $7 } END { print "This concludes the listing" } #执行gawk命令截取/etc/passwd输出 #gawk -f gawk.sh /etc/passwd ================================================ FILE: codes/shell/查找替换文本/gawk/gawk函数库 ================================================ #!/bin/bash function myprint() { printf "%-16s - %s", $1, $4 } function myrand ( limit ) { return int ( limit * rand ( ) ) } ================================================ FILE: codes/shell/查找替换文本/gawk/gawk脚本 ================================================ #!/bin/bash BEGIN{ FS="\n"; RS=""} { myprint() } ================================================ FILE: codes/shell/查找替换文本/gawk/script ================================================ BEGIN{FS=","; print n} {print $n} ================================================ FILE: codes/shell/查找替换文本/gawk/test ================================================ 10 5 123 50 ================================================ FILE: codes/shell/查找替换文本/gawk/使用变量.sh ================================================ #!/usr/bin/env bash #使用内建变量 # NF 当前记录的字段个数 # NR 到目前为止读的记录数量 #下面的程序在每行开头输出行号,并在最后输出文件的总字段数 gawk '{ total+=NF; print NR, $0 }END{ print "Total: ", total}' gawk 'BEGIN {testing="This is a test"; print testing; testing=45; print testing}' #处理数字值 gawk 'BEGIN{x=4; x= x*2+3; printx}' #处理数组 gawk 'BEGIN{capital["Ill"] = "SprintField"; print capital["Ill"]}' #遍历数组变量 gawk 'BEGIN{ var["a"] = 1 var["g"] = 2 var["m"] = 3 for( test in var) { print "Index:",test,"- Value:",var[test] } }' print "------" #删除数组变量 gawk 'BEGIN{ var["a"] = 1 var["g"] = 2 for (test in var) { print "Index:",test," - Value:", var[test] } delete var["g"] print "----" for (test in var) { print "Index;",test," - Value:", var[test] } }' ================================================ FILE: codes/shell/查找替换文本/gawk/使用模式,结构化命令.sh ================================================ #!/usr/bin/env bash #正则表达式 gawk 'BEGIN{FS=","} /11/{print $1} ' test #if-else语句 gawk '{ if($1 > 20) { x=$1*20 print x } else { x=$1/2 print x } }' test #while 语句 gawk '{ total = 0 i=1 while(i<4) { total+=$i i++ } avg = total/3 print "Average:".avg }' test #do-while语句 gawk '{ total=0 i=1 do { total += $i i++ }while(total < 150) print total }' test #for语句 gawk '{ total = 0 for (i=1; i<4; i++) { total+=$i } avg = total/3 print "Average:".avg }' test ================================================ FILE: codes/shell/查找替换文本/gawk/自定义函数.sh ================================================ #!/usr/bin/env bash #gawk 自定义函数 gawk ' function myprint() { printf "%-16s - %s\n", $1, $4 } BEGIN{FS="\n"; RS=""} { myprint() }' test ================================================ FILE: codes/shell/查找替换文本/gawk/调用函数库和脚本.sh ================================================ #!/usr/bin/env bash #使用函数库和gawk脚本 gawk -f gawk函数库 -f gawk脚本 test ================================================ FILE: codes/shell/查找替换文本/gawk/输出.sh ================================================ #!/usr/bin/env bash #print用于产生简单输出 #多个表达式的字符串值之间用输出字段分隔符分开 gawk '{ print $1, $2 }' #输出字段分割符与输出记录分隔符存储在内建变量OFS与ORS中, #初始情况下,OFS与ORS被设置成一个空格符与一个换行符,但它们的值可以在任何时候改变 #下面这个程序打印每一行的第1第2个字段,字段之间用分号分开,在每一行的第2个字段之后输出两个换行符 gawk 'BEGIN { OFS = ":"; ORS = "\n\n" } { print $1, $2 }' #下面这个程序拼接第1个与第2个字段,两个字段之间没有输出字段分隔符插入 gawk '{ print $1 $2 }' #这两句话等价 gawk '{ print }' gawk '{ print $0 }' #输出空行 gawk '{ print "" }' #printf用于产生格式化输出 #printf不会自动换行,需要手动添加\n #格式说明符以%开始,以转换字符结束 # - 表达式在它的域内左对齐,没有则右对齐 # width 为了达到规定的宽度,必要时填充空格 # .prec 字符串最大宽度, 或十进制数的小数部分的位数 gawk '{ printf ("Name:%-10sAge:%-5dWeight:%7.2f\n", $1, $2, $3) }' #输出到文件 #重定向运算符>与>>用于将输出重定向到文件,文件名必须用双引号括起来 #下面这个程序将所有输入行的第1个与第3个字段输出到两个文件中:如果第3个字段大于100,则输出到bigpop,否则输出到smallpop gawk '{ print($1, $3) > ($3 > 100 ? "bigpop" : "smallpop") }' #输出到管道 #print的输出将以管道的方式传递给command # Canada 3852 # China 3705 # USA 3615 # Brazil 3286 gawk '{ pop[$1]+=$2 } END{ for(c in pop) printf("%15-s%6d\n", c, pop[c]) | "sort -nk 2"; close("sort -nk 2") }' #关闭文件与管道 #语句close(expression)关闭一个文件或管道,文件或管道由expression指定。 #expression的字符串值必须与最初用于创建文件或管道的字符串值相同。 #在同一个程序中,如果你写了一个文件,而待会儿想要读取它,那么就需要调用close。 #某一时刻,同时处于打开状态的文件或管道数量最大值由实现定义。 close ( " sort -nk 2 " ) ================================================ FILE: codes/shell/查找替换文本/grep.sh ================================================ #!/usr/bin/env bash # 使用 grep 命令可以用于在文件中查找指定文本 # 在二进制文件中查找字符串 file=$1 keyword=$2 strings ${file} | grep ${keyword} ================================================ FILE: codes/shell/查找替换文本/regex/目录文件计数.sh ================================================ #!/usr/bin/env bash # count number of files in your PATH mypath=`echo $PATH | sed 's/:/ /g'` count=0 for directory in $mypath do check=`ls $directory` echo $check for item in $check do count=$[ $count + 1 ] done echo "$directory - $count" count=0 done ================================================ FILE: codes/shell/查找替换文本/regex/邮件验证.sh ================================================ #!/usr/bin/env bash #验证邮件 gawk --re-interval '/^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\-\+]+)\.([a-zA-Z]{2,5})/{print $0}' ================================================ FILE: codes/shell/查找替换文本/sed/sed文件操作.sh ================================================ #!/usr/bin/env bash #向文件写入 sed '1,2w test1' test1 echo -e "next\n" #从文件读取 sed '3r ./test' ./test echo -e "next\n" #从文件读取,并插入字符流 sed '/lazy/r test' test #向数据流末尾添加数据 sed '$r test' test echo -e "next1\n" sed '/lazy/ { r test d }' test ================================================ FILE: codes/shell/查找替换文本/sed/sed编辑器基础.sh ================================================ #!/usr/bin/env bash #sed编辑器基础 #替换标记 sed 's/lazy/ht/' ./test echo -e "next\n" #可用的替换标记 #1.数字 表明新闻本将替换第几处模式匹配的地方 sed 's/lazy/ht/2' ./test #2.g 表明新文件将会替换所有已有文本出现的地方 sed 's/lazy/ht/g' ./test #3.p 表明原来行的内容要打印出来,替换后的 sed 's/lazy/ht/p' ./test #4.w file 将替换的结果写到文件中 sed 's/lazy/ht/w test1' ./test echo -e "next\n" #替换字符 sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd #或者 sed 's!/bin/bash!/bin/csh!' /etc/passwd echo -e "next\n" #使用地址 #1.数字方式的行寻址 sed '2s/lazy/cat/' ./test sed '2,3s/lazy/cat/' ./test sed '2,$s/lazy/cat/' ./test #2.使用文本模式过滤器 sed '/tiandi/s/bash/csh/' /etc/passwd echo -e "next\n" #组合命令 sed '2{ s/fox/elephant/ s/dog/cat/ }' test sed '2,${ s/fox/elephant/ s/dog/cat/ }' test echo -e "next\n" #删除行 sed '3d' ./test sed '2,$d' ./test sed '/number 1/d' ./test #删除两个文本模式来删除某个范围的行,第一个开启删除功能,第二个关闭删除功能 sed '/1/,/3/d' ./test echo -e "next\n" #插入和附加文本 sed '3i\ This is an appended line.' ./test sed '$a\ This is a new line of text.' ./test #修改行 sed '3c\ This a changed line of text.' ./test sed '/number 1/c\ This a changed line of text.' ./test #替换两行文本 #sed '2,3c\ #This a changed line of text.' ./test #转换命令,处理单个字符 #sed 'y/123/789/' ./test echo -e "next\n" #回顾打印 # p 打印文本行 # -n 禁止其他行,只打印包含匹配文本模式的行 sed -n '/number 3/p' ./test #查看修改之前的行和修改之后的行 #sed -n '/3/{ #p #s/line/test/p #}' ./test echo -e "next\n" # 打印行号 sed '=' ./test #打印指定的行和行号 #sed -n '/lazy/{ #= #p #}' ./test #列出行 打印数据流中的文本和不可打印的ASCII字符,任何不可打印的字符都用它们的八进制值前加一个反斜线或标准C风格的命名法,比如用\t来代表制表符 sed -n 'l' ./test ================================================ FILE: codes/shell/查找替换文本/sed/test ================================================ This is the header line This is the first data line This is the data line This is the second data line line This is the last line ================================================ FILE: codes/shell/查找替换文本/sed/保持空间.sh ================================================ #!/usr/bin/env bash #h将模式空间保存到保持空间 #H将模式空间附加到保持空间 #g将保持空间复制到模式空间 #G将保持空间保存到模式空间 #x交换模式空间和保持空间的内容 sed -n '/first/{ h p n p g p }' test ================================================ FILE: codes/shell/查找替换文本/sed/删除指定的空白行和删除html标签.sh ================================================ #!/usr/bin/env bash #多个空格只保留一个 #sed '/./,/^$/!d' test #删除开头的空白行 #sed '/./,$!d' test #删除结尾的空白行 sed '{ :start /^\n*$/{$d; N; b start} }' test #删除html标签 #有问题 #s/<.*>//g #sed 's/<[^>]*>//g' test1 #sed 's/<[^>]*>//g;/^$/d' test1 ================================================ FILE: codes/shell/查找替换文本/sed/在脚本中使用sed.sh ================================================ #!/usr/bin/env bash # shell wrapper for sed editor script to reverse lines sed -n '{ 1!G h $p }' $1 ================================================ FILE: codes/shell/查找替换文本/sed/排除命令.sh ================================================ !/bin/bash #排除命令,使本来起作用的命令不起作用 sed -n '/heade/!p' test #反转文本文件 sed -n '{1!G ; h; $p}' test ================================================ FILE: codes/shell/查找替换文本/sed/模式替代.sh ================================================ #!/usr/bin/env bash #and符号,代表替换命令中的匹配模式,不管预定义模式是什么文本,都可以用and符号替换,and符号会提取匹配替换命令中指定替换模式中的所有字符串 echo "The cat sleeps in his hat" | sed 's/.at/"&"/g' #替换单独的单词 echo "The System Administrator manual" | sed 's/\(System\) Administrator/\1 user/' #在长数字中插入逗号 echo "1234567" | sed '{:start; s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/; t start}' ================================================ FILE: codes/shell/查找替换文本/sed/测试.sh ================================================ #!/usr/bin/env bash #测试,如果测试成功,如果没有标签,sed会跳转到结尾,如果有标签,就跳转到标签,如果测试失败,则不会跳转 sed -n '{s/first/matched/; t; s/This is the/No match on/}' test ================================================ FILE: codes/shell/查找替换文本/sed/给文件中的行编号.sh ================================================ #!/usr/bin/env bash sed '=' test | sed 'N; s/\n/ /' ================================================ FILE: codes/shell/查找替换文本/sed/跳转.sh ================================================ #!/usr/bin/env bash #跳转到指定脚本 sed '{/first/b jump1; s/This is the/No jump on/; :jump1; s/This is the/Jump here on/}' test #跳转到开头,删除每一个逗号,并保证删除最后一个逗号之后,跳出循环 sed -n '{:start; s/,//1p; /,/b start}' test ================================================ FILE: codes/shell/查找替换文本/sed/输出末尾指定行数的数据.sh ================================================ #!/usr/bin/env bash #输出末尾10行数据 sed '{ :start $q N 11,$D b start }' /etc/passwd ================================================ FILE: codes/shell/查找替换文本/sed/重定向sed输出.sh ================================================ #!/usr/bin/env bash # add commas to numbers in factorial answer factorial=1 counter=1 number=$1 while [ $counter -le $number ] do factorial=$[ $factorial * $counter ] counter=$[ $counter + 1 ] done result=`echo $factorial | sed '{ :start s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/ t start }'` echo "The result is $result" ================================================ FILE: codes/shell/系统管理/控制远程服务器执行指令.sh ================================================ #!/usr/bin/expect user="root" password="root" /usr/bin/expect << EOF set timeout 5 spawn ssh -o "StrictHostKeyChecking no" ${user}@${host} expect { "yes/no)?" { send "yes\r"; exp_continue } "password:" { send "${password}\r" } } expect "root*" send "ssh-keygen -t rsa\r" expect "Enter file in which to save the key*" send "\r" expect { "(y/n)?" { send "n\r"; exp_continue } "Enter passphrase*" { send "\r"; exp_continue } "Enter same passphrase again:" { send "\r" } } expect "root*" send "df -h\r" expect "root*" send "exit\r" EOF ================================================ FILE: codes/shell/系统管理/系统用户管理.sh ================================================ #!/usr/bin/env bash ################################################################################### # Linux 系统用户管理 # @author: Zhang Peng ################################################################################### # 创建用户组 groupadd elk # 创建新用户,-g elk 设置其用户组为 elk,-p elk 设置其密码为 elk useradd elk -g elk -p elk # 更改 /opt 文件夹及内部文件的所属用户及组为 elk:elk chown -R elk:elk /opt ================================================ FILE: codes/shell/脚本函数/lib.sh ================================================ #!/usr/bin/env bash add() { echo $[ $1 + $2 ] } sub() { echo $[ $1 - $2 ] } mul() { echo $[ $1 * $2 ] } div() { if [[ $2 -ne 0 ]]; then echo $[ $1 / $2 ] else echo -1 fi } ================================================ FILE: codes/shell/脚本函数/从函数返回数组.sh ================================================ #!/usr/bin/env bash # returning an array value function arraydblr { local origarry local newarray local elements local i origarry=( `echo "$@"` ) newarray=( `echo "$@"` ) elements=$[ $# - 1 ] for (( i = 0; i <= $elements; i ++ )) { newarray [ $i ] = $[ ${origarry[$i]} * 2 ] } echo ${newarray[*]} } myarray=( 1 2 3 4 5 ) echo "The original array is : ${myarray[*]}" arg1=`echo ${myarray[*]}` result=( `arraydblr $arg1` ) echo "The new array is : ${result[*]}" ================================================ FILE: codes/shell/脚本函数/使用return命令.sh ================================================ #!/usr/bin/env bash # using the return command in a function function db1 { read -p "Enter a value:" value echo "doubling the value" return $[ $value * 2 ] } db1 echo "The new value is $?" ================================================ FILE: codes/shell/脚本函数/使用全局变量带来的问题.sh ================================================ #!/usr/bin/env bash # demonstrating a bad use of variables function func1 { temp=$[ $value + 5 ] result=$[ $temp * 2 ] } temlp=4 value=6 func1 echo "The result is $result" if [ $temp -gt $value ] then echo "Temp is larger" else echo "temp is smaller" fi ================================================ FILE: codes/shell/脚本函数/使用函数输出.sh ================================================ #!/usr/bin/env bash # using the echo to return a value function db1 { read -p "Enter a value:" value echo $[ $value * 2 ] } result=`db1` echo "The new value is $result" ================================================ FILE: codes/shell/脚本函数/使用命令行中传递的参数.sh ================================================ #!/usr/bin/env bash # using a global variable to pass a value function db1 { # $1和$2 不能从命令行中传递,只能调用函数时,手动传递 echo $[ $1 * $2 ] } if [ $# -eq 2 ] then value=`db1 $1 $2` echo "The result is $value" else echo "Usage: badtest1 a b" fi ================================================ FILE: codes/shell/脚本函数/使用局部变量.sh ================================================ #!/usr/bin/env bash # demonstrating the local keyword function func1 { local temp=$[ $value + 5 ] result=$[ $temp * 2 ] } temp=4 value=6 func1 echo "The result is $result" if [ $temp -gt $value ] then echo "temp is larger" else echo "temp is smaller" fi ================================================ FILE: codes/shell/脚本函数/使用库函数.sh ================================================ #!/usr/bin/env bash # 装载其它脚本 source lib.sh echo "20 + 10 = $(add 20 10)" echo "20 - 10 = $(sub 20 10)" echo "20 * 10 = $(mul 20 10)" echo "20 / 10 = $(div 20 10)" ================================================ FILE: codes/shell/脚本函数/全局变量.sh ================================================ #!/usr/bin/env bash # using a global variable to pass a value function db1 { value=$[ $value * 2 ] } read -p "Enter a value: " value db1 echo "The new value is : $value" ================================================ FILE: codes/shell/脚本函数/函数入参.sh ================================================ #!/usr/bin/env bash x=0 if [[ -n $1 ]]; then echo "第一个参数为:$1" x=$1 else echo "第一个参数为空" fi y=0 if [[ -n $2 ]]; then echo "第二个参数为:$2" y=$2 else echo "第二个参数为空" fi paramsFunction() { echo "函数第一个入参:$1" echo "函数第二个入参:$2" } paramsFunction ${x} ${y} ================================================ FILE: codes/shell/脚本函数/函数入参2.sh ================================================ #!/usr/bin/env bash runner() { return 0 } name=zp paramsFunction() { echo "函数第一个入参:$1" echo "函数第二个入参:$2" echo "传递到脚本的参数个数:$#" echo "所有参数:" printf "+ %s\n" "$*" echo "脚本运行的当前进程 ID 号:$$" echo "后台运行的最后一个进程的 ID 号:$!" echo "所有参数:" printf "+ %s\n" "$@" echo "Shell 使用的当前选项:$-" runner echo "runner 函数的返回值:$?" } paramsFunction 1 "abc" "hello, \"zp\"" # Output: # 函数第一个入参:1 # 函数第二个入参:abc # 传递到脚本的参数个数:3 # 所有参数: # + 1 abc hello, "zp" # 脚本运行的当前进程 ID 号:26400 # 后台运行的最后一个进程的 ID 号: # 所有参数: # + 1 # + abc # + hello, "zp" # Shell 使用的当前选项:hB # runner 函数的返回值:0 ================================================ FILE: codes/shell/脚本函数/函数基本示例.sh ================================================ #!/usr/bin/env bash # using a function in script function func1 { echo "This is an example of a function" } count=1 while [ $count -le 5 ] do func1 count=$[ $count + 1 ] done echo "This is the end of the loop" func1 echo "Now this is the end of the script" ================================================ FILE: codes/shell/脚本函数/函数基本示例2.sh ================================================ #!/usr/bin/env bash calc() { PS3="choose the oper: " select oper in "+" "-" "*" "/" # 生成操作符选择菜单 do echo -n "enter first num: " && read x # 读取输入参数 echo -n "enter second num: " && read y # 读取输入参数 exec case ${oper} in "+") return $((${x} + ${y})) ;; "-") return $((${x} - ${y})) ;; "*") return $((${x} * ${y})) ;; "/") return $((${x} / ${y})) ;; *) echo "${oper} is not support!" return 0 ;; esac break done } calc echo "the result is: $?" # $? 获取 calc 函数返回值 # $ ./function-demo.sh # 1) + # 2) - # 3) * # 4) / # choose the oper: 3 # enter first num: 10 # enter second num: 10 # the result is: 100 ================================================ FILE: codes/shell/脚本函数/函数递归.sh ================================================ #!/usr/bin/env bash function factorial { if [ $1 -eq 1 ] then echo 1 else local temp=$[ $1 - 1 ] local result=`factorial $temp` echo $[ $result * $1 ] fi } read -p "Please input a value: " value result=`factorial $value` echo "The factorial of $value is: $result" ================================================ FILE: codes/shell/脚本函数/在函数中使用参数.sh ================================================ #!/usr/bin/env bash # passing parameters to a function function addem { if [ $# -eq 0 ] || [ $# -gt 2 ] then echo -1 elif [ $# -eq 1 ] then echo $[ $1 + $1 ] else echo $[ $1 + $2 ] fi } echo -n "Adding 10 and 15:" value=`addem 10 15` echo $value echo -n "Let's try adding just one number: " value=`addem 10` echo $value echo -n "Now trying adding no number: " value=`addem` echo $value echo -n "Finally, try adding three or more numbers: " value=`addem 10 15 20` echo $value ================================================ FILE: codes/shell/脚本函数/想函数传数组数据.sh ================================================ #!/usr/bin/env bash # trying to pass an array variable function testit { echo "The parameters are : $@" #函数只会读取数组变量的第一个值 thisarray=$1 echo "The received array is ${thisarray[*]}" local newarray newarray=( `echo "$@"` ) echo "The new array value is : ${newarray[*]}" } myarray=( 1 2 3 4 5 ) echo "The original array is : ${myarray[*]}" #将数组变量当成一个函数参数,函数只会去函数变量第一个值 #testit $myarray testit ${myarray[*]} ================================================ FILE: codes/shell/脚本函数/累加数组中的值.sh ================================================ #!/usr/bin/env bash #adding values in the array function addarray { local sum=0 local newarray newarray=( `echo "$@"` ) for value in ${newarray[*]} do sum=$[ $sum + $value ] done echo $sum } myarray=( 1 2 3 4 5 ) echo "The original array is : ${myarray[*]}" arg1=`echo ${myarray[*]}` result=`addarray $arg1` echo "The result is $result" ================================================ FILE: codes/shell/脚本函数/默认退出状态码.sh ================================================ #!/usr/bin/env bash # testing the exit status of a function func1() { echo "Trying to display a non-existent file" ls -l badfile } #由于最后一条命令未执行成功,返回的状态码非0 echo "testing the function" func1 echo "The exit status is : $?" func2() { ls -l badfile echo "Another test to display a non-existent file" } #由于最后一条命令echo执行成功,返回的状态码为0 echo "Another test" func2 echo "The exit status is : $?" ================================================ FILE: codes/shell/菜单/使用msgbox部件.sh ================================================ #!/usr/bin/env bash dialog --title text --msgbox "This is a test" 10 20 ================================================ FILE: codes/shell/菜单/使用select命令.sh ================================================ #!/usr/bin/env bash # using select in the menu function diskspace { clear df -k } function whoseon { clear who } function menusage { clear cat /proc/meminfo } PS3="Enter option:" select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program" do case $option in "Exit program") break ;; "Display disk space") diskspace ;; "Display logged on users") whoseon ;; "Display memory usage") menusage ;; *) clear echo "Sorry, wrong selection" ;; esac done clear ================================================ FILE: codes/shell/菜单/使用脚本菜单.sh ================================================ #!/usr/bin/env bash function menu { clear echo echo -e "\t\tSys Admin Menu\n" echo -e "\t1. Display disk space" echo -e "\t2. Display logged on users" echo -e "\t3. Display memory usage" echo -e "\t0. Exit program\n\n" echo -en "\t\tEnter option:" read -n 1 option } function diskspace { clear df -k } function whoseon { clear who } function menusage { clear cat /proc/meminfo } while [ 1 ] do menu case $option in 0) break ;; 1) diskspace ;; 2) whoseon ;; 3) menusage ;; *) clear echo "Sorry, wrong selection" ;; esac echo -en "\n\n\t\tHit any key to continue" read -n 1 line done clear ================================================ FILE: codes/shell/菜单/在脚本中使用dialog命令.sh ================================================ #!/usr/bin/env bash # using dialog to create a menu temp=`mktemp -t test.XXXXXX` temp2=`mktemp -t test2.XXXXXX` function diskspace { df -k > $temp dialog --textbox $temp 20 60 } function whoseon { who > $temp dialog --textbox $temp 20 50 } function menusage { cat /proc/meminfo > $temp dialog --textbox $temp 20 50 } while [ 1 ] do dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2 if [ $? -eq 1 ] then break fi selection=`cat $temp2` case $selection in 1) diskspace ;; 2) whoseon ;; 3) menusage ;; 0) break ;; *) dialog --msgbox "Sorry,invalid selection" 10 30 esac done rm -f $temp 2> /dev/null rm -f $temp2 2> /dev/null ================================================ FILE: codes/shell/输入和输出/test ================================================ ls: cannot access badtest: No such file or directory but this should go to the testerror file ================================================ FILE: codes/shell/输入和输出/test1 ================================================ This is the end of the script ================================================ FILE: codes/shell/输入和输出/临时重定向.sh ================================================ #!/usr/bin/env bash # testing STDERR messages echo "This is an error " >&2 echo "This is another error" echo "This is also an error" >&2 ================================================ FILE: codes/shell/输入和输出/使用getopts.sh ================================================ #!/usr/bin/env bash # simple demonstration of the getopts command while getopts :ab:c opt do case "$opt" in a) echo "Found the -a option" ;; b) echo "Found the -b option, with value $OPTARG" ;; c) echo "Found the -c option" ;; *) echo "Unknown option:$opt" ;; esac done ================================================ FILE: codes/shell/输入和输出/使用getopts处理选项和参数.sh ================================================ #!/usr/bin/env bash # processing options and parameters with getopts while getopts :ab:cd opt do case "$opt" in a) echo "Found the -a option" ;; b) echo "Found the -b option,with value $OPTARG" ;; c) echo "Found the -c option" ;; d) echo "Found the -d option" ;; *) echo "Unknown option: $opt" ;; esac done shift $[ $OPTIND - 1 ] count=1 for param in "$@" do echo "Parameter $count: $param" count=$[ $count + 1 ] done ================================================ FILE: codes/shell/输入和输出/使用getopt命令.sh ================================================ #!/usr/bin/env bash #extracting command line options and values with getopt # getopt command is not goot at dealing with space,we can use getopts set -- `getopt -q ab:c "$@"` while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) param="$2" echo "Found the -b option,with parameter value $param" shift ;; -c) echo "Found the -c option" ;; --) shift break ;; *) echo "$1 is not an option" ;; esac shift done count=1 for param in "$@" do echo "Parameter #$count: $param" count=$[ $count + 1 ] done ================================================ FILE: codes/shell/输入和输出/使用shift命令.sh ================================================ #!/usr/bin/env bash # shift n 移动变量 count=1 while [ -n "$1" ] do echo "Parameter #$count = $1" count=$[ $count + 1 ] shift done echo -e "\n" # demonstrating a multi-position shift echo "The original parameters : $*" shift 2 echo "Here's the new first parameter: $1" ================================================ FILE: codes/shell/输入和输出/免密码传输.sh ================================================ #!/usr/bin/env bash # ------------------------------------------------------------------------------ # 免密码传输 # @author Zhang Peng # @since 2020/4/8 # ------------------------------------------------------------------------------ REMOTE_HOST=192.168.0.2 # 如果本机没有公私钥对,可以执行以下命令生成 ssh 公私钥对 #ssh-keygen -t rsa # 服务器 A 上执行以下命令 scp ~/.ssh/id_rsa.pub root@${REMOTE_HOST}:~/.ssh/id_rsa.pub.tmp # 服务器 B 上执行以下命令 cat ~/.ssh/id_rsa.pub.tmp >> ~/.ssh/authorized_keys rm ~/.ssh/id_rsa.pub.tmp ================================================ FILE: codes/shell/输入和输出/分离参数和选项.sh ================================================ #!/usr/bin/env bash #extracting options and parameters while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) echo "Found the -b option" ;; -c) echo "Found the -c option" ;; --) shift break ;; *) echo "$1 is not an option" ;; esac shift done count=1 for param in $@ do echo "Parameter #$count: $param" count=$[ $count + 1 ] done ================================================ FILE: codes/shell/输入和输出/列出当前脚本打开的文件描述符.sh ================================================ #!/usr/bin/env bash # testing lsof with file descriptors exec 3> test exec 6> test exec 7< test lsof -a -p $$ -d0,1,2,3,6,7 ================================================ FILE: codes/shell/输入和输出/创建自己的重定向/从以重定向的文件描述符中恢复.sh ================================================ #!/usr/bin/env bash #storing STDOUT, then coming back to it exec 3>&1 exec 1> test echo "This should store in output file" echo "along with this line" exec 1>&3 echo "Now things should be back to normal" ================================================ FILE: codes/shell/输入和输出/创建自己的重定向/关闭文件描述符.sh ================================================ #!/usr/bin/env bash # testing closing file descriptors exec 3> test echo "This is a test line of data" >&3 # closing file descriptor exec 3>&- echo "This won't work" >&3 cat test #覆盖前一个test文件 exec 3> test echo "This'll be bad" >&3 ================================================ FILE: codes/shell/输入和输出/创建自己的重定向/创建读写文件描述符.sh ================================================ #!/usr/bin/env bash # testing inpiut/output file descriptor exec 3<> test read line <&3 echo "Read: $line" echo "This is the test line" >&3 ================================================ FILE: codes/shell/输入和输出/创建自己的重定向/创建输入文件描述符.sh ================================================ #!/usr/bin/env bash # redirecting input file descriptors exec 3>&1 echo "This is the 3 file descriptor" >&3 exec 6>&0 exec 0< test count=1 while read line do echo "Line #$count: $line" count=$[ $count + 1 ] done exec 0<&6 read -p "Are you done now?" answer case $answer in Y | y) echo "Goodbye" ;; N | n) echo "Sorry, this is the end" ;; esac ================================================ FILE: codes/shell/输入和输出/创建自己的重定向/创建输出文件描述符.sh ================================================ #!/usr/bin/env bash # using an alternative file descriptor exec 3> test echo "This should display on the monitor" echo "and this should be stored in the file" >&3 echo "Then this should be back on the monitor" ================================================ FILE: codes/shell/输入和输出/参数计数.sh ================================================ #!/usr/bin/env bash # getting the number of parameters echo There were $# parameters supplied #花括号里不能使用美元符号 params=$# echo The last parameter is $params echo The last parameter is ${!#} ================================================ FILE: codes/shell/输入和输出/在脚本中使用重定向输入.sh ================================================ #!/usr/bin/env bash # redirecting the inpiut # 从test中读取数据,而不是从STDIN中读取数据 exec 0< test count=1 while read line do echo "Line #$count : $line " count=$[ $count + 1 ] done ================================================ FILE: codes/shell/输入和输出/处理带值的选项.sh ================================================ #!/usr/bin/env bash # extracting command line options and values while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) param="$2" echo "Found the -b option, with parameter value $param" shift ;; -c) echo "Found the -c option" ;; --) shift break ;; *) echo "$1 is not an option" ;; esac shift done count=1 for param in "$@" do echo "Parameter #$count : $param" count=$[ $count + 1 ] done ================================================ FILE: codes/shell/输入和输出/处理简单选项.sh ================================================ #!/usr/bin/env bash # extracting command line options as parameters while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) echo "Found the -b optins" ;; -c) echo "Found the -c optins" ;; *) echo "$1 is not a valid options" ;; esac shift done ================================================ FILE: codes/shell/输入和输出/快速清除文件或日志.sh ================================================ #!/usr/bin/env bash # 清除日志 cat /dev/null > [Logname] ================================================ FILE: codes/shell/输入和输出/抓取所有数据.sh ================================================ #!/usr/bin/env bash # testing $* and $@ count=1 for param in "$*" do echo "\$* Parameter #$count = $param" count=$[ $count + 1 ] done count=1 for param in "$@" do echo "\$@ Paramenter #$count = $param" count=$[ $count + 1 ] done ================================================ FILE: codes/shell/输入和输出/永久重定向.sh ================================================ #!/usr/bin/env bash # testing STDERR messages # redirecting all to a file # 脚本执行期间,用exec命令告诉shell重定向某个特定文件描述符 exec 2> test ls badtest echo "This is test of redirecting all output" echo "from a script to another file" exec 1> test1 echo "This is the end of the script" echo "but this should go to the testerror file" >&2 ================================================ FILE: codes/shell/输入和输出/测试.txt ================================================ ================================================ FILE: codes/shell/输入和输出/获取用户输入.sh ================================================ #!/usr/bin/env bash # testing the reading command echo -n "Enter your name:" read name echo "Hello $name, welcome to my program" read -p "Please enter your age: " age days=$[ $age * 365 ] echo "That makes you over $days days old" #指定多个变量,输入的每个数据值都会分配给表中的下一个变量,如果用完了,就全分配各最后一个变量 read -p "Please enter name:" first last echo "Checking data for $last. $first..." #如果不指定变量,read命令就会把它收到的任何数据都放到特殊环境变量REPLY中 read -p "Enter a number:" factorial=1 for (( count = 1; count <= $REPLY; count ++ )) do factorial=$[ $factorial * $count ] done echo "The factorial of $REPLY is $factorial" ================================================ FILE: codes/shell/输入和输出/记录信息.sh ================================================ #!/usr/bin/env bash # using the tee command for logging #将输入一边发送到STDOUT,一边发送到日志文件 tempfile=test echo "This is the start of the test" | tee $tempfile echo "This is the second line of the test" | tee -a $tempfile echo "This is the end line of the test" | tee -a $tempfile ================================================ FILE: codes/shell/输入和输出/读取参数.sh ================================================ #!/usr/bin/env bash # using one command line parameter factorial=1 for (( number = 1; number <= $1; number ++ )) do factorial=$[ $factorial * $number ] done echo The factor of $1 is $factorial ================================================ FILE: codes/shell/输入和输出/读取多个命令行参数.sh ================================================ #!/usr/bin/env bash # handing lots of parameters total=$[ ${10} * ${11} ] echo The tenth parameter is ${10} echo The eleventh parameter is ${11} echo The total is $total ================================================ FILE: codes/shell/输入和输出/读取程序名.sh ================================================ #!/usr/bin/env bash # testing the $0 parameter echo The command entered is $0 #当传给$0变量的真实字符串是整个脚本的路径是,程序中就会使用整个路径,而不仅仅是程序名 name=`basename $0` echo The command entered is $name ================================================ FILE: codes/shell/输入和输出/读取选择参数.sh ================================================ #!/usr/bin/env bash ############################################################## # 很多场景下,我们需要在执行脚本时,输入指定的参数,脚本会 # 根据参数执行不同的行为。这就需要读取脚本参数并校验。 ############################################################## ################### 读取脚本输入参数并校验 ################### declare -a serial serial=( start stop restart ) echo -n "请选择操作(可选值:start|stop|restart):" read oper if ! echo ${serial[@]} | grep -q ${oper}; then echo "请选择正确操作(可选值:start|stop|restart)" exit 1 fi declare -a serial2 serial2=( dev test prod ) echo -n "请选择运行环境(可选值:dev|test|prod):" read profile if ! echo ${serial2[@]} | grep -q ${profile}; then echo "请选择正确运行环境(可选值:dev|test|prod)" exit 1 fi ================================================ FILE: codes/shell/输入和输出/超时和输入计数.sh ================================================ #!/usr/bin/env bash # timing the data entry if read -t 5 -p "Please enter your name:" name then echo "Hello, $name, welcome to my script" else #起到换行的作用 echo #输入计数 -n1 read -n1 -p "Do you want to continue [Y/N]?" answer case $answer in Y | y) echo echo "Fine, continue on..." ;; N | n) echo echo "OK,goodbye" ;; *) echo echo "OK, wrong, goodbye" esac echo "Sorry, this is the end of the script" fi ================================================ FILE: codes/shell/输入和输出/隐藏方式读取数据.sh ================================================ #!/usr/bin/env bash # hiding input data from monitor read -s -p "Please enter your password: " pass #添加了-s选项之后,不会自动换行,不添加-s 会自动换行 echo echo "Is your password really $pass?" ================================================ FILE: codes/shell/进阶脚本/创建捕捉脚本.sh ================================================ #!/usr/bin/env bash # # Capture_Stats - Gather System Performance Statistics # ######################################################### # # Set Script Variables # REPORT_FILE=/home/tiandi/Documents/capstats.csv DATE=`date +%m/%d/%y` TIME=`date +%k:%M:%S` # ############################################################ # USERS=`uptime | sed 's/user.*$//' | gawk '{print $NF}'` LOAD=`uptime | gawk '{print $NF}'` # FREE=`vmstat 1 2 | sed -n '/[0-9]/p' | sed -n '2p' | gawk '{print $4}'` IDLE=`vmstat 1 2 | sed -n '/[0-9]/p' | sed -n '2p' | gawk '{print $15}'` # ########################################## # echo "$DATE,$TIME,$USERS,$LOAD,$FREE,$IDLE" >> $REPORT_FILE # ================================================ FILE: codes/shell/进阶脚本/开启debug模式.sh ================================================ #!/usr/bin/env bash # 开启 debug set -x for (( i = 0; i < 3; i ++ )); do printf ${i} done # 关闭 debug set +x # Output: # + (( i = 0 )) # + (( i < 3 )) # + printf 0 # 0+ (( i++ )) # + (( i < 3 )) # + printf 1 # 1+ (( i++ )) # + (( i < 3 )) # + printf 2 # 2+ (( i++ )) # + (( i < 3 )) # + set +x for i in {1..5}; do printf ${i}; done printf "\n" # Output: 12345 ================================================ FILE: codes/shell/进阶脚本/查看uptime获取在线用户数.sh ================================================ #!/usr/bin/env bash # uptime | sed 's/user.*$//' | gawk '{print $NF}' ================================================ FILE: codes/shell/进阶脚本/生成报告脚本-基于创建捕捉脚本.sh ================================================ #!/usr/bin/env bash # # Report_Stats - Generates Rpt from Captured Perf Stats # ############################################################ # # Set Script Variables # REPORT_FILE=/home/tiandi/Documents/capstats.csv TEMP_FILE=/home/tiandi/Documents/capstats.html # DATE=`date +%m/%d/%y` # MAIL=`which mutt` MAIL_TO=tiandi # ###############################################################3 # # Create Report Header # echo "

Report for $DATE

" > $TEMP_FILE echo "" >> $TEMP_FILE echo "" >> $TEMP_FILE echo "" >> $TEMP_FILE # ############################################################### # # Place Performance Stats in Report # cat $REPORT_FILE | gawk -F, '{ printf "", $1, $2, $3; printf "\n\n", $4, $5, $6; }' >> $TEMP_FILE # echo "
DateTimeUsersLoadFree Memory%CPU Idle
%s%s%s%s%s%s
" >> $TEMP_FILE # ################################################################ # # Mail Performance Report & Clean up # #$MAIL -a $TEMP_FILE -s "Performance Report $DATE" #-- $MAIL_TO < /dev/null # #rm -r $TEMP_FILE # ================================================ FILE: codes/shell/进阶脚本/系统快照报告.sh ================================================ #!/usr/bin/env bash # # Snapshot_Stats - produces a report for system stats # ############################################## # # Set Script Variables # DATE=`date +%m%d%y` DISKS_TO_MONITOR="/dev/sda1" MAIL=`which mutt` MAIL_TO=tiandi REPORT=/home/tiandi/Documents/Snapshot_Stats_$DATE.rpt # #################################################### # # Create Report File # exec 3>&1 # Save file descriptor # exec 1> $REPORT # direct output to rpt file # ################################################### # echo echo -e "\t\tDaily System Report" echo # ################################################### # Date Stamp the Report # echo -e "Today is" `date +%m%d%y` echo # ################################################## # #1) Gather System Uptime Statistics # echo -e "System has been \c" uptime | sed -n '/,/s/,/ /gp' | gawk '{if($4 == "days" || $4 == "day") {print $2,$3,$4,$5} else {print $2,$3}}' # ################################################# # #2) Gather Disk Usage Statistics # echo for DISK in $DISK_TO_MONITOR # loop to check disk space do echo -e "$DISK usage: \c" df -h $DISK | sed -n '/% \//p' | gawk '{ print $5 }' done # ################################################################## # #3) Gather Memory Usage Statstics # echo echo -e "Memory Usage: \c" # free | sed -n '2p' | gawk 'x = int(($3 / $2) * 100) {print x}' | sed 's/$/%/' # ############################################################### # #4) Gather Number of Zombie Processes # echo ZOMBIE_CHECK=`ps -al | gawk '{print $2,$4}' | grep Z` # if [ "$ZOMBIE_CHECK" = "" ] then echo "No Zombie Process on System at this Time" else echo "Current System Zombie Processes" ps -al | gawk '{print $2,$4}' | grep Z fi echo # ##################################################################### # # Restore File Descriptor & Mail Report # exec 1>&3 # Restore output to STDOUT # #$MAIL -a $REPORT -s "System Sstatistics Report for $DATE" #-- $MAIL_TO < /dev/null # ############################################################### # # Clean up # #rm -f $REPORT # ================================================ FILE: codes/shell/进阶脚本/输出颜色.sh ================================================ #!/usr/bin/env bash #输出特效格式控制 #${RESET} 关闭所有属性 #\033[1m 设置高亮度 #\03[4m 下划线 #\033[5m 闪烁 #\033[7m 反显 #\033[8m 消隐 #\033[30m -- \033[37m 设置前景色 #\033[40m -- \033[47m 设置背景色 #光标位置等的格式控制 #\033[nA 光标上移n行 #\03[nB 光标下移n行 #\033[nC 光标右移n行 #\033[nD 光标左移n行 #\033[y;xH设置光标位置 #\033[2J 清屏 #\033[K 清除从光标到行尾的内容 #\033[s 保存光标位置 #\033[u 恢复光标位置 #\033[?25l 隐藏光标 #\33[?25h 显示光标 #整理: # 编码 颜色/动作 #  0 重新设置属性到缺省设置 #  1 设置粗体 #  2 设置一半亮度(模拟彩色显示器的颜色) #  4 设置下划线(模拟彩色显示器的颜色) #  5 设置闪烁 #  7 设置反向图象 #  22 设置一般密度 #  24 关闭下划线 #  25 关闭闪烁 #  27 关闭反向图象 #  30 设置黑色前景 #  31 设置红色前景 #  32 设置绿色前景 #  33 设置棕色前景 #  34 设置蓝色前景 #  35 设置紫色前景 #  36 设置青色前景 #  37 设置白色前景 #  38 在缺省的前景颜色上设置下划线 #  39 在缺省的前景颜色上关闭下划线 #  40 设置黑色背景 #  41 设置红色背景 #  42 设置绿色背景 #  43 设置棕色背景 #  44 设置蓝色背景 #  45 设置紫色背景 #  46 设置青色背景 #  47 设置白色背景 #  49 设置缺省黑色背景 #特效可以叠加,需要使用“;”隔开,例如:闪烁+下划线+白底色+黑字为 \033[5;4;47;30m闪烁+下划线+白底色+黑字为${RESET} RESET="$(tput sgr0)" BLACK="\033[1;30m" RED="\033[1;31m" GREEN="\033[1;32m" YELLOW="\033[1;33m" BLUE="\033[1;34m" PURPLE="\033[1;35m" CYAN="\033[1;36m" WHITE="\033[1;37m" # 示例: echo -e "${BLACK} 黑色字 ${RESET}" echo -e "${RED} 红色字 ${RESET}" echo -e "${GREEN} 绿色字 ${RESET}" echo -e "${YELLOW} 黄色字 ${RESET}" echo -e "${BLUE} 蓝色字 ${RESET}" echo -e "${PURPLE} 紫色字 ${RESET}" echo -e "${CYAN} 天蓝字 ${RESET}" echo -e "${WHITE} 白色字 ${RESET}" # 字背景颜色范围:40-47 echo -e "\033[40;37m 黑底白字 ${RESET}" echo -e "\033[41;30m 红底黑字 ${RESET}" echo -e "\033[42;34m 绿底蓝字 ${RESET}" echo -e "\033[43;34m 黄底蓝字 ${RESET}" echo -e "\033[44;30m 蓝底黑字 ${RESET}" echo -e "\033[45;30m 紫底黑字 ${RESET}" echo -e "\033[46;30m 天蓝底黑字 ${RESET}" echo -e "\033[47;34m 白底蓝字 ${RESET}" #控制选项说明 #${RESET} 关闭所有属性 #\033[1m 设置高亮度 #\033[4m 下划线 echo -e "\033[4;31m 下划线红字 ${RESET}" #闪烁 echo -e "\033[5;34m 红字在闪烁 ${RESET}" #反影 echo -e "\033[8m 消隐 ${RESET} " #\033[30m-\033[37m 设置前景色 #\033[40m-\033[47m 设置背景色 #\033[nA光标上移n行 #\033[nB光标下移n行 echo -e "\033[4A 光标上移4行 ${RESET}" #\033[nC光标右移n行 #\033[nD光标左移n行 #\033[y;xH设置光标位置 #\033[2J清屏 #\033[K清除从光标到行尾的内容 echo -e "\033[K 清除光标到行尾的内容 ${RESET}" #\033[s 保存光标位置 #\033[u 恢复光标位置 #\033[?25| 隐藏光标 #\033[?25h 显示光标 echo -e "\033[?25l 隐藏光标 ${RESET}" echo -e "\033[?25h 显示光标 ${RESET}" ================================================ FILE: codes/shell/进阶脚本/问题跟踪数据库/Update_Problem.sh ================================================ #!/usr/bin/env bash # # Update_Problem - updates problem record in database # ############################################################ # # Determine sql location & set variable # MYSQL=`which mysql`" Problem_Trek -u root" # ############################################################## # # Obtain Record Id # if [ $# -eq 0 ] # Check if id number was passed then # If not passed ask for it # # Check if any unfinished records exist. RECORDS_EXIST=`$MYSQL -Bse 'SELECT id_number FROM problem_logger where fixed_date="0000-00-00" OR prob_solutions=""'` # if [ "$RECORDS_EXIST" != "" ] then echo echo "The following record(s) need updating..." $MYSQL << EOF SELECT id_number, report_date, prob_symptoms FROM problem_logger WHERE fixed_date="0000-00-00" OR prob_solutions=""\G EOF fi # echo echo "What is the ID number for the" echo -e "problem you want to update?: \c" read ANSWER ID_NUMBER=$ANSWER else ID_NUMBER=$1 fi # ########################################################## # # Obtain Solution (aka Fixed) Date # echo echo -e "Was Problem solved today? (y/n) \c" read ANSWER # case $ANSWER in y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) # FIXED_DATE=`date +%Y%m%d` ;; *) # if answer is anything but "yes", ask for date echo echo -e "What was the date of resolution? [YYYYMMDD] \c" read ANSWER # FIXED_DATE=$ANSWER ;; esac # ######################################################## # # Acquire problem solution # echo echo -e "Birefly describe the problem solution: \c" # read ANSWER PROB_SOLUTIONS=$ANSWER # ######################################################## # # Update problem record # echo echo "Problem record updated as follows:" echo $MYSQL << EOF UPDATE problem_logger SET prob_solutions="$PROB_SOLUTIONS", fixed_date=$FIXED_DATE where id_number=$ID_NUMBER; SELECT * FROM problem_logger WHERE id_number=$ID_NUMBER\G EOF ================================================ FILE: codes/shell/进阶脚本/问题跟踪数据库/查找问题.sh ================================================ #!/usr/bin/env bash # # Find_Problem - finds problem records using keywords # ########################################################### # # Determine sql location & set variable # MYSQL=`which mysql`" Problem_Trek -u root" # ########################################################## # # Obtain Keyword(s) # if [ -n "$1" ] # Check if a keyword was passed then # Grab all the passed keywords # KEYWORDS=$@ # Grab all the params as separate words, same string # else # Keyword(s) not passed, Ask for them echo echo "What keywords would you like to search for?" echo -e "Please separate words by a space: \c" read ANSWER KEYWORDS=$ANSWER fi # ####################################################### # # Find problem record # echo echo "The following was found using keywords: $KEYWORDS" echo # KEYWORDS=`echo $KEYWORDS | sed 's/ /|/g'` # $MYSQL << EOF SELECT * FROM problem_logger WHERE prob_symptoms REGEXP '($KEYWORDS)' OR prob_solutions REGEXP '($KEYWORDS)'\G EOF # ================================================ FILE: codes/shell/进阶脚本/问题跟踪数据库/记录问题.sh ================================================ #!/usr/bin/env bash # # Record_Problem - records system problems in database # ########################################################### # # Determine mysql location & put into variable # MYSQL=`which mysql`" Problem_Trek -u root" # ########################################################### # # Create Record Id & Report_Date # #ID_NUMBER=`date +%y%m%d%H%M` # REPORT_DATE=`date +%y%m%d` # ############################################################ # # Acquire information to put into table # echo echo -e "Birefly describe the problem & its symptoms: \c" # read ANSWER PROB_SYMPTOMS=$ANSWER # # Set Fixed Date & Problem Solution to null for now # FIXED_DATE=0 PROB_SOLUTIONS="" # ############################################################# # # Insert acquired information into table # echo echo "Problem recorded as follows:" echo id=$($MYSQL -e "INSERT INTO problem_logger VALUES (null,$REPORT_DATE,$FIXED_DATE,'$PROB_SYMPTOMS','$PROB_SOLUTIONS');SELECT LAST_INSERT_ID() id") id=`echo $id | gawk '{print $2}'` $MYSQL << EOF SELECT * FROM problem_logger where id_number=$id\G EOF # ############################################################# # # Check if want to enter a solution now # echo echo -e "Do you have a solution yet?(y/n) \c" read ANSWER # case $ANSWER in y | Y | YES | yes | Yes | yEs | yeS | YEs | yES) ./Update_Problem.sh $id # ;; *) # if answer is anything but yes, just exit script ;; esac # ############################################################ ================================================ FILE: codes/shell/逻辑控制/bash-shell无法处理浮点数.sh ================================================ #!/usr/bin/env bash #bash shell 仅能处理浮点数值,test命令无法处理val1变量中存储的浮点值 #testing floating point numbers val1=`echo "scale=4; 10 / 3" | bc` echo "The test value is $val1" if [ $val1 -gt 3 ] then echo "The result is larger than 3" fi ================================================ FILE: codes/shell/逻辑控制/break示例.sh ================================================ #!/usr/bin/env bash ################### 使用break跳出外部循环 ################### # 查找 10 以内第一个能整除 2 和 3 的正整数 i=1 while [[ ${i} -lt 10 ]]; do if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then echo ${i} break; fi i=`expr ${i} + 1` done # Output: 6 ================================================ FILE: codes/shell/逻辑控制/case示例.sh ================================================ #!/usr/bin/env bash echo "input param: " $1 $2 $3 x=0 if [[ -n $1 ]]; then x=$1 fi oper="" if [[ -n $2 ]]; then oper=$2 fi y=0 if [[ -n $3 ]]; then y=$3 fi exec case ${oper} in + | add) val=`expr ${x} + ${y}` echo "${x} + ${y} = ${val}" ;; - | sub) val=`expr ${x} - ${y}` echo "${x} - ${y} = ${val}" ;; * | mul) val=`expr ${x} \* ${y}` echo "${x} * ${y} = ${val}" ;; / | div) val=`expr ${x} / ${y}` echo "${x} / ${y} = ${val}" ;; *) echo "Unknown oper!" ;; esac ================================================ FILE: codes/shell/逻辑控制/continue示例.sh ================================================ #!/usr/bin/env bash # 打印10以内的奇数 for (( i = 0; i < 10; i ++ )); do if [[ $((i % 2)) -eq 0 ]]; then continue fi echo ${i} done # Output: # 1 # 3 # 5 # 7 # 9 # 多重循环中的 continue 用法 for (( a = 1; a <= 5; a ++ )) do echo "Iteration $a:" for (( b = 1; b < 3; b ++ )) do if [[ $a -gt 2 ]] && [[ $a -lt 4 ]] then continue 2 fi var3=$[ $a * $b ] echo " The result of $a * $b is $var3" done done ================================================ FILE: codes/shell/逻辑控制/for循环示例.sh ================================================ #!/usr/bin/env bash ################### for 语句 ################### echo "print 0 to 9" for (( j = 0; j < 10; j ++ )); do echo ${j} done # Output: # print 0 to 9 # 0 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 ################### for in 语句 ################### echo "print 1 to 5" for i in {1..5}; do echo ${i}; done # Output: # print 1 to 5 # 1 # 2 # 3 # 4 # 5 ################### for in 语句遍历文件 ################### DIR=/home/zp for FILE in ${DIR}/*.sh; do mv "$FILE" "${DIR}/scripts" done # 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts ################### 在 for 语句中使用多个变量 ################### for (( x = 1 , y = 10; x <= y; x ++ , y -- )) do echo "$y - $x = $(($y - $x))" done ################### 嵌套 for 循环 ################### for (( x = 1; x <= 3; x ++ )) do echo "Starting loop $x:" for (( y = 1; y <= 3; y ++ )) do echo "Inside loog: $y:" done done #Output #Starting loop 1: #Inside loog: 1: #Inside loog: 2: #Inside loog: 3: #Starting loop 2: #Inside loog: 1: #Inside loog: 2: #Inside loog: 3: #Starting loop 3: #Inside loog: 1: #Inside loog: 2: #Inside loog: 3: ================================================ FILE: codes/shell/逻辑控制/if-elif-else示例.sh ================================================ #!/usr/bin/env bash ################### if 语句 ################### # 写成一行 if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi # Output: 1 -eq 1 result is: true # 写成多行 if [[ "abc" -eq "abc" ]] then echo ""abc" -eq "abc" result is: true" fi # Output: abc -eq abc result is: true ################### if else 语句 ################### if [[ 2 -ne 1 ]]; then echo "true" else echo "false" fi # Output: true ################### if elif else 语句 ################### x=10 y=20 if [[ ${x} > ${y} ]]; then echo "${x} > ${y}" elif [[ ${x} < ${y} ]]; then echo "${x} < ${y}" else echo "${x} = ${y}" fi # Output: 10 < 20 ================================================ FILE: codes/shell/逻辑控制/output.txt ================================================ /home/tiandi/Desktop is a directory /home/tiandi/Documents is a directory /home/tiandi/Downloads is a directory /home/tiandi/examples.desktop is a file /home/tiandi/lamp is a directory /home/tiandi/Music is a directory /home/tiandi/Pictures is a directory /home/tiandi/Public is a directory /home/tiandi/sh is a directory /home/tiandi/Templates is a directory /home/tiandi/test is a directory /home/tiandi/Videos is a directory ================================================ FILE: codes/shell/逻辑控制/select菜单示例.sh ================================================ #!/usr/bin/env bash PS3="Choose the package manager: " select ITEM in bower npm gem pip do echo -n "Enter the package name: " && read PACKAGE case ${ITEM} in bower) bower install ${PACKAGE} ;; npm) npm install ${PACKAGE} ;; gem) gem install ${PACKAGE} ;; pip) pip install ${PACKAGE} ;; esac break # 避免无限循环 done ================================================ FILE: codes/shell/逻辑控制/until示例.sh ================================================ #!/usr/bin/env bash x=0 until [[ ${x} -ge 5 ]]; do echo ${x} x=`expr ${x} + 1` done # Output: # 0 # 1 # 2 # 3 # 4 x=0 until echo $x [[ $x -ge 5 ]] do x=$[ $x + 1 ] done # Output: # 0 # 1 # 2 # 3 # 4 # 5 ================================================ FILE: codes/shell/逻辑控制/while循环示例.sh ================================================ #!/usr/bin/env bash ################### while 循环输出 0 ~ 9 的平方数 ################### x=0 while [[ ${x} -lt 10 ]]; do echo $((x * x)) x=$((x + 1)) done # Output: # 0 # 1 # 4 # 9 # 16 # 25 # 36 # 49 # 64 # 81 ################### while 循环输出 0 ~ 9 ################### x=0 while echo ${x} [[ ${x} -lt 9 ]] do x=$((x + 1)) done # Output: # 0 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 ################### while 循环嵌套 for 循环 ################### x=5 while [[ $x -ge 0 ]] do echo "Outer loop: $x" for (( y = 1; $y < 3; y ++ )) do z=$[ $x * $y ] echo "Inner loop: $x * $y = $z" done x=$[ $x - 1 ] done ================================================ FILE: codes/shell/逻辑控制/从命令读取值.sh ================================================ #!/usr/bin/env bash #reading values from a file file="state" #更改字段分隔符,使其只能识别换行符 IFS=$'\n' #处理长脚本是,在一个地方修改了该值,然后可能忘了修改过该值 #IFS.OLD=$IFS #IFS=$'\n' #具体代码 #IFS=$IFS.OLD for state in `cat $file` do echo "Visit beautiful $state" done ================================================ FILE: codes/shell/逻辑控制/使用双圆括号.sh ================================================ #!/usr/bin/env bash # using double parenthesis var1=10 if (($var1 ** 2 > 90)) then ((var2 = $var1 ** 2)) echo "The square of $var1 if $var2" fi ================================================ FILE: codes/shell/逻辑控制/使用双方括号.sh ================================================ #!/usr/bin/env bash # using pattern matching if [[ $USER == r* ]] then echo "Hello $USER" else echo "Sorry, I do not know you" fi ================================================ FILE: codes/shell/逻辑控制/使用大于小于号.sh ================================================ #!/usr/bin/env bash # 大于小于号必须转义,否则shell会将它们当做重定向符号而把字符串值当做文件名处理 # 大于小于号顺序和sort命令所采用的有所不同 # mis-using string comparisons val1=baseball val2=hockey ################### 错误使用大于小于号 ################## if [[ $val1 > $val2 ]] then echo "$val1 is greater than $val2" else echo "$val1 is less than $val2" fi ################### 正确使用大于小于号 ################### if [[ $val1 \> $val2 ]] then echo "$val1 is greater than $val2" else echo "$val1 is less than $val2" fi ================================================ FILE: codes/shell/逻辑控制/使用嵌套循环并修改IFS.sh ================================================ #!/usr/bin/env bash #changing the IFS value IFS.OLD=$IFS IFS=$'\n' for entry in `cat /etc/passwd` do echo "Values in $entry -" IFS=: for value in $entry do echo " $value" done done ================================================ FILE: codes/shell/逻辑控制/使用管道或重定向.sh ================================================ #!/usr/bin/env bash # redirecting the for output to a file for file in /home/tiandi/* do if [ -d "$file" ] then echo "$file is a directory" else echo "$file is a file" fi done > output.txt # piping a loop to another command for state in "North Dakota" Connecticut do echo "$state is next place to go" done | sort echo "This completes our travels" ================================================ FILE: codes/shell/逻辑控制/使用通配符处理目录.sh ================================================ #!/usr/bin/env bash #iterate through all the files in a directory for file in /home/tiandi/test/* do if [ -d "$file" ] then echo "$file is a directory" elif [ -f "$file" ] then echo "$file is a file" fi done ================================================ FILE: codes/shell/逻辑控制/注意test大小写顺序和sort不同.sh ================================================ #!/usr/bin/env bash #test命令中,大小字母会被当成小于小写字符,而在sort中,小写字母会先出现,test使用标准的ASCII排序,sort使用本地化语言设置进行排序,对于英语,本地化设置制定了排序顺序中小写字母出现在大写字母之前 var1=Testing var2=testing if [ $val1 \> $val2 ] then echo '$val1 is greater than $val2' else echo '$val1 is less than $val2' fi ================================================ FILE: codes/shell/逻辑控制/读取里表中复杂的值.sh ================================================ #!/usr/bin/env bash # another example of how not to use the for command for test in I don't know if this'll work do echo "word:$test" done ================================================ FILE: docs/.remarkrc ================================================ { "plugins": [ "preset-lint-recommended", "preset-lint-consistent", ["lint-list-item-indent", "space"], ["lint-heading-style", false] ] } ================================================ FILE: docs/.textlint.terms.json ================================================ [ "Stylus", "VuePress", [ "front[- ]matter", "frontmatter" ] ] ================================================ FILE: docs/.textlintrc.js ================================================ module.exports = { rules: { '@textlint-rule/no-unmatched-pair': true, apostrophe: true, 'common-misspellings': true, diacritics: true, 'en-capitalization': { allowHeading: false }, 'stop-words': { severity: 'warning' }, terminology: { terms: `${__dirname}/.textlint.terms.json` }, 'write-good': { severity: 'warning' } }, filters: { comments: true } } ================================================ FILE: docs/.vuepress/config.js ================================================ /** * @see https://vuepress.vuejs.org/zh/ */ module.exports = { port: '4000', dest: 'dist', base: '/linux-tutorial/', title: 'LINUX-TUTORIAL', description: '数据库教程', head: [['link', { rel: 'icon', href: `/favicon.ico` }]], markdown: { externalLinks: { target: '_blank', rel: 'noopener noreferrer', }, }, themeConfig: { logo: '/images/dunwu-logo-100.png', repo: 'dunwu/linux-tutorial', repoLabel: 'Github', docsDir: 'docs', docsBranch: 'master', editLinks: true, smoothScroll: true, locales: { '/': { label: '简体中文', selectText: 'Languages', editLinkText: '帮助我们改善此页面!', lastUpdated: '上次更新', nav: [ { text: 'Linux 命令', link: '/linux/cli/', }, { text: 'Linux 运维', link: '/linux/ops/', }, { text: 'Linux 软件运维', link: '/linux/soft/', }, { text: 'Docker 教程', link: '/docker/', }, { text: '🎯 博客', link: 'https://github.com/dunwu/blog', target: '_blank', rel: '', }, ], sidebar: 'auto', sidebarDepth: 2, }, }, }, plugins: [ [ '@vuepress/active-header-links', { sidebarLinkSelector: '.sidebar-link', headerAnchorSelector: '.header-anchor', }, ], ['@vuepress/back-to-top', true], [ '@vuepress/pwa', { serviceWorker: true, updatePopup: true, }, ], [ '@vuepress/last-updated', { transformer: (timestamp, lang) => { // 不要忘了安装 moment const moment = require('moment') moment.locale(lang) return moment(timestamp).fromNow() }, }, ], ['@vuepress/medium-zoom', true], [ 'container', { type: 'vue', before: '
',
        after: '
', }, ], [ 'container', { type: 'upgrade', before: (info) => ``, after: '', }, ], ['flowchart'], ], } ================================================ FILE: docs/.vuepress/enhanceApp.js ================================================ export default ({ Vue, isServer }) => { if (!isServer) { import('vue-toasted' /* webpackChunkName: "notification" */).then(module => { Vue.use(module.default) }) } } ================================================ FILE: docs/README.md ================================================ --- home: true heroImage: https://raw.githubusercontent.com/dunwu/images/master/common/dunwu-logo.png heroText: LINUX-TUTORIAL tagline: 📚 linux-tutorial 是一个 Linux 教程。 actionLink: / footer: CC-BY-SA-4.0 Licensed | Copyright © 2018-Now Dunwu --- ![license](https://badgen.net/github/license/dunwu/linux-tutorial) ![build](https://travis-ci.com/dunwu/linux-tutorial.svg?branch=master) > 📚 **linux-tutorial** 是一个 Linux 教程。 > > 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial) > > 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/) ## 📖 内容 ### Linux 命令 > 学习 Linux 的第一步:当然是从 [Linux 命令](linux/cli/README.md) 入手了。 - [查看 Linux 命令帮助信息](linux/cli/linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - [Linux 文件目录管理](linux/cli/linux-cli-dir.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` - [Linux 文件内容查看命令](linux/cli/linux-cli-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` - [Linux 文件压缩和解压](linux/cli/linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` - [Linux 用户管理](linux/cli/linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` - [Linux 系统管理](linux/cli/linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` - [Linux 网络管理](linux/cli/linux-cli-net.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` - [Linux 硬件管理](linux/cli/linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` - [Linux 软件管理](linux/cli/linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get` ### Linux 运维 > Linux 系统的常见运维工作。 - [网络运维](linux/ops/network-ops.md) - [Samba](linux/ops/samba.md) - [NTP](linux/ops/ntp.md) - [Firewalld](linux/ops/firewalld.md) - [Crontab](linux/ops/crontab.md) - [Systemd](linux/ops/systemd.md) - [Vim](linux/ops/vim.md) - [Iptables](linux/ops/iptables.md) - [oh-my-zsh](linux/ops/zsh.md) ### 软件运维 > 部署在 Linux 系统上的软件运维。 > > 配套安装脚本:⌨ [软件运维配置脚本集合](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) - 开发环境 - [JDK 安装](linux/soft/jdk-install.md) - [Maven 安装](linux/soft/maven-install.md) - [Nodejs 安装](linux/soft/nodejs-install.md) - 开发工具 - [Nexus 运维](linux/soft/nexus-ops.md) - [Gitlab 运维](linux/soft/gitlab-ops.md) - [Jenkins 运维](linux/soft/jenkins-ops.md) - [Svn 运维](linux/soft/svn-ops.md) - [YApi 运维](linux/soft/yapi-ops.md) - 中间件服务 - [Elastic 运维](linux/soft/elastic) - [Kafka 运维](linux/soft/kafka-install.md) - [RocketMQ 运维](linux/soft/rocketmq-install.md) - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) - [Nacos 运维](linux/soft/nacos-install.md) - 服务器 - [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚 - [Tomcat 运维](linux/soft/tomcat-install.md) - [数据库](https://github.com/dunwu/db-tutorial) 📚 - [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md) - [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md) ### Docker - [Docker 快速入门](docker/docker-quickstart.md) - [Dockerfile 最佳实践](docker/docker-dockerfile.md) - [Docker Cheat Sheet](docker/docker-cheat-sheet.md) - [Kubernetes 应用指南](docker/kubernetes.md) ### 其他 - [一篇文章让你彻底掌握 Python](https://dunwu.github.io/blog/pages/ef501b/) - [一篇文章让你彻底掌握 Shell](https://dunwu.github.io/blog/pages/ea6ae1/) - [如何优雅的玩转 Git](https://dunwu.github.io/blog/pages/2fc8b1/) ## ⌨ 脚本 ### Shell 脚本大全 **Shell 脚本大全** 精心收集、整理了 Linux 环境下的常见 Shell 脚本操作片段。 源码:[**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/shell) ### CentOS 运维脚本集合 本人作为一名 Java 后端,苦于经常在 CentOS 环境上开荒虚拟机。为提高效率,写了一套 Shell 脚本,提供如下功能:安装常用 lib 库、命令工具、设置 DNS、NTP、配置国内 yum 源、一键安装常用软件等。 源码:[**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux) ## 📚 资料 - **Linux 命令** - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - [Linux 命令大全](https://man.linuxde.net/) - [linux-command](https://github.com/jaywcjlove/linux-command) - **社区网站** - [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术 - [实验楼](https://www.shiyanlou.com/) - 免费提供了 Linux 在线环境,不用在自己机子上装系统也可以学习 Linux,超方便实用。 - [鸟哥的 linux 私房菜](http://linux.vbird.org/) - 非常适合 Linux 入门初学者看的教程。 - [Linux 公社](http://www.linuxidc.com/) - Linux 相关的新闻、教程、主题、壁纸都有。 - [Linux Today](http://www.linuxde.net) - Linux 新闻资讯发布,Linux 职业技术学习!。 - **知识相关** - [Linux 思维导图整理](http://www.jianshu.com/p/59f759207862) - [Linux 初学者进阶学习资源整理](http://www.jianshu.com/p/fe2a790b41eb) - [Linux 基础入门(新版)](https://www.shiyanlou.com/courses/1) - [【译】Linux 概念架构的理解](http://www.jianshu.com/p/c5ae8f061cfe) [En](http://oss.org.cn/ossdocs/linux/kernel/a1/index.html) - [Linux 守护进程的启动方法](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html) - [Linux 编程之内存映射](https://www.shiyanlou.com/questions/2992) - [Linux 知识点小结](https://blog.huachao.me/2016/1/Linux%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B0%8F%E7%BB%93/) - [10 大白帽黑客专用的 Linux 操作系统](https://linux.cn/article-6971-1.html) - **软件工具** - [超赞的 Linux 软件](https://www.gitbook.com/book/alim0x/awesome-linux-software-zh_cn/details) Github 仓库[Zh](https://github.com/alim0x/Awesome-Linux-Software-zh_CN) [En](https://github.com/VoLuong/Awesome-Linux-Software) - [程序员喜欢的 9 款最佳的 Linux 文件比较工具](http://os.51cto.com/art/201607/513796.htm) - [提高 Linux 开发效率的 5 个工具](http://www.codeceo.com/article/5-linux-productivity-tools.html) - [你要了解的 11 款面向 Linux 系统的一流备份实用工具](http://os.51cto.com/art/201603/508027.htm) - [16 个很有用的在线工具](http://www.simlinux.com/archives/264.html) - Adobe 软件的最佳替代品 [原文在这里](https://linux.cn/article-8928-1.html) - [Evince (Adobe Acrobat Reader)](https://wiki.gnome.org/Apps/Evince) 一个“支持多种文档格式的文档查看器”,可以查看 PDF,还支持各种漫画书格式 - [Pixlr (Adobe Photoshop)](https://pixlr.com/) 一个强大的图像编辑工具 - [Inkscape (Adobe Illustrator)](https://inkscape.org/zh/) 一个专业的矢量图形编辑器 - [Pinegrow Web Editor (Adobe Dreamweaver)](https://pinegrow.com/) 一个可视化编辑制作 HTML 网站 - [Scribus (Adobe InDesign)](https://www.scribus.net/) 一个开源电子杂志制作软件 - [Webflow (Adobe Muse)](https://webflow.com/) 一款可以帮助用户不用编码就可以快速创建网站的谷歌浏览器插件。 - [Tupi (Adobe Animate)](http://www.maefloresta.com/portal/) 一款可以创建 HTML5 动画的工具。 - [Black Magic Fusion (Adobe After Effects)](https://www.blackmagicdesign.com) 一款先进的合成软件,广泛应用于视觉特效、广电影视设计以及 3D 动画设计等领域。 - **中国开源镜像** - [阿里云开源镜像站](http://mirrors.aliyun.com/) - [网易开源镜像站](http://mirrors.163.com/) - [搜狐开源镜像站](http://mirrors.sohu.com/) - [北京交通大学](http://mirror.bjtu.edu.cn/) - [兰州大学](http://mirror.lzu.edu.cn/) - [厦门大学](http://mirrors.xmu.edu.cn/) - [上海交通大学](http://ftp.sjtu.edu.cn/) - [清华大学](http://mirrors.tuna.tsinghua.edu.cn/) - [中国科学技术大学](http://mirrors.ustc.edu.cn/) - [东北大学](http://mirror.neu.edu.cn/) - [浙江大学](http://mirrors.zju.edu.cn/) - [东软信息学院](http://mirrors.neusoft.edu.cn/) ## 🚪 传送门 ◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ ================================================ FILE: docs/docker/README.md ================================================ # Docker 教程 ## 📖 内容 - [Docker 快速入门](docker-quickstart.md) - [Dockerfile 最佳实践](docker-dockerfile.md) - [Docker Cheat Sheet](docker-cheat-sheet.md) - [Kubernetes 应用指南](kubernetes.md) ## 📚 资料 - **官方** - [Docker 官网](http://www.docker.com) - [Docker 官方文档](https://docs.docker.com/) - [Docker Github](https://github.com/moby/moby) - [Docker Compose Github](https://github.com/docker/compose) - [Docker Hub](https://hub.docker.com/) - [Docker 开源](https://www.docker.com/community/open-source) - **资源整理** - [Awesome Docker](https://github.com/veggiemonk/awesome-docker) - **教程** - [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) - 非常详尽的 Docker 中文教程 - [Docker 中文网站](https://www.docker-cn.com/) - [Docker 安装手册](https://docs.docker-cn.com/engine/installation/) - **镜像** - [时速云镜像仓库](https://hub.tenxcloud.com/) - [网易云镜像服务](https://c.163.com/hub#/m/library/) - [DaoCloud 镜像市场](https://hub.daocloud.io/) - [阿里云镜像库](https://cr.console.aliyun.com/) - **文章** - [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) - [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn) ## 🚪 传送门 ◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ ================================================ FILE: docs/docker/docker-cheat-sheet.md ================================================ # Docker Cheat Sheet > 内容主要搬迁自:[Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn) - [为何使用 Docker](#为何使用-docker) - [运维](#运维) - [容器(Container)](#容器container) - [镜像(Images)](#镜像images) - [网络(Networks)](#网络networks) - [仓管中心和仓库(Registry & Repository)](#仓管中心和仓库registry--repository) - [Dockerfile](#dockerfile) - [层(Layers)](#层layers) - [链接(Links)](#链接links) - [卷标(Volumes)](#卷标volumes) - [暴露端口(Exposing ports)](#暴露端口exposing-ports) - [最佳实践](#最佳实践) - [安全(Security)](#安全security) - [小贴士](#小贴士) - [参考资料](#参考资料) ## 为何使用 Docker 「通过 Docker,开发者可以使用任何语言任何工具创建任何应用。“Dockerized” 的应用是完全可移植的,能在任何地方运行 - 不管是同事的 OS X 和 Windows 笔记本,或是在云端运行的 Ubuntu QA 服务,还是在虚拟机运行的 Red Hat 产品数据中心。 Docker Hub 上有 13000+ 的应用,开发者可以从中选取一个进行快速扩展开发。Docker 跟踪管理变更和依赖关系,让系统管理员能更容易理解开发人员是如何让应用运转起来的。而开发者可以通过 Docker Hub 的共有/私有仓库,构建他们的自动化编译,与其他合作者共享成果。 Docker 帮助开发者更快地构建和发布高质量的应用。」—— [什么是 Docker](https://www.docker.com/what-docker/#copy1) ## 运维 ### 安装 Docker 是一个开源的商业产品,有两个版本:社区版(Community Edition,缩写为 CE)和企业版(Enterprise Edition,缩写为 EE)。企业版包含了一些收费服务,个人开发者一般用不到。 Docker CE 的安装请参考官方文档。 - [Mac](https://docs.docker.com/docker-for-mac/install/) - [Windows](https://docs.docker.com/docker-for-windows/install/) - [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/) - [Debian](https://docs.docker.com/install/linux/docker-ce/debian/) - [CentOS](https://docs.docker.com/install/linux/docker-ce/centos/) - [Fedora](https://docs.docker.com/install/linux/docker-ce/fedora/) - [其他 Linux 发行版](https://docs.docker.com/install/linux/docker-ce/binaries/) ### 检查版本 [`docker version`](https://docs.docker.com/engine/reference/commandline/version/) 查看你正在运行的 Docker 版本。 获取 Docker 服务版本: ``` docker version --format '{{.Server.Version}}' ``` 你也可以输出原始的 JSON 数据: ``` docker version --format '{{json .}}' ``` ### Docker 加速 国内访问 Docker Hub 很慢,所以,推荐配置 Docker 镜像仓库来提速。 镜像仓库清单: | 镜像仓库 | 镜像仓库地址 | 说明 | | --------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [DaoCloud 镜像站](https://daocloud.io/mirror) | `http://f1361db2.m.daocloud.io` | 开发者需要开通 DaoCloud 账户,然后可以得到专属加速器。 | | [阿里云](https://cr.console.aliyun.com) | `https://yourcode.mirror.aliyuncs.com` | 开发者需要开通阿里开发者帐户,再使用阿里的加速服务。登录后阿里开发者帐户后,`https://cr.console.aliyun.com/undefined/instances/mirrors` 中查看你的您的专属加速器地址。 | | [网易云](https://c.163yun.com/hub) | `https://hub-mirror.c.163.com` | 直接配置即可,亲测较为稳定。 | 配置镜像仓库方法(以 CentOS 为例): > 下面的示例为在 CentOS 环境中,指定镜像仓库为 `https://hub-mirror.c.163.com` (1)修改配置文件 修改 `/etc/docker/daemon.json` ,如果不存在则新建。执行以下 Shell: ```bash sudo mkdir -p /etc/docker cat >> /etc/docker/daemon.json << EOF { "registry-mirrors": [ "https://hub-mirror.c.163.com" ] } EOF ``` 重启 docker 以生效: ```bash sudo systemctl daemon-reload sudo systemctl restart docker ``` 执行 `docker info` 命令,查看 `Registry Mirrors` 是否已被改为 `https://hub-mirror.c.163.com` ,如果是,则表示配置成功。 ## 容器(Container) [关于 Docker 进程隔离的基础](http://etherealmind.com/basics-docker-containers-hypervisors-coreos/)。容器 (Container) 之于虚拟机 (Virtual Machine) 就好比线程之于进程。或者你可以把他们想成是「吃了类固醇的 chroots」。 ### 生命周期 - [`docker create`](https://docs.docker.com/engine/reference/commandline/create) 创建容器但不启动它。 - [`docker rename`](https://docs.docker.com/engine/reference/commandline/rename/) 用于重命名容器。 - [`docker run`](https://docs.docker.com/engine/reference/commandline/run) 一键创建并同时启动该容器。 - [`docker rm`](https://docs.docker.com/engine/reference/commandline/rm) 删除容器。 - 如果要删除一个运行中的容器,可以添加 `-f` 参数。Docker 会发送 `SIGKILL` 信号给容器。 - [`docker update`](https://docs.docker.com/engine/reference/commandline/update/) 调整容器的资源限制。 - 清理掉所有处于终止状态的容器。 通常情况下,不使用任何命令行选项启动一个容器,该容器将会立即启动并停止。若需保持其运行,你可以使用 `docker run -td container_id` 命令。选项 `-t` 表示分配一个 pseudo-TTY 会话,`-d` 表示自动将容器与终端分离(也就是说在后台运行容器,并输出容器 ID)。 如果你需要一个临时容器,可使用 `docker run --rm` 会在容器停止之后删除它。 如果你需要映射宿主机 (host) 的目录到 Docker 容器内,可使用 `docker run -v $HOSTDIR:$DOCKERDIR`。详见 [卷标(Volumes)](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#卷标volumes) 一节。 如果你想同时删除与容器相关联的卷标,那么在删除容器的时候必须包含 `-v` 选项,像这样 `docker rm -v`。 从 Docker 1.10 起,其内置一套各容器独立的 [日志引擎](https://docs.docker.com/engine/admin/logging/overview/),每个容器可以独立使用。你可以使用 `docker run --log-driver=syslog` 来自定义日志引擎(例如以上的 `syslog`)。 ### 启动和停止 - [`docker start`](https://docs.docker.com/engine/reference/commandline/start) 启动已存在的容器。 - [`docker stop`](https://docs.docker.com/engine/reference/commandline/stop) 停止运行中的容器。 - [`docker restart`](https://docs.docker.com/engine/reference/commandline/restart) 重启容器。 - [`docker pause`](https://docs.docker.com/engine/reference/commandline/pause/) 暂停运行中的容器,将其「冻结」在当前状态。 - [`docker unpause`](https://docs.docker.com/engine/reference/commandline/unpause/) 结束容器暂停状态。 - [`docker wait`](https://docs.docker.com/engine/reference/commandline/wait) 阻塞地等待某个运行中的容器直到停止。 - [`docker kill`](https://docs.docker.com/engine/reference/commandline/kill) 向运行中的容器发送 SIGKILL 指令。 - [`docker attach`](https://docs.docker.com/engine/reference/commandline/attach) 连接到运行中的容器。 如果你想将容器的端口 (ports) 暴露至宿主机,请见 [暴露端口](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#暴露端口exposing-ports) 一节。 关于 Docker 实例崩溃后的重启策略,详见 [本文](http://container42.com/2014/09/30/docker-restart-policies/)。 #### CPU 限制 你可以限制 CPU 资源占用,无论是指定百分比,或是特定核心数。 例如,你可以设置 [`cpu-shares`](https://docs.docker.com/engine/reference/run/#/cpu-share-constraint)。该配置看起来有点奇怪 -- 1024 表示 100% CPU,因此如果你希望容器使用所有 CPU 内核的 50%,应将其设置为 512: ``` docker run -ti --c 512 agileek/cpuset-test ``` 更多信息请参阅 https://goldmann.pl/blog/2014/09/11/resource-management-in-docker/#_cpu。 通过 [`cpuset-cpus`](https://docs.docker.com/engine/reference/run/#/cpuset-constraint) 可使用特定 CPU 内核。 ``` docker run -ti --cpuset-cpus=0,4,6 agileek/cpuset-test ``` 请参阅 https://agileek.github.io/docker/2014/08/06/docker-cpuset/ 获取更多细节以及一些不错的视频。 注意,Docker 在容器内仍然能够 **看到** 全部 CPU -- 它仅仅是不使用全部而已。请参阅 https://github.com/docker/docker/issues/20770 获取更多细节。 #### 内存限制 同样,亦可给 Docker 设置 [内存限制](https://docs.docker.com/engine/reference/run/#/user-memory-constraints): ``` docker run -it -m 300M ubuntu:14.04 /bin/bash ``` #### 能力(Capabilities) Linux 的 Capability 可以通过使用 `cap-add` 和 `cap-drop` 设置。请参阅 https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities 获取更多细节。这有助于提高安全性。 如需要挂载基于 FUSE 的文件系统,你需要结合 `--cap-add` 和 `--device` 使用: ``` docker run --rm -it --cap-add SYS_ADMIN --device /dev/fuse sshfs ``` 授予对某个设备的访问权限: ``` docker run -it --device=/dev/ttyUSB0 debian bash ``` 授予对所有设备的访问权限: ``` docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash ``` 有关容器特权的更多信息请参阅 [本文](https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities)。 ### 信息 - [`docker ps`](https://docs.docker.com/engine/reference/commandline/ps) 查看运行中的所有容器。 - [`docker logs`](https://docs.docker.com/engine/reference/commandline/logs) 从容器中读取日志。(你也可以使用自定义日志驱动,不过在 1.10 中,它只支持 `json-file` 和 `journald`)。 - [`docker inspect`](https://docs.docker.com/engine/reference/commandline/inspect) 查看某个容器的所有信息(包括 IP 地址)。 - [`docker events`](https://docs.docker.com/engine/reference/commandline/events) 从容器中获取事件 (events)。 - [`docker port`](https://docs.docker.com/engine/reference/commandline/port) 查看容器的公开端口。 - [`docker top`](https://docs.docker.com/engine/reference/commandline/top) 查看容器中活动进程。 - [`docker stats`](https://docs.docker.com/engine/reference/commandline/stats) 查看容器的资源使用量统计信息。 - [`docker diff`](https://docs.docker.com/engine/reference/commandline/diff) 查看容器文件系统中存在改动的文件。 `docker ps -a` 将显示所有容器,包括运行中和已停止的。 `docker stats --all` 同样将显示所有容器,默认仅显示运行中的容器。 ### 导入 / 导出 - [`docker cp`](https://docs.docker.com/engine/reference/commandline/cp) 在容器和本地文件系统之间复制文件或目录。 - [`docker export`](https://docs.docker.com/engine/reference/commandline/export) 将容器的文件系统打包为归档文件流 (tarball archive stream) 并输出至标准输出 (STDOUT)。 ### 执行命令 - [`docker exec`](https://docs.docker.com/engine/reference/commandline/exec) 在容器内执行命令。 例如,进入正在运行的 `foo` 容器,并连接 (attach) 到一个新的 Shell 进程:`docker exec -it foo /bin/bash`。 ## 镜像(Images) 镜像是 [Docker 容器的模板](https://docs.docker.com/engine/understanding-docker/#how-does-a-docker-image-work)。 ### 生命周期 - [`docker images`](https://docs.docker.com/engine/reference/commandline/images) 查看所有镜像。 - [`docker import`](https://docs.docker.com/engine/reference/commandline/import) 从归档文件创建镜像。 - [`docker build`](https://docs.docker.com/engine/reference/commandline/build) 从 Dockerfile 创建镜像。 - [`docker commit`](https://docs.docker.com/engine/reference/commandline/commit) 为容器创建镜像,如果容器正在运行则会临时暂停。 - [`docker rmi`](https://docs.docker.com/engine/reference/commandline/rmi) 删除镜像。 - [`docker load`](https://docs.docker.com/engine/reference/commandline/load) 从标准输入 (STDIN) 加载归档包 (tar archive) 作为镜像,包括镜像本身和标签 (tags, 0.7 起)。 - [`docker save`](https://docs.docker.com/engine/reference/commandline/save) 将镜像打包为归档包,并输出至标准输出 (STDOUT),包括所有的父层、标签和版本 (parent layers, tags, versions, 0.7 起)。 ### 其它信息 - [`docker history`](https://docs.docker.com/engine/reference/commandline/history) 查看镜像的历史记录。 - [`docker tag`](https://docs.docker.com/engine/reference/commandline/tag) 给镜像打标签命名(本地或者仓库均可)。 ### 清理 虽然你可以用 `docker rmi` 命令来删除指定的镜像,不过有个名为 [docker-gc](https://github.com/spotify/docker-gc) 的工具,它可以以一种安全的方式,清理掉那些不再被任何容器使用的镜像。Docker 1.13 起,使用 `docker image prune` 亦可删除未使用的镜像。参见 [清理](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#清理)。 ### 加载 / 保存镜像 从文件中加载镜像: ``` docker load < my_image.tar.gz ``` 保存既有镜像: ``` docker save my_image:my_tag | gzip > my_image.tar.gz ``` ### 导入 / 导出容器 从文件中导入容器镜像: ``` cat my_container.tar.gz | docker import - my_image:my_tag ``` 导出既有容器: ``` docker export my_container | gzip > my_container.tar.gz ``` ### 加载已保存的镜像 与 导入已导出为镜像的容器 的不同 通过 `load` 命令来加载镜像,会创建一个新的镜像,并继承原镜像的所有历史。 通过 `import` 将容器作为镜像导入,也会创建一个新的镜像,但并不包含原镜像的历史,因此会比使用 `load` 方式生成的镜像更小。 ## 网络(Networks) Docker 具备 [网络](https://docs.docker.com/engine/userguide/networking/) 功能。我并不是很了解它,所以这是一个扩展本文的好地方。文档 [使用网络](https://docs.docker.com/engine/userguide/networking/work-with-networks/) 指出,这是一种无需暴露端口即可实现 Docker 容器间通信的好方法。 ### 生命周期 - [`docker network create`](https://docs.docker.com/engine/reference/commandline/network_create/) - [`docker network rm`](https://docs.docker.com/engine/reference/commandline/network_rm/) ### 其它信息 - [`docker network ls`](https://docs.docker.com/engine/reference/commandline/network_ls/) - [`docker network inspect`](https://docs.docker.com/engine/reference/commandline/network_inspect/) ### 建立连接 - [`docker network connect`](https://docs.docker.com/engine/reference/commandline/network_connect/) - [`docker network disconnect`](https://docs.docker.com/engine/reference/commandline/network_disconnect/) 你可以 [为容器指定 IP 地址](https://blog.jessfraz.com/post/ips-for-all-the-things/): ``` # 使用你自己的子网和网关创建一个桥接网络 docker network create --subnet 203.0.113.0/24 --gateway 203.0.113.254 iptastic # 基于以上创建的网络,运行一个 Nginx 容器并指定 IP $ docker run --rm -it --net iptastic --ip 203.0.113.2 nginx # 在其他地方使用 CURL 访问这个 IP(假设该 IP 为公网) $ curl 203.0.113.2 ``` ## 暴露端口(Exposing ports) 通过宿主容器暴露输入端口相当 [繁琐但有效的](https://docs.docker.com/engine/reference/run/#expose-incoming-ports)。 例如使用 `-p` 将容器端口映射到宿主端口上(只使用本地主机 (localhost) 接口): ``` docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage ``` 你可以使用 [EXPOSE](https://docs.docker.com/engine/reference/builder/#expose) 告知 Docker,该容器在运行时监听指定的端口: ``` EXPOSE ``` 但是注意 EXPOSE 并不会直接暴露端口,你需要用参数 `-p` 。比如说你要在 localhost 上暴露容器的端口: ``` iptables -t nat -A DOCKER -p tcp --dport -j DNAT --to-destination : ``` 如果你是在 Virtualbox 中运行 Docker,那么你需要配置端口转发 (forward the port)。使用 [forwarded_port](https://docs.vagrantup.com/v2/networking/forwarded_ports.html) 在 Vagrantfile 上配置暴露的端口范围,这样你就可以动态地映射了: ``` Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| ... (49000..49900).each do |port| config.vm.network :forwarded_port, :host => port, :guest => port end ... end ``` 如果你忘记了将什么端口映射到宿主机上的话,可使用 `docker port` 查看: ``` docker port CONTAINER $CONTAINERPORT ``` ## 仓管中心和仓库(Registry & Repository) 仓库 (repository) 是 _被托管(hosted)_ 的已命名镜像 (tagged images) 的集合,这组镜像用于构建容器文件系统。 仓管中心 (registry) 则是 _托管服务(host)_ -- 用于存储仓库并提供 HTTP API,以便 [管理仓库的上传和下载](https://docs.docker.com/engine/tutorials/dockerrepos/)。 Docker 官方托管着自己的 [仓管中心](https://hub.docker.com/),包含着数量众多的仓库。不过话虽如此,这个仓管中心 [并没有很好地验证镜像](https://titanous.com/posts/docker-insecurity),所以如果你担心安全问题的话,请尽量避免使用它。 - [`docker login`](https://docs.docker.com/engine/reference/commandline/login) 登入仓管中心。 - [`docker logout`](https://docs.docker.com/engine/reference/commandline/logout) 登出仓管中心。 - [`docker search`](https://docs.docker.com/engine/reference/commandline/search) 从仓管中心检索镜像。 - [`docker pull`](https://docs.docker.com/engine/reference/commandline/pull) 从仓管中心拉取镜像到本地。 - [`docker push`](https://docs.docker.com/engine/reference/commandline/push) 从本地推送镜像到仓管中心。 ### 本地仓管中心 你可以使用 [docker distribution](https://github.com/docker/distribution) 项目搭建本地的仓管中心,详情参阅 [本地发布 (local deploy)](https://github.com/docker/docker.github.io/blob/master/registry/deploying.md) 的介绍。 科学上网后,也可以看看 [Google+ Group](https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution)。 ## Dockerfile 当你执行 `docker build` 时,Docker 将会根据 [配置文件](https://docs.docker.com/engine/reference/builder/) 启动 Docker 容器。远优于使用 `docker commit`。 以下是一些编写 Dockerfile 的常用编辑器,并链接到适配的语法高亮模块︰ - 如果你在使用 [jEdit](http://jedit.org/),你可以使用我开发的 Dockerfile [语法高亮模块](https://github.com/wsargent/jedit-docker-mode)。 - [Sublime Text 2](https://packagecontrol.io/packages/Dockerfile Syntax Highlighting) - [Atom](https://atom.io/packages/language-docker) - [Vim](https://github.com/ekalinin/Dockerfile.vim) - [Emacs](https://github.com/spotify/dockerfile-mode) - [TextMate](https://github.com/docker/docker/tree/master/contrib/syntax/textmate) - 更多信息请参阅 [Docker 遇上 IDE](https://domeide.github.io/) ### 指令 - [.dockerignore](https://docs.docker.com/engine/reference/builder/#dockerignore-file) - [FROM](https://docs.docker.com/engine/reference/builder/#from) 为其他指令设置基础镜像 (Base Image)。 - [MAINTAINER (deprecated - use LABEL instead)](https://docs.docker.com/engine/reference/builder/#maintainer-deprecated) 为生成的镜像设置作者字段。 - [RUN](https://docs.docker.com/engine/reference/builder/#run) 在当前镜像的基础上生成一个新层并执行命令。 - [CMD](https://docs.docker.com/engine/reference/builder/#cmd) 设置容器默认执行命令。 - [EXPOSE](https://docs.docker.com/engine/reference/builder/#expose) 告知 Docker 容器在运行时所要监听的网络端口。注意:并没有实际上将端口设置为可访问。 - [ENV](https://docs.docker.com/engine/reference/builder/#env) 设置环境变量。 - [ADD](https://docs.docker.com/engine/reference/builder/#add) 将文件、目录或远程文件复制到容器中。缓存无效。请尽量用 `COPY` 代替 `ADD`。 - [COPY](https://docs.docker.com/engine/reference/builder/#copy) 将文件或文件夹复制到容器中。注意:将使用 ROOT 用户复制文件,故无论 USER / WORKDIR 指令如何配置,你都需要手动修改其所有者(`chown`),`ADD` 也是一样。 - [ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint) 将容器设为可执行的。 - [VOLUME](https://docs.docker.com/engine/reference/builder/#volume) 在容器内部创建挂载点 (mount point) 指向外部挂载的卷标或其他容器。 - [USER](https://docs.docker.com/engine/reference/builder/#user) 设置随后执行 RUN / CMD / ENTRYPOINT 命令的用户名。 - [WORKDIR](https://docs.docker.com/engine/reference/builder/#workdir) 设置工作目录 (working directory)。 - [ARG](https://docs.docker.com/engine/reference/builder/#arg) 定义编译时 (build-time) 变量。 - [ONBUILD](https://docs.docker.com/engine/reference/builder/#onbuild) 添加触发指令,当该镜像被作为其他镜像的基础镜像时该指令会被触发。 - [STOPSIGNAL](https://docs.docker.com/engine/reference/builder/#stopsignal) 设置停止容器时,向容器内发送的系统调用信号 (system call signal)。 - [LABEL](https://docs.docker.com/config/labels-custom-metadata/) 将键值对元数据 (key/value metadata) 应用到镜像、容器或是守护进程。 ### 教程 - [Flux7's Dockerfile Tutorial](http://flux7.com/blogs/docker/docker-tutorial-series-part-3-automation-is-the-word-using-dockerfile/) ### 例子 - [Examples](https://docs.docker.com/engine/reference/builder/#dockerfile-examples) - [Best practices for writing Dockerfiles](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/) - [Michael Crosby](http://crosbymichael.com/) 还有更多的 [Dockerfiles best practices](http://crosbymichael.com/dockerfile-best-practices.html) / [take 2](http://crosbymichael.com/dockerfile-best-practices-take-2.html) - [Building Good Docker Images](http://jonathan.bergknoff.com/journal/building-good-docker-images) / [Building Better Docker Images](http://jonathan.bergknoff.com/journal/building-better-docker-images) - [Managing Container Configuration with Metadata](https://speakerdeck.com/garethr/managing-container-configuration-with-metadata) ## 层(Layers) Docker 的版本化文件系统是基于层的。就像 [Git 的提交或文件变更系统](https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/) 一样。 ## 链接(Links) 链接 (links) [通过 TCP/IP 端口](https://docs.docker.com/userguide/dockerlinks/) 实现 Docker 容器之间的通讯。[Atlassian](https://blogs.atlassian.com/2013/11/docker-all-the-things-at-atlassian-automation-and-wiring/) 展示了可用的例子。你还可以 [通过主机名 (hostname) 链接](https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/#/updating-the-etchosts-file)。 在某种意义上来说,该特性已经被 [自定义网络](https://docs.docker.com/network/) 所替代。 注意: 如果你希望容器之间**只**通过链接进行通讯,在启动 Docker 守护进程时,请使用 `-icc=false` 来禁用内部进程通讯。 假设你有一个名为 CONTAINER 的容器(通过 `docker run --name CONTAINER` 指定)并且在 Dockerfile 中,暴露了一个端口: ``` EXPOSE 1337 ``` 然后,我们创建另外一个名为 LINKED 的容器: ``` docker run -d --link CONTAINER:ALIAS --name LINKED user/wordpress ``` 然后 CONTAINER 暴露的端口和别名将会以如下的环境变量出现在 LINKED 中: ``` $ALIAS_PORT_1337_TCP_PORT $ALIAS_PORT_1337_TCP_ADDR ``` 那么你便可以通过这种方式来连接它了。 使用 `docker rm --link` 即可删除链接。 通常,Docker 容器(亦可理解为「服务」)之间的链接,是「服务发现」的一个子集。如果你打算在生产中大规模使用 Docker,这将是一个很大的问题。请参阅[The Docker Ecosystem: Service Discovery and Distributed Configuration Stores](https://www.digitalocean.com/community/tutorials/the-docker-ecosystem-service-discovery-and-distributed-configuration-stores) 获取更多信息。 ## 卷标(Volumes)和挂载 ### 卷标 Docker 的卷标 (volumes) 是 [独立的文件系统](https://docs.docker.com/engine/tutorials/dockervolumes/)。它们并非必须连接到特定的容器上。 `数据卷` 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性: - `数据卷` 可以在容器之间共享和重用 - 对 `数据卷` 的修改会立马生效 - 对 `数据卷` 的更新,不会影响镜像 - `数据卷` 默认会一直存在,即使容器被删除 卷标相关命令: - [`docker volume create`](https://docs.docker.com/engine/reference/commandline/volume_create/) - 创建卷标 - [`docker volume rm`](https://docs.docker.com/engine/reference/commandline/volume_rm/) - 删除卷标 - [`docker volume ls`](https://docs.docker.com/engine/reference/commandline/volume_ls/) - 查看卷标 - [`docker volume inspect`](https://docs.docker.com/engine/reference/commandline/volume_inspect/) - 查看数据卷的具体信息 - [`docker volume prune`](https://docs.docker.com/engine/reference/commandline/volume_prune/) - 清理无主的数据卷 卷标在不能使用链接(只有 TCP/IP)的情况下非常有用。例如,如果你有两个 Docker 实例需要通讯并在文件系统上留下记录。 你可以一次性将其挂载到多个 docker 容器上,通过 `docker run --volumes-from`。 因为卷标是独立的文件系统,它们通常被用于存储各容器之间的瞬时状态。也就是说,你可以配置一个无状态临时容器,关掉之后,当你有第二个这种临时容器实例的时候,你可以从上一次保存的状态继续执行。 查看 [卷标进阶](http://crosbymichael.com/advanced-docker-volumes.html) 来获取更多细节。[Container42](http://container42.com/2014/11/03/docker-indepth-volumes/) 非常有用。 你可以 [将宿主 MacOS 的文件夹映射为 Docker 卷标](https://docs.docker.com/engine/tutorials/dockervolumes/#mount-a-host-directory-as-a-data-volume): ``` docker run -v /Users/wsargent/myapp/src:/src ``` 你也可以用远程 NFS 卷标,如果你觉得你 [有足够勇气](https://docs.docker.com/engine/tutorials/dockervolumes/#/mount-a-shared-storage-volume-as-a-data-volume)。 还可以考虑运行一个纯数据容器,像 [这里](http://container42.com/2013/12/16/persistent-volumes-with-docker-container-as-volume-pattern/) 所说的那样,提供可移植数据。 记得,[文件也可以被挂载为卷标](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#将文件挂载为卷标)。 ### 挂载 使用 `--mount` 标记可以指定挂载一个本地主机的目录到容器中去。 在用 `docker run` 命令的时候,使用 `--mount` 标记来将 `数据卷` 挂载到容器里。在一次 `docker run` 中可以挂载多个 `数据卷`。 ## 最佳实践 这里有一些最佳实践,以及争论焦点: - [The Rabbit Hole of Using Docker in Automated Tests](http://gregoryszorc.com/blog/2014/10/16/the-rabbit-hole-of-using-docker-in-automated-tests/) - [Bridget Kromhout](https://twitter.com/bridgetkromhout) has a useful blog post on [running Docker in production](http://sysadvent.blogspot.co.uk/2014/12/day-1-docker-in-production-reality-not.html) at Dramafever. - There's also a best practices [blog post](http://developers.lyst.com/devops/2014/12/08/docker/) from Lyst. - [A Docker Dev Environment in 24 Hours!](https://engineering.salesforceiq.com/2013/11/05/a-docker-dev-environment-in-24-hours-part-2-of-2.html) - [Building a Development Environment With Docker](https://tersesystems.com/2013/11/20/building-a-development-environment-with-docker/) - [Discourse in a Docker Container](https://samsaffron.com/archive/2013/11/07/discourse-in-a-docker-container) ## 安全(Security) 这节准备讨论一些关于 Docker 安全性的问题。Docker 官方文档 [安全](https://docs.docker.com/articles/security/) 页面讲述了更多细节。 首先第一件事:Docker 是有 root 权限的。如果你在 `docker` 组,那么你就有 [root 权限](https://web.archive.org/web/20161226211755/http://reventlov.com/advisories/using-the-docker-command-to-root-the-host)。如果你将 Docker 的 Unix Socket 暴露给容器,意味着你赋予了容器 [宿主机 root 权限](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container.html)。 Docker 不应当作为唯一的防御措施。你应当使其更加安全可靠。 为了更好地理解容器暴露了什么,可参阅由 [Aaron Grattafiori](https://twitter.com/dyn___) 编写的 [Understanding and Hardening Linux Containers](https://www.nccgroup.trust/globalassets/our-research/us/whitepapers/2016/april/ncc_group_understanding_hardening_linux_containers-1-1.pdf)。这是一个完整全面且包含大量链接和脚注的容器问题指南,介绍了许多有用的内容。即使你已经加固过容器,以下的安全提示依然十分有帮助,但并不能代替理解的过程。 ### 安全提示 为了最大的安全性,你应当考虑在虚拟机上运行 Docker。这是直接从 Docker 安全团队拿来的资料 -- [slides](http://www.slideshare.net/jpetazzo/linux-containers-lxc-docker-and-security) / [notes](http://www.projectatomic.io/blog/2014/08/is-it-safe-a-look-at-docker-and-security-from-linuxcon/)。之后,可使用 AppArmor、seccomp、SELinux、grsec 等来 [限制容器的权限](http://linux-audit.com/docker-security-best-practices-for-your-vessel-and-containers/)。更多细节,请查阅 [Docker 1.10 security features](https://blog.docker.com/2016/02/docker-engine-1-10-security/)。 Docker 镜像 ID 属于 [敏感信息](https://medium.com/@quayio/your-docker-image-ids-are-secrets-and-its-time-you-treated-them-that-way-f55e9f14c1a4) 所以它不应该向外界公开。请将它们当作密码来对待。 阅读由 [Thomas Sjögren](https://github.com/konstruktoid) 编写的 [Docker Security Cheat Sheet](https://github.com/konstruktoid/Docker/blob/master/Security/CheatSheet.adoc):关于加固容器的不错的建议。 查看 [Docker 安全测试脚本](https://github.com/docker/docker-bench-security),下载 [最佳实践白皮书](https://blog.docker.com/2015/05/understanding-docker-security-and-best-practices/)。 你应当远离使用非稳定版本 grsecurity / pax 的内核,比如 [Alpine Linux](https://en.wikipedia.org/wiki/Alpine_Linux)。如果在产品中用了 grsecurity,那么你应该考虑使用有 [商业支持](https://grsecurity.net/business_support.php) 的 [稳定版本](https://grsecurity.net/announce.php),就像你对待 RedHat 那样。虽然要 \$200 每月,但对于你的运维预算来说不值一提。 从 Docker 1.11 开始,你可以轻松的限制在容器中可用的进程数,以防止 fork 炸弹。 这要求 Linux 内核 >= 4.3,并且要在内核配置中打开 CGROUP_PIDS=y。 ``` docker run --pids-limit=64 ``` 同时,你也可以限制进程再获取新权限。该功能是 Linux 内核从 3.5 版本开始就拥有的。你可以从 [这篇博客](http://www.projectatomic.io/blog/2016/03/no-new-privs-docker/) 中阅读到更多关于这方面的内容。 ``` docker run --security-opt=no-new-privileges ``` 以下内容摘选自 [Container Solutions](http://container-solutions.com/is-docker-safe-for-production/) 的 [Docker Security Cheat Sheet](http://container-solutions.com/content/uploads/2015/06/15.06.15_DockerCheatSheet_A2.pdf)(PDF 版本,难以使用,故复制至此): 关闭内部进程通讯: ``` docker -d --icc=false --iptables ``` 设置容器为只读: ``` docker run --read-only ``` 通过 hashsum 来验证卷标: ``` docker pull debian@sha256:a25306f3850e1bd44541976aa7b5fd0a29be ``` 设置卷标为只读: ``` docker run -v $(pwd)/secrets:/secrets:ro debian ``` 在 Dockerfile 中定义用户并以该用户运行,避免在容器中以 ROOT 身份操作: ``` RUN groupadd -r user && useradd -r -g user user USER user ``` ### 用户命名空间(User Namespaces) 还可以通过使用 [用户命名空间](https://s3hh.wordpress.com/2013/07/19/creating-and-using-containers-without-privilege/) -- 自 1.10 版本起已内置,但默认并未启用。 要在 Ubuntu 15.10 中启用用户命名空间 (remap the userns),请 [跟着这篇博客的例子](https://raesene.github.io/blog/2016/02/04/Docker-User-Namespaces/) 来做。 ### 安全相关视频 - [Using Docker Safely](https://youtu.be/04LOuMgNj9U) - [Securing your applications using Docker](https://youtu.be/KmxOXmPhZbk) - [Container security: Do containers actually contain?](https://youtu.be/a9lE9Urr6AQ) - [Linux Containers: Future or Fantasy?](https://www.youtube.com/watch?v=iN6QbszB1R8) ### 安全路线图 Docker 的路线图提到关于 [seccomp 的支持](https://github.com/docker/docker/blob/master/ROADMAP.md#11-security)。 一个名为 [bane](https://github.com/jfrazelle/bane) 的 AppArmor 策略生成器正在实现 [安全配置文件](https://github.com/docker/docker/issues/17142)。 ## 小贴士 链接: - [15 Docker Tips in 5 minutes](http://sssslide.com/speakerdeck.com/bmorearty/15-docker-tips-in-5-minutes) - [CodeFresh Everyday Hacks Docker](https://codefresh.io/blog/everyday-hacks-docker/) ### 清理 最新的 [数据管理命令](https://github.com/docker/docker/pull/26108) 已在 Docker 1.13 实现: - `docker system prune` - `docker volume prune` - `docker network prune` - `docker container prune` - `docker image prune` ### df 命令 `docker system df` 将显示当前 Docker 各部分占用的磁盘空间。 ### Heredoc 声明 Docker 容器 ``` docker build -t htop - << EOF FROM alpine RUN apk --no-cache add htop EOF ``` ### 最近一次的容器 ID ``` alias dl='docker ps -l -q' docker run ubuntu echo hello world docker commit $(dl) helloworld ``` ### 带命令的提交(需要 Dockerfile) ``` docker commit -run='{"Cmd":["postgres", "-too -many -opts"]}' $(dl) postgres ``` ### 获取 IP 地址 ``` docker inspect $(dl) | grep -wm1 IPAddress | cut -d '"' -f 4 ``` 或使用 [jq](https://stedolan.github.io/jq/): ``` docker inspect $(dl) | jq -r '.[0].NetworkSettings.IPAddress' ``` 或使用 [go 模板](https://docs.docker.com/engine/reference/commandline/inspect): ``` docker inspect -f '{{ .NetworkSettings.IPAddress }}' ``` 或在通过 Dockerfile 构建镜像时,通过构建参数 (build argument) 传入: ``` DOCKER_HOST_IP=`ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1` echo DOCKER_HOST_IP = $DOCKER_HOST_IP docker build \ --build-arg ARTIFACTORY_ADDRESS=$DOCKER_HOST_IP -t sometag \ some-directory/ ``` ### 获取端口映射 ``` docker inspect -f '{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' ``` ### 通过正则匹配容器 ``` for i in $(docker ps -a | grep "REGEXP_PATTERN" | cut -f1 -d" "); do echo $i; done` ``` ### 获取环境变量配置 ``` docker run --rm ubuntu env ``` ### 强行终止运行中的容器 ``` docker kill $(docker ps -q) ``` ### 删除所有容器(强行删除!无论容器运行或停止) ``` docker rm -f $(docker ps -qa) ``` ### 删除旧容器 ``` docker ps -a | grep 'weeks ago' | awk '{print $1}' | xargs docker rm ``` ### 删除已停止的容器 ``` docker rm -v `docker ps -a -q -f status=exited` ``` ### 停止并删除容器 ``` docker stop $(docker ps -aq) && docker rm -v $(docker ps -aq) ``` ### 删除无用 (dangling) 的镜像 ``` docker rmi $(docker images -q -f dangling=true) ``` ### 删除所有镜像 ``` docker rmi $(docker images -q) ``` ### 删除无用 (dangling) 的卷标 Docker 1.9 版本起: ``` docker volume rm $(docker volume ls -q -f dangling=true) ``` 1.9.0 中,参数 `dangling=false` 居然 _没_ 用 - 它会被忽略然后列出所有的卷标。 ### 查看镜像依赖 ``` docker images -viz | dot -Tpng -o docker.png ``` ### Docker 容器瘦身 - 在某层 (RUN layer) 清理 APT 这应当和其他 apt 命令在同一层中完成。 否则,前面的层将会保持原有信息,而你的镜像则依旧臃肿。 ``` RUN {apt commands} \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ``` - 压缩镜像 ``` ID=$(docker run -d image-name /bin/bash) docker export $ID | docker import – flat-image-name ``` - 备份 ``` ID=$(docker run -d image-name /bin/bash) (docker export $ID | gzip -c > image.tgz) gzip -dc image.tgz | docker import - flat-image-name ``` ### 监视运行中容器的系统资源利用率 检查某个容器的 CPU、内存以及网络 I/O 使用情况,你可以: ``` docker stats ``` 按 ID 列出所有容器: ``` docker stats $(docker ps -q) ``` 按名称列出所有容器: ``` docker stats $(docker ps --format '{{.Names}}') ``` 按指定镜像名称列出所有容器: ``` docker ps -a -f ancestor=ubuntu ``` 删除所有未标签命名 (untagged) 的容器: ``` docker rmi $(docker images | grep “^” | awk '{split($0,a," "); print a[3]}') ``` 通过正则匹配删除指定容器: ``` docker ps -a | grep wildfly | awk '{print $1}' | xargs docker rm -f ``` 删除所有已退出 (exited) 的容器: ``` docker rm -f $(docker ps -a | grep Exit | awk '{ print $1 }') ``` ### 将文件挂载为卷标 文件也可以被挂载为卷标。例如你可以仅仅注入单个配置文件: ```bash # 从容器复制文件 docker run --rm httpd cat /usr/local/apache2/conf/httpd.conf > httpd.conf # 编辑文件 vim httpd.conf # 挂载修改后的配置启动容器 docker run --rm -ti -v "$PWD/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro" -p "80:80" httpd ``` ## 参考资料 - [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn) ================================================ FILE: docs/docker/docker-compose.md ================================================ # Docker Compose > [compose](https://github.com/docker/compose) 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 `OpenStack` 中的 `Heat` 十分类似。 ## 一、Compose 简介 **`Compose` 的定位是:定义和运行多个 Docker 容器的应用**。 使用一个 `Dockerfile` 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。 `Compose` 恰好满足了这样的需求。它允许用户通过一个单独的 `docker-compose.yml` 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 `Compose` 中有两个重要的概念: - **服务 (`service`)**:一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。 - **项目 (`project`)**:由一组关联的应用容器组成的一个完整业务单元,在 `docker-compose.yml` 文件中定义。 `Compose` 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。 ## 二、安装卸载 `Compose` 支持 Linux、macOS、Windows10 三大平台。 Linux 安装方式: ```bash sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose ``` > :bell: 详情请参考:[Install Docker Compose](https://docs.docker.com/compose/install/) ## 三、快速入门 ### web 应用 新建文件夹,在该目录中编写 `app.py` 文件 ```python from flask import Flask from redis import Redis app = Flask(__name__) redis = Redis(host='redis', port=6379) @app.route('/') def hello(): count = redis.incr('hits') return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) if __name__ == "__main__": app.run(host="0.0.0.0", debug=True) ``` ### Dockerfile 编写 `Dockerfile` 文件,内容为 ```docker FROM python:3.6-alpine ADD . /code WORKDIR /code RUN pip install redis flask CMD ["python", "app.py"] ``` ### docker-compose.yml 编写 `docker-compose.yml` 文件,这个是 Compose 使用的主模板文件。 ```yaml version: '3' services: web: build: . ports: - "5000:5000" redis: image: "redis:alpine" ``` ### 运行 compose 项目 ```bash $ docker-compose up ``` 此时访问本地 `5000` 端口,每次刷新页面,计数就会加 1。 ## 四、命令 > :bell: 请参考: > > - [Compose 官方命令说明文档](https://docs.docker.com/compose/reference/) > - [Compose 命令说明中文文档](https://yeasy.gitbooks.io/docker_practice/content/compose/commands.html) ## 五、模板文件 > `docker-compose.yml` 文件是 Docker Compose 的模板文件,其作用类似于 Dockerfile 和 Docker。 [docker-compose.yml 支持的默认环境变量官方文档](https://docs.docker.com/compose/env-file/) ## 参考资料 - **官方** - [Docker Compose Github](https://github.com/docker/compose) - [Docker Compose 官方文档](https://docs.docker.com/compose/) - **教程** - [Docker — 从入门到实践 - Docker Compose 项目]( https://yeasy.gitbooks.io/docker_practice/content/compose/ ) ================================================ FILE: docs/docker/docker-dockerfile.md ================================================ # Dockerfile 最佳实践 - [一、Dockerfile 指令](#一dockerfile-指令) - [FROM(指定基础镜像)](#from指定基础镜像) - [RUN(执行命令)](#run执行命令) - [COPY(复制文件)](#copy复制文件) - [ADD(更高级的复制文件)](#add更高级的复制文件) - [CMD(容器启动命令)](#cmd容器启动命令) - [ENTRYPOINT(入口点)](#entrypoint入口点) - [ENV(设置环境变量)](#env设置环境变量) - [ARG(构建参数)](#arg构建参数) - [VOLUME(定义匿名卷)](#volume定义匿名卷) - [EXPOSE(暴露端口)](#expose暴露端口) - [WORKDIR(指定工作目录)](#workdir指定工作目录) - [USER(指定当前用户)](#user指定当前用户) - [HEALTHCHECK(健康检查)](#healthcheck健康检查) - [ONBUILD(为他人作嫁衣裳)](#onbuild为他人作嫁衣裳) - [参考资料](#参考资料) ## 一、Dockerfile 简介 Docker 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。 Dockerfile 是一个文本文件,其内包含了一条条的 **指令(Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。 ### 使用 Dockerfile 构建镜像 ## 二、Dockerfile 指令详解 ### FROM(指定基础镜像) > 作用:**`FROM` 指令用于指定基础镜像**。 所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 `nginx` 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 `FROM` 就是指定**基础镜像**,因此一个 `Dockerfile` 中 `FROM` 是必备的指令,并且必须是第一条指令。 在 [Docker Store](https://store.docker.com/) 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 [`nginx`](https://store.docker.com/images/nginx/)、[`redis`](https://store.docker.com/images/redis/)、[`mongo`](https://store.docker.com/images/mongo/)、[`mysql`](https://store.docker.com/images/mysql/)、[`httpd`](https://store.docker.com/images/httpd/)、[`php`](https://store.docker.com/images/php/)、[`tomcat`](https://store.docker.com/images/tomcat/) 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 [`node`](https://store.docker.com/images/node)、[`openjdk`](https://store.docker.com/images/openjdk/)、[`python`](https://store.docker.com/images/python/)、[`ruby`](https://store.docker.com/images/ruby/)、[`golang`](https://store.docker.com/images/golang/) 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。 如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 [`ubuntu`](https://store.docker.com/images/ubuntu/)、[`debian`](https://store.docker.com/images/debian/)、[`centos`](https://store.docker.com/images/centos/)、[`fedora`](https://store.docker.com/images/fedora/)、[`alpine`](https://store.docker.com/images/alpine/) 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。 除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 `scratch`。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。 ```dockerfile FROM scratch ... ``` 如果你以 `scratch` 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。 不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 [`swarm`](https://hub.docker.com/_/swarm/)、[`coreos/etcd`](https://quay.io/repository/coreos/etcd)。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 `FROM scratch` 会让镜像体积更加小巧。使用 [Go 语言](https://golang.org/) 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。 ### RUN(执行命令) > **`RUN` 指令是用来执行命令行命令的**。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种: > > - _shell_ 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。 > > ```dockerfile > RUN echo '

Hello, Docker!

' > /usr/share/nginx/html/index.html > ``` > > - _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。 既然 `RUN` 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样: ```dockerfile FROM debian:jessie RUN apt-get update RUN apt-get install -y gcc libc6-dev make RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" RUN mkdir -p /usr/src/redis RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 RUN make -C /usr/src/redis RUN make -C /usr/src/redis install ``` 之前说过,Dockerfile 中每一个指令都会建立一层,`RUN` 也不例外。每一个 `RUN` 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,`commit` 这一层的修改,构成新的镜像。 而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。 _Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。_ 上面的 `Dockerfile` 正确的写法应该是这样: ```dockerfile FROM debian:jessie RUN buildDeps='gcc libc6-dev make' \ && apt-get update \ && apt-get install -y $buildDeps \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps ``` 首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 `RUN` 对一一对应不同的命令,而是仅仅使用一个 `RUN` 指令,并使用 `&&` 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。 并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 `\` 的命令换行方式,以及行首 `#` 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。 此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 `apt` 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。 很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。 ### COPY(复制文件) > **`COPY` 指令将从构建上下文目录中 `<源路径>` 的文件/目录复制到新的一层的镜像内的 `<目标路径>` 位置。** 格式: - `COPY [--chown=:] <源路径>... <目标路径>` - `COPY [--chown=:] ["<源路径1>",... "<目标路径>"]` 示例: ```dockerfile COPY package.json /usr/src/app/ ``` `<源路径>` 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 [`filepath.Match`](https://golang.org/pkg/path/filepath/#Match) 规则,如: ```dockerfile COPY hom* /mydir/ COPY hom?.txt /mydir/ ``` `<目标路径>` 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 `WORKDIR` 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。 此外,还需要注意一点,使用 `COPY` 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。 在使用该指令的时候还可以加上 `--chown=:` 选项来改变文件的所属用户及所属组。 ```dockerfile COPY --chown=55:mygroup files* /mydir/ COPY --chown=bin files* /mydir/ COPY --chown=1 files* /mydir/ COPY --chown=10:11 files* /mydir/ ``` ### ADD(更高级的复制文件) > `ADD` 指令和 `COPY` 的格式和性质基本一致。但是在 `COPY` 基础上增加了一些功能。 > > 比如 `<源路径>` 可以是一个 `URL`,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 `<目标路径>`去。下载后的文件权限自动设置为 `600`,如果这并不是想要的权限,那么还需要增加额外的一层 `RUN` 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 `RUN` 指令进行解压缩。所以不如直接使用 `RUN` 指令,然后使用 `wget` 或者 `curl` 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。 > > 如果 `<源路径>` 为一个 `tar` 压缩文件的话,压缩格式为 `gzip`, `bzip2` 以及 `xz` 的情况下,`ADD` 指令将会自动解压缩这个压缩文件到 `<目标路径>` 去。 在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 `ubuntu` 中: ```dockerfile FROM scratch ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz / ... ``` 但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 `ADD` 命令了。 在 Docker 官方的 [Dockerfile 最佳实践文档](https://yeasy.gitbooks.io/docker_practice/content/appendix/best_practices.html) 中要求,尽可能的使用 `COPY`,因为 `COPY` 的语义很明确,就是复制文件而已,而 `ADD` 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 `ADD` 的场合,就是所提及的需要自动解压缩的场合。 另外需要注意的是,`ADD` 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。 因此在 `COPY` 和 `ADD` 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 `COPY` 指令,仅在需要自动解压缩的场合使用 `ADD`。 在使用该指令的时候还可以加上 `--chown=:` 选项来改变文件的所属用户及所属组。 ```dockerfile ADD --chown=55:mygroup files* /mydir/ ADD --chown=bin files* /mydir/ ADD --chown=1 files* /mydir/ ADD --chown=10:11 files* /mydir/ ``` ### CMD(容器启动命令) > 之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。`CMD` 指令就是用于指定默认的容器主进程的启动命令的。 `CMD` 指令的格式和 `RUN` 相似,也是两种格式: - `shell` 格式:`CMD <命令>` - `exec` 格式:`CMD ["可执行文件", "参数1", "参数2"...]` - 参数列表格式:`CMD ["参数1", "参数2"...]`。在指定了 `ENTRYPOINT` 指令后,用 `CMD` 指定具体的参数。 在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,`ubuntu` 镜像默认的 `CMD` 是 `/bin/bash`,如果我们直接 `docker run -it ubuntu` 的话,会直接进入 `bash`。我们也可以在运行时指定运行别的命令,如 `docker run -it ubuntu cat /etc/os-release`。这就是用 `cat /etc/os-release` 命令替换了默认的 `/bin/bash` 命令了,输出了系统版本信息。 在指令格式上,一般推荐使用 `exec` 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 `"`,而不要使用单引号。 如果使用 `shell` 格式的话,实际的命令会被包装为 `sh -c` 的参数的形式进行执行。比如: ```dockerfile CMD echo $HOME ``` 在实际执行中,会将其变更为: ```dockerfile CMD [ "sh", "-c", "echo $HOME" ] ``` 这就是为什么我们可以使用环境变量的原因,因为这些环境变量会被 shell 进行解析处理。 提到 `CMD` 就不得不提容器中应用在前台执行和后台执行的问题。这是初学者常出现的一个混淆。 Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,容器内没有后台服务的概念。 一些初学者将 `CMD` 写为: ```dockerfile CMD service nginx start ``` 然后发现容器执行后就立即退出了。甚至在容器内去使用 `systemctl` 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。 对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。 而使用 `service nginx start` 命令,则是希望 upstart 来以后台守护进程形式启动 `nginx` 服务。而刚才说了 `CMD service nginx start` 会被理解为 `CMD [ "sh", "-c", "service nginx start"]`,因此主进程实际上是 `sh`。那么当 `service nginx start` 命令结束后,`sh` 也就结束了,`sh` 作为主进程退出了,自然就会令容器退出。 正确的做法是直接执行 `nginx` 可执行文件,并且要求以前台形式运行。比如: ```dockerfile CMD ["nginx", "-g", "daemon off;"] ``` ### ENTRYPOINT(入口点) `ENTRYPOINT` 的格式和 `RUN` 指令格式一样,分为 `exec` 格式和 `shell` 格式。 `ENTRYPOINT` 的目的和 `CMD` 一样,都是在指定容器启动程序及参数。`ENTRYPOINT` 在运行时也可以替代,不过比 `CMD` 要略显繁琐,需要通过 `docker run` 的参数 `--entrypoint` 来指定。 当指定了 `ENTRYPOINT` 后,`CMD` 的含义就发生了改变,不再是直接的运行其命令,而是将 `CMD` 的内容作为参数传给 `ENTRYPOINT` 指令,换句话说实际执行时,将变为: ```bash "" ``` 那么有了 `CMD` 后,为什么还要有 `ENTRYPOINT` 呢?这种 ` ""` 有什么好处么?让我们来看几个场景。 #### 场景一:让镜像变成像命令一样使用 假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 `CMD` 来实现: ```dockerfile FROM ubuntu:18.04 RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* CMD [ "curl", "-s", "https://ip.cn" ] ``` 假如我们使用 `docker build -t myip .` 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行: ```bash $ docker run myip 当前 IP:61.148.226.66 来自:北京市 联通 ``` 嗯,这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 `CMD` 中可以看到实质的命令是 `curl`,那么如果我们希望显示 HTTP 头信息,就需要加上 `-i` 参数。那么我们可以直接加 `-i` 参数给 `docker run myip` 么? ```bash $ docker run myip -i docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n". ``` 我们可以看到可执行文件找不到的报错,`executable file not found`。之前我们说过,跟在镜像名后面的是 `command`,运行时会替换 `CMD` 的默认值。因此这里的 `-i` 替换了原来的 `CMD`,而不是添加在原来的 `curl -s https://ip.cn` 后面。而 `-i` 根本不是命令,所以自然找不到。 那么如果我们希望加入 `-i` 这参数,我们就必须重新完整的输入这个命令: ```bash $ docker run myip curl -s https://ip.cn -i ``` 这显然不是很好的解决方案,而使用 `ENTRYPOINT` 就可以解决这个问题。现在我们重新用 `ENTRYPOINT` 来实现这个镜像: ```dockerfile FROM ubuntu:18.04 RUN apt-get update \ && apt-get install -y curl \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT [ "curl", "-s", "https://ip.cn" ] ``` 这次我们再来尝试直接使用 `docker run myip -i`: ```bash $ docker run myip 当前 IP:61.148.226.66 来自:北京市 联通 $ docker run myip -i HTTP/1.1 200 OK Server: nginx/1.8.0 Date: Tue, 22 Nov 2016 05:12:40 GMT Content-Type: text/html; charset=UTF-8 Vary: Accept-Encoding X-Powered-By: PHP/5.6.24-1~dotdeb+7.1 X-Cache: MISS from cache-2 X-Cache-Lookup: MISS from cache-2:80 X-Cache: MISS from proxy-2_6 Transfer-Encoding: chunked Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006 Connection: keep-alive 当前 IP:61.148.226.66 来自:北京市 联通 ``` 可以看到,这次成功了。这是因为当存在 `ENTRYPOINT` 后,`CMD` 的内容将会作为参数传给 `ENTRYPOINT`,而这里 `-i` 就是新的 `CMD`,因此会作为参数传给 `curl`,从而达到了我们预期的效果。 #### 场景二:应用运行前的准备工作 启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。 比如 `mysql` 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的 mysql 服务器运行之前解决。 此外,可能希望避免使用 `root` 用户去启动服务,从而提高安全性,而在启动服务前还需要以 `root` 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 `root` 身份执行,方便调试等。 这些准备工作是和容器 `CMD` 无关的,无论 `CMD` 为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 `ENTRYPOINT` 中去执行,而这个脚本会将接到的参数(也就是 ``)作为命令,在脚本最后执行。比如官方镜像 `redis` 中就是这么做的: ```dockerfile FROM alpine:3.4 ... RUN addgroup -S redis && adduser -S -G redis redis ... ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 6379 CMD [ "redis-server" ] ``` 可以看到其中为了 redis 服务创建了 redis 用户,并在最后指定了 `ENTRYPOINT` 为 `docker-entrypoint.sh` 脚本。 ```bash #!/bin/sh ... # allow the container to be started with `--user` if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then chown -R redis . exec su-exec redis "$0" "$@" fi exec "$@" ``` 该脚本的内容就是根据 `CMD` 的内容来判断,如果是 `redis-server` 的话,则切换到 `redis` 用户身份启动服务器,否则依旧使用 `root` 身份执行。比如: ```bash $ docker run -it redis id uid=0(root) gid=0(root) groups=0(root) ``` ### ENV(设置环境变量) > `ENV` 指令用于设置环境变量。无论是后面的其它指令,如 `RUN`,还是运行时的应用,都可以直接使用这里定义的环境变量。 格式: - `ENV ` - `ENV = =...` 示例 1: ```dockerfile ENV VERSION=1.0 DEBUG=on \ NAME="Happy Feet" ``` 这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是一致的。 示例 2: 定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。比如在官方 `node` 镜像 `Dockerfile` 中,就有类似这样的代码: ```dockerfile ENV NODE_VERSION 7.2.0 RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \ && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \ && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \ && ln -s /usr/local/bin/node /usr/local/bin/nodejs ``` 在这里先定义了环境变量 `NODE_VERSION`,其后的 `RUN` 这层里,多次使用 `$NODE_VERSION` 来进行操作定制。可以看到,将来升级镜像构建版本的时候,只需要更新 `7.2.0` 即可,`Dockerfile` 构建维护变得更轻松了。 下列指令可以支持环境变量展开: `ADD`、`COPY`、`ENV`、`EXPOSE`、`LABEL`、`USER`、`WORKDIR`、`VOLUME`、`STOPSIGNAL`、`ONBUILD`。 可以从这个指令列表里感觉到,环境变量可以使用的地方很多,很强大。通过环境变量,我们可以让一份 `Dockerfile` 制作更多的镜像,只需使用不同的环境变量即可。 ### ARG(构建参数) > `Dockerfile` 中的 `ARG` 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 `docker build` 中用 `--build-arg <参数名>=<值>` 来覆盖。 > > 构建参数和 `ENV` 的效果一样,都是设置环境变量。所不同的是,`ARG` 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 `ARG` 保存密码之类的信息,因为 `docker history` 还是可以看到所有值的。 格式:`ARG <参数名>[=<默认值>]` 在 1.13 之前的版本,要求 `--build-arg` 中的参数名,必须在 `Dockerfile` 中用 `ARG` 定义过了,换句话说,就是 `--build-arg` 指定的参数,必须在 `Dockerfile` 中使用了。如果对应参数没有被使用,则会报错退出构建。从 1.13 开始,这种严格的限制被放开,不再报错退出,而是显示警告信息,并继续构建。这对于使用 CI 系统,用同样的构建流程构建不同的 `Dockerfile` 的时候比较有帮助,避免构建命令必须根据每个 Dockerfile 的内容修改。 ### VOLUME(定义匿名卷) 格式: - `VOLUME ["<路径1>", "<路径2>"...]` - `VOLUME <路径>` 之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 `Dockerfile` 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。 ```dockerfile VOLUME /data ``` 这里的 `/data` 目录就会在运行时自动挂载为匿名卷,任何向 `/data` 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如: ```dockerfile docker run -d -v mydata:/data xxxx ``` 在这行命令中,就使用了 `mydata` 这个命名卷挂载到了 `/data` 这个位置,替代了 `Dockerfile` 中定义的匿名卷的挂载配置。 ### EXPOSE(暴露端口) > `EXPOSE` 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 `docker run -P` 时,会自动随机映射 `EXPOSE` 的端口。 > > 要将 `EXPOSE` 和在运行时使用 `-p <宿主端口>:<容器端口>` 区分开来。`-p`,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 `EXPOSE` 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。 格式:`EXPOSE <端口1> [<端口2>...]`。 ### WORKDIR(指定工作目录) > 使用 `WORKDIR` 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,`WORKDIR` 会帮你建立目录。 格式:`WORKDIR <工作目录路径>`。 示例 1: 之前提到一些初学者常犯的错误是把 `Dockerfile` 等同于 Shell 脚本来书写,这种错误的理解还可能会导致出现下面这样的错误: ```dockerfile RUN cd /app RUN echo "hello" > world.txt ``` 如果将这个 `Dockerfile` 进行构建镜像运行后,会发现找不到 `/app/world.txt` 文件,或者其内容不是 `hello`。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 `Dockerfile` 中,这两行 `RUN` 命令的执行环境根本不同,是两个完全不同的容器。这就是对 `Dockerfile` 构建分层存储的概念不了解所导致的错误。 之前说过每一个 `RUN` 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 `RUN cd /app` 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。 因此如果需要改变以后各层的工作目录的位置,那么应该使用 `WORKDIR` 指令。 ### LABEL `LABEL`用于为镜像添加元数据,元数以键值对的形式指定: ``` LABEL = = = ... ``` 使用`LABEL`指定元数据时,一条`LABEL`指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条`LABEL`指令指定,以免生成过多的中间镜像。 如,通过`LABEL`指定一些元数据: ``` LABEL version="1.0" description="这是一个Web服务器" by="IT笔录" ``` 指定后可以通过`docker inspect`查看: ``` $sudo docker inspect itbilu/test "Labels": { "version": "1.0", "description": "这是一个Web服务器", "by": "IT笔录" }, ``` *注意;*`Dockerfile`中还有个`MAINTAINER`命令,该命令用于指定镜像作者。但`MAINTAINER`并不推荐使用,更推荐使用`LABEL`来指定镜像作者。如: ``` LABEL maintainer="itbilu.com" ``` ### USER(指定当前用户) > `USER` 指令和 `WORKDIR` 相似,都是改变环境状态并影响以后的层。`WORKDIR` 是改变工作目录,`USER` 则是改变之后层的执行 `RUN`, `CMD` 以及 `ENTRYPOINT` 这类命令的身份。 > > 当然,和 `WORKDIR` 一样,`USER` 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。 格式:`USER <用户名>[:<用户组>]` 示例 1: ```dockerfile RUN groupadd -r redis && useradd -r -g redis redis USER redis RUN [ "redis-server" ] ``` 如果以 `root` 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 `su`或者 `sudo`,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 [`gosu`](https://github.com/tianon/gosu)。 ```dockerfile # 建立 redis 用户,并使用 gosu 换另一个用户执行命令 RUN groupadd -r redis && useradd -r -g redis redis # 下载 gosu RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \ && chmod +x /usr/local/bin/gosu \ && gosu nobody true # 设置 CMD,并以另外的用户执行 CMD [ "exec", "gosu", "redis", "redis-server" ] ``` ### HEALTHCHECK(健康检查) 格式: - `HEALTHCHECK [选项] CMD <命令>`:设置检查容器健康状况的命令 - `HEALTHCHECK NONE`:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令 `HEALTHCHECK` 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。 在没有 `HEALTHCHECK` 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。 而自 1.12 之后,Docker 提供了 `HEALTHCHECK` 指令,通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。 当在一个镜像指定了 `HEALTHCHECK` 指令后,用其启动容器,初始状态会为 `starting`,在 `HEALTHCHECK` 指令检查成功后变为 `healthy`,如果连续一定次数失败,则会变为 `unhealthy`。 `HEALTHCHECK` 支持下列选项: - `--interval=<间隔>`:两次健康检查的间隔,默认为 30 秒; - `--timeout=<时长>`:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒; - `--retries=<次数>`:当连续失败指定次数后,则将容器状态视为 `unhealthy`,默认 3 次。 和 `CMD`, `ENTRYPOINT` 一样,`HEALTHCHECK` 只可以出现一次,如果写了多个,只有最后一个生效。 在 `HEALTHCHECK [选项] CMD` 后面的命令,格式和 `ENTRYPOINT` 一样,分为 `shell` 格式,和 `exec` 格式。命令的返回值决定了该次健康检查的成功与否:`0`:成功;`1`:失败;`2`:保留,不要使用这个值。 假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否在正常工作,我们可以用 `curl` 来帮助判断,其 `Dockerfile` 的 `HEALTHCHECK` 可以这么写: ```dockerfile FROM nginx RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1 ``` 这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 `curl -fs http://localhost/ || exit 1` 作为健康检查命令。 使用 `docker build` 来构建这个镜像: ```bash $ docker build -t myweb:v1 . ``` 构建好了后,我们启动一个容器: ```bash $ docker run -d --name web -p 80:80 myweb:v1 ``` 当运行该镜像后,可以通过 `docker container ls` 看到最初的状态为 `(health: starting)`: ```bash $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web ``` 在等待几秒钟后,再次 `docker container ls`,就会看到健康状态变化为了 `(healthy)`: ```bash $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web ``` 如果健康检查连续失败超过了重试次数,状态就会变为 `(unhealthy)`。 为了帮助排障,健康检查命令的输出(包括 `stdout` 以及 `stderr`)都会被存储于健康状态里,可以用 `docker inspect` 来查看。 ```bash $ docker inspect --format '{{json .State.Health}}' web | python -m json.tool { "FailingStreak": 0, "Log": [ { "End": "2016-11-25T14:35:37.940957051Z", "ExitCode": 0, "Output": "\n\n\nWelcome to nginx!\n\n\n\n

Welcome to nginx!

\n

If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.

\n\n

For online documentation and support please refer to\nnginx.org.
\nCommercial support is available at\nnginx.com.

\n\n

Thank you for using nginx.

\n\n\n", "Start": "2016-11-25T14:35:37.780192565Z" } ], "Status": "healthy" } ``` ### ONBUILD(为他人作嫁衣裳) 格式:`ONBUILD <其它指令>`。 `ONBUILD` 是一个特殊的指令,它后面跟的是其它指令,比如 `RUN`, `COPY` 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。 `Dockerfile` 中的其它指令都是为了定制当前镜像而准备的,唯有 `ONBUILD` 是为了帮助别人定制自己而准备的。 假设我们要制作 Node.js 所写的应用的镜像。我们都知道 Node.js 使用 `npm` 进行包管理,所有依赖、配置、启动信息等会放到 `package.json` 文件里。在拿到程序代码后,需要先进行 `npm install` 才可以获得所有需要的依赖。然后就可以通过 `npm start`来启动应用。因此,一般来说会这样写 `Dockerfile`: ```dockerfile FROM node:slim RUN mkdir /app WORKDIR /app COPY ./package.json /app RUN [ "npm", "install" ] COPY . /app/ CMD [ "npm", "start" ] ``` 把这个 `Dockerfile` 放到 Node.js 项目的根目录,构建好镜像后,就可以直接拿来启动容器运行。但是如果我们还有第二个 Node.js 项目也差不多呢?好吧,那就再把这个 `Dockerfile` 复制到第二个项目里。那如果有第三个项目呢?再复制么?文件的副本越多,版本控制就越困难,让我们继续看这样的场景维护的问题。 如果第一个 Node.js 项目在开发过程中,发现这个 `Dockerfile` 里存在问题,比如敲错字了、或者需要安装额外的包,然后开发人员修复了这个 `Dockerfile`,再次构建,问题解决。第一个项目没问题了,但是第二个项目呢?虽然最初 `Dockerfile` 是复制、粘贴自第一个项目的,但是并不会因为第一个项目修复了他们的 `Dockerfile`,而第二个项目的 `Dockerfile` 就会被自动修复。 那么我们可不可以做一个基础镜像,然后各个项目使用这个基础镜像呢?这样基础镜像更新,各个项目不用同步 `Dockerfile`的变化,重新构建后就继承了基础镜像的更新?好吧,可以,让我们看看这样的结果。那么上面的这个 `Dockerfile` 就会变为: ```dockerfile FROM node:slim RUN mkdir /app WORKDIR /app CMD [ "npm", "start" ] ``` 这里我们把项目相关的构建指令拿出来,放到子项目里去。假设这个基础镜像的名字为 `my-node` 的话,各个项目内的自己的 `Dockerfile` 就变为: ```dockerfile FROM my-node COPY ./package.json /app RUN [ "npm", "install" ] COPY . /app/ ``` 基础镜像变化后,各个项目都用这个 `Dockerfile` 重新构建镜像,会继承基础镜像的更新。 那么,问题解决了么?没有。准确说,只解决了一半。如果这个 `Dockerfile` 里面有些东西需要调整呢?比如 `npm install` 都需要加一些参数,那怎么办?这一行 `RUN` 是不可能放入基础镜像的,因为涉及到了当前项目的 `./package.json`,难道又要一个个修改么?所以说,这样制作基础镜像,只解决了原来的 `Dockerfile` 的前 4 条指令的变化问题,而后面三条指令的变化则完全没办法处理。 `ONBUILD` 可以解决这个问题。让我们用 `ONBUILD` 重新写一下基础镜像的 `Dockerfile`: ```dockerfile FROM node:slim RUN mkdir /app WORKDIR /app ONBUILD COPY ./package.json /app ONBUILD RUN [ "npm", "install" ] ONBUILD COPY . /app/ CMD [ "npm", "start" ] ``` 这次我们回到原始的 `Dockerfile`,但是这次将项目相关的指令加上 `ONBUILD`,这样在构建基础镜像的时候,这三行并不会被执行。然后各个项目的 `Dockerfile` 就变成了简单地: ```dockerfile FROM my-node ``` 是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。 ## 二、最佳实践 有任何的问题或建议,欢迎给我留言 :laughing: ## 参考资料 - [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/) - [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) - [Docker 官方镜像 Dockerfile](https://github.com/docker-library/docs) - [Dockerfile 指令详解](https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile/) ================================================ FILE: docs/docker/docker-quickstart.md ================================================ # Docker 快速入门 - [一、Docker 的简介](#一docker-的简介) - [二、Docker 的运维](#二docker-的运维) - [三、hello world 实例](#三hello-world-实例) - [四、制作 Docker 容器](#四制作-docker-容器) - [参考资料](#参考资料) ## 一、Docker 的简介 ### 什么是 Docker > **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。** 它是目前最流行的 Linux 容器解决方案。 Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。 总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。 ### 为什么需要 Docker - **更高效的利用系统资源** - 由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,`Docker` 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。 - **更快速的启动时间** - 传统的虚拟机技术启动应用服务往往需要数分钟,而 `Docker` 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。 - **一致的运行环境** - 开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 `Docker` 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 _「这段代码在我机器上没问题啊」_ 这类问题。 - **持续交付和部署** - 对开发和运维([DevOps](https://zh.wikipedia.org/wiki/DevOps))人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。使用 `Docker` 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 [Dockerfile](https://yeasy.gitbooks.io/docker_practice/image/dockerfile) 来进行镜像构建,并结合 [持续集成(Continuous Integration)](https://en.wikipedia.org/wiki/Continuous_integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 [持续部署(Continuous Delivery/Deployment)](https://en.wikipedia.org/wiki/Continuous_delivery) 系统进行自动部署。而且使用 [`Dockerfile`](https://yeasy.gitbooks.io/docker_practice/image/build.html) 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。 - **更轻松的迁移** - 由于 `Docker` 确保了执行环境的一致性,使得应用的迁移更加容易。`Docker` 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。 - **更轻松的维护和扩展** - `Docker` 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,`Docker` 团队同各个开源项目团队一起维护了一大批高质量的 [官方镜像](https://hub.docker.com/search/?type=image&image_filter=official),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。 ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/docker/containers-and-vm.png) ### Docker 的主要用途 Docker 提供了被称为容器的松散隔离环境,在环境中可以打包和运行应用程序。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机的内核中运行。这意味着您可以在给定的硬件组合上运行更多容器,而不是使用虚拟机。你甚至可以在实际上是虚拟机的主机中运行 Docker 容器! Docker 的主要用途,目前有三大类。 - **提供一次性的环境。**比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。 - **提供弹性的云服务。**因为 Docker 容器可以随开随关,很适合动态扩容和缩容。 - **组建微服务架构。**通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。 ### Docker 的核心概念 #### 镜像 Docker 把应用程序及其依赖,打包在镜像(Image)文件里面。 我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。 Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。 **分层存储** 因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。 镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。 分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。 #### 容器 镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。 容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。 前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为**容器存储层**。 容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。 按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。 数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。 #### 仓库 镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,[Docker Registry](https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html) 就是这样的服务。 一个 **Docker Registry** 中可以包含多个**仓库**(`Repository`);每个仓库可以包含多个**标签**(`Tag`);每个标签对应一个镜像。 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 `latest` 作为默认标签。 以 [Ubuntu 镜像](https://store.docker.com/images/ubuntu) 为例,`ubuntu` 是仓库的名字,其内包含有不同的版本标签,如,`16.04`, `18.04`。我们可以通过 `ubuntu:14.04`,或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 `ubuntu`,那将视为 `ubuntu:latest`。 仓库名经常以 _两段式路径_ 形式出现,比如 `jwilder/nginx-proxy`,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。 ## 二、Docker 的运维 不同操作系统环境下安装 Docker 的方式有所不同,详情可以参: - [Docker 官方安装指南](https://docs.docker.com/install/) - [安装 Docker(中文)](https://docker_practice.gitee.io/install/) 国内访问 Docker 比较慢,如果需要提速,可以参考 [镜像加速器](https://docker_practice.gitee.io/install/mirror.html) 安装完成后,运行下面的命令,验证是否安装成功。 - `docker version` - `docker info` Docker 需要用户具有 sudo 权限,为了避免每次命令都输入`sudo`,可以把用户加入 Docker 用户组([官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user))。 ```bash $ sudo usermod -aG docker $USER ``` Docker 是服务器----客户端架构。命令行运行`docker`命令的时候,需要本机有 Docker 服务。如果这项服务没有启动,可以用下面的命令启动([官方文档](https://docs.docker.com/config/daemon/systemd/))。 ```bash # service 命令的用法 $ sudo service docker start # systemctl 命令的用法 $ sudo systemctl start docker ``` ## 三、Hello World 实例 下面,我们通过最简单的 image 文件"[hello world"](https://hub.docker.com/r/library/hello-world/),感受一下 Docker。 需要说明的是,国内连接 Docker 的官方仓库很慢,还会断线,需要将默认仓库改成国内的镜像网站,具体的修改方法在[下一篇文章](http://www.ruanyifeng.com/blog/2018/02/docker-wordpress-tutorial.html)的第一节。有需要的朋友,可以先看一下。 首先,运行下面的命令,将 image 文件从仓库抓取到本地。 > ```bash > $ docker image pull library/hello-world > ``` 上面代码中,`docker image pull`是抓取 image 文件的命令。`library/hello-world`是 image 文件在仓库里面的位置,其中`library`是 image 文件所在的组,`hello-world`是 image 文件的名字。 由于 Docker 官方提供的 image 文件,都放在[`library`](https://hub.docker.com/r/library/)组里面,所以它的是默认组,可以省略。因此,上面的命令可以写成下面这样。 > ```bash > $ docker image pull hello-world > ``` 抓取成功以后,就可以在本机看到这个 image 文件了。 > ```bash > $ docker image ls > ``` 现在,运行这个 image 文件。 > ```bash > $ docker container run hello-world > ``` `docker container run`命令会从 image 文件,生成一个正在运行的容器实例。 注意,`docker container run`命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的`docker image pull`命令并不是必需的步骤。 如果运行成功,你会在屏幕上读到下面的输出。 > ```bash > $ docker container run hello-world > > Hello from Docker! > This message shows that your installation appears to be working correctly. > > ... ... > ``` 输出这段提示以后,`hello world`就会停止运行,容器自动终止。 有些容器不会自动终止,因为提供的是服务。比如,安装运行 Ubuntu 的 image,就可以在命令行体验 Ubuntu 系统。 > ```bash > $ docker container run -it ubuntu bash > ``` 对于那些不会自动终止的容器,必须使用[`docker container kill`](https://docs.docker.com/engine/reference/commandline/container_kill/) 命令手动终止。 > ```bash > $ docker container kill [containID] > ``` ## 四、制作 Docker 容器 下面我以 [koa-demos](http://www.ruanyifeng.com/blog/2017/08/koa.html) 项目为例,介绍怎么写 Dockerfile 文件,实现让用户在 Docker 容器里面运行 Koa 框架。 作为准备工作,请先[下载源码](https://github.com/ruanyf/koa-demos/archive/master.zip)。 > ```bash > $ git clone https://github.com/ruanyf/koa-demos.git > $ cd koa-demos > ``` ### 编写 Dockerfile 文件 首先,在项目的根目录下,新建一个文本文件`.dockerignore`,写入下面的[内容](https://github.com/ruanyf/koa-demos/blob/master/.dockerignore)。 > ```bash > .git > node_modules > npm-debug.log > ``` 上面代码表示,这三个路径要排除,不要打包进入 image 文件。如果你没有路径要排除,这个文件可以不新建。 然后,在项目的根目录下,新建一个文本文件 Dockerfile,写入下面的[内容](https://github.com/ruanyf/koa-demos/blob/master/Dockerfile)。 > ```bash > FROM node:8.4 > COPY . /app > WORKDIR /app > RUN npm install --registry=https://registry.npm.taobao.org > EXPOSE 3000 > ``` 上面代码一共五行,含义如下。 > - `FROM node:8.4`:该 image 文件继承官方的 node image,冒号表示标签,这里标签是`8.4`,即 8.4 版本的 node。 > - `COPY . /app`:将当前目录下的所有文件(除了`.dockerignore`排除的路径),都拷贝进入 image 文件的`/app`目录。 > - `WORKDIR /app`:指定接下来的工作路径为`/app`。 > - `RUN npm install`:在`/app`目录下,运行`npm install`命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。 > - `EXPOSE 3000`:将容器 3000 端口暴露出来, 允许外部连接这个端口。 ### 创建 image 文件 有了 Dockerfile 文件以后,就可以使用`docker image build`命令创建 image 文件了。 > ```bash > $ docker image build -t koa-demo . > # 或者 > $ docker image build -t koa-demo:0.0.1 . > ``` 上面代码中,`-t`参数用来指定 image 文件的名字,后面还可以用冒号指定标签。如果不指定,默认的标签就是`latest`。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。 如果运行成功,就可以看到新生成的 image 文件`koa-demo`了。 > ```bash > $ docker image ls > ``` ### 生成容器 `docker container run`命令会从 image 文件生成容器。 > ```bash > $ docker container run -p 8000:3000 -it koa-demo /bin/bash > # 或者 > $ docker container run -p 8000:3000 -it koa-demo:0.0.1 /bin/bash > ``` 上面命令的各个参数含义如下: > - `-p`参数:容器的 3000 端口映射到本机的 8000 端口。 > - `-it`参数:容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。 > - `koa-demo:0.0.1`:image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)。 > - `/bin/bash`:容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。 如果一切正常,运行上面的命令以后,就会返回一个命令行提示符。 > ```bash > root@66d80f4aaf1e:/app# > ``` 这表示你已经在容器里面了,返回的提示符就是容器内部的 Shell 提示符。执行下面的命令。 > ```bash > root@66d80f4aaf1e:/app# node demos/01.js > ``` 这时,Koa 框架已经运行起来了。打开本机的浏览器,访问 http://127.0.0.1:8000,网页显示"Not Found",这是因为这个 [demo](https://github.com/ruanyf/koa-demos/blob/master/demos/01.js) 没有写路由。 这个例子中,Node 进程运行在 Docker 容器的虚拟环境里面,进程接触到的文件系统和网络接口都是虚拟的,与本机的文件系统和网络接口是隔离的,因此需要定义容器与物理机的端口映射(map)。 现在,在容器的命令行,按下 Ctrl + c 停止 Node 进程,然后按下 Ctrl + d (或者输入 exit)退出容器。此外,也可以用`docker container kill`终止容器运行。 > ```bash > # 在本机的另一个终端窗口,查出容器的 ID > $ docker container ls > > # 停止指定的容器运行 > $ docker container kill [containerID] > ``` 容器停止运行之后,并不会消失,用下面的命令删除容器文件。 > ```bash > # 查出容器的 ID > $ docker container ls --all > > # 删除指定的容器文件 > $ docker container rm [containerID] > ``` 也可以使用`docker container run`命令的`--rm`参数,在容器终止运行后自动删除容器文件。 > ```bash > $ docker container run --rm -p 8000:3000 -it koa-demo /bin/bash > ``` ### CMD 命令 上一节的例子里面,容器启动以后,需要手动输入命令`node demos/01.js`。我们可以把这个命令写在 Dockerfile 里面,这样容器启动以后,这个命令就已经执行了,不用再手动输入了。 > ```bash > FROM node:8.4 > COPY . /app > WORKDIR /app > RUN npm install --registry=https://registry.npm.taobao.org > EXPOSE 3000 > CMD node demos/01.js > ``` 上面的 Dockerfile 里面,多了最后一行`CMD node demos/01.js`,它表示容器启动后自动执行`node demos/01.js`。 你可能会问,`RUN`命令与`CMD`命令的区别在哪里?简单说,`RUN`命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;`CMD`命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个`RUN`命令,但是只能有一个`CMD`命令。 注意,指定了`CMD`命令以后,`docker container run`命令就不能附加命令了(比如前面的`/bin/bash`),否则它会覆盖`CMD`命令。现在,启动容器可以使用下面的命令。 > ```bash > $ docker container run --rm -p 8000:3000 -it koa-demo:0.0.1 > ``` ### 发布 image 文件 容器运行成功后,就确认了 image 文件的有效性。这时,我们就可以考虑把 image 文件分享到网上,让其他人使用。 首先,去 [hub.docker.com](https://hub.docker.com/) 或 [cloud.docker.com](https://cloud.docker.com/) 注册一个账户。然后,用下面的命令登录。 > ```bash > $ docker login > ``` 接着,为本地的 image 标注用户名和版本。 > ```bash > $ docker image tag [imageName] [username]/[repository]:[tag] > # 实例 > $ docker image tag koa-demos:0.0.1 ruanyf/koa-demos:0.0.1 > ``` 也可以不标注用户名,重新构建一下 image 文件。 > ```bash > $ docker image build -t [username]/[repository]:[tag] . > ``` 最后,发布 image 文件。 > ```bash > $ docker image push [username]/[repository]:[tag] > ``` 发布成功以后,登录 hub.docker.com,就可以看到已经发布的 image 文件。 ## 参考资料 - [Docker 入门教程](https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) - [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) ================================================ FILE: docs/docker/kubernetes.md ================================================ # Kubernetes 应用指南 > Kubernetes 是谷歌开源的容器集群管理系统 是用于自动部署,扩展和管理 Docker 应用程序的开源系统,简称 K8S。 > > 关键词: `docker` - [一、K8S 简介](#一k8s-简介) - [二、K8S 命令](#二k8s-命令) - [参考资料](#参考资料) ## 一、K8S 简介 K8S 主控组件(Master) 包含三个进程,都运行在集群中的某个节上,通常这个节点被称为 master 节点。这些进程包括:`kube-apiserver`、`kube-controller-manager` 和 `kube-scheduler`。 集群中的每个非 master 节点都运行两个进程: - kubelet,和 master 节点进行通信。 - kube-proxy,一种网络代理,将 Kubernetes 的网络服务代理到每个节点上。 ### K8S 功能 - 基于容器的应用部署、维护和滚动升级 - 负载均衡和服务发现 - 跨机器和跨地区的集群调度 - 自动伸缩 - 无状态服务和有状态服务 - 广泛的 Volume 支持 - 插件机制保证扩展性 ### K8S 核心组件 Kubernetes 主要由以下几个核心组件组成: - etcd 保存了整个集群的状态; - apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制; - controller manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等; - scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上; - kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理; - Container runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI); - kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡 ![img](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LDAOok5ngY4pc1lEDes%2F-LpOIkR-zouVcB8QsFj_%2F-LpOIpZIYxaDoF-FJMZk%2Farchitecture.png?generation=1569161437087842&alt=media) ### K8S 核心概念 K8S 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。 ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/kubernetes/pod.svg) - `Pod` - K8S 使用 Pod 来管理容器,每个 Pod 可以包含一个或多个紧密关联的容器。Pod 是一组紧密关联的容器集合,它们共享 PID、IPC、Network 和 UTS namespace,是 K8S 调度的基本单位。Pod 内的多个容器共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。 - `Node` - Node 是 Pod 真正运行的主机,可以是物理机,也可以是虚拟机。为了管理 Pod,每个 Node 节点上至少要运行 container runtime(比如 docker 或者 rkt)、`kubelet` 和 `kube-proxy` 服务。 - `Namespace` - Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 pods, services, replication controllers 和 deployments 等都是属于某一个 namespace 的(默认是 default),而 node, persistentVolumes 等则不属于任何 namespace。 - `Service` - Service 是应用服务的抽象,通过 labels 为应用提供负载均衡和服务发现。匹配 labels 的 Pod IP 和端口列表组成 endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 endpoints 上。每个 Service 都会自动分配一个 cluster IP(仅在集群内部可访问的虚拟地址)和 DNS 名,其他容器可以通过该地址或 DNS 来访问服务,而不需要了解后端容器的运行。 - `Label` - Label 是识别 K8S 对象的标签,以 key/value 的方式附加到对象上(key 最长不能超过 63 字节,value 可以为空,也可以是不超过 253 字节的字符串)。Label 不提供唯一性,并且实际上经常是很多对象(如 Pods)都使用相同的 label 来标志具体的应用。Label 定义好后其他对象可以使用 Label Selector 来选择一组相同 label 的对象(比如 ReplicaSet 和 Service 用 label 来选择一组 Pod)。Label Selector 支持以下几种方式: - 等式,如 `app=nginx` 和 `env!=production` - 集合,如 `env in (production, qa)` - 多个 label(它们之间是 AND 关系),如 `app=nginx,env=test` - `Annotations` - Annotations 是 key/value 形式附加于对象的注解。不同于 Labels 用于标志和选择对象,Annotations 则是用来记录一些附加信息,用来辅助应用部署、安全策略以及调度策略等。比如 deployment 使用 annotations 来记录 rolling update 的状态。 ## 二、K8S 命令 ### 客户端配置 ```bash # Setup autocomplete in bash; bash-completion package should be installed first source <(kubectl completion bash) # View Kubernetes config kubectl config view # View specific config items by json path kubectl config view -o jsonpath='{.users[?(@.name == "k8s")].user.password}' # Set credentials for foo.kuberntes.com kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword ``` ### 查找资源 ```bash # List all services in the namespace kubectl get services # List all pods in all namespaces in wide format kubectl get pods -o wide --all-namespaces # List all pods in json (or yaml) format kubectl get pods -o json # Describe resource details (node, pod, svc) kubectl describe nodes my-node # List services sorted by name kubectl get services --sort-by=.metadata.name # List pods sorted by restart count kubectl get pods --sort-by='.status.containerStatuses[0].restartCount' # Rolling update pods for frontend-v1 kubectl rolling-update frontend-v1 -f frontend-v2.json # Scale a replicaset named 'foo' to 3 kubectl scale --replicas=3 rs/foo # Scale a resource specified in "foo.yaml" to 3 kubectl scale --replicas=3 -f foo.yaml # Execute a command in every pod / replica for i in 0 1; do kubectl exec foo-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done ``` ### 资源管理 ```bash # Get documentation for pod or service kubectl explain pods,svc # Create resource(s) like pods, services or daemonsets kubectl create -f ./my-manifest.yaml # Apply a configuration to a resource kubectl apply -f ./my-manifest.yaml # Start a single instance of Nginx kubectl run nginx --image=nginx # Create a secret with several keys cat < 实测环境:Centos ## 查看可下载镜像 ```docker # docker search mysql INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED docker.io docker.io/mysql MySQL is a widely used, open-source relati... 5757 [OK] docker.io docker.io/mariadb MariaDB is a community-developed fork of M... 1863 [OK] docker.io docker.io/mysql/mysql-server Optimized MySQL Server Docker images. Crea... 397 [OK] ... ``` ## 选择下载官方镜像 比如,我想下载最新版本,则执行如下命令: ```docker docker pull mysql ``` ## 使用镜像 ```docker docker run -p 3306:3306 --name mysql -v /opt/docker_v/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql ``` ## 资源 * https://hub.docker.com/_/mysql/ ================================================ FILE: docs/docker/service/docker-install-nginx.md ================================================ # Docker 安装 Nginx > 实测环境:Centos ## 查看可用镜像 执行 `docker search nginx` 命令查看可用镜像: ```docker # docker search nginx INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED docker.io docker.io/nginx Official build of Nginx. 8272 [OK] docker.io docker.io/jwilder/nginx-proxy Automated Nginx reverse proxy for docker c... 1300 [OK] docker.io docker.io/richarvey/nginx-php-fpm Container running Nginx + PHP-FPM capable ... 540 [OK] docker.io docker.io/jrcs/letsencrypt-nginx-proxy-companion LetsEncrypt container to use with nginx as... 336 [OK] ... ``` ## 选择下载镜像 执行 `docker pull nginx` 命令下载镜像 ## 运行镜像 ``` docker run -p 80:80 --name mynginx -d nginx ``` ================================================ FILE: docs/linux/cli/README.md ================================================ # Linux 命令行 > 学习 Linux 的第一步:当然是从 Linux 命令入手了。 ## 📖 内容 - [查看 Linux 命令帮助信息](linux-cli-help.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` - [Linux 文件目录管理](linux-cli-dir.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm` - [Linux 文件内容查看命令](linux-cli-file.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` - [Linux 文件压缩和解压](linux-cli-file-compress.md) - 关键词:`tar`, `gzip`, `zip`, `unzip` - [Linux 用户管理](linux-cli-user.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` - [Linux 系统管理](linux-cli-system.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` - [Linux 网络管理](linux-cli-net.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` - [Linux 硬件管理](linux-cli-hardware.md) - 关键词:`df`, `du`, `top`, `free`, `iotop` - [Linux 软件管理](linux-cli-software.md) - 关键词:`rpm`, `yum`, `apt-get` ## 📚 资料 - [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - [Linux命令大全](https://man.linuxde.net/) - [linux-command](https://github.com/jaywcjlove/linux-command) ## 🚪 传送门 ◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ ================================================ FILE: docs/linux/cli/free.md ================================================ # free 显示内存的使用情况 ## 补充说明 **free 命令** 可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。 ### 语法 ```shell free(选项) ``` ### 选项 ```shell -b # 以Byte为单位显示内存使用情况; -k # 以KB为单位显示内存使用情况; -m # 以MB为单位显示内存使用情况; -g # 以GB为单位显示内存使用情况。 -o # 不显示缓冲区调节列; -s<间隔秒数> # 持续观察内存使用状况; -t # 显示内存总和列; -V # 显示版本信息。 ``` ### 实例 ```shell free -t # 以总和的形式显示内存的使用信息 free -s 10 # 周期性的查询内存使用信息,每10s 执行一次命令 ``` 显示内存使用情况 ```shell free -m total used free shared buffers cached Mem: 2016 1973 42 0 163 1497 -/+ buffers/cache: 312 1703 Swap: 4094 0 4094 ``` **第一部分 Mem 行解释:** ```shell total:内存总数; used:已经使用的内存数; free:空闲的内存数; shared:当前已经废弃不用; buffers Buffer:缓存内存数; cached Page:缓存内存数。 ``` 关系:total = used + free **第二部分(-/+ buffers/cache)解释:** ```shell (-buffers/cache) used内存数:第一部分Mem行中的 used – buffers – cached (+buffers/cache) free内存数: 第一部分Mem行中的 free + buffers + cached ``` 可见-buffers/cache 反映的是被程序实实在在吃掉的内存,而+buffers/cache 反映的是可以挪用的内存总数。 第三部分是指交换分区。 输出结果的第四行是交换分区 SWAP 的,也就是我们通常所说的虚拟内存。 区别:第二行(mem)的 used/free 与第三行(-/+ buffers/cache) used/free 的区别。 这两个的区别在于使用的角度来看,第一行是从 OS 的角度来看,因为对于 OS,buffers/cached 都是属于被使用,所以他的可用内存是 2098428KB,已用内存是 30841684KB,其中包括,内核(OS)使用+Application(X, oracle,etc)使用的+buffers+cached. 第三行所指的是从应用程序角度来看,对于应用程序来说,buffers/cached 是等于可用的,因为 buffer/cached 是为了提高文件读取的性能,当应用程序需在用到内存的时候,buffer/cached 会很快地被回收。 所以从应用程序的角度来说,可用内存=系统 free memory+buffers+cached。 如本机情况的可用内存为: 18007156=2098428KB+4545340KB+11363424KB 接下来解释什么时候内存会被交换,以及按什么方交换。 当可用内存少于额定值的时候,就会开会进行交换。如何看额定值: ```shell cat /proc/meminfo MemTotal: 16140816 kB MemFree: 816004 kB MemAvailable: 2913824 kB Buffers: 17912 kB Cached: 2239076 kB SwapCached: 0 kB Active: 12774804 kB Inactive: 1594328 kB Active(anon): 12085544 kB Inactive(anon): 94572 kB Active(file): 689260 kB Inactive(file): 1499756 kB Unevictable: 116888 kB Mlocked: 116888 kB SwapTotal: 8191996 kB SwapFree: 8191996 kB Dirty: 56 kB Writeback: 0 kB AnonPages: 12229228 kB Mapped: 117136 kB Shmem: 58736 kB Slab: 395568 kB SReclaimable: 246700 kB SUnreclaim: 148868 kB KernelStack: 30496 kB PageTables: 165104 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 16262404 kB Committed_AS: 27698600 kB VmallocTotal: 34359738367 kB VmallocUsed: 311072 kB VmallocChunk: 34350899200 kB HardwareCorrupted: 0 kB AnonHugePages: 3104768 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 225536 kB DirectMap2M: 13279232 kB DirectMap1G: 5242880 kB ``` 交换将通过三个途径来减少系统中使用的物理页面的个数: 1. 减少缓冲与页面 cache 的大小, 2. 将系统 V 类型的内存页面交换出去, 3. 换出或者丢弃页面。(Application 占用的内存页,也就是物理内存不足)。 事实上,少量地使用 swap 是不是影响到系统性能的。 那 buffers 和 cached 都是缓存,两者有什么区别呢? 为了提高磁盘存取效率, Linux 做了一些精心的设计, 除了对 dentry 进行缓存(用于 VFS,加速文件路径名到 inode 的转换), 还采取了两种主要 Cache 方式: Buffer Cache 和 Page Cache。前者针对磁盘块的读写,后者针对文件 inode 的读写。这些 Cache 有效缩短了 I/O 系统调用(比如 read,write,getdents)的时间。 磁盘的操作有逻辑级(文件系统)和物理级(磁盘块),这两种 Cache 就是分别缓存逻辑和物理级数据的。 Page cache 实际上是针对文件系统的,是文件的缓存,在文件层面上的数据会缓存到 page cache。文件的逻辑层需要映射到实际的物理磁盘,这种映射关系由文件系统来完成。当 page cache 的数据需要刷新时,page cache 中的数据交给 buffer cache,因为 Buffer Cache 就是缓存磁盘块的。但是这种处理在 2.6 版本的内核之后就变的很简单了,没有真正意义上的 cache 操作。 Buffer cache 是针对磁盘块的缓存,也就是在没有文件系统的情况下,直接对磁盘进行操作的数据会缓存到 buffer cache 中,例如,文件系统的元数据都会缓存到 buffer cache 中。 简单说来,page cache 用来缓存文件数据,buffer cache 用来缓存磁盘数据。在有文件系统的情况下,对文件操作,那么数据会缓存到 page cache,如果直接采用 dd 等工具对磁盘进行读写,那么数据会缓存到 buffer cache。 所以我们看 linux,只要不用 swap 的交换空间,就不用担心自己的内存太少.如果常常 swap 用很多,可能你就要考虑加物理内存了.这也是 linux 看内存是否够用的标准. 如果是应用服务器的话,一般只看第二行,+buffers/cache,即对应用程序来说 free 的内存太少了,也是该考虑优化程序或加内存了。 ================================================ FILE: docs/linux/cli/grep.md ================================================ # grep 强大的文本搜索工具 ## 补充说明 **grep** (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。用于过滤/搜索的特定字符。可使用正则表达式能多种命令配合使用,使用上十分灵活。 ### 选项 ```shell -a --text # 不要忽略二进制数据。 -A <显示行数> --after-context=<显示行数> # 除了显示符合范本样式的那一行之外,并显示该行之后的内容。 -b --byte-offset # 在显示符合范本样式的那一行之外,并显示该行之前的内容。 -B<显示行数> --before-context=<显示行数> # 除了显示符合样式的那一行之外,并显示该行之前的内容。 -c --count # 计算符合范本样式的列数。 -C<显示行数> --context=<显示行数>或-<显示行数> # 除了显示符合范本样式的那一列之外,并显示该列之前后的内容。 -d<进行动作> --directories=<动作> # 当指定要查找的是目录而非文件时,必须使用这项参数,否则grep命令将回报信息并停止动作。 -e<范本样式> --regexp=<范本样式> # 指定字符串作为查找文件内容的范本样式。 -E --extended-regexp # 将范本样式为延伸的普通表示法来使用,意味着使用能使用扩展正则表达式。 -f<范本文件> --file=<规则文件> # 指定范本文件,其内容有一个或多个范本样式,让grep查找符合范本条件的文件内容,格式为每一列的范本样式。 -F --fixed-regexp # 将范本样式视为固定字符串的列表。 -G --basic-regexp # 将范本样式视为普通的表示法来使用。 -h --no-filename # 在显示符合范本样式的那一列之前,不标示该列所属的文件名称。 -H --with-filename # 在显示符合范本样式的那一列之前,标示该列的文件名称。 -i --ignore-case # 忽略字符大小写的差别。 -l --file-with-matches # 列出文件内容符合指定的范本样式的文件名称。 -L --files-without-match # 列出文件内容不符合指定的范本样式的文件名称。 -n --line-number # 在显示符合范本样式的那一列之前,标示出该列的编号。 -P --perl-regexp # PATTERN 是一个 Perl 正则表达式 -q --quiet或--silent # 不显示任何信息。 -R/-r --recursive # 此参数的效果和指定“-d recurse”参数相同。 -s --no-messages # 不显示错误信息。 -v --revert-match # 反转查找。 -V --version # 显示版本信息。 -w --word-regexp # 只显示全字符合的列。 -x --line-regexp # 只显示全列符合的列。 -y # 此参数效果跟“-i”相同。 -o # 只输出文件中匹配到的部分。 -m --max-count= # 找到num行结果后停止查找,用来限制匹配行数 ``` ### 规则表达式 ```shell ^ # 锚定行的开始 如:'^grep'匹配所有以grep开头的行。 $ # 锚定行的结束 如:'grep$' 匹配所有以grep结尾的行。 . # 匹配一个非换行符的字符 如:'gr.p'匹配gr后接一个任意字符,然后是p。 * # 匹配零个或多个先前字符 如:'*grep'匹配所有一个或多个空格后紧跟grep的行。 .* # 一起用代表任意字符。 [] # 匹配一个指定范围内的字符,如'[Gg]rep'匹配Grep和grep。 [^] # 匹配一个不在指定范围内的字符,如:'[^A-FH-Z]rep'匹配不包含A-R和T-Z的一个字母开头,紧跟rep的行。 \(..\) # 标记匹配字符,如'\(love\)',love被标记为1。 \< # 锚定单词的开始,如:'\ # 锚定单词的结束,如'grep\>'匹配包含以grep结尾的单词的行。 x\{m\} # 重复字符x,m次,如:'0\{5\}'匹配包含5个o的行。 x\{m,\} # 重复字符x,至少m次,如:'o\{5,\}'匹配至少有5个o的行。 x\{m,n\} # 重复字符x,至少m次,不多于n次,如:'o\{5,10\}'匹配5--10个o的行。 \w # 匹配文字和数字字符,也就是[A-Za-z0-9],如:'G\w*p'匹配以G后跟零个或多个文字或数字字符,然后是p。 \W # \w的反置形式,匹配一个或多个非单词字符,如点号句号等。 \b # 单词锁定符,如: '\bgrep\b'只匹配grep。 ``` ## grep 命令常见用法 在文件中搜索一个单词,命令会返回一个包含 **“match_pattern”** 的文本行: ```shell grep match_pattern file_name grep "match_pattern" file_name ``` 在多个文件中查找: ```shell grep "match_pattern" file_1 file_2 file_3 ... ``` 输出除之外的所有行 **-v** 选项: ```shell grep -v "match_pattern" file_name ``` 标记匹配颜色 **--color=auto** 选项: ```shell grep "match_pattern" file_name --color=auto ``` 使用正则表达式 **-E** 选项: ```shell grep -E "[1-9]+" # 或 egrep "[1-9]+" ``` 使用正则表达式 **-P** 选项: ```shell grep -P "(\d{3}\-){2}\d{4}" file_name ``` 只输出文件中匹配到的部分 **-o** 选项: ```shell echo this is a test line. | grep -o -E "[a-z]+\." line. echo this is a test line. | egrep -o "[a-z]+\." line. ``` 统计文件或者文本中包含匹配字符串的行数 **-c** 选项: ```shell grep -c "text" file_name ``` 输出包含匹配字符串的行数 **-n** 选项: ```shell grep "text" -n file_name # 或 cat file_name | grep "text" -n #多个文件 grep "text" -n file_1 file_2 ``` 打印样式匹配所位于的字符或字节偏移: ```shell echo gun is not unix | grep -b -o "not" 7:not #一行中字符串的字符便宜是从该行的第一个字符开始计算,起始值为0。选项 **-b -o** 一般总是配合使用。 ``` 搜索多个文件并查找匹配文本在哪些文件中: ```shell grep -l "text" file1 file2 file3... ``` ### grep 递归搜索文件 在多级目录中对文本进行递归搜索: ```shell grep "text" . -r -n # .表示当前目录。 ``` 忽略匹配样式中的字符大小写: ```shell echo "hello world" | grep -i "HELLO" # hello ``` 选项 **-e** 制动多个匹配样式: ```shell echo this is a text line | grep -e "is" -e "line" -o is line #也可以使用 **-f** 选项来匹配多个样式,在样式文件中逐行写出需要匹配的字符。 cat patfile aaa bbb echo aaa bbb ccc ddd eee | grep -f patfile -o ``` 在 grep 搜索结果中包括或者排除指定文件: ```shell # 只在目录中所有的.php和.html文件中递归搜索字符"main()" grep "main()" . -r --include *.{php,html} # 在搜索结果中排除所有README文件 grep "main()" . -r --exclude "README" # 在搜索结果中排除filelist文件列表里的文件 grep "main()" . -r --exclude-from filelist ``` 使用 0 值字节后缀的 grep 与 xargs: ```shell # 测试文件: echo "aaa" > file1 echo "bbb" > file2 echo "aaa" > file3 grep "aaa" file* -lZ | xargs -0 rm # 执行后会删除file1和file3,grep输出用-Z选项来指定以0值字节作为终结符文件名(\0),xargs -0 读取输入并用0值字节终结符分隔文件名,然后删除匹配文件,-Z通常和-l结合使用。 ``` grep 静默输出: ```shell grep -q "test" filename # 不会输出任何信息,如果命令运行成功返回0,失败则返回非0值。一般用于条件测试。 ``` 打印出匹配文本之前或者之后的行: ```shell # 显示匹配某个结果之后的3行,使用 -A 选项: seq 10 | grep "5" -A 3 5 6 7 8 # 显示匹配某个结果之前的3行,使用 -B 选项: seq 10 | grep "5" -B 3 2 3 4 5 # 显示匹配某个结果的前三行和后三行,使用 -C 选项: seq 10 | grep "5" -C 3 2 3 4 5 6 7 8 # 如果匹配结果有多个,会用“--”作为各匹配结果之间的分隔符: echo -e "a\nb\nc\na\nb\nc" | grep a -A 1 a b -- a b ``` ================================================ FILE: docs/linux/cli/iostat.md ================================================ # iostat 监视系统输入输出设备和 CPU 的使用情况 ## 补充说明 **iostat 命令** 被用于监视系统输入输出设备和 CPU 的使用情况。它的特点是汇报磁盘活动统计情况,同时也会汇报出 CPU 使用情况。同 vmstat 一样,iostat 也有一个弱点,就是它不能对某个进程进行深入分析,仅对系统的整体情况进行分析。 ### 语法 ```shell iostat(选项)(参数) ``` ### 选项 ```shell -c:仅显示CPU使用情况; -d:仅显示设备利用率; -k:显示状态以千字节每秒为单位,而不使用块每秒; -m:显示状态以兆字节每秒为单位; -p:仅显示块设备和所有被使用的其他分区的状态; -t:显示每个报告产生时的时间; -V:显示版号并退出; -x:显示扩展状态。 ``` ### 参数 - 间隔时间:每次报告的间隔时间(秒); - 次数:显示报告的次数。 ### 实例 用`iostat -x /dev/sda1`来观看磁盘 I/O 的详细情况: ```shell iostat -x /dev/sda1 Linux 2.6.18-164.el5xen (localhost.localdomain) 2010年03月26日 avg-cpu: %user %nice %system %iowait %steal %idle 0.11 0.02 0.18 0.35 0.03 99.31 Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn sda1 0.02 0.08 0.00 2014 4 ``` 详细说明:第二行是系统信息和监测时间,第三行和第四行显示 CPU 使用情况(具体内容和 mpstat 命令相同)。这里主要关注后面 I/O 输出的信息,如下所示: | 标示 | 说明 | | -------- | ----------------------------------- | | Device | 监测设备名称 | | rrqm/s | 每秒需要读取需求的数量 | | wrqm/s | 每秒需要写入需求的数量 | | r/s | 每秒实际读取需求的数量 | | w/s | 每秒实际写入需求的数量 | | rsec/s | 每秒读取区段的数量 | | wsec/s | 每秒写入区段的数量 | | rkB/s | 每秒实际读取的大小,单位为 KB | | wkB/s | 每秒实际写入的大小,单位为 KB | | avgrq-sz | 需求的平均大小区段 | | avgqu-sz | 需求的平均队列长度 | | await | 等待 I/O 平均的时间(milliseconds) | | svctm | I/O 需求完成的平均时间 | | %util | 被 I/O 需求消耗的 CPU 百分比 | ================================================ FILE: docs/linux/cli/iotop.md ================================================ # iotop 用来监视磁盘 I/O 使用状况的工具 ## 补充说明 **iotop 命令** 是一个用来监视磁盘 I/O 使用状况的 top 类工具。iotop 具有与 top 相似的 UI,其中包括 PID、用户、I/O、进程等相关信息。Linux 下的 IO 统计工具如 iostat,nmon 等大多数是只能统计到 per 设备的读写情况,如果你想知道每个进程是如何使用 IO 的就比较麻烦,使用 iotop 命令可以很方便的查看。 iotop 使用 Python 语言编写而成,要求 Python2.5(及以上版本)和 Linux kernel2.6.20(及以上版本)。iotop 提供有源代码及 rpm 包,可从其官方主页下载。 ### 安装 **Ubuntu** ```shell apt-get install iotop ``` **CentOS** ```shell yum install iotop ``` **编译安装** ```shell wget http://guichaz.free.fr/iotop/files/iotop-0.4.4.tar.gz tar zxf iotop-0.4.4.tar.gz python setup.py build python setup.py install ``` ### 语法 ```shell iotop(选项) ``` ### 选项 ```shell -o:只显示有io操作的进程 -b:批量显示,无交互,主要用作记录到文件。 -n NUM:显示NUM次,主要用于非交互式模式。 -d SEC:间隔SEC秒显示一次。 -p PID:监控的进程pid。 -u USER:监控的进程用户。 ``` **iotop 常用快捷键:** 1. 左右箭头:改变排序方式,默认是按 IO 排序。 2. r:改变排序顺序。 3. o:只显示有 IO 输出的进程。 4. p:进程/线程的显示方式的切换。 5. a:显示累积使用量。 6. q:退出。 ### 实例 直接执行 iotop 就可以看到效果了: ```shell Total DISK read: 0.00 B/s | Total DISK write: 0.00 B/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> command 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init [3] 2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd] 3 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/0] 4 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0] 5 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/0] 6 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/1] 7 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/1] 8 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/1] 9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/0] 10 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/1] 11 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [khelper] 2572 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [bluetooth] ``` ================================================ FILE: docs/linux/cli/linux-cli-dir.md ================================================ # Linux 文件目录管理 > 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `scp`, `mv`, `rm` ## 1. Linux 文件目录工作机制 ### 1.1. Linux 目录结构 linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用: ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/linux-folders.png) ### 1.2. Linux 文件属性 Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限。为了保护系统的安全性,Linux 系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定。 在 Linux 中我们可以使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组,如: ```bash $ ls -l total 64 drwxr-xr-x 2 root root 4096 Dec 14 2012 bin dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot ``` 实例中,bin 文件的第一个属性用 `d` 表示。`d` 在 Linux 中代表该文件是一个目录文件。 在 Linux 中第一个字符代表这个文件是目录、文件或链接文件等等。 - 当为 `d` 则是目录 - 当为 `-` 则是文件; - 若是 `l` 则表示为链接文档(link file); - 若是 `b` 则表示为装置文件里面的可供储存的接口设备(可随机存取装置); - 若是 `c` 则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。 接下来的字符中,以三个为一组,且均为『rwx』 的三个参数的组合。其中,`r` 代表可读(read)、`w` 代表可写(write)、`x` 代表可执行(execute)。 要注意的是,这三个权限的位置不会改变,如果没有权限,就会出现减号 `-` 而已。 每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920180927171909.png) 从左至右用 0-9 这些数字来表示。 - 第 0 位确定文件类型 - 第 1-3 位确定属主(该文件的拥有者)拥有该文件的权限。 - 第 4-6 位确定属组(拥有者的同组用户)拥有该文件的权限。 - 第 7-9 位确定其他用户拥有该文件的权限。 - 第 1、4、7 位表示读权限,如果用"r"字符表示,则有读权限,如果用"-"字符表示,则没有读权限。 - 第 2、5、8 位表示写权限,如果用"w"字符表示,则有写权限,如果用"-"字符表示没有写权限。 - 第 3、6、9 位表示可执行权限,如果用"x"字符表示,则有执行权限,如果用"-"字符表示,则没有执行权限。 ```bash d rwx r-x r-x ↑ ↑↑↑ ↑↑↑ ↑↑↑ │ │││ │││ │││-其他用户执行权限 (x/-) │ │││ │││ │└─ 其他用户写权限 (w/-) │ │││ │││ └── 其他用户读权限 (r/-) │ │││ ││└──── 属组用户执行权限 (x/-) │ │││ │└───── 属组用户写权限 (w/-) │ │││ └────── 属组用户读权限 (r/-) │ ││└──────── 属主用户执行权限 (x/-) │ │└───────── 属主用户写权限 (w/-) │ └────────── 属主用户读权限 (r/-) └──────────── 文件类型 (该文件是目录) ``` #### 1.2.1. Linux 文件属主和属组 ```bash $ ls -l total 64 drwxr-xr-x 2 root root 4096 Dec 14 2012 bin dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot ``` - 对于文件来说,它都有一个特定的拥有者,也就是对该文件具有所有权的用户。 - 同时,在 Linux 系统中,用户是按组分类的,一个用户属于一个或多个组。 - 文件拥有者以外的用户又可以分为文件拥有者的同组用户和其他用户。 - 因此,Linux 系统按文件拥有者、文件拥有者同组用户和其他用户来规定了不同的文件访问权限。 - 在以上实例中,bin 文件是一个目录文件,属主和属组都为 root,属主有可读、可写、可执行的权限;与属主同组的其他用户有可读和可执行的权限;其他用户也有可读和可执行的权限。 ## 2. Linux 文件目录管理要点 ### 2.1. 目录管理 - 切换目录 - 使用 [cd](#cd) - 查看目录信息 - 使用 [ls](#ls) - 显示当前目录的绝对路径 - 使用 [pwd](#pwd) - 树状显示目录的内容 - 使用 [tree](#tree) - 创建目录 - 使用 [mkdir](#mkdir) - 删除目录 - 使用 [rmdir](#rmdir) ### 2.2. 文件管理 - 创建空文件 - 使用 [touch](#touch) - 为文件创建连接 - 使用 [ln](#ln) - 批量重命名 - 使用 [rename](#rename) - 显示文件的详细信息 - 使用 [stat](#stat) - 探测文件类型 - 使用 [file](#file) - 设置文件或目录的权限 - 使用 [chmod](#chmod) - 设置文件或目录的拥有者或所属群组 - 使用 [chown](#chown) - 查找文件或目录 - 使用 [locate](#locate) - 在指定目录下查找文件 - 使用 [find](#find) - 查找命令的绝对路径 - 使用 [which](#which) - 查找命令的程序、源代码等相关文件 - 使用 [whereis](#whereis) ### 2.3. 文件和目录通用管理 - 复制文件或目录 - 使用 [cp](#cp) - 复制文件或目录到远程服务器 - 使用 [scp](#scp) - 移动文件或目录 - 使用 [mv](#mv) - 删除文件或目录 - 使用 [rm](#rm) ## 3. 命令常见用法 ### 3.1. cd > cd 命令用来切换工作目录。 > > 参考:http://man.linuxde.net/cd 示例: ```bash cd # 切换到用户主目录 cd ~ # 切换到用户主目录 cd - # 切换到上一个工作目录 cd .. # 切换到上级目录 cd ../.. # 切换到上两级目录 ``` ### 3.2. ls > ls 命令用来显示目录信息。 > > 参考:http://man.linuxde.net/ls 示例: ```bash ls # 列出当前目录可见文件 ls -l # 列出当前目录可见文件详细信息 ls -la # 列出所有文件(包括隐藏)的详细信息 ls -lh # 列出详细信息并以可读大小显示文件大小 ls -lt # 按时间列出文件和文件夹详细信息 ls -ltr # 按修改时间列出文件和文件夹详细信息 ls --color=auto # 列出文件并标记颜色分类 ``` ### 3.3. pwd > pwd 命令用来显示当前目录的绝对路径。 > > 参考:http://man.linuxde.net/pwd ### 3.4. mkdir > mkdir 命令用来创建目录。 > > 参考:http://man.linuxde.net/mkdir 示例: ```bash # 在当前目录中创建 zp 和 zp 的子目录 test mkdir -p zp/test # 在当前目录中创建 zp 和 zp 的子目录 test;权限设置为文件主可读、写、执行,同组用户可读和执行,其他用户无权访问 mkdir -p -m 750 zp/test ``` ### 3.5. rmdir > rmdir 命令用来删除空目录。 > > 参考:http://man.linuxde.net/rmdir 示例: ```bash # 删除子目录 test 和其父目录 zp rmdir -p zp/test ``` ### 3.6. tree > tree 命令以树状显示目录的内。 > > 参考:http://man.linuxde.net/tree 示例: ```bash # 列出目录 /private 第一级文件名 tree /private -L 1 /private/ ├── etc ├── tftpboot ├── tmp └── var # 忽略文件夹 tree -I node_modules # 忽略当前目录文件夹 node_modules tree -P node_modules # 列出当前目录文件夹 node_modules 的目录结构 tree -P node_modules -L 2 # 显示目录 node_modules 两层的目录树结构 tree -L 2 > /home/www/tree.txt # 当前目录结果存到 tree.txt 文件中 # 忽略多个文件夹 tree -I 'node_modules|icon|font' -L 2 ``` ### 3.7. touch > touch 命令有两个功能:一是用于把已存在文件的时间标签更新为系统当前的时间(默认方式),它们的数据将原封不动地保留下来;二是用来创建空文件。 > > 参考:http://man.linuxde.net/touch 示例: ``` touch ex2 ``` ### 3.8. ln > ln 命令用来为文件创建连接,连接类型分为硬连接和符号连接两种,默认的连接类型是硬连接。如果要创建符号连接必须使用"-s"选项。 > > 🔔 注意:符号链接文件不是一个独立的文件,它的许多属性依赖于源文件,所以给符号链接文件设置存取权限是没有意义的。 > > 参考:http://man.linuxde.net/ln 示例: ```bash # 将目录 /usr/mengqc/mub1 下的文件 m2.c 链接到目录 /usr/liu 下的文件 a2.c cd /usr/mengqc ln /mub1/m2.c /usr/liu/a2.c # 在目录 /usr/liu 下建立一个符号链接文件 abc,使它指向目录 /usr/mengqc/mub1 # 执行该命令后,/usr/mengqc/mub1 代表的路径将存放在名为 /usr/liu/abc 的文件中 ln -s /usr/mengqc/mub1 /usr/liu/abc ``` ### 3.9. rename > rename 命令用字符串替换的方式批量重命名。 > > 参考:http://man.linuxde.net/rename 示例: ```bash # 将 main1.c 重命名为 main.c rename main1.c main.c main1.c rename "s/AA/aa/" * # 把文件名中的 AA 替换成 aa rename "s//.html//.php/" * # 把 .html 后缀的改成 .php 后缀 rename "s/$//.txt/" * # 把所有的文件名都以 txt 结尾 rename "s//.txt//" * # 把所有以 .txt 结尾的文件名的.txt 删掉 ``` ### 3.10. stat > stat 命令用于显示文件的状态信息。stat 命令的输出信息比 ls 命令的输出信息要更详细。 > > 参考:http://man.linuxde.net/stat 示例: ```bash stat myfile ``` ### 3.11. file > file 命令用来探测给定文件的类型。file 命令对文件的检查分为文件系统、魔法幻数检查和语言检查 3 个过程。 > > 参考:http://man.linuxde.net/file 示例: ```bash file install.log # 显示文件类型 file -b install.log # 不显示文件名称 file -i install.log # 显示 MIME 类型 file -L /var/spool/mail # 显示符号链接的文件类型 ``` ### 3.12. chmod > chmod 命令用来变更文件或目录的权限。在 UNIX 系统家族里,文件或目录权限的控制分别以读取、写入、执行 3 种一般权限来区分,另有 3 种特殊权限可供运用。用户可以使用 chmod 指令去变更文件与目录的权限,设置方式采用文字或数字代号皆可。符号连接的权限无法变更,如果用户对符号连接修改权限,其改变会作用在被连接的原始文件。 > > 参考:http://man.linuxde.net/chmod 知识扩展: Linux 用 户分为:拥有者、组群(Group)、其他(other),Linux 系统中,预设的情況下,系统中所有的帐号与一般身份使用者,以及 root 的相关信 息, 都是记录在`/etc/passwd`文件中。每个人的密码则是记录在`/etc/shadow`文件下。 此外,所有的组群名称记录在`/etc/group`內! linux 文件的用户权限的分析图 ```bash -rw-r--r-- 1 user staff 651 Oct 12 12:53 .gitmodules # ↑╰┬╯╰┬╯╰┬╯ # ┆ ┆ ┆ ╰┈ 0 其他人 # ┆ ┆ ╰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ g 属组 # ┆ ╰┈┈┈┈ u 属组 # ╰┈┈ 第一个字母 `d` 代表目录,`-` 代表普通文件 ``` 例:rwx   rw-  r-- r=读取属性  //值= 4 w=写入属性  //值= 2 x=执行属性  //值= 1 示例: ```bash chmod u+x,g+w f01  # 为文件f01设置自己可以执行,组员可以写入的权限 chmod u=rwx,g=rw,o=r f01 chmod 764 f01 chmod a+x f01   # 对文件f01的u,g,o都设置可执行属性 # 将/home/wwwroot/里的所有文件和文件夹设置为755权限 chmod -R 755 /home/wwwroot/* ``` ### 3.13. chown > chown 命令改变某个文件或目录的所有者和所属的组,该命令可以向某个用户授权,使该用户变成指定文件的所有者或者改变文件所属的组。用户可以是用户或者是用户 D,用户组可以是组名或组 id。文件名可以使由空格分开的文件列表,在文件名中可以包含通配符。 > > 只有文件拥有者和超级用户才可以便用该命令。 > > 参考:http://man.linuxde.net/chown 示例: ```bash # 将目录/usr/meng及其下面的所有文件、子目录的文件主改成 liu chown -R liu /usr/meng ``` ### 3.14. locate > locate 命令和 slocate 命令都用来查找文件或目录。 > > locate 命令其实是 find -name 的另一种写法,但是要比后者快得多,原因在于它不搜索具体目录,而是搜索一个数据库/var/lib/locatedb,这个数据库中含有本地所有文件信息。Linux 系统自动创建这个数据库,并且每天自动更新一次,所以使用 locate 命令查不到最新变动过的文件。为了避免这种情况,可以在使用 locate 之前,先使用 updatedb 命令,手动更新数据库。 > > 参考:http://man.linuxde.net/locate_slocate 示例: ```bash locate pwd # 查找和 pwd 相关的所有文件 locate /etc/sh # 搜索 etc 目录下所有以 sh 开头的文件 ``` ### 3.15. find > find 命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则 find 命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。 > > 参考:http://man.linuxde.net/find ```bash # 当前目录搜索所有文件,文件内容 包含 “140.206.111.111” 的内容 find . -type f -name "*" | xargs grep "140.206.111.111" # 列出当前目录及子目录下所有文件和文件夹 find . # 在 /home 目录下查找以 .txt 结尾的文件名 find /home -name "*.txt" # 同上,但忽略大小写 find /home -iname "*.txt" # 当前目录及子目录下查找所有以 .txt 和 .pdf 结尾的文件 find . -name "*.txt" -o -name "*.pdf" # 匹配文件路径或者文件 find /usr/ -path "*local*" # 基于正则表达式匹配文件路径 find . -regex ".*\(\.txt\|\.pdf\)$" # 同上,但忽略大小写 find . -iregex ".*\(\.txt\|\.pdf\)$" # 找出 /home 下不是以 .txt 结尾的文件 find /home ! -name "*.txt" ``` ### 3.16. cp > cp 命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录。它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下。cp 命令还支持同时复制多个文件,当一次复制多个文件时,目标文件参数必须是一个已经存在的目录,否则将出现错误。 > > 参考:http://man.linuxde.net/cp 示例: #### 3.16.1. 参数 - 源文件:制定源文件列表。默认情况下,cp 命令不能复制目录,如果要复制目录,则必须使用`-R`选项; - 目标文件:指定目标文件。当“源文件”为多个文件时,要求“目标文件”为指定的目录。 示例: ```bash # 将文件 file 复制到目录 /usr/men/tmp 下,并改名为 file1 cp file /usr/men/tmp/file1 # 将目录 /usr/men下的所有文件及其子目录复制到目录 /usr/zh 中 cp -r /usr/men /usr/zh # 强行将 /usr/men下的所有文件复制到目录 /usr/zh 中,无论是否有文件重复 cp -rf /usr/men/* /usr/zh # 将目录 /usr/men 中的以 m 打头的所有 .c 文件复制到目录 /usr/zh 中 cp -i /usr/men m*.c /usr/zh ``` ### 3.17. scp > scp 命令用于在 Linux 下进行远程拷贝文件的命令,和它类似的命令有 cp,不过 cp 只是在本机进行拷贝不能跨服务器,而且 scp 传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system 时,用 scp 可以帮你把文件移出来。另外,scp 还非常不占资源,不会提高多少系统负荷,在这一点上,rsync 就远远不及它了。虽然 rsync 比 scp 会快一点,但当小文件众多的情况下,rsync 会导致硬盘 I/O 非常高,而 scp 基本不影响系统正常使用。 示例: ```bash # 拷贝文件到远程服务器的指定目录 scp @: scp test.txt root@192.168.0.1:/opt # 拷贝目录到远程服务器的指定目录 scp -r @: scp -r test root@192.168.0.1:/opt ``` #### 3.17.1. 免密码传输 (1)生成 ssh 公私钥对 ``` ssh-keygen -t rsa ``` (2)将服务器 A 的 `\~/.ssh/id_rsa.pub` 文件内容复制到服务器 B 的 `\~/.ssh/authorized_keys` 文件中。 ```bash # 服务器 A 上执行以下命令 scp ~/.ssh/id_rsa.pub root@192.168.0.2:~/.ssh/id_rsa.pub.tmp # 服务器 B 上执行以下命令 cat ~/.ssh/id_rsa.pub.tmp >> ~/.ssh/authorized_keys rm ~/.ssh/id_rsa.pub.tmp ``` ### 3.18. mv > mv 命令用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中。source 表示源文件或目录,target 表示目标文件或目录。如果将一个文件移到一个已经存在的目标文件中,则目标文件的内容将被覆盖。 > > 参考:http://man.linuxde.net/mv 示例: ```bash mv file1.txt /home/office/ # 移动单个文件 mv file2.txt file3.txt file4.txt /home/office/ # 移动多个文件 mv *.txt /home/office/ # 移动所有 txt 文件 mv dir1/ /home/office/ # 移动目录 mv /usr/men/* . # 将指定目录中的所有文件移到当前目录中 mv file1.txt file2.txt # 重命名文件 mv dir1/ dir2/ # 重命名目录 mv -v *.txt /home/office # 打印移动信息 mv -i file1.txt /home/office # 提示是否覆盖文件 mv -uv *.txt /home/office # 源文件比目标文件新时才执行更新 mv -vn *.txt /home/office # 不要覆盖任何已存在的文件 mv -f *.txt /home/office # 无条件覆盖已经存在的文件 mv -bv *.txt /home/office # 复制时创建备份 ``` ### 3.19. rm > rm 命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉。对于链接文件,只是删除整个链接文件,而原有文件保持不变。 > > 参考:http://man.linuxde.net/rm ```bash rm test.txt # 删除文件 rm -i test.txt test2.txt # 交互式删除文件 rm -r * # 删除当前目录下的所有文件和目录 rm -r testdir # 删除目录下的所有文件和目录 rm -rf testdir # 强制删除目录下的所有文件和目录 rm -v testdir # 显示当前删除操作的详情 ``` ================================================ FILE: docs/linux/cli/linux-cli-file-compress.md ================================================ # Linux 文件压缩和解压 > 关键词:`tar`, `gzip`, `zip`, `unzip` ## 1. Linux 文件压缩和解压要点 - 压缩和解压 tar 文件 - 使用 [tar](#tar) - 压缩和解压 gz 文件 - 使用 [gzip](#gzip) - 压缩和解压 zip 文件 - 分别使用 [zip](#zip)、[unzip](#unzip) ## 2. 命令常见用法 ### 2.1. tar > tar 命令可以为 linux 的文件和目录创建档案。利用 tar,可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件。tar 最初被用来在磁带上创建档案,现在,用户可以在任何设备上创建档案。利用 tar 命令,可以把一大堆的文件和目录全部打包成一个文件,这对于备份文件或将几个文件组合成为一个文件以便于网络传输是非常有用的。 > > 参考:http://man.linuxde.net/tar 示例: ```bash tar -cvf log.tar log2012.log # 仅打包,不压缩 tar -zcvf log.tar.gz log2012.log # 打包后,以 gzip 压缩 tar -jcvf log.tar.bz2 log2012.log # 打包后,以 bzip2 压缩 tar -ztvf log.tar.gz # 查阅上述 tar 包内有哪些文件 tar -zxvf log.tar.gz # 将 tar 包解压缩 tar -zxvf log30.tar.gz log2013.log # 只将 tar 内的部分文件解压出来 ``` ### 2.2. gzip > gzip 命令用来压缩文件。gzip 是个使用广泛的压缩程序,文件经它压缩过后,其名称后面会多出“.gz”扩展名。 > > gzip 是在 Linux 系统中经常使用的一个对文件进行压缩和解压缩的命令,既方便又好用。gzip 不仅可以用来压缩大的、较少使用的文件以节省磁盘空间,还可以和 tar 命令一起构成 Linux 操作系统中比较流行的压缩文件格式。据统计,gzip 命令对文本文件有 60%~ 70%的压缩率。减少文件大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。 > > 参考:http://man.linuxde.net/gzip 示例: ```bash gzip * # 将所有文件压缩成 .gz 文件 gzip -l * # 详细显示压缩文件的信息,并不解压 gzip -dv * # 解压上例中的所有压缩文件,并列出详细的信息 gzip -r log.tar # 压缩一个 tar 备份文件,此时压缩文件的扩展名为.tar.gz gzip -rv test/ # 递归的压缩目录 gzip -dr test/ # 递归地解压目录 ``` ### 2.3. zip > zip 命令可以用来解压缩文件,或者对文件进行打包操作。zip 是个使用广泛的压缩程序,文件经它压缩后会另外产生具有“.zip”扩展名的压缩文件。 > > 参考:http://man.linuxde.net/zip 示例: ```bash # 将 /home/Blinux/html/ 这个目录下所有文件和文件夹打包为当前目录下的 html.zip zip -q -r html.zip /home/Blinux/html ``` ### 2.4. unzip > unzip 命令用于解压缩由 zip 命令压缩的“.zip”压缩包。 > > 参考:http://man.linuxde.net/unzip 示例: ```bash unzip test.zip # 解压 zip 文件 unzip -n test.zip -d /tmp/ # 在指定目录下解压缩 unzip -o test.zip -d /tmp/ # 在指定目录下解压缩,如果有相同文件存在则覆盖 unzip -v test.zip # 查看压缩文件目录,但不解压 ``` ================================================ FILE: docs/linux/cli/linux-cli-file.md ================================================ # Linux 文件内容查看编辑 > 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep` ## 1. Linux 文件内容查看编辑要点 - 连接文件并打印到标准输出设备 - 使用 [cat](#cat) - 显示指定文件的开头若干行 - 使用 [head](#head) - 显示指定文件的末尾若干行,常用于实时打印日志文件内容 - 使用 [tail](#tail) - 显示文件内容,每次显示一屏 - 使用 [more](#more) - 显示文件内容,每次显示一屏 - 使用 [less](#less) - 自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等 - 使用 [sed](#sed) - 文本编辑器 - 使用 [vi](#vi) - 使用正则表达式搜索文本,并把匹配的行打印出来 - 使用 [grep](#grep) ## 2. 命令常见用法 ### 2.1. cat > cat 命令用于连接文件并打印到标准输出设备上。 > > 参考:http://man.linuxde.net/cat 示例: ```bash cat m1 # 在屏幕上显示文件 ml 的内容 cat m1 m2 # 同时显示文件 ml 和 m2 的内容 cat m1 m2 > file # 将文件 ml 和 m2 合并后放入文件 file 中 ``` ### 2.2. head > head 命令用于显示文件的开头内容。在默认情况下,head 命令显示文件的头部 10 行内容。 > > 参考:http://man.linuxde.net/head ### 2.3. tail > tail 命令用于显示文件的尾部内容。在默认情况下,tail 命令显示文件的尾部 10 行内容。如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题。如果没有指定文件或者文件名为“-”,则读取标准输入。 > > 参考:http://man.linuxde.net/tail 示例: ```bash tail file # 显示文件file的最后10行 tail -n +20 file # 显示文件file的内容,从第20行至文件末尾 tail -c 10 file # 显示文件file的最后10个字符 ``` ### 2.4. more > more 命令是一个基于 vi 编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持 vi 中的关键字定位操作。more 名单中内置了若干快捷键,常用的有 H(获得帮助信息),Enter(向下翻滚一行),空格(向下滚动一屏),Q(退出命令)。 > > 该命令一次显示一屏文本,满屏后停下来,并且在屏幕的底部出现一个提示信息,给出至今己显示的该文件的百分比:--More--(XX%)可以用下列不同的方法对提示做出回答: > > - 按 Space 键:显示文本的下一屏内容。 > - 按 Enier 键:只显示文本的下一行内容。 > - 按斜线符|:接着输入一个模式,可以在文本中寻找下一个相匹配的模式。 > - 按 H 键:显示帮助屏,该屏上有相关的帮助信息。 > - 按 B 键:显示上一屏内容。 > - 按 Q 键:退出 rnore 命令。 > > 参考:http://man.linuxde.net/more 示例: ```bash # 显示文件 file 的内容,但在显示之前先清屏,并且在屏幕的最下方显示完核的百分比。 more -dc file # 显示文件 file 的内容,每 10 行显示一次,而且在显示之前先清屏。 more -c -10 file ``` ### 2.5. less less 命令的作用与 more 十分相似,都可以用来浏览文字档案的内容,不同的是 less 命令允许用户向前或向后浏览文件,而 more 命令只能向前浏览。用 less 命令显示文件时,用 PageUp 键向上翻页,用 PageDown 键向下翻页。要退出 less 程序,应按 Q 键。 示例: ```bash less /var/log/shadowsocks.log ``` ### 2.6. sed > sed 是一种流编辑器,它是文本处理工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed 主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。 > > 参考:http://man.linuxde.net/sed 示例: ```bash # 替换文本中的字符串 sed 's/book/books/' file # -n 选项 和 p 命令 一起使用表示只打印那些发生替换的行 sed -n 's/test/TEST/p' file # 直接编辑文件选项 -i ,会匹配 file 文件中每一行的第一个 book 替换为 books sed -i 's/book/books/g' file # 使用后缀 /g 标记会替换每一行中的所有匹配 sed 's/book/books/g' file # 删除空白行 sed '/^$/d' file # 删除文件的第2行 sed '2d' file # 删除文件的第2行到末尾所有行 sed '2,$d' file # 删除文件最后一行 sed '$d' file # 删除文件中所有开头是test的行 sed '/^test/'d file ``` ### 2.7. vi > vi 命令是 UNIX 操作系统和类 UNIX 操作系统中最通用的全屏幕纯文本编辑器。Linux 中的 vi 编辑器叫 vim,它是 vi 的增强版(vi Improved),与 vi 编辑器完全兼容,而且实现了很多增强功能。 > > 参考:http://man.linuxde.net/vi > > 引申阅读:[Vim 入门指南](https://github.com/dunwu/OS/blob/master/docs/vim.md) ### 2.8. grep > grep(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。 > > 参考:http://man.linuxde.net/grep 示例: ```bash # 在多级目录中对文本递归搜索(程序员搜代码的最爱): $ grep "class" . -R -n # 忽略匹配样式中的字符大小写 $ echo "hello world" | grep -i "HELLO" # 匹配多个模式: $ grep -e "class" -e "vitural" file # 只在目录中所有的.php和.html文件中递归搜索字符"main()" $ grep "main()" . -r --include *.{php,html} # 在搜索结果中排除所有README文件 $ grep "main()" . -r --exclude "README" # 在搜索结果中排除filelist文件列表里的文件 $ grep "main()" . -r --exclude-from filelist ``` ## 3. 参考资料 - [Linux 命令大全](http://man.linuxde.net/) ================================================ FILE: docs/linux/cli/linux-cli-hardware.md ================================================ # Linux 硬件管理 > 关键词:`df`, `du`, `top`, `free`, `iotop` ## 1. Linux 硬件管理要点 - 查看磁盘空间 - 使用 [df](#df) - 查看文件或目录的磁盘空间 - 使用 [du](#du) - 实时查看系统整体运行状态(如:CPU、内存) - 使用 [top](#top) - 查看已使用和未使用的内存 - 使用 [free](#free) - 查看磁盘 I/O 使用状况 - 使用 [iotop](#iotop) ## 2. 命令常见用法 ### 2.1. df > df 命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为 KB。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。 > > 参考:http://man.linuxde.net/df 示例: ```bash # 查看系统磁盘设备,默认是 KB 为单位 [root@LinServ-1 ~]# df 文件系统 1K-块 已用 可用 已用% 挂载点 /dev/sda2 146294492 28244432 110498708 21% / /dev/sda1 1019208 62360 904240 7% /boot tmpfs 1032204 0 1032204 0% /dev/shm /dev/sdb1 2884284108 218826068 2518944764 8% /data1 # 使用 -h 选项以 KB 以上的单位来显示,可读性高 [root@LinServ-1 ~]# df -h 文件系统 容量 已用 可用 已用% 挂载点 /dev/sda2 140G 27G 106G 21% / /dev/sda1 996M 61M 884M 7% /boot tmpfs 1009M 0 1009M 0% /dev/shm /dev/sdb1 2.7T 209G 2.4T 8% /data1 # 查看全部文件系统 [root@LinServ-1 ~]# df -a 文件系统 1K-块 已用 可用 已用% 挂载点 /dev/sda2 146294492 28244432 110498708 21% / proc 0 0 0 - /proc sysfs 0 0 0 - /sys devpts 0 0 0 - /dev/pts /dev/sda1 1019208 62360 904240 7% /boot tmpfs 1032204 0 1032204 0% /dev/shm /dev/sdb1 2884284108 218826068 2518944764 8% /data1 none 0 0 0 - /proc/sys/fs/binfmt_misc ``` ### 2.2. du > du 命令也是查看使用空间的,但是与 df 命令不同的是:du 命令是对文件和目录磁盘使用的空间的查看,还是和 df 命令有一些区别的。 > > 参考:http://man.linuxde.net/du 示例: ```bash # 显示目录或者文件所占空间 root@localhost [test]# du 608 ./test6 308 ./test4 4 ./scf/lib 4 ./scf/service/deploy/product 4 ./scf/service/deploy/info 12 ./scf/service/deploy 16 ./scf/service 4 ./scf/doc 4 ./scf/bin 32 ./scf 8 ./test3 1288 . # 显示指定文件所占空间 [root@localhost test]# du log2012.log 300 log2012.log # 查看指定目录的所占空间 [root@localhost test]# du scf 4 scf/lib 4 scf/service/deploy/product 4 scf/service/deploy/info 12 scf/service/deploy 16 scf/service 4 scf/doc 4 scf/bin 32 scf # 显示多个文件所占空间 [root@localhost test]# du log30.tar.gz log31.tar.gz 4 log30.tar.gz 4 log31.tar.gz # 只显示总和的大小 [root@localhost test]# du -s 1288 . [root@localhost test]# du -s scf 32 scf ``` ### 2.3. top > top 命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过 top 命令所提供的互动式界面,用热键可以管理。 > > 参考:http://man.linuxde.net/top ### 2.4. free > free 命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。 > > 参考:http://man.linuxde.net/free 示例: ```bash free -t # 以总和的形式显示内存的使用信息 free -s 10 # 周期性的查询内存使用信息,每10s 执行一次命令 # 显示内存使用情况 free -m total used free shared buffers cached Mem: 2016 1973 42 0 163 1497 -/+ buffers/cache: 312 1703 Swap: 4094 0 4094 ``` ### 2.5. iotop > iotop 命令是一个用来监视磁盘 I/O 使用状况的 top 类工具。iotop 具有与 top 相似的 UI,其中包括 PID、用户、I/O、进程等相关信息。Linux 下的 IO 统计工具如 iostat,nmon 等大多数是只能统计到 per 设备的读写情况,如果你想知道每个进程是如何使用 IO 的就比较麻烦,使用 iotop 命令可以很方便的查看。 > > 参考:http://man.linuxde.net/iotop 示例: ```bash Total DISK read: 0.00 B/s | Total DISK write: 0.00 B/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> command 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init [3] 2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd] 3 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/0] 4 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0] 5 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/0] 6 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/1] 7 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/1] 8 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/1] 9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/0] 10 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/1] 11 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [khelper] 2572 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [bluetooth] ``` ================================================ FILE: docs/linux/cli/linux-cli-help.md ================================================ # 查看 Linux 命令帮助信息 > Linux 中有非常多的命令,想全部背下来是很困难的事。所以,我认为学习 Linux 的第一步,就是了解如何快速检索命令说明。 > > 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man` ## 1. 查看 Linux 命令帮助信息的要点 - 查看 Shell 内部命令的帮助信息 - 使用 [help](#help) - 查看命令的简要说明 - 使用 [whatis](#whatis) - 查看命令的详细说明 - 使用 [info](#info) - 查看命令的位置 - 使用 [which](#which) - 定位指令的二进制程序、源代码文件和 man 手册页等相关文件的路径 - 使用 [whereis](#whereis) - 查看命令的帮助手册(包含说明、用法等信息) - 使用 [man](#man) - 只记得部分命令关键字 - 使用 man -k > 注:推荐一些 Linux 命令中文手册: > > - [Linux 命令大全](http://man.linuxde.net/) > - [linux-command](https://github.com/jaywcjlove/linux-command) ## 2. 命令常见用法 ### 2.1. help > help 命令用于查看 Shell 内部命令的帮助信息。而对于外部命令的帮助信息只能使用 man 或者 info 命令查看。 > > 参考:http://man.linuxde.net/help ### 2.2. whatis > whatis 用于查询一个命令执行什么功能。 > > 参考:http://man.linuxde.net/whatis 示例: ```bash # 查看 man 命令的简要说明 $ whatis man # 查看以 loca 开拓的命令的简要说明 $ whatis -w "loca*" ``` ### 2.3. info > info 是 Linux 下 info 格式的帮助指令。 > > 参考:http://man.linuxde.net/info 示例: ```bash # 查看 man 命令的详细说明 $ info man ``` ### 2.4. which > which 命令用于查找并显示给定命令的绝对路径,环境变量 PATH 中保存了查找命令时需要遍历的目录。which 指令会在环境变量$PATH 设置的目录里查找符合条件的文件。也就是说,使用 which 命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。 > > 参考:http://man.linuxde.net/which 示例: ```bash which pwd # 查找命令的路径 ``` 说明:which 是根据使用者所配置的 PATH 变量内的目录去搜寻可运行档的!所以,不同的 PATH 配置内容所找到的命令当然不一样的! ```bash [root@localhost ~]# which cd cd: shell built-in command ``` cd 这个常用的命令竟然找不到啊!为什么呢?这是因为 cd 是 bash 内建的命令!但是 which 默认是找 PATH 内所规范的目录,所以当然一定找不到的! ### 2.5. whereis > whereis 命令用来定位指令的二进制程序、源代码文件和 man 手册页等相关文件的路径。 > > whereis 命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man 说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。 > > 参考:http://man.linuxde.net/whereis 示例: ```bash whereis git # 将相关的文件都查找出来 ``` ### 2.6. man > man 命令是 Linux 下的帮助指令,通过 man 指令可以查看 Linux 中的指令帮助、配置文件帮助和编程帮助等信息。 > > 参考:http://man.linuxde.net/man 示例: ```bash $ man date # 查看 date 命令的帮助手册 $ man 3 printf # 查看 printf 命令的帮助手册中的第 3 类 $ man -k keyword # 根据命令中部分关键字来查询命令 ``` #### 2.6.1. man 要点 在 man 的帮助手册中,可以使用 page up 和 page down 来上下翻页。 man 的帮助手册中,将帮助文档分为了 9 个类别,对于有的关键字可能存在多个类别中, 我们就需要指定特定的类别来查看;(一般我们查询 bash 命令,归类在 1 类中)。 man 页面的分类(常用的是分类 1 和分类 3): 1. 可执行程序或 shell 命令 2. 系统调用(内核提供的函数) 3. 库调用(程序库中的函数) 4. 特殊文件(通常位于 /dev) 5. 文件格式和规范,如 /etc/passwd 6. 游戏 7. 杂项(包括宏包和规范,如 man(7),groff(7)) 8. 系统管理命令(通常只针对 root 用户) 9. 内核例程 [非标准] 前面说到使用 whatis 会显示命令所在的具体的文档类别,我们学习如何使用它 ```bash $ whatis printf printf (1) - format and print data printf (1p) - write formatted output printf (3) - formatted output conversion printf (3p) - print formatted output printf [builtins](1) - bash built-in commands, see bash(1) ``` 我们看到 printf 在分类 1 和分类 3 中都有;分类 1 中的页面是命令操作及可执行文件的帮助;而 3 是常用函数库说明;如果我们想看的是 C 语言中 printf 的用法,可以指定查看分类 3 的帮助: ```bash $ man 3 printf ``` ## 3. 参考资料 https://linuxtools-rst.readthedocs.io/zh_CN/latest/base/01_use_man.html ================================================ FILE: docs/linux/cli/linux-cli-net.md ================================================ # Linux 网络管理 > 关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat` ## 1. Linux 网络应用要点 - 下载文件 - 使用 [curl](#curl)、[wget](#wget) - telnet 方式登录远程主机,对远程主机进行管理 - 使用 [telnet](#telnet) - 查看或操纵 Linux 主机的路由、网络设备、策略路由和隧道 - 使用 [ip](#ip) - 查看和设置系统的主机名 - 使用 [hostname](#hostname) - 查看和配置 Linux 内核中网络接口的网络参数 - 使用 [ifconfig](#ifconfig) - 查看和设置 Linux 内核中的网络路由表 - 使用 [route](#route) - ssh 方式连接远程主机 - 使用 ssh - 为 ssh 生成、管理和转换认证密钥 - 使用 [ssh-keygen](#ssh-keygen) - 查看、设置防火墙(Centos7),使用 [firewalld](#firewalld) - 查看、设置防火墙(Centos7 以前),使用 [iptables](#iptables) - 查看域名信息 - 使用 [host](#host), [nslookup](#nslookup) - 设置路由 - 使用 [nc/netcat](#ncnetcat) - 测试主机之间网络是否连通 - 使用 [ping](#ping) - 追踪数据在网络上的传输时的全部路径 - 使用 [traceroute](#traceroute) - 查看当前工作的端口信息 - 使用 [netstat](#netstat) ## 2. 命令常见用法 ### 2.1. curl > curl 命令是一个利用 URL 规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称 curl 为下载工具。作为一款强力工具,curl 支持包括 HTTP、HTTPS、ftp 等众多协议,还支持 POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl 可以祝一臂之力。 > > 参考:http://man.linuxde.net/curl 示例: ```bash # 下载文件 $ curl http://man.linuxde.net/text.iso --silent # 下载文件,指定下载路径,并查看进度 $ curl http://man.linuxde.net/test.iso -o filename.iso --progress ########################################## 100.0% ``` ### 2.2. wget > wget 命令用来从指定的 URL 下载文件。 > > 参考:http://man.linuxde.net/wget 示例: ```bash # 使用 wget 下载单个文件 $ wget http://www.linuxde.net/testfile.zip ``` ### 2.3. telnet > telnet 命令用于登录远程主机,对远程主机进行管理。 > > 参考:http://man.linuxde.net/telnet 示例: ```bash telnet 192.168.2.10 Trying 192.168.2.10... Connected to 192.168.2.10 (192.168.2.10). Escape character is '^]'. localhost (Linux release 2.6.18-274.18.1.el5 #1 SMP Thu Feb 9 12:45:44 EST 2012) (1) login: root Password: Login incorrect ``` ### 2.4. ip > ip 命令用来查看或操纵 Linux 主机的路由、网络设备、策略路由和隧道,是 Linux 下较新的功能强大的网络配置工具。 > > 参考:http://man.linuxde.net/ip 示例: ```bash $ ip link show # 查看网络接口信息 $ ip link set eth0 upi # 开启网卡 $ ip link set eth0 down # 关闭网卡 $ ip link set eth0 promisc on # 开启网卡的混合模式 $ ip link set eth0 promisc offi # 关闭网卡的混个模式 $ ip link set eth0 txqueuelen 1200 # 设置网卡队列长度 $ ip link set eth0 mtu 1400 # 设置网卡最大传输单元 $ ip addr show # 查看网卡IP信息 $ ip addr add 192.168.0.1/24 dev eth0 # 设置eth0网卡IP地址192.168.0.1 $ ip addr del 192.168.0.1/24 dev eth0 # 删除eth0网卡IP地址 $ ip route show # 查看系统路由 $ ip route add default via 192.168.1.254 # 设置系统默认路由 $ ip route list # 查看路由信息 $ ip route add 192.168.4.0/24 via 192.168.0.254 dev eth0 # 设置192.168.4.0网段的网关为192.168.0.254,数据走eth0接口 $ ip route add default via 192.168.0.254 dev eth0 # 设置默认网关为192.168.0.254 $ ip route del 192.168.4.0/24 # 删除192.168.4.0网段的网关 $ ip route del default # 删除默认路由 $ ip route delete 192.168.1.0/24 dev eth0 # 删除路由 ``` ### 2.5. hostname > hostname 命令用于查看和设置系统的主机名称。环境变量 HOSTNAME 也保存了当前的主机名。在使用 hostname 命令设置主机名后,系统并不会永久保存新的主机名,重新启动机器之后还是原来的主机名。如果需要永久修改主机名,需要同时修改 `/etc/hosts` 和 `/etc/sysconfig/network` 的相关内容。 > > 参考:http://man.linuxde.net/hostname 示例: ```bash $ hostname AY1307311912260196fcZ ``` ### 2.6. ifconfig > ifconfig 命令被用于查看和配置 Linux 内核中网络接口的网络参数。用 ifconfig 命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在。要想将上述的配置信息永远的存的电脑里,那就要修改网卡的配置文件了。 > > 参考:http://man.linuxde.net/ifconfig 示例: ```bash # 查看网络设备信息(激活状态的) [root@localhost ~]# ifconfig eth0 Link encap:Ethernet HWaddr 00:16:3E:00:1E:51 inet addr:10.160.7.81 Bcast:10.160.15.255 Mask:255.255.240.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:61430830 errors:0 dropped:0 overruns:0 frame:0 TX packets:88534 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:3607197869 (3.3 GiB) TX bytes:6115042 (5.8 MiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:56103 errors:0 dropped:0 overruns:0 frame:0 TX packets:56103 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:5079451 (4.8 MiB) TX bytes:5079451 (4.8 MiB) ``` ### 2.7. route > route 命令用来查看和设置 Linux 内核中的网络路由表,route 命令设置的路由主要是静态路由。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。 > > 参考:http://man.linuxde.net/route 示例: ```bash # 查看当前路由 route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 112.124.12.0 * 255.255.252.0 U 0 0 0 eth1 10.160.0.0 * 255.255.240.0 U 0 0 0 eth0 192.168.0.0 10.160.15.247 255.255.0.0 UG 0 0 0 eth0 172.16.0.0 10.160.15.247 255.240.0.0 UG 0 0 0 eth0 10.0.0.0 10.160.15.247 255.0.0.0 UG 0 0 0 eth0 default 112.124.15.247 0.0.0.0 UG 0 0 0 eth1 route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 # 添加网关/设置网关 route add -net 224.0.0.0 netmask 240.0.0.0 reject # 屏蔽一条路由 route del -net 224.0.0.0 netmask 240.0.0.0 # 删除路由记录 route add default gw 192.168.120.240 # 添加默认网关 route del default gw 192.168.120.240 # 删除默认网关 ``` ### 2.8. ssh > ssh 命令是 openssh 套件中的客户端连接工具,可以给予 ssh 加密协议实现安全的远程登录服务器。 > > 参考:http://man.linuxde.net/ssh 示例: ```bash # ssh 用户名@远程服务器地址 ssh user1@172.24.210.101 # 指定端口 ssh -p 2211 root@140.206.185.170 ``` 引申阅读:[ssh 背后的故事](https://linux.cn/article-8476-1.html) ### 2.9. ssh-keygen > ssh-keygen 命令用于为 ssh 生成、管理和转换认证密钥,它支持 RSA 和 DSA 两种认证密钥。 > > 参考:http://man.linuxde.net/ssh-keygen ### 2.10. firewalld > firewalld 命令是 Linux 上的防火墙软件(Centos7 默认防火墙)。 > > 参考:https://www.cnblogs.com/moxiaoan/p/5683743.html #### 2.10.1. firewalld 的基本使用 - 启动 - systemctl start firewalld - 关闭 - systemctl stop firewalld - 查看状态 - systemctl status firewalld - 开机禁用 - systemctl disable firewalld - 开机启用 - systemctl enable firewalld #### 2.10.2. 使用 systemctl 管理 firewalld 服务 systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 service 和 chkconfig 的功能于一体。 - 启动一个服务 - systemctl start firewalld.service - 关闭一个服务 - systemctl stop firewalld.service - 重启一个服务 - systemctl restart firewalld.service - 显示一个服务的状态 - systemctl status firewalld.service - 在开机时启用一个服务 - systemctl enable firewalld.service - 在开机时禁用一个服务 - systemctl disable firewalld.service - 查看服务是否开机启动 - systemctl is-enabled firewalld.service - 查看已启动的服务列表 - systemctl list-unit-files|grep enabled - 查看启动失败的服务列表 - systemctl --failed #### 2.10.3. 配置 firewalld-cmd - 查看版本 - firewall-cmd --version - 查看帮助 - firewall-cmd --help - 显示状态 - firewall-cmd --state - 查看所有打开的端口 - firewall-cmd --zone=public --list-ports - 更新防火墙规则 - firewall-cmd --reload - 查看区域信息: firewall-cmd --get-active-zones - 查看指定接口所属区域 - firewall-cmd --get-zone-of-interface=eth0 - 拒绝所有包:firewall-cmd --panic-on - 取消拒绝状态 - firewall-cmd --panic-off - 查看是否拒绝 - firewall-cmd --query-panic #### 2.10.4. 在防火墙中开放一个端口 - 添加(--permanent 永久生效,没有此参数重启后失效) - firewall-cmd --zone=public --add-port=80/tcp --permanent - 重新载入 - firewall-cmd --reload - 查看 - firewall-cmd --zone= public --query-port=80/tcp - 删除 - firewall-cmd --zone= public --remove-port=80/tcp --permanent ### 2.11. iptables > iptables 命令是 Linux 上常用的防火墙软件,是 netfilter 项目的一部分。可以直接配置,也可以通过许多前端和图形界面配置。 > > 参考:http://man.linuxde.net/iptables 示例: ```bash # 开放指定的端口 iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT #允许本地回环接口(即运行本机访问本机) iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #允许已建立的或相关连的通行 iptables -A OUTPUT -j ACCEPT #允许所有本机向外的访问 iptables -A INPUT -p tcp --dport 22 -j ACCEPT #允许访问22端口 iptables -A INPUT -p tcp --dport 80 -j ACCEPT #允许访问80端口 iptables -A INPUT -p tcp --dport 21 -j ACCEPT #允许ftp服务的21端口 iptables -A INPUT -p tcp --dport 20 -j ACCEPT #允许FTP服务的20端口 iptables -A INPUT -j reject #禁止其他未允许的规则访问 iptables -A FORWARD -j REJECT #禁止其他未允许的规则访问 # 屏蔽IP iptables -I INPUT -s 123.45.6.7 -j DROP #屏蔽单个IP的命令 iptables -I INPUT -s 123.0.0.0/8 -j DROP #封整个段即从123.0.0.1到123.255.255.254的命令 iptables -I INPUT -s 124.45.0.0/16 -j DROP #封IP段即从123.45.0.1到123.45.255.254的命令 iptables -I INPUT -s 123.45.6.0/24 -j DROP #封IP段即从123.45.6.1到123.45.6.254的命令是 # 查看已添加的iptables规则 iptables -L -n -v Chain INPUT (policy DROP 48106 packets, 2690K bytes) pkts bytes target prot opt in out source destination 5075 589K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 191K 90M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 1499K 133M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 4364K 6351M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 6256 327K ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 3382K packets, 1819M bytes) pkts bytes target prot opt in out source destination 5075 589K ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 ``` ### 2.12. host > host 命令是常用的分析域名查询工具,可以用来测试域名系统工作是否正常。 > > 参考:http://man.linuxde.net/host 示例: ```bash [root@localhost ~]# host www.jsdig.com www.jsdig.com is an alias for host.1.jsdig.com. host.1.jsdig.com has address 100.42.212.8 [root@localhost ~]# host -a www.jsdig.com Trying "www.jsdig.com" ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34671 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;www.jsdig.com. IN ANY ;; ANSWER SECTION: www.jsdig.com. 463 IN CNAME host.1.jsdig.com. Received 54 bytes from 202.96.104.15#53 in 0 ms ``` ### 2.13. nslookup > nslookup 命令是常用域名查询工具,就是查 DNS 信息用的命令。 > > 参考:http://man.linuxde.net/nslookup 示例: ```bash [root@localhost ~]# nslookup www.jsdig.com Server: 202.96.104.15 Address: 202.96.104.15#53 Non-authoritative answer: www.jsdig.com canonical name = host.1.jsdig.com. Name: host.1.jsdig.com Address: 100.42.212.8 ``` ### 2.14. nc/netcat > nc 命令是 netcat 命令的简称,都是用来设置路由器。 > > 参考:http://man.linuxde.net/nc_netcat 示例: ```bash # TCP 端口扫描 [root@localhost ~]# nc -v -z -w2 192.168.0.3 1-100 192.168.0.3: inverse host lookup failed: Unknown host (UNKNOWN) [192.168.0.3] 80 (http) open (UNKNOWN) [192.168.0.3] 23 (telnet) open (UNKNOWN) [192.168.0.3] 22 (ssh) open # UDP 端口扫描 [root@localhost ~]# nc -u -z -w2 192.168.0.1 1-1000 # 扫描192.168.0.3 的端口 范围是 1-1000 ``` ### 2.15. ping > ping 命令用来测试主机之间网络的连通性。执行 ping 指令会使用 ICMP 传输协议,发出要求回应的信息,若远端主机的网络功能没有问题,就会回应该信息,因而得知该主机运作正常。 > > 参考:http://man.linuxde.net/ping 示例: ```bash [root@AY1307311912260196fcZ ~]# ping www.jsdig.com PING host.1.jsdig.com (100.42.212.8) 56(84) bytes of data. 64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=1 ttl=50 time=177 ms 64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=2 ttl=50 time=178 ms 64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=3 ttl=50 time=174 ms 64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=4 ttl=50 time=177 ms ...按Ctrl+C结束 --- host.1.jsdig.com ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 2998ms rtt min/avg/max/mdev = 174.068/176.916/178.182/1.683 ms ``` ### 2.16. traceroute > traceroute 命令用于追踪数据包在网络上的传输时的全部路径,它默认发送的数据包大小是 40 字节。 > > 参考:http://man.linuxde.net/traceroute 示例: ```bash traceroute www.58.com traceroute to www.58.com (211.151.111.30), 30 hops max, 40 byte packets 1 unknown (192.168.2.1) 3.453 ms 3.801 ms 3.937 ms 2 221.6.45.33 (221.6.45.33) 7.768 ms 7.816 ms 7.840 ms 3 221.6.0.233 (221.6.0.233) 13.784 ms 13.827 ms 221.6.9.81 (221.6.9.81) 9.758 ms 4 221.6.2.169 (221.6.2.169) 11.777 ms 122.96.66.13 (122.96.66.13) 34.952 ms 221.6.2.53 (221.6.2.53) 41.372 ms 5 219.158.96.149 (219.158.96.149) 39.167 ms 39.210 ms 39.238 ms 6 123.126.0.194 (123.126.0.194) 37.270 ms 123.126.0.66 (123.126.0.66) 37.163 ms 37.441 ms 7 124.65.57.26 (124.65.57.26) 42.787 ms 42.799 ms 42.809 ms 8 61.148.146.210 (61.148.146.210) 30.176 ms 61.148.154.98 (61.148.154.98) 32.613 ms 32.675 ms 9 202.106.42.102 (202.106.42.102) 44.563 ms 44.600 ms 44.627 ms 10 210.77.139.150 (210.77.139.150) 53.302 ms 53.233 ms 53.032 ms 11 211.151.104.6 (211.151.104.6) 39.585 ms 39.502 ms 39.598 ms 12 211.151.111.30 (211.151.111.30) 35.161 ms 35.938 ms 36.005 ms ``` ### 2.17. netstat > netstat 命令用来打印 Linux 中网络系统的状态信息,可让你得知整个 Linux 系统的网络情况。 > > 参考:http://man.linuxde.net/netstat 示例: ```bash # 列出所有端口 (包括监听和未监听的) netstat -a #列出所有端口 netstat -at #列出所有tcp端口 netstat -au #列出所有udp端口 # 列出所有处于监听状态的 Sockets netstat -l #只显示监听端口 netstat -lt #只列出所有监听 tcp 端口 netstat -lu #只列出所有监听 udp 端口 netstat -lx #只列出所有监听 UNIX 端口 # 显示每个协议的统计信息 netstat -s 显示所有端口的统计信息 netstat -st 显示TCP端口的统计信息 netstat -su 显示UDP端口的统计信息 ``` ================================================ FILE: docs/linux/cli/linux-cli-software.md ================================================ # Linux 软件管理 > 关键词:`rpm`, `yum`, `apt-get` ## 1. rpm > rpm 命令是 RPM 软件包的管理工具。rpm 原本是 Red Hat Linux 发行版专门用来管理 Linux 各项套件的程序,由于它遵循 GPL 规则且功能强大方便,因而广受欢迎。逐渐受到其他发行版的采用。RPM 套件管理方式的出现,让 Linux 易于安装,升级,间接提升了 Linux 的适用度。 > > 参考:http://man.linuxde.net/rpm 示例: (1)安装 rpm 包 ``` rpm -ivh xxx.rpm ``` (2)安装.src.rpm 软件包 这类软件包是包含了源代码的 rpm 包,在安装时需要进行编译 ```bash rpm -i xxx.src.rpm cd /usr/src/redhat/SPECS rpmbuild -bp xxx.specs #一个和你的软件包同名的specs文件 cd /usr/src/redhat/BUILD/xxx/ #一个和你的软件包同名的目录 ./configure #这一步和编译普通的源码软件一样,可以加上参数 make make install ``` (3)卸载 rpm 软件包 使用命令 `rpm -e 包名`,包名可以包含版本号等信息,但是不可以有后缀.rpm,比如卸载软件包 proftpd-1.2.8-1,可以使用下列格式: ```bash rpm -e proftpd-1.2.8-1 rpm -e proftpd-1.2.8 rpm -e proftpd- rpm -e proftpd ``` 不可以是下列格式: ```bash rpm -e proftpd-1.2.8-1.i386.rpm rpm -e proftpd-1.2.8-1.i386 rpm -e proftpd-1.2 rpm -e proftpd-1 ``` 有时会出现一些错误或者警告: ``` ... is needed by ... ``` 这说明这个软件被其他软件需要,不能随便卸载,可以用 rpm -e --nodeps 强制卸载 (4)查看与 rpm 包相关的文件和其他信息 ```bash rpm -qa # 列出所有安装过的包 ``` ## 2. yum > yum 命令是在 Fedora 和 RedHat 以及 SUSE 中基于 rpm 的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理 RPM 软件包,能够从指定的服务器自动下载 RPM 包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。 > > 参考:http://man.linuxde.net/yum 示例: 部分常用的命令包括: - 自动搜索最快镜像插件:`yum install yum-fastestmirror` - 安装 yum 图形窗口插件:`yum install yumex` - 查看可能批量安装的列表:`yum grouplist` **安装** ``` yum install #全部安装 yum install package1 #安装指定的安装包package1 yum groupinsall group1 #安装程序组group1 ``` **更新和升级** ``` yum update #全部更新 yum update package1 #更新指定程序包package1 yum check-update #检查可更新的程序 yum upgrade package1 #升级指定程序包package1 yum groupupdate group1 #升级程序组group1 ``` **查找和显示** ``` yum info package1 #显示安装包信息package1 yum list #显示所有已经安装和可以安装的程序包 yum list package1 #显示指定程序包安装情况package1 yum groupinfo group1 #显示程序组group1信息yum search string 根据关键字string查找安装包 yum search #查找软件包 ``` **删除程序** ``` yum remove #删除程序包package_name yum groupremove group1 #删除程序组group1 yum deplist package1 #查看程序package1依赖情况 ``` **清除缓存** ``` yum clean packages #清除缓存目录下的软件包 yum clean headers #清除缓存目录下的 headers yum clean oldheaders #清除缓存目录下旧的 headers ``` ### 2.1. yum 源 yum 的默认源是国外的,下载速度比较慢,所以最好替换为一个国内的 yum 源。 | 推荐 yum 国内源 | 源地址 | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | | Centos6:http://mirrors.aliyun.com/repo/Centos-6.repo
Centos7:http://mirrors.aliyun.com/repo/Centos-7.repo | | | Centos6:http://mirrors.163.com/.help/CentOS6-Base-163.repo
Centos7:http://mirrors.163.com/.help/CentOS7-Base-163.repo | > 🔔 注意:Cento5 已废弃,只能使用 http://vault.centos.org/ 替换,但由于是国外镜像,速度较慢。 替换方法,以 aliyun CentOS7 为例: ``` cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo yum clean all yum makecache ``` ## 3. apt-get > apt-get 命令是 Debian Linux 发行版中的 APT 软件包管理工具。所有基于 Debian 的发行都使用这个包管理系统。deb 包可以把一个应用的文件包在一起,大体就如同 Windows 上的安装文件。 > > 参考:http://man.linuxde.net/apt-get 示例: 使用 apt-get 命令的第一步就是引入必需的软件库,Debian 的软件库也就是所有 Debian 软件包的集合,它们存在互联网上的一些公共站点上。把它们的地址加入,apt-get 就能搜索到我们想要的软件。/etc/apt/sources.list 是存放这些地址列表的配置文件,其格式如下: deb [web 或 ftp 地址][发行版名字] [main/contrib/non-free] 我们常用的 Ubuntu 就是一个基于 Debian 的发行,我们使用 apt-get 命令获取这个列表,以下是我整理的常用命令: 在修改 /etc/apt/sources.list 或者 /etc/apt/preferences 之后运行该命令。 ```bash # 更新 apt-get apt-get update # 安装一个软件包 apt-get install packagename # 卸载一个已安装的软件包(保留配置文件) apt-get remove packagename # 卸载一个已安装的软件包(删除配置文件) apt-get –purge remove packagename # 如果需要空间的话,可以让这个命令来删除你已经删掉的软件 apt-get autoclean apt # 把安装的软件的备份也删除,不过这样不会影响软件的使用的 apt-get clean # 更新所有已安装的软件包 apt-get upgrade # 将系统升级到新版本 apt-get dist-upgrade ``` ## 4. 参考资料 - http://man.linuxde.net/rpm - http://man.linuxde.net/yum - http://man.linuxde.net/apt-get - http://www.runoob.com/linux/linux-yum.html ================================================ FILE: docs/linux/cli/linux-cli-system.md ================================================ # Linux 系统管理 > 关键词:`lsb_release`, `reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab` ## 1. Linux 系统管理要点 - 查看 Linux 系统发行版本 - 使用 [lsb_release](#lsb_release)(此命令适用于所有的 Linux 发行版本) - 使用 `cat /etc/redhat-release`(此方法只适合 Redhat 系的 Linux) - 查看 CPU 信息 - 使用 `cat /proc/cpuinfo` - 重新启动 Linux 操作系统 - 使用 [reboot](#reboot) - 退出 shell,并返回给定值 - 使用 [exit](#exit) - 关闭系统 - 使用 [shutdown](#shutdown) - 查看或设置系统时间与日期 - 使用 [date](#date) - 挂载文件系统 - 使用 [mount](#mount) - 取消挂载文件系统 - 使用 [umount](#umount) - 查看系统当前进程状态 - 使用 [ps](#ps) - 删除当前正在运行的进程 - 使用 [kill](#kill) - 启动、停止、重启、关闭、显示系统服务(Centos7),使用 [systemctl](#systemctl) - 启动、停止、重启、关闭、显示系统服务(Centos7 以前),使用 [service](#service) - 管理需要周期性执行的任务,使用 [crontab](#crontab) ## 2. 命令常见用法 ### 2.1. lsb_release lsb_release 不是 bash 默认命令,如果要使用,需要先安装。 安装方法: 1. 执行 `yum provides lsb_release`,查看支持 lsb_release 命令的包。 2. 选择合适版本,执行类似这样的安装命令:`yum install -y redhat-lsb-core-4.1-27.el7.centos.1.x86_64` ### 2.2. reboot > reboot 命令用来重新启动正在运行的 Linux 操作系统。 > > 参考:http://man.linuxde.net/reboot 示例: ```bash reboot # 重开机。 reboot -w # 做个重开机的模拟(只有纪录并不会真的重开机)。 ``` ### 2.3. exit > exit 命令同于退出 shell,并返回给定值。在 shell 脚本中可以终止当前脚本执行。执行 exit 可使 shell 以指定的状态值退出。若不设置状态值参数,则 shell 以预设值退出。状态值 0 代表执行成功,其他值代表执行失败。 > > 参考:http://man.linuxde.net/exit 示例: ```bash # 退出当前 shell [root@localhost ~]# exit logout # 在脚本中,进入脚本所在目录,否则退出 cd $(dirname $0) || exit 1 # 在脚本中,判断参数数量,不匹配就打印使用方式,退出 if [ "$#" -ne "2" ]; then echo "usage: $0 " exit 2 fi # 在脚本中,退出时删除临时文件 trap "rm -f tmpfile; echo Bye." EXIT # 检查上一命令的退出码 ./mycommand.sh EXCODE=$? if [ "$EXCODE" == "0" ]; then echo "O.K" fi ``` ### 2.4. shutdown > shutdown 命令用来系统关机命令。shutdown 指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作。 > > 参考:http://man.linuxde.net/shutdown 示例: ```bash # 指定现在立即关机 shutdown -h now # 指定 5 分钟后关机,同时送出警告信息给登入用户 shutdown +5 "System will shutdown after 5 minutes" ``` ### 2.5. date > date 命令是显示或设置系统时间与日期。 > > 参考:http://man.linuxde.net/date 示例: ```bash # 格式化输出 date +"%Y-%m-%d" 2009-12-07 # 输出昨天日期 date -d "1 day ago" +"%Y-%m-%d" 2012-11-19 # 2 秒后输出 date -d "2 second" +"%Y-%m-%d %H:%M.%S" 2012-11-20 14:21.31 # 传说中的 1234567890 秒 date -d "1970-01-01 1234567890 seconds" +"%Y-%m-%d %H:%m:%S" 2009-02-13 23:02:30 # 普通转格式 date -d "2009-12-12" +"%Y/%m/%d %H:%M.%S" 2009/12/12 00:00.00 # apache 格式转换 date -d "Dec 5, 2009 12:00:37 AM" +"%Y-%m-%d %H:%M.%S" 2009-12-05 00:00.37 # 格式转换后时间游走 date -d "Dec 5, 2009 12:00:37 AM 2 year ago" +"%Y-%m-%d %H:%M.%S" 2007-12-05 00:00.37 # 加减操作 date +%Y%m%d # 显示前天年月日 date -d "+1 day" +%Y%m%d # 显示前一天的日期 date -d "-1 day" +%Y%m%d # 显示后一天的日期 date -d "-1 month" +%Y%m%d # 显示上一月的日期 date -d "+1 month" +%Y%m%d # 显示下一月的日期 date -d "-1 year" +%Y%m%d # 显示前一年的日期 date -d "+1 year" +%Y%m%d # 显示下一年的日期 # 设定时间 date -s # 设置当前时间,只有root权限才能设置,其他只能查看 date -s 20120523 # 设置成20120523,这样会把具体时间设置成空00:00:00 date -s 01:01:01 # 设置具体时间,不会对日期做更改 date -s "01:01:01 2012-05-23" # 这样可以设置全部时间 date -s "01:01:01 20120523" # 这样可以设置全部时间 date -s "2012-05-23 01:01:01" # 这样可以设置全部时间 date -s "20120523 01:01:01" # 这样可以设置全部时间 # 有时需要检查一组命令花费的时间 #!/bin/bash start=$(date +%s) nmap man.linuxde.net &> /dev/null end=$(date +%s) difference=$(( end - start )) echo $difference seconds. ``` ### 2.6. mount > mount 命令用于挂载文件系统到指定的挂载点。此命令的最常用于挂载 cdrom,使我们可以访问 cdrom 中的数据,因为你将光盘插入 cdrom 中,Linux 并不会自动挂载,必须使用 Linux mount 命令来手动完成挂载。 > > 参考:http://man.linuxde.net/mount > https://blog.csdn.net/weishujie000/article/details/76531924 示例: ```bash # 将 /dev/hda1 挂在 /mnt 之下 mount /dev/hda1 /mnt # 将 /dev/hda1 用唯读模式挂在 /mnt 之下 mount -o ro /dev/hda1 /mnt # 将 /tmp/image.iso 这个光碟的 image 档使用 loop 模式挂在 /mnt/cdrom 之下 # 用这种方法可以将一般网络上可以找到的 Linux ISO 在不烧录成光碟的情况下检视其内容 mount -o loop /tmp/image.iso /mnt/cdrom ``` ### 2.7. umount > umount 命令用于卸载已经挂载的文件系统。利用设备名或挂载点都能 umount 文件系统,不过最好还是通过挂载点卸载,以免使用绑定挂载(一个设备,多个挂载点)时产生混乱。 > > 参考:http://man.linuxde.net/umount 示例: ```bash # 通过设备名卸载 umount -v /dev/sda1 /dev/sda1 umounted # 通过挂载点卸载 umount -v /mnt/mymount/ /tmp/diskboot.img umounted ``` ### 2.8. ps > ps 命令用于报告当前系统的进程状态。可以搭配 kill 指令随时中断、删除不必要的程序。ps 命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等,总之大部分信息都是可以通过执行该命令得到的。 > > 参考:http://man.linuxde.net/ps 示例: ```bash # 按内存资源的使用量对进程进行排序 ps aux | sort -rnk 4 # 按 CPU 资源的使用量对进程进行排序 ps aux | sort -nk 3 ``` ### 2.9. kill > kill 命令用来删除执行中的程序或工作。kill 可将指定的信息送至程序。预设的信息为 SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9) 信息尝试强制删除程序。程序或工作的编号可利用 ps 指令或 job 指令查看。 > > 参考:http://man.linuxde.net/kill 示例: ```bash # 列出所有信号名称 kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX # 先用 ps 查找进程,然后用 kill 杀掉 ps -ef | grep vim root 3268 2884 0 16:21 pts/1 00:00:00 vim install.log root 3370 2822 0 16:21 pts/0 00:00:00 grep vim kill 3268 kill 3268 -bash: kill: (3268) - 没有那个进程 ``` ### 2.10. systemctl > systemctl 命令是系统服务管理器指令,它实际上将 service 和 chkconfig 这两个命令组合到一起。 > > 参考:http://man.linuxde.net/systemctl 示例: ```bash # 1.启动 nfs 服务 systemctl start nfs-server.service # 2.设置开机自启动 systemctl enable nfs-server.service # 3.停止开机自启动 systemctl disable nfs-server.service # 4.查看服务当前状态 systemctl status nfs-server.service # 5.重新启动某服务 systemctl restart nfs-server.service # 6.查看所有已启动的服务 systemctl list-units --type=service # 7. 开启防火墙 22 端口 iptables -I INPUT -p tcp --dport 22 -j accept # 8. 彻底关闭防火墙 sudo systemctl status firewalld.service sudo systemctl stop firewalld.service sudo systemctl disable firewalld.service ``` ### 2.11. service > service 命令是 Redhat Linux 兼容的发行版中用来控制系统服务的实用工具,它以启动、停止、重新启动和关闭系统服务,还可以显示所有系统服务的当前状态。 > > 参考:http://man.linuxde.net/service 示例: ```bash service network status 配置设备: lo eth0 当前的活跃设备: lo eth0 service network restart 正在关闭接口 eth0: [ 确定 ] 关闭环回接口: [ 确定 ] 设置网络参数: [ 确定 ] 弹出环回接口: [ 确定 ] 弹出界面 eth0: [ 确定 ] ``` ### 2.12. crontab > crontab 命令被用来提交和管理用户的需要周期性执行的任务,与 windows 下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动 crond 进程,crond 进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。 > > 参考:http://man.linuxde.net/crontab 示例: ```bash # 每 1 分钟执行一次 command * * * * * command # 每小时的第 3 和第 15 分钟执行 3,15 * * * * command # 在上午 8 点到 11 点的第 3 和第 15 分钟执行 3,15 8-11 * * * command # 每隔两天的上午 8 点到 11 点的第 3 和第 15 分钟执行 3,15 8-11 */2 * * command # 每个星期一的上午 8 点到 11 点的第 3 和第 15 分钟执行 3,15 8-11 * * 1 command # 每晚的 21:30 重启 smb 30 21 * * * /etc/init.d/smb restart # 每月 1、10、22 日的 4 : 45 重启 smb 45 4 1,10,22 * * /etc/init.d/smb restart # 每周六、周日的 1:10 重启 smb 10 1 * * 6,0 /etc/init.d/smb restart # 每天 18 : 00 至 23 : 00 之间每隔 30 分钟重启 smb 0,30 18-23 * * * /etc/init.d/smb restart # 每星期六的晚上 11:00 pm 重启 smb 0 23 * * 6 /etc/init.d/smb restart # 每一小时重启 smb * */1 * * * /etc/init.d/smb restart # 晚上 11 点到早上 7 点之间,每隔一小时重启 smb * 23-7/1 * * * /etc/init.d/smb restart # 每月的 4 号与每周一到周三的 11 点重启 smb 0 11 4 * mon-wed /etc/init.d/smb restart # 一月一号的 4 点重启 smb 0 4 1 jan * /etc/init.d/smb restart # 每小时执行`/etc/cron.hourly`目录内的脚本 01 * * * * root run-parts /etc/cron.hourly ``` ================================================ FILE: docs/linux/cli/linux-cli-user.md ================================================ # Linux 用户管理 > 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo` ## 1. Linux 用户管理要点 - 创建用户组 - 使用 [groupadd](#groupadd) - 删除用户组 - 使用 [groupdel](#groupdel) - 修改用户组信息 - 使用 [groupmod](#groupmod) - 创建用户 - 使用 [useradd](#useradd) - 删除用户 - 使用 [userdel](#userdel) - 修改用户信息 - 使用 [usermod](#usermod) - 设置用户认证信息 - 使用 [passwd](#passwd) - 切换用户 - 使用 [su](#su) - 当前用户想执行没有权限执行的命令时,使用其他用户身份去执行 - 使用 [sudo](#sudo) ## 2. 命令常见用法 ### 2.1. groupadd > groupadd 命令用于创建一个新的用户组,新用户组的信息将被添加到系统文件中。 > > 参考:http://man.linuxde.net/groupadd 示例: ```bash # 建立一个新组,并设置组 ID 加入系统 $ groupadd -g 344 jsdigname ``` ### 2.2. groupdel > groupdel 命令用于删除指定的用户组,本命令要修改的系统文件包括 `/ect/group` 和 `/ect/gshadow`。若该群组中仍包括某些用户,则必须先删除这些用户后,方能删除群组。 > > 参考:http://man.linuxde.net/groupdel 示例: ```bash $ groupadd damon # 创建damon用户组 $ groupdel damon # 删除这个用户组 ``` ### 2.3. groupmod > groupmod 命令更改群组识别码或名称。需要更改群组的识别码或名称时,可用 groupmod 指令来完成这项工作。 > > 参考:http://man.linuxde.net/groupmod ### 2.4. useradd > useradd 命令用于 Linux 中创建的新的系统用户。useradd 可用来建立用户帐号。帐号建好之后,再用 passwd 设定帐号的密码.而可用 userdel 删除帐号。使用 useradd 指令所建立的帐号,实际上是保存在 `/etc/passwd` 文本文件中。 > > 参考:http://man.linuxde.net/useradd 示例: ```bash # 新建用户加入组 $ useradd –g sales jack –G company,employees # -g:加入主要组、-G:加入次要组 # 建立一个新用户账户,并设置 ID $ useradd caojh -u 544 ``` ### 2.5. userdel > userdel 命令用于删除给定的用户,以及与用户相关的文件。若不加选项,则仅删除用户帐号,而不删除相关文件。 > > 参考:http://man.linuxde.net/userdel 示例: userdel 命令很简单,比如我们现在有个用户 linuxde,其 home 目录位于`/var`目录中,现在我们来删除这个用户: ```bash $ userdel linuxde # 删除用户linuxde,但不删除其家目录及文件; $ userdel -r linuxde # 删除用户linuxde,其 home 目录及文件一并删除; ``` ### 2.6. usermod > usermod 命令用于修改用户的基本信息。usermod 命令不允许你改变正在线上的使用者帐号名称。当 usermod 命令用来改变 user id,必须确认这名 user 没在电脑上执行任何程序。你需手动更改使用者的 crontab 档。也需手动更改使用者的 at 工作档。采用 NIS server 须在 server 上更动相关的 NIS 设定。 > > 参考:http://man.linuxde.net/usermod 示例: ```bash # 将 newuser2 添加到组 staff 中 $ usermod -G staff newuser2 # 修改 newuser 的用户名为 newuser1 $ usermod -l newuser1 newuser # 锁定账号 newuser1 $ usermod -L newuser1 # 解除对 newuser1 的锁定 $ usermod -U newuser1 ``` ### 2.7. passwd > passwd 命令用于设置用户的认证信息,包括用户密码、密码过期时间等。系统管理者则能用它管理系统用户的密码。只有管理者可以指定用户名称,一般用户只能变更自己的密码。 > > 参考:http://man.linuxde.net/passwd 示例: ```bash # 如果是普通用户执行 passwd 只能修改自己的密码。 # 如果新建用户后,要为新用户创建密码,则用 passwd 用户名,注意要以 root 用户的权限来创建。 $ passwd linuxde # 更改或创建linuxde用户的密码; Changing password for user linuxde. New UNIX password: # 请输入新密码; Retype new UNIX password: # 再输入一次; passwd: all authentication tokens updated successfully. # 成功; # 普通用户如果想更改自己的密码,直接运行 passwd 即可,比如当前操作的用户是 linuxde。 $ passwd Changing password for user linuxde. # 更改linuxde用户的密码; (current) UNIX password: # 请输入当前密码; New UNIX password: # 请输入新密码; Retype new UNIX password: # 确认新密码; passwd: all authentication tokens updated successfully. # 更改成功; # 比如我们让某个用户不能修改密码,可以用`-l`选项来锁定: $ passwd -l linuxde # 锁定用户linuxde不能更改密码; Locking password for user linuxde. passwd: Success # 锁定成功; $ su linuxde # 通过su切换到linuxde用户; $ passwd # linuxde来更改密码; Changing password for user linuxde. Changing password for linuxde (current) UNIX password: # 输入linuxde的当前密码; passwd: Authentication token manipulation error # 失败,不能更改密码; $ passwd -d linuxde # 清除linuxde用户密码; Removing password for user linuxde. passwd: Success # 清除成功; $ passwd -S linuxde # 查询linuxde用户密码状态; Empty password. # 空密码,也就是没有密码; ``` ### 2.8. su > su 命令用于切换当前用户身份到其他用户身份,变更时须输入所要变更的用户帐号与密码。 > > 参考:http://man.linuxde.net/su 示例: ```bash # 变更帐号为 root 并在执行 ls 指令后退出变回原使用者: $ su -c ls root # 变更帐号为 root 并传入`-f`选项给新执行的 shell: $ su root -f # 变更帐号为 test 并改变工作目录至 test 的家目录: $ su -test ``` ### 2.9. sudo > sudo 命令用来以其他身份来执行命令,预设的身份为 root。在 `/etc/sudoers` 中设置了可执行 sudo 指令的用户。若其未经授权的用户企图使用 sudo,则会发出警告的邮件给管理员。用户使用 sudo 时,必须先输入密码,之后有 5 分钟的有效期限,超过期限则必须重新输入密码。 > > 参考:http://man.linuxde.net/sudo 示例: ```bash # 指定用户执行命令 $ sudo -u userb ls -l # 列出目前的权限 $ sudo -l # 显示sudo设置 $ sudo -L ``` #### 2.9.1. 给普通用户授权 sudo 假设要给普通用户 mary 配置 sudo 权限: 1. `/etc/sudoers` 文件存放了 sudo 的相关用户,但是默认是没有写权限的,所以需要设为可写:`chmod u+w /etc/sudoers` 2. 在该文件中添加 `mary ALL=(ALL) ALL` ,保存并退出,让 mary 具有 sudo 的所有权限 3. 再将 `/etc/sudoers` 的权限恢复到默认状态:`chmod u-w /etc/sudoers` #### 2.9.2. 免密码授权 sudo 与给普通用户授权 sudo 类似,区别仅在于第 2 步:`mary ALL=(ALL) NOPASSWD: ALL`。 ================================================ FILE: docs/linux/cli/scp.md ================================================ # scp 加密的方式在本地主机和远程主机之间复制文件 ## 补充说明 **scp 命令** 用于在 Linux 下进行远程拷贝文件的命令,和它类似的命令有 cp,不过 cp 只是在本机进行拷贝不能跨服务器,而且 scp 传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system 时,用 scp 可以帮你把文件移出来。另外,scp 还非常不占资源,不会提高多少系统负荷,在这一点上,rsync 就远远不及它了。虽然  rsync 比 scp 会快一点,但当小文件众多的情况下,rsync 会导致硬盘 I/O 非常高,而 scp 基本不影响系统正常使用。 ### 语法 ```shell scp(选项)(参数) ``` ### 选项 ```shell -1:使用ssh协议版本1; -2:使用ssh协议版本2; -4:使用ipv4; -6:使用ipv6; -B:以批处理模式运行; -C:使用压缩; -F:指定ssh配置文件; -i:identity_file 从指定文件中读取传输时使用的密钥文件(例如亚马逊云pem),此参数直接传递给ssh; -l:指定宽带限制; -o:指定使用的ssh选项; -P:指定远程主机的端口号; -p:保留文件的最后修改时间,最后访问时间和权限模式; -q:不显示复制进度; -r:以递归方式复制。 ``` ### 参数 - 源文件:指定要复制的源文件。 - 目标文件:目标文件。格式为`user@host:filename`(文件名为目标文件的名称)。 ### 实例 从远程复制到本地的 scp 命令与上面的命令雷同,只要将从本地复制到远程的命令后面 2 个参数互换顺序就行了。 **从远处复制文件到本地目录** ```shell scp root@10.10.10.10:/opt/soft/nginx-0.5.38.tar.gz /opt/soft/ ``` 从 10.10.10.10 机器上的`/opt/soft/`的目录中下载 nginx-0.5.38.tar.gz  文件到本地`/opt/soft/`目录中。 **从亚马逊云复制 OpenVPN 到本地目录** ```shell scp -i amazon.pem ubuntu@10.10.10.10:/usr/local/openvpn_as/etc/exe/openvpn-connect-2.1.3.110.dmg openvpn-connect-2.1.3.110.dmg ``` 从 10.10.10.10 机器上下载 openvpn 安装文件到本地当前目录来。 **从远处复制到本地** ```shell scp -r root@10.10.10.10:/opt/soft/mongodb /opt/soft/ ``` 从 10.10.10.10 机器上的`/opt/soft/`中下载 mongodb 目录到本地的`/opt/soft/`目录来。 **上传本地文件到远程机器指定目录** ```shell scp /opt/soft/nginx-0.5.38.tar.gz root@10.10.10.10:/opt/soft/scptest # 指定端口 2222 scp -rp -P 2222 /opt/soft/nginx-0.5.38.tar.gz root@10.10.10.10:/opt/soft/scptest ``` 复制本地`/opt/soft/`目录下的文件 nginx-0.5.38.tar.gz 到远程机器 10.10.10.10 的`opt/soft/scptest`目录。 **上传本地目录到远程机器指定目录** ```shell scp -r /opt/soft/mongodb root@10.10.10.10:/opt/soft/scptest ``` 上传本地目录`/opt/soft/mongodb`到远程机器 10.10.10.10 上`/opt/soft/scptest`的目录中去。 ================================================ FILE: docs/linux/cli/top.md ================================================ # top 显示或管理执行中的程序 ## 补充说明 **top 命令** 可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过 top 命令所提供的互动式界面,用热键可以管理。 ### 语法 ```shell top(选项) ``` ### 选项 ```shell -b:以批处理模式操作; -c:显示完整的治命令; -d:屏幕刷新间隔时间; -I:忽略失效过程; -s:保密模式; -S:累积模式; -i<时间>:设置间隔时间; -u<用户名>:指定用户名; -p<进程号>:指定进程; -n<次数>:循环显示的次数。 ``` ### top 交互命令 在 top 命令执行过程中可以使用的一些交互命令。这些命令都是单字母的,如果在命令行中使用了-s 选项,  其中一些命令可能会被屏蔽。 ```shell h:显示帮助画面,给出一些简短的命令总结说明; k:终止一个进程; i:忽略闲置和僵死进程,这是一个开关式命令; q:退出程序; r:重新安排一个进程的优先级别; S:切换到累计模式; s:改变两次刷新之间的延迟时间(单位为s),如果有小数,就换算成ms。输入0值则系统将不断刷新,默认值是5s; f或者F:从当前显示中添加或者删除项目; o或者O:改变显示项目的顺序; l:切换显示平均负载和启动时间信息; m:切换显示内存信息; t:切换显示进程和CPU状态信息; c:切换显示命令名称和完整命令行; M:根据驻留内存大小进行排序; P:根据CPU使用百分比大小进行排序; T:根据时间/累计时间进行排序; w:将当前设置写入~/.toprc文件中。 ``` ### 实例 ```shell top - 09:44:56 up 16 days, 21:23, 1 user, load average: 9.59, 4.75, 1.92 Tasks: 145 total, 2 running, 143 sleeping, 0 stopped, 0 zombie Cpu(s): 99.8%us, 0.1%sy, 0.0%ni, 0.2%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 4147888k total, 2493092k used, 1654796k free, 158188k buffers Swap: 5144568k total, 56k used, 5144512k free, 2013180k cached ``` **解释:** - top - 09:44:56[当前系统时间], - 16 days[系统已经运行了 16 天], - 1 user[个用户当前登录], - load average: 9.59, 4.75, 1.92[系统负载,即任务队列的平均长度] - Tasks: 145 total[总进程数], - 2 running[正在运行的进程数], - 143 sleeping[睡眠的进程数], - 0 stopped[停止的进程数], - 0 zombie[冻结进程数], - Cpu(s): 99.8%us[用户空间占用 CPU 百分比], - 0.1%sy[内核空间占用 CPU 百分比], - 0.0%ni[用户进程空间内改变过优先级的进程占用 CPU 百分比], - 0.2%id[空闲 CPU 百分比], 0.0%wa[等待输入输出的 CPU 时间百分比], - 0.0%hi[], - 0.0%st[], - Mem: 4147888k total[物理内存总量], - 2493092k used[使用的物理内存总量], - 1654796k free[空闲内存总量], - 158188k buffers[用作内核缓存的内存量] - Swap:  5144568k total[交换区总量], - 56k used[使用的交换区总量], - 5144512k free[空闲交换区总量], - 2013180k cached[缓冲的交换区总量], ================================================ FILE: docs/linux/cli/vmstat.md ================================================ # vmstat 显示虚拟内存状态 ## 补充说明 **vmstat 命令** 的含义为显示虚拟内存状态(“Viryual Memor Statics”),但是它可以报告关于进程、内存、I/O 等系统整体运行状态。 ### 语法 ```shell vmstat(选项)(参数) ``` ### 选项 ```shell -a:显示活动内页; -f:显示启动后创建的进程总数; -m:显示slab信息; -n:头信息仅显示一次; -s:以表格方式显示事件计数器和内存状态; -d:报告磁盘状态; -p:显示指定的硬盘分区状态; -S:输出信息的单位。 ``` ### 参数 - 事件间隔:状态信息刷新的时间间隔; - 次数:显示报告的次数。 ### 实例 ```shell vmstat 3 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 320 42188 167332 1534368 0 0 4 7 1 0 0 0 99 0 0 0 0 320 42188 167332 1534392 0 0 0 0 1002 39 0 0 100 0 0 0 0 320 42188 167336 1534392 0 0 0 19 1002 44 0 0 100 0 0 0 0 320 42188 167336 1534392 0 0 0 0 1002 41 0 0 100 0 0 0 0 320 42188 167336 1534392 0 0 0 0 1002 41 0 0 100 0 0 ``` **字段说明:** Procs(进程) - r: 运行队列中进程数量,这个值也可以判断是否需要增加 CPU。(长期大于 1) - b: 等待 IO 的进程数量。 Memory(内存) - swpd: 使用虚拟内存大小,如果 swpd 的值不为 0,但是 SI,SO 的值长期为 0,这种情况不会影响系统性能。 - free: 空闲物理内存大小。 - buff: 用作缓冲的内存大小。 - cache: 用作缓存的内存大小,如果 cache 的值大的时候,说明 cache 处的文件数多,如果频繁访问到的文件都能被 cache 处,那么磁盘的读 IO bi 会非常小。 Swap - si: 每秒从交换区写到内存的大小,由磁盘调入内存。 - so: 每秒写入交换区的内存大小,由内存调入磁盘。 注意:内存够用的时候,这 2 个值都是 0,如果这 2 个值长期大于 0 时,系统性能会受到影响,磁盘 IO 和 CPU 资源都会被消耗。有些朋友看到空闲内存(free)很少的或接近于 0 时,就认为内存不够用了,不能光看这一点,还要结合 si 和 so,如果 free 很少,但是 si 和 so 也很少(大多时候是 0),那么不用担心,系统性能这时不会受到影响的。 IO(现在的 Linux 版本块的大小为 1kb) - bi: 每秒读取的块数 - bo: 每秒写入的块数 注意:随机磁盘读写的时候,这 2 个值越大(如超出 1024k),能看到 CPU 在 IO 等待的值也会越大。 system(系统) - in: 每秒中断数,包括时钟中断。 - cs: 每秒上下文切换数。 注意:上面 2 个值越大,会看到由内核消耗的 CPU 时间会越大。 CPU(以百分比表示) - us: 用户进程执行时间百分比(user time) us 的值比较高时,说明用户进程消耗的 CPU 时间多,但是如果长期超 50%的使用,那么我们就该考虑优化程序算法或者进行加速。 - sy: 内核系统进程执行时间百分比(system time) sy 的值高时,说明系统内核消耗的 CPU 资源多,这并不是良性表现,我们应该检查原因。 - wa: IO 等待时间百分比 wa 的值高时,说明 IO 等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。 - id: 空闲时间百分比 ================================================ FILE: docs/linux/cli/命令行的艺术.md ================================================ > 转载自 https://github.com/jlevy/the-art-of-command-line _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](README-el.md) ∙ [English](../README.md) ∙ [Español](README-es.md) ∙ [Français](README-fr.md) ∙ [Indonesia](README-id.md) ∙ [Italiano](README-it.md) ∙ [日本語](README-ja.md) ∙ [한국어](README-ko.md) ∙ [Português](README-pt.md) ∙ [Română](README-ro.md) ∙ [Русский](README-ru.md) ∙ [Slovenščina](README-sl.md) ∙ [Українська](README-uk.md) ∙ [简体中文](README-zh.md) ∙ [繁體中文](README-zh-Hant.md)_ # 命令行的艺术 ![img](https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - [前言](#前言) - [基础](#基础) - [日常使用](#日常使用) - [文件及数据处理](#文件及数据处理) - [系统调试](#系统调试) - [单行脚本](#单行脚本) - [冷门但有用](#冷门但有用) - [仅限 OS X 系统](#仅限-os-x-系统) - [仅限 Windows 系统](#仅限-windows-系统) - [更多资源](#更多资源) - [免责声明](#免责声明) ![img](https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/cowsay.png) 熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际上,它会提高你作为工程师的灵活性以及生产力。本文是一份我在 Linux 上工作时,发现的一些命令行使用技巧的摘要。有些技巧非常基础,而另一些则相当复杂,甚至晦涩难懂。这篇文章并不长,但当你能够熟练掌握这里列出的所有技巧时,你就学会了很多关于命令行的东西了。 这篇文章是[许多作者和译者](AUTHORS.md)共同的成果。 这里的部分内容 [首次](http://www.quora.com/What-are-some-lesser-known-but-useful-Unix-commands) [出现](http://www.quora.com/What-are-the-most-useful-Swiss-army-knife-one-liners-on-Unix) 于 [Quora](http://www.quora.com/What-are-some-time-saving-tips-that-every-Linux-user-should-know), 但已经迁移到了 Github,并由众多高手做出了许多改进。 如果你在本文中发现了错误或者存在可以改善的地方,请[**贡献你的一份力量**](/CONTRIBUTING.md)。 ## 前言 涵盖范围: - 这篇文章不仅能帮助刚接触命令行的新手,而且对具有经验的人也大有裨益。本文致力于做到*覆盖面广*(涉及所有重要的内容),_具体_(给出具体的最常用的例子),以及*简洁*(避免冗余的内容,或是可以在其他地方轻松查到的细枝末节)。在特定应用场景下,本文的内容属于基本功或者能帮助您节约大量的时间。 - 本文主要为 Linux 所写,但在[仅限 OS X 系统](#仅限-os-x-系统)章节和[仅限 Windows 系统](#仅限-windows-系统)章节中也包含有对应操作系统的内容。除去这两个章节外,其它的内容大部分均可在其他类 Unix 系统或 OS X,甚至 Cygwin 中得到应用。 - 本文主要关注于交互式 Bash,但也有很多技巧可以应用于其他 shell 和 Bash 脚本当中。 - 除去“标准的”Unix 命令,本文还包括了一些依赖于特定软件包的命令(前提是它们具有足够的价值)。 注意事项: - 为了能在一页内展示尽量多的东西,一些具体的信息可以在引用的页面中找到。我们相信机智的你知道如何使用 Google 或者其他搜索引擎来查阅到更多的详细信息。文中部分命令需要您使用 `apt-get`,`yum`,`dnf`,`pacman`, `pip` 或 `brew`(以及其它合适的包管理器)来安装依赖的程序。 - 遇到问题的话,请尝试使用 [Explainshell](http://explainshell.com/) 去获取相关命令、参数、管道等内容的解释。 ## 基础 - 学习 Bash 的基础知识。具体地,在命令行中输入 `man bash` 并至少全文浏览一遍; 它理解起来很简单并且不冗长。其他的 shell 可能很好用,但 Bash 的功能已经足够强大并且到几乎总是可用的( 如果你*只*学习 zsh,fish 或其他的 shell 的话,在你自己的设备上会显得很方便,但过度依赖这些功能会给您带来不便,例如当你需要在服务器上工作时)。 - 熟悉至少一个基于文本的编辑器。通常而言 Vim (`vi`) 会是你最好的选择,毕竟在终端中编辑文本时 Vim 是最好用的工具(甚至大部分情况下 Vim 要比 Emacs、大型 IDE 或是炫酷的编辑器更好用)。 - 学会如何使用 `man` 命令去阅读文档。学会使用 `apropos` 去查找文档。知道有些命令并不对应可执行文件,而是在 Bash 内置好的,此时可以使用 `help` 和 `help -d` 命令获取帮助信息。你可以用 `type 命令` 来判断这个命令到底是可执行文件、shell 内置命令还是别名。 - 学会使用 `>` 和 `<` 来重定向输出和输入,学会使用 `|` 来重定向管道。明白 `>` 会覆盖了输出文件而 `>>` 是在文件末添加。了解标准输出 stdout 和标准错误 stderr。 - 学会使用通配符 `*` (或许再算上 `?` 和 `[`...`]`) 和引用以及引用中 `'` 和 `"` 的区别(后文中有一些具体的例子)。 - 熟悉 Bash 中的任务管理工具:`&`,**ctrl-z**,**ctrl-c**,`jobs`,`fg`,`bg`,`kill` 等。 - 学会使用 `ssh` 进行远程命令行登录,最好知道如何使用 `ssh-agent`,`ssh-add` 等命令来实现基础的无密码认证登录。 - 学会基本的文件管理工具:`ls` 和 `ls -l` (了解 `ls -l` 中每一列代表的意义),`less`,`head`,`tail` 和 `tail -f` (甚至 `less +F`),`ln` 和 `ln -s` (了解硬链接与软链接的区别),`chown`,`chmod`,`du` (硬盘使用情况概述:`du -hs *`)。 关于文件系统的管理,学习 `df`,`mount`,`fdisk`,`mkfs`,`lsblk`。知道 inode 是什么(与 `ls -i` 和 `df -i` 等命令相关)。 - 学习基本的网络管理工具:`ip` 或 `ifconfig`,`dig`。 - 学习并使用一种版本控制管理系统,例如 `git`。 - 熟悉正则表达式,学会使用 `grep`/`egrep`,它们的参数中 `-i`,`-o`,`-v`,`-A`,`-B` 和 `-C` 这些是很常用并值得认真学习的。 - 学会使用 `apt-get`,`yum`,`dnf` 或 `pacman` (具体使用哪个取决于你使用的 Linux 发行版)来查找和安装软件包。并确保你的环境中有 `pip` 来安装基于 Python 的命令行工具 (接下来提到的部分程序使用 `pip` 来安装会很方便)。 ## 日常使用 - 在 Bash 中,可以通过按 **Tab** 键实现自动补全参数,使用 **ctrl-r** 搜索命令行历史记录(按下按键之后,输入关键字便可以搜索,重复按下 **ctrl-r** 会向后查找匹配项,按下 **Enter** 键会执行当前匹配的命令,而按下右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改)。 - 在 Bash 中,可以按下 **ctrl-w** 删除你键入的最后一个单词,**ctrl-u** 可以删除行内光标所在位置之前的内容,**alt-b** 和 **alt-f** 可以以单词为单位移动光标,**ctrl-a** 可以将光标移至行首,**ctrl-e** 可以将光标移至行尾,**ctrl-k** 可以删除光标至行尾的所有内容,**ctrl-l** 可以清屏。键入 `man readline` 可以查看 Bash 中的默认快捷键。内容有很多,例如 **alt-.** 循环地移向前一个参数,而 **alt-\*** 可以展开通配符。 * 你喜欢的话,可以执行 `set -o vi` 来使用 vi 风格的快捷键,而执行 `set -o emacs` 可以把它改回来。 * 为了便于编辑长命令,在设置你的默认编辑器后(例如 `export EDITOR=vim`),**ctrl-x** **ctrl-e** 会打开一个编辑器来编辑当前输入的命令。在 vi 风格下快捷键则是 **escape-v**。 * 键入 `history` 查看命令行历史记录,再用 `!n`(`n` 是命令编号)就可以再次执行。其中有许多缩写,最有用的大概就是 `!$`, 它用于指代上次键入的参数,而 `!!` 可以指代上次键入的命令了(参考 man 页面中的“HISTORY EXPANSION”)。不过这些功能,你也可以通过快捷键 **ctrl-r** 和 **alt-.** 来实现。 * `cd` 命令可以切换工作路径,输入 `cd \~` 可以进入 home 目录。要访问你的 home 目录中的文件,可以使用前缀 `\~`(例如 `\~/.bashrc`)。在 `sh` 脚本里则用环境变量 `$HOME` 指代 home 目录的路径。 * 回到前一个工作路径:`cd -`。 * 如果你输入命令的时候中途改了主意,按下 **alt-#** 在行首添加 `#` 把它当做注释再按下回车执行(或者依次按下 **ctrl-a**, **#**, **enter**)。这样做的话,之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令。 * 使用 `xargs` ( 或 `parallel`)。他们非常给力。注意到你可以控制每行参数个数(`-L`)和最大并行数(`-P`)。如果你不确定它们是否会按你想的那样工作,先使用 `xargs echo` 查看一下。此外,使用 `-I{}` 会很方便。例如: ```bash find . -name '*.py' | xargs grep some_function cat hosts | xargs -I{} ssh root@{} hostname ``` - `pstree -p` 以一种优雅的方式展示进程树。 - 使用 `pgrep` 和 `pkill` 根据名字查找进程或发送信号(`-f` 参数通常有用)。 - 了解你可以发往进程的信号的种类。比如,使用 `kill -STOP [pid]` 停止一个进程。使用 `man 7 signal` 查看详细列表。 - 使用 `nohup` 或 `disown` 使一个后台进程持续运行。 - 使用 `netstat -lntp` 或 `ss -plat` 检查哪些进程在监听端口(默认是检查 TCP 端口; 添加参数 `-u` 则检查 UDP 端口)或者 `lsof -iTCP -sTCP:LISTEN -P -n` (这也可以在 OS X 上运行)。 - `lsof` 来查看开启的套接字和文件。 - 使用 `uptime` 或 `w` 来查看系统已经运行多长时间。 - 使用 `alias` 来创建常用命令的快捷形式。例如:`alias ll='ls -latr'` 创建了一个新的命令别名 `ll`。 - 可以把别名、shell 选项和常用函数保存在 `\~/.bashrc`,具体看下这篇[文章](http://superuser.com/a/183980/7106)。这样做的话你就可以在所有 shell 会话中使用你的设定。 - 把环境变量的设定以及登陆时要执行的命令保存在 `\~/.bash_profile`。而对于从图形界面启动的 shell 和 `cron` 启动的 shell,则需要单独配置文件。 - 要想在几台电脑中同步你的配置文件(例如 `.bashrc` 和 `.bash_profile`),可以借助 Git。 - 当变量和文件名中包含空格的时候要格外小心。Bash 变量要用引号括起来,比如 `"$FOO"`。尽量使用 `-0` 或 `-print0` 选项以便用 NULL 来分隔文件名,例如 `locate -0 pattern | xargs -0 ls -al` 或 `find / -print0 -type d | xargs -0 ls -al`。如果 for 循环中循环访问的文件名含有空字符(空格、tab 等字符),只需用 `IFS=$'\n'` 把内部字段分隔符设为换行符。 - 在 Bash 脚本中,使用 `set -x` 去调试输出(或者使用它的变体 `set -v`,它会记录原始输入,包括多余的参数和注释)。尽可能地使用严格模式:使用 `set -e` 令脚本在发生错误时退出而不是继续运行;使用 `set -u` 来检查是否使用了未赋值的变量;试试 `set -o pipefail`,它可以监测管道中的错误。当牵扯到很多脚本时,使用 `trap` 来检测 ERR 和 EXIT。一个好的习惯是在脚本文件开头这样写,这会使它能够检测一些错误,并在错误发生时中断程序并输出信息: ```bash set -euo pipefail trap "echo 'error: Script failed: see failed command above'" ERR ``` - 在 Bash 脚本中,子 shell(使用括号 `(...)`)是一种组织参数的便捷方式。一个常见的例子是临时地移动工作路径,代码如下: ```bash # do something in current dir (cd /some/other/dir && other-command) # continue in original dir ``` - 在 Bash 中,变量有许多的扩展方式。`${name:?error message}` 用于检查变量是否存在。此外,当 Bash 脚本只需要一个参数时,可以使用这样的代码 `input_file=${1:?usage: $0 input_file}`。在变量为空时使用默认值:`${name:-default}`。如果你要在之前的例子中再加一个(可选的)参数,可以使用类似这样的代码 `output_file=${2:-logfile}`,如果省略了 \$2,它的值就为空,于是 `output_file` 就会被设为 `logfile`。数学表达式:`i=$(( (i + 1) % 5 ))`。序列:`{1..10}`。截断字符串:`${var%suffix}` 和 `${var#prefix}`。例如,假设 `var=foo.pdf`,那么 `echo ${var%.pdf}.txt` 将输出 `foo.txt`。 - 使用括号扩展(`{`...`}`)来减少输入相似文本,并自动化文本组合。这在某些情况下会很有用,例如 `mv foo.{txt,pdf} some-dir`(同时移动两个文件),`cp somefile{,.bak}`(会被扩展成 `cp somefile somefile.bak`)或者 `mkdir -p test-{a,b,c}/subtest-{1,2,3}`(会被扩展成所有可能的组合,并创建一个目录树)。 - 通过使用 `<(some command)` 可以将输出视为文件。例如,对比本地文件 `/etc/hosts` 和一个远程文件: ```bash diff /etc/hosts <(ssh somehost cat /etc/hosts) ``` - 编写脚本时,你可能会想要把代码都放在大括号里。缺少右括号的话,代码就会因为语法错误而无法执行。如果你的脚本是要放在网上分享供他人使用的,这样的写法就体现出它的好处了,因为这样可以防止下载不完全代码被执行。 ```bash { # 在这里写代码 } ``` - 了解 Bash 中的“here documents”,例如 `cat <logfile 2>&1` 或者 `some-command &>logfile`。通常,为了保证命令不会在标准输入里残留一个未关闭的文件句柄捆绑在你当前所在的终端上,在命令后添加 `>> 2+3 5 ``` ## 文件及数据处理 - 在当前目录下通过文件名查找一个文件,使用类似于这样的命令:`find . -iname '*something*'`。在所有路径下通过文件名查找文件,使用 `locate something` (但注意到 `updatedb` 可能没有对最近新建的文件建立索引,所以你可能无法定位到这些未被索引的文件)。 - 使用 [`ag`](https://github.com/ggreer/the_silver_searcher) 在源代码或数据文件里检索(`grep -r` 同样可以做到,但相比之下 `ag` 更加先进)。 - 将 HTML 转为文本:`lynx -dump -stdin`。 - Markdown,HTML,以及所有文档格式之间的转换,试试 [`pandoc`](http://pandoc.org/)。 - 当你要处理棘手的 XML 时候,`xmlstarlet` 算是上古时代流传下来的神器。 - 使用 [`jq`](http://stedolan.github.io/jq/) 处理 JSON。 - 使用 [`shyaml`](https://github.com/0k/shyaml) 处理 YAML。 - 要处理 Excel 或 CSV 文件的话,[csvkit](https://github.com/onyxfish/csvkit) 提供了 `in2csv`,`csvcut`,`csvjoin`,`csvgrep` 等方便易用的工具。 - 当你要处理 Amazon S3 相关的工作的时候,[`s3cmd`](https://github.com/s3tools/s3cmd) 是一个很方便的工具而 [`s4cmd`](https://github.com/bloomreach/s4cmd) 的效率更高。Amazon 官方提供的 [`aws`](https://github.com/aws/aws-cli) 以及 [`saws`](https://github.com/donnemartin/saws) 是其他 AWS 相关工作的基础,值得学习。 - 了解如何使用 `sort` 和 `uniq`,包括 uniq 的 `-u` 参数和 `-d` 参数,具体内容在后文单行脚本节中。另外可以了解一下 `comm`。 - 了解如何使用 `cut`,`paste` 和 `join` 来更改文件。很多人都会使用 `cut`,但遗忘了 `join`。 - 了解如何运用 `wc` 去计算新行数(`-l`),字符数(`-m`),单词数(`-w`)以及字节数(`-c`)。 - 了解如何使用 `tee` 将标准输入复制到文件甚至标准输出,例如 `ls -al | tee file.txt`。 - 要进行一些复杂的计算,比如分组、逆序和一些其他的统计分析,可以考虑使用 [`datamash`](https://www.gnu.org/software/datamash/)。 - 注意到语言设置(中文或英文等)对许多命令行工具有一些微妙的影响,比如排序的顺序和性能。大多数 Linux 的安装过程会将 `LANG` 或其他有关的变量设置为符合本地的设置。要意识到当你改变语言设置时,排序的结果可能会改变。明白国际化可能会使 sort 或其他命令运行效率下降*许多倍*。某些情况下(例如集合运算)你可以放心的使用 `export LC_ALL=C` 来忽略掉国际化并按照字节来判断顺序。 - 你可以单独指定某一条命令的环境,只需在调用时把环境变量设定放在命令的前面,例如 `TZ=Pacific/Fiji date` 可以获取斐济的时间。 - 了解如何使用 `awk` 和 `sed` 来进行简单的数据处理。 参阅 [One-liners](#one-liners) 获取示例。 - 替换一个或多个文件中出现的字符串: ```bash perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt ``` - 使用 [`repren`](https://github.com/jlevy/repren) 来批量重命名文件,或是在多个文件中搜索替换内容。(有些时候 `rename` 命令也可以批量重命名,但要注意,它在不同 Linux 发行版中的功能并不完全一样。) ```bash # 将文件、目录和内容全部重命名 foo -> bar: repren --full --preserve-case --from foo --to bar . # 还原所有备份文件 whatever.bak -> whatever: repren --renames --from '(.*)\.bak' --to '\1' *.bak # 用 rename 实现上述功能(若可用): rename 's/\.bak$//' *.bak ``` - 根据 man 页面的描述,`rsync` 是一个快速且非常灵活的文件复制工具。它闻名于设备之间的文件同步,但其实它在本地情况下也同样有用。在安全设置允许下,用 `rsync` 代替 `scp` 可以实现文件续传,而不用重新从头开始。它同时也是删除大量文件的[最快方法](https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html)之一: ```bash mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir ``` - 若要在复制文件时获取当前进度,可使用 `pv`,[`pycp`](https://github.com/dmerejkowsky/pycp),[`progress`](https://github.com/Xfennec/progress),`rsync --progress`。若所执行的复制为 block 块拷贝,可以使用 `dd status=progress`。 - 使用 `shuf` 可以以行为单位来打乱文件的内容或从一个文件中随机选取多行。 - 了解 `sort` 的参数。显示数字时,使用 `-n` 或者 `-h` 来显示更易读的数(例如 `du -h` 的输出)。明白排序时关键字的工作原理(`-t` 和 `-k`)。例如,注意到你需要 `-k1,1` 来仅按第一个域来排序,而 `-k1` 意味着按整行排序。稳定排序(`sort -s`)在某些情况下很有用。例如,以第二个域为主关键字,第一个域为次关键字进行排序,你可以使用 `sort -k1,1 | sort -s -k2,2`。 - 如果你想在 Bash 命令行中写 tab 制表符,按下 **ctrl-v** **[Tab]** 或键入 `$'\t'` (后者可能更好,因为你可以复制粘贴它)。 - 标准的源代码对比及合并工具是 `diff` 和 `patch`。使用 `diffstat` 查看变更总览数据。注意到 `diff -r` 对整个文件夹有效。使用 `diff -r tree1 tree2 | diffstat` 查看变更的统计数据。`vimdiff` 用于比对并编辑文件。 - 对于二进制文件,使用 `hd`,`hexdump` 或者 `xxd` 使其以十六进制显示,使用 `bvi`,`hexedit` 或者 `biew` 来进行二进制编辑。 - 同样对于二进制文件,`strings`(包括 `grep` 等工具)可以帮助在二进制文件中查找特定比特。 - 制作二进制差分文件(Delta 压缩),使用 `xdelta3`。 - 使用 `iconv` 更改文本编码。需要更高级的功能,可以使用 `uconv`,它支持一些高级的 Unicode 功能。例如,这条命令移除了所有重音符号: ```bash uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt ``` - 拆分文件可以使用 `split`(按大小拆分)和 `csplit`(按模式拆分)。 - 操作日期和时间表达式,可以用 [`dateutils`](http://www.fresse.org/dateutils/) 中的 `dateadd`、`datediff`、`strptime` 等工具。 - 使用 `zless`、`zmore`、`zcat` 和 `zgrep` 对压缩过的文件进行操作。 - 文件属性可以通过 `chattr` 进行设置,它比文件权限更加底层。例如,为了保护文件不被意外删除,可以使用不可修改标记:`sudo chattr +i /critical/directory/or/file` - 使用 `getfacl` 和 `setfacl` 以保存和恢复文件权限。例如: ```bash getfacl -R /some/path > permissions.txt setfacl --restore=permissions.txt ``` - 为了高效地创建空文件,请使用 `truncate`(创建[稀疏文件](https://zh.wikipedia.org/wiki/稀疏文件)),`fallocate`(用于 ext4,xfs,btrf 和 ocfs2 文件系统),`xfs_mkfile`(适用于几乎所有的文件系统,包含在 xfsprogs 包中),`mkfile`(用于类 Unix 操作系统,比如 Solaris 和 Mac OS)。 ## 系统调试 - `curl` 和 `curl -I` 可以被轻松地应用于 web 调试中,它们的好兄弟 `wget` 也是如此,或者也可以试试更潮的 [`httpie`](https://github.com/jkbrzt/httpie)。 - 获取 CPU 和硬盘的使用状态,通常使用使用 `top`(`htop` 更佳),`iostat` 和 `iotop`。而 `iostat -mxz 15` 可以让你获悉 CPU 和每个硬盘分区的基本信息和性能表现。 - 使用 `netstat` 和 `ss` 查看网络连接的细节。 - `dstat` 在你想要对系统的现状有一个粗略的认识时是非常有用的。然而若要对系统有一个深度的总体认识,使用 [`glances`](https://github.com/nicolargo/glances),它会在一个终端窗口中向你提供一些系统级的数据。 - 若要了解内存状态,运行并理解 `free` 和 `vmstat` 的输出。值得留意的是“cached”的值,它指的是 Linux 内核用来作为文件缓存的内存大小,而与空闲内存无关。 - Java 系统调试则是一件截然不同的事,一个可以用于 Oracle 的 JVM 或其他 JVM 上的调试的技巧是你可以运行 `kill -3 ` 同时一个完整的栈轨迹和堆概述(包括 GC 的细节)会被保存到标准错误或是日志文件。JDK 中的 `jps`,`jstat`,`jstack`,`jmap` 很有用。[SJK tools](https://github.com/aragozin/jvm-tools) 更高级。 - 使用 [`mtr`](http://www.bitwizard.nl/mtr/) 去跟踪路由,用于确定网络问题。 - 用 [`ncdu`](https://dev.yorhel.nl/ncdu) 来查看磁盘使用情况,它比寻常的命令,如 `du -sh *`,更节省时间。 - 查找正在使用带宽的套接字连接或进程,使用 [`iftop`](http://www.ex-parrot.com/~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs)。 - `ab` 工具(Apache 中自带)可以简单粗暴地检查 web 服务器的性能。对于更复杂的负载测试,使用 `siege`。 - [`wireshark`](https://wireshark.org/),[`tshark`](https://www.wireshark.org/docs/wsug_html_chunked/AppToolstshark.html) 和 [`ngrep`](http://ngrep.sourceforge.net/) 可用于复杂的网络调试。 - 了解 `strace` 和 `ltrace`。这俩工具在你的程序运行失败、挂起甚至崩溃,而你却不知道为什么或你想对性能有个总体的认识的时候是非常有用的。注意 profile 参数(`-c`)和附加到一个运行的进程参数 (`-p`)。 - 了解使用 `ldd` 来检查共享库。但是[永远不要在不信任的文件上运行](http://www.catonmat.net/blog/ldd-arbitrary-code-execution/)。 - 了解如何运用 `gdb` 连接到一个运行着的进程并获取它的堆栈轨迹。 - 学会使用 `/proc`。它在调试正在出现的问题的时候有时会效果惊人。比如:`/proc/cpuinfo`,`/proc/meminfo`,`/proc/cmdline`,`/proc/xxx/cwd`,`/proc/xxx/exe`,`/proc/xxx/fd/`,`/proc/xxx/smaps`(这里的 `xxx` 表示进程的 id 或 pid)。 - 当调试一些之前出现的问题的时候,[`sar`](http://sebastien.godard.pagesperso-orange.fr/) 非常有用。它展示了 cpu、内存以及网络等的历史数据。 - 关于更深层次的系统分析以及性能分析,看看 `stap`([SystemTap](https://sourceware.org/systemtap/wiki)),[`perf`](),以及[`sysdig`](https://github.com/draios/sysdig)。 - 查看你当前使用的系统,使用 `uname`,`uname -a`(Unix/kernel 信息)或者 `lsb_release -a`(Linux 发行版信息)。 - 无论什么东西工作得很欢乐(可能是硬件或驱动问题)时可以试试 `dmesg`。 - 如果你删除了一个文件,但通过 `du` 发现没有释放预期的磁盘空间,请检查文件是否被进程占用: `lsof | grep deleted | grep "filename-of-my-big-file"` ## 单行脚本 一些命令组合的例子: - 当你需要对文本文件做集合交、并、差运算时,`sort` 和 `uniq` 会是你的好帮手。具体例子请参照代码后面的,此处假设 `a` 与 `b` 是两内容不同的文件。这种方式效率很高,并且在小文件和上 G 的文件上都能运用(注意尽管在 `/tmp` 在一个小的根分区上时你可能需要 `-T` 参数,但是实际上 `sort` 并不被内存大小约束),参阅前文中关于 `LC_ALL` 和 `sort` 的 `-u` 参数的部分。 ```bash sort a b | uniq > c # c 是 a 并 b sort a b | uniq -d > c # c 是 a 交 b sort a b b | uniq -u > c # c 是 a - b ``` - 使用 `grep . *`(每行都会附上文件名)或者 `head -100 *`(每个文件有一个标题)来阅读检查目录下所有文件的内容。这在检查一个充满配置文件的目录(如 `/sys`、`/proc`、`/etc`)时特别好用。 * 计算文本文件第三列中所有数的和(可能比同等作用的 Python 代码快三倍且代码量少三倍): ```bash awk '{ x += $3 } END { print x }' myfile ``` - 如果你想在文件树上查看大小/日期,这可能看起来像递归版的 `ls -l` 但比 `ls -lR` 更易于理解: ```bash find . -type f -ls ``` - 假设你有一个类似于 web 服务器日志文件的文本文件,并且一个确定的值只会出现在某些行上,假设一个 `acct_id` 参数在 URI 中。如果你想计算出每个 `acct_id` 值有多少次请求,使用如下代码: ```bash egrep -o 'acct_id=[0-9]+' access.log | cut -d= -f2 | sort | uniq -c | sort -rn ``` - 要持续监测文件改动,可以使用 `watch`,例如检查某个文件夹中文件的改变,可以用 `watch -d -n 2 'ls -rtlh | tail'`;或者在排查 WiFi 设置故障时要监测网络设置的更改,可以用 `watch -d -n 2 ifconfig`。 - 运行这个函数从这篇文档中随机获取一条技巧(解析 Markdown 文件并抽取项目): ```bash function taocl() { curl -s https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/README-zh.md| pandoc -f markdown -t html | iconv -f 'utf-8' -t 'unicode' | xmlstarlet fo --html --dropdtd | xmlstarlet sel -t -v "(html/body/ul/li[count(p)>0])[$RANDOM mod last()+1]" | xmlstarlet unesc | fmt -80 } ``` ## 冷门但有用 - `expr`:计算表达式或正则匹配 - `m4`:简单的宏处理器 - `yes`:多次打印字符串 - `cal`:漂亮的日历 - `env`:执行一个命令(脚本文件中很有用) - `printenv`:打印环境变量(调试时或在写脚本文件时很有用) - `look`:查找以特定字符串开头的单词或行 - `cut`,`paste` 和 `join`:数据修改 - `fmt`:格式化文本段落 - `pr`:将文本格式化成页/列形式 - `fold`:包裹文本中的几行 - `column`:将文本格式化成多个对齐、定宽的列或表格 - `expand` 和 `unexpand`:制表符与空格之间转换 - `nl`:添加行号 - `seq`:打印数字 - `bc`:计算器 - `factor`:分解因数 - [`gpg`](https://gnupg.org/):加密并签名文件 - `toe`:terminfo 入口列表 - `nc`:网络调试及数据传输 - `socat`:套接字代理,与 `netcat` 类似 - [`slurm`](https://github.com/mattthias/slurm):网络流量可视化 - `dd`:文件或设备间传输数据 - `file`:确定文件类型 - `tree`:以树的形式显示路径和文件,类似于递归的 `ls` - `stat`:文件信息 - `time`:执行命令,并计算执行时间 - `timeout`:在指定时长范围内执行命令,并在规定时间结束后停止进程 - `lockfile`:使文件只能通过 `rm -f` 移除 - `logrotate`: 切换、压缩以及发送日志文件 - `watch`:重复运行同一个命令,展示结果并/或高亮有更改的部分 - [`when-changed`](https://github.com/joh/when-changed):当检测到文件更改时执行指定命令。参阅 `inotifywait` 和 `entr`。 - `tac`:反向输出文件 - `shuf`:文件中随机选取几行 - `comm`:一行一行的比较排序过的文件 - `strings`:从二进制文件中抽取文本 - `tr`:转换字母 - `iconv` 或 `uconv`:文本编码转换 - `split` 和 `csplit`:分割文件 - `sponge`:在写入前读取所有输入,在读取文件后再向同一文件写入时比较有用,例如 `grep -v something some-file | sponge some-file` - `units`:将一种计量单位转换为另一种等效的计量单位(参阅 `/usr/share/units/definitions.units`) - `apg`:随机生成密码 - `xz`:高比例的文件压缩 - `ldd`:动态库信息 - `nm`:提取 obj 文件中的符号 - `ab` 或 [`wrk`](https://github.com/wg/wrk):web 服务器性能分析 - `strace`:调试系统调用 - [`mtr`](http://www.bitwizard.nl/mtr/):更好的网络调试跟踪工具 - `cssh`:可视化的并发 shell - `rsync`:通过 ssh 或本地文件系统同步文件和文件夹 - [`wireshark`](https://wireshark.org/) 和 [`tshark`](https://www.wireshark.org/docs/wsug_html_chunked/AppToolstshark.html):抓包和网络调试工具 - [`ngrep`](http://ngrep.sourceforge.net/):网络层的 grep - `host` 和 `dig`:DNS 查找 - `lsof`:列出当前系统打开文件的工具以及查看端口信息 - `dstat`:系统状态查看 - [`glances`](https://github.com/nicolargo/glances):高层次的多子系统总览 - `iostat`:硬盘使用状态 - `mpstat`: CPU 使用状态 - `vmstat`: 内存使用状态 - `htop`:top 的加强版 - `last`:登入记录 - `w`:查看处于登录状态的用户 - `id`:用户/组 ID 信息 - [`sar`](http://sebastien.godard.pagesperso-orange.fr/):系统历史数据 - [`iftop`](http://www.ex-parrot.com/~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs):套接字及进程的网络利用情况 - `ss`:套接字数据 - `dmesg`:引导及系统错误信息 - `sysctl`: 在内核运行时动态地查看和修改内核的运行参数 - `hdparm`:SATA/ATA 磁盘更改及性能分析 - `lsblk`:列出块设备信息:以树形展示你的磁盘以及磁盘分区信息 - `lshw`,`lscpu`,`lspci`,`lsusb` 和 `dmidecode`:查看硬件信息,包括 CPU、BIOS、RAID、显卡、USB 设备等 - `lsmod` 和 `modinfo`:列出内核模块,并显示其细节 - `fortune`,`ddate` 和 `sl`:额,这主要取决于你是否认为蒸汽火车和莫名其妙的名人名言是否“有用” ## 仅限 OS X 系统 以下是*仅限于* OS X 系统的技巧。 - 用 `brew` (Homebrew)或者 `port` (MacPorts)进行包管理。这些可以用来在 OS X 系统上安装以上的大多数命令。 - 用 `pbcopy` 复制任何命令的输出到桌面应用,用 `pbpaste` 粘贴输入。 - 若要在 OS X 终端中将 Option 键视为 alt 键(例如在上面介绍的 **alt-b**、**alt-f** 等命令中用到),打开 偏好设置 -> 描述文件 -> 键盘 并勾选“使用 Option 键作为 Meta 键”。 - 用 `open` 或者 `open -a /Applications/Whatever.app` 使用桌面应用打开文件。 - Spotlight:用 `mdfind` 搜索文件,用 `mdls` 列出元数据(例如照片的 EXIF 信息)。 - 注意 OS X 系统是基于 BSD UNIX 的,许多命令(例如 `ps`,`ls`,`tail`,`awk`,`sed`)都和 Linux 中有微妙的不同( Linux 很大程度上受到了 System V-style Unix 和 GNU 工具影响)。你可以通过标题为 "BSD General Commands Manual" 的 man 页面发现这些不同。在有些情况下 GNU 版本的命令也可能被安装(例如 `gawk` 和 `gsed` 对应 GNU 中的 awk 和 sed )。如果要写跨平台的 Bash 脚本,避免使用这些命令(例如,考虑 Python 或者 `perl` )或者经过仔细的测试。 - 用 `sw_vers` 获取 OS X 的版本信息。 ## 仅限 Windows 系统 以下是*仅限于* Windows 系统的技巧。 ### 在 Winodws 下获取 Unix 工具 - 可以安装 [Cygwin](https://cygwin.com/) 允许你在 Microsoft Windows 中体验 Unix shell 的威力。这样的话,本文中介绍的大多数内容都将适用。 - 在 Windows 10 上,你可以使用 [Bash on Ubuntu on Windows](https://msdn.microsoft.com/commandline/wsl/about),它提供了一个熟悉的 Bash 环境,包含了不少 Unix 命令行工具。好处是它允许 Linux 上编写的程序在 Windows 上运行,而另一方面,Windows 上编写的程序却无法在 Bash 命令行中运行。 - 如果你在 Windows 上主要想用 GNU 开发者工具(例如 GCC),可以考虑 [MinGW](http://www.mingw.org/) 以及它的 [MSYS](http://www.mingw.org/wiki/msys) 包,这个包提供了例如 bash,gawk,make 和 grep 的工具。MSYS 并不包含所有可以与 Cygwin 媲美的特性。当制作 Unix 工具的原生 Windows 端口时 MinGW 将特别地有用。 - 另一个在 Windows 下实现接近 Unix 环境外观效果的选项是 [Cash](https://github.com/dthree/cash)。注意在此环境下只有很少的 Unix 命令和命令行可用。 ### 实用 Windows 命令行工具 - 可以使用 `wmic` 在命令行环境下给大部分 Windows 系统管理任务编写脚本以及执行这些任务。 - Windows 实用的原生命令行网络工具包括 `ping`,`ipconfig`,`tracert`,和 `netstat`。 - 可以使用 `Rundll32` 命令来实现[许多有用的 Windows 任务](http://www.thewindowsclub.com/rundll32-shortcut-commands-windows) 。 ### Cygwin 技巧 - 通过 Cygwin 的包管理器来安装额外的 Unix 程序。 - 使用 `mintty` 作为你的命令行窗口。 - 要访问 Windows 剪贴板,可以通过 `/dev/clipboard`。 - 运行 `cygstart` 以通过默认程序打开一个文件。 - 要访问 Windows 注册表,可以使用 `regtool`。 - 注意 Windows 驱动器路径 `C:\` 在 Cygwin 中用 `/cygdrive/c` 代表,而 Cygwin 的 `/` 代表 Windows 中的 `C:\cygwin`。要转换 Cygwin 和 Windows 风格的路径可以用 `cygpath`。这在需要调用 Windows 程序的脚本里很有用。 - 学会使用 `wmic`,你就可以从命令行执行大多数 Windows 系统管理任务,并编成脚本。 - 要在 Windows 下获得 Unix 的界面和体验,另一个办法是使用 [Cash](https://github.com/dthree/cash)。需要注意的是,这个环境支持的 Unix 命令和命令行参数非常少。 - 要在 Windows 上获取 GNU 开发者工具(比如 GCC)的另一个办法是使用 [MinGW](http://www.mingw.org/) 以及它的 [MSYS](http://www.mingw.org/wiki/msys) 软件包,该软件包提供了 bash、gawk、make、grep 等工具。然而 MSYS 提供的功能没有 Cygwin 完善。MinGW 在创建 Unix 工具的 Windows 原生移植方面非常有用。 ## 更多资源 - [awesome-shell](https://github.com/alebcay/awesome-shell):一份精心组织的命令行工具及资源的列表。 - [awesome-osx-command-line](https://github.com/herrbischoff/awesome-osx-command-line):一份针对 OS X 命令行的更深入的指南。 - [Strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/):为了编写更好的脚本文件。 - [shellcheck](https://github.com/koalaman/shellcheck):一个静态 shell 脚本分析工具,本质上是 bash/sh/zsh 的 lint。 - [Filenames and Pathnames in Shell](http://www.dwheeler.com/essays/filenames-in-shell.html):有关如何在 shell 脚本里正确处理文件名的细枝末节。 - [Data Science at the Command Line](http://datascienceatthecommandline.com/#tools):用于数据科学的一些命令和工具,摘自同名书籍。 ## 免责声明 除去特别小的工作,你编写的代码应当方便他人阅读。能力往往伴随着责任,你 _有能力_ 在 Bash 中玩一些奇技淫巧并不意味着你应该去做!;) ## 授权条款 ![img](http://creativecommons.org/licenses/by-sa/4.0/) 本文使用授权协议 [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)。 ================================================ FILE: docs/linux/expect.md ================================================ # expect shell 脚本 ## expect 简介 `expect` 是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。 在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用 `expect`,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。这就是 `expect` 。 expect 自动交互流程: 1. spawn 启动指定进程 2. expect 获取指定关键字 3. send 向指定程序发送指定字符 4. 执行完成退出 ## expect 安装 ### yum 安装 执行命令: ```shell yum -y install expect ``` ### 手动安装 expect 依赖 tcl,所以需要先安装 tcl: ```shell wget https://nchc.dl.sourceforge.net/project/tcl/Tcl/8.6.9/tcl8.6.9-src.tar.gz tar xf tcl8.6.9-src.tar.gz cd tcl8.6.9/unix/ ./configure && make && sudo make install ``` 再安装 expect: ```shell wget https://nchc.dl.sourceforge.net/project/expect/Expect/5.45.4/expect5.45.4.tar.gz tar xf expect5.45.4.tar.gz cd ./expect5.45.4 ./configure && make && sudo make install ``` ## expect 参数 启用选项: - `-c` - 执行脚本前先执行的命令,可多次使用。 - `-d` - debug 模式,可以在运行时输出一些诊断信息,与在脚本开始处使用 `exp_internal 1` 相似。 - `-D` - 启用交换调式器,可设一整数参数。 - `-f` - 从文件读取命令,仅用于使用 `#!` 时。如果文件名为 `-`,则从 stdin 读取(使用 `./-` 从文件名为-的文件读取)。 - `-i` - 交互式输入命令,使用 `exit` 或 `EOF` 退出输入状态。 - `--` - 标示选项结束(如果你需要传递与 `expect` 选项相似的参数给脚本时),可放到 `#!` 行: `#!/usr/bin/expect --` 。 - `-v` - 显示 `expect` 版本信息。 ## expect 命令 - `spawn` - 命令用来启动新的进程,`spawn`后的`send`和`expect`命令都是和使用`spawn`打开的进程进行交互。 - `expect` - 获取匹配信息,匹配成功则执行 `expect` 后面的程序动作。 - `exp_continue` - 在 `expect` 中多次匹配就需要用到。 - `send` - 命令接收一个字符串参数,并将该参数发送到进程。 - `send exp_send` - 用于发送指定的字符串信息。 - `interact` - 命令用的其实不是很多,一般情况下使用`spawn`、`send`和`expect`命令就可以很好的完成我们的任务;但在一些特殊场合下还是需要使用`interact`命令的,`interact`命令主要用于退出自动化,进入人工交互。比如我们使用`spawn`、`send`和`expect`命令完成了 ftp 登陆主机,执行下载文件任务,但是我们希望在文件下载结束以后,仍然可以停留在 ftp 命令行状态,以便手动的执行后续命令,此时使用`interact`命令就可以很好的完成这个任务。 - `send_user` - 用来打印输出 相当于 shell 中的 echo - `set` - 定义变量。 - `set timeout` - 设置超时时间。 - `puts` - 输出变量。 - `exit` - 退出 expect 脚本 - `eof` - expect 执行结束,退出。 ## 示例场景 远程登录 (1)ssh 登录远程主机执行命令,执行方法 `expect 1.sh` 或者 `source 1.sh` ```shell #!/usr/bin/expect spawn ssh saneri@192.168.56.103 df -Th expect "*password" send "123456\n" expect eof ``` (2)ssh 远程登录主机执行命令,在 shell 脚本中执行 expect 命令,执行方法 sh 2.sh、bash 2.sh 或./2.sh 都可以执行. ``` #!/bin/bash passwd='123456' /usr/bin/expect <<-EOF set time 30 spawn ssh saneri@192.168.56.103 df -Th expect { "*yes/no" { send "yes\r"; exp_continue } "*password:" { send "$passwd\r" } } expect eof EOF ``` (3)expect 执行多条命令 ``` #!/usr/bin/expect -f set timeout 10 spawn sudo su - root expect "*password*" send "123456\r" expect "#*" send "ls\r" expect "#*" send "df -Th\r" send "exit\r" expect eof ``` (4)创建 ssh key,将 id_rsa 和 id_rsa.pub 文件分发到各台主机上面。 ```shell #!/bin/bash # 判断id_rsa密钥文件是否存在 if [ ! -f ~/.ssh/id_rsa ];then ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa else echo "id_rsa has created ..." fi #分发到各个节点,这里分发到host文件中的主机中. while read line do user=`echo $line | cut -d " " -f 2` ip=`echo $line | cut -d " " -f 1` passwd=`echo $line | cut -d " " -f 3` expect < 环境:CentOS 通过 `crontab` 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script 脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。这个命令非常适合周期性的日志分析或数据备份等工作。 ## crond 服务 Linux 通过 crond 服务来支持 crontab。 ### 检查 `crond` 服务 使用 `systemctl list-unit-files` 命令确认 `crond` 服务是否已安装。 ```shell $ systemctl list-unit-files | grep crond crond.service enabled ``` 如果为 enabled,表示服务正运行。 ### crond 服务命令 开机自动启动 crond 服务:`chkconfig crond on` 或者,按以下命令手动启动: ```shell systemctl enable crond.service # 开启服务(开机自动启动服务) systemctl disable crond.service # 关闭服务(开机不会自动启动服务) systemctl start crond.service # 启动服务 systemctl stop crond.service # 停止服务 systemctl restart crond.service # 重启服务 systemctl reload crond.service # 重新载入配置 systemctl status crond.service # 查看服务状态 ``` ## crontab ### crontab 命令 crontab 命令格式如下: ```shell crontab [-u user] file crontab [-u user] [ -e | -l | -r ] ``` 说明: - `-u user`:用来设定某个用户的 crontab 服务; - `file`:file 是命令文件的名字,表示将 file 做为 crontab 的任务列表文件并载入 crontab。如果在命令行中没有指定这个文件,crontab 命令将接受标准输入(键盘)上键入的命令,并将它们载入 crontab。 - `-e`:编辑某个用户的 crontab 文件内容。如果不指定用户,则表示编辑当前用户的 crontab 文件。 - `-l`:显示某个用户的 crontab 文件内容,如果不指定用户,则表示显示当前用户的 crontab 文件内容。 - `-r`:从/var/spool/cron 目录中删除某个用户的 crontab 文件,如果不指定用户,则默认删除当前用户的 crontab 文件。 - `-i`:在删除用户的 crontab 文件时给确认提示。 有两种方法写入定时任务: - 在命令行输入:`crontab -e` 然后添加相应的任务,存盘退出。 - 直接编辑 `/etc/crontab` 文件,即 `vi /etc/crontab`,添加相应的任务。 ### crontab 文件 crontab 要执行的定时任务都被保存在 `/etc/crontab` 文件中。 crontab 的文件格式如下: ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200211113339.png) #### 标准字段 **逗号**用于分隔列表。例如,在第 5 个字段(星期几)中使用 `MON,WED,FRI` 表示周一、周三和周五。 **连字符**定义范围。例如,`2000-2010` 表示 2000 年至 2010 年期间的每年,包括 2000 年和 2010 年。 除非用反斜杠(\)转义,否则命令中的**百分号(%)**会被替换成换行符,第一个百分号后面的所有数据都会作为标准输入发送给命令。 | 字段 | 是否必填 | 允许值 | 允许特殊字符 | | :----------- | :------- | :-------------- | :----------- | | Minutes | 是 | 0–59 | `*`,`-` | | Hours | 是 | 0–23 | `*`,`-` | | Day of month | 是 | 1–31 | `*`,`-` | | Month | 是 | 1–12 or JAN–DEC | `*`,`-` | | Day of week | 是 | 0–6 or SUN–SAT | `*`,`-` | `/etc/crontab` 文件示例: ```shell SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed # 每两个小时以root身份执行 /home/hello.sh 脚本 0 */2 * * * root /home/hello.sh ``` ### crontab 实例 #### 实例 1:每 1 分钟执行一次 myCommand ```shell * * * * * myCommand ``` #### 实例 2:每小时的第 3 和第 15 分钟执行 ```shell 3,15 * * * * myCommand ``` #### 实例 3:在上午 8 点到 11 点的第 3 和第 15 分钟执行 ```shell 3,15 8-11 * * * myCommand ``` #### 实例 4:每隔两天的上午 8 点到 11 点的第 3 和第 15 分钟执行 ```shell 3,15 8-11 */2 * * myCommand ``` #### 实例 5:每周一上午 8 点到 11 点的第 3 和第 15 分钟执行 ```shell 3,15 8-11 * * 1 myCommand ``` #### 实例 6:每晚的 21:30 重启 smb ```shell 30 21 * * * /etc/init.d/smb restart ``` #### 实例 7:每月 1、10、22 日的 4 : 45 重启 smb ```shell 45 4 1,10,22 * * /etc/init.d/smb restart ``` #### 实例 8:每周六、周日的 1 : 10 重启 smb ```shell 10 1 * * 6,0 /etc/init.d/smb restart ``` #### 实例 9:每天 18 : 00 至 23 : 00 之间每隔 30 分钟重启 smb ```shell 0,30 18-23 * * * /etc/init.d/smb restart ``` #### 实例 10:每星期六的晚上 11 : 00 pm 重启 smb ```shell 0 23 * * 6 /etc/init.d/smb restart ``` #### 实例 11:每一小时重启 smb ```shell 0 * * * * /etc/init.d/smb restart ``` #### 实例 12:晚上 11 点到早上 7 点之间,每隔一小时重启 smb ```shell 0 23-7 * * * /etc/init.d/smb restart ``` ## 参考资料 - **文章** - [crontab 定时任务](https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/crontab.html) - [linux 定时执行脚本](https://blog.csdn.net/z_yong_cool/article/details/79288397) - **在线工具** - [https://tool.lu/crontab/](https://tool.lu/crontab/) - [https://cron.qqe2.com/](https://cron.qqe2.com/) ================================================ FILE: docs/linux/ops/firewalld.md ================================================ # 防火墙 - Firewalld ## 一、firewalld 服务命令 ```shell systemctl enable firewalld.service # 开启服务(开机自动启动服务) systemctl disable firewalld.service # 关闭服务(开机不会自动启动服务) systemctl start firewalld.service # 启动服务 systemctl stop firewalld.service # 停止服务 systemctl restart firewalld.service # 重启服务 systemctl reload firewalld.service # 重新载入配置 systemctl status firewalld.service # 查看服务状态 ``` ## 二、firewall-cmd 命令 `firewall-cmd` 命令用于配置防火墙。 ```shell firewall-cmd --version # 查看版本 firewall-cmd --help # 查看帮助 firewall-cmd --state # 显示状态 firewall-cmd --reload # 更新防火墙规则 firewall-cmd --get-active-zones # 查看区域信息 firewall-cmd --get-zone-of-interface=eth0 # 查看指定接口所属区域 firewall-cmd --panic-on # 拒绝所有包 firewall-cmd --panic-off # 取消拒绝状态 firewall-cmd --query-panic # 查看是否拒绝 firewall-cmd --zone=public --list-ports # 查看所有打开的端口 firewall-cmd --zone=public --query-port=80/tcp # 查看是否有开放的 80 TCP 端口 firewall-cmd --zone=public --add-port=8080/tcp --permanent # 添加开放端口(--permanent永久生效,没有此参数重启后失效) firewall-cmd --zone=public --remove-port=80/tcp --permanent # 永久删除开放的 80 TCP 端口 ``` ## 参考资料 - [CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) ================================================ FILE: docs/linux/ops/iptables.md ================================================ # Iptables 应用 > _iptables_ 是一个配置 Linux 内核 [防火墙](https://wiki.archlinux.org/index.php/Firewall) 的命令行工具,是 [netfilter](https://en.wikipedia.org/wiki/Netfilter) 项目的一部分。 可以直接配置,也可以通过许多前端和图形界面配置。 > > iptables 也经常代指该内核级防火墙。iptables 用于 [ipv4](https://en.wikipedia.org/wiki/Ipv4),_ip6tables_ 用于 [ipv6](https://en.wikipedia.org/wiki/Ipv6)。 > > [nftables](https://wiki.archlinux.org/index.php/Nftables) 已经包含在 [Linux kernel 3.13](http://www.phoronix.com/scan.php?page=news_item&px=MTQ5MDU) 中,以后会取代 iptables 成为主要的 Linux 防火墙工具。 > > 环境:CentOS7 ## 1. 简介 **iptables 可以检测、修改、转发、重定向和丢弃 IPv4 数据包**。 过滤 IPv4 数据包的代码已经内置于内核中,并且按照不同的目的被组织成 **表** 的集合。**表** 由一组预先定义的 **链** 组成,**链**包含遍历顺序规则。每一条规则包含一个谓词的潜在匹配和相应的动作(称为 **目标**),如果谓词为真,该动作会被执行。也就是说条件匹配。 ## 2. 安装 iptables (1)禁用 firewalld CentOS 7 上默认安装了 firewalld 作为防火墙,使用 iptables 建议关闭并禁用 firewalld。 ```bash systemctl stop firewalld systemctl disable firewalld ``` (2)安装 iptables ``` yum install -y iptables-services ``` (3)服务管理 - 查看服务状态:`systemctl status iptables` - 启用服务:`systemctl enable iptables` - 禁用服务:`systemctl disable iptables` - 启动服务:`systemctl start iptables` - 重启服务:`systemctl restart iptables` - 关闭服务: `systemctl stop iptables` ## 3. 命令 基本语法: ``` iptables(选项)(参数) ``` 基本选项说明: | 参数 | 作用 | | ----------- | ------------------------------------------------- | | -P | 设置默认策略:iptables -P INPUT (DROP | | -F | 清空规则链 | | -L | 查看规则链 | | -A | 在规则链的末尾加入新规则 | | -I | num 在规则链的头部加入新规则 | | -D | num 删除某一条规则 | | -s | 匹配来源地址 IP/MASK,加叹号"!"表示除这个 IP 外。 | | -d | 匹配目标地址 | | -i | 网卡名称 匹配从这块网卡流入的数据 | | -o | 网卡名称 匹配从这块网卡流出的数据 | | -p | 匹配协议,如 tcp,udp,icmp | | --dport num | 匹配目标端口号 | | --sport num | 匹配来源端口号 | 顺序: ``` iptables -t 表名 <-A/I/D/R> 规则链名 [规则号] <-i/o 网卡名> -p 协议名 <-s 源IP/源子网> --sport 源端口 <-d 目标IP/目标子网> --dport 目标端口 -j 动作 ``` ## 4. iptables 示例 ### 4.1. 清空当前的所有规则和计数 ```shell iptables -F # 清空所有的防火墙规则 iptables -X # 删除用户自定义的空链 iptables -Z # 清空计数 ``` ### 4.2. 配置允许 ssh 端口连接 ```shell iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT # 22为你的ssh端口, -s 192.168.1.0/24表示允许这个网段的机器来连接,其它网段的ip地址是登陆不了你的机器的。 -j ACCEPT表示接受这样的请求 ``` ### 4.3. 允许本地回环地址可以正常使用 ```shell iptables -A INPUT -i lo -j ACCEPT #本地圆环地址就是那个127.0.0.1,是本机上使用的,它进与出都设置为允许 iptables -A OUTPUT -o lo -j ACCEPT ``` ### 4.4. 设置默认的规则 ```shell iptables -P INPUT DROP # 配置默认的不让进 iptables -P FORWARD DROP # 默认的不允许转发 iptables -P OUTPUT ACCEPT # 默认的可以出去 ``` ### 4.5. 配置白名单 ```shell iptables -A INPUT -p all -s 192.168.1.0/24 -j ACCEPT # 允许机房内网机器可以访问 iptables -A INPUT -p all -s 192.168.140.0/24 -j ACCEPT # 允许机房内网机器可以访问 iptables -A INPUT -p tcp -s 183.121.3.7 --dport 3380 -j ACCEPT # 允许183.121.3.7访问本机的3380端口 ``` ### 4.6. 开启相应的服务端口 ```shell iptables -A INPUT -p tcp --dport 80 -j ACCEPT # 开启80端口,因为web对外都是这个端口 iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT # 允许被ping iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 已经建立的连接得让它进来 ``` ### 4.7. 保存规则到配置文件中 ```shell cp /etc/sysconfig/iptables /etc/sysconfig/iptables.bak # 任何改动之前先备份,请保持这一优秀的习惯 iptables-save > /etc/sysconfig/iptables cat /etc/sysconfig/iptables ``` ### 4.8. 列出已设置的规则 > iptables -L [-t 表名][链名] - 四个表名 `raw`,`nat`,`filter`,`mangle` - 五个规则链名 `INPUT`、`OUTPUT`、`FORWARD`、`PREROUTING`、`POSTROUTING` - filter 表包含`INPUT`、`OUTPUT`、`FORWARD`三个规则链 ```shell iptables -L -t nat # 列出 nat 上面的所有规则 # ^ -t 参数指定,必须是 raw, nat,filter,mangle 中的一个 iptables -L -t nat --line-numbers # 规则带编号 iptables -L INPUT iptables -L -nv # 查看,这个列表看起来更详细 ``` ### 4.9. 清除已有规则 ```shell iptables -F INPUT # 清空指定链 INPUT 上面的所有规则 iptables -X INPUT # 删除指定的链,这个链必须没有被其它任何规则引用,而且这条上必须没有任何规则。 # 如果没有指定链名,则会删除该表中所有非内置的链。 iptables -Z INPUT # 把指定链,或者表中的所有链上的所有计数器清零。 ``` ### 4.10. 删除已添加的规则 ```shell # 添加一条规则 iptables -A INPUT -s 192.168.1.5 -j DROP ``` 将所有 iptables 以序号标记显示,执行: ```shell iptables -L -n --line-numbers ``` 比如要删除 INPUT 里序号为 8 的规则,执行: ```shell iptables -D INPUT 8 ``` ### 4.11. 开放指定的端口 ```shell iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT #允许本地回环接口(即运行本机访问本机) iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #允许已建立的或相关连的通行 iptables -A OUTPUT -j ACCEPT #允许所有本机向外的访问 iptables -A INPUT -p tcp --dport 22 -j ACCEPT #允许访问22端口 iptables -A INPUT -p tcp --dport 80 -j ACCEPT #允许访问80端口 iptables -A INPUT -p tcp --dport 21 -j ACCEPT #允许ftp服务的21端口 iptables -A INPUT -p tcp --dport 20 -j ACCEPT #允许FTP服务的20端口 iptables -A INPUT -j reject #禁止其他未允许的规则访问 iptables -A FORWARD -j REJECT #禁止其他未允许的规则访问 ``` ### 4.12. 屏蔽 IP ```shell iptables -A INPUT -p tcp -m tcp -s 192.168.0.8 -j DROP # 屏蔽恶意主机(比如,192.168.0.8 iptables -I INPUT -s 123.45.6.7 -j DROP #屏蔽单个IP的命令 iptables -I INPUT -s 123.0.0.0/8 -j DROP #封整个段即从123.0.0.1到123.255.255.254的命令 iptables -I INPUT -s 124.45.0.0/16 -j DROP #封IP段即从123.45.0.1到123.45.255.254的命令 iptables -I INPUT -s 123.45.6.0/24 -j DROP #封IP段即从123.45.6.1到123.45.6.254的命令是 ``` ### 4.13. 指定数据包出去的网络接口 只对 OUTPUT,FORWARD,POSTROUTING 三个链起作用。 ```shell iptables -A FORWARD -o eth0 ``` ### 4.14. 查看已添加的规则 ```shell iptables -L -n -v Chain INPUT (policy DROP 48106 packets, 2690K bytes) pkts bytes target prot opt in out source destination 5075 589K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 191K 90M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 1499K 133M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 4364K 6351M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 6256 327K ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 3382K packets, 1819M bytes) pkts bytes target prot opt in out source destination 5075 589K ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 ``` ### 4.15. 启动网络转发规则 公网`210.14.67.7`让内网`192.168.188.0/24`上网 ```shell iptables -t nat -A POSTROUTING -s 192.168.188.0/24 -j SNAT --to-source 210.14.67.127 ``` ### 4.16. 端口映射 本机的 2222 端口映射到内网 虚拟机的 22 端口 ```shell iptables -t nat -A PREROUTING -d 210.14.67.127 -p tcp --dport 2222 -j DNAT --to-dest 192.168.188.115:22 ``` ### 4.17. 字符串匹配 比如,我们要过滤所有 TCP 连接中的字符串`test`,一旦出现它我们就终止这个连接,我们可以这么做: ```shell iptables -A INPUT -p tcp -m string --algo kmp --string "test" -j REJECT --reject-with tcp-reset iptables -L # Chain INPUT (policy ACCEPT) # target prot opt source destination # REJECT tcp -- anywhere anywhere STRING match "test" ALGO name kmp TO 65535 reject-with tcp-reset # # Chain FORWARD (policy ACCEPT) # target prot opt source destination # # Chain OUTPUT (policy ACCEPT) # target prot opt source destination ``` ### 4.18. 阻止 Windows 蠕虫的攻击 ```shell iptables -I INPUT -j DROP -p tcp -s 0.0.0.0/0 -m string --algo kmp --string "cmd.exe" ``` ### 4.19. 防止 SYN 洪水攻击 ```shell iptables -A INPUT -p tcp --syn -m limit --limit 5/second -j ACCEPT ``` ## 5. 参考资料 - https://wiki.archlinux.org/index.php/iptables_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87) - https://wangchujiang.com/linux-command/c/iptables.html ================================================ FILE: docs/linux/ops/network-ops.md ================================================ # Linux 典型运维应用 > 💡 如果没有特殊说明,本文的案例都是针对 Centos 发行版本。 ## 网络操作 ### 无法访问外网域名 (1)在 hosts 中添加本机实际 IP 和本机实际域名的映射 ```shell echo "192.168.0.1 hostname" >> /etc/hosts ``` 如果不知道本机域名,使用 `hostname` 命令查一下;如果不知道本机实际 IP,使用 `ifconfig` 查一下。 (2)配置信赖的 DNS 服务器 执行 `vi /etc/resolv.conf` ,添加以下内容: ```shell nameserver 114.114.114.114 nameserver 8.8.8.8 ``` > 114.114.114.114 是国内老牌 DNS > > 8.8.8.8 是 Google DNS > > :point_right: 参考:[公共 DNS 哪家强](https://www.zhihu.com/question/32229915) (3)测试一下能否 ping 通 www.baidu.com ### 配置网卡 使用 root 权限编辑 `/etc/sysconfig/network-scripts/ifcfg-eno16777736X` 文件 参考以下进行配置: ```properties TYPE=Ethernet                        # 网络类型:Ethernet以太网 BOOTPROTO=none                       # 引导协议:自动获取、static静态、none不指定 DEFROUTE=yes                         # 启动默认路由 IPV4_FAILURE_FATAL=no                # 不启用IPV4错误检测功能 IPV6INIT=yes                         # 启用IPV6协议 IPV6_AUTOCONF=yes                    # 自动配置IPV6地址 IPV6_DEFROUTE=yes                    # 启用IPV6默认路由 IPV6_FAILURE_FATAL=no                # 不启用IPV6错误检测功能 IPV6_PEERDNS=yes IPV6_PEERROUTES=yes IPV6_PRIVACY="no" NAME=eno16777736                     # 网卡设备的别名(需要和文件名同名) UUID=90528772-9967-46da-b401-f82b64b4acbc # 网卡设备的UUID唯一标识号 DEVICE=eno16777736                   # 网卡的设备名称 ONBOOT=yes                           # 开机自动激活网卡 IPADDR=192.168.1.199                 # 网卡的固定IP地址 PREFIX=24                            # 子网掩码 GATEWAY=192.168.1.1                  # 默认网关IP地址 DNS1=8.8.8.8                         # DNS域名解析服务器的IP地址 ``` 修改完后,执行 `systemctl restart network.service` 重启网卡服务。 ## 系统维护 ## 自动化脚本 ### Linux 开机自启动脚本 (1)在 `/etc/rc.local` 文件中添加命令 如果不想将脚本粘来粘去,或创建链接,可以在 `/etc/rc.local` 文件中添加启动命令 1. 先修改好脚本,使其所有模块都能在任意目录启动时正常执行; 2. 再在 `/etc/rc.local` 的末尾添加一行以绝对路径启动脚本的行; 例: 执行 `vim /etc/rc.local` 命令,输入以下内容: ```shell #!/bin/sh # # This script will be executed *after* all the other init scripts. # You can put your own initialization stuff in here if you don't # want to do the full Sys V style init stuff. touch /var/lock/subsys/local /opt/pjt_test/test.pl ``` (2)在 `/etc/rc.d/init.d` 目录下添加自启动脚本 Linux 在 `/etc/rc.d/init.d` 下有很多的文件,每个文件都是可以看到内容的,其实都是一些 shell 脚本或者可执行二进制文件。 Linux 开机的时候,会加载运行 `/etc/rc.d/init.d` 目录下的程序,因此我们可以把想要自动运行的脚本放到这个目录下即可。系统服务的启动就是通过这种方式实现的。 (3)运行级别设置 简单的说,运行级就是操作系统当前正在运行的功能级别。 ```shell 不同的运行级定义如下: # 0 - 停机(千万不能把initdefault 设置为0 ) # 1 - 单用户模式   进入方法#init s = init 1 # 2 - 多用户,没有 NFS # 3 - 完全多用户模式(标准的运行级) # 4 - 没有用到 # 5 - X11 多用户图形模式(xwindow) # 6 - 重新启动 (千万不要把initdefault 设置为6 ) ``` 这些级别在 `/etc/inittab` 文件里指定,这个文件是 init 程序寻找的主要文件,最先运行的服务是放在/etc/rc.d 目录下的文件。 在 `/etc` 目录下面有这么几个目录值得注意:rcS.d rc0.d rc1.d ... rc6.d (0,1... 6 代表启动级别 0 代表停止,1 代表单用户模式,2-5 代表多用户模式,6 代表重启) 它们的作用就相当于 redhat 下的 rc.d ,你可以把脚本放到 rcS.d,然后修改文件名,给它一个启动序号,如: S88mysql 不过,最好的办法是放到相应的启动级别下面。具体作法: (1)先把脚本 mysql 放到 /etc/init.d 目录下 (2)查看当前系统的启动级别 ```shell $ runlevel N 3 ``` (3)设定启动级别 ```shell # 98 为启动序号 # 2 是系统的运行级别,可自己调整,注意不要忘了结尾的句点 $ update-rc.d mysql start 98 2 . ``` 现在我们到 /etc/rc2.d 下,就多了一个 S98mysql 这样的符号链接。 (4)重启系统,验证设置是否有效。 (5)移除符号链接 当你需要移除这个符号连接时,方法有三种: 1. 直接到 `/etc/rc2.d` 下删掉相应的链接,当然不是最好的方法; 2. 推荐做法:`update-rc.d -f s10 remove` 3. 如果 update-rc.d 命令你不熟悉,还可以试试看 rcconf 这个命令,也很方便。 > :point_right: 参考: > > - [linux 添加开机自启动脚本示例详解](https://blog.csdn.net/linuxshine/article/details/50717272) > - [linux 设置开机自启动](https://www.cnblogs.com/ssooking/p/6094740.html) ### 定时执行脚本 ## 配置 ### 设置 Linux 启动模式 1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动) 2. 单用户模式,就像 Win9X 下的安全模式 3. 多用户,但是没有 NFS 4. 完全多用户模式,准则的运行级 5. 通常不用,在一些特殊情况下可以用它来做一些事情 6. X11,即进到 X-Window 系统 7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动) 设置方法: ```shell sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab ``` ## 参考资料 - [CentOS7 使用 firewalld 打开关闭防火墙与端口](https://www.cnblogs.com/moxiaoan/p/5683743.html) - [linux 定时执行脚本](https://blog.csdn.net/z_yong_cool/article/details/79288397) ================================================ FILE: docs/linux/ops/ntp.md ================================================ # 时间服务器 - NTP ## 一、NTP 简介 网络时间协议(英语:Network Time Protocol,缩写:NTP)是在数据网络潜伏时间可变的计算机系统之间通过分组交换进行时钟同步的一个网络协议,位于 OSI 模型的应用层。自 1985 年以来,NTP 是目前仍在使用的最古老的互联网协议之一。NTP 由特拉华大学的 David L. Mills(英语:David L. Mills)设计。 **NTP 意图将所有参与计算机的协调世界时(UTC)时间同步到几毫秒的误差内**。 NTP 要点: - 地球共有 24 个时区,而以格林威治时间 (GMT) 为标准时间; - 中国本地时间为 GMT +8 小时; - 最准确的时间为使用原子钟 (Atomic clock) 所计算的,例如 UTC (Coordinated Universal Time) 就是一例; - Linux 系统本来就有两种时间,一种是 Linux 以 `1970/01/01` 开始计数的系统时间,一种则是 BIOS 记载的硬件时间; - Linux 可以透过网络校时,最常见的网络校时为使用 NTP 服务器,这个服务启动在 `udp port 123`; - 时区档案主要放置于 `/usr/share/zoneinfo/` 目录下,而本地时区则参考 `/etc/localtime`; - NTP 服务器为一种阶层式的服务,所以 NTP 服务器本来就会与上层时间服务器作时间的同步化, 因此 `nptd` 与 `ntpdate` 两个指令不可同时使用; - NTP 服务器的联机状态可以使用 `ntpstat` 及 `ntpq -p` 来查询; - NTP 提供的客户端软件为 `ntpdate` 这个指令; - 在 Linux 下想要手动处理时间时,需以 `date` 设定时间后,以 `hwclock -w` 来写入 BIOS 所记录的时间。 - NTP 服务器之间的时间误差不可超过 1000 秒,否则 NTP 服务会自动关闭。 > 更多 NTP 详情可以参考:[鸟哥的 Linux 私房菜-- NTP 时间服务器](http://cn.linux.vbird.org/linux_server/0440ntp.php) ## 二、ntpd 服务 > 环境:CentOS ### yum 安装 CentOS 安装 NTP 很简单,执行以下命令即可: ```shell yum -y install ntp ``` ### ntpd 配置 ntp 的配置文件路径为: `/etc/ntp.conf` ,参考配置: ```shell # 1. 先处理权限方面的问题,包括放行上层服务器以及开放区网用户来源: # restrict default kod nomodify notrap nopeer noquery # 拒绝 IPv4 的用户 # restrict -6 default kod nomodify notrap nopeer noquery # 拒绝 IPv6 的用户 restrict default nomodify notrap nopeer noquery #restrict 192.168.100.0 mask 255.255.255.0 nomodify # 放行同局域网来源(根据网关和子网掩码决定) restrict 127.0.0.1 # 默认值,放行本机 IPv4 来源 restrict ::1 # 默认值,放行本机 IPv6 来源 # 2. 设定 NTP 主机来源 # 注释掉默认 NTP 来源 # server 0.centos.pool.ntp.org iburst # server 1.centos.pool.ntp.org iburst # server 2.centos.pool.ntp.org iburst # server 3.centos.pool.ntp.org iburst # 设置国内 NTP 来源 server cn.pool.ntp.org prefer # 以这个主机为优先 server ntp1.aliyun.com server ntp.sjtu.edu.cn # 3. 预设时间差异分析档案与暂不用到的 keys 等,不需要更改它: driftfile /var/lib/ntp/drift keys /etc/ntp/keys includefile /etc/ntp/crypto/pw ``` > 注意:如果更改配置,必须重启 NTP 服务(`systemctl restart ntpd`)才能生效。 ### 放开防火墙限制 NTP 服务的端口是 `123`,使用的是 udp 协议,所以 NTP 服务器的防火墙必须对外开放 udp 123 这个端口。 如果防火墙使用 **`iptables`**,执行以下命令: ```shell iptables -A INPUT -p UDP -i eth0 -s 192.168.0.0/24 --dport 123 -j ACCEPT ``` 如果防火墙使用 **`firewalld`**,执行以下命令: ```shell firewall-cmd --zone=public --add-port=123/udp --permanent ``` ### ntpd 服务命令 ```shell systemctl enable ntpd.service # 开启服务(开机自动启动服务) systemctl disable ntpd.service # 关闭服务(开机不会自动启动服务) systemctl start ntpd.service # 启动服务 systemctl stop ntpd.service # 停止服务 systemctl restart ntpd.service # 重启服务 systemctl reload ntpd.service # 重新载入配置 systemctl status ntpd.service # 查看服务状态 ``` ### 查看 ntp 服务状态 #### 验证 NTP 服务正常工作 执行 `ntpstat` 可以查看 ntp 服务器有无和上层 ntp 连通,,如果成功,可以看到类似以下的内容: ```shell $ ntpstat synchronised to NTP server (5.79.108.34) at stratum 3 time correct to within 1129 ms polling server every 64 s ``` #### 查看 ntp 服务器与上层 ntp 的状态 ```shell ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================== *ntp1.ams1.nl.le 130.133.1.10 2 u 36 64 367 230.801 5.271 2.791 120.25.115.20 10.137.53.7 2 u 33 64 377 25.930 15.908 3.168 time.cloudflare 10.21.8.251 3 u 31 64 367 251.109 16.976 3.264 ``` ## 三、ntpdate 命令 > 注意:NTP 服务器为一种阶层式的服务,所以 NTP 服务器本来就会与上层时间服务器作时间的同步化, 因此 `nptd` 与 `ntpdate` 两个指令不可同时使用。 ### 手动执行时间同步 `ntpdate` 命令是 NTP 的客户端软件,它可以用于请求时间同步。 语法: ```shell /usr/sbin/ntpdate ``` `ntp_server` 可以从 [国内 NTP 服务器](#国内 NTP 服务器) 中选择。 示例: ```shell $ ntpdate cn.pool.ntp.org 11 Feb 10:47:12 ntpdate[30423]: step time server 84.16.73.33 offset -49.894774 sec ``` ### 自动定时同步时间 如果需要自动定时同步时间,可以利用 [Crontab](#crontab) 工具。本质就是用 crontab 定时执行一次手动时间同步命令 ntp。 示例:执行如下命令,就可以在每天凌晨 3 点同步系统时间: ```shell echo "0 3 * * * /usr/sbin/ntpdate cn.pool.ntp.org" >> /etc/crontab # 修改 crond 服务配置 systemctl restart crond # 重启 crond 服务以生效 ``` ## 四、国内 NTP 服务器 以下 NTP 服务器搜集自网络: ```shell cn.pool.ntp.org # 最常用的国内NTP服务器,参考:https://www.ntppool.org/zh/use.html cn.ntp.org.cn # 中国 edu.ntp.org.cn # 中国教育网 ntp1.aliyun.com # 阿里云 ntp2.aliyun.com # 阿里云 ntp.sjtu.edu.cn # 上海交通大学 s1a.time.edu.cn # 北京邮电大学 s1b.time.edu.cn # 清华大学 s1c.time.edu.cn # 北京大学 s1d.time.edu.cn # 东南大学 s1e.time.edu.cn # 清华大学 s2a.time.edu.cn # 清华大学 s2b.time.edu.cn # 清华大学 s2c.time.edu.cn # 北京邮电大学 s2d.time.edu.cn # 西南地区网络中心 s2e.time.edu.cn # 西北地区网络中心 s2f.time.edu.cn # 东北地区网络中心 s2g.time.edu.cn # 华东南地区网络中心 s2h.time.edu.cn # 四川大学网络管理中心 s2j.time.edu.cn # 大连理工大学网络中心 s2k.time.edu.cn # CERNET桂林主节点 ``` ## 参考资料 - [鸟哥的 Linux 私房菜-- NTP 时间服务器](http://cn.linux.vbird.org/linux_server/0440ntp.php) - [Linux 配置 ntp 时间服务器](https://www.cnblogs.com/quchunhui/p/7658853.html) ================================================ FILE: docs/linux/ops/samba.md ================================================ # Samba 应用 > samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的一个免费软件。 > > samba 提供了在不同计算机(即使操作系统不同)上共享服务的能力。 > > 关键词:`samba`, `selinux` ## 1. 安装配置 samba 本文将以一个完整的示例来展示如何配置 samba 来实现 Linux 和 Windows 的文件共享。 目标:假设希望共享 Linux 服务器上的 /share/fs 目录。 ### 1.1. 查看是否已经安装 samba - CentOS:`rpm -qa | grep samba` - Ubuntu:`dpkg -l | grep samba` ### 1.2. 安装 samba 工具 - CentOS:`yum install -y samba samba-client samba-common` - Ubuntu:`sudo apt-get install -y samba samba-client` ### 1.3. 配置 samba samba 服务的配置文件是 `/etc/samba/smb.conf`,如果没有则 samba 无法启动。 执行以下命令,编辑配置文件: ```bash vim /etc/samba/smb.conf ``` 修改配置如下: ```ini [global] workgroup = SAMBA security = user passdb backend = tdbsam printing = cups printcap name = cups load printers = yes cups options = raw [homes] comment = Home Directories valid users = %S, %D%w%S browseable = No read only = No inherit acls = Yes [printers] comment = All Printers path = /var/tmp printable = Yes create mask = 0600 browseable = No [print$] comment = Printer Drivers path = /var/lib/samba/drivers write list = @printadmin root force group = @printadmin create mask = 0664 directory mask = 0775 [fs] comment = share folder path = /share/fs browseable = yes writable = yes read only = no guest ok = yes create mask = 0777 directory mask = 0777 public = yes valid users = root ``` > 说明: > > - 我在这里添加了一个 **[fs]** 标签,这就是共享区域的配置。 > - 这里设置 `path` 属性为 `/share/fs`,意味着准备共享 `/share/fs` 目录,需要根据实际需要设置路径。`/share/fs` 目录的权限要设置为 **777**:`chmod 777 /share/fs`。 > - `browseable`、`writable` 等属性就比较容易理解了,即配置共享目录的访问权限。 > - `valid users` 属性指定允许访问的用户,需要注意的是指定的用户必须是 Linux 机器上实际存在的用户。 ### 1.4. 创建 samba 用户 创建的 samba 用户必须是 Linux 机器上实际存在的用户。 ```bash $ sudo smbpasswd -a root New SMB password: Retype new SMB password: Added user root. ``` 根据提示输入 samba 用户的密码。当 samba 服务成功安装、启动后,通过 Windows 系统访问机器共享目录时,就要输入这里配置的用户名、密码。 - 查看 samba 服务器中已拥有哪些用户 - `pdbedit -L` - 删除 samba 服务中的某个用户 - `smbpasswd -x 用户名` ### 1.5. 启动 samba 服务 CentOS 6 ```bash $ sudo service samba restart # 重启 samba $ sudo service smb restart # 重启 samba ``` CentOS 7 ```bash $ sudo systemctl start smb.service # 启动 samba $ sudo systemctl restart smb.service # 重启 samba $ sudo systemctl enable smb.service # 设置开机自动启动 $ sudo systemctl status smb.service # 查询 samba 状态 ``` Ubuntu 16.04.3 ``` $ sudo service smbd restart ``` ### 1.6. 为 samba 添加防火墙规则 ``` $ sudo firewall-cmd --permanent --zone=public --add-service=samba $ sudo firewall-cmd --reload ``` ### 1.7. 测试 samba 服务 ``` $ smbclient //localhost/fs -U root ``` 输入 samba 用户的密码,如果成功,就会进入 `smb: \>`。 ### 1.8. 访问 samba 服务共享的目录 Windows: 访问:`\\<你的ip>\<你的共享路径>` : ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920180928161334.png) Mac: 与 Windows 类似,直接在 Finder 中访问 `smb://<你的ip>/<你的共享路径>` 即可。 ## 2. 配置详解 ### 2.1. samba 默认配置 你可以从 [这里](https://git.samba.org/samba.git/?p=samba.git;a=blob_plain;f=examples/smb.conf.default;hb=HEAD) 获取到默认配置文件: ``` $ cp /etc/samba/smb.conf /etc/samba/smb.conf.bak $ wget "https://git.samba.org/samba.git/?p=samba.git;a=blob_plain;f=examples/smb.conf.default;hb=HEAD" -O /etc/samba/smb.conf ``` smb.conf 默认内容如下: ```ini [global] workgroup = SAMBA security = user passdb backend = tdbsam printing = cups printcap name = cups load printers = yes cups options = raw [homes] comment = Home Directories valid users = %S, %D%w%S browseable = No read only = No inherit acls = Yes [printers] comment = All Printers path = /var/tmp printable = Yes create mask = 0600 browseable = No [print$] comment = Printer Drivers path = /var/lib/samba/drivers write list = root create mask = 0664 directory mask = 0775 ``` ### 2.2. 全局参数 [global] ```ini [global] config file = /usr/local/samba/lib/smb.conf.%m 说明:config file可以让你使用另一个配置文件来覆盖缺省的配置文件。如果文件 不存在,则该项无效。这个参数很有用,可以使得samba配置更灵活,可以让一台samba服务器模拟多台不同配置的服务器。比如,你想让PC1(主机名)这台电脑在访问Samba Server时使用它自己的配置文件,那么先在/etc/samba/host/下为PC1配置一个名为smb.conf.pc1的文件,然后在smb.conf中加入:config file=/etc/samba/host/smb.conf.%m。这样当PC1请求连接Samba Server时,smb.conf.%m就被替换成smb.conf.pc1。这样,对于PC1来说,它所使用的Samba服务就是由smb.conf.pc1定义的,而其他机器访问Samba Server则还是应用smb.conf。 workgroup = WORKGROUP 说明:设定 Samba Server 所要加入的工作组或者域。 server string = Samba Server Version %v 说明:设定 Samba Server 的注释,可以是任何字符串,也可以不填。宏%v表示显示Samba的版本号。 netbios name = smbserver 说明:设置Samba Server的NetBIOS名称。如果不填,则默认会使用该服务器的DNS名称的第一部分。netbios name和workgroup名字不要设置成一样了。 interfaces = lo eth0 192.168.12.2/24 192.168.13.2/24 说明:设置Samba Server监听哪些网卡,可以写网卡名,也可以写该网卡的IP地址。 hosts allow = 127.192.168.1 192.168.10.1 说明:表示允许连接到Samba Server的客户端,多个参数以空格隔开。可以用一个IP表示,也可以用一个网段表示。hosts deny 与hosts allow 刚好相反。 例如: # 表示容许来自172.17.2.*.*的主机连接,但排除172.17.2.50 hosts allow=172.17.2.EXCEPT172.17.2.50 # 表示容许来自172.17.2.0/255.255.0.0子网中的所有主机连接 hosts allow=172.17.2.0/255.255.0.0 # 表示容许来自M1和M2两台计算机连接 hosts allow=M1,M2 # 表示容许来自SC域的所有计算机连接 hosts allow=@SC max connections = 0 说明:max connections用来指定连接Samba Server的最大连接数目。如果超出连接数目,则新的连接请求将被拒绝。0表示不限制。 deadtime = 0 说明:deadtime用来设置断掉一个没有打开任何文件的连接的时间。单位是分钟,0代表Samba Server不自动切断任何连接。 time server = yes/no 说明:time server用来设置让nmdb成为windows客户端的时间服务器。 log file = /var/log/samba/log.%m 说明:设置Samba Server日志文件的存储位置以及日志文件名称。在文件名后加个宏%m(主机名),表示对每台访问Samba Server的机器都单独记录一个日志文件。如果pc1、pc2访问过Samba Server,就会在/var/log/samba目录下留下log.pc1和log.pc2两个日志文件。 max log size = 50 说明:设置Samba Server日志文件的最大容量,单位为kB,0代表不限制。 security = user 说明:设置用户访问Samba Server的验证方式,一共有四种验证方式。 1. share:用户访问Samba Server不需要提供用户名和口令, 安全性能较低。 2. user:Samba Server共享目录只能被授权的用户访问,由Samba Server负责检查账号和密码的正确性。账号和密码要在本Samba Server中建立。 3. server:依靠其他Windows NT/2000或Samba Server来验证用户的账号和密码,是一种代理验证。此种安全模式下,系统管理员可以把所有的Windows用户和口令集中到一个NT系统上,使用Windows NT进行Samba认证, 远程服务器可以自动认证全部用户和口令,如果认证失败,Samba将使用用户级安全模式作为替代的方式。 4. domain:域安全级别,使用主域控制器(PDC)来完成认证。 passdb backend = tdbsam 说明:passdb backend就是用户后台的意思。目前有三种后台:smbpasswd、tdbsam和ldapsam。sam应该是security account manager(安全账户管理)的简写。 smbpasswd:该方式是使用smb自己的工具smbpasswd来给系统用户(真实 用户或者虚拟用户)设置一个Samba密码,客户端就用这个密码来访问Samba的资源。 1. smbpasswd文件默认在/etc/samba目录下,不过有时候要手工建立该文件。 2. tdbsam:该方式则是使用一个数据库文件来建立用户数据库。数据库文件叫passdb.tdb,默认在/etc/samba目录下。passdb.tdb用户数据库可以使用smbpasswd –a来建立Samba用户,不过要建立的Samba用户必须先是系统用户。我们也可以使用pdbedit命令来建立Samba账户。pdbedit命令的参数很多,我们列出几个主要的。 pdbedit –a username:新建Samba账户。 pdbedit –x username:删除Samba账户。 pdbedit –L:列出Samba用户列表,读取passdb.tdb数据库文件。 pdbedit –Lv:列出Samba用户列表的详细信息。 pdbedit –c “[D]” –u username:暂停该Samba用户的账号。 pdbedit –c “[]” –u username:恢复该Samba用户的账号。 3. ldapsam:该方式则是基于LDAP的账户管理方式来验证用户。首先要建立LDAP服务,然后设置“passdb backend = ldapsam:ldap://LDAP Server” encrypt passwords = yes/no 说明:是否将认证密码加密。因为现在windows操作系统都是使用加密密码,所以一般要开启此项。不过配置文件默认已开启。 smb passwd file = /etc/samba/smbpasswd 说明:用来定义samba用户的密码文件。smbpasswd文件如果没有那就要手工新建。 username map = /etc/samba/smbusers 说明:用来定义用户名映射,比如可以将root换成administrator、admin等。不过要事先在smbusers文件中定义好。比如:root = administrator admin,这样就可以用administrator或admin这两个用户来代替root登陆Samba Server,更贴近windows用户的习惯。 guest account = nobody 说明:用来设置guest用户名。 socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192 说明:用来设置服务器和客户端之间会话的Socket选项,可以优化传输速度。 domain master = yes/no 说明:设置Samba服务器是否要成为网域主浏览器,网域主浏览器可以管理跨子网域的浏览服务。 local master = yes/no 说明:local master用来指定Samba Server是否试图成为本地网域主浏览器。如果设为no,则永远不会成为本地网域主浏览器。但是即使设置为yes,也不等于该Samba Server就能成为主浏览器,还需要参加选举。 preferred master = yes/no 说明:设置Samba Server一开机就强迫进行主浏览器选举,可以提高Samba Server成为本地网域主浏览器的机会。如果该参数指定为yes时,最好把domain master也指定为yes。使用该参数时要注意:如果在本Samba Server所在的子网有其他的机器(不论是windows NT还是其他Samba Server)也指定为首要主浏览器时,那么这些机器将会因为争夺主浏览器而在网络上大发广播,影响网络性能。如果同一个区域内有多台Samba Server,将上面三个参数设定在一台即可。 os level = 200 说明:设置samba服务器的os level。该参数决定Samba Server是否有机会成为本地网域的主浏览器。os level从0到255,winNT的os level是32,win95/98的os level是1。Windows 2000的os level是64。如果设置为0,则意味着Samba Server将失去浏览选择。如果想让Samba Server成为PDC,那么将它的os level值设大些。 domain logons = yes/no 说明:设置Samba Server是否要做为本地域控制器。主域控制器和备份域控制器都需要开启此项。 logon . = %u.bat 说明:当使用者用windows客户端登陆,那么Samba将提供一个登陆档。如果设置成%u.bat,那么就要为每个用户提供一个登陆档。如果人比较多,那就比较麻烦。可以设置成一个具体的文件名,比如start.bat,那么用户登陆后都会去执行start.bat,而不用为每个用户设定一个登陆档了。这个文件要放置在[netlogon]的path设置的目录路径下。 wins support = yes/no 说明:设置samba服务器是否提供wins服务。 wins server = wins服务器IP地址 说明:设置Samba Server是否使用别的wins服务器提供wins服务。 wins proxy = yes/no 说明:设置Samba Server是否开启wins代理服务。 dns proxy = yes/no 说明:设置Samba Server是否开启dns代理服务。 load printers = yes/no 说明:设置是否在启动Samba时就共享打印机。 printcap name = cups 说明:设置共享打印机的配置文件。 printing = cups 说明:设置Samba共享打印机的类型。现在支持的打印系统有:bsd, sysv, plp, lprng, aix, hpux, qnx ``` ### 2.3. 共享参数 [共享名] ```ini [共享名] comment = 任意字符串 说明:comment是对该共享的描述,可以是任意字符串。 path = 共享目录路径 说明:path用来指定共享目录的路径。可以用%u、%m这样的宏来代替路径里的unix用户和客户机的Netbios名,用宏表示主要用于[homes]共享域。例如:如果我们不打算用home段做为客户的共享,而是在/home/share/下为每个Linux用户以他的用户名建个目录,作为他的共享目录,这样path就可以写成:path = /home/share/%u; 。用户在连接到这共享时具体的路径会被他的用户名代替,要注意这个用户名路径一定要存在,否则,客户机在访问时会找不到网络路径。同样,如果我们不是以用户来划分目录,而是以客户机来划分目录,为网络上每台可以访问samba的机器都各自建个以它的netbios名的路径,作为不同机器的共享资源,就可以这样写:path = /home/share/%m 。 browseable = yes/no 说明:browseable用来指定该共享是否可以浏览。 writable = yes/no 说明:writable用来指定该共享路径是否可写。 available = yes/no 说明:available用来指定该共享资源是否可用。 admin users = 该共享的管理者 说明:admin users用来指定该共享的管理员(对该共享具有完全控制权限)。在samba 3.0中,如果用户验证方式设置成“security=share”时,此项无效。 例如:admin users =bobyuan,jane(多个用户中间用逗号隔开)。 valid users = 允许访问该共享的用户 说明:valid users用来指定允许访问该共享资源的用户。 例如:valid users = bobyuan,@bob,@tech(多个用户或者组中间用逗号隔开,如果要加入一个组就用“@+组名”表示。) invalid users = 禁止访问该共享的用户 说明:invalid users用来指定不允许访问该共享资源的用户。 例如:invalid users = root,@bob(多个用户或者组中间用逗号隔开。) write list = 允许写入该共享的用户 说明:write list用来指定可以在该共享下写入文件的用户。 例如:write list = bobyuan,@bob public = yes/no 说明:public用来指定该共享是否允许guest账户访问。 guest ok = yes/no 说明:意义同“public”。 几个特殊共享: [homes] comment = Home Directories browseable = no writable = yes valid users = %S ; valid users = MYDOMAIN\%S [printers] comment = All Printers path = /var/spool/samba browseable = no guest ok = no writable = no printable = yes [netlogon] comment = Network Logon Service path = /var/lib/samba/netlogon guest ok = yes writable = no share modes = no [Profiles] path = /var/lib/samba/profiles browseable = no guest ok = yes ``` ## 3. 常见问题 ### 3.1. 你可能没有权限访问网络资源 问题现象: - 出现 **NT_STATUS_ACCESS_DENIED** 错误 - Windows 下成功登陆 samba 后,点击共享目录仍然提示——你可能没有权限访问网络资源。 解决步骤: 1. 检查是否配置了防火墙规则 ```bash # 一种方法是强行关闭防火墙 $ sudo service iptables stop # 另一种方法是配置防火墙规则 $ sudo firewall-cmd --permanent --zone=public --add-service=samba $ sudo firewall-cmd --reload ``` 2. 关闭 selinux ```bash # 将 /etc/selinux/config 文件中的 SELINUX 设为 disabled $ sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config # 重启生效 $ reboot ``` ### 3.2. window 下对 samba 的清理操作 1. windows 清除访问 samba 局域网密码缓存 - 在 dos 窗口中输入 `control userpasswords2` 或者 `control keymgr.dll`,然后【高级】/【密码管理】,删掉保存的该机器密码。 2. windows 清除连接的 linux 的 samba 服务缓存 1. 打开 win 的命令行。 2. 输入 net use,就会打印出当前缓存的连接上列表。 3. 根据列表,一个个删除连接: net use 远程连接名称 /del;或者一次性全部删除:`net use * /del`。 ## 4. 参考资料 - http://blog.51cto.com/yuanbin/115761 - https://www.jianshu.com/p/750be209a6f0 - https://github.com/judasn/Linux-Tutorial/blob/master/markdown-file/Samba.md - https://blog.csdn.net/lan120576664/article/details/50396511 ================================================ FILE: docs/linux/ops/systemd.md ================================================ # Systemd 应用 > 搬运自:[Systemd 入门教程:命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html)、[Systemd 入门教程:实战篇](hhttp://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) Systemd 是 Linux 系统工具,用来启动[守护进程](http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html),已成为大多数发行版的标准配置。 本文介绍它的基本用法,分为上下两篇。今天介绍它的主要命令,[下一篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html)介绍如何用于实战。 ## 1. 由来 历史上,[Linux 的启动](http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html)一直采用[`init`](https://en.wikipedia.org/wiki/Init)进程。 下面的命令用来启动服务。 ```bash $ sudo /etc/init.d/apache2 start # 或者 $ service apache2 start ``` 这种方法有两个缺点。 一是启动时间长。`init`进程是串行启动,只有前一个进程启动完,才会启动下一个进程。 二是启动脚本复杂。`init`进程只是执行启动脚本,不管其他事情。脚本需要自己处理各种 情况,这往往使得脚本变得很长。 ## 2. Systemd 概述 Systemd 就是为了解决这些问题而诞生的。它的设计目标是,为系统的启动和管理提供一套 完整的解决方案。 根据 Linux 惯例,字母`d`是守护进程(daemon)的缩写。 Systemd 这个名字的含义,就 是它要守护整个系统。 使用了 Systemd,就不需要再用`init`了。Systemd 取代了`initd`,成为系统的第一个进 程(PID 等于 1),其他进程都是它的子进程。 ```bash $ systemctl --version ``` 上面的命令查看 Systemd 的版本。 Systemd 的优点是功能强大,使用方便,缺点是体系庞大,非常复杂。事实上,现在还有很 多人反对使用 Systemd,理由就是它过于复杂,与操作系统的其他部分强耦合,违反"keep simple, keep stupid" 的[Unix 哲学](http://www.ruanyifeng.com/blog/2009/06/unix_philosophy.html)。 ![img](http://www.ruanyifeng.com/blogimg/asset/2016/bg2016030703.png) (上图为 Systemd 架构图) ## 3. 系统管理 Systemd 并不是一个命令,而是一组命令,涉及到系统管理的方方面面。 ### 3.1. systemctl `systemctl`是 Systemd 的主命令,用于管理系统。 ```bash # 重启系统 $ sudo systemctl reboot # 关闭系统,切断电源 $ sudo systemctl poweroff # CPU停止工作 $ sudo systemctl halt # 暂停系统 $ sudo systemctl suspend # 让系统进入冬眠状态 $ sudo systemctl hibernate # 让系统进入交互式休眠状态 $ sudo systemctl hybrid-sleep # 启动进入救援状态(单用户状态) $ sudo systemctl rescue ``` ### 3.2. systemd-analyze `systemd-analyze`命令用于查看启动耗时。 ```bash # 查看启动耗时 $ systemd-analyze # 查看每个服务的启动耗时 $ systemd-analyze blame # 显示瀑布状的启动过程流 $ systemd-analyze critical-chain # 显示指定服务的启动流 $ systemd-analyze critical-chain atd.service ``` ### 3.3. hostnamectl `hostnamectl`命令用于查看当前主机的信息。 ```bash # 显示当前主机的信息 $ hostnamectl # 设置主机名。 $ sudo hostnamectl set-hostname rhel7 ``` ### 3.4. localectl `localectl`命令用于查看本地化设置。 ```bash # 查看本地化设置 $ localectl # 设置本地化参数。 $ sudo localectl set-locale LANG=en_GB.utf8 $ sudo localectl set-keymap en_GB ``` ### 3.5. timedatectl `timedatectl`命令用于查看当前时区设置。 ```bash # 查看当前时区设置 $ timedatectl # 显示所有可用的时区 $ timedatectl list-timezones # 设置当前时区 $ sudo timedatectl set-timezone America/New_York $ sudo timedatectl set-time YYYY-MM-DD $ sudo timedatectl set-time HH:MM:SS ``` ### 3.6. loginctl `loginctl`命令用于查看当前登录的用户。 ```bash # 列出当前session $ loginctl list-sessions # 列出当前登录用户 $ loginctl list-users # 列出显示指定用户的信息 $ loginctl show-user ruanyf ``` ## 4. Unit ### 4.1. 含义 Systemd 可以管理所有系统资源。不同的资源统称为 Unit(单位)。 Unit 一共分成 12 种。 - Service unit:系统服务 - Target unit:多个 Unit 构成的一个组 - Device Unit:硬件设备 - Mount Unit:文件系统的挂载点 - Automount Unit:自动挂载点 - Path Unit:文件或路径 - Scope Unit:不是由 Systemd 启动的外部进程 - Slice Unit:进程组 - Snapshot Unit:Systemd 快照,可以切回某个快照 - Socket Unit:进程间通信的 socket - Swap Unit:swap 文件 - Timer Unit:定时器 `systemctl list-units`命令可以查看当前系统的所有 Unit 。 ```bash # 列出正在运行的 Unit $ systemctl list-units # 列出所有Unit,包括没有找到配置文件的或者启动失败的 $ systemctl list-units --all # 列出所有没有运行的 Unit $ systemctl list-units --all --state=inactive # 列出所有加载失败的 Unit $ systemctl list-units --failed # 列出所有正在运行的、类型为 service 的 Unit $ systemctl list-units --type=service ``` ### 4.2. Unit 的状态 `systemctl status`命令用于查看系统状态和单个 Unit 的状态。 ```bash # 显示系统状态 $ systemctl status # 显示单个 Unit 的状态 $ sysystemctl status bluetooth.service # 显示远程主机的某个 Unit 的状态 $ systemctl -H root@rhel7.example.com status httpd.service ``` 除了`status`命令,`systemctl`还提供了三个查询状态的简单方法,主要供脚本内部的判 断语句使用。 ```bash # 显示某个 Unit 是否正在运行 $ systemctl is-active application.service # 显示某个 Unit 是否处于启动失败状态 $ systemctl is-failed application.service # 显示某个 Unit 服务是否建立了启动链接 $ systemctl is-enabled application.service ``` ### 4.3. Unit 管理 对于用户来说,最常用的是下面这些命令,用于启动和停止 Unit(主要是 service)。 ```bash # 立即启动一个服务 $ sudo systemctl start apache.service # 立即停止一个服务 $ sudo systemctl stop apache.service # 重启一个服务 $ sudo systemctl restart apache.service # 杀死一个服务的所有子进程 $ sudo systemctl kill apache.service # 重新加载一个服务的配置文件 $ sudo systemctl reload apache.service # 重载所有修改过的配置文件 $ sudo systemctl daemon-reload # 显示某个 Unit 的所有底层参数 $ systemctl show httpd.service # 显示某个 Unit 的指定属性的值 $ systemctl show -p CPUShares httpd.service # 设置某个 Unit 的指定属性 $ sudo systemctl set-property httpd.service CPUShares=500 ``` ### 4.4. 依赖关系 Unit 之间存在依赖关系:A 依赖于 B,就意味着 Systemd 在启动 A 的时候,同时会去启 动 B。 `systemctl list-dependencies`命令列出一个 Unit 的所有依赖。 ```bash $ systemctl list-dependencies nginx.service ``` 上面命令的输出结果之中,有些依赖是 Target 类型(详见下文),默认不会展开显示。如 果要展开 Target,就需要使用`--all`参数。 ```bash $ systemctl list-dependencies --all nginx.service ``` ## 5. Unit 的配置文件 ### 5.1. 概述 每一个 Unit 都有一个配置文件,告诉 Systemd 怎么启动这个 Unit 。 Systemd 默认从目录`/etc/systemd/system/`读取配置文件。但是,里面存放的大部分文件 都是符号链接,指向目录`/usr/lib/systemd/system/`,真正的配置文件存放在那个目录。 `systemctl enable`命令用于在上面两个目录之间,建立符号链接关系。 ```bash $ sudo systemctl enable clamd@scan.service # 等同于 $ sudo ln -s '/usr/lib/systemd/system/clamd@scan.service' '/etc/systemd/system/multi-user.target.wants/clamd@scan.service' ``` 如果配置文件里面设置了开机启动,`systemctl enable`命令相当于激活开机启动。 与之对应的,`systemctl disable`命令用于在两个目录之间,撤销符号链接关系,相当于 撤销开机启动。 ```bash $ sudo systemctl disable clamd@scan.service ``` 配置文件的后缀名,就是该 Unit 的种类,比如`sshd.socket`。如果省略,Systemd 默认 后缀名为`.service`,所以`sshd`会被理解成`sshd.service`。 ### 5.2. 配置文件的状态 `systemctl list-unit-files`命令用于列出所有配置文件。 ```bash # 列出所有配置文件 $ systemctl list-unit-files # 列出指定类型的配置文件 $ systemctl list-unit-files --type=service ``` 这个命令会输出一个列表。 ```bash $ systemctl list-unit-files UNIT FILE STATE chronyd.service enabled clamd@.service static clamd@scan.service disabled ``` 这个列表显示每个配置文件的状态,一共有四种。 - enabled:已建立启动链接 - disabled:没建立启动链接 - static:该配置文件没有`[Install]`部分(无法执行),只能作为其他配置文件的依赖 - masked:该配置文件被禁止建立启动链接 注意,从配置文件的状态无法看出,该 Unit 是否正在运行。这必须执行前面提到 的`systemctl status`命令。 ```bash $ systemctl status bluetooth.service ``` 一旦修改配置文件,就要让 SystemD 重新加载配置文件,然后重新启动,否则修改不会生 效。 ```bash $ sudo systemctl daemon-reload $ sudo systemctl restart httpd.service ``` ### 5.3. 配置文件的格式 配置文件就是普通的文本文件,可以用文本编辑器打开。 `systemctl cat`命令可以查看配置文件的内容。 ```bash $ systemctl cat atd.service [Unit] Description=ATD daemon [Service] Type=forking ExecStart=/usr/bin/atd [Install] WantedBy=multi-user.target ``` 从上面的输出可以看到,配置文件分成几个区块。每个区块的第一行,是用方括号表示的区 别名,比如`[Unit]`。注意,配置文件的区块名和字段名,都是大小写敏感的。 每个区块内部是一些等号连接的键值对。 ```bash [Section] Directive1=value Directive2=value . . . ``` 注意,键值对的等号两侧不能有空格。 ### 5.4. 配置文件的区块 `[Unit]`区块通常是配置文件的第一个区块,用来定义 Unit 的元数据,以及配置与其他 Unit 的关系。它的主要字段如下。 - `Description`:简短描述 - `Documentation`:文档地址 - `Requires`:当前 Unit 依赖的其他 Unit,如果它们没有运行,当前 Unit 会启动失败 - `Wants`:与当前 Unit 配合的其他 Unit,如果它们没有运行,当前 Unit 不会启动失败 - `BindsTo`:与`Requires`类似,它指定的 Unit 如果退出,会导致当前 Unit 停止运行 - `Before`:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之后启动 - `After`:如果该字段指定的 Unit 也要启动,那么必须在当前 Unit 之前启动 - `Conflicts`:这里指定的 Unit 不能与当前 Unit 同时运行 - `Condition...`:当前 Unit 运行必须满足的条件,否则不会运行 - `Assert...`:当前 Unit 运行必须满足的条件,否则会报启动失败 `[Install]`通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。它 的主要字段如下。 - `WantedBy`:它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放 入`/etc/systemd/system`目录下面以 Target 名 + `.wants`后缀构成的子目录中 - `RequiredBy`:它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放 入`/etc/systemd/system`目录下面以 Target 名 + `.required`后缀构成的子目录中 - `Alias`:当前 Unit 可用于启动的别名 - `Also`:当前 Unit 激活(enable)时,会被同时激活的其他 Unit `[Service]`区块用来 Service 的配置,只有 Service 类型的 Unit 才有这个区块。它的 主要字段如下。 - `Type`:定义启动时的进程行为。它有以下几种值。 - `Type=simple`:默认值,执行`ExecStart`指定的命令,启动主进程 - `Type=forking`:以 fork 方式从父进程创建子进程,创建后父进程会立即退出 - `Type=oneshot`:一次性进程,Systemd 会等当前服务退出,再继续往下执行 - `Type=dbus`:当前服务通过 D-Bus 启动 - `Type=notify`:当前服务启动完毕,会通知`Systemd`,再继续往下执行 - `Type=idle`:若有其他任务执行完毕,当前服务才会运行 - `ExecStart`:启动当前服务的命令 - `ExecStartPre`:启动当前服务之前执行的命令 - `ExecStartPost`:启动当前服务之后执行的命令 - `ExecReload`:重启当前服务时执行的命令 - `ExecStop`:停止当前服务时执行的命令 - `ExecStopPost`:停止当其服务之后执行的命令 - `RestartSec`:自动重启当前服务间隔的秒数 - `Restart`:定义何种情况 Systemd 会自动重启当前服务,可能的值包括`always`(总是 重启)、`on-success`、`on-failure`、`on-abnormal`、`on-abort`、`on-watchdog` - `TimeoutSec`:定义 Systemd 停止当前服务之前等待的秒数 - `Environment`:指定环境变量 Unit 配置文件的完整字段清单,请参 考[官方文档](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)。 ## 6. Target 启动计算机的时候,需要启动大量的 Unit。如果每一次启动,都要一一写明本次启动需要 哪些 Unit,显然非常不方便。Systemd 的解决方案就是 Target。 简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候 ,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于"状态点 ",启动某个 Target 就好比启动到某种状态。 传统的`init`启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是 ,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动 。 ```bash # 查看当前系统的所有 Target $ systemctl list-unit-files --type=target # 查看一个 Target 包含的所有 Unit $ systemctl list-dependencies multi-user.target # 查看启动时的默认 Target $ systemctl get-default # 设置启动时的默认 Target $ sudo systemctl set-default multi-user.target # 切换 Target 时,默认不关闭前一个 Target 启动的进程, # systemctl isolate 命令改变这种行为, # 关闭前一个 Target 里面所有不属于后一个 Target 的进程 $ sudo systemctl isolate multi-user.target ``` Target 与 传统 RunLevel 的对应关系如下。 ```bash Traditional runlevel New target name Symbolically linked to... Runlevel 0 | runlevel0.target -> poweroff.target Runlevel 1 | runlevel1.target -> rescue.target Runlevel 2 | runlevel2.target -> multi-user.target Runlevel 3 | runlevel3.target -> multi-user.target Runlevel 4 | runlevel4.target -> multi-user.target Runlevel 5 | runlevel5.target -> graphical.target Runlevel 6 | runlevel6.target -> reboot.target ``` 它与`init`进程的主要差别如下。 **(1)默认的 RunLevel**(在`/etc/inittab`文件设置)现在被默认的 Target 取代, 位置是`/etc/systemd/system/default.target`,通常符号链接到`graphical.target`( 图形界面)或者`multi-user.target`(多用户命令行)。 **(2)启动脚本的位置**,以前是`/etc/init.d`目录,符号链接到不同的 RunLevel 目 录 (比如`/etc/rc3.d`、`/etc/rc5.d`等),现在则存放 在`/lib/systemd/system`和`/etc/systemd/system`目录。 **(3)配置文件的位置**,以前`init`进程的配置文件是`/etc/inittab`,各种服务的 配置文件存放在`/etc/sysconfig`目录。现在的配置文件主要存放在`/lib/systemd`目录 ,在`/etc/systemd`目录里面的修改可以覆盖原始设置。 ## 7. 日志管理 Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用`journalctl`一个命 令,查看所有日志(内核日志和应用日志)。日志的配置文件 是`/etc/systemd/journald.conf`。 `journalctl`功能强大,用法非常多。 ```bash # 查看所有日志(默认情况下 ,只保存本次启动的日志) $ sudo journalctl # 查看内核日志(不显示应用日志) $ sudo journalctl -k # 查看系统本次启动的日志 $ sudo journalctl -b $ sudo journalctl -b -0 # 查看上一次启动的日志(需更改设置) $ sudo journalctl -b -1 # 查看指定时间的日志 $ sudo journalctl --since="2012-10-30 18:17:16" $ sudo journalctl --since "20 min ago" $ sudo journalctl --since yesterday $ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00" $ sudo journalctl --since 09:00 --until "1 hour ago" # 显示尾部的最新10行日志 $ sudo journalctl -n # 显示尾部指定行数的日志 $ sudo journalctl -n 20 # 实时滚动显示最新日志 $ sudo journalctl -f # 查看指定服务的日志 $ sudo journalctl /usr/lib/systemd/systemd # 查看指定进程的日志 $ sudo journalctl _PID=1 # 查看某个路径的脚本的日志 $ sudo journalctl /usr/bin/bash # 查看指定用户的日志 $ sudo journalctl _UID=33 --since today # 查看某个 Unit 的日志 $ sudo journalctl -u nginx.service $ sudo journalctl -u nginx.service --since today # 实时滚动显示某个 Unit 的最新日志 $ sudo journalctl -u nginx.service -f # 合并显示多个 Unit 的日志 $ journalctl -u nginx.service -u php-fpm.service --since today # 查看指定优先级(及其以上级别)的日志,共有8级 # 0: emerg # 1: alert # 2: crit # 3: err # 4: warning # 5: notice # 6: info # 7: debug $ sudo journalctl -p err -b # 日志默认分页输出,--no-pager 改为正常的标准输出 $ sudo journalctl --no-pager # 以 JSON 格式(单行)输出 $ sudo journalctl -b -u nginx.service -o json # 以 JSON 格式(多行)输出,可读性更好 $ sudo journalctl -b -u nginx.serviceqq -o json-pretty # 显示日志占据的硬盘空间 $ sudo journalctl --disk-usage # 指定日志文件占据的最大空间 $ sudo journalctl --vacuum-size=1G # 指定日志文件保存多久 $ sudo journalctl --vacuum-time=1years ``` ## 8. 实战 ### 8.1. 开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在`/usr/lib/systemd/system`目录添 加一个配置文件。 如果你想让该软件开机启动,就执行下面的命令(以`httpd.service`为例)。 ```bash $ sudo systemctl enable httpd ``` 上面的命令相当于在`/etc/systemd/system`目录添加一个符号链接,指 向`/usr/lib/systemd/system`里面的`httpd.service`文件。 这是因为开机时,`Systemd`只执行`/etc/systemd/system`目录里面的配置文件。这也意味 着,如果把修改后的配置文件放在该目录,就可以达到覆盖原始配置的效果。 ### 8.2. 启动服务 设置开机启动以后,软件并不会立即启动,必须等到下一次开机。如果想现在就运行该软件 ,那么要执行`systemctl start`命令。 ```bash $ sudo systemctl start httpd ``` 执行上面的命令以后,有可能启动失败,因此要用`systemctl status`命令查看一下该服务 的状态。 ```bash $ sudo systemctl status httpd httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled) Active: active (running) since 金 2014-12-05 12:18:22 JST; 7min ago Main PID: 4349 (httpd) Status: "Total requests: 1; Current requests/sec: 0; Current traffic: 0 B/sec" CGroup: /system.slice/httpd.service ├─4349 /usr/sbin/httpd -DFOREGROUND ├─4350 /usr/sbin/httpd -DFOREGROUND ├─4351 /usr/sbin/httpd -DFOREGROUND ├─4352 /usr/sbin/httpd -DFOREGROUND ├─4353 /usr/sbin/httpd -DFOREGROUND └─4354 /usr/sbin/httpd -DFOREGROUND 12月 05 12:18:22 localhost.localdomain systemd[1]: Starting The Apache HTTP Server... 12月 05 12:18:22 localhost.localdomain systemd[1]: Started The Apache HTTP Server. 12月 05 12:22:40 localhost.localdomain systemd[1]: Started The Apache HTTP Server. ``` 上面的输出结果含义如下。 - `Loaded`行:配置文件的位置,是否设为开机启动 - `Active`行:表示正在运行 - `Main PID`行:主进程 ID - `Status`行:由应用本身(这里是 httpd )提供的软件当前状态 - `CGroup`块:应用的所有子进程 - 日志块:应用的日志 ### 8.3. 停止服务 终止正在运行的服务,需要执行`systemctl stop`命令。 ```bash $ sudo systemctl stop httpd.service ``` 有时候,该命令可能没有响应,服务停不下来。这时候就不得不"杀进程"了,向正在运行的 进程发出`kill`信号。 ```bash $ sudo systemctl kill httpd.service ``` 此外,重启服务要执行`systemctl restart`命令。 ```bash $ sudo systemctl restart httpd.service ``` ### 8.4. 读懂配置文件 一个服务怎么启动,完全由它的配置文件决定。下面就来看,配置文件有些什么内容。 前面说过,配置文件主要放在`/usr/lib/systemd/system`目录,也可能 在`/etc/systemd/system`目录。找到配置文件以后,使用文本编辑器打开即可。 `systemctl cat`命令可以用来查看配置文件,下面以`sshd.service`文件为例,它的作用 是启动一个 SSH 服务器,供其他用户以 SSH 方式登录。 ``` $ systemctl cat sshd.service [Unit] Description=OpenSSH server daemon Documentation=man:sshd(8) man:sshd_config(5) After=network.target sshd-keygen.service Wants=sshd-keygen.service [Service] EnvironmentFile=/etc/sysconfig/sshd ExecStart=/usr/sbin/sshd -D $OPTIONS ExecReload=/bin/kill -HUP $MAINPID Type=simple KillMode=process Restart=on-failure RestartSec=42s [Install] WantedBy=multi-user.target ``` 可以看到,配置文件分成几个区块,每个区块包含若干条键值对。 下面依次解释每个区块的内容。 ### 8.5. [Unit] 区块:启动顺序与依赖关系。 `Unit`区块的`Description`字段给出当前服务的简单描述,`Documentation`字段给出文档 位置。 接下来的设置是启动顺序和依赖关系,这个比较重要。 > `After`字段:表示如果`network.target`或`sshd-keygen.service`需要启动,那 > 么`sshd.service`应该在它们之后启动。 相应地,还有一个`Before`字段,定义`sshd.service`应该在哪些服务之前启动。 注意,`After`和`Before`字段只涉及启动顺序,不涉及依赖关系。 举例来说,某 Web 应用需要 postgresql 数据库储存数据。在配置文件中,它只定义要在 postgresql 之后启动,而没有定义依赖 postgresql 。上线后,由于某种原因 ,postgresql 需要重新启动,在停止服务期间,该 Web 应用就会无法建立数据库连接。 设置依赖关系,需要使用`Wants`字段和`Requires`字段。 > `Wants`字段:表示`sshd.service`与`sshd-keygen.service`之间存在"弱依赖"关系,即 > 如果"sshd-keygen.service"启动失败或停止运行,不影响`sshd.service`继续执行。 `Requires`字段则表示"强依赖"关系,即如果该服务启动失败或异常退出,那 么`sshd.service`也必须退出。 注意,`Wants`字段与`Requires`字段只涉及依赖关系,与启动顺序无关,默认情况下是同 时启动的。 ### 8.6. [Service] 区块:启动行为 `Service`区块定义如何启动当前服务。 #### 8.6.1. 启动命令 许多软件都有自己的环境参数文件,该文件可以用`EnvironmentFile`字段读取。 > `EnvironmentFile`字段:指定当前服务的环境参数文件。该文件内部的`key=value`键值 > 对,可以用`$key`的形式,在当前配置文件中获取。 上面的例子中,sshd 的环境参数文件是`/etc/sysconfig/sshd`。 配置文件里面最重要的字段是`ExecStart`。 > `ExecStart`字段:定义启动进程时执行的命令。 上面的例子中,启动`sshd`,执行的命令是`/usr/sbin/sshd -D $OPTIONS`,其中的变 量`$OPTIONS`就来自`EnvironmentFile`字段指定的环境参数文件。 与之作用相似的,还有如下这些字段。 - `ExecReload`字段:重启服务时执行的命令 - `ExecStop`字段:停止服务时执行的命令 - `ExecStartPre`字段:启动服务之前执行的命令 - `ExecStartPost`字段:启动服务之后执行的命令 - `ExecStopPost`字段:停止服务之后执行的命令 请看下面的例子。 ``` [Service] ExecStart=/bin/echo execstart1 ExecStart= ExecStart=/bin/echo execstart2 ExecStartPost=/bin/echo post1 ExecStartPost=/bin/echo post2 ``` 上面这个配置文件,第二行`ExecStart`设为空值,等于取消了第一行的设置,运行结果如 下。 ``` execstart2 post1 post2 ``` 所有的启动设置之前,都可以加上一个连词号(`-`),表示"抑制错误",即发生错误的时 候,不影响其他命令的执行。比如,`EnvironmentFile=-/etc/sysconfig/sshd`(注意等号 后面的那个连词号),就表示即使`/etc/sysconfig/sshd`文件不存在,也不会抛出错误。 #### 8.6.2. 启动类型 `Type`字段定义启动类型。它可以设置的值如下。 - simple(默认值):`ExecStart`字段启动的进程为主进程 - forking:`ExecStart`字段将以`fork()`方式启动,此时父进程将会退出,子进程将成 为主进程 - oneshot:类似于`simple`,但只执行一次,Systemd 会等它执行完,才启动其他服务 - dbus:类似于`simple`,但会等待 D-Bus 信号后启动 - notify:类似于`simple`,启动结束后会发出通知信号,然后 Systemd 再启动其他服 务 - idle:类似于`simple`,但是要等到其他任务都执行完,才会启动该服务。一种使用场 合是为让该服务的输出,不与其他服务的输出相混合 下面是一个`oneshot`的例子,笔记本电脑启动时,要把触摸板关掉,配置文件可以这样写 。 ``` [Unit] Description=Switch-off Touchpad [Service] Type=oneshot ExecStart=/usr/bin/touchpad-off [Install] WantedBy=multi-user.target ``` 上面的配置文件,启动类型设为`oneshot`,就表明这个服务只要运行一次就够了,不需要 长期运行。 如果关闭以后,将来某个时候还想打开,配置文件修改如下。 ``` [Unit] Description=Switch-off Touchpad [Service] Type=oneshot ExecStart=/usr/bin/touchpad-off start ExecStop=/usr/bin/touchpad-off stop RemainAfterExit=yes [Install] WantedBy=multi-user.target ``` 上面配置文件中,`RemainAfterExit`字段设为`yes`,表示进程退出以后,服务仍然保持执 行。这样的话,一旦使用`systemctl stop`命令停止服务,`ExecStop`指定的命令就会执行 ,从而重新开启触摸板。 #### 8.6.3. 重启行为 `Service`区块有一些字段,定义了重启行为。 > `KillMode`字段:定义 Systemd 如何停止 sshd 服务。 上面这个例子中,将`KillMode`设为`process`,表示只停止主进程,不停止任何 sshd 子 进程,即子进程打开的 SSH session 仍然保持连接。这个设置不太常见,但对 sshd 很重 要,否则你停止服务的时候,会连自己打开的 SSH session 一起杀掉。 `KillMode`字段可以设置的值如下。 - control-group(默认值):当前控制组里面的所有子进程,都会被杀掉 - process:只杀主进程 - mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号 - none:没有进程会被杀掉,只是执行服务的 stop 命令。 接下来是`Restart`字段。 > `Restart`字段:定义了 sshd 退出后,Systemd 的重启方式。 上面的例子中,`Restart`设为`on-failure`,表示任何意外的失败,就将重启 sshd。如果 sshd 正常停止(比如执行`systemctl stop`命令),它就不会重启。 `Restart`字段可以设置的值如下。 - no(默认值):退出后不会重启 - on-success:只有正常退出时(退出状态码为 0),才会重启 - on-failure:非正常退出时(退出状态码非 0),包括被信号终止和超时,才会重启 - on-abnormal:只有被信号终止和超时,才会重启 - on-abort:只有在收到没有捕捉到的信号终止时,才会重启 - on-watchdog:超时退出,才会重启 - always:不管是什么退出原因,总是重启 对于守护进程,推荐设为`on-failure`。对于那些允许发生错误退出的服务,可以设 为`on-abnormal`。 最后是`RestartSec`字段。 > `RestartSec`字段:表示 Systemd 重启服务之前,需要等待的秒数。上面的例子设为等 > 待 42 秒。 ### 8.7. [Install] 区块 `Install`区块,定义如何安装这个配置文件,即怎样做到开机启动。 `WantedBy`字段:表示该服务所在的 Target。 `Target`的含义是服务组,表示一组服务。`WantedBy=multi-user.target`指的是,sshd 所在的 Target 是`multi-user.target`。 这个设置非常重要,因为执行`systemctl enable sshd.service`命令时 ,`sshd.service`的一个符号链接,就会放在`/etc/systemd/system`目录下面 的`multi-user.target.wants`子目录之中。 Systemd 有默认的启动 Target。 ```bash $ systemctl get-default multi-user.target ``` 上面的结果表示,默认的启动 Target 是`multi-user.target`。在这个组里的所有服务, 都将开机启动。这就是为什么`systemctl enable`命令能设置开机启动的原因。 使用 Target 的时候,`systemctl list-dependencies`命令和`systemctl isolate`命令也 很有用。 ```bash # 查看 multi-user.target 包含的所有服务 $ systemctl list-dependencies multi-user.target # 切换到另一个 target # shutdown.target 就是关机状态 $ sudo systemctl isolate shutdown.target ``` 一般来说,常用的 Target 有两个:一个是`multi-user.target`,表示多用户命令行状态 ;另一个是`graphical.target`,表示图形用户状态,它依赖于`multi-user.target`。官 方文档有一张非常清晰的 [Target 依赖关系图](https://www.freedesktop.org/software/systemd/man/bootup.html#System%20Manager%20Bootup)。 ### 8.8. Target 的配置文件 Target 也有自己的配置文件。 ```bash $ systemctl cat multi-user.target [Unit] Description=Multi-User System Documentation=man:systemd.special(7) Requires=basic.target Conflicts=rescue.service rescue.target After=basic.target rescue.service rescue.target AllowIsolate=yes ``` 注意,Target 配置文件里面没有启动命令。 上面输出结果中,主要字段含义如下。 - `Requires`字段:要求`basic.target`一起运行。 - `Conflicts`字段:冲突字段。如果`rescue.service`或`rescue.target`正在运行 ,`multi-user.target`就不能运行,反之亦然。 - `After`:表示`multi-user.target`在`basic.target` 、 `rescue.service`、 `rescue.target`之后启动,如果它们有启动的话。 - `AllowIsolate`:允许使用`systemctl isolate`命令切换到`multi-user.target`。 ### 8.9. 修改配置文件后重启 修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。 ```bash # 重新加载配置文件 $ sudo systemctl daemon-reload # 重启相关服务 $ sudo systemctl restart foobar ``` ## 9. 参考资料 - [Systemd 入门教程:命令篇](http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html) - [Systemd 入门教程:实战篇](hhttp://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html) ================================================ FILE: docs/linux/ops/vim.md ================================================ # Vim 应用 ## 1. 概念 ### 1.1. 什么是 vim Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。和 Emacs 并列成为类 Unix 系统用户最喜欢的编辑器。 ### 1.2. Vim 的模式 基本上 vi/vim 共分为三种模式,分别是**命令模式(Command mode)**,**插入模式(Insert mode)**和**底线命令模式(Last line mode)**。 #### 1.2.1. 命令模式 **用户刚刚启动 vi/vim,便进入了命令模式。** 此状态下敲击键盘动作会被 Vim 识别为命令,而非输入字符。 #### 1.2.2. 插入模式 **在命令模式下按下 `i` 就进入了输入模式。** 在输入模式下,你可以输入文本内容。 #### 1.2.3. 底线命令模式 **在命令模式下按下 `:`(英文冒号)就进入了底线命令模式。** 底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。 ## 2. Vim 渐进学习 ### 2.1. 存活 1. 安装 [vim](http://www.vim.org/) 2. 启动 vim 3. **什么也别干!**请先阅读 当你安装好一个编辑器后,你一定会想在其中输入点什么东西,然后看看这个编辑器是什么样子。但 vim 不是这样的,请按照下面的命令操作: - 启 动 Vim 后,vim 在 _Normal_ 模式下。 - 让我们进入 _Insert_ 模式,请按下键 i 。(注:你会看到 vim 左下角有一个–insert–字样,表示,你可以以插入的方式输入了) - 此时,你可以输入文本了,就像你用“记事本”一样。 - 如果你想返回 _Normal_ 模式,请按 `ESC` 键。 现在,你知道如何在 _Insert_ 和 _Normal_ 模式下切换了。下面是一些命令,可以让你在 _Normal_ 模式下幸存下来: > - `i` → _Insert_ 模式,按 `ESC` 回到 _Normal_ 模式. > - `x` → 删当前光标所在的一个字符。 > - `:wq` → 存盘 + 退出 (`:w` 存盘, `:q` 退出) (注::w 后可以跟文件名) > - `dd` → 删除当前行,并把删除的行存到剪贴板里 > - `p` → 粘贴剪贴板 > > **推荐** > > - `hjkl` (强例推荐使用其移动光标,但不必需) → 你也可以使用光标键 (←↓↑→). 注: `j` 就像下箭头。 > - `:help ` → 显示相关命令的帮助。你也可以就输入 `:help` 而不跟命令。(注:退出帮助需要输入:q) 你能在 vim 幸存下来只需要上述的那 5 个命令,你就可以编辑文本了,你一定要把这些命令练成一种下意识的状态。于是你就可以开始进阶到第二级了。 当是,在你进入第二级时,需要再说一下 _Normal_ 模式。在一般的编辑器下,当你需要 copy 一段文字的时候,你需要使用 `Ctrl` 键,比如:`Ctrl-C`。也就是说,`Ctrl` 键就好像功能键一样,当你按下了功能键 `Ctrl` 后,C 就不在是 C 了,而且就是一个命令或是一个快键键了,**在 vim 的 Normal 模式下,所有的键都是功能键**。这个你需要知道。 > **标记** > > - 下面的文字中,如果是 `Ctrl-λ`我会写成 ``. > - 以 `:` 开始的命令你需要输入 ``回车,例如 — 如果我写成 `:q` 也就是说你要输入 `:q`. ### 2.2. 感觉良好 上面的那些命令只能让你存活下来,现在是时候学习一些更多的命令了,下面是我的建议:(注:所有的命令都需要在 Normal 模式下使用,如果你不知道现在在什么样的模式,你就狂按几次 ESC 键) 1. 各种插入模式 > - `a` → 在光标后插入 > - `o` → 在当前行后插入一个新行 > - `O` → 在当前行前插入一个新行 > - `cw` → 替换从光标所在位置后到一个单词结尾的字符 2. 简单的移动光标 > - `0` → 数字零,到行头 > - `^` → 到本行第一个不是 blank 字符的位置(所谓 blank 字符就是空格,tab,换行,回车等) > - `$` → 到本行行尾 > - `g_` → 到本行最后一个不是 blank 字符的位置。 > - `/pattern` → 搜索 `pattern` 的字符串(注:如果搜索出多个匹配,可按 n 键到下一个) 3. 拷贝/粘贴 (注:p/P 都可以,p 是表示在当前位置之后,P 表示在当前位置之前) > - `P` → 粘贴 > - `yy` → 拷贝当前行当行于 `ddP` 4. Undo/Redo > - `u` → undo > - `` → redo 5. 打开/保存/退出/改变文件 (Buffer) > - `:e ` → 打开一个文件 > - `:w` → 存盘 > - `:saveas ` → 另存为 `` > - `:x`, `ZZ` 或 `:wq` → 保存并退出 (`:x` 表示仅在需要时保存,ZZ 不需要输入冒号并回车) > - `:q!` → 退出不保存 `:qa!` 强行退出所有的正在编辑的文件,就算别的文件有更改。 > - `:bn` 和 `:bp` → 你可以同时打开很多文件,使用这两个命令来切换下一个或上一个文件。(注:我喜欢使用:n 到下一个文件) 花点时间熟悉一下上面的命令,一旦你掌握他们了,你就几乎可以干其它编辑器都能干的事了。但是到现在为止,你还是觉得使用 vim 还是有点笨拙,不过没关系,你可以进阶到第三级了。 ### 2.3. 更好,更强,更快 先恭喜你!你干的很不错。我们可以开始一些更为有趣的事了。在第三级,我们只谈那些和 vi 可以兼容的命令。 #### 2.3.1. 更好 下面,让我们看一下 vim 是怎么重复自己的:1515G 1. `.` → (小数点) 可以重复上一次的命令 2. `N` → 重复某个命令 N 次 下面是一个示例,找开一个文件你可以试试下面的命令: > - `2dd` → 删除 2 行 > - `3p` → 粘贴文本 3 次 > - `100idesu [ESC]` → 会写下 “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “ > - `.` → 重复上一个命令—— 100 “desu “. > - `3.` → 重复 3 次 “desu” (注意:不是 300,你看,VIM 多聪明啊). #### 2.3.2. 更强 你要让你的光标移动更有效率,你一定要了解下面的这些命令,**千万别跳过**。 1. N`G` → 到第 N 行 (注:注意命令中的 G 是大写的,另我一般使用 : N 到第 N 行,如 :137 到第 137 行) 2. `gg` → 到第一行。(注:相当于 1G,或 :1) 3. `G` → 到最后一行。 4. 按单词移动: > 1. `w` → 到下一个单词的开头。 > 2. `e` → 到下一个单词的结尾。 > > \> 如果你认为单词是由默认方式,那么就用小写的 e 和 w。默认上来说,一个单词由字母,数字和下划线组成(注:程序变量) > > \> 如果你认为单词是由 blank 字符分隔符,那么你需要使用大写的 E 和 W。(注:程序语句) > > ![img](http://upload-images.jianshu.io/upload_images/3101171-46f752c581d79057.jpg) 下面,让我来说说最强的光标移动: > - `%` : 匹配括号移动,包括 `(`, `{`, `[`. (注:你需要把光标先移到括号上) > - `*` 和 `#`: 匹配光标当前所在的单词,移动光标到下一个(或上一个)匹配单词(\*是下一个,#是上一个) 相信我,上面这三个命令对程序员来说是相当强大的。 #### 2.3.3. 更快 你一定要记住光标的移动,因为很多命令都可以和这些移动光标的命令连动。很多命令都可以如下来干: `` 例如 `0y$` 命令意味着: - `0` → 先到行头 - `y` → 从这里开始拷贝 - `$` → 拷贝到本行最后一个字符 你可可以输入 `ye`,从当前位置拷贝到本单词的最后一个字符。 你也可以输入 `y2/foo` 来拷贝 2 个 “foo” 之间的字符串。 还有很多时间并不一定你就一定要按 y 才会拷贝,下面的命令也会被拷贝: - `d` (删除 ) - `v` (可视化的选择) - `gU` (变大写) - `gu` (变小写) - 等等 (注:可视化选择是一个很有意思的命令,你可以先按 v,然后移动光标,你就会看到文本被选择,然后,你可能 d,也可 y,也可以变大写等) ### 2.4. Vim 超能力 你只需要掌握前面的命令,你就可以很舒服的使用 VIM 了。但是,现在,我们向你介绍的是 VIM 杀手级的功能。下面这些功能是我只用 vim 的原因。 #### 2.4.1. 在当前行上移动光标: `0` `^` `####`f`F`t`T`,``;` > - `0` → 到行头 > - `^` → 到本行的第一个非 blank 字符 > - `$` → 到行尾 > - `g_` → 到本行最后一个不是 blank 字符的位置。 > - `fa` → 到下一个为 a 的字符处,你也可以 fs 到下一个为 s 的字符。 > - `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。 > - `3fa` → 在当前行查找第三个出现的 a。 > - `F` 和 `T` → 和 `f` 和 `t` 一样,只不过是相反方向。 > ![img](http://upload-images.jianshu.io/upload_images/3101171-00835b8316330c58.jpg) 还有一个很有用的命令是 `dt"` → 删除所有的内容,直到遇到双引号—— `"。` #### 2.4.2. 区域选择 `a` 或 `i` 在 visual 模式下,这些命令很强大,其命令格式为 `a` 和 `i` - action 可以是任何的命令,如 `d` (删除), `y` (拷贝), `v` (可以视模式选择)。 - object 可能是: `w` 一个单词, `W` 一个以空格为分隔的单词, `s` 一个句字, `p` 一个段落。也可以是一个特别的字符:`"、` `'、` `)、` `}、` `]。` 假设你有一个字符串 `(map (+) ("foo"))`.而光标键在第一个 `o`的位置。 > - `vi"` → 会选择 `foo`. > - `va"` → 会选择 `"foo"`. > - `vi)` → 会选择 `"foo"`. > - `va)` → 会选择`("foo")`. > - `v2i)` → 会选择 `map (+) ("foo")` > - `v2a)` → 会选择 `(map (+) ("foo"))` ![img](http://upload-images.jianshu.io/upload_images/3101171-0b109d66a6111c83.png) #### 2.4.3. 块操作: `` 块操作,典型的操作: `0 I-- [ESC]` - `^` → 到行头 - `` → 开始块操作 - `` → 向下移动 (你也可以使用 hjkl 来移动光标,或是使用%,或是别的) - `I-- [ESC]` → I 是插入,插入“`--`”,按 ESC 键来为每一行生效。 ![img](http://upload-images.jianshu.io/upload_images/3101171-8b093a0f65707949.gif?imageMogr2/auto-orient/strip) 在 Windows 下的 vim,你需要使用 `` 而不是 `` ,`` 是拷贝剪贴板。 #### 2.4.4. 自动提示: `` 和 `` 在 Insert 模式下,你可以输入一个词的开头,然后按 `或是,自动补齐功能就出现了……` ![img](http://upload-images.jianshu.io/upload_images/3101171-e2ae877e67880ff7.gif?imageMogr2/auto-orient/strip) #### 2.4.5. 宏录制: `qa` 操作序列 `q`, `@a`, `@@` - `qa` 把你的操作记录在寄存器 `a。` - 于是 `@a` 会 replay 被录制的宏。 - `@@` 是一个快捷键用来 replay 最新录制的宏。 > **示例** > > 在一个只有一行且这一行只有“1”的文本中,键入如下命令: > > - ``` > qaYpq > ``` > > → > > - `qa` 开始录制 > - `Yp` 复制行. > - `` 增加 1. > - `q` 停止录制. > > - `@a` → 在 1 下面写下 2 > > - `@@` → 在 2 正面写下 3 > > - 现在做 `100@@` 会创建新的 100 行,并把数据增加到 103. ![img](http://upload-images.jianshu.io/upload_images/3101171-f1889f8bca723964.gif?imageMogr2/auto-orient/strip) #### 2.4.6. 可视化选择: `v`,`V`,`` 前面,我们看到了 ``的示例 (在 Windows 下应该是),我们可以使用 `v` 和 `V`。一但被选好了,你可以做下面的事: - `J` → 把所有的行连接起来(变成一行) - `<` 或 `>` → 左右缩进 - `=` → 自动给缩进 (注:这个功能相当强大,我太喜欢了) ![img](http://upload-images.jianshu.io/upload_images/3101171-fe1e19983fca213f.gif?imageMogr2/auto-orient/strip) 在所有被选择的行后加上点东西: - `` - 选中相关的行 (可使用 `j` 或 `` 或是 `/pattern` 或是 `%` 等……) - `$` 到行最后 - `A`, 输入字符串,按 `ESC。` ![img](http://upload-images.jianshu.io/upload_images/3101171-b192601247334c4e.gif?imageMogr2/auto-orient/strip) #### 2.4.7. 分屏: `:split` 和 `vsplit`. 下面是主要的命令,你可以使用 VIM 的帮助 `:help split`. 你可以参考本站以前的一篇文章[VIM 分屏](https://coolshell.cn/articles/1679.html)。 > - `:split` → 创建分屏 (`:vsplit`创建垂直分屏) > - `` : dir 就是方向,可以是 `hjkl` 或是 ←↓↑→ 中的一个,其用来切换分屏。 > - `_` (或 `|`) : 最大化尺寸 (| 垂直分屏) > - `+` (或 `-`) : 增加尺寸 ![img](http://upload-images.jianshu.io/upload_images/3101171-f329d01e299cb366.gif?imageMogr2/auto-orient/strip) ## 3. Vim Cheat Sheet > 本节内容的原文地址:[http://cenalulu.github.io/linux/all-vim-cheatsheat/](http://cenalulu.github.io/linux/all-vim-cheatsheat/) ### 3.1. 经典版 下面这个键位图应该是大家最常看见的经典版了。其实这个版本是一系列的入门教程键位图的组合结果。要查看不同编辑模式下的键位图,可以看[这里打包下载](http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html) 此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。 ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet.png) ### 3.2. 入门版 基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。 ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/basic-vim-cheat-sheet.png) ### 3.3. 进阶版 下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等 ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png) ### 3.4. 增强版 下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/) ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-02.png) ### 3.5. 文字版 [原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf) ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-text-01.png) ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/os/linux/vim/vim-cheat-sheet-text-02.png) ## 4. 资料 - [简明 VIM 练级攻略](https://coolshell.cn/articles/5426.html) ,Vim 渐进学习内容来源于这篇文章,作为 Vim 新手,我觉得入门效果很好。 - [vim 官方文档](https://vim.sourceforge.io/docs.php) - [vim-galore](https://github.com/mhinz/vim-galore) - [Vim 入门基础](http://www.jianshu.com/p/bcbe916f97e1) ================================================ FILE: docs/linux/ops/zsh.md ================================================ # oh-my-zsh 应用 ## 1. Zsh 简介 ### 1.1. Zsh 是什么 使用 Linux 的人都知道:***Shell_ 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。_Shell_ 既是一种命令语言,又是一种程序设计语言**。 Shell 的类型有很多种,linux 下默认的是 bash,虽然 bash 的功能已经很强大,但对于以懒惰为美德的程序员来说,bash 的提示功能不够强大,界面也不够炫,并非理想工具。 [**Zsh**](http://www.zsh.org/) 也是一种 Shell(据传说 99% 的 Bash 操作 和 Zsh 是相同的),它的功能极其强大,只是配置过于复杂,起初只有极客才在用。后来,出现了一个名叫 [**oh-my-zsh**](https://github.com/robbyrussell/oh-my-zsh) 的开源项目,使用 zsh 就变得十分简易了。 ## 2. Zsh 安装 ### 2.1. 环境要求 - CentOS 6.7 64 bit - root 用户 ### 2.2. 安装 zsh - 先看下你的 CentOS 支持哪些 shell:`cat /etc/shells`,正常结果应该是这样的: ```bash /bin/sh /bin/bash /sbin/nologin /bin/dash /bin/tcsh /bin/csh ``` 如果已经有 zsh ,那么我们就不必安装了。 - CentOS 安装:`sudo yum install -y zsh` - Ubuntu 安装:`sudo apt-get install -y zsh` - 检查系统的 shell:`cat /etc/shells`,你会发现多了一个:`/bin/zsh` ### 2.3. 安装 oh-my-zsh 使用 [**Zsh**](http://www.zsh.org/),怎么能离开灵魂伴侣 [**oh-my-zsh**](https://github.com/robbyrussell/oh-my-zsh)? ```bash # 安装 oh-my-zsh wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh ``` ### 2.4. 配置 oh-my-zsh #### 2.4.1. 插件 > oh-my-zsh 插件太多,不一一列举,请参考:[oh-my-zsh 插件列表](https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins) - 启用 oh-my-zsh 中自带的插件。 - 查看 oh-my-zsh 插件数:`ls -l /root/.oh-my-zsh/plugins |grep "^d"|wc -l` - 编辑配置文件:`vim /root/.zshrc` - 插件推荐: - [`zsh-autosuggestions`](https://github.com/zsh-users/zsh-autosuggestions) - 这个插件会对历史命令一些补全,类似 fish 终端 - 安装,复制该命令:`git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-\~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions` - 编辑:`vim \~/.zshrc`,找到这一行,后括号里面的后面添加:`plugins=( 前面的一些插件名称,换行,加上:zsh-autosuggestions)` - 刷新下配置:`source \~/.zshrc` - extract - 功能强大的解压插件,所有类型的文件解压一个命令 x 全搞定,再也不需要去记 tar 后面到底是哪几个参数了。 - z - 强大的目录自动跳转命令,会记忆你曾经进入过的目录,用模糊匹配快速进入你想要的目录。 - [`zsh-syntax-highlighting`](https://github.com/zsh-users/zsh-syntax-highlighting) - 这个插件会对终端命令高亮显示,比如正确的拼写会是绿色标识,否则是红色,另外对于一些 shell 输出语句也会有高亮显示,算是不错的辅助插件 - 安装,复制该命令:`git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-\~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting` - 编辑:`vim \~/.zshrc`,找到这一行,后括号里面的后面添加:`plugins=( 前面的一些插件名称,换行,加上:zsh-syntax-highlighting)` - 刷新下配置:`source \~/.zshrc` - [`wd`](https://github.com/mfaerevaag/wd) - 简单地讲就是给指定目录映射一个全局的名字,以后方便直接跳转到这个目录,比如: - 编辑配置文件,添加上 wd 的名字:`vim /root/.zshrc` - 我常去目录:**/opt/setups**,每次进入该目录下都需要这样:`cd /opt/setups` - 现在用 wd 给他映射一个快捷方式:`cd /opt/setups ; wd add setups` - 以后我在任何目录下只要运行:`wd setups` 就自动跑到 /opt/setups 目录下了 - [`autojump`](https://github.com/wting/autojump) - 这个插件会记录你常去的那些目录,然后做一下权重记录,你可以用这个命令看到你的习惯:`j --stat`,如果这个里面有你的记录,那你就只要敲最后一个文件夹名字即可进入,比如我个人习惯的 program:`j program`,就可以直接到:`/usr/program` - 插件下载:`wget https://github.com/downloads/wting/autojump/autojump_v21.1.2.tar.gz` - 解压:`tar zxvf autojump_v21.1.2.tar.gz` - 进入解压后目录并安装:`cd autojump_v21.1.2/ ; ./install.sh` - 再执行下这个:`source /etc/profile.d/autojump.sh` - 编辑配置文件,添加上 autojump 的名字:`vim /root/.zshrc` #### 2.4.2. 主题 > oh-my-zsh 主题太多,不一一列举,请参考:[oh-my-zsh 主题列表](https://github.com/robbyrussell/oh-my-zsh/wiki/Themes) - 查看 oh-my-zsh 主题数:`ls -l /root/.oh-my-zsh/themes |grep "^-"|wc -l` - 个人比较推荐的是(排名有先后): - `ys` - `agnoster` - `avit` - `blinks` - 编辑配置文件:`vim /root/.zshrc` - 配置好新主题需要重新连接 shell 才能看到效果 zsh 效果如下: ![img](https://cloud.githubusercontent.com/assets/2618447/6316862/70f58fb6-ba03-11e4-82c9-c083bf9a6574.png) ## 3. 快捷键 - 呃,这个其实可以不用讲的,你自己用的时候你自己会发现的,各种便捷,特别是用 Tab 多的人一定会有各种惊喜的。 - 使用 ctrl-r 来搜索命令历史记录。按完此快捷键后,可以输入关键命令词语,如果历史记录有含有此词语会显示出来。 - 命令别名: - 在命令行中输入 alias 可以查看已经有的命令别名 - 自己新增一些别名,编辑文件:`vim \~/.zshrc`,在文件加入下面格式的命令,比如以下是网友提供的一些思路: ```shell alias cls='clear' alias ll='ls -l' alias la='ls -a' alias grep="grep --color=auto" alias -s html='vim' # 在命令行直接输入后缀为 html 的文件名,会在 Vim 中打开 alias -s rb='vim' # 在命令行直接输入 ruby 文件,会在 Vim 中打开 alias -s py='vim' # 在命令行直接输入 python 文件,会用 vim 中打开,以下类似 alias -s js='vim' alias -s c='vim' alias -s java='vim' alias -s txt='vim' alias -s gz='tar -xzvf' # 在命令行直接输入后缀为 gz 的文件名,会自动解压打开 alias -s tgz='tar -xzvf' alias -s zip='unzip' alias -s bz2='tar -xjvf' ``` ## 4. 参考资料 - [oh-my-zsh Github](https://github.com/robbyrussell/oh-my-zsh) ================================================ FILE: docs/linux/soft/README.md ================================================ # 软件安装配置 ## 📖 内容 - 开发环境 - [JDK 安装](jdk-install.md) - [Maven 安装](maven-install.md) - [Nodejs 安装](nodejs-install.md) - 开发工具 - [Nexus 运维](nexus-ops.md) - [Gitlab 运维](gitlab-ops.md) - [Jenkins 运维](jenkins-ops.md) - [Svn 运维](svn-ops.md) - [YApi 运维](yapi-ops.md) - 中间件服务 - [Elastic 运维](elastic) - [RocketMQ 运维](rocketmq-install.md) - [Nacos 运维](nacos-install.md) - 服务器 - [Nginx 教程](https://github.com/dunwu/nginx-tutorial) 📚 - [Tomcat 运维](tomcat-install.md) - [数据库](https://github.com/dunwu/db-tutorial) 📚 - [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md) - [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md) - 大数据服务 - [Kafka 运维](kafka-install.md) - [Zookeeper 运维](https://github.com/dunwu/javatech/blob/master/docs/technology/monitor/zookeeper-ops.md) ## 🚪 传送门 ◾ 💧 [钝悟的 IT 知识图谱](https://dunwu.github.io/waterdrop/) ◾ 🎯 [钝悟的博客](https://dunwu.github.io/blog/) ◾ ================================================ FILE: docs/linux/soft/apollo/README.md ================================================ # Apollo > Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。 官方 Github:https://github.com/ctripcorp/apollo 由于官方示例不能直接使用,所以我 Fork 后,略作修改:https://github.com/dunwu/apollo ================================================ FILE: docs/linux/soft/elastic/README.md ================================================ # Elastic 技术栈 > **Elastic 技术栈通常被用来作为日志中心。** > > ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/products/elasticsearch) 、[Logstash](https://www.elastic.co/products/logstash) 、[Kibana](https://www.elastic.co/products/kibana) 的首字母组合。 > > [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 [Lucene](http://lucene.apache.org/core/documentation.html) 构建的开源,分布式,RESTful 搜索引擎。 > > [Logstash](https://www.elastic.co/products/logstash) 传输和处理你的日志、事务或其他数据。 > > [Kibana](https://www.elastic.co/products/kibana) 将 Elasticsearch 的数据分析并渲染为可视化的报表。 > > Elastic 技术栈,在 ELK 的基础上扩展了一些新的产品,如:[Beats](https://www.elastic.co/products/beats) 、[X-Pack](https://www.elastic.co/products/x-pack) 。 ## 目录 [Elastic 技术栈之入门指南](elastic-quickstart.md) [Elastic 技术栈之 Logstash 基础](elastic-logstash.md) ## 资源 **官方资源** [Elastic 官方文档](https://www.elastic.co/guide/index.html) **第三方工具** [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) **教程** [Elasticsearch 权威指南(中文版)](https://es.xiaoleilu.com/index.html) [ELK Stack权威指南](https://github.com/chenryn/logstash-best-practice-cn) **博文** [Elasticsearch+Logstash+Kibana教程](https://www.cnblogs.com/xing901022/p/4704319.html) [ELK(Elasticsearch、Logstash、Kibana)安装和配置](https://github.com/judasn/Linux-Tutorial/blob/master/ELK-Install-And-Settings.md) ================================================ FILE: docs/linux/soft/elastic/elastic-beats.md ================================================ --- title: Elastic 技术栈之 Filebeat date: 2017-01-03 categories: - javatool tags: - java - javatool - log - elastic --- # Elastic 技术栈之 Filebeat ## 简介 Beats 是安装在服务器上的数据中转代理。 Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。 ![img](https://www.elastic.co/guide/en/beats/libbeat/current/images/beats-platform.png) Beats 有多种类型,可以根据实际应用需要选择合适的类型。 常用的类型有: - **Packetbeat:**网络数据包分析器,提供有关您的应用程序服务器之间交换的事务的信息。 - **Filebeat:**从您的服务器发送日志文件。 - **Metricbeat:**是一个服务器监视代理程序,它定期从服务器上运行的操作系统和服务收集指标。 - **Winlogbeat:**提供Windows事件日志。 > **参考** > > 更多 Beats 类型可以参考:[community-beats](https://www.elastic.co/guide/en/beats/libbeat/current/community-beats.html) > > **说明** > > 由于本人工作中只应用了 FileBeat,所以后面内容仅介绍 FileBeat 。 ### FileBeat 的作用 相比 Logstash,FileBeat 更加轻量化。 在任何环境下,应用程序都有停机的可能性。 Filebeat 读取并转发日志行,如果中断,则会记住所有事件恢复联机状态时所在位置。 Filebeat带有内部模块(auditd,Apache,Nginx,System和MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。 FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。 ![img](https://www.elastic.co/guide/en/beats/filebeat/current/images/filebeat.png) ## 安装 Unix / Linux 系统建议使用下面方式安装,因为比较通用。 ``` wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.1.1-linux-x86_64.tar.gz tar -zxf filebeat-6.1.1-linux-x86_64.tar.gz ``` > **参考** > > 更多内容可以参考:[filebeat-installation](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html) ## 配置 ### 配置文件 首先,需要知道的是:`filebeat.yml` 是 filebeat 的配置文件。配置文件的路径会因为你安装方式的不同而变化。 Beat 所有系列产品的配置文件都基于 [YAML](http://www.yaml.org/) 格式,FileBeat 当然也不例外。 filebeat.yml 部分配置示例: ```yaml filebeat: prospectors: - type: log paths: - /var/log/*.log multiline: pattern: '^[' match: after ``` > **参考** > > 更多 filebeat 配置内容可以参考:[配置 filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-howto-filebeat.html) > > 更多 filebeat.yml 文件格式内容可以参考:[filebeat.yml 文件格式](https://www.elastic.co/guide/en/beats/libbeat/6.1/config-file-format.html) ### 重要配置项 #### filebeat.prospectors (文件监视器)用于指定需要关注的文件。 **示例** ```yaml filebeat.prospectors: - type: log enabled: true paths: - /var/log/*.log ``` #### output.elasticsearch 如果你希望使用 filebeat 直接向 elasticsearch 输出数据,需要配置 output.elasticsearch 。 **示例** ```yaml output.elasticsearch: hosts: ["192.168.1.42:9200"] ``` #### output.logstash 如果你希望使用 filebeat 向 logstash输出数据,然后由 logstash 再向elasticsearch 输出数据,需要配置 output.logstash。 > **注意** > > 相比于向 elasticsearch 输出数据,个人更推荐向 logstash 输出数据。 > > 因为 logstash 和 filebeat 一起工作时,如果 logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。这样,可以减少管道超负荷的情况。 **示例** ```yaml output.logstash: hosts: ["127.0.0.1:5044"] ``` 此外,还需要在 logstash 的配置文件(如 logstash.conf)中指定 beats input 插件: ```yaml input { beats { port => 5044 # 此端口需要与 filebeat.yml 中的端口相同 } } # The filter part of this file is commented out to indicate that it is # optional. # filter { # # } output { elasticsearch { hosts => "localhost:9200" manage_template => false index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" document_type => "%{[@metadata][type]}" } } ``` #### setup.kibana 如果打算使用 Filebeat 提供的 Kibana 仪表板,需要配置 setup.kibana 。 **示例** ```yaml setup.kibana: host: "localhost:5601" ``` #### setup.template.settings 在 Elasticsearch 中,[索引模板](https://www.elastic.co/guide/en/elasticsearch/reference/6.1/indices-templates.html)用于定义设置和映射,以确定如何分析字段。 在 Filebeat 中,setup.template.settings 用于配置索引模板。 Filebeat 推荐的索引模板文件由 Filebeat 软件包安装。如果您接受 filebeat.yml 配置文件中的默认配置,Filebeat在成功连接到 Elasticsearch 后自动加载模板。 您可以通过在 Filebeat 配置文件中配置模板加载选项来禁用自动模板加载,或加载自己的模板。您还可以设置选项来更改索引和索引模板的名称。 > **参考** > > 更多内容可以参考:[filebeat-template](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-template.html) > > **说明** > > 如无必要,使用 Filebeat 配置文件中的默认索引模板即可。 #### setup.dashboards Filebeat 附带了示例 Kibana 仪表板。在使用仪表板之前,您需要创建索引模式 `filebeat- *`,并将仪表板加载到Kibana 中。为此,您可以运行 `setup` 命令或在 `filebeat.yml` 配置文件中配置仪表板加载。 为了在 Kibana 中加载 Filebeat 的仪表盘,需要在 `filebeat.yml` 配置中启动开关: ``` setup.dashboards.enabled: true ``` > **参考** > > 更多内容可以参考:[configuration-dashboards](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-dashboards.html) > ## 命令 filebeat 提供了一系列命令来完成各种功能。 执行命令方式: ```bash ./filebeat COMMAND ``` > **参考** > > 更多内容可以参考:[command-line-options](https://www.elastic.co/guide/en/beats/filebeat/current/command-line-options.html) > > **说明** > > 个人认为命令行没有必要一一掌握,因为绝大部分功能都可以通过配置来完成。且通过命令行指定功能这种方式要求每次输入同样参数,不利于固化启动方式。 > > 最重要的当然是启动命令 run 了。 > > **示例** 指定配置文件启动 > > ```bash > ./filebeat run -e -c filebeat.yml -d "publish" > ./filebeat -e -c filebeat.yml -d "publish" # run 可以省略 > ``` ## 模块 Filebeat 提供了一套预构建的模块,让您可以快速实施和部署日志监视解决方案,并附带示例仪表板和数据可视化。这些模块支持常见的日志格式,例如Nginx,Apache2和MySQL 等。 ### 运行模块的步骤 - 配置 elasticsearch 和 kibana ``` output.elasticsearch: hosts: ["myEShost:9200"] username: "elastic" password: "elastic" setup.kibana: host: "mykibanahost:5601" username: "elastic" password: "elastic ``` > username 和 password 是可选的,如果不需要认证则不填。 - 初始化环境 执行下面命令,filebeat 会加载推荐索引模板。 ``` ./filebeat setup -e ``` - 指定模块 执行下面命令,指定希望加载的模块。 ``` ./filebeat -e --modules system,nginx,mysql ``` > **参考** > > 更多内容可以参考: [配置 filebeat 模块](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-modules.html) | [filebeat 支持模块](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html) ## 原理 Filebeat 有两个主要组件: harvester:负责读取一个文件的内容。它会逐行读取文件内容,并将内容发送到输出目的地。 prospector:负责管理 harvester 并找到所有需要读取的文件源。比如类型是日志,prospector 就会遍历制定路径下的所有匹配要求的文件。 ```yaml filebeat.prospectors: - type: log paths: - /var/log/*.log - /var/path2/*.log ``` Filebeat保持每个文件的状态,并经常刷新注册表文件中的磁盘状态。状态用于记住 harvester 正在读取的最后偏移量,并确保发送所有日志行。 Filebeat 将每个事件的传递状态存储在注册表文件中。所以它能保证事件至少传递一次到配置的输出,没有数据丢失。 ## 资料 [Beats 官方文档](https://www.elastic.co/guide/en/beats/libbeat/current/index.html) ================================================ FILE: docs/linux/soft/elastic/elastic-kibana.md ================================================ # Elastic 技术栈之 Kibana ## Discover 单击侧面导航栏中的 `Discover` ,可以显示 `Kibana` 的数据查询功能功能。 ![img](https://www.elastic.co/guide/en/kibana/current/images/tutorial-discover.png) 在搜索栏中,您可以输入Elasticsearch查询条件来搜索您的数据。您可以在 `Discover` 页面中浏览结果并在 `Visualize` 页面中创建已保存搜索条件的可视化。 当前索引模式显示在查询栏下方。索引模式确定提交查询时搜索哪些索引。要搜索一组不同的索引,请从下拉菜单中选择不同的模式。要添加索引模式(index pattern),请转至 `Management/Kibana/Index Patterns` 并单击 `Add New`。 您可以使用字段名称和您感兴趣的值构建搜索。对于数字字段,可以使用比较运算符,如大于(>),小于(<)或等于(=)。您可以将元素与逻辑运算符 `AND`,`OR` 和 `NOT` 链接,全部使用大写。 默认情况下,每个匹配文档都显示所有字段。要选择要显示的文档字段,请将鼠标悬停在“可用字段”列表上,然后单击要包含的每个字段旁边的添加按钮。例如,如果只添加account_number,则显示将更改为包含五个帐号的简单列表: ![img](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-discover-3.png) ### 查询语义 kibana 的搜索栏遵循 [query-string-syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax) 文档中所说明的查询语义。 这里说明一些最基本的查询语义。 查询字符串会被解析为一系列的术语和运算符。一个术语可以是一个单词(如:quick、brown)或用双引号包围的短语(如"quick brown")。 查询操作允许您自定义搜索 - 下面介绍了可用的选项。 #### 字段名称 正如查询字符串查询中所述,将在搜索条件中搜索default_field,但可以在查询语法中指定其他字段: 例如: * 查询 `status` 字段中包含 `active` 关键字 ``` status:active ``` * `title` 字段包含 `quick` 或 `brown` 关键字。如果您省略 `OR` 运算符,则将使用默认运算符 ``` title:(quick OR brown) title:(quick brown) ``` * author 字段查找精确的短语 "john smith",即精确查找。 ``` author:"John Smith" ``` * 任意字段 `book.title`,`book.content` 或 `book.date` 都包含 `quick` 或 `brown`(注意我们需要如何使用 `\*` 表示通配符) ``` book.\*:(quick brown) ``` * title 字段包含任意非 null 值 ``` _exists_:title ``` #### 通配符 ELK 提供了 ? 和 * 两个通配符。 * `?` 表示任意单个字符; * `*` 表示任意零个或多个字符。 ``` qu?ck bro* ``` > **注意:通配符查询会使用大量的内存并且执行性能较为糟糕,所以请慎用。** > **提示**:纯通配符 \* 被写入 [exsits](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html) 查询,从而提高了查询效率。因此,通配符 `field:*` 将匹配包含空值的文档,如:```{“field”:“”}```,但是如果字段丢失或显示将值置为null则不匹配,如:```“field”:null}``` > **提示**:在一个单词的开头(例如:`*ing`)使用通配符这种方式的查询量特别大,因为索引中的所有术语都需要检查,以防万一匹配。通过将 `allow_leading_wildcard` 设置为 `false`,可以禁用。 #### 正则表达式 可以通过 `/` 将正则表达式包裹在查询字符串中进行查询 例: ``` name:/joh?n(ath[oa]n)/ ``` 支持的正则表达式语义可以参考:[Regular expression syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#regexp-syntax) #### 模糊查询 我们可以使用 `~` 运算符来进行模糊查询。 例: 假设我们实际想查询 ``` quick brown forks ``` 但是,由于拼写错误,我们的查询关键字变成如下情况,依然可以查到想要的结果。 ``` quikc\~ brwn\~ foks\~ ``` 这种模糊查询使用 Damerau-Levenshtein 距离来查找所有匹配最多两个更改的项。所谓的更改是指单个字符的插入,删除或替换,或者两个相邻字符的换位。 默认编辑距离为 `2`,但编辑距离为 `1` 应足以捕捉所有人类拼写错误的80%。它可以被指定为: ``` quikc\~1 ``` #### 近似检索 尽管短语查询(例如,`john smith`)期望所有的词条都是完全相同的顺序,但是近似查询允许指定的单词进一步分开或以不同的顺序排列。与模糊查询可以为单词中的字符指定最大编辑距离一样,近似搜索也允许我们指定短语中单词的最大编辑距离: 例 ``` "fox quick"\~5 ``` 字段中的文本越接近查询字符串中指定的原始顺序,该文档就越被认为是相关的。当与上面的示例查询相比时,短语 `"quick fox"` 将被认为比 `"quick brown fox"` 更近似查询条件。 #### 范围 可以为日期,数字或字符串字段指定范围。闭区间范围用方括号 `[min TO max]` 和开区间范围用花括号 `{min TO max}` 来指定。 我们不妨来看一些示例。 * 2012 年的所有日子 ``` date:[2012-01-01 TO 2012-12-31] ``` * 数字 1 到 5 ``` count:[1 TO 5] ``` * 在 `alpha` 和 `omega` 之间的标签,不包括 `alpha` 和 `omega` ``` tag:{alpha TO omega} ``` * 10 以上的数字 ``` count:[10 TO *] ``` * 2012 年以前的所有日期 ``` date:{* TO 2012-01-01} ``` 此外,开区间和闭区间也可以组合使用 * 数组 1 到 5,但不包括 5 ``` count:[1 TO 5} ``` 一边无界的范围也可以使用以下语法: ``` age:>10 age:>=10 age:<10 age:<=10 ``` 当然,你也可以使用 AND 运算符来得到连个查询结果的交集 ``` age:(>=10 AND <20) age:(+>=10 +<20) ``` #### Boosting 使用操作符 `^` 使一个术语比另一个术语更相关。例如,如果我们想查找所有有关狐狸的文档,但我们对狐狸特别感兴趣: ``` quick^2 fox ``` 默认提升值是1,但可以是任何正浮点数。 0到1之间的提升减少了相关性。 增强也可以应用于短语或组: ``` "john smith"^2 (foo bar)^4 ``` #### 布尔操作 默认情况下,只要一个词匹配,所有词都是可选的。搜索 `foo bar baz` 将查找包含 `foo` 或 `bar` 或 `baz` 中的一个或多个的任何文档。我们已经讨论了上面的`default_operator`,它允许你强制要求所有的项,但也有布尔运算符可以在查询字符串本身中使用,以提供更多的控制。 首选的操作符是 `+`(此术语必须存在)和 `-` (此术语不得存在)。所有其他条款是可选的。例如,这个查询: ``` quick brown +fox -news ``` 这条查询意味着: * fox 必须存在 * news 必须不存在 * quick 和 brown 是可有可无的 熟悉的运算符 `AND`,`OR` 和 `NOT`(也写成 `&&`,`||` 和 `!`)也被支持。然而,这些操作符有一定的优先级:`NOT` 优先于 `AND`,`AND` 优先于 `OR`。虽然 `+` 和 `-` 仅影响运算符右侧的术语,但 `AND` 和 `OR` 会影响左侧和右侧的术语。 #### 分组 多个术语或子句可以用圆括号组合在一起,形成子查询 ``` (quick OR brown) AND fox ``` 可以使用组来定位特定的字段,或者增强子查询的结果: ``` status:(active OR pending) title:(full text search)^2 ``` #### 保留字 如果你需要使用任何在你的查询本身中作为操作符的字符(而不是作为操作符),那么你应该用一个反斜杠来转义它们。例如,要搜索(1 + 1)= 2,您需要将查询写为 `\(1\+1\)\=2` 保留字符是:`+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /` 无法正确地转义这些特殊字符可能会导致语法错误,从而阻止您的查询运行。 #### 空查询 如果查询字符串为空或仅包含空格,则查询将生成一个空的结果集。 ## Visualize 要想使用可视化的方式展示您的数据,请单击侧面导航栏中的 `Visualize`。 Visualize工具使您能够以多种方式(如饼图、柱状图、曲线图、分布图等)查看数据。要开始使用,请点击蓝色的 `Create a visualization` 或 `+` 按钮。 ![https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-landing.png](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-landing.png) 有许多可视化类型可供选择。 ![https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-wizard-step-1.png](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-wizard-step-1.png) 下面,我们来看创建几个图标示例: ### Pie 您可以从保存的搜索中构建可视化文件,也可以输入新的搜索条件。要输入新的搜索条件,首先需要选择一个索引模式来指定要搜索的索引。 默认搜索匹配所有文档。最初,一个“切片”包含整个饼图: ![https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-pie-1.png](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-visualize-pie-1.png) 要指定在图表中展示哪些数据,请使用Elasticsearch存储桶聚合。分组汇总只是将与您的搜索条件相匹配的文档分类到不同的分类中,也称为分组。 为每个范围定义一个存储桶: 1. 单击 `Split Slices`。 2. 在 `Aggregation` 列表中选择 `Terms`。_注意:这里的 Terms 是 Elk 采集数据时定义好的字段或标签。_ 3. 在 `Field` 列表中选择 `level.keyword`。 4. 点击 ![images/apply-changes-button.png](https://www.elastic.co/guide/en/kibana/6.1/images/apply-changes-button.png) 按钮来更新图表。 ![image.png](https://upload-images.jianshu.io/upload_images/3101171-7fb2042dc6d59520.png) 完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。 ### Vertical Bar 我们在展示一下如何创建柱状图。 1. 点击蓝色的 `Create a visualization` 或 `+` 按钮。选择 `Vertical Bar` 2. 选择索引模式。由于您尚未定义任何 bucket ,因此您会看到一个大栏,显示与默认通配符查询匹配的文档总数。 3. 指定 Y 轴所代表的字段 4. 指定 X 轴所代表的字段 5. 点击 ![images/apply-changes-button.png](https://www.elastic.co/guide/en/kibana/6.1/images/apply-changes-button.png) 按钮来更新图表。 ![image.png](https://upload-images.jianshu.io/upload_images/3101171-5aa7627284c19a56.png) 完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。 ## Dashboard `Dashboard` 可以整合和共享 `Visualize` 集合。 1. 点击侧面导航栏中的 Dashboard。 2. 点击添加显示保存的可视化列表。 3. 点击之前保存的 `Visualize`,然后点击列表底部的小向上箭头关闭可视化列表。 4. 将鼠标悬停在可视化对象上会显示允许您编辑,移动,删除和调整可视化对象大小的容器控件。 ================================================ FILE: docs/linux/soft/elastic/elastic-logstash.md ================================================ --- title: Elastic 技术栈之 Logstash 基础 date: 2017-12-26 categories: - javatool tags: - java - javatool - log - elastic --- # Elastic 技术栈之 Logstash 基础 > 本文是 Elastic 技术栈(ELK)的 Logstash 应用。 > > 如果不了解 Elastic 的安装、配置、部署,可以参考:[Elastic 技术栈之快速入门](https://github.com/dunwu/JavaStack/blob/master/docs/javatool/elastic/elastic-quickstart.md) ## 简介 Logstash 可以传输和处理你的日志、事务或其他数据。 ### 功能 Logstash 是 Elasticsearch 的最佳数据管道。 Logstash 是插件式管理模式,在输入、过滤、输出以及编码过程中都可以使用插件进行定制。Logstash 社区有超过 200 种可用插件。 ### 工作原理 Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`filter`。 这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。 ![img](https://www.elastic.co/guide/en/logstash/current/static/images/basic_logstash_pipeline.png) - input 负责从数据源采集数据。 - filter 将数据修改为你指定的格式或内容。 - output 将数据传输到目的地。 在实际应用场景中,通常输入、输出、过滤器不止一个。Logstash 的这三个元素都使用插件式管理方式,用户可以根据应用需要,灵活的选用各阶段需要的插件,并组合使用。 后面将对插件展开讲解,暂且不表。 ## 设置 ### 设置文件 - **`logstash.yml`**:logstash 的默认启动配置文件 - **`jvm.options`**:logstash 的 JVM 配置文件。 - **`startup.options`** (Linux):包含系统安装脚本在 `/usr/share/logstash/bin` 中使用的选项为您的系统构建适当的启动脚本。安装 Logstash 软件包时,系统安装脚本将在安装过程结束时执行,并使用 `startup.options` 中指定的设置来设置用户,组,服务名称和服务描述等选项。 ### logstash.yml 设置项 节选部分设置项,更多项请参考:https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html | 参数 | 描述 | 默认值 | | -------------------------- | ---------------------------------------- | ---------------------------------------- | | `node.name` | 节点名 | 机器的主机名 | | `path.data` | Logstash及其插件用于任何持久性需求的目录。 | `LOGSTASH_HOME/data` | | `pipeline.workers` | 同时执行管道的过滤器和输出阶段的工作任务数量。如果发现事件正在备份,或CPU未饱和,请考虑增加此数字以更好地利用机器处理能力。 | Number of the host’s CPU cores | | `pipeline.batch.size` | 尝试执行过滤器和输出之前,单个工作线程从输入收集的最大事件数量。较大的批量处理大小一般来说效率更高,但是以增加的内存开销为代价。您可能必须通过设置 `LS_HEAP_SIZE` 变量来有效使用该选项来增加JVM堆大小。 | `125` | | `pipeline.batch.delay` | 创建管道事件批处理时,在将一个尺寸过小的批次发送给管道工作任务之前,等待每个事件需要多长时间(毫秒)。 | `5` | | `pipeline.unsafe_shutdown` | 如果设置为true,则即使在内存中仍存在inflight事件时,也会强制Logstash在关闭期间退出。默认情况下,Logstash将拒绝退出,直到所有接收到的事件都被推送到输出。启用此选项可能会导致关机期间数据丢失。 | `false` | | `path.config` | 主管道的Logstash配置路径。如果您指定一个目录或通配符,配置文件将按字母顺序从目录中读取。 | Platform-specific. See [[dir-layout\]](https://github.com/elastic/logstash/blob/6.1/docs/static/settings-file.asciidoc#dir-layout). | | `config.string` | 包含用于主管道的管道配置的字符串。使用与配置文件相同的语法。 | None | | `config.test_and_exit` | 设置为true时,检查配置是否有效,然后退出。请注意,使用此设置不会检查grok模式的正确性。 Logstash可以从目录中读取多个配置文件。如果将此设置与log.level:debug结合使用,则Logstash将记录组合的配置文件,并注掉其源文件的配置块。 | `false` | | `config.reload.automatic` | 设置为true时,定期检查配置是否已更改,并在配置更改时重新加载配置。这也可以通过SIGHUP信号手动触发。 | `false` | | `config.reload.interval` | Logstash 检查配置文件更改的时间间隔。 | `3s` | | `config.debug` | 设置为true时,将完全编译的配置显示为调试日志消息。您还必须设置`log.level:debug`。警告:日志消息将包括任何传递给插件配置作为明文的“密码”选项,并可能导致明文密码出现在您的日志! | `false` | | `config.support_escapes` | 当设置为true时,带引号的字符串将处理转义字符。 | `false` | | `modules` | 配置时,模块必须处于上表所述的嵌套YAML结构中。 | None | | `http.host` | 绑定地址 | `"127.0.0.1"` | | `http.port` | 绑定端口 | `9600` | | `log.level` | 日志级别。有效选项:fatal > error > warn > info > debug > trace | `info` | | `log.format` | 日志格式。json (JSON 格式)或 plain (原对象) | `plain` | | `path.logs` | Logstash 自身日志的存储路径 | `LOGSTASH_HOME/logs` | | `path.plugins` | 在哪里可以找到自定义的插件。您可以多次指定此设置以包含多个路径。 | | ## 启动 ### 命令行 通过命令行启动 logstash 的方式如下: ``` bin/logstash [options] ``` 其中 [options] 是您可以指定用于控制 Logstash 执行的命令行标志。 在命令行上设置的任何标志都会覆盖 Logstash 设置文件(`logstash.yml`)中的相应设置,但设置文件本身不会更改。 > **注** > > 虽然可以通过指定命令行参数的方式,来控制 logstash 的运行方式,但显然这么做很麻烦。 > > 建议通过指定配置文件的方式,来控制 logstash 运行,启动命令如下: > > ``` > bin/logstash -f logstash.conf > ``` > 若想了解更多的命令行参数细节,请参考:https://www.elastic.co/guide/en/logstash/current/running-logstash-command-line.html > ### 配置文件 上节,我们了解到,logstash 可以执行 `bin/logstash -f logstash.conf` ,按照配置文件中的参数去覆盖默认设置文件(`logstash.yml`)中的设置。 这节,我们就来学习一下这个配置文件如何配置参数。 #### 配置文件结构 在工作原理一节中,我们已经知道了 Logstash 主要有三个工作阶段 input 、filter、output。而 logstash 配置文件文件结构也与之相对应: ``` input {} filter {} output {} ``` > 每个部分都包含一个或多个插件的配置选项。如果指定了多个过滤器,则会按照它们在配置文件中的显示顺序应用它们。 #### 插件配置 插件的配置由插件名称和插件的一个设置块组成。 下面的例子中配置了两个输入文件配置: ``` input { file { path => "/var/log/messages" type => "syslog" } file { path => "/var/log/apache/access.log" type => "apache" } } ``` 您可以配置的设置因插件类型而异。你可以参考: [Input Plugins](https://www.elastic.co/guide/en/logstash/current/input-plugins.html), [Output Plugins](https://www.elastic.co/guide/en/logstash/current/output-plugins.html), [Filter Plugins](https://www.elastic.co/guide/en/logstash/current/filter-plugins.html), 和 [Codec Plugins](https://www.elastic.co/guide/en/logstash/current/codec-plugins.html) 。 #### 值类型 一个插件可以要求设置的值是一个特定的类型,比如布尔值,列表或哈希值。以下值类型受支持。 - Array ``` users => [ {id => 1, name => bob}, {id => 2, name => jane} ] ``` - Lists ``` path => [ "/var/log/messages", "/var/log/*.log" ] uris => [ "http://elastic.co", "http://example.net" ] ``` - Boolean ``` ssl_enable => true ``` - Bytes ``` my_bytes => "1113" # 1113 bytes my_bytes => "10MiB" # 10485760 bytes my_bytes => "100kib" # 102400 bytes my_bytes => "180 mb" # 180000000 bytes ``` - Codec ``` codec => "json" ``` - Hash ``` match => { "field1" => "value1" "field2" => "value2" ... } ``` - Number ``` port => 33 ``` - Password ``` my_password => "password" ``` - URI ``` my_uri => "http://foo:bar@example.net" ``` - Path ``` my_path => "/tmp/logstash" ``` - String - 转义字符 ## 插件 ### input > Logstash 支持各种输入选择 ,可以在同一时间从众多常用来源捕捉事件。能够以连续的流式传输方式,轻松地从您的日志、指标、Web 应用、数据存储以及各种 AWS 服务采集数据。 #### 常用 input 插件 - **file**:从文件系统上的文件读取,就像UNIX命令 `tail -0F` 一样 - **syslog:**在众所周知的端口514上侦听系统日志消息,并根据RFC3164格式进行解析 - **redis:**从redis服务器读取,使用redis通道和redis列表。 Redis经常用作集中式Logstash安装中的“代理”,它将来自远程Logstash“托运人”的Logstash事件排队。 - **beats:**处理由Filebeat发送的事件。 更多详情请见:[Input Plugins](https://www.elastic.co/guide/en/logstash/current/input-plugins.html) ### filter > 过滤器是Logstash管道中的中间处理设备。如果符合特定条件,您可以将条件过滤器组合在一起,对事件执行操作。 #### 常用 filter 插件 - **grok:**解析和结构任意文本。 Grok目前是Logstash中将非结构化日志数据解析为结构化和可查询的最佳方法。 - **mutate:**对事件字段执行一般转换。您可以重命名,删除,替换和修改事件中的字段。 - **drop:**完全放弃一个事件,例如调试事件。 - **clone:**制作一个事件的副本,可能会添加或删除字段。 - **geoip:**添加有关IP地址的地理位置的信息(也可以在Kibana中显示惊人的图表!) 更多详情请见:[Filter Plugins](https://www.elastic.co/guide/en/logstash/current/filter-plugins.html) ### output > 输出是Logstash管道的最后阶段。一个事件可以通过多个输出,但是一旦所有输出处理完成,事件就完成了执行。 #### 常用 output 插件 - **elasticsearch:**将事件数据发送给 Elasticsearch(推荐模式)。 - **file:**将事件数据写入文件或磁盘。 - **graphite:**将事件数据发送给 graphite(一个流行的开源工具,存储和绘制指标。 http://graphite.readthedocs.io/en/latest/)。 - **statsd:**将事件数据发送到 statsd (这是一种侦听统计数据的服务,如计数器和定时器,通过UDP发送并将聚合发送到一个或多个可插入的后端服务)。 更多详情请见:[Output Plugins](https://www.elastic.co/guide/en/logstash/current/output-plugins.html) ### codec 用于格式化对应的内容。 #### 常用 codec 插件 - **json:**以JSON格式对数据进行编码或解码。 - **multiline:**将多行文本事件(如java异常和堆栈跟踪消息)合并为单个事件。 更多插件请见:[Codec Plugins](https://www.elastic.co/guide/en/logstash/current/codec-plugins.html) ## 实战 前面的内容都是对 Logstash 的介绍和原理说明。接下来,我们来实战一些常见的应用场景。 ### 传输控制台数据 > stdin input 插件从标准输入读取事件。这是最简单的 input 插件,一般用于测试场景。 > **应用** (1)创建 `logstash-input-stdin.conf` : ``` input { stdin { } } output { elasticsearch { hosts => ["localhost:9200"] } stdout { codec => rubydebug } } ``` 更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-stdin.html (2)执行 logstash,使用 `-f` 来指定你的配置文件: ``` bin/logstash -f logstash-input-stdin.conf ``` ### 传输 logback 日志 > elk 默认使用的 Java 日志工具是 log4j2 ,并不支持 logback 和 log4j。 > > 想使用 logback + logstash ,可以使用 [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) 。[logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) 提供了 UDP / TCP / 异步方式来传输日志数据到 logstash。 > > 如果你使用的是 log4j ,也不是不可以用这种方式,只要引入桥接 jar 包即可。如果你对 log4j 、logback ,或是桥接 jar 包不太了解,可以参考我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。 #### TCP 应用 1. logstash 配置 (1)创建 `logstash-input-tcp.conf` : ``` input { tcp { port => 9251 codec => json_lines mode => server } } output { elasticsearch { hosts => ["localhost:9200"] } stdout { codec => rubydebug } } ``` 更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-tcp.html (2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-udp.conf` 2. java 应用配置 (1)在 Java 应用的 pom.xml 中引入 jar 包: ```xml net.logstash.logback logstash-logback-encoder 4.11 ch.qos.logback logback-core 1.2.3 ch.qos.logback logback-classic 1.2.3 ch.qos.logback logback-access 1.2.3 ``` (2)接着,在 logback.xml 中添加 appender ```xml 192.168.28.32:9251 ``` (3)接下来,就是 logback 的具体使用 ,如果对此不了解,不妨参考一下我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。 **实例:**[我的logback.xml](https://github.com/dunwu/JavaStack/blob/master/codes/javatool/src/main/resources/logback.xml) #### UDP 应用 UDP 和 TCP 的使用方式大同小异。 1. logstash 配置 (1)创建 `logstash-input-udp.conf` : ``` input { udp { port => 9250 codec => json } } output { elasticsearch { hosts => ["localhost:9200"] } stdout { codec => rubydebug } } ``` 更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-udp.html (2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-udp.conf` 2. java 应用配置 (1)在 Java 应用的 pom.xml 中引入 jar 包: 与 **TCP 应用** 一节中的引入依赖包完全相同。 (2)接着,在 logback.xml 中添加 appender ```xml 192.168.28.32 9250 ``` (3)接下来,就是 logback 的具体使用 ,如果对此不了解,不妨参考一下我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。 **实例:**[我的logback.xml](https://github.com/dunwu/JavaStack/blob/master/codes/javatool/src/main/resources/logback.xml) ### 传输文件 > 在 Java Web 领域,需要用到一些重要的工具,例如 Tomcat 、Nginx 、Mysql 等。这些不属于业务应用,但是它们的日志数据对于定位问题、分析统计同样很重要。这时无法使用 logback 方式将它们的日志传输到 logstash。 > > 如何采集这些日志文件呢?别急,你可以使用 logstash 的 file input 插件。 > > 需要注意的是,传输文件这种方式,必须在日志所在的机器上部署 logstash 。 **应用** logstash 配置 (1)创建 `logstash-input-file.conf` : ``` input { file { path => ["/var/log/nginx/access.log"] type => "nginx-access-log" start_position => "beginning" } } output { if [type] == "nginx-access-log" { elasticsearch { hosts => ["localhost:9200"] index => "nginx-access-log" } } } ``` (2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-file.conf` 更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html ## 小技巧 ### 启动、终止应用 如果你的 logstash 每次都是通过指定配置文件方式启动。不妨建立一个启动脚本。 ``` # cd xxx 进入 logstash 安装目录下的 bin 目录 logstash -f logstash.conf ``` 如果你的 logstash 运行在 linux 系统下,不妨使用 nohup 来启动一个守护进程。这样做的好处在于,即使关闭终端,应用仍会运行。 **创建 startup.sh** ``` nohup ./logstash -f logstash.conf >> nohup.out 2>&1 & ``` 终止应用没有什么好方法,你只能使用 ps -ef | grep logstash ,查出进程,将其kill 。不过,我们可以写一个脚本来干这件事: **创建 shutdown.sh** 脚本不多解释,请自行领会作用。 ``` PID=`ps -ef | grep logstash | awk '{ print $2}' | head -n 1` kill -9 ${PID} ``` ## 资料 - [Logstash 官方文档](https://www.elastic.co/guide/en/logstash/current/index.html) - [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) - [ELK Stack权威指南](https://github.com/chenryn/logstash-best-practice-cn) - [ELK(Elasticsearch、Logstash、Kibana)安装和配置](https://github.com/judasn/Linux-Tutorial/blob/master/ELK-Install-And-Settings.md) ## 推荐阅读 - [Elastic 技术栈](https://github.com/dunwu/JavaStack/blob/master/docs/javatool/elastic/README.md) - [JavaStack](https://github.com/dunwu/JavaStack) ================================================ FILE: docs/linux/soft/elastic/elastic-quickstart.md ================================================ --- title: Elastic 技术栈之快速入门 date: 2017-12-06 categories: - javatool tags: - java - javatool - log - elastic --- # Elastic 技术栈之快速入门 ## 概念 ### ELK 是什么 ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/products/elasticsearch) 、[Logstash](https://www.elastic.co/products/logstash) 、[Kibana](https://www.elastic.co/products/kibana) 的首字母组合。 [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 [Lucene](http://lucene.apache.org/core/documentation.html) 构建的开源,分布式,RESTful 搜索引擎。 [Logstash](https://www.elastic.co/products/logstash) 传输和处理你的日志、事务或其他数据。 [Kibana](https://www.elastic.co/products/kibana) 将 Elasticsearch 的数据分析并渲染为可视化的报表。 ### 为什么使用 ELK ? 对于有一定规模的公司来说,通常会很多个应用,并部署在大量的服务器上。运维和开发人员常常需要通过查看日志来定位问题。如果应用是集群化部署,试想如果登录一台台服务器去查看日志,是多么费时费力。 而通过 ELK 这套解决方案,可以同时实现日志收集、日志搜索和日志分析的功能。 ### Elastic 架构 ![img](https://www.elastic.co/guide/en/logstash/current/static/images/deploy3.png) > **说明** > > 以上是 ELK 技术栈的一个架构图。从图中可以清楚的看到数据流向。 > > [Beats](https://www.elastic.co/products/beats) 是单一用途的数据传输平台,它可以将多台机器的数据发送到 Logstash 或 ElasticSearch。但 Beats 并不是不可或缺的一环,所以本文中暂不介绍。 > > [Logstash](https://www.elastic.co/products/logstash) 是一个动态数据收集管道。支持以 TCP/UDP/HTTP 多种方式收集数据(也可以接受 Beats 传输来的数据),并对数据做进一步丰富或提取字段处理。 > > [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 JSON 的分布式的搜索和分析引擎。作为 ELK 的核心,它集中存储数据。 > > [Kibana](https://www.elastic.co/products/kibana) 是 ELK 的用户界面。它将收集的数据进行可视化展示(各种报表、图形化数据),并提供配置、管理 ELK 的界面。 ## 安装 ### 准备 ELK 要求本地环境中安装了 JDK 。如果不确定是否已安装,可使用下面的命令检查: ```bash java -version ``` > **注意** > > 本文使用的 ELK 是 6.0.0,要求 jdk 版本不低于 JDK8。 > > 友情提示:安装 ELK 时,三个应用请选择统一的版本,避免出现一些莫名其妙的问题。例如:由于版本不统一,导致三个应用间的通讯异常。 ### Elasticsearch 安装步骤如下: 1. [elasticsearch 官方下载地址](https://www.elastic.co/downloads/elasticsearch)下载所需版本包并解压到本地。 2. 运行 `bin/elasticsearch` (Windows 上运行 `bin\elasticsearch.bat`) 3. 验证运行成功:linux 上可以执行 `curl http://localhost:9200/` ;windows 上可以用访问 REST 接口的方式来访问 http://localhost:9200/ > **说明** > > Linux 上可以执行下面的命令来下载压缩包: > > ``` > curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0.tar.gz > ``` > > Mac 上可以执行以下命令来进行安装: > > ``` > brew install elasticsearch > ``` > > Windows 上可以选择 MSI 可执行安装程序,将应用安装到本地。 ### Logstash 安装步骤如下: 1. 在 [logstash 官方下载地址](https://www.elastic.co/downloads/logstash)下载所需版本包并解压到本地。 2. 添加一个 `logstash.conf` 文件,指定要使用的插件以及每个插件的设置。举个简单的例子: ``` input { stdin { } } output { elasticsearch { hosts => ["localhost:9200"] } stdout { codec => rubydebug } } ``` 3. 运行 `bin/logstash -f logstash.conf` (Windows 上运行`bin/logstash.bat -f logstash.conf`) ### Kibana 安装步骤如下: 1. 在 [kibana 官方下载地址](https://www.elastic.co/downloads/kibana)下载所需版本包并解压到本地。 2. 修改 `config/kibana.yml` 配置文件,设置 `elasticsearch.url` 指向 Elasticsearch 实例。 3. 运行 `bin/kibana` (Windows 上运行 `bin\kibana.bat`) 4. 在浏览器上访问 http://localhost:5601 ### 安装 FAQ #### elasticsearch 不允许以 root 权限来运行 **问题:**在 Linux 环境中,elasticsearch 不允许以 root 权限来运行。 如果以 root 身份运行 elasticsearch,会提示这样的错误: ``` can not run elasticsearch as root ``` **解决方法:**使用非 root 权限账号运行 elasticsearch ```bash # 创建用户组 groupadd elk # 创建新用户,-g elk 设置其用户组为 elk,-p elk 设置其密码为 elk useradd elk -g elk -p elk # 更改 /opt 文件夹及内部文件的所属用户及组为 elk:elk chown -R elk:elk /opt # 假设你的 elasticsearch 安装在 opt 目录下 # 切换账号 su elk ``` #### vm.max_map_count 不低于 262144 **问题:**`vm.max_map_count` 表示虚拟内存大小,它是一个内核参数。elasticsearch 默认要求 `vm.max_map_count` 不低于 262144。 ``` max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] ``` **解决方法:** 你可以执行以下命令,设置 `vm.max_map_count` ,但是重启后又会恢复为原值。 ``` sysctl -w vm.max_map_count=262144 ``` 持久性的做法是在 `/etc/sysctl.conf` 文件中修改 `vm.max_map_count` 参数: ``` echo "vm.max_map_count=262144" > /etc/sysctl.conf sysctl -p ``` > **注意** > > 如果运行环境为 docker 容器,可能会限制执行 sysctl 来修改内核参数。 > > 这种情况下,你只能选择直接修改宿主机上的参数了。 #### nofile 不低于 65536 **问题:** `nofile` 表示进程允许打开的最大文件数。elasticsearch 进程要求可以打开的最大文件数不低于 65536。 ``` max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536] ``` **解决方法:** 在 `/etc/security/limits.conf` 文件中修改 `nofile` 参数: ``` echo "* soft nofile 65536" > /etc/security/limits.conf echo "* hard nofile 131072" > /etc/security/limits.conf ``` #### nproc 不低于 2048 **问题:** `nproc` 表示最大线程数。elasticsearch 要求最大线程数不低于 2048。 ``` max number of threads [1024] for user [user] is too low, increase to at least [2048] ``` **解决方法:** 在 `/etc/security/limits.conf` 文件中修改 `nproc` 参数: ``` echo "* soft nproc 2048" > /etc/security/limits.conf echo "* hard nproc 4096" > /etc/security/limits.conf ``` #### Kibana No Default Index Pattern Warning **问题:**安装 ELK 后,访问 kibana 页面时,提示以下错误信息: ``` Warning No default index pattern. You must select or create one to continue. ... Unable to fetch mapping. Do you have indices matching the pattern? ``` 这就说明 logstash 没有把日志写入到 elasticsearch。 **解决方法:** 检查 logstash 与 elasticsearch 之间的通讯是否有问题,一般问题就出在这。 ## 使用 本人使用的 Java 日志方案为 slf4j + logback,所以这里以 logback 来讲解。 ### Java 应用输出日志到 ELK **修改 logstash.conf 配置** 首先,我们需要修改一下 logstash 服务端 logstash.conf 中的配置 ``` input { # stdin { } tcp { # host:port就是上面appender中的 destination, # 这里其实把logstash作为服务,开启9250端口接收logback发出的消息 host => "127.0.0.1" port => 9250 mode => "server" tags => ["tags"] codec => json_lines } } output { elasticsearch { hosts => ["localhost:9200"] } stdout { codec => rubydebug } } ``` > **说明** > > 这个 input 中的配置其实是 logstash 服务端监听 9250 端口,接收传递来的日志数据。 然后,在 Java 应用的 pom.xml 中引入 jar 包: ```xml net.logstash.logback logstash-logback-encoder 4.11 ``` 接着,在 logback.xml 中添加 appender ```xml 127.0.0.1:9250 ``` 大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。 ![img](http://upload-images.jianshu.io/upload_images/3101171-cd876d79a14955b0.png) ## 资料 - [elastic 官方文档](https://www.elastic.co/guide/index.html) - [elasticsearch github](https://github.com/elastic/elasticsearch) - [logstash github](https://github.com/elastic/logstash) - [kibana github](https://github.com/elastic/kibana) ================================================ FILE: docs/linux/soft/fastdfs.md ================================================ # FastDFS ## 简介 FastDFS 是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。 FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS 很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。 ## 概念 FastDFS 服务端有三个角色:跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)。 **tracker server**:跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。相比 GFS 中的 master 更为精简,不记录文件索引信息,占用的内存量很少。 Tracker 是 FastDFS 的协调者,负责管理所有的 storage server 和 group,每个 storage 在启动后会连接 Tracker,告知自己所属的 group 等信息,并保持周期性的心跳,tracker 根据 storage 的心跳信息,建立 group==>[storage server list]的映射表。 Tracker 需要管理的元信息很少,会全部存储在内存中;另外 tracker 上的元信息都是由 storage 汇报的信息生成的,本身不需要持久化任何数据,这样使得 tracker 非常容易扩展,直接增加 tracker 机器即可扩展为 tracker cluster 来服务,cluster 里每个 tracker 之间是完全对等的,所有的 tracker 都接受 stroage 的心跳信息,生成元数据信息来提供读写服务。 **storage server**:存储服务器(又称:存储节点或数据服务器),文件和文件属性(meta data)都保存到存储服务器上。Storage server 直接利用 OS 的文件系统调用管理文件。 Storage server(后简称 storage)以组(卷,group 或 volume)为单位组织,一个 group 内包含多台 storage 机器,数据互为备份,存储空间以 group 内容量最小的 storage 为准,所以建议 group 内的多个 storage 尽量配置相同,以免造成存储空间的浪费。 以 group 为单位组织存储能方便的进行应用隔离、负载均衡、副本数定制(group 内 storage server 数量即为该 group 的副本数),比如将不同应用数据存到不同的 group 就能隔离应用数据,同时还可根据应用的访问特性来将应用分配到不同的 group 来做负载均衡;缺点是 group 的容量受单机存储容量的限制,同时当 group 内有机器坏掉时,数据恢复只能依赖 group 内地其他机器,使得恢复时间会很长。 group 内每个 storage 的存储依赖于本地文件系统,storage 可配置多个数据存储目录,比如有 10 块磁盘,分别挂载在`/data/disk1-/data/disk10`,则可将这 10 个目录都配置为 storage 的数据存储目录。 storage 接受到写文件请求时,会根据配置好的规则(后面会介绍),选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多,在 storage 第一次启动时,会在每个数据存储目录里创建 2 级子目录,每级 256 个,总共 65536 个文件,新写的文件会以 hash 的方式被路由到其中某个子目录下,然后将文件数据直接作为一个本地文件存储到该目录中。 **client**:客户端,作为业务请求的发起方,通过专有接口,使用 TCP/IP 协议与跟踪器服务器或存储节点进行数据交互。FastDFS 向使用者提供基本文件访问接口,比如 upload、download、append、delete 等,以客户端库的方式提供给用户使用。 另外两个概念: **group** :组, 也可称为卷。 同组内服务器上的文件是完全相同的 ,同一组内的 storage server 之间是对等的, 文件上传、 删除等操作可以在任意一台 storage server 上进行 。 **meta data** :文件相关属性,键值对( Key Value Pair) 方式,如:width=1024,heigth=768 。 ![img](http://www.ityouknow.com/assets/images/2018/fastdfs/fastdfs_arch.png) Tracker 相当于 FastDFS 的大脑,不论是上传还是下载都是通过 tracker 来分配资源;客户端一般可以使用 ngnix 等静态服务器来调用或者做一部分的缓存;存储服务器内部分为卷(或者叫做组),卷于卷之间是平行的关系,可以根据资源的使用情况随时增加,卷内服务器文件相互同步备份,以达到容灾的目的。 ## 参考资料 - [FastDFS 官方 Github](https://github.com/happyfish100/fastdfs) - [FastDFS 官方 wiki](https://github.com/happyfish100/fastdfs/wiki) - [分布式文件系统 FastDFS 详解](https://www.cnblogs.com/ityouknow/p/8240976.html) ================================================ FILE: docs/linux/soft/gitlab-ops.md ================================================ # Gitlab 运维 ## 一、gitlab 安装 ### Gitlab 的普通安装 #### 下载 进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190129155838.png) 以 CentOS7 为例: #### 安装和配置必要依赖 在系统防火墙中启用 HTTP 和 SSH ``` sudo yum install -y curl policycoreutils-python openssh-server sudo systemctl enable sshd sudo systemctl start sshd sudo firewall-cmd --permanent --add-service=http sudo systemctl reload firewalld ``` 安装 Postfix ,使得 Gitlab 可以发送通知邮件 ``` sudo yum install postfix sudo systemctl enable postfix sudo systemctl start postfix ``` #### 添加 Gitlab yum 仓库并安装包 添加 Gitlab yum 仓库 ``` curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash ``` 通过 yum 安装 gitlab-ce ``` sudo EXTERNAL_URL="http://gitlab.example.com" yum install -y gitlab-ce ``` 安装完成后,即可通过默认的 root 账户进行登录。更多细节可以参考:[documentation for detailed instructions on installing and configuration](https://docs.gitlab.com/omnibus/README.html#installation-and-configuration-using-omnibus-package) ### Gitlab 的 Docker 安装 拉取镜像 ``` docker pull docker.io/gitlab/gitlab-ce ``` 启动 ``` docker run -d \ --hostname gitlab.zp.io \ --publish 8443:443 --publish 80:80 --publish 2222:22 \ --name gitlab \ --restart always \ --volume $GITLAB_HOME/config:/etc/gitlab \ --volume $GITLAB_HOME/logs:/var/log/gitlab \ --volume $GITLAB_HOME/data:/var/opt/gitlab \ gitlab/gitlab-ce ``` ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190131150515.png) ### 自签名证书 首先,创建认证目录 ``` sudo mkdir -p /etc/gitlab/ssl sudo chmod 700 /etc/gitlab/ssl ``` (1)创建 Private Key ``` sudo openssl genrsa -des3 -out /etc/gitlab/ssl/gitlab.domain.com.key 2048 ``` 会提示输入密码,请记住 (2)生成 Certificate Request ``` sudo openssl req -new -key /etc/gitlab/ssl/gitlab.domain.com.key -out /etc/gitlab/ssl/gitlab.domain.com.csr ``` 根据提示,输入信息 ``` Country Name (2 letter code) [XX]:CN State or Province Name (full name) []:JS Locality Name (eg, city) [Default City]:NJ Organization Name (eg, company) [Default Company Ltd]:xxxxx Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) []:gitlab.xxxx.io Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: ``` (3)移除 Private Key 中的密码短语 ``` sudo cp -v /etc/gitlab/ssl/gitlab.domain.com.{key,original} sudo openssl rsa -in /etc/gitlab/ssl/gitlab.domain.com.original -out /etc/gitlab/ssl/gitlab.domain.com.key sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.original ``` (4)设置文件权限 ``` sudo chmod 600 /etc/gitlab/ssl/gitlab.domain.com.* ``` ## 二、gitlab-ci-multi-runner 安装 > 参考:https://docs.gitlab.com/runner/install/ ### 常规安装 gitlab-ci-multi-runner #### 下载 ``` sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64 ``` #### 配置执行权限 ``` sudo chmod +x /usr/local/bin/gitlab-runner ``` #### 如果想使用 Docker,安装 Docker(可选的) ``` curl -sSL https://get.docker.com/ | sh ``` #### 创建 CI 用户 ``` sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash ``` #### 安装并启动服务 ``` sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner sudo gitlab-runner start ``` #### 注册 Runner (1)执行命令: ``` sudo gitlab-runner register ``` (2)输入 Gitlab URL 和 令牌 URL 和令牌信息在 Gitlab 的 Runner 管理页面获取: ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190129163100.png) ``` Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) https://gitlab.com Please enter the gitlab-ci token for this runner xxx ``` (3)输入 Runner 的描述 ``` Please enter the gitlab-ci description for this runner [hostame] my-runner ``` (4)输入 Runner 相关的标签 ``` Please enter the gitlab-ci tags for this runner (comma separated): my-tag,another-tag ``` (5)输入 Runner 执行器 ``` Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell: docker ``` 如果想选择 Docker 作为执行器,你需要指定默认镜像( `.gitlab-ci.yml` 中没有此配置) ``` Please enter the Docker image (eg. ruby:2.1): alpine:latest ``` ### Docker 安装 gitlab-ci-multi-runner 拉取镜像 ``` docker pull docker.io/gitlab/gitlab-runner ``` 启动 ``` docker run -d --name gitlab-runner --restart always \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest ``` ## 三、gitlab 配置 ### 基本配置 ``` sudo vim /etc/gitlab/gitlab.rb external_url 'https://gitlab.domain.com' ``` #### gitlab 网站 https: ``` nginx['redirect_http_to_https'] = true ``` #### gitlab ci 网站 https: ``` ci_nginx['redirect_http_to_https'] = true ``` #### 复制证书到 gitlab 目录: ``` sudo cp /etc/gitlab/ssl/gitlab.domain.com.crt /etc/gitlab/trusted-certs/ ``` #### gitlab 重新配置+更新: ``` sudo gitlab-ctl reconfigure sudo gitlab-ctl restart ``` ### 创建你的 SSH key 1. 使用 Gitlab 的第一步是生成你自己的 SSH 密钥对(Github 也类似)。 2. 登录 Gitlab 3. 打开 **Profile settings**. ![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings.png) 4. 跳转到 **SSH keys** tab 页 ![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys.png) 5. 黏贴你的 SSH 公钥内容到 Key 文本框 ![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png) 6. 为了便于识别,你可以为其命名 ![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_title.png) 7. 点击 **Add key** 将 SSH 公钥添加到 GitLab ![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_single_key.png) ### 创建项目 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190131150658.png) 输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190131150759.png) ### 克隆项目到本地 可以选择 SSH 或 HTTPS 方式克隆项目到本地(推荐 SSH) 拷贝项目地址,然后在本地执行 `git clone ` ### 创建 Issue 依次点击 **Project’s Dashboard** > **Issues** > **New Issue** 可以新建 Issue ![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_tracker_list.png) 在项目中直接添加 issue ![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue.png) 在未关闭 issue 中,点击 **New Issue** 添加 issue ![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_open_issue.png) 通过项目面板添加 issue ![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_projects_dashboard.png) 通过 issue 面板添加 issue ![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png) ## 四、gitlab 权限配置 ### 用户组的权限 - 用户组有这几种权限的概念:`Guest、Reporter、Developer、Master、Owner` - 这个概念在设置用户组的时候会遇到,叫做:`Add user(s) to the group`,比如链接:`https:///` | 行为 | Guest | Reporter | Developer | Master | Owner | | ---------- | ----- | -------- | --------- | ------ | ----- | | 浏览组 | ✓ | ✓ | ✓ | ✓ | ✓ | | 编辑组 | | | | | ✓ | | 创建项目 | | | | ✓ | ✓ | | 管理组成员 | | | | | ✓ | | 移除组 | | | | | | ## 五、备份/迁移/升级 ### 备份 #### 手动备份 执行 `gitlab-rake gitlab:backup:create` 开始备份全量数据,成功后,会在 `/var/opt/gitlab/backups` 下生产一个名称类似 `1585910556_2020_04_03_11.3.0_gitlab_backup.tar` 的压缩包。 ### 定时自动备份 可以利用 crontab 来定时执行备份命令。 执行 `vim /etc/crontab` 或 `crontab -e` 手动编辑定时任务。 ### 迁移 > 迁移前,需要确保新老机器的 Gitlab 版本号一致。 将备份的压缩包拷贝到新机器的备份路径下(默认为 `/var/opt/gitlab/backups`)。 (1)将备份文件权限修改为 777,不然可能恢复的时候会出现权限不够,不能解压的问题 ```shell chmod 777 1585910556_2020_04_03_11.3.0_gitlab_backup.tar ``` (2)停止相关数据连接服务 ```shell gitlab-ctl stop unicorn gitlab-ctl stop sidekiq ``` (3)从备份文件中恢复 Gitlab ```shell # gitlab-rake gitlab:backup:restore BACKUP=备份文件编号 gitlab-rake gitlab:backup:restore BACKUP=1585910556_2020_04_03_11.3.0 ``` ### 升级 升级前,一定要做好备份,记录当前 gitlab 的版本号。 第一步还是使用官方命令进行升级。 ```shell sudo yum install -y gitlab-ce ``` 如果下载速度理想,就无需手动升级安装。不理想就需要`停止自动更新`,并手动下载安装包 访问官方地址,下载对应`版本`,对应`系统`的安装包。 注:可以根据`自动升级时下载的版本`,选择对应文件。 ```http https://packages.gitlab.com/gitlab/gitlab-ce ``` 安装包手动上传至服务器,并`替换`下载未完成的安装包。下面是升级缓存地址: ``` /var/cache/yum/x86_64/7/gitlab_gitlab-ce/packages/ ``` 再次执行官方升级命令即可完成自动安装。 ## 参考资料 - 官网:https://about.gitlab.com/ - 中文网:https://www.gitlab.com.cn/ - 官网下载:https://about.gitlab.com/downloads/ - 官网安装说明:https://about.gitlab.com/installation/#centos-7 - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) ================================================ FILE: docs/linux/soft/jdk-install.md ================================================ # JDK 安装 > 关键词:JDK, JAVA_HOME, CLASSPATH, PATH - [JDK 安装步骤](#jdk-安装步骤) - [Windows 系统安装方法](#windows-系统安装方法) - [Linux 系统安装方法](#linux-系统安装方法) - [RedHat 发行版本使用 rpm 安装方法](#redhat-发行版本使用-rpm-安装方法) - [参考资料](#参考资料) ## JDK 安装步骤 JDK 安装步骤: (1)下载 JDK a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/javase/downloads/index.html); b. 选择需要的版本: ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920181010164121.png) c. 选择对应操作系统的安装包: Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20180920181010164308.png) (2)运行安装包,按提示逐步安装 (3)配置系统环境变量:`JAVA_HOME`, `CLASSPATH`, `PATH` (4)验证 Java 是否安装成功 ## Windows 系统安装方法 (1)下载 JDK 需要根据 Windows 系统实际情况,选择 exe 安装文件: - 32 位计算机选择 Windows x86 - 64 位计算机选择 Windows x64 (2)运行安装包,按提示逐步安装 (3)配置系统环境变量 a. 安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置"; ![img](https://www.runoob.com/wp-content/uploads/2013/12/win-java1.png) b. 选择"高级"选项卡,点击"环境变量"; ![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win2.png) 然后就会出现如下图所示的画面: 在"系统变量"中设置 3 项属性,JAVA_HOME,PATH,CLASSPATH(大小写无所谓),若已存在则点击"编辑",不存在则点击"新建"。 变量设置参数如下: - 变量名:**JAVA_HOME** - 变量值:**C:\Program Files (x86)\Java\jdk1.8.0_91** // 要根据自己的实际路径配置 - 变量名:**CLASSPATH** - 变量值:**.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;** //记得前面有个"." - 变量名:**Path** - 变量值:**%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;** (4)验证 Java 是否安装成功 a. "开始"->"运行",键入"cmd"; b. 键入命令: **java -version**、**java**、**javac** 几个命令,出现以下信息,说明环境变量配置成功; ![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win9.png) ## Linux 系统安装方法 (1)下载 JDK 需要根据 Linux 系统实际情况,选择 tar.gz 压缩包: - 32 位计算机选择 Linux x86 - 64 位计算机选择 Linux x64 (2)解压压缩包到本地 ```bash $ tar -zxf jdk-8u162-linux-x64.tar.gz ``` (3)配置系统环境变量 执行 `vi /etc/profile` 命令,添加以下内容: ```bash # JDK 的根路径 export JAVA_HOME=/opt/java/jdk1.8.0_162 export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH ``` 执行 `source /etc/profile` ,立即生效 (4)验证 Java 是否安装成功 执行 `java -version` 命令,验证安装是否成功。 ### RedHat 发行版本使用 rpm 安装方法 (1)下载 JDK 下载 rpm 安装包 (2)选择一个合适的版本安装 ```bash $ rpm -ivh jdk-8u181-linux-x64.rpm ``` 安装成功后,默认安装路径在 `/usr/local` 下: (3)设置环境变量,同压缩包安装。 (4)检验是否安装成功,执行 `java -version` 命令 ## 更多内容 - **引申** - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) - **引用** - http://www.runoob.com/java/java-environment-setup.html - https://blog.csdn.net/deliciousion/article/details/78046007 ================================================ FILE: docs/linux/soft/jenkins-ops.md ================================================ # Jenkins 运维 > 环境要求 > > - JDK:JDK7+,官网推荐是 JDK 8 > - Jenkins:2.190.1 ## Jenkins 简介 ### Jenkins 是什么 Jenkins 是一款开源 CI&CD 软件,用于自动化各种任务,包括构建、测试和部署软件。 Jenkins 支持各种运行方式,可通过系统包、Docker 或者通过一个独立的 Java 程序。 ### CI/CD 是什么 CI(Continuous integration,中文意思是持续集成)是一种软件开发时间。持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。借用网络图片对 CI 加以理解。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310174528.png) CD(Continuous Delivery, 中文意思持续交付)是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中。比如,我们完成单元测试后,可以把代码部署到连接数据库的 Staging 环境中更多的测试。如果代码没有问题,可以继续手动部署到生产环境。下图反应的是 CI/CD 的大概工作模式。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310174544.png) ## Jenkins 安装 > 更详细内容请参考:[Jenkins 官方安装文档](https://jenkins.io/zh/doc/book/installing/) ### War 包部署 安装步骤如下: (1)下载并解压到本地 进入[官网下载地址](https://jenkins.io/zh/download/),选择合适的版本下载。 我选择的是最新稳定 war 版本 2.89.4:http://mirrors.jenkins.io/war-stable/latest/jenkins.war 我个人喜欢存放在:`/opt/software/jenkins` ```bash mkdir -p /opt/software/jenkins wget -O /opt/software/jenkins/jenkins.war http://mirrors.jenkins.io/war-stable/latest/jenkins.wa ``` (2)启动 如果你和我一样,选择 war 版本,那么你可以将 war 移到 Tomcat 的 webapps 目录下,通过 Tomcat 来启动。 当然,也可以通过 `java -jar` 方式来启动。 ```bash cd /opt/software/jenkins nohup java -jar jenkins.war --httpPort=8080 >> nohup.out 2>&1 & ``` ### rpm 包部署 (1)下载安装 ```bash sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key yum install jenkins ``` (2)启动 ```bash systemctl start jenkins ``` ### 访问 1. 打开浏览器进入链接 `http://localhost:8080`. 2. 按照说明完成安装. ## Jenkins 基本使用 Jenkins 是一个强大的 CI 工具,虽然本身使用 Java 开发,但也能用来做其他语言开发的项目 CI。下面讲解如何使用 Jenkins 创建一个构建任务。 登录 Jenkins, 点击左侧的新建,创建新的构建任务。 ![img](https:////upload-images.jianshu.io/upload_images/6464255-22b3c49af599565d.png?imageMogr2/auto-orient/strip|imageView2/2/w/374/format/webp) 跳转到如下界面。任务名称可以自行设定,但需要全局唯一。输入名称后选择构建一个自由风格的软件项目(其他选项不作介绍)。并点击下方的确定按钮即创建了一个构建任务。之后会自动跳转到该 job 的配置页面。 ![img](https:////upload-images.jianshu.io/upload_images/6464255-0febc0bc4ca3cadd.png?imageMogr2/auto-orient/strip|imageView2/2/w/1044/format/webp) 新建自由风格的软件项目 下图是构建任务设置界面,可以看到上方的几个选项**"General", "源码管理", "构建触发器","构建环境", "构建", "构建后操作"**。下面逐一介绍。 ![img](https:////upload-images.jianshu.io/upload_images/6464255-77998a3e6a70b83f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1032/format/webp) ### General General 是构建任务的一些基本配置。名称,描述之类的。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310221814.png) 重要配置项: - **Description**:对构建任务的描述。 - **Discard old builds**:服务器资源是有限的,有时候保存了太多的历史构建,会导致 Jenkins 速度变慢,并且服务器硬盘资源也会被占满。当然下方的"保持构建天数" 和 保持构建的最大个数是可以自定义的,需要根据实际情况确定一个合理的值。 点击右方的问号图标可以查看帮助信息。 ### Source Code Management **Source Code Management**,即源码管理,就是配置你代码的存放位置。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310222110.png) - **Git:** 支持主流的 Github 和 Gitlab 代码仓库。因我们的研发团队使用的是 gitlab,所以下面我只会对该项进行介绍。 - **Repository URL**:仓库地址。 - **Credentials**:凭证。可以使用 HTTP 方式的用户名密码,也可以是 RSA 文件。 但要通过后面的"ADD"按钮添加凭证。 - **Branches to build**:构建的分支。`*/master` 表示 master 分支,也可以设置为其他分支。 - **Repository browser**:你所使用的代码仓库管理工具,如 Github、Gitlab. - **Subversion**:即 SVN,这里不作介绍。 ### Build Triggers **Build Triggers**,即构建触发器,用于构建任务的触发器。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310222608.png) 配置说明: - **Trigger builds remotely (e.g., from scripts)**:触发远程构建(例如,使用脚本)。该选项会提供一个接口,可以用来在代码层面触发构建。 - **Build after other projects are built**:该选项意思是"在其他项目构建后再构建"。 - **Build periodically**:周期性的构建。就是每隔一段时间进行构建。日程表类似 linux crontab 书写格式。如:`H/30 * * * *`,表示每隔 30 分钟进行一次构建。 - **Build when a change is pushed to GitLab:**当有 git push 到 Gitlab 仓库,即触发构建。后面会有一个触发构建的地址,一般被称为 webhooks。需要将这个地址配置到 gitlab 中,webhooks 如何配置后面介绍。这个是常用的构建触发器。 - **Poll SCM:**该选项是配合上面这个选项使用的。当代码仓库发生改动,jenkins 并不知道。需要配置这个选项,周期性的去检查代码仓库是否发生改动。 ### Build Environment **Build Environment**,即构建环境,配置构建前的一些准备工作,如指定构建工具。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310223004.png) ### Build Build,即构建。 点击下图中的 Add build step 按钮,会弹出一个构建任务菜单,可以根据实际需要来选择。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310223241.png) 【说明】 - **Copy artifacts from another project**:从其他项目获取构建。一般当本任务有上游任务,需要获取上游任务的构件时使用。比如:有个 Java Web 项目,需要依赖于上一个前端构建任务输出的静态文件压缩包。 - Eexcute NodeJS script:执行 Nodejs 脚本。默认支持 nodejs、npm 命令。 - **Eexcute shell**: 执行 shell 脚本。用于 Linux 环境。 - **Execute Windows batch command**:执行 batch 脚本。用于 Windows 环境。 - **Invoke Ant**:Ant 是一款 java 项目构建工具。 - **Invoke Gradle script**:Gradle 构建项目。 - **Invoke top-level Maven targets**:Maven 构建项目。 ### Post-build Actions **Post-build Actions**,即构建后操作,用于构建完本项目的一些后续操作,比如生成相应的代码测试报告。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224106.png) ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224254.png) ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224331.png) 个人较常用的配置: - **Archive the artifacts**:归档构件。 - **Build other projects**:构建其他项目。 - **Trigger parameterized build on other projects**:构建其他项目,并传输构建参数。 - **Publish JUnit test result report**:发布 Junit 测试报告。 - **E-mail Notification**:邮件通知,构建完成后发邮件到指定的邮箱。 --- **以上配置完成后,点击保存即可。** ### 开始构建 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310224927.png) 如上图所示,一切配置好后,即可点击 **Build Now** 开始构建。 ### 构建结果 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200310225234.png) - **构建状态** - **Successful 蓝色**:构建完成,并且被认为是稳定的。 - **Unstable 黄色**:构建完成,但被认为是不稳定的。 - **Failed 红色**:构建失败。 - **Disable 灰色**:构建已禁用 - **构建稳定性** - 构建稳定性用天气表示:**晴、晴转多云、多云、小雨、雷阵雨**。天气越好表示构建越稳定,反之亦然。 - 构建历史界面 - **console output**:输出构建的日志信息 ## 其他相关配置 ### SSH Server 配置 登录 jenkins -> 系统管理 -> 系统设置 配置请看下图: ![img](https:////upload-images.jianshu.io/upload_images/6464255-15476f9e273daa58.png?imageMogr2/auto-orient/strip|imageView2/2/w/1108/format/webp) 重要配置: - **SSH Servers:** 由于 jenkins 服务器公钥文件我已经配置好,所以之后新增 SSH Servers 只需要配置这一项即可。 - **Name:** 自定义,需要全局唯一。 - **HostName:** 主机名,直接用 ip 地址即可。 - **Username:** 新增 Server 的用户名,这里配置的是 root。 - **Remote Directory:** 远程目录。jenkins 服务器发送文件给新增的 server 默认是在这个目录。 ### 配置 Gitlab webhooks 在 gitlab 的 project 页面 打开**settings**,再打开 **web hooks** 。点击**"ADD WEB HOOK"** 添加 webhook。把之前 jenkins 配置中的那个 url 添加到这里,添加完成后,点击**"TEST HOOK"**进行测试,如果显示 SUCCESS 则表示添加成功。 ![img](https:////upload-images.jianshu.io/upload_images/6464255-9f8d04a1400556f9.png?imageMogr2/auto-orient/strip|imageView2/2/w/246/format/webp) ![img](https:////upload-images.jianshu.io/upload_images/6464255-154a62db330c819b.png?imageMogr2/auto-orient/strip|imageView2/2/w/240/format/webp) ![img](https:////upload-images.jianshu.io/upload_images/6464255-e4d1ea1e1dbde812.png?imageMogr2/auto-orient/strip|imageView2/2/w/1036/format/webp) ![img](https:////upload-images.jianshu.io/upload_images/6464255-c7a687207b2c26fc.png?imageMogr2/auto-orient/strip|imageView2/2/w/1106/format/webp) ![img](https:////upload-images.jianshu.io/upload_images/6464255-ce8ae810bc2cb0d4.png?imageMogr2/auto-orient/strip|imageView2/2/w/1154/format/webp) 配置 phpunit.xml phpunit.xml 是 phpunit 这个工具用来单元测试所需要的配置文件。这个文件的名称同样也是可以自定义的,但是要在"build.xml"中配置好名字就行。默认情况下,用"phpunit.xml", 则不需要在"build.xml"中配置文件名。 ![img](https:////upload-images.jianshu.io/upload_images/6464255-aa212d3b3eaff548.png?imageMogr2/auto-orient/strip|imageView2/2/w/798/format/webp) build.xml 中 phpunit 配置 fileset dir 指定单元测试文件所在路径,include 指定包含哪些文件,支持通配符匹配。当然也可以用 exclude 关键字指定不包含的文件。 ![img](https:////upload-images.jianshu.io/upload_images/6464255-dbc0084f6d50a240.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) ### jenkins 权限管理 由于 jenkins 默认的权限管理体系不支持用户组或角色的配置,因此需要安装第三发插件来支持角色的配置,本文将使用 Role Strategy Plugin。基于这个插件的权限管理设置请参考这篇文章:[http://blog.csdn.net/russ44/article/details/52276222](https://link.jianshu.com?t=http%3A%2F%2Fblog.csdn.net%2Fruss44%2Farticle%2Fdetails%2F52276222),这里不作详细介绍。 至此,就可以用 jenkins 周而复始的进行 CI 了,当然 jenkins 是一个强大的工具,功能绝不仅仅是以上这些,其他方面要是以后用到,我会更新到这篇文章中。有疑问欢迎在下方留言。 ## Jenkins FAQ ### 登录密码 如果不知道初始登录密码,可以通过以下方式查看: 执行命令 `cat /root/.jenkins/secrets/initialAdminPassword`,打印出来的即是初始登录密码。 ### 忘记密码 1.执行 `vim /root/.jenkins/config.xml` ,删除以下内容 ```xml true true true false ``` 2.重启 Jenkins 服务; 3.进入首页>“系统管理”>“Configure Global Security”; 4.勾选“启用安全”; 5.点选“Jenkins 专有用户数据库”,并点击“保存”; 6.重新点击首页>“系统管理”,发现此时出现“管理用户”; 7.点击进入展示“用户列表”; 8.点击右侧进入修改密码页面,修改后即可重新登录。 ### 卡在 check 页面 **现象**:输入密码后,卡在 check 页面 **原因**:jenkins 在安装插件前总是尝试连接 www.google.com,来判断网络是否连通。谷歌的网站在大陆是连不上的,所以会出现这个问题。 **解决方案**:执行`vim /root/.jenkins/updates/default.json`,将 `connectionCheckUrl` 后的 `www.google.com` 改为 `www.baidu.com` 。然后重启即可。 或者直接执行命令: ```bash sed -i 's/www.google.com/www.baidu.com/g' /root/.jenkins/updates/default.json ``` ### 卡在 getting startted 页面 **现象**:卡在 getting startted 页面 **原因**:jenkins 默认的插件下载服务器地址在国外,如果不翻墙下载不了。 **解决方案**:执行`vim /root/.jenkins/hudson.model.UpdateCenter.xml`,将 `` 改为 `http://mirror.xmission.com/jenkins/updates/update-center.json` 。然后重启即可。 或者直接执行命令: ```bash sed -i '/^/s/.*/http:\/\/mirror.xmission.com\/jenkins\/updates\/update-center.json<\/url>/g' /root/.jenkins/hudson.model.UpdateCenter.xml ``` ### 以 root 用户运行 (1)修改 jenkins 用户 ```bash vim /etc/sysconfig/jenkins ``` 修改用户 ```bash $JENKINS_USER="root" ``` (2)修改 `Jenkins` 相关文件夹用户权限 ```bash chown -R root:root /var/lib/jenkins chown -R root:root /var/cache/jenkins chown -R root:root /var/log/jenkins ``` (3)重启 Jenkins ``` systemctl restart jenkins ``` ## 参考资料 - **官方** - [Jenkins 官网](https://jenkins.io/zh/) - [Jenkins 中文文档](https://jenkins.io/zh/doc/tutorials/) - **引申** - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) - **文章** - https://jenkins.io/doc/pipeline/tour/getting-started/ - https://www.cnblogs.com/austinspark-jessylu/p/6894944.html - http://blog.csdn.net/jlminghui/article/details/54952148 - [Jenkins 详细教程](https://www.jianshu.com/p/5f671aca2b5a) ================================================ FILE: docs/linux/soft/kafka-install.md ================================================ # Kafka 安装部署 > 环境要求: > > - JDK8 > - ZooKeeper - [下载解压](#下载解压) - [启动服务器](#启动服务器) - [停止服务器](#停止服务器) - [创建主题](#创建主题) - [生产者生产消息](#生产者生产消息) - [消费者消费消息](#消费者消费消息) - [集群部署](#集群部署) ## 下载解压 进入官方下载地址:http://kafka.apache.org/downloads,选择合适版本。 解压到本地: ``` > tar -xzf kafka_2.11-1.1.0.tgz > cd kafka_2.11-1.1.0 ``` 现在您已经在您的机器上下载了最新版本的 Kafka。 ## 启动服务器 由于 Kafka 依赖于 ZooKeeper,所以运行前需要先启动 ZooKeeper ``` > bin/zookeeper-server-start.sh config/zookeeper.properties [2013-04-22 15:01:37,495] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig) ... ``` 然后,启动 Kafka ``` > bin/kafka-server-start.sh config/server.properties [2013-04-22 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties) [2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties) ... ``` ## 停止服务器 执行所有操作后,可以使用以下命令停止服务器 ``` $ bin/kafka-server-stop.sh config/server.properties ``` ## 创建主题 创建一个名为 test 的 Topic,这个 Topic 只有一个分区以及一个备份: ``` > bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test ``` ## 生产者生产消息 运行生产者,然后可以在控制台中输入一些消息,这些消息会发送到服务器: ``` > bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test This is a message This is another message ``` ## 消费者消费消息 启动消费者,然后获得服务器中 Topic 下的消息: ``` > bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning This is a message This is another message ``` ## 集群部署 复制配置为多份(Windows 使用 copy 命令代理): ``` > cp config/server.properties config/server-1.properties > cp config/server.properties config/server-2.properties ``` 修改配置: ``` config/server-1.properties: broker.id=1 listeners=PLAINTEXT://:9093 log.dir=/tmp/kafka-logs-1 config/server-2.properties: broker.id=2 listeners=PLAINTEXT://:9094 log.dir=/tmp/kafka-logs-2 ``` 其中,broker.id 这个参数必须是唯一的。 端口故意配置的不一致,是为了可以在一台机器启动多个应用节点。 根据这两份配置启动三个服务器节点: ``` > bin/kafka-server-start.sh config/server.properties & ... > bin/kafka-server-start.sh config/server-1.properties & ... > bin/kafka-server-start.sh config/server-2.properties & ... ``` 创建一个新的 Topic 使用 三个备份: ``` > bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic ``` 查看主题: ``` > bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs: Topic: my-replicated-topic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0 ``` - leader - 负责指定分区的所有读取和写入的节点。每个节点将成为随机选择的分区部分的领导者。 - replicas - 是复制此分区日志的节点列表,无论它们是否为领导者,或者即使它们当前处于活动状态。 - isr - 是“同步”复制品的集合。这是副本列表的子集,该列表当前处于活跃状态并且已经被领导者捕获。 ## 更多内容 - **引申** - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) ================================================ FILE: docs/linux/soft/maven-install.md ================================================ # Maven 安装 > 环境要求: > > - JDK - [安装方法](#安装方法) - [脚本](#脚本) ## 安装方法 安装步骤如下: (1)下载 进入官网下载地址:https://maven.apache.org/download.cgi ,选择合适的版本下载。 我选择的是最新 Maven3 版本:http://mirrors.hust.edu.cn/apache/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.tar.gz (2)解压到本地 我个人喜欢存放在:`/opt/maven` (3)设置环境变量 输入 `vi /etc/profile` ,添加环境变量如下: ``` # MAVEN 的根路径 export MAVEN_HOME=/opt/maven/apache-maven-3.5.2 export PATH=$MAVEN_HOME/bin:$PATH ``` 执行 `source /etc/profile` ,立即生效 (4)检验是否安装成功,执行 `mvn -v` 命令 ## 脚本 以上两种安装方式,我都写了脚本去执行: | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | ================================================ FILE: docs/linux/soft/mongodb-ops.md ================================================ # Mongodb 安装 - [安装](#安装) - [启动](#启动) - [脚本](#脚本) ## 安装 安装步骤如下: (1)下载并解压到本地 进入官网下载地址:https://www.mongodb.com/download-center#community ,选择合适的版本下载。 我选择的是最新稳定版本 3.6.3:https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.6.3.tgz 我个人喜欢存放在:`/opt/mongodb` ``` wget -O /opt/mongodb/mongodb-linux-x86_64-3.6.3.tgz https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.6.3.tgz cd /opt/mongodb tar zxvf mongodb-linux-x86_64-3.6.3.tgz mv mongodb-linux-x86_64-3.6.3 mongodb-3.6.3 mkdir -p /data/db ``` ## 启动 **启动 mongodb 服务** ``` cd /opt/mongodb/mongodb-3.6.3/bin ./mongod --dbpath=/data/db ``` **启动 mongodb 客户端** ``` cd /opt/mongodb/mongodb-3.6.3/bin ./mongo ``` ## 脚本 | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | ================================================ FILE: docs/linux/soft/nacos-install.md ================================================ # Nacos 安装配置 > [Nacos](https://nacos.io/zh-cn/) 是一款发现、配置和管理微服务的软件。 ## 1.预备环境准备 Nacos 依赖 [Java](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/) 环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 [Maven](https://maven.apache.org/index.html)环境,请确保是在以下版本环境中安装使用: 1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。 2. 64 bit JDK 1.8+;[下载](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) & [配置](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/)。 3. Maven 3.2.x+;[下载](https://maven.apache.org/download.cgi) & [配置](https://maven.apache.org/settings.html)。 ## 2.下载源码或者安装包 你可以通过源码和发行包两种方式来获取 Nacos。 ### 从 Github 上下载源码方式 ```bash git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos clean install -U ls -al distribution/target/ // change the $version to your actual path cd distribution/target/nacos-server-$version/nacos/bin ``` ### 下载编译后压缩包方式 您可以从 [最新稳定版本](https://github.com/alibaba/nacos/releases) 下载 `nacos-server-$version.zip` 包。 ```bash unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz cd nacos/bin ``` ## 3.启动服务器 ### Linux/Unix/Mac 启动命令(standalone代表着单机模式运行,非集群模式): ``` sh startup.sh -m standalone ``` ### Windows 启动命令: ``` cmd startup.cmd ``` 或者双击startup.cmd运行文件。 ## 4.服务注册&发现和配置管理 ### 服务注册 ``` curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080' ``` ### 服务发现 ``` curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=nacos.naming.serviceName' ``` ### 发布配置 ``` curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld" ``` ### 获取配置 ``` curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test" ``` ## 5.关闭服务器 ### Linux/Unix/Mac ``` sh shutdown.sh ``` ### Windows ``` cmd shutdown.cmd ``` 或者双击shutdown.cmd运行文件。 ## 参考资料 - [Nacos Quick Start](https://nacos.io/zh-cn/docs/quick-start.html) ================================================ FILE: docs/linux/soft/nexus-ops.md ================================================ # Nexus 运维 > Nexus 是一个强大的 Maven 仓库管理器,可以用来搭建 Maven 私服。 > > 关键词:maven, nexus > > 部署环境: > > - Nexus 3.13.0 > - JDK 1.8 > - Maven 3.5.4 ## 一、Nexus 安装 进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20181127203029.png) 本人将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。 这里,如果想通过命令方式直接下载(比如用脚本安装),可以在[官方历史发布版本页面](https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3)中找到合适版本,然后执行以下命令: ```shell # 个人习惯将 nexus 安装在 /opt/maven 目录下 wget -O /opt/maven/nexus-unix.tar.gz http://download.sonatype.com/nexus/3/nexus-3.13.0-01-unix.tar.gz ``` - 【解压】执行 `tar -zxf nexus-unix.tar.gz` 命令,会解压出两个目录: - `nexus-` - 程序目录。包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。 - `nexus-/etc` - 配置目录。 - `nexus-/etc/nexus.properties` - nexus 核心配置文件(默认 etc 目录下有 `nexus-default.properties`,可以基于此修改)。 - `sonatype-work` - 仓库目录。包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。 - [修改环境变量】执行 `vim /etc/profile`,在文件尾部添加以下内容: ``` NEXUS_HOME=/usr/program/nexus2.11.4 export NEXUS_HOME ``` 刷新环境变量:`source /etc/profile` - 【检查安装是否成功】执行 `nexus -version` 查看是否安装成功。 - 【防火墙】 - iptabes - 添加规则:`iptables -I INPUT -p tcp -m tcp --dport 8081 -j ACCEPT` - 载入规则:`/etc/rc.d/init.d/iptables save` - 重启 iptables:`service iptables restart` - firewalld - 添加规则:`firewall-cmd --zone=public --add-port=8081/tcp --permanent` - 载入规则:`firewall-cmd --reload` ## 二、Nexus 使用 ### 启动/停止 Nexus 进入 `nexus-3.13.0-01/bin` 目录,有一个可执行脚本 nexus。 执行 `./nexus`,可以查看允许执行的参数,如下所示,含义可谓一目了然: ```shell $ ./nexus Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload} ``` - 启动 nexus - `./nexus start` - 停止 nexus - `./nexus stop` - 重启 nexus - `./nexus restart` Nexus 的默认启动端口为 `8081`,启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示: ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20181127203131.png) 点击右上角 Sign in 登录,默认用户名/密码为:`admin/admin123`。 ### 配置 maven 仓库 Nexus 中的仓库有以下类型: - `hosted` - 宿主仓库。主要用于部署无法从公共仓库获取的构件(如 oracle 的 JDBC 驱动)以及自己或第三方的项目构件; - `proxy` - 代理仓库。代理公共的远程仓库; - `virtual` - 虚拟仓库。用于适配 Maven 1; - `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。 ![img](https://raw.githubusercontent.com/dunwu/images/master/cs/java/javalib/maven/nexus.png) 建议配置如下: - **hosted 仓库** - **maven-releases** - 存储私有仓库的发行版 jar 包 - **maven-snapshots** - 存储私有仓库的快照版(调试版本) jar 包 - **proxy 仓库** - **maven-central-maven** - 中央库(如果没有配置 mirror,默认就从这里下载 jar 包),从 https://repo1.maven.org/maven2/ 获取资源 - **maven-aliyun** - 国内 maven 仓库,提高访问速度。 - **group 仓库** - **maven-public** - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20181127203156.png) > 其中: > > **maven-central**、**maven-public**、**maven-release**、**maven-snapshot** 仓库是默认配置好的 maven 仓库。maven-central 配置的是 `https://repo1.maven.org/maven2/` 的代理仓库,即 maven 中央仓库地址。 参考配置如下: ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200403165258.png) 推荐配置的代理仓库: - OSS SNAPSHOT 仓库:`http://oss.jfrog.org/artifactory/oss-snapshot-local/` - aliyun 仓库(受限于国内网络,可以通过它来加速):`http://maven.aliyun.com/nexus/content/groups/public/` ### 配置 yum 仓库 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200403201609.png) 推荐配置的 yum 代理仓库: - aliyun yum 仓库:`http://mirrors.aliyun.com/centos` 配置本地 yum: (1)新增 nexus.repo 文件,内容如下: ```ini [base] name=Nexus baseurl= http://:/repository/yum-aliyun/$releasever/os/$basearch/ enabled=1 gpgcheck=0 priority=1 ``` (2)更新 yum 缓存,执行以下命令: ```shell yum clean all yum makecache ``` ### 定时任务 随着 jar 包越来越多,尤其是 SNAPSHOT 包由于不限制重复上传,尤其容易导致磁盘空间膨胀。所以,需要定期进行清理或修复。 Nexus 内置了多个定时任务,可以执行清理。 【示例】定期清理 SNAPSHOST ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20200403173030.png) ## 三、开机自启动 作为常用服务,有必要将 Nexus 设为 `systemd` 服务,以便在断电恢复后自动重启。 配置方法如下: 在 `/lib/systemd/system` 目录下创建 `nexus.service` 文件,内容如下: ```ini [Unit] Description=nexus After=network.target [Service] Type=forking LimitNOFILE=65536 #警告处理 Environment=RUN_AS_USER=root ExecStart=/opt/maven/nexus-3.13.0-01/bin/nexus start ExecReload=/opt/maven/nexus-3.13.0-01/bin/nexus restart ExecStop=/opt/maven/nexus-3.13.0-01/bin/nexus stop Restart=on-failure PrivateTmp=true [Install] WantedBy=multi-user.target ``` 保存后,可以使用以下命令应用 nexus 服务: - `systemctl enable nexus` - 启动 nexus 开机启动 - `systemctl disable nexus` - 关闭 nexus 开机启动 - `systemctl start nexus` - 启动 nexus 服务 - `systemctl stop nexus` - 停止 nexus 服务 - `systemctl restart nexus` - 重启 nexus 服务 > 执行 `systemctl enable nexus` 后,再执行 reboot 重启,重连后,可以检测是否成功开机自动重启。 ## 四、Nexus 和 Maven Nexus 是 maven 私服。现在,Nexus 服务器已经部署好了,如何配合 maven 使用呢? ### 配置 settings.xml 如果要使用 Nexus,还必须在 `settings.xml` 和 `pom.xml` 中配置认证信息。 一份完整的 `settings.xml`: ```xml org.sonatype.plugins D:\Tools\maven\.m2 releases admin admin123 snapshots admin admin123 public * http://10.255.255.224:8081/repository/maven-public/ zp central http://central true true central http://central true true always zp ``` ### 配置 pom.xml 在 `pom.xml` 中添加如下配置,这样就可以执行 `mvn deploy`,将本地构建的 jar、war 等包发布到私服上。 ```xml releases Releases http://10.255.255.224:8081/repository/maven-releases snapshots Snapshot http://10.255.255.224:8081/repository/maven-snapshots ``` > 🔔 注意: > > - `` 和 `` 的 id 必须和 `settings.xml` 配置文件中的 `` 标签中的 id 匹配。 > - `` 标签的地址需要和 maven 私服的地址匹配。 ### 执行 maven 构建 如果要使用 `settings.xml` 中的私服配置,必须通过指定 `-P zp` 来激活 profile。 示例: ```shell # 编译并打包 maven 项目 $ mvn clean package -Dmaven.skip.test=true -P zp # 编译并上传 maven 交付件(jar 包) $ mvn clean deploy -Dmaven.skip.test=true -P zp ``` > 至此,已经可以正常向 Nexus 上传、下载 jar 包。 ## 五、备份和迁移 Nexus 三个重要目录: | 名称 | 目录名 | 重要配置文件 | | :----------------- | :------------- | :------------------------------------------------ | | nexus 主目录 | nexus-2.6.4-02 | conf/nexus.properties 里面有 sonatype-work 的地址 | | sonatype-work 目录 | sonatype-work | nexus/conf/nexus.xml 里面有 storage 的地址 | | storage 目录 | storage | 里面主要是各种程序的 jar 包等 | ### 备份 Nexus 的数据都存储在 sonatype-work 目录,备份 Nexus 数据只需要将其打包即可。 ### 迁移 将原 Nexus 服务器中的 sonatype-work 目录迁移到新 Nexus 服务器的 sonatype-work 目录下。 ## 六、FAQ ### 配置 INSTALL4J_JAVA_HOME 我在工作中遇到 nexus systemctl 服务无法自启动的问题,通过查看状态,发现以下报错: ``` Please define INSTALL4J_JAVA_HOME to point to a suitable JVM ``` 通过排查,找到原因:即使环境上已安装 JDK,且配置了 JAVA_HOME,但 nexus 仍然无法正确找到 JDK,需要在 `/bin/nexus` 中指定 `INSTALL4J_JAVA_HOME_OVERRIDE=` ## 参考资料 - [maven 私库 nexus3 安装及使用](https://blog.csdn.net/clj198606061111/article/details/52200928) - [Nexus 安装 使用说明](https://www.cnblogs.com/jtlgb/p/7473837.html) - [企业级开源仓库 nexus3 实战应用–使用 nexus3 配置 yum 私有仓库](http://www.eryajf.net/2002.html) ================================================ FILE: docs/linux/soft/nodejs-install.md ================================================ # Nodejs 安装 - [安装方法](#安装方法) - [先安装 nvm](#先安装-nvm) - [安装 Nodejs](#安装-nodejs) - [脚本](#脚本) ## 安装方法 ### 先安装 nvm 推荐安装 nvm(Node Version Manager) ,来管理 node.js 版本。 安装步骤如下: (1)执行安装脚本 ``` rm -rf ~/.nvm curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash . ~/.nvm/nvm.sh ``` (2)检验是否安装成功 执行 `nvm --version` 命令。 注意:如果出现 `nvm: command not found` ,关闭终端,然后再打开终端试试。 ### 安装 Nodejs 安装步骤如下: (1)使用 nvm 安装 nodejs 指定版本 执行以下命令: ``` nvm install 8.9.4 nvm use 8.9.4 ``` (2)检验是否安装成功 执行 `node --version` 命令。 注意:如果出现 `node: command not found` ,关闭终端,然后再打开终端试试。 ## 脚本 | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | ## 更多内容 - **引申** - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) ================================================ FILE: docs/linux/soft/rocketmq-install.md ================================================ # RocketMQ 安装部署 - [环境要求](#环境要求) - [下载解压](#下载解压) - [启动 Name Server](#启动-name-server) - [启动 Broker](#启动-broker) - [收发消息](#收发消息) - [关闭服务器](#关闭服务器) - [FAQ](#faq) - [connect to <172.17.0.1:10909> failed](#connect-to-172170110909-failed) - [参考资料](#参考资料) ## 环境要求 - 推荐 64 位操作系统:Linux/Unix/Mac - 64bit JDK 1.8+ - Maven 3.2.x - Git ## 下载解压 进入官方下载地址:https://rocketmq.apache.org/dowloading/releases/,选择合适版本 建议选择 binary 版本。 解压到本地: ```bash > unzip rocketmq-all-4.2.0-source-release.zip > cd rocketmq-all-4.2.0/ ``` ## 启动 Name Server ```bash > nohup sh bin/mqnamesrv & > tail -f ~/logs/rocketmqlogs/namesrv.log The Name Server boot success... ``` ## 启动 Broker ```bash > nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf & > tail -f ~/logs/rocketmqlogs/broker.log The broker[%s, 172.30.30.233:10911] boot success... ``` ## 收发消息 执行收发消息操作之前,不许告诉客户端命名服务器的位置。在 RocketMQ 中有多种方法来实现这个目的。这里,我们使用最简单的方法——设置环境变量 `NAMESRV_ADDR` : ```bash > export NAMESRV_ADDR=localhost:9876 > sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer SendResult [sendStatus=SEND_OK, msgId= ... > sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer ConsumeMessageThread_%d Receive New Messages: [MessageExt... ``` ## 关闭服务器 ```bash > sh bin/mqshutdown broker The mqbroker(36695) is running... Send shutdown request to mqbroker(36695) OK > sh bin/mqshutdown namesrv The mqnamesrv(36664) is running... Send shutdown request to mqnamesrv(36664) OK ``` ## FAQ ### connect to <172.17.0.1:10909> failed 启动后,生产者客户端连接 RocketMQ 时报错: ```java org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to <172.17.0.1:10909> failed at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:357) at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageSync(MQClientAPIImpl.java:343) at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:327) at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:290) at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:688) at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendSelectImpl(DefaultMQProducerImpl.java:901) at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:878) at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:873) at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:369) at com.emrubik.uc.mdm.sync.utils.MdmInit.sendMessage(MdmInit.java:62) at com.emrubik.uc.mdm.sync.utils.MdmInit.main(MdmInit.java:2149) ``` 原因:RocketMQ 部署在虚拟机上,内网 ip 为 10.10.30.63,该虚拟机一个 docker0 网卡,ip 为 172.17.0.1。RocketMQ broker 启动时默认使用了 docker0 网卡,生产者客户端无法连接 172.17.0.1,造成以上问题。 解决方案 (1)干掉 docker0 网卡或修改网卡名称 (2)停掉 broker,修改 broker 配置文件,重启 broker。 修改 conf/broker.conf,增加两行来指定启动 broker 的 IP: ``` namesrvAddr = 10.10.30.63:9876 brokerIP1 = 10.10.30.63 ``` 启动时需要指定配置文件 ```bash nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf & ``` ## 更多内容 - **引申** - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) - **引用** - [RocketMQ 官方文档](http://rocketmq.apache.org/docs/quick-start/) - [RocketMQ 搭建及刨坑](http://laciagin.me/2017/12/07/RocketMQ%E6%90%AD%E5%BB%BA%E5%8F%8A%E5%88%A8%E5%9D%91/) ================================================ FILE: docs/linux/soft/svn-ops.md ================================================ # Svn 运维 > Svn 是 Subversion 的简称,是一个开放源代码的版本控制系统,它采用了分支管理系统。 > > 本文目的在于记录 svn 的安装、配置、使用。 ## 安装 ### 安装 svn ```bash $ yum install -y subversion ``` ### 创建 svn 仓库 ```bash $ mkdir -p /share/svn $ svnadmin create /share/svn $ ls /share/svn conf db format hooks locks README.txt ``` 在 conf 目录下有三个重要的配置文件 - `authz` - 是权限控制文件 - `passwd` - 是帐号密码文件 - `svnserve.conf` - 是 SVN 服务配置文件 ## 配置 ### 配置 svnserve.conf ```bash $ vim /share/svn/conf/svnserve.conf ``` 打开下面的 5 个注释 ```ini anon-access = read #匿名用户可读 auth-access = write #授权用户可写 password-db = passwd #使用哪个文件作为账号文件 authz-db = authz #使用哪个文件作为权限文件 realm = /share/svn # 认证空间名,版本库所在目录 ``` ### 配置 passwd ```bash $ vim /share/svn/conf/passwd ``` 添加新用户的用户名/密码如下: ```ini [users] user1 = 123456 user2 = 123456 user3 = 123456 ``` ### 配置 authz ```bash $ vim /share/svn/conf/authz ``` 指定用户的访问权限(`r` 为读权限;`w` 为写权限): ```ini [/] user1 = rw user2 = rw user3 = rw *= ``` ## 服务器管理 ### 启动关闭 svn ```bash $ svnserve -d -r /share/svn # 启动 svn $ killall svnserve # 关闭 svn ``` ### 开机自启动 svn 方法 安装好 svn 服务后,默认是没有随系统启动自动启动的,而一般我们有要求 svn 服务稳定持续的提供服务。所以,有必要配置开机自启动 svn 服务。 #### Centos7 以前 编辑 `/etc/rc.d/rc.local` 文件: ```bash $ vi /etc/rc.d/rc.local ``` 输入以下内容: ```bash # 开机自动启动 svn,默认端口是 3690 $ /usr/bin/svnserve -d -r /share/svn --listen-port 3690 ``` 注意: 我们在用终端操作的时候,可以直接使用以下命令启动 SVN:`svnserve -d -r /share/svn`,但是在 `/etc/rc.d/rc.local` 文件中必须写上完整的路径! 如果不知道 svnserve 命令安装在哪儿,可以使用 whereis svnserve 查找。 #### Centos7 CentOS 7 中的 `/etc/rc.d/rc.local` 是没有执行权限的,系统建议创建 `systemd service` 启动服务。 找到 svn 的 service 配置文件 `/etc/sysconfig/svnserve` 编辑配置文件 ```bash $ vi /etc/sysconfig/svnserve ``` 将 `OPTIONS="-r /var/svn"` 改为 svn 版本库存放的目录,:wq 保存退出。 执行 `systemctl enable svnserve.service` 重启服务器后,执行 `ps -ef | grep svn` 应该可以看到 svn 服务的进程已经启动。 - 启动一个服务 - systemctl start svnserve.service - 关闭一个服务 - systemctl stop svnserve.service - 重启一个服务 - systemctl restart svnserve.service - 显示一个服务的状态 - systemctl status svnserve.service - 在开机时启用一个服务 - systemctl enable svnserve.service - 在开机时禁用一个服务 - systemctl disable svnserve.service ## 客户端使用 进入 [svn 官方下载地址](https://tortoisesvn.net/downloads.html),选择合适的版本,下载并安装。 新建一个目录,然后打开鼠标右键菜单,选择 **SVN Checkout**。 在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示: ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/20190129175443.png) ## 参考资料 - https://www.cnblogs.com/liuxianan/p/linux_install_svn_server.html - https://blog.csdn.net/testcs_dn/article/details/45395645 - https://www.cnblogs.com/moxiaoan/p/5683743.html - https://blog.csdn.net/realghost/article/details/52396648 ================================================ FILE: docs/linux/soft/tomcat-install.md ================================================ # Tomcat 安装 - [安装](#安装) - [启动](#启动) - [脚本](#脚本) ## 安装 安装步骤如下: (1)下载并解压到本地 进入官网下载地址:https://tomcat.apache.org/download-80.cgi ,选择合适的版本下载。 我选择的是最新稳定版本 8.5.28:http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.28/bin/apache-tomcat-8.5.28.tar.gz 我个人喜欢存放在:`/opt/tomcat` ``` wget -O /opt/tomcat/apache-tomcat-8.5.28.tar.gz http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.28/bin/apache-tomcat-8.5.28.tar.gz cd /opt/tomcat tar zxvf apache-tomcat-8.5.28.tar.gz ``` ## 启动 **启动 tomcat 服务** ``` cd /opt/tomcat/apache-tomcat-8.5.28/bin ./catalina.sh start ``` **停止 tomcat 服务** ``` cd /opt/tomcat/apache-tomcat-8.5.28/bin ./catalina.sh stop ``` ## 脚本 | [安装脚本](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/soft) | ## 更多内容 - **引申** - [操作系统、运维部署总结系列](https://github.com/dunwu/OS) ================================================ FILE: docs/linux/soft/yapi-ops.md ================================================ # YApi 运维 > [YApi](https://github.com/YMFE/yapi) 是一个可本地部署的、打通前后端及 QA 的、可视化的接口管理平台。 > > 本文目的在于记录 svn 的安装、配置、使用。 ![img](https://raw.githubusercontent.com/dunwu/images/master/snap/1562814562978.png) - [1. 普通部署](#1-普通部署) - [1.1. 环境要求](#11-环境要求) - [1.2. 部署](#12-部署) - [1.3. 升级](#13-升级) - [2. Docker 部署](#2-docker-部署) - [2.1. 环境要求](#21-环境要求) - [2.2. 部署](#22-部署) - [3. 参考资料](#3-参考资料) ## 1. 普通部署 ### 1.1. 环境要求 - nodejs(7.6+) - mongodb(2.6+) - git ### 1.2. 部署 #### 方式一. 可视化部署[推荐] 执行 yapi server 启动可视化部署程序,输入相应的配置和点击开始部署,就能完成整个网站的部署。部署完成之后,可按照提示信息,执行 node/{网站路径/server/app.js} 启动服务器。在浏览器打开指定 url, 点击登录输入您刚才设置的管理员邮箱,默认密码(ymfe.org) 登录系统(默认密码可在个人中心修改)。 ```bash $ npm install -g yapi-cli --registry https://registry.npm.taobao.org $ yapi server ``` #### 方式二. 命令行部署 如果 github 压缩文件无法下载,或需要部署到一些特殊的服务器,可尝试此方法 ```bash mkdir yapi cd yapi git clone https://github.com/YMFE/yapi.git vendors //或者下载 zip 包解压到 vendors 目录(clone 整个仓库大概 140+ M,可以通过 `git clone --depth=1 https://github.com/YMFE/yapi.git vendors` 命令减少,大概 10+ M) cp vendors/config_example.json ./config.json //复制完成后请修改相关配置 cd vendors npm install --production --registry https://registry.npm.taobao.org npm run install-server //安装程序会初始化数据库索引和管理员账号,管理员账号名可在 config.json 配置 node server/app.js //启动服务器后,请访问 127.0.0.1:{config.json配置的端口},初次运行会有个编译的过程,请耐心等候 ``` 安装后的目录结构如下: ``` |-- config.json |-- init.lock |-- log `-- vendors |-- CHANGELOG.md |-- LICENSE |-- README.md |-- client |-- common |-- config_example.json |-- doc |-- exts |-- nodemon.json |-- npm-debug.log |-- package.json |-- plugin.json |-- server |-- static |-- test |-- webpack.alias.js |-- yapi-base-flow.jpg |-- ydocfile.js `-- ykit.config.js ``` ### 1.3. 升级 升级项目版本是非常容易的,并且不会影响已有的项目数据,只会同步 vendors 目录下的源码文件。 ``` cd {项目目录} yapi ls //查看版本号列表 yapi update //升级到最新版本 yapi update -v v1.1.0 //升级到指定版本 ``` ## 2. Docker 部署 ### 2.1. 环境要求 - 系统:`CentOS 7.4` - 硬件要求:`1 GB RAM minimum` - ip:`http://192.168.1.121` - docker version:`17.12.1-ce, build 7390fc6` - docker-compose version:`1.18.0, build 8dd22a9` > 建议部署成 http 站点,因 chrome 浏览器安全限制,部署成 https 会导致测试功能在请求 http 站点时文件上传功能异常。--[来源](https://yapi.ymfe.org/devops.html) ### 2.2. 部署 - 一个好心人的维护: - 使用方法: - work path:`mkdir -p /opt/git-data` - clone:`cd /opt/git-data && git clone https://github.com/branchzero/yapi-docker.git` - permission:`chmod -R 777 /opt/git-data` - run command:`cd /opt/git-data/yapi-docker && docker-compose up -d` - open chrome:`http://192.168.1.121:3000` - 初始化管理员账号名:`admin@admin.com`,密码:`ymfe.org` ## 3. 参考资料 - [官方 Github](https://github.com/YMFE/yapi) - [官网在线演示](http://yapi.demo.qunar.com/) - [官方使用手册](https://hellosean1025.github.io/yapi/index.html) ================================================ FILE: docs/mac/soft/ruby-install.md ================================================ # 安装 Ruby ## 安装 rvm ### 下载安装 rvm - 先安装好 RVM - RVM 是一个便捷的多版本 Ruby 环境的管理和切换工具 官网:[https://rvm.io/](https://links.jianshu.com/go?to=https%3A%2F%2Frvm.io%2F) - 在终端控制台命令: $ curl -sSL [https://get.rvm.io](https://links.jianshu.com/go?to=https%3A%2F%2Fget.rvm.io) | bash -s stable 之后按回车键 - 截止到目前 最新的版本是 1.29.9 - 如下所示: ```shell :~ admin$ curl -sSL https://get.rvm.io | bash -s stable Downloading https://github.com/rvm/rvm/archive/1.29.1.tar.gz Downloading https://github.com/rvm/rvm/releases/download/1.29.1/1.29.1.tar.gz.asc Found PGP signature at: 'https://github.com/rvm/rvm/releases/download/1.29.1/1.29.1.tar.gz.asc', but no GPG software exists to validate it, skipping. Installing RVM to /Users/admin/.rvm/ Adding rvm PATH line to /Users/admin/.profile /Users/admin/.mkshrc /Users/admin/.bashrc /Users/admin/.zshrc. Adding rvm loading line to /Users/admin/.profile /Users/admin/.bash_profile /Users/admin/.zlogin. Installation of RVM in /Users/admin/.rvm/ is almost complete: * To start using RVM you need to run `source /Users/admin/.rvm/scripts/rvm` in all your open shell windows, in rare cases you need to reopen all shell windows. # admin, # # Thank you for using RVM! # We sincerely hope that RVM helps to make your life easier and more enjoyable!!! # # ~Wayne, Michal & team. In case of problems: https://rvm.io/help and https://twitter.com/rvm_io ``` 等待一两分钟,成功安装好 RVM。 ### 设置环境变量 ```shell # 1.2 然后,载入 RVM 环境: $ source /etc/profile.d/rvm.sh $ sudo chmod -R 777 /usr/local/rvm/archives # 1.3 修改 RVM 下载 Ruby 的源,到 Ruby China 的镜像 $ echo "ruby_url=https://cache.ruby-china.com/pub/ruby" > /usr/local/rvm/user/db $ rvm install 2.7.0 --disable-binary // 如下所示: AdmindeiMac-4:~ admin$ source ~/.rvm/scripts/rvm AdmindeiMac-4:~ admin$ echo "ruby_url=https://cache.ruby-china.org/pub/ruby" > ~/.rvm/user/db AdmindeiMac-4:~ admin$ rvm -v rvm 1.29.9 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io/] 如果能显示版本号,则安装成功。 ``` ## 参考资料 - [MAC_Ruby 安装](https://www.jianshu.com/p/c073e6fc01f5) ================================================ FILE: docs/package.json ================================================ { "name": "linux-tutorial", "version": "1.0.0", "private": true, "scripts": { "clean": "rimraf dist && rimraf .temp", "build": "npm run clean && vuepress build ./ --temp .temp", "start": "vuepress dev ./ --temp .temp", "lint": "markdownlint -r markdownlint-rule-emphasis-style -c ./.markdownlint.json **/*.md -i node_modules", "lint:fix": "markdownlint -f -r markdownlint-rule-emphasis-style -c ./.markdownlint.json **/*.md -i node_modules", "show-help": "vuepress --help", "view-info": "vuepress view-info ./ --temp .temp" }, "devDependencies": { "@vuepress/plugin-active-header-links": "^1.8.2", "@vuepress/plugin-back-to-top": "^1.8.2", "@vuepress/plugin-medium-zoom": "^1.8.2", "@vuepress/plugin-pwa": "^1.8.2", "@vuepress/theme-vue": "^1.8.2", "markdownlint-cli": "^0.25.0", "markdownlint-rule-emphasis-style": "^1.0.1", "rimraf": "^3.0.1", "vue-toasted": "^1.1.25", "vuepress": "^1.8.2", "vuepress-plugin-flowchart": "^1.4.2" }, "dependencies": { "moment": "^2.29.1" } } ================================================ FILE: scripts/deploy.sh ================================================ #!/usr/bin/env sh # ------------------------------------------------------------------------------ # gh-pages 部署脚本 # @author Zhang Peng # @since 2020/2/10 # ------------------------------------------------------------------------------ # 装载其它库 ROOT_DIR=$(cd `dirname $0`/..; pwd) # 确保脚本抛出遇到的错误 set -e cd ${ROOT_DIR}/docs # 生成静态文件 npm install npm run build # 进入生成的文件夹 cd dist # 如果是发布到自定义域名 # echo 'www.example.com' > CNAME git init git checkout -b gh-pages && git add . git commit -m 'deploy' # 如果发布到 https://.github.io/ if [[ ${GITHUB_TOKEN} && ${GITEE_TOKEN} ]]; then echo "使用 token 公钥部署 gh-pages" # ${GITHUB_TOKEN} 是 Github 私人令牌;${GITEE_TOKEN} 是 Gitee 私人令牌 # ${GITHUB_TOKEN} 和 ${GITEE_TOKEN} 都是环境变量;travis-ci 构建时会传入变量 git push --force --quiet "https://dunwu:${GITHUB_TOKEN}@github.com/dunwu/linux-tutorial.git" gh-pages git push --force --quiet "https://turnon:${GITEE_TOKEN}@gitee.com/turnon/linux-tutorial.git" gh-pages else echo "使用 ssh 公钥部署 gh-pages" git push -f git@github.com:dunwu/linux-tutorial.git gh-pages git push -f git@gitee.com:turnon/linux-tutorial.git gh-pages fi cd ${ROOT_DIR}