diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..220f76d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +#========================================= +# Author : Colben +#========================================= + +/public/ +/resources/ +/themes/ + diff --git a/README.md b/README.md index fd8e7a0..419f716 100644 --- a/README.md +++ b/README.md @@ -48,15 +48,11 @@ - 克隆本仓库的前端目录 ```bash docker exec -ti gitea /bin/bash # 进入 gitea 容器 - mkdir /html/www.colben.cn - chown gitea.www-data /html/www.colben.cn + chown gitea.www-data /html su - gitea - cd /html/www.colben.cn - git init - git remote add -f origin https://git.colben.cn/colben/www.colben.cn.git - #git config core.sparsecheckout true - #echo 'public' > .git/info/sparse-checkout - git pull origin master + cd /html + git clone https://git.colben.cn/colben/www.colben.cn.git hugo + exit # 退出容器 ``` diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..f7ccd84 --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,9 @@ +--- +title: "{{ replace .TranslationBaseName "-" " " | title }}" +date: {{ .Date }} +lastmod: {{ .Date }} +draft: true +tags: [] +categories: [] +--- + diff --git a/archetypes/default.md.origin b/archetypes/default.md.origin new file mode 100644 index 0000000..f4ff840 --- /dev/null +++ b/archetypes/default.md.origin @@ -0,0 +1,43 @@ +--- +title: "{{ replace .TranslationBaseName "-" " " | title }}" +date: {{ .Date }} +lastmod: {{ .Date }} +draft: true +keywords: [] +description: "" +tags: [] +categories: [] +author: "" + +# You can also close(false) or open(true) something for this content. +# P.S. comment can only be closed +comment: false +toc: false +autoCollapseToc: false +postMetaInFooter: false +hiddenFromHomePage: false +# You can also define another contentCopyright. e.g. contentCopyright: "This is another copyright." +contentCopyright: false +reward: false +mathjax: false +mathjaxEnableSingleDollar: false +mathjaxEnableAutoNumber: false + +# You unlisted posts you might want not want the header or footer to show +hideHeaderAndFooter: false + +# You can enable or disable out-of-date content warning for individual post. +# Comment this out to use the global config. +#enableOutdatedInfoWarning: false + +flowchartDiagrams: + enable: false + options: "" + +sequenceDiagrams: + enable: false + options: "" + +--- + + diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..f312f41 --- /dev/null +++ b/config.toml @@ -0,0 +1,224 @@ +baseURL = "https://colben.cn/" +languageCode = "en" +defaultContentLanguage = "zh-cn" # en / zh-cn / ... (This field determines which i18n file to use) +title = "Colben Notes" +preserveTaxonomyNames = true +enableRobotsTXT = true +enableEmoji = true +theme = ["hugo-search-fuse-js", "even"] +enableGitInfo = false # use git commit log to generate lastmod record # 可根据 Git 中的提交生成最近更新记录。 + +# Syntax highlighting by Chroma. NOTE: Don't enable `highlightInClient` and `chroma` at the same time! +pygmentsOptions = "linenos=table" +pygmentsCodefences = true +pygmentsUseClasses = true +pygmentsCodefencesGuessSyntax = true + +hasCJKLanguage = true # has chinese/japanese/korean ? # 自动检测是否包含 中文\日文\韩文 +paginate = 10 # 首页每页显示的文章数 +disqusShortname = "" # disqus_shortname +googleAnalytics = "" # UA-XXXXXXXX-X +copyright = "" # default: author.name ↓ # 默认为下面配置的author.name ↓ + +[markup] + defaultMarkdownHandler = "blackfriday" + +[author] # essential # 必需 + name = "Colben" + +[sitemap] # essential # 必需 + changefreq = "weekly" + priority = 0.5 + filename = "sitemap.xml" + +[[menu.main]] # config your menu # 配置目录 + name = "主页" + weight = 10 + identifier = "home" + url = "/" +[[menu.main]] + name = "归档" + weight = 20 + identifier = "archives" + url = "/post/" +[[menu.main]] + name = "标签" + weight = 30 + identifier = "tags" + url = "/tags/" +[[menu.main]] + name = "分类" + weight = 40 + identifier = "categories" + url = "/categories/" + +[params] + version = "4.x" # Used to give a friendly message when you have an incompatible update + debug = false # If true, load `eruda.min.js`. See https://github.com/liriliri/eruda + + since = "2019" # Site creation time # 站点建立时间 + # use public git repo url to link lastmod git commit, enableGitInfo should be true. + # 指定 git 仓库地址,可以生成指向最近更新的 git commit 的链接,需要将 enableGitInfo 设置成 true. + gitRepo = "https://git.colben.cn/colben/www.colben.cn.git" + + # site info (optional) # 站点信息(可选,不需要的可以直接注释掉) + logoTitle = "" # default: the title value # 默认值: 上面设置的title值 + keywords = ["colben","notes"] + description = "personal blogs" + + # paginate of archives, tags and categories # 归档、标签、分类每页显示的文章数目,建议修改为一个较大的值 + archivePaginate = 16 + + # show 'xx Posts In Total' in archive page ? # 是否在归档页显示文章的总数 + showArchiveCount = true + + # The date format to use; for a list of valid formats, see https://gohugo.io/functions/format/ + dateFormatToUse = "2006-01-02" + + # show word count and read time ? # 是否显示字数统计与阅读时间 + moreMeta = false + + # Syntax highlighting by highlight.js + highlightInClient = false + + # 一些全局开关,你也可以在每一篇内容的 front matter 中针对单篇内容关闭或开启某些功能,在 archetypes/default.md 查看更多信息。 + # Some global options, you can also close or open something in front matter for a single post, see more information from `archetypes/default.md`. + toc = true # 是否开启目录 + autoCollapseToc = false # Auto expand and collapse toc # 目录自动展开/折叠 + fancybox = true # see https://github.com/fancyapps/fancybox # 是否启用fancybox(图片可点击) + + # mathjax + mathjax = false # see https://www.mathjax.org/ # 是否使用mathjax(数学公式) + mathjaxEnableSingleDollar = false # 是否使用 $...$ 即可進行inline latex渲染 + mathjaxEnableAutoNumber = false # 是否使用公式自动编号 + mathjaxUseLocalFiles = false # You should install mathjax in `your-site/static/lib/mathjax` + + postMetaInFooter = true # contain author, lastMod, markdown link, license # 包含作者,上次修改时间,markdown链接,许可信息 + linkToMarkDown = false # Only effective when hugo will output .md files. # 链接到markdown原始文件(仅当允许hugo生成markdown文件时有效) + contentCopyright = '' # e.g. 'CC BY-NC-ND 4.0' + + changyanAppid = "" # Changyan app id # 畅言 + changyanAppkey = "" # Changyan app key + + livereUID = "" # LiveRe UID # 来必力 + + baiduPush = false # baidu push # 百度 + baiduAnalytics = "" # Baidu Analytics + baiduVerification = "" # Baidu Verification + googleVerification = "" # Google Verification # 谷歌 + + # Link custom CSS and JS assets + # (relative to /static/css and /static/js respectively) + customCSS = [] + customJS = [] + + uglyURLs = false # please keep same with uglyurls setting + + [params.publicCDN] # load these files from public cdn # 启用公共CDN,需自行定义 + enable = true + jquery = '' + slideout = '' + fancyboxJS = '' + fancyboxCSS = '' + timeagoJS = '' + timeagoLocalesJS = '' + flowchartDiagramsJS = ' ' + sequenceDiagramsCSS = '' + sequenceDiagramsJS = ' ' + + # Display a message at the beginning of an article to warn the readers that it's content may be outdated. + # 在文章开头显示提示信息,提醒读者文章内容可能过时。 + [params.outdatedInfoWarning] + enable = false + hint = 30 # Display hint if the last modified time is more than these days ago. # 如果文章最后更新于这天数之前,显示提醒 + warn = 180 # Display warning if the last modified time is more than these days ago. # 如果文章最后更新于这天数之前,显示警告 + + #[params.gitment] # Gitment is a comment system based on GitHub issues. see https://github.com/imsun/gitment + # owner = "colben" # Your GitHub ID + # repo = "comments" # The repo to store comments + # clientId = "1480b0d51916c635576f" # Your client ID + # clientSecret = "86344d4eecfa8e1cc1ee1e099f8489f731b43c77" # Your client secret + + [params.utterances] # https://utteranc.es/ + owner = "" # Your GitHub ID + repo = "" # The repo to store comments + + [params.gitalk] # Gitalk is a comment system based on GitHub issues. see https://github.com/gitalk/gitalk + owner = "" # Your GitHub ID + repo = "" # The repo to store comments + clientId = "" # Your client ID + clientSecret = "" # Your client secret + + # Valine. + # You can get your appid and appkey from https://leancloud.cn + # more info please open https://valine.js.org + [params.valine] + enable = false + appId = '你的appId' + appKey = '你的appKey' + notify = false # mail notifier , https://github.com/xCss/Valine/wiki + verify = false # Verification code + avatar = 'mm' + placeholder = '说点什么吧...' + visitor = false + + [params.flowchartDiagrams]# see https://blog.olowolo.com/example-site/post/js-flowchart-diagrams/ + enable = false + options = "" + + [params.sequenceDiagrams] # see https://blog.olowolo.com/example-site/post/js-sequence-diagrams/ + enable = false + options = "" # default: "{theme: 'simple'}" + + [params.busuanzi] # count web traffic by busuanzi # 是否使用不蒜子统计站点访问量 + enable = false + siteUV = true + sitePV = true + pagePV = true + + [params.reward] # 文章打赏 + enable = false + wechat = "/path/to/your/wechat-qr-code.png" # 微信二维码 + alipay = "/path/to/your/alipay-qr-code.png" # 支付宝二维码 + + #[params.social] # 社交链接 + # a-email = "mailto:colbenlee@163.com" + # #b-stack-overflow = "http://localhost:1313" + # c-twitter = "https://twitter.com/ColbenLee" + # #d-facebook = "http://localhost:1313" + # #e-linkedin = "http://localhost:1313" + # #f-google = "http://localhost:1313" + # g-github = "https://github.com/colben" + # #h-weibo = "http://localhost:1313" + # #i-zhihu = "http://localhost:1313" + # #j-douban = "http://localhost:1313" + # #k-pocket = "http://localhost:1313" + # #l-tumblr = "http://localhost:1313" + # #m-instagram = "http://localhost:1313" + # n-gitlab = "https://git.colben.cn" + # #o-bilibili = "http://localhost:1313" + +# See https://gohugo.io/about/hugo-and-gdpr/ +[privacy] + [privacy.googleAnalytics] + anonymizeIP = true # 12.214.31.144 -> 12.214.31.0 + [privacy.youtube] + privacyEnhanced = true + +# 将下面这段配置取消注释可以使 hugo 生成 .md 文件 +# Uncomment these options to make hugo output .md files. +#[mediaTypes] +# [mediaTypes."text/plain"] +# suffixes = ["md"] +# +#[outputFormats.MarkDown] +# mediaType = "text/plain" +# isPlainText = true +# isHTML = false +# +#[outputs] +# home = ["HTML", "RSS"] +# page = ["HTML", "MarkDown"] +# section = ["HTML", "RSS"] +# taxonomy = ["HTML", "RSS"] +# taxonomyTerm = ["HTML"] diff --git a/content/about.md b/content/about.md new file mode 100644 index 0000000..fd2f15c --- /dev/null +++ b/content/about.md @@ -0,0 +1,27 @@ +--- +title: "关于" +date: 2019-10-29T17:38:52+08:00 +lastmod: 2019-10-29T17:44:00+08:00 +menu: "main" +weight: 50 +--- + +
![Richard Matthew Stallman](/img/richard-stallman.png)
+

俊赛潘安,才比管乐

+
+ +### **IT 运维工作注意事项** +- 无论世界怎样变化,用户总会有问题 +- 当你拿不准时,请重启机器。 +- 迟早有一天会遇到一个忘了插电源的人说电脑启动不起来。 +- 会逐渐害怕来电话,因为没有人会通过技术支持电话向我道早安. +- 在通话的一开始没有用户会告诉你所有的事情真相 +- "我没有做任何事"和"突然间就变成这样了"是用户的咒语,作为一个技术支持人员,你要做的事是识破用户的谎言,解决问题不过是一件附带的事 +- 有些用户永远都不会去学习,这意味着运维永远都不会失业 +- 要一直保持平静的语气。 +- 无论你在做什么,都不要恐慌。 +- 永远都应该像这样回答用户的问题: "相信我,我知道我在做什么" +- 修理一台电脑要比指出损坏原因要简单的多 +- 用户总想知道问题被解决的原因,如果你并不太清楚请尽管撒谎,他们永远都不会知道的。例如说: "一个偏离的电子进入了处理器然后问题就产生了......" +- 同行之间探讨问题,如有可能尽量向年龄最小的人询问,因为只有他们会说实话。 + diff --git a/content/others.md b/content/others.md new file mode 100644 index 0000000..41055e4 --- /dev/null +++ b/content/others.md @@ -0,0 +1,44 @@ +--- +title: "其他" +date: 2019-10-30T13:43:55+08:00 +lastmod: 2019-10-30T13:43:55+08:00 +menu: "main" +weight: 60 +--- + +# Linux +- [**Linux监控命令图文详述**](http://www.linuxidc.com/Linux/2015-01/111577.htm) +- [**Linux crontab 命令详细用法及示例**](http://www.linuxidc.com/Linux/2015-03/114339.htm) +- [**Linux下top命令详解**](http://www.linuxidc.com/Linux/2015-04/116101.htm) +- [**Ubuntu 通过 Live CD 更新Grub恢复引导Boot Menu**](http://www.linuxidc.com/Linux/2015-04/116451.htm) +- [**Kickstart配置文件超详细解析**](http://www.linuxidc.com/Linux/2017-08/146168.htm) +- [**PXE+Kickstart无人值守安装CentOS 7**](http://www.linuxidc.com/Linux/2017-08/146169.htm) +- [**PXE+Kickstart无人值守安装CentOS 6**](http://www.linuxidc.com/Linux/2017-08/146170.htm) +- [**Cobbler无人值守批量安装Linux系统**](http://www.linuxidc.com/Linux/2017-08/146171.htm) +- [**PXE+DHCP+TFTP+Cobbler 无人值守安装CentOS 7**](http://www.linuxidc.com/Linux/2017-09/146705.htm) +- [**Linux下搭建无人执守安装服务器**](http://www.linuxidc.com/Linux/2017-04/143182.htm) + +# Network +- [**关于TCP连接建立与终止那点事**](http://www.linuxidc.com/Linux/2015-09/122777.htm) + +# Database +- [**Oracle Linux 5.8安装Oracle 11g RAC**](http://www.linuxidc.com/Linux/2013-05/84251.htm) +- [**RAC环境数据库重启实例**](http://www.linuxidc.com/Linux/2013-08/88855.htm) +- [**使用Oracle 的 imp ,exp 命令实现数据的导入导出**](http://blog.csdn.net/studyvcmfc/article/details/5674290) +- [**ORACLE EXPDP命令使用详细**](http://blog.csdn.net/zftang/article/details/6387325) +- [**控制文件和控制文件的备份**](http://blog.csdn.net/seertan/article/details/8449050) +- [**MySQL如何通过EXPLAIN分析SQL的执行计划**](https://www.linuxidc.com/Linux/2018-08/153354.htm) + +# Container +- [**Docker 终极指南**](http://www.linuxidc.com/Linux/2015-01/111631.htm) + +# Python +- [**Python 的 OptionParser 模块**](http://www.it165.net/pro/html/201211/4140.html) + +# Firewalld +- [**Iptables防火墙规则使用详解**](https://www.linuxidc.com/Linux/2018-08/153378.htm) + +# Dev +- [**Make 命令教程详解**](http://www.linuxidc.com/Linux/2015-06/118278.htm) +- [**深入理解Java内存与垃圾回收调优**](https://www.linuxidc.com/Linux/2018-08/153457.htm) + diff --git a/content/post/ansible.md b/content/post/ansible.md new file mode 100644 index 0000000..602d487 --- /dev/null +++ b/content/post/ansible.md @@ -0,0 +1,56 @@ +--- +title: "Ansible" +date: 2019-10-30T11:38:39+08:00 +lastmod: 2019-10-30T11:38:39+08:00 +tags: ["ansible"] +categories: ["dev/ops"] +--- + +# CentOS7 安装 ansible +```bash +yum install ansible +``` + +# 修改配置文件 /etc/ansible/ansible.conf +``` +# 取消 ssh key 验证 +host_key_checking = False +# 改用 debug 输出模式 +stdout_callback = debug +``` + +# 修改配置文件 hosts +``` +# 增加 tomcat 服务器组 +[tomcat] +tomcat101 ansible_ssh_host=192.168.1.101 +tomcat102 ansible_ssh_host=192.168.1.102 +tomcat103 ansible_ssh_host=192.168.1.103 +# 增加 mysql 服务器组 +[mysql] +mysql201 ansible_ssh_host=192.168.1.201 ansible_ssh_pass=111111 +mysql202 ansible_ssh_host=192.168.1.202 +mysql203 ansible_ssh_host=192.168.1.203 +# 全局设置全部服务器的默认设置 +[all:vars] +ansible_ssh_port=22 +ansible_ssh_user=root +ansible_ssh_pass=123456 +``` + +# authorized_key 模块 +```bash +# 分发密钥 +ansible all -m authorized_key -a "user=root key='{{ lookup('file', '/root/.ssh/id_rsa.pub') }}'" +``` + +# 简单使用 +- 批量设置主机名为资产名 + ```bash + ansible all -m shell -a 'hostnamectl set-hostname {{inventory_hostname}}' + ansible all -m shell -a 'echo "{{ansible_ssh_host}} {{inventory_hostname}}" >> /etc/hosts' + ``` + +# ansible 常用 roles +- [https://gitee.com/colben/ansible](https://gitee.com/colben/ansible) + diff --git a/content/post/archlinux-install.md b/content/post/archlinux-install.md new file mode 100644 index 0000000..e98cc6e --- /dev/null +++ b/content/post/archlinux-install.md @@ -0,0 +1,211 @@ +--- +title: "安装 Archlinux" +date: 2021-07-04T11:23:00+08:00 +lastmod: 2021-07-04T11:23:00+08:00 +keywords: [] +tags: ["archlinux"] +categories: ["os"] +--- + +# U 盘启动,进入 archlinux live +- 下载 archlinux 镜像 + ```bash + curl -LO https://mirrors.tuna.tsinghua.edu.cn/archlinux/iso/2021.07.01/archlinux-2021.07.01-x86_64.iso + ``` + +- 把镜像写进一个无用的 U 盘(/dev/sdX) + ```bash + dd if=archlinux-2021.07.01-x86_64.iso bs=1M of=/dev/sdX + ``` + +- 使用该 U 盘启动自己的 PC,进入 archlinux live + +# 联网,硬盘分区,安装系统 +- 插上网线,自动分配地址,验证是否可以连接外网 + ```bash + curl -I https://www.baidu.com + # 返回 200 + ``` + +- 规划硬盘,选择一个无用硬盘(/dev/sdX),规划 efi 分区和根分区 + ```bash + # fdisk 规划分区 + fdisk /dev/sdX + g # 创建新的 gpt 分区表 + n # 创建一个新分区 + # 直接回车 + # 直接回车 + +256M # 指定大小 + t # 更改分区类型 + # 直接回车 + 1 # 选择 EFI System + n # 创建一个新分区 + # 直接回车 + # 直接回车 + +XXXG # 指定根分区大小 + w # 保存并退出 fdisk + + 官网还有 swap 分区,现在内存都比较大,不需要 swap 了 + ``` + +- 格式化分区 + ```bash + mkfs.fat -F32 /dev/sdX1 + mkfs.ext4 /dev/sdX2 + ``` + +- 挂载分区 + ```bash + mount /dev/sdX2 /mnt + mkdir /mnt/EFI + mount /dev/sdX1 /mnt/EFI + ``` + +- 修改软件源(vim /etc/pacman.d/mirrorlist),只保留中国源 + ``` + ## China + Server = http://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.bit.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.aliyun.com/archlinux/$repo/os/$arch + ## China + Server = https://mirrors.cloud.tencent.com/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.zju.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.xjtu.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.163.com/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.sohu.com/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.neusoft.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirrors.shu.edu.cn/archlinux/$repo/os/$arch + ## China + Server = http://mirror.lzu.edu.cn/archlinux/$repo/os/$arch + ``` + +- 安装系统包 + ```bash + pacstrap /mnt base linux linux-firmware + # 这里会从中国源里下载安装包并安装,具体时间就看网速了 + ``` + +- 生成 fstab + ```bash + genfstab -U /mnt >> /mnt/etc/fstab + ``` + +- chroot 进入新系统 + ```bash + arch-chroot /mnt /bin/bash + ``` + +# 配置新安装的系统 +- 设置时区 + ```bash + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + ``` + +- 编辑 /etc/locale.gen,选择 en_US 和 zh_CN + ```bash + sed -i \ + -e '/^#en_US.UTF-8/s/^#//' \ + -e '/^#zh_CN/s/^#//' \ + /etc/locale.gen + + locale-gen + + # 生成 /etc/locale.conf + echo 'LANG=zh_CN.UTF-8' > /etc/locale.conf + ``` + +- 设置主机名(xxxx) + ```bash + echo xxxx > /etc/hostname + ``` + +- 安装内核 + ```bash + mkinitcpio -P + ``` + +- 设置 root 密码 + ```bash + passwd + ``` + +- 创建一个普通用户(xxxx),用于自己日常使用 + ```bash + useradd -m xxxx + passwd xxxx + echo "xxxx ALL=(ALL) ALL" > /etc/sudoers.d/xxxx + ``` + +- 安装其他必须包 + ```bash + pacman -S grub networkmanager + pacman -S intel-ucode # intel 处理器 + pacman -S amd-code # amd 处理器 + ``` + +- 创建 efi 启动项(xxxx) + ```bash + grub-install --target=x86_64-efi --efi-directory=/EFI --bootloader-id=xxxx + ``` + +- 生成 grub.cfg + ```bash + grub-mkconfig -o /boot/grub/grub.cfg + ``` + +- 退出新系统,返回 archlinux live + ```bash + exit + ``` + +# 卸载硬盘,重启 +- 卸载硬盘 + ```bash + umount -R /mnt + ``` + +- 重启 + ```bash + reboot + ``` + +# 日常使用 +- 登陆普通用户 +- 启动 NetworkManager,并设置开机自动启动 + ```bash + sudo systemctl start NetworkManager + sudo systemctl enable NetworkManager + ``` + +- 连接网络 + ```bash + nmtui + ``` + +- 安装常用命令行软件 + ```bash + sudo pacman -S ctags cscope tmux expect git p7zip unrar zip unzip \ + ntfs-3g espeak hostapd dnsmasq openssh tcpdump vim cdrtools \ + tree ethtool openbsd-netcat arch-install-scripts + ``` + +- 安装 gnome 桌面 + ```bash + sudo pacman -S wqy-zenhei ttf-liberation + sudo pacman -S gnome + sudo systemctl enable gdm.service + sudo pacman -S gvim file-roller freerdp mpv ibus-sunpinyin + ``` + +- [Gnome 主题网站](https://gnome-look.org) + diff --git a/content/post/awk.md b/content/post/awk.md new file mode 100644 index 0000000..2ae113e --- /dev/null +++ b/content/post/awk.md @@ -0,0 +1,141 @@ +--- +title: "awk 命令" +date: 2019-10-30T00:47:01+08:00 +lastmod: 2019-10-30T00:47:01+08:00 +keywords: [] +tags: ["awk"] +categories: ["shell"] +--- + +# 格式 +- awk -F '分隔符正则' -v 变量名=值 'BEGIN{动作} 条件{动作} END{动作}' 文件1 文件2 ... +- awk -F '分隔符正则' -f awk脚本文件名 文件1 文件2 ... + +# 常用内置变量 +变量 | 说明 +---- | ---- +$0 | 当前记录 +$1~$n | 当前记录的第n个字段,字段间由FS分隔 +FS | 输入字段分隔符 默认是空格 +NF | 当前记录中的字段个数,就是有多少列 +NR | awk 开始执行程序后所读取的数据行数,就是行号,从1开始 +RS | 输入的记录他隔符默 认为换行符 +OFS | 输出字段分隔符 默认也是空格 +ORS | 输出的记录分隔符,默认为换行符 +FNR | 当前记录数,awk每打开一个新文件,FNR便从0重新累计 +ARGC | 命令行参数个数 +ARGV | 命令行参数字典,ARGV[0] 是 'awk',ARGV[1] 是 FILENAME +FILENAME | 当前输入文件的名字 +IGNORECASE | 如果为真,则进行忽略大小写的匹配 +ENVIRON | UNIX环境变量,如 ENVIRON["PATH"] +FIELDWIDTHS | 输入字段宽度的空白分隔字符串 +OFMT | 数字的输出格式(默认值是%.6g) +RSTART | 被 match 匹配函数匹配的字符串位置 +RLENGTH | 被 match 匹配函数匹配的字符串长度 + +# 函数 +### 常用内置函数 +函数 | 说明 +---- | ---- +int(x) | 返回 x 的整数部分 +rand() | 返回 0 到 1 之间的浮点数 +gsub(Ere, "dest", str) | 把 str 中匹配扩展正则 Ere 的全部子串换成字符串 "dest" +sub(Ere, "dest", str) | 把 str 中匹配扩展正则 Ere 的第一个子串换成字符串 "dest" +substr(str, start, len) | 返回 str 中从 start 开始(长度为 len )的子串 +index(str1, str2) | 返回 str1 中出现 str2 的位置,如果未找到,返回 0 +length(str) | 返回 str 的字符个数,如果未指定 str,返回 $0 包含字符个数 +blength(str) | 返回 str 的字节个数,如果未指定 str,返回 $0 包含字节个数 +match(str, Ere) | 返回 str 中匹配扩展正则 Ere 的位置,如果未找到,返回 0 +split(str, arr, Ere) | 把 str 按照扩展正则 Ere 切分成字典,存储到 arr 中,返回字典长度 +tolower(str) | 把 str 换成小写 +toupper(str) | 把 str 换成大写 +sprintf("%s-%d", "abcd", 1234) | 返回 abcd-1234 +strtonum(str) | 返回十进制数字 +delete arr[n] | 删除字典/字典的对应元素 +getline | 读入当前行的下一行,重写变量 0 +next | 停止处理当前记录,开始处理下一行 +nextfile | 停止处理当前文件,开始处理下一个文件 +system(shell-command) | 返回命令退出状态 +exit n | 终止 awk,返回 n + +### 自定义函数 +- 格式 + ```awk + function fun_name(arg1, arg2, ...){ + #函数体 + return + } + ``` + +# 判断语句 +```awk +if(条件){ + # 语句 +}else if(条件){ + # 语句 +}else{ + # 语句 +} +``` + +# 循环语句 +- for + ```awk + for(初始化;条件;变化){ + # 语句 + } + for(变量 in 字典){ + # 语句 + } + ``` +- while + ```awk + while(条件){ + # 语句 + } + ``` +- do while + ```awk + do{ + # 语句 + }while(条件) + ``` +- break 退出当前循环体 +- continue 退出本次循环,继续下一次循环 + +# 脚本 +```awk +#!/usr/bin/awk -f + +# 自定义的变量和函数 +# ... + +# 从这里开始执行 +BEGIN{ + # 语句 +} +条件{ + # 语句 +} +END{ + # 语句 +} +``` + +# 其他说明 +- 变量在使用时直接赋值即可,无需提前声明或定义 +- 个人认为,awk 没有数组只有字典,数组是键为整数的字典 +- 运算符(+, -, \*, /, ++, -- 等)和关系符操作(>, >=, <, <=, ~, !~ 等)与 C 基本一致,也支持三目运算符(条件?值1:值2) +- shell 中 awk 执行显式命令时,加载 shell 中的变量 + ```bash + #!/bin/bash + shell_value='abcd' + echo | awk '{print "'$shell_value'"}' + # 输出 abcd + ``` + +# 参考 +- man awk +- [其他内置函数](https://www.gnu.org/software/gawk/manual/html_node/Built_002din.html#Built_002din) +- [gnu awk 手册](https://www.gnu.org/software/gawk/manual/gawk.html) + diff --git a/content/post/bond.md b/content/post/bond.md new file mode 100644 index 0000000..eeac0ad --- /dev/null +++ b/content/post/bond.md @@ -0,0 +1,140 @@ +--- +title: "CentOS7 双网卡绑定" +date: 2019-10-30T11:07:30+08:00 +lastmod: 2019-10-30T11:07:30+08:00 +keywords: [] +tags: ["centos", "bond"] +categories: ["network"] +--- + +# bond 概要 +### 什么是 bond +- 网卡bond是通过把多张网卡绑定为一个逻辑网卡,实现本地网卡的冗余,带宽扩容和负载均衡。在应用部署中是一种常用的技术。 +### bond的模式种类 +- Mode=0(balance-rr) 表示负载分担round-robin,和交换机的聚合强制不协商的方式配合 +- Mode=1(active-backup) 表示主备模式,只有一块网卡是active,另外一块是备的standby + - **如果交换机配的是捆绑,将不能正常工作,因为交换机往两块网卡发包,有一半包是丢弃的** +- Mode=2(balance-xor) 表示XOR Hash负载分担,和交换机的聚合强制不协商方式配合(需要xmit_hash_policy) +- Mode=3(broadcast) 表示所有包从所有interface发出,这个不均衡,只有冗余机制...和交换机的聚合强制不协商方式配合 +- Mode=4(802.3ad) 表示支持802.3ad协议,和交换机的聚合LACP方式配合(需要xmit_hash_policy) +- Mode=5(balance-tlb) 是根据每个slave的负载情况选择slave进行发送,接收时使用当前轮到的slave +- Mode=6(balance-alb) 在5的tlb基础上增加了rlb + +# CentOS7 配置 bond +### 环境 +- 操作系统 CentOS7.6,禁用 NetworkManager 服务 +- 物理网卡 eth0, eth1 绑定到 bond0 +- 物理网卡 eth2, eth3 绑定到 bond1 + +### 网卡 eth0 配置 +- 修改 /etc/sysconfig/network-scripts/ifcfg-eth0 + ``` + TYPE=Ethernet + BOOTPROTO=none + DEVICE=eth0 + ONBOOT=yes + USERCTL=no + SLAVE=yes + MASTER=bond0 + ``` + +### 网卡 eth1 配置 +- 修改 /etc/sysconfig/network-scripts/ifcfg-eth1 + ``` + TYPE=Ethernet + BOOTPROTO=none + DEVICE=eth1 + ONBOOT=yes + USERCTL=no + SLAVE=yes + MASTER=bond0 + ``` + +### 网卡 eth2 配置 +- 修改 /etc/sysconfig/network-scripts/ifcfg-eth2 + ``` + TYPE=Ethernet + BOOTPROTO=none + DEVICE=eth2 + ONBOOT=yes + USERCTL=no + SLAVE=yes + MASTER=bond1 + ``` + +### 网卡 eth3 配置 +- 修改 /etc/sysconfig/network-scripts/ifcfg-eth3 + ``` + TYPE=Ethernet + BOOTPROTO=none + DEVICE=eth3 + ONBOOT=yes + USERCTL=no + SLAVE=yes + MASTER=bond1 + ``` + +### 增加网卡 bond0 配置 +- 创建 /etc/sysconfig/network-scripts/ifcfg-bond0 + ``` + TYPE=Ethernet + BOOTPROTO=static + NAME=bond0 + DEVICE=bond0 + ONBOOT=yes + IPADDR=192.168.1.101 + NETMASK=255.255.255.0 + GATEWAY=192.168.1.1 + DNS1=114.114.114.114 + ``` + +### 增加网卡 bond1 配置 +- 创建 /etc/sysconfig/network-scripts/ifcfg-bond1 + ``` + TYPE=Ethernet + BOOTPROTO=static + NAME=bond0 + DEVICE=bond0 + ONBOOT=yes + IPADDR=192.168.1.102 + NETMASK=255.255.255.0 + #GATEWAY=192.168.1.1 + #DNS1=114.114.114.114 + ``` + +### 配置绑定模式 +- 创建 /etc/modprobe.d/bonding.conf,加入以下内容 + ``` + alias bond0 bonding + alias bond1 bonding + options bonding miimon=100 mode=1 + ``` + +### 载入 bonding 模块,重启 network 服务 +```bash +modprob bonding +systemctl restart network +``` + +### 查看网卡绑定状态 +```bash +cat /proc/net/bonding/bond0 +cat /proc/net/bonding/bond1 +``` + +# CentOS8 配置 bond +- 建立 bond 连接配置文件 + ```bash + nmcli c add con-name bond0 type bond ifname bond0 mode active-backup + ``` +- 增加两个网卡都 bond0 + ```bash + nmcli c add type bond-slave ifname eth1 master bond0 + nmcli c add type bond-slave ifname eth2 master bond0 + ``` +- 启动这两个 slave 连接 + ```bash + nmcli c up bond-slave-eth1 + nmcli c up bond-slave-eth2 + ``` + diff --git a/content/post/cassandra-install.md b/content/post/cassandra-install.md new file mode 100644 index 0000000..7c6c9e9 --- /dev/null +++ b/content/post/cassandra-install.md @@ -0,0 +1,154 @@ +--- +title: "CentOS7 安装 Cassandra 集群" +date: 2019-10-30T00:59:02+08:00 +lastmod: 2019-10-30T00:59:02+08:00 +keywords: [] +tags: ["cassandra"] +categories: ["database"] +--- + +# 环境 + +主机名 | Public IP | Cluster IP| 操作系统 | Cassandra 版本 +---- | ---- | ---- | ---- | ---- +cassandra101 | 10.0.4.101 | 10.10.10.101 | CentOS7.6 | 3.0.18 +cassandra102 | 10.0.4.102 | 10.10.10.102 | CentOS7.6 | 3.0.18 +cassandra103 | 10.0.4.103 | 10.10.10.103 | CentOS7.6 | 3.0.18 + +- 下载 [apache-cassandra-3.0.18-bin.tar.gz](https://mirrors.tuna.tsinghua.edu.cn/apache/cassandra/3.0.18/apache-cassandra-3.0.18-bin.tar.gz) + +# 各节点初始配置 + +- 关闭 selinux、防火墙 +- 部署 java 运行环境 +- 创建 cassandra 用户 + ```bash + useradd -m cassandra + ``` +- 创建数据目录 + ```bash + cd /var/lib + mkdir -p cassandra/data1 #多个存储磁盘可以创建多个数据存储目录 + mkdir -p cassandra/hints #建议与数据磁盘分开 + mkdir -p cassandra/commitlog #建议与数据磁盘分开 + mkdir -p cassandra/saved_caches #建议与数据磁盘分开 + chown -R cassandra.cassandra cassandra/ + ``` +- 创建日志目录 + ```bash + cd /var/log + mkdir -p cassandra + chown -R cassandra.cassandra cassandra/ + ``` +- 创建 pid 目录 + ```bash + cd /run + mkdir -p cassandra + chown -R cassandra.cassandra cassandra/ + ``` +- 增加 sysctl.conf 配置,执行 sysctl -p 生效 + ``` + vm.max_map_count=1048576 + ``` +- 安装 jemalloc (推荐) + ```bash + yum install jemalloc + ``` +- 创建文件 /usr/lib/systemd/system/cassandra.service,内容如下 + ``` + [Unit] + Description=Cassandra + Requires=network.service + After=network.service + [Service] + Type=forking + WorkingDirectory=/opt/cassandra + Environment=JAVA_HOME=/opt/jre + Environment=LOCAL_JMX=no + PIDFile=/run/cassandra/cassandra.pid + ExecStart=/opt/cassandra/bin/cassandra -p /run/cassandra/cassandra.pid + User=cassandra + Group=cassandra + LimitNOFILE=65536 + LimitNPROC=65536 + LimitMEMLOCK=infinity + SuccessExitStatus=143 + [Install] + WantedBy=multi-user.target + ``` + +# 部署 Cassandra + +- 登陆 cassandra101,下载 cassandra,解压至 /opt/ 下 +- 修改 /opt/cassandra/conf/cassandra.yaml + ```yaml + cluster_name: CassandraCluster + hists_directory: /var/lib/cassandra/hints + data_file_directories: + - /var/lib/cassandra/data1 + commitlog_directory: /var/lib/cassandra/commitlog + saved_caches_directory: /var/lib/cassandra/saved_caches + seed_provider: + - class_name: org.apache.cassandra.locator.SimpleSeedProvider + parameters: + - seeds: "10.10.10.101,10.10.10.102,10.10.10.103" + listen_address: 10.10.10.101 + rpc_address: 10.0.4.101 + ``` +- 修改 /opt/cassandra/conf/logback.xml + ```bash + sed -i 's,\${cassandra.logdir},/var/log,' /opt/cassandra/conf/logback.xml + ``` +- 修改 /opt/cassandra/conf/cassandra-env.sh + ```bash + # 这里我暂时关闭了 jmx 远程验证,否则需要手动创建 jmxremote.password 文件 + sed -i 's/jmxremote.authenticate=true/jmxremote.authenticate=false/' /opt/cassandra/conf/cassandra-env.sh + ``` +- 修改 cassandra 目录的权限 + ```bash + chown -R cassandra.cassandra cassandra/ + ``` +- 打包 cassandra 目录,部署到 cassandra102 和 cassandra103 的 /opt 下,并修改 cassandra.yaml + ``` + # cassandra102 + listen_address: 10.10.10.102 + rpc_address: 10.0.4.102 + # cassandra103 + listen_address: 10.10.10.103 + rpc_address: 10.0.4.103 + ``` + +# 启动集群 + +- 启动 cassandra 服务 + ```bash + + systemctl daemon-reload + systemctl start cassandra + ``` + +# 简单使用 + +- cqlsh 连接数据库 + ```bash + /opt/cassandra/bin/cqlsh 10.0.4.101 9042 + cqlsh> desc keyspaces; + ``` + +# 注意事项 +- 创建包含复合主键的表 + ```cql + create table t1 ( + c1 text, + c2 text, + c3 text, + c4 text, + c5 text, + primary key((c1,c2),c3,c4) + ); +- 复合主键的第一列 "(c1,c2)" 构成 PartitionKey,其余列 c3,c4 都是 ClusteringKey +- Cassandra 对 PartitionKey 计算 Hash 值,决定该记录的存放 node,ClusteringKey 在 Partition 内部排序 +- 默认只支持**主键列**和**索引列**查询,否则需要手动指定 **allow filtering** +- 根据多个 ClustringKey 查询时,需指定全部的 PartitionKey,ClusteringKey 不能跳过 +- 主键列不可修改 + diff --git a/content/post/centos6-migrate.md b/content/post/centos6-migrate.md new file mode 100644 index 0000000..c1dc7e5 --- /dev/null +++ b/content/post/centos6-migrate.md @@ -0,0 +1,63 @@ +--- +title: "Centos6 系统盘迁移" +date: 2019-10-30T13:34:47+08:00 +lastmod: 2019-10-30T13:34:47+08:00 +tags: ["centos", "系统盘迁移"] +categories: ["os"] +--- + +# 环境 +- Linux 物理机,已安装 VirtualBox 虚拟机软件 +- CentOS6.9 live 启动 U 盘 +- CentOS6.9 iso 镜像文件 +- 待安装笔记本 IBM x32 + +# VirtualBox 创建 Redhat6 虚拟机 +- VMware 没用过,建议硬件配置尽量和目标设备一致 +- Thinkpad X32 的处理器只有一核,无 PAE,内存 1024MB +- 虚拟硬盘 8G 就够了,使用 CentOS6.9 iso 装好虚拟机后,禁用 selinux,可能需要重启生效。 + +# 打包操作系统根目录 +``` +cd / +tar cvpzf backup.tgz --exclude=/backup.tgz --one-file-system / +``` +# 导出 /backup.tgz 文件 +- 返回物理机操作系统,通过 ssh、http 或其他方式把虚拟机的 /backup.tgz 拷贝至物理机中 /root 下 + +# 格式化磁盘 +- 取出待安装笔记本的硬盘,通过 USB 或其他方式挂载到该物理机上,fdisk 分区,格式化 + ``` + #fdisk 分成俩个分区,前面一个大的根分区,后面一个2G的 swap 分区,其他情况自己决定 + #假设刚挂载的这个目标磁盘设备是 sdg + mkfs.ext3 /dev/sdg1 + mkswap /dev/sdg2 + ``` + +# 部署操作系统 +- 挂载待部署磁盘的根分区,解压操作系统文件,修改启动相关参数 + ``` + mount /dev/sdg1 /mnt + tar xvpzf /root/backup.tgz -C /mnt/ + #查看目标磁盘根分区的 uuid,替换 /mnt/boot/grub/grub.conf 和 /mnt/etc/fstab 中对应项 + ls -lh /dev/disk/by-uuid/|grep sdg1 + #查看目标磁盘 swap 分区的uuid,替换 /mnt/etc/fstab 中对应项 + ls -lh /dev/disk/by-uuid/|grep sdg2 + #检查 /mnt/etc/mtab 中列出的信息是否正确 + ``` + +# 安装 grub2 +- 取消挂载,把部署好的磁盘安装回待安装笔记本中,插上 CentOS6.9 的启动 U 盘,从 U 盘启动待安装笔记本,进入 live 模式(安装盘可以在安装界面开始时按下 Alt + F1 切换到 live 模式下),安装 grub2 + ``` + #在 live 模式下切换到 root + sudo -i + #挂载(假设 live 识别到的硬盘根分区是 /dev/sda1) + mount /dev/sda1 /mnt + #安装 grub2 + grub-install --root-directory=/mnt/ /dev/sda + #如果显示 “no error”,即可退出,取消挂载 + exit + umount -f /mnt + ``` +- 此时重启就可以正常进入系统了 + diff --git a/content/post/centos6-nopae.md b/content/post/centos6-nopae.md new file mode 100644 index 0000000..65e0a75 --- /dev/null +++ b/content/post/centos6-nopae.md @@ -0,0 +1,88 @@ +--- +title: "Centos6 安装 nopae 内核" +date: 2019-10-30T13:06:52+08:00 +lastmod: 2019-10-30T13:06:52+08:00 +tags: ["centos", "nopae"] +categories: ["os"] +--- + +# 环境 +- IBM Thinkpad X31 +- VirtualBox +- [CentOS-6.9-i386-minimal.iso](http://mirrors.ustc.edu.cn/centos/6.9/isos/i386/CentOS-6.9-i386-minimal.iso) + +# 需求 +- 手头有台笔记本 IBM thinkpad X31,处理器不支持 pae,不支持 64 位操作系统,要安装一个32位 CentOS6,且内核无 pae 要求。 + +# 准备环境 +- 在支持 pae 的计算机上安装 [VirtualBox](https://www.virtualbox.org/wiki/Downloads) 及其扩展包,以支持 VirtualBox 虚拟机中挂载宿主机 USB 存储。 +- 取出 Thinkpad 笔记本的硬盘,通过移动硬盘盒等方法连接刚刚安装 VirtualBox 的计算机,确保该存储正常识别可用。 +- 下载[CentOS-6.9-i386-minimal.iso](http://mirrors.ustc.edu.cn/centos/6.9/isos/i386/CentOS-6.9-i386-minimal.iso) + +# VirtualBox 下安装 CentOS6.9 虚拟机 +- 创建 CentOS6.9 x32 虚拟机,这里无需创建虚拟磁盘,后面会把 Thinkpad 笔记本的硬盘挂载到虚拟机中,直接把操作系统安装到该硬盘中。 +![不用创建虚拟磁盘](/img/03092704_DUG6.png "无需创建虚拟机磁盘") +- 设置虚拟机,启动 USB 3.0 控制器,增加筛选器,选中刚刚 USB 连接的 Thinkpad 硬盘;网络模式自选,确保虚拟机可上网。 +![启用USB 3.0 控制器](/img/03094814_oVJY.png "设置 USB") +- 使用刚刚下载好的 CentOS-6.9-i386-minimal.iso 启动 CentOS6.9 虚拟机,如下图 +![启动虚拟机](/img/03100036_sRDb.png "启动虚拟机") +- 此时查看菜单栏"设备",会发现 Thinkpad 硬盘已挂载,如下图 +![输入图片说明](/img/03100217_mMNU.png "查看挂载的 USB 设备") +- 选择 "Install or upgrade an existing system" 安装系统,选择安装设备,会看到唯一一个磁盘,如下图 +![磁盘选择](/img/03101245_wAKJ.png "默认唯一的磁盘即 USB 设备") +- 该设备就是通过 USB 挂载的 Thinkpad 磁盘,划分好分区,完成系统安装。 + +# CentOS6.9 虚拟机 Rescue 启动挂载 +- 由于 VirtuaBox 虚拟机尚不支持从 USB 启动,所以在上一步完成系统安装后,重启虚拟机,依旧使用 CentOS-6.9-i386-minimal.iso 启动。 +- 此时从 "Rescue Installed system" 启动,如下图 +![rescue](/img/03102522_BRPU.png "Rescue Installed system") +- 根据提示选择好语言、键盘,激活网卡,如下图 +![激活网络](/img/03104347_AHmM.png "激活网络") +- "OK" 确认,选择网卡,如下图 +![选择网卡](/img/03103545_bX3Q.png "选择网卡") +- "OK",根据自己的网络环境配置上网方式,如下图 +![配置网卡地址](/img/03103719_CPnJ.png "配置网卡地址") +- "OK",进入 Rescue 界面,如下图 +![Rescue](/img/03103936_CZ1s.png "Rescue") +- "Continue",选择好磁盘,"OK" 确认,提示根分区已挂载至 /mnt/sysimage 下,如下图 +![根目录挂载地址](/img/03104155_Lz45.png "根目录挂载至 /mnt/sysimage") +- "OK", 进入 Rescue Shell,此时执行 + ```bash + chroot /mnt/sysimage/ /bin/bash + ``` +- 至此,通过 VirtualBox 虚拟机成功启动 USB 存储(Thinkpad 硬盘)中的根分区并进入其 Bash 环境。 + +# 安装 NONPAE 内核 +- 在刚刚启动的 Bash Shell 中,检查网络 + ```bash + ping www.baidu.com + ``` +- 关闭 selinux (可选) + ```bash + sed -i 's/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + ``` +- 安装 NONPAE 内核 + ```bash + rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org + rpm -Uvh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm + yum --enablerepo=elrepo-kernel install kernel-lt-NONPAE + ``` +- 检查系统已安装的内核 + ```bash + rpm -qa|grep kernel + ``` +- 发现两个内核,一个是官方的 kernel-2.6,另一个是刚刚安装的 kernel-3.10,kernel-3.10 就是可以在无 pae 处理器上启动的 NONPAE 内核。 +- 退出当前 Chroot Shell 和 Rescure Shell + ```bash + exit + exit + ``` + +# 启动 Thinkpad X31 +- 关闭虚拟机,退出 USB 磁盘,把磁盘装回 Thinkpad X31 笔记本中,开机,选择 3.10 内核即可正常启动。 +- 卸载官方 2.6 内核(推荐) + ```bash + yum erase kernel-2.6.32* + yum erase kernel-firemware-2.6.32* + ``` + diff --git a/content/post/centos6-oracle11g.md b/content/post/centos6-oracle11g.md new file mode 100644 index 0000000..f7f5c29 --- /dev/null +++ b/content/post/centos6-oracle11g.md @@ -0,0 +1,122 @@ +--- +title: "CentOS6 安装 Oracle11g" +date: 2019-10-30T17:56:57+08:00 +lastmod: 2019-10-30T17:56:57+08:00 +tags: ["oracle", "centos"] +categories: ["database"] +--- + +# 安装依赖 +```bash +yum install binutils compat-libstdc++-33 compat-libstdc++-33.i686 \ +elfutils-libelf elfutils-libelf-devel gcc gcc-c++ glibc glibc.i686 \ +glibc-common glibc-devel glibc-devel.i686 glibc-headers ksh libaio \ +libaio.i686 libaio-devel libaio-devel.i686 libgcc libgcc.i686 libstdc++ \ +libstdc++.i686 libstdc++-devel make sysstat unixODBC unixODBC-devel +``` + +# 创建用户和用户组 +```bash +groupadd dba oinstall +useradd -g oinstall -m oracle +``` + +# 配置oracle用户环境变量 +- 打开 oracle 用户的的默认shell配置文件 ~/.bashrc,在最后添加以下代码 + ```bash + export ORACLE_BASE=/opt/oracle/app #oracle数据库安装目录 + export ORACLE_HOME=$ORACLE_BASE/product/11.2.0/db_home1 #oracle数据库路径 + export ORACLE_SID=orcl #oracle启动数据库实例名 + export PATH=$ORACLE_HOME/bin:/usr/sbin:$PATH #添加系统环境变量 + export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib #添加系统环境变量 + #export NLS_LANG="SIMPLIFIED CHINESE_CHINA.AL32UTF8" #设置Oracle客户端中文utf8 + export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK #设置Oracle客户端中文gbk + ``` +- 使设置立刻生效 + ```bash + source ~/.bashrc + ``` + +# 创建oracle 11g软件安装路径 +```bash +mkdir /opt/oracle/app/product/11.2.0/db_home1 -p +chown oracle.oinstall /opt/oracle -R +``` + +# 配置内核参数 +- 编辑 /etc/sysctl.conf,在文件尾追加下面的参数设置 + ``` + fs.file-max = 6815744 + fs.aio-max-nr =1048576 + net.ipv4.ip_local_port_range = 9000 65500 + net.core.rmem_default = 262144 + net.core.rmem_max = 4194304 + net.core.wmem_default = 262144 + net.core.wmem_max = 1048576 + kernel.sem = 250 32000 100 128 + ``` +- 使设置生效 + ```bash + sysctl -p + ``` + +# 限制 oracle 用户资源 +- 编辑 /etc/security/limits.conf,在末尾添加以下代码 + ``` + oracle soft nproc 2047 + oracle hard nproc 16384 + oracle soft nofile 1024 + oracle hard nofile 65536 + ``` + +# 安装字体 +- 一般需要安装中易宋体字体,百度随便下载一个zysong.ttf,切换到zysong.ttf所在路径,运行: + ```bash + mkdir /usr/share/fonts/zh_CN/TureType/ -p + mv zydong.ttf /usr/share/fonts/zh_CN/TrueType/ + fc-cache -fv + ``` + +# 安装oracle 11g +- 解压下载好的oracle 11g文件 + ```bash + unzip linux.x64_11gR2_database_1of2.zip -d /home/oracle/ + unzip linux.x64_11gR2_database_2of2.zip -d /home/oracle/ + chown oracle.oinstall /home/oracle/database/ -R + ``` +- **切换到 oracle 用户下**,运行安装程序 + ```bash + su - oracle + cd database + ./runInstaller + ``` +- 若提示swap空间不足,自行百度解决! + +# 配置监听器数据库 +- Oracle软件安装完后,执行 netca 命令配置监听器 +``` +netca +``` +- 在图形界面中按提示配置监听器 +- 执行 dbca 命令安装数据库 +```bash +dbca +``` +- 在图形界面中按提示安装数据库就可以了。 + +# 测试运行 +- 数据库安装完后监听器与数据库实例就已启动, +- 停止和启动监听器 +```bash +lsnrctl stop +lsnrctl start +``` +- 停止和启动实例 +``` +sqlplus /nolog +SQL> connect / as sysdba; +SQL> shutdown +SQL> startup +#执行其它SQL语句测试数据库 +``` + diff --git a/content/post/centos7-migrate.md b/content/post/centos7-migrate.md new file mode 100644 index 0000000..525f41b --- /dev/null +++ b/content/post/centos7-migrate.md @@ -0,0 +1,126 @@ +--- +title: "CentOS7 系统盘迁移" +date: 2019-10-30T01:03:45+08:00 +lastmod: 2019-10-30T01:03:45+08:00 +keywords: [] +tags: ["centos", "系统", "迁移"] +categories: ["os"] +--- + +# 环境 +- 两台服务器(A,B) +- A 已安装好 CentOS7,且已**关闭 selinux** +- B 裸机,待安装操作系统 + +# 打包根分区 +- 从 B 上拆下系统硬盘,接在 A 上,启动 A +- 清空日志(推荐) + ```bash + cd /var/log/ + find . -type f | xargs rm -f + ``` +- 关闭 selinux + ```bash + sed -i '/^SELINUX=/cSELINUX=disabled' /mnt/etc/selinux/config + ``` +- 如果 A 是 MBR 启动,则直接打包根分区 + ```bash + tar zcpf /centos7.tgz --exclude=/centos7.tgz --one-file-system / + ``` +- 如果 A 是 EFI 启动,则需打包根分区和 EFI 分区 + ```bash + # 假设 efi 分区挂载在 /boot/efi 下 + tar zcpf /centos7.tgz --exclude=/centos7.tgz --one-file-system / /boot/efi + ``` + +# 硬盘分区 +- 假设 /dev/sdb 是 B 的系统硬盘 +- MBR 启动时,分区表是 dos,只分一个根分区即可 +- EFI 启动时,分区表是 gpt,需要分一个 512MB 的 **efi 分区**和一个根分区 + ```bash + fdisk /dev/sdb + # n 创建新分区 + # t 指定分区类型 1 (即 efi system) + ``` + +# 格式化 +- MBR 启动 + ```bash + mkfs.xfs /dev/sdb1 + ``` +- EFI 启动 + ```bash + mkfs.vfat -F32 /dev/sdb1 + mkfs.xfs /dev/sdb2 + ``` + +# 挂载硬盘 +- MBR 启动 + ```bash + mount /dev/sdb1 /mnt/ + ``` +- EFI 启动 + ```bash + mount /dev/sdb2 /mnt/ + mkdir -p /mnt/boot/efi + mount /dev/sdb1 /mnt/boot/efi/ + ``` + +# 部署操作系统 +- 解压之前打包的 /centos7.tgz + ```bash + tar zxpf /centos7.tgz -C /mnt/ + ``` +- 替换 fstab 中的 uuid 信息 + ```bash + # 获取 B 的系统硬盘分区的 uuid 信息 + lsblk -f /dev/sdb + # 把结果中的 uuid 替换到 /mnt/etc/fstab 中的相应位置 + ``` +- 如果打包时未关闭 selinux,此时可以修改配置文件 + ```bash + sed -i '/^SELINUX=/cSELINUX=disabled' /mnt/etc/selinux/config + ``` +- 删除网卡硬件标识(推荐) + ```bash + sed -i -e '/HWADDR/d' -e '/UUID/d' /mnt/etc/sysconfig/network-scripts/ifcfg-{eth,enp}* + ``` +- 删除 ssh 主机密钥(推荐) + ```bash + rm -rf /etc/ssh/ssh_host_* + ``` + +# 部署 grub +- MBR 启动 + ```bash + mount --bind /dev/ /mnt/dev/ + mount -t proc procfs /mnt/proc/ + mount -t sysfs sysfs /mnt/sys/ + chroot /mnt + grub2-install /dev/sdb + grub2-mkconfig -o /boot/grub2/grub.cfg + exit + ``` +- EFI 启动 + ```bash + mount --bind /dev/ /mnt/dev/ + mount -t proc procfs /mnt/proc/ + mount -t sysfs sysfs /mnt/sys/ + mount -t efivarfs efivarfs /target/sys/firmware/efi/efivars/ + chroot /mnt + efibootmgr -c -p 1 -d /dev/sdb -L "centos" + grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg + exit + ``` + +# 启动操作系统 +- 卸载 B 的系统硬盘 + ```bash + umount -R /mnt + ``` +- 关闭 A,拆下刚部署好操作系统的硬盘,接回 B 中 +- 启动 B,刚部署的 CentOS7 正常启动 + +# 参考 +- [https://wiki.centos.org/zh/HowTos/ManualInstall?highlight=%28grub2-install%29](https://wiki.centos.org/zh/HowTos/ManualInstall?highlight=%28grub2-install%29) + diff --git a/content/post/centos7.md b/content/post/centos7.md new file mode 100644 index 0000000..f6e9774 --- /dev/null +++ b/content/post/centos7.md @@ -0,0 +1,189 @@ +--- +title: "CentOS7 笔记" +date: 2019-10-30T10:58:18+08:00 +lastmod: 2019-10-30T10:58:18+08:00 +keywords: [] +tags: ["centos"] +categories: ["os"] +--- + +# 常用初始配置 +- 系统更新 + ```bash + yum update + ``` +- 禁用 firewalld + ```bash + systemctl stop firewalld + systemctl disable firewalld + ``` +- 禁用 NetworkManager + ```bash + systemctl stop NetworkManager + systemctl disable NetworkManager + ``` +- 禁用 postfix + ```bash + systemctl stop postfix + systemctl disable postfix + ``` +- 如果不用 NFS,可以禁用 rpcbind + ```bash + systemctl stop rpcbind + systemctl disable rpcbind + ``` +- 禁用 selinux,可能需要重启操作系统 + ```bash + sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + setenforce 0 + # 可能需要重启 + ``` +- 配置网卡静态地址 + ```bash + cd /etc/sysconfig/network-scripts + sed -i -e '/^BOOTPROTO/d' -e '/^ONBOOT/d' \ + -e '/^IPADDR/d' -e '/^NETMASK/d' -e '/^PREFIX/d' \ + -e '/^GATEWAY/d' -e '/^DNS/d' ${ifcfg} + cat >> ${ifcfg} <<-END + ONBOOT=yes + BOOTPROTO=static + IPADDR=${ip} + PREFIX=${mask} + GATEWAY=${gw} + DNS1=${dns} + END + systemctl restart network + ``` +- 修改 sysctl.conf + ```bash + cat >> /etc/sysctl.conf <<-END + # 防止一个套接字在有过多试图连接到达时引起过载 + net.ipv4.tcp_syncookies = 1 + # 连接队列的长度,默认值为128 + net.core.somaxconn = 1024 + # timewait的超时时间,设置短一些 + net.ipv4.tcp_fin_timeout = 10 + # os直接使用timewait的连接 + net.ipv4.tcp_tw_reuse = 1 + # 回收timewait连接 + net.ipv4.tcp_tw_recycle = 1 + END + sysctl -p + ``` +- 修改主机名 + ```bash + hostnamectl set-hostname ${hostname} + sed -i "/[ \t]\+${hostname}[ \t]*$/d" /etc/hosts + echo "${ip} ${hostname}" >> /etc/hosts + ``` +- 禁用 sshd 域名解析 + ```bash + sed -i '/UseDNS/d' /etc/ssh/sshd_config + echo 'UseDNS no' >> /etc/ssh/sshd_config + ``` +- 删除可能存在的 TMOUT 环境变量 + ```bash + sed -i '/^export[ \t]\+TMOUT=/d' /etc/profile + ``` +- 配置 history 命令数量和执行时间 + ```bash + echo 'export HISTSIZE=10000' > /etc/profile.d/history.sh + echo 'export HISTTIMEFORMAT="[%F %T] "' >> /etc/profile.d/history.sh + ``` +- 修改时间同步服务器地址 + ```bash + sed -i '/^server /d' /etc/chrony.conf + echo "server ${ip|domain} iburst" >> /etc/chrony.conf + ``` +- 修改 rsyslog 服务的时间格式 + ```bash + cat > /etc/rsyslog.d/custom.conf <> /etc/hosts + echo "10.0.4.42 ceph42" >> /etc/hosts + echo "10.0.4.43 ceph43" >> /etc/hosts + ``` + +# 配置 yum 源 +- 在全部节点上执行如下操作 +- 移动系统默认的 repo 文件到备份目录 + ```bash + cd /etc/yum.repos.d + mkdir bak + mv Rocky-*.repo bak/ + ``` + +- 创建新的系统 yum 源文件 /etc/yum.repos.d/rocky-nju.repo,使用南京大学镜像站,内容如下 + ``` + [appstream] + name=Rocky Linux $releasever - AppStream + baseurl=https://mirrors.nju.edu.cn/$contentdir/$releasever/AppStream/$basearch/os/ + gpgcheck=1 + enabled=1 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial + + [baseos] + name=Rocky Linux $releasever - BaseOS + baseurl=https://mirrors.nju.edu.cn/$contentdir/$releasever/BaseOS/$basearch/os/ + gpgcheck=1 + enabled=1 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial + ``` + +- 创建 epel yum 源文件 /etc/yum.repos.d/epel-tsinghua.repo,使用清华大学镜像站,内容如下 + ``` + [epel] + name=Extra Packages for Enterprise Linux $releasever - $basearch + baseurl=https://mirrors.tuna.tsinghua.edu.cn/epel/$releasever/Everything/$basearch + failovermethod=priority + enabled=1 + gpgcheck=1 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-$releasever + ``` + +- 下载 RPM-GPG-KEY-EPEL-8 + ```bash + cd /etc/pki/rpm-gpg/ + curl -LO https://mirrors.nju.edu.cn/epel/RPM-GPG-KEY-EPEL-8 + ``` + +- 创建 ceph yum 源文件 /etc/yum.repos.d/ceph-tsinghua.repo,使用清华大学镜像站,内容如下 + ``` + [ceph] + name=Ceph packages for $basearch + baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-15.2.14/el8/$basearch + enabled=1 + priority=2 + gpgcheck=1 + gpgkey=https://mirrors.tuna.tsinghua.edu.cn/ceph/keys/release.asc + + [ceph-noarch] + name=Ceph noarch packages + baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-15.2.14/el8/noarch + enabled=1 + priority=2 + gpgcheck=1 + gpgkey=https://mirrors.tuna.tsinghua.edu.cn/ceph/keys/release.asc + ``` + +- 导入 release.asc + ```bash + rpm --import 'https://mirrors.tuna.tsinghua.edu.cn/ceph/keys/release.asc' + ``` + +- 更新 yum 缓存 + ```bash + dnf clean all + dnf makecache + ``` + +# 配置时间同步 +- 在全部节点上执行如下操作 +- 安装 chrony + ```bash + dnf install chrony + ``` + +- 如果内网没有时间服务器,可以在 ceph41 上启动一个时间同步服务,修改 /etc/chrony.conf + ``` + ... + pool ntp.aliyun.com iburst + ... + allow 10.0.4.0/24 + ... + local stratum 10 + ... + ``` + +- 设置 ceph42 和 ceph43 从 ceph41 上同步时间,修改 /etc/chrony.conf + ``` + ... + pool ceph41 iburst + ... + ``` + +- 在全部服务器上启动 chronyd 服务,并设置开机自动启动 + ```bash + systemctl start chronyd + systemctl enable chronyd + ``` + +# 安装 ceph +- 在全部节点上执行如下操作 + ```bash + dnf install leveldb gdisk gperftools-libs python3-ceph-argparse nvme-cli + dnf install ceph + ``` + +- 创建 ceph 配置文件 /etc/ceph/ceph.conf,内容如下 + ``` + [global] + fsid = aaaa0000-bbbb-1111-cccc-2222dddd3333 + mon_initial_members = ceph41, ceph42, ceph43 + mon_host = 10.0.4.41, 10.0.4.42, 10.0.4.43 + public_network = 10.0.4.0/24 + cluster_network = 192.168.4.0/24 + auth_cluster_required = cephx + auth_service_required = cephx + auth_client_required = cephx + osd_pool_default_size = 2 # 推荐使用官方默认的 3 + osd_pool_default_min_size = 2 + ``` + +# 部署 mon +- 在 ceph41 上执行如下操作 +- 这里创建了一堆傻逼密钥文件,没看懂啥意思,照搬官网 + ```bash + ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *' + ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *' + ceph-authtool --create-keyring /var/lib/ceph/bootstrap-osd/ceph.keyring --gen-key -n client.bootstrap-osd --cap mon 'profile bootstrap-osd' --cap mgr 'allow r' + ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring + ceph-authtool /tmp/ceph.mon.keyring --import-keyring /var/lib/ceph/bootstrap-osd/ceph.keyring + chown ceph:ceph /tmp/ceph.mon.keyring + monmaptool --create --add ceph41 10.0.4.41 --add ceph42 10.0.4.42 --add ceph43 10.0.4.43 --fsid aaaa0000-bbbb-1111-cccc-2222dddd3333 /tmp/monmap + ``` + +- 初始化 mon 数据目录 + ```bash + sudo -u ceph mkdir /var/lib/ceph/mon/ceph-ceph41 + sudo -u ceph ceph-mon --mkfs -i ceph41 --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring + ``` + +- 启动 mon 服务,并设置开机自动启动 + ```bash + systemctl start ceph-mon@ceph41 + systemctl enable ceph-mon@ceph41 + ``` + +- 复制密钥文件到 ceph42 和 ceph43 上 + ```bash + scp /tmp/{ceph.mon.keyring,monmap} ceph42:/tmp/ + scp /etc/ceph/ceph.client.admin.keyring ceph42:/etc/ceph/ + scp /var/lib/ceph/bootstrap-osd/ceph.keyring ceph42:/var/lib/ceph/bootstrap-osd/ + + scp /tmp/{ceph.mon.keyring,monmap} ceph43:/tmp/ + scp /etc/ceph/ceph.client.admin.keyring ceph43:/etc/ceph/ + scp /var/lib/ceph/bootstrap-osd/ceph.keyring ceph43:/var/lib/ceph/bootstrap-osd/ + ``` + +- 在 **ceph42** 上执行如下操作 +- 初始化 mon 数据目录 + ```bash + chown ceph:ceph /tmp/ceph.mon.keyring + sudo -u ceph mkdir /var/lib/ceph/mon/ceph-ceph42 + sudo -u ceph ceph-mon --mkfs -i ceph42 --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring + ``` + +- 启动 mon 服务,并设置开机自动启动 + ```bash + systemctl start ceph-mon@ceph42 + systemctl enable ceph-mon@ceph42 + ``` + +- 在 **ceph43** 上执行如下操作 +- 初始化 mon 数据目录 + ```bash + chown ceph:ceph /tmp/ceph.mon.keyring + sudo -u ceph mkdir /var/lib/ceph/mon/ceph-ceph43 + sudo -u ceph ceph-mon --mkfs -i ceph43 --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring + ``` + +- 启动 mon 服务,并设置开机自动启动 + ```bash + systemctl start ceph-mon@ceph43 + systemctl enable ceph-mon@ceph43 + ``` + +- 在任一节点上执行如下操作 +- mon 额外配置 + ```bash + # 开启 msgr2,监听 tcp 3300 端口 + ceph mon enable-msgr2 + + # 禁用 auth_allow_insecure_global_id_reclaim + ceph config set mon auth_allow_insecure_global_id_reclaim false + ``` + +## 查看集群状态 +- 在任一节点上执行如下操作 + ```bash + ceph -s + ``` + +- 集群状态如下 + ``` + cluster: + id: aaaa0000-bbbb-1111-cccc-2222dddd3333 + health: HEALTH_OK + + services: + mon: 3 daemons, quorum ceph41,ceph42,ceph43 (age ...) + mgr: no daemons active + osd: 0 osds: 0 up, 0 in + + data: + pools: 0 pools, 0 pgs + objects: 0 objects, 0 B + usage: 0 B used, 0 B / 0 B avail + pgs: + ``` + +# 部署 mgr +- 在 ceph41 上执行如下操作 +- 又是创建密钥文件,没看懂啥意思,照搬官网 + ```bash + sudo -u ceph mkdir /var/lib/ceph/mgr/ceph-ceph41 + ceph auth get-or-create mgr.ceph41 mon 'allow profile mgr' osd 'allow *' mds 'allow *' -o /var/lib/ceph/mgr/ceph-ceph41/keyring + chown ceph.ceph /var/lib/ceph/mgr/ceph-ceph41/keyring + ``` + +- 启动 mgr 服务,并配置开机自动启动 + ```bash + systemctl start ceph-mgr@ceph41 + systemctl enable ceph-mgr@ceph41 + ``` + +- 在 ceph42 上执行如下操作 +- 创建密钥文件 + ```bash + sudo -u ceph mkdir /var/lib/ceph/mgr/ceph-ceph42 + ceph auth get-or-create mgr.ceph42 mon 'allow profile mgr' osd 'allow *' mds 'allow *' -o /var/lib/ceph/mgr/ceph-ceph42/keyring + chown ceph.ceph /var/lib/ceph/mgr/ceph-ceph42/keyring + ``` + +- 启动 mgr 服务,并配置开机自动启动 + ```bash + systemctl start ceph-mgr@ceph42 + systemctl enable ceph-mgr@ceph42 + ``` + +- 在 ceph43 上执行如下操作 +- 创建密钥文件 + ```bash + sudo -u ceph mkdir /var/lib/ceph/mgr/ceph-ceph43 + ceph auth get-or-create mgr.ceph43 mon 'allow profile mgr' osd 'allow *' mds 'allow *' -o /var/lib/ceph/mgr/ceph-ceph43/keyring + chown ceph.ceph /var/lib/ceph/mgr/ceph-ceph43/keyring + ``` + +- 启动 mgr 服务,并配置开机自动启动 + ```bash + systemctl start ceph-mgr@ceph43 + systemctl enable ceph-mgr@ceph43 + ``` + +## 查看集群状态 +- 在任一节点上执行如下操作 + ```bash + ceph -s + ``` + +- 集群状态如下 + ``` + cluster: + id: aaaa0000-bbbb-1111-cccc-2222dddd3333 + health: HEALTH_WARN + OSD count 0 < osd_pool_default_size 2 + + services: + mon: 3 daemons, quorum ceph41,ceph42,ceph43 (age ...) + mgr: ceph41(active, since ...), standbys: ceph42, ceph43 + osd: 0 osds: 0 up, 0 in + + data: + pools: 0 pools, 0 pgs + objects: 0 objects, 0 B + usage: 0 B used, 0 B / 0 B avail + pgs: + ``` + +# 部署 osd +## 逻辑卷 osd +- 操作简单,推荐 +- 直接创建并启动逻辑卷 osd + ```bash + ceph-volume lvm create --bluestore --data /dev/sdb + ceph-volume lvm create --bluestore --data /dev/sdc + ``` + +- 上一步执行成功后,每个 ceph-osd 服务都已启动,且开机自动启动 + +## 裸设备 osd +- 操作麻烦,不推荐 +- 在全部节点上执行如下操作 +- 准备 osd + ```bash + ceph-volume raw prepare --bluestore --data /dev/sdb + ceph-volume raw prepare --bluestore --data /dev/sdc + ``` + +- 查看 osd 的 id + ```bash + ceph-volume raw list + ``` + +- 激活 osd,**使用裸设备创建 osd 时不支持 systemd,需要单独配置开机自动启动** + ```bash + ceph-volume raw activate --device /dev/sdb --no-systemd + ceph-volume raw activate --device /dev/sdc --no-systemd + ``` + +- 在 ceph41 上启动 osd 服务 + ```bash + systemctl start ceph-osd@0 + systemctl start ceph-osd@1 + ``` + +- 配置开机自动启动 + ```bash + chmod 0755 /etc/rc.d/rc.local + echo 'ceph-volume raw activate --device /dev/sdb --no-systemd + ceph-volume raw activate --device /dev/sdc --no-systemd + systemctl start ceph-osd@0 + systemctl start ceph-osd@1 + ' >> /etc/rc.d/rc.local + ``` + +- 在 ceph42 上启动 osd 服务 + ```bash + systemctl start ceph-osd@2 + systemctl start ceph-osd@3 + ``` + +- 配置开机自动启动 + ```bash + chmod 0755 /etc/rc.d/rc.local + echo 'ceph-volume raw activate --device /dev/sdb --no-systemd + ceph-volume raw activate --device /dev/sdc --no-systemd + systemctl start ceph-osd@2 + systemctl start ceph-osd@3 + ' >> /etc/rc.d/rc.local + ``` + +- 在 ceph43 上启动 osd 服务 + ```bash + systemctl start ceph-osd@4 + systemctl start ceph-osd@5 + ``` + +- 配置开机自动启动 + ```bash + chmod 0755 /etc/rc.d/rc.local + echo 'ceph-volume raw activate --device /dev/sdb --no-systemd + ceph-volume raw activate --device /dev/sdc --no-systemd + systemctl start ceph-osd@4 + systemctl start ceph-osd@5 + ' >> /etc/rc.d/rc.local + ``` + +## 查看集群状态 +- 在任一节点执行如下操作 + ```bash + ceph -s + ``` + +- 集群状态如下 + ``` + cluster: + id: aaaa0000-bbbb-1111-cccc-2222dddd3333 + health: HEALTH_OK + + services: + mon: 3 daemons, quorum ceph41,ceph42,ceph43 (age ...) + mgr: ceph41(active, since ...), standbys: ceph42, ceph43 + osd: 6 osds: 6 up (since ...), 6 in (since ...) + + data: + pools: 1 pools, 1 pgs + objects: 0 objects, 0 B + usage: ... GiB used, ... GiB / ... GiB avail + pgs: 1 active+clean + ``` + +# 部署 mds +- 只有 cephfs 会用到 mds +- 在 ceph41 上执行如下操作 +- 创建密钥文件 ...... 照搬官网 + ```bash + sudo -u ceph mkdir -p /var/lib/ceph/mds/ceph-ceph41 + sudo -u ceph ceph-authtool --create-keyring /var/lib/ceph/mds/ceph-ceph41/keyring --gen-key -n mds.ceph41 + ceph auth add mds.ceph41 osd "allow rwx" mds "allow *" mon "allow profile mds" -i /var/lib/ceph/mds/ceph-ceph41/keyring + ``` + +- 启动 mds 服务,并配置开机自动启动 + ```bash + systemctl start ceph-mds@ceph41 + systemctl enable ceph-mds@ceph41 + ``` + +- 在 ceph42 上执行如下操作 +- 创建密钥文件 ...... 照搬官网 + ```bash + sudo -u ceph mkdir -p /var/lib/ceph/mds/ceph-ceph42 + sudo -u ceph ceph-authtool --create-keyring /var/lib/ceph/mds/ceph-ceph42/keyring --gen-key -n mds.ceph42 + ceph auth add mds.ceph42 osd "allow rwx" mds "allow *" mon "allow profile mds" -i /var/lib/ceph/mds/ceph-ceph42/keyring + ``` + +- 启动 mds 服务,并配置开机自动启动 + ```bash + systemctl start ceph-mds@ceph42 + systemctl enable ceph-mds@ceph42 + ``` + +- 在 ceph43 上执行如下操作 +- 创建密钥文件 ...... 照搬官网 + ```bash + sudo -u ceph mkdir -p /var/lib/ceph/mds/ceph-ceph43 + sudo -u ceph ceph-authtool --create-keyring /var/lib/ceph/mds/ceph-ceph43/keyring --gen-key -n mds.ceph43 + ceph auth add mds.ceph43 osd "allow rwx" mds "allow *" mon "allow profile mds" -i /var/lib/ceph/mds/ceph-ceph43/keyring + ``` + +- 启动 mds 服务,并配置开机自动启动 + ```bash + systemctl start ceph-mds@ceph43 + systemctl enable ceph-mds@ceph43 + ``` + +## 查看集群状态 +- 在任一节点上执行如下操作 + ```bash + ceph -s + ``` + +- 集群状态如下 + ``` + cluster: + id: aaaa0000-bbbb-1111-cccc-2222dddd3333 + health: HEALTH_OK + + services: + mon: 3 daemons, quorum ceph41,ceph42,ceph43 (age ...) + mgr: ceph41(active, since ...), standbys: ceph42, ceph43 + mds: cephfs:1 {0=ceph43=up:active} 2 up:standby + osd: 3 osds: 3 up, 3 in + + data: + pools: 1 pools, 1 pgs + objects: 0 objects, 0 B + usage: ... GiB used, ... GiB / ... GiB avail + pgs: 1 active+clean + ``` + +# 简单使用 +## rbd +- 创建 rbd 池 + ```bash + ceph osd pool create rbd 128 128 + ceph osd pool application enable rbd rbd + ``` + +## cephfs +- 创建 cephfs 池 + ```bash + # 创建 cephfs 元数据池,pg 不用太大,设置 3 个副本 + ceph osd pool create cephfs_metadata 8 8 + ceph osd pool set cephfs_metadata size 3 + + # 创建 cephfs 数据池,根据数据量配置相应 pg + ceph osd pool create cephfs_data 128 128 + ``` + +- 创建 cephfs 文件系统 + ```bash + ceph fs new cephfs cephfs_metadata cephfs_data + ``` + +- 查看 mds 状态 + ```bash + ceph mds stat + ``` + +- 在任一 ceph 节点服务器上查看 admin 用户的 key + ```bash + cat /etc/ceph/ceph.client.admin.keyring | grep key | awk '{print $2}' + ``` + +- 在其他服务器上挂载 cephfs + ```bash + mount -t ceph 10.0.4.41:6789,10.0.4.42:6789,10.0.4.43:6789:/ /mnt -o name=admin,secret={admin 的 key} + ``` + diff --git a/content/post/ceph.md b/content/post/ceph.md new file mode 100644 index 0000000..647ddd3 --- /dev/null +++ b/content/post/ceph.md @@ -0,0 +1,177 @@ +--- +title: "Ceph 笔记" +date: 2019-10-30T11:44:37+08:00 +lastmod: 2019-10-30T11:44:37+08:00 +tags: ["ceph"] +categories: ["storage"] +--- + +# 测试环境 +操作系统 | 主机名 | IP | OSD 设备 | OSD ID | 容量 | ceph 版本 +---- | ---- | ---- | ---- | ---- | ---- | ---- +CentOS7 | ceph101 | 192.168.1.101 | /dev/sdb | 0 | 3TB | jewel +CentOS7 | ceph102 | 192.168.1.102 | /dev/sdb | 1 | 3TB | jewel +CentOS7 | ceph103 | 192.168.1.103 | /dev/sdb | 2 | 3TB | jewel + +- ceph 部署机 + - 操作系统: CentOS7 + - 部署用户: cephdeploy + - 操作目录: /home/cephdeploy/ceph-cluster + - IP: 192.168.1.100 + +# 新增 OSD +- 设置 ceph 三个 noflag,禁止 ceph 自动迁移数据 + ```bash + # 在任一节点上执行下面命令即可 + ceph osd set noout + ceph osd set nobackfill + ceph osd set norecover + ceph -s # 此时能看到这三个 flag,而且集群处于不健康状态 + ``` +- 关闭这三台 ceph 服务器, 加装新的磁盘,个人认为无需配置多盘 raid +- 启动这三台 ceph 服务器,此时 ceph 自动启动,那三个 noflag 依旧有效 +- 此时查看每台服务器的存储,应该能看到新增的一个或多个裸磁盘 + ```bash + lsblk + ``` +- 这里假设每台服务器新增两个磁盘(/dev/sdc,/dev/sdd),都配置成 osd + ```bash + # 在 ceph 部署机上执行以下命令 + su - cephdeploy + cd /home/cephdeploy/ceph-cluster + # 每次创建一个,别浪 ... + ceph-deploy osd create ceph101:/dev/sdc + ceph-deploy osd create ceph101:/dev/sdd + ceph-deploy osd create ceph102:/dev/sdc + ceph-deploy osd create ceph102:/dev/sdd + ceph-deploy osd create ceph103:/dev/sdc + ceph-deploy osd create ceph103:/dev/sdd + ``` +- osd 增加完成后, 取消之前设置的那三个 noflag + ```bash + # 在任一节点上执行下面命令即可 + ceph osd unset noout + ceph osd unset nobackfill + ceph osd unset norecover + ``` +- 此时数据向新增的 osd 上均衡,时间不确定 …… 只能等! + ```bash + # 在任一节点上执行下面命令,观察数据迁移 + ceph -w + ``` +- 直至 ceph 恢复健康状态 + ```bash + # 在任一节点上执行下面命令,查看集群状态 + ceph -s + ``` + +# 删除 OSD +- 把指定的 osd 踢出集群 + ```bash + # 在任一节点上执行下面命令即可 + ceph osd out {osd-id} + ceph -s # 此时能看到一个 osd 已经 out + ``` +- 此时数据在剩下的几个 osd 上均衡,时间不确定 …… 只能等! + ```bash + # 在任一节点上执行下面命令,观察数据迁移 + ceph -w + ``` +- 直至 ceph 恢复健康状态 + ```bash + # 在任一节点上执行下面命令,查看集群状态 + ceph -s + ``` +- 停止该被踢出的 osd + ```bash + # 在运行该 osd 的节点上执行下面命令 + systemctl stop ceph-osd@{osd-id} + ceph -s # 此时能看到一个 osd 已经 down + ``` +- 删除该被停止的 osd + ```bash + # 在任一节点上执行下面命令即可 + # 删除 CRUSH 图对应的 osd 条目 + ceph osd crush remove osd.{osd-id} + # 删除 osd 认证密钥 + ceph auth del osd.{osd-id} + # 删除 osd + ceph osd rm {osd-num} + # 删除各节点的 ceph.conf 可能存在的 osd.{osd-id} 配置 + ``` +- 设置 ceph 三个 noflag,禁止 ceph 自动迁移数据 + ```bash + # 在任一节点上执行下面命令即可 + ceph osd set noout + ceph osd set nobackfill + ceph osd set norecover + ceph -s # 此时能看到这三个 flag,而且集群处于不健康状态 + ``` +- 关闭这三台 ceph 服务器,撤掉已被删除 osd 对应的旧磁盘 +- 启动这三台 ceph 服务器,此时 ceph 自动启动,三个 noflag 依旧有效; +- 取消之前设置的那三个 noflag + ```bash + # 在任一节点上执行下面命令即可 + ceph osd unset noout + ceph osd unset nobackfill + ceph osd unset norecover + ``` +- 直至 ceph 恢复健康状态 + ```bash + # 在任一节点上执行下面命令,查看集群状态 + ceph -s + ``` + +# OSD 动态配置 +- 查看 osd 当前配置 + ```bash + ceph -n osd.0 --show-config + ``` +- 动态修改 osd 某个参数 + ```bash + ceph tell osd.* injectargs '--osd_max_backfills 7' + ``` + +# PG 和 PGP +- 少于 5 个 OSD 时可把 pg_num 设置为 128 +- OSD 数量在 5 到 10 个时,可把 pg_num 设置为 512 +- OSD 数量在 10 到 50 个时,可把 pg_num 设置为 1024 +- OSD 数量大于 50 时, * 100/副本数量(默认3),该值接近的 2 的 N 次方值 +- 存储池的 PG 和 PGP 数量一般相等,都是 2 的 N 次方,只能增加,每次增加为当前的 2 倍 +- 查看存储池的 PG 和 PGP 数量 + ```bash + ceph osd pool get {pool_name} pg_num + ceph osd pool get {pool_name} pgp_num + ``` +- 增加/设置存储池的 PG 和 PGP 数量 + ```bash + ceph osd pool set {pool_name} *2 + ceph osd pool set {pool_name} *2 + ``` +- 获取所有卡在某状态的归置组统计信息 + ```bash + ceph pg dump_stuck inactive|unclean|stale|undersized|degraded + #Inactive (不活跃)归置组不能处理读写,因为它们在等待一个有最新数据的 OSD 复活且进入集群 + #Unclean (不干净)归置组含有复制数未达到期望数量的对象,它们应该在恢复中 + ``` +- 获取一个具体归置组的归置组图 + ```bash + ceph pg map {pg-id} + ``` + +# CEPH 服务器关机维护 +- 设置 ceph 节点 down 后不自动迁移或恢复数据 + ```bash + ceph osd set noout + ceph osd set nobackfill + ceph osd set norecover + ``` +- 直接关机 +- 下次开机 +- 设置 ceph 节点开始自动迁移或回复数据 + ```bash + ceph osd unset noout + ceph osd unset nobackfill + ceph osd unset norecover + ``` + diff --git a/content/post/ch-buffer.md b/content/post/ch-buffer.md new file mode 100644 index 0000000..f6f6877 --- /dev/null +++ b/content/post/ch-buffer.md @@ -0,0 +1,39 @@ +--- +title: "ClickHouse 表引擎之 Buffer" +date: 2020-10-08T18:08:00+08:00 +lastmod: 2020-10-08T18:08:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Buffer 表引擎简介 +- 只写内存,无持久化存储 +- 缓冲高并发写入,满足条件时,Buffer 表会把数据刷新到目标表 + +# 创建 Join 引擎表 +- 声明 + ```sql + ENGINE = Buffer( + database, + table, + num_layers, + min_time, + max_time, + min_rows, + max_rows, + min_bytes, + max_bytes + ) + ``` + +- database: 目标表所在数据库 +- table: 目标表名 +- num_layers: 刷新数据到目标表时开启的线程数,官方建议 16 +- min_time 和 max_time: 时间条件,单位秒 +- min_rows 和 max_rows: 数据行数条件 +- min_bytes 和 max_bytes: 数据体量条件,单位字节 +- 所有最小阈值都满足时,触发刷新 +- 至少一个最大阈值满足时,触发刷新 +- 如果一批数据的行数大于 max_rows,或体量大于 max_bytes,则数据直接写入目标表 +- 各线程(num_layers)单独计算刷新规则 + diff --git a/content/post/ch-datadict.md b/content/post/ch-datadict.md new file mode 100644 index 0000000..66d1a22 --- /dev/null +++ b/content/post/ch-datadict.md @@ -0,0 +1,255 @@ +--- +title: "ClickHouse 数据字典" +date: 2020-09-23T13:30:00+08:00 +lastmod: 2020-10-08T18:50:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 简介 +- 常驻内存,支持动态更新 +- 适合保存常量和经常使用的维度表数据 +- 可通过字典函数访问,也可通过袋里表与其他数据表实现 JOIN 查询 + +# 内置字典 +- 默认禁用 +- 不想写了,没啥意思 + +# 外部扩展字典 +## 配置文件 +- 位置: /etc/clickhouse-server/\*\_dictionary.xml +- 自动感知变更,不停机在线更新 +- 系统表: system.dictionaries +- 配置结构 + ```xml + + + + + dict_name + + + + + + field_name + + + + + + field_name + field_type + + + ... + + + + + + + field_name + String + + + + + + field_name + + + field_name + + + + + + field_name + + + field_type + + + + + + + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + 16384 + + + + + + + + 16384 + + + + + + + + + + + /path/to/data.csv + CSV + + + + + cat /path/to/data.csv + CSV + + + + + http://192.168.1.2:9080/data.csv + CSV + + + + + root + 123456 + + 192.168.1.3 + 1 + + 3306 + db_name + table_name
+ + + id=1 + + + sql +
+ + + + default + + 192.168.1.4 + 9000 + default + table_name
+ id=1 + sql +
+ + + + + + 192.168.1.5 + 27017 + db_name + collection_name + + + + + + + + 300 + 360 + +
+ + ... + +
+ ``` + +## 操作 +- 手动更新全部数据字典 + ```sql + SYSTEM RELOAD DICTIONARIES; + ``` + +- 手动更新指定字典 + ```sql + SYSTEM RELOAD DICTIONARY dict_name + ``` + +- 查看所有字典信息 + ```sql + SELECT name, type, key, attribute.names, attribute.types source FROM system.dictionaries; + ``` + +- 字典函数查询 + ```sql + SELECT dictGet('dict_name', 'attr_name', key) + ``` + +- 其他字典函数 + - 无符号整型: dictGetUInt8、dictGetUInt16、dictGetUInt32、dictGetUInt64 + - 整型: dictGetInt8、dictGetInt16、dictGetInt32、dictGetInt64 + - 浮点数: dictGetFloat32、dictGetFloat64 + - 字符串: ditGetString、dictGetUUID + - 日期: dictGetDate、dictGetDateTime + +- ddl 创建字典 + ```sql + CREATE DICTIONARY dict_name( + ... + ) PRIMARY KEY id + LAYOUT(FLAT()) + SOURCE(FILE(PATH '/path/to/data.csv' FORMAT CSV)) + LIFETIME(1); + ``` + +# Dictionary 表引擎 +- 创建字典表 + ```sql + CREATE TABLE table_name( + ... + ) ENGINE = Dictionary(dict_name); + ``` + +- dict_name: 已加载的字典名称 +- 创建字典数据库,自动为每个字典创建对应的字典表 + ```sql + CREATE DATABASE dict_db ENGINE = Dictionary; + ``` + diff --git a/content/post/ch-datatype.md b/content/post/ch-datatype.md new file mode 100644 index 0000000..cbbfe8f --- /dev/null +++ b/content/post/ch-datatype.md @@ -0,0 +1,343 @@ +--- +title: "ClickHouse 数据定义" +date: 2020-09-23T12:00:00+08:00 +lastmod: 2020-09-23T12:00:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 基础类型 +## 整数 +声明 | 大小(字节) | 范围 +---- | ---- | ---- +Int8 | 1 | -128 到 127 +UInt8 | 1 | 0 到 255 +Int16 | 2 | -32768 到 32767 +UInt16 | 2 | 0 到 65535 +Int32 | 4 | -2147483648 到 2147483647 +UInt32 | 4 | 0 到 4294967295 +Int64 | 8 | -9223372036854775808 到 9223372036854775807 +UInt64 | 8 | 0 到 18446744073709551615 + +## 浮点数 +声明 | 大小(字节) | 有效精度(位数) +---- | ---- | ---- +Float32 | 4 | 7 +Float64 | 8 | 16 + +- 正无穷: SELECT 0.8/0 +- 负无穷: SELECT -0.8/0 +- 非数字: SELECT 0/0 + +## 定点数 +- 原生声明: Decimal(P,S) + - P: 总位数(整数+小数),取值范围 1~38 + - S: 小数位数,取值范围 0~P + +- 其他声明 + - Decimal32(S): -10^(9-S) 到 10^(9-S) + - Decimal64(S): -10^(18-S) 到 10^(18-S) + - Decimal128(S): -10^(18-S) 到 10^(38-S) + +## 字符串 +声明 | 备注 +---- | ---- +String | 长度不固定,不限字符集,建议 UTF-8 +FixedString(N) | 长度固定,null 字节填充 +UUID | 格式是 8-4-4-4-12,0 填充 + +## 时间 +声明 | 精度 | 示例 +---- | ---- | ---- +Datetime | 秒 | 2020-09-18 19:59:00 +Datetime64(N) | 亚秒 | 2020-09-18 19:59:00.00 +Date | 日 | 2020-09-18 + +# 复合类型 +## 数组 +- 声明: [T], Array(T) +- 查询时会以最小储存代价为原则推断类型 +- 元素类型可以不同,但必须兼容 + +## 元组 +- 声明: (T), tuple(T) +- 查询时会以最小储存代价为原则推断类型 +- 元素类型可以不同,且无须兼容 + +## 枚举 +声明 | Key 类型 | Value 类型 +---- | ---- | ---- +Enum8('k1'=1,'k2'=2, ... ,'kN'=N) | String | Int8 +Enum16('k1'=1,'k2'=2, ... ,'kN'=N) | String | Int16 + +- 枚举的所有后续操作,都会使用 Int 类型的 Value 值 + +## 嵌套 +- 声明 + ```sql + Nested( + column1 T, + column2 T + ) + ``` +- 嵌套字段中的每一列期望写入的是数组: Array(T) +- 同一行数据内,嵌套字段中的每一列的数组长度必须相等 +- 访问嵌套字段中的列时用点(.)连接 + +## 可空类型 +- 声明: Nullable(T) +- 只能和基础类型搭配使用,不用用于复合类型和索引字段 +- 慎用,会额外生成 [Column].null.bin 文件保存 null 值,导致双倍文件操作,使查询和写入变慢 + +## 域名类型 +声明 | 封装类型 +---- | ---- +IPv4 | UInt32 +IPv6 | FixedString(16) + +- Domain 类型不是字符串,不支持自动类型转换 +- 调用 IPv4NumToString 或 IPv6NumToString 函数返回 IP 的字符串形式 + +# 数据库 +## 操作 +- 创建 + ```sql + CREATE DATABASE [IF NOT EXISTS] db_name [ENGINE = engine]; + ``` + +- 查看数据库列表 + ```sql + SHOW DATABASES; + ``` + +- 切换 + ```sql + USE db_name; + ``` + +- 查看当前数据库中的数据表列表 + ```sql + SHOW TABLES; + ``` + +## 引擎 +- Ordinary: 默认引擎,无须刻意声明,可以使用任意类型表引擎 +- Dictionary: 字典引擎,自动为所有数据字典创建数据表 +- Memory: 内存引擎,存放临时数据,数据只停留在内存中 +- Lazy: 日志引擎,只能使用 Log 系列的表引擎 +- MySQL: MySQL 引擎,自动拉取远端 MySQL 中的数据,并创建 MySQL 表引擎的数据表 + +# 数据表 +## 操作 +- 常规建表,默认在 default 数据库中创建 + ```sql + CREATE TABLE [IF NOT EXISTS] [db_name.]table_name { + column1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], + column1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], + ... + columnM Nested( + column11 [type], + column22 [type], + ... + ), + ... + } ENGINE = engine; + ``` + +- 复制其他表的结构 + ```sql + CREATE TABLE [IF NOT EXISTS] [db_name.]table_name + AS [db_name1.]table_name1 + [ENGINE = engine]; + ``` + +- 通过 SELECT 字句建表,顺带写入 SELECT 子查询的数据 + ```sql + CREATE TABLE [IF NOT EXISTS] [db_name.]table_name + ENGINE = engine + AS SELECT ... ; + ``` + +- 删表 + ```sql + DROP TABLE [IF EXISTS] [db_name.]table_name; + ``` + +- 增加表字段,默认值补全 + ```sql + ALTER TABLE [db_name.]table_name ADD COLUMN [IF NOT EXISTS] + col_name [type] [default_expr] [AFTER col_name_after]; + ``` + +- 修改表字段 + ```sql + ALTER TABLE [db_name.]table_name MODIFY COLUMN [IF EXISTS] + col_name [type] [default_expr]; + ``` + +- 修改备注 + ```sql + ALTER TABLE [db_name.]table_name COMMENT COLUMN [IF EXISTS] + name 'some comment'; + ``` + +- 删除字段 + ```sql + ALTER TABLE [db_name.]table_name DROP COLUMN [IF EXISTS] name; + ``` + +- 移动数据表,只能在单节点内移动 + ```sql + RENAME TABLE [db_name.]tb_name TO [db_name1.]tb_name1, + [db_name2.]tb_name2 TO [db_name3.]tb_name3, + ... ; + ``` + +- 清空数据表 + ```sql + TRUNCATE TABLE [IF EXISTS] [db_name.]tb_name; + ``` + +## 表字段默认值 +- 声明: DEFAULT、MATERIALIZED、ALIAS +- 如果表字段没有明确类型定义,则可根据默认值进行类型推断 +- 写入数据时,只有 DEFAULT 类型字段可以 INSERT +- 查询数据时,只有 DEFUALT 类型字段可以 SELECT * 返回 +- DEFAULT 和 MATERIALIZED 类型字段可以持久化 + +# 临时表 +## 操作 +- 创建 + ```sql + CREATE TEMPORARY TABLE [IF NOT EXISTS] table_name { + column1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], + column1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], + ... + columnM Nested( + column11 [type], + column22 [type], + ... + ), + ... + }; + ``` + +## 其他 +- 临时表只支持 Memory 表引擎,与会话绑定 +- 临时表不属于任何数据库,创建时无数据库参数和表引擎参数 +- 临时表优先级大于普通表 + +# 分区表 +## 操作 +- 创建 + ```sql + CREATE TABLE partition_v1 ( + ID String, + URL String, + EventTime Date + ) ENGINE = MergeTree() + PARTITION BY toYYYYMMM(EventTime) + GROUP BY ID; + ``` + +- 查看分区状态 + ```sql + SELECT table,partition,path from system.parts WHERE table = 'partition_v1' + ``` + +- 删除分区 + ```sql + ALTER TABLE tb_name DROP PARTITION part_expr; + ``` + +- 复制分区,前提是两表的结构和分区键相同 + ```sql + ALTER TABLE table_name1 REPLACE PARTITION part_expr FROM table_name; + ``` + +- 重置分区数据 + ```sql + ALTER TABLE table_name CLEAR COLUMN col_name IN PARTITION part_expr; + ``` + +- 卸载分区 + ```sql + ALTER TABLE table_name DETACH PARTITION part_expr; + ``` + +- 装载分区 + ```sql + ALTER TABLE table_name ATTACH PARTITION part_expr; + ``` + +## 其他 +- 分区支持删除、替换和重置,只有 MergeTree 系列表引擎支持 + +# 视图 +## 操作 +- 创建普通视图 + ```sql + CREATE VIEW [IF NOT EXISTS] [db_name.]view_name AS SELECT ... ; + ``` + +- 创建物化视图 + ```sql + CREATE [MATERIALIZED] VIEW [IF NOT EXISTS] [db.]table_name + [TO [db.]name] [ENGINE = engine] [POPULATE] AS SELECT ... ; + # 如果使用了 POPULATE 修饰符,那么在创建视图时会一并导入 SELECT 结果集 + ``` + +- 查看物化视图列表 + ```sql + SHOW TABLES + # 输出前缀是 .inner. + ``` + +- 删除视图 + ```sql + DROP TABLE view_name + ``` + +## 其他 +- 普通视图只是查询代理 +- 物化视图有独立存储,不支持同步删除 + +# 分布式 DDL +- 使用 ON CLUSTER cluster_name 声明语句 + ```sql + CREATE TABLE table_name ON CLUSTER cluster_name( + col1 [type], + col2 [type], + ... + ) ENGINE = engine ... ; + ``` + +# 写入数据 +- INSERT 语句三种语法 + ```sql + INSERT INTO [db_name.]table_name [(c1,c2,c3 ...)] + VALUES (v1,v2,v3 ...), (v4,v5,v6 ...) ... ; + + INSERT INTO [db_name.]table_name [(c1,c2,c3 ...)] + FORMAT format_name data_set; + + INSERT INTO [db_name.]table_name [(c1,c2,c3 ...)] SELECT ... ; + ``` + +# 修改和删除数据 +## 操作 +- 删除 + ```sql + ALTER TABLE [db_name.]table_name DELETE WHERE filter_expr; + ``` + +- 修改 + ```sql + ALTER TABLE [db_name.]table_name UPDATE col1 = expr1 [, ...] + WHERE filter_expr; + ``` + +## 其他 +- mutation 操作很重,后台执行,语句提交后立即返回,不支持事务,不能回滚 +- 通过 system.mutations 系统表查询进度 + diff --git a/content/post/ch-file.md b/content/post/ch-file.md new file mode 100644 index 0000000..e80e3f0 --- /dev/null +++ b/content/post/ch-file.md @@ -0,0 +1,30 @@ +--- +title: "ClickHouse 表引擎之 File" +date: 2020-10-08T12:00:00+08:00 +lastmod: 2020-10-08T12:00:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# File 表引擎简介 +- 直接读取本地文件 +- 修改文件 = 数据更新 +- 导出数据到本地文件 +- 数据格式转换 + +# 创建 FILE 引擎表 +- 声明 + ```sql + ENGINE = File('format') + ``` + +- format 是文件中的数据格式,如 TSV、CSV 和 JSONEachRow 等 +- 数据文件保存在 config.xml 中指定的 path 下,目录名是表名,数据文件名是 data.{format} +- 可以通过 CREATE 语句建表,也可以直接在 shell 下创建目录文件,再通过 ATTACH 语句挂载 + ```sql + ATTACH TABLE file_table( + name String, + value UInt32 + ) ENGINE = file(CSV) + ``` + diff --git a/content/post/ch-install.md b/content/post/ch-install.md new file mode 100644 index 0000000..710ab64 --- /dev/null +++ b/content/post/ch-install.md @@ -0,0 +1,250 @@ +--- +title: "CentOS7 安装 ClickHouse 集群" +date: 2020-09-23T10:18:00+08:00 +lastmod: 2020-10-10T01:40:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 环境 +## Zookeeper 服务器 +eth0 IP | eth1 IP | 操作系统 | ZK 版本 | myid +---- | ---- | ---- | ---- | ---- +10.0.4.101 | 10.1.4.101 | CentOS7.8 | 3.4.14 | 101 +10.0.4.102 | 10.1.4.102 | CentOS7.8 | 3.4.14 | 102 +10.0.4.103 | 10.1.4.103 | CentOS7.8 | 3.4.14 | 103 + +- eth0 网卡用于向客户端提供服务,eth1 网卡用于 Zookeeper 集群内部通信 +- 配置时间同步,关闭 selinux 和 firewalld + +## ClickHouse 服务器 +eth0 IP | eth1 IP | 操作系统 | CH 版本 | shard 值 | replica 值 +---- | ---- | ---- | ---- | ---- | ---- +10.0.4.181 | 10.1.4.181 | CentOS7.8 | 20.3 LTS | 1 | 10.1.4.181 +10.0.4.182 | 10.1.4.182 | CentOS7.8 | 20.3 LTS | 1 | 10.1.4.182 +10.0.4.183 | 10.1.4.183 | CentOS7.8 | 20.3 LTS | 2 | 10.1.4.183 +10.0.4.184 | 10.1.4.184 | CentOS7.8 | 20.3 LTS | 2 | 10.1.4.184 +10.0.4.185 | 10.1.4.185 | CentOS7.8 | 20.3 LTS | 3 | 10.1.4.185 +10.0.4.186 | 10.1.4.186 | CentOS7.8 | 20.3 LTS | 3 | 10.1.4.186 + +- eth0 网卡用于向客户端提供服务,eth1 网卡用于 ClickHouse 集群内部通信 +- 配置时间同步,关闭 selinux 和 firewalld + +# 安装 Zookeeper 集群 +- ClickHouse 集群依赖 zookeeper 管理集群配置 +- 安装过程参考: [CentOS7 安装 zookeeper 集群](/post/zk-install/) +- 启动 zookeeper 集群,zookeeper 正常运行后,才能进行后续步骤 + +# 安装 ClickHouse 集群 +## 配置 ClickHouse yum 源 +- 在每台 ClickHouse 服务器上执行如下操作 +- 生成 clickhouse.repo 文件 + ```bash + echo '[clickhouse-lts] + name=ClickHouse - LTS Repository + baseurl=https://mirrors.tuna.tsinghua.edu.cn/clickhouse/rpm/lts/$basearch/ + gpgkey=https://mirrors.tuna.tsinghua.edu.cn/clickhouse/CLICKHOUSE-KEY.GPG + gpgcheck=1 + enabled=1 + EOF + ' > /etc/yum.repos.d/clickhouse.repo + ``` + +- 重建 yum 缓存 + ```bash + yum clean all + yum makecache fast + ``` + +## 安装 ClickHouse +- 在每台 ClickHouse 服务器上执行如下操作 +- 安装 clickhouse-server 和 clickhouse-client + ```bash + yum install clickhouse-server clickhouse-client + ``` + +## 修改 ClickHouse 配置 +- 在每台 ClickHouse 服务器上执行如下操作 +- 我没用 /etc/metrika.xml 和 config.d 子目录,直接修改的 config.xml,先备份 + ```bash + cd /etc/clickhouse-server/ + cp config.xml config.xml.origin + ``` + +- 编辑 /etc/clickhouse-server/config.xml,修改部分如下 + ```xml + + 10.1.4.181 + 10.1.4.182 + 10.1.4.183 + 10.1.4.184 + 10.1.4.185 + 10.1.4.186 + + + 0.0.0.0 + + + /var/lib/clickhouse/ + + + + + + 1073741824 + + + /clickhouse/disk1/ + + + /clickhouse/disk2/ + + + + + + + disk1 + disk2 + + + + + + + + true + + + + + + true + + 10.1.4.181 + 9000 + + + 10.1.4.182 + 9000 + + + + true + + 10.1.4.183 + 9000 + + + 10.1.4.184 + 9000 + + + + true + + 10.1.4.185 + 9000 + + + 10.1.4.186 + 9000 + + + + + + + + + 10.0.4.101 + 2181 + + + 10.0.4.102 + 2181 + + + 10.0.4.103 + 2181 + + + + + + 1 + 10.1.4.181 + + + 1 + 10.1.4.182 + + + 2 + 10.1.4.183 + + + 2 + 10.1.4.184 + + + 3 + 10.1.4.185 + + + 3 + 10.1.4.186 + + ``` + +## 启动 ClickHouse +- 在每台 ClickHouse 服务器上执行如下操作 +- 启动 clickhouse-server 服务 + ```bash + systemctl start clickhouse-server + ``` + +# 查看集群状态 +- 在任一 ClickHouse 服务器上执行如下操作 +- 查询 system.cluster 表 + ```sql + SELECT * FROM system.clusters; + ``` + +# 简单使用 +- 在任意节点上登陆 clickhouse + ```bash + clickhouse-client -h 127.0.0.1 + ``` + +- 创建数据库 + ```sql + CREATE DATABASE db1 ON CLUSTER cluser_3s2r; + USE db1; + ``` + +- 创建数据表 + ```sql + CREATE TABLE db1.t1_local + ON CLUSTER cluster_3s2r ( + col1 UInt32, + col2 String + ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/t1_local', '{replica}') + ORDER BY (col1) + SETTINGS STORAGE_POLICY='policy_jbod'; + ``` + +- 创建数据表对应的分布式代理表 + ```sql + CREATE TABLE db1.t1 + ON CLUSTER cluster_3s2r + AS db1.t1_local + ENGINE = Distributed(cluster_3s2r, db1, t1_local, rand()); + ``` + +- 通过分布式代理表写入和查询数据 + ```sql + INSERT INTO db1.t1 values(1,'aa'); + SELECT * FROM db1.t1; + ``` + diff --git a/content/post/ch-join.md b/content/post/ch-join.md new file mode 100644 index 0000000..105274a --- /dev/null +++ b/content/post/ch-join.md @@ -0,0 +1,27 @@ +--- +title: "ClickHouse 表引擎之 Join" +date: 2020-10-08T17:40:00+08:00 +lastmod: 2020-10-08T17:40:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Join 表引擎简介 +- 数据先写内存,再同步到磁盘,服务重启后全量加载到内存 +- 与 Set 表引擎共用大部分处理逻辑 +- 简单封装了一层 JOIN 查询,主要用做 JOIN 查询 + +# 创建 Join 引擎表 +- 声明 + ```sql + ENGINE = Join(join_strictness, join_type, key1[, key2, ...]) + ``` + +- join_strictness: 连接精度,支持 ALL、ANY 和 ASOF +- join_type: 连接类型,支持 INNRE、OUTER、CROSS +- key1, key2 ...: 连接键,关联字段 +- 如果连接精度是 ANY,写入数据时会忽略连接键相同的数据 + +# 参考 +- [ClickHouse 表引擎之 Join](/post/ch-search/#JOIN) + diff --git a/content/post/ch-kafka.md b/content/post/ch-kafka.md new file mode 100644 index 0000000..c0e4e45 --- /dev/null +++ b/content/post/ch-kafka.md @@ -0,0 +1,73 @@ +--- +title: "ClickHouse 表引擎之 Kafka" +date: 2020-10-08T10:42:00+08:00 +lastmod: 2020-10-08T10:42:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Kafka 表引擎简介 +- 对接 Kafka 系统,订阅 Kafka 主题,接收消息 + +# 创建 Kafka 引擎表 +- 声明 + ```sql + ENGINE = Kafka() + SETTINGS + kafka_broker_list = 'host:port, ...', + kafka_topic_list = 'topic1, topic2, ...', + kafka_group_name = 'consumer_group_name', + kafka_format = 'data_format', + [kafka_row_delimiter = 'delimiter_symbol',] + [kafka_schema = '',] + [kafka_num_consumers = N,] + [kafka_skip_broken_messages = N,] + [kafka_commit_every_batch =N]; + ``` + +- kafka_broker_list: kafka 节点地址列表,用逗号分隔 +- kafka_topic_list: 订阅的主题列表,用逗号分隔 +- kafka_group_name: 消费组名称,引擎会依据此名称创建消费组 +- kafka_format: 消息格式,如 TSV、JSONEachRow、CSV 等 +- kafka_row_delimiter: 一行数据的结束符,默认 '\0' +- kafka_schema: kafka schema 参数 +- kafka_num_consumers: 消费者数量,默认 1 +- kafka_skip_broken_messages: 允许跳过的错误消息数量,默认0 +- kafka_commit_every_batch: kafka commit 频率,默认 0,即整个 Block 完全写入后才 commit + +# Kafka 表引擎其他参数 +- stream_poll_timeout_ms: 默认每 500ms 消费一次数据,写入缓存 +- 刷新缓存触发条件: + - 一个数据块(kafka_max_block_size,默认 65536)写入完成 + - 等待 7500 毫秒(stream_flush_interval_ms) + +- config.xml 中的 librdkafka 配置,参考 [https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md](https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md) + ```xml + + smallest + + ``` + +# Kafka 引擎表一般用法 +- 创建 Kafka 引擎表,充当数据管道 +- 创建 MergeTree 引擎表,用于查询 +- 创建物化视图,同步 kafka 数据到 MergeTree 引擎表 + ```sql + CREATE MATERIALIZED VIEW kafka_view TO mergetree_table + AS SELECT col1, col2, ... FROM kafka_table; + ``` + +- 要停止数据同步,可以删除视图,也可以卸载视图 + ```sql + -- 删除 + DROP TABLE kafka_view; + -- 卸载 + DETACH TABLE kafka_view; + ``` + +- 恢复数据同步,装载视图 + ```sql + ATTACH MATERIALIZED VIEW kafka_view TO mergetree_table + AS SELECT col1, col2, ... FROM kafka_table; + ``` + diff --git a/content/post/ch-liveview.md b/content/post/ch-liveview.md new file mode 100644 index 0000000..5e92c97 --- /dev/null +++ b/content/post/ch-liveview.md @@ -0,0 +1,26 @@ +--- +title: "ClickHouse 视图之 Live View" +date: 2020-10-08T19:05:00+08:00 +lastmod: 2020-10-08T19:05:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Live View 视图简介 +- 类似时间监听器 +- 需设置 allow_experimental_live_view 为 1,检查 + ```sql + SELECT name, value FROM system.settings WHERE name LIKE '%live_view%'; + ``` + +# 创建 Live View 视图 +- 创建 + ```sql + CREATE LIVE VIEW lv_name AS SELECT count(*) FROM table_name; + ``` + +- 监听 + ```sql + WATCH lv_name + ``` + diff --git a/content/post/ch-log.md b/content/post/ch-log.md new file mode 100644 index 0000000..008bb1f --- /dev/null +++ b/content/post/ch-log.md @@ -0,0 +1,36 @@ +--- +title: "ClickHouse 表引擎之日志" +date: 2020-10-08T18:23:00+08:00 +lastmod: 2020-10-08T18:23:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 日志表引擎简介 +- 数据量 100 万行一下,一次写入多次查询 +- 不支持索引、分区、并发读写 + +# TinyLog 表引擎 +- 数据文件按列存储 +- 无标记文件,不支持并行读取 +- 声明 + ```sql + ENGINE = TinyLog() + ``` + +# StripeLog 表引擎 +- 只用数据写入一个文件 +- 有数据标记文件,可并行读取 +- 声明 + ```sql + ENGINE = StripeLog() + ``` + +# Log 表引擎 +- 数据文件按列存储 +- 有数据标记文件,可并行读取 +- 声明 + ```sql + ENGINE = Log() + ``` + diff --git a/content/post/ch-memory.md b/content/post/ch-memory.md new file mode 100644 index 0000000..9d282f4 --- /dev/null +++ b/content/post/ch-memory.md @@ -0,0 +1,19 @@ +--- +title: "ClickHouse 表引擎之 Memory" +date: 2020-10-08T13:00:00+08:00 +lastmod: 2020-10-08T13:00:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Memory 表引擎简介 +- 数据只存于内存中,无压缩,无格式转换 +- 支持并行查询 +- 一般用于 clickhouse 内部,作为集群间分发数据的载体 + +# 创建 Memory 引擎表 +- 声明 + ```sql + ENGINE = Memory() + ``` + diff --git a/content/post/ch-merge.md b/content/post/ch-merge.md new file mode 100644 index 0000000..3302081 --- /dev/null +++ b/content/post/ch-merge.md @@ -0,0 +1,24 @@ +--- +title: "ClickHouse 表引擎之 Merge" +date: 2020-10-08T18:35:00+08:00 +lastmod: 2020-10-08T18:35:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Merge 表引擎简介 +- 本身不存储数据,只整合其他数据表 +- 不支持数据写入 +- 合并异步查询的结果集 +- 各异步查询的数据表需要在同一个数据库下,且表结构相同,表引擎和分区定义可不同 + +# 创建 Merge 表引擎 +- 声明 + ```sql + ENGINE = Merge(database, table_name) + ``` + +- database: 数据库名 +- table_name: 数据表名,支持正则表达式 +- Merge 引擎表可以使用虚拟字段 "\_table" 来查询和过滤数据表 + diff --git a/content/post/ch-mergetree.md b/content/post/ch-mergetree.md new file mode 100644 index 0000000..b0c2edb --- /dev/null +++ b/content/post/ch-mergetree.md @@ -0,0 +1,388 @@ +--- +title: "ClickHouse 表引擎之 MergeTree(合并树)" +date: 2020-10-06T19:55:00+08:00 +lastmod: 2020-10-07T22:54:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 简介 +- 支持主键索引、数据分区、数据副本、数据采样、ALTER 操作 +- 扩展表引擎丰富,生产环境中大多使用该表引擎 +- 数据以片段形式写入磁盘,后台定期合并片段到各分区相应片段 + +# 数据表 +- 建表语句 + ```sql + CREATE TABLE [IF NOT EXISTS] [db_name.]table_name( + ... + ) ENGINE = MergeTree() + [PARTITION BY expr] + [ORDER BY expr] + [PRIMARY KEY expr] + [SAMPLE BY expr] + [SETTINGS name=value, ...]; + ``` + +- PARTITION BY: 分区键,选填,支持单字段、多字段和表达式,默认生成一个 all 分区 +- ORDER BY: 排序键,必填,支持单列和元组(包含多列) +- PRIMARY KEY: 主键,选填,默认与排序键相同,允许重复数据 +- SAMPLE BY: 抽样,选填,该配置需在主键中同时声明 +- SETTINGS: 其他参数,选填,示例如下 + - index_granularity: 索引粒度,默认 8192,通常不需要修改 + - index_granularity_bytes: 每批次写入的数据大小,用于自适应索引间隔,默认 10MB,0 表示无视数据大小 + - enable_mixed_granularity_parts: 自适应索引间隔,默认开启 + - merge_with_ttl_timeout: TTL 合并间隔时间,默认 86400(1天) + - storage_policy: 数据在硬盘上的存储策略 + +# 数据文件 +- 目录和文件 + ``` + table_name # 表名目录 + |___ partition_1 # 分区目录 + |___ checksums.txt # 校验文件,二进制,记录该分区目录中其他文件的大小和哈希值 + |___ columns.txt # 列信息文件,明文,记录该分区下的列字段信息 + |___ count.txt # 计数文件,明文,记录该分区总行数 + |___ primary.txt # 一级索引文件,二进制,存放稀疏索引 + |___ {column_name}.bin # 列数据文件,默认 LZ4 压缩 + |___ {column_name}.mrk # 列标记文件,二进制,记录对应数据文件(.bin)中的数据偏移量 + |___ {column_name}.mrk2 # 如果表使用了自适应索引间隔,那么对应的列字段标记文件以 .mrk2 命令 + |___ partition.dat # 保存当前分区表达式的值,二进制 + |___ minmax_{column_name}.idx # 保存当前分区字段对应原始数据的最小和最大值,二进制 + |___ skp_idx_{column_name}.idx # 二级索引(跳数索引)文件 + |___ skp_idx_{column_name}.mrk # 二级索引(跳数索引)列的标记文件 + ``` + +# 数据分区 +## 分区 ID +- 单字段分区 ID 生成规则 + +类型 | 样例数据 | 分区表达式 | 分区 ID +---- | ---- | ---- | ---- +无分区键 | - | 无 | all +整型 | 18,19,20 | PARTITION BY Age | 分区1: 18,分区2: 19,分区3: 20 +整型 | 'A0', 'A1', 'A2' | PARTITION BY length(Code) | 分区1: 2 +日期 | 2020-10-05, 2020-10-06 | PARTITION BY EventTime | 分区1: 20201005,分区2: 20201006 +日期 | 2020-09-25, 2020-10-06 | PARTITION BY toYYYYMM(EventTime) | 分区1: 202009,分区2: 202010 +其他 | 'www.colben.cn' | PARTITION BY URL | 分区1: {128 位 Hash 算法} + +- 多字段(元组)分区时, 先按单字段生成对应 ID,再用 "-" 拼接 + +## 分区目录 +- 分区目录命名: PartitionID_MinBlockNum_MaxBlockNum_Level,例如 202010_1_1_0 + - PartitionID: 分区 ID + - MinBlockNum: 最小数据块编号,**表内全局累加**,从 1 开始 + - MaxBlockNum: 最大数据块编号,**表内全局累加**,从 1 开始 + - Level: 分区合并次数,从 0 开始 + +- 不同批次写入的数据,即使分区相同,也会存储在不同目录中 +- 后台在默认 10-15 分钟后自动合并分区相同的多个目录,也可以手动执行 optimize 语句 +- 合并成功后,旧分区目录被置为非激活状态,在默认 8 分钟后被后台删除 +- 合并后新目录的命名规则: + - MinBlockNum: 所有合并目录中的最小 MinBlockNum + - MaxBlockNum: 所有合并目录中的最大 MaxBlockNum + - Level: 所有合并目录中的最大 Level 值并加 1 + +# 数据索引 +- 常驻内存 +- 一级索引是稀疏索引,间隔 index_granularity (默认 8192) 行数据生成一条索引记录 +- 二级索引又称跳数索引,有数据的聚合信息构建而成,在 CREATE 语句中定义如下: + ```sql + INDEX index_name expr TYPE index_type(...) GRANULARITY granularity + -- GRANULARITY 指定一行跳数索引聚合的数据段(index_granularity 区间)的个数 + ``` + +- 跳数索引类型 + - minmax: 记录一段数据内的最小值和最大值 + ```sql + INDEX index_name ID TYPE minmax GRANULARITY 5 + ``` + + - set: 记录字段或表达式的无重复取值 + ```sql + INDEX index_name (length(ID)) TYPE set(100) GRANULARITY 5 + -- 每个数据段(index_granularity 区间)内最多记录 100 条 set 索引记录 + ``` + + - ngrambf_v1: 只支持 String 和 FixedString,只能提升 in、notIn、like、equals 和 notEquals 性能 + ```sql + INDEX index_name (ID, Code) TYPE ngrambf_v1(3, 256, 2, 0) GRANULARITY 5; + -- 3: token 长度,把数据切割成长度为 3 的短语 + -- 256: 布隆过滤器大小 + -- 2: 哈希函数个数 + -- 0: 哈希函数随机种子 + ``` + + - tokenbf_v1: ngrambf_v1 变种,按照非 字母和数字 自动分割 + ```sql + INDEX index_name ID TYPE tokenbf_v1(256, 2, 0) GRANULARITY 5; + -- 注意传参时不需要指定 token 长度 + ``` + +# 数据存储 +- 按列独立存储 +- 默认 LZ4 压缩 +- 按照 order by 排序 +- 以数据压缩块形式写入 .bin 文件,规则如下: + - 单批次数据 < 64KB,继续获取下一批数据 + - 64KB <= 单批次数据 <= 1MB,直接生成压缩数据块 + - 单批次数据 > 1MB,按照 1MB 大小截断并生成数据块,剩余数据继续按前面规则执行 + +# 数据标记 +- 使用 LRU 策略缓存 +- 每一行标记数据记录的是一个数据片段在 .bin 文件中的读取位置 + +# 数据写入 +- 生成分区目录,合并分区相同的目录 +- 按照 index_granularity 索引粒度,生成一级索引、二级索引、数据标记文件和数据压缩文件 + +# 数据查询 +- 借助分区、索引、数据标记来缩小扫描范围 +- 如果未指定查询条件,或条件未匹配到索引,MergeTree 仍可借助数据标记多线程读取压缩数据块 + +# 数据 TTL +## TTL 机制 +- TTL 信息保存在分区目录中的 ttl.txt 中 +- 支持的时间单位: SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR +- 触发 TTL 删除过期数据 + - 后台分区合并 + - merge_with_ttl_timeout 合并频率,默认 86400 秒 + - 手动执行 OPTIMIZE 语句 + +- 合并分区时,TTL 全部到期的数据分区不会参与合并 +- 控制全局 TTL 合并任务 + ```sql + -- 启动 + SYSTEM START TTL MERGES; + -- 停止 + SYSTEM STOP TTL MERGES; + ``` + +## 列级别 TTL +- 到达时间时,列数据被还原为对应数据类型的默认值 +- 主键字段不能被声明 TTL +- 声明列级别 TTL + ```sql + CREATE TABLE table_name( + id String, + create_time DateTime, + code String TTL create_time + INTERVAL 10 SECOND, + type UInt8 TTL create_time + INTERVAL 16 SECOND + ) ENGINE = MergeTree() + PARTITION BY toYYYYMM(create_time) + ORDER BY id; + ``` + +- 修改列级别 TTL + ```sql + ALTER TABLE table_name MODIFY COLUMN code String TTL create_time + INTERVAL 1 DAY; + ``` + +## 表级别 TTL +- 到达时间时,删除过期的数据行 +- 声明表级别 TTL + ```sql + CREATE TABLE table_name( + id String, + create_time DateTime, + code String TTL create_time _ INTERVAL 1 MINUTE, + type UInt8 + ) ENGINE = MergeTree + PARTITION BY toYYYYMM(create_time) + ORDER BY create_time + TTL create_time + INTERVAL 1 DAY; + ``` + +- 修改表级别 TTL + ```sql + ALTER TABLE table_name MODIFY TTL create_time + INTERVAL 3 DAY; + ``` + +# 存储策略 +- 最小移动单元是数据分区 +- 三大策略: 默认、JBOD、HOT/COLD + +## 默认策略 +- 无需配置,所有分区自动保存至 config.xml 中的 path 目录下 + +## JOB 策略 +- 适用于多磁盘无 RAID 场景 +- INSERT 或 MERGE 产生的新分区轮询写入各磁盘,类似 RAID0 +- 磁盘故障时,丢掉相应数据,需要副本机制保障数据可靠性 + +## HOT/COLD 策略 +- 适用于已挂载不同类型磁盘的场景 +- 把磁盘划分到 HOT 和 COLD 两个区域,HOT 使用 SSD,注重性能,CODE 使用 HDD,注重经济 +- 单个区域内可应用 JBOD 策略 + +## 配置策略 +- 配置示例 + ```xml + + + + + + /ch/ssd0 + + 1073741824 + + + /ch/ssd1 + + + /ch/hdd0 + 2147483648 + + + /ch/hdd1 + + + /ch/hdd2 + + + + + + + + + + disk_hot_0 + disk_hot_1 + + + 1073741824 + + + + + + + disk_hot_0 + disk_hot_1 + + + disk_cold_0 + disk_cold_1 + disk_cold_2 + + + + + 0.2 + + + + ``` + +- clickhouse 用户需要有权限读写各存储目录 +- 存储配置不支持动态更新 +- 存储磁盘系统表: system.disks +- 存储策略系统表: system.storage_policies +- 移动分区到其他 disk + ```sql + ALTER TABLE table_name MOVE PART 'part_name' TO DISK 'disk_name'; + ``` + +- 移动分区到其他 volume + ```sql + ALTER TABLE table_name MOVE PART 'part_name' TO VOLUME 'volume_name'; + ``` + +# ReplacingMergeTree +- 依据 ORDER BY 字段去重 +- 合并分区时,**以分区为单位**删除重复数据 +- 声明 + ```sql + ENGINE = ReplacingMergeTree(version_column) + ``` + +- version_column 选填,指定一个 UInt\*、Date 或 DateTime 字段作为版本号 +- 未指定 version_column 时,保留同一组重复数据中的最后一行 +- 指定 version_column 时,保留同一组重复数据中该字段取值最大的一行 + +# SummingMergeTree +- 场景: 用户只需要汇总结果,不关心明细 +- 依据 ORDER BY 字段聚合 +- 合并分区时,触发条件聚合,**以分区为单位**把同一分组下的多行数据汇总成一行 +- 声明: + ```sql + ENGINE = SummingMergeTree((col1,col2, ...)) + ``` + +- col1、col2 选填,不可指定主键,指定被 SUM 汇总的数值类型字段 +- 未指定任何汇总字段时,默认汇总所有非主键的数值类型字段 +- 非汇总字段保留同组内的第一行数据 +- 汇总嵌套字段时,字段名需以 Map 为后缀,默认嵌套字段中第一列作为聚合 Key,其他以 \*Key、\*Id、\*Type 未后缀名的列会和第一列组成复合 Key + +# AggregatingMergeTree +- 预先计算聚合数据,二进制格式存入表中,空间换时间,可看成是 SummingMergeTree 的*升级版* +- 依据 ORDER BY 字段聚合 +- 使用 AggregationFunction 字段类型定义聚合函数和字段 +- 分区合并时,触发**以分区为单位**的合并计算 +- 非汇总字段保留同组内的第一行数据 +- 写数据时调用 \*State 函数,查询时调用 \*Merge 函数 +- 一般用作物化视图的表引擎,与普通 MergeTree 搭配使用,示例如下 + - 创建明细数据表,俗称底表 + ```sql + CREATE TABLE table_name( + id String, + city String, + code String, + value Uint32 + ) ENGINE = MergeTree() + PARTITION BY city + ORDER BY (id, city); + ``` + + - 创建物化视图 + ```sql + CREATE MATERIALIZED VIEW view_name + ENGINE = AggregatingMergeTree() + PARTITION BY city + ORDER BY (id, city) + AS SELECT + id, + city, + uniqState(code) AS code, + sumState(value) AS value + FROM table_name + GROUP BY id, city; + ``` + + - 使用常规 SQL 面向底表增加数据 + - 面向物化视图查询 + ```sql + SELECT id, sumMerge(value), uniqMerge(code) FROM agg_view GROUP BY id,city; + ``` + +# CollapsingMergeTree +- 以增代删 +- 声明 + ```sql + ENGINE = CollapsingMergeTree(sign) + ``` + +- 定义 sign 标记字段,Int8 类型,1 代表有效,-1 代表无效 +- 依据 ORDER BY 字段作为数据唯一性依据 +- 规则 + - 如果 sign=1 比 sign=-1 多一行,则保留最后一行 sign=1 的数据 + - 如果 sign=-1 比 sign=1 多一行,则保留第一行 sign=-1 的数据 + - 如果 sign=-1 和 sign=1 一样多,且最后一行是 sign=1,则保留第一行 sign=-1 和最后一行 sign=1 的数据 + - 如果 sign=-1 和 sign=1 一样多,且最后一行是 sign=-1,则不保留任何数据 + - 其他情况打印告警日志 + +- 合并分区时,触发**以分区为单位**的数据折叠 +- 严格要求数据写入顺序,只有先写入 sign=1,再写入 sign=-1,才能正常折叠 + +# VersionedCollapsingMergeTree +- 与 CollapsingMergeTree 类似,但对数据写入顺序没有要求 +- 声明 + ```sql + ENGINE = VersionedCollapsingMergeTree(sign, ver) + ``` + +- ver 是 UInt8 类型的版本号字段 +- 每个分区内的数据都以 ORDER BY column_name, ver DESC 排序 + diff --git a/content/post/ch-mysql.md b/content/post/ch-mysql.md new file mode 100644 index 0000000..7d6193d --- /dev/null +++ b/content/post/ch-mysql.md @@ -0,0 +1,41 @@ +--- +title: "ClickHouse 表引擎之 MySQL" +date: 2020-10-08T10:15:00+08:00 +lastmod: 2020-10-08T10:15:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# MySQL 表引擎简介 +- 可以与 MySQL 数据库中的表建立映射 +- 只支持 SELECT 和 INSERT,不支持 UPDATE 和 DELETE + +# 创建 MySQL 引擎表 +- 声明 + ```sql + ENGINE = MySQL( + 'host:port', + 'database', + 'table', + 'user', + 'password' + [, + replace_query, + 'on_duplicate_clause' + ] + ) + ``` + +- host:port: mysql 的地址和端口 +- database: mysql 数据库名 +- table: mysql 表名 +- user: mysql 用户名 +- password: mysql 密码 +- replace_query: 对应 mysql 的 replace into 语法,默认 0,不启用 +- on_duplicate_clause: 对应 mysql 的 on duplicate key 语法,默认空,如果要使用,需设置 replace_query 为 0 + +# MySQL 引擎表一般用法 +- 在 mysql 中建表 +- 在 clickhouse 中创建对应的 MySQL 引擎表 +- 在 clickhouse 中创建 MergeTree 引擎的物化视图,从 MySQL 引擎表中读取数据 + diff --git a/content/post/ch-null.md b/content/post/ch-null.md new file mode 100644 index 0000000..b633f2d --- /dev/null +++ b/content/post/ch-null.md @@ -0,0 +1,18 @@ +--- +title: "ClickHouse 表引擎之 Null" +date: 2020-10-08T19:11:00+08:00 +lastmod: 2020-10-08T19:11:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Null 表引擎简介 +- 类似 /dev/null,忽略写入的任何数据,查询时返回空表 +- 如果物化视图不需要保留源表数据,则可设置源表为 Null 引擎 + +# 创建 Null 表引擎 +- 声明 + ```sql + ENGINE = Null + ``` + diff --git a/content/post/ch-ops.md b/content/post/ch-ops.md new file mode 100644 index 0000000..902ed60 --- /dev/null +++ b/content/post/ch-ops.md @@ -0,0 +1,26 @@ +--- +title: "ClickHouse 维护" +date: 2020-10-10T01:30:00+08:00 +lastmod: 2020-10-10T01:30:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 指标 +- system.metrics: 正在执行的概要信息,如正在运行的查询和操作数量 +- system.events: 累积概要信息,如总的查询次数或 SELECT 次数 +- system.asynchronous_metrics: 后台异步运行的概要信息,如分配内存、队列中的人物数量 + +# 日志 +- system.query: 执行用户、查询语句、执行时间、返回数据量等信息 + ```xml + + ... + + ``` + +- query_thread_log: 查询语句、执行线程、执行时间、内存使用量 +- part_log: MergeTree 系列表引擎的操作类型、表名称、分区信息、执行时间 +- text_log: 记录 clickhouse 运行时的终端输出日志 +- metric_log: 汇聚 system.metrics 和 system.events + diff --git a/content/post/ch-replicated.md b/content/post/ch-replicated.md new file mode 100644 index 0000000..118850d --- /dev/null +++ b/content/post/ch-replicated.md @@ -0,0 +1,181 @@ +--- +title: "ClickHouse 集群" +date: 2020-10-09T22:14:00+08:00 +lastmod: 2020-10-09T22:14:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 简介 +- 一个集群包含多个逻辑分片,每个逻辑分片包含多个副本节点 +- 向集群内读写数据时,需依赖 Distributed 引擎表做为代理,实现数据的分发、写入、查询和路由 +- ReplicatedMerge 表引擎配合 zookeeper 实现数据的复制 + +# 集群配置 +- 节点分配 + ```xml + + + + true + 1 + + 10.1.4.181 + 9000 + + + 10.1.4.182 + 9000 + + + + true + + 10.1.4.183 + 9000 + + + 10.1.4.184 + 9000 + + + + true + + 10.1.4.185 + 9000 + + + 10.1.4.186 + 9000 + + + + + ``` + +- 各节点的宏变量 + - 10.0.4.181 + ```xml + + 1 + 10.1.4.181 + + ``` + - 10.0.4.182 + ```xml + + 1 + 10.1.4.182 + + ``` + - 10.0.4.183 + ```xml + + 2 + 10.1.4.183 + + ``` + - 10.0.4.184 + ```xml + + 2 + 10.1.4.184 + + ``` + - 10.0.4.185 + ```xml + + 3 + 10.1.4.185 + + ``` + - 10.0.4.186 + ```xml + + 3 + 10.1.4.186 + + ``` + +- Zookeeper + ```xml + + + 10.0.4.101 + 2181 + + + 10.0.4.102 + 2181 + + + 10.0.4.103 + 2181 + + + ``` + +- system.zookeeper: zookeeper 代理表,可通过 sql 查看 zookeeper 信息 +- system.clusters: 集群信息表 + +# ReplicatedMergeTree 表引擎 +- 引入 zookeeper 实现分布式协同,zookeeper 本身不涉及表数据传输 +- 副本节点是多主架构,可在节点上执行读写操作 +- 数据块: 默认 1048576 行(max_insert_block_size) + - 基本基本写入单元 + - 原子性: 一个块内的数据,要么都写入成功,要么都失败 + - 唯一性: 记录 hash 信息,相同的数据块会被忽略 + + +## 创建 ReplicatedMergeTree 引擎表 +- 声明 + ```sql + CREATE TABLE table_name_local ON CLUSTER cluster_name_2 + ENGINE = ReplicatedMergeTree( + '/clickhouse/tables/{shard}/db_name/table_name_local', + '{replica}' + ) + ``` + +- table_name_local: 本地表名,推荐以 \_local 为后缀 +- cluster_name_2: 在该集群内创建数据库和数据表的分片和副本 +- /clickhouse/tables/ 是约定俗成的固定 zookeeper path 路径 +- {shard}: 分片编号,从各自节点的宏变量中获取 +- db_name: 数据库名 +- {replica}: 节点域名/IP,从各自节点的宏变量中获取 + +# Distributed 表引擎 +- 又叫分布式表,自身不存储数据,只代理数据分片 + +## 创建 Distributed 引擎表 +- 声明 + ```sql + CREATE TABLE table_name_all ON CLUSTER cluster_name_1 + ENGINE = Distributed(cluster_name_2, db, table, [,sharding_key]) + ``` + +- table_name_all: 分布式表名,通常以 \_all 为后缀 +- ON CLUSTER: 集群操作 +- cluster_name_1: 在该集群内创建分布式表 table_name_all +- cluster_name_2: 数据的分片和副本所在集群 +- db: 数据库名 +- table_name_local: 数据表名,即前面创建的 ReplicatedMergeTree 引擎表,通常以 \_local 为后缀 +- sharding_key: 分片键,可以是整型列字段或返回整型的表达式,决定数据分配到哪些节点中 + +# 分布式查询 +- 分布式表(Distributed)把查询转换为并行的各分片查询 +- 汇总各分片的查询结果 + +## GlOBAL 优化查询 +- 场景: 涉及到 JOIN 和 IN 时,可能会放大分布式查询 +- GLOBAL 查询过程: + - 提出 IN 子句,发起分布式查询 + - 汇总 IN 子句在各分片的查询结果,存入临时表(内存) + - 把这个临时表发送到其他分片节点,**考虑到该表既要内存存储,又要通过网络分发,所以数据量不宜过大** + - 在各分片节点执行完整的 SQL 语句,此时 IN 子句直接使用上一步的临时表 +- 使用示例 + ```sql + SELECT ... WHERE ... AND ... GLOBAL IN (...) + ``` + diff --git a/content/post/ch-search.md b/content/post/ch-search.md new file mode 100644 index 0000000..c9fb1c1 --- /dev/null +++ b/content/post/ch-search.md @@ -0,0 +1,192 @@ +--- +title: "ClickHouse 数据查询" +date: 2020-10-08T19:27:00+08:00 +lastmod: 2020-10-08T22:17:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# 查询注意 +- **避免使用 SELECT * 查询** + +# WITH +- **WITH 子句只能返回一行数据** +- 定义变量 + ```sql + WITH 10 AS var_name SELECT ... + ``` + +- 调用函数 + ```sql + WITH SUM(column_name) AS with_name SELECT ... + ``` + +- 定义子查询 + ```sql + WITH ( + SELECT ... + ) AS with_name + SELECt ... + ``` + +- WITH 子句可在子查询中嵌套使用 + +# FROM +- 支持表、表函数和子查询 +- 可用 FINAL 修饰以强制合并,会降低性能,应尽量避免使用 + +# SAMPLE +- 返回采样数据,减少查询负载,适用于近似查询 +- 只能用于 MergeTree 系列引擎表,且声明了 SAMPLE BY 抽样表达式 +- 虚拟字段 \_sample_factor 是采样系数 +- 不采样 + ```sql + SELECT ... SAMPLE 0 + -- 或 + SELECT ... SAMPLE 1 + ``` + +- SAMPLE factor,factor 是采样因子,取值 0~1 + ```sql + SELECT ... SAMPLE 0.1 + -- 或者 + SELECT ... SAMPLE 1/10 + ``` + +- SAMPLE rows,采样**近似**行数,必须大于 1 + ```sql + SELECT ... SAMPLE 10000 + ``` + +- SAMPLE factor OFFSET n,偏移 n\*100% 的数据量后才开始按 factor 因子采样,取值都在 0~1 + ```sql + SELECT ... SAMPLE 0.4 OFFSET 0.5 + ``` + +# ARRAY JOIN +- 允许在数据表内部,与数组或嵌套字段进行 JOIN 操作,操作时把数组或嵌套字段拆成多行 +- 支持 INNER 和 LEFT,默认 INNER + ```sql + SELECT ... FROM table_name ARRAY JOIN column_name AS alias_name + SELECT ... FROM table_name LEFT ARRAY JOIN column_name AS alias_name + ``` + +# JOIN +## 连接精度 +- ALL: 默认,左表的每行数据,在右表中有多行连接匹配,返回右表全部连接数据 +- ANY: 左表的每行数据,在右表中有多行连接匹配,返回右表第一行连接数据 +- ASOF: 增加模糊连接条件,对应字段必须是整数、浮点数和日期这类有序数据类型 + ```sql + SELECT ... FROM table_a ASOF INNER JOIN table_b USING(key_1, key_2) + -- key_1 字段是 join key,key_2 是模糊连接条件字段 + ``` + +## 连接类型 +- INNER: 内连接,返回交集部分 +- OUTER: 外链接 + - LEFT: 左表数据全部返回,右表匹配则返回,不匹配则填充相应字段的默认值 + - RIGHT: 与 LEFT 相反 + - FULL: 先 LEFT,右表剩下的数据再 RIGHT + +- CROSS: 交叉连接,返回笛卡儿积 + +## JOIN 查询优化 +- 左大右小,小表放右侧,右表会被加载到内存中 +- JOIN 查询无缓存,应用可考虑实现查询缓存 +- 大量维度属性补全时,建议使用字典表代替 JOIN 查询 +- USING 语法简写 + ```sql + SELECT ... FROM table_1 INNTER JOIN table_2 USING key_1 + ``` + +# PREWHERE +- 只能用于 MergeTree 系列表引擎 +- 与 WHERE 不同之处: + - 只读取 PREWHERE 指定的列字段,条件过滤 + - 根据过滤好的数据再读取 SELECT 指定的列字段 + +- clickhouse 会在合适条件下自动把 WHERE 替换成 PREWHERE + +# GROUP BY +- WITH ROLLUP,按聚合键从右向左上卷数据,基于聚合函数依次生成分组小计和总计 + ```sql + SELECT table, name, SUM(bytes_on_disk) FROM system.parts + GROUP BY table,name + WITH ROLLUP + ORDER BY table + ``` + +- WITH CUBE,基于聚合键之间的所有组合生成小计信息 + ```sql + SELECT ... + GROUP BY key1,key2,key3, ... + WITH CUBE + ... + ``` + +- WITH TOTALS,常规聚合完成后,增加一行对所有数据的汇总统计 + ```sql + SELECT ... + GROUP BY key1 + WITH TOTALS + ... + ``` + +# HAVING +- 必须与 GROUP BY 配合使用,把聚合结果二次过滤 + ```sql + SELECT ... GROUP BY ... HAVING ... + ``` + +# ORDER BY +- 默认 ASC(升序) +- NULLS LAST,默认,其他值 -> NaN -> NULL +- NULLS FIRST,NULL -> NaN -> 其他值 + +# LIMIT BY +- 返回指定分组的最多前 n 行数据 + ```sql + LIMIT n BY key1,key2 ... + ``` + +- 支持 OFFSET + ```sql + LIMIT n OFFSET m BY key1,key2 ... + -- 简写 + LIMIT m,n BY key1,key2 ... + ``` + +# LIMIT +- 返回指定的前 n 行数据 + ```sql + LIMIT n + LIMIT n OFFSET m + LIMIT m,n + ``` + +- 推荐搭配 ORDER BY,保证全局顺序 + +# SELECT +- 查询正则匹配的列字段 + ```sql + SELECT COLUMNS('^n'), COLUMNS('p') FROM system.databases + ``` + +# DISTINCT +- 去重 +- 先 DISTINCT 后 ORDER BY + +# UNION ALL +- 联合左右两边的子查询,一并返回结果,可多次声明使用联合多组查询 + ```sql + SELECT c1, c2 FROM t1 UNION ALL SELECT c3, c4 FROM t2 + ``` + +- 两边列字段数量必须一样,类型兼容,查询结果列名以左侧为准 + +# SQL 执行计划 +- 设置日志到 DEBUG 或 TRACE 级别,可查看 SQL 执行日志 +- SQL 需真正执行后才有日志,如果查询量大,推荐 LIMIT +- **不要用 SELECT * 查询** +- 尽可能利用索引,避免全表扫描 + diff --git a/content/post/ch-set.md b/content/post/ch-set.md new file mode 100644 index 0000000..87b6cab --- /dev/null +++ b/content/post/ch-set.md @@ -0,0 +1,27 @@ +--- +title: "ClickHouse 表引擎之 Set" +date: 2020-10-08T17:04:00+08:00 +lastmod: 2020-10-08T17:04:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# Set 表引擎简介 +- 数据先写内存,再同步到磁盘,服务重启后全量加载到内存 +- 支持 INSERT,写入时,重复数据被忽略 +- 不能直接 SELECT,只能作为 IN 查询的右侧条件 + +# 创建 Set 引擎表 +- 声明 + ```sql + ENGINE = Set() + ``` + +# 使用 +- 创建 Set 引擎表 +- INSERT 写入数据 +- 查询 + ```sql + SELECT arrayJoin([1,2,3]) AS a WHERE a IN set_table; + ``` + diff --git a/content/post/ch-url.md b/content/post/ch-url.md new file mode 100644 index 0000000..e39b659 --- /dev/null +++ b/content/post/ch-url.md @@ -0,0 +1,22 @@ +--- +title: "ClickHouse 表引擎之 URL" +date: 2020-10-08T19:16:00+08:00 +lastmod: 2020-10-08T19:16:00+08:00 +tags: [] +categories: ["clickhouse"] +--- + +# URL 表引擎简介 +- http 客户端,支持 http 和 https 协议 +- SELECT 转换成 GET 请求 +- INSERT 转换成 POST 请求 + +# 创建 URL 表引擎 +- 声明 + ```sql + ENGINE = URL('url', format) + ``` + +- url: 请求远端服务的 url +- format: TSV、CSV、JSONEachRow + diff --git a/content/post/cloudera-manager.md b/content/post/cloudera-manager.md new file mode 100644 index 0000000..7946bc0 --- /dev/null +++ b/content/post/cloudera-manager.md @@ -0,0 +1,260 @@ +--- +title: "CentOS7 安装 Cloudera Manager" +date: 2020-08-16T13:48:37+08:00 +lastmod: 2020-08-16T18:27:00+08:00 +keywords: [] +tags: [cloudera cdh] +categories: ["hadoop"] +--- + +# 环境 + +角色 | IP | 主机名 | 服务 +---- | ---- | ---- | ---- +Utility | 192.168.1.100 | cm0.colben.cn | ClouderaManager
ClouderaManagerManagementService
HiveMetastore +Gateway | 192.168.1.101 | gw0.colben.cn | GatewayConfiguration
HiveServer2
Zookeeper +Master | 192.168.1.102 | m0.colben.cn | NameNode
JournalNode
FailoverController
YarnResourceManager
Zookeeper +Master | 192.168.1.103 | m1.colben.cn | NameNode
JournalNode
FailoverController
YarnResourceManager
Zookeeper +Worker | 192.168.1.104 | w0.colben.cn | DataNode
NodeManager +Worker | 192.168.1.105 | w0.colben.cn | DataNode
NodeManager +Worker | 192.168.1.106 | w0.colben.cn | DataNode
NodeManager + +# 配置 ssh 免密登陆 + +- 在 cm0 上配置 ssh 可免密登陆全部服务器 + ``` + ssh-keygen + seq -f'192.168.1.%g' 100 106 | xargs -L1 ssh-copy-id + ``` + +- 这里的**私钥会在后面通过界面增加主机时用到** + +# 关闭防火墙和 selinux + +- 在全部服务器上关闭防火墙 + ``` + systemctl stop firewalld + systemctl disable firewalld + ``` + +- 在全部服务器上关闭 selinux + ``` + setenforce 0 + sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + ``` + +# 配置网络名称 + +- 在全部服务器上修改 /etc/hosts,增加如下解析记录 + ``` + 192.168.1.100 cm0.colben.cn cm0 + 192.168.1.101 gw0.colben.cn gw0 + 192.168.1.102 m0.colben.cn m0 + 192.168.1.103 m1.colben.cn m1 + 192.168.1.104 w0.colben.cn w0 + 192.168.1.105 w1.colben.cn w1 + 192.168.1.106 w2.colben.cn w2 + ``` + +- 在全部服务器上修改 /etc/sysconfig/network,增加各自主机名设置 + ``` + HOSTNAME=XXXX.colben.cn + ``` + +- 在全部服务器上设置各自主机名 + ``` + hostnamectl set-hostname XXXX.colben.cn + ``` + +# 配置时间同步 + +- 在 cm0 上配置修改 /etc/chrony.conf + ``` + server ntp.aliyun.com iburst + allow 192.168.1.0/24 + ``` + +- 在其他服务器上修改 /etc/chrony.conf + ``` + server cm0.colben.cn iburst + ``` + +- 在全部服务器上重启 chronyd 服务 + ``` + systemctl restart chronyd + ``` + +# 配置 cloudera manager 内网安装源 + +- 在 cm0 上安装并启动 httpd 服务 + ``` + yum install httpd + systemctl enable httpd + systemctl start httpd + ``` + +- 在 cm0 上下载 cloudera manager yum 仓库 + ``` + mkdir -p /var/www/html/cloudera-repos/cm6/6.3.1 + wget https://archive.cloudera.com/cm6/6.3.1/repo-as-tarball/cm6.3.1-redhat7.tar.gz + tar zxf cm6.3.1-redhat7.tar.gz -C /var/www/html/cloudera-repos/cm6/6.3.1 --strip-components=1 + chmod -R ugo+rX /var/www/html/cloudera-repos/cm6 + ``` + +- 在 cm0 上下载 cdh yum 仓库(**体积较小,需手动升级**) + ``` + mkdir -p /var/www/html/cloudera-repos + wget --recursive --no-parent --no-host-directories \ + https://archive.cloudera.com/cdh6/6.3.2/redhat7/ -P /var/www/html/cloudera-repos + wget --recursive --no-parent --no-host-directories \ + https://archive.cloudera.com/gplextras6/6.3.2/redhat7/ -P /var/www/html/cloudera-repos + chmod -R ugo+rX /var/www/html/cloudera-repos/cdh6 + chmod -R ugo+rX /var/www/html/cloudera-repos/gplextras6 + ``` + +- 在 cm0 上下载 cdh parcel 仓库(**体积较大,可通过界面操作自动升级**) + ``` + mkdir -p /var/www/html/cloudera-repos + wget --recursive --no-parent --no-host-directories \ + https://archive.cloudera.com/cdh6/6.3.2/parcels/ -P /var/www/html/cloudera-repos + wget --recursive --no-parent --no-host-directories \ + https://archive.cloudera.com/gplextras6/6.3.2/parcels/ -P /var/www/html/cloudera-repos + chmod -R ugo+rX /var/www/html/cloudera-repos/cdh6 + chmod -R ugo+rX /var/www/html/cloudera-repos/gplextras6 + ``` + +- 在 cm0 上创建 cloudera.repo,并重建 yum 缓存 + ``` + cat > /etc/yum.repos.d/cloudera.repo <<-EOF + [cloudera-manager] + name=cloudera-manager + baseurl=http://cm0.colben.cn/cloudera-repos/cm6/ + enabled=1 + gpgcheck=0 + EOF + yum clean all + yum makecache fast + ``` + +- 通过界面增加其他主机时,cloudera manager 会自动部署该 repo 文件 + +# 安装 jdk + +- 在 cm0 上安装 jdk + ``` + yum install oracle-j2sdk1.8 + ``` + +- 通过界面增加其他主机时,可自动安装 oracle-j2sdk1.8 + +# 安装 cloudera manager + +- 在 cm0 上安装 cloudera-manager + ``` + yum install cloudera-manager-daemons cloudera-manager-agent cloudera-manager-server + ``` + +# 配置 cloudera manager 数据库 + +- 在 cm0 上安装 mysql,[参考这里](/post/mysql5.7-install/) +- 官方推荐 my.cnf + ``` + [mysqld] + transaction-isolation = READ-COMMITTED + symbolic-links = 0 + sql_mode=STRICT_ALL_TABLES + + key_buffer_size = 32M + max_allowed_packet = 32M + thread_stack = 256K + thread_cache_size = 64 + query_cache_limit = 8M + query_cache_size = 64M + query_cache_type = 1 + max_connections = 600 + + log_bin=/var/lib/mysql/mysql_binary_log + server_id=1 + binlog_format = mixed + + read_buffer_size = 2M + read_rnd_buffer_size = 16M + sort_buffer_size = 8M + join_buffer_size = 8M + + innodb_file_per_table = 1 + innodb_flush_log_at_trx_commit = 2 + innodb_log_buffer_size = 64M + innodb_buffer_pool_size = 4G + innodb_thread_concurrency = 8 + innodb_flush_method = O_DIRECT + innodb_log_file_size = 512M + ``` + +- 在 cm0 上启动 mysql + ``` + systemctl enable mysqld + systemctl start mysqld + ``` + +- 在 cm0 上的 mysql 中创建数据库和用户 + ``` + CREATE DATABASE scm DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE amon DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE rman DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE hue DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE metastore DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE sentry DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE nav DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE navms DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + CREATE DATABASE oozie DEFAULT CHARSET utf8 DEFAULT COLLATE utf8_general_ci; + GRANT ALL ON scm.* to scm@'%' identified by 'Pass-1234'; + GRANT ALL ON amon.* to amon@'%' identified by 'Pass-1234'; + GRANT ALL ON rman.* to rman@'%' identified by 'Pass-1234'; + GRANT ALL ON hue.* to hue@'%' identified by 'Pass-1234'; + GRANT ALL ON metastore.* to hive@'%' identified by 'Pass-1234'; + GRANT ALL ON sentry.* to sentry@'%' identified by 'Pass-1234'; + GRANT ALL ON nav.* to nav@'%' identified by 'Pass-1234'; + GRANT ALL ON navms.* to navms@'%' identified by 'Pass-1234'; + GRANT ALL ON oozie.* to oozie@'%' identified by 'Pass-1234'; + ``` + +- 在 cm0 上初始化数据库 + ``` + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql scm scm + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql amon amon + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql rman rman + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql hue hue + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql metastore hive + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql sentry sentry + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql nav nav + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql navms navms + /opt/cloudera/cm/schema/scm_prepare_database.sh mysql oozie oozie + ``` + +- 在全部服务器上安装 mysql connector + ``` + wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.46.tar.gz + tar zxvf mysql-connector-java-5.1.46.tar.gz + mkdir -p /usr/share/java/ + cp mysql-connector-java-5.1.46/mysql-connector-java-5.1.46-bin.jar \ + /usr/share/java/mysql-connector-java.jar + ``` + +# 启动 cloudera manager +- 在 cm0 上启动 cloudera manager + ``` + systemctl start cloudera-scm-server + ``` + +- 日志: /var/log/cloudera-scm-server/cloudera-scm-server.log +- 日志出现如下信息,表示 cloudera-scm-server 启动完成 + ``` + INFO WebServerImpl:com.cloudera.server.cmf.WebServerImpl: Started Jetty server. + ``` + +- 浏览器访问 + - 地址: http://cm0.colben.cn:7180/ + - 用户: admin + - 密码: admin + diff --git a/content/post/configmap.md b/content/post/configmap.md new file mode 100644 index 0000000..3e497cb --- /dev/null +++ b/content/post/configmap.md @@ -0,0 +1,280 @@ +--- +title: "ConfigMap 笔记" +date: 2019-12-22T22:04:37+08:00 +lastmod: 2019-12-22T22:04:37+08:00 +tags: ["kubernetes", "configmap"] +categories: ["container"] +--- + +# 概述 +- ConfigMap 通常用于设置环境变量、设置命令行参数、创建配置文件 +- Pod 使用 ConfigMap 前,ConfigMap 必须存在,否则 pod 不能启动 +- ConfigMap 只能被在同一一个命名空间中的Pod所引用 + +# 创建 ConfigMap +- 命令如下 + ```bash + kubectl create configmap + # 或者 + kubectl apply -f + ``` +- map-name: ConfigMap 名称 +- data-source: 目录、文件或具体值 + +## 通过目录创建 ConfigMaps +- 命令如下 + ```bash + kubectl create configmap game-config \ + --from-file=https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl + ``` +- docs/tasks/configure-pod-container/configmap/kubectl/目录下的文件包括 + ```bash + ls docs/tasks/configure-pod-container/configmap/kubectl/ + ``` + - 输出如下 + ``` + game.properties + ui.properties + ``` +- 查看 game-config 信息 + ```bash + kubectl describe configmaps game-config + # 或者 + kubectl get configmaps game-config -o yaml + ``` + +## 通过文件创建 ConfigMaps +- 通过单个文件创建 + ```bash + kubectl create configmap game-config-2 \ + --from-file=https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/game.properties + ``` +- 通过多个文件创建 + ```bash + kubectl create configmap game-config-3 \ + --from-file=https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/game.properties \ + --from-file=https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/ui.properties + ``` +- 通过文件创建ConfigMap时可以定义文件的键 + ```bash + kubectl create configmap game-config-4 \ + --from-file=game-special-key=https://k8s.io/docs/tasks/configure-pod-container/configmap/kubectl/game.properties + # key 是 "game-special-key" + # value 是 game.properties 文件的内容 + ``` + +## 通过具体值创建 ConfigMaps +- 使用 --from-literal 参数定义具体值 + ```bash + kubectl create configmap special-config \ + --from-literal=special.how=very \ + --from-literal=special.type=charm + ``` + +# 使用 ConfigMap +## 定义 pod 环境变量 +### Pod 环境变量的值来自于单一 ConfigMap +- 在ConfigMap中定义一个环境变量作为键值对 + ```bash + kubectl create configmap special-config --from-literal=special.how=very + ``` +- 指派ConfigMap中定义的special.how的值给Pod中SPECIAL_LEVEL_KEY环境变量 + ```yaml + apiVersion:v1 + kind:Pod + metadata: + name:dapi-test-pod + spec: + containers: + - name:test-container + image:k8s.gcr.io/busybox + command:["/bin/sh","-c","env"] + env: + # Define the environment variable + - name:SPECIAL_LEVEL_KEY + valueFrom: + configMapKeyRef: + # The ConfigMap containing the value you want to assign to SPECIAL_LEVEL_KEY + name:special-config + # Specify the key associated with the value + key:special.how + restartPolicy:Never + ``` +- 保存Pod规格的变化,Pod将输出SPECIAL_LEVEL_KEY=very + +### Pod 环境变量的值来自于多个 ConfigMap +- 创建两个 ConfigMap + ```yaml + --- + apiVersion:v1 + kind:ConfigMap + metadata: + name:special-config + namespace:default + data: + special.how:very + + --- + apiVersion:v1 + kind:ConfigMap + metadata: + name:env-config + namespace:default + data: + log_level:INFO + ``` +- 在Pod规格中定义环境变量 + ```yaml + apiVersion:v1 + kind:Pod + metadata: + name:dapi-test-pod + spec: + containers: + - name:test-container + image:k8s.gcr.io/busybox + command:["/bin/sh","-c","env"] + env: + - name:SPECIAL_LEVEL_KEY + valueFrom: + configMapKeyRef: + name:special-config + key:special.how + - name:LOG_LEVEL + valueFrom: + configMapKeyRef: + name:env-config + key:log_level + restartPolicy:Neverv + ``` +- 保存变更后的Pod,Pod将会输出SPECIAL_LEVEL_KEY=very和LOG_LEVEL=info + +## 在一个ConfigMap中配置的键值对都作为一个Pod的环境变量 +- **Kubernetes v1.6+可用** +- 创建包含多个键-值对的ConfigMap + ```yaml + apiVersion:v1 + kind:ConfigMap + metadata: + name:special-config + namespace:default + data: + SPECIAL_LEVEL:very + SPECIAL_TYPE:charm + ``` +- 使用envFrom定义所有的ConfigMap数据作为Pod的环境变量。来自于Config的键成为Pod中环境变量的名 + ```yaml + apiVersion:v1 + kind:Pod + metadata: + name:dapi-test-pod + spec: + containers: + - name:test-container + image:k8s.gcr.io/busybox + command:["/bin/sh","-c","env"] + envFrom: + - configMapRef: + name:special-config + restartPolicy:Never + ``` +- Pod的输出包括: SPECIAL_LEVEL=very 和 SPECIAL_TYPE=charm + +## 在Pod命令行中使用ConfigMap定义的环境变量 +- 在Pod规范的command 中使用$(VAR_NAME) ,获取ConfigMap定义的环境变量 + ```yaml + apiVersion:v1 + kind:Pod + metadata: + name:dapi-test-pod + spec: + containers: + - name:test-container + image:k8s.gcr.io/busybox + command:["/bin/sh","-c","echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)"] + env: + - name:SPECIAL_LEVEL_KEY + valueFrom: + configMapKeyRef: + name:special-config + key:SPECIAL_LEVEL + - name:SPECIAL_TYPE_KEY + valueFrom: + configMapKeyRef: + name:special-config + key:SPECIAL_TYPE + restartPolicy:Never + ``` +- test-container容器的输出: very charm + +# 添加ConfigMap数据至存储卷 +- 当通过–from-file创建的ConfigMap时,文件将作为一个键保存在ConfigMap中,而此文件的内容将作为值 + ```bash + apiVersion:v1 + kind:ConfigMap + metadata: + name:special-config + namespace:default + data: + special.level:very + special.type:charm + ``` + +## 将ConfigMap中的数据传播到指定目录 +- 在Pod的存储卷区域添加ConfigMap的名称 +- 这将添加ConfigMap数据到volumeMounts.mountPath指定的目录下(此例为/etc/config) +- command区域将引用保存在ConfigMap中的special.level条目 + ```bash + apiVersion:v1 + kind:Pod + metadata: + name:dapi-test-pod + spec: + containers: + - name:test-container + image:k8s.gcr.io/busybox + command:["/bin/sh","-c","ls /etc/config/"] + volumeMounts: + - name:config-volume + mountPath:/etc/config + volumes: + - name:config-volume + configMap: + # Provide the name of the ConfigMap containing the files you want + # to add to the container + name:special-config + restartPolicy:Never + ``` +- Pod运行时,command (“ls /etc/config/”)将输出: special.level special.type +- **如果在/etc/config/目录下存在文件,将不会删除** + +## 添加ConfigMap数据至存储卷指定的目录 +- 为ConfigMap条目,使用path指定文件路径 +- 此例中,special.level将在config-volume存储卷中被挂接至/etc/config/keys + ```yaml + apiVersion:v1 + kind:Pod + metadata: + name:dapi-test-pod + spec: + containers: + - name:test-container + image:k8s.gcr.io/busybox + command:["/bin/sh","-c","cat /etc/config/keys"] + volumeMounts: + - name:config-volume + mountPath:/etc/config + volumes: + - name:config-volume + configMap: + name:special-config + items: + - key:special.level + path:keys + restartPolicy:Never + ``` +- Pod运行时,(“cat /etc/config/keys”) 将输出: very + +# 参考 +- [Kubernetes-配置字典ConfigMap](https://blog.csdn.net/bbwangj/article/details/81776648) + diff --git a/content/post/docker.md b/content/post/docker.md new file mode 100644 index 0000000..3f6256d --- /dev/null +++ b/content/post/docker.md @@ -0,0 +1,328 @@ +--- +title: "Docker 笔记" +date: 2019-10-30T13:29:00+08:00 +lastmod: 2021-11-05T11:11:00+08:00 +tags: ["docker"] +categories: ["container"] +--- + +# 安装 docker +## CentOS7 安装 docker-ce +- 配置 yum 源 + ```bash + curl -Lo /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo + #替换成清华源 + sed -i 's#download.docker.com#mirrors.tuna.tsinghua.edu.cn/docker-ce#' /etc/yum.repos.d/docker-ce.repo + yum clean all + yum makecache + ``` + +- 安装 docker + ```bash + yum install docker-ce + ``` + +- 修改 docker 配置文件,建议选择一个与本地网络不冲突的网段 + ```bash + mkdir -p /etc/docker + cat > /etc/docker/daemon.json <<-EOF + { + "insecure-registries": ["harbor.colben.cn"], + "default-address-pools" : [{"base":"10.110.0.0/16", "size": 24}], + "log-driver": "json-file", + "log-opts": {"max-size":"100m", "max-file":"4"} + } + EOF + ``` + +- 启动 docker + ```bash + systemctl start docker + ``` + +## 常用的 linux with systemd 安装 docker +- 下载 docker 二进制文件 + ```bsah + curl -LO https://download.docker.com/linux/static/stable/x86_64/docker-20.10.10.tgz + ``` + +- 安装 + ```bash + tar zxf docker-20.10.10.tgz + mv docker/* /usr/bin/ + rm -rf docker/ docker-20.10.10.tgz + groupadd -g 10110 docker + ``` + +- 创建 /usr/lib/systemd/system/containerd.service,内容如下 + ``` + [Unit] + Description=containerd container runtime + Documentation=https://containerd.io + After=network.target local-fs.target + + [Service] + ExecStartPre=-/sbin/modprobe overlay + ExecStart=/usr/bin/containerd + + Type=notify + Delegate=yes + KillMode=process + Restart=always + RestartSec=5 + # Having non-zero Limit*s causes performance problems due to accounting overhead + # in the kernel. We recommend using cgroups to do container-local accounting. + LimitNPROC=infinity + LimitCORE=infinity + LimitNOFILE=1048576 + # Comment TasksMax if your systemd version does not supports it. + # Only systemd 226 and above support this version. + TasksMax=infinity + OOMScoreAdjust=-999 + + [Install] + WantedBy=multi-user.target + ``` + +- 创建 /usr/lib/systemd/system/container-getty@.service,内容如下 + ``` + [Unit] + Description=Container Getty on /dev/pts/%I + Documentation=man:agetty(8) man:machinectl(1) + After=systemd-user-sessions.service plymouth-quit-wait.service + After=rc-local.service getty-pre.target + Before=getty.target + IgnoreOnIsolate=yes + ConditionPathExists=/dev/pts/%I + + [Service] + ExecStart=-/sbin/agetty --noclear --keep-baud pts/%I 115200,38400,9600 $TERM + Type=idle + Restart=always + RestartSec=0 + UtmpIdentifier=pts/%I + TTYPath=/dev/pts/%I + TTYReset=yes + TTYVHangup=yes + KillMode=process + IgnoreSIGPIPE=no + SendSIGHUP=yes + ``` + +- 创建 /usr/lib/systemd/system/docker.socket,内容如下 + ``` + [Unit] + Description=Docker Socket for the API + + [Socket] + ListenStream=/var/run/docker.sock + SocketMode=0660 + SocketUser=root + SocketGroup=docker + + [Install] + WantedBy=sockets.target + ``` + +- 创建 /usr/lib/systemd/system/docker.service,内容如下 + ``` + [Unit] + Description=Docker Application Container Engine + Documentation=https://docs.docker.com + After=network-online.target firewalld.service containerd.service + Wants=network-online.target + Requires=docker.socket containerd.service + + [Service] + Type=notify + # the default is not to use systemd for cgroups because the delegate issues still + # exists and systemd currently does not support the cgroup feature set required + # for containers run by docker + ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock + ExecReload=/bin/kill -s HUP $MAINPID + TimeoutSec=0 + RestartSec=2 + Restart=always + + # Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229. + # Both the old, and new location are accepted by systemd 229 and up, so using the old location + # to make them work for either version of systemd. + StartLimitBurst=3 + + # Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230. + # Both the old, and new name are accepted by systemd 230 and up, so using the old name to make + # this option work for either version of systemd. + StartLimitInterval=60s + + # Having non-zero Limit*s causes performance problems due to accounting overhead + # in the kernel. We recommend using cgroups to do container-local accounting. + LimitNOFILE=infinity + LimitNPROC=infinity + LimitCORE=infinity + + # Comment TasksMax if your systemd version does not support it. + # Only systemd 226 and above support this option. + TasksMax=infinity + + # set delegate yes so that systemd does not reset the cgroups of docker containers + Delegate=yes + + # kill only the docker process, not all processes in the cgroup + KillMode=process + OOMScoreAdjust=-500 + + [Install] + WantedBy=multi-user.target + ``` + +- 修改 docker 配置文件,建议选择一个与本地网络不冲突的网段 + ```bash + mkdir -p /etc/docker + cat > /etc/docker/daemon.json <<-EOF + { + "insecure-registries": ["harbor.colben.cn"], + "default-address-pools" : [{"base":"10.110.0.0/16", "size": 24}], + "log-driver": "json-file", + "log-opts": {"max-size":"100m", "max-file":"4"} + } + EOF + ``` + +- 启动 docker + ```bash + systemctl start docker + ``` + +# 安装 docker-compose +- 下载 docker-compose + ```bash + curl -LO https://github.com/docker/compose/releases/download/v2.1.0/docker-compose-linux-x86_64 + ``` + +- 安装 + ```bash + mv docker-compose-linux-x86_64 /usr/bin/docker-compose + chmod 0755 /usr/bin/docker-compose + ``` + +# 安装 gojq +- 该工具支持平台较多,无依赖,与 jq 命令操作完全一致,可用于替换 jq 命令 +- 下载 + ```bash + curl -LO https://github.com/itchyny/gojq/releases/download/v0.12.5/gojq_v0.12.5_linux_amd64.tar.gz + ``` + +- 安装 + ```bash + tar zxf gojq_v0.12.5_linux_amd64.tar.gz + mv gojq_v0.12.5_linux_amd64/gojq /usr/bin/ + chmod 0755 /usr/bin/gojq + rm -rf gojq_* + ``` + +# 基本命令 +- 镜像 + ```bash + docker pull [选项] [Docker Registry地址]<仓库名>:<标签> #获取镜像 + docker images [选项] #列出镜像 + docker images -f dangling=true #列出虚悬镜像 + docker images -q -f dangling=true | xargs docker rmi #删除全部虚悬镜像 + docker commit -m "提交的说明信息" -a "更新的用户信息" <容器ID> [地址]<仓库名>:<标签> #根据现有容器创建镜像 + docker build -t="[地址]<仓库名>:<标签>" #构建镜像 + docker tag <镜像ID> [地址]<仓库名>:<标签> #修改镜像的标签 + docker push [地址]<仓库名>:<标签> #上传镜像 + docker save -o <本地文件名.tar> [地址]<仓库名>:<标签> #保存镜像到本地文件 + docker load < <本地文件名.tar> #把本地文件加载到镜像库 + docker rmi <镜像ID> #删除镜像 + ``` + +- 容器 + ```bash + docker run [选项] [地址]<仓库名>:<标签> [命令] #从镜像启动一个容器 + docker stop <容器名|容器ID> #终止运行中的容器 + docker start <容器名|容器ID> #启动已停止的容器 + docker retart <容器名|容器ID> #重新启动运行中的容器 + docker ps [-a] #查看(全部)容器信息 + docker logs <容器名|容器ID> #获取容器输出信息 + docker attach <容器名|容器ID> #进入运行中的容器 + docker export <容器ID> > <本地文件名.tar> #导出容器快照到本地文件 + cat <本地文件名.tar> | docker import - [地址]<仓库名>:<标签> #从本地文件导入容器快照 + docker rm -r <容器名|容器ID> #删除(运行中的)容器 + ``` + +- 数据卷 + ```bash + docker run [选项] -v /webapp [地址]<仓库名>:<标签> [命令] #启动容器时创建一个数据卷挂载到容器的 /webapp 下 + docker rm -v <容器名|容器ID> #删除容器时同时删除数据卷 + docker run [选项] -v <主机绝对目录>:<容器绝对目录>[:ro] [地址]<仓库名>:<标签> [命令] #启动容器时挂载本地目录到容器指定目录下,默认可读写 + docker run [选项] -v <主机文件>:<容器文件>[:ro] [地址]<仓库名>:<标签> [命令] #启动容器时挂载本地文件到容器指定文件,默认可读写 + docker inspect <容器名|容器ID> #查看容器信息 + docker run [选项] --volumes-from <挂载数据卷的容器名> [地址]<仓库名>:<标签> [命令] #在其他容器中挂载指定容器(不必运行)的数据卷 + ``` + +- 备份数据卷 + ```bash + docker run [选项] --volumes-from <挂载数据卷的容器名> -v $(pwd):/backup [地址]<仓库名>:<标签> tar cvf /backup/backup.tar <数据卷挂载目录> #备份数据卷到主机当前目录的 backup.tar 文件 + ``` + +- 恢复数据卷 + ```bash + docker run [选项] -v <数据卷挂载目录> --name <自定义一个容器名> [地址]<仓库名>:<标签> [命令] #创建一个带空数据卷的容器 + docker run [选项] --volumes-from <第一步挂载空数据卷的容器名> -v $(pwd):/backup busybox tar xvf /backup/backup.tar #挂载空数据卷和本机备份目录,解压备份文件 + docker run [选项] --volumes-from <第一步挂载空数据卷的容器名> busybox 'ls <数据卷挂载目录>' #查看恢复的数据 + ``` + +- 网络 + ```bash + docker run [选项] -P [地址]<仓库名>:<标签> [命令] #随机映射主机 49000-49900 中的端口到容器开放的端口 + docker run [选项] -p [/udp] [地址]<仓库名>:<标签> [命令] #映射本机指定tcp(udp)端口到容器指定tcp(udp)端口 + docker port <容器名> <容器开放的端口> #查看主机被绑定的地址 + docker run [选项] --link <待链接容器名>:<链接别名> [地址]<仓库名>:<标签> [命令] #创建一个链接到其他容器的新容器 + ``` + +# Dockerfile +- 井号 "#" 后是注释 +- FROM 基础镜像 +- MAINTAINER 维护者信息 +- RUN shell命令 +- ADD 复制本地文件到容器,自动解压 tar 文件,可以增加网络文件 +- COPY 复制本地文件到容器,不自动解压,也不可以增加网络文件 +- LABEL 为镜像添加元数据 +- ENV 设置镜像内环境变量 +- USER 容器运行时的用户和用户组 +- ONBUILD 镜像触发器 +- EXPOSE 向外部开放端口 +- CMD 容器启动后运行的程序 + +# docker 镜像仓库 +## 官方 registry +- 直接 docker 启动 + ```bash + docker run -d \ + --name registry \ + --net host \ + -e "REGISTRY_HTTP_ADDR='0.0.0.0:80'" \ + -v /some/path:/var/lib/registry \ + registry + ``` + +## VMWare Harbor +- 安装[参考这里](https://goharbor.io/docs/2.0.0/install-config/) +- docker registry 采用 http 协议,客户端提示 “server gave HTTP response to HTTPS client” + ```bash + #在客户端 /etc/docker/daemon.json 中增加 insecure-registries 配置 + # "insecure-registries":["10.0.2.22:5080"] + #重启客户端的 docker 服务 + systemctl restart docker + ``` + +- docker registry 采用 https 协议,客户端提示 "authority unknown ..." + ```bash + #在客户端 /etc/docker/ 下创建 registry server 的 domain/ip 目录 + mkdir -p /etc/docker/certs.d/10.0.2.22:5080/ + #复制 registry server 的 ca.crt (该文件由 openssl 创建密钥时生成) + scp root@10.0.2.22:/opt/harbor/keys/ca.crt /etc/docker/certs.d/10.0.2.22:5080/ + #重启客户端的 docker 服务 + systemctl restart docker + ``` + diff --git a/content/post/drone.md b/content/post/drone.md new file mode 100644 index 0000000..36b7387 --- /dev/null +++ b/content/post/drone.md @@ -0,0 +1,215 @@ +--- +title: "Drone 笔记" +date: 2021-02-08T17:03:13+08:00 +lastmod: 2021-02-08T21:08:00+08:00 +keywords: [] +tags: ["drone", "cicd"] +categories: ["dev/ops"] +--- + +# 环境 + +- 操作系统 Linux x86_64 +- 这里的 drone 是基于 gitea 配置的 +- 安装 gitea,参考[官方文档](https://docs.gitea.io/zh-cn/install-from-binary/) +- 安装 docker-ce,参考[我的 docker 笔记](https://www.colben.cn/post/docker/#%E5%AE%89%E8%A3%85) + +# 创建 OAuth2 应用 + +- 登陆 gitea +- 点击 "个人头像" - "设置" - "应用" +- 在 "管理 OAuth2 应用程序" 中,输入 + - 输入应用名称: "drone" + - 重定向URI: "http://:/login" + +- 点击 "创建应用",在弹出的新页面中 + - 记录 "客户端 ID" + - 记录 "客户端密钥" + - 点击 "保存" + +# 安装 drone + +- 下载 docker 镜像 + ```bash + docker pull drone/drone + ``` + +- 启动容器 + ```bash + docker run -d \ + --name drone \ + -e DRONE_GITEA_SERVER=http://: \ + -e DRONE_GITEA_CLIENT_ID=<客户端 ID> \ + -e DRONE_GITEA_CLIENT_SECRET=<客户端密钥> \ + -e DRONE_RPC_SECRET=1111aaaa2222bbbb3333cccc4444dddd \ + -e DRONE_SERVER_HOST=: \ + -e DRONE_SERVER_PROTO=http \ + -e DRONE_GIT_ALWAYS_AUTH=true \ + -p :80 \ + -v <外挂的 drone 数据目录>:/data \ + drone/drone + ``` + +- 参考链接[https://docs.drone.io/server/provider/gitea/](https://docs.drone.io/server/provider/gitea/) + +# 安装 drone runner +## 安装 docker runner + +- 下载 drone-runner-docker 镜像 + ```bash + docker pull drone/drone-runner-docker + ``` + +- 启动容器 + ```bash + docker run -d \ + --name drone_runner_docker \ + -e DRONE_RPC_PROTO=http \ + -e DRONE_RPC_HOST=: \ + -e DRONE_RPC_SECRET=1111aaaa2222bbbb3333cccc4444dddd \ + -e DRONE_RUNNER_CAPACITY=10 \ + -e DRONE_RUNNER_NAME=<该 runner 的名字> \ + -e DRONE_RUNNER_LABELS=: \ + -p :3000 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + drone/drone-runner-docker + ``` + +- 参考链接[https://docs.drone.io/runner/docker/installation/linux/](https://docs.drone.io/runner/docker/installation/linux/) + +## 安装 exec runner + +- 下载 + ```bash + curl -L https://github.com/drone-runners/drone-runner-exec/releases/latest/download/drone_runner_exec_linux_amd64.tar.gz \ + | tar zxf -C /usr/local/bin + chmod 0755 /usr/local/bin/drone-runner-exec + ``` + +- 创建配置文件 + ```bash + cat > /etc/drone-runner-exec/config <<-EOF + DRONE_RPC_PROTO=http + DRONE_RPC_HOST=: + DRONE_RPC_SECRET=1111aaaa2222bbbb3333cccc4444dddd + DRONE_HTTP_BIND=: + DRONE_LOG_FILE=/var/log/drone-runner-exec/log.txt + DRONE_RUNNER_LABELS=: + EOF + ``` + +- 创建 systemd service 文件 + ```bash + cat > /etc/systemd/system/drone-runner-exec.service <<-EOF + [Unit] + Description=Drone Exec Runner + ConditionFileIsExecutable=/usr/local/bin/drone-runner-exec + + [Service] + ExecStart=/usr/local/bin/drone-runner-exec "service" "run" "--config" "/etc/drone-runner-exec/config" + StartLimitInterval=5 + StartLimitBurst=10 + Restart=on-failure + RestartSec=120 + + [Install] + WantedBy=multi-user.target + EOF + ``` + +- 参考链接[https://docs.drone.io/runner/exec/installation/linux/](https://docs.drone.io/runner/exec/installation/linux/) + +# 使用 drone + +- 登陆 drone: http://:,此时会跳转到 gitea 登陆界面 +- 登陆成功后,浏览器返回 drone 首页,这里会显示我们创建/参与的 git 项目 +- 选择一个项目,点击对应的 "ACTIVATE",drone 会打开该项目的 "SETTINGS" 页面,这里一般无需设置,默认即可 +- 在该项目的 "ACTIVITY FEED" 页面会显示每次项目提交后触发的 CI/CD 流程 +- 编辑该项目代码,在项目根目录下创建文件 .drone.yml,内容如下 + - docker pipeline 示例,详细参考[https://docs.drone.io/pipeline/docker/overview/](https://docs.drone.io/pipeline/docker/overview/) + ```yaml + --- + kind: pipeline + type: docker + name: default + + steps: + - name: greeting + image: golang:1.12 + commands: + - go build + - go test + + node: + : + ``` + + - exec pipeline 示例,详情参考[https://docs.drone.io/pipeline/exec/overview/](https://docs.drone.io/pipeline/exec/overview/) + ```yaml + --- + kind: pipeline + type: exec + name: default + + platform: + os: linux + arch: amd64 + + steps: + - name: greeting + commands: + - echo hello world + + node: + : + ``` + + - 其他 pipeline 参考[https://docs.drone.io/pipeline/overview/](https://docs.drone.io/pipeline/overview/) + +# 适配 sonarqube + +- 下载镜像 + ```bash + docker pull sonarqube + ``` + +- 启动 sonarqube 容器 + ```bash + docker run -d \ + --name sonarqube \ + -p :9000 \ + -v sonarqube_data:/opt/sonarqube/data \ + -v sonarqube_extension:/opt/sonarqube/extensions \ + -v sonarqube_log:/opt/sonarqube/logs \ + sonarqube + ``` + +- 登陆 sonarquebe: http://:,创建用户,获取 token +- 在 gitea 对应项目根目录下创建 .drone.yml,内容如下 + ```yaml + kind: pipeline + type: docker + name: 代码分析 + + steps: + - name: 代码分析 + image: aosapps/drone-sonar-plugin + settings: + sonar_host: http://: + sonar_token: + + node: + role: sonarqube + ``` + +- 在 gitea 对应项目根目录下创建 sonar-project.properties,内容如下 + ``` + sonar.projectKey={项目名称} + sonar.sources=. + ``` + +- 提交代码,在 drone 中对应项目的 "ACTIVITY FEED" 页面下查看 pipeline 执行过程 +- pipeline 执行完成后,浏览器打开 http://:,即可查看刚检测完成的项目 +- sonarqube pipeline 参考[https://hub.docker.com/r/aosapps/drone-sonar-plugin](https://hub.docker.com/r/aosapps/drone-sonar-plugin) +- sonarqube 配置参考[https://docs.sonarqube.org/latest/analysis/analysis-parameters/](https://docs.sonarqube.org/latest/analysis/analysis-parameters/) + diff --git a/content/post/elasticsearch.md b/content/post/elasticsearch.md new file mode 100644 index 0000000..5ba771e --- /dev/null +++ b/content/post/elasticsearch.md @@ -0,0 +1,187 @@ +--- +title: "Elasticsearch 笔记" +date: 2019-10-30T11:49:53+08:00 +lastmod: 2019-10-30T11:49:53+08:00 +tags: ["elasticsearch"] +categories: ["database"] +--- + +# 索引 +- 查看某节点的全部索引 + ```bash + curl http://127.0.0.1:9200/_cat/indices?v + ``` +- 新建 index + ```bash + curl -X PUT http://127.0.0.1:9200/index_name + ``` +- 删除 index + ```bash + curl -X DELETE http://127.0.0.1:9200/index_name + ``` + +# 记录 +- 新增记录(指定记录id) + ```bash + curl -X PUT -H "Content-Type: application/json" http://127.0.0.1:9200/index_name/doc_id -d ' + { + "aa": "11", + "bb": "22" + }' + ``` +- 新增记录(不指定记录id) + ```bash + curl -X POST -H "Content-Type: application/json" http://127.0.0.1:9200/index_name -d ' + { + "aa": "11", + "bb": "22" + }' + ``` +- 查看记录 + ```bash + curl http://127.0.0.1:9200/index_name/doc_id?pretty=true + ``` +- 删除记录 + ```bash + curl -X DELETE http://127.0.0.1:9200/index_name/doc_id + ``` +- 更新记录 + ```bash + curl -X PUT -H "Content-Type: application/json" http://127.0.0.1:9200/index_name/doc_id -d ' + { + "aa": "33", + "bb": "44" + }' + ``` + +# 查询 +- 查询所有记录 + ```bash + curl http://127.0.0.1:9200/index_name/_search + ``` +- 查询匹配 + ```bash + curl -H "Content-Type: application/json" http://127.0.0.1:9200/index_name/_search -d ' + { + "query": {"match": {"key_name": "value_pattern"}} + }' + ``` +- 从位置2(默认0)开始查询8(默认10)条记录 + ```bash + curl -H "Content-Type: application/json" http://127.0.0.1:9200/index_name/_search -d ' + { + "query": {"match": {"key_name": "value_pattern"}}, + "from": 2, + "size": 8 + }' + ``` +- 逻辑 or 查询 + ```bash + curl -H "Content-Type: application/json" http://127.0.0.1:9200/index_name/_search -d ' + { + "query": {"match": {"key_name": "value_pattern_1 value_pattern_2"}} + }' + ``` +- 逻辑 and 查询 + ```bash + curl -H "Content-Type: application/json" http://127.0.0.1:9200/index_name/_search -d ' + { + "query": { + "bool": { + "must": [ + {"match": {"key_name": "value_pattern_1"}}, + {"match": {"key_name": "value_pattern_2"}} + ] + } + } + }' + ``` +- 区间查询 + ```bash + set -euo pipefail + export START_TIME="$(date +%s -d $1)" + export END_TIME="$(date +%s -d $2)" + curl -s -H "Content-Type: application/json" -o result.txt \ + http://127.0.0.1:9200/wangmei_raw/_search?pretty -d @- <] --list-all + ``` + +# 源地址(source) +- 列出指定zone的所有绑定的source地址 + ```bash + firewall-cmd [--permanent] [--zone=zone] --list-sources + ``` +- 查询指定zone是否跟指定source地址进行了绑定 + ```bash + firewall-cmd [--permanent] [--zone=zone] --query-source=ip[/mask] + ``` +- 用于将一个source地址绑定到指定的zone(只可绑定一次,第二次绑定到不同的zone会报错) + ```bash + firewall-cmd [--permanent] [--zone=zone] --add-source=ip[/mask] + ``` +- 改变source地址所绑定的zone,如果原来没有绑定则进行绑定 + ```bash + firewall-cmd [--permanent] [--zone=zone] --change-source=ip[/mask] + ``` +- 删除source地址跟zone的绑定 + ```bash + firewall-cmd [--permanent] [--zone=zone] --remove-source=ip[/mask] + ``` + +# 网卡(interface) +- 获取网卡所在的 zone + ```bash + firewall-cmd --get-zone-of-interface= + ``` +- 增加网卡到 zone + ```bash + firewall-cmd [--zone=] --add-interface= + ``` +- 修改网卡到 zone + ```bash + firewall-cmd [--zone=] --change-interface= + ``` +- 从 zone 中删除网卡 + ```bash + firewall-cmd [--zone=] --remove-interface= + ``` +- 查看 zone 中是否包含某网卡 + ```bash + firewall-cmd [--zone=] --query-interface= + ``` +# target +- 默认可以取四个值: default、ACCEPT、%%REJECT%%、DROP +- 查看 taget + ```bash + firewall-cmd --permanent [--zone=zone] --get-target + ``` +- 设置 target + ```bash + firewall-cmd --permanent [--zone=zone] --set-target=target + ``` +- **必须使用参数 --permanent**,而且使用 firewall-cmd 命令不能直接生效,需 reload + +# 服务(service) +- 查看支持的 service + ```bash + firewall-cmd --get-services [--permanent] + ``` +- 查看 zone 启动的 service + ```bash + firewall-cmd [--zone=] --list-services + ``` +- 在 zone 中启动 service + ```bash + firewall-cmd [--zone=] --add-service= \ + [ --permanent | --timeout= ] + ``` +- 禁用 zone 中的 service + ```bash + firewall-cmd [--zone=] --remove-service= [--permanent] + ``` +- 查看 zone 中是否启动 service + ```bash + firewall-cmd [--zone=] --query-service= + ``` + +# 端口和协议组合 +- 查看配置的全部端口规则 + ```bash + firewall-cmd [--permanent] [--zone=zone] --list-ports + ``` +- 启动 zone 中指定协议的端口 + ```bash + firewall-cmd [--zone=] --add-port=[-]/ \ + [ --permanent | --timeout= ] + ``` +- 禁用 zone 中指定协议的端口 + ```bash + firewall-cmd [--zone=] --remove-port=[-]/ [--permanent] + ``` +- 查看 zone 中指定协议的端口 + ```bash + firewall-cmd [--zone=] --query-port=[-]/ [--permanent] + ``` + +# ICMP +- 查看支持的 icmp 类型 + ```bash + firewall-cmd --get-icmptypes [--permanent] + ``` +- 查看全部 icmp 阻塞规则 + ```bash + firewall-cmd [--permanent] [--zone=zone] --list-icmp-blocks + ``` +- 启动 zone 中的 icmp 阻塞 + ```bash + firewall-cmd [--zone=] --add-icmp-block= \ + [ --permanent | --timeout=seconds ] + ``` +- 禁用 zone 中的 icmp 阻塞 + ```bash + firewall-cmd [--zone=] --remove-icmp-block= [--permanent] + ``` +- 查询 zone 的 icmp 阻塞 + ```bash + firewall-cmd [--zone=] --query-icmp-block= [--permanent] + ``` + +# IPV4 源地址转换 +- 启动 zone 中 ipv4 源地址转换 + ```bash + firewall-cmd [--zone=] --add-masquerade \ + [ --permanent | --timeout=seconds ] + ``` +- 禁用 zone 中 ipv4 源地址转换 + ```bash + firewall-cmd [--zone=] --remove-masquerade [--permanent] + ``` +- 查看 zone 中 ipv4 源地址转换 + ```bash + firewall-cmd [--zone=] --query-masquerade [--permanent] + ``` + +# 端口转发 +- 查看全部端口转发规则 + ```bash + firewall-cmd [--permanent] [--zone=zone] --list-forward-ports + ``` +- 启动 zone 中端口转发 + ```bash + firewall-cmd [--zone=] --add-forward-port=port=[-]:proto= \ + { :toport=[-] | :toaddr=
| :toport=[-]:toaddr=
} \ + [ --permanent | --timeout=seconds ] + ``` +- 禁用 zone 中端口转发 + ```bash + firewall-cmd [--zone=] --remove-forward-port=port=[-]:proto= \ + { :toport=[-] | :toaddr=
| :toport=[-]:toaddr=
} \ + [--permanent] + ``` +- 查看 zone 中端口转发 + ```bash + firewall-cmd [--zone=] --query-forward-port=port=[-]:proto= \ + { :toport=[-] | :toaddr=
| :toport=[-]:toaddr=
} \ + [--permanent] + ``` + +# Rich Rules +- 通用结构 + ```bash + firewall-cmd [--zone=] [ --permanent | --timeout=seconds ] \ + <--add|--remove>-rich-rule='rule [family= \ + source address= [invert=] \ + destination address= [invert=] \ + service name= \ + port port=[-] \ + protocol= \ + icmp-block name= \ + masquerade \ + forward-port port=[-] protocol= to-port=[-] to-address=
\ + log [prefix=] [level=] [limit value=] \ + accept|reject [type=]|drop' + ``` +- 查看全部 rich rule + ```bash + firewall-cmd [--permanent] [--zone=zone] --list-rich-rules + ``` +- 具体参数解释见系统 man 手册 + ```bash + man firewalld.richlanguage 5 + ``` + +# 应急模式(panic) +- 启动 panic,即断网 + ```bash + firewall-cmd --panic-on + ``` +- 关闭 panic,即联网 + ```bash + firewall-cmd --panic-off + ``` +- 查询应急模式 + ```bash + firewall-cmd --query-panic + ``` + +# 重新载入(reload) +- 重新载入防火墙,不中断用户连接 + ```bash + firewall-cmd --reload + ``` +- 重新载入防火墙并中断用户连接 + ```bash + firewall-cmd --complete-reload + ``` + +# 备注 +- 参数 --timeout 是让规则生效一段时间,过期自动删除,不能与 --permanent 一起使用 + diff --git a/content/post/ftp.md b/content/post/ftp.md new file mode 100644 index 0000000..2b94c1b --- /dev/null +++ b/content/post/ftp.md @@ -0,0 +1,186 @@ +--- +title: "Ftp 笔记" +date: 2019-10-30T17:48:02+08:00 +lastmod: 2019-10-31T21:49:00+08:00 +tags: ["ftp", "vsftp"] +categories: ["storage"] +--- + +# 环境 +- CentOS7 +- vsftpd +- 关闭 selinux + +# 安装 vsftpd 服务 +```bash +yum install vsftpd +``` + +# 常用客户端 +- ftp +- lftp +- curl + +# 主动模式 +- 建立控制命令连接 + - 客户端连接服务端 21 号端口 + +- 建立数据传送连接 + - 客户端在本地监听一个端口(大于 1024 ),并通过 PORT 命令通知服务端 + - 服务端从 20 端口连接客户端正在监听的端口,向客户端发送数据 + +- 相关配置 + ```ini + # 开启主动模式 + pasv_enable = no + ``` + +# 被动模式 +- 建立控制命令连接 + - 客户端连接服务端 21 号端口 + +- 建立数据传送连接 + - 服务端在本地再次监听一个端口(大于 1024),并通过 PASV 命令通知客户端 + - 客户端连接服务端新监听的端口,下载服务端数据 + +- 相关配置 + ```ini + # 开启被动模式 + pasv_enable = yes + # 数据连接可以使用的端口范围的最大端口,0 表示任意端口,默认值为0 + pasv_min_port=30000 + # 数据连接可以使用的端口范围的最小端口,0 表示任意端口,默认值为0 + pasv_max_port=30999 + ``` + +# 匿名用户配置 +```ini +# 控制是否允许匿名用户登入 +# 匿名用户使用的登陆名为 ftp 或 anonymous,口令为空 +# 匿名用户不能离开匿名用户家目录/var/ftp,且只能下载不能上传 +anonymous_enable=YES/NO(YES) + +# 匿名登入时,不会询问密码 +no_anon_password=YES/NO(NO) + +# 定义匿名登入的使用者名称,默认值为ftp +ftp_username=ftp + +# 是否允许登陆用户有写权限,属于全局设置 +write_enable=YES/NO(YES) + +# 使用匿名登入时,所登入的目录,默认值为/var/ftp +# 注意ftp目录不能是777的权限属性,即匿名用户的家目录不能有777的权限 +anon_root=/var/ftp + +# 是否允许匿名者有上传文件(非目录)的权限 +# 只有在write_enable=YES时,此项才有效 +# 匿名用户必须要有对上层目录的写入权 +anon_upload_enable=YES/NO(NO) + +# 是否允许匿名者下载可阅读的档案 +anon_world_readable_only=YES/NO(YES) + +# 是否允许匿名者有新增目录的权限 +# 只有在write_enable=YES时,此项才有效 +# 匿名用户必须要有对上层目录的写入权 +anon_mkdir_write_enable=YES/NO(NO) + +# 是否允许匿名入者拥有其他权限,譬如删除或者重命名 +# 如果anon_upload_enable=NO,则匿名用户不能上传文件,但可以删除或者重命名已经存在的文件 +# 如果anon_mkdir_write_enable=NO,则匿名用户不能上传或者新建文件夹,但可以删除或者重命名已经存在的目录 +anon_other_write_enable=YES/NO(NO) + +# 是否改变匿名用户上传文件(非目录)的属主 +chown_uploads=YES/NO(NO) + +# 设置匿名用户上传文件(非目录)的属主名 +# 建议不要设置为root +chown_username=username + +# 设置匿名登入者新增或上传档案时的 umask 值,默认值为 077 +anon_umask=077 +``` + +# 配置 +## 常用配置 +```ini +#允许匿名用户登陆 +anonymous_enable=YES +#允许本地用户登陆 +local_enable=YES +#允许登陆用户写可访问的目录或文件 +write_enable=YES +#指定用户登陆后直接进入系统的/mnt目录 +local_root=/mnt +chroot_list_enable=YES +#限定登陆用户可访问的目录只有自己的家目录或指定的local_root目录 +chroot_list_file=/etc/vsftpd/chroot_list +``` + +## 允许 vsftpd 匿名用户上传和下载 +- 创建匿名用户登陆目录 + ```bash + mkdir -p /var/ftp/pub + chown -R ftp.ftp /var/ftp/pub + chmod o+w /var/ftp/pub + ``` + +- 修改 vsftpd.conf + ```ini + #允许匿名用户登录FTP + anonymous_enable=YES + #设置匿名用户的登录目录(如需要,需自己添加并修改) + anon_root=/var/ftp/pub + #打开匿名用户的上传权限 + anon_upload_enable=YES + #打开匿名用户创建目录的权限 + anon_mkdir_write_enable=YES + #打开匿名用户删除和重命名的权限(如需要,需自己添加) + anon_other_write_enable=YES + anon_umask=022 + ``` + +## 本地用户登陆 +- 修改 vsftpd.conf + ``` + # 不允许匿名用户登入 + anonymous_enable=no + # 允许本地用户登入 + local_enable=YES + # 当本地用户登入时,将被更换到定义的目录下 + # 默认值为各用户的家目录 + local_root=/home/username + # 是否允许登陆用户有写权限 + # 属于全局设置,默认值为YES。 + write_enable=YES/NO(YES) + # 本地用户新增档案时的 umask 值,默认值为077 + local_umask=022 + # 本地用户上传档案后的档案权限 + # 与chmod所使用的数值相同,默认值为0666 + file_open_mode=0755 + # 指定用户列表文件中的用户不允许切换到上级目录 + chroot_local_user=YES + ``` + +## 创建 ftp 专用账户 +- 创建用户 ftpuser1 + ```bash + useradd -s /sbin/nologin ftpuser1 + passwd ftpuser1 + ``` + +- 修改 vsftpd.conf + ```ini + anonymous_enable=no + local_enable=YES + local_root=/home/ftpuser + write_enable=YES + local_umask=022 + file_open_mode=0755 + chroot_local_user=YES + ``` + +# 参考 +- [vsftpd 详细配置](http://vsftpd.beasts.org/vsftpd_conf.html) + diff --git a/content/post/git-http.md b/content/post/git-http.md new file mode 100644 index 0000000..7022d9d --- /dev/null +++ b/content/post/git-http.md @@ -0,0 +1,114 @@ +--- +title: "搭建 Git http 服务" +date: 2019-10-30T17:35:56+08:00 +lastmod: 2019-10-30T17:35:56+08:00 +tags: ["git", "nginx", "http", "fcgiwrap", "spawn-fcgi"] +categories: ["dev/ops"] +--- + +# 环境 +- Centos/Redhat 6 +- 安装好 git + +# 安装 fcgiwrap +- 参考 https://github.com/gnosek/fcgiwrap +![](/img/150546_2jE9_2298475.png) + +# 安装 spawn-fcgi +- 参考 https://github.com/lighttpd/spawn-fcgi +![](/img/150655_jGEp_2298475.png) + +# 编辑 spawn-fcgi/sbin/fcgiwrap 开机启动脚本: +```bash +#! /bin/sh +DESC="fcgiwrap daemon" +DEAMON=/opt/spawn-fcgi/bin/spawn-fcgi +PIDFILE=/tmp/spawn-fcgi.pid +# fcgiwrap socket +FCGI_SOCKET=/tmp/fcgiwrap.socket +# fcgiwrap 可执行文件 +FCGI_PROGRAM=/opt/fcgiwrap/sbin/fcgiwrap +# fcgiwrap 执行的用户和用户组 +FCGI_USER=nobody +FCGI_GROUP=nobody +FCGI_EXTRA_OPTIONS="-M 0770" +OPTIONS="-u $FCGI_USER -g $FCGI_GROUP -s $FCGI_SOCKET -S $FCGI_EXTRA_OPTIONS -F 1 -P $PIDFILE -- $FCGI_PROGRAM" +do_start() { + $DEAMON $OPTIONS || echo -n "$DESC already running" +} +do_stop() { + kill -INT `cat $PIDFILE` || echo -n "$DESC not running" +} +case "$1" in + start) + echo -n "Starting $DESC: $NAME" + do_start + echo "." + ;; + stop) + echo -n "Stopping $DESC: $NAME" + do_stop + echo "." + ;; + restart) + echo -n "Restarting $DESC: $NAME" + do_stop + do_start + echo "." + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 + exit 3 + ;; +esac +exit 0 +``` + +# 启动 fcgiwrap +```bash +chmod 0755 /opt/spawn-fcgi/sbin/fcgiwrap +/opt/spawn-fcgi/sbin/fcgiwrap start +``` + +# 安装 nginx +- 参考 http://nginx.org/en/download.html,启动 nginx + +# 在 nginx 前端目录 html 下创建 git 仓库目录 nginx/html/git/ +```bash +mkdir /opt/nginx/html/git/ +chown nobody.nobody /opt/nginx/html/git/ -R +``` + +# 配置 nginx 的 git 服务 +```nginx +# 在 server section 中添加 git +location ~ /git(/.*) { + gzip off; + fastcgi_pass unix:/tmp/fcgiwrap.socket; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend; + fastcgi_param GIT_HTTP_EXPORT_ALL ""; + fastcgi_param GIT_PROJECT_ROOT /opt/nginx/html/git; + fastcgi_param PATH_INFO $1; + fastcgi_param REMOTE_USER $remote_user; + client_max_body_size 500m; +} +``` + +# 重新加载配置文件 +```bash +/opt/nginx/sbin/nginx -s reload +``` + +# 测试,在仓库目录下新建一个空repo +```bash +cd /opt/nginx/html/git/ +git init --bare test-repo +chown nobody.nobody test-repo -R +cd test-repo +git config http.reveivepack true +``` + +# 完成 +- 在另一台服务器中可以顺利的进行 clone、push 及 pull 等操作。 + diff --git a/content/post/gnome.md b/content/post/gnome.md new file mode 100644 index 0000000..ed49bf1 --- /dev/null +++ b/content/post/gnome.md @@ -0,0 +1,119 @@ +--- +title: "Gnome 笔记" +date: 2019-10-30T11:34:41+08:00 +lastmod: 2020-03-14T16:10:00+08:00 +tags: ["gnome", "desktop", "桌面"] +categories: ["os"] +--- + +# 常用设置 +```bash +# 关闭左上角热响应 +gsettings set org.gnome.desktop.interface enable-hot-corners false + +# 缩放系统字体 +gsettings set org.gnome.desktop.interface text-scaling-factor 1.5 + +# monospace 字体 +gsettings set org.gnome.desktop.interface monospace-font-name 'YaHei Consolas Hybrid 15' + +# gtk 主题 +gsettings set org.gnome.desktop.interface gtk-theme 'Vertex-Dark' + +# gnome-shell 主题 +gsettings set org.gnome.shell.extensions.user-theme name 'Vertex' + +# 图标主题 +gsettings set org.gnome.desktop.interface icon-theme 'Faenza' + +# 调整 gnome3 桌面(包括 gdm )放大级别 +# 0 系统自动缩放 +# n > 0 放大 n 倍 +gsettings set org.gnome.desktop.interface scaling-factor 1 + +# 启用用户扩展 +gsettings set org.gnome.shell disable-user-extensions false +gsettings set org.gnome.shell enabled-extensions "['user-theme@gnome-shell-extensions.gcampax.github.com', 'workspace-indicator@gnome-shell-extensions.gcampax.github.com', 'dash-to-panel@jderose9.github.com']" + +# 工作区 +gsettings set org.gnome.mutter dynamic-workspaces false +gsettings set org.gnome.desktop.wm.preferences num-workspaces 4 +gsettings set org.gnome.desktop.wm.preferences workspace-names "['乾坤', '巽震', '坎离', '艮兑']" + +# 窗口按钮 +gsettings set org.gnome.desktop.wm.preferences button-layout 'appmenu:minimize,maximize,close' +``` + +# 在 "活动" 中创建 "文件夹" +```bash +# 创建文件夹 +gsettings set org.gnome.desktop.app-folders folder-children "['Office','VirtualBox']" + +# 指定文件夹名字 +gsettings set org.gnome.desktop.app-folders.folder:/org/gnome/desktop/app-folders/folders/Office/ name "Office" +gsettings set org.gnome.desktop.app-folders.folder:/org/gnome/desktop/app-folders/folders/VirtualBox/ name "VirtualBox" + +# 指定文件夹包含的应用类别 +gsettings set org.gnome.desktop.app-folders.folder:/org/gnome/desktop/app-folders/folders/Office/ categories "['Office']" +gsettings set org.gnome.desktop.app-folders.folder:/org/gnome/desktop/app-folders/folders/VirtualBox/ categories "['Emulator']" +``` + +# 快捷键 +```bash +gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-1 "[]" +gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-last "[]" +gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-up "['Up']" +gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-down "['Down']" +gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-left "['Left']" +gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-right "['Right']" + +gsettings set org.gnome.desktop.wm.keybindings switch-applications "[]" +gsettings set org.gnome.desktop.wm.keybindings switch-applications-backward "[]" +gsettings set org.gnome.desktop.wm.keybindings switch-windows "['Tab']" +gsettings set org.gnome.desktop.wm.keybindings switch-windows-backward "['Tab']" +gsettings set org.gnome.shell.window-switcher current-workspace-only true +gsettings set org.gnome.shell.window-switcher app-icon-mode both + +gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-down "['Down']" +gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-up "['Up']" +gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-last "[]" + +gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-up "[]" +gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-right "[]" +gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-left "[]" +gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-down "[]" + +gsettings set org.gnome.desktop.wm.keybindings show-desktop "['d']" +gsettings set org.gnome.desktop.wm.keybindings toggle-maximized "['Up']" +gsettings set org.gnome.desktop.wm.keybindings minimize "['Down']" +gsettings set org.gnome.desktop.wm.keybindings maximize "[]" +``` + +# 在 CentOS6 下安装 gnome 桌面 +```bash +yum groupinstall 'X Window System' +yum groupinstall Desktop +sed -i '/^id/id:5:initdefault:' /etc/inittab +``` + +# 在 CentOS7 下安装 gnome3 桌面 +```bash +yum groupinstall 'X Window System' +yum groupinstall 'Gnome Desktop' +systemctl set-default graphical.target +systemctl enable gdm +# 创建一个可登陆的普通用户 +``` + +# 在 gdm 中隐藏用户名 +- 修改文件/etc/gdm/gdm.schemas,找到这一段: + ```xml + + greeter/Exclude + s + bin,root, daemon,adm,lp,sync,shutdown,halt,mail,news,uucp,operator, + nobody,nobody4,noaccess,postgres,pvm,rpm,nfsnobody,pcap + + ``` +- 将用户名添加在之间即可,用逗号隔开,保存退出即可。 + diff --git a/content/post/grub.md b/content/post/grub.md new file mode 100644 index 0000000..c95d873 --- /dev/null +++ b/content/post/grub.md @@ -0,0 +1,92 @@ +--- +title: "Grub 笔记" +date: 2019-10-30T17:39:58+08:00 +lastmod: 2019-10-30T17:39:58+08:00 +tags: ["grub"] +categories: ["OS"] +--- + +# 启动 archlinux 镜像 64位系统 +``` +menuentry "Archlinux-ISO-x86-64" --class iso { + set isofile="" + set partition="" + loopback loop (hd0,$partition)$isofile + linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_ISO_X86-64 img_dev=/dev/sda$partition img_loop=$isofile earlymodules=loop + initrd (loop)/arch/boot/x86_64/archiso.img +} +``` + +# 启动 archlinux 镜像 32位系统 +```bash +menuentry "Archlinux-ISO-i686" --class iso { + set isofile="" + set partition="2" + loopback loop (hd0,$partition)$isofile + linux (loop)/arch/boot/i686/vmlinuz archisolabel=ARCH_ISO_I686 img_dev=/dev/sda$partition img_loop=$isofile earlymodules=loop + initrd (loop)/arch/boot/i686/archiso.img +} +``` + +# 启动 ubuntu 镜像 +```bash +menuentry "ubuntu-ISO" { + set isofile="" + loopback loop (hd0,1)$isofile + linux (loop)/casper/vmlinuz.efi boot=casper iso-scan/filename=$isofile quiet noeject noprompt splash -- + initrd (loop)/casper/initrd.lz +} +``` + +# 启动安装在第一块硬盘第一个mbr分区的 win7 系统 +```bash +menuentry "Microsoft Windows Vista/7/8/8.1/10 BIOS-MBR" { + insmod part_msdos + insmod ntfs + insmod search_fs_uuid + insmod ntldr + #"9AB0287EB028634D" 是 win7 系统分区的 uuid + search --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 9AB0287EB028634D + ntldr /bootmgr +} +``` + +# 启动安装在第一块硬盘的第一个gpt分区的 Win10 EFI +```bash +menuentry "Microsoft Windows 10" { + insmod part_gpt + insmod fat + insmod search_fs_uuid + insmod chain + #"0007-C871" 是 win10 EFI 分区的 uuid + search --fs-uuid --set=root --hint-bios=hd0,gpt1 --hint-efi=hd0,gpt1 --hint-baremetal=ahci0,gpt1 0007-C871 + chainloader /EFI/Microsoft/Boot/bootmgfw.efi +} +``` + +# 关机 +```bash +menuentry "System shutdown" { + echo "System shutting down..." + halt +} +``` + +# 重启 +```bash +menuentry "System restart" { + echo "System rebooting..." + reboot +} +``` + +# UEFI 配置 +```bash +menuentry "Firmware setup" { + fwsetup +} +``` + +# 详细使用参考 +- [GRUB-ArchWiki](https://wiki.archlinux.org/index.php/GRUB) + diff --git a/content/post/haproxy.md b/content/post/haproxy.md new file mode 100644 index 0000000..b5637ef --- /dev/null +++ b/content/post/haproxy.md @@ -0,0 +1,175 @@ +--- +title: "Haproxy 笔记" +date: 2019-10-30T11:40:20+08:00 +lastmod: 2019-10-30T11:40:20+08:00 +tags: ["haproxy", "高可用", "负载均衡"] +categories: ["ha/lb"] +--- + +# CentOS7 下安装 +- CentOS7 自带的 haproxy 版本太低,这里通过 cheese 源安装最新版本 +- 安装 cheese repo,详细参考[这里](http://www.nosuchhost.net/~cheese/fedora/packages/epel-7/x86_64/cheese-release.html) + ```bash + wget http://www.nosuchhost.net/~cheese/fedora/packages/epel-7/x86_64/cheese-release-7-1.noarch.rpm + rpm -Uvh cheese-release-7-1.noarch.rpm + ``` +- 安装 haproxy + ```bash + yum install haproxy + ``` +- 修改 sysctl.conf + ```bash + cat >> /etc/sysctl.conf <<-END + net.ipv4.ip_forward=1 + net.ipv4.tcp_syncookies = 1 + net.ipv4.tcp_tw_reuse = 1 + net.ipv4.tcp_tw_recycle = 1 + net.ipv4.tcp_fin_timeout = 8 + END + sysctl -p + ``` +- 禁用 selinux + ```bash + sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + setenforce 0 + ``` + +# 全局配置 +``` +global + log 127.0.0.1 local2 info + chroot /var/lib/haproxy # 如果需要外部检查脚本,则需注释该行 + #external-check # 如果需要外部检查脚本,则取消注释 + pidfile /var/run/haproxy.pid + maxconn 102400 + user haproxy + group haproxy + daemon + stats socket /var/lib/haproxy/stats +``` + +# 默认配置 +``` +defaults + log global + option dontlognull + option redispatch + option abortonclose + timeout check 8s +``` + +# tcp 连接多个 ceph-radosgw +``` +frontend ceph-radosgw + bind *:7480 + timeout client 8s + default_backend ceph-radosgw +backend ceph-radosgw + mode tcp + balance roundrobin + timeout connect 8s + timeout server 8s + retries 2 + #option external-check + #external-check command /var/lib/haproxy/health_check.sh + server cpeh240 10.9.10.234:7480 check + server ceph241 10.9.10.235:7480 check + server ceph243 10.9.10.236:7480 check +``` + +# tcp 连接 mysql galera cluster +``` +frontend mysql + bind *:3306 + timeout client 1800s + default_backend mysql +backend mysql + balance source + option tcpka + timeout connect 8s + timeout server 1800s + retries 2 + server mysql231 10.9.10.231:3306 check inter 4s + server mysql232 10.9.10.232:3306 check inter 4s + server mysql233 10.9.10.233:3306 check inter 4s +``` + +# tcp 连接 redis 主库 +``` +frontend redis + bind *:6379 + timeout client 1800s + default_backend redis +backend redis + balance roundrobin + timeout connect 8s + timeout server 1800s + retries 2 + option tcp-check + tcp-check connect + tcp-check send PING\r\n + tcp-check expect string +PONG + tcp-check send info\ replication\r\n + tcp-check expect string role:master + tcp-check send QUIT\r\n + tcp-check expect string +OK + server redis87 10.1.14.87:6379 check inter 4s + server redis88 10.1.14.88:6379 check inter 4s + server redis89 10.1.14.89:6379 check inter 4s +``` + +# 状态页面 +``` +listen admin_stats + bind 0.0.0.0:10080 + mode http + maxconn 100 + timeout client 1m + timeout connect 4s + timeout server 4s + stats refresh 30s + stats uri / + stats auth username:password + stats realm haproxy for private user, enter username/password + stats hide-version +``` + +# 通过 rsyslog 生成日志 +```bash +sed -i -e '/ModLoad imudp/s/^#//' \ + -e '/UDPServerRun 514/s/^#//' /etc/rsyslog.conf +cat > /etc/rsyslog.d/haproxy.conf < + DAV svn + SVNListParentPath off + SVNPath /mnt/vdb1/svn_repos/test_prj/ + #Satisfy Any + AuthzSVNAccessFile /mnt/vdb1/svn_repos/test_prj/conf/authz + Require valid-user + + ``` +- 增加 apache 用户读写 test_prj 目录的权限 + ```bash + usermod -a -G root apache + chmod -R g+w /mnt/vdb1/svn_repos/ + ``` +- 重启 httpd 服务 + ```bash + systemctl restart httpd + ``` + +# Basic HTTP 认证 +- 生成密码文件(用户名是admin,密码是123456) + ```bash + htpasswd -c -m /etc/httpd/httpd.auth admin # 按提示输入密码 + ``` +- 在 Location 中配置如下 + ```xml + + AuthType Basic + AuthName "提示信息" + AuthUserFile /etc/httpd/httpd.auth + + ``` + diff --git a/content/post/ip.md b/content/post/ip.md new file mode 100644 index 0000000..cb1cb48 --- /dev/null +++ b/content/post/ip.md @@ -0,0 +1,33 @@ +--- +title: "IP 地址分类及内网 IP" +date: 2019-10-29T18:49:31+08:00 +lastmod: 2019-10-29T18:49:31+08:00 +keywords: [] +description: "" +tags: ["ip"] +categories: ["network"] +--- + +# 私有 IP +- A类: 10.0.0.1-10.255.255.254 +- B类: 172.16.0.1-172.31.255.254 +- C类: 192.168.0.1-192.168.255.254 + +# 地址分类 +- A类: 一个A类IP地址仅使用第一个8位位组表示网络地址。剩下的3个8位位组表示主机地址。A类地址的第一个位总为0,这一点在数学上限制了A类地址的范围小于 127,127是64+32+16+8+4+2+1的和。最左边位表示128,在这里空缺。因此仅有127个可能的A类网络。A类地址后面的24位(3个点-十进制数)表示可能的主机地址,A类网络地址的范围从1.0.0.0到126.0.0.0。注意只有第一个8位位组表示网络地址,剩余的3个8位位组用于表示第一个8位位组所表示网络中惟一的主机地址,当用于描述网络时这些位置为0。注意技术上讲,127.0.0.0 也是一个A类地址,但是它已被保留作闭环(look back )测试之用而不能分配给一个网络。每一个A类地址能支持16777214个不同的主机地址,这个数是由2的24次方再减去2得到的。减2是必要的,因为 IP把全0保留为表示网络而全1表示网络内的广播地址。其中10.0.0.0 至10.255.255.255保留,作为局域网地址使用。 +- B类: 设计B类地址的目的是支持中到大型的网络。B类网络地址范围从128.1.0.0到191.254.0.0。B 类地址蕴含的数学逻辑是相当简单的。一个B类IP地址使用两个8位位组表示网络号,另外两个8位位组表示主机号。B类地址的第1个8位位组的前两位总置为 10,剩下的6位既可以是0也可以是1,这样就限制其范围小于等于191,由128+32+16+8+4+2+1得到。最后的16位( 2个8位位组)标识可能的主机地址。每一个B类地址能支持64534 个惟一的主机地址,这个数由2的16次方减2得到。B类网络仅有16382个。其中172.16.0.0至172.31.255.255保留作为局域网地址使用。 +- C类: C类地址用于支持大量的小型网络。这类地址可以认为与A类地址正好相反。A类地址使用第一个8位位组表示网络号,剩下的3个表示主机号,而C类地址使用三个8位位组表示网络地址,仅用一个8位位组表示主机号。C类地址的前3位数为110,前两位和为192(128+64),这形成了C类地址空间的下界。第三位等于十进制数32,这一位为0限制了地址空间的上界。不能使用第三位限制了此8位位组的最大值为255-32等于223。因此C类网络地址范围从 192.0.1.0 至223.255.254.0。最后一个8位位组用于主机寻址。每一个C类地址理论上可支持最大256个主机地址(0~255),但是仅有254个可用,因为0和255不是有效的主机地址。可以有2097150个不同的C类网络地址。其中192.168.0.0至192.168.255.255保留作为局域网地址使用。 +- D类: D类地址用于在IP网络中的组播( multicasting ,又称为多目广播)。D类地址的前4位恒为1110 ,预置前3位为1意味着D类地址开始于128+64+32等于224。第4位为0意味着D类地址的最大值为128+64+32+8+4+2+1为239,因此D类地址空间的范围从224.0.0.0到239. 255. 255.254。 +- E类: E类地址保留作研究之用。因此Internet上没有可用的E类地址。E类地址的前4位恒为1,因此有效的地址范围从240.0.0.0 至255.255.255.255。 +- 总的来说,ip地址分类由第一个八位组的值来确定。任何一个0到127 间的网络地址均是一个A类地址。任何一个128到191间的网络地址是一个B类地址。任何一个192到223 间的网络地址是一个C类地址。任何一个第一个八位组在224到239 间的网络地址是一个组播地址即D类地址。E类保留。 + +# 特殊 IP +- 127.0.0.0: 127是一个保留地址,该地址是指电脑本身,主要作用是预留下作为测试使用,用于网络软件测试以及本地机进程间通 信。在Windows系统下,该地址还有一个别名叫“localhost”,无论是哪个程序,一旦使用该地址发送数据,协议软件会立即返回,不进行任何网 络传输,除非出错,包含该网络号的分组是不能够出现在任何网络上的。 +- 10.x.x.x、172.16.x.x~172.31.x.x、192.168.x.x: 私有地址,这些地址被大量用于企业内部网络中。一些宽带路由器,也往往使用192.168.1.1作为缺省地址。私有网络由于不与外部互连,因而可能使 用随意的IP地址。保留这样的地址供其使用是为了避免以后接入公网时引起地址混乱。使用私有地址的私有网络在接入Internet时,要使用地址翻译 (nat),将私有地址翻译成公用合法地址。在Internet上,这类地址是不能出现的。 +- 0.0.0.0: 严格意义上来 说,0.0.0.0已经不是真正意义上的IP地址了。它表示的是这样一个集合,所有不清楚的主机和目的网络。这里的不清楚是指在本机的路由表里没有特定条 目指明如何到达。对本机来说,它就是一个收容所,所有不认识的三无人员,一律送进去。如果你在网络设置中设置了缺省网关,那么Windows系统就会自动 产生一个目地址为0.0.0.0的缺省路由。若IP地址全为0,也就是0.0.0.0,则这个IP地址在IP数据报中只能用作源IP地址,这发生在当设备启动时但又不知道自己的IP地址情况下。在使 用DHCP分配IP地址的网络环境中,这样的地址是很常见的。用户主机为了获得一个可用的IP地址,就给DHCP服务器发送IP分组,并用这样的地址作为 源地址,目的地址为255.255.255.255(因为主机这时还不知道DHCP服务器的IP地址)。 +- 255.255.255.255: 受限制的广播地址,对本机来说,这个地址指本网段内(同一 个广播域)的所有主机,该地址用于主机配置过程中IP数据包的目的地址,这时主机可能还不知道它所在网络的网络掩码,甚至连它的IP地址也还不知道。在任 何情况下,路由器都会禁止转发目的地址为受限的广播地址的数据包,这样的数据包仅会出现在本地网络中。 +- 224.0.0.1: 组播地址,注意它和广播的区别。从224.0.0.0到239.255.255.255都是这样的地址。224.0.0.1特指所有主 机,224.0.0.2特指所有路由器。这样的地址多用于一些特定的程序以及多媒体程序。如果你的主机开启了IRDP(Internet路由发现协议,使 用组播功能)功能,那么你的主机路由表中应该有这样一条路由。 +- 169.254.*.*: 如果你的主机使用了DHCP功能自动获 得一个IP地址,那么当你的DHCP服务器发生故障或响应时间太长而超出系统规定的一个时间,Windows系统会为你分配这样一个地址。如果你发现你的 主机IP地址是个诸如此类的地址,很不幸,十有八九是你的网络不能正常运行了。 +- 直接广播地址: 一个网络中的最后一个地址为直接广播地址,也就是HostID全为1的地址。主机使用这种地址把一个IP数据报发送到本地网段的所有设备上,路由器会转发这种数据报到特定网络上的所有主机。**注意:这个地址在IP数据报中只能作为目的地址。另外,直接广播地址使一个网段中可分配给设备的地址数减少了1个**。 +- NetID为0的IP地址: 当某个主机向同一网段上的其他主机发送报文时就可以使用这样的地址,分组也不会被路由器转发。比如12.12.12.0/24这个网络中的一台主机12.12.12.2/24在与同一网络中的另一台主机12.12.12.8/24通信时,目的地址可以是0.0.0.8。 + diff --git a/content/post/ipv6.md b/content/post/ipv6.md new file mode 100644 index 0000000..ed39581 --- /dev/null +++ b/content/post/ipv6.md @@ -0,0 +1,32 @@ +--- +title: "Ipv6 笔记" +date: 2019-10-29T21:07:06+08:00 +lastmod: 2019-10-29T21:07:06+08:00 +keywords: [] +tags: ["ipv6", "centos"] +categories: ["network"] +--- + +# CentOS7 禁用 ipv6 +## 方法1 +- 编辑 /etc/sysctl.conf,增加如下内容 + ``` + net.ipv6.conf.all.disable_ipv6 =1 + net.ipv6.conf.default.disable_ipv6 =1 + ``` +- 如果你想要为特定的网卡禁止IPv6,比如,对于enp0s3,添加下面的行 + ``` + net.ipv6.conf.enp0s3.disable_ipv6 =1 + ``` +- 生效 + ```bash + sysctl -p + ``` + +## 方法2 +- 执行下面命令 + ```bash + echo 1>/proc/sys/net/ipv6/conf/all/disable_ipv6 + echo 1>/proc/sys/net/ipv6/conf/default/disable_ipv6 + ``` + diff --git a/content/post/k3s-install.md b/content/post/k3s-install.md new file mode 100644 index 0000000..9651223 --- /dev/null +++ b/content/post/k3s-install.md @@ -0,0 +1,159 @@ +--- +title: "CentOS7 安装 K3S" +date: 2020-09-25T14:21:00+08:00 +lastmod: 2020-09-25T14:21:00+08:00 +keywords: [] +tags: ["rancher", "k3s"] +categories: ["container"] +--- + +# 环境 + +角色 | 主机名 | 操作系统 | 软件 +---- | ---- | ---- | ---- +数据库 | - | - | docker-ce 19.03 +k3s server | k3s-server0 | CentOS7.8 | docker-ce 19.03, k3s v1.18.9 +k3s server | k3s-server1 | CentOS7.8 | docker-ce 19.03, k3s v1.18.9 +k3s agent | k3s-agent0 | CentOS7.8 | docker-ce 19.03, k3s v1.18.9 +k3s agent | k3s-agent1 | CentOS7.8 | docker-ce 19.03, k3s v1.18.9 + +- **全部服务器关闭 firewalld、selinux 和 swap,设置时间同步** +- **全部 k3s 服务器(除了数据库)必须设置唯一主机名** + +# 安装数据库 +- 在数据库服务器上执行如下操作 +- 启动 docker 容器 + ```bash + docker run -d \ + --name mariadb \ + -p 3306:3306 \ + -v /data/mariadb/binlog:/var/lib/mysql-bin \ + -v /data/mariadb/db:/var/lib/mysql \ + -v /data/mariadb/log:/var/log/mysql \ + harbor.colben.cn/general/alpine-mariadb + ``` + +- 创建 k3s 数据库 + ```bash + docker exec mariadb mysql -e " + CREATE DATABASE k3s DEFAULT CHARSET UTF8MB4; + CREATE USER k3s@'%' IDENTIFIED BY 'Password_1234'; + GRANT ALL ON k3s.* TO k3s@'%'; + FLUSH PRIVILEGES; + " + ``` + +- 生产环境建议配置 mysql 主从高可用,参考[MariaDB 主从复制](/post/mariadb-replication/) + +# 安装 k3s server +- 在每台 k3s server 服务器上执行如下操作 +- 下载并安装 k3s + ```bash + cd /usr/local/bin/ + curl -LO https://github.com/rancher/k3s/releases/download/v1.18.9%2Bk3s1/k3s + chmod 0755 k3s + ln -s k3s kubectl + ``` + +- 创建 systemd 服务文件 /etc/systemd/system/k3s-server.service,内容如下 + ```ini + [Unit] + Description=Lightweight Kubernetes + Documentation=https://k3s.io + Wants=network-online.target + + [Install] + WantedBy=multi-user.target + + [Service] + Type=notify + KillMode=process + Delegate=yes + LimitNOFILE=infinity + LimitNPROC=infinity + LimitCORE=infinity + TasksMax=infinity + TimeoutStartSec=0 + Restart=always + RestartSec=5s + SuccessExitStatus=1 + ExecStartPre=-/sbin/modprobe br_netfilter + ExecStartPre=-/sbin/modprobe overlay + ExecStart=/usr/local/bin/k3s \ + server \ + --docker \ + --datastore-endpoint 'mysql://k3s:Password_1234@tcp({mysql 地址}:{mysql 端口})/k3s' \ + --disable 'coredns,servicelb,traefik,local-storage,metrics-server' \ + --pause-image 'harbor.colben.cn/k3s/pause:3.2' + ``` + +- 重载 systemd 系统服务,启动 k3s-server 服务 + ```bash + systemctl daemon-reload + systemctl start k3s-server + ``` + +- 获取 token 信息(同一集群内各 server 上该文件完全一样),该信息用于 agent 连接 + ```bash + cat /var/lib/rancher/k3s/server/token + ``` + +- 多个 k3s-server 服务可通过 keepalived 配置高可用,参考[keepalived 笔记](/post/keepalived/) + +# 安装 k3s agent +- 在每台 k3s agent 服务器上执行如下操作 +- 下载并安装 k3s + ```bash + cd /usr/local/bin/ + curl -LO https://github.com/rancher/k3s/releases/download/v1.18.9%2Bk3s1/k3s + chmod 0755 k3s + ``` + +- 创建 systemd 服务文件 /etc/systemd/system/k3s-agent.service,内容如下 + ```ini + [Unit] + Description=Lightweight Kubernetes + Documentation=https://k3s.io + Wants=network-online.target + + [Install] + WantedBy=multi-user.target + + [Service] + Type=notify + KillMode=process + Delegate=yes + LimitNOFILE=infinity + LimitNPROC=infinity + LimitCORE=infinity + TasksMax=infinity + TimeoutStartSec=0 + Restart=always + RestartSec=5s + SuccessExitStatus=1 + ExecStartPre=-/sbin/modprobe br_netfilter + ExecStartPre=-/sbin/modprobe overlay + ExecStart=/usr/local/bin/k3s \ + agent \ + --docker \ + --server 'https://{任一 server 地址或 server 高可用地址}:6443' \ + --pause-image 'harbor.boyachain.cn:20443/k3s/pause:3.2' \ + --token '{server token 信息}' + ``` + +- 重载 systemd 系统服务,启动 k3s-agent 服务 + ```bash + systemctl daemon-reload + systemctl start k3s-agent + ``` + +# 查看节点信息 +- 在任一 k3s server 服务器上执行如下操作 +- 查看节点信息 + ```bash + kubectl get nodes + ``` + +# 注意事项 +- k3s 内部 ssl 证书有效期一年,可在到期前重启 k3s 集群轮换证书 + diff --git a/content/post/k8s-coredns.md b/content/post/k8s-coredns.md new file mode 100644 index 0000000..5264780 --- /dev/null +++ b/content/post/k8s-coredns.md @@ -0,0 +1,276 @@ +--- +title: "K8s 部署 Coredns 组件" +date: 2019-10-30T01:06:56+08:00 +lastmod: 2020-02-10T14:36:00+08:00 +keywords: [] +tags: ["kubernetes", "k8s", "coredns"] +categories: ["container"] +--- + +# 环境 + +- [二进制部署的 kubernetes v1.17.2 集群](https://colben.cn/post/k8s-install/) +- coreDNS 1.6.6 + +# 生成 service account 文件 +- 创建 0.coredns-sa.yml + ```bash + cat > 0.coredns-sa.yml < 1.coredns-rbac.yml < 2.coredns-configmap.yml < 3.coredns-deployment.yml < 4.coredns-service.yml < -n kube-system + ``` + +# 参考 +- [coredns 部署](https://github.com/coredns/deployment/tree/master/kubernetes) + diff --git a/content/post/k8s-install.md b/content/post/k8s-install.md new file mode 100644 index 0000000..5bab88f --- /dev/null +++ b/content/post/k8s-install.md @@ -0,0 +1,832 @@ +--- +title: "K8s 二进制安装" +date: 2019-10-30T01:09:48+08:00 +lastmod: 2020-02-03T11:43:30+08:00 +keywords: [] +tags: ["kubernetes", "k8s", "二进制"] +categories: ["container"] +--- + +# 环境 + +角色 | 主机名 | 内网 IP | 集群 IP | 操作系统 | 服务 | 执行目录 +---- | ---- | ---- | ---- | ---- | ---- | ---- +部署机 k8s-master | master120 | 10.0.4.120 | - | CentOS | kube-apiserver kube-scheduler kube-controller-manager | /opt/kubernetes/ +etcd-node | etcd121 | 10.0.4.121 | 10.10.10.121 | CentOS | etcd | /opt/etcd/ +etcd-node | etcd122 | 10.0.4.122 | 10.10.10.122 | CentOS | etcd | /opt/etcd/ +etcd-node | etcd123 | 10.0.4.123 | 10.10.10.123 | CentOS | etcd | /opt/etcd/ +k8s-node | node124 | 10.0.4.124 | - | CentOS | docker flannel kubelet kube-proxy | /opt/kubernetes/ +k8s-node | node125 | 10.0.4.125 | - | CentOS | docker flannel kubelet kube-proxy | /opt/kubernetes/ +k8s-node | node126 | 10.0.4.126 | - | CentOS | docker flannel kubelet kube-proxy | /opt/kubernetes/ + +- 全部服务器关闭 firewalld 和 selinux,禁用 swap,部署机(master120)可免密 ssh 登陆其他服务器 +- 软件版本 + - CentOS: 7.7 + - etcd: 3.3.18 + - docker: ce-19.03.5 + - flannel: 0.11.0 + - kubernetes: 1.17.2 +- k8s牵扯到多个网段,这里说明下 + - 10.0.4.0/24 该网段是服务器物理网卡 IP 地址段,通过该地址访问互联网 + - 10.10.10.0/24 该网段是杜撰的,但也配置在服务器物理网卡上,用于 etcd 集群节点间通信,与 k8s 集群无关 + - 10.10.9.0/24 该网段是杜撰的,分配 k8s service 的 clusterIP + - 172.17.0.0/24 该网段是杜撰的,是 docker 网桥自带网段,也是 flannel 提供的网段,实现不同节点间的容器互通 + - 172.16.0.0/24 该网段是杜撰的,是 k8s pod 的 IP 地址区间,用于区别流量来源 + +# 部署 etcd 集群 + +- 在部署机(master120)上操作下面步骤 +- 创建 etcd 部署目录 + ```bash + mkdir /home/deploy/etcd/{bin,cfg,ssl} -p + mkdir /home/deploy/ssl/etcd -p + ``` + +- 安装 cfssl 工具 + ```bash + curl -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 + curl -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 + curl -o /usr/local/bin/cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 + chmod 0755 /usr/local/bin/cfssl* + ``` + +- 创建 ca-config.json 文件 + ```bash + cat > /home/deploy/ssl/etcd/ca-config.json <<< ' + { + "signing": { + "default": { + "expiry": "87600h" + }, + "profiles": { + "etcd": { + "expiry": "87600h", + "usages": [ + "signing", + "key encipherment", + "server auth", + "client auth" + ] + } + } + } + } + ' + ``` + +- 创建 ca-csr.json 文件 + ```bash + cat > /home/deploy/ssl/etcd/ca-csr.json <<< ' + { + "CN": "etcd", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "CN", + "L": "Beijing", + "ST": "Beijing" + } + ] + } + ' + ``` + +- 创建 server-csr.json 文件 + ```bash + # 注意修改 "10.0.4.*" 和 "10.10.10.*" 为自己环境地址 + cat > /home/deploy/ssl/etcd/server-csr.json <<< ' + { + "CN": "etcd", + "hosts": [ + "10.0.4.121", + "10.0.4.122", + "10.0.4.123", + "10.10.10.121", + "10.10.10.122", + "10.10.10.123", + "127.0.0.1" + ], + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "CN", + "L": "BeiJing", + "ST": "BeiJing" + } + ] + } + ' + ``` + +- 生成证书 + ```bash + cd /home/deploy/ssl/etcd/ + # 生成 ca.pem ca-key.pem + cfssl gencert -initca ca-csr.json | cfssljson -bare ca - + # 生成 server.pem server-key.pem + cfssl gencert \ + -ca=ca.pem \ + -ca-key=ca-key.pem \ + -config=ca-config.json \ + -profile=etcd \ + server-csr.json | cfssljson -bare server + # 复制证书到部署目录 + scp *.pem /home/deploy/etcd/ssl/ + ``` + +- 下载 etcd 二进制包 + ```bash + cd /home/deploy/ + curl -L -O https://github.com/etcd-io/etcd/releases/download/v3.3.18/etcd-v3.3.18-linux-amd64.tar.gz + tar zxf etcd-v3.3.18-linux-amd64.tar.gz + scp etcd-v3.3.18-linux-amd64/{etcd,etcdctl} etcd/bin/ + ``` + +- 创建 etcd 配置文件 + ```bash + # 这里的 etcd 虚拟机都有两个网卡,一个用于提供服务,另一个用于集群通信 + # 注意修改 "10.0.4.*" 和 "10.10.10.*" 为自己环境地址 + cat > /home/deploy/etcd/cfg/etcd <<<' + # [Member] + ETCD_NAME="etcdXXX" + ETCD_DATA_DIR="/var/lib/etcd/default.etcd" + ETCD_LISTEN_PEER_URLS="https://10.10.10.XXX:2380" + ETCD_LISTEN_CLIENT_URLS="https://10.0.4.XXX:2379" + + # [Clustering] + ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.10.10.XXX:2380" + ETCD_ADVERTISE_CLIENT_URLS="https://10.0.4.XXX:2379" + ETCD_INITIAL_CLUSTER="etcd121=https://10.10.10.121:2380,etcd122=https://10.10.10.122:2380,etcd123=https://10.10.10.123:2380" + ETCD_INITIAL_CLUSTER_TOKEN="fucking-etcd-cluster" + ETCD_INITIAL_CLUSTER_STATE="new" + ' + ``` + +- 创建 etcd.service + ```bash + cat > /home/deploy/etcd.service <<<' + [Unit] + Description=Etcd Server + After=network.target + After=network-online.target + Wants=network-online.target + + [Service] + Type=notify + EnvironmentFile=/opt/etcd/cfg/etcd + ExecStart=/opt/etcd/bin/etcd \ + --name=${ETCD_NAME} \ + --data-dir=${ETCD_DATA_DIR} \ + --listen-peer-urls=${ETCD_LISTEN_PEER_URLS} \ + --listen-client-urls=${ETCD_LISTEN_CLIENT_URLS},http://127.0.0.1:2379 \ + --advertise-client-urls=${ETCD_ADVERTISE_CLIENT_URLS} \ + --initial-advertise-peer-urls=${ETCD_INITIAL_ADVERTISE_PEER_URLS} \ + --initial-cluster=${ETCD_INITIAL_CLUSTER} \ + --initial-cluster-token=${ETCD_INITIAL_CLUSTER_TOKEN} \ + --initial-cluster-state=${ETCD_INITIAL_CLUSTER_STATE} \ + --cert-file=/opt/etcd/ssl/server.pem \ + --key-file=/opt/etcd/ssl/server-key.pem \ + --peer-cert-file=/opt/etcd/ssl/server.pem \ + --peer-key-file=/opt/etcd/ssl/server-key.pem \ + --trusted-ca-file=/opt/etcd/ssl/ca.pem \ + --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem + Restart=on-failure + LimitNOFILE=65536 + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 部署到远程三个 etcd 节点(etcd121、etcd122、etcd123) + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + cd /home/deploy + for id in $(seq 121 123); do + ip="10.0.4.$id" + scp -r etcd $ip:/opt/ + ssh $ip "sed -i 's/XXX/$id/g' /opt/etcd/cfg/etcd" + scp etcd.service $ip:/usr/lib/systemd/system/ + systemctl -H $ip daemon-reload + systemctl -H $ip enable etcd + done + ``` + +- 启动三个 etcd 节点的 etcd 服务 + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + for ip in $(seq -f'10.0.4.%g' 121 123); do + systemctl -H $ip start etcd + done + ``` + +- 查看 etcd 集群状态 + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + cd /home/deploy/etcd/ssl + ../bin/etcdctl \ + --ca-file=ca.pem \ + --cert-file=server.pem \ + --key-file=server-key.pem \ + --endpoints="https://10.0.4.121:2379" \ + cluster-health + ``` + +# 安装 Docker + +- 在每个 k8s node 服务器(node124、node125、node126)上操作下面步骤 +- 安装 docker-ce,[参考这里](https://my.oschina.net/colben/blog/1505141#h1_2) + +# 部署 Flannel 网络 + +- 在部署机(master120)上操作下面步骤 +- 创建 flannel 部署目录 + ```bash + cd /home/deploy + mkdir flannel/{bin,cfg,ssl} -p + # 复制 etcd 证书到 flannel 证书目录下 + rm -rf flannel/ssl/etcd && scp -r etcd/ssl flannel/ssl/etcd + ``` + +- 连接 etcd,写入预定义的子网段 + ```bash + # 这里的预定义字段是 "172.17.0.0/16",推荐用这个,与 docker 原生网段一致 + # 注意修改 "10.0.4.*" 为自己环境地址 + cd /home/deploy/etcd/ssl + ../bin/etcdctl \ + --ca-file=ca.pem \ + --cert-file=server.pem \ + --key-file=server-key.pem \ + --endpoints="https://10.0.4.122:2379" \ + set /coreos.com/network/config '{"Network": "172.17.0.0/16", "Backend": {"Type": "vxlan"}}' + ``` + +- 获取 flannel 二进制包 + ```bash + cd /home/deploy + curl -L -O https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz + tar zxf flannel-v0.11.0-linux-amd64.tar.gz + scp flanneld mk-docker-opts.sh flannel/bin/ + ``` + +- 创建 flannel 配置文件 + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + cat > /home/deploy/flannel/cfg/flanneld <<< ' + FLANNEL_OPTIONS=" \ + --etcd-endpoints=https://10.0.4.121:2379,https://10.0.4.122:2379,https://10.0.4.123:2379 \ + -etcd-cafile=/opt/kubernetes/ssl/etcd/ca.pem \ + -etcd-certfile=/opt/kubernetes/ssl/etcd/server.pem \ + -etcd-keyfile=/opt/kubernetes/ssl/etcd/server-key.pem \ + " + ' + ``` + +- 创建 flanneld.service + ```bash + cat > /home/deploy/flanneld.service <<< ' + [Unit] + Description=Flanneld overlay address etcd agent + After=network-online.target network.target + Before=docker.service + + [Service] + Type=notify + EnvironmentFile=/opt/kubernetes/cfg/flanneld + ExecStart=/opt/kubernetes/bin/flanneld --ip-masq $FLANNEL_OPTIONS + ExecStartPost=/opt/kubernetes/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/subnet.env + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 修改 docker.service,用于从指定的子网段启动 docker + ```bash + # 关键在 "EnvironmentFile=/run/flannel/subnet.env" + # 该文件就是 flanneld 服务生成的 docker 参数 + # 这里仅作记录,具体实现已移到下一步的部署脚本中 + ``` + +- 部署到远程三个 k8s node 节点(node124、node125、node126) + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + cd /home/deploy + for ip in $(seq -f'10.0.4.%g' 124 126); do + systemctl -H $ip stop docker + ssh $ip "mkdir /opt/kubernetes" + scp -r flannel/* $ip:/opt/kubernetes/ + scp flanneld.service $ip:/usr/lib/systemd/system/ + ssh $ip 'sed -i \ + -e "/^Type/aEnvironmentFile=/run/flannel/subnet.env" \ + -e "/^ExecStart/s/$/\$DOCKER_NETWORK_OPTIONS/" \ + /usr/lib/systemd/system/docker.service \ + ' + systemctl -H $ip daemon-reload + systemctl -H $ip enable flanneld + done + ``` + +- 启动三个 k8s node 节点的 flanneld 和 docker 服务 + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + for ip in $(seq -f'10.0.4.%g' 124 126); do + systemctl -H $ip start flanneld + systemctl -H $ip start docker + done + ``` + +- 启动完成后,不同节点的 docker0 网卡 ip 互通 + +# 部署 k8s master 节点 + +- 部署前确保前面的 etcd 集群、flannel 网络和 docker 都是正常的 +- 在部署机(master120,即当前节点)上操作下面步骤 +- 创建 master 部署目录 + ```bash + cd /home/deploy + mkdir master/{bin,cfg,ssl} -p + mkdir ssl/master -p + # 复制 etcd 证书到 master 证书目录下 + rm -rf master/ssl/etcd && scp -r etcd/ssl master/ssl/etcd + ``` + +- 创建 ca-config.json 文件 + ```bash + cat > /home/deploy/ssl/master/ca-config.json <<< ' + { + "signing": { + "default": { + "expiry": "87600h" + }, + "profiles": { + "kubernetes": { + "expiry": "87600h", + "usages": [ + "signing", + "key encipherment", + "server auth", + "client auth" + ] + } + } + } + } + ' + ``` + +- 创建 ca-csr.json 文件 + ```bash + cat > /home/deploy/ssl/master/ca-csr.json <<< ' + { + "CN": "kubernetes", + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "CN", + "L": "Beijing", + "ST": "Beijing", + "O": "k8s", + "OU": "System" + } + ] + } + ' + ``` + +- 创建 kube-apiserver-csr.json 文件 + ```bash + # 这里的 10.10.9.1 是 kubernetes service 的集群地址 + # 该地址默认是下文中的 service-cluster-ip-range 网段的第一个 ip + # dns 组件会用到 + # 注意修改 "10.0.4.*" 为自己环境地址 + cat > /home/deploy/ssl/master/kube-apiserver-csr.json <<< ' + { + "CN": "kubernetes", + "hosts": [ + "127.0.0.1", + "10.0.4.120", + "10.10.9.1", + "kubernetes", + "kubernetes.default", + "kubernetes.default.svc", + "kubernetes.default.svc.cluster", + "kubernetes.default.svc.cluster.local" + ], + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "CN", + "L": "BeiJing", + "ST": "BeiJing", + "O": "k8s", + "OU": "System" + } + ] + } + ' + ``` + +- 创建 kube-proxy-csr.json 文件 + ```bash + cat > /home/deploy/ssl/master/kube-proxy-csr.json <<< ' + { + "CN": "system:kube-proxy", + "hosts": [], + "key": { + "algo": "rsa", + "size": 2048 + }, + "names": [ + { + "C": "CN", + "L": "BeiJing", + "ST": "BeiJing", + "O": "k8s", + "OU": "System" + } + ] + } + ' + ``` + +- 生成证书 + ```bash + cd /home/deploy/ssl/master + cfssl gencert -initca ca-csr.json | cfssljson -bare ca - + cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-apiserver-csr.json | cfssljson -bare kube-apiserver + cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy + # 复制证书到部署目录 + scp *.pem /home/deploy/master/ssl/ + ``` + +- 获取 kubernetes 二进制包 + ```bash + cd /home/deploy + curl -L -O https://dl.k8s.io/v1.17.2/kubernetes-server-linux-amd64.tar.gz + tar zxf kubernetes-server-linux-amd64.tar.gz + cd kubernetes/server/bin + scp kube-apiserver kube-scheduler kube-controller-manager kubectl /home/deploy/master/bin/ + ``` + +- 创建 token 文件 + ```bash + # 第一个字符串随机写的,看心情 …… + echo '1111222233334444aaaabbbbccccdddd,kubelet-bootstrap,10001,"system:kubelet-bootstrap"' > /home/deploy/master/cfg/token.csv + ``` + +- 创建 kube-apiserver 配置文件 + ```bash + # 这里的 service-cluster-ip-range 是 k8s service 地址区间,提供一个与现有网络不通的网段 + # 注意修改 "10.0.4.*" 为自己环境地址 + cat > /home/deploy/master/cfg/kube-apiserver <<< ' + KUBE_APISERVER_OPTS=" \ + --logtostderr=true \ + --v=4 \ + --etcd-servers=https://10.0.4.121:2379,https://10.0.4.122:2379,https://10.0.4.123:2379 \ + --bind-address=10.0.4.120 \ + --secure-port=6443 \ + --advertise-address=10.0.4.120 \ + --allow-privileged=true \ + --service-cluster-ip-range=10.10.9.0/24 \ + --enable-admission-plugins=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota,NodeRestriction \ + --authorization-mode=RBAC,Node \ + --enable-bootstrap-token-auth \ + --token-auth-file=/opt/kubernetes/cfg/token.csv \ + --service-node-port-range=30000-50000 \ + --tls-cert-file=/opt/kubernetes/ssl/kube-apiserver.pem \ + --tls-private-key-file=/opt/kubernetes/ssl/kube-apiserver-key.pem \ + --client-ca-file=/opt/kubernetes/ssl/ca.pem \ + --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \ + --etcd-cafile=/opt/kubernetes/ssl/etcd/ca.pem \ + --etcd-certfile=/opt/kubernetes/ssl/etcd/server.pem \ + --etcd-keyfile=/opt/kubernetes/ssl/etcd/server-key.pem \ + " + ' + ``` + +- 创建 kube-apiserver.service 文件 + ```bash + cat > /home/deploy/kube-apiserver.service <<< ' + [Unit] + Description=Kubernetes API Server + Documentation=https://github.com/kubernetes/kubernetes + + [Service] + EnvironmentFile=-/opt/kubernetes/cfg/kube-apiserver + ExecStart=/opt/kubernetes/bin/kube-apiserver $KUBE_APISERVER_OPTS + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 创建 kube-scheduler 配置文件 + ```bash + cat > /home/deploy/master/cfg/kube-scheduler <<< ' + KUBE_SCHEDULER_OPTS=" \ + --logtostderr=true \ + --v=4 \ + --master=127.0.0.1:8080 \ + --leader-elect \ + " + ' + ``` + +- 创建 kube-scheduler.service + ```bash + cat > /home/deploy/kube-scheduler.service <<< ' + [Unit] + Description=Kubernetes Scheduler + Documentation=https://github.com/kubernetes/kubernetes + + [Service] + EnvironmentFile=-/opt/kubernetes/cfg/kube-scheduler + ExecStart=/opt/kubernetes/bin/kube-scheduler $KUBE_SCHEDULER_OPTS + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 创建 kube-controller-mananger 配置文件 + ```bash + # 注意这里设置了 cluster-name 为 "my_k8s_cluster" + # 这个名字在后面会用到 + # 这里的 service-cluster-ip-range 是 k8s service 地址区间,与之前配置的网段相同 + cat > /home/deploy/master/cfg/kube-controller-manager <<< ' + KUBE_CONTROLLER_MANAGER_OPTS=" \ + --logtostderr=true \ + --v=4 \ + --master=127.0.0.1:8080 \ + --leader-elect=true \ + --address=127.0.0.1 \ + --service-cluster-ip-range=10.10.9.0/24 \ + --cluster-name=my_k8s_cluster \ + --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \ + --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem \ + --root-ca-file=/opt/kubernetes/ssl/ca.pem \ + --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \ + " + ' + ``` + +- 创建 kube-controller-manager.service + ```bash + cat > /home/deploy/kube-controller-manager.service <<< ' + [Unit] + Description=Kubernetes Controller Manager + Documentation=https://github.com/kubernetes/kubernetes + + [Service] + EnvironmentFile=-/opt/kubernetes/cfg/kube-controller-manager + ExecStart=/opt/kubernetes/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 部署到执行目录 + ```bash + cd /home/deploy + mkdir /opt/kubernetes + scp -r master/* /opt/kubernetes/ + scp kube-apiserver.service kube-scheduler.service kube-controller-manager.service /usr/lib/systemd/system/ + systemctl daemon-reload + systemctl enable kube-apiserver kube-scheduler kube-controller-manager + ln -sf /opt/kubernetes/bin/kubectl /usr/local/bin/ + ``` + +- 启动 k8s master 各组件 + ```bash + systemctl start kube-apiserver + systemctl start kube-scheduler + systemctl start kube-controller-manager + ``` + +- 查看集群各组件状态 + ```bash + kubectl get cs + ``` + +# 部署 k8s node 节点 + +- 在部署机(master120)上操作下面步骤 +- 创建 node 部署目录 + ```bash + cd /home/deploy + mkdir node/{bin,cfg,ssl} -p + scp kubernetes/server/bin/{kubelet,kube-proxy} node/bin/ + ``` + +- 将kubelet-bootstrap用户绑定到系统集群角色 + ```bash + kubectl create clusterrolebinding kubelet-bootstrap \ + --clusterrole=system:node-bootstrapper \ + --user=kubelet-bootstrap + ``` + +- 生成 bootstrap.kubeconfig 文件 + ```bash + export BOOTSTRAP_TOKEN=1111222233334444aaaabbbbccccdddd + export KUBE_APISERVER="https://10.0.4.120:6443" + cd /home/deploy/master/ssl + # 设置集群参数,这里的指定的集群就是前面设置的 "my_k8s_cluster" + kubectl config set-cluster my_k8s_cluster \ + --certificate-authority=ca.pem \ + --embed-certs=true \ + --server=${KUBE_APISERVER} \ + --kubeconfig=/home/deploy/node/cfg/bootstrap.kubeconfig + # 设置客户端认证参数 + kubectl config set-credentials kubelet-bootstrap \ + --token=${BOOTSTRAP_TOKEN} \ + --kubeconfig=/home/deploy/node/cfg/bootstrap.kubeconfig + # 设置上下文参数 + kubectl config set-context default \ + --cluster=my_k8s_cluster \ + --user=kubelet-bootstrap \ + --kubeconfig=/home/deploy/node/cfg/bootstrap.kubeconfig + # 设置默认上下文 + kubectl config use-context default --kubeconfig=/home/deploy/node/cfg/bootstrap.kubeconfig + ``` + +- 生成 kube-proxy.kubeconfig 文件 + ```bash + export KUBE_APISERVER="https://10.0.4.120:6443" + cd /home/deploy/master/ssl + # 设置集群参数,这里的指定的集群就是前面设置的 "my_k8s_cluster" + kubectl config set-cluster my_k8s_cluster \ + --certificate-authority=ca.pem \ + --embed-certs=true \ + --server=${KUBE_APISERVER} \ + --kubeconfig=/home/deploy/node/cfg/kube-proxy.kubeconfig + kubectl config set-credentials kube-proxy \ + --client-certificate=kube-proxy.pem \ + --client-key=kube-proxy-key.pem \ + --embed-certs=true \ + --kubeconfig=/home/deploy/node/cfg/kube-proxy.kubeconfig + kubectl config set-context default \ + --cluster=my_k8s_cluster \ + --user=kube-proxy \ + --kubeconfig=/home/deploy/node/cfg/kube-proxy.kubeconfig + kubectl config use-context default --kubeconfig=/home/deploy/node/cfg/kube-proxy.kubeconfig + ``` + +- 创建 kubelet 配置文件 + ```bash + # --kubeconfig 指定 kubeconfig 文件位置,会自动生成 + # --cert-dir 颁发证书待存放位置 + # 注意修改 "10.0.4.*" 为自己环境地址 + # 这里保留 "XXX",后面部署命令会统一替换 + cat > /home/deploy/node/cfg/kubelet <<< ' + KUBELET_OPTS=" \ + --logtostderr=true \ + --v=4 \ + --hostname-override=10.0.4.XXX \ + --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \ + --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \ + --config=/opt/kubernetes/cfg/kubelet.config \ + --cert-dir=/opt/kubernetes/ssl \ + --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 \ + " + ' + # registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 该镜像可以提前导入本地局域网中的私有 docker 仓库中 + ``` + +- 创建 kubelet.config 配置文件 + ```bash + # 这里的 clusterDNS 是 DNS service 的集群地址,这里分配 10.10.9.2 + # 注意修改 "10.0.4.*" 为自己环境地址 + # 这里保留 "XXX",后面部署命令会统一替换 + cat > /home/deploy/node/cfg/kubelet.config <<< ' + kind: KubeletConfiguration + apiVersion: kubelet.config.k8s.io/v1beta1 + address: 10.0.4.XXX + port: 10250 + readOnlyPort: 10255 + cgroupDriver: cgroupfs + clusterDNS: ["10.10.9.2"] + clusterDomain: cluster.local. + failSwapOn: false + authentication: + anonymous: + enabled: true + ' + ``` + +- 创建 kubelet.service + ```bash + cat > /home/deploy/kubelet.service <<< ' + [Unit] + Description=Kubernetes Kubelet + After=docker.service + Requires=docker.service + + [Service] + EnvironmentFile=/opt/kubernetes/cfg/kubelet + ExecStart=/opt/kubernetes/bin/kubelet $KUBELET_OPTS + Restart=on-failure + KillMode=process + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 创建 kube-proxy 配置文件 + ```bash + # 这里的 cluster-cidr 是 pod ip 地址区间 + # 注意修改 "10.0.4.*" 为自己环境地址 + # 这里保留 "XXX",后面部署命令会统一替换 + cat > /home/deploy/node/cfg/kube-proxy <<< ' + KUBE_PROXY_OPTS=" \ + --logtostderr=true \ + --v=4 \ + --hostname-override=10.0.4.XXX \ + --cluster-cidr=172.16.0.0/16 \ + --kubeconfig=/opt/kubernetes/cfg/kube-proxy.kubeconfig \ + " + ' + ``` + +- 创建 kube-proxy.service + ```bash + cat > /home/deploy/kube-proxy.service <<< ' + [Unit] + Description=Kubernetes Proxy + After=network.target + + [Service] + EnvironmentFile=-/opt/kubernetes/cfg/kube-proxy + ExecStart=/opt/kubernetes/bin/kube-proxy $KUBE_PROXY_OPTS + Restart=on-failure + + [Install] + WantedBy=multi-user.target + ' + ``` + +- 部署到远程三个 k8s node 节点(node124、node125、node126) + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + cd /home/deploy + for id in $(seq 124 126); do + ip="10.0.4.$id" + scp -r node/* $ip:/opt/kubernetes/ + ssh $ip "sed -i 's/XXX/$id/g' /opt/kubernetes/cfg/kubelet" + ssh $ip "sed -i 's/XXX/$id/g' /opt/kubernetes/cfg/kubelet.config" + ssh $ip "sed -i 's/XXX/$id/g' /opt/kubernetes/cfg/kube-proxy" + scp kubelet.service kube-proxy.service $ip:/usr/lib/systemd/system/ + systemctl -H $ip daemon-reload + systemctl -H $ip enable kubelet kube-proxy + done + ``` + +- 启动三个 k8s node 节点的 kubelet 和 kube-proxy 服务 + ```bash + # 注意修改 "10.0.4.*" 为自己环境地址 + for ip in $(seq -f'10.0.4.%g' 124 126); do + systemctl -H $ip start kubelet + systemctl -H $ip start kube-proxy + done + ``` + +- 审批 node 加入集群 + ```bash + kubectl get csr + # "XXXX" 上一命令输出的 NAME 列 + kubectl certificate approve XXXX + kubectl get node + ``` + +- 查看集群状态 + ```bash + kubectl get node + kubectl get cs + ``` + +# 部署其他组件 + +- [coreDNS](https://colben.cn/post/k8s-coredns/) + +# 运行一个测试示例 + +- 暂无 + diff --git a/content/post/kafka-install.md b/content/post/kafka-install.md new file mode 100644 index 0000000..7c45c31 --- /dev/null +++ b/content/post/kafka-install.md @@ -0,0 +1,104 @@ +--- +title: "CentOS7 安装 Kafka 集群" +date: 2019-10-30T01:22:25+08:00 +lastmod: 2019-10-30T01:22:25+08:00 +keywords: [] +tags: ["kafka", "centos"] +categories: ["MQ"] +--- + +# 环境 + +主机名 | IP | 操作系统 | kafka 版本 +---- | ---- | ---- | ---- +kafka224 | 192.168.1.224 | CentOS7.5 | 2.12 +kafka225 | 192.168.1.225 | CentOS7.5 | 2.12 +kafka226 | 192.168.1.226 | CentOS7.5 | 2.12 + +- 下载 [kafka_2.12-2.1.0.tgz](http://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.1.0/kafka_2.12-2.1.0.tgz) +- zookeeper 连接: 192.168.1.221:2181,192.168.1.222:2181,192.168.1.223:2181 + +# 各节点初始配置 + +- 关闭 selinux、防火墙 +- 部署 java 运行环境 +- 创建数据目录 + ``` + mkdir -p /var/lib/kafka + ``` +- 创建日志目录 + ``` + mkdir -p /var/log/kafka + ``` + +# 部署 kafka + +- 登陆 kafka224,下载 kafka,解压至 /opt/ 下 +- 修改日志目录 + ``` + cd /opt/kafka/ + rm -rf logs && ln -s /var/log/kafka logs + ``` +- 修改 /opt/kafka/config/server.properties + ``` + broker.id=224 + listeners=PLAINTEXT://192.168.1.224:9092 + log.dirs=/var/lib/kafka + zookeeper.connect=192.168.1.221:2181,192.168.1.222:2181,192.168.1.223:2181 + ``` +- 启动脚本(/opt/kafka/bin/kafka-server-start.sh)太繁琐,我精简了下 + ```bash + #!/bin/bash + base_dir=$(dirname $0) + if [ "x$KAFKA_LOG4J_OPTS" = "x" ]; then + export KAFKA_LOG4J_OPTS="-Dlog4j.configuration=file:$base_dir/../config/log4j.properties" + fi + if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then + # jvm 堆内存根据实际情况修改 + export KAFKA_HEAP_OPTS="-Xmx2G -Xms2G" + # 开启 jmx + export JMX_PORT=1099 + fi + EXTRA_ARGS=${EXTRA_ARGS-'-name kafkaServer -loggc'} + EXTRA_ARGS="-daemon "$EXTRA_ARGS + exec $base_dir/kafka-run-class.sh $EXTRA_ARGS kafka.Kafka "$base_dir/../config/server.properties" + ``` +- 打包 kafka 目录,复制到 kafka225 和 kafka226 上,并修改 server.properties + ``` + # kafka225 + broker.id=225 + listeners=PLAINTEXT://192.168.1.225:9092 + # kafka226 + broker.id=226 + listeners=PLAINTEXT://192.168.1.226:9092 + ``` + +# 启动集群(两种启动方式) + +- 直接启动二进制 + - 在每个节点上启动 kafka 服务 + ```bash + /opt/kafka/bin/kafka-server-start.sh + ``` +- systemd 启动 + - 创建 /usr/lib/systemd/system/kafka.service + ``` + [Unit] + Description=Kafka + Requires=network.service + After=network.service + [Service] + Environment=JAVA_HOME=/opt/jre + ExecStart=/opt/kafka/bin/kafka-server-start.sh + ExecStop=/opt/kafka/bin/kafka-server-stop.sh + Type=forking + KillMode=none + [Install] + WantedBy=multi-user.target + ``` + - 启动 kafka 服务 + ```bash + systemctl daemon-reload + systemctl start kafka + ``` + diff --git a/content/post/keepalived-install.md b/content/post/keepalived-install.md new file mode 100644 index 0000000..abe0e16 --- /dev/null +++ b/content/post/keepalived-install.md @@ -0,0 +1,195 @@ +--- +title: "Keepalived 基础" +date: 2019-10-30T11:26:18+08:00 +lastmod: 2019-10-30T11:26:18+08:00 +tags: ["keepalived"] +categories: ["ha/lb"] +--- + +# VRRP 简介 +- VRRP通过一种竞选协议动态地将路由任务交给LAN中**虚拟路由器**中的某台**VRRP路由器** +- VRRP路由器是一台实现了VRRP协议(运行VRRPD程序)的物理路由器 +- 虚拟路由器是由多台VRRP物理路由器组成的逻辑路由器,对外看起来就像一台路由器 +- 虚拟路由器中,只有一台MASTER物理路由器工作,其他都是BACKUP +- MASTER路由器一直发送VRRP广播,MASTER不可用后(BACKUP收不到广播),其余BACKUP会根据优先级竞选出一台MASTER +- MASTER拥有虚拟路由器IP地址及其它路由配置 + +# 全局配置 +- 全局定义 + ``` + global_defs { + notification_email { + admin@exammple.com #keepalived发生事件时通知该email + ... + } + notification_email_from admin@example.com + smtp_server 127.0.0.1 #smtp服务器 + smtp_connect_timeout 30 + router_id my_hostname #机器标识 + default_interface eth0 #设置静态地址默认绑定的端口,默认是eth0 + vrrp_mcast_group4 #VRRP 的组播IPV4地址,默认224.0.0.18 + vrrp_mcast_group6 #VRRP 的组播IPV4地址,默认ff02::12 + vrrp_version 2|3 #设置默认的VRRP版本,默认是2 + script_user [groupname] #设置运行脚本默认用户和组,如果没有指定,则默认用户为keepalived_script(需要该用户存在),否则为root用户,默认groupname同username + enable_script_security #如果脚本路径的任一部分对于非root用户来说,都具有可写权限,则不会以root身份运行脚本 + } + ``` +- 静态地址和路由,不随 vrrpd instance 的开/关变化 + ``` + static_ipaddress { + $ip/$mask dev $interface #ip命令规则 + ... + } + static_routes { + $dest_ip/$dest_mask via $dest_gateway dev $interface #ip命令规则 + ... + } + ``` +- 一般服务器都配置了网络信息,所以通常无需配置静态地址和路由,如果不指定 dev,则使用 default_interface + +# VRRPD 配置 +- VRRP检查脚本(vrrp_script) + ``` + vrrp_script { + scrip "/path/to/script-file" #可执行的脚本的绝对路径 + interval #脚本执行的间隔,单位是秒,默认为1s + timeout #指定在多少秒后,脚本被认为执行失败 + weight <-254 --- 254> #调整优先级,默认为2 + #如果脚本执行成功(退出状态码为0),weight大于0,则priority增加 + #如果脚本执行失败(退出状态码为非0),weight小于0,则priority减少 + #其他情况下,priority不变 + rise #执行成功多少次才认为是成功 + fall #执行失败多少次才认为失败 + user [GROUPNAME] #运行脚本的用户和组 + init_fail #假设脚本初始状态是失败状态 + } + ``` +- VRRP同步组(sync group) + ``` + vrrp_sync_group VG_1 { + group { + inside_network #实例名 + ... + } + notify_master /path/to/master.sh #切换到master时执行该脚本 + notify_backup /path/to/backup.sh #切换到backup时执行该脚本 + notify_fault /path/to/fault.sh #出错时执行该脚本 + notify /path/to/notify.sh #该脚本会在notify_*脚本后执行,默认3个参数:$1(GROUP|INSTANCE),$2(group或instance名字),$3(MASTER|BACKUP|FAULT) + smtp_alert #发送邮件通知 + } + ``` +- VRRP实例(instance)配置 + ``` + vrrp_instance inside_network { + state MASTER #初始状态 + interface eth0 #绑定的网卡 + dont_track_primary #忽略VRRP的interface错误(默认不设置) + track_interface { #这里的任一网卡出现问题,都会进入FAULT状态 + eth0 + eth1 weight <-254 - 254> + ... + } + track_script { #这里的任一脚本返回码非0,都会进入FAULT状态 + + weight <-254-254> + ... + } + mcast_src_ip #多播包发送源地址,默认网卡当前ip + garp_master_delay 10 #切换到MASTER后,延迟arp请求 + virtual_router_id 1 #VRID标记(0..255) + priority 100 #高优先级竞选为MASTER,MASTER高于BACKUP至少50 + advert_int 1 #检查间隔,默认1秒 + authentication { + auth_type PASS #密码认证 + auth_pass 1111 + } + virtual_ipaddress { #漂移地址,符合ip命令规则 + $vip/$vmask dev $interface + ... + } + virtual_routes { #随地址一同漂移的路由,符合ip命令规则 + $dest_ip/$dest_mask via $dest_gateway dev $interface + ... + } + nopreempt #BACKUP配置,且优先级比其他高 + preempt_delay 300 #抢占延迟,默认5分钟 + debug #Debug级别 + lvs_sync_daemon_interface #lvn syncd绑定的网卡 + } + ``` + +# LVS配置(不涉及lvs时无需下面配置) +- 虚拟主机组 + ``` + virtual_server_group { + #VIP VPORT + + + ... + fwmark + } + ``` +- 虚拟主机3中配置 + ``` + #virtual_server IP port + #virtual_server fwmark int + #virtual_server group string + virtual_server 192.168.1.229 80 { #配置一个virtual server + delay_loop 3 #每隔3秒检查一次RealServer是否可用 + lb_algo rr|wrr|lc|wlc|lblc|sh|dh #LVS调度算法 + lb_kind NAT|DR|TUN #LVS集群模式 + persistence_timeout 120 #同一个客户端IP在120秒内分到同一个RealServer + persistence_granularity #会话保持粒度,默认255.255.255.255,即根据每个客户端IP做会话保持 + protocol TCP #协议 + ha_suspend + virtualhost #HTTP_GET健康检查时使用的HOST} + sorry_server #所有RS失效后连接该备用机 + real_server { + weight 1 #默认1,0失效 + inhibit_on_failure #健康检查失败后将weight置0,不从IPVS中删除 + notify_up #检测到service up后执行的脚本 + notify_down #检测到service down后执行的脚本 + #检查方式,HTTP_GET|SSL_GET|TCP_CHECK + HTTP_GET|SSL_GET { + url { + path / + digest #SSL检查返回的摘要信息 + status_code 200 #HTTP检查返回的状态码 + } + connect_port 80 #检查端口 + bindto #使用该地址发送健康检查 + connect_timeout #连接超时时间 + nb_get_retry 3 #重连次数 + delay_before_retry 2 #重连间隔(秒) + } + TCP_CHECK { + connect_port 80 + bindto + connect_timeout 4 + } + } + } + ``` + +# 配置日志文件 +- 修改服务启动参数 + ```bash + sed -i '/^KEEPALIVED_OPTIONS/d' /etc/sysconfig/keepalived + echo 'KEEPALIVED_OPTIONS="-D -d -S 2"' >> /etc/sysconfig/keepalived + ``` +- 修改 rsyslog 配置文件 + ```bash + echo "local2.* /var/log/keepalived.log" >> /etc/rsyslog.conf + ``` +- 重启 rsyslog 服务 + ```bash + systemctl restart rsyslog + ``` +- 重启 keepalived 服务 + ```bash + systemctl restart keepalived + ``` + +# 其他参考 +- [详细解释](https://blog.csdn.net/wos1002/article/details/56483325) + diff --git a/content/post/keepalived.md b/content/post/keepalived.md new file mode 100644 index 0000000..bb71ae6 --- /dev/null +++ b/content/post/keepalived.md @@ -0,0 +1,285 @@ +--- +title: "Keepalived 笔记" +date: 2019-10-30T11:22:03+08:00 +lastmod: 2019-10-30T11:22:03+08:00 +tags: ["keepalived", "高可用", "负载均衡"] +categories: ["ha/lb"] +--- + +# 两个 haproxy 不抢占 +- 环境 + - haproxy 服务器 + - haproxy101: 10.1.1.101 + - haproxy102: 10.1.1.102 + - 虚拟地址 + - ip: 10.1.1.100 +- 在全部 haproxy 服务器上安装 keepalived + ```bash + yum install keepalived + ``` +- 在全部 haproxy 服务器上配置 haproxy 和 keepalived 自启动 + ```bash + systemctl enable haproxy + systemctl enable keepalived + ``` +- MASTER/BACKUP 完整配置 + ``` + global_defs { + router_id haproxy101 #BACKUP 这里是 haproxy102 + script_user root + enable_script_security + } + vrrp_script chk_haproxy { + script "/usr/bin/systemctl status haproxy" + interval 2 + weight 0 + fall 2 + rise 2 + } + vrrp_instance VI_1 { + state BACKUP #MASTER 和 BACKUP 这里都是 BACKUP + virtual_router_id 1 + priority 150 #BACKUP 这里是 100 + advert_int 2 + nopreempt #BACKUP 优先级低,需注释此行 + interface eth0 + track_script { + chk_haproxy + } + authentication { + auth_type PASS + auth_pass 1011100 + } + virtual_ipaddress { + 10.1.1.100/24 dev eth0 + } + } + ``` + +# 两个 LVS-DR 调度器不抢占均衡后端 MySQL 和 Ceph 负载 +- 环境 + - keepalived 服务器 + - ka101: 10.1.1.101 + - ka102: 10.1.1.102 + - 虚拟地址 + - ip: 10.1.1.100 + - mysqld 服务器 + - mysql103: 10.1.1.103 + - mysql104: 10.1.1.104 + - mysql105: 10.1.1.105 + - ceph-radosgw 服务器 + - ceph106: 10.1.1.106 + - ceph107: 10.1.1.107 + - ceph108: 10.1.1.108 +- 在全部 keepalived 服务器上安装 keepalived + ```bash + yum install keepalived + ``` +- 在全部 keepalived 服务器上配置 keepalived 自启动 + ```bash + systemctl enable keepalived + ``` +- MASTER/BACKUP 完整配置 + - 全局和实例配置 + ``` + global_defs { + router_id keepalive101 #BACKUP 配置 keepalive102 + } + vrrp_instance V1_1 { + state BACKUP #BACKUP 也配置 BACKUP + interface eth0 + virtual_router_id 1 + priority 150 #BACKUP 配置 100 + advert_int 1 + nopreempt #BACKUP 优先级低,需注释此行 + authentication { + auth_type PASS + auth_pass 1011100 + } + virtual_ipaddress { + 10.1.1.100/24 dev eth0 + } + } + ``` + - LVS 均衡 mysql galera cluser 负载 + ``` + virtual_server 10.1.1.100 3306 { + delay_loop 16 + lb_algo sh + lb_kind DR + protocol TCP + real_server 10.1.1.103 3306 { + weight 1 + TCP_CHECK { + connect_timeout 4 + delay_before_retry 2 + connect_port 3306 + } + } + real_server 10.1.1.104 3306 { + weight 1 + TCP_CHECK { + connect_timeout 4 + delay_before_retry 2 + connect_port 3306 + } + } + real_server 10.1.1.105 3306 { + weight 1 + TCP_CHECK { + connect_timeout 4 + delay_before_retry 2 + connect_port 3306 + } + } + } + ``` + - LVS 均衡 ceph radosgw 负载 + ``` + virtual_server 10.1.1.100 7480 { + delay_loop 16 + lb_algo sh + lb_kind DR + protocol TCP + real_server 10.1.1.106 7480 { + weight 1 + HTTP_GET { + url { + path / + status_code 200 + } + connect_timeout 8 + nb_get_retry 2 + delay_before_retry 2 + connect_port 7480 + } + } + real_server 10.1.1.107 7480 { + weight 1 + HTTP_GET { + url { + path / + status_code 200 + } + connect_timeout 8 + nb_get_retry 2 + delay_before_retry 2 + connect_port 7480 + } + } + real_server 10.1.1.108 7480 { + weight 1 + HTTP_GET { + url { + path / + status_code 200 + } + connect_timeout 8 + nb_get_retry 2 + delay_before_retry 2 + connect_port 7480 + } + } + } + ``` +- 在全部 mysql 和 ceph-radosgw 服务器上配置虚拟 ip + ```bash + echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore + echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce + echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore + echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce + ip addr add 10.1.1.100/32 brd 10.1.1.100 dev lo + ip route add 10.1.1.100 dev lo + ``` + +# 不抢占自动切换两台 Redis 主从状态 +- 环境 + - Redis 服务器 + - redis101: 10.1.1.101 + - redis102: 10.1.1.102 + - 虚拟地址 + - ip: 10.1.1.100 +- 在全部 redis 服务器上安装 keepalived + ```bash + yum install keepalived + ``` +- 在全部 redis 服务器上配置 redis 和 keepalived 自启动 + ```bash + systemctl enable redis + systemctl enable keepalived + ``` +- MASTER/BACKUP 完整配置 + ``` + global_defs { + router_id redis101 #BACKUP 这里是 redis102 + script_user root + enable_script_security + } + vrrp_script chk_redis { + script "/usr/bin/systemctl status redis" + interval 2 + weight 0 + fall 2 + rise 2 + } + vrrp_instance VI_1 { + state BACKUP #MASTER 和 BACKUP 这里都是 BACKUP + virtual_router_id 51 + priority 150 #BACKUP 这里是 100 + advert_int 1 + nopreempt #BACKUP 优先级低,需注释此行 + interface eth0 + notify_master /etc/keepalived/scripts/master.sh + notify_backup /etc/keepalived/scripts/backup.sh + track_script { + chk_redis + } + authentication { + auth_type PASS + auth_pass 123456 + } + virtual_ipaddress { + 10.1.1.100/24 dev eth0 + } + } + ``` +- 在全部 redis 服务器上创建 /etc/keepalived/scripts/master.sh 脚本,内容如下 + ```bash + #!/bin/bash + # + /usr/bin/sed -i '/^slaveof/d' /etc/redis.conf + /usr/bin/systemctl restart redis + ``` +- 在 redis101 上创建 /etc/keepalived/scripts/backup.sh 脚本,内容如下 + ```bash + #!/bin/bash + # + /usr/bin/sed -i '/^slaveof/d' /etc/redis.conf + echo 'slaveof 10.1.1.102 6379' >> /etc/redis.conf + /usr/bin/systemctl restart redis + ``` +- 在 redis102 上创建 /etc/keepalived/scripts/backup.sh 脚本,内容如下 + ```bash + #!/bin/bash + # + /usr/bin/sed -i '/^slaveof/d' /etc/redis.conf + echo 'slaveof 10.1.1.101 6379' >> /etc/redis.conf + /usr/bin/systemctl restart redis + ``` +- 在全部 redis 服务器上赋予脚本可执行权限 + ```bash + chmod 0755 /etc/keepalived/scripts/*.sh + ``` +- 修改 redis 配置 + ``` + requirepass redis_password + maxclients 1000 + maxmemory 4294967296 + maxmemory-policy volatile-lru + ``` +- 在全部 redis 服务器上启动 redis 和 keepalived 服务 + ```bash + systemctl start redis + systemctl start keepalived + ``` + diff --git a/content/post/kickstart-centos7.md b/content/post/kickstart-centos7.md new file mode 100644 index 0000000..2527cff --- /dev/null +++ b/content/post/kickstart-centos7.md @@ -0,0 +1,208 @@ +--- +title: "Kickstart 安装 CentOS7" +date: 2019-10-29T21:00:25+08:00 +lastmod: 2019-10-29T21:00:25+08:00 +keywords: [] +tags: ["kickstart", "centos"] +categories: ["os"] +--- + +# 环境 +- CentOS7.6 +- genisoimage 1.1.11 +- CentOS-7-x86_64-Minimal-1810.iso + +# 复制 iso 内容到本地磁盘 +```bash +mount -o loop CentOS-7-x86_64-Minimal-1810.iso /mnt/ +mkdir -p /home/iso/centos7 +cd /mnt && cp -af * .* /home/iso/centos7/ +``` + +# 创建 isolinux/ks.cfg +- mbr 启动,/home/iso/centos7/isolinux/ks.cfg 内容如下 + ``` + # Install OS instead of upgrade + install + # Reboot after installation + reboot + # System authorization information + auth --enableshadow --passalgo=sha512 + # Use CDROM installation media + cdrom + # Use graphical install + graphical + # Run the Setup Agent on first boot + firstboot --enable + ignoredisk --only-use=sda + # Keyboard layouts + keyboard --vckeymap=cn --xlayouts='cn' + # System language + lang zh_CN.UTF-8 + # Firewall configuration + firewall --disabled + # SELinux configuration + selinux --disabled + + # Network information + #network --bootproto=dhcp --device=eth0 --onboot=off --ipv6=auto --no-activate + #network --hostname=localhost.localdomain + + # Root password(111111) + rootpw --iscrypted $6$kD.hMvv5nCY8a/SM$Gnmb4zspkuyL75BP2Gj.1SGUaWBugXkd/zMFhoDndp9CSi8VP7R5JP7rfWzL4y7fy8crH3ryDT4PFkKCc7/xM. + # System services + services --enabled="chronyd" + # System timezone + timezone Asia/Shanghai --isUtc + # Clear the Master Boot Record + zerombr + # System bootloader configuration + bootloader --location=mbr --boot-drive=sda + # Partition clearing information + clearpart --none --initlabel + # Disk partitioning information + part /boot --fstype="xfs" --ondisk=sda --size=512 + part / --fstype="xfs" --ondisk=sda --grow --size=1 + + %packages + @^minimal + @core + chrony + + %end + + %post + lsblk > /root/lsblk + %end + + %addon com_redhat_kdump --disable --reserve-mb='auto' + + %end + + %anaconda + pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty + pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok + pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty + %end + ``` +- efi 启动,/home/iso/centos7/isolinux/ks.cfg 内容如下 + ``` + # Install OS instead of upgrade + install + # Reboot after installation + reboot + # System authorization information + auth --enableshadow --passalgo=sha512 + # Use CDROM installation media + cdrom + # Use graphical install + graphical + # Run the Setup Agent on first boot + firstboot --enable + ignoredisk --only-use=sda + # Keyboard layouts + keyboard --vckeymap=cn --xlayouts='cn' + # System language + lang zh_CN.UTF-8 + # Firewall configuration + firewall --disabled + # SELinux configuration + selinux --disabled + + # Network information + #network --bootproto=dhcp --device=eth0 --onboot=off --ipv6=auto --no-activate + #network --hostname=localhost.localdomain + + # Root password(111111) + rootpw --iscrypted $6$kD.hMvv5nCY8a/SM$Gnmb4zspkuyL75BP2Gj.1SGUaWBugXkd/zMFhoDndp9CSi8VP7R5JP7rfWzL4y7fy8crH3ryDT4PFkKCc7/xM. + # System services + services --enabled="chronyd" + # System timezone + timezone Asia/Shanghai --isUtc + # Clear the Master Boot Record + zerombr + # System bootloader configuration + bootloader --location=mbr --boot-drive=sda + # Partition clearing information + clearpart --none --initlabel + # Disk partitioning information + part /boot --fstype="xfs" --ondisk=sda --size=512 + part /boot/efi --fstype="xfs" --ondisk=sda --size=512 + part / --fstype="xfs" --ondisk=sda --grow --size=1 + + %packages + @^minimal + @core + chrony + + %end + + %post + %end + + %addon com_redhat_kdump --disable --reserve-mb='auto' + + %end + + %anaconda + pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty + pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok + pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty + %end + ``` + +# 修改启动项文件 +- mbr 启动,只需修改 isolinux/isolinux.cfg + - 删除 "label check" 下的 "menu default" 一行 + - 在 "label linux" 一行上方添加如下内容 + ``` + label auto + menu label ^Auto install CentOS 7 + menu default + kernel vmlinuz + append initrd=initrd.img inst.stage2=hd:LABEL=CentOS7 inst.ks=cdrom:/isolinux/ks.cfg quiet + ``` +- efi 启动,只需修改 EFI/BOOT/grub.cfg + - 修改第一行 + ``` + set default="0" + ``` + - 在 "### BEGIN /etc/grub.d/10_linux ###" 一行下添加如下内容 + ``` + menuentry 'Auto Install CentOS 7' --class fedora --class gnu-linux --class gnu --class os { + linuxefi /images/pxeboot/vmlinuz inst.ks=cdrom:/isolinux/ks.cfg inst.stage2=hd:LABEL=CentOS7 quiet + initrdefi /images/pxeboot/initrd.img + } + ``` + +# 生成 ISO 镜像 +- mbr 启动,执行如下命令 + ```bash + genisoimage -v -R -J -T -V CentOS7 \ + -b isolinux/isolinux.bin \ + -c isolinux/boot.cat \ + -cache-inodes \ + -joliet-long \ + -no-emul-boot \ + -boot-load-size 4 \ + -boot-info-table \ + -o /home/centos7.iso \ + /home/iso/centos7 + ``` +- efi 启动,执行如下命令 + ```bash + genisoimage -v -R -J -T -V CentOS7 \ + -b images/efiboot.img \ + -c isolinux/boot.cat \ + -cache-inodes \ + -joliet-long \ + -no-emul-boot \ + -boot-load-size 4 \ + -boot-info-table \ + -o /home/centos7-efi.iso \ + /home/iso/centos7 + ``` + +# 参考 +- [https://boke.wsfnk.com/archives/382.html](https://boke.wsfnk.com/archives/382.html) + diff --git a/content/post/kubeadm.md b/content/post/kubeadm.md new file mode 100644 index 0000000..91c90c4 --- /dev/null +++ b/content/post/kubeadm.md @@ -0,0 +1,163 @@ +--- +title: "Kubeadm" +date: 2019-10-30T11:19:06+08:00 +lastmod: 2019-10-30T11:19:06+08:00 +tags: ["kubernetes", "k8s", "kubeadm"] +categories: ["container"] +--- + +# kubeadm 安装 kubernetes + + +### 全部服务器配置 + IP 地址 | 主机名 | 操作系统 | 内存 | swap | 硬盘 | Internet | firewalld | selinux | /etc/hosts 增加行 + --------- | -------- | -------- | ---- | ---- | ---- | -------- | --------- | ------- | ------------------ + 10.0.2.80 | master80 | CentOS7 | 4GB | 关闭 | 20GB | 可达 | 关闭 | 关闭 | 127.0.0.1 master80 + 10.0.2.81 | node81 | CentOS7 | 2GB | 关闭 | 20GB | 可达 | 关闭 | 关闭 | 127.0.0.1 node81 + 10.0.2.82 | node82 | CentOS7 | 2GB | 关闭 | 20GB | 可达 | 关闭 | 关闭 | 127.0.0.1 node82 + +- 确认各服务器工作网卡的 MAC 和 UUID 均不相同 + ```bash + #Ovirt 从模板创建虚拟机可忽略此步骤 + #不确定时可参考如下命令 + rm -rf /etc/udev/rules.d/70-* + sed -i -e '/HWADDR/d' -e '/UUID/d' /etc/sysconfig/network-scripts/ifcfg-{eth,enp}* + ``` + +- 配置好 IP,确认各服务器网络互连,且可连互联网 + ```bash + #10.0.2.80 + sed -i 's/10.0.2.127/10.0.2.80/' /etc/sysconfig/network-scripts/ifcfg-eth0 + #10.0.2.81 + sed -i 's/10.0.2.127/10.0.2.81/' /etc/sysconfig/network-scripts/ifcfg-eth0 + #10.0.2.82 + sed -i 's/10.0.2.127/10.0.2.82/' /etc/sysconfig/network-scripts/ifcfg-eth0 + ``` + +- 关闭各服务器的防火墙 + ```bash + systemctl stop firewalld + systemctl disable firewalld + ``` + +- 关闭各服务器的 selinux + ```bash + setenforce 0 + sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + ``` + +- 关闭各服务器的 swap + ```bash + swapoff -a + sed -i '/swap/s/^/#/' /etc/fstab + ``` + +- 安装 ebtables 和 ethtool + ```bash + yum install ebtables ethtool + ``` + +- 配置各服务器的 hostname + ```bash + #10.0.2.80 + hostnamectl set-hostname master80 + echo '127.0.0.1 master80' >> /etc/hosts + #10.0.2.81 + hostnamectl set-hostname node81 + echo '127.0.0.1 node81' >> /etc/hosts + #10.0.2.82 + hostnamectl set-hostname node82 + echo '127.0.0.1 node82' >> /etc/hosts + ``` + +- 重启各服务器 + ```bash + reboot + ``` + + +### 安装 docker +- 各服务器安装 docker + ```bash + yum install docker + ``` + +- 各服务器配置 iptables 转发 + ```bash + cat < /etc/sysctl.d/k8s.conf + net.bridge.bridge-nf-call-ip6tables = 1 + net.bridge.bridge-nf-call-iptables = 1 + EOF + sysctl --system + ``` + +- 各服务器配置 docker 本地仓库(可选) + ```bash + cat < /etc/docker/daemon.json + { + "insecure-registries":["10.0.16.125:5080"] + } + EOF + ``` + +- 各服务器启动 docker + ```bash + systemctl enable docker && systemctl start docker + ``` + + +### 安装 kubernetes +- 各服务器配置 kubernetes yum 源 + ```bash + cat < /etc/yum.repos.d/kubernetes.repo + [kubernetes] + name=Kubernetes + baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 + enabled=1 + gpgcheck=1 + repo_gpgcheck=1 + gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg + https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg + EOF + ``` + +- 各服务器安装 kubeadm + ```bash + yum install -y kubelet kubeadm kubectl + ``` + +- 各服务器启动 kubelet + ```bash + systemctl enable kubelet && systemctl start kubelet + ``` + + +### 在 master80 服务器上安装 kubernetes master 服务组件 +- 初始化 kubeadm + ```bash + kubeadm init --pod-network-cidr=192.168.0.0/16 --token-ttl 0 + mkdir -p /root/.kube + cp -i /etc/kubernetes/admin.conf /root/.kube/config + #记录下输出的最后一行,类似如下 + #kubeadm join --token : --discovery-token-ca-cert-hash sha256: + ``` + +- 安装 Calico 网络插件 + ```bash + kubectl apply -f http://docs.projectcalico.org/v2.4/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml + ``` + + +### 加入其他节点 +- 在 node81 和 node82 服务器上执行以下命令,即 master80 服务器 'kuberadm init' 命令的最后一行输出 + ```bash + kubeadm join --token : --discovery-token-ca-cert-hash sha256: + ``` + +- 在 master80 服务器查看节点和 pod 情况 + ```bash + kubectl get pods --all-namespaces + kubectl get nodes + ``` + + diff --git a/content/post/letsencrypt.md b/content/post/letsencrypt.md new file mode 100644 index 0000000..de56a47 --- /dev/null +++ b/content/post/letsencrypt.md @@ -0,0 +1,84 @@ +--- +title: "Letsencrypt 笔记" +date: 2021-11-06T20:52:00+08:00 +lastmod: 2021-11-06T20:52:00+08:00 +keywords: [] +tags: ["letsencrypt", "certbot", "ssl"] +categories: ["web"] +--- + +# 安装 certbot +- 在 alpine linux 中安装 certbot + ```bash + apk add --no-cache certbot openssl + ``` + +- 注册 + ```bash + certbot register --register-unsafely-without-email --agree-tos + ``` + +# 普通域名证书 +- 申请 ssl 证书,有效期 90 天 + ```bash + certbot certonly -n -d x.x.com --standalone + + # 证书文件生成到 /etc/letsencrypt/live/x.x.com/ 下 + # 参数 -d 可以使用多次来指定多个域名,也可以在一个 -d 参数中使用逗号分隔多个域名 + # 参数 --cert-name 可以指定证书文件的父级目录名字(替换默认的 x.x.com) + ``` + +- 续签 ssl 证书 + ```bash + cerbot renew --force-renewal + ``` + +- 生成 2048 位的交换密钥文件 + ```bash + openssl dhparam -out /etc/letsencrypt/dhparam.pem 2048 + ``` + +# 通配域名证书 +- 申请 ssl 证书,有效期 90 天 + ```bash + certbot certonly --manual -d '*.x.com' \ + --preferred-challenges dns \ + --server https://acme-v02.api.letsencrypt.org/directory + + # 证书文件生成到 /etc/letsencrypt/live/x.com/ 下 + # 参数 --cert-name 可以指定证书文件的父级目录名字(替换默认的 x.com) + ``` + +- 续签 ssl 证书,使用 certonly 子命令指定域名单独更新 + ```bash + certbot certonly --force-renewal --manual -d '*.x.com' \ + --preferred-challenges dns \ + --server https://acme-v02.api.letsencrypt.org/directory + ``` + +- 生成 2048 位的交换密钥文件 + ```bash + openssl dhparam -out /etc/letsencrypt/dhparam.pem 2048 + ``` + +# 使用证书 +- nginx 配置 ssl + ``` + server { + listen 443 ssl; + server_name x.x.x; + ssl_certificate /etc/letsencrypt/live/x.x.x/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/x.x.x/privkey.pem; + ssl_session_cache shared:le_nginx_SSL:10m; + ssl_session_timeout 1440m; + ssl_session_tickets off; + ssl_prefer_server_ciphers off; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; + ssl_dhparam /etc/letsencrypt/dhparam.pem; + location / { + return 404; + } + } + ``` + diff --git a/content/post/linux-io-random.md b/content/post/linux-io-random.md new file mode 100644 index 0000000..4ddb76b --- /dev/null +++ b/content/post/linux-io-random.md @@ -0,0 +1,188 @@ +--- +title: "Linux 硬盘随机读写" +date: 2019-10-29T21:29:26+08:00 +lastmod: 2019-10-29T21:29:26+08:00 +keywords: [] +tags: ["linux", "随机读", "随机写"] +categories: ["storage"] +--- + +# 前言 +- 随机写会导致磁头不停地换道,造成效率的极大降低 +- 顺序写磁头几乎不用换道,或者换道的时间很短 + +# fio 介绍 +- fio的输出报告中的几个关键指标: + - slat: 是指从 I/O 提交到实际执行 I/O 的时长(Submission latency) + - clat: 是指从 I/O 提交到 I/O 完成的时长(Completion latency) + - lat: 指的是从 fio 创建 I/O 到 I/O 完成的总时长 + - bw : 吞吐量 + - iops: 每秒 I/O 的次数 + +# 同步写测试 +### 同步随机写 +- 使用strace工具查看系统调用 + ```bash + strace -f -tt -o /tmp/randwrite.log -D fio -name=randwrite -rw=randwrite \ + -direct=1 -bs=4k -size=1G -numjobs=1 -group_reporting -filename=/tmp/test.db + ``` +- 提取关键信息 + ``` + root@wilson-ubuntu:~# strace -f -tt -o /tmp/randwrite.log -D fio -name=randwrite -rw=randwrite \ + > -direct=1 -bs=4k -size=1G -numjobs=1 -group_reporting -filename=/tmp/test.db + randwrite: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=sync, iodepth=1 + fio-2.2.10 + Starting 1 process + ... + randwrite: (groupid=0, jobs=1): err= 0: pid=26882: Wed Aug 14 10:39:02 2019 + write: io=1024.0MB, bw=52526KB/s, iops=13131, runt= 19963msec + clat (usec): min=42, max=18620, avg=56.15, stdev=164.79 + lat (usec): min=42, max=18620, avg=56.39, stdev=164.79 + ... + bw (KB /s): min=50648, max=55208, per=99.96%, avg=52506.03, stdev=1055.83 + ... + + Run status group 0 (all jobs): + WRITE: io=1024.0MB, aggrb=52525KB/s, minb=52525KB/s, maxb=52525KB/s, mint=19963msec, maxt=19963msec + + Disk stats (read/write): + ... + sda: ios=0/262177, merge=0/25, ticks=0/7500, in_queue=7476, util=36.05% + ``` +- 重点关注的信息 + - clat: 平均时长56ms左右 + - lat: 平均时长56ms左右 + - bw: 吞吐量,大概在52M左右 +- 内核调用信息,随机读每一次写入之前都要通过lseek去定位当前的文件偏移量 + ``` + root@wilson-ubuntu:~# more /tmp/randwrite.log + ... + 26882 10:38:41.919904 lseek(3, 665198592, SEEK_SET) = 665198592 + 26882 10:38:41.919920 write(3, "\220\240@\6\371\341\277>\0\200\36\31\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.919969 lseek(3, 4313088, SEEK_SET) = 4313088 + 26882 10:38:41.919985 write(3, "\220\240@\6\371\341\277>\0\200\36\31\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920032 lseek(3, 455880704, SEEK_SET) = 455880704 + 26882 10:38:41.920048 write(3, "\220\240@\6\371\341\277>\0\200\36\31\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920096 lseek(3, 338862080, SEEK_SET) = 338862080 + 26882 10:38:41.920112 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920161 lseek(3, 739086336, SEEK_SET) = 739086336 + 26882 10:38:41.920177 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920229 lseek(3, 848175104, SEEK_SET) = 848175104 + 26882 10:38:41.920245 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920296 lseek(3, 1060147200, SEEK_SET) = 1060147200 + 26882 10:38:41.920312 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920362 lseek(3, 863690752, SEEK_SET) = 863690752 + 26882 10:38:41.920377 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920428 lseek(3, 279457792, SEEK_SET) = 279457792 + 26882 10:38:41.920444 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920492 lseek(3, 271794176, SEEK_SET) = 271794176 + 26882 10:38:41.920508 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + 26882 10:38:41.920558 lseek(3, 1067864064, SEEK_SET) = 1067864064 + 26882 10:38:41.920573 write(3, "\220\240@\6\371\341\277>\0\2402\24\0\0\0\0\202\2\7\320\343\6H\26P\340\277\370\330\30e\30"..., 4096) = 4096 + ... + ``` + +### 同步顺序写 +- 测试顺序写 + ```bash + strace -f -tt -o /tmp/write.log -D fio -name=write -rw=write \ + -direct=1 -bs=4k -size=1G -numjobs=1 -group_reporting -filename=/tmp/test.db + ``` +- 可以看到,吞吐量提升至70M左右 + ``` + write: (g=0): rw=write, bs=4K-4K/4K-4K/4K-4K, ioengine=sync, iodepth=1 + fio-2.2.10 + Starting 1 process + Jobs: 1 (f=1): [W(1)] [100.0% done] [0KB/70432KB/0KB /s] [0/17.7K/0 iops] [eta 00m:00s] + write: (groupid=0, jobs=1): err= 0: pid=27005: Wed Aug 14 10:53:02 2019 + write: io=1024.0MB, bw=70238KB/s, iops=17559, runt= 14929msec + clat (usec): min=43, max=7464, avg=55.95, stdev=56.24 + lat (usec): min=43, max=7465, avg=56.15, stdev=56.25 + ... + bw (KB /s): min=67304, max=72008, per=99.98%, avg=70225.38, stdev=1266.88 + ... + + Run status group 0 (all jobs): + WRITE: io=1024.0MB, aggrb=70237KB/s, minb=70237KB/s, maxb=70237KB/s, mint=14929msec, maxt=14929msec + + Disk stats (read/write): + ... + sda: ios=0/262162, merge=0/10, ticks=0/6948, in_queue=6932, util=46.49% + ``` +- 内核调用 + ``` + root@wilson-ubuntu:~# more /tmp/write.log + ... + 27046 10:54:28.194508 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\360\t\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194568 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194627 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194687 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194747 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194807 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194868 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194928 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.194988 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195049 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195110 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195197 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195262 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195330 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195426 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195497 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195567 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195637 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195704 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195757 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195807 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195859 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195910 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.195961 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196012 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196062 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0\220\24\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196112 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196162 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196213 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196265 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196314 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196363 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196414 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196472 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196524 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + 27046 10:54:28.196573 write(3, "\0\0\23\0\0\0\0\0\0\300\16\0\0\0\0\0\0 \26\0\0\0\0\0\0\320\17\0\0\0\0\0"..., 4096) = 4096 + ... + ``` +- 顺序读不需要反复定位文件偏移量,所以能够专注于写操作 + +# 异步顺序写 +- 前面 fio 的测试报告中没有发现slat,那是由于上述都是同步操作,对同步 I/O 来说,由于 I/O 提交和 I/O 完成是一个动作,所以 slat 实际上就是 I/O 完成的时间 +- 异步顺序写测试命令 + ```bash + # 同步顺序写的命令添加 -ioengine=libaio + fio -name=write -rw=write -ioengine=libaio -direct=1 -bs=4k -size=1G -numjobs=1 -group_reporting -filename=/tmp/test.db + ``` +- slat指标出现,lat 近似等于 slat + clat 之和(avg平均值);并且换成异步io之后,吞吐量得到了极大的提升,120M左右 + ``` + write: (g=0): rw=write, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=1 + fio-2.2.10 + Starting 1 process + Jobs: 1 (f=1): [W(1)] [100.0% done] [0KB/119.3MB/0KB /s] [0/30.6K/0 iops] [eta 00m:00s] + write: (groupid=0, jobs=1): err= 0: pid=27258: Wed Aug 14 11:14:36 2019 + write: io=1024.0MB, bw=120443KB/s, iops=30110, runt= 8706msec + slat (usec): min=3, max=70, avg= 4.31, stdev= 1.56 + clat (usec): min=0, max=8967, avg=28.13, stdev=55.68 + lat (usec): min=22, max=8976, avg=32.53, stdev=55.72 + ... + bw (KB /s): min=118480, max=122880, per=100.00%, avg=120467.29, stdev=1525.68 + ... + + Run status group 0 (all jobs): + WRITE: io=1024.0MB, aggrb=120442KB/s, minb=120442KB/s, maxb=120442KB/s, mint=8706msec, maxt=8706msec + + Disk stats (read/write): + ... + sda: ios=0/262147, merge=0/1, ticks=0/6576, in_queue=6568, util=74.32% + ``` + +# 参考 +- [https://www.linuxidc.com/Linux/2019-08/160097.htm](https://www.linuxidc.com/Linux/2019-08/160097.htm) + diff --git a/content/post/lvm.md b/content/post/lvm.md new file mode 100644 index 0000000..a4b365c --- /dev/null +++ b/content/post/lvm.md @@ -0,0 +1,198 @@ +--- +title: "Lvm 笔记" +date: 2019-10-29T21:44:02+08:00 +lastmod: 2019-10-29T21:44:02+08:00 +keywords: [] +tags: ["lvm"] +categories: ["storage"] +--- + +# LVM 一览 +![lvm一览](/img/lvm.jpg "lvm一览") + +# LVM 结构 +- PE 物理扩展 +- PV 物理卷,在设备起始处放置一个标签,包括 uuid,lvm 配置元数据位置以及剩余空间 + - PV 可以由分区创建,也可以直接用磁盘创建 +- VG 卷组 +- LV 逻辑卷 + - Linear 线性卷 + - Stripe 条带卷 + - RAID raid 逻辑卷 + - Mirror 镜像卷 + - Thinly-Provision 精简配置逻辑卷 + - Snapshot 快照卷 + - Thinly-Provisioned Snapshot 精简配置快照卷 + - Cache 缓存卷 + +# LVM 相关命令 + +## PV +- pvchange 更改物理卷属性 +- pvck 检查物理卷元数据 +- pvcreate 初始化磁盘或分区以供lvm使用 +- pvdisplay 显示物理卷的属性 +- pvmove 移动物理Exent +- pvremove 删除物理卷 +- pvresize 调整lvm2使用的磁盘或分区的大小 +- pvs 报告有关物理卷的信息 +- pvscan 扫描物理卷的所有磁盘 + +## VG +- vgcfgbackup 备份卷组描述符区域 +- vgcfgrestore 恢复卷组描述符区域 +- vgchange 更改卷组的属性 +- vgck 检查卷组元数据 +- vgconvert 转换卷组元数据格式 +- vgcreate 创建卷组 +- vgdisplay 显示卷组的属性 +- vgexport 使卷组对系统不了解(这是个什么) +- vgextend 将物理卷添加到卷组 +- vgimportclone 导入并重命名重复的卷组(例如硬件快照) +- vgmerge 合并两个卷组 +- vgmknodes 重新创建卷组目录和逻辑卷特殊文件 +- vgreduce 通过删除一个或多个物理卷来减少卷组(将物理卷踢出VG) +- vgremove 删除卷组 +- vgrename 重命名卷组 +- vgs 报告有关卷组信息 +- vgscan 扫描卷组的所有磁盘并重建高速缓存 +- vgsplit 将卷组拆分为两个,通过移动整个物理卷将任何逻辑卷从一个卷组移动到另一个卷组 + +## LV +- lvchange 更改逻辑卷属性 +- lvconvert 将逻辑卷从线性转换为镜像或快照 +- lvcreate 将现有卷组中创建逻辑卷 +- lvdisplay 显示逻辑卷的属性 +- lvextend 扩展逻辑卷的大小 +- lvmconfig 在加载lvm.conf和任何其他配置文件后显示配置信息 +- lvmdiskscan 扫描lvm2可见的所有设备 +- lvmdump 创建lvm2信息转储以用于诊断目的 +- lvreduce 减少逻辑卷的大小 +- lvremove 删除逻辑卷 +- lvrename 重命名逻辑卷 +- lvresize 调整逻辑卷大小 +- lvs 报告有关逻辑卷的信息 +- lvscan 扫描所有的逻辑卷 + +# 创建 LVM +- 将磁盘创建为 pv(物理卷),其实物理磁盘被条带化为 pv,划成了一个一个的 pe,默认每个 pe 大小是 4MB +- 创建 vg,其实它是一个空间池,不同PV加入同一 vg +- 创建 lv,组成 lv 的 pe 可能来自不同的物理磁盘 +- 格式化 lv,挂载使用 + +# PV 管理 +- 制作 pv + ```bash + pvcreate /dev/sdb1 + ``` +- 删除 pv(需先踢出 vg) + ```bash + pvremote /dev/sdb1 + ``` + +# VG 管理 +- 制作 vg + ```bash + # vgcreate vg_name 磁盘设备或分区 + vgcreate datavg /dev/sdb1 + vgcreate datavg /dev/sdb1 /dev/sdb2 + # -s 指定pe的大小为16M,默认不指定是4M + vgcreate -s 16M datavg2 /dev/sdb3 + ``` +- 从 vg 中移除缺失的磁盘 + ```bash + vgreduce --removemissing datavg + vgreduce --removemissing datavg --force # 强制移除 + ``` +- 扩展 vg 空间 + ```bash + vgextend datavg /dev/sdb3 /dev/sdc + ``` +- 踢出 vg 中的某个成员 + ```bash + vgreduce datavg /dev/sdb3 + ``` +- 删除 vg + ```bash + vgremove VG + ``` +- 重命名 vg + ```bash + vgrename xxxx-vgid-xxxx-xxxx new_name + ``` + +# LV 管理 +- 制作 lv + ```bash + # -n lv_name,-L lv_size,datavg(vg name) + lvcreate -n lvdata1 -L 1.5G datavg + ``` +- 激活修复后的逻辑卷 + ```bash + lvchange -ay /dev/datavg/lvdata1 + lvchange -ay /dev/datavg/lvdata1 -K # 强制激活 + ``` +- 创建 lvm 快照 + ```bash + # 数据一致性备份 + # 先做一个快照,冻结当前系统,这样快照里面的内容可暂时保持不变 + # 系统本身继续运行,通过重新挂载备份快照卷,实现不中断服务备份。 + lvcreate -s -n kuaizhao01 -L 100M /dev/datavg/lvdata1 + ``` +- 删除 lv + ```bash + lvremove /dev/mapper/VG-mylv + ``` +- 扩大一个 lv + - 用vgdisplay查看vg还有多少空余空间 + - 扩充逻辑卷 + ```bash + lvextend -L +1G /dev/VG/LV01 + # -r 表示在扩展的同时也更新文件系统,不是所有的发行版本都支持 + lvextend -L +1G /dev/VG/LV01 -r + ``` + - 扩充操作后,df -h 发现大小并没有变,需更新文件系统 + ```bash + # 不同文件系统更新的命令不一样 + e2fsck -f /dev/datavg/lvdata1 # ext4 文件系统,检查 lv 的文件系统 + resize2fs /dev/VG/LV01 # ext4 文件系统命令,该命令后面接 lv 的设备名就行 + xfs_growfs /nas # xfs 文件系统,该命令后面直接跟的是挂载点 + ``` + - 更新文件系统后,df -h 正常 +- 缩小一个 lv + - umount 卸载 + - 缩小文件系统 + ```bash + resize2fs /dev/VG/LV01 2G + ``` + - 缩小 lv + ```bash + lvreduce -L -1G /dev/VG/LV01 + ``` + - 查看 lvs,mount 挂载 + +# lvm 灾难恢复 +- 场景: 三块盘做 lvm,现有一物理盘损坏,将剩下两块放到其他linux服务器上 +- 恢复步骤 + - 查看磁盘信息,lvm信息,确认能查到lvm相关信息,找到VG组的名字 + ```bash + pvs + lvs + vgs + fidsk + blkid + ``` + - 删除 lvm 信息中损坏的磁盘角色,强制提出故障磁盘 + ```bash + vgreduce --removemissing VG_name + ``` + - 强制激活 vg 组 + ```bash + vgchange -ay + ``` + - 强制激活 lvm + ```bash + lvchange -ay /dev/VG_name + ``` + - 挂载 + diff --git a/content/post/lvs.md b/content/post/lvs.md new file mode 100644 index 0000000..62cd301 --- /dev/null +++ b/content/post/lvs.md @@ -0,0 +1,44 @@ +--- +title: "Lvs 笔记" +date: 2019-10-30T12:55:50+08:00 +lastmod: 2019-10-30T12:55:50+08:00 +tags: ["lvs", "负载均衡"] +categories: ["ha/lb"] +--- + +# 环境 +角色 | 地址 +---- | ---- +负载分配服务器 | 192.168.1.209 +tomcat 服务器1 | 192.168.1.207 +tomcat 服务器2 | 192.168.1.208 +VIP | 192.168.1.250 + +# LVS-DR +- 配置负载分配服务器(192.168.1.209) + ```bash + #在与 tomcat 服务器连通的网卡(eth0)上配置虚拟 IP(192.168.1.250) + ip addr add 192.168.1.250/32 brd 192.168.1.250 dev eth0 + ip route add 192.168.1.250 dev eth0 + #开启转发 + echo 1 > /proc/sys/net/ipv4/ip_forward + #安装 ipvsadm + yum install ipvsadm + ipvsadm -C + ipvsadm -A -t 192.168.1.250:80 -s rr + ipvsadm -a -t 192.168.1.250:80 -r 192.168.1.207:80 -g + ipvsadm -a -t 192.168.1.250:80 -r 192.168.1.208:80 -g + ``` +- 在两台 tomcat 服务器上都做如下配置 + ```bash + #禁用 arp 响应 + echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore + echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce + echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore + echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce + #添加虚拟 IP + ip addr add 192.168.1.250/32 brd 192.168.1.250 dev lo + ip route add 192.168.1.250 dev lo + #在个人浏览器中访问 http://192.168.1.250/webapp/ + ``` + diff --git a/content/post/makefile.md b/content/post/makefile.md new file mode 100644 index 0000000..fc8ca41 --- /dev/null +++ b/content/post/makefile.md @@ -0,0 +1,1750 @@ +--- +title: "Makefile 笔记" +date: 2019-10-30T14:24:58+08:00 +lastmod: 2019-10-30T14:24:58+08:00 +tags: ["makefile"] +categories: ["dev/ops"] +--- + +# Makefile 介绍 +### 介绍 +- make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。 +- 首先,我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册,在这个示例中,我们的工程有8个C文件,和3个头文件,我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是: + - 如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。 + - 如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。 + - 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。 +- 只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。 + +### Makefile的规则 +- 在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。 + ```makefile + target... : prerequisites ... + command + ... + ... + ``` + - target 也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的"伪目标"章节中会有叙述。 + - prerequisites 就是要生成那个target所需要的文件或是目标。 + - command 也就是make需要执行的命令(任意的Shell命令)。 +- 这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。 + +### 一个示例 +- 正如前面所说的,如果一个工程有3个头文件,和8个C文件,我们为了完成前面所述的那三个规则,我们的Makefile应该是下面的这个样子的。 + ```makefile + edit : main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + cc -o edit main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + + main.o : main.c defs.h + cc -c main.c + kbd.o : kbd.c defs.h command.h + cc -c kbd.c + command.o : command.c defs.h command.h + cc -c command.c + display.o : display.c defs.h buffer.h + cc -c display.c + insert.o : insert.c defs.h buffer.h + cc -c insert.c + search.o : search.c defs.h buffer.h + cc -c search.c + files.o : files.c defs.h buffer.h command.h + cc -c files.c + utils.o : utils.c defs.h + cc -c utils.c + clean : + rm edit main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + ``` + - 我们可以把这个内容保存在文件为"Makefile"或"makefile"的文件中,然后在该目录下直接输入命令"make"就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下"make clean"就可以了。 + - 在这个makefile中,目标文件(target)包含:执行文件edit和中间目标文件(\*.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。 + - 在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。 + - 这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言中的lable一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个lable的名字。这样的方法非常有用,我们可以在一个makefile中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。 + +### make 是如何工作的 +- 在默认的方式下,也就是我们只输入make命令。那么, + - make会在当前目录下找名字叫"Makefile"或"makefile"的文件。 + - 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到"edit"这个文件,并把这个文件作为最终的目标文件。 + - 如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。 + - 如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程) + - 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件声明make的终极任务,也就是执行文件edit了。 +- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。 +- 通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令 "make clean",以此来清除所有的目标文件,以便重编译。 +- 于是在我们编程中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如file.c,那么根据我们的依赖性,我们的目标file.o会被重编译(也就是在这个依性关系后面所定义的命令),于是file.o的文件也是最新的啦,于是file.o的文件修改时间要比edit要新,所以edit也会被重新链接了(详见edit目标文件后定义的命令)。 +而如果我们改变了"command.h",那么,kdb.o、command.o和files.o都会被重编译,并且,edit会被重链接。 + +### makefile中使用变量 +- 在上面的例子中,先让我们看看edit的规则: + ```makefile + edit : main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + cc -o edit main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + ``` + - 我们可以看到[.o]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。当然,我们的makefile并不复杂,所以在两个地方加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉一个需要加入的地方,而导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。 +- 比如,我们声明一个变量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能够表示obj文件就行了。我们在makefile一开始就这样定义: + ```makefile + objects = main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + ``` +- 于是,我们就可以很方便地在我们的makefile中以"$(objects)"的方式来使用这个变量了,于是我们的改良版makefile就变成下面这个样子: + ```makefile + objects = main.o kbd.o command.o display.o \ + insert.osearch.o files.o utils.o + edit : $(objects) + cc -o edit $(objects) + main.o : main.c defs.h + cc -c main.c + kbd.o : kbd.c defs.h command.h + cc -c kbd.c + command.o : command.c defs.h command.h + cc -c command.c + display.o : display.c defs.h buffer.h + cc -c display.c + insert.o : insert.c defs.h buffer.h + cc -c insert.c + search.o : search.c defs.h buffer.h + cc -c search.c + files.o : files.c defs.h buffer.h command.h + cc -c files.c + utils.o : utils.c defs.h + cc -c utils.c + clean : + rm edit $(objects) + ``` + - 如果有新的 .o 文件加入,我们只需简单地修改一下 objects 变量就可以了。 + +### 让make自动推导 +- GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。 +- 只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。 + ```makefile + objects = main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + + edit : $(objects) + cc -o edit $(objects) + + main.o : defs.h + kbd.o : defs.h command.h + command.o : defs.h command.h + display.o : defs.h buffer.h + insert.o : defs.h buffer.h + search.o : defs.h buffer.h + files.o : defs.h buffer.h command.h + utils.o : defs.h + + .PHONY : clean + clean : + rm edit $(objects) + ``` +- 这种方法,也就是make的"隐晦规则"。上面文件内容中,".PHONY"表示,clean是个伪目标文件。 + +### 另类风格的makefile +- 既然我们的make可以自动推导命令,那么我看到那堆[.o]和[.h]的依赖就有点不爽,那么多的重复的[.h],能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了自动推导命令和文件的功能呢?来看看最新风格的makefile吧。 + ```makefile + objects = main.o kbd.o command.o display.o \ + insert.o search.o files.o utils.o + + edit : $(objects) + cc -o edit $(objects) + + $(objects) : defs.h + kbd.o command.o files.o : command.h + display.o insert.o search.o files.o : buffer.h + + .PHONY : clean + clean : + rm edit $(objects) + ``` +- 这种风格,让我们的makefile变得很简单,但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了 + +### 清空目标文件的规则 +- 每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个"修养"(呵呵,还记得我的《编程修养》吗)。一般的风格都是: + ```makefile + clean: + rm edit $(objects) + ``` +- 更为稳健的做法是: + ```makefile + .PHONY : clean + clean : + -rm edit $(objects) + ``` +- 前面说过,.PHONY意思表示clean是一个"伪目标",而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是 "clean从来都是放在文件的最后"。 + + +# Makefile 总述 +### Makefile里有什么? +- Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 +- 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 +- 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。 +- 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。 +- 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。 +- 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用"#"字符,这个就像C/C++中的"//"一样。如果你要在你的Makefile中使用"#"字符,可以用反斜框进行转义,如:"\#"。 +- 最后,还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。 + +### Makefile的文件名 +- 默认的情况下,make命令会在当前目录下按顺序找寻文件名为"GNUmakefile"、"makefile"、"Makefile"的文件,找到了解释这个文件。在这三个文件名中,最好使用"Makefile"这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用"GNUmakefile",这个文件是GNU的make识别的。有另外一些make只对全小写的"makefile"文件名敏感,但是基本上来说,大多数的make都支持"makefile"和"Makefile"这两种默认文件名。 +- 当然,你可以使用别的文件名来书写Makefile,比如:"Make.Linux","Make.Solaris","Make.AIX"等,如果要指定特定的Makefile,你可以使用make的"-f"和"--file"参数,如:make -f Make.Linux或make --file Make.AIX。 + +### 引用其它的Makefile +- 在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是: + ```makefile + includefilename可以是当前操作系统Shell的文件模式(可以保含路径和通配符) + ``` +- 在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句: + ```makefile + include foo.make *.mk $(bar) + ``` + - 等价于: + ```makefile + include foo.make a.mk b.mk c.mk e.mk f.mk + ``` +- make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找: + - 如果make执行时,有"-I"或"--include-dir"参数,那么make就会在这个参数所指定的目录下去寻找。 + - 如果目录/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。 +- 如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号"-"。如: + ```makefile + -include + ``` +- 其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令是sinclude,其作用和这一个是一样的。 + +### 环境变量 MAKEFILES +- 如果你的当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变中引入的Makefile的"目标"不会起作用,如果环境变量中定义的文件发现错误,make也会不理。 +- 但是在这里我还是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响,这绝不是你想看到的。在这里提这个事,只是为了告诉大家,也许有时候你的Makefile出现了怪事,那么你可以看看当前环境中有没有定义这个变量。 + +### make的工作方式 +- GNU的make工作时的执行步骤入下 + - 读入所有的Makefile。 + - 读入被include的其它Makefile。 + - 初始化文件中的变量。 + - 推导隐晦规则,并分析所有规则。 + - 为所有的目标文件创建依赖关系链。 + - 根据依赖关系,决定哪些目标要重新生成。 + - 执行生成命令。 +- 步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。 +- 当然,这个工作方式你不一定要清楚,但是知道这个方式你也会对make更为熟悉。有了这个基础,后续部分也就容易看懂了。 + +# Makefile书写规则 +### 规则 +- 规则包含两个部分,一个是依赖关系,一个是生成目标的方法。 +- 在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。 +- 好了,还是让我们来看一看如何书写规则。 + +### 规则举例 + ```makefile + foo.o: foo.c defs.h # foo模块 + cc -c -g foo.c + ``` +- 看到这个例子,各位应该不是很陌生了,前面也已说过,foo.o是我们的目标,foo.c和defs.h是目标所依赖的源文件,而只有一个命令"cc -c -g foo.c"(以Tab键开头)。这个规则告诉我们两件事 + - 文件的依赖关系,foo.o依赖于foo.c和defs.h的文件,如果foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依赖关系发生。 + - 如果生成(或更新)foo.o文件。也就是那个cc命令,其说明了,如何生成foo.o这个文件。(当然foo.c文件include了defs.h文件) + +### 规则的语法 + ```makefile + targets : prerequisites + command + ... + ``` + - 或是这样: + ```makefile + targets : prerequisites ; command + command + ... + ``` +- targets 是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。 +- command 是命令行,如果其不与"target:prerequisites"在一行,那么,必须以[Tab键]开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上) +- prerequisites 也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是"过时的",被认为是需要重生成的。这个在前面已经讲过了。 +- 如果命令太长,你可以使用反斜框(‘\’)作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何成成目标文件。 +- 一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。 + +### 在规则中使用通配符 +- 如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:"\*","?"和"[...]"。这是和Unix的B-Shell是相同的。 +- "~" 波浪号("~")字符在文件名中也有比较特殊的用途。如果是"~/test",这就表示当前用户的$HOME目录下的test目录。而"~hchen/test"则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量"HOME"而定。 +- "\*" 通配符代替了你一系列的文件,如"\*.c"表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:"\*",那么可以用转义字符"\",如"\*"来表示真实的"\*"字符,而不是任意长度的字符串。 +- 先来看几个例子 + ```makefile + clean: + rm -f *.o + ``` +- 上面这个例子我不不多说了,这是操作系统Shell所支持的通配符。这是在命令中的通配符。 + ```makefile + print: *.c + lpr -p $? + touch print + ``` +- 上面这个例子说明了通配符也可以在我们的规则中,目标print依赖于所有的[.c]文件。其中的"$?"是一个自动化变量,我会在后面给你讲述。 + ```makefile + objects = *.o + ``` +- 上面这个例子,表示了,通符同样可以用在变量中。并不是说[*.o]会展开,不!objects的值就是"*.o"。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有[.o]的文件名的集合,那么,你可以这样: + ```makefile + objects := $(wildcard *.o) + ``` +- 这种用法由关键字"wildcard"指出,关于Makefile的关键字,我们将在后面讨论。 + +### 文件搜寻 +- 在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。 +- Makefile文件中的特殊变量"VPATH"就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。 + ```makefile + VPATH = src:../headers + ``` +- 上面的的定义指定两个目录,"src"和"../headers",make会按照这个顺序进行搜索。目录由"冒号"分隔。(当然,当前目录永远是最高优先搜索的地方) +- 另一个设置文件搜索路径的方法是使用make的"vpath"关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种: + - vpath # 为符合模式的文件指定搜索目录。 + - vpath # 清除符合模式的文件的搜索目录。 + - vpath # 清除所有已被设置好了的文件搜索目录。 +- vapth使用方法中的需要包含"%"字符。"%"的意思是匹配零或若干字符,例如,"%.h"表示所有以".h"结尾的文件。< pattern>指定了要搜索的文件集,而< directories>则指定了的文件集的搜索的目录。例如: + ```makefile + vpath %.h ../headers + ``` +- 该语句表示,要求make在"../headers"目录下搜索所有以".h"结尾的文件。(如果某文件在当前目录没有找到的话) +- 我们可以连续地使用vpath语句,以指定不同搜索策略。如果连续的vpath语句中出现了相同的,或是被重复了的,那么,make会按照vpath语句的先后顺序来执行搜索。如 + ```makefile + vpath %.c foo + vpath % blish + vpath %.c bar + ``` +- 其表示".c"结尾的文件,先在"foo"目录,然后是"blish",最后是"bar"目录。 + ```makefile + vpath %.c foo:bar + vpath % blish + ``` +- 而上面的语句则表示".c"结尾的文件,先在"foo"目录,然后是"bar"目录,最后才是"blish"目录。 + +### 伪目标 +- 最早先的一个例子中,我们提到过一个"clean"的目标,这是一个"伪目标", + ```makefile + clean: + rm *.o temp + ``` +- 正像我们前面例子中的"clean"一样,即然我们生成了许多文件编译文件,我们也应该提供一个清除它们的"目标"以备完整地重编译而用。 (以"make clean"来使用该目标) +- 因为,我们并不生成"clean"这个文件。"伪目标"并不是一个文件,只是一个标签,由于"伪目标"不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个"目标"才能让其生效。当然,"伪目标"的取名不能和文件名重名,不然其就失去了"伪目标"的意义了。 +- 当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记".PHONY"来显示地指明一个目标是"伪目标",向make说明,不管是否有这个文件,这个目标就是"伪目标"。 + ```makefile + .PHONY : clean + ``` +- 只要有这个声明,不管是否有"clean"文件,要运行"clean"这个目标,只有"make clean"这样。于是整个过程可以这样写: + ```makefile + .PHONY: clean + clean: + rm *.o temp + ``` +- 伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为"默认目标",只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用"伪目标"这个特性: + ```makefile + all : prog1 prog2 prog3 + .PHONY : all + + prog1 : prog1.o utils.o + cc -o prog1 prog1.o utils.o + + prog2 : prog2.o + cc -o prog2 prog2.o + + prog3 : prog3.o sort.o utils.o + cc -o prog3 prog3.o sort.o utils.o + ``` +- 我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个"all"的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是不如"all"这个目标新。所以,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。".PHONY : all"声明了"all"这个目标为"伪目标"。 +- 随便提一句,从上面的例子我们可以看出,目标也可以成为依赖。所以,伪目标同样也可成为依赖。看下面的例子: + ```makefile + .PHONY: cleanall cleanobj cleandiff + + cleanall : cleanobj cleandiff + rm program + + cleanobj : + rm *.o + + cleandiff : + rm *.diff + ``` +- "makeclean"将清除所有要被清除的文件。"cleanobj"和"cleandiff"这两个伪目标有点像"子程序"的意思。我们可以输入"makecleanall"和"make cleanobj"和"makecleandiff"命令来达到清除不同种类文件的目的 + +### 多目标 +- Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。当然,多个目标的生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自动化变量"$@"(关于自动化变量,将在后面讲述),这个变量表示着目前规则中所有的目标的集合,这样说可能很抽象,还是看一个例子吧。 + ```makefile + bigoutput littleoutput : text.g + generate text.g -$(subst output,,$@) > $@ + ``` + - 上述规则等价于: + ```makefile + bigoutput : text.g + generate text.g -big > bigoutput + littleoutput : text.g + generate text.g -little > littleoutput + ``` +- 其中,-$(subst output,,$@)中的"$"表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,"$@"表示目标的集合,就像一个数组,"$@"依次取出目标,并执于命令。 + +### 静态模式 +- 静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法: + ```makefile + : : +     + ... + ``` + - targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。 + - target-parrtern是指明了targets的模式,也就是的目标集模式。 + - prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。 +- 这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的定义成"%.o",意思是我们的集合中都是以".o"结尾的,而如果我们的定义成"%.c",意思是对所形成的目标集进行二次定义,其计算方法是,取模式中的"%"(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。 +- 所以,我们的"目标模式"或是"依赖模式"中都应该有"%"这个字符,如果你的文件名中有"%"那么你可以使用反斜杠"\"进行转义,来标明真实的"%"字符。 +看一个例子: + ```makefile + objects = foo.o bar.o + + all: $(objects) + + $(objects): %.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + ``` +- 上面的例子中,指明了我们的目标从$object中获取,"%.o"表明要所有以".o"结尾的目标,也就是"foo.o bar.o",也就是变量$object集合的模式,而依赖模式"%.c"则取模式"%.o"的"%",也就是"foobar",并为其加下".c"的后缀,于是,我们的依赖目标就是"foo.cbar.c"。而命令中的"$<"和"$@"则是自动化变量,"$<"表示所有的依赖目标集(也就是"foo.c bar.c"),"$@"表示目标集(也褪恰癴oo.o bar.o")。于是,上面的规则展开后等价于下面的规则: + ```makefile + foo.o : foo.c + $(CC) -c $(CFLAGS) foo.c -o foo.o + bar.o : bar.c + $(CC) -c $(CFLAGS) bar.c -o bar.o + ``` +- 试想,如果我们的"%.o"有几百个,那种我们只要用这种很简单的"静态模式规则"就可以写完一堆规则,实在是太有效率了。"静态模式规则"的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子: + ```makefile + files = foo.elc bar.o lose.o + + $(filter %.o,$(files)): %.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + $(filter %.elc,$(files)): %.elc: %.el + emacs -f batch-byte-compile $< + ``` +- $(filter%.o,$(files))表示调用Makefile的filter函数,过滤"$filter"集,只要其中模式为"%.o"的内容。其的它内容,我就不用多说了吧。这个例字展示了Makefile中更大的弹性。 + +### 自动生成依赖性 +- 在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c中有一句"#include "defs.h"",那么我们的依赖关系应该是: + ```makefile + main.o : main.c defs.h + ``` +- 但是,如果是一个比较大型的工程,你必需清楚哪些C文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,我们可以使用C/C++编译的一个功能。大多数的C/C++编译器都支持一个"-M"的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令: + ```bash + cc -M main.c + ``` + - 其输出是: + ``` + main.o : main.c defs.h + ``` +- 于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用GNU的C/C++编译器,你得用"-MM"参数,不然,"-M"参数会把一些标准库的头文件也包含进来。 + ```makefile + gcc -M main.c + ``` + - 输出是: + ``` + main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \ + /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \ + /usr/include/bits/sched.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/include/bits/wchar.h /usr/include/gconv.h \ + /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \ + /usr/include/bits/stdio_lim.h + ``` + ```bash + gcc-MM main.c + ``` + - 输出则是: + ``` + main.o: main.c defs.h + ``` +- 那么,编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来,我们的Makefile也要根据这些源文件重新生成,让Makefile自已依赖于源文件?这个功能并不现实,不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个"name.c"的文件都生成一个"name.d"的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。 +- 于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。 +- 这里,我们给出了一个模式规则来产生[.d]文件: + ```makefile + %.d: %.c + @set -e; rm -f $@; \ + $(CC) -M $(CPPFLAGS) $< > $@.; \ + sed 's,$*\.o[ :]*,\1.o $@ : ,g' < $@. > $@; \ + rm -f $@. + ``` +- 这个规则的意思是,所有的[.d]文件依赖于[.c]文件,"rm-f $@"的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件"$<",也就是[.c]文件生成依赖文件,"$@"表示模式"%.d"文件,如果有一个C文件是name.c,那么"%"就是"name",""意为一个随机编号,第二行生成的文件有可能是"name.d.12345",第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。 +- 总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系: + ```bash + main.o : main.c defs.h + ``` +- 转成: + ```bash + main.o main.d : main.c defs.h + ``` +- 于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的"include"命令,来引入别的Makefile文件(前面讲过),例如: + ```makefile + sources = foo.c bar.c + include $(sources:.c=.d) + ``` +- 上述语句中的"$(sources:.c=.d)"中的".c=.d"的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个"替换"的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标 + +# Makefile 书写命令 +### 命令 +- 每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。 +- 我们在UNIX下可能会使用不同的Shell,但是make的命令默认是被"/bin/sh" UNIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中,"#"是注释符,很像C/C++中的"//",其后的本行字符都被注释。 + +### 显示命令 +- 通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用"@"字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。如: + ```makefile + @echo 正在编译XXX模块...... + ``` +- 当make执行时,会输出"正在编译XXX模块......"字串,但不会输出命令,如果没有"@",那么,make将输出: + ```makefile + echo 正在编译XXX模块...... + 正在编译XXX模块...... + ``` +- 如果make执行时,带入make参数"-n"或"--just-print",那么其只是显示命令,但不会执行命令,这个功能很有利于我们调试我们的Makefile,看看我们书写的命令是执行起来是什么样子的或是什么顺序的。 +- make参数"-s"或"--slient"则是全面禁止命令的显示。 + +### 命令执行 +- 当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。如: + ```makefile + # 示例一: + exec: + cd /home/hchen + pwd + + # 示例二: + exec: + cd /home/hchen; pwd + ``` +- 当我们执行"make exec"时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出"/home/hchen"。 +- make 一般是使用环境变量SHELL中所定义的系统Shell来执行命令,默认情况下使用UNIX的标准Shell /bin/sh来执行命令。但在MS-DOS下有点特殊,因为MS-DOS下没有SHELL环境变量,当然你也可以指定。如果你指定了UNIX风格的目录形式,首先,make会在SHELL所指定的路径中找寻命令解释器,如果找不到,其会在当前盘符中的当前目录中寻找,如果再找不到,其会在PATH环境变量中所定义的所有路径中寻找。MS-DOS中,如果你定义的命令解释器没有找到,其会给你的命令解释器加上诸如".exe"、".com"、".bat"、".sh"等后缀。 + +### 命令出错 +- 每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。 +- 有些时候,命令的出错并不表示就是错误的。例如mkdir命令,我们一定需要建立一个目录,如果目录不存在,那么mkdir就成功执行,万事大吉,如果目录存在,那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录,于是我们就不希望mkdir出错而终止规则的运行。 +- 为了做到这一点,忽略命令的出错,我们可以在Makefile的命令行前加一个减号"-"(在Tab键之后),标记为不管命令出不出错都认为是成功的。如: + ```makefile + clean: + -rm -f *.o + ``` +- 还有一个全局的办法是,给make加上"-i"或是"--ignore-errors"参数,那么,Makefile中所有命令都会忽略错误。而如果一个规则是以".IGNORE"作为目标的,那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法,你可以根据你的不同喜欢设置。 +- 还有一个要提一下的make的参数的是"-k"或是"--keep-going",这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。 + +### 嵌套执行make +- 在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。 +- 例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写: + ```makefile + subsystem: + cd subdir && $(MAKE) + ``` + - 其等价于: + ```makefile + subsystem: + $(MAKE) -C subdir + ``` +- 定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入"subdir"目录,然后执行make命令。 +- 我们把这个Makefile叫做"总控Makefile",总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了"-e"参数。 +- 如果你要传递变量到下级Makefile中,那么你可以使用这样的声明: + ```makefile + export + ``` +- 如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明: + ```makefile + unexport + ``` +- 如: + ```makefile + # 示例一: + export variable = value + # 其等价于: + variable = value + export variable + # 其等价于: + export variable := value + # 其等价于: + variable := value + export variable + + # 示例二: + export variable += value + # 其等价于: + variable += value + export variable + ``` +- 如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。 +- 需要注意的是,有两个变量,一个是SHELL,一个是MAKEFLAGS,这两个变量不管你是否export,其总是要传递到下层Makefile中,特别是MAKEFILES变量,其中包含了make的参数信息,如果我们执行"总控Makefile"时有make参数或是在上层Makefile中定义了这个变量,那么MAKEFILES变量将会是这些参数,并会传递到下层Makefile中,这是一个系统级的环境变量。 +- 但是make命令中的有几个参数并不往下传递,它们是"-C","-f","-h""-o"和"-W"(有关Makefile参数的细节将在后面说明),如果你不想往下层传递参数,那么,你可以这样来: + ```makefile + subsystem: + cd subdir && $(MAKE) MAKEFLAGS= + ``` +- 如果你定义了环境变量MAKEFLAGS,那么你得确信其中的选项是大家都会用到的,如果其中有"-t","-n",和"-q"参数,那么将会有让你意想不到的结果,或许会让你异常地恐慌。 +- 还有一个在"嵌套执行"中比较有用的参数,"-w"或是"--print-directory"会在make的过程中输出一些信息,让你看到目前的工作目录。比如,如果我们的下级make目录是"/home/hchen/gnu/make",如果我们使用"make -w"来执行,那么当进入该目录时,我们会看到: + ``` + make: Entering directory `/home/hchen/gnu/make'. + ``` +- 而在完成下层make后离开目录时,我们会看到: + ``` + make: Leaving directory `/home/hchen/gnu/make' + ``` +- 当你使用"-C"参数来指定make下层Makefile时,"-w"会被自动打开的。如果参数中有"-s"("--slient")或是"--no-print-directory",那么,"-w"总是失效的。 + +### 定义命令包 +- 如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以"define"开始,以"endef"结束,如: + ```makefile + define run-yacc + yacc $(firstword $^) + mv y.tab.c $@ + endef + ``` +- 这里,"run-yacc"是这个命令包的名字,其不要和Makefile中的变量重名。在"define"和"endef"中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序,因为Yacc程序总是生成"y.tab.c"的文件,所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。 + ```makefile + foo.c : foo.y + $(run-yacc) + ``` +- 我们可以看见,要使用这个命令包,我们就好像使用变量一样。在这个命令包的使用中,命令包"run-yacc"中的"$^"就是"foo.y","$@"就是"foo.c"(有关这种以"$"开头的特殊变量,我们会在后面介绍),make在执行命令包时,命令包中的每个命令会被依次独立执行。 +- 在 Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同的是,你可以在Makefile中改变其值。在Makefile中,变量可以使用在"目标","依赖目标","命令"或是 Makefile的其它部分中。变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有":"、"#"、"="或是空字符(空格、回车等)。变量是大小写敏感的,"foo"、"Foo"和"FOO"是三个不同的变量名。传统的Makefile的变量名是全大写的命名方式,但我推荐使用大小写搭配的变量名,如:MakeFlags。这样可以避免和系统的变量冲突,而发生意外的事情。有一些变量是很奇怪字串,如"$<"、"$@"等,这些是自动化变量。 + +# 变量 +### 变量的基础 +- 变量在声明时需要给予初值,而在使用时,需要给在变量名前加上"$"符号,但最好用小括号"()"或是大括号"{}"把变量给包括起来。如果你要使用真实的"$"字符,那么你需要用"$$"来表示。变量可以使用在许多地方,如规则中的"目标"、"依赖"、"命令"以及新的变量中。 +- 先看一个例子: + ```makefile + objects = program.o foo.o utils.o + program : $(objects) + cc -o program $(objects) + + $(objects) : defs.h + ``` +- 变量会在使用它的地方精确地展开,就像C/C++中的宏一样,例如: + ```makefile + foo = c + prog.o : prog.$(foo) + $(foo)$(foo) -$(foo) prog.$(foo) + ``` +- 展开后得到: + ```makefile + prog.o : prog.c + cc -c prog.c + ``` +- 当然,千万不要在你的Makefile中这样干,这里只是举个例子来表明Makefile中的变量在使用处展开的真实样子。可见其就是一个"替代"的原理。另外,给变量加上括号完全是为了更加安全地使用这个变量,在上面的例子中,如果你不想给变量加上括号,那也可以,但我还是强烈建议你给变量加上括号。 + +### 变量中的变量 +- 在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。 +- 先看第一种方式,也就是简单的使用"="号,在"="左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好 +的值,其也可以使用后面定义的值。如: + ```makefile + foo = $(bar) + bar = $(ugh) + ugh = Huh? + all: + echo $(foo) + ``` +- 我们执行"make all"将会打出变量$(foo)的值是"Huh?"( $(foo)的值是$(bar),$(bar)的值是$(ugh),$(ugh)的值是"Huh?")可见,变量是可以使用后面的变量来定义的。 +- 这个功能有好的地方,也有不好的地方,好的地方是,我们可以把变量的真实值推到后面来定义,如: + ```makefile + CFLAGS = $(include_dirs) -O + include_dirs = -Ifoo -Ibar + ``` +- 当"CFLAGS"在命令中被展开时,会是"-Ifoo -Ibar -O"。但这种形式也有不好的地方,那就是递归定义,如: + ```makefile + CFLAGS = $(CFLAGS) -O + ``` +- 或: + ```makefile + A = $(B) + B = $(A) + ``` +- 这会让make陷入无限的变量展开过程中去,当然,我们的make是有能力检测这样的定义,并会报错。还有就是如果在变量中使用函数,那么,这种方式会让我们的make运行时非常慢,更糟糕的是,他会使用得两个make的函数"wildcard"和"shell"发生不可预知的错误。因为你不会知道这两个函数会被调用多少次。 +- 为了避免上面的这种方法,我们可以使用make中的另一种用变量来定义变量的方法。这种方法使用的是":="操作符,如: + ```makefile + x := foo + y := $(x) bar + x := later + ``` +- 其等价于: + ```makefile + y := foo bar + x := later + ``` +- 值得一提的是,这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。如果是这样: + ```makefile + y := $(x) bar + x := foo + ``` +- 那么,y的值是"bar",而不是"foo bar"。 +- 上面都是一些比较简单的变量使用了,让我们来看一个复杂的例子,其中包括了make的函数、条件表达式和一个系统变量"MAKELEVEL"的使用: + ```makefile + ifeq (0,${MAKELEVEL}) + cur-dir := $(shell pwd) + whoami := $(shell whoami) + host-type := $(shell arch) + MAKE := ${MAKE} host-type=${host-type} whoami=${whoami} + endif + ``` +- 关于条件表达式和函数,我们在后面再说,对于系统变量"MAKELEVEL",其意思是,如果我们的make有一个嵌套执行的动作(参见前面的"嵌套使用make"),那么,这个变量会记录了我们的当前Makefile的调用层数。 +- 下面再介绍两个定义变量时我们需要知道的,请先看一个例子,如果我们要定义一个变量,其值是一个空格,那么我们可以这样来: + ```makefile + nullstring := + space := $(nullstring) # end of the line + ``` +- nullstring 是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。因为在操作符的右边是很难描述一个空格的,这里采用的技术很管用,先用一个 Empty变量来标明变量的值开始了,而后面采用"#"注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空格的变量。请注意这里关于"#"的使用,注释符"#"的这种特性值得我们注意,如果我们这样定义一个变量: + ```makefile + dir := /foo/bar # directory to put the frobs in + ``` +- dir这个变量的值是"/foo/bar",后面还跟了4个空格,如果我们这样使用这样变量来指定别的目录 "$(dir)/file"那么就完蛋了。 +- 还有一个比较有用的操作符是"?=",先看示例: + ```makefile + FOO ?= bar + ``` +- 其含义是,如果FOO没有被定义过,那么变量FOO的值就是"bar",如果FOO先前被定义过,那么这条语将什么也不做,其等价于: + ```makefile + ifeq ($(origin FOO), undefined) + FOO = bar + endif + ``` + +### 变量高级用法 +- 这里介绍两种变量的高级使用方法,第一种是变量值的替换。 +- 我们可以替换变量中的共有的部分,其格式是"$(var:a=b)"或是"${var:a=b}",其意思是,把变量"var"中所有以"a"字串"结尾"的"a"替换成"b"字串。这里的"结尾"意思是"空格"或是"结束符"。 +- 还是看一个示例吧: + ```makefile + foo := a.o b.o c.o + bar := $(foo:.o=.c) + ``` +- 这个示例中,我们先定义了一个"$(foo)"变量,而第二行的意思是把"$(foo)"中所有以".o"字串"结尾"全部替换成".c",所以我们的"$(bar)"的值就是"a.c b.c c.c"。 +- 另外一种变量替换的技术是以"静态模式"(参见前面章节)定义的,如: + ```makefile + foo := a.o b.o c.o + bar := $(foo:%.o=%.c) + ``` +- 这依赖于被替换字串中的有相同的模式,模式中必须包含一个"%"字符,这个例子同样让$(bar)变量的值为"a.c b.c c.c"。 +- 第二种高级用法是 "把变量的值再当成变量"。先看一个例子: + ```makefile + x = y + y = z + a := $($(x)) + ``` +- 在这个例子中,$(x)的值是"y",所以$($(x))就是$(y),于是$(a)的值就是"z"。(注意,是"x=y",而不是"x=$(y)") +我们还可以使用更多的层次: + ```makefile + x = y + y = z + z = u + a := $($($(x))) + ``` +- 这里的$(a)的值是"u",相关的推导留给读者自己去做吧。 +- 让我们再复杂一点,使用上"在变量定义中使用变量"的第一个方式,来看一个例子: + ```makefile + x = $(y) + y = z + z = Hello + a := $($(x)) + ``` +- 这里的$($(x))被替换成了$($(y)),因为$(y)值是"z",所以,最终结果是:a:=$(z),也就是"Hello"。 +- 再复杂一点,我们再加上函数: + ```makefile + x = variable1 + variable2 := Hello + y = $(subst 1,2,$(x)) + z = y + a := $($($(z))) + ``` +- 这个例子中,"$($($(z)))"扩展为"$($(y))",而其再次被扩展为"$($(subst 1,2,$(x)))"。$(x)的值是"variable1",subst函数把"variable1"中的所有"1"字串替换成"2"字串,于是,"variable1"变成"variable2",再取其值,所以,最终,$(a)的值就是$(variable2)的值 "Hello"。(喔,好不容易) +- 在这种方式中,或要可以使用多个变量来组成一个变量的名字,然后再取其值: + ```makefile + first_second = Hello + a = first + b = second + all = $($a_$b) + ``` +- 这里的"$a_$b"组成了"first_second",于是,$(all)的值就是"Hello"。 +- 再来看看结合第一种技术的例子: + ```makefile + a_objects := a.o b.o c.o + 1_objects := 1.o 2.o 3.o + sources := $($(a1)_objects:.o=.c) + ``` +- 这个例子中,如果$(a1)的值是"a"的话,那么,$(sources)的值就是"a.c b.c c.c";如果$(a1)的值是"1",那么$(sources)的值是"1.c 2.c 3.c"。 +- 再来看一个这种技术和"函数"与"条件语句"一同使用的例子: + ```makefile + ifdef do_sort + func := sort + else + func := strip + endif + + bar := a d b g q c + + foo := $($(func) $(bar)) + ``` +- 这个示例中,如果定义了"do_sort",那么:foo := $(sort a d b g q c),于是$(foo)的值就是"a b c d g q",而如果没有定义"do_sort",那么:foo := $(sort a d bg q c),调用的就是strip函数。 +- 当然,"把变量的值再当成变量"这种技术,同样可以用在操作符的左边: + ```makefile + dir = foo + $(dir)_sources := $(wildcard $(dir)/*.c) + define $(dir)_print + lpr $($(dir)_sources) + endef + ``` +- 这个例子中定义了三个变量:"dir","foo_sources"和"foo_print"。 + +### 追加变量值 +- 我们可以使用"+="操作符给变量追加值,如: + ```makefile + objects = main.o foo.o bar.o utils.o + objects += another.o + ``` +- 于是,我们的$(objects)值变成:"main.o foo.o bar.o utils.o another.o"(another.o被追加进去了) +- 使用"+="操作符,可以模拟为下面的这种例子: + ```makefile + objects = main.o foo.o bar.o utils.o + objects := $(objects) another.o + ``` +- 所不同的是,用"+="更为简洁。 +- 如果变量之前没有定义过,那么,"+="会自动变成"=",如果前面有变量定义,那么"+="会继承于前次操作的赋值符。如果前一次的是":=",那么"+="会以":="作为其赋值符,如: + ```makefile + variable := value + variable += more + ``` +- 等价于: + ```makefile + variable := value + variable := $(variable) more + ``` +- 但如果是这种情况: + ```makefile + variable = value + variable += more + ``` +- 由于前次的赋值符是"=",所以"+="也会以"="来做为赋值,那么岂不会发生变量的递补归定义,这是很不好的,所以make会自动为我们解决这个问题,我们不必担心这个问题。 + +### override 指示符 +- 如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如果你想在Makefile中设置这类参数的值,那么,你可以使用"override"指示符。其语法是: + ```makefile + override = + override := + ``` +- 当然,你还可以追加: + ```makefile + override += + ``` +- 对于多行的变量定义,我们用define指示符,在define指示符前,也同样可以使用ovveride指示符,如: + ```makefile + override define foo + bar + endef + ``` + +### 多行变量 +- 还有一种设置变量值的方法是使用define关键字。使用define关键字设置变量的值可以有换行,这有利于定义一系列的命令(前面我们讲过"命令包"的技术就是利用这个关键字)。 +- define 指示符后面跟的是变量的名字,而重起一行定义变量的值,定义是以endef关键字结束。其工作方式和"="操作符一样。变量的值可以包含函数、命令、文字,或是其它变量。因为命令需要以[Tab]键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,那么make就不会把其认为是命令。 +- 下面的这个示例展示了define的用法: + ```makefile + define two-lines + echo foo + echo $(bar) + endef + ``` + +### 环境变量 +- make 运行时的系统环境变量可以在make开始运行时被载入到Makefile文件中,但是如果Makefile中已定义了这个变量,或是这个变量由make命令行带入,那么系统的环境变量的值将被覆盖。(如果make指定了"-e"参数,那么,系统环境变量将覆盖Makefile中定义的变量) +- 因此,如果我们在环境变量中设置了"CFLAGS"环境变量,那么我们就可以在所有的Makefile中使用这个变量了。这对于我们使用统一的编译参数有比较大的好处。如果Makefile中定义了CFLAGS,那么则会使用Makefile中的这个变量,如果没有定义则使用系统环境变量的值,一个共性和个性的统一,很像"全局变量"和"局部变量"的特性。 当make嵌套调用时(参见前面的"嵌套调用"章节),上层Makefile中定义的变量会以系统环境变量的方式传递到下层的Makefile中。当然,默认情况下,只有通过命令行设置的变量会被传递。而定义在文件中的变量,如果要向下层 Makefile传递,则需要使用exprot关键字来声明。(参见前面章节) +- 当然,我并不推荐把许多的变量都定义在系统环境中,这样,在我们执行不用的Makefile时,拥有的是同一套系统变量,这可能会带来更多的麻烦。 + +### 目标变量 +- 前面我们所讲的在Makefile中定义的变量都是"全局变量",在整个文件,我们都可以访问这些变量。当然,"自动化变量"除外,如"$<"等这种类量的自动化变量就属于"规则型变量",这种变量的值依赖于规则的目标和依赖目标的定义。 +- 当然,我样同样可以为某个目标设置局部变量,这种变量被称为"Target-specific Variable",它可以和"全局变量"同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。 +- 其语法是: + ```makefile + : + : overide + ``` +- 可以是前面讲过的各种赋值表达式,如"="、":="、"+="或是"?="。第二个语法是针对于make命令行带入的变量,或是系统环境变量。 +- 这个特性非常的有用,当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。如: + ```makefile + prog : CFLAGS = -g + prog : prog.o foo.o bar.o + $(CC) $(CFLAGS) prog.o foo.o bar.o + prog.o : prog.c + $(CC) $(CFLAGS) prog.c + + foo.o : foo.c + $(CC) $(CFLAGS) foo.c + + bar.o : bar.c + $(CC) $(CFLAGS) bar.c + ``` +- 在这个示例中,不管全局的$(CFLAGS)的值是什么,在prog目标,以及其所引发的所有规则中(prog.o foo.o bar.o的规则),$(CFLAGS)的值都是"-g" + +### 模式变量 +- 在GNU的make中,还支持模式变量(Pattern-specific Variable),通过上面的目标变量中,我们知道,变量可以定义在某个目标上。模式变量的好处就是,我们可以给定一种"模式",可以把变量定义在符合这种模式的所有目标上。 +- 我们知道,make的"模式"一般是至少含有一个"%"的,所以,我们可以以如下方式给所有以[.o]结尾的目标定义目标变量: + ```makefile + %.o : CFLAGS = -O + ``` +- 同样,模式变量的语法和"目标变量"一样: + ```makefile + : + : override + ``` +- override同样是针对于系统环境传入的变量,或是make命令行指定的变量。 + +# 条件判断 +### 一个例子 +- 下面的例子,判断$(CC)变量是否"gcc",如果是的话,则使用GNU函数编译目标。 + ```makefile + libs_for_gcc = -lgnu + normal_libs = + + foo: $(objects) + ifeq ($(CC),gcc) + $(CC) -o foo $(objects) $(libs_for_gcc) + else + $(CC) -o foo $(objects) $(normal_libs) + endif + ``` +- 可见,在上面示例的这个规则中,目标"foo"可以根据变量"$(CC)"值来选取不同的函数库来编译程序。 +- 我们可以从上面的示例中看到三个关键字:ifeq、else和endif。ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以endif结束。 +- 当我们的变量$(CC)值是"gcc"时,目标foo的规则是: + ```makefile + foo: $(objects) + $(CC) -o foo $(objects) $(libs_for_gcc) + ``` +- 而当我们的变量$(CC)值不是"gcc"时(比如"cc"),目标foo的规则是: + ```makefile + foo: $(objects) + $(CC) -o foo $(objects) $(normal_libs) + ``` +- 当然,我们还可以把上面的那个例子写得更简洁一些: + ```makefile + libs_for_gcc = -lgnu + normal_libs = + + ifeq ($(CC),gcc) + libs=$(libs_for_gcc) + else + libs=$(normal_libs) + endif + + foo: $(objects) + $(CC) -o foo $(objects) $(libs) + ``` + +### 语法 +- 条件表达式的语法为: + ```makefile + + + endif + ``` +- 以及: + ```makefile + + + else + + endif + ``` +- 其中表示条件关键字,如"ifeq"。这个关键字有四个。 +- 第一个是我们前面所见过的"ifeq" + ```makefile + ifeq (, ) + ifeq '' '' + ifeq "" "" + ifeq "" '' + ifeq '' "" + ``` +- 比较参数"arg1"和"arg2"的值是否相同。当然,参数中我们还可以使用make的函数。如: + ```makefile + ifeq ($(strip $(foo)),) + + endif + ``` +- 这个示例中使用了"strip"函数,如果这个函数的返回值是空(Empty),那么就生效。 +- 第二个条件关键字是"ifneq"。语法是: + ```makefile + ifneq (, ) + ifneq '' '' + ifneq "" "" + ifneq "" '' + ifneq '' "" + ``` +- 其比较参数"arg1"和"arg2"的值是否相同,如果不同,则为真。和"ifeq"类似。 +- 第三个条件关键字是"ifdef"。语法是: + ```makefile + ifdef + ``` +- 如果变量的值非空,那到表达式为真。否则,表达式为假。当然,同样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子: + ```makefile + 示例一: + bar = + foo = $(bar) + ifdef foo + frobozz = yes + else + frobozz = no + endif + + 示例二: + foo = + ifdef foo + frobozz = yes + else + frobozz = no + endif + ``` +- 第一个例子中,"$(frobozz)"值是"yes",第二个则是"no"。 +- 第四个条件关键字是"ifndef"。其语法是: + ```makefile + ifndef + ``` +- 这个我就不多说了,和"ifdef"是相反的意思。 +- 在这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令)。而注释符"#"同样也是安全的。"else"和"endif"也一样,只要不是以[Tab]键开始就行了。 +- 特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如"$@"等)放入条件表达式中,因为自动化变量是在运行时才有的。而且,为了避免混乱,make不允许把整个条件语句分成两部分放在不同的文件中。 + + +# 函数 +### 函数的调用语法 +- 函数调用,很像变量的使用,也是以"$"来标识的,其语法如下: + ```makefile + $( ) + ``` +- 或是 + ```makefile + ${ } + ``` +- 这里,就是函数名,make支持的函数不多。是函数的参数,参数间以逗号","分隔,而函数名和参数之间以"空格"分隔。函数调用以"$"开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用"$(subst a,b,$(x))"这样的形式,而不是"$(subst a,b,${x})"的形式。因为统一会更清楚,也会减少一些不必要的麻烦。 +- 还是来看一个示例: + ```makefile + comma:= , + empty:= + space:= $(empty) $(empty) + foo:= a b c + bar:= $(subst $(space),$(comma),$(foo)) + ``` +- 在这个示例中,$(comma)的值是一个逗号。$(space)使用了$(empty)定义了一个空格,$(foo)的值是"a b c",$(bar)的定义用,调用了函数"subst",这是一个替换函数,这个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数是替换操作作用的字串。这个函数也就是把$(foo)中的空格替换成逗号,所以$(bar)的值是"a,b,c"。 + +### 字符串处理函数 +##### 字符串替换函数 subst +- 功能:把字串\中的字符串替换成。 +- 返回:函数返回被替换过后的字符串。 +- 语法: + ```makefile + $(subst ,,\ ) + ``` +- 示例: + ```makefile + $(subst ee,EE,feet on the street), + ``` +- 把"feet on the street"中的"ee"替换成"EE",返回结果是"fEEt on the strEEt"。 + +##### 模式字符串替换函数 patsubst +- 功能:查找\中的单词(单词以"空格"、"Tab"或"回车""换行"分隔)是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符"%",表示任意长度的字串。如果中也包含"%",那么,中的这个"%"将是中的那个"%"所代表的字串。(可以用"\"来转义,以"\%"来表示真实含义的"%"字符)返回:函数返回被替换过后的字符串。 +- 语法: + ```makefile + $(patsubst ,,\ ) + ``` +- 示例: + ```makefile + $(patsubst %.c,%.o,x.c.c bar.c) + ``` +- 把字串"x.c.c bar.c"符合模式[%.c]的单词替换成[%.o],返回结果是"x.c.o bar.o" +- 这和我们前面"变量章节"说过的相关知识有点相似。如: + ```makefile + "$(var:= )" + ``` +- 相当于 + ```makefile + "$(patsubst ,,$(var))", + ``` +- 而 + ```makefile + "$(var: = )" + ``` +- 则相当于 + ```makefile + "$(patsubst %,%,$(var))"。 + ``` +- 例如有:objects = foo.o bar.o baz.o,那么,"$(objects:.o=.c)"和"$(patsubst %.o,%.c,$(objects))"是一样的。 + +##### 去空格函数 strip +- 功能:去掉字串中开头和结尾的空字符。 +- 返回:返回被去掉空格的字符串值。 +- 语法 + ```makefile + $(strip ) + ``` +- 示例: + ```makefile + $(strip a b c ) + ``` +- 把字串"a b c "去到开头和结尾的空格,结果是"a b c"。 + +##### 查找字符串函数findstring +- 功能:在字串中查找字串。 +- 返回:如果找到,那么返回,否则返回空字符串。 +- 语法: + ```makefile + $(findstring , ) + ``` +- 示例: + ```makefile + $(findstring a,a b c) + $(findstring a,b c) + ``` +- 第一个函数返回"a"字符串,第二个返回""字符串(空字符串) + +##### 过滤函数 filter +- 功能:以模式过滤\字符串中的单词,保留符合模式的单词。可以有多个模式。 +- 返回:返回符合模式的字串。 +- 语法: + ```makefile + $(filter ,\ ) + ``` +- 示例: + ```makefile + sources := foo.c bar.c baz.s ugh.h + foo: $(sources) + cc $(filter %.c %.s,$(sources)) -o foo + ``` +- $(filter %.c %.s,$(sources))返回的值是"foo.c bar.c baz.s"。 + + +##### 反过滤函数 filter-out +- 功能:以模式过滤\字符串中的单词,去除符合模式的单词。可以有多个模式。 +- 返回:返回不符合模式的字串。 +- 语法: + ```makefile + $(filter-out ,\ ) + ``` +示例: + ```makefile + objects=main1.o foo.o main2.o bar.o + mains=main1.o main2.o + ``` +- $(filter-out $(mains),$(objects)) 返回值是"foo.o bar.o"。 + +##### 排序函数 sort。 +- 功能:给字符串中的单词排序(升序)。 +- 返回:返回排序后的字符串。 +- 语法: + ```makefile + $(sort ) + ``` +- 示例:$(sort foo bar lose)返回"bar foo lose" 。 +- 备注:sort函数会去掉中相同的单词。 + +##### 取单词函数 word +- 功能:取字符串\中第个单词。(从一开始) +- 返回:返回字符串\中第个单词。如果比\中的单词数要大,那么返回空字符串。 +- 语法: + ```makefile + $(word ,\ ) + ``` +- 示例:$(word 2, foo bar baz)返回值是"bar"。 + +##### 取单词串函数 wordlist +- 功能:从字符串\中取从\开始到的单词串,\是一个数字。 +- 返回:返回字符串\中从\的单词字串。如果\比\中的单词数要大,那么返回空字符串。如果大于\的单词数,那么返回从\开始,到\结束的单词串。 +- 语法: + ```makefile + $(wordlist \,,\ ) + ``` +- 示例: $(wordlist 2, 3, foo bar baz)返回值是"bar baz"。 + +##### 单词个数统计函数 words +- 功能:统计\中字符串中的单词个数。 +- 返回:返回\中的单词数。 +- 语法: + ```makefile + $(words \ ) + ``` +- 示例:$(words, foo bar baz)返回值是"3"。 +- 备注:如果我们要取\中最后的一个单词,我们可以这样 + ```makefile + $(word $(words \),\ )。 + ``` + +##### 首单词函数 firstword +- 功能:取字符串\中的第一个单词。 +- 返回:返回字符串\的第一个单词。 +- 语法: + ```makefile + $(firstword \ ) + ``` +- 示例:$(firstword foo bar)返回值是"foo"。 +- 备注:这个函数可以用word函数来实现:$(word 1,\ )。 + +##### 应用例子 +- make使用"VPATH"变量来指定"依赖文件"的搜索路径,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数CFLAGS,如: + ```makefile + override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH))) + ``` +- 如果我们的"$(VPATH)"值是"src:../headers",那么"$(patsubst %,-I%,$(subst :, ,$(VPATH)))"将返回"-Isrc -I../headers",这正是cc或gcc搜索头文件路径的参数 。 + +### 文件名操作函数 +- 下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是一系列的文件名来对待。 + +##### 取目录函数 dir +- 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠("/")之 +- 前的部分。如果没有反斜杠,那么返回"./"。 +- 返回:返回文件名序列的目录部分。 +- 语法: + ```makefile + $(dir ) + ``` +- 示例: $(dir src/foo.c hacks)返回值是"src/ ./"。 + +##### 取文件函数 notdir +- 功能:从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠("/")之后的部分。 +- 返回:返回文件名序列的非目录部分。 +- 语法: + ```makefile + $(notdir ) + ``` +- 示例: $(notdir src/foo.c hacks)返回值是"foo.c hacks"。 + +##### 取后缀函数 suffix +- 功能:从文件名序列中取出各个文件名的后缀。 +- 返回:返回文件名序列的后缀序列,如果文件没有后缀,则返回空字串。 +- 语法: + ```makefile + $(suffix ) + ``` +- 示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是".c .c"。 + +##### 取前缀函数 basename +- 功能:从文件名序列中取出各个文件名的前缀部分。 +- 返回:返回文件名序列的前缀序列,如果文件没有前缀,则返回空字串。 +- 语法: + ```makefile + $(basename ) + ``` +- 示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是"src/foo src-1.0/bar hacks"。 + +##### 加后缀函数 addsuffix +- 功能:把后缀加到中的每个单词后面。 +- 返回:返回加过后缀的文件名序列。 +- 语法: + ```makefile + $(addsuffix , ) + ``` +- 示例:$(addsuffix .c,foo bar)返回值是"foo.c bar.c"。 + +##### 加前缀函数 addprefix +- 功能:把前缀加到中的每个单词后面。 +- 返回:返回加过前缀的文件名序列。 +- 语法: + ```makefile + $(addprefix , ) + ``` +- 示例:$(addprefix src/,foo bar)返回值是"src/foo src/bar"。 + +##### 连接函数 join。 +- 功能:把中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。 +- 返回:返回连接过后的字符串。 +- 语法: + ```makefile + $(join , ) + ``` +- 示例:$(join aaa bbb , 111 222 333)返回值是"aaa111 bbb222 333"。 + +### foreach 函数 +- foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile中的foreach函数几乎是仿照于Unix标准Shell(/bin /sh)中的for语句,或是C-Shell(/bin/csh)中的foreach语句而构建的。它的语法是: + ```makefile + $(foreach ,,\ ) + ``` +- 这个函数的意思是,把参数中的单词逐一取出放到参数所指定的变量中,然后再执行\所包含的表达式。每一次\会返回一个字符串,循环过程中,\的所返回的每个字符串会以空格分隔,最后当整个循环结束时,\所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 +- 所以,最好是一个变量名,可以是一个表达式,而\中一般会使用这个参数来依次枚举中的单词。举个例子: + + ```makefile + names := a b c d + files := $(foreach n,$(names),$(n).o) + ``` +- 上面的例子中,$(name)中的单词会被挨个取出,并存到变量"n"中,"$(n).o"每次根据"$(n)"计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是"a.o b.o c.o d.o"。 +- 注意,foreach中的参数是一个临时的局部变量,foreach函数执行完后,参数的变量将不在作用,其作用域只在foreach函数当中。 + +### if 函数 +- if函数很像GNU的make所支持的条件语句 ifeq(参见前面所述的章节),if函数的语法是: + ```makefile + $(if , ) + ``` +- 或是 + ```makefile + $(if ,, ) + ``` +- 可见,if函数可以包含"else"部分,或是不含。即if函数的参数可以是两个,也可以是三个。参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,会被计算,否则 会被计算。 +- 而if函数的返回值是,如果为真(非空字符串),那个会是整个函数的返回值,如果为假(空字符串),那么会是整个函数的返回值,此时如果没有被定义,那么,整个函数返回空字串。 +- 所以,只会有一个被计算。 + +### call函数 +- call函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参数。其语法是: + ```makefile + $(call ,,,...) + ``` +- 当 make执行这个函数时,参数中的变量,如$(1),$(2),$(3)等,会被参数依次取代。而的返回值就是 call函数的返回值。例如: + ```makefile + reverse = $(1) $(2) + foo = $(call reverse,a,b) + ``` +- 那么,foo的值就是"a b"。当然,参数的次序是可以自定义的,不一定是顺序的,如: + ```makefile + reverse = $(2) $(1) + foo = $(call reverse,a,b) + ``` +- 此时的foo的值就是"b a"。 + +### origin 函数 +- origin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是: + ```makefile + $(origin ) + ``` +- 注意,是变量的名字,不应该是引用。所以你最好不要在中使用"$"字符。Origin函数会以其返回值来告诉你这个变量的"出生情况",下面是origin函数的返回值: + - "undefined" 如果从来没有定义过,origin函数返回这个值"undefined"。 + - "default" 如果是一个默认的定义,比如"CC"这个变量,这种变量我们将在后面讲述。 + - "environment" 如果是一个环境变量,并且当Makefile被执行时,"-e"参数没有被打开。 + - "file" 如果这个变量被定义在Makefile中。 + - "command line" 如果这个变量是被命令行定义的。 + - "override" 如果是被override指示符重新定义的。 + - "automatic" 如果是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。 +- 这些信息对于我们编写Makefile是非常有用的,例如,假设我们有一个Makefile其包了一个定义文件Make.def,在Make.def中定义了一个变量"bletch",而我们的环境中也有一个环境变量"bletch",此时,我们想判断一下,如果变量来源于环境,那么我们就把之重定义了,如果来源于Make.def或是命令行等非环境的,那么我们就不重新定义它。于是,在我们的Makefile中,我们可以这样写: + ```makefile + ifdef bletch + ifeq "$(origin bletch)" "environment" + bletch = barf, gag, etc. + endif + endif + ``` +- 当然,你也许会说,使用override关键字不就可以重新定义环境中的变量了吗?为什么需要使用这样的步骤?是的,我们用override是可以达到这样的效果,可是override过于粗暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来的,而不想重新定义命令行传来的。 + +### shell函数 +- shell 函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号"`"是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成一个变量,如: + ```makefile + contents := $(shell cat foo) + files := $(shell echo *.c) + ``` +- 注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果你的Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你想像的多得多。 + + +### 控制make的函数 +- make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,你是让make继续执行,还是停止。 + ```makefile + $(error ) + ``` +- 产生一个致命的错误,是错误信息。注意,error函数不会在一被使用就会产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也是可以的。例如: + ```makefile + 示例一: + ifdef ERROR_001 + $(error error is $(ERROR_001)) + endif + + 示例二: + ERR = $(error found an error!) + .PHONY: err + err: ; $(ERR) + ``` +- 示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。 +- 这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。 + ```makefile + $(warning ) + ``` + +# make 的运行 +### make 退出码 +- make命令执行后有三个退出码: + - 0 表示成功执行。 + - 1 如果make运行时出现任何错误,其返回1。 + - 2 如果你使用了make的"-q"选项,并且make使得一些目标不需要更新,那么返回2。 +- Make的相关参数我们会在后续章节中讲述。 + +### 指定Makefile +- 前面我们说过,GNU make找寻默认的Makefile的规则是在当前目录下依次找三个文件 "GNUmakefile"、"makefile"和"Makefile"。其按顺序找这三个文件,一旦找到,就开始读取这个文件并执行。 +- 当前,我们也可以给make命令指定一个特殊名字的Makefile。要达到这个功能,我们要使用make的"-f"或是"--file"参数("-- makefile"参数也行)。例如,我们有个makefile的名字是"hchen.mk",那么,我们可以这样来让make来执行这个文件: + ```bash + make –f hchen.mk + ``` +- 如果在make的命令行是,你不只一次地使用了"-f"参数,那么,所有指定的makefile将会被连在一起传递给make执行。 + +### 指定目标 +- 一般来说,make的最终目标是makefile中的第一个目标,而其它目标一般是由这个目标连带出来的。这是make的默认行为。当然,一般来说,你的 makefile中的第一个目标是由许多个目标组成,你可以指示make,让其完成你所指定的目标。要达到这一目的很简单,需在make命令后直接跟目标的名字就可以完成(如前面提到的"make clean"形式)任何在makefile中的目标都可以被指定成终极目标,但是除了以"- "打头,或是包含了"="的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。甚至没有被我们明确写出来的目标也可以成为make的终极目标,也就是说,只要make可以找到其隐含规则推导规则,那么这个隐含目标同样可以被指定成终极目标。 +- 有一个make的环境变量叫"MAKECMDGOALS",这个变量中会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值。这个变量可以让你使用在一些比较特殊的情形下。比如下面的例子: + ```makefile + sources = foo.c bar.c + ifneq ( $(MAKECMDGOALS),clean) + include $(sources:.c=.d) + endif + ``` +- 基于上面的这个例子,只要我们输入的命令不是"make clean",那么makefile会自动包含"foo.d"和"bar.d"这两个makefile。 +- 使用指定终极目标的方法可以很方便地让我们编译我们的程序,例如下面这个例子: + ```makefile + .PHONY: all + all: prog1 prog2 prog3 prog4 + ``` +- 从这个例子中,我们可以看到,这个makefile中有四个需要编译的程序 "prog1", "prog2", "prog3"和 "prog4",我们可以使用"make all"命令来编译所有的目标(如果把all置成第一个目标,那么只需执行"make"),我们也可以使用"make prog2"来单独编译目标"prog2"。 +- 即然make可以指定所有makefile中的目标,那么也包括"伪目标",于是我们可以根据这种性质来让我们的makefile根据指定的不同的目标来完成不同的事。在Unix世界中,软件发布时,特别是GNU这种开源软件的发布时,其 makefile都包含了编译、安装、打包等功能。我们可以参照这种规则来书写我们的makefile中的目标。 + - "all" 这个伪目标是所有目标的目标,其功能一般是编译所有的目标。 + - "clean" 这个伪目标功能是删除所有被make创建的文件。 + - "install" 这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。 + - "print" 这个伪目标的功能是例出改变过的源文件。 + - "tar" 这个伪目标功能是把源程序打包备份。也就是一个tar文件。 + - "dist" 这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。 + - "TAGS" 这个伪目标功能是更新所有的目标,以备完整地重编译使用。 + - "check"和"test" 这两个伪目标一般用来测试makefile的流程。 +- 当然一个项目的makefile中也不一定要书写这样的目标,这些东西都是GNU的东西,但是我想,GNU搞出这些东西一定有其可取之处(等你的UNIX下的程序文件一多时你就会发现这些功能很有用了),这里只不过是说明了,如果你要书写这种功能,最好使用这种名字命名你的目标,这样规范一些,规范的好处就是 不用解释,大家都明白。而且如果你的makefile中有这些功能,一是很实用,二是可以显得你的makefile很专业(不是那种初学者的作品)。 + +### 检查规则 +- 有时候,我们不想让我们的makefile中的规则执行起来,我们只想检查一下我们的命令,或是执行的序列。于是我们可以使用make命令的下述参数: + - "-n" "--just-print" "--dry-run" "--recon" 不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。 + - "-t" "--touch" 这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。 + - "-q" "--question" 这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。 + - "-W " "--what-if=" "--assume-new=" "--new-file=" 这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和"-n"参数一同使用,来查看这个依赖文件所发生的规则命令。 + - 另外一个很有意思的用法是结合"-p"和"-v"来输出makefile被执行时的信息(这个将在后面讲述)。 + +### make的参数 +- 下面列举了所有GNU make 3.80版的参数定义。其它版本和产商的make大同小异,不过其它产商的make的具体参数还是请参考各自的产品文档。 + - "-b" "-m" 这两个参数的作用是忽略和其它版本make的兼容性。 + - "-B" "--always-make" 认为所有的目标都需要更新(重编译)。 + - "-C " "--directory=" 指定读取makefile的目录。如果有多个"-C"参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:"make –C ~hchen/test –C prog" 等价于"make –C ~hchen/test/prog"。 + +- "--debug[=]" 输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是的取值: + - a 也就是all,输出所有的调试信息。(会非常的多) + - b 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。 + - v 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。 + - i 也就是implicit,输出所以的隐含规则。 + - j 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。 + - m 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。 +- "-d" 相当于"--debug=a"。 +- "-e" "--environment-overrides" 指明环境变量的值覆盖makefile中定义的变量的值。 +- "-f=" "--file=" "--makefile=" 指定需要执行的makefile。 +- "-h" "--help" 显示帮助信息。 +- "-i" "--ignore-errors" 在执行时忽略所有的错误。 +- "-I \" "--include-dir=\" 指定一个被包含makefile的搜索目标。可以使用多个"-I"参数来指定多个目录。 +- "-j []" "--jobs[=]" 指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的"-j"参数,那么仅最后一个"-j"才是有效的。(注意这个参数在MS-DOS中是无用的) +- "-k" "--keep-going" 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。 +- "-l " "--load-average[=]" 指定make运行命令的负载。 +- "-n" "--just-print" "--dry-run" "--recon" 仅输出执行过程中的命令序列,但并不执行。 +- "-o " "--old-file=" "--assume-old=" 不重新生成的指定的,即使这个目标的依赖文件新于它。 +- "-p" "--print-data-base" 输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行 makefile,你可以使用"make -qp"命令。如果你想查看执行makefile前的预设变量和规则,你可以使用"make –p –f /dev/null"。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。 +- "-q" "--question" 不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。 +- "-r" "--no-builtin-rules" 禁止make使用任何隐含规则。 +- "-R" "--no-builtin-variabes" 禁止make使用任何作用于变量上的隐含规则。 +- "-s" "--silent" "--quiet" 在命令运行时不输出命令的输出。 +- "-S" "--no-keep-going" "--stop" 取消"-k"选项的作用。因为有些时候,make的选项是从环境变量"MAKEFLAGS"中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的"-k"选项失效。 +- "-t" "--touch" 相当于UNIX的touch命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的命令运行。 +- "-v" "--version" 输出make程序的版本、版权等关于make的信息。 +- "-w" "--print-directory" 输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。 +- "--no-print-directory" 禁止"-w"选项。 +- "-W " "--what-if=" "--new-file=" "--assume-file=" 假定目标需要更新,如果和"-n"选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有"-n"那么就像运行UNIX的"touch"命令一样,使得的修改时间为当前时间。 +- "--warn-undefined-variables" 只要make发现有未定义的变量,那么就输出警告信息。 + +# 隐含规则 +- "隐含规则"也就是一种惯例,make会按照这种"惯例"心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。 +- "隐含规则"会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。如系统变量"CFLAGS"可以控制编译时的编译器参数。 +- 我们还可以通过"模式规则"的方式写下自己的隐含规则。用"后缀规则"来定义隐含规则会有许多的限制。使用"模式规则"会更回得智能和清楚,但"后缀规则"可以用来保证我们Makefile的兼容性。 + +### 使用隐含规则 +- 如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。当然,隐含规则是make事先约定好的一些东西。例如,我们有下面的一个Makefile: + ```makefile + foo : foo.o bar.o + cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS) + ``` +- 我们可以注意到,这个Makefile中并没有写下如何生成foo.o和bar.o这两目标的规则和命令。因为make的"隐含规则"功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。 +- make 会在自己的"隐含规则"库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把 [.o]的目标的依赖文件置成[.c],并使用C的编译命令"cc –c $(CFLAGS) [.c]"来生成[.o]的目标。也就是说,我们完全没有必要写下下面的两条规则: +- 语法: + ```makefile + foo.o : foo.c + cc –c foo.c $(CFLAGS) + bar.o : bar.c + cc –c bar.c $(CFLAGS) + ``` +- 因为,这已经是"约定"好了的事了,make和我们约定好了用C编译器"cc"生成[.o]文件的规则,这就是隐含规则。 +- 当然,如果我们为[.o]文件书写了自己的规则,那么make就不会自动推导并调用隐含规则,它会按照我们写好的规则忠实地执行。 +- 还有,在make的"隐含规则库"中,每一条隐含规则都在库中有其顺序,越靠前的则是越被经常使用的,所以,这会导致我们有些时候即使我们显示地指定了目标依赖,make也不会管。如下面这条规则(没有命令): + ```makefile + foo.o : foo.p + ``` +- 依赖文件"foo.p"(Pascal程序的源文件)有可能变得没有意义。如果目录下存在了"foo.c"文件,那么我们的隐含规则一样会生效,并会通过 "foo.c"调用C的编译器生成foo.o文件。因为,在隐含规则中,Pascal的规则出现在C的规则之后,所以,make找到可以生成foo.o的 C的规则就不再寻找下一条规则了。如果你确实不希望任何隐含规则推导,那么,你就不要只写出"依赖规则",而不写命令。 + +### 隐含规则一览 +- 这里我们将讲述所有预先设置(也就是make内建)的隐含规则,如果我们不明确地写下规则,那么,make就会在这些规则中寻找所需要规则和命令。当然,我们也可以使用make的参数"-r"或"--no-builtin-rules"选项来取消所有的预设置的隐含规则。 +- 当然,即使是我们指定了"-r"参数,某些隐含规则还是会生效,因为有许多的隐含规则都是使用了"后缀规则"来定义的,所以,只要隐含规则中有"后缀列表 "(也就一系统定义在目标.SUFFIXES的依赖目标),那么隐含规则就会生效。默认的后缀列表是:.out,.a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。具体的细节,我们会在后面讲述。 +- 常用的隐含规则 + - 编译C程序的隐含规则: ".o"的目标的依赖目标会自动推导为".c",并且其生成命令是 + ```makefile + $(CC) –c $(CPPFLAGS) $(CFLAGS) + ``` + - 编译C++程序的隐含规则: ".o" 的目标的依赖目标会自动推导为".cc"或是".C",并且其生成命令是 + ```makefile + $(CXX) –c $(CPPFLAGS) $(CFLAGS) + # 建议使用".cc"作为C++源文件的后缀,而不是".C" + ``` + - 编译Pascal程序的隐含规则: ".o"的目标的依赖目标会自动推导为".p",并且其生成命令是 + ```makefile + $(PC) –c $(PFLAGS) + ``` + - 编译Fortran/Ratfor程序的隐含规则: ".o"的目标的依赖目标会自动推导为".r"或".F"或".f",并且其生成命令是 + ```makefile + ".f" "$(FC) –c $(FFLAGS)" + ".F" "$(FC) –c $(FFLAGS) $(CPPFLAGS)" + ".f" "$(FC) –c $(FFLAGS) $(RFLAGS)" + ``` + - 预处理Fortran/Ratfor程序的隐含规则: ".f"的目标的依赖目标会自动推导为".r"或".F"。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是 + ```makefile + ".F" "$(FC) –F $(CPPFLAGS) $(FFLAGS)" + ".r" "$(FC) –F $(FFLAGS) $(RFLAGS)" + ``` + - 编译Modula-2程序的隐含规则: ".sym" 的目标的依赖目标会自动推导为".def",并且其生成命令是:"$(M2C) $(M2FLAGS) $(DEFFLAGS)"。"" 的目标的依赖目标会自动推导为".mod",并且其生成命令是 + ```makefile + $(M2C) $(M2FLAGS) $(MODFLAGS) + ``` +- 汇编和汇编预处理的隐含规则: ".o" 的目标的依赖目标会自动推导为".s",默认使用编译品"as",并且其生成命令是 + ```makefile + $(AS) $(ASFLAGS)" + ``` + - ".s" 的目标的依赖目标会自动推导为".S",默认使用C预编译器"cpp",并且其生成命令是 + ```makefile + "$(AS) $(ASFLAGS)"。 + ``` +- 链接Object文件的隐含规则: "" 目标依赖于".o",通过运行C的编译器来运行链接程序生成(一般是"ld"),其生成命令是 + ```makefile + $(CC) $(LDFLAGS) .o $(LOADLIBES) $(LDLIBS) + ``` + - 这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则: + ```makefile + x : y.o z.o + ``` + - 并且"x.c"、"y.c"和"z.c"都存在时,隐含规则将执行如下命令: + ```makefile + cc -c x.c -o x.o + cc -c y.c -o y.o + cc -c z.c -o z.o + cc x.o y.o z.o -o x + rm -f x.o + rm -f y.o + rm -f z.o + ``` + - 如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。 +- Yacc C程序时的隐含规则: ".c"的依赖文件被自动推导为"n.y"(Yacc生成的文件),其生成命令是 + ```makefile + $(YACC) $(YFALGS) + ``` +- Lex C程序时的隐含规则: ".c"的依赖文件被自动推导为"n.l"(Lex生成的文件),其生成命令是 + ```makefile + $(LEX) $(LFALGS) + ``` +- Lex Ratfor程序时的隐含规则: ".r"的依赖文件被自动推导为"n.l"(Lex生成的文件),其生成命令是 + ```makefile + $(LEX) $(LFALGS) + ``` +- 从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。 +".ln" (lint生成的文件)的依赖文件被自动推导为"n.c",其生成命令是:"$(LINT) $(LINTFALGS) $(CPPFLAGS) -i"。对于".y"和".l"也是同样的规则。 + +### 隐含规则使用的变量 +- 在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。当然,你也可以利用make的"-R"或"--no– builtin-variables"参数来取消你所定义的变量对隐含规则的作用。 +- 例如,第一条隐含规则 编译C程序的隐含规则的命令是"$(CC) –c $(CFLAGS) $(CPPFLAGS)"。Make默认的编译命令是"cc",如果你把变量"$(CC)"重定义成"gcc",把变量"$(CFLAGS)"重定义成 "-g",那么,隐含规则中的命令全部会以"gcc –c -g $(CPPFLAGS)"的样子来执行了。 +- 我们可以把隐含规则中使用的变量分成两种:一种是命令相关的,如"CC";一种是参数 +相的关,如"CFLAGS"。下面是所有隐含规则中会用到的变量: + - 关于命令的变量 + -AR 函数库打包程序。默认命令是"ar"。 + -AS 汇编语言编译程序。默认命令是"as"。 + -CC C语言编译程序。默认命令是"cc"。 + -CXX C++语言编译程序。默认命令是"g++"。 + -CO 从 RCS文件中扩展文件程序。默认命令是"co"。 + -CPP C程序的预处理器(输出是标准输出设备)。默认命令是"$(CC) –E"。 + -FC Fortran 和 Ratfor 的编译器和预处理程序。默认命令是"f77"。 + -GET 从SCCS文件中扩展文件的程序。默认命令是"get"。 + -LEX Lex方法分析器程序(针对于C或Ratfor)。默认命令是"lex"。 + -PC Pascal语言编译程序。默认命令是"pc"。 + -YACC Yacc文法分析器(针对于C程序)。默认命令是"yacc"。 + -YACCR Yacc文法分析器(针对于Ratfor程序)。默认命令是"yacc –r"。 + -MAKEINFO 转换Texinfo源文件(.texi)到Info文件程序。默认命令是"makeinfo"。 + -TEX 从TeX源文件创建TeX DVI文件的程序。默认命令是"tex"。 + -TEXI2DVI 从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是"texi2dvi"。 + -WEAVE 转换Web到TeX的程序。默认命令是"weave"。 + -CWEAVE 转换C Web 到 TeX的程序。默认命令是"cweave"。 + -TANGLE 转换Web到Pascal语言的程序。默认命令是"tangle"。 + -CTANGLE 转换C Web 到 C。默认命令是"ctangle"。 + -RM 删除文件命令。默认命令是"rm –f"。 + - 关于命令参数的变量,下面的这些变量都是相关上面的命令的参数。如果没有指明其默认值,那么其默认值都是空 + - ARFLAGS 函数库打包程序AR命令的参数。默认值是"rv"。 + - ASFLAGS 汇编语言编译器参数。(当明显地调用".s"或".S"文件时)。 + - CFLAGS C语言编译器参数。 + - CXXFLAGS C++语言编译器参数。 + - COFLAGS RCS命令参数。 + - CPPFLAGS C预处理器参数。( C 和 Fortran 编译器也会用到)。 + - FFLAGS Fortran语言编译器参数。 + - GFLAGS SCCS "get"程序参数。 + - LDFLAGS 链接器参数。(如:"ld") + - LFLAGS Lex文法分析器参数。 + - PFLAGS Pascal语言编译器参数。 + - RFLAGS Ratfor 程序的Fortran 编译器参数。 + - YFLAGS Yacc文法分析器参数。 + +### 隐含规则链 +- 有些时候,一个目标可能被一系列的隐含规则所作用。例如,一个[.o]的文件生成,可能会是先被Yacc的[.y]文件先成[.c],然后再被C的编译器生成。我们把这一系列的隐含规则叫做"隐含规则链"。 +- 在上面的例子中,如果文件[.c]存在,那么就直接调用C的编译器的隐含规则,如果没有[.c]文件,但有一个[.y]文件,那么Yacc的隐含规则会被调用,生成[.c]文件,然后,再调用C编译的隐含规则最终由[.c]生成[.o]文件,达到目标。 +- 在默认情况下,对于中间目标,它和一般的目标有两个地方所不同:第一个不同是除非中间的目标不存在,才会引发中间规则。第二个不同的是,只要目标成功产生,那么,产生最终目标过程中,所产生的中间目标文件会被以"rm -f"删除。 +- 通常,一个被makefile指定成目标或是依赖目标的文件不能被当作中介。然而,你可以明显地说明一个文件或是目标是中介目标,你可以使用伪目标".INTERMEDIATE"来强制声明。(如:.INTERMEDIATE : mid ) +- 你也可以阻止make自动删除中间目标,要做到这一点,你可以使用伪目标".SECONDARY"来强制声明(如:.SECONDARY : sec)。你还可以把你的目标,以模式的方式来指定(如:%.o)成伪目标".PRECIOUS"的依赖目标,以保存被隐含规则所生成的中间文件。 +- 在"隐含规则链"中,禁止同一个目标出现两次或两次以上,这样一来,就可防止在make自动推导时出现无限递归的情况。 +- Make 会优化一些特殊的隐含规则,而不生成中间文件。如,从文件"foo.c"生成目标程序"foo",按道理,make会编译生成中间文件"foo.o",然后链接成"foo",但在实际情况下,这一动作可以被一条"cc"的命令完成(cc –o foo foo.c),于是优化过的规则就不会生成中间文件。 + +### 定义模式规则 +- 你可以使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有"%"字符。"%"的意思是表示一个或多个任意字符。在依赖目标中同样可以使用"%",只是依赖目标中的"%"的取值,取决于其目标。 +- 有一点需要注意的是,"%"的展开发生在变量和函数的展开之后,变量和函数的展开发生在make载入Makefile时,而模式规则中的"%"则发生在运行时。 + +##### 模式规则介绍 +- 模式规则中,至少在规则的目标定义中要包含"%",否则,就是一般的规则。目标中的"%"定义表示对文件名的匹配,"%"表示长度任意的非空字符串。例如:"%.c"表示以".c"结尾的文件名(文件名的长度至少为3),而"s.%.c"则表示以"s."开头,".c"结尾的文件名(文件名的长度至少为 5)。 +- 如果"%"定义在目标中,那么,目标中的"%"的值决定了依赖目标中的"%"的值,也就是说,目标中的模式的"%"决定了依赖目标中"%"的样子。例如有一个模式规则如下: + ```makefile + %.o : %.c ; + ``` +- 其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是"a.o b.o",那么"%c"就是"a.c b.c"。 +- 一旦依赖目标中的"%"模式被确定,那么,make会被要求去匹配当前目录下所有的文件名,一旦找到,make就会规则下的命令,所以,在模式规则中,目标可能会是多个的,如果有模式匹配出多个目标,make就会产生所有的模式目标,此时,make关心的是依赖的文件名和生成目标的命令这两件事。 + +##### 模式规则示例 +- 下面这个例子表示了,把所有的[.c]文件都编译成[.o]文件. + ```makefile + %.o : %.c + $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ + ``` +- 其中,"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。这些奇怪的变量我们叫"自动化变量",后面会详细讲述。 +- 下面的这个例子中有两个目标是模式的: + ```makefile + %.tab.c %.tab.h: %.y + bison -d $< + ``` +- 这条规则告诉make把所有的[.y]文件都以"bison -d .y"执行,然后生成".tab.c"和".tab.h"文件。(其中,"" 表示一个任意字符串)。如果我们的执行程序"foo"依赖于文件"parse.tab.o"和"scan.o",并且文件"scan.o"依赖于文件"parse.tab.h",如果"parse.y"文件被更新了,那么根据上述的规则,"bison -d parse.y"就会被执行一次,于是,"parse.tab.o"和"scan.o"的依赖文件就齐了。(假设,"parse.tab.o" 由"parse.tab.c"生成,和"scan.o"由"scan.c"生成,而"foo"由"parse.tab.o"和"scan.o"链接生成,而且foo和其[.o]文件的依赖关系也写好,那么,所有的目标都会得到满足) + +##### 自动化变量 +- 在上述的模式规则中,目标和依赖文件都是一系例的文件,那么我们如何书写一个命令来完成从不同的依赖文件生成相应的目标?因为在每一次的对模式规则的解析时,都会是不同的目标和依赖文件。 +- 自动化变量就是完成这个功能的。在前面,我们已经对自动化变量有所提涉,相信你看到这里已对它有一个感性认识了。所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。 +- 下面是所有的自动化变量及其说明: + - $@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。 + - $% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。 + - $< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 + - $? 所有比目标新的依赖目标的集合。以空格分隔。 + - $^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。 + - $+ 这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。 + - $* 这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir /a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是 make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。 +- 当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有一个函数库文件叫"lib",其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是: + ```makefile + lib : foo.o bar.o lose.o win.o + ar r lib $? + ``` +- 在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D"或"F"字样。这是GNU make中老版本的特性,在新版本中,我们使用函数"dir"或"notdir"就可以做到了。"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。 +- 下面是对于上面的七个变量分别加上"D"或是"F"的含义: + - $(@D) 表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。 + - $(@F) 表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"。 + - "$(*D)" "$(*F)" 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,"$(*D)"返回"dir",而"$(*F)"返回"foo" + - "$(%D)" "$(%F)" 分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。 + - "$(> /etc/ld.so.conf +ldconfig +``` + diff --git a/content/post/mariadb-galera.md b/content/post/mariadb-galera.md new file mode 100644 index 0000000..5970127 --- /dev/null +++ b/content/post/mariadb-galera.md @@ -0,0 +1,144 @@ +--- +title: "CentOS7 部署 Mariadb Galera 集群" +date: 2019-10-30T14:01:06+08:00 +lastmod: 2019-10-30T14:01:06+08:00 +tags: ["mariadb", "galera"] +categories: ["database"] +--- + +# 环境 +cpu | mem | hostname | public ip | cluster ip | CentOS | MariaDB +---- | ---- | ---- | ---- | ---- | ---- | ---- +双核 | 2GB | mariadb_1 | 10.0.0.231 | 10.10.10.1 | 7.5 | 10.1.33 +双核 | 2GB | mariadb_2 | 10.0.0.232 | 10.10.10.2 | 7.5 | 10.1.33 +双核 | 2GB | mariadb_3 | 10.0.0.233 | 10.10.10.3 | 7.5 | 10.1.33 + + +# 安装数据库 +### 离线安装 +- [下载 rpm](http://yum.mariadb.org/10.1/centos7-amd64/rpms/) + - MariaDB-10.1.33-centos7-x86_64-client.rpm + - MariaDB-10.1.33-centos7-x86_64-common.rpm + - MariaDB-10.1.33-centos7-x86_64-server.rpm + - MariaDB-10.1.33-centos7-x86_64-shared.rpm + - galera-25.3.23-1.rhel7.el7.centos.x86_64.rpm + - jemalloc-3.6.0-1.el7.x86_64.rpm +- 安装 rpm + ```bash + yum erase mariadb-libs + rpm -ivh *.rpm + ``` +- 安装后会自动执行数据库初始化脚本,如果未执行,可手动运行 + ```bash + mysql_install_db --user=mysql + ``` + +### yum 安装 +- 创建 MariaDB-10.1 的 yum 源文件(就近选择一个) + ```bash + #官方 + cat > /etc/yum.repos.d/MariaDB.repo <<-END + [mariadb] + name = MariaDB + baseurl = http://yum.mariadb.org/10.1/centos7-amd64 + gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB + gpgcheck=1 + END + #中科大 + cat > /etc/yum.repos.d/MariaDB.repo <<-END + [mariadb] + name = MariaDB + baseurl = https://mirrors.ustc.edu.cn/mariadb/yum/10.1/centos7-amd64 + gpgkey=https://mirrors.ustc.edu.cn/mariadb/yum/RPM-GPG-KEY-MariaDB + gpgcheck=1 + END + #上海大学 + cat > /etc/yum.repos.d/MariaDB.repo <<-END + [mariadb] + name = MariaDB + baseurl = https://mirrors.shu.edu.cn/mariadb/yum/10.1/centos7-amd64 + gpgkey=https://mirrors.shu.edu.cn/mariadb/yum/RPM-GPG-KEY-MariaDB + gpgcheck=1 + END + ``` +- 安装 MariaDB + ```bash + yum install MariaDB-client MariaDB-server + # galera_4 会作为依赖自动安装 + ``` + +# 启动server +```bash +systemctl start mariadb +``` + +# 安全设置 +- 设置root账户密码(推荐) + ```bash + mysqladmin -u root password 'password' + ``` +- 数据库安全设置(推荐) + ```bash + mysql_secure_installation + ``` + +# 配置Galera Cluster +- 修改/etc/my.cnf.d/server.cnf如下 + ``` + [server] + innodb-flush-log-at-trx-commit=0 + innodb-buffer-pool-size=1024M #一半内存 + innodb-autoinc-lock-mode=2 + default-storage-engine=InnoDB + # + [mysqld] + # + [galera] + wsrep-on=ON + wsrep-provider = /usr/lib64/galera/libgalera_smm.so + wsrep-provider-options="gcache.dir=/var/lib/gcache;gcache.size=1G;gcache.recover=yes;pc.recovery=TRUE" + wsrep-cluster-name="mariadb_galera_cluster" + wsrep-cluster-address = "gcomm://10.10.10.1,10.10.10.2,10.10.10.3" + wsrep-node-name = mariadb_1 #当前节点名字 + wsrep-node-address = 10.10.10.1 #当前节点地址 + binlog-format=ROW + wsrep-slave-threads=2 + wsrep-sst-method=rsync + #wsrep-auto-increment-control=OFF #只通过一个节点做增删改时使用 + # + [embedded] + # + [mariadb] + # + [mariadb-10.1] + ``` + +# 停止 server + ```bash + systemctl stop mariadb + ``` + +# 启动集群 +- 启动 galera cluster + ```bash + mysqld --wsrep-new-cluster --user=mysql + ``` +- 查看集群状态 + ```sql + show status like 'wsrep_%'; + ``` +- 在剩余两台服务器启动 server,向集群中添加节点 + ```bash + systemctl start mariadb + ``` +- 再次查看集群状态 + ```sql + show status like 'wsrep_%'; + ``` + +# 注意事项 +- 防火墙开放 3306、4444 和 4567 端口 +- 关闭 selinux +- 集群关闭时,/var/lib/mysql/grastate.dat 文件中 safe_to_bootstrap 项为 1 的节点服务器是最后关闭的数据库,数据最全,所以下次集群启动时应从这台节点服务器启动 + + diff --git a/content/post/mariadb-replication.md b/content/post/mariadb-replication.md new file mode 100644 index 0000000..2644f05 --- /dev/null +++ b/content/post/mariadb-replication.md @@ -0,0 +1,130 @@ +--- +title: "MariaDB 主从复制" +date: 2020-04-13T18:00:00+08:00 +lastmod: 2020-04-13T18:00:00+08:00 +tags: ["mariadb", "master", "slave", "gtid", "replication", "同步"] +categories: ["database"] +--- + +# 环境 +- mariadb 主服务器,centos7.8,192.168.1.141 +- mariadb 主服务器,centos7.8,192.168.1.142 +- mariadb 从服务器,centos7.8,192.168.1.143 +- 忽略系统数据库: information_schema, mysql, performance_schema + +# 在两个主服务器上创建用于备份的用户 replicator +```sql +grant replication slave on *.* to 'replicator'@'%' identified by 'password'; +flush privileges; +``` + +# 在两个主服务器上修改 my.cnf +``` +# 唯一 ID +# 192.168.1.141 配置 +server-id = 141 +# 192.168.1.142 配置 +server-id = 142 +read-only = 0 + +# binlog +# MariaDB 默认已开启 GTID +log-bin = /var/lib/mysql-bin/master +binlog-format = row +sync_binlog = 1 +expire-logs-days = 3 +gtid-strict-mode = ON + +# relay log +replicate-wild-ignore-table = information_schema.% +replicate-wild-ignore-table = init_sql.% +replicate-wild-ignore-table = mysql.% +replicate-wild-ignore-table = performance_schema.% +relay-log = /var/lib/mysql-bin/slave +relay-log-recovery = TRUE +# 复制线程数不超过 cpu 核数 +slave-parallel-threads = 4 + +# 双主或多主互备时,可能会用到以下配置 +# 自增主键初始值,与其他互备服务器一致 +#auto-increment-offset = +# 自增主键等差值,与其他互备服务器均不一致 +#auto-increment-increment = +# 该环境中,双主配合 keepalived 实现高可用,无需配置自增 +``` + +# 开启互主同步 +```sql +-- 在 192.168.1.141 上启动 mariadb,配置同步 192.168.1.142: +change master to master_host = '192.168.1.142', + master_port = 3306, + master_user = 'replicator', + master_password = 'password', + master_use_gtid = slave_pos; + +-- 在 192.168.1.142 上启动 mariadb,配置同步 192.168.1.141: +change master to master_host = '192.168.1.141', + master_port = 3306, + master_user = 'replicator', + master_password = 'password', + master_use_gtid = slave_pos; +``` + +# 在两个主服务器上启动 slave ,查看 slave 状态 +```sql +start slave; +-- 查看 slave 状态 +show slave status\G +-- 如果看到 +-- Slave_IO_Running: Yes +-- Slave_SQL_Running: Yes +-- 则表示 slave 开启成功! +``` + +# 在从服务器上编辑 my.cnf +``` +# 唯一 ID +server-id = 143 +# 从服务器只做查询,无增删改 +read-only = 1 + +# 忽略的数据表 +#replicate-ignore-table = db1.t1 +replicate-wild-ignore-table = information_schema.% +replicate-wild-ignore-table = init_sql.% +replicate-wild-ignore-table = mysql.% +replicate-wild-ignore-table = performance_schema.% +relay-log = /var/log/mysql-bin/slave +slave-parallel-threads = 4 +``` + +# 开启同步 +```sql +change master 'db141' to master_host = '192.168.1.141', + master_port = 3306, + master_user = 'replicator', + master_password = 'password', + master_use_gtid = slave_pos; + +change master 'db142' to master_host = '192.168.1.142', + master_port = 3306, + master_user = 'replicator', + master_password = 'password', + master_use_gtid = slave_pos; +``` + +# 在从服务器上启动 slave ,查看 slave 状态 +```sql +start all slaves; +-- 在从服务器上查看 slave 状态 +show all slaves status\G +-- 如果看到 +-- Slave_IO_Running: Yes +-- Slave_SQL_Running: Yes +-- 则表示 slave 开启成功! +``` + +# 参考 +- [MariaDB 复制](https://mariadb.com/kb/en/standard-replication/) +- [MariaDB 多源复制](https://mariadb.com/kb/en/multi-source-replication/) + diff --git a/content/post/minio.md b/content/post/minio.md new file mode 100644 index 0000000..55271cd --- /dev/null +++ b/content/post/minio.md @@ -0,0 +1,210 @@ +--- +title: "Minio 笔记" +date: 2021-10-17T00:48:00+08:00 +lastmod: 2021-10-17T00:48:00+08:00 +keywords: [] +tags: ["minio"] +categories: ["storage"] +--- + +# 部署单节点分布式存储 +## 环境 +- 服务器 IP: 192.168.1.10 +- 自定义域名: X.X.X + +## 安装 minio +- 下载 + ```bash + curl -L -o /usr/local/bin/minio https://dl.min.io/server/minio/release/linux-amd64/minio + curl -L -o /usr/local/bin/mc https://dl.min.io/client/mc/release/linux-amd64/mc + chmod 0755 /usr/local/bin/{minio,mc} + ``` + +- 修改 hosts,增加自定义域名解析 + ``` + 192.168.1.10 X.X.X + ``` + +- 创建四个目录,用于存储 minio 数据 + ```bash + mkdir -p /minio/{1,2,3,4} + ``` + +- 创建 /etc/systemd/system/minio.service,内容如下 + ```ini + [Unit] + Description=MinIO + After=network.service + Wants=network.service + + [Service] + Environment=MINIO_ROOT_USER=XXX + Environment=MINIO_ROOT_PASSWORD=XXXXXXXX + ExecStart=/usr/local/bin/minio server \ + --console-address ":9001" \ + http://X.X.X:9000/minio/{1...4} + #Restart=on-failure + RestartSec=10s + + [Install] + WantedBy=multi-user.target + ``` + +## 启动 minio +- 配置开机自动启动 + ```bash + systemctl daemon-reload + systemctl enable minio + ``` + +- 启动 minio + ```bash + systemctl start monio + ``` + +# 部署多节点分布式存储 +## 环境 +自定义域名 | 网卡 IP | 硬盘槽 | 数据分区 +---- | ---- | ---- | ---- +X1.X.X | eth0: 192.168.1.11
eth1:10.0.0.11 | 8 个 | /dev/sdb1, /dev/sdc1
/dev/sdd1, /dev/sde1 +X2.X.X | eth0: 192.168.1.12
eth1:10.0.0.12 | 8 个 | /dev/sdb1, /dev/sdc1
/dev/sdd1, /dev/sde1 +X3.X.X | eth0: 192.168.1.13
eth1:10.0.0.13 | 8 个 | /dev/sdb1, /dev/sdc1
/dev/sdd1, /dev/sde1 +X4.X.X | eth0: 192.168.1.14
eth1:10.0.0.14 | 8 个 | /dev/sdb1, /dev/sdc1
/dev/sdd1, /dev/sde1 + +- eth0 用于接收 api 请求 +- eth1 用于集群内部通信 +- 每台服务器有 8 个硬盘槽,这里先各用 4 个组成 server pool,运行 minio 集群 +- 每台服务器剩下的 4 个硬盘槽用于后面的扩容操作 + +## 挂载数据分区 +- 在全部服务器上执行如下操作 +- 挂载硬盘 + ```bash + mkdir -p /mnt/minio{1,2,3,4} + mount /dev/sdb1 /mnt/minio1 + mount /dev/sdc1 /mnt/minio2 + mount /dev/sdd1 /mnt/minio3 + mount /dev/sde1 /mnt/minio4 + ``` + +- 建议在 fstab 中配置开机自动挂载 + +## 安装 minio +- 在全部服务器上执行如下操作 +- 下载 + ```bash + curl -L -o /usr/local/bin/minio https://dl.min.io/server/minio/release/linux-amd64/minio + curl -L -o /usr/local/bin/mc https://dl.min.io/client/mc/release/linux-amd64/mc + chmod 0755 /usr/local/bin/{minio,mc} + ``` + +- 修改 hosts,增加自定义域名解析,**注意这里是集群通信,要用 eth1 网卡 ip** + ``` + 10.0.0.11 X1.X.X + 10.0.0.12 X2.X.X + 10.0.0.13 X3.X.X + 10.0.0.14 X4.X.X + ``` + +- 创建 /etc/systemd/system/minio.service,内容如下 + ```ini + [Unit] + Description=MinIO + After=network.service + Wants=network.service + + [Service] + Environment=MINIO_ROOT_USER=XXX + Environment=MINIO_ROOT_PASSWORD=XXXXXXXX + ExecStart=/usr/local/bin/minio server \ + --console-address ":9001" \ + http://X{1...4}.X.X:9000/mnt/minio{1...4} + #Restart=on-failure + RestartSec=10s + + [Install] + WantedBy=multi-user.target + ``` + +## 启动 minio +- 在全部服务器上执行如下操作 +- 配置开机自动启动 + ```bash + systemctl daemon-reload + systemctl enable minio + ``` + +- 启动 minio + ```bash + systemctl start monio + ``` + +# 扩容多节点分布式存储 +## 加装硬盘 +- 在全部服务器上执行如下操作 +- 在每台服务器上个增加四个新硬盘,每个硬盘创建一个分区,格式化 +- 挂载新增的硬盘分区 + ```bash + mkdir -p /mnt/minio{5,6,7,8} + mount /dev/sdf1 /mnt/minio5 + mount /dev/sdg1 /mnt/minio6 + mount /dev/sdh1 /mnt/minio7 + mount /dev/sdi1 /mnt/minio8 + ``` + +- 建议在 fstab 中配置开机自动挂载 + +## 扩容 +- 在全部服务器上执行如下操作 +- 停止 minio 服务 + ```bash + systemctl stop minio + ``` + +- 修改 /etc/systemd/system/minio.service,增加新的 server pool,内容如下 + ```ini + [Unit] + Description=MinIO + After=network.service + Wants=network.service + + [Service] + Environment=MINIO_ROOT_USER=XXX + Environment=MINIO_ROOT_PASSWORD=XXXXXXXX + ExecStart=/usr/local/bin/minio server \ + --console-address ":9001" \ + http://X{1...4}.X.X:9000/mnt/minio{1...4} \ + http://X{1...4}.X.X:9000/mnt/minio{5...8} + #Restart=on-failure + RestartSec=10s + + [Install] + WantedBy=multi-user.target + ``` + +- 启动 minio 服务 + ```bash + systemctl start minio + ``` + +# 客户端常用操作 +- 创建 alias,**注意这里是 api 调用,要用 eth0 网卡 ip** + ```bash + mc alias set XXX http://192.168.1.X:9000 XXX XXXXXXXX + ``` + +- 创建 bucket + ```bash + mc mb XXX/XXX + ``` + +- 创建用户 + ```bash + mc admin user add XXX XXXXXXXX + ``` + +- 给新增用户授权 + ```bash + mc admin policy set XXX readwrite user=XXX + ``` + diff --git a/content/post/mongodb.md b/content/post/mongodb.md new file mode 100644 index 0000000..9d4683d --- /dev/null +++ b/content/post/mongodb.md @@ -0,0 +1,217 @@ +--- +title: "Mongodb 笔记" +date: 2019-10-30T10:56:11+08:00 +lastmod: 2019-10-30T10:56:11+08:00 +keywords: [] +tags: ["mongodb"] +categories: ["database"] +--- + +# 数据迁移 +### 导出集合数据到 json 或 csv 文件 +- 命令 + ``` + mongoexport -d dbname -c collectionname -o file --type json/csv -f "field1,field2,...,fieldN" + ``` +- 参数 + - -d 数据库名 + - -c 集合名 + - -o 输出文件名 + - --type 输出格式,默认 json + - -f 输出字段,如果导出 csv,需指定字段名 + +### 导入数据到集合 +- 命令 + ``` + mongoimport -d dbname -c collectionname --file filename --headerline --type json/csv -f "field1,field2,...,fieldN" + ``` +- 参数 + - -d 数据库名 + - -c 集合名 + - --type 导入格式,默认 json + - -f 导入的字段名 + - --headerline 导入csv时,使用第一行的标题作为导入字段 + - --file 导入的文件 + +### 备份数据库 +- 命令 + ``` + mongodump -h dbhost -d dbname -o backup_dir + ``` +- 参数 + - -h ip[:port] + - -d 数据库名 + - -o 备份数据存放目录,需提前建立 + +### 恢复数据库 +- 命令 + ``` + mongorestore -h dbhost -d dbname --dir backup_dir + ``` +- 参数 + -h ip[:port] + -d 数据库名 + --dir 备份数据存放目录 + --drop 恢复前,先删除当前数据 + + +# 操作数据库 +- 默认数据库 test +- 创建 use dbname +- 查看当前选择的数据库 db +- 查看数据库列表 show dbs,不显示空数据库 +- 删除数据库 use dbname; db.dropDatabase() + +# 操作集合 +### 创建 +``` +db.createCollection("集合名", {capped:false, autoIndexId:false, size:最大字节, max:文档最多数量}) +# 或者在插入文档时自动创建 +db.集合名.inert({}) +``` + +### 删除 +``` +db.集合名.drop() +``` + +# 操作文档 +### insert +- 插入一个文档 + ``` + db.集合名.insert({}) + # 或者 + db.集合名.insertOne({}) + ``` +- 插入多个文档 + ``` + db.集合名.insert([{},{},{},...,{}]) + # 或者 + db.集合名.insertMany([{},{},{},...,{}]) + ``` + +### find +- 查询匹配的文档 + ``` + db.集合名.find({}).pretty() + ``` +- 查询,只返回一个文档 + ``` + db.集合名.findOne({}) + ``` + - 相等 {"key":"value"} + - 小于 {"key":{$lt:"value"}} + - 小于等于 {"key":{$lte:"value"}} + - 大于 {"key":{$gt:"value"}} + - 大于等于 {"key":{$gte:"value"}} + - 不等于 {"key":{$ne:"value"}} + - and {$and:[{},{}]} + - or {$or:[{},{}]} +- 投影 + ``` + db.集合名.find({},{"key1":1,"key2":0 ...}) + ``` +- 限制返回文档数量 + ``` + db.集合名.find({}).limit(N) + ``` +- 跳过返回文档数量 + ``` + db.集合名.find({}).limit(N).skip(N) + ``` +- 排序,1升序,-1降序 + ``` + db.集合名.find({}).sort({"key":1}) + ``` + +### update +- 更新一个文档 + ``` + db.集合名.update({},{$set:{}}) + ``` +- 更新多个文档 + ``` + db.集合名.update({},{$set:{},{multi:true}}) + ``` + +### save +``` +db.集合名.save({,...}) +``` + +### remove +- 删除多条记录 + ``` + db.集合名.remove({}) + ``` +- 删除一条记录 + ``` + db.集合名.remove({},1) + ``` +- 删除集合中的所有文档,相当于 truncate + ``` + db.集合名.remove() + ``` + +### 索引 +- 创建索引,1升序,-1降序 + ``` + db.集合名.createIndex({"key1":1,"key2":-1}) + ``` + +### 聚合 +``` +db.集合名.aggregate([{}]) +``` + +# 用户 +### 创建用户 +- 管理员权限 + ``` + use admin + db.createUser( + { + user: "username", + pwd: "password", + roles: [{role: "root", db: "admin"}] + } + ) + ``` +- 测试库读写权限 + ``` + use testdb + db.createUser( + { + user: "username", + pwd: "password", + roles: ["readWrite"] + } + ) + ``` + +### 验证身份 +``` +db.auth("username", "password") +``` + +### 列出所有用户 +``` +db.getUsers() +``` + +### 删除数据库用户 +``` +use testdb +db.dropUser("username") +``` + +### 更改用户密码 +``` +db.updateUser( + "username", + { + pwd: "new_password" + } +) +``` + diff --git a/content/post/mysql-binlog.md b/content/post/mysql-binlog.md new file mode 100644 index 0000000..c65ab66 --- /dev/null +++ b/content/post/mysql-binlog.md @@ -0,0 +1,86 @@ +--- +title: "Mysql 二进制日志" +date: 2019-10-30T11:05:08+08:00 +lastmod: 2019-10-30T11:05:08+08:00 +keywords: [] +tags: ["mysql", "binlog"] +categories: ["database"] +--- + +# MySQL 5.7 开启 binlog +- 修改 my.cnf 文件 + ``` + [mysqld] + log-bin=[/存放目录/]mysql-bin #注意 mysql 可读写“存放目录”,默认数据存放目录 + expire-logs-days=7 #保留7天内修改过的 binglog 文件 + max-binlog-size=512M #单个 binlog 文件大小上限,默认1G + #指定或忽略要复制的数据库,存在跨库问题 + binlog-do-db=db1 + binlog-db-db=db2 + #binlog-ignore-db=db1 + #binlog-ignore-db=db2 + ``` + +# 常用操作 +- 查看所有 binlog 文件列表 + ```sql + show master logs; + ``` +- 查看 master 状态,包含最新 binlog 文件名和 position + ```sql + show master status; + ``` +- 清除过期 binlog 文件,并使用新编号的 binlog 文件开始记录日志 + ```sql + flush logs; + ``` +- 删除 binlog 文件 + - 删除旧的 binlog 文件 + ```sql + purge master logs to 'mysql-bin.000573'; + purge master logs before '2018-04-18 06:00:00'; + purge master logs before DATE_SUB(NOW(), INTERVAL 2 DAY); + ``` + - 清空所有 binlog 文件 + ```sql + reset master + ``` + +# 使用 mysqlbinlog 命令查看 binlog 文件的内容 +- 使用 + ```bash + # 查看日志 + mysqlbinlog [选项] binlog文件名 + # 恢复数据 + mysqlbinlog [选项] binlog文件名 | mysql -u用户名 -p密码 -D数据库 [-v] + ``` +- 常用选项 + - --start-position=128 起始 pos + - --stop-position=256 结束 pos + - --start-datetime="2018-08-08 00:00:00" 起始时间 + - --stop-datetime="2018-08-09 12:00:00" 结束时间 + - --database=db_name 只恢复 db_name 数据库 + +# 使用 sql 查看 binlog 文件的内容 +- 查询语句 + ```sql + SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]; + ``` +- 选项 + - log_name binlog文件名,默认第一个 binlog 文件 + - pos 查询起始 pos,默认 log_name 中的第一个 pos + - offset 偏移 pos 个数 + - row_count 查询数量 + +# 调整 binlog_cache_size +- 查看当前 binlog_cache_size 大小(byte),默认 32k + ```sql + show variables like 'binlog_cache_size'; + ``` +- 查看当前 binlog_cache_use 和 binlog_cache_disk_use 次数 + ```sql + show status like 'binlog_cache%'; + -- binlog_cache_disk_use 使用临时文件写 binlog 文件的次数 + -- binlog_cache_use 使用缓存写 binlog 文件的次数 + ``` + diff --git a/content/post/mysql-cache.md b/content/post/mysql-cache.md new file mode 100644 index 0000000..32c3ddc --- /dev/null +++ b/content/post/mysql-cache.md @@ -0,0 +1,105 @@ +--- +title: "Mysql 缓存" +date: 2019-10-30T11:00:49+08:00 +lastmod: 2019-10-30T11:00:49+08:00 +keywords: [] +tags: ["cache", "mysql", "缓存"] +categories: ["database"] +--- + +# MySQL 超时 +- connect_timeout 与客户端连接建立超时,默认10秒 +- interactive_timeout 交互终端超时断开,默认28800秒 +- wait_timeout 非交互终端超时断开,默认28800秒 +- net_read_timeout 从客户端读取数据超时,默认30秒 +- net_write_timeout 向客户端写入数据超时,默认60秒 +- innodb_lock_wait_timeout 锁等待超时,默认50秒 +- innodb_rollback_on_timeout 超时后回滚整个事务操作,默认OFF +- slave_net_timeout 从库读取binlog失败后,等待指定秒后重新连接主库,默认60秒 + +# 查询缓存配置 +** MySQL 8.0 已关闭该功能 ** +- have_query_cache 是否支持查询缓存,默认yes +- query_cache_limit select结果集大于该值时不缓存,默认1048576 +- qeury_cache_min_res_unit 查询缓存申请的内存单位(块)大小,默认4096,一般不改 +- query_cache_size 查询缓存大小,默认1048576 +- query_cache_type + - 0(OFF) 关闭查询缓存,默认 + - 1(ON) 如果select语句没有sql_no_cache选项,则使用查询缓存 + - 2(DEMOND) 如果select语句没有sql_cache选项,则不使用查询缓存 +- query_cache_wlock_invalidate + - 0(OFF) 锁表后,该表的查询缓存依旧有效,默认 + - 1(ON) 锁表后,该表的查询缓存失效 + +# 查询缓存状态 +** MySQL 8.0 已关闭该功能 ** +- Qcache_free_blocks 查询缓存中的内存碎片数量 + - 碎片过多表明查询结果集较小,可适当减小query_cache_min_res_unit + - 整理查询缓存中的碎片,获得一个较大的内存空闲空间 + ```sql + flush query cache + ``` + - 缓存碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100% +- Qcache_free_memory 查询缓存中的可用内存 +- Qcache_hits 使用查询缓存的次数 +- Qcache_inserts 查询缓存中缓存过的select结果集数量(包括当前正在缓存的) +- Qcache_lowmen_prunes 因查询缓存已满而删除的select结果集数量 + - 该值较大表明查询缓存较小 +- Qcache_not_cached 没有进入查询缓存的select语句个数 +- Qcache_queries_in_cache 查询缓存中当前缓存着select结果集数量 +- Qcache_total_blocks 查询缓存中的块总数 +- Com_select 执行过的select语句数量 +- 查询缓存的命中率 = Qcache_hits / Com_select * 100% + +# 排序缓存配置 +- sort_buffer_size 排序缓存大小,默认262144 +- max_length_for_sort_data 排序sql中的全部字段长度总和小于该值时,使用优化排序方式,否则使用常规排序方式,默认1024 + - [参考](https://www.cnblogs.com/moss_tan_jun/p/6021822.html) +- max_sort_length 使用列的前(默认1024)个字节排序 +- innodb_sort_buffer_size InnoDB用于创建/重建索引的排序缓存,默认1048576 + +# 排序缓存状态 +- Sort_merge_passes 使用临时文件完成排序操作的次数 + - 增加sort_buffer_size可减少使用临时文件排序的次数 +- Sort_range 使用范围排序的次数 +- Sort_rows 已经排序的记录行数 +- Sort_scan 通过全表扫描完成排序的次数 + +# join 连接缓存 +- join_buffer_size 两表join但不能使用索引时,为每张表分配的连接缓存,默认262144 + - 尽量实现索引join + +# 表/表结构 缓存配置 +- table_open_cache 可以缓存表和视图的数量 +- table_defination_cache 可以存储的表结构数量 +- open_files_limit 可以打开的文件数量 + +# InnoDB 重做日志缓存配置 +- innodb_log_buffer_size 事务提交前,把重做日志写入缓存中 +- innodb_flush_log_at_trx_commit + - 0 每隔一秒,把日志缓存写到日志文件,并刷新到磁盘 + - 1 每次提交时,把日志缓存写到日志文件,并刷新到磁盘 + - 2 每次提交是,把日志缓存写到日志文件,每隔一秒刷新到磁盘 + +# InnoDB 预读配置 +- innodb_read_ahead_threshold 预读的前后数据库个数 + +# FLUSH 语句 +- HOSTS 清空主机表 +- LOGS 关闭binlog文件,创建新编号binlog文件 +- PRIVILEGES 从数据库授权表中重新装载权限到缓存中 +- TABLES 关闭打开的表,清空查询缓存 +- QUERY CACHE 整理碎片,不影响查询缓存中现有数据 + - 清空查询缓存 + ```sql + RESET QUERY CACHE + ``` +- TABLES WITH READ LOCK 关闭打开的表并加一个只读锁 + - 解锁 + ```sql + UNLOCK TABLES + ``` +- STATUS 重置大多数状态变量到0 +- MASTER(RESET MASTER) 删除全部binlog文件,重建一个新的binlog文件 +- SLAVE(RESET SLAVE) 忘记主库binlog文件位置,删除relay log + diff --git a/content/post/mysql-galera.md b/content/post/mysql-galera.md new file mode 100644 index 0000000..2073c12 --- /dev/null +++ b/content/post/mysql-galera.md @@ -0,0 +1,115 @@ +--- +title: "CentOS7 安装 Mysql Galera 集群" +date: 2019-10-30T11:13:44+08:00 +lastmod: 2019-10-30T11:13:44+08:00 +keywords: [] +tags: ["mysql", "galera"] +categories: ["database"] +--- + +# 环境 +cpu | mem | hostname | public ip | cluster ip | CentOS | MySQL +---- | ---- | ---- | ---- | ---- | ---- | ---- +双核 | 2GB | mysql_1 | 10.0.0.231 | 10.10.10.1 | 7.5 | 5.7 +双核 | 2GB | mysql_2 | 10.0.0.232 | 10.10.10.2 | 7.5 | 5.7 +双核 | 2GB | mysql_3 | 10.0.0.233 | 10.10.10.3 | 7.5 | 5.7 + +# 创建 galera yum 源文件 +```bash +cat > /etc/yum.repos.d/galera.repo <<-END +[galera] +name = Galera +baseurl = http://releases.galeracluster.com/galera-3/centos/7/x86_64/ +gpgkey = http://releases.galeracluster.com/GPG-KEY-galeracluster.com +gpgcheck = 1 +# +[mysql-wsrep] +name = MySQL-wsrep +baseurl = http://releases.galeracluster.com/mysql-wsrep-5.7/centos/7/x86_64/ +gpgkey = http://releases.galeracluster.com/GPG-KEY-galeracluster.com +gpgcheck = 1 +END +``` + +# 安装 +```bash +yum install galera-3 mysql-wsrep-5.7 rsync +``` + +# 修改 /etc/my.cnf +``` +[mysqld] +datadir=/var/lib/mysql +socket=/var/lib/mysql/mysql.sock +user=mysql +binlog-format=ROW +bind-address=0.0.0.0 +default-storage-engine=innodb +innodb-autoinc-lock-mode=2 +innodb-flush-log-at-trx-commit=0 +innodb-buffer-pool-size=1024M #物理内存一半 +wsrep-provider=/usr/lib64/galera-3/libgalera_smm.so +wsrep-provider-options="gcache.dir=/var/lib/gcache;gcache.size=1G;gcache.recover=yes;pc.recovery=TRUE" +wsrep-cluster-name="mysql_galera_cluster" #集群名字 +wsrep-cluster-address="gcomm://10.10.10.1,10.10.10.2,10.10.10.3" +wsrep-sst-method=rsync +wsrep-node-name=mysql_1 #当前节点名字 +wsrep-node-address="10.10.10.1" #当前节点 cluster ip +#wsrep-auto-increment-control=OFF #只通过一个节点做增删改时使用 +# +[mysql_safe] +log-error=/var/log/mysqld.log +pid-file=/var/run/mysqld/mysqld.pid +# +!includedir /etc/my.cnf.d/ +``` + +# 随机选择一个节点,使用专用脚本 mysqld_bootstrap 初始化集群 +```bash +/usr/bin/mysqld_bootstrap +#该命令会启动本机的 mysqld 服务 +systemctl status mysqld +``` + +# 查找密码,修改初始密码 +```bash +grep -i password /var/log/messages +#记录输出的密码 +mysqladmin -uroot -p password 'P@sswo2d' +#根据提示输入上一步输出的密码 +``` + +# 在其他节点上启动 mysqld 服务 +```bash +systemctl start mysqld +``` + +# 查看集群节点数量 +```sql +show status like 'wsrep_cluster_size'; +``` + +# ssl 加密同步数据(不推荐,存在性能损失) +- 生成证书 + ```bash + mkdir /etc/my.cnf.d/ssl && cd /etc/my.cnf.d/ssl + openssl genrsa 2048 > ca-key.pem + openssl req -new -x509 -nodes -days 365000 \ + -key ca-key.pem -out ca-cert.pem #按提示输入信息 + openssl req -newkey rsa:2048 -days 365000 \ + -nodes -keyout server-key.pem -out server-req.pem #按提示输入信息,与上一步信息不同 + openssl rsa -in server-key.pem -out server-key.pem + openssl x509 -req -in server-req.pem -days 365000 \ + -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 \ + -out server-cert.pem + ``` +- 修改配置文件 my.cnf + ``` + #在 wsrep_provider_options 中添加如下选项,选项间用分号";"间隔 + socket.ssl_key=/etc/my.cnf.d/ssl/server-key.pem; socket.ssl_cert=/etc/my.cnf.d/ssl/server-cert.pem; socket.ssl_ca=/etc/my.cnf.d/ssl/ca-cert.pem; socket.checksum=2; socket.ssl_cipher=AES128-SHA + ``` +- 重新启动集群 + +# ssl 加密客户端(不推荐,存在性能损失) +- MySQL 5.7 server 自带 ssl 加密,客户端连接时,指定参数 --ssl-mode=required 即可 + diff --git a/content/post/mysql-group-replication.md b/content/post/mysql-group-replication.md new file mode 100644 index 0000000..36a5729 --- /dev/null +++ b/content/post/mysql-group-replication.md @@ -0,0 +1,148 @@ +--- +title: "MySQL 组复制" +date: 2020-04-28T00:52:00+08:00 +lastmod: 2020-04-28T00:52:00+08:00 +tags: ["mysql", "group", "replication", "组复制"] +categories: ["database"] +--- + +# 环境 +hostname | ip | os | mysql +---- | ---- | ---- | ---- +mysql_11 | 192.168.1.11 | centos7.7 | 8.0.19 +mysql_22 | 192.168.1.22 | centos7.7 | 8.0.19 +mysql_33 | 192.168.1.33 | centos7.7 | 8.0.19 + +# 安装 mysql +- 懒得写了 ... + +# 修改 my.cnf +``` +# mysql_11(选择一个) +server-id = 11 +# mysql_22(选择一个) +server-id = 22 +# mysql_33(选择一个) +server-id = 33 + +# 开启 binlog +log-bin = /var/log/mysql-bin/master +binlog-format = ROW +# 关闭 binlog 校验 +binlog-checksum = NONE +# 保留 2 天的 binlog +binlog-expire-logs-seconds = 172800 +# 开启 gtid +gtid-mode = ON +enforce-gtid-consistency = TRUE +# 指定 relay-log 存储位置 +relay-log = /var/lib/mysql-bin/slave +# relay-log 更新计入 binlog +log-slave-updates = TRUE + +# 多线程执行从库日志(可选) +slave-parallel-workers = 2 +slave-parallel-type = LOGICAL_CLOCK +slave-preserve-commit-order = ON + +# 存储引擎只能用 InnoDB +disabled-storage-engines = "MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY" +# 加载插件,克隆插件用于快速 state transfer +plugin-load-add = "group_replication.so;mysql_clone.so" +# 集群 uuid +group-replication-group-name = "aaaa1111-bbbb-2222-cccc-3333dddd4444" + +# mysql_11(选择一个) +group-replication-local-address = "192.168.1.11:33061" +# mysql_22(选择一个) +group-replication-local-address = "192.168.1.22:33061" +# mysql_33(选择一个) +group-replication-local-address = "192.168.1.33:33061" + +# 种子节点 +group-replication-group-seeds = "192.168.1.11:33061,192.168.1.22:33061,192.168.1.33:33061" +# 新主库在执行完自己的从库日志后,再处理用户的写请求 +group-replication-consistency = BEFORE_ON_PRIMARY_FAILOVER +# 启动时,不自动创建/初始化新集群 +group-replication-bootstrap-group = OFF +# 新节点启动时,先不启动组复制,待手动配置完成并确认正常后,再把 OFF 改成 ON +group-replication-start-on-boot = OFF + +# 怀疑某节点不可用,2秒内,如果该嫌疑节点依旧无响应,则开除它(可选) +group-replication-member-expel-timeout = 2 +# 2秒内,依旧连接不上主网(majority),则退出组复制,进入 ERROR 状态(可选) +group-replication-unreachable-majority-timeout = 2 +# 退出组复制后,不再尝试重新加入组复制,直接执行指定的退出动作(默认) +group-replication-autorejoin-tries = 0 +# 指定退出动作: 数据库设置超级只读并关闭客户端连接(推荐) +group-replication-exit-state-action = OFFLINE_MODE +``` + +# 初始化集群 +- 重新启动节点 mysql_11 + ```bash + systemctl restart mysqld + ``` +- 创建同步用户 + ```sql + SET SQL_LOG_BIN = 0; + CREATE USER rpl_user@'%' IDENTIFIED BY 'password'; + GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%'; + GRANT BACKUP_ADMIN ON *.* TO rpl_user@'%'; + FLUSH PRIVILEGES; + SET SQL_LOG_BIN = 1; + ``` +- 配置同步信息 + ```sql + CHANGE MASTER TO + MASTER_USER='rpl_user', + MASTER_PASSWORD='password' + FOR CHANNEL 'group_replication_recovery'; + ``` +- 启动集群 + ```sql + SET GLOBAL group_replication_bootstrap_group=ON; + START GROUP_REPLICATION; + SET GLOBAL group_replication_bootstrap_group=OFF; + ``` +- 查看集群成员(只有一个) + ```sql + SELECT * FROM performance_schema.replication_group_members; + ``` +- 修改 my.cnf,配置 group-replication-start-on-boot = ON + +# 增加节点 +- 重新启动节点 mysql_22 + ```bash + systemctl restart mysql_22 + ``` +- 创建同步用户,与 mysql_11 相同 + ```sql + SET SQL_LOG_BIN = 0; + CREATE USER rpl_user@'%' IDENTIFIED BY 'password'; + GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%'; + GRANT BACKUP_ADMIN ON *.* TO rpl_user@'%'; + FLUSH PRIVILEGES; + SET SQL_LOG_BIN = 1; + ``` +- 配置同步信息 + ```sql + CHANGE MASTER TO + MASTER_USER='rpl_user', + MASTER_PASSWORD='password' + FOR CHANNEL 'group_replication_recovery'; + ``` +- 启动组复制 + ```sql + START GROUP_REPLICATION; + ``` +- 在 mysql_33 上重复 mysql_22 的步骤 +- 查看集群成员(有三个) + ```sql + SELECT * FROM performance_schema.replication_group_members; + ``` +- 修改 mysql_22 和 mysql_33 的 my.cnf,配置 group-replication-start-on-boot = ON + +# 注意 +- 每张表都必须显式指定主键 + diff --git a/content/post/mysql-replication.md b/content/post/mysql-replication.md new file mode 100644 index 0000000..a0cdd97 --- /dev/null +++ b/content/post/mysql-replication.md @@ -0,0 +1,296 @@ +--- +title: "MySQL 主从复制" +date: 2019-10-30T17:33:13+08:00 +lastmod: 2020-04-18T23:40:00+08:00 +tags: ["mysql", "master", "slave", "gtid", "replication", "同步"] +categories: ["database"] +--- + +# 环境 +- mysql 主服务器,centos7.4,192.168.1.10,端口 10000 +- mysql 从服务器,centos7.4,192.168.1.6 +- 要复制的数据库有 data_db、conf_db + +# 在主服务器上创建用于备份的用户 replicator +```sql +grant replication slave on *.* to 'replicator'@'192.168.1.6' identified by 'password'; +flush privileges; +``` + +# 在主服务器上修改 my.cnf +``` +# 建议与本机ip地址最后一位一致,与其他互备服务器均不一致 +server-id = 10 +read-only = 0 + +# 开启 binlog +log-bin = /var/log/mysql-bin/master +binlog-format = row +#关注要复制的数据库,存在跨库问题 +binlog-do-db = data-db +binlog-do-db = conf-db +#忽略的数据库,存在跨库问题 +#binlog-ignore-db = +#binlog 有效时间 +expire-logs-days = +#GTID 复制模式 +#gtid-mode = ON +#enforce-gtid-consistency=true +#双主或多主互备时,会用到以下配置 +#自增主键初始值,与其他互备服务器一致 +#auto-increment-offset = +#自增主键等差值,与其他互备服务器均不一致 +#auto-increment-increment = +``` + +# 在主服务器上重启 mysql,获取 master 状态 +```sql +-- 如果 mysql 是全新安装,则无须导出数据库初态,直接查看 binlog pos 即可 +-- 锁定要导出的数据库表 +flush tables with read lock; +``` + +# 导出数据库初态 +```bash +#在主服务器的另一个终端中运行 +mysqldump -uroot -p data_db > /tmp/data_db.sql +mysqldump -uroot -p conf_db > /tmp/conf_db.sql +#复制到从服务器上 +scp /tmp/data_db.sql /tmp/conf_db.sql 192.168.1.6:/tmp/ +``` + +# 查看 binary 日志位置 +```sql +show master status\G +-- 记住输出里的如下类似两行(不记录也可以,这两个信息已经写入了导出的sql文件中) +-- File: mysql-bin.000001 +-- Position: 137103822 +-- 解锁数据库表 +unlock tables; +``` + +# 在从服务器上编辑 my.cnf +``` +# 建议与本机ip地址最后一位一致,与其他互备服务器均不一致 +server-id = 6 +read-only = 1 + +# 如果该 slave 中也运行了 master,或者使用了 gtid 模式复制,则开启 binlog +#log-bin = mysql-bin +#binlog-format = row +# 把 slave 操作也计入 binlog,用于链式同步 +#log-slave-updates = ON +# 指定要复制的数据库,存在跨库问题 +#replicate-do-db = data_db +#replicate-do-db = conf_db +# 指定要复制的数据表,无跨库问题 +replicate-do-table = db1.t1 +replicate-wild-do-table = db1.% +# 忽略的数据库,存在跨库问题 +#replicate-ignore-db = +# 忽略的数据表,无跨库问题 +#replicate-ignore-table = db1.t1 +#replicate-wild-ignore-table = db1.% +# 中继日志 +relay-log = /var/lib/mysql-bin/slave +# 多线程复制 +slave-parallel-type = logical-clock +slave-parallel-workers = 4 +# GTID 模式 +#gtid-mode = ON +#enforce-gtid-consistency=true +# 双主或多主互备时,会用到以下配置 +# 自增主键初始值,与其他互备服务器一致 +#auto-increment-offset = +# 自增主键等差值,与其他互备服务器均不一致 +#auto-increment-increment = +``` + +# 在从服务器上重启 mysql,导入初态 +```sql +-- 创建要导入的数据库 +create database data_db default charset utf8mb4; +create database conf_db default charset utf8mb4; +``` + +# 导入数据库 +```bash +msyql -uroot -p data_db < /tmp/data_db.sql +mysql -uroot -p conf_db < /tmp/conf_db.sql +``` + +# 开启同步 +```sql +-- 基于 binlog 文件位置复制 +change master to master_host = '192.168.1.10', + master_port = 3306, + master_user = 'replicator', + master_password = 'password', + master_log_file = 'mysql-bin.000001', + master_log_pos = 137103822; +flush privileges; +-- 基于 gtid 复制 +change master to master_host = '192.168.1.10', + master_port = 3306, + master_user = 'replicator', + master_password = 'password', + master_auto_position = 1; +``` + +# 启动 slave,查看 slave 状态 +```sql +start slave; +-- 在从服务器上查看 slave 状态 +show slave status\G +-- 如果看到 +-- Slave_IO_Running: Yes +-- Slave_SQL_Running: Yes +-- 则表示 slave 开启成功! +``` + +# MySQL8 gtid 互为主从配置 +``` +[mysqld] +# ---- 固定配置 ---- +datadir = /var/lib/mysql +socket = /var/lib/mysql/mysql.sock +pid-file = /var/lib/mysql/mysql.pid +log-timestamps = SYSTEM +log-error = /var/log/mysql/error.log +slow-query-log = TRUE +slow-query-log-file = /var/log/mysql/slow.log +default-authentication-plugin = mysql_native_password + +# ---- 动态配置 ---- +mysqlx = OFF +character-set-server = utf8mb4 +default-storage-engine = innodb +lower-case-table-names = 1 +#skip-name-resolve = 1 +#max-user-connections = 600 +#innodb-buffer-pool-size = 8G +#innodb-buffer-pool-instances = 8 + +# master +# 确认不同节点该 id 唯一 +server-id = 1 +log-bin = /var/lib/mysql-bin/master +binlog-format = ROW +#binlog-do-db = db1 +binlog-expire-logs-seconds = 172800 +gtid-mode = ON +enforce-gtid-consistency = TRUE + +# slave +replicate-wild-ignore-table = information_schema.% +replicate-wild-ignore-table = mysql.% +replicate-wild-ignore-table = performance_schema.% +replicate-wild-ignore-table = sys.% +slave-parallel-workers = 2 +log-slave-updates = FALSE +relay-log = /var/lib/mysql-bin/slave +relay-log-recovery = TRUE +#read-only = ON +``` + +# MySQL8 gtid 多源复制从库配置 +- 修改 my.cnf + ``` + [mysqld] + # ---- 固定配置 ---- + datadir = /data/mysql + socket = /var/lib/mysql/mysql.sock + pid-file = /var/lib/mysql/mysql.pid + log-timestamps = SYSTEM + log-error = /var/log/mysql/error.log + slow-query-log-file = /var/log/mysql/slow.log + slow-query-log = TRUE + default-authentication-plugin = mysql_native_password + + # ---- 动态配置 ---- + mysqlx = OFF + character-set-server = utf8mb4 + default-storage-engine = innodb + lower-case-table-names = 1 + #skip-name-resolve = 1 + #max-user-connections = 600 + #innodb-buffer-pool-size = 8G + #innodb-buffer-pool-instances = 8 + + # master + server-id = 39 + log-bin = /var/lib/mysql/master + binlog-format = ROW + #binlog-do-db = db1 + gtid-mode = ON + enforce-gtid-consistency = TRUE + binlog-expire-logs-seconds = 172800 + + # slave + #replicate-wild-do-table = db1.% + replicate-wild-ignore-table = information_schema.% + replicate-wild-ignore-table = mysql.% + replicate-wild-ignore-table = performance_schema.% + replicate-wild-ignore-table = sys.% + slave-parallel-workers = 2 + log-slave-updates = FALSE + relay-log = /var/lib/mysql/slave + relay-log-recovery = TRUE + super_read_only = ON + master_info_repository = table + relay_log_info_repository = table + ``` +- 导出主库数据 + ```bash + mysqldump -uroot -h<主库1> -p --single-transaction --set-gtid-purged=on --databases db1 > 1_db1.sql + mysqldump -uroot -h<主库2> -p --single-transaction --set-gtid-purged=on --databases db2 > 2_db2.sql + ``` +- 在导出的文件(1_db1.sql,2_db2.sql)中找到 "SET @@GLOBAL.gtid_purged ..." 语句,记录下来,并在该文件中删除 +- 导入 1_db1.sql 和 2_db2.sql + ```bash + mysql -uroot -p < 1_db1.sql + mysql -uroot -p < 2_db2.sql + ``` +- 合并这两个 "SET @@GLOBAL.gtid_purged ..." 语句(gtid set 做并集),导入 gtid set 并集 + ```sql + set @@global.gtid_purged = '' + ``` +- 加入两个主库的同步配置 + ```sql + change master to master_host='<主库1>', + master_port=3306, + master_user='<主库上的 replication 账户>', + master_password='<主库上的 replication 账户的密码>', + master_auto_position=1 for channel ''; + change master to master_host='<主库2>', + master_port=3306, + master_user='<主库上的 replication 账户>', + master_password='<主库上的 replication 账户的密码>', + master_auto_position=1 for channel ''; + ``` +- 启动从库 + ```sql + start slave for channel ''; + start slave for channel ''; + ``` +- 查看从库 + ```sql + show slave status for channel ''\G + show slave status for channel ''\G + ``` +- 停止从库 + ```sql + -- 停止全部 slave + stop slave; + -- 停止指定 slave + stop slave for channel '...'; + ``` +- 重置从库 + ```sql + -- 重置全部 slave + reset slave; + -- 重置指定 slave + reset slave for channel '...'; + ``` +- 监控表: performance_schema.replication_connection_status + diff --git a/content/post/mysql.md b/content/post/mysql.md new file mode 100644 index 0000000..1e047f6 --- /dev/null +++ b/content/post/mysql.md @@ -0,0 +1,406 @@ +--- +title: "Mysql 笔记" +date: 2019-10-30T11:51:23+08:00 +lastmod: 2019-10-30T11:51:23+08:00 +tags: ["mysql"] +categories: ["database"] +--- + +# MySQL 5.7 配置文件 my.cnf +``` +[mysqld] +datadir = /db/mysql +socket = /var/lib/mysql/mysql.sock +symbolic-links = 0 +log-timestamps = SYSTEM +slow-query-log = 1 +slow-query-log-file = /var/log/mysqld/slow.log +long-query-time = 8 +#log-queries-not-using-indexes = 1 +log-error = /var/log/mysqld/error.log +pid-file = /var/run/mysqld/mysqld.pid +max-connections = 1000 +max-connect-errors = 1000 +max-user-connections = 600 +interactive-timeout = 3600 +wait-timeout = 3600 +skip-name-resolve = 1 +lower-case-table-names = 1 +default-time-zone = '+08:00' +character-set-server = utf8mb4 +sql-mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES +bind-address = 0.0.0.0 +table-open-cache = 2048 +default-storage-engine = innodb +innodb-autoinc-lock-mode = 2 +innodb-flush-log-at-trx-commit = 0 +# 建议物理内存一半 +innodb-buffer-pool-size = 8G +innodb-buffer-pool-instances = 8 +max-allowed-packet = 512M +query-cache-size = 0 +query-cache-type = 0 +# 建议点分 ip 的最后一个数字 +server-id = 123 +# bin log +#binlog-format = ROW +#log-bin = /var/lib/mysql/mysql-bin +#expire-logs-days = 3 +# relay log +#read-only = 1 +#replicate-wild-do-table = db1.% +#relay-log = /var/lib/mysql/mysql-relay-bin +#slave-parallel-type = logical-clock +``` + +# 复制表结构 +```sql +create table db1.t1 like db2.t2; +create table db1.t1 select db2.t2 where 1=2; +``` + +# 复制表结构及其数据 +```sql +create table db1.t1 select db2.t2 [where ...] +``` + +# 复制表数据 +``` +insert into db2.t2(column1, column2 ...) + select column1, column2 ... from db1.t1 [where ...] +``` + +# 通过复制表文件来复制表数据 +- 在db2中创建同结构表 + ```sql + create table db2.t1 like db1.t1; + ``` +- 丢弃表空间 + ```sql + alter table db2.t1 discard tablespace; + ``` +- 复制 t1 的表数据文件 + ```bash + #关闭数据库 + systemctl stop mysqld + cd /var/lib/mysql + scp db1/t1.idb db2/t1.idb + chown mysql.mysql db2/t1.idb + #启动数据库 + systemctl start mysqld + ``` +- 导入表空间 + ```sql + alter table db2.t1 import tablespace; + ``` + +# 设置一个表的空列自增 +```sql +-- 删除可能存在的主键 +alter table 表名 drop primary key; +alter table 表名 modify 列名 auto_increment primary key; +``` + +# 查看数据库中每个表的全部列名 +```sql +select table_name, column_name from + information_schema.columns + where table_schema = '数据库名'; +``` + +# 查看数据库中每个表的行数 +```sql +select table_name, table_rows + from information_schema.tables + where table_schema = '数据库名'; +``` + +# 查看数据库中每个表的索引 +```sql +select table_name, column_name, index_name + from INFORMATION_SCHEMA.STATISTICS + where table_schema = '数据库名'; +``` + +# 表的部分列数据到另一个表 +```sql +update db2.t2(column1, column2 ...) = ( + select column1, column2 from db1.t1 + where db1.t1.id = db2.t2.id); +``` + +# 把语句执行结果写到文件 +```bash +mysql -uroot -p -hsever_ip -Ddb_name + -Ne "select ... from table_name;" > file_name +``` + +# 表分区 +- 查看表的分区情况 + ```sql + select table_schema, table_name, partition_name, table_rows + from information_schema.partitions + where table_name = 'table_name'; + ``` +- 建表时指定 + ```sql + create table table_name(...) + partition by range columns(column_name) + (partition part_name values less than(some_value)); + ``` +- 修改成分区表 + ```sql + alter table table_name + partition by range(column_name) + (partition part_name values less than(som_value)); + ``` +- 增加分区 + ```sql + alter table table_name add partition + (partition part_name values less than(som_value)); + ``` +- 删除分区 + ```sql + alter table table_name drop partition part_name; + ``` +- 合并/拆分分区 + ```sql + alter table table_name + reorganize part_old_1, part_old_2, part_old_3 into + (partition part_new_1 values less than(value_1), + partition part_new_2 values less than(value_2)); + ``` +- 重建分区,整理分区碎片 + ```sql + alter table table_name + rebuild partition part_name_1, part_name_2; + ``` +- 优化分区,回收空间,整理碎片 + ```sql + alter table table_name + optimize partition part_name_1, part_name_2; + ``` +- 分析分区,读取并保存分区的健分布 + ```sql + alter table table_name + analyze partition part_name_1, part_name_2; + ``` +- 修复分区 + ```sql + alter table table_name + repair partition part_name_1, part_name_2; + ``` +- 检查分区 + ```sql + alter table table_name + check partition part_name_1, part_name_2; + ``` + +# MySQL 5.7 从库多线程同步 +```sql +stop slave; +set global slave_parallel_type='logical_clock'; +set global slave_parallel_workers=4; +start slave; +show processlist; +``` + +# MySQL 5.7 提示密码复杂度不够 +```sql +set global validate_password_policy=0; +``` + +# MySQL 5.7 从库复制失败跳过指定数量的事务 +```sql +stop slave; +-- 跳过一个事务 +SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; +start slave; +-- 修改 my.cnf +slave-skip-errors=1062,1053,1146,1032 #跳过指定error no类型的错误 +slave-skip-errors=all #跳过所有错误 +``` + +# MySQL 5.7 查看全部任务 +```sql +-- 分号换成 \G 显示完整 sql +show processlist; +show full processlist; +SELECT command FROM information_schema.processlist; +``` + +# MySQL 5.7 ssl 连接 +``` +--ssl-mode=REQUIRED +``` + +# MariaDB 10.1 修改密码 +```sql +UPDATE user SET password=password('newpassword') WHERE user='root'; +``` + +# MySQL 5.7 编码 +- 查看 + ```sql + SHOW VARIABLES LIKE 'character_set%'; + ``` +- 数据库连接参数中,characterEncoding=utf8 会被自动识别为 utf8mb4,但是 autoReconnect=true 必须指定 +- 更改数据库编码 + ```sql + ALTER DATABASE db_name + CHARSET UTF8MB4 COLLATE UTF8MB4_GENERAL_CI; + ``` +- 更改表编码 + ```sql + ALTER TABLE table_name + CONVERT TO CHARSET UTF8MB4 COLLATE UTF8MB4_GENERAL_CI; + ``` + +# MySQL 5.7 升级数据库管理表结构 +```bash +mysql_upgrade -u root -p +``` + +# MySQL 5.7 误删 root 后恢复 root 账户 +- 停止 mysql 服务 + ```bash + systemctl stop mysqld + ``` +- 修改 my.cnf + ``` + # 添加如下一行 + skip-grant-tables + ``` +- 启动 mysql 服务 + ```bash + systemctl start mysqld + ``` +- 重建 root 账户,并授权 + ```sql + insert into user + set user='root', + ssl_cipher='', + x509_issuer='', + x509_subject=''; + update user + set Host='localhost', + select_priv='y', + insert_priv='y', + update_priv='y', + Alter_priv='y', + delete_priv='y', + create_priv='y', + drop_priv='y', + reload_priv='y', + shutdown_priv='y', + Process_priv='y', + file_priv='y', + grant_priv='y', + References_priv='y', + index_priv='y', + create_user_priv='y', + show_db_priv='y', + super_priv='y', + create_tmp_table_priv='y', + Lock_tables_priv='y', + execute_priv='y', + repl_slave_priv='y', + repl_client_priv='y', + create_view_priv='y', + show_view_priv='y', + create_routine_priv='y', + alter_routine_priv='y', + create_user_priv='y', + event_priv='y', + trigger_priv='y', + create_tablespace_priv='y' + where user='root'; + flush privileges; + ``` +- 停止 mysql 服务 + ```bash + systemctl stop mysqld + ``` +- 修改 my.cnf + ``` + # 删除刚添加的如下一行 + #skip-grant-tables + ``` +- 启动 mysql 服务,root 账户正常可用 + ```bash + systemctl start mysqld + ``` + +# 通过EXPLAIN分析SQL的执行计划 +- 使用 + ```sql + explain sql + ``` +- select_type 查询类型 + - SIMPLE 简单表,没有表连接或子查询 + - PRIMARY 最外层的查询 + - UNION union语句的后置查询 + - SUBQUERY 第一个子查询 +- table 表/别名 +- type 访问类型 + - ALL 全表扫描 + - index 全索引扫描 + - range 索引范围扫描 + - ref 非唯一索引扫描 + - eq_ref 唯一索引扫描 + - const,system 单表最多一个匹配行 + - NULL 不需要扫描表或索引 +- possible_keys 查询可能使用的索引 +- key 实际使用的索引 +- key_len 使用的索引字段的长度 +- ref 其他匹配字段 +- rows 扫描行的数量 +- filtered 满足查询条件的记录占存储引擎返回记录的比例 +- Extra 执行情况说明 + - Using Index 全部使用索引,没有回表查询 + - Using Where 有回表查询 + - Using Index Condition ICP优化,直接在存储引擎完成条件过滤 + - Using Flesort 依靠索引顺序达不到排序效果,需额外排序 + +# 统计 insert、delete、update 和 select 次数 +```sql +show global status where Variable_name in + ('com_insert', 'com_delete', 'com_update', 'com_select'); +``` + +# csv 文件 +- 导出 + ``` + # Shell 终端 + # mysql -e "select * from t1 + into outfile '/var/lib/mysql-files/t1.csv' + fields terminated by ',' + enclosed by '\"' + escaped by '\\\' + lines terminated by '\n'" + # MySQL 终端 + # MySQL> select * from t1 + into outfile '/var/lib/mysql-files/t1.csv' + fields terminated by ',' + enclosed by '\"' + escaped by '\\' + lines terminated by '\n'; + ``` +- 导入 + ``` + # Shell 终端 + # mysql -e "load data infile '/var/lib/mysql-files/t1.csv' + into table t1 + fields terminated by ',' + enclosed by '\"' + escaped by '\\\' + lines terminated by '\n'" + # MySQL 终端 + # MySQL> load data infile '/var/lib/mysql-files/t1.csv' + into table t1 + fields terminated by ',' + enclosed by '\"' + escaped by '\\' + lines terminated by '\n'; + ``` + diff --git a/content/post/mysql5.7-install.md b/content/post/mysql5.7-install.md new file mode 100644 index 0000000..f04577d --- /dev/null +++ b/content/post/mysql5.7-install.md @@ -0,0 +1,57 @@ +--- +title: "CentOS7 yum 安装 Mysql5.7" +date: 2019-10-30T13:00:28+08:00 +lastmod: 2019-10-30T13:00:28+08:00 +tags: ["centos", "yum", "mysql"] +categories: ["database"] +--- + +# 环境 +- CentOS 7.4 x86_64 最小安装 +- MySQL 5.7.20 + +# 下载 mysql 源安装包 +```bash +#repo 地址: https://repo.mysql.com/ +cd /root/ +curl -O https://repo.mysql.com/mysql57-community-release-el7.rpm +``` + +# 安装 mysql 源 +```bash +rpm -ivh /root/mysql57-community-release-el7.rpm +``` + +# 更新 yum 缓存 +```bash +yum clean all +yum makecache fast +``` + +# 安装 mysql +```bash +yum install mysql-community-server +``` + +# 启动 mysql +```bash +systemctl start mysqld +``` + +# 查找 mysql 默认密码 +```bash +grep 'temporary password' /var/log/mysqld.log +mysql -uroot -p +# 输入查找到的密码 +``` + +# 修改 mysql 本地密码 +- 在 mysql 下修改 +```sql +mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'Pass-1234'; +``` +- 或者直接在终端修改 +```bash +mysqladmin -uroot -p password 'Pass-1234' +``` + diff --git a/content/post/networkmanager.md b/content/post/networkmanager.md new file mode 100644 index 0000000..0b71030 --- /dev/null +++ b/content/post/networkmanager.md @@ -0,0 +1,202 @@ +--- +title: "NetworKmanager" +date: 2019-10-29T18:26:17+08:00 +lastmod: 2019-10-29T18:40:00+08:00 +keywords: ["centos", "networkmanager", "network"] +tags: ["centos", "networkmanager", "network"] +categories: ["network"] +--- + +# 环境 +- CentOS8 已废弃 network.service, 推荐使用 NetworkManager + +# 概念 +- 在NM里,有2个维度: 连接(connection)和设备(device),这是多对一的关系 +- 想给某个网卡配ip,首先NM要能纳管这个网卡。设备里存在的网卡(即 nmcli d可以看到的),就是NM纳管的 +- 可以为一个设备配置多个连接(即 nmcli c可以看到的),每个连接可以理解为一个ifcfg配置文件 +- 同一时刻,一个设备只能有一个连接活跃,可以通过 nmcli c up切换连接 + +# 配置连接 +- 状态 + - 活跃(带颜色字体):表示当前该connection生效 + - 非活跃(正常字体):表示当前该connection不生效 +- 创建 connection,配置静态 ip + ```bash + nmcli c add \ + type ethernet \ + con-name ethX \ + ifname ethX \ + ipv4.addr 192.168.1.100/24 \ + ipv4.gateway 192.168.1.1 \ + ipv4.method manual \ + autoconnect yes + # type ethernet:创建连接时候必须指定类型,类型有很多,可通过 "nmcli c add type -h" 看到 + # con-name ethX: 表示连接(connection)的名字,可任意定义,无需和网卡名相同 + # ifname ethX: 表示网卡名,这个 ethX 必须是在 nmcli d里能看到的 + # ipv4.addr: 指定一个或多个 ip 地址 + # ipv4.gateway: 网关 + # ipv4.method: 对应ifcfg文件内容的BOOTPROTO + # ipv4.method 默认为auto,对应为BOOTPROTO=dhcp,这种时候如果指定ip,就可能导致网卡同时有dhcp分配的ip和静态ip + # ipv4.method 设置为manual表示BOOTPROTO=none,即只有静态ip + # 如果这是为ethX创建的第一个连接,则自动生效 + # 如果此时已有连接存在,则该连接不会自动生效,可以执行 nmcli c up ethX-test来切换生效 + # autoconnect: 开机后自动连接 + ``` +- 创建 connection,配置多个静态 ip + ```bash + nmcli c add \ + type ethernet \ + con-name ethX-X \ + ifname ethX \ + ipv4.addr '192.168.1.100/24,192.10.0.3.100/25' \ + ipv4.routes '192.168.0.0/16 192.168.1.10,10.0.0.0/8 10.0.3.1' \ + ipv4.gateway 192.168.1.254 \ + ipv4.dns '8.8.8.8,4.4.4.4' \ + ipv4.method manual + # ipv4.routes: 设置自定义路由 + # ipv4.dns: 设置 dns + ``` +- 创建 connection,配置动态 ip + ```bash + nmcli c add \ + type ethernet \ + con-name ethX \ + ifname ethX \ + ipv4.method auto + ``` +- 修改 ip + ```bash + # ethX 是 connection name,也可以指定 connection UUID + + # 直接替换 + nmcli c modify ethX \ + ipv4.addr '192.168.1.200/24' + nmcli c up ethX + # 通过nmcli c modify修改con-name,只会对应修改ifcfg文件中的NAME,而不会更改ifcfg文件名 + + # 增加 ip + nmcli c modify ethX \ + +ipv4.addr '192.168.2.200/24' + +ipv4.routes '10.1.0.0/16 192.168.2.1' + nmcli c up ethX + + # 删除 ip + nmcli c modify ethX \ + -ipv4.addr '192.168.2.200/24' + -ipv4.routes '10.1.0.0/16 192.168.2.1' + nmcli c up ethX + ``` +- 操作 connection + ```bash + # ethX 是 connection name,也可以指定 connection UUID + + # 启动 + nmcli c up ethX + + # 停止 + nmcli c down ethX + + # 删除 + nmcli c delete ethX + # 删除当前连接后,会自动选择同一个设备的其他连接来顶替生效 + ``` +- 查看 connection + ```bash + # ethX 是 connection name,也可以指定 connection UUID + + # 查看列表 + nmcli c show + # 查看活动连接列表 + nmcli c show -a + + # 查看指定 connection 详细信息 + nmcli c show ethX + ``` +- 重载配置但不立即生效 + ```bash + # 重载全部 connection 的所有 ifcfg-xxxx 和 route-xxxx + nmcli c reload + + # 重载指定 ifcfg-ethX 和 route-ethX + nmcli c load /etc/sysconfig/network-scripts/ifcfg-ethX + nmcli c load /etc/sysconfig/network-scripts/route-ethX + ``` +- 重载配置并立即生效 + ```bash + # ethXX 是 connection name,也可以指定 connection UUID + nmcli c up ethXX + + # ethX 是 device name + nmcli d reapply ethX + nmcli d connect ethX + ``` + +# 配置网卡设备 +- 状态 + - connected: 已被NM纳管,并且当前有活跃的connection + - disconnected: 已被NM纳管,但是当前没有活跃的connection + - unmanaged: 未被NM纳管 + - unavailable: 不可用,NM无法纳管,通常出现于网卡link为down的时候(比如ip link set ethX down) +- 查看网卡列表 + ```bash + nmcli d + + # 查看全部网卡的详细信息 + nmcli d show + ``` +- 操作网卡 + ```bash + # ethX 是 device name + + # 设置 nm 管理网卡 ethX + # 并刷新该网卡对应的活跃 connection(如果之前有修改过connection配置) + # 如果有connection但是都处于非活跃状态,则自动选择一个connection并将其活跃 + # 如果没有connection,则自动生成一个并将其活跃 + nmcli d connect ethX + + # 设置 nm 不管理网卡 ethX + # 此操作不会变更实际网卡的link状态,只会使对应的connection变成非活跃 + # 若重启系统则又会自动connect + # 另外,如果手工将该网卡的connection全部删掉,该网卡状态也会自动变为disconnected + nmcli d disconnect ethX + + # 设置 nm 是否自动启动网卡 + nmcli d set ethX autoconnect yes|no + # 设置 nm 是否自动管理网卡 + nmcli d set ethX managed yes|no + ``` +- 关闭无线网络(默认启动) + ```bash + nmcli r all off + ``` + +# NM 状态 +- 查看当前 nm 连接信息 + ```bash + nmcli + ``` +- 查看当前 nm 全部状态 + ```bash + nmcli general status + ``` +- 纳管状态 + ```bash + # 查看 + nmcli n + # 开启 + nmcli n on + # 关闭 + nmcli n off + ``` +- 查看 nm 当前是否在线可用 + ```bash + nm-oncline + ``` + +# 注意事项 +- 如果希望NM不要纳管网卡,只有一个办法最彻底最靠谱,就是自己写ifcfg,内容加上 NM_CONTROLLED=no,这样该device的状态就会始终保持unmanaged。nmcli c up、nmcli c reload、nmcil c load都不会对其起任何作用 +- NM只能对link状态为up的网卡进行操作,如果手动 ip link set ethX down,那么NM就无法对该网卡做任何操作(即使nmcli d connect也没有用) +- NetworkManager支持3种获取dhcp的方式:dhclient、dhcpcd、internal,当/etc/NetworkManager/NetworkManager.conf配置文件中的[main]部分没配置 dhcp=时候,默认使用internal(rhel7/centos7默认是dhclient)。internal是NM内部实现的dhcp客户端 +- NM默认会从dhcp里获取dns信息,并修改/etc/resolv.conf,如果不想让NM管理/etc/resolv.conf,则只需在/etc/NetworkManager/NetworkManager.conf里的[main]里增加 dns=none即可 +- 如果想让NM不要自动管理新网卡(比如不要给新网卡获取ip地址),则只需在/etc/NetworkManager/NetworkManager.conf里的[main]里增加 no-auto-default=\* 即可,改完后通过 systemctl restart NetworkManager 或者重启系统来生效 + diff --git a/content/post/nfs.md b/content/post/nfs.md new file mode 100644 index 0000000..c7375a5 --- /dev/null +++ b/content/post/nfs.md @@ -0,0 +1,88 @@ +--- +title: "Nfs 笔记" +date: 2019-10-30T00:51:22+08:00 +lastmod: 2019-10-30T00:51:22+08:00 +keywords: [] +tags: ["nfs"] +categories: ["storage"] +--- + +# 环境 +- 服务端 CentOS7 192.168.1.100 +- 客户端 CentOS7 192.168.1.101 + +# 服务端 +- 安装 + ```bash + yum install nfs-utils + systemctl enable nfs-server + systemctl start nfs-server + # rpcbind 服务自动 start 并 enable + ``` +- 创建配置文件 + ```bash + mkdir /nfs_share -p + chown -R nfsnobody.nfsnobody /nfs_share + cat > /etc/exports.d/mynfs.conf < /etc/nginx/nginx.auth + #或者 + htpasswd -c -m /mnt/vdb1/svnrepos/accesspwd gxfp #根据提示输入密码 + ``` +- 修改 nginx 配置,http、server 和 location 都可以 + ```nginx + location / { + auth_basic "Kibana"; + auth_basic_user_file /etc/nginx/nginx.auth; + } + ``` + +# 创建 ssl 密钥 +```bash +mkdir /etc/nginx/ssl && cd /etc/nginx/ssl +openssl genrsa -out ssl.key 2048 +openssl req -new -key ssl.key -days 3650 -out ssl.csr +openssl x509 -req -in ssl.csr -signkey ssl.key -out ssl.crt +``` + +# https 访问 +```nginx +server { + ssl on; + listen 443 ssl; + server_name www.domain.com; + ssl_certificate /etc/nginx/ssl/ssl.crt; + ssl_certificate_key /etc/nginx/ssl/ssl.key; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; +} +``` + +# http 自动跳转 https,有三种配置 +- rewrite 服务端重定向 + ```nginx + server { + listen 80; + server_name www.domain.com; + rewrite ^(.*) https://$server_name$1 permanent; + } + ``` +- return 客户端重定向 + ```nginx + server { + listen 80; + server_name www.domain.com; + return 301 https://$server_name$request_uri; + } + ``` +- error_page 客户端重定向 + ```nginx + server { + ssl on; + listen 80; + listen 443 ssl; + server_name www.domain.com; + ssl_certificate /etc/nginx/ssl/ssl.crt; + ssl_certificate_key /etc/nginx/ssl/ssl.key; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + error_page 497 https://$server_name$request_uri; + } + ``` + +# http 和 https 共存 +```nginx +server { + listen 80; + listen 443 ssl; + server_name www.domain.com; + ssl_certificate /etc/nginx/ssl/ssl.crt; + ssl_certificate_key /etc/nginx/ssl/ssl.key; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; +} +``` + +# nginx 日志配置 +- http 常规日志 + ``` + log_format main '$remote_addr - [$time_local] "$request_method $uri" "$args" ' + '"-" $status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + ``` +- http 登陆日志,打印 post 请求体 + ``` + log_format login '$remote_addr - [$time_local] "$request_method $uri" "$args" ' + '"$request_body" $status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + ``` +- https 常规日志,从 http_x_forwoarded_for 中获取请求源地址 + ``` + log_format smain '$http_x_forwarded_for - [$time_local] "$request_method $uri" "$args" ' + '"-" $status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "-"'; + ``` +- https 登陆日志,从 http_x_forwoarded_for 中获取请求源地址,并打印 post 请求体 + ``` + log_format slogin '$http_x_forwarded_for - [$time_local] "$request_method $uri" "$args" ' + '"$request_body" $status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "-"'; + ``` + +# 常用全局配置 +```nginx +events { + use epoll; + multi_accept on; + worker_connections 10240; +} +http { + access_log /var/log/nginx/access.log main; + gzip on; + sendfile on; + tcp_nopush on; + tcp_nodelay off; + server_tokens off; + keepalive_timeout 65; + types_hash_max_size 2048; +} +``` + diff --git a/content/post/openvpn-install.md b/content/post/openvpn-install.md new file mode 100644 index 0000000..3df7082 --- /dev/null +++ b/content/post/openvpn-install.md @@ -0,0 +1,175 @@ +--- +title: "CentOS7 安装 Openvpn" +date: 2019-10-30T01:12:40+08:00 +lastmod: 2019-10-30T01:12:40+08:00 +tags: ["openvpn"] +categories: ["network"] +--- + +# 环境 + +角色 | 主机名 | 操作系统 | IP +---- | ---- | ---- | ---- +vpn 服务端 | vpn-server | CentOS7 | 192.168.1.90 +vpn 客户端 | vpn-client | CentOS7 | 192.168.1.91 + +# 两台服务器初始准备 +- 关闭 SELinux +- 关闭防火墙或放行 udp 端口 1194 +- 安装 openvpn + ```bash + yum install epel-release + yum clean all + yum makecache fast + yum install easy-rsa openvpn + ``` + +# 在 vpn-server 上创建证书 +- 复制 easy-rsa 脚本到 /opt/easy-rsa/ 下 + ```bash + cp -af /usr/share/easy-rsa/3.0.3/ /opt/easy-rsa + # vars 文件包含证书相关配置,可修改 + cp /usr/share/doc/easy-rsa-3.0.3/vars.example /opt/easy-rsa/vars + ``` +- 初始化 pki 目录结构 + ```bash + cd /opt/easy-rsa + ./easyrsa init-pki + ``` +- 生成免密 ca 证书 + ```bash + # 使用默认 common name 即可 + ./easyrsa build-ca nopass + ``` +- 生成服务端免密证书 + ```bash + # 参数 my-server0 指定生成的客户端证书文件名及其 common name + ./easyrsa build-server-full my-server0 nopass + ``` +- 生成客户端免密证书 + ```bash + # 参数 my-client0 指定生成的客户端证书文件名及其 common name + # 不同的客户端需指定不同的 common name + # 方便在 client-conf-dir 目录下创建对应的同名客户端配置文件 + ./easyrsa build-client-full my-client0 nopass + ``` +- 生成 dh.pem + ```bash + ./easyrsa gen-dh + ``` +- 生成 ta.key + ```bash + openvpn --genkey --secret pki/ta.key + ``` +- 查看证书目录 + ``` + /opt/easy-rsa/ + ├── easyrsa + ├── openssl-1.0.cnf + ├── pki + │ ├── ca.crt + │ ├── certs_by_serial + │ │ ├── 1835C740AD1421868300998618C0641F.pem + │ │ └── 4F3AF1ED7D42ED56CCF26CC7622F4F50.pem + │ ├── dh.pem + │ ├── index.txt + │ ├── index.txt.attr + │ ├── index.txt.attr.old + │ ├── index.txt.old + │ ├── issued + │ │ ├── my-client0.crt + │ │ └── my-server0.crt + │ ├── private + │ │ ├── ca.key + │ │ ├── my-client0.key + │ │ └── my-server0.key + │ ├── reqs + │ │ ├── my-client0.req + │ │ └── my-server0.req + │ ├── serial + │ ├── serial.old + │ └── ta.key + ├── vars + └── x509-types + ├── ca + ├── client + ├── COMMON + ├── san + └── server + ``` +- **该证书目录 /opt/easyrsa 需妥善保管,后期增加其他客户端证书时会用到** + +# 配置 vpn-server +- 开启路由转发,修改 /etc/sysctl.conf + ```bash + sysctl -w 'net.ipv4.ip_forward = 1' + sysctl -p + ``` +- 复制服务端证书到 openvpn 配置目录下 + ```bash + mkdir -p /etc/openvpn/server/my-server0/ + cd /opt/easy-rsa/pki + cp ca.crt dh.pem issued/my-server0.crt private/my-server0.key ta.key \ + /etc/openvpn/server/my-server0/ + ``` +- 创建 /etc/openvpn/server/my-server0.conf + ```bash + cd /usr/share/doc/openvpn-2.4.7/sample/sample-config-files + cp server.conf /etc/openvpn/server/my-server0.conf + ``` +- 修改 /etc/openvpn/server/my-server0.conf + ``` + ca my-server0/ca.crt + cert my-server0/my-server0.crt + key my-server0/my-server0.key + dh my-server0/dh.pem + tls-auth my-server0/ta.key 0 + ``` + +# 启动 vpn-server 服务 +- 启动 openvpn-server@my-server0.service 服务 + ```bash + systemctl start openvpn-server@my-server0.service + ``` +- 如需提供 ca 密码 + ```bash + systemd-tty-ask-password-agent --query + ``` + +# 配置 vpn-client +- 复制 vpn-server 上的客户端证书到 openvpn 配置目录下 + ```bash + mkdir -p /etc/openvpn/client/my-client0 + scp vpn-server:/opt/easy-rsa/pki/{ca.crt,issued/my-client0.crt,private/my-client0.key,ta.key} /etc/openvpn/client/my-client0/ + ``` +- 创建 /etc/openvpn/client/my-client0.conf + ```bash + cd /usr/share/doc/openvpn-3.0.3/sample/sample-config-files + cp client.conf /etc/openvpn/client/my-client0.conf + ``` +- 修改 /etc/openvpn/client/my-client0.conf + ``` + remote 192.168.1.90 1194 # vpn server 地址 + ca my-client0/ca.crt + cert my-client0/client.crt + key my-client0/client.key + tls-auth my-client0/ta.key 1 + ``` + +# 启动 vpn-client 服务 +- 启动 openvpn-client@my-client0 服务 + ```bash + systemctl start openvpn-client@my-client0.service + ``` +- 如需提供 ca 密码 + ```bash + systemd-tty-ask-password-agent --query + ``` + +# 验证 +- vpn server 新增网卡 tun0,地址是 10.8.0.1/24 +- vpn client 新增网卡 tun0,地址是 10.8.0.2/24 + +# 参考 +- [创建证书](https://blog.csdn.net/zhuwei_clark/article/details/87949043) + diff --git a/content/post/openvpn.md b/content/post/openvpn.md new file mode 100644 index 0000000..75d0076 --- /dev/null +++ b/content/post/openvpn.md @@ -0,0 +1,325 @@ +--- +title: "Openvpn 笔记" +date: 2019-10-30T00:45:06+08:00 +lastmod: 2019-10-30T00:45:06+08:00 +keywords: [] +tags: ["openvpn"] +categories: ["network"] +--- + +# 服务端配置文件 server.conf +``` +################################################# +# 针对多客户端的OpenVPN 2.0 的服务器端配置文件示例 +# +# 本文件用于多客户端<->单服务器端的OpenVPN服务器端配置 +# +# OpenVPN也支持单机<->单机的配置(更多信息请查看网站上的示例页面) +# +# 该配置支持Windows或者Linux/BSD系统。此外,在Windows上,记得将路径加上双引号, +# 并且使用两个反斜杠,例如:"C:\\Program Files\\OpenVPN\\config\\foo.key" +# +# '#' or ';'开头的均为注释内容 +################################################# + +#OpenVPN应该监听本机的哪些IP地址? +#该命令是可选的,如果不设置,则默认监听本机的所有IP地址。 +;local a.b.c.d + +# OpenVPN应该监听哪个TCP/UDP端口? +# 如果你想在同一台计算机上运行多个OpenVPN实例,你可以使用不同的端口号来区分它们。 +# 此外,你需要在防火墙上开放这些端口。 +port 1194 + +#OpenVPN使用TCP还是UDP协议? +;proto tcp +proto udp + +# 指定OpenVPN创建的通信隧道类型。 +# "dev tun"将会创建一个路由IP隧道, +# "dev tap"将会创建一个以太网隧道。 +# +# 如果你是以太网桥接模式,并且提前创建了一个名为"tap0"的与以太网接口进行桥接的虚拟接口,则你可以使用"dev tap0" +# +# 如果你想控制VPN的访问策略,你必须为TUN/TAP接口创建防火墙规则。 +# +# 在非Windows系统中,你可以给出明确的单位编号(unit number),例如"tun0"。 +# 在Windows中,你也可以使用"dev-node"。 +# 在多数系统中,除非你部分禁用或者完全禁用了TUN/TAP接口的防火墙,否则VPN将不起作用。 +;dev tap +dev tun + +# 如果你想配置多个隧道,你需要用到网络连接面板中TAP-Win32适配器的名称(例如"MyTap")。 +# 在XP SP2或更高版本的系统中,你可能需要有选择地禁用掉针对TAP适配器的防火墙 +# 通常情况下,非Windows系统则不需要该指令。 +;dev-node MyTap + +# 设置SSL/TLS根证书(ca)、证书(cert)和私钥(key)。 +# 每个客户端和服务器端都需要它们各自的证书和私钥文件。 +# 服务器端和所有的客户端都将使用相同的CA证书文件。 +# +# 通过easy-rsa目录下的一系列脚本可以生成所需的证书和私钥。 +# 记住,服务器端和每个客户端的证书必须使用唯一的Common Name。 +# +# 你也可以使用遵循X509标准的任何密钥管理系统来生成证书和私钥。 +# OpenVPN 也支持使用一个PKCS #12格式的密钥文件(详情查看站点手册页面的"pkcs12"指令) +ca ca.crt +cert server.crt +key server.key # 该文件应该保密 + +# 指定迪菲·赫尔曼参数。 +# 你可以使用如下名称命令生成你的参数: +# openssl dhparam -out dh1024.pem 1024 +# 如果你使用的是2048位密钥,使用2048替换其中的1024。 +dh dh1024.pem + +# 设置服务器端模式,并提供一个VPN子网,以便于从中为客户端分配IP地址。 +# 在此处的示例中,服务器端自身将占用10.8.0.1,其他的将提供客户端使用。 +# 如果你使用的是以太网桥接模式,请注释掉该行。更多信息请查看官方手册页面。 +server 10.8.0.0 255.255.255.0 + +# 指定用于记录客户端和虚拟IP地址的关联关系的文件。 +# 当重启OpenVPN时,再次连接的客户端将分配到与上一次分配相同的虚拟IP地址 +ifconfig-pool-persist ipp.txt + +# 该指令仅针对以太网桥接模式。 +# 首先,你必须使用操作系统的桥接能力将以太网网卡接口和TAP接口进行桥接。 +# 然后,你需要手动设置桥接接口的IP地址、子网掩码; +# 在这里,我们假设为10.8.0.4和255.255.255.0。 +# 最后,我们必须指定子网的一个IP范围(例如从10.8.0.50开始,到10.8.0.100结束),以便于分配给连接的客户端。 +# 如果你不是以太网桥接模式,直接注释掉这行指令即可。 +;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100 + +# 该指令仅针对使用DHCP代理的以太网桥接模式, +# 此时客户端将请求服务器端的DHCP服务器,从而获得分配给它的IP地址和DNS服务器地址。 +# +# 在此之前,你也需要先将以太网网卡接口和TAP接口进行桥接。 +# 注意:该指令仅用于OpenVPN客户端,并且该客户端的TAP适配器需要绑定到一个DHCP客户端上。 +;server-bridge + +# 推送路由信息到客户端,以允许客户端能够连接到服务器背后的其他私有子网。 +# (简而言之,就是允许客户端访问VPN服务器自身所在的其他局域网) +# 记住,这些私有子网也要将OpenVPN客户端的地址池(10.8.0.0/255.255.255.0)反馈回OpenVPN服务器。 +;push "route 192.168.10.0 255.255.255.0" +;push "route 192.168.20.0 255.255.255.0" + +# 为指定的客户端分配指定的IP地址,或者客户端背后也有一个私有子网想要访问VPN, +# 那么你可以针对该客户端的配置文件使用ccd子目录。 +# (简而言之,就是允许客户端所在的局域网成员也能够访问VPN) + +# 举个例子:假设有个Common Name为"Thelonious"的客户端背后也有一个小型子网想要连接到VPN,该子网为192.168.40.128/255.255.255.248。 +# 首先,你需要去掉下面两行指令的注释: +;client-config-dir ccd +;route 192.168.40.128 255.255.255.248 +# 然后创建一个文件ccd/Thelonious,该文件的内容为: +# iroute 192.168.40.128 255.255.255.248 +#这样客户端所在的局域网就可以访问VPN了。 +# 注意,这个指令只能在你是基于路由、而不是基于桥接的模式下才能生效。 +# 比如,你使用了"dev tun"和"server"指令。 + +# 再举个例子:假设你想给Thelonious分配一个固定的IP地址10.9.0.1。 +# 首先,你需要去掉下面两行指令的注释: +;client-config-dir ccd +;route 10.9.0.0 255.255.255.252 +# 然后在文件ccd/Thelonious中添加如下指令: +# ifconfig-push 10.9.0.1 10.9.0.2 + +# 如果你想要为不同群组的客户端启用不同的防火墙访问策略,你可以使用如下两种方法: +# (1)运行多个OpenVPN守护进程,每个进程对应一个群组,并为每个进程(群组)启用适当的防火墙规则。 +# (2) (进阶)创建一个脚本来动态地修改响应于来自不同客户的防火墙规则。 +# 关于learn-address脚本的更多信息请参考官方手册页面。 +;learn-address ./script + +# 如果启用该指令,所有客户端的默认网关都将重定向到VPN,这将导致诸如web浏览器、DNS查询等所有客户端流量都经过VPN。 +# (为确保能正常工作,OpenVPN服务器所在计算机可能需要在TUN/TAP接口与以太网之间使用NAT或桥接技术进行连接) +;push "redirect-gateway def1 bypass-dhcp" + +# 某些具体的Windows网络设置可以被推送到客户端,例如DNS或WINS服务器地址。 +# 下列地址来自opendns.com提供的Public DNS 服务器。 +;push "dhcp-option DNS 208.67.222.222" +;push "dhcp-option DNS 208.67.220.220" + +# 去掉该指令的注释将允许不同的客户端之间相互"可见"(允许客户端之间互相访问)。 +# 默认情况下,客户端只能"看见"服务器。为了确保客户端只能看见服务器,你还可以在服务器端的TUN/TAP接口上设置适当的防火墙规则。 +;client-to-client + +# 如果多个客户端可能使用相同的证书/私钥文件或Common Name进行连接,那么你可以取消该指令的注释。 +# 建议该指令仅用于测试目的。对于生产使用环境而言,每个客户端都应该拥有自己的证书和私钥。 +# 如果你没有为每个客户端分别生成Common Name唯一的证书/私钥,你可以取消该行的注释(但不推荐这样做)。 +;duplicate-cn + +# keepalive指令将导致类似于ping命令的消息被来回发送,以便于服务器端和客户端知道对方何时被关闭。 +# 每10秒钟ping一次,如果120秒内都没有收到对方的回复,则表示远程连接已经关闭。 +keepalive 10 120 + +# 出于SSL/TLS之外更多的安全考虑,创建一个"HMAC 防火墙"可以帮助抵御DoS攻击和UDP端口淹没攻击。 +# 你可以使用以下命令来生成: +# openvpn --genkey --secret ta.key +# +# 服务器和每个客户端都需要拥有该密钥的一个拷贝。 +# 第二个参数在服务器端应该为'0',在客户端应该为'1'。 +;tls-auth ta.key 0 # 该文件应该保密 + +# 选择一个密码加密算法。 +# 该配置项也必须复制到每个客户端配置文件中。 +;cipher BF-CBC # Blowfish (默认) +;cipher AES-128-CBC # AES +;cipher DES-EDE3-CBC # Triple-DES + +# 在VPN连接上启用压缩。 +# 如果你在此处启用了该指令,那么也应该在每个客户端配置文件中启用它。 +comp-lzo + +# 允许并发连接的客户端的最大数量 +;max-clients 100 + +# 在完成初始化工作之后,降低OpenVPN守护进程的权限是个不错的主意。 +# 该指令仅限于非Windows系统中使用。 +;user nobody +;group nobody + +# 持久化选项可以尽量避免访问那些在重启之后由于用户权限降低而无法访问的某些资源。 +persist-key +persist-tun + +# 输出一个简短的状态文件,用于显示当前的连接状态,该文件每分钟都会清空并重写一次。 +status openvpn-status.log + +# 默认情况下,日志消息将写入syslog(在Windows系统中,如果以服务方式运行,日志消息将写入OpenVPN安装目录的log文件夹中)。 +# 你可以使用log或者log-append来改变这种默认情况。 +# "log"方式在每次启动时都会清空之前的日志文件。 +# "log-append"这是在之前的日志内容后进行追加。 +# 你可以使用两种方式之一(但不要同时使用)。 +;log openvpn.log +;log-append openvpn.log + +# 为日志文件设置适当的冗余级别(0~9)。冗余级别越高,输出的信息越详细。 +# +# 0 表示静默运行,只记录致命错误。 +# 4 表示合理的常规用法。 +# 5 和 6 可以帮助调试连接错误。 +# 9 表示极度冗余,输出非常详细的日志信息。 +verb 3 + +# 重复信息的沉默度。 +# 相同类别的信息只有前20条会输出到日志文件中。 +;mute 20 +``` + +# 客户端配置文件 +``` +############################################## +# 针对多个客户端的OpenVPN 2.0 的客户端配置文件示例 +# +# 该配置文件可以被多个客户端使用,当然每个客户端都应该有自己的证书和密钥文件 +# +# 在Windows上此配置文件的后缀应该是".ovpn",在Linux/BSD系统中则是".conf" +############################################## + +# 指定这是一个客户端,我们将从服务器获取某些配置文件指令 +client + +# 在大多数系统中,除非你部分禁用或者完全禁用了TUN/TAP接口的防火墙,否则VPN将不起作用。 +;dev tap +dev tun + +# 在Windows系统中,如果你想配置多个隧道,则需要该指令。 +# 你需要用到网络连接面板中TAP-Win32适配器的名称(例如"MyTap")。 +# 在XP SP2或更高版本的系统中,你可能需要禁用掉针对TAP适配器的防火墙。 +;dev-node MyTap + +# 指定连接的服务器是采用TCP还是UDP协议。 +# 这里需要使用与服务器端相同的设置。 +;proto tcp +proto udp + +# 指定服务器的主机名(或IP)以及端口号。 +# 如果有多个VPN服务器,为了实现负载均衡,你可以设置多个remote指令。 +remote my-server-1 1194 +;remote my-server-2 1194 + +# 如果指定了多个remote指令,启用该指令将随机连接其中的一台服务器, +# 否则,客户端将按照指定的先后顺序依次尝试连接服务器。 +;remote-random + +# 启用该指令,与服务器连接中断后将自动重新连接,这在网络不稳定的情况下(例如:笔记本电脑无线网络)非常有用。 +resolv-retry infinite + +# 大多数客户端不需要绑定本机特定的端口号 +nobind + +# 在初始化完毕后,降低OpenVPN的权限(该指令仅限于非Windows系统中使用) +;user nobody +;group nobody + +# 持久化选项可以尽量避免访问在重启时由于用户权限降低而无法访问的某些资源。 +persist-key +persist-tun + +# 如果你是通过HTTP代理方式来连接到实际的VPN服务器,请在此处指定代理服务器的主机名(或IP)和端口号。 +# 如果你的代理服务器需要身份认证,请参考官方手册页面。 +;http-proxy-retry # 连接失败时自动重试 +;http-proxy [proxy server] [proxy port #] + +# 无线网络通常会产生大量的重复数据包。设置此标识将忽略掉重复数据包的警告信息。 +;mute-replay-warnings + +# SSL/TLS 参数配置。 +# 更多描述信息请参考服务器端配置文件。 +# 最好为每个客户端单独分配.crt/.key文件对。 +# 单个CA证书可以供所有客户端使用。 +ca ca.crt +cert client.crt +key client.key + +# 指定通过检查证书的nsCertType字段是否为"server"来验证服务器端证书。 +# 这是预防潜在攻击的一种重要措施。 +# +# 为了使用该功能,你需要在生成服务器端证书时,将其中的nsCertType字段设为"server" +# easy-rsa文件夹中的build-key-server脚本文件可以达到该目的。 +ns-cert-type server + +# 如果服务器端使用了tls-auth密钥,那么每个客户端也都应该有该密钥。 +;tls-auth ta.key 1 + +# 指定密码的加密算法。 +# 如果服务器端启用了cipher指令选项,那么你必须也在这里指定它。 +;cipher x + +# 在VPN连接中启用压缩。 +# 该指令的启用/禁用应该与服务器端保持一致。 +comp-lzo + +# 设置日志文件冗余级别(0~9)。 +# 0 表示静默运行,只记录致命错误。 +# 4 表示合理的常规用法。 +# 5 和 6 可以帮助调试连接错误。 +# 9 表示极度冗余,输出非常详细的日志信息。 +verb 3 + +# 忽略过多的重复信息。 +# 相同类别的信息只有前20条会输出到日志文件中。 +;mute 20 +``` + +# 撤销客户端证书 +- 生成/更新 crl.pem + ```bash + ./easyrsa revoke my-client0 # my-client0 是被注销证书的 common name + ./easyrsa gen-crl + cp pki/crl.pem /etc/openvpn-server/server/my-server0/ + ``` +- 修改 openvpn-server 的配置文件,追加/修改下面一行 + ``` + # 注意: 该 crl.pem 文件本身及其上级目录,均需对 openvpn 运行用户可读 + crl-verify my-server0/crl.pem + ``` +- 重启 openvpn-server 服务 + +# 参考链接 +- [https://www.iyunv.com/forum.php?mod=viewthread&tid=250551](https://www.iyunv.com/forum.php?mod=viewthread&tid=250551) +- [https://blog.csdn.net/a8039974/article/details/89279147](https://blog.csdn.net/a8039974/article/details/89279147) +- [https://blog.csdn.net/a8039974/article/details/89279086](https://blog.csdn.net/a8039974/article/details/89279086) +- [https://blog.csdn.net/a8039974/article/details/89279036](https://blog.csdn.net/a8039974/article/details/89279036) +- [https://my.oschina.net/liucao/blog/863112](https://my.oschina.net/liucao/blog/863112) + diff --git a/content/post/oracle-sysctl.md b/content/post/oracle-sysctl.md new file mode 100644 index 0000000..bd61e8c --- /dev/null +++ b/content/post/oracle-sysctl.md @@ -0,0 +1,86 @@ +--- +title: "安装 Oracle 需要调整的内核参数" +date: 2019-10-30T17:58:46+08:00 +lastmod: 2019-10-30T17:58:46+08:00 +tags: ["oracle"] +categories: ["database"] +--- + +在**安装**Oracle的时候需要调整**linux**的**内核参数**,但是各参数代表什么含义呢,下面做详细解析。 + +Linux安装文档中给出的最小值: + +``` +fs.aio-max-nr = 1048576 +fs.file-max = 6815744 +kernel.shmall = 2097152 +kernel.shmmax = 4294967295 +kernel.shmmni = 4096 +kernel.sem = 250 32000 100 128 +net.ipv4.ip_local_port_range = 9000 65500 +net.core.rmem_default = 262144 +net.core.rmem_max = 4194304 +net.core.wmem_default = 262144 +net.core.wmem_max = 1048586 +``` + +各参数详解: + +**kernel.shmmax:** + +是核心参数中最重要的参数之一,用于定义单个共享内存段的最大值。设置应该足够大,能在一个共享内存段下容纳下整个的SGA ,设置的过低可能会导致需要创建多个共享内存段,这样可能导致系统性能的下降。至于导致系统下降的主要原因为在实例启动以及**Server**Process创建的时候,多个小的共享内存段可能会导致当时轻微的系统性能的降低(在启动的时候需要去创建多个虚拟地址段,在进程创建的时候要让进程对多个段进行“识别”,会有一些影响),但是其他时候都不会有影响。 + +官方建议值: + +32位linux系统:可取最大值为4GB(4294967296bytes)-1byte,即4294967295。建议值为多于内存的一半,所以如果是32为系统,一般可取值为4294967295。32位系统对SGA大小有限制,所以SGA肯定可以包含在单个共享内存段中。 + +64位linux系统:可取的最大值为物理内存值-1byte,建议值为多于物理内存的一半,一般取值大于SGA\_MAX\_SIZE即可,可以取物理内存-1byte。例如,如果为12GB物理内存,可取12\*1024\*1024\*1024-1=12884901887,SGA肯定会包含在单个共享内存段中。 + +**kernel.shmall:** + + 该参数控制可以使用的共享内存的总页数。Linux共享内存页大小为4KB,共享内存段的大小都是共享内存页大小的整数倍。一个共享内存段的最大大小是16G,那么需要共享内存页数是16GB/4KB=16777216KB /4KB=4194304(页),也就是64Bit系统下16GB物理内存,设置kernel.shmall = 4194304才符合要求(几乎是原来设置2097152的两倍)。这时可以将shmmax参数调整到16G了,同时可以修改SGA\_MAX\_SIZE和SGA_TARGET为12G(您想设置的SGA最大大小,当然也可以是2G~14G等,还要协调PGA参数及OS等其他内存使用,不能设置太满,比如16G) + +**kernel.shmmni:** + +该参数是共享内存段的最大数量。shmmni缺省值4096,一般肯定是够用了。 + +**fs.file-max:** + +该参数决定了系统中所允许的文件句柄最大数目,文件句柄设置代表linux系统中可以打开的文件的数量。 + +**fs.aio-max-nr:** + +此参数限制并发未完成的请求,应该设置避免I/O子系统故障。 + +**kernel.sem:** + +以kernel.sem = 250 32000 100 128为例: + + 250是参数semmsl的值,表示一个信号量集合中能够包含的信号量最大数目。 + + 32000是参数semmns的值,表示系统内可允许的信号量最大数目。 + + 100是参数semopm的值,表示单个semopm()调用在一个信号量集合上可以执行的操作数量。 + + 128是参数semmni的值,表示系统信号量集合总数。 + +**net.ipv4.ip_local_port_range: ** + + 表示应用程序可使用的IPv4端口范围。 + +**net.core.rmem_default: ** + +表示套接字接收缓冲区大小的缺省值。 + +**net.core.rmem_max: ** + +表示套接字接收缓冲区大小的最大值。 + +**net.core.wmem_default: ** + +表示套接字发送缓冲区大小的缺省值。 + +**net.core.wmem_max: ** + +表示套接字发送缓冲区大小的最大值 + diff --git a/content/post/oracle.md b/content/post/oracle.md new file mode 100644 index 0000000..840217f --- /dev/null +++ b/content/post/oracle.md @@ -0,0 +1,496 @@ +--- +title: "Oracle 基础" +date: 2019-10-30T11:55:46+08:00 +lastmod: 2019-10-30T11:55:46+08:00 +tags: ["oracle"] +categories: ["database"] +--- + +# 字符型 +类型 | 大小 | 描述 +---- | ---- | ---- +varchar2 | 0~4000 | 可变长度字符串 +nvarchar2 | 0~1000 | Unicode字符串的可变长字符型数据 +char | 0~2000 | 定长字符型数据 +nchar | 0~1000 | Unicode字符集定长字符型数据 +long | 0~2GB | 变长字符串 + +# 数字型 +类型 | 进制 | 描述 +---- | ---- | ---- +number(p,s) | 十进制 | p最大精度38位,s小数位数 +float | 二进制 | 126位整数 + +# 日期 +类型 | 大小 | 描述 +---- | ---- | ---- +date | 公元前4712-1-1~9999-12-31 | 存储日期和时间 +timestamp | 公元前4712-1-1~9999-12-31 | 精确到小数秒,显示上下午 + +# 其他数据类型 +类型 | 大小 | 描述 +---- | ---- | ---- +blob | 4GB | 二进制 +clob | 4GB | 字符串 +bfile | 视操作系统 | 存储非结构化数据到数据库外的文件中 + +# 创建表 +```sql +CREATE TABLE table_name +( +column_name datatype [NULL|NOT NULL], +..., +PRIMARY KEY(), +CONSTRAINT table_name constraint_name FOREIGN KEY (column_name) REFERENCE table_name(column_name) ON DELETE CASCADE, +CONSTRAINT constraint_name CHECK(condition), +CONSTRAINT constraint_name UNIQUE(column_name) +) +|AS SELECT column_name1,column_name2,...FROM source_table; +DROP TABLE table_name; +``` + +# 删除表 +```bash +DROP TABLE table_name; +# 执行最快,删除数据、结构、索引、约束、触发器和索引,存储过程和索引invalid状态,直接生效,不可回滚,不释放空间 +TRUNCATE TABLE table_name; +# 执行较快,只删除数据,直接生效,不可回滚,释放空间 +DELETE FROM table_name [WHERE condition]; +# 执行最慢,只删除数据,commit 后生效,可回滚,不释放空间 +``` + +# 操作表列 +```sql +ALTER TABLE table_name +ADD column_name datatype [NULL|NOT NULL] +|MODIFY column_name new_datatype|NULL|NOT NULL +|DROP COLUMN column_name; +-- 删除列时通常追加 CASCADE CONSTRAINTS ,以删除于该列有关的约束 +``` + +# 操作主键 +```sql +ALTER TABLE table_name +ADD CONSTRAINTS constraint_name PRIMARY KEY(column_name) +|DROP CONSTRAINTS constraint_name; +``` + +# 操作外键 +```sql +ALTER TABLE table_name +ADD CONSTRAINT constraint_name FOREIGN KEY(column_name) REFERENCE table_name(column_name) ON DELETE CASCADE +|DROP CONSTRAINT constraint_name; +``` + +# 操作CHECK约束 +```sql +ALTER TABLE table_name +ADD CONSTRAINT constraint_name CHECK(condition) +|DROP CONSTRAINT constraint_name; +``` + +# 操作UNIQUE约束 +```sql +ALTER TABLE table_name +ADD CONSTRAINT constraint_name UNIQUE(column_name) +|DROP CONSTRAINT constraint_name; +``` + +# 添加数据 +```sql +INSERT INTO table_name(column_name1,column_name2,...) +VALUES(data1,data2,...) +|SELECT column_name1,column_name2...FROM table_name2; +``` + +# 修改数据 +```sql +UPDATE table_name SET column_name1=data1,column_name2=data2,...[WHERE condition]; +``` + +# 删除数据 +```sql +DELETE FROM table_name [WHERE condition]; +TRUNCATE TABLE table_name; +``` + +# 查询数据 +```sql +SELECT column_name1,column_name2,...FROM table_name [WHERE condition]; +``` + +# MERGE语句 +```sql +MERGE INTO table_name1 USING table_name2 ON(condition) WHEN MATCHED THEN ... WHEN NOT MATCHED THEN ...; +``` + +# SELECT 语句 +```sql +SELECT [DISTINCT|ALL] select_list FROM table_list [WHERE ...] [GROUP BY ...] [HAVING ...] [ORDER BY ...]; +``` + +# select_list +```sql +*|[schema.] {table|view} .*|expr[ [AS ]c_alias] +``` + +# expr +```sql +"||" 连接的字符串 | 函数 +``` + +# ORDER BY ... +```sql +{expr|positon|c_alias} {ASC|DESC} {NULLS FIRST|NULLS LAST}[ {expr|positon|c_alias} {ASC|DESC} {NULLS FIRST|NULLS LAST},...] +``` + +# 模糊查询关键字like +```sql +'_'替代一个字符,'%'替代多个字符 +``` + +# 从给定值中选取查询 +```sql +IN(data1,data2,...) +``` + +# 连接 +```sql +-- 连接,只能查询匹配记录 +SELECT select_list FROM table_name1 INNER JOIN table_name2 ON condition; +-- 左外连接 +SELECT select_list FROM table_name1 LEFT JOIN table_name2 ON condition; +-- 右外连接 +SELECT select_list FROM table_name1 RIGHT JOIN table_name2 ON condition; +-- 全外连接 +SELECT select_list FROM table_name1 FULL JOIN table_name2 ON condition; +``` + +# 内置函数 +```sql +ABS(n) -- n绝对值 +MOD(n2,n1) -- n2对n1取余 +SIGN(n) -- n的符号 +CEIL(n) -- 大于等于n的最小整数 +FLOOR(n) -- 小于等于n的最大整数 +SQRT(n) -- n的平方根 +POWER(n2,n1) -- n2的n1次幂 +EXP(n) -- e的n次幂 +LOG(n1,n2) -- n1为底n2的对数 +LN(n) -- n的自然对数 +ROUND(n2,n1) -- n2小数部分四舍五入至n1位 +TRUNC(n2,n1) -- n2截取至n1位 +CHR(n) -- 把n根据ASCII转换成字符 +ASCII(char) -- 参数首字母的ASCII值 +LENGTH(char) -- 字符串长度 +SUBSTR(char,position[,substring_length]) -- 截取字符串 +CONCAT(char1,char2) -- 连接字符串 +INSTR(string,substring[,position[,occurrence]]) -- 查找字符串 +UPPER(char) -- 转换成大写 +LOWER(char) -- 转换成小些 +INITCAP(char) -- 单词首字母大写 +NLSSORT(char[,nslparam]) -- 按指定方式排序'NLS_SORT=SCHINESE_PINYIN_M' +REPLACE(char,search_string[,replacement_string]) -- 字符串替换,默认删除 +RPAD(expr1,n[,expr2]) -- 用expr2右填充expr1至长度为n,默认空格 +LPAD(expr1,n[,expr2]) -- 用expr2左填充expr1至长度为n,默认空格 +TRIM([LEADING|TRAILING|BOTH] [trim_character FROM] trim_source) -- 删除字符串首尾指定字符 +SYSDATE -- 获取系统当前日期 +SYSTIMESTAMP -- 获取系统当前时间 +DBTIMEZONE -- 获取数据库当前时区 +ADD_MONTHS(date,integer) -- 指定日期增加指定月份数 +SESSIONTIMEZONE -- 获取当前会话的时区 +LAST_DAY(date) -- 获取指定日期对应月份的最后一天 +NEXT_DAY(date,char) -- 获取下周char的日期 +CURRENT_DATE -- 获取会话时区的当前日期 +EXTRACT(datetime) -- 从指定时间中获取指定部分 +MONTHS_BETWEEN(date1,date2) -- 获取两个时间之间的月份数 +NET_TIME(date,timezone1,timezone2) -- 获取时区1中的时间转换到时区2后的时间 +TO_CHAR(n[,fmt]) -- 转换为字符类型 +TO_DATE(n[,fmt]) -- 转换为时间类型 +TO_NUMBER(n[,fmt]) -- 转换为数字类型 +LNNVL(condition) -- 排除指定条件函数 +NVL(expr1,expr2) -- expr1为空时返回expr2 +NVL2(expr1,expr2,expr3) -- expr1为空时返回expr3,不为空返回expr2 +AVG([DISTINCT|ALL ]expr) -- 获取平均值 +COUNT(*|[DISTINCT|ALL ]expr) -- 获取数量 +SUM([DISTINCT|ALL ]expr) -- 获取和 +SELECT USER FROM DUAL; -- 返回当前会话的登录名 +USERENV(param) -- 返回当前会话的信息 +SYS_CONTEXT(namespace,param) -- 返回oracle已创建的context +DECODE(expr,search,result[,search1,result1...]) -- expr结果是search返回result +``` + +# 查看所有默认表空间 +```sql +SELECT TABLESPACE_NAME FROM DBA_TABLESPACES; +``` + +# 查看指定用户默认表空间 +```sql +SELECT DEFAULT_STAPCE,USERNAME FROM DBA_USERS WHERE USERNAME='username'; +``` + +# 创建表空间 +```sql +CREATE TABLESPACE tablespace_name +DATAFILE filename +SIZE size +[AUTOEXTEND [ON NEXT size|OFF]] +[MAXSIZE size] +[PERMANENT|TEMPORARY] 永久/临时表空间,默认永久 +[EXTENT MANAGEMENT [DICTIONARY|LOCAL 字典/本地管理方式,默认本地 +[AUTOALLOCATE|UNIFORM. [SIZE integer[K|M]]]]; +``` + +# 重命名表空间 +```sql +ALTER TABLESPACE oldname RENAME TO newname; +``` + +# 修改表空间大小 +```sql +ALTER DATABASE DATAFILE filename RESIZE size; +``` + +# 增加表空间大小 +```sql +ALTER TABLESPACE tablespace_name ADD DATAFILE filename SIZE size; +``` + +# 设置表空间读写状态 +```sql +ALTER TABLESPACE tablespace_name READ {ONLY|WRITE}; +``` + +# 设置表空间可用状态 +```sql +ALTER TABLESPACE tablespace_name {ONLINE|OFFLINE [NORMAL|TEMPORARY|IMMEDIATE]}; +``` + +# 创建大文件表空间 +```sql +CREATE BIGFILE TABLESPACE tablespace_name DATAFILE filename SIZE size; +``` + +# 删除表空间 +```sql +DROP TABLESPACE tablespace_name +[INCLUDING CONTENTS AND DATAFILES] 数据文件删除 +[CASCADE CONSTRAINTS]; 完整性删除 +``` + +# 查看表空间大小 +```sql +SELECT TABLESPACE_NAME,FILE_NAME,BYTES FROM DBA_DATA_FILES WHERE TABLESPACE_NAME=tablespace_name; +``` + +# 查看表空间剩余 空间 +```sql +SELECT TABLESPACE_NAME,BYTES FROM DBA_FREE_SPACES; +``` + +# 创建/修改用户 +```sql +CREATE|ALTER +USER user_name +IDENTIFIED BY password +[DEFAULT TABLESPACE tablespace_name ] +[QUOTA size|UNLIMITED ON tablespace_name ] 用户使用表空间的最大值 +[PROFILE profile ] 概要文件 +[PASSWORD EXPIRE ] 用户密码过期 +[ACCOUNT LOCK|UNLOCK]; 默认锁定状态 +``` + +# 删除用户 +```sql +DROP USER user_name CASCADE; +``` + +# 授予系统权限 +```sql +GRANT +system_privileges|ALL PRIVILEGES 权限 +TO {user IDENTIFIED BY password|role } 用户/角色 +[WITH ADMIN OPTION]; 授予其他用户或角色系统权限 +``` + +# 授予对象权限 +```sql +GRANT +object_privilege|ALL 权限 +ON schema.object 对象 +TO user_name|role_name 用户/角色 +[WITH ADMIN OPTION ] 授予其他用户或角色系统权限 +[WITH THE GRANT ANY OBJECT]; 授予其他用户或角色对象权限 +``` + +# 撤销系统权限 +```sql +REVOKE system_privilege FROM +user|role; +``` + +# 撤销对象权限 +```sql +REVOKE +object_privilege |ALL +ON schema.object FROM +user_name|role_name +[CASCADE CONSTRAINTS]; +``` + +# 数据字典 +数据 | 字典 +---- | ---- +系统权限 | DBA_SYS_PRIVS +对象权限 | DBA_TAB_PRIVS +用户角色 | DBA_ROLE_PRIVS + +# 创建角色 +```sql +CREATE|ALTER //创建/修改 +ROLE role_name +[NOT IDENTIFIED|IDENDIFIED BY [password]]; +GRANT //填充权限 +system_privilege|ALL PRIVILEGES +TO role_name +[WITH ADMIN OPTION]; +-- 角色创建完成后不能直接使用,需将角色赋予用户才能使用 +GRANT role_name TO user_name; +SET ROLE role_name -- 设置角色生效 +SET ROLE ALL -- 设置所有角色生效 +SET ROLE ALL EXCEPT role_name -- 设置只有role_name失效 +SET ROLE NONE -- 设置所有角色失效 +``` + +# 删除角色 +```sql +DROP ROLE role_name; +``` + +# 脱机备份(冷备份)/恢复 +```sql +关闭数据库服务后直接复制需要的文件,包括数据文件和控制文件 +``` + +# 联机备份(热备份) +```sql +ARCHIVE LOG LIST 查看本机数据库的日志状态 +ALTER SYSTEM SET LOG_ARCHIVE_START=TRUE SCOPE=SPFILE; 设置日志模式为归档 +SHUTDOWN IMMEDIATE; 关闭数据库 +STARTUP MOUNT; 启动mount实例 +ALTER DATABASE ARCHIVELOG; 更改数据库为归档日志模式 +ALTER DATABASE OPEN; 更改数据库状态为打开模式 +ALTER TABLESPACE tablespace_name BEGIN BACKUP; 开始备份数据库 +复制文件到其他目录 +ALTER TABLESPACE tablespace_name END BACKUP; 结束备份操作 +``` + +# 恢复 +```sql +ALTER SYSTEM ARCHIVE LOG CURRENT; 归档当前日志 +ALTER SYSTEM SWITCH LOGFILE; 切换日志文件 +SELECT * FROM v$RECOVER_FILE 获取文件编号 +ALTER DATABASE DATAFILE file_id OFFLINE DROP; 把要恢复的数据文件脱机 +ALTER DATABASE OPEN; 更改数据库状态为打开模式 +RECOVER DATAFILE file_id; 恢复数据文件 +ALTER DATABASE DATAFILE file_id ONLINE; 设置数据文件联机 +``` + +# EXP工具导出数据 +```bash +exp db_user/password 登陆数据库的用户名和密码,非SYS +``` + +# EXP工具直接导出表 +```bash +exp db_user/password file="filename.dmp" tables="table_name,..." +``` + +# EXP工具导出表空间 +```bash +exp db_user/password file="filename.dmp" tablespaces="tablespaces_name" +``` + +# EXPDP导出数据 +```sql +CREATE DIRECTORY directory_name AS 'file_name'; 目录名称 文件名称 +GRANT READ,WRITE ON DIRECTORY directory_name TO db_user; 授权用户使用该目录 +#expdp db_user/password directory=directory_name dumpfile=file_name tables=table_name; +``` + +# IMP导入数据 +```sql +imp db_user/password +``` + +# IMP直接导入表 +```sql +imp db_user/password file="filename.dmp" tables="table_name,..." +``` + +# IMPDP导入数据 +```sql +impdp db_user_password +``` + +# IMPDP直接导入表 +```bash +impdp db_user/password directory=dir dumpfile=filename.dmp tables=table_name; +``` + +# RMAN工具配置 +```sql +CONN /AS SYSDBA; 连接恢复目录数据库 +CREATE USER rman_user IDENTIFIED BY password DEFAULT TABLESPACE tablespace_name; 创建恢复用户 +GRANT RECOVERY_CATALOG_OWNER TO rman_user; 为新创建的用户授权 +#rman +CONN CATALOG rman_user/password; 连接新创建的用户 +CREATE CATALOG; 创建恢复目录 +``` + +# RMAN工具使用 +```sql +#rman target db_user/password@servicename catalog rman_user/password 连接恢复目录数据库 +CONNECT TARGET db_user/password@servicename; 连接目标数据库 +CONNECT CATALOG rman_user/password@servicename; 连接恢复目录数据库 +REGISTER database; 在恢复目录数据库中注册数据库 +``` + +# 手动分配通道 +```sql +关闭目标数据库,启动到mount状态,运行: +run +{ + ALLOCATE CHANNEL channel_name1 DEVICE TYPE {sbt|disk}; + ... + BACKUP [level] [backup type] [option] +} +``` + +# 自动分配通道 +```sql +CONFIGURE DEVICE TYPE {sbt|disk} PARALLELISM n; 指定通道类型和名称 +CONFIGURE DEFAULT DEVICE TYPE {sbt|disk}; 指定默认设备类型 +BACKUP [level] [backup type] [option]; +``` + +# BACKUP 参数 +```sql +level 备份增量,1、2、3、4或者FULL(全备份) +backup type 对象类型,database、datafile、tablespace、controlfilecopy、archivelog all +option channel备份使用的通道 maxsetsize定义备份集的最大值 +``` + +# RESTORE还原 +```sql +RESTORE database_object; +database_object: DATABASE(mount),TABLESPACE(open),DATAFILE,CONTROLFILE(mount),ARCHIVELOG,SPFILE(mount) +``` + +# RECOVER同步恢复 +```sql +RECOVER database_object; +database_object: DATABASE(mount),TABLESPACE(open),DATAFILE +``` + diff --git a/content/post/oracle11g-install.md b/content/post/oracle11g-install.md new file mode 100644 index 0000000..0dc7973 --- /dev/null +++ b/content/post/oracle11g-install.md @@ -0,0 +1,199 @@ +--- +title: "CentOS7.4 静默安装 Oracle11g" +date: 2019-10-30T11:54:49+08:00 +lastmod: 2019-10-30T11:54:49+08:00 +tags: ["oracle"] +categories: ["database"] +--- + +# 环境 +- CentOS7.4 最小安装 +- 数据库软件 + - linux.x64_11gR2_database_1of2.zip + - linux.x64_11gR2_database_2of2.zip + +# 操作系统配置 +- 关闭 SELinux + ```bash + sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + ``` +- 关闭防火墙,或者放行 tcp 1521 端口 + ```bash + systemctl disable firewalld + systemctl stop firewalld + ``` +- 重启操作系统 + ```bash + reboot + ``` + +# 安装依赖 +- 安装可能用到的工具 + ```bash + yum install epel-release + yum clean all + yum makecache fast + yum install vim unzip rlwrap + ``` +- 安装 oracle 需要的包 + ```bash + yum install binutils compat-libcap1 compat-libstdc++-33 \ + compat-libstdc++-33*i686 gcc gcc-c++ glibc glibc*.i686 \ + glibc-devel glibc-devel*.i686 ksh libaio libaio*.i686 libaio-devel \ + libgcc libgcc*.i686 libstdc++ libstdc++*.i686 libstdc++-devel \ + libXi libXi*.i686 libXtst libXtst*.i686 make sysstat unixODBC \ + unixODBC*.i686 unixODBC-devel unixODBC-devel*.i686 + ``` + +# 配置安装环境 +- 创建 oracle 用户 + ```bash + groupadd oinstall + groupadd dba + useradd -g oinstall -G dba oracle + ``` +- 创建 oracle 安装目录 + ```bash + mkdir -p /opt/oracle/app/product/11.2.0 + mkdir -p /opt/oracle/app/oradata + mkdir -p /opt/oracle/app/fast_recovery_area + mkdir -p /opt/oracle/inventory + chown -R oracle:oinstall /opt/oracle + chmod -R 775 /opt/oracle + ``` +- 修改 sysctl.conf + ```bash + cat << EOF >> /etc/sysctl.conf + fs.aio-max-nr = 1048576 + fs.file-max = 6815744 + #物理内存一半和4G中的较大者 + kernel.shmmax = 4294967296 + #shmmax / 4k (getconf PAGESIZE) + kernel.shmall = 1048576 + kernel.shmmni = 4096 + kernel.sem = 250 32000 200 200 + net.ipv4.ip_local_port_range = 9000 65500 + net.core.rmem_default = 262144 + net.core.wmem_default = 262144 + net.core.wmem_max = 1048586 + net.core.rmem_max = 4194304 + EOF + sysctl -p + ``` +- 修改 limits.conf + ```bash + cat << EOF >> /etc/security/limits.conf + oracle soft nproc 2047 + oracle hard nproc 16384 + oracle soft nofile 1024 + oracle hard nofile 65536 + EOF + ``` +- 修改 login + ```bash + cat << EOF >> /etc/pam.d/login + session required /lib64/security/pam_limits.so + session required pam_limits.so + EOF + ``` +- 修改 profile + ```bash + cat << EOF >> /etc/profile + if [ \$USER = "oracle" ] ; then + if [ \$SHELL = "/bin/ksh" ]; then + ulimit -p 16384 + ulimit -n 65536 + else + ulimit -u 16384 -n 65536 + fi + umask 022 + fi + EOF + ``` +- 修改 oracle 用户的 .bash_profile + ```bash + cat << EOF >> /home/oracle/.bash_profile + export ORACLE_BASE=/opt/oracle/app + export ORACLE_HOME=\$ORACLE_BASE/product/11.2.0 + export ORACLE_SID=orcl + export PATH=\$PATH:\$ORACLE_HOME/bin + #export NLS_LANG="SIMPLIFIED CHINESE_CHINA.AL32UTF8" + #export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK" + EOF + ``` + +# 安装数据库 +- 上传数据库软件到 /root 下,解压 + ```bash + unzip linux.x64_11gR2_database_1of2.zip -d /home/oracle/ + unzip linux.x64_11gR2_database_2of2.zip -d /home/oracle/ + chown -R oracle.oinstall /home/oracle/database + ``` +- 切换到 oracle 用户,后续操作都在该 oracle 用户下执行 + ```bash + su - oracle + ``` +- 创建 response 文件 + ```bash + cd /home/oracle + cp database/response/*.rsp ./ + ``` +- 修改 db_install.rsp + ```bash + sed -i \ + -e '/^oracle.install.option=/s#=.*$#=INSTALL_DB_SWONLY#' \ + -e '/^UNIX_GROUP_NAME=/s#=.*$#=oinstall#' \ + -e '/^INVENTORY_LOCATION=/s#=.*$#=/opt/oracle/inventory#' \ + -e '/^SELECTED_LANGUAGES=/s#=.*$#=en,zh_CN#' \ + -e '/^ORACLE_HOME=/s#=.*$#=/opt/oracle/app/product/11.2.0#' \ + -e '/^ORACLE_BASE=/s#=.*$#=/opt/oracle/app#' \ + -e '/^oracle.install.db.InstallEdition=/s#=.*$#=EE#' \ + -e '/^oracle.install.db.DBA_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle.install.db.OPER_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle.install.db.config.starterdb.type=/s#=.*$#=GENERAL_PURPOSE#' \ + -e '/^DECLINE_SECURITY_UPDATES=/s#=.*$#=true#' \ + /home/oracle/db_install.rsp + ``` +- 无需修改 netca.rsp +- 修改 dbca.rsp + ```bash + sed -i \ + -e '/^GDBNAME=/s#=.*$#=orcl#' \ + -e '/^SID=/s#=.*$#=orcl#' \ + -e '/^SYSPASSWORD=/s#=.*$#=111111#' \ + -e '/^SYSTEMPASSWORD=/s#=.*$#=111111#' \ + -e '/^CHARACTERSET=/s#=.*$#=ZHS16GBK#' \ + /home/oracle/dbca.rsp + ``` +- 安装 oracle 软件 + ```bash + cd /home/oracle/database + ./runInstaller -silent -responseFile /home/oracle/db_install.rsp -ignorePrereq + #安装成功后,系统提示需要在 root 下执行两个脚本 + /opt/oracle/invertory/orainstRoot.sh + /opt/oracle/app/product/11.2.0/root.sh + ``` +- 配置监听 + ```bash + netca /silent /responseFile /home/oracle/netca.rsp + #配置成功后,监听启动,查看监听状态 + lsnrctl status + ``` +- 创建数据库 + ```bash + dbca -silent -responseFile /home/oracle/dbca.rsp + #查看屏幕输出的创建进度 + ``` + +# 简单使用 +- 登陆数据库 + ``` + [oracle@localhost ~]$ rlwrap sqlplus / as sysdba + SQL*Plus: Release 11.2.0.1.0 Production on 星期一 6月 25 14:46:58 2018 + Copyright (c) 1982, 2009, Oracle. All rights reserved. + 连接到: + Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production + With the Partitioning, OLAP, Data Mining and Real Application Testing options + SQL> + ``` + diff --git a/content/post/oracle12c-install.md b/content/post/oracle12c-install.md new file mode 100644 index 0000000..3a2d1dd --- /dev/null +++ b/content/post/oracle12c-install.md @@ -0,0 +1,228 @@ +--- +title: "CentOS7 静默安装 Oracle12c" +date: 2019-10-30T11:11:13+08:00 +lastmod: 2019-10-30T11:11:13+08:00 +keywords: [] +tags: ["oracle"] +categories: ["database"] +--- + +# 环境 +- CentOS7.5 最小安装 +- 数据库软件 + - linuxx64_12201_database.zip + +# 操作系统配置 +- 关闭 SELinux + ```bash + sed -i '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config + ``` +- 关闭防火墙 + ```bash + systemctl disable firewalld + systemctl stop firewalld + ``` +- 禁用 NetworkManager + ```bash + systemctl disable NetworkManager + systemctl stop NetworkManager + ``` +- 重启操作系统 + ```bash + reboot + ``` + +# 安装依赖 +- 安装可能用到的工具 + ```bash + yum install epel-release + yum clean all + yum makecache fast + yum install vim unzip rlwrap + ``` +- 安装 oracle 需要的包 + ```bash + yum install binutils compat-libcap1 compat-libstdc++-33 \ + compat-libstdc++-33*i686 gcc gcc-c++ glibc glibc*.i686 \ + glibc-devel glibc-devel*.i686 ksh libaio libaio*.i686 libaio-devel \ + libgcc libgcc*.i686 libstdc++ libstdc++*.i686 libstdc++-devel \ + libXi libXi*.i686 libXtst libXtst*.i686 make sysstat unixODBC \ + unixODBC*.i686 unixODBC-devel unixODBC-devel*.i686 + ``` + +# 配置安装环境 +- 创建 oracle 用户 + ```bash + groupadd oinstall + groupadd dba + groupadd oper + useradd -g oinstall -G dba,oper oracle + ``` +- 创建 oracle 安装目录 + ```bash + mkdir -p /opt/oracle/app/product/12.2.0 + mkdir -p /opt/oracle/app/oradata + mkdir -p /opt/oracle/app/fast_recovery_area + chown -R oracle:oinstall /opt/oracle + chmod -R 775 /opt/oracle + ``` +- 修改 sysctl.conf + ```bash + cat << EOF >> /etc/sysctl.conf + fs.aio-max-nr = 1048576 + fs.file-max = 6815744 + #物理内存一半和4G中的较大者,当前服务器16G + kernel.shmmax = 8589934592 + #shmmax / 4k (getconf PAGESIZE) + kernel.shmall = 2097152 + kernel.shmmni = 4096 + kernel.sem = 250 32000 200 200 + net.ipv4.ip_local_port_range = 9000 65500 + net.core.rmem_default = 262144 + net.core.wmem_default = 262144 + net.core.wmem_max = 1048586 + net.core.rmem_max = 4194304 + EOF + sysctl -p + ``` +- 修改 limits.conf + ```bash + cat << EOF >> /etc/security/limits.conf + oracle soft nproc 2047 + oracle hard nproc 16384 + oracle soft nofile 1024 + oracle hard nofile 65536 + oracle soft stack 10240 + EOF + ``` +- 修改 login + ```bash + cat << EOF >> /etc/pam.d/login + session required /lib64/security/pam_limits.so + session required pam_limits.so + EOF + ``` +- 修改 profile + ```bash + cat << EOF >> /etc/profile + if [ \$USER = "oracle" ] ; then + if [ \$SHELL = "/bin/ksh" ]; then + ulimit -p 16384 + ulimit -n 65536 + else + ulimit -u 16384 -n 65536 + fi + umask 022 + fi + EOF + ``` +- 修改 oracle 用户的 .bash_profile + ```bash + cat << EOF >> /home/oracle/.bash_profile + export ORACLE_BASE=/opt/oracle/app + export ORACLE_HOME=\$ORACLE_BASE/product/12.2.0 + export ORACLE_SID=orcl + export PATH=\$PATH:\$ORACLE_HOME/bin + #export NLS_LANG="SIMPLIFIED CHINESE_CHINA.AL32UTF8" + #export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK" + EOF + ``` + +# 安装数据库 +- 上传数据库软件到 /root 下,解压 + ```bash + unzip linuxx64_12201_database.zip -d /home/oracle/ + chown -R oracle.oinstall /home/oracle/database + ``` +- 切换到 oracle 用户,后续操作都在该 oracle 用户下执行 + ```bash + su - oracle + ``` +- 创建 response 文件 + ```bash + cd /home/oracle + cp database/response/*.rsp ./ + ``` +- 修改 db_install.rsp + ```bash + sed -i \ + -e '/^oracle\.install\.option=/s#=.*$#=INSTALL_DB_SWONLY#' \ + -e '/^UNIX_GROUP_NAME=/s#=.*$#=oinstall#' \ + -e '/^INVENTORY_LOCATION=/s#=.*$#=/opt/oracle/oraInventory#' \ + -e '/^ORACLE_HOME=/s#=.*$#=/opt/oracle/app/product/12.2.0#' \ + -e '/^ORACLE_BASE=/s#=.*$#=/opt/oracle/app#' \ + -e '/^oracle\.install\.db\.InstallEdition=/s#=.*$#=EE#' \ + -e '/^oracle\.install\.db\.OSDBA_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle\.install\.db\.OSOPER_GROUP=/s#=.*$#=oper#' \ + -e '/^oracle\.install\.db\.OSBACKUPDBA_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle\.install\.db\.OSDGDBA_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle\.install\.db\.OSKMDBA_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle\.install\.db\.OSRACDBA_GROUP=/s#=.*$#=dba#' \ + -e '/^oracle.install.db.config.starterdb.type=/s#=.*$#=GENERAL_PURPOSE#' \ + -e '/^oracle\.install\.db\.config\.starterdb\.characterSet=/s#=.*$#=ZHS16GBK#' \ + -e '/^DECLINE_SECURITY_UPDATES=/s#=.*$#=true#' \ + /home/oracle/db_install.rsp + ``` +- 无需修改 netca.rsp +- 修改 dbca.rsp + ```bash + sed -i \ + -e '/^gdbName=/s#=.*$#=orcl#' \ + -e '/^sid=/s#=.*$#=orcl#' \ + -e '/^createAsContainerDatabase=/s#=.*$#=true#' \ + -e '/^numberOfPDBs=/s#=.*$#=1#' \ + -e '/^pdbName=/s#=.*$#=pdborcl#' \ + -e '/^templateName=/s#=.*$#=General_Purpose.dbc#' \ + -e '/^pdbAdminPassword=/s#=.*$#=P@sswo2d#' \ + -e '/^sysPassword=/s#=.*$#=P@sswo2d#' \ + -e '/^systemPassword=/s#=.*$#=P@sswo2d#' \ + /home/oracle/dbca.rsp + ``` +- 安装 oracle 软件 + ```bash + cd /home/oracle/database + ./runInstaller -silent -responseFile /home/oracle/db_install.rsp -ignorePrereq + #安装成功后,系统提示需要在 root 下执行两个脚本 + /opt/oracle/oraInventory/orainstRoot.sh + /opt/oracle/app/product/12.2.0/root.sh + ``` +- 配置监听 + ```bash + netca /silent /responseFile /home/oracle/netca.rsp + #配置成功后,监听启动,查看监听状态 + lsnrctl status + ``` +- 创建数据库 + ```bash + dbca -silent -createDatabase -responseFile /home/oracle/dbca.rsp + #查看屏幕输出的创建进度 + ``` + +# 简单使用 +- 登陆数据库,切换到 pdb 数据库 + ``` + [oracle@fpManager2 ~]$ rlwrap sqlplus / as sysdba + SQL*Plus: Release 12.2.0.1.0 Production on 星期一 6月 25 14:41:16 2018 + Copyright (c) 1982, 2016, Oracle. All rights reserved. + 连接到: + Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production + SQL> show con_name + CON_NAME + ------------------------------ + CDB$ROOT + SQL> alter session set container=pdborcl; + 会话已更改。 + SQL> show con_name + CON_NAME + ------------------------------ + PDBORCL + SQL> select file_name from dba_data_files; + FILE_NAME + -------------------------------------------------------------------------------- + /opt/oracle/app/oradata/orcl/pdborcl/system01.dbf + /opt/oracle/app/oradata/orcl/pdborcl/sysaux01.dbf + /opt/oracle/app/oradata/orcl/pdborcl/undotbs01.dbf + /opt/oracle/app/oradata/orcl/pdborcl/users01.dbf + SQL> + ``` + diff --git a/content/post/oracle_1.md b/content/post/oracle_1.md new file mode 100644 index 0000000..9476b8d --- /dev/null +++ b/content/post/oracle_1.md @@ -0,0 +1,242 @@ +--- +title: "Oracle 笔记" +date: 2019-10-30T11:53:08+08:00 +lastmod: 2019-10-30T11:53:08+08:00 +tags: ["oracle"] +categories: ["database"] +--- + +# 导出一个表的表结构 sql +```sql +set heading off; +set echo off; +set pages 999; +set long 90000; +spool table_name.sql +select dbms_metadata.get_ddl('TABLE','table_name','user_name') from dual; +spool off; +``` + +# 到处一个用户的全部表结构 sql +```sql +set pagesize 0 +set long 90000 +set feedback off +set echo off +spool user_tables.sql +SELECT DBMS_METADATA.GET_DDL('TABLE',t1.table_name) FROM USER_TABLES t1; +spool off; +``` + +# 设置 oracle 客户端的字符集 +```sql +select * from v$nls_parameters; +-- 在输出中找到 NLS_LANGUAGE、NLS_TERRITORY、NLS_CHARACTERSET 这三个变量的值 +-- 环境变量 NLS_LANG 就是由这三个变量组成:NLS_LANG = language_territory.charset +``` + +# 终端连接(ssh) sqlplus 中文乱码 +- 设置该终端与数据库服务器当前字符集编码一致即可 + +# 修改数据库字符集 +```sql +shutdown immediate; (把database停了) +startup mount; (把database重开去可更改情況) +alter system enable restricted session; +alter system set job_queue_processes=0; +alter system set aq_tm_processes=0; +alter database open; +alter database character set internal_use utf8; +shutdown immediate; +startup; (重开正常oracle) +``` + +# Oracle 12c PDB 管理 +- 查看当前模式 + ```sql + show con_name; -- 默认 CDB$ROOT + ``` +- 查看 CDB$ROOT 下所有 pdbs + ```sql + select name, open_mode from v$pdbs; + -- 或者 + show pdbs; + ``` +- 创建 PDB 数据库 + ```sql + create pluggable database pdborcl1 + admin user pdborcl1_adm identified by pdborcl1_pass + roles=(dba,connect,resource) + file_name_convert=('/opt/oracle/app/oradata/orcl/pdbseed','/opt/oracle/app/oradata/orcl/pdborcl1') + path_prefix='/opt/oracle/app/oradata/orcl/pdborcl1'; + ``` +- PDB 数据库创建完成后进入 mount 状态,需手动启动 + ```sql + alter pluggable database {pdborcl1[,pdborcl2]|all [except pdborcl2]} open [read only] [force]; + ``` +- 进入打开的 PDB 数据库 + ```sql + alter session set container=pdborcl1; + ``` +- 切换回 CDB$ROOT + ```sql + alter session set container=cdb$root; + ``` +- 关闭 PDB 数据库 + ```sql + alter pluggable database {pdborcl1[,pdborcl2]|all [except pdborcl2]} close [immediate]; + ``` +- 删除关闭的 PDB 数据库 + ```sql + drop pluggable database pdborcl1 [including datafiles]; + ``` + +# sqlplus 到处 csv 文件 +```sql +sqool /home/oracle/sql_result.csv +select '"' || field1 || '","' || field2 || '","' ... '","' || fieldn || '"' from table1; +spool off +``` + +# 系统变量 + +## 常用设置 + +- 域输出分隔符 + ```sql + set colsep ' '; + ``` +- 设置查询出来的数据分多少页显示,如果需要连续的数据,中间不要出现空行就把newp设置为none,这样输出的数据行都是连续的,中间没有空行之类的 + ```sql + set newp none; + ``` +- 显示start启动的脚本中的每个sql命令,缺省为on + ```sql + set echo off; + ``` +- 设置运行命令是是否显示语句 + ```sql + set echo on; + ``` +- 设置显示"已选择XX行" + ```sql + set feedback on; + ``` +- 回显本次sql命令处理的记录条数,缺省为on即去掉最后的 "已经选择10000行" + ```sql + set feedback off; + ``` +- 输出域标题,缺省为on 设置为off就去掉了select结果的字段名,只显示数据 + ```sql + set heading off; + ``` +- 输出每页行数,缺省为24,为了避免分页,可设定为0。 + ```sql + set pagesize 0; + ``` +- 输出一行字符个数,缺省为80 + ```sql + set linesize 80; + ``` +- 输出number类型域长度,缺省为10 + ```sql + set numwidth 12; + ``` +- 显示脚本中的命令的执行结果,缺省为on + ```sql + set termout off; + ``` +- 去除标准输出每行的拖尾空格,缺省为off + ```sql + set trimout on; + ``` +- 去除重定向(spool)输出每行的拖尾空格,缺省为off + ```sql + set trimspool on; + ``` +- 设置允许显示输出类似dbms_output + ```sql + set serveroutput on; + ``` +- 设置显示已用时间 + ```sql + set timing on; + ``` +- 设置允许对执行的sql进行分析 + ```sql + set autotrace on; + ``` + +## 系统变量记录 + +- ARRAY[SIZE] {20(默认值)|n} sqlplus 一次从数据库获取的行数,有效值为1至5000. 大的值可提高查询和子查询的有效性,可获取许多行,但也需要更多的内存.当超过1000时,其效果不大. +- AUTO[COMMIT] {OFF(默认值)|ON|IMM[EDIATE]} 控制ORACLE对数据库的修改的提交. 置ON时,在ORACLE执行每个SQL命令或PL/SQL块后对数据库提交修改;置OFF时则制止自动提交,需要手工地提交修改,例如用SQL的COMMIT命令. IMMEDIATE功能同ON. +- BLO[CKTERMINATOR] {.(默认值)|C} 置非字母数字字符,用于结束PL/SQL块.要执行块时,必须发出RUN命令或/命令. +- CMDS[EP] {;|C|OFF(默认值)|ON} 置非字母数字字符,用于分隔在一行中输入的多个SQL/PLUS命令.ON或OFF控制在一行中是否能输入多个命令. ON时将自动地将命令分隔符设为分号(.其中C表示所置字符. +- COM[PATIBILITY] {V5|V6|V7|NATIVE(默认值)} 指定当前所链接的ORACLE版本.如果当前ORACLE的版本为5,则置COMPATIBILITY为V5; 为版本6时置成V6; 为版本7时置成V7. 如果希望由数据库决定该设置,在置成NATIVE. +- CON[CAT] {.(默认值)|C|OFF|ON(默认值)} 设置结束一替换变量引用的字符.在中止替换变量引用字符之后可跟所有字符,作为体会组成部分,否则sqlplus将解释为替换变量名的一部分.当CONCAT开关为ON时,sqlplus可重置CONCAT的值为点(.). +- COPYC[OMMIT] {0(默认值)|n} 控制COPY命令提交对数据库修改的批数.每次拷贝n批后,将提交到目标数据库.有效值为0到5000. 可用变量ARRAYSIZE设置一批的大小.如果置COPYCOMMIT为0,则仅在COPY操作结束时执行一次提交. +- CRT crt 改变 sqlplus RUNFORM 命令使用的缺省CRT文件.如果置CRT不包含什么,则crt仅包含''''.如果在一个Form的系统调用期间,要使用NEW.CRT(缺省CRT是OLD.CRT),可按下列形式调用Form: + ``` + SQL>;RUNFORM -C NEW form名 + 或者 + SQL>;SET CRT NEW + SQL>;RUNFORM form名 + # 第二中方法存储CRT选择,以致在下次运行RUNFORM命令(是在同一次sqlplus交互中)时,不需要指定. + ``` +- DEF[INE] {&|C|OFF|ON(默认值)} 设置在替换变量时所使用的字符.ON或OFF控制sqlplus是否扫描替换变量的命令及用他们的值代替. DEFINE的ON或OFF的设置控制SCAN变量的设置. +- ECHO {OFF|ON} 控制START命令是否列出命令文件中的每一命令.为ON时,列出命令;为OFF时,制止列清单. +- EMBEDDED {OFF(默认值)|ON} 控制每一报表在一页中开始的地方. 为OFF时,迫使每一报表是在新页的顶部开始;为ON时,运行一报表在一页的任何位置开始. +- ESC[APE] {\(默认值)|C|OFF(默认值)|ON} 定义作为Escape字符的字符.为OFF时,使Escape字符不起作用.为ON时,使Escape字符起作用. +- FEED[BACK] {6(默认值)|n|OFF|ON} 显示由查询返回的记录数.ON和OFF置显示为开或关.置FEEDBACK为ON时,等价于置n为1. 如果置FEEDBACK为0,等价于将它置成OFF. +- FLU[SH] {OFF|ON(默认值)} 控制输出送至用户的显示设备.为OFF时,运行操作系统做缓冲区输出;为ON时,不允许缓冲. 仅当非交互方式运行命令文件时使用OFF,这样可减少程序I/O总是,从而改进性能. +- HEA[DING] {OFF|ON(默认值)} 控制报表中列标题的打印.为ON时,在报表中打印列标题;为OFF时禁止打印列标题. +- HEADS[EP] {|(默认值)|C|OFF|ON(默认值)} 定义标题分隔字符.可在COLUMN命令中使用标题分隔符,将列标题分成多行.ON和OFF将标题分隔置成开或关.当标题分隔为关(OFF)时,sqlplus打印标题分隔符像任何字符一样. +- LIN[ESIZE] {80(默认值)|n} 置sqlplus在一行中显示的字符总数,它还控制在TTITLE和BTITLE中对准中心的文本和右对齐文本. 可定义LINESIZE为1至最大值,其最大值依赖于操作系统. +- LONG {80(默认值)|n} 为显示和拷贝LONG类型值的最大宽度的设置. 对于ORACLE7, n的最大值为2G字节;对于版本6,最大值为32767. +- LONGC[HUNKSIZE] {80(默认值)|n} 为sqlplus检索LONG类型值的增量大小.由于内存的限制,可按增量检索,该变量仅应用于ORACLE7. +- MAXD[ATA] n 置sqlplus可处理的最大行宽字符数,其缺省值和最大值在不同操作系统中是可变的. +- NEWP[AGE] {1(默认值)|n} 置每一页的头和顶部标题之间要打印的空行数.如果为0, 在页之间送一换号符,并在许多终端上清屏. +- NULL text 设置表示空值(null)的文本,如果NULL没有文本,则显示空格(缺省时). 使用COLUMN命令中的NULL子句可控制NULL变量对该列的设置. +- NUMF[ORMAT] 格式 设置显示数值的缺省格式,该格式是数值格式. +- NUM[WIDTH] {10(默认值)|n} 对显示数值设置缺省宽度. +- PAGES[IZE] {14(默认值)|n} 置从顶部标题至页结束之间的行数.在11英寸长的纸上打印报表,其值为54,上下各留一英寸(NEWPAGE值为6). +- PAU[SE] {OFF(默认值)|ON|text} 在显示报表时,控制终端滚动.在每一暂停时,必须按RETURN键.ON将引起sqlplus在每一报表输出页开始时暂停.所指定的文本是每一次sqlplus暂停时显示的文本.如果要键入多个词,必须用单引号将文本括起来. +- RECSEP {WR[APPED](默认值)|EA[CH]|OFF} 指定显示或打印记录分行符的条件.一个记录分行符,是由RECSEPCHAR指定的字符组成的单行.空格为RECSEPCHAR的默认字符. +- RECSEPCHAR { |C} 告诉sqlplus在哪儿做记录分隔.例如将RECSEP置成WRAPPED,在每一缠绕行之后,打印记录分行符.如果将RECSEP置成EACH,sqlplus在每一行后打印一记录分行符.如果将RECSEP置成OFF, sqlplus不打印分行符. +- SCAN {OFF|ON(默认值)} 控制对存在的替换变量和值的扫描.OFF禁止替换变量和值的处理; ON则允许正常处理. +- SERVEROUT[PUT] {OFF|ON} [SIZE n] 控制在sqlplus中的存储过程是否显示输出.OFF时为禁止; ON时则显示输出. SIZE设置缓冲输出的字节数,缺省值为2000, n不能小于2000或大于一百万. +- SHOW[MODE] {OFF(默认值)|ON} 控制sqlplus在执行SET命令时是否列出其新老值old或new的设置. +- SPA[CE] {1(默认值)|n} 设置输出列之间空格的数目,其最大值为10. +- SQLC[ASE] {MIX[ED](默认值)|LO[WER]|UP[PER]} 先于执行之前,将SQL命令和PL/SQL块的大小写进行转换. sqlplus将转换命令中的全部文本,包括带引号的直接量和标示符.SQLCASE不改变SQL缓冲区本身. +- SQLCO[NTINUE] {>;(默认值)|文本} 在一附加行上继续一sqlplus命令时,sqlplus以该设置的字符序列进行提示. +- SQLN[UMBER] {OFF|ON(默认值)} 为SQL命令和PL/SQL块的第二行和后继行设置提示.为ON时,提示行号;为OFF时,提示设置为SQLPROMPT的值. +- SQLPER[FIX] {#(默认值)|C} 设置sqlplus前缀字符.在键入一SQL命令或PL/SQL块时,可在单独行上键入一sqlplus命令,由sqlplus的前缀字符做前缀. sqlplus直接执行该命令,不影响SQL命令或PL/SQL块.前缀字符必须是非字母数字字符. +- SQLP[ROMPT] {SQL>;(默认值)|文本} 设置sqlplus的命令提示符. + ``` + 11:37:45 SQL> show sqlprompt + sqlprompt "SQL> " + 11:38:21 SQL> set sqlprompt Jamm> + 11:39:54 Jamm>show sqlprompt + sqlprompt "Jamm>" + 11:40:03 Jamm> + ``` +- SQLT[ERMINATOR] {;(默认值)|C|OFF|ON(默认值)} 设置用于结束和执行SQL命令的字符. OFF意味着sqlplus不识别命令终止符,用键入空行来结束SQL命令. ON重设置终止符为默认的分号. +- SUF[FIX] {SQL(默认值)|文本} 设置缺省文件的后缀,sqlplus在命令中使用,来引用命令文件. SUFFIX不控制输出(spool)文件的扩展名. +- TAB {OFF|ON(默认值)} 决定sqlplus在终端输出中如何格式化空白空间. 为OFF时,在输出中使用空格格式化空白空间;为ON时,用TAB字符. TAB的缺省值依赖于系统,用SHOW TAB命令可查看该缺省值. +- TERM[OUT] {OFF|ON(默认值)} 控制由文件执行命令所产生的输出的显示. OFF禁止显示,以致从一个命令文件假脱机输出,在屏幕上看不到输出. ON时显示输出. TERMOUT OFF 不影响交互地进行命令的输出. +- TI[ME] {OFF(默认值)|ON} 控制当前日期的显示. ON时,在每条命令提示前显示当前时间; OFF时禁止时间的显示. + ``` + 11:37:54 SQL> set time off + SQL> set time on + 11:38:21 SQL> + ``` +- TIMI[NG] {OFF(默认值)|ON} 控制时间统计的显示. ON时,显示每一个运行的SQL命令或PL/SQL块的时间统计; OFF时,禁止每一个命令的时间统计. +- TRIM[OUT] {OFF|ON(默认值)} 决定sqlplus在每一显示行的末端是否允许带空格. ON时将每行尾部的空格去了,特别当从慢速的通信设备存取sqlplus时可改进性能; OFF时允许sqlplus显示尾部的空格.TRIMOUT ON 不影响假脱机输出. 设置TAB ON时,sqlplus忽略TRIMOUT ON. +- UND[ERLINE] {-(默认值)|C|OFF|ON(默认值)} {-(默认值)|C|OFF|ON(默认值)} 设置用在sqlplus报表中下划线列标题的字符. ON或OFF将下划线置成开或关. +- VER[IFY] {OFF|ON(默认值)} 控制sqlplus用值替换前、后是否列出命令的文本. ON时显示文本;OFF时禁止列清单. +- WRA[P] {OFF|ON(默认值)} 控制sqlplus是否截断数据项的显示. OFF时截断数据项;ON时允许数据项缠绕到下一行. 在COLUMN命令中使用WRAPPED和TRUNCATED子句可控制对指定列的WRAP的设置. + +# 参考 +- [https://blog.csdn.net/lfc453048573/article/details/12956191](https://blog.csdn.net/lfc453048573/article/details/12956191) + diff --git a/content/post/ovirt-install.md b/content/post/ovirt-install.md new file mode 100644 index 0000000..4b161a9 --- /dev/null +++ b/content/post/ovirt-install.md @@ -0,0 +1,145 @@ +--- +title: "Ovirt 安装" +date: 2019-10-30T11:16:47+08:00 +lastmod: 2019-10-30T11:16:47+08:00 +tags: ["ovirt", "kvm", "虚拟化"] +categories: ["kvm"] +--- + +# 版本 +Ovirt 4.1.7 + +# 操作系统初始配置 + 角色 | 主机名 | 配置 | IP | 版本 | /etc/hosts 追加行 +-------- | ------- | -------------------- | ----------- | ------------------ | ------------------- +管理节点 | engine | CPU: 多核
RAM: 2G | 10.0.16.160 | CentOS 7.4 Minimal | 10.0.16.160 engine engine.ovirt
10.0.16.161 host161 +计算节点 | host161 | CPU: 多核
RAM: 4G | 10.0.16.161 | CentOS 7.4 Minimal | 10.0.16.161 host161 + +- 建议保留操作系统自带的 NetworkManager 和 SELinux 服务 +- 建议禁用操作系统自带的 firewalld 服务 +- 时间同步,编辑 /etc/chrony.conf,修改 server 地址如下 + ``` + server s1c.time.edu.cn iburst + server s2m.time.edu.cn iburst + server s1b.time.edu.cn iburst + server s1e.time.edu.cn iburst + server s2a.time.edu.cn iburst + server s2b.time.edu.cn iburst + ``` + +- 重启时间同步服务 + ``` + systemctl restart chronyd + ``` + +- 存储服务暂时采用计算节点的本机 NFS + +# 导入 Ovirt yum 源 +- 创建 ovirt.repo,内容如下 + ``` + [ovirt-4.1] + name=Latest oVirt 4.1 Release + baseurl=http://mirror.isoc.org.il/pub/ovirt/ovirt-4.1/rpm/el7/ + enabled=1 + skip_if_unavailable=0 + gpgcheck=1 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-ovirt-4.1 + [ovirt-4.1-epel] + name=Extra Packages for Enterprise Linux 7 - $basearch + baseurl=https://mirrors.tuna.tsinghua.edu.cn/epel/7/x86_64/ + failovermethod=priority + enabled=1 + includepkgs=epel-release,python-uinput,puppet,python-lockfile,python-cpopen,python-ordereddict,python-pthreading,python-inotify,python-argparse,novnc,python-ply,python-kitchen,python-daemon,python-websockify,livecd-tools,spice-html5,mom,python-IPy,python-ioprocess,ioprocess,safelease,python-paramiko,python2-paramiko,python2-crypto,libtomcrypt,libtommath,python-cheetah,python-ecdsa,python2-ecdsa,python-markdown,rubygem-rgen,ovirt-guest-agent*,userspace-rcu,protobuf-java,objenesis,python34*,hystrix-core,archaius-core,rxjava,jctools,hystrix-metrics-event-stream,jackson-core,python-simplejson + gpgcheck=1 + gpgkey=https://mirrors.tuna.tsinghua.edu.cn/epel/RPM-GPG-KEY-EPEL-7 + [ovirt-centos7] + name=ovirt 4.1 CentOS 7 + baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/7/virt/x86_64/ovirt-4.1/ + enabled=1 + gpgcheck=0 + [centos-opstools-release] + name=CentOS-7 - OpsTools - release + baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/7/opstools/x86_64/ + enabled=1 + gpgcheck=0 + [patternfly-patternfly1] + name=Copr repo for patternfly1 owned by patternfly + baseurl=https://copr-be.cloud.fedoraproject.org/results/patternfly/patternfly1/epel-7-$basearch/ + type=rpm-md + skip_if_unavailable=True + gpgcheck=1 + gpgkey=https://copr-be.cloud.fedoraproject.org/results/patternfly/patternfly1/pubkey.gpg + repo_gpgcheck=0 + enabled=1 + enabled_metadata=1 + ``` + +- 分发 ovirt.repo 到全部服务器的 /etc/yum.repos.d/ 下 +- 下载[RPM-GPG-ovirt-4.1](http://mirror.isoc.org.il/pub/ovirt/keys/RPM-GPG-ovirt-v2),分发到全部服务器的 /etc/pki/rpm-gpg/ 下 +- 在全部服务器上,执行以下命令: + ```bash + yum clean all + yum makecache fast + yum repolist #此时能看到新增加的 ovirt.repo 文件中的 yum 源 + ``` + +# 配置管理节点(engine) +- 在 engine 上分发免密登陆各计算节点(host)的密钥 + ```bash + ssh-keygen #全部默认回车即可 + ssh-copy-id host161 + ``` +- 安装 ovirt-engine 软件包 + ```bash + yum install ovirt-engine + ``` +- 配置 engine + ```bash + engine-setup + #进入交互中,根据提示选择适合自己的选项 + ``` +- 配置完成后,engine 服务启动 +- 在个人 PC 上配置好 "10.0.16.160 engine engine.ovirt",浏览器打开 https://engine.ovirt + +# 配置计算节点(host161) +- 打开 ovirt 管理 web 界面: http://engine.ovirt,以 admin 身份登陆 +- 打开 "主机" 标签页,单击左上角的 "新建" +- 在打开 "新建主机" 窗口中,选择 "主机集群",填写 "名称"、"地址"、"ssh端口"、"用户名" 等信息 +- 选择 "ssh公共密钥",复制右侧文本框中的公钥到目标计算节点(host161)的 /root/.ssh/authorized_keys 中 +- 单击 "确定",host161 开始自动安装,通过 web 界面底部的详情窗格中可以看到安装过程 +- host 安装完成后会自动启动,状态变成 "up" + +# 配置存储 +- 服务器数量有限,这里直接在计算节点上启动 NFS +- 在 host161 上编辑 /etc/exports,增加如下配置 + ``` + /NFS/iso1 10.0.16.160(rw,no_all_squash,async) + /NFS/iso1 10.0.16.161(rw,no_all_squash,async) + /NFS/data1 10.0.16.161(rw,no_all_squash,async) + ``` +- 在 host161 上创建可读写的 nfs 目录,启动 NFS + ```bash + mkdir /NFS/{data1,iso1} + chmod -R 0777 /NFS + systemctl start nfs + systemctl enable nfs + ``` +- 打开 ovirt 管理 web 界面: http://engine.ovirt,以 admin 身份登陆 +- 打开 "存储" 标签页,单击左上角的 "新建域" +- 在打开的 "新建域" 窗口中,选择 "域功能"-"Data", "存储类型"-"NFS", "使用主机"-"host161", "名称"-"data1" +- 在 "导出路径" 文本框中输入: 10.0.16.161:/NFS/data1 +- 单击 "确定",DATA 存储域 "data1" 开始自动配置 +- 自动配置成功后,该 DATA 存储域状态自动切换为 "Active" +- 再次单击 "新建域",在 "新建域" 窗口中选择 "域功能"-"ISO", "名称"-"iso1", 其他项与 DATA 存储域配置一致 +- 在 "导出路径" 文本框中输入: 10.0.16.161:/NFS/iso1 +- 单击 "确定",ISO 存储域 "iso1" 开始自动配置,完成后,该存储域状态自动切换为 "Active" + +# 导入操作系统 iso +- 前面的存储创建好后,ovirt 就可以用了,接下来是导入 iso 镜像 +- 从 engine 导入 iso 文件 + ```bash + engine-iso-uploader --iso-domain=iso1 upload {filename.iso} + #密码是 web 管理界面 admin 用户的密码 + ``` +- iso 文件导入成功后,就可以用来创建虚拟机了 + diff --git a/content/post/ovirt.md b/content/post/ovirt.md new file mode 100644 index 0000000..35e6183 --- /dev/null +++ b/content/post/ovirt.md @@ -0,0 +1,101 @@ +--- +title: "Ovirt 笔记" +date: 2019-10-30T13:04:51+08:00 +lastmod: 2019-10-30T13:04:51+08:00 +tags: ["ovirt"] +categories: ["kvm"] +--- + +# 封装 CentOS6/7 虚拟机 +- 标识 “重新配置” + ```bash + touch /.unconfigured + ``` +- 删除 ssh 主机密钥 + ```bash + rm -rf /etc/ssh/ssh_host_* + ``` +- 恢复原始主机名 ”localhost“ + ```bash + #CentOS6 + sed -i '/^HOSTNAME/cHOSTNAME=localhost.localdomain' /etc/sysconfig/network + #CentOS7 + sed -i '/^HOSTNAME/cHOSTNAME=localhost.localdomain' /etc/hostname + ``` +- 清理 udev + ```bash + rm -rf /etc/udev/rules.d/70-* + ``` +- 删除网卡硬件标识 + ```bash + sed -i -e '/HWADDR/d' -e '/UUID/d' /etc/sysconfig/network-scripts/ifcfg-{eth,enp}* + ``` +- 清空日志 + ```bash + rm -rf /var/log/* + ``` +- 关闭虚拟机 + ```bash + poweroff + ``` + +# 解锁 admin 用户 +- 查看状态 + ```bash + ovirt-aaa-jdbc-tool user show admin + ``` +- 解锁 + ```bash + ovirt-aaa-jdbc-tool user unlock admin + ``` + +# 创建本地 yum 源 +- 安装工具 + ```bash + yum install yum-utils createrepo + ``` +- 解压 rpm 包(无用,仅作记录) + ```bash + rpm2cpio {filename}.rpm | cpio -ivd + ``` +- 下载 ovirt.repo,保存到本地服务器的 /etc/yum.repos.d/ 下 +- 下载 RPM-GPG-ovirt-4.1,保存到本地服务器的 /etc/pki/rpm-gpg/ 下 +- 在该服务器上,执行以下命令: + ```bash + yum clean all + yum makecache fast + yum repolist #此时能看到新增加的 ovirt.repo 文件中的 yum 源 + ``` +- 同步 yum repo 到本地 + ```bash + mkdir /DATA/yum && cd /DATA/yum + reposync -nld \ + --repoid=ovirt-4.1 \ + --repoid=ovirt-4.1-epel \ + --repoid=ovirt-centos7 \ + --repoid=centos-opstools-release \ + --repoid=patternfly-patternfly1 + ``` +- 创建本地 yum repo + ```bash + createrepo --update ovirt-4.1 + createrepo --update ovirt-4.1-epel + createrepo --update ovirt-centos7 + createrepo --update centos-opstools-release + createrepo --update patternfly-patternfly1 + ``` +- nginx.conf 配置 + ```nginx + server { + listen 1080 default_server; + listen [::]:1080 default_server; + server_name _; + root /DATA/yum; + location / { + autoindex on; + autoindex_exact_size off; + autoindex_localtime on; + } + } + ``` + diff --git a/content/post/pip.md b/content/post/pip.md new file mode 100644 index 0000000..0b6fd45 --- /dev/null +++ b/content/post/pip.md @@ -0,0 +1,22 @@ +--- +title: "python pip" +date: 2019-10-29T21:17:22+08:00 +lastmod: 2019-10-29T21:17:22+08:00 +keywords: [] +tags: ["pip", "python"] +categories: ["python"] +--- + +# 配置 pip 源 +- 创建 $HOME/.pip/pip.conf 文件,内容如下 + ```ini + [global] + index-url=https://pypi.tuna.tsinghua.edu.cn/simple + + [install] + trusted-host=pypi.tuna.tsinghua.edu.cn + + [list] + format=columns + ``` + diff --git a/content/post/postgresql-install.md b/content/post/postgresql-install.md new file mode 100644 index 0000000..e4cf5e6 --- /dev/null +++ b/content/post/postgresql-install.md @@ -0,0 +1,101 @@ +--- +title: "CentOS7 安装 Postgresql" +date: 2019-10-30T00:48:54+08:00 +lastmod: 2019-11-04T02:03:00+08:00 +keywords: [] +tags: ["postgresql"] +categories: ["database"] +--- + +# 环境 +- CentOS 7 +- PostgreSQL 10.10 + +# 安装 +- 安装 yum 源 + ```bash + yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm + # 或者从清华镜像站下载 + yum install https://mirrors.tuna.tsinghua.edu.cn/postgresql/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm + # 可以更换成清华镜像站地址 + sed -i 's,download.postgresql.org/pub,mirrors.tuna.tsinghua.edu.cn/postgresql,' /etc/yum.repos.d/pgdg-redhat-all.repo + # 更新 yum 缓存 + yum clean all + yum makecache fast + ``` +- 安装 postgresql-server + ```bash + yum install postgresql10-server + ``` +- 初始化数据库 + ```bash + /usr/pgsql-10/bin/postgresql-10-setup initdb + ``` +- 启动数据库 + ```bash + systemctl start postgresql-10 + ``` + +# 配置 postgresql.conf +- 监听本机全部地址 + ``` + clisten_addresses = '*' + ``` + +# 配置 pg_hba.conf +- 配置同网段客户端可通过 pguser1 使用密码登陆 pgdb1 + ```bash + echo 'host pgdb1 pguser1 samenet md5' >> pg_hba.conf + ``` +- 配置任何客户端可通过任何用户使用密码登陆任何数据库 + ```bash + echo 'host all all all md5' >> pg_hba.conf + ``` + +# 简单使用 +- 登陆数据库 + ```bash + su - postgres + psql -U postgres + ``` +- 简单操作 + ```sql + -- 修改 postgres 用户的默认密码 + alter user postgres with password '123456'; + -- 创建新用户 pguser1 + create user pguser1 with password '123456'; + -- 创建数据库 pgdb1 + create database pgdb1 owner pguser1 encoding utf8; + -- 授权 + grant all on database pgdb1 to pguser1; + -- 查看系统操作帮助 + \? + -- 查看 sql 语句操作帮助 + \h + -- 查看建库语句操作 + \h create database + -- 退出数据库 + \q + ``` + +# 迁移数据库目录 +- 停止数据库 postgresql + ```bash + systemctl stop postgresql-10 + ``` +- 移动数据库目录 + ```bash + mkdir -p /data/pgsql/10 + mv /var/lib/pgsql/10/data /data/pgsql/10/ + chown -R postgres.postgres /data/pgsql + ``` +- 修改 service 文件 + ```bash + sed -i 's,/var/lib/pgsql/10,/data/pgsql/10,' /usr/lib/systemd/system/postgresql-10.service + systemctl daemon-reload + ``` +- 启动数据库 + ```bash + systemctl start postgresql-10 + ``` + diff --git a/content/post/postgresql-replication.md b/content/post/postgresql-replication.md new file mode 100644 index 0000000..b1614f0 --- /dev/null +++ b/content/post/postgresql-replication.md @@ -0,0 +1,69 @@ +--- +title: "Postgresql 主从" +date: 2019-11-04T02:09:29+08:00 +lastmod: 2019-11-04T02:09:29+08:00 +tags: ["postgresql", "replcation", "同步"] +categories: ["database"] +--- + +# 主库配置 +- 修改 postgresql.conf + ``` + listen_address = '*' + wal_level = replica + wax_wal_senders = 10 + wal_keep_segments = 64 + hot_standby = on + ``` +- 启动主库 postgresql-10 服务 + ```bash + systemctl start postgresql-10 + ``` +- 创建同步账户 + ```sql + create user replica superuser password '123456'; + -- 这里可以只赋予 replication 权限,后面从库复制初始数据库时使用其他有权限帐号 + ``` +- 修改 pg_hba.conf + ``` + host replication replica samenet md5 + ``` + +# 从库配置 +- 停止从库 postgresql-10 服务,清空数据库目录 + ```bash + systemctl stop postgresql-10 + rm -rf /var/lib/pgsql/10/data/* + ``` +- 从主库复制初始数据库 + ```bash + pg_basebackup \ + -h <主库ip> \ + -U replica \ + -F p \ + -X stream \ + -P -R \ + -D /var/lib/pgsql/10/data/ \ + -l backup_20191104 + ``` + - -h 指定连接的数据库的主机名或IP地址 + - -U 指定连接的用户名 + - -F 指定了输出的格式,支持p(原样输出)或者t(tar格式输出) + - -X 表示备份开始后,启动另一个流复制连接从主库接收WAL日志 + - -P 表示允许在备份过程中实时的打印备份的进度 + - -R 表示会在备份结束后自动生成recovery.conf文件 + - -D 指定备份写入的数据目录,需要与数据库配置的数据库目录一致,初次备份之前从库的数据目录需要手动清空 + - -l 表示指定一个备份的标识 + +# 检查状态 +- 检查从库进程 + ```bash + ps -ef|grep postgres + # 可以看到 wal sender 和 receiver process 两个进程 + ``` +- 从库为只读模式,无法进行 增/删/改 操作 +- 主库查看 replication 客户端 + ```sql + select client_addr,sync_state from pg_stat_replication; + ``` + diff --git a/content/post/pxe-dnsmasq.md b/content/post/pxe-dnsmasq.md new file mode 100644 index 0000000..cf5ccf8 --- /dev/null +++ b/content/post/pxe-dnsmasq.md @@ -0,0 +1,98 @@ +--- +title: "Dnsmasq 实现网络 PXE 装机" +date: 2019-11-08T15:52:55+08:00 +lastmod: 2019-11-08T15:52:55+08:00 +tags: ["dnsmasq", "pxe"] +categories: ["OS"] +--- + +# 把 dnsmasq 配置成 pxe 服务器 +- 安装 dnsmasq + ```bash + # rhel + yum install dnsmasq + # archlinux + pacman -S dnsmasq + ``` +- 修改配置 /etc/dnsmasq.conf + ```ini + port=0 # 用不着 dns 功能,可以关闭 + #interface=ens8u2u4u1 # 指定网卡 + dhcp-range=10.0.86.1,10.0.86.9,255.255.255.0,1h + #dhcp-boot=pxelinux.0 # bios 引导 + dhcp-boot=grubx64.efi # efi 引导 + enable-tftp + tftp-root=/var/ftpd + ``` +- 启动 dnsmasq + ```bash + systemctl start dnsmasq + ``` + +# 安装 CentOS7/8 +- **本次测试使用 CentOS7.7 和 CentOS8.0 镜像** + +## 挂载系统镜像,提供软件源服务 +- 下载 centos7/8 镜像到 dnsmasq 服务器 +- 挂载镜像到 /mnt 目录 + ```bash + mount -o loop xxxx.iso /mnt + ``` +- 直接在系统镜像的挂载目录(/mnt)启动 http 服务 + ```bash + cd /mnt + python2 -m SimpleHTTPServer 10086 + # 或者使用 python3 + python3 -m http.server 10086 + ``` + +## 网络 BIOS 引导 +- 复制 centos7/8 镜像里的启动文件到 dnsmasq 服务器的 /var/ftpd/ 下 + ```bash + cd /var/ftpd + cp /mnt/isolinux/* . + mkidr pxelinux.cfg + mv isolinux.cfg pxelinux.cfg/default + ``` +- 打开 /var/ftpd/pxelinux.cfg/default,修改第一个启动项 + ``` + label linux + menu label ^Install CentOS 7/8 + kernel vmlinuz + append initrd=initrd.img inst.repo=http://10.0.86.1:10086/ quiet + # ks 参数: inst.ks= + ``` +- 在 centos7/8 上安装 syslinux + ```bash + yum install syslinux # centos7 + dnf install syslinux # centos 8 + ``` +- 把 /user/share/syslinux/pxelinux.0 复制到 dnsmasq 服务器的 /var/ftpd/ 下 +- 修改文件权限,确保 dnsmasq 用户可读 + ```bash + chown -R dnsmasq.dnsmasq /var/ftpd/ + ``` + +## 网络 EFI 引导 +- **不支持 secure boot** +- 复制 centos7/8 镜像里的启动文件到 dnsmasq 服务器的 /var/ftpd/ 下 + ```bash + cp -a /mnt/EFI/BOOT/* /var/ftpd/ + ``` +- 打开 /var/ftpd/grub.cfg,修改第一个启动项 + ``` + menuentry 'Install CentOS 7/8' --class fedora --class gnu-linux --class gnu --class os { + linuxefi vmlinuz inst.repo=http://10.0.86.1:10086/ quiet + initrdefi initrd.img + } + # ks 参数: inst.ks= + ``` +- 修改文件权限,确保 dnsmasq 用户可读 + ```bash + chown -R dnsmasq.dnsmasq /var/ftpd/ + ``` + +# 装机 +- 把待安装机器和 dnsmasq 服务器接入同一个交换机(无其他 dhcp 广播) +- 启动待安装机器,选择 pxe 引导,从第一个启动项启动 + diff --git a/content/post/python-cf.md b/content/post/python-cf.md new file mode 100644 index 0000000..7fcd979 --- /dev/null +++ b/content/post/python-cf.md @@ -0,0 +1,169 @@ +--- +title: "Python 的 ConfigParser 模块" +date: 2019-10-30T18:04:09+08:00 +lastmod: 2019-10-30T18:04:09+08:00 +tags: ["python", "configparser"] +categories: ["python"] +--- + +# 样例文件 +- 该类配置文件可以包含一个或多个节(section),每个节可以有多个参数(键=值)。 +- 样例配置文件(/proj/conf/example_conf) + ``` + [book] + title:ConfigParser模块教程 + author:大头爸爸 + email:366500050@qq.com + time:2012-09-20 22:04:55 + [size] + size:1024 + [other] + blog:csdn.net + ``` +- 上面配置文件中用的是冒号,也可以用等号。 + +# 读取配置文件 +- 示例文件: example.py + ```python + # -*- coding: utf-8 -*- + import ConfigParser + import string + config=ConfigParser.ConfigParser() + config.read(u'/proj/conf/example_conf') + print string.upper(config.get("book","title")), + print "by",config.get("book","author"), + print "("+config.get("book","email")+")" + print + print config.get("size","size") + print + print config.sections() + for section in config.sections(): + print section + for option in config.options(section): + print " ",option,"=",config.get(section,option) + ``` +- example.py文件执行结果 + ``` + CONFIGPARSER模块教程 by 大头爸爸 (366500050@qq.com) + 1024 + ['book', 'size', 'other'] + book + title = ConfigParser模块教程 + author = 大头爸爸 + email = 366500050@qq.com + time = 2012-09-20 22:04:55 size + size = 1024 other + blog = csdn.n + ``` + +# 写入配置文件 +- 示例 + ```python + import ConfigParser + import sys + config=ConfigParser.ConfigParser() + config.add_section("book") + config.set("book","title","这是标题") + config.set("book","author","大头爸爸") + config.add_section("size") + config.set("size","size",1024) + config.write(sys.stdout) + ``` +- 执行结果 + ``` + [book] + title = 这是标题 + author = 大头爸爸 + [size] + size = 1024 + ``` + +# ConfigParser方法 +- 创建ConfigParser实例 + ```python + config=ConfigParser.ConfigParser() + ``` +- 返回配置文件中节序列 + ```python + config.sections() + ``` +- 返回某个项目中的所有键的序列 + ```python + config=ConfigParser.ConfigParser() + config.options(section) + ``` +- 返回section节中,option的键值 + ```python + config=ConfigParser.ConfigParser() + config.get(section,option) + ``` +- 添加一个配置文件节点(str) + ```python + config=ConfigParser.ConfigParser() + config.add_section(str) + ``` +- 设置section节点中,键名为option的值(val) + ```python + config=ConfigParser.ConfigParser() + config.set(section,option,val) + ``` +- 读取配置文件 + ```python + config=ConfigParser.ConfigParser() + config.read(filename) + ``` +- 写入配置文件 + ```python + config=ConfigParser.ConfigParser() + config.write(obj_file) + ``` + +# 综合实例 +```python +#coding=utf-8 + +import ConfigParser + +def writeConfig(filename): + config = ConfigParser.ConfigParser() + # set db + section_name = 'db' + config.add_section( section_name ) + config.set( section_name, 'dbname', 'MySQL') + config.set( section_name, 'host', '127.0.0.1') + config.set( section_name, 'port', '80') + config.set( section_name, 'password', '123456') + config.set( section_name, 'databasename', 'test') + + # set app + section_name = 'app' + config.add_section( section_name ) + config.set( section_name, 'loggerapp', '192.168.20.2') + config.set( section_name, 'reportapp', '192.168.20.3') + + # write to file + config.write( open(filename, 'a') ) + +def updateConfig(filename, section, **keyv): + config = ConfigParser.ConfigParser() + config.read(filename) + print config.sections() + for section in config.sections(): + print "[",section,"]" + items = config.items(section) + for item in items: + print "\t",item[0]," = ",item[1] + print config.has_option("dbname", "MySQL") + print config.set("db", "dbname", "11") + print "..............." + for key in keyv: + print "\t",key," = ", keyv[key] + config.write( open(filename, 'r+') ) + +if __name__ == '__main__': + file_name = 'test.ini' + writeConfig(file_name) + updateConfig(file_name, 'app', reportapp = '192.168.100.100') + print "end__" +``` + diff --git a/content/post/python-cx_Oracle.md b/content/post/python-cx_Oracle.md new file mode 100644 index 0000000..53ce336 --- /dev/null +++ b/content/post/python-cx_Oracle.md @@ -0,0 +1,65 @@ +--- +title: "Python 的 cx_Oracle 模块" +date: 2019-10-30T17:55:41+08:00 +lastmod: 2019-10-30T17:55:41+08:00 +tags: ["python", "cx_oracle"] +categories: ["python"] +--- + +# 导入 cx_Oracle 模块 +``` +import cx_Oracle # 导入模块 +``` + +# 连接数据库 +``` +db = cx_Oracle.connect('user', 'password', 'host:port/SID') #建立连接,3个参数分开写 +print db.version +#输出 10.2.0.1.0 测试成功 +``` + +# 自动提交 +``` +db.autocommit=True #开启自动提交 +db.autocommit=False #关闭自动提交 +``` + +# 建立 cursor 光标 +``` +cursor = db.cursor() #建立一个cursor +``` + +# 执行sql +``` +cursor.execute(‘select * from tabs’) # 执行一条sql +sql = "insert into person(name, age, telephone) values(%s, %s, %s)" +tmp = (('ninini', 89, '888999'), ('koko', 900, '999999')) +conn.executemany(sql, tmp) #执行多条sql +``` + +# 获取执行结果 +``` +row=cursor.fetchone() #取一行结果,元组(a,b,c,d) +row=cursor.fetchall() #获取所有结果,列表[(a,b,c,d),(e,f,g,h),...] +for x in row: + For y in x: + Print y +print cursor.rowcount() #获取输出记录数量 +``` + +# 提交 +``` +db.commit() +``` + +# 回滚 +``` +db.rollback() +``` + +# 关闭连接 +``` +cursor.close() +db.close() +``` + diff --git a/content/post/python-logging.md b/content/post/python-logging.md new file mode 100644 index 0000000..70ca9ae --- /dev/null +++ b/content/post/python-logging.md @@ -0,0 +1,77 @@ +--- +title: "Python 的 logging 模块" +date: 2019-10-30T18:03:55+08:00 +lastmod: 2019-10-30T18:03:55+08:00 +tags: ["python", "logging"] +categories: ["python"] +--- + +# 单输出日志到屏幕 +- 示例 + ```python + import logging + logging.debug('debug message') + logging.info('info message') + logging.warning('warning message') + logging.error('error message') + logging.critical('critical message') + ``` +- 输出 + ```python + WARNING:root:warning message + ERROR:root:error message + CRITICAL:root:critical message + ``` +- 默认情况下python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志。 +- 默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)。 +- 默认的日志格式为日志级别:Logger名称:用户输出消息。 + +# 配置日志 +- 示例 + ```python + import logging + logging.basicConfig(level=logging.DEBUG, + format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', + datefmt='%a, %d %b %Y %H:%M:%S', + filename='/tmp/test.log', + filemode='w') + logging.debug('debug message') + logging.info('info message') + logging.warning('warning message') + logging.error('error message') + logging.critical('critical message') + ``` +- 输出 + ```python + cat /tmp/test.log + Mon, 05 May 2014 16:29:53 test_logging.py[line:9] DEBUG debug message + Mon, 05 May 2014 16:29:53 test_logging.py[line:10] INFO info message + Mon, 05 May 2014 16:29:53 test_logging.py[line:11] WARNING warning message + Mon, 05 May 2014 16:29:53 test_logging.py[line:12] ERROR error message + Mon, 05 May 2014 16:29:53 test_logging.py[line:13] CRITICAL critical message + ``` +- logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,参数解释: +- filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。 +- filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 +- format:指定handler使用的日志显示格式。 +- datefmt:指定日期时间格式。 +- level:设置rootlogger(后边会讲解具体概念)的日志级别 +- stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。 +- format参数中可能用到的格式化串: + - %(name)s Logger的名字 + - %(levelno)s 数字形式的日志级别 + - %(levelname)s 文本形式的日志级别 + - %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有 + - %(filename)s 调用日志输出函数的模块的文件名 + - %(module)s 调用日志输出函数的模块名 + - %(funcName)s 调用日志输出函数的函数名 + - %(lineno)d 调用日志输出函数的语句所在的代码行 + - %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示 + - %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数 + - %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 + - %(thread)d 线程ID。可能没有 + - %(threadName)s 线程名。可能没有 + - %(process)d 进程ID。可能没有 + - %(message)s用户输出的消息 + + diff --git a/content/post/python-op.md b/content/post/python-op.md new file mode 100644 index 0000000..3a7f487 --- /dev/null +++ b/content/post/python-op.md @@ -0,0 +1,104 @@ +--- +title: "Python 的 optionparser 模块" +date: 2019-10-30T17:46:52+08:00 +lastmod: 2019-10-30T17:46:52+08:00 +tags: ["python", "optionparser"] +categories: ["python"] +--- + +# 生成标准的、符合Unix/Posix 规范的命令行说明 +```python +from optparse import OptionParser +parser = OptionParser() +parser.add_option('-p', '--pdbk', action = 'store_true', dest = 'pdcl', default = False, + help = 'write pdbk data to oracle db') +parser.add_option('-z', '--zdbk', action = 'store_true', dest = 'zdcl', default = False, + help = 'write zdbk data to oracle db') +(options, args) = parser.parse_args() +if options.pdcl == True: + print 'pdcl is true.' +if options.zdcl == True: + print 'zdcl is True.' +``` + +# 简单流程 +```python +#引入OptionParser类,创建OptionParser对象 +from optparse import OptionParser +parser = OptionParser() +# 定义命令行参数 +parser.add_option(opt_str, ..., attr = value, ...) +# 解析命令行参数 +(options, args) = parser.parse_args() +``` + +# parse_args 和 add_options 函数 +- parse_args() 接收一个命令行列表,默认使用 sys.argv\[:-1\] +- 返回两个值,options 保存命令行参数值,args 是由 positional arguments 组成的列表 +- add_option() 用来加入选项,parse_args() 解析选项 + ```python + from optparse import OptionParser + parser = OptionParser() + parser.add_option('-f', '--file', dest = 'filename', metavar = 'FILE', + help = 'write report to FILE') + parser.add_option('-q', '--quit', action = 'store_false', dest = 'verbose', default = True, + help = 'Don\'t print status message to stdout.') + (options, args) = parser.parse_args() + ``` +- action 默认 store 表示将参数值保存到 options 对象里 + ```python + from optparse import OptionParser + parser = OptionParser() + parser.add_option('-f', '--file', action = 'store', type = 'string', dest = 'filename') + args = ['-f', 'foo.txt'] + (options, args) = parser.parse_args(args) + print options.filename + ``` +- type 默认 'string',也可以是 'int' 或 'float' 等,长参数名可选,dest 未指定时将用命令行的参数名来存取 options 对象的值 +- store 其他两种形式:store_true 和 store_false,还有 store_const、append、count、callback + ```python + parser.add_option('-v', action = 'store_true', dest = 'verbose') + parser.add_option('-q', action = 'store_false', dest = 'verbose') + #当解析到 '-v', options.verbose 为 True,解析到 '-q',options.verbose 为 False + ``` +- default 设置参数默认值 + ``` + parser.add_option('-f', action = 'store', dest = 'filename', default = 'foo.txt') + parser.add_option('-v', action = 'store_true', dest = 'verbose', default = True) + #也可以使用 set_default() + parser.set_defaults(filename = 'foo.txt', verbose = True) + parser.add_option(...) + (options, args) = parser.parse_args() + ``` +- help 生成帮助信息 + ```python + usage = 'usage: %prog [options] arg1 arg2' + parser = OptionParser(usage = usage) + parser.add_option('-v', '--verbose', action = 'store_true', dest = 'verbose', default = True, + help = 'make lots of noise [default]') + parser.add_option('-q', '--quiet', action = 'store_false', dest = 'verbose', + help = 'be very quiet') + parser.add_option('-f', '--filename', metavar = 'FILE', + help = 'write output to FILE') + parser.add_option('-m', '--mode', metavar='MODE', default = 'intermediate', + help = 'interaction mode: novice, intermediate, or expert [default: %default]') + ``` + +# 备注 +- optparse 解析到 help 后不再解析其他命令行参数,usage 信息会优先打印,默认 "usage: %prog \[options\]" +- metavar 提醒用户该参数期待的参数,如 metavar = 'mode' 会在帮助中显示成 -m MODE, --mode=MODE + +# OptionGroup 参数分组 +```python +group = OptionGroup(parser, 'Dangerous Options', + 'Caution: use these options at your own risk. It is believed that some of them bite.') +group.add_option('-g', action = 'store_true', help = 'Group option.') +parser.add_option_group(group) +``` +- version 创建OptionParser对象时指定该参数,会解释成 --version 命令行参数 +- optparser 可以自动探测并处理一些用户异常,也可以使用 parser.error() 方法来自定义部分异常的处理 + ``` + if options.a and options.b: + parser.error('options -a and -b are mutually exclusive') + ``` + diff --git a/content/post/python-os.md b/content/post/python-os.md new file mode 100644 index 0000000..436d8b7 --- /dev/null +++ b/content/post/python-os.md @@ -0,0 +1,26 @@ +--- +title: "Python 的 os 和 os.path" +date: 2019-10-30T17:45:33+08:00 +lastmod: 2019-10-30T17:45:33+08:00 +tags: ["python", "os", "os.path"] +categories: ["python"] +--- + +- os.getcwd() #返回运行脚本的目录 +- os.listdir() #获取目录中的内容 +- os.path.basename() #去掉目录路径,返回文件名 +- os.path.dirname() #去掉文件名,返回目录路径 +- os.path.join() #组合成合法路径 +- os.path.split() #返回路径和文件名元组 +- os.path.splitdrive() #返回驱动器符号和路径元组 +- os.path.splitext() #返回文件名和扩展名元组 +- os.path.getatime() #返回文件最近的访问时间戳 +- os.path.getctime() #返回文件的创建时间戳 +- os.path.getmtime() #返回文件修改时间 +- os.path.getsize() #返回文件大小(Bytes) +- os.path.exists() #指定目录或文件是否存在 +- os.path.isabs() #指定路径是否为绝对路径 +- os.path.isdir() #指定路径是否为目录 +- os.path.isfile() #指定路径是否为文件 +- os.path.samefile() #指定路径名是否指向同一个文件 + diff --git a/content/post/python-re.md b/content/post/python-re.md new file mode 100644 index 0000000..a923b27 --- /dev/null +++ b/content/post/python-re.md @@ -0,0 +1,65 @@ +--- +title: "Python 的 re 模块" +date: 2019-10-30T17:52:21+08:00 +lastmod: 2019-10-30T17:52:21+08:00 +tags: ["python", "re"] +categories: ["python"] +--- + +# match +- 定义 + ```python + re.match(pattern, string, flags) + ``` +- 尝试从字符串的开始匹配一个模式 +- 第一个参数是正则表达式,如果匹配成功,则返回一个Match,否则返回一个None; +- 第二个参数表示要匹配的字符串; +- 第三个参数是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 + +# search +- 定义 + ```python + re.search(pattern, string, flags) + ``` +- 在字符串内查找模式匹配,只到找到第一个匹配然后返回,如果字符串没有匹配,则返回None。 +- 每个参数的含意与re.match一样。 + +# sub +- 定义 + ```python + re.sub(pattern, repl, string, count) + ``` +- 替换字符串中的匹配项 +- 其中第二个函数是替换后的字符串 +- 第四个参数指替换个数。默认为0,表示每个匹配项都替换。 +re.sub还允许使用函数对匹配项的替换进行复杂的处理,如:re.sub(r'\s', lambda m: '[' + m.group(0) + ']', text, 0);将字符串中的空格' '替换为'[ ]'。 + +# split +- 定义 + ```python + re.split(pattern, string) + ``` +- 分割字符串 + +# findall +- 定义 + ```python + re.findall(pattern, text) + ``` +- 获取字符串中所有匹配的字符串 + +# compile +- 定义 + ```python + re.compile(pattern) + ``` +- 把正则表达式编译成一个正则表达式对象 +- 示例 + ```python + import re + text = "JGood is a handsome boy, he is cool, clever, and so on..." + regex = re.compile(r'\w*oo\w*') + print regex.findall(text) #查找所有包含'oo'的单词 + print regex.sub(lambda m: '[' + m.group(0) + ']', text) #将字符串中含有'oo'的单词用[]括起来 + ``` + diff --git a/content/post/python-shell.md b/content/post/python-shell.md new file mode 100644 index 0000000..ba75ac0 --- /dev/null +++ b/content/post/python-shell.md @@ -0,0 +1,70 @@ +--- +title: "Python 调用 Shell 命令" +date: 2019-10-30T17:44:22+08:00 +lastmod: 2019-11-03T17:12:00+08:00 +tags: ["python", "shell"] +categories: ["python"] +--- + +# os +- 阻塞,返回shell执行参数命令的状态,即成功返回0 + ```python + os.system('cat /proc/cpuinfo') + ``` +- 阻塞,返回file read的对象,对该对象进行 read() 可以获取shell执行参数命令的结果,即标准输出 + ```python + os.popen('cat /proc/cpuinfo') + ``` + +# commands +- 阻塞,返回参数指定的系统文件的详细属性 + ```python + commands.getstatus('/proc/cputinfo') + ``` +- 阻塞,返回shell执行参数命令的结果 + ```python + commands.getoutput('cat /proc/cpuinfo') + ``` +- 阻塞,返回shell状态和shell输出的元组(status, output) + ```python + commands.getstatusoutput('cat /proc/cpuinfo') + ``` + +# subprocess +- 阻塞,返回shell状态,禁用 PIPE 参数 + ```python + subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False) + ``` +- 阻塞,shell 执行成功返回0, 否则无返回,并抛出包含shell错误状态的 CalledProcessError 异常,禁用PIPE参数 + ```python + subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False) + ``` +- 阻塞,shell 执行成功返回shell结果,否则无返回,并抛出包含shell错误状态的 CalledProcessError 异常,禁用PIPE参数 + ```python + subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False) + ``` + +# Pope +- 不阻塞,返回Popen对象 +```python +subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0) +``` +- subprocess 参数: + - args: 字符串或列表(*nix 下第一项视为命令,后面是命令参数) + - bufsize: 默认 0 不缓冲,1 行缓冲,其他正数表示缓冲大小,负数表示使用系统默认全缓冲 + - stdin stdout stderr: subprocess.PIPE 表示管道操作,subprocess.STDOUT 表示输出到标准输出 + - preexec_fn: *nix 下子进程被执行前调用 + - shell: True 时表示指定命令在shell里解释执行 + - subprocess.PIPE: 用于stdin、stdout 和 stderr ,表示创建并写入一个管道 + - subprocess.STDOUT: 用于 stderr,表示标准错误重定向到标准输出 +- Popen 对象属性: + - Popen.poll(): 检查子进程是否结束,0 表示退出 + - Popen.wait(): 等待子进程结束,注意子进程是否写管道 + - Popen.communicate(input=None): 与子进程交互,字符串数据发送到stdin,并从stdout和stderr读数据,知道EOF,等待子进程结束。注意读写stdin、stdout或stderr时要给定PIPE参数。返回元组(stdoutdata, stderrdata)。 + - Popen.send_signal(signal): 给子进程发送信号 + - Popen.terminate(): 停止子进程 + - Popen.kill(): 杀死子进程 + - Popen.stdin Popen.stdout Popen.stderr: PIPE参数时为文件对象,否则None + - Popen.pid: 子进程的进程号 + - Popen.returncode None表示子进程没终止,负数-N表示子进程被N号信号终止 + diff --git a/content/post/python-time.md b/content/post/python-time.md new file mode 100644 index 0000000..83af4ba --- /dev/null +++ b/content/post/python-time.md @@ -0,0 +1,46 @@ +--- +title: "Python 的 time 模块" +date: 2019-10-30T17:49:27+08:00 +lastmod: 2019-10-30T17:49:27+08:00 +tags: ["python", "time"] +categories: ["python"] +--- + +- 时间戳: 相对1970.1.1 00:00:00计算的秒数 +- struct_time: year(2015) month(1-12) day(1-31) hours(0-23) minutes(0-59) seconds(0-59) weekday(0-6,Monday is 0) julian day(day in the year,1-366) DST flag(-1,0,1) + +- time.asctime([tuple]) #把struct_time 转成字符串 +- time.clock() #第一次调用返回程序运行的实际时间,后面调用返回与第一次调用的时间间隔 +- time.sleep() #推迟指定秒的时间 +- time.ctime() #把时间戳(默认当前时间)转成字符串 +- time.gmtime() #把时间戳(默认当前时间)转成UTC时区的struct_time +- time.localtime() #把时间戳(默认当前时间)转成当前时区的struct_time +- time.mktime() #把struct_time转成时间戳 +- time.strftime(format[,tuple]) #把struct_time(默认当前时间)根据制定字符串输出 +- strptime(string, format) #把时间字符串根据指定的格式转成struct_time +- time.time() #返回当前时间的时间戳 + +- python中时间日期格式化符号: + - %y 两位数的年份表示(00-99) + - %Y 四位数的年份表示(000-9999) + - %m 月份(01-12) + - %d 月内中的一天(0-31) + - %H 24小时制小时数(0-23) + - %I 12小时制小时数(01-12) + - %M 分钟数(00 = 59) + - %S 秒(00-59) + - %a 本地简化星期名称 + - %A 本地完整星期名称 + - %b 本地简化的月份名称 + - %B 本地完整的月份名称 + - %c 本地相应的日期表示和时间表示 + - %j 年内的一天(001-366) + - %p 本地A.M.或P.M.的等价符 + - %U 一年中的星期数(00-53)星期天为星期的开始 + - %w 星期(0-6),星期天为星期的开始 + - %W 一年中的星期数(00-53)星期一为星期的开始 + - %x 本地相应的日期表示 + - %X 本地相应的时间表示 + - %Z 当前时区的名称 + - %% %号本身 + diff --git a/content/post/redis-cluster-install.md b/content/post/redis-cluster-install.md new file mode 100644 index 0000000..5c4cecb --- /dev/null +++ b/content/post/redis-cluster-install.md @@ -0,0 +1,358 @@ +--- +title: "CentOS7 安装 Redis 集群" +date: 2019-11-29T15:14:29+08:00 +lastmod: 2019-11-29T15:14:29+08:00 +tags: ["redis"] +categories: ["database"] +--- + +# 环境 +- 操作系统: CentOS7.7,关闭 firewalld 和 selinux +- Redis: 5.0.6 +- master 实例 + - 10.0.4.110:6371 + - 10.0.4.110:6372 + - 10.0.4.110:6373 +- slave 实例 + - 10.0.4.111:6371 + - 10.0.4.111:6372 + - 10.0.4.111:6373 + +# 编译 redis server +- 在 CentOS7.7 测试服务器上下载、编译 + ```bash + wget http://download.redis.io/releases/redis-5.0.6.tar.gz + tar xzf redis-5.0.6.tar.gz + cd redis-5.0.6 + make + ``` +- 抽取安装文件 + ```bash + mkdir -p /opt/redis_cluster/{bin,conf} + cp src/{redis-server,redis-cli,redis-benchmark} /opt/redis_cluster/bin/ + # redis-check-rdb 和 redis-check-aof 与 redis-server 校验值一致,无需复制 + ``` +- 创建 /opt/redis_cluster/conf/redis.conf,常用配置如下 + ```bash + bind 0.0.0.0 + protected-mode yes + tcp-backlog 511 + timeout 3600 + tcp-keepalive 300 + daemonize no + supervised no + loglevel notice + always-show-logo no + save 900 1 + save 300 10 + save 60 10000 + stop-writes-on-bgsave-error yes + rdbcompression yes + rdbchecksum yes + dbfilename dump.rdb + #masterauth "123456" + replica-serve-stale-data yes + replica-read-only yes + repl-diskless-sync no + repl-diskless-sync-delay 5 + repl-disable-tcp-nodelay no + replica-priority 100 + #requirepass "123456" + maxclients 10000 + maxmemory 4294967296 + maxmemory-policy volatile-lru + lazyfree-lazy-eviction no + lazyfree-lazy-expire no + lazyfree-lazy-server-del no + replica-lazy-flush no + appendonly no + appendfilename "appendonly.aof" + appendfsync everysec + no-appendfsync-on-rewrite no + auto-aof-rewrite-percentage 100 + auto-aof-rewrite-min-size 64mb + aof-load-truncated yes + aof-use-rdb-preamble yes + lua-time-limit 5000 + cluster-enabled yes + cluster-node-timeout 5000 + slowlog-log-slower-than 10000 + slowlog-max-len 128 + latency-monitor-threshold 0 + notify-keyspace-events "" + hash-max-ziplist-entries 512 + hash-max-ziplist-value 64 + list-max-ziplist-size -2 + list-compress-depth 0 + set-max-intset-entries 512 + zset-max-ziplist-entries 128 + zset-max-ziplist-value 64 + hll-sparse-max-bytes 3000 + stream-node-max-bytes 4096 + stream-node-max-entries 100 + activerehashing yes + client-output-buffer-limit normal 0 0 0 + client-output-buffer-limit replica 256mb 64mb 60 + client-output-buffer-limit pubsub 32mb 8mb 60 + hz 10 + dynamic-hz yes + aof-rewrite-incremental-fsync yes + rdb-save-incremental-fsync yes + ``` +- 创建 /opt/redis_cluster/redis@.service,配置如下 + ```ini + [Unit] + Description=Redis cluster -- node %i + After=network.target + + [Service] + ExecStartPre=/usr/bin/ls -d /var/lib/redis_cluster/%i + ExecStart=/opt/redis_cluster/bin/redis-server /opt/redis_cluster/conf/redis.conf \ + --daemonize no \ + --port %i \ + --pidfile /var/run/redis_%i.pid \ + --logfile /var/log/redis_cluster/%i.log \ + --dir /var/lib/redis_cluster/%i \ + --cluster-config-file /var/lib/redis_cluster/%i/nodes.conf + ExecStop=/opt/redis_cluster/bin/redis-cli -h 127.0.0.1 -p %i shutdown + Restart=on-failure + RestartSec=1 + + [Install] + WantedBy=multi-user.target + ``` + +# 部署 +- 把测试机的 /opt/redis_cluster 目录复制到 10.0.4.110 和 10.0.4.111 上 + ```bash + scp -r /opt/redis_cluster 10.0.4.110:/opt/ + scp -r /opt/redis_cluster 10.0.4.111:/opt/ + ``` +- 在 10.0.4.110 和 10.0.4.111 上都执行以下操作 + ```bash + mv /opt/redis_cluster/redis@.service /usr/lib/systemd/system/ + systemctl daemon-reload + mkdir -p /var/lib/redis_cluster/{6371..6373} + mkdir -p /var/log/redis_cluster + ``` +- 在 10.0.4.110 和 10.0.4.111 上配置实例开机启动 + ```bash + systemctl enable redis@6371 + systemctl enable redis@6372 + systemctl enable redis@6373 + ``` +- 在 10.0.4.110 和 10.0.4.111 上启动全部实例 + ```bash + systemctl start redis@6371 + systemctl start redis@6372 + systemctl start redis@6373 + ``` + +# 配置集群 +- 在 10.0.4.110 或 10.0.4.111 上创建集群,指定三个 master 实例 + ```bash + /opt/redis_cluster/bin/redis-cli \ + --cluster create 10.0.4.110:6371 10.0.4.110:6372 10.0.4.110:6373 + ``` +- 输出如下图 +![](https://colben.cn/img/dc44a6e8688218edb10ad7f2427b0525132.png) +- 同意配置,输入 "yes",回车后输出如下图 +![](https://colben.cn/img/eb3c0ccd3d0017c18d4eba3a0b776d29e21.png) +- 查看集群当前实例 + ```bash + /opt/redis_cluster/bin/redis-cli -p 6371 cluster nodes + ``` +- 输出如下图 +![](https://colben.cn/img/0bfe3ac4984fbd6973738dddb20d5bb6c72.png) +- 添加 slave 实例 10.0.4.111:6371,同步 master 10.0.4.110:6371 + ```bash + /opt/redis_cluster/bin/redis-cli \ + --cluster add-node 10.0.4.111:6371 10.0.4.110:6371 \ + --cluster-slave \ + --cluster-master-id 5be0edaf74d1eec50bbe675f9698397023a943b7 + # 上述命令中,10.0.4.111:6371 是待添加实例,10.0.4.110:6371 是已存在集群中的任一实例 + # --cluster-slave 指定添加 slave 实例 + # --cluster-master-id 指定 10.0.4.110:6371 的 id,即本次添加实例的 master + ``` +- 输出如下图 +![](https://colben.cn/img/cf50fb5da24afb930eab80100707b13efbe.png) +- 添加 slave 实例 10.0.4.111:6372,同步 master 10.0.4.110:6372 + ```bash + /opt/redis_cluster/bin/redis-cli \ + --cluster add-node 10.0.4.111:6372 10.0.4.110:6371 \ + --cluster-slave \ + --cluster-master-id 96f2225fdd054d8a16b31726fd1eb0b66c612646 + ``` +- 输出如下图 +![](https://colben.cn/img/4577cd8a2c7aea037e50fb002f91bb40842.png) +- 添加 slave 实例 10.0.4.111:6373,同步 master 10.0.4.110:6373 + ```bash + /opt/redis_cluster/bin/redis-cli \ + --cluster add-node 10.0.4.111:6373 10.0.4.110:6371 \ + --cluster-slave \ + --cluster-master-id cd60c238e66c8b4a31355bee4610444a47acef86 + ``` +- 输出如下图 +![](https://colben.cn/img/633adea823cad6380030826ce75a44109fb.png) +- 查看集群当前实例 + ```bash + /opt/redis_cluster/bin/redis-cli -p 6371 cluster nodes + ``` +- 输出如下图 +![](https://colben.cn/img/3d5e7c0b8e7a7e51028d11358f442947367.png) + +# Docker 部署 +- 安装 docker-ce,参考[这里](https://www.colben.cn/post/docker/#%E5%AE%89%E8%A3%85) +- 安装 docker-compose,参考[这里](https://www.colben.cn/post/docker/#Compose) +- 选择一个较大分区,创建 redis 集群数据目录和日志目录 + ```bsah + mkdir -p redis_cluster/{db,log}/{6371..6376} + ``` + +- 在同级目录下创建 docker-compose.yml 文件,内容如下 + ```yml + version: "3.7" + + services: + redis-6371: + image: harbor.colben.cn/general/alpine-redis + container_name: redis-6371 + restart: on-failure + stop_grace_period: 1m + privileged: true + network_mode: "host" + environment: + _CENV_cluster-enabled: "yes" + _CENV_port: 6371 + _CENV_maxmemory: 256MB + # master 和 slave 实例的密码必须一致,可都为空 + _CENV_masterauth: Pass_1234 + _CENV_requirepass: Pass_1234 + # master 节点列表 + MASTER_NODES: "127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373" + # slave 节点列表 + # 可以为空,表示没有 slave 节点 + # 不为空时,数量必须与 master 节点数量一致,按顺序依次为 master 节点的从节点 + SLAVE_NODES: "127.0.0.1:6374 127.0.0.1:6375 127.0.0.1:6376" + volumes: + - type: bind + source: ./redis_cluster/db/6371 + target: /var/lib/redis + - type: bind + source: ./redis_cluster/log/6371 + target: /var/log/redis + + redis-6372: + image: harbor.colben.cn/general/alpine-redis + container_name: redis-6372 + restart: on-failure + stop_grace_period: 1m + privileged: true + network_mode: "host" + environment: + _CENV_cluster-enabled: "yes" + _CENV_port: 6372 + _CENV_maxmemory: 256MB + _CENV_masterauth: Pass_1234 + _CENV_requirepass: Pass_1234 + volumes: + - type: bind + source: ./redis_cluster/db/6372 + target: /var/lib/redis + - type: bind + source: ./redis_cluster/log/6372 + target: /var/log/redis + + redis-6373: + image: harbor.colben.cn/general/alpine-redis + container_name: redis-6373 + restart: on-failure + stop_grace_period: 1m + privileged: true + network_mode: "host" + environment: + _CENV_cluster-enabled: "yes" + _CENV_port: 6373 + _CENV_maxmemory: 256MB + _CENV_masterauth: Pass_1234 + _CENV_requirepass: Pass_1234 + volumes: + - type: bind + source: ./redis_cluster/db/6373 + target: /var/lib/redis + - type: bind + source: ./redis_cluster/log/6373 + target: /var/log/redis + + redis-6374: + image: harbor.colben.cn/general/alpine-redis + container_name: redis-6374 + restart: on-failure + stop_grace_period: 1m + privileged: true + network_mode: "host" + environment: + _CENV_cluster-enabled: "yes" + _CENV_port: 6374 + _CENV_maxmemory: 256MB + _CENV_masterauth: Pass_1234 + _CENV_requirepass: Pass_1234 + volumes: + - type: bind + source: ./redis_cluster/db/6374 + target: /var/lib/redis + - type: bind + source: ./redis_cluster/log/6374 + target: /var/log/redis + + redis-6375: + image: harbor.colben.cn/general/alpine-redis + container_name: redis-6375 + restart: on-failure + stop_grace_period: 1m + privileged: true + network_mode: "host" + environment: + _CENV_cluster-enabled: "yes" + _CENV_port: 6375 + _CENV_maxmemory: 256MB + _CENV_masterauth: Pass_1234 + _CENV_requirepass: Pass_1234 + volumes: + - type: bind + source: ./redis_cluster/db/6375 + target: /var/lib/redis + - type: bind + source: ./redis_cluster/log/6375 + target: /var/log/redis + + redis-6376: + image: harbor.colben.cn/general/alpine-redis + container_name: redis-6376 + restart: on-failure + stop_grace_period: 1m + privileged: true + network_mode: "host" + environment: + _CENV_cluster-enabled: "yes" + _CENV_port: 6376 + _CENV_maxmemory: 256MB + _CENV_masterauth: Pass_1234 + _CENV_requirepass: Pass_1234 + volumes: + - type: bind + source: ./redis_cluster/db/6376 + target: /var/lib/redis + - type: bind + source: ./redis_cluster/log/6376 + target: /var/log/redis + ``` + +- 启动 redis 集群 + ```bash + docker-compose up -d + ``` + +# 参考 +- [redis 官网](https://redis.io/topics/cluster-tutorial) + diff --git a/content/post/redis-sentinel.md b/content/post/redis-sentinel.md new file mode 100644 index 0000000..bbac9f7 --- /dev/null +++ b/content/post/redis-sentinel.md @@ -0,0 +1,121 @@ +--- +title: "Redis Sentinel" +date: 2019-10-30T11:37:36+08:00 +lastmod: 2019-10-30T11:37:36+08:00 +tags: ["redis", "sentinel", "哨兵"] +categories: ["database"] +--- + +# 环境 +主机名 | 地址 | 操作系统 | 初始状态 +---- | ---- | ---- | ---- +sentinel86 | 10.1.14.86 | CentOS7.4 | null +redis87 | 10.1.14.87 | CentOS7.4 | master +redis88 | 10.1.14.88 | CentOS7.4 | slave +redis89 | 10.1.14.89 | CentOS7.4 | slave + +# 安装 redis +- 在全部服务器上安装 redis + ```bash + yum install epel-release + yum install redis + ``` + +# redis master 配置 +- 在 redis87 上修改 /etc/redis.conf + ``` + bind 0.0.0.0 + ``` + +# redis slave 配置 +- 在 redis88 和 redis89 上修改 /etc/redis.conf + ``` + bind 0.0.0.0 + slaveof 10.1.14.87 6379 + ``` + +# redis sentinel 配置 +- 在 sentinel86 上创建三个 sentinel 配置文件 + ```bash + cd /etc/ + cp redis-sentinel.conf redis-sentinel-16379.conf + cp redis-sentinel.conf redis-sentinel-26379.conf + mv redis-sentinel.conf redis-sentinel-36379.conf + chown redis.root redis-sentinel-?6379.conf + ``` +- 修改 /etc/redis-sentinel-16379.conf + ``` + bind 0.0.0.0 + port 16379 + dir /var/log/redis/16379 + sentinel monitor mymaster 10.1.14.87 6379 2 + sentinel down-after-milliseconds mymaster 8000 + logfile /var/log/redis/16379/sentinel.log + ``` +- 修改 /etc/redis-sentinel-26379.conf + ``` + bind 0.0.0.0 + port 26379 + dir /var/log/redis/26379 + sentinel monitor mymaster 10.1.14.87 6379 2 + sentinel down-after-milliseconds mymaster 8000 + logfile /var/log/redis/26379/sentinel.log + ``` +- 修改 /etc/redis-sentinel-36379.conf + ``` + bind 0.0.0.0 + port 36379 + dir /var/log/redis/36379 + sentinel monitor mymaster 10.1.14.87 6379 2 + sentinel down-after-milliseconds mymaster 8000 + logfile /var/log/redis/36379/sentinel.log + ``` +- 创建日志目录 + ```bash + cd /var/log/redis/ + mkdir 16379 26379 36379 + chown redis.redis ?6379 -R + ``` +- 创建两个 sentinel service 文件 + ```bash + cd /usr/lib/systemd/system/ + cp redis-sentinel.service redis-sentinel-16379.service + cp redis-sentinel.service redis-sentinel-26379.service + mv redis-sentinel.service redis-sentinel-36379.service + ``` +- 修改 /usr/lib/systemd/system/redis-sentinel-16379.service + ``` + ExecStart=/usr/bin/redis-sentinel /etc/redis-sentinel-16379.conf --daemonize no + ExecStop=/usr/libexec/redis-shutdown redis-sentinel-16379 + ``` +- 修改 /usr/lib/systemd/system/redis-sentinel-26379.service + ``` + ExecStart=/usr/bin/redis-sentinel /etc/redis-sentinel-26379.conf --daemonize no + ExecStop=/usr/libexec/redis-shutdown redis-sentinel-26379 + ``` +- 修改 /usr/lib/systemd/system/redis-sentinel-36379.service + ``` + ExecStart=/usr/bin/redis-sentinel /etc/redis-sentinel-36379.conf --daemonize no + ExecStop=/usr/libexec/redis-shutdown redis-sentinel-36379 + ``` +- 更新服务 + ```bash + systemctl daemon-reload + ``` + +# 启动 redis 服务 +- 在 redis87、redis88 和 redis89 上启动 redis 服务 + ```bash + systemctl start redis + # 查看当前节点主从信息 + redis-cli info replication + ``` + +# 启动 redis sentinel 监控 +- 在 sentinel86 上启动 sentinel 监管程序 + ```bash + systemctl start redis-sentinel-16379 + systemctl start redis-sentinel-26379 + systemctl start redis-sentinel-36379 + ``` + diff --git a/content/post/rsync.md b/content/post/rsync.md new file mode 100644 index 0000000..e924dd0 --- /dev/null +++ b/content/post/rsync.md @@ -0,0 +1,101 @@ +--- +title: "rsync 命令" +date: 2019-10-29T21:21:14+08:00 +lastmod: 2019-10-29T21:21:14+08:00 +keywords: [] +tags: ["rsync", "同步"] +categories: ["shell"] +--- + +# 简介 +- rsync(Remote Sync,远程同步)是一个开源的快速备份工具,可以在不同主机之间镜像同步整个目录树,支持增量备份,保持链接和权限,且采用优化的同步算法,传输前执行压缩,因此非常适用于异地备份、镜像服务器等应用。 +- 支持: + - 本地复制 + - 与其他SSH同步 + - 与rsync主机同步 + +# rsyncd 源服务 +- 创建 rsync 配置文件 /etc/rsyncd.conf,内容如下 + ```ini + # 限制客户端登陆身份为 nobody:nobody + uid = nobody + gid = nobody + # 禁锢在源目录下 + use chroot = yes + # 指定监听端口,默认监听 tcp 873 端口 + port = 873 + # 指定监听地址,默认监听全部网卡 + address = 192.168.1.101 + # 允许访问的客户端地址 + hosts allow = 192.168.1.0/24 + max connections = 4 + # 日志位置 + log file = /var/log/rsyncd.log + # PID 文件位置 + pid file = /var/run/rsyncd.pid + exclude = lost+found/ + transfer logging = yes + timeout = 900 + ignore nonreadable = yes + # 同步时不压缩的文件类型 + dont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 + # 账户文件,每行格式 user:password,权限 600 + secrets file = /etc/rsyncd_users.db + + [data1] + # 源目录实际路径,注意该目录需要对 nobody 用户可读 + path = /path/to/data1 + # 描述 + comment = sth about dir data1 + # 只读 + read only = yes + # 授权账户 + auth users = backuper + ``` +- 创建备份帐号信息 + ```bash + echo 'backuper:123456' > /etc/rsyncd_users.db + chmod 0600 /etc/rsyncd_users.db + ``` +- 启动 rsyncd 服务 + ```bash + systemctl start rsyncd + ``` + +# rsync 命令选项 +- -r: 递归模式,包含目录及子目录中所有文件 +- -l: 对于符号链接文件仍然复制为符号链接文件 +- -p: 保留文件的权限标记 +- -t: 保留文件的时间标记 +- -g: 保留文件的属组标记(仅超级用户使用) +- -o: 保留文件的属主标记(仅超级用户使用) +- -D: 保留设备文件及其他特殊文件 +- -a: 归档模式,递归并保留对象属性,等同于 -rlptgoD +- -v: 显示同步过程的详细(verbose)信息 +- -z: 在传输文件时进行压缩(compress) +- -H: 保留硬连接文件 +- -A: 保留ACL属性信息 +- --delete: 删除目标位置有而原始位置没有的文件 +- --checksum: 根据对象的校验和来决定是否跳过文件 +- --password-file: 指定包含密码的文件 + +# 简单使用 +- 本地复制,类似 cp 命令 + ```bash + rsync -a /etc/passwd 123.txt + ``` +- 与其他 ssh 同步 + ```bash + rsync -av root@192.168.1.100:/root/123.txt . + ``` +- 与 rsync 主机同步 + ```bash + rsync -avz backuper@192.168.1.100::data1 /root + # 或者 + rsync -avz rsync://backuper@192.168.1.100/data1 /root + ``` +- 向同步源服务上传文件时,直接调换源参数和目的参数,注意客户端可写上传目录 + +# 参考 +- [https://www.linuxidc.com/Linux/2019-08/160108.htm](https://www.linuxidc.com/Linux/2019-08/160108.htm) + diff --git a/content/post/rust.md b/content/post/rust.md new file mode 100644 index 0000000..5da9bf1 --- /dev/null +++ b/content/post/rust.md @@ -0,0 +1,88 @@ +--- +title: "Rust 笔记" +date: 2021-08-29T15:02:13+08:00 +lastmod: 2021-08-29T15:02:13+08:00 +keywords: [] +tags: [] +categories: ["dev/ops"] +--- + +# 安装 rust +- 安装 rustup,参考官网 + ```bash + export RUST_UPDATE_ROOT=https://mirrors.tuna.tsinghua.edu.cn/rustup + export RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + # 按照提示,一直 default 即可 + ``` + +- 配置 rustup、toolchain 和 target 更新源 + ```bash + cat >> /etc/profile.d/rustup.sh <<-EOF + export RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup + EOF + ``` + +- 配置 crate 源 + ```bash + cat >> ~/.cargo/config <<-EOF + [source.crates-io] + replace-with = 'tuna' + + [source.tuna] + registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" + EOF + ``` + +# 交叉编译 +## 树梅派 Alpine armv7/armhf +- 下载交叉编译工具 + ```bash + curl -LO https://musl.cc/armv7l-linux-musleabihf-cross.tgz + tar zxf armv7l-linux-musleabihf-cross.tgz -C /opt/ + export PATH=/opt/armv7l-linux-musleabihf-cross/bin/:$PATH + ``` + +- 配置 target + ```bash + rustup target add armv7-unknown-linux-musleabihf + cat >> ~/.cargo/config <<-EOF + [target.armv7-unknown-linux-musleabihf] + linker = "armv7l-linux-musleabihf-ld" + EOF + ``` + +- 编译 + ```bash + # 操作系统如果是 armv7,则需指定该 cflag 来禁用 fpu + export CFLAGS='-mfpu=neon' + + export CC=armv7l-linux-musleabihf-gcc + cargo build --target armv7-unknown-linux-musleabihf --release + armv7l-linux-musleabihf-strip target/armv7-unknown-linux-musleabihf/release/{目标二进制文件} + ``` + +## 树梅派 Alpine aarch64 +- 下载交叉编译工具 + ```bash + curl -LO https://musl.cc/aarch64-linux-musl-cross.tgz + tar zxf aarch64-linux-musl-cross.tgz -C /opt/ + export PATH=/opt/aarch64-linux-musl-cross/bin/:$PATH + ``` + +- 配置 target + ```bash + rustup target add aarch64-unknown-linux-musl + cat >> ~/.cargo/config <<-EOF + [target.aarch64-unknown-linux-musl] + linker = "aarch64-linux-musl-ld" + EOF + ``` + +- 编译 + ```bash + export CC=aarch64-linux-musl-gcc + cargo build --target aarch64-unknown-linux-musl --release + aarch64-linux-musl-strip target/aarch64-unknown-linux-musl/release/{目标二进制文件} + ``` + diff --git a/content/post/sed.md b/content/post/sed.md new file mode 100644 index 0000000..b863957 --- /dev/null +++ b/content/post/sed.md @@ -0,0 +1,179 @@ +--- +title: "sed 命令" +date: 2019-10-30T00:56:26+08:00 +lastmod: 2019-10-30T00:56:26+08:00 +keywords: [] +tags: ["sed"] +categories: ["shell"] +--- + +# 流程 +- 每次从输入中读取一行 +- 匹配数据 +- 修改数据 +- 输出数据(默认 stdout) + +# 选项 +- -e 执行多个编辑命令 + ```bash + sed -e 's/root/ROOT/g; s/bin/BIN/g' /etc/passwd + sed -e 's/root/ROOT/g' -e 's/bin/BIN/g' /etc/passwd + ``` +- -f 从指定文件中获取 sed 命令 + ```bash + cat > script.sed <> /etc/sudoers +或者 +echo "cephdeploy ALL = (root) NOPASSWD:ALL" > /etc/sudoers.d/username +chmod 0400 /etc/sudoers.d/username +``` + +# 用户登陆操作记录 +- 创建日志共存目录 + ```bash + mkdir /var/log/user_history -p + chmod 0777 /var/log/user_history -R + ``` +- 配置 history 历史文件 /etc/profile.d/history.sh + ```bash + #!/bin/bash + # + export HISTSIZE=10000 + export HISTTIMEFORMAT="[%F %T] " + # + LOG_DIR=/var/log/user_history + mkdir -p $LOG_DIR/$USER + if [ 0 -eq $? ]; then + SRC_IP="$(who -u am i 2>/dev/null | awk '{print $NF}' | sed 's/[()]//g')" + [ -z "$SRC_IP" ] && SRC_IP='127.0.0.1' + hist_file="$USER/${SRC_IP}_$(date +%Y%m%d-%H%M%S)" + export HISTFILE="$LOG_DIR/$hist_file" + else + echo "本次登陆没有监管,10秒后自动断开!" + export TMOUT=10 + fi + ``` + diff --git a/content/post/soft-raid.md b/content/post/soft-raid.md new file mode 100644 index 0000000..b477999 --- /dev/null +++ b/content/post/soft-raid.md @@ -0,0 +1,1484 @@ +--- +title: "软 RAID" +date: 2019-10-29T21:31:58+08:00 +lastmod: 2019-10-29T21:31:58+08:00 +keywords: [] +tags: ["raid"] +categories: ["storage"] +--- + +# mdadm 命令 +- 创建软 raid + ```bash + mdadm -C -v /dev/创建的设备名 -l级别 -n数量 添加的磁盘 [-x数量 添加的热备份盘] + ``` + - -C: 创建一个新的阵列--create + - -v: 显示细节--verbose + - -l: 设定RAID级别--level= + - -n: 指定阵列中可用device数目--raid-devices= + - -x: 指定初始阵列的富余device数目--spare-devices=,空闲盘(热备磁盘)能在工作盘损坏后自动顶替 +- 查看详细信息 + ```bash + mdadm -D /dev/设备名 + ``` +- 查看 RAID 状态 + ```bash + cat /proc/mdstat + ``` +- 模拟损坏 + ```bash + mdadm -f /dev/设备名 磁盘 + ``` +- 移除损坏的磁盘 + ```bash + mdadm -r /dev/设备名 磁盘 + ``` +- 添加新硬盘作为热备份盘 + ```bash + mdadm -a /dev/设备名 磁盘 + ``` + +# RAID0 +- RAID0俗称条带,它将两个或多个硬盘组成一个逻辑硬盘,容量是所有硬盘之和,因为是多个硬盘组合成一个,故可并行写操作,写入速度提高,但此方式硬盘数据没有冗余,没有容错,一旦一个物理硬盘损坏,则所有数据均丢失。因而,RAID0适合于对数据量大,但安全性要求不高的场景,比如音像、视频文件的存储等 +![](/img/raid0.jpg) +- 实验: RAID0创建,格式化,挂载使用 +- 添加2块20G的硬盘,分区,类型ID为fd + ``` + [root@localhost ~]# fdisk -l | grep raid + /dev/sdb1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdc1 2048 41943039 20970496 fd Linux raid autodetect + ``` +- 创建RAID0 + ``` + [root@localhost ~]# mdadm -C -v /dev/md0 -l0 -n2 /dev/sd{b,c}1 + mdadm: chunk size defaults to 512K + mdadm: Fail create md0 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md0 started. + ``` +- 查看raidstat状态 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid0] + md0 : active raid0 sdc1[1] sdb1[0] + 41906176 blocks super 1.2 512k chunks + + unused devices: + ``` +- 查看RAID0的详细信息 + ``` + [root@localhost ~]# mdadm -D /dev/md0 + /dev/md0: + Version : 1.2 + Creation Time : Sun Aug 25 15:28:13 2019 + Raid Level : raid0 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 15:28:13 2019 + State : clean + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Chunk Size : 512K + + Consistency Policy : none + + Name : localhost:0 (local to host localhost) + UUID : 7ff54c57:b99a59da:6b56c6d5:a4576ccf + Events : 0 + + Number Major Minor RaidDevice State + 0 8 17 0 active sync /dev/sdb1 + 1 8 33 1 active sync /dev/sdc1 + ``` +- 格式化 + ``` + [root@localhost ~]# mkfs.xfs /dev/md0 + meta-data=/dev/md0 isize=512 agcount=16, agsize=654720 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=1 finobt=0, sparse=0 + data = bsize=4096 blocks=10475520, imaxpct=25 + = sunit=128 swidth=256 blks + naming =version 2 bsize=4096 ascii-ci=0 ftype=1 + log =internal log bsize=4096 blocks=5120, version=2 + = sectsz=512 sunit=8 blks, lazy-count=1 + realtime =none extsz=4096 blocks=0, rtextents=0 + ``` +- 挂载使用 + ``` + [root@localhost ~]# mkdir /mnt/md0 + [root@localhost ~]# mount /dev/md0 /mnt/md0/ + [root@localhost ~]# df -hT + Filesystem Type Size Used Avail Use% Mounted on + /dev/mapper/CentOS-root xfs 17G 1013M 16G 6% / + devtmpfs devtmpfs 901M 0 901M 0% /dev + tmpfs tmpfs 912M 0 912M 0% /dev/shm + tmpfs tmpfs 912M 8.7M 904M 1% /run + tmpfs tmpfs 912M 0 912M 0% /sys/fs/cgroup + /dev/sda1 xfs 1014M 143M 872M 15% /boot + tmpfs tmpfs 183M 0 183M 0% /run/user/0 + /dev/md0 xfs 40G 33M 40G 1% /mnt/md0 + ``` + +# RAID1 +- RAID1俗称镜像,它最少由两个硬盘组成,且两个硬盘上存储的数据均相同,以实现数据冗余。RAID1读操作速度有所提高,写操作理论上与单硬盘速度一样,但由于数据需要同时写入所有硬盘,实际上稍为下降。容错性是所有组合方式里最好的,只要有一块硬盘正常,则能保持正常工作。但它对硬盘容量的利用率则是最低,只有50%,因而成本也是最高。RAID1适合对数据安全性要求非常高的场景,比如存储数据库数据文件之类 +![](/img/raid1.jpg) +- 实验: RAID1创建,格式化,挂载使用,故障模拟,重新添加热备份 +- 添加3块20G的硬盘,分区,类型ID为fd。 + ``` + [root@localhost ~]# fdisk -l | grep raid + /dev/sdb1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdc1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdd1 2048 41943039 20970496 fd Linux raid autodetect + ``` +- 创建RAID1,并添加1个热备份盘。 + ``` + [root@localhost ~]# mdadm -C -v /dev/md1 -l1 -n2 /dev/sd{b,c}1 -x1 /dev/sdd1 + mdadm: Note: this array has metadata at the start and + may not be suitable as a boot device. If you plan to + store '/boot' on this device please ensure that + your boot-loader understands md/v1.x metadata, or use + --metadata=0.90 + mdadm: size set to 20953088K + Continue creating array? y + mdadm: Fail create md1 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md1 started. + ``` +- 查看raidstat状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] + md1 : active raid1 sdd1[2](S) sdc1[1] sdb1[0] + 20953088 blocks super 1.2 [2/2] [UU] + [========>............] resync = 44.6% (9345792/20953088) finish=0.9min speed=203996K/sec + + unused devices: + + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] + md1 : active raid1 sdd1[2](S) sdc1[1] sdb1[0] + 20953088 blocks super 1.2 [2/2] [UU] + + unused devices: + ``` +- 查看RAID1的详细信息。 + ``` + [root@localhost ~]# mdadm -D /dev/md1 + /dev/md1: + Version : 1.2 + Creation Time : Sun Aug 25 15:38:44 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 3 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 15:39:24 2019 + State : clean, resyncing + Active Devices : 2 + Working Devices : 3 + Failed Devices : 0 + Spare Devices : 1 + + Consistency Policy : resync + + Resync Status : 40% complete + + Name : localhost:1 (local to host localhost) + UUID : b921e8b3:a18e2fc9:11706ba4:ed633dfd + Events : 6 + + Number Major Minor RaidDevice State + 0 8 17 0 active sync /dev/sdb1 + 1 8 33 1 active sync /dev/sdc1 + + 2 8 49 - spare /dev/sdd1 + ``` +- 格式化。 + ``` + [root@localhost ~]# mkfs.xfs /dev/md1 + meta-data=/dev/md1 isize=512 agcount=4, agsize=1309568 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=1 finobt=0, sparse=0 + data = bsize=4096 blocks=5238272, imaxpct=25 + = sunit=0 swidth=0 blks + naming =version 2 bsize=4096 ascii-ci=0 ftype=1 + log =internal log bsize=4096 blocks=2560, version=2 + = sectsz=512 sunit=0 blks, lazy-count=1 + realtime =none extsz=4096 blocks=0, rtextents=0 + ``` +- 挂载使用。 + ``` + [root@localhost ~]# mkdir /mnt/md1 + [root@localhost ~]# mount /dev/md1 /mnt/md1/ + [root@localhost ~]# df -hT + Filesystem Type Size Used Avail Use% Mounted on + /dev/mapper/centos-root xfs 17G 1014M 16G 6% / + devtmpfs devtmpfs 901M 0 901M 0% /dev + tmpfs tmpfs 912M 0 912M 0% /dev/shm + tmpfs tmpfs 912M 8.7M 904M 1% /run + tmpfs tmpfs 912M 0 912M 0% /sys/fs/cgroup + /dev/sda1 xfs 1014M 143M 872M 15% /boot + tmpfs tmpfs 183M 0 183M 0% /run/user/0 + /dev/md1 xfs 20G 33M 20G 1% /mnt/md1 + ``` +- 创建测试文件。 + ``` + [root@localhost ~]# touch /mnt/md1/test{1..9}.txt + [root@localhost ~]# ls /mnt/md1/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 故障模拟。 + ``` + [root@localhost ~]# mdadm -f /dev/md1 /dev/sdb1 + mdadm: set /dev/sdb1 faulty in /dev/md1 + ``` +- 查看测试文件。 + ``` + [root@localhost ~]# ls /mnt/md1/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] + md1 : active raid1 sdd1[2] sdc1[1] sdb1[0](F) + 20953088 blocks super 1.2 [2/1] [_U] + [=====>...............] recovery = 26.7% (5600384/20953088) finish=1.2min speed=200013K/sec + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md1 + /dev/md1: + Version : 1.2 + Creation Time : Sun Aug 25 15:38:44 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 3 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 15:47:57 2019 + State : active, degraded, recovering + Active Devices : 1 + Working Devices : 2 + Failed Devices : 1 + Spare Devices : 1 + + Consistency Policy : resync + + Rebuild Status : 17% complete + + Name : localhost:1 (local to host localhost) + UUID : b921e8b3:a18e2fc9:11706ba4:ed633dfd + Events : 22 + + Number Major Minor RaidDevice State + 2 8 49 0 spare rebuilding /dev/sdd1 + 1 8 33 1 active sync /dev/sdc1 + + 0 8 17 - faulty /dev/sdb1 + ``` +- 再次查看状态 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] + md1 : active raid1 sdd1[2] sdc1[1] sdb1[0](F) + 20953088 blocks super 1.2 [2/2] [UU] + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md1 + /dev/md1: + Version : 1.2 + Creation Time : Sun Aug 25 15:38:44 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 3 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 15:49:28 2019 + State : active + Active Devices : 2 + Working Devices : 2 + Failed Devices : 1 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:1 (local to host localhost) + UUID : b921e8b3:a18e2fc9:11706ba4:ed633dfd + Events : 37 + + Number Major Minor RaidDevice State + 2 8 49 0 active sync /dev/sdd1 + 1 8 33 1 active sync /dev/sdc1 + + 0 8 17 - faulty /dev/sdb1 + ``` +- 移除损坏的磁盘 + ``` + [root@localhost ~]# mdadm -r /dev/md1 /dev/sdb1 + mdadm: hot removed /dev/sdb1 from /dev/md1 + + [root@localhost ~]# mdadm -D /dev/md1 + /dev/md1: + Version : 1.2 + Creation Time : Sun Aug 25 15:38:44 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 15:52:57 2019 + State : active + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:1 (local to host localhost) + UUID : b921e8b3:a18e2fc9:11706ba4:ed633dfd + Events : 38 + + Number Major Minor RaidDevice State + 2 8 49 0 active sync /dev/sdd1 + 1 8 33 1 active sync /dev/sdc1 + ``` +- 重新添加热备份盘。 + ``` + [root@localhost ~]# mdadm -a /dev/md1 /dev/sdb1 + mdadm: added /dev/sdb1 + + [root@localhost ~]# mdadm -D /dev/md1 + /dev/md1: + Version : 1.2 + Creation Time : Sun Aug 25 15:38:44 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 3 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 15:53:32 2019 + State : active + Active Devices : 2 + Working Devices : 3 + Failed Devices : 0 + Spare Devices : 1 + + Consistency Policy : resync + + Name : localhost:1 (local to host localhost) + UUID : b921e8b3:a18e2fc9:11706ba4:ed633dfd + Events : 39 + + Number Major Minor RaidDevice State + 2 8 49 0 active sync /dev/sdd1 + 1 8 33 1 active sync /dev/sdc1 + + 3 8 17 - spare /dev/sdb1 + ``` + +# RAID5 +- RAID5最少由三个硬盘组成,它将数据分散存储于阵列中的每个硬盘,并且还伴有一个数据校验位,数据位与校验位通过算法能相互验证,当丢失其中的一位时,RAID控制器能通过算法,利用其它两位数据将丢失的数据进行计算还原。因而RAID5最多能允许一个硬盘损坏,有容错性。RAID5相对于其它的组合方式,在容错与成本方面有一个平衡,因而受到大多数使用者的欢迎。一般的磁盘阵列,最常使用的就是RAID5这种方式 +![](/img/raid5.jpg) +- 实验: RAID5创建,格式化,挂载使用,故障模拟,重新添加热备份 +- 添加4块20G的硬盘,分区,类型ID为fd。 + ``` + [root@localhost ~]# fdisk -l | grep raid + /dev/sdb1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdc1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdd1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sde1 2048 41943039 20970496 fd Linux raid autodetect + ``` +- 创建RAID5,并添加1个热备份盘。 + ``` + [root@localhost ~]# mdadm -C -v /dev/md5 -l5 -n3 /dev/sd[b-d]1 -x1 /dev/sde1 + mdadm: layout defaults to left-symmetric + mdadm: layout defaults to left-symmetric + mdadm: chunk size defaults to 512K + mdadm: size set to 20953088K + mdadm: Fail create md5 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md5 started. + ``` +- 查看raidstat状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md5 : active raid5 sdd1[4] sde1[3](S) sdc1[1] sdb1[0] + 41906176 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/2] [UU_] + [====>................] recovery = 24.1% (5057340/20953088) finish=1.3min speed=202293K/sec + + unused devices: + + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md5 : active raid5 sdd1[4] sde1[3](S) sdc1[1] sdb1[0] + 41906176 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU] + + unused devices: + ``` +- 查看RAID5的详细信息。 + ``` + [root@localhost ~]# mdadm -D /dev/md5 + /dev/md5: + Version : 1.2 + Creation Time : Sun Aug 25 16:13:44 2019 + Raid Level : raid5 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 3 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:15:29 2019 + State : clean + Active Devices : 3 + Working Devices : 4 + Failed Devices : 0 + Spare Devices : 1 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:5 (local to host localhost) + UUID : a055094e:9adaff79:2edae9b9:0dcc3f1b + Events : 18 + + Number Major Minor RaidDevice State + 0 8 17 0 active sync /dev/sdb1 + 1 8 33 1 active sync /dev/sdc1 + 4 8 49 2 active sync /dev/sdd1 + + 3 8 65 - spare /dev/sde1 + ``` +- 格式化。 + ``` + [root@localhost ~]# mkfs.xfs /dev/md5 + meta-data=/dev/md5 isize=512 agcount=16, agsize=654720 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=1 finobt=0, sparse=0 + data = bsize=4096 blocks=10475520, imaxpct=25 + = sunit=128 swidth=256 blks + naming =version 2 bsize=4096 ascii-ci=0 ftype=1 + log =internal log bsize=4096 blocks=5120, version=2 + = sectsz=512 sunit=8 blks, lazy-count=1 + realtime =none extsz=4096 blocks=0, rtextents=0 + ``` +- 挂载使用。 + ``` + [root@localhost ~]# mkdir /mnt/md5 + [root@localhost ~]# mount /dev/md5 /mnt/md5/ + [root@localhost ~]# df -hT + Filesystem Type Size Used Avail Use% Mounted on + /dev/mapper/centos-root xfs 17G 1014M 16G 6% / + devtmpfs devtmpfs 901M 0 901M 0% /dev + tmpfs tmpfs 912M 0 912M 0% /dev/shm + tmpfs tmpfs 912M 8.7M 904M 1% /run + tmpfs tmpfs 912M 0 912M 0% /sys/fs/cgroup + /dev/sda1 xfs 1014M 143M 872M 15% /boot + tmpfs tmpfs 183M 0 183M 0% /run/user/0 + /dev/md5 xfs 40G 33M 40G 1% /mnt/md5 + ``` +- 创建测试文件。 + ``` + [root@localhost ~]# touch /mnt/md5/test{1..9}.txt + [root@localhost ~]# ls /mnt/md5/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 故障模拟。 + ``` + [root@localhost ~]# mdadm -f /dev/md5 /dev/sdb1 + mdadm: set /dev/sdb1 faulty in /dev/md5 + ``` +- 查看测试文件。 + ``` + [root@localhost ~]# ls /mnt/md5/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md5 : active raid5 sdd1[4] sde1[3] sdc1[1] sdb1[0](F) + 41906176 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/2] [_UU] + [====>................] recovery = 21.0% (4411136/20953088) finish=1.3min speed=210054K/sec + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md5 + /dev/md5: + Version : 1.2 + Creation Time : Sun Aug 25 16:13:44 2019 + Raid Level : raid5 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 3 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:21:31 2019 + State : clean, degraded, recovering + Active Devices : 2 + Working Devices : 3 + Failed Devices : 1 + Spare Devices : 1 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Rebuild Status : 12% complete + + Name : localhost:5 (local to host localhost) + UUID : a055094e:9adaff79:2edae9b9:0dcc3f1b + Events : 23 + + Number Major Minor RaidDevice State + 3 8 65 0 spare rebuilding /dev/sde1 + 1 8 33 1 active sync /dev/sdc1 + 4 8 49 2 active sync /dev/sdd1 + + 0 8 17 - faulty /dev/sdb1 + ``` +- 再次查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md5 : active raid5 sdd1[4] sde1[3] sdc1[1] sdb1[0](F) + 41906176 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU] + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md5 + /dev/md5: + Version : 1.2 + Creation Time : Sun Aug 25 16:13:44 2019 + Raid Level : raid5 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 3 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:23:09 2019 + State : clean + Active Devices : 3 + Working Devices : 3 + Failed Devices : 1 + Spare Devices : 0 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:5 (local to host localhost) + UUID : a055094e:9adaff79:2edae9b9:0dcc3f1b + Events : 39 + + Number Major Minor RaidDevice State + 3 8 65 0 active sync /dev/sde1 + 1 8 33 1 active sync /dev/sdc1 + 4 8 49 2 active sync /dev/sdd1 + + 0 8 17 - faulty /dev/sdb1 + ``` +- 移除损坏的磁盘。 + ``` + [root@localhost ~]# mdadm -r /dev/md5 /dev/sdb1 + mdadm: hot removed /dev/sdb1 from /dev/md5 + + [root@localhost ~]# mdadm -D /dev/md5 + /dev/md5: + Version : 1.2 + Creation Time : Sun Aug 25 16:13:44 2019 + Raid Level : raid5 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 3 + Total Devices : 3 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:25:01 2019 + State : clean + Active Devices : 3 + Working Devices : 3 + Failed Devices : 0 + Spare Devices : 0 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:5 (local to host localhost) + UUID : a055094e:9adaff79:2edae9b9:0dcc3f1b + Events : 40 + + Number Major Minor RaidDevice State + 3 8 65 0 active sync /dev/sde1 + 1 8 33 1 active sync /dev/sdc1 + 4 8 49 2 active sync /dev/sdd1 + ``` +- 重新添加热备份盘。 + ``` + [root@localhost ~]# mdadm -a /dev/md5 /dev/sdb1 + mdadm: added /dev/sdb1 + + [root@localhost ~]# mdadm -D /dev/md5 + /dev/md5: + Version : 1.2 + Creation Time : Sun Aug 25 16:13:44 2019 + Raid Level : raid5 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 3 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:25:22 2019 + State : clean + Active Devices : 3 + Working Devices : 4 + Failed Devices : 0 + Spare Devices : 1 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:5 (local to host localhost) + UUID : a055094e:9adaff79:2edae9b9:0dcc3f1b + Events : 41 + + Number Major Minor RaidDevice State + 3 8 65 0 active sync /dev/sde1 + 1 8 33 1 active sync /dev/sdc1 + 4 8 49 2 active sync /dev/sdd1 + + 5 8 17 - spare /dev/sdb1 + ``` + +# RAID6 +- RAID6是在RAID5的基础上改良而成的,RAID6再将数据校验位增加一位,所以允许损坏的硬盘数量也由 RAID5的一个增加到二个。由于同一阵列中两个硬盘同时损坏的概率非常少,所以,RAID6用增加一块硬盘的代价,换来了比RAID5更高的数据安全性 +![](/img/raid6.jpg) +- 实验: RAID6创建,格式化,挂载使用,故障模拟,重新添加热备份 +- 添加6块20G的硬盘,分区,类型ID为fd。 + ``` + [root@localhost ~]# fdisk -l | grep raid + /dev/sdb1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdc1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdd1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sde1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdf1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdg1 2048 41943039 20970496 fd Linux raid autodetect + ``` +- 创建RAID6,并添加2个热备份盘。 + ``` + [root@localhost ~]# mdadm -C -v /dev/md6 -l6 -n4 /dev/sd[b-e]1 -x2 /dev/sd[f-g]1 + mdadm: layout defaults to left-symmetric + mdadm: layout defaults to left-symmetric + mdadm: chunk size defaults to 512K + mdadm: size set to 20953088K + mdadm: Fail create md6 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md6 started. + ``` +- 查看raidstat状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md6 : active raid6 sdg1[5](S) sdf1[4](S) sde1[3] sdd1[2] sdc1[1] sdb1[0] + 41906176 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/4] [UUUU] + [===>.................] resync = 18.9% (3962940/20953088) finish=1.3min speed=208575K/sec + + unused devices: + + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md6 : active raid6 sdg1[5](S) sdf1[4](S) sde1[3] sdd1[2] sdc1[1] sdb1[0] + 41906176 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/4] [UUUU] + + unused devices: + ``` +- 查看RAID6的详细信息。 + ``` + [root@localhost ~]# mdadm -D /dev/md6 + /dev/md6: + Version : 1.2 + Creation Time : Sun Aug 25 16:34:36 2019 + Raid Level : raid6 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 4 + Total Devices : 6 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:34:43 2019 + State : clean, resyncing + Active Devices : 4 + Working Devices : 6 + Failed Devices : 0 + Spare Devices : 2 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Resync Status : 10% complete + + Name : localhost:6 (local to host localhost) + UUID : 7c3d15a2:4066f2c6:742f3e4c:82aae1bb + Events : 1 + + Number Major Minor RaidDevice State + 0 8 17 0 active sync /dev/sdb1 + 1 8 33 1 active sync /dev/sdc1 + 2 8 49 2 active sync /dev/sdd1 + 3 8 65 3 active sync /dev/sde1 + + 4 8 81 - spare /dev/sdf1 + 5 8 97 - spare /dev/sdg1 + ``` +- 格式化。 + ``` + [root@localhost ~]# mkfs.xfs /dev/md6 + meta-data=/dev/md6 isize=512 agcount=16, agsize=654720 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=1 finobt=0, sparse=0 + data = bsize=4096 blocks=10475520, imaxpct=25 + = sunit=128 swidth=256 blks + naming =version 2 bsize=4096 ascii-ci=0 ftype=1 + log =internal log bsize=4096 blocks=5120, version=2 + = sectsz=512 sunit=8 blks, lazy-count=1 + realtime =none extsz=4096 blocks=0, rtextents=0 + ``` +- 挂载使用。 + ``` + [root@localhost ~]# mkdir /mnt/md6 + [root@localhost ~]# mount /dev/md6 /mnt/md6/ + [root@localhost ~]# df -hT + Filesystem Type Size Used Avail Use% Mounted on + /dev/mapper/centos-root xfs 17G 1014M 16G 6% / + devtmpfs devtmpfs 901M 0 901M 0% /dev + tmpfs tmpfs 912M 0 912M 0% /dev/shm + tmpfs tmpfs 912M 8.7M 903M 1% /run + tmpfs tmpfs 912M 0 912M 0% /sys/fs/cgroup + /dev/sda1 xfs 1014M 143M 872M 15% /boot + tmpfs tmpfs 183M 0 183M 0% /run/user/0 + /dev/md6 xfs 40G 33M 40G 1% /mnt/md6 + ``` +- 创建测试文件。 + ``` + [root@localhost ~]# touch /mnt/md6/test{1..9}.txt + [root@localhost ~]# ls /mnt/md6/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 故障模拟。 + ``` + [root@localhost ~]# mdadm -f /dev/md6 /dev/sdb1 + mdadm: set /dev/sdb1 faulty in /dev/md6 + [root@localhost ~]# mdadm -f /dev/md6 /dev/sdc1 + mdadm: set /dev/sdc1 faulty in /dev/md6 + ``` +- 查看测试文件。 + ``` + [root@localhost ~]# ls /mnt/md6/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md6 : active raid6 sdg1[5] sdf1[4] sde1[3] sdd1[2] sdc1[1](F) sdb1[0](F) + 41906176 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/2] [__UU] + [====>................] recovery = 23.8% (4993596/20953088) finish=1.2min speed=208066K/sec + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md6 + /dev/md6: + Version : 1.2 + Creation Time : Sun Aug 25 16:34:36 2019 + Raid Level : raid6 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 4 + Total Devices : 6 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:41:09 2019 + State : clean, degraded, recovering + Active Devices : 2 + Working Devices : 4 + Failed Devices : 2 + Spare Devices : 2 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Rebuild Status : 13% complete + + Name : localhost:6 (local to host localhost) + UUID : 7c3d15a2:4066f2c6:742f3e4c:82aae1bb + Events : 27 + + Number Major Minor RaidDevice State + 5 8 97 0 spare rebuilding /dev/sdg1 + 4 8 81 1 spare rebuilding /dev/sdf1 + 2 8 49 2 active sync /dev/sdd1 + 3 8 65 3 active sync /dev/sde1 + + 0 8 17 - faulty /dev/sdb1 + 1 8 33 - faulty /dev/sdc1 + ``` +- 再次查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid6] [raid5] [raid4] + md6 : active raid6 sdg1[5] sdf1[4] sde1[3] sdd1[2] sdc1[1](F) sdb1[0](F) + 41906176 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/4] [UUUU] + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md6 + /dev/md6: + Version : 1.2 + Creation Time : Sun Aug 25 16:34:36 2019 + Raid Level : raid6 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 4 + Total Devices : 6 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:42:42 2019 + State : clean + Active Devices : 4 + Working Devices : 4 + Failed Devices : 2 + Spare Devices : 0 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:6 (local to host localhost) + UUID : 7c3d15a2:4066f2c6:742f3e4c:82aae1bb + Events : 46 + + Number Major Minor RaidDevice State + 5 8 97 0 active sync /dev/sdg1 + 4 8 81 1 active sync /dev/sdf1 + 2 8 49 2 active sync /dev/sdd1 + 3 8 65 3 active sync /dev/sde1 + + 0 8 17 - faulty /dev/sdb1 + 1 8 33 - faulty /dev/sdc1 + ``` +- 移除损坏的磁盘。 + ``` + [root@localhost ~]# mdadm -r /dev/md6 /dev/sd{b,c}1 + mdadm: hot removed /dev/sdb1 from /dev/md6 + mdadm: hot removed /dev/sdc1 from /dev/md6 + + [root@localhost ~]# mdadm -D /dev/md6 + /dev/md6: + Version : 1.2 + Creation Time : Sun Aug 25 16:34:36 2019 + Raid Level : raid6 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 4 + Total Devices : 4 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:43:43 2019 + State : clean + Active Devices : 4 + Working Devices : 4 + Failed Devices : 0 + Spare Devices : 0 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:6 (local to host localhost) + UUID : 7c3d15a2:4066f2c6:742f3e4c:82aae1bb + Events : 47 + + Number Major Minor RaidDevice State + 5 8 97 0 active sync /dev/sdg1 + 4 8 81 1 active sync /dev/sdf1 + 2 8 49 2 active sync /dev/sdd1 + 3 8 65 3 active sync /dev/sde1 + ``` +- 重新添加热备份盘。 + ``` + [root@localhost ~]# mdadm -a /dev/md6 /dev/sd{b,c}1 + mdadm: added /dev/sdb1 + mdadm: added /dev/sdc1 + + [root@localhost ~]# mdadm -D /dev/md6 + /dev/md6: + Version : 1.2 + Creation Time : Sun Aug 25 16:34:36 2019 + Raid Level : raid6 + Array Size : 41906176 (39.96 GiB 42.91 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 4 + Total Devices : 6 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:44:01 2019 + State : clean + Active Devices : 4 + Working Devices : 6 + Failed Devices : 0 + Spare Devices : 2 + + Layout : left-symmetric + Chunk Size : 512K + + Consistency Policy : resync + + Name : localhost:6 (local to host localhost) + UUID : 7c3d15a2:4066f2c6:742f3e4c:82aae1bb + Events : 49 + + Number Major Minor RaidDevice State + 5 8 97 0 active sync /dev/sdg1 + 4 8 81 1 active sync /dev/sdf1 + 2 8 49 2 active sync /dev/sdd1 + 3 8 65 3 active sync /dev/sde1 + + 6 8 17 - spare /dev/sdb1 + 7 8 33 - spare /dev/sdc1 + ``` + +# RAID10 +- RAID10是先将数据进行镜像操作,然后再对数据进行分组,RAID1在这里就是一个冗余的备份阵列,而RAID0则负责数据的读写阵列。至少要四块盘,两两组合做RAID1,然后做RAID0,RAID10对存储容量的利用率和RAID1一样低,只有50%。Raid10方案造成了50%的磁盘浪费,但是它提供了200%的速度和单磁盘损坏的数据安全性,并且当同时损坏的磁盘不在同一RAID1中,就能保证数据安全性,RAID10能提供比RAID5更好的性能。这种新结构的可扩充性不好,使用此方案比较昂贵 +![](/img/raid10.jpg) +- 实验: RAID10创建,格式化,挂载使用,故障模拟,重新添加热备份 +- 添加4块20G的硬盘,分区,类型ID为fd。 + ``` + [root@localhost ~]# fdisk -l | grep raid + /dev/sdb1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdc1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sdd1 2048 41943039 20970496 fd Linux raid autodetect + /dev/sde1 2048 41943039 20970496 fd Linux raid autodetect + ``` +- 创建两个RAID1,不添加热备份盘。 + ``` + [root@localhost ~]# mdadm -C -v /dev/md101 -l1 -n2 /dev/sd{b,c}1 + mdadm: Note: this array has metadata at the start and + may not be suitable as a boot device. If you plan to + store '/boot' on this device please ensure that + your boot-loader understands md/v1.x metadata, or use + --metadata=0.90 + mdadm: size set to 20953088K + Continue creating array? y + mdadm: Fail create md101 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md101 started. + + [root@localhost ~]# mdadm -C -v /dev/md102 -l1 -n2 /dev/sd{d,e}1 + mdadm: Note: this array has metadata at the start and + may not be suitable as a boot device. If you plan to + store '/boot' on this device please ensure that + your boot-loader understands md/v1.x metadata, or use + --metadata=0.90 + mdadm: size set to 20953088K + Continue creating array? y + mdadm: Fail create md102 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md102 started. + ``` +- 查看raidstat状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] + md102 : active raid1 sde1[1] sdd1[0] + 20953088 blocks super 1.2 [2/2] [UU] + [=========>...........] resync = 48.4% (10148224/20953088) finish=0.8min speed=200056K/sec + + md101 : active raid1 sdc1[1] sdb1[0] + 20953088 blocks super 1.2 [2/2] [UU] + [=============>.......] resync = 69.6% (14604672/20953088) finish=0.5min speed=200052K/sec + + unused devices: + + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] + md102 : active raid1 sde1[1] sdd1[0] + 20953088 blocks super 1.2 [2/2] [UU] + + md101 : active raid1 sdc1[1] sdb1[0] + 20953088 blocks super 1.2 [2/2] [UU] + + unused devices: + ``` +- 查看两个RAID1的详细信息。 + ``` + [root@localhost ~]# mdadm -D /dev/md101 + /dev/md101: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:00 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:53:58 2019 + State : clean, resyncing + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Resync Status : 62% complete + + Name : localhost:101 (local to host localhost) + UUID : 80bb4fc5:1a628936:275ba828:17f23330 + Events : 9 + + Number Major Minor RaidDevice State + 0 8 17 0 active sync /dev/sdb1 + 1 8 33 1 active sync /dev/sdc1 + + [root@localhost ~]# mdadm -D /dev/md102 + /dev/md102: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:23 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:54:02 2019 + State : clean, resyncing + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Resync Status : 42% complete + + Name : localhost:102 (local to host localhost) + UUID : 38abac72:74fa8a53:3a21b5e4:01ae64cd + Events : 6 + + Number Major Minor RaidDevice State + 0 8 49 0 active sync /dev/sdd1 + 1 8 65 1 active sync /dev/sde1 + ``` +- 创建RAID10。 + ``` + [root@localhost ~]# mdadm -C -v /dev/md10 -l0 -n2 /dev/md10{1,2} + mdadm: chunk size defaults to 512K + mdadm: Fail create md10 when using /sys/module/md_mod/parameters/new_array + mdadm: Defaulting to version 1.2 metadata + mdadm: array /dev/md10 started. + ``` +- 查看raidstat状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] [raid0] + md10 : active raid0 md102[1] md101[0] + 41871360 blocks super 1.2 512k chunks + + md102 : active raid1 sde1[1] sdd1[0] + 20953088 blocks super 1.2 [2/2] [UU] + + md101 : active raid1 sdc1[1] sdb1[0] + 20953088 blocks super 1.2 [2/2] [UU] + + unused devices: + ``` +- 查看RAID10的详细信息。 + ``` + [root@localhost ~]# mdadm -D /dev/md10 + /dev/md10: + Version : 1.2 + Creation Time : Sun Aug 25 16:56:08 2019 + Raid Level : raid0 + Array Size : 41871360 (39.93 GiB 42.88 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:56:08 2019 + State : clean + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Chunk Size : 512K + + Consistency Policy : none + + Name : localhost:10 (local to host localhost) + UUID : 23c6abac:b131a049:db25cac8:686fb045 + Events : 0 + + Number Major Minor RaidDevice State + 0 9 101 0 active sync /dev/md101 + 1 9 102 1 active sync /dev/md102 + ``` +- 格式化。 + ``` + [root@localhost ~]# mkfs.xfs /dev/md10 + meta-data=/dev/md10 isize=512 agcount=16, agsize=654208 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=1 finobt=0, sparse=0 + data = bsize=4096 blocks=10467328, imaxpct=25 + = sunit=128 swidth=256 blks + naming =version 2 bsize=4096 ascii-ci=0 ftype=1 + log =internal log bsize=4096 blocks=5112, version=2 + = sectsz=512 sunit=8 blks, lazy-count=1 + realtime =none extsz=4096 blocks=0, rtextents=0 + ``` +- 挂载使用。 + ``` + [root@localhost ~]# mkdir /mnt/md10 + [root@localhost ~]# mount /dev/md10 /mnt/md10/ + [root@localhost ~]# df -hT + Filesystem Type Size Used Avail Use% Mounted on + /dev/mapper/centos-root xfs 17G 1014M 16G 6% / + devtmpfs devtmpfs 901M 0 901M 0% /dev + tmpfs tmpfs 912M 0 912M 0% /dev/shm + tmpfs tmpfs 912M 8.7M 903M 1% /run + tmpfs tmpfs 912M 0 912M 0% /sys/fs/cgroup + /dev/sda1 xfs 1014M 143M 872M 15% /boot + tmpfs tmpfs 183M 0 183M 0% /run/user/0 + /dev/md10 xfs 40G 33M 40G 1% /mnt/md10 + ``` +- 创建测试文件。 + ``` + [root@localhost ~]# touch /mnt/md10/test{1..9}.txt + [root@localhost ~]# ls /mnt/md10/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 故障模拟。 + ``` + [root@localhost ~]# mdadm -f /dev/md101 /dev/sdb1 + mdadm: set /dev/sdb1 faulty in /dev/md101 + [root@localhost ~]# mdadm -f /dev/md102 /dev/sdd1 + mdadm: set /dev/sdd1 faulty in /dev/md102 + ``` +- 查看测试文件。 + ``` + [root@localhost ~]# ls /mnt/md10/ + test1.txt test2.txt test3.txt test4.txt test5.txt test6.txt test7.txt test8.txt test9.txt + ``` +- 查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] [raid0] + md10 : active raid0 md102[1] md101[0] + 41871360 blocks super 1.2 512k chunks + + md102 : active raid1 sde1[1] sdd1[0](F) + 20953088 blocks super 1.2 [2/1] [_U] + + md101 : active raid1 sdc1[1] sdb1[0](F) + 20953088 blocks super 1.2 [2/1] [_U] + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md101 + /dev/md101: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:00 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 17:01:11 2019 + State : clean, degraded + Active Devices : 1 + Working Devices : 1 + Failed Devices : 1 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:101 (local to host localhost) + UUID : 80bb4fc5:1a628936:275ba828:17f23330 + Events : 23 + + Number Major Minor RaidDevice State + - 0 0 0 removed + 1 8 33 1 active sync /dev/sdc1 + + 0 8 17 - faulty /dev/sdb1 + + [root@localhost ~]# mdadm -D /dev/md102 + /dev/md102: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:23 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 17:00:43 2019 + State : clean, degraded + Active Devices : 1 + Working Devices : 1 + Failed Devices : 1 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:102 (local to host localhost) + UUID : 38abac72:74fa8a53:3a21b5e4:01ae64cd + Events : 19 + + Number Major Minor RaidDevice State + - 0 0 0 removed + 1 8 65 1 active sync /dev/sde1 + + 0 8 49 - faulty /dev/sdd1 + + [root@localhost ~]# mdadm -D /dev/md10 + /dev/md10: + Version : 1.2 + Creation Time : Sun Aug 25 16:56:08 2019 + Raid Level : raid0 + Array Size : 41871360 (39.93 GiB 42.88 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 16:56:08 2019 + State : clean + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Chunk Size : 512K + + Consistency Policy : none + + Name : localhost:10 (local to host localhost) + UUID : 23c6abac:b131a049:db25cac8:686fb045 + Events : 0 + + Number Major Minor RaidDevice State + 0 9 101 0 active sync /dev/md101 + 1 9 102 1 active sync /dev/md102 + ``` +- 移除损坏的磁盘。 + ``` + [root@localhost ~]# mdadm -r /dev/md101 /dev/sdb1 + mdadm: hot removed /dev/sdb1 from /dev/md101 + [root@localhost ~]# mdadm -r /dev/md102 /dev/sdd1 + mdadm: hot removed /dev/sdd1 from /dev/md102 + + [root@localhost ~]# mdadm -D /dev/md101 + /dev/md101: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:00 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 1 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 17:04:59 2019 + State : clean, degraded + Active Devices : 1 + Working Devices : 1 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:101 (local to host localhost) + UUID : 80bb4fc5:1a628936:275ba828:17f23330 + Events : 26 + + Number Major Minor RaidDevice State + - 0 0 0 removed + 1 8 33 1 active sync /dev/sdc1 + + [root@localhost ~]# mdadm -D /dev/md102 + /dev/md102: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:23 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 1 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 17:05:07 2019 + State : clean, degraded + Active Devices : 1 + Working Devices : 1 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:102 (local to host localhost) + UUID : 38abac72:74fa8a53:3a21b5e4:01ae64cd + Events : 20 + + Number Major Minor RaidDevice State + - 0 0 0 removed + 1 8 65 1 active sync /dev/sde1 + ``` +- 重新添加热备份盘。 + ``` + [root@localhost ~]# mdadm -a /dev/md101 /dev/sdb1 + mdadm: added /dev/sdb1 + [root@localhost ~]# mdadm -a /dev/md102 /dev/sdd1 + mdadm: added /dev/sdd1 + ``` +- 再次查看状态。 + ``` + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] [raid0] + md10 : active raid0 md102[1] md101[0] + 41871360 blocks super 1.2 512k chunks + + md102 : active raid1 sdd1[2] sde1[1] + 20953088 blocks super 1.2 [2/1] [_U] + [====>................] recovery = 23.8% (5000704/20953088) finish=1.2min speed=208362K/sec + + md101 : active raid1 sdb1[2] sdc1[1] + 20953088 blocks super 1.2 [2/1] [_U] + [======>..............] recovery = 32.0% (6712448/20953088) finish=1.1min speed=203407K/sec + + unused devices: + + [root@localhost ~]# cat /proc/mdstat + Personalities : [raid1] [raid0] + md10 : active raid0 md102[1] md101[0] + 41871360 blocks super 1.2 512k chunks + + md102 : active raid1 sdd1[2] sde1[1] + 20953088 blocks super 1.2 [2/2] [UU] + + md101 : active raid1 sdb1[2] sdc1[1] + 20953088 blocks super 1.2 [2/2] [UU] + + unused devices: + + [root@localhost ~]# mdadm -D /dev/md101 + /dev/md101: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:00 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 17:07:28 2019 + State : clean + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:101 (local to host localhost) + UUID : 80bb4fc5:1a628936:275ba828:17f23330 + Events : 45 + + Number Major Minor RaidDevice State + 2 8 17 0 active sync /dev/sdb1 + 1 8 33 1 active sync /dev/sdc1 + + [root@localhost ~]# mdadm -D /dev/md102 + /dev/md102: + Version : 1.2 + Creation Time : Sun Aug 25 16:53:23 2019 + Raid Level : raid1 + Array Size : 20953088 (19.98 GiB 21.46 GB) + Used Dev Size : 20953088 (19.98 GiB 21.46 GB) + Raid Devices : 2 + Total Devices : 2 + Persistence : Superblock is persistent + + Update Time : Sun Aug 25 17:07:36 2019 + State : clean + Active Devices : 2 + Working Devices : 2 + Failed Devices : 0 + Spare Devices : 0 + + Consistency Policy : resync + + Name : localhost:102 (local to host localhost) + UUID : 38abac72:74fa8a53:3a21b5e4:01ae64cd + Events : 39 + + Number Major Minor RaidDevice State + 2 8 49 0 active sync /dev/sdd1 + 1 8 65 1 active sync /dev/sde1 + ``` + +# 常用 RAID 比较 +名称 | 硬盘数量 | 容量/利用率 | 读性能 | 写性能 | 数据冗余 +---- | ---- | ---- | ---- | ---- | ---- +RAID0 | N | N块总和 | N倍 | N倍 | 无,一个故障,丢失所有数据 +RAID1 | N(偶数) | 50% | ↑ | ↓ | 写两个设备,允许一个故障 +RAID5 | N≥3 | (N-1)/N | ↑↑ | ↓ | 计算校验,允许一个故障 +RAID6 | N≥4 | (N-2)/N | ↑↑ | ↓↓ | 双重校验,允许两个故障 +RAID10 | N(偶数,N≥4) | 50% | (N/2)倍 | (N/2)倍 | 允许基组中的磁盘各损坏一个 + + +# 参考 +- [https://www.linuxidc.com/Linux/2019-08/160345.htm](https://www.linuxidc.com/Linux/2019-08/160345.htm) + diff --git a/content/post/sqoop.md b/content/post/sqoop.md new file mode 100644 index 0000000..dd258c8 --- /dev/null +++ b/content/post/sqoop.md @@ -0,0 +1,59 @@ +--- +title: "Sqoop 笔记" +date: 2019-10-30T11:35:48+08:00 +lastmod: 2019-10-30T11:35:48+08:00 +tags: ["sqoop", "hive", "mysql"] +categories: ["hadoop"] +--- + +# 把 mysql 表数据导入 hive 中 +- 命令格式 + ```bash + sqoop import {参数列表} + ``` +- 默认在 hive 中创建指定表,如果该表已存在,则把 mysql 表数据追加到该表中,常规参数如下 + ```bash + --connect jdbc:mysql://_mysql_server_:3306/_mysql_dbname_ + --username _mysql_user_ + --password _mysql_password_ + --table _mysql_tablename_ + --hive-import + --hive-database _hive_dbname_ + --hive-table _hive_tablename_ + --hive-drop-import-delims + -m 1 + ``` +- 如果该表已存在,则报错退出,参数如下 + ```bash + --create-hive-table + ``` +- 如果该表已存在,则覆盖重新导入,参数如下 + ```bash + --hive-overwrite + ``` +- 导入增量数据,参数如下 + ```bash + --incremental append + --check-column _mysql_tablecolumn_ + --last-value _last_value_ + ``` + +# 把 hive 表数据导入 mysql 中 +- 命令格式 + ```bash + sqoop export {参数列表} + ``` +- mysql 中需提前创建好待导入的表,常规参数如下 + ```bash + --connect jdbc:mysql://_mysql_server_:3306/_mysql_dbname_ + --username _mysql_username_ + --password _mysql_password_ + --table _mysql_tablename_ + --hcatalog-database _hive_dbname_ + --hcatalog-table _hive_tablename_ + ``` +- 只导出 hive 表中的部分的指定(部分)字段,参数如下 + ```bash + --columns "_column_name1_,_column_name2_,_column_name3_" + ``` + diff --git a/content/post/ssh.md b/content/post/ssh.md new file mode 100644 index 0000000..d5abe82 --- /dev/null +++ b/content/post/ssh.md @@ -0,0 +1,61 @@ +--- +title: "ssh 命令" +date: 2019-10-29T21:10:48+08:00 +lastmod: 2019-10-29T21:10:48+08:00 +keywords: [] +tags: ["ssh", "隧道", "转发"] +categories: ["shell"] +--- + +# sshd 常规设置 +- 禁用 root 远程登陆 + ``` + PermitRootLogin no + ``` +- 只允许私钥登陆 + ``` + PubkeyAuthentication yes + PasswordAuthentication no + ``` +- 开启端口转发功能 + ``` + AllowTcpForwarding yes + GatewayPorts yes + X11Forwarding yes + TCPKeepAlive yes + ClientAliveInterval 60 + ClientAliveCountMax 3 + ``` + +# ssh 参数 +- -f 后台运行 +- -N 仅作端口转发,不执行任何命令 +- -g 绑定端口到全部网卡 + +# 本地定向转发 +- ssh-client 不可达 remote-host, ssh-server 可达 remote-host, ssh 隧道映射 ssh-client 指定端口到 remote-host 指定端口 +- 在 ssh-client 执行 + ```bash + ssh -f -N -g \ + -L [:]:: \ + username@ + ``` + +# 远程定向转发 +- ssh-client 可达 remote-host, ssh-server 不可达 remote-host, ssh 隧道映射 ssh-server 指定端口到 remote-host 指定端口 +- 在 ssh-client 执行 + ```bash + ssh -f -N -g \ + -R [:]:: \ + username@ + ``` + +# 动态转发 +- SOCKS5 代理 +- 在 ssh-client 执行 + ```bash + ssh -f -N -g \ + -D \ + username@ + ``` + diff --git a/content/post/systemd.md b/content/post/systemd.md new file mode 100644 index 0000000..5e6e8e8 --- /dev/null +++ b/content/post/systemd.md @@ -0,0 +1,302 @@ +--- +title: "Systemd 笔记" +date: 2019-10-30T11:32:04+08:00 +lastmod: 2019-10-30T11:32:04+08:00 +tags: ["systemd", "systemctl", "journalctl"] +categories: ["OS"] +--- + +# 系统资源(unit)分类 +Unit | 描述 +---- | ---- +Service | 系统服务,默认 +Target | 多个 unit 构成的一个组 +Device | 硬件设备 +Mount | 文件系统的挂载点 +Automount | 自动挂载点 +Path | 文件或路径 +Scope | 不是由 systemd 启动的外部进程 +Slice | 进程组 +Snapshot | systemd 快照,可以切回某个快照 +Socket | 进程间通信的 socket +Swap | swap 文件 +Timer | 定时器 + +# Target 与传统 runlevel 对应关系 +传统运行等级 | 新版 target 命名 | 链接名 +---- | ---- | ---- +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 + +# systemctl 命令 +- 查看版本 + ```bash + systemctl --version + ``` +- 系统操作 + ```bash + systemctl reboot # 重启 + systemctl poweroff # 关闭系统,切断电源 + systemctl halt # 停止 CPU 工作 + systemctl suspend # 暂停系统 + systemctl hibernate # 让系统进入休眠状态 + systemctl hybrid-sleep # 让系统进入交互式休眠状态 + systemctl rescue # 启动进入救援状态(单用户状态) + systemctl status # 显示系统状态 + ``` +- 查看全部 Unit + ```bash + systemctl list-unit-files # 列出所有 Unit 配置文件 + # enabled 已建立启动链接 + # disabled 没建立启动链接 + # static 该配置文件没有[Install]部分,无法执行,只能作为其他 Unit 配置文件的依赖 + # masked 该配置文件被禁止建立启动链接 + systemctl list-unit-files --type=service # 列出指定类型的 Unit 配置文件 + systemctl list-units # 列出正在运行的 Unit + systemctl list-units --all # 列出所有 Unit,包括没有找到配置文件的或者启动失败的 + systemctl list-units --all --state=inactive # 列出所有没有运行的 Unit + systemctl list-units --failed # 列出所有加载失败的 Unit + systemctl list-units --type=service # 列出所有正在运行的、类型为 service 的 Unit + ``` +- 查看一个 Unit + ```bash + systemctl list-dependencies nginx.service # 显示一个 Unit 的所有依赖 + systemctl list-dependencies --all nginx.service # 显示一个 Unit 的所有依赖,展开 target + systemctl cat nginx.service # 查看一个 Unit 配置文件 + systemctl show httpd.service # 显示一个 Unit 的所有底层参数 + systemctl show -p CPUShares httpd.service # 显示一个 Unit 的指定属性的值 + systemctl status nginx.service # 显示单个 Unit 的状态 + systemctl -H root@ssh_server status nginx.service # 显示远程主机的某个 Unit 的状态 (ssh 连接) + systemctl is-active nginx.service # 显示某个 Unit 是否正在运行 + systemctl is-failed nginx.service # 显示某个 Unit 是否处于启动失败状态 + systemctl is-enabled nginx.service # 显示某个 Unit 服务是否建立了启动链接 + ``` +- 操作一个 Unit + ```bash + systemctl reload apache.service # 重新加载一个服务的配置文件 + systemctl set-property httpd.service CPUShares=500 # 设置一个服务的指定属性 + systemctl start apache.service # 立即启动一个服务 + systemctl stop apache.service # 立即停止一个服务 + systemctl restart apache.service # 重启一个服务 + systemctl kill apache.service # 杀死一个服务的所有子进程 + systemctl enable nginx.service # 建立启动链接,激活开机启动 + systemctl disable nginx.service # 删除启动链接,取消开机启动 + ``` +- 重新加载全部修改过的 Unit 配置文件 + ```bash + systemctl daemon-reload + ``` + +# systemd-analyze 命令 +- 查看启动耗时 + ```bash + systemd-analyze + ``` +- 查看每个服务的启动耗时 + ```bash + systemd-analyze blame + ``` +- 显示瀑布状的启动过程流 + ```bash + systemd-analyze critical-chain + ``` +- 显示指定服务的启动流 + ```bash + systemd-analyze critical-chain atd.service + ``` + +# journalctl 命令 +- 查看所有日志(默认只保存本次启动的日志) + ```bash + journalctl + ``` +- 查看内核日志(不显示应用日志) + ```bash + journalctl -k + ``` +- 查看系统启动日志 + ```bash + journalctl -b # 本次启动的日志 + journalctl -b -0 # 本次启动的日志 + journalctl -b -1 # 查看上一次启动的日志(需更改设置) + ``` +- 查看指定时间的日志 + ```bash + journalctl --since="2012-10-30 18:17:16" + journalctl --since "20 min ago" + journalctl --since yesterday + journalctl --since "2018-11-11" --until "2019-01-01 01:00" + journalctl --since 09:00 --until "1 hour ago" + ``` +- 显示尾部的最新日志 + ```bash + journalctl -n # 最近 10 行 + journalctl -n 20 # 最近 20 行日志 + ``` +- 实时滚动显示最新日志 + ```bash + journalctl -f + ``` +- 查看指定服务的日志 + ```bash + journalctl /usr/lib/systemd/systemd + ``` +- 查看指定进程的日志 + ```bash + journalctl _PID=1 + ``` +- 查看某个路径的脚本的日志 + ```bash + journalctl /usr/bin/bash + ``` +- 查看指定用户的日志 + ```bash + journalctl _UID=33 --since today + ``` +- 查看某个 Unit 的日志 + ```bash + journalctl -u nginx.service + journalctl -u nginx.service --since today + ``` +- 实时滚动显示某个 Unit 的最新日志 + ```bash + journalctl -u nginx.service -f + ``` +- 合并显示多个 Unit 的日志 + ```bash + 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 + ```bash + journalctl -p err -b + ``` +- 日志默认分页输出,--no-pager 改为正常的标准输出 + ```bash + journalctl --no-pager + ``` +- 以 JSON 格式(单行)输出 + ```bash + journalctl -b -u nginx.service -o json + ``` +- 以 JSON 格式(多行)输出,可读性更好 + ```bash + journalctl -b -u nginx.serviceqq -o json-pretty + ``` +- 显示日志占据的硬盘空间 + ```bash + journalctl --disk-usage + ``` +- 指定日志文件占据的最大空间 + ```bash + journalctl --vacuum-size=1G + ``` +- 指定日志文件保存多久 + ```bash + journalctl --vacuum-time=1years + ``` + +# 其他相似命令 +- hostnamectl +- localectl +- timedatectl +- loginctl + +# 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 运行必须满足的条件,否则会报启动失败 +### [Service] +- WorkingDirectory: 设置进程的工作目录 + - 特殊值 "~" 表示 User= 用户的家目录 + - 设为一个以 RootDirectory= 为基准的绝对路径 + - 例如当 RootDirectory=/sysroot 并且 WorkingDirectory=/work/dir 时,实际的工作目录将是 /sysroot/work/dir + - 当 systemd 作为系统实例运行时,此选项的默认值是 / + - 当 systemd 作为用户实例运行时,此选项的默认值是对应用户的家目录 + - 给目录加上 "-" 前缀,表示即使此目录不存在,也不算致命错误 + - 未设置 RootDirectory=/RootImage= 选项,那么为 WorkingDirectory= 设置的绝对路径 将以主机(或容器)的根目录(也就是运行 systemd 的系统根目录)为基准 +- RootDirectory: 设置以 chroot(2) 方式执行进程时的根目录 + - 必须设为一个以主机(或容器)的根目录(也就是运行 systemd 的系统根目录)为基准的绝对路径 + - 设置了此选项,必须确保进程及其辅助文件在 chroot() 监狱中确实可用 +- User/Group: 设置进程在执行时使用的用户与组 +- TimeoutSec: 定义 Systemd 停止当前服务之前等待的秒数 +- Environment: 指定环境变量 +- EnvironmentFile: 环境参数文件,内部 key=value 格式,在当前文件中使用 $key 获取 value +- ExecStart: 启动时执行的命令 +- ExecReload: 重启服务时执行的命令 +- ExecStop: 停止服务时执行的命令 +- ExecStartPre: 启动服务之前执行的命令 +- ExecStartPost: 启动服务之后执行的命令 +- ExecStopPost: 停止服务之后执行的命令 +- "-" 配置项前的减号"-"表示"抑制错误",发生错误时不影响后续命令执行 +- Type + - simple: 默认,ExecStart 启动的进程就是主进程 + - forking: ExecStart 启动时 fork 子进程,之后该父进程退出,子进程成为主进程 + - oneshot: 类似 Simple,只执行一次 + - dbus: 类似 Simple,等待 D-Bus 信号启动 + - notify: 类似 Simple,启动完成后发出通知信号 + - idle: 类似 Simple,等到其他任务执行完成后才会启动该服务 +- RemainAfterExit=yes: 进程退出后服务保持运行 +- KillMode + - control-group: 默认,杀掉所有子进程 + - process: 只杀主进程 + - mixed: 主进程收到 SIGTERM 信号,子进程收到 SIGKILL 信号 + - none: 不杀进程,只执行服务的 stop 命令 +- Restart + - no: 默认,退出后不重启 + - on-success: 正常退出时重启 + - on-failure: 非正常退出、被信号终止或超时重启 + - on-abnormal: 被信号终止和超时重启 + - on-abort: 收到没有捕捉到的信号终止时重启 + - on-watchdog: 超时退出,才会重启 + - always: 不管是什么退出原因,总是重启 +- RestartSec: 系统重启前等待的秒数 +### [Install] +- WantedBy: 它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants 后缀构成的子目录中 +- RequiredBy: 它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入/etc/systemd/system目录下面以 Target 名 + .required 后缀构成的子目录中 +- Alias: 当前 Unit 可用于启动的别名 +- Also: 当前 Unit 激活(enable)时,会被同时激活的其他 Unit + +# Unit 配置文件占位符 +占位符 | 作用 | 描述 +---- | ---- | ---- +%n | 完整的服务名称 | +%N | 不转义的完整服务名称 | +%p | 前缀名 | 对于实例化的服务,这是前@前面的部分,对于其它的服务,是指去掉后缀(即类型)的部分 +%P | 不转义的前缀名 | +%i | 实例名称 | 对于实例化的服务,这是指 @和后缀之间的部分 +%I | 不转义的实例名 | +%f | 不转义的文件名 | 这可以不转义的实例名(如果可用)或前缀名,带有/前缀 +%c | 服务的控制组路径 | +%r | systemd 的根控制组路径 | +%R | systemd 的根控制组路径的父目录 | +%t | 运行时 Socket 目录 | 这可以是 /run (系统管理器) 或 $XDG_RUNTIME_DIR (用户管理器) +%u | 用户名 | 这是服务配置的用户或systemd运行实例的用户(如果没有配置的话) +%U | 用户 UID | 这是服务配置的用户UID或systemd运行实例的用户UID(如果没有配置的话) +%h | 用户家目录 | 这是服务配置的用户家目录或systemd运行实例的用户家目录(如果没有配置的话) +%s | 用户Shell | 这是服务配置的用户shell或systemd运行实例的用户shell(如果没有配置的话) +%m | 机器 ID | 运行系统的机器 ID,格式是一个字符串 +%b | 启动 ID | 运行系统的启动 ID,格式是一个字符串 +%H | 主机名 | 运行系统的主机名 +%% | 转义 % | 一个单百分号 + diff --git a/content/post/tomcat.md b/content/post/tomcat.md new file mode 100644 index 0000000..3660670 --- /dev/null +++ b/content/post/tomcat.md @@ -0,0 +1,67 @@ +--- +title: "Tomcat 笔记" +date: 2019-10-29T21:19:44+08:00 +lastmod: 2019-10-29T21:19:44+08:00 +keywords: [] +tags: ["tomcat", "jdk", "java"] +categories: ["web"] +--- + +# 部署 jre8 +- 下载 oracle jre8,[下载界面](https://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html) +- 解压 + ```bash + tar zxf jre-8u221-linux-x64.tar.gz -C /opt/ + cd /opt && mv jre-8u221-linux jre + ``` +- 创建 /etc/profile.d/jre.sh,内容如下 + ```bash + export JAVA_HOME=/opt/jre + export PATH=$JAVA_HOME/bin:$PATH + ``` + +# 部署 tomcat8 +- 下载 tomcat + ```bash + curl -O http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.45/bin/apache-tomcat-8.5.45.tar.gz + ``` +- 解压 + ```bash + tar zxf apache-tomcat-8.5.45.tar.gz -C /opt/ + cd /opt/ && mv apache-tomcat-8.5.45 tomcat + ``` +- 创建文件 /opt/tomcat/bin/setenv.sh,内容如下 + ```bash + export JAVA_HOME=/opt/jre + export JAVA_OPTS="$JAVA_OPTS -server -Xms8192M -Xmx8192M -Xmn3072M -Xss512K -Djava.security.egd=file:/dev/./urandom -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Djava.awt.headless=true -Ddruid.registerToSysProperty=true" + # -Djava.library.path=/opt/tomcat/libs/tcnative # tcnative 库位置 + #export CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=10087" + export CATALINA_PID="/var/run/tomcat.pid" + ``` +- 创建 /usr/lib/systemd/system/tomcat.service 文件,内容如下 + ```bash + [Unit] + Description=The Apache Tomcat + After=network.target + + [Service] + Type=forking + PIDFile=/var/run/tomcat.pid + ExecStart=/opt/tomcat/bin/catalina.sh start + ExecStop=/opt/tomcat/bin/catalina.sh stop + SuccessExitStatus=143 + + [Install] + WantedBy=multi-user.target + ``` +- 启动 tomcat 服务 + ```bash + systemctl daemon-reload + systemctl start tomcat + ``` + +# 验证码字体 +```bash +yum install dejagnu dejavu-sans-mono-fonts dejavu +``` + diff --git a/content/post/websocket.md b/content/post/websocket.md new file mode 100644 index 0000000..a903c62 --- /dev/null +++ b/content/post/websocket.md @@ -0,0 +1,122 @@ +--- +title: "Websocket 查看实时日志" +date: 2019-10-30T11:43:23+08:00 +lastmod: 2019-10-30T11:43:23+08:00 +tags: ["websocket"] +categories: ["web"] +--- + +# 操作系统 +- CentOS7 + +# 下载 websocketd +- [websocketd-0.3.0-linux_amd64.zip](https://github.com/joewalnes/websocketd/releases/download/v0.3.0/websocketd-0.3.0-linux_amd64.zip) + +# 安装 nc 命令 +```bash +yum install nmap-ncat +``` + +# 创建监听脚本 +```bash +cat > cmd.sh <<-END +#!/bin/bash +pkill -x nc +while :; do + nc -nkl 10088 + sleep 1 +done +END +``` + +# 创建 log.html +```html + + + + + + +
实时日志
+
    + + + + +``` + +# 文件部署位置 +``` +websocketd/ +├── [-rwxr-xr-x] cmd.sh +├── [drwxr-xr-x] websocket +│ └── [-rw-r--r--] log.html +└── [-rwxr-xr-x] websocketd +``` + +# 启动 websocketd +```bash +cd websocketd +./websocketd --port=8008 --staticdir=. ./cmd.sh +``` + +# 在浏览器中打开日志浏览页面 +- http://{websocket-server}:8008/websocket/log.html + +# 在其他应用服务器,传输实时日志 +```bash +tail -f /tomcat/logs/catalina.out | nc -n {websocket-server} 10088 +``` + +# 关闭实时日志 +- 在目标服务器中 kill nc + ```bash + pkill -x nc + ``` +- 在 websocket server 中 kill nc + ```bash + pkill -x nc + ``` +- 直接刷新浏览器的日志浏览页面 + diff --git a/content/post/xargs.md b/content/post/xargs.md new file mode 100644 index 0000000..147ccb7 --- /dev/null +++ b/content/post/xargs.md @@ -0,0 +1,41 @@ +--- +title: "xargs 命令" +date: 2019-10-29T18:43:36+08:00 +lastmod: 2019-10-29T18:46:00+08:00 +keywords: [] +tags: ["shell", "xargs"] +categories: ["shell"] +--- + +# 基本使用 +- 把标准输入转换成命令行参数,传递给 xargs 后的命令使用 + ```bash + echo 'one two three' | xargs mkdir + # 等于 + mkdir one two three + ``` +- 默认命令 echo + ```bash + seq 1 8 | xargs + # 等于 + seq 1 8 | xargs echo + ``` +- -d 指定分隔符,默认是换行和空格 + ```bash + echo -e "a\tb\tc" | xargs -d "\t" echo + ``` +- -p 打印出要执行的命令,并询问用户是否执行 +- -t 打印出要执行的命令,直接执行 +- -0 指定 null 当作分隔符,一般用于分隔包含空格的字符串列表 + ```bash + find /path -type f -print0 | xargs -0 rm + ``` +- -L 指定标准输入中的多少行作为一个参数 +- -n 一行中可能包含空格分隔的多项,该参数指定每次将多少项作为命令行参数 +- -I 指定每一项命令行参数的替代字符串 + ```bash + seq 1 8 | xargs -I PAT echo PAT + ``` +- -P 指定同时使用多少个进程执行命令,默认只用一个,0 表示不限制 +- --show-limits 查看系统命令行缓冲上限 + diff --git a/content/post/xtrabackup.md b/content/post/xtrabackup.md new file mode 100644 index 0000000..21a9927 --- /dev/null +++ b/content/post/xtrabackup.md @@ -0,0 +1,87 @@ +--- +title: "Xtrabackup 笔记" +date: 2019-10-30T00:54:15+08:00 +lastmod: 2019-10-30T00:54:15+08:00 +tags: ["xtrabackup", "备份", "mysql"] +categories: ["database"] +--- + +# 环境 +- CentOS7 +- MySQL 5.27.22 +- XtraBackup 2.4 + +# 安装 +- 创建 percona.repo 文件,内容如下 + ```ini + [percona-release-x86_64] + name = Percona Original release/x86_64 YUM repository + #baseurl = http://repo.percona.com/percona/yum/release/$releasever/RPMS/x86_64 + baseurl = https://mirrors.cloud.tencent.com/percona/percona/yum/release/$releasever/RPMS/x86_64 + enabled = 1 + gpgcheck = 0 + gpgkey = file:///etc/pki/rpm-gpg/PERCONA-PACKAGING-KEY + [percona-release-noarch] + name = Percona Original release/noarch YUM repository + #baseurl = http://repo.percona.com/percona/yum/release/$releasever/RPMS/noarch + baseurl = https://mirrors.cloud.tencent.com/percona/percona/yum/release/$releasever/RPMS/noarch + enabled = 1 + gpgcheck = 0 + gpgkey = file:///etc/pki/rpm-gpg/PERCONA-PACKAGING-KEY + [percona-release-sources] + name = Percona Original release/sources YUM repository + #baseurl = http://repo.percona.com/percona/yum/release/$releasever/SRPMS + baseurl = https://mirrors.cloud.tencent.com/percona/percona/yum/release/$releasever/SRPMS + enabled = 0 + gpgcheck = 0 + gpgkey = file:///etc/pki/rpm-gpg/PERCONA-PACKAGING-KEY + ``` +- 安装 + ```bash + yum clean all + yum makecache fast + yum install percona-xtrabackup-24 + ``` + +# 创建数据库备份用户 +```sql +create user percona@localhost identified by '123456'; +grant reload, lock tables, process, replication client on *.* to percona@localhost; +flush privileges; +``` + +# 全量备份 +- 按天创建全量备份目录 + ```bash + xtrabackup --backup --rsync --parallel=4 --user=percona --password=123456 \ + --target-dir=/backup/$(date +mysql.%Y%m%d) + # --databases='db1 db2.t3', 只导出 db1 数据库和 db2 中的 t3 表 + # --databases-exclude='db4 db5.t6', 不导出 db4 数据库和 db5 中的 t6 表,优先级高于 --database + # --databases-file=/path/to/filename, 指定包含要备份的数据库的文件名,每行一个 db 或者 db.tb + # --tables='db1.t2 db3.t4', 只导出 db1 中的 t2 表和 db3 中的 t4 表 + # --tables-exclude='db5.t6 db7.t8', 不导出 db5 数据库的 t6 表和 db7 的 t8 表 + # --tables-file=/path/to/filename, 指定包含要备份的数据表的文件名,每行一个 db.tb + # --safe-slave-backup, 备份前刷新 slave 临时表,安全退出 slave sql 线程 + # --slave-info 记录 slave 读取的 master binlog 的位置 + # NFS 挂载时,需使用 sync 模式 + # 保留最近 3 天的全量备份 + ls -d mysql.* | head -n -3 | xargs rm -rf + ``` + +# 恢复数据 +- 准备一个 xtrabackup 备份目录 + ```bash + xtrabackup --prepare --use-memory=2G --target-dir=/backup/20190711 + # --use-memory=2G 允许使用 2G 内存用于 prepare 数据,默认 100M + ``` +- 停止 mysql 服务,移除数据目录 + ```bash + systemctl stop mysqld + cd /var/lib && [ -d mysql ] && mv mysql mysql_origin + ``` +- 恢复 mysql 数据 + ```bash + xtrabackup --copy-back --rsync --parallel=4 --target-dir=/backup/20190711 + chown -R mysql.mysql /var/lib/mysql + ``` + diff --git a/content/post/yum.md b/content/post/yum.md new file mode 100644 index 0000000..df3819d --- /dev/null +++ b/content/post/yum.md @@ -0,0 +1,46 @@ +--- +title: "Yum 笔记" +date: 2019-10-30T12:49:22+08:00 +lastmod: 2019-10-30T12:49:22+08:00 +tags: ["yum", "repo", "iso"] +categories: ["os"] +--- + +# CentOS 配置本地 ISO 镜像源 +### 上传 iso 文件 +- 上传 CentOS7 的 DVD iso 镜像文件到目标服务器,例如 /home/centos7.4.iso + +### 挂载 iso 文件 +- 挂载该 iso 文件到操作系统的一个空目录中,例如 /mnt/ 下 + ```bash + mount -o loop /home/centos7.4.iso /mnt/ + ``` + +### 修改软件源文件 +- 备份全部的 .repo 文件 + ```bash + cd /etc/yum.repos.d/ + ls *.repo|xargs -i mv {} {}_bak + ``` +- 创建 centos-base.repo 空文件,写入下面几行 + ``` + [base] + name=centos iso + baseurl=file:///mnt/ + enabled=1 + gpgcheck=0 + ``` + +### 重新创建 yum 缓存 +- 清空以前的 yum cache + ```bash + yum clean all + ``` +- 生成新的 yum 缓存 + ```bash + yum makecache fast + ``` + +### 其他 yum 源汇总 +- [pkgs.org](https://pkgs.org/) + diff --git a/content/post/zabbix-alert-email.md b/content/post/zabbix-alert-email.md new file mode 100644 index 0000000..9129c08 --- /dev/null +++ b/content/post/zabbix-alert-email.md @@ -0,0 +1,68 @@ +--- +title: "Zabbix 邮件告警" +date: 2019-10-29T21:23:57+08:00 +lastmod: 2019-10-29T21:23:57+08:00 +keywords: [] +tags: ["zabbix", "邮件", "告警"] +categories: ["zabbix"] +--- + +# 环境 +- CentOS7 +- sendEmail 1.56 + +# 部署 sendEmail +- 安装 sendEmail + ```bash + curl -O http://caspian.dotconf.net/menu/Software/SendEmail/sendEmail-v1.56.tar.gz + tar zxf sendEmail-v1.56.tar.gz && cp sendEmail-v1.56/sendEmail /usr/local/bin/ + ``` +- 查看 zabbix_server.conf 找到告警脚本存放目录,即 AlertScriptsPath,默该目录已存在 +- 在该目录下创建 sendemail.sh,内容如下: + ```bash + #!/bin/bash + + recipients="${1//;/ }" + subject="$2" + message="$3" + + for recipient in $recipients; do + /usr/local/bin/sendEmail \ + -f <发件人邮箱> \ + -t $recipient \ + -s <发件人邮箱的服务器> \ + -o tls=no \ + -o message-charset=utf8 \ + -xu <发件人邮箱> \ + -xp <发见人邮箱的密码> \ + -u "$subject" \ + -m "$message" \ + -l /var/log/sendEmail.log & + done + wait + ``` +- 测试,如果正常收到邮件,则表示脚本正常 + ```bash + ./sendemail.sh person1@email.com;person2@email.com;person3@email.com test_subject test_content + ``` + +# 配置 zabbix +- 登陆 zabbix web,点击“报警媒介类型”-“创建媒体类型”,打开“报警媒介类型”窗口,配置如下 + - 名称: 脚本发邮件报警 + - 类型: 脚本 + - 脚本名称: sendemail.sh #注意这里的名称与上一步创建脚本名称一致 + - 脚本参数: + - {ALERT.SENDTO} + - {ALERT.SUBJECT} + - {ALERT.MESSAGE} + - 已启用: 启用 +- 点击“添加”完成脚本媒介配置 +- 点击“管理”-“用户”-“admin”-“报警媒介”-“添加”,打开“报警媒介”窗口,配置如下 + - 类型: 脚本发邮件报警 + - 收件人: 一个或多个收件人email,用空格或分号分隔 +- 点击“添加”完成该报警媒介配置 +- 点击“配置”-“动作”-“Report problems to Zabbix administrators”-“操作”,自定义报警信息 + +# 测试 +- 触发器被触发后,收件人邮箱收到告警邮件 + diff --git a/content/post/zabbix-alert-wechat.md b/content/post/zabbix-alert-wechat.md new file mode 100644 index 0000000..51998e4 --- /dev/null +++ b/content/post/zabbix-alert-wechat.md @@ -0,0 +1,190 @@ +--- +title: "Zabbix 企业微信告警" +date: 2019-10-29T21:26:35+08:00 +lastmod: 2021-04-11T01:00:00+08:00 +keywords: [] +tags: ["zabbix", "微信", "告警"] +categories: ["zabbix"] +--- + +# 环境 +- CentOS7,docker-ce 20.0 +- 启动 zabbix5 容器 + ```bash + docker run -d \ + --net host \ + --name zabbix \ + harbor.colben.cn/general/alpine-zabbix + ``` + +- 打开浏览器,访问 http://ip/zabbix/,打开 zabbix web 安装界面 +- 不要做任何修改,一直下一步,直到打开 zabbix 登陆页面,默认 Admin/zabbix + +# 部署脚本 +- 进入 zabbix 容器中 +- 安装 jq 命令 +- 修改 /etc/zabbix/zabbix_server.conf,配置告警脚本加载位置 + ``` + AlertScriptsPath=/var/log/zabbix/alertscripts + ``` + +- 创建 /var/log/zabbix/alertscripts/wechat.sh,内容如下 + ```bash + set -euo pipefail + export LANG=en_US.UTF-8 + trap Quit EXIT + + # 企业 ID + CORP_ID='ww11112222aaaabbbb' + # 应用 Secret + CORP_SECRET='ABCDefghIJKLmnopQRSTuvwxYZ0000111122223333U' + # 应用 ID + AGENT_ID='1000002' + # 多个用户之间用"| 间隔 + TO_USER='@all' + # 部门 ID + TO_PARTY='1' + # token 文件存放位置 + TOKEN_FILE='/tmp/wechat_token' + + function Print { + echo -e "$(date +'[%F %T] INFO') $*" + } + + function Warn { + echo -e "$(date +'[%F %T] WARN') $*" + } + + function Error { + echo -e "$(date +'[%F %T] ERRRO') $*" + exit 1 + } + + function Quit { + [ 0 -ne $? ] && rm -f $TOKEN_FILE && Error Failed to alert! + [ -z "${END:-}" ] && echo && Error Interrupted manually! + Print Succeeded to alert. + } + + function RequestToken { + Warn Request token ... + date +%s > $TOKEN_FILE + local response=$(curl -sSL \ + "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$CORP_ID&corpsecret=$CORP_SECRET") + local errcode=$(echo "$response" | jq -erM .errcode) + local errmsg=$(echo "$response" | jq -erM .errmsg) + [ 0 -ne $errcode -a 'ok' != "$errmsg" ] && Error Request token failed: $errcode, $errmsg! + echo $response | jq -erM .access_token >> $TOKEN_FILE + } + + function GetToken { + [ ! -e $TOKEN_FILE ] && RequestToken && return + Warn Get token from localfile ... + local now=$(date +%s) + local token="$(sed -n 2p $TOKEN_FILE)" + local token_time=$(sed -n 1p $TOKEN_FILE) + if [ $((now-token_time)) -gt 7200 ]; then + Warn Local token expired ... + RequestToken + fi + } + + function SendMsg { + Warn Send message: $1 ... + local token=$(sed -n 2p $TOKEN_FILE) + local response=$(curl -sSL -X POST \ + -H "Content-Type: application/json" \ + "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$token" \ + -d @- <<-EOF + { + "touser": "$TO_USER", + "toparty": "$TO_PARTY", + "msgtype": "text", + "agentid": "$AGENT_ID", + "text": { + "content": "$1\n$2" + }, + "safe": 0 + } + EOF + ) + local errcode=$(echo "$response" | jq -erM .errcode) + local errmsg="$(echo "$response" | jq -erM .errmsg)" + [ 0 -eq $errcode -o 'ok' = "$errmsg" ] || Error Send msg failed: $errcode, $errmsg! + } + + # Start here + GetToken + SendMsg "$1" "$2" + END=1 + ``` + +- 重启 zabbix 容器 +- 测试脚本,如果企业微信能收到信息则表示脚本正常 + ```bash + /var/log/zabbix/alertscripts/wechat.sh test_subject test_content + ``` + +# 配置 zabbix +- 浏览器登陆 zabbix +- 单击"管理"-"报警媒介类型"-"创建媒体类型",输入如下信息 + - 名称: 企业微信 + - 类型: 脚本 + - 脚本名称: wechat.py + - 脚本参数: + - {ALERT.SUBJECT} + - {ALERT.MESSAGE} + - 其它默认,单击"添加" +- 单击"管理"-"用户"-"Admin"-"报警媒介"-"添加",输入如下信息 + - 类型: 企业微信 + - 收件人: 这一项用不到了,随便写 ... + - 其它默认,单击"添加" +- 单击"配置"-"动作"-"Report problems to Zabbix administrators" + - 在"动作"标签页下,选中"已启用" + - 在"操作"标签页下,输入如下信息 + - 默认标题: + ``` + XXXX系统 {HOST.NAME} 故障: {TRIGGER.NAME} + ``` + - 消息内容: + ``` + 服务器: {HOST.NAME}\n + 故障时间: {EVENT.DATE} {EVENT.TIME}\n + 故障名称: {TRIGGER.NAME}\n + 故障状态: {TRIGGER.STATUS}\n + 故障等级: {TRIGGER.SEVERITY}\n + 故障编号: ID: {EVENT.ID}\n + 监控项: {ITEM.NAME1} ({HOST.NAME1}:{ITEM.KEY1})\n + 监控值:\n + {ITEM.VALUE1} + ``` + - 单击"操作"框中的"编辑",打开"操作细节"框,输入如下信息 + - 操作类型: 发送消息 + - 发送到用户: "添加" Admin 用户 + - 仅送到: 企业微信 + - 其他默认,单击"更新" + - 在"恢复操作"标签页下,输入如下信息 + - 默认标题: + ``` + XXXX系统 {HOST.NAME} 恢复: {TRIGGER.NAME} + ``` + - 消息内容: + ``` + 服务器: {HOST.NAME}\n + 恢复时间: {EVENT.DATE} {EVENT.TIME}\n + 故障名称: {TRIGGER.NAME}\n + 故障状态: {TRIGGER.STATUS}\n + 故障等级: {TRIGGER.SEVERITY}\n + 故障编号: ID: {EVENT.ID}\n + 监控项: {ITEM.NAME1} ({HOST.NAME1}:{ITEM.KEY1})\n + 监控值:\n + {ITEM.VALUE1} + ``` + - 单击"操作"框中的步骤1的"编辑",打开"操作细节"框,输入如下信息 + - 操作类型: 发送消息 + - 发送到用户: "添加" Admin 用户 + - 仅送到: 企业微信 + - 其他默认,单击"更新" + - 忽略"更新操作" + - 其他默认,单击"更新" + diff --git a/content/post/zabbix-install.md b/content/post/zabbix-install.md new file mode 100644 index 0000000..068d4e5 --- /dev/null +++ b/content/post/zabbix-install.md @@ -0,0 +1,318 @@ +--- +title: "CentOS7 yum 安装 Zabbix 4.4" +date: 2019-10-29T21:41:17+08:00 +lastmod: 2020-02-12T05:50:00+08:00 +keywords: [] +tags: ["zabbix", "centos", "yum"] +categories: ["zabbix"] +--- + +# 环境 +- centos 7 +- nginx 1.16 +- php 7.2 +- zabbix 4.4 +- mysql 5.7 + +# 虚拟机 +- 192.168.1.100 安装 nginx, php, mysql, zabbix-server, zabbix-agent +- 192.168.1.101 安装 zabbix-agent + +# 导入软件源 +- 只在 192.168.1.100 上操作 +- 创建 /etc/yum.repos.d/epel.repo,内容如下 + ```ini + [epel] + name=Extra Packages for Enterprise Linux 7 - $basearch + baseurl=https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch + failovermethod=priority + enabled=1 + gpgcheck=1 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 + ``` +- 创建 /etc/yum.repos.d/nginx.repo,内容如下 + ```ini + [nginx-stable] + name=nginx stable repo + baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ + enabled=1 + gpgcheck=0 + gpgkey=https://nginx.org/keys/nginx_signing.key + ``` +- 安装 webtatic 源,用于安装 php7.2 + ```bash + #这货依赖 epel-release,前面已经手动创建 epel 源,不鸟它! + rpm -Uvh --nodeps https://mirror.webtatic.com/yum/el7/webtatic-release.rpm + ``` +- 创建 /etc/yum.repos.d/mysql5.7.repo,内容如下 + ```ini + [mysql-connectors-community] + name=MySQL Connectors Community + baseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql-connectors-community-el7/ + enabled=1 + gpgcheck=0 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql + + [mysql-tools-community] + name=MySQL Tools Community + baseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql-tools-community-el7/ + enabled=1 + gpgcheck=0 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql + + [mysql57-community] + name=MySQL 5.7 Community Server + baseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el7/ + enabled=1 + gpgcheck=0 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql + ``` +- 创建 /etc/yum.repos.d/zabbix.repo,内容如下 + ```ini + [zabbix] + name=Zabbix Official Repository - $basearch + baseurl=https://mirrors.tuna.tsinghua.edu.cn/zabbix/zabbix/4.4/rhel/7/$basearch/ + enabled=1 + gpgcheck=0 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ZABBIX-A14FE591 + ``` +- 重建 yum 缓存 + ```bash + yum clean all + yum makecache fast + ``` + +# 安装 nginx +- 安装 + ```bash + yum install nginx + ``` +- 修改 /etc/nginx/nginx.conf,内容如下 + ```nginx + # Nginx main config + user nginx; + pid /run/nginx.pid; + error_log /var/log/nginx/error.log; + worker_processes auto; + worker_rlimit_nofile 65535; + include /usr/share/nginx/modules/*.conf; + events { + use epoll; + multi_accept on; + worker_connections 10240; + } + http { + log_format main '$remote_addr - [$time_local] "$request_method $uri" "$args" ' + '"-" $status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + gzip on; + sendfile on; + tcp_nopush on; + tcp_nodelay off; + server_tokens off; + keepalive_timeout 65; + types_hash_max_size 2048; + default_type application/octet-stream; + include /etc/nginx/mime.types; + include /etc/nginx/conf.d/*.conf; + } + ``` +- 创建 /etc/nginx/conf.d/10080.conf,内容如下 + ```nginx + # nginx listen 10080 + server { + listen 10080; + server_name _; + location /zabbix/ { + root /usr/share; + access_log /var/log/nginx/access-zabbix.log main; + index index.php index.html index.html; + } + location ~ ^/zabbix/.+\.php$ { + root /usr/share; + access_log /var/log/nginx/access-zabbix.log main; + index index.php index.html index.html; + expires -1s; + include fastcgi_params; + try_files $uri =404; + fastcgi_pass unix:/var/lib/php/phpfpm.sock; + fastcgi_index index.php; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + break; + } + location / { + access_log /var/log/nginx/access-illegal.log main; + return 403; + } + } + ``` +- 开机自启 + ```bash + systemctl enable nginx + ``` +- 启动 nginx + ```bash + systemctl start nginx + ``` + +# 安装 php +- 安装 + ```bash + yum install php72w \ + php72w-cli \ + php72w-fpm \ + php72w-gd \ + php72w-mbstring \ + php72w-xml \ + php72w-mysqlnd \ + php72w-bcmath + ``` +- 修改 /etc/php.ini 中如下配置项 + ```ini + max_execution_time = 300 + memory_limit = 128M + post_max_size = 16M + upload_max_filesize = 2M + max_input_time = 300 + max_input_vars = 10000 + date.timezone = PRC + #php 与 mysql 同机可指定 mysql 本地套接字 + pdo_mysql.default_socket = /var/lib/mysql/mysql.sock + mysqli.default_socket = /var/lib/mysql/mysql.sock + ``` +- 修改 /etc/php-fpm.d/www.conf 中如下配置 + ```ini + listen = /var/lib/php/phpfpm.sock + listen.mode = 0666 + ``` +- 开机自启 + ```bash + systemctl enable php-fpm + ``` +- 启动 php-fpm + ```bash + systemctl start php-fpm + ``` + +# 安装 mysql 5.7 +- 安装,参考 [CentOS7 yum 安装 MySQL5.7](/post/mysql5.7-install/) +- 启动 mysqld 服务,创建 zabbix 数据库及其用户 + ```sql + create database zabbix default charset utf8mb4; + grant all on zabbix.* to zbx@localhost identified by 'Zbx_123456'; + flush privileges; + ``` + +# 安装 zabbix server +- 安装 + ```bash + yum install zabbix-server-mysql zabbix-web-mysql + ``` +- 导入 zabbix server 初始数据 + ```bash + zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzbx -p -Dzabbix + ``` +- 修改 /etc/zabbix/zabbix_server.conf 中如下配置 + ```ini + DBName=zabbix + DBUser=zbx + DBPassword=Zbx_123456 + DBSocket=/var/lib/mysql/mysql.sock + AllowRoot=1 + ``` +- 开机自启 + ```bash + systemctl enable zabbix-server + ``` +- 启动 zabbix-server + ```bash + systemctl start zabbix-server + ``` + +# web 界面设置 +- 使用常用浏览器访问: http://192.168.1.100:10080/zabbix/ +- 根据界面提示提供数据库等信息 +- zabbix 安装完成,进入系统登陆界面,默认 admin/zabbix + +# 安装 zabbix agent +- 可以直接 yum 安装,这里推荐先下载 rpm 包,方便复制到其他服务器中运行 + ```bash + yum install zabbix-agent --downloadonly --downloaddir=. + ``` +- 安装 + ```bash + rpm -Uvh zabbix-agent-4.4.*.rpm + ``` +- 修改 /etc/zabbix/zabbix_agentd.conf 中如下配置 + ```ini + EnableRemoteCommands=1 + AllowRoot=1 + #UnsafeUserParameters=1 + #UserParameter=UP[*],/etc/zabbix/UP.sh $1 $2 $3 $4 $5 $6 $7 $8 $9 + ``` +- 开机自启 + ```bash + systemctl enable zabbix-agent + ``` +- 启动 zabbix-agent + ```bash + systemctl start zabbix-agent + ``` + +# 在其它服务器上安装 zabbix agent +- 从 192.168.1.100 复制 zabbix-agent-4.4.\*.rpm 到 192.168.1.101 下 +- 登陆 192.168.1.101,安装 zabbix-agent + ```bash + rpm -Uvh zabbix-agent-4.4.\*.rpm + ``` +- 修改 /etc/zabbix/zabbix_agentd.conf 中如下配置 + ```ini + EnableRemoteCommands=1 + Server=192.168.1.100 + ServerActive=192.168.1.100:10051 + Hostname=app101 #自定义,别和其他 agent 重名即可 + AllowRoot=1 + #UnsafeUserParameters=1 + #UserParameter=UP[*],/etc/zabbix/UP.sh $1 $2 $3 $4 $5 $6 $7 $8 $9 + ``` +- 开机自启 + ```bash + systemctl enable zabbix-agent + ``` +- 启动 zabbix-agent + ```bash + systemctl start zabbix-agent + ``` + +# Docker 启动 zabbix server +- 下载镜像 + ```bash + docker pull ccr.ccs.tencentyun.com/colben/zabbix + ``` +- 启动容器 + ```bash + docker run -d \ + --name zabbix-server \ + -p 80:80 \ + -p 10051:10051 \ + ccr.ccs.tencentyun.com/colben/zabbix + ``` +- 浏览器访问 http://{ip}/zabbix/ ,打开 zabbix 页面向导,一直 "下一步" 即可 +- 配置文件 + - /etc/my.cnf + - /etc/my.cnf.d/\*.cnf + - /etc/nginx/nginx.conf + - /etc/nginx/conf.d + - /etc/zabbix/zabbix_server.conf +- 数据目录 + - /var/lib/mysql + - /var/lib/mysql-bin +- 日志目录 + - /var/log/mysql + - /var/log/nginx + - /var/log/php7 + - /var/log/zabbix + diff --git a/content/post/zabbix.md b/content/post/zabbix.md new file mode 100644 index 0000000..7aad863 --- /dev/null +++ b/content/post/zabbix.md @@ -0,0 +1,39 @@ +--- +title: "Zabbix 笔记" +date: 2019-10-30T11:45:29+08:00 +lastmod: 2019-10-30T11:45:29+08:00 +tags: ["zabbix", "tomcat"] +categories: ["zabbix"] +--- + +# 通过 JavaGateway 监控 tomcat +- 编译 zabbix server,configure 增加 --enable-java +- 修改 zabbix_server.conf 文件,配置 JavaGateway 参数 + ``` + JavaGateway=127.0.0.1 + JavaGatewayPort=10052 + StartJavaPollers=5 + ``` +- 配置 tomcat catalina 启动参数 + ```bash + export CATALINA_OPTS=" + -Dcom.sun.management.jmxremote + -Dcom.sun.management.jmxremote.authenticate=false + -Dcom.sun.management.jmxremote.ssl=false + -Dcom.sun.management.jmxremote.port=10053 + -Djava.rmi.server.hostname={tomcat_server_ip} + " + ``` +- 重启 tomcat +- 启动 JavaGateway + ```bash + # 进入 zabbix_server 安装目录,执行 + ./sbin/zabbix_java/startup.sh + ``` +- 重启 zabbix-server + ``` + # 进入 zabbix_server 安装目录,执行 + ./sbin/zabbix_server + ``` + + diff --git a/content/post/zabbix3.10-install.md b/content/post/zabbix3.10-install.md new file mode 100644 index 0000000..925c713 --- /dev/null +++ b/content/post/zabbix3.10-install.md @@ -0,0 +1,257 @@ +--- +title: "CentOS7 安装 Zabbix3.10" +date: 2019-10-30T13:27:44+08:00 +lastmod: 2019-10-30T13:27:44+08:00 +tags: ["zabbix"] +categories: ["zabbix"] +--- + +# 环境 +- 操作系统:CentOS7 (192.168.1.100) +- 数据库:MariaDB 10.1.26 +- Web后台:PHP 5.6.31 +- Web引擎:Nginx 1.12.1 +- Zabbix:Zabbix 3.0.10 +- 部署方式:单机部署 + +# 安装操作系统 CentOS +- 推荐 RHEL6 及以上的 x64 版本。 +- 这里最小安装 CentOS7 ,外网不可达服务器可能需要配置本地 yum 源。 + +# 安装数据库 MariaDB +- MariaDB/MySQL 的常见版本。 +- 本地如果已存在可用的 MariaDB/MySQL 数据库,则可跳过此步。 +- 安装方式很多,这里直接用官方编译好的二进制通用包: +- CentOS7 下载 [mariadb-10.1.26-linux-glibc_214-x86_64.tar.gz](http://mirrors.neusoft.edu.cn/mariadb//mariadb-10.1.26/bintar-linux-glibc_214-x86_64/mariadb-10.1.26-linux-glibc_214-x86_64.tar.gz) +- CentOS6 下载 [mariadb-10.1.26-linux-x86_64.tar.gz](http://mirrors.neusoft.edu.cn/mariadb//mariadb-10.1.26/bintar-linux-x86_64/mariadb-10.1.26-linux-x86_64.tar.gz) +- 安装 [参考我的另一篇博客](https://my.oschina.net/colben/blog/361465) + +# 安装 PHP +- PHP-5.5 及以上版本。 +- 本地如果已存在可用的 PHP ,则可跳过此步。 +- 安装方法很多,这里使用官方源码编译: +- 下载 [php-5.6.31.tar.bz2](http://cn2.php.net/distributions/php-5.6.31.tar.bz2) +- 安装 + ```bash + #解压 + tar jxf php-5.6.31.tar.bz2 -C /root/ + cd /root/php-5.6.31/ + #安装依赖包 + yum install gcc make gd-devel libjpeg-devel libpng-devel libxml2-devel bzip2-devel libcurl-devel + #创建安装目录 + mkdir /opt/php-5.6.31/etc/ -p + ln -s /opt/php-5.6.31 /opt/php + #检查编译环境 + ./configure --prefix=/opt/php \ + --with-config-file-path=/opt/php/etc --with-bz2 --with-curl \ + --enable-ftp --enable-sockets --disable-ipv6 --with-gd \ + --with-jpeg-dir=/usr/local --with-png-dir=/usr/local \ + --with-freetype-dir=/usr/local --enable-gd-native-ttf \ + --with-iconv-dir=/usr/local --enable-mbstring --enable-calendar \ + --with-gettext --with-libxml-dir=/usr/local --with-zlib \ + --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd \ + --enable-dom --enable-xml --enable-fpm --with-libdir=lib64 --enable-bcmath + #编译安装 + make + make install + ``` +- 配置 + ```ini + #复制配置文件 + cp php.ini-production /opt/php/etc/php.ini + cp /opt/php/etc/php-fpm.conf.default /opt/php/etc/php-fpm.conf + #修改 /opt/php/etc/php.ini 的下面选项 + max_execution_time = 300 + memory_limit = 128M + post_max_size = 16M + upload_max_filesize = 2M + max_input_time = 300 + date.timezone PRC + always_populate_raw_post_data = -1 + #php 与 mysql 同机可指定 mysql 本地套接字 + pdo_mysql.default_socket = /tmp/mysql.sock #指定数据库本地套接字地址 + mysqli.default_socket = /tmp/mysql.sock #指定数据库本地套接字地址 + #修改 /opt/php/etc/php-fpm.conf 的下面选项 + user = nobody + group = nobody + # php 与 nginx 同机可指定 php 套接字,供 nginx 配置使用 + #listen = 127.0.0.1:9000 + listen = /dev/shm/phpfpm.sock + listen.owner = nobody + listen.group = nobody + listen.mode = 0660 + ``` +- 启动 + ```bash + /opt/php/sbin/php-fpm + ``` + +# 安装 Nginx +- Nginx 的常见版本。 +- 本地如果已存在可用的 Nginx ,则无需再次安装,直接在配置文件中添加配置即可。 +- 安装方式很多,这里使用官方源码编译: +- 下载 [nginx-1.12.1.tar.gz](http://nginx.org/download/nginx-1.12.1.tar.gz) +- 安装 + ```bash + #解压 + tar zxf nginx-1.12.1.tar.gz -C /root/ + cd /root/nginx-1.12.1/ + #安装依赖包 + yum install pcre pcre-devel openssl openssl-devel + #创建安装目录 + mkdir /opt/nginx-1.12.1 + ln -s /opt/nginx-1.12.1 /opt/nginx + #检查编译环境 + ./configure --prefix=/opt/nginx \ + --with-http_ssl_module --with-pcre \ + --with-http_stub_status_module + #编译安装 + make + make install + ``` +- 配置 + ```c + #在 /opt/nginx/conf/nginx.conf 中配置 php + server { + listen 80; + index index.php index.html index.html; + root html; + location /zabbix/ { + access_log /var/log/nginx/zabbix-access.log main; + index index.php index.html index.html; + } + location ~ ^/zabbix/.+\.php$ { + access_log /var/log/nginx/zabbix-access.log login; + index index.php index.html index.html; + expires -1s; + include fastcgi_params; + try_files $uri =404; + fastcgi_pass unix:/dev/shm/phpfpm.sock; + fastcgi_index index.php; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + break; + } + } + ``` +- 启动 + ```bash + /opt/nginx/sbin/nginx + ``` + +# 安装 Zabbix Server +- 推荐 Zabbix 3.0 及以上版本。 +- 安装方式很多,这里使用官方源码编译: +- 下载 [zabbix-3.0.10.tar.gz](https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/3.0.10/zabbix-3.0.10.tar.gz) +- 安装 + ```bash + #解压 + tar zxf zabbix-3.0.10.tar.gz -C /root/ + cd /root/zabbix-3.0.10/ + #安装依赖包 + yum install snmp-devel curl-devel + #创建安装目录 + mkdir /opt/zabbix-server-3.0.10 + ln -s /opt/zabbix-server-3.0.10 /opt/zabbix-server + #检查编译环境 + ./configure --prefix=/opt/zabbix-server/ --enable-server \ + --enable-agent --with-mysql --with-net-snmp --with-libcurl --with-libxml2 + #编译安装 + make + make install + ``` +- 创建数据库 + ```bash + mysql -uroot -p12345678 + mysql> create database db_zbx default charset utf8; + mysql> create user zbx@localhost identified by 'zbx_pass'; + mysql> grant all on db_zbx.* to zbx@localhost; + mysql> grant reload on *.* to zbx@localhost; + mysql> flush privileges; + mysql> quit; + mysql -uzbx -pzbx_pass db_zbx < database/mysql/schema.sql + mysql -uzbx -pzbx_pass db_zbx < database/mysql/images.sql + mysql -uzbx -pzbx_pass db_zbx < database/mysql/date.sql + ``` +- 配置 + ```ini + #在 /opt/zabbix-server/etc/zabbix_server.conf 中,修改如下配置 + DBHost=localhost + DBName=db_zbx + DBUser=zbx + DBPassword=zbx_pass + #这里的数据库是 mysql 且 DBHost 配置为 localhost,zabbix 会使用 mysql.sock 连接 mysql + DBSocket=/tmp/mysql.sock + DBPort=3306 + AllowRoot=1 + #在 /opt/zabbix-server/etc/zabbix_agentd.conf 中,修改如下配置 + EnableRemoteCommands=1 + Server=127.0.0.1 + ServerActive=127.0.0.1 + Hostname=127.0.0.1 + AllowRoot=1 + ``` +- 启动 + ```bash + /opt/zabbix-server/sbin/zabbix_server + /opt/zabbix-server/sbin/zabbix_agentd + ``` + +# 配置 zabbix 管理网站 +- 部署前端文件 + ```bash + cp -ar frontends/php /opt/nginx/html/zabbix + ``` +- 浏览器打开 http://192.168.1.100/zabbix/ +- 根据界面提示配置数据库及相关 zabbix 信息,生成配置文件 /opt/nginx/html/zabbix/conf/zabbix.conf.php +- 修改 /opt/nginx/html/zabbix/conf/zabbix.conf.php + ```php + #zabbix server 与 nginx、php、mysql 同机时,可注释 DB['SERVER'] 和 DB['PORT'] 项,使 php 通过 mysql.sock 与本地 MariaDB 通信 + #$DB['SERVER'] = 'localhost'; + #$DB['PORT'] = '0'; + ``` + +# 登陆 zabbix 管理网站 +- 浏览器输入 http://192.168.1.100/zabbix/ +- 默认用户名: admin ,默认密码: zabbix + +# 在被监控服务器 (192.168.1.101) 上安装 Zabbix Agent +- 推荐 Zabbix 3.0 及以上版本。 +- 安装方式很多,这里使用官方源码编译: +- 下载 [zabbix-3.0.10.tar.gz](https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/3.0.10/zabbix-3.0.10.tar.gz) (同 server) +- 安装 + ```bash + #解压 + tar zxf zabbix-3.0.10.tar.gz -C /root/ + cd /root/zabbix-3.0.10/ + #创建安装目录 + mkdir /opt/zabbix + #检查编译环境 + ./configure --prefix=/opt/zabbix --enable-agent + #编译安装 + make + make install + ``` +- 配置 + ```ini + #在 /opt/zabbix/etc/zabbix-agentd.conf 中,修改如下配置 + EnableRemoteCommands=1 + Server=192.168.1.100 + ServerActive=192.168.1.100 + Hostname=192.168.1.101 + AllowRoot=1 + ``` +- 启动 + ```bash + /opt/zabbix/sbin/zabbix_agentd + ``` + +# 管理界面增加监控主机 +- 登陆 zabbix 管理界面 http://192.168.1.100/zabbix +- 在 “配置-主机” 下点击 “新增主机”, 名称填写被监控服务器中 /opt/zabbix/etc/zabbix_agentd.conf 中 Hostname 项(192.168.1.101) +- 在主机 (192.168.1.101) 下添加需要的监控项、触发器及图形,即可完成该服务器的监控。 + +# 参考 +- [https://www.ttlsa.com/zabbix/install-zabbix-on-linux-5-ttlsa/](https://www.ttlsa.com/zabbix/install-zabbix-on-linux-5-ttlsa/) + diff --git a/content/post/zk-install.md b/content/post/zk-install.md new file mode 100644 index 0000000..f0648a6 --- /dev/null +++ b/content/post/zk-install.md @@ -0,0 +1,104 @@ +--- +title: "CentOS7 安装 zookeeper 集群" +date: 2019-10-30T01:25:48+08:00 +lastmod: 2019-10-30T01:25:48+08:00 +keywords: [] +tags: ["zookeeper", "centos7"] +categories: ["storage"] +--- + +# 环境 + +主机名 | eth0 IP | eth1 IP | 操作系统 | ZK 版本 | myid +---- | ---- | ---- | ---- | ---- | ---- +zk221 | 192.168.1.221 | 192.168.16.221 | CentOS7.5 | 3.4.14 | 221 +zk222 | 192.168.1.222 | 192.168.16.222 | CentOS7.5 | 3.4.14 | 222 +zk223 | 192.168.1.223 | 192.168.16.223 | CentOS7.5 | 3.4.14 | 223 + +- 下载 [zookeeper-3.4.14](https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz) +- eth0 网卡用于向客户端提供服务,eth1 网卡用于 zookeeper 集群内部通信 + +# 各节点初始配置 + +- 关闭 selinux、防火墙 +- 部署 java 运行环境 +- 创建数据(快照日志)目录 + ```bash + mkdir -p /var/lib/zookeeper/data + ``` +- 创建事物日志目录 + ```bash + mkdir -p /var/lib/zookeeper/dataLog + ``` +- 创建服务日志目录 + ``` + mkdir -p /var/log/zookeeper + ``` +- 生成 myid 文件 + ``` + # zk221 + echo 221 > /var/lib/zookeeper/data/myid + # zk222 + echo 222 > /var/lib/zookeeper/data/myid + # zk223 + echo 223 > /var/lib/zookeeper/data/myid + ``` + +# 部署 zookeeper + +- 登陆 zk221,下载 zookeeper,解压至 /opt/ 下 +- 生成配置文件 + ``` + cd /opt/zookeeper/conf/ + cp zoo_sample.cfg zoo.cfg + ``` +- 修改 /opt/zookeeper/conf/zoo.cfg + ``` + dataDir=/var/lib/zookeeper/data + dataLogDir=/var/lib/zookeeper/dataLog + # servers + server.221=192.168.16.221:2888:3888 + server.222=192.168.16.222:2888:3888 + server.223=192.168.16.223:2888:3888 + ``` +- 修改 /opt/zookeeper/bin/zkEnv.sh + ``` + # 找到 ZOO_LOG_DIR="." 一行,换成如下 + ZOO_LOG_DIR="/var/log/zookeeper" + ``` +- 打包 zookeeper 目录,复制到 zk222 和 zk223 上 + +# 启动集群 + +- 直接启动脚本 + - 在每个节点上启动 zookeeper 服务 + ```bash + /opt/zookeeper/bin/zkServer.sh start + ``` +- systemd 启动 + - 创建 /usr/lib/systemd/system/zookeeper.service,内容如下 + ``` + [Unit] + Description=Zookeeper + Requires=network.service + After=network.service + [Service] + Environment=JAVA_HOME=/opt/jre + ExecStart=/opt/zookeeper/bin/zkServer.sh start-foreground + SuccessExitStatus=143 + [Install] + WantedBy=multi-user.target + ``` + - 启动 zookeeper 服务 + ```bash + systemctl daemon-reload + systemctl start zookeeper + ``` + +# 查看集群状态 + +- 查看节点状态 + ```bash + /opt/zookeeper/bin/zkServer.sh status + ``` + diff --git a/content/search.md b/content/search.md new file mode 100644 index 0000000..996d6d2 --- /dev/null +++ b/content/search.md @@ -0,0 +1,9 @@ ++++ +title = "搜索" +layout = "search" +menu = "main" +outputs = ["html", "json"] +[sitemap] + priority = 0.1 ++++ + diff --git a/static/img/03092704_DUG6.png b/static/img/03092704_DUG6.png new file mode 100644 index 0000000..d3287b3 Binary files /dev/null and b/static/img/03092704_DUG6.png differ diff --git a/static/img/03094814_oVJY.png b/static/img/03094814_oVJY.png new file mode 100644 index 0000000..093886f Binary files /dev/null and b/static/img/03094814_oVJY.png differ diff --git a/static/img/03100036_sRDb.png b/static/img/03100036_sRDb.png new file mode 100644 index 0000000..1fbeab5 Binary files /dev/null and b/static/img/03100036_sRDb.png differ diff --git a/static/img/03100217_mMNU.png b/static/img/03100217_mMNU.png new file mode 100644 index 0000000..e88574a Binary files /dev/null and b/static/img/03100217_mMNU.png differ diff --git a/static/img/03101245_wAKJ.png b/static/img/03101245_wAKJ.png new file mode 100644 index 0000000..3c903c5 Binary files /dev/null and b/static/img/03101245_wAKJ.png differ diff --git a/static/img/03102522_BRPU.png b/static/img/03102522_BRPU.png new file mode 100644 index 0000000..dd8cba9 Binary files /dev/null and b/static/img/03102522_BRPU.png differ diff --git a/static/img/03103545_bX3Q.png b/static/img/03103545_bX3Q.png new file mode 100644 index 0000000..1a439d8 Binary files /dev/null and b/static/img/03103545_bX3Q.png differ diff --git a/static/img/03103719_CPnJ.png b/static/img/03103719_CPnJ.png new file mode 100644 index 0000000..b1957f5 Binary files /dev/null and b/static/img/03103719_CPnJ.png differ diff --git a/static/img/03103936_CZ1s.png b/static/img/03103936_CZ1s.png new file mode 100644 index 0000000..676d81f Binary files /dev/null and b/static/img/03103936_CZ1s.png differ diff --git a/static/img/03104155_Lz45.png b/static/img/03104155_Lz45.png new file mode 100644 index 0000000..047e46e Binary files /dev/null and b/static/img/03104155_Lz45.png differ diff --git a/static/img/03104347_AHmM.png b/static/img/03104347_AHmM.png new file mode 100644 index 0000000..d004a61 Binary files /dev/null and b/static/img/03104347_AHmM.png differ diff --git a/static/img/0bfe3ac4984fbd6973738dddb20d5bb6c72.png b/static/img/0bfe3ac4984fbd6973738dddb20d5bb6c72.png new file mode 100644 index 0000000..2af3c5f Binary files /dev/null and b/static/img/0bfe3ac4984fbd6973738dddb20d5bb6c72.png differ diff --git a/static/img/150546_2jE9_2298475.png b/static/img/150546_2jE9_2298475.png new file mode 100644 index 0000000..3ffa84d Binary files /dev/null and b/static/img/150546_2jE9_2298475.png differ diff --git a/static/img/150655_jGEp_2298475.png b/static/img/150655_jGEp_2298475.png new file mode 100644 index 0000000..c863a0b Binary files /dev/null and b/static/img/150655_jGEp_2298475.png differ diff --git a/static/img/3d5e7c0b8e7a7e51028d11358f442947367.png b/static/img/3d5e7c0b8e7a7e51028d11358f442947367.png new file mode 100644 index 0000000..61b1abc Binary files /dev/null and b/static/img/3d5e7c0b8e7a7e51028d11358f442947367.png differ diff --git a/static/img/4577cd8a2c7aea037e50fb002f91bb40842.png b/static/img/4577cd8a2c7aea037e50fb002f91bb40842.png new file mode 100644 index 0000000..6e74400 Binary files /dev/null and b/static/img/4577cd8a2c7aea037e50fb002f91bb40842.png differ diff --git a/static/img/633adea823cad6380030826ce75a44109fb.png b/static/img/633adea823cad6380030826ce75a44109fb.png new file mode 100644 index 0000000..da00944 Binary files /dev/null and b/static/img/633adea823cad6380030826ce75a44109fb.png differ diff --git a/static/img/cf50fb5da24afb930eab80100707b13efbe.png b/static/img/cf50fb5da24afb930eab80100707b13efbe.png new file mode 100644 index 0000000..10cc3e8 Binary files /dev/null and b/static/img/cf50fb5da24afb930eab80100707b13efbe.png differ diff --git a/static/img/dc44a6e8688218edb10ad7f2427b0525132.png b/static/img/dc44a6e8688218edb10ad7f2427b0525132.png new file mode 100644 index 0000000..24b4319 Binary files /dev/null and b/static/img/dc44a6e8688218edb10ad7f2427b0525132.png differ diff --git a/static/img/eb3c0ccd3d0017c18d4eba3a0b776d29e21.png b/static/img/eb3c0ccd3d0017c18d4eba3a0b776d29e21.png new file mode 100644 index 0000000..6c996e3 Binary files /dev/null and b/static/img/eb3c0ccd3d0017c18d4eba3a0b776d29e21.png differ diff --git a/static/img/lvm.jpg b/static/img/lvm.jpg new file mode 100644 index 0000000..1f0d87d Binary files /dev/null and b/static/img/lvm.jpg differ diff --git a/static/img/raid0.jpg b/static/img/raid0.jpg new file mode 100644 index 0000000..32d59cf Binary files /dev/null and b/static/img/raid0.jpg differ diff --git a/static/img/raid1.jpg b/static/img/raid1.jpg new file mode 100644 index 0000000..e272a6b Binary files /dev/null and b/static/img/raid1.jpg differ diff --git a/static/img/raid10.jpg b/static/img/raid10.jpg new file mode 100644 index 0000000..e17cd88 Binary files /dev/null and b/static/img/raid10.jpg differ diff --git a/static/img/raid5.jpg b/static/img/raid5.jpg new file mode 100644 index 0000000..1b31818 Binary files /dev/null and b/static/img/raid5.jpg differ diff --git a/static/img/raid6.jpg b/static/img/raid6.jpg new file mode 100644 index 0000000..db84dcf Binary files /dev/null and b/static/img/raid6.jpg differ diff --git a/static/img/richard-stallman.png b/static/img/richard-stallman.png new file mode 100644 index 0000000..9b3edac Binary files /dev/null and b/static/img/richard-stallman.png differ